Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

forConstruction option in offset2d #618

Closed
marcus7070 opened this issue Feb 5, 2021 · 6 comments · Fixed by #639
Closed

forConstruction option in offset2d #618

marcus7070 opened this issue Feb 5, 2021 · 6 comments · Fixed by #639
Labels
enhancement New feature or request

Comments

@marcus7070
Copy link
Member

I often want to use Workplane.offset2D to create construction geometry. For example:

board_dims = (100, 50)
w = (
    cq.Workplane()
    .box(*board_dims, 1)
    .faces(">Z")
    .workplane()
    .rect(*board_dims)
    .offset2D(-2.4)
    .vertices()
    .hole(3)
)

Which actually works just fine:
screenshot2021-02-05-112648
Until you go to use w.ctx.pendingWires, for example:

board_dims = (100, 50)
w = (
    cq.Workplane()
    .box(*board_dims, 1)
    .faces(">Z")
    .workplane()
    .rect(*board_dims)
    .offset2D(-2.4)
    .vertices()
    .hole(3)
    .faces(">Z")
    .workplane()
    .circle(10)
    .extrude(1)
)

screenshot2021-02-05-112923
And now you realise offset2D leaves the shape in pendingWires.

Is it worth adding a forConstruction option, similar to rect and other methods?

@adam-urbanczyk
Copy link
Member

+1, good idea.

@adam-urbanczyk adam-urbanczyk added the enhancement New feature or request label Feb 5, 2021
@marcus7070
Copy link
Member Author

I would think a typical use case would be:

w = (
    cq.Workplane
    .rect(10, 10, centered=False, forConstruction=True)
    .offset2D(-1, forConstruction=True)
    .vertices()
    .hole(1)
)

So currently offset2D gets wires from pendingEdges and pendingWires.

cadquery/cadquery/cq.py

Lines 3675 to 3693 in 184a985

def offset2D(
self, d: float, kind: Literal["arc", "intersection", "tangent"] = "arc"
) -> "Workplane":
"""
Creates a 2D offset wire.
:param float d: thickness. Negative thickness denotes offset to inside.
:param kind: offset kind. Use "arc" for rounded and "intersection" for sharp edges (default: "arc")
:return: CQ object with resulting wire(s).
"""
ws = self._consolidateWires()
rv = list(chain.from_iterable(w.offset2D(d, kind) for w in ws))
self.ctx.pendingEdges = []
self.ctx.pendingWires = rv
return self.newObject(rv)

To enable it to work off construction geometry, I think when forConstruction=True, offset2D should take wires from self.objects and not do anything with pendingEdges and pendingWires. Does that sound right @adam-urbanczyk? I want to make sure I get this right because I think this is the first method that transforms construction geometry, and I would hate to set a bad precedent.

@jmwright
Copy link
Member

@marcus7070 Are you blocked on this? You probably won't find a 1-to-1 comparison with offset2d in the codebase, but you could take a look at how forConstruction is handled currently other places to see if you can find a precedent.

@adam-urbanczyk
Copy link
Member

@marcus7070 I'm not sure what the possible issue is. Does something go wrong, if you just set the forConstruction field of the generated wire?

@marcus7070
Copy link
Member Author

marcus7070 commented Feb 14, 2021

Not blocked, thanks.

Out of the two methods (taking edges from self.objects or pending wires/edges when forConstruction=True), I decided that self.objects made more sense to me and wrote the code, tests (marcus7070@5e3fb9e) and an example based off that (marcus7070@e3cdb52).

Then I noticed that Workplane.wire already uses pending edges/wires. So I will rewrite my stuff to keep in line with the existing code.

cadquery/cadquery/cq.py

Lines 2079 to 2117 in d1ebfba

def wire(self, forConstruction: bool = False) -> "Workplane":
"""
Returns a CQ object with all pending edges connected into a wire.
All edges on the stack that can be combined will be combined into a single wire object,
and other objects will remain on the stack unmodified
:param forConstruction: whether the wire should be used to make a solid, or if it is just
for reference
:type forConstruction: boolean. true if the object is only for reference
This method is primarily of use to plugin developers making utilities for 2-d construction.
This method should be called when a user operation implies that 2-d construction is
finished, and we are ready to begin working in 3d
SEE '2-d construction concepts' for a more detailed explanation of how CadQuery handles
edges, wires, etc
Any non edges will still remain.
"""
edges = self.ctx.pendingEdges
# do not consolidate if there are no free edges
if len(edges) == 0:
return self
self.ctx.pendingEdges = []
others = []
for e in self.objects:
if type(e) != Edge:
others.append(e)
w = Wire.assembleEdges(edges)
if not forConstruction:
self._addPendingWire(w)
return self.newObject(others + [w])

@marcus7070
Copy link
Member Author

I'm not sure what the possible issue is.

I had some misconceptions about pending wires and construction geometry. I was thinking that it should be possible to chain construction geometry calls together, like w = Workplane().rect(1, 1, forConstruction=True).offset2D(1, forConstruction=True). I was also thinking that turning pending wires into construction geometry should not be allowed, since pending wires are:

cadquery/cadquery/cq.py

Lines 85 to 87 in d1ebfba

self.pendingWires = (
[]
) # a list of wires that have been created and need to be extruded

But I've come around now, there is no other way to do the simple Workplane().vLine(1).hLine(1).close().offset2D(1, forConstruction=True) except to use pending edges.

I'll get the PR done today or tomorrow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants