Open lalebarde opened 4 years ago
You could use CQ-editor: the "Inspect CQ object" function visualizes the history in the CQ object inspector pane and allows to graphically step through it.
Thanks Adam, it is very useful, but does not provide information on the stack state. For example:
I can see all the intermediate steps in the rendering view, (un)select the different variables containing a solid in the Object
view and (un)hide them in the rendering view, and as you have explained, step graphically in the CQ object inspector
view. That's great. First remark, in the CQ object inspector
view, only the first two wires are shown. After that, I have no details between the first solid and the next compound as you can see in the figure.
Secondly and to come back on stack information, still between the first solid and the first compound with the second one, I create two wires, and I want to be able to check they are both in the stack. Such a feature would be very appreciated to solve problems such as "ValueError: More than one wire is required".
A stack view would be great to have, along with a more complete CQ object inspector
view. For the stack view, possibly it could fit in the Object
view which at present time shows only solids. But I think everything is here to add more objects : faces, edges, wires, workplanes, etc, and each one could be enabled or not.
Could you add examples of CQ class
usage in the documentation please?
add(obj) : Adds an object or a list of objects to the stack
all() : Return a list of all CQ objects on the stack
first() : Return the first item on the stack
item(i) : Return the ith item on the stack
last() : Return the last item on the stack
size() : Return the number of objects currently on the stack
val() : Return the first value on the stack
vals() : get the values in the current list
Thanks Adam, it is very useful, but does not provide information on the stack state. For example:
I can see all the intermediate steps in the rendering view, (un)select the different variables containing a solid in the
Object
view and (un)hide them in the rendering view, and as you have explained, step graphically in theCQ object inspector
view. That's great. First remark, in theCQ object inspector
view, only the first two wires are shown. After that, I have no details between the first solid and the next compound as you can see in the figure.Secondly and to come back on stack information, still between the first solid and the first compound with the second one, I create two wires, and I want to be able to check they are both in the stack. Such a feature would be very appreciated to solve problems such as "ValueError: More than one wire is required".
A stack view would be great to have, along with a more complete
CQ object inspector
view. For the stack view, possibly it could fit in theObject
view which at present time shows only solids. But I think everything is here to add more objects : faces, edges, wires, workplanes, etc, and each one could be enabled or not.
I think we are using different semantics. CQ object inspector
is a visualization of the stack - you can inspect which items are on the stack at every level. It visualizes what you'd get by calling vals()
or accessing .objects
. What it does not visualize is the content of ctx.pendingWires
and ctx.pendingEdges
. These fields are currently 'orthogonal' to the stack, adding new levels does not change their content.
Is there a stack per object, or only a global stack?
Methods like shells, vertices, wires(selector=None, tag=None) which selecst the shells/vertices/wires of objects on the stack applies to the whole stack ?
Having a view in CQ-Editor of ctx
would be nice
Is there a stack per object, or only a global stack?
Global. Each CQ object that is returned by an operation has a reference to its parent. You can walk the stack backwards.
https://cadquery.readthedocs.io/en/latest/primer.html?highlight=stack#the-stack
which selecst the shells/vertices/wires of objects on the stack applies to the whole stack ?
Should be there whole stack. If you add a second solid or a hole, you can select the edges from the first and second solid/cut. If it didn't work that way, you'd only get the edges from the latest operation.
Thanks Jeremy. Some advanced examples of stack manipulation would be welcome.
I'm also struggling with this. I am trying to create a loft from two somewhat complex shapes, and have a very hard time understanding what my Wires are doing and where they are. The two shapes are created using moveTo
, lineTo
, radiusArc
and mirrorY
commands in a workplane. Whatever I try, I either end up with a ValueError: More than one wire is required
or additional copies of my wires in strange places (maybe due to mirrorY
?).
So while the Stack is imho quite well explained in the docs, there is very little clarity how those "pendingWires" and "pendingEdges" work - in fact, I have never heard of them before reading this issue.
For me, as an experienced python programmer but new to CadQuery, it seems rather un-pythonic to try pinpoint specific objects in a hidden Stack using string tags, when I would expect to be able to simply create two shapes independently from each other, keep a python variable of each and then loft those two (I have tried Workplane.add
, but without success). The documentation suggests that cadquery operations do not modify their objects but create a "new" one - which is very favourable for reasoning. However, it seems that this is not true for those "pending" objects.
Therefore, a clear description on the semantics of cadquery objects including those pending objects would be greatly appreciated.
Whatever I try, I either end up with a ValueError: More than one wire is required or additional copies of my wires in strange places (maybe due to mirrorY?).
There has been a fair amount of trouble with mirroring. https://github.com/CadQuery/cadquery/issues/125
If you mirror the points in your code and make sure the result is closed, do you have more success with the loft?
Hit the wrong button on mobile...
The documentation suggests that cadquery operations do not modify their objects but create a "new" one - which is very favourable for reasoning.
Some methods return a copy and others modify the object in place. The docstrings usually say which, but there may be instances where that documentation is missing.
Well, the point is, even those which return a copy do not return a deep copy in that they still refer to the same context object, which gets modified a lot by those 2D workplane methods. Reading the code has clarified many things, and in the end, I have been able to get my code working using a similar approach as in #327: By managing ctx.pendingWires
and ctx.pendingEdges
manually.
However, IMHO that is an indication that the semantics of the cadquery objects are not sufficiently clearly defined: What does it represent: A workplane? A shape? A 2D sketch? Depending on the operation, either and all of those may be implied and affected. Thus the real answer is "some type of context for CAD operations". That this encompasses "pending" objects, "lists of current objects" (the "stack" as named in the code) as well as a "list of parent objects" (the "stack" as I had understood it from reading the documentation) would need more clarification in the documentation. Furthermore, ideally every operation would have clearly specified semantics for all those elements.
Workplane encodes your modeling history and provides an interface for executing modeling steps (both 3D ops and basic 2D sketching). As you noticed there is a shared context used for aggregating Edges and Wires for sketches. The context also holds information about the tags. In your script you can instantiate many Workplane objects and they will be completely independent.
If you want a lower-level API you can use cq.occ_impl.shapes
for modeling without history or other any goodies. In general CQ does not want to be specifically pythonic; whatever this means in the context of scripted CAD.
In the end you were able to solve the issue at hand, so could share your code? It will for sure help other users more than an abstract discussion about the Workplane semantics.
Sure. I wrote a little helper that tries to remove cadquery completely from managing the pending wires, because I simply couldn't get cadquery to bring me the right wires together:
import contextlib
@contextlib.contextmanager
def collect_pending_wires(workplane):
prev_edges = list(workplane.ctx.pendingEdges)
prev_wires = list(workplane.ctx.pendingWires)
ret = []
yield ret
assert workplane.ctx.pendingEdges == prev_edges
n = len(prev_wires)
assert workplane.ctx.pendingWires[:n] == prev_wires
ret.extend(workplane.ctx.pendingWires[n:])
workplane.ctx.pendingWires = prev_wires
This context manager tries to restore the pending state as it was and hands you any newly created wires. It can be used like so:
base = cq.Workplane("XY").box(10,5,2)
with collect_pending_wires(base) as wire_1:
base.faces("<X").workplane(origin=(0,0,0)).circle(3)
with collect_pending_wires(base) as wire_2:
base.faces(">X").workplane(origin=(1,2,0)).rect(2,3)
base.ctx.pendingWires = wire_1 + wire_2
loft = base.loft(combine=False)
show(cut)
In this simple demonstration you could probably get away with chaining. However, it seems to me, that as soon as you try to use mirrorX
or similar to build the second wire, you'll mess up the first wire. With this approach above, I have the wires under control.
Thanks. Other options would be to add the original Shape
object (base.val()
) to separate workplanes and in that way obtain decoupled contexts. You can always add arbitrary wires using toPending()
.
In the long run we need a separate sketch class (see #42, #52).
Hi all,
For debugging, I would like to trace what is in the CQ stack. So I am considering using the stack API described here.
I have looked also at the code:
I have tried:
So, I can have the size of an object, but how can I have the size of the stack? This is related to my efforts to debug "ValueError: More than one wire is required".