Glowforge still doesn't support the even-odd fill rule in SVG files as of January 2020

I suppose it’s time to file my annual bug report that the Glowforge software still does not support “fill-rule: evenodd” in SVG files.

Simplest possible test case:

This SVG donut will render as a filled circle in the Glowforge UI (and will be engraved as a solid circle). The Glowforge ought to treat it exactly as the web browser does: a donut shape. (If your web browser doesn’t show a blue donut here then you need to upgrade your browser.)

even-odd donut

This PDF conversion of the file displays correctly in the Glowforge user interface because this is one of several places where the Glowforge works better with PDF files than with SVG files.

donut.pdf (5.5 KB)

Please do not “fix” this by breaking the PDF rendering to match the buggy SVG behavior. Please instead fix the Glowforge SVG support to make “fill-rule: evenodd” work correctly.

5 Likes

Trying to understand (not doubting the issue) - but I use this fairly often to engrave specific shapes.

Here’s one I did just this weekend to create a patch for a print that was damaged. It engraved between the inner and outer black shapes only, the center was left untouched. The blue line was a subsequent cut path.

image

It seems similar to your ‘donut’, where even/odd rules would apply.

I just created a circle within a circle, and it engraved just the area between them, leaving the center untouched as well. I agree it appears to be completely filled in the UI:
image
image

… but the result is as expected.

I’ve done more complex parts where more nested shapes are inside of each other, and it alternates across object path boundaries exactly as I would expect. Thinking about it, it’s the same as how text engraving works as well.

So I guess I don’t understand what’s different between what I’m used to working with, and the shape you have.

Edit - I just realized - are you not converting the object to paths?

1 Like

Every major vector graphics system (Postscript, PDF, SVG, etc.) supports two different rules for determining which points are part of an enclosed shape. One is called the even-odd rule and the other is the nonzero rule.

The main difference is that the even-odd rule doesn’t care about the direction of the paths. It just checks to see whether a point would have to cross the path an even or odd number of times to reach the “outside” of the shape. If the number is odd, then the point is inside the shape, but if the answer is even, the point is not part of the shape.

The nonzero rule, on the other hand, does care about the direction of paths. When deciding whether or not a point is part of the shape it computes the winding number around the point. If it’s nonzero, then the point is part of the shape. See this wikipedia entry for a thorough explanation of how winding number is defined, but the simple version is to look at each of the edges you have to cross to get from a point to the outside of the shape. Add +1 if an edge is going clockwise and -1 if it’s going counter-clockwise. If the total is zero when you reach the outside of the shape, then the point is not part of the filled area. If the total is nonzero then the point is part of the filled area.

The blue donut I uploaded uses the even-odd rule, but the GFUI interprets the SVG version using the nonzero rule, so it doesn’t engrave correctly. (The PDF version of the file works just fine, as Glowforge does support both fill rules in PDF files.)

Take a look at this example (borrowed from Wikipedia):

The filled shape on the left is the result of filling it using the even-odd rule. On the right is the result of filling it with the nonzero rule.

Both rules are commonly used and are supported by any serious vector graphics system. Glowforge does seem to support both rules in PDF files but for some reason they don’t support the even-odd rule in SVG files and instead always use the nonzero rule. This causes any shape that’s meant to look like what’s on the left to end up with extra filled areas, as shown on the right. Look at that area in the middle that is different between the two. Following the arrow you’ll see that it crosses two edges to reach the outside of the shape. Using the even-odd rule, it is not part of the filled area because 2 is even. Using the nonzero rule, it crosses two edges that are both going clockwise, so the total is +2 which is nonzero, so it is part of the filled area. The two different rules yield two different results.

This bug was reported to Glowforge back in 2017 but it has yet to be fixed. (It used to be a bit worse, in that they would correctly show shapes using the even-odd rule on screen in the GFUI, but then engrave them using the nonzero rule. At some point they “fixed” it by changing the display to match what will be engraved, but it’s still not proper fix.)

2 Likes

Thanks. What you describe makes sense and I have seen the overlapping text path cause the issue shown. I’ve always addressed that using path/union.

What I still don’t understand is how you define two concentric circles (or any embedded objects) as using one rule or another.

In SVG:

