Cosplay/nhf/test.py

250 lines
7.8 KiB
Python
Raw Normal View History

"""
Unit tests for tooling
"""
2024-07-21 00:04:59 -07:00
from dataclasses import dataclass
import math
2024-06-20 23:29:18 -07:00
import unittest
2024-06-24 11:13:11 -07:00
import cadquery as Cq
2024-07-04 00:42:14 -07:00
from nhf.build import Model, target
2024-07-21 00:04:59 -07:00
from nhf.parts.item import Item
import nhf.checks
import nhf.geometry
import nhf.utils
# Color presets for testing purposes
color_parent = Cq.Color(0.7, 0.7, 0.5, 0.5)
color_child = Cq.Color(0.5, 0.7, 0.7, 0.5)
def makeSphere(r: float) -> Cq.Solid:
"""
Makes a full sphere. The default function makes a hemisphere
"""
return Cq.Solid.makeSphere(r, angleDegrees1=-90)
2024-06-20 23:29:18 -07:00
2024-07-21 00:04:59 -07:00
@dataclass(frozen=True)
class MassBall(Item):
"""
A ball with fixed mass
"""
radius: float = 0.2
@property
def name(self) -> str:
return f"MassBall {self.mass}"
def generate(self) -> Cq.Solid:
return makeSphere(self.radius)
2024-07-04 00:42:14 -07:00
class BuildScaffold(Model):
2024-06-20 23:29:18 -07:00
def __init__(self):
super().__init__(name="scaffold")
2024-07-04 00:42:14 -07:00
@target(name="obj1")
def o1(self):
return Cq.Solid.makeBox(10, 10, 10)
2024-06-30 14:28:42 -07:00
2024-07-04 00:42:14 -07:00
def o2(self):
return Cq.Solid.makeCylinder(10, 20)
2024-06-20 23:29:18 -07:00
2024-07-04 00:42:14 -07:00
class TestBuild(unittest.TestCase):
2024-06-25 06:11:48 -07:00
2024-07-04 00:42:14 -07:00
def test_build_scaffold(self):
s = BuildScaffold()
names = ["obj1"]
self.assertEqual(s.target_names, names)
self.assertEqual(s.check_all(), len(names))
2024-06-26 08:28:25 -07:00
class TestChecks(unittest.TestCase):
def intersect_test_case(self, offset):
assembly = (
Cq.Assembly()
.add(Cq.Solid.makeBox(10, 10, 10),
name="c1",
loc=Cq.Location((0, 0, 0)))
.add(Cq.Solid.makeBox(10, 10, 10),
name="c2",
loc=Cq.Location((0, 0, offset)))
)
coll = nhf.checks.pairwise_intersection(assembly)
if -10 < offset and offset < 10:
self.assertEqual(len(coll), 1)
else:
self.assertEqual(coll, [])
def test_intersect(self):
for offset in [9, 10, 11, -10]:
with self.subTest(offset=offset):
self.intersect_test_case(offset)
class TestGeometry(unittest.TestCase):
def test_min_radius_contraction_span_pos(self):
sl = 50.0
dc = 112.0
do = dc + sl
theta = math.radians(60.0)
r, phi = nhf.geometry.min_radius_contraction_span_pos(do, dc, theta)
with self.subTest(state='open'):
x = r * math.cos(phi)
y = r * math.sin(phi)
d = math.sqrt((x - r) ** 2 + y ** 2)
self.assertAlmostEqual(d, do)
with self.subTest(state='closed'):
x = r * math.cos(phi - theta)
y = r * math.sin(phi - theta)
d = math.sqrt((x - r) ** 2 + y ** 2)
self.assertAlmostEqual(d, dc)
def test_min_tangent_contraction_span_pos(self):
sl = 50.0
dc = 112.0
do = dc + sl
theta = math.radians(60.0)
r, phi, rp = nhf.geometry.min_tangent_contraction_span_pos(do, dc, theta)
with self.subTest(state='open'):
x = r * math.cos(phi)
y = r * math.sin(phi)
d = math.sqrt((x - rp) ** 2 + y ** 2)
self.assertAlmostEqual(d, do)
with self.subTest(state='closed'):
x = r * math.cos(phi - theta)
y = r * math.sin(phi - theta)
d = math.sqrt((x - rp) ** 2 + y ** 2)
self.assertAlmostEqual(d, dc)
class TestUtils(unittest.TestCase):
2024-07-17 14:47:34 -07:00
def test_2d_orientation(self):
l1 = Cq.Location.from2d(1.2, 0)
l2 = Cq.Location.from2d(0, 0, 90)
l3 = l2 * l1
(x, y), r = l3.to2d()
self.assertAlmostEqual(x, 0)
self.assertAlmostEqual(y, 1.2)
self.assertAlmostEqual(r, 90)
def test_2d_planar(self):
l1 = Cq.Location.from2d(1.2, 4.5, 67)
l2 = Cq.Location.from2d(98, 5.4, 36)
l3 = Cq.Location.from2d(10, 10, 0)
l = l3 * l2 * l1
self.assertTrue(l.is2d())
def test_tag_point(self):
"""
A board with 3 holes of unequal sizes. Each hole is marked
"""
p4x, p4y = 5, 5
p3x, p3y = 0, 0
p2x, p2y = -5, 0
board = (
Cq.Workplane('XY')
.box(15, 15, 5)
.faces("<Z")
.workplane()
.pushPoints([(p4x, p4y)])
.hole(4, depth=2)
.pushPoints([(p3x, p3y)])
.hole(3, depth=1.5)
.pushPoints([(p2x, p2y)])
.hole(2, depth=1)
)
board.moveTo(p4x, p4y).tagPoint("h4")
board.moveTo(p3x, p3y).tagPoint("h3")
board.moveTo(p2x, p2y).tagPoint("h2")
assembly = (
Cq.Assembly()
.add(board, name="board", color=color_parent)
.add(makeSphere(2), name="s4", color=color_child)
.add(makeSphere(1.5), name="s3", color=color_child)
.add(makeSphere(1), name="s2", color=color_child)
.constrain("board?h4", "s4", "Point")
.constrain("board?h3", "s3", "Point")
.constrain("board?h2", "s2", "Point")
.solve()
)
self.assertEqual(nhf.checks.pairwise_intersection(assembly), [])
bbox = assembly.toCompound().BoundingBox()
self.assertAlmostEqual(bbox.xlen, 15)
self.assertAlmostEqual(bbox.ylen, 15)
self.assertAlmostEqual(bbox.zlen, 7)
def test_tag_plane(self):
p4x, p4y = 5, 5
p3x, p3y = 0, 0
p2x, p2y = -5, 0
board = (
Cq.Workplane('XY')
.box(15, 15, 5)
.faces("<Z")
.workplane()
.pushPoints([(p4x, p4y)])
.hole(4, depth=2)
.pushPoints([(p3x, p3y)])
.hole(3, depth=1.5)
.pushPoints([(p2x, p2y)])
.hole(2, depth=1)
)
board.moveTo(p4x, p4y).tagPlane("h4")
board.moveTo(p3x, p3y).tagPlane("h3")
board.moveTo(p2x, p2y).tagPlane("h2")
def markedCylOf(r):
cyl = (
Cq.Workplane('XY')
.cylinder(radius=r, height=r)
)
cyl.faces("<Z").tag("mate")
return cyl
assembly = (
Cq.Assembly()
.add(board, name="board", color=color_parent)
.add(markedCylOf(2), name="c4", color=color_child)
.add(markedCylOf(1.5), name="c3", color=color_child)
.add(markedCylOf(1), name="c2", color=color_child)
.constrain("board?h4", "c4?mate", "Plane", param=0)
.constrain("board?h3", "c3?mate", "Plane", param=0)
.constrain("board?h2", "c2?mate", "Plane", param=0)
.solve()
)
self.assertEqual(nhf.checks.pairwise_intersection(assembly), [])
bbox = assembly.toCompound().BoundingBox()
self.assertAlmostEqual(bbox.xlen, 15)
self.assertAlmostEqual(bbox.ylen, 15)
self.assertAlmostEqual(bbox.zlen, 5)
2024-06-25 06:11:48 -07:00
2024-07-20 23:52:11 -07:00
def test_abs_location(self):
box = Cq.Solid.makeBox(1, 1, 1)
assembly = (
Cq.Assembly()
.add(box, name="b1")
.add(box, name="b2", loc=Cq.Location((0,0,1)))
.add(box, name="b3", loc=Cq.Location((0,0,2)))
)
(x, y, z), _ = assembly.get_abs_location("b2@faces@>Y").toTuple()
self.assertAlmostEqual(x, 0.5)
self.assertAlmostEqual(y, 1)
self.assertAlmostEqual(z, 1.5)
(rx, ry, rz), _ = assembly.get_abs_direction("b2@faces@>Y").toTuple()
self.assertAlmostEqual(rx, 0)
self.assertAlmostEqual(ry, 1)
self.assertAlmostEqual(rz, 0)
2024-07-21 00:04:59 -07:00
def test_centre_of_mass(self):
assembly = (
Cq.Assembly()
.add(MassBall(mass=3).assembly(), name="s1", loc=Cq.Location((0, 0, 0)))
.add(MassBall(mass=7).assembly(), name="s2", loc=Cq.Location((0, 0, 10)))
)
com = assembly.centre_of_mass()
self.assertAlmostEqual(com.x, 0)
self.assertAlmostEqual(com.y, 0)
self.assertAlmostEqual(com.z, 7)
2024-06-20 23:29:18 -07:00
if __name__ == '__main__':
unittest.main()