mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-29 03:37:21 +00:00
Merge branch 'v0.7.0' of https://github.com/IfcOpenShell/IfcOpenShell into pr-daily-builds
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
ISO-10303-21;
|
||||||
|
HEADER;
|
||||||
|
FILE_DESCRIPTION((),'2;1');
|
||||||
|
FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',(),(),'EPset_Drawing','EPset_Drawing',$);
|
||||||
|
FILE_SCHEMA(('IFC4'));
|
||||||
|
ENDSEC;
|
||||||
|
DATA;
|
||||||
|
#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation',(#2,#3,#4,#5,#6,#7));
|
||||||
|
#2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||||
|
#3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||||
|
#4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
|
||||||
|
#5=IFCSIMPLEPROPERTYTEMPLATE('0AK5C2UpL4$eaac2LszAx$',$,'HasUnderlay','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||||
|
#6=IFCSIMPLEPROPERTYTEMPLATE('2j2ZEZR8X5tONm7kli5hM6',$,'HasLinework','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||||
|
#7=IFCSIMPLEPROPERTYTEMPLATE('1ttChRysH9UuEX2FeMj5Hu',$,'HasAnnotation','',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.);
|
||||||
|
ENDSEC;
|
||||||
|
END-ISO-10303-21;
|
||||||
@@ -23,10 +23,19 @@ text { fill: black; stroke: none; }
|
|||||||
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; fill-rule: evenodd; }
|
.cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; fill-rule: evenodd; }
|
||||||
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
.projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||||
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||||
.hidden { stroke-dasharray: 3, 2; }
|
|
||||||
.solid {}
|
.solid {}
|
||||||
.leader { marker-end: url(#leader-marker); }
|
.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
|
||||||
.stair { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
|
.PredefinedType-MISC { fill: red; stroke: blue; stroke-width: 1; }
|
||||||
|
.PredefinedType-GRID { marker-start: url(#grid-marker); marker-end: url(#grid-marker); }
|
||||||
|
.PredefinedType-SECTIONLEVEL { marker-start: url(#section-level-marker); stroke-dasharray: 12.5, 3, 3, 3; }
|
||||||
|
.PredefinedType-PLANLEVEL { marker-end: url(#plan-level-marker); }
|
||||||
|
.PredefinedType-DIMENSION { marker-start: url(#dimension-marker-start); marker-end: url(#dimension-marker-end); }
|
||||||
|
.PredefinedType-HIDDENLINE { stroke-dasharray: 3, 2; }
|
||||||
|
.PredefinedType-STAIR { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
|
||||||
|
.PredefinedType-BOUNDARY { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12, 4, 3, 4, 3, 4; }
|
||||||
|
.PredefinedType-TEXT { fill: black; stroke: none; }
|
||||||
|
path.PredefinedType-TEXTLEADER { marker-end: url(#leader-marker); }
|
||||||
|
text.PredefinedType-TEXTLEADER { fill: black; stroke: none; }
|
||||||
.break { fill: white; }
|
.break { fill: white; }
|
||||||
.breakline { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
|
.breakline { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
|
||||||
.material-blank { fill: white; }
|
.material-blank { fill: white; }
|
||||||
@@ -44,5 +53,4 @@ text { fill: black; stroke: none; }
|
|||||||
.material-honeycomb { fill: url(#honeycomb); }
|
.material-honeycomb { fill: url(#honeycomb); }
|
||||||
.material-sand { fill: url(#sand); }
|
.material-sand { fill: url(#sand); }
|
||||||
.material-concrete { fill: url(#concrete); stroke-width: 0.5; }
|
.material-concrete { fill: url(#concrete); stroke-width: 0.5; }
|
||||||
.material-boundary { fill: none; stroke: red; stroke-width: 1; stroke-dasharray: 12,4,3,4,3,4; }
|
|
||||||
.IfcSpace { fill: none; stroke: none; }
|
.IfcSpace { fill: none; stroke: none; }
|
||||||
|
|||||||
@@ -157,12 +157,12 @@ def loadIfcStore(scene):
|
|||||||
if not IfcStore.get_file():
|
if not IfcStore.get_file():
|
||||||
return
|
return
|
||||||
IfcStore.get_schema()
|
IfcStore.get_schema()
|
||||||
IfcStore.reload_linked_elements()
|
IfcStore.relink_all_objects()
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def undo_pre(scene):
|
def undo_pre(scene):
|
||||||
IfcStore.update_undo_redo_stack_objects()
|
IfcStore.track_undo_redo_stack_object_map()
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
@@ -171,13 +171,13 @@ def undo_post(scene):
|
|||||||
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||||
IfcStore.undo()
|
IfcStore.undo()
|
||||||
purge_module_data()
|
purge_module_data()
|
||||||
IfcStore.update_undo_redo_stack_objects()
|
IfcStore.track_undo_redo_stack_selected_objects()
|
||||||
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
|
IfcStore.reload_undo_redo_stack_objects()
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
def redo_pre(scene):
|
def redo_pre(scene):
|
||||||
IfcStore.update_undo_redo_stack_objects()
|
IfcStore.track_undo_redo_stack_object_map()
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
@@ -186,8 +186,8 @@ def redo_post(scene):
|
|||||||
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
|
||||||
IfcStore.redo()
|
IfcStore.redo()
|
||||||
purge_module_data()
|
purge_module_data()
|
||||||
IfcStore.update_undo_redo_stack_objects()
|
IfcStore.track_undo_redo_stack_selected_objects()
|
||||||
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
|
IfcStore.reload_undo_redo_stack_objects()
|
||||||
|
|
||||||
|
|
||||||
@persistent
|
@persistent
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ class IfcStore:
|
|||||||
library_file = None
|
library_file = None
|
||||||
element_listeners = set()
|
element_listeners = set()
|
||||||
undo_redo_stack_objects = set()
|
undo_redo_stack_objects = set()
|
||||||
|
undo_redo_stack_object_names = {}
|
||||||
current_transaction = ""
|
current_transaction = ""
|
||||||
last_transaction = ""
|
last_transaction = ""
|
||||||
history = []
|
history = []
|
||||||
@@ -147,14 +148,32 @@ class IfcStore:
|
|||||||
IfcStore.element_listeners.add(callback)
|
IfcStore.element_listeners.add(callback)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def update_undo_redo_stack_objects():
|
def track_undo_redo_stack_object_map():
|
||||||
|
"""Keeps track of currently mapped object names, typically during undo and redo
|
||||||
|
|
||||||
|
When any Blender object is stored outside a Blender PointerProperty, such as
|
||||||
|
in a regular Python list, there is the likely probability that the object
|
||||||
|
will be invalidated when undo or redo occurs. Object invalidation seems to
|
||||||
|
occur whenever an object is affected during an operation.
|
||||||
|
|
||||||
|
For example, if an operator deletes a modifier on o1, then o1 will be invalidated.
|
||||||
|
"""
|
||||||
|
for key, value in IfcStore.id_map.items():
|
||||||
|
try:
|
||||||
|
IfcStore.undo_redo_stack_object_names[key] = value.name
|
||||||
|
except:
|
||||||
|
continue
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def track_undo_redo_stack_selected_objects():
|
||||||
"""Keeps track of selected object names, typically during undo and redo
|
"""Keeps track of selected object names, typically during undo and redo
|
||||||
|
|
||||||
When any Blender object is stored outside a Blender PointerProperty, such as
|
When any Blender object is stored outside a Blender PointerProperty, such as
|
||||||
in a regular Python list, there is the likely probability that the object
|
in a regular Python list, there is the likely probability that the object
|
||||||
will be invalidated when undo or redo occurs. Object invalidation seems to
|
will be invalidated when undo or redo occurs. Object invalidation seems to
|
||||||
only occur for selected objects either pre/post undo/redo event, including
|
occur for selected objects either pre/post undo/redo event, including
|
||||||
selected objects for consecutive undo/redos, and all children.
|
selected objects for consecutive undo/redos, and all children. This is
|
||||||
|
important because selected objects are often deleted from the scene.
|
||||||
|
|
||||||
So if I first select o1, then o2, then o3, then press undo, o3 will be
|
So if I first select o1, then o2, then o3, then press undo, o3 will be
|
||||||
invalidated. If instead I press undo twice, o3 and o2 will be invalidated.
|
invalidated. If instead I press undo twice, o3 and o2 will be invalidated.
|
||||||
@@ -169,14 +188,23 @@ class IfcStore:
|
|||||||
IfcStore.undo_redo_stack_objects |= objects
|
IfcStore.undo_redo_stack_objects |= objects
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def reload_linked_elements(objects=None):
|
def reload_undo_redo_stack_objects():
|
||||||
|
"""Reloads any invalidated objects after undo or redo
|
||||||
|
|
||||||
|
After an undo or redo operation, objects may have been invalidated in
|
||||||
|
our id_map and guid_map. Invalidated objects are typically those that
|
||||||
|
have been manipulated or deleted. This checks the cache of mapped and
|
||||||
|
selected objects prior to the operation and ensures that if the object
|
||||||
|
is invalidated, they are reloaded based on the object name that was
|
||||||
|
tracked prior to the undo / redo.
|
||||||
|
"""
|
||||||
file = IfcStore.get_file()
|
file = IfcStore.get_file()
|
||||||
if not file:
|
if not file:
|
||||||
return
|
return
|
||||||
if objects is None:
|
|
||||||
objects = bpy.data.objects
|
|
||||||
|
|
||||||
for obj in objects:
|
# First, reload objects that were selected or active
|
||||||
|
for name in IfcStore.undo_redo_stack_objects:
|
||||||
|
obj = bpy.data.objects.get(name)
|
||||||
if not obj:
|
if not obj:
|
||||||
continue
|
continue
|
||||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
@@ -187,6 +215,38 @@ class IfcStore:
|
|||||||
data["guid"] = element.GlobalId
|
data["guid"] = element.GlobalId
|
||||||
IfcStore.commit_link_element(data)
|
IfcStore.commit_link_element(data)
|
||||||
|
|
||||||
|
# Scan for any straggling invalidated objects which were indirectly affected and reload them too.
|
||||||
|
for key, value in IfcStore.id_map.items():
|
||||||
|
try:
|
||||||
|
value.name
|
||||||
|
except:
|
||||||
|
obj = bpy.data.objects.get(IfcStore.undo_redo_stack_object_names[key])
|
||||||
|
if not obj or not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
continue
|
||||||
|
element = file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
data = {"id": element.id(), "obj": obj.name}
|
||||||
|
if hasattr(element, "GlobalId"):
|
||||||
|
data["guid"] = element.GlobalId
|
||||||
|
IfcStore.commit_link_element(data)
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def relink_all_objects():
|
||||||
|
file = IfcStore.get_file()
|
||||||
|
if not file:
|
||||||
|
return
|
||||||
|
for obj in bpy.data.objects:
|
||||||
|
if not obj:
|
||||||
|
continue
|
||||||
|
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||||
|
continue
|
||||||
|
element = file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
data = {"id": element.id(), "obj": obj.name}
|
||||||
|
if hasattr(element, "GlobalId"):
|
||||||
|
data["guid"] = element.GlobalId
|
||||||
|
IfcStore.commit_link_element(data)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def delete_element(element):
|
def delete_element(element):
|
||||||
IfcStore.deleted_ids.add(element.id())
|
IfcStore.deleted_ids.add(element.id())
|
||||||
@@ -312,6 +372,7 @@ class IfcStore:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def begin_transaction(operator):
|
def begin_transaction(operator):
|
||||||
IfcStore.undo_redo_stack_objects = set()
|
IfcStore.undo_redo_stack_objects = set()
|
||||||
|
IfcStore.undo_redo_stack_object_names = {}
|
||||||
IfcStore.current_transaction = str(uuid.uuid4())
|
IfcStore.current_transaction = str(uuid.uuid4())
|
||||||
operator.transaction_key = IfcStore.current_transaction
|
operator.transaction_key = IfcStore.current_transaction
|
||||||
|
|
||||||
|
|||||||
@@ -1682,6 +1682,12 @@ class IfcImporter:
|
|||||||
else:
|
else:
|
||||||
camera.BIMCameraProperties.diagram_scale = "CUSTOM"
|
camera.BIMCameraProperties.diagram_scale = "CUSTOM"
|
||||||
camera.BIMCameraProperties.custom_diagram_scale = pset["Scale"]
|
camera.BIMCameraProperties.custom_diagram_scale = pset["Scale"]
|
||||||
|
if "HasUnderlay" in pset:
|
||||||
|
camera.BIMCameraProperties.has_underlay = pset["HasUnderlay"]
|
||||||
|
if "HasLinework" in pset:
|
||||||
|
camera.BIMCameraProperties.has_linework = pset["HasLinework"]
|
||||||
|
if "HasAnnotation" in pset:
|
||||||
|
camera.BIMCameraProperties.has_annotation = pset["HasAnnotation"]
|
||||||
return camera
|
return camera
|
||||||
|
|
||||||
def create_curve(self, element, shape):
|
def create_curve(self, element, shape):
|
||||||
|
|||||||
@@ -119,9 +119,10 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
start = time.time()
|
start = time.time()
|
||||||
self.profile_code("Start drawing generation process")
|
self.profile_code("Start drawing generation process")
|
||||||
self.props = context.scene.DocProperties
|
self.props = context.scene.DocProperties
|
||||||
|
self.cprops = self.camera.data.BIMCameraProperties
|
||||||
self.drawing_name = self.file.by_id(self.camera.BIMObjectProperties.ifc_definition_id).Name
|
self.drawing_name = self.file.by_id(self.camera.BIMObjectProperties.ifc_definition_id).Name
|
||||||
self.get_scale()
|
self.get_scale()
|
||||||
if self.camera.data.BIMCameraProperties.update_representation(self.camera):
|
if self.cprops.update_representation(self.camera):
|
||||||
bpy.ops.bim.update_representation(obj=self.camera.name, ifc_representation_class="")
|
bpy.ops.bim.update_representation(obj=self.camera.name, ifc_representation_class="")
|
||||||
underlay_svg = self.generate_underlay(context)
|
underlay_svg = self.generate_underlay(context)
|
||||||
self.profile_code("Generate underlay")
|
self.profile_code("Generate underlay")
|
||||||
@@ -186,12 +187,12 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
return svg_path
|
return svg_path
|
||||||
|
|
||||||
def generate_underlay(self, context):
|
def generate_underlay(self, context):
|
||||||
if not self.props.has_underlay:
|
if not self.cprops.has_underlay:
|
||||||
return
|
return
|
||||||
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-underlay.svg")
|
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-underlay.svg")
|
||||||
context.scene.render.filepath = svg_path[0:-4] + ".png"
|
context.scene.render.filepath = svg_path[0:-4] + ".png"
|
||||||
drawing_style = context.scene.DocProperties.drawing_styles[
|
drawing_style = context.scene.DocProperties.drawing_styles[
|
||||||
self.camera.data.BIMCameraProperties.active_drawing_style_index
|
self.cprops.active_drawing_style_index
|
||||||
]
|
]
|
||||||
|
|
||||||
if drawing_style.render_type == "DEFAULT":
|
if drawing_style.render_type == "DEFAULT":
|
||||||
@@ -246,7 +247,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
return svg_path
|
return svg_path
|
||||||
|
|
||||||
def generate_linework(self, context):
|
def generate_linework(self, context):
|
||||||
if not self.props.has_linework:
|
if not self.cprops.has_linework:
|
||||||
return
|
return
|
||||||
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-linework.svg")
|
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-linework.svg")
|
||||||
if os.path.isfile(svg_path) and self.props.should_use_linework_cache:
|
if os.path.isfile(svg_path) and self.props.should_use_linework_cache:
|
||||||
@@ -410,7 +411,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
return re.sub("[^0-9a-zA-Z]+", "", name)
|
return re.sub("[^0-9a-zA-Z]+", "", name)
|
||||||
|
|
||||||
def generate_annotation(self, context):
|
def generate_annotation(self, context):
|
||||||
if not self.props.has_annotation:
|
if not self.cprops.has_annotation:
|
||||||
return
|
return
|
||||||
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-annotation.svg")
|
svg_path = os.path.join(context.scene.BIMProperties.data_dir, "cache", self.drawing_name + "-annotation.svg")
|
||||||
if os.path.isfile(svg_path) and self.props.should_use_annotation_cache:
|
if os.path.isfile(svg_path) and self.props.should_use_annotation_cache:
|
||||||
@@ -420,7 +421,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
svg_writer = svgwriter.SvgWriter()
|
svg_writer = svgwriter.SvgWriter()
|
||||||
|
|
||||||
drawing_style = context.scene.DocProperties.drawing_styles[
|
drawing_style = context.scene.DocProperties.drawing_styles[
|
||||||
camera.data.BIMCameraProperties.active_drawing_style_index
|
self.cprops.active_drawing_style_index
|
||||||
]
|
]
|
||||||
|
|
||||||
render = context.scene.render
|
render = context.scene.render
|
||||||
@@ -473,7 +474,7 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
svg_writer.annotations["section_level_obj"] = obj
|
svg_writer.annotations["section_level_obj"] = obj
|
||||||
elif element.ObjectType == "TEXT":
|
elif element.ObjectType == "TEXT":
|
||||||
svg_writer.annotations.setdefault("text_objs", []).append(obj)
|
svg_writer.annotations.setdefault("text_objs", []).append(obj)
|
||||||
elif element.ObjectType == "MISC":
|
else:
|
||||||
svg_writer.annotations.setdefault("misc_objs", []).append(obj)
|
svg_writer.annotations.setdefault("misc_objs", []).append(obj)
|
||||||
|
|
||||||
svg_writer.annotations["attributes"] = [a.name for a in drawing_style.attributes]
|
svg_writer.annotations["attributes"] = [a.name for a in drawing_style.attributes]
|
||||||
|
|||||||
@@ -153,6 +153,30 @@ def update_drawing_name(self, context):
|
|||||||
core.update_drawing_name(tool.Ifc, tool.Drawing, drawing=drawing, name=self.name)
|
core.update_drawing_name(tool.Ifc, tool.Drawing, drawing=drawing, name=self.name)
|
||||||
|
|
||||||
|
|
||||||
|
def update_has_underlay(self, context):
|
||||||
|
update_layer(self, context, "HasUnderlay", self.has_underlay)
|
||||||
|
|
||||||
|
|
||||||
|
def update_has_linework(self, context):
|
||||||
|
update_layer(self, context, "HasLinework", self.has_linework)
|
||||||
|
|
||||||
|
|
||||||
|
def update_has_annotation(self, context):
|
||||||
|
update_layer(self, context, "HasAnnotation", self.has_annotation)
|
||||||
|
|
||||||
|
|
||||||
|
def update_layer(self, context, name, value):
|
||||||
|
element = tool.Ifc.get_entity(context.active_object)
|
||||||
|
if not element:
|
||||||
|
return
|
||||||
|
pset = ifcopenshell.util.element.get_psets(element).get("EPset_Drawing")
|
||||||
|
if pset:
|
||||||
|
pset = tool.Ifc.get().by_id(pset["id"])
|
||||||
|
else:
|
||||||
|
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Drawing")
|
||||||
|
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={name: value})
|
||||||
|
|
||||||
|
|
||||||
def getTitleblocks(self, context):
|
def getTitleblocks(self, context):
|
||||||
global titleblocks_enum
|
global titleblocks_enum
|
||||||
if len(titleblocks_enum) < 1:
|
if len(titleblocks_enum) < 1:
|
||||||
@@ -276,9 +300,6 @@ class RasterStyleProperty(enum.Enum):
|
|||||||
|
|
||||||
|
|
||||||
class DocProperties(PropertyGroup):
|
class DocProperties(PropertyGroup):
|
||||||
has_underlay: BoolProperty(name="Underlay", default=False)
|
|
||||||
has_linework: BoolProperty(name="Linework", default=True)
|
|
||||||
has_annotation: BoolProperty(name="Annotation", default=True)
|
|
||||||
should_use_underlay_cache: BoolProperty(name="Use Underlay Cache", default=False)
|
should_use_underlay_cache: BoolProperty(name="Use Underlay Cache", default=False)
|
||||||
should_use_linework_cache: BoolProperty(name="Use Linework Cache", default=False)
|
should_use_linework_cache: BoolProperty(name="Use Linework Cache", default=False)
|
||||||
should_use_annotation_cache: BoolProperty(name="Use Annotation Cache", default=False)
|
should_use_annotation_cache: BoolProperty(name="Use Annotation Cache", default=False)
|
||||||
@@ -320,6 +341,9 @@ class DocProperties(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class BIMCameraProperties(PropertyGroup):
|
class BIMCameraProperties(PropertyGroup):
|
||||||
|
has_underlay: BoolProperty(name="Underlay", default=False, update=update_has_underlay)
|
||||||
|
has_linework: BoolProperty(name="Linework", default=True, update=update_has_linework)
|
||||||
|
has_annotation: BoolProperty(name="Annotation", default=True, update=update_has_annotation)
|
||||||
representation: StringProperty(name="Representation")
|
representation: StringProperty(name="Representation")
|
||||||
view_name: StringProperty(name="View Name")
|
view_name: StringProperty(name="View Name")
|
||||||
diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale", update=update_diagram_scale)
|
diagram_scale: EnumProperty(items=get_diagram_scales, name="Drawing Scale", update=update_diagram_scale)
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import pystache
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
import svgwrite
|
import svgwrite
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim.module.drawing.helper as helper
|
import blenderbim.bim.module.drawing.helper as helper
|
||||||
@@ -153,8 +154,8 @@ class SvgWriter:
|
|||||||
|
|
||||||
for grid_obj in self.annotations.get("grid_objs", []):
|
for grid_obj in self.annotations.get("grid_objs", []):
|
||||||
matrix_world = grid_obj.matrix_world
|
matrix_world = grid_obj.matrix_world
|
||||||
|
classes = self.get_attribute_classes(grid_obj)
|
||||||
for edge in grid_obj.data.edges:
|
for edge in grid_obj.data.edges:
|
||||||
classes = ["annotation", "grid"]
|
|
||||||
v0_global = matrix_world @ grid_obj.data.vertices[edge.vertices[0]].co.xyz
|
v0_global = matrix_world @ grid_obj.data.vertices[edge.vertices[0]].co.xyz
|
||||||
v1_global = matrix_world @ grid_obj.data.vertices[edge.vertices[1]].co.xyz
|
v1_global = matrix_world @ grid_obj.data.vertices[edge.vertices[1]].co.xyz
|
||||||
v0 = self.project_point_onto_camera(v0_global)
|
v0 = self.project_point_onto_camera(v0_global)
|
||||||
@@ -167,8 +168,6 @@ class SvgWriter:
|
|||||||
start=tuple(start * self.scale), end=tuple(end * self.scale), class_=" ".join(classes)
|
start=tuple(start * self.scale), end=tuple(end * self.scale), class_=" ".join(classes)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
line["marker-start"] = "url(#grid-marker)"
|
|
||||||
line["marker-end"] = "url(#grid-marker)"
|
|
||||||
line["stroke-dasharray"] = "12.5, 3, 3, 3"
|
line["stroke-dasharray"] = "12.5, 3, 3, 3"
|
||||||
axis_tag = tool.Ifc.get_entity(grid_obj).Name
|
axis_tag = tool.Ifc.get_entity(grid_obj).Name
|
||||||
self.svg.add(
|
self.svg.add(
|
||||||
@@ -201,13 +200,13 @@ class SvgWriter:
|
|||||||
self.draw_ifc_annotation()
|
self.draw_ifc_annotation()
|
||||||
|
|
||||||
for obj in self.annotations.get("misc_objs", []):
|
for obj in self.annotations.get("misc_objs", []):
|
||||||
self.draw_misc_annotation(obj, ["IfcAnnotation"])
|
self.draw_misc_annotation(obj)
|
||||||
|
|
||||||
for obj_data in self.annotations.get("hidden_objs", []):
|
for obj_data in self.annotations.get("hidden_objs", []):
|
||||||
self.draw_line_annotation(obj_data, ["hidden"])
|
self.draw_line_annotation(obj_data)
|
||||||
|
|
||||||
for obj_data in self.annotations.get("solid_objs", []):
|
for obj_data in self.annotations.get("solid_objs", []):
|
||||||
self.draw_line_annotation(obj_data, ["solid"])
|
self.draw_line_annotation(obj_data)
|
||||||
|
|
||||||
self.draw_leader_annotations()
|
self.draw_leader_annotations()
|
||||||
|
|
||||||
@@ -216,8 +215,8 @@ class SvgWriter:
|
|||||||
|
|
||||||
if self.annotations.get("section_level_obj"):
|
if self.annotations.get("section_level_obj"):
|
||||||
matrix_world = self.annotations["section_level_obj"].matrix_world
|
matrix_world = self.annotations["section_level_obj"].matrix_world
|
||||||
|
classes = self.get_attribute_classes(self.annotations["section_level_obj"])
|
||||||
for spline in self.annotations["section_level_obj"].data.splines:
|
for spline in self.annotations["section_level_obj"].data.splines:
|
||||||
classes = ["annotation", "section-level"]
|
|
||||||
points = self.get_spline_points(spline)
|
points = self.get_spline_points(spline)
|
||||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||||
d = " ".join(
|
d = " ".join(
|
||||||
@@ -228,8 +227,6 @@ class SvgWriter:
|
|||||||
)
|
)
|
||||||
d = "M{}".format(d[1:])
|
d = "M{}".format(d[1:])
|
||||||
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
|
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
|
||||||
path["marker-start"] = "url(#section-level-marker)"
|
|
||||||
path["stroke-dasharray"] = "12.5, 3, 3, 3"
|
|
||||||
text_position = Vector(
|
text_position = Vector(
|
||||||
(
|
(
|
||||||
(x_offset + projected_points[0].x) * self.scale,
|
(x_offset + projected_points[0].x) * self.scale,
|
||||||
@@ -258,8 +255,8 @@ class SvgWriter:
|
|||||||
|
|
||||||
if self.annotations.get("stair_obj"):
|
if self.annotations.get("stair_obj"):
|
||||||
matrix_world = self.annotations["stair_obj"].matrix_world
|
matrix_world = self.annotations["stair_obj"].matrix_world
|
||||||
|
classes = self.get_attribute_classes(self.annotations["stair_obj"])
|
||||||
for spline in self.annotations["stair_obj"].data.splines:
|
for spline in self.annotations["stair_obj"].data.splines:
|
||||||
classes = ["annotation", "stair"]
|
|
||||||
points = self.get_spline_points(spline)
|
points = self.get_spline_points(spline)
|
||||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||||
d = " ".join(
|
d = " ".join(
|
||||||
@@ -316,13 +313,13 @@ class SvgWriter:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def draw_misc_annotation(self, obj, classes):
|
def draw_misc_annotation(self, obj):
|
||||||
# We have to decide whether this should come from Blender or from IFC.
|
# We have to decide whether this should come from Blender or from IFC.
|
||||||
# For the moment, for convenience of experimenting with ideas, it comes
|
# For the moment, for convenience of experimenting with ideas, it comes
|
||||||
# from Blender. In the future, it should probably come from IFC.
|
# from Blender. In the future, it should probably come from IFC.
|
||||||
if not isinstance(obj.data, bpy.types.Mesh):
|
if not isinstance(obj.data, bpy.types.Mesh):
|
||||||
return
|
return
|
||||||
classes.extend(self.get_attribute_classes(obj))
|
classes = self.get_attribute_classes(obj)
|
||||||
if len(obj.data.polygons) == 0:
|
if len(obj.data.polygons) == 0:
|
||||||
self.draw_edge_annotation(obj, classes)
|
self.draw_edge_annotation(obj, classes)
|
||||||
return
|
return
|
||||||
@@ -332,6 +329,7 @@ class SvgWriter:
|
|||||||
for polygon in obj.data.polygons:
|
for polygon in obj.data.polygons:
|
||||||
points = [obj.data.vertices[v] for v in polygon.vertices]
|
points = [obj.data.vertices[v] for v in polygon.vertices]
|
||||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||||
|
projected_points.append(projected_points[0])
|
||||||
d = " ".join(
|
d = " ".join(
|
||||||
[
|
[
|
||||||
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
|
"L {} {}".format((x_offset + p.x) * self.scale, (y_offset - p.y) * self.scale)
|
||||||
@@ -342,12 +340,12 @@ class SvgWriter:
|
|||||||
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
|
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
|
||||||
|
|
||||||
def get_attribute_classes(self, obj):
|
def get_attribute_classes(self, obj):
|
||||||
classes = [obj.name.split("/")[0]]
|
element = tool.Ifc.get_entity(obj)
|
||||||
for slot in obj.material_slots:
|
global_id = "GlobalId-{}".format(element.GlobalId)
|
||||||
if slot.material:
|
predefined_type = "PredefinedType-" + self.canonicalise_class_name(
|
||||||
classes.append("material-{}".format(re.sub("[^0-9a-zA-Z]+", "", slot.material.name)))
|
str(ifcopenshell.util.element.get_predefined_type(element))
|
||||||
global_id = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
|
)
|
||||||
classes.append("globalid-{}".format(global_id))
|
classes = [global_id, element.is_a(), predefined_type]
|
||||||
for attribute in self.annotations.get("attributes", []):
|
for attribute in self.annotations.get("attributes", []):
|
||||||
result = self.get_obj_value(obj, attribute)
|
result = self.get_obj_value(obj, attribute)
|
||||||
if result:
|
if result:
|
||||||
@@ -356,6 +354,9 @@ class SvgWriter:
|
|||||||
)
|
)
|
||||||
return classes
|
return classes
|
||||||
|
|
||||||
|
def canonicalise_class_name(self, name):
|
||||||
|
return re.sub("[^0-9a-zA-Z]+", "", name)
|
||||||
|
|
||||||
def get_obj_value(self, obj, key):
|
def get_obj_value(self, obj, key):
|
||||||
# This is a duplicate implementation of the IFC selector key in Blender
|
# This is a duplicate implementation of the IFC selector key in Blender
|
||||||
# In the future if all this becomes purely IFC based this can be deleted
|
# In the future if all this becomes purely IFC based this can be deleted
|
||||||
@@ -381,14 +382,14 @@ class SvgWriter:
|
|||||||
if result:
|
if result:
|
||||||
return result.string_value
|
return result.string_value
|
||||||
|
|
||||||
def draw_line_annotation(self, obj_data, classes):
|
def draw_line_annotation(self, obj_data):
|
||||||
# TODO: properly scope these offsets
|
# TODO: properly scope these offsets
|
||||||
x_offset = self.raw_width / 2
|
x_offset = self.raw_width / 2
|
||||||
y_offset = self.raw_height / 2
|
y_offset = self.raw_height / 2
|
||||||
|
|
||||||
obj, data = obj_data
|
obj, data = obj_data
|
||||||
|
|
||||||
classes.extend(["annotation"])
|
classes = self.get_attribute_classes(obj)
|
||||||
matrix_world = obj.matrix_world
|
matrix_world = obj.matrix_world
|
||||||
|
|
||||||
if isinstance(data, bpy.types.Curve):
|
if isinstance(data, bpy.types.Curve):
|
||||||
@@ -424,7 +425,7 @@ class SvgWriter:
|
|||||||
|
|
||||||
def draw_leader_annotations(self):
|
def draw_leader_annotations(self):
|
||||||
for obj in self.annotations.get("leader_objs", []):
|
for obj in self.annotations.get("leader_objs", []):
|
||||||
self.draw_line_annotation((obj, obj.data), ["leader"])
|
self.draw_line_annotation((obj, obj.data))
|
||||||
spline = obj.data.splines[0]
|
spline = obj.data.splines[0]
|
||||||
spline_points = spline.bezier_points if spline.bezier_points else spline.points
|
spline_points = spline.bezier_points if spline.bezier_points else spline.points
|
||||||
if spline_points:
|
if spline_points:
|
||||||
@@ -437,7 +438,7 @@ class SvgWriter:
|
|||||||
x_offset = self.raw_width / 2
|
x_offset = self.raw_width / 2
|
||||||
y_offset = self.raw_height / 2
|
y_offset = self.raw_height / 2
|
||||||
|
|
||||||
self.draw_line_annotation((obj, obj.data), ["solid"])
|
self.draw_line_annotation((obj, obj.data))
|
||||||
|
|
||||||
v1 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, 0)))
|
v1 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, 0)))
|
||||||
v2 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, -1)))
|
v2 = self.project_point_onto_camera(obj.matrix_world @ Vector((0, 0, -1)))
|
||||||
@@ -632,8 +633,8 @@ class SvgWriter:
|
|||||||
x_offset = self.raw_width / 2
|
x_offset = self.raw_width / 2
|
||||||
y_offset = self.raw_height / 2
|
y_offset = self.raw_height / 2
|
||||||
matrix_world = obj.matrix_world
|
matrix_world = obj.matrix_world
|
||||||
|
classes = self.get_attribute_classes(obj)
|
||||||
for spline in obj.data.splines:
|
for spline in obj.data.splines:
|
||||||
classes = ["annotation", "plan-level"]
|
|
||||||
points = self.get_spline_points(spline)
|
points = self.get_spline_points(spline)
|
||||||
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
|
||||||
d = " ".join(
|
d = " ".join(
|
||||||
@@ -644,7 +645,6 @@ class SvgWriter:
|
|||||||
)
|
)
|
||||||
d = "M{}".format(d[1:])
|
d = "M{}".format(d[1:])
|
||||||
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
|
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
|
||||||
path["marker-end"] = "url(#plan-level-marker)"
|
|
||||||
text_position = Vector(
|
text_position = Vector(
|
||||||
(
|
(
|
||||||
(x_offset + projected_points[0].x) * self.scale,
|
(x_offset + projected_points[0].x) * self.scale,
|
||||||
@@ -676,6 +676,7 @@ class SvgWriter:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def draw_dimension_annotations(self, dimension_obj, text_override=None):
|
def draw_dimension_annotations(self, dimension_obj, text_override=None):
|
||||||
|
classes = self.get_attribute_classes(dimension_obj)
|
||||||
matrix_world = dimension_obj.matrix_world
|
matrix_world = dimension_obj.matrix_world
|
||||||
for spline in dimension_obj.data.splines:
|
for spline in dimension_obj.data.splines:
|
||||||
points = self.get_spline_points(spline)
|
points = self.get_spline_points(spline)
|
||||||
@@ -684,7 +685,7 @@ class SvgWriter:
|
|||||||
continue
|
continue
|
||||||
v0_global = matrix_world @ points[i].co.xyz
|
v0_global = matrix_world @ points[i].co.xyz
|
||||||
v1_global = matrix_world @ points[i + 1].co.xyz
|
v1_global = matrix_world @ points[i + 1].co.xyz
|
||||||
self.draw_dimension_annotation(v0_global, v1_global, text_override)
|
self.draw_dimension_annotation(v0_global, v1_global, classes, text_override)
|
||||||
|
|
||||||
def draw_measureit_arch_dimension_annotations(self):
|
def draw_measureit_arch_dimension_annotations(self):
|
||||||
try:
|
try:
|
||||||
@@ -694,10 +695,11 @@ class SvgWriter:
|
|||||||
except:
|
except:
|
||||||
return
|
return
|
||||||
for coord in coords:
|
for coord in coords:
|
||||||
self.draw_dimension_annotation(Vector(coord[0]), Vector(coord[1]))
|
self.draw_dimension_annotation(
|
||||||
|
Vector(coord[0]), Vector(coord[1]), ["IfcAnnotation", "PredefinedType-DIMENSION"]
|
||||||
|
)
|
||||||
|
|
||||||
def draw_dimension_annotation(self, v0_global, v1_global, text_override=None):
|
def draw_dimension_annotation(self, v0_global, v1_global, classes, text_override=None):
|
||||||
classes = ["annotation", "dimension"]
|
|
||||||
x_offset = self.raw_width / 2
|
x_offset = self.raw_width / 2
|
||||||
y_offset = self.raw_height / 2
|
y_offset = self.raw_height / 2
|
||||||
v0 = self.project_point_onto_camera(v0_global)
|
v0 = self.project_point_onto_camera(v0_global)
|
||||||
@@ -719,8 +721,6 @@ class SvgWriter:
|
|||||||
line = self.svg.add(
|
line = self.svg.add(
|
||||||
self.svg.line(start=tuple(start * self.scale), end=tuple(end * self.scale), class_=" ".join(classes))
|
self.svg.line(start=tuple(start * self.scale), end=tuple(end * self.scale), class_=" ".join(classes))
|
||||||
)
|
)
|
||||||
line["marker-start"] = "url(#dimension-marker-start)"
|
|
||||||
line["marker-end"] = "url(#dimension-marker-end)"
|
|
||||||
if text_override is not None:
|
if text_override is not None:
|
||||||
text = text_override
|
text = text_override
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ class BIM_PT_camera(Panel):
|
|||||||
|
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.prop(dprops, "has_underlay", icon="OUTLINER_OB_IMAGE")
|
row.prop(props, "has_underlay", icon="OUTLINER_OB_IMAGE")
|
||||||
row.prop(dprops, "should_use_underlay_cache", text="", icon="FILE_REFRESH")
|
row.prop(dprops, "should_use_underlay_cache", text="", icon="FILE_REFRESH")
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.prop(dprops, "has_linework", icon="IMAGE_DATA")
|
row.prop(props, "has_linework", icon="IMAGE_DATA")
|
||||||
row.prop(dprops, "should_use_linework_cache", text="", icon="FILE_REFRESH")
|
row.prop(dprops, "should_use_linework_cache", text="", icon="FILE_REFRESH")
|
||||||
row = col.row(align=True)
|
row = col.row(align=True)
|
||||||
row.prop(dprops, "has_annotation", icon="MOD_EDGESPLIT")
|
row.prop(props, "has_annotation", icon="MOD_EDGESPLIT")
|
||||||
row.prop(dprops, "should_use_annotation_cache", text="", icon="FILE_REFRESH")
|
row.prop(dprops, "should_use_annotation_cache", text="", icon="FILE_REFRESH")
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
|
|||||||
Reference in New Issue
Block a user