|
from fontTools.varLib.models import VariationModel, normalizeValue, piecewiseLinearMap |
|
|
|
|
|
def Location(loc): |
|
return tuple(sorted(loc.items())) |
|
|
|
|
|
class VariableScalar: |
|
"""A scalar with different values at different points in the designspace.""" |
|
|
|
def __init__(self, location_value={}): |
|
self.values = {} |
|
self.axes = {} |
|
for location, value in location_value.items(): |
|
self.add_value(location, value) |
|
|
|
def __repr__(self): |
|
items = [] |
|
for location, value in self.values.items(): |
|
loc = ",".join(["%s=%i" % (ax, loc) for ax, loc in location]) |
|
items.append("%s:%i" % (loc, value)) |
|
return "(" + (" ".join(items)) + ")" |
|
|
|
@property |
|
def does_vary(self): |
|
values = list(self.values.values()) |
|
return any(v != values[0] for v in values[1:]) |
|
|
|
@property |
|
def axes_dict(self): |
|
if not self.axes: |
|
raise ValueError( |
|
".axes must be defined on variable scalar before interpolating" |
|
) |
|
return {ax.axisTag: ax for ax in self.axes} |
|
|
|
def _normalized_location(self, location): |
|
location = self.fix_location(location) |
|
normalized_location = {} |
|
for axtag in location.keys(): |
|
if axtag not in self.axes_dict: |
|
raise ValueError("Unknown axis %s in %s" % (axtag, location)) |
|
axis = self.axes_dict[axtag] |
|
normalized_location[axtag] = normalizeValue( |
|
location[axtag], (axis.minValue, axis.defaultValue, axis.maxValue) |
|
) |
|
|
|
return Location(normalized_location) |
|
|
|
def fix_location(self, location): |
|
location = dict(location) |
|
for tag, axis in self.axes_dict.items(): |
|
if tag not in location: |
|
location[tag] = axis.defaultValue |
|
return location |
|
|
|
def add_value(self, location, value): |
|
if self.axes: |
|
location = self.fix_location(location) |
|
|
|
self.values[Location(location)] = value |
|
|
|
def fix_all_locations(self): |
|
self.values = { |
|
Location(self.fix_location(l)): v for l, v in self.values.items() |
|
} |
|
|
|
@property |
|
def default(self): |
|
self.fix_all_locations() |
|
key = Location({ax.axisTag: ax.defaultValue for ax in self.axes}) |
|
if key not in self.values: |
|
raise ValueError("Default value could not be found") |
|
|
|
return self.values[key] |
|
|
|
def value_at_location(self, location, model_cache=None, avar=None): |
|
loc = location |
|
if loc in self.values.keys(): |
|
return self.values[loc] |
|
values = list(self.values.values()) |
|
return self.model(model_cache, avar).interpolateFromMasters(loc, values) |
|
|
|
def model(self, model_cache=None, avar=None): |
|
if model_cache is not None: |
|
key = tuple(self.values.keys()) |
|
if key in model_cache: |
|
return model_cache[key] |
|
locations = [dict(self._normalized_location(k)) for k in self.values.keys()] |
|
if avar is not None: |
|
mapping = avar.segments |
|
locations = [ |
|
{ |
|
k: piecewiseLinearMap(v, mapping[k]) if k in mapping else v |
|
for k, v in location.items() |
|
} |
|
for location in locations |
|
] |
|
m = VariationModel(locations) |
|
if model_cache is not None: |
|
model_cache[key] = m |
|
return m |
|
|
|
def get_deltas_and_supports(self, model_cache=None, avar=None): |
|
values = list(self.values.values()) |
|
return self.model(model_cache, avar).getDeltasAndSupports(values) |
|
|
|
def add_to_variation_store(self, store_builder, model_cache=None, avar=None): |
|
deltas, supports = self.get_deltas_and_supports(model_cache, avar) |
|
store_builder.setSupports(supports) |
|
index = store_builder.storeDeltas(deltas) |
|
return int(self.default), index |
|
|