This commit is contained in:
Andrej730
2024-07-31 12:50:57 +05:00
parent 598cf4ba79
commit 159355d63a
6 changed files with 29 additions and 17 deletions
@@ -21,6 +21,7 @@ import json
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.guid import ifcopenshell.guid
import ifcopenshell.util.element
import blenderbim.bim.helper import blenderbim.bim.helper
import blenderbim.bim.handler import blenderbim.bim.handler
import blenderbim.tool as tool import blenderbim.tool as tool
@@ -36,7 +37,7 @@ class EnableEditingAttributes(bpy.types.Operator):
def execute(self, context): def execute(self, context):
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
obj = bpy.data.objects.get(self.obj) obj = bpy.data.objects[self.obj]
props = obj.BIMAttributeProperties props = obj.BIMAttributeProperties
props.attributes.clear() props.attributes.clear()
@@ -19,6 +19,7 @@
import bpy import bpy
import ifcopenshell import ifcopenshell
import ifcopenshell.util.element
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.core.covering as core import blenderbim.core.covering as core
@@ -18,25 +18,28 @@
import bpy import bpy
import bmesh import bmesh
from typing import Callable
def calculate_height(obj): def calculate_height(obj: bpy.types.Object) -> float:
return obj.dimensions[2] return obj.dimensions[2]
def calculate_edges_lengths(objs, context): def calculate_edges_lengths(objs: list[bpy.types.Object], context: bpy.types.Context):
return calculate_mesh_quantity(objs, context, lambda bm: sum((e.calc_length() for e in bm.edges if e.select))) return calculate_mesh_quantity(objs, context, lambda bm: sum((e.calc_length() for e in bm.edges if e.select)))
def calculate_faces_areas(objs, context): def calculate_faces_areas(objs: list[bpy.types.Object], context: bpy.types.Context) -> float:
return calculate_mesh_quantity(objs, context, lambda bm: sum((f.calc_area() for f in bm.faces if f.select))) return calculate_mesh_quantity(objs, context, lambda bm: sum((f.calc_area() for f in bm.faces if f.select)))
def calculate_volumes(objs, context): def calculate_volumes(objs: list[bpy.types.Object], context: bpy.types.Context) -> float:
return calculate_mesh_quantity(objs, context, lambda bm: bm.calc_volume()) return calculate_mesh_quantity(objs, context, lambda bm: bm.calc_volume())
def calculate_mesh_quantity(objs: bpy.types.Object, context, operation): def calculate_mesh_quantity(
objs: list[bpy.types.Object], context: bpy.types.Context, operation: Callable[[bmesh.types.BMesh], float]
) -> float:
"""Get the sum of the target quantity on all passed mesh objects """Get the sum of the target quantity on all passed mesh objects
:param objs: iterable of mesh object :param objs: iterable of mesh object
@@ -58,7 +61,7 @@ def calculate_mesh_quantity(objs: bpy.types.Object, context, operation):
return result return result
def calculate_formwork_area(objs, context): def calculate_formwork_area(objs: list[bpy.types.Object], context: bpy.types.Context) -> float:
""" """
Formwork is defined as the surface area required to cover all exposed Formwork is defined as the surface area required to cover all exposed
surfaces of one or more objects, excluding top surfaces (i.e. that have a surfaces of one or more objects, excluding top surfaces (i.e. that have a
@@ -85,6 +88,7 @@ def calculate_formwork_area(objs, context):
copied_obj.name = "Formwork" copied_obj.name = "Formwork"
copied_obj.BIMObjectProperties.ifc_definition_id = 0 copied_obj.BIMObjectProperties.ifc_definition_id = 0
modifier = copied_obj.modifiers.new("Formwork", "REMESH") modifier = copied_obj.modifiers.new("Formwork", "REMESH")
assert isinstance(modifier, bpy.types.RemeshModifier)
modifier.mode = "SHARP" modifier.mode = "SHARP"
# This hardcoded value may be optimised through a better understanding of the octree division. # This hardcoded value may be optimised through a better understanding of the octree division.
# These values are based off some trial and error heuristics I've learned through experience. # These values are based off some trial and error heuristics I've learned through experience.
@@ -108,7 +112,7 @@ def calculate_formwork_area(objs, context):
return result return result
def calculate_side_formwork_area(objs, context): def calculate_side_formwork_area(objs: list[bpy.types.Object], context: bpy.types.Context) -> float:
""" """
Side formwork is defined as the surface area required to cover all exposed Side formwork is defined as the surface area required to cover all exposed
surfaces of one or more objects, excluding top and bottom surfaces (i.e. surfaces of one or more objects, excluding top and bottom surfaces (i.e.
@@ -32,6 +32,7 @@ from bpy.props import (
) )
import blenderbim.tool as tool import blenderbim.tool as tool
import ifcopenshell import ifcopenshell
import ifcopenshell.util.element
def get_subelement_class(self, context): def get_subelement_class(self, context):
+4 -2
View File
@@ -21,10 +21,12 @@ import json
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.api.pset import ifcopenshell.api.pset
import ifcopenshell.util.element
import ifcopenshell.util.unit import ifcopenshell.util.unit
import ifcopenshell.util.selector import ifcopenshell.util.selector
import multiprocessing import multiprocessing
from collections import namedtuple from collections import namedtuple
from typing import Any
Function = namedtuple("Function", ["measure", "name", "description"]) Function = namedtuple("Function", ["measure", "name", "description"])
@@ -47,7 +49,7 @@ def quantify(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_inst
return results return results
def edit_qtos(ifc_file, results) -> None: def edit_qtos(ifc_file: ifcopenshell.file, results: dict[ifcopenshell.entity_instance, Any]) -> None:
for element, qtos in results.items(): for element, qtos in results.items():
for name, quantities in qtos.items(): for name, quantities in qtos.items():
qto = ifcopenshell.util.element.get_pset(element, name, should_inherit=False) qto = ifcopenshell.util.element.get_pset(element, name, should_inherit=False)
@@ -59,7 +61,7 @@ def edit_qtos(ifc_file, results) -> None:
class SI2ProjectUnitConverter: class SI2ProjectUnitConverter:
def __init__(self, ifc_file): def __init__(self, ifc_file: ifcopenshell.file):
self.project_units = { self.project_units = {
"IfcAreaMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "AREAUNIT"), "IfcAreaMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "AREAUNIT"),
"IfcLengthMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "LENGTHUNIT"), "IfcLengthMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "LENGTHUNIT"),
@@ -17,12 +17,15 @@
# along with IfcPatch. If not, see <http://www.gnu.org/licenses/>. # along with IfcPatch. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.pset import ifcopenshell.util.pset
import ifcopenshell.util.element import ifcopenshell.util.element
from logging import Logger
from typing import Union
class Patcher: class Patcher:
def __init__(self, src, file, logger, property_name=None, quantity_name=None): def __init__(self, src: str, file: ifcopenshell.file, logger: Logger, property_name: str, quantity_name: str):
"""Converts a property to a standardised quantity """Converts a property to a standardised quantity
IFC can store arbitrary key value metadata associated with a elements IFC can store arbitrary key value metadata associated with a elements
@@ -40,14 +43,12 @@ class Patcher:
:param property_name: The name of the property to convert into a :param property_name: The name of the property to convert into a
quantity. The name of the property set is not considered. quantity. The name of the property set is not considered.
:type property_name: str
:param quantity_name: The name of the quantity that this property should :param quantity_name: The name of the quantity that this property should
be stored in. This should be a standard name that is one of the be stored in. This should be a standard name that is one of the
quantity names of a buildingSMART quantity template. For example, it quantity names of a buildingSMART quantity template. For example, it
may be "NetSideArea" for walls, which exists in may be "NetSideArea" for walls, which exists in
Qto_WallBaseQuantities. The quantity set name will be based on the Qto_WallBaseQuantities. The quantity set name will be based on the
standard buildingSMART quantity template. standard buildingSMART quantity template.
:type quantity_name: str
Example: Example:
@@ -63,7 +64,7 @@ class Patcher:
self.source_property_name = property_name self.source_property_name = property_name
self.destination_quantity_name = quantity_name self.destination_quantity_name = quantity_name
def patch(self): def patch(self) -> None:
self.qto_template_cache = {} self.qto_template_cache = {}
self.psetqto = ifcopenshell.util.pset.get_template("IFC4") self.psetqto = ifcopenshell.util.pset.get_template("IFC4")
@@ -76,7 +77,9 @@ class Patcher:
[r.RelatingPropertyDefinition for r in product.IsDefinedBy if r.is_a("IfcRelDefinesByProperties")], [r.RelatingPropertyDefinition for r in product.IsDefinedBy if r.is_a("IfcRelDefinesByProperties")],
) )
def process_product(self, product, definitions): def process_product(
self, product: ifcopenshell.entity_instance, definitions: list[ifcopenshell.entity_instance]
) -> None:
value = None value = None
has_quantity = False has_quantity = False
qtos = {} qtos = {}
@@ -98,12 +101,12 @@ class Patcher:
"pset.edit_qto", self.file, qto=qto, Properties={self.destination_quantity_name: value} "pset.edit_qto", self.file, qto=qto, Properties={self.destination_quantity_name: value}
) )
def get_qto_name(self, ifc_class): def get_qto_name(self, ifc_class: str) -> Union[str, None]:
for template in self.get_qto_templates(ifc_class): for template in self.get_qto_templates(ifc_class):
if self.destination_quantity_name in [t.Name for t in template.HasPropertyTemplates]: if self.destination_quantity_name in [t.Name for t in template.HasPropertyTemplates]:
return template.Name return template.Name
def get_qto_templates(self, ifc_class): def get_qto_templates(self, ifc_class: str) -> list[ifcopenshell.entity_instance]:
if ifc_class not in self.qto_template_cache: if ifc_class not in self.qto_template_cache:
self.qto_template_cache[ifc_class] = self.psetqto.get_applicable(ifc_class, qto_only=True) self.qto_template_cache[ifc_class] = self.psetqto.get_applicable(ifc_class, qto_only=True)
return self.qto_template_cache[ifc_class] return self.qto_template_cache[ifc_class]