1*e1fe3e4aSElliott Hughesfrom fontTools.varLib.models import VariationModel, normalizeValue, piecewiseLinearMap 2*e1fe3e4aSElliott Hughes 3*e1fe3e4aSElliott Hughes 4*e1fe3e4aSElliott Hughesdef Location(loc): 5*e1fe3e4aSElliott Hughes return tuple(sorted(loc.items())) 6*e1fe3e4aSElliott Hughes 7*e1fe3e4aSElliott Hughes 8*e1fe3e4aSElliott Hughesclass VariableScalar: 9*e1fe3e4aSElliott Hughes """A scalar with different values at different points in the designspace.""" 10*e1fe3e4aSElliott Hughes 11*e1fe3e4aSElliott Hughes def __init__(self, location_value={}): 12*e1fe3e4aSElliott Hughes self.values = {} 13*e1fe3e4aSElliott Hughes self.axes = {} 14*e1fe3e4aSElliott Hughes for location, value in location_value.items(): 15*e1fe3e4aSElliott Hughes self.add_value(location, value) 16*e1fe3e4aSElliott Hughes 17*e1fe3e4aSElliott Hughes def __repr__(self): 18*e1fe3e4aSElliott Hughes items = [] 19*e1fe3e4aSElliott Hughes for location, value in self.values.items(): 20*e1fe3e4aSElliott Hughes loc = ",".join(["%s=%i" % (ax, loc) for ax, loc in location]) 21*e1fe3e4aSElliott Hughes items.append("%s:%i" % (loc, value)) 22*e1fe3e4aSElliott Hughes return "(" + (" ".join(items)) + ")" 23*e1fe3e4aSElliott Hughes 24*e1fe3e4aSElliott Hughes @property 25*e1fe3e4aSElliott Hughes def does_vary(self): 26*e1fe3e4aSElliott Hughes values = list(self.values.values()) 27*e1fe3e4aSElliott Hughes return any(v != values[0] for v in values[1:]) 28*e1fe3e4aSElliott Hughes 29*e1fe3e4aSElliott Hughes @property 30*e1fe3e4aSElliott Hughes def axes_dict(self): 31*e1fe3e4aSElliott Hughes if not self.axes: 32*e1fe3e4aSElliott Hughes raise ValueError( 33*e1fe3e4aSElliott Hughes ".axes must be defined on variable scalar before interpolating" 34*e1fe3e4aSElliott Hughes ) 35*e1fe3e4aSElliott Hughes return {ax.axisTag: ax for ax in self.axes} 36*e1fe3e4aSElliott Hughes 37*e1fe3e4aSElliott Hughes def _normalized_location(self, location): 38*e1fe3e4aSElliott Hughes location = self.fix_location(location) 39*e1fe3e4aSElliott Hughes normalized_location = {} 40*e1fe3e4aSElliott Hughes for axtag in location.keys(): 41*e1fe3e4aSElliott Hughes if axtag not in self.axes_dict: 42*e1fe3e4aSElliott Hughes raise ValueError("Unknown axis %s in %s" % (axtag, location)) 43*e1fe3e4aSElliott Hughes axis = self.axes_dict[axtag] 44*e1fe3e4aSElliott Hughes normalized_location[axtag] = normalizeValue( 45*e1fe3e4aSElliott Hughes location[axtag], (axis.minValue, axis.defaultValue, axis.maxValue) 46*e1fe3e4aSElliott Hughes ) 47*e1fe3e4aSElliott Hughes 48*e1fe3e4aSElliott Hughes return Location(normalized_location) 49*e1fe3e4aSElliott Hughes 50*e1fe3e4aSElliott Hughes def fix_location(self, location): 51*e1fe3e4aSElliott Hughes location = dict(location) 52*e1fe3e4aSElliott Hughes for tag, axis in self.axes_dict.items(): 53*e1fe3e4aSElliott Hughes if tag not in location: 54*e1fe3e4aSElliott Hughes location[tag] = axis.defaultValue 55*e1fe3e4aSElliott Hughes return location 56*e1fe3e4aSElliott Hughes 57*e1fe3e4aSElliott Hughes def add_value(self, location, value): 58*e1fe3e4aSElliott Hughes if self.axes: 59*e1fe3e4aSElliott Hughes location = self.fix_location(location) 60*e1fe3e4aSElliott Hughes 61*e1fe3e4aSElliott Hughes self.values[Location(location)] = value 62*e1fe3e4aSElliott Hughes 63*e1fe3e4aSElliott Hughes def fix_all_locations(self): 64*e1fe3e4aSElliott Hughes self.values = { 65*e1fe3e4aSElliott Hughes Location(self.fix_location(l)): v for l, v in self.values.items() 66*e1fe3e4aSElliott Hughes } 67*e1fe3e4aSElliott Hughes 68*e1fe3e4aSElliott Hughes @property 69*e1fe3e4aSElliott Hughes def default(self): 70*e1fe3e4aSElliott Hughes self.fix_all_locations() 71*e1fe3e4aSElliott Hughes key = Location({ax.axisTag: ax.defaultValue for ax in self.axes}) 72*e1fe3e4aSElliott Hughes if key not in self.values: 73*e1fe3e4aSElliott Hughes raise ValueError("Default value could not be found") 74*e1fe3e4aSElliott Hughes # I *guess* we could interpolate one, but I don't know how. 75*e1fe3e4aSElliott Hughes return self.values[key] 76*e1fe3e4aSElliott Hughes 77*e1fe3e4aSElliott Hughes def value_at_location(self, location, model_cache=None, avar=None): 78*e1fe3e4aSElliott Hughes loc = location 79*e1fe3e4aSElliott Hughes if loc in self.values.keys(): 80*e1fe3e4aSElliott Hughes return self.values[loc] 81*e1fe3e4aSElliott Hughes values = list(self.values.values()) 82*e1fe3e4aSElliott Hughes return self.model(model_cache, avar).interpolateFromMasters(loc, values) 83*e1fe3e4aSElliott Hughes 84*e1fe3e4aSElliott Hughes def model(self, model_cache=None, avar=None): 85*e1fe3e4aSElliott Hughes if model_cache is not None: 86*e1fe3e4aSElliott Hughes key = tuple(self.values.keys()) 87*e1fe3e4aSElliott Hughes if key in model_cache: 88*e1fe3e4aSElliott Hughes return model_cache[key] 89*e1fe3e4aSElliott Hughes locations = [dict(self._normalized_location(k)) for k in self.values.keys()] 90*e1fe3e4aSElliott Hughes if avar is not None: 91*e1fe3e4aSElliott Hughes mapping = avar.segments 92*e1fe3e4aSElliott Hughes locations = [ 93*e1fe3e4aSElliott Hughes { 94*e1fe3e4aSElliott Hughes k: piecewiseLinearMap(v, mapping[k]) if k in mapping else v 95*e1fe3e4aSElliott Hughes for k, v in location.items() 96*e1fe3e4aSElliott Hughes } 97*e1fe3e4aSElliott Hughes for location in locations 98*e1fe3e4aSElliott Hughes ] 99*e1fe3e4aSElliott Hughes m = VariationModel(locations) 100*e1fe3e4aSElliott Hughes if model_cache is not None: 101*e1fe3e4aSElliott Hughes model_cache[key] = m 102*e1fe3e4aSElliott Hughes return m 103*e1fe3e4aSElliott Hughes 104*e1fe3e4aSElliott Hughes def get_deltas_and_supports(self, model_cache=None, avar=None): 105*e1fe3e4aSElliott Hughes values = list(self.values.values()) 106*e1fe3e4aSElliott Hughes return self.model(model_cache, avar).getDeltasAndSupports(values) 107*e1fe3e4aSElliott Hughes 108*e1fe3e4aSElliott Hughes def add_to_variation_store(self, store_builder, model_cache=None, avar=None): 109*e1fe3e4aSElliott Hughes deltas, supports = self.get_deltas_and_supports(model_cache, avar) 110*e1fe3e4aSElliott Hughes store_builder.setSupports(supports) 111*e1fe3e4aSElliott Hughes index = store_builder.storeDeltas(deltas) 112*e1fe3e4aSElliott Hughes return int(self.default), index 113