mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 02:07:36 +00:00
some refactor for bbim drawings svg serialization
Made some changes to svg serialization code by moving chunks of code so `.generate_linework()` will be easier to read - moved gathering linework contexts to a separate method - moved common code for elements serialization to a function - replaced self.yield_from_iterator(it) with it.__iter__ Functionally code is exactly the same.
This commit is contained in:
@@ -44,6 +44,7 @@ import blenderbim.bim.module.drawing.helper as helper
|
|||||||
import blenderbim.bim.export_ifc
|
import blenderbim.bim.export_ifc
|
||||||
from blenderbim.bim.module.drawing.decoration import CutDecorator
|
from blenderbim.bim.module.drawing.decoration import CutDecorator
|
||||||
from blenderbim.bim.module.drawing.data import DecoratorData, DrawingsData
|
from blenderbim.bim.module.drawing.data import DecoratorData, DrawingsData
|
||||||
|
from typing import NamedTuple, List
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from mathutils import Vector, Color, Matrix
|
from mathutils import Vector, Color, Matrix
|
||||||
from timeit import default_timer as timer
|
from timeit import default_timer as timer
|
||||||
@@ -77,6 +78,11 @@ class Operator:
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class LineworkContexts(NamedTuple):
|
||||||
|
body: List[List[int]]
|
||||||
|
annotation: List[List[int]]
|
||||||
|
|
||||||
|
|
||||||
class AddAnnotationType(bpy.types.Operator, Operator):
|
class AddAnnotationType(bpy.types.Operator, Operator):
|
||||||
bl_idname = "bim.add_annotation_type"
|
bl_idname = "bim.add_annotation_type"
|
||||||
bl_label = "Add Annotation Type"
|
bl_label = "Add Annotation Type"
|
||||||
@@ -367,6 +373,105 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
self.svg_writer.create_blank_svg(svg_path).draw_underlay(context.scene.render.filepath).save()
|
self.svg_writer.create_blank_svg(svg_path).draw_underlay(context.scene.render.filepath).save()
|
||||||
return svg_path
|
return svg_path
|
||||||
|
|
||||||
|
def get_linework_contexts(self, ifc, target_view) -> LineworkContexts:
|
||||||
|
plan_body_target_contexts = []
|
||||||
|
plan_body_model_contexts = []
|
||||||
|
model_body_target_contexts = []
|
||||||
|
model_body_model_contexts = []
|
||||||
|
|
||||||
|
plan_annotation_target_contexts = []
|
||||||
|
plan_annotation_model_contexts = []
|
||||||
|
model_annotation_target_contexts = []
|
||||||
|
model_annotation_model_contexts = []
|
||||||
|
|
||||||
|
for rep_context in ifc.by_type("IfcGeometricRepresentationContext"):
|
||||||
|
if rep_context.is_a("IfcGeometricRepresentationSubContext"):
|
||||||
|
if rep_context.ContextType == "Plan":
|
||||||
|
if rep_context.ContextIdentifier in ["Body", "Facetation"]:
|
||||||
|
if rep_context.TargetView == target_view:
|
||||||
|
plan_body_target_contexts.append(rep_context.id())
|
||||||
|
elif rep_context.TargetView == "MODEL_VIEW":
|
||||||
|
plan_body_model_contexts.append(rep_context.id())
|
||||||
|
elif rep_context.ContextIdentifier == "Annotation":
|
||||||
|
if rep_context.TargetView == target_view:
|
||||||
|
plan_annotation_target_contexts.append(rep_context.id())
|
||||||
|
elif rep_context.TargetView == "MODEL_VIEW":
|
||||||
|
plan_annotation_model_contexts.append(rep_context.id())
|
||||||
|
elif rep_context.ContextType == "Model":
|
||||||
|
if rep_context.ContextIdentifier in ["Body", "Facetation"]:
|
||||||
|
if rep_context.TargetView == target_view:
|
||||||
|
model_body_target_contexts.append(rep_context.id())
|
||||||
|
elif rep_context.TargetView == "MODEL_VIEW":
|
||||||
|
model_body_model_contexts.append(rep_context.id())
|
||||||
|
elif rep_context.ContextIdentifier == "Annotation":
|
||||||
|
if rep_context.TargetView == target_view:
|
||||||
|
model_annotation_target_contexts.append(rep_context.id())
|
||||||
|
elif rep_context.TargetView == "MODEL_VIEW":
|
||||||
|
model_annotation_model_contexts.append(rep_context.id())
|
||||||
|
elif rep_context.ContextType == "Model":
|
||||||
|
# You should never purely assign to a "Model" context, but
|
||||||
|
# if you do, this is what we assume your intention is.
|
||||||
|
model_body_model_contexts.append(rep_context.id())
|
||||||
|
continue
|
||||||
|
|
||||||
|
body_contexts = (
|
||||||
|
[
|
||||||
|
plan_body_target_contexts,
|
||||||
|
plan_body_model_contexts,
|
||||||
|
model_body_target_contexts,
|
||||||
|
model_body_model_contexts,
|
||||||
|
]
|
||||||
|
if target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]
|
||||||
|
else [
|
||||||
|
model_body_target_contexts,
|
||||||
|
model_body_model_contexts,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
annotation_contexts = (
|
||||||
|
[
|
||||||
|
plan_annotation_target_contexts,
|
||||||
|
plan_annotation_model_contexts,
|
||||||
|
model_annotation_target_contexts,
|
||||||
|
model_annotation_model_contexts,
|
||||||
|
]
|
||||||
|
if target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]
|
||||||
|
else [
|
||||||
|
model_annotation_target_contexts,
|
||||||
|
model_annotation_model_contexts,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
return LineworkContexts(body_contexts, annotation_contexts)
|
||||||
|
|
||||||
|
def serialize_contexts_elements(
|
||||||
|
self, ifc, tree: ifcopenshell.geom.tree, contexts: LineworkContexts, context_type, drawing_elements, target_view
|
||||||
|
):
|
||||||
|
drawing_elements = drawing_elements.copy()
|
||||||
|
contexts = getattr(contexts, context_type)
|
||||||
|
for context in contexts:
|
||||||
|
with profile(f"Processing {context_type} context"):
|
||||||
|
if not context or not drawing_elements:
|
||||||
|
continue
|
||||||
|
geom_settings = ifcopenshell.geom.settings(
|
||||||
|
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||||
|
)
|
||||||
|
if ifc.by_id(context[0]).ContextType == "Plan" and "PLAN_VIEW" in target_view:
|
||||||
|
offset = ifcopenshell.ifcopenshell_wrapper.float_array_3()
|
||||||
|
# A 2mm Z offset to combat Z-fighting in plan or RCPs
|
||||||
|
offset[2] = 0.002 if target_view == "PLAN_VIEW" else -0.002
|
||||||
|
geom_settings.offset = offset
|
||||||
|
geom_settings.set_context_ids(context)
|
||||||
|
it = ifcopenshell.geom.iterator(
|
||||||
|
geom_settings, ifc, multiprocessing.cpu_count(), include=drawing_elements
|
||||||
|
)
|
||||||
|
processed = set()
|
||||||
|
for elem in it:
|
||||||
|
processed.add(ifc.by_id(elem.id))
|
||||||
|
self.serialiser.write(elem)
|
||||||
|
tree.add_element(elem)
|
||||||
|
drawing_elements -= processed
|
||||||
|
|
||||||
def generate_linework(self, context):
|
def generate_linework(self, context):
|
||||||
if not ifcopenshell.util.element.get_pset(self.drawing, "EPset_Drawing", "HasLinework"):
|
if not ifcopenshell.util.element.get_pset(self.drawing, "EPset_Drawing", "HasLinework"):
|
||||||
return
|
return
|
||||||
@@ -408,77 +513,8 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
# Drawings only draw bodies and annotations (and facetation, due to a Revit bug).
|
# Drawings only draw bodies and annotations (and facetation, due to a Revit bug).
|
||||||
# A drawing prioritises a target view context first, followed by a model view context as a fallback.
|
# A drawing prioritises a target view context first, followed by a model view context as a fallback.
|
||||||
# Specifically for PLAN_VIEW and REFLECTED_PLAN_VIEW, any Plan context is also prioritised.
|
# Specifically for PLAN_VIEW and REFLECTED_PLAN_VIEW, any Plan context is also prioritised.
|
||||||
|
|
||||||
plan_body_target_contexts = []
|
|
||||||
plan_body_model_contexts = []
|
|
||||||
model_body_target_contexts = []
|
|
||||||
model_body_model_contexts = []
|
|
||||||
|
|
||||||
plan_annotation_target_contexts = []
|
|
||||||
plan_annotation_model_contexts = []
|
|
||||||
model_annotation_target_contexts = []
|
|
||||||
model_annotation_model_contexts = []
|
|
||||||
|
|
||||||
target_view = ifcopenshell.util.element.get_psets(self.camera_element)["EPset_Drawing"]["TargetView"]
|
target_view = ifcopenshell.util.element.get_psets(self.camera_element)["EPset_Drawing"]["TargetView"]
|
||||||
|
contexts = self.get_linework_contexts(ifc, target_view)
|
||||||
for rep_context in ifc.by_type("IfcGeometricRepresentationContext"):
|
|
||||||
if rep_context.is_a("IfcGeometricRepresentationSubContext"):
|
|
||||||
if rep_context.ContextType == "Plan":
|
|
||||||
if rep_context.ContextIdentifier in ["Body", "Facetation"]:
|
|
||||||
if rep_context.TargetView == target_view:
|
|
||||||
plan_body_target_contexts.append(rep_context.id())
|
|
||||||
elif rep_context.TargetView == "MODEL_VIEW":
|
|
||||||
plan_body_model_contexts.append(rep_context.id())
|
|
||||||
elif rep_context.ContextIdentifier == "Annotation":
|
|
||||||
if rep_context.TargetView == target_view:
|
|
||||||
plan_annotation_target_contexts.append(rep_context.id())
|
|
||||||
elif rep_context.TargetView == "MODEL_VIEW":
|
|
||||||
plan_annotation_model_contexts.append(rep_context.id())
|
|
||||||
elif rep_context.ContextType == "Model":
|
|
||||||
if rep_context.ContextIdentifier in ["Body", "Facetation"]:
|
|
||||||
if rep_context.TargetView == target_view:
|
|
||||||
model_body_target_contexts.append(rep_context.id())
|
|
||||||
elif rep_context.TargetView == "MODEL_VIEW":
|
|
||||||
model_body_model_contexts.append(rep_context.id())
|
|
||||||
elif rep_context.ContextIdentifier == "Annotation":
|
|
||||||
if rep_context.TargetView == target_view:
|
|
||||||
model_annotation_target_contexts.append(rep_context.id())
|
|
||||||
elif rep_context.TargetView == "MODEL_VIEW":
|
|
||||||
model_annotation_model_contexts.append(rep_context.id())
|
|
||||||
elif rep_context.ContextType == "Model":
|
|
||||||
# You should never purely assign to a "Model" context, but
|
|
||||||
# if you do, this is what we assume your intention is.
|
|
||||||
model_body_model_contexts.append(rep_context.id())
|
|
||||||
continue
|
|
||||||
|
|
||||||
body_contexts = (
|
|
||||||
[
|
|
||||||
plan_body_target_contexts,
|
|
||||||
plan_body_model_contexts,
|
|
||||||
model_body_target_contexts,
|
|
||||||
model_body_model_contexts,
|
|
||||||
]
|
|
||||||
if target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]
|
|
||||||
else [
|
|
||||||
model_body_target_contexts,
|
|
||||||
model_body_model_contexts,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
annotation_contexts = (
|
|
||||||
[
|
|
||||||
plan_annotation_target_contexts,
|
|
||||||
plan_annotation_model_contexts,
|
|
||||||
model_annotation_target_contexts,
|
|
||||||
model_annotation_model_contexts,
|
|
||||||
]
|
|
||||||
if target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]
|
|
||||||
else [
|
|
||||||
model_annotation_target_contexts,
|
|
||||||
model_annotation_model_contexts,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
|
drawing_elements = tool.Drawing.get_drawing_elements(self.camera_element)
|
||||||
|
|
||||||
self.setup_serialiser(ifc, target_view)
|
self.setup_serialiser(ifc, target_view)
|
||||||
@@ -487,60 +523,15 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
tree = ifcopenshell.geom.tree()
|
tree = ifcopenshell.geom.tree()
|
||||||
tree.enable_face_styles(True)
|
tree.enable_face_styles(True)
|
||||||
|
|
||||||
elements = drawing_elements.copy()
|
self.serialize_contexts_elements(ifc, tree, contexts, "body", drawing_elements, target_view)
|
||||||
for body_context in body_contexts:
|
self.serialize_contexts_elements(ifc, tree, contexts, "annotation", drawing_elements, target_view)
|
||||||
with profile("Processing body context"):
|
|
||||||
if body_context and elements:
|
|
||||||
geom_settings = ifcopenshell.geom.settings(
|
|
||||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
|
||||||
)
|
|
||||||
if ifc.by_id(body_context[0]).ContextType == "Plan" and "PLAN_VIEW" in target_view:
|
|
||||||
offset = ifcopenshell.ifcopenshell_wrapper.float_array_3()
|
|
||||||
# A 2mm Z offset to combat Z-fighting in plan or RCPs
|
|
||||||
offset[2] = 0.002 if target_view == "PLAN_VIEW" else -0.002
|
|
||||||
geom_settings.offset = offset
|
|
||||||
geom_settings.set_context_ids(body_context)
|
|
||||||
it = ifcopenshell.geom.iterator(
|
|
||||||
geom_settings, ifc, multiprocessing.cpu_count(), include=elements
|
|
||||||
)
|
|
||||||
it.set_cache(cache)
|
|
||||||
processed = set()
|
|
||||||
for elem in self.yield_from_iterator(it):
|
|
||||||
processed.add(ifc.by_id(elem.id))
|
|
||||||
self.serialiser.write(elem)
|
|
||||||
tree.add_element(elem)
|
|
||||||
elements -= processed
|
|
||||||
|
|
||||||
elements = drawing_elements.copy()
|
|
||||||
for annotation_context in annotation_contexts:
|
|
||||||
with profile("Processing annotation context"):
|
|
||||||
if annotation_context and elements:
|
|
||||||
geom_settings = ifcopenshell.geom.settings(
|
|
||||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
|
||||||
)
|
|
||||||
if ifc.by_id(annotation_context[0]).ContextType == "Plan" and "PLAN_VIEW" in target_view:
|
|
||||||
offset = ifcopenshell.ifcopenshell_wrapper.float_array_3()
|
|
||||||
# A 2mm Z offset to combat Z-fighting in plan or RCPs
|
|
||||||
offset[2] = 0.002 if target_view == "PLAN_VIEW" else -0.002
|
|
||||||
geom_settings.offset = offset
|
|
||||||
geom_settings.set_context_ids(annotation_context)
|
|
||||||
it = ifcopenshell.geom.iterator(
|
|
||||||
geom_settings, ifc, multiprocessing.cpu_count(), include=elements
|
|
||||||
)
|
|
||||||
it.set_cache(cache)
|
|
||||||
processed = set()
|
|
||||||
for elem in self.yield_from_iterator(it):
|
|
||||||
processed.add(ifc.by_id(elem.id))
|
|
||||||
self.serialiser.write(elem)
|
|
||||||
tree.add_element(elem)
|
|
||||||
elements -= processed
|
|
||||||
|
|
||||||
if self.camera_element not in drawing_elements:
|
if self.camera_element not in drawing_elements:
|
||||||
with profile("Camera element"):
|
with profile("Camera element"):
|
||||||
# The camera must always be included, regardless of any include/exclude filters.
|
# The camera must always be included, regardless of any include/exclude filters.
|
||||||
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True)
|
geom_settings = ifcopenshell.geom.settings(DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True)
|
||||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
|
it = ifcopenshell.geom.iterator(geom_settings, ifc, include=[self.camera_element])
|
||||||
for elem in self.yield_from_iterator(it):
|
for elem in it:
|
||||||
self.serialiser.write(elem)
|
self.serialiser.write(elem)
|
||||||
|
|
||||||
with profile("Finalizing"):
|
with profile("Finalizing"):
|
||||||
@@ -884,13 +875,6 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
# the style of the face when running tree.select_ray()
|
# the style of the face when running tree.select_ray()
|
||||||
# tree.enable_face_styles(True)
|
# tree.enable_face_styles(True)
|
||||||
|
|
||||||
def yield_from_iterator(self, it):
|
|
||||||
if it.initialize():
|
|
||||||
while True:
|
|
||||||
yield it.get()
|
|
||||||
if not it.next():
|
|
||||||
break
|
|
||||||
|
|
||||||
def get_svg_classes(self, element):
|
def get_svg_classes(self, element):
|
||||||
classes = [element.is_a()]
|
classes = [element.is_a()]
|
||||||
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user