[instancer] Parse and percolate triples for axis values
This commit is contained in:
parent
eae0613a82
commit
6752d07c98
@ -123,6 +123,27 @@ class NormalizedAxisRange(AxisRange):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class AxisTent(collections.namedtuple("AxisTent", "minimum default maximum")):
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
self = super().__new__(cls, *args, **kwargs)
|
||||||
|
if not (self.minimum <= self.default <= self.maximum):
|
||||||
|
raise ValueError(
|
||||||
|
f"Tent minimum ({self.minimum:g}) must be <= default ({self.default:g}) which must be <= maximum ({self.maximum:g})"
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{type(self).__name__}({self.minimum:g}, {self.default:g}, {self.maximum:g})"
|
||||||
|
|
||||||
|
|
||||||
|
class NormalizedAxisTent(AxisTent):
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
self = super().__new__(cls, *args, **kwargs)
|
||||||
|
if self.minimum < -1.0 or self.maximum > 1.0:
|
||||||
|
raise ValueError("Axis tent values must be normalized to -1..+1 range")
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
class OverlapMode(IntEnum):
|
class OverlapMode(IntEnum):
|
||||||
KEEP_AND_DONT_SET_FLAGS = 0
|
KEEP_AND_DONT_SET_FLAGS = 0
|
||||||
KEEP_AND_SET_FLAGS = 1
|
KEEP_AND_SET_FLAGS = 1
|
||||||
@ -568,7 +589,7 @@ class _TupleVarStoreAdapter(object):
|
|||||||
|
|
||||||
pinnedAxes = {
|
pinnedAxes = {
|
||||||
axisTag
|
axisTag
|
||||||
for axisTag, (minimum, maximum) in axisLimits.items()
|
for axisTag, (minimum, default, maximum) in axisLimits.items()
|
||||||
if minimum == maximum
|
if minimum == maximum
|
||||||
}
|
}
|
||||||
self.axisOrder = [
|
self.axisOrder = [
|
||||||
@ -913,7 +934,7 @@ def instantiateAvar(varfont, axisLimits):
|
|||||||
# dropping any mappings that fall outside the restricted range.
|
# dropping any mappings that fall outside the restricted range.
|
||||||
# The keys ('fromCoord') are specified in default normalized coordinate space,
|
# The keys ('fromCoord') are specified in default normalized coordinate space,
|
||||||
# whereas the values ('toCoord') are "mapped forward" using the SegmentMap.
|
# whereas the values ('toCoord') are "mapped forward" using the SegmentMap.
|
||||||
normalizedRanges = normalizeAxisLimits(varfont, axisRanges, usingAvar=False)
|
normalizedRanges = normalizeAxisLimits(varfont, axisLimits, usingAvar=False)
|
||||||
newSegments = {}
|
newSegments = {}
|
||||||
for axisTag, mapping in segments.items():
|
for axisTag, mapping in segments.items():
|
||||||
if not _isValidAvarSegmentMap(axisTag, mapping):
|
if not _isValidAvarSegmentMap(axisTag, mapping):
|
||||||
@ -1094,23 +1115,29 @@ def normalizeAxisLimits(varfont, axisLimits, usingAvar=True):
|
|||||||
if usingAvar and "avar" in varfont:
|
if usingAvar and "avar" in varfont:
|
||||||
avarSegments = varfont["avar"].segments
|
avarSegments = varfont["avar"].segments
|
||||||
|
|
||||||
for axis_tag, (_, default, _) in axes.items():
|
|
||||||
value = axisLimits[axis_tag]
|
|
||||||
if isinstance(value, tuple):
|
|
||||||
minV, maxV = value
|
|
||||||
if minV != maxV and (minV > default or maxV < default):
|
|
||||||
raise NotImplementedError(
|
|
||||||
f"Unsupported range {axis_tag}={minV:g}:{maxV:g}; "
|
|
||||||
f"can't change default position ({axis_tag}={default:g})"
|
|
||||||
)
|
|
||||||
|
|
||||||
normalizedLimits = {}
|
normalizedLimits = {}
|
||||||
|
|
||||||
for axis_tag, triple in axes.items():
|
for axis_tag, triple in axes.items():
|
||||||
|
default = triple[1]
|
||||||
|
|
||||||
|
value = axisLimits[axis_tag]
|
||||||
|
|
||||||
|
minV, defaultV, maxV = value
|
||||||
|
if defaultV is None:
|
||||||
|
defaultV = default
|
||||||
|
if (minV != maxV or minV != defaultV) and defaultV != default:
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"Unsupported range {axis_tag}={minV:g}:{defaultV:g}:{maxV:g}; "
|
||||||
|
f"can't change default position ({axis_tag}={default:g})"
|
||||||
|
)
|
||||||
|
|
||||||
|
value = (minV, defaultV, maxV)
|
||||||
|
|
||||||
avarMapping = avarSegments.get(axis_tag, None)
|
avarMapping = avarSegments.get(axis_tag, None)
|
||||||
minimum, maximum = axisLimits[axis_tag]
|
normalizedLimits[axis_tag] = NormalizedAxisTent(
|
||||||
normalizedLimits[axis_tag] = NormalizedAxisRange(
|
*(normalize(v, triple, avarMapping) for v in value)
|
||||||
*(normalize(v, triple, avarMapping) for v in (minimum, maximum))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return normalizedLimits
|
return normalizedLimits
|
||||||
|
|
||||||
|
|
||||||
@ -1298,7 +1325,7 @@ def setRibbiBits(font):
|
|||||||
|
|
||||||
def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
|
def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
|
||||||
location, axisRanges = {}, {}
|
location, axisRanges = {}, {}
|
||||||
for axisTag, (minimum, maximum) in axisLimits.items():
|
for axisTag, (minimum, default, maximum) in axisLimits.items():
|
||||||
if minimum == maximum:
|
if minimum == maximum:
|
||||||
location[axisTag] = minimum
|
location[axisTag] = minimum
|
||||||
else:
|
else:
|
||||||
@ -1309,7 +1336,7 @@ def splitAxisLocationAndRanges(axisLimits, rangeType=AxisRange):
|
|||||||
def parseLimits(limits):
|
def parseLimits(limits):
|
||||||
result = {}
|
result = {}
|
||||||
for limitString in limits:
|
for limitString in limits:
|
||||||
match = re.match(r"^(\w{1,4})=(?:(drop)|(?:([^:]+)(?:[:](.+))?))$", limitString)
|
match = re.match(r"^(\w{1,4})=(?:(drop)|(?:([^:]+)(?:[:]([^:]+))?(?:[:]([^:]+))?))$", limitString)
|
||||||
if not match:
|
if not match:
|
||||||
raise ValueError("invalid location format: %r" % limitString)
|
raise ValueError("invalid location format: %r" % limitString)
|
||||||
tag = match.group(1).ljust(4)
|
tag = match.group(1).ljust(4)
|
||||||
@ -1317,10 +1344,19 @@ def parseLimits(limits):
|
|||||||
lbound = None
|
lbound = None
|
||||||
else:
|
else:
|
||||||
lbound = strToFixedToFloat(match.group(3), precisionBits=16)
|
lbound = strToFixedToFloat(match.group(3), precisionBits=16)
|
||||||
ubound = lbound
|
ubound = default = lbound
|
||||||
if match.group(4):
|
if match.group(4):
|
||||||
ubound = strToFixedToFloat(match.group(4), precisionBits=16)
|
ubound = default = strToFixedToFloat(match.group(4), precisionBits=16)
|
||||||
result[tag] = (lbound, ubound)
|
default = None
|
||||||
|
if match.group(5):
|
||||||
|
default = ubound
|
||||||
|
ubound = strToFixedToFloat(match.group(5), precisionBits=16)
|
||||||
|
|
||||||
|
if default is None:
|
||||||
|
assert lbound <= ubound
|
||||||
|
else:
|
||||||
|
assert lbound <= default <= ubound
|
||||||
|
result[tag] = (lbound, default, ubound)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user