def associate_inner_boundaries(fc_boundaries, doc): """Associate parent boundary and inner boundaries""" to_delete = [] for fc_boundary in fc_boundaries: if not fc_boundary.IsHosted or fc_boundary.ParentBoundary: continue host_boundaries = [] for host_element in fc_boundary.RelatedBuildingElement.HostElements: host_boundaries.extend(host_element.ProvidesBoundaries) candidates = set(fc_boundaries).intersection(host_boundaries) try: host = get_host(fc_boundary, candidates) except InvalidBoundary as err: logger.exception(err) to_delete.append(fc_boundary) continue fc_boundary.ParentBoundary = host if host: utils.append(host, "InnerBoundaries", fc_boundary) # Remove invalid boundary and corresponding inner wire updated_boundaries = fc_boundaries[:] for boundary in to_delete: remove_invalid_inner_wire(boundary, updated_boundaries) updated_boundaries.remove(boundary) doc.removeObject(boundary.Name)
def merge_boundaries(boundary1, boundary2) -> bool: """Try to merge 2 boundaries. Retrun True if successfully merged""" wire1 = utils.get_outer_wire(boundary1) wire2 = utils.get_outer_wire(boundary2) new_wire, extra_inner_wires = merged_wires(wire1, wire2) if not new_wire: return False # Update shape if boundary1.IsHosted: utils.remove_inner_wire(boundary1.ParentBoundary, wire1) utils.remove_inner_wire(boundary2.ParentBoundary, wire2) utils.append_inner_wire(boundary1.ParentBoundary, new_wire) else: for inner_boundary in boundary2.InnerBoundaries: utils.append(boundary1, "InnerBoundaries", inner_boundary) inner_boundary.ParentBoundary = boundary1 inner_wires = utils.get_inner_wires(boundary1)[:] inner_wires.extend(utils.get_inner_wires(boundary2)) inner_wires.extend(extra_inner_wires) try: utils.generate_boundary_compound(boundary1, new_wire, inner_wires) except RuntimeError as error: logger.exception(error) return False RelSpaceBoundary.recompute_areas(boundary1) return True
def handle_curtain_walls(space, doc) -> None: """Add an hosted window with full area in curtain wall boundaries as they are not handled by BEM softwares""" for boundary in space.SecondLevel.Group: if getattr(boundary.RelatedBuildingElement, "IfcType", "") != "IfcCurtainWall": continue # Prevent Revit issue which produce curtain wall with an hole inside but no inner boundary if not boundary.InnerBoundaries: if len(boundary.Shape.SubShapes) > 2: outer_wire = boundary.Shape.SubShapes[1] utils.generate_boundary_compound(boundary, outer_wire, ()) boundary.LesoType = "Wall" fake_window = doc.copyObject(boundary) fake_window.IsHosted = True fake_window.LesoType = "Window" fake_window.ParentBoundary = boundary fake_window.GlobalId = ifcopenshell.guid.new() fake_window.Id = IfcId.new(doc) RelSpaceBoundary.set_label(fake_window) space.SecondLevel.addObject(fake_window) # Host cannot be an empty face so inner wire is scaled down a little inner_wire = utils.get_outer_wire(boundary).scale(0.999) inner_wire = utils.project_wire_to_plane(inner_wire, utils.get_plane(boundary)) utils.append_inner_wire(boundary, inner_wire) utils.append(boundary, "InnerBoundaries", fake_window) if FreeCAD.GuiUp: fake_window.ViewObject.ShapeColor = (0.0, 0.7, 1.0)
def ensure_hosted_element_are(space, doc): for boundary in space.SecondLevel.Group: try: ifc_type = boundary.RelatedBuildingElement.IfcType except AttributeError: continue if not is_typically_hosted(ifc_type): continue if boundary.IsHosted and boundary.ParentBoundary: continue def valid_hosts(boundary): """Guess valid hosts""" for boundary2 in space.SecondLevel.Group: if boundary is boundary2 or is_typically_hosted( boundary2.IfcType): continue if not boundary2.Area.Value - boundary.Area.Value >= 0: continue if not utils.are_parallel_boundaries(boundary, boundary2): continue if utils.are_too_far(boundary, boundary2): continue yield boundary2 def find_host(boundary): fallback_solution = None for boundary2 in valid_hosts(boundary): fallback_solution = boundary2 for inner_wire in utils.get_inner_wires(boundary2): if (not abs( Part.Face(inner_wire).Area - boundary.Area.Value) < TOLERANCE): continue return boundary2 if not fallback_solution: raise HostNotFound( f"No host found for RelSpaceBoundary Id<{boundary.Id}>") logger.warning( f"Using fallback solution to resolve host of RelSpaceBoundary Id<{boundary.Id}>" ) return fallback_solution try: host = find_host(boundary) except HostNotFound as err: host = create_fake_host(boundary, space, doc) logger.exception(err) boundary.IsHosted = True boundary.ParentBoundary = host utils.append(host, "InnerBoundaries", boundary)
def create_element_type(self, fc_element, ifc_entity_type): if not ifc_entity_type: return try: fc_element_type = self.element_types[ifc_entity_type.id()] except KeyError: fc_element_type = ElementType.create_from_ifc( ifc_entity_type, self) self.element_types[fc_element_type.Id] = fc_element_type fc_element.IsTypedBy = fc_element_type utils.append(fc_element_type, "ApplicableOccurrence", fc_element)
def assign_material(self, material_select): if material_select.is_a("IfcMaterial"): self.obj.Material = self.create_single(material_select) elif material_select.is_a("IfcMaterialLayerSet"): self.obj.Material = self.create_layer_set(material_select) utils.append(self.obj.Material, "AssociatedTo", self.obj) elif material_select.is_a("IfcMaterialConstituentSet"): self.obj.Material = self.create_constituent_set(material_select) elif material_select.is_a("IfcMaterialProfileSet"): self.obj.Material = self.create_profile_set(material_select) else: raise NotImplementedError( f"{material_select.is_a()} not handled yet")
def associate_host_element(ifc_file, elements_group): # Associate Host / Hosted elements ifc_elements = (e for e in ifc_file.by_type("IfcElement") if e.ProvidesBoundaries) for ifc_entity in ifc_elements: if ifc_entity.FillsVoids: try: host = utils.get_element_by_guid( utils.get_host_guid(ifc_entity), elements_group) except LookupError as err: logger.exception(err) continue hosted = utils.get_element_by_guid(ifc_entity.GlobalId, elements_group) utils.append(host, "HostedElements", hosted) utils.append(hosted, "HostElements", host)
def associate_parent_and_corresponding(ifc_file, doc): try: for boundary in ifc_file.by_type("IfcRelSpaceBoundary2ndLevel"): if boundary.ParentBoundary: fc_boundary = utils.get_object(boundary, doc) fc_parent = utils.get_object(boundary.ParentBoundary, doc) fc_boundary.ParentBoundary = fc_parent utils.append(fc_parent, "InnerBoundaries", fc_boundary) if boundary.CorrespondingBoundary: fc_boundary = utils.get_object(boundary, doc) if fc_boundary.CorrespondingBoundary: continue fc_corresponding_boundary = utils.get_object( boundary.CorrespondingBoundary, doc) fc_boundary.CorrespondingBoundary = fc_corresponding_boundary fc_corresponding_boundary.CorrespondingBoundary = fc_boundary except RuntimeError: # When entity do not exist in the schema pass
def create_fake_host(boundary, space, doc): fake_host = doc.copyObject(boundary) fake_host.IsHosted = False fake_host.LesoType = "Wall" fake_host.GlobalId = ifcopenshell.guid.new() fake_host.Id = IfcId.new(doc) RelSpaceBoundary.set_label(fake_host) space.SecondLevel.addObject(fake_host) inner_wire = utils.get_outer_wire(boundary) outer_wire = inner_wire.scaled(1.001, inner_wire.CenterOfMass) plane = utils.get_plane(boundary) outer_wire = utils.project_wire_to_plane(outer_wire, plane) inner_wire = utils.project_wire_to_plane(inner_wire, plane) utils.generate_boundary_compound(fake_host, outer_wire, [inner_wire]) boundary.ParentBoundary = fake_host fake_building_element = doc.copyObject(boundary.RelatedBuildingElement) fake_building_element.Id = IfcId.new(doc) fake_host.RelatedBuildingElement = fake_building_element utils.append(fake_host, "InnerBoundaries", boundary) if FreeCAD.GuiUp: fake_host.ViewObject.ShapeColor = (0.7, 0.3, 0.0) return fake_host