Files
cadquery/tests/test_workplanes.py
Austin Gregg-Smith c42fa77ac0 Add mirror from face features (with example now fixed) (#527)
* Add the ability to mirror from a selected face

* Fix errors in example.rst

* Add mirror from face features (with example now fixed)

* recommit merge conflicts in shape.py that were stopping CI merging into master

* use correct variable name when selecting face

* formatting with black v19 instead of black v21

* add missing string literal types and clean up docstring

* Add volume assertions to the test cases

* black formatting

* Mypy fix

* Restructured mirror

* update examples to use basePoint instead of basePointVector

* Add tests for other workplane strings and non union option to increate code coverage

* Add test to check that exception is thown for incorrect input

* Go back to basePointVector

* Update arg naming in examples

* Mirror all objects

* Typo fix

Co-authored-by: Adam Urbańczyk <adam-urbanczyk@users.noreply.github.com>
2020-12-08 08:26:53 +01:00

253 lines
9.7 KiB
Python

"""
Tests basic workplane functionality
"""
# core modules
# my modules
from cadquery import *
from tests import BaseTest, toTuple
xAxis_ = Vector(1, 0, 0)
yAxis_ = Vector(0, 1, 0)
zAxis_ = Vector(0, 0, 1)
xInvAxis_ = Vector(-1, 0, 0)
yInvAxis_ = Vector(0, -1, 0)
zInvAxis_ = Vector(0, 0, -1)
class TestWorkplanes(BaseTest):
def testYZPlaneOrigins(self):
# xy plane-- with origin at x=0.25
base = Vector(0.25, 0, 0)
p = Plane(base, Vector(0, 1, 0), Vector(1, 0, 0))
# origin is always (0,0,0) in local coordinates
self.assertTupleAlmostEquals((0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
# (0,0,0) is always the original base in global coordinates
self.assertTupleAlmostEquals(
base.toTuple(), p.toWorldCoords((0, 0)).toTuple(), 2
)
def testXYPlaneOrigins(self):
base = Vector(0, 0, 0.25)
p = Plane(base, Vector(1, 0, 0), Vector(0, 0, 1))
# origin is always (0,0,0) in local coordinates
self.assertTupleAlmostEquals((0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
# (0,0,0) is always the original base in global coordinates
self.assertTupleAlmostEquals(
toTuple(base), p.toWorldCoords((0, 0)).toTuple(), 2
)
def testXZPlaneOrigins(self):
base = Vector(0, 0.25, 0)
p = Plane(base, Vector(0, 0, 1), Vector(0, 1, 0))
# (0,0,0) is always the original base in global coordinates
self.assertTupleAlmostEquals(
toTuple(base), p.toWorldCoords((0, 0)).toTuple(), 2
)
# origin is always (0,0,0) in local coordinates
self.assertTupleAlmostEquals((0, 0, 0), p.toLocalCoords(p.origin).toTuple(), 2)
def testPlaneBasics(self):
p = Plane.XY()
# local to world
self.assertTupleAlmostEquals(
(1.0, 1.0, 0), p.toWorldCoords((1, 1)).toTuple(), 2
)
self.assertTupleAlmostEquals(
(-1.0, -1.0, 0), p.toWorldCoords((-1, -1)).toTuple(), 2
)
# world to local
self.assertTupleAlmostEquals(
(-1.0, -1.0), p.toLocalCoords(Vector(-1, -1, 0)).toTuple(), 2
)
self.assertTupleAlmostEquals(
(1.0, 1.0), p.toLocalCoords(Vector(1, 1, 0)).toTuple(), 2
)
p = Plane.YZ()
self.assertTupleAlmostEquals(
(0, 1.0, 1.0), p.toWorldCoords((1, 1)).toTuple(), 2
)
# world to local
self.assertTupleAlmostEquals(
(1.0, 1.0), p.toLocalCoords(Vector(0, 1, 1)).toTuple(), 2
)
p = Plane.XZ()
r = p.toWorldCoords((1, 1)).toTuple()
self.assertTupleAlmostEquals((1.0, 0.0, 1.0), r, 2)
# world to local
self.assertTupleAlmostEquals(
(1.0, 1.0), p.toLocalCoords(Vector(1, 0, 1)).toTuple(), 2
)
def testOffsetPlanes(self):
"Tests that a plane offset from the origin works ok too"
p = Plane.XY(origin=(10.0, 10.0, 0))
self.assertTupleAlmostEquals(
(11.0, 11.0, 0.0), p.toWorldCoords((1.0, 1.0)).toTuple(), 2
)
self.assertTupleAlmostEquals(
(2.0, 2.0), p.toLocalCoords(Vector(12.0, 12.0, 0)).toTuple(), 2
)
# TODO test these offsets in the other dimensions too
p = Plane.YZ(origin=(0, 2, 2))
self.assertTupleAlmostEquals(
(0.0, 5.0, 5.0), p.toWorldCoords((3.0, 3.0)).toTuple(), 2
)
self.assertTupleAlmostEquals(
(10, 10.0, 0.0), p.toLocalCoords(Vector(0.0, 12.0, 12.0)).toTuple(), 2
)
p = Plane.XZ(origin=(2, 0, 2))
r = p.toWorldCoords((1.0, 1.0)).toTuple()
self.assertTupleAlmostEquals((3.0, 0.0, 3.0), r, 2)
self.assertTupleAlmostEquals(
(10.0, 10.0), p.toLocalCoords(Vector(12.0, 0.0, 12.0)).toTuple(), 2
)
def testXYPlaneBasics(self):
p = Plane.named("XY")
self.assertTupleAlmostEquals(p.zDir.toTuple(), zAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.xDir.toTuple(), xAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.yDir.toTuple(), yAxis_.toTuple(), 4)
def testYZPlaneBasics(self):
p = Plane.named("YZ")
self.assertTupleAlmostEquals(p.zDir.toTuple(), xAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.xDir.toTuple(), yAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.yDir.toTuple(), zAxis_.toTuple(), 4)
def testZXPlaneBasics(self):
p = Plane.named("ZX")
self.assertTupleAlmostEquals(p.zDir.toTuple(), yAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.xDir.toTuple(), zAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.yDir.toTuple(), xAxis_.toTuple(), 4)
def testXZPlaneBasics(self):
p = Plane.named("XZ")
self.assertTupleAlmostEquals(p.zDir.toTuple(), yInvAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.xDir.toTuple(), xAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.yDir.toTuple(), zAxis_.toTuple(), 4)
def testYXPlaneBasics(self):
p = Plane.named("YX")
self.assertTupleAlmostEquals(p.zDir.toTuple(), zInvAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.xDir.toTuple(), yAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.yDir.toTuple(), xAxis_.toTuple(), 4)
def testZYPlaneBasics(self):
p = Plane.named("ZY")
self.assertTupleAlmostEquals(p.zDir.toTuple(), xInvAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.xDir.toTuple(), zAxis_.toTuple(), 4)
self.assertTupleAlmostEquals(p.yDir.toTuple(), yAxis_.toTuple(), 4)
def test_mirror(self):
"""Create a unit box and mirror it so that it doubles in size"""
b2 = Workplane().box(1, 1, 1)
b2 = b2.mirror("XY", (0, 0, 0.5), union=True)
bbBox = b2.findSolid().BoundingBox()
assert [bbBox.xlen, bbBox.ylen, bbBox.zlen] == [1.0, 1.0, 2]
self.assertAlmostEqual(b2.findSolid().Volume(), 2, 5)
def test_all_planes(self):
b2 = Workplane().box(1, 1, 1)
for p in ["XY", "YX", "XZ", "ZX", "YZ", "ZY"]:
b2 = b2.mirror(p)
bbBox = b2.findSolid().BoundingBox()
assert [bbBox.xlen, bbBox.ylen, bbBox.zlen] == [1.0, 1.0, 1.0]
self.assertAlmostEqual(b2.findSolid().Volume(), 1, 5)
def test_bad_plane_input(self):
b2 = Workplane().box(1, 1, 1)
with self.assertRaises(ValueError) as context:
b2.mirror(b2.edges())
self.assertTrue("Face required, got" in str(context.exception))
def test_mirror_axis(self):
"""Create a unit box and mirror it so that it doubles in size"""
b2 = Workplane().box(1, 1, 1)
b2 = b2.mirror((0, 0, 1), (0, 0, 0.5), union=True)
bbBox = b2.findSolid().BoundingBox()
assert [bbBox.xlen, bbBox.ylen, bbBox.zlen] == [1.0, 1.0, 2]
self.assertAlmostEqual(b2.findSolid().Volume(), 2, 5)
def test_mirror_workplane(self):
"""Create a unit box and mirror it so that it doubles in size"""
b2 = Workplane().box(1, 1, 1)
# double in Z plane
b2 = b2.mirror(b2.faces(">Z"), union=True)
bbBox = b2.findSolid().BoundingBox()
assert [bbBox.xlen, bbBox.ylen, bbBox.zlen] == [1.0, 1.0, 2]
self.assertAlmostEqual(b2.findSolid().Volume(), 2, 5)
# double in Y plane
b2 = b2.mirror(b2.faces(">Y"), union=True)
bbBox = b2.findSolid().BoundingBox()
assert [bbBox.xlen, bbBox.ylen, bbBox.zlen] == [1.0, 2.0, 2]
self.assertAlmostEqual(b2.findSolid().Volume(), 4, 5)
# double in X plane
b2 = b2.mirror(b2.faces(">X"), union=True)
bbBox = b2.findSolid().BoundingBox()
assert [bbBox.xlen, bbBox.ylen, bbBox.zlen] == [2.0, 2.0, 2]
self.assertAlmostEqual(b2.findSolid().Volume(), 8, 5)
def test_mirror_equivalence(self):
"""test that the plane string, plane normal and face object perform a mirror operation in the same way"""
boxes = []
boxDims = 1
for i in range(3): # create 3 sets of identical boxes
boxTmp = Workplane("XY").box(boxDims, boxDims, boxDims)
boxTmp = boxTmp.translate([i * 2, 0, boxDims / 2])
boxes.append(boxTmp)
# 3 different types of plane definition
planeArg = ["XY", (0, 0, 1), boxes[0].faces("<Z")]
planeOffset = (0, 0, 0.5) # use the safe offset for each
boxResults = [] # store the resulting mirrored objects
for b, p in zip(boxes, planeArg):
boxResults.append(b.mirror(p, planeOffset, union=True))
# all resulting boxes should be equal to each other
for i in range(len(boxResults) - 1):
curBoxDims = boxResults[i].findSolid().BoundingBox() # get bbox dims
nextBoxDims = boxResults[i + 1].findSolid().BoundingBox() # get bbox dims
cbd = (curBoxDims.xlen, curBoxDims.ylen, curBoxDims.zlen)
nbd = (nextBoxDims.xlen, nextBoxDims.ylen, nextBoxDims.zlen)
self.assertTupleAlmostEquals(cbd, nbd, 4)
self.assertAlmostEqual(
boxResults[i].findSolid().Volume(),
boxResults[i + 1].findSolid().Volume(),
5,
)
def test_mirror_face(self):
"""Create a triangle and mirror into a unit box"""
r = Workplane("XY").line(0, 1).line(1, -1).close().extrude(1)
bbBox = r.findSolid().BoundingBox()
self.assertTupleAlmostEquals(
(bbBox.xlen, bbBox.ylen, bbBox.zlen), (1.0, 1.0, 1.0), 4
)
self.assertAlmostEqual(r.findSolid().Volume(), 0.5, 5)
r = r.mirror(r.faces().objects[1], union=True)
bbBox = r.findSolid().BoundingBox()
self.assertTupleAlmostEquals(
(bbBox.xlen, bbBox.ylen, bbBox.zlen), (1.0, 1.0, 1.0), 4
)
self.assertAlmostEqual(r.findSolid().Volume(), 1.0, 5)