-
Notifications
You must be signed in to change notification settings - Fork 289
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
How to cut a lip on a case seam? #684
Comments
You could try selecting the wires and using offset2d to create the lip. |
Thank you @jmwright. I forgot about offset2D. For this experiment I modified the case shape from my previous example to make it non-convex. def get_case_bottom(wp):
return\
split_bottom(
get_inner_volume(wp)
.union(get_inner_volume(wp.move(25,25)))\
.shell(2),
0) Cutting lip with ridge on the outer side of case seam by removing material was easy:
def cut_inner_lip(wp, width):
return\
wp\
.faces(">Z")\
.wires()\
.item(0)\
.toPending()\
.workplane()\
.offset2D(-width)\
.cutBlind(-1)
result = \
cut_inner_lip(
get_case_bottom(
cq.Workplane("XY")),
0.9) But what if I want to add the ridge material without removing part of existing wall? For some reason I can not extrude or cut with one of the top face wires. Their 2D offsets can be cut or extruded but the wires themselves can not. For example I tried to
def add_outer_lip(wp, width):
return\
wp\
.faces(">Z")\
.wires()\
.item(0)\
.toPending()\
.workplane()\
.extrude(1)\
.faces(">Z")\
.wires()\
.item(0)\
.toPending()\
.workplane()\
.offset2D(-width)\
.cutBlind(-1)
result = \
add_outer_lip(
get_case_bottom(
cq.Workplane("XY")),
0.9) This code fails at For some reason if I replace Same thing with simpler geometry works result = \
cq.Workplane("XY")\
.rect(50,50)\
.extrude(50)\
.faces(">Z")\
.shell(2)\
.faces(">Z")\
.wires()\
.item(1)\
.toPending()\
.workplane()\
.extrude(1)\
.faces(">Z")\
.wires()\
.item(0)\
.toPending()\
.workplane()\
.offset2D(-1)\
.cutBlind(-1) Full code of minimal failing exampledef get_inner_volume(wp): return\ wp\ .rect(50,50)\ .extrude(50)\ .edges()\ .fillet(10)def split_bottom( def get_case_bottom(wp): def add_outer_lip(wp, width): result = |
Can you paste the minimal example with correct formatting? |
Sorry. Did not notice that details tag has garbled it. |
This looks like a pretty cool project @fedorkotov! I don't have time to fully read and digest your code, so I might be missing a better solution, but for:
Note that there is a method w = (
# make 2 unioned boxes
cq.Workplane()
.box(10, 10, 2)
.union(
cq.Workplane()
.box(10, 10, 2, centered=(False, False, True))
)
# shell and remove the top face
.faces(">Z")
.shell(-0.5, "intersection")
# select the two wires from the top face
.faces(">Z")
.wires()
)
wires_sorted = w.vals()
wires_sorted.sort(key=lambda x: x.Length())
# select just the outer wire and fillet that edge
w = w.newObject([wires_sorted[1]]).fillet(0.4) @fedorkotov's code here is another use case for #646. |
Method of ordering by length proposed by @marcus7070 sounds cool and easy to implement but it is based on the following implicit assumption For two continuous closed 2D curves Obviously this is not true for all curves. Counterexample is easy to construct. Probably it is true for the curves of interest but it is not obvious (to me) what additional assumptions about A better criterion would be area bounded by the curve. Proposition analogous to above for areas instead of lengths is easy to prove. |
The following converts an arbitrary set of wires to a face and gets the area from that. It uses import cadquery as cq
result = cq.Workplane().rect(10, 10)
face = cq.Face.makeFromWires(result.wires().val())
log(face.Faces()[0].Area()) # logs out 99.99999999999 |
@fedorkotov check |
@jmwright , @adam-urbanczyk thank you for suggestions. I will check them out over the weekend and will share my results |
I wrote a selector that helps select wire with nth area. class WireWithNthAreaSelector(cq.Selector):
def __init__(
self,
idx):
self._idx = idx
def filter(self, objectlist: Sequence[cq.Wire]) -> List[cq.Wire]:
if len(objectlist) == 0:
# nothing to filter
raise ValueError(
"Can not return the Nth nested wire of an empty list")
if(len(objectlist) <= self._idx or
self._idx < -len(objectlist)):
raise IndexError(
"Can not return wire with index {} from list with {} wires"\
.format(self._idx, len(objectlist)))
wires_with_areas = \
[(wire,
cq.Face.makeFromWires(wire).Area())
for wire in objectlist]
wires_sorted_by_area = \
sorted(
wires_with_areas,
key=lambda w_data: w_data[1])
selected_wire, _ = \
wires_sorted_by_area[self._idx]
return [selected_wire] this is how I used it to add inner and outer lips def add_inner_lip(wp, width):
return \
wp\
.faces(">Z")\
.wires(WireWithNthAreaSelector(-1))\
.toPending()\
.workplane()\
.offset2D(-width)\
.extrude(1)\
.faces(">Z[-2]")\
.wires(WireWithNthAreaSelector(0))\
.toPending()\
.workplane()\
.cutBlind(1)
def add_outer_lip(wp, width):
return\
wp\
.faces(">Z")\
.wires(WireWithNthAreaSelector(-1))\
.toPending()\
.workplane()\
.extrude(1)\
.faces(">Z")\
.wires()\
.item(0)\
.toPending()\
.workplane()\
.offset2D(-width)\
.cutBlind(-1) Full source code is attached below Many thanks to everyone for your help. |
Great, a PR would be nice! I will probably propose to generalize it into |
#688 is merged, can this be closed @fedorkotov ? If so, maybe consider adding an example to cq-contrib? |
Yes it can be closed. |
What is the proper way of making lips on a case seam like this?
Note that there is a gap between lips of bottom and top half that is necessary to account for 3D printing inaccuracies.
Below I describe how I do it. Maybe there is a better way?
I thought about sweeping lip cross section along seam boundary but did not find a way to do that without manually redrawing sweeping path which would be tedious for anything but a simple box with sharp corners.
Or maybe someone knows a library that does this automatically?
My method is
(see code below)
The text was updated successfully, but these errors were encountered: