mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
typing
This commit is contained in:
@@ -132,7 +132,8 @@ class IfcExporter:
|
||||
bpy.ops.bim.update_representation(obj=obj.name)
|
||||
|
||||
def has_changed_materials(self, obj: bpy.types.Object) -> bool:
|
||||
checksum = obj.data.BIMMeshProperties.material_checksum
|
||||
mprops = tool.Geometry.get_mesh_props(obj.data)
|
||||
checksum = mprops.material_checksum
|
||||
return checksum != tool.Geometry.get_material_checksum(obj)
|
||||
|
||||
def sync_object_placement(self, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
|
||||
@@ -221,7 +221,8 @@ def refresh_ui_data():
|
||||
if isinstance(tool.Ifc.get(), ifcopenshell.sqlite):
|
||||
tool.Ifc.get().clear_cache()
|
||||
|
||||
bpy.context.scene.DocProperties.should_draw_decorations = bpy.context.scene.DocProperties.should_draw_decorations
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.should_draw_decorations = props.should_draw_decorations
|
||||
if bpy.context.scene.WebProperties.is_connected:
|
||||
tool.Web.send_webui_data()
|
||||
|
||||
@@ -343,7 +344,7 @@ def load_post(scene):
|
||||
bpy.context.scene.BIMProperties.has_blend_warning = True
|
||||
|
||||
# Bonsai overlays
|
||||
georeference_props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
georeference_props = tool.Georeference.get_georeference_props()
|
||||
aggregate_props = bpy.context.scene.BIMAggregateProperties
|
||||
nest_props = bpy.context.scene.BIMNestProperties
|
||||
model_props = tool.Model.get_model_props()
|
||||
|
||||
@@ -178,6 +178,7 @@ class IfcStore:
|
||||
if not os.path.isfile(path):
|
||||
return
|
||||
extension = path.split(".")[-1]
|
||||
props = tool.Project.get_project_props()
|
||||
if extension.lower() == "ifczip":
|
||||
with tempfile.TemporaryDirectory() as unzipped_path:
|
||||
with zipfile.ZipFile(path, "r") as zip_ref:
|
||||
@@ -187,7 +188,7 @@ class IfcStore:
|
||||
return
|
||||
elif extension.lower() == "ifcxml":
|
||||
IfcStore.file = ifcopenshell.file(ifcopenshell.ifcopenshell_wrapper.parse_ifcxml(path))
|
||||
elif bpy.context.scene.BIMProjectProperties.should_stream:
|
||||
elif props.should_stream:
|
||||
IfcStore.file = ifcopenshell.open(path, should_stream=True)
|
||||
else:
|
||||
IfcStore.file = ifcopenshell.open(path)
|
||||
@@ -195,9 +196,8 @@ class IfcStore:
|
||||
@staticmethod
|
||||
def get_schema() -> ifcopenshell.ifcopenshell_wrapper.schema_definition:
|
||||
if IfcStore.file is None:
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(
|
||||
bpy.context.scene.BIMProjectProperties.export_schema
|
||||
)
|
||||
props = tool.Project.get_project_props()
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(props.export_schema)
|
||||
elif IfcStore.schema is None:
|
||||
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(IfcStore.file.schema_identifier)
|
||||
return IfcStore.schema
|
||||
@@ -247,7 +247,7 @@ class IfcStore:
|
||||
# refactor this class and deprecate usage of IfcStore in favour of
|
||||
# tools.
|
||||
if not isinstance(obj, (bpy.types.Object, bpy.types.Material)):
|
||||
obj.BIMMeshProperties.ifc_definition_id = element.id()
|
||||
tool.Geometry.get_mesh_props(obj).ifc_definition_id = element.id()
|
||||
return
|
||||
|
||||
existing_obj = IfcStore.id_map.get(element.id(), None)
|
||||
|
||||
@@ -90,7 +90,8 @@ class MaterialCreator:
|
||||
return # Already has materials assign to the representation itself
|
||||
# Otherwise, we need to check for material styles on the element, since
|
||||
# create_shape on types only works on representations.
|
||||
context = tool.Ifc.get().by_id(self.mesh.BIMMeshProperties.ifc_definition_id).ContextOfItems
|
||||
mprops = tool.Geometry.get_mesh_props(self.mesh)
|
||||
context = tool.Ifc.get().by_id(mprops.ifc_definition_id).ContextOfItems
|
||||
for material in ifcopenshell.util.element.get_materials(element):
|
||||
if style := ifcopenshell.util.representation.get_material_style(material, context):
|
||||
self.mesh["ios_materials"] = (style.id(),)
|
||||
@@ -218,7 +219,7 @@ class IfcImporter:
|
||||
self.progress = 0
|
||||
|
||||
self.material_creator = MaterialCreator(ifc_import_settings, self)
|
||||
classes_to_wireframe_str = bpy.context.scene.DocProperties.classes_to_wireframe
|
||||
classes_to_wireframe_str = tool.Drawing.get_document_props().classes_to_wireframe
|
||||
self.classes_to_wireframe_list = [word.strip() for word in classes_to_wireframe_str.split(",")]
|
||||
|
||||
def profile_code(self, message: str) -> None:
|
||||
@@ -434,7 +435,7 @@ class IfcImporter:
|
||||
return False
|
||||
|
||||
def calculate_model_offset(self) -> None:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if self.ifc_import_settings.false_origin_mode == "MANUAL":
|
||||
tool.Loader.set_manual_blender_offset(self.file)
|
||||
elif self.ifc_import_settings.false_origin_mode == "AUTOMATIC":
|
||||
|
||||
@@ -1316,7 +1316,7 @@ class ActivateBcfViewpoint(bpy.types.Operator):
|
||||
[0, 0, 0, 1],
|
||||
)
|
||||
)
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||
matrix = ifcopenshell.util.geolocation.global2local(
|
||||
|
||||
@@ -114,7 +114,7 @@ class Loader:
|
||||
bm.edges.new((verts[-1], verts[0]))
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
mesh.BIMMeshProperties.ifc_definition_id = surface.id()
|
||||
tool.Ifc.link(surface, mesh)
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
matrix = mathutils.Matrix(
|
||||
ifcopenshell.util.placement.get_axis2placement(surface.BasisSurface.Position).tolist()
|
||||
|
||||
@@ -69,10 +69,12 @@ class CadTool(WorkSpaceTool):
|
||||
("bim.cad_hotkey", {"type": "X", "value": "PRESS", "shift": True}, {"properties": [("hotkey", "S_X")]}),
|
||||
)
|
||||
|
||||
def draw_settings(context, layout, workspace_tool):
|
||||
def draw_settings(
|
||||
context: bpy.types.Context, layout: bpy.types.UILayout, workspace_tool: bpy.types.WorkSpaceTool
|
||||
) -> None:
|
||||
ui_context = str(context.region.type)
|
||||
obj = context.active_object
|
||||
if not obj or not obj.data:
|
||||
if not obj or not (data := obj.data):
|
||||
return
|
||||
is_profile = tool.Geometry.is_profile_object_active()
|
||||
if is_profile:
|
||||
@@ -122,7 +124,10 @@ class CadTool(WorkSpaceTool):
|
||||
row = row if ui_context == "TOOL_HEADER" else layout.row(align=True)
|
||||
add_layout_hotkey_operator(row, "Reset Vertex", "S_X", bpy.ops.bim.reset_vertex.__doc__, ui_context)
|
||||
|
||||
elif hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.subshape_type == "AXIS":
|
||||
elif (
|
||||
isinstance(data, tool.Geometry.TYPES_WITH_MESH_PROPERTIES)
|
||||
and tool.Geometry.get_mesh_props(data).subshape_type == "AXIS"
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout, "Edit Axis", "bim.edit_extrusion_axis", "bim.disable_editing_extrusion_axis", ui_context
|
||||
)
|
||||
@@ -141,7 +146,7 @@ class CadTool(WorkSpaceTool):
|
||||
if (
|
||||
(RailingData.is_loaded or not RailingData.load())
|
||||
and RailingData.data["pset_data"]
|
||||
and context.active_object.BIMRailingProperties.is_editing_path
|
||||
and obj.BIMRailingProperties.is_editing_path
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout,
|
||||
@@ -154,7 +159,7 @@ class CadTool(WorkSpaceTool):
|
||||
elif (
|
||||
(RoofData.is_loaded or not RoofData.load())
|
||||
and RoofData.data["pset_data"]
|
||||
and context.active_object.BIMRoofProperties.is_editing_path
|
||||
and obj.BIMRoofProperties.is_editing_path
|
||||
):
|
||||
add_header_apply_button(
|
||||
layout, "Edit Roof Path", "bim.finish_editing_roof_path", "bim.cancel_editing_roof_path", ui_context
|
||||
@@ -252,15 +257,27 @@ class CadHotkey(bpy.types.Operator):
|
||||
bpy.ops.bim.cad_offset(distance=self.props.distance / si_conversion)
|
||||
|
||||
def hotkey_S_Q(self):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if bpy.context.active_object.data.BIMMeshProperties.subshape_type == "PROFILE":
|
||||
obj = bpy.context.active_object
|
||||
|
||||
if not obj:
|
||||
return
|
||||
|
||||
if not tool.Geometry.has_mesh_properties(data := obj.data):
|
||||
return
|
||||
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
if mprops.subshape_type == "PROFILE":
|
||||
if element.is_a("IfcProfileDef"):
|
||||
bpy.ops.bim.edit_arbitrary_profile()
|
||||
elif element.is_a("IfcRelSpaceBoundary"):
|
||||
bpy.ops.bim.edit_boundary_geometry()
|
||||
else:
|
||||
bpy.ops.bim.edit_extrusion_profile()
|
||||
elif bpy.context.active_object.data.BIMMeshProperties.subshape_type == "AXIS":
|
||||
elif mprops.subshape_type == "AXIS":
|
||||
bpy.ops.bim.edit_extrusion_axis()
|
||||
|
||||
def hotkey_S_R(self):
|
||||
|
||||
@@ -201,7 +201,8 @@ class EnableEditingClassification(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
def callback(name, prop, data):
|
||||
if name == "ReferenceTokens":
|
||||
new = bpy.context.scene.BIMGeoreferenceProperties.projected_crs.add()
|
||||
geo_props = tool.Georeference.get_georeference_props()
|
||||
new = geo_props.projected_crs.add()
|
||||
new.name = name
|
||||
new.data_type = "string"
|
||||
new.is_null = data[name] is None
|
||||
|
||||
@@ -101,13 +101,13 @@ class ConvertToBlender(bpy.types.Operator):
|
||||
if tool.Geometry.has_mesh_properties(data):
|
||||
if data.library:
|
||||
continue
|
||||
data.BIMMeshProperties.ifc_definition_id = 0
|
||||
tool.Geometry.get_mesh_props(data).ifc_definition_id = 0
|
||||
for material in bpy.data.materials:
|
||||
if material.library:
|
||||
continue
|
||||
tool.Ifc.unlink(obj=material)
|
||||
context.scene.BIMProperties.ifc_file = ""
|
||||
context.scene.BIMDebugProperties.attributes.clear()
|
||||
tool.Debug.get_debug_props().attributes.clear()
|
||||
IfcStore.purge()
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
@@ -256,7 +256,7 @@ class CreateShapeFromStepId(bpy.types.Operator):
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(context, IfcStore.path, logger)
|
||||
self.file = tool.Ifc.get()
|
||||
element = self.file.by_id(self.step_id or int(context.scene.BIMDebugProperties.step_id))
|
||||
element = self.file.by_id(self.step_id or int(tool.Debug.get_debug_props().step_id))
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set("keep-bounding-boxes", True)
|
||||
if self.should_include_curves:
|
||||
@@ -309,7 +309,7 @@ class RewindInspector(bpy.types.Operator):
|
||||
bl_description = "Rewind the Inspector to the previously inspected element"
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
total_breadcrumbs = len(props.step_id_breadcrumb)
|
||||
if total_breadcrumbs < 2:
|
||||
return {"FINISHED"}
|
||||
@@ -332,9 +332,9 @@ class InspectFromStepId(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
debug_props = context.scene.BIMDebugProperties
|
||||
debug_props = tool.Debug.get_debug_props()
|
||||
debug_props.active_step_id = self.step_id
|
||||
crumb = context.scene.BIMDebugProperties.step_id_breadcrumb.add()
|
||||
crumb = debug_props.step_id_breadcrumb.add()
|
||||
crumb.name = str(self.step_id)
|
||||
element = self.file.by_id(self.step_id)
|
||||
debug_props.attributes.clear()
|
||||
@@ -385,7 +385,7 @@ class InspectFromObject(bpy.types.Operator):
|
||||
if (
|
||||
(data := obj.data)
|
||||
and tool.Geometry.has_mesh_properties(data)
|
||||
and (ifc_id := data.BIMMeshProperties.ifc_definition_id)
|
||||
and (ifc_id := tool.Geometry.get_mesh_props(data).ifc_definition_id)
|
||||
):
|
||||
return ifc_id
|
||||
|
||||
@@ -438,7 +438,8 @@ class ParseExpress(bpy.types.Operator):
|
||||
bl_label = "Parse Express"
|
||||
|
||||
def execute(self, context):
|
||||
core.parse_express(tool.Debug, context.scene.BIMDebugProperties.express_file)
|
||||
props = tool.Debug.get_debug_props()
|
||||
core.parse_express(tool.Debug, props.express_file)
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -452,8 +453,9 @@ class SelectExpressFile(bpy.types.Operator):
|
||||
filter_glob: bpy.props.StringProperty(default="*.exp", options={"HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
props = tool.Debug.get_debug_props()
|
||||
if os.path.exists(self.filepath) and "exp" in os.path.splitext(self.filepath)[1]:
|
||||
context.scene.BIMDebugProperties.express_file = self.filepath
|
||||
props.express_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -497,7 +499,7 @@ class PrintUnusedElementStats(bpy.types.Operator):
|
||||
ignore_styled_items: bpy.props.BoolProperty(name="Ignore Styled Items", default=True)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
# ignore some classes that could have zero 0 inverse references by their nature
|
||||
ignore_classes = []
|
||||
if self.ignore_contexts:
|
||||
@@ -543,7 +545,7 @@ class PurgeUnusedElementsByClass(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
if props.ifc_class_purge:
|
||||
purged_elements = core.purge_unused_elements(tool.Ifc, tool.Debug, props.ifc_class_purge)
|
||||
self.report({"INFO"}, f"{purged_elements} unused elements found and removed.")
|
||||
@@ -803,7 +805,7 @@ class DebugActiveDrawing(bpy.types.Operator):
|
||||
)
|
||||
|
||||
def execute(self, context: bpy.types.Context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_item = props.drawings[props.active_drawing_index]
|
||||
drawing = tool.Ifc.get().by_id(drawing_item.ifc_definition_id)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
from bonsai.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -28,6 +29,9 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Literal, get_args
|
||||
|
||||
DisplayType = Literal["BOUNDS", "WIRE", "SOLID", "TEXTURED"]
|
||||
|
||||
|
||||
class BIMDebugProperties(PropertyGroup):
|
||||
@@ -41,14 +45,23 @@ class BIMDebugProperties(PropertyGroup):
|
||||
inverse_references: CollectionProperty(name="Inverse References", type=Attribute)
|
||||
express_file: StringProperty(name="Express File")
|
||||
display_type: EnumProperty(
|
||||
items=[
|
||||
("BOUNDS", "Bounds", ""),
|
||||
("WIRE", "Wire", ""),
|
||||
("SOLID", "Solid", ""),
|
||||
("TEXTURED", "Textured", ""),
|
||||
],
|
||||
items=[(display_type, display_type.capitalize(), "") for display_type in get_args(DisplayType)],
|
||||
name="Display Type",
|
||||
default="BOUNDS",
|
||||
)
|
||||
ifc_class_purge: StringProperty(name="Unused Elements IFC Class", default="")
|
||||
package_name: StringProperty(name="Package Name", default="")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
step_id: int
|
||||
number_of_polygons: int
|
||||
percentile_of_polygons: int
|
||||
active_step_id: int
|
||||
step_id_breadcrumb: bpy.types.bpy_prop_collection_idprop[StrProperty]
|
||||
attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
inverse_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
inverse_references: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
express_file: str
|
||||
display_type: str
|
||||
ifc_class_purge: str
|
||||
package_name: str
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import Panel
|
||||
|
||||
|
||||
@@ -32,7 +33,7 @@ class BIM_PT_debug(Panel):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
props = context.scene.BIMDebugProperties
|
||||
props = tool.Debug.get_debug_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(context.scene.BIMProperties, "ifc_file", text="")
|
||||
@@ -87,27 +88,25 @@ class BIM_PT_debug(Panel):
|
||||
row.prop(props, "step_id", text="")
|
||||
|
||||
row = layout.split(factor=0.7, align=True)
|
||||
row.operator("bim.select_high_polygon_meshes").threshold = context.scene.BIMDebugProperties.number_of_polygons
|
||||
row.operator("bim.select_high_polygon_meshes").threshold = props.number_of_polygons
|
||||
row.prop(props, "number_of_polygons", text="")
|
||||
row = layout.split(factor=0.7, align=True)
|
||||
row.operator("bim.select_highest_polygon_meshes").percentile = (
|
||||
context.scene.BIMDebugProperties.percentile_of_polygons
|
||||
)
|
||||
row.operator("bim.select_highest_polygon_meshes").percentile = props.percentile_of_polygons
|
||||
row.prop(props, "percentile_of_polygons", text="")
|
||||
|
||||
row = layout.split(factor=0.5, align=True)
|
||||
row.prop(props, "display_type", text="")
|
||||
row.operator("bim.override_display_type").display = context.scene.BIMDebugProperties.display_type
|
||||
row.operator("bim.override_display_type").display = props.display_type
|
||||
|
||||
layout.operator("bim.purge_unused_representations")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.BIMDebugProperties, "ifc_class_purge", text="")
|
||||
row.prop(props, "ifc_class_purge", text="")
|
||||
row.operator("bim.purge_unused_elements_by_class", text="Purge Orphaned", icon="TRASH")
|
||||
row.operator("bim.print_unused_elements_stats", text="", icon="INFO")
|
||||
|
||||
if context.active_object and context.active_object.data:
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if context.active_object and (data := context.active_object.data):
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
row = layout.row()
|
||||
row.operator("bim.get_representation_ifc_parameters")
|
||||
for index, ifc_parameter in enumerate(mprops.ifc_parameters):
|
||||
@@ -123,7 +122,7 @@ class BIM_PT_debug(Panel):
|
||||
row.operator("bim.rewind_inspector", icon="FRAME_PREV", text="")
|
||||
row.prop(props, "active_step_id", text="")
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.inspect_from_step_id").step_id = context.scene.BIMDebugProperties.active_step_id
|
||||
row.operator("bim.inspect_from_step_id").step_id = props.active_step_id
|
||||
row.operator("bim.inspect_from_object")
|
||||
|
||||
if props.attributes:
|
||||
|
||||
@@ -88,7 +88,8 @@ class SheetsData:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
titleblocks_dir = ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "TitleblocksDir")
|
||||
if not titleblocks_dir:
|
||||
titleblocks_dir = bpy.context.scene.DocProperties.titleblocks_dir
|
||||
props = tool.Drawing.get_document_props()
|
||||
titleblocks_dir = props.titleblocks_dir
|
||||
titleblocks_dir = tool.Ifc.resolve_uri(titleblocks_dir)
|
||||
if os.path.exists(titleblocks_dir):
|
||||
files.extend([str(f.stem) for f in Path(titleblocks_dir).glob("*.svg")])
|
||||
@@ -120,23 +121,25 @@ class DrawingsData:
|
||||
|
||||
@classmethod
|
||||
def location_hint(cls):
|
||||
if bpy.context.scene.DocProperties.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
results = [("0", "Origin", "")]
|
||||
results.extend(
|
||||
[(str(s.id()), s.Name or "Unnamed", "") for s in tool.Ifc.get().by_type("IfcBuildingStorey")]
|
||||
)
|
||||
return results
|
||||
elif bpy.context.scene.DocProperties.target_view in ["MODEL_VIEW"]:
|
||||
elif props.target_view in ["MODEL_VIEW"]:
|
||||
return [(h.upper(), h, "") for h in ["Orthographic", "Perspective"]]
|
||||
return [(h.upper(), h, "") for h in ["North", "South", "East", "West"]]
|
||||
|
||||
@classmethod
|
||||
def active_drawing_pset_data(cls):
|
||||
ifc_file = tool.Ifc.get()
|
||||
drawing_id = bpy.context.scene.DocProperties.active_drawing_id
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_id = props.active_drawing_id
|
||||
if drawing_id == 0:
|
||||
return {}
|
||||
drawing = ifc_file.by_id(bpy.context.scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(drawing_id)
|
||||
return ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing")
|
||||
|
||||
|
||||
|
||||
@@ -423,7 +423,8 @@ class BaseDecorator:
|
||||
# font_size = 16 <-- this is a good default
|
||||
# TODO: need to synchronize it better with svg
|
||||
|
||||
magic_font_scale = bpy.context.scene.DocProperties.magic_font_scale
|
||||
props = tool.Drawing.get_document_props()
|
||||
magic_font_scale = props.magic_font_scale
|
||||
font_size_px = int(magic_font_scale * mm_to_px) * font_size_mm / 2.5
|
||||
pos = pos - line_no * font_size_px * rotation_matrix[1]
|
||||
|
||||
@@ -2022,7 +2023,8 @@ class DecorationsHandler:
|
||||
for object_type in ("SLOPE_ANGLE", "SLOPE_FRACTION", "SLOPE_PERCENT"):
|
||||
self.decorators[object_type] = self.decorators["FALL"]
|
||||
self.decorators["MULTI_SYMBOL"] = self.decorators["SYMBOL"]
|
||||
if drawing_font := bpy.context.scene.DocProperties.drawing_font:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if drawing_font := props.drawing_font:
|
||||
drawing_font_path = tool.Blender.get_data_dir_path(Path("fonts") / drawing_font)
|
||||
if drawing_font_path.is_file():
|
||||
font_id = blf.load(drawing_font_path.__str__())
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import blf
|
||||
import gpu
|
||||
import bonsai.tool as tool
|
||||
from bpy import types
|
||||
from mathutils import Vector
|
||||
from mathutils import geometry
|
||||
@@ -478,21 +479,25 @@ class ExtrusionWidget(types.GizmoGroup):
|
||||
bl_options = {"3D", "PERSISTENT", "SHOW_MODAL_ALL"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, ctx):
|
||||
obj = ctx.object
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
return (
|
||||
obj
|
||||
and obj.type == "MESH"
|
||||
and obj.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth") is not None
|
||||
and (data := obj.data)
|
||||
and isinstance(data, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(data).ifc_parameters.get("IfcExtrudedAreaSolid/Depth") is not None
|
||||
)
|
||||
|
||||
def setup(self, ctx):
|
||||
target = ctx.object
|
||||
prop = target.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
def setup(self, context: bpy.types.Context) -> None:
|
||||
target = context.object
|
||||
assert target
|
||||
mesh = target.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
prop = tool.Geometry.get_mesh_props(mesh).ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
|
||||
basis = target.matrix_world.normalized()
|
||||
theme = ctx.preferences.themes[0].user_interface
|
||||
scale_value = self.get_scale_value(ctx.scene.unit_settings.system, ctx.scene.unit_settings.length_unit)
|
||||
theme = context.preferences.themes[0].user_interface
|
||||
scale_value = self.get_scale_value(context.scene.unit_settings.system, context.scene.unit_settings.length_unit)
|
||||
|
||||
# setup handle
|
||||
gz = self.handle = self.gizmos.new("BIM_GT_uglydot_3d")
|
||||
@@ -521,23 +526,26 @@ class ExtrusionWidget(types.GizmoGroup):
|
||||
# gz.use_draw_modal = True
|
||||
# gz.target_set_prop('value', target.demo, 'depth')
|
||||
|
||||
def refresh(self, ctx):
|
||||
def refresh(self, context: bpy.types.Context) -> None:
|
||||
"""updating gizmos"""
|
||||
target = ctx.object
|
||||
target = context.active_object
|
||||
basis = target.matrix_world.normalized()
|
||||
self.handle.matrix_basis = basis
|
||||
self.guides.matrix_basis = basis
|
||||
|
||||
def update(self, ctx):
|
||||
def update(self, context: bpy.types.Context) -> None:
|
||||
"""updating object"""
|
||||
bpy.ops.bim.update_parametric_representation()
|
||||
target = ctx.object
|
||||
prop = target.data.BIMMeshProperties.ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
target = context.active_object
|
||||
assert target
|
||||
mesh = target.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
prop = tool.Geometry.get_mesh_props(mesh).ifc_parameters.get("IfcExtrudedAreaSolid/Depth")
|
||||
self.handle.target_set_prop("offset", prop, "value")
|
||||
self.guides.target_set_prop("depth", prop, "value")
|
||||
|
||||
@staticmethod
|
||||
def get_scale_value(system, length_unit):
|
||||
def get_scale_value(system: str, length_unit: str) -> float:
|
||||
scale_value = 1
|
||||
if system == "METRIC":
|
||||
if length_unit == "KILOMETERS":
|
||||
|
||||
@@ -24,7 +24,8 @@ from bpy.app.handlers import persistent
|
||||
|
||||
@persistent
|
||||
def load_post(*args):
|
||||
if bpy.context.scene.DocProperties.should_draw_decorations:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.should_draw_decorations:
|
||||
decoration.DecorationsHandler.install(bpy.context)
|
||||
else:
|
||||
decoration.DecorationsHandler.uninstall()
|
||||
@@ -35,9 +36,11 @@ def depsgraph_update_pre_handler(scene):
|
||||
set_active_camera_resolution(scene)
|
||||
|
||||
|
||||
def set_active_camera_resolution(scene):
|
||||
if not scene.camera or "/" not in scene.camera.name or not scene.DocProperties.drawings:
|
||||
def set_active_camera_resolution(scene: bpy.types.Scene) -> None:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not scene.camera or "/" not in scene.camera.name or not props.drawings:
|
||||
return
|
||||
assert isinstance(scene.camera.data, bpy.types.Camera)
|
||||
props = scene.camera.data.BIMCameraProperties
|
||||
ortho_scale = max((props.width, props.height))
|
||||
aspect_ratio = props.width / props.height
|
||||
@@ -60,5 +63,3 @@ def set_active_camera_resolution(scene):
|
||||
|
||||
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x = int(raster_x)
|
||||
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y = int(raster_y)
|
||||
|
||||
current_drawing = scene.DocProperties.drawings[scene.DocProperties.current_drawing_index]
|
||||
|
||||
@@ -22,6 +22,7 @@ import mathutils.geometry
|
||||
import ifcopenshell
|
||||
import bonsai.tool as tool
|
||||
from mathutils import Vector
|
||||
from typing import Union
|
||||
|
||||
# Code taken and updated from https://blenderartists.org/t/detecting-intersection-of-bounding-boxes/457520/2
|
||||
|
||||
@@ -136,7 +137,9 @@ def format_distance(
|
||||
scaleFactor = bpy.context.scene.unit_settings.scale_length
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
unit_length = bpy.context.scene.unit_settings.length_unit
|
||||
area_unit_symbol = " " + ifcopenshell.util.unit.get_unit_symbol(ifcopenshell.util.unit.get_project_unit(tool.Ifc.get(), "AREAUNIT"))
|
||||
area_unit_symbol = " " + ifcopenshell.util.unit.get_unit_symbol(
|
||||
ifcopenshell.util.unit.get_project_unit(tool.Ifc.get(), "AREAUNIT")
|
||||
)
|
||||
|
||||
value *= scaleFactor
|
||||
|
||||
@@ -326,16 +329,18 @@ def format_distance(
|
||||
fmt += area_unit_symbol
|
||||
d_cm = value * (1000000)
|
||||
tx_dist = fmt % d_cm
|
||||
|
||||
|
||||
else:
|
||||
tx_dist = fmt % value
|
||||
|
||||
return tx_dist
|
||||
|
||||
|
||||
def get_active_drawing(scene):
|
||||
def get_active_drawing(
|
||||
scene: bpy.types.Scene,
|
||||
) -> Union[tuple[bpy.types.Collection, bpy.types.Camera], tuple[None, None]]:
|
||||
"""Get active drawing collection and camera"""
|
||||
props = scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
try:
|
||||
camera = tool.Ifc.get_object(tool.Ifc.get().by_id(props.active_drawing_id))
|
||||
return camera.BIMObjectProperties.collection, camera
|
||||
|
||||
@@ -134,19 +134,19 @@ class AddDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Add a drawing view to the IFC project"
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
hint = self.props.location_hint
|
||||
if self.props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
props = tool.Drawing.get_document_props()
|
||||
hint = props.location_hint
|
||||
if props.target_view in ["PLAN_VIEW", "REFLECTED_PLAN_VIEW"]:
|
||||
hint = int(hint)
|
||||
core.add_drawing(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Drawing,
|
||||
target_view=self.props.target_view,
|
||||
target_view=props.target_view,
|
||||
location_hint=hint,
|
||||
)
|
||||
try:
|
||||
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||
drawing = tool.Ifc.get().by_id(props.active_drawing_id)
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||
except:
|
||||
pass
|
||||
@@ -162,7 +162,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -176,7 +176,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
row.prop(self, "should_duplicate_annotations")
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
core.duplicate_drawing(
|
||||
tool.Ifc,
|
||||
tool.Drawing,
|
||||
@@ -184,7 +184,7 @@ class DuplicateDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
should_duplicate_annotations=self.should_duplicate_annotations,
|
||||
)
|
||||
try:
|
||||
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||
drawing = tool.Ifc.get().by_id(props.active_drawing_id)
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||
except:
|
||||
pass
|
||||
@@ -244,7 +244,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
active_drawing_id = context.scene.camera.BIMObjectProperties.ifc_definition_id
|
||||
if self.print_all:
|
||||
@@ -385,7 +385,7 @@ class CreateDrawing(bpy.types.Operator):
|
||||
obj.hide_render = obj.name not in visible_object_names
|
||||
|
||||
context.scene.render.filepath = str(Path(svg_path).with_suffix(".png"))
|
||||
drawing_style = context.scene.DocProperties.drawing_styles[self.cprops.active_drawing_style_index]
|
||||
drawing_style = self.props.drawing_styles[self.cprops.active_drawing_style_index]
|
||||
|
||||
if drawing_style.render_type == "DEFAULT":
|
||||
bpy.ops.render.render(write_still=True)
|
||||
@@ -714,7 +714,8 @@ class CreateDrawing(bpy.types.Operator):
|
||||
|
||||
files = {context.scene.BIMProperties.ifc_file: tool.Ifc.get()}
|
||||
|
||||
for link in context.scene.BIMProjectProperties.links:
|
||||
props = tool.Project.get_project_props()
|
||||
for link in props.links:
|
||||
if link.name not in IfcStore.session_files:
|
||||
IfcStore.session_files[link.name] = ifcopenshell.open(link.name)
|
||||
files[link.name] = IfcStore.session_files[link.name]
|
||||
@@ -1141,7 +1142,8 @@ class CreateDrawing(bpy.types.Operator):
|
||||
try:
|
||||
return tool.Ifc.get().by_guid(guid)
|
||||
except:
|
||||
for link in bpy.context.scene.BIMProjectProperties.links:
|
||||
props = tool.Project.get_project_props()
|
||||
for link in props.links:
|
||||
if link.name not in IfcStore.session_files:
|
||||
IfcStore.session_files[link.name] = ifcopenshell.open(link.name)
|
||||
try:
|
||||
@@ -1458,7 +1460,8 @@ class AddSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_description = "Add a sheet to the project"
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_sheet(tool.Ifc, tool.Drawing, titleblock=context.scene.DocProperties.titleblock)
|
||||
props = tool.Drawing.get_document_props()
|
||||
core.add_sheet(tool.Ifc, tool.Drawing, titleblock=props.titleblock)
|
||||
|
||||
|
||||
class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -1474,7 +1477,7 @@ class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
cls.poll_message_set("Not implemented yet.")
|
||||
return False
|
||||
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -1483,7 +1486,7 @@ class DuplicateSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
pass
|
||||
"""
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
core.duplicate_sheet(
|
||||
tool.Ifc,
|
||||
tool.Drawing,
|
||||
@@ -1508,7 +1511,7 @@ class OpenLayout(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
sheet_builder = sheeter.SheetBuilder()
|
||||
sheet_builder.update_sheet_drawing_sizes(sheet)
|
||||
@@ -1530,7 +1533,8 @@ class SelectAllSheets(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
for sheet in context.scene.DocProperties.sheets:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in props.sheets:
|
||||
if sheet.is_selected != self.select_all:
|
||||
sheet.is_selected = self.select_all
|
||||
return {"FINISHED"}
|
||||
@@ -1550,7 +1554,7 @@ class OpenSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(is_sheet=True):
|
||||
cls.poll_message_set("No sheet selected.")
|
||||
return False
|
||||
@@ -1564,7 +1568,7 @@ class OpenSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
svg2pdf_command = tool.Blender.get_addon_preferences().svg2pdf_command
|
||||
|
||||
if self.open_all:
|
||||
@@ -1612,7 +1616,7 @@ class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
# Won't be visible in UI anyway.
|
||||
if not props.sheets or not context.scene.BIMProperties.data_dir:
|
||||
return False
|
||||
@@ -1622,8 +1626,8 @@ class AddDrawingToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
active_drawing = tool.Drawing.get_active_drawing_item()
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_drawing = props.drawings[props.active_drawing_index]
|
||||
assert active_drawing
|
||||
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
@@ -1680,7 +1684,7 @@ class RemoveDrawingFromSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_item = tool.Drawing.get_active_sheet_item()
|
||||
if active_item is None:
|
||||
return False
|
||||
@@ -1714,7 +1718,7 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(is_sheet=True):
|
||||
cls.poll_message_set("No sheet selected.")
|
||||
return False
|
||||
@@ -1731,7 +1735,7 @@ class CreateSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
scene = context.scene
|
||||
props = scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
svg2pdf_command = tool.Blender.get_addon_preferences().svg2pdf_command
|
||||
svg2dxf_command = tool.Blender.get_addon_preferences().svg2dxf_command
|
||||
|
||||
@@ -1838,7 +1842,8 @@ class SelectAllDrawings(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
for drawing in context.scene.DocProperties.drawings:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in props.drawings:
|
||||
if drawing.is_selected != self.select_all:
|
||||
drawing.is_selected = self.select_all
|
||||
return {"FINISHED"}
|
||||
@@ -1857,7 +1862,7 @@ class OpenDrawing(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -1871,7 +1876,7 @@ class OpenDrawing(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
if self.open_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in self.props.drawings if d.is_drawing and d.is_selected
|
||||
@@ -1907,7 +1912,7 @@ class ActivateModel(bpy.types.Operator):
|
||||
bl_description = "Activate the model view, hide all annotations"
|
||||
|
||||
def execute(self, context):
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.active_drawing_id = 0
|
||||
|
||||
CutDecorator.uninstall()
|
||||
@@ -1971,11 +1976,12 @@ class ActivateDrawingBase:
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
if bpy.context.scene.DocProperties.is_editing_drawings == False:
|
||||
props = tool.Drawing.get_document_props()
|
||||
if props.is_editing_drawings == False:
|
||||
bpy.ops.bim.load_drawings()
|
||||
|
||||
drawing = tool.Ifc.get().by_id(self.drawing)
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
|
||||
if self.use_quick_preview:
|
||||
tool.Blender.activate_camera(tool.Drawing.import_temporary_drawing_camera(drawing))
|
||||
@@ -2027,7 +2033,7 @@ class ActivateDrawing(bpy.types.Operator, ActivateDrawingBase):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2050,7 +2056,7 @@ class ActivateDrawingFromSheet(bpy.types.Operator, ActivateDrawingBase):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_sheet_item(reference_type="DRAWING"):
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2066,7 +2072,8 @@ class SelectDocIfcFile(bpy.types.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DocProperties.ifc_files[self.index].name = self.filepath
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.ifc_files[self.index].name = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -2098,7 +2105,7 @@ class RemoveDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -2112,12 +2119,10 @@ class RemoveDrawing(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.remove_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id)
|
||||
for d in context.scene.DocProperties.drawings
|
||||
if d.is_drawing and d.is_selected
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in props.drawings if d.is_drawing and d.is_selected
|
||||
]
|
||||
else:
|
||||
if not self.drawing:
|
||||
@@ -2187,7 +2192,8 @@ class ReloadDrawingStyles(bpy.types.Operator):
|
||||
with open(json_path, "r") as fi:
|
||||
shading_styles_json = json.load(fi)
|
||||
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
drawing_styles.clear()
|
||||
styles = [style for style in shading_styles_json]
|
||||
for style_name in styles:
|
||||
@@ -2212,7 +2218,8 @@ class AddDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
new = drawing_styles.add()
|
||||
# drawing style is saved to ifc on rename
|
||||
new.name = tool.Blender.ensure_unique_name("New Drawing Style", drawing_styles)
|
||||
@@ -2227,7 +2234,8 @@ class RemoveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
index: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DocProperties.drawing_styles.remove(self.index)
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.drawing_styles.remove(self.index)
|
||||
context.scene.camera.data.BIMCameraProperties.active_drawing_style_index = max(self.index - 1, 0)
|
||||
bpy.ops.bim.save_drawing_styles_data()
|
||||
return {"FINISHED"}
|
||||
@@ -2283,7 +2291,8 @@ class SaveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
index = int(self.index)
|
||||
else:
|
||||
index = context.scene.camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
scene.DocProperties.drawing_styles[index].raster_style = json.dumps(style)
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.drawing_styles[index].raster_style = json.dumps(style)
|
||||
|
||||
bpy.ops.bim.save_drawing_styles_data()
|
||||
return {"FINISHED"}
|
||||
@@ -2309,7 +2318,8 @@ class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not DrawingsData.is_loaded:
|
||||
DrawingsData.load()
|
||||
drawing_pset_data = DrawingsData.data["active_drawing_pset_data"]
|
||||
drawing_styles = context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = props.drawing_styles
|
||||
|
||||
rel_path = drawing_pset_data["ShadingStyles"]
|
||||
current_style = drawing_pset_data.get("CurrentShadingStyle", None)
|
||||
@@ -2338,7 +2348,7 @@ class SaveDrawingStylesData(bpy.types.Operator, tool.Ifc.Operator):
|
||||
new_style_name = None
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
drawing = ifc_file.by_id(context.scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(props.active_drawing_id)
|
||||
pset = tool.Pset.get_element_pset(drawing, "EPset_Drawing")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", ifc_file, pset=pset, properties={"CurrentShadingStyle": new_style_name}
|
||||
@@ -2357,17 +2367,18 @@ class ActivateDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
scene = context.scene
|
||||
ifc_file = tool.Ifc.get()
|
||||
active_drawing_style_index = scene.camera.data.BIMCameraProperties.active_drawing_style_index
|
||||
props = tool.Drawing.get_document_props()
|
||||
|
||||
if active_drawing_style_index >= len(scene.DocProperties.drawing_styles):
|
||||
if active_drawing_style_index >= len(props.drawing_styles):
|
||||
self.report({"ERROR"}, "Could not find active drawing style")
|
||||
return {"CANCELLED"}
|
||||
|
||||
self.drawing_style = scene.DocProperties.drawing_styles[active_drawing_style_index]
|
||||
self.drawing_style = props.drawing_styles[active_drawing_style_index]
|
||||
|
||||
self.set_raster_style(context)
|
||||
self.set_query(context)
|
||||
|
||||
drawing = ifc_file.by_id(scene.DocProperties.active_drawing_id)
|
||||
drawing = ifc_file.by_id(props.active_drawing_id)
|
||||
pset = tool.Pset.get_element_pset(drawing, "EPset_Drawing")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", ifc_file, pset=pset, properties={"CurrentShadingStyle": self.drawing_style.name}
|
||||
@@ -2392,7 +2403,8 @@ class ActivateDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def set_query(self, context: bpy.types.Context) -> None:
|
||||
self.include_global_ids = []
|
||||
self.exclude_global_ids = []
|
||||
for ifc_file in context.scene.DocProperties.ifc_files:
|
||||
props = tool.Drawing.get_document_props()
|
||||
for ifc_file in props.ifc_files:
|
||||
try:
|
||||
ifc = ifcopenshell.open(ifc_file.name)
|
||||
except:
|
||||
@@ -2507,14 +2519,14 @@ class AddScheduleToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not props.schedules:
|
||||
cls.poll_message_set("No schedule selected.")
|
||||
return False
|
||||
return props.schedules and props.sheets and context.scene.BIMProperties.data_dir
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_schedule = props.schedules[props.active_schedule_index]
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
schedule = tool.Ifc.get().by_id(active_schedule.ifc_definition_id)
|
||||
@@ -2573,14 +2585,14 @@ class AddReferenceToSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not props.references:
|
||||
cls.poll_message_set("No reference selected.")
|
||||
return False
|
||||
return props.references and props.sheets and context.scene.BIMProperties.data_dir
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
active_reference = props.references[props.active_reference_index]
|
||||
active_sheet = tool.Drawing.get_active_sheet(context)
|
||||
extref = tool.Ifc.get().by_id(active_reference.ifc_definition_id)
|
||||
@@ -2682,7 +2694,8 @@ class AddDrawingStyleAttribute(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.add()
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.drawing_styles[props.active_drawing_style_index].attributes.add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2695,7 +2708,8 @@ class RemoveDrawingStyleAttribute(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
context.scene.DocProperties.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
dprops.drawing_styles[props.active_drawing_style_index].attributes.remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -2979,7 +2993,7 @@ class LoadSheets(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
core.load_sheets(tool.Drawing)
|
||||
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
sheets_not_found = []
|
||||
for sheet_prop in props.sheets:
|
||||
if not sheet_prop.is_sheet:
|
||||
@@ -3009,7 +3023,7 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
document_type: Literal["SHEET", "TITLEBLOCK", "EMBEDDED"]
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
if sheet.is_a("IfcDocumentInformation"):
|
||||
self.document_type = "SHEET"
|
||||
@@ -3023,6 +3037,7 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.document_type == "SHEET":
|
||||
row = self.layout.row()
|
||||
row.prop(self, "identification", text="Identification")
|
||||
@@ -3030,13 +3045,13 @@ class EditSheet(bpy.types.Operator, tool.Ifc.Operator):
|
||||
row.prop(self, "name", text="Name")
|
||||
elif self.document_type == "TITLEBLOCK":
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "titleblock", text="Titleblock")
|
||||
row.prop(props, "titleblock", text="Titleblock")
|
||||
elif self.document_type == "EMBEDDED":
|
||||
row = self.layout.row()
|
||||
row.prop(self, "identification", text="Identification")
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
sheet = tool.Ifc.get().by_id(self.props.sheets[self.props.active_sheet_index].ifc_definition_id)
|
||||
if self.document_type == "SHEET":
|
||||
core.rename_sheet(tool.Ifc, tool.Drawing, sheet=sheet, identification=self.identification, name=self.name)
|
||||
@@ -3134,7 +3149,7 @@ class ExpandTargetView(bpy.types.Operator):
|
||||
target_view: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in [d for d in props.drawings if d.target_view == self.target_view]:
|
||||
drawing.is_expanded = True
|
||||
core.load_drawings(tool.Drawing)
|
||||
@@ -3150,7 +3165,7 @@ class ContractTargetView(bpy.types.Operator):
|
||||
target_view: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for drawing in [d for d in props.drawings if d.target_view == self.target_view]:
|
||||
drawing.is_expanded = False
|
||||
core.load_drawings(tool.Drawing)
|
||||
@@ -3166,7 +3181,7 @@ class ExpandSheet(bpy.types.Operator):
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
|
||||
sheet.is_expanded = True
|
||||
core.load_sheets(tool.Drawing)
|
||||
@@ -3182,7 +3197,7 @@ class ContractSheet(bpy.types.Operator):
|
||||
sheet: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for sheet in [s for s in props.sheets if s.ifc_definition_id == self.sheet]:
|
||||
sheet.is_expanded = False
|
||||
core.load_sheets(tool.Drawing)
|
||||
@@ -3373,7 +3388,7 @@ class ConvertSVGToDXF(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not tool.Drawing.get_active_drawing_item():
|
||||
cls.poll_message_set("No drawing selected.")
|
||||
return False
|
||||
@@ -3387,14 +3402,13 @@ class ConvertSVGToDXF(bpy.types.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = tool.Drawing.get_document_props()
|
||||
if self.convert_all:
|
||||
drawings = [
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id)
|
||||
for d in context.scene.DocProperties.drawings
|
||||
if d.is_drawing and d.is_selected
|
||||
tool.Ifc.get().by_id(d.ifc_definition_id) for d in props.drawings if d.is_drawing and d.is_selected
|
||||
]
|
||||
else:
|
||||
drawings = [tool.Ifc.get().by_id(context.scene.DocProperties.drawings.get(self.view).ifc_definition_id)]
|
||||
drawings = [tool.Ifc.get().by_id(props.drawings.get(self.view).ifc_definition_id)]
|
||||
|
||||
drawing_uris: list[Path] = []
|
||||
drawings_not_found: list[str] = []
|
||||
|
||||
@@ -76,7 +76,7 @@ def update_diagram_scale(self, context):
|
||||
try:
|
||||
element = (
|
||||
tool.Ifc.get()
|
||||
.by_id(self.id_data.BIMMeshProperties.ifc_definition_id)
|
||||
.by_id(tool.Geometry.get_mesh_props(self.id_data).ifc_definition_id)
|
||||
.OfProductRepresentation[0]
|
||||
.ShapeOfProduct[0]
|
||||
)
|
||||
@@ -93,7 +93,7 @@ def update_diagram_scale(self, context):
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties=diagram_scale)
|
||||
|
||||
|
||||
def update_is_nts(self, context):
|
||||
def update_is_nts(self: "BIMCameraProperties", context: bpy.types.Context) -> None:
|
||||
if not self.update_props:
|
||||
return
|
||||
if not context.scene.camera or context.scene.camera.data != self.id_data:
|
||||
@@ -104,7 +104,7 @@ def update_is_nts(self, context):
|
||||
try:
|
||||
element = (
|
||||
tool.Ifc.get()
|
||||
.by_id(self.id_data.BIMMeshProperties.ifc_definition_id)
|
||||
.by_id(tool.Geometry.get_mesh_props(self.id_data).ifc_definition_id)
|
||||
.OfProductRepresentation[0]
|
||||
.ShapeOfProduct[0]
|
||||
)
|
||||
@@ -192,8 +192,8 @@ def get_drawing_style_name(self: "DrawingStyle"):
|
||||
|
||||
def set_drawing_style_name(self: "DrawingStyle", new_value: str) -> None:
|
||||
"""ensure the name is unique"""
|
||||
scene = bpy.context.scene
|
||||
drawing_styles = [s.name for s in scene.DocProperties.drawing_styles if s.name != self.name]
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawing_styles = [s.name for s in props.drawing_styles if s.name != self.name]
|
||||
new_value = tool.Blender.ensure_unique_name(new_value, drawing_styles)
|
||||
old_value = self.name
|
||||
self["name"] = new_value
|
||||
|
||||
@@ -56,7 +56,7 @@ def a1_to_rc(cell):
|
||||
|
||||
|
||||
class Scheduler:
|
||||
def schedule(self, infile, outfile):
|
||||
def schedule(self, infile: str, outfile: str) -> None:
|
||||
self.svg = svgwrite.Drawing(
|
||||
outfile,
|
||||
debug=False,
|
||||
@@ -71,11 +71,12 @@ class Scheduler:
|
||||
elif infile.endswith("xlsx"):
|
||||
self.schedule_xlsx(infile, outfile)
|
||||
|
||||
def parse_css(self, infile):
|
||||
def parse_css(self, infile: str) -> None:
|
||||
props = tool.Drawing.get_document_props()
|
||||
stylesheet_path = os.path.splitext(infile)[0] + ".css"
|
||||
if not os.path.exists(stylesheet_path):
|
||||
stylesheet_rel_path = getattr(bpy.context.scene.DocProperties, "schedules_stylesheet_path")
|
||||
ifc_file_path = os.path.dirname(IfcStore.path)
|
||||
stylesheet_rel_path = props.schedules_stylesheet_path
|
||||
ifc_file_path = os.path.dirname(tool.Ifc.get_path())
|
||||
stylesheet_path = ifc_file_path + "\\" + stylesheet_rel_path
|
||||
if not os.path.exists(stylesheet_path):
|
||||
stylesheet_path = tool.Blender.get_data_dir_path(Path("assets") / "schedule.css")
|
||||
@@ -91,7 +92,7 @@ class Scheduler:
|
||||
|
||||
self.svg.defs.add(self.svg.style(css))
|
||||
|
||||
def schedule_xlsx(self, infile, outfile):
|
||||
def schedule_xlsx(self, infile: str, outfile: str) -> None:
|
||||
workbook = openpyxl.open(infile, data_only=True)
|
||||
sheet = workbook.active
|
||||
|
||||
@@ -236,7 +237,7 @@ class Scheduler:
|
||||
self.svg["viewBox"] = "0 0 {} {}".format(total_width, total_height)
|
||||
self.svg.save(pretty=True)
|
||||
|
||||
def schedule_ods(self, infile, outfile):
|
||||
def schedule_ods(self, infile: str, outfile: str) -> None:
|
||||
doc = load_ods(infile)
|
||||
|
||||
# useful for debugging ods
|
||||
@@ -495,11 +496,11 @@ class Scheduler:
|
||||
self.svg["viewBox"] = "0 0 {} {}".format(total_width, total_height)
|
||||
self.svg.save(pretty=True)
|
||||
|
||||
def get_style(self, style_name, styles):
|
||||
def get_style(self, style_name: str, styles: dict) -> dict:
|
||||
style = styles[style_name] if style_name else {}
|
||||
return style
|
||||
|
||||
def get_box_alignment(self, style):
|
||||
def get_box_alignment(self, style: dict) -> str:
|
||||
if style and "vertical-align" in style and style["vertical-align"] != "automatic":
|
||||
vertical_align = style["vertical-align"]
|
||||
else:
|
||||
|
||||
@@ -49,7 +49,7 @@ class BIM_PT_camera(Panel):
|
||||
return
|
||||
|
||||
self.layout.use_property_split = True
|
||||
dprops = context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
props = context.scene.camera.data.BIMCameraProperties
|
||||
|
||||
col = self.layout.column(align=True)
|
||||
@@ -161,7 +161,7 @@ class BIM_PT_drawing_underlay(Panel):
|
||||
layout.use_property_split = True
|
||||
camera = context.scene.camera
|
||||
assert camera
|
||||
dprops = context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
props = camera.data.BIMCameraProperties
|
||||
drawing_index_is_valid = props.active_drawing_style_index < len(dprops.drawing_styles)
|
||||
|
||||
@@ -229,7 +229,7 @@ class BIM_PT_drawings(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_drawings:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -302,7 +302,7 @@ class BIM_PT_schedules(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_schedules:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -352,7 +352,7 @@ class BIM_PT_references(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_references:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -394,7 +394,7 @@ class BIM_PT_sheets(Panel):
|
||||
draw_project_not_saved_ui(self)
|
||||
return
|
||||
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
|
||||
if not self.props.is_editing_sheets:
|
||||
row = self.layout.row(align=True)
|
||||
@@ -601,7 +601,7 @@ class BIM_UL_drawinglist(bpy.types.UIList):
|
||||
selected_icon = "CHECKBOX_HLT" if item.is_selected else "CHECKBOX_DEHLT"
|
||||
row.prop(item, "is_selected", text="", icon=selected_icon, emboss=False)
|
||||
row.prop(item, "name", text="", emboss=False)
|
||||
self.props = context.scene.DocProperties
|
||||
self.props = tool.Drawing.get_document_props()
|
||||
if (
|
||||
self.props.drawings
|
||||
and self.props.active_drawing_id
|
||||
|
||||
@@ -197,6 +197,8 @@ def create_annotation_occurrence(context):
|
||||
|
||||
|
||||
class AnnotationToolUI:
|
||||
layout: bpy.types.UILayout
|
||||
|
||||
@classmethod
|
||||
def draw(cls, context, layout):
|
||||
cls.layout = layout
|
||||
@@ -224,7 +226,8 @@ class AnnotationToolUI:
|
||||
@classmethod
|
||||
def draw_create_object_interface(cls):
|
||||
row = cls.layout.row(align=True)
|
||||
row.prop(bpy.context.scene.DocProperties, "should_draw_decorations", text="Viewport Annotations")
|
||||
props = tool.Drawing.get_document_props()
|
||||
row.prop(props, "should_draw_decorations", text="Viewport Annotations")
|
||||
|
||||
@classmethod
|
||||
def draw_edit_object_interface(cls, context):
|
||||
|
||||
@@ -98,12 +98,14 @@ addon_keymaps = []
|
||||
|
||||
|
||||
@persistent
|
||||
def block_scale(scene):
|
||||
def block_scale(scene: bpy.types.Scene) -> None:
|
||||
import bonsai.tool as tool
|
||||
|
||||
if obj := (getattr(bpy.context, "active_object", None) or bpy.context.view_layer.objects.active):
|
||||
if isinstance(obj, bpy.types.Object) and obj.BIMObjectProperties.ifc_definition_id:
|
||||
if obj.scale != (1, 1, 1):
|
||||
obj.scale = (1, 1, 1)
|
||||
elif isinstance(obj, bpy.types.Mesh) and obj.BIMMeshProperties.ifc_definition_id:
|
||||
elif isinstance(obj, bpy.types.Mesh) and tool.Geometry.get_mesh_props(obj).ifc_definition_id:
|
||||
if obj.scale != (1, 1, 1):
|
||||
obj.scale = (1, 1, 1)
|
||||
|
||||
|
||||
@@ -53,15 +53,16 @@ class ViewportData:
|
||||
obj = bpy.context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
modes = [obj_mode]
|
||||
|
||||
if bpy.context.scene.BIMGeometryProperties.representation_obj:
|
||||
modes: list[tuple[str, str, str, str, int]] = [obj_mode]
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
if gprops.representation_obj:
|
||||
modes.append(item_mode)
|
||||
|
||||
if not obj:
|
||||
return modes
|
||||
|
||||
if obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
|
||||
pprops = tool.Project.get_project_props()
|
||||
if obj in pprops.clipping_planes_objs:
|
||||
pass
|
||||
elif element:
|
||||
if tool.Geometry.is_locked(element):
|
||||
@@ -107,8 +108,9 @@ class RepresentationsData:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
active_representation_id = None
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties"):
|
||||
active_representation_id = obj.data.BIMMeshProperties.ifc_definition_id
|
||||
active_representation = tool.Geometry.get_active_representation(obj)
|
||||
if active_representation:
|
||||
active_representation_id = active_representation.id()
|
||||
|
||||
for representation in tool.Geometry.get_representations_iter(element):
|
||||
representation_type = representation.RepresentationType
|
||||
@@ -158,9 +160,9 @@ class RepresentationsData:
|
||||
if not obj.data:
|
||||
return []
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not (active_representation_id := obj.data.BIMMeshProperties.ifc_definition_id):
|
||||
base_representation = tool.Geometry.get_active_representation(obj)
|
||||
if not base_representation:
|
||||
return [] # Maybe in profile editing mode
|
||||
base_representation = tool.Ifc.get().by_id(active_representation_id)
|
||||
|
||||
# shape aspects matching context of the active representation
|
||||
matching_shape_aspects = []
|
||||
@@ -390,7 +392,7 @@ class PlacementData:
|
||||
def load(cls):
|
||||
cls.data = {"has_placement": cls.has_placement()}
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
obj = bpy.context.active_object
|
||||
if obj and props.has_blender_offset:
|
||||
xyz = cls.original_xyz(obj)
|
||||
@@ -413,7 +415,7 @@ class PlacementData:
|
||||
@classmethod
|
||||
def original_xyz(cls, obj):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
xyz = ifcopenshell.util.geolocation.xyz2enh(
|
||||
obj.matrix_world[0][3],
|
||||
obj.matrix_world[1][3],
|
||||
|
||||
@@ -46,12 +46,14 @@ class ItemDecorator:
|
||||
obj_is_boolean: dict[str, list[ifcopenshell.entity_instance]] = {}
|
||||
objs: dict[str, dict[str, list]] = {}
|
||||
obj_matrix: dict[str, Matrix] = {}
|
||||
for item_obj in context.scene.BIMGeometryProperties.item_objs:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item_obj in props.item_objs:
|
||||
if obj := item_obj.obj:
|
||||
obj: bpy.types.Object
|
||||
objs[obj.name] = cls.get_obj_data(obj)
|
||||
obj_is_selected[obj.name] = obj.select_get()
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
obj_is_boolean[obj.name] = [i for i in tool.Ifc.get().get_inverse(item) if i.is_a("IfcBooleanResult")]
|
||||
obj_matrix[obj.name] = obj.matrix_world.copy()
|
||||
|
||||
@@ -143,7 +145,8 @@ class ItemDecorator:
|
||||
color = selected_elements_color
|
||||
blf.color(font_id, *color)
|
||||
|
||||
for item in context.scene.BIMGeometryProperties.item_objs:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item in props.item_objs:
|
||||
if (obj := item.obj) and obj.hide_get() == False:
|
||||
if obj.select_get():
|
||||
centroid = obj.matrix_world @ Vector(obj.bound_box[0]).lerp(Vector(obj.bound_box[6]), 0.5)
|
||||
|
||||
@@ -23,6 +23,7 @@ import numpy as np
|
||||
import numpy.typing as npt
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.layer
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
@@ -78,7 +79,8 @@ class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.separate_element(element)
|
||||
|
||||
def separate_item(self, context, obj):
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
previous_selected_objects = context.selected_objects
|
||||
bpy.ops.mesh.separate(type=self.type)
|
||||
@@ -86,7 +88,7 @@ class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if obj in previous_selected_objects:
|
||||
continue
|
||||
self.add_meshlike_item(obj)
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(tool.Geometry.get_geometry_props().representation_obj)
|
||||
else:
|
||||
self.report({"INFO"}, f"Separating an {item.is_a()} is not supported")
|
||||
|
||||
@@ -121,7 +123,7 @@ class OverrideMeshSeparate(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, obj)
|
||||
props.add_item_object(obj, item)
|
||||
|
||||
def separate_element(self, element):
|
||||
@@ -302,11 +304,12 @@ class AddRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def draw(self, context):
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
row = self.layout.row()
|
||||
row.prop(self, "representation_conversion_method", text="")
|
||||
if self.representation_conversion_method == "OBJECT":
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.BIMGeometryProperties, "representation_from_object", text="")
|
||||
row.prop(props, "representation_from_object", text="")
|
||||
|
||||
|
||||
class SelectConnection(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -457,13 +460,17 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def update_obj_mesh_representation(self, context: bpy.types.Context, obj: bpy.types.Object) -> None:
|
||||
data = obj.data
|
||||
assert tool.Geometry.is_data_supported_for_adding_representation(data)
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
|
||||
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
||||
material = ifcopenshell.util.element.get_material(product, should_skip_usage=True)
|
||||
|
||||
# NOTE: Currently iterator doesn't detect whether opening is actually affected the representation
|
||||
# or it's just present on the element. In theory, we can also allow editing representations
|
||||
# if we know that representation wasn't affected by existing openings.
|
||||
has_openings = tool.Geometry.has_openings(product) and obj.data.BIMMeshProperties.has_openings_applied
|
||||
has_openings = tool.Geometry.has_openings(product) and tool.Geometry.get_mesh_props(data).has_openings_applied
|
||||
if has_openings and not self.apply_openings:
|
||||
# Meshlike things with openings can only be updated without openings applied.
|
||||
if self.from_ui:
|
||||
@@ -486,7 +493,8 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if tool.Ifc.is_moved(obj) or tool.Geometry.is_scaled(obj):
|
||||
core.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
old_representation = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
old_representation = tool.Geometry.get_active_representation(obj)
|
||||
assert old_representation
|
||||
if material and material.is_a() in ["IfcMaterialProfileSet", "IfcMaterialLayerSet"]:
|
||||
if self.ifc_representation_class == "IfcTessellatedFaceSet":
|
||||
# We are explicitly casting to a tessellation, so remove all parametric materials.
|
||||
@@ -545,12 +553,12 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Geometry.run_style_add_style(obj=mat)
|
||||
for mat in tool.Geometry.get_object_materials_without_styles(obj)
|
||||
]
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_representation_styles",
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
ifcopenshell.api.style.assign_representation_styles(
|
||||
self.file,
|
||||
shape_representation=new_representation,
|
||||
styles=tool.Geometry.get_styles(obj, only_assigned_to_faces=True),
|
||||
should_use_presentation_style_assignment=context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
should_use_presentation_style_assignment=props.should_use_presentation_style_assignment,
|
||||
)
|
||||
tool.Geometry.record_object_materials(obj)
|
||||
|
||||
@@ -569,8 +577,8 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
continue
|
||||
representation.RepresentationIdentifier = "Reference"
|
||||
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
|
||||
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
|
||||
tool.Ifc.link(new_representation, data)
|
||||
data.name = tool.Loader.get_mesh_name(new_representation)
|
||||
|
||||
# TODO: In simple scenarios, a type has a ShapeRepresentation of ID
|
||||
# 123. This is then mapped through mapped representations by
|
||||
@@ -586,7 +594,7 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# transformations.
|
||||
|
||||
core.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_representation)
|
||||
if obj.data.BIMMeshProperties.ifc_parameters:
|
||||
if mprops.ifc_parameters:
|
||||
core.get_representation_ifc_parameters(tool.Geometry, obj=obj)
|
||||
|
||||
|
||||
@@ -598,12 +606,13 @@ class UpdateParametricRepresentation(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object and context.active_object.mode == "OBJECT"
|
||||
return (obj := context.active_object) and obj.mode == "OBJECT" and tool.Geometry.has_mesh_properties(obj.data)
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
obj = context.active_object
|
||||
props = obj.data.BIMMeshProperties
|
||||
assert obj and tool.Geometry.has_mesh_properties(obj.data)
|
||||
props = tool.Geometry.get_mesh_props(obj.data)
|
||||
parameter = props.ifc_parameters[self.index]
|
||||
self.file.by_id(parameter.step_id)[parameter.index] = parameter.value
|
||||
show_representation_parameters = bool(props.ifc_parameters)
|
||||
@@ -627,8 +636,10 @@ class GetRepresentationIfcParameters(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
core.get_representation_ifc_parameters(tool.Geometry, obj=context.active_object)
|
||||
parameters = context.active_object.data.BIMMeshProperties.ifc_parameters
|
||||
obj = context.active_object
|
||||
assert obj and tool.Geometry.has_mesh_properties((data := obj.data))
|
||||
core.get_representation_ifc_parameters(tool.Geometry, obj=obj)
|
||||
parameters = tool.Geometry.get_mesh_props(data).ifc_parameters
|
||||
self.report({"INFO"}, f"{len(parameters)} parameters found.")
|
||||
|
||||
|
||||
@@ -721,7 +732,7 @@ class OverrideDelete(bpy.types.Operator):
|
||||
row = self.layout.row()
|
||||
row.label(text="Warning: Faster deletion will use more memory.", icon="ERROR")
|
||||
|
||||
def _execute(self, context):
|
||||
def _execute(self, context: bpy.types.Context):
|
||||
start_time = time()
|
||||
|
||||
if self.is_batch:
|
||||
@@ -730,9 +741,7 @@ class OverrideDelete(bpy.types.Operator):
|
||||
self.process_arrays(context)
|
||||
clear_active_object = True
|
||||
for obj in context.selected_objects:
|
||||
try:
|
||||
obj.name
|
||||
except:
|
||||
if not tool.Blender.is_valid_data_block(obj):
|
||||
continue
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
@@ -782,7 +791,7 @@ class OverrideDelete(bpy.types.Operator):
|
||||
data["old_file"].redo()
|
||||
tool.Ifc.set(data["new_file"])
|
||||
|
||||
def process_arrays(self, context):
|
||||
def process_arrays(self, context: bpy.types.Context) -> None:
|
||||
selected_objects = set(context.selected_objects)
|
||||
array_parents = set()
|
||||
for obj in context.selected_objects:
|
||||
@@ -1030,7 +1039,8 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
# Unlink from previous boolean element
|
||||
# and keep object tracked for decorations.
|
||||
if is_tracked_opening:
|
||||
new_obj.data.BIMMeshProperties.ifc_boolean_id = 0
|
||||
mprops = tool.Geometry.get_mesh_props(new_obj.data)
|
||||
mprops.ifc_boolean_id = 0
|
||||
tool.Root.add_tracked_opening(new_obj, tracked_opening_type)
|
||||
|
||||
if obj == context.active_object:
|
||||
@@ -1054,7 +1064,7 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
if new.is_a("IfcRelSpaceBoundary"):
|
||||
surface = new.ConnectionGeometry.SurfaceOnRelatingElement
|
||||
temp_data.name = f"0/{surface.id()}"
|
||||
temp_data.BIMMeshProperties.ifc_definition_id = surface.id()
|
||||
tool.Ifc.link(surface, temp_data)
|
||||
else:
|
||||
tool.Blender.remove_data_block(temp_data)
|
||||
|
||||
@@ -1090,12 +1100,14 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
||||
@staticmethod
|
||||
def duplicate_item(obj: bpy.types.Object) -> None:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
new_item = ifcopenshell.util.element.copy_deep(tool.Ifc.get(), item)
|
||||
new_obj = obj.copy()
|
||||
assert tool.Geometry.has_mesh_properties(obj.data)
|
||||
temp_data = obj.data.copy()
|
||||
new_obj.data = temp_data
|
||||
new_obj.data.BIMMeshProperties.ifc_definition_id = new_item.id()
|
||||
tool.Ifc.link(new_item, temp_data)
|
||||
new_obj.name = obj.data.name = f"Item/{new_item.is_a()}/{new_item.id()}"
|
||||
props.add_item_object(new_obj, new_item)
|
||||
|
||||
@@ -1671,7 +1683,8 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def join_item(self) -> None:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
ifc_file = tool.Ifc.get()
|
||||
item = tool.Ifc.get().by_id(self.target.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = tool.Geometry.get_active_representation(self.target)
|
||||
assert item
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
tool.Geometry.dissolve_triangulated_edges(self.target)
|
||||
item_objs = [i.obj for i in props.item_objs if i.obj]
|
||||
@@ -1694,7 +1707,7 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
for item_data in items_data:
|
||||
props.add_item_object(item_data["obj"], ifc_file.by_id(item_data["ifc_definition_id"]))
|
||||
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
bpy.context.view_layer.update()
|
||||
tool.Root.reload_item_decorator()
|
||||
|
||||
@@ -1702,7 +1715,8 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_file = tool.Ifc.get()
|
||||
builder = ShapeBuilder(ifc_file)
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
representation = ifc_file.by_id(self.target.data.BIMMeshProperties.ifc_definition_id)
|
||||
representation = tool.Geometry.get_active_representation(self.target)
|
||||
assert representation
|
||||
representation_type = representation.RepresentationType
|
||||
if representation_type in ("Tessellation", "Brep"):
|
||||
for obj in bpy.context.selected_objects:
|
||||
@@ -1741,7 +1755,8 @@ class OverrideJoin(bpy.types.Operator, tool.Ifc.Operator):
|
||||
continue
|
||||
|
||||
# Only objects of the same representation type can be joined
|
||||
obj_rep = ifc_file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
obj_rep = tool.Geometry.get_active_representation(obj)
|
||||
assert obj_rep
|
||||
if obj_rep.RepresentationType != representation_type:
|
||||
obj.select_set(False)
|
||||
self.report(
|
||||
@@ -1892,9 +1907,10 @@ class OverrideEscape(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
if context.scene.BIMGeometryProperties.mode == "ITEM":
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
if props.mode == "ITEM":
|
||||
tool.Geometry.disable_item_mode()
|
||||
elif context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
elif props.mode == "EDIT":
|
||||
bpy.ops.bim.override_mode_set_object("INVOKE_DEFAULT", should_save=False)
|
||||
tool.Geometry.disable_item_mode()
|
||||
elif tool.Model.get_model_props().openings:
|
||||
@@ -1950,6 +1966,7 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def handle_single_object(self, context: bpy.types.Context, obj: bpy.types.Object) -> None:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
pprops = tool.Project.get_project_props()
|
||||
if obj == props.representation_obj:
|
||||
self.report({"ERROR"}, f"Element '{obj.name}' is in item mode and cannot be edited directly")
|
||||
elif obj in [o.obj for o in context.scene.BIMAggregateProperties.not_editing_objects]:
|
||||
@@ -1957,7 +1974,7 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report(
|
||||
{"ERROR"}, f"Element '{obj.name}' does not belong to this aggregate and cannot be edited directly"
|
||||
)
|
||||
elif obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
|
||||
elif obj in pprops.clipping_planes_objs:
|
||||
self.report({"ERROR"}, "Clipping planes cannot be edited")
|
||||
elif element:
|
||||
if not obj.data:
|
||||
@@ -2005,12 +2022,15 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.handle_single_object(context, obj)
|
||||
|
||||
def enable_editing_representation_item(self, context: bpy.types.Context, obj: bpy.types.Object) -> None:
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
element = tool.Ifc.get_entity(context.scene.BIMGeometryProperties.representation_obj)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
element = tool.Ifc.get_entity(tool.Geometry.get_geometry_props().representation_obj)
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
tool.Geometry.dissolve_triangulated_edges(obj)
|
||||
tool.Blender.select_and_activate_single_object(context, obj)
|
||||
obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
|
||||
assert isinstance(mesh := obj.data, bpy.types.Mesh)
|
||||
props = tool.Geometry.get_mesh_props(mesh)
|
||||
props.mesh_checksum = tool.Geometry.get_mesh_checksum(mesh)
|
||||
self.enable_edit_mode(context)
|
||||
elif (
|
||||
item.is_a("IfcSweptAreaSolid")
|
||||
@@ -2027,21 +2047,21 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
f"Couldn't import profile, editing it directly is not yet supported. Failing profile: {profile}.",
|
||||
)
|
||||
return
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, obj.data)
|
||||
self.enable_edit_mode(context)
|
||||
ProfileDecorator.install(context)
|
||||
if not bpy.app.background:
|
||||
tool.Blender.set_viewport_tool("bim.cad_tool")
|
||||
elif item.is_a("IfcAnnotationFillArea"):
|
||||
tool.Model.import_annotation_fill_area(item, obj=obj)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, obj.data)
|
||||
self.enable_edit_mode(context)
|
||||
ProfileDecorator.install(context)
|
||||
if not bpy.app.background:
|
||||
tool.Blender.set_viewport_tool("bim.cad_tool")
|
||||
elif tool.Geometry.is_curvelike_item(item):
|
||||
tool.Model.import_curve(item, obj=obj)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, obj.data)
|
||||
self.enable_edit_mode(context)
|
||||
ProfileDecorator.install(context)
|
||||
if not bpy.app.background:
|
||||
@@ -2052,10 +2072,11 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def enable_edit_mode(self, context: bpy.types.Context) -> Union[None, set[str]]:
|
||||
if tool.Blender.toggle_edit_mode(context) == {"CANCELLED"}:
|
||||
return {"CANCELLED"}
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "EDIT":
|
||||
context.scene.BIMGeometryProperties.mode = "EDIT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
props.is_changing_mode = True
|
||||
if props.mode != "EDIT":
|
||||
props.mode = "EDIT"
|
||||
props.is_changing_mode = False
|
||||
|
||||
def has_aggregates(self, objs):
|
||||
for obj in objs:
|
||||
@@ -2102,14 +2123,15 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
tool.Blender.toggle_edit_mode(context)
|
||||
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.representation_obj:
|
||||
if context.scene.BIMGeometryProperties.mode != "ITEM":
|
||||
context.scene.BIMGeometryProperties.mode = "ITEM"
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
props.is_changing_mode = True
|
||||
if props.representation_obj:
|
||||
if props.mode != "ITEM":
|
||||
props.mode = "ITEM"
|
||||
else:
|
||||
if context.scene.BIMGeometryProperties.mode != "OBJECT":
|
||||
context.scene.BIMGeometryProperties.mode = "OBJECT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
if props.mode != "OBJECT":
|
||||
props.mode = "OBJECT"
|
||||
props.is_changing_mode = False
|
||||
|
||||
if context.active_object and self.should_save:
|
||||
element = tool.Ifc.get_entity(context.active_object)
|
||||
@@ -2155,24 +2177,27 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
else:
|
||||
bpy.ops.bim.edit_extrusion_profile()
|
||||
return self.execute(context)
|
||||
elif obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
if not tool.Geometry.has_geometric_data(obj):
|
||||
elif representation := tool.Geometry.get_active_representation(obj):
|
||||
if not tool.Geometry.is_geometric_data(obj.data):
|
||||
self.is_valid = False
|
||||
self.should_save = False
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
assert tool.Geometry.has_mesh_properties(obj.data)
|
||||
mesh_props = tool.Geometry.get_mesh_props(obj.data)
|
||||
if tool.Geometry.is_meshlike(
|
||||
representation
|
||||
) and obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
|
||||
) and mesh_props.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
|
||||
self.edited_objs.append(obj)
|
||||
elif getattr(element, "HasOpenings", None):
|
||||
self.unchanged_objs_with_openings.append(obj)
|
||||
else:
|
||||
tool.Ifc.finish_edit(obj)
|
||||
elif element.is_a("IfcGridAxis"):
|
||||
if not tool.Geometry.has_geometric_data(obj):
|
||||
if not tool.Geometry.is_geometric_data(obj.data):
|
||||
self.is_valid = False
|
||||
self.should_save = False
|
||||
if obj.data.BIMMeshProperties.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
|
||||
assert tool.Geometry.has_mesh_properties(obj.data)
|
||||
mesh_props = tool.Geometry.get_mesh_props(obj.data)
|
||||
if mesh_props.mesh_checksum != tool.Geometry.get_mesh_checksum(obj.data):
|
||||
self.edited_objs.append(obj)
|
||||
else:
|
||||
tool.Ifc.finish_edit(obj)
|
||||
@@ -2197,9 +2222,11 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def edit_representation_item(self, obj: bpy.types.Object) -> None:
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
if tool.Geometry.is_meshlike_item(item):
|
||||
if tool.Geometry.has_geometric_data(obj) and obj.data.polygons:
|
||||
if tool.Geometry.is_geometric_data(obj.data) and obj.data.polygons:
|
||||
tool.Geometry.edit_meshlike_item(obj)
|
||||
else:
|
||||
tool.Geometry.import_item(obj)
|
||||
@@ -2220,11 +2247,11 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_profile, profile)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_profile)
|
||||
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.import_item_attributes(obj)
|
||||
|
||||
element = tool.Ifc.get_entity(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
element = tool.Ifc.get_entity(props.representation_obj)
|
||||
# Only certain classes should have a footprint
|
||||
if element.is_a() in ("IfcSlab", "IfcRamp"):
|
||||
footprint_context = ifcopenshell.util.representation.get_context(
|
||||
@@ -2277,9 +2304,9 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
for inverse in tool.Ifc.get().get_inverse(item):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, item, profile)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = profile.id()
|
||||
tool.Ifc.link(profile, obj.data)
|
||||
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.import_item_attributes(obj)
|
||||
elif tool.Geometry.is_curvelike_item(item):
|
||||
@@ -2305,10 +2332,9 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, item, new)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item)
|
||||
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = new.id()
|
||||
tool.Ifc.link(new, obj.data)
|
||||
tool.Geometry.import_item(obj)
|
||||
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item in additional_curves:
|
||||
representation = tool.Geometry.get_active_representation(props.representation_obj)
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
@@ -2317,7 +2343,7 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
name = f"Item/{item.is_a()}/{item.id()}"
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
new_obj = bpy.data.objects.new(name, mesh)
|
||||
new_obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Ifc.link(item, new_obj.data)
|
||||
bpy.context.collection.objects.link(new_obj)
|
||||
props.add_item_object(new_obj, item)
|
||||
new_obj.matrix_world = obj.matrix_world
|
||||
@@ -2330,10 +2356,11 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def enable_edit_mode(self, context):
|
||||
if tool.Blender.toggle_edit_mode(context) == {"CANCELLED"}:
|
||||
return {"CANCELLED"}
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = True
|
||||
if context.scene.BIMGeometryProperties.mode != "EDIT":
|
||||
context.scene.BIMGeometryProperties.mode = "EDIT"
|
||||
context.scene.BIMGeometryProperties.is_changing_mode = False
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
props.is_changing_mode = True
|
||||
if props.mode != "EDIT":
|
||||
props.mode = "EDIT"
|
||||
props.is_changing_mode = False
|
||||
|
||||
|
||||
class FlipObject(bpy.types.Operator):
|
||||
@@ -2370,12 +2397,13 @@ class EnableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
item.tags += ","
|
||||
item.tags += tag
|
||||
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties"):
|
||||
active_representation_id = obj.data.BIMMeshProperties.ifc_definition_id
|
||||
representation = tool.Ifc.get().by_id(active_representation_id)
|
||||
if tool.Geometry.has_mesh_properties((data := obj.data)):
|
||||
representation = tool.Geometry.get_data_representation(data)
|
||||
assert representation
|
||||
|
||||
# Shape aspects must be considered from the PartOfProductDefinitionShape level
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
product_reps = []
|
||||
if element.is_a("IfcProduct"):
|
||||
product_reps = [element.Representation]
|
||||
@@ -2440,7 +2468,8 @@ class DisableEditingRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
obj.BIMGeometryProperties.is_editing = False
|
||||
assert obj
|
||||
tool.Geometry.get_object_geometry_props(obj).is_editing = False
|
||||
|
||||
|
||||
class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -2451,9 +2480,12 @@ class RemoveRepresentationItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if context.scene.BIMGeometryProperties.representation_obj:
|
||||
if tool.Geometry.get_geometry_props().representation_obj:
|
||||
return False # Artificial restriction for now to prevent removing when in item mode
|
||||
if not (obj := tool.Geometry.get_active_or_representation_obj()) or len(obj.BIMGeometryProperties.items) <= 1:
|
||||
if (
|
||||
not (obj := tool.Geometry.get_active_or_representation_obj())
|
||||
or len(tool.Geometry.get_object_geometry_props(obj).items) <= 1
|
||||
):
|
||||
cls.poll_message_set(
|
||||
"Active object need to have more than 1 representation items to keep representation valid"
|
||||
)
|
||||
@@ -2488,13 +2520,17 @@ class SelectRepresentationItem(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
item = tool.Ifc.get().by_id(obj.BIMGeometryProperties.active_item.ifc_definition_id)
|
||||
obj_props = tool.Geometry.get_object_geometry_props(obj)
|
||||
assert obj_props.active_item
|
||||
item = tool.Ifc.get().by_id(obj_props.active_item.ifc_definition_id)
|
||||
item_ids = self.get_nested_item_ids(item)
|
||||
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
for item_obj in props.item_objs:
|
||||
if item_obj.obj.data.BIMMeshProperties.ifc_definition_id in item_ids:
|
||||
tool.Blender.select_object(item_obj.obj)
|
||||
obj_ = item_obj.obj
|
||||
props = tool.Geometry.get_mesh_props(obj_.data)
|
||||
if props.ifc_definition_id in item_ids:
|
||||
tool.Blender.select_object(obj_)
|
||||
return {"FINISHED"}
|
||||
|
||||
def get_nested_item_ids(self, item):
|
||||
@@ -2512,7 +2548,7 @@ class SelectRepresentationItem(bpy.types.Operator):
|
||||
def poll_editing_representation_item_style(cls, context):
|
||||
if not (obj := tool.Geometry.get_active_or_representation_obj()):
|
||||
return False
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
if not props.is_editing:
|
||||
return False
|
||||
if not (item := props.active_item):
|
||||
@@ -2547,7 +2583,8 @@ class EnableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_style = True
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
@@ -2566,7 +2603,8 @@ class EditRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_style = False
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
@@ -2588,7 +2626,7 @@ class DisableEditingRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operato
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_style = False
|
||||
|
||||
|
||||
@@ -2604,7 +2642,8 @@ class UnassignRepresentationItemStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
active_obj = tool.Geometry.get_active_or_representation_obj()
|
||||
active_props = active_obj.BIMGeometryProperties
|
||||
assert active_obj
|
||||
active_props = tool.Geometry.get_object_geometry_props(active_obj)
|
||||
active_props.is_editing_item_style = False
|
||||
|
||||
# Get active representation item
|
||||
@@ -2671,7 +2710,8 @@ class EnableEditingRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Op
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_shape_aspect = True
|
||||
|
||||
# set dropdown to currently active shape aspect
|
||||
@@ -2687,11 +2727,13 @@ class EditRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
assert obj
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_shape_aspect = False
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
assert props.active_item
|
||||
representation_item_id = props.active_item.ifc_definition_id
|
||||
representation_item = ifc_file.by_id(representation_item_id)
|
||||
|
||||
@@ -2744,7 +2786,8 @@ class DisableEditingRepresentationItemShapeAspect(bpy.types.Operator, tool.Ifc.O
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
props = obj.BIMGeometryProperties
|
||||
assert obj
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
props.is_editing_item_shape_aspect = False
|
||||
|
||||
|
||||
@@ -2755,10 +2798,12 @@ class RemoveRepresentationItemFromShapeAspect(bpy.types.Operator, tool.Ifc.Opera
|
||||
|
||||
def _execute(self, context):
|
||||
obj = tool.Geometry.get_active_or_representation_obj()
|
||||
assert obj
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMGeometryProperties
|
||||
props = tool.Geometry.get_object_geometry_props(obj)
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
assert props.active_item
|
||||
representation_item_id = props.active_item.ifc_definition_id
|
||||
representation_item = ifc_file.by_id(representation_item_id)
|
||||
shape_aspect = ifc_file.by_id(props.active_item.shape_aspect_id)
|
||||
@@ -2841,7 +2886,7 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
|
||||
boolean_ids.add(item.SecondOperand.id())
|
||||
continue
|
||||
item_mesh = bpy.data.meshes.new(f"Item/{item.is_a()}/{item_id}")
|
||||
item_mesh.BIMMeshProperties.ifc_definition_id = item_id
|
||||
tool.Ifc.link(item, item_mesh)
|
||||
|
||||
item_obj = bpy.data.objects.new(f"Item/{item.is_a()}/{item_id}", item_mesh)
|
||||
item_obj.matrix_world = obj.matrix_world
|
||||
@@ -2871,7 +2916,7 @@ class UpdateItemAttributes(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj = context.active_object
|
||||
tool.Geometry.sync_item_positions()
|
||||
tool.Geometry.update_item_attributes(obj)
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(tool.Geometry.get_geometry_props().representation_obj)
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Root.reload_item_decorator()
|
||||
|
||||
@@ -2888,6 +2933,10 @@ class NameProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
options={"SKIP_SAVE"},
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
extrusion_item_obj: str
|
||||
profile_name: str
|
||||
|
||||
def invoke(self, context, event):
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
@@ -2902,7 +2951,7 @@ class NameProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
extrusion_item_obj = bpy.data.objects[self.extrusion_item_obj]
|
||||
mesh_props = extrusion_item_obj.data.BIMMeshProperties
|
||||
mesh_props = tool.Geometry.get_mesh_props(extrusion_item_obj.data)
|
||||
extrusion = ifc_file.by_id(mesh_props.ifc_definition_id)
|
||||
assert extrusion.is_a("IfcSweptAreaSolid")
|
||||
profile = extrusion.SweptArea
|
||||
@@ -2972,9 +3021,9 @@ class AddMeshlikeItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
props.add_item_object(obj, item)
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Geometry.get_mesh_props(obj.data).ifc_definition_id = item.id()
|
||||
tool.Root.reload_item_decorator()
|
||||
|
||||
|
||||
@@ -3020,10 +3069,10 @@ class AddSweptAreaSolidItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
props.add_item_object(obj, item)
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Geometry.get_mesh_props(obj.data).ifc_definition_id = item.id()
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.import_item_attributes(obj)
|
||||
tool.Root.reload_item_decorator()
|
||||
@@ -3091,10 +3140,10 @@ class AddCurvelikeItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
props.add_item_object(obj, item)
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Geometry.get_mesh_props(obj.data).ifc_definition_id = item.id()
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.import_item_attributes(obj)
|
||||
|
||||
@@ -3140,10 +3189,10 @@ class AddHalfSpaceSolidItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
representation = ifcopenshell.util.representation.resolve_representation(representation)
|
||||
|
||||
representation.Items = list(representation.Items) + [item]
|
||||
tool.Geometry.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
|
||||
obj.name = obj.data.name = f"Item/{item.is_a()}/{item.id()}"
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = item.id()
|
||||
tool.Geometry.get_mesh_props(obj.data).ifc_definition_id = item.id()
|
||||
tool.Geometry.import_item(obj)
|
||||
|
||||
# TODO refactor to core and not rely on selection
|
||||
|
||||
@@ -32,7 +32,7 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import Optional, TYPE_CHECKING, Union
|
||||
from typing import Optional, TYPE_CHECKING, Union, Literal
|
||||
|
||||
|
||||
def get_contexts(self, context):
|
||||
@@ -270,6 +270,9 @@ class BIMObjectGeometryProperties(PropertyGroup):
|
||||
representation_item_layer: str
|
||||
|
||||
|
||||
GeometryMode = Literal["OBJECT", "ITEM", "EDIT"]
|
||||
|
||||
|
||||
class BIMGeometryProperties(PropertyGroup):
|
||||
# Revit workaround
|
||||
should_use_presentation_style_assignment: BoolProperty(name="Force Presentation Style Assignment", default=False)
|
||||
@@ -308,7 +311,7 @@ class BIMGeometryProperties(PropertyGroup):
|
||||
should_force_faceted_brep: bool
|
||||
should_force_triangulation: bool
|
||||
is_changing_mode: bool
|
||||
mode: str
|
||||
mode: GeometryMode
|
||||
representation_obj: Union[bpy.types.Object, None]
|
||||
item_objs: bpy.types.bpy_prop_collection_idprop[RepresentationItemObject]
|
||||
representation_from_object: Union[bpy.types.Object, None]
|
||||
|
||||
@@ -53,9 +53,10 @@ def mode_menu(self, context):
|
||||
UIData.load()
|
||||
ifc_icon = f"{UIData.data['menu_icon_color_mode']}_ifc"
|
||||
row = self.layout.row(align=True)
|
||||
if context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
if props.mode == "EDIT":
|
||||
row.operator("bim.override_mode_set_object", icon="CANCEL", text="Discard Changes").should_save = False
|
||||
row.prop(context.scene.BIMGeometryProperties, "mode", text="", icon_value=bonsai.bim.icons[ifc_icon].icon_id)
|
||||
row.prop(props, "mode", text="", icon_value=bonsai.bim.icons[ifc_icon].icon_id)
|
||||
|
||||
|
||||
def object_menu(self, context):
|
||||
@@ -411,15 +412,18 @@ class BIM_PT_mesh(Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
(obj := context.active_object) is not None
|
||||
and (mesh := obj.data)
|
||||
and isinstance(mesh, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(mesh).ifc_definition_id
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
if not context.active_object.data:
|
||||
return
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
|
||||
row = self.layout.row()
|
||||
row.label(text="Advanced Users Only", icon="ERROR")
|
||||
|
||||
@@ -427,7 +431,7 @@ class BIM_PT_mesh(Panel):
|
||||
|
||||
row = layout.row()
|
||||
text = "Manually Save Representation"
|
||||
if tool.Ifc.is_edited(context.active_object):
|
||||
if tool.Ifc.is_edited(obj):
|
||||
text += "*"
|
||||
row.operator("bim.update_representation", text=text)
|
||||
|
||||
@@ -454,8 +458,8 @@ class BIM_PT_mesh(Panel):
|
||||
op = row.operator("bim.update_representation", text="Convert To Arbitrary Extrusion With Voids")
|
||||
op.ifc_representation_class = "IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids"
|
||||
|
||||
if context.active_object and context.active_object.data:
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if True:
|
||||
mprops = tool.Geometry.get_mesh_props(mesh)
|
||||
row = layout.row()
|
||||
row.operator("bim.get_representation_ifc_parameters")
|
||||
for index, ifc_parameter in enumerate(mprops.ifc_parameters):
|
||||
@@ -477,7 +481,7 @@ class BIM_PT_placement(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object and context.active_object.BIMObjectProperties.ifc_definition_id
|
||||
return (obj := context.active_object) and obj.BIMObjectProperties.ifc_definition_id
|
||||
|
||||
def draw(self, context):
|
||||
if not PlacementData.is_loaded:
|
||||
@@ -571,10 +575,10 @@ class BIM_PT_workarounds(Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
(obj := context.active_object) is not None
|
||||
and (mesh := obj.data)
|
||||
and isinstance(mesh, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(mesh).ifc_definition_id
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
@@ -588,7 +592,7 @@ class BIM_PT_workarounds(Panel):
|
||||
|
||||
|
||||
class BIM_UL_representation_items(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(self, context, layout: bpy.types.UILayout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
icon = "MATERIAL" if item.surface_style else "MESH_UVSPHERE"
|
||||
row = layout.row(align=True)
|
||||
|
||||
@@ -167,7 +167,7 @@ class GeoreferenceData:
|
||||
result["rotation"] = str(round(ifcopenshell.util.geolocation.yaxis2angle(*wcs[:, 1][:2]), 3))
|
||||
result["x"], result["y"], result["z"] = wcs[:, 3][:3]
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
blender_xyz = ifcopenshell.util.geolocation.enh2xyz(
|
||||
result["x"],
|
||||
@@ -193,7 +193,7 @@ class GeoreferenceData:
|
||||
|
||||
@classmethod
|
||||
def local_origin(cls):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.has_blender_offset:
|
||||
return
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
@@ -21,6 +21,7 @@ import blf
|
||||
import gpu
|
||||
import bmesh
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.geolocation
|
||||
import bonsai.tool as tool
|
||||
from math import radians
|
||||
from bpy.types import SpaceView3D
|
||||
@@ -53,7 +54,8 @@ class GeoreferenceDecorator:
|
||||
cls.is_installed = False
|
||||
|
||||
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||
self.scale = bpy.context.scene.BIMGeoreferenceProperties.visualization_scale
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
self.scale = props.visualization_scale
|
||||
content_pos = [v * self.scale for v in content_pos]
|
||||
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||
@@ -64,7 +66,7 @@ class GeoreferenceDecorator:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.model_origin: # If this is empty, no georeferencing data has been loaded.
|
||||
return
|
||||
|
||||
@@ -177,7 +179,7 @@ class GeoreferenceDecorator:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if not props.model_origin: # If this is empty, no georeferencing data has been loaded.
|
||||
return
|
||||
|
||||
@@ -350,7 +352,7 @@ class GeoreferenceDecorator:
|
||||
self.gn_angle = float(GeoreferenceData.data["map_derived_angle"] or 0)
|
||||
self.tn_angle = float(GeoreferenceData.data["true_derived_angle"] or 0)
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
blender_angle = ifcopenshell.util.geolocation.xaxis2angle(
|
||||
float(props.blender_x_axis_abscissa), float(props.blender_x_axis_ordinate)
|
||||
|
||||
@@ -33,15 +33,18 @@ from bpy.props import (
|
||||
)
|
||||
from bonsai.bim.module.georeference.data import GeoreferenceData
|
||||
from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
def get_coordinate_operation_class(self, context):
|
||||
def get_coordinate_operation_class(
|
||||
self: "BIMGeoreferenceProperties", context: bpy.types.Context
|
||||
) -> list[tuple[str, str, str]]:
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
return GeoreferenceData.data["coordinate_operation_class"]
|
||||
|
||||
|
||||
def update_true_north_angle(self, context):
|
||||
def update_true_north_angle(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -54,7 +57,7 @@ def update_true_north_angle(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_true_north_vector(self, context):
|
||||
def update_true_north_vector(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -67,7 +70,7 @@ def update_true_north_vector(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_grid_north_angle(self, context):
|
||||
def update_grid_north_angle(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -81,7 +84,7 @@ def update_grid_north_angle(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_grid_north_vector(self, context):
|
||||
def update_grid_north_vector(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.is_changing_angle:
|
||||
return
|
||||
self.is_changing_angle = True
|
||||
@@ -95,15 +98,15 @@ def update_grid_north_vector(self, context):
|
||||
self.is_changing_angle = False
|
||||
|
||||
|
||||
def update_should_visualise(self, context):
|
||||
def update_should_visualise(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
if self.should_visualise:
|
||||
GeoreferenceDecorator.install(bpy.context)
|
||||
else:
|
||||
GeoreferenceDecorator.uninstall()
|
||||
|
||||
|
||||
def update_blender_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_blender_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -123,8 +126,8 @@ def update_blender_coordinates(self, context):
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
def update_local_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_local_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -147,8 +150,8 @@ def update_local_coordinates(self, context):
|
||||
props.is_updating_coordinates = False
|
||||
|
||||
|
||||
def update_map_coordinates(self, context):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
def update_map_coordinates(self: "BIMGeoreferenceProperties", context: bpy.types.Context) -> None:
|
||||
props = self
|
||||
if props.is_updating_coordinates:
|
||||
return
|
||||
props.is_updating_coordinates = True
|
||||
@@ -248,3 +251,45 @@ class BIMGeoreferenceProperties(PropertyGroup):
|
||||
wcs_y: StringProperty(name="WCS Y", default="0")
|
||||
wcs_z: StringProperty(name="WCS Z", default="0")
|
||||
wcs_rotation: StringProperty(name="WCS Rotation", default="0")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
coordinate_operation_class: str
|
||||
is_changing_angle: bool
|
||||
is_editing: bool
|
||||
is_editing_wcs: bool
|
||||
is_editing_true_north: bool
|
||||
coordinate_operation: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
projected_crs: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
is_updating_coordinates: bool
|
||||
blender_coordinates: str
|
||||
local_coordinates: str
|
||||
map_coordinates: str
|
||||
should_visualise: bool
|
||||
visualization_scale: float
|
||||
grid_north_angle: str
|
||||
x_axis_abscissa: str
|
||||
x_axis_ordinate: str
|
||||
x_axis_is_null: bool
|
||||
|
||||
host_model_origin: str
|
||||
host_model_origin_si: str
|
||||
host_model_project_north: str
|
||||
|
||||
model_origin: str
|
||||
model_origin_si: str
|
||||
model_project_north: str
|
||||
|
||||
has_blender_offset: bool
|
||||
blender_offset_x: str
|
||||
blender_offset_y: str
|
||||
blender_offset_z: str
|
||||
blender_x_axis_abscissa: str
|
||||
blender_x_axis_ordinate: str
|
||||
|
||||
true_north_angle: str
|
||||
true_north_abscissa: str
|
||||
true_north_ordinate: str
|
||||
wcs_x: str
|
||||
wcs_y: str
|
||||
wcs_z: str
|
||||
wcs_rotation: str
|
||||
|
||||
@@ -32,7 +32,7 @@ class BIM_PT_gis(Panel):
|
||||
bl_parent_id = "BIM_PT_tab_geometry"
|
||||
|
||||
def draw_header(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
row = self.layout.row(align=True)
|
||||
icon = "HIDE_OFF" if props.should_visualise else "HIDE_ON"
|
||||
row.label(text="") # empty text occupies the left of the row
|
||||
@@ -43,7 +43,7 @@ class BIM_PT_gis(Panel):
|
||||
def draw(self, context):
|
||||
self.layout.use_property_split = True
|
||||
self.layout.use_property_decorate = False
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
@@ -54,7 +54,7 @@ class BIM_PT_gis(Panel):
|
||||
self.draw_ui(context)
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Projected CRS", icon="WORLD")
|
||||
row.operator("bim.edit_georeferencing", icon="CHECKMARK", text="")
|
||||
@@ -87,7 +87,7 @@ class BIM_PT_gis(Panel):
|
||||
draw_attribute(attribute, self.layout.row())
|
||||
|
||||
def draw_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
row = self.layout.row()
|
||||
@@ -149,7 +149,7 @@ class BIM_PT_gis_true_north(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
self.props = context.scene.BIMGeoreferenceProperties
|
||||
self.props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if self.props.is_editing_true_north:
|
||||
self.draw_editable_ui(context)
|
||||
@@ -200,7 +200,7 @@ class BIM_PT_gis_blender(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row()
|
||||
@@ -233,7 +233,7 @@ class BIM_PT_gis_wcs(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.is_editing_wcs:
|
||||
self.draw_editable_ui(context)
|
||||
@@ -263,7 +263,7 @@ class BIM_PT_gis_wcs(Panel):
|
||||
row.operator("bim.enable_editing_wcs", icon="GREASEPENCIL", text="")
|
||||
|
||||
def draw_editable_ui(self, context):
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="World Coordinate System", icon="EMPTY_ARROWS")
|
||||
@@ -293,7 +293,7 @@ class BIM_PT_gis_calculator(Panel):
|
||||
if not GeoreferenceData.is_loaded:
|
||||
GeoreferenceData.load()
|
||||
|
||||
props = context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
if props.has_blender_offset:
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -147,7 +147,7 @@ class AssignPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"layer.assign_layer",
|
||||
self.file,
|
||||
**{
|
||||
"items": [self.file.by_id(item.BIMMeshProperties.ifc_definition_id)],
|
||||
"items": [self.file.by_id(tool.Geometry.get_mesh_props(item).ifc_definition_id)],
|
||||
"layer": self.file.by_id(self.layer),
|
||||
},
|
||||
)
|
||||
@@ -164,15 +164,10 @@ class UnassignPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data
|
||||
self.file = IfcStore.get_file()
|
||||
ifcopenshell.api.run(
|
||||
"layer.unassign_layer",
|
||||
self.file,
|
||||
**{
|
||||
"items": [self.file.by_id(item.BIMMeshProperties.ifc_definition_id)],
|
||||
"layer": self.file.by_id(self.layer),
|
||||
},
|
||||
)
|
||||
ifc_file = tool.Ifc.get()
|
||||
representation = tool.Geometry.get_data_representation(item)
|
||||
assert representation
|
||||
ifcopenshell.api.layer.unassign_layer(ifc_file, items=[representation], layer=ifc_file.by_id(self.layer))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ class BIM_UL_layers(UIList):
|
||||
row.label(text=item.name)
|
||||
|
||||
if context.active_object and isinstance(context.active_object.data, Mesh):
|
||||
mprops = context.active_object.data.BIMMeshProperties
|
||||
if item.ifc_definition_id in LayersData.data["active_layers"]:
|
||||
op = row.operator("bim.unassign_presentation_layer", text="", icon="KEYFRAME_HLT", emboss=False)
|
||||
op.layer = item.ifc_definition_id
|
||||
|
||||
@@ -282,8 +282,9 @@ class ObjectMaterialData:
|
||||
if item.is_a("IfcMaterialLayer"):
|
||||
total_thickness = item.LayerThickness
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
props = tool.Drawing.get_document_props()
|
||||
if unit_system == "IMPERIAL":
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
precision = props.imperial_precision
|
||||
else:
|
||||
precision = None
|
||||
formatted_thickness = format_distance(
|
||||
|
||||
@@ -351,9 +351,10 @@ class BIM_PT_object_material(Panel):
|
||||
if ObjectMaterialData.data["total_thickness"]:
|
||||
total_thickness = ObjectMaterialData.data["total_thickness"]
|
||||
unit_system = bpy.context.scene.unit_settings.system
|
||||
props = tool.Drawing.get_document_props()
|
||||
|
||||
if unit_system == "IMPERIAL":
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
precision = props.imperial_precision
|
||||
else:
|
||||
precision = None
|
||||
formatted_thickness = format_distance(
|
||||
|
||||
@@ -299,7 +299,7 @@ class DrawSystemArrows(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Blender.select_and_activate_single_object(context, curve)
|
||||
|
||||
def get_absolute_matrix(self, matrix):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
matrix = np.array(
|
||||
ifcopenshell.util.geolocation.global2local(
|
||||
|
||||
@@ -236,9 +236,9 @@ class FilledOpeningGenerator:
|
||||
voided_elements = ifcopenshell.util.element.get_parts(voided_element) or [voided_element]
|
||||
for voided_element in voided_elements:
|
||||
voided_obj = tool.Ifc.get_object(voided_element)
|
||||
if not voided_obj.data:
|
||||
representation = tool.Geometry.get_active_representation(voided_obj)
|
||||
if not representation:
|
||||
continue
|
||||
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
bonsai.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
@@ -465,10 +465,13 @@ class AddBoolean(Operator, tool.Ifc.Operator):
|
||||
self.report({"INFO"}, "At least two representation items must be selected to add a boolean.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
|
||||
first_item = tool.Ifc.get().by_id(first_obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
second_items = [tool.Ifc.get().by_id(o.data.BIMMeshProperties.ifc_definition_id) for o in second_objs]
|
||||
first_item = tool.Geometry.get_active_representation(first_obj)
|
||||
assert first_item
|
||||
second_items = [
|
||||
representation for o in second_objs if (representation := tool.Geometry.get_active_representation(o))
|
||||
]
|
||||
booleans = ifcopenshell.api.geometry.add_boolean(tool.Ifc.get(), first_item, second_items, props.operator)
|
||||
|
||||
rep_obj = tool.Geometry.get_geometry_props().representation_obj
|
||||
@@ -664,6 +667,7 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
||||
def edit_openings(
|
||||
self, building_objs: set[bpy.types.Object], opening_elements: set[ifcopenshell.entity_instance]
|
||||
) -> None:
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
objects_to_remove: set[bpy.types.Object] = set()
|
||||
for opening_element in opening_elements:
|
||||
opening_obj = tool.Ifc.get_object(opening_element)
|
||||
@@ -690,8 +694,8 @@ class EditOpenings(Operator, tool.Ifc.Operator):
|
||||
self.get_all_building_objects_of_similar_openings(opening_element)
|
||||
) # NB this has nothing to do with clone similar_opening
|
||||
tool.Ifc.unlink(element=opening_element)
|
||||
if bpy.context.scene.BIMGeometryProperties.representation_obj == opening_obj:
|
||||
bpy.context.scene.BIMGeometryProperties.representation_obj = None
|
||||
if props.representation_obj == opening_obj:
|
||||
props.representation_obj = None
|
||||
objects_to_remove.add(opening_obj)
|
||||
tool.Blender.remove_data_blocks(objects_to_remove, remove_unused_data=True)
|
||||
|
||||
@@ -817,11 +821,11 @@ class RemoveBoolean(Operator, tool.Ifc.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
return props.active_boolean
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
ifcopenshell.api.geometry.remove_boolean(
|
||||
tool.Ifc.get(), tool.Ifc.get().by_id(props.active_boolean.ifc_definition_id)
|
||||
)
|
||||
@@ -841,7 +845,7 @@ class SelectBoolean(Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
return props.active_boolean
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -850,9 +854,9 @@ class SelectBoolean(Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
queue = [tool.Ifc.get().by_id(props.active_boolean.ifc_definition_id)]
|
||||
items = {i.ifc_definition_id: i.obj for i in context.scene.BIMGeometryProperties.item_objs}
|
||||
items = {i.ifc_definition_id: i.obj for i in tool.Geometry.get_geometry_props().item_objs}
|
||||
while queue:
|
||||
item = queue.pop()
|
||||
if item.is_a("IfcBooleanResult"):
|
||||
@@ -911,11 +915,12 @@ class DecorationsHandler:
|
||||
gpu.state.point_size_set(6)
|
||||
gpu.state.blend_set("ALPHA")
|
||||
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
for opening in props.openings:
|
||||
obj = opening.obj
|
||||
if context.scene.BIMGeometryProperties.representation_obj == obj:
|
||||
if gprops.representation_obj == obj:
|
||||
# We are editing the representation of the opening :
|
||||
for item in context.scene.BIMGeometryProperties.item_objs:
|
||||
for item in gprops.item_objs:
|
||||
if item.obj.mode == "EDIT":
|
||||
obj = item.obj
|
||||
break
|
||||
|
||||
@@ -22,6 +22,7 @@ import bmesh
|
||||
import mathutils.geometry
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.util.type
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
@@ -265,7 +266,7 @@ class DumbProfileRegenerator:
|
||||
|
||||
def _regenerate_from_type(self, related_object: ifcopenshell.entity_instance) -> None:
|
||||
obj = tool.Ifc.get_object(related_object)
|
||||
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
if not obj or not tool.Geometry.get_active_representation(obj):
|
||||
return
|
||||
DumbProfileRecalculator().recalculate([obj])
|
||||
|
||||
@@ -501,7 +502,9 @@ class DumbProfileJoiner:
|
||||
"geometry.assign_representation", tool.Ifc.get(), product=element, representation=new_axis
|
||||
)
|
||||
|
||||
def get_placement_axes(body_representation):
|
||||
def get_placement_axes(
|
||||
body_representation: Union[ifcopenshell.entity_instance, None],
|
||||
) -> Union[tuple[tuple[float, float, float], tuple[float, float, float]], tuple[None, None]]:
|
||||
if not body_representation:
|
||||
return None, None
|
||||
extrusion = tool.Model.get_extrusion(body_representation)
|
||||
@@ -513,8 +516,7 @@ class DumbProfileJoiner:
|
||||
return ((0.0, 0.0, 1.0), (1.0, 0.0, 0.0))
|
||||
|
||||
old_body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
new_body = ifcopenshell.api.run(
|
||||
"geometry.add_profile_representation",
|
||||
new_body = ifcopenshell.api.geometry.add_profile_representation(
|
||||
tool.Ifc.get(),
|
||||
context=self.body_context,
|
||||
profile=self.profile,
|
||||
@@ -527,8 +529,9 @@ class DumbProfileJoiner:
|
||||
if old_body:
|
||||
for inverse in tool.Ifc.get().get_inverse(old_body):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_body, new_body)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_body.id())
|
||||
obj.data.name = f"{self.body_context.id()}/{new_body.id()}"
|
||||
assert isinstance(mesh := obj.data, bpy.types.Mesh)
|
||||
tool.Ifc.link(new_body, mesh)
|
||||
mesh.name = tool.Loader.get_mesh_name(new_body)
|
||||
bonsai.core.geometry.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_body)
|
||||
else:
|
||||
ifcopenshell.api.run(
|
||||
|
||||
@@ -259,7 +259,7 @@ class DumbSlabPlaner:
|
||||
self, related_object: ifcopenshell.entity_instance, layer_set_direction: Optional[str], new_thickness: float
|
||||
) -> None:
|
||||
obj = tool.Ifc.get_object(related_object)
|
||||
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
if not obj or not tool.Geometry.get_active_representation(obj):
|
||||
return
|
||||
|
||||
material = ifcopenshell.util.element.get_material(related_object)
|
||||
|
||||
@@ -908,7 +908,7 @@ class DumbWallPlaner:
|
||||
self, related_object: ifcopenshell.entity_instance, layer_set_direction: Optional[str]
|
||||
) -> None:
|
||||
obj = tool.Ifc.get_object(related_object)
|
||||
if not obj or not obj.data or not obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
if not obj or not tool.Geometry.get_active_representation(obj):
|
||||
return
|
||||
|
||||
material = ifcopenshell.util.element.get_material(related_object)
|
||||
@@ -1331,7 +1331,9 @@ class DumbWallJoiner:
|
||||
axis = body = tool.Model.get_wall_axis(obj)["reference"]
|
||||
self.axis = copy.deepcopy(axis)
|
||||
self.body = copy.deepcopy(body)
|
||||
extrusion_data = self.get_extrusion_data(tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id))
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
assert representation
|
||||
extrusion_data = self.get_extrusion_data(representation)
|
||||
height = extrusion_data["height"]
|
||||
x_angle = extrusion_data["x_angle"]
|
||||
self.clippings = []
|
||||
@@ -1410,8 +1412,9 @@ class DumbWallJoiner:
|
||||
if old_body:
|
||||
for inverse in tool.Ifc.get().get_inverse(old_body):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, old_body, new_body)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_body.id())
|
||||
obj.data.name = f"{self.body_context.id()}/{new_body.id()}"
|
||||
assert isinstance(mesh := obj.data, bpy.types.Mesh)
|
||||
tool.Ifc.link(new_body, mesh)
|
||||
mesh.name = tool.Loader.get_mesh_name(new_body)
|
||||
bonsai.core.geometry.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_body)
|
||||
else:
|
||||
ifcopenshell.api.run(
|
||||
@@ -1708,10 +1711,11 @@ class DumbWallJoiner:
|
||||
|
||||
return True
|
||||
|
||||
def clip(self, wall1, slab2):
|
||||
def clip(self, wall1: bpy.types.Object, slab2: bpy.types.Object) -> float:
|
||||
"""returns height of the clipped wall, adds clipping plane to `clippings`"""
|
||||
element1 = tool.Ifc.get_entity(wall1)
|
||||
element2 = tool.Ifc.get_entity(slab2)
|
||||
assert element1 and element2
|
||||
|
||||
layers1 = tool.Model.get_material_layer_parameters(element1)
|
||||
axis1 = tool.Model.get_wall_axis(wall1, layers1)
|
||||
@@ -1719,7 +1723,9 @@ class DumbWallJoiner:
|
||||
bases = [axis1["base"][0].to_3d(), axis1["base"][1].to_3d(), axis1["side"][0].to_3d(), axis1["side"][1].to_3d()]
|
||||
bases = [Vector((v[0], v[1], wall1.matrix_world.translation.z)) for v in bases] # add wall Z location
|
||||
|
||||
extrusion = self.get_extrusion_data(tool.Ifc.get().by_id(wall1.data.BIMMeshProperties.ifc_definition_id))
|
||||
representation = tool.Geometry.get_active_representation(wall1)
|
||||
assert representation
|
||||
extrusion = self.get_extrusion_data(representation)
|
||||
wall_dir = wall1.matrix_world.to_quaternion() @ extrusion["direction"]
|
||||
|
||||
slab_pt = slab2.matrix_world @ Vector((0, 0, 0))
|
||||
|
||||
@@ -96,7 +96,8 @@ class BimTool(WorkSpaceTool):
|
||||
def draw_settings(
|
||||
cls, context: bpy.types.Context, layout: bpy.types.UILayout, ws_tool: bpy.types.WorkSpaceTool
|
||||
) -> None:
|
||||
if context.scene.BIMGeometryProperties.mode == "ITEM":
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
if props.mode == "ITEM":
|
||||
EditItemUI.draw(context, layout)
|
||||
elif (
|
||||
active_ifc_object := (context.active_object and tool.Ifc.get_entity(context.active_object))
|
||||
@@ -430,7 +431,7 @@ class EditItemUI:
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
|
||||
mesh_props = obj.data.BIMMeshProperties
|
||||
mesh_props = tool.Geometry.get_mesh_props(obj.data)
|
||||
if AuthoringData.data["is_representation_item_swept_solid"]:
|
||||
# TODO: support EndSweptArea for IfcRevolvedAreaSolidTapered,
|
||||
# will need to add second attribute for this.
|
||||
@@ -440,10 +441,10 @@ class EditItemUI:
|
||||
op = row.operator("bim.name_profile", text="", icon="TAG")
|
||||
op.extrusion_item_obj = obj.name
|
||||
|
||||
for item_attribute in obj.data.BIMMeshProperties.item_attributes:
|
||||
for item_attribute in mesh_props.item_attributes:
|
||||
row = cls.layout.row()
|
||||
draw_attribute(item_attribute, cls.layout)
|
||||
if len(obj.data.BIMMeshProperties.item_attributes) or AuthoringData.data["is_representation_item_swept_solid"]:
|
||||
if len(mesh_props.item_attributes) or AuthoringData.data["is_representation_item_swept_solid"]:
|
||||
row = cls.layout.row()
|
||||
row.operator("bim.update_item_attributes", icon="FILE_REFRESH", text="")
|
||||
|
||||
@@ -1136,7 +1137,8 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
row.prop(self, "z")
|
||||
|
||||
def hotkey_S_A(self):
|
||||
if bpy.context.scene.BIMGeometryProperties.mode == "ITEM":
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
if gprops.mode == "ITEM":
|
||||
bpy.ops.wm.call_menu(name="BIM_MT_add_representation_item")
|
||||
return
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class ProfileData:
|
||||
obj = bpy.context.active_object
|
||||
return (
|
||||
obj
|
||||
and obj.data
|
||||
and hasattr(obj.data, "BIMMeshProperties")
|
||||
and obj.data.BIMMeshProperties.subshape_type == "PROFILE"
|
||||
and (data := obj.data)
|
||||
and isinstance(data, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(data).subshape_type == "PROFILE"
|
||||
)
|
||||
|
||||
@@ -248,7 +248,12 @@ class EnableEditingArbitraryProfile(bpy.types.Operator):
|
||||
|
||||
def disable_editing_arbitrary_profile(context):
|
||||
obj = context.active_object
|
||||
if obj and obj.type == "MESH" and obj.data and obj.data.BIMMeshProperties.subshape_type == "PROFILE":
|
||||
if (
|
||||
obj
|
||||
and (mesh := obj.data)
|
||||
and isinstance(mesh, bpy.types.Mesh)
|
||||
and tool.Geometry.get_mesh_props(mesh).subshape_type == "PROFILE"
|
||||
):
|
||||
ProfileDecorator.uninstall()
|
||||
bpy.ops.object.mode_set(mode="OBJECT")
|
||||
profile_mesh = obj.data
|
||||
|
||||
@@ -31,7 +31,8 @@ from typing import Union
|
||||
|
||||
@persistent
|
||||
def toggle_decorations_on_load(*args):
|
||||
if bpy.context.scene.BIMProjectProperties.clipping_planes:
|
||||
props = tool.Project.get_project_props()
|
||||
if props.clipping_planes:
|
||||
ClippingPlaneDecorator.install(bpy.context)
|
||||
else:
|
||||
ClippingPlaneDecorator.uninstall()
|
||||
@@ -99,7 +100,7 @@ class ProjectDecorator:
|
||||
selected_edges = []
|
||||
selected_tris = []
|
||||
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
try:
|
||||
obj = props.queried_obj
|
||||
selected_vertices = obj["selected_vertices"]
|
||||
@@ -171,7 +172,8 @@ class ClippingPlaneDecorator:
|
||||
unselected_edges = []
|
||||
unselected_tris = []
|
||||
|
||||
for clipping_plane in context.scene.BIMProjectProperties.clipping_planes:
|
||||
props = tool.Project.get_project_props()
|
||||
for clipping_plane in props.clipping_planes:
|
||||
obj = clipping_plane.obj
|
||||
if not obj or not obj.data:
|
||||
continue
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bpy.types import GizmoGroup
|
||||
from mathutils import Matrix
|
||||
|
||||
@@ -32,11 +33,12 @@ class ClippingPlane(GizmoGroup):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.object
|
||||
props = tool.Project.get_project_props()
|
||||
return (
|
||||
context.selected_objects
|
||||
and obj
|
||||
and obj.name.startswith("ClippingPlane")
|
||||
and obj in [sp.obj for sp in context.scene.BIMProjectProperties.clipping_planes]
|
||||
and obj in [sp.obj for sp in props.clipping_planes]
|
||||
)
|
||||
|
||||
def setup(self, context):
|
||||
|
||||
@@ -75,35 +75,36 @@ class NewProject(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
bpy.ops.wm.read_homefile()
|
||||
pprops = tool.Project.get_project_props()
|
||||
|
||||
if self.preset == "metric_m":
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||
pprops.export_schema = "IFC4"
|
||||
bpy.context.scene.unit_settings.system = "METRIC"
|
||||
bpy.context.scene.unit_settings.length_unit = "METERS"
|
||||
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
pprops.template_file = "0"
|
||||
elif self.preset == "metric_mm":
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||
pprops.export_schema = "IFC4"
|
||||
bpy.context.scene.unit_settings.system = "METRIC"
|
||||
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
|
||||
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
pprops.template_file = "0"
|
||||
elif self.preset == "imperial_ft":
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||
pprops.export_schema = "IFC4"
|
||||
bpy.context.scene.unit_settings.system = "IMPERIAL"
|
||||
bpy.context.scene.unit_settings.length_unit = "FEET"
|
||||
bpy.context.scene.BIMProperties.area_unit = "square foot"
|
||||
bpy.context.scene.BIMProperties.volume_unit = "cubic foot"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
pprops.template_file = "0"
|
||||
elif self.preset == "demo":
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4"
|
||||
pprops.export_schema = "IFC4"
|
||||
bpy.context.scene.unit_settings.system = "METRIC"
|
||||
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
|
||||
bpy.context.scene.BIMProperties.area_unit = "SQUARE_METRE"
|
||||
bpy.context.scene.BIMProperties.volume_unit = "CUBIC_METRE"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "IFC4 Demo Template.ifc"
|
||||
pprops.template_file = "IFC4 Demo Template.ifc"
|
||||
|
||||
if self.preset != "wizard":
|
||||
bpy.ops.bim.create_project()
|
||||
@@ -126,7 +127,7 @@ class CreateProject(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
template = None if props.template_file == "0" else props.template_file
|
||||
if tool.Blender.is_default_scene():
|
||||
for obj in bpy.data.objects:
|
||||
@@ -592,7 +593,8 @@ class AppendLibraryElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.import_presentation_style_from_ifc(element, context)
|
||||
|
||||
try:
|
||||
context.scene.BIMProjectProperties.library_elements[self.prop_index].is_appended = True
|
||||
props = tool.Project.get_project_props()
|
||||
props.library_elements[self.prop_index].is_appended = True
|
||||
except:
|
||||
# TODO Remove this terrible code when I refactor this into the core
|
||||
pass
|
||||
@@ -771,8 +773,8 @@ class EnableEditingHeader(bpy.types.Operator):
|
||||
return IfcStore.get_file()
|
||||
|
||||
def execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMProjectProperties
|
||||
self.file = tool.Ifc.get()
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_editing = True
|
||||
|
||||
mvd = "".join(IfcStore.get_file().wrapped_data.header.file_description.description)
|
||||
@@ -818,8 +820,8 @@ class EditHeader(bpy.types.Operator):
|
||||
return result
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = IfcStore.get_file()
|
||||
props = context.scene.BIMProjectProperties
|
||||
self.file = tool.Ifc.get()
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_editing = True
|
||||
|
||||
self.file.wrapped_data.header.file_description.description = (f"ViewDefinition[{props.mvd}]",)
|
||||
@@ -860,7 +862,8 @@ class DisableEditingHeader(bpy.types.Operator):
|
||||
bl_description = "Cancel unsaved header information"
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMProjectProperties.is_editing = False
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_editing = False
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -983,9 +986,10 @@ class LoadProject(bpy.types.Operator, IFCFileSelector):
|
||||
f"Error loading IFC file from filepath '{filepath}'. See logs above in the system console for the details.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
context.scene.BIMProjectProperties.is_loading = True
|
||||
context.scene.BIMProjectProperties.total_elements = len(tool.Ifc.get().by_type("IfcElement"))
|
||||
context.scene.BIMProjectProperties.use_relative_project_path = self.use_relative_path
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_loading = True
|
||||
props.total_elements = len(tool.Ifc.get().by_type("IfcElement"))
|
||||
props.use_relative_project_path = self.use_relative_path
|
||||
tool.Blender.register_toolbar()
|
||||
tool.Project.add_recent_ifc_project(self.get_filepath_abs())
|
||||
|
||||
@@ -1051,8 +1055,8 @@ class LoadProjectElements(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMProjectProperties
|
||||
self.file = IfcStore.get_file()
|
||||
self.props = tool.Project.get_project_props()
|
||||
self.file = tool.Ifc.get()
|
||||
bonsai.bim.schema.reload(self.file.schema_identifier)
|
||||
start = time.time()
|
||||
logger = logging.getLogger("ImportIFC")
|
||||
@@ -1084,7 +1088,8 @@ class LoadProjectElements(bpy.types.Operator):
|
||||
ifc_importer.execute()
|
||||
settings.logger.info("Import finished in {:.2f} seconds".format(time.time() - start))
|
||||
print("Import finished in {:.2f} seconds".format(time.time() - start))
|
||||
context.scene.BIMProjectProperties.is_loading = False
|
||||
props = tool.Project.get_project_props()
|
||||
props.is_loading = False
|
||||
|
||||
tool.Project.load_pset_templates()
|
||||
tool.Project.load_default_thumbnails()
|
||||
@@ -1156,7 +1161,8 @@ class ToggleFilterCategories(bpy.types.Operator):
|
||||
should_select: bpy.props.BoolProperty(name="Should Select", default=True)
|
||||
|
||||
def execute(self, context):
|
||||
for filter_category in context.scene.BIMProjectProperties.filter_categories:
|
||||
props = tool.Project.get_project_props()
|
||||
for filter_category in props.filter_categories:
|
||||
filter_category.is_selected = self.should_select
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -1183,7 +1189,7 @@ class LinkIfc(bpy.types.Operator):
|
||||
directory: str
|
||||
|
||||
def draw(self, context):
|
||||
pprops = context.scene.BIMProjectProperties
|
||||
pprops = tool.Project.get_project_props()
|
||||
row = self.layout.row()
|
||||
row.prop(self, "use_relative_path")
|
||||
row = self.layout.row()
|
||||
@@ -1204,7 +1210,8 @@ class LinkIfc(bpy.types.Operator):
|
||||
if bpy.data.filepath and filepath.samefile(bpy.data.filepath):
|
||||
self.report({"INFO"}, "Can't link the current .blend file")
|
||||
continue
|
||||
new = context.scene.BIMProjectProperties.links.add()
|
||||
props = tool.Project.get_project_props()
|
||||
new = props.links.add()
|
||||
filepath = tool.Ifc.get_uri(filepath, use_relative_path=self.use_relative_path)
|
||||
new.name = filepath
|
||||
status = bpy.ops.bim.load_link(filepath=filepath, use_cache=self.use_cache)
|
||||
@@ -1235,9 +1242,10 @@ class UnlinkIfc(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
filepath = Path(self.filepath).as_posix()
|
||||
bpy.ops.bim.unload_link(filepath=filepath)
|
||||
index = context.scene.BIMProjectProperties.links.find(filepath)
|
||||
props = tool.Project.get_project_props()
|
||||
index = props.links.find(filepath)
|
||||
if index != -1:
|
||||
context.scene.BIMProjectProperties.links.remove(index)
|
||||
props.links.remove(index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1257,8 +1265,9 @@ class UnloadLink(bpy.types.Operator):
|
||||
if tool.Blender.ensure_blender_path_is_abs(Path(library.filepath)) == filepath:
|
||||
bpy.data.libraries.remove(library)
|
||||
|
||||
links = context.scene.BIMProjectProperties.links
|
||||
link = links.get(self.filepath)
|
||||
props = tool.Project.get_project_props()
|
||||
links = props.links
|
||||
link = links[self.filepath]
|
||||
# Let's assume that user might delete it.
|
||||
if empty_handle := link.empty_handle:
|
||||
bpy.data.objects.remove(empty_handle)
|
||||
@@ -1267,7 +1276,7 @@ class UnloadLink(bpy.types.Operator):
|
||||
if not any([l.is_loaded for l in links]):
|
||||
ProjectDecorator.uninstall()
|
||||
# we make sure we don't draw queried object from the file that was just unlinked
|
||||
elif queried_obj := context.scene.BIMProjectProperties.queried_obj:
|
||||
elif queried_obj := props.queried_obj:
|
||||
queried_filepath = Path(queried_obj["ifc_filepath"])
|
||||
if queried_filepath == filepath:
|
||||
ProjectDecorator.uninstall()
|
||||
@@ -1299,7 +1308,7 @@ class LoadLink(bpy.types.Operator):
|
||||
def link_blend(self, filepath: Path) -> None:
|
||||
with bpy.data.libraries.load(str(filepath), link=True) as (data_from, data_to):
|
||||
data_to.scenes = data_from.scenes
|
||||
link = bpy.context.scene.BIMProjectProperties.links[self.filepath]
|
||||
link = tool.Project.get_project_props().links[self.filepath]
|
||||
for scene in bpy.data.scenes:
|
||||
if not scene.library or Path(scene.library.filepath) != filepath:
|
||||
continue
|
||||
@@ -1325,13 +1334,13 @@ class LoadLink(bpy.types.Operator):
|
||||
|
||||
if not blend_filepath.exists():
|
||||
pprops = tool.Project.get_project_props()
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
|
||||
code = f"""
|
||||
import bpy
|
||||
|
||||
def run():
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
# Our model origin becomes their host model origin
|
||||
gprops.host_model_origin = "{gprops.model_origin}"
|
||||
gprops.host_model_origin_si = "{gprops.model_origin_si}"
|
||||
@@ -1342,7 +1351,7 @@ def run():
|
||||
gprops.blender_offset_z = "{gprops.blender_offset_z}"
|
||||
gprops.blender_x_axis_abscissa = "{gprops.blender_x_axis_abscissa}"
|
||||
gprops.blender_x_axis_ordinate = "{gprops.blender_x_axis_ordinate}"
|
||||
pprops = bpy.context.scene.BIMProjectProperties
|
||||
pprops = tool.Project.get_project_props()
|
||||
pprops.distance_limit = {pprops.distance_limit}
|
||||
pprops.false_origin_mode = "{pprops.false_origin_mode}"
|
||||
pprops.false_origin = "{pprops.false_origin}"
|
||||
@@ -1397,7 +1406,7 @@ except Exception as e:
|
||||
with open(json_filepath, "r") as f:
|
||||
data = json.load(f)
|
||||
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
for prop in ("model_origin", "model_origin_si", "model_project_north"):
|
||||
if (value := data.get(prop, None)) is not None:
|
||||
setattr(gprops, prop, value)
|
||||
@@ -1433,8 +1442,8 @@ class ToggleLinkSelectability(bpy.types.Operator):
|
||||
link: bpy.props.StringProperty(name="Linked IFC Filepath")
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
link = props.links.get(self.link)
|
||||
props = tool.Project.get_project_props()
|
||||
link = props.links[self.link]
|
||||
self.library_filepath = tool.Blender.ensure_blender_path_is_abs(Path(self.link).with_suffix(".ifc.cache.blend"))
|
||||
link.is_selectable = (is_selectable := not link.is_selectable)
|
||||
for collection in self.get_linked_collections():
|
||||
@@ -1460,8 +1469,8 @@ class ToggleLinkVisibility(bpy.types.Operator):
|
||||
mode: bpy.props.EnumProperty(name="Visibility Mode", items=((i, i, "") for i in ("WIREFRAME", "VISIBLE")))
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
link = props.links.get(self.link)
|
||||
props = tool.Project.get_project_props()
|
||||
link = props.links[self.link]
|
||||
self.library_filepath = tool.Blender.ensure_blender_path_is_abs(Path(self.link).with_suffix(".ifc.cache.blend"))
|
||||
if self.mode == "WIREFRAME":
|
||||
self.toggle_wireframe(link)
|
||||
@@ -1507,7 +1516,7 @@ class SelectLinkHandle(bpy.types.Operator):
|
||||
index: bpy.props.IntProperty(name="Link Index")
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
link = props.links[self.index]
|
||||
handle = link.empty_handle
|
||||
if not handle:
|
||||
@@ -1549,7 +1558,7 @@ class ExportIFC(bpy.types.Operator):
|
||||
bpy.ops.wm.save_mainfile("INVOKE_DEFAULT")
|
||||
return {"FINISHED"}
|
||||
|
||||
self.use_relative_path = context.scene.BIMProjectProperties.use_relative_project_path
|
||||
self.use_relative_path = tool.Project.get_project_props().use_relative_project_path
|
||||
if (filepath := context.scene.BIMProperties.ifc_file) and not self.should_save_as:
|
||||
self.filepath = str(tool.Blender.ensure_blender_path_is_abs(Path(filepath)))
|
||||
return self.execute(context)
|
||||
@@ -1564,7 +1573,7 @@ class ExportIFC(bpy.types.Operator):
|
||||
return {"RUNNING_MODAL"}
|
||||
|
||||
def execute(self, context):
|
||||
project_props = context.scene.BIMProjectProperties
|
||||
project_props = tool.Project.get_project_props()
|
||||
project_props.use_relative_project_path = self.use_relative_path
|
||||
if project_props.should_disable_undo_on_save:
|
||||
old_history_size = tool.Ifc.get().history_size
|
||||
@@ -1612,10 +1621,12 @@ class ExportIFC(bpy.types.Operator):
|
||||
# New project created in Bonsai should be in recent projects too.
|
||||
tool.Project.add_recent_ifc_project(Path(output_file))
|
||||
scene = context.scene
|
||||
if not scene.DocProperties.ifc_files:
|
||||
new = scene.DocProperties.ifc_files.add()
|
||||
props = tool.Drawing.get_document_props()
|
||||
if not props.ifc_files:
|
||||
new = props.ifc_files.add()
|
||||
new.name = output_file
|
||||
if context.scene.BIMProjectProperties.use_relative_project_path and bpy.data.is_saved:
|
||||
props = tool.Project.get_project_props()
|
||||
if props.use_relative_project_path and bpy.data.is_saved:
|
||||
output_file = os.path.relpath(output_file, bpy.path.abspath("//"))
|
||||
if scene.BIMProperties.ifc_file != output_file and extension not in ("ifczip", "ifcjson"):
|
||||
scene.BIMProperties.ifc_file = output_file
|
||||
@@ -1660,8 +1671,8 @@ class LoadLinkedProject(bpy.types.Operator):
|
||||
|
||||
start = time.time()
|
||||
|
||||
pprops = bpy.context.scene.BIMProjectProperties
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
pprops = tool.Project.get_project_props()
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
|
||||
self.filepath = Path(self.filepath).as_posix()
|
||||
print("Processing", self.filepath)
|
||||
@@ -1875,7 +1886,7 @@ class LoadLinkedProject(bpy.types.Operator):
|
||||
mesh = bpy.data.meshes.new("Mesh")
|
||||
|
||||
geometry = shape.geometry
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
if (
|
||||
gprops.has_blender_offset
|
||||
and geometry.verts
|
||||
@@ -1981,9 +1992,8 @@ class QueryLinkedElement(bpy.types.Operator):
|
||||
from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d
|
||||
|
||||
LinksData.linked_data = {}
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
props.queried_obj = None
|
||||
props.quried_obj_root = None
|
||||
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == "PROPERTIES":
|
||||
@@ -2119,6 +2129,7 @@ class AppendInspectedLinkedElement(AppendLibraryElement):
|
||||
def _execute(self, context):
|
||||
from bonsai.bim.module.project.data import LinksData
|
||||
|
||||
props = tool.Project.get_project_props()
|
||||
if not LinksData.linked_data:
|
||||
self.report({"INFO"}, "No linked element found.")
|
||||
return {"CANCELLED"}
|
||||
@@ -2128,7 +2139,7 @@ class AppendInspectedLinkedElement(AppendLibraryElement):
|
||||
self.report({"INFO"}, "Cannot find Global Id for element.")
|
||||
return {"CANCELLED"}
|
||||
|
||||
queried_obj = context.scene.BIMProjectProperties.queried_obj
|
||||
queried_obj = props.queried_obj
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
linked_ifc_file: ifcopenshell.file
|
||||
@@ -2275,34 +2286,36 @@ class RefreshClippingPlanes(bpy.types.Operator):
|
||||
|
||||
def modal(self, context, event):
|
||||
should_refresh = False
|
||||
props = tool.Project.get_project_props()
|
||||
|
||||
self.clean_deleted_planes(context)
|
||||
|
||||
for clipping_plane in context.scene.BIMProjectProperties.clipping_planes:
|
||||
for clipping_plane in props.clipping_planes:
|
||||
if clipping_plane.obj and self.is_moved(clipping_plane.obj):
|
||||
should_refresh = True
|
||||
break
|
||||
|
||||
total_planes = len(context.scene.BIMProjectProperties.clipping_planes)
|
||||
total_planes = len(props.clipping_planes)
|
||||
if should_refresh or total_planes != self.total_planes:
|
||||
self.refresh_clipping_planes(context)
|
||||
for clipping_plane in context.scene.BIMProjectProperties.clipping_planes:
|
||||
for clipping_plane in props.clipping_planes:
|
||||
if clipping_plane.obj:
|
||||
tool.Geometry.record_object_position(clipping_plane.obj)
|
||||
self.total_planes = total_planes
|
||||
return {"PASS_THROUGH"}
|
||||
|
||||
def clean_deleted_planes(self, context):
|
||||
def clean_deleted_planes(self, context: bpy.types.Context) -> None:
|
||||
props = tool.Project.get_project_props()
|
||||
while True:
|
||||
for i, clipping_plane in enumerate(context.scene.BIMProjectProperties.clipping_planes):
|
||||
for i, clipping_plane in enumerate(props.clipping_planes):
|
||||
if clipping_plane.obj:
|
||||
try:
|
||||
clipping_plane.obj.name
|
||||
except:
|
||||
context.scene.BIMProjectProperties.clipping_planes.remove(i)
|
||||
props.clipping_planes.remove(i)
|
||||
break
|
||||
else:
|
||||
context.scene.BIMProjectProperties.clipping_planes.remove(i)
|
||||
props.clipping_planes.remove(i)
|
||||
break
|
||||
else:
|
||||
break
|
||||
@@ -2326,14 +2339,15 @@ class RefreshClippingPlanes(bpy.types.Operator):
|
||||
region = next(r for r in area.regions if r.type == "WINDOW")
|
||||
data = region.data
|
||||
|
||||
if not len(context.scene.BIMProjectProperties.clipping_planes):
|
||||
props = tool.Project.get_project_props()
|
||||
if not len(props.clipping_planes):
|
||||
data.use_clip_planes = False
|
||||
else:
|
||||
with bpy.context.temp_override(area=area, region=region):
|
||||
bpy.ops.view3d.clip_border()
|
||||
|
||||
clip_planes = []
|
||||
for clipping_plane in bpy.context.scene.BIMProjectProperties.clipping_planes:
|
||||
for clipping_plane in tool.Project.get_project_props().clipping_planes:
|
||||
obj = clipping_plane.obj
|
||||
if not obj:
|
||||
continue
|
||||
@@ -2372,8 +2386,8 @@ class CreateClippingPlane(bpy.types.Operator):
|
||||
from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_origin_3d
|
||||
|
||||
# Clean up deleted planes
|
||||
|
||||
if len(context.scene.BIMProjectProperties.clipping_planes) > 5:
|
||||
props = tool.Project.get_project_props()
|
||||
if len(props.clipping_planes) > 5:
|
||||
self.report({"INFO"}, "Maximum of six clipping planes allowed.")
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -2413,7 +2427,7 @@ class CreateClippingPlane(bpy.types.Operator):
|
||||
|
||||
context.scene.cursor.location = location
|
||||
|
||||
new = context.scene.BIMProjectProperties.clipping_planes.add()
|
||||
new = tool.Project.get_project_props().clipping_planes.add()
|
||||
new.obj = plane_obj
|
||||
|
||||
tool.Blender.set_active_object(plane_obj)
|
||||
@@ -2444,7 +2458,7 @@ class FlipClippingPlane(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
if obj in context.scene.BIMProjectProperties.clipping_planes_objs:
|
||||
if obj in tool.Project.get_project_props().clipping_planes_objs:
|
||||
obj.rotation_euler[0] += radians(180)
|
||||
context.view_layer.update()
|
||||
return {"FINISHED"}
|
||||
@@ -2462,14 +2476,15 @@ class BIM_OT_save_clipping_planes(bpy.types.Operator):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if IfcStore.path:
|
||||
return context.scene.BIMProjectProperties.clipping_planes
|
||||
return tool.Project.get_project_props().clipping_planes
|
||||
cls.poll_message_set("Please Save The IFC File")
|
||||
|
||||
def execute(self, context):
|
||||
clipping_planes_to_serialize = defaultdict(dict)
|
||||
clipping_planes = context.scene.BIMProjectProperties.clipping_planes
|
||||
clipping_planes = tool.Project.get_project_props().clipping_planes
|
||||
for clipping_plane in clipping_planes:
|
||||
obj = clipping_plane.obj
|
||||
assert obj
|
||||
name = obj.name
|
||||
clipping_planes_to_serialize[name]["location"] = obj.location[0:3]
|
||||
clipping_planes_to_serialize[name]["rotation"] = obj.rotation_euler[0:3]
|
||||
@@ -2495,13 +2510,14 @@ class BIM_OT_load_clipping_planes(bpy.types.Operator):
|
||||
cls.poll_message_set("Please Save The IFC File")
|
||||
|
||||
def execute(self, context):
|
||||
bpy.data.batch_remove(context.scene.BIMProjectProperties.clipping_planes_objs)
|
||||
context.scene.BIMProjectProperties.clipping_planes.clear()
|
||||
props = tool.Project.get_project_props()
|
||||
bpy.data.batch_remove(props.clipping_planes_objs)
|
||||
props.clipping_planes.clear()
|
||||
with open(Path(IfcStore.path).with_name(CLIPPING_PLANES_FILE_NAME), "r") as file:
|
||||
clipping_planes_dict = json.load(file)
|
||||
for name, values in clipping_planes_dict.items():
|
||||
bpy.ops.bim.create_clipping_plane()
|
||||
obj = context.scene.BIMProjectProperties.clipping_planes_objs[-1]
|
||||
obj = props.clipping_planes_objs[-1]
|
||||
obj.name = name
|
||||
obj.location = values["location"]
|
||||
obj.rotation_euler = values["rotation"]
|
||||
|
||||
@@ -451,7 +451,8 @@ def get_side_area(o: bpy.types.Object) -> float:
|
||||
|
||||
|
||||
def get_cross_section_area(obj: bpy.types.Object) -> float:
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
assert representation
|
||||
item = representation.Items[0]
|
||||
while True:
|
||||
if item.is_a("IfcExtrudedAreaSolid"):
|
||||
|
||||
@@ -711,14 +711,14 @@ class SelectSimilar(Operator, tool.Ifc.Operator):
|
||||
return self.execute(context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMSearchProperties
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
key = self.key
|
||||
if key == "PredefinedType":
|
||||
key = "predefined_type"
|
||||
value = ifcopenshell.util.selector.get_element_value(element, key)
|
||||
tolerance = bpy.context.scene.DocProperties.tolerance
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
tolerance = dprops.tolerance
|
||||
|
||||
# Determine the number of decimal places based on the magnitude of the rounding value
|
||||
if tolerance < 1:
|
||||
|
||||
@@ -155,10 +155,10 @@ class UnlinkStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
# for unlinked blender material.
|
||||
updated_meshes = set()
|
||||
for obj in bpy.data.objects:
|
||||
mesh = obj.data
|
||||
if not isinstance(mesh, bpy.types.Mesh):
|
||||
if not (mesh := obj.data) or not isinstance(mesh, bpy.types.Mesh):
|
||||
continue
|
||||
if not mesh.BIMMeshProperties.ifc_definition_id:
|
||||
representation = tool.Geometry.get_data_representation(mesh)
|
||||
if not representation:
|
||||
continue
|
||||
if mesh in updated_meshes:
|
||||
continue
|
||||
@@ -1137,13 +1137,16 @@ class AssignStyleToSelected(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_file = tool.Ifc.get()
|
||||
style = ifc_file.by_id(self.style_id)
|
||||
material = tool.Ifc.get_object(style)
|
||||
assert isinstance(material, bpy.types.Material)
|
||||
|
||||
has_items = False
|
||||
representations: dict[ifcopenshell.entity_instance, bpy.types.Object] = {}
|
||||
for obj in context.selected_objects:
|
||||
if tool.Geometry.is_representation_item(obj):
|
||||
has_items = True
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
assert isinstance(obj.data, bpy.types.Mesh)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
tool.Style.assign_style_to_representation_item(item, style)
|
||||
obj.data.materials.clear()
|
||||
obj.data.materials.append(material)
|
||||
@@ -1156,7 +1159,8 @@ class AssignStyleToSelected(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
if has_items:
|
||||
tool.Geometry.reload_representation(context.scene.BIMGeometryProperties.representation_obj)
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
tool.Geometry.reload_representation(gprops.representation_obj)
|
||||
bpy.ops.bim.disable_editing_representation_items()
|
||||
bpy.ops.bim.enable_editing_representation_items()
|
||||
|
||||
|
||||
@@ -127,26 +127,14 @@ class BooleansData:
|
||||
def booleans(cls):
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
obj = props.representation_obj or bpy.context.active_object
|
||||
if (
|
||||
not obj.data
|
||||
or not hasattr(obj.data, "BIMMeshProperties")
|
||||
or not obj.data.BIMMeshProperties.ifc_definition_id
|
||||
):
|
||||
if not (representation := tool.Geometry.get_active_representation(obj)):
|
||||
return []
|
||||
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
return tool.Model.get_booleans(representation=representation)
|
||||
|
||||
@classmethod
|
||||
def manual_booleans(cls):
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
obj = props.representation_obj or bpy.context.active_object
|
||||
if (
|
||||
not obj.data
|
||||
or not hasattr(obj.data, "BIMMeshProperties")
|
||||
or not obj.data.BIMMeshProperties.ifc_definition_id
|
||||
):
|
||||
if not (representation := tool.Geometry.get_active_representation(obj)):
|
||||
return []
|
||||
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
return tool.Model.get_manual_booleans(tool.Ifc.get_entity(obj), representation=representation)
|
||||
|
||||
@@ -139,7 +139,8 @@ class AddOpening(bpy.types.Operator, tool.Ifc.Operator):
|
||||
tool.Ifc, tool.Geometry, tool.Surveyor, obj=voided_obj
|
||||
)
|
||||
|
||||
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
representation = tool.Geometry.get_active_representation(voided_obj)
|
||||
assert representation
|
||||
bonsai.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
@@ -269,20 +270,18 @@ class BooleansMarkAsManual(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
obj = context.active_object
|
||||
if (
|
||||
obj
|
||||
and tool.Ifc.get_entity(obj)
|
||||
and hasattr(obj.data, "BIMMeshProperties")
|
||||
and obj.data.BIMMeshProperties.ifc_definition_id
|
||||
):
|
||||
if obj and tool.Ifc.get_entity(obj) and tool.Geometry.get_active_representation(obj):
|
||||
return True
|
||||
cls.poll_message_set("Need to select IFC element with representation")
|
||||
return False
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
assert element
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
assert representation
|
||||
booleans = tool.Model.get_booleans(representation=representation)
|
||||
|
||||
if self.mark_as_manual:
|
||||
@@ -304,13 +303,13 @@ class EnableEditingBooleans(bpy.types.Operator):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not bpy.context.scene.BIMGeometryProperties.representation_obj:
|
||||
if not tool.Geometry.get_geometry_props().representation_obj:
|
||||
cls.poll_message_set("To enable editing booleans object should be in item mode.")
|
||||
return False
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
rep_obj = gprops.representation_obj
|
||||
assert rep_obj
|
||||
@@ -344,6 +343,6 @@ class DisableEditingBooleans(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
props.is_editing = False
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -19,34 +19,51 @@
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import PointerProperty, StringProperty, IntProperty, BoolProperty, CollectionProperty, EnumProperty
|
||||
from typing import Union
|
||||
from typing import Union, TYPE_CHECKING, Literal, get_args
|
||||
|
||||
OperatorType = Literal["DIFFERENCE", "INTERSECTION", "UNION"]
|
||||
|
||||
|
||||
class Boolean(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
operator: StringProperty(name="Operator")
|
||||
operator: EnumProperty(
|
||||
items=[(i, i, "") for i in get_args(OperatorType)],
|
||||
name="Operator",
|
||||
default="DIFFERENCE",
|
||||
)
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
level: IntProperty(name="Level")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
operator: OperatorType
|
||||
name: str
|
||||
ifc_definition_id: int
|
||||
level: int
|
||||
|
||||
|
||||
class VoidProperties(PropertyGroup):
|
||||
desired_opening: PointerProperty(name="Desired Opening To Fill", type=bpy.types.Object)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
desired_opening: Union[bpy.types.Object, None]
|
||||
|
||||
|
||||
class BIMBooleanProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing", default=False)
|
||||
booleans: CollectionProperty(name="Booleans", type=Boolean)
|
||||
active_boolean_index: IntProperty(name="Active Boolean Index")
|
||||
operator: EnumProperty(
|
||||
items=[
|
||||
("DIFFERENCE", "DIFFERENCE", ""),
|
||||
("INTERSECTION", "INTERSECTION", ""),
|
||||
("UNION", "UNION", ""),
|
||||
],
|
||||
items=[(i, i, "") for i in get_args(OperatorType)],
|
||||
name="Operator",
|
||||
default="DIFFERENCE",
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
is_editing: bool
|
||||
booleans: bpy.types.bpy_prop_collection_idprop[Boolean]
|
||||
active_boolean_index: int
|
||||
operator: OperatorType
|
||||
|
||||
@property
|
||||
def active_boolean(self) -> Union[Boolean, None]:
|
||||
if self.booleans and 0 <= self.active_boolean_index < len(self.booleans):
|
||||
|
||||
@@ -126,13 +126,10 @@ class BIM_PT_booleans(Panel):
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (
|
||||
context.active_object is not None
|
||||
and context.active_object.type == "MESH"
|
||||
and hasattr(context.active_object.data, "BIMMeshProperties")
|
||||
and (
|
||||
context.active_object.data.BIMMeshProperties.ifc_definition_id
|
||||
or context.active_object.data.BIMMeshProperties.ifc_boolean_id
|
||||
)
|
||||
(obj := context.active_object) is not None
|
||||
and isinstance(data := obj.data, bpy.types.Mesh)
|
||||
and (mesh_props := tool.Geometry.get_mesh_props(data))
|
||||
and (mesh_props.ifc_definition_id or mesh_props.ifc_boolean_id)
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
@@ -141,13 +138,13 @@ class BIM_PT_booleans(Panel):
|
||||
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
|
||||
if not context.active_object.data:
|
||||
return
|
||||
layout = self.layout
|
||||
props = context.scene.BIMBooleanProperties
|
||||
props = tool.Feature.get_boolean_props()
|
||||
|
||||
if context.active_object.data.BIMMeshProperties.ifc_definition_id:
|
||||
if tool.Geometry.get_mesh_props(mesh).ifc_definition_id:
|
||||
row = layout.row(align=True)
|
||||
total_booleans = BooleansData.data["total_booleans"]
|
||||
manual_booleans = BooleansData.data["manual_booleans"]
|
||||
|
||||
@@ -828,7 +828,8 @@ class AddIfcFile(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DocProperties.ifc_files.add()
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.ifc_files.add()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -839,7 +840,8 @@ class RemoveIfcFile(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DocProperties.ifc_files.remove(self.index)
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.ifc_files.remove(self.index)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1060,7 +1062,8 @@ class ClippingPlaneCutWithCappings(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
cutting_planes = [p.obj for p in context.scene.BIMProjectProperties.clipping_planes]
|
||||
props = tool.Project.get_project_props()
|
||||
cutting_planes = [obj for p in props.clipping_planes if (obj := p.obj)]
|
||||
if not cutting_planes:
|
||||
self.report({"INFO"}, "No cutting planes found.")
|
||||
return {"FINISHED"}
|
||||
@@ -1070,7 +1073,7 @@ class ClippingPlaneCutWithCappings(bpy.types.Operator):
|
||||
objects_processed, t0 = 0, time.time()
|
||||
wm.progress_begin(0, len(context.selected_objects))
|
||||
for obj_i, obj in enumerate(context.selected_objects):
|
||||
if obj.type != "MESH":
|
||||
if not isinstance((mesh := obj.data), bpy.types.Mesh):
|
||||
continue
|
||||
|
||||
if obj in cutting_planes:
|
||||
@@ -1082,7 +1085,6 @@ class ClippingPlaneCutWithCappings(bpy.types.Operator):
|
||||
ws_to_ls = obj.matrix_world.inverted()
|
||||
rotation = ws_to_ls.to_quaternion()
|
||||
|
||||
mesh = obj.data
|
||||
bm = tool.Blender.get_bmesh_for_mesh(mesh)
|
||||
object_changed = False
|
||||
|
||||
@@ -1106,7 +1108,7 @@ class ClippingPlaneCutWithCappings(bpy.types.Operator):
|
||||
# don't swap mesh if it wasn't affected by any of the cutting planes
|
||||
if object_changed:
|
||||
temp_mesh = bpy.data.meshes.new("temp_cut")
|
||||
temp_mesh.BIMMeshProperties.replaced_mesh = mesh
|
||||
tool.Geometry.get_mesh_props(temp_mesh).replaced_mesh = mesh
|
||||
for material in mesh.materials:
|
||||
temp_mesh.materials.append(material)
|
||||
obj.data = temp_mesh
|
||||
@@ -1163,9 +1165,10 @@ class RevertClippingPlaneCut(bpy.types.Operator):
|
||||
self.report({"INFO"}, f"{objects_processed} processed - {time.time()-t0:.3f} sec")
|
||||
return {"FINISHED"}
|
||||
|
||||
def revert_object_mesh(self, obj):
|
||||
def revert_object_mesh(self, obj: bpy.types.Object) -> None:
|
||||
mesh = obj.data
|
||||
replaced_mesh = mesh.BIMMeshProperties.replaced_mesh
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
replaced_mesh = tool.Geometry.get_mesh_props(mesh).replaced_mesh
|
||||
if replaced_mesh:
|
||||
obj.data = replaced_mesh
|
||||
tool.Blender.remove_data_block(mesh, do_unlink=False)
|
||||
|
||||
@@ -525,6 +525,13 @@ class IfcParameter(PropertyGroup):
|
||||
value: FloatProperty(name="Value") # For now, only floats
|
||||
type: StringProperty(name="Type")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
name: str
|
||||
step_id: int
|
||||
index: int
|
||||
value: float
|
||||
type: str
|
||||
|
||||
|
||||
class PsetQto(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
@@ -532,6 +539,11 @@ class PsetQto(PropertyGroup):
|
||||
is_expanded: BoolProperty(name="Is Expanded", default=True)
|
||||
is_editable: BoolProperty(name="Is Editable")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
properties: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
is_expanded: bool
|
||||
is_editable: bool
|
||||
|
||||
|
||||
class GlobalId(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
@@ -540,6 +552,9 @@ class GlobalId(PropertyGroup):
|
||||
class BIMCollectionProperties(PropertyGroup):
|
||||
obj: PointerProperty(type=bpy.types.Object)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
obj: Union[bpy.types.Object, None]
|
||||
|
||||
|
||||
class BIMObjectProperties(PropertyGroup):
|
||||
collection: PointerProperty(type=bpy.types.Collection)
|
||||
@@ -564,6 +579,9 @@ def get_profiles(self: "BIMMeshProperties", context: bpy.types.Context):
|
||||
return ItemData.data["profiles_enum"]
|
||||
|
||||
|
||||
SubshapeType = Literal["-", "PROFILE", "AXIS"]
|
||||
|
||||
|
||||
class BIMMeshProperties(PropertyGroup):
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
ifc_boolean_id: IntProperty(name="IFC Boolean ID")
|
||||
@@ -572,7 +590,7 @@ class BIMMeshProperties(PropertyGroup):
|
||||
is_native: BoolProperty(name="Is Native", default=False)
|
||||
is_swept_solid: BoolProperty(name="Is Swept Solid")
|
||||
is_parametric: BoolProperty(name="Is Parametric", default=False)
|
||||
subshape_type: EnumProperty(name="Subshape Type", items=[(i, i, "") for i in ("-", "PROFILE", "AXIS")])
|
||||
subshape_type: EnumProperty(name="Subshape Type", items=[(i, i, "") for i in get_args(SubshapeType)])
|
||||
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
|
||||
item_attributes: CollectionProperty(name="Item Attributes", type=Attribute)
|
||||
item_profile: EnumProperty(name="Item Profile", items=get_profiles)
|
||||
@@ -580,6 +598,22 @@ class BIMMeshProperties(PropertyGroup):
|
||||
mesh_checksum: StringProperty(name="Mesh Checksum", default="")
|
||||
replaced_mesh: PointerProperty(type=bpy.types.Mesh, description="Original mesh to revert section cutaway")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
ifc_definition_id: int
|
||||
ifc_boolean_id: int
|
||||
obj: Union[bpy.types.Object, None]
|
||||
has_openings_applied: bool
|
||||
is_native: bool
|
||||
is_swept_solid: bool
|
||||
is_parametric: bool
|
||||
subshape_type: SubshapeType
|
||||
ifc_parameters: bpy.types.bpy_prop_collection_idprop[IfcParameter]
|
||||
item_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
item_profile: str
|
||||
material_checksum: str
|
||||
mesh_checksum: str
|
||||
replaced_mesh: Union[bpy.types.Mesh, None]
|
||||
|
||||
|
||||
class BIMFacet(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
@@ -599,16 +633,30 @@ class BIMFacet(PropertyGroup):
|
||||
],
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pset: str
|
||||
value: str
|
||||
type: str
|
||||
comparison: Literal["=", "!=", ">=", "<=", ">", "<", "*=", "!*="]
|
||||
|
||||
|
||||
class BIMFilterGroup(PropertyGroup):
|
||||
filters: CollectionProperty(type=BIMFacet, name="filters")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
filters: bpy.types.bpy_prop_collection_idprop[BIMFacet]
|
||||
|
||||
|
||||
class BIMSnapGroups(PropertyGroup):
|
||||
object: BoolProperty(name="Object", default=True)
|
||||
polyline: BoolProperty(name="Polyline", default=True)
|
||||
measure: BoolProperty(name="Measure", default=True)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
object: bool
|
||||
polyline: bool
|
||||
measure: bool
|
||||
|
||||
|
||||
class BIMSnapProperties(PropertyGroup):
|
||||
vertex: BoolProperty(name="Vertex", default=True)
|
||||
@@ -616,3 +664,10 @@ class BIMSnapProperties(PropertyGroup):
|
||||
edge_center: BoolProperty(name="Edge Center", default=True)
|
||||
edge_intersection: BoolProperty(name="Edge Intersection", default=True)
|
||||
face: BoolProperty(name="Face", default=True)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
vertex: bool
|
||||
edge: bool
|
||||
edge_center: bool
|
||||
edge_intersection: bool
|
||||
face: bool
|
||||
|
||||
+59
-33
@@ -19,6 +19,7 @@
|
||||
import os
|
||||
import bpy
|
||||
import platform
|
||||
import bonsai.bim.helper
|
||||
from pathlib import Path
|
||||
from bpy.types import Panel
|
||||
from bpy.props import StringProperty, IntProperty, BoolProperty
|
||||
@@ -34,7 +35,7 @@ import bonsai.bim
|
||||
import bonsai.tool as tool
|
||||
from ifcopenshell.util.file import IfcHeaderExtractor
|
||||
from bonsai.bim.prop import Attribute
|
||||
from typing import Optional
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
|
||||
class IFCFileSelector:
|
||||
@@ -147,7 +148,7 @@ class BIM_PT_section_with_cappings(Panel):
|
||||
row.operator("bim.clipping_plane_cut_with_cappings", icon="XRAY", text="Cut")
|
||||
row.operator("bim.revert_clipping_plane_cut", icon="FILE_REFRESH", text="Revert Cut")
|
||||
|
||||
props = context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
box = layout.box()
|
||||
header = box.row(align=True)
|
||||
header.label(text="Clipping Planes")
|
||||
@@ -247,7 +248,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
should_play_chaching_sound: BoolProperty(name="Play A Cha-Ching Sound When Project Costs Updates", default=False)
|
||||
tmp_dir: StringProperty(
|
||||
name="Temporary Directory",
|
||||
description='Path to create and store temporary files. If left blank, a system default will be used.',
|
||||
description="Path to create and store temporary files. If left blank, a system default will be used.",
|
||||
)
|
||||
spatial_elements_unselectable: BoolProperty(
|
||||
name="Make Spatial Elements Unselectable By Default",
|
||||
@@ -302,7 +303,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
size=4,
|
||||
description="Color of background overlays",
|
||||
)
|
||||
|
||||
opening_focus_opacity: bpy.props.IntProperty(
|
||||
default=100,
|
||||
min=0,
|
||||
@@ -312,7 +312,29 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
description="When modifying openings, other elements of the model will display with some transparency.\n0 is fully transparent and 100 is fully opaque",
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
if TYPE_CHECKING:
|
||||
svg2pdf_command: str
|
||||
svg2dxf_command: str
|
||||
svg_command: str
|
||||
layout_svg_command: str
|
||||
pdf_command: str
|
||||
spreadsheet_command: str
|
||||
should_hide_empty_props: bool
|
||||
should_setup_workspace: bool
|
||||
activate_workspace: bool
|
||||
should_setup_toolbar: bool
|
||||
should_play_chaching_sound: bool
|
||||
spatial_elements_unselectable: bool
|
||||
tmp_dir: str
|
||||
decorations_colour: tuple[float, float, float, float]
|
||||
decorator_color_selected: tuple[float, float, float, float]
|
||||
decorator_color_unselected: tuple[float, float, float, float]
|
||||
decorator_color_special: tuple[float, float, float, float]
|
||||
decorator_color_error: tuple[float, float, float, float]
|
||||
decorator_color_background: tuple[float, float, float, float]
|
||||
opening_focus_opacity: int
|
||||
|
||||
def draw(self, context: bpy.types.Context) -> None:
|
||||
layout = self.layout
|
||||
|
||||
row = layout.row()
|
||||
@@ -333,7 +355,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
bonsai.bim.helper.draw_expandable_panel(self.layout, context, "Drawing", self.draw_drawing_settings)
|
||||
bonsai.bim.helper.draw_expandable_panel(self.layout, context, "Openings", self.draw_openings_settings)
|
||||
|
||||
def draw_commands(self, layout, context):
|
||||
def draw_commands(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
||||
layout.prop(self, "svg2pdf_command")
|
||||
layout.prop(self, "svg2dxf_command")
|
||||
layout.prop(self, "svg_command")
|
||||
@@ -341,15 +363,16 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
layout.prop(self, "pdf_command")
|
||||
layout.prop(self, "spreadsheet_command")
|
||||
|
||||
def draw_misc_settings(self, layout, context):
|
||||
def draw_misc_settings(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
||||
layout.prop(self, "should_hide_empty_props")
|
||||
layout.prop(self, "should_setup_workspace")
|
||||
layout.prop(self, "activate_workspace")
|
||||
layout.prop(self, "should_setup_toolbar")
|
||||
layout.prop(self, "should_play_chaching_sound")
|
||||
layout.prop(self, "spatial_elements_unselectable")
|
||||
layout.prop(context.scene.BIMProjectProperties, "should_disable_undo_on_save")
|
||||
layout.prop(context.scene.BIMProjectProperties, "should_stream")
|
||||
props = tool.Project.get_project_props()
|
||||
layout.prop(props, "should_disable_undo_on_save")
|
||||
layout.prop(props, "should_stream")
|
||||
|
||||
def draw_model_settings(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
||||
props = tool.Model.get_model_props()
|
||||
@@ -357,7 +380,7 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
if props.occurrence_name_style == "CUSTOM":
|
||||
layout.prop(props, "occurrence_name_function")
|
||||
|
||||
def draw_directories(self, layout, context):
|
||||
def draw_directories(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
||||
row = layout.row(align=True)
|
||||
row.prop(context.scene.BIMProperties, "data_dir")
|
||||
row.operator("bim.select_dir", icon="FILE_FOLDER", text="").data_path = "scene.BIMProperties.data_dir"
|
||||
@@ -370,25 +393,26 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row.prop(self, "tmp_dir")
|
||||
row.operator("bim.select_dir", icon="FILE_FOLDER", text="").data_path = "preferences.tmp_dir"
|
||||
|
||||
def draw_drawing_settings(self, layout, context):
|
||||
def draw_drawing_settings(self, layout: bpy.types.UILayout, context: bpy.types.Context) -> None:
|
||||
layout.prop(context.scene.BIMProperties, "pset_dir")
|
||||
layout.prop(context.scene.DocProperties, "sheets_dir")
|
||||
layout.prop(context.scene.DocProperties, "layouts_dir")
|
||||
layout.prop(context.scene.DocProperties, "titleblocks_dir")
|
||||
layout.prop(context.scene.DocProperties, "drawings_dir")
|
||||
layout.prop(context.scene.DocProperties, "stylesheet_path")
|
||||
layout.prop(context.scene.DocProperties, "schedules_stylesheet_path")
|
||||
layout.prop(context.scene.DocProperties, "markers_path")
|
||||
layout.prop(context.scene.DocProperties, "symbols_path")
|
||||
layout.prop(context.scene.DocProperties, "patterns_path")
|
||||
layout.prop(context.scene.DocProperties, "shadingstyles_path")
|
||||
layout.prop(context.scene.DocProperties, "shadingstyle_default")
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
layout.prop(dprops, "sheets_dir")
|
||||
layout.prop(dprops, "layouts_dir")
|
||||
layout.prop(dprops, "titleblocks_dir")
|
||||
layout.prop(dprops, "drawings_dir")
|
||||
layout.prop(dprops, "stylesheet_path")
|
||||
layout.prop(dprops, "schedules_stylesheet_path")
|
||||
layout.prop(dprops, "markers_path")
|
||||
layout.prop(dprops, "symbols_path")
|
||||
layout.prop(dprops, "patterns_path")
|
||||
layout.prop(dprops, "shadingstyles_path")
|
||||
layout.prop(dprops, "shadingstyle_default")
|
||||
row = layout.row()
|
||||
row.prop(context.scene.DocProperties, "drawing_font")
|
||||
row.prop(context.scene.DocProperties, "magic_font_scale")
|
||||
layout.prop(context.scene.DocProperties, "imperial_precision")
|
||||
layout.prop(context.scene.DocProperties, "tolerance")
|
||||
layout.prop(context.scene.DocProperties, "classes_to_wireframe")
|
||||
row.prop(dprops, "drawing_font")
|
||||
row.prop(dprops, "magic_font_scale")
|
||||
layout.prop(dprops, "imperial_precision")
|
||||
layout.prop(dprops, "tolerance")
|
||||
layout.prop(dprops, "classes_to_wireframe")
|
||||
|
||||
def draw_decorator_colors(self, layout, context):
|
||||
layout.row().prop(self, "decorations_colour")
|
||||
@@ -496,9 +520,10 @@ class BIM_PT_tabs(Panel):
|
||||
op.uri = "https://docs.bonsaibim.org/guides/troubleshooting.html#saving-and-loading-blend-files"
|
||||
row.operator("bim.close_blend_warning", text="", icon="CANCEL")
|
||||
|
||||
if context.mode == "OBJECT" and context.scene.BIMGeometryProperties.mode in ("OBJECT", "ITEM"):
|
||||
gprops = tool.Geometry.get_geometry_props()
|
||||
if context.mode == "OBJECT" and gprops.mode in ("OBJECT", "ITEM"):
|
||||
pass
|
||||
elif context.mode.startswith("EDIT") and context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
elif context.mode.startswith("EDIT") and gprops.mode == "EDIT":
|
||||
pass
|
||||
else:
|
||||
box = self.layout.box()
|
||||
@@ -533,7 +558,7 @@ class BIM_PT_tab_new_project_wizard(Panel):
|
||||
if not tool.Blender.is_tab(context, "PROJECT"):
|
||||
return False
|
||||
props = context.scene.BIMProperties
|
||||
pprops = context.scene.BIMProjectProperties
|
||||
pprops = tool.Project.get_project_props()
|
||||
if pprops.is_loading:
|
||||
return False
|
||||
elif tool.Ifc.get() or props.ifc_file:
|
||||
@@ -555,7 +580,7 @@ class BIM_PT_tab_project_info(Panel):
|
||||
if not tool.Blender.is_tab(context, "PROJECT"):
|
||||
return False
|
||||
props = context.scene.BIMProperties
|
||||
pprops = context.scene.BIMProjectProperties
|
||||
pprops = tool.Project.get_project_props()
|
||||
if pprops.is_loading:
|
||||
return True
|
||||
elif tool.Ifc.get() or props.ifc_file:
|
||||
@@ -854,6 +879,7 @@ class BIM_PT_tab_object_metadata(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
props = tool.Project.get_project_props()
|
||||
return (
|
||||
tool.Blender.is_tab(context, "OBJECT")
|
||||
and tool.Ifc.get()
|
||||
@@ -862,7 +888,7 @@ class BIM_PT_tab_object_metadata(Panel):
|
||||
and (
|
||||
obj.type != "EMPTY"
|
||||
or not obj.instance_collection
|
||||
or not any(l.empty_handle == obj for l in context.scene.BIMProjectProperties.links)
|
||||
or not any(l.empty_handle == obj for l in props.links)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1231,7 +1257,7 @@ class BIM_PT_decorators_overlay(Panel):
|
||||
view = context.space_data
|
||||
overlay = view.overlay
|
||||
|
||||
georeference_props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
georeference_props = tool.Georeference.get_georeference_props()
|
||||
aggregate_props = bpy.context.scene.BIMAggregateProperties
|
||||
nest_props = bpy.context.scene.BIMNestProperties
|
||||
model_props = tool.Model.get_model_props()
|
||||
|
||||
@@ -913,7 +913,7 @@ class Blender(bonsai.core.tool.Blender):
|
||||
return False
|
||||
if not (element := tool.Ifc.get_entity(obj)):
|
||||
return True
|
||||
if obj in bpy.context.scene.BIMProjectProperties.clipping_planes_objs:
|
||||
if obj in tool.Project.get_project_props().clipping_planes_objs:
|
||||
return False
|
||||
usage_type = tool.Model.get_usage_type(element)
|
||||
if usage_type in ("LAYER1", "LAYER2"):
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import json
|
||||
import bpy
|
||||
@@ -30,10 +31,17 @@ import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from mathutils import Vector
|
||||
from collections import defaultdict
|
||||
from typing import Iterable, Literal
|
||||
from typing import Iterable, Literal, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.debug.prop import BIMDebugProperties
|
||||
|
||||
|
||||
class Debug(bonsai.core.tool.Debug):
|
||||
@classmethod
|
||||
def get_debug_props(cls) -> BIMDebugProperties:
|
||||
return bpy.context.scene.BIMDebugProperties
|
||||
|
||||
@classmethod
|
||||
def add_schema_identifier(cls, schema: W.schema_definition) -> None:
|
||||
IfcStore.schema_identifiers.append(schema.name())
|
||||
|
||||
@@ -346,19 +346,23 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def disable_editing_drawings(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_drawings = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_drawings = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_schedules(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_schedules = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_schedules = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_references(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_references = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_references = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_sheets(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_sheets = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_sheets = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_text(cls, obj: bpy.types.Object) -> None:
|
||||
@@ -383,19 +387,23 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def enable_editing_drawings(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_drawings = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_drawings = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_schedules(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_schedules = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_schedules = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_references(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_references = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_references = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_sheets(cls) -> None:
|
||||
bpy.context.scene.DocProperties.is_editing_sheets = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_sheets = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_text(cls, obj: bpy.types.Object) -> None:
|
||||
@@ -616,7 +624,8 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def is_editing_sheets(cls) -> bool:
|
||||
return bpy.context.scene.DocProperties.is_editing_sheets
|
||||
props = tool.Drawing.get_document_props()
|
||||
return props.is_editing_sheets
|
||||
|
||||
@classmethod
|
||||
def remove_literal_from_annotation(cls, obj: bpy.types.Object, literal: ifcopenshell.entity_instance) -> None:
|
||||
@@ -810,7 +819,7 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def import_drawings(cls) -> None:
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
expanded_target_views = {d.target_view for d in props.drawings if d.is_expanded}
|
||||
if not hasattr(cls, "drawing_selected_states"):
|
||||
cls.drawing_selected_states = {}
|
||||
@@ -1048,7 +1057,8 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def show_decorations(cls) -> None:
|
||||
bpy.context.scene.DocProperties.should_draw_decorations = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.should_draw_decorations = True
|
||||
|
||||
@classmethod
|
||||
def update_text_value(cls, obj: bpy.types.Object) -> None:
|
||||
@@ -1147,36 +1157,33 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
@classmethod
|
||||
def get_default_layout_path(cls, identification: str, name: str) -> str:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
props = tool.Drawing.get_document_props()
|
||||
layouts_dir = (
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "LayoutsDir")
|
||||
or bpy.context.scene.DocProperties.layouts_dir
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "LayoutsDir") or props.layouts_dir
|
||||
)
|
||||
return os.path.join(layouts_dir, cls.sanitise_filename(f"{identification} - {name}.svg")).replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
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")
|
||||
or bpy.context.scene.DocProperties.sheets_dir
|
||||
)
|
||||
props = tool.Drawing.get_document_props()
|
||||
sheets_dir = ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "SheetsDir") or props.sheets_dir
|
||||
return os.path.join(sheets_dir, cls.sanitise_filename(f"{identification} - {name}.svg")).replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
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")
|
||||
or bpy.context.scene.DocProperties.titleblocks_dir
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "TitleblocksDir") or props.titleblocks_dir
|
||||
)
|
||||
return os.path.join(titleblocks_dir, cls.sanitise_filename(f"{name}.svg")).replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
def get_default_drawing_path(cls, name: str) -> str:
|
||||
project = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
props = tool.Drawing.get_document_props()
|
||||
drawings_dir = (
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "DrawingsDir")
|
||||
or bpy.context.scene.DocProperties.drawings_dir
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", "DrawingsDir") or props.drawings_dir
|
||||
)
|
||||
return os.path.join(drawings_dir, cls.sanitise_filename(f"{name}.svg")).replace("\\", "/")
|
||||
|
||||
@@ -1187,15 +1194,16 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
@classmethod
|
||||
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"
|
||||
props = tool.Drawing.get_document_props()
|
||||
resource_path = (
|
||||
ifcopenshell.util.element.get_pset(project, "BBIM_Documentation", f"{resource}Path") or props.resource_path
|
||||
)
|
||||
if resource_path:
|
||||
return resource_path.replace("\\", "/")
|
||||
|
||||
@classmethod
|
||||
def get_default_shading_style(cls) -> str:
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
return dprops.shadingstyle_default
|
||||
|
||||
@classmethod
|
||||
@@ -1501,7 +1509,7 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
dst.data = dst.data.copy()
|
||||
dst.name = dst.name.replace("IfcGridAxis/", "")
|
||||
dst.BIMObjectProperties.ifc_definition_id = 0
|
||||
dst.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
tool.Geometry.get_geometry_props(dst).ifc_definition_id = 0
|
||||
return dst
|
||||
|
||||
def disassemble(obj: bpy.types.Object) -> tuple[bpy.types.Object, bmesh.types.BMesh]:
|
||||
@@ -1883,7 +1891,8 @@ class Drawing(bonsai.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def is_active_drawing(cls, drawing: ifcopenshell.entity_instance) -> bool:
|
||||
return drawing.id() == bpy.context.scene.DocProperties.active_drawing_id
|
||||
props = tool.Drawing.get_document_props()
|
||||
return drawing.id() == props.active_drawing_id
|
||||
|
||||
@classmethod
|
||||
def run_drawing_activate_model(cls) -> None:
|
||||
|
||||
@@ -16,17 +16,25 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import bpy
|
||||
import bonsai.core.tool
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.helper
|
||||
import ifcopenshell
|
||||
from typing import Iterable
|
||||
from typing import Iterable, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.void.prop import BIMBooleanProperties
|
||||
|
||||
|
||||
class Feature(bonsai.core.tool.Feature):
|
||||
# TODO: consolidate module/model/opening and module/void into new module/feature
|
||||
|
||||
@classmethod
|
||||
def get_boolean_props(cls) -> BIMBooleanProperties:
|
||||
return bpy.context.scene.BIMBooleanProperties
|
||||
|
||||
@classmethod
|
||||
def add_feature(cls, featured_obj: bpy.types.Object, feature_objs: Iterable[bpy.types.Object]) -> None:
|
||||
featured_element = tool.Ifc.get_entity(featured_obj)
|
||||
|
||||
@@ -27,6 +27,7 @@ import numpy.typing as npt
|
||||
import multiprocessing
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.api.grid
|
||||
import ifcopenshell.api.profile
|
||||
import ifcopenshell.api.style
|
||||
@@ -53,11 +54,23 @@ from math import radians, pi
|
||||
from mathutils import Vector, Matrix
|
||||
from mathutils.bvhtree import BVHTree
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
from typing import Union, Iterable, Optional, Literal, Iterator, List, TYPE_CHECKING, get_args, Generator, cast
|
||||
from typing import (
|
||||
Union,
|
||||
Iterable,
|
||||
Optional,
|
||||
Literal,
|
||||
Iterator,
|
||||
List,
|
||||
TYPE_CHECKING,
|
||||
get_args,
|
||||
Generator,
|
||||
cast,
|
||||
TypeGuard,
|
||||
)
|
||||
from typing_extensions import TypeIs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.prop import Attribute
|
||||
from bonsai.bim.prop import Attribute, BIMMeshProperties
|
||||
from bonsai.bim.module.geometry.prop import BIMObjectGeometryProperties, BIMGeometryProperties
|
||||
|
||||
|
||||
@@ -70,6 +83,10 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
def get_object_geometry_props(cls, object: bpy.types.Object) -> BIMObjectGeometryProperties:
|
||||
return object.BIMGeometryProperties
|
||||
|
||||
@classmethod
|
||||
def get_mesh_props(cls, mesh: TYPES_WITH_MESH_PROPERTIES) -> BIMMeshProperties:
|
||||
return mesh.BIMMeshProperties
|
||||
|
||||
@classmethod
|
||||
def change_object_data(cls, obj: bpy.types.Object, data: bpy.types.ID, is_global: bool = False) -> None:
|
||||
if is_global:
|
||||
@@ -182,7 +199,9 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
if item_obj.obj == obj:
|
||||
props.item_objs.remove(i)
|
||||
break
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
item = tool.Ifc.get().by_id(tool.Geometry.get_mesh_props(mesh).ifc_definition_id)
|
||||
cls.remove_representation_item(item)
|
||||
cls.reload_representation(props.representation_obj)
|
||||
bpy.data.objects.remove(obj)
|
||||
@@ -249,17 +268,18 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
bonsai.core.system.remove_port(tool.Ifc, tool.System, port=port)
|
||||
ifcopenshell.api.run("root.remove_product", tool.Ifc.get(), product=element)
|
||||
|
||||
if isinstance(obj.data, bpy.types.Mesh) and not tool.Ifc.get_entity_by_id(
|
||||
obj.data.BIMMeshProperties.ifc_definition_id
|
||||
):
|
||||
tool.Blender.remove_data_block(obj.data)
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
if not tool.Ifc.get_entity_by_id(tool.Geometry.get_mesh_props(mesh).ifc_definition_id):
|
||||
tool.Blender.remove_data_block(mesh)
|
||||
|
||||
if is_spatial:
|
||||
bonsai.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
try:
|
||||
obj.name
|
||||
if bpy.context.scene.BIMGeometryProperties.representation_obj == obj:
|
||||
bpy.context.scene.BIMGeometryProperties.representation_obj = None
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
if props.representation_obj == obj:
|
||||
props.representation_obj = None
|
||||
bpy.data.objects.remove(obj)
|
||||
except:
|
||||
pass
|
||||
@@ -268,7 +288,9 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
def dissolve_triangulated_edges(cls, obj: bpy.types.Object) -> None:
|
||||
# AdvancedBreps may contain non-faceted, curved faces (e.g. as part of
|
||||
# a cylinder) so dissolving edges should not be allowed.
|
||||
mesh_element = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, Geometry.TYPES_WITH_MESH_PROPERTIES)
|
||||
mesh_element = tool.Ifc.get().by_id(tool.Geometry.get_mesh_props(mesh).ifc_definition_id)
|
||||
if (
|
||||
(
|
||||
mesh_element.is_a("IfcShapeRepresentation")
|
||||
@@ -279,26 +301,30 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
or not obj.data
|
||||
):
|
||||
return
|
||||
if hasattr(obj.data, "attributes") and (ios_edges_attribute := obj.data.attributes.get("ios_edges")):
|
||||
|
||||
if not isinstance(mesh, bpy.types.Mesh):
|
||||
return
|
||||
|
||||
if hasattr(mesh, "attributes") and (ios_edges_attribute := mesh.attributes.get("ios_edges")):
|
||||
# Edges from a forced triangulation are stored as True in a boolean attribute on the mesh
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
bm.from_mesh(mesh)
|
||||
edges_to_dissolve = [e for i, e in enumerate(bm.edges) if not ios_edges_attribute.data[i].value]
|
||||
bmesh.ops.dissolve_edges(bm, edges=edges_to_dissolve)
|
||||
bm.to_mesh(obj.data)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
elif "ios_edges" in obj.data:
|
||||
elif "ios_edges" in mesh:
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(obj.data)
|
||||
edges_to_keep = set(map(frozenset, obj.data["ios_edges"]))
|
||||
bm.from_mesh(mesh)
|
||||
edges_to_keep = set(map(frozenset, mesh["ios_edges"]))
|
||||
edges_to_dissolve = []
|
||||
for edge in bm.edges:
|
||||
if frozenset([vert.index for vert in edge.verts]) not in edges_to_keep:
|
||||
edges_to_dissolve.append(edge)
|
||||
bmesh.ops.dissolve_edges(bm, edges=edges_to_dissolve)
|
||||
bm.to_mesh(obj.data)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
del obj.data["ios_edges"]
|
||||
del mesh["ios_edges"]
|
||||
|
||||
@classmethod
|
||||
def apply_item_ids_as_vertex_groups(cls, obj: bpy.types.Object) -> None:
|
||||
@@ -486,13 +512,19 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
@classmethod
|
||||
def get_active_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
""":return: IfcRepresentation/IfcRepresentationItem or None"""
|
||||
if obj.data and hasattr(obj.data, "BIMMeshProperties") and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if (
|
||||
(data := obj.data)
|
||||
and isinstance(data, Geometry.TYPES_WITH_MESH_PROPERTIES)
|
||||
and (ifc_id := tool.Geometry.get_mesh_props(data).ifc_definition_id)
|
||||
):
|
||||
return tool.Ifc.get().by_id(ifc_id)
|
||||
|
||||
@classmethod
|
||||
def get_data_representation(cls, data: bpy.types.Mesh) -> ifcopenshell.entity_instance | None:
|
||||
if hasattr(data, "BIMMeshProperties") and data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(data.BIMMeshProperties.ifc_definition_id)
|
||||
def get_data_representation(cls, data: bpy.types.ID) -> ifcopenshell.entity_instance | None:
|
||||
if isinstance(data, Geometry.TYPES_WITH_MESH_PROPERTIES) and (
|
||||
ifc_id := tool.Geometry.get_mesh_props(data).ifc_definition_id
|
||||
):
|
||||
return tool.Ifc.get().by_id(ifc_id)
|
||||
|
||||
@classmethod
|
||||
def get_active_representation_context(cls, obj: bpy.types.Object) -> ifcopenshell.entity_instance:
|
||||
@@ -670,13 +702,13 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
return data.users != 0
|
||||
|
||||
@classmethod
|
||||
def has_geometric_data(cls, obj: bpy.types.Object) -> bool:
|
||||
if not obj.data:
|
||||
def is_geometric_data(cls, data: Union[bpy.types.ID, None]) -> TypeGuard[Union[bpy.types.Mesh, bpy.types.Curve]]:
|
||||
if not data:
|
||||
return False
|
||||
if isinstance(obj.data, bpy.types.Mesh):
|
||||
return bool(obj.data.vertices)
|
||||
elif isinstance(obj.data, bpy.types.Curve):
|
||||
return bool(obj.data.splines)
|
||||
if isinstance(data, bpy.types.Mesh):
|
||||
return bool(data.vertices)
|
||||
elif isinstance(data, bpy.types.Curve):
|
||||
return bool(data.splines)
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
@@ -826,7 +858,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
ifc_importer.material_creator.load_existing_materials()
|
||||
shape_has_openings = cls.does_shape_has_openings(shape)
|
||||
ifc_importer.material_creator.create(element, obj, mesh, shape_has_openings)
|
||||
mesh.BIMMeshProperties.has_openings_applied = apply_openings
|
||||
mprops = tool.Geometry.get_mesh_props(mesh)
|
||||
mprops.has_openings_applied = apply_openings
|
||||
if not shape_has_openings:
|
||||
tool.Loader.load_indexed_colour_map(representation, mesh)
|
||||
tool.Loader.link_mesh(shape, mesh)
|
||||
@@ -852,7 +885,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
ifc_importer.material_creator.load_existing_materials()
|
||||
shape_has_openings = False
|
||||
ifc_importer.material_creator.create(element, obj, mesh, shape_has_openings)
|
||||
mesh.BIMMeshProperties.has_openings_applied = apply_openings
|
||||
mprops = tool.Geometry.get_mesh_props(mesh)
|
||||
mprops.has_openings_applied = apply_openings
|
||||
if not shape_has_openings:
|
||||
tool.Loader.load_indexed_colour_map(representation, mesh)
|
||||
meshes[mesh_name] = mesh
|
||||
@@ -867,7 +901,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def import_representation_parameters(cls, data: bpy.types.Mesh) -> None:
|
||||
props = data.BIMMeshProperties
|
||||
props = tool.Geometry.get_mesh_props(data)
|
||||
elements = tool.Ifc.get().traverse(tool.Ifc.get().by_id(props.ifc_definition_id))
|
||||
props.ifc_parameters.clear()
|
||||
for element in elements:
|
||||
@@ -974,7 +1008,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def is_profile_based(cls, data: bpy.types.Mesh) -> bool:
|
||||
return data.BIMMeshProperties.subshape_type == "PROFILE"
|
||||
props = tool.Geometry.get_mesh_props(data)
|
||||
return props.subshape_type == "PROFILE"
|
||||
|
||||
@classmethod
|
||||
def is_profile_object_active(cls) -> bool:
|
||||
@@ -992,7 +1027,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
data = obj.data
|
||||
if (
|
||||
isinstance(data, Geometry.TYPES_WITH_MESH_PROPERTIES)
|
||||
and (ifc_id := data.BIMMeshProperties.ifc_definition_id)
|
||||
and (ifc_id := tool.Geometry.get_mesh_props(data).ifc_definition_id)
|
||||
and ((item := tool.Ifc.get().by_id(ifc_id)).is_a("IfcRepresentationItem"))
|
||||
):
|
||||
return item
|
||||
@@ -1008,14 +1043,14 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
if tool.Ifc.get_entity(obj):
|
||||
return obj
|
||||
elif tool.Geometry.is_representation_item(obj):
|
||||
return bpy.context.scene.BIMGeometryProperties.representation_obj
|
||||
return tool.Geometry.get_geometry_props().representation_obj
|
||||
|
||||
@classmethod
|
||||
def is_boolean_operand(cls, obj: bpy.types.Object) -> bool:
|
||||
return bool(
|
||||
(data := obj.data)
|
||||
and isinstance(data, Geometry.TYPES_WITH_MESH_PROPERTIES)
|
||||
and (ifc_id := data.BIMMeshProperties.ifc_definition_id)
|
||||
and (ifc_id := tool.Geometry.get_mesh_props(data).ifc_definition_id)
|
||||
and (item := tool.Ifc.get().by_id(ifc_id))
|
||||
and (
|
||||
item.is_a("IfcBooleanResult")
|
||||
@@ -1041,7 +1076,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def record_object_materials(cls, obj: bpy.types.Object) -> None:
|
||||
obj.data.BIMMeshProperties.material_checksum = cls.get_material_checksum(obj)
|
||||
props = tool.Geometry.get_mesh_props(obj.data)
|
||||
props.material_checksum = cls.get_material_checksum(obj)
|
||||
|
||||
@classmethod
|
||||
def record_object_position(cls, obj: bpy.types.Object) -> None:
|
||||
@@ -1146,11 +1182,13 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def should_force_faceted_brep(cls) -> bool:
|
||||
return bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
return props.should_force_faceted_brep
|
||||
|
||||
@classmethod
|
||||
def should_force_triangulation(cls) -> bool:
|
||||
return bpy.context.scene.BIMGeometryProperties.should_force_triangulation
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
return props.should_force_triangulation
|
||||
|
||||
@classmethod
|
||||
def should_generate_uvs(cls, obj: bpy.types.Object) -> bool:
|
||||
@@ -1167,7 +1205,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def should_use_presentation_style_assignment(cls) -> bool:
|
||||
return bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
return props.should_use_presentation_style_assignment
|
||||
|
||||
@classmethod
|
||||
def get_model_representations(cls) -> list[ifcopenshell.entity_instance]:
|
||||
@@ -1238,7 +1277,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
In the most cases just use reload_representation
|
||||
as it will handle those complications by itself.
|
||||
"""
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
representation = cls.get_active_representation(obj)
|
||||
assert representation
|
||||
bonsai.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
@@ -1526,7 +1566,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def get_blender_offset_type(cls, obj: bpy.types.Object) -> Optional[str]:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
if (result := obj.BIMObjectProperties.blender_offset_type) == "NONE":
|
||||
result = obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
|
||||
@@ -1641,7 +1681,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
for item_obj in props.item_objs:
|
||||
if not (obj := item_obj.obj) or not tool.Ifc.is_moved(obj):
|
||||
continue
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
item = cls.get_active_representation(obj)
|
||||
assert item
|
||||
if item.is_a("IfcSweptAreaSolid"):
|
||||
has_changed = True
|
||||
old_position = item.Position
|
||||
@@ -1683,7 +1724,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def import_item_attributes(cls, obj: bpy.types.Object) -> None:
|
||||
props = obj.data.BIMMeshProperties
|
||||
props = tool.Geometry.get_mesh_props(obj.data)
|
||||
props.item_attributes.clear()
|
||||
item = tool.Ifc.get().by_id(props.ifc_definition_id)
|
||||
allowed_attributes = [
|
||||
@@ -1710,10 +1751,10 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
|
||||
@classmethod
|
||||
def update_item_attributes(cls, obj: bpy.types.Object) -> None:
|
||||
props = obj.data.BIMMeshProperties
|
||||
props = tool.Geometry.get_mesh_props(obj.data)
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
item = tool.Ifc.get().by_id(props.ifc_definition_id)
|
||||
item = ifc_file.by_id(props.ifc_definition_id)
|
||||
for attribute in props.item_attributes:
|
||||
setattr(item, attribute.name, attribute.get_value())
|
||||
|
||||
@@ -1738,7 +1779,9 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get())
|
||||
tool.Loader.settings.context_settings = tool.Loader.create_settings()
|
||||
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
assert isinstance(obj.data, bpy.types.Mesh)
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
obj.data.clear_geometry()
|
||||
|
||||
if item.is_a("IfcHalfSpaceSolid"):
|
||||
@@ -1802,12 +1845,14 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
props.mode = "OBJECT"
|
||||
props.is_changing_mode = False
|
||||
props.representation_obj = None
|
||||
bpy.context.scene.BIMBooleanProperties.is_editing = False
|
||||
tool.Feature.get_boolean_props().is_editing = False
|
||||
|
||||
@classmethod
|
||||
def edit_meshlike_item(cls, obj: bpy.types.Object) -> None:
|
||||
item = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if obj.data.BIMMeshProperties.mesh_checksum == cls.get_mesh_checksum(obj.data):
|
||||
item = tool.Geometry.get_active_representation(obj)
|
||||
assert item
|
||||
mprops = tool.Geometry.get_mesh_props(obj.data)
|
||||
if mprops.mesh_checksum == cls.get_mesh_checksum(obj.data):
|
||||
return
|
||||
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
@@ -1830,8 +1875,8 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
for inverse in tool.Ifc.get().get_inverse(item):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, item, new_item)
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), item)
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = new_item.id()
|
||||
cls.reload_representation(bpy.context.scene.BIMGeometryProperties.representation_obj)
|
||||
tool.Ifc.link(new_item, obj.data)
|
||||
cls.reload_representation(props.representation_obj)
|
||||
|
||||
@classmethod
|
||||
def split_by_loose_parts(cls, obj: bpy.types.Object) -> List[bpy.types.Mesh]:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import bpy
|
||||
import json
|
||||
import numpy as np
|
||||
@@ -27,24 +28,34 @@ import ifcopenshell.util.unit
|
||||
import bonsai.core.tool
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.helper
|
||||
from typing import Any, Union, Literal
|
||||
from typing import Any, Union, Literal, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.georeference.prop import BIMGeoreferenceProperties
|
||||
|
||||
|
||||
class Georeference(bonsai.core.tool.Georeference):
|
||||
COORDINATE_TYPE = Literal["blender", "local", "map"]
|
||||
|
||||
@classmethod
|
||||
def get_georeference_props(cls) -> BIMGeoreferenceProperties:
|
||||
return bpy.context.scene.BIMGeoreferenceProperties
|
||||
|
||||
@classmethod
|
||||
def add_georeferencing(cls) -> None:
|
||||
props = cls.get_georeference_props()
|
||||
tool.Ifc.run(
|
||||
"georeference.add_georeferencing",
|
||||
ifc_class=bpy.context.scene.BIMGeoreferenceProperties.coordinate_operation_class,
|
||||
ifc_class=props.coordinate_operation_class,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def import_projected_crs(cls) -> None:
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
def callback(name, prop, data):
|
||||
if name == "MapUnit":
|
||||
new = bpy.context.scene.BIMGeoreferenceProperties.projected_crs.add()
|
||||
new = props.projected_crs.add()
|
||||
new.name = name
|
||||
new.data_type = "enum"
|
||||
new.is_null = data[name] is None
|
||||
@@ -61,7 +72,6 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
new.update = "tool.Georeference.update_map_unit"
|
||||
return True
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props.projected_crs.clear()
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
@@ -84,7 +94,8 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
result = 1.0
|
||||
else:
|
||||
result = 1.0
|
||||
for attribute in bpy.context.scene.BIMGeoreferenceProperties.coordinate_operation:
|
||||
props = cls.get_georeference_props()
|
||||
for attribute in props.coordinate_operation:
|
||||
if attribute.name == "Scale":
|
||||
attribute.set_value(str(result))
|
||||
|
||||
@@ -92,7 +103,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
def import_coordinate_operation(cls) -> None:
|
||||
def callback(name, prop, data):
|
||||
if name in ("FirstCoordinate", "SecondCoordinate"):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
if name == "FirstCoordinate":
|
||||
new = props.coordinate_operation.add()
|
||||
new.name = "Measure Type"
|
||||
@@ -110,7 +121,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
prop.string_value = "" if prop.is_null else str(data[name].wrappedValue)
|
||||
return True
|
||||
elif name == "XAxisAbscissa":
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
props.is_changing_angle = True
|
||||
if data["XAxisAbscissa"] is None or data["XAxisOrdinate"] is None:
|
||||
props.x_axis_is_null = True
|
||||
@@ -132,7 +143,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
prop.string_value = "" if prop.is_null else str(data[name])
|
||||
return True
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
props.coordinate_operation.clear()
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
@@ -151,7 +162,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
return
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
props.is_changing_angle = True
|
||||
props.true_north_abscissa = "0"
|
||||
props.true_north_ordinate = "1"
|
||||
@@ -175,7 +186,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
attributes[prop.name] = tool.Ifc.get().by_id(int(prop.enum_value))
|
||||
return True
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
return bonsai.bim.helper.export_attributes(props.projected_crs, callback=callback)
|
||||
|
||||
@classmethod
|
||||
@@ -191,7 +202,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
attributes[prop.name] = tool.Ifc.get().create_entity(measure_type, float(prop.string_value))
|
||||
return True
|
||||
elif prop.name == "XAxisAbscissa":
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
if props.x_axis_is_null:
|
||||
attributes["XAxisAbscissa"] = None
|
||||
attributes["XAxisOrdinate"] = None
|
||||
@@ -206,12 +217,12 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
attributes[prop.name] = float(prop.string_value)
|
||||
return True
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
return bonsai.bim.helper.export_attributes(props.coordinate_operation, callback=callback)
|
||||
|
||||
@classmethod
|
||||
def get_true_north_attributes(cls) -> Union[list[float], None]:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
try:
|
||||
return [float(props.true_north_abscissa), float(props.true_north_ordinate)]
|
||||
except ValueError:
|
||||
@@ -219,36 +230,42 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
|
||||
@classmethod
|
||||
def enable_editing(cls) -> None:
|
||||
bpy.context.scene.BIMGeoreferenceProperties.is_editing = True
|
||||
props = cls.get_georeference_props()
|
||||
props.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def disable_editing(cls) -> None:
|
||||
bpy.context.scene.BIMGeoreferenceProperties.is_editing = False
|
||||
props = cls.get_georeference_props()
|
||||
props.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing_wcs(cls) -> None:
|
||||
bpy.context.scene.BIMGeoreferenceProperties.is_editing_wcs = True
|
||||
props = cls.get_georeference_props()
|
||||
props.is_editing_wcs = True
|
||||
|
||||
@classmethod
|
||||
def disable_editing_wcs(cls) -> None:
|
||||
bpy.context.scene.BIMGeoreferenceProperties.is_editing_wcs = False
|
||||
props = cls.get_georeference_props()
|
||||
props.is_editing_wcs = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing_true_north(cls) -> None:
|
||||
bpy.context.scene.BIMGeoreferenceProperties.is_editing_true_north = True
|
||||
props = cls.get_georeference_props()
|
||||
props.is_editing_true_north = True
|
||||
|
||||
@classmethod
|
||||
def disable_editing_true_north(cls) -> None:
|
||||
bpy.context.scene.BIMGeoreferenceProperties.is_editing_true_north = False
|
||||
props = cls.get_georeference_props()
|
||||
props.is_editing_true_north = False
|
||||
|
||||
@classmethod
|
||||
def set_coordinates(cls, io: COORDINATE_TYPE, coordinates: list[float]) -> None:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
setattr(props, f"{io}_coordinates", ",".join([str(o) for o in coordinates]))
|
||||
|
||||
@classmethod
|
||||
def get_coordinates(cls, io: COORDINATE_TYPE) -> list[float]:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
return [float(co) for co in getattr(props, f"{io}_coordinates").split(",")]
|
||||
|
||||
@classmethod
|
||||
@@ -260,7 +277,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
def xyz2enh(
|
||||
cls, coordinates: tuple[float, float, float], should_return_in_map_units: bool = True
|
||||
) -> tuple[float, float, float]:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
coordinates = ifcopenshell.util.geolocation.xyz2enh(
|
||||
coordinates[0],
|
||||
@@ -279,7 +296,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
@classmethod
|
||||
def enh2xyz(cls, coordinates: tuple[float, float, float]) -> tuple[float, float, float]:
|
||||
coordinates = ifcopenshell.util.geolocation.auto_enh2xyz(tool.Ifc.get(), *coordinates)
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
coordinates = ifcopenshell.util.geolocation.enh2xyz(
|
||||
coordinates[0],
|
||||
@@ -329,7 +346,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
|
||||
@classmethod
|
||||
def import_wcs(cls) -> None:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
wcs = None
|
||||
for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||
wcs = context.WorldCoordinateSystem
|
||||
@@ -346,7 +363,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
|
||||
@classmethod
|
||||
def export_wcs(cls) -> dict[str, float]:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = cls.get_georeference_props()
|
||||
return {
|
||||
"x": float(props.wcs_x),
|
||||
"y": float(props.wcs_y),
|
||||
@@ -361,7 +378,7 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
@classmethod
|
||||
def set_model_origin(cls) -> None:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
gprops = bpy.context.scene.BIMGeoreferenceProperties
|
||||
gprops = tool.Georeference.get_georeference_props()
|
||||
e, n, h = cls.xyz2enh((0, 0, 0), should_return_in_map_units=False)
|
||||
gprops.model_origin = f"{e},{n},{h}"
|
||||
gprops.model_origin_si = f"{e * unit_scale},{n * unit_scale},{h * unit_scale}"
|
||||
@@ -375,4 +392,4 @@ class Georeference(bonsai.core.tool.Georeference):
|
||||
|
||||
@classmethod
|
||||
def has_blender_offset(cls) -> bool:
|
||||
return bpy.context.scene.BIMGeoreferenceProperties.has_blender_offset
|
||||
return tool.Georeference.get_georeference_props().has_blender_offset
|
||||
|
||||
@@ -111,7 +111,7 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
elif isinstance(obj, bpy.types.Material):
|
||||
props = obj.BIMStyleProperties
|
||||
else:
|
||||
props = obj.BIMMeshProperties
|
||||
props = tool.Geometry.get_mesh_props(obj)
|
||||
|
||||
if props and (ifc_definition_id := props.ifc_definition_id):
|
||||
try:
|
||||
@@ -180,7 +180,7 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
cls.setup_listeners(obj)
|
||||
|
||||
IfcStore.edited_objs = set()
|
||||
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
|
||||
edited_objs = tool.Project.get_project_props().edited_objs
|
||||
for i in range(len(edited_objs))[::-1]:
|
||||
obj = edited_objs[i].obj
|
||||
if obj:
|
||||
@@ -220,7 +220,7 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
"""
|
||||
if obj in IfcStore.edited_objs:
|
||||
return
|
||||
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
|
||||
edited_objs = tool.Project.get_project_props().edited_objs
|
||||
edited_objs.add().obj = obj
|
||||
IfcStore.edited_objs.add(obj)
|
||||
IfcStore.history_edit_object(obj, finish_editing=False)
|
||||
@@ -233,7 +233,7 @@ class Ifc(bonsai.core.tool.Ifc):
|
||||
"""
|
||||
if obj not in IfcStore.edited_objs:
|
||||
return
|
||||
edited_objs = bpy.context.scene.BIMProjectProperties.edited_objs
|
||||
edited_objs = tool.Project.get_project_props().edited_objs
|
||||
edited_objs.remove(next(i for i, o in enumerate(edited_objs) if o.obj == obj))
|
||||
IfcStore.edited_objs.discard(obj)
|
||||
IfcStore.history_edit_object(obj, finish_editing=True)
|
||||
|
||||
@@ -89,7 +89,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
|
||||
@classmethod
|
||||
def get_mesh_name(cls, representation: ifcopenshell.entity_instance) -> str:
|
||||
context_id = representation.ContextOfItems.id() if hasattr(representation, "ContextOfItems") else 0
|
||||
context_id = context.id() if (context := getattr(representation, "ContextOfItems", None)) else 0
|
||||
return "{}/{}".format(context_id, representation.id())
|
||||
|
||||
@classmethod
|
||||
@@ -105,7 +105,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
mesh: tool.Geometry.TYPES_WITH_MESH_PROPERTIES,
|
||||
) -> None:
|
||||
geometry = shape.geometry if hasattr(shape, "geometry") else shape
|
||||
mesh.BIMMeshProperties.ifc_definition_id = int(geometry.id.split("-")[0])
|
||||
tool.Geometry.get_mesh_props(mesh).ifc_definition_id = int(geometry.id.split("-")[0])
|
||||
|
||||
@classmethod
|
||||
def create_surface_style_shading(
|
||||
@@ -698,7 +698,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
project_north = 0
|
||||
|
||||
if has_offset or has_rotation:
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.blender_offset_x = str(model_offset[0])
|
||||
props.blender_offset_y = str(model_offset[1])
|
||||
props.blender_offset_z = str(model_offset[2])
|
||||
@@ -747,7 +747,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
cls, element: ifcopenshell.entity_instance, is_gross: bool = False
|
||||
) -> Union[ifcopenshell.geom.ShapeElementType, None]:
|
||||
context_settings = cls.settings.gross_context_settings if is_gross else cls.settings.context_settings
|
||||
geometry_library = bpy.context.scene.BIMProjectProperties.geometry_library
|
||||
geometry_library = tool.Project.get_project_props().geometry_library
|
||||
for settings in context_settings:
|
||||
try:
|
||||
result = ifcopenshell.geom.create_shape(settings, element, geometry_library=geometry_library)
|
||||
@@ -952,7 +952,7 @@ class Loader(bonsai.core.tool.Loader):
|
||||
matrix[1][3] = offset_xyz[1]
|
||||
matrix[2][3] = offset_xyz[2]
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset:
|
||||
if obj.BIMObjectProperties.blender_offset_type == "NONE":
|
||||
obj.BIMObjectProperties.blender_offset_type = "OBJECT_PLACEMENT"
|
||||
|
||||
@@ -113,10 +113,11 @@ class Misc(bonsai.core.tool.Misc):
|
||||
|
||||
new_objs = []
|
||||
for obj in objs:
|
||||
if obj.type != "MESH" or obj == cutter:
|
||||
mesh = obj.data
|
||||
if not isinstance(mesh, bpy.types.Mesh) or obj == cutter:
|
||||
continue
|
||||
new_obj = obj.copy()
|
||||
new_obj.data = obj.data.copy()
|
||||
new_obj.data = mesh.copy()
|
||||
|
||||
for collection in obj.users_collection:
|
||||
collection.objects.link(new_obj)
|
||||
|
||||
@@ -277,7 +277,7 @@ class Model(bonsai.core.tool.Model):
|
||||
|
||||
mesh = bpy.data.meshes.new("Axis")
|
||||
mesh.from_pydata(cls.vertices, cls.edges, [])
|
||||
mesh.BIMMeshProperties.subshape_type = "AXIS"
|
||||
tool.Geometry.get_mesh_props(mesh).subshape_type = "AXIS"
|
||||
|
||||
if obj is None:
|
||||
obj = bpy.data.objects.new("Axis", mesh)
|
||||
@@ -334,7 +334,7 @@ class Model(bonsai.core.tool.Model):
|
||||
|
||||
mesh = bpy.data.meshes.new("Profile")
|
||||
mesh.from_pydata(cls.vertices, cls.edges, [])
|
||||
mesh.BIMMeshProperties.subshape_type = "PROFILE"
|
||||
tool.Geometry.get_mesh_props(mesh).subshape_type = "PROFILE"
|
||||
|
||||
if obj is None:
|
||||
obj = bpy.data.objects.new("Profile", mesh)
|
||||
@@ -376,7 +376,7 @@ class Model(bonsai.core.tool.Model):
|
||||
|
||||
mesh = bpy.data.meshes.new("Curve")
|
||||
mesh.from_pydata(cls.vertices, cls.edges, [])
|
||||
mesh.BIMMeshProperties.subshape_type = "PROFILE"
|
||||
tool.Geometry.get_mesh_props(mesh).subshape_type = "PROFILE"
|
||||
|
||||
if obj is None:
|
||||
obj = bpy.data.objects.new("Curve", mesh)
|
||||
@@ -417,7 +417,7 @@ class Model(bonsai.core.tool.Model):
|
||||
|
||||
mesh = bpy.data.meshes.new("Surface")
|
||||
mesh.from_pydata(cls.vertices, cls.edges, [])
|
||||
mesh.BIMMeshProperties.subshape_type = "PROFILE"
|
||||
tool.Geometry.get_mesh_props(mesh).subshape_type = "PROFILE"
|
||||
|
||||
if obj is None:
|
||||
obj = bpy.data.objects.new("Surface", mesh)
|
||||
@@ -569,7 +569,10 @@ class Model(bonsai.core.tool.Model):
|
||||
element: Optional[ifcopenshell.entity_instance] = None,
|
||||
representation: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
"""Either element or representation must be provided."""
|
||||
assert element or representation, "Either element or representation must be provided."
|
||||
if representation is None:
|
||||
assert element
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
return []
|
||||
@@ -1461,9 +1464,9 @@ class Model(bonsai.core.tool.Model):
|
||||
after material assignment or material unassignment.
|
||||
"""
|
||||
for element in elements:
|
||||
if not (obj := tool.Ifc.get_object(element)) or not obj.data:
|
||||
if not (obj := tool.Ifc.get_object(element)) or not (data := obj.data):
|
||||
continue
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
representation = tool.Ifc.get().by_id(tool.Geometry.get_mesh_props(data).ifc_definition_id)
|
||||
bonsai.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
@@ -1931,7 +1934,9 @@ class Model(bonsai.core.tool.Model):
|
||||
or it's not referring to an object (e.g. potential boolean object)."""
|
||||
if obj.type != "MESH":
|
||||
return
|
||||
return obj.data.BIMMeshProperties.obj
|
||||
mesh = obj.data
|
||||
assert isinstance(mesh, bpy.types.Mesh)
|
||||
return tool.Geometry.get_mesh_props(mesh).obj
|
||||
|
||||
@classmethod
|
||||
def get_tracked_opening_type(cls, obj: bpy.types.Object) -> Union[Literal["OPENING", "BOOLEAN"], None]:
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import bmesh
|
||||
import math
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import bonsai.core.tool
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.drawing.helper import format_distance
|
||||
@@ -452,7 +453,8 @@ class Polyline(bonsai.core.tool.Polyline):
|
||||
def format_input_ui_units(cls, value: float, is_area: bool = False) -> str:
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
if bpy.context.scene.unit_settings.system == "IMPERIAL":
|
||||
precision = bpy.context.scene.DocProperties.imperial_precision
|
||||
dprops = tool.Drawing.get_document_props()
|
||||
precision = dprops.imperial_precision
|
||||
if is_area:
|
||||
area_unit = bpy.context.scene.BIMProperties.area_unit
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get(), unit_type=area_unit)
|
||||
|
||||
@@ -235,7 +235,7 @@ class Project(bonsai.core.tool.Project):
|
||||
|
||||
@classmethod
|
||||
def load_linked_models_from_ifc(cls) -> None:
|
||||
links = bpy.context.scene.BIMProjectProperties.links
|
||||
links = tool.Project.get_project_props().links
|
||||
links.clear()
|
||||
links_document = cls.get_linked_models_document()
|
||||
if not links_document:
|
||||
@@ -252,7 +252,7 @@ class Project(bonsai.core.tool.Project):
|
||||
@classmethod
|
||||
def save_linked_models_to_ifc(cls) -> None:
|
||||
ifc_file = tool.Ifc.get()
|
||||
links = bpy.context.scene.BIMProjectProperties.links
|
||||
links = tool.Project.get_project_props().links
|
||||
filepaths: set[Path] = set()
|
||||
for link in links:
|
||||
filepaths.add(Path(link.name))
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
@@ -49,12 +50,12 @@ class Root(bonsai.core.tool.Root):
|
||||
tool.Geometry.run_style_add_style(obj=mat)
|
||||
for mat in tool.Geometry.get_object_materials_without_styles(obj)
|
||||
]
|
||||
ifcopenshell.api.run(
|
||||
"style.assign_representation_styles",
|
||||
props = tool.Geometry.get_geometry_props()
|
||||
ifcopenshell.api.style.assign_representation_styles(
|
||||
tool.Ifc.get(),
|
||||
shape_representation=body,
|
||||
styles=tool.Geometry.get_styles(obj),
|
||||
should_use_presentation_style_assignment=bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment,
|
||||
should_use_presentation_style_assignment=props.should_use_presentation_style_assignment,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -169,8 +170,8 @@ class Root(bonsai.core.tool.Root):
|
||||
|
||||
@classmethod
|
||||
def get_object_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if obj.data and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if obj.data and (mesh_props := tool.Geometry.get_mesh_props(obj.data)).ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(mesh_props.ifc_definition_id)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element.is_a("IfcTypeProduct"):
|
||||
if element.RepresentationMaps:
|
||||
@@ -302,8 +303,8 @@ class Root(bonsai.core.tool.Root):
|
||||
voided_objs.append(subobj)
|
||||
|
||||
for voided_obj in voided_objs:
|
||||
if voided_obj.data:
|
||||
representation = tool.Ifc.get().by_id(voided_obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
if data := voided_obj.data:
|
||||
representation = tool.Ifc.get().by_id(tool.Geometry.get_mesh_props(data).ifc_definition_id)
|
||||
bonsai.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
@@ -421,8 +422,8 @@ class Root(bonsai.core.tool.Root):
|
||||
to unlink them.
|
||||
"""
|
||||
tool.Ifc.unlink(obj=obj)
|
||||
if hasattr(obj.data, "BIMMeshProperties"):
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
if tool.Geometry.has_mesh_properties((data := obj.data)):
|
||||
tool.Geometry.get_mesh_props(mesh).ifc_definition_id = 0
|
||||
for material_slot in obj.material_slots:
|
||||
if material := material_slot.material:
|
||||
tool.Ifc.unlink(obj=material)
|
||||
|
||||
@@ -25,6 +25,7 @@ import math
|
||||
import mathutils
|
||||
from mathutils import Matrix, Vector
|
||||
from lark import Lark, Transformer
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Snap(bonsai.core.tool.Snap):
|
||||
@@ -313,7 +314,9 @@ class Snap(bonsai.core.tool.Snap):
|
||||
plane_normal = tool.Polyline.use_transform_orientations(plane_normal)
|
||||
return plane_origin, plane_normal
|
||||
|
||||
def cast_rays_to_single_object(obj, mouse_pos):
|
||||
def cast_rays_to_single_object(
|
||||
obj: bpy.types.Object, mouse_pos: tuple[int, int]
|
||||
) -> Union[tuple[bpy.types.Object, Vector, int], tuple[None, None, None]]:
|
||||
if obj.type != "MESH":
|
||||
return None, None, None
|
||||
hit, normal, face_index = tool.Raycast.obj_ray_cast(context, event, obj)
|
||||
@@ -332,7 +335,9 @@ class Snap(bonsai.core.tool.Snap):
|
||||
else:
|
||||
return None, None, None
|
||||
|
||||
def cast_rays_and_get_best_object(objs_to_raycast, mouse_pos):
|
||||
def cast_rays_and_get_best_object(
|
||||
objs_to_raycast: list[bpy.types.Object], mouse_pos: tuple[int, int]
|
||||
) -> Union[tuple[bpy.types.Object, Vector, int], tuple[None, None, None]]:
|
||||
best_length_squared = 1.0
|
||||
best_obj = None
|
||||
best_hit = None
|
||||
|
||||
@@ -699,9 +699,10 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
for obj in bpy.context.visible_objects:
|
||||
visible_element = tool.Ifc.get_entity(obj)
|
||||
|
||||
old_mesh = obj.data
|
||||
if (
|
||||
not visible_element
|
||||
or obj.type != "MESH"
|
||||
or not isinstance(old_mesh, bpy.types.Mesh)
|
||||
or not cls.is_bounding_class(visible_element)
|
||||
or not tool.Drawing.is_intersecting_plane(obj, cut_point, cut_normal)
|
||||
):
|
||||
@@ -959,7 +960,7 @@ class Spatial(bonsai.core.tool.Spatial):
|
||||
old_mesh = active_obj.data
|
||||
old_mesh_name = old_mesh.name
|
||||
assert active_obj and isinstance(old_mesh, bpy.types.Mesh)
|
||||
mesh.BIMMeshProperties.ifc_definition_id = old_mesh.BIMMeshProperties.ifc_definition_id
|
||||
tool.Geometry.get_mesh_props(mesh).ifc_definition_id = tool.Geometry.get_mesh_props(old_mesh).ifc_definition_id
|
||||
tool.Geometry.change_object_data(active_obj, mesh, is_global=True)
|
||||
tool.Ifc.edit(active_obj)
|
||||
tool.Blender.remove_data_block(old_mesh)
|
||||
|
||||
@@ -32,7 +32,7 @@ class Surveyor(bonsai.core.tool.Surveyor):
|
||||
def get_absolute_matrix(cls, obj: bpy.types.Object) -> npt.NDArray[np.float64]:
|
||||
M_TRANSLATION = (slice(0, 3), 3)
|
||||
matrix = np.array(obj.matrix_world)
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
if props.has_blender_offset and obj.BIMObjectProperties.blender_offset_type != "NOT_APPLICABLE":
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
coordinate_offset = tool.Geometry.get_cartesian_point_offset(obj)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# This can be run using `blender -b -P headless_import.py`
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.ifc import IfcStore
|
||||
|
||||
# When federating, you may wish to manually specify the origin to ensure models
|
||||
# with different or arbitrary origin conventions will turn up in the right spot.
|
||||
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
|
||||
# A good idea it to test import a portion of the model (or grids only) and check
|
||||
# georeferencing coordinates in the IFC Georeferencing panel before filling out
|
||||
@@ -19,7 +20,7 @@ props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
# props.blender_x_axis_ordinate = '0.989063862448262'
|
||||
# props.has_blender_offset = True
|
||||
|
||||
props = bpy.context.scene.BIMProjectProperties
|
||||
props = tool.Project.get_project_props()
|
||||
|
||||
# Generally recommended to disable caching for stability right now
|
||||
props.should_cache = False
|
||||
|
||||
@@ -23,6 +23,7 @@ import bpy
|
||||
import pytest
|
||||
import webbrowser
|
||||
import bonsai.bim.handler
|
||||
import bonsai.tool as tool
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
@@ -66,7 +67,8 @@ class NewIfc4X3:
|
||||
bpy.data.batch_remove(bpy.data.objects)
|
||||
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
|
||||
bonsai.bim.handler.load_post(None)
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC4X3_ADD2"
|
||||
props = tool.Project.get_project_props()
|
||||
props.export_schema = "IFC4X3_ADD2"
|
||||
bpy.ops.bim.create_project()
|
||||
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ scenarios("feature")
|
||||
|
||||
variables = {
|
||||
"cwd": Path.cwd().as_posix(),
|
||||
"ifc": "IfcStore.get_file()",
|
||||
"ifc": "tool.Ifc.get()",
|
||||
"pset_ifc": "IfcStore.pset_template_file",
|
||||
"classification_ifc": "IfcStore.classification_file",
|
||||
}
|
||||
@@ -190,7 +190,8 @@ def an_empty_blender_session():
|
||||
# default project settings
|
||||
bpy.context.scene.unit_settings.system = "METRIC"
|
||||
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
props = tool.Project.get_project_props()
|
||||
props.template_file = "0"
|
||||
tool.Blender.get_addon_preferences().should_play_chaching_sound = False
|
||||
|
||||
|
||||
@@ -203,7 +204,8 @@ def an_empty_ifc_project():
|
||||
@given("an empty IFC2X3 project")
|
||||
def an_empty_ifc_2x3_project():
|
||||
an_empty_blender_session()
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = "IFC2X3"
|
||||
props = tool.Project.get_project_props()
|
||||
props.export_schema = "IFC2X3"
|
||||
bpy.ops.bim.create_project()
|
||||
|
||||
|
||||
@@ -742,7 +744,7 @@ def the_object_name_has_a_representation_type_of_context(name, type, context):
|
||||
def the_object_name_data_is_a_type_representation_of_context(name, type, context):
|
||||
ifc = an_ifc_file_exists()
|
||||
context, subcontext, target_view = context.split("/")
|
||||
rep = ifc.by_id(the_object_name_exists(name).data.BIMMeshProperties.ifc_definition_id)
|
||||
rep = ifc.by_id(tool.Geometry.get_mesh_props(the_object_name_exists(name).data).ifc_definition_id)
|
||||
assert rep
|
||||
assert rep.RepresentationType == type, f"The object {name} is not a {type} representation"
|
||||
assert rep.ContextOfItems.ContextType == context
|
||||
@@ -888,7 +890,7 @@ def the_object_name_has_no_data(name):
|
||||
|
||||
@then(parsers.parse('the object "{name}" has data which is an IFC representation'))
|
||||
def the_object_name_has_ifc_representation_data(name):
|
||||
id = the_object_name_exists(name).data.BIMMeshProperties.ifc_definition_id
|
||||
id = tool.Geometry.get_mesh_props(the_object_name_exists(name).data).ifc_definition_id
|
||||
assert id != 0, f"The ID is {id}"
|
||||
|
||||
|
||||
|
||||
@@ -113,30 +113,34 @@ class TestDeleteDrawingElements(NewFile):
|
||||
|
||||
class TestDisableEditingDrawings(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.is_editing_drawings = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_drawings = True
|
||||
subject.disable_editing_drawings()
|
||||
assert bpy.context.scene.DocProperties.is_editing_drawings == False
|
||||
assert props.is_editing_drawings == False
|
||||
|
||||
|
||||
class TestDisableEditingSchedules(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.is_editing_schedules = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_schedules = True
|
||||
subject.disable_editing_schedules()
|
||||
assert bpy.context.scene.DocProperties.is_editing_schedules == False
|
||||
assert props.is_editing_schedules == False
|
||||
|
||||
|
||||
class TestDisableEditingReferences(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.is_editing_references = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_references = True
|
||||
subject.disable_editing_references()
|
||||
assert bpy.context.scene.DocProperties.is_editing_references == False
|
||||
assert props.is_editing_references == False
|
||||
|
||||
|
||||
class TestDisableEditingSheets(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.is_editing_sheets = True
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_sheets = True
|
||||
subject.disable_editing_sheets()
|
||||
assert bpy.context.scene.DocProperties.is_editing_sheets == False
|
||||
assert props.is_editing_sheets == False
|
||||
|
||||
|
||||
class TestDisableEditingText(NewFile):
|
||||
@@ -166,30 +170,34 @@ class TestEnableEditing(NewFile):
|
||||
|
||||
class TestEnableEditingDrawings(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.is_editing_drawings = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_drawings = False
|
||||
subject.enable_editing_drawings()
|
||||
assert bpy.context.scene.DocProperties.is_editing_drawings == True
|
||||
assert props.is_editing_drawings == True
|
||||
|
||||
|
||||
class TestEnableEditingSchedules(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.is_editing_schedules = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_schedules = False
|
||||
subject.enable_editing_schedules()
|
||||
assert bpy.context.scene.DocProperties.is_editing_schedules == True
|
||||
assert props.is_editing_schedules == True
|
||||
|
||||
|
||||
class TestEnableEditingReferences(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.is_editing_references = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_references = False
|
||||
subject.enable_editing_references()
|
||||
assert bpy.context.scene.DocProperties.is_editing_references == True
|
||||
assert props.is_editing_references == True
|
||||
|
||||
|
||||
class TestEnableEditingSheets(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.is_editing_sheets = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.is_editing_sheets = False
|
||||
subject.enable_editing_sheets()
|
||||
assert bpy.context.scene.DocProperties.is_editing_sheets == True
|
||||
assert props.is_editing_sheets == True
|
||||
|
||||
|
||||
class TestEnableEditingText(NewFile):
|
||||
@@ -492,7 +500,7 @@ class TestImportDrawings(NewFile):
|
||||
pset = ifcopenshell.api.run("pset.add_pset", ifc, product=drawing, name="EPset_Drawing")
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc, pset=pset, properties={"TargetView": "PLAN_VIEW"})
|
||||
subject.import_drawings()
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
for d in props.drawings:
|
||||
d.is_expanded = True
|
||||
subject.import_drawings()
|
||||
@@ -508,7 +516,7 @@ class TestImportSchedules(NewFile):
|
||||
ifc.createIfcDocumentInformation(Identification="Y", Name="FOOBAZ")
|
||||
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="SCHEDULE")
|
||||
subject.import_documents("SCHEDULE")
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
assert props.schedules[0].ifc_definition_id == document.id()
|
||||
assert props.schedules[0].identification == "X"
|
||||
assert props.schedules[0].name == "FOOBAR"
|
||||
@@ -519,7 +527,7 @@ class TestImportSchedules(NewFile):
|
||||
ifc.createIfcDocumentInformation(DocumentId="Y", Name="FOOBAZ")
|
||||
document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="SCHEDULE")
|
||||
subject.import_documents("SCHEDULE")
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
assert props.schedules[0].ifc_definition_id == document.id()
|
||||
assert props.schedules[0].identification == "X"
|
||||
assert props.schedules[0].name == "FOOBAR"
|
||||
@@ -532,7 +540,7 @@ class TestImportReferences(NewFile):
|
||||
ifc.createIfcDocumentInformation(Identification="Y", Name="FOOBAZ")
|
||||
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="REFERENCE")
|
||||
subject.import_documents("REFERENCE")
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
assert props.references[0].ifc_definition_id == document.id()
|
||||
assert props.references[0].identification == "X"
|
||||
assert props.references[0].name == "FOOBAR"
|
||||
@@ -543,7 +551,7 @@ class TestImportReferences(NewFile):
|
||||
ifc.createIfcDocumentInformation(DocumentId="Y", Name="FOOBAZ")
|
||||
document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="REFERENCE")
|
||||
subject.import_documents("REFERENCE")
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
assert props.references[0].ifc_definition_id == document.id()
|
||||
assert props.references[0].identification == "X"
|
||||
assert props.references[0].name == "FOOBAR"
|
||||
@@ -556,7 +564,7 @@ class TestImportSheets(NewFile):
|
||||
ifc.createIfcDocumentInformation(Identification="Y", Name="FOOBAZ")
|
||||
document = ifc.createIfcDocumentInformation(Identification="X", Name="FOOBAR", Scope="SHEET")
|
||||
subject.import_sheets()
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
assert props.sheets[0].ifc_definition_id == document.id()
|
||||
assert props.sheets[0].identification == "X"
|
||||
assert props.sheets[0].name == "FOOBAR"
|
||||
@@ -567,7 +575,7 @@ class TestImportSheets(NewFile):
|
||||
ifc.createIfcDocumentInformation(DocumentId="Y", Name="FOOBAZ")
|
||||
document = ifc.createIfcDocumentInformation(DocumentId="X", Name="FOOBAR", Scope="SHEET")
|
||||
subject.import_sheets()
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
assert props.sheets[0].ifc_definition_id == document.id()
|
||||
assert props.sheets[0].identification == "X"
|
||||
assert props.sheets[0].name == "FOOBAR"
|
||||
@@ -657,9 +665,10 @@ class TestSetName(NewFile):
|
||||
|
||||
class TestShowDecorations(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.DocProperties.should_draw_decorations = False
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.should_draw_decorations = False
|
||||
subject.show_decorations()
|
||||
assert bpy.context.scene.DocProperties.should_draw_decorations is True
|
||||
assert props.should_draw_decorations is True
|
||||
|
||||
|
||||
class TestDrawingMaintainingSheetPosition(NewFile):
|
||||
@@ -680,7 +689,7 @@ class TestDrawingMaintainingSheetPosition(NewFile):
|
||||
return drawing_data
|
||||
|
||||
def test_run(self):
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
bpy.ops.bim.create_project()
|
||||
ifc = tool.Ifc.get()
|
||||
sheet_path = Path.cwd() / "layouts" / "A00 - UNTITLED.svg"
|
||||
@@ -845,10 +854,11 @@ class TestDrawingStyles(NewFile):
|
||||
ifc = tool.Ifc.get()
|
||||
drawing = ifc.by_type("IfcAnnotation")[0]
|
||||
bpy.ops.bim.expand_target_view(target_view="PLAN_VIEW")
|
||||
props = bpy.context.scene.DocProperties
|
||||
props = tool.Drawing.get_document_props()
|
||||
props.active_drawing_index = 2
|
||||
bpy.ops.bim.activate_drawing(drawing=drawing.id())
|
||||
self.drawing_styles = bpy.context.scene.DocProperties.drawing_styles
|
||||
props = tool.Drawing.get_document_props()
|
||||
self.drawing_styles = props.drawing_styles
|
||||
|
||||
def test_drawing_styles_not_loaded_if_underlay_is_inactive(self):
|
||||
self.setup_project_with_drawing()
|
||||
@@ -867,7 +877,8 @@ class TestDrawingStyles(NewFile):
|
||||
|
||||
class TestAddReferenceImage(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
props = tool.Project.get_project_props()
|
||||
props.template_file = "0"
|
||||
bpy.ops.bim.create_project()
|
||||
ifc_path = Path("test/files/temp/test.ifc").absolute()
|
||||
bpy.ops.bim.save_project(filepath=str(ifc_path), should_save_as=True)
|
||||
|
||||
@@ -171,14 +171,14 @@ class TestGetCartesianPointCoordinateOffset(NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = True
|
||||
obj.BIMObjectProperties.cartesian_point_offset = "1,2,3"
|
||||
assert np.allclose(subject.get_cartesian_point_offset(obj), np.array((1.0, 2.0, 3.0)))
|
||||
|
||||
def test_get_null_if_not_a_cartesian_point_offset_type(self):
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = True
|
||||
obj.BIMObjectProperties.cartesian_point_offset = "1,2,3"
|
||||
assert subject.get_cartesian_point_offset(obj) is None
|
||||
@@ -186,7 +186,7 @@ class TestGetCartesianPointCoordinateOffset(NewFile):
|
||||
def test_get_null_if_no_blender_offset(self):
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
obj.BIMObjectProperties.blender_offset_type = "CARTESIAN_POINT"
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = False
|
||||
assert subject.get_cartesian_point_offset(obj) is None
|
||||
|
||||
@@ -237,14 +237,15 @@ class TestImportRepresentationParameters(NewFile):
|
||||
item = ifc.createIfcExtrudedAreaSolid(SweptArea=swept_area, Depth=2)
|
||||
representation = ifc.createIfcShapeRepresentation(Items=[item])
|
||||
data = bpy.data.meshes.new("Mesh")
|
||||
data.BIMMeshProperties.ifc_definition_id = representation.id()
|
||||
mprops = tool.Geometry.get_mesh_props(data)
|
||||
mprops.ifc_definition_id = representation.id()
|
||||
subject.import_representation_parameters(data)
|
||||
assert data.BIMMeshProperties.ifc_parameters[0].name == "IfcExtrudedAreaSolid/Depth"
|
||||
assert data.BIMMeshProperties.ifc_parameters[0].step_id == item.id()
|
||||
assert data.BIMMeshProperties.ifc_parameters[0].index == 3
|
||||
assert data.BIMMeshProperties.ifc_parameters[1].name == "IfcCircleProfileDef/Radius"
|
||||
assert data.BIMMeshProperties.ifc_parameters[1].step_id == swept_area.id()
|
||||
assert data.BIMMeshProperties.ifc_parameters[1].index == 3
|
||||
assert mprops.ifc_parameters[0].name == "IfcExtrudedAreaSolid/Depth"
|
||||
assert mprops.ifc_parameters[0].step_id == item.id()
|
||||
assert mprops.ifc_parameters[0].index == 3
|
||||
assert mprops.ifc_parameters[1].name == "IfcCircleProfileDef/Radius"
|
||||
assert mprops.ifc_parameters[1].step_id == swept_area.id()
|
||||
assert mprops.ifc_parameters[1].index == 3
|
||||
|
||||
|
||||
class TestIsBodyRepresentation(NewFile):
|
||||
@@ -293,7 +294,7 @@ class TestLink(NewFile):
|
||||
element = ifc.createIfcShapeRepresentation()
|
||||
obj = bpy.data.meshes.new("Mesh")
|
||||
subject.link(element, obj)
|
||||
assert obj.BIMMeshProperties.ifc_definition_id == element.id()
|
||||
assert tool.Geometry.get_mesh_props(obj).ifc_definition_id == element.id()
|
||||
|
||||
|
||||
class TestRecordObjectMaterials(NewFile):
|
||||
@@ -306,7 +307,7 @@ class TestRecordObjectMaterials(NewFile):
|
||||
material.BIMStyleProperties.ifc_definition_id = style.id()
|
||||
obj.data.materials.append(material)
|
||||
subject.record_object_materials(obj)
|
||||
assert obj.data.BIMMeshProperties.material_checksum == str([style.id()])
|
||||
assert tool.Geometry.get_mesh_props(obj).material_checksum == str([style.id()])
|
||||
|
||||
|
||||
class TestRecordObjectPosition(NewFile):
|
||||
|
||||
@@ -39,7 +39,7 @@ class TestImportProjectedCRS(NewFile):
|
||||
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
|
||||
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
|
||||
subject.import_projected_crs()
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
assert len(props.projected_crs) == 0
|
||||
|
||||
def test_importing_projected_crs(self):
|
||||
@@ -58,7 +58,7 @@ class TestImportProjectedCRS(NewFile):
|
||||
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT")
|
||||
projected_crs.MapUnit = unit
|
||||
subject.import_projected_crs()
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
assert props.projected_crs.get("Name").string_value == "Name"
|
||||
assert props.projected_crs.get("Description").string_value == "Description"
|
||||
assert props.projected_crs.get("GeodeticDatum").string_value == "GeodeticDatum"
|
||||
@@ -71,7 +71,7 @@ class TestImportProjectedCRS(NewFile):
|
||||
ifc = ifcopenshell.file(schema="IFC2X3")
|
||||
tool.Ifc.set(ifc)
|
||||
subject.import_projected_crs()
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
assert len(props.projected_crs) == 0
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class TestImportCoordinateOperation(NewFile):
|
||||
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
|
||||
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
|
||||
subject.import_coordinate_operation()
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
assert len(props.coordinate_operation) == 0
|
||||
|
||||
def test_importing_coordinate_operation(self):
|
||||
@@ -99,7 +99,7 @@ class TestImportCoordinateOperation(NewFile):
|
||||
map_conversion.XAxisOrdinate = 5
|
||||
map_conversion.Scale = 6
|
||||
subject.import_coordinate_operation()
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
assert props.coordinate_operation.get("Eastings").string_value == "1.0"
|
||||
assert props.coordinate_operation.get("Northings").string_value == "2.0"
|
||||
assert props.coordinate_operation.get("OrthogonalHeight").string_value == "3.0"
|
||||
@@ -112,7 +112,7 @@ class TestImportCoordinateOperation(NewFile):
|
||||
ifc = ifcopenshell.file(schema="IFC2X3")
|
||||
tool.Ifc.set(ifc)
|
||||
subject.import_coordinate_operation()
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
assert len(props.coordinate_operation) == 0
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ class TestImportTrueNorth(NewFile):
|
||||
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
|
||||
ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
|
||||
subject.import_true_north()
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
assert props.true_north_abscissa == "0"
|
||||
assert props.true_north_ordinate == "1"
|
||||
assert props.true_north_angle == "0"
|
||||
@@ -135,7 +135,7 @@ class TestImportTrueNorth(NewFile):
|
||||
context = ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
|
||||
context.TrueNorth = ifc.createIfcDirection((1.0, 2.0, 0.0))
|
||||
subject.import_true_north()
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
assert props.true_north_abscissa == "1.0"
|
||||
assert props.true_north_ordinate == "2.0"
|
||||
assert props.true_north_angle == "-26.5650512"
|
||||
@@ -176,35 +176,39 @@ class TestGetTrueNorthAttributes(NewFile):
|
||||
|
||||
class TestEnableEditing(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMGeoreferenceProperties.is_editing = False
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.is_editing = False
|
||||
subject.enable_editing()
|
||||
assert bpy.context.scene.BIMGeoreferenceProperties.is_editing is True
|
||||
assert props.is_editing is True
|
||||
|
||||
|
||||
class TestDisableEditing(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMGeoreferenceProperties.is_editing = True
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.is_editing = True
|
||||
subject.disable_editing()
|
||||
assert bpy.context.scene.BIMGeoreferenceProperties.is_editing is False
|
||||
assert props.is_editing is False
|
||||
|
||||
|
||||
class TestSetCoordinates(NewFile):
|
||||
def test_run(self):
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
subject.set_coordinates("local", [1.0, 2.0, 3.0])
|
||||
assert bpy.context.scene.BIMGeoreferenceProperties.local_coordinates == "1.0,2.0,3.0"
|
||||
assert props.local_coordinates == "1.0,2.0,3.0"
|
||||
subject.set_coordinates("blender", [4.0, 5.0, 6.0])
|
||||
assert bpy.context.scene.BIMGeoreferenceProperties.blender_coordinates == "4.0,5.0,6.0"
|
||||
assert props.blender_coordinates == "4.0,5.0,6.0"
|
||||
subject.set_coordinates("map", [7.0, 8.0, 9.0])
|
||||
assert bpy.context.scene.BIMGeoreferenceProperties.map_coordinates == "7.0,8.0,9.0"
|
||||
assert props.map_coordinates == "7.0,8.0,9.0"
|
||||
|
||||
|
||||
class TestGetCoordinates(NewFile):
|
||||
def test_run(self):
|
||||
bpy.context.scene.BIMGeoreferenceProperties.local_coordinates = "1.0,2.0,3.0"
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.local_coordinates = "1.0,2.0,3.0"
|
||||
assert subject.get_coordinates("local") == [1.0, 2.0, 3.0]
|
||||
bpy.context.scene.BIMGeoreferenceProperties.blender_coordinates = "4.0,5.0,6.0"
|
||||
props.blender_coordinates = "4.0,5.0,6.0"
|
||||
assert subject.get_coordinates("blender") == [4.0, 5.0, 6.0]
|
||||
bpy.context.scene.BIMGeoreferenceProperties.map_coordinates = "7.0,8.0,9.0"
|
||||
props.map_coordinates = "7.0,8.0,9.0"
|
||||
assert subject.get_coordinates("map") == [7.0, 8.0, 9.0]
|
||||
|
||||
|
||||
@@ -231,7 +235,7 @@ class TestXyz2Enh(NewFile):
|
||||
ifc = ifcopenshell.file()
|
||||
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
|
||||
tool.Ifc.set(ifc)
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = True
|
||||
props.blender_offset_x = "1.0"
|
||||
assert subject.xyz2enh([0.0, 0.0, 0.0]) == (1.0, 0.0, 0.0)
|
||||
@@ -247,7 +251,7 @@ class TestXyz2Enh(NewFile):
|
||||
assert subject.xyz2enh([0.0, 0.0, 0.0]) == (1.0, 0.0, 0.0)
|
||||
|
||||
def test_applying_both_blender_offset_and_map_conversion(self):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = True
|
||||
props.blender_offset_x = "1.0"
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -271,7 +275,7 @@ class TestEnh2Xyz(NewFile):
|
||||
ifc = ifcopenshell.file()
|
||||
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
|
||||
tool.Ifc.set(ifc)
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = True
|
||||
props.blender_offset_x = "1.0"
|
||||
assert subject.enh2xyz([0.0, 0.0, 0.0]) == (-1.0, 0.0, 0.0)
|
||||
@@ -287,7 +291,7 @@ class TestEnh2Xyz(NewFile):
|
||||
assert subject.enh2xyz([0.0, 0.0, 0.0]) == (-1.0, 0.0, 0.0)
|
||||
|
||||
def test_applying_both_blender_offset_and_map_conversion(self):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = True
|
||||
props.blender_offset_x = "1.0"
|
||||
ifc = ifcopenshell.file()
|
||||
|
||||
@@ -189,7 +189,7 @@ class TestLink(test.bim.bootstrap.NewFile):
|
||||
element = ifc.create_entity("IfcShapeRepresentation")
|
||||
obj = bpy.data.meshes.new("Material")
|
||||
subject.link(element, obj)
|
||||
assert obj.BIMMeshProperties.ifc_definition_id == element.id()
|
||||
assert tool.Geometry.get_mesh_props(obj).ifc_definition_id == element.id()
|
||||
|
||||
|
||||
class TestUnlink(test.bim.bootstrap.NewFile):
|
||||
|
||||
@@ -520,7 +520,8 @@ class TestLoadingIndexedMap(NewFile):
|
||||
class TestSetupActiveBsddClassification(NewFile):
|
||||
def run_test(self, schema: ifcopenshell.util.schema.IFC_SCHEMA) -> None:
|
||||
schema_ = "IFC4X3_ADD2" if schema == "IFC4X3" else schema
|
||||
bpy.context.scene.BIMProjectProperties.export_schema = schema_
|
||||
props = tool.Project.get_project_props()
|
||||
props.export_schema = schema_
|
||||
bpy.ops.bim.create_project()
|
||||
ifc_file = tool.Ifc.get()
|
||||
name = "CCI Construction"
|
||||
|
||||
@@ -378,7 +378,7 @@ class TestGenerateStair2DProfile(NewFile):
|
||||
|
||||
class TestUsingArrays(NewFile):
|
||||
def setup_array(self, add_second_layer=False, sync_children=False):
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
tool.Project.get_project_props().template_file = "0"
|
||||
bpy.ops.bim.create_project()
|
||||
|
||||
bpy.ops.mesh.primitive_cube_add()
|
||||
@@ -467,7 +467,8 @@ class TestApplyIfcMaterialChanges(NewFile):
|
||||
return mesh
|
||||
|
||||
def setup_test(self, and_elements: bool = True) -> None:
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "0"
|
||||
props = tool.Project.get_project_props()
|
||||
props.template_file = "0"
|
||||
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
|
||||
bpy.ops.bim.create_project()
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
@@ -244,25 +244,25 @@ class TestLoadProject(NewFile):
|
||||
|
||||
class TestLoadLinkedModels(NewFile):
|
||||
def test_load_linked_models_no_document(self):
|
||||
links = bpy.context.scene.BIMProjectProperties.links
|
||||
props = tool.Project.get_project_props()
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
subject.load_linked_models_from_ifc()
|
||||
assert len(links) == 0
|
||||
assert len(props.links) == 0
|
||||
|
||||
def test_load_linked_models_document_no_references(self):
|
||||
ifc = ifcopenshell.file()
|
||||
links = bpy.context.scene.BIMProjectProperties.links
|
||||
props = tool.Project.get_project_props()
|
||||
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
|
||||
document = ifcopenshell.api.document.add_information(ifc)
|
||||
document.Name = "BBIM_Linked_Models"
|
||||
tool.Ifc.set(ifc)
|
||||
subject.load_linked_models_from_ifc()
|
||||
assert len(links) == 0
|
||||
assert len(props.links) == 0
|
||||
|
||||
def test_load_linked_models_document_with_references(self):
|
||||
ifc = ifcopenshell.file()
|
||||
links = bpy.context.scene.BIMProjectProperties.links
|
||||
props = tool.Project.get_project_props()
|
||||
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
|
||||
document = ifcopenshell.api.document.add_information(ifc)
|
||||
document.Name = "BBIM_Linked_Models"
|
||||
@@ -271,8 +271,8 @@ class TestLoadLinkedModels(NewFile):
|
||||
reference.Location = linked_model_path
|
||||
tool.Ifc.set(ifc)
|
||||
subject.load_linked_models_from_ifc()
|
||||
assert len(links) == 1
|
||||
assert links[0].name == linked_model_path
|
||||
assert len(props.links) == 1
|
||||
assert props.links[0].name == linked_model_path
|
||||
|
||||
|
||||
class TestSaveLinkedModelsToIfc(NewFile):
|
||||
@@ -286,8 +286,8 @@ class TestSaveLinkedModelsToIfc(NewFile):
|
||||
def test_save_linked_models_to_ifc_paths_to_add(self):
|
||||
ifc = ifcopenshell.file()
|
||||
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
|
||||
links = bpy.context.scene.BIMProjectProperties.links
|
||||
link = links.add()
|
||||
props = tool.Project.get_project_props()
|
||||
link = props.links.add()
|
||||
linked_model_path = "test.ifc"
|
||||
link.name = linked_model_path
|
||||
tool.Ifc.set(ifc)
|
||||
@@ -299,7 +299,7 @@ class TestSaveLinkedModelsToIfc(NewFile):
|
||||
|
||||
def test_save_linked_models_to_ifc_already_created_references(self):
|
||||
ifc = ifcopenshell.file()
|
||||
links = bpy.context.scene.BIMProjectProperties.links
|
||||
links = tool.Project.get_project_props().links
|
||||
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
|
||||
|
||||
document = ifcopenshell.api.document.add_information(ifc)
|
||||
@@ -326,7 +326,7 @@ class TestSaveLinkedModelsToIfc(NewFile):
|
||||
|
||||
def test_save_linked_models_to_ifc_references_to_remove(self):
|
||||
ifc = ifcopenshell.file()
|
||||
links = bpy.context.scene.BIMProjectProperties.links
|
||||
links = tool.Project.get_project_props().links
|
||||
ifcopenshell.api.root.create_entity(ifc, "IfcProject")
|
||||
|
||||
document = ifcopenshell.api.document.add_information(ifc)
|
||||
|
||||
@@ -120,8 +120,8 @@ class TestGetObjectRepresentation(NewFile):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
representation = ifc.createIfcShapeRepresentation()
|
||||
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = representation.id()
|
||||
obj = bpy.data.objects.new("Object", (mesh := bpy.data.meshes.new("Mesh")))
|
||||
tool.Geometry.get_mesh_props(mesh).ifc_definition_id = representation.id()
|
||||
assert subject.get_object_representation(obj) == representation
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ class TestSetObjectName(NewFile):
|
||||
|
||||
class TestReassignClass(NewFile):
|
||||
def test_reassigning_multiple_occurrences_of_the_same_type(self):
|
||||
bpy.context.scene.BIMProjectProperties.template_file = "IFC4 Demo Template.ifc"
|
||||
tool.Project.get_project_props().template_file = "IFC4 Demo Template.ifc"
|
||||
bpy.ops.bim.create_project()
|
||||
ifc_file = tool.Ifc.get()
|
||||
context = bpy.context
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.geolocation
|
||||
|
||||
import test.bim.bootstrap
|
||||
import bonsai.core.tool
|
||||
@@ -34,7 +35,7 @@ class TestImplementsTool(test.bim.bootstrap.NewFile):
|
||||
|
||||
class TestGetGlobalMatrix(test.bim.bootstrap.NewFile):
|
||||
def test_getting_an_absolute_matrix_if_no_blender_offset(self):
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = False
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
assert (subject.get_absolute_matrix(obj) == np.array(obj.matrix_world)).all()
|
||||
@@ -45,7 +46,7 @@ class TestGetGlobalMatrix(test.bim.bootstrap.NewFile):
|
||||
unit = ifcopenshell.api.run("unit.add_si_unit", ifc, unit_type="LENGTHUNIT", prefix="MILLI")
|
||||
ifcopenshell.api.run("unit.assign_unit", ifc, units=[unit])
|
||||
tool.Ifc.set(ifc)
|
||||
props = bpy.context.scene.BIMGeoreferenceProperties
|
||||
props = tool.Georeference.get_georeference_props()
|
||||
props.has_blender_offset = True
|
||||
props.blender_offset_x = "1000"
|
||||
props.blender_offset_y = "2000"
|
||||
|
||||
@@ -30,11 +30,8 @@ def copy_cost_item_values(
|
||||
parametrically linked, so if one value changes, the other will not.
|
||||
|
||||
:param source: The IfcCostItem to copy cost values from
|
||||
:type source: ifcopenshell.entity_instance
|
||||
:param destination: The IfcCostItem to copy cost values from
|
||||
:type destination: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -53,11 +50,9 @@ def copy_cost_item_values(
|
||||
# Let's copy the value from one item to another
|
||||
ifcopenshell.api.cost.copy_cost_item_values(model, source=item1, destination=item2)
|
||||
"""
|
||||
settings = {"source": source, "destination": destination}
|
||||
|
||||
for cost_value in settings["destination"].CostValues or []:
|
||||
for cost_value in destination.CostValues or []:
|
||||
ifcopenshell.api.cost.remove_cost_item_value(file, cost_value=cost_value)
|
||||
copied_cost_values = []
|
||||
for cost_value in settings["source"].CostValues or []:
|
||||
for cost_value in source.CostValues or []:
|
||||
copied_cost_values.append(ifcopenshell.util.element.copy_deep(file, cost_value))
|
||||
settings["destination"].CostValues = copied_cost_values
|
||||
destination.CostValues = copied_cost_values
|
||||
|
||||
@@ -62,15 +62,10 @@ def assign_layer(
|
||||
# only one item) to the layer.
|
||||
ifcopenshell.api.layer.assign_layer(model, items=[representation.Items[0]], layer=layer)
|
||||
"""
|
||||
settings = {
|
||||
"items": items,
|
||||
"layer": layer,
|
||||
}
|
||||
|
||||
# support AssignedItems == None since layer might just got created
|
||||
layer = settings["layer"]
|
||||
assigned_items: set[ifcopenshell.entity_instance]
|
||||
assigned_items = set(layer.AssignedItems or [])
|
||||
items = set(settings["items"])
|
||||
if items.issubset(assigned_items):
|
||||
items_set = set(items)
|
||||
if items_set.issubset(assigned_items):
|
||||
return
|
||||
layer.AssignedItems = list(assigned_items | items)
|
||||
layer.AssignedItems = list(assigned_items | items_set)
|
||||
|
||||
@@ -91,8 +91,7 @@ def edit_profile_usage(
|
||||
usecase = Usecase()
|
||||
|
||||
usecase.file = file
|
||||
usecase.settings = {"usage": usage, "attributes": attributes}
|
||||
return usecase.execute()
|
||||
return usecase.execute(usage, attributes)
|
||||
|
||||
|
||||
class Usecase:
|
||||
|
||||
@@ -30,17 +30,15 @@ def get_application(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instanc
|
||||
IfcApplication. See ifcopenshell.api.owner.create_owner_history for details.
|
||||
|
||||
:param ifc: The IFC file object that is being edited.
|
||||
:type ifc: ifcopenshell.file
|
||||
:return: The IfcApplication with metadata of the authoring software.
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
"""
|
||||
app = ifc.by_type("IfcApplication")
|
||||
app = next(iter(ifc.by_type("IfcApplication")), None)
|
||||
if not app and ifc.schema == "IFC2X3":
|
||||
raise Exception(
|
||||
"Please create an application to continue. See the owner.create_owner_history docs for more info."
|
||||
"https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
||||
)
|
||||
return (app or [None])[0]
|
||||
return app
|
||||
|
||||
|
||||
def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]:
|
||||
@@ -50,17 +48,15 @@ def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None
|
||||
IfcApplication. See ifcopenshell.api.owner.create_owner_history for details.
|
||||
|
||||
:param ifc: The IFC file object that is being edited.
|
||||
:type ifc: ifcopenshell.file
|
||||
:return: The IfcPersonAndOrganization with metadata of the authoring user.
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
"""
|
||||
pao = ifc.by_type("IfcPersonAndOrganization")
|
||||
pao = next(iter(ifc.by_type("IfcPersonAndOrganization")), None)
|
||||
if not pao and ifc.schema == "IFC2X3":
|
||||
raise Exception(
|
||||
"Please create a user to continue. See the owner.create_owner_history docs for more info."
|
||||
"https://docs.ifcopenshell.org/autoapi/ifcopenshell/api/owner/create_owner_history/index.html"
|
||||
)
|
||||
return (pao or [None])[0]
|
||||
return pao
|
||||
|
||||
|
||||
get_application_factory = get_application
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
import datetime
|
||||
import ifcopenshell.util.date
|
||||
import ifcopenshell.util.sequence
|
||||
from ifcopenshell.util.sequence import DURATION_TYPE
|
||||
from typing import Union, Optional
|
||||
|
||||
|
||||
def cascade_schedule(file: ifcopenshell.file, task: ifcopenshell.entity_instance) -> None:
|
||||
@@ -41,9 +43,7 @@ def cascade_schedule(file: ifcopenshell.file, task: ifcopenshell.entity_instance
|
||||
be equivalent to be Tuesday 8am, for instance.
|
||||
|
||||
:param task: The start task to begin cascading from.
|
||||
:type task: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -103,16 +103,22 @@ def cascade_schedule(file: ifcopenshell.file, task: ifcopenshell.entity_instance
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
usecase.settings = {"task": task}
|
||||
return usecase.execute()
|
||||
return usecase.execute(task)
|
||||
|
||||
|
||||
class Usecase:
|
||||
def execute(self):
|
||||
self.calendar_cache = {}
|
||||
self.cascade_task(self.settings["task"], is_first_task=True)
|
||||
file: ifcopenshell.file
|
||||
|
||||
def cascade_task(self, task, is_first_task=False, task_sequence=None):
|
||||
def execute(self, task: ifcopenshell.entity_instance):
|
||||
self.calendar_cache = {}
|
||||
self.cascade_task(task, is_first_task=True)
|
||||
|
||||
def cascade_task(
|
||||
self,
|
||||
task: ifcopenshell.entity_instance,
|
||||
is_first_task: bool = False,
|
||||
task_sequence: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||
) -> None:
|
||||
if task_sequence is None:
|
||||
task_sequence = []
|
||||
|
||||
@@ -316,18 +322,22 @@ class Usecase:
|
||||
for nested_task in rel.RelatedObjects or []
|
||||
]
|
||||
|
||||
def get_lag_time_days(self, lag_time):
|
||||
def get_lag_time_days(self, lag_time: ifcopenshell.entity_instance) -> int:
|
||||
return ifcopenshell.util.date.ifc2datetime(lag_time.LagValue.wrappedValue).days
|
||||
|
||||
def get_calendar(self, task):
|
||||
def get_calendar(self, task: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
if task.id() not in self.calendar_cache:
|
||||
self.calendar_cache[task.id()] = ifcopenshell.util.sequence.derive_calendar(task)
|
||||
return self.calendar_cache[task.id()]
|
||||
|
||||
def offset_date(self, date, days, duration_type, calendar):
|
||||
def offset_date(
|
||||
self, date: datetime.datetime, days: int, duration_type: DURATION_TYPE, calendar: ifcopenshell.entity_instance
|
||||
) -> datetime.datetime:
|
||||
return ifcopenshell.util.sequence.offset_date(date, datetime.timedelta(days=days), duration_type, calendar)
|
||||
|
||||
def get_task_time_attribute(self, task, attribute):
|
||||
def get_task_time_attribute(
|
||||
self, task: ifcopenshell.entity_instance, attribute: str
|
||||
) -> Union[datetime.datetime, None]:
|
||||
if task.TaskTime:
|
||||
value = getattr(task.TaskTime, attribute)
|
||||
if value:
|
||||
|
||||
@@ -75,7 +75,9 @@ class Usecase:
|
||||
baseline_work_schedule.Name = name
|
||||
self.create_baseline_reference(work_schedule, baseline_work_schedule)
|
||||
for summary_task in ifcopenshell.util.sequence.get_root_tasks(work_schedule):
|
||||
current, duplicate = ifcopenshell.api.sequence.duplicate_task(self.file, task=summary_task)
|
||||
res = ifcopenshell.api.sequence.duplicate_task(self.file, task=summary_task)
|
||||
assert isinstance(res, list)
|
||||
current, duplicate = res
|
||||
ifcopenshell.api.control.assign_control(
|
||||
self.file, relating_control=baseline_work_schedule, related_object=duplicate[0]
|
||||
)
|
||||
|
||||
+10
-19
@@ -16,42 +16,33 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
import ifcopenshell
|
||||
from ifcopenshell.util.shape_builder import VectorType, ifc_safe_vector_type
|
||||
|
||||
|
||||
def edit_structural_connection_cs(
|
||||
file: ifcopenshell.file,
|
||||
structural_item: ifcopenshell.entity_instance,
|
||||
axis: tuple[float, float, float] = (0.0, 0.0, 1.0),
|
||||
ref_direction: tuple[float, float, float] = (1.0, 0.0, 0.0),
|
||||
axis: VectorType = (0.0, 0.0, 1.0),
|
||||
ref_direction: VectorType = (1.0, 0.0, 0.0),
|
||||
) -> None:
|
||||
"""Edits the coordinate system of a structural connection
|
||||
|
||||
:param structural_item: The IfcStructuralItem you want to modify.
|
||||
:type structural_item: ifcopenshell.entity_instance
|
||||
:param axis: The unit Z axis vector defined as a list of 3 floats.
|
||||
Defaults to (0., 0., 1.).
|
||||
:type axis: tuple[float, float, float]
|
||||
:param ref_direction: The unit X axis vector defined as a list of 3
|
||||
floats. Defaults to (1., 0., 0.).
|
||||
:type ref_direction: tuple[float, float, float]
|
||||
:return: None
|
||||
:rtype: None
|
||||
"""
|
||||
settings = {
|
||||
"structural_item": structural_item,
|
||||
"axis": axis,
|
||||
"ref_direction": ref_direction,
|
||||
}
|
||||
|
||||
if settings["structural_item"].ConditionCoordinateSystem is None:
|
||||
if structural_item.ConditionCoordinateSystem is None:
|
||||
point = file.createIfcCartesianPoint((0.0, 0.0, 0.0))
|
||||
ccs = file.createIfcAxis2Placement3D(point, None, None)
|
||||
settings["structural_item"].ConditionCoordinateSystem = ccs
|
||||
structural_item.ConditionCoordinateSystem = ccs
|
||||
|
||||
ccs = settings["structural_item"].ConditionCoordinateSystem
|
||||
ccs = structural_item.ConditionCoordinateSystem
|
||||
if ccs.Axis and len(file.get_inverse(ccs.Axis)) == 1:
|
||||
file.remove(ccs.Axis)
|
||||
ccs.Axis = file.createIfcDirection(settings["axis"])
|
||||
if ccs.RefDirection and len(file.get_inverse(ccs.RefDirection)) == 1:
|
||||
file.remove(ccs.RefDirection)
|
||||
ccs.RefDirection = file.createIfcDirection(settings["ref_direction"])
|
||||
ccs.Axis = file.create_entity("IfcDirection", ifc_safe_vector_type(axis))
|
||||
if (prev_ref_direction := ccs.RefDirection) and len(file.get_inverse(prev_ref_direction)) == 1:
|
||||
file.remove(prev_ref_direction)
|
||||
ccs.RefDirection = file.create_entity("IfcDirection", ifc_safe_vector_type(ref_direction))
|
||||
|
||||
@@ -84,7 +84,8 @@ class Patcher:
|
||||
import bonsai.tool as tool
|
||||
from math import degrees
|
||||
|
||||
bpy.context.scene.BIMProjectProperties.should_use_native_meshes = True
|
||||
props = tool.Project.get_project_props()
|
||||
props.should_use_native_meshes = True
|
||||
bpy.ops.bim.load_project(filepath=self.filepath)
|
||||
|
||||
old_history_size = tool.Ifc.get().history_size
|
||||
|
||||
Reference in New Issue
Block a user