Skip to content

Commit f0308ce

Browse files
authored
Merge pull request #678 from marcus7070/getPending
popPendingEdges and Wires
2 parents ceea71d + d7e7812 commit f0308ce

File tree

2 files changed

+109
-43
lines changed

2 files changed

+109
-43
lines changed

cadquery/cq.py

Lines changed: 43 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,30 @@ def __init__(self):
9393
self.tolerance = 0.0001 # user specified tolerance
9494
self.tags = {}
9595

96+
def popPendingEdges(self, errorOnEmpty: bool = True) -> List[Edge]:
97+
"""
98+
Get and clear pending edges.
99+
100+
:raises ValueError: if errorOnEmpty is True and no edges are present.
101+
"""
102+
if errorOnEmpty and not self.pendingEdges:
103+
raise ValueError("No pending edges present")
104+
out = self.pendingEdges
105+
self.pendingEdges = []
106+
return out
107+
108+
def popPendingWires(self, errorOnEmpty: bool = True) -> List[Wire]:
109+
"""
110+
Get and clear pending wires.
111+
112+
:raises ValueError: if errorOnEmpty is True and no wires are present.
113+
"""
114+
if errorOnEmpty and not self.pendingWires:
115+
raise ValueError("No pending wires present")
116+
out = self.pendingWires
117+
self.pendingWires = []
118+
return out
119+
96120

97121
class Workplane(object):
98122
"""
@@ -2099,6 +2123,8 @@ def _addPendingWire(self, wire: Wire) -> None:
20992123

21002124
def _consolidateWires(self) -> List[Wire]:
21012125

2126+
# note: do not use CQContext.popPendingEdges or Wires here, this method does not
2127+
# clear pending edges or wires.
21022128
wires = cast(
21032129
List[Union[Edge, Wire]],
21042130
[el for el in chain(self.ctx.pendingEdges, self.ctx.pendingWires)],
@@ -2136,39 +2162,33 @@ def wire(self, forConstruction: bool = False) -> "Workplane":
21362162
Returns a CQ object with all pending edges connected into a wire.
21372163
21382164
All edges on the stack that can be combined will be combined into a single wire object,
2139-
and other objects will remain on the stack unmodified
2165+
and other objects will remain on the stack unmodified. If there are no pending edges,
2166+
this method will just return self.
21402167
21412168
:param forConstruction: whether the wire should be used to make a solid, or if it is just
21422169
for reference
2143-
:type forConstruction: boolean. true if the object is only for reference
21442170
21452171
This method is primarily of use to plugin developers making utilities for 2-d construction.
21462172
This method should be called when a user operation implies that 2-d construction is
2147-
finished, and we are ready to begin working in 3d
2173+
finished, and we are ready to begin working in 3d.
21482174
21492175
SEE '2-d construction concepts' for a more detailed explanation of how CadQuery handles
2150-
edges, wires, etc
2176+
edges, wires, etc.
21512177
21522178
Any non edges will still remain.
21532179
"""
21542180

2155-
edges = self.ctx.pendingEdges
2156-
21572181
# do not consolidate if there are no free edges
2158-
if len(edges) == 0:
2182+
if len(self.ctx.pendingEdges) == 0:
21592183
return self
21602184

2161-
self.ctx.pendingEdges = []
2162-
2163-
others = []
2164-
for e in self.objects:
2165-
if type(e) != Edge:
2166-
others.append(e)
2167-
2185+
edges = self.ctx.popPendingEdges()
21682186
w = Wire.assembleEdges(edges)
21692187
if not forConstruction:
21702188
self._addPendingWire(w)
21712189

2190+
others = [e for e in self.objects if not isinstance(e, Edge)]
2191+
21722192
return self.newObject(others + [w])
21732193

