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, Default, Copy, Clone, PartialEq)]
10pub struct Point {
11 pub x: f64,
13 pub y: f64,
15}
16
17impl Point {
18 pub fn new(x: f64, y: f64) -> Self {
20 Self { x, y }
21 }
22 fn area(&self, other: &Self) -> f64 {
24 (self.x * other.y - other.x * self.y) / 2.
25 }
26}
27
28impl std::ops::Add<Point> for Point {
29 type Output = Self;
30
31 fn add(self, other: Self) -> Self {
32 Self {
33 x: self.x + other.x,
34 y: self.y + other.y,
35 }
36 }
37}
38
39impl std::ops::Sub<Point> for Point {
40 type Output = Self;
41
42 fn sub(self, other: Self) -> Self {
43 Self {
44 x: self.x - other.x,
45 y: self.y - other.y,
46 }
47 }
48}
49
50impl std::ops::Div<f64> for Point {
51 type Output = Self;
52
53 fn div(self, other: f64) -> Self {
54 Self {
55 x: self.x / other,
56 y: self.y / other,
57 }
58 }
59}
60
61#[derive(Default, Debug, Copy, Clone, PartialEq)]
62pub struct Delta {
63 pub delta_x: f64,
65 pub delta_y: f64,
67}
68
69impl Delta {
70 pub fn new(delta_x: f64, delta_y: f64) -> Self {
72 Self { delta_x, delta_y }
73 }
74
75 pub fn area(&self, other: &Self) -> f64 {
76 (self.delta_x * other.delta_y - other.delta_x * self.delta_y) / 2.
77 }
78}
79
80impl From<Point> for DispatchMouseEventParams {
82 fn from(el: Point) -> DispatchMouseEventParams {
83 let mut params =
84 DispatchMouseEventParams::new(DispatchMouseEventType::MousePressed, el.x, el.y);
85 params.button = Some(MouseButton::Left);
86 params.click_count = Some(1);
87 params
88 }
89}
90
91#[derive(Default, Debug, Clone, Copy)]
93pub enum ScrollBehavior {
94 #[default]
95 Auto,
96 Instant,
97 Smooth,
98}
99
100#[derive(Debug, Default, Copy, Clone)]
101pub struct ElementQuad {
102 pub top_left: Point,
103 pub top_right: Point,
104 pub bottom_right: Point,
105 pub bottom_left: Point,
106}
107
108impl ElementQuad {
109 pub fn from_quad(quad: &Quad) -> Self {
110 let raw_quad = quad.inner();
111 debug_assert_eq!(raw_quad.len(), 8);
112 if raw_quad.len() < 8 {
113 return Self::default();
114 }
115 Self {
116 top_left: Point {
117 x: raw_quad[0],
118 y: raw_quad[1],
119 },
120 top_right: Point {
121 x: raw_quad[2],
122 y: raw_quad[3],
123 },
124 bottom_right: Point {
125 x: raw_quad[4],
126 y: raw_quad[5],
127 },
128 bottom_left: Point {
129 x: raw_quad[6],
130 y: raw_quad[7],
131 },
132 }
133 }
134
135 pub fn quad_center(&self) -> Point {
136 Point {
137 x: (self.top_left.x + self.top_right.x + self.bottom_right.x + self.bottom_left.x) / 4.,
138 y: (self.top_left.y + self.top_right.y + self.bottom_right.y + self.bottom_left.y) / 4.,
139 }
140 }
141 pub fn quad_area(&self) -> f64 {
144 let area = self.top_left.area(&self.top_right)
145 + self.top_right.area(&self.bottom_right)
146 + self.bottom_right.area(&self.bottom_left)
147 + self.bottom_left.area(&self.top_left);
148 area.abs()
149 }
150
151 pub fn height(&self) -> f64 {
154 self.bottom_left.y - self.top_left.y
155 }
156
157 pub fn width(&self) -> f64 {
160 self.top_right.x - self.top_left.x
161 }
162
163 pub fn aspect_ratio(&self) -> f64 {
165 self.width() / self.height()
166 }
167
168 pub fn most_left(&self) -> f64 {
170 self.top_right
171 .x
172 .min(self.top_left.x)
173 .min(self.bottom_right.x)
174 .min(self.bottom_left.x)
175 }
176
177 pub fn most_right(&self) -> f64 {
179 self.top_right
180 .x
181 .max(self.top_left.x)
182 .max(self.bottom_right.x)
183 .max(self.bottom_left.x)
184 }
185
186 pub fn most_top(&self) -> f64 {
188 self.top_right
189 .y
190 .min(self.top_left.y)
191 .min(self.bottom_right.y)
192 .min(self.bottom_left.y)
193 }
194
195 pub fn most_bottom(&self) -> f64 {
197 self.top_right
198 .y
199 .max(self.top_left.y)
200 .max(self.bottom_right.y)
201 .max(self.bottom_left.y)
202 }
203
204 pub fn strictly_above(&self, other: &Self) -> bool {
207 self.most_bottom() < other.most_top()
208 }
209
210 pub fn above(&self, other: &Self) -> bool {
213 self.most_bottom() <= other.most_top()
214 }
215
216 pub fn strictly_below(&self, other: &Self) -> bool {
219 self.most_top() > other.most_bottom()
220 }
221
222 pub fn below(&self, other: &Self) -> bool {
225 self.most_top() >= other.most_bottom()
226 }
227
228 pub fn strictly_left_of(&self, other: &Self) -> bool {
231 self.most_right() < other.most_left()
232 }
233
234 pub fn left_of(&self, other: &Self) -> bool {
237 self.most_right() <= other.most_left()
238 }
239
240 pub fn strictly_right_of(&self, other: &Self) -> bool {
243 self.most_left() > other.most_right()
244 }
245
246 pub fn right_of(&self, other: &Self) -> bool {
249 self.most_left() >= other.most_right()
250 }
251
252 pub fn within_horizontal_bounds_of(&self, other: &Self) -> bool {
254 self.most_left() >= other.most_left() && self.most_right() <= other.most_right()
255 }
256
257 pub fn within_vertical_bounds_of(&self, other: &Self) -> bool {
259 self.most_top() >= other.most_top() && self.most_bottom() <= other.most_bottom()
260 }
261
262 pub fn within_bounds_of(&self, other: &Self) -> bool {
264 self.within_horizontal_bounds_of(other) && self.within_vertical_bounds_of(other)
265 }
266}
267
268#[derive(Debug, Clone)]
269pub struct BoxModel {
270 pub content: ElementQuad,
272 pub padding: ElementQuad,
274 pub border: ElementQuad,
276 pub margin: ElementQuad,
278 pub width: u32,
280 pub height: u32,
282}
283
284impl BoxModel {
285 pub fn content_viewport(&self) -> Viewport {
287 Viewport {
288 x: self.content.top_left.x,
289 y: self.content.top_left.y,
290 width: self.content.width(),
291 height: self.content.height(),
292 scale: 1.0,
293 }
294 }
295
296 pub fn padding_viewport(&self) -> Viewport {
298 Viewport {
299 x: self.padding.top_left.x,
300 y: self.padding.top_left.y,
301 width: self.padding.width(),
302 height: self.padding.height(),
303 scale: 1.0,
304 }
305 }
306
307 pub fn border_viewport(&self) -> Viewport {
309 Viewport {
310 x: self.border.top_left.x,
311 y: self.border.top_left.y,
312 width: self.border.width(),
313 height: self.border.height(),
314 scale: 1.0,
315 }
316 }
317
318 pub fn margin_viewport(&self) -> Viewport {
320 Viewport {
321 x: self.margin.top_left.x,
322 y: self.margin.top_left.y,
323 width: self.margin.width(),
324 height: self.margin.height(),
325 scale: 1.0,
326 }
327 }
328}
329
330#[derive(Debug, Clone)]
331pub struct BoundingBox {
332 pub x: f64,
334 pub y: f64,
336 pub width: f64,
338 pub height: f64,
340}