def test_toolpath(self): passes = feed.pocket(config, self.pocket()).passes def fy(yl, line): fx = line[-1] # Account for the feed-up line segment. return [*y(yl, line), (Point(fx, yl), Point(fx, yl + 0.25))] paths = [[(l.p1, l.p2) for l in p.segments()] for p in passes] self.assertEqual(paths, [ [ *fy(0.25, (0.25, 2.75)), *fy(0.50, (2.75, 0.25)), *fy(0.75, (0.25, 0.75)), *fy(1.00, (0.75, 0.25)), *fy(1.25, (0.25, 0.75)), *fy(1.50, (0.75, 0.25)), *fy(1.75, (0.25, 0.75)), *fy(2.00, (0.75, 0.25)), *fy(2.25, (0.25, 0.75)), # need to move over after feeding up halfway into a line (Point(0.75, 2.50), Point(0.25, 2.50)), *fy(2.50, (0.25, 2.75)), *y(2.75, (2.75, 0.25)), ], [ *fy(0.75, (2.25, 2.75)), *fy(1.00, (2.75, 2.25)), *fy(1.25, (2.25, 2.75)), *fy(1.50, (2.75, 2.25)), *fy(1.75, (2.25, 2.75)), *fy(2.00, (2.75, 2.25)), *y(2.25, (2.25, 2.75)), ] ])
def test_offset_merge(self): notch = Polygon([Point(0, 0), Point(7, 0), Point(4, 4), Point(3, 4)]) parts = notch.offset(-(1.0 + 0.1)) self.assertEqual(len(parts.polygons), 1) self.assertEqual(len(parts.polygons[0].points), 3)
def test_offset_square(self): square = Polygon([Point(0, 0), Point(3, 0), Point(3, 3), Point(0, 3)]) parts = square.offset(-0.25) self.assertEqual( set(c for poly in parts.polygons for p in poly.points for c in (p.x, p.y)), set([0.25, 2.75]))
def test_double_diamond(self): diamond = Polygon([ Point(1, 0), Point(2, 1), Point(1, 2), Point(0, 1), ]) double = [diamond, diamond + Point(3, 0.1)] polygons = decompose.trapezoidal(double)
def test_offset_split_notch(self): notch = Polygon( [Point(0, 0), Point(6, 0), Point(6, 5), Point(3, 1), Point(0, 5)]) parts = notch.offset(-(0.375 + 0.1)) self.assertEqual(len(parts.polygons), 2)
def test_part(self): r = 10 n = 6 angles = [tau * i / n for i in range(n)] circle = Polygon([ Point(math.cos(theta) * r, math.sin(theta) * r) for theta in angles ]) part = Part(circle, [Tab(Line(Point(0, 0), Vector(0, 1)), 2)], 1.0) passes = feed.part(config, part).passes self.assertEqual(len(passes), 2)
def test_contain_through_lines(self): shape = Polygon([Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]) self.assertFalse(shape.contains(Point(-1, 0))) self.assertFalse(shape.contains(Point(0, -1))) self.assertFalse(shape.contains(Point(2, 0))) self.assertFalse(shape.contains(Point(0, 2)))
def test_point_on_shape(self): shape = Polygon([Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]) self.assertTrue(shape.contains(Point(0.5, 0))) self.assertTrue(shape.contains(Point(0, 0.5))) self.assertTrue(shape.contains(Point(1, 0.5))) self.assertTrue(shape.contains(Point(0.5, 1)))
def test_outset(self): square = Polygon([Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)]) self.assertEqual(list(square.offset(0.1).points), [ Point(-0.1, -0.1), Point(-0.1, 1.1), Point(1.1, 1.1), Point(1.1, -0.1) ])
def test_engrave(self): polygon = ComplexPolygon([ Polygon([Point(0, 0), Point(0, 100), Point(100, 100), Point(100, 0)]), Polygon([Point(1, 1), Point(1, 1.25), Point(1.25, 1.25), Point(1.25, 1)]) ]) engrave = Engrave(polygon, 0.5) passes = feed.engrave(config, engrave).passes self.assertEqual(len(passes[0].path), 5)
def test_unworkable_part_removal(self): polygon = ComplexPolygon([ Polygon([Point(0, 0), Point(0, 100), Point(100, 100), Point(100, 0)]), Polygon([Point(1, 1), Point(1, 1.25), Point(1.25, 1.25), Point(1.25, 1)]) ]) pocket = Part(polygon, [], 1.0) passes = feed.part(config, pocket).passes self.assertEqual(len(passes), 1)
def test_square(self): square = Polygon([ Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0), ]) polygons = decompose.trapezoidal([square]) self.assertEqual([p.points for p in polygons], [ [Point(0, 0), Point(0, 1), Point(1, 1), Point(1, 0)], ])
def test_simple(self): polygons = [ rect(Point(0, 0), Point(1, 1)), rect(Point(1, 0), Point(2, 2)) ] segments = [l for p in polygons for l in p.segments()] fragments = [ LineSegment(l.p1, l.p2) for l in decompose.fragment(segments) ] self.assertEqual( set(fragments) - set(segments), set([ LineSegment(Point(1, 0), Point(1, 1)), LineSegment(Point(1, 1), Point(1, 2)) ])) self.assertEqual( set(segments) - set(fragments), set([LineSegment(Point(1, 0), Point(1, 2))]))
def test_complex_offset_cleanup(self): polygon = ComplexPolygon([ Polygon( [Point(0, 0), Point(0, 100), Point(100, 100), Point(100, 0)]), Polygon([Point(1, -1), Point(1, -2), Point(2, -2), Point(2, -1)]) ]) self.assertEqual(len(polygon.offset(-2).exterior), 1)
def test_offset_foot_double_merge(self): foot = Polygon([ Point(0, 0), Point(10, 0), Point(10, 1), Point(10, 10), Point(1, 10), Point(1, 2), Point(0, 1) ]) rect = foot.offset(-2) self.assertEqual(len(rect.polygons[0].points), 4)
def test_inset_local_merge(self): # the magic of 3-4-5 triangles... trapezoid = Polygon( [Point(0, 0), Point(9, 12), Point(9 + 5, 12), Point(9 + 5 + 9, 0)]) self.assertEqual( [p.round(4) for p in trapezoid.offset(-5).points], [Point(10 + 3, 5), Point(10, 5), Point(10 + (3 / 2), 5 + (4 / 2))])
def test_star_contain(self): # taken from https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/fill-rule # we use the evenodd fill-rule so we don't have to calculate crossing # orientation. star = Polygon([ Point(50, 0), Point(21, 90), Point(98, 35), Point(2, 35), Point(79, 90) ]) self.assertFalse(star.contains(Point(50, 50)))
def test_embedded(self): square = Polygon([ Point(0, 0), Point(0, 10), Point(10, 10), Point(10, 0), ]) inner = Polygon([Point(4, 4), Point(4, 6), Point(6, 6), Point(6, 4)]) polygons = decompose.trapezoidal([square, inner]) self.assertEqual([p.points for p in polygons], [[ Point(0.0, 0.0), Point(0.0, 4.0), Point(10.0, 4.0), Point(10.0, 0.0) ], [ Point(0.0, 4.0), Point(0.0, 6.0), Point(4.0, 6.0), Point(4.0, 4.0) ], [ Point(6.0, 4.0), Point(6.0, 6.0), Point(10.0, 6.0), Point(10.0, 4.0) ], [ Point(0.0, 6.0), Point(0.0, 10.0), Point(10.0, 10.0), Point(10.0, 6.0) ]])
def test_contain_through_point(self): # when we cross through a vertex, it still only should count as one # boundary crossing for evenodd purposes. shape = Polygon([Point(-1, 0), Point(0, 1), Point(1, 0), Point(0, -1)]) self.assertTrue(shape.contains(Point(0, 0))) self.assertTrue(shape.contains(Point(-0.5, 0))) self.assertTrue(shape.contains(Point(0, -0.5))) self.assertTrue(shape.contains(Point(0, 0.5))) self.assertTrue(shape.contains(Point(0.5, 0))) self.assertFalse(shape.contains(Point(2, 0))) self.assertFalse(shape.contains(Point(0, 2))) self.assertFalse(shape.contains(Point(0, -2))) self.assertFalse(shape.contains(Point(-2, 0)))
def test_generic_instance(self): self.assertTrue(isinstance(Point(1, 1), generic.Point))
def test_complex_multiply(self): polygon = ComplexPolygon([ Polygon([Point(0, 0), Point(0, 3), Point(3, 3), Point(3, 0)]), Polygon([Point(1, 1), Point(1, 2), Point(2, 2), Point(2, 1)]) ]) moved = polygon * Vector(20, 10) self.assertEqual(moved.interior, [ Polygon([Point(1, 1), Point(1, 2), Point(2, 2), Point(2, 1)]) * Vector(20, 10) ]) self.assertEqual(moved.exterior, [ Polygon([Point(0, 0), Point(0, 3), Point(3, 3), Point(3, 0)]) * Vector(20, 10) ]) moved = polygon * 2 self.assertEqual(moved.interior, [ Polygon([Point(1, 1), Point(1, 2), Point(2, 2), Point(2, 1)]) * 2 ]) self.assertEqual(moved.exterior, [ Polygon([Point(0, 0), Point(0, 3), Point(3, 3), Point(3, 0)]) * 2 ]) moved = polygon / 2 self.assertEqual(moved.interior, [ Polygon([Point(1, 1), Point(1, 2), Point(2, 2), Point(2, 1)]) / 2 ]) self.assertEqual(moved.exterior, [ Polygon([Point(0, 0), Point(0, 3), Point(3, 3), Point(3, 0)]) / 2 ])
def y(y, pairs): pairs = [pairs] if isinstance(pairs, tuple) else pairs return [(Point(x1, y), Point(x2, y)) for x1, x2 in pairs]
def test_inset_split(self): dumbbell = Polygon([ Point(0, 0), Point(0, 3), Point(1, 3), Point(1, 4), Point(0, 4), Point(0, 7), Point(3, 7), Point(3, 4), Point(2, 4), Point(2, 3), Point(3, 3), Point(3, 0) ]) self.assertEqual(dumbbell.inset(2), [ Polygon([Point(1, 5), Point(1, 6), Point(2, 6), Point(2, 5)]), Polygon([Point(1, 1), Point(1, 2), Point(2, 2), Point(2, 1)]) ])
def pocket(self): polygon = ComplexPolygon([ Polygon([Point(0, 0), Point(0, 3), Point(3, 3), Point(3, 0)]), Polygon([Point(1, 1), Point(1, 2), Point(2, 2), Point(2, 1)]) ]) return Pocket(polygon, 1.0)
def test_batching_from_bottom_overlap(self): polygon = Circle(Point(0, 0), 2.0) pocket = Pocket(polygon, 1.0) batches = batch_scanlines(feed.scanlines(config, pocket)) self.assertEqual(len(batches), 1)
def fy(yl, line): fx = line[-1] # Account for the feed-up line segment. return [*y(yl, line), (Point(fx, yl), Point(fx, yl + 0.25))]
def test_complex(self): f = Polygon([ Point(0, 0), Point(0, 7), Point(4, 7), Point(4, 5), Point(2, 5), Point(2, 4), Point(4, 4), Point(4, 2), Point(2, 2), Point(2, 0) ]) polygons = decompose.trapezoidal([f]) self.assertEqual( [p.points for p in polygons], [[Point(0, 0), Point(0, 2), Point(2, 2), Point(2, 0)], [Point(0, 2), Point(0, 4), Point(4, 4), Point(4, 2)], [Point(0, 4), Point(0, 5), Point(2, 5), Point(2, 4)], [Point(0, 5), Point(0, 7), Point(4, 7), Point(4, 5)]]) inverted = Polygon([Point(p.y, p.x) for p in f.points]) polygons = decompose.trapezoidal([inverted]) self.assertEqual([p.points for p in polygons], [[ Point(7.0, 0.0), Point(7.0, 2.0), Point(0.0, 2.0), Point(0.0, 0.0) ], [ Point(2.0, 2.0), Point(2.0, 4.0), Point(4.0, 4.0), Point(4.0, 2.0) ], [ Point(5.0, 2.0), Point(5.0, 4.0), Point(7.0, 4.0), Point(7.0, 2.0) ]])
def test_complex_add(self): polygon = ComplexPolygon([ Polygon([Point(0, 0), Point(0, 3), Point(3, 3), Point(3, 0)]), Polygon([Point(1, 1), Point(1, 2), Point(2, 2), Point(2, 1)]) ]) moved = polygon + Vector(20, 10) self.assertEqual(moved.interior, [ Polygon([Point(1, 1), Point(1, 2), Point(2, 2), Point(2, 1)]) + Vector(20, 10) ]) self.assertEqual(moved.exterior, [ Polygon([Point(0, 0), Point(0, 3), Point(3, 3), Point(3, 0)]) + Vector(20, 10) ])
def test_inwards(self): w = Polygon([ Point(0, 0), Point(0, 2), Point(1, 2), Point(1, 1), Point(2, 1), Point(2, 2), Point(3, 2), Point(3, 1), Point(4, 1), Point(4, 2), Point(5, 2), Point(5, 0), ]) down = Vector(0, -1) up = Vector(0, 1) left = Vector(-1, 0) right = Vector(1, 0) directions = [ right, down, left, down, right, down, left, down, right, down, left, up ] for s, d in zip(w.segments(), directions): self.assertEqual(w.inwards(s), d)
def test_circle_inset(self): c = Circle(Point(0, 0), 53.975, segments=100).to_clockwise() self.assertEqual(len(list(c.offset(-1.5875).points)), 100)