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