Files
IfcOpenShell/src/blenderbim/blenderbim/tool/model.py
T
Gorgious f07cf1d1ef Squashed commit of the following:
commit 7fb4cdaa16
Author: Gorgious <nathan.hild@gmail.com>
Date:   Mon Oct 31 09:35:06 2022 +0100

    Fix #2546 : Editing a material now automatically ends the edition of its material set item if necessary

commit 427411e758
Author: Dion Moult <dion@thinkmoult.com>
Date:   Mon Oct 31 19:04:57 2022 +1100

    You can now edit arbitrary profiles in the profile manager

commit cc056d5d53
Author: Gorgious <nathan.hild@gmail.com>
Date:   Mon Oct 31 08:52:11 2022 +0100

    Fix #2553 : Right click documentation is now only available for Blender 3.3+
    Also fix an error in the console when right clicking a button

commit 31600033bf
Author: ArturTomczak <artomczak@gmail.com>
Date:   Sun Oct 30 23:16:08 2022 +0100

    add BCF reporter to IDS (#2552)

    * grammar

    * reason not always a list

    * extend readme with instruction

    * remove microseconds from date

    * add BCF reporter

commit 0338825175
Author: Dion Moult <dion@thinkmoult.com>
Date:   Sun Oct 30 23:26:59 2022 +1100

    You can now add new profile definitions in the profile manager

commit d9f8aa4e7b
Author: Andrej730 <azhilenkov@gmail.com>
Date:   Sun Oct 30 11:29:42 2022 +0600

    Added ifc types schema for Ifc2x3 and Ifc4

    Types schema located in separate json file, it's structure:
    { ifc_type_name: {"description": type_description, "spec_url": type_spec_url} }

    I've also added new API function to get type data - `get_type_doc(version, ifc_type)`

commit 6803788676
Author: Dion Moult <dion@thinkmoult.com>
Date:   Sun Oct 30 11:02:18 2022 +1100

    Load thumbnails in a paginated manner to allow to scale to larger models

commit d421cc0bbb
Author: Massimo Fabbro <79401028+maxfb87@users.noreply.github.com>
Date:   Sun Oct 30 00:07:56 2022 +0200

    No mapped quantities in qto calculator  has None value (#2549)

commit bf57c8b1bf
Author: Dion Moult <dion@thinkmoult.com>
Date:   Sat Oct 29 23:45:04 2022 +1100

    Refactor profile module to use data class

commit 9a25db6584
Author: Andrej730 <azhilenkov@gmail.com>
Date:   Sat Oct 29 18:31:40 2022 +0600

    get_attribute_doc get_entity_doc now use recursive method by default

commit 17ccd0adf7
Author: Andrej730 <azhilenkov@gmail.com>
Date:   Sat Oct 29 18:27:51 2022 +0600

    Simplified getting entity parent information for schema api

    Removed unnecssary parent information from json schema - now API is getting it from ifcopenshell schema.

commit 937f39c898
Author: Andrej730 <azhilenkov@gmail.com>
Date:   Sat Oct 29 17:35:43 2022 +0600

    Added information about parent entity to schema

    Added information about parent entity to schema (it's located in "parent_entity" entity parameter inside schema), both for Ifc2x3 and Ifc4.

    Modified get_entity_doc and get_attribute_doc - now you can use additional optional parameter `recursive=True` with those functions - then parent entities attributes will be included to the list of entity's attributes. For example IfcWindow will also have attributes from IfcBuildingElement, IfcElement, IfcProduct etc...

commit 0b5ccd88a5
Author: Andrej730 <azhilenkov@gmail.com>
Date:   Sat Oct 29 16:31:54 2022 +0600

    Added descriptions for psets and qsets to ifc2x3, ifc4 schema

    Example of getting pset description: `get_property_set_doc("IFC4", "Pset_ZoneCommon")['description']`

commit ebf3cd8909
Author: Andrej730 <azhilenkov@gmail.com>
Date:   Sat Oct 29 10:32:26 2022 +0600

    Added IFC libraries for AU and EU profiles

    Also added script that was used to generate those libraries (the script is using data from https://github.com/boltsparts/BOLTS).

    Types of steel profiles included:
    I profiles, Z profiles, C profiles (known as U profiles in IFC), L profiles (both equal and unequal), hollow profiles (circle, square and rectangular).

commit 08afec3461
Author: Dion Moult <dion@thinkmoult.com>
Date:   Fri Oct 28 22:32:15 2022 +1100

    Support material layer set usage negative direction sense for parametric walls

commit 4408a23873
Author: Dion Moult <dion@thinkmoult.com>
Date:   Fri Oct 28 22:30:43 2022 +1100

    Merge duplicate types IfcPatch recipe can now merge based on any attribute
2022-10-31 09:51:05 +01:00

205 lines
8.5 KiB
Python

# BlenderBIM Add-on - OpenBIM Blender Add-on
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
#
# 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 <http://www.gnu.org/licenses/>.
import bpy
import ifcopenshell
import ifcopenshell.util.placement
import ifcopenshell.util.representation
import blenderbim.core.tool
import blenderbim.tool as tool
from mathutils import Matrix, Vector
from blenderbim.bim import import_ifc
class Model(blenderbim.core.tool.Model):
@classmethod
def generate_occurrence_name(cls, element_type, ifc_class):
props = bpy.context.scene.BIMModelProperties
if props.occurrence_name_style == "CLASS":
return ifc_class[3:]
elif props.occurrence_name_style == "TYPE":
return element_type.Name or "Unnamed"
elif props.occurrence_name_style == "CUSTOM":
try:
# Power users gonna power
return eval(props.occurrence_name_function) or "Instance"
except:
return "Instance"
@classmethod
def get_extrusion(cls, representation):
item = representation.Items[0]
while True:
if item.is_a("IfcExtrudedAreaSolid"):
return item
elif item.is_a("IfcBooleanResult"):
item = item.FirstOperand
else:
break
@classmethod
def load_openings(cls, element, openings):
if not openings:
return []
obj = tool.Ifc.get_object(element)
ifc_import_settings = import_ifc.IfcImportSettings.factory()
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.file = tool.Ifc.get()
ifc_importer.calculate_unit_scale()
ifc_importer.process_context_filter()
ifc_importer.material_creator.load_existing_materials()
openings = set(openings)
openings -= ifc_importer.create_products(openings)
for opening in openings or []:
if tool.Ifc.get_object(opening):
continue
opening_obj = ifc_importer.create_product(opening)
if obj:
opening_obj.parent = obj
opening_obj.matrix_parent_inverse = obj.matrix_world.inverted()
for obj in ifc_importer.added_data.values():
bpy.context.scene.collection.objects.link(obj)
return ifc_importer.added_data.values()
@classmethod
def clear_scene_openings(cls):
props = bpy.context.scene.BIMModelProperties
has_deleted_opening = True
while has_deleted_opening:
has_deleted_opening = False
for i, opening in enumerate(props.openings):
if not opening.obj:
props.openings.remove(i)
has_deleted_opening = True
@classmethod
def get_material_layer_parameters(cls, element):
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
offset = 0.0
thickness = 0.0
direction_sense = "POSITIVE"
material = ifcopenshell.util.element.get_material(element)
if material:
if material.is_a("IfcMaterialLayerSetUsage"):
offset = material.OffsetFromReferenceLine * unit_scale
direction_sense = material.DirectionSense
material = material.ForLayerSet
if material.is_a("IfcMaterialLayerSet"):
thickness = sum([l.LayerThickness for l in material.MaterialLayers]) * unit_scale
if direction_sense == "NEGATIVE":
thickness *= -1
offset *= -1
return {"thickness": thickness, "offset": offset, "direction_sense": direction_sense}
@classmethod
def get_manual_booleans(cls, element):
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
if not body:
return []
booleans = []
items = list(body.Items)
while items:
item = items.pop()
if item.is_a() == "IfcBooleanResult":
booleans.append(item)
items.append(item.FirstOperand)
elif item.is_a("IfcBooleanClippingResult"):
items.append(item.FirstOperand)
return booleans
@classmethod
def get_wall_axis(cls, obj, layers=None):
x_values = [v[0] for v in obj.bound_box]
min_x = min(x_values)
max_x = max(x_values)
axes = {}
if layers:
axes = {
"base": [
(obj.matrix_world @ Vector((min_x, layers["offset"], 0.0))).to_2d(),
(obj.matrix_world @ Vector((max_x, layers["offset"], 0.0))).to_2d(),
],
"side": [
(obj.matrix_world @ Vector((min_x, layers["offset"] + layers["thickness"], 0.0))).to_2d(),
(obj.matrix_world @ Vector((max_x, layers["offset"] + layers["thickness"], 0.0))).to_2d(),
],
}
axes["reference"] = [
(obj.matrix_world @ Vector((min_x, 0.0, 0.0))).to_2d(),
(obj.matrix_world @ Vector((max_x, 0.0, 0.0))).to_2d(),
]
return axes
@classmethod
def regenerate_array(cls, parent, data):
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
obj_stack = [parent]
for array in data:
child_i = 0
existing_children = set(array["children"])
total_existing_children = len(array["children"])
children_elements = []
children_objs = []
for i in range(0, array["count"]):
if i == 0:
continue
offset = Vector((i * array["x"] * unit_scale, i * array["y"] * unit_scale, i * array["z"] * unit_scale))
for obj in obj_stack:
if child_i >= total_existing_children:
child_obj = tool.Spatial.duplicate_object_and_data(obj)
child_element = tool.Spatial.run_root_copy_class(obj=child_obj)
else:
global_id = array["children"][child_i]
try:
child_element = tool.Ifc.get().by_guid(global_id)
child_obj = tool.Ifc.get_object(child_element)
assert child_obj
except:
child_obj = tool.Spatial.duplicate_object_and_data(obj)
child_element = tool.Spatial.run_root_copy_class(obj=child_obj)
child_psets = ifcopenshell.util.element.get_psets(child_element)
child_pset = child_psets.get("BBIM_Array")
if child_pset:
ifcopenshell.api.run(
"pset.edit_pset",
tool.Ifc.get(),
product=child_element,
pset=tool.Ifc.get().by_id(child_pset["id"]),
properties={"Data": None},
)
new_matrix = obj.matrix_world.copy()
new_matrix.col[3] = (obj.matrix_world.col[3].to_3d() + offset).to_4d()
child_obj.matrix_world = new_matrix
children_objs.append(child_obj)
children_elements.append(child_element)
child_i += 1
obj_stack.extend(children_objs)
array["children"] = [e.GlobalId for e in children_elements]
removed_children = set(existing_children) - set(array["children"])
for removed_child in removed_children:
element = tool.Ifc.get().by_guid(removed_child)
obj = tool.Ifc.get_object(element)
if obj:
bpy.data.objects.remove(obj)
# TODO: Not sufficient, refactor OverrideDeleteTrait
bpy.context.view_layer.update()