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}