svgpathtools icon indicating copy to clipboard operation
svgpathtools copied to clipboard

Arcs with rotation transforms are not properly interpreted

Open abey79 opened this issue 5 years ago • 1 comments

Setup

python 3.8.6 svgpathtools master (45dc873f82fba28ae905eb4262446cc54908a8a8)

Expected

Arc with rotation transform are interpreted correctly:

E.g.:

[...]
    <g>
        <g transform="translate(40,40)">
            <path style="fill:none;stroke:#666" d="M12 -12 A12 12 0 0 1 -12 -12"/>
        </g>
        <g transform="translate(80,40) rotate(36)">
            <path style="fill:none;stroke:#666" d="M12 -12 A12 12 0 0 1 -12 -12"/>
        </g>
    </g>
    <g transform="translate(40,80)">
        <path style="fill:none;stroke:#666" d="M12 -12 A12 12 0 0 1 -12 -12"/>
    </g>
    <g transform="translate(80,80)">
        <g transform="rotate(36)">
            <path style="fill:none;stroke:#666" d="M12 -12 A12 12 0 0 1 -12 -12"/>
        </g>
    </g>
[...]

Full test file: arc_with_rotate.svg.zip

Displayed by macOS: image

Actual

Arcs with a rotation transform (the ones on the right in this example) are deformed when loaded with svgpathtools.

import math

import matplotlib.pyplot as plt
import numpy as np
import svgpathtools as svg

doc = svg.Document("arc_with_rotate.svg")
paths = doc.paths()

quantization = 1

for elem in paths:
    step = int(math.ceil(elem.length() / quantization))
    coords = np.empty(step + 1, dtype=complex)
    coords[0] = elem.point(0)
    for i in range(step):
        coords[i + 1] = elem.point((i + 1) / step)

    plt.plot(np.real(coords), np.imag(coords), "-")

plt.axis("equal")
plt.gca().invert_yaxis()
plt.show()
image

abey79 avatar Nov 17 '20 09:11 abey79

This simple test case is fixed by the following modification:

--- a/svgpathtools/path.py
+++ b/svgpathtools/path.py
@@ -288,7 +288,7 @@ def transform(curve, tf):
     elif isinstance(curve, Arc):
         new_start = to_complex(tf.dot(to_point(curve.start)))
         new_end = to_complex(tf.dot(to_point(curve.end)))
-        new_radius = to_complex(tf.dot(to_vector(curve.radius)))
+        new_radius = curve.radius
         if tf[0][0] * tf[1][1] >= 0.0:
             new_sweep = curve.sweep
         else:

However, I am unsure if this induces other regressions.

abey79 avatar Nov 17 '20 12:11 abey79