Source code for blueprints.body

import xml.etree.ElementTree as xml
import numpy  as np
import blueprints as blue



[docs] class Body(blue.BodyType, blue.thing.MoveableThing, blue.thing.NodeThing): """ This class is available through the shortcut :class:`blueprints.Body <Body>`. Attributes from base classes :class:`MoveableThing <blueprints.thing.moveable.MoveableThing>` and :class:`NodeThing <blueprints.thing.node.NodeThing>` are inherited. A :class:`Body` is the primary Thing used to build the structure of a kinematic tree. This is done by attaching them to other bodies as well as other bodies to them. Bodies do have a position and an orientation but no further physical properties like a shape, a size or mass. Positions and orientations are local meaning that a body is placed relative to its parents frame of reference.To flesh out a :class:`Body` the following Things can be attached to it: * :class:`Geom <blueprints.geoms.BaseGeom>`: This Thing is used to fill in the Body with matter. Geoms have a shape, mass, friction etc. * :class:`Site <blueprints.sites.BaseSite>`: Sites similar to Geoms have shape, but do not effect the simulation physics. Instead :class:`Sensor <blueprints.sensors.BaseSensor>` instances as well as :class:`Actuator <blueprints.actuators.BaseActuator>` can be attached to it. * :class:`Camera <blueprints.camera.Camera>`: If attached to the Body, the Camera can be view trough :meth:`World.view <blueprints.world.World.view>` by setting the :attr:`rendering <blueprints.camera.Camera.rendering>` camera value to the Cameras name. If the Body is included in an :class:`Agent <blueprints.agent.Agent>` (not existing right now) the camera will be available in the Agents observations. * :class:`Placeholder <blueprints.placeholder.Placeholder>`: A placeholder can be used to specify a position and orientation relative to the Body to which it is attached, that is of special interest. If a Thing is attached to a placeholder it is instead attached to its parent in a position and orientation, that matches attachment to the placeholder. * :class:`Actuator <blueprints.actuators.BaseActuator>`: Actuators are used to apply force to the kinematic tree at the node they are attached to. If the Body is included in an :class:`Agent <blueprints.agent.Agent>` (not yet implemented) the actuators input will appear in the Agents action space. * :class:`Light <blueprints.light.Light>`: Light sources illuminate the space around them, but are not necessary for the model to be visible, since a base luminescence is presence always present. * :class:`Joint <blueprints.joints.BaseJoint>`: This Thing is used to move the :class:`Body`:. If a force is applied to the Body it moves along the degrees of freedom defined by the Joint. .. note:: Unintuitively Bodies are not connected with each other via a Joint. Instead a Joint attached to a body defines the way in which a Body can be moved w.r.t. to the Bodies parent. A kinematic tree can either be constructed bottom up or top down. The following two examples are used to create a kinematic tree of this structure: .. code-block:: mxml :caption: XML Structure <body name="tree"> <body pos="0.0 0.0 8.0" name="stem"> <body euler="0.0 -0.7853982 0.0" pos="0.0 0.0 4.0" name="branch_(0)"> <body euler="0.0 -0.7853982 0.0" pos="0.0 0.0 2.0" name="twig_(0)"> <body euler="0.0 -0.7853982 0.0" pos="0.0 0.0 1.0" name="leaf_(0)" /> <body euler="0.0 0.7853982 0.0" pos="0.0 0.0 1.0" name="leaf_(1)" /> </body> <body euler="0.0 0.7853982 0.0" pos="0.0 0.0 2.0" name="twig_(1)"> <body euler="0.0 -0.7853982 0.0" pos="0.0 0.0 1.0" name="leaf_(2)" /> <body euler="0.0 0.7853982 0.0" pos="0.0 0.0 1.0" name="leaf_(3)" /> </body> </body> <body euler="0.0 0.7853982 0.0" pos="0.0 0.0 4.0" name="branch_(1)"> <body euler="0.0 -0.7853982 0.0" pos="0.0 0.0 2.0" name="twig_(2)"> <body euler="0.0 -0.7853982 0.0" pos="0.0 0.0 1.0" name="leaf_(4)" /> <body euler="0.0 0.7853982 0.0" pos="0.0 0.0 1.0" name="leaf_(5)" /> </body> <body euler="0.0 0.7853982 0.0" pos="0.0 0.0 2.0" name="twig_(3)"> <body euler="0.0 -0.7853982 0.0" pos="0.0 0.0 1.0" name="leaf_(6)" /> <body euler="0.0 0.7853982 0.0" pos="0.0 0.0 1.0" name="leaf_(7)" /> </body> </body> </body> </body> .. code-block:: py :caption: Bottom-Up >>> tree = blue.Body(name='tree') >>> stem = blue.Body(name='stem', pos=[0, 0, 8]) >>> bran = blue.Body(name='branch', pos=[0, 0, 4]) >>> twig = blue.Body(name='twig', pos=[0, 0, 2]) >>> leaf = blue.Body(name='leaf', pos=[0, 0, 1]) >>> angle = TAU/8 >>> # starting at the leafs >>> twig.attach(leaf.rotate(beta=-angle), leaf.rotate(beta=angle)) >>> bran.attach(twig.rotate(beta=-angle), twig.rotate(beta=angle)) >>> stem.attach(bran.rotate(beta=-angle), bran.rotate(beta=angle)) >>> tree.attach(stem) .. code-block:: py :caption: Top-Down >>> tree = blue.Body(name='tree') >>> stem = blue.Body(name='stem', pos=[0, 0, 0]) >>> bran = blue.Body(name='branch', pos=[0, 0, 4]) >>> twig = blue.Body(name='twig', pos=[0, 0, 2]) >>> leaf = blue.Body(name='leaf', pos=[0, 0, 1]) >>> angle = TAU/8 >>> # starting at the stem >>> tree.attach(stem) >>> tree.bodies.attach(bran.rotate(beta=-angle), bran.rotate(beta=angle)) >>> tree.bodies.bodies.attach(twig.rotate(beta=-angle), twig.rotate(beta=angle)) >>> tree.bodies.bodies.bodies.attach(leaf.rotate(beta=-angle), leaf.rotate(beta=angle)) Attributes ---------- bodies, cameras, geoms, sites, joints, lights, cameras, actuators, placeholders : :class:`View <blueprints.utils.view.View>` All children that have been attached to the Body are retrieved by the attribute as a :class:`View <blueprints.utils.view.View>`. """
[docs] @blue.restrict def __init__(self, pos: list[int|float]|np.ndarray = [0., 0., 0.], alpha: int|float|None = 0., beta: int|float|None = 0., gamma: int|float|None = 0., geoms: list[blue.ThingType]|blue.ThingType|None = None, sites: list[blue.ThingType]|blue.ThingType|None = None, joints: list[blue.ThingType]|blue.ThingType|None = None, bodies: list[blue.ThingType]|blue.ThingType|None = None, lights: list[blue.ThingType]|blue.ThingType|None = None, cameras: list[blue.ThingType]|blue.ThingType|None = None, actuators: list[blue.ThingType]|blue.ThingType|None = None, placeholders: list[blue.ThingType]|blue.ThingType|None = None, name: str|None = None, copy: bool = True, x: int|float|np.int32|np.int64|np.float32|np.float64|None = None, y: int|float|np.int32|np.int64|np.float32|np.float64|None = None, z: int|float|np.int32|np.int64|np.float32|np.float64|None = None, quat: list[int|float]|np.ndarray|None = None, **kwargs) -> None: """ Parameters ---------- pos : list[int | float] | np.ndarray, optional Represents the position of the object. Changing this attribute also changes the properties :attr:`x`, :attr:`y` and :attr:`z`. alpha : int | float | None, optional (Improper) euler angle of rotation around the x-axis in radian. Changing this value also changes the :attr:`euler` property. beta : int | float | None, optional (Improper) euler angle of rotation around the y-axis in radian. Changing this value also changes the :attr:`euler` property. gamma : int | float | None, optional (Improper) euler angle of rotation around the z-axis in radian. Changing this value also changes the :attr:`euler` property. geoms : list[blue.ThingType] | blue.ThingType | None, optional The ``geoms`` argument mus either be a list of Geoms or a single Geom. sites : list[blue.ThingType] | blue.ThingType | None, optional The ``sites`` argument mus either be a list of Sites or a single Site. joints : list[blue.ThingType] | blue.ThingType | None, optional The ``joints`` argument mus either be a list of Joints or a single Joint. bodies : list[blue.ThingType] | blue.ThingType | None, optional The ``bodies`` argument mus either be a list of Bodies or a single Body. lights : list[blue.ThingType] | blue.ThingType | None, optional The ``lights`` argument mus either be a list of Lights or a single Light. cameras : list[blue.ThingType] | blue.ThingType | None, optional The ``cameras`` argument mus either be a list of Cameras or a single Camera. actuators : list[blue.ThingType] | blue.ThingType | None, optional The ``actuators`` argument mus either be a list of Actuators or a single Actuator. placeholders : list[blue.ThingType] | blue.ThingType | None, optional The ``placeholders`` argument mus either be a list of Placeholders or a single Placeholder. name : str | None, optional The user specified name of the Body. In the case of a naming conflict the name is appended by an enumeration scheme. copy : bool, optional This argument indicates whether the children should be copied before they are attached. If copy if set to False this will result in graph cycles if a parent of the Body is attached resulting in further errors. x : int | float |np.int32 | np.int64 | np.float32 | np.float64 | None, optional If `pos` is not specified, this argument sets the X position coordinate. y : int | float |np.int32 | np.int64 | np.float32 | np.float64 | None, optional If `pos` is not specified, this argument sets the Y position coordinate. z : int | float |np.int32 | np.int64 | np.float32 | np.float64 | None, optional If `pos` is not specified, this argument sets the Z position coordinate. quat: list [ int | float ] | np.ndarray | None, optional If set, the quaternion orientation overwrites the euler angles ``alpha``, ``beta`` and ``gamma``. **kwargs Keyword arguments are passed to ``super().__init__``. """ pack = lambda x: x if isinstance(x, list) else [x] geoms = pack(geoms) if geoms else [] joints = pack(joints) if joints else [] bodies = pack(bodies) if bodies else [] lights = pack(lights) if lights else [] sites = pack(sites) if sites else [] cameras = pack(cameras) if cameras else [] actuators = pack(actuators) if actuators else [] placeholders = pack(placeholders) if placeholders else [] self._geoms = [] self._joints = [] self._bodies = [] self._lights = [] self._sites = [] self._cameras = [] self._actuators = [] self._placeholders = [] self._targeting_lights = [] self._targeting_cameras = [] self._CHILDREN = {'lights': {'type': blue.LightType, 'children': self._lights}, 'joints': {'type': blue.JointType, 'children': self._joints}, 'geoms': {'type': blue.GeomType, 'children': self._geoms}, 'sites': {'type': blue.SiteType, 'children': self._sites}, 'cameras': {'type': blue.CameraType, 'children': self._cameras}, 'actuators': {'type': blue.ActuatorType, 'children': self._actuators}, 'placeholders': {'type': blue.PlaceholderType, 'children': self._placeholders}, 'bodies': {'type': blue.BodyType, 'children': self._bodies}} self._CYCLE_REF = {'targeting_lights': {'type': blue.CameraType, 'children': self._targeting_lights}, 'targeting_cameras': {'type': blue.CameraType, 'children': self._targeting_cameras}} super().__init__(pos=pos, x=x, y=y, z=z, alpha=alpha, beta=beta, gamma=gamma, quat=quat, name=name, **kwargs) self.attach(*geoms, *joints, *bodies, *sites, *lights, *cameras, *actuators, *placeholders, copy=copy) self._check_children_types()
@blue.restrict def _build(self, parent, world, indicies, **kwargs): """ This method is called to build the xml. Parameters ---------- parent : xml.etree.ElementTree.Element The xml element of its parent world : WorldType The World from which the build method was called initially indicies : dict All indecies used for retrieving data during the simulation. Returns ------- xml.etree.ElementTree.Element The builded xml element of the Thing. """ self._index = indicies['body'] indicies['body'] += 1 return super()._build(parent, world, indicies, **kwargs) # KINEMATIC TREES PROPERTIES/METHODS @blue.restrict @classmethod def _from_xml_element(cls, xml_element: xml.Element, actuators: list = []) -> blue.ThingType: """ This method reconstructs a Body from an xml element. Parameters ---------- xml_element : xml.Element The xml element from which a Body is reconstructed. Returns ------- blue.ThingType The reconstructed Body. """ body = super()._from_xml_element(xml_element) for actuator in actuators: body.attach(actuator, copy=False) actuator.body = body return body # KINEMATIC TREE PROPERTIES @property def targeting_cameras(self): """ Targeting Cameras contain :class:`Camera <blueprints.camera.Camera>` that track this Body. Returns ------- :class:`View <blueprints.utils.view.View>` """ return blue.View(self._targeting_cameras, name='targeting_cameras', parent=self) @targeting_cameras.setter @blue.restrict def targeting_cameras(self, cameras: blue.CameraType|list[blue.CameraType]|blue.ViewType): """ Parameters ---------- actuators : list[blue.ActuatorType] | blue.ViewType Targeting Cameras contain :class:`Camera <blueprints.camera.Camera>` that track this Body. """ cameras = [cameras] if isinstance(cameras, blue.CameraType) else cameras for camera in cameras: camera.target = self @property def targeting_lights(self): """ Targeting Lights contain :class:`Light <blueprints.light.Light>` that track this Body. Returns ------- :class:`blueprints.utils.view.View` """ return blue.View(self._targeting_lights, name='targeting_lights', parent=self) @targeting_lights.setter @blue.restrict def targeting_lights(self, lights: blue.LightType|list[blue.LightType]|blue.ViewType): """ Parameters ---------- actuators : list[blue.ActuatorType] | blue.ViewType Targeting Lights contain :class:`Light <blueprints.light.Light>` that track this Body. """ lights = [lights] if isinstance(lights, blue.LightType) else lights for light in lights: light.target = self