Merge branch 'v0.7.0' of https://github.com/IfcOpenShell/IfcOpenShell into pr-daily-builds

This commit is contained in:
krande
2022-04-16 17:02:10 +02:00
9 changed files with 177 additions and 61 deletions
@@ -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; }
.projection { fill: white; 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 {}
.leader { marker-end: url(#leader-marker); }
.stair { marker-start: url(#stair-marker-start); marker-end: url(#stair-marker-end); }
.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; }
.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; }
.breakline { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; marker-mid: url(#breakline-marker); }
.material-blank { fill: white; }
@@ -44,5 +53,4 @@ text { fill: black; stroke: none; }
.material-honeycomb { fill: url(#honeycomb); }
.material-sand { fill: url(#sand); }
.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; }
+7 -7
View File
@@ -157,12 +157,12 @@ def loadIfcStore(scene):
if not IfcStore.get_file():
return
IfcStore.get_schema()
IfcStore.reload_linked_elements()
IfcStore.relink_all_objects()
@persistent
def undo_pre(scene):
IfcStore.update_undo_redo_stack_objects()
IfcStore.track_undo_redo_stack_object_map()
@persistent
@@ -171,13 +171,13 @@ def undo_post(scene):
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
IfcStore.undo()
purge_module_data()
IfcStore.update_undo_redo_stack_objects()
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
IfcStore.track_undo_redo_stack_selected_objects()
IfcStore.reload_undo_redo_stack_objects()
@persistent
def redo_pre(scene):
IfcStore.update_undo_redo_stack_objects()
IfcStore.track_undo_redo_stack_object_map()
@persistent
@@ -186,8 +186,8 @@ def redo_post(scene):
IfcStore.last_transaction = bpy.context.scene.BIMProperties.last_transaction
IfcStore.redo()
purge_module_data()
IfcStore.update_undo_redo_stack_objects()
IfcStore.reload_linked_elements(objects=[bpy.data.objects.get(o) for o in IfcStore.undo_redo_stack_objects])
IfcStore.track_undo_redo_stack_selected_objects()
IfcStore.reload_undo_redo_stack_objects()
@persistent
+68 -7
View File
@@ -43,6 +43,7 @@ class IfcStore:
library_file = None
element_listeners = set()
undo_redo_stack_objects = set()
undo_redo_stack_object_names = {}
current_transaction = ""
last_transaction = ""
history = []
@@ -147,14 +148,32 @@ class IfcStore:
IfcStore.element_listeners.add(callback)
@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
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
only occur for selected objects either pre/post undo/redo event, including
selected objects for consecutive undo/redos, and all children.
occur for selected objects either pre/post undo/redo event, including
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
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
@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()
if not file:
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:
continue
if not obj.BIMObjectProperties.ifc_definition_id:
@@ -187,6 +215,38 @@ class IfcStore:
data["guid"] = element.GlobalId
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
def delete_element(element):
IfcStore.deleted_ids.add(element.id())
@@ -312,6 +372,7 @@ class IfcStore:
@staticmethod
def begin_transaction(operator):
IfcStore.undo_redo_stack_objects = set()
IfcStore.undo_redo_stack_object_names = {}
IfcStore.current_transaction = str(uuid.uuid4())
operator.transaction_key = IfcStore.current_transaction
@@ -1682,6 +1682,12 @@ class IfcImporter:
else:
camera.BIMCameraProperties.diagram_scale = "CUSTOM"
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
def create_curve(self, element, shape):
@@ -119,9 +119,10 @@ class CreateDrawing(bpy.types.Operator):
start = time.time()
self.profile_code("Start drawing generation process")
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.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="")
underlay_svg = self.generate_underlay(context)
self.profile_code("Generate underlay")
@@ -186,12 +187,12 @@ class CreateDrawing(bpy.types.Operator):
return svg_path
def generate_underlay(self, context):
if not self.props.has_underlay:
if not self.cprops.has_underlay:
return
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"
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":
@@ -246,7 +247,7 @@ class CreateDrawing(bpy.types.Operator):
return svg_path
def generate_linework(self, context):
if not self.props.has_linework:
if not self.cprops.has_linework:
return
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:
@@ -410,7 +411,7 @@ class CreateDrawing(bpy.types.Operator):
return re.sub("[^0-9a-zA-Z]+", "", name)
def generate_annotation(self, context):
if not self.props.has_annotation:
if not self.cprops.has_annotation:
return
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:
@@ -420,7 +421,7 @@ class CreateDrawing(bpy.types.Operator):
svg_writer = svgwriter.SvgWriter()
drawing_style = context.scene.DocProperties.drawing_styles[
camera.data.BIMCameraProperties.active_drawing_style_index
self.cprops.active_drawing_style_index
]
render = context.scene.render
@@ -473,7 +474,7 @@ class CreateDrawing(bpy.types.Operator):
svg_writer.annotations["section_level_obj"] = obj
elif element.ObjectType == "TEXT":
svg_writer.annotations.setdefault("text_objs", []).append(obj)
elif element.ObjectType == "MISC":
else:
svg_writer.annotations.setdefault("misc_objs", []).append(obj)
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)
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):
global titleblocks_enum
if len(titleblocks_enum) < 1:
@@ -276,9 +300,6 @@ class RasterStyleProperty(enum.Enum):
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_linework_cache: BoolProperty(name="Use Linework Cache", default=False)
should_use_annotation_cache: BoolProperty(name="Use Annotation Cache", default=False)
@@ -320,6 +341,9 @@ class DocProperties(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")
view_name: StringProperty(name="View Name")
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 svgwrite
import ifcopenshell
import ifcopenshell.util.element
import ifcopenshell.util.representation
import blenderbim.tool as tool
import blenderbim.bim.module.drawing.helper as helper
@@ -153,8 +154,8 @@ class SvgWriter:
for grid_obj in self.annotations.get("grid_objs", []):
matrix_world = grid_obj.matrix_world
classes = self.get_attribute_classes(grid_obj)
for edge in grid_obj.data.edges:
classes = ["annotation", "grid"]
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
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)
)
)
line["marker-start"] = "url(#grid-marker)"
line["marker-end"] = "url(#grid-marker)"
line["stroke-dasharray"] = "12.5, 3, 3, 3"
axis_tag = tool.Ifc.get_entity(grid_obj).Name
self.svg.add(
@@ -201,13 +200,13 @@ class SvgWriter:
self.draw_ifc_annotation()
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", []):
self.draw_line_annotation(obj_data, ["hidden"])
self.draw_line_annotation(obj_data)
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()
@@ -216,8 +215,8 @@ class SvgWriter:
if self.annotations.get("section_level_obj"):
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:
classes = ["annotation", "section-level"]
points = self.get_spline_points(spline)
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
d = " ".join(
@@ -228,8 +227,6 @@ class SvgWriter:
)
d = "M{}".format(d[1:])
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(
(
(x_offset + projected_points[0].x) * self.scale,
@@ -258,8 +255,8 @@ class SvgWriter:
if self.annotations.get("stair_obj"):
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:
classes = ["annotation", "stair"]
points = self.get_spline_points(spline)
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
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.
# For the moment, for convenience of experimenting with ideas, it comes
# from Blender. In the future, it should probably come from IFC.
if not isinstance(obj.data, bpy.types.Mesh):
return
classes.extend(self.get_attribute_classes(obj))
classes = self.get_attribute_classes(obj)
if len(obj.data.polygons) == 0:
self.draw_edge_annotation(obj, classes)
return
@@ -332,6 +329,7 @@ class SvgWriter:
for polygon in obj.data.polygons:
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.append(projected_points[0])
d = " ".join(
[
"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)))
def get_attribute_classes(self, obj):
classes = [obj.name.split("/")[0]]
for slot in obj.material_slots:
if slot.material:
classes.append("material-{}".format(re.sub("[^0-9a-zA-Z]+", "", slot.material.name)))
global_id = IfcStore.get_file().by_id(obj.BIMObjectProperties.ifc_definition_id).GlobalId
classes.append("globalid-{}".format(global_id))
element = tool.Ifc.get_entity(obj)
global_id = "GlobalId-{}".format(element.GlobalId)
predefined_type = "PredefinedType-" + self.canonicalise_class_name(
str(ifcopenshell.util.element.get_predefined_type(element))
)
classes = [global_id, element.is_a(), predefined_type]
for attribute in self.annotations.get("attributes", []):
result = self.get_obj_value(obj, attribute)
if result:
@@ -356,6 +354,9 @@ class SvgWriter:
)
return classes
def canonicalise_class_name(self, name):
return re.sub("[^0-9a-zA-Z]+", "", name)
def get_obj_value(self, obj, key):
# 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
@@ -381,14 +382,14 @@ class SvgWriter:
if result:
return result.string_value
def draw_line_annotation(self, obj_data, classes):
def draw_line_annotation(self, obj_data):
# TODO: properly scope these offsets
x_offset = self.raw_width / 2
y_offset = self.raw_height / 2
obj, data = obj_data
classes.extend(["annotation"])
classes = self.get_attribute_classes(obj)
matrix_world = obj.matrix_world
if isinstance(data, bpy.types.Curve):
@@ -424,7 +425,7 @@ class SvgWriter:
def draw_leader_annotations(self):
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_points = spline.bezier_points if spline.bezier_points else spline.points
if spline_points:
@@ -437,7 +438,7 @@ class SvgWriter:
x_offset = self.raw_width / 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)))
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
y_offset = self.raw_height / 2
matrix_world = obj.matrix_world
classes = self.get_attribute_classes(obj)
for spline in obj.data.splines:
classes = ["annotation", "plan-level"]
points = self.get_spline_points(spline)
projected_points = [self.project_point_onto_camera(matrix_world @ p.co.xyz) for p in points]
d = " ".join(
@@ -644,7 +645,6 @@ class SvgWriter:
)
d = "M{}".format(d[1:])
path = self.svg.add(self.svg.path(d=d, class_=" ".join(classes)))
path["marker-end"] = "url(#plan-level-marker)"
text_position = Vector(
(
(x_offset + projected_points[0].x) * self.scale,
@@ -676,6 +676,7 @@ class SvgWriter:
)
def draw_dimension_annotations(self, dimension_obj, text_override=None):
classes = self.get_attribute_classes(dimension_obj)
matrix_world = dimension_obj.matrix_world
for spline in dimension_obj.data.splines:
points = self.get_spline_points(spline)
@@ -684,7 +685,7 @@ class SvgWriter:
continue
v0_global = matrix_world @ points[i].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):
try:
@@ -694,10 +695,11 @@ class SvgWriter:
except:
return
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):
classes = ["annotation", "dimension"]
def draw_dimension_annotation(self, v0_global, v1_global, classes, text_override=None):
x_offset = self.raw_width / 2
y_offset = self.raw_height / 2
v0 = self.project_point_onto_camera(v0_global)
@@ -719,8 +721,6 @@ class SvgWriter:
line = self.svg.add(
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:
text = text_override
else:
@@ -47,13 +47,13 @@ class BIM_PT_camera(Panel):
col = layout.column(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 = 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 = 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 = layout.row()