import ifcopenshell import ifcopenshell.api.feature import ifcopenshell.geom import ifcopenshell.api import ifcopenshell.util.unit import numpy as np import propertygroups O = 0.0, 0.0, 0.0 X = 1.0, 0.0, 0.0 Y = 0.0, 1.0, 0.0 Z = 0.0, 0.0, 1.0 class Context: model = None body = None storey = None def __init__(self): self._create_empty_model() def clear(self): self._create_empty_model() def open(self, file_content): self.model = ifcopenshell.file.from_string(file_content) body = [ctx for ctx in self.model.by_type('IfcGeometricRepresentationContext') if ctx.ContextIdentifier == 'Body'] if body: self.body = body[0] else: context = ifcopenshell.api.run("context.add_context", self.model, context_type="Model") self.body = ifcopenshell.api.run( "context.add_context", self.model, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context, ) axis = [ctx for ctx in self.model.by_type('IfcGeometricRepresentationContext') if ctx.ContextIdentifier == 'Axis'] if body: self.axis = axis[0] else: context = ifcopenshell.api.run("context.add_context", self.model, context_type="Model") self.axis = ifcopenshell.api.run( "context.add_context", self.model, context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent=context, ) self.storey = self.model.by_type('IfcBuildingStorey')[0] def _create_empty_model(self): # Create a blank model self.model = ifcopenshell.file() # All projects must have one IFC Project element project = ifcopenshell.api.run( "root.create_entity", self.model, ifc_class="IfcProject", name="My Project" ) # Geometry is optional in IFC, but because we want to use geometry in this example, let's define units # Assigning without arguments defaults to metric units ifcopenshell.api.run("unit.assign_unit", self.model) # Let's create a modeling geometry context, so we can store 3D geometry (note: IFC supports 2D too!) context = ifcopenshell.api.run("context.add_context", self.model, context_type="Model") # In particular, in this example we want to store the 3D "body" geometry of objects, i.e. the body shape self.body = ifcopenshell.api.run( "context.add_context", self.model, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context, ) self.axis = ifcopenshell.api.run( "context.add_context", self.model, context_type="Model", context_identifier="Axis", target_view="GRAPH_VIEW", parent=context, ) # Create a site, building, and storey. Many hierarchies are possible. site = ifcopenshell.api.run( "root.create_entity", self.model, ifc_class="IfcSite", name="My Site" ) building = ifcopenshell.api.run( "root.create_entity", self.model, ifc_class="IfcBuilding", name="Building A" ) self.storey = ifcopenshell.api.run( "root.create_entity", self.model, ifc_class="IfcBuildingStorey", name="Ground Floor", ) # Since the site is our top level location, assign it to the project # Then place our building on the site, and our storey in the building ifcopenshell.api.run( "aggregate.assign_object", self.model, relating_object=project, products=[site], ) ifcopenshell.api.run( "aggregate.assign_object", self.model, relating_object=site, products=[building], ) ifcopenshell.api.run( "aggregate.assign_object", self.model, relating_object=building, products=[self.storey], ) def create_2pt_wall( self, p1, p2, elevation, height, thickness, container, wall_type=None ): p1 = np.array([p1[0], p1[1]]) p2 = np.array([p2[0], p2[1]]) wall = ifcopenshell.api.run("root.create_entity", self.model, ifc_class="IfcWall") length = float(np.linalg.norm(p2 - p1)) representation = ifcopenshell.api.run( "geometry.add_wall_representation", self.model, context=self.body, length=length, height=height, thickness=thickness, ) ifcopenshell.api.run( "geometry.assign_representation", self.model, product=wall, representation=representation, ) representation = ifcopenshell.api.run( "geometry.add_axis_representation", self.model, context=self.axis, axis=[(0.0, 0.0), (length, 0.0)], ) ifcopenshell.api.run( "geometry.assign_representation", self.model, product=wall, representation=representation, ) v = p2 - p1 v = np.divide(v, float(np.linalg.norm(v)), casting="unsafe") matrix = np.array( [ [v[0], -v[1], 0, p1[0]], [v[1], v[0], 0, p1[1]], [0, 0, 1, elevation], [0, 0, 0, 1], ] ) ifcopenshell.api.run("geometry.edit_object_placement", self.model, product=wall, matrix=matrix) ifcopenshell.api.run( "spatial.assign_container", self.model, relating_structure=container, products=[wall], ) if wall_type: ifcopenshell.api.run( "type.assign_type", self.model, related_object=wall, relating_type=wall_type, ) return wall def get_element(self, guid): return self.model[guid] def get_model(self): return self.model def create_fill(self, ty, pt, wall): if isinstance(wall, str): wall = self.model[wall] if not wall.is_a('IfcWall'): raise ValueError("Only 'wall' hosts are supported") if ty == 'door': props = propertygroups.BIMDoorProperties() elif ty == 'window': props = propertygroups.BIMWindowProperties() else: raise ValueError("Only 'door' or 'window' fills are supported") si_conversion = ifcopenshell.util.unit.calculate_unit_scale(self.model) body = ifcopenshell.util.representation.get_context( self.model, "Model", "Body", "MODEL_VIEW" ) representation_data = props.to_dict(si_conversion=si_conversion) representation_data["context"] = body door_representation = ifcopenshell.api.run( f"geometry.add_{ty}_representation", self.model, **representation_data ) door = ifcopenshell.api.run( "root.create_entity", self.model, ifc_class=f"ifc{ty}" ) door.OverallWidth = props.overall_width / si_conversion door.OverallHeight = props.overall_height / si_conversion ifcopenshell.api.run( "geometry.assign_representation", self.model, product=door, representation=door_representation, ) ifcopenshell.api.run( "spatial.assign_container", self.model, relating_structure=self.storey, products=[door], ) r = [ r for r in wall.Representation.Representations if r.RepresentationIdentifier == "Axis" ] if not r: raise ValueError("Axis representation is needed") r = r[0] axis_geometry = ifcopenshell.geom.create_shape( ifcopenshell.geom.settings( DIMENSIONALITY=ifcopenshell.ifcopenshell_wrapper.CURVES_SURFACES_AND_SOLIDS, USE_WORLD_COORDS=True, ), wall, r, ) vs = np.array(axis_geometry.geometry.verts).reshape((-1, 3)) es = np.array(axis_geometry.geometry.edges).reshape((-1, 2)) A, B = vs[es[0]] v = B - A P = np.zeros(3) P[0 : len(pt)] = pt AP = P - A AP_dot_v = np.dot(AP, v) v_dot_v = np.dot(v, v) t = AP_dot_v / v_dot_v * np.linalg.norm(v) / si_conversion opening = ifcopenshell.api.run( "root.create_entity", self.model, ifc_class="IfcOpeningElement", predefined_type="OPENING", name="Opening", ) position_3d = None if self.model.schema == "IFC2X3": position_3d = self.model.createIfcAxis2Placement2D( self.model.createIfcCartesianPoint([0.0, 0.0, 0.0]) ) position_2d = self.model.createIfcAxis2Placement2D( self.model.createIfcCartesianPoint([door.OverallWidth / 2.0, 0.0]) ) opening.Representation = self.model.createIfcProductDefinitionShape( Representations=[ self.model.createIfcShapeRepresentation( body, "Body", "SweptSolid", Items=[ self.model.createIfcExtrudedAreaSolid( self.model.createIfcRectangleProfileDef( "AREA", None, position_2d, door.OverallWidth, 1.2 / si_conversion, ), position_3d, self.model.createIfcDirection((0.0, 0.0, 1.0)), door.OverallHeight, ) ], ) ] ) ifcopenshell.api.feature.add_feature(self.model, feature=opening, element=wall) ifcopenshell.api.feature.add_filling(self.model, opening=opening, element=door) z_offsets = { 'door': 0, 'window': 1 } opening.ObjectPlacement = self.model.createIfcLocalPlacement( wall.ObjectPlacement, self.model.createIfcAxis2Placement3D( self.model.createIfcCartesianPoint((float(t), 0.0, z_offsets[ty] / si_conversion)) ), ) door.ObjectPlacement = self.model.createIfcLocalPlacement( opening.ObjectPlacement, self.model.createIfcAxis2Placement3D( self.model.createIfcCartesianPoint((0.0, 0.0, 0.0)) ), ) return door def to_obj_file(self, fn): st = ifcopenshell.geom.settings(USE_WORLD_COORDS=True, WELD_VERTICES=False) it = ifcopenshell.geom.iterator(st, self.model, exclude=("IfcOpeningElement",)) sr = ifcopenshell.geom.serializers.obj( fn, fn + ".mtl", st, ifcopenshell.geom.serializer_settings() ) if it.initialize(): for el in ifcopenshell.geom.consume_iterator(it): sr.write(el) sr.finalize() if __name__ == "__main__": m = Context() w1 = m.create_2pt_wall((0.0, 0.0), (4.0, 0.0), 0.0, 3.0, 0.1, m.storey) w2 = m.create_2pt_wall((1.0, 3.0), (1.0, 0.0), 0.0, 3.0, 0.1, m.storey) m.create_fill('door', [2, 0.0], w1) m.create_fill('window', [1, 1.5], w2) m.model.write("out.ifc") m.to_obj_file("out.obj")