diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py
index cf1018c3d7..38af2fc914 100644
--- a/src/blenderbim/blenderbim/bim/helper.py
+++ b/src/blenderbim/blenderbim/bim/helper.py
@@ -267,6 +267,35 @@ def convert_property_group_from_si(property_group, skip_props=()):
setattr(property_group, prop_name, prop_value)
+def draw_image_for_ifc_profile(draw, profile, size):
+ """generates image based on `profile` using `PIL.ImageDraw`"""
+ 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)
+
+
class IfcHeaderExtractor:
def __init__(self, filepath: str):
self.filepath = filepath
diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py
index cd4c662948..397563b416 100644
--- a/src/blenderbim/blenderbim/bim/module/model/product.py
+++ b/src/blenderbim/blenderbim/bim/module/model/product.py
@@ -32,6 +32,7 @@ from . import wall, slab, profile, mep
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.module.model.prop import store_cursor_position
+from blenderbim.bim.helper import draw_image_for_ifc_profile
from ifcopenshell.api.pset.data import Data as PsetData
from mathutils import Vector, Matrix
from bpy_extras.object_utils import AddObjectHelper
@@ -370,30 +371,8 @@ class LoadTypeThumbnails(bpy.types.Operator, tool.Ifc.Operator):
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)]
+ draw_image_for_ifc_profile(draw, profile, size)
- 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)
diff --git a/src/blenderbim/blenderbim/bim/module/profile/__init__.py b/src/blenderbim/blenderbim/bim/module/profile/__init__.py
index 101e9a3cde..9c697aaf60 100644
--- a/src/blenderbim/blenderbim/bim/module/profile/__init__.py
+++ b/src/blenderbim/blenderbim/bim/module/profile/__init__.py
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see .
import bpy
-from . import ui, prop, operator
+from . import ui, prop, operator, data
classes = (
operator.AddProfileDef,
@@ -43,3 +43,5 @@ def register():
def unregister():
del bpy.types.Scene.BIMProfileProperties
+ if data.ProfileData.preview_collection:
+ bpy.utils.previews.remove(data.ProfileData.preview_collection)
diff --git a/src/blenderbim/blenderbim/bim/module/profile/data.py b/src/blenderbim/blenderbim/bim/module/profile/data.py
index 23c03c969a..83f614319f 100644
--- a/src/blenderbim/blenderbim/bim/module/profile/data.py
+++ b/src/blenderbim/blenderbim/bim/module/profile/data.py
@@ -27,6 +27,7 @@ def refresh():
class ProfileData:
data = {}
+ preview_collection = bpy.utils.previews.new()
is_loaded = False
@classmethod
diff --git a/src/blenderbim/blenderbim/bim/module/profile/operator.py b/src/blenderbim/blenderbim/bim/module/profile/operator.py
index 6972837cc8..f925609b60 100644
--- a/src/blenderbim/blenderbim/bim/module/profile/operator.py
+++ b/src/blenderbim/blenderbim/bim/module/profile/operator.py
@@ -22,6 +22,7 @@ import blenderbim.bim.helper
import blenderbim.tool as tool
import blenderbim.bim.module.model.profile as model_profile
from blenderbim.bim.module.model.slab import DecorationsHandler
+from blenderbim.bim.module.profile.prop import generate_thumbnail_for_active_profile
class LoadProfiles(bpy.types.Operator):
@@ -103,6 +104,7 @@ class EditProfile(bpy.types.Operator, tool.Ifc.Operator):
ifcopenshell.api.run("profile.edit_profile", tool.Ifc.get(), profile=profile, attributes=attributes)
model_profile.DumbProfileRegenerator().regenerate_from_profile_def(profile)
bpy.ops.bim.load_profiles()
+ generate_thumbnail_for_active_profile()
class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator):
@@ -169,6 +171,7 @@ class EditArbitraryProfile(bpy.types.Operator, tool.Ifc.Operator):
profile = tool.Model.export_profile(obj)
if not profile:
+
def msg(self, context):
self.layout.label(text="INVALID PROFILE: " + indices[1])
diff --git a/src/blenderbim/blenderbim/bim/module/profile/prop.py b/src/blenderbim/blenderbim/bim/module/profile/prop.py
index 3b7b0a91ef..8c833c95ee 100644
--- a/src/blenderbim/blenderbim/bim/module/profile/prop.py
+++ b/src/blenderbim/blenderbim/bim/module/profile/prop.py
@@ -20,9 +20,11 @@ import bpy
import ifcopenshell
import ifcopenshell.util.schema
import ifcopenshell.util.attribute
+import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.profile.data import ProfileData
+from blenderbim.bim.helper import draw_image_for_ifc_profile
from bpy.types import PropertyGroup
from bpy.props import (
PointerProperty,
@@ -55,3 +57,35 @@ class BIMProfileProperties(PropertyGroup):
active_profile_id: IntProperty(name="Active Profile Id")
profile_attributes: CollectionProperty(name="Profile Attributes", type=Attribute)
profile_classes: EnumProperty(items=get_profile_classes, name="Profile Classes")
+
+
+def generate_thumbnail_for_active_profile():
+ from PIL import Image, ImageDraw
+
+ if bpy.app.background:
+ return
+
+ props = bpy.context.scene.BIMProfileProperties
+ ifc_file = tool.Ifc.get()
+ preview_collection = ProfileData.preview_collection
+
+ if not props.profiles:
+ bpy.ops.bim.load_profiles()
+
+ profile_id = props.profiles[props.active_profile_index].ifc_definition_id
+ profile = ifc_file.by_id(profile_id)
+
+ # generate image
+ size = 128
+ img = Image.new("RGBA", (size, size))
+ draw = ImageDraw.Draw(img)
+ draw_image_for_ifc_profile(draw, profile, size)
+ pixels = [item for sublist in img.getdata() for item in sublist]
+
+ # save generated image to preview collection
+ profile_id_str = str(profile_id)
+ if profile_id_str in preview_collection:
+ del preview_collection[profile_id_str]
+ preview_image = preview_collection.new(profile_id_str)
+ preview_image.image_size = size, size
+ preview_image.image_pixels_float = pixels
diff --git a/src/blenderbim/blenderbim/bim/module/profile/ui.py b/src/blenderbim/blenderbim/bim/module/profile/ui.py
index 1226be9f87..4228c9f225 100644
--- a/src/blenderbim/blenderbim/bim/module/profile/ui.py
+++ b/src/blenderbim/blenderbim/bim/module/profile/ui.py
@@ -16,12 +16,15 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see .
+from bpy.types import Panel, UIList
+import bpy
+
import blenderbim.bim.helper
import blenderbim.tool as tool
-from bpy.types import Panel, UIList
from blenderbim.bim.module.profile.data import ProfileData
+from blenderbim.bim.module.profile.prop import generate_thumbnail_for_active_profile
-
+# Profile Manager Class
class BIM_PT_profiles(Panel):
bl_label = "IFC Profiles"
bl_idname = "BIM_PT_profiles"
@@ -40,6 +43,18 @@ class BIM_PT_profiles(Panel):
ProfileData.load()
self.props = context.scene.BIMProfileProperties
+ if self.props.is_editing:
+ preview_collection = ProfileData.preview_collection
+ box = self.layout.box()
+ profile_id = self.props.profiles[self.props.active_profile_index].ifc_definition_id
+ preview_image = preview_collection.get(str(profile_id), None)
+
+ if preview_image:
+ box.template_icon(icon_value=preview_image.icon_id, scale=5)
+ else:
+ # TODO: test if called multiple times before one is loaded
+ generate_thumbnail_for_active_profile()
+
row = self.layout.row(align=True)
row.label(text=f"{ProfileData.data['total_profiles']} Named Profiles Found", icon="SNAP_GRID")
if self.props.is_editing: