CadQuery / cadquery

A python parametric CAD scripting framework based on OCCT
https://cadquery.readthedocs.io
Other
3.21k stars 292 forks source link

Pythonic API #195

Closed Peque closed 4 years ago

Peque commented 5 years ago

I know it may come too late for v2 already, and maybe you already discussed this, but I did not find an issue about it.

I think it would be great to have a Pythonic API, which means having methods like .push_points() instead of .pushPoints(). Same with variable/parameter names, which would be lowercase.

It could be a progressive change with deprecation warnings. Or it could be a breaking change if you are willing to for v2.

Maybe you do not think this is important, but I thought if there was a time for this that time would be now, not later. :joy:

adam-urbanczyk commented 5 years ago

Thanks for your input @Peque . Don't get me wrong, but I don't think I'm personally in favor of doing that. It's quite some work and the benefit is unclear too me.

Peque commented 5 years ago

@adam-urbanczyk I can totally understand that.

It is mostly a matter of preference, I guess.

It can be a little inconvenient when integrating CadQuery code in an already-existing code base with lint checks, which can enforce PEP8 naming conventions.

Feel free to close this, specially if some other main developer (@dcowden ?) is against as well. :+1:

helmecke commented 4 years ago

I would love to see this happen. It would flatten the learning curve for new users and increase adoption.

adam-urbanczyk commented 4 years ago

How would pep8 naming flatten the learning curve exactly? Also note that CQ is around for very long time and this would be a rather breaking change.

michaelgale commented 4 years ago

Pythonic / PEP-8 compliant naming is a good policy for new projects; however, it is untenable for a mature API with established user community. The marginal benefits would easily be lost by the burden of breaking/fixing end user code. It would be very difficult to convince end users that PEP-8 conformance would offer any additional value. Many mature python libraries (including core modules) have legacy naming conventions that technically violate PEP-8 but have not changed, nor are likely to do so in future. To quote Raymond Hettinger of the python core team: "Do not PEP 8 unto others; only PEP 8 thy self" p.s. A workaround could be achieved by making an adapter class/module around CQ with PEP8 compliant interface--if someone felt sufficiently inspired and committed to that goal! ;)

helmecke commented 4 years ago

How would pep8 naming flatten the learning curve exactly? Also note that CQ is around for very long time and this would be a rather breaking change.

Maybe learning curve was bad choice of words. I mean you have to learn that CQ is different and need to remember it every time you come back to it.

Just going by Github stars, adoption of CQ is not that high. I feel that a breaking change now would benefit CQ in the long run. But I'm new to CQ so for me this is easy to say.

I'll use it either way because it's great. If CQ member/community are against this change, please close this issue, preferably with comment or label not going to happen.

adam-urbanczyk commented 4 years ago

Alright, adding a wontifx on this one.

Peque commented 4 years ago

@michaelgale I don't think the CadQuery vs. Python core modules is a good comparison. The standard library is where projects go to die and yeah, stability there is of utmost important.

CadQuery, on the other hand, is hopefully still at its earliest stage and will grow much more in the future. :heart: If there was a time to propose this API change, it was now. If OpenSCAD has 3k stars on GitHub I picture CadQuery still growing at least to that point (i.e.: a community 10x larger). :heart_eyes:

Having said that, I understand and respect @adam-urbanczyk's decision. I wish there had been a little been more discussion first though (@dcowden @jmwright @bragostin @marcus7070 ?).

Advantages of switching:

I agree with the disadvantages:

The advantages look like small, but long-term. The disadvantages look like big, but short-term. So yeah, it is not clear... :joy:

jmwright commented 4 years ago

I appreciate the desire to be Pythonic and the discussion around this topic. My thoughts are below.

As far as potential solutions, we'd probably need to do something like this to give users time to transition their old code.

def pushPoints(self, pntList):
    push_points(self, pntList)

    warn(
                "pushPoints will be removed in favor of push_points in the future",
                DeprecationWarning,
            )

def push_points(self, pntList):
    # Holds original function code
   ...

This is another possibility to avoid all the cruft of adding extra functions, but a general deprecation warning would probably need to be shown each time CadQuery was imported. Unless we wanted to leave the codebase like this forever for backwards compatibility.

pushPoints = push_points
def push_points(self, pntList):
    ...

It would be nice to have fully pythonic/compliant code, but I don't think it's practical for the core team to attempt it.

  1. The core team doesn't have the resources/interest to do all that and keep CadQuery moving forward. Not to mention the implications for libraries like cqparts that are also maintained by volunteers with limited time on their hands.
  2. I cringe at all the clutter that will be in the codebase for an undetermined amount of time. I can deal with it, but it's still a thing.
  3. There will be a significant amount of users like me that have CQ scripts, notebooks, examples etc tucked away in different repos and blog posts that will still run with CQ 2.x and that will eventually be broken by this. The original maintainer won't circle back and update their code unless it's in active use by themselves. There will then be users who are confused when they grab a script from jmwright's personal CQ script repo and then get a ton of warnings that "X has no attribute Y". You can argue that people should update their code, but when people did something for fun or just to be helpful in the past, the reality is that they usually won't update that code. Are we willing to jettison the body of work that's already been built up by the CQ community for this?

A potential compromise might be to use the aliasing method in the second code snippet above, assuming that someone outside the core dev team is willing to take that on, and then just leave the codebase like that forever (or at least for a very long time). That would prevent previously existing scripts from breaking, but the documentation would reflect the new Pythonic names. I'm sure there are extra details to work out, like how to keep the aliases out of the generated Sphinx docs, but it could be worth discussing.

helmecke commented 4 years ago

I just stumbled upon michaelgale/cq-kit. It might be personal preference but reading through the code was just a breathe. It looks so clean and reads fluently.

Sorry for reviving an old(done) discussion.

michaelgale commented 4 years ago

@helmecke You're too kind. cq-kit still needs a lot of cleanup, but thank you for acknowledging my "pep 8" style tendencies (despite my comments above!)

dcowden commented 4 years ago

@michaelgale kudos on cq-kit, i visited today, very , very nice! I like in particular the shared faces one.

In my experience using conventional CAD, its also common to do operations based upon prior selections. What do you thiink about the idea of allowing combination of previously tagged items with yours, so that you can easily select things you marked before, and then combine that with others?

Just to pique your interest, the MOST powerful selectors in my experience are ones which can query topology based on how it interacted with the solid. suppose we have an operation, like an extrude-- its very powerful to be able to filter the topology by combination of the operation id and what happened-- for example, added, deleted, or changed.

I have developed several FeatureScripts for cloud based cad OnShape. CQ is much better in nearly every way than FS, but loses in two categories. One, they are based on the parasolid kernel, which is simply better than OpenCascade-- nothing we can do about that. But the other is that they do support query by what an operation did. Once you have that, you can't live without it.

michaelgale commented 4 years ago

@dcowden Thanks! Selectors are quiet super-hero components of CQ. Generally, they are mechanism of "querying the topology" -- coincidentally, "query" is in the CQ name--and ironically, I understand that was not the intent!

However, the key idea in this discussion is to recognize the power of CQ to "query the topology" and how we can extend this capability even more. Currently, we have the Selector class and recently we've added tagging. What we need now is to take stock of what we have and figure out what we're missing--e.g. query by operation. Then, figure out a general purpose way of capturing these ideas into coherent API taxonomy.

My hunch is that the Selector class is the place to do this. It already has the foundation of arithmetic and boolean operators, string based selectors, and offers consistent access to filtering/querying any primitive (vertex, edge, wire, solid). By carefully constructing "convenience" accessor methods which under the hood combine a clever combination of Selectors it can supercharge CQ's fluent operations.

Am I right in thinking that the big missing component in achieving what you're describing is the ability to review the "history" of an object? That is, the history of operations? Given that most CQ operations return back a copy of the object (for fluent use) could this not involve appending a hash or similar to a "log" attribute for an object? Each operation appends the log and returns itself. But how would this be used? Would we have to reconstruct the object from zero up to the desired state to then perform a query on a vertex, etc.? It sounds like it could be computationally expensive? Perhaps I'm missing the point (or vertex in this case)? ;)

dcowden commented 4 years ago

@michaelgale

Am I right in thinking that the big missing component in achieving what you're describing is the ability to review the "history" of an object?

Correct! To my way of thinking, there are two key concepts. There's the notion of operations, and history the changes they made. Somewhat separately, there's the notion of the topology hierarchy, for example the fact that an edge is related to a vertex, or a surface comes from an edge, after extrusion.

Together, these form very powerful selectors. Tagging or auto-generated operations can limit the scope of selection to those affected by a given operation. Additionally, the topology hierarchy can be useful as well.

OCCT does contemplate this sort of thing, though primarily in the AIS package, though in the context of a UI. There might be some things we can use from there

michaelgale commented 4 years ago

As p.s. follow-up, here's how I think CQ's powerful Selector mechanism sets it apart using an example: My CQ code builds this complicated shape progressively in a hierarchy of subassemblies and processes: Now that it is a monolithic solid, I want to make a plastic mold. But first, I need to draft and fillet parts of this solid--but where do I even start?

Using CQ's selectors, I can currently say, "select all the planar faces at height 3.2mm with 4 edges in closed loops and return the edges" then "with these edges, give me all edges which share vertices with these edges and fillet them". This sequence of querying topology, reliably selects all of the enclosed voids between the tracks and fillets their edges. I didn't have to worry about the shape or dimensions of the track, I just had to describe the desired topology of objects I wanted. Similarly, with a sequence of similar operations I can fillet the entire solid with surgical precision.

Operations like these have to be performed at the end when the solid has its final shape. Furthermore, the unfilleted version of this solid is used for other tasks like 3D printing or documentation. Thus filleting is an "optional" operation--and thanks to CQ, I don't have to re-render the entire shape just make a filleted version.

adam-urbanczyk commented 4 years ago

Wow, that is a great example of CQ in action @michaelgale !

Regarding history and selectors:

To summarize, selectors are definitely a thing to develop further. Let's continue the discussion in a separate issue though.

dcowden commented 4 years ago

@michaelgale that's very impressive, and i'd never thought of application of selectors to drafting and such-- amazing! You've definitely sold me on how much we've undersold selectors in the documentation!

@adam-urbanczyk absolutely agreed on starting a new issue!