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

different interpolation between Glyphs.app and MutatorMath #264

Closed
marekjez86 opened this issue Feb 27, 2017 · 36 comments
Closed

different interpolation between Glyphs.app and MutatorMath #264

marekjez86 opened this issue Feb 27, 2017 · 36 comments
Assignees
Labels

Comments

@marekjez86
Copy link
Contributor

marekjez86 commented Feb 27, 2017

The metrics for the fonts produced by the pipeline and by the glyphs.app are NOT the same. In most of the cases I examined the fonts produced by the pipeline are slightly "taller" or slightly "fatter". While the differences are not that big, they might cause a font designer to exclaim: "Pipeline is NOT producing the font I designed" :-)

The following was shared with me via e-mail (the fatter [green width: 803] is from pipeline and the thinner [red width: 789] is from the glyphs.app).

source used: https://github.com/googlei18n/noto-source/tree/master/src/NotoSansTamil
I'm not sure about the weight and width of the image below, but I've done my own research (please see below the following image).

unnamed

In my experiments I used notodiff (see https://github.com/googlei18n/nototools/tree/master/nototools ) with NotoSansTamil*.otf from https://github.com/googlei18n/noto-fonts/tree/master/alpha/from-glyphsapp/unhinted/otf/sans as --before fonts and NotoSansTamil*.otf from https://github.com/googlei18n/noto-fonts/tree/master/alpha/from-pipeline/unhinted/otf/sans as the --after font.

When generating images I set 3% (0.03) as the diff-threshhold value (i.e., ignored differences smaller than 3%).

Here are some of the difference samples (green indicates pipeline generated font, red the glyphs.app and black means common between the both fonts). I picked iitamil 0B88 for this, but there are many others:

iitamil
iitamil 1
iitamil 2
iitamil 3
iitamil 4
iitamil 5
iitamil 6
iitamil 7
iitamil 8
iitamil 9

I guess the above shape is boring :-)

Here's a difference with jaiivowelsigntamil
NotoSansTamil-CondensedBold
jaiivowelsigntamil
NotoSansTamil-ExtraCondensedBold
jaiivowelsigntamil 1
jaiivowelsigntamil 2
jaiivowelsigntamil 3
jaiivowelsigntamil 4
jaiivowelsigntamil 5
jaiivowelsigntamil 6
jaiivowelsigntamil 7
jaiivowelsigntamil 8
jaiivowelsigntamil 9
jaiivowelsigntamil 10
jaiivowelsigntamil 11

an example where height is changing:
bracketleft.tamil from NotoSansTamil-ExtraCondensedBold
bracketleft tamil

Here's the entire collection of significant differences from NotoSansTamil-ExtraCondensedBold

NotoSansTamil-ExtraCondensedBold.zip

Note when I ask for the differences above 3% I get them in the following fonts:
NotoSansTamil-CondensedBold
NotoSansTamil-CondensedExtraBold
NotoSansTamil-CondensedMedium
NotoSansTamil-CondensedSemiBold

NotoSansTamil-ExtraCondensedBold
NotoSansTamil-ExtraCondensedExtraBold
NotoSansTamil-ExtraCondensedMedium
NotoSansTamil-ExtraCondensedSemiBold

NotoSansTamil-SemiCondensedBold
NotoSansTamil-SemiCondensedSemiBold

Note when I ask for the differences above 0.03% I get them in the following fonts:
NotoSansTamil-CondensedBlack
NotoSansTamil-CondensedBold
NotoSansTamil-CondensedExtraBold
NotoSansTamil-CondensedExtraLight
NotoSansTamil-CondensedMedium
NotoSansTamil-CondensedSemiBold

NotoSansTamil-ExtraCondensedBold
NotoSansTamil-ExtraCondensedExtraBold
NotoSansTamil-ExtraCondensedLight
NotoSansTamil-ExtraCondensedMedium
NotoSansTamil-ExtraCondensedSemiBold

NotoSansTamil-SemiCondensedBold
NotoSansTamil-SemiCondensedSemiBold

@marekjez86
Copy link
Contributor Author

@behdad , @anthrotype : what do you think about the differences? is this something that we should learn to live with?

@JelleBosmaMT : FYI

@JelleBosmaMT
Copy link

This is definitely something that nobody should live with.

You will notice that the distortions are centered around the NotoSansTamil-ExtraCondensedBold. This has interpolation values weight 151 width 70 and is supposed to be a straight interpolation between the masters at 90-70 and 190-70.
What is special about weight 151-70? This design has 7 masters: 4 in the corners and two intermediates for width 100 at 90 and 151 and one intermediate for width 70 at 90.

The interpolation algorithm must have assumptions about the location of masters, that is now being tested by a real world example that doesn't match the assumption.
I only have been forwarded 2 screen shots with differences by our QA team, which is not enough to figure out what may be going wrong. But let me conclude by pointing out where we see distortions in issue #225: In the NotoSansSinhala-ExtraCondensedBold... another design with 7 masters.

@JelleBosmaMT
Copy link

With regards to the height change of the bracketleft.tamil: all masters have the same height except the narrow thin (26-70). But that should not have any effect on 151-70. Or has it?

@anthrotype anthrotype added the bug label Feb 28, 2017
@anthrotype
Copy link
Member

Thank you Marek and Jelle for the thorough bug report.

I've examined the sources and the outputted fonts from both Glyphs.app and fontmake (via glyphsLib and MutatorMath), and can confirm the above mentioned differences in the interpolated instances.

As Jelle pointed out, it seems like, when we generate instances with MutatorMath, the seventh "SemiBold" master (weight=151, width=100) placed in between the "Regular" (90, 100) and "Bold" masters (190, 100) distorts the interpolation for the condensed instances around weight=151, e.g. "ExtraCondensed Bold" (weight=151, width=70).

On the other hand, Glyphs.app seems to not be affected by such asymmetric arrangement of the masters (4 weights for the normal width, 3 weights for the condensed width).

Here's the output from notodiff for "jatamil" and "two" glyphs in ExtraCondensed Bold, the red outline is Glyphs.app's, the green is MutatorMath's:

notodiff_jatamil notodiff_2

I was also able to replicate this situation using the .designspace generated by glyphsLib (found inside "master_ufo" folder that fontmake produces) with Superpolator 3, which like fontmake uses MutatorMath under the hood. There one can quickly "mute" masters and see the results updated live and interactively.

One thing I noticed is that the "ExtraCondensed Bold" (151, 70) is interpolated exactly the same way in Glyphs.app whether or not that "SemiBold" (151, 100) master is defined. That is, if I delete the "SemiBold" master, in Glyphs.app I get the same output for the "ExtraCondensed Bold" instance.

Similarly, if I delete "SemiBold" (151, 100) master and interpolate "ExtraCondensed Bold" through MutatorMath, I get the same result as Glyphs.app.

This leads me to think that if one were to add another intermediate master between "Condensed" (90, 70) and "Condensed Bold" (190, 70) masters, then the outputs from Glyphs.app and MutatorMath would agree? I still haven't tried that.. @JelleBosmaMT do you think this could be viable workaround?

In conclusion, it's not clear to me whether this is a bug in MutatorMath, or rather a "feature".

I'd be curious to know what @schriftgestalt or @LettError have to say about these different interpolations between their respective editors.

To help debugging, I'm attaching below a small .glyphs file containing just a couple of outlines from NotoSansTamil, that reproduces the current issue when exported from Glyphs.app and fontmake:

https://www.dropbox.com/s/a8cv56ga2p8d1zl/test_interpolation.zip?dl=0

(There's a AWS outage affecting Github so I can't upload attachments, so I'm using my own Dropbox)

@anthrotype anthrotype changed the title The metrics for the fonts produced by the pipeline and by the glyphs.app are NOT the same different interpolation between Glyphs.app and MutatorMath Feb 28, 2017
@twardoch
Copy link
Contributor

twardoch commented Mar 1, 2017

Here’s what I "sort of know", or at least my understanding of how design spaces differ in MutatorMath and Glyphs:

Both Superpolator / MutatorMath and OpenType Variations iterate through each axis and determine the interpolated coordinates per each axis individually, step by step. This allows an arbitrary number of axes, but at a cost — predictable results are given if the masters are at "corresponding" locations, like you can see on the below image. In other words, in MutatorMath, the design space always needs to be divided into rectangular areas:

Glyphs is different: it throws all the masters from all the axes into the interpolation mix at the same time, so with each additional axis, the complexity of the equation grows by a degree. This allows greater flexibility in the arrangement of masters, but makes it much more difficult to add more axes. This is why Glyphs has the limit of 3 axes. In Glyphs, different equations are used depending on whether you have 1 axis, or 2 axes or 3.

My understanding is that in MutatorMath, and I think also in OT Variations, your designspace always needs to be divided into rectangular areas. In other words, in Glyphs, you can have a setup like this:

but in MutatorMath or OT Variations, you cannot.

@twardoch
Copy link
Contributor

twardoch commented Mar 1, 2017

Ps. The way I understand it, the current setup where fontmake just takes Glyphs masters and their locations on the design space, and then applies a completely different interpolation algorithm (MutatorMath) is "naive" and won’t ever guarantee full compatibility. I think in order to make arbitrary Glyphs design spaces work reliably in MutatorMath or OT Variations, in cases where irregular design spaces are involved, one would need to generate more masters to build a design space that only has non-rectangular areas.

Unfortunately, AFAIK, the Glyphs interpolation model isn’t public, so it’s hard to tell what it’s actually doing. :)