<path d="M372.994,78.621C534.775,78.621 665.924,209.77
665.924,371.551C665.924,533.332 534.775,664.48
372.994,664.48C211.214,664.48 80.065,533.331 80.065,371.551C80.065,209.77
211.214,78.621 372.994,78.621ZM372.994,518.015C292.104,518.015
226.53,452.441 226.53,371.551C226.53,290.66 292.104,225.086
372.994,225.086C453.885,225.086 519.459,290.66
519.459,371.551C519.459,452.44 453.885,518.015 372.994,518.015Z"
style="fill:#34a5ff; fill-rule:evenodd;"/>

See that “fill-rule:evenodd” at the end? That means “fill this path using the even-odd rule”. You can probably guess what happens if it says “fill-rule:nonzero:slight_smile:

A good explanation of fill rules in SVG:


In Affinity Designer one can set the fill rule for a particular path using the Layer→Fill Mode menu items:


In Inkscape you use these two buttons in the Fill and Stroke panel:


I haven’t used Illustrator in many, many years so I don’t remember where Adobe put that option. But I’m sure it must have it.

2 Likes

Interesting. It was the fill mode options I had never seen. Something else to learn, yay. :smiley:

It looks like the Glowforge SVG parser isn’t handling fill-rule style inheritance correctly. With the sample dounut SVG, moving the fill-rule style attribute out of the SVG tag and down to the path tag makes it work just fine in the Glowforge UI and engraving.

Your original which demonstrates the problem (note the fill-rule style is global in the SVG tag):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 960 960" version="1.1" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
    <g transform="matrix(1.33333,0,0,1.33333,0,0)">
        <path d="M372.994,78.621C534.775,78.621 665.924,209.77 665.924,371.551C665.924,533.332 534.775,664.48 372.994,664.48C211.214,664.48 80.065,533.331 80.065,371.551C80.065,209.77 211.214,78.621 372.994,78.621ZM372.994,518.015C292.104,518.015 226.53,452.441 226.53,371.551C226.53,290.66 292.104,225.086 372.994,225.086C453.885,225.086 519.459,290.66 519.459,371.551C519.459,452.44 453.885,518.015 372.994,518.015Z" style="fill:#34a5ff;"/>
    </g>
</svg>

The modified file which works (evenodd fill-rule style applied to path only):

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 960 960" version="1.1" xml:space="preserve" style="clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
    <g transform="matrix(1.33333,0,0,1.33333,0,0)">
        <path style="fill-rule:evenodd" d="M372.994,78.621C534.775,78.621 665.924,209.77 665.924,371.551C665.924,533.332 534.775,664.48 372.994,664.48C211.214,664.48 80.065,533.331 80.065,371.551C80.065,209.77 211.214,78.621 372.994,78.621ZM372.994,518.015C292.104,518.015 226.53,452.441 226.53,371.551C226.53,290.66 292.104,225.086 372.994,225.086C453.885,225.086 519.459,290.66 519.459,371.551C519.459,452.44 453.885,518.015 372.994,518.015Z" style="fill:#34a5ff;"/>
    </g>

Weirdly, leaving the “evenodd” fill-rule style in the SVG tag and “overriding” it in the path doesn’t work:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 960 960" version="1.1" xml:space="preserve" style="style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
    <g transform="matrix(1.33333,0,0,1.33333,0,0)">
        <path style="fill-rule:evenodd" d=…

But changing the fill-rule style in the SVG tag to “non-zero” and overriding it back to “evenodd” in the path tag does work:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:serif="http://www.serif.com/" width="100%" height="100%" viewBox="0 0 960 960" version="1.1" xml:space="preserve" style="fill-rule:nonzero;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
    <g transform="matrix(1.33333,0,0,1.33333,0,0)">
        <path style="fill-rule:evenodd" d=...

It seems like having style=fillrule:evenodd in the SVG tag causes the parser to lock the fill-rule to nonzero for the rest of the file.

2 Likes

Oh, how interesting. That points to some serious problems with the CSS implementation.

That might be part of their previous “fix” to solve the problem where it used to display normally (honoring fill rules) in the GFUI but then engrave wrong by forcing everything to use the nonzero winding rule when rasterizing before sending to the Glowforge. This fixed the problem of not engraving what is displayed, but of course it didn’t fix the underlying problem of not supporting both fill rules.

Thank you for your feedback about this. I’ve passed it along to our team.

I’m going to close this thread. If you run into any other trouble please post a new topic here on the forum or email us at support@glowforge.com and we’ll be happy to help!