diff --git a/src/blenderbim/blenderbim/bim/helper.py b/src/blenderbim/blenderbim/bim/helper.py index 38af2fc914..cf1018c3d7 100644 --- a/src/blenderbim/blenderbim/bim/helper.py +++ b/src/blenderbim/blenderbim/bim/helper.py @@ -267,35 +267,6 @@ 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/door.py b/src/blenderbim/blenderbim/bim/module/model/door.py index 699c08c92b..0cf815ec07 100644 --- a/src/blenderbim/blenderbim/bim/module/model/door.py +++ b/src/blenderbim/blenderbim/bim/module/model/door.py @@ -30,7 +30,6 @@ import blenderbim.core.geometry as core from blenderbim.bim.helper import convert_property_group_from_si from blenderbim.bim.ifc import IfcStore from blenderbim.bim.module.model.window import create_bm_window, create_bm_box, update_simple_openings -from blenderbim.bim.module.model.helper import replace_ifc_representation_for_object from mathutils import Vector, Matrix from pprint import pprint @@ -80,20 +79,20 @@ def update_door_modifier_representation(context): elevation_representation = ifcopenshell.api.run( "geometry.add_door_representation", ifc_file, **representation_data ) - replace_ifc_representation_for_object(ifc_file, profile, obj, elevation_representation) + tool.Model.replace_object_ifc_representation(profile, obj, elevation_representation) # MODEL_VIEW representation body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW") representation_data["context"] = body model_representation = ifcopenshell.api.run("geometry.add_door_representation", ifc_file, **representation_data) - replace_ifc_representation_for_object(ifc_file, body, obj, model_representation) + tool.Model.replace_object_ifc_representation(body, obj, model_representation) # PLAN_VIEW representation plan = ifcopenshell.util.representation.get_context(ifc_file, "Plan", "Body", "PLAN_VIEW") if plan: representation_data["context"] = plan plan_representation = ifcopenshell.api.run("geometry.add_door_representation", ifc_file, **representation_data) - replace_ifc_representation_for_object(ifc_file, plan, obj, plan_representation) + tool.Model.replace_object_ifc_representation(plan, obj, plan_representation) # adding switch representation at the end instead of changing order of representations # to prevent #2744 diff --git a/src/blenderbim/blenderbim/bim/module/model/helper.py b/src/blenderbim/blenderbim/bim/module/model/helper.py deleted file mode 100644 index 1666c07877..0000000000 --- a/src/blenderbim/blenderbim/bim/module/model/helper.py +++ /dev/null @@ -1,67 +0,0 @@ -# BlenderBIM Add-on - OpenBIM Blender Add-on -# Copyright (C) 2023 @Andrej730 -# -# This file is part of BlenderBIM Add-on. -# -# BlenderBIM Add-on is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# BlenderBIM Add-on is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with BlenderBIM Add-on. If not, see . - -import bpy -import bmesh -import ifcopenshell -import ifcopenshell.util.unit -import blenderbim.core.geometry as core -import blenderbim.tool as tool - - -def get_ifc_context_or_create(ifc_file, context_type, context_identifier, target_view): - ifc_context = ifcopenshell.util.representation.get_context(ifc_file, context_type, context_identifier, target_view) - if not ifc_context: - parent_context = ifcopenshell.util.representation.get_context(ifc_file, context_type) - ifc_context = ifcopenshell.api.run( - "context.add_context", - ifc_file, - context_type=context_type, - context_identifier=context_identifier, - target_view=target_view, - parent=parent_context, - ) - return ifc_context - - -def replace_ifc_representation_for_object(ifc_file, ifc_context, obj, new_representation): - ifc_element = tool.Ifc.get_entity(obj) - old_representation = ifcopenshell.util.representation.get_representation( - ifc_element, ifc_context.ContextType, ifc_context.ContextIdentifier, ifc_context.TargetView - ) - - if old_representation: - old_representation = tool.Geometry.resolve_mapped_representation(old_representation) - for inverse in ifc_file.get_inverse(old_representation): - ifcopenshell.util.element.replace_attribute(inverse, old_representation, new_representation) - ifcopenshell.api.run("geometry.remove_representation", ifc_file, representation=old_representation) - else: - ifcopenshell.api.run( - "geometry.assign_representation", ifc_file, product=ifc_element, representation=new_representation - ) - core.switch_representation( - tool.Ifc, - tool.Geometry, - obj=obj, - representation=new_representation, - should_reload=True, - is_global=True, - should_sync_changes_first=False, - ) - - diff --git a/src/blenderbim/blenderbim/bim/module/model/product.py b/src/blenderbim/blenderbim/bim/module/model/product.py index 1c70b83d3a..87424b06ae 100644 --- a/src/blenderbim/blenderbim/bim/module/model/product.py +++ b/src/blenderbim/blenderbim/bim/module/model/product.py @@ -32,7 +32,6 @@ 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 mathutils import Vector, Matrix from bpy_extras.object_utils import AddObjectHelper from . import prop @@ -374,7 +373,7 @@ 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 - draw_image_for_ifc_profile(draw, profile, size) + 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] diff --git a/src/blenderbim/blenderbim/bim/module/model/window.py b/src/blenderbim/blenderbim/bim/module/model/window.py index c925766778..c3d27c7e18 100644 --- a/src/blenderbim/blenderbim/bim/module/model/window.py +++ b/src/blenderbim/blenderbim/bim/module/model/window.py @@ -32,7 +32,6 @@ from bpy.props import FloatProperty, IntProperty, BoolProperty from bmesh.types import BMVert from blenderbim.bim.helper import convert_property_group_from_si from blenderbim.bim.ifc import IfcStore -from blenderbim.bim.module.model.helper import replace_ifc_representation_for_object from mathutils import Vector @@ -148,13 +147,13 @@ def update_window_modifier_representation(context): elevation_representation = ifcopenshell.api.run( "geometry.add_window_representation", ifc_file, **representation_data ) - replace_ifc_representation_for_object(ifc_file, profile, obj, elevation_representation) + tool.Model.replace_object_ifc_representation(profile, obj, elevation_representation) # MODEL_VIEW representation body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW") representation_data["context"] = body model_representation = ifcopenshell.api.run("geometry.add_window_representation", ifc_file, **representation_data) - replace_ifc_representation_for_object(ifc_file, body, obj, model_representation) + tool.Model.replace_object_ifc_representation(body, obj, model_representation) # PLAN_VIEW representation plan = ifcopenshell.util.representation.get_context(ifc_file, "Plan", "Body", "PLAN_VIEW") @@ -163,7 +162,7 @@ def update_window_modifier_representation(context): plan_representation = ifcopenshell.api.run( "geometry.add_window_representation", ifc_file, **representation_data ) - replace_ifc_representation_for_object(ifc_file, plan, obj, plan_representation) + tool.Model.replace_object_ifc_representation(plan, obj, plan_representation) # adding switch representation at the end instead of changing order of representations # to prevent #2744 diff --git a/src/blenderbim/blenderbim/bim/module/profile/prop.py b/src/blenderbim/blenderbim/bim/module/profile/prop.py index 346149b7ba..747831e98d 100644 --- a/src/blenderbim/blenderbim/bim/module/profile/prop.py +++ b/src/blenderbim/blenderbim/bim/module/profile/prop.py @@ -24,7 +24,6 @@ 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, @@ -79,7 +78,7 @@ def generate_thumbnail_for_active_profile(): size = 128 img = Image.new("RGBA", (size, size)) draw = ImageDraw.Draw(img) - draw_image_for_ifc_profile(draw, profile, size) + tool.Profile.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 diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py index 94221f45dc..34f7a748a5 100644 --- a/src/blenderbim/blenderbim/core/tool.py +++ b/src/blenderbim/blenderbim/core/tool.py @@ -398,7 +398,23 @@ class Misc: @interface class Model: - pass + def convert_si_to_unit(cls, value): pass + def convert_unit_to_si(cls, value): pass + def export_curve(cls, position, edge_indices, points=None): pass + def export_points(cls, position, indices): pass + def export_profile(cls, obj, position=None): pass + def generate_occurrence_name(cls, element_type, ifc_class): pass + def get_extrusion(cls, representation): pass + def import_profile(cls, profile, obj=None, position=None): pass + def import_curve(cls, obj, position, curve): pass + def import_rectangle(cls, obj, position, profile): pass + def load_openings(cls, element, openings): pass + def clear_scene_openings(cls): pass + def get_material_layer_parameters(cls, element): pass + def get_manual_booleans(cls, element): pass + def get_wall_axis(cls, obj, layers=None): pass + def regenerate_array(cls, parent, data): pass + def replace_object_ifc_representation(cls, ifc_file, ifc_context, obj, new_representation): pass @interface @@ -461,6 +477,11 @@ class Project: def set_default_modeling_dimensions(cls): pass +@interface +class Profile: + def draw_image_for_ifc_profile(cls, draw, profile, size): pass + + @interface class Pset: def get_element_pset(cls, element, pset_name): pass diff --git a/src/blenderbim/blenderbim/tool/__init__.py b/src/blenderbim/blenderbim/tool/__init__.py index f011bf6039..5ccefb71be 100644 --- a/src/blenderbim/blenderbim/tool/__init__.py +++ b/src/blenderbim/blenderbim/tool/__init__.py @@ -38,6 +38,7 @@ from blenderbim.tool.model import Model from blenderbim.tool.owner import Owner from blenderbim.tool.patch import Patch from blenderbim.tool.project import Project +from blenderbim.tool.profile import Profile from blenderbim.tool.pset import Pset from blenderbim.tool.qto import Qto from blenderbim.tool.resource import Resource @@ -51,4 +52,4 @@ from blenderbim.tool.system import System from blenderbim.tool.type import Type from blenderbim.tool.unit import Unit from blenderbim.tool.search import Search -from blenderbim.tool.cost import Cost \ No newline at end of file +from blenderbim.tool.cost import Cost diff --git a/src/blenderbim/blenderbim/tool/model.py b/src/blenderbim/blenderbim/tool/model.py index 1ccf6126da..f450b1b172 100644 --- a/src/blenderbim/blenderbim/tool/model.py +++ b/src/blenderbim/blenderbim/tool/model.py @@ -24,6 +24,7 @@ import ifcopenshell.util.placement import ifcopenshell.util.representation import blenderbim.core.tool import blenderbim.tool as tool +import blenderbim.core.geometry as geometry from mathutils import Matrix, Vector from blenderbim.bim import import_ifc from blenderbim.bim.module.geometry.helper import Helper @@ -408,3 +409,30 @@ class Model(blenderbim.core.tool.Model): # TODO: Not sufficient, refactor OverrideDeleteTrait bpy.context.view_layer.update() + + @classmethod + def replace_object_ifc_representation(cls, ifc_context, obj, new_representation): + ifc_file = tool.Ifc.get() + ifc_element = tool.Ifc.get_entity(obj) + old_representation = ifcopenshell.util.representation.get_representation( + ifc_element, ifc_context.ContextType, ifc_context.ContextIdentifier, ifc_context.TargetView + ) + + if old_representation: + old_representation = tool.Geometry.resolve_mapped_representation(old_representation) + for inverse in ifc_file.get_inverse(old_representation): + ifcopenshell.util.element.replace_attribute(inverse, old_representation, new_representation) + ifcopenshell.api.run("geometry.remove_representation", ifc_file, representation=old_representation) + else: + ifcopenshell.api.run( + "geometry.assign_representation", ifc_file, product=ifc_element, representation=new_representation + ) + geometry.switch_representation( + tool.Ifc, + tool.Geometry, + obj=obj, + representation=new_representation, + should_reload=True, + is_global=True, + should_sync_changes_first=False, + ) diff --git a/src/blenderbim/blenderbim/tool/profile.py b/src/blenderbim/blenderbim/tool/profile.py new file mode 100644 index 0000000000..ab1402daaf --- /dev/null +++ b/src/blenderbim/blenderbim/tool/profile.py @@ -0,0 +1,54 @@ +# BlenderBIM Add-on - OpenBIM Blender Add-on +# Copyright (C) 2022 @Andrej730 +# +# This file is part of BlenderBIM Add-on. +# +# BlenderBIM Add-on is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# BlenderBIM Add-on is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with BlenderBIM Add-on. If not, see . + +import ifcopenshell +import ifcopenshell.util.unit +import ifcopenshell.util.placement +import ifcopenshell.util.representation +import blenderbim.core.tool + + +class Profile(blenderbim.core.tool.Profile): + @classmethod + def draw_image_for_ifc_profile(cls, 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)