class OrientedPoint(Point): """Implementation of the Scenic class ``OrientedPoint``. The default mutator for `OrientedPoint` adds Gaussian noise to ``heading`` with a standard deviation given by the ``headingStdDev`` property, then applies the mutator for `Point`. Properties: heading (float; dynamic): Heading of the `OrientedPoint`. Default value 0 (North). viewAngle (float): View cone angle for ``can see`` operator. Default value :math:`2\\pi`. """ heading: PropertyDefault((), {'dynamic'}, lambda self: 0) viewAngle: math.tau mutator: PropertyDefault({'headingStdDev'}, {'additive'}, lambda self: HeadingMutator(self.headingStdDev)) headingStdDev: math.radians(5) @cached_property def visibleRegion(self): return SectorRegion(self.position, self.visibleDistance, self.heading, self.viewAngle) def relativize(self, vec): pos = self.relativePosition(vec) return OrientedPoint(position=pos, heading=self.heading) def relativePosition(self, vec): return self.position.offsetRotated(self.heading, vec) def toHeading(self) -> float: return self.heading
class OrientedPoint(Point): heading: 0 viewAngle: math.tau mutator: PropertyDefault({'headingStdDev'}, {'additive'}, lambda self: HeadingMutator(self.headingStdDev)) headingStdDev: math.radians(5) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.heading = toScalar(self.heading, f'"heading" of {self} not a scalar') self.visibleRegion = SectorRegion(self.position, self.visibleDistance, self.heading, self.viewAngle) def relativize(self, vec): pos = self.relativePosition(vec) return OrientedPoint(position=pos, heading=self.heading) def relativePosition(self, x, y=None): vec = x if y is None else Vector(x, y) pos = self.position.offsetRotated(self.heading, vec) return OrientedPoint(position=pos, heading=self.heading) def toHeading(self): return self.heading
class Point(Constructible): """Implementation of the Scenic class ``Point``. The default mutator for `Point` adds Gaussian noise to ``position`` with a standard deviation given by the ``positionStdDev`` property. Attributes: position (`Vector`): Position of the point. Default value is the origin. visibleDistance (float): Distance for ``can see`` operator. Default value 50. width (float): Default value zero (only provided for compatibility with operators that expect an `Object`). height (float): Default value zero. """ position: Vector(0, 0) width: 0 height: 0 visibleDistance: 50 mutationEnabled: False mutator: PropertyDefault({'positionStdDev'}, {'additive'}, lambda self: PositionMutator(self.positionStdDev)) positionStdDev: 1 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 toVector(self): return self.position.toVector() def canSee(self, other): # TODO improve approximation? for corner in other.corners: if self.visibleRegion.containsPoint(corner): return True return False def sampleGiven(self, value): sample = super().sampleGiven(value) if self.mutationEnabled: for mutator in self.mutator: if mutator is None: continue sample, proceed = mutator.appliedTo(sample) if not proceed: break return sample # Points automatically convert to Vectors when needed def __getattr__(self, attr): if hasattr(Vector, attr): return getattr(self.toVector(), attr) else: raise AttributeError( f"'{type(self).__name__}' object has no attribute '{attr}'")
def defaults(cla): # TODO improve so this need only be done once? # find all defaults provided by the class or its superclasses allDefs = collections.defaultdict(list) for sc in inspect.getmro(cla): if hasattr(sc, '__annotations__'): for prop, value in sc.__annotations__.items(): allDefs[prop].append(PropertyDefault.forValue(value)) # resolve conflicting defaults resolvedDefs = {} for prop, defs in allDefs.items(): primary, rest = defs[0], defs[1:] spec = primary.resolveFor(prop, rest) resolvedDefs[prop] = spec return resolvedDefs
def __init_subclass__(cls): # find all defaults provided by the class or its superclasses allDefs = collections.defaultdict(list) for sc in inspect.getmro(cls): if hasattr(sc, '__annotations__'): for prop, value in sc.__annotations__.items(): allDefs[prop].append(PropertyDefault.forValue(value)) # resolve conflicting defaults resolvedDefs = {} for prop, defs in allDefs.items(): primary, rest = defs[0], defs[1:] spec = primary.resolveFor(prop, rest) resolvedDefs[prop] = spec cls.defaults = resolvedDefs
class Point(Constructible): position: Vector(0, 0) width: 0 height: 0 visibleDistance: 50 mutationEnabled: False mutator: PropertyDefault({'positionStdDev'}, {'additive'}, lambda self: PositionMutator(self.positionStdDev)) positionStdDev: 1 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 toVector(self): return self.position.toVector() def canSee(self, other): # TODO improve approximation? for corner in other.corners: if self.visibleRegion.containsPoint(corner): return True return False def sampleGiven(self, value): sample = super().sampleGiven(value) if self.mutationEnabled: for mutator in self.mutator: if mutator is None: continue sample, proceed = mutator.appliedTo(sample) if not proceed: break return sample # Points automatically convert to Vectors when needed def __getattr__(self, attr): if hasattr(Vector, attr): return getattr(self.toVector(), attr) else: raise AttributeError( f"'{type(self).__name__}' object has no attribute '{attr}'")
def __init_subclass__(cls): super().__init_subclass__() # find all defaults provided by the class or its superclasses allDefs = collections.defaultdict(list) for sc in cls.__mro__: if issubclass(sc, _Constructible) and hasattr( sc, '__annotations__'): for prop, value in sc.__annotations__.items(): allDefs[prop].append(PropertyDefault.forValue(value)) # resolve conflicting defaults and gather dynamic properties resolvedDefs = {} dyns = [] for prop, defs in allDefs.items(): primary, rest = defs[0], defs[1:] spec = primary.resolveFor(prop, rest) resolvedDefs[prop] = spec if any(defn.isDynamic for defn in defs): dyns.append(prop) cls._defaults = resolvedDefs cls._dynamicProperties = tuple(dyns)
class OrientedPoint(Point): """Implementation of the Scenic class ``OrientedPoint``. The default mutator for `OrientedPoint` adds Gaussian noise to ``heading`` with a standard deviation given by the ``headingStdDev`` property, then applies the mutator for `Point`. Attributes: heading (float): Heading of the `OrientedPoint`. Default value 0 (North). viewAngle (float): View cone angle for ``can see`` operator. Default value :math:`2\\pi`. """ heading: 0 viewAngle: math.tau mutator: PropertyDefault({'headingStdDev'}, {'additive'}, lambda self: HeadingMutator(self.headingStdDev)) headingStdDev: math.radians(5) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.heading = toScalar(self.heading, f'"heading" of {self} not a scalar') self.visibleRegion = SectorRegion(self.position, self.visibleDistance, self.heading, self.viewAngle) def relativize(self, vec): pos = self.relativePosition(vec) return OrientedPoint(position=pos, heading=self.heading) def relativePosition(self, x, y=None): vec = x if y is None else Vector(x, y) pos = self.position.offsetRotated(self.heading, vec) return OrientedPoint(position=pos, heading=self.heading) def toHeading(self): return self.heading
def to_annotations(model_desc: t.Dict[str, t.Any], input_dir: str, models_dir: str): typ = ModelTypes[model_desc['type']] name = model_desc['name'] annotations = { 'gz_name': name, 'type': typ, } if typ == ModelTypes.MISSION_ONLY: annotations.update({ 'width': model_desc.get('width', 0.00001), 'length': model_desc.get('length', 0.00001) }) elif typ == ModelTypes.CUSTOM_MODEL: dir_path = os.path.join(input_dir, models_dir) dir_path = os.path.join(dir_path, name) url = model_desc.get('url', '') elif typ in [ModelTypes.GAZEBO_MODEL, ModelTypes.GAZEBO_DB_MODEL]: # TODO we need to download files from gazebo repo # and do the same as CUSTOM_MODEL dir_path, gazebo_db = gazebo_dir_and_path( os.path.join(input_dir, models_dir), name) annotations[ 'type'] = ModelTypes.GAZEBO_DB_MODEL if gazebo_db else ModelTypes.GAZEBO_MODEL url = '' if typ != ModelTypes.MISSION_ONLY: sdf_path = handle_path(dir_path, url) info = process_sdf(dir_path, sdf_path) if not model_desc.get('dynamic_size', info.dynamic_size): annotations.update({ 'length': info.length, 'width': info.width, 'height': info.height }) else: annotations.update({ 'length': Range(info.length / 2, info.length * 2), 'width': Range(info.width / 2, info.width * 2), 'height': Range(info.height / 2, info.height * 2), 'dynamic_size': info.dynamic_size, 'o_length': info.length / info.orig_scale[1], 'o_width': info.width / info.orig_scale[0], 'o_height': info.height / info.orig_scale[2] }) if info.eq_width_length: annotations['width'] = PropertyDefault( ('length', ), {}, lambda self: self.length) if 'z' in model_desc: annotations['z'] = model_desc['z'] if 'heading' in model_desc: annotations['heading'] = model_desc['heading'] if 'dynamic_size' in model_desc: annotations['dynamic_size'] = model_desc['dynamic_size'] annotations['allowCollisions'] = model_desc.get('allow_collisions', False) return annotations
class Object(OrientedPoint, _RotatedRectangle): """Implementation of the Scenic class ``Object``. This is the default base class for Scenic classes. Properties: width (float): Width of the object, i.e. extent along its X axis. Default value 1. length (float): Length of the object, i.e. extent along its Y axis. Default value 1. allowCollisions (bool): Whether the object is allowed to intersect other objects. Default value ``False``. requireVisible (bool): Whether the object is required to be visible from the ``ego`` object. Default value ``True``. regionContainedIn (`Region` or ``None``): A `Region` the object is required to be contained in. If ``None``, the object need only be contained in the scenario's workspace. cameraOffset (`Vector`): Position of the camera for the ``can see`` operator, relative to the object's ``position``. Default ``0 @ 0``. speed (float; dynamic): Speed in dynamic simulations. Default value 0. velocity (`Vector`; *dynamic*): Velocity in dynamic simulations. Default value is the velocity determined by ``self.speed`` and ``self.heading``. angularSpeed (float; *dynamic*): Angular speed in dynamic simulations. Default value 0. behavior: Behavior for dynamic agents, if any (see :ref:`dynamics`). Default value ``None``. """ width: 1 length: 1 allowCollisions: False requireVisible: True regionContainedIn: None cameraOffset: Vector(0, 0) velocity: PropertyDefault( ('speed', 'heading'), {'dynamic'}, lambda self: Vector(0, self.speed).rotatedBy(self.heading)) speed: PropertyDefault((), {'dynamic'}, lambda self: 0) angularSpeed: PropertyDefault((), {'dynamic'}, lambda self: 0) behavior: None lastActions: None def __new__(cls, *args, **kwargs): obj = super().__new__(cls) # The _dynamicProxy attribute stores a mutable copy of the object used during # simulations, intercepting all attribute accesses to the original object; # we set this attribute very early to prevent problems during unpickling. object.__setattr__(obj, '_dynamicProxy', obj) return obj def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.hw = hw = self.width / 2 self.hl = hl = self.length / 2 self.radius = hypot(hw, hl) # circumcircle; for collision detection self.inradius = min(hw, hl) # incircle; for collision detection self._relations = [] def _specify(self, prop, value): # Normalize types of some built-in properties if prop == 'behavior': import scenic.syntax.veneer as veneer # TODO improve? value = toType(value, veneer.Behavior, f'"behavior" of {self} not a behavior') super()._specify(prop, value) def _register(self): import scenic.syntax.veneer as veneer # TODO improve? veneer.registerObject(self) def __getattribute__(self, name): proxy = object.__getattribute__(self, '_dynamicProxy') return object.__getattribute__(proxy, name) def __setattr__(self, name, value): proxy = object.__getattribute__(self, '_dynamicProxy') object.__setattr__(proxy, name, value) def __delattr__(self, name): proxy = object.__getattribute__(self, '_dynamicProxy') object.__delattr__(proxy, name) @cached_property def left(self): return self.relativize(Vector(-self.hw, 0)) @cached_property def right(self): return self.relativize(Vector(self.hw, 0)) @cached_property def front(self): return self.relativize(Vector(0, self.hl)) @cached_property def back(self): return self.relativize(Vector(0, -self.hl)) @cached_property def frontLeft(self): return self.relativize(Vector(-self.hw, self.hl)) @cached_property def frontRight(self): return self.relativize(Vector(self.hw, self.hl)) @cached_property def backLeft(self): return self.relativize(Vector(-self.hw, -self.hl)) @cached_property def backRight(self): return self.relativize(Vector(self.hw, -self.hl)) @cached_property def visibleRegion(self): camera = self.position.offsetRotated(self.heading, self.cameraOffset) return SectorRegion(camera, self.visibleDistance, self.heading, self.viewAngle) @cached_property def corners(self): hw, hl = self.hw, self.hl return (self.relativePosition(Vector(hw, hl)), self.relativePosition(Vector(-hw, hl)), self.relativePosition(Vector(-hw, -hl)), self.relativePosition(Vector(hw, -hl))) def show(self, workspace, plt, highlight=False): if needsSampling(self): raise RuntimeError('tried to show() symbolic Object') pos = self.position spos = workspace.scenicToSchematicCoords(pos) if highlight: # Circle around object rad = 1.5 * max(self.width, self.length) c = plt.Circle(spos, rad, color='g', fill=False) plt.gca().add_artist(c) # View cone ha = self.viewAngle / 2.0 camera = self.position.offsetRotated(self.heading, self.cameraOffset) cpos = workspace.scenicToSchematicCoords(camera) for angle in (-ha, ha): p = camera.offsetRadially(20, self.heading + angle) edge = [cpos, workspace.scenicToSchematicCoords(p)] x, y = zip(*edge) plt.plot(x, y, 'b:') corners = [ workspace.scenicToSchematicCoords(corner) for corner in self.corners ] x, y = zip(*corners) color = self.color if hasattr(self, 'color') else (1, 0, 0) plt.fill(x, y, color=color) frontMid = averageVectors(corners[0], corners[1]) baseTriangle = [frontMid, corners[2], corners[3]] triangle = [averageVectors(p, spos, weight=0.5) for p in baseTriangle] x, y = zip(*triangle) plt.fill(x, y, "w") plt.plot(x + (x[0], ), y + (y[0], ), color="k", linewidth=1)
class Point(_Constructible): """Implementation of the Scenic base class ``Point``. The default mutator for `Point` adds Gaussian noise to ``position`` with a standard deviation given by the ``positionStdDev`` property. Properties: position (`Vector`; dynamic): Position of the point. Default value is the origin. visibleDistance (float): Distance for ``can see`` operator. Default value 50. width (float): Default value zero (only provided for compatibility with operators that expect an `Object`). length (float): Default value zero. .. note:: If you're looking into Scenic's internals, note that `Point` is actually a subclass of the internal Python class `_Constructible`. """ position: PropertyDefault((), {'dynamic'}, lambda self: Vector(0, 0)) width: 0 length: 0 visibleDistance: 50 mutationEnabled: False mutator: PropertyDefault({'positionStdDev'}, {'additive'}, lambda self: PositionMutator(self.positionStdDev)) positionStdDev: 1 @cached_property def visibleRegion(self): return CircularRegion(self.position, self.visibleDistance) @cached_property def corners(self): return (self.position, ) def toVector(self) -> Vector: return self.position def canSee(self, other) -> bool: # TODO improve approximation? for corner in other.corners: if self.visibleRegion.containsPoint(corner): return True return False def sampleGiven(self, value): sample = super().sampleGiven(value) if self.mutationEnabled: for mutator in self.mutator: if mutator is None: continue sample, proceed = mutator.appliedTo(sample) if not proceed: break return sample # Points automatically convert to Vectors when needed def __getattr__(self, attr): if hasattr(Vector, attr): return getattr(self.toVector(), attr) else: raise AttributeError( f"'{type(self).__name__}' object has no attribute '{attr}'")