Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified otls/ae__SVG_Import.hda
Binary file not shown.
1 change: 1 addition & 0 deletions python3.10libs/svg/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
3 changes: 3 additions & 0 deletions python3.10libs/svg/path/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .path import Path, Move, Line, Arc, Close # noqa: 401
from .path import CubicBezier, QuadraticBezier # noqa: 401
from .parser import parse_path # noqa: 401
189 changes: 189 additions & 0 deletions python3.10libs/svg/path/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
# SVG Path specification parser

import re
from . import path

COMMANDS = set("MmZzLlHhVvCcSsQqTtAa")
UPPERCASE = set("MZLHVCSQTA")

COMMAND_RE = re.compile(r"([MmZzLlHhVvCcSsQqTtAa])")
FLOAT_RE = re.compile(r"[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?")


def _tokenize_path(pathdef):
for x in COMMAND_RE.split(pathdef):
if x in COMMANDS:
yield x
for token in FLOAT_RE.findall(x):
yield token


def parse_path(pathdef, current_pos=0j):
# In the SVG specs, initial movetos are absolute, even if
# specified as 'm'. This is the default behavior here as well.
# But if you pass in a current_pos variable, the initial moveto
# will be relative to that current_pos. This is useful.
elements = list(_tokenize_path(pathdef))
# Reverse for easy use of .pop()
elements.reverse()

segments = path.Path()
start_pos = None
command = None

while elements:

if elements[-1] in COMMANDS:
# New command.
last_command = command # Used by S and T
command = elements.pop()
absolute = command in UPPERCASE
command = command.upper()
else:
# If this element starts with numbers, it is an implicit command
# and we don't change the command. Check that it's allowed:
if command is None:
raise ValueError(
"Unallowed implicit command in %s, position %s"
% (pathdef, len(pathdef.split()) - len(elements))
)
last_command = command # Used by S and T

if command == "M":
# Moveto command.
x = elements.pop()
y = elements.pop()
pos = float(x) + float(y) * 1j
if absolute:
current_pos = pos
else:
current_pos += pos
segments.append(path.Move(current_pos))
# when M is called, reset start_pos
# This behavior of Z is defined in svg spec:
# http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
start_pos = current_pos

# Implicit moveto commands are treated as lineto commands.
# So we set command to lineto here, in case there are
# further implicit commands after this moveto.
command = "L"

elif command == "Z":
# Close path
segments.append(path.Close(current_pos, start_pos))
current_pos = start_pos
start_pos = None
command = None # You can't have implicit commands after closing.

elif command == "L":
x = elements.pop()
y = elements.pop()
pos = float(x) + float(y) * 1j
if not absolute:
pos += current_pos
segments.append(path.Line(current_pos, pos))
current_pos = pos

elif command == "H":
x = elements.pop()
pos = float(x) + current_pos.imag * 1j
if not absolute:
pos += current_pos.real
segments.append(path.Line(current_pos, pos))
current_pos = pos

elif command == "V":
y = elements.pop()
pos = current_pos.real + float(y) * 1j
if not absolute:
pos += current_pos.imag * 1j
segments.append(path.Line(current_pos, pos))
current_pos = pos

elif command == "C":
control1 = float(elements.pop()) + float(elements.pop()) * 1j
control2 = float(elements.pop()) + float(elements.pop()) * 1j
end = float(elements.pop()) + float(elements.pop()) * 1j

if not absolute:
control1 += current_pos
control2 += current_pos
end += current_pos

segments.append(path.CubicBezier(current_pos, control1, control2, end))
current_pos = end

elif command == "S":
# Smooth curve. First control point is the "reflection" of
# the second control point in the previous path.

if last_command not in "CS":
# If there is no previous command or if the previous command
# was not an C, c, S or s, assume the first control point is
# coincident with the current point.
control1 = current_pos
else:
# The first control point is assumed to be the reflection of
# the second control point on the previous command relative
# to the current point.
control1 = current_pos + current_pos - segments[-1].control2

control2 = float(elements.pop()) + float(elements.pop()) * 1j
end = float(elements.pop()) + float(elements.pop()) * 1j

if not absolute:
control2 += current_pos
end += current_pos

segments.append(path.CubicBezier(current_pos, control1, control2, end))
current_pos = end

elif command == "Q":
control = float(elements.pop()) + float(elements.pop()) * 1j
end = float(elements.pop()) + float(elements.pop()) * 1j

if not absolute:
control += current_pos
end += current_pos

segments.append(path.QuadraticBezier(current_pos, control, end))
current_pos = end

elif command == "T":
# Smooth curve. Control point is the "reflection" of
# the second control point in the previous path.

if last_command not in "QT":
# If there is no previous command or if the previous command
# was not an Q, q, T or t, assume the first control point is
# coincident with the current point.
control = current_pos
else:
# The control point is assumed to be the reflection of
# the control point on the previous command relative
# to the current point.
control = current_pos + current_pos - segments[-1].control

end = float(elements.pop()) + float(elements.pop()) * 1j

if not absolute:
end += current_pos

segments.append(path.QuadraticBezier(current_pos, control, end))
current_pos = end

elif command == "A":
radius = float(elements.pop()) + float(elements.pop()) * 1j
rotation = float(elements.pop())
arc = float(elements.pop())
sweep = float(elements.pop())
end = float(elements.pop()) + float(elements.pop()) * 1j

if not absolute:
end += current_pos

segments.append(path.Arc(current_pos, radius, rotation, arc, sweep, end))
current_pos = end

return segments
Loading