From 82ac895bbbac3c64d2f33f6a23a6be6d19fa0545 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 4 Jan 2023 16:09:46 +1100 Subject: [PATCH] Write docs for void API module --- .../ifcopenshell/api/void/add_filling.py | 82 ++++++++++++++++++- .../ifcopenshell/api/void/add_opening.py | 80 +++++++++++++++++- .../ifcopenshell/api/void/remove_filling.py | 35 +++++++- .../ifcopenshell/api/void/remove_opening.py | 27 +++++- 4 files changed, 208 insertions(+), 16 deletions(-) diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py index f172505f10..712a172d2a 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_filling.py @@ -20,11 +20,85 @@ import ifcopenshell class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, opening=None, element=None): + """Fill an opening with an element + + Physical elements may have openings in them. For example, a wall might + have an opening for a door. That opening is then filled by the door. + This indicates that when the door moves, the opening will move with it. + Or if the door is removed, then the opening may remain and need to be + filled. + + :param opening: The IfcOpeningElement to fill with the element. + :type opening: ifcopenshell.entity_instance.entity_instance + :param element: The IfcElement to be inserted into the opening. + :type element: ifcopenshell.entity_instance.entity_instance + :return: The new IfcRelFillsElement relationship + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our wall and opening. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + + # Let's create an opening representation of a 950mm x 2100mm door. + # Notice how the thickness is greater than the wall thickness, this + # helps resolve floating point resolution errors in 3D. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.4) + ifcopenshell.api.run("geometry.assign_representation", model, + product=opening, representation=representation) + + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, to create a nice overlap for the opening boolean. + matrix = np.identity(4) + matrix[:,3] = [1, -.1, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) + + # The opening will now void the wall. + ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) + + # Create a door + door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") + + # Let's create a door representation of a 950mm x 2100mm door. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.05) + ifcopenshell.api.run("geometry.assign_representation", model, + product=door, representation=representation) + + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, which lines up with our opening. + matrix = np.identity(4) + matrix[:,3] = [1, .05, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=door, matrix=matrix) + + # The door will now fill the opening. + ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) + """ self.file = file - self.settings = {"opening": None, "element": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"opening": opening, "element": element} def execute(self): fills_voids = self.settings["element"].FillsVoids diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py index 09e8e6e313..350b50b13f 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/add_opening.py @@ -22,11 +22,83 @@ import ifcopenshell.util.placement class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, opening=None, element=None): + """Create an opening in an element + + It is often necessary to cut out openings in elements like walls and + slabs to make space to insert doors, windows, and other services that go + through these penetrations. + + Whereas it is possible to simply draw the wall as a rectangle with a + hole in it for the opening, often these openings have specific meanings. + For example, an opening might be filled with a window, and so when the + window moves, the opening should move with it. Alternatively, the + opening itself might have fire or acoustic requirements, such that any + service or equipment passing through that space must also comply with + those requirements. For these types of semantic openings, you should + have a distinct opening element which voids your regular element. For + example, your wall will still be a rectangular prism with no hole in it, + and a separate opening element will have a box representing the extents + of the opening for a window. The opening element will automatically + perform a geometric boolean operation to cut out the wall's geometry. + + Whenever you have an opening in you project, you should determine + whether or not the opening is semantic (i.e. should be represented by a + distinct opening object) or non-semantic (i.e. should simply be + booleaned or be part of the shape of the object). + + :param opening: The IfcOpeningElement to cut out the element. + :type opening: ifcopenshell.entity_instance.entity_instance + :param element: The IfcElement to insert the opening into. + :type element: ifcopenshell.entity_instance.entity_instance + :return: The new IfcRelVoidsElement relationship + :rtype: ifcopenshell.entity_instance.entity_instance + + Example:: + + # A bit of preparation, let's create some geometric contexts since + # we want to create some geometry for our wall and opening. + model3d = ifcopenshell.api.run("context.add_context", model, context_type="Model") + body = ifcopenshell.api.run("context.add_context", model, + context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d) + + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Let's use the "3D Body" representation we created earlier to add a + # new wall-like body geometry, 5 meters long, 3 meters high, and + # 200mm thick + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=5, height=3, thickness=0.2) + ifcopenshell.api.run("geometry.assign_representation", model, + product=wall, representation=representation) + + # Place our wall at the origin + ifcopenshell.api.run("geometry.edit_object_placement", model, product=wall) + + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + + # Let's create an opening representation of a 950mm x 2100mm door. + # Notice how the thickness is greater than the wall thickness, this + # helps resolve floating point resolution errors in 3D. + representation = ifcopenshell.api.run("geometry.add_wall_representation", model, + context=body, length=.95, height=2.1, thickness=0.4) + ifcopenshell.api.run("geometry.assign_representation", model, + product=opening, representation=representation) + + # Let's shift our door 1 meter along the wall and 100mm along the + # wall, to create a nice overlap for the opening boolean. + matrix = np.identity(4) + matrix[:,3] = [1, -.1, 0, 0] + ifcopenshell.api.run("geometry.edit_object_placement", model, product=opening, matrix=matrix) + + # The opening will now void the wall. + ifcopenshell.api.run("void.add_opening", model, opening=opening, element=wall) + """ self.file = file - self.settings = {"opening": None, "element": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"opening": opening, "element": element} def execute(self): voids_elements = self.settings["opening"].VoidsElements diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py index 629b50469e..f79f2d05fd 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_filling.py @@ -18,11 +18,38 @@ class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, element=None): + """Remove a filling relationship + + If an element is filling an opening, this removes the relationship such + that the opening and element both still exist, but the element no longer + fills the opening. + + :param element: The element filling an opening. + :type element: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # Create a wall + wall = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcWall") + + # Create an opening, such as for a service penetration with fire and + # acoustic requirements. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + + # Create a door + door = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcDoor") + + # The door will now fill the opening. + ifcopenshell.api.run("void.add_filling", model, opening=opening, element=door) + + # Not anymore! + ifcopenshell.api.run("void.remove_filling", model, element=door) + """ self.file = file - self.settings = {"element": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"element": element} def execute(self): for rel in self.file.by_type("IfcRelFillsElement"): diff --git a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py index f7fdc6530c..5100c73ba8 100644 --- a/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py +++ b/src/ifcopenshell-python/ifcopenshell/api/void/remove_opening.py @@ -20,11 +20,30 @@ import ifcopenshell.api class Usecase: - def __init__(self, file, **settings): + def __init__(self, file, opening=None): + """Remove an opening + + Fillings are retained as orphans. Voided elements remain. Openings + cannot exist by themselves, so not only is the opening relationship + removed, the opening is also removed. + + :param opening: The IfcOpeningElement to remove. + :type opening: ifcopenshell.entity_instance.entity_instance + :return: None + :rtype: None + + Example:: + + # Create an oprhaned opening. Note that an orphaned opening is + # invalid, as an opening can only exist when voiding another + # element. + opening = ifcopenshell.api.run("root.create_entity", model, ifc_class="IfcOpeningElement") + + # Remove it. This brings us back to a valid model. + ifcopenshell.api.run("void.remove_opening", model, opening=opening) + """ self.file = file - self.settings = {"opening": None} - for key, value in settings.items(): - self.settings[key] = value + self.settings = {"opening": opening} def execute(self): for rel in self.settings["opening"].VoidsElements: