Profile manager thumnails (#2568)

This commit is contained in:
Andrej730
2023-01-31 13:42:35 +05:00
parent 0fddd243c2
commit f01900bdb1
7 changed files with 89 additions and 26 deletions
+29
View File
@@ -267,6 +267,35 @@ def convert_property_group_from_si(property_group, skip_props=()):
setattr(property_group, prop_name, prop_value) 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: class IfcHeaderExtractor:
def __init__(self, filepath: str): def __init__(self, filepath: str):
self.filepath = filepath self.filepath = filepath
@@ -32,6 +32,7 @@ from . import wall, slab, profile, mep
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.model.data import AuthoringData from blenderbim.bim.module.model.data import AuthoringData
from blenderbim.bim.module.model.prop import store_cursor_position 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 ifcopenshell.api.pset.data import Data as PsetData
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from bpy_extras.object_utils import AddObjectHelper 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) material = ifcopenshell.util.element.get_material(element)
if material and material.is_a("IfcMaterialProfileSet"): if material and material.is_a("IfcMaterialProfileSet"):
profile = material.MaterialProfiles[0].Profile profile = material.MaterialProfiles[0].Profile
settings = ifcopenshell.geom.settings() draw_image_for_ifc_profile(draw, profile, size)
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"): elif material and material.is_a("IfcMaterialLayerSet"):
thicknesses = [l.LayerThickness for l in material.MaterialLayers] thicknesses = [l.LayerThickness for l in material.MaterialLayers]
total_thickness = sum(thicknesses) total_thickness = sum(thicknesses)
@@ -17,7 +17,7 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
from . import ui, prop, operator from . import ui, prop, operator, data
classes = ( classes = (
operator.AddProfileDef, operator.AddProfileDef,
@@ -43,3 +43,5 @@ def register():
def unregister(): def unregister():
del bpy.types.Scene.BIMProfileProperties del bpy.types.Scene.BIMProfileProperties
if data.ProfileData.preview_collection:
bpy.utils.previews.remove(data.ProfileData.preview_collection)
@@ -27,6 +27,7 @@ def refresh():
class ProfileData: class ProfileData:
data = {} data = {}
preview_collection = bpy.utils.previews.new()
is_loaded = False is_loaded = False
@classmethod @classmethod
@@ -22,6 +22,7 @@ import blenderbim.bim.helper
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.bim.module.model.profile as model_profile import blenderbim.bim.module.model.profile as model_profile
from blenderbim.bim.module.model.slab import DecorationsHandler 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): 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) ifcopenshell.api.run("profile.edit_profile", tool.Ifc.get(), profile=profile, attributes=attributes)
model_profile.DumbProfileRegenerator().regenerate_from_profile_def(profile) model_profile.DumbProfileRegenerator().regenerate_from_profile_def(profile)
bpy.ops.bim.load_profiles() bpy.ops.bim.load_profiles()
generate_thumbnail_for_active_profile()
class AddProfileDef(bpy.types.Operator, tool.Ifc.Operator): 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) profile = tool.Model.export_profile(obj)
if not profile: if not profile:
def msg(self, context): def msg(self, context):
self.layout.label(text="INVALID PROFILE: " + indices[1]) self.layout.label(text="INVALID PROFILE: " + indices[1])
@@ -20,9 +20,11 @@ import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.util.schema import ifcopenshell.util.schema
import ifcopenshell.util.attribute import ifcopenshell.util.attribute
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.prop import StrProperty, Attribute from blenderbim.bim.prop import StrProperty, Attribute
from blenderbim.bim.module.profile.data import ProfileData 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.types import PropertyGroup
from bpy.props import ( from bpy.props import (
PointerProperty, PointerProperty,
@@ -55,3 +57,35 @@ class BIMProfileProperties(PropertyGroup):
active_profile_id: IntProperty(name="Active Profile Id") active_profile_id: IntProperty(name="Active Profile Id")
profile_attributes: CollectionProperty(name="Profile Attributes", type=Attribute) profile_attributes: CollectionProperty(name="Profile Attributes", type=Attribute)
profile_classes: EnumProperty(items=get_profile_classes, name="Profile Classes") 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
@@ -16,12 +16,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from bpy.types import Panel, UIList
import bpy
import blenderbim.bim.helper import blenderbim.bim.helper
import blenderbim.tool as tool import blenderbim.tool as tool
from bpy.types import Panel, UIList
from blenderbim.bim.module.profile.data import ProfileData 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): class BIM_PT_profiles(Panel):
bl_label = "IFC Profiles" bl_label = "IFC Profiles"
bl_idname = "BIM_PT_profiles" bl_idname = "BIM_PT_profiles"
@@ -40,6 +43,18 @@ class BIM_PT_profiles(Panel):
ProfileData.load() ProfileData.load()
self.props = context.scene.BIMProfileProperties 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 = self.layout.row(align=True)
row.label(text=f"{ProfileData.data['total_profiles']} Named Profiles Found", icon="SNAP_GRID") row.label(text=f"{ProfileData.data['total_profiles']} Named Profiles Found", icon="SNAP_GRID")
if self.props.is_editing: if self.props.is_editing: