Exemple #1
0
    def windingNumberOfPoint(self, pt):
        bounds = self.bounds()
        bounds.addMargin(10)
        ray1 = Line(Point(bounds.left, pt.y), pt)
        ray2 = Line(Point(bounds.right, pt.y), pt)
        leftIntersections = {}
        rightIntersections = {}
        leftWinding = 0
        rightWinding = 0
        for s in self.asSegments():
            for i in s.intersections(ray1):
                # print("Found left intersection with %s: %s" % (ray1, i.point))
                leftIntersections[i.point] = i

            for i in s.intersections(ray2):
                rightIntersections[i.point] = i

        for i in leftIntersections.values():
            # XXX tangents here are all positive? Really?
            # print(i.seg1, i.t1, i.point)
            tangent = s.tangentAtTime(i.t1)
            # print("Tangent at left intersection %s is %f" % (i.point,tangent.y))
            leftWinding += int(math.copysign(1, tangent.y))

        for i in rightIntersections.values():
            tangent = s.tangentAtTime(i.t1)
            # print("Tangent at right intersection %s is %f" % (i.point,tangent.y))
            rightWinding += int(math.copysign(1, tangent.y))

        # print("Left winding: %i right winding: %i " % (leftWinding,rightWinding))
        return max(abs(leftWinding), abs(rightWinding))
Exemple #2
0
    def get_cached_glyph(self, name):
        if name in self.glyphcache: return self.glyphcache[name]
        paths = BezierPath.fromFonttoolsGlyph(self.font, name)
        pathbounds = []
        paths = list(filter(lambda p: p.length > 0, paths))
        for p in paths:
            p.hasAnchor = False
            p.glyphname = name
            if name in self.anchors:
                for a in self.anchors[name]:
                    if p.pointIsInside(Point(*a)): p.hasAnchor = True
            bounds = p.bounds()
            pathbounds.append(bounds)

        glyphbounds = BoundingBox()
        if pathbounds:
            for p in pathbounds:
                glyphbounds.extend(p)
        else:
            glyphbounds.tr = Point(0, 0)
            glyphbounds.bl = Point(0, 0)
        self.glyphcache[name] = {
            "name": name,
            "paths": paths,
            "pathbounds": pathbounds,
            "glyphbounds": glyphbounds,
            "category": categorize_glyph(self.font, name)[0],
            "pathconvexhull": None  # XXX
        }
        assert (len(self.glyphcache[name]["pathbounds"]) == len(
            self.glyphcache[name]["paths"]))
        return self.glyphcache[name]
Exemple #3
0
  def _line_line_intersections(self, other):
    a = self.start
    b = self.end
    c = other.start
    d = other.end
    if isclose(c.x, d.x) and isclose(a.x, b.x): return []
    if isclose(c.y, d.y) and isclose(a.y, b.y): return []
    if c == d or a == b: return []
    if isclose(b.x,a.x):
      x = a.x
      slope34 = ( d.y - c.y) / ( d.x - c.x )
      y = slope34 * ( x - c.x ) + c.y
      p = Point(x,y)
      i = Intersection(self,self.tOfPoint(p), other, other.tOfPoint(p))
      return [ i ]
    if isclose(c.x,d.x):
      x = c.x
      slope12 = ( b.y - a.y) / ( b.x - a.x )
      y = slope12 * ( x - a.x ) + a.y
      p = Point(x,y)
      i = Intersection(self,self.tOfPoint(p), other, other.tOfPoint(p))
      return [ i ]

    slope12 = ( b.y - a.y) / ( b.x - a.x )
    slope34 = ( d.y - c.y) / ( d.x - c.x )
    if abs(slope12 - slope34) < my_epsilon: return [ ]
    x = ( slope12 * a.x - a.y - slope34 * c.x + c.y ) / ( slope12 - slope34 )
    y = slope12 * ( x - a.x ) + a.y
    intersection = Point(x,y)
    if (self._bothPointsAreOnSameSideOfOrigin(intersection, b, a) and self._bothPointsAreOnSameSideOfOrigin(intersection, c, d)):
      return [ Intersection(self,self.tOfPoint(intersection), other, other.tOfPoint(intersection)) ]
    return []
Exemple #4
0
    def thicknessAtX(path, x):
        """Returns the thickness of the path at x-coordinate ``x``."""
        bounds = path.bounds()
        bounds.addMargin(10)
        ray = Line(Point(x - 0.1, bounds.bottom), Point(x + 0.1, bounds.top))
        intersections = []
        for seg in path.asSegments():
            intersections.extend(seg.intersections(ray))
        if len(intersections) < 2:
            return None
        intersections = list(sorted(intersections, key=lambda i: i.point.y))
        i1, i2 = intersections[0:2]
        inorm1 = i1.seg1.normalAtTime(i1.t1)
        ray1 = Line(i1.point + (inorm1 * 1000), i1.point + (inorm1 * -1000))
        iii = i2.seg1.intersections(ray1)
        if iii:
            ll1 = i1.point.distanceFrom(iii[0].point)
        else:
            # Simple, vertical version
            return abs(i1.point.y - i2.point.y)

        inorm2 = i2.seg1.normalAtTime(i2.t1)
        ray2 = Line(i2.point + (inorm2 * 1000), i2.point + (inorm2 * -1000))
        iii = i1.seg1.intersections(ray2)
        if iii:
            ll2 = i2.point.distanceFrom(iii[0].point)
            return (ll1 + ll2) * 0.5
        else:
            return ll1
 def test_extremes3(self):
     # console.log((new Bezier(127,242,27,5,210,60)).extrema())
     q = QuadraticBezier(Point(127, 242), Point(27, 5), Point(210, 60))
     r = q.findExtremes()
     self.assertEqual(len(r), 2)
     self.assertAlmostEqual(r[0], 0.35335689045936397)
     self.assertAlmostEqual(r[1], 0.8116438356164384)
Exemple #6
0
 def test_multiple_application(self):
     p = Point(10, 10)
     m = AffineTransformation()
     m.translate(Point(6, 5))
     m.scale(1.5, 2)
     p.transform(m)
     self.assertEqual(p.x, 24)
     self.assertEqual(p.y, 30)
 def test_quadratic_bounds(self):
     # console.log((new Bezier(150,40,80,30,105,150)).bbox())
     q = QuadraticBezier(Point(150, 40), Point(80, 30), Point(105, 150))
     b = q.bounds()
     self.assertAlmostEqual(b.bl.x, 98.42105263157895)
     self.assertAlmostEqual(b.tr.x, 150)
     self.assertAlmostEqual(b.bl.y, 39.23076923076923)
     self.assertAlmostEqual(b.tr.y, 150)
 def test_overlap(self):
     b1 = BoundingBox()
     b2 = BoundingBox()
     b1.extend(Point(5, 5))
     b1.extend(Point(10, 10))
     b2.extend(Point(7, 7))
     b2.extend(Point(14, 14))
     print("%s v %s" % (b1, b2))
     self.assertTrue(b1.overlaps(b2))
     self.assertTrue(b2.overlaps(b1))
Exemple #9
0
    def drawWithBrush(self, other):
        """Assuming that `other` is a closed Bezier path representing a pen or
    brush of a certain shape and that `self` is an open path, this method
    traces the brush along the path, returning an array of Bezier paths.

    `other` may also be a function which, given a time `t` (0-1), returns a closed
    path representing the shape of the brush at the given time.

    This requires the `shapely` library to be installed.
    """
        from shapely.geometry import Polygon
        from shapely.ops import unary_union
        polys = []
        samples = self.sample(self.length / 2)

        def constantBrush(t):
            return other

        brush = other
        if not callable(brush):
            brush = constantBrush

        c = brush(0).centroid

        from itertools import tee

        def pairwise(iterable):
            "s -> (s0,s1), (s1,s2), (s2, s3), ..."
            a, b = tee(iterable)
            next(b, None)
            return zip(a, b)

        t = 0
        for n in samples:
            brushHere = brush(t).clone().flatten()
            brushHere.translate(n - brushHere.centroid)
            polys.append(
                Polygon([(x[0].x, x[0].y) for x in brushHere.asSegments()]))
            t = t + 1.0 / len(samples)
        concave_hull = unary_union(polys)
        ll = []
        for x, y in pairwise(concave_hull.exterior.coords):
            l = Line(Point(x[0], x[1]), Point(y[0], y[1]))
            ll.append(l)
        paths = [BezierPath.fromSegments(ll)]

        for interior in concave_hull.interiors:
            ll = []
            for x, y in pairwise(interior.coords):
                l = Line(Point(x[0], x[1]), Point(y[0], y[1]))
                ll.append(l)
            paths.append(BezierPath.fromSegments(ll))

        return paths
 def test_extremes(self):
   q = CubicBezier(
     Point(65,59), Point(194,90), Point(220,260), Point(70,261)
   )
   # console.log(Bezier(65,59, 194,90, 220,260, 70,261).extrema())
   r = q.findExtremes()
   self.assertEqual(len(r), 1)
   self.assertAlmostEqual(r[0], 0.5275787707261016)
   r = q.findExtremes(inflections = True)
   self.assertEqual(len(r), 2)
   self.assertAlmostEqual(r[0], 0.4512987012987013)
   self.assertAlmostEqual(r[1], 0.5275787707261016)
Exemple #11
0
 def test_addextremes(self):
     q = CubicBezier(Point(42, 135), Point(129, 242), Point(167, 77),
                     Point(65, 59))
     ex = q.findExtremes()
     self.assertEqual(len(ex), 2)
     path = BezierPath()
     path.closed = False
     path.activeRepresentation = SegmentRepresentation(path, [q])
     path.addExtremes()
     path.balance()
     segs = path.asSegments()
     self.assertEqual(len(segs), 3)
  def _fitCurve(self, points, tangent1, tangent2, error, cornerTolerance, maxSegments):
    if len(points) == 0: return
    if len(points) == 2:
      return [self.fitLine(points, tangent1, tangent2)]
    maxIterations = 3
    isCorner = False
    u = self.chordLengthParameterize(points)
    if u[-1] == 0.0: return []
    bez = self.generateBezier(points, u, tangent1, tangent2, error)
    self.reparameterize(bez, points, u)
    tolerance = math.sqrt(error + 1e-9)
    (maxErrorRatio, splitPoint) = self.computeMaxError(bez, points, u, tolerance, cornerTolerance)
    if abs(maxErrorRatio) <= 1.0: return [bez]
    if ( 0.0 <= maxErrorRatio and maxErrorRatio <= 3.0 ):
      for _ in range(0, maxIterations+1):
        bez = self.generateBezier(points, u, tangent1, tangent2, error)
        (maxErrorRatio, splitPoint) = self.computeMaxError( bez,  points,  u,  tolerance,  cornerTolerance)
        if abs(maxErrorRatio) <= 1.0: return [bez]

    isCorner = maxErrorRatio < 0
    if isCorner:
      if splitPoint == 0:
        if tangent1 == None:
          splitPoint = splitPoint + 1
        else:
          return self._fitCurve( points, Point(0.0,0.0), tangent2, error, cornerTolerance, maxSegments)

      elif splitPoint == len(points) - 1:
        if tangent2 == None:
          splitPoint = splitPoint - 1
        else:
          return self._fitCurve(points, tangent1, Point(0.0,0.0), error, cornerTolerance, maxSegments)

    if 1 < maxSegments:
      segmentsRemaining = maxSegments - 1
      if isCorner:
        if not (0 < splitPoint and splitPoint < len(points) - 1): return []
        recTHat1 = Point(0.0,0.0)
        recTHat2 = Point(0.0,0.0)
      else:
        recTHat2 = self.centerTangent(points, splitPoint)
        recTHat1 = recTHat2 * -1

      lPoints = points[:splitPoint+1]
      rPoints = points[splitPoint:]
      lbeziers = self._fitCurve(lPoints, tangent1, recTHat2, error, cornerTolerance, segmentsRemaining)
      if lbeziers:
        segmentsRemaining = segmentsRemaining - len(lbeziers)
      rbeziers = self._fitCurve(rPoints, recTHat1, tangent2, error, cornerTolerance, segmentsRemaining)
      return lbeziers + rbeziers
    else:
      return []
 def test_align(self):
   q = CubicBezier(
     Point(120,160), Point(35,200), Point(220,260), Point(220,40)
   )
   s = q.aligned()
   self.assertAlmostEqual(s[0].x,0.0)
   self.assertAlmostEqual(s[0].y,0.0)
   self.assertAlmostEqual(s[1].x,-85.14452515537582)
   self.assertAlmostEqual(s[1].y,-39.69143277919774)
   self.assertAlmostEqual(s[2].x,-12.803687993289572)
   self.assertAlmostEqual(s[2].y,140.84056792618557)
   self.assertAlmostEqual(s[3].x,156.2049935181331)
   self.assertAlmostEqual(s[3].y,0.0)
 def test_cf1(self):
     nodes = [
         Point(122, 102),
         Point(35, 200),
         Point(228, 145),
         Point(190, 46)
     ]
     t = CurveFit._leftTangent(nodes)
     self.assertAlmostEqual(t.x, -0.663890062102)
     self.assertAlmostEqual(t.y, 0.747830184896)
     t = CurveFit._rightTangent(nodes)
     self.assertAlmostEqual(t.x, -0.3583470773350791)
     self.assertAlmostEqual(t.y, -0.9335884383203376)
    def test_cubic_cubic(self):
        # q1 = Bezier(10,100, 90,30, 40,140, 220,220)
        # q2 = Bezier(5,150, 180,20, 80,250, 210,190)
        # console.log(q1.intersects(q2))
        q1 = CubicBezier(Point(10, 100), Point(90, 30), Point(40, 140),
                         Point(220, 220))
        q2 = CubicBezier(Point(5, 150), Point(180, 20), Point(80, 250),
                         Point(210, 190))
        i = q1.intersections(q2)
        # self.assertEqual(len(i),3)
        # self.assertAlmostEqual(i[0].point.x,81.7904225873)
        # self.assertAlmostEqual(i[0].point.y,109.899396337)
        # self.assertAlmostEqual(i[1].point.x,133.186831292)
        # self.assertAlmostEqual(i[1].point.y,167.148173322)
        # self.assertAlmostEqual(i[2].point.x,179.869157678)
        # self.assertAlmostEqual(i[2].point.y,199.661989162)
        import matplotlib.pyplot as plt
        fig, ax = plt.subplots()

        path = BezierPath()
        path.closed = False
        path.activeRepresentation = SegmentRepresentation(path, [q1])
        path.plot(ax)
        path.activeRepresentation = SegmentRepresentation(path, [q2])
        path.plot(ax)

        for n in i:
            circle = plt.Circle((n.point.x, n.point.y),
                                2,
                                fill=True,
                                color="red")
            ax.add_artist(circle)
