def overlapping_path(): path = Path() path.moveTo(0, 0) path.lineTo(10, 0) path.lineTo(10, 10) path.lineTo(0, 10) path.close() path.moveTo(5, 5) path.lineTo(15, 5) path.lineTo(15, 15) path.lineTo(5, 15) path.close() return path
def _convert_vmobject_to_skia_path(self, vmobject: VMobject) -> SkiaPath: """Converts a :class:`~.VMobject` to SkiaPath. This method only works for cairo renderer because it treats the points as Cubic beizer curves. Parameters ---------- vmobject: The :class:`~.VMobject` to convert from. Returns ------- SkiaPath: The converted path. """ path = SkiaPath() if not np.all(np.isfinite(vmobject.points)): points = np.zeros((1, 3)) # point invalid? else: points = vmobject.points if len(points) == 0: # what? No points so return empty path return path # In OpenGL it's quadratic beizer curves while on Cairo it's cubic... if config.renderer == "opengl": subpaths = vmobject.get_subpaths_from_points(points) for subpath in subpaths: quads = vmobject.get_bezier_tuples_from_points(subpath) start = subpath[0] path.moveTo(*start[:2]) for p0, p1, p2 in quads: path.quadTo(*p1[:2], *p2[:2]) if vmobject.consider_points_equals(subpath[0], subpath[-1]): path.close() else: subpaths = vmobject.gen_subpaths_from_points_2d(points) for subpath in subpaths: quads = vmobject.gen_cubic_bezier_tuples_from_points(subpath) start = subpath[0] path.moveTo(*start[:2]) for p0, p1, p2, p3 in quads: path.cubicTo(*p1[:2], *p2[:2], *p3[:2]) if vmobject.consider_points_equals_2d(subpath[0], subpath[-1]): path.close() return path
def test_pen_addComponent_decomposed_from_glyphSet(self): a = Path() a.moveTo(0, 0) a.lineTo(1, 0) a.lineTo(1, 1) a.lineTo(0, 1) a.close() glyphSet = {"a": a} b = Path() pen = b.getPen(glyphSet=glyphSet) pen.addComponent("a", (2, 0, 0, 2, 10, 10)) glyphSet["b"] = b assert list(b) == [ (PathVerb.MOVE, ((10, 10),)), (PathVerb.LINE, ((12, 10),)), (PathVerb.LINE, ((12, 12),)), (PathVerb.LINE, ((10, 12),)), (PathVerb.CLOSE, ()), ] c = Path() pen = c.getPen(glyphSet=glyphSet) pen.addComponent("a", (1, 0, 0, 1, 2, 2)) pen.addComponent("b", (1, 0, 0, 1, -10, -10)) glyphSet["c"] = c assert list(c) == [ (PathVerb.MOVE, ((2, 2),)), (PathVerb.LINE, ((3, 2),)), (PathVerb.LINE, ((3, 3),)), (PathVerb.LINE, ((2, 3),)), (PathVerb.CLOSE, ()), (PathVerb.MOVE, ((0, 0),)), (PathVerb.LINE, ((2, 0),)), (PathVerb.LINE, ((2, 2),)), (PathVerb.LINE, ((0, 2),)), (PathVerb.CLOSE, ()), ]
def test_transform(self): path = Path() path.moveTo(125, 376) path.cubicTo(181, 376, 218, 339, 218, 290) path.cubicTo(218, 225, 179, 206, 125, 206) path.close() # t = Transform().rotate(radians(-45)).translate(-100, 0) matrix = (0.707107, -0.707107, 0.707107, 0.707107, -70.7107, 70.7107) result = path.transform(*matrix) expected = Path() expected.moveTo( bits2float(0x438dc663), # 283.55 bits2float(0x437831ce), # 248.195 ) expected.cubicTo( bits2float(0x43a192ee), # 323.148 bits2float(0x435098b8), # 208.597 bits2float(0x43a192ee), # 323.148 bits2float(0x431c454a), # 156.271 bits2float(0x43903ff5), # 288.5 bits2float(0x42f33ead), # 121.622 ) expected.cubicTo( bits2float(0x437289a8), # 242.538 bits2float(0x42975227), # 75.6605 bits2float(0x43498688), # 201.526 bits2float(0x42b39aee), # 89.8026 bits2float(0x4323577c), # 163.342 bits2float(0x42fff906), # 127.986 ) expected.close() result.dump(as_hex=True) assert result == expected
def test_strip_collinear_moveTo(): # https://github.com/fonttools/skia-pathops/issues/12 path = Path() path.moveTo( bits2float(0x440b8000), # 558 bits2float(0x0), # 0 ) path.lineTo( bits2float(0x44098000), # 550 bits2float(0x0), # 0 ) path.lineTo( bits2float(0x440c247f), # 560.57 bits2float(0x41daf87e), # 27.3713 ) path.lineTo( bits2float(0x440e247f), # 568.57 bits2float(0x41daf87e), # 27.3713 ) path.close() path.moveTo( bits2float(0x440b0000), # 556 bits2float(0x40e00000), # 7 ) path.lineTo( bits2float(0x440a4000), # 553 bits2float(0x0), # 0 ) path.lineTo( bits2float(0x44049c26), # 530.44 bits2float(0x0), # 0 ) path.lineTo( bits2float(0x44052891), # 532.634 bits2float(0x40e00000), # 7 ) path.close() path.simplify() expected = Path() expected.moveTo( bits2float(0x440b8000), # 558 bits2float(0x0), # 0 ) expected.lineTo( bits2float(0x440e247f), # 568.57 bits2float(0x41daf87e), # 27.3713 ) expected.lineTo( bits2float(0x440c247f), # 560.57 bits2float(0x41daf87e), # 27.3713 ) expected.lineTo( bits2float(0x440a2d02), # 552.703 bits2float(0x40e00000), # 7 ) expected.lineTo( bits2float(0x44052891), # 532.634 bits2float(0x40e00000), # 7 ) expected.lineTo( bits2float(0x44049c26), # 530.44 bits2float(0x0), # 0 ) # expected.lineTo( # bits2float(0x44098000), # 550 # bits2float(0x0), # 0 # ) expected.close() assert list(path) == list(expected)
def test_duplicate_start_point(): # https://github.com/fonttools/skia-pathops/issues/13 path = Path() path.moveTo( bits2float(0x43480000), # 200 bits2float(0x43db8ce9), # 439.101 ) path.lineTo( bits2float(0x43480000), # 200 bits2float(0x4401c000), # 519 ) path.cubicTo( bits2float(0x43480000), # 200 bits2float(0x441f0000), # 636 bits2float(0x43660000), # 230 bits2float(0x44340000), # 720 bits2float(0x43c80000), # 400 bits2float(0x44340000), # 720 ) path.cubicTo( bits2float(0x4404c000), # 531 bits2float(0x44340000), # 720 bits2float(0x440d0000), # 564 bits2float(0x442b8000), # 686 bits2float(0x44118000), # 582 bits2float(0x4416c000), # 603 ) path.lineTo( bits2float(0x442cc000), # 691 bits2float(0x441c8000), # 626 ) path.cubicTo( bits2float(0x44260000), # 664 bits2float(0x443d4000), # 757 bits2float(0x44114000), # 581 bits2float(0x444a8000), # 810 bits2float(0x43c88000), # 401 bits2float(0x444a8000), # 810 ) path.cubicTo( bits2float(0x43350000), # 181 bits2float(0x444a8000), # 810 bits2float(0x42c80000), # 100 bits2float(0x442e0000), # 696 bits2float(0x42c80000), # 100 bits2float(0x4401c000), # 519 ) path.lineTo( bits2float(0x42c80000), # 100 bits2float(0x438a8000), # 277 ) path.cubicTo( bits2float(0x42c80000), # 100 bits2float(0x42cc0000), # 102 bits2float(0x433e0000), # 190 bits2float(0xc1200000), # -10 bits2float(0x43cd0000), # 410 bits2float(0xc1200000), # -10 ) path.cubicTo( bits2float(0x441d8000), # 630 bits2float(0xc1200000), # -10 bits2float(0x442f0000), # 700 bits2float(0x42e60000), # 115 bits2float(0x442f0000), # 700 bits2float(0x437a0000), # 250 ) path.lineTo( bits2float(0x442f0000), # 700 bits2float(0x43880000), # 272 ) path.cubicTo( bits2float(0x442f0000), # 700 bits2float(0x43d18000), # 419 bits2float(0x44164000), # 601 bits2float(0x43fa0000), # 500 bits2float(0x43c88000), # 401 bits2float(0x43fa0000), # 500 ) path.cubicTo( bits2float(0x43964752), # 300.557 bits2float(0x43fa0000), # 500 bits2float(0x436db1ed), # 237.695 bits2float(0x43ef6824), # 478.814 bits2float(0x43480000), # 200 bits2float(0x43db8ce9), # 439.101 ) path.close() path.moveTo( bits2float(0x434805cb), # 200.023 bits2float(0x43881798), # 272.184 ) path.cubicTo( bits2float(0x43493da4), # 201.241 bits2float(0x43b2a869), # 357.316 bits2float(0x437bd6b1), # 251.839 bits2float(0x43cd0000), # 410 bits2float(0x43c80000), # 400 bits2float(0x43cd0000), # 410 ) path.cubicTo( bits2float(0x44098000), # 550 bits2float(0x43cd0000), # 410 bits2float(0x44160000), # 600 bits2float(0x43b20000), # 356 bits2float(0x44160000), # 600 bits2float(0x43868000), # 269 ) path.lineTo( bits2float(0x44160000), # 600 bits2float(0x43808000), # 257 ) path.cubicTo( bits2float(0x44160000), # 600 bits2float(0x43330000), # 179 bits2float(0x44110000), # 580 bits2float(0x429c0000), # 78 bits2float(0x43cd0000), # 410 bits2float(0x429c0000), # 78 ) path.cubicTo( bits2float(0x43725298), # 242.323 bits2float(0x429c0000), # 78 bits2float(0x43491e05), # 201.117 bits2float(0x431ccd43), # 156.802 bits2float(0x434805cb), # 200.023 bits2float(0x43881797), # 272.184 ) path.close() contours = list(path.contours) # on the second contour, the last and first points' Y coordinate only # differ by one bit: 0x43881798 != 0x43881797 points = contours[1].points assert points[0] != points[-1] assert points[0] == pytest.approx(points[-1]) # when "drawn" as segments, almost equal last/first points are treated # as exactly equal, without the need of an extra closing lineTo for contour in path.contours: segments = list(contour.segments) assert segments[-1][0] == "closePath" first_type, first_pts = segments[0] last_type, last_pts = segments[-2] assert first_type == "moveTo" assert last_type == "curveTo" assert last_pts[-1] == first_pts[-1]