"""Classes and functions to interface the ODE physics engine with
the API defined in :mod:`physics`.
"""
from abc import ABCMeta
from ...constants import G_VECTOR
from ...lib.pydispatch import dispatcher
from ..collision import ode_adapter as shapes
from . import base, ode_objects_factories, signals
#==============================================================================
# Environment
#==============================================================================
[docs]class Engine(base.Engine):
"""Adapter to the ODE physics engine"""
world_class = None # we have to define it below World's definition
[docs]class World(base.World):
"""Adapter to :class:`ode.World`.
.. seealso::
Read abouth ERP and CFM in ODE's wiki
http://ode-wiki.org/wiki/index.php?title=Manual:_Concepts
"""
def __init__(self, gravity=G_VECTOR, ERP=0.2, CFM=1E-10, *args, **kwargs):
"""Constructor.
:param gravity: gravity acceleration vector
:type gravity: 3-sequence of floats
:param ERP: Error Reduction Parameter
:type ERP: float
:param CFM: Constraint Force Mixing
:type CFM: float
"""
super(World, self).__init__(gravity, *args, **kwargs)
self._inner_object = ode_objects_factories.create_ode_world(
gravity, ERP, CFM)
@property
[docs] def gravity(self):
return self._inner_object.getGravity()
[docs] def step(self, time_step):
dispatcher.send(signals.WORLD_PRE_STEP, sender=self)
self._inner_object.step(time_step)
dispatcher.send(signals.WORLD_POST_STEP, sender=self)
# This is a workaround necessary to solve the issue caused by the fact World
# is defined after Engine thus the latter can't use the former in its own
# definition (class attribute).
Engine.world_class = World
#==============================================================================
# Parents
#==============================================================================
[docs]class Body(object):
"""Abstract class, sort of equivalent to :class:`ode.Body`."""
__metaclass__ = ABCMeta
def __init__(self, world, space, mass=None, density=None, *args, **kwargs):
"""Constructor.
:param world:
:type world: :class:`.base.World`
:param space:
:type space: :class:`.collision.base.Space`
:param mass:
:type mass: float or None
:param density:
:type density: float or None
"""
self._inner_object = None
#==========================================================================
# Getters and setters
#==========================================================================
[docs] def get_position(self):
"""Get the position of the body.
:return: position
:rtype: 3-sequence of floats
"""
return self._inner_object.getPosition()
[docs] def get_linear_velocity(self):
return self._inner_object.getLinearVel()
[docs] def get_rotation(self):
"""Get the orientation of the body.
:return: rotation matrix
:rtype: 9-sequence of floats
"""
return self._inner_object.getRotation()
[docs] def get_angular_velocity(self):
return self._inner_object.getAngularVel()
[docs] def get_mass(self):
return self._inner_object.getMass().mass
[docs] def get_center_of_gravity(self):
return self._inner_object.getMass().c
[docs] def get_inertia_tensor(self):
return self._inner_object.getMass().I
[docs] def set_position(self, pos):
"""Set the position of the body.
Sends :data:`signals.BODY_PRE_SET_POSITION` and
:data:`signals.BODY_POST_SET_POSITION`.
:param pos: position
:type pos: 3-sequence of floats
"""
dispatcher.send(signals.BODY_PRE_SET_POSITION, sender=self)
self._inner_object.setPosition(pos)
dispatcher.send(signals.BODY_POST_SET_POSITION, sender=self)
[docs] def set_rotation(self, rot):
"""Set the orientation of the body.
Sends :data:`signals.BODY_PRE_SET_ROTATION` and
:data:`signals.BODY_POST_SET_ROTATION`.
:param rot: rotation matrix
:type rot: 9-sequence of floats
"""
dispatcher.send(signals.BODY_PRE_SET_ROTATION, sender=self)
self._inner_object.setRotation(rot)
dispatcher.send(signals.BODY_POST_SET_ROTATION, sender=self)
#==============================================================================
# Bodies
#==============================================================================
[docs]class Box(Body, base.Box):
def __init__(self, world, space, size, mass=None, density=None):
Body.__init__(self, world, space, mass, density)
base.Box.__init__(self, size, mass, density)
body = ode_objects_factories.create_ode_box(
world._inner_object, size, mass, density)
geom = shapes.Box(space, size)
self._inner_object = body
self.attach_geom(geom)
[docs]class Sphere(Body, base.Sphere):
def __init__(self, world, space, radius, mass=None, density=None):
Body.__init__(self, world, space, mass, density)
base.Sphere.__init__(self, radius, mass, density)
body = ode_objects_factories.create_ode_sphere(
world._inner_object, radius, mass, density)
geom = shapes.Sphere(space, radius)
self._inner_object = body
self.attach_geom(geom)
[docs]class Capsule(Body, base.Capsule):
def __init__(self, world, space, length, radius, mass=None, density=None):
"""create capsule body (aligned along the z-axis so that it matches the
Geom created below, which is aligned along the Z-axis by default)"""
Body.__init__(self, world, space, mass, density)
base.Capsule.__init__(self, length, radius, mass, density)
body = ode_objects_factories.create_ode_capsule(
world._inner_object, length, radius, mass, density)
geom = shapes.Capsule(space, length, radius)
self._inner_object = body
self.attach_geom(geom)
[docs]class Cylinder(Body, base.Cylinder):
def __init__(self, world, space, length, radius, mass=None, density=None):
"""create cylinder body (aligned along the z-axis so that it matches
the Geom created below, which is aligned along the Z-axis by default)"""
Body.__init__(self, world, space, mass, density)
base.Cylinder.__init__(self, length, radius, mass, density)
body = ode_objects_factories.create_ode_cylinder(
world._inner_object, length, radius, mass, density)
geom = shapes.Cylinder(space, length, radius)
self._inner_object = body
self.attach_geom(geom)