Spaces:
Runtime error
Runtime error
import torch | |
import svgpathtools | |
import math | |
class Circle: | |
def __init__(self, radius, center, stroke_width = torch.tensor(1.0), id = ''): | |
self.radius = radius | |
self.center = center | |
self.stroke_width = stroke_width | |
self.id = id | |
class Ellipse: | |
def __init__(self, radius, center, stroke_width = torch.tensor(1.0), id = ''): | |
self.radius = radius | |
self.center = center | |
self.stroke_width = stroke_width | |
self.id = id | |
class Path: | |
def __init__(self, | |
num_control_points, | |
points, | |
is_closed, | |
stroke_width = torch.tensor(1.0), | |
id = '', | |
use_distance_approx = False): | |
self.num_control_points = num_control_points | |
self.points = points | |
self.is_closed = is_closed | |
self.stroke_width = stroke_width | |
self.id = id | |
self.use_distance_approx = use_distance_approx | |
class Polygon: | |
def __init__(self, points, is_closed, stroke_width = torch.tensor(1.0), id = ''): | |
self.points = points | |
self.is_closed = is_closed | |
self.stroke_width = stroke_width | |
self.id = id | |
class Rect: | |
def __init__(self, p_min, p_max, stroke_width = torch.tensor(1.0), id = ''): | |
self.p_min = p_min | |
self.p_max = p_max | |
self.stroke_width = stroke_width | |
self.id = id | |
class ShapeGroup: | |
def __init__(self, | |
shape_ids, | |
fill_color, | |
use_even_odd_rule = True, | |
stroke_color = None, | |
shape_to_canvas = torch.eye(3), | |
id = ''): | |
self.shape_ids = shape_ids | |
self.fill_color = fill_color | |
self.use_even_odd_rule = use_even_odd_rule | |
self.stroke_color = stroke_color | |
self.shape_to_canvas = shape_to_canvas | |
self.id = id | |
def from_svg_path(path_str, shape_to_canvas = torch.eye(3), force_close = False): | |
path = svgpathtools.parse_path(path_str) | |
if len(path) == 0: | |
return [] | |
ret_paths = [] | |
subpaths = path.continuous_subpaths() | |
for subpath in subpaths: | |
if subpath.isclosed(): | |
if len(subpath) > 1 and isinstance(subpath[-1], svgpathtools.Line) and subpath[-1].length() < 1e-5: | |
subpath.remove(subpath[-1]) | |
subpath[-1].end = subpath[0].start # Force closing the path | |
subpath.end = subpath[-1].end | |
assert(subpath.isclosed()) | |
else: | |
beg = subpath[0].start | |
end = subpath[-1].end | |
if abs(end - beg) < 1e-5: | |
subpath[-1].end = beg # Force closing the path | |
subpath.end = subpath[-1].end | |
assert(subpath.isclosed()) | |
elif force_close: | |
subpath.append(svgpathtools.Line(end, beg)) | |
subpath.end = subpath[-1].end | |
assert(subpath.isclosed()) | |
num_control_points = [] | |
points = [] | |
for i, e in enumerate(subpath): | |
if i == 0: | |
points.append((e.start.real, e.start.imag)) | |
else: | |
# Must begin from the end of previous segment | |
assert(e.start.real == points[-1][0]) | |
assert(e.start.imag == points[-1][1]) | |
if isinstance(e, svgpathtools.Line): | |
num_control_points.append(0) | |
elif isinstance(e, svgpathtools.QuadraticBezier): | |
num_control_points.append(1) | |
points.append((e.control.real, e.control.imag)) | |
elif isinstance(e, svgpathtools.CubicBezier): | |
num_control_points.append(2) | |
points.append((e.control1.real, e.control1.imag)) | |
points.append((e.control2.real, e.control2.imag)) | |
elif isinstance(e, svgpathtools.Arc): | |
# Convert to Cubic curves | |
# https://www.joecridge.me/content/pdf/bezier-arcs.pdf | |
start = e.theta * math.pi / 180.0 | |
stop = (e.theta + e.delta) * math.pi / 180.0 | |
sign = 1.0 | |
if stop < start: | |
sign = -1.0 | |
epsilon = 0.00001 | |
debug = abs(e.delta) >= 90.0 | |
while (sign * (stop - start) > epsilon): | |
arc_to_draw = stop - start | |
if arc_to_draw > 0.0: | |
arc_to_draw = min(arc_to_draw, 0.5 * math.pi) | |
else: | |
arc_to_draw = max(arc_to_draw, -0.5 * math.pi) | |
alpha = arc_to_draw / 2.0 | |
cos_alpha = math.cos(alpha) | |
sin_alpha = math.sin(alpha) | |
cot_alpha = 1.0 / math.tan(alpha) | |
phi = start + alpha | |
cos_phi = math.cos(phi) | |
sin_phi = math.sin(phi) | |
lambda_ = (4.0 - cos_alpha) / 3.0 | |
mu = sin_alpha + (cos_alpha - lambda_) * cot_alpha | |
last = sign * (stop - (start + arc_to_draw)) <= epsilon | |
num_control_points.append(2) | |
rx = e.radius.real | |
ry = e.radius.imag | |
cx = e.center.real | |
cy = e.center.imag | |
rot = e.phi * math.pi / 180.0 | |
cos_rot = math.cos(rot) | |
sin_rot = math.sin(rot) | |
x = lambda_ * cos_phi + mu * sin_phi | |
y = lambda_ * sin_phi - mu * cos_phi | |
xx = x * cos_rot - y * sin_rot | |
yy = x * sin_rot + y * cos_rot | |
points.append((cx + rx * xx, cy + ry * yy)) | |
x = lambda_ * cos_phi - mu * sin_phi | |
y = lambda_ * sin_phi + mu * cos_phi | |
xx = x * cos_rot - y * sin_rot | |
yy = x * sin_rot + y * cos_rot | |
points.append((cx + rx * xx, cy + ry * yy)) | |
if not last: | |
points.append((cx + rx * math.cos(rot + start + arc_to_draw), | |
cy + ry * math.sin(rot + start + arc_to_draw))) | |
start += arc_to_draw | |
first = False | |
if i != len(subpath) - 1: | |
points.append((e.end.real, e.end.imag)) | |
else: | |
if subpath.isclosed(): | |
# Must end at the beginning of first segment | |
assert(e.end.real == points[0][0]) | |
assert(e.end.imag == points[0][1]) | |
else: | |
points.append((e.end.real, e.end.imag)) | |
points = torch.tensor(points) | |
points = torch.cat((points, torch.ones([points.shape[0], 1])), dim = 1) @ torch.transpose(shape_to_canvas, 0, 1) | |
points = points / points[:, 2:3] | |
points = points[:, :2].contiguous() | |
ret_paths.append(Path(torch.tensor(num_control_points), points, subpath.isclosed())) | |
return ret_paths | |