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.geometry
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 mathutils import Vector, Matrix
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["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"]),
(end_segment_data["start_point"], end_segment_data["end_point"]),
get_z_basis = lambda o: o.matrix_world.col[2].normalized().to_3d()
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]
end_port = points_ports_map[end_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"}
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))
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)
def get_segments_extend():
end_segment_z_local = to_start_object_space @ get_z_basis(end_object)
segments_intersection = tool.Cad.intersect_edges(
(V(0, 0, 1), V(0, 0, 0)), (profile_offset + end_segment_z_local, profile_offset)
)[0]
segments_intersection = segments_intersection_ws - start_point
segments_intersection = to_start_object_space @ segments_intersection
curent_start_offset = segments_intersection.length
required_start_offset = abs(radial_offset.z)
# since tangent segments are equal
# 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
required_end_offset = abs(radial_offset[lateral_axis])
start_extend = curent_start_offset - required_start_offset
end_extend = current_end_offset - required_end_offset
start_extend = current_start_offset - required_offset
end_extend = current_end_offset - required_offset
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
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)
projection = check_new_segment_length(first_segment_start, start_point, start_segment_extend_point)
if projection is not None:
@@ -1108,6 +1116,7 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
angle,
self.radius / 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)
@@ -1367,9 +1367,7 @@ class DumbWallJoiner:
clamp_axis = clamp_axis[::-1]
vectors = tuple([clamp_point_by_direction(v, clamp_axis) for v in vectors])
closest = tool.Cad.closest_vector(ref_point_2d.to_3d(), vectors)
farthest = vectors[1] if closest == vectors[0] else vectors[0]
return closest, farthest
return tool.Cad.closest_and_furthest_vectors(ref_point_2d.to_3d(), vectors)
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))
+12
View File
@@ -192,6 +192,18 @@ class Cad:
distance_test = (v1 - pt).length >= (v2 - pt).length
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
def coords_tuple_from_edge_idx(cls, bm, idx):
"""bm is a bmesh representation"""
@@ -1278,7 +1278,14 @@ class ShapeBuilder:
return angle
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 type: float
: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.
Values themselves are replaced by the radius.
used mainly to determine the seconn bend axis and it's direction.
: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
"""
@@ -1318,7 +1327,7 @@ class ShapeBuilder:
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
lateral_sign = sign(profile_offset[lateral_axis])
z_sign = sign(profile_offset.z)
z_sign = -1 if flip_z_axis else 1
rep_items = []