Skip to content

Commit 055742c

Browse files
ClemDoumcau-git
andauthored
fix: Better BoundingRectangle.angle and BoundingRectangle.angle_360 computation (#237)
* fix: fix `BoundingRectanble.angle` and `BoundingRectanble.angle_360` computation Signed-off-by: Clément Doumouro <[email protected]> * fix: compute page bounding rectangle angle between (0-2pi) / (0-360) Signed-off-by: Clément Doumouro <[email protected]> --------- Signed-off-by: Clément Doumouro <[email protected]> Co-authored-by: Christoph Auer <[email protected]>
1 parent 5e4c0fd commit 055742c

File tree

2 files changed

+91
-17
lines changed

2 files changed

+91
-17
lines changed

docling_core/types/doc/page.py

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -116,33 +116,28 @@ def height(self) -> float:
116116

117117
@property
118118
def angle(self) -> float:
119-
"""Calculate the angle of the rectangle in radians."""
119+
"""Calculate the angle of the rectangle in radians (0-2pi range)."""
120120
p_0 = ((self.r_x0 + self.r_x3) / 2.0, (self.r_y0 + self.r_y3) / 2.0)
121121
p_1 = ((self.r_x1 + self.r_x2) / 2.0, (self.r_y1 + self.r_y2) / 2.0)
122122

123123
delta_x, delta_y = p_1[0] - p_0[0], p_1[1] - p_0[1]
124124

125-
if abs(delta_x) > 1.0e-3:
126-
return math.atan(delta_y / delta_x)
127-
elif delta_y > 0:
128-
return 3.142592 / 2.0
125+
if abs(delta_y) < 1.0e-3:
126+
angle = 0.0
127+
elif abs(delta_x) < 1.0e-3:
128+
angle = np.pi / 2.0 if delta_y > 0 else -np.pi / 2.0
129129
else:
130-
return -3.142592 / 2.0
130+
angle = math.atan(delta_y / delta_x)
131+
if delta_x < 0:
132+
angle += np.pi
133+
if angle < 0:
134+
angle += 2 * np.pi
135+
return angle
131136

132137
@property
133138
def angle_360(self) -> int:
134139
"""Calculate the angle of the rectangle in degrees (0-360 range)."""
135-
p_0 = ((self.r_x0 + self.r_x3) / 2.0, (self.r_y0 + self.r_y3) / 2.0)
136-
p_1 = ((self.r_x1 + self.r_x2) / 2.0, (self.r_y1 + self.r_y2) / 2.0)
137-
138-
delta_x, delta_y = p_1[0] - p_0[0], p_1[1] - p_0[1]
139-
140-
if abs(delta_y) < 1.0e-2:
141-
return 0
142-
elif abs(delta_x) < 1.0e-2:
143-
return 90
144-
else:
145-
return round(-math.atan(delta_y / delta_x) / np.pi * 180)
140+
return round(self.angle / np.pi * 180)
146141

147142
@property
148143
def centre(self):

test/test_page.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import math
2+
3+
import numpy as np
4+
import pytest
5+
6+
from docling_core.types.doc.page import BoundingRectangle
7+
8+
SQRT_2 = math.sqrt(2)
9+
10+
R_0 = BoundingRectangle(r_x0=0, r_y0=0, r_x1=1, r_y1=0, r_x2=1, r_y2=1, r_x3=0, r_y3=1)
11+
R_45 = BoundingRectangle(
12+
r_x0=0,
13+
r_y0=0,
14+
r_x1=SQRT_2 / 2,
15+
r_y1=SQRT_2 / 2,
16+
r_x2=0,
17+
r_y2=SQRT_2,
18+
r_x3=-SQRT_2 / 2,
19+
r_y3=SQRT_2 / 2,
20+
)
21+
R_90 = BoundingRectangle(
22+
r_x0=0, r_y0=0, r_x1=0, r_y1=1, r_x2=-1, r_y2=1, r_x3=-1, r_y3=0
23+
)
24+
R_135 = BoundingRectangle(
25+
r_x0=0,
26+
r_y0=0,
27+
r_x1=-SQRT_2 / 2,
28+
r_y1=SQRT_2 / 2,
29+
r_x2=-SQRT_2,
30+
r_y2=0,
31+
r_x3=-SQRT_2 / 2,
32+
r_y3=-SQRT_2 / 2,
33+
)
34+
R_180 = BoundingRectangle(
35+
r_x0=0, r_y0=0, r_x1=-0, r_y1=0, r_x2=-1, r_y2=-1, r_x3=0, r_y3=-1
36+
)
37+
R_MINUS_135 = BoundingRectangle(
38+
r_x0=0,
39+
r_y0=0,
40+
r_x1=-SQRT_2 / 2,
41+
r_y1=-SQRT_2 / 2,
42+
r_x2=0,
43+
r_y2=-SQRT_2,
44+
r_x3=SQRT_2 / 2,
45+
r_y3=-SQRT_2 / 2,
46+
)
47+
R_MINUS_90 = BoundingRectangle(
48+
r_x0=0, r_y0=0, r_x1=0, r_y1=-1, r_x2=1, r_y2=-1, r_x3=1, r_y3=0
49+
)
50+
R_MINUS_45 = BoundingRectangle(
51+
r_x0=0,
52+
r_y0=0,
53+
r_x1=SQRT_2 / 2,
54+
r_y1=-SQRT_2 / 2,
55+
r_x2=SQRT_2,
56+
r_y2=0,
57+
r_x3=SQRT_2 / 2,
58+
r_y3=SQRT_2 / 2,
59+
)
60+
61+
62+
@pytest.mark.parametrize(
63+
("rectangle", "expected_angle", "expected_angle_360"),
64+
[
65+
(R_0, 0, 0.0),
66+
(R_45, np.pi / 4, 45),
67+
(R_90, np.pi / 2, 90),
68+
(R_135, 3 * np.pi / 4, 135),
69+
(R_180, np.pi, 180),
70+
(R_MINUS_135, 5 * np.pi / 4, 225),
71+
(R_MINUS_90, 3 * np.pi / 2, 270),
72+
(R_MINUS_45, 7 * np.pi / 4, 315),
73+
],
74+
)
75+
def test_bounding_rectangle_angle(
76+
rectangle: BoundingRectangle, expected_angle: float, expected_angle_360: int
77+
):
78+
assert pytest.approx(rectangle.angle, abs=1e-6) == expected_angle
79+
assert pytest.approx(rectangle.angle_360, abs=1e-6) == expected_angle_360

0 commit comments

Comments
 (0)