Erik van Blokland 3646055ea2 initial import
git-svn-id: http://svn.robofab.com/trunk@1 b5fa9d6c-a76f-4ffd-b3cb-f825fc41095c
2008-01-07 17:40:34 +00:00

397 lines
16 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<xml>
<synopsis description="" keywords="" generate="0" name="RoboThon 2006" />
<h1>
Scripting for interpolation
</h1>
<img src="http://letterror.com/code/robofab/img/ondrawmodel_17.gif" width="140" height="96"/>
<img src="http://letterror.com/code/robofab/img/ondrawmodel_07.gif"/>
<h2>Interpolation</h2>
<p>
In this session we're going to look at scripting and interpolation. Building an interpolation system has 2 phases: <strong>preparing</strong> it and <strong>using</strong> it. Though this isn't necessarily a design / production difference. In the first phase you need to make sure all the data is set up properly, the compatible glyphs in each master, matching contours in each glyph, path directions, start point locations etc. This can be a lot of work, but RoboFab can assist by reporting the problems. The second phase, using the interpolation comes when everything works and rather than look for problems (there shouldn't be any) you just want to generate all the weights as fast as possible and get on with it proofing and other things.
</p>
<h2>Terminology</h2>
<p>
The glyphs (or whole fonts) that are used in an interpolation system are usually called <strong>masters</strong>. The <strong>interpolation factor</strong> is a number between 0 and 1. With factor = 0 the result will be identical to the first master (in this case glyph "A"), and as you can probably guess, with factor = 1 the result will match glyph "B". Factor =0.5 means the resulting shape will be exactly between the two masters. The factor can actually also be outside the 0, 1 range - in that case we speak of <strong>extrapolation</strong>. The particular change in a glyph when interpolating from one master to another is called an <strong>axis</strong>.
</p>
<h2>Why use RoboFab interpolation?</h2>
<p>
All the alternatives come with strings attached. FontLab's <strong>Blend</strong> tool forces points in contours which don't match. This is handy if you're looking for a quick fix, but it can more or less destroy your masters. Alternatively you can use the Multiple Master tools, but this requires all of your masters to be in the same file and drawing this way can be tricky. Since interpolation is a useful process in typedesign, and a good candidate for scripting we decided to include solid interpolation support in Robofab.
</p>
<h2>Interpolating glyphs</h2>
<p>
In the first example we're going to interpolate two glyphs in the same font. This can be useful in the design stage, for sketching or testing. The result is stored in a third glyph. Note: if you want to run this example in FontLab, open a new, empty font. Use the circle tool to draw one circle in "A", and again in "B". Make sure these are different enough to tell what's going on.
</p>
<pythonsource src="examples/interpolateGlyphs.py"/>
<p>
You see there are 3 glyphs involved: the two masters (<strong>"A"</strong> and <strong>"B"</strong>) and a new glyph which is used to store the results. In this case the new glyph is stored as <strong>"C"</strong>, but it can be helpful to give it a more descriptive name. This is not very PostScript-like, but chances are these glyphs won't make it to production anyway, so it's not a problem. Notice that <strong>interpolate()</strong> is a method of the <strong>Glyph</strong> object. The <strong>Font</strong> object has an interpolate method as well, more about that later. The <a href="http://letterror.com/code/robofab/howto/interpolate.html" class="reference" target="docs">obligatory link</a> to the relevant RoboFab documentation.
</p>
<p>
Here's the same script, but now it generates a range of interpolated glyphs, each with a better name which tells you which interpolation factor was used:
</p>
<pythonsource src="examples/interpolateSeries.py"/>
<python type="output"><![CDATA[interpolating result_0.000000
interpolating result_0.100000
interpolating result_0.200000
interpolating result_0.300000
interpolating result_0.400000
interpolating result_0.500000
interpolating result_0.600000
interpolating result_0.700000
interpolating result_0.800000
interpolating result_0.900000]]></python>
<img src="talks/glyphinterpol.gif" alt="" width="493" height="65" />
<p>
Here you see a range of resulting glyphs as they could be generated by the script above.
</p>
<h2>Rounding errors</h2>
<p>
When you interpolate in FontLab you need to take into account that the results will be rounded off. Coordinates can only consist of whole numbers, i,e, "101" or "102" but not "101.3290" There is a nice solution for working with floating point precision glyphs using RoboFab. Here's a brief introduction:</p>
<pythonsource src="examples/floatingPointGlyph.py"/>
<python type="output"><![CDATA[float glyph 285.07676 114.59806
float glyph 641.51202 285.66048
float glyph 452.009385 679.5407
float glyph 95.96647 508.47828
integer glyph 285 115
integer glyph 642 286
integer glyph 452 680
integer glyph 96 508]]></python>
<p>
Use <strong>font.insertGlyph(myOrphanFloatGlyph, as="someName")</strong> if you want to insert the glyph back into a real font. Inserting causes the glyph points to be rounded off again. In a similar way you can make a font object which does not refer to an open FontLab font. In such a font you can store intermediate floating point glyphs of interpolations.
</p>
<h2>Making it work</h2>
<p>
The following table shows the problems glyphs can have when interpolating. <strong>Compatible</strong> means that the data can interpolate. <strong>Functioning</strong> means that the result actually works as a glyph. You'll see there are several combinations where glyphs are compatible, but the interpolation is not functional.
</p>
<table>
<thead>
<tr>
<td>masters</td>
<td>result</td>
<td>fix</td>
</tr>
</thead>
<tr>
<td>
<img src="talks/compatibility-scheme_2_01.gif" alt="compatibility" width="226" height="89" />
</td>
<td>
Compatible and functioning. Same number of points, same direction, same start point location.
</td>
<td>-
</td>
</tr>
<tr>
<td><img src="talks/compatibility-scheme_2_02.gif" alt="unusual but compatible and functioning" width="226" height="90" />
</td>
<td>
Unusual, but compatible and functioning. The number of off-curve points differ, but these are assumed to be on top of the on-curve when missing. Note: this only works for segments with 2 off-curve points.
</td>
<td>-
</td>
</tr>
<tr>
<td><img src="talks/compatibility-scheme_2_03.gif" alt="compatible, functioning" width="226" height="90" />
</td>
<td>
Compatible and functioning. Same number of points, same direction, same start point location, same contour order.
</td>
<td>-
</td>
</tr>
<tr>
<td><img src="talks/compatibility-scheme_2_04.gif" alt="incompatible" width="226" height="90" />
</td>
<td>
Incompatible and not functioning: different number of points
</td>
<td>Edit the masters.
</td>
</tr>
<tr>
<td><img src="talks/compatibility-scheme_2_05.gif" alt="compatible, but not functioning" width="226" height="90" />
</td>
<td>
Compatible but not functioning: start point is in the wrong place.</td>
<td>apply <strong> c.autoStartSegment()</strong> on each contour, otherwise edit the masters.
</td>
</tr>
<tr>
<td><img src="talks/compatibility-scheme_2_06.gif" alt="incompatible and not functioning" width="226" height="90" />
</td>
<td>
Incompatible and not functioning: different number of contours
</td>
<td>
Edit the masters.
</td>
</tr>
<tr>
<td><img src="talks/compatibility-scheme_2_07.gif" alt="compatible but not functioning" width="226" height="90" />
</td>
<td>
Compatible but not functioning: one of the contours is turning in the wrong direction.
</td>
<td>apply <strong>glyph.correctDirection()</strong>, otherwise edit the masters.
</td>
</tr>
<tr>
<td><img src="talks/compatibility-scheme_2_08.gif" alt="compatible but not functioning" width="226" height="91" />
</td>
<td>
Compatible but not functioning. Contour order: the paths are in the wrong order.
</td>
<td>
apply <strong>glyph.autoContourOrder()</strong>, otherwise edit the masters.
</td>
</tr>
</table>
<p>
Here are some snippets which can help prepare your glyphs. Suppose your test font has two incompatible glyphs "A" and "B".
</p>
<python><![CDATA[# see if "A" and "B" can interpolate
from robofab.world import CurrentFont
f = CurrentFont()
a = f["a"]
print a.isCompatible(f["b"], False)]]></python>
<python type="output"><![CDATA[False
]]></python>
<p>
So, there's the answer in code, they can't interpolate. Suppose the glyphs were in fact compatible, the answer will read <strong>True</strong>. The <strong>isCompatible()</strong> method takes another parameter,
</p>
<python><![CDATA[# see if "A" and "B" can interpolate
# and find out what's wrong if you can
from robofab.world import CurrentFont
f = CurrentFont()
a = f["a"]
print a.isCompatible(f["b"], True)]]></python>
<python type="output"><![CDATA[(False, ["Fatal error: contour 1 in glyph A and glyph B
don't have the same number of segments."])]]></python>
<p>Apart from the stunted grammar, this will tell you more or less what's wrong with the two glyphs. Now you have something to fix. Another frequently found error is this:
</p>
<python type="output"><![CDATA[(False, ["Fatal error: glyph A and glyph B
don't have the same number of contours."])]]></python>
<p>
More subtle errors happen when one of the contours in one of the masters turns in the wrong direction, or the start point of the contour is in a different place. These won't trip incompatibility errors like ones above, you have to verify the results.
</p>
<p>
The following example shows methods which can help to make glyph more compatible. These methods use rules to arrange the starting point, the order of the contours and the direction of the contours. It is likely, but not garanteed, that other masters of your interpolation will respond the same way to these rules. For instance, <strong>autoStartSegment()</strong> moves the starting point of a contour to the most, bottom left point on the contour. If all your masters share the same structure, this will make sense. But if the masters are radically different on purpose, these rules won't produce the right results and you have to prepare the glyphs manually.
</p>
<pythonsource src="examples/prepareInterpolGlyph.py"/>
<br/>
<h2>Interpolating fonts</h2>
<p>
The following script interpolates two fonts and stores the results in a third. It also smoothly introduces a couple of simple user interface thingies: <strong>AskString</strong> and <strong>SelectFont.</strong> Have a look at the <a href="http://letterror.com/code/robofab/tools/dialogs.html" target="new" class="reference">how to page on the simple dialogs stuff</a>. <strong>AskString()</strong> presents a small dialogbox with a prompt and a text input box. It will return the value you typed in, or <strong>None</strong> if you didn't. Which kinda implies you need to check whether the input makes sense before continuing, but that's a different project. <strong>SelectFont()</strong> gives you simple dialog with a list of the currently open fonts. When you select a fontname, the object for that font is returned. If you don't select anything, <strong>None</strong> is returned.
</p>
<pythonsource src="examples/interpolateTwoFonts.py"/>
<p>
This script asks you to select 2 fonts, then it asks you for an interpolation factor. Then is creates a new font (in FontLab a new empty font window will pop up). Then the <strong>font.interpolate()</strong> method of this new font is called with the interpolation factor you entered before, and the two fonts you selected.
</p>
<h2>Interpolate Kerning</h2>
<p>
In the example above the kerning is interpolated seperately, the Kerning object has its own <strong>interpolate()</strong> method. When a kernpair exists in both masters, the resulting pair will be the expected interpolated value. If a pair is missing from one, the interpolation will assume it has value 0. This only works for flat, non-class kerning. Interpolating class based kerning with exceptions requires more attention. Some Robofab developers have this working though.
</p>
<h2>GlyphMath (aside 1)</h2>
<p>
If they're compatible, Robofab Glyph objects can be used in Python math expression: you can add, subtract, multiply and divide them like normal numbers or variables. The math is applied to the coordinates of each point in the glyph. The result of a glyphMath operation is a new glyph. You can then insert this glyph in a font, or use it for other math operations.
</p>
<table>
<thead>
<tr><td>GlyphMath</td><td>operation</td></tr>
</thead>
<tr><td><img src="talks/glyphmath_examples_01.gif" alt="addition" width="285" height="77" />
</td><td><strong>addition:</strong> the coordinates of each point are added</td>
</tr>
<tr><td><img src="talks/glyphmath_examples_02.gif" alt="subtraction" width="285" height="78" />
</td><td><strong>subtraction:</strong> the coordinates of each point are subtracted. Note that though the glyph looks unrecognisable, all points are still there. Literally the difference between the two glyphs.</td>
</tr>
<tr><td><img src="talks/glyphmath_examples_03.gif" alt="multiplication" width="285" height="78" />
</td><td><strong>multiplication:</strong> scaling the glyph up. When you multiply with a tuple like (1.3, 1.05) the first value is used to multiply the x coordinates, the second value is used for the y coordinates.</td>
</tr>
<tr><td><img src="talks/glyphmath_examples_04.gif" alt="division" width="285" height="78" />
</td><td><strong>division:</strong> scaling the glyph down. When you divide with a tuple like (30, 29) the first value is used to divide the x coordinates, the second value is used for the y coordinates.</td>
</tr>
<tr><td><img src="talks/glyphmath_examples_05.gif" alt="alltogether" width="285" height="193" />
</td><td>Combination of operations to make a real interpolation.</td>
</tr>
</table>
<pythonsource src="examples/glyphMathOperations.py"/>
<p>
You can use GlyphMath to create interpolation effects, transplant transformations from one glyph to another and superimpose several effects at once.
</p>
<h2>Superpolation (aside 2)</h2>
<p>
Shameless commercial: <a href="http:?//http://letterror.com/code/superpolator/index.html" class="reference" target="docs">Superpolator</a> is a tool for building complex interpolation systems. It's based on Robofab and doesn't really have a place in this presentation. It doesn't make complex interpolations easier, it makes them possible. But ask Erik afterwards.
</p>
<h2>Advanced Interpolation</h2>
<p>
Here are two more advanced examples of interpolation. The first script asks for two master fonts. Then it will generate a list of weights with predefined names and factors. After interpolating, it will close the result font and continue with the next weigt.
</p>
<pythonsource src="examples/interpolateFontSeries.py"/>
<p>
The next script is a bit tricky, but it can be useful tool in typedesign. Suppose you have a two interpolating masters of different weights. The script interpolates <strong>in horizontal direction</strong> with the heavy weight to increase the stem thickness of the glyph. Then it proceeds to horizontally scale the glyph in such a way that the increase of weight from the interpolation is reduced again. The effect of both operations is a condensed version of the original glyph, but with a comparable stem thickness as the original. If you measure the stems of both masters and enter the values in the script, it can calculate an exact match. Note that the success of this script depends on the quality of the interpolation, and how far you're pushing the limit. From a design point of view you might not even want the condensed to have the same stem thickness. This script won't produce ready-made condensed versions of your typeface, but it can be used to create a starting point for further editing.
</p>
<pythonsource src="examples/glyphCondensor.py"/>
<img src="talks/condensor.jpg" alt="condensor examples" width="552" height="90" />
<p>
</p>
</xml>