Experimental new thumbnail with smarter layer / profile thumbnails and no timers.

This commit is contained in:
Dion Moult
2022-10-10 23:13:37 +11:00
parent a5517071cc
commit 16582cf57f
5 changed files with 151 additions and 18 deletions
+5 -1
View File
@@ -328,7 +328,11 @@ class IfcImporter:
if self.ifc_import_settings.has_filter or offset or offset_limit < len(self.elements):
self.element_types = set([ifcopenshell.util.element.get_type(e) for e in self.elements])
else:
self.element_types = set(self.file.by_type("IfcElementType"))
self.element_types = set(
self.file.by_type("IfcElementType")
+ self.file.by_type("IfcDoorStyle")
+ self.file.by_type("IfcWindowStyle")
)
if self.ifc_import_settings.has_filter and self.ifc_import_settings.should_filter_spatial_elements:
self.spatial_elements = self.get_spatial_elements_filtered_by_elements(self.elements)
@@ -29,21 +29,32 @@ def refresh():
class AuthoringData:
data = {}
type_thumbnails = {}
is_loaded = False
@classmethod
def load(cls):
cls.is_loaded = True
if not hasattr(cls, "data"):
cls.data = {}
cls.props = bpy.context.scene.BIMModelProperties
cls.load_ifc_classes()
cls.load_relating_types()
cls.load_relating_types_browser()
cls.data["type_thumbnail"] = cls.type_thumbnail()
cls.data["is_voidable_element"] = cls.is_voidable_element()
cls.data["has_visible_openings"] = cls.has_visible_openings()
cls.data["active_class"] = cls.active_class()
@classmethod
def type_thumbnail(cls):
props = bpy.context.scene.BIMModelProperties
if not props.relating_type_id:
return 0
element = tool.Ifc.get().by_id(int(props.relating_type_id))
obj = tool.Ifc.get_object(element)
if not obj:
return 0
return cls.type_thumbnails.get(element.id(), None) or 0
@classmethod
def load_ifc_classes(cls):
cls.data["ifc_classes"] = cls.ifc_classes()
@@ -339,6 +339,122 @@ class DynamicallyVoidProduct(bpy.types.Operator):
return {"FINISHED"}
class LoadTypeThumbnails(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.load_type_thumbnails"
bl_label = "Load Type Thumbnails"
bl_options = {"REGISTER", "UNDO"}
ifc_class: bpy.props.StringProperty()
def _execute(self, context):
from PIL import Image, ImageDraw
processing = set()
# Only process at most one class at a time.
# Large projects have hundreds of types which can lead to unnecessary lag.
queue = tool.Ifc.get().by_type(self.ifc_class)
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
while queue:
# if bpy.app.is_job_running("RENDER_PREVIEW") does not seem to reflect asset preview generation
element = queue.pop()
obj = tool.Ifc.get_object(element)
if not obj:
continue # Nothing to process
elif AuthoringData.type_thumbnails.get(element.id(), None):
continue # Already processed
elif obj.preview and obj.preview.icon_id:
AuthoringData.type_thumbnails[element.id()] = obj.preview.icon_id
continue
if obj.data:
obj.asset_generate_preview()
while not obj.preview:
pass
else:
size = 128
img = Image.new("RGBA", (size, size))
draw = ImageDraw.Draw(img)
material = ifcopenshell.util.element.get_material(element)
if material and material.is_a("IfcMaterialProfileSet"):
profile = material.MaterialProfiles[0].Profile
settings = ifcopenshell.geom.settings()
settings.set(settings.INCLUDE_CURVES, True)
shape = ifcopenshell.geom.create_shape(settings, profile)
verts = shape.verts
edges = shape.edges
grouped_verts = [[verts[i], verts[i + 1]] for i in range(0, len(verts), 3)]
grouped_edges = [[edges[i], edges[i + 1]] for i in range(0, len(edges), 2)]
max_x = max([v[0] for v in grouped_verts])
min_x = min([v[0] for v in grouped_verts])
max_y = max([v[1] for v in grouped_verts])
min_y = min([v[1] for v in grouped_verts])
dim_x = max_x - min_x
dim_y = max_y - min_y
max_dim = max([dim_x, dim_y])
scale = 100 / max_dim
for vert in grouped_verts:
vert[0] = round(scale * (vert[0] - min_x)) + ((size / 2) - scale * (dim_x / 2))
vert[1] = round(scale * (vert[1] - min_y)) + ((size / 2) - scale * (dim_y / 2))
for e in grouped_edges:
draw.line((tuple(grouped_verts[e[0]]), tuple(grouped_verts[e[1]])), fill="white", width=2)
elif material and material.is_a("IfcMaterialLayerSet"):
thicknesses = [l.LayerThickness for l in material.MaterialLayers]
total_thickness = sum(thicknesses)
si_total_thickness = total_thickness * unit_scale
if si_total_thickness < 0.05:
width = 10
elif si_total_thickness < 0.1:
width = 20
elif si_total_thickness < 0.2:
width = 30
elif si_total_thickness < 0.3:
width = 40
else:
width = 50
height = 100
if element.is_a("IfcSlabType"):
width, height = height, width
x_offset = (size / 2) - (width / 2)
y_offset = (size / 2) - (height / 2)
draw.rectangle([x_offset, y_offset, width + x_offset, height + y_offset], outline="white", width=2)
current_thickness = 0
for thickness in thicknesses:
current_thickness += thickness
if element.is_a("IfcSlabType"):
y = (current_thickness / total_thickness) * height
line = [x_offset, y_offset + y, x_offset + width, y_offset + y]
else:
x = (current_thickness / total_thickness) * width
line = [x_offset + x, y_offset, x_offset + x, y_offset + height]
draw.line(line, fill="white", width=2)
else:
# For things like parametric duct segments
draw.line([0, 0, size, size], fill="red", width=2)
draw.line([0, size, size, 0], fill="red", width=2)
pixels = [item for sublist in img.getdata() for item in sublist]
obj.asset_generate_preview()
while not obj.preview:
pass
obj.preview.image_size = size, size
obj.preview.image_pixels_float = pixels
queue.append(element)
return {"FINISHED"}
def generate_box(usecase_path, ifc_file, settings):
box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW")
if not box_context:
@@ -72,10 +72,8 @@ def update_icon_id(self, context, browser=False):
def update_ifc_class(self, context):
AuthoringData.load_ifc_classes()
AuthoringData.load_relating_types()
if not self.updating:
update_icon_id(self, context)
AuthoringData.data["relating_types_ids"] = AuthoringData.relating_types()
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
def update_ifc_class_browser(self, context):
@@ -93,8 +91,7 @@ def update_ifc_class_browser(self, context):
def update_relating_type(self, context):
AuthoringData.load_relating_types()
if not self.updating:
update_icon_id(self, context)
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
def update_relating_type_browser(self, context):
@@ -74,10 +74,10 @@ class BimTool(WorkSpaceTool):
AuthoringData.data["relating_types_ids"] if "relating_types_ids" in AuthoringData.data else False
)
if ifc_classes and relating_types_ids and not props.icon_id and not is_tool_header:
# hack Dion won't like to show a preview also on the first time the sidebar is shown
bpy.app.timers.register(lambda: prop.update_ifc_class(props, "lost_context"))
bpy.app.timers.register(lambda: prop.update_relating_type(props, "lost_context"))
#if ifc_classes and relating_types_ids and not props.icon_id and not is_tool_header:
# # hack Dion won't like to show a preview also on the first time the sidebar is shown
# bpy.app.timers.register(lambda: prop.update_ifc_class(props, "lost_context"))
# bpy.app.timers.register(lambda: prop.update_relating_type(props, "lost_context"))
ifc_class = props.ifc_class
relating_type_id = props.relating_type_id
@@ -96,8 +96,8 @@ class BimTool(WorkSpaceTool):
else:
row.label(text="No Construction Type", icon="FILE_3D")
if ifc_classes:
row = layout.row()
row.operator("bim.display_constr_types", icon="TRIA_DOWN", text="")
#row = layout.row()
#row.operator("bim.display_constr_types", icon="TRIA_DOWN", text="")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
@@ -107,7 +107,7 @@ class BimTool(WorkSpaceTool):
op.ifc_class = ifc_class
if relating_type_id.isnumeric():
op.relating_type_id = int(relating_type_id)
else:
elif is_sidebar:
row = layout.row(align=True)
if ifc_classes:
row.label(text="", icon="FILE_VOLUME")
@@ -120,9 +120,14 @@ class BimTool(WorkSpaceTool):
prop_with_search(row, props, "relating_type_id", text="")
else:
row.label(text="No Construction Type", icon="FILE_3D")
if is_sidebar and ifc_classes and relating_types_ids:
if AuthoringData.data["type_thumbnail"]:
box = layout.box()
box.template_icon(icon_value=props.icon_id, scale=8)
box.template_icon(icon_value=AuthoringData.data["type_thumbnail"], scale=5)
else:
box = layout.box()
op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH")
op.ifc_class = props.ifc_class
if ifc_classes:
row = layout.row(align=True)