# #####
# This file is part of the RobotDesigner of the Neurorobotics subproject (SP10)
# in the Human Brain Project (HBP).
# It has been forked from the RobotEditor (https://gitlab.com/h2t/roboteditor)
# developed at the Karlsruhe Institute of Technology in the
# High Performance Humanoid Technologies Laboratory (H2T).
# #####
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# #####
#
# Copyright (c) 2015, Karlsruhe Institute of Technology (KIT)
# Copyright (c) 2016, FZI Forschungszentrum Informatik
#
# Changes:
#
# 2015-01-16: Stefan Ulbrich (FZI), Major refactoring. Integrated into complex plugin framework.
#
# ######
# System imports
import math
import mathutils
# Blender imports
import bpy
from bpy.props import StringProperty
from ..core import Condition
from ..core.constants import StringConstants
from ..core import RDOperator
def _vec_roll_to_mat3(vec, roll):
"""
Function to convert a given rotation vector and a roll angle along this axis into a 3x3 rotation matrix
Python port of the C function defined in armature.c
Credits: blenderartists.org user vida_vida
:param vec:
:param roll:
:return:
"""
target = mathutils.Vector((0, 1, 0))
nor = vec.normalized()
axis = target.cross(nor)
if (
axis.dot(axis) > 0.0000000001
): # this seems to be the problem for some bones, no idea how to fix
axis.normalize()
theta = target.angle(nor)
bMatrix = mathutils.Matrix.Rotation(theta, 3, axis)
else:
updown = 1 if target.dot(nor) > 0 else -1
bMatrix = mathutils.Matrix.Scale(updown, 3)
# C code:
# bMatrix[0][0]=updown; bMatrix[1][0]=0.0; bMatrix[2][0]=0.0;
# bMatrix[0][1]=0.0; bMatrix[1][1]=updown; bMatrix[2][1]=0.0;
# bMatrix[0][2]=0.0; bMatrix[1][2]=0.0; bMatrix[2][2]=1.0;
bMatrix[2][2] = 1.0
rMatrix = mathutils.Matrix.Rotation(roll, 3, nor)
mat = rMatrix @ bMatrix
return mat
def _mat3_to_vec_roll(mat):
"""
Function to convert a 3x3 rotation matrix to a rotation axis and a roll angle along this axis
Python port of the C function defined in armature.c
Thanks to blenderartists.org user vida_vida
:param mat:
:return:
"""
from ..properties.globals import global_properties
vec = mat.col[1] * global_properties.bone_length.get(bpy.context.scene)
vecmat = _vec_roll_to_mat3(mat.col[1], 0)
vecmatinv = vecmat.inverted()
rollmat = vecmatinv @ mat
roll = math.atan2(rollmat[0][2], rollmat[2][2])
return vec, roll
[docs]class ModelSelected(Condition):
[docs] @staticmethod
def check():
"""
:ref:`condition` that assures the armature is selected.
:return: True if the condition is met, else false. String with error message.
"""
if bpy.context.active_object:
return (
bpy.context.active_object.type == "ARMATURE",
"Model not selected and active.",
)
else:
return False, "No model selected"
[docs]class SingleSegmentSelected(Condition):
[docs] @staticmethod
def check():
"""
:ref:`condition` that assures that a single mesh object (i.e., two selected objects where exactly one is a mesh).
:return: True if the condition is met, else false. String with error message.
"""
if bpy.context.active_bone:
selected_segments = [
i for i in bpy.context.active_object.data.bones if i.select
]
return len(selected_segments) == 1, "Single Segment must be selected"
else:
return False, "No Object Selected"
[docs]class WorldSelected(Condition):
[docs] @staticmethod
def check():
"""
:ref:`condition` that assures that a single world object.
:return: True if the condition is met, else false. String with error message.
"""
from ..properties.globals import global_properties
world = global_properties.world_name.get(bpy.context.scene)
if world != "" and world != "Select World":
if bpy.data.objects[world]:
return (
bpy.data.objects[world].RobotDesigner.tag == 'WORLD',
"Single world object must be selected."
)
else:
return False, "No world selected"
# return world != "" and bpy.data.objects[world].RobotDesigner.tag == 'WORLD', "Single world object must be selected."
# if bpy.context.active_object:
# return (
# bpy.context.active_object.RobotDesigner.tag == "WORLD",
# "World not selected and active.",
# )
# else:
# return False, "No world selected"
[docs]class AtLeastOneSegmentSelected(Condition):
[docs] @staticmethod
def check():
"""
:ref:`condition` that assures that at least one bone of the active object is selected
:return: True if the condition is met, else false. String with error message.
"""
if bpy.context.active_bone:
selected_segments = [
i for i in bpy.context.active_object.data.bones if i.select
]
return len(selected_segments) >= 1, "At least one segment must be selected"
else:
return False, "No Object Selected"
[docs]class SingleMeshSelected(Condition):
[docs] @staticmethod
def check():
"""
:ref:`condition` that assures that a single mesh object (i.e., two selected objects where exactly one is a mesh).
:return: True if the condition is met, else false. String with error message.
"""
selected = [i for i in bpy.context.selected_objects if i.type == "MESH"]
return len(selected) == 1, "Single mesh object must be selected."
[docs]class SingleSensorSelected(Condition):
[docs] @staticmethod
def check():
"""
:ref:`condition` that assures that a single sensor object ist selected.
:return: True if the condition is met, else false. String with error message.
"""
selected = [i for i in bpy.context.selected_objects if i.RobotDesigner.tag == "SENSOR"]
return len(selected) == 1, "Single sensor object must be selected."
[docs]class ObjectMode(Condition):
[docs] @staticmethod
def check():
"""
:term:`condition` that assures that the :term:`object mode` is selected.
"""
if bpy.context.object:
return bpy.context.object.mode == "OBJECT", "Must be in object mode"
else:
return True, ""
[docs]class PoseMode(Condition):
[docs] @staticmethod
def check():
"""
:term:`condition` that assures that the :term:`pose mode` is selected.
"""
if bpy.context.object:
return bpy.context.object.mode == "POSE", "Must be in Pose Mode"
else:
return True, ""
[docs]class NotEditMode(Condition):
[docs] @staticmethod
def check():
"""
:term:`condition` that assures that the :term:`edit mode` is *not* selected.
"""
if bpy.context.object:
return bpy.context.object.mode != "EDIT", "Must not be in Edit Mode"
else:
return True, ""
[docs]class SingleCameraSelected(Condition):
[docs] @staticmethod
def check():
"""
:term:`condition` that assures that a :class:`bpy.types.Camera` associated object is selected.
"""
selected = [
i for i in bpy.context.selected_objects if i.type == StringConstants.camera
]
return len(selected) == 1, "Single camera object must be selected."
[docs]class SingleMassObjectSelected(Condition):
[docs] @staticmethod
def check():
"""
:term:`condition` that assures that a :class:`bpy.types.Camera` associated object is selected.
"""
selected = [
i
for i in bpy.context.selected_objects
if i.type == StringConstants.empty
and i.RobotDesigner.tag == "PHYSICS_FRAME"
]
return len(selected) == 1, "Single mass object must be selected."
[docs]class SelectObjectBase(RDOperator):
"""
Base class for :ref:`operators <operator>` for selecting an object (:class:`bpy.types.Object`)
second to the selected model (Blender object with :class:`bpy.types.Armature` data)
"""
object_name = StringProperty()
object_data_type = "MESH"
[docs] @classmethod
def run(cls, object_name=""):
return super().run(**cls.pass_keywords())
@RDOperator.OperatorLogger
def execute(self, context):
mesh = bpy.data.objects[self.object_name]
arm = context.active_object
for obj in context.scene.objects:
obj.select_set(False)
mesh.select_set(True)
arm.select_set(True)
return {"FINISHED"}
[docs]class AssignObjectBase(RDOperator):
"""
Base class for :ref:`operators <operator>` for assigning an object to the currently selected
segment segment.
"""
[docs] @classmethod
def run(cls):
return super().run(**cls.pass_keywords())
@RDOperator.OperatorLogger
def execute(self, context):
bpy.ops.object.parent_set(type="BONE", keep_transform=True)
return {"FINISHED"}
[docs]class ObjectScaled(Condition):
[docs] @staticmethod
def check():
"""
:term:`condition` that assures that model has been scaled before the operation.
"""
if bpy.context.active_object:
return (
bpy.context.active_object.scale.x
== bpy.context.active_object.scale.y
== bpy.context.active_object.scale.z
== 1.0
), "Object has to be scaled!"
else:
return True, ""