mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 02:31:09 +00:00
mathutils deprecation - mep_bend_shape #5192
This commit is contained in:
@@ -32,7 +32,6 @@ from itertools import chain
|
|||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
V = lambda *x: Vector([float(i) for i in x])
|
V = lambda *x: Vector([float(i) for i in x])
|
||||||
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]]
|
||||||
@@ -1649,110 +1648,117 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
def mep_bend_shape(
|
def mep_bend_shape(
|
||||||
self,
|
self,
|
||||||
segment,
|
segment: ifcopenshell.entity_instance,
|
||||||
start_length: float,
|
start_length: float,
|
||||||
end_length: float,
|
end_length: float,
|
||||||
angle: float,
|
angle: float,
|
||||||
radius: float,
|
radius: float,
|
||||||
bend_vector: Vector,
|
bend_vector: VectorType,
|
||||||
flip_z_axis: bool,
|
flip_z_axis: bool,
|
||||||
) -> ifcopenshell.entity_instance:
|
) -> tuple[ifcopenshell.entity_instance, dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
|
Generate a MEP bend shape for the provided segments.
|
||||||
|
|
||||||
:param segment: IfcFlowSegment for a bend.
|
:param segment: IfcFlowSegment for a bend.
|
||||||
Note that for a bend start and end segments types should match.
|
Note that for a bend start and end segments types should match.
|
||||||
:type segment: ifcopenshell.entity_instance
|
|
||||||
:param angle: bend angle, in radians
|
:param angle: bend angle, in radians
|
||||||
:type angle: float
|
|
||||||
:param radius: bend radius
|
:param radius: bend radius
|
||||||
:type radius: float
|
|
||||||
:param bend_vector: offset between start and end segments in local space of start segment
|
:param bend_vector: offset between start and end segments in local space of start segment
|
||||||
used mainly to determine the second bend axis and it's direction (positive or negative),
|
used mainly to determine the second bend axis and it's direction (positive or negative),
|
||||||
the actual magnitude of the vector is not important (though near zero values will be ignored).
|
the actual magnitude of the vector is not important (though near zero values will be ignored).
|
||||||
:type bend_vector: Vector
|
|
||||||
:param flip_z_axis: since we cannot determine z axis direction from the profile offset,
|
:param flip_z_axis: since we cannot determine z axis direction from the profile offset,
|
||||||
there is an option to flip it if bend is going by start segment Z- axis.
|
there is an option to flip it if bend is going by start segment Z- axis.
|
||||||
:type flip_z_axis: bool
|
:return: tuple of Model/Body/MODEL_VIEW IfcRepresentation and dictionary of transition shape data
|
||||||
|
|
||||||
:return: tuple of Model/Body/MODEL_VIEW IfcRepresentation and transition shape data
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_profile(element):
|
def get_profile(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||||
if material and material.is_a("IfcMaterialProfileSet") and len(material.MaterialProfiles) == 1:
|
if material and material.is_a("IfcMaterialProfileSet") and len(material.MaterialProfiles) == 1:
|
||||||
return material.MaterialProfiles[0].Profile
|
return material.MaterialProfiles[0].Profile
|
||||||
|
|
||||||
def get_dim(profile, depth):
|
def get_dim(profile: ifcopenshell.entity_instance, depth: float) -> Union[np.ndarray, None]:
|
||||||
if profile.is_a("IfcRectangleProfileDef"):
|
if profile.is_a("IfcRectangleProfileDef"):
|
||||||
return V(profile.XDim / 2, profile.YDim / 2, depth)
|
return np.array([profile.XDim / 2, profile.YDim / 2, depth])
|
||||||
elif profile.is_a("IfcCircleProfileDef"):
|
elif profile.is_a("IfcCircleProfileDef"):
|
||||||
return V(profile.Radius, profile.Radius, depth)
|
return np.array([profile.Radius, profile.Radius, depth])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
np_Z = 2
|
||||||
|
|
||||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(self.file)
|
||||||
profile = get_profile(segment)
|
profile = get_profile(segment)
|
||||||
|
assert profile
|
||||||
is_circular_profile = profile.is_a("IfcCircleProfileDef")
|
is_circular_profile = profile.is_a("IfcCircleProfileDef")
|
||||||
profile_dim = get_dim(profile, start_length)
|
profile_dim = get_dim(profile, start_length)
|
||||||
|
assert profile_dim is not None
|
||||||
|
|
||||||
rounded_bend_vector = round_vector_to_precision(bend_vector, si_conversion)
|
rounded_bend_vector = np_round_to_precision(bend_vector, si_conversion)
|
||||||
lateral_axis = next(i for i in range(2) if not is_x(rounded_bend_vector[i], 0))
|
lateral_axis = next(i for i in range(2) if not is_x(rounded_bend_vector[i], 0))
|
||||||
non_lateral_axis = 1 if lateral_axis == 0 else 0
|
non_lateral_axis = 1 if lateral_axis == 0 else 0
|
||||||
lateral_sign = sign(bend_vector[lateral_axis])
|
lateral_sign = np.sign(bend_vector[lateral_axis])
|
||||||
z_sign = -1 if flip_z_axis else 1
|
z_sign = -1 if flip_z_axis else 1
|
||||||
|
|
||||||
rep_items = []
|
rep_items: list[ifcopenshell.entity_instance] = []
|
||||||
|
|
||||||
# bend circle center
|
# bend circle center
|
||||||
O = V(0, 0, 0)
|
O = np.zeros(3)
|
||||||
O[lateral_axis] = (radius + profile_dim[lateral_axis]) * lateral_sign
|
O[lateral_axis] = (radius + profile_dim[lateral_axis]) * lateral_sign
|
||||||
theta = angle
|
theta = angle
|
||||||
|
|
||||||
def get_circle_point(angle, radius):
|
def get_circle_points(angles: np.ndarray, radius: float) -> np.ndarray:
|
||||||
point = V(0, 0, 0)
|
"""
|
||||||
angle -= pi / 2
|
:param angles: Angles, in radians.
|
||||||
|
"""
|
||||||
|
angles = angles - pi / 2
|
||||||
|
points = np.zeros((len(angles), 3))
|
||||||
# fmt: off
|
# fmt: off
|
||||||
point.z = z_sign * cos(angle) * radius
|
points[:, np_Z] = z_sign * np.cos(angles) * radius
|
||||||
point[lateral_axis] = lateral_sign * sin(angle) * radius
|
points[:, lateral_axis] = lateral_sign * np.sin(angles) * radius
|
||||||
# fmt: on
|
# fmt: on
|
||||||
return point
|
return points
|
||||||
|
|
||||||
def get_circle_tangent(angle):
|
def get_circle_tangent(angle: float) -> np.ndarray:
|
||||||
tangent = V(0, 0, 0)
|
"""
|
||||||
tangent.z = cos(angle) * z_sign
|
:param angle: Angle, in radians.
|
||||||
|
:return: Tangent vector.
|
||||||
|
"""
|
||||||
|
tangent = np.zeros(3)
|
||||||
|
tangent[np_Z] = cos(angle) * z_sign
|
||||||
tangent[lateral_axis] = sin(angle) * lateral_sign
|
tangent[lateral_axis] = sin(angle) * lateral_sign
|
||||||
return tangent
|
return tangent
|
||||||
|
|
||||||
def get_bend_representation_item():
|
def get_bend_representation_item() -> ifcopenshell.entity_instance:
|
||||||
r = radius
|
r = radius
|
||||||
theta_segments = [0, theta / 2, theta]
|
theta_segments = np.array([0.0, theta / 2, theta])
|
||||||
|
points: np.ndarray
|
||||||
if is_circular_profile:
|
if is_circular_profile:
|
||||||
r += profile_dim[lateral_axis]
|
r += profile_dim[lateral_axis]
|
||||||
points = [get_circle_point(cur_theta, r) for cur_theta in theta_segments]
|
points = get_circle_points(theta_segments, r)
|
||||||
arc_points = (1,)
|
arc_points = (1,)
|
||||||
else:
|
else:
|
||||||
outer_r = r + 2 * profile_dim[lateral_axis]
|
outer_r = r + 2 * profile_dim[lateral_axis]
|
||||||
outer_points = [get_circle_point(cur_theta, outer_r) for cur_theta in theta_segments[::-1]]
|
outer_points = get_circle_points(theta_segments[::-1], outer_r)
|
||||||
if is_x(r, 0):
|
if is_x(r, 0):
|
||||||
points = [get_circle_point(theta, r)] + outer_points
|
points = get_circle_points(np.full(1, theta), r)
|
||||||
|
points = np.vstack((points, outer_points))
|
||||||
arc_points = (2,)
|
arc_points = (2,)
|
||||||
else:
|
else:
|
||||||
inner_points = [get_circle_point(cur_theta, r) for cur_theta in theta_segments]
|
inner_points = get_circle_points(theta_segments, r)
|
||||||
points = inner_points + outer_points
|
points = np.vstack((inner_points, outer_points))
|
||||||
arc_points = (1, 4)
|
arc_points = (1, 4)
|
||||||
|
|
||||||
points = [p + O for p in points]
|
points += O
|
||||||
offset = V(0, 0, 0)
|
offset = np.zeros(3)
|
||||||
offset.z = z_sign * start_length
|
offset[np_Z] = z_sign * start_length
|
||||||
|
|
||||||
if is_circular_profile:
|
if is_circular_profile:
|
||||||
bend_path = self.polyline(points, closed=False, arc_points=arc_points, position_offset=offset)
|
bend_path = self.polyline(points, closed=False, arc_points=arc_points, position_offset=offset)
|
||||||
bend = self.create_swept_disk_solid(bend_path, profile_dim[lateral_axis])
|
bend = self.create_swept_disk_solid(bend_path, profile_dim[lateral_axis])
|
||||||
else:
|
else:
|
||||||
main_axes = lambda v: getattr(v, "xy"[lateral_axis] + "z")
|
|
||||||
offset[non_lateral_axis] = -profile_dim[non_lateral_axis]
|
offset[non_lateral_axis] = -profile_dim[non_lateral_axis]
|
||||||
|
|
||||||
extrusion_kwargs = self.extrude_kwargs("XY"[non_lateral_axis])
|
extrusion_kwargs = self.extrude_kwargs("XY"[non_lateral_axis])
|
||||||
profile_curve = self.polyline([main_axes(p) for p in points], arc_points=arc_points, closed=True)
|
polyline_points = points[:, [lateral_axis, np_Z]]
|
||||||
|
profile_curve = self.polyline(polyline_points, arc_points=arc_points, closed=True)
|
||||||
bend = self.extrude(
|
bend = self.extrude(
|
||||||
self.profile(profile_curve), profile_dim[non_lateral_axis] * 2, position=offset, **extrusion_kwargs
|
self.profile(profile_curve), profile_dim[non_lateral_axis] * 2, position=offset, **extrusion_kwargs
|
||||||
)
|
)
|
||||||
@@ -1760,28 +1766,29 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
rep_items.append(get_bend_representation_item())
|
rep_items.append(get_bend_representation_item())
|
||||||
if start_length:
|
if start_length:
|
||||||
rep_items.append(self.extrude(profile, start_length, extrusion_vector=V(0, 0, z_sign)))
|
rep_items.append(self.extrude(profile, start_length, extrusion_vector=(0, 0, z_sign)))
|
||||||
if end_length:
|
if end_length:
|
||||||
end_position = O + get_circle_point(theta, radius + profile_dim[lateral_axis])
|
end_position = O + get_circle_points(np.full(1, theta), radius + profile_dim[lateral_axis])[0]
|
||||||
end_position.z += start_length * z_sign
|
end_position[np_Z] += start_length * z_sign
|
||||||
|
|
||||||
# define extrusion space for the segment after the bend
|
# define extrusion space for the segment after the bend
|
||||||
z_axis = get_circle_tangent(theta)
|
z_axis = get_circle_tangent(theta)
|
||||||
extrude_kwargs = {
|
extrude_kwargs = {
|
||||||
"position_z_axis": z_axis,
|
"position_z_axis": z_axis,
|
||||||
"extrusion_vector": Vector((0, 0, 1)),
|
"extrusion_vector": (0, 0, 1),
|
||||||
}
|
}
|
||||||
# since we are sure that tangent involves only two axis
|
# since we are sure that tangent involves only two axis
|
||||||
# it's safe to assume that non lateral axis is untouched
|
# it's safe to assume that non lateral axis is untouched
|
||||||
if lateral_axis == 0:
|
if lateral_axis == 0:
|
||||||
x_axis = z_axis.cross(Vector((0, 1, 0)))
|
x_axis = np.cross(z_axis, (0, 1, 0))
|
||||||
else:
|
else:
|
||||||
x_axis = Vector((1, 0, 0))
|
x_axis = (1, 0, 0)
|
||||||
extrude_kwargs["position_x_axis"] = x_axis
|
extrude_kwargs["position_x_axis"] = x_axis
|
||||||
|
|
||||||
rep_items.append(self.extrude(profile, end_length, end_position, **extrude_kwargs))
|
rep_items.append(self.extrude(profile, end_length, end_position, **extrude_kwargs))
|
||||||
|
|
||||||
body = ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW")
|
body = ifcopenshell.util.representation.get_context(self.file, "Model", "Body", "MODEL_VIEW")
|
||||||
|
assert body
|
||||||
rep = self.get_representation(body, rep_items)
|
rep = self.get_representation(body, rep_items)
|
||||||
|
|
||||||
bend_data = {
|
bend_data = {
|
||||||
|
|||||||
Reference in New Issue
Block a user