AATA: Add test matrix to spot check min/max limits, tangency for each condition

This commit is contained in:
Jonathan Wagenet 2025-08-01 15:49:26 -04:00
parent ab6eaff52b
commit 6dd89cf004
2 changed files with 88 additions and 12 deletions

View file

@ -1377,7 +1377,7 @@ class ArcArcTangentArc(BaseEdgeObject):
keep specifies tangent arc position with a Keep pair: (placement, type)
- placement: start_arc is tangent INSIDE or OUTSIDE the tangent arc. BOTH is a
- placement: start_arc is tangent INSIDE or OUTSIDE the tangent arc. BOTH is a
special case for overlapping arcs with type INSIDE
- type: tangent arc is INSIDE or OUTSIDE start_arc and end_arc
@ -1432,13 +1432,14 @@ class ArcArcTangentArc(BaseEdgeObject):
else:
workplane = copy_module.copy(WorkplaneList._get_context().workplanes[0])
side_sign = 1 if side == Side.LEFT else -1
keep_sign = 1 if keep_placement == Keep.OUTSIDE else -1
arcs = [start_arc, end_arc]
points = [arc.arc_center for arc in arcs]
radii = [arc.radius for arc in arcs]
side_sign = 1 if side == Side.LEFT else -1
keep_sign = 1 if keep_placement == Keep.OUTSIDE else -1
r_sign = 1 if radii[0] < radii[1] else -1
# make a normal vector for sorting intersections
# Make a normal vector for sorting intersections
midline = points[1] - points[0]
normal = side_sign * midline.cross(workplane.z_dir)
@ -1447,19 +1448,19 @@ class ArcArcTangentArc(BaseEdgeObject):
if midline.length == sum(radii) and keep_type == Keep.INSIDE:
raise ValueError(
"Cannot find tangent type Keep.INSIDE for non-overlapping arcs " \
"Cannot find tangent type Keep.INSIDE for non-overlapping arcs "
"already tangent."
)
if midline.length == abs(radii[0] - radii[1]) and keep_placement == Keep.INSIDE:
raise ValueError(
"Cannot find tangent placement Keep.INSIDE for completely " \
"Cannot find tangent placement Keep.INSIDE for completely "
"overlapping arcs already tangent."
)
min_radius = 0.
# Set following parameters based on overlap condition and keep configuration
min_radius = 0.0
max_radius = None
r_sign = 1 if radii[0] < radii[1] else -1
x_sign = [1, 1]
pick_index = 0
if midline.length > abs(radii[0] - radii[1]) and keep_type == Keep.OUTSIDE:
@ -1558,6 +1559,7 @@ class ArcArcTangentArc(BaseEdgeObject):
)
arc_center = ref_intersections.sort_by(Axis(points[0], normal))[pick_index]
# x_sign determines if tangent is near side or far side of circle
intersect = [
points[i]
+ x_sign[i] * radii[i] * (Vector(arc_center) - points[i]).normalized()

View file

@ -652,6 +652,7 @@ class BuildLineTests(unittest.TestCase):
- Arcs must be coplanar
- Cannot make tangent for concentric arcs
"""
# Test line properties in algebra mode
start_r = 2
end_r = 5
@ -765,10 +766,83 @@ class BuildLineTests(unittest.TestCase):
start_arc, CenterArc((0, end_r - start_r), end_r, 0, 360), 3
)
# Radius size
with self.assertRaises(ValueError):
r = (separation - (start_r + end_r)) / 2 - 1
ArcArcTangentArc(CenterArc((0, 0, 1), 5, 0, 360), end_arc, r)
## Spot check all conditions
r1, r2 = 3, 8
start_center = (0, 0)
start_arc = CenterArc(start_center, r1, 0, 360)
end_y = {
"no_overlap": (r1 + r2) * 1.1,
"partial_overlap": (r1 + r2) / 2,
"full_overlap": (r2 - r1) * 0.9,
}
# Test matrix:
# (separation, keep pair, [min_limit, max_limit])
# actual limit will be (separation + min_limit) / 2
cases = [
(end_y["no_overlap"], (Keep.INSIDE, Keep.INSIDE), [r1 - r2, None]),
(end_y["no_overlap"], (Keep.OUTSIDE, Keep.INSIDE), [-r1 + r2, None]),
(end_y["no_overlap"], (Keep.INSIDE, Keep.OUTSIDE), [r1 + r2, None]),
(end_y["no_overlap"], (Keep.OUTSIDE, Keep.OUTSIDE), [-r1 - r2, None]),
(end_y["partial_overlap"], (Keep.INSIDE, Keep.INSIDE), [None, r1 - r2]),
(end_y["partial_overlap"], (Keep.OUTSIDE, Keep.INSIDE), [None, -r1 + r2]),
(end_y["partial_overlap"], (Keep.BOTH, Keep.INSIDE), [None, r1 + r2]),
(end_y["partial_overlap"], (Keep.INSIDE, Keep.OUTSIDE), [r1 + r2, None]),
(end_y["partial_overlap"], (Keep.OUTSIDE, Keep.OUTSIDE), [None, None]),
(end_y["full_overlap"], (Keep.INSIDE, Keep.INSIDE), [r1 + r2, r1 + r2]),
(end_y["full_overlap"], (Keep.OUTSIDE, Keep.INSIDE), [-r1 + r2, -r1 + r2]),
]
# Check min and max radii, tangency
for case in cases:
end_center = (0, case[0])
end_arc = CenterArc(end_center, r2, 0, 360)
flip_max = -1 if case[1] == (Keep.BOTH, Keep.INSIDE) else 1
flip_min = -1 if case[0] == end_y["full_overlap"] else 1
min_r = 0 if case[2][0] is None else (flip_min * case[0] + case[2][0]) / 2
max_r = 1e6 if case[2][1] is None else (flip_max * case[0] + case[2][1]) / 2
print(case[1], min_r, max_r, case[0])
print(min_r + 0.01, min_r * 0.99, max_r - 0.01, max_r + 0.01)
print((case[0] - 1 * (r1 + r2)) / 2)
# Greater than min
l1 = ArcArcTangentArc(start_arc, end_arc, min_r + 0.01, keep=case[1])
_, p1, p2 = start_arc.distance_to_with_closest_points(l1)
self.assertTupleAlmostEquals(tuple(p1), tuple(p2), 5)
self.assertAlmostEqual(
start_arc.tangent_at(p1).cross(l1.tangent_at(p2)).length, 0, 5
)
_, p1, p2 = end_arc.distance_to_with_closest_points(l1)
self.assertTupleAlmostEquals(tuple(p1), tuple(p2), 5)
self.assertAlmostEqual(
end_arc.tangent_at(p1).cross(l1.tangent_at(p2)).length, 0, 5
)
# Less than max
l1 = ArcArcTangentArc(start_arc, end_arc, max_r - 0.01, keep=case[1])
_, p1, p2 = start_arc.distance_to_with_closest_points(l1)
self.assertTupleAlmostEquals(tuple(p1), tuple(p2), 5)
self.assertAlmostEqual(
start_arc.tangent_at(p1).cross(l1.tangent_at(p2)).length, 0, 5
)
_, p1, p2 = end_arc.distance_to_with_closest_points(l1)
self.assertTupleAlmostEquals(tuple(p1), tuple(p2), 5)
self.assertAlmostEqual(
end_arc.tangent_at(p1).cross(l1.tangent_at(p2)).length, 0, 5
)
# Less than min
with self.assertRaises(ValueError):
ArcArcTangentArc(start_arc, end_arc, min_r * 0.99, keep=case[1])
# Greater than max
if max_r != 1e6:
with self.assertRaises(ValueError):
ArcArcTangentArc(start_arc, end_arc, max_r + 0.01, keep=case[1])
def test_line_with_list(self):
"""Test line with a list of points"""