Source code for fontTools.varLib.plot

"""Visualize DesignSpaceDocument and resulting VariationModel."""

from fontTools.varLib.models import VariationModel, supportScalar
from fontTools.designspaceLib import DesignSpaceDocument
from matplotlib import pyplot
from mpl_toolkits.mplot3d import axes3d
from itertools import cycle
import math
import logging
import sys

log = logging.getLogger(__name__)


[docs] def stops(support, count=10): a, b, c = support return ( [a + (b - a) * i / count for i in range(count)] + [b + (c - b) * i / count for i in range(count)] + [c] )
def _plotLocationsDots(locations, axes, subplot, **kwargs): for loc, color in zip(locations, cycle(pyplot.cm.Set1.colors)): if len(axes) == 1: subplot.plot([loc.get(axes[0], 0)], [1.0], "o", color=color, **kwargs) elif len(axes) == 2: subplot.plot( [loc.get(axes[0], 0)], [loc.get(axes[1], 0)], [1.0], "o", color=color, **kwargs, ) else: raise AssertionError(len(axes))
[docs] def plotLocations(locations, fig, names=None, **kwargs): n = len(locations) cols = math.ceil(n**0.5) rows = math.ceil(n / cols) if names is None: names = [None] * len(locations) model = VariationModel(locations) names = [names[model.reverseMapping[i]] for i in range(len(names))] axes = sorted(locations[0].keys()) if len(axes) == 1: _plotLocations2D(model, axes[0], fig, cols, rows, names=names, **kwargs) elif len(axes) == 2: _plotLocations3D(model, axes, fig, cols, rows, names=names, **kwargs) else: raise ValueError("Only 1 or 2 axes are supported")
def _plotLocations2D(model, axis, fig, cols, rows, names, **kwargs): subplot = fig.add_subplot(111) for i, (support, color, name) in enumerate( zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names)) ): if name is not None: subplot.set_title(name) subplot.set_xlabel(axis) pyplot.xlim(-1.0, +1.0) Xs = support.get(axis, (-1.0, 0.0, +1.0)) X, Y = [], [] for x in stops(Xs): y = supportScalar({axis: x}, support) X.append(x) Y.append(y) subplot.plot(X, Y, color=color, **kwargs) _plotLocationsDots(model.locations, [axis], subplot) def _plotLocations3D(model, axes, fig, rows, cols, names, **kwargs): ax1, ax2 = axes axis3D = fig.add_subplot(111, projection="3d") for i, (support, color, name) in enumerate( zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names)) ): if name is not None: axis3D.set_title(name) axis3D.set_xlabel(ax1) axis3D.set_ylabel(ax2) pyplot.xlim(-1.0, +1.0) pyplot.ylim(-1.0, +1.0) Xs = support.get(ax1, (-1.0, 0.0, +1.0)) Ys = support.get(ax2, (-1.0, 0.0, +1.0)) for x in stops(Xs): X, Y, Z = [], [], [] for y in Ys: z = supportScalar({ax1: x, ax2: y}, support) X.append(x) Y.append(y) Z.append(z) axis3D.plot(X, Y, Z, color=color, **kwargs) for y in stops(Ys): X, Y, Z = [], [], [] for x in Xs: z = supportScalar({ax1: x, ax2: y}, support) X.append(x) Y.append(y) Z.append(z) axis3D.plot(X, Y, Z, color=color, **kwargs) _plotLocationsDots(model.locations, [ax1, ax2], axis3D)
[docs] def plotDocument(doc, fig, **kwargs): doc.normalize() locations = [s.location for s in doc.sources] names = [s.name for s in doc.sources] plotLocations(locations, fig, names, **kwargs)
def _plotModelFromMasters2D(model, masterValues, fig, **kwargs): assert len(model.axisOrder) == 1 axis = model.axisOrder[0] axis_min = min(loc.get(axis, 0) for loc in model.locations) axis_max = max(loc.get(axis, 0) for loc in model.locations) import numpy as np X = np.arange(axis_min, axis_max, (axis_max - axis_min) / 100) Y = [] for x in X: loc = {axis: x} v = model.interpolateFromMasters(loc, masterValues) Y.append(v) subplot = fig.add_subplot(111) subplot.plot(X, Y, "-", **kwargs) def _plotModelFromMasters3D(model, masterValues, fig, **kwargs): assert len(model.axisOrder) == 2 axis1, axis2 = model.axisOrder[0], model.axisOrder[1] axis1_min = min(loc.get(axis1, 0) for loc in model.locations) axis1_max = max(loc.get(axis1, 0) for loc in model.locations) axis2_min = min(loc.get(axis2, 0) for loc in model.locations) axis2_max = max(loc.get(axis2, 0) for loc in model.locations) import numpy as np X = np.arange(axis1_min, axis1_max, (axis1_max - axis1_min) / 100) Y = np.arange(axis2_min, axis2_max, (axis2_max - axis2_min) / 100) X, Y = np.meshgrid(X, Y) Z = [] for row_x, row_y in zip(X, Y): z_row = [] Z.append(z_row) for x, y in zip(row_x, row_y): loc = {axis1: x, axis2: y} v = model.interpolateFromMasters(loc, masterValues) z_row.append(v) Z = np.array(Z) axis3D = fig.add_subplot(111, projection="3d") axis3D.plot_surface(X, Y, Z, **kwargs)
[docs] def plotModelFromMasters(model, masterValues, fig, **kwargs): """Plot a variation model and set of master values corresponding to the locations to the model into a pyplot figure. Variation model must have axisOrder of size 1 or 2.""" if len(model.axisOrder) == 1: _plotModelFromMasters2D(model, masterValues, fig, **kwargs) elif len(model.axisOrder) == 2: _plotModelFromMasters3D(model, masterValues, fig, **kwargs) else: raise ValueError("Only 1 or 2 axes are supported")
[docs] def main(args=None): from fontTools import configLogger if args is None: args = sys.argv[1:] # configure the library logger (for >= WARNING) configLogger() # comment this out to enable debug messages from logger # log.setLevel(logging.DEBUG) if len(args) < 1: print("usage: fonttools varLib.plot source.designspace", file=sys.stderr) print(" or") print("usage: fonttools varLib.plot location1 location2 ...", file=sys.stderr) print(" or") print( "usage: fonttools varLib.plot location1=value1 location2=value2 ...", file=sys.stderr, ) sys.exit(1) fig = pyplot.figure() fig.set_tight_layout(True) if len(args) == 1 and args[0].endswith(".designspace"): doc = DesignSpaceDocument() doc.read(args[0]) plotDocument(doc, fig) else: axes = [chr(c) for c in range(ord("A"), ord("Z") + 1)] if "=" not in args[0]: locs = [dict(zip(axes, (float(v) for v in s.split(",")))) for s in args] plotLocations(locs, fig) else: locations = [] masterValues = [] for arg in args: loc, v = arg.split("=") locations.append(dict(zip(axes, (float(v) for v in loc.split(","))))) masterValues.append(float(v)) model = VariationModel(locations, axes[: len(locations[0])]) plotModelFromMasters(model, masterValues, fig) pyplot.show()
if __name__ == "__main__": import sys sys.exit(main())