fix cli redundant images
finger-xynova first design completed
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
"""Run with python -m pytest blender/scripts/test_seam_gap.py (no Blender needed).
|
||||
|
||||
The margin exists to absorb two-pass registration error, so the property that matters is that
|
||||
the band's width is the requested *physical* distance and that the edge is straight. Both are
|
||||
checkable in closed form on a triangle whose surface-to-UV map is known.
|
||||
"""
|
||||
import unittest
|
||||
|
||||
from seam_gap import clip_polygon, polygon_area, triangle_gap_planes
|
||||
|
||||
# Unit right triangle in the world XY plane, so world (x, y) and UV can be made identical.
|
||||
P0, P1, P2 = (0.0, 0.0, 0.0), (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)
|
||||
TRIANGLE = (P0, P1, P2)
|
||||
UV_IDENTITY = ((0.0, 0.0), (1.0, 0.0), (0.0, 1.0))
|
||||
# The bottom edge, which lies along world y = 0 (and UV v = 0).
|
||||
SEAM_BOTTOM = ((P0, P1),)
|
||||
|
||||
|
||||
def _signed(plane, point):
|
||||
"""How far ``point`` is on the kept side of ``plane`` (positive = kept)."""
|
||||
(cx, cy), k = plane
|
||||
return cx * point[0] + cy * point[1] - k
|
||||
|
||||
|
||||
class PlaneTests(unittest.TestCase):
|
||||
def test_no_seam_edges_gives_no_planes(self):
|
||||
self.assertEqual(triangle_gap_planes(TRIANGLE, UV_IDENTITY, [], 0.1), [])
|
||||
|
||||
def test_world_margin_maps_to_the_same_uv_margin(self):
|
||||
"""Identity mapping: a 0.1 mm cut in the world is v >= 0.1 in UV."""
|
||||
planes = triangle_gap_planes(TRIANGLE, UV_IDENTITY, SEAM_BOTTOM, 0.1)
|
||||
self.assertEqual(len(planes), 1)
|
||||
# Assert the half-*space*, not the coefficients: the representation is not normalised,
|
||||
# so a scaled (c, k) describes the same cut.
|
||||
self.assertAlmostEqual(_signed(planes[0], (0.5, 0.1)), 0.0, places=9)
|
||||
self.assertLess(_signed(planes[0], (0.5, 0.05)), 0.0)
|
||||
self.assertGreater(_signed(planes[0], (0.5, 0.15)), 0.0)
|
||||
|
||||
def test_uv_scale_is_accounted_for(self):
|
||||
"""A 10x UV scale means the same physical cut is 10x further in UV."""
|
||||
planes = triangle_gap_planes(
|
||||
TRIANGLE, ((0.0, 0.0), (10.0, 0.0), (0.0, 10.0)), SEAM_BOTTOM, 0.1
|
||||
)
|
||||
self.assertEqual(len(planes), 1)
|
||||
# The cut lands at v_uv = 1.0, i.e. the world distance 0.1 is honoured.
|
||||
self.assertAlmostEqual(_signed(planes[0], (5.0, 1.0)), 0.0, places=9)
|
||||
self.assertLess(_signed(planes[0], (5.0, 0.9)), 0.0)
|
||||
self.assertGreater(_signed(planes[0], (5.0, 1.1)), 0.0)
|
||||
|
||||
def test_margin_scales_linearly_in_the_world(self):
|
||||
"""Doubling the inset must double the world distance, whatever UV does."""
|
||||
uv = ((0.0, 0.0), (10.0, 0.0), (0.0, 10.0))
|
||||
near = triangle_gap_planes(TRIANGLE, uv, SEAM_BOTTOM, 0.05)
|
||||
far = triangle_gap_planes(TRIANGLE, uv, SEAM_BOTTOM, 0.10)
|
||||
# Boundary positions along the v axis: 0.5 world mm and 1.0 world mm in UV.
|
||||
self.assertAlmostEqual(_signed(near[0], (5.0, 0.5)), 0.0, places=9)
|
||||
self.assertAlmostEqual(_signed(far[0], (5.0, 1.0)), 0.0, places=9)
|
||||
|
||||
def test_edge_orientation_does_not_change_the_cut(self):
|
||||
forward = triangle_gap_planes(TRIANGLE, UV_IDENTITY, ((P0, P1),), 0.1)
|
||||
reverse = triangle_gap_planes(TRIANGLE, UV_IDENTITY, ((P1, P0),), 0.1)
|
||||
for point in ((0.5, 0.05), (0.5, 0.1), (0.5, 0.2)):
|
||||
self.assertAlmostEqual(
|
||||
_signed(forward[0], point), _signed(reverse[0], point), places=12
|
||||
)
|
||||
|
||||
def test_a_seam_edge_on_the_far_side_keeps_the_triangle(self):
|
||||
"""The cut is oriented into the triangle, so the opposite edge cuts the other way."""
|
||||
planes = triangle_gap_planes(TRIANGLE, UV_IDENTITY, ((P1, P2),), 0.1)
|
||||
self.assertEqual(len(planes), 1)
|
||||
polygon = clip_polygon(UV_IDENTITY, planes)
|
||||
# Clipping away from the hypotenuse keeps the corner at (0, 0).
|
||||
self.assertTrue(any(abs(x) < 1e-9 and abs(y) < 1e-9 for x, y in polygon))
|
||||
|
||||
def test_degenerate_uv_triangle_yields_no_planes(self):
|
||||
collapsed = ((0.0, 0.0), (0.0, 0.0), (0.0, 0.0))
|
||||
self.assertEqual(triangle_gap_planes(TRIANGLE, collapsed, SEAM_BOTTOM, 0.1), [])
|
||||
|
||||
def test_zero_margin_still_produces_the_edge_plane(self):
|
||||
planes = triangle_gap_planes(TRIANGLE, UV_IDENTITY, SEAM_BOTTOM, 0.0)
|
||||
self.assertEqual(len(planes), 1)
|
||||
self.assertAlmostEqual(planes[0][1], 0.0)
|
||||
|
||||
|
||||
class ClipTests(unittest.TestCase):
|
||||
def test_margin_strip_is_removed(self):
|
||||
"""Removing v < 0.1 leaves the triangle (0,0.1), (0.9,0.1), (0,1): area 0.405."""
|
||||
planes = triangle_gap_planes(TRIANGLE, UV_IDENTITY, SEAM_BOTTOM, 0.1)
|
||||
clipped = clip_polygon(UV_IDENTITY, planes)
|
||||
self.assertEqual(len(clipped), 3)
|
||||
self.assertAlmostEqual(polygon_area(clipped), 0.405, places=9)
|
||||
|
||||
def test_clipped_edge_is_straight_at_the_requested_distance(self):
|
||||
planes = triangle_gap_planes(TRIANGLE, UV_IDENTITY, SEAM_BOTTOM, 0.1)
|
||||
clipped = clip_polygon(UV_IDENTITY, planes)
|
||||
# Every vertex introduced by the cut sits exactly on v = 0.1.
|
||||
on_cut = [point for point in clipped if abs(point[1] - 0.1) < 1e-9]
|
||||
self.assertEqual(len(on_cut), 2)
|
||||
# And nothing survives below it.
|
||||
self.assertTrue(all(point[1] >= 0.1 - 1e-9 for point in clipped))
|
||||
|
||||
def test_polygon_entirely_inside_is_returned_unchanged(self):
|
||||
planes = (((0.0, 1.0), -1.0),) # v >= -1, trivially satisfied
|
||||
clipped = clip_polygon(UV_IDENTITY, planes)
|
||||
self.assertAlmostEqual(polygon_area(clipped), 0.5)
|
||||
|
||||
def test_polygon_entirely_outside_is_removed(self):
|
||||
planes = (((0.0, 1.0), 10.0),) # v >= 10
|
||||
self.assertEqual(clip_polygon(UV_IDENTITY, planes), [])
|
||||
|
||||
def test_two_planes_intersect_correctly(self):
|
||||
planes = (((0.0, 1.0), 0.1), ((1.0, 0.0), 0.1))
|
||||
clipped = clip_polygon(UV_IDENTITY, planes)
|
||||
self.assertTrue(all(point[1] >= 0.1 - 1e-9 for point in clipped))
|
||||
self.assertTrue(all(point[0] >= 0.1 - 1e-9 for point in clipped))
|
||||
self.assertAlmostEqual(polygon_area(clipped), 0.32, places=9)
|
||||
|
||||
def test_degenerate_input_is_returned_empty(self):
|
||||
self.assertEqual(clip_polygon([(0.0, 0.0), (1.0, 0.0)], []), [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user