diff options
Diffstat (limited to 'Lib/fontTools/varLib/plot.py')
-rw-r--r-- | Lib/fontTools/varLib/plot.py | 331 |
1 files changed, 201 insertions, 130 deletions
diff --git a/Lib/fontTools/varLib/plot.py b/Lib/fontTools/varLib/plot.py index 811559fa..e0a7ca50 100644 --- a/Lib/fontTools/varLib/plot.py +++ b/Lib/fontTools/varLib/plot.py @@ -13,155 +13,226 @@ log = logging.getLogger(__name__) def stops(support, count=10): - a,b,c = support + 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] + 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.], - 'o', - color=color, - **kwargs - ) - elif len(axes) == 2: - subplot.plot( - [loc.get(axes[0], 0)], - [loc.get(axes[1], 0)], - [1.], - 'o', - color=color, - **kwargs - ) - else: - raise AssertionError(len(axes)) + 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**.5) - rows = math.ceil(n / cols) + n = len(locations) + cols = math.ceil(n**0.5) + rows = math.ceil(n / cols) - if names is None: - names = [None] * len(locations) + if names is None: + names = [None] * len(locations) - model = VariationModel(locations) - names = [names[model.reverseMapping[i]] for i in range(len(names))] + 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") + 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.,+1.) - - Xs = support.get(axis, (-1.,0.,+1.)) - 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) + 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.,+1.) - pyplot.ylim(-1.,+1.) - - Xs = support.get(ax1, (-1.,0.,+1.)) - Ys = support.get(ax2, (-1.,0.,+1.)) - 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) + 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) + 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) - 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)] - locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args] - plotLocations(locs, fig) - - pyplot.show() - -if __name__ == '__main__': - import sys - sys.exit(main()) + 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()) |