mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-19 14:41:25 +00:00
MEP bends - more robust start/end points detection
also support for bends with angles > 90 degrees and some other bugs fixed
This commit is contained in:
@@ -35,7 +35,7 @@ import blenderbim.core.type
|
|||||||
import blenderbim.core.root
|
import blenderbim.core.root
|
||||||
import blenderbim.core.geometry
|
import blenderbim.core.geometry
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from math import pi, degrees, radians, sin, cos, asin
|
from math import pi, degrees, radians, sin, cos, asin, tan
|
||||||
from copy import copy
|
from copy import copy
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
from ifcopenshell.util.shape_builder import ShapeBuilder
|
from ifcopenshell.util.shape_builder import ShapeBuilder
|
||||||
@@ -973,10 +973,20 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
end_segment_data["start_point"]: end_segment_data["start_port"],
|
end_segment_data["start_point"]: end_segment_data["start_port"],
|
||||||
end_segment_data["end_point"]: end_segment_data["end_port"],
|
end_segment_data["end_point"]: end_segment_data["end_port"],
|
||||||
}
|
}
|
||||||
(start_point, end_point), (first_segment_start, second_segment_end) = tool.Cad.closest_points(
|
|
||||||
(start_segment_data["start_point"], start_segment_data["end_point"]),
|
get_z_basis = lambda o: o.matrix_world.col[2].normalized().to_3d()
|
||||||
(end_segment_data["start_point"], end_segment_data["end_point"]),
|
segments_intersection_ws = tool.Cad.intersect_edges(
|
||||||
|
(start_object.location, start_object.location + get_z_basis(start_object)),
|
||||||
|
(end_object.location, end_object.location + get_z_basis(end_object)),
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
start_point, first_segment_start = tool.Cad.closest_and_furthest_vectors(
|
||||||
|
segments_intersection_ws, (start_segment_data["start_point"], start_segment_data["end_point"])
|
||||||
)
|
)
|
||||||
|
end_point, second_segment_end = tool.Cad.closest_and_furthest_vectors(
|
||||||
|
segments_intersection_ws, (end_segment_data["start_point"], end_segment_data["end_point"])
|
||||||
|
)
|
||||||
|
|
||||||
start_port = points_ports_map[start_point]
|
start_port = points_ports_map[start_point]
|
||||||
end_port = points_ports_map[end_point]
|
end_port = points_ports_map[end_point]
|
||||||
start_point_on_origin = start_point == start_segment_data["start_point"]
|
start_point_on_origin = start_point == start_segment_data["start_point"]
|
||||||
@@ -1032,7 +1042,6 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
O = V(0, 0, 0)
|
O = V(0, 0, 0)
|
||||||
get_z_basis = lambda o: o.matrix_world.col[2].normalized().to_3d()
|
|
||||||
angle = tool.Cad.angle_edges((get_z_basis(start_object), O), (get_z_basis(end_object), O))
|
angle = tool.Cad.angle_edges((get_z_basis(start_object), O), (get_z_basis(end_object), O))
|
||||||
|
|
||||||
lateral_sign = tool.Cad.sign(profile_offset[lateral_axis])
|
lateral_sign = tool.Cad.sign(profile_offset[lateral_axis])
|
||||||
@@ -1042,18 +1051,18 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
radial_offset.z = ref_point_radius * sin(angle)
|
radial_offset.z = ref_point_radius * sin(angle)
|
||||||
|
|
||||||
def get_segments_extend():
|
def get_segments_extend():
|
||||||
end_segment_z_local = to_start_object_space @ get_z_basis(end_object)
|
segments_intersection = segments_intersection_ws - start_point
|
||||||
segments_intersection = tool.Cad.intersect_edges(
|
segments_intersection = to_start_object_space @ segments_intersection
|
||||||
(V(0, 0, 1), V(0, 0, 0)), (profile_offset + end_segment_z_local, profile_offset)
|
|
||||||
)[0]
|
|
||||||
|
|
||||||
curent_start_offset = segments_intersection.length
|
# since tangent segments are equal
|
||||||
required_start_offset = abs(radial_offset.z)
|
# if drawn for the circle from the same point
|
||||||
|
required_offset = ref_point_radius * tan(angle / 2)
|
||||||
|
|
||||||
|
current_start_offset = segments_intersection.length
|
||||||
current_end_offset = (segments_intersection - profile_offset).length
|
current_end_offset = (segments_intersection - profile_offset).length
|
||||||
required_end_offset = abs(radial_offset[lateral_axis])
|
|
||||||
|
|
||||||
start_extend = curent_start_offset - required_start_offset
|
start_extend = current_start_offset - required_offset
|
||||||
end_extend = current_end_offset - required_end_offset
|
end_extend = current_end_offset - required_offset
|
||||||
|
|
||||||
return start_extend, end_extend
|
return start_extend, end_extend
|
||||||
|
|
||||||
@@ -1076,7 +1085,6 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
# adjust segments to fit the radius and angle
|
# adjust segments to fit the radius and angle
|
||||||
start_segment_extend, end_segment_extend = get_segments_extend()
|
start_segment_extend, end_segment_extend = get_segments_extend()
|
||||||
|
|
||||||
start_segment_extend_point = start_point + start_segment_sign * start_segment_extend * get_z_basis(start_object)
|
start_segment_extend_point = start_point + start_segment_sign * start_segment_extend * get_z_basis(start_object)
|
||||||
projection = check_new_segment_length(first_segment_start, start_point, start_segment_extend_point)
|
projection = check_new_segment_length(first_segment_start, start_point, start_segment_extend_point)
|
||||||
if projection is not None:
|
if projection is not None:
|
||||||
@@ -1108,6 +1116,7 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
angle,
|
angle,
|
||||||
self.radius / si_conversion,
|
self.radius / si_conversion,
|
||||||
profile_offset / si_conversion,
|
profile_offset / si_conversion,
|
||||||
|
flip_z_axis=start_segment_sign == -1,
|
||||||
)
|
)
|
||||||
|
|
||||||
bpy.ops.bim.create_shape_from_step_id(step_id=rep.id(), should_include_curves=True)
|
bpy.ops.bim.create_shape_from_step_id(step_id=rep.id(), should_include_curves=True)
|
||||||
|
|||||||
@@ -1367,9 +1367,7 @@ class DumbWallJoiner:
|
|||||||
clamp_axis = clamp_axis[::-1]
|
clamp_axis = clamp_axis[::-1]
|
||||||
vectors = tuple([clamp_point_by_direction(v, clamp_axis) for v in vectors])
|
vectors = tuple([clamp_point_by_direction(v, clamp_axis) for v in vectors])
|
||||||
|
|
||||||
closest = tool.Cad.closest_vector(ref_point_2d.to_3d(), vectors)
|
return tool.Cad.closest_and_furthest_vectors(ref_point_2d.to_3d(), vectors)
|
||||||
farthest = vectors[1] if closest == vectors[0] else vectors[0]
|
|
||||||
return closest, farthest
|
|
||||||
|
|
||||||
bbn, bbf = get_closest_and_furthest_vectors(axis1["base"][i], (bb1, bb2), bba1)
|
bbn, bbf = get_closest_and_furthest_vectors(axis1["base"][i], (bb1, bb2), bba1)
|
||||||
bsn, bsf = get_closest_and_furthest_vectors(axis1["side"][i], (bs1, bs2))
|
bsn, bsf = get_closest_and_furthest_vectors(axis1["side"][i], (bs1, bs2))
|
||||||
|
|||||||
@@ -192,6 +192,18 @@ class Cad:
|
|||||||
distance_test = (v1 - pt).length >= (v2 - pt).length
|
distance_test = (v1 - pt).length >= (v2 - pt).length
|
||||||
return v1 if distance_test else v2
|
return v1 if distance_test else v2
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def closest_and_furthest_vectors(cls, pt, e):
|
||||||
|
"""
|
||||||
|
> pt: vector
|
||||||
|
> e: 2 vector tuple
|
||||||
|
< returns the two vectors closest to and furthest from pt.
|
||||||
|
"""
|
||||||
|
if isinstance(e, tuple) and all([isinstance(co, Vector) for co in e]):
|
||||||
|
closest = cls.closest_vector(pt, e)
|
||||||
|
furthest = e[1] if closest == e[0] else e[0]
|
||||||
|
return closest, furthest
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def coords_tuple_from_edge_idx(cls, bm, idx):
|
def coords_tuple_from_edge_idx(cls, bm, idx):
|
||||||
"""bm is a bmesh representation"""
|
"""bm is a bmesh representation"""
|
||||||
|
|||||||
@@ -1278,7 +1278,14 @@ class ShapeBuilder:
|
|||||||
return angle
|
return angle
|
||||||
|
|
||||||
def mep_bend_shape(
|
def mep_bend_shape(
|
||||||
self, segment, start_length: float, end_length: float, angle: float, radius: float, profile_offset: Vector
|
self,
|
||||||
|
segment,
|
||||||
|
start_length: float,
|
||||||
|
end_length: float,
|
||||||
|
angle: float,
|
||||||
|
radius: float,
|
||||||
|
profile_offset: Vector,
|
||||||
|
flip_z_axis: bool,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -1290,9 +1297,11 @@ class ShapeBuilder:
|
|||||||
:param radius: bend radius
|
:param radius: bend radius
|
||||||
:param type: float
|
:param type: float
|
||||||
:param profile_offset: offset between start and end segments in local space of start segment
|
:param profile_offset: offset between start and end segments in local space of start segment
|
||||||
used mainly to determine the bend axes and their direction.
|
used mainly to determine the seconn bend axis and it's direction.
|
||||||
Values themselves are replaced by the radius.
|
|
||||||
:param type: Vector
|
:param type: Vector
|
||||||
|
: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.
|
||||||
|
:param type: bool
|
||||||
|
|
||||||
:return: tuple of Model/Body/MODEL_VIEW IfcRepresentation and transition shape data
|
:return: tuple of Model/Body/MODEL_VIEW IfcRepresentation and transition shape data
|
||||||
"""
|
"""
|
||||||
@@ -1318,7 +1327,7 @@ class ShapeBuilder:
|
|||||||
lateral_axis = next(i for i in range(2) if not is_x(rounded_offset[i], 0))
|
lateral_axis = next(i for i in range(2) if not is_x(rounded_offset[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(profile_offset[lateral_axis])
|
lateral_sign = sign(profile_offset[lateral_axis])
|
||||||
z_sign = sign(profile_offset.z)
|
z_sign = -1 if flip_z_axis else 1
|
||||||
|
|
||||||
rep_items = []
|
rep_items = []
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user