I’m too poor at geometry myself to be sure that what I’ve written above is correct, so please treat this as just potential hints. I think @LettError and @schriftgestalt are much more qualified to give more solid feedback.

@LettError
Copy link

LettError commented Mar 1, 2017 via email

@JelleBosmaMT
Copy link

JelleBosmaMT commented Mar 1, 2017

I have the hunch if I am going to work out in detail how this works, my boss would like to quote for it first. But I can solve it for my 7 masters.

All instances are within a rectangle. You can connect the masters by solid lines. So in my Tamil and Sinhala x = width, y is weight. One intermediate master left, two on the right.

interpol

All instances that are on a solid line are interpolated between the nearest masters. That is why the "missing master" at 151-70 has nothing to do with either 151-100, nor with 26-70 (or vice versa).
It also shows the relative ease to add a missing master. In fact, we can do this in under two seconds in GlyphsApp, as we only have to select the instance and select a "Add as master" button. However as we know from accessing instances trough Python, GlyphsApp handles them all as fractional values in the interpolations. Having a physical master adds rounding. So the output is slightly different and having additional rounding is bad: 1000 units per EM is crude as it is.

So for any instance that is within the rectangle, but not on a solid line, you need intermediate interpolation masters that are on a solid line. The additional challenge that is caused by the master without horizontal solid line, is to get an additional intermediate master at the same height, which is on a solid line.

@LettError
Copy link

LettError commented Mar 1, 2017 via email

@JelleBosmaMT
Copy link

JelleBosmaMT commented Mar 1, 2017 via email

@schriftgestalt
Copy link

Adam explained it quite accurately (only that it uses the same algorithms for all master setups and the algorithm actually supports an unlimited number of masters, Glyphs internally can use 5 (the settings are accessible by script). Only the UI is limiting it, still.

The .glyphs file contains the interpolation values (instance.instanceInterpolations) for each instance. So you could skip mutatormath and interpolate yourself.

@JelleBosmaMT
Copy link

Indeed it is all there. Almost too easy ;-)

@marekjez86
Copy link
Contributor Author

marekjez86 commented Mar 20, 2017

@anthrotype , @behdad , @brawer : whom could make this happen? I'd like us to make progress on this.

@anthrotype
Copy link
Member

I don't think we are planning to "skip MutatorMath" anytime soon, however "easy" this may seem to some of you.
What is relatively easier (i.e. wouldn't require changes to the code) would be to modify the .glyphs sources that have such non-rectangular interpolation space, and add intermediate masters to make it so. In theory, this could (should) be done automatically in glyphsLib, but I am a bit swamped at the moment.

@JelleBosmaMT
Copy link

JelleBosmaMT commented Mar 21, 2017 via email

@LettError
Copy link

Here are some pictures that show how VariationModel calculates the factors, and how to interpret a system with non-aligned masters. http://letterror.com/dev/mutmath_varmod/

@JelleBosmaMT
Copy link

I just realized, that if @anthrotype can use GlyphsLib to automatically add "missing masters", then GlyphsLib can generate ANY interpolated instance. Which would solve the problem too.

@justanotherfoundry
Copy link

This allows greater flexibility in the arrangement of masters, but makes it much more difficult to add more axes. This is why Glyphs has the limit of 3 axes.

Just a quick note, the 3-axis limitation of Glyphs is not a restriction of the algorithm that computes the coefficients. It can handle an unlimited number of axes and an unlimited number of masters.

@LettError
Copy link

@justanotherfoundry great, publish it.

@behdad
Copy link
Contributor

behdad commented Mar 25, 2018

Anyone who can reproduce the issue wants to try this patch?

diff --git a/Lib/fontTools/varLib/models.py b/Lib/fontTools/varLib/models.py
index 10825ac8..3d10921c 100644
--- a/Lib/fontTools/varLib/models.py
+++ b/Lib/fontTools/varLib/models.py
@@ -233,7 +233,10 @@ class VariationModel(object):
 				if not axis in loc:
 					continue
 				locV = loc[axis]
-				box[axis] = (self.lowerBound(locV, values), locV, self.upperBound(locV, values))
+				if locV > 0:
+					box[axis] = (0, locV, max({locV}|values))
+				else:
+					box[axis] = (min({locV}|values), locV, 0)
 
 			locAxes = set(loc.keys())
 			# Walk over previous masters now

@anthrotype
Copy link
Member

Anyone who can reproduce the issue wants to try this patch?

what is this patch supposed to do?
I tried applying it then rebuilt the variable font NotoSansTamil-VF.ttf with this command:

fontmake -g src/NotoSansTamil/NotoSansTamil-MM.glyphs --mti-source src/NotoSansTamil/NotoSansTamil-MM.plist -o variable

Diffing the fonts produced with and without the above patch shows no differences.

@behdad
Copy link
Contributor

behdad commented Mar 27, 2018

Thanks Cosimo. Sorry about that.

I fixed varLib for, I hope this issue, and the issues that @LettError pointed out at TYPO Labs 2017. Erik, care to give it another go? It should perform as you expect now.

behdad added a commit to fonttools/fonttools that referenced this issue Mar 27, 2018
@behdad
Copy link
Contributor

behdad commented Mar 27, 2018

Err. This:
fonttools/fonttools@42bef17

@anthrotype
Copy link
Member

Yay, it's working! 🎉
Above is NotoSansTamil-VF.ttf before the patch, below is with behdad's commit in fonttools master:
image

notice the bottom of the shoulder of number 2, it breaks in the top one whereas appears smooth in the bottom. Also the overall weight appears lighter for the bottom one (in fact it is slightly more compact).

This only fixes the variable font pipeline for asymmetric designspaces; for static fonts we'll need to figure out how to use Variation Model for instantiating UFOs.

@moyogo
Copy link
Collaborator

moyogo commented Mar 27, 2018

This only fixes the variable font pipeline for asymmetric designspaces; for static fonts we'll need to figure out how to use Variation Model for instantiating UFOs.

Why not generate static fonts from the variable font?

@anthrotype
Copy link
Member

well, for one thing, that currently only works for TTFs.

@anthrotype
Copy link
Member

also, the outlines are converted interpolatably for the varfont's masters, whereas for a single instance they could be converted slightly more efficiently if taken by itself. And we don't remove overlaps for interpolatable TTF masters that make up the varfont, whereas we do for the static TTFs.

@behdad
Copy link
Contributor

behdad commented Mar 28, 2018

Yeah we should import ufoLib, add math operations to our tiny UFO objects, and run varLib on them to interpolate before creating static fonts. Basically replace mutatorMath.

@behdad
Copy link
Contributor

behdad commented Mar 28, 2018

I made another, I hope, "improvement" to the model:

fonttools/fonttools@a523a69

There's one more addition coming, but then that would be about it.

@marekjez86
Copy link
Contributor Author

marekjez86 commented Mar 28, 2018 via email

@madig
Copy link
Collaborator

madig commented Aug 28, 2019

Can this be closed?

@marekjez86
Copy link
Contributor Author

Seems to be done.

@anthrotype
Copy link
Member

anthrotype commented Sep 28, 2022

@LettError Hey Eric, your http://letterror.com/dev/mutmath_varmod/ is no longer online, have you still got a copy, do you mind uploading it again somewhere? This difference b/w varLib and MutatorMath came up in discussions with a colleague and would be nice to read your handy summary. Thanks

EDIT: the Internet Archive Wayback Machine only has the text, no images: https://web.archive.org/web/20171014153346/https://letterror.com/dev/mutmath_varmod/

@LettError
Copy link

Page recreated at https://letterror.com/dev/mutmath_varmod/ with images from my archive and html from archive.org, as that was somehow newer than the version I had on disk. I did not edit the text, I did not study this specific current issue.

In 5.3 conclusions it says that VariationModel does not extrapolate. This was recently added by Behdad.

@anthrotype
Copy link
Member

@LettError thanks!

@belluzj
Copy link
Collaborator

belluzj commented Sep 28, 2022

Thanks a lot, that page is great, I was looking for it the other day and wondered where it was!

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

No branches or pull requests