def resolveFor(self, prop, overriddenDefs): """Create a Specifier for a property from this default and any superclass defaults.""" if self.isAdditive: allReqs = self.requiredProperties for other in overriddenDefs: allReqs |= other.requiredProperties def concatenator(context): allVals = [self.value(context)] for other in overriddenDefs: allVals.append(other.value(context)) return tuple(allVals) val = DelayedArgument(allReqs, concatenator) else: val = DelayedArgument(self.requiredProperties, self.value) return Specifier(prop, val)
def RelativeTo(X, Y): """The 'X relative to Y' polymorphic operator. Allowed forms: F relative to G (with at least one a field, the other a field or heading) <vector> relative to <oriented point> (and vice versa) <vector> relative to <vector> <heading> relative to <heading> """ xf, yf = isA(X, VectorField), isA(Y, VectorField) if xf or yf: if xf and yf and X.valueType != Y.valueType: raise RuntimeParseError('"X relative to Y" with X, Y fields of different types') fieldType = X.valueType if xf else Y.valueType error = '"X relative to Y" with field and value of different types' def helper(context): pos = context.position.toVector() xp = X[pos] if xf else toType(X, fieldType, error) yp = Y[pos] if yf else toType(Y, fieldType, error) return xp + yp return DelayedArgument({'position'}, helper) else: if isinstance(X, OrientedPoint): # TODO too strict? if isinstance(Y, OrientedPoint): raise RuntimeParseError('"X relative to Y" with X, Y both oriented points') Y = toVector(Y, '"X relative to Y" with X an oriented point but Y not a vector') return X.relativize(Y) elif isinstance(Y, OrientedPoint): X = toVector(X, '"X relative to Y" with Y an oriented point but X not a vector') return Y.relativize(X) else: X = toTypes(X, (Vector, float), '"X relative to Y" with X neither a vector nor scalar') Y = toTypes(Y, (Vector, float), '"X relative to Y" with Y neither a vector nor scalar') return evaluateRequiringEqualTypes(lambda: X + Y, X, Y, '"X relative to Y" with vector and scalar')
def ApparentlyFacing(heading, fromPt=None): heading = toHeading(heading, 'specifier "apparently facing X" with X not a heading') if fromPt is None: fromPt = ego() fromPt = toVector(fromPt, 'specifier "apparently facing X from Y" with Y not a vector') value = lambda self: fromPt.angleTo(self.position) + heading return Specifier('heading', DelayedArgument({'position'}, value))
def Facing(heading): if isinstance(heading, VectorField): return Specifier('heading', DelayedArgument({'position'}, lambda self: heading[self.position])) else: heading = toHeading(heading, 'specifier "facing X" with X not a heading or vector field') return Specifier('heading', heading)
def RelativeTo(X, Y): xf, yf = isA(X, VectorField), isA(Y, VectorField) if xf or yf: if xf and yf and X.valueType != Y.valueType: raise RuntimeParseError('"X relative to Y" with X, Y fields of different types') fieldType = X.valueType if xf else Y.valueType error = '"X relative to Y" with field and value of different types' def helper(context): pos = context.position.toVector() xp = X[pos] if xf else toType(X, fieldType, error) yp = Y[pos] if yf else toType(Y, fieldType, error) return xp + yp return DelayedArgument({'position'}, helper) else: if isinstance(X, OrientedPoint): # TODO too strict? if isinstance(Y, OrientedPoint): raise RuntimeParseError('"X relative to Y" with X, Y both oriented points') Y = toVector(Y, '"X relative to Y" with X an oriented point but Y not a vector') return X.relativize(Y) elif isinstance(Y, OrientedPoint): X = toVector(X, '"X relative to Y" with Y an oriented point but X not a vector') return Y.relativize(X) else: X = toTypes(X, (Vector, float), '"X relative to Y" with X neither a vector nor scalar') Y = toTypes(Y, (Vector, float), '"X relative to Y" with Y neither a vector nor scalar') return valueRequiringEqualTypes(X + Y, X, Y, '"X relative to Y" with vector and scalar')
def leftSpecHelper(syntax, pos, dist, axis, toComponents, makeOffset): extras = set() if canCoerce(dist, float): dx, dy = toComponents(coerce(dist, float)) elif canCoerce(dist, Vector): dx, dy = coerce(dist, Vector) else: raise RuntimeParseError(f'"{syntax} X by D" with D not a number or vector') if isinstance(pos, OrientedPoint): # TODO too strict? val = lambda self: pos.relativize(makeOffset(self, dx, dy)) new = DelayedArgument({axis}, val) extras.add('heading') else: pos = toVector(pos, f'specifier "{syntax} X" with X not a vector') val = lambda self: pos.offsetRotated(self.heading, makeOffset(self, dx, dy)) new = DelayedArgument({axis, 'heading'}, val) return Specifier('position', new, optionals=extras)
def FacingToward(pos): """The 'facing toward <vector>' specifier. Specifies 'heading', depending on 'position'. """ pos = toVector(pos, 'specifier "facing toward X" with X not a vector') return Specifier('heading', DelayedArgument({'position'}, lambda self: self.position.angleTo(pos)))
def test_delayed_call(): da = DelayedArgument(('blob', ), lambda context: (lambda x, y=1: x * y + context.blob)) res = da(42, y=2) assert isinstance(res, DelayedArgument) context = LazilyEvaluable.makeContext(blob=-7) evres = valueInContext(res, context) assert not needsLazyEvaluation(evres) assert evres == 77
def test_distribution_method_encapsulation_lazy(): vf = VectorField("Foo", lambda pos: 0) da = DelayedArgument(set(), lambda context: Options([1, 2])) pt = vf.followFrom(Vector(0, 0), da, steps=1) assert isinstance(pt, DelayedArgument) evpt = valueInContext(pt, {}) assert not needsLazyEvaluation(evpt) assert isinstance(evpt, VectorMethodDistribution) assert evpt.method is underlyingFunction(vf.followFrom)
def test_delayed_call(): da = DelayedArgument(('blob', ), lambda context: (lambda x, y=1: x * y + context.blob)) res = da(42, y=2) assert isinstance(res, DelayedArgument) Thingy = namedtuple('Thingy', 'blob') evres = valueInContext(res, Thingy(blob=-7)) assert not needsLazyEvaluation(evres) assert evres == 77
def ApparentlyFacing(heading, fromPt=None): """The 'apparently facing <heading> [from <vector>]' specifier. Specifies 'heading', depending on 'position'. If the 'from <vector>' is omitted, the position of ego is used. """ heading = toHeading(heading, 'specifier "apparently facing X" with X not a heading') if fromPt is None: fromPt = ego() fromPt = toVector(fromPt, 'specifier "apparently facing X from Y" with Y not a vector') value = lambda self: fromPt.angleTo(self.position) + heading return Specifier('heading', DelayedArgument({'position'}, value))
def Facing(heading): """The 'facing X' polymorphic specifier. Specifies 'heading', with dependencies depending on the form: facing <number> -- no dependencies; facing <field> -- depends on 'position'. """ if isinstance(heading, VectorField): return Specifier('heading', DelayedArgument({'position'}, lambda self: heading[self.position])) else: heading = toHeading(heading, 'specifier "facing X" with X not a heading or vector field') return Specifier('heading', heading)
def FacingToward(pos): pos = toVector(pos, 'specifier "facing toward X" with X not a vector') return Specifier('heading', DelayedArgument({'position'}, lambda self: self.position.angleTo(pos)))
def test_pickle_object(): spec = Specifier( 'blob', DelayedArgument(('width', ), lambda context: 2 * context.width), {'width'}) obj = Object(spec) tryPickling(obj)