Example #1
0
 def getExtremes(self, point=Tuple[int, ...]) -> list:
     # gets extremes of a cubic bezier curve
     pt1, pt2, pt3, pt4 = point
     (ax, ay), (bx, by), (cx, cy), _ = calcCubicParameters(pt1, pt2, pt3, pt4)
     ax3 = ax * 3.0
     ay3 = ay * 3.0
     bx2 = bx * 2.0
     by2 = by * 2.0
     collector: list = []
     if self.horizontal:
         collector += [t for t in solveQuadratic(ax3, bx2, cx) if 0 <= t < 1]
     if self.vertical:
         collector += [t for t in solveQuadratic(ay3, by2, cy) if 0 <= t < 1]
     return sorted(collector)
Example #2
0
def getExtremaForCubic(pt1, pt2, pt3, pt4, h=True, v=False):
	(ax, ay), (bx, by), c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
	ax *= 3.0
	ay *= 3.0
	bx *= 2.0
	by *= 2.0
	h_roots = []
	v_roots = []
	if h:
		h_roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 < t < 1]
	if v:
		v_roots  = [t for t in solveQuadratic(ax, bx, c[0]) if 0 < t < 1]
	roots = h_roots + v_roots
	return [p[3] for p in splitCubicAtT(pt1, pt2, pt3, pt4, *roots)[:-1]]
Example #3
0
def getExtremaForCubic(pt1, pt2, pt3, pt4, h=True, v=False):
    (ax, ay), (bx, by), c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
    ax *= 3.0
    ay *= 3.0
    bx *= 2.0
    by *= 2.0
    h_roots = []
    v_roots = []
    if h:
        h_roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 < t < 1]
    if v:
        v_roots = [t for t in solveQuadratic(ax, bx, c[0]) if 0 < t < 1]
    roots = h_roots + v_roots
    return [p[3] for p in splitCubicAtT(pt1, pt2, pt3, pt4, *roots)[:-1]]
Example #4
0
def _tValueForPointOnQuadCurve(point, pts, isHorizontal=0):
    quadSegments = decomposeQuadraticSegment(pts[1:])
    previousOnCurve = pts[0]
    solutionsDict = dict()
    for index, (pt1, pt2) in enumerate(quadSegments):
        a, b, c = bezierTools.calcQuadraticParameters(previousOnCurve, pt1,
                                                      pt2)
        subSolutions = bezierTools.solveQuadratic(
            a[isHorizontal], b[isHorizontal],
            c[isHorizontal] - point[isHorizontal])
        subSolutions = [t for t in subSolutions if 0 <= t < 1]
        for t in subSolutions:
            solutionsDict[(t, index)] = _getQuadPoint(t, previousOnCurve, pt1,
                                                      pt2)
        previousOnCurve = pt2
    solutions = solutionsDict.keys()
    if not solutions and not isHorizontal:
        return _tValueForPointOnQuadCurve(point, pts, isHorizontal=1)
    if len(solutions) > 1:
        intersectionLenghts = {}
        for t in solutions:
            tp = solutionsDict[t]
            dist = _distance(tp, point)
            intersectionLenghts[dist] = t
        minDist = min(intersectionLenghts.keys())
        solutions = [intersectionLenghts[minDist]]
    return solutions
Example #5
0
def getExtremaForCubic(pt1, pt2, pt3, pt4, h=True, v=False):
	(ax, ay), (bx, by), c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
	ax *= 3.0
	ay *= 3.0
	bx *= 2.0
	by *= 2.0
	points = []
	vectors = []
	if h:
		roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 < t < 1]
		points, vectors = get_extrema_points_vectors(roots, pt1, pt2, pt3, pt4)
	if v:
		roots = [t for t in solveQuadratic(ax, bx, c[0]) if 0 < t < 1]
		v_points, v_vectors = get_extrema_points_vectors(roots, pt1, pt2, pt3, pt4)
		points += v_points
		vectors += v_vectors
	return points, vectors
def getExtremaForCubic(pt1, pt2, pt3, pt4, h=True, v=False):
    (ax, ay), (bx, by), c, d = calcCubicParameters(pt1, pt2, pt3, pt4)
    ax *= 3.0
    ay *= 3.0
    bx *= 2.0
    by *= 2.0
    points = []
    vectors = []
    if h:
        roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 < t < 1]
        points, vectors = get_extrema_points_vectors(roots, pt1, pt2, pt3, pt4)
    if v:
        roots = [t for t in solveQuadratic(ax, bx, c[0]) if 0 < t < 1]
        v_points, v_vectors = get_extrema_points_vectors(
            roots, pt1, pt2, pt3, pt4)
        points += v_points
        vectors += v_vectors
    return points, vectors
Example #7
0
def getExtremaForCubic(
    pt0: Point,
    pt1: Point,
    pt2: Point,
    pt3: Point,
    h: bool = True,
    v: bool = False,
    include_start_end: bool = False,
) -> List[float]:
    """
    Return a list of t values at which the cubic curve defined by pt0, pt1,
    pt2, pt3 has extrema.

    :param h: Calculate extrema for horizontal derivative == 0 (= what type
       designers call vertical extrema!).
    :type h: bool
    :param v: Calculate extrema for vertical derivative == 0 (= what type
       designers call horizontal extrema!).
    :type v: bool
    :param include_start_end: Also calculate extrema that lie at the start or
       end point of the curve.
    :type include_start_end: bool
    """
    (ax, ay), (bx, by), c, _d = calcCubicParameters(pt0, pt1, pt2, pt3)
    ax *= 3.0
    ay *= 3.0
    bx *= 2.0
    by *= 2.0
    roots = []
    if include_start_end:
        if h:
            roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 <= t <= 1]
        if v:
            roots += [t for t in solveQuadratic(ax, bx, c[0]) if 0 <= t <= 1]
    else:
        if h:
            roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 < t < 1]
        if v:
            roots += [t for t in solveQuadratic(ax, bx, c[0]) if 0 < t < 1]
    return roots
Example #8
0
def split_at_extrema(pt1, pt2, pt3, pt4, transform=Transform()):
    """
    Add extrema to a cubic curve, after applying a transformation.
    Example ::

        >>> # A segment in which no extrema will be added.
        >>> split_at_extrema((297, 52), (406, 52), (496, 142), (496, 251))
        [((297, 52), (406, 52), (496, 142), (496, 251))]
        >>> from fontTools.misc.transform import Transform
        >>> split_at_extrema((297, 52), (406, 52), (496, 142), (496, 251), Transform().rotate(-27))
        [((297.0, 52.0), (84.48072108963274, -212.56513799170233), (15.572491694678519, -361.3686192413668), (15.572491694678547, -445.87035970621713)), ((15.572491694678547, -445.8703597062171), (15.572491694678554, -506.84825401175414), (51.4551516055374, -534.3422304091257), (95.14950889754756, -547.6893014808263))]
    """
    # Transform the points for extrema calculation;
    # transform is expected to rotate the points by - nib angle.
    t2, t3, t4 = transform.transformPoints([pt2, pt3, pt4])
    # When pt1 is the current point of the path,  it is already transformed, so
    # we keep it like it is.
    t1 = pt1

    (ax, ay), (bx, by), c, d = calcCubicParameters(t1, t2, t3, t4)
    ax *= 3.0
    ay *= 3.0
    bx *= 2.0
    by *= 2.0

    # vertical
    roots = [t for t in solveQuadratic(ay, by, c[1]) if 0 < t < 1]

    # horizontal
    roots += [t for t in solveQuadratic(ax, bx, c[0]) if 0 < t < 1]

    # Use only unique roots and sort them
    # They should be unique before, or can a root be duplicated (in h and v?)
    roots = sorted(set(roots))

    if not roots:
        return [(t1, t2, t3, t4)]

    return splitCubicAtT(t1, t2, t3, t4, *roots)
	def _qCurveToOne_unfinished(self, bcp, point):
		# XXX need to finish this, for now doing it through a cubic
		# (BasePen implements _qCurveTo in terms of a cubic) will
		# have to do.
		x, y = self.testPoint
		x1, y1 = self._getCurrentPoint()
		x2, y2 = bcp
		x3, y3 = point
		c = y1
		b = (y2 - c) * 2.0
		a = y3 - c - b
		solutions = sorted(solveQuadratic(a, b, c - y))
		solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
		if not solutions:
			return
Example #10
0
	def _qCurveToOne_unfinished(self, bcp, point):
		# XXX need to finish this, for now doing it through a cubic
		# (BasePen implements _qCurveTo in terms of a cubic) will
		# have to do.
		x, y = self.testPoint
		x1, y1 = self._getCurrentPoint()
		x2, y2 = bcp
		x3, y3 = point
		c = y1
		b = (y2 - c) * 2.0
		a = y3 - c - b
		solutions = sorted(solveQuadratic(a, b, c - y))
		solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
		if not solutions:
			return
Example #11
0
def qcurveIntersections(x1, y1, x2, y2, *pts):
    """
    Computes intersection between a cubic spline and a line segment.
    Adapted from: https://www.particleincell.com/2013/cubic-line-intersection/

    Takes four defcon points describing curve and four scalars describing line
    parameters.
    """

    sol = []

    # PACK for fontTools
    points = []
    for pt in pts:
        points.append((pt.x, pt.y))

    p1 = (pts[0].x, pts[0].y)
    for p2, p3 in decomposeQuadraticSegment(points[1:]):
        bx, by = (y1 - y2), (x2 - x1)
        m = x1 * y2 - x2 * y1
        a, b, c = bezierTools.calcQuadraticParameters(p1, p2, p3)

        # prepare for next turn
        p1 = p3

        pc0 = bx * a[0] - by * a[1]
        pc1 = (bx * b[0] + by * b[1]) / pc0
        pc2 = (bx * c[0] + by * c[1] + m) / pc0
        r = bezierTools.solveQuadratic(pc0, pc1, pc2)

        for t in r:
            if t < 0 or t > 1:
                continue
            s0 = a[0] * (1 - t) ** 2 + b[0] * 2 * t * (1 - t) + c[0] * t ** 2
            s1 = a[1] * (1 - t) ** 2 + b[1] * 2 * t * (1 - t) + c[1] * t ** 2
            if (x2 - x1) != 0:
                s = (s0 - x1) / (x2 - x1)
            else:
                s = (s1 - y1) / (y2 - y1)
            if s < 0 or s > 1:
                continue
            sol.append((s0, s1, t))
    return sol
Example #12
0
def _tValueForPointOnQuadCurve(point, pts, isHorizontal=0):
    quadSegments = decomposeQuadraticSegment(pts[1:])
    previousOnCurve = pts[0]
    solutionsDict = dict()
    for index, (pt1, pt2) in enumerate(quadSegments):
        a, b, c = bezierTools.calcQuadraticParameters(previousOnCurve, pt1, pt2)
        subSolutions = bezierTools.solveQuadratic(a[isHorizontal], b[isHorizontal], c[isHorizontal] - point[isHorizontal])
        subSolutions = [t for t in subSolutions if 0 <= t < 1]
        for t in subSolutions:
            solutionsDict[(t, index)] = _getQuadPoint(t, previousOnCurve, pt1, pt2)
        previousOnCurve = pt2
    solutions = list(solutionsDict.keys())
    if not solutions and not isHorizontal:
        return _tValueForPointOnQuadCurve(point, pts, isHorizontal=1)
    if len(solutions) > 1:
        intersectionLenghts = {}
        for t in solutions:
            tp = solutionsDict[t]
            dist = _distance(tp, point)
            intersectionLenghts[dist] = t
        minDist = min(intersectionLenghts.keys())
        solutions = [intersectionLenghts[minDist]]
    return solutions