First Commit
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Create relationships between features (e.g. openings) and physical elements
|
||||
|
||||
A feature is a special element (created using
|
||||
:func:`ifcopenshell.api.root.create_entity`) that may then be used to create
|
||||
geometric changes in other elements (such as walls and slabs). Most commonly, a
|
||||
feature would be an opening void. These voids may then be filled with doors,
|
||||
trapdoors, skylights, and so on.
|
||||
"""
|
||||
|
||||
from .. import wrap_usecases
|
||||
from .add_feature import add_feature
|
||||
from .add_filling import add_filling
|
||||
from .remove_feature import remove_feature
|
||||
from .remove_filling import remove_filling
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_feature",
|
||||
"add_filling",
|
||||
"remove_feature",
|
||||
"remove_filling",
|
||||
]
|
||||
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,149 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# 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
|
||||
import ifcopenshell.api.aggregate
|
||||
import ifcopenshell.api.geometry
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
|
||||
|
||||
def add_feature(
|
||||
file: ifcopenshell.file, feature: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Create a projecting, voiding, or surface feature in an element
|
||||
|
||||
There are three main types of features: those that add, remove, or
|
||||
influence geometry of a parent object.
|
||||
|
||||
The most common of these is an opening. For example, 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 feature: The IfcFeatureElement to affect the element.
|
||||
:param element: The IfcElement to add the feature to.
|
||||
:return: The new IfcRelVoidsElement relationship
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# 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.context.add_context(model, context_type="Model")
|
||||
body = ifcopenshell.api.context.add_context(model,
|
||||
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
|
||||
|
||||
# Create a wall
|
||||
wall = ifcopenshell.api.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.geometry.add_wall_representation(model,
|
||||
context=body, length=5, height=3, thickness=0.2)
|
||||
ifcopenshell.api.geometry.assign_representation(model,
|
||||
product=wall, representation=representation)
|
||||
|
||||
# Place our wall at the origin
|
||||
ifcopenshell.api.geometry.edit_object_placement(model, product=wall)
|
||||
|
||||
# Create an opening, such as for a service penetration with fire and
|
||||
# acoustic requirements.
|
||||
opening = ifcopenshell.api.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.geometry.add_wall_representation(model,
|
||||
context=body, length=.95, height=2.1, thickness=0.4)
|
||||
ifcopenshell.api.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.geometry.edit_object_placement(model, product=opening, matrix=matrix)
|
||||
|
||||
# The opening will now void the wall.
|
||||
ifcopenshell.api.feature.add_feature(model, feature=opening, element=wall)
|
||||
"""
|
||||
if feature.is_a("IfcFeatureElementSubtraction"):
|
||||
rels = feature.VoidsElements
|
||||
ifc_class = "IfcRelVoidsElement"
|
||||
elif feature.is_a("IfcFeatureElementAddition"):
|
||||
rels = feature.ProjectsElements
|
||||
ifc_class = "IfcRelProjectsElement"
|
||||
elif feature.is_a("IfcSurfaceFeature"):
|
||||
if file.schema == "IFC4":
|
||||
return ifcopenshell.api.aggregate.assign_object(file, [feature], element)
|
||||
rels = feature.AdheresToElement
|
||||
ifc_class = "IfcRelAdheresToElement"
|
||||
|
||||
if rels:
|
||||
if rels[0][4] == element:
|
||||
return rels[0]
|
||||
elif ifc_class == "IfcRelAdheresToElement" and len(rels[0].RelatedSurfaceFeatures) != 1:
|
||||
rels[0].RelatedSurfaceFeatures = list(set(rels[0].RelatedSurfaceFeatures) - {feature})
|
||||
else:
|
||||
history = rels[0].OwnerHistory
|
||||
file.remove(rels[0])
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
rel = file.create_entity(
|
||||
ifc_class,
|
||||
ifcopenshell.guid.new(),
|
||||
ifcopenshell.api.owner.create_owner_history(file),
|
||||
None,
|
||||
None,
|
||||
element,
|
||||
[feature] if ifc_class == "IfcRelAdheresToElement" else feature,
|
||||
)
|
||||
|
||||
if (placement := feature.ObjectPlacement) and placement.is_a("IfcLocalPlacement"):
|
||||
ifcopenshell.api.geometry.edit_object_placement(
|
||||
file,
|
||||
product=feature,
|
||||
matrix=ifcopenshell.util.placement.get_local_placement(placement),
|
||||
is_si=False,
|
||||
)
|
||||
|
||||
return rel
|
||||
@@ -0,0 +1,117 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# 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
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def add_filling(
|
||||
file: ifcopenshell.file, opening: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""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.
|
||||
:param element: The IfcElement to be inserted into the opening.
|
||||
:return: The new IfcRelFillsElement relationship
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# 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.context.add_context(model, context_type="Model")
|
||||
body = ifcopenshell.api.context.add_context(model,
|
||||
context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=model3d)
|
||||
|
||||
# Create a wall
|
||||
wall = ifcopenshell.api.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.geometry.add_wall_representation(model,
|
||||
context=body, length=5, height=3, thickness=0.2)
|
||||
ifcopenshell.api.geometry.assign_representation(model,
|
||||
product=wall, representation=representation)
|
||||
|
||||
# Place our wall at the origin
|
||||
ifcopenshell.api.geometry.edit_object_placement(model, product=wall)
|
||||
|
||||
# Create an opening, such as for a service penetration with fire and
|
||||
# acoustic requirements.
|
||||
opening = ifcopenshell.api.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.geometry.add_wall_representation(model,
|
||||
context=body, length=.95, height=2.1, thickness=0.4)
|
||||
ifcopenshell.api.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.geometry.edit_object_placement(model, product=opening, matrix=matrix)
|
||||
|
||||
# The opening will now void the wall.
|
||||
ifcopenshell.api.feature.add_feature(model, feature=opening, element=wall)
|
||||
|
||||
# Create a door
|
||||
door = ifcopenshell.api.root.create_entity(model, ifc_class="IfcDoor")
|
||||
|
||||
# Let's create a door representation of a 950mm x 2100mm door.
|
||||
representation = ifcopenshell.api.geometry.add_wall_representation(model,
|
||||
context=body, length=.95, height=2.1, thickness=0.05)
|
||||
ifcopenshell.api.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.geometry.edit_object_placement(model, product=door, matrix=matrix)
|
||||
|
||||
# The door will now fill the opening.
|
||||
ifcopenshell.api.feature.add_filling(model, opening=opening, element=door)
|
||||
"""
|
||||
fills_voids = element.FillsVoids
|
||||
|
||||
if fills_voids:
|
||||
if fills_voids[0].RelatingOpeningElement == opening:
|
||||
return fills_voids[0]
|
||||
history = fills_voids[0].OwnerHistory
|
||||
file.remove(fills_voids[0])
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
return file.create_entity(
|
||||
"IfcRelFillsElement",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
RelatingOpeningElement=opening,
|
||||
RelatedBuildingElement=element,
|
||||
)
|
||||
@@ -0,0 +1,68 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# 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.api.aggregate
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def remove_feature(file: ifcopenshell.file, feature: ifcopenshell.entity_instance) -> None:
|
||||
"""Permanently delete a feature element and its void or projection relationship.
|
||||
|
||||
The feature entity (e.g. IfcOpeningElement) is removed from the model
|
||||
along with its IfcRelVoidsElement or IfcRelProjectsElement relationship.
|
||||
The host element (wall, slab, etc.) is unaffected. Any fillings (windows,
|
||||
doors) that occupied the opening become orphaned and must be separately
|
||||
deleted via root.remove_product.
|
||||
|
||||
:param feature: The IfcFeatureElement to remove.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create an orphaned opening. Note that an orphaned opening is
|
||||
# invalid, as an opening can only exist when voiding another
|
||||
# element.
|
||||
feature = ifcopenshell.api.root.create_entity(model, ifc_class="IfcOpeningElement")
|
||||
|
||||
# Remove it. This brings us back to a valid model.
|
||||
ifcopenshell.api.feature.remove_feature(model, feature=feature)
|
||||
"""
|
||||
if feature.is_a("IfcFeatureElementSubtraction"):
|
||||
rels = feature.VoidsElements
|
||||
elif feature.is_a("IfcFeatureElementAddition"):
|
||||
rels = feature.ProjectsElements
|
||||
elif feature.is_a("IfcSurfaceFeature"):
|
||||
if file.schema == "IFC4":
|
||||
ifcopenshell.api.aggregate.unassign_object(file, products=[feature])
|
||||
rels = []
|
||||
else:
|
||||
rels = feature.ProjectsElements
|
||||
for rel in rels:
|
||||
history = rel.OwnerHistory
|
||||
file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
if feature.is_a("IfcOpeningElement"):
|
||||
for rel in feature.HasFillings:
|
||||
history = rel.OwnerHistory
|
||||
file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
ifcopenshell.api.root.remove_product(file, product=feature)
|
||||
@@ -0,0 +1,59 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# 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
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
def remove_filling(file: ifcopenshell.file, element: ifcopenshell.entity_instance) -> 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.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# Create a wall
|
||||
wall = ifcopenshell.api.root.create_entity(model, ifc_class="IfcWall")
|
||||
|
||||
# Create an opening, such as for a service penetration with fire and
|
||||
# acoustic requirements.
|
||||
opening = ifcopenshell.api.root.create_entity(model, ifc_class="IfcOpeningElement")
|
||||
|
||||
# Create a door
|
||||
door = ifcopenshell.api.root.create_entity(model, ifc_class="IfcDoor")
|
||||
|
||||
# The door will now fill the opening.
|
||||
ifcopenshell.api.feature.add_filling(model, opening=opening, element=door)
|
||||
|
||||
# Not anymore!
|
||||
ifcopenshell.api.feature.remove_filling(model, element=door)
|
||||
"""
|
||||
for rel in file.by_type("IfcRelFillsElement"):
|
||||
if rel.RelatedBuildingElement == element:
|
||||
history = rel.OwnerHistory
|
||||
file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
break
|
||||
Reference in New Issue
Block a user