mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 15:08:51 +00:00
fixed bug in mep transitions
it wasn't taking into account that offset can be too big to sustain the angle
This commit is contained in:
@@ -1106,13 +1106,16 @@ class ShapeBuilder:
|
|||||||
|
|
||||||
def check_transition(end_profile=False):
|
def check_transition(end_profile=False):
|
||||||
length = self.mep_transition_calculate(**calculation_arguments, angle=angle, end_profile=end_profile)
|
length = self.mep_transition_calculate(**calculation_arguments, angle=angle, end_profile=end_profile)
|
||||||
|
if length is None:
|
||||||
|
return
|
||||||
|
|
||||||
other_side_angle = self.mep_transition_calculate(
|
other_side_angle = self.mep_transition_calculate(
|
||||||
**calculation_arguments, length=length, end_profile=not end_profile
|
**calculation_arguments, length=length, end_profile=not end_profile
|
||||||
)
|
)
|
||||||
|
|
||||||
# NOTE: for now we just hardcode the good value for that case
|
# NOTE: for now we just hardcode the good value for that case
|
||||||
same_dimensions = is_x(diff.length, 0)
|
same_dimension = is_x(diff.y if not end_profile else diff.x, 0)
|
||||||
if same_dimensions and is_x(offset.y if not end_profile else offset.x, 0):
|
if same_dimension and is_x(offset.y if not end_profile else offset.x, 0):
|
||||||
requested_angle = 90
|
requested_angle = 90
|
||||||
else:
|
else:
|
||||||
requested_angle = angle
|
requested_angle = angle
|
||||||
@@ -1142,17 +1145,24 @@ class ShapeBuilder:
|
|||||||
if end_profile:
|
if end_profile:
|
||||||
diff, offset = diff.yx, offset.yx
|
diff, offset = diff.yx, offset.yx
|
||||||
|
|
||||||
same_dimensions = is_x(diff.length, 0)
|
same_dimension = is_x(diff.x, 0)
|
||||||
|
|
||||||
a = diff.x + offset.x
|
a = diff.x + offset.x
|
||||||
b = diff.x - offset.x
|
b = diff.x - offset.x
|
||||||
if length is None:
|
if length is None:
|
||||||
if not same_dimensions:
|
if not same_dimension:
|
||||||
if diff.x == 0:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
t = tan(radians(angle))
|
t = tan(radians(angle))
|
||||||
h = (a + b + sqrt(a**2 + 4 * a * b * t**2 + 2 * a * b + b**2)) / (2 * t)
|
h0 = a**2 + 4 * a * b * t**2 + 2 * a * b + b**2
|
||||||
|
# TODO: we might need to specify the exact failing cases in the future
|
||||||
|
if h0 < 0:
|
||||||
|
print(
|
||||||
|
f"B. Coulndn't calculate transition length for angle = {angle}, offset = {offset}, diff = {diff}"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
h = (a + b + sqrt(h0)) / (2 * t)
|
||||||
|
if h < abs(offset.y) or is_x(h, offset.y):
|
||||||
|
print(f"B. angle = {angle} requires h = {h} which is not possible with y offset = {offset.y}")
|
||||||
|
return None
|
||||||
length = sqrt(h**2 - offset.y**2)
|
length = sqrt(h**2 - offset.y**2)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
@@ -1168,6 +1178,9 @@ class ShapeBuilder:
|
|||||||
if is_x(offset.x, 0):
|
if is_x(offset.x, 0):
|
||||||
angle = 90 # NOTE: for now we just hardcode the good value for that case
|
angle = 90 # NOTE: for now we just hardcode the good value for that case
|
||||||
h = start_half_dim.x / tan(radians(angle / 2))
|
h = start_half_dim.x / tan(radians(angle / 2))
|
||||||
|
if h < abs(offset.y) or is_x(h, offset.y):
|
||||||
|
print(f"B. angle = {angle} requires h = {h} which is not possible with y offset = {offset.y}")
|
||||||
|
return None
|
||||||
length = sqrt(h**2 - offset.y**2)
|
length = sqrt(h**2 - offset.y**2)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
@@ -1178,6 +1191,9 @@ class ShapeBuilder:
|
|||||||
print(f"B. length = {length}, requested angle = {angle}, tested angle = {tested_angle}")
|
print(f"B. length = {length}, requested angle = {angle}, tested angle = {tested_angle}")
|
||||||
else:
|
else:
|
||||||
h = offset.x / tan(radians(angle))
|
h = offset.x / tan(radians(angle))
|
||||||
|
if h < abs(offset.y) or is_x(h, offset.y):
|
||||||
|
print(f"C. angle = {angle} requires h = {h} which is not possible with y offset = {offset.y}")
|
||||||
|
return None
|
||||||
length = sqrt(h**2 - offset.y**2)
|
length = sqrt(h**2 - offset.y**2)
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
@@ -1192,13 +1208,14 @@ class ShapeBuilder:
|
|||||||
return length
|
return length
|
||||||
|
|
||||||
elif angle is None:
|
elif angle is None:
|
||||||
if not same_dimensions:
|
if not same_dimension:
|
||||||
if length == 0:
|
if length == 0:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
h = sqrt(length**2 + offset.y**2)
|
h = sqrt(length**2 + offset.y**2)
|
||||||
t = -h * (a + b) / (a * b - h**2)
|
t = -h * (a + b) / (a * b - h**2)
|
||||||
angle = degrees(atan(t))
|
angle = degrees(atan(t))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
h = sqrt(length**2 + offset.y**2)
|
h = sqrt(length**2 + offset.y**2)
|
||||||
if is_x(offset.x, 0):
|
if is_x(offset.x, 0):
|
||||||
|
|||||||
@@ -34,14 +34,19 @@ class TestCalculateTransitions(test.bootstrap.IFC4):
|
|||||||
angle = params["angle"]
|
angle = params["angle"]
|
||||||
|
|
||||||
calculated_length = self.builder.mep_transition_calculate(**params)
|
calculated_length = self.builder.mep_transition_calculate(**params)
|
||||||
|
if length is None:
|
||||||
|
assert calculated_length is None
|
||||||
|
return
|
||||||
|
|
||||||
assert is_x(calculated_length, length)
|
assert is_x(calculated_length, length)
|
||||||
|
|
||||||
# angle confirmation methods:
|
# angle confirmation methods:
|
||||||
# A - between two profiles of different dimensions
|
# A - between two profiles of different dimensions
|
||||||
# B - between two profiles of same dimensions, no offset by x
|
# B - between two profiles of same dimensions, no offset by x
|
||||||
# C - between two profiles of same dimensions, has offset by x
|
# C - between two profiles of same dimensions, has offset by x
|
||||||
same_dimensions = is_x((start_half_dim.xy - end_half_dim.xy).length, 0)
|
diff = start_half_dim.xy - end_half_dim.xy
|
||||||
if not same_dimensions:
|
same_dimension = is_x(diff.x if not end_profile else diff.y, 0)
|
||||||
|
if not same_dimension:
|
||||||
confirmation_method = "A"
|
confirmation_method = "A"
|
||||||
else:
|
else:
|
||||||
confirmation_method = "B" if is_x(offset.x, 0) else "C"
|
confirmation_method = "B" if is_x(offset.x, 0) else "C"
|
||||||
@@ -134,3 +139,26 @@ class TestCalculateTransitions(test.bootstrap.IFC4):
|
|||||||
"verbose": True,
|
"verbose": True,
|
||||||
}
|
}
|
||||||
self.calculate_and_test(params, 165.83124)
|
self.calculate_and_test(params, 165.83124)
|
||||||
|
|
||||||
|
def test_mep_transition_y_offset_too_big(self):
|
||||||
|
self.builder = ShapeBuilder(self.file)
|
||||||
|
|
||||||
|
# method A
|
||||||
|
params = {
|
||||||
|
"start_half_dim": V(100, 50, 0),
|
||||||
|
"end_half_dim": V(50, 100, 0),
|
||||||
|
# offset.y > h - 190 > 186.6
|
||||||
|
"offset": V(0, 190),
|
||||||
|
"end_profile": False,
|
||||||
|
"angle": 30,
|
||||||
|
"verbose": True,
|
||||||
|
}
|
||||||
|
self.calculate_and_test(params, None)
|
||||||
|
|
||||||
|
# method B
|
||||||
|
params["end_half_dim"] = V(100, 100, 0)
|
||||||
|
self.calculate_and_test(params, None)
|
||||||
|
|
||||||
|
# method C
|
||||||
|
params["offset"].x = 10
|
||||||
|
self.calculate_and_test(params, None)
|
||||||
|
|||||||
Reference in New Issue
Block a user