From f01f659fa02bdc86da4945b7393c38ca4211694b Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Mon, 17 Jul 2023 16:32:10 +0500 Subject: [PATCH] Revision Clouds - option to match revision cloud with the selected object Works similarly to tagging, example - https://imgur.com/a/Atr0nYE This feature also can be useful in the future if we integrate revision clouds with ifcgit --- .../bim/module/drawing/workspace.py | 24 +++++++++++----- src/blenderbim/blenderbim/tool/drawing.py | 28 +++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/drawing/workspace.py b/src/blenderbim/blenderbim/bim/module/drawing/workspace.py index b4ead1321d..2e42fabada 100644 --- a/src/blenderbim/blenderbim/bim/module/drawing/workspace.py +++ b/src/blenderbim/blenderbim/bim/module/drawing/workspace.py @@ -248,16 +248,16 @@ class AnnotationToolUI: add_layout_hotkey_operator(cls.layout, "Add", "S_A", "Create a new annotation") - if object_type in ("TEXT", "STAIR_ARROW"): + if object_type in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP: add_layout_hotkey_operator( cls.layout, "Bulk Tag", "S_T", "Create new annotations and automatically adjust them to the selected objects", ) - add_layout_hotkey_operator( - cls.layout, "Readjust", "S_G", "Readjust tags based on the products they are assigned to" - ) + add_layout_hotkey_operator( + cls.layout, "Readjust", "S_G", "Readjust tags based on the products they are assigned to" + ) class Hotkey(bpy.types.Operator, Operator): @@ -289,14 +289,18 @@ class Hotkey(bpy.types.Operator, Operator): def hotkey_S_T(self): props = bpy.context.scene.BIMAnnotationProperties - object_type = props.object_type + annotation_type = props.object_type + + if annotation_type not in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP: + self.report({"ERROR"}, f"Annotation type {annotation_type} is not supported for tagging.") + return related_objects = bpy.context.selected_objects for related_object in related_objects: create_annotation() obj = bpy.context.active_object bpy.ops.object.mode_set(mode="OBJECT") - tool.Drawing.setup_annotation_object(obj, object_type, related_object) + tool.Drawing.setup_annotation_object(obj, annotation_type, related_object) def hotkey_S_A(self): create_annotation() @@ -314,9 +318,15 @@ class Hotkey(bpy.types.Operator, Operator): if not element or not element.is_a("IfcAnnotation"): continue + annotation_type = element.ObjectType + if annotation_type not in tool.Drawing.ANNOTATION_TYPES_SUPPORT_SETUP: + self.report({"ERROR"}, f"Annotation type {annotation_type} is not supported for readjustment.") + continue + related_product = tool.Drawing.get_assigned_product(element) if not related_product: + self.report({"ERROR"}, "Selected annotation has no product assigned.") continue related_object = tool.Ifc.get_object(related_product) - tool.Drawing.setup_annotation_object(obj, element.ObjectType, related_object) + tool.Drawing.setup_annotation_object(obj, annotation_type, related_object) diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py index 5a4f98059a..9d3bb0f884 100644 --- a/src/blenderbim/blenderbim/tool/drawing.py +++ b/src/blenderbim/blenderbim/tool/drawing.py @@ -24,6 +24,8 @@ import lark import bmesh import shutil import logging +import shapely +from shapely.ops import unary_union import mathutils import webbrowser import subprocess @@ -97,6 +99,8 @@ class Drawing(blenderbim.core.tool.Drawing): current_pos = camera.matrix_world @ current_pos obj.location = current_pos + ANNOTATION_TYPES_SUPPORT_SETUP = ("STAIR_ARROW", "TEXT", "REVISION_CLOUD", "FILL_AREA") + @classmethod def setup_annotation_object(cls, obj, object_type, related_object=None): """Finish object's adjustments after both object and entity are created""" @@ -135,6 +139,30 @@ class Drawing(blenderbim.core.tool.Drawing): cls.ensure_annotation_in_drawing_plane(obj) assign_product = True + elif object_type == "REVISION_CLOUD": + revised_object, cloud = related_object, obj + + verts = [np.array(revised_object.matrix_world @ v.co) for v in revised_object.data.vertices] + verts = [(np.around(v[[0, 1]], decimals=3)).tolist() for v in verts] + edges = [e.vertices for e in revised_object.data.edges] + + # shapely magic + boundary_lines = [shapely.LineString([verts[v] for v in e]) for e in edges] + unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines)) + all_polygons = shapely.polygonize(unioned_boundaries.geoms).geoms + outer_shell = unary_union(all_polygons) + + bm = tool.Blender.get_bmesh_for_mesh(obj.data, clean=True) + new_verts = list(outer_shell.exterior.coords) + bm_verts = [bm.verts.new(v + (0,)) for v in new_verts] + bm_edges = [bm.edges.new([bm_verts[i], bm_verts[i + 1]]) for i in range(len(new_verts) - 1)] + bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001) + + tool.Blender.apply_bmesh(obj.data, bm, obj) + cloud.location = Vector((0, 0, 0)) + cls.ensure_annotation_in_drawing_plane(cloud) + assign_product = True + if assign_product and not cls.get_assigned_product(obj_entity): tool.Ifc.run("drawing.assign_product", relating_product=related_entity, related_object=obj_entity)