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,
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, 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        assert_eq!(quad.inner().len(), 8);
111        let raw_quad = quad.inner();
112        Self {
113            top_left: Point {
114                x: raw_quad[0],
115                y: raw_quad[1],
116            },
117            top_right: Point {
118                x: raw_quad[2],
119                y: raw_quad[3],
120            },
121            bottom_right: Point {
122                x: raw_quad[4],
123                y: raw_quad[5],
124            },
125            bottom_left: Point {
126                x: raw_quad[6],
127                y: raw_quad[7],
128            },
129        }
130    }
131
132    pub fn quad_center(&self) -> Point {
133        Point {
134            x: (self.top_left.x + self.top_right.x + self.bottom_right.x + self.bottom_left.x) / 4.,
135            y: (self.top_left.y + self.top_right.y + self.bottom_right.y + self.bottom_left.y) / 4.,
136        }
137    }
138    pub fn quad_area(&self) -> f64 {
141        let area = self.top_left.area(&self.top_right)
142            + self.top_right.area(&self.bottom_right)
143            + self.bottom_right.area(&self.bottom_left)
144            + self.bottom_left.area(&self.top_left);
145        area.abs()
146    }
147
148    pub fn height(&self) -> f64 {
151        self.bottom_left.y - self.top_left.y
152    }
153
154    pub fn width(&self) -> f64 {
157        self.top_right.x - self.top_left.x
158    }
159
160    pub fn aspect_ratio(&self) -> f64 {
162        self.width() / self.height()
163    }
164
165    pub fn most_left(&self) -> f64 {
167        self.top_right
168            .x
169            .min(self.top_left.x)
170            .min(self.bottom_right.x)
171            .min(self.bottom_left.x)
172    }
173
174    pub fn most_right(&self) -> f64 {
176        self.top_right
177            .x
178            .max(self.top_left.x)
179            .max(self.bottom_right.x)
180            .max(self.bottom_left.x)
181    }
182
183    pub fn most_top(&self) -> f64 {
185        self.top_right
186            .y
187            .min(self.top_left.y)
188            .min(self.bottom_right.y)
189            .min(self.bottom_left.y)
190    }
191
192    pub fn most_bottom(&self) -> f64 {
194        self.top_right
195            .y
196            .max(self.top_left.y)
197            .max(self.bottom_right.y)
198            .max(self.bottom_left.y)
199    }
200
201    pub fn strictly_above(&self, other: &Self) -> bool {
204        self.most_bottom() < other.most_top()
205    }
206
207    pub fn above(&self, other: &Self) -> bool {
210        self.most_bottom() <= other.most_top()
211    }
212
213    pub fn strictly_below(&self, other: &Self) -> bool {
216        self.most_top() > other.most_bottom()
217    }
218
219    pub fn below(&self, other: &Self) -> bool {
222        self.most_top() >= other.most_bottom()
223    }
224
225    pub fn strictly_left_of(&self, other: &Self) -> bool {
228        self.most_right() < other.most_left()
229    }
230
231    pub fn left_of(&self, other: &Self) -> bool {
234        self.most_right() <= other.most_left()
235    }
236
237    pub fn strictly_right_of(&self, other: &Self) -> bool {
240        self.most_left() > other.most_right()
241    }
242
243    pub fn right_of(&self, other: &Self) -> bool {
246        self.most_left() >= other.most_right()
247    }
248
249    pub fn within_horizontal_bounds_of(&self, other: &Self) -> bool {
251        self.most_left() >= other.most_left() && self.most_right() <= other.most_right()
252    }
253
254    pub fn within_vertical_bounds_of(&self, other: &Self) -> bool {
256        self.most_top() >= other.most_top() && self.most_bottom() <= other.most_bottom()
257    }
258
259    pub fn within_bounds_of(&self, other: &Self) -> bool {
261        self.within_horizontal_bounds_of(other) && self.within_vertical_bounds_of(other)
262    }
263}
264
265#[derive(Debug, Clone)]
266pub struct BoxModel {
267    pub content: ElementQuad,
269    pub padding: ElementQuad,
271    pub border: ElementQuad,
273    pub margin: ElementQuad,
275    pub width: u32,
277    pub height: u32,
279}
280
281impl BoxModel {
282    pub fn content_viewport(&self) -> Viewport {
284        Viewport {
285            x: self.content.top_left.x,
286            y: self.content.top_left.y,
287            width: self.content.width(),
288            height: self.content.height(),
289            scale: 1.0,
290        }
291    }
292
293    pub fn padding_viewport(&self) -> Viewport {
295        Viewport {
296            x: self.padding.top_left.x,
297            y: self.padding.top_left.y,
298            width: self.padding.width(),
299            height: self.padding.height(),
300            scale: 1.0,
301        }
302    }
303
304    pub fn border_viewport(&self) -> Viewport {
306        Viewport {
307            x: self.border.top_left.x,
308            y: self.border.top_left.y,
309            width: self.border.width(),
310            height: self.border.height(),
311            scale: 1.0,
312        }
313    }
314
315    pub fn margin_viewport(&self) -> Viewport {
317        Viewport {
318            x: self.margin.top_left.x,
319            y: self.margin.top_left.y,
320            width: self.margin.width(),
321            height: self.margin.height(),
322            scale: 1.0,
323        }
324    }
325}
326
327#[derive(Debug, Clone)]
328pub struct BoundingBox {
329    pub x: f64,
331    pub y: f64,
333    pub width: f64,
335    pub height: f64,
337}