Skip to content

Support reification of rotate transformations - #109

Open
reznakt wants to merge 4 commits into
mainfrom
reify-rotate
Open

Support reification of rotate transformations#109
reznakt wants to merge 4 commits into
mainfrom
reify-rotate

Conversation

@reznakt

@reznakt reznakt commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Adds support for reifying rotate() transformations, converting shapes that cannot express a rotation into paths.

Behavior

  • reify() now applies rotate(), not just translate()/scale().
  • Exact on circle, line, polyline, polygon and path.
  • Exact on rect and ellipse at multiples of 90 degrees.
  • Pushed down through containers.
  • rect/ellipse at any other angle are converted to <path> in place.
  • text, image, nested viewports and percentage coordinates keep the transformation, as before.
  • Reifying a rotation requires the affected lengths to be convertible to user units and not be percentages, because a rotation mixes the x-axis and the y-axis.

Signatures

  • reify() returns Element instead of None — itself, or the replacement <path>.
  • Ignoring the return still works, except for a converted root element, which the caller has to rebind.
  • Element.replace_with(new) — new method, swaps an element for another in its parent.

Contracts that changed

  • BasicShape.to_path() returns a detached element (parent is None) carrying deep copies of its children. It previously returned one attached to a deep-copied clone of the whole document, with the children dropped.
  • Rect.to_path_data() emits a closed subpath, omits degenerate A0,0 arcs on square corners, and clamps rx/ry to half the width/height. Command counts and stroke joins differ from before.
  • swap_transforms() raises SvgTransformSwapError for a rotation and a non-uniform scale. It used to return a mathematically wrong pair: the identity holds only when the scale is isotropic or the rotation is a multiple of 180 degrees.
  • swap_transforms() now also handles rotation ↔ rotation about differing centres.
  • Reifiable includes Rotate.

No removals; the only source-level break is code that annotated reify() as returning None.

@codecov

codecov Bot commented Aug 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.82390% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.60%. Comparing base (9cca15b) to head (43028cb).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
svglab/entities.py 87.83% 4 Missing and 5 partials ⚠️
svglab/elements/elements.py 94.52% 1 Missing and 3 partials ⚠️

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #109      +/-   ##
==========================================
+ Coverage   91.27%   91.60%   +0.33%     
==========================================
  Files          32       32              
  Lines        3495     3623     +128     
  Branches      364      383      +19     
==========================================
+ Hits         3190     3319     +129     
+ Misses        199      195       -4     
- Partials      106      109       +3     
Files with missing lines Coverage Δ
svglab/attrparse/transform.py 93.00% <100.00%> (ø)
svglab/elements/traits.py 100.00% <100.00%> (ø)
svglab/models.py 94.11% <100.00%> (ø)
svglab/elements/elements.py 97.47% <94.52%> (-0.07%) ⬇️
svglab/entities.py 84.20% <87.83%> (+1.58%) ⬆️

... and 2 files with indirect coverage changes

@reznakt
reznakt force-pushed the reify-rotate branch 2 times, most recently from 147c03f to e0c0613 Compare August 21, 2026 13:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant