import bonsai.tool as tool import ifcopenshell import ifcopenshell.api import bpy import numpy as np # 📌 STEP 1: Load the IFC File in Bonsai ifc_file = tool.Ifc.get() # 📌 STEP 2: Load the Blender (.blend) File & Append the Specific Wall blend_filepath = "G:\\My Drive\\Buildtrade\\Kiri\\LR8_singlewall.blend" wall_name = "wall_440F0EA1_7ED7_449F_8A79_91E000A708B9" # Append the object from the .blend file bpy.ops.wm.append( filepath=blend_filepath, directory=f"{blend_filepath}\\Object\\", filename=wall_name ) # Get the wall object from Blender original_wall = bpy.data.objects.get(wall_name) if not original_wall: raise ValueError(f"Wall {wall_name} not found in Blender.") # 📌 STEP 3: Extract Wall Geometry if not original_wall.data or not hasattr(original_wall.data, "vertices"): raise ValueError(f"Wall {wall_name} has no mesh data.") # Convert Blender coordinates to world coordinates vertices = np.array([original_wall.matrix_world @ v.co for v in original_wall.data.vertices]) # Compute wall elevation and height min_z = np.min(vertices[:, 2]) max_z = np.max(vertices[:, 2]) wall_height = float(max_z - min_z) new_base_elevation = max_z + 0.5 # Raise wall by 0.5m # Compute the 2D base path of the wall (filtering base-level vertices) base_path = [(v[0], v[1]) for v in vertices if np.isclose(v[2], min_z)] base_path = sorted(base_path, key=lambda v: v[0]) # Sort by X for consistency # 📌 STEP 4: Get IFC Context & Storey contexts = ifc_file.by_type("IfcGeometricRepresentationContext") if not contexts: raise ValueError("No IfcGeometricRepresentationContext found in IFC file.") context = contexts[0] # Use the first available context storeys = ifc_file.by_type("IfcBuildingStorey") if not storeys: raise ValueError("No IfcBuildingStorey found in the IFC file.") ground_storey = storeys[0] # 📌 STEP 5: Create a New Parametric IFC Wall new_wall = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcWall") new_wall.Name = f"Traced_{wall_name}" new_wall.Description = "Parametric IFC wall created from scan" # Assign wall to the storey ifcopenshell.api.run("spatial.assign_container", ifc_file, relating_structure=ground_storey, products=[new_wall]) # 📌 STEP 6: Manually Create IfcPolyline from IfcCartesianPoints cartesian_points = [ifc_file.create_entity("IfcCartesianPoint", Coordinates=tuple(float(val) for val in coord)) for coord in base_path] ifc_polyline = ifc_file.create_entity("IfcPolyline", Points=cartesian_points) # Create the profile definition (rectangle for wall thickness) profile = ifc_file.create_entity("IfcArbitraryClosedProfileDef", "AREA", None, ifc_polyline) # Create the extrusion direction direction = ifc_file.create_entity("IfcDirection", DirectionRatios=(0.0, 0.0, 1.0)) # Create the extrusion extrusion = ifc_file.create_entity("IfcExtrudedAreaSolid", profile, None, direction, wall_height) # Create the shape representation shape_representation = ifc_file.create_entity("IfcShapeRepresentation", context, "Body", "SweptSolid", [extrusion]) # Assign the representation to the wall ifcopenshell.api.run("geometry.assign_representation", ifc_file, product=new_wall, representation=shape_representation) # 📌 STEP 7: Add Door and Window Openings def create_opening_element(obj, ifc_file, context, wall): vertices = np.array([obj.matrix_world @ v.co for v in obj.data.vertices]) cartesian_points = [ifc_file.create_entity("IfcCartesianPoint", Coordinates=tuple(float(val) for val in v[:3])) for v in vertices] ifc_polyline = ifc_file.create_entity("IfcPolyline", Points=cartesian_points) profile = ifc_file.create_entity("IfcArbitraryClosedProfileDef", "AREA", None, ifc_polyline) direction = ifc_file.create_entity("IfcDirection", DirectionRatios=(0.0, 0.0, 1.0)) extrusion = ifc_file.create_entity("IfcExtrudedAreaSolid", profile, None, direction, wall_height) shape_representation = ifc_file.create_entity("IfcShapeRepresentation", context, "Body", "SweptSolid", [extrusion]) opening_element = ifcopenshell.api.run("root.create_entity", ifc_file, ifc_class="IfcOpeningElement") ifcopenshell.api.run("geometry.assign_representation", ifc_file, product=opening_element, representation=shape_representation) ifcopenshell.api.run("spatial.assign_container", ifc_file, relating_structure=wall, products=[opening_element]) return opening_element for obj in bpy.data.objects: if obj.type == 'MESH' and obj.parent == original_wall: if "door" in obj.name.lower(): door_opening = create_opening_element(obj, ifc_file, context, new_wall) if "window" in obj.name.lower(): window_opening = create_opening_element(obj, ifc_file, context, new_wall) # 📌 STEP 8: Save the Modified IFC File ifc_filepath = "G:\\My Drive\\Buildtrade\\Kiri\\LR8C_mod.ifc" ifc_file.write(ifc_filepath) print(f"✅ IFC file saved to: {ifc_filepath}") print(f"✅ New IFC wall created from scanned model: {new_wall.Name}")