mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Update type manager thumbnails on editing profiles
Example - https://imgur.com/a/IcV8IIR
This commit is contained in:
@@ -292,8 +292,6 @@ class LoadTypeThumbnails(bpy.types.Operator, tool.Ifc.Operator):
|
||||
offset: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
if bpy.app.background:
|
||||
return
|
||||
|
||||
@@ -310,106 +308,11 @@ class LoadTypeThumbnails(bpy.types.Operator, tool.Ifc.Operator):
|
||||
offset = 0
|
||||
queue = queue[offset : offset + 9]
|
||||
|
||||
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
|
||||
tool.Profile.draw_image_for_ifc_profile(draw, profile, size)
|
||||
|
||||
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.051:
|
||||
width = 10
|
||||
elif si_total_thickness <= 0.11:
|
||||
width = 20
|
||||
elif si_total_thickness <= 0.21:
|
||||
width = 30
|
||||
elif si_total_thickness <= 0.31:
|
||||
width = 40
|
||||
else:
|
||||
width = 50
|
||||
|
||||
height = 100
|
||||
|
||||
is_horizontal = False
|
||||
if element.is_a("IfcSlabType"):
|
||||
is_horizontal = True
|
||||
|
||||
parametric = ifcopenshell.util.element.get_psets(element).get("EPset_Parametric")
|
||||
if parametric:
|
||||
layer_set_direction = parametric.get("LayerSetDirection", None)
|
||||
if layer_set_direction == "AXIS2":
|
||||
is_horizontal = False
|
||||
elif layer_set_direction == "AXIS3":
|
||||
is_horizontal = True
|
||||
|
||||
if is_horizontal:
|
||||
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=5)
|
||||
current_thickness = 0
|
||||
del thicknesses[-1]
|
||||
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)
|
||||
elif False:
|
||||
# TODO: things like parametric duct segments
|
||||
pass
|
||||
elif not element.RepresentationMaps:
|
||||
# Empties are represented by a generic thumbnail
|
||||
width = height = 100
|
||||
x_offset = (size / 2) - (width / 2)
|
||||
y_offset = (size / 2) - (height / 2)
|
||||
draw.line([x_offset, y_offset, width + x_offset, height + y_offset], fill="white", width=2)
|
||||
draw.line([x_offset, y_offset + height, width + x_offset, y_offset], fill="white", width=2)
|
||||
draw.rectangle([x_offset, y_offset, width + x_offset, height + y_offset], outline="white", width=5)
|
||||
else:
|
||||
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)
|
||||
if tool.Model.update_thumbnail_for_element(element):
|
||||
queue.append(element)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -472,7 +375,6 @@ class MirrorElements(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj.matrix_world = newmat
|
||||
|
||||
|
||||
|
||||
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:
|
||||
|
||||
@@ -145,12 +145,21 @@ class DumbProfileRegenerator:
|
||||
objs = []
|
||||
if not profile:
|
||||
return
|
||||
|
||||
element_types = set()
|
||||
for element in self.get_elements_using_profile(profile):
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
objs.append(obj)
|
||||
if element.is_a("IfcElementType"):
|
||||
element_types.add(element)
|
||||
|
||||
DumbProfileRecalculator().recalculate(objs)
|
||||
|
||||
# update related thumbnails
|
||||
for element in self.get_element_types_using_profile(profile):
|
||||
tool.Model.update_thumbnail_for_element(element, refresh=True)
|
||||
|
||||
def regenerate_from_profile(self, usecase_path, ifc_file, settings):
|
||||
self.file = ifc_file
|
||||
objs = []
|
||||
@@ -165,9 +174,10 @@ class DumbProfileRegenerator:
|
||||
|
||||
def get_elements_using_profile(self, profile):
|
||||
results = []
|
||||
for profile_set in [
|
||||
profile_sets = [
|
||||
mp.ToMaterialProfileSet[0] for mp in self.file.get_inverse(profile) if mp.is_a("IfcMaterialProfile")
|
||||
]:
|
||||
]
|
||||
for profile_set in profile_sets:
|
||||
for inverse in self.file.get_inverse(profile_set):
|
||||
if not inverse.is_a("IfcMaterialProfileSetUsage"):
|
||||
continue
|
||||
@@ -181,6 +191,18 @@ class DumbProfileRegenerator:
|
||||
results.extend(rel.RelatedObjects)
|
||||
return results
|
||||
|
||||
def get_element_types_using_profile(self, profile):
|
||||
results = []
|
||||
profile_sets = [
|
||||
mp.ToMaterialProfileSet[0] for mp in self.file.get_inverse(profile) if mp.is_a("IfcMaterialProfile")
|
||||
]
|
||||
for profile_set in profile_sets:
|
||||
for inverse in self.file.get_inverse(profile_set):
|
||||
if not inverse.is_a("IfcRelAssociatesMaterial"):
|
||||
continue
|
||||
results.extend(inverse.RelatedObjects)
|
||||
return results
|
||||
|
||||
def regenerate_from_type(self, usecase_path, ifc_file, settings):
|
||||
obj = tool.Ifc.get_object(settings["related_object"])
|
||||
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
|
||||
@@ -202,7 +202,6 @@ class Loader(blenderbim.core.tool.Loader):
|
||||
ifc_path = Path(tool.Ifc.get_path())
|
||||
image_url = ifc_path.parent / image_url
|
||||
|
||||
# import pdb; pdb.set_trace()
|
||||
if not image_url.exists():
|
||||
print(f"WARNING. Couldn't find texture by path {image_url}, it will be skipped.")
|
||||
continue
|
||||
|
||||
@@ -29,6 +29,7 @@ from mathutils import Matrix, Vector
|
||||
from blenderbim.bim import import_ifc
|
||||
from blenderbim.bim.module.geometry.helper import Helper
|
||||
import collections
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
|
||||
|
||||
class Model(blenderbim.core.tool.Model):
|
||||
@@ -630,3 +631,101 @@ class Model(blenderbim.core.tool.Model):
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def update_thumbnail_for_element(cls, element, refresh=False):
|
||||
if bpy.app.background:
|
||||
return
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not obj:
|
||||
return # Nothing to process
|
||||
|
||||
if not refresh and element.id() in AuthoringData.type_thumbnails:
|
||||
return # Already processed
|
||||
|
||||
obj.asset_generate_preview()
|
||||
while not obj.preview:
|
||||
pass
|
||||
|
||||
# if object has .data we can use default blender .asset_generate_preview()
|
||||
if not obj.data:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
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
|
||||
tool.Profile.draw_image_for_ifc_profile(draw, profile, size)
|
||||
|
||||
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.051:
|
||||
width = 10
|
||||
elif si_total_thickness <= 0.11:
|
||||
width = 20
|
||||
elif si_total_thickness <= 0.21:
|
||||
width = 30
|
||||
elif si_total_thickness <= 0.31:
|
||||
width = 40
|
||||
else:
|
||||
width = 50
|
||||
|
||||
height = 100
|
||||
|
||||
is_horizontal = False
|
||||
if element.is_a("IfcSlabType"):
|
||||
is_horizontal = True
|
||||
|
||||
parametric = ifcopenshell.util.element.get_psets(element).get("EPset_Parametric")
|
||||
if parametric:
|
||||
layer_set_direction = parametric.get("LayerSetDirection", None)
|
||||
if layer_set_direction == "AXIS2":
|
||||
is_horizontal = False
|
||||
elif layer_set_direction == "AXIS3":
|
||||
is_horizontal = True
|
||||
|
||||
if is_horizontal:
|
||||
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=5)
|
||||
current_thickness = 0
|
||||
del thicknesses[-1]
|
||||
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)
|
||||
elif False:
|
||||
# TODO: things like parametric duct segments
|
||||
pass
|
||||
elif not element.RepresentationMaps:
|
||||
# Empties are represented by a generic thumbnail
|
||||
width = height = 100
|
||||
x_offset = (size / 2) - (width / 2)
|
||||
y_offset = (size / 2) - (height / 2)
|
||||
draw.line([x_offset, y_offset, width + x_offset, height + y_offset], fill="white", width=2)
|
||||
draw.line([x_offset, y_offset + height, width + x_offset, y_offset], fill="white", width=2)
|
||||
draw.rectangle([x_offset, y_offset, width + x_offset, height + y_offset], outline="white", width=5)
|
||||
else:
|
||||
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.preview.image_size = size, size
|
||||
obj.preview.image_pixels_float = pixels
|
||||
|
||||
AuthoringData.type_thumbnails[element.id()] = obj.preview.icon_id
|
||||
|
||||
Reference in New Issue
Block a user