Exemple #16
0
    def plot(self, ax, **kwargs):
        """Plot the path on a Matplot subplot which you supply

    ::

          import matplotlib.pyplot as plt
          fig, ax = plt.subplots()
          path.plot(ax)

    """
        import matplotlib.pyplot as plt
        from matplotlib.lines import Line2D
        from matplotlib.path import Path
        import matplotlib.patches as patches
        path = self.asMatplot()
        if not "lw" in kwargs:
            kwargs["lw"] = 2
        if not "fill" in kwargs:
            kwargs["fill"] = False
        drawNodes = (not ("drawNodes" in kwargs)
                     or kwargs["drawNodes"] != False)
        if "drawNodes" in kwargs:
            kwargs.pop("drawNodes")
        patch = patches.PathPatch(path, **kwargs)
        ax.add_patch(patch)
        left, right = ax.get_xlim()
        top, bottom = ax.get_ylim()
        bounds = self.bounds()
        bounds.addMargin(5)
        if not (left == 0.0 and right == 1.0 and top == 0.0 and bottom == 1.0):
            bounds.extend(Point(left, top))
            bounds.extend(Point(right, bottom))
        ax.set_xlim(bounds.left, bounds.right)
        ax.set_ylim(bounds.bottom, bounds.top)
        if drawNodes:
            nl = self.asNodelist()
            for i in range(0, len(nl)):
                n = nl[i]
                if n.type == "offcurve":
                    circle = plt.Circle((n.x, n.y), 1, fill=False)
                    ax.add_artist(circle)
                    if i + 1 < len(nl) and nl[i + 1].type != "offcurve":
                        l = Line2D([n.x, nl[i + 1].x], [n.y, nl[i + 1].y])
                        ax.add_artist(l)
                    if i - 0 >= 0 and nl[i - 1].type != "offcurve":
                        l = Line2D([n.x, nl[i - 1].x], [n.y, nl[i - 1].y])
                        ax.add_artist(l)
                else:
                    circle = plt.Circle((n.x, n.y), 1)
                    ax.add_artist(circle)
Exemple #17
0
 def test_inside(self):
   p = BezierPath.fromNodelist([
     Node(329,320,"line"),
     Node(564,190,"line"),
     Node(622,332,"offcurve"),
     Node(495,471,"offcurve"),
     Node(329,471,"curve"),
     Node(164,471,"offcurve"),
     Node(34,334,"offcurve"),
     Node(93,190,"curve")
   ])
   self.assertTrue(p.pointIsInside(Point(326,423)))
   self.assertFalse(p.pointIsInside(Point(326,123)))
   self.assertFalse(p.pointIsInside(Point(326,251)))
   self.assertTrue(p.pointIsInside(Point(526,251)))
   self.assertTrue(p.pointIsInside(Point(126,251)))
 def pointAtTime(self, t):
     """Returns the point at time t (0->1) along the curve."""
     x = (1 - t) * (1 - t) * self[0].x + 2 * (
         1 - t) * t * self[1].x + t * t * self[2].x
     y = (1 - t) * (1 - t) * self[0].y + 2 * (
         1 - t) * t * self[1].y + t * t * self[2].y
     return Point(x, y)
Exemple #19
0
 def get_yb_clearance(self, parser, bariye):
     font = parser.font
     paths = get_bezier_paths(font, bariye)
     path = paths[0]
     bounds = path.bounds()
     x_of_tail = get_rise(font.font, bariye)
     ray = Line(
         Point(x_of_tail - 0.1, bounds.bottom - 5),
         Point(x_of_tail + 0.1, bounds.top + 5),
     )
     intersections = []
     for seg in path.asSegments():
         intersections.extend(seg.intersections(ray))
     intersections = list(sorted(intersections, key=lambda i: i.point.y))
     i = intersections[-1]
     return i.point.y
Exemple #20
0
    def get_glyphs(self, text, buf=None):
        """Returns an list of dictionaries representing a shaped string.

        Args:
            text: text to check
            buf: (Optional) already shaped uharfbuzz buffer.

        This is the first step in collision detection; the dictionaries
        returned can be fed to ``draw_overlaps`` and ``has_collisions``."""
        if not buf:
            buf = self.shape_a_text(text)
        glyf = self.font["glyf"]
        cursor = 0
        glyphs = []
        ix = 0
        for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
            position = Point(cursor + pos.position[0], pos.position[1])

            name = glyf.getGlyphName(info.codepoint)
            g = self.get_positioned_glyph(name, position)
            g["advance"] = pos.position[2]
            for p in g["paths"]:
                p.origin = info.cluster
                p.glyphIndex = ix
            glyphs.append(g)
            ix = ix + 1
            cursor = cursor + pos.position[2]
        return glyphs
Exemple #21
0
 def centerTangent(self, data, center):
     if data[center + 1] == data[center - 1]:
         ret = data[center] - data[center - 1]
         ret.rotate(Point(0, 0), math.pi / 2.0)
         return ret.toUnitVector()
     else:
         ret = data[center - 1] - data[center + 1]
         return ret.toUnitVector()
Exemple #22
0
 def factor(P, r_or_k):
     n = len(P) - 1
     upsilon = min(r_or_k, n)
     theta = max(0, r_or_k - n)
     summand = Point(0, 0)
     for i in range(theta, upsilon + 1):
         summand += P[i] * C(i, n) * C(r_or_k - i, n) / C(r_or_k, 2 * n)
     return summand
Exemple #23
0
def xheight_intersections(ttFont, glyph):
    glyphset = ttFont.getGlyphSet()
    if glyph not in glyphset:
        return []

    paths = BezierPath.fromFonttoolsGlyph(ttFont, glyph)
    if len(paths) != 1:
        return []
    path = paths[0]

    xheight = ttFont["OS/2"].sxHeight

    bounds = path.bounds()
    bounds.addMargin(10)
    ray = Line(Point(bounds.left, xheight), Point(bounds.right, xheight))
    intersections = []
    for seg in path.asSegments():
        intersections.extend(seg.intersections(ray))
    return sorted(intersections, key=lambda i: i.point.x)
Exemple #24
0
    def test_scale(self):
        p = Point(4, 5)
        m = AffineTransformation.scaling(2)
        p.transform(m)
        self.assertEqual(p.x, 8)
        self.assertEqual(p.y, 10)

        p = Point(4, 5)
        m = AffineTransformation.scaling(1.5, -2)
        p.transform(m)
        self.assertEqual(p.x, 6)
        self.assertEqual(p.y, -10)
Exemple #25
0
 def appendSegment(self, seg):
     seg = [Point(n[0], n[1]) for n in seg]
     if len(seg) == 2:
         self.segments.append(Line(*seg))
     elif len(seg) == 3:
         self.segments.append(QuadraticBezier(*seg))
     elif len(seg) == 4:
         self.segments.append(CubicBezier(*seg))
     else:
         raise ValueError("Unknown segment type")
Exemple #26
0
 def bestcut(self, args=None):
     """ Find the best line that cuts this octabox and its segments so that the
         resulting two bounding octaboxes (of the two sets of segments) is minimised."""
     currbest = OctaScore(self, None, self.area)
     for x, d in enumerate(((1, 0), (0, 1), (-1, 1), (1, 1))):
         splitline = Line(Point(d[0] * self.xi, d[1] * self.yi),
                          Point(d[0] * self.xa, d[1] * self.ya))
         for sl in findshifts(self.segs, splitline):
             r, l = splitWith(self.segs, sl)
             rightbox = Octabox(r)
             leftbox = Octabox(l)
             score = rightbox.area + leftbox.area
             if args is not None and args.detail & 8:
                 print("    {}:L[{}, {}], R[{}, {}]".format(
                     "xysd"[x], leftbox.area,
                     sum(s.area for s in leftbox.segs), rightbox.area,
                     sum(s.area for s in rightbox.segs)))
             if score < currbest.score:
                 currbest = OctaScore(leftbox, rightbox, score)
     return currbest
Exemple #27
0
def thickness_at_x(path, x):
    """Find the path thickness at a given X coordinate

    This measure the thickness of the lowest horizontal stem at the given
    coordinate. If there is no stem at this X coordinate, ``None`` is
    returned.

    Args:
        path: A ``beziers.path.BezierPath`` object
        x: X coordinate to search

    Returns:
        The thickness of the path at this point, in font units.
    """
    bounds = path.bounds()
    bounds.addMargin(10)
    ray = Line(Point(x - 0.1, bounds.bottom), Point(x + 0.1, bounds.top))
    intersections = []
    for seg in path.asSegments():
        intersections.extend(seg.intersections(ray))
    if len(intersections) < 2:
        return None
    intersections = list(sorted(intersections, key=lambda i: i.point.y))
    i1, i2 = intersections[0:2]
    inorm1 = i1.seg1.normalAtTime(i1.t1)
    ray1 = Line(i1.point + (inorm1 * 1000), i1.point + (inorm1 * -1000))
    iii = i2.seg1.intersections(ray1)
    if iii:
        ll1 = i1.point.distanceFrom(iii[0].point)
    else:
        # Simple, vertical version
        return abs(i1.point.y - i2.point.y)

    inorm2 = i2.seg1.normalAtTime(i2.t1)
    ray2 = Line(i2.point + (inorm2 * 1000), i2.point + (inorm2 * -1000))
    iii = i1.seg1.intersections(ray2)
    if iii:
        ll2 = i2.point.distanceFrom(iii[0].point)
        return (ll1 + ll2) * 0.5
    else:
        return ll1
