Exemplo n.º 1
0
    def split_at_pt_fast(self, pt: Point) -> Tuple[Point, Point, Point, Point]:
        if DEBUG_SPLIT:
            print("SuperCubic.split_at_pt_fast", pt, "->")
        index = 0
        x, y = pt
        a, b, c, d = self.cubics[0].params
        solutions_h = solveCubic(a[1], b[1], c[1], d[1] - y)
        solutions_v = solveCubic(a[0], b[0], c[0], d[0] - x)
        solutions_h = [t for t in solutions_h if 0 <= t < 1]
        solutions_v = [t for t in solutions_v if 0 <= t < 1]
        if DEBUG_SPLIT:
            print(solutions_h, solutions_v)
        if len(solutions_h) == 1 and solutions_v:
            # Take the average of both values
            t = (solutions_v[0] + solutions_h[0]) * 0.5
        else:
            print(
                "    Different number of solutions for h and v:",
                solutions_h,
                solutions_v,
            )
            index_t = self.t_for_point(pt)
            if index_t is None:
                raise ValueError

            index, t = index_t
            print("        Choosing via thorough method:", t)
        self._split_index = index
        return self.cubics[index].split_at_t(t)
Exemplo n.º 2
0
def curveIntersections(p1, p2, p3, p4, x1, y1, x2, y2):
    """
    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.
    """

    bx, by = x1 - x2, y2 - y1
    m = x1 * (y1 - y2) + y1 * (x2 - x1)
    a, b, c, d = bezierTools.calcCubicParameters(
        (p1.x, p1.y), (p2.x, p2.y), (p3.x, p3.y), (p4.x, p4.y))

    pc0 = by * a[0] + bx * a[1]
    pc1 = by * b[0] + bx * b[1]
    pc2 = by * c[0] + bx * c[1]
    pc3 = by * d[0] + bx * d[1] + m
    r = bezierTools.solveCubic(pc0, pc1, pc2, pc3)

    sol = []
    for t in r:
        s0 = a[0] * t ** 3 + b[0] * t ** 2 + c[0] * t + d[0]
        s1 = a[1] * t ** 3 + b[1] * t ** 2 + c[1] * t + d[1]
        if (x2 - x1) != 0:
            s = (s0 - x1) / (x2 - x1)
        else:
            s = (s1 - y1) / (y2 - y1)
        if not (t < 0 or t > 1 or s < 0 or s > 1):
            sol.append((s0, s1, t))
    return sol
Exemplo n.º 3
0
	def _curveToOne(self, bcp1, bcp2, point):
		x, y = self.testPoint
		x1, y1 = self._getCurrentPoint()
		x2, y2 = bcp1
		x3, y3 = bcp2
		x4, y4 = point

		if x1 < x and x2 < x and x3 < x and x4 < x:
			return
		if y1 < y and y2 < y and y3 < y and y4 < y:
			return
		if y1 >= y and y2 >= y and y3 >= y and y4 >= y:
			return

		dy = y1
		cy = (y2 - dy) * 3.0
		by = (y3 - y2) * 3.0 - cy
		ay = y4 - dy - cy - by
		solutions = sorted(solveCubic(ay, by, cy, dy - y))
		solutions = [t for t in solutions if -0. <= t <= 1.]
		if not solutions:
			return

		dx = x1
		cx = (x2 - dx) * 3.0
		bx = (x3 - x2) * 3.0 - cx
		ax = x4 - dx - cx - bx

		above = y1 >= y
		lastT = None
		for t in solutions:
			if t == lastT:
				continue
			lastT = t
			t2 = t * t
			t3 = t2 * t

			direction = 3*ay*t2 + 2*by*t + cy
			incomingGoingUp = outgoingGoingUp = direction > 0.0
			if direction == 0.0:
				direction = 6*ay*t + 2*by
				outgoingGoingUp = direction > 0.0
				incomingGoingUp = not outgoingGoingUp
				if direction == 0.0:
					direction = ay
					incomingGoingUp = outgoingGoingUp = direction > 0.0

			xt = ax*t3 + bx*t2 + cx*t + dx
			if xt < x:
				continue

			if t in (0.0, -0.0):
				if not outgoingGoingUp:
					self._addIntersection(outgoingGoingUp)
			elif t == 1.0:
				if incomingGoingUp:
					self._addIntersection(incomingGoingUp)
			else:
				if incomingGoingUp == outgoingGoingUp:
					self._addIntersection(outgoingGoingUp)
Exemplo n.º 4
0
def _tValueForPointOnCubicCurve(point, cubicCurve, isHorizontal=0):
    """
    Finds a t value on a curve from a point.
    The points must be originaly be a point on the curve.
    This will only back trace the t value, needed to split the curve in parts
    """
    pt1, pt2, pt3, pt4 = cubicCurve
    a, b, c, d = bezierTools.calcCubicParameters(pt1, pt2, pt3, pt4)
    solutions = bezierTools.solveCubic(a[isHorizontal], b[isHorizontal],
                                       c[isHorizontal],
                                       d[isHorizontal] - point[isHorizontal])
    solutions = [t for t in solutions if 0 <= t < 1]
    if not solutions and not isHorizontal:
        # can happen that a horizontal line doens intersect, try the vertical
        return _tValueForPointOnCubicCurve(point, (pt1, pt2, pt3, pt4),
                                           isHorizontal=1)
    if len(solutions) > 1:
        intersectionLenghts = {}
        for t in solutions:
            tp = _getCubicPoint(t, pt1, pt2, pt3, pt4)
            dist = _distance(tp, point)
            intersectionLenghts[dist] = t
        minDist = min(intersectionLenghts.keys())
        solutions = [intersectionLenghts[minDist]]
    return solutions
Exemplo n.º 5
0
	def _curveToOne(self, bcp1, bcp2, point):
		x, y = self.testPoint
		x1, y1 = self._getCurrentPoint()
		x2, y2 = bcp1
		x3, y3 = bcp2
		x4, y4 = point

		if x1 < x and x2 < x and x3 < x and x4 < x:
			return
		if y1 < y and y2 < y and y3 < y and y4 < y:
			return
		if y1 >= y and y2 >= y and y3 >= y and y4 >= y:
			return

		dy = y1
		cy = (y2 - dy) * 3.0
		by = (y3 - y2) * 3.0 - cy
		ay = y4 - dy - cy - by
		solutions = sorted(solveCubic(ay, by, cy, dy - y))
		solutions = [t for t in solutions if -0. <= t <= 1.]
		if not solutions:
			return

		dx = x1
		cx = (x2 - dx) * 3.0
		bx = (x3 - x2) * 3.0 - cx
		ax = x4 - dx - cx - bx

		above = y1 >= y
		lastT = None
		for t in solutions:
			if t == lastT:
				continue
			lastT = t
			t2 = t * t
			t3 = t2 * t

			direction = 3*ay*t2 + 2*by*t + cy
			incomingGoingUp = outgoingGoingUp = direction > 0.0
			if direction == 0.0:
				direction = 6*ay*t + 2*by
				outgoingGoingUp = direction > 0.0
				incomingGoingUp = not outgoingGoingUp
				if direction == 0.0:
					direction = ay
					incomingGoingUp = outgoingGoingUp = direction > 0.0

			xt = ax*t3 + bx*t2 + cx*t + dx
			if xt < x:
				continue

			if t in (0.0, -0.0):
				if not outgoingGoingUp:
					self._addIntersection(outgoingGoingUp)
			elif t == 1.0:
				if incomingGoingUp:
					self._addIntersection(incomingGoingUp)
			else:
				if incomingGoingUp == outgoingGoingUp:
					self._addIntersection(outgoingGoingUp)
Exemplo n.º 6
0
def test_solveCubic():
    assert solveCubic(1, 1, -6, 0) == [-3.0, -0.0, 2.0]
    assert solveCubic(-10.0, -9.0, 48.0, -29.0) == [-2.9, 1.0, 1.0]
    assert solveCubic(-9.875, -9.0, 47.625, -28.75) == [-2.911392, 1.0, 1.0]
    assert solveCubic(1.0, -4.5, 6.75, -3.375) == [1.5, 1.5, 1.5]
    assert solveCubic(-12.0, 18.0, -9.0, 1.50023651123) == [0.5, 0.5, 0.5]
    assert solveCubic(9.0, 0.0, 0.0, -7.62939453125e-05) == [-0.0, -0.0, -0.0]
Exemplo n.º 7
0
def test_solveCubic():
    assert solveCubic(1, 1, -6, 0) == [-3.0, -0.0, 2.0]
    assert solveCubic(-10.0, -9.0, 48.0, -29.0) == [-2.9, 1.0, 1.0]
    assert solveCubic(-9.875, -9.0, 47.625, -28.75) == [-2.911392, 1.0, 1.0]
    assert solveCubic(1.0, -4.5, 6.75, -3.375) == [1.5, 1.5, 1.5]
    assert solveCubic(-12.0, 18.0, -9.0, 1.50023651123) == [0.5, 0.5, 0.5]
    assert solveCubic(9.0, 0.0, 0.0, -7.62939453125e-05) == [-0.0, -0.0, -0.0]
def distanceToQuadratic(m, quad):
	p0, p1, p2 = quad
	# From http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
	A = p1 - p0
	B = p2 - p1 - A
	# coeffs of third degree polynomial
	a = B.squaredLength()
	b = 3.0 * (A | B)
	mp = p0 - m
	c = 2.0 * A.squaredLength() + (mp | B)
	d = (mp | A)
	pts = [splitQuadratic(t, quad)[0][2] for t in rbt.solveCubic(a,b,c,d) if (t>=eps) and (t<=1.0-eps)]
	dists = [(m-p).length() for p in (pts+[p0,p2])]
	return min(dists)
def distanceToQuadratic(m, quad):
    p0, p1, p2 = quad
    # From http://blog.gludion.com/2009/08/distance-to-quadratic-bezier-curve.html
    A = p1 - p0
    B = p2 - p1 - A
    # coeffs of third degree polynomial
    a = B.squaredLength()
    b = 3.0 * (A | B)
    mp = p0 - m
    c = 2.0 * A.squaredLength() + (mp | B)
    d = (mp | A)
    pts = [
        splitQuadratic(t, quad)[0][2] for t in rbt.solveCubic(a, b, c, d)
        if (t >= eps) and (t <= 1.0 - eps)
    ]
    dists = [(m - p).length() for p in (pts + [p0, p2])]
    return min(dists)
Exemplo n.º 10
0
def _tValueForPointOnCubicCurve(point, cubicCurve, isHorizontal=0):
    """
    Finds a t value on a curve from a point.
    The points must be originaly be a point on the curve.
    This will only back trace the t value, needed to split the curve in parts
    """
    pt1, pt2, pt3, pt4 = cubicCurve
    a, b, c, d = bezierTools.calcCubicParameters(pt1, pt2, pt3, pt4)
    solutions = bezierTools.solveCubic(a[isHorizontal], b[isHorizontal], c[isHorizontal],
        d[isHorizontal] - point[isHorizontal])
    solutions = [t for t in solutions if 0 <= t < 1]
    if not solutions and not isHorizontal:
        # can happen that a horizontal line doens intersect, try the vertical
        return _tValueForPointOnCubicCurve(point, (pt1, pt2, pt3, pt4), isHorizontal=1)
    if len(solutions) > 1:
        intersectionLenghts = {}
        for t in solutions:
            tp = _getCubicPoint(t, pt1, pt2, pt3, pt4)
            dist = _distance(tp, point)
            intersectionLenghts[dist] = t
        minDist = min(intersectionLenghts.keys())
        solutions = [intersectionLenghts[minDist]]
    return solutions
Exemplo n.º 11
0
	def _curveToOne(self, bcp1, bcp2, point):
		x, y = self.testPoint
		x1, y1 = self._getCurrentPoint()
		x2, y2 = bcp1
		x3, y3 = bcp2
		x4, y4 = point

		if x1 < x and x2 < x and x3 < x and x4 < x:
			return
		if y1 < y and y2 < y and y3 < y and y4 < y:
			return
		if y1 >= y and y2 >= y and y3 >= y and y4 >= y:
			return

		dy = y1
		cy = (y2 - dy) * 3.0
		by = (y3 - y2) * 3.0 - cy
		ay = y4 - dy - cy - by
		solutions = sorted(solveCubic(ay, by, cy, dy - y))
		solutions = [t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON]
		if not solutions:
			return

		dx = x1
		cx = (x2 - dx) * 3.0
		bx = (x3 - x2) * 3.0 - cx
		ax = x4 - dx - cx - bx

		above = y1 >= y
		lastT = None
		for t in solutions:
			if t == lastT:
				continue
			lastT = t
			t2 = t * t
			t3 = t2 * t

			direction = 3*ay*t2 + 2*by*t + cy
			if direction == 0.0:
				direction = 6*ay*t + 2*by
				if direction == 0.0:
					direction = ay
			goingUp = direction > 0.0

			xt = ax*t3 + bx*t2 + cx*t + dx
			if xt < x:
				above = goingUp
				continue

			if t == 0.0:
				if not goingUp:
					self._addIntersection(goingUp)
			elif t == 1.0:
				if not above:
					self._addIntersection(goingUp)
			else:
				if above != goingUp:
					self._addIntersection(goingUp)
				#else:
				#   we're not really intersecting, merely touching the 'top'
			above = goingUp
Exemplo n.º 12
0

# ----------
# Misc. Math
# ----------


