mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 02:07:36 +00:00
typing
This commit is contained in:
@@ -45,8 +45,8 @@ class CoveringTool(WorkSpaceTool):
|
|||||||
CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
CoveringToolUI.draw(context, layout, ifc_element_type=cls.ifc_element_type)
|
||||||
|
|
||||||
|
|
||||||
def add_layout_hotkey(layout, text, hotkey, description):
|
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None:
|
||||||
args = ["covering", layout, text, hotkey, description]
|
args = ("covering", layout, text, hotkey, description)
|
||||||
tool.Blender.add_layout_hotkey_operator(*args)
|
tool.Blender.add_layout_hotkey_operator(*args)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,9 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.geolocation
|
||||||
|
import ifcopenshell.util.placement
|
||||||
|
import ifcopenshell.util.unit
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
import bonsai.core.misc as core
|
import bonsai.core.misc as core
|
||||||
import bonsai.core.geometry as core_geometry
|
import bonsai.core.geometry as core_geometry
|
||||||
|
|||||||
@@ -519,7 +519,7 @@ class AlignProduct(bpy.types.Operator):
|
|||||||
obj.matrix_world = Matrix.Translation(active_y_axis * -y_distances[i]) @ obj.matrix_world
|
obj.matrix_world = Matrix.Translation(active_y_axis * -y_distances[i]) @ obj.matrix_world
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
def get_axis_distances(self, point, axis, context):
|
def get_axis_distances(self, point: Vector, axis: Vector, context: bpy.types.Context) -> list[float]:
|
||||||
results = []
|
results = []
|
||||||
for obj in context.selected_objects:
|
for obj in context.selected_objects:
|
||||||
if self.align_type == "CENTERLINE":
|
if self.align_type == "CENTERLINE":
|
||||||
|
|||||||
@@ -412,11 +412,11 @@ class DumbWallAligner:
|
|||||||
# An alignment shifts the origin of all walls to the closest point on the
|
# An alignment shifts the origin of all walls to the closest point on the
|
||||||
# local X axis of the reference wall. In addition, the Z rotation is copied.
|
# local X axis of the reference wall. In addition, the Z rotation is copied.
|
||||||
# Z translations are ignored for alignment.
|
# Z translations are ignored for alignment.
|
||||||
def __init__(self, wall, reference_wall):
|
def __init__(self, wall: bpy.types.Object, reference_wall: bpy.types.Object):
|
||||||
self.wall = wall
|
self.wall = wall
|
||||||
self.reference_wall = reference_wall
|
self.reference_wall = reference_wall
|
||||||
|
|
||||||
def align_centerline(self):
|
def align_centerline(self) -> None:
|
||||||
self.align_rotation()
|
self.align_rotation()
|
||||||
|
|
||||||
l_start = Vector(self.reference_wall.bound_box[0]).lerp(Vector(self.reference_wall.bound_box[3]), 0.5)
|
l_start = Vector(self.reference_wall.bound_box[0]).lerp(Vector(self.reference_wall.bound_box[3]), 0.5)
|
||||||
@@ -434,7 +434,7 @@ class DumbWallAligner:
|
|||||||
new_origin = point - offset
|
new_origin = point - offset
|
||||||
self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy
|
self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy
|
||||||
|
|
||||||
def align_last_layer(self):
|
def align_last_layer(self) -> None:
|
||||||
self.align_rotation()
|
self.align_rotation()
|
||||||
|
|
||||||
if self.is_rotation_flipped():
|
if self.is_rotation_flipped():
|
||||||
@@ -457,7 +457,7 @@ class DumbWallAligner:
|
|||||||
new_origin = point - offset
|
new_origin = point - offset
|
||||||
self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy
|
self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy
|
||||||
|
|
||||||
def align_first_layer(self):
|
def align_first_layer(self) -> None:
|
||||||
self.align_rotation()
|
self.align_rotation()
|
||||||
|
|
||||||
if self.is_rotation_flipped():
|
if self.is_rotation_flipped():
|
||||||
@@ -480,7 +480,7 @@ class DumbWallAligner:
|
|||||||
new_origin = point - offset
|
new_origin = point - offset
|
||||||
self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy
|
self.wall.matrix_world.translation[0], self.wall.matrix_world.translation[1] = new_origin.xy
|
||||||
|
|
||||||
def align_rotation(self):
|
def align_rotation(self) -> None:
|
||||||
reference = (self.reference_wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
|
reference = (self.reference_wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
|
||||||
wall = (self.wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
|
wall = (self.wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
|
||||||
angle = reference.angle_signed(wall)
|
angle = reference.angle_signed(wall)
|
||||||
@@ -492,7 +492,7 @@ class DumbWallAligner:
|
|||||||
self.wall.rotation_euler[2] += angle
|
self.wall.rotation_euler[2] += angle
|
||||||
bpy.context.view_layer.update()
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
def is_rotation_flipped(self):
|
def is_rotation_flipped(self) -> bool:
|
||||||
reference = (self.reference_wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
|
reference = (self.reference_wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
|
||||||
wall = (self.wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
|
wall = (self.wall.matrix_world.to_quaternion() @ Vector((1, 0, 0))).to_2d()
|
||||||
angle = reference.angle_signed(wall)
|
angle = reference.angle_signed(wall)
|
||||||
@@ -835,7 +835,7 @@ class DumbWallJoiner:
|
|||||||
body = copy.deepcopy(axis1["reference"])
|
body = copy.deepcopy(axis1["reference"])
|
||||||
self.recreate_wall(element1, wall1, axis, body)
|
self.recreate_wall(element1, wall1, axis, body)
|
||||||
|
|
||||||
def split(self, wall1, target):
|
def split(self, wall1: bpy.types.Object, target: Vector) -> None:
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
|
||||||
element1 = tool.Ifc.get_entity(wall1)
|
element1 = tool.Ifc.get_entity(wall1)
|
||||||
@@ -895,7 +895,7 @@ class DumbWallJoiner:
|
|||||||
self.recreate_wall(element1, wall1, axis1["reference"], axis1["reference"])
|
self.recreate_wall(element1, wall1, axis1["reference"], axis1["reference"])
|
||||||
self.recreate_wall(element2, wall2, axis2["reference"], axis2["reference"])
|
self.recreate_wall(element2, wall2, axis2["reference"], axis2["reference"])
|
||||||
|
|
||||||
def flip(self, wall1):
|
def flip(self, wall1: bpy.types.Object) -> None:
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
|
|
||||||
if tool.Ifc.is_moved(wall1):
|
if tool.Ifc.is_moved(wall1):
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ class SpatialTool(WorkSpaceTool):
|
|||||||
SpatialToolUI.draw(context, layout)
|
SpatialToolUI.draw(context, layout)
|
||||||
|
|
||||||
|
|
||||||
def add_layout_hotkey(layout, text, hotkey, description):
|
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None:
|
||||||
args = ["spatial", layout, text, hotkey, description]
|
args = ("spatial", layout, text, hotkey, description)
|
||||||
tool.Blender.add_layout_hotkey_operator(*args)
|
tool.Blender.add_layout_hotkey_operator(*args)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ class StructuralTool(WorkSpaceTool):
|
|||||||
StructuralToolUI.draw(context, layout)
|
StructuralToolUI.draw(context, layout)
|
||||||
|
|
||||||
|
|
||||||
def add_layout_hotkey(layout, text, hotkey, description):
|
def add_layout_hotkey(layout: bpy.types.UILayout, text: str, hotkey: str, description: str) -> None:
|
||||||
args = ["structural", layout, text, hotkey, description]
|
args = ("structural", layout, text, hotkey, description)
|
||||||
tool.Blender.add_layout_hotkey_operator(*args)
|
tool.Blender.add_layout_hotkey_operator(*args)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,16 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
def resize_to_storey(misc, obj=None, total_storeys=None):
|
if TYPE_CHECKING:
|
||||||
|
import bpy
|
||||||
|
import ifcopenshell
|
||||||
|
import bonsai.tool as tool
|
||||||
|
|
||||||
|
|
||||||
|
def resize_to_storey(misc: tool.Misc, obj: bpy.types.Object, total_storeys: int) -> None:
|
||||||
storey = misc.get_object_storey(obj)
|
storey = misc.get_object_storey(obj)
|
||||||
if not storey:
|
if not storey:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -19,28 +19,33 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import bmesh
|
import bmesh
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
import ifcopenshell.util.placement
|
||||||
|
import ifcopenshell.util.unit
|
||||||
import bonsai.core.tool
|
import bonsai.core.tool
|
||||||
import bonsai.core.root
|
import bonsai.core.root
|
||||||
import bonsai.tool as tool
|
import bonsai.tool as tool
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from bonsai.bim.ifc import IfcStore
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
class Misc(bonsai.core.tool.Misc):
|
class Misc(bonsai.core.tool.Misc):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_object_storey(cls, obj):
|
def get_object_storey(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
storey = ifcopenshell.util.element.get_container(tool.Ifc.get_entity(obj))
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
assert element
|
||||||
|
storey = ifcopenshell.util.element.get_container(element)
|
||||||
if storey and storey.is_a("IfcBuildingStorey"):
|
if storey and storey.is_a("IfcBuildingStorey"):
|
||||||
return storey
|
return storey
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_storey_elevation_in_si(cls, storey):
|
def get_storey_elevation_in_si(cls, storey: ifcopenshell.entity_instance) -> float:
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||||
elevation = ifcopenshell.util.placement.get_storey_elevation(storey)
|
elevation = ifcopenshell.util.placement.get_storey_elevation(storey)
|
||||||
return elevation * unit_scale
|
return elevation * unit_scale
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_storey_height_in_si(cls, storey, total_storeys):
|
def get_storey_height_in_si(cls, storey: ifcopenshell.entity_instance, total_storeys: int) -> Union[float, None]:
|
||||||
building = ifcopenshell.util.element.get_aggregate(storey)
|
building = ifcopenshell.util.element.get_aggregate(storey)
|
||||||
related_objects = []
|
related_objects = []
|
||||||
for rel in building.IsDecomposedBy:
|
for rel in building.IsDecomposedBy:
|
||||||
@@ -60,11 +65,12 @@ class Misc(bonsai.core.tool.Misc):
|
|||||||
return (next_storey_elevation - storey_elevation) * unit_scale
|
return (next_storey_elevation - storey_elevation) * unit_scale
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_object_origin_to_bottom(cls, obj):
|
def set_object_origin_to_bottom(cls, obj: bpy.types.Object) -> None:
|
||||||
absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box]
|
absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box]
|
||||||
min_z = min([c[2] for c in absolute_bound_box])
|
min_z = min([c[2] for c in absolute_bound_box])
|
||||||
new_origin = obj.matrix_world.translation.copy()
|
new_origin = obj.matrix_world.translation.copy()
|
||||||
new_origin[2] = min_z
|
new_origin[2] = min_z
|
||||||
|
assert isinstance(obj.data, bpy.types.Mesh)
|
||||||
obj.data.transform(
|
obj.data.transform(
|
||||||
Matrix.Translation(
|
Matrix.Translation(
|
||||||
(obj.matrix_world.inverted().to_quaternion() @ (obj.matrix_world.translation - new_origin))
|
(obj.matrix_world.inverted().to_quaternion() @ (obj.matrix_world.translation - new_origin))
|
||||||
@@ -73,15 +79,15 @@ class Misc(bonsai.core.tool.Misc):
|
|||||||
obj.matrix_world.translation = new_origin
|
obj.matrix_world.translation = new_origin
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def move_object_to_elevation(cls, obj, elevation):
|
def move_object_to_elevation(cls, obj: bpy.types.Object, elevation: float) -> None:
|
||||||
obj.matrix_world.translation[2] = elevation
|
obj.matrix_world.translation[2] = elevation
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_root_copy_class(cls, obj=None):
|
def run_root_copy_class(cls, obj: bpy.types.Object) -> ifcopenshell.entity_instance:
|
||||||
return bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
|
return bonsai.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def scale_object_to_height(cls, obj, height):
|
def scale_object_to_height(cls, obj: bpy.types.Object, height: float) -> None:
|
||||||
absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box]
|
absolute_bound_box = [obj.matrix_world @ Vector(c) for c in obj.bound_box]
|
||||||
max_z = max([c[2] for c in absolute_bound_box])
|
max_z = max([c[2] for c in absolute_bound_box])
|
||||||
min_z = min([c[2] for c in absolute_bound_box])
|
min_z = min([c[2] for c in absolute_bound_box])
|
||||||
@@ -101,6 +107,7 @@ class Misc(bonsai.core.tool.Misc):
|
|||||||
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
|
cls, objs: list[bpy.types.Object], cutter: bpy.types.Object
|
||||||
) -> list[bpy.types.Object]:
|
) -> list[bpy.types.Object]:
|
||||||
cutter_mesh = cutter.data
|
cutter_mesh = cutter.data
|
||||||
|
assert isinstance(cutter_mesh, bpy.types.Mesh)
|
||||||
|
|
||||||
bm = bmesh.new()
|
bm = bmesh.new()
|
||||||
bm.from_mesh(cutter_mesh)
|
bm.from_mesh(cutter_mesh)
|
||||||
@@ -119,6 +126,7 @@ class Misc(bonsai.core.tool.Misc):
|
|||||||
collection.objects.link(new_obj)
|
collection.objects.link(new_obj)
|
||||||
|
|
||||||
mod = new_obj.modifiers.new(type="BOOLEAN", name="Boolean")
|
mod = new_obj.modifiers.new(type="BOOLEAN", name="Boolean")
|
||||||
|
assert isinstance(mod, bpy.types.BooleanModifier)
|
||||||
mod.object = cutter
|
mod.object = cutter
|
||||||
with bpy.context.temp_override(object=new_obj):
|
with bpy.context.temp_override(object=new_obj):
|
||||||
bpy.ops.object.modifier_apply(modifier="Boolean")
|
bpy.ops.object.modifier_apply(modifier="Boolean")
|
||||||
@@ -126,6 +134,7 @@ class Misc(bonsai.core.tool.Misc):
|
|||||||
bm_flipped.to_mesh(cutter_mesh)
|
bm_flipped.to_mesh(cutter_mesh)
|
||||||
|
|
||||||
mod = obj.modifiers.new(type="BOOLEAN", name="Boolean")
|
mod = obj.modifiers.new(type="BOOLEAN", name="Boolean")
|
||||||
|
assert isinstance(mod, bpy.types.BooleanModifier)
|
||||||
mod.object = cutter
|
mod.object = cutter
|
||||||
with bpy.context.temp_override(object=obj):
|
with bpy.context.temp_override(object=obj):
|
||||||
bpy.ops.object.modifier_apply(modifier="Boolean")
|
bpy.ops.object.modifier_apply(modifier="Boolean")
|
||||||
|
|||||||
@@ -762,6 +762,12 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_x_y_z_h_mat_from_obj(cls, obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]:
|
def get_x_y_z_h_mat_from_obj(cls, obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]:
|
||||||
|
"""
|
||||||
|
`x`, `y` - object's center XY in world space;\n
|
||||||
|
`z` - object's local Z- in world space;\n
|
||||||
|
`h` - object's Z dimension;\n
|
||||||
|
`mat` - object's matrix
|
||||||
|
"""
|
||||||
mat = obj.matrix_world
|
mat = obj.matrix_world
|
||||||
local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector())
|
local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector())
|
||||||
global_bbox_center = mat @ local_bbox_center
|
global_bbox_center = mat @ local_bbox_center
|
||||||
@@ -774,6 +780,12 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_x_y_z_h_mat_from_cursor(cls) -> tuple[float, float, float, float, Matrix]:
|
def get_x_y_z_h_mat_from_cursor(cls) -> tuple[float, float, float, float, Matrix]:
|
||||||
|
"""
|
||||||
|
`x`, `y` - from cursor;\n
|
||||||
|
`z` - from default container Z location (if set);\n
|
||||||
|
`h` - default value of 3;\n
|
||||||
|
`mat` - identity matrix
|
||||||
|
"""
|
||||||
x, y, z = bpy.context.scene.cursor.location.xyz
|
x, y, z = bpy.context.scene.cursor.location.xyz
|
||||||
if container := tool.Root.get_default_container():
|
if container := tool.Root.get_default_container():
|
||||||
if container_obj := tool.Ifc.get_object(container):
|
if container_obj := tool.Ifc.get_object(container):
|
||||||
@@ -940,6 +952,7 @@ class Spatial(bonsai.core.tool.Spatial):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def edit_active_space_obj_from_mesh(cls, mesh: bpy.types.Mesh) -> None:
|
def edit_active_space_obj_from_mesh(cls, mesh: bpy.types.Mesh) -> None:
|
||||||
active_obj = bpy.context.active_object
|
active_obj = bpy.context.active_object
|
||||||
|
assert active_obj and isinstance(active_obj.data, bpy.types.Mesh)
|
||||||
mesh.name = active_obj.data.name
|
mesh.name = active_obj.data.name
|
||||||
mesh.BIMMeshProperties.ifc_definition_id = active_obj.data.BIMMeshProperties.ifc_definition_id
|
mesh.BIMMeshProperties.ifc_definition_id = active_obj.data.BIMMeshProperties.ifc_definition_id
|
||||||
tool.Geometry.change_object_data(active_obj, mesh, is_global=True)
|
tool.Geometry.change_object_data(active_obj, mesh, is_global=True)
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ HEADER_FIELDS = {
|
|||||||
|
|
||||||
|
|
||||||
class Transaction:
|
class Transaction:
|
||||||
def __init__(self, ifc_file):
|
def __init__(self, ifc_file: file):
|
||||||
self.file = ifc_file
|
self.file: file = ifc_file
|
||||||
self.operations = []
|
self.operations = []
|
||||||
self.is_batched = False
|
self.is_batched = False
|
||||||
self.batch_delete_index = 0
|
self.batch_delete_index = 0
|
||||||
|
|||||||
Reference in New Issue
Block a user