753 lines
27 KiB
Python
753 lines
27 KiB
Python
__author__ = "dcowden"
|
|
|
|
"""
|
|
Tests for CadQuery Selectors
|
|
|
|
These tests do not construct any solids, they test only selectors that query
|
|
an existing solid
|
|
|
|
"""
|
|
|
|
import math
|
|
import unittest
|
|
import sys
|
|
import os.path
|
|
|
|
# my modules
|
|
from tests import BaseTest, makeUnitCube, makeUnitSquareWire
|
|
from cadquery import *
|
|
from cadquery import selectors
|
|
|
|
|
|
class TestCQSelectors(BaseTest):
|
|
def testWorkplaneCenter(self):
|
|
"Test Moving workplane center"
|
|
s = Workplane(Plane.XY())
|
|
|
|
# current point and world point should be equal
|
|
self.assertTupleAlmostEquals((0.0, 0.0, 0.0), s.plane.origin.toTuple(), 3)
|
|
|
|
# move origin and confirm center moves
|
|
s = s.center(-2.0, -2.0)
|
|
|
|
# current point should be 0,0, but
|
|
|
|
self.assertTupleAlmostEquals((-2.0, -2.0, 0.0), s.plane.origin.toTuple(), 3)
|
|
|
|
def testVertices(self):
|
|
t = makeUnitSquareWire() # square box
|
|
c = CQ(t)
|
|
|
|
self.assertEqual(4, c.vertices().size())
|
|
self.assertEqual(4, c.edges().size())
|
|
self.assertEqual(0, c.vertices().edges().size()) # no edges on any vertices
|
|
# but selecting all edges still yields all vertices
|
|
self.assertEqual(4, c.edges().vertices().size())
|
|
self.assertEqual(1, c.wires().size()) # just one wire
|
|
self.assertEqual(0, c.faces().size())
|
|
# odd combinations all work but yield no results
|
|
self.assertEqual(0, c.vertices().faces().size())
|
|
self.assertEqual(0, c.edges().faces().size())
|
|
self.assertEqual(0, c.edges().vertices().faces().size())
|
|
|
|
def testEnd(self):
|
|
c = CQ(makeUnitSquareWire())
|
|
# 4 because there are 4 vertices
|
|
self.assertEqual(4, c.vertices().size())
|
|
# 1 because we started with 1 wire
|
|
self.assertEqual(1, c.vertices().end().size())
|
|
|
|
def testAll(self):
|
|
"all returns a list of CQ objects, so that you can iterate over them individually"
|
|
c = CQ(makeUnitCube())
|
|
self.assertEqual(6, c.faces().size())
|
|
self.assertEqual(6, len(c.faces().all()))
|
|
self.assertEqual(4, c.faces().all()[0].vertices().size())
|
|
|
|
def testFirst(self):
|
|
c = CQ(makeUnitCube())
|
|
self.assertEqual(type(c.vertices().first().val()), Vertex)
|
|
self.assertEqual(type(c.vertices().first().first().first().val()), Vertex)
|
|
|
|
def testCompounds(self):
|
|
c = CQ(makeUnitSquareWire())
|
|
self.assertEqual(0, c.compounds().size())
|
|
self.assertEqual(0, c.shells().size())
|
|
self.assertEqual(0, c.solids().size())
|
|
|
|
def testSolid(self):
|
|
c = CQ(makeUnitCube(False))
|
|
# make sure all the counts are right for a cube
|
|
self.assertEqual(1, c.solids().size())
|
|
self.assertEqual(6, c.faces().size())
|
|
self.assertEqual(12, c.edges().size())
|
|
self.assertEqual(8, c.vertices().size())
|
|
self.assertEqual(0, c.compounds().size())
|
|
|
|
# now any particular face should result in 4 edges and four vertices
|
|
self.assertEqual(4, c.faces().first().edges().size())
|
|
self.assertEqual(1, c.faces().first().size())
|
|
self.assertEqual(4, c.faces().first().vertices().size())
|
|
|
|
self.assertEqual(4, c.faces().last().edges().size())
|
|
|
|
def testFaceTypesFilter(self):
|
|
"Filters by face type"
|
|
c = CQ(makeUnitCube())
|
|
self.assertEqual(c.faces().size(), c.faces("%PLANE").size())
|
|
self.assertEqual(c.faces().size(), c.faces("%plane").size())
|
|
self.assertEqual(0, c.faces("%sphere").size())
|
|
self.assertEqual(0, c.faces("%cone").size())
|
|
self.assertEqual(0, c.faces("%SPHERE").size())
|
|
|
|
def testPerpendicularDirFilter(self):
|
|
c = CQ(makeUnitCube())
|
|
|
|
perp_edges = c.edges("#Z")
|
|
self.assertEqual(8, perp_edges.size()) # 8 edges are perp. to z
|
|
# dot product of perpendicular vectors is zero
|
|
for e in perp_edges.vals():
|
|
self.assertAlmostEqual(e.tangentAt(0).dot(Vector(0, 0, 1)), 0.0)
|
|
perp_faces = c.faces("#Z")
|
|
self.assertEqual(4, perp_faces.size()) # 4 faces are perp to z too!
|
|
for f in perp_faces.vals():
|
|
self.assertAlmostEqual(f.normalAt(None).dot(Vector(0, 0, 1)), 0.0)
|
|
|
|
def testFaceDirFilter(self):
|
|
c = CQ(makeUnitCube())
|
|
# a cube has one face in each direction
|
|
self.assertEqual(1, c.faces("+Z").size())
|
|
self.assertTupleAlmostEquals(
|
|
(0, 0, 1), c.faces("+Z").val().Center().toTuple(), 3
|
|
)
|
|
self.assertEqual(1, c.faces("-Z").size())
|
|
self.assertTupleAlmostEquals(
|
|
(0, 0, 0), c.faces("-Z").val().Center().toTuple(), 3
|
|
)
|
|
self.assertEqual(1, c.faces("+X").size())
|
|
self.assertTupleAlmostEquals(
|
|
(0.5, 0, 0.5), c.faces("+X").val().Center().toTuple(), 3
|
|
)
|
|
self.assertEqual(1, c.faces("-X").size())
|
|
self.assertTupleAlmostEquals(
|
|
(-0.5, 0, 0.5), c.faces("-X").val().Center().toTuple(), 3
|
|
)
|
|
self.assertEqual(1, c.faces("+Y").size())
|
|
self.assertTupleAlmostEquals(
|
|
(0, 0.5, 0.5), c.faces("+Y").val().Center().toTuple(), 3
|
|
)
|
|
self.assertEqual(1, c.faces("-Y").size())
|
|
self.assertTupleAlmostEquals(
|
|
(0, -0.5, 0.5), c.faces("-Y").val().Center().toTuple(), 3
|
|
)
|
|
self.assertEqual(0, c.faces("XY").size())
|
|
self.assertEqual(1, c.faces("X").size()) # should be same as +X
|
|
self.assertEqual(c.faces("+X").val().Center(), c.faces("X").val().Center())
|
|
self.assertNotEqual(c.faces("+X").val().Center(), c.faces("-X").val().Center())
|
|
|
|
def testParallelPlaneFaceFilter(self):
|
|
c = CQ(makeUnitCube(centered=False))
|
|
|
|
# faces parallel to Z axis
|
|
# these two should produce the same behaviour:
|
|
for s in ["|Z", selectors.ParallelDirSelector(Vector(0, 0, 1))]:
|
|
parallel_faces = c.faces(s)
|
|
self.assertEqual(2, parallel_faces.size())
|
|
for f in parallel_faces.vals():
|
|
self.assertAlmostEqual(abs(f.normalAt(None).dot(Vector(0, 0, 1))), 1)
|
|
self.assertEqual(
|
|
2, c.faces(selectors.ParallelDirSelector(Vector((0, 0, -1)))).size()
|
|
) # same thing as above
|
|
|
|
# just for fun, vertices on faces parallel to z
|
|
self.assertEqual(8, c.faces("|Z").vertices().size())
|
|
|
|
# check that the X & Y center of these faces is the same as the box (ie. we haven't selected the wrong face)
|
|
faces = c.faces(selectors.ParallelDirSelector(Vector((0, 0, 1)))).vals()
|
|
for f in faces:
|
|
c = f.Center()
|
|
self.assertAlmostEqual(c.x, 0.5)
|
|
self.assertAlmostEqual(c.y, 0.5)
|
|
|
|
def testParallelEdgeFilter(self):
|
|
c = CQ(makeUnitCube())
|
|
for sel, vec in zip(
|
|
["|X", "|Y", "|Z"], [Vector(1, 0, 0), Vector(0, 1, 0), Vector(0, 0, 1)]
|
|
):
|
|
edges = c.edges(sel)
|
|
# each direction should have 4 edges
|
|
self.assertEqual(4, edges.size())
|
|
# each edge should be parallel with vec and have a cross product with a length of 0
|
|
for e in edges.vals():
|
|
self.assertAlmostEqual(e.tangentAt(0).cross(vec).Length, 0.0)
|
|
|
|
def testCenterNthSelector(self):
|
|
sel = selectors.CenterNthSelector
|
|
c = CQ(makeUnitCube(centered=True))
|
|
|
|
bottom_face = c.faces(sel(0, Vector(0, 0, 1)))
|
|
self.assertEqual(bottom_face.size(), 1)
|
|
self.assertTupleAlmostEquals((0, 0, 0), bottom_face.val().Center().toTuple(), 3)
|
|
|
|
side_faces = c.faces(sel(1, Vector(0, 0, 1)))
|
|
self.assertEqual(side_faces.size(), 4)
|
|
for f in side_faces.vals():
|
|
self.assertAlmostEqual(0.5, f.Center().z)
|
|
|
|
top_face = c.faces(sel(2, Vector(0, 0, 1)))
|
|
self.assertEqual(top_face.size(), 1)
|
|
self.assertTupleAlmostEquals((0, 0, 1), top_face.val().Center().toTuple(), 3)
|
|
|
|
with self.assertRaises(IndexError):
|
|
c.faces(sel(3, Vector(0, 0, 1)))
|
|
|
|
left_face = c.faces(sel(0, Vector(1, 0, 0)))
|
|
self.assertEqual(left_face.size(), 1)
|
|
self.assertTupleAlmostEquals(
|
|
(-0.5, 0, 0.5), left_face.val().Center().toTuple(), 3
|
|
)
|
|
|
|
middle_faces = c.faces(sel(1, Vector(1, 0, 0)))
|
|
self.assertEqual(middle_faces.size(), 4)
|
|
for f in middle_faces.vals():
|
|
self.assertAlmostEqual(0, f.Center().x)
|
|
|
|
right_face = c.faces(sel(2, Vector(1, 0, 0)))
|
|
self.assertEqual(right_face.size(), 1)
|
|
self.assertTupleAlmostEquals(
|
|
(0.5, 0, 0.5), right_face.val().Center().toTuple(), 3
|
|
)
|
|
|
|
with self.assertRaises(IndexError):
|
|
c.faces(sel(3, Vector(1, 0, 0)))
|
|
|
|
# lower corner faces
|
|
self.assertEqual(c.faces(sel(0, Vector(1, 1, 1))).size(), 3)
|
|
# upper corner faces
|
|
self.assertEqual(c.faces(sel(1, Vector(1, 1, 1))).size(), 3)
|
|
with self.assertRaises(IndexError):
|
|
c.faces(sel(2, Vector(1, 1, 1)))
|
|
|
|
for idx, z_val in zip([0, 1, 2], [0, 0.5, 1]):
|
|
edges = c.edges(sel(idx, Vector(0, 0, 1)))
|
|
self.assertEqual(edges.size(), 4)
|
|
for e in edges.vals():
|
|
self.assertAlmostEqual(z_val, e.Center().z)
|
|
with self.assertRaises(IndexError):
|
|
c.edges(sel(3, Vector(0, 0, 1)))
|
|
|
|
for idx, z_val in zip([0, 1], [0, 1]):
|
|
vertices = c.vertices(sel(idx, Vector(0, 0, 1)))
|
|
self.assertEqual(vertices.size(), 4)
|
|
for e in vertices.vals():
|
|
self.assertAlmostEqual(z_val, e.Z)
|
|
with self.assertRaises(IndexError):
|
|
c.vertices(sel(3, Vector(0, 0, 1)))
|
|
|
|
# select a non-linear edge
|
|
part = (
|
|
Workplane()
|
|
.rect(10, 10, centered=False)
|
|
.extrude(1)
|
|
.faces(">Z")
|
|
.workplane(centerOption="CenterOfMass")
|
|
.move(-3, 0)
|
|
.hole(2)
|
|
)
|
|
hole = part.faces(">Z").edges(sel(1, Vector(1, 0, 0)))
|
|
# have we selected a single hole?
|
|
self.assertEqual(1, hole.size())
|
|
self.assertAlmostEqual(1, hole.val().radius())
|
|
|
|
# select solids
|
|
box0 = Workplane().box(1, 1, 1, centered=(True, True, True))
|
|
box1 = Workplane("XY", origin=(10, 10, 10)).box(
|
|
1, 1, 1, centered=(True, True, True)
|
|
)
|
|
part = box0.add(box1)
|
|
self.assertEqual(part.solids().size(), 2)
|
|
for direction in [(0, 0, 1), (0, 1, 0), (1, 0, 0)]:
|
|
box0_selected = part.solids(sel(0, Vector(direction)))
|
|
self.assertEqual(1, box0_selected.size())
|
|
self.assertTupleAlmostEquals(
|
|
(0, 0, 0), box0_selected.val().Center().toTuple(), 3
|
|
)
|
|
box1_selected = part.solids(sel(1, Vector(direction)))
|
|
self.assertEqual(1, box0_selected.size())
|
|
self.assertTupleAlmostEquals(
|
|
(10, 10, 10), box1_selected.val().Center().toTuple(), 3
|
|
)
|
|
|
|
def testMaxDistance(self):
|
|
c = CQ(makeUnitCube())
|
|
|
|
# should select the topmost face
|
|
self.assertEqual(1, c.faces(">Z").size())
|
|
self.assertEqual(4, c.faces(">Z").vertices().size())
|
|
|
|
# vertices should all be at z=1, if this is the top face
|
|
self.assertEqual(4, len(c.faces(">Z").vertices().vals()))
|
|
for v in c.faces(">Z").vertices().vals():
|
|
self.assertAlmostEqual(1.0, v.Z, 3)
|
|
|
|
# test the case of multiple objects at the same distance
|
|
el = c.edges(">Z").vals()
|
|
self.assertEqual(4, len(el))
|
|
for e in el:
|
|
self.assertAlmostEqual(e.Center().z, 1)
|
|
|
|
def testMinDistance(self):
|
|
c = CQ(makeUnitCube())
|
|
|
|
# should select the bottom face
|
|
self.assertEqual(1, c.faces("<Z").size())
|
|
self.assertEqual(4, c.faces("<Z").vertices().size())
|
|
|
|
# vertices should all be at z=0, if this is the bottom face
|
|
self.assertEqual(4, len(c.faces("<Z").vertices().vals()))
|
|
for v in c.faces("<Z").vertices().vals():
|
|
self.assertAlmostEqual(0.0, v.Z, 3)
|
|
|
|
# test the case of multiple objects at the same distance
|
|
el = c.edges("<Z").vals()
|
|
self.assertEqual(4, len(el))
|
|
for e in el:
|
|
self.assertAlmostEqual(e.Center().z, 0)
|
|
|
|
def testNthDistance(self):
|
|
c = Workplane("XY").pushPoints([(-2, 0), (2, 0)]).box(1, 1, 1)
|
|
|
|
# 2nd face
|
|
val = c.faces(selectors.DirectionNthSelector(Vector(1, 0, 0), 1)).val()
|
|
self.assertAlmostEqual(val.Center().x, -1.5)
|
|
|
|
# 2nd face with inversed selection vector
|
|
val = c.faces(selectors.DirectionNthSelector(Vector(-1, 0, 0), 1)).val()
|
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
|
|
|
# 2nd last face
|
|
val = c.faces(selectors.DirectionNthSelector(Vector(1, 0, 0), -2)).val()
|
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
|
|
|
# Last face
|
|
val = c.faces(selectors.DirectionNthSelector(Vector(1, 0, 0), -1)).val()
|
|
self.assertAlmostEqual(val.Center().x, 2.5)
|
|
|
|
# check if the selected face if normal to the specified Vector
|
|
self.assertAlmostEqual(val.normalAt().cross(Vector(1, 0, 0)).Length, 0.0)
|
|
|
|
# repeat the test using string based selector
|
|
|
|
# 2nd face
|
|
val = c.faces(">(1,0,0)[1]").val()
|
|
self.assertAlmostEqual(val.Center().x, -1.5)
|
|
val = c.faces(">X[1]").val()
|
|
self.assertAlmostEqual(val.Center().x, -1.5)
|
|
|
|
# 2nd face with inversed selection vector
|
|
val = c.faces(">(-1,0,0)[1]").val()
|
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
|
val = c.faces("<X[1]").val()
|
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
|
|
|
# 2nd last face
|
|
val = c.faces(">X[-2]").val()
|
|
self.assertAlmostEqual(val.Center().x, 1.5)
|
|
|
|
# Last face
|
|
val = c.faces(">X[-1]").val()
|
|
self.assertAlmostEqual(val.Center().x, 2.5)
|
|
|
|
# check if the selected face if normal to the specified Vector
|
|
self.assertAlmostEqual(val.normalAt().cross(Vector(1, 0, 0)).Length, 0.0)
|
|
|
|
# test selection of multiple faces with the same distance
|
|
c = (
|
|
Workplane("XY")
|
|
.box(1, 4, 1, centered=(False, True, False))
|
|
.faces("<Z")
|
|
.box(2, 2, 2, centered=(True, True, False))
|
|
.faces(">Z")
|
|
.box(1, 1, 1, centered=(True, True, False))
|
|
)
|
|
|
|
# select 2nd from the bottom (NB python indexing is 0-based)
|
|
vals = c.faces(">Z[1]").vals()
|
|
self.assertEqual(len(vals), 2)
|
|
|
|
val = c.faces(">Z[1]").val()
|
|
self.assertAlmostEqual(val.Center().z, 1)
|
|
|
|
# do the same but by selecting 3rd from the top
|
|
vals = c.faces("<Z[2]").vals()
|
|
self.assertEqual(len(vals), 2)
|
|
|
|
val = c.faces("<Z[2]").val()
|
|
self.assertAlmostEqual(val.Center().z, 1)
|
|
|
|
# do the same but by selecting 2nd last from the bottom
|
|
vals = c.faces("<Z[-2]").vals()
|
|
self.assertEqual(len(vals), 2)
|
|
|
|
val = c.faces("<Z[-2]").val()
|
|
self.assertAlmostEqual(val.Center().z, 1)
|
|
|
|
# note that .val() will return the workplane center if the objects list
|
|
# is empty, so to make sure this test fails with a selector that
|
|
# selects nothing, use .vals()[0]
|
|
# verify that <Z[-1] is equivalent to <Z
|
|
val1 = c.faces("<Z[-1]").vals()[0]
|
|
val2 = c.faces("<Z").vals()[0]
|
|
self.assertTupleAlmostEquals(
|
|
val1.Center().toTuple(), val2.Center().toTuple(), 3
|
|
)
|
|
|
|
# verify that >Z[-1] is equivalent to >Z
|
|
val1 = c.faces(">Z[-1]").vals()[0]
|
|
val2 = c.faces(">Z").vals()[0]
|
|
self.assertTupleAlmostEquals(
|
|
val1.Center().toTuple(), val2.Center().toTuple(), 3
|
|
)
|
|
|
|
# DirectionNthSelector should not select faces that are not perpendicular
|
|
twisted_boxes = (
|
|
Workplane()
|
|
.box(1, 1, 1, centered=(True, True, False))
|
|
.transformed(rotate=(45, 0, 0), offset=(0, 0, 3))
|
|
.box(1, 1, 1)
|
|
)
|
|
self.assertTupleAlmostEquals(
|
|
twisted_boxes.faces(">Z[-1]").val().Center().toTuple(), (0, 0, 1), 3
|
|
)
|
|
# this should select a face on the upper/rotated cube, not the lower/unrotated cube
|
|
self.assertGreater(twisted_boxes.faces("<(0, 1, 1)[-1]").val().Center().z, 1)
|
|
# verify that >Z[-1] is equivalent to >Z
|
|
self.assertTupleAlmostEquals(
|
|
twisted_boxes.faces(">(0, 1, 1)[0]").vals()[0].Center().toTuple(),
|
|
twisted_boxes.faces("<(0, 1, 1)[-1]").vals()[0].Center().toTuple(),
|
|
3,
|
|
)
|
|
|
|
def testNearestTo(self):
|
|
c = CQ(makeUnitCube(centered=False))
|
|
|
|
# nearest vertex to origin is (0,0,0)
|
|
t = (0.1, 0.1, 0.1)
|
|
|
|
v = c.vertices(selectors.NearestToPointSelector(t)).vals()[0]
|
|
self.assertTupleAlmostEquals((0.0, 0.0, 0.0), (v.X, v.Y, v.Z), 3)
|
|
|
|
t = (0.1, 0.1, 0.2)
|
|
# nearest edge is the vertical side edge, 0,0,0 -> 0,0,1
|
|
e = c.edges(selectors.NearestToPointSelector(t)).vals()[0]
|
|
v = c.edges(selectors.NearestToPointSelector(t)).vertices().vals()
|
|
self.assertEqual(2, len(v))
|
|
|
|
# nearest solid is myself
|
|
s = c.solids(selectors.NearestToPointSelector(t)).vals()
|
|
self.assertEqual(1, len(s))
|
|
|
|
def testBox(self):
|
|
c = CQ(makeUnitCube(centered=False))
|
|
|
|
# test vertice selection
|
|
test_data_vertices = [
|
|
# box point0, box point1, selected vertice
|
|
((0.9, 0.9, 0.9), (1.1, 1.1, 1.1), (1.0, 1.0, 1.0)),
|
|
((-0.1, 0.9, 0.9), (0.9, 1.1, 1.1), (0.0, 1.0, 1.0)),
|
|
((-0.1, -0.1, 0.9), (0.1, 0.1, 1.1), (0.0, 0.0, 1.0)),
|
|
((-0.1, -0.1, -0.1), (0.1, 0.1, 0.1), (0.0, 0.0, 0.0)),
|
|
((0.9, -0.1, -0.1), (1.1, 0.1, 0.1), (1.0, 0.0, 0.0)),
|
|
((0.9, 0.9, -0.1), (1.1, 1.1, 0.1), (1.0, 1.0, 0.0)),
|
|
((-0.1, 0.9, -0.1), (0.1, 1.1, 0.1), (0.0, 1.0, 0.0)),
|
|
((0.9, -0.1, 0.9), (1.1, 0.1, 1.1), (1.0, 0.0, 1.0)),
|
|
]
|
|
|
|
for d in test_data_vertices:
|
|
vl = c.vertices(selectors.BoxSelector(d[0], d[1])).vals()
|
|
self.assertEqual(1, len(vl))
|
|
v = vl[0]
|
|
self.assertTupleAlmostEquals(d[2], (v.X, v.Y, v.Z), 3)
|
|
|
|
# this time box points are swapped
|
|
vl = c.vertices(selectors.BoxSelector(d[1], d[0])).vals()
|
|
self.assertEqual(1, len(vl))
|
|
v = vl[0]
|
|
self.assertTupleAlmostEquals(d[2], (v.X, v.Y, v.Z), 3)
|
|
|
|
# test multiple vertices selection
|
|
vl = c.vertices(
|
|
selectors.BoxSelector((-0.1, -0.1, 0.9), (0.1, 1.1, 1.1))
|
|
).vals()
|
|
self.assertEqual(2, len(vl))
|
|
vl = c.vertices(
|
|
selectors.BoxSelector((-0.1, -0.1, -0.1), (0.1, 1.1, 1.1))
|
|
).vals()
|
|
self.assertEqual(4, len(vl))
|
|
|
|
# test edge selection
|
|
test_data_edges = [
|
|
# box point0, box point1, edge center
|
|
((0.4, -0.1, -0.1), (0.6, 0.1, 0.1), (0.5, 0.0, 0.0)),
|
|
((-0.1, -0.1, 0.4), (0.1, 0.1, 0.6), (0.0, 0.0, 0.5)),
|
|
((0.9, 0.9, 0.4), (1.1, 1.1, 0.6), (1.0, 1.0, 0.5)),
|
|
((0.4, 0.9, 0.9), (0.6, 1.1, 1.1,), (0.5, 1.0, 1.0)),
|
|
]
|
|
|
|
for d in test_data_edges:
|
|
el = c.edges(selectors.BoxSelector(d[0], d[1])).vals()
|
|
self.assertEqual(1, len(el))
|
|
ec = el[0].Center()
|
|
self.assertTupleAlmostEquals(d[2], (ec.x, ec.y, ec.z), 3)
|
|
|
|
# test again by swapping box points
|
|
el = c.edges(selectors.BoxSelector(d[1], d[0])).vals()
|
|
self.assertEqual(1, len(el))
|
|
ec = el[0].Center()
|
|
self.assertTupleAlmostEquals(d[2], (ec.x, ec.y, ec.z), 3)
|
|
|
|
# test multiple edge selection
|
|
el = c.edges(selectors.BoxSelector((-0.1, -0.1, -0.1), (0.6, 0.1, 0.6))).vals()
|
|
self.assertEqual(2, len(el))
|
|
el = c.edges(selectors.BoxSelector((-0.1, -0.1, -0.1), (1.1, 0.1, 0.6))).vals()
|
|
self.assertEqual(3, len(el))
|
|
|
|
# test face selection
|
|
test_data_faces = [
|
|
# box point0, box point1, face center
|
|
((0.4, -0.1, 0.4), (0.6, 0.1, 0.6), (0.5, 0.0, 0.5)),
|
|
((0.9, 0.4, 0.4), (1.1, 0.6, 0.6), (1.0, 0.5, 0.5)),
|
|
((0.4, 0.4, 0.9), (0.6, 0.6, 1.1), (0.5, 0.5, 1.0)),
|
|
((0.4, 0.4, -0.1), (0.6, 0.6, 0.1), (0.5, 0.5, 0.0)),
|
|
]
|
|
|
|
for d in test_data_faces:
|
|
fl = c.faces(selectors.BoxSelector(d[0], d[1])).vals()
|
|
self.assertEqual(1, len(fl))
|
|
fc = fl[0].Center()
|
|
self.assertTupleAlmostEquals(d[2], (fc.x, fc.y, fc.z), 3)
|
|
|
|
# test again by swapping box points
|
|
fl = c.faces(selectors.BoxSelector(d[1], d[0])).vals()
|
|
self.assertEqual(1, len(fl))
|
|
fc = fl[0].Center()
|
|
self.assertTupleAlmostEquals(d[2], (fc.x, fc.y, fc.z), 3)
|
|
|
|
# test multiple face selection
|
|
fl = c.faces(selectors.BoxSelector((0.4, 0.4, 0.4), (0.6, 1.1, 1.1))).vals()
|
|
self.assertEqual(2, len(fl))
|
|
fl = c.faces(selectors.BoxSelector((0.4, 0.4, 0.4), (1.1, 1.1, 1.1))).vals()
|
|
self.assertEqual(3, len(fl))
|
|
|
|
# test boundingbox option
|
|
el = c.edges(
|
|
selectors.BoxSelector((-0.1, -0.1, -0.1), (1.1, 0.1, 0.6), True)
|
|
).vals()
|
|
self.assertEqual(1, len(el))
|
|
fl = c.faces(
|
|
selectors.BoxSelector((0.4, 0.4, 0.4), (1.1, 1.1, 1.1), True)
|
|
).vals()
|
|
self.assertEqual(0, len(fl))
|
|
fl = c.faces(
|
|
selectors.BoxSelector((-0.1, 0.4, -0.1), (1.1, 1.1, 1.1), True)
|
|
).vals()
|
|
self.assertEqual(1, len(fl))
|
|
|
|
def testRadiusNthSelector(self):
|
|
part = (
|
|
Workplane()
|
|
.box(10, 10, 1)
|
|
.edges(">(1, 1, 0) and |Z")
|
|
.fillet(1)
|
|
.edges(">(-1, 1, 0) and |Z")
|
|
.fillet(1)
|
|
.edges(">(-1, -1, 0) and |Z")
|
|
.fillet(2)
|
|
.edges(">(1, -1, 0) and |Z")
|
|
.fillet(3)
|
|
.faces(">Z")
|
|
)
|
|
# smallest radius is 1.0
|
|
self.assertAlmostEqual(
|
|
part.edges(selectors.RadiusNthSelector(0)).val().radius(), 1.0
|
|
)
|
|
# there are two edges with the smallest radius
|
|
self.assertEqual(len(part.edges(selectors.RadiusNthSelector(0)).vals()), 2)
|
|
# next radius is 2.0
|
|
self.assertAlmostEqual(
|
|
part.edges(selectors.RadiusNthSelector(1)).val().radius(), 2.0
|
|
)
|
|
# largest radius is 3.0
|
|
self.assertAlmostEqual(
|
|
part.edges(selectors.RadiusNthSelector(-1)).val().radius(), 3.0
|
|
)
|
|
# accessing index 3 should be an IndexError
|
|
with self.assertRaises(IndexError):
|
|
part.edges(selectors.RadiusNthSelector(3))
|
|
# reversed
|
|
self.assertAlmostEqual(
|
|
part.edges(selectors.RadiusNthSelector(0, directionMax=False))
|
|
.val()
|
|
.radius(),
|
|
3.0,
|
|
)
|
|
|
|
# test the selector on wires
|
|
wire_circles = (
|
|
Workplane()
|
|
.circle(2)
|
|
.moveTo(10, 0)
|
|
.circle(2)
|
|
.moveTo(20, 0)
|
|
.circle(4)
|
|
.consolidateWires()
|
|
)
|
|
self.assertEqual(
|
|
len(wire_circles.wires(selectors.RadiusNthSelector(0)).vals()), 2
|
|
)
|
|
self.assertEqual(
|
|
len(wire_circles.wires(selectors.RadiusNthSelector(1)).vals()), 1
|
|
)
|
|
self.assertAlmostEqual(
|
|
wire_circles.wires(selectors.RadiusNthSelector(0)).val().radius(), 2
|
|
)
|
|
self.assertAlmostEqual(
|
|
wire_circles.wires(selectors.RadiusNthSelector(1)).val().radius(), 4
|
|
)
|
|
|
|
def testAndSelector(self):
|
|
c = CQ(makeUnitCube())
|
|
|
|
S = selectors.StringSyntaxSelector
|
|
BS = selectors.BoxSelector
|
|
|
|
el = c.edges(
|
|
selectors.AndSelector(S("|X"), BS((-2, -2, 0.1), (2, 2, 2)))
|
|
).vals()
|
|
self.assertEqual(2, len(el))
|
|
|
|
# test 'and' (intersection) operator
|
|
el = c.edges(S("|X") & BS((-2, -2, 0.1), (2, 2, 2))).vals()
|
|
self.assertEqual(2, len(el))
|
|
|
|
# test using extended string syntax
|
|
v = c.vertices(">X and >Y").vals()
|
|
self.assertEqual(2, len(v))
|
|
|
|
def testSumSelector(self):
|
|
c = CQ(makeUnitCube())
|
|
|
|
S = selectors.StringSyntaxSelector
|
|
|
|
fl = c.faces(selectors.SumSelector(S(">Z"), S("<Z"))).vals()
|
|
self.assertEqual(2, len(fl))
|
|
el = c.edges(selectors.SumSelector(S("|X"), S("|Y"))).vals()
|
|
self.assertEqual(8, len(el))
|
|
|
|
# test the sum operator
|
|
fl = c.faces(S(">Z") + S("<Z")).vals()
|
|
self.assertEqual(2, len(fl))
|
|
el = c.edges(S("|X") + S("|Y")).vals()
|
|
self.assertEqual(8, len(el))
|
|
|
|
# test using extended string syntax
|
|
fl = c.faces(">Z or <Z").vals()
|
|
self.assertEqual(2, len(fl))
|
|
el = c.edges("|X or |Y").vals()
|
|
self.assertEqual(8, len(el))
|
|
|
|
def testSubtractSelector(self):
|
|
c = CQ(makeUnitCube())
|
|
|
|
S = selectors.StringSyntaxSelector
|
|
|
|
fl = c.faces(selectors.SubtractSelector(S("#Z"), S(">X"))).vals()
|
|
self.assertEqual(3, len(fl))
|
|
|
|
# test the subtract operator
|
|
fl = c.faces(S("#Z") - S(">X")).vals()
|
|
self.assertEqual(3, len(fl))
|
|
|
|
# test using extended string syntax
|
|
fl = c.faces("#Z exc >X").vals()
|
|
self.assertEqual(3, len(fl))
|
|
|
|
def testInverseSelector(self):
|
|
c = CQ(makeUnitCube())
|
|
|
|
S = selectors.StringSyntaxSelector
|
|
|
|
fl = c.faces(selectors.InverseSelector(S(">Z"))).vals()
|
|
self.assertEqual(5, len(fl))
|
|
el = c.faces(">Z").edges(selectors.InverseSelector(S(">X"))).vals()
|
|
self.assertEqual(3, len(el))
|
|
|
|
# test invert operator
|
|
fl = c.faces(-S(">Z")).vals()
|
|
self.assertEqual(5, len(fl))
|
|
el = c.faces(">Z").edges(-S(">X")).vals()
|
|
self.assertEqual(3, len(el))
|
|
|
|
# test using extended string syntax
|
|
fl = c.faces("not >Z").vals()
|
|
self.assertEqual(5, len(fl))
|
|
el = c.faces(">Z").edges("not >X").vals()
|
|
self.assertEqual(3, len(el))
|
|
|
|
def testComplexStringSelector(self):
|
|
c = CQ(makeUnitCube())
|
|
|
|
v = c.vertices("(>X and >Y) or (<X and <Y)").vals()
|
|
self.assertEqual(4, len(v))
|
|
|
|
def testFaceCount(self):
|
|
c = CQ(makeUnitCube())
|
|
self.assertEqual(6, c.faces().size())
|
|
self.assertEqual(2, c.faces("|Z").size())
|
|
|
|
def testVertexFilter(self):
|
|
"test selecting vertices on a face"
|
|
c = CQ(makeUnitCube(centered=False))
|
|
|
|
# TODO: filters work ok, but they are in global coordinates which sux. it would be nice
|
|
# if they were available in coordinates local to the selected face
|
|
|
|
v2 = c.faces("+Z").vertices("<XY")
|
|
self.assertEqual(1, v2.size()) # another way
|
|
# make sure the vertex is the right one
|
|
|
|
self.assertTupleAlmostEquals((0.0, 0.0, 1.0), v2.val().toTuple(), 3)
|
|
|
|
def testGrammar(self):
|
|
"""
|
|
Test if reasonable string selector expressions parse without an error
|
|
"""
|
|
|
|
gram = selectors._expression_grammar
|
|
|
|
expressions = [
|
|
"+X ",
|
|
"-Y",
|
|
"|(1,0,0)",
|
|
"|(-1, -0.1 , 2. )",
|
|
"#(1.,1.4114,-0.532)",
|
|
"%Plane",
|
|
">XZ",
|
|
"<Z[-2]",
|
|
">(1,4,55.)[20]",
|
|
"|XY",
|
|
"<YZ[0]",
|
|
"front",
|
|
"back",
|
|
"left",
|
|
"right",
|
|
"top",
|
|
"bottom",
|
|
"not |(1,1,0) and >(0,0,1) or XY except >(1,1,1)[-1]",
|
|
"(not |(1,1,0) and >(0,0,1)) exc XY and (Z or X)",
|
|
"not ( <X or >X or <Y or >Y )",
|
|
]
|
|
|
|
for e in expressions:
|
|
gram.parseString(e, parseAll=True)
|