Exemple #28
0
    def test_overlap(self):
        nodes = [
            Node(698.0, 413.0, "offcurve"),
            Node(401.0, 179.0, "offcurve"),
            Node(401.0, 274.0, "curve"),
            Node(401.0, 368.0, "offcurve"),
            Node(315.0, 445.0, "offcurve"),
            Node(210.0, 445.0, "curve"),
            Node(104.0, 445.0, "offcurve"),
            Node(18.0, 368.0, "offcurve"),
            Node(18.0, 274.0, "curve"),
            Node(18.0, 179.0, "offcurve"),
            Node(439.0, 400.0, "offcurve"),
            Node(533.0, 405.0, "curve")
        ]
        p = BezierPath.fromNodelist(nodes)
        p.closed = True
        i = p.getSelfIntersections()
        self.assertEqual(len(i), 1)
        self.assertEqual(i[0].point, Point(377.714262786, 355.53493137))

        # import matplotlib.pyplot as plt
        # fig, ax = plt.subplots()
        # p.plot(ax)
        # for n in i:
        #   circle = plt.Circle((n.point.x, n.point.y), 2, fill=True, color="red")
        #   ax.add_artist(circle)
        # plt.show()

        p = BezierPath.fromNodelist([
            Node(310.0, 389.0, "line"),
            Node(453.0, 222.0, "line"),
            Node(289.0, 251.0, "line"),
            Node(447.0, 367.0, "line"),
            Node(578.0, 222.0, "line"),
            Node(210.0, -8.0, "line"),
        ])

        i = p.getSelfIntersections()
        self.assertEqual(len(i), 1)
        self.assertEqual(i[0].point, Point(374.448829525, 313.734583702))
  def test_loop(self):
    q = CubicBezier(
      Point(171,272), Point(388,249), Point(167,444), Point(388,176)
    )
    self.assertTrue(not q.hasLoop)

    q = CubicBezier(
      Point(171,272), Point(595,249), Point(167,444), Point(388,176)
    )
    roots = q.hasLoop
    p1 = q.pointAtTime(roots[0])
    p2 = q.pointAtTime(roots[1])
    self.assertTrue(q.hasLoop)
    self.assertEqual(p1,p2)
Exemple #30
0
    def offset(self, vector, rotateVector=True):
        """Returns a new BezierPath which approximates offsetting the
    current Bezier path by the given vector. Note that the vector
    will be rotated around the normal of the curve so that the
    offsetting always happens on the same 'side' of the curve:

..  figure:: offset1.png
    :scale: 75 %
    :alt: offset1

    If you don't want that and you want 'straight' offsetting instead
    (which may intersect with the original curve), pass
    `rotateVector=False`:

..  figure:: offset2.png
    :scale: 75 %
    :alt: offset1

    """
        # Method 1 - curve fit
        newsegs = []
        points = []

        def finishPoints(newsegs, points):
            if len(points) > 0:
                bp = BezierPath.fromPoints(points,
                                           error=0.1,
                                           cornerTolerance=1)
                newsegs.extend(bp.asSegments())
            while len(points) > 0:
                points.pop()

        for seg in self.asSegments():
            if isinstance(seg, Line):
                finishPoints(newsegs, points)
                newsegs.append(seg.translated(vector))
            else:
                t = 0.0
                while t < 1.0:
                    if rotateVector:
                        points.append(
                            seg.pointAtTime(t) +
                            vector.rotated(Point(0, 0),
                                           seg.normalAtTime(t).angle))
                    else:
                        points.append(seg.pointAtTime(t) + vector)
                    step = max(abs(seg.curvatureAtTime(t)), 0.1)
                    t = t + min(seg.length / step, 0.1)
        finishPoints(newsegs, points)
        newpath = BezierPath()
        newpath.activeRepresentation = SegmentRepresentation(newpath, newsegs)
        return newpath
Exemple #31
0
 def fromRepr(klass,text):
   import re
   p = re.compile("^B<(.*?)-(.*?)-(.*?)-(.*?)>$")
   m = p.match(text)
   points = [ Point.fromRepr(m.group(t)) for t in range(1,5) ]
   return klass(*points)
Exemple #32
0
 def tangentAtTime(self,t):
   """Returns the tangent at time t (0->1) along the line."""
   return Point.fromAngle(math.atan2(self.end.y-self.start.y,self.end.x-self.start.x))