Skip to content

Commit 41b0e83

Browse files
committed
Added RadiusNthSelector
1 parent 9a471b3 commit 41b0e83

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed

cadquery/selectors.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,51 @@ def filter(self, objectList):
300300
return r
301301

302302

303+
class RadiusNthSelector(Selector):
304+
"""
305+
Select the object with the Nth radius.
306+
307+
Applicability:
308+
All Edge and Wires.
309+
310+
Will ignore any shape that can not be represented as a circle or an arc of
311+
a circle.
312+
"""
313+
314+
def __init__(self, n, directionMax=True, tolerance=0.0001):
315+
self.N = n
316+
self.directionMax = directionMax
317+
self.TOLERANCE = tolerance
318+
319+
def filter(self, objectList):
320+
# calculate how many digits of precision do we need
321+
digits = -math.floor(math.log10(self.TOLERANCE))
322+
323+
# make a radius dict
324+
# this is one to many mapping so I am using a default dict with list
325+
objectDict = defaultdict(list)
326+
for el in objectList:
327+
try:
328+
rad = el.radius()
329+
except ValueError:
330+
continue
331+
objectDict[round(rad, digits)].append(el)
332+
333+
# choose the Nth unique rounded distance
334+
sortedObjectList = sorted(
335+
list(objectDict.keys()), reverse=not self.directionMax
336+
)
337+
try:
338+
nth_distance = sortedObjectList[self.N]
339+
except IndexError:
340+
raise IndexError(
341+
f"Attempted to access the {self.N}-th radius in a list {len(sortedObjectList)} long"
342+
)
343+
344+
# map back to original objects and return
345+
return objectDict[nth_distance]
346+
347+
303348
class DirectionMinMaxSelector(Selector):
304349
"""
305350
Selects objects closest or farthest in the specified direction

doc/apireference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ as a basis for futher operations.
176176
ParallelDirSelector
177177
DirectionSelector
178178
DirectionNthSelector
179+
RadiusSelector
179180
PerpendicularDirSelector
180181
TypeSelector
181182
DirectionMinMaxSelector

doc/classreference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Selector Classes
6161
ParallelDirSelector
6262
DirectionSelector
6363
DirectionNthSelector
64+
RadiusSelector
6465
PerpendicularDirSelector
6566
TypeSelector
6667
DirectionMinMaxSelector

tests/test_selectors.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,84 @@ def testBox(self):
391391
).vals()
392392
self.assertEqual(1, len(fl))
393393

394+
def testRadiusNthSelector(self):
395+
part = (
396+
Workplane()
397+
.box(10, 10, 1)
398+
.edges(">(1, 1, 0) and |Z")
399+
.fillet(1)
400+
.edges(">(-1, 1, 0) and |Z")
401+
.fillet(1)
402+
.edges(">(-1, -1, 0) and |Z")
403+
.fillet(2)
404+
.edges(">(1, -1, 0) and |Z")
405+
.fillet(3)
406+
.faces(">Z")
407+
)
408+
# smallest radius is 1.0
409+
self.assertAlmostEqual(
410+
part.edges(selectors.RadiusNthSelector(0)).val().radius(), 1.0
411+
)
412+
# there are two edges with the smallest radius
413+
self.assertEqual(len(part.edges(selectors.RadiusNthSelector(0)).vals()), 2)
414+
# next radius is 2.0
415+
self.assertAlmostEqual(
416+
part.edges(selectors.RadiusNthSelector(1)).val().radius(), 2.0
417+
)
418+
# largest radius is 3.0
419+
self.assertAlmostEqual(
420+
part.edges(selectors.RadiusNthSelector(-1)).val().radius(), 3.0
421+
)
422+
# accessing index 3 should be an IndexError
423+
with self.assertRaises(IndexError):
424+
part.edges(selectors.RadiusNthSelector(3))
425+
# reversed
426+
self.assertAlmostEqual(
427+
part.edges(selectors.RadiusNthSelector(0, directionMax=False))
428+
.val()
429+
.radius(),
430+
3.0,
431+
)
432+
433+
# test the selector on wires
434+
wire_circles = (
435+
Workplane()
436+
.circle(2)
437+
.moveTo(10, 0)
438+
.circle(2)
439+
.moveTo(20, 0)
440+
.circle(4)
441+
.consolidateWires()
442+
)
443+
self.assertEqual(
444+
len(wire_circles.wires(selectors.RadiusNthSelector(0)).vals()), 2
445+
)
446+
self.assertEqual(
447+
len(wire_circles.wires(selectors.RadiusNthSelector(1)).vals()), 1
448+
)
449+
self.assertAlmostEqual(
450+
wire_circles.wires(selectors.RadiusNthSelector(0)).val().radius(), 2
451+
)
452+
self.assertAlmostEqual(
453+
wire_circles.wires(selectors.RadiusNthSelector(1)).val().radius(), 4
454+
)
455+
456+
# a polygon with rounded corners has a radius, according to OCCT
457+
loop_wire = Wire.makePolygon(
458+
[Vector(-10, 0, 0), Vector(0, 10, 0), Vector(10, 0, 0),]
459+
)
460+
loop_workplane = (
461+
Workplane().add(loop_wire.offset2D(1)).add(loop_wire.offset2D(2))
462+
)
463+
self.assertAlmostEqual(
464+
loop_workplane.wires(selectors.RadiusNthSelector(0)).val().radius(), 1.0
465+
)
466+
self.assertAlmostEqual(
467+
loop_workplane.wires(selectors.RadiusNthSelector(1)).val().radius(), 2.0
468+
)
469+
with self.assertRaises(IndexError):
470+
loop_workplane.wires(selectors.RadiusNthSelector(2))
471+
394472
def testAndSelector(self):
395473
c = CQ(makeUnitCube())
396474

0 commit comments

Comments
 (0)