def OffsetAlong(X, H, Y): X = toVector(X, '"X offset along H by Y" with X not a vector') Y = toVector(Y, '"X offset along H by Y" with Y not a vector') if isinstance(H, VectorField): H = H[X] H = toHeading(H, '"X offset along H by Y" with H not a heading or vector field') return X.offsetRotated(H, Y)
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 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 RelativePosition(X, Y=None): """The 'relative position of <vector> [from <vector>]' operator. If the 'from <vector>' is omitted, the position of ego is used. """ X = toVector(X, '"relative position of X from Y" with X not a vector') if Y is None: Y = ego() Y = toVector(Y, '"relative position of X from Y" with Y not a vector') return X - Y
def DistanceFrom(X, Y=None): """The 'distance from <vector> [to <vector>]' operator. If the 'to <vector>' is omitted, the position of ego is used. """ X = toVector(X, '"distance from X to Y" with X not a vector') if Y is None: Y = ego() Y = toVector(Y, '"distance from X to Y" with Y not a vector') return X.distanceTo(Y)
def Beyond(pos, offset, fromPt=None): pos = toVector(pos, 'specifier "beyond X by Y" with X not a vector') dType = underlyingType(offset) if dType is float or dType is int: offset = Vector(0, offset) elif dType is not Vector: raise RuntimeParseError('specifier "beyond X by Y" with Y not a number or vector') if fromPt is None: fromPt = ego() fromPt = toVector(fromPt, 'specifier "beyond X by Y from Z" with Z not a vector') lineOfSight = fromPt.angleTo(pos) return Specifier('position', pos.offsetRotated(lineOfSight, offset))
def OffsetAlong(X, H, Y): """The 'X offset along H by Y' polymorphic operator. Allowed forms: <vector> offset along <heading> by <vector> <vector> offset along <field> by <vector> """ X = toVector(X, '"X offset along H by Y" with X not a vector') Y = toVector(Y, '"X offset along H by Y" with Y not a vector') if isinstance(H, VectorField): H = H[X] H = toHeading(H, '"X offset along H by Y" with H not a heading or vector field') return X.offsetRotated(H, Y)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.position = toVector(self.position, f'"position" of {self} not a vector') self.corners = (self.position, ) self.visibleRegion = CircularRegion(self.position, self.visibleDistance)
def ApparentHeading(X, Y=None): if not isinstance(X, OrientedPoint): raise RuntimeParseError('"apparent heading of X from Y" with X not an OrientedPoint') if Y is None: Y = ego() Y = toVector(Y, '"relative heading of X from Y" with Y not a vector') return apparentHeadingAtPoint(X.position, X.heading, Y)
def __contains__(self, thing): """Check if this `Region` contains an object or vector.""" from scenic.core.object_types import Object if isinstance(thing, Object): return self.containsObject(thing) vec = toVector(thing, '"X in Y" with X not an Object or a vector') return self.containsPoint(vec)
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 CanSee(X, Y): if not isinstance(X, Point): raise RuntimeParseError('"X can see Y" with X not a Point') if isinstance(Y, Object): return X.canSee(Y) else: Y = toVector(Y, '"X can see Y" with Y not a vector') return X.visibleRegion.containsPoint(Y)
def Follow(F, X, D): if not isinstance(F, VectorField): raise RuntimeParseError('"follow F from X for D" with F not a vector field') X = toVector(X, '"follow F from X for D" with X not a vector') D = toScalar(D, '"follow F from X for D" with D not a number') pos = F.followFrom(X, D) heading = F[pos] return OrientedPoint(position=pos, heading=heading)
def OffsetBy(offset): """The 'offset by <vector>' specifier. Specifies 'position', with no dependencies. """ offset = toVector(offset, 'specifier "offset by X" with X not a vector') pos = RelativeTo(offset, ego()).toVector() return Specifier('position', pos)
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 _specify(self, prop, value): assert prop not in self.properties # Normalize types of some built-in properties if prop == 'position': value = toVector(value, f'"position" of {self} not a vector') elif prop == 'heading': value = toHeading(value, f'"heading" of {self} not a heading') self.properties.add(prop) object.__setattr__(self, prop, value)
def ApparentHeading(X, Y=None): """The 'apparent heading of <oriented point> [from <vector>]' operator. If the 'from <vector>' is omitted, the position of ego is used. """ if not isinstance(X, OrientedPoint): raise RuntimeParseError('"apparent heading of X from Y" with X not an OrientedPoint') if Y is None: Y = ego() Y = toVector(Y, '"relative heading of X from Y" with Y not a vector') return apparentHeadingAtPoint(X.position, X.heading, Y)
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 Beyond(pos, offset, fromPt=None): """The 'beyond X by Y [from Z]' polymorphic specifier. Specifies 'position', with no dependencies. Allowed forms: beyond <vector> by <number> [from <vector>] beyond <vector> by <vector> [from <vector>] If the 'from <vector>' is omitted, the position of ego is used. """ pos = toVector(pos, 'specifier "beyond X by Y" with X not a vector') dType = underlyingType(offset) if dType is float or dType is int: offset = Vector(0, offset) elif dType is not Vector: raise RuntimeParseError('specifier "beyond X by Y" with Y not a number or vector') if fromPt is None: fromPt = ego() fromPt = toVector(fromPt, 'specifier "beyond X by Y from Z" with Z not a vector') lineOfSight = fromPt.angleTo(pos) return Specifier('position', pos.offsetRotated(lineOfSight, offset))
def CanSee(X, Y): """The 'X can see Y' polymorphic operator. Allowed forms: <point> can see <object> <point> can see <vector> """ if not isinstance(X, Point): raise RuntimeParseError('"X can see Y" with X not a Point') if isinstance(Y, Point): return X.canSee(Y) else: Y = toVector(Y, '"X can see Y" with Y not a vector') return X.visibleRegion.containsPoint(Y)
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 Following(field, dist, fromPt=None): """The 'following F [from X] for D' specifier. Specifies 'position', and optionally 'heading', with no dependencies. Allowed forms: following <field> [from <vector>] for <number> If the 'from <vector>' is omitted, the position of ego is used. """ if fromPt is None: fromPt = ego() else: dist, fromPt = fromPt, dist if not isinstance(field, VectorField): raise RuntimeParseError('"following F" specifier with F not a vector field') fromPt = toVector(fromPt, '"following F from X for D" with X not a vector') dist = toScalar(dist, '"following F for D" with D not a number') pos = field.followFrom(fromPt, dist) heading = field[pos] val = OrientedVector.make(pos, heading) return Specifier('position', val, optionals={'heading'})
def At(pos): """The 'at <vector>' specifier. Specifies 'position', with no dependencies.""" pos = toVector(pos, 'specifier "at X" with X not a vector') return Specifier('position', pos)
def AngleFrom(X, Y): """The 'angle from <vector> to <vector>' operator.""" X = toVector(X, '"angle from X to Y" with X not a vector') Y = toVector(Y, '"angle from X to Y" with Y not a vector') return X.angleTo(Y)
def AngleTo(X): """The 'angle to <vector>' operator (using the position of ego as the reference).""" X = toVector(X, '"angle to X" with X not a vector') return ego().angleTo(X)
def AngleFrom(X, Y): X = toVector(X, '"angle from X to Y" with X not a vector') Y = toVector(Y, '"angle from X to Y" with Y not a vector') return X.angleTo(Y)
def At(pos): pos = toVector(pos, 'specifier "at X" with X not a vector') return Specifier('position', pos)
def FieldAt(X, Y): """The '<VectorField> at <vector>' operator.""" if not isinstance(X, VectorField): raise RuntimeParseError('"X at Y" with X not a vector field') Y = toVector(Y, '"X at Y" with Y not a vector') return X[Y]
def OffsetBy(offset): offset = toVector(offset, 'specifier "offset by X" with X not a vector') pos = RelativeTo(offset, ego()).toVector() return Specifier('position', pos)
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)))