New UI List in Sandbox panel to access, flip, remove, add, edit clipping planes

and some tweaks :
- Disable possibility to edit clipping plane quad geometry
- Add select and delete operators
- Remove polls from CreateClippingPlane that prevented from using it in the Properties editor. The clipping plane is added near the origin when using the Add button in the panel
This commit is contained in:
Gorgious
2024-04-08 21:31:23 +02:00
parent 163f5f2611
commit 69d51283c7
7 changed files with 109 additions and 27 deletions
@@ -97,8 +97,10 @@ for name in modules.keys():
classes = [ classes = [
operator.AddIfcFile, operator.AddIfcFile,
operator.BIM_OT_add_section_plane, operator.BIM_OT_add_section_plane,
operator.BIM_OT_delete_object,
operator.BIM_OT_open_webbrowser, operator.BIM_OT_open_webbrowser,
operator.BIM_OT_remove_section_plane, operator.BIM_OT_remove_section_plane,
operator.BIM_OT_select_object,
operator.BIM_OT_show_description, operator.BIM_OT_show_description,
operator.ClippingPlaneCutWithCappings, operator.ClippingPlaneCutWithCappings,
operator.EditBlenderCollection, operator.EditBlenderCollection,
@@ -131,6 +133,7 @@ classes = [
prop.BIMMeshProperties, prop.BIMMeshProperties,
prop.BIMFacet, prop.BIMFacet,
prop.BIMFilterGroup, prop.BIMFilterGroup,
ui.BIM_UL_clipping_plane,
ui.BIM_UL_generic, ui.BIM_UL_generic,
ui.BIM_UL_topics, ui.BIM_UL_topics,
ui.BIM_ADDON_preferences, ui.BIM_ADDON_preferences,
@@ -1015,7 +1015,9 @@ class DuplicateMoveLinkedAggregate(bpy.types.Operator):
return return
linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.group_name) linked_aggregate_group = ifcopenshell.api.run("group.add_group", tool.Ifc.get(), Name=self.group_name)
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group) ifcopenshell.api.run(
"group.assign_group", tool.Ifc.get(), products=[element], group=linked_aggregate_group
)
def custom_incremental_naming_for_element_assembly(old_to_new): def custom_incremental_naming_for_element_assembly(old_to_new):
for new in old_to_new.values(): for new in old_to_new.values():
@@ -1271,7 +1273,9 @@ class RefreshLinkedAggregate(bpy.types.Operator):
tool.Ifc.get_object(base_instance).select_set(True) tool.Ifc.get_object(base_instance).select_set(True)
old_to_new = DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(self, context) old_to_new = DuplicateMoveLinkedAggregate.execute_ifc_duplicate_linked_aggregate_operator(
self, context
)
for old, new in old_to_new.items(): for old, new in old_to_new.items():
new_obj = tool.Ifc.get_object(new[0]) new_obj = tool.Ifc.get_object(new[0])
new_base_matrix = Matrix.LocRotScale(*duplicate_matrix) new_base_matrix = Matrix.LocRotScale(*duplicate_matrix)
@@ -1443,7 +1447,9 @@ class OverrideModeSetEdit(bpy.types.Operator):
for obj in selected_objs: for obj in selected_objs:
if not obj: if not obj:
continue continue
if not obj.data: obj_supports_edit_mode, message = tool.Blender.object_supports_edit_mode(obj)
if not obj_supports_edit_mode:
self.report({"INFO"}, message)
obj.select_set(False) obj.select_set(False)
continue continue
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
@@ -1920,32 +1920,32 @@ class CreateClippingPlane(bpy.types.Operator):
bl_label = "Create Clipping Plane" bl_label = "Create Clipping Plane"
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
return context.area.type == "VIEW_3D"
def execute(self, context): def execute(self, context):
from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d
# Clean up deleted planes # Clean up deleted planes
if len(bpy.context.scene.BIMProjectProperties.clipping_planes) > 5: if len(context.scene.BIMProjectProperties.clipping_planes) > 5:
self.report({"INFO"}, "Maximum of six clipping planes allowed.") self.report({"INFO"}, "Maximum of six clipping planes allowed.")
return {"FINISHED"} return {"FINISHED"}
for area in bpy.context.screen.areas: for area in context.screen.areas:
if area.type == "VIEW_3D": if area.type == "VIEW_3D":
area.tag_redraw() area.tag_redraw()
region = context.region region = context.region
rv3d = context.region_data rv3d = context.region_data
coord = (self.mouse_x, self.mouse_y) if rv3d: # Called from a 3D viewport
origin = region_2d_to_origin_3d(region, rv3d, coord) coord = (self.mouse_x, self.mouse_y)
direction = region_2d_to_vector_3d(region, rv3d, coord) origin = region_2d_to_origin_3d(region, rv3d, coord)
hit, location, normal, face_index, obj, matrix = self.ray_cast(context, origin, direction) direction = region_2d_to_vector_3d(region, rv3d, coord)
if not hit: hit, location, normal, face_index, obj, matrix = self.ray_cast(context, origin, direction)
self.report({"INFO"}, "No object found.") if not hit:
return {"FINISHED"} self.report({"INFO"}, "No object found.")
return {"FINISHED"}
else: # Not Called from a 3D viewport
location = (0, 0, 1)
normal = (0, 0, 1)
vertices = [(-0.5, -0.5, 0), (0.5, -0.5, 0), (0.5, 0.5, 0), (-0.5, 0.5, 0)] vertices = [(-0.5, -0.5, 0), (0.5, -0.5, 0), (0.5, 0.5, 0), (-0.5, 0.5, 0)]
@@ -1956,20 +1956,20 @@ class CreateClippingPlane(bpy.types.Operator):
mesh.update() mesh.update()
plane_obj = bpy.data.objects.new("ClippingPlane", mesh) plane_obj = bpy.data.objects.new("ClippingPlane", mesh)
bpy.context.collection.objects.link(plane_obj) context.collection.objects.link(plane_obj)
z_axis = Vector((0, 0, 1)) z_axis = Vector((0, 0, 1))
rotation_matrix = z_axis.rotation_difference(normal).to_matrix().to_4x4() rotation_matrix = z_axis.rotation_difference(normal).to_matrix().to_4x4()
plane_obj.matrix_world = rotation_matrix plane_obj.matrix_world = rotation_matrix
plane_obj.matrix_world.translation = location plane_obj.matrix_world.translation = location
bpy.context.scene.cursor.location = location context.scene.cursor.location = location
new = bpy.context.scene.BIMProjectProperties.clipping_planes.add() new = context.scene.BIMProjectProperties.clipping_planes.add()
new.obj = plane_obj new.obj = plane_obj
tool.Blender.set_active_object(plane_obj) tool.Blender.set_active_object(plane_obj)
ClippingPlaneDecorator.install(bpy.context) ClippingPlaneDecorator.install(context)
bpy.ops.bim.refresh_clipping_planes("INVOKE_DEFAULT") bpy.ops.bim.refresh_clipping_planes("INVOKE_DEFAULT")
return {"FINISHED"} return {"FINISHED"}
@@ -1991,11 +1991,11 @@ class FlipClippingPlane(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return context.area.type == "VIEW_3D" return context.active_object
def execute(self, context): def execute(self, context):
obj = bpy.context.active_object obj = context.active_object
if obj in [cp.obj for cp in bpy.context.scene.BIMProjectProperties.clipping_planes]: if obj in context.scene.BIMProjectProperties.clipping_planes_objs:
obj.rotation_euler[0] += radians(180) obj.rotation_euler[0] += radians(180)
bpy.context.view_layer.update() context.view_layer.update()
return {"FINISHED"} return {"FINISHED"}
@@ -187,6 +187,11 @@ class BIMProjectProperties(PropertyGroup):
use_relative_project_path: BoolProperty(name="Use Relative Project Path", default=False) use_relative_project_path: BoolProperty(name="Use Relative Project Path", default=False)
queried_obj: bpy.props.PointerProperty(type=bpy.types.Object) queried_obj: bpy.props.PointerProperty(type=bpy.types.Object)
clipping_planes: bpy.props.CollectionProperty(type=ObjProperty) clipping_planes: bpy.props.CollectionProperty(type=ObjProperty)
clipping_planes_active: bpy.props.IntProperty(min=0, default=0, max=5)
@property
def clipping_planes_objs(self):
return list({cp.obj for cp in self.clipping_planes if cp.obj})
def get_library_element_index(self, lib_element): def get_library_element_index(self, lib_element):
return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element)) return next((i for i in range(len(self.library_elements)) if self.library_elements[i] == lib_element))
+25
View File
@@ -936,6 +936,31 @@ class BIM_OT_enum_property_search(bpy.types.Operator):
) )
class BIM_OT_select_object(bpy.types.Operator):
bl_idname = "bim.select_object"
bl_label = "Select Object"
bl_options = {"REGISTER", "UNDO"}
obj_name: bpy.props.StringProperty(description="Object Name To Select")
def execute(self, context):
obj = bpy.data.objects[self.obj_name]
tool.Blender.set_objects_selection(context, obj, [obj], clear_previous_selection=True)
return {"FINISHED"}
class BIM_OT_delete_object(bpy.types.Operator):
bl_idname = "bim.delete_object"
bl_label = "Delete Object"
bl_options = {"REGISTER", "UNDO"}
obj_name: bpy.props.StringProperty(description="Object Name To Delete")
def execute(self, context):
obj = bpy.data.objects[self.obj_name]
with context.temp_override(selected_objects=[obj], active_object=obj):
bpy.ops.bim.override_object_delete(is_batch=False)
return {"FINISHED"}
class EditBlenderCollection(bpy.types.Operator): class EditBlenderCollection(bpy.types.Operator):
bl_idname = "bim.edit_blender_collection" bl_idname = "bim.edit_blender_collection"
bl_label = "Add or Remove Blender Collection Item" bl_label = "Add or Remove Blender Collection Item"
+34
View File
@@ -127,6 +127,40 @@ class BIM_PT_section_with_cappings(Panel):
row.operator("bim.clipping_plane_cut_with_cappings", icon="XRAY", text="Cut") row.operator("bim.clipping_plane_cut_with_cappings", icon="XRAY", text="Cut")
row.operator("bim.revert_clipping_plane_cut", icon="FILE_REFRESH", text="Revert Cut") row.operator("bim.revert_clipping_plane_cut", icon="FILE_REFRESH", text="Revert Cut")
props = context.scene.BIMProjectProperties
box = layout.box()
header = box.row(align=True)
header.label(text="Clipping Planes")
header.operator("bim.create_clipping_plane", text="", icon="ADD")
box.template_list(
"BIM_UL_clipping_plane",
"",
props,
"clipping_planes",
props,
"clipping_planes_active",
)
if props.clipping_planes_active < len(props.clipping_planes):
active_clipping_plane_obj = props.clipping_planes[props.clipping_planes_active].obj
box.prop(active_clipping_plane_obj, "location")
box.prop(active_clipping_plane_obj, "rotation_euler")
class BIM_UL_clipping_plane(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
if item:
obj = item.obj
row = layout.row(align=True)
row.prop(obj, "name", text="", emboss=False)
row.operator("bim.select_object", text="", icon="RESTRICT_SELECT_OFF").obj_name = obj.name
row.context_pointer_set("active_object", obj)
row.operator("bim.flip_clipping_plane", text="", icon="PASTEFLIPDOWN")
row.operator("bim.delete_object", text="", icon="TRASH").obj_name = obj.name
else:
layout.label(text="", translate=False)
class BIM_UL_generic(bpy.types.UIList): class BIM_UL_generic(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
+12 -3
View File
@@ -689,6 +689,14 @@ class Blender(blenderbim.core.tool.Blender):
return collections_mapping return collections_mapping
@classmethod
def object_supports_edit_mode(cls, obj):
if not obj.data:
return False, "Can't Edit Empty Object"
if obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
return False, "Can't Edit Clipping Plane Geometry"
return True, ""
class Modifier: class Modifier:
@classmethod @classmethod
def is_eligible_for_railing_modifier(cls, obj): def is_eligible_for_railing_modifier(cls, obj):
@@ -790,8 +798,7 @@ class Blender(blenderbim.core.tool.Blender):
@classmethod @classmethod
def get_modifiers_data(cls, parent_element): def get_modifiers_data(cls, parent_element):
array_pset = ifcopenshell.util.element.get_pset(parent_element, "BBIM_Array") array_pset = ifcopenshell.util.element.get_pset(parent_element, "BBIM_Array")
for modifier_data in json.loads(array_pset["Data"]): yield from json.loads(array_pset["Data"])
yield modifier_data
@classmethod @classmethod
def get_children_objects(cls, modifier_data): def get_children_objects(cls, modifier_data):
@@ -838,7 +845,9 @@ class Blender(blenderbim.core.tool.Blender):
bpy.utils.register_tool(ws_model.PipeTool, after={"bim.duct_tool"}, separator=False, group=False) bpy.utils.register_tool(ws_model.PipeTool, after={"bim.duct_tool"}, separator=False, group=False)
bpy.utils.register_tool(ws_model.BimTool, after={"bim.pipe_tool"}, separator=False, group=False) bpy.utils.register_tool(ws_model.BimTool, after={"bim.pipe_tool"}, separator=False, group=False)
bpy.utils.register_tool(ws_drawing.AnnotationTool, after={"bim.bim_tool"}, separator=True, group=False) bpy.utils.register_tool(ws_drawing.AnnotationTool, after={"bim.bim_tool"}, separator=True, group=False)
bpy.utils.register_tool(ws_spatial.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=False) bpy.utils.register_tool(
ws_spatial.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=False
)
bpy.utils.register_tool( bpy.utils.register_tool(
ws_structural.StructuralTool, after={"bim.spatial_tool"}, separator=False, group=False ws_structural.StructuralTool, after={"bim.spatial_tool"}, separator=False, group=False
) )