Skip to content

feat: add evenoddToNonzero to reorient subpaths for the nonzero rule - #101

Open
YevheniiKotyrlo wants to merge 3 commits into
nfroidure:mainfrom
YevheniiKotyrlo:feat/evenodd-to-nonzero
Open

YevheniiKotyrlo wants to merge 3 commits into
nfroidure:mainfrom
YevheniiKotyrlo:feat/evenodd-to-nonzero

Conversation

@YevheniiKotyrlo

@YevheniiKotyrlo YevheniiKotyrlo commented Oct 2, 2026 •

Copy link
Copy Markdown

Proposed changes

  • SVGPathData#evenoddToNonzero() (SVGPathDataTransformer.EVENODD_TO_NONZERO): reorients the subpaths of an evenodd path so that nonzero fills the same area — each nested subpath winds against its enclosure, outermost ones keep their direction. Moved from fix(core): keep the holes of evenodd paths svgicons2svgfont#182 at @armano2's request; fonts fill by nonzero (It seems that fill-rule="evenodd" does not works correctly svgicons2svgfont#62).
  • A subpath's enclosure is the smallest subpath holding more of its points inside than outside, a point on that subpath's own outline counting neither way, so a hole touching its outline still nests in it. Subpaths of equal area nest in the order they are drawn, so a subpath repeated point for point cancels out as evenodd paints it; the same shape drawn with other commands is not recognised as a repeat. Reversal goes through REVERSE_PATH, so a reoriented path comes back absolute with cubic curves; a path that needs nothing is returned as given. It assumes no subpath crosses itself or another.
  • src/tests/evenodd_to_nonzero.test.ts: 22 cases, including a subpath drawn twice and three times, holes touching each side of their outline, idempotency and an unmodified input. jest, eslint, tsc, prettier and commitlint pass; metapak -s fails identically on main on my Windows machine.

Code quality

  • I made some tests for my changes
  • I added my name in the
    contributors
    field of the package.json file. Beware to use the same format than for the author field
    for the entries so that you'll get a mention in the README.md with a link to your website.

License

To get your contribution merged, you must check the following.

  • I read the project license in the LICENSE file
  • I agree with publishing under this project license

A path filled with the evenodd rule leaves any region enclosed an even
number of times empty, whatever the directions of its subpaths. Font
outlines fill by the nonzero rule instead, where a hole stays empty only
when its subpath winds against the subpath enclosing it.

EVENODD_TO_NONZERO finds each subpath's tightest enclosing subpath and
reverses every subpath that winds the same way as its enclosure, so the
nonzero rule fills the same area. A path whose subpaths already
alternate is returned as given.
evenoddToNonzero nested a subpath only in a strictly larger one holding
most of its points by the crossing test, which counts a point on the
left or top edge of an outline as inside it and one on the right or
bottom edge as outside. A hole touching the right or bottom of its
outline kept its direction, a subpath touching another from the left
could land inside it, and a subpath drawn twice nested in neither copy.

A point on the other subpath's outline now counts neither way, and
subpaths of equal area nest in the order they are drawn, so the copies
of a subpath drawn twice cancel out as evenodd paints them.
A subpath cancels out only when it repeats another point for point:
from any of its points and in either direction, but drawn with the same
commands. The same shape drawn with other commands, such as a circle's
arcs starting elsewhere, flattens to a different outline and is not
recognised as a repeat.
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