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:
Andrej730
2023-09-11 10:29:25 +05:00
parent 098ae2b69f
commit e93ff7a6b1
4 changed files with 50 additions and 22 deletions
@@ -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))
+12
View File
@@ -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 = []