Files
cadquery/tests/test_cad_objects.py
AU 995d1393bd Free function api (#1469)
* Initial free functions

* Mypy fixes

* Add a mypy plugin that handles _get*

* More helpers and fixes

* black

* More hooks

* More ops and primitives

* Fill with constraints and cap

* Minimal docstrings and mypy fix

* Bool op operators for Shape

* Extra docstring

* Added spline primitive

* Added alternative constructors

* Update solid

* Add shape normalization

* Add text

* Added moved overload

* Another moved overload

* Convert location constructor to multimethod

* Additional Loc constructor

* Extra vertex constructor

* Additional cone overload

* Start with tests

* Fix compouund normalization

* Bool op tests

* Additional Location overload

* test moved and fix bool ops

* Different cap params

* More tests

* Test revolve and offset

* Test sweep and loft

* Add bool ops

* More tests

* Test text

* Improve coverage for utils

* More move[d] and Location overloads

* Start working on some docs

* Update index

* Doc fix

* Typo fix

* More move/moved overloads

* Small doc update

* Better Location coverage

* Fix angle units in Location

* More docs and a usability fix

* Cosmetics

* Mypy fix

* Remove dead code

* Coverage tweaks

* More docs'

* Box centering and box/plane arg order

* Docs cosmetics - nicer sweep

* Apply suggestions

Co-authored-by: Jeremy Wright <wrightjmf@gmail.com>

* Add docstrings

* Doc tweaks

* Bump multimethod version

* Add occ_impl.shapes

* Mention free funcs in the primer

* Typos

* Typo

* Punctuation

---------

Co-authored-by: Jeremy Wright <wrightjmf@gmail.com>
2024-05-05 11:25:45 +02:00

799 lines
26 KiB
Python

# system modules
import math
import pytest
import unittest
from tests import BaseTest
from OCP.gp import gp_Vec, gp_Pnt, gp_Ax2, gp_Circ, gp_Elips, gp, gp_XYZ, gp_Trsf
from OCP.BRepBuilderAPI import BRepBuilderAPI_MakeEdge
from cadquery import *
DEG2RAD = math.pi / 180
RAD2DEG = 180 / math.pi
class TestCadObjects(BaseTest):
def _make_circle(self):
circle = gp_Circ(gp_Ax2(gp_Pnt(1, 2, 3), gp.DZ_s()), 2.0)
return Shape.cast(BRepBuilderAPI_MakeEdge(circle).Edge())
def _make_ellipse(self):
ellipse = gp_Elips(gp_Ax2(gp_Pnt(1, 2, 3), gp.DZ_s()), 4.0, 2.0)
return Shape.cast(BRepBuilderAPI_MakeEdge(ellipse).Edge())
def testVectorConstructors(self):
v1 = Vector(1, 2, 3)
v2 = Vector((1, 2, 3))
v3 = Vector(gp_Vec(1, 2, 3))
v4 = Vector([1, 2, 3])
v5 = Vector(gp_XYZ(1, 2, 3))
for v in [v1, v2, v3, v4, v5]:
self.assertTupleAlmostEquals((1, 2, 3), v.toTuple(), 4)
v6 = Vector((1, 2))
v7 = Vector([1, 2])
v8 = Vector(1, 2)
for v in [v6, v7, v8]:
self.assertTupleAlmostEquals((1, 2, 0), v.toTuple(), 4)
v9 = Vector()
self.assertTupleAlmostEquals((0, 0, 0), v9.toTuple(), 4)
v9.x = 1.0
v9.y = 2.0
v9.z = 3.0
self.assertTupleAlmostEquals((1, 2, 3), (v9.x, v9.y, v9.z), 4)
with self.assertRaises(TypeError):
Vector("vector")
with self.assertRaises(TypeError):
Vector(1, 2, 3, 4)
def testVertex(self):
"""
Tests basic vertex functions
"""
v = Vertex.makeVertex(1, 1, 1)
self.assertEqual(1, v.X)
self.assertEqual(Vector, type(v.Center()))
def testBasicBoundingBox(self):
v = Vertex.makeVertex(1, 1, 1)
v2 = Vertex.makeVertex(2, 2, 2)
self.assertEqual(BoundBox, type(v.BoundingBox()))
self.assertEqual(BoundBox, type(v2.BoundingBox()))
bb1 = v.BoundingBox().add(v2.BoundingBox())
# OCC uses some approximations
self.assertAlmostEqual(bb1.xlen, 1.0, 1)
# Test adding to an existing bounding box
v0 = Vertex.makeVertex(0, 0, 0)
bb2 = v0.BoundingBox().add(v.BoundingBox())
bb3 = bb1.add(bb2)
self.assertTupleAlmostEquals((2, 2, 2), (bb3.xlen, bb3.ylen, bb3.zlen), 7)
bb3 = bb2.add((3, 3, 3))
self.assertTupleAlmostEquals((3, 3, 3), (bb3.xlen, bb3.ylen, bb3.zlen), 7)
bb3 = bb2.add(Vector(3, 3, 3))
self.assertTupleAlmostEquals((3, 3, 3), (bb3.xlen, bb3.ylen, bb3.zlen), 7)
# Test 2D bounding boxes
bb1 = (
Vertex.makeVertex(1, 1, 0)
.BoundingBox()
.add(Vertex.makeVertex(2, 2, 0).BoundingBox())
)
bb2 = (
Vertex.makeVertex(0, 0, 0)
.BoundingBox()
.add(Vertex.makeVertex(3, 3, 0).BoundingBox())
)
bb3 = (
Vertex.makeVertex(0, 0, 0)
.BoundingBox()
.add(Vertex.makeVertex(1.5, 1.5, 0).BoundingBox())
)
# Test that bb2 contains bb1
self.assertEqual(bb2, BoundBox.findOutsideBox2D(bb1, bb2))
self.assertEqual(bb2, BoundBox.findOutsideBox2D(bb2, bb1))
# Test that neither bounding box contains the other
self.assertIsNone(BoundBox.findOutsideBox2D(bb1, bb3))
# Test creation of a bounding box from a shape - note the low accuracy comparison
# as the box is a little larger than the shape
bb1 = BoundBox._fromTopoDS(Solid.makeCylinder(1, 1).wrapped, optimal=False)
self.assertTupleAlmostEquals((2, 2, 1), (bb1.xlen, bb1.ylen, bb1.zlen), 1)
def testEdgeWrapperCenter(self):
e = self._make_circle()
self.assertTupleAlmostEquals((1.0, 2.0, 3.0), e.Center().toTuple(), 3)
def testEdgeWrapperEllipseCenter(self):
e = self._make_ellipse()
w = Wire.assembleEdges([e])
self.assertTupleAlmostEquals(
(1.0, 2.0, 3.0), Face.makeFromWires(w).Center().toTuple(), 3
)
def testEdgeWrapperMakeCircle(self):
halfCircleEdge = Edge.makeCircle(
radius=10, pnt=(0, 0, 0), dir=(0, 0, 1), angle1=0, angle2=180
)
# self.assertTupleAlmostEquals((0.0, 5.0, 0.0), halfCircleEdge.CenterOfBoundBox(0.0001).toTuple(),3)
self.assertTupleAlmostEquals(
(10.0, 0.0, 0.0), halfCircleEdge.startPoint().toTuple(), 3
)
self.assertTupleAlmostEquals(
(-10.0, 0.0, 0.0), halfCircleEdge.endPoint().toTuple(), 3
)
def testEdgeWrapperMakeTangentArc(self):
tangent_arc = Edge.makeTangentArc(
Vector(1, 1), # starts at 1, 1
Vector(0, 1), # tangent at start of arc is in the +y direction
Vector(2, 1), # arc curves 180 degrees and ends at 2, 1
)
self.assertTupleAlmostEquals((1, 1, 0), tangent_arc.startPoint().toTuple(), 3)
self.assertTupleAlmostEquals((2, 1, 0), tangent_arc.endPoint().toTuple(), 3)
self.assertTupleAlmostEquals(
(0, 1, 0), tangent_arc.tangentAt(locationParam=0).toTuple(), 3
)
self.assertTupleAlmostEquals(
(1, 0, 0), tangent_arc.tangentAt(locationParam=0.5).toTuple(), 3
)
self.assertTupleAlmostEquals(
(0, -1, 0), tangent_arc.tangentAt(locationParam=1).toTuple(), 3
)
def testEdgeWrapperMakeEllipse1(self):
# Check x_radius > y_radius
x_radius, y_radius = 20, 10
angle1, angle2 = -75.0, 90.0
arcEllipseEdge = Edge.makeEllipse(
x_radius=x_radius,
y_radius=y_radius,
pnt=(0, 0, 0),
dir=(0, 0, 1),
angle1=angle1,
angle2=angle2,
)
start = (
x_radius * math.cos(angle1 * DEG2RAD),
y_radius * math.sin(angle1 * DEG2RAD),
0.0,
)
end = (
x_radius * math.cos(angle2 * DEG2RAD),
y_radius * math.sin(angle2 * DEG2RAD),
0.0,
)
self.assertTupleAlmostEquals(start, arcEllipseEdge.startPoint().toTuple(), 3)
self.assertTupleAlmostEquals(end, arcEllipseEdge.endPoint().toTuple(), 3)
def testEdgeWrapperMakeEllipse2(self):
# Check x_radius < y_radius
x_radius, y_radius = 10, 20
angle1, angle2 = 0.0, 45.0
arcEllipseEdge = Edge.makeEllipse(
x_radius=x_radius,
y_radius=y_radius,
pnt=(0, 0, 0),
dir=(0, 0, 1),
angle1=angle1,
angle2=angle2,
)
start = (
x_radius * math.cos(angle1 * DEG2RAD),
y_radius * math.sin(angle1 * DEG2RAD),
0.0,
)
end = (
x_radius * math.cos(angle2 * DEG2RAD),
y_radius * math.sin(angle2 * DEG2RAD),
0.0,
)
self.assertTupleAlmostEquals(start, arcEllipseEdge.startPoint().toTuple(), 3)
self.assertTupleAlmostEquals(end, arcEllipseEdge.endPoint().toTuple(), 3)
def testEdgeWrapperMakeCircleWithEllipse(self):
# Check x_radius == y_radius
x_radius, y_radius = 20, 20
angle1, angle2 = 15.0, 60.0
arcEllipseEdge = Edge.makeEllipse(
x_radius=x_radius,
y_radius=y_radius,
pnt=(0, 0, 0),
dir=(0, 0, 1),
angle1=angle1,
angle2=angle2,
)
start = (
x_radius * math.cos(angle1 * DEG2RAD),
y_radius * math.sin(angle1 * DEG2RAD),
0.0,
)
end = (
x_radius * math.cos(angle2 * DEG2RAD),
y_radius * math.sin(angle2 * DEG2RAD),
0.0,
)
self.assertTupleAlmostEquals(start, arcEllipseEdge.startPoint().toTuple(), 3)
self.assertTupleAlmostEquals(end, arcEllipseEdge.endPoint().toTuple(), 3)
def testFaceWrapperMakePlane(self):
mplane = Face.makePlane(10, 10)
self.assertTupleAlmostEquals((0.0, 0.0, 1.0), mplane.normalAt().toTuple(), 3)
def testMatrixOfInertia(self):
"""
Tests the calculation of the matrix of inertia for a solid
"""
radius = 1.0
height = 2.0
cylinder = Solid.makeCylinder(radius=radius, height=height)
moi = Shape.matrixOfInertia(cylinder)
two_pi = 2 * math.pi
true_moi = (
two_pi * (radius ** 2 / 4 + height ** 2 / 12),
two_pi * (radius ** 2 / 4 + height ** 2 / 12),
two_pi * radius ** 2 / 2,
)
self.assertTupleAlmostEquals((moi[0][0], moi[1][1], moi[2][2]), true_moi, 3)
def testVertexMatrixOfInertiaNotImplemented(self):
with self.assertRaises(NotImplementedError):
vertex = Vertex.makeVertex(1, 1, 1)
Shape.matrixOfInertia(vertex)
def testCenterOfBoundBox(self):
pass
def testCombinedCenterOfBoundBox(self):
pass
def testCompoundCenter(self):
"""
Tests whether or not a proper weighted center can be found for a compound
"""
def cylinders(self, radius, height):
c = Solid.makeCylinder(radius, height, Vector())
# Combine all the cylinders into a single compound
r = self.eachpoint(lambda loc: c.located(loc), True).combineSolids()
return r
Workplane.cyl = cylinders
# Now test. here we want weird workplane to see if the objects are transformed right
s = (
Workplane("XY")
.rect(2.0, 3.0, forConstruction=True)
.vertices()
.cyl(0.25, 0.5)
)
self.assertEqual(4, len(s.val().Solids()))
self.assertTupleAlmostEquals((0.0, 0.0, 0.25), s.val().Center().toTuple(), 3)
def testDot(self):
v1 = Vector(2, 2, 2)
v2 = Vector(1, -1, 1)
self.assertEqual(2.0, v1.dot(v2))
def testVectorAdd(self):
result = Vector(1, 2, 0) + Vector(0, 0, 3)
self.assertTupleAlmostEquals((1.0, 2.0, 3.0), result.toTuple(), 3)
def testVectorOperators(self):
result = Vector(1, 1, 1) + Vector(2, 2, 2)
self.assertEqual(Vector(3, 3, 3), result)
result = Vector(1, 2, 3) - Vector(3, 2, 1)
self.assertEqual(Vector(-2, 0, 2), result)
result = Vector(1, 2, 3) * 2
self.assertEqual(Vector(2, 4, 6), result)
result = 3 * Vector(1, 2, 3)
self.assertEqual(Vector(3, 6, 9), result)
result = Vector(2, 4, 6) / 2
self.assertEqual(Vector(1, 2, 3), result)
self.assertEqual(Vector(-1, -1, -1), -Vector(1, 1, 1))
self.assertEqual(0, abs(Vector(0, 0, 0)))
self.assertEqual(1, abs(Vector(1, 0, 0)))
self.assertEqual((1 + 4 + 9) ** 0.5, abs(Vector(1, 2, 3)))
def testVectorEquals(self):
a = Vector(1, 2, 3)
b = Vector(1, 2, 3)
c = Vector(1, 2, 3.000001)
self.assertEqual(a, b)
self.assertEqual(a, c)
def testVectorProject(self):
"""
Test line projection and plane projection methods of cq.Vector
"""
decimal_places = 9
normal = Vector(1, 2, 3)
base = Vector(5, 7, 9)
x_dir = Vector(1, 0, 0)
# test passing Plane object
point = Vector(10, 11, 12).projectToPlane(Plane(base, x_dir, normal))
self.assertTupleAlmostEquals(
point.toTuple(), (59 / 7, 55 / 7, 51 / 7), decimal_places
)
# test line projection
vec = Vector(10, 10, 10)
line = Vector(3, 4, 5)
angle = vec.getAngle(line)
vecLineProjection = vec.projectToLine(line)
self.assertTupleAlmostEquals(
vecLineProjection.normalized().toTuple(),
line.normalized().toTuple(),
decimal_places,
)
self.assertAlmostEqual(
vec.Length * math.cos(angle), vecLineProjection.Length, decimal_places
)
def testVectorNotImplemented(self):
v = Vector(1, 2, 3)
with self.assertRaises(NotImplementedError):
v.distanceToLine()
with self.assertRaises(NotImplementedError):
v.distanceToPlane()
def testVectorSpecialMethods(self):
v = Vector(1, 2, 3)
self.assertEqual(repr(v), "Vector: (1.0, 2.0, 3.0)")
self.assertEqual(str(v), "Vector: (1.0, 2.0, 3.0)")
def testMatrixCreationAndAccess(self):
def matrix_vals(m):
return [[m[r, c] for c in range(4)] for r in range(4)]
# default constructor creates a 4x4 identity matrix
m = Matrix()
identity = [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]
self.assertEqual(identity, matrix_vals(m))
vals4x4 = [
[1.0, 0.0, 0.0, 1.0],
[0.0, 1.0, 0.0, 2.0],
[0.0, 0.0, 1.0, 3.0],
[0.0, 0.0, 0.0, 1.0],
]
vals4x4_tuple = tuple(tuple(r) for r in vals4x4)
# test constructor with 16-value input
m = Matrix(vals4x4)
self.assertEqual(vals4x4, matrix_vals(m))
m = Matrix(vals4x4_tuple)
self.assertEqual(vals4x4, matrix_vals(m))
# test constructor with 12-value input (the last 4 are an implied
# [0,0,0,1])
m = Matrix(vals4x4[:3])
self.assertEqual(vals4x4, matrix_vals(m))
m = Matrix(vals4x4_tuple[:3])
self.assertEqual(vals4x4, matrix_vals(m))
# Test 16-value input with invalid values for the last 4
invalid = [
[1.0, 0.0, 0.0, 1.0],
[0.0, 1.0, 0.0, 2.0],
[0.0, 0.0, 1.0, 3.0],
[1.0, 2.0, 3.0, 4.0],
]
with self.assertRaises(ValueError):
Matrix(invalid)
# Test input with invalid type
with self.assertRaises(TypeError):
Matrix("invalid")
# Test input with invalid size / nested types
with self.assertRaises(TypeError):
Matrix([[1, 2, 3, 4], [1, 2, 3], [1, 2, 3, 4]])
with self.assertRaises(TypeError):
Matrix([1, 2, 3])
# Invalid sub-type
with self.assertRaises(TypeError):
Matrix([[1, 2, 3, 4], "abc", [1, 2, 3, 4]])
# test out-of-bounds access
m = Matrix()
with self.assertRaises(IndexError):
m[0, 4]
with self.assertRaises(IndexError):
m[4, 0]
with self.assertRaises(IndexError):
m["ab"]
# test __repr__ methods
m = Matrix(vals4x4)
mRepr = "Matrix([[1.0, 0.0, 0.0, 1.0],\n [0.0, 1.0, 0.0, 2.0],\n [0.0, 0.0, 1.0, 3.0],\n [0.0, 0.0, 0.0, 1.0]])"
self.assertEqual(repr(m), mRepr)
self.assertEqual(str(eval(repr(m))), mRepr)
def testMatrixFunctionality(self):
# Test rotate methods
def matrix_almost_equal(m, target_matrix):
for r, row in enumerate(target_matrix):
for c, target_value in enumerate(row):
self.assertAlmostEqual(m[r, c], target_value)
root_3_over_2 = math.sqrt(3) / 2
m_rotate_x_30 = [
[1, 0, 0, 0],
[0, root_3_over_2, -1 / 2, 0],
[0, 1 / 2, root_3_over_2, 0],
[0, 0, 0, 1],
]
mx = Matrix()
mx.rotateX(30 * DEG2RAD)
matrix_almost_equal(mx, m_rotate_x_30)
m_rotate_y_30 = [
[root_3_over_2, 0, 1 / 2, 0],
[0, 1, 0, 0],
[-1 / 2, 0, root_3_over_2, 0],
[0, 0, 0, 1],
]
my = Matrix()
my.rotateY(30 * DEG2RAD)
matrix_almost_equal(my, m_rotate_y_30)
m_rotate_z_30 = [
[root_3_over_2, -1 / 2, 0, 0],
[1 / 2, root_3_over_2, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
]
mz = Matrix()
mz.rotateZ(30 * DEG2RAD)
matrix_almost_equal(mz, m_rotate_z_30)
# Test matrix multipy vector
v = Vector(1, 0, 0)
self.assertTupleAlmostEquals(
mz.multiply(v).toTuple(), (root_3_over_2, 1 / 2, 0), 7
)
# Test matrix multipy matrix
m_rotate_xy_30 = [
[root_3_over_2, 0, 1 / 2, 0],
[1 / 4, root_3_over_2, -root_3_over_2 / 2, 0],
[-root_3_over_2 / 2, 1 / 2, 3 / 4, 0],
[0, 0, 0, 1],
]
mxy = mx.multiply(my)
matrix_almost_equal(mxy, m_rotate_xy_30)
# Test matrix inverse
vals4x4 = [[1, 2, 3, 4], [5, 1, 6, 7], [8, 9, 1, 10], [0, 0, 0, 1]]
vals4x4_invert = [
[-53 / 144, 25 / 144, 1 / 16, -53 / 144],
[43 / 144, -23 / 144, 1 / 16, -101 / 144],
[37 / 144, 7 / 144, -1 / 16, -107 / 144],
[0, 0, 0, 1],
]
m = Matrix(vals4x4).inverse()
matrix_almost_equal(m, vals4x4_invert)
def testTranslate(self):
e = Edge.makeCircle(2, (1, 2, 3))
e2 = e.translate(Vector(0, 0, 1))
self.assertTupleAlmostEquals((1.0, 2.0, 4.0), e2.Center().toTuple(), 3)
def testVertices(self):
e = Shape.cast(BRepBuilderAPI_MakeEdge(gp_Pnt(0, 0, 0), gp_Pnt(1, 1, 0)).Edge())
self.assertEqual(2, len(e.Vertices()))
def testPlaneEqual(self):
# default orientation
self.assertEqual(
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
)
# moved origin
self.assertEqual(
Plane(origin=(2, 1, -1), xDir=(1, 0, 0), normal=(0, 0, 1)),
Plane(origin=(2, 1, -1), xDir=(1, 0, 0), normal=(0, 0, 1)),
)
# moved x-axis
self.assertEqual(
Plane(origin=(0, 0, 0), xDir=(1, 1, 0), normal=(0, 0, 1)),
Plane(origin=(0, 0, 0), xDir=(1, 1, 0), normal=(0, 0, 1)),
)
# moved z-axis
self.assertEqual(
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 1, 1)),
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 1, 1)),
)
def testPlaneNotEqual(self):
# type difference
for value in [None, 0, 1, "abc"]:
self.assertNotEqual(
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)), value
)
# origin difference
self.assertNotEqual(
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
Plane(origin=(0, 0, 1), xDir=(1, 0, 0), normal=(0, 0, 1)),
)
# x-axis difference
self.assertNotEqual(
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
Plane(origin=(0, 0, 0), xDir=(1, 1, 0), normal=(0, 0, 1)),
)
# z-axis difference
self.assertNotEqual(
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 1)),
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 1, 1)),
)
def testInvalidPlane(self):
# Test plane creation error handling
with self.assertRaises(ValueError):
Plane.named("XX", (0, 0, 0))
with self.assertRaises(ValueError):
Plane(origin=(0, 0, 0), xDir=(0, 0, 0), normal=(0, 1, 1))
with self.assertRaises(ValueError):
Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 0, 0))
def testPlaneMethods(self):
# Test error checking
p = Plane(origin=(0, 0, 0), xDir=(1, 0, 0), normal=(0, 1, 0))
with self.assertRaises(ValueError):
p.toLocalCoords("box")
with self.assertRaises(NotImplementedError):
p.mirrorInPlane([Solid.makeBox(1, 1, 1)], "Z")
# Test translation to local coordinates
local_box = Workplane(p.toLocalCoords(Solid.makeBox(1, 1, 1)))
local_box_vertices = [(v.X, v.Y, v.Z) for v in local_box.vertices().vals()]
target_vertices = [
(0, -1, 0),
(0, 0, 0),
(0, -1, 1),
(0, 0, 1),
(1, -1, 0),
(1, 0, 0),
(1, -1, 1),
(1, 0, 1),
]
for i, target_point in enumerate(target_vertices):
self.assertTupleAlmostEquals(target_point, local_box_vertices[i], 7)
# Test mirrorInPlane
mirror_box = Workplane(p.mirrorInPlane([Solid.makeBox(1, 1, 1)], "Y")[0])
mirror_box_vertices = [(v.X, v.Y, v.Z) for v in mirror_box.vertices().vals()]
target_vertices = [
(0, 0, 1),
(0, 0, 0),
(0, -1, 1),
(0, -1, 0),
(-1, 0, 1),
(-1, 0, 0),
(-1, -1, 1),
(-1, -1, 0),
]
for i, target_point in enumerate(target_vertices):
self.assertTupleAlmostEquals(target_point, mirror_box_vertices[i], 7)
def testLocation(self):
# empty
loc = Location()
T = loc.wrapped.Transformation().TranslationPart()
self.assertTupleAlmostEquals((T.X(), T.Y(), T.Z()), (0, 0, 0), 6)
# Tuple
loc0 = Location((0, 0, 1))
T = loc0.wrapped.Transformation().TranslationPart()
self.assertTupleAlmostEquals((T.X(), T.Y(), T.Z()), (0, 0, 1), 6)
# Vector
loc1 = Location(Vector(0, 0, 1))
T = loc1.wrapped.Transformation().TranslationPart()
self.assertTupleAlmostEquals((T.X(), T.Y(), T.Z()), (0, 0, 1), 6)
# rotation + translation
loc2 = Location(Vector(0, 0, 1), Vector(0, 0, 1), 45)
angle = loc2.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
self.assertAlmostEqual(45, angle)
# gp_Trsf
T = gp_Trsf()
T.SetTranslation(gp_Vec(0, 0, 1))
loc3 = Location(T)
assert (
loc1.wrapped.Transformation().TranslationPart().Z()
== loc3.wrapped.Transformation().TranslationPart().Z()
)
# Test creation from the OCP.gp.gp_Trsf object
loc4 = Location(gp_Trsf())
self.assertTupleAlmostEquals(loc4.toTuple()[0], (0, 0, 0), 7)
self.assertTupleAlmostEquals(loc4.toTuple()[1], (0, 0, 0), 7)
# Test composition
loc4 = Location((0, 0, 0), Vector(0, 0, 1), 15)
loc5 = loc1 * loc4
loc6 = loc4 * loc4
loc7 = loc4 ** 2
T = loc5.wrapped.Transformation().TranslationPart()
self.assertTupleAlmostEquals((T.X(), T.Y(), T.Z()), (0, 0, 1), 6)
angle5 = (
loc5.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
)
self.assertAlmostEqual(15, angle5)
angle6 = (
loc6.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
)
self.assertAlmostEqual(30, angle6)
angle7 = (
loc7.wrapped.Transformation().GetRotation().GetRotationAngle() * RAD2DEG
)
self.assertAlmostEqual(30, angle7)
# Test error handling on creation
with self.assertRaises(TypeError):
Location([0, 0, 1])
with self.assertRaises(TypeError):
Location("xy_plane")
# test to tuple
loc8 = Location(z=2, ry=15)
trans, rot = loc8.toTuple()
self.assertTupleAlmostEquals(trans, (0, 0, 2), 6)
self.assertTupleAlmostEquals(rot, (0, 15, 0), 6)
def testEdgeWrapperRadius(self):
# get a radius from a simple circle
e0 = Edge.makeCircle(2.4)
self.assertAlmostEqual(e0.radius(), 2.4)
# radius of an arc
e1 = Edge.makeCircle(1.8, pnt=(5, 6, 7), dir=(1, 1, 1), angle1=20, angle2=30)
self.assertAlmostEqual(e1.radius(), 1.8)
# test value errors
e2 = Edge.makeEllipse(10, 20)
with self.assertRaises(ValueError):
e2.radius()
# radius from a wire
w0 = Wire.makeCircle(10, Vector(1, 2, 3), (-1, 0, 1))
self.assertAlmostEqual(w0.radius(), 10)
# radius from a wire with multiple edges
rad = 2.3
pnt = (7, 8, 9)
direction = (1, 0.5, 0.1)
w1 = Wire.assembleEdges(
[
Edge.makeCircle(rad, pnt, direction, 0, 10),
Edge.makeCircle(rad, pnt, direction, 10, 25),
Edge.makeCircle(rad, pnt, direction, 25, 230),
]
)
self.assertAlmostEqual(w1.radius(), rad)
# test value error from wire
w2 = Wire.makePolygon([Vector(-1, 0, 0), Vector(0, 1, 0), Vector(1, -1, 0),])
with self.assertRaises(ValueError):
w2.radius()
# (I think) the radius of a wire is the radius of it's first edge.
# Since this is stated in the docstring better make sure.
no_rad = Wire.assembleEdges(
[
Edge.makeLine(Vector(0, 0, 0), Vector(0, 1, 0)),
Edge.makeCircle(1.0, angle1=90, angle2=270),
]
)
with self.assertRaises(ValueError):
no_rad.radius()
yes_rad = Wire.assembleEdges(
[
Edge.makeCircle(1.0, angle1=90, angle2=270),
Edge.makeLine(Vector(0, -1, 0), Vector(0, 1, 0)),
]
)
self.assertAlmostEqual(yes_rad.radius(), 1.0)
many_rad = Wire.assembleEdges(
[
Edge.makeCircle(1.0, angle1=0, angle2=180),
Edge.makeCircle(3.0, pnt=Vector(2, 0, 0), angle1=180, angle2=359),
]
)
self.assertAlmostEqual(many_rad.radius(), 1.0)
def testWireFillet(self):
points = [
(0.000, 0.000, 0.000),
(-0.287, 1.183, -0.592),
(-1.404, 4.113, -2.787),
(-1.332, 1.522, 0.553),
(7.062, 0.433, -0.097),
(8.539, -0.000, -0.000),
]
wire = Wire.makePolygon(points, close=False)
# Fillet the wire
wfillet = wire.fillet(radius=0.560)
assert len(wfillet.Edges()) == 2 * len(points) - 3
# Fillet a single vertex
wfillet = wire.fillet(radius=0.560, vertices=wire.Vertices()[1:2])
assert len(wfillet.Edges()) == len(points)
# Assert exception if trying to fillet with too big
# a radius
with self.assertRaises(ValueError):
wfillet = wire.fillet(radius=1.0)
@pytest.mark.parametrize(
"points, close, expected_edges",
[
(((0, 0, 0), (0, 1, 0), (1, 0, 0)), False, 2),
(((0, 0, 0), (0, 1, 0), (1, 0, 0)), True, 3),
(((0, 0, 0), (0, 1, 0), (1, 0, 0), (0, 0, 0)), False, 3),
(((0, 0, 0), (0, 1, 0), (1, 0, 0), (0, 0, 0)), True, 3),
],
)
def test_wire_makepolygon(points, close, expected_edges):
assert len(Wire.makePolygon(points, False, close).Edges()) == expected_edges
if __name__ == "__main__":
unittest.main()