mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 14:31:39 +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.api
|
||||||
import ifcopenshell.util.placement
|
import ifcopenshell.util.placement
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
import blenderbim.core.geometry
|
||||||
import blenderbim.core.aggregate
|
import blenderbim.core.aggregate
|
||||||
import blenderbim.core.spatial
|
import blenderbim.core.spatial
|
||||||
import blenderbim.core.style
|
import blenderbim.core.style
|
||||||
@@ -179,6 +180,7 @@ class IfcExporter:
|
|||||||
self.sync_object_placement(grid_obj)
|
self.sync_object_placement(grid_obj)
|
||||||
if grid_obj.matrix_world != obj.matrix_world:
|
if grid_obj.matrix_world != obj.matrix_world:
|
||||||
bpy.ops.bim.update_representation(obj=obj.name)
|
bpy.ops.bim.update_representation(obj=obj.name)
|
||||||
|
tool.Geometry.record_object_position(obj)
|
||||||
|
|
||||||
def get_application_name(self):
|
def get_application_name(self):
|
||||||
return "BlenderBIM"
|
return "BlenderBIM"
|
||||||
|
|||||||
@@ -678,6 +678,7 @@ class ActivateView(bpy.types.Operator):
|
|||||||
project_collection.children["Views"].children[camera.users_collection[0].name].hide_viewport = False
|
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.data.collections.get(camera.users_collection[0].name).hide_render = False
|
||||||
bpy.ops.bim.activate_drawing_style()
|
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"}
|
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)
|
ifc.run("group.assign_group", group=drawing_tool.get_drawing_group(drawing), product=element)
|
||||||
collector.assign(obj)
|
collector.assign(obj)
|
||||||
drawing_tool.enable_editing(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 create_svg_sheet(cls, document, titleblock): pass
|
||||||
def delete_collection(cls, collection): pass
|
def delete_collection(cls, collection): pass
|
||||||
def delete_drawing_elements(cls, elements): pass
|
def delete_drawing_elements(cls, elements): pass
|
||||||
|
def delete_object(cls, obj): pass
|
||||||
def disable_editing_drawings(cls): pass
|
def disable_editing_drawings(cls): pass
|
||||||
def disable_editing_sheets(cls): pass
|
def disable_editing_sheets(cls): pass
|
||||||
def disable_editing_text(cls, obj): 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 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 set_drawing_collection_name(cls, group, collection): pass
|
||||||
def show_decorations(cls): pass
|
def show_decorations(cls): pass
|
||||||
|
def sync_object_placement(cls, obj): pass
|
||||||
def update_text_value(cls, obj): pass
|
def update_text_value(cls, obj): pass
|
||||||
|
|
||||||
|
|
||||||
@@ -247,6 +249,9 @@ class Ifc:
|
|||||||
def get_entity(cls, obj): pass
|
def get_entity(cls, obj): pass
|
||||||
def get_object(cls, entity): pass
|
def get_object(cls, entity): pass
|
||||||
def get_schema(cls): 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 link(cls, element, obj): pass
|
||||||
def run(cls, command, **kwargs): pass
|
def run(cls, command, **kwargs): pass
|
||||||
def set(cls, ifc): pass
|
def set(cls, ifc): pass
|
||||||
|
|||||||
@@ -19,13 +19,17 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import bpy
|
import bpy
|
||||||
|
import bmesh
|
||||||
import mathutils
|
import mathutils
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
import numpy as np
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
|
import blenderbim.core.geometry
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
import blenderbim.bim.module.drawing.sheeter as sheeter
|
import blenderbim.bim.module.drawing.sheeter as sheeter
|
||||||
import blenderbim.bim.module.drawing.annotation as annotation
|
import blenderbim.bim.module.drawing.annotation as annotation
|
||||||
|
import blenderbim.bim.module.drawing.helper as helper
|
||||||
|
|
||||||
|
|
||||||
class Drawing(blenderbim.core.tool.Drawing):
|
class Drawing(blenderbim.core.tool.Drawing):
|
||||||
@@ -83,6 +87,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
if obj:
|
if obj:
|
||||||
bpy.data.objects.remove(obj)
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def delete_object(cls, obj):
|
||||||
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def disable_editing_drawings(cls):
|
def disable_editing_drawings(cls):
|
||||||
bpy.context.scene.DocProperties.is_editing_drawings = False
|
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:
|
elif target_view in ("SECTION_VIEW", "ELEVATION_VIEW") and location_hint:
|
||||||
return location_hint + " " + target_view.split("_")[0]
|
return location_hint + " " + target_view.split("_")[0]
|
||||||
return target_view
|
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/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import numpy as np
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
@@ -44,6 +45,26 @@ class Ifc(blenderbim.core.tool.Ifc):
|
|||||||
if IfcStore.get_file():
|
if IfcStore.get_file():
|
||||||
return IfcStore.get_file().schema
|
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
|
@classmethod
|
||||||
def schema(cls):
|
def schema(cls):
|
||||||
return IfcStore.get_schema()
|
return IfcStore.get_schema()
|
||||||
|
|||||||
@@ -31,16 +31,32 @@ class Usecase:
|
|||||||
self.settings[key] = value
|
self.settings[key] = value
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
if self.settings["related_object"].HasAssignments:
|
is_grid_axis = self.settings["relating_product"].is_a("IfcGridAxis")
|
||||||
for assignment in self.settings["related_object"].HasAssignments:
|
|
||||||
if (
|
if is_grid_axis:
|
||||||
assignment.is_a("IfcRelAssignsToProduct")
|
if self.settings["related_object"].HasAssignments:
|
||||||
and assignment.RelatingProduct == self.settings["relating_product"]
|
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
|
return
|
||||||
|
|
||||||
referenced_by = None
|
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]
|
referenced_by = self.settings["relating_product"].ReferencedBy[0]
|
||||||
|
|
||||||
if referenced_by:
|
if referenced_by:
|
||||||
@@ -58,4 +74,7 @@ class Usecase:
|
|||||||
"RelatingProduct": self.settings["relating_product"],
|
"RelatingProduct": self.settings["relating_product"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if is_grid_axis:
|
||||||
|
referenced_by.Name = axis.AxisTag
|
||||||
return referenced_by
|
return referenced_by
|
||||||
|
|||||||
Reference in New Issue
Block a user