Generate functions for all API usecases for better static code features. See #2693.

This commit is contained in:
Dion Moult
2024-05-06 14:35:39 +10:00
parent 10f894e2ea
commit d11ec67129
330 changed files with 13283 additions and 13751 deletions
@@ -15,3 +15,30 @@
#
# You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
from .add_axis_representation import add_axis_representation
from .add_boolean import add_boolean
from .add_door_representation import add_door_representation
from .add_footprint_representation import add_footprint_representation
from .add_mesh_representation import add_mesh_representation
from .add_profile_representation import add_profile_representation
from .add_railing_representation import add_railing_representation
try:
from .add_representation import add_representation
except ModuleNotFoundError as e:
print(f"Note: API not available due to missing dependencies: geometry.add_representation - {e}")
from .add_slab_representation import add_slab_representation
from .add_wall_representation import add_wall_representation
from .add_window_representation import add_window_representation
from .assign_representation import assign_representation
from .connect_element import connect_element
from .connect_path import connect_path
from .create_2pt_wall import create_2pt_wall
from .disconnect_element import disconnect_element
from .disconnect_path import disconnect_path
from .edit_object_placement import edit_object_placement
from .map_representation import map_representation
from .remove_boolean import remove_boolean
from .remove_representation import remove_representation
from .unassign_representation import unassign_representation
@@ -19,61 +19,64 @@
import ifcopenshell.util.unit
def add_axis_representation(file, context=None, axis=None) -> None:
"""Adds a new axis representation
Certain objects are typically "axis-based", such as walls, beams,
and columns. This means you can represent them abstractly by simply
drawing a single line either in 2D (such as for walls) or 3D (for beams
and columns). Humans can understand this axis-based representation as
being a simplification of a layered extrusion or a profile that is being
extruded along that axis and joined to other elements.
Using an axis-based representation makes it easy for users and computers
to analyse connectivity and spatial relationships, as well as makes it
easy to parametrically edit these objects by simply stretching the start
or end of the axis.
For now, only simple straight line axes are supported, represented by a
start and end coordinate. The order is important. For walls, the start
must be at the minimum local X ordinate, and the end at the maximum
local X ordinate. For beams and columns, the start is at the minimum
local Z ordinate, and the end of the maximum local Z ordinate. The first
coordinate is the "start" and the second coordinate is the "end". This
stat and end is then used to determine any parametric junctions with
other elements.
Using an axis-representation is optional, but highly recommended for
"standard" representations of walls, beams, columns, and other
structural members. A rule of thumb is that if you can draw it as a line
on paper, you can probably represent it using an axis.
:param context: The IfcGeometricRepresentationContext that the
representation is part of. This must be either a
Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D).
:type context: ifcopenshell.entity_instance
:param axis: The axis, as a list of two coordinates, the coordinates
being either a list of 2 or 3 float coordinates depending on whether
the axis is 2D or 3D.
:type axis: list[list[float]]
:return: The newly created IfcShapeRepresentation entity
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
context = ifcopenshell.util.representation.get_context(model, "Plan", "Axis", "GRAPH_VIEW")
axis = ifcopenshell.api.run("geometry.add_axis_representation", model,
context=context, axis=[(0.0, 0.0), (1.0, 0.0)])
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {
"context": context,
"axis": axis or [],
}
return usecase.execute()
class Usecase:
def __init__(self, file, context=None, axis=None):
"""Adds a new axis representation
Certain objects are typically "axis-based", such as walls, beams,
and columns. This means you can represent them abstractly by simply
drawing a single line either in 2D (such as for walls) or 3D (for beams
and columns). Humans can understand this axis-based representation as
being a simplification of a layered extrusion or a profile that is being
extruded along that axis and joined to other elements.
Using an axis-based representation makes it easy for users and computers
to analyse connectivity and spatial relationships, as well as makes it
easy to parametrically edit these objects by simply stretching the start
or end of the axis.
For now, only simple straight line axes are supported, represented by a
start and end coordinate. The order is important. For walls, the start
must be at the minimum local X ordinate, and the end at the maximum
local X ordinate. For beams and columns, the start is at the minimum
local Z ordinate, and the end of the maximum local Z ordinate. The first
coordinate is the "start" and the second coordinate is the "end". This
stat and end is then used to determine any parametric junctions with
other elements.
Using an axis-representation is optional, but highly recommended for
"standard" representations of walls, beams, columns, and other
structural members. A rule of thumb is that if you can draw it as a line
on paper, you can probably represent it using an axis.
:param context: The IfcGeometricRepresentationContext that the
representation is part of. This must be either a
Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D).
:type context: ifcopenshell.entity_instance
:param axis: The axis, as a list of two coordinates, the coordinates
being either a list of 2 or 3 float coordinates depending on whether
the axis is 2D or 3D.
:type axis: list[list[float]]
:return: The newly created IfcShapeRepresentation entity
:rtype: ifcopenshell.entity_instance
Example:
.. code:: python
context = ifcopenshell.util.representation.get_context(model, "Plan", "Axis", "GRAPH_VIEW")
axis = ifcopenshell.api.run("geometry.add_axis_representation", model,
context=context, axis=[(0.0, 0.0), (1.0, 0.0)])
"""
self.file = file
self.settings = {
"context": context,
"axis": axis or [],
}
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
is_2d = len(self.settings["axis"][0]) == 2
@@ -82,9 +85,13 @@ class Usecase:
curve = self.file.createIfcPolyline([self.file.createIfcCartesianPoint(p) for p in points])
else:
if is_2d:
curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList2D(points), None, False)
curve = self.file.createIfcIndexedPolyCurve(
self.file.createIfcCartesianPointList2D(points), None, False
)
else:
curve = self.file.createIfcIndexedPolyCurve(self.file.createIfcCartesianPointList3D(points), None, False)
curve = self.file.createIfcIndexedPolyCurve(
self.file.createIfcCartesianPointList3D(points), None, False
)
return self.file.createIfcShapeRepresentation(
self.settings["context"],
self.settings["context"].ContextIdentifier,
@@ -20,24 +20,27 @@ import ifcopenshell.util.unit
import numpy as np
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"representation": None,
"operator": "DIFFERENCE",
# IfcHalfSpaceSolid, Mesh
"type": "IfcHalfSpaceSolid",
# The XY plane is the clipping boundary and +Z is removed.
"matrix": None, # A matrix to define a clipping Ifchalfspacesolid.
"blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type
"blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type
"should_force_faceted_brep": False,
"should_force_triangulation": False,
}
for key, value in settings.items():
self.settings[key] = value
def add_boolean(file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {
"representation": None,
"operator": "DIFFERENCE",
# IfcHalfSpaceSolid, Mesh
"type": "IfcHalfSpaceSolid",
# The XY plane is the clipping boundary and +Z is removed.
"matrix": None, # A matrix to define a clipping Ifchalfspacesolid.
"blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type
"blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type
"should_force_faceted_brep": False,
"should_force_triangulation": False,
}
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
if self.settings["type"] == "IfcHalfSpaceSolid":
@@ -63,11 +63,7 @@ def create_ifc_door_lining(
points = [p.xz for p in points]
door_lining = builder.polyline(points, closed=True)
door_lining = builder.extrude(
door_lining,
size.y,
**builder.extrude_kwargs("Y")
)
door_lining = builder.extrude(door_lining, size.y, **builder.extrude_kwargs("Y"))
builder.translate(door_lining, position)
return door_lining
@@ -79,75 +75,78 @@ def create_ifc_box(builder: ShapeBuilder, size: Vector, position: Vector = V(0,
return box
class Usecase:
def __init__(self, file, **settings):
"""units in settings expected to be in ifc project units"""
self.file = file
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm
self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)}
self.settings.update(
{
"context": None, # IfcGeometricRepresentationContext
"overall_height": self.convert_si_to_unit(2.0),
"overall_width": self.convert_si_to_unit(0.9),
# DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL,
# DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT,
# DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING,
# DOUBLE_SWING_LEFT, DOUBLE_SWING_RIGHT, FOLDING_TO_LEFT,
# FOLDING_TO_RIGHT, LIFTING_HORIZONTAL, LIFTING_VERTICAL_LEFT,
# LIFTING_VERTICAL_RIGHT, REVOLVING, REVOLVING_VERTICAL,
# ROLLINGUP, SINGLE_SWING_LEFT, SINGLE_SWING_RIGHT, SLIDING_TO_LEFT,
# SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT
"operation_type": "SINGLE_SWING_LEFT", # door type
"lining_properties": {
"LiningDepth": self.convert_si_to_unit(0.050),
"LiningThickness": self.convert_si_to_unit(0.050),
# offset from the outer side of the wall (by Y-axis)
"LiningOffset": self.convert_si_to_unit(0.0),
# offset from the wall
"LiningToPanelOffsetX": self.convert_si_to_unit(0.025),
# offset from the X-axis (unlike windows)
"LiningToPanelOffsetY": self.convert_si_to_unit(0.025),
# transom - vertical distance between door and window panels
"TransomThickness": self.convert_si_to_unit(0.000),
# TransomOffset - distance from the bottom door opening
# to the beginning of the transom
# unlike windows TransomOffset which goes to the center of the transom
"TransomOffset": self.convert_si_to_unit(1.525),
"ShapeAspectStyle": None, # DEPRECATED
# Casing cover wall faces around the opening
# on the left, right and upper sides
# Casing should be either on both sides of the wall or no casing
# If `LiningOffset` is present then therefore casing is not possible on outer wall
# therefore there will be no casing on inner wall either
"CasingDepth": self.convert_si_to_unit(0.005),
"CasingThickness": self.convert_si_to_unit(0.075), # by Z-axis
# Threshold covers the bottom side of the opening
"ThresholdDepth": self.convert_si_to_unit(0.1),
"ThresholdThickness": self.convert_si_to_unit(0.025), # by Z-axis
# offset by Y-axis
"ThresholdOffset": self.convert_si_to_unit(0.000),
},
"panel_properties": {
"PanelDepth": self.convert_si_to_unit(0.035), # by Y
"PanelWidth": 1.0, # as ratio to the clear door opening
"FrameDepth": self.convert_si_to_unit(0.035), # by Y
"FrameThickness": self.convert_si_to_unit(0.035), # by X
# LEFT, MIDDLE, RIGHT, NOTDEFINED
"PanelPosition": ..., # NEVER USED
# defines the basic ways to describe how door panels operate
# basically how it opens
"PanelOperation": None, # NEVER USED
"ShapeAspectStyle": None, # DEPRECATED
},
}
)
for key, value in settings.items():
self.settings[key] = value
def add_door_representation(file, **usecase_settings) -> None:
"""units in usecase_settings expected to be in ifc project units"""
usecase = Usecase()
usecase.file = file
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoor.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorLiningProperties.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.htm
usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
usecase.settings.update(
{
"context": None, # IfcGeometricRepresentationContext
"overall_height": usecase.convert_si_to_unit(2.0),
"overall_width": usecase.convert_si_to_unit(0.9),
# DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL,
# DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT,
# DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING,
# DOUBLE_SWING_LEFT, DOUBLE_SWING_RIGHT, FOLDING_TO_LEFT,
# FOLDING_TO_RIGHT, LIFTING_HORIZONTAL, LIFTING_VERTICAL_LEFT,
# LIFTING_VERTICAL_RIGHT, REVOLVING, REVOLVING_VERTICAL,
# ROLLINGUP, SINGLE_SWING_LEFT, SINGLE_SWING_RIGHT, SLIDING_TO_LEFT,
# SLIDING_TO_RIGHT, SWING_FIXED_LEFT, SWING_FIXED_RIGHT
"operation_type": "SINGLE_SWING_LEFT", # door type
"lining_properties": {
"LiningDepth": usecase.convert_si_to_unit(0.050),
"LiningThickness": usecase.convert_si_to_unit(0.050),
# offset from the outer side of the wall (by Y-axis)
"LiningOffset": usecase.convert_si_to_unit(0.0),
# offset from the wall
"LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025),
# offset from the X-axis (unlike windows)
"LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025),
# transom - vertical distance between door and window panels
"TransomThickness": usecase.convert_si_to_unit(0.000),
# TransomOffset - distance from the bottom door opening
# to the beginning of the transom
# unlike windows TransomOffset which goes to the center of the transom
"TransomOffset": usecase.convert_si_to_unit(1.525),
"ShapeAspectStyle": None, # DEPRECATED
# Casing cover wall faces around the opening
# on the left, right and upper sides
# Casing should be either on both sides of the wall or no casing
# If `LiningOffset` is present then therefore casing is not possible on outer wall
# therefore there will be no casing on inner wall either
"CasingDepth": usecase.convert_si_to_unit(0.005),
"CasingThickness": usecase.convert_si_to_unit(0.075), # by Z-axis
# Threshold covers the bottom side of the opening
"ThresholdDepth": usecase.convert_si_to_unit(0.1),
"ThresholdThickness": usecase.convert_si_to_unit(0.025), # by Z-axis
# offset by Y-axis
"ThresholdOffset": usecase.convert_si_to_unit(0.000),
},
"panel_properties": {
"PanelDepth": usecase.convert_si_to_unit(0.035), # by Y
"PanelWidth": 1.0, # as ratio to the clear door opening
"FrameDepth": usecase.convert_si_to_unit(0.035), # by Y
"FrameThickness": usecase.convert_si_to_unit(0.035), # by X
# LEFT, MIDDLE, RIGHT, NOTDEFINED
"PanelPosition": ..., # NEVER USED
# defines the basic ways to describe how door panels operate
# basically how it opens
"PanelOperation": None, # NEVER USED
"ShapeAspectStyle": None, # DEPRECATED
},
}
)
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
builder = ShapeBuilder(self.file)
overall_height = self.settings["overall_height"]
@@ -19,20 +19,17 @@
import ifcopenshell.util.unit
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"context": None, # IfcGeometricRepresentationContext
"curves": [], # A list of IFC curves to include in the curve set
}
for key, value in settings.items():
self.settings[key] = value
def add_footprint_representation(file, **usecase_settings) -> None:
settings = {
"context": None, # IfcGeometricRepresentationContext
"curves": [], # A list of IFC curves to include in the curve set
}
for key, value in usecase_settings.items():
settings[key] = value
def execute(self):
return self.file.createIfcShapeRepresentation(
self.settings["context"],
self.settings["context"].ContextIdentifier,
"GeometricCurveSet",
[self.file.createIfcGeometricCurveSet(self.settings["curves"])],
)
return file.createIfcShapeRepresentation(
settings["context"],
settings["context"].ContextIdentifier,
"GeometricCurveSet",
[file.createIfcGeometricCurveSet(settings["curves"])],
)
@@ -19,25 +19,28 @@
import ifcopenshell.util.unit
class Usecase:
def __init__(self, file: ifcopenshell.file, **settings):
self.file = file
self.settings = {
"context": None, # IfcGeometricRepresentationContext
# Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...]
# ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...]
"vertices": None, # A list of coordinates
# ... where itemN = [(0, 1), (1, 2), (v1, v2), ...]
"edges": None, # A list of edges, represented by vertex index pairs
# ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...]
"faces": None, # A list of polygons, represented by vertex indices
"coordinate_offset": None, # Optionally apply a vector offset to all coordinates
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
"force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets
}
for key, value in settings.items():
self.settings[key] = value
def add_mesh_representation(file: ifcopenshell.file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {
"context": None, # IfcGeometricRepresentationContext
# Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...]
# ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...]
"vertices": None, # A list of coordinates
# ... where itemN = [(0, 1), (1, 2), (v1, v2), ...]
"edges": None, # A list of edges, represented by vertex index pairs
# ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...]
"faces": None, # A list of polygons, represented by vertex indices
"coordinate_offset": None, # Optionally apply a vector offset to all coordinates
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
"force_faceted_brep": False, # Force using IfcFacetedBreps instead of IfcPolygonalFaceSets
}
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
if self.settings["unit_scale"] is None:
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -21,22 +21,25 @@ import ifcopenshell.util.unit
from ifcopenshell.util.data import Clipping
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"context": None, # IfcGeometricRepresentationContext
"profile": None,
"depth": 1.0,
"cardinal_point": 5,
# Planes are defined either by Clipping objects
# or by dictionaries of arguments for `Clipping.parse`
"clippings": [], # A list of planes that define clipping half space solids
"placement_zx_axes": (None, None),
}
for key, value in settings.items():
self.settings[key] = value
def add_profile_representation(file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {
"context": None, # IfcGeometricRepresentationContext
"profile": None,
"depth": 1.0,
"cardinal_point": 5,
# Planes are defined either by Clipping objects
# or by dictionaries of arguments for `Clipping.parse`
"clippings": [], # A list of planes that define clipping half space solids
"placement_zx_axes": (None, None),
}
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]]
@@ -31,39 +31,42 @@ def mm(x):
return x / 1000
def add_railing_representation(file, **usecase_settings) -> None:
"""
units in usecase_settings expected to be in ifc project units
`railing_path` is a list of point coordinates for the railing path,
coordinates are expected to be at the top of the railing, not at the center
`railing_path` is expected to be a list of Vector objects
"""
usecase = Usecase()
usecase.file = file
usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
usecase.settings.update(
{
"context": None, # IfcGeometricRepresentationContext
"railing_type": "WALL_MOUNTED_HANDRAIL",
"railing_path": usecase.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]),
"use_manual_supports": False,
"support_spacing": usecase.convert_si_to_unit(mm(1000)),
"railing_diameter": usecase.convert_si_to_unit(mm(50)),
"clear_width": usecase.convert_si_to_unit(mm(40)),
"terminal_type": "180",
"height": usecase.convert_si_to_unit(mm(1000)),
"looped_path": False,
}
)
for key, value in usecase_settings.items():
usecase.settings[key] = value
if usecase.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL":
raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.')
return usecase.execute()
class Usecase:
def __init__(self, file, **settings):
"""
units in settings expected to be in ifc project units
`railing_path` is a list of point coordinates for the railing path,
coordinates are expected to be at the top of the railing, not at the center
`railing_path` is expected to be a list of Vector objects
"""
self.file = file
self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)}
self.settings.update(
{
"context": None, # IfcGeometricRepresentationContext
"railing_type": "WALL_MOUNTED_HANDRAIL",
"railing_path": self.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]),
"use_manual_supports": False,
"support_spacing": self.convert_si_to_unit(mm(1000)),
"railing_diameter": self.convert_si_to_unit(mm(50)),
"clear_width": self.convert_si_to_unit(mm(40)),
"terminal_type": "180",
"height": self.convert_si_to_unit(mm(1000)),
"looped_path": False,
}
)
for key, value in settings.items():
self.settings[key] = value
if self.settings["railing_type"] != "WALL_MOUNTED_HANDRAIL":
raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.')
def execute(self):
arc_points = []
items_3d = []
@@ -28,37 +28,40 @@ X_AXIS = Vector((1, 0, 0))
EPSILON = 1e-6
class Usecase:
def __init__(self, file: ifcopenshell.file, **settings):
# TODO: This usecase currently depends on Blender's data model
self.file = file
self.settings = {
"context": None, # IfcGeometricRepresentationContext
"blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now
"geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now
"coordinate_offset": None, # Optionally apply a vector offset to all coordinates
"total_items": 1, # How many representation items to create
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
"should_force_triangulation": False, # If we should force triangulation for meshes
"should_generate_uvs": False, # If UV coordinates should also be generated
# Possible IFC representation classes:
# IfcExtrudedAreaSolid/IfcRectangleProfileDef
# IfcExtrudedAreaSolid/IfcCircleProfileDef
# IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef
# IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids
# IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage
# IfcGeometricCurveSet/IfcTextLiteral
# IfcTextLiteral
"ifc_representation_class": None, # Whether to cast a mesh into a particular class
"profile_set_usage": None, # The material profile set if the extrusion requires it
"text_literal": None, # The text literal if the representation requires it
}
self.ifc_vertices = []
for key, value in settings.items():
self.settings[key] = value
def add_representation(file: ifcopenshell.file, **usecase_settings) -> ifcopenshell.entity_instance:
usecase = Usecase()
# TODO: This usecase currently depends on Blender's data model
usecase.file = file
usecase.settings = {
"context": None, # IfcGeometricRepresentationContext
"blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now
"geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now
"coordinate_offset": None, # Optionally apply a vector offset to all coordinates
"total_items": 1, # How many representation items to create
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
"should_force_triangulation": False, # If we should force triangulation for meshes
"should_generate_uvs": False, # If UV coordinates should also be generated
# Possible IFC representation classes:
# IfcExtrudedAreaSolid/IfcRectangleProfileDef
# IfcExtrudedAreaSolid/IfcCircleProfileDef
# IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef
# IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids
# IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage
# IfcGeometricCurveSet/IfcTextLiteral
# IfcTextLiteral
"ifc_representation_class": None, # Whether to cast a mesh into a particular class
"profile_set_usage": None, # The material profile set if the extrusion requires it
"text_literal": None, # The text literal if the representation requires it
}
usecase.ifc_vertices = []
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
def execute(self) -> ifcopenshell.entity_instance:
class Usecase:
def execute(self):
self.is_manifold = None
if (
isinstance(self.settings["geometry"], bpy.types.Mesh)
@@ -374,10 +377,12 @@ class Usecase:
return items
def create_plane(self, polygon):
return self.file.createIfcPlane(Position=self.file.createIfcAxis2Placement3D(
Location=self.file.createIfcCartesianPoint(polygon.center),
Axis=self.file.createIfcDirection(polygon.normal),
))
return self.file.createIfcPlane(
Position=self.file.createIfcAxis2Placement3D(
Location=self.file.createIfcCartesianPoint(polygon.center),
Axis=self.file.createIfcDirection(polygon.normal),
)
)
def create_annotation_fill_areas(self, is_2d=False) -> list[ifcopenshell.entity_instance]:
items = []
@@ -20,20 +20,23 @@ import ifcopenshell.util.unit
from math import sin, cos
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"context": None, # IfcGeometricRepresentationContext
"depth": 0.2,
"x_angle": 0, # Radians
# Planes are defined either by Clipping objects
# or by dictionaries of arguments for `Clipping.parse`
"clippings": [], # A list of planes that define clipping half space solids
}
for key, value in settings.items():
self.settings[key] = value
def add_slab_representation(file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {
"context": None, # IfcGeometricRepresentationContext
"depth": 0.2,
"x_angle": 0, # Radians
# Planes are defined either by Clipping objects
# or by dictionaries of arguments for `Clipping.parse`
"clippings": [], # A list of planes that define clipping half space solids
}
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
return self.file.createIfcShapeRepresentation(
@@ -21,25 +21,28 @@ from math import sin, cos
from ifcopenshell.util.data import Clipping
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"context": None, # IfcGeometricRepresentationContext
"length": 1.0,
"height": 3.0,
"offset": 0.0,
"thickness": 0.2,
# Sloped walls along the wall's X axis, provided in radians
"x_angle": 0,
# Planes are defined either by Clipping objects
# or by dictionaries of arguments for `Clipping.parse`
"clippings": [], # A list of planes that define clipping half space solids
"booleans": [], # Any existing IfcBooleanResults
}
for key, value in settings.items():
self.settings[key] = value
def add_wall_representation(file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {
"context": None, # IfcGeometricRepresentationContext
"length": 1.0,
"height": 3.0,
"offset": 0.0,
"thickness": 0.2,
# Sloped walls along the wall's X axis, provided in radians
"x_angle": 0,
# Planes are defined either by Clipping objects
# or by dictionaries of arguments for `Clipping.parse`
"clippings": [], # A list of planes that define clipping half space solids
"booleans": [], # Any existing IfcBooleanResults
}
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
self.settings["clippings"] = [Clipping.parse(c) for c in self.settings["clippings"]]
@@ -56,12 +56,7 @@ def create_ifc_window_frame_simple(
th_left, th_up, th_right, th_bottom = thickness
def get_extruded_profile(profile):
return builder.extrude(
profile,
size.y,
position=position,
**builder.extrude_kwargs("Y")
)
return builder.extrude(profile, size.y, position=position, **builder.extrude_kwargs("Y"))
# if all lining sides are present then we can just use two rectangles
# as inner and outer curves of the profile
@@ -207,12 +202,7 @@ def create_ifc_window(
glass_position = frame_position + V(0, frame_size.y / 2 - glass_thickness / 2, 0)
glass_rect = builder.deep_copy(frame_extruded_items[0].SweptArea.InnerCurves[0])
glass = builder.extrude(
glass_rect,
glass_thickness,
position=glass_position,
**builder.extrude_kwargs("Y")
)
glass = builder.extrude(glass_rect, glass_thickness, position=glass_position, **builder.extrude_kwargs("Y"))
output_items = [lining_items, frame_extruded_items, [glass]]
builder.translate(chain(*output_items), position)
@@ -220,73 +210,76 @@ def create_ifc_window(
return output_items
class Usecase:
def __init__(self, file, **settings):
"""units in settings expected to be in ifc project units"""
self.file = file
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm
self.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(self.file)}
self.settings.update(
{
"context": None, # IfcGeometricRepresentationContext
# SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL,
# TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT,
# TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
"partition_type": "SINGLE_PANEL",
"overall_height": self.convert_si_to_unit(0.9),
"overall_width": self.convert_si_to_unit(0.6),
"lining_properties": {
"LiningDepth": self.convert_si_to_unit(0.050),
"LiningThickness": self.convert_si_to_unit(0.050),
"LiningOffset": self.convert_si_to_unit(0.050), # offset to the wall
# offset from the wall
"LiningToPanelOffsetX": self.convert_si_to_unit(0.025),
# offset from the lining
# that way it allows you to define overall_depth constant between all panels
# and still have panels with different size:
# overall_depth = lining_depth + offset_y
# full offset from X axis = overall_depth - frame_depth
"LiningToPanelOffsetY": self.convert_si_to_unit(0.025),
# applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop,
# TriplePanelLeft, TriplePanelRight
# mullion - horizontal distance between panels
"MullionThickness": self.convert_si_to_unit(0.050),
# distance from the first lining to the mullion center
"FirstMullionOffset": self.convert_si_to_unit(0.3),
# applies to TriplePanelVertical
# distance from the first lining to the second mullion center
"SecondMullionOffset": self.convert_si_to_unit(0.45),
# applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop,
# TriplePanelLeft, TriplePanelRight
# works similar way to mullion
"TransomThickness": self.convert_si_to_unit(0.050),
"FirstTransomOffset": self.convert_si_to_unit(0.3),
# applies to TriplePanelHorizontal
"SecondTransomOffset": self.convert_si_to_unit(0.6),
def add_window_representation(file, **usecase_settings) -> None:
"""units in usecase_settings expected to be in ifc project units"""
usecase = Usecase()
usecase.file = file
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindow.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowTypePartitioningEnum.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowLiningProperties.htm
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.htm
usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
usecase.settings.update(
{
"context": None, # IfcGeometricRepresentationContext
# SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL,
# TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT,
# TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
"partition_type": "SINGLE_PANEL",
"overall_height": usecase.convert_si_to_unit(0.9),
"overall_width": usecase.convert_si_to_unit(0.6),
"lining_properties": {
"LiningDepth": usecase.convert_si_to_unit(0.050),
"LiningThickness": usecase.convert_si_to_unit(0.050),
"LiningOffset": usecase.convert_si_to_unit(0.050), # offset to the wall
# offset from the wall
"LiningToPanelOffsetX": usecase.convert_si_to_unit(0.025),
# offset from the lining
# that way it allows you to define overall_depth constant between all panels
# and still have panels with different size:
# overall_depth = lining_depth + offset_y
# full offset from X axis = overall_depth - frame_depth
"LiningToPanelOffsetY": usecase.convert_si_to_unit(0.025),
# applies to DoublePanelVertical, TriplePanelBottom, TriplePanelTop,
# TriplePanelLeft, TriplePanelRight
# mullion - horizontal distance between panels
"MullionThickness": usecase.convert_si_to_unit(0.050),
# distance from the first lining to the mullion center
"FirstMullionOffset": usecase.convert_si_to_unit(0.3),
# applies to TriplePanelVertical
# distance from the first lining to the second mullion center
"SecondMullionOffset": usecase.convert_si_to_unit(0.45),
# applies to DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop,
# TriplePanelLeft, TriplePanelRight
# works similar way to mullion
"TransomThickness": usecase.convert_si_to_unit(0.050),
"FirstTransomOffset": usecase.convert_si_to_unit(0.3),
# applies to TriplePanelHorizontal
"SecondTransomOffset": usecase.convert_si_to_unit(0.6),
"ShapeAspectStyle": None, # DEPRECATED
},
"panel_properties": [
{
"FrameDepth": usecase.convert_si_to_unit(0.035), # by Y
"FrameThickness": usecase.convert_si_to_unit(0.035), # by X
# BOTTOM, LEFT, MIDDLE, RIGHT, TOP
"PanelPosition": ..., # NEVER USED
# defines the basic ways to describe how window panels operate
# how it's hanged, how it opens
"OperationType": None, # NEVER USED
"ShapeAspectStyle": None, # DEPRECATED
},
"panel_properties": [
{
"FrameDepth": self.convert_si_to_unit(0.035), # by Y
"FrameThickness": self.convert_si_to_unit(0.035), # by X
# BOTTOM, LEFT, MIDDLE, RIGHT, TOP
"PanelPosition": ..., # NEVER USED
# defines the basic ways to describe how window panels operate
# how it's hanged, how it opens
"OperationType": None, # NEVER USED
"ShapeAspectStyle": None, # DEPRECATED
},
],
}
)
],
}
)
for key, value in settings.items():
self.settings[key] = value
self.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[self.settings["partition_type"]]
for key, value in usecase_settings.items():
usecase.settings[key] = value
usecase.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[usecase.settings["partition_type"]]
return usecase.execute()
class Usecase:
def execute(self):
builder = ShapeBuilder(self.file)
overall_height = self.settings["overall_height"]
@@ -20,13 +20,16 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"product": None, "representation": None}
for key, value in settings.items():
self.settings[key] = value
def assign_representation(file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {"product": None, "representation": None}
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
if self.settings["product"].is_a("IfcProduct"):
product_type = ifcopenshell.util.element.get_type(self.settings["product"])
@@ -21,44 +21,41 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"relating_element": None,
"related_element": None,
"description": None,
}
for key, value in settings.items():
self.settings[key] = value
def connect_element(file, **usecase_settings) -> None:
settings = {
"relating_element": None,
"related_element": None,
"description": None,
}
for key, value in usecase_settings.items():
settings[key] = value
def execute(self):
incompatible_connections = []
incompatible_connections = []
for rel in self.settings["relating_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]:
incompatible_connections.append(rel)
for rel in settings["relating_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]:
incompatible_connections.append(rel)
for rel in settings["related_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
incompatible_connections.append(rel)
if incompatible_connections:
for connection in set(incompatible_connections):
history = connection.OwnerHistory
self.file.remove(connection)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
if incompatible_connections:
for connection in set(incompatible_connections):
history = connection.OwnerHistory
file.remove(connection)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
for rel in self.settings["relating_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]:
rel.Description = self.settings["description"]
return rel
for rel in settings["relating_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
rel.Description = settings["description"]
return rel
return self.file.createIfcRelConnectsElements(
ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
Description=self.settings["description"],
RelatingElement=self.settings["relating_element"],
RelatedElement=self.settings["related_element"],
)
return file.createIfcRelConnectsElements(
ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
Description=settings["description"],
RelatingElement=settings["relating_element"],
RelatedElement=settings["related_element"],
)
@@ -21,76 +21,73 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"relating_element": None,
"related_element": None,
"relating_connection": "NOTDEFINED",
"related_connection": "NOTDEFINED",
"description": None,
}
for key, value in settings.items():
self.settings[key] = value
def connect_path(file, **usecase_settings) -> None:
settings = {
"relating_element": None,
"related_element": None,
"relating_connection": "NOTDEFINED",
"related_connection": "NOTDEFINED",
"description": None,
}
for key, value in usecase_settings.items():
settings[key] = value
def execute(self):
incompatible_connections = []
for rel in self.settings["relating_element"].ConnectedTo:
if not rel.is_a("IfcRelConnectsPathElements"):
continue
if rel.RelatedElement == self.settings["related_element"]:
incompatible_connections.append(rel)
elif (
rel.RelatingConnectionType in ["ATSTART", "ATEND"]
and rel.RelatingConnectionType == self.settings["relating_connection"]
):
incompatible_connections.append(rel)
incompatible_connections = []
for rel in settings["relating_element"].ConnectedTo:
if not rel.is_a("IfcRelConnectsPathElements"):
continue
if rel.RelatedElement == settings["related_element"]:
incompatible_connections.append(rel)
elif (
rel.RelatingConnectionType in ["ATSTART", "ATEND"]
and rel.RelatingConnectionType == settings["relating_connection"]
):
incompatible_connections.append(rel)
for rel in self.settings["relating_element"].ConnectedFrom:
if not rel.is_a("IfcRelConnectsPathElements"):
continue
if (
rel.RelatedConnectionType in ["ATSTART", "ATEND"]
and rel.RelatedConnectionType == self.settings["relating_connection"]
):
incompatible_connections.append(rel)
for rel in settings["relating_element"].ConnectedFrom:
if not rel.is_a("IfcRelConnectsPathElements"):
continue
if (
rel.RelatedConnectionType in ["ATSTART", "ATEND"]
and rel.RelatedConnectionType == settings["relating_connection"]
):
incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedFrom:
if not rel.is_a("IfcRelConnectsPathElements"):
continue
if (
rel.RelatedConnectionType in ["ATSTART", "ATEND"]
and rel.RelatedConnectionType == self.settings["related_connection"]
):
incompatible_connections.append(rel)
for rel in settings["related_element"].ConnectedFrom:
if not rel.is_a("IfcRelConnectsPathElements"):
continue
if (
rel.RelatedConnectionType in ["ATSTART", "ATEND"]
and rel.RelatedConnectionType == settings["related_connection"]
):
incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedTo:
if not rel.is_a("IfcRelConnectsPathElements"):
continue
if rel.RelatedElement == self.settings["relating_element"]:
incompatible_connections.append(rel)
elif (
rel.RelatingConnectionType in ["ATSTART", "ATEND"]
and rel.RelatingConnectionType == self.settings["related_connection"]
):
incompatible_connections.append(rel)
for rel in settings["related_element"].ConnectedTo:
if not rel.is_a("IfcRelConnectsPathElements"):
continue
if rel.RelatedElement == settings["relating_element"]:
incompatible_connections.append(rel)
elif (
rel.RelatingConnectionType in ["ATSTART", "ATEND"]
and rel.RelatingConnectionType == settings["related_connection"]
):
incompatible_connections.append(rel)
if incompatible_connections:
for connection in set(incompatible_connections):
history = connection.OwnerHistory
self.file.remove(connection)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
if incompatible_connections:
for connection in set(incompatible_connections):
history = connection.OwnerHistory
file.remove(connection)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
return self.file.createIfcRelConnectsPathElements(
ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", self.file),
Description=self.settings["description"],
RelatingElement=self.settings["relating_element"],
RelatedElement=self.settings["related_element"],
RelatingConnectionType=self.settings["relating_connection"],
RelatedConnectionType=self.settings["related_connection"],
RelatingPriorities=[],
RelatedPriorities=[],
)
return file.createIfcRelConnectsPathElements(
ifcopenshell.guid.new(),
OwnerHistory=ifcopenshell.api.run("owner.create_owner_history", file),
Description=settings["description"],
RelatingElement=settings["relating_element"],
RelatedElement=settings["related_element"],
RelatingConnectionType=settings["relating_connection"],
RelatedConnectionType=settings["related_connection"],
RelatingPriorities=[],
RelatedPriorities=[],
)
@@ -21,20 +21,25 @@ import ifcopenshell.api
import ifcopenshell.util.unit
class Usecase:
def __init__(self, file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True):
self.file = file
self.settings = {
"element": element,
"context": context,
"p1": p1,
"p2": p2,
"elevation": elevation,
"height": height,
"thickness": thickness,
"is_si": is_si
}
def create_2pt_wall(
file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True
) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {
"element": element,
"context": context,
"p1": p1,
"p2": p2,
"elevation": elevation,
"height": height,
"thickness": thickness,
"is_si": is_si,
}
return usecase.execute()
class Usecase:
def execute(self):
self.settings["unit_scale"] = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -44,9 +49,9 @@ class Usecase:
length = float(np.linalg.norm(self.settings["p2"] - self.settings["p1"]))
if not self.settings["is_si"]:
length=self.convert_unit_to_si(length)
self.settings["height"]=self.convert_unit_to_si(self.settings["height"])
self.settings["thickness"]=self.convert_unit_to_si(self.settings["thickness"])
length = self.convert_unit_to_si(length)
self.settings["height"] = self.convert_unit_to_si(self.settings["height"])
self.settings["thickness"] = self.convert_unit_to_si(self.settings["thickness"])
self.settings["p1"][0] = self.convert_unit_to_si(self.settings["p1"][0])
self.settings["p1"][1] = self.convert_unit_to_si(self.settings["p1"][1])
self.settings["elevation"] = self.convert_unit_to_si(self.settings["elevation"])
@@ -20,38 +20,35 @@ import ifcopenshell
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"relating_element": None,
"related_element": None,
}
for key, value in settings.items():
self.settings[key] = value
def disconnect_element(file, **usecase_settings) -> None:
settings = {
"relating_element": None,
"related_element": None,
}
for key, value in usecase_settings.items():
settings[key] = value
def execute(self):
incompatible_connections = []
incompatible_connections = []
for rel in self.settings["relating_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["related_element"]:
incompatible_connections.append(rel)
for rel in settings["relating_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
incompatible_connections.append(rel)
for rel in self.settings["relating_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["related_element"]:
incompatible_connections.append(rel)
for rel in settings["relating_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == self.settings["relating_element"]:
incompatible_connections.append(rel)
for rel in settings["related_element"].ConnectedTo:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
incompatible_connections.append(rel)
for rel in self.settings["related_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == self.settings["relating_element"]:
incompatible_connections.append(rel)
for rel in settings["related_element"].ConnectedFrom:
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["relating_element"]:
incompatible_connections.append(rel)
if incompatible_connections:
for connection in set(incompatible_connections):
history = connection.OwnerHistory
self.file.remove(connection)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
if incompatible_connections:
for connection in set(incompatible_connections):
history = connection.OwnerHistory
file.remove(connection)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -21,38 +21,35 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {
"relating_element": None,
"related_element": None,
"element": None,
"connection_type": None,
}
for key, value in settings.items():
self.settings[key] = value
def disconnect_path(file, **usecase_settings) -> None:
settings = {
"relating_element": None,
"related_element": None,
"element": None,
"connection_type": None,
}
for key, value in usecase_settings.items():
settings[key] = value
def execute(self):
if self.settings["connection_type"] and self.settings["element"]:
connections = [
r
for r in self.settings["element"].ConnectedTo
if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == self.settings["connection_type"]
] + [
r
for r in self.settings["element"].ConnectedFrom
if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == self.settings["connection_type"]
]
else:
connections = [
r
for r in self.settings["relating_element"].ConnectedTo
if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == self.settings["related_element"]
]
if settings["connection_type"] and settings["element"]:
connections = [
r
for r in settings["element"].ConnectedTo
if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == settings["connection_type"]
] + [
r
for r in settings["element"].ConnectedFrom
if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == settings["connection_type"]
]
else:
connections = [
r
for r in settings["relating_element"].ConnectedTo
if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == settings["related_element"]
]
for connection in set(connections):
history = connection.OwnerHistory
self.file.remove(connection)
if history:
ifcopenshell.util.element.remove_deep2(self.file, history)
for connection in set(connections):
history = connection.OwnerHistory
file.remove(connection)
if history:
ifcopenshell.util.element.remove_deep2(file, history)
@@ -27,24 +27,26 @@ from typing import Optional, Union
NPArrayOfFloats = npt.NDArray[np.float64]
class Usecase:
def __init__(
self,
file: ifcopenshell.file,
product: ifcopenshell.entity_instance,
matrix: Optional[NPArrayOfFloats] = None,
is_si=True,
should_transform_children=False,
):
self.file = file
self.settings = {
"product": product,
"matrix": matrix if matrix is not None else np.eye(4),
"is_si": is_si,
"should_transform_children": should_transform_children,
}
def edit_object_placement(
file: ifcopenshell.file,
product: ifcopenshell.entity_instance,
matrix: Optional[NPArrayOfFloats] = None,
is_si=True,
should_transform_children=False,
) -> ifcopenshell.entity_instance:
usecase = Usecase()
usecase.file = file
usecase.settings = {
"product": product,
"matrix": matrix if matrix is not None else np.eye(4),
"is_si": is_si,
"should_transform_children": should_transform_children,
}
return usecase.execute()
def execute(self) -> ifcopenshell.entity_instance:
class Usecase:
def execute(self):
if not hasattr(self.settings["product"], "ObjectPlacement"):
return
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(self.file)
@@ -17,14 +17,17 @@
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"representation": None}
self.ifc_vertices = []
for key, value in settings.items():
self.settings[key] = value
def map_representation(file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {"representation": None}
usecase.ifc_vertices = []
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
mapping_source = self.get_mapping_source()
@@ -19,13 +19,16 @@
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"item": None}
for key, value in settings.items():
self.settings[key] = value
def remove_boolean(file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {"item": None}
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
item = None
for inverse in self.file.get_inverse(self.settings["item"]):
@@ -19,62 +19,57 @@
import ifcopenshell.util.element
class Usecase:
def __init__(self, file: ifcopenshell.file, representation: ifcopenshell.entity_instance):
"""Remove a representation.
def remove_representation(file: ifcopenshell.file, representation: ifcopenshell.entity_instance) -> None:
"""Remove a representation.
Also purges representation items and their related elements
like IfcStyledItem, tessellated facesets colours and UV map.
Also purges representation items and their related elements
like IfcStyledItem, tessellated facesets colours and UV map.
:param representation: IfcRepresentation to remove.
Note that it's expected that IfcRepresentation won't be in use
before calling this method (in such elements as IfcProductRepresentation, IfcShapeAspect)
otherwise representation won't be removed.
:type representation: ifcopenshell.entity_instance
:return: None
:rtype: None
"""
self.file = file
self.settings = {"representation": representation}
:param representation: IfcRepresentation to remove.
Note that it's expected that IfcRepresentation won't be in use
before calling this method (in such elements as IfcProductRepresentation, IfcShapeAspect)
otherwise representation won't be removed.
:type representation: ifcopenshell.entity_instance
:return: None
:rtype: None
"""
settings = {"representation": representation}
def execute(self) -> None:
styled_items = set()
presentation_layer_assignments = set()
textures = set()
colours = set()
for subelement in self.file.traverse(self.settings["representation"]):
if subelement.is_a("IfcRepresentationItem"):
[styled_items.add(s) for s in subelement.StyledByItem or []]
# IFC2X3 is using LayerAssignments
for s in (
subelement.LayerAssignment
if hasattr(subelement, "LayerAssignment")
else subelement.LayerAssignments
):
presentation_layer_assignments.add(s)
# IfcTessellatedFaceSet inverses
[textures.add(t) for t in getattr(subelement, "HasTextures", []) or []]
[colours.add(t) for t in getattr(subelement, "HasColours", []) or []]
elif subelement.is_a("IfcRepresentation"):
for layer in subelement.LayerAssignments:
presentation_layer_assignments.add(layer)
styled_items = set()
presentation_layer_assignments = set()
textures = set()
colours = set()
for subelement in file.traverse(settings["representation"]):
if subelement.is_a("IfcRepresentationItem"):
[styled_items.add(s) for s in subelement.StyledByItem or []]
# IFC2X3 is using LayerAssignments
for s in (
subelement.LayerAssignment if hasattr(subelement, "LayerAssignment") else subelement.LayerAssignments
):
presentation_layer_assignments.add(s)
# IfcTessellatedFaceSet inverses
[textures.add(t) for t in getattr(subelement, "HasTextures", []) or []]
[colours.add(t) for t in getattr(subelement, "HasColours", []) or []]
elif subelement.is_a("IfcRepresentation"):
for layer in subelement.LayerAssignments:
presentation_layer_assignments.add(layer)
ifcopenshell.util.element.remove_deep2(
self.file,
self.settings["representation"],
also_consider=list(styled_items | presentation_layer_assignments | colours),
do_not_delete=self.file.by_type("IfcGeometricRepresentationContext"),
)
ifcopenshell.util.element.remove_deep2(
file,
settings["representation"],
also_consider=list(styled_items | presentation_layer_assignments | colours),
do_not_delete=file.by_type("IfcGeometricRepresentationContext"),
)
for texture in textures:
ifcopenshell.util.element.remove_deep2(self.file, texture)
for colour in colours:
ifcopenshell.util.element.remove_deep2(self.file, colour)
for texture in textures:
ifcopenshell.util.element.remove_deep2(file, texture)
for colour in colours:
ifcopenshell.util.element.remove_deep2(file, colour)
to_delete = getattr(self.file, "to_delete", ())
for element in styled_items:
if not element.Item or element.Item in to_delete:
self.file.remove(element)
for element in presentation_layer_assignments:
if all(item in to_delete for item in element.AssignedItems):
self.file.remove(element)
to_delete = getattr(file, "to_delete", ())
for element in styled_items:
if not element.Item or element.Item in to_delete:
file.remove(element)
for element in presentation_layer_assignments:
if all(item in to_delete for item in element.AssignedItems):
file.remove(element)
@@ -20,13 +20,16 @@ import ifcopenshell.api
import ifcopenshell.util.element
class Usecase:
def __init__(self, file, **settings):
self.file = file
self.settings = {"product": None, "representation": None}
for key, value in settings.items():
self.settings[key] = value
def unassign_representation(file, **usecase_settings) -> None:
usecase = Usecase()
usecase.file = file
usecase.settings = {"product": None, "representation": None}
for key, value in usecase_settings.items():
usecase.settings[key] = value
return usecase.execute()
class Usecase:
def execute(self):
if self.settings["product"].is_a("IfcProduct"):
self.unassign_product_representation(self.settings["product"], self.settings["representation"])