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
59#[derive(Default, Debug, Copy, Clone, PartialEq)]
60pub struct Delta {
61 pub delta_x: f64,
63 pub delta_y: f64,
65}
66
67impl Delta {
68 pub fn new(delta_x: f64, delta_y: f64) -> Self {
70 Self { delta_x, delta_y }
71 }
72
73 pub fn area(&self, other: &Self) -> f64 {
74 (self.delta_x * other.delta_y - other.delta_x * self.delta_y) / 2.
75 }
76}
77
78impl From<Point> for DispatchMouseEventParams {
80 fn from(el: Point) -> DispatchMouseEventParams {
81 let mut params =
82 DispatchMouseEventParams::new(DispatchMouseEventType::MousePressed, el.x, el.y);
83 params.button = Some(MouseButton::Left);
84 params.click_count = Some(1);
85 params
86 }
87}
88
89#[derive(Default, Debug, Clone, Copy)]
91pub enum ScrollBehavior {
92 #[default]
93 Auto,
94 Instant,
95 Smooth,
96}
97
98#[derive(Debug, Copy, Clone)]
99pub struct ElementQuad {
100 pub top_left: Point,
101 pub top_right: Point,
102 pub bottom_right: Point,
103 pub bottom_left: Point,
104}
105
106impl ElementQuad {
107 pub fn from_quad(quad: &Quad) -> Self {
108 assert_eq!(quad.inner().len(), 8);
109 let raw_quad = quad.inner();
110 Self {
111 top_left: Point {
112 x: raw_quad[0],
113 y: raw_quad[1],
114 },
115 top_right: Point {
116 x: raw_quad[2],
117 y: raw_quad[3],
118 },
119 bottom_right: Point {
120 x: raw_quad[4],
121 y: raw_quad[5],
122 },
123 bottom_left: Point {
124 x: raw_quad[6],
125 y: raw_quad[7],
126 },
127 }
128 }
129
130 pub fn quad_center(&self) -> Point {
131 Point {
132 x: (self.top_left.x + self.top_right.x + self.bottom_right.x + self.bottom_left.x) / 4.,
133 y: (self.top_left.y + self.top_right.y + self.bottom_right.y + self.bottom_left.y) / 4.,
134 }
135 }
136 pub fn quad_area(&self) -> f64 {
139 let area = self.top_left.area(&self.top_right)
140 + self.top_right.area(&self.bottom_right)
141 + self.bottom_right.area(&self.bottom_left)
142 + self.bottom_left.area(&self.top_left);
143 area.abs()
144 }
145
146 pub fn height(&self) -> f64 {
147 self.bottom_left.y - self.top_left.y
148 }
149
150 pub fn width(&self) -> f64 {
151 self.top_right.x - self.top_left.x
152 }
153
154 pub fn aspect_ratio(&self) -> f64 {
156 self.width() / self.height()
157 }
158
159 pub fn most_left(&self) -> f64 {
161 self.top_right
162 .x
163 .min(self.top_left.x)
164 .min(self.bottom_right.x)
165 .min(self.bottom_left.x)
166 }
167
168 pub fn most_right(&self) -> f64 {
170 self.top_right
171 .x
172 .max(self.top_left.x)
173 .max(self.bottom_right.x)
174 .max(self.bottom_left.x)
175 }
176
177 pub fn most_top(&self) -> f64 {
179 self.top_right
180 .y
181 .min(self.top_left.y)
182 .min(self.bottom_right.y)
183 .min(self.bottom_left.y)
184 }
185
186 pub fn most_bottom(&self) -> f64 {
188 self.top_right
189 .y
190 .max(self.top_left.y)
191 .max(self.bottom_right.y)
192 .max(self.bottom_left.y)
193 }
194
195 pub fn strictly_above(&self, other: &Self) -> bool {
198 self.most_bottom() < other.most_top()
199 }
200
201 pub fn above(&self, other: &Self) -> bool {
204 self.most_bottom() <= other.most_top()
205 }
206
207 pub fn strictly_below(&self, other: &Self) -> bool {
210 self.most_top() > other.most_bottom()
211 }
212
213 pub fn below(&self, other: &Self) -> bool {
216 self.most_top() >= other.most_bottom()
217 }
218
219 pub fn strictly_left_of(&self, other: &Self) -> bool {
222 self.most_right() < other.most_left()
223 }
224
225 pub fn left_of(&self, other: &Self) -> bool {
228 self.most_right() <= other.most_left()
229 }
230
231 pub fn strictly_right_of(&self, other: &Self) -> bool {
234 self.most_left() > other.most_right()
235 }
236
237 pub fn right_of(&self, other: &Self) -> bool {
240 self.most_left() >= other.most_right()
241 }
242
243 pub fn within_horizontal_bounds_of(&self, other: &Self) -> bool {
245 self.most_left() >= other.most_left() && self.most_right() <= other.most_right()
246 }
247
248 pub fn within_vertical_bounds_of(&self, other: &Self) -> bool {
250 self.most_top() >= other.most_top() && self.most_bottom() <= other.most_bottom()
251 }
252
253 pub fn within_bounds_of(&self, other: &Self) -> bool {
255 self.within_horizontal_bounds_of(other) && self.within_vertical_bounds_of(other)
256 }
257}
258
259#[derive(Debug, Clone)]
260pub struct BoxModel {
261 pub content: ElementQuad,
262 pub padding: ElementQuad,
263 pub border: ElementQuad,
264 pub margin: ElementQuad,
265 pub width: u32,
266 pub height: u32,
267}
268
269impl BoxModel {
270 pub fn content_viewport(&self) -> Viewport {
272 Viewport {
273 x: self.content.top_left.x,
274 y: self.content.top_left.y,
275 width: self.content.width(),
276 height: self.content.height(),
277 scale: 1.0,
278 }
279 }
280
281 pub fn padding_viewport(&self) -> Viewport {
283 Viewport {
284 x: self.padding.top_left.x,
285 y: self.padding.top_left.y,
286 width: self.padding.width(),
287 height: self.padding.height(),
288 scale: 1.0,
289 }
290 }
291
292 pub fn border_viewport(&self) -> Viewport {
294 Viewport {
295 x: self.border.top_left.x,
296 y: self.border.top_left.y,
297 width: self.border.width(),
298 height: self.border.height(),
299 scale: 1.0,
300 }
301 }
302
303 pub fn margin_viewport(&self) -> Viewport {
305 Viewport {
306 x: self.margin.top_left.x,
307 y: self.margin.top_left.y,
308 width: self.margin.width(),
309 height: self.margin.height(),
310 scale: 1.0,
311 }
312 }
313}
314
315#[derive(Debug, Clone)]
316pub struct BoundingBox {
317 pub x: f64,
319 pub y: f64,
321 pub width: f64,
323 pub height: f64,
325}