Source code for morse.core.object
import logging; logger = logging.getLogger("morse." + __name__)
from abc import ABCMeta, abstractmethod
from collections import OrderedDict
from morse.core.abstractobject import AbstractObject
import morse.helpers.transformation
from morse.core.services import service
from morse.core import blenderapi
[docs]class Object(AbstractObject):
""" Basic Class for all 3D objects (components) used in the simulation.
Provides common attributes.
"""
# Make this an abstract class
__metaclass__ = ABCMeta
def __init__ (self, obj, parent=None):
AbstractObject.__init__(self)
# Fill in the data sent as parameters
self.bge_object = obj
self.robot_parent = parent
self.level = self.bge_object.get("abstraction_level", "default")
# Variable to indicate the activation status of the component
self._active = True
self.check_level()
# Define the position of sensors with respect
# to their robot parent
# TODO: implement this using morse.helpers.transformation
if parent:
self.relative_position = obj.getVectTo(parent.bge_object)
# Create an instance of the 3d transformation class
self.position_3d = morse.helpers.transformation.Transformation3d(obj)
self.initialize_local_data()
self.update_properties()
# The actual frequency at which the action is called
# The frequency of the game sensor specifies how many times
# the action is skipped when the logic brick is executed.
# e.g. game sensor frequency = 0 -> sensor runs at full logic rate
sensors = blenderapi.getalwayssensors(obj)
self._frequency = blenderapi.getfrequency()
# New MORSE_LOGIC sensor, see AbstractComponent.morseable()
morselogic = [s for s in sensors if s.name.startswith('MORSE_LOGIC')]
if len(morselogic) == 1:
self._frequency /= morselogic[0].frequency + 1
# Backward compatible (some actuators got special logic)
elif len(sensors) == 1:
self._frequency /= sensors[0].frequency + 1
elif len(sensors) == 0:
logger.warning("Can't get frequency for " + self.name() + \
" as the Game Logic sensor calling the action can't be found.")
else:
logger.warning(self.name() + " has too many Game Logic sensors to get " + \
"an unambiguous frequency for the action.")
[docs] def check_level(self):
if self.level == "default":
return # fine
if hasattr(self, '_levels') and self.level in self._levels:
return #fine
msg = "Component <%s> has no abstraction level <%s>. Please check your scene." % (self.name(), self.level)
logger.error(msg)
raise ValueError(msg)
[docs] def initialize_local_data(self):
"""
Creates and initializes 'local data' fields, according to the
current component abstraction level.
"""
all_data_fields = OrderedDict()
for cls in reversed(type(self).__mro__):
if hasattr(cls, '_data_fields'):
all_data_fields.update(cls._data_fields)
for name, details in all_data_fields.items():
default_value, type_, doc, level = details
if level == "all" or self.level in level:
self.local_data[name] = default_value
[docs] def fetch_properties(self):
"""
Returns the "_properties" of a component
:return: a dictionary of the field "_properties"
"""
all_properties = OrderedDict()
#fetches '_properties'
for cls in reversed(type(self).__mro__):
if hasattr(cls, '_properties'):
all_properties.update(cls._properties)
return all_properties
@service
[docs] def get_properties(self):
"""
Returns the properties of a component.
:return: a dictionary of the current component's properties
"""
all_properties = self.fetch_properties()
return {'properties': all_properties}
@service
[docs] def get_configurations(self):
"""
Returns the configurations of a component (parsed from the properties).
:return: a dictionary of the current component's configurations
"""
all_properties = self.fetch_properties()
tmp = {}
#parses 'all_properties' to get only "key"-"value"-pairs
#"key" is python_name and "value" is default_value
for item in all_properties.items():
tmp[item[0]] = getattr(self, item[1][3])
transform = self.robot_parent.position_3d.transformation3d_with(self.position_3d)
rotation = [ list(vec) for vec in transform.rotation_matrix ]
translation = list(transform.translation)
tmp['object_to_robot'] = {'rotation': rotation, 'translation': translation}
return {'configurations': tmp}
[docs] def update_properties(self):
"""
Takes all registered properties (see add_property), and update
their values according to the values set in Blender object.
"""
all_properties = self.fetch_properties()
for name, details in all_properties.items():
default_value, _, _, python_name = details
val = default_value
try:
val = self.bge_object[name]
except KeyError:
pass
setattr(self, python_name, val)
[docs] def name(self):
return self.bge_object.name
[docs] def action(self):
""" Call the regular action function of the component.
Can be redefined in some of the subclases (sensor and actuator).
"""
self.default_action()
[docs] def in_zones(self, name = None, type = None):
"""
Determine which zone(s) contain(s) current object
If a :param name: is precised, check only if this specific zone
contains the position
If a :param type: is precised, only search in the zone of this
type.
"""
zone_manager = blenderapi.persistantstorage().zone_manager
return zone_manager.contains(self, name = name, type = type)
@abstractmethod
[docs] def default_action(self):
""" Base action performed by any object.
This method should be implemented by all subclasses that
will be instanced (GPS, v_Omega, ATRV, etc.).
"""
pass
[docs] def toggle_active(self):
self._active = not self._active
@property
[docs] def frequency(self):
""" Frequency of the object action in Hz (float). """
return self._frequency