Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SVG paths have 15 decimal points of precision #1152

Closed
ronyeh opened this issue Sep 24, 2021 · 4 comments · Fixed by #1396
Closed

SVG paths have 15 decimal points of precision #1152

ronyeh opened this issue Sep 24, 2021 · 4 comments · Fixed by #1396
Labels

Comments

@ronyeh
Copy link
Collaborator

ronyeh commented Sep 24, 2021

While looking at #1150, I noticed that our SVG outputs 15 decimal points of precision!

For a simple VexFlow demo like this:

output

The output SVG looks like this:
SVG

The PNG is 3KB and the SVG is 8KB of text. And indeed, when modifying our visual diffs to output SVG paths & files instead of PNG files, I noticed the tests ran slower and the output folder took up much more disk space. That's probably mostly due to PNG compression vs uncompressed SVG text.... but it would certainly make for smaller SVG files if we rounded.

For SVG path diffing, we might consider rounding down to 3 or 4 decimal points of precision.

I found this educational thread over at the d3 project:
d3/d3-path#10 (comment)

d3/d3-path#10 (comment)

d3/d3-path#10

The discussion (from a while ago) suggests that maybe the Chrome debugging experience suffers with excessively long SVG path strings.

In any case, SVG file sizes would be a bit smaller :-). Maybe we can round for the purposes of doing SVG diffing, since some of the comments suggest that different architectures might have tiny floating point differences that affect string diffing:

"The snapshot was generated on Mac but the unit tests were ran on Linux systems, and the floating point error makes the entire snapshot testing fail."

Originally posted in #1150 (comment)

@tommadams
Copy link
Contributor

Wow, that d3 issue has been open for nearly five years.

Truncating to 3 decimal points of precision should be more than sufficient. As noted in the d3 issue, we should go with a custom rounding function rather than .toFixed to avoid padding with zeros.

Something like:

// Round to a maximum of 3 decimal places
function round3(x: number): string {
  return (Math.round(x*1000)/1000).toString();
}
for (const x of [Math.PI, 1.0, 0.5555, 1.5555]) {
  console.log(round3(-x), round3(x));
}

// Output:
-3.142 3.142
-1 1
-0.555 0.556
-1.555 1.556

@tommadams
Copy link
Contributor

Actually, I had to zoom right in to the pixels to spot a difference with only single decimal point of precision, so we could probably get away with only 2 digits of precision. 3 digits is definitely enough.

Full precision:
original

Single digit precision (the difference is most noticeable on the inside curve of the top half of the 3 in the time signature):
round1

Single digit precision diff:
diff1

2 digit precision:
round2

2 digit precision diff:
diff2

3 digit precision:
round3

3 digit precision diff:
diff3

Treble clef path at full precision:
M125 151M138.280256 160.199872C139.116608 160.199872,140.07968 160.12384,140.814656 159.971776C141.194816 159.895744,141.270848 159.8704,141.34688 160.301248C141.777728 162.759616,142.335296 165.927616,142.335296 167.651008C142.335296 173.04928,138.68576000000002 173.708224,136.53152 173.708224C134.554688 173.708224,133.61696 173.125312,133.61696 172.643776C133.61696 172.390336,133.94643200000002 172.28896,134.782784 172.010176C135.923264 171.680704,137.215808 170.717632,137.215808 168.588736C137.215808 166.58656,135.948608 164.863168,133.718336 164.863168C131.285312 164.863168,129.81536 166.814656,129.81536 169.070272C129.81536 171.427264,131.234624 175.026112,136.759616 175.026112C139.19264 175.026112,143.931968 173.910976,143.931968 167.72704C143.931968 165.623488,143.273024 162.176704,142.892864 159.895744C142.816832 159.464896,142.842176 159.515584,143.349056 159.287488C147.04928 157.817536,149.482304 154.725568,149.482304 150.594496C149.482304 145.9312,146.060864 141.800128,140.687936 141.800128C139.750208 141.800128,139.750208 141.800128,139.623488 141.141184L138.711104 135.844288C138.660416 135.41344,138.711104 135.388096,138.9392 135.16C142.892864 131.48512,145.883456 126.847168,145.883456 121.246144C145.883456 118.078144,144.996416 114.93548799999999,143.50112000000001 112.75590399999999C142.943552 111.944896,142.005824 110.931136,141.60032 110.931136C141.09344 110.931136,139.95296 111.868864,139.243328 112.67987199999999C136.53152 115.670464,135.64448 120.232384,135.64448 124.033984C135.64448 126.137536,135.923264 128.519872,136.176704 130.015168C136.252736 130.446016,136.27808 130.52204799999998,135.847232 130.902208C130.57568 135.236032,125 140.456896,125 147.832C125 154.168,129.333824 160.199872,138.280256 160.199872M127.91456 150.265024C127.91456 148.110784,128.29472 145.221568,131.310656 141.800128C133.515584 139.367104,135.188288 137.998528,136.886336 136.629952C137.266496 136.325824,137.34252800000002 136.376512,137.41856 136.756672L138.17888 141.445312C138.280256 142.1296,138.280256 142.104256,137.621312 142.307008C134.427968 143.3968,132.324416 146.286016,132.324416 149.403328C132.324416 152.672704,134.047808 155.004352,136.53152 155.866048C136.835648 155.967424,137.266496 156.0688,137.519936 156.0688C137.79872 156.0688,137.950784 155.891392,137.950784 155.663296C137.950784 155.409856,137.672 155.30848,137.41856 155.207104C135.872576 154.54816,134.782784 152.976832,134.782784 151.304128C134.782784 149.200576,136.202048 147.654592,138.43232 147.020992C139.015232 146.868928,139.091264 146.919616,139.167296 147.32512L140.992064 158.197696C141.068096 158.6032,141.017408 158.6032,140.485184 158.704576C139.902272 158.805952,139.167296 158.881984,138.43232 158.881984C132.045632 158.881984,127.91456 155.333824,127.91456 150.265024M142.157888 116.582848C143.349056 116.582848,144.337472 117.571264,144.337472 119.57344C144.337472 123.62848,140.865344 126.9232,138.001472 129.432256C137.748032 129.660352,137.595968 129.609664,137.519936 129.128128C137.367872 128.1904,137.29184 126.948544,137.29184 125.78272C137.29184 120.08032,139.927616 116.582848,142.157888 116.582848M141.09344 146.742208C144.058688 146.995648,146.491712 149.47935999999999,146.491712 152.672704C146.491712 154.979008,145.097792 156.82912,143.070272 157.868224C142.639424 158.070976,142.563392 158.070976,142.48736 157.640128L140.687936 147.249088C140.611904 146.792896,140.662592 146.69152,141.09344 146.742208

Treble clef path at 2 digit precision:
M125 151M138.28 160.2C139.12 160.2,140.08 160.12,140.81 159.97C141.19 159.9,141.27 159.87,141.35 160.3C141.78 162.76,142.34 165.93,142.34 167.65C142.34 173.05,138.69 173.71,136.53 173.71C134.55 173.71,133.62 173.13,133.62 172.64C133.62 172.39,133.95 172.29,134.78 172.01C135.92 171.68,137.22 170.72,137.22 168.59C137.22 166.59,135.95 164.86,133.72 164.86C131.29 164.86,129.82 166.81,129.82 169.07C129.82 171.43,131.23 175.03,136.76 175.03C139.19 175.03,143.93 173.91,143.93 167.73C143.93 165.62,143.27 162.18,142.89 159.9C142.82 159.46,142.84 159.52,143.35 159.29C147.05 157.82,149.48 154.73,149.48 150.59C149.48 145.93,146.06 141.8,140.69 141.8C139.75 141.8,139.75 141.8,139.62 141.14L138.71 135.84C138.66 135.41,138.71 135.39,138.94 135.16C142.89 131.49,145.88 126.85,145.88 121.25C145.88 118.08,145 114.94,143.5 112.76C142.94 111.94,142.01 110.93,141.6 110.93C141.09 110.93,139.95 111.87,139.24 112.68C136.53 115.67,135.64 120.23,135.64 124.03C135.64 126.14,135.92 128.52,136.18 130.02C136.25 130.45,136.28 130.52,135.85 130.9C130.58 135.24,125 140.46,125 147.83C125 154.17,129.33 160.2,138.28 160.2M127.91 150.27C127.91 148.11,128.29 145.22,131.31 141.8C133.52 139.37,135.19 138,136.89 136.63C137.27 136.33,137.34 136.38,137.42 136.76L138.18 141.45C138.28 142.13,138.28 142.1,137.62 142.31C134.43 143.4,132.32 146.29,132.32 149.4C132.32 152.67,134.05 155,136.53 155.87C136.84 155.97,137.27 156.07,137.52 156.07C137.8 156.07,137.95 155.89,137.95 155.66C137.95 155.41,137.67 155.31,137.42 155.21C135.87 154.55,134.78 152.98,134.78 151.3C134.78 149.2,136.2 147.65,138.43 147.02C139.02 146.87,139.09 146.92,139.17 147.33L140.99 158.2C141.07 158.6,141.02 158.6,140.49 158.7C139.9 158.81,139.17 158.88,138.43 158.88C132.05 158.88,127.91 155.33,127.91 150.27M142.16 116.58C143.35 116.58,144.34 117.57,144.34 119.57C144.34 123.63,140.87 126.92,138 129.43C137.75 129.66,137.6 129.61,137.52 129.13C137.37 128.19,137.29 126.95,137.29 125.78C137.29 120.08,139.93 116.58,142.16 116.58M141.09 146.74C144.06 147,146.49 149.48,146.49 152.67C146.49 154.98,145.1 156.83,143.07 157.87C142.64 158.07,142.56 158.07,142.49 157.64L140.69 147.25C140.61 146.79,140.66 146.69,141.09 146.74

Treble clef path at 3 digit precision:
M125 151M138.28 160.2C139.117 160.2,140.08 160.124,140.815 159.972C141.195 159.896,141.271 159.87,141.347 160.301C141.778 162.76,142.335 165.928,142.335 167.651C142.335 173.049,138.686 173.708,136.532 173.708C134.555 173.708,133.617 173.125,133.617 172.644C133.617 172.39,133.946 172.289,134.783 172.01C135.923 171.681,137.216 170.718,137.216 168.589C137.216 166.587,135.949 164.863,133.718 164.863C131.285 164.863,129.815 166.815,129.815 169.07C129.815 171.427,131.235 175.026,136.76 175.026C139.193 175.026,143.932 173.911,143.932 167.727C143.932 165.623,143.273 162.177,142.893 159.896C142.817 159.465,142.842 159.516,143.349 159.287C147.049 157.818,149.482 154.726,149.482 150.594C149.482 145.931,146.061 141.8,140.688 141.8C139.75 141.8,139.75 141.8,139.623 141.141L138.711 135.844C138.66 135.413,138.711 135.388,138.939 135.16C142.893 131.485,145.883 126.847,145.883 121.246C145.883 118.078,144.996 114.935,143.501 112.756C142.944 111.945,142.006 110.931,141.6 110.931C141.093 110.931,139.953 111.869,139.243 112.68C136.532 115.67,135.644 120.232,135.644 124.034C135.644 126.138,135.923 128.52,136.177 130.015C136.253 130.446,136.278 130.522,135.847 130.902C130.576 135.236,125 140.457,125 147.832C125 154.168,129.334 160.2,138.28 160.2M127.915 150.265C127.915 148.111,128.295 145.222,131.311 141.8C133.516 139.367,135.188 137.999,136.886 136.63C137.266 136.326,137.343 136.377,137.419 136.757L138.179 141.445C138.28 142.13,138.28 142.104,137.621 142.307C134.428 143.397,132.324 146.286,132.324 149.403C132.324 152.673,134.048 155.004,136.532 155.866C136.836 155.967,137.266 156.069,137.52 156.069C137.799 156.069,137.951 155.891,137.951 155.663C137.951 155.41,137.672 155.308,137.419 155.207C135.873 154.548,134.783 152.977,134.783 151.304C134.783 149.201,136.202 147.655,138.432 147.021C139.015 146.869,139.091 146.92,139.167 147.325L140.992 158.198C141.068 158.603,141.017 158.603,140.485 158.705C139.902 158.806,139.167 158.882,138.432 158.882C132.046 158.882,127.915 155.334,127.915 150.265M142.158 116.583C143.349 116.583,144.337 117.571,144.337 119.573C144.337 123.628,140.865 126.923,138.001 129.432C137.748 129.66,137.596 129.61,137.52 129.128C137.368 128.19,137.292 126.949,137.292 125.783C137.292 120.08,139.928 116.583,142.158 116.583M141.093 146.742C144.059 146.996,146.492 149.479,146.492 152.673C146.492 154.979,145.098 156.829,143.07 157.868C142.639 158.071,142.563 158.071,142.487 157.64L140.688 147.249C140.612 146.793,140.663 146.692,141.093 146.742

@tommadams
Copy link
Contributor

It just occurred to me that I should have said "3 decimal places" not "3 digit precision". Sorry, I was working on a different kind of quantization all day today :)

@ronyeh
Copy link
Collaborator Author

ronyeh commented Sep 25, 2021

Cool. Thanks for looking into this!

Can we get away with no rounding at all? I'd have to investigate, but I have a branch that is exploring 1) updating the Bravura / Petaluma font files, 2) getting rid of the NOGLYPH glyphs, and 3) trying to do as little transformation of the SVG path strings as possible. (Ideally the SVG path that we extract from the OTF is shoved directly into our final SVG output.)

So far, I've extracted the updated fonts, and now use C for curves, instead of B. Looks OK so far. Baby steps!

The gClef path looks like this at the moment:

"m 541 598 c 550 625 539 615 541 616 c 824 1174 706 770 824 953 c 730 1509 824 1299 789 1423 c 655 1581 708 1541 671 1581 c 562 1512 635 1581 590 1544 c 420 1064 455 1394 420 1214 c 441 828 420 981 431 887 c 428 793 444 811 445 808 c 0 125 220 622 0 416 c 524 -363 0 -125 171 -363 c 624 -354 557 -363 595 -360 c 645 -367 639 -351 642 -350 c 684 -657 662 -464 684 -589 c 455 -896 684 -870 540 -896 c 340 -854 377 -896 340 -873 c 386 -829 340 -844 353 -840 c 482 -694 431 -816 482 -778 c 344 -547 482 -615 432 -547 c 190 -713 248 -547 190 -624 c 464 -948 190 -806 246 -948 c 747 -660 560 -948 747 -904 c 706 -351 747 -577 721 -441 c 724 -327 703 -334 704 -336 c 966 16 870 -269 966 -147 c 619 363 966 200 831 363 c 577 389 582 363 582 363 z m 677 1358 c 763 1240 724 1358 763 1319 c 513 851 763 1080 626 950 c 494 863 503 842 497 844 c 485 995 488 900 485 949 c 677 1358 485 1220 589 1358 z m 520 377 c 498 343 524 350 524 351 c 289 63 372 300 289 186 c 455 -192 289 -66 357 -158 c 494 -200 467 -196 484 -200 c 511 -184 505 -200 511 -193 c 490 -166 511 -174 500 -170 c 386 -12 429 -140 386 -78 c 530 157 386 71 442 132 c 559 145 553 163 556 161 l 631 -284 c 611 -304 634 -300 632 -300 c 530 -311 588 -308 559 -311 c 115 29 278 -311 115 -171 c 249 363 115 114 130 228 c 469 567 336 459 402 513 c 490 562 484 579 487 577 z m 619 148 c 635 168 616 166 618 170 c 848 -66 752 158 848 60 c 713 -271 848 -157 793 -230 c 690 -262 696 -279 693 -279 z"

A couple things:

  • Our smufl_fontgen.js always outputs lower case. I'll probably have to tweak this, since upper case and lower case commands are supposed to do different things.
  • OTF are designed on a 1000 x 1000 grid. The paths are all integers, and look so clean before we scale them!

Assuming that the glyphs within a single font (e.g., Bravura) are all correctly sized relative to each other, can we stick these raw SVG paths into the same SVG group, and then apply a single transform to scale them all down?

Of course, we'd have to handle things like grace notes where the glyphs are smaller, or superscript/subscript text, etc.

Anyways, in an ideal world I think I'd like our rendered SVG to have as many integers as possible. I have no idea how much performance impact there will be if we scale groups of paths. I know you tested out use / symbol. We could go that route eventually, but at first I'd stick with separate paths.

We talked about this a few weeks ago: #1127 (comment)

It might be time for me to finally investigate! I'll report back if I make any good progress.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants