parent
2da39e4c02
commit
1579c544aa
|
@ -4,7 +4,7 @@ Arc."""
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
from __future__ import division, absolute_import, print_function
|
from __future__ import division, absolute_import, print_function
|
||||||
from math import sqrt, cos, sin, acos, asin, degrees, radians, log, pi
|
from math import sqrt, cos, sin, acos, degrees, radians, log, pi
|
||||||
from cmath import exp, sqrt as csqrt, phase
|
from cmath import exp, sqrt as csqrt, phase
|
||||||
from collections import MutableSequence
|
from collections import MutableSequence
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
@ -686,29 +686,6 @@ class Line(object):
|
||||||
ymax = max(self.start.imag, self.end.imag)
|
ymax = max(self.start.imag, self.end.imag)
|
||||||
return xmin, xmax, ymin, ymax
|
return xmin, xmax, ymin, ymax
|
||||||
|
|
||||||
def point_to_t(self, point):
|
|
||||||
"""If the point lies on the Line, returns its `t` parameter.
|
|
||||||
If the point does not lie on the Line, returns None."""
|
|
||||||
|
|
||||||
# Single-precision floats have only 7 significant figures of
|
|
||||||
# resolution, so test that we're within 6 sig figs.
|
|
||||||
if np.isclose(point, self.start, rtol=0, atol=1e-6):
|
|
||||||
return 0.0
|
|
||||||
elif np.isclose(point, self.end, rtol=0, atol=1e-6):
|
|
||||||
return 1.0
|
|
||||||
|
|
||||||
# Finding the point "by hand" here is much faster than calling
|
|
||||||
# radialrange(), see the discussion on PR #40:
|
|
||||||
# https://github.com/mathandy/svgpathtools/pull/40#issuecomment-358134261
|
|
||||||
|
|
||||||
p = self.poly()
|
|
||||||
# p(t) = (p_1 * t) + p_0 = point
|
|
||||||
# t = (point - p_0) / p_1
|
|
||||||
t = (point - p[0]) / p[1]
|
|
||||||
if np.isclose(t.imag, 0) and (t.real >= 0.0) and (t.real <= 1.0):
|
|
||||||
return t.real
|
|
||||||
return None
|
|
||||||
|
|
||||||
def cropped(self, t0, t1):
|
def cropped(self, t0, t1):
|
||||||
"""returns a cropped copy of this segment which starts at
|
"""returns a cropped copy of this segment which starts at
|
||||||
self.point(t0) and ends at self.point(t1)."""
|
self.point(t0) and ends at self.point(t1)."""
|
||||||
|
@ -1471,128 +1448,6 @@ class Arc(object):
|
||||||
y = rx*sinphi*cos(angle) + ry*cosphi*sin(angle) + self.center.imag
|
y = rx*sinphi*cos(angle) + ry*cosphi*sin(angle) + self.center.imag
|
||||||
return complex(x, y)
|
return complex(x, y)
|
||||||
|
|
||||||
def point_to_t(self, point):
|
|
||||||
"""If the point lies on the Arc, returns its `t` parameter.
|
|
||||||
If the point does not lie on the Arc, returns None.
|
|
||||||
This function only works on Arcs with rotation == 0.0"""
|
|
||||||
|
|
||||||
def in_range(min, max, val):
|
|
||||||
return (min <= val) and (max >= val)
|
|
||||||
|
|
||||||
# Single-precision floats have only 7 significant figures of
|
|
||||||
# resolution, so test that we're within 6 sig figs.
|
|
||||||
if np.isclose(point, self.start, rtol=0.0, atol=1e-6):
|
|
||||||
return 0.0
|
|
||||||
elif np.isclose(point, self.end, rtol=0.0, atol=1e-6):
|
|
||||||
return 1.0
|
|
||||||
|
|
||||||
if self.rotation != 0.0:
|
|
||||||
raise ValueError("Arc.point_to_t() only works on non-rotated Arcs.")
|
|
||||||
|
|
||||||
v = point - self.center
|
|
||||||
distance_from_center = sqrt((v.real * v.real) + (v.imag * v.imag))
|
|
||||||
min_radius = min(self.radius.real, self.radius.imag)
|
|
||||||
max_radius = max(self.radius.real, self.radius.imag)
|
|
||||||
if (distance_from_center < min_radius) and not np.isclose(distance_from_center, min_radius):
|
|
||||||
return None
|
|
||||||
if (distance_from_center > max_radius) and not np.isclose(distance_from_center, max_radius):
|
|
||||||
return None
|
|
||||||
|
|
||||||
# x = center_x + radius_x cos(radians(theta + t delta))
|
|
||||||
# y = center_y + radius_y sin(radians(theta + t delta))
|
|
||||||
#
|
|
||||||
# For x:
|
|
||||||
# cos(radians(theta + t delta)) = (x - center_x) / radius_x
|
|
||||||
# radians(theta + t delta) = acos((x - center_x) / radius_x)
|
|
||||||
# theta + t delta = degrees(acos((x - center_x) / radius_x))
|
|
||||||
# t_x = (degrees(acos((x - center_x) / radius_x)) - theta) / delta
|
|
||||||
#
|
|
||||||
# Similarly for y:
|
|
||||||
# t_y = (degrees(asin((y - center_y) / radius_y)) - theta) / delta
|
|
||||||
|
|
||||||
x = point.real
|
|
||||||
y = point.imag
|
|
||||||
|
|
||||||
#
|
|
||||||
# +Y points down!
|
|
||||||
#
|
|
||||||
# sweep mean clocwise
|
|
||||||
# sweep && (delta > 0)
|
|
||||||
# !sweep && (delta < 0)
|
|
||||||
#
|
|
||||||
# -180 <= theta_1 <= 180
|
|
||||||
#
|
|
||||||
# large_arc && (-360 <= delta <= 360)
|
|
||||||
# !large_arc && (-180 < delta < 180)
|
|
||||||
#
|
|
||||||
|
|
||||||
end_angle = self.theta + self.delta
|
|
||||||
min_angle = min(self.theta, end_angle)
|
|
||||||
max_angle = max(self.theta, end_angle)
|
|
||||||
|
|
||||||
acos_arg = (x - self.center.real) / self.radius.real
|
|
||||||
if acos_arg > 1.0:
|
|
||||||
acos_arg = 1.0
|
|
||||||
elif acos_arg < -1.0:
|
|
||||||
acos_arg = -1.0
|
|
||||||
|
|
||||||
x_angle_0 = degrees(acos(acos_arg))
|
|
||||||
while x_angle_0 < min_angle:
|
|
||||||
x_angle_0 += 360.0
|
|
||||||
while x_angle_0 > max_angle:
|
|
||||||
x_angle_0 -= 360.0
|
|
||||||
|
|
||||||
x_angle_1 = -1.0 * x_angle_0
|
|
||||||
while x_angle_1 < min_angle:
|
|
||||||
x_angle_1 += 360.0
|
|
||||||
while x_angle_1 > max_angle:
|
|
||||||
x_angle_1 -= 360.0
|
|
||||||
|
|
||||||
t_x_0 = (x_angle_0 - self.theta) / self.delta
|
|
||||||
t_x_1 = (x_angle_1 - self.theta) / self.delta
|
|
||||||
|
|
||||||
asin_arg = (y - self.center.imag) / self.radius.imag
|
|
||||||
if asin_arg > 1.0:
|
|
||||||
asin_arg = 1.0
|
|
||||||
elif asin_arg < -1.0:
|
|
||||||
asin_arg = -1.0
|
|
||||||
|
|
||||||
y_angle_0 = degrees(asin(asin_arg))
|
|
||||||
while y_angle_0 < min_angle:
|
|
||||||
y_angle_0 += 360.0
|
|
||||||
while y_angle_0 > max_angle:
|
|
||||||
y_angle_0 -= 360.0
|
|
||||||
|
|
||||||
y_angle_1 = 180 - y_angle_0
|
|
||||||
while y_angle_1 < min_angle:
|
|
||||||
y_angle_1 += 360.0
|
|
||||||
while y_angle_1 > max_angle:
|
|
||||||
y_angle_1 -= 360.0
|
|
||||||
|
|
||||||
t_y_0 = (y_angle_0 - self.theta) / self.delta
|
|
||||||
t_y_1 = (y_angle_1 - self.theta) / self.delta
|
|
||||||
|
|
||||||
t = None
|
|
||||||
if np.isclose(t_x_0, t_y_0):
|
|
||||||
t = (t_x_0 + t_y_0) / 2.0
|
|
||||||
elif np.isclose(t_x_0, t_y_1):
|
|
||||||
t= (t_x_0 + t_y_1) / 2.0
|
|
||||||
elif np.isclose(t_x_1, t_y_0):
|
|
||||||
t = (t_x_1 + t_y_0) / 2.0
|
|
||||||
elif np.isclose(t_x_1, t_y_1):
|
|
||||||
t = (t_x_1 + t_y_1) / 2.0
|
|
||||||
else:
|
|
||||||
# Comparing None and float yields a result in python2,
|
|
||||||
# but throws TypeError in python3. This fix (suggested by
|
|
||||||
# @CatherineH) explicitly handles and avoids the case where
|
|
||||||
# the None-vs-float comparison would have happened below.
|
|
||||||
return None
|
|
||||||
|
|
||||||
if (t >= 0.0) and (t <= 1.0):
|
|
||||||
return t
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def centeriso(self, z):
|
def centeriso(self, z):
|
||||||
"""This is an isometry that translates and rotates self so that it
|
"""This is an isometry that translates and rotates self so that it
|
||||||
is centered on the origin and has its axes aligned with the xy axes."""
|
is centered on the origin and has its axes aligned with the xy axes."""
|
||||||
|
@ -1764,123 +1619,7 @@ class Arc(object):
|
||||||
to let me know if you're interested in such a feature -- or even better
|
to let me know if you're interested in such a feature -- or even better
|
||||||
please submit an implementation if you want to code one."""
|
please submit an implementation if you want to code one."""
|
||||||
|
|
||||||
# This special case can be easily solved algebraically.
|
if is_bezier_segment(other_seg):
|
||||||
if (self.rotation == 0) and isinstance(other_seg, Line):
|
|
||||||
a = self.radius.real
|
|
||||||
b = self.radius.imag
|
|
||||||
|
|
||||||
# Ignore the ellipse's center point (to pretend that it's
|
|
||||||
# centered at the origin), and translate the Line to match.
|
|
||||||
l = Line(start=(other_seg.start-self.center), end=(other_seg.end-self.center))
|
|
||||||
|
|
||||||
# This gives us the translated Line as a parametric equation.
|
|
||||||
# s = p1 t + p0
|
|
||||||
p = l.poly()
|
|
||||||
|
|
||||||
if p[1].real == 0.0:
|
|
||||||
# The `x` value doesn't depend on `t`, the line is vertical.
|
|
||||||
c = p[0].real
|
|
||||||
x_values = [c]
|
|
||||||
|
|
||||||
# Substitute the line `x = c` into the equation for the
|
|
||||||
# (origin-centered) ellipse.
|
|
||||||
#
|
|
||||||
# x^2/a^2 + y^2/b^2 = 1
|
|
||||||
# c^2/a^2 + y^2/b^2 = 1
|
|
||||||
# y^2/b^2 = 1 - c^2/a^2
|
|
||||||
# y^2 = b^2(1 - c^2/a^2)
|
|
||||||
# y = +-b sqrt(1 - c^2/a^2)
|
|
||||||
|
|
||||||
discriminant = 1 - (c * c)/(a * a)
|
|
||||||
if discriminant < 0:
|
|
||||||
return []
|
|
||||||
elif discriminant == 0:
|
|
||||||
y_values = [0]
|
|
||||||
else:
|
|
||||||
val = b * sqrt(discriminant)
|
|
||||||
y_values = [val, -val]
|
|
||||||
|
|
||||||
else:
|
|
||||||
# This is a non-vertical line.
|
|
||||||
#
|
|
||||||
# Convert the Line's parametric equation to the "y = mx + c" format.
|
|
||||||
# x = p1.real t + p0.real
|
|
||||||
# y = p1.imag t + p0.imag
|
|
||||||
#
|
|
||||||
# t = (x - p0.real) / p1.real
|
|
||||||
# t = (y - p0.imag) / p1.imag
|
|
||||||
#
|
|
||||||
# (y - p0.imag) / p1.imag = (x - p0.real) / p1.real
|
|
||||||
# (y - p0.imag) = ((x - p0.real) * p1.imag) / p1.real
|
|
||||||
# y = ((x - p0.real) * p1.imag) / p1.real + p0.imag
|
|
||||||
# y = (x p1.imag - p0.real * p1.imag) / p1.real + p0.imag
|
|
||||||
# y = x p1.imag/p1.real - p0.real p1.imag / p1.real + p0.imag
|
|
||||||
# m = p1.imag/p1.real
|
|
||||||
# c = -m p0.real + p0.imag
|
|
||||||
m = p[1].imag / p[1].real
|
|
||||||
c = (-m * p[0].real) + p[0].imag
|
|
||||||
|
|
||||||
# Substitute the line's y(x) equation into the equation for
|
|
||||||
# the ellipse. We can pretend the ellipse is centered at the
|
|
||||||
# origin, since we shifted the Line by the ellipse's center.
|
|
||||||
#
|
|
||||||
# x^2/a^2 + y^2/b^2 = 1
|
|
||||||
# x^2/a^2 + (mx+c)^2/b^2 = 1
|
|
||||||
# (b^2 x^2 + a^2 (mx+c)^2)/(a^2 b^2) = 1
|
|
||||||
# b^2 x^2 + a^2 (mx+c)^2 = a^2 b^2
|
|
||||||
# b^2 x^2 + a^2(m^2 x^2 + 2mcx + c^2) = a^2 b^2
|
|
||||||
# b^2 x^2 + a^2 m^2 x^2 + 2a^2 mcx + a^2 c^2 - a^2 b^2 = 0
|
|
||||||
# (a^2 m^2 + b^2)x^2 + 2a^2 mcx + a^2(c^2 - b^2) = 0
|
|
||||||
#
|
|
||||||
# The quadratic forumla tells us: x = (-B +- sqrt(B^2 - 4AC)) / 2A
|
|
||||||
# Where:
|
|
||||||
# A = a^2 m^2 + b^2
|
|
||||||
# B = 2 a^2 mc
|
|
||||||
# C = a^2(c^2 - b^2)
|
|
||||||
#
|
|
||||||
# The determinant is: B^2 - 4AC
|
|
||||||
#
|
|
||||||
# The solution simplifies to:
|
|
||||||
# x = (-a^2 mc +- a b sqrt(a^2 m^2 + b^2 - c^2)) / (a^2 m^2 + b^2)
|
|
||||||
#
|
|
||||||
# Solving the line for x(y) and substituting *that* into
|
|
||||||
# the equation for the ellipse gives this solution for y:
|
|
||||||
# y = (b^2 c +- abm sqrt(a^2 m^2 + b^2 - c^2)) / (a^2 m^2 + b^2)
|
|
||||||
|
|
||||||
denominator = (a * a * m * m) + (b * b)
|
|
||||||
|
|
||||||
discriminant = denominator - (c * c)
|
|
||||||
if discriminant < 0:
|
|
||||||
return []
|
|
||||||
|
|
||||||
x_sqrt = a * b * sqrt(discriminant)
|
|
||||||
x1 = (-(a * a * m * c) + x_sqrt) / denominator
|
|
||||||
x2 = (-(a * a * m * c) - x_sqrt) / denominator
|
|
||||||
x_values = [x1]
|
|
||||||
if x1 != x2:
|
|
||||||
x_values.append(x2)
|
|
||||||
|
|
||||||
y_sqrt = x_sqrt * m
|
|
||||||
y1 = ((b * b * c) + y_sqrt) / denominator
|
|
||||||
y2 = ((b * b * c) - y_sqrt) / denominator
|
|
||||||
y_values = [y1]
|
|
||||||
if y1 != y2:
|
|
||||||
y_values.append(y2)
|
|
||||||
|
|
||||||
intersections = []
|
|
||||||
for x in x_values:
|
|
||||||
for y in y_values:
|
|
||||||
p = complex(x, y) + self.center
|
|
||||||
my_t = self.point_to_t(p)
|
|
||||||
if my_t == None:
|
|
||||||
continue
|
|
||||||
other_t = other_seg.point_to_t(p)
|
|
||||||
if other_t == None:
|
|
||||||
continue
|
|
||||||
intersections.append([my_t, other_t])
|
|
||||||
return intersections
|
|
||||||
|
|
||||||
elif is_bezier_segment(other_seg):
|
|
||||||
u1poly = self.u1transform(other_seg.poly())
|
u1poly = self.u1transform(other_seg.poly())
|
||||||
u1poly_mag2 = real(u1poly)**2 + imag(u1poly)**2
|
u1poly_mag2 = real(u1poly)**2 + imag(u1poly)**2
|
||||||
t2s = polyroots01(u1poly_mag2 - 1)
|
t2s = polyroots01(u1poly_mag2 - 1)
|
||||||
|
|
|
@ -4,7 +4,6 @@ import unittest
|
||||||
from math import sqrt, pi
|
from math import sqrt, pi
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import random
|
|
||||||
|
|
||||||
# Internal dependencies
|
# Internal dependencies
|
||||||
from svgpathtools import *
|
from svgpathtools import *
|
||||||
|
@ -17,48 +16,6 @@ from svgpathtools.path import _NotImplemented4ArcException
|
||||||
# to be correct visually with the disvg() function.
|
# to be correct visually with the disvg() function.
|
||||||
|
|
||||||
|
|
||||||
def random_line():
|
|
||||||
x = (random.random() - 0.5) * 2000
|
|
||||||
y = (random.random() - 0.5) * 2000
|
|
||||||
start = complex(x, y)
|
|
||||||
|
|
||||||
x = (random.random() - 0.5) * 2000
|
|
||||||
y = (random.random() - 0.5) * 2000
|
|
||||||
end = complex(x, y)
|
|
||||||
|
|
||||||
return Line(start, end)
|
|
||||||
|
|
||||||
|
|
||||||
def random_arc():
|
|
||||||
x = (random.random() - 0.5) * 2000
|
|
||||||
y = (random.random() - 0.5) * 2000
|
|
||||||
start = complex(x, y)
|
|
||||||
|
|
||||||
x = (random.random() - 0.5) * 2000
|
|
||||||
y = (random.random() - 0.5) * 2000
|
|
||||||
end = complex(x, y)
|
|
||||||
|
|
||||||
x = (random.random() - 0.5) * 2000
|
|
||||||
y = (random.random() - 0.5) * 2000
|
|
||||||
radius = complex(x, y)
|
|
||||||
|
|
||||||
large_arc = random.choice([True, False])
|
|
||||||
sweep = random.choice([True, False])
|
|
||||||
|
|
||||||
return Arc(start=start, radius=radius, rotation=0.0, large_arc=large_arc, sweep=sweep, end=end)
|
|
||||||
|
|
||||||
|
|
||||||
def assert_intersections(a_seg, b_seg, intersections, count):
|
|
||||||
if count != None:
|
|
||||||
assert(len(intersections) == count)
|
|
||||||
for i in intersections:
|
|
||||||
assert(i[0] >= 0.0)
|
|
||||||
assert(i[0] <= 1.0)
|
|
||||||
assert(i[1] >= 0.0)
|
|
||||||
assert(i[1] <= 1.0)
|
|
||||||
assert(np.isclose(a_seg.point(i[0]), b_seg.point(i[1])))
|
|
||||||
|
|
||||||
|
|
||||||
class LineTest(unittest.TestCase):
|
class LineTest(unittest.TestCase):
|
||||||
|
|
||||||
def test_lines(self):
|
def test_lines(self):
|
||||||
|
@ -99,73 +56,6 @@ class LineTest(unittest.TestCase):
|
||||||
self.assertTrue(line != str(line))
|
self.assertTrue(line != str(line))
|
||||||
self.assertFalse(cubic == line)
|
self.assertFalse(cubic == line)
|
||||||
|
|
||||||
def test_point_to_t(self):
|
|
||||||
l = Line(start=(0+0j), end=(0+10j))
|
|
||||||
assert(l.point_to_t(0+0j) == 0.0)
|
|
||||||
assert(np.isclose(l.point_to_t(0+5j), 0.5))
|
|
||||||
assert(l.point_to_t(0+10j) == 1.0)
|
|
||||||
assert(l.point_to_t(1+0j) == None)
|
|
||||||
assert(l.point_to_t(0-1j) == None)
|
|
||||||
assert(l.point_to_t(0+11j) == None)
|
|
||||||
|
|
||||||
l = Line(start=(0+0j), end=(10+10j))
|
|
||||||
assert(l.point_to_t(0+0j) == 0.0)
|
|
||||||
assert(np.isclose(l.point_to_t(5+5j), 0.5))
|
|
||||||
assert(l.point_to_t(10+10j) == 1.0)
|
|
||||||
assert(l.point_to_t(1+0j) == None)
|
|
||||||
assert(l.point_to_t(0-1j) == None)
|
|
||||||
assert(l.point_to_t(0+11j) == None)
|
|
||||||
assert(l.point_to_t(10.001+10.001j) == None)
|
|
||||||
assert(l.point_to_t(-0.001-0.001j) == None)
|
|
||||||
|
|
||||||
l = Line(start=(0+0j), end=(10+0j))
|
|
||||||
assert(l.point_to_t(0+0j) == 0.0)
|
|
||||||
assert(np.isclose(l.point_to_t(5+0j), 0.5))
|
|
||||||
assert(l.point_to_t(10+0j) == 1.0)
|
|
||||||
assert(l.point_to_t(0+1j) == None)
|
|
||||||
assert(l.point_to_t(0-1j) == None)
|
|
||||||
assert(l.point_to_t(0+11j) == None)
|
|
||||||
assert(l.point_to_t(10.001+0j) == None)
|
|
||||||
assert(l.point_to_t(-0.001-0j) == None)
|
|
||||||
|
|
||||||
l = Line(start=(-2-1j), end=(11-20j))
|
|
||||||
assert(l.point_to_t(-2-1j) == 0.0)
|
|
||||||
assert(np.isclose(l.point_to_t(4.5-10.5j), 0.5))
|
|
||||||
assert(l.point_to_t(11-20j) == 1.0)
|
|
||||||
assert(l.point_to_t(0+1j) == None)
|
|
||||||
assert(l.point_to_t(0-1j) == None)
|
|
||||||
assert(l.point_to_t(0+11j) == None)
|
|
||||||
assert(l.point_to_t(10.001+0j) == None)
|
|
||||||
assert(l.point_to_t(-0.001-0j) == None)
|
|
||||||
|
|
||||||
l = Line(start=(40.234-32.613j), end=(12.7-32.613j))
|
|
||||||
assert(l.point_to_t(40.234-32.613j) == 0.0)
|
|
||||||
assert(np.isclose(l.point_to_t(33.3505-32.613j), 0.25))
|
|
||||||
assert(np.isclose(l.point_to_t(26.467-32.613j), 0.50))
|
|
||||||
assert(np.isclose(l.point_to_t(19.5835-32.613j), 0.75))
|
|
||||||
assert(l.point_to_t(12.7-32.613j) == 1.0)
|
|
||||||
assert(l.point_to_t(40.25-32.613j) == None)
|
|
||||||
assert(l.point_to_t(12.65-32.613j) == None)
|
|
||||||
assert(l.point_to_t(11-20j) == None)
|
|
||||||
assert(l.point_to_t(0+1j) == None)
|
|
||||||
assert(l.point_to_t(0-1j) == None)
|
|
||||||
assert(l.point_to_t(0+11j) == None)
|
|
||||||
assert(l.point_to_t(10.001+0j) == None)
|
|
||||||
assert(l.point_to_t(-0.001-0j) == None)
|
|
||||||
|
|
||||||
random.seed()
|
|
||||||
for line_index in range(100):
|
|
||||||
l = random_line()
|
|
||||||
print(l)
|
|
||||||
for t_index in range(100):
|
|
||||||
orig_t = random.random()
|
|
||||||
p = l.point(orig_t)
|
|
||||||
computed_t = l.point_to_t(p)
|
|
||||||
print("orig_t=%f, p=%s, computed_t=%f" % (orig_t, p, computed_t))
|
|
||||||
assert(np.isclose(orig_t, computed_t))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CubicBezierTest(unittest.TestCase):
|
class CubicBezierTest(unittest.TestCase):
|
||||||
def test_approx_circle(self):
|
def test_approx_circle(self):
|
||||||
|
@ -588,71 +478,6 @@ class ArcTest(unittest.TestCase):
|
||||||
self.assertTrue(segment == Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j))
|
self.assertTrue(segment == Arc(0j, 100 + 50j, 0, 0, 0, 100 + 50j))
|
||||||
self.assertTrue(segment != Arc(0j, 100 + 50j, 0, 1, 0, 100 + 50j))
|
self.assertTrue(segment != Arc(0j, 100 + 50j, 0, 1, 0, 100 + 50j))
|
||||||
|
|
||||||
def test_point_to_t(self):
|
|
||||||
a = Arc(start=(0+0j), radius=(5+5j), rotation=0.0, large_arc=True, sweep=True, end=(0+10j))
|
|
||||||
assert(a.point_to_t(0+0j) == 0.0)
|
|
||||||
assert(np.isclose(a.point_to_t(5+5j), 0.5))
|
|
||||||
assert(a.point_to_t(0+10j) == 1.0)
|
|
||||||
assert(a.point_to_t(-5+5j) == None)
|
|
||||||
assert(a.point_to_t(0+5j) == None)
|
|
||||||
assert(a.point_to_t(1+0j) == None)
|
|
||||||
assert(a.point_to_t(0-1j) == None)
|
|
||||||
assert(a.point_to_t(0+11j) == None)
|
|
||||||
|
|
||||||
a = Arc(start=(0+0j), radius=(5+5j), rotation=0.0, large_arc=True, sweep=False, end=(0+10j))
|
|
||||||
assert(a.point_to_t(0+0j) == 0.0)
|
|
||||||
assert(np.isclose(a.point_to_t(-5+5j), 0.5))
|
|
||||||
assert(a.point_to_t(0+10j) == 1.0)
|
|
||||||
assert(a.point_to_t(5+5j) == None)
|
|
||||||
assert(a.point_to_t(0+5j) == None)
|
|
||||||
assert(a.point_to_t(1+0j) == None)
|
|
||||||
assert(a.point_to_t(0-1j) == None)
|
|
||||||
assert(a.point_to_t(0+11j) == None)
|
|
||||||
|
|
||||||
a = Arc(start=(-10+0j), radius=(10+20j), rotation=0.0, large_arc=True, sweep=True, end=(10+0j))
|
|
||||||
assert(a.point_to_t(-10+0j) == 0.0)
|
|
||||||
assert(np.isclose(a.point_to_t(0-20j), 0.5))
|
|
||||||
assert(a.point_to_t(10+0j) == 1.0)
|
|
||||||
assert(a.point_to_t(0+20j) == None)
|
|
||||||
assert(a.point_to_t(-5+5j) == None)
|
|
||||||
assert(a.point_to_t(0+5j) == None)
|
|
||||||
assert(a.point_to_t(1+0j) == None)
|
|
||||||
assert(a.point_to_t(0-1j) == None)
|
|
||||||
assert(a.point_to_t(0+11j) == None)
|
|
||||||
|
|
||||||
a = Arc(start=(100.834+27.987j), radius=(60.6+60.6j), rotation=0.0, large_arc=False, sweep=False, end=(40.234-32.613j))
|
|
||||||
assert(a.point_to_t(100.834+27.987j) == 0.0)
|
|
||||||
assert(np.isclose(a.point_to_t(96.2210993246+4.7963831644j), 0.25))
|
|
||||||
assert(np.isclose(a.point_to_t(83.0846703014-14.8636715784j), 0.50))
|
|
||||||
assert(np.isclose(a.point_to_t(63.4246151671-28.0001000158j), 0.75))
|
|
||||||
assert(a.point_to_t(40.234-32.613j) == 1.00)
|
|
||||||
assert(a.point_to_t(-10+0j) == None)
|
|
||||||
assert(a.point_to_t(0+0j) == None)
|
|
||||||
|
|
||||||
a = Arc(start=(423.049961698-41.3779390229j), radius=(904.283878032+597.298520765j), rotation=0.0, large_arc=True, sweep=False, end=(548.984030235-312.385118044j))
|
|
||||||
orig_t = 0.854049465076
|
|
||||||
p = a.point(orig_t)
|
|
||||||
computed_t = a.point_to_t(p)
|
|
||||||
assert(np.isclose(orig_t, computed_t))
|
|
||||||
|
|
||||||
a = Arc(start=(-1-750j), radius=(750+750j), rotation=0.0, large_arc=True, sweep=False, end=1-750j)
|
|
||||||
assert(np.isclose(a.point_to_t(730.5212132777968+169.8191111892562j), 0.71373858))
|
|
||||||
assert(a.point_to_t(730.5212132777968+169j) == None)
|
|
||||||
assert(a.point_to_t(730.5212132777968+171j) == None)
|
|
||||||
|
|
||||||
random.seed()
|
|
||||||
for arc_index in range(100):
|
|
||||||
a = random_arc()
|
|
||||||
print(a)
|
|
||||||
for t_index in range(100):
|
|
||||||
orig_t = random.random()
|
|
||||||
p = a.point(orig_t)
|
|
||||||
computed_t = a.point_to_t(p)
|
|
||||||
print("t:", orig_t)
|
|
||||||
print("p:", p)
|
|
||||||
print("computed t:", computed_t)
|
|
||||||
assert(np.isclose(orig_t, computed_t))
|
|
||||||
|
|
||||||
|
|
||||||
class TestPath(unittest.TestCase):
|
class TestPath(unittest.TestCase):
|
||||||
|
|
||||||
|
@ -1380,147 +1205,6 @@ class Test_intersect(unittest.TestCase):
|
||||||
assert(abs(l0.point(i[0][0])-l1.point(i[0][1])) < 1e-9)
|
assert(abs(l0.point(i[0][0])-l1.point(i[0][1])) < 1e-9)
|
||||||
|
|
||||||
|
|
||||||
def test_arc_line(self):
|
|
||||||
l = Line(start=(-20+1j), end=(20+1j))
|
|
||||||
a = Arc(start=(-10+0), radius=(10+10j), rotation=0.0, large_arc=True, sweep=False, end=(10+0j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 2)
|
|
||||||
|
|
||||||
l = Line(start=(-20-1j), end=(20-1j))
|
|
||||||
a = Arc(start=(-10+0), radius=(10+10j), rotation=0.0, large_arc=True, sweep=False, end=(10+0j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 0)
|
|
||||||
|
|
||||||
l = Line(start=(-20+1j), end=(20+1j))
|
|
||||||
a = Arc(start=(-10+0), radius=(10+10j), rotation=0.0, large_arc=True, sweep=True, end=(10+0j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 0)
|
|
||||||
|
|
||||||
l = Line(start=(-20-1j), end=(20-1j))
|
|
||||||
a = Arc(start=(-10+0), radius=(10+10j), rotation=0.0, large_arc=True, sweep=True, end=(10+0j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 2)
|
|
||||||
|
|
||||||
l = Line(start=(-20+0j), end=(20+0j))
|
|
||||||
a = Arc(start=(-10+0), radius=(10+10j), rotation=0.0, large_arc=True, sweep=True, end=(10+0j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 2)
|
|
||||||
|
|
||||||
l = Line(start=(-20+0j), end=(20+0j))
|
|
||||||
a = Arc(start=(-10+0), radius=(10+10j), rotation=0.0, large_arc=True, sweep=False, end=(10+0j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 2)
|
|
||||||
|
|
||||||
l = Line(start=(-20+10j), end=(20+10j))
|
|
||||||
a = Arc(start=(-10+0), radius=(10+10j), rotation=0.0, large_arc=True, sweep=False, end=(10+0j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 1)
|
|
||||||
|
|
||||||
l = Line(start=(229.226097475-282.403591377j), end=(751.681212592+188.907748894j))
|
|
||||||
a = Arc(start=(-1-750j), radius=(750+750j), rotation=0.0, large_arc=True, sweep=False, end=(1-750j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 1)
|
|
||||||
|
|
||||||
# end of arc touches start of horizontal line
|
|
||||||
l = Line(start=(40.234-32.613j), end=(12.7-32.613j))
|
|
||||||
a = Arc(start=(100.834+27.987j), radius=(60.6+60.6j), rotation=0.0, large_arc=False, sweep=False, end=(40.234-32.613j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 1)
|
|
||||||
|
|
||||||
# vertical line, intersects half-arc once
|
|
||||||
l = Line(start=(1-100j), end=(1+100j))
|
|
||||||
a = Arc(start=(10.0+0j), radius=(10+10j), rotation=0, large_arc=False, sweep=True, end=(-10.0+0j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 1)
|
|
||||||
|
|
||||||
# vertical line, intersects nearly-full arc twice
|
|
||||||
l = Line(start=(1-100j), end=(1+100j))
|
|
||||||
a = Arc(start=(0.1-10j), radius=(10+10j), rotation=0, large_arc=True, sweep=True, end=(-0.1-10j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 2)
|
|
||||||
|
|
||||||
# vertical line, start of line touches end of arc
|
|
||||||
l = Line(start=(15.4+100j), end=(15.4+90.475j))
|
|
||||||
a = Arc(start=(25.4+90j), radius=(10+10j), rotation=0, large_arc=False, sweep=True, end=(15.4+100j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 1)
|
|
||||||
|
|
||||||
l = Line(start=(100-60.913j), end=(40+59j))
|
|
||||||
a = Arc(start=(100.834+27.987j), radius=(60.6+60.6j), rotation=0.0, large_arc=False, sweep=False, end=(40.234-32.613j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 1)
|
|
||||||
|
|
||||||
l = Line(start=(128.57143 + 380.93364j), end=(300.00001 + 389.505069j))
|
|
||||||
a = Arc(start=(214.28572 + 598.07649j), radius=(85.714287 + 108.57143j), rotation=0.0, large_arc=False, sweep=True, end=(128.57143 + 489.50507j))
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, 0)
|
|
||||||
|
|
||||||
random.seed()
|
|
||||||
for arc_index in range(50):
|
|
||||||
a = random_arc()
|
|
||||||
print(a)
|
|
||||||
for line_index in range(100):
|
|
||||||
l = random_line()
|
|
||||||
print(l)
|
|
||||||
intersections = a.intersect(l)
|
|
||||||
assert_intersections(a, l, intersections, None)
|
|
||||||
|
|
||||||
|
|
||||||
def test_intersect_arc_line_1(self):
|
|
||||||
|
|
||||||
"""Verify the return value of intersects() when an Arc ends at
|
|
||||||
the starting point of a Line."""
|
|
||||||
|
|
||||||
a = Arc(start=(0+0j), radius=(10+10j), rotation=0, large_arc=False,
|
|
||||||
sweep=False, end=(10+10j), autoscale_radius=False)
|
|
||||||
l = Line(start=(10+10j), end=(20+10j))
|
|
||||||
|
|
||||||
i = a.intersect(l)
|
|
||||||
assert(len(i) == 1)
|
|
||||||
assert(i[0][0] == 1.0)
|
|
||||||
assert(i[0][1] == 0.0)
|
|
||||||
|
|
||||||
|
|
||||||
def test_intersect_arc_line_2(self):
|
|
||||||
|
|
||||||
"""Verify the return value of intersects() when an Arc is pierced
|
|
||||||
once by a Line."""
|
|
||||||
|
|
||||||
a = Arc(start=(0+0j), radius=(10+10j), rotation=0, large_arc=False,
|
|
||||||
sweep=False, end=(10+10j), autoscale_radius=False)
|
|
||||||
l = Line(start=(0+9j), end=(20+9j))
|
|
||||||
|
|
||||||
i = a.intersect(l)
|
|
||||||
assert(len(i) == 1)
|
|
||||||
assert(i[0][0] >= 0.0)
|
|
||||||
assert(i[0][0] <= 1.0)
|
|
||||||
assert(i[0][1] >= 0.0)
|
|
||||||
assert(i[0][1] <= 1.0)
|
|
||||||
|
|
||||||
|
|
||||||
def test_intersect_arc_line_3(self):
|
|
||||||
|
|
||||||
"""Verify the return value of intersects() when an Arc misses
|
|
||||||
a Line, but the circle that the Arc is part of hits the Line."""
|
|
||||||
|
|
||||||
a = Arc(start=(0+0j), radius=(10+10j), rotation=0, large_arc=False,
|
|
||||||
sweep=False, end=(10+10j), autoscale_radius=False)
|
|
||||||
l = Line(start=(11+100j), end=(11-100j))
|
|
||||||
|
|
||||||
i = a.intersect(l)
|
|
||||||
assert(len(i) == 0)
|
|
||||||
|
|
||||||
|
|
||||||
def test_intersect_arc_line_disjoint_bboxes(self):
|
|
||||||
# The arc is very short, which contributes to the problem here.
|
|
||||||
l = Line(start=(125.314540561+144.192926144j), end=(125.798713132+144.510685287j))
|
|
||||||
a = Arc(start=(128.26640649+146.908463323j), radius=(2+2j),
|
|
||||||
rotation=0, large_arc=False, sweep=True,
|
|
||||||
end=(128.26640606+146.90846449j))
|
|
||||||
i = l.intersect(a)
|
|
||||||
assert(i == [])
|
|
||||||
|
|
||||||
|
|
||||||
class TestPathTools(unittest.TestCase):
|
class TestPathTools(unittest.TestCase):
|
||||||
# moved from test_pathtools.py
|
# moved from test_pathtools.py
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue