• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Visualize DesignSpaceDocument and resulting VariationModel."""
2
3from __future__ import print_function, division, absolute_import
4from fontTools.misc.py23 import *
5from fontTools.varLib.models import VariationModel, supportScalar
6from fontTools.designspaceLib import DesignSpaceDocument
7from mpl_toolkits.mplot3d import axes3d
8from matplotlib import pyplot
9from itertools import cycle
10import math
11import logging
12import sys
13
14log = logging.getLogger(__name__)
15
16
17def stops(support, count=10):
18	a,b,c = support
19
20	return [a + (b - a) * i / count for i in range(count)] + \
21	       [b + (c - b) * i / count for i in range(count)] + \
22	       [c]
23
24
25def _plotLocationsDots(locations, axes, subplot, **kwargs):
26	for loc, color in zip(locations, cycle(pyplot.cm.Set1.colors)):
27		if len(axes) == 1:
28			subplot.plot(
29				[loc.get(axes[0], 0)],
30				[1.],
31				'o',
32				color=color,
33				**kwargs
34			)
35		elif len(axes) == 2:
36			subplot.plot(
37				[loc.get(axes[0], 0)],
38				[loc.get(axes[1], 0)],
39				[1.],
40				'o',
41				color=color,
42				**kwargs
43			)
44		else:
45			raise AssertionError(len(axes))
46
47
48def plotLocations(locations, fig, names=None, **kwargs):
49	n = len(locations)
50	cols = math.ceil(n**.5)
51	rows = math.ceil(n / cols)
52
53	if names is None:
54		names = [None] * len(locations)
55
56	model = VariationModel(locations)
57	names = [names[model.reverseMapping[i]] for i in range(len(names))]
58
59	axes = sorted(locations[0].keys())
60	if len(axes) == 1:
61		_plotLocations2D(
62			model, axes[0], fig, cols, rows, names=names, **kwargs
63		)
64	elif len(axes) == 2:
65		_plotLocations3D(
66			model, axes, fig, cols, rows, names=names, **kwargs
67		)
68	else:
69		raise ValueError("Only 1 or 2 axes are supported")
70
71
72def _plotLocations2D(model, axis, fig, cols, rows, names, **kwargs):
73	for i, (support, color, name) in enumerate(
74		zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))
75	):
76		subplot = fig.add_subplot(rows, cols, i + 1)
77		if name is not None:
78			subplot.set_title(name)
79		subplot.set_xlabel(axis)
80		pyplot.xlim(-1.,+1.)
81
82		Xs = support.get(axis, (-1.,0.,+1.))
83		X, Y = [], []
84		for x in stops(Xs):
85			y = supportScalar({axis:x}, support)
86			X.append(x)
87			Y.append(y)
88		subplot.plot(X, Y, color=color, **kwargs)
89
90		_plotLocationsDots(model.locations, [axis], subplot)
91
92
93def _plotLocations3D(model, axes, fig, rows, cols, names, **kwargs):
94	ax1, ax2 = axes
95
96	for i, (support, color, name) in enumerate(
97		zip(model.supports, cycle(pyplot.cm.Set1.colors), cycle(names))
98	):
99		axis3D = fig.add_subplot(rows, cols, i + 1, projection='3d')
100		if name is not None:
101			axis3D.set_title(name)
102		axis3D.set_xlabel(ax1)
103		axis3D.set_ylabel(ax2)
104		pyplot.xlim(-1.,+1.)
105		pyplot.ylim(-1.,+1.)
106
107		Xs = support.get(ax1, (-1.,0.,+1.))
108		Ys = support.get(ax2, (-1.,0.,+1.))
109		for x in stops(Xs):
110			X, Y, Z = [], [], []
111			for y in Ys:
112				z = supportScalar({ax1:x, ax2:y}, support)
113				X.append(x)
114				Y.append(y)
115				Z.append(z)
116			axis3D.plot(X, Y, Z, color=color, **kwargs)
117		for y in stops(Ys):
118			X, Y, Z = [], [], []
119			for x in Xs:
120				z = supportScalar({ax1:x, ax2:y}, support)
121				X.append(x)
122				Y.append(y)
123				Z.append(z)
124			axis3D.plot(X, Y, Z, color=color, **kwargs)
125
126		_plotLocationsDots(model.locations, [ax1, ax2], axis3D)
127
128
129def plotDocument(doc, fig, **kwargs):
130	doc.normalize()
131	locations = [s.location for s in doc.sources]
132	names = [s.name for s in doc.sources]
133	plotLocations(locations, fig, names, **kwargs)
134
135
136def main(args=None):
137	from fontTools import configLogger
138
139	if args is None:
140		args = sys.argv[1:]
141
142	# configure the library logger (for >= WARNING)
143	configLogger()
144	# comment this out to enable debug messages from logger
145	# log.setLevel(logging.DEBUG)
146
147	if len(args) < 1:
148		print("usage: fonttools varLib.plot source.designspace", file=sys.stderr)
149		print("  or")
150		print("usage: fonttools varLib.plot location1 location2 ...", file=sys.stderr)
151		sys.exit(1)
152
153	fig = pyplot.figure()
154	fig.set_tight_layout(True)
155
156	if len(args) == 1 and args[0].endswith('.designspace'):
157		doc = DesignSpaceDocument()
158		doc.read(args[0])
159		plotDocument(doc, fig)
160	else:
161		axes = [chr(c) for c in range(ord('A'), ord('Z')+1)]
162		locs = [dict(zip(axes, (float(v) for v in s.split(',')))) for s in args]
163		plotLocations(locs, fig)
164
165	pyplot.show()
166
167if __name__ == '__main__':
168	import sys
169	sys.exit(main())
170