def _tValueForPointOnCubicCurve(point, (pt1, pt2, pt3, pt4), isHorizontal=0):
    """
    Finds a t value on a curve from a point.
    The points must be originaly be a point on the curve.
    This will only back trace the t value, needed to split the curve in parts
    """
    a, b, c, d = bezierTools.calcCubicParameters(pt1, pt2, pt3, pt4)
    solutions = bezierTools.solveCubic(a[isHorizontal], b[isHorizontal],
                                       c[isHorizontal],
                                       d[isHorizontal] - point[isHorizontal])
    solutions = [t for t in solutions if 0 <= t < 1]
    if not solutions and not isHorizontal:
        # can happen that a horizontal line doens intersect, try the vertical
        return _tValueForPointOnCubicCurve(point, (pt1, pt2, pt3, pt4),
                                           isHorizontal=1)
    if len(solutions) > 1:
        intersectionLenghts = {}
        for t in solutions:
            tp = _getCubicPoint(t, pt1, pt2, pt3, pt4)
            dist = _distance(tp, point)
            intersectionLenghts[dist] = t
        minDist = min(intersectionLenghts.keys())
        solutions = [intersectionLenghts[minDist]]
    return solutions
    def _curveToOne(self, bcp1, bcp2, point):
        x, y = self.testPoint
        x1, y1 = self._getCurrentPoint()
        x2, y2 = bcp1
        x3, y3 = bcp2
        x4, y4 = point

        if x1 < x and x2 < x and x3 < x and x4 < x:
            return
        if y1 < y and y2 < y and y3 < y and y4 < y:
            return
        if y1 >= y and y2 >= y and y3 >= y and y4 >= y:
            return

        dy = y1
        cy = (y2 - dy) * 3.0
        by = (y3 - y2) * 3.0 - cy
        ay = y4 - dy - cy - by
        solutions = sorted(solveCubic(ay, by, cy, dy - y))
        solutions = [
            t for t in solutions if ZERO_MINUS_EPSILON <= t <= ONE_PLUS_EPSILON
        ]
        if not solutions:
            return

        dx = x1
        cx = (x2 - dx) * 3.0
        bx = (x3 - x2) * 3.0 - cx
        ax = x4 - dx - cx - bx

        above = y1 >= y
        lastT = None
        for t in solutions:
            if t == lastT:
                continue
            lastT = t
            t2 = t * t
            t3 = t2 * t

            direction = 3 * ay * t2 + 2 * by * t + cy
            if direction == 0.0:
                direction = 6 * ay * t + 2 * by
                if direction == 0.0:
                    direction = ay
            goingUp = direction > 0.0

            xt = ax * t3 + bx * t2 + cx * t + dx
            if xt < x:
                above = goingUp
                continue

            if t == 0.0:
                if not goingUp:
                    self._addIntersection(goingUp)
            elif t == 1.0:
                if not above:
                    self._addIntersection(goingUp)
            else:
                if above != goingUp:
                    self._addIntersection(goingUp)
                #else:
                #   we're not really intersecting, merely touching the 'top'
            above = goingUp
Exemplo n.º 14
0
    # get the area
    area = sum([x0 * y1 - x1 * y0 for ((x0, y0), (x1, y1)) in segments])
    return area <= 0

# ----------
# Misc. Math
# ----------

def _tValueForPointOnCubicCurve(point, (pt1, pt2, pt3, pt4), isHorizontal=0):
    """
    Finds a t value on a curve from a point.
    The points must be originaly be a point on the curve.
    This will only back trace the t value, needed to split the curve in parts
    """
    a, b, c, d = bezierTools.calcCubicParameters(pt1, pt2, pt3, pt4)
    solutions = bezierTools.solveCubic(a[isHorizontal], b[isHorizontal], c[isHorizontal],
        d[isHorizontal] - point[isHorizontal])
    solutions = [t for t in solutions if 0 <= t < 1]
    if not solutions and not isHorizontal:
        # can happen that a horizontal line doens intersect, try the vertical
        return _tValueForPointOnCubicCurve(point, (pt1, pt2, pt3, pt4), isHorizontal=1)
    if len(solutions) > 1:
        intersectionLenghts = {}
        for t in solutions:
            tp = _getCubicPoint(t, pt1, pt2, pt3, pt4)
            dist = _distance(tp, point)
            intersectionLenghts[dist] = t
        minDist = min(intersectionLenghts.keys())
        solutions = [intersectionLenghts[minDist]]
    return solutions

def _tValueForPointOnQuadCurve(point, pts, isHorizontal=0):