123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- """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__)
- 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))
- 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)
- 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)
- 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")
- 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())
|