| 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 |
| 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 |
| 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: |
| |
| 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): |
| |
| |
| 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(): |
| |
| 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 |
|
|