mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 18:57:17 +00:00
typing, format
This commit is contained in:
@@ -23,10 +23,11 @@ import ifcopenshell
|
|||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from math import pi, pow
|
from math import pi, pow
|
||||||
from mathutils import Vector, Matrix, geometry
|
from mathutils import Vector, Matrix, geometry
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
class Helper:
|
class Helper:
|
||||||
def __init__(self, file):
|
def __init__(self, file: ifcopenshell.file):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||||
|
|
||||||
@@ -34,7 +35,7 @@ class Helper:
|
|||||||
# edge that shares a single vertex only with that face to find the extrusion
|
# edge that shares a single vertex only with that face to find the extrusion
|
||||||
# edge. A face with the normal facing down is prioritised. A limited
|
# edge. A face with the normal facing down is prioritised. A limited
|
||||||
# dissolve ensure that faces are quads and not tris.
|
# dissolve ensure that faces are quads and not tris.
|
||||||
def auto_detect_rectangle_profile_extruded_area_solid(self, mesh):
|
def auto_detect_rectangle_profile_extruded_area_solid(self, mesh: bpy.types.Mesh) -> dict:
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bm.from_mesh(mesh)
|
bm.from_mesh(mesh)
|
||||||
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
|
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
|
||||||
@@ -115,7 +116,9 @@ class Helper:
|
|||||||
|
|
||||||
return {"profile": profile, "extrusion": extrusion}
|
return {"profile": profile, "extrusion": extrusion}
|
||||||
|
|
||||||
def auto_detect_arbitrary_profile_with_voids(self, obj, mesh):
|
def auto_detect_arbitrary_profile_with_voids(
|
||||||
|
self, obj: bpy.types.Object, mesh: bpy.types.Mesh
|
||||||
|
) -> Union[tuple, dict]:
|
||||||
groups = {"IFCARCINDEX": [], "IFCCIRCLE": []}
|
groups = {"IFCARCINDEX": [], "IFCCIRCLE": []}
|
||||||
for i, group in enumerate(obj.vertex_groups):
|
for i, group in enumerate(obj.vertex_groups):
|
||||||
if "IFCARCINDEX" in group.name:
|
if "IFCARCINDEX" in group.name:
|
||||||
@@ -148,18 +151,18 @@ class Helper:
|
|||||||
if total_groups > 1: # A vert can only belong to one group
|
if total_groups > 1: # A vert can only belong to one group
|
||||||
return (False, "AMBIGUOUS_SPECIAL_VERTEX")
|
return (False, "AMBIGUOUS_SPECIAL_VERTEX")
|
||||||
elif is_circle:
|
elif is_circle:
|
||||||
pass # Circles are allowed to be unclosed
|
pass # Circles are allowed to be unclosed
|
||||||
elif total_groups == 0 and len(vert.link_edges) != 2: # Unclosed loop or forked loop
|
elif total_groups == 0 and len(vert.link_edges) != 2: # Unclosed loop or forked loop
|
||||||
return (False, "UNCLOSED_LOOP")
|
return (False, "UNCLOSED_LOOP")
|
||||||
|
|
||||||
for group_type, group_counts in group_verts.items():
|
for group_type, group_counts in group_verts.items():
|
||||||
if group_type == "IFCARCINDEX":
|
if group_type == "IFCARCINDEX":
|
||||||
for group_count in group_counts.values():
|
for group_count in group_counts.values():
|
||||||
if group_count != 3: # Each arc needs 3 verts
|
if group_count != 3: # Each arc needs 3 verts
|
||||||
return (False, "3POINT_ARC")
|
return (False, "3POINT_ARC")
|
||||||
elif group_type == "IFCCIRCLE":
|
elif group_type == "IFCCIRCLE":
|
||||||
for group_count in group_counts.values():
|
for group_count in group_counts.values():
|
||||||
if group_count != 2: # Each circle needs 2 verts
|
if group_count != 2: # Each circle needs 2 verts
|
||||||
return (False, "CIRCLE")
|
return (False, "CIRCLE")
|
||||||
|
|
||||||
loop_edges = set(bm.edges)
|
loop_edges = set(bm.edges)
|
||||||
@@ -269,7 +272,7 @@ class Helper:
|
|||||||
inner_loops.remove(outer_loop)
|
inner_loops.remove(outer_loop)
|
||||||
|
|
||||||
# Copy vectors to prevent random data mangling after bmesh is freed.
|
# Copy vectors to prevent random data mangling after bmesh is freed.
|
||||||
points = [Vector(list(v.co)) for v in bm.verts]
|
points = [v.co.copy() for v in bm.verts]
|
||||||
|
|
||||||
bm.to_mesh(mesh)
|
bm.to_mesh(mesh)
|
||||||
mesh.update()
|
mesh.update()
|
||||||
|
|||||||
@@ -20,9 +20,12 @@ import bpy
|
|||||||
import json
|
import json
|
||||||
import bmesh
|
import bmesh
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.type
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.unit
|
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
import ifcopenshell.util.placement
|
||||||
|
import ifcopenshell.util.representation
|
||||||
|
import ifcopenshell.util.unit
|
||||||
|
import ifcopenshell.util.type
|
||||||
import blenderbim.bim.handler
|
import blenderbim.bim.handler
|
||||||
import blenderbim.core.type
|
import blenderbim.core.type
|
||||||
import blenderbim.core.geometry
|
import blenderbim.core.geometry
|
||||||
@@ -698,9 +701,7 @@ class EditExtrusionProfile(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
new_footprint = ifcopenshell.api.run(
|
new_footprint = ifcopenshell.api.run(
|
||||||
"geometry.add_footprint_representation", tool.Ifc.get(), context=footprint_context, curves=curves
|
"geometry.add_footprint_representation", tool.Ifc.get(), context=footprint_context, curves=curves
|
||||||
)
|
)
|
||||||
old_footprint = ifcopenshell.util.representation.get_representation(
|
old_footprint = ifcopenshell.util.representation.get_representation(element, "Plan", "FootPrint", "SKETCH_VIEW")
|
||||||
element, "Plan", "FootPrint", "SKETCH_VIEW"
|
|
||||||
)
|
|
||||||
if old_footprint:
|
if old_footprint:
|
||||||
for inverse in tool.Ifc.get().get_inverse(old_footprint):
|
for inverse in tool.Ifc.get().get_inverse(old_footprint):
|
||||||
ifcopenshell.util.element.replace_attribute(inverse, old_footprint, new_footprint)
|
ifcopenshell.util.element.replace_attribute(inverse, old_footprint, new_footprint)
|
||||||
|
|||||||
@@ -519,7 +519,7 @@ class Misc:
|
|||||||
class Model:
|
class Model:
|
||||||
def convert_si_to_unit(cls, value): pass
|
def convert_si_to_unit(cls, value): pass
|
||||||
def convert_unit_to_si(cls, value): pass
|
def convert_unit_to_si(cls, value): pass
|
||||||
def export_curve(cls, position, edge_indices, points=None): pass
|
def export_curve(cls, position, edge_indices): pass
|
||||||
def export_points(cls, position, indices): pass
|
def export_points(cls, position, indices): pass
|
||||||
def export_profile(cls, obj, position=None): pass
|
def export_profile(cls, obj, position=None): pass
|
||||||
def generate_occurrence_name(cls, element_type, ifc_class): pass
|
def generate_occurrence_name(cls, element_type, ifc_class): pass
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import bpy
|
|||||||
import json
|
import json
|
||||||
import bmesh
|
import bmesh
|
||||||
import collections
|
import collections
|
||||||
|
import collections.abc
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
@@ -36,23 +37,26 @@ from blenderbim.bim import import_ifc
|
|||||||
from blenderbim.bim.module.geometry.helper import Helper
|
from blenderbim.bim.module.geometry.helper import Helper
|
||||||
from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData
|
from blenderbim.bim.module.model.data import AuthoringData, RailingData, RoofData, WindowData, DoorData
|
||||||
from ifcopenshell.util.shape_builder import V, ShapeBuilder
|
from ifcopenshell.util.shape_builder import V, ShapeBuilder
|
||||||
|
from typing import Optional, Union, TypeVar, Any
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
|
||||||
|
|
||||||
class Model(blenderbim.core.tool.Model):
|
class Model(blenderbim.core.tool.Model):
|
||||||
@classmethod
|
@classmethod
|
||||||
def convert_si_to_unit(cls, value):
|
def convert_si_to_unit(cls, value: T) -> T:
|
||||||
if isinstance(value, (tuple, list)):
|
if isinstance(value, (tuple, list)):
|
||||||
return [v / cls.unit_scale for v in value]
|
return [v / cls.unit_scale for v in value]
|
||||||
return value / cls.unit_scale
|
return value / cls.unit_scale
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def convert_unit_to_si(cls, value):
|
def convert_unit_to_si(cls, value: T) -> T:
|
||||||
if isinstance(value, (tuple, list)):
|
if isinstance(value, (tuple, list)):
|
||||||
return [v * cls.unit_scale for v in value]
|
return [v * cls.unit_scale for v in value]
|
||||||
return value * cls.unit_scale
|
return value * cls.unit_scale
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def convert_data_to_project_units(cls, data, non_si_props=[]):
|
def convert_data_to_project_units(cls, data: dict[str, Any], non_si_props: list[str] = []) -> dict[str, Any]:
|
||||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
for prop_name in data:
|
for prop_name in data:
|
||||||
if prop_name in non_si_props:
|
if prop_name in non_si_props:
|
||||||
@@ -65,7 +69,7 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def convert_data_to_si_units(cls, data, non_si_props=[]):
|
def convert_data_to_si_units(cls, data: dict[str, Any], non_si_props: list[str] = []) -> dict[str, Any]:
|
||||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
for prop_name in data:
|
for prop_name in data:
|
||||||
if prop_name in non_si_props:
|
if prop_name in non_si_props:
|
||||||
@@ -78,34 +82,35 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_curve(cls, position, edge_indices, points=None):
|
def export_curve(cls, position: Matrix, edge_indices: list[tuple[int, int]]) -> ifcopenshell.entity_instance:
|
||||||
position_i = position.inverted()
|
position_i = position.inverted()
|
||||||
|
ifc_file = tool.Ifc.get()
|
||||||
if len(edge_indices) == 2:
|
if len(edge_indices) == 2:
|
||||||
diameter = edge_indices[0]
|
diameter = edge_indices[0]
|
||||||
p1 = cls.bm.verts[diameter[0]].co
|
p1 = cls.bm.verts[diameter[0]].co
|
||||||
p2 = cls.bm.verts[diameter[1]].co
|
p2 = cls.bm.verts[diameter[1]].co
|
||||||
center = cls.convert_si_to_unit(list(position_i @ p1.lerp(p2, 0.5)))
|
center = cls.convert_si_to_unit(list(position_i @ p1.lerp(p2, 0.5)))
|
||||||
radius = cls.convert_si_to_unit((p1 - p2).length / 2)
|
radius = cls.convert_si_to_unit((p1 - p2).length / 2)
|
||||||
return tool.Ifc.get().createIfcCircle(
|
return ifc_file.createIfcCircle(
|
||||||
tool.Ifc.get().createIfcAxis2Placement2D(tool.Ifc.get().createIfcCartesianPoint(center[0:2])), radius
|
ifc_file.createIfcAxis2Placement2D(ifc_file.createIfcCartesianPoint(center[0:2])), radius
|
||||||
)
|
)
|
||||||
if tool.Ifc.get().schema == "IFC2X3":
|
if ifc_file.schema == "IFC2X3":
|
||||||
points = []
|
points = []
|
||||||
for edge in edge_indices:
|
for edge in edge_indices:
|
||||||
local_point = (position_i @ Vector(cls.bm.verts[edge[0]].co)).to_2d()
|
local_point = (position_i @ Vector(cls.bm.verts[edge[0]].co)).to_2d()
|
||||||
points.append(tool.Ifc.get().createIfcCartesianPoint(cls.convert_si_to_unit(local_point)))
|
points.append(ifc_file.createIfcCartesianPoint(cls.convert_si_to_unit(local_point)))
|
||||||
points.append(points[0])
|
points.append(points[0])
|
||||||
return tool.Ifc.get().createIfcPolyline(points)
|
return ifc_file.createIfcPolyline(points)
|
||||||
segments = []
|
segments = []
|
||||||
for segment in edge_indices:
|
for segment in edge_indices:
|
||||||
if len(segment) == 2:
|
if len(segment) == 2:
|
||||||
segments.append(tool.Ifc.get().createIfcLineIndex([i + 1 for i in segment]))
|
segments.append(ifc_file.createIfcLineIndex([i + 1 for i in segment]))
|
||||||
elif len(segment) == 3:
|
elif len(segment) == 3:
|
||||||
segments.append(tool.Ifc.get().createIfcArcIndex([i + 1 for i in segment]))
|
segments.append(ifc_file.createIfcArcIndex([i + 1 for i in segment]))
|
||||||
return tool.Ifc.get().createIfcIndexedPolyCurve(cls.points, segments, False)
|
return ifc_file.createIfcIndexedPolyCurve(cls.points, segments, False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_points(cls, position, indices):
|
def export_points(cls, position: Matrix, indices: list[Vector]) -> ifcopenshell.entity_instance:
|
||||||
position_i = position.inverted()
|
position_i = position.inverted()
|
||||||
points = []
|
points = []
|
||||||
for point in indices:
|
for point in indices:
|
||||||
@@ -114,7 +119,10 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
return tool.Ifc.get().createIfcCartesianPointList2D(points)
|
return tool.Ifc.get().createIfcCartesianPointList2D(points)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_profile(cls, obj, position=None):
|
def export_profile(
|
||||||
|
cls, obj: bpy.types.Object, position: Optional[Matrix] = None
|
||||||
|
) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
|
"""Returns `None` in case if profile was invalid."""
|
||||||
if position is None:
|
if position is None:
|
||||||
position = Matrix()
|
position = Matrix()
|
||||||
|
|
||||||
@@ -150,7 +158,7 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
return profile
|
return profile
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def export_surface(cls, obj):
|
def export_surface(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
p1, p2, p3 = [v.co.copy() for v in obj.data.vertices[0:3]]
|
p1, p2, p3 = [v.co.copy() for v in obj.data.vertices[0:3]]
|
||||||
|
|
||||||
edge1 = p2 - p1
|
edge1 = p2 - p1
|
||||||
@@ -202,7 +210,7 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
return surface
|
return surface
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_occurrence_name(cls, element_type: ifcopenshell.entity_instance, ifc_class: str):
|
def generate_occurrence_name(cls, element_type: ifcopenshell.entity_instance, ifc_class: str) -> str:
|
||||||
props = bpy.context.scene.BIMModelProperties
|
props = bpy.context.scene.BIMModelProperties
|
||||||
if props.occurrence_name_style == "CLASS":
|
if props.occurrence_name_style == "CLASS":
|
||||||
return ifc_class[3:]
|
return ifc_class[3:]
|
||||||
@@ -216,7 +224,8 @@ class Model(blenderbim.core.tool.Model):
|
|||||||
return "Instance"
|
return "Instance"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_extrusion(cls, representation):
|
def get_extrusion(cls, representation: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
|
"""return first found IfcExtrudedAreaSolid"""
|
||||||
item = representation.Items[0]
|
item = representation.Items[0]
|
||||||
while True:
|
while True:
|
||||||
if item.is_a("IfcExtrudedAreaSolid"):
|
if item.is_a("IfcExtrudedAreaSolid"):
|
||||||
|
|||||||
Reference in New Issue
Block a user