mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +00:00
Annotation rotation when doing bulk tags (#7049)
* first attempt to do annotation rotation when dong bulk tags * Enhance annotation rotation handling based on camera orientation * handle properly S_G * black . * reorder property in UI * cleanup imports
This commit is contained in:
@@ -864,6 +864,19 @@ class BIMAnnotationProperties(PropertyGroup):
|
|||||||
)
|
)
|
||||||
is_adding_type: bpy.props.BoolProperty(default=False)
|
is_adding_type: bpy.props.BoolProperty(default=False)
|
||||||
type_name: bpy.props.StringProperty(name="Name", default="TYPEX")
|
type_name: bpy.props.StringProperty(name="Name", default="TYPEX")
|
||||||
|
tag_rotation_mode: bpy.props.EnumProperty(
|
||||||
|
name="Tag Rotation Mode",
|
||||||
|
description="How to orient the tag relative to the tagged object",
|
||||||
|
items=[
|
||||||
|
("NONE", "No Rotation", "Keep tag in default orientation"),
|
||||||
|
("LOCAL_X", "Local X Axis", "Align tag with object's local X axis"),
|
||||||
|
("LOCAL_Y", "Local Y Axis", "Align tag with object's local Y axis"),
|
||||||
|
("LOCAL_Z", "Local Z Axis", "Align tag with object's local Z axis"),
|
||||||
|
("CAMERA_Horizontal", "Camera Horizontal", "Align tag with camera X axis"),
|
||||||
|
("CAMERA_Vertical", "Camera Vertical", "Align tag with camera Y axis"),
|
||||||
|
],
|
||||||
|
default="NONE",
|
||||||
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
object_type: str
|
object_type: str
|
||||||
|
|||||||
@@ -228,7 +228,6 @@ class AnnotationToolUI:
|
|||||||
def draw_type_selection_interface(cls):
|
def draw_type_selection_interface(cls):
|
||||||
# shared by both sidebar and header
|
# shared by both sidebar and header
|
||||||
object_type = cls.props.object_type
|
object_type = cls.props.object_type
|
||||||
|
|
||||||
row = cls.layout.row(align=True)
|
row = cls.layout.row(align=True)
|
||||||
row.label(text="", icon="FILE_VOLUME")
|
row.label(text="", icon="FILE_VOLUME")
|
||||||
prop_with_search(row, cls.props, "object_type", text="")
|
prop_with_search(row, cls.props, "object_type", text="")
|
||||||
@@ -248,6 +247,9 @@ class AnnotationToolUI:
|
|||||||
add_layout_hotkey_operator(cls.layout, "Add", "S_A", "Create a new annotation")
|
add_layout_hotkey_operator(cls.layout, "Add", "S_A", "Create a new annotation")
|
||||||
|
|
||||||
if object_type in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP:
|
if object_type in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP:
|
||||||
|
row = cls.layout.row(align=True)
|
||||||
|
row.label(text="", icon="DRIVER_ROTATIONAL_DIFFERENCE")
|
||||||
|
row.prop(cls.props, "tag_rotation_mode", text="")
|
||||||
add_layout_hotkey_operator(
|
add_layout_hotkey_operator(
|
||||||
cls.layout,
|
cls.layout,
|
||||||
"Bulk Tag",
|
"Bulk Tag",
|
||||||
@@ -313,7 +315,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
),
|
),
|
||||||
enable_editing=False,
|
enable_editing=False,
|
||||||
)
|
)
|
||||||
tool.Drawing.setup_annotation_object(obj, object_type, related_object)
|
tool.Drawing.setup_annotation_object(obj, object_type, related_object, props.tag_rotation_mode)
|
||||||
created_objects.append(obj)
|
created_objects.append(obj)
|
||||||
|
|
||||||
# Select the created annotation objects
|
# Select the created annotation objects
|
||||||
@@ -323,6 +325,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
if created_objects:
|
if created_objects:
|
||||||
bpy.context.view_layer.objects.active = created_objects[-1]
|
bpy.context.view_layer.objects.active = created_objects[-1]
|
||||||
|
|
||||||
|
|
||||||
def hotkey_S_A(self):
|
def hotkey_S_A(self):
|
||||||
if bpy.ops.bim.add_annotation.poll():
|
if bpy.ops.bim.add_annotation.poll():
|
||||||
bpy.ops.bim.add_annotation()
|
bpy.ops.bim.add_annotation()
|
||||||
@@ -351,4 +354,5 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
continue
|
continue
|
||||||
related_object = tool.Ifc.get_object(related_product)
|
related_object = tool.Ifc.get_object(related_product)
|
||||||
|
|
||||||
tool.Drawing.setup_annotation_object(obj, annotation_type, related_object)
|
rotation_mode = tool.Drawing.get_annotation_props().tag_rotation_mode
|
||||||
|
tool.Drawing.setup_annotation_object(obj, annotation_type, related_object, rotation_mode)
|
||||||
|
|||||||
@@ -251,7 +251,11 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_annotation_object(
|
def setup_annotation_object(
|
||||||
cls, obj: bpy.types.Object, object_type: str, related_object: Optional[bpy.types.Object] = None
|
cls,
|
||||||
|
obj: bpy.types.Object,
|
||||||
|
object_type: str,
|
||||||
|
related_object: Optional[bpy.types.Object] = None,
|
||||||
|
rotation_mode: str = "NONE",
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Finish object's adjustments after both object and entity are created"""
|
"""Finish object's adjustments after both object and entity are created"""
|
||||||
|
|
||||||
@@ -323,6 +327,55 @@ class Drawing(bonsai.core.tool.Drawing):
|
|||||||
ifc_file, relating_product=related_entity, related_object=obj_entity
|
ifc_file, relating_product=related_entity, related_object=obj_entity
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if rotation_mode != "NONE" and related_object:
|
||||||
|
cls.apply_annotation_rotation(obj, related_object, rotation_mode)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def apply_annotation_rotation(
|
||||||
|
cls, tag_obj: bpy.types.Object, related_object: bpy.types.Object, rotation_mode: str
|
||||||
|
) -> None:
|
||||||
|
"""Apply rotation to annotation based on the selected rotation mode"""
|
||||||
|
camera = bpy.context.scene.camera
|
||||||
|
camera_right = camera.matrix_world.to_3x3() @ mathutils.Vector((1, 0, 0))
|
||||||
|
|
||||||
|
if rotation_mode == "CAMERA_Horizontal":
|
||||||
|
location = tag_obj.location.copy()
|
||||||
|
camera_matrix = camera.matrix_world.copy()
|
||||||
|
camera_matrix.translation = location
|
||||||
|
tag_obj.matrix_world = camera_matrix
|
||||||
|
|
||||||
|
elif rotation_mode == "CAMERA_Vertical":
|
||||||
|
location = tag_obj.location.copy()
|
||||||
|
camera_matrix = camera.matrix_world.copy()
|
||||||
|
camera_matrix.translation = location
|
||||||
|
rotation_90z = mathutils.Matrix.Rotation(math.pi / 2, 4, "Z")
|
||||||
|
camera_matrix = camera_matrix @ rotation_90z
|
||||||
|
tag_obj.matrix_world = camera_matrix
|
||||||
|
|
||||||
|
elif rotation_mode == "LOCAL_X":
|
||||||
|
local_x = related_object.matrix_world.to_3x3() @ mathutils.Vector((1, 0, 0))
|
||||||
|
local_x = local_x.normalized()
|
||||||
|
if local_x.dot(camera_right) < 0:
|
||||||
|
local_x = -local_x
|
||||||
|
|
||||||
|
tag_obj.rotation_euler = local_x.to_track_quat("X", "Z").to_euler()
|
||||||
|
|
||||||
|
elif rotation_mode == "LOCAL_Y":
|
||||||
|
local_y = related_object.matrix_world.to_3x3() @ mathutils.Vector((0, 1, 0))
|
||||||
|
local_y = local_y.normalized()
|
||||||
|
if local_y.dot(camera_right) < 0:
|
||||||
|
local_y = -local_y
|
||||||
|
|
||||||
|
tag_obj.rotation_euler = local_y.to_track_quat("X", "Z").to_euler()
|
||||||
|
|
||||||
|
elif rotation_mode == "LOCAL_Z":
|
||||||
|
local_z = related_object.matrix_world.to_3x3() @ mathutils.Vector((0, 0, 1))
|
||||||
|
local_z = local_z.normalized()
|
||||||
|
if local_z.dot(camera_right) < 0:
|
||||||
|
local_z = -local_z
|
||||||
|
|
||||||
|
tag_obj.rotation_euler = local_z.to_track_quat("X", "Z").to_euler()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_annotation_object_type(
|
def is_annotation_object_type(
|
||||||
cls, element: ifcopenshell.entity_instance, object_types: Union[str, Sequence[str]]
|
cls, element: ifcopenshell.entity_instance, object_types: Union[str, Sequence[str]]
|
||||||
|
|||||||
Reference in New Issue
Block a user