21742194
def each(
@@ -2734,10 +2754,7 @@ def twistExtrude(
27342754
"""
27352755
# group wires together into faces based on which ones are inside the others
27362756
# result is a list of lists
2737-
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires))
2738-
2739-
# now all of the wires have been used to create an extrusion
2740-
self.ctx.pendingWires = []
2757+
wireSets = sortWiresByBuildOrder(self.ctx.popPendingWires())
27412758

27422759
# compute extrusion vector and extrude
27432760
eDir = self.plane.zDir.multiply(distance)
@@ -3187,13 +3204,12 @@ def cutThruAll(self, clean: bool = True, taper: float = 0) -> "Workplane":
31873204
31883205
:param boolean clean: call :py:meth:`clean` afterwards to have a clean shape
31893206
:raises ValueError: if there is no solid to subtract from in the chain
3207+
:raises ValueError: if there are no pending wires to cut with
31903208
:return: a CQ object with the resulting object selected
31913209
31923210
see :py:meth:`cutBlind` to cut material to a limited depth
31933211
"""
3194-
wires = self.ctx.pendingWires
3195-
self.ctx.pendingWires = []
3196-
3212+
wires = self.ctx.popPendingWires()
31973213
solidRef = self.findSolid()
31983214

31993215
rv = []
@@ -3214,8 +3230,7 @@ def loft(
32143230
Make a lofted solid, through the set of wires.
32153231
:return: a CQ object containing the created loft
32163232
"""
3217-
wiresToLoft = self.ctx.pendingWires
3218-
self.ctx.pendingWires = []
3233+
wiresToLoft = self.ctx.popPendingWires()
32193234

32203235
r: Shape = Solid.makeLoft(wiresToLoft, ruled)
32213236

@@ -3245,9 +3260,7 @@ def _extrude(
32453260
# group wires together into faces based on which ones are inside the others
32463261
# result is a list of lists
32473262

3248-
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires))
3249-
# now all of the wires have been used to create an extrusion
3250-
self.ctx.pendingWires = []
3263+
wireSets = sortWiresByBuildOrder(self.ctx.popPendingWires())
32513264

32523265
# compute extrusion vector and extrude
32533266
eDir = self.plane.zDir.multiply(distance)
@@ -3293,11 +3306,8 @@ def _revolve(
32933306
32943307
This method is a utility method, primarily for plugin and internal use.
32953308
"""
3296-
# We have to gather the wires to be revolved
3297-
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires))
3298-
3299-
# Mark that all of the wires have been used to create a revolution
3300-
self.ctx.pendingWires = []
3309+
# Get the wires to be revolved
3310+
wireSets = sortWiresByBuildOrder(self.ctx.popPendingWires())
33013311

33023312
# Revolve the wires, make a compound out of them and then fuse them
33033313
toFuse = []
@@ -3350,19 +3360,17 @@ def _sweep(
33503360
mode = wire
33513361

33523362
if not multisection:
3353-
wireSets = sortWiresByBuildOrder(list(self.ctx.pendingWires))
3363+
wireSets = sortWiresByBuildOrder(self.ctx.popPendingWires())
33543364
for ws in wireSets:
33553365
thisObj = Solid.sweep(
33563366
ws[0], ws[1:], p, makeSolid, isFrenet, mode, transition
33573367
)
33583368
toFuse.append(thisObj)
33593369
else:
3360-
sections = self.ctx.pendingWires
3370+
sections = self.ctx.popPendingWires()
33613371
thisObj = Solid.sweep_multi(sections, p, makeSolid, isFrenet, mode)
33623372
toFuse.append(thisObj)
33633373

3364-
self.ctx.pendingWires = []
3365-
33663374
return Compound.makeCompound(toFuse)
33673375

33683376
def interpPlate(

tests/test_cadquery.py

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -394,8 +394,7 @@ def testRect(self):
394394

395395
def testLoft(self):
396396
"""
397-
Test making a lofted solid
398-
:return:
397+
Test making a lofted solid
399398
"""
400399
s = Workplane("XY").circle(4.0).workplane(5.0).rect(2.0, 2.0).loft()
401400
self.saveModel(s)
@@ -405,10 +404,13 @@ def testLoft(self):
405404
# the resulting loft had a split on the side, not sure why really, i expected only 3 faces
406405
self.assertEqual(7, s.faces().size())
407406

408-
def testLoftWithOneWireRaisesValueError(self):
409-
s = Workplane("XY").circle(5)
407+
def testLoftRaisesValueError(self):
408+
s0 = Workplane().hLine(1) # no wires
409+
with raises(ValueError):
410+
s0.loft()
411+
s1 = Workplane("XY").circle(5) # one wire
410412
with self.assertRaises(ValueError) as cm:
411-
s.loft()
413+
s1.loft()
412414
err = cm.exception
413415
self.assertEqual(str(err), "More than one wire is required")
414416

@@ -551,6 +553,14 @@ def testRevolveCone(self):
551553
self.assertEqual(2, result.vertices().size())
552554
self.assertEqual(2, result.edges().size())
553555

556+
def testRevolveErrors(self):
557+
"""
558+
Test that revolve raises errors when used incorrectly.
559+
"""
560+
result = Workplane("XY").lineTo(0, 10).lineTo(5, 0)
561+
with raises(ValueError):
562+
result.revolve()
563+
554564
def testSpline(self):
555565
"""
556566
Tests construction of splines
@@ -1040,6 +1050,11 @@ def testSweep(self):
10401050

10411051
self.assertAlmostEqual(v1.getAngle(v2), math.pi / 4, 6)
10421052

1053+
# test for ValueError if pending wires is empty
1054+
w0 = Workplane().hLine(1).vLine(1)
1055+
with raises(ValueError):
1056+
w0.sweep(path)
1057+
10431058
# Test aux spine invalid input handling
10441059
with raises(ValueError):
10451060
result = (
@@ -1655,10 +1670,13 @@ def testCutThroughAll(self):
16551670
self.assertTrue(r.val().isValid())
16561671
self.assertEqual(r.faces().size(), 7)
16571672

1658-
# test ValueError when no solids found
1659-
w0 = Workplane().hLine(1).vLine(1).close()
1673+
# test errors
1674+
box0 = Workplane().box(1, 1, 1).faces(">Z").workplane().hLine(1)
16601675
with raises(ValueError):
1661-
w0.cutThruAll()
1676+
box0.cutThruAll()
1677+
no_box = Workplane().hLine(1).vLine(1).close()
1678+
with raises(ValueError):
1679+
no_box.cutThruAll()
16621680

16631681
def testCutToFaceOffsetNOTIMPLEMENTEDYET(self):
16641682
"""
@@ -4256,6 +4274,46 @@ def testFindFace(self):
42564274
with raises(ValueError):
42574275
w2.findFace(searchParents=False)
42584276

4277+
def testPopPending(self):
4278+
# test pending edges
4279+
w0 = Workplane().hLine(1)
4280+
self.assertEqual(len(w0.ctx.pendingEdges), 1)
4281+
edges = w0.ctx.popPendingEdges()
4282+
self.assertEqual(len(edges), 1)
4283+
self.assertEqual(edges[0], w0.val())
4284+
# pending edges should now be cleared
4285+
self.assertEqual(len(w0.ctx.pendingEdges), 0)
4286+
4287+
# test pending wires
4288+
w1 = Workplane().hLine(1).vLine(1).close()
4289+
wire = w1.val()
4290+
self.assertEqual(w1.ctx.pendingWires[0], wire)
4291+
pop_pending_output = w1.ctx.popPendingWires()
4292+
self.assertEqual(pop_pending_output[0], wire)
4293+
# pending wires should now be cleared
4294+
self.assertEqual(len(w1.ctx.pendingWires), 0)
4295+
4296+
# test error when empty pending edges
4297+
w2 = Workplane()
4298+
# the following 2 should not raise an exception
4299+
w2.ctx.popPendingEdges(errorOnEmpty=False)
4300+
w2.ctx.popPendingWires(errorOnEmpty=False)
4301+
4302+
# empty edges
4303+
w3 = Workplane().hLine(1).vLine(1).close()
4304+
with self.assertRaises(ValueError):
4305+
w3.ctx.popPendingEdges()
4306+
4307+
# empty wires
4308+
w4 = Workplane().circle(1).extrude(1)
4309+
with self.assertRaises(ValueError):
4310+
w4.ctx.popPendingWires()
4311+
4312+
# test via cutBlind
4313+
w5 = Workplane().circle(1).extrude(1)
4314+
with self.assertRaises(ValueError):
4315+
w5.cutBlind(-1)
4316+
42594317
def testCompSolid(self):
42604318

42614319
from OCP.BRepPrimAPI import BRepPrimAPI_MakePrism

0 commit comments

Comments
 (0)