YOLaT-VectorGraphicsRecogni.../Datasets/a2c.py

194 строки
5.1 KiB
Python

import math
### Python conversion of the javascript a2c function
### Original function at: https://github.com/fontello/svgpath
# Convert an arc to a sequence of cubic bezier curves
#
TAU = math.pi * 2
# eslint-disable space-infix-ops
# Calculate an angle between two unit vectors
#
# Since we measure angle between radii of circular arcs,
# we can use simplified math (without length normalization)
#
def unit_vector_angle(ux, uy, vx, vy):
if(ux * vy - uy * vx < 0):
sign = -1
else:
sign = 1
dot = ux * vx + uy * vy
# Add this to work with arbitrary vectors:
# dot /= math.sqrt(ux * ux + uy * uy) * math.sqrt(vx * vx + vy * vy)
# rounding errors, e.g. -1.0000000000000002 can screw up this
if (dot > 1.0):
dot = 1.0
if (dot < -1.0):
dot = -1.0
return sign * math.acos(dot)
# Convert from endpoint to center parameterization,
# see http:#www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
#
# Return [cx, cy, theta1, delta_theta]
#
def get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi):
# Step 1.
#
# Moving an ellipse so origin will be the middlepoint between our two
# points. After that, rotate it to line up ellipse axes with coordinate
# axes.
#
x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2
y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2
rx_sq = rx * rx
ry_sq = ry * ry
x1p_sq = x1p * x1p
y1p_sq = y1p * y1p
# Step 2.
#
# Compute coordinates of the centre of this ellipse (cx', cy')
# in the new coordinate system.
#
radicant = (rx_sq * ry_sq) - (rx_sq * y1p_sq) - (ry_sq * x1p_sq)
if (radicant < 0):
# due to rounding errors it might be e.g. -1.3877787807814457e-17
radicant = 0
radicant /= (rx_sq * y1p_sq) + (ry_sq * x1p_sq)
factor = 1
if(fa == fs):# Migration Note: note ===
factor = -1
radicant = math.sqrt(radicant) * factor #(fa === fs ? -1 : 1)
cxp = radicant * rx/ry * y1p
cyp = radicant * -ry/rx * x1p
# Step 3.
#
# Transform back to get centre coordinates (cx, cy) in the original
# coordinate system.
#
cx = cos_phi*cxp - sin_phi*cyp + (x1+x2)/2
cy = sin_phi*cxp + cos_phi*cyp + (y1+y2)/2
# Step 4.
#
# Compute angles (theta1, delta_theta).
#
v1x = (x1p - cxp) / rx
v1y = (y1p - cyp) / ry
v2x = (-x1p - cxp) / rx
v2y = (-y1p - cyp) / ry
theta1 = unit_vector_angle(1, 0, v1x, v1y)
delta_theta = unit_vector_angle(v1x, v1y, v2x, v2y)
if (fs == 0 and delta_theta > 0):#Migration Note: note ===
delta_theta -= TAU
if (fs == 1 and delta_theta < 0):#Migration Note: note ===
delta_theta += TAU
return [ cx, cy, theta1, delta_theta ]
#
# Approximate one unit arc segment with bezier curves,
# see http:#math.stackexchange.com/questions/873224
#
def approximate_unit_arc(theta1, delta_theta):
alpha = 4.0/3 * math.tan(delta_theta/4)
x1 = math.cos(theta1)
y1 = math.sin(theta1)
x2 = math.cos(theta1 + delta_theta)
y2 = math.sin(theta1 + delta_theta)
return [ x1, y1, x1 - y1*alpha, y1 + x1*alpha, x2 + y2*alpha, y2 - x2*alpha, x2, y2 ]
def a2c(x1, y1, x2, y2, fa, fs, rx, ry, phi):
sin_phi = math.sin(phi * TAU / 360)
cos_phi = math.cos(phi * TAU / 360)
# Make sure radii are valid
#
x1p = cos_phi*(x1-x2)/2 + sin_phi*(y1-y2)/2
y1p = -sin_phi*(x1-x2)/2 + cos_phi*(y1-y2)/2
if (x1p == 0 and y1p == 0): # Migration Note: note ===
# we're asked to draw line to itself
return []
if (rx == 0 or ry == 0): # Migration Note: note ===
# one of the radii is zero
return []
# Compensate out-of-range radii
#
rx = abs(rx)
ry = abs(ry)
lmbd = (x1p * x1p) / (rx * rx) + (y1p * y1p) / (ry * ry)
if (lmbd > 1):
rx *= math.sqrt(lmbd)
ry *= math.sqrt(lmbd)
# Get center parameters (cx, cy, theta1, delta_theta)
#
cc = get_arc_center(x1, y1, x2, y2, fa, fs, rx, ry, sin_phi, cos_phi)
result = []
theta1 = cc[2]
delta_theta = cc[3]
# Split an arc to multiple segments, so each segment
# will be less than 90
#
segments = int(max(math.ceil(abs(delta_theta) / (TAU / 4)), 1))
delta_theta /= segments
for i in range(0, segments):
result.append(approximate_unit_arc(theta1, delta_theta))
theta1 += delta_theta
# We have a bezier approximation of a unit circle,
# now need to transform back to the original ellipse
#
return getMappedList(result, rx, ry, sin_phi, cos_phi, cc)
def getMappedList(result, rx, ry, sin_phi, cos_phi, cc):
mappedList = []
for elem in result:
curve = []
for i in range(0, len(elem), 2):
x = elem[i + 0]
y = elem[i + 1]
# scale
x *= rx
y *= ry
# rotate
xp = cos_phi*x - sin_phi*y
yp = sin_phi*x + cos_phi*y
# translate
elem[i + 0] = xp + cc[0]
elem[i + 1] = yp + cc[1]
curve.append(complex(elem[i + 0], elem[i + 1]))
mappedList.append(curve)
return mappedList