fixed issue adding bend between segments located at the same point

also added some tests
This commit is contained in:
Andrej730
2023-11-14 17:18:57 +05:00
parent e8aa8c44e5
commit c6454803ee
4 changed files with 106 additions and 31 deletions
@@ -311,6 +311,7 @@ class MEPGenerator:
profile_joiner.set_depth(connected_obj, connected_element_length)
def get_segment_data(self, segment):
"""returns points data is in world space"""
ports = tool.System.get_ports(segment)
segment_object = tool.Ifc.get_object(segment)
start_point = segment_object.location
@@ -1025,7 +1026,14 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
# TODO: profile offset may need to be flipped (check transition code)
to_start_object_space = start_object_rotation.inverted()
profile_offset = (to_start_object_space @ end_point) - (to_start_object_space @ start_point)
ref_point = end_point.copy()
end_segment_dir = (second_segment_end - end_point).normalized()
# we prioritize direction between end_point and start_point for bend_vector
# if those point match we use general end segment direction
if tool.Cad.is_x((end_point - start_point).length, 0):
ref_point = end_point + end_segment_dir
bend_vector = (to_start_object_space @ ref_point) - (to_start_object_space @ start_point)
z_axis_end_object_local = to_start_object_space @ tool.Cad.get_basis_vector(end_object, 2)
def check_for_double_bends():
@@ -1049,7 +1057,7 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
)
non_lateral_axis = 0 if lateral_axes[0] == 1 else 1
non_lateral_axis_offset = profile_offset[non_lateral_axis]
non_lateral_axis_offset = bend_vector[non_lateral_axis]
if not tool.Cad.is_x(non_lateral_axis_offset, 0):
return (
None,
@@ -1075,7 +1083,7 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
angle, rotation_axis = get_bend_rotation()
lateral_sign = tool.Cad.sign(profile_offset[lateral_axis])
lateral_sign = tool.Cad.sign(bend_vector[lateral_axis])
radial_offset = V(0, 0, 0)
ref_point_radius = self.radius + profile_dim[lateral_axis]
radial_offset[lateral_axis] = ref_point_radius * (1 - cos(angle)) * lateral_sign
@@ -1083,21 +1091,19 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
end_port_offset = radial_offset + V(0, 0, self.start_length * start_segment_sign)
end_port_offset += z_axis_end_object_local * (self.end_length * -end_segment_sign)
def get_segments_extend():
segments_intersection = segments_intersection_ws - start_point
segments_intersection = to_start_object_space @ segments_intersection
def get_segments_extend_points():
# 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
start_segment_extend_point = segments_intersection_ws - start_segment_sign * (
self.start_length + required_offset
) * get_z_basis(start_object)
end_segment_extend_point = segments_intersection_ws - end_segment_sign * (
self.end_length + required_offset
) * get_z_basis(end_object)
start_extend = current_start_offset - (required_offset + self.start_length)
end_extend = current_end_offset - (required_offset + self.end_length)
return start_extend, end_extend
return start_segment_extend_point, end_segment_extend_point
def check_new_segment_length(start_point, end_point, extend_point):
"""Check if segment is placed too near to the bend point.
@@ -1117,8 +1123,7 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
return None
# 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)
start_segment_extend_point, end_segment_extend_point = get_segments_extend_points()
projection = check_new_segment_length(first_segment_start, start_point, start_segment_extend_point)
if projection is not None:
self.report(
@@ -1127,7 +1132,6 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
)
return {"ERROR"}
end_segment_extend_point = end_point + end_segment_sign * end_segment_extend * get_z_basis(end_object)
projection = check_new_segment_length(second_segment_end, end_point, end_segment_extend_point)
if projection is not None:
self.report(
@@ -1148,7 +1152,7 @@ class MEPAddBend(bpy.types.Operator, tool.Ifc.Operator):
self.end_length / si_conversion,
angle,
self.radius / si_conversion,
profile_offset / si_conversion,
bend_vector / si_conversion,
flip_z_axis=start_segment_sign == -1,
)
+62 -2
View File
@@ -530,6 +530,7 @@ Scenario: Create a MEP transition
Given an empty IFC project
And I create default MEP types
And the variable "element_types" is "[str(e.id()) for e in {ifc}.by_type('IfcDuctSegmentType')]"
And I set "scene.BIMModelProperties.relating_type_id" to "{element_types}[0]"
And I press "bim.add_constr_type_instance"
And I rename the object "IfcDuctSegment/DuctSegment" to "IfcDuctSegment/RectSegment"
@@ -542,11 +543,70 @@ Scenario: Create a MEP transition
And the object "IfcDuctSegment/CircleSegment" is moved to "2.5,0,0"
And the object "IfcDuctSegment/CircleSegment" is selected
And additionally the object "IfcDuctSegment/RectSegment" is selected
And I press "bim.mep_add_transition()"
And I press "bim.mep_add_transition"
Then the object "IfcDuctSegment/RectSegment" is at "0,0,0"
Then the object "IfcDuctFitting/DuctFitting" exists
And the object "IfcDuctFittingType/Transition" exists
And the object "IfcDuctSegment/RectSegment" is at "0,0,0"
And the object "IfcDuctSegment/RectSegment" dimensions are "0.4,0.2,2.370096"
And the object "IfcDuctSegment/CircleSegment" is at "3.1299,0,0"
And the object "IfcDuctSegment/CircleSegment" dimensions are "0.1000, 0.09927, 2.370096"
And the object "IfcDuctFitting/DuctFitting" is at "2.370096, 0.0000, 0.0000"
And the object "IfcDuctFitting/DuctFitting" dimensions are "0.4000, 0.2000, 0.759807"
Scenario: Create a MEP bend between intersecting with different locations
Given an empty IFC project
And I create default MEP types
And the variable "element_types" is "[str(e.id()) for e in {ifc}.by_type('IfcDuctSegmentType')]"
And I set "scene.BIMModelProperties.relating_type_id" to "{element_types}[0]"
And I set "scene.BIMModelProperties.extrusion_depth" to "5.0"
And I press "bim.add_constr_type_instance"
And I rename the object "IfcDuctSegment/DuctSegment" to "IfcDuctSegment/Seg1"
And I set "scene.BIMModelProperties.relating_type_id" to "{element_types}[0]"
And I press "bim.add_constr_type_instance"
And I rename the object "IfcDuctSegment/DuctSegment" to "IfcDuctSegment/Seg2"
And the object "IfcDuctSegment/Seg2" is rotated by "0,0,90" deg
And the object "IfcDuctSegment/Seg2" is moved to "6,1,1"
And the object "IfcDuctSegment/Seg1" is selected
And additionally the object "IfcDuctSegment/Seg2" is selected
And I press "bim.mep_add_bend"
Then the object "IfcDuctFitting/DuctFitting" exists
And the object "IfcDuctFittingType/Bend" exists
And the object "IfcDuctSegment/Seg1" is at "0,0,1.0"
And the object "IfcDuctSegment/Seg1" dimensions are "0.4,0.2,5.5"
And the object "IfcDuctSegment/Seg2" is at "6.0,0.5,1.0"
And the object "IfcDuctSegment/Seg2" dimensions are "0.4,0.2,5.5"
And the object "IfcDuctFitting/DuctFitting" is at "6.0, 0.5, 1.0"
And the object "IfcDuctFitting/DuctFitting" dimensions are "0.7, 0.2, 0.7"
Scenario: Create a MEP bend between intersecting segments at the same location
Given an empty IFC project
And I create default MEP types
And the variable "element_types" is "[str(e.id()) for e in {ifc}.by_type('IfcDuctSegmentType')]"
And I set "scene.BIMModelProperties.relating_type_id" to "{element_types}[0]"
And I set "scene.BIMModelProperties.extrusion_depth" to "5.0"
And I press "bim.add_constr_type_instance"
And I rename the object "IfcDuctSegment/DuctSegment" to "IfcDuctSegment/Seg1"
And I set "scene.BIMModelProperties.relating_type_id" to "{element_types}[0]"
And I press "bim.add_constr_type_instance"
And I rename the object "IfcDuctSegment/DuctSegment" to "IfcDuctSegment/Seg2"
And the object "IfcDuctSegment/Seg2" is rotated by "0,0,90" deg
And the object "IfcDuctSegment/Seg1" is selected
And additionally the object "IfcDuctSegment/Seg2" is selected
And I press "bim.mep_add_bend"
Then the object "IfcDuctFitting/DuctFitting" exists
And the object "IfcDuctFittingType/Bend" exists
And the object "IfcDuctSegment/Seg1" is at "0.5,0,1.0"
And the object "IfcDuctSegment/Seg1" dimensions are "0.4,0.2,4.5"
And the object "IfcDuctSegment/Seg2" is at "0.0,0.5,1.0"
And the object "IfcDuctSegment/Seg2" dimensions are "0.4,0.2,4.5"
And the object "IfcDuctFitting/DuctFitting" is at "0.0, 0.5, 1.0"
And the object "IfcDuctFitting/DuctFitting" dimensions are "0.7, 0.2, 0.7"
+14 -3
View File
@@ -28,6 +28,7 @@ from blenderbim.tool.brick import BrickStore
from blenderbim.bim.module.model.data import AuthoringData
from pytest_bdd import scenarios, given, when, then, parsers
from mathutils import Vector
from math import radians
scenarios("feature")
@@ -241,13 +242,23 @@ def then_the_object_name_is_selected(name):
assert obj in bpy.context.selected_objects
@given(parsers.parse('the object "{name}" is rotated by "{rotation_deg}" deg'))
@when(parsers.parse('the object "{name}" is rotated by "{rotation_deg}" deg'))
def the_object_name_is_rotated_by(name, rotation_deg):
rotation_deg = [radians(float(rot)) for rot in rotation_deg.split(",")]
obj = the_object_name_exists(name)
obj.rotation_euler[0] += rotation_deg[0]
obj.rotation_euler[1] += rotation_deg[1]
obj.rotation_euler[2] += rotation_deg[2]
bpy.context.view_layer.update() # make sure matrix is updated
@given(parsers.parse('the object "{name}" is moved to "{location}"'))
@when(parsers.parse('the object "{name}" is moved to "{location}"'))
def the_object_name_is_moved_to_location(name, location):
location = [float(co) for co in location.split(",")]
the_object_name_exists(name).matrix_world[0][3] = location[0]
the_object_name_exists(name).matrix_world[1][3] = location[1]
the_object_name_exists(name).matrix_world[2][3] = location[2]
obj = the_object_name_exists(name)
obj.matrix_world.translation = location
@given(parsers.parse('the object "{name}" is scaled to "{scale}"'))
@@ -1172,10 +1172,9 @@ class ShapeBuilder:
"""
print = lambda *args, **kwargs: __builtins__["print"](*args, **kwargs) if verbose else None
# offsets tend to have bunch of float point garbage
# vectors tend to have bunch of float point garbage
# that can result in errors when we're calculating value for square root below
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(self.file)
offset = round_vector_to_precision(profile_offset, si_conversion)
offset = round_vector_to_precision(profile_offset, 1)
diff = start_half_dim.xy - end_half_dim.xy
diff = Vector([abs(i) for i in diff])
@@ -1320,7 +1319,7 @@ class ShapeBuilder:
end_length: float,
angle: float,
radius: float,
profile_offset: Vector,
bend_vector: Vector,
flip_z_axis: bool,
):
"""
@@ -1332,8 +1331,9 @@ class ShapeBuilder:
:param type: float
: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 seconn bend axis and it's direction.
: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),
the actual magnitude of the vector is not important (though near zero values will be ignored).
: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.
@@ -1359,10 +1359,10 @@ class ShapeBuilder:
is_circular_profile = profile.is_a("IfcCircleProfileDef")
profile_dim = get_dim(profile, start_length)
rounded_offset = round_vector_to_precision(profile_offset, si_conversion)
lateral_axis = next(i for i in range(2) if not is_x(rounded_offset[i], 0))
rounded_bend_vector = round_vector_to_precision(bend_vector, si_conversion)
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
lateral_sign = sign(profile_offset[lateral_axis])
lateral_sign = sign(bend_vector[lateral_axis])
z_sign = -1 if flip_z_axis else 1
rep_items = []