Last active
July 19, 2019 16:58
-
-
Save madig/6ded5e68a06f6f0301de6b704eaba5cb to your computer and use it in GitHub Desktop.
designspace deskew
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#%% | |
import collections | |
import itertools | |
import fontTools.designspaceLib as designspaceLib | |
import matplotlib.pyplot as plt | |
import numpy as np | |
import scipy.spatial | |
def designspace_vertices(sources): | |
source_locations = [ | |
tuple(x for x in source.location.values()) for source in sources | |
] | |
n_axes = len(source_locations[0]) | |
vertices = sorted(scipy.spatial.ConvexHull(source_locations).vertices) | |
expected_vertices = 2 ** n_axes | |
if len(vertices) != expected_vertices: | |
raise ValueError( | |
f"Designspace has {n_axes} axes. Expected {expected_vertices} vertices, " | |
f"got {vertices}, cannot rectangularize." | |
) | |
return [source_locations[i] for i in vertices] | |
def project_coordinates(coords, projection_matrix): | |
coords_np = np.array([x for x in coords] + [1]) | |
coords_projected_homogenous_full = projection_matrix.dot(coords_np) | |
coords_projected_homogenous = coords_projected_homogenous_full[:-1] | |
coords_projected = ( | |
coords_projected_homogenous / coords_projected_homogenous_full[-1] | |
) | |
return coords_projected.round() | |
#%% | |
d = designspaceLib.DesignSpaceDocument.fromfile("Encode-Sans-skewed.designspace") | |
#%% | |
plt.xlabel(d.axes[0].name) | |
plt.ylabel(d.axes[1].name) | |
for source in d.sources: | |
plt.plot(*source.location.values(), "sy") | |
for instance in d.instances: | |
plt.plot(*instance.location.values(), ".k") | |
plt.show() | |
#%% | |
# https://math.stackexchange.com/questions/296794/finding-the-transform-matrix-from-4-projected-points-with-javascript/339033 | |
vertices_orig = sorted(designspace_vertices(d.sources)) | |
axis_bounds = [ | |
(axis.map_forward(axis.minimum), axis.map_forward(axis.maximum)) for axis in d.axes | |
] | |
vertices_new = sorted(itertools.product(*axis_bounds)) | |
coord_orig = [(*t, 1) for t in vertices_orig] | |
coord_orig_a = coord_orig[:-1] | |
coord_orig_b = coord_orig[-1] | |
coord_orig_a_m = np.array(coord_orig_a).transpose() | |
coord_orig_b_m = np.array(coord_orig_b).transpose() | |
A = coord_orig_a_m * np.linalg.solve(coord_orig_a_m, coord_orig_b_m) | |
coord_new = [(*t, 1) for t in vertices_new] | |
coord_new_a = coord_new[:-1] | |
coord_new_b = coord_new[-1] | |
coord_new_a_m = np.array(coord_new_a).transpose() | |
coord_new_b_m = np.array(coord_new_b).transpose() | |
B = coord_new_a_m * np.linalg.solve(coord_new_a_m, coord_new_b_m) | |
H = B.dot(np.linalg.inv(A)) | |
#%% | |
plt.xlabel(d.axes[0].name) | |
plt.ylabel(d.axes[1].name) | |
for source in d.sources: | |
coords = project_coordinates(source.location.values(), H) | |
plt.plot(*coords, "sy") | |
for instance in d.instances: | |
coords = project_coordinates(instance.location.values(), H) | |
plt.plot(*coords, ".k") | |
plt.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version='1.0' encoding='UTF-8'?> | |
<designspace format="4.0"> | |
<axes> | |
<axis tag="wght" name="Weight" minimum="100" maximum="900" default="100"> | |
<map input="100" output="34"/> | |
<map input="200" output="45"/> | |
<map input="300" output="62"/> | |
<map input="400" output="84"/> | |
<map input="500" output="111"/> | |
<map input="600" output="141"/> | |
<map input="700" output="173"/> | |
<map input="800" output="204"/> | |
<map input="900" output="232"/> | |
</axis> | |
<axis tag="wdth" name="Width" minimum="75" maximum="125" default="75"> | |
<map input="75" output="0"/> | |
<map input="87.5" output="250"/> | |
<map input="100" output="500"/> | |
<map input="112.5" output="750"/> | |
<map input="125" output="1000"/> | |
</axis> | |
</axes> | |
<sources> | |
<source filename="EncodeSans-ThinCondensed.ufo"> | |
<location> | |
<dimension name="Weight" xvalue="34"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</source> | |
<source filename="EncodeSans-CondensedBold.ufo"> | |
<location> | |
<dimension name="Weight" xvalue="193"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</source> | |
<source filename="EncodeSans-ThinExpanded.ufo"> | |
<location> | |
<dimension name="Weight" xvalue="34"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</source> | |
<source filename="EncodeSans-BoldExpanded.ufo"> | |
<location> | |
<dimension name="Weight" xvalue="232"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</source> | |
</sources> | |
<instances> | |
<instance stylename="Thin"> | |
<location> | |
<dimension name="Weight" xvalue="34"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraLight"> | |
<location> | |
<dimension name="Weight" xvalue="44"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="Light"> | |
<location> | |
<dimension name="Weight" xvalue="58"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="Regular"> | |
<location> | |
<dimension name="Weight" xvalue="76"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="Medium"> | |
<location> | |
<dimension name="Weight" xvalue="97"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="SemiBold"> | |
<location> | |
<dimension name="Weight" xvalue="121"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="Bold"> | |
<location> | |
<dimension name="Weight" xvalue="146"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraBold"> | |
<location> | |
<dimension name="Weight" xvalue="171"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="Black"> | |
<location> | |
<dimension name="Weight" xvalue="193"/> | |
<dimension name="Width" xvalue="0"/> | |
</location> | |
</instance> | |
<instance stylename="Thin"> | |
<location> | |
<dimension name="Weight" xvalue="34"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraLight"> | |
<location> | |
<dimension name="Weight" xvalue="44"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="Light"> | |
<location> | |
<dimension name="Weight" xvalue="59"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="Regular"> | |
<location> | |
<dimension name="Weight" xvalue="78"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="Medium"> | |
<location> | |
<dimension name="Weight" xvalue="101"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="SemiBold"> | |
<location> | |
<dimension name="Weight" xvalue="126"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="Bold"> | |
<location> | |
<dimension name="Weight" xvalue="153"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraBold"> | |
<location> | |
<dimension name="Weight" xvalue="180"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="Black"> | |
<location> | |
<dimension name="Weight" xvalue="203"/> | |
<dimension name="Width" xvalue="250"/> | |
</location> | |
</instance> | |
<instance stylename="Thin"> | |
<location> | |
<dimension name="Weight" xvalue="34"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraLight"> | |
<location> | |
<dimension name="Weight" xvalue="44"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="Light"> | |
<location> | |
<dimension name="Weight" xvalue="60"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="Regular"> | |
<location> | |
<dimension name="Weight" xvalue="80"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="Medium"> | |
<location> | |
<dimension name="Weight" xvalue="104"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="SemiBold"> | |
<location> | |
<dimension name="Weight" xvalue="131"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="Bold"> | |
<location> | |
<dimension name="Weight" xvalue="160"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraBold"> | |
<location> | |
<dimension name="Weight" xvalue="188"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="Black"> | |
<location> | |
<dimension name="Weight" xvalue="213"/> | |
<dimension name="Width" xvalue="500"/> | |
</location> | |
</instance> | |
<instance stylename="Thin"> | |
<location> | |
<dimension name="Weight" xvalue="34"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraLight"> | |
<location> | |
<dimension name="Weight" xvalue="45"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="Light"> | |
<location> | |
<dimension name="Weight" xvalue="61"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="Regular"> | |
<location> | |
<dimension name="Weight" xvalue="82"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="Medium"> | |
<location> | |
<dimension name="Weight" xvalue="108"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="SemiBold"> | |
<location> | |
<dimension name="Weight" xvalue="136"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="Bold"> | |
<location> | |
<dimension name="Weight" xvalue="167"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraBold"> | |
<location> | |
<dimension name="Weight" xvalue="196"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="Black"> | |
<location> | |
<dimension name="Weight" xvalue="222"/> | |
<dimension name="Width" xvalue="750"/> | |
</location> | |
</instance> | |
<instance stylename="Thin"> | |
<location> | |
<dimension name="Weight" xvalue="34"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraLight"> | |
<location> | |
<dimension name="Weight" xvalue="45"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
<instance stylename="Light"> | |
<location> | |
<dimension name="Weight" xvalue="62"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
<instance stylename="Regular"> | |
<location> | |
<dimension name="Weight" xvalue="84"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
<instance stylename="Medium"> | |
<location> | |
<dimension name="Weight" xvalue="111"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
<instance stylename="SemiBold"> | |
<location> | |
<dimension name="Weight" xvalue="141"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
<instance stylename="Bold"> | |
<location> | |
<dimension name="Weight" xvalue="173"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
<instance stylename="ExtraBold"> | |
<location> | |
<dimension name="Weight" xvalue="204"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
<instance stylename="Black"> | |
<location> | |
<dimension name="Weight" xvalue="232"/> | |
<dimension name="Width" xvalue="1000"/> | |
</location> | |
</instance> | |
</instances> | |
</designspace> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment