mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 17:58:20 +00:00
typing
This commit is contained in:
@@ -32,7 +32,7 @@ class AddInstanceFlooringCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operato
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type_id = int(bpy.context.scene.BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
return relating_type == "FLOORING"
|
||||
|
||||
@@ -51,7 +51,7 @@ class AddInstanceCeilingCoveringFromCursor(bpy.types.Operator, tool.Ifc.Operator
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type_id = int(bpy.context.scene.BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
return relating_type == "CEILING"
|
||||
|
||||
@@ -91,7 +91,7 @@ class AddInstanceFlooringCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operato
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type_id = int(bpy.context.scene.BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
if element and element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2":
|
||||
return context.selected_objects and relating_type == "FLOORING"
|
||||
@@ -119,7 +119,7 @@ class AddInstanceCeilingCoveringsFromWalls(bpy.types.Operator, tool.Ifc.Operator
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
relating_type_id = int(bpy.data.scenes["Scene"].BIMModelProperties.relating_type_id)
|
||||
relating_type_id = int(bpy.context.scene.BIMModelProperties.relating_type_id)
|
||||
relating_type = ifcopenshell.util.element.get_predefined_type(tool.Ifc.get().by_id(relating_type_id))
|
||||
if element and element.is_a("IfcWall") and tool.Model.get_usage_type(element) == "LAYER2":
|
||||
return context.selected_objects and relating_type == "CEILING"
|
||||
|
||||
@@ -16,8 +16,16 @@
|
||||
# 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/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
def add_instance_flooring_covering_from_cursor(ifc, root, spatial):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def add_instance_flooring_covering_from_cursor(ifc: tool.Ifc, root: tool.Root, spatial: tool.Spatial) -> None:
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
|
||||
@@ -32,7 +40,7 @@ def add_instance_flooring_covering_from_cursor(ifc, root, spatial):
|
||||
relating_type = None
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_obj(active_obj)
|
||||
else:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor()
|
||||
|
||||
@@ -59,7 +67,9 @@ def add_instance_flooring_covering_from_cursor(ifc, root, spatial):
|
||||
spatial.regen_obj_representation(obj, body)
|
||||
|
||||
|
||||
def add_instance_ceiling_covering_from_cursor(ifc, root, covering, spatial):
|
||||
def add_instance_ceiling_covering_from_cursor(
|
||||
ifc: tool.Ifc, root: tool.Root, covering: tool.Covering, spatial: tool.Spatial
|
||||
) -> None:
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
|
||||
@@ -74,7 +84,7 @@ def add_instance_ceiling_covering_from_cursor(ifc, root, covering, spatial):
|
||||
relating_type = None
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_obj(active_obj)
|
||||
else:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor()
|
||||
ceiling_height = covering.get_z_from_ceiling_height()
|
||||
@@ -101,7 +111,7 @@ def add_instance_ceiling_covering_from_cursor(ifc, root, covering, spatial):
|
||||
spatial.regen_obj_representation(obj, body)
|
||||
|
||||
|
||||
def regen_selected_covering_object(root, spatial):
|
||||
def regen_selected_covering_object(root: tool.Root, spatial: tool.Spatial) -> None:
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
|
||||
@@ -109,7 +119,7 @@ def regen_selected_covering_object(root, spatial):
|
||||
selected_objects = spatial.get_selected_objects()
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_obj(active_obj)
|
||||
|
||||
space_polygon = spatial.get_space_polygon_from_context_visible_objects(x, y)
|
||||
|
||||
@@ -132,7 +142,7 @@ def regen_selected_covering_object(root, spatial):
|
||||
|
||||
|
||||
# TODO CHECK IF IT IS POSSIBLE TO CREATE ONLY ONE CORE FUNCTION FOR _FROM_WALLS
|
||||
def add_instance_flooring_coverings_from_walls(root, spatial):
|
||||
def add_instance_flooring_coverings_from_walls(root: tool.Root, spatial: tool.Spatial) -> None:
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
|
||||
@@ -158,7 +168,7 @@ def add_instance_flooring_coverings_from_walls(root, spatial):
|
||||
spatial.regen_obj_representation(obj, body)
|
||||
|
||||
|
||||
def add_instance_ceiling_coverings_from_walls(root, spatial, covering):
|
||||
def add_instance_ceiling_coverings_from_walls(root: tool.Root, spatial: tool.Spatial, covering: tool.Covering) -> None:
|
||||
if not root.get_default_container():
|
||||
raise NoDefaultContainer()
|
||||
|
||||
|
||||
@@ -18,11 +18,12 @@
|
||||
|
||||
from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
@@ -225,7 +226,11 @@ def disable_editing_drawings(drawing: tool.Drawing) -> None:
|
||||
|
||||
|
||||
def add_drawing(
|
||||
ifc: tool.Ifc, collector: tool.Collector, drawing: tool.Drawing, target_view=None, location_hint=None
|
||||
ifc: tool.Ifc,
|
||||
collector: tool.Collector,
|
||||
drawing: tool.Drawing,
|
||||
target_view: Union[ifcopenshell.util.representation.TARGET_VIEW, None] = None,
|
||||
location_hint: Union[str, None] = None,
|
||||
) -> None:
|
||||
drawing_name = drawing.ensure_unique_drawing_name(drawing.generate_drawing_name(target_view, location_hint))
|
||||
drawing_matrix = drawing.generate_drawing_matrix(target_view, location_hint)
|
||||
|
||||
@@ -173,7 +173,7 @@ def generate_space(ifc: tool.Ifc, model: tool.Model, root: tool.Root, spatial: t
|
||||
relating_type = None
|
||||
|
||||
if selected_objects and active_obj:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_active_obj(active_obj)
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_obj(active_obj)
|
||||
element = ifc.get_entity(active_obj)
|
||||
else:
|
||||
x, y, z, h, mat = spatial.get_x_y_z_h_mat_from_cursor()
|
||||
|
||||
@@ -888,7 +888,7 @@ class Spatial:
|
||||
def get_boundary_lines_from_context_visible_objects(cls): pass
|
||||
def get_gross_mesh_from_element(cls, visible_element): pass
|
||||
def create_mesh_from_shape(cls, shape): pass
|
||||
def get_x_y_z_h_mat_from_active_obj(cls, active_obj): pass
|
||||
def get_x_y_z_h_mat_from_obj(cls, obj): pass
|
||||
def get_x_y_z_h_mat_from_cursor(cls): pass
|
||||
def get_union_shape_from_selected_objects(cls): pass
|
||||
def get_boundary_elements(cls, selected_objects): pass
|
||||
|
||||
@@ -33,7 +33,7 @@ from shapely import Polygon, MultiPolygon
|
||||
|
||||
class Covering(blenderbim.core.tool.Covering):
|
||||
@classmethod
|
||||
def get_z_from_ceiling_height(cls):
|
||||
def get_z_from_ceiling_height(cls) -> float:
|
||||
props = bpy.context.scene.BIMCoveringProperties
|
||||
return props.ceiling_height
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import os
|
||||
import re
|
||||
import collections
|
||||
import collections.abc
|
||||
import bpy
|
||||
import math
|
||||
import json
|
||||
@@ -79,11 +80,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
# fmt: on
|
||||
|
||||
@classmethod
|
||||
def canonicalise_class_name(cls, name):
|
||||
def canonicalise_class_name(cls, name: str) -> str:
|
||||
return re.sub("[^0-9a-zA-Z]+", "", name)
|
||||
|
||||
@classmethod
|
||||
def copy_representation(cls, source, dest):
|
||||
def copy_representation(cls, source: ifcopenshell.entity_instance, dest: ifcopenshell.entity_instance) -> None:
|
||||
if source.Representation:
|
||||
dest.Representation = ifcopenshell.util.element.copy_deep(
|
||||
tool.Ifc.get(), source.Representation, exclude=["IfcGeometricRepresentationContext"]
|
||||
@@ -114,7 +115,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def ensure_annotation_in_drawing_plane(cls, obj, camera=None):
|
||||
def ensure_annotation_in_drawing_plane(
|
||||
cls, obj: bpy.types.Object, camera: Optional[bpy.types.Object] = None
|
||||
) -> None:
|
||||
"""Make sure annotation object is going to be visible in the camera view"""
|
||||
|
||||
def get_camera_from_annotation_object(obj):
|
||||
@@ -138,7 +141,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
ANNOTATION_TYPES_SUPPORT_SETUP = ("STAIR_ARROW", "TEXT", "REVISION_CLOUD", "FILL_AREA")
|
||||
|
||||
@classmethod
|
||||
def setup_annotation_object(cls, obj, object_type, related_object=None):
|
||||
def setup_annotation_object(
|
||||
cls, obj: bpy.types.Object, object_type: str, related_object: Optional[bpy.types.Object] = None
|
||||
) -> None:
|
||||
"""Finish object's adjustments after both object and entity are created"""
|
||||
|
||||
if not related_object:
|
||||
@@ -205,7 +210,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
tool.Drawing.update_text_value(obj)
|
||||
|
||||
@classmethod
|
||||
def is_annotation_object_type(cls, element, object_types):
|
||||
def is_annotation_object_type(
|
||||
cls, element: ifcopenshell.entity_instance, object_types: Union[str, list[str]]
|
||||
) -> bool:
|
||||
if not isinstance(object_types, collections.abc.Iterable):
|
||||
object_types = [object_types]
|
||||
|
||||
@@ -226,7 +233,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_annotation_representation(cls, element):
|
||||
def get_annotation_representation(
|
||||
cls, element: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
rep = ifcopenshell.util.representation.get_representation(
|
||||
element, "Plan", "Annotation"
|
||||
) or ifcopenshell.util.representation.get_representation(element, "Model", "Annotation")
|
||||
@@ -237,7 +246,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return rep
|
||||
|
||||
@classmethod
|
||||
def create_camera(cls, name, matrix, location_hint):
|
||||
def create_camera(
|
||||
cls, name: str, matrix: Matrix, location_hint: Literal["PERPSECTIVE", "ORTHOGRAPHIC"]
|
||||
) -> bpy.types.Object:
|
||||
camera = bpy.data.objects.new(name, bpy.data.cameras.new(name))
|
||||
camera.location = (0, 0, 1.5) # The view shall be 1.5m above the origin
|
||||
camera.data.show_limits = True
|
||||
@@ -257,7 +268,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return camera
|
||||
|
||||
@classmethod
|
||||
def create_svg_schedule(cls, schedule):
|
||||
def create_svg_schedule(cls, schedule: ifcopenshell.entity_instance) -> None:
|
||||
import blenderbim.bim.module.drawing.scheduler as scheduler
|
||||
|
||||
schedule_creator = scheduler.Scheduler()
|
||||
@@ -276,7 +287,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return uri
|
||||
|
||||
@classmethod
|
||||
def add_drawings(cls, sheet):
|
||||
def add_drawings(cls, sheet: ifcopenshell.entity_instance) -> None:
|
||||
import blenderbim.bim.module.drawing.sheeter as sheeter
|
||||
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
@@ -293,7 +304,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
sheet_builder.add_drawing(drawing_references[drawing_annotation.Name], drawing_annotation, sheet)
|
||||
|
||||
@classmethod
|
||||
def delete_collection(cls, collection):
|
||||
def delete_collection(cls, collection: bpy.types.Collection) -> None:
|
||||
bpy.data.collections.remove(collection, do_unlink=True)
|
||||
|
||||
@classmethod
|
||||
@@ -312,19 +323,19 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_drawings(cls):
|
||||
def disable_editing_drawings(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_drawings = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_schedules(cls):
|
||||
def disable_editing_schedules(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_schedules = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_references(cls):
|
||||
def disable_editing_references(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_references = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_sheets(cls):
|
||||
def disable_editing_sheets(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_sheets = False
|
||||
|
||||
@classmethod
|
||||
@@ -344,19 +355,19 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
bpy.ops.object.mode_set(mode="EDIT")
|
||||
|
||||
@classmethod
|
||||
def enable_editing_drawings(cls):
|
||||
def enable_editing_drawings(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_drawings = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_schedules(cls):
|
||||
def enable_editing_schedules(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_schedules = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_references(cls):
|
||||
def enable_editing_references(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_references = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_sheets(cls):
|
||||
def enable_editing_sheets(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_sheets = True
|
||||
|
||||
@classmethod
|
||||
@@ -368,14 +379,14 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
obj.BIMAssignedProductProperties.is_editing_product = True
|
||||
|
||||
@classmethod
|
||||
def ensure_unique_drawing_name(cls, name):
|
||||
def ensure_unique_drawing_name(cls, name: str) -> str:
|
||||
names = [e.Name for e in tool.Ifc.get().by_type("IfcAnnotation") if e.ObjectType == "DRAWING"]
|
||||
while name in names:
|
||||
name += "-X"
|
||||
return name
|
||||
|
||||
@classmethod
|
||||
def ensure_unique_identification(cls, identification):
|
||||
def ensure_unique_identification(cls, identification: str) -> str:
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
ids = [d.DocumentId for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "DOCUMENTATION"]
|
||||
else:
|
||||
@@ -431,7 +442,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Annotation", target_view)
|
||||
|
||||
@classmethod
|
||||
def get_body_context(cls):
|
||||
def get_body_context(cls) -> ifcopenshell.entity_instance:
|
||||
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@classmethod
|
||||
@@ -462,15 +473,15 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
@classmethod
|
||||
def get_path_with_ext(cls, path, ext):
|
||||
def get_path_with_ext(cls, path: str, ext: str) -> str:
|
||||
return os.path.splitext(path)[0] + f".{ext}"
|
||||
|
||||
@classmethod
|
||||
def get_unit_system(cls):
|
||||
def get_unit_system(cls) -> Literal["NONE", "METRIC", "IMPERIAL"]:
|
||||
return bpy.context.scene.unit_settings.system
|
||||
|
||||
@classmethod
|
||||
def get_drawing_collection(cls, drawing):
|
||||
def get_drawing_collection(cls, drawing: ifcopenshell.entity_instance) -> Union[bpy.types.Collection, None]:
|
||||
obj = tool.Ifc.get_object(drawing)
|
||||
if obj:
|
||||
return obj.BIMObjectProperties.collection
|
||||
@@ -488,8 +499,8 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return rel.RelatingDocument
|
||||
|
||||
@classmethod
|
||||
def get_drawing_references(cls, drawing):
|
||||
results = set()
|
||||
def get_drawing_references(cls, drawing: ifcopenshell.entity_instance) -> set[ifcopenshell.entity_instance]:
|
||||
results: set[ifcopenshell.entity_instance] = set()
|
||||
for inverse in tool.Ifc.get().get_inverse(drawing):
|
||||
if inverse.is_a("IfcRelAssignsToProduct") and inverse.RelatingProduct == drawing:
|
||||
results.update(inverse.RelatedObjects)
|
||||
@@ -505,7 +516,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return rel.RelatedObjects
|
||||
|
||||
@classmethod
|
||||
def get_ifc_representation_class(cls, object_type):
|
||||
def get_ifc_representation_class(cls, object_type: str) -> str:
|
||||
if object_type == "TEXT":
|
||||
return "IfcTextLiteral"
|
||||
elif object_type == "TEXT_LEADER":
|
||||
@@ -517,7 +528,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return element.Name
|
||||
|
||||
@classmethod
|
||||
def generate_drawing_matrix(cls, target_view, location_hint):
|
||||
def generate_drawing_matrix(
|
||||
cls, target_view: ifcopenshell.util.representation.TARGET_VIEW, location_hint: str
|
||||
) -> Matrix:
|
||||
x, y, z = (0, 0, 0) if location_hint == 0 else bpy.context.scene.cursor.matrix.translation
|
||||
if target_view == "PLAN_VIEW":
|
||||
if location_hint:
|
||||
@@ -554,12 +567,14 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return mathutils.Matrix()
|
||||
|
||||
@classmethod
|
||||
def generate_sheet_identification(cls):
|
||||
def generate_sheet_identification(cls) -> str:
|
||||
number = len([d for d in tool.Ifc.get().by_type("IfcDocumentInformation") if d.Scope == "DOCUMENTATION"])
|
||||
return "A" + str(number).zfill(2)
|
||||
|
||||
@classmethod
|
||||
def get_text_literal(cls, obj, return_list=False):
|
||||
def get_text_literal(
|
||||
cls, obj: bpy.types.Object, return_list: bool = False
|
||||
) -> Union[ifcopenshell.entity_instance, None, list[ifcopenshell.entity_instance]]:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -575,11 +590,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return items[0]
|
||||
|
||||
@classmethod
|
||||
def is_editing_sheets(cls):
|
||||
def is_editing_sheets(cls) -> bool:
|
||||
return bpy.context.scene.DocProperties.is_editing_sheets
|
||||
|
||||
@classmethod
|
||||
def remove_literal_from_annotation(cls, obj, literal):
|
||||
def remove_literal_from_annotation(cls, obj: bpy.types.Object, literal: ifcopenshell.entity_instance) -> None:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -617,7 +632,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
cls.remove_literal_from_annotation(obj, literal)
|
||||
|
||||
@classmethod
|
||||
def add_literal_to_annotation(cls, obj, Literal="Literal", Path="RIGHT", BoxAlignment="bottom-left"):
|
||||
def add_literal_to_annotation(
|
||||
cls, obj: bpy.types.Object, Literal: str = "Literal", Path: str = "RIGHT", BoxAlignment: str = "bottom-left"
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
@@ -831,7 +848,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
new.identification = schedule.Identification
|
||||
|
||||
@classmethod
|
||||
def import_sheets(cls):
|
||||
def import_sheets(cls) -> None:
|
||||
props = bpy.context.scene.DocProperties
|
||||
expanded_sheets = {s.ifc_definition_id for s in props.sheets if s.is_expanded}
|
||||
props.sheets.clear()
|
||||
@@ -868,7 +885,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
new.reference_type = reference_description
|
||||
|
||||
@classmethod
|
||||
def get_active_sheet(cls, context):
|
||||
def get_active_sheet(cls, context: bpy.types.Context) -> bpy.types.PropertyGroup:
|
||||
props = context.scene.DocProperties
|
||||
return next(s for s in props.sheets[: props.active_sheet_index + 1][::-1] if s.is_sheet)
|
||||
|
||||
@@ -904,7 +921,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
obj.BIMAssignedProductProperties.relating_product = None
|
||||
|
||||
@classmethod
|
||||
def open_with_user_command(cls, user_command, path):
|
||||
def open_with_user_command(cls, user_command: str, path: str) -> None:
|
||||
if user_command:
|
||||
commands = json.loads(user_command)
|
||||
replacements = {"path": path}
|
||||
@@ -920,15 +937,15 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
subprocess.call(("xdg-open", path))
|
||||
|
||||
@classmethod
|
||||
def open_spreadsheet(cls, uri):
|
||||
def open_spreadsheet(cls, uri: str) -> None:
|
||||
cls.open_with_user_command(tool.Blender.get_addon_preferences().spreadsheet_command, uri)
|
||||
|
||||
@classmethod
|
||||
def open_svg(cls, uri):
|
||||
def open_svg(cls, uri: str) -> None:
|
||||
cls.open_with_user_command(tool.Blender.get_addon_preferences().svg_command, uri)
|
||||
|
||||
@classmethod
|
||||
def open_layout_svg(cls, uri):
|
||||
def open_layout_svg(cls, uri: str) -> None:
|
||||
cls.open_with_user_command(tool.Blender.get_addon_preferences().layout_svg_command, uri)
|
||||
|
||||
@classmethod
|
||||
@@ -954,15 +971,17 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def set_drawing_collection_name(cls, drawing, collection):
|
||||
def set_drawing_collection_name(
|
||||
cls, drawing: ifcopenshell.entity_instance, collection: bpy.types.Collection
|
||||
) -> None:
|
||||
collection.name = tool.Loader.get_name(drawing)
|
||||
|
||||
@classmethod
|
||||
def set_name(cls, element, name):
|
||||
def set_name(cls, element: ifcopenshell.entity_instance, name: str) -> None:
|
||||
element.Name = name
|
||||
|
||||
@classmethod
|
||||
def show_decorations(cls):
|
||||
def show_decorations(cls) -> None:
|
||||
bpy.context.scene.DocProperties.should_draw_decorations = True
|
||||
|
||||
@classmethod
|
||||
@@ -1013,15 +1032,15 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
# TODO below this point is highly experimental prototype code with no tests
|
||||
|
||||
@classmethod
|
||||
def does_file_exist(cls, uri):
|
||||
def does_file_exist(cls, uri: str) -> bool:
|
||||
return os.path.exists(uri)
|
||||
|
||||
@classmethod
|
||||
def delete_file(cls, uri):
|
||||
def delete_file(cls, uri: str) -> None:
|
||||
os.remove(uri)
|
||||
|
||||
@classmethod
|
||||
def move_file(cls, src, dest):
|
||||
def move_file(cls, src: str, dest: str) -> None:
|
||||
try:
|
||||
shutil.move(src, dest)
|
||||
except:
|
||||
@@ -1029,7 +1048,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
shutil.copy(src, dest)
|
||||
|
||||
@classmethod
|
||||
def generate_drawing_name(cls, target_view, location_hint):
|
||||
def generate_drawing_name(
|
||||
cls, target_view: ifcopenshell.util.representation.TARGET_VIEW, location_hint: str
|
||||
) -> str:
|
||||
if target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW") and location_hint:
|
||||
location = tool.Ifc.get().by_id(location_hint)
|
||||
if target_view == "REFLECTED_PLAN_VIEW":
|
||||
@@ -1042,7 +1063,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return target_view
|
||||
|
||||
@classmethod
|
||||
def get_default_layout_path(cls, identification, name):
|
||||
def get_default_layout_path(cls, identification: str, name: str) -> str:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
layouts_dir = (
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "LayoutsDir")
|
||||
@@ -1051,7 +1072,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return os.path.join(layouts_dir, cls.sanitise_filename(f"{identification} - {name}.svg")).replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
def get_default_sheet_path(cls, identification, name):
|
||||
def get_default_sheet_path(cls, identification: str, name: str) -> str:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
sheets_dir = (
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "SheetsDir")
|
||||
@@ -1060,7 +1081,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return os.path.join(sheets_dir, cls.sanitise_filename(f"{identification} - {name}.svg")).replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
def get_default_titleblock_path(cls, name):
|
||||
def get_default_titleblock_path(cls, name: str) -> str:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
titleblocks_dir = (
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "TitleblocksDir")
|
||||
@@ -1069,7 +1090,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return os.path.join(titleblocks_dir, cls.sanitise_filename(f"{name}.svg")).replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
def get_default_drawing_path(cls, name):
|
||||
def get_default_drawing_path(cls, name: str) -> str:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
drawings_dir = (
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "DrawingsDir")
|
||||
@@ -1078,11 +1099,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return os.path.join(drawings_dir, cls.sanitise_filename(f"{name}.svg")).replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
def sanitise_filename(cls, name):
|
||||
def sanitise_filename(cls, name: str) -> str:
|
||||
return "".join(x for x in name if (x.isalnum() or x in "._- "))
|
||||
|
||||
@classmethod
|
||||
def get_default_drawing_resource_path(cls, resource):
|
||||
def get_default_drawing_resource_path(cls, resource: str) -> Union[str, None]:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
resource_path = ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", f"{resource}Path") or getattr(
|
||||
bpy.context.scene.DocProperties, f"{resource.lower()}_path"
|
||||
@@ -1091,12 +1112,12 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return resource_path.replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
def get_default_shading_style(cls):
|
||||
def get_default_shading_style(cls) -> str:
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
return dprops.shadingstyle_default
|
||||
|
||||
@classmethod
|
||||
def setup_shading_styles_path(cls, resource_path):
|
||||
def setup_shading_styles_path(cls, resource_path: str) -> None:
|
||||
resource_path = tool.Ifc.resolve_uri(resource_path)
|
||||
os.makedirs(os.path.dirname(resource_path), exist_ok=True)
|
||||
if not os.path.exists(resource_path):
|
||||
@@ -1387,7 +1408,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
clipping = is_ortho and target_view in ("PLAN_VIEW", "REFLECTED_PLAN_VIEW")
|
||||
elevating = is_ortho and target_view in ("ELEVATION_VIEW", "SECTION_VIEW")
|
||||
|
||||
def clone(src):
|
||||
def clone(src: bpy.types.Object) -> bpy.types.Object:
|
||||
dst = src.copy()
|
||||
dst.data = dst.data.copy()
|
||||
dst.name = dst.name.replace("IfcGridAxis/", "")
|
||||
@@ -1395,13 +1416,13 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
dst.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
return dst
|
||||
|
||||
def disassemble(obj):
|
||||
def disassemble(obj: bpy.types.Object) -> tuple[bpy.types.Object, bmesh.types.BMesh]:
|
||||
mesh = bmesh.new()
|
||||
mesh.verts.ensure_lookup_table()
|
||||
mesh.from_mesh(obj.data)
|
||||
return obj, mesh
|
||||
|
||||
def assemble(obj, mesh):
|
||||
def assemble(obj: bpy.types.Object, mesh: bmesh.types.BMesh) -> bpy.types.Object:
|
||||
mesh.to_mesh(obj.data)
|
||||
return obj
|
||||
|
||||
@@ -1413,7 +1434,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
obj.matrix_world.translation += annotation_offset
|
||||
return obj, mesh
|
||||
|
||||
def clip_to_camera_boundary(mesh):
|
||||
def clip_to_camera_boundary(mesh: bmesh.types.BMesh) -> bmesh.types.BMesh:
|
||||
mesh.verts.ensure_lookup_table()
|
||||
points = [v.co for v in mesh.verts[0:2]]
|
||||
points = helper.clip_segment(bounds, points)
|
||||
@@ -1423,7 +1444,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
mesh.verts[1].co = points[1]
|
||||
return mesh
|
||||
|
||||
def draw_grids_vertically(mesh):
|
||||
def draw_grids_vertically(mesh: bmesh.types.BMesh) -> bmesh.types.BMesh:
|
||||
mesh.verts.ensure_lookup_table()
|
||||
points = [v.co for v in mesh.verts[0:2]]
|
||||
points = helper.elevate_segment(bounds, points)
|
||||
@@ -1534,11 +1555,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return text
|
||||
|
||||
@classmethod
|
||||
def sync_object_representation(cls, obj):
|
||||
def sync_object_representation(cls, obj: bpy.types.Object) -> None:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
@classmethod
|
||||
def sync_object_placement(cls, obj):
|
||||
def sync_object_placement(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
blender_matrix = np.array(obj.matrix_world)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if (obj.scale - mathutils.Vector((1.0, 1.0, 1.0))).length > 1e-4:
|
||||
@@ -1552,7 +1573,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return element
|
||||
|
||||
@classmethod
|
||||
def sync_grid_axis_object_placement(cls, obj, element):
|
||||
def sync_grid_axis_object_placement(cls, obj: bpy.types.Object, element: ifcopenshell.entity_instance) -> None:
|
||||
grid = (element.PartOfU or element.PartOfV or element.PartOfW)[0]
|
||||
grid_obj = tool.Ifc.get_object(grid)
|
||||
if grid_obj:
|
||||
@@ -1568,11 +1589,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return document.HasDocumentReferences or []
|
||||
|
||||
@classmethod
|
||||
def get_references_with_location(cls, location):
|
||||
def get_references_with_location(cls, location: Union[str, None]) -> list[ifcopenshell.entity_instance]:
|
||||
return [r for r in tool.Ifc.get().by_type("IfcDocumentReference") if r.Location == location]
|
||||
|
||||
@classmethod
|
||||
def update_embedded_svg_location(cls, uri, reference, new_location):
|
||||
def update_embedded_svg_location(cls, uri: str, reference: ifcopenshell.entity_instance, new_location: str) -> None:
|
||||
tree = etree.parse(uri)
|
||||
root = tree.getroot()
|
||||
rel_location = os.path.relpath(new_location, os.path.dirname(uri))
|
||||
@@ -1612,11 +1633,13 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def get_reference_location(cls, reference):
|
||||
def get_reference_location(cls, reference: ifcopenshell.entity_instance) -> Union[str, None]:
|
||||
return reference.Location
|
||||
|
||||
@classmethod
|
||||
def get_reference_element(cls, reference):
|
||||
def get_reference_element(
|
||||
cls, reference: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
refs = [r for r in tool.Ifc.get().by_type("IfcRelAssociatesDocument") if r.RelatingDocument == reference]
|
||||
else:
|
||||
@@ -1625,12 +1648,12 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return refs[0].RelatedObjects[0]
|
||||
|
||||
@classmethod
|
||||
def get_drawing_human_scale(cls, drawing):
|
||||
def get_drawing_human_scale(cls, drawing: ifcopenshell.entity_instance) -> str:
|
||||
pset = ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing") or {}
|
||||
return "NTS" if pset.get("IsNTS", False) else pset.get("HumanScale", "NTS")
|
||||
|
||||
@classmethod
|
||||
def get_drawing_metadata(cls, drawing):
|
||||
def get_drawing_metadata(cls, drawing: ifcopenshell.entity_instance) -> list[str]:
|
||||
# fmt: off
|
||||
return [
|
||||
v.strip()
|
||||
@@ -1642,7 +1665,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
# fmt: on
|
||||
|
||||
@classmethod
|
||||
def get_annotation_z_index(cls, drawing):
|
||||
def get_annotation_z_index(cls, drawing: ifcopenshell.entity_instance) -> float:
|
||||
return ifcopenshell.util.element.get_pset(drawing, "EPset_Annotation", "ZIndex") or 0
|
||||
|
||||
@classmethod
|
||||
@@ -1654,11 +1677,11 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return symbol
|
||||
|
||||
@classmethod
|
||||
def has_linework(cls, drawing):
|
||||
def has_linework(cls, drawing: ifcopenshell.entity_instance) -> bool:
|
||||
return ifcopenshell.util.element.get_psets(drawing).get("EPset_Drawing", {}).get("HasLinework", False)
|
||||
|
||||
@classmethod
|
||||
def has_annotation(cls, drawing):
|
||||
def has_annotation(cls, drawing: ifcopenshell.entity_instance) -> bool:
|
||||
return ifcopenshell.util.element.get_psets(drawing).get("EPset_Drawing", {}).get("HasAnnotation", False)
|
||||
|
||||
@classmethod
|
||||
@@ -1723,19 +1746,21 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return elements
|
||||
|
||||
@classmethod
|
||||
def get_annotation_element(cls, element):
|
||||
def get_annotation_element(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for rel in element.HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct"):
|
||||
return rel.RelatingProduct
|
||||
|
||||
@classmethod
|
||||
def get_drawing_reference(cls, drawing):
|
||||
def get_drawing_reference(cls, drawing: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for rel in drawing.HasAssociations:
|
||||
if rel.is_a("IfcRelAssociatesDocument"):
|
||||
return rel.RelatingDocument
|
||||
|
||||
@classmethod
|
||||
def get_reference_document(cls, reference):
|
||||
def get_reference_document(
|
||||
cls, reference: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
return reference.ReferenceToDocument[0]
|
||||
return reference.ReferencedDocument
|
||||
@@ -1749,13 +1774,13 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
tool.Ifc.get_object(product).select_set(True)
|
||||
|
||||
@classmethod
|
||||
def is_drawing_active(cls):
|
||||
def is_drawing_active(cls) -> bool:
|
||||
camera = bpy.context.scene.camera
|
||||
area = tool.Blender.get_view3d_area()
|
||||
return camera and camera.type == "CAMERA" and camera.BIMObjectProperties.ifc_definition_id and area
|
||||
|
||||
@classmethod
|
||||
def is_camera_orthographic(cls):
|
||||
def is_camera_orthographic(cls) -> bool:
|
||||
camera = bpy.context.scene.camera
|
||||
return True if (camera and camera.data.type == "ORTHO") else False
|
||||
|
||||
@@ -1764,7 +1789,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return drawing.id() == bpy.context.scene.DocProperties.active_drawing_id
|
||||
|
||||
@classmethod
|
||||
def run_drawing_activate_model(cls):
|
||||
def run_drawing_activate_model(cls) -> None:
|
||||
bpy.ops.bim.activate_model()
|
||||
|
||||
@classmethod
|
||||
@@ -1906,7 +1931,15 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_in_camera_view(cls, obj, camera_inverse_matrix, x, y, clip_start, clip_end):
|
||||
def is_in_camera_view(
|
||||
cls,
|
||||
obj: bpy.types.Object,
|
||||
camera_inverse_matrix: Matrix,
|
||||
x: float,
|
||||
y: float,
|
||||
clip_start: float,
|
||||
clip_end: float,
|
||||
) -> bool:
|
||||
local_bbox = [camera_inverse_matrix @ obj.matrix_world @ Vector(v) for v in obj.bound_box]
|
||||
local_x = [v.x for v in local_bbox]
|
||||
local_y = [v.y for v in local_bbox]
|
||||
@@ -1921,14 +1954,14 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def is_intersecting_camera(cls, obj, camera):
|
||||
def is_intersecting_camera(cls, obj: bpy.types.Object, camera: bpy.types.Object) -> bool:
|
||||
# Based on separating axis theorem
|
||||
plane_co = camera.matrix_world.translation
|
||||
plane_no = camera.matrix_world.col[2].xyz
|
||||
return cls.is_intersecting_plane(obj, plane_co, plane_no)
|
||||
|
||||
@classmethod
|
||||
def is_intersecting_plane(cls, obj, plane_co, plane_no):
|
||||
def is_intersecting_plane(cls, obj: bpy.types.Object, plane_co: Vector, plane_no: Vector) -> bool:
|
||||
# Broadphase check using the bounding box
|
||||
bounding_box_world_coords = [obj.matrix_world @ Vector(coord) for coord in obj.bound_box]
|
||||
bounding_box_signed_distances = [plane_no.dot(v - plane_co) for v in bounding_box_world_coords]
|
||||
@@ -1958,7 +1991,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return pos_exists and neg_exists
|
||||
|
||||
@classmethod
|
||||
def bisect_mesh(cls, obj, camera):
|
||||
def bisect_mesh(cls, obj: bpy.types.Object, camera: bpy.types.Object) -> tuple[list[Vector], list[list[int]]]:
|
||||
camera_matrix = obj.matrix_world.inverted() @ camera.matrix_world
|
||||
plane_co = camera_matrix.translation
|
||||
plane_no = camera_matrix.col[2].xyz
|
||||
@@ -1969,7 +2002,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return cls.bisect_mesh_with_plane(obj, plane_co, plane_no, global_offset=global_offset)
|
||||
|
||||
@classmethod
|
||||
def bisect_mesh_with_plane(cls, obj, plane_co, plane_no, global_offset=None):
|
||||
def bisect_mesh_with_plane(
|
||||
cls, obj: bpy.types.Object, plane_co: Vector, plane_no: Vector, global_offset: Optional[Vector] = None
|
||||
) -> tuple[list[Vector], list[list[int]]]:
|
||||
if global_offset is None:
|
||||
global_offset = Vector()
|
||||
|
||||
@@ -1980,9 +2015,9 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
geom = bm.verts[:] + bm.edges[:] + bm.faces[:]
|
||||
results = bmesh.ops.bisect_plane(bm, geom=geom, dist=0.0001, plane_co=plane_co, plane_no=plane_no)
|
||||
|
||||
vert_map = {}
|
||||
verts = []
|
||||
edges = []
|
||||
vert_map: dict[int, int] = {}
|
||||
verts: list[Vector] = []
|
||||
edges: list[list[int]] = []
|
||||
i = 0
|
||||
for geom in results["geom_cut"]:
|
||||
if isinstance(geom, bmesh.types.BMVert):
|
||||
@@ -1998,12 +2033,12 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return verts, edges
|
||||
|
||||
@classmethod
|
||||
def get_scale_ratio(cls, scale):
|
||||
def get_scale_ratio(cls, scale: str) -> float:
|
||||
numerator, denominator = scale.split("/")
|
||||
return float(numerator) / float(denominator)
|
||||
|
||||
@classmethod
|
||||
def get_diagram_scale(cls, obj):
|
||||
def get_diagram_scale(cls, obj: bpy.types.Object) -> dict[str, float]:
|
||||
props = obj.data.BIMCameraProperties
|
||||
scale = props.diagram_scale
|
||||
if scale != "CUSTOM":
|
||||
@@ -2029,7 +2064,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return {"HumanScale": human_scale, "Scale": scale}
|
||||
|
||||
@classmethod
|
||||
def convert_scale_string(cls, value):
|
||||
def convert_scale_string(cls, value: str) -> float:
|
||||
try:
|
||||
return float(value)
|
||||
except:
|
||||
@@ -2060,7 +2095,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return result * 0.0254
|
||||
|
||||
@classmethod
|
||||
def extend_line(cls, start, end, distance):
|
||||
def extend_line(cls, start: Vector, end: Vector, distance: float) -> tuple[list[float], list[float]]:
|
||||
start = np.array(start)
|
||||
end = np.array(end)
|
||||
direction = end - start
|
||||
@@ -2068,8 +2103,8 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return (start - offset).tolist(), (end + offset).tolist()
|
||||
|
||||
@classmethod
|
||||
def get_sheet_references(cls, drawing):
|
||||
sheet_references = []
|
||||
def get_sheet_references(cls, drawing: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
sheet_references: list[ifcopenshell.entity_instance] = []
|
||||
drawing_reference = cls.get_drawing_document(drawing)
|
||||
for sheet in tool.Ifc.get().by_type("IfcDocumentInformation"):
|
||||
if not sheet.Scope == "SHEET":
|
||||
@@ -2082,7 +2117,7 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
return sheet_references
|
||||
|
||||
@classmethod
|
||||
def get_camera_matrix(cls, camera):
|
||||
def get_camera_matrix(cls, camera: bpy.types.Object) -> Matrix:
|
||||
matrix_world = camera.matrix_world.copy().normalized()
|
||||
location, rotation, scale = matrix_world.decompose()
|
||||
if scale.x < 0 or scale.y < 0 or scale.z < 0:
|
||||
|
||||
@@ -21,6 +21,9 @@ import json
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.georeference
|
||||
import ifcopenshell.util.geolocation
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.helper
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2021 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/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import re
|
||||
import bpy
|
||||
@@ -5,6 +24,7 @@ import logging
|
||||
from blenderbim.bim import import_ifc
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
import blenderbim.tool as tool
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
# allows git import even if git executable isn't found
|
||||
os.environ["GIT_PYTHON_REFRESH"] = "quiet"
|
||||
@@ -13,15 +33,20 @@ try:
|
||||
except ImportError:
|
||||
print("Warning: GitPython not available.")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import git
|
||||
|
||||
|
||||
class IfcGit:
|
||||
STEP_IDS = dict[str, set[int]]
|
||||
|
||||
@classmethod
|
||||
def init_repo(cls, path_dir):
|
||||
def init_repo(cls, path_dir: str) -> None:
|
||||
IfcGitRepo.repo = git.Repo.init(path_dir)
|
||||
cls.config_info_attributes(IfcGitRepo.repo)
|
||||
|
||||
@classmethod
|
||||
def clone_repo(cls, remote_url, local_folder):
|
||||
def clone_repo(cls, remote_url: str, local_folder: str) -> git.Repo:
|
||||
IfcGitRepo.repo = git.Repo.clone_from(
|
||||
url=remote_url,
|
||||
to_path=local_folder,
|
||||
@@ -30,7 +55,7 @@ class IfcGit:
|
||||
return IfcGitRepo.repo
|
||||
|
||||
@classmethod
|
||||
def load_anyifc(cls, repo):
|
||||
def load_anyifc(cls, repo: git.Repo) -> bool:
|
||||
working_dir = repo.working_dir
|
||||
for item in os.listdir(working_dir):
|
||||
path = os.path.join(working_dir, item)
|
||||
@@ -40,11 +65,11 @@ class IfcGit:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def get_path_dir(cls, path_ifc):
|
||||
def get_path_dir(cls, path_ifc: str) -> str:
|
||||
return os.path.abspath(os.path.dirname(path_ifc))
|
||||
|
||||
@classmethod
|
||||
def repo_from_path(cls, path):
|
||||
def repo_from_path(cls, path: str) -> Union[git.Repo, None]:
|
||||
"""Returns a Git repository object or None"""
|
||||
|
||||
if os.path.isdir(path):
|
||||
@@ -72,7 +97,7 @@ class IfcGit:
|
||||
return repo
|
||||
|
||||
@classmethod
|
||||
def add_file_to_repo(cls, repo, path_file):
|
||||
def add_file_to_repo(cls, repo: git.Repo, path_file: str) -> None:
|
||||
if os.name == "nt":
|
||||
cls.dos2unix(path_file)
|
||||
repo.index.add(path_file)
|
||||
@@ -80,11 +105,11 @@ class IfcGit:
|
||||
bpy.ops.ifcgit.refresh()
|
||||
|
||||
@classmethod
|
||||
def git_checkout(cls, path_file):
|
||||
def git_checkout(cls, path_file: str) -> None:
|
||||
IfcGitRepo.repo.git.checkout(path_file)
|
||||
|
||||
@classmethod
|
||||
def checkout_new_branch(cls, path_file):
|
||||
def checkout_new_branch(cls, path_file: str) -> None:
|
||||
"""Create a branch and move uncommitted changes to this branch"""
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
if props.new_branch_name:
|
||||
@@ -94,7 +119,7 @@ class IfcGit:
|
||||
bpy.ops.ifcgit.refresh()
|
||||
|
||||
@classmethod
|
||||
def git_commit(cls, path_file):
|
||||
def git_commit(cls, path_file: str) -> None:
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
repo = IfcGitRepo.repo
|
||||
if os.name == "nt":
|
||||
@@ -104,7 +129,7 @@ class IfcGit:
|
||||
props.commit_message = ""
|
||||
|
||||
@classmethod
|
||||
def add_tag(cls, repo):
|
||||
def add_tag(cls, repo: git.Repo) -> None:
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
item = props.ifcgit_commits[props.commit_index]
|
||||
repo.create_tag(props.new_tag_name, ref=item.hexsha, message=props.new_tag_message)
|
||||
@@ -112,19 +137,19 @@ class IfcGit:
|
||||
props.new_tag_message = ""
|
||||
|
||||
@classmethod
|
||||
def delete_tag(cls, repo, tag_name):
|
||||
def delete_tag(cls, repo: git.Repo, tag_name: git.TagReference) -> None:
|
||||
if tag_name in repo.tags:
|
||||
repo.delete_tag(tag_name)
|
||||
|
||||
@classmethod
|
||||
def add_remote(cls, repo):
|
||||
def add_remote(cls, repo: git.Repo) -> None:
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
repo.create_remote(name=props.remote_name, url=props.remote_url)
|
||||
props.remote_name = ""
|
||||
props.remote_url = ""
|
||||
|
||||
@classmethod
|
||||
def delete_remote(cls, repo):
|
||||
def delete_remote(cls, repo: git.Repo) -> None:
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
remote_name = props.select_remote
|
||||
if remote_name in repo.remotes:
|
||||
@@ -133,7 +158,7 @@ class IfcGit:
|
||||
props.select_remote = repo.remotes[0].name
|
||||
|
||||
@classmethod
|
||||
def push(cls, repo, remote_name, branch_name):
|
||||
def push(cls, repo: git.Repo, remote_name: str, branch_name: str) -> Union[str, None]:
|
||||
cls.config_push(repo)
|
||||
remote = repo.remotes[remote_name]
|
||||
try:
|
||||
@@ -142,7 +167,7 @@ class IfcGit:
|
||||
return exc.stderr
|
||||
|
||||
@classmethod
|
||||
def create_new_branch(cls):
|
||||
def create_new_branch(cls) -> None:
|
||||
"""Convert a detached HEAD into a branch"""
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
repo = IfcGitRepo.repo
|
||||
@@ -154,7 +179,7 @@ class IfcGit:
|
||||
bpy.ops.ifcgit.refresh()
|
||||
|
||||
@classmethod
|
||||
def clear_commits_list(cls):
|
||||
def clear_commits_list(cls) -> None:
|
||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||
area.spaces[0].shading.color_type = "MATERIAL"
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
@@ -163,7 +188,7 @@ class IfcGit:
|
||||
props.ifcgit_commits.clear()
|
||||
|
||||
@classmethod
|
||||
def get_commits_list(cls, path_ifc, lookup):
|
||||
def get_commits_list(cls, path_ifc: str, lookup: dict[str, Any]) -> None:
|
||||
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
repo = cls.repo_from_path(path_ifc)
|
||||
@@ -205,14 +230,14 @@ class IfcGit:
|
||||
list_item.tags[-1].message = tag.tag.message
|
||||
|
||||
@classmethod
|
||||
def refresh_revision_list(cls, path_ifc):
|
||||
def refresh_revision_list(cls, path_ifc: str) -> None:
|
||||
repo = cls.repo_from_path(path_ifc)
|
||||
cls.clear_commits_list()
|
||||
lookup = cls.tags_by_hexsha(repo)
|
||||
cls.get_commits_list(path_ifc, lookup)
|
||||
|
||||
@classmethod
|
||||
def is_valid_ref_format(cls, string):
|
||||
def is_valid_ref_format(cls, string: str) -> Union[re.Match[str], None]:
|
||||
"""Check a bare branch or tag name is valid"""
|
||||
|
||||
return re.match(
|
||||
@@ -221,7 +246,7 @@ class IfcGit:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def load_project(cls, path_ifc=""):
|
||||
def load_project(cls, path_ifc: str = "") -> None:
|
||||
"""Clear and load an ifc project"""
|
||||
|
||||
if path_ifc:
|
||||
@@ -248,7 +273,7 @@ class IfcGit:
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
|
||||
@classmethod
|
||||
def branches_by_hexsha(cls, repo):
|
||||
def branches_by_hexsha(cls, repo: git.Repo) -> dict[str, Any]:
|
||||
"""reverse lookup for branches"""
|
||||
|
||||
result = {}
|
||||
@@ -267,7 +292,7 @@ class IfcGit:
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def tags_by_hexsha(cls, repo):
|
||||
def tags_by_hexsha(cls, repo: git.Repo) -> dict[str, Any]:
|
||||
"""reverse lookup for tags"""
|
||||
|
||||
result = {}
|
||||
@@ -279,7 +304,7 @@ class IfcGit:
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def ifc_diff_ids(cls, repo, hash_a, hash_b, path_ifc):
|
||||
def ifc_diff_ids(cls, repo: git.Repo, hash_a: str, hash_b: str, path_ifc: str) -> STEP_IDS:
|
||||
"""Given two revision hashes and a filename, retrieve"""
|
||||
"""step-ids of modified, added and removed entities"""
|
||||
|
||||
@@ -309,7 +334,7 @@ class IfcGit:
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_revisions_step_ids(cls):
|
||||
def get_revisions_step_ids(cls) -> Union[STEP_IDS, None]:
|
||||
|
||||
path_ifc = bpy.data.scenes["Scene"].BIMProperties.ifc_file
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
@@ -341,7 +366,7 @@ class IfcGit:
|
||||
return step_ids
|
||||
|
||||
@classmethod
|
||||
def get_modified_shape_object_step_ids(cls, step_ids):
|
||||
def get_modified_shape_object_step_ids(cls, step_ids: STEP_IDS) -> STEP_IDS:
|
||||
model = tool.Ifc.get()
|
||||
modified_shape_object_step_ids = {"modified": []}
|
||||
|
||||
@@ -353,7 +378,7 @@ class IfcGit:
|
||||
return modified_shape_object_step_ids
|
||||
|
||||
@classmethod
|
||||
def update_step_ids(cls, step_ids, modified_shape_object_step_ids):
|
||||
def update_step_ids(cls, step_ids: STEP_IDS, modified_shape_object_step_ids: STEP_IDS) -> STEP_IDS:
|
||||
|
||||
final_step_ids = {}
|
||||
final_step_ids["added"] = step_ids["added"]
|
||||
@@ -362,7 +387,7 @@ class IfcGit:
|
||||
return final_step_ids
|
||||
|
||||
@classmethod
|
||||
def colourise(cls, step_ids):
|
||||
def colourise(cls, step_ids: STEP_IDS) -> None:
|
||||
area = next(area for area in bpy.context.screen.areas if area.type == "VIEW_3D")
|
||||
area.spaces[0].shading.color_type = "OBJECT"
|
||||
bpy.ops.object.select_all(action="DESELECT")
|
||||
@@ -384,7 +409,7 @@ class IfcGit:
|
||||
obj.color = (1.0, 1.0, 1.0, 0.5)
|
||||
|
||||
@classmethod
|
||||
def switch_to_revision_item(cls):
|
||||
def switch_to_revision_item(cls) -> None:
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
repo = IfcGitRepo.repo
|
||||
item = props.ifcgit_commits[props.commit_index]
|
||||
@@ -399,13 +424,13 @@ class IfcGit:
|
||||
repo.git.checkout(item.hexsha)
|
||||
|
||||
@classmethod
|
||||
def delete_collection(cls, blender_collection):
|
||||
def delete_collection(cls, blender_collection: bpy.types.Collection) -> None:
|
||||
for obj in blender_collection.objects:
|
||||
bpy.data.objects.remove(obj, do_unlink=True)
|
||||
bpy.data.collections.remove(blender_collection)
|
||||
|
||||
@classmethod
|
||||
def is_valid_branch_name(cls, new_branch_name):
|
||||
def is_valid_branch_name(cls, new_branch_name: str):
|
||||
"""Check if a branch name is valid and doesn't conflict with existing branches"""
|
||||
if not cls.is_valid_ref_format(new_branch_name):
|
||||
return False
|
||||
@@ -414,7 +439,7 @@ class IfcGit:
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def config_ifcmerge(cls):
|
||||
def config_ifcmerge(cls) -> None:
|
||||
config_reader = IfcGitRepo.repo.config_reader()
|
||||
section = 'mergetool "ifcmerge"'
|
||||
if not config_reader.has_section(section):
|
||||
@@ -428,7 +453,7 @@ class IfcGit:
|
||||
config_writer.set_value(section, "trustExitCode", True)
|
||||
|
||||
@classmethod
|
||||
def config_push(cls, repo):
|
||||
def config_push(cls, repo: git.Repo) -> None:
|
||||
"""Set push.autoSetupRemote"""
|
||||
config_reader = repo.config_reader()
|
||||
if not config_reader.has_section("push"):
|
||||
@@ -437,7 +462,7 @@ class IfcGit:
|
||||
config_writer.set_value("push", "autoSetupRemote", True)
|
||||
|
||||
@classmethod
|
||||
def config_info_attributes(cls, repo):
|
||||
def config_info_attributes(cls, repo: git.Repo) -> None:
|
||||
"""Set IFC files as text in .git/info/attributes"""
|
||||
path_attributes = os.path.join(repo.git_dir, "info", "attributes")
|
||||
if not os.path.exists(path_attributes):
|
||||
@@ -446,7 +471,7 @@ class IfcGit:
|
||||
f.write("*.ifc text")
|
||||
|
||||
@classmethod
|
||||
def dos2unix(cls, path_file):
|
||||
def dos2unix(cls, path_file: str) -> None:
|
||||
with open(path_file, "rb") as infile:
|
||||
content = infile.read()
|
||||
with open(path_file, "wb") as output:
|
||||
@@ -454,7 +479,7 @@ class IfcGit:
|
||||
output.write(line + b"\n")
|
||||
|
||||
@classmethod
|
||||
def execute_merge(cls, path_ifc, operator):
|
||||
def execute_merge(cls, path_ifc: str, operator: bpy.types.Operator) -> Union[None, False]:
|
||||
props = bpy.context.scene.IfcGitProperties
|
||||
repo = IfcGitRepo.repo
|
||||
item = props.ifcgit_commits[props.commit_index]
|
||||
@@ -498,7 +523,7 @@ class IfcGit:
|
||||
cls.refresh_revision_list(path_ifc)
|
||||
|
||||
@classmethod
|
||||
def entity_log(cls, path_ifc, step_id):
|
||||
def entity_log(cls, path_ifc: str, step_id: int) -> str:
|
||||
"""Raw git log for this entity"""
|
||||
repo = IfcGitRepo.repo
|
||||
if not repo:
|
||||
@@ -514,4 +539,4 @@ class IfcGit:
|
||||
|
||||
|
||||
class IfcGitRepo:
|
||||
repo = None
|
||||
repo: git.Repo = None
|
||||
|
||||
@@ -392,10 +392,11 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
continue
|
||||
|
||||
old_mesh = obj.data
|
||||
assert isinstance(old_mesh, bpy.types.Mesh)
|
||||
if visible_element.HasOpenings:
|
||||
new_mesh = cls.get_gross_mesh_from_element(visible_element)
|
||||
else:
|
||||
new_mesh = obj.data.copy()
|
||||
new_mesh = old_mesh.copy()
|
||||
obj.data = new_mesh
|
||||
|
||||
# Boundary objects are likely triangulated. If a triangulated quad
|
||||
@@ -463,15 +464,15 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
return mesh
|
||||
|
||||
@classmethod
|
||||
def get_x_y_z_h_mat_from_active_obj(cls, active_obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]:
|
||||
mat = active_obj.matrix_world
|
||||
local_bbox_center = 0.125 * sum((Vector(b) for b in active_obj.bound_box), Vector())
|
||||
def get_x_y_z_h_mat_from_obj(cls, obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]:
|
||||
mat = obj.matrix_world
|
||||
local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector())
|
||||
global_bbox_center = mat @ local_bbox_center
|
||||
x = global_bbox_center.x
|
||||
y = global_bbox_center.y
|
||||
z = (mat @ Vector(active_obj.bound_box[0])).z
|
||||
z = (mat @ Vector(obj.bound_box[0])).z
|
||||
|
||||
h = active_obj.dimensions.z
|
||||
h = obj.dimensions.z
|
||||
return x, y, z, h, mat
|
||||
|
||||
@classmethod
|
||||
@@ -490,7 +491,11 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
boundary_elements = cls.get_boundary_elements(selected_objects)
|
||||
polys = cls.get_polygons(boundary_elements)
|
||||
converted_tolerance = cls.get_converted_tolerance(tolerance=0.03)
|
||||
union = shapely.ops.unary_union(polys).buffer(converted_tolerance, cap_style=2, join_style=2)
|
||||
union = shapely.ops.unary_union(polys).buffer(
|
||||
converted_tolerance,
|
||||
cap_style=shapely.constructive.BufferCapStyle.flat,
|
||||
join_style=shapely.constructive.BufferJoinStyle.mitre,
|
||||
)
|
||||
union = cls.get_purged_inner_holes_poly(union_geom=union, min_area=cls.get_converted_tolerance(tolerance=0.1))
|
||||
return union
|
||||
|
||||
@@ -576,7 +581,12 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
def get_buffered_poly_from_linear_ring(cls, linear_ring: shapely.LinearRing) -> Polygon:
|
||||
poly = Polygon(linear_ring)
|
||||
converted_tolerance = cls.get_converted_tolerance(tolerance=0.03)
|
||||
poly = poly.buffer(converted_tolerance, single_sided=True, cap_style=2, join_style=2)
|
||||
poly = poly.buffer(
|
||||
converted_tolerance,
|
||||
single_sided=True,
|
||||
cap_style=shapely.BufferCapStyle.flat,
|
||||
join_style=shapely.BufferJoinStyle.mitre,
|
||||
)
|
||||
return poly
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -17,14 +17,15 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
from typing import Optional, Literal
|
||||
import ifcopenshell.util.representation
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def add_context(
|
||||
file: ifcopenshell.file,
|
||||
context_type: Optional[Literal["Model", "Plan"]] = None,
|
||||
context_identifier: Optional[str] = None,
|
||||
target_view: Optional[str] = None,
|
||||
context_type: Optional[ifcopenshell.util.representation.CONTEXT_TYPE] = None,
|
||||
context_identifier: Optional[ifcopenshell.util.representation.REPRESENTATION_IDENTIFIER] = None,
|
||||
target_view: Optional[ifcopenshell.util.representation.TARGET_VIEW] = None,
|
||||
parent: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Adds a new geometric representation context
|
||||
@@ -106,7 +107,6 @@ def add_context(
|
||||
the common target views above or consult the IFC documentation
|
||||
(under the IfcShapeRepresentation page) for more details. Optional
|
||||
for contexts, but mandatory for subcontexts.
|
||||
:type target_view: str, optional
|
||||
:param parent: the parent context. Must be left as None (the default)
|
||||
for contexts, and only set for subcontexts. Note that there are only
|
||||
contexts and subcontexts, a subcontext cannot have any children.
|
||||
|
||||
@@ -26,6 +26,7 @@ import typing
|
||||
import inspect
|
||||
import collections
|
||||
import importlib
|
||||
import importlib.util
|
||||
from typing import Union
|
||||
|
||||
|
||||
|
||||
@@ -18,8 +18,14 @@
|
||||
|
||||
import ifcpatch
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.context
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.api.georeference
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.geolocation
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.shape
|
||||
import ifcopenshell.util.shape_builder
|
||||
import test.bootstrap
|
||||
import tempfile
|
||||
|
||||
Reference in New Issue
Block a user