Interpolating colors

'''Interpolating two RGB colors.'''

size(1000, 500)

# 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

'''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

'''
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 masters

'''
Interpolate glyphs from two master fonts into a third font.

The script expects 3 fonts open:

- the current font, where the glyphs will be interpolated
- one master font named “Regular”
- another master 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 not glyphName in f1:
        print('%s not in %s, skipping…', (glyphName, f1))
        continue
    if not glyphName in f2:
        print('%s not in %s, skipping…' % (glyphName, f1))
        continue

    # if the glyph does not exist in the destination font, create it
    if not glyphName in f:
        f.newGlyph(glyphName)

    # interpolate glyph
    print('interpolating %s…' % glyphName)
    f[glyphName].interpolate(factor, f1[glyphName], f2[glyphName])

Interpolating fonts

'''Generate a new font by interpolating two master fonts.'''

# get fonts
font1 = OpenFont()
font2 = OpenFont()

# 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(font1.kerning, font2.kerning, factor)

# done!
font3.changed()

Batch interpolating fonts

'''
Generate a series of instances by interpolating two master 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()
font2 = OpenFont()
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("\tgenerating %s (%s)..." % (styleName, factor))

    # make a new font
    dst = NewFont()

    # interpolate the glyphs
    dst.interpolate(factor, font1, font2)

    # interpolate the kerning
    # comment this line out of you're just testing
    # dst.kerning.interpolate(font1.kerning, font2.kerning, value)

    # set font name
    dst.info.familyName = "MyFamily"
    dst.info.styleName = styleName
    dst.changed()

    # save instance
    fileName = '%s_%s.ufo' % (dst.info.familyName, dst.info.styleName)
    ufoPath = os.path.join(folder, fileName)
    print('\tsaving instances at %s...' % ufoPath)
    dst.save(ufoPath)

    # done with instance
    dst.close()
    print

print('...done.')
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

'''
Generate condensed glyphs from a Regular and a Bold.

Calculates a scaling factor from the Regular and Bold stem widths.

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()
Last edited on 28/01/2019