1use chromiumoxide_cdp::cdp::browser_protocol::dom::Quad;
4use chromiumoxide_cdp::cdp::browser_protocol::input::{
5 DispatchMouseEventParams, DispatchMouseEventType, MouseButton,
6};
7use chromiumoxide_cdp::cdp::browser_protocol::page::Viewport;
8
9#[derive(Debug, Copy, Clone, PartialEq)]
10pub struct Point {
11 pub x: f64,
12 pub y: f64,
13}
14
15impl Point {
16 pub fn new(x: f64, y: f64) -> Self {
18 Self { x, y }
19 }
20
21 fn area(&self, other: &Self) -> f64 {
22 (self.x * other.y - other.x * self.y) / 2.
23 }
24}
25
26impl std::ops::Add<Point> for Point {
27 type Output = Self;
28
29 fn add(self, other: Self) -> Self {
30 Self {
31 x: self.x + other.x,
32 y: self.y + other.y,
33 }
34 }
35}
36
37impl std::ops::Sub<Point> for Point {
38 type Output = Self;
39
40 fn sub(self, other: Self) -> Self {
41 Self {
42 x: self.x - other.x,
43 y: self.y - other.y,
44 }
45 }
46}
47
48impl std::ops::Div<f64> for Point {
49 type Output = Self;
50
51 fn div(self, other: f64) -> Self {
52 Self {
53 x: self.x / other,
54 y: self.y / other,
55 }
56 }
57}
58
59impl From<Point> for DispatchMouseEventParams {
61 fn from(el: Point) -> DispatchMouseEventParams {
62 let mut params =
63 DispatchMouseEventParams::new(DispatchMouseEventType::MousePressed, el.x, el.y);
64 params.button = Some(MouseButton::Left);
65 params.click_count = Some(1);
66 params
67 }
68}
69
70#[derive(Debug, Copy, Clone)]
71pub struct ElementQuad {
72 pub top_left: Point,
73 pub top_right: Point,
74 pub bottom_right: Point,
75 pub bottom_left: Point,
76}
77
78impl ElementQuad {
79 pub fn from_quad(quad: &Quad) -> Self {
80 assert_eq!(quad.inner().len(), 8);
81 let raw_quad = quad.inner();
82 Self {
83 top_left: Point {
84 x: raw_quad[0],
85 y: raw_quad[1],
86 },
87 top_right: Point {
88 x: raw_quad[2],
89 y: raw_quad[3],
90 },
91 bottom_right: Point {
92 x: raw_quad[4],
93 y: raw_quad[5],
94 },
95 bottom_left: Point {
96 x: raw_quad[6],
97 y: raw_quad[7],
98 },
99 }
100 }
101
102 pub fn quad_center(&self) -> Point {
103 Point {
104 x: (self.top_left.x + self.top_right.x + self.bottom_right.x + self.bottom_left.x) / 4.,
105 y: (self.top_left.y + self.top_right.y + self.bottom_right.y + self.bottom_left.y) / 4.,
106 }
107 }
108 pub fn quad_area(&self) -> f64 {
111 let area = self.top_left.area(&self.top_right)
112 + self.top_right.area(&self.bottom_right)
113 + self.bottom_right.area(&self.bottom_left)
114 + self.bottom_left.area(&self.top_left);
115 area.abs()
116 }
117
118 pub fn height(&self) -> f64 {
119 self.bottom_left.y - self.top_left.y
120 }
121
122 pub fn width(&self) -> f64 {
123 self.top_right.x - self.top_left.x
124 }
125
126 pub fn aspect_ratio(&self) -> f64 {
128 self.width() / self.height()
129 }
130
131 pub fn most_left(&self) -> f64 {
133 self.top_right
134 .x
135 .min(self.top_left.x)
136 .min(self.bottom_right.x)
137 .min(self.bottom_left.x)
138 }
139
140 pub fn most_right(&self) -> f64 {
142 self.top_right
143 .x
144 .max(self.top_left.x)
145 .max(self.bottom_right.x)
146 .max(self.bottom_left.x)
147 }
148
149 pub fn most_top(&self) -> f64 {
151 self.top_right
152 .y
153 .min(self.top_left.y)
154 .min(self.bottom_right.y)
155 .min(self.bottom_left.y)
156 }
157
158 pub fn most_bottom(&self) -> f64 {
160 self.top_right
161 .y
162 .max(self.top_left.y)
163 .max(self.bottom_right.y)
164 .max(self.bottom_left.y)
165 }
166
167 pub fn strictly_above(&self, other: &Self) -> bool {
170 self.most_bottom() < other.most_top()
171 }
172
173 pub fn above(&self, other: &Self) -> bool {
176 self.most_bottom() <= other.most_top()
177 }
178
179 pub fn strictly_below(&self, other: &Self) -> bool {
182 self.most_top() > other.most_bottom()
183 }
184
185 pub fn below(&self, other: &Self) -> bool {
188 self.most_top() >= other.most_bottom()
189 }
190
191 pub fn strictly_left_of(&self, other: &Self) -> bool {
194 self.most_right() < other.most_left()
195 }
196
197 pub fn left_of(&self, other: &Self) -> bool {
200 self.most_right() <= other.most_left()
201 }
202
203 pub fn strictly_right_of(&self, other: &Self) -> bool {
206 self.most_left() > other.most_right()
207 }
208
209 pub fn right_of(&self, other: &Self) -> bool {
212 self.most_left() >= other.most_right()
213 }
214
215 pub fn within_horizontal_bounds_of(&self, other: &Self) -> bool {
217 self.most_left() >= other.most_left() && self.most_right() <= other.most_right()
218 }
219
220 pub fn within_vertical_bounds_of(&self, other: &Self) -> bool {
222 self.most_top() >= other.most_top() && self.most_bottom() <= other.most_bottom()
223 }
224
225 pub fn within_bounds_of(&self, other: &Self) -> bool {
227 self.within_horizontal_bounds_of(other) && self.within_vertical_bounds_of(other)
228 }
229}
230
231#[derive(Debug, Clone)]
232pub struct BoxModel {
233 pub content: ElementQuad,
234 pub padding: ElementQuad,
235 pub border: ElementQuad,
236 pub margin: ElementQuad,
237 pub width: u32,
238 pub height: u32,
239}
240
241impl BoxModel {
242 pub fn content_viewport(&self) -> Viewport {
244 Viewport {
245 x: self.content.top_left.x,
246 y: self.content.top_left.y,
247 width: self.content.width(),
248 height: self.content.height(),
249 scale: 1.0,
250 }
251 }
252
253 pub fn padding_viewport(&self) -> Viewport {
255 Viewport {
256 x: self.padding.top_left.x,
257 y: self.padding.top_left.y,
258 width: self.padding.width(),
259 height: self.padding.height(),
260 scale: 1.0,
261 }
262 }
263
264 pub fn border_viewport(&self) -> Viewport {
266 Viewport {
267 x: self.border.top_left.x,
268 y: self.border.top_left.y,
269 width: self.border.width(),
270 height: self.border.height(),
271 scale: 1.0,
272 }
273 }
274
275 pub fn margin_viewport(&self) -> Viewport {
277 Viewport {
278 x: self.margin.top_left.x,
279 y: self.margin.top_left.y,
280 width: self.margin.width(),
281 height: self.margin.height(),
282 scale: 1.0,
283 }
284 }
285}
286
287#[derive(Debug, Clone)]
288pub struct BoundingBox {
289 pub x: f64,
291 pub y: f64,
293 pub width: f64,
295 pub height: f64,
297}