def _pointWithinThreshold(x, y, curve, eps): """ See whether *(x, y)* is within *eps* of *curve*. """ path = QPainterPath() path.addEllipse(x - eps, y - eps, 2 * eps, 2 * eps) curvePath = QPainterPath() if curve[-1].segmentType == "curve": p1, p2, p3, p4 = curve curvePath.moveTo(p1.x, p1.y) curvePath.cubicTo(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y) curvePath.cubicTo(p3.x, p3.y, p2.x, p2.y, p1.x, p1.y) else: first = curve[0] curvePath.moveTo(first.x, first.y) # PACK for fontTools pts = [] for pt in curve: pts.append((pt.x, pt.y)) # draw for pt1, pt2 in decomposeQuadraticSegment(pts[1:]): curvePath.quadTo(*pt1 + pt2) for pt1, pt2 in decomposeQuadraticSegment(list(reversed(pts[:-1]))): curvePath.quadTo(*pt1 + pt2) return path.intersects(curvePath)
def _pointWithinThreshold(x, y, curve, eps): """ See whether *(x, y)* is within *eps* of *curve*. """ path = QPainterPath() path.addEllipse(x - eps, y - eps, 2 * eps, 2 * eps) curvePath = QPainterPath() if curve[-1].segmentType == "curve": p1, p2, p3, p4 = curve curvePath.moveTo(p1.x, p1.y) curvePath.cubicTo(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y) curvePath.cubicTo(p3.x, p3.y, p2.x, p2.y, p1.x, p1.y) else: first = curve[0] curvePath.moveTo(first.x, first.y) # PACK for fontTools pts = [] for pt in curve: pts.append((pt.x, pt.y)) # draw for pt1, pt2 in decomposeQuadraticSegment(pts[1:]): curvePath.quadTo(*pt1+pt2) for pt1, pt2 in decomposeQuadraticSegment(list(reversed(pts[:-1]))): curvePath.quadTo(*pt1+pt2) return path.intersects(curvePath)
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
def qCurveTo(self, *points): decomp = decomposeQuadraticSegment(points) for pair in decomp: if pair[1] is None: pair = (pair[0],decomp[0][0]) shifted = self.shiftList(pair) print(self.indent+"path.quadTo(%gf,%gf,%gf,%gf);" % tuple(shifted[0]+shifted[1]))
def qCurveTo(self, *points): if points and points[-1] is None: print("// this shouldn't happen but it does") print(self.indent + "path.moveTo(%gf,%gf);" % self.shift(points[0])) points = points[:-1] decomp = decomposeQuadraticSegment(points) for pair in decomp: shifted = self.shiftList(pair) if shifted[0] and shifted[1]: print(self.indent + "path.quadTo(%gf,%gf,%gf,%gf);" % tuple(shifted[0] + shifted[1]))
def __init__(self, points=None, previousOnCurve=None, willBeReversed=False): if points is None: points = [] self.points = points self.previousOnCurve = previousOnCurve self.scaledPreviousOnCurve = _scaleSinglePoint(previousOnCurve, scale=clipperScale) self.used = False self.flat = [] # if the bcps are equal to the oncurves convert the segment to a line segment. # otherwise this causes an error when flattening. if self.segmentType == "curve": if previousOnCurve == points[0].coordinates and points[1].coordinates == points[-1].coordinates: oncurve = points[-1] oncurve.segmentType = "line" self.points = points = [oncurve] # its a reversed segment the flat points will be set later on in the InputContour if willBeReversed: return pointsToFlatten = [] if self.segmentType == "qcurve": assert len(points) >= 0 flat = [] currentOnCurve = previousOnCurve pointCoordinates = [point.coordinates for point in points] for pt1, pt2 in decomposeQuadraticSegment(pointCoordinates[1:]): pt0x, pt0y = currentOnCurve pt1x, pt1y = pt1 pt2x, pt2y = pt2 mid1x = pt0x + 0.66666666666666667 * (pt1x - pt0x) mid1y = pt0y + 0.66666666666666667 * (pt1y - pt0y) mid2x = pt2x + 0.66666666666666667 * (pt1x - pt2x) mid2y = pt2y + 0.66666666666666667 * (pt1y - pt2y) convertedQuadPointToFlatten = [currentOnCurve, (mid1x, mid1y), (mid2x, mid2y), pt2] flat.extend(_flattenSegment(convertedQuadPointToFlatten)) currentOnCurve = pt2 self.flat = flat elif self.segmentType == "curve": pointsToFlatten = [previousOnCurve] + [point.coordinates for point in points] else: assert len(points) == 1 self.flat = [point.coordinates for point in points] if pointsToFlatten: self.flat = _flattenSegment(pointsToFlatten) self.flat = _scalePoints(self.flat, scale=clipperScale) self.used = False
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
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
def qCurveTo(self, *points): decomp = decomposeQuadraticSegment(points) for pair in decomp: self.update(pair[1])
def qCurveTo(self, *points): _qCurveToOne = self._qCurveToOne for pt1, pt2 in decomposeQuadraticSegment(points): _qCurveToOne(pt1, pt2) self.__currentPoint = pt2