mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-31 17:06:29 +00:00
typing
This commit is contained in:
@@ -20,6 +20,7 @@ import re
|
|||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.api
|
||||||
import ifcopenshell.guid
|
import ifcopenshell.guid
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.selector
|
import ifcopenshell.util.selector
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ def assign_connection_geometry(
|
|||||||
usecase.axis = axis
|
usecase.axis = axis
|
||||||
usecase.ref_direction = ref_direction
|
usecase.ref_direction = ref_direction
|
||||||
usecase.unit_scale = unit_scale
|
usecase.unit_scale = unit_scale
|
||||||
usecase.ifc_vertices = []
|
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,17 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
def add_context(file, context_type=None, context_identifier=None, target_view=None, parent=None) -> None:
|
|
||||||
|
def add_context(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
context_type: str,
|
||||||
|
context_identifier: Optional[str] = None,
|
||||||
|
target_view: Optional[str] = None,
|
||||||
|
parent: Optional[ifcopenshell.entity_instance] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Adds a new geometric representation context
|
"""Adds a new geometric representation context
|
||||||
|
|
||||||
In IFC, physical objects may have zero, one, or multiple geometric
|
In IFC, physical objects may have zero, one, or multiple geometric
|
||||||
@@ -104,7 +113,7 @@ def add_context(file, context_type=None, context_identifier=None, target_view=No
|
|||||||
:type parent: ifcopenshell.entity_instance, optional
|
:type parent: ifcopenshell.entity_instance, optional
|
||||||
:return: the newly created IfcGeometricRepresentationContext or
|
:return: the newly created IfcGeometricRepresentationContext or
|
||||||
IfcGeometricRepresentationSubContext entity
|
IfcGeometricRepresentationSubContext entity
|
||||||
:rtype: ifcopenshell.entity_instance, optional
|
:rtype: ifcopenshell.entity_instance
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,11 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
def edit_context(file, context, attributes) -> None:
|
|
||||||
|
def edit_context(file: ifcopenshell.file, context: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
|
||||||
"""Edits the attributes of an IfcGeometricRepresentationContext
|
"""Edits the attributes of an IfcGeometricRepresentationContext
|
||||||
|
|
||||||
For more information about the attributes and data types of an
|
For more information about the attributes and data types of an
|
||||||
@@ -26,7 +29,7 @@ def edit_context(file, context, attributes) -> None:
|
|||||||
:param context: The IfcGeometricRepresentationContext entity you want to edit
|
:param context: The IfcGeometricRepresentationContext entity you want to edit
|
||||||
:type context: ifcopenshell.entity_instance
|
:type context: ifcopenshell.entity_instance
|
||||||
:param attributes: a dictionary of attribute names and values.
|
:param attributes: a dictionary of attribute names and values.
|
||||||
:type attributes: dict, optional
|
:type attributes: dict
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
|
||||||
@@ -44,7 +47,7 @@ def edit_context(file, context, attributes) -> None:
|
|||||||
ifcopenshell.api.run("context.edit_context", model,
|
ifcopenshell.api.run("context.edit_context", model,
|
||||||
context=body, attributes={"ContextIdentifier": "Body"})
|
context=body, attributes={"ContextIdentifier": "Body"})
|
||||||
"""
|
"""
|
||||||
settings = {"context": context, "attributes": attributes or {}}
|
settings = {"context": context, "attributes": attributes}
|
||||||
|
|
||||||
for name, value in settings["attributes"].items():
|
for name, value in settings["attributes"].items():
|
||||||
setattr(settings["context"], name, value)
|
setattr(settings["context"], name, value)
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ from datetime import datetime
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def add_cost_schedule(file: ifcopenshell.file, name: Optional[str] = None, predefined_type="NOTDEFINED") -> None:
|
def add_cost_schedule(
|
||||||
|
file: ifcopenshell.file, name: Optional[str] = None, predefined_type: str = "NOTDEFINED"
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Add a new cost schedule
|
"""Add a new cost schedule
|
||||||
|
|
||||||
A cost schedule is a group of cost items which typically represent a
|
A cost schedule is a group of cost items which typically represent a
|
||||||
|
|||||||
@@ -16,13 +16,13 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from typing import Any, Optional
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def edit_information(
|
def edit_information(
|
||||||
file: ifcopenshell.file,
|
file: ifcopenshell.file,
|
||||||
information: ifcopenshell.entity_instance,
|
information: ifcopenshell.entity_instance,
|
||||||
attributes: Optional[dict[str, Any]] = None,
|
attributes: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Edits the attributes of an IfcDocumentInformation
|
"""Edits the attributes of an IfcDocumentInformation
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ def edit_information(
|
|||||||
:param reference: The IfcDocumentInformation entity you want to edit
|
:param reference: The IfcDocumentInformation entity you want to edit
|
||||||
:type reference: ifcopenshell.entity_instance
|
:type reference: ifcopenshell.entity_instance
|
||||||
:param attributes: a dictionary of attribute names and values.
|
:param attributes: a dictionary of attribute names and values.
|
||||||
:type attributes: dict, optional
|
:type attributes: dict
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ def edit_information(
|
|||||||
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
|
attributes={"Identification": "A-GA-6100", "Name": "Overall Plan",
|
||||||
"Location": "A-GA-6100 - Overall Plan.pdf"})
|
"Location": "A-GA-6100 - Overall Plan.pdf"})
|
||||||
"""
|
"""
|
||||||
settings = {"information": information, "attributes": attributes or {}}
|
settings = {"information": information, "attributes": attributes}
|
||||||
|
|
||||||
for name, value in settings["attributes"].items():
|
for name, value in settings["attributes"].items():
|
||||||
setattr(settings["information"], name, value)
|
setattr(settings["information"], name, value)
|
||||||
|
|||||||
@@ -16,13 +16,13 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from typing import Any, Optional
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def edit_reference(
|
def edit_reference(
|
||||||
file: ifcopenshell.file,
|
file: ifcopenshell.file,
|
||||||
reference: ifcopenshell.entity_instance,
|
reference: ifcopenshell.entity_instance,
|
||||||
attributes: Optional[dict[str, Any]] = None,
|
attributes: dict[str, Any],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Edits the attributes of an IfcDocumentReference
|
"""Edits the attributes of an IfcDocumentReference
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ def edit_reference(
|
|||||||
:param reference: The IfcDocumentReference entity you want to edit
|
:param reference: The IfcDocumentReference entity you want to edit
|
||||||
:type reference: ifcopenshell.entity_instance
|
:type reference: ifcopenshell.entity_instance
|
||||||
:param attributes: a dictionary of attribute names and values.
|
:param attributes: a dictionary of attribute names and values.
|
||||||
:type attributes: dict, optional
|
:type attributes: dict
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ def edit_reference(
|
|||||||
ifcopenshell.api.run("document.edit_reference", model,
|
ifcopenshell.api.run("document.edit_reference", model,
|
||||||
reference=reference, attributes={"Identification": "2.1.15"})
|
reference=reference, attributes={"Identification": "2.1.15"})
|
||||||
"""
|
"""
|
||||||
settings = {"reference": reference, "attributes": attributes or {}}
|
settings = {"reference": reference, "attributes": attributes}
|
||||||
|
|
||||||
for name, value in settings["attributes"].items():
|
for name, value in settings["attributes"].items():
|
||||||
setattr(settings["reference"], name, value)
|
setattr(settings["reference"], name, value)
|
||||||
|
|||||||
@@ -17,9 +17,14 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
COORD = Union[tuple[float, float], tuple[float, float, float]]
|
||||||
|
|
||||||
|
|
||||||
def add_axis_representation(file, context=None, axis=None) -> None:
|
def add_axis_representation(
|
||||||
|
file: ifcopenshell.file, context: ifcopenshell.entity_instance, axis: tuple[COORD, COORD]
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Adds a new axis representation
|
"""Adds a new axis representation
|
||||||
|
|
||||||
Certain objects are typically "axis-based", such as walls, beams,
|
Certain objects are typically "axis-based", such as walls, beams,
|
||||||
|
|||||||
@@ -16,27 +16,51 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import numpy.typing as npt
|
||||||
|
from typing import Optional, TYPE_CHECKING, Literal
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
import bpy.types
|
||||||
|
|
||||||
|
|
||||||
def add_boolean(file, **usecase_settings) -> None:
|
NPArrayOfFloats = npt.NDArray[np.float64]
|
||||||
|
|
||||||
|
|
||||||
|
def add_boolean(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
representation: ifcopenshell.entity_instance,
|
||||||
|
# A matrix to define a clipping Ifchalfspacesolid.
|
||||||
|
# The XY plane is the clipping boundary and +Z is removed.
|
||||||
|
operator: str = "DIFFERENCE",
|
||||||
|
# IfcHalfSpaceSolid, Mesh
|
||||||
|
type: Literal["IfcHalfSpaceSolid", "Mesh"] = "IfcHalfSpaceSolid",
|
||||||
|
matrix: Optional[NPArrayOfFloats] = None,
|
||||||
|
# A Blender OBJ to define the voided OBJ for a "Mesh" type
|
||||||
|
blender_obj: Optional[bpy.types.Object] = None,
|
||||||
|
# A Blender OBJ to define the void OBJ for a "Mesh" type
|
||||||
|
blender_void: Optional[bpy.types.Object] = None,
|
||||||
|
should_force_faceted_brep: bool = False,
|
||||||
|
should_force_triangulation: bool = False,
|
||||||
|
) -> list[ifcopenshell.entity_instance]:
|
||||||
|
"""For `type` values:
|
||||||
|
- "IfcHalfSpaceSolid" - `matrix` is not optional.
|
||||||
|
- "Mesh" - `blender_obj` and `blender_void` are not optional
|
||||||
|
"""
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {
|
usecase.settings = {
|
||||||
"representation": None,
|
"representation": representation,
|
||||||
"operator": "DIFFERENCE",
|
"operator": operator,
|
||||||
# IfcHalfSpaceSolid, Mesh
|
"type": type,
|
||||||
"type": "IfcHalfSpaceSolid",
|
"matrix": matrix,
|
||||||
# The XY plane is the clipping boundary and +Z is removed.
|
"blender_obj": blender_obj,
|
||||||
"matrix": None, # A matrix to define a clipping Ifchalfspacesolid.
|
"blender_void": blender_void,
|
||||||
"blender_obj": None, # A Blender OBJ to define the voided OBJ for a "Mesh" type
|
"should_force_faceted_brep": should_force_faceted_brep,
|
||||||
"blender_void": None, # A Blender OBJ to define the void OBJ for a "Mesh" type
|
"should_force_triangulation": should_force_triangulation,
|
||||||
"should_force_faceted_brep": False,
|
|
||||||
"should_force_triangulation": False,
|
|
||||||
}
|
}
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
usecase.settings[key] = value
|
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,13 +16,15 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import collections.abc
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
||||||
from ifcopenshell.api.geometry.add_window_representation import create_ifc_window
|
from ifcopenshell.api.geometry.add_window_representation import create_ifc_window
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from math import cos, radians
|
from math import cos, radians
|
||||||
|
from typing import Any, Optional, Literal, Union
|
||||||
import collections
|
import dataclasses
|
||||||
|
|
||||||
|
|
||||||
SUPPORTED_DOOR_TYPES = (
|
SUPPORTED_DOOR_TYPES = (
|
||||||
@@ -38,9 +40,14 @@ SUPPORTED_DOOR_TYPES = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def mm(x: float) -> float:
|
||||||
|
"""mm to meters shortcut for readability"""
|
||||||
|
return x / 1000
|
||||||
|
|
||||||
|
|
||||||
def create_ifc_door_lining(
|
def create_ifc_door_lining(
|
||||||
builder: ShapeBuilder, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
|
builder: ShapeBuilder, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
|
||||||
):
|
) -> ifcopenshell.entity_instance:
|
||||||
"""`thickness` of the profile is defined as list in the following order: `(SIDE, TOP)`
|
"""`thickness` of the profile is defined as list in the following order: `(SIDE, TOP)`
|
||||||
|
|
||||||
`thickness` can be also defined just as 1 float value.
|
`thickness` can be also defined just as 1 float value.
|
||||||
@@ -69,80 +76,212 @@ def create_ifc_door_lining(
|
|||||||
return door_lining
|
return door_lining
|
||||||
|
|
||||||
|
|
||||||
def create_ifc_box(builder: ShapeBuilder, size: Vector, position: Vector = V(0, 0, 0).freeze()):
|
def create_ifc_box(
|
||||||
|
builder: ShapeBuilder, size: Vector, position: Vector = V(0, 0, 0).freeze()
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
rect = builder.rectangle(size.xy)
|
rect = builder.rectangle(size.xy)
|
||||||
box = builder.extrude(rect, size.z, position=position, extrusion_vector=V(0, 0, 1))
|
box = builder.extrude(rect, size.z, position=position, extrusion_vector=V(0, 0, 1))
|
||||||
return box
|
return box
|
||||||
|
|
||||||
|
|
||||||
def add_door_representation(file, **usecase_settings) -> None:
|
# we use dataclass as we need default values for arguments
|
||||||
"""units in usecase_settings expected to be in ifc project units"""
|
# it's okay to use slots since we don't need dynamic attributes
|
||||||
|
@dataclasses.dataclass(slots=True)
|
||||||
|
class DoorLiningProperties:
|
||||||
|
LiningDepth: Optional[float] = None
|
||||||
|
"""Optional, defaults to 50mm."""
|
||||||
|
|
||||||
|
LiningThickness: Optional[float] = None
|
||||||
|
"""Optional, defaults to 50mm."""
|
||||||
|
|
||||||
|
LiningOffset: Optional[float] = None
|
||||||
|
"""Offset from the outer side of the wall (by Y-axis). Optional, defaults to 0.0."""
|
||||||
|
|
||||||
|
LiningToPanelOffsetX: Optional[float] = None
|
||||||
|
"""Offset from the wall. Optional, defaults to 25mm."""
|
||||||
|
|
||||||
|
LiningToPanelOffsetY: Optional[float] = None
|
||||||
|
"""Offset from the X-axis (unlike windows). Optional, defaults to 25mm."""
|
||||||
|
|
||||||
|
TransomThickness: Optional[float] = None
|
||||||
|
"""Vertical distance between door and window panels. Optional, defaults to 0.0."""
|
||||||
|
|
||||||
|
TransomOffset: Optional[float] = None
|
||||||
|
"""Distance from the bottom door opening
|
||||||
|
to the beginning of the transom
|
||||||
|
unlike windows TransomOffset which goes to the center of the transom.
|
||||||
|
Optional, defaults 1.525m."""
|
||||||
|
|
||||||
|
ShapeAspectStyle: None = None
|
||||||
|
"""Optional. Deprecated argument."""
|
||||||
|
|
||||||
|
CasingDepth: Optional[float] = None
|
||||||
|
"""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. Optional, defaults to 5mm."""
|
||||||
|
|
||||||
|
CasingThickness: Optional[float] = None
|
||||||
|
"""Casing thickness by Z-axis. Optional, defaults to 75mm."""
|
||||||
|
|
||||||
|
ThresholdDepth: Optional[float] = None
|
||||||
|
"""Threshold covers the bottom side of the opening. Optional, defaults to 100mm."""
|
||||||
|
|
||||||
|
ThresholdThickness: Optional[float] = None
|
||||||
|
"""Theshold thickness by Z-axis. Optional, defaults to 25mm."""
|
||||||
|
|
||||||
|
ThresholdOffset: Optional[float] = None
|
||||||
|
"""Threshold offset by Y-axis. Optional, defaults to 0.0."""
|
||||||
|
|
||||||
|
def initialize_properties(self, unit_scale: float) -> None:
|
||||||
|
# in meters
|
||||||
|
# fmt: off
|
||||||
|
default_values: dict[str, float] = dict(
|
||||||
|
LiningDepth = mm(50),
|
||||||
|
LiningThickness = mm(50),
|
||||||
|
LiningOffset = 0.0,
|
||||||
|
LiningToPanelOffsetX = mm(25),
|
||||||
|
LiningToPanelOffsetY = mm(25),
|
||||||
|
TransomThickness = 0.0,
|
||||||
|
TransomOffset = mm(1525),
|
||||||
|
CasingDepth = mm(5),
|
||||||
|
CasingThickness = mm(75),
|
||||||
|
ThresholdDepth = mm(100),
|
||||||
|
ThresholdThickness = mm(25),
|
||||||
|
ThresholdOffset = 0.0,
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
si_conversion = 1 / unit_scale
|
||||||
|
for attr, default_value in default_values.items():
|
||||||
|
if getattr(self, attr) is not None:
|
||||||
|
continue
|
||||||
|
setattr(self, attr, default_value * si_conversion)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(slots=True)
|
||||||
|
class DoorPanelProperties:
|
||||||
|
PanelDepth: Optional[float] = None
|
||||||
|
"""Frame thickness by Y axis. Optional, defaults to 35 mm."""
|
||||||
|
|
||||||
|
PanelWidth: float = 1.0
|
||||||
|
"""Ratio to the clear door opening. Optional, defaults to 1.0."""
|
||||||
|
|
||||||
|
FrameDepth: Optional[float] = None
|
||||||
|
"""Frame thickness by Y axis. Optional, defaults to 35 mm."""
|
||||||
|
|
||||||
|
FrameThickness: Optional[float] = None
|
||||||
|
"""Frame thickness by X axis. Optional, defaults to 35 mm."""
|
||||||
|
|
||||||
|
PanelPosition: None = None
|
||||||
|
"""Optional, value is never used"""
|
||||||
|
|
||||||
|
PanelOperation: None = None
|
||||||
|
"""Optional, value is never used.
|
||||||
|
Defines the basic ways to describe how door panels operate."""
|
||||||
|
|
||||||
|
ShapeAspectStyle: None = None
|
||||||
|
"""Optional. Deprecated argument."""
|
||||||
|
|
||||||
|
def initialize_properties(self, unit_scale: float) -> None:
|
||||||
|
# in meters
|
||||||
|
# fmt: off
|
||||||
|
default_values: dict[str, float] = dict(
|
||||||
|
PanelDepth = mm(35),
|
||||||
|
FrameDepth = mm(35),
|
||||||
|
FrameThickness = mm(35),
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
si_conversion = 1 / unit_scale
|
||||||
|
for attr, default_value in default_values.items():
|
||||||
|
if getattr(self, attr) is not None:
|
||||||
|
continue
|
||||||
|
setattr(self, attr, default_value * si_conversion)
|
||||||
|
|
||||||
|
|
||||||
|
def add_door_representation(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
*, # keywords only as this API implementation is probably not final
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
overall_height: Optional[float] = None,
|
||||||
|
overall_width: Optional[float] = None,
|
||||||
|
# door type
|
||||||
|
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorTypeOperationEnum.htm
|
||||||
|
operation_type: Literal[
|
||||||
|
"SINGLE_SWING_LEFT",
|
||||||
|
"SINGLE_SWING_RIGHT",
|
||||||
|
"DOUBLE_SWING_RIGHT",
|
||||||
|
"DOUBLE_SWING_LEFT",
|
||||||
|
"DOUBLE_DOOR_SINGLE_SWING",
|
||||||
|
"DOUBLE_DOOR_DOUBLE_SWING",
|
||||||
|
"SLIDING_TO_LEFT",
|
||||||
|
"SLIDING_TO_RIGHT",
|
||||||
|
"DOUBLE_DOOR_SLIDING",
|
||||||
|
] = "SINGLE_SWING_LEFT",
|
||||||
|
lining_properties: Optional[Union[DoorLiningProperties, dict[str, Any]]] = None,
|
||||||
|
panel_properties: Optional[Union[DoorPanelProperties, dict[str, Any]]] = None,
|
||||||
|
unit_scale: Optional[float] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
|
"""units in usecase_settings expected to be in ifc project units
|
||||||
|
|
||||||
|
:param context: IfcGeometricRepresentationContext for the representation.
|
||||||
|
:type context: ifcopenshell.entity_instance
|
||||||
|
:param overall_height: Overall door height. Defaults to 2m.
|
||||||
|
:type overall_height: float, optional
|
||||||
|
:param overall_width: Overall door width. Defaults to 0.9m.
|
||||||
|
:type overall_width: float, optional
|
||||||
|
:param operation_type: Type of the door. Defaults to SINGLE_SWING_LEFT.
|
||||||
|
:type operation_type: str, optional
|
||||||
|
:param lining_properties: DoorLiningProperties or a dictionary to create one.
|
||||||
|
See DoorLiningProperties description for details.
|
||||||
|
:type lining_properties: Union[DoorLiningProperties, dict[str, Any]]]
|
||||||
|
:param panel_properties: DoorPanelProperties or a dictionary to create one.
|
||||||
|
See DoorPanelProperties description for details.
|
||||||
|
:type panel_properties: Union[DoorPanelProperties, dict[str, Any]]]
|
||||||
|
:param unit_scale: The unit scale as calculated by
|
||||||
|
ifcopenshell.util.unit.calculate_unit_scale. If not provided, it
|
||||||
|
will be automatically calculated for you.
|
||||||
|
:type unit_scale: float, optional
|
||||||
|
:return: IfcShapeRepresentation for a door.
|
||||||
|
:rtype: ifcopenshell.entity_instance
|
||||||
|
|
||||||
|
"""
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
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/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/IfcDoorLiningProperties.htm
|
||||||
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcDoorPanelProperties.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)}
|
# define unit_scale first as it's going to be used setting default arguments
|
||||||
usecase.settings.update(
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file) if unit_scale is None else unit_scale
|
||||||
|
settings: dict[str, Any] = {"unit_scale": unit_scale}
|
||||||
|
|
||||||
|
if lining_properties is None:
|
||||||
|
lining_properties = DoorLiningProperties()
|
||||||
|
elif not isinstance(lining_properties, DoorLiningProperties):
|
||||||
|
lining_properties = DoorLiningProperties(**lining_properties)
|
||||||
|
lining_properties.initialize_properties(unit_scale)
|
||||||
|
lining_properties = dataclasses.asdict(lining_properties)
|
||||||
|
|
||||||
|
if panel_properties is None:
|
||||||
|
panel_properties = DoorPanelProperties()
|
||||||
|
elif not isinstance(panel_properties, DoorPanelProperties):
|
||||||
|
panel_properties = DoorPanelProperties(**panel_properties)
|
||||||
|
panel_properties.initialize_properties(unit_scale)
|
||||||
|
panel_properties = dataclasses.asdict(panel_properties)
|
||||||
|
|
||||||
|
settings.update(
|
||||||
{
|
{
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
"overall_height": usecase.convert_si_to_unit(2.0),
|
"overall_height": overall_height if overall_height is not None else usecase.convert_si_to_unit(2.0),
|
||||||
"overall_width": usecase.convert_si_to_unit(0.9),
|
"overall_width": overall_width if overall_width is not None else usecase.convert_si_to_unit(0.9),
|
||||||
# DOUBLE_DOOR_DOUBLE_SWING, DOUBLE_DOOR_FOLDING, DOUBLE_DOOR_LIFTING_VERTICAL,
|
"operation_type": operation_type,
|
||||||
# DOUBLE_DOOR_SINGLE_SWING, DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_LEFT,
|
"lining_properties": lining_properties,
|
||||||
# DOUBLE_DOOR_SINGLE_SWING_OPPOSITE_RIGHT, DOUBLE_DOOR_SLIDING,
|
"panel_properties": panel_properties,
|
||||||
# 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 = settings
|
||||||
usecase.settings[key] = value
|
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,13 +19,17 @@
|
|||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
|
|
||||||
|
|
||||||
def add_footprint_representation(file, **usecase_settings) -> None:
|
def add_footprint_representation(
|
||||||
|
file,
|
||||||
|
# IfcGeometricRepresentationContext
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
# A list of IFC curves to include in the curve set
|
||||||
|
curves: list[ifcopenshell.entity_instance],
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
settings = {
|
settings = {
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
"curves": [], # A list of IFC curves to include in the curve set
|
"curves": curves,
|
||||||
}
|
}
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
settings[key] = value
|
|
||||||
|
|
||||||
return file.createIfcShapeRepresentation(
|
return file.createIfcShapeRepresentation(
|
||||||
settings["context"],
|
settings["context"],
|
||||||
|
|||||||
@@ -17,26 +17,43 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
COORD_3D = tuple[float, float, float]
|
||||||
|
|
||||||
|
|
||||||
def add_mesh_representation(file: ifcopenshell.file, **usecase_settings) -> None:
|
def add_mesh_representation(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
# IfcGeometricRepresentationContext
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
# Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...]
|
||||||
|
# A list of coordinates
|
||||||
|
# ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...]
|
||||||
|
vertices: list[COORD_3D],
|
||||||
|
# A list of edges, represented by vertex index pairs
|
||||||
|
# ... where itemN = [(0, 1), (1, 2), (v1, v2), ...]
|
||||||
|
edges: list[tuple[int, int]],
|
||||||
|
# A list of polygons, represented by vertex indices
|
||||||
|
# ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...]
|
||||||
|
faces: list[list[int]],
|
||||||
|
# Optionally apply a vector offset to all coordinates
|
||||||
|
cooridnate_offset: Optional[COORD_3D] = None,
|
||||||
|
# A scale factor to apply for all vectors in case the unit is different
|
||||||
|
unit_scale: Optional[float] = None,
|
||||||
|
# Force using IfcFacetedBreps instead of IfcPolygonalFaceSets
|
||||||
|
force_faceted_brep: bool = False,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {
|
usecase.settings = {
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
# Vertices, edges, and faces are given in the form of: [item1, item2, item3, ...]
|
"vertices": vertices,
|
||||||
# ... where itemN = [(0., 0., 0.), (1., 1., 1.), (x, y, z), ...]
|
"edges": edges,
|
||||||
"vertices": None, # A list of coordinates
|
"faces": faces,
|
||||||
# ... where itemN = [(0, 1), (1, 2), (v1, v2), ...]
|
"coordinate_offset": cooridnate_offset,
|
||||||
"edges": None, # A list of edges, represented by vertex index pairs
|
"unit_scale": unit_scale,
|
||||||
# ... where itemN = [(0, 1, 2), (5, 4, 2, 3), (v1, v2, v3, ... vN), ...]
|
"force_faceted_brep": force_faceted_brep,
|
||||||
"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()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,23 +19,35 @@
|
|||||||
import ifcopenshell.geom
|
import ifcopenshell.geom
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from ifcopenshell.util.data import Clipping
|
from ifcopenshell.util.data import Clipping
|
||||||
|
from typing import Any, Union, Optional, Literal
|
||||||
|
|
||||||
|
VECTOR_3D = tuple[float, float, float]
|
||||||
|
|
||||||
|
|
||||||
def add_profile_representation(file, **usecase_settings) -> None:
|
def add_profile_representation(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
# IfcGeometricRepresentationContext
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
profile: ifcopenshell.entity_instance,
|
||||||
|
# in meters
|
||||||
|
depth: float = 1.0,
|
||||||
|
cardinal_point: Literal[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] = 5,
|
||||||
|
# A list of planes that define clipping half space solids
|
||||||
|
# Planes are defined either by Clipping objects
|
||||||
|
# or by dictionaries of arguments for `Clipping.parse`
|
||||||
|
clippings: Optional[list[Union[Clipping, dict[str, Any]]]] = None,
|
||||||
|
placement_zx_axes: tuple[Union[VECTOR_3D, None], Union[VECTOR_3D, None]] = (None, None),
|
||||||
|
) -> None:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {
|
usecase.settings = {
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
"profile": None,
|
"profile": profile,
|
||||||
"depth": 1.0,
|
"depth": depth,
|
||||||
"cardinal_point": 5,
|
"cardinal_point": cardinal_point,
|
||||||
# Planes are defined either by Clipping objects
|
"clippings": clippings if clippings is not None else [],
|
||||||
# or by dictionaries of arguments for `Clipping.parse`
|
"placement_zx_axes": placement_zx_axes,
|
||||||
"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()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,46 +22,100 @@ from itertools import chain
|
|||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
import collections
|
import collections
|
||||||
import mathutils
|
import mathutils
|
||||||
from pprint import pprint
|
|
||||||
from math import pi, cos, sin, tan, radians
|
from math import pi, cos, sin, tan, radians
|
||||||
|
from typing import Literal, Optional, Any
|
||||||
|
|
||||||
|
|
||||||
def mm(x):
|
def mm(x: float) -> float:
|
||||||
"""mm to meters shortcut for readability"""
|
"""mm to meters shortcut for readability"""
|
||||||
return x / 1000
|
return x / 1000
|
||||||
|
|
||||||
|
|
||||||
def add_railing_representation(file, **usecase_settings) -> None:
|
def add_railing_representation(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
*, # keywords only as this API implementation is probably not final
|
||||||
|
# IfcGeometricRepresentationContext
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
railing_type: Literal["WALL_MOUNTED_HANDRAIL"] = "WALL_MOUNTED_HANDRAIL",
|
||||||
|
railing_path: list[Vector],
|
||||||
|
use_manual_supports: bool = False,
|
||||||
|
support_spacing: Optional[float] = None,
|
||||||
|
railing_diameter: Optional[float] = None,
|
||||||
|
clear_width: Optional[float] = None,
|
||||||
|
terminal_type: Literal[
|
||||||
|
"180",
|
||||||
|
"TO_END_POST",
|
||||||
|
"TO_WALL",
|
||||||
|
"TO_FLOOR",
|
||||||
|
"TO_END_POST_AND_FLOOR",
|
||||||
|
] = "180",
|
||||||
|
height: Optional[float] = None,
|
||||||
|
looped_path: bool = False,
|
||||||
|
unit_scale: Optional[float] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""
|
"""
|
||||||
units in usecase_settings expected to be in ifc project units
|
Units are expected to be in IFC project units.
|
||||||
|
|
||||||
`railing_path` is a list of point coordinates for the railing path,
|
:param context: IfcGeometricRepresentationContext for the representation.
|
||||||
coordinates are expected to be at the top of the railing, not at the center
|
:type context: ifcopenshell.entity_instance
|
||||||
|
:param railing_type: Type of the railing. Defaults to "WALL_MOUNTED_HANDRAIL".
|
||||||
|
:type railing_type: Literal["WALL_MOUNTED_HANDRAIL"], optional
|
||||||
|
:param railing_path: A list of points coordinates for the railing path,
|
||||||
|
coordinates are expected to be at the top of the railing, not at the center.
|
||||||
|
If not provided, default path [(0, 0, 1), (1, 0, 1), (2, 0, 1)] (in meters) will be used
|
||||||
|
:type railing_path: list[Vector], optional.
|
||||||
|
:param use_manual_supports: If enabled, supports are added on every vertex on the edges of the railing path.
|
||||||
|
If disabled, supports are added automatically based on the support spacing. Default to False.
|
||||||
|
:type use_manual_supports: bool, optional
|
||||||
|
:param support_spacing: Distance between supports if automatic supports are used. Defaults to 1m.
|
||||||
|
:type support_spacing: float, optional
|
||||||
|
:param railing_diameter: Railing diameter. Defaults to 50mm.
|
||||||
|
:type railing_diameter: float, optional
|
||||||
|
:param clear_width: Clear width between the railing and the wall. Defaults to 40mm.
|
||||||
|
:type clear_width: float, optional
|
||||||
|
:param terminal_type: type of the cap. Defaults to "180".
|
||||||
|
:type terminal_type: Literal["180","TO_END_POST","TO_WALL","TO_FLOOR","TO_END_POST_AND_FLOOR"], optional
|
||||||
|
:param height: defaults to 1m
|
||||||
|
:type height: float, optional
|
||||||
|
:param looped_path: Whether to end the railing on the first point of `railing_path`. Defaults to False.
|
||||||
|
:type looped_path: bool, optional
|
||||||
|
:param unit_scale: The unit scale as calculated by
|
||||||
|
ifcopenshell.util.unit.calculate_unit_scale. If not provided, it
|
||||||
|
will be automatically calculated for you.
|
||||||
|
:type unit_scale: float, optional
|
||||||
|
:return: IfcShapeRepresentation for a railing.
|
||||||
|
:rtype: ifcopenshell.entity_instance
|
||||||
|
|
||||||
`railing_path` is expected to be a list of Vector objects
|
|
||||||
"""
|
"""
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(usecase.file)}
|
# define unit_scale first as it's going to be used setting default arguments
|
||||||
usecase.settings.update(
|
settings: dict[str, Any] = {
|
||||||
|
"unit_scale": ifcopenshell.util.unit.calculate_unit_scale(file) if unit_scale is None else unit_scale,
|
||||||
|
}
|
||||||
|
settings.update(
|
||||||
{
|
{
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
"railing_type": "WALL_MOUNTED_HANDRAIL",
|
"railing_type": railing_path,
|
||||||
"railing_path": usecase.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)]),
|
"railing_path": (
|
||||||
"use_manual_supports": False,
|
railing_path
|
||||||
"support_spacing": usecase.convert_si_to_unit(mm(1000)),
|
if railing_path is not None
|
||||||
"railing_diameter": usecase.convert_si_to_unit(mm(50)),
|
else usecase.path_si_to_units([V(0, 0, 1), V(1, 0, 1), V(2, 0, 1)])
|
||||||
"clear_width": usecase.convert_si_to_unit(mm(40)),
|
),
|
||||||
"terminal_type": "180",
|
"use_manual_supports": use_manual_supports,
|
||||||
"height": usecase.convert_si_to_unit(mm(1000)),
|
"support_spacing": support_spacing if support_spacing is not None else usecase.convert_si_to_unit(mm(1000)),
|
||||||
"looped_path": False,
|
"railing_diameter": (
|
||||||
|
railing_diameter if railing_diameter is not None else usecase.convert_si_to_unit(mm(50))
|
||||||
|
),
|
||||||
|
"clear_width": clear_width if clear_width is not None else usecase.convert_si_to_unit(mm(40)),
|
||||||
|
"terminal_type": terminal_type,
|
||||||
|
"height": height if height is not None else usecase.convert_si_to_unit(mm(1000)),
|
||||||
|
"looped_path": looped_path,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
usecase.settings = settings
|
||||||
|
|
||||||
for key, value in usecase_settings.items():
|
if railing_type != "WALL_MOUNTED_HANDRAIL":
|
||||||
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.')
|
raise Exception('Only "WALL_MOUNTED_HANDRAIL" railing_type is supported at the moment.')
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,12 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import bpy
|
import bpy.types
|
||||||
import math
|
import math
|
||||||
import bmesh
|
import bmesh
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
from typing import Union, Optional, Literal
|
||||||
|
|
||||||
|
|
||||||
Z_AXIS = Vector((0, 0, 1))
|
Z_AXIS = Vector((0, 0, 1))
|
||||||
@@ -28,7 +29,44 @@ X_AXIS = Vector((1, 0, 0))
|
|||||||
EPSILON = 1e-6
|
EPSILON = 1e-6
|
||||||
|
|
||||||
|
|
||||||
def add_representation(file: ifcopenshell.file, **usecase_settings) -> ifcopenshell.entity_instance:
|
def add_representation(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
*, # keywords only as this API implementation is probably not final
|
||||||
|
# IfcGeometricRepresentationContext
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
# This is (currently) a Blender object, hence this depends on Blender now
|
||||||
|
blender_object: bpy.types.Object,
|
||||||
|
# This is (currently) a Blender data object, hence this depends on Blender now
|
||||||
|
geometry: Union[bpy.types.Mesh, bpy.types.Curve],
|
||||||
|
# Optionally apply a vector offset to all coordinates
|
||||||
|
coordinate_offset: Optional[Vector] = None,
|
||||||
|
# How many representation items to create
|
||||||
|
total_items: int = 1,
|
||||||
|
# A scale factor to apply for all vectors in case the unit is different
|
||||||
|
unit_scale: Optional[float] = None,
|
||||||
|
# If we should force faceted breps for meshes
|
||||||
|
should_force_faceted_brep: bool = False,
|
||||||
|
# If we should force triangulation for meshes
|
||||||
|
should_force_triangulation: bool = False,
|
||||||
|
# If UV coordinates should also be generated
|
||||||
|
should_generate_uvs: bool = False,
|
||||||
|
# Whether to cast a mesh into a particular class
|
||||||
|
ifc_representation_class: Optional[
|
||||||
|
Literal[
|
||||||
|
"IfcExtrudedAreaSolid/IfcRectangleProfileDef",
|
||||||
|
"IfcExtrudedAreaSolid/IfcCircleProfileDef",
|
||||||
|
"IfcExtrudedAreaSolid/IfcArbitraryClosedProfileDef",
|
||||||
|
"IfcExtrudedAreaSolid/IfcArbitraryProfileDefWithVoids",
|
||||||
|
"IfcExtrudedAreaSolid/IfcMaterialProfileSetUsage",
|
||||||
|
"IfcGeometricCurveSet/IfcTextLiteral",
|
||||||
|
"IfcTextLiteral",
|
||||||
|
]
|
||||||
|
] = None,
|
||||||
|
# The material profile set if the extrusion requires it
|
||||||
|
profile_set_usage: Optional[ifcopenshell.entity_instance] = None,
|
||||||
|
# The text literal if the representation requires it
|
||||||
|
text_literal: Optional[ifcopenshell.entity_instance] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
# lazy import Helper to avoid circular import
|
# lazy import Helper to avoid circular import
|
||||||
if "Helper" not in globals():
|
if "Helper" not in globals():
|
||||||
from blenderbim.bim.module.geometry.helper import Helper
|
from blenderbim.bim.module.geometry.helper import Helper
|
||||||
@@ -37,30 +75,20 @@ def add_representation(file: ifcopenshell.file, **usecase_settings) -> ifcopensh
|
|||||||
# TODO: This usecase currently depends on Blender's data model
|
# TODO: This usecase currently depends on Blender's data model
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {
|
usecase.settings = {
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
"blender_object": None, # This is (currently) a Blender object, hence this depends on Blender now
|
"blender_object": blender_object,
|
||||||
"geometry": None, # This is (currently) a Blender data object, hence this depends on Blender now
|
"geometry": geometry,
|
||||||
"coordinate_offset": None, # Optionally apply a vector offset to all coordinates
|
"coordinate_offset": coordinate_offset,
|
||||||
"total_items": 1, # How many representation items to create
|
"total_items": total_items,
|
||||||
"unit_scale": None, # A scale factor to apply for all vectors in case the unit is different
|
"unit_scale": unit_scale,
|
||||||
"should_force_faceted_brep": False, # If we should force faceted breps for meshes
|
"should_force_faceted_brep": should_force_faceted_brep,
|
||||||
"should_force_triangulation": False, # If we should force triangulation for meshes
|
"should_force_triangulation": should_force_triangulation,
|
||||||
"should_generate_uvs": False, # If UV coordinates should also be generated
|
"should_generate_uvs": should_generate_uvs,
|
||||||
# Possible IFC representation classes:
|
"ifc_representation_class": ifc_representation_class,
|
||||||
# IfcExtrudedAreaSolid/IfcRectangleProfileDef
|
"profile_set_usage": profile_set_usage,
|
||||||
# IfcExtrudedAreaSolid/IfcCircleProfileDef
|
"text_literal": text_literal,
|
||||||
# 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 = []
|
usecase.ifc_vertices = []
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
usecase.settings[key] = value
|
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,22 +17,32 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
|
from ifcopenshell.util.data import Clipping
|
||||||
from math import sin, cos
|
from math import sin, cos
|
||||||
|
from typing import Any, Optional, Union
|
||||||
|
|
||||||
|
|
||||||
def add_slab_representation(file, **usecase_settings) -> None:
|
def add_slab_representation(
|
||||||
|
file,
|
||||||
|
# IfcGeometricRepresentationContext
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
# in meters
|
||||||
|
depth: float = 0.2,
|
||||||
|
# in radians
|
||||||
|
x_angle: float = 0.0,
|
||||||
|
# A list of planes that define clipping half space solids
|
||||||
|
# Planes are defined either by Clipping objects
|
||||||
|
# or by dictionaries of arguments for `Clipping.parse`
|
||||||
|
clippings: Optional[list[Union[Clipping, dict[str, Any]]]] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {
|
usecase.settings = {
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
"depth": 0.2,
|
"depth": depth,
|
||||||
"x_angle": 0, # Radians
|
"x_angle": x_angle,
|
||||||
# Planes are defined either by Clipping objects
|
"clippings": clippings if clippings is not None else [],
|
||||||
# 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()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,27 +18,39 @@
|
|||||||
|
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from math import sin, cos
|
from math import sin, cos
|
||||||
|
from typing import Optional, Union, Any
|
||||||
from ifcopenshell.util.data import Clipping
|
from ifcopenshell.util.data import Clipping
|
||||||
|
|
||||||
|
|
||||||
def add_wall_representation(file, **usecase_settings) -> None:
|
def add_wall_representation(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
context: ifcopenshell.entity_instance, # IfcGeometricRepresentationContext
|
||||||
|
# all lengths are in meters
|
||||||
|
length: float = 1.0,
|
||||||
|
height: float = 3.0,
|
||||||
|
offset: float = 0.0,
|
||||||
|
thickness: float = 0.2,
|
||||||
|
# Sloped walls along the wall's X axis, provided in radians
|
||||||
|
x_angle: float = 0.0,
|
||||||
|
# A list of planes that define clipping half space solids
|
||||||
|
# Planes are defined either by Clipping objects
|
||||||
|
# or by dictionaries of arguments for `Clipping.parse`
|
||||||
|
clippings: Optional[list[Union[Clipping, dict[str, Any]]]] = None,
|
||||||
|
# Any existing IfcBooleanResults
|
||||||
|
booleans: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {
|
usecase.settings = {
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
"length": 1.0,
|
"length": length,
|
||||||
"height": 3.0,
|
"height": height,
|
||||||
"offset": 0.0,
|
"offset": offset,
|
||||||
"thickness": 0.2,
|
"thickness": thickness,
|
||||||
# Sloped walls along the wall's X axis, provided in radians
|
"x_angle": x_angle,
|
||||||
"x_angle": 0,
|
"clippings": clippings if clippings is not None else [],
|
||||||
# Planes are defined either by Clipping objects
|
"booleans": booleans if booleans is not None else [],
|
||||||
# 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()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,14 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import collections.abc
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
from ifcopenshell.util.shape_builder import ShapeBuilder, V
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
import collections
|
import dataclasses
|
||||||
|
from typing import Any, Optional, Literal, Union
|
||||||
|
|
||||||
|
|
||||||
# SCHEMAS describe panels setup
|
# SCHEMAS describe panels setup
|
||||||
@@ -42,6 +45,11 @@ DEFAULT_PANEL_SCHEMAS = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def mm(x: float) -> float:
|
||||||
|
"""mm to meters shortcut for readability"""
|
||||||
|
return x / 1000
|
||||||
|
|
||||||
|
|
||||||
def create_ifc_window_frame_simple(
|
def create_ifc_window_frame_simple(
|
||||||
builder: ShapeBuilder, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
|
builder: ShapeBuilder, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
|
||||||
):
|
):
|
||||||
@@ -210,71 +218,209 @@ def create_ifc_window(
|
|||||||
return output_items
|
return output_items
|
||||||
|
|
||||||
|
|
||||||
def add_window_representation(file, **usecase_settings) -> None:
|
# we use dataclass as we need default values for arguments
|
||||||
"""units in usecase_settings expected to be in ifc project units"""
|
# it's okay to use slots since we don't need dynamic attributes
|
||||||
|
@dataclasses.dataclass(slots=True)
|
||||||
|
class WindowLiningProperties:
|
||||||
|
LiningDepth: Optional[float] = None
|
||||||
|
"""Optional, defaults to 50mm."""
|
||||||
|
|
||||||
|
LiningThickness: Optional[float] = None
|
||||||
|
"""Optional, defaults to 50mm."""
|
||||||
|
|
||||||
|
LiningOffset: Optional[float] = None
|
||||||
|
"""Offset to the wall. Optional, defaults to 50mm."""
|
||||||
|
|
||||||
|
LiningToPanelOffsetX: Optional[float] = None
|
||||||
|
"""Offset from the wall. Optional, defaults to 25mm."""
|
||||||
|
|
||||||
|
# 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: Optional[float] = None
|
||||||
|
"""Offset from the lining. Optional, defaults to 25mm."""
|
||||||
|
|
||||||
|
MullionThickness: Optional[float] = None
|
||||||
|
"""Mullion thickness (horizontal distance between panels).
|
||||||
|
|
||||||
|
Applies to windows of types: DoublePanelVertical, TriplePanelBottom, TriplePanelTop,
|
||||||
|
TriplePanelLeft, TriplePanelRight.
|
||||||
|
|
||||||
|
Optional, defaults to 50mm."""
|
||||||
|
|
||||||
|
FirstMullionOffset: Optional[float] = None
|
||||||
|
"""Distance from the first lining to the mullion center. Optional, defaults to 300mm."""
|
||||||
|
|
||||||
|
SecondMullionOffset: Optional[float] = None
|
||||||
|
"""Distance from the first lining to the second mullion center.
|
||||||
|
|
||||||
|
Applies to windows of type: TriplePanelVertical.
|
||||||
|
|
||||||
|
Optional, defaults to 450mm."""
|
||||||
|
|
||||||
|
TransomThickness: Optional[float] = None
|
||||||
|
"""Transom thickness (vertical distance between panels), works similar way to mullions.
|
||||||
|
|
||||||
|
Applies to windows of types:DoublePanelHorizontal, TriplePanelBottom, TriplePanelTop,
|
||||||
|
TriplePanelLeft, TriplePanelRight.
|
||||||
|
|
||||||
|
Optional, defaults to 50mm."""
|
||||||
|
|
||||||
|
FirstTransomOffset: Optional[float] = None
|
||||||
|
"""Optional, defaults to 300mm."""
|
||||||
|
|
||||||
|
SecondTransomOffset: Optional[float] = None
|
||||||
|
"""
|
||||||
|
Applies to windows of type: TriplePanelHorizontal.
|
||||||
|
Optional, defaults to 600mm."""
|
||||||
|
|
||||||
|
ShapeAspectStyle: None = None
|
||||||
|
"""Optional. Deprecated argument."""
|
||||||
|
|
||||||
|
def initialize_properties(self, unit_scale: float) -> None:
|
||||||
|
# in meters
|
||||||
|
# fmt: off
|
||||||
|
default_values: dict[str, float] = dict(
|
||||||
|
LiningDepth = mm(50),
|
||||||
|
LiningThickness = mm(50),
|
||||||
|
LiningOffset = mm(50),
|
||||||
|
LiningToPanelOffsetX = mm(25),
|
||||||
|
LiningToPanelOffsetY = mm(25),
|
||||||
|
MullionThickness = mm(50),
|
||||||
|
FirstMullionOffset = mm(300),
|
||||||
|
SecondMullionOffset = mm(450),
|
||||||
|
TransomThickness = mm(50),
|
||||||
|
FirstTransomOffset = mm(300),
|
||||||
|
SecondTransomOffset = mm(600),
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
si_conversion = 1 / unit_scale
|
||||||
|
for attr, default_value in default_values.items():
|
||||||
|
if getattr(self, attr) is not None:
|
||||||
|
continue
|
||||||
|
setattr(self, attr, default_value * si_conversion)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass(slots=True)
|
||||||
|
class WindowPanelProperties:
|
||||||
|
FrameDepth: Optional[float] = None
|
||||||
|
"""Frame thickness by Y axis. Optional, defaults to 35 mm."""
|
||||||
|
|
||||||
|
FrameThickness: Optional[float] = None
|
||||||
|
"""Frame thickness by X axis. Optional, defaults to 35 mm."""
|
||||||
|
|
||||||
|
PanelPosition: None = None
|
||||||
|
"""Optional, value is never used"""
|
||||||
|
|
||||||
|
PanelOperation: None = None
|
||||||
|
"""Optional, value is never used.
|
||||||
|
Defines the basic ways to describe how window panels operate."""
|
||||||
|
|
||||||
|
ShapeAspectStyle: None = None
|
||||||
|
"""Optional. Deprecated argument."""
|
||||||
|
|
||||||
|
def initialize_properties(self, unit_scale: float) -> None:
|
||||||
|
# in meters
|
||||||
|
# fmt: off
|
||||||
|
default_values: dict[str, float] = dict(
|
||||||
|
FrameDepth = mm(35),
|
||||||
|
FrameThickness = mm(35),
|
||||||
|
)
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
|
si_conversion = 1 / unit_scale
|
||||||
|
for attr, default_value in default_values.items():
|
||||||
|
if getattr(self, attr) is not None:
|
||||||
|
continue
|
||||||
|
setattr(self, attr, default_value * si_conversion)
|
||||||
|
|
||||||
|
|
||||||
|
def add_window_representation(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
*, # keywords only as this API implementation is probably not final
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
overall_height: Optional[float] = None,
|
||||||
|
overall_width: Optional[float] = None,
|
||||||
|
partition_type: Literal[
|
||||||
|
"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",
|
||||||
|
] = "SINGLE_PANEL",
|
||||||
|
lining_properties: Optional[Union[WindowLiningProperties, dict[str, Any]]] = None,
|
||||||
|
panel_properties: Optional[list[Union[WindowPanelProperties, dict[str, Any]]]] = None,
|
||||||
|
unit_scale: Optional[float] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
|
"""units in usecase_settings expected to be in ifc project units
|
||||||
|
|
||||||
|
:param context: IfcGeometricRepresentationContext for the representation.
|
||||||
|
:type context: ifcopenshell.entity_instance
|
||||||
|
:param overall_height: Overall window height. Defaults to 0.9m.
|
||||||
|
:type overall_height: float, optional
|
||||||
|
:param overall_width: Overall window width. Defaults to 0.6m.
|
||||||
|
:type overall_width: float, optional
|
||||||
|
:param partition_type: Type of the window. Defaults to SINGLE_PANEL.
|
||||||
|
:type partition_type: str, optional
|
||||||
|
:param lining_properties: WindowLiningProperties or a dictionary to create one.
|
||||||
|
See WindowLiningProperties description for details.
|
||||||
|
:type lining_properties: Union[WindowLiningProperties, dict[str, Any]]]
|
||||||
|
:param panel_properties: A list of WindowPanelProperties or dictionaries to create one.
|
||||||
|
See WindowPanelProperties description for details.
|
||||||
|
:type panel_properties: list[Union[WindowPanelProperties, dict[str, Any]]]]
|
||||||
|
:param unit_scale: The unit scale as calculated by
|
||||||
|
ifcopenshell.util.unit.calculate_unit_scale. If not provided, it
|
||||||
|
will be automatically calculated for you.
|
||||||
|
:type unit_scale: float, optional
|
||||||
|
:return: IfcShapeRepresentation for a window.
|
||||||
|
:rtype: ifcopenshell.entity_instance
|
||||||
|
|
||||||
|
"""
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
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/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/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/IfcWindowLiningProperties.htm
|
||||||
# http://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcWindowPanelProperties.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)}
|
# define unit_scale first as it's going to be used setting default arguments
|
||||||
usecase.settings.update(
|
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(file) if unit_scale is None else unit_scale
|
||||||
|
settings: dict[str, Any] = {"unit_scale": unit_scale}
|
||||||
|
|
||||||
|
if lining_properties is None:
|
||||||
|
lining_properties = WindowLiningProperties()
|
||||||
|
elif not isinstance(lining_properties, WindowLiningProperties):
|
||||||
|
lining_properties = WindowLiningProperties(**lining_properties)
|
||||||
|
lining_properties.initialize_properties(unit_scale)
|
||||||
|
lining_properties = dataclasses.asdict(lining_properties)
|
||||||
|
|
||||||
|
if panel_properties is None:
|
||||||
|
panel_properties = [WindowPanelProperties()]
|
||||||
|
|
||||||
|
for i in range(len(panel_properties)):
|
||||||
|
properties = panel_properties[i]
|
||||||
|
if not isinstance(properties, WindowPanelProperties):
|
||||||
|
properties = WindowPanelProperties(**properties)
|
||||||
|
properties.initialize_properties(unit_scale)
|
||||||
|
panel_properties[i] = dataclasses.asdict(properties)
|
||||||
|
|
||||||
|
settings.update(
|
||||||
{
|
{
|
||||||
"context": None, # IfcGeometricRepresentationContext
|
"context": context,
|
||||||
# SINGLE_PANEL, DOUBLE_PANEL_HORIZONTAL, DOUBLE_PANEL_VERTICAL,
|
"overall_height": overall_height if overall_height is not None else usecase.convert_si_to_unit(0.9),
|
||||||
# TRIPLE_PANEL_BOTTOM, TRIPLE_PANEL_HORIZONTAL, TRIPLE_PANEL_LEFT,
|
"overall_width": overall_width if overall_width is not None else usecase.convert_si_to_unit(0.6),
|
||||||
# TRIPLE_PANEL_RIGHT, TRIPLE_PANEL_TOP, TRIPLE_PANEL_VERTICAL
|
"partition_type": partition_type,
|
||||||
"partition_type": "SINGLE_PANEL",
|
"lining_properties": lining_properties,
|
||||||
"overall_height": usecase.convert_si_to_unit(0.9),
|
"panel_properties": panel_properties,
|
||||||
"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
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
for key, value in usecase_settings.items():
|
usecase.settings = settings
|
||||||
usecase.settings[key] = value
|
|
||||||
usecase.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[usecase.settings["partition_type"]]
|
usecase.settings["panel_schema"] = DEFAULT_PANEL_SCHEMAS[usecase.settings["partition_type"]]
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ import ifcopenshell.api
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
def assign_representation(file, **usecase_settings) -> None:
|
def assign_representation(
|
||||||
|
file: ifcopenshell.file, product: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance
|
||||||
|
) -> None:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {"product": None, "representation": None}
|
usecase.settings = {"product": product, "representation": representation}
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
usecase.settings[key] = value
|
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,16 +20,20 @@ import ifcopenshell
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.guid
|
import ifcopenshell.guid
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def connect_element(file, **usecase_settings) -> None:
|
def connect_element(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
relating_element: ifcopenshell.entity_instance,
|
||||||
|
related_element: ifcopenshell.entity_instance,
|
||||||
|
description: Optional[str] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
settings = {
|
settings = {
|
||||||
"relating_element": None,
|
"relating_element": relating_element,
|
||||||
"related_element": None,
|
"related_element": related_element,
|
||||||
"description": None,
|
"description": description,
|
||||||
}
|
}
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
settings[key] = value
|
|
||||||
|
|
||||||
incompatible_connections = []
|
incompatible_connections = []
|
||||||
|
|
||||||
|
|||||||
@@ -20,18 +20,24 @@ import ifcopenshell
|
|||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.guid
|
import ifcopenshell.guid
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def connect_path(file, **usecase_settings) -> None:
|
def connect_path(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
relating_element: ifcopenshell.entity_instance,
|
||||||
|
related_element: ifcopenshell.entity_instance,
|
||||||
|
relating_connection: str = "NOTDEFINED",
|
||||||
|
related_connection: str = "NOTDEFINED",
|
||||||
|
description: Optional[str] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
settings = {
|
settings = {
|
||||||
"relating_element": None,
|
"relating_element": relating_element,
|
||||||
"related_element": None,
|
"related_element": related_element,
|
||||||
"relating_connection": "NOTDEFINED",
|
"relating_connection": relating_connection,
|
||||||
"related_connection": "NOTDEFINED",
|
"related_connection": related_connection,
|
||||||
"description": None,
|
"description": description,
|
||||||
}
|
}
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
settings[key] = value
|
|
||||||
|
|
||||||
incompatible_connections = []
|
incompatible_connections = []
|
||||||
for rel in settings["relating_element"].ConnectedTo:
|
for rel in settings["relating_element"].ConnectedTo:
|
||||||
|
|||||||
@@ -22,8 +22,16 @@ import ifcopenshell.util.unit
|
|||||||
|
|
||||||
|
|
||||||
def create_2pt_wall(
|
def create_2pt_wall(
|
||||||
file, element=None, context=None, p1=None, p2=None, elevation=None, height=None, thickness=None, is_si=True
|
file: ifcopenshell.file,
|
||||||
) -> None:
|
element: ifcopenshell.entity_instance,
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
p1: tuple[float, float],
|
||||||
|
p2: tuple[float, float],
|
||||||
|
elevation: float,
|
||||||
|
height: float,
|
||||||
|
thickness: float,
|
||||||
|
is_si: bool = True,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {
|
usecase.settings = {
|
||||||
|
|||||||
@@ -20,30 +20,31 @@ import ifcopenshell
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
def disconnect_element(file, **usecase_settings) -> None:
|
def disconnect_element(
|
||||||
settings = {
|
file: ifcopenshell.file,
|
||||||
"relating_element": None,
|
relating_element: ifcopenshell.entity_instance,
|
||||||
"related_element": None,
|
related_element: ifcopenshell.entity_instance,
|
||||||
}
|
) -> None:
|
||||||
for key, value in usecase_settings.items():
|
# TODO: arguments relating_element, related_element probably
|
||||||
settings[key] = value
|
# should be renamed to element1, element2
|
||||||
|
# as api call doesn't really treat them as "relating" and "related"
|
||||||
|
# and just purging all connections between them
|
||||||
incompatible_connections = []
|
incompatible_connections = []
|
||||||
|
|
||||||
for rel in settings["relating_element"].ConnectedTo:
|
for rel in relating_element.ConnectedTo:
|
||||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
|
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == related_element:
|
||||||
incompatible_connections.append(rel)
|
incompatible_connections.append(rel)
|
||||||
|
|
||||||
for rel in settings["relating_element"].ConnectedFrom:
|
for rel in relating_element.ConnectedFrom:
|
||||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
|
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == related_element:
|
||||||
incompatible_connections.append(rel)
|
incompatible_connections.append(rel)
|
||||||
|
|
||||||
for rel in settings["related_element"].ConnectedTo:
|
for rel in related_element.ConnectedTo:
|
||||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
|
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == relating_element:
|
||||||
incompatible_connections.append(rel)
|
incompatible_connections.append(rel)
|
||||||
|
|
||||||
for rel in settings["related_element"].ConnectedFrom:
|
for rel in related_element.ConnectedFrom:
|
||||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["relating_element"]:
|
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == relating_element:
|
||||||
incompatible_connections.append(rel)
|
incompatible_connections.append(rel)
|
||||||
|
|
||||||
if incompatible_connections:
|
if incompatible_connections:
|
||||||
|
|||||||
@@ -19,33 +19,36 @@
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def disconnect_path(file, **usecase_settings) -> None:
|
def disconnect_path(
|
||||||
settings = {
|
file: ifcopenshell.file,
|
||||||
"relating_element": None,
|
element: Optional[ifcopenshell.entity_instance] = None,
|
||||||
"related_element": None,
|
connection_type: Optional[str] = None,
|
||||||
"element": None,
|
relating_element: Optional[ifcopenshell.entity_instance] = None,
|
||||||
"connection_type": None,
|
related_element: Optional[ifcopenshell.entity_instance] = None,
|
||||||
}
|
) -> None:
|
||||||
for key, value in usecase_settings.items():
|
"""There are two options to use this API method:
|
||||||
settings[key] = value
|
- provide `element` (connected from) and `connection_type` that should be disconnected.
|
||||||
|
- provide connected elements to disconnect explicitly:
|
||||||
if settings["connection_type"] and settings["element"]:
|
`relating_element` (connected from) and `related_element` (connected to)
|
||||||
|
"""
|
||||||
|
if connection_type and element:
|
||||||
connections = [
|
connections = [
|
||||||
r
|
r
|
||||||
for r in settings["element"].ConnectedTo
|
for r in element.ConnectedTo
|
||||||
if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == settings["connection_type"]
|
if r.is_a("IfcRelConnectsPathElements") and r.RelatingConnectionType == connection_type
|
||||||
] + [
|
] + [
|
||||||
r
|
r
|
||||||
for r in settings["element"].ConnectedFrom
|
for r in element.ConnectedFrom
|
||||||
if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == settings["connection_type"]
|
if r.is_a("IfcRelConnectsPathElements") and r.RelatedConnectionType == connection_type
|
||||||
]
|
]
|
||||||
else:
|
elif related_element:
|
||||||
connections = [
|
connections = [
|
||||||
r
|
r
|
||||||
for r in settings["relating_element"].ConnectedTo
|
for r in relating_element.ConnectedTo
|
||||||
if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == settings["related_element"]
|
if r.is_a("IfcRelConnectsPathElements") and r.RelatedElement == related_element
|
||||||
]
|
]
|
||||||
|
|
||||||
for connection in set(connections):
|
for connection in set(connections):
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ def edit_object_placement(
|
|||||||
file: ifcopenshell.file,
|
file: ifcopenshell.file,
|
||||||
product: ifcopenshell.entity_instance,
|
product: ifcopenshell.entity_instance,
|
||||||
matrix: Optional[NPArrayOfFloats] = None,
|
matrix: Optional[NPArrayOfFloats] = None,
|
||||||
is_si=True,
|
is_si: bool = True,
|
||||||
should_transform_children=False,
|
should_transform_children: bool = False,
|
||||||
) -> ifcopenshell.entity_instance:
|
) -> ifcopenshell.entity_instance:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
|
|||||||
@@ -16,14 +16,15 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
|
||||||
def map_representation(file, **usecase_settings) -> None:
|
|
||||||
|
def map_representation(
|
||||||
|
file: ifcopenshell.file, representation: ifcopenshell.entity_instance
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {"representation": None}
|
usecase.settings = {"representation": representation}
|
||||||
usecase.ifc_vertices = []
|
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
usecase.settings[key] = value
|
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,12 +19,10 @@
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
def remove_boolean(file, **usecase_settings) -> None:
|
def remove_boolean(file: ifcopenshell.file, item: ifcopenshell.entity_instance) -> None:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {"item": None}
|
usecase.settings = {"item": item}
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
usecase.settings[key] = value
|
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ import ifcopenshell.api
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
def unassign_representation(file, **usecase_settings) -> None:
|
def unassign_representation(
|
||||||
|
file: ifcopenshell.file, product: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance
|
||||||
|
) -> None:
|
||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {"product": None, "representation": None}
|
usecase.settings = {"product": product, "representation": representation}
|
||||||
for key, value in usecase_settings.items():
|
|
||||||
usecase.settings[key] = value
|
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,10 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
|
||||||
def add_georeferencing(file) -> None:
|
|
||||||
|
def add_georeferencing(file: ifcopenshell.file) -> None:
|
||||||
"""Add empty georeferencing entities to a model
|
"""Add empty georeferencing entities to a model
|
||||||
|
|
||||||
By default, models are not georeferenced. Georeferencing requires two
|
By default, models are not georeferenced. Georeferencing requires two
|
||||||
|
|||||||
@@ -16,8 +16,16 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
from typing import Optional, Any
|
||||||
|
|
||||||
def edit_georeferencing(file, map_conversion=None, projected_crs=None, true_north=None) -> None:
|
|
||||||
|
def edit_georeferencing(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
map_conversion: Optional[dict[str, Any]] = None,
|
||||||
|
projected_crs: Optional[dict[str, Any]] = None,
|
||||||
|
true_north: Optional[tuple[float, float]] = None,
|
||||||
|
) -> None:
|
||||||
"""Edits the attributes of a map conversion, projected CRS, and true north
|
"""Edits the attributes of a map conversion, projected CRS, and true north
|
||||||
|
|
||||||
Setting the correct georeferencing parameters is a complex topic and
|
Setting the correct georeferencing parameters is a complex topic and
|
||||||
@@ -47,7 +55,7 @@ def edit_georeferencing(file, map_conversion=None, projected_crs=None, true_nort
|
|||||||
names and values you want to edit.
|
names and values you want to edit.
|
||||||
:type projected_crs: dict, optional
|
:type projected_crs: dict, optional
|
||||||
:param true_north: A unitised 2D vector, where each ordinate is a float
|
:param true_north: A unitised 2D vector, where each ordinate is a float
|
||||||
:type true_north: list[float]
|
:type true_north: tuple[float, float], optional
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
|
||||||
@@ -101,7 +109,7 @@ class Usecase:
|
|||||||
self.set_true_north()
|
self.set_true_north()
|
||||||
|
|
||||||
def set_true_north(self):
|
def set_true_north(self):
|
||||||
if self.settings["true_north"] == []:
|
if self.settings["true_north"] == None:
|
||||||
return
|
return
|
||||||
for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
for context in self.file.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
|
||||||
if context.TrueNorth:
|
if context.TrueNorth:
|
||||||
@@ -111,6 +119,8 @@ class Usecase:
|
|||||||
context.TrueNorth = self.file.create_entity("IfcDirection")
|
context.TrueNorth = self.file.create_entity("IfcDirection")
|
||||||
direction = context.TrueNorth
|
direction = context.TrueNorth
|
||||||
if self.settings["true_north"] is None:
|
if self.settings["true_north"] is None:
|
||||||
|
# TODO: code will never be executed since None value
|
||||||
|
# is substituted by an empty list
|
||||||
context.TrueNorth = self.settings["true_north"]
|
context.TrueNorth = self.settings["true_north"]
|
||||||
elif context.CoordinateSpaceDimension == 2:
|
elif context.CoordinateSpaceDimension == 2:
|
||||||
direction.DirectionRatios = self.settings["true_north"][0:2]
|
direction.DirectionRatios = self.settings["true_north"][0:2]
|
||||||
|
|||||||
@@ -16,8 +16,10 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import ifcopenshell
|
||||||
|
|
||||||
def remove_georeferencing(file) -> None:
|
|
||||||
|
def remove_georeferencing(file: ifcopenshell.file) -> None:
|
||||||
"""Remove georeferencing data
|
"""Remove georeferencing data
|
||||||
|
|
||||||
All georeferencing parameters such as projected CRS and map conversion
|
All georeferencing parameters such as projected CRS and map conversion
|
||||||
|
|||||||
@@ -16,13 +16,18 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.element
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
import ifcopenshell.util.placement
|
import ifcopenshell.util.placement
|
||||||
from mathutils import Matrix # For now, we depend on Blender
|
from mathutils import Matrix # For now, we depend on Blender
|
||||||
|
import bpy.types
|
||||||
|
|
||||||
|
|
||||||
def create_axis_curve(file, axis_curve=None, grid_axis=None) -> None:
|
def create_axis_curve(
|
||||||
|
file: ifcopenshell.file, axis_curve: bpy.types.Object, grid_axis: ifcopenshell.entity_instance
|
||||||
|
) -> None:
|
||||||
"""Adds curve geometry to a grid axis to represent the axis extents
|
"""Adds curve geometry to a grid axis to represent the axis extents
|
||||||
|
|
||||||
This currently depends on the Blender geometry kernel to function.
|
This currently depends on the Blender geometry kernel to function.
|
||||||
|
|||||||
@@ -15,9 +15,17 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
import ifcopenshell
|
||||||
|
from typing import Optional, Literal
|
||||||
|
|
||||||
|
|
||||||
def create_grid_axis(file, axis_tag=None, same_sense=None, uvw_axes=None, grid=None) -> None:
|
def create_grid_axis(
|
||||||
|
file: ifcopenshell.file,
|
||||||
|
grid: ifcopenshell.entity_instance,
|
||||||
|
axis_tag: str = "A",
|
||||||
|
same_sense: bool = True,
|
||||||
|
uvw_axes: Literal["UAxes", "VAxes", "WAxes"] = "UAxes",
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Adds a new grid axis to a grid
|
"""Adds a new grid axis to a grid
|
||||||
|
|
||||||
An IFC grid will typically have a minimum of two axes which will be
|
An IFC grid will typically have a minimum of two axes which will be
|
||||||
@@ -66,17 +74,9 @@ def create_grid_axis(file, axis_tag=None, same_sense=None, uvw_axes=None, grid=N
|
|||||||
axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
|
axis_1 = ifcopenshell.api.run("grid.create_grid_axis", model,
|
||||||
axis_tag="1", uvw_axes="VAxes", grid=grid)
|
axis_tag="1", uvw_axes="VAxes", grid=grid)
|
||||||
"""
|
"""
|
||||||
settings = {
|
|
||||||
"axis_tag": axis_tag or "A",
|
|
||||||
"same_sense": same_sense or True,
|
|
||||||
"uvw_axes": uvw_axes or "UAxes", # Choose which axes
|
|
||||||
"grid": grid,
|
|
||||||
}
|
|
||||||
|
|
||||||
element = file.create_entity(
|
element = file.create_entity("IfcGridAxis", **{"AxisTag": axis_tag, "SameSense": same_sense})
|
||||||
"IfcGridAxis", **{"AxisTag": settings["axis_tag"], "SameSense": settings["same_sense"]}
|
axes = list(getattr(grid, uvw_axes) or [])
|
||||||
)
|
|
||||||
axes = list(getattr(settings["grid"], settings["uvw_axes"]) or [])
|
|
||||||
axes.append(element)
|
axes.append(element)
|
||||||
setattr(settings["grid"], settings["uvw_axes"], axes)
|
setattr(grid, uvw_axes, axes)
|
||||||
return element
|
return element
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
def remove_grid_axis(file, axis=None) -> None:
|
def remove_grid_axis(file: ifcopenshell.file, axis: ifcopenshell.entity_instance) -> None:
|
||||||
"""Removes a grid axis from a grid
|
"""Removes a grid axis from a grid
|
||||||
|
|
||||||
:param axis: The IfcGridAxis you want to remove.
|
:param axis: The IfcGridAxis you want to remove.
|
||||||
@@ -43,9 +43,8 @@ def remove_grid_axis(file, axis=None) -> None:
|
|||||||
# Let's remove it!
|
# Let's remove it!
|
||||||
ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2)
|
ifcopenshell.api.run("grid.remove_grid_axis", model, axis=axis_2)
|
||||||
"""
|
"""
|
||||||
settings = {"axis": axis}
|
axis_curve = axis.AxisCurve
|
||||||
|
if len(file.get_inverse(axis_curve)) == 1:
|
||||||
if len(file.get_inverse(settings["axis"].AxisCurve)) == 1:
|
ifcopenshell.util.element.remove_deep(file, axis_curve)
|
||||||
ifcopenshell.util.element.remove_deep(file, settings["axis"].AxisCurve)
|
file.remove(axis_curve)
|
||||||
file.remove(settings["axis"].AxisCurve)
|
file.remove(axis)
|
||||||
file.remove(settings["axis"])
|
|
||||||
|
|||||||
@@ -19,9 +19,12 @@
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
import ifcopenshell.guid
|
import ifcopenshell.guid
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
def add_group(file, Name="Unnamed", Description=None) -> None:
|
def add_group(
|
||||||
|
file: ifcopenshell.file, Name: str = "Unnamed", Description: Optional[str] = None
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Adds a new group
|
"""Adds a new group
|
||||||
|
|
||||||
An IFC group is an arbitrary collection of products, which are typically
|
An IFC group is an arbitrary collection of products, which are typically
|
||||||
|
|||||||
@@ -15,9 +15,11 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
import ifcopenshell
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
def edit_group(file, group=None, attributes=None) -> None:
|
def edit_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance, attributes: dict[str, Any]) -> None:
|
||||||
"""Edits the attributes of an IfcGroup
|
"""Edits the attributes of an IfcGroup
|
||||||
|
|
||||||
For more information about the attributes and data types of an
|
For more information about the attributes and data types of an
|
||||||
@@ -26,7 +28,7 @@ def edit_group(file, group=None, attributes=None) -> None:
|
|||||||
:param group: The IfcGroup entity you want to edit
|
:param group: The IfcGroup entity you want to edit
|
||||||
:type group: ifcopenshell.entity_instance
|
:type group: ifcopenshell.entity_instance
|
||||||
:param attributes: a dictionary of attribute names and values.
|
:param attributes: a dictionary of attribute names and values.
|
||||||
:type attributes: dict, optional
|
:type attributes: dict
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
|
||||||
@@ -38,7 +40,7 @@ def edit_group(file, group=None, attributes=None) -> None:
|
|||||||
ifcopenshell.api.run("group.edit_group", model,
|
ifcopenshell.api.run("group.edit_group", model,
|
||||||
group=group, attributes={"Description": "All furniture and joinery included in the unit"})
|
group=group, attributes={"Description": "All furniture and joinery included in the unit"})
|
||||||
"""
|
"""
|
||||||
settings = {"group": group, "attributes": attributes or {}}
|
settings = {"group": group, "attributes": attributes}
|
||||||
|
|
||||||
for name, value in settings["attributes"].items():
|
for name, value in settings["attributes"].items():
|
||||||
setattr(settings["group"], name, value)
|
setattr(settings["group"], name, value)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import ifcopenshell.api
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
def remove_group(file, group=None) -> None:
|
def remove_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance) -> None:
|
||||||
"""Removes a group
|
"""Removes a group
|
||||||
|
|
||||||
All products assigned to the group will remain, but the relationship to
|
All products assigned to the group will remain, but the relationship to
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ import ifcopenshell.api
|
|||||||
import ifcopenshell.guid
|
import ifcopenshell.guid
|
||||||
|
|
||||||
|
|
||||||
def update_group_products(file, group=None, products=None) -> None:
|
def update_group_products(
|
||||||
|
file: ifcopenshell.file, group: ifcopenshell.entity_instance, products: list[ifcopenshell.entity_instance]
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Sets a group products to be an explicit list of products
|
"""Sets a group products to be an explicit list of products
|
||||||
|
|
||||||
Any previous products assigned to that group will have their assignment
|
Any previous products assigned to that group will have their assignment
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import ifcopenshell.util.schema
|
|||||||
import ifcopenshell.util.date
|
import ifcopenshell.util.date
|
||||||
|
|
||||||
|
|
||||||
def add_library(file: ifcopenshell.entity_instance, name: str) -> ifcopenshell.entity_instance:
|
def add_library(file: ifcopenshell.file, name: str) -> ifcopenshell.entity_instance:
|
||||||
"""Adds a new library to the project
|
"""Adds a new library to the project
|
||||||
|
|
||||||
A library is an external data source that is related to the project. It
|
A library is an external data source that is related to the project. It
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ def edit_profile_usage(
|
|||||||
:param usage: The IfcMaterialProfileSetUsage entity you want to edit
|
:param usage: The IfcMaterialProfileSetUsage entity you want to edit
|
||||||
:type usage: ifcopenshell.entity_instance
|
:type usage: ifcopenshell.entity_instance
|
||||||
:param attributes: a dictionary of attribute names and values.
|
:param attributes: a dictionary of attribute names and values.
|
||||||
:type attributes: dict, optional
|
:type attributes: dict
|
||||||
:return: None
|
:return: None
|
||||||
:rtype: None
|
:rtype: None
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ def edit_profile_usage(
|
|||||||
usecase = Usecase()
|
usecase = Usecase()
|
||||||
|
|
||||||
usecase.file = file
|
usecase.file = file
|
||||||
usecase.settings = {"usage": usage, "attributes": attributes or {}}
|
usecase.settings = {"usage": usage, "attributes": attributes}
|
||||||
return usecase.execute()
|
return usecase.execute()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import ifcopenshell.guid
|
|||||||
|
|
||||||
|
|
||||||
def assign_product(
|
def assign_product(
|
||||||
file: ifcopenshell.entity_instance,
|
file: ifcopenshell.file,
|
||||||
relating_product: ifcopenshell.entity_instance,
|
relating_product: ifcopenshell.entity_instance,
|
||||||
related_object: ifcopenshell.entity_instance,
|
related_object: ifcopenshell.entity_instance,
|
||||||
) -> ifcopenshell.entity_instance:
|
) -> ifcopenshell.entity_instance:
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from typing import Any
|
|||||||
|
|
||||||
|
|
||||||
def edit_work_schedule(
|
def edit_work_schedule(
|
||||||
file: ifcopenshell.entity_instance, work_schedule: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
file: ifcopenshell.file, work_schedule: ifcopenshell.entity_instance, attributes: dict[str, Any]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Edits the attributes of an IfcWorkSchedule
|
"""Edits the attributes of an IfcWorkSchedule
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import ifcopenshell
|
|||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
def remove_work_plan(file: ifcopenshell.entity_instance, work_plan: ifcopenshell.entity_instance) -> None:
|
def remove_work_plan(file: ifcopenshell.file, work_plan: ifcopenshell.entity_instance) -> None:
|
||||||
"""Removes a work plan
|
"""Removes a work plan
|
||||||
|
|
||||||
Note that schedules that are grouped under the work plan are not
|
Note that schedules that are grouped under the work plan are not
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
def add_surface_textures(
|
def add_surface_textures(
|
||||||
file: ifcopenshell.entity_instance,
|
file: ifcopenshell.file,
|
||||||
material: Optional[bpy.types.Material] = None,
|
material: Optional[bpy.types.Material] = None,
|
||||||
textures: Optional[list[dict]] = None,
|
textures: Optional[list[dict]] = None,
|
||||||
uv_maps: Optional[list[ifcopenshell.entity_instance]] = None,
|
uv_maps: Optional[list[ifcopenshell.entity_instance]] = None,
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import zipfile
|
|||||||
import functools
|
import functools
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any, Union, Callable
|
||||||
|
|
||||||
from . import ifcopenshell_wrapper
|
from . import ifcopenshell_wrapper
|
||||||
from .entity_instance import entity_instance
|
from .entity_instance import entity_instance
|
||||||
@@ -379,7 +379,7 @@ class file:
|
|||||||
|
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr) -> Union[Any, Callable[..., ifcopenshell.entity_instance]]:
|
||||||
if attr[0:6] == "create":
|
if attr[0:6] == "create":
|
||||||
return functools.partial(self.create_entity, attr[6:])
|
return functools.partial(self.create_entity, attr[6:])
|
||||||
elif attr == "schema":
|
elif attr == "schema":
|
||||||
|
|||||||
@@ -979,7 +979,7 @@ def get_decomposition(element: ifcopenshell.entity_instance, is_recursive=True)
|
|||||||
def get_grouped_by(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
def get_grouped_by(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||||
"""Retrieves all subelements of an element based on the group.
|
"""Retrieves all subelements of an element based on the group.
|
||||||
|
|
||||||
:param element: The IFC element
|
:param element: IfcGroup entity
|
||||||
:type element: ifcopenshell.entity_instance
|
:type element: ifcopenshell.entity_instance
|
||||||
:return: All subelements of the group
|
:return: All subelements of the group
|
||||||
:rtype: list[ifcopenshell.entity_instance]
|
:rtype: list[ifcopenshell.entity_instance]
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import ifcopenshell.util.element
|
|||||||
import ifcopenshell.util.representation
|
import ifcopenshell.util.representation
|
||||||
import ifcopenshell.util.unit
|
import ifcopenshell.util.unit
|
||||||
from math import cos, sin, pi, tan, radians, degrees, atan, sqrt, ceil
|
from math import cos, sin, pi, tan, radians, degrees, atan, sqrt, ceil
|
||||||
from typing import List, Tuple, Type, Union
|
from typing import Union, Optional, Literal, Any
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ V = lambda *x: Vector([float(i) for i in x])
|
|||||||
sign = lambda x: x and (1, -1)[x < 0]
|
sign = lambda x: x and (1, -1)[x < 0]
|
||||||
PRECISION = 1.0e-5
|
PRECISION = 1.0e-5
|
||||||
|
|
||||||
VectorTuple = Type[Tuple[float, float, float]]
|
VectorTuple = type[tuple[float, float, float]]
|
||||||
"tuple of 3 `float` values"
|
"tuple of 3 `float` values"
|
||||||
|
|
||||||
|
|
||||||
@@ -59,13 +59,17 @@ class ShapeBuilder:
|
|||||||
self.file = ifc_file
|
self.file = ifc_file
|
||||||
|
|
||||||
def polyline(
|
def polyline(
|
||||||
self, points: List[Vector], closed: bool = False, position_offset: Vector = None, arc_points: List[int] = []
|
self,
|
||||||
|
points: list[Vector],
|
||||||
|
closed: bool = False,
|
||||||
|
position_offset: Optional[Vector] = None,
|
||||||
|
arc_points: list[int] = [],
|
||||||
) -> ifcopenshell.entity_instance:
|
) -> ifcopenshell.entity_instance:
|
||||||
"""
|
"""
|
||||||
Generate an IfcIndexedPolyCurve based on the provided points.
|
Generate an IfcIndexedPolyCurve based on the provided points.
|
||||||
|
|
||||||
:param points: List of 2d or 3d points
|
:param points: List of 2d or 3d points
|
||||||
:type points: List[Vector]
|
:type points: list[Vector]
|
||||||
:param closed: Whether polyline should be closed. Default is `False`
|
:param closed: Whether polyline should be closed. Default is `False`
|
||||||
:type closed: bool, optional
|
:type closed: bool, optional
|
||||||
:param position_offset: offset to be applied to all points
|
:param position_offset: offset to be applied to all points
|
||||||
@@ -73,7 +77,7 @@ class ShapeBuilder:
|
|||||||
:param arc_points: Indices of the middle points for arcs. For creating an arc segment,
|
:param arc_points: Indices of the middle points for arcs. For creating an arc segment,
|
||||||
provide 3 points: `arc_start`, `arc_middle` and `arc_end` to `points` and add the `arc_middle`
|
provide 3 points: `arc_start`, `arc_middle` and `arc_end` to `points` and add the `arc_middle`
|
||||||
point's index to `arc_points`
|
point's index to `arc_points`
|
||||||
:type arc_points: List[int], optional
|
:type arc_points: list[int], optional
|
||||||
|
|
||||||
:return: IfcIndexedPolyCurve
|
:return: IfcIndexedPolyCurve
|
||||||
:rtype: ifcopenshell.entity_instance
|
:rtype: ifcopenshell.entity_instance
|
||||||
@@ -155,7 +159,9 @@ class ShapeBuilder:
|
|||||||
ifc_curve = self.file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=ifc_segments)
|
ifc_curve = self.file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=ifc_segments)
|
||||||
return ifc_curve
|
return ifc_curve
|
||||||
|
|
||||||
def get_rectangle_coords(self, size: Vector = Vector((1.0, 1.0)).freeze(), position: Vector = None) -> List[Vector]:
|
def get_rectangle_coords(
|
||||||
|
self, size: Vector = Vector((1.0, 1.0)).freeze(), position: Optional[Vector] = None
|
||||||
|
) -> list[Vector]:
|
||||||
"""
|
"""
|
||||||
Get rectangle coords arranged as below:
|
Get rectangle coords arranged as below:
|
||||||
|
|
||||||
@@ -248,7 +254,7 @@ class ShapeBuilder:
|
|||||||
# TODO: explain points order for the curve_between_two_points
|
# TODO: explain points order for the curve_between_two_points
|
||||||
# because the order is important and defines the center of the curve
|
# because the order is important and defines the center of the curve
|
||||||
# currently it seems like the first point shifted by x-axis defines the center
|
# currently it seems like the first point shifted by x-axis defines the center
|
||||||
def curve_between_two_points(self, points):
|
def curve_between_two_points(self, points: tuple[Vector, Vector]) -> ifcopenshell.entity_instance:
|
||||||
# > points - list of 2 Vectors
|
# > points - list of 2 Vectors
|
||||||
"""Simple circle based curve between two points
|
"""Simple circle based curve between two points
|
||||||
Good for creating curves and fillets, won't work for continuous ellipse shapes.
|
Good for creating curves and fillets, won't work for continuous ellipse shapes.
|
||||||
@@ -268,7 +274,13 @@ class ShapeBuilder:
|
|||||||
curve = self.file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=[seg])
|
curve = self.file.createIfcIndexedPolyCurve(Points=ifc_points, Segments=[seg])
|
||||||
return curve
|
return curve
|
||||||
|
|
||||||
def get_trim_points_from_mask(self, x_axis_radius, y_axis_radius, trim_points_mask, position_offset=None):
|
def get_trim_points_from_mask(
|
||||||
|
self,
|
||||||
|
x_axis_radius: float,
|
||||||
|
y_axis_radius: float,
|
||||||
|
trim_points_mask: list[int],
|
||||||
|
position_offset: Optional[Vector] = None,
|
||||||
|
) -> list[Vector]:
|
||||||
"""Handy way to get edge points of the ellipse like shape of a given radiuses.
|
"""Handy way to get edge points of the ellipse like shape of a given radiuses.
|
||||||
|
|
||||||
Mask points are numerated from 0 to 3 ccw starting from (x_axis_radius/2; 0).
|
Mask points are numerated from 0 to 3 ccw starting from (x_axis_radius/2; 0).
|
||||||
@@ -289,13 +301,13 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
def create_ellipse_curve(
|
def create_ellipse_curve(
|
||||||
self,
|
self,
|
||||||
x_axis_radius,
|
x_axis_radius: float,
|
||||||
y_axis_radius,
|
y_axis_radius: float,
|
||||||
position=Vector((0.0, 0.0)).freeze(),
|
position=Vector((0.0, 0.0)).freeze(),
|
||||||
trim_points=[],
|
trim_points: list[Vector] = (),
|
||||||
ref_x_direction=Vector((1.0, 0.0)),
|
ref_x_direction: Vector = Vector((1.0, 0.0)),
|
||||||
trim_points_mask=[],
|
trim_points_mask: list[int] = (),
|
||||||
):
|
) -> ifcopenshell.entity_instance:
|
||||||
"""
|
"""
|
||||||
Ellipse trimming points should be specified in counter clockwise order.
|
Ellipse trimming points should be specified in counter clockwise order.
|
||||||
|
|
||||||
@@ -329,7 +341,13 @@ class ShapeBuilder:
|
|||||||
)
|
)
|
||||||
return trim_ellipse
|
return trim_ellipse
|
||||||
|
|
||||||
def profile(self, outer_curve, name=None, inner_curves=[], profile_type="AREA"):
|
def profile(
|
||||||
|
self,
|
||||||
|
outer_curve: ifcopenshell.entity_instance,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
inner_curves: list[ifcopenshell.entity_instance] = (),
|
||||||
|
profile_type: str = "AREA",
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
# > inner_curves - list of IfcCurve;
|
# > inner_curves - list of IfcCurve;
|
||||||
# inner_curves could be used as a tool for boolean operation
|
# inner_curves could be used as a tool for boolean operation
|
||||||
# but if any point of inner curve will go outside the outer curve
|
# but if any point of inner curve will go outside the outer curve
|
||||||
@@ -369,7 +387,12 @@ class ShapeBuilder:
|
|||||||
profile = self.file.create_entity("IfcArbitraryClosedProfileDef", **kwargs)
|
profile = self.file.create_entity("IfcArbitraryClosedProfileDef", **kwargs)
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
def translate(self, curve_or_item, translation: Vector, create_copy=False):
|
def translate(
|
||||||
|
self,
|
||||||
|
curve_or_item: Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]],
|
||||||
|
translation: Vector,
|
||||||
|
create_copy: bool = False,
|
||||||
|
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]:
|
||||||
# > curve_or_item - could be a list of curves or items or representations
|
# > curve_or_item - could be a list of curves or items or representations
|
||||||
# < returns translated object
|
# < returns translated object
|
||||||
|
|
||||||
@@ -413,7 +436,7 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
def rotate_2d_point(
|
def rotate_2d_point(
|
||||||
self, point_2d: Vector, angle=90, pivot_point: Vector = Vector((0.0, 0.0)).freeze(), counter_clockwise=False
|
self, point_2d: Vector, angle=90, pivot_point: Vector = Vector((0.0, 0.0)).freeze(), counter_clockwise=False
|
||||||
):
|
) -> Vector:
|
||||||
# > angle - in degrees
|
# > angle - in degrees
|
||||||
# < rotated Vector
|
# < rotated Vector
|
||||||
|
|
||||||
@@ -425,12 +448,12 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
def rotate(
|
def rotate(
|
||||||
self,
|
self,
|
||||||
curve_or_item,
|
curve_or_item: Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]],
|
||||||
angle=90,
|
angle: float = 90,
|
||||||
pivot_point: Vector = Vector((0.0, 0.0)).freeze(),
|
pivot_point: Vector = Vector((0.0, 0.0)).freeze(),
|
||||||
counter_clockwise=False,
|
counter_clockwise: bool = False,
|
||||||
create_copy=False,
|
create_copy: bool = False,
|
||||||
):
|
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]:
|
||||||
# > curve_or_item - could be a list of curves or items
|
# > curve_or_item - could be a list of curves or items
|
||||||
# > angle - in degrees
|
# > angle - in degrees
|
||||||
# < returns rotated object
|
# < returns rotated object
|
||||||
@@ -479,7 +502,7 @@ class ShapeBuilder:
|
|||||||
point_2d: Vector,
|
point_2d: Vector,
|
||||||
mirror_axes: Vector = Vector((1.0, 1.0)).freeze(),
|
mirror_axes: Vector = Vector((1.0, 1.0)).freeze(),
|
||||||
mirror_point: Vector = Vector((0.0, 0.0)).freeze(),
|
mirror_point: Vector = Vector((0.0, 0.0)).freeze(),
|
||||||
):
|
) -> Vector:
|
||||||
"""mirror_axes - along which axes mirror will be applied"""
|
"""mirror_axes - along which axes mirror will be applied"""
|
||||||
base = point_2d # prevent mutating the argument
|
base = point_2d # prevent mutating the argument
|
||||||
mirror_axes = Vector([-1 if i > 0 else 1 for i in mirror_axes])
|
mirror_axes = Vector([-1 if i > 0 else 1 for i in mirror_axes])
|
||||||
@@ -558,12 +581,12 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
def mirror(
|
def mirror(
|
||||||
self,
|
self,
|
||||||
curve_or_item,
|
curve_or_item: Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]],
|
||||||
mirror_axes: Vector = Vector((1.0, 1.0)).freeze(),
|
mirror_axes: Vector = Vector((1.0, 1.0)).freeze(),
|
||||||
mirror_point: Vector = Vector((0.0, 0.0)).freeze(),
|
mirror_point: Vector = Vector((0.0, 0.0)).freeze(),
|
||||||
create_copy=False,
|
create_copy: bool = False,
|
||||||
placement_matrix=None,
|
placement_matrix: Optional[Matrix] = None,
|
||||||
):
|
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]:
|
||||||
"""mirror_axes - along which axes mirror will be applied
|
"""mirror_axes - along which axes mirror will be applied
|
||||||
|
|
||||||
For example, mirroring `A(1,0)` by axis `(1,0)` will result in `A'(-1,0)`
|
For example, mirroring `A(1,0)` by axis `(1,0)` will result in `A'(-1,0)`
|
||||||
@@ -687,14 +710,14 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
def extrude(
|
def extrude(
|
||||||
self,
|
self,
|
||||||
profile_or_curve,
|
profile_or_curve: ifcopenshell.entity_instance,
|
||||||
magnitude=1.0,
|
magnitude: float = 1.0,
|
||||||
position: Vector = Vector([0.0, 0.0, 0.0]).freeze(),
|
position: Vector = Vector([0.0, 0.0, 0.0]).freeze(),
|
||||||
extrusion_vector: Vector = Vector((0.0, 0.0, 1.0)).freeze(),
|
extrusion_vector: Vector = Vector((0.0, 0.0, 1.0)).freeze(),
|
||||||
position_z_axis: Vector = Vector((0.0, 0.0, 1.0)).freeze(),
|
position_z_axis: Vector = Vector((0.0, 0.0, 1.0)).freeze(),
|
||||||
position_x_axis: Vector = Vector((1.0, 0.0, 0.0)).freeze(),
|
position_x_axis: Vector = Vector((1.0, 0.0, 0.0)).freeze(),
|
||||||
position_y_axis: Vector = None,
|
position_y_axis: Optional[Vector] = None,
|
||||||
):
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Extrude profile or curve to get IfcExtrudedAreaSolid.
|
"""Extrude profile or curve to get IfcExtrudedAreaSolid.
|
||||||
|
|
||||||
REMEMBER when handling custom axes - IFC is using RIGHT handed coordinate system.
|
REMEMBER when handling custom axes - IFC is using RIGHT handed coordinate system.
|
||||||
@@ -730,7 +753,9 @@ class ShapeBuilder:
|
|||||||
)
|
)
|
||||||
return extruded_area
|
return extruded_area
|
||||||
|
|
||||||
def create_swept_disk_solid(self, path_curve, radius):
|
def create_swept_disk_solid(
|
||||||
|
self, path_curve: ifcopenshell.entity_instance, radius: float
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Create IfcSweptDiskSolid from `path_curve` (must be 3D) and `radius`"""
|
"""Create IfcSweptDiskSolid from `path_curve` (must be 3D) and `radius`"""
|
||||||
if path_curve.Dim != 3:
|
if path_curve.Dim != 3:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
@@ -741,16 +766,22 @@ class ShapeBuilder:
|
|||||||
disk_solid = self.file.createIfcSweptDiskSolid(Directrix=path_curve, Radius=radius)
|
disk_solid = self.file.createIfcSweptDiskSolid(Directrix=path_curve, Radius=radius)
|
||||||
return disk_solid
|
return disk_solid
|
||||||
|
|
||||||
def get_representation(self, context, items, representation_type: str = None) -> ifcopenshell.entity_instance:
|
def get_representation(
|
||||||
|
self,
|
||||||
|
context: ifcopenshell.entity_instance,
|
||||||
|
items: Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]],
|
||||||
|
representation_type: Optional[str] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""Create IFC representation for the specified context and items.
|
"""Create IFC representation for the specified context and items.
|
||||||
|
|
||||||
:param context: IfcGeometricRepresentationSubContext
|
:param context: IfcGeometricRepresentationSubContext
|
||||||
|
:type context: ifcopenshell.entity_instance
|
||||||
:param items: could be a list or single curve/IfcExtrudedAreaSolid
|
:param items: could be a list or single curve/IfcExtrudedAreaSolid
|
||||||
:param representation_type: Explicitly specified RepresentationType, defaults to `None`.
|
:param representation_type: Explicitly specified RepresentationType, defaults to `None`.
|
||||||
If not provided it will be guessed from the items types
|
If not provided it will be guessed from the items types
|
||||||
:type representation_type: str, optional
|
:type representation_type: str, optional
|
||||||
|
|
||||||
:return: IfcRepresentation
|
:return: IfcShapeRepresentation
|
||||||
:rtype: ifcopenshell.entity_instance
|
:rtype: ifcopenshell.entity_instance
|
||||||
"""
|
"""
|
||||||
if not isinstance(items, collections.abc.Iterable):
|
if not isinstance(items, collections.abc.Iterable):
|
||||||
@@ -779,11 +810,11 @@ class ShapeBuilder:
|
|||||||
)
|
)
|
||||||
return representation
|
return representation
|
||||||
|
|
||||||
def deep_copy(self, element):
|
def deep_copy(self, element: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||||
return ifcopenshell.util.element.copy_deep(self.file, element)
|
return ifcopenshell.util.element.copy_deep(self.file, element)
|
||||||
|
|
||||||
# UTILITIES
|
# UTILITIES
|
||||||
def extrude_kwargs(self, axis):
|
def extrude_kwargs(self, axis: Literal["Y", "X", "Z"]) -> dict[str, Vector]:
|
||||||
"""Shortcut to get kwargs for `ShapeBuilder.extrude` to extrude by some axis.
|
"""Shortcut to get kwargs for `ShapeBuilder.extrude` to extrude by some axis.
|
||||||
|
|
||||||
It assumes you have 2D profile in:
|
It assumes you have 2D profile in:
|
||||||
@@ -814,7 +845,9 @@ class ShapeBuilder:
|
|||||||
"extrusion_vector": Vector((0, 0, 1)),
|
"extrusion_vector": Vector((0, 0, 1)),
|
||||||
}
|
}
|
||||||
|
|
||||||
def rotate_extrusion_kwargs_by_z(self, kwargs, angle, counter_clockwise=False):
|
def rotate_extrusion_kwargs_by_z(
|
||||||
|
self, kwargs: dict[str, Any], angle: float, counter_clockwise: bool = False
|
||||||
|
) -> dict[str, Vector]:
|
||||||
"""shortcut to rotate extrusion kwargs by z axis
|
"""shortcut to rotate extrusion kwargs by z axis
|
||||||
|
|
||||||
`kwargs` expected to have `position_x_axis` and `position_z_axis` keys
|
`kwargs` expected to have `position_x_axis` and `position_z_axis` keys
|
||||||
@@ -829,7 +862,7 @@ class ShapeBuilder:
|
|||||||
kwargs["position_z_axis"].rotate(rot)
|
kwargs["position_z_axis"].rotate(rot)
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def get_polyline_coords(self, polyline):
|
def get_polyline_coords(self, polyline: ifcopenshell.entity_instance) -> list[Vector]:
|
||||||
"""polyline should be either `IfcIndexedPolyCurve` or `IfcPolyline`"""
|
"""polyline should be either `IfcIndexedPolyCurve` or `IfcPolyline`"""
|
||||||
coords = None
|
coords = None
|
||||||
if polyline.is_a("IfcIndexedPolyCurve"):
|
if polyline.is_a("IfcIndexedPolyCurve"):
|
||||||
@@ -838,7 +871,7 @@ class ShapeBuilder:
|
|||||||
coords = [p.Coordinates for p in polyline.Points]
|
coords = [p.Coordinates for p in polyline.Points]
|
||||||
return coords
|
return coords
|
||||||
|
|
||||||
def set_polyline_coords(self, polyline, coords):
|
def set_polyline_coords(self, polyline: ifcopenshell.entity_instance, coords: list[Vector]) -> None:
|
||||||
"""polyline should be either `IfcIndexedPolyCurve` or `IfcPolyline`"""
|
"""polyline should be either `IfcIndexedPolyCurve` or `IfcPolyline`"""
|
||||||
if polyline.is_a("IfcIndexedPolyCurve"):
|
if polyline.is_a("IfcIndexedPolyCurve"):
|
||||||
polyline.Points.CoordList = coords
|
polyline.Points.CoordList = coords
|
||||||
@@ -846,7 +879,14 @@ class ShapeBuilder:
|
|||||||
for i, co in enumerate(coords):
|
for i, co in enumerate(coords):
|
||||||
polyline.Points[i].Coordinates = co
|
polyline.Points[i].Coordinates = co
|
||||||
|
|
||||||
def get_simple_2dcurve_data(self, coords, fillets=[], fillet_radius=[], closed=True, create_ifc_curve=None):
|
def get_simple_2dcurve_data(
|
||||||
|
self,
|
||||||
|
coords: list[Vector],
|
||||||
|
fillets: list[int] = (),
|
||||||
|
fillet_radius: list[float] = (),
|
||||||
|
closed: bool = True,
|
||||||
|
create_ifc_curve: bool = False,
|
||||||
|
) -> tuple[list[Vector], list[tuple[int, int], Union[ifcopenshell.entity_instance, None]]]:
|
||||||
"""
|
"""
|
||||||
Creates simple 2D curve from set of 2d coords and list of points with fillets.
|
Creates simple 2D curve from set of 2d coords and list of points with fillets.
|
||||||
Simple curve means that all fillets are based on 90 degree angle.
|
Simple curve means that all fillets are based on 90 degree angle.
|
||||||
@@ -957,8 +997,14 @@ class ShapeBuilder:
|
|||||||
return (points, segments, ifc_curve)
|
return (points, segments, ifc_curve)
|
||||||
|
|
||||||
def create_z_profile_lips_curve(
|
def create_z_profile_lips_curve(
|
||||||
self, FirstFlangeWidth, SecondFlangeWidth, Depth, Girth, WallThickness, FilletRadius
|
self,
|
||||||
):
|
FirstFlangeWidth: float,
|
||||||
|
SecondFlangeWidth: float,
|
||||||
|
Depth: float,
|
||||||
|
Girth: float,
|
||||||
|
WallThickness: float,
|
||||||
|
FilletRadius: float,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
x1 = FirstFlangeWidth
|
x1 = FirstFlangeWidth
|
||||||
x2 = SecondFlangeWidth
|
x2 = SecondFlangeWidth
|
||||||
y = Depth / 2
|
y = Depth / 2
|
||||||
@@ -996,7 +1042,9 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
return ifc_curve
|
return ifc_curve
|
||||||
|
|
||||||
def create_transition_arc_ifc(self, width, height, create_ifc_curve=False):
|
def create_transition_arc_ifc(
|
||||||
|
self, width: str, height: str, create_ifc_curve: bool = False
|
||||||
|
) -> tuple[list[Vector], list[tuple[int, int], Union[ifcopenshell.entity_instance, None]]]:
|
||||||
# create an arc in the rectangle with specified width and height
|
# create an arc in the rectangle with specified width and height
|
||||||
# if it's not possible to make a complete arc
|
# if it's not possible to make a complete arc
|
||||||
# it will create arc with longest radius possible
|
# it will create arc with longest radius possible
|
||||||
@@ -1028,7 +1076,7 @@ class ShapeBuilder:
|
|||||||
)
|
)
|
||||||
return points, segments, transition_arc
|
return points, segments, transition_arc
|
||||||
|
|
||||||
def polygonal_face_set(self, points, faces):
|
def polygonal_face_set(self, points: list[Vector], faces: list[[list[int]]]) -> ifcopenshell.entity_instance:
|
||||||
"""
|
"""
|
||||||
> `points` - list of points
|
> `points` - list of points
|
||||||
|
|
||||||
@@ -1048,8 +1096,14 @@ class ShapeBuilder:
|
|||||||
return face_set
|
return face_set
|
||||||
|
|
||||||
def extrude_face_set(
|
def extrude_face_set(
|
||||||
self, points, magnitude: float, extrusion_vector=V(0, 0, 1).freeze(), offset=None, start_cap=True, end_cap=True
|
self,
|
||||||
):
|
points: list[Vector],
|
||||||
|
magnitude: float,
|
||||||
|
extrusion_vector: Vector = V(0, 0, 1).freeze(),
|
||||||
|
offset: Optional[Vector] = None,
|
||||||
|
start_cap: bool = True,
|
||||||
|
end_cap: bool = True,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
"""
|
"""
|
||||||
Method to extrude by creating face sets rather than creating IfcExtrudedAreaSolid.
|
Method to extrude by creating face sets rather than creating IfcExtrudedAreaSolid.
|
||||||
|
|
||||||
@@ -1057,18 +1111,20 @@ class ShapeBuilder:
|
|||||||
to assure CorrectItemsForType.
|
to assure CorrectItemsForType.
|
||||||
|
|
||||||
:param points: list of points, assuming they form consecutive closed polyline.
|
:param points: list of points, assuming they form consecutive closed polyline.
|
||||||
|
:type points: list[Vector]
|
||||||
:param magnitude: extrusion magnitude
|
:param magnitude: extrusion magnitude
|
||||||
:param type: float
|
:type magnitude: float
|
||||||
:param extrusion_vector: extrusion direction, by default it's extruding by Z+ axis
|
:param extrusion_vector: extrusion direction, by default it's extruding by Z+ axis
|
||||||
:param type: Vector, optional
|
:type extrusion_vector: Vector, optional
|
||||||
:param offset: offset from the points
|
:param offset: offset from the points
|
||||||
:param type: Vector, optional
|
:type offset: Vector, optional
|
||||||
:param start_cap: if True, create start cap, by default it's True
|
:param start_cap: if True, create start cap, by default it's True
|
||||||
:param type: bool, optional
|
:type start_cap: bool, optional
|
||||||
:param end_cap: if True, create end cap, by default it's True
|
:param end_cap: if True, create end cap, by default it's True
|
||||||
:param type: bool, optional
|
:type end_cap: bool, optional
|
||||||
|
|
||||||
:return: IfcPolygonalFaceSet
|
:return: IfcPolygonalFaceSet
|
||||||
|
:rtype: ifcopenshell.entity_instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# prevent mutating arguments, deepcopy doesn't work
|
# prevent mutating arguments, deepcopy doesn't work
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import test.bootstrap
|
import test.bootstrap
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class TestRemovePset(test.bootstrap.IFC4):
|
class TestRemovePset(test.bootstrap.IFC4):
|
||||||
|
|||||||
Reference in New Issue
Block a user