Interpolation scripts ↩
- Interpolating colors
- Interpolating position and size
- Checking interpolation compatibility
- Interpolating glyphs in the same font
- Interpolating between two sources
- Interpolating fonts
- Batch interpolating fonts
- Condensomatic
Interpolating colors
This example shows how to interpolate two RGB colors by interpolating each R, G, B color value separately.
size(1000, 200)
# define two rgb colors
color1 = 0.5, 0.0, 0.3
color2 = 1.0, 0.6, 0.1
# total number of steps
steps = 12
# initial position
x, y = 0, 0
# calculate size for steps
w = width() / steps
h = height()
# iterate over the total amount of steps
for i in range(steps):
# calculate interpolation factor for this step
factor = i * 1.0 / (steps - 1)
# interpolate each rgb channel separately
r = color1[0] + factor * (color2[0] - color1[0])
g = color1[1] + factor * (color2[1] - color1[1])
b = color1[2] + factor * (color2[2] - color1[2])
# draw a rectangle with the interpolated color
fill(r, g, b)
stroke(r, g, b)
rect(x, y, w, h)
# increase x-position for the next step
x += w
Interpolating position and size
The next example shows the same approach used to interpolate position and size.
# define position and size for two rectangles
x1, y1 = 98, 88
w1, h1 = 480, 492
x2, y2 = 912, 794
w2, h2 = 20, 126
# total amount of steps
steps = 22
# define blend mode and color
blendMode('multiply')
fill(0, 0.3, 1, 0.2)
# iterate over the total amount of steps
for i in range(steps):
# calculate interpolation factor for this step
factor = i * 1.0 / (steps - 1)
# interpolate each rectangle attribute separately
x = x1 + factor * (x2 - x1)
y = y1 + factor * (y2 - y1)
w = w1 + factor * (w2 - w1)
h = h1 + factor * (h2 - h1)
# draw a rectangle with the calculated variables
rect(x, y, w, h)
Checking interpolation compatibility
Before interpolating two glyphs, we need to make sure that they are compatible. We can do that with code using a glyph’s isCompatible
method. This function returns two values:
- The first is a boolean indicating if the two glyphs are compatible.
- If the first value is
False
, the second will contain a report of the problems.
f = CurrentFont()
g = f['O']
print(g.isCompatible(f['o']))
(True, '')
print(g.isCompatible(f['n']))
(False, '[Fatal] Contour 0 contains a different number of segments.\n[Fatal] Contour 1 contains a different number of segments.\n[Warning] The glyphs do not contain components with exactly the same base glyphs.')
Interpolating glyphs in the same font
The script below shows how to generate a number of interpolation and extrapolation steps between two selected glyphs in the current font.
# settings
interpolationSteps = 5
extrapolateSteps = 2
# get the currentFont
f = CurrentFont()
if f is None:
# no font open
print("Oeps! There is not font open.")
else:
# get the selection
selection = f.selectedGlyphNames
# check if the selection contains only two glyphs
if len(selection) != 2:
print("Incompatible selection: two compatible glyphs are required.")
else:
# get the master glyphs
source1 = f[selection[0]]
source2 = f[selection[1]]
# check if they are compatible
if not source1.isCompatible(source2)[0]:
# the glyphs are not compatible
print("Incompatible masters: Glyph %s and %s are not compatible." % (source1.name, source2.name))
else:
# loop over the amount of required interpolations
nameSteps = 0
for i in range(-extrapolateSteps, interpolationSteps + extrapolateSteps + 1, 1):
# create a new name
name = "interpolation.%03i" % nameSteps
nameSteps += 1
# create the glyph if does not exist
dest = f.newGlyph(name)
# get the interpolation factor (a value between 0.0 and 1.0)
factor = i / float(interpolationSteps)
# interpolate between the two masters with the factor
dest.interpolate(factor, source1, source2)
# done!
f.changed()
Interpolating between two sources
This example shows how to interpolate glyphs from two source fonts into a third font.
The script expects 3 fonts open:
- the current font, where the glyphs will be interpolated
- one source font named “Regular”
- another source font named “Bold”
# the font where the interpolated glyphs will be stored
f = CurrentFont()
# the two master fonts
f1 = AllFonts().getFontsByStyleName('Regular')[0]
f2 = AllFonts().getFontsByStyleName('Bold')[0]
# the interpolation factor
factor = 0.4
# a list of glyph names to be interpolated
glyphNames = ['A', 'B', 'C', 'a', 'b', 'c']
# iterate over the glyph names
for glyphName in glyphNames:
# if this glyph is not available in one of the masters, skip it
if glyphName not in f1:
print(f'{glyphName} not in {f1}, skipping…')
continue
if glyphName not in f2:
print(f'{glyphName} not in {f2}, skipping…')
continue
# if the glyph does not exist in the destination font, create it
if glyphName not in f:
f.newGlyph(glyphName)
# interpolate glyph
print(f'interpolating {glyphName}…')
f[glyphName].interpolate(factor, f1[glyphName], f2[glyphName])
Interpolating fonts
Generate a new font by interpolating two source fonts.
# get fonts
font1 = OpenFont('path/to/font1.ufo')
font2 = OpenFont('path/to/font2.ufo')
# define interpolation factor
factor = 0.5
# make destination font
font3 = NewFont()
# this interpolates the glyphs
font3.interpolate(factor, font1, font2)
# this interpolates the kerning
# comment this line out of you're just testing
font3.kerning.interpolate(factor, font1.kerning, font2.kerning)
# done!
font3.save('path/to/font3.ufo')
Batch interpolating fonts
The following example generates a set of instances by interpolating two source fonts.
The names and interpolation values of each instance are defined in a list of tuples.
import os
from vanilla.dialogs import getFolder
# get masters and destination folder
font1 = OpenFont('path/to/font1.ufo')
font2 = OpenFont('path/to/font2.ufo')
folder = getFolder("Select a folder to save the interpolations")[0]
# instance names and interpolation factors
instances = [
("Light", 0.25),
("Regular", 0.5),
("Bold", 0.75),
]
# generate instances
print('generating instances...\n')
for instance in instances:
styleName, factor = instance
print(f"\tgenerating {styleName} ({factor})...")
# make a new font
destFont = NewFont()
# interpolate the glyphs
destFont.interpolate(factor, font1, font2)
# set font name
destFont.info.familyName = "MyFamily"
destFont.info.styleName = styleName
destFont.changed()
# save instance
fileName = f'{destFont.info.familyName}_{destFont.info.styleName}.ufo'
ufoPath = os.path.join(folder, fileName)
print(f'\tsaving instances at {ufoPath}...')
destFont.save(ufoPath)
# done with instance
destFont.close()
print() # empty line in console
print('...done.\n')
generating instances...
generating Light (0.25)...
saving instances at /myFolder/MyFamily_Light.ufo...
generating Regular (0.5)...
saving instances at /myFolder/MyFamily_Regular.ufo...
generating Bold (0.75)...
saving instances at /myFolder/MyFamily_Bold.ufo...
...done.
Condensomatic
A handy script to generate a Condensed from a Regular and a Bold.
- calculates a scaling factor from stem widths of Regular and Bold
- uses interpolation and horizontal scaling to create condensed glyphs
# get Condensed, Regular and Bold fonts
condensedFont = CurrentFont()
regularFont = AllFonts().getFontsByStyleName('Roman')[0]
boldFont = AllFonts().getFontsByStyleName('Bold')[0]
# measure the Regular and Bold stem widths
regularStem = 95
boldStem = 140
# condensing factor
condenseFactor = 0.85
# calculate scale factor from stem widths
xFactor = float(regularStem) / (regularStem + condenseFactor * (boldStem - regularStem))
# interpolate selected glyphs
for glyphName in condensedFont.templateSelectedGlyphNames:
# get Condensed, Regular and Bold glyphs
condensedGlyph = condensedFont.newGlyph(glyphName, clear=True)
regularGlyph = regularFont[glyphName]
boldGlyph = boldFont[glyphName]
# interpolate Regular and Bold
condensedGlyph.clear()
condensedGlyph.interpolate((condenseFactor, 0), regularGlyph, boldGlyph)
# scale glyph horizontally
condensedGlyph.scaleBy((xFactor, 1))
# calculate glyph margins
condensedGlyph.leftMargin = (regularGlyph.leftMargin + boldGlyph.leftMargin) * 0.5 * (1.0 - condenseFactor)
condensedGlyph.rightMargin = (regularGlyph.rightMargin + boldGlyph.rightMargin) * 0.5 * (1.0 - condenseFactor)
# done!
condensedGlyph.changed()