diff --git a/src/blenderbim/blenderbim/bim/module/model/covering.py b/src/blenderbim/blenderbim/bim/module/model/covering.py
index bca60ed16c..c9de0b8ccc 100644
--- a/src/blenderbim/blenderbim/bim/module/model/covering.py
+++ b/src/blenderbim/blenderbim/bim/module/model/covering.py
@@ -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"
diff --git a/src/blenderbim/blenderbim/core/covering.py b/src/blenderbim/blenderbim/core/covering.py
index fd85fa4aed..123da50899 100644
--- a/src/blenderbim/blenderbim/core/covering.py
+++ b/src/blenderbim/blenderbim/core/covering.py
@@ -16,8 +16,16 @@
# You should have received a copy of the GNU General Public License
# along with BlenderBIM Add-on. If not, see .
+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()
diff --git a/src/blenderbim/blenderbim/core/drawing.py b/src/blenderbim/blenderbim/core/drawing.py
index f7e3c23d55..a8c19fa42a 100644
--- a/src/blenderbim/blenderbim/core/drawing.py
+++ b/src/blenderbim/blenderbim/core/drawing.py
@@ -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)
diff --git a/src/blenderbim/blenderbim/core/spatial.py b/src/blenderbim/blenderbim/core/spatial.py
index e59aa042d0..7ff8457309 100644
--- a/src/blenderbim/blenderbim/core/spatial.py
+++ b/src/blenderbim/blenderbim/core/spatial.py
@@ -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()
diff --git a/src/blenderbim/blenderbim/core/tool.py b/src/blenderbim/blenderbim/core/tool.py
index 2455f74ae1..261029e27f 100644
--- a/src/blenderbim/blenderbim/core/tool.py
+++ b/src/blenderbim/blenderbim/core/tool.py
@@ -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
diff --git a/src/blenderbim/blenderbim/tool/covering.py b/src/blenderbim/blenderbim/tool/covering.py
index 5268a5b1e7..7a76d0d4be 100644
--- a/src/blenderbim/blenderbim/tool/covering.py
+++ b/src/blenderbim/blenderbim/tool/covering.py
@@ -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
diff --git a/src/blenderbim/blenderbim/tool/drawing.py b/src/blenderbim/blenderbim/tool/drawing.py
index ea78066650..b8d18d689f 100644
--- a/src/blenderbim/blenderbim/tool/drawing.py
+++ b/src/blenderbim/blenderbim/tool/drawing.py
@@ -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:
diff --git a/src/blenderbim/blenderbim/tool/georeference.py b/src/blenderbim/blenderbim/tool/georeference.py
index 3c9bb0eea1..577a173259 100644
--- a/src/blenderbim/blenderbim/tool/georeference.py
+++ b/src/blenderbim/blenderbim/tool/georeference.py
@@ -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
diff --git a/src/blenderbim/blenderbim/tool/ifcgit.py b/src/blenderbim/blenderbim/tool/ifcgit.py
index f1a4eb86c3..81b397964a 100644
--- a/src/blenderbim/blenderbim/tool/ifcgit.py
+++ b/src/blenderbim/blenderbim/tool/ifcgit.py
@@ -1,3 +1,22 @@
+# BlenderBIM Add-on - OpenBIM Blender Add-on
+# Copyright (C) 2021 Dion Moult
+#
+# 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 .
+
+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
diff --git a/src/blenderbim/blenderbim/tool/spatial.py b/src/blenderbim/blenderbim/tool/spatial.py
index 21b417acdb..f7bffc8fbd 100644
--- a/src/blenderbim/blenderbim/tool/spatial.py
+++ b/src/blenderbim/blenderbim/tool/spatial.py
@@ -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
diff --git a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py
index bae2c32ea8..fcf8799707 100644
--- a/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py
+++ b/src/ifcopenshell-python/ifcopenshell/api/context/add_context.py
@@ -17,14 +17,15 @@
# along with IfcOpenShell. If not, see .
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.
diff --git a/src/ifcpatch/ifcpatch/__init__.py b/src/ifcpatch/ifcpatch/__init__.py
index 19eae9790e..f5cb2902b0 100644
--- a/src/ifcpatch/ifcpatch/__init__.py
+++ b/src/ifcpatch/ifcpatch/__init__.py
@@ -26,6 +26,7 @@ import typing
import inspect
import collections
import importlib
+import importlib.util
from typing import Union
diff --git a/src/ifcpatch/test/test_MergeProject.py b/src/ifcpatch/test/test_MergeProject.py
index c190a4d05a..3c4e84c4f2 100644
--- a/src/ifcpatch/test/test_MergeProject.py
+++ b/src/ifcpatch/test/test_MergeProject.py
@@ -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