Old annotation systems now creates IFC elements again. Also text and misc annotations now work again with new system. See #1153.

This commit is contained in:
Dion Moult
2021-05-16 18:26:48 +10:00
parent e0b3a87fdb
commit f2352e9a17
8 changed files with 106 additions and 42 deletions
+2 -2
View File
@@ -14,14 +14,14 @@ def mode_callback(obj, data):
if ( if (
obj.mode != "EDIT" obj.mode != "EDIT"
or not obj.data or not obj.data
or not isinstance(obj.data, bpy.types.Mesh) or not isinstance(obj.data, (bpy.types.Mesh, bpy.types.Curve, bpy.types.TextCurve))
or not obj.BIMObjectProperties.ifc_definition_id or not obj.BIMObjectProperties.ifc_definition_id
or not bpy.context.scene.BIMProjectProperties.is_authoring or not bpy.context.scene.BIMProjectProperties.is_authoring
): ):
return return
if obj.data.BIMMeshProperties.ifc_definition_id: if obj.data.BIMMeshProperties.ifc_definition_id:
representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id) representation = IfcStore.get_file().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
if representation.RepresentationType == "Tessellation" or representation.RepresentationType == "Brep": if representation.RepresentationType in ["Tessellation", "Brep", "Annotation2D"]:
IfcStore.edited_objs.add(obj.name) IfcStore.edited_objs.add(obj.name)
elif IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).is_a("IfcGridAxis"): elif IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).is_a("IfcGridAxis"):
IfcStore.edited_objs.add(obj.name) IfcStore.edited_objs.add(obj.name)
@@ -17,9 +17,9 @@ class Annotator:
@staticmethod @staticmethod
def add_text(related_element=None): def add_text(related_element=None):
curve = bpy.data.curves.new(type="FONT", name="Plan/Annotation/PLAN_VIEW/Text") curve = bpy.data.curves.new(type="FONT", name="Text")
curve.body = "TEXT" curve.body = "TEXT"
obj = bpy.data.objects.new("IfcAnnotation/Text", curve) obj = bpy.data.objects.new("Text", curve)
obj.matrix_world = bpy.context.scene.camera.matrix_world obj.matrix_world = bpy.context.scene.camera.matrix_world
if related_element is None: if related_element is None:
location, _, _, _ = Annotator.get_placeholder_coords() location, _, _, _ = Annotator.get_placeholder_coords()
@@ -138,12 +138,12 @@ class Annotator:
if name in obj.name: if name in obj.name:
return obj return obj
if data_type == "mesh": if data_type == "mesh":
data = bpy.data.meshes.new("Plan/Annotation/PLAN_VIEW/" + name) data = bpy.data.meshes.new(name)
elif data_type == "curve": elif data_type == "curve":
data = bpy.data.curves.new("Plan/Annotation/PLAN_VIEW/" + name, type="CURVE") data = bpy.data.curves.new(name, type="CURVE")
data.dimensions = "3D" data.dimensions = "3D"
data.resolution_u = 2 data.resolution_u = 2
obj = bpy.data.objects.new("IfcAnnotation/" + name, data) obj = bpy.data.objects.new(name, data)
collection.objects.link(obj) collection.objects.link(obj)
return obj return obj
@@ -164,4 +164,4 @@ class Annotator:
return (camera.location + z_offset, return (camera.location + z_offset,
camera.location + z_offset + y_offset, camera.location + z_offset + y_offset,
camera.location + z_offset + x_offset, camera.location + z_offset + x_offset,
camera.location + z_offset + x_offset + y_offset) camera.location + z_offset + x_offset + y_offset)
@@ -3,6 +3,7 @@ import bpy
import json import json
import subprocess import subprocess
import webbrowser import webbrowser
import ifcopenshell.util.representation
import blenderbim.bim.module.drawing.svgwriter as svgwriter import blenderbim.bim.module.drawing.svgwriter as svgwriter
import blenderbim.bim.module.drawing.annotation as annotation import blenderbim.bim.module.drawing.annotation as annotation
from mathutils import Vector, Matrix, Euler, geometry from mathutils import Vector, Matrix, Euler, geometry
@@ -13,6 +14,7 @@ from ifcopenshell.api.pset.data import Data as PsetData
cwd = os.path.dirname(os.path.realpath(__file__)) cwd = os.path.dirname(os.path.realpath(__file__))
class AddDrawing(bpy.types.Operator): class AddDrawing(bpy.types.Operator):
bl_idname = "bim.add_drawing" bl_idname = "bim.add_drawing"
bl_label = "Add Drawing" bl_label = "Add Drawing"
@@ -69,7 +71,7 @@ class CreateDrawing(bpy.types.Operator):
def combine_svgs(self, context, base, annotation): def combine_svgs(self, context, base, annotation):
# Hacky :) # Hacky :)
svg_path = context.scene.BIMProperties.ifc_file[0:-4] + ".svg" svg_path = context.scene.BIMProperties.ifc_file[0:-4] + ".svg"
with open(svg_path, 'w') as outfile: with open(svg_path, "w") as outfile:
with open(base) as infile: with open(base) as infile:
for line in infile: for line in infile:
if "</svg>" in line: if "</svg>" in line:
@@ -197,6 +199,11 @@ class AddAnnotation(bpy.types.Operator):
def execute(self, context): def execute(self, context):
if not bpy.context.scene.camera: if not bpy.context.scene.camera:
return {"FINISHED"} return {"FINISHED"}
subcontext = ifcopenshell.util.representation.get_context(
IfcStore.get_file(), "Plan", "Annotation", context.scene.camera.data.BIMCameraProperties.target_view
)
if not subcontext:
return {"FINISHED"}
if self.data_type == "text": if self.data_type == "text":
if bpy.context.selected_objects: if bpy.context.selected_objects:
for selected_object in bpy.context.selected_objects: for selected_object in bpy.context.selected_objects:
@@ -209,7 +216,14 @@ class AddAnnotation(bpy.types.Operator):
obj = annotation.Annotator.add_plane_to_annotation(obj) obj = annotation.Annotator.add_plane_to_annotation(obj)
else: else:
obj = annotation.Annotator.add_line_to_annotation(obj) obj = annotation.Annotator.add_line_to_annotation(obj)
if not obj.BIMObjectProperties.ifc_definition_id:
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcAnnotation", context_id=subcontext.id())
else:
bpy.ops.bim.update_mesh_representation(obj=obj.name)
bpy.ops.object.select_all(action="DESELECT") bpy.ops.object.select_all(action="DESELECT")
bpy.context.view_layer.objects.active = obj bpy.context.view_layer.objects.active = obj
obj.select_set(True)
bpy.ops.object.mode_set(mode="EDIT") bpy.ops.object.mode_set(mode="EDIT")
return {"FINISHED"} return {"FINISHED"}
@@ -3,6 +3,7 @@ import numpy as np
import ifcopenshell import ifcopenshell
import ifcopenshell.util.unit import ifcopenshell.util.unit
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.representation
import logging import logging
import ifcopenshell.api import ifcopenshell.api
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -105,7 +106,7 @@ class AddRepresentation(bpy.types.Operator):
return {"FINISHED"} return {"FINISHED"}
box_context_id = get_context_id("Model", "Box", "MODEL_VIEW") box_context_id = get_context_id("Model", "Box", "MODEL_VIEW")
old_box = ifcopenshell.util.element.get_representation(product, "Model", "Box", "MODEL_VIEW") old_box = ifcopenshell.util.representation.get_representation(product, "Model", "Box", "MODEL_VIEW")
if ( if (
box_context_id box_context_id
and context_of_items.ContextType == "Model" and context_of_items.ContextType == "Model"
@@ -358,7 +359,7 @@ class UpdateMeshRepresentation(bpy.types.Operator):
new_representation = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data) new_representation = ifcopenshell.api.run("geometry.add_representation", self.file, **representation_data)
box_context_id = get_context_id("Model", "Box", "MODEL_VIEW") box_context_id = get_context_id("Model", "Box", "MODEL_VIEW")
old_box = ifcopenshell.util.element.get_representation(product, "Model", "Box", "MODEL_VIEW") old_box = ifcopenshell.util.representation.get_representation(product, "Model", "Box", "MODEL_VIEW")
if ( if (
box_context_id box_context_id
and old_box and old_box
+2 -1
View File
@@ -11,6 +11,7 @@ import ifcopenshell
import ifcopenshell.util.selector import ifcopenshell.util.selector
import ifcopenshell.util.geolocation import ifcopenshell.util.geolocation
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.representation
import ifcopenshell.util.schema import ifcopenshell.util.schema
import numpy as np import numpy as np
from . import export_ifc from . import export_ifc
@@ -617,7 +618,7 @@ class CutSection(bpy.types.Operator):
if not element.Representation: if not element.Representation:
return False return False
for representation in element.Representation.Representations: for representation in element.Representation.Representations:
if ifcopenshell.util.element.is_representation_of_context( if ifcopenshell.util.representation.is_representation_of_context(
representation, "Plan", "Annotation", camera.data.BIMCameraProperties.target_view representation, "Plan", "Annotation", camera.data.BIMCameraProperties.target_view
): ):
return True return True
@@ -48,7 +48,7 @@ class Usecase:
for modifier in self.settings["blender_object"].modifiers: for modifier in self.settings["blender_object"].modifiers:
if modifier.type == "BOOLEAN": if modifier.type == "BOOLEAN":
modifier.show_viewport = False modifier.show_viewport = False
if self.settings["should_force_triangulation"]: if self.settings["should_force_triangulation"]:
mesh = self.settings["blender_object"].evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh() mesh = self.settings["blender_object"].evaluated_get(bpy.context.evaluated_depsgraph_get()).to_mesh()
bm = bmesh.new() bm = bmesh.new()
@@ -119,6 +119,41 @@ class Usecase:
elif self.settings["context"].ContextIdentifier == "SurveyPoints": elif self.settings["context"].ContextIdentifier == "SurveyPoints":
pass pass
def create_text_representation(self):
return self.file.createIfcShapeRepresentation(
self.settings["context"],
self.settings["context"].ContextIdentifier,
"Annotation2D",
[self.create_text()],
)
def create_text(self):
text = self.settings["geometry"]
if text.align_y in ["TOP_BASELINE", "BOTTOM_BASELINE", "BOTTOM"]:
y = "bottom"
elif text.align_y == "CENTER":
y = "middle"
elif text.align_y == "TOP":
y = "top"
if text.align_x == "LEFT":
x = "left"
elif text.align_x == "CENTER":
x = "middle"
elif text.align_x == "RIGHT":
x = "right"
origin = self.file.createIfcAxis2Placement3D(
self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)),
self.file.createIfcDirection((0.0, 0.0, 1.0)),
self.file.createIfcDirection((1.0, 0.0, 0.0)),
)
# TODO: Planar extent right now is wrong ...
return self.file.createIfcTextLiteralWithExtent(
text.body, origin, "RIGHT", self.file.createIfcPlanarExtent(1000, 1000), f"{y}-{x}"
)
def create_variable_representation(self): def create_variable_representation(self):
if self.settings["is_wireframe"]: if self.settings["is_wireframe"]:
return self.create_wireframe_representation() return self.create_wireframe_representation()
@@ -108,24 +108,6 @@ def replace_attribute(element, old, new):
element[i] = new_attribute element[i] = new_attribute
def is_representation_of_context(representation, context, subcontext=None, target_view=None):
if target_view is not None:
return (
representation.ContextOfItems.is_a("IfcGeometricRepresentationSubContext")
and representation.ContextOfItems.TargetView == target_view
and representation.ContextOfItems.ContextIdentifier == subcontext
and representation.ContextOfItems.ContextType == context
)
elif subcontext is not None:
return (
representation.ContextOfItems.is_a("IfcGeometricRepresentationSubContext")
and representation.ContextOfItems.ContextIdentifier == subcontext
and representation.ContextOfItems.ContextType == context
)
elif representation.ContextOfItems.ContextType == context:
return True
def remove_deep(ifc_file, element): def remove_deep(ifc_file, element):
# @todo maybe some sort of try-finally mechanism. # @todo maybe some sort of try-finally mechanism.
ifc_file.batch() ifc_file.batch()
@@ -159,14 +141,3 @@ def copy_deep(ifc_file, element):
attribute[j] = copy_deep(ifc_file, item) attribute[j] = copy_deep(ifc_file, item)
new[i] = attribute new[i] = attribute
return new return new
def get_representation(element, context, subcontext=None, target_view=None):
if element.is_a("IfcProduct") and element.Representation:
for r in element.Representation.Representations:
if is_representation_of_context(r, context, subcontext, target_view):
return r
elif element.is_a("IfcTypeProduct") and element.RepresentationMaps:
for r in element.RepresentationMaps:
if is_representation_of_context(r.MappedRepresentation, context, subcontext, target_view):
return r.MappedRepresentation
@@ -0,0 +1,42 @@
def get_context(ifc_file, context, subcontext=None, target_view=None):
if subcontext or target_view:
elements = ifc_file.by_type("IfcGeometricRepresentationSubContext")
else:
elements = ifc_file.by_type("IfcGeometricRepresentationContext", include_subtypes=False)
for element in elements:
if context and element.ContextType != context:
continue
if subcontext and getattr(element, "ContextIdentifier") != subcontext:
continue
if target_view and getattr(element, "TargetView") != target_view:
continue
return element
def is_representation_of_context(representation, context, subcontext=None, target_view=None):
if target_view is not None:
return (
representation.ContextOfItems.is_a("IfcGeometricRepresentationSubContext")
and representation.ContextOfItems.TargetView == target_view
and representation.ContextOfItems.ContextIdentifier == subcontext
and representation.ContextOfItems.ContextType == context
)
elif subcontext is not None:
return (
representation.ContextOfItems.is_a("IfcGeometricRepresentationSubContext")
and representation.ContextOfItems.ContextIdentifier == subcontext
and representation.ContextOfItems.ContextType == context
)
elif representation.ContextOfItems.ContextType == context:
return True
def get_representation(element, context, subcontext=None, target_view=None):
if element.is_a("IfcProduct") and element.Representation:
for r in element.Representation.Representations:
if is_representation_of_context(r, context, subcontext, target_view):
return r
elif element.is_a("IfcTypeProduct") and element.RepresentationMaps:
for r in element.RepresentationMaps:
if is_representation_of_context(r.MappedRepresentation, context, subcontext, target_view):
return r.MappedRepresentation