mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 14:31:39 +00:00
Experimental new thumbnail with smarter layer / profile thumbnails and no timers.
This commit is contained in:
@@ -328,7 +328,11 @@ class IfcImporter:
|
|||||||
if self.ifc_import_settings.has_filter or offset or offset_limit < len(self.elements):
|
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])
|
self.element_types = set([ifcopenshell.util.element.get_type(e) for e in self.elements])
|
||||||
else:
|
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:
|
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)
|
self.spatial_elements = self.get_spatial_elements_filtered_by_elements(self.elements)
|
||||||
|
|||||||
@@ -29,21 +29,32 @@ def refresh():
|
|||||||
|
|
||||||
class AuthoringData:
|
class AuthoringData:
|
||||||
data = {}
|
data = {}
|
||||||
|
type_thumbnails = {}
|
||||||
is_loaded = False
|
is_loaded = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def load(cls):
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
if not hasattr(cls, "data"):
|
|
||||||
cls.data = {}
|
|
||||||
cls.props = bpy.context.scene.BIMModelProperties
|
cls.props = bpy.context.scene.BIMModelProperties
|
||||||
cls.load_ifc_classes()
|
cls.load_ifc_classes()
|
||||||
cls.load_relating_types()
|
cls.load_relating_types()
|
||||||
cls.load_relating_types_browser()
|
cls.load_relating_types_browser()
|
||||||
|
cls.data["type_thumbnail"] = cls.type_thumbnail()
|
||||||
cls.data["is_voidable_element"] = cls.is_voidable_element()
|
cls.data["is_voidable_element"] = cls.is_voidable_element()
|
||||||
cls.data["has_visible_openings"] = cls.has_visible_openings()
|
cls.data["has_visible_openings"] = cls.has_visible_openings()
|
||||||
cls.data["active_class"] = cls.active_class()
|
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
|
@classmethod
|
||||||
def load_ifc_classes(cls):
|
def load_ifc_classes(cls):
|
||||||
cls.data["ifc_classes"] = cls.ifc_classes()
|
cls.data["ifc_classes"] = cls.ifc_classes()
|
||||||
|
|||||||
@@ -339,6 +339,122 @@ class DynamicallyVoidProduct(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
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):
|
def generate_box(usecase_path, ifc_file, settings):
|
||||||
box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW")
|
box_context = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Box", "MODEL_VIEW")
|
||||||
if not box_context:
|
if not box_context:
|
||||||
|
|||||||
@@ -72,10 +72,8 @@ def update_icon_id(self, context, browser=False):
|
|||||||
|
|
||||||
|
|
||||||
def update_ifc_class(self, context):
|
def update_ifc_class(self, context):
|
||||||
AuthoringData.load_ifc_classes()
|
AuthoringData.data["relating_types_ids"] = AuthoringData.relating_types()
|
||||||
AuthoringData.load_relating_types()
|
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
|
||||||
if not self.updating:
|
|
||||||
update_icon_id(self, context)
|
|
||||||
|
|
||||||
|
|
||||||
def update_ifc_class_browser(self, context):
|
def update_ifc_class_browser(self, context):
|
||||||
@@ -93,8 +91,7 @@ def update_ifc_class_browser(self, context):
|
|||||||
|
|
||||||
def update_relating_type(self, context):
|
def update_relating_type(self, context):
|
||||||
AuthoringData.load_relating_types()
|
AuthoringData.load_relating_types()
|
||||||
if not self.updating:
|
AuthoringData.data["type_thumbnail"] = AuthoringData.type_thumbnail()
|
||||||
update_icon_id(self, context)
|
|
||||||
|
|
||||||
|
|
||||||
def update_relating_type_browser(self, context):
|
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
|
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:
|
#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
|
# # 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_ifc_class(props, "lost_context"))
|
||||||
bpy.app.timers.register(lambda: prop.update_relating_type(props, "lost_context"))
|
# bpy.app.timers.register(lambda: prop.update_relating_type(props, "lost_context"))
|
||||||
|
|
||||||
ifc_class = props.ifc_class
|
ifc_class = props.ifc_class
|
||||||
relating_type_id = props.relating_type_id
|
relating_type_id = props.relating_type_id
|
||||||
@@ -96,8 +96,8 @@ class BimTool(WorkSpaceTool):
|
|||||||
else:
|
else:
|
||||||
row.label(text="No Construction Type", icon="FILE_3D")
|
row.label(text="No Construction Type", icon="FILE_3D")
|
||||||
if ifc_classes:
|
if ifc_classes:
|
||||||
row = layout.row()
|
#row = layout.row()
|
||||||
row.operator("bim.display_constr_types", icon="TRIA_DOWN", text="")
|
#row.operator("bim.display_constr_types", icon="TRIA_DOWN", text="")
|
||||||
|
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.label(text="", icon="EVENT_SHIFT")
|
row.label(text="", icon="EVENT_SHIFT")
|
||||||
@@ -107,7 +107,7 @@ class BimTool(WorkSpaceTool):
|
|||||||
op.ifc_class = ifc_class
|
op.ifc_class = ifc_class
|
||||||
if relating_type_id.isnumeric():
|
if relating_type_id.isnumeric():
|
||||||
op.relating_type_id = int(relating_type_id)
|
op.relating_type_id = int(relating_type_id)
|
||||||
else:
|
elif is_sidebar:
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
if ifc_classes:
|
if ifc_classes:
|
||||||
row.label(text="", icon="FILE_VOLUME")
|
row.label(text="", icon="FILE_VOLUME")
|
||||||
@@ -120,9 +120,14 @@ class BimTool(WorkSpaceTool):
|
|||||||
prop_with_search(row, props, "relating_type_id", text="")
|
prop_with_search(row, props, "relating_type_id", text="")
|
||||||
else:
|
else:
|
||||||
row.label(text="No Construction Type", icon="FILE_3D")
|
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 = 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:
|
if ifc_classes:
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user