mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 09:15:21 +00:00
shape_builder.create_axis2_placement_3d utility method and docs
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import ifcopenshell
|
||||||
|
|
||||||
|
|
||||||
def a2p(o, z, x):
|
def a2p(o, z, x):
|
||||||
@@ -44,7 +45,7 @@ def a2p(o, z, x):
|
|||||||
return r.T
|
return r.T
|
||||||
|
|
||||||
|
|
||||||
def get_axis2placement(placement):
|
def get_axis2placement(placement: ifcopenshell.entity_instance) -> np.array:
|
||||||
"""Parses an IfcAxis2Placement (2D or 3D) to a 4x4 transformation matrix
|
"""Parses an IfcAxis2Placement (2D or 3D) to a 4x4 transformation matrix
|
||||||
|
|
||||||
Note that this function only parses a single placement axis. If you want to
|
Note that this function only parses a single placement axis. If you want to
|
||||||
@@ -202,6 +203,7 @@ def rotation(angle, axis, is_degrees=True):
|
|||||||
theta = np.radians(angle) if is_degrees else angle
|
theta = np.radians(angle) if is_degrees else angle
|
||||||
cos, sin = np.cos(theta), np.sin(theta)
|
cos, sin = np.cos(theta), np.sin(theta)
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
if axis == "X":
|
if axis == "X":
|
||||||
return np.array([
|
return np.array([
|
||||||
[1, 0, 0, 0],
|
[1, 0, 0, 0],
|
||||||
@@ -223,3 +225,4 @@ def rotation(angle, axis, is_degrees=True):
|
|||||||
[0, 0, 1, 0],
|
[0, 0, 1, 0],
|
||||||
[0, 0, 0, 1]
|
[0, 0, 0, 1]
|
||||||
])
|
])
|
||||||
|
# fmt: on
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import collections
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
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
|
from typing import List, Tuple, Type, Union
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
@@ -29,6 +29,9 @@ 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]]
|
||||||
|
"tuple of 3 `float` values"
|
||||||
|
|
||||||
|
|
||||||
def is_x(value, x, si_conversion=None):
|
def is_x(value, x, si_conversion=None):
|
||||||
if si_conversion:
|
if si_conversion:
|
||||||
@@ -217,15 +220,24 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
def plane(
|
def plane(
|
||||||
self, location: Vector = Vector((0.0, 0.0, 0.0)).freeze(), normal: Vector = Vector((0.0, 0.0, 1.0)).freeze()
|
self, location: Vector = Vector((0.0, 0.0, 0.0)).freeze(), normal: Vector = Vector((0.0, 0.0, 1.0)).freeze()
|
||||||
):
|
) -> ifcopenshell.entity_instance:
|
||||||
location = self.file.createIfcCartesianPoint(location)
|
"""
|
||||||
direction = self.file.createIfcDirection(normal)
|
Create IfcPlane.
|
||||||
|
|
||||||
|
:param location: plane position, defaults to `(0.0, 0.0, 0.0)`
|
||||||
|
:type location: Vector, optional
|
||||||
|
:param normal: plane normal direction, defaults to `(0.0, 0.0, 1.0)`
|
||||||
|
:type normal: Vector, optional
|
||||||
|
:return: IfcPlane
|
||||||
|
:rtype: ifcopenshell.entity_instance
|
||||||
|
"""
|
||||||
|
|
||||||
if normal.to_tuple(2) == Vector((0.0, 0.0, 1.0)):
|
if normal.to_tuple(2) == Vector((0.0, 0.0, 1.0)):
|
||||||
arbitrary_vector = Vector((0.0, 1.0, 0.0))
|
arbitrary_vector = Vector((0.0, 1.0, 0.0))
|
||||||
else:
|
else:
|
||||||
arbitrary_vector = Vector((0.0, 0.0, 1.0))
|
arbitrary_vector = Vector((0.0, 0.0, 1.0))
|
||||||
x_axis = self.file.createIfcDirection(normal.cross(arbitrary_vector).normalized())
|
x_axis = normal.cross(arbitrary_vector).normalized()
|
||||||
axis_placement = self.file.createIfcAxis2Placement3D(location, direction, x_axis)
|
axis_placement = self.create_axis2_placement_3d(location, normal, x_axis)
|
||||||
return self.file.createIfcPlane(axis_placement)
|
return self.file.createIfcPlane(axis_placement)
|
||||||
|
|
||||||
# TODO: explain points order for the curve_between_two_points
|
# TODO: explain points order for the curve_between_two_points
|
||||||
@@ -465,8 +477,15 @@ class ShapeBuilder:
|
|||||||
point_2d = relative_point + mirror_point
|
point_2d = relative_point + mirror_point
|
||||||
return point_2d
|
return point_2d
|
||||||
|
|
||||||
def get_axis2_placement_3d_matrix(self, axis2_placement_3d):
|
def get_axis2_placement_3d_matrix(self, axis2_placement_3d: ifcopenshell.entity_instance) -> Matrix:
|
||||||
# > IfcAxis2Placement3D
|
"""
|
||||||
|
Generate a Matrix from IfcAxis2Placement3D.
|
||||||
|
|
||||||
|
:param axis2_placement_3d: IfcAxis2Placement3D
|
||||||
|
:type axis2_placement_3d: ifcopenshell.entity_instance
|
||||||
|
:return: generated matrix
|
||||||
|
:rtype: Matrix
|
||||||
|
"""
|
||||||
p = axis2_placement_3d
|
p = axis2_placement_3d
|
||||||
|
|
||||||
M = Matrix.Identity(3)
|
M = Matrix.Identity(3)
|
||||||
@@ -484,6 +503,48 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
return rotation_matrix
|
return rotation_matrix
|
||||||
|
|
||||||
|
def create_axis2_placement_3d(
|
||||||
|
self,
|
||||||
|
position: VectorTuple = (0.0, 0.0, 0.0),
|
||||||
|
z_axis: VectorTuple = (0.0, 0.0, 1.0),
|
||||||
|
x_axis: VectorTuple = (1.0, 0.0, 0.0),
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
|
"""
|
||||||
|
Create IfcAxis2Placement3D.
|
||||||
|
|
||||||
|
:param position: placement position (Axis), defaults to `(0.0, 0.0, 0.0)`
|
||||||
|
:type position: VectorTuple, optional
|
||||||
|
:param z_axis: local Z axis direction, defaults to `(0.0, 0.0, 1.0)`
|
||||||
|
:type z_axis: VectorTuple, optional
|
||||||
|
:param x_axis: local X axis direction (RefDirection), defaults to `(1.0, 0.0, 0.0)`
|
||||||
|
:type x_axis: VectorTuple, optional
|
||||||
|
:return: IfcAxis2Placement3D
|
||||||
|
:rtype: ifcopenshell.entity_instance
|
||||||
|
"""
|
||||||
|
return self.file.createIfcAxis2Placement3D(
|
||||||
|
self.file.createIfcCartesianPoint(position),
|
||||||
|
Axis=self.file.createIfcDirection(z_axis),
|
||||||
|
RefDirection=self.file.createIfcDirection(x_axis),
|
||||||
|
)
|
||||||
|
|
||||||
|
def create_axis2_placement_3d_from_matrix(
|
||||||
|
self,
|
||||||
|
matrix: Union[np.ndarray, None] = None,
|
||||||
|
) -> ifcopenshell.entity_instance:
|
||||||
|
"""
|
||||||
|
Create IfcAxis2Placement3D from numpy matrix.
|
||||||
|
|
||||||
|
:param matrix: 4x4 transformation matrix, defaults to `np.eye(4)`
|
||||||
|
:type matrix: np.array[np.array[float]], optional
|
||||||
|
:return: IfcAxis2Placement3D
|
||||||
|
:rtype: ifcopenshell.entity_instance
|
||||||
|
"""
|
||||||
|
if matrix is None:
|
||||||
|
matrix = np.eye(4, dtype=float)
|
||||||
|
return self.create_axis2_placement_3d(
|
||||||
|
position=matrix[:, 3][:3].tolist(), z_axis=matrix[:, 2][:3].tolist(), x_axis=matrix[:, 0][:3].tolist()
|
||||||
|
)
|
||||||
|
|
||||||
def mirror(
|
def mirror(
|
||||||
self,
|
self,
|
||||||
curve_or_item,
|
curve_or_item,
|
||||||
@@ -599,22 +660,18 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
return processed_objects if (multiple_objects or multiple_transformations) else processed_objects[0]
|
return processed_objects if (multiple_objects or multiple_transformations) else processed_objects[0]
|
||||||
|
|
||||||
def sphere(self, radius: float = 1.0, center: Vector = Vector((0, 0, 0)).freeze()) -> ifcopenshell.entity_instance:
|
def sphere(self, radius: float = 1.0, center: VectorTuple = (0.0, 0.0, 0.0)) -> ifcopenshell.entity_instance:
|
||||||
"""
|
"""
|
||||||
:param radius: radius of the sphere, defaults to 1.0
|
:param radius: radius of the sphere, defaults to 1.0
|
||||||
:type radius: float, optional
|
:type radius: float, optional
|
||||||
:param center: sphere position, defaults to zero-vector
|
:param center: sphere position, defaults to `(0.0, 0.0, 0.0)`
|
||||||
:type center: Vector, optional
|
:type center: VectorTuple, optional
|
||||||
|
|
||||||
:return: IfcSphere
|
:return: IfcSphere
|
||||||
:rtype: ifcopenshell.entity_instance
|
:rtype: ifcopenshell.entity_instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ifc_position = self.file.createIfcAxis2Placement3D(
|
ifc_position = self.create_axis2_placement_3d(position=center)
|
||||||
self.file.createIfcCartesianPoint(center), # position
|
|
||||||
self.file.createIfcDirection((0.0, 0.0, 1.0)), # Z-axis / Axis
|
|
||||||
self.file.createIfcDirection((1.0, 0.0, 0.0)), # X-axis / RefDirection
|
|
||||||
)
|
|
||||||
return self.file.createIfcSphere(Radius=radius, Position=ifc_position)
|
return self.file.createIfcSphere(Radius=radius, Position=ifc_position)
|
||||||
|
|
||||||
def extrude(
|
def extrude(
|
||||||
@@ -655,11 +712,7 @@ class ShapeBuilder:
|
|||||||
if position_y_axis:
|
if position_y_axis:
|
||||||
position_z_axis = position_x_axis.cross(position_y_axis)
|
position_z_axis = position_x_axis.cross(position_y_axis)
|
||||||
|
|
||||||
ifc_position = self.file.createIfcAxis2Placement3D(
|
ifc_position = self.create_axis2_placement_3d(position, position_z_axis, position_x_axis)
|
||||||
self.file.createIfcCartesianPoint(position), # position
|
|
||||||
self.file.createIfcDirection(position_z_axis), # Z-axis / Axis
|
|
||||||
self.file.createIfcDirection(position_x_axis), # X-axis / RefDirection
|
|
||||||
)
|
|
||||||
ifc_direction = self.file.createIfcDirection(extrusion_vector)
|
ifc_direction = self.file.createIfcDirection(extrusion_vector)
|
||||||
extruded_area = self.file.createIfcExtrudedAreaSolid(
|
extruded_area = self.file.createIfcExtrudedAreaSolid(
|
||||||
SweptArea=profile_or_curve, Position=ifc_position, ExtrudedDirection=ifc_direction, Depth=magnitude
|
SweptArea=profile_or_curve, Position=ifc_position, ExtrudedDirection=ifc_direction, Depth=magnitude
|
||||||
|
|||||||
Reference in New Issue
Block a user