mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
#1153. References to grids in drawings are now auto synchronised when activating a drawing view.
This commit is contained in:
@@ -28,6 +28,7 @@ import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.placement
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.spatial
|
||||
import blenderbim.core.style
|
||||
@@ -179,6 +180,7 @@ class IfcExporter:
|
||||
self.sync_object_placement(grid_obj)
|
||||
if grid_obj.matrix_world != obj.matrix_world:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
tool.Geometry.record_object_position(obj)
|
||||
|
||||
def get_application_name(self):
|
||||
return "BlenderBIM"
|
||||
|
||||
@@ -678,6 +678,7 @@ class ActivateView(bpy.types.Operator):
|
||||
project_collection.children["Views"].children[camera.users_collection[0].name].hide_viewport = False
|
||||
bpy.data.collections.get(camera.users_collection[0].name).hide_render = False
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -158,3 +158,42 @@ def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None)
|
||||
ifc.run("group.assign_group", group=drawing_tool.get_drawing_group(drawing), product=element)
|
||||
collector.assign(obj)
|
||||
drawing_tool.enable_editing(obj)
|
||||
|
||||
|
||||
def sync_references(ifc, collector, drawing_tool, drawing=None):
|
||||
context = drawing_tool.get_annotation_context(drawing_tool.get_drawing_target_view(drawing))
|
||||
if not context:
|
||||
return
|
||||
|
||||
group = drawing_tool.get_drawing_group(drawing)
|
||||
for reference_element in drawing_tool.get_potential_reference_elements(drawing):
|
||||
reference_obj = ifc.get_object(reference_element)
|
||||
annotation = drawing_tool.get_drawing_reference_annotation(drawing, reference_element)
|
||||
|
||||
should_delete_existing_annotation = False
|
||||
should_create_annotation = False
|
||||
|
||||
if annotation and (not reference_obj or ifc.is_moved(reference_obj) or ifc.is_edited(reference_obj)):
|
||||
should_delete_existing_annotation = True
|
||||
|
||||
if reference_obj and (should_delete_existing_annotation or not annotation):
|
||||
should_create_annotation = True
|
||||
|
||||
if should_delete_existing_annotation:
|
||||
annotation_obj = ifc.get_object(annotation)
|
||||
if annotation_obj:
|
||||
drawing_tool.delete_object(annotation_obj)
|
||||
ifc.run("root.remove_product", product=annotation)
|
||||
|
||||
if should_create_annotation:
|
||||
annotation = drawing_tool.generate_reference_annotation(drawing, reference_element, context)
|
||||
if annotation:
|
||||
ifc.run("drawing.assign_product", relating_product=reference_element, related_object=annotation)
|
||||
ifc.run("group.assign_group", group=group, product=annotation)
|
||||
collector.assign(ifc.get_object(annotation))
|
||||
|
||||
if reference_obj and ifc.is_moved(reference_obj):
|
||||
drawing_tool.sync_object_placement(reference_obj)
|
||||
|
||||
if reference_obj and ifc.is_edited(reference_obj):
|
||||
drawing_tool.sync_object_representation(reference_obj)
|
||||
|
||||
@@ -159,6 +159,7 @@ class Drawing:
|
||||
def create_svg_sheet(cls, document, titleblock): pass
|
||||
def delete_collection(cls, collection): pass
|
||||
def delete_drawing_elements(cls, elements): pass
|
||||
def delete_object(cls, obj): pass
|
||||
def disable_editing_drawings(cls): pass
|
||||
def disable_editing_sheets(cls): pass
|
||||
def disable_editing_text(cls, obj): pass
|
||||
@@ -193,6 +194,7 @@ class Drawing:
|
||||
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
|
||||
def set_drawing_collection_name(cls, group, collection): pass
|
||||
def show_decorations(cls): pass
|
||||
def sync_object_placement(cls, obj): pass
|
||||
def update_text_value(cls, obj): pass
|
||||
|
||||
|
||||
@@ -247,6 +249,9 @@ class Ifc:
|
||||
def get_entity(cls, obj): pass
|
||||
def get_object(cls, entity): pass
|
||||
def get_schema(cls): pass
|
||||
def is_deleted(cls, element): pass
|
||||
def is_edited(cls, obj): pass
|
||||
def is_moved(cls, obj): pass
|
||||
def link(cls, element, obj): pass
|
||||
def run(cls, command, **kwargs): pass
|
||||
def set(cls, ifc): pass
|
||||
|
||||
@@ -19,13 +19,17 @@
|
||||
import os
|
||||
import re
|
||||
import bpy
|
||||
import bmesh
|
||||
import mathutils
|
||||
import webbrowser
|
||||
import numpy as np
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.bim.module.drawing.sheeter as sheeter
|
||||
import blenderbim.bim.module.drawing.annotation as annotation
|
||||
import blenderbim.bim.module.drawing.helper as helper
|
||||
|
||||
|
||||
class Drawing(blenderbim.core.tool.Drawing):
|
||||
@@ -83,6 +87,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
if obj:
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def delete_object(cls, obj):
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_drawings(cls):
|
||||
bpy.context.scene.DocProperties.is_editing_drawings = False
|
||||
@@ -363,3 +371,154 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
elif target_view in ("SECTION_VIEW", "ELEVATION_VIEW") and location_hint:
|
||||
return location_hint + " " + target_view.split("_")[0]
|
||||
return target_view
|
||||
|
||||
@classmethod
|
||||
def get_potential_reference_elements(cls, drawing):
|
||||
elements = []
|
||||
existing_references = cls.get_group_elements(cls.get_drawing_group(drawing))
|
||||
for element in tool.Ifc.get().by_type("IfcAnnotation"):
|
||||
if element in existing_references or element == drawing:
|
||||
continue
|
||||
if element.ObjectType == "DRAWING":
|
||||
psets = ifcopenshell.util.element.get_psets(element)
|
||||
if psets.get("EPset_Drawing", {}).get("TargetView", None) in ("SECTION_VIEW", "ELEVATION_VIEW"):
|
||||
elements.append(element)
|
||||
for element in tool.Ifc.get().by_type("IfcGridAxis"):
|
||||
elements.append(element)
|
||||
return elements
|
||||
|
||||
@classmethod
|
||||
def get_drawing_reference_annotation(cls, drawing, reference_element):
|
||||
if drawing == reference_element:
|
||||
return True
|
||||
for element in cls.get_group_elements(cls.get_drawing_group(drawing)):
|
||||
if element.is_a("IfcAnnotation"):
|
||||
for rel in element.HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct"):
|
||||
if rel.RelatingProduct == reference_element:
|
||||
return element
|
||||
# We cannot associate IfcGridAxis directly, so we establish a convention:
|
||||
# IfcRelAssignsToProduct.RelatingProduct = IfcGrid
|
||||
# IfcRelAssignsToProduct.Name = IfcGridAxis.AxisTag
|
||||
elif reference_element.is_a("IfcGridAxis") and rel.Name == reference_element.AxisTag:
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def generate_reference_annotation(cls, drawing, reference_element, context):
|
||||
if reference_element.is_a("IfcGridAxis"):
|
||||
return cls.generate_grid_axis_reference_annotation(drawing, reference_element, context)
|
||||
|
||||
@classmethod
|
||||
def generate_grid_axis_reference_annotation(cls, drawing, reference_element, context):
|
||||
target_view = tool.Drawing.get_drawing_target_view(drawing)
|
||||
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
|
||||
is_ortho = camera.data.type == "ORTHO"
|
||||
bounds = helper.ortho_view_frame(camera.data) if is_ortho else None
|
||||
clipping = is_ortho and target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW")
|
||||
elevating = is_ortho and target_view in ("ELEVATION_VIEW", "SECTION_VIEW")
|
||||
|
||||
def clone(src):
|
||||
dst = src.copy()
|
||||
dst.data = dst.data.copy()
|
||||
dst.name = dst.name.replace("IfcGridAxis/", "")
|
||||
dst.BIMObjectProperties.ifc_definition_id = 0
|
||||
dst.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
return dst
|
||||
|
||||
def disassemble(obj):
|
||||
mesh = bmesh.new()
|
||||
mesh.verts.ensure_lookup_table()
|
||||
mesh.from_mesh(obj.data)
|
||||
return obj, mesh
|
||||
|
||||
def assemble(obj, mesh):
|
||||
mesh.to_mesh(obj.data)
|
||||
return obj
|
||||
|
||||
def to_camera_coords(obj, mesh):
|
||||
mesh.transform(camera.matrix_world.inverted() @ obj.matrix_world)
|
||||
obj.matrix_world = camera.matrix_world
|
||||
annotation_offset = mathutils.Vector((0, 0, -camera.data.clip_start))
|
||||
annotation_offset = camera.matrix_world.to_quaternion() @ annotation_offset
|
||||
obj.matrix_world[0][3] += annotation_offset[0]
|
||||
obj.matrix_world[1][3] += annotation_offset[1]
|
||||
obj.matrix_world[2][3] += annotation_offset[2]
|
||||
return obj, mesh
|
||||
|
||||
def clip_to_camera_boundary(mesh):
|
||||
mesh.verts.ensure_lookup_table()
|
||||
points = [v.co for v in mesh.verts[0:2]]
|
||||
points = helper.clip_segment(bounds, points)
|
||||
if points is None:
|
||||
return None
|
||||
mesh.verts[0].co = points[0]
|
||||
mesh.verts[1].co = points[1]
|
||||
return mesh
|
||||
|
||||
def draw_grids_vertically(mesh):
|
||||
mesh.verts.ensure_lookup_table()
|
||||
points = [v.co for v in mesh.verts[0:2]]
|
||||
points = helper.elevate_segment(bounds, points)
|
||||
if points is None:
|
||||
return None
|
||||
points = helper.clip_segment(bounds, points)
|
||||
if points is None:
|
||||
return None
|
||||
mesh.verts[0].co = points[0]
|
||||
mesh.verts[1].co = points[1]
|
||||
return mesh
|
||||
|
||||
obj = tool.Ifc.get_object(reference_element)
|
||||
if not obj:
|
||||
return
|
||||
obj, mesh = to_camera_coords(*disassemble(clone(obj)))
|
||||
|
||||
if clipping:
|
||||
mesh = clip_to_camera_boundary(mesh)
|
||||
elif elevating:
|
||||
mesh = draw_grids_vertically(mesh)
|
||||
|
||||
if mesh is None:
|
||||
return
|
||||
|
||||
assemble(obj, mesh)
|
||||
|
||||
element = cls.run_root_assign_class(
|
||||
obj=obj,
|
||||
ifc_class="IfcAnnotation",
|
||||
predefined_type="GRID",
|
||||
should_add_representation=True,
|
||||
context=context,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def sync_object_representation(cls, obj):
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
@classmethod
|
||||
def sync_object_placement(cls, obj):
|
||||
blender_matrix = np.array(obj.matrix_world)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if (obj.scale - mathutils.Vector((1.0, 1.0, 1.0))).length > 1e-4:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
return element
|
||||
if element.is_a("IfcGridAxis"):
|
||||
return cls.sync_grid_axis_object_placement(obj, element)
|
||||
if not hasattr(element, "ObjectPlacement"):
|
||||
return
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def sync_grid_axis_object_placement(cls, obj, element):
|
||||
grid = (element.PartOfU or element.PartOfV or element.PartOfW)[0]
|
||||
grid_obj = tool.Ifc.get_object(grid)
|
||||
if grid_obj:
|
||||
cls.sync_object_placement(grid_obj)
|
||||
if grid_obj.matrix_world != obj.matrix_world:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
tool.Geometry.record_object_position(obj)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import numpy as np
|
||||
import ifcopenshell.api
|
||||
import blenderbim.core.tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
@@ -44,6 +45,26 @@ class Ifc(blenderbim.core.tool.Ifc):
|
||||
if IfcStore.get_file():
|
||||
return IfcStore.get_file().schema
|
||||
|
||||
@classmethod
|
||||
def is_deleted(cls, element):
|
||||
return element.id() in IfcStore.deleted_ids
|
||||
|
||||
@classmethod
|
||||
def is_edited(cls, obj):
|
||||
return list(obj.scale) != [1.0, 1.0, 1.0] or obj in IfcStore.edited_objs
|
||||
|
||||
@classmethod
|
||||
def is_moved(cls, obj):
|
||||
if not obj.BIMObjectProperties.location_checksum:
|
||||
return True # Let's be conservative
|
||||
loc_check = np.frombuffer(eval(obj.BIMObjectProperties.location_checksum))
|
||||
rot_check = np.frombuffer(eval(obj.BIMObjectProperties.rotation_checksum))
|
||||
loc_real = np.array(obj.matrix_world.translation).flatten()
|
||||
rot_real = np.array(obj.matrix_world.to_3x3()).flatten()
|
||||
if np.allclose(loc_check, loc_real, atol=1e-4) and np.allclose(rot_check, rot_real, atol=1e-2):
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def schema(cls):
|
||||
return IfcStore.get_schema()
|
||||
|
||||
@@ -31,16 +31,32 @@ class Usecase:
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
if self.settings["related_object"].HasAssignments:
|
||||
for assignment in self.settings["related_object"].HasAssignments:
|
||||
if (
|
||||
assignment.is_a("IfcRelAssignsToProduct")
|
||||
and assignment.RelatingProduct == self.settings["relating_product"]
|
||||
):
|
||||
is_grid_axis = self.settings["relating_product"].is_a("IfcGridAxis")
|
||||
|
||||
if is_grid_axis:
|
||||
if self.settings["related_object"].HasAssignments:
|
||||
for rel in self.settings["related_object"].HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct") and rel.Name == self.settings["relating_product"].AxisTag:
|
||||
return
|
||||
elif self.settings["related_object"].HasAssignments:
|
||||
for rel in self.settings["related_object"].HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == self.settings["relating_product"]:
|
||||
return
|
||||
|
||||
referenced_by = None
|
||||
if self.settings["relating_product"].ReferencedBy:
|
||||
|
||||
if is_grid_axis:
|
||||
axis = self.settings["relating_product"]
|
||||
grid = None
|
||||
for attribute in ("PartOfW", "PartOfV", "PartOfU"):
|
||||
if getattr(axis, attribute, None):
|
||||
grid = getattr(axis, attribute)[0]
|
||||
self.settings["relating_product"] = grid
|
||||
for rel in grid.ReferencedBy:
|
||||
if rel.Name == axis.AxisTag:
|
||||
referenced_by = rel
|
||||
break
|
||||
elif self.settings["relating_product"].ReferencedBy:
|
||||
referenced_by = self.settings["relating_product"].ReferencedBy[0]
|
||||
|
||||
if referenced_by:
|
||||
@@ -58,4 +74,7 @@ class Usecase:
|
||||
"RelatingProduct": self.settings["relating_product"],
|
||||
}
|
||||
)
|
||||
|
||||
if is_grid_axis:
|
||||
referenced_by.Name = axis.AxisTag
|
||||
return referenced_by
|
||||
|
||||
Reference in New Issue
Block a user