1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5 use std::{cmp, f64, fmt};
6
7 use svgtypes::FuzzyEq;
8
9 use crate::{tree, IsValidLength};
10
11
12 /// Line representation.
13 #[allow(missing_docs)]
14 #[derive(Clone, Copy, Debug)]
15 pub(crate) struct Line {
16 pub x1: f64,
17 pub y1: f64,
18 pub x2: f64,
19 pub y2: f64,
20 }
21
22 impl Line {
23 /// Creates a new line.
24 #[inline]
new(x1: f64, y1: f64, x2: f64, y2: f64) -> Line25 pub fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Line {
26 Line { x1, y1, x2, y2 }
27 }
28
29 /// Calculates the line length.
30 #[inline]
length(&self) -> f6431 pub fn length(&self) -> f64 {
32 let x = self.x2 - self.x1;
33 let y = self.y2 - self.y1;
34 (x*x + y*y).sqrt()
35 }
36
37 /// Sets the line length.
set_length(&mut self, len: f64)38 pub fn set_length(&mut self, len: f64) {
39 let x = self.x2 - self.x1;
40 let y = self.y2 - self.y1;
41 let len2 = (x*x + y*y).sqrt();
42 let line = Line {
43 x1: self.x1, y1: self.y1,
44 x2: self.x1 + x/len2, y2: self.y1 + y/len2
45 };
46
47 self.x2 = self.x1 + (line.x2 - line.x1) * len;
48 self.y2 = self.y1 + (line.y2 - line.y1) * len;
49 }
50 }
51
52
53 /// A 2D point representation.
54 #[derive(Clone, Copy)]
55 pub struct Point<T> {
56 /// Position along the X-axis.
57 pub x: T,
58
59 /// Position along the Y-axis.
60 pub y: T,
61 }
62
63 impl<T> Point<T> {
64 /// Create a new point.
new(x: T, y: T) -> Self65 pub fn new(x: T, y: T) -> Self {
66 Point { x, y }
67 }
68 }
69
70 impl<T: fmt::Display> fmt::Debug for Point<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result71 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72 write!(f, "Point({} {})", self.x, self.y)
73 }
74 }
75
76 impl<T: fmt::Display> fmt::Display for Point<T> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result77 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
78 write!(f, "{:?}", self)
79 }
80 }
81
82
83 /// A 2D size representation.
84 ///
85 /// Width and height are guarantee to be > 0.
86 #[derive(Clone, Copy)]
87 pub struct Size {
88 width: f64,
89 height: f64,
90 }
91
92 impl Size {
93 /// Creates a new `Size` from values.
94 #[inline]
new(width: f64, height: f64) -> Option<Self>95 pub fn new(width: f64, height: f64) -> Option<Self> {
96 if width.is_valid_length() && height.is_valid_length() {
97 Some(Size { width, height })
98 } else {
99 None
100 }
101 }
102
103 /// Returns width.
104 #[inline]
width(&self) -> f64105 pub fn width(&self) -> f64 {
106 self.width
107 }
108
109 /// Returns height.
110 #[inline]
height(&self) -> f64111 pub fn height(&self) -> f64 {
112 self.height
113 }
114
115 /// Converts `Size` to `ScreenSize`.
116 #[inline]
to_screen_size(&self) -> ScreenSize117 pub fn to_screen_size(&self) -> ScreenSize {
118 ScreenSize::new(
119 cmp::max(1, self.width().round() as u32),
120 cmp::max(1, self.height().round() as u32),
121 ).unwrap()
122 }
123
124 /// Converts the current size to `Rect` at provided position.
125 #[inline]
to_rect(&self, x: f64, y: f64) -> Rect126 pub fn to_rect(&self, x: f64, y: f64) -> Rect {
127 Rect::new(x, y, self.width, self.height).unwrap()
128 }
129 }
130
131 impl fmt::Debug for Size {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result132 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133 write!(f, "Size({} {})", self.width, self.height)
134 }
135 }
136
137 impl fmt::Display for Size {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 write!(f, "{:?}", self)
140 }
141 }
142
143 impl FuzzyEq for Size {
144 #[inline]
fuzzy_eq(&self, other: &Self) -> bool145 fn fuzzy_eq(&self, other: &Self) -> bool {
146 self.width.fuzzy_eq(&other.width)
147 && self.height.fuzzy_eq(&other.height)
148 }
149 }
150
151
152 /// A 2D screen size representation.
153 ///
154 /// Width and height are guarantee to be > 0.
155 #[allow(missing_docs)]
156 #[derive(Clone, Copy, PartialEq)]
157 pub struct ScreenSize {
158 width: u32,
159 height: u32,
160 }
161
162 impl ScreenSize {
163 /// Creates a new `ScreenSize` from values.
164 #[inline]
new(width: u32, height: u32) -> Option<Self>165 pub fn new(width: u32, height: u32) -> Option<Self> {
166 if width > 0 && height > 0 {
167 Some(ScreenSize { width, height })
168 } else {
169 None
170 }
171 }
172
173 /// Returns width.
174 #[inline]
width(&self) -> u32175 pub fn width(&self) -> u32 {
176 self.width
177 }
178
179 /// Returns height.
180 #[inline]
height(&self) -> u32181 pub fn height(&self) -> u32 {
182 self.height
183 }
184
185 /// Returns width and height as a tuple.
186 #[inline]
dimensions(&self) -> (u32, u32)187 pub fn dimensions(&self) -> (u32, u32) {
188 (self.width, self.height)
189 }
190
191 /// Scales current size to specified size.
192 #[inline]
scale_to(&self, to: Self) -> Self193 pub fn scale_to(&self, to: Self) -> Self {
194 size_scale(*self, to, false)
195 }
196
197 /// Expands current size to specified size.
198 #[inline]
expand_to(&self, to: Self) -> Self199 pub fn expand_to(&self, to: Self) -> Self {
200 size_scale(*self, to, true)
201 }
202
203 /// Fits size into a viewbox.
fit_view_box(&self, vb: &tree::ViewBox) -> Self204 pub fn fit_view_box(&self, vb: &tree::ViewBox) -> Self {
205 let s = vb.rect.to_screen_size();
206
207 if vb.aspect.align == tree::Align::None {
208 s
209 } else {
210 if vb.aspect.slice {
211 self.expand_to(s)
212 } else {
213 self.scale_to(s)
214 }
215 }
216 }
217
218 /// Converts the current `ScreenSize` to `Size`.
219 #[inline]
to_size(&self) -> Size220 pub fn to_size(&self) -> Size {
221 // Can't fail, because `ScreenSize` is always valid.
222 Size::new(self.width as f64, self.height as f64).unwrap()
223 }
224 }
225
226 impl fmt::Debug for ScreenSize {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result227 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228 write!(f, "ScreenSize({} {})", self.width, self.height)
229 }
230 }
231
232 impl fmt::Display for ScreenSize {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result233 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234 write!(f, "{:?}", self)
235 }
236 }
237
size_scale( s1: ScreenSize, s2: ScreenSize, expand: bool, ) -> ScreenSize238 fn size_scale(
239 s1: ScreenSize,
240 s2: ScreenSize,
241 expand: bool,
242 ) -> ScreenSize {
243 let rw = (s2.height as f64 * s1.width as f64 / s1.height as f64).ceil() as u32;
244 let with_h = if expand { rw <= s2.width } else { rw >= s2.width };
245 if !with_h {
246 ScreenSize::new(rw, s2.height).unwrap()
247 } else {
248 let h = (s2.width as f64 * s1.height as f64 / s1.width as f64).ceil() as u32;
249 ScreenSize::new(s2.width, h).unwrap()
250 }
251 }
252
253
254 /// A rect representation.
255 ///
256 /// Width and height are guarantee to be > 0.
257 #[derive(Clone, Copy)]
258 pub struct Rect {
259 x: f64,
260 y: f64,
261 width: f64,
262 height: f64,
263 }
264
265 impl Rect {
266 /// Creates a new `Rect` from values.
267 #[inline]
new(x: f64, y: f64, width: f64, height: f64) -> Option<Self>268 pub fn new(x: f64, y: f64, width: f64, height: f64) -> Option<Self> {
269 if width.is_valid_length() && height.is_valid_length() {
270 Some(Rect { x, y, width, height })
271 } else {
272 None
273 }
274 }
275
276 /// Creates a new `Rect` for bounding box calculation.
277 ///
278 /// Shorthand for `Rect::new(f64::MAX, f64::MAX, 1.0, 1.0)`.
279 #[inline]
new_bbox() -> Self280 pub fn new_bbox() -> Self {
281 Rect::new(f64::MAX, f64::MAX, 1.0, 1.0).unwrap()
282 }
283
284 /// Returns rect's size.
285 #[inline]
size(&self) -> Size286 pub fn size(&self) -> Size {
287 Size::new(self.width, self.height).unwrap()
288 }
289
290 /// Returns rect's X position.
291 #[inline]
x(&self) -> f64292 pub fn x(&self) -> f64 {
293 self.x
294 }
295
296 /// Returns rect's Y position.
297 #[inline]
y(&self) -> f64298 pub fn y(&self) -> f64 {
299 self.y
300 }
301
302 /// Returns rect's width.
303 #[inline]
width(&self) -> f64304 pub fn width(&self) -> f64 {
305 self.width
306 }
307
308 /// Returns rect's height.
309 #[inline]
height(&self) -> f64310 pub fn height(&self) -> f64 {
311 self.height
312 }
313
314 /// Returns rect's left edge position.
315 #[inline]
left(&self) -> f64316 pub fn left(&self) -> f64 {
317 self.x
318 }
319
320 /// Returns rect's right edge position.
321 #[inline]
right(&self) -> f64322 pub fn right(&self) -> f64 {
323 self.x + self.width
324 }
325
326 /// Returns rect's top edge position.
327 #[inline]
top(&self) -> f64328 pub fn top(&self) -> f64 {
329 self.y
330 }
331
332 /// Returns rect's bottom edge position.
333 #[inline]
bottom(&self) -> f64334 pub fn bottom(&self) -> f64 {
335 self.y + self.height
336 }
337
338 /// Translates the rect by the specified offset.
339 #[inline]
translate(&self, tx: f64, ty: f64) -> Self340 pub fn translate(&self, tx: f64, ty: f64) -> Self {
341 Rect {
342 x: self.x + tx,
343 y: self.y + ty,
344 width: self.width,
345 height: self.height,
346 }
347 }
348
349 /// Translates the rect to the specified position.
350 #[inline]
translate_to(&self, x: f64, y: f64) -> Self351 pub fn translate_to(&self, x: f64, y: f64) -> Self {
352 Rect {
353 x,
354 y,
355 width: self.width,
356 height: self.height,
357 }
358 }
359
360 /// Checks that the rect contains a point.
361 #[inline]
contains(&self, x: f64, y: f64) -> bool362 pub fn contains(&self, x: f64, y: f64) -> bool {
363 if x < self.x || x > self.x + self.width - 1.0 {
364 return false;
365 }
366
367 if y < self.y || y > self.y + self.height - 1.0 {
368 return false;
369 }
370
371 true
372 }
373
374 /// Expands the `Rect` to the provided size.
375 #[inline]
expand(&self, r: Rect) -> Self376 pub fn expand(&self, r: Rect) -> Self {
377 #[inline]
378 fn f64_min(v1: f64, v2: f64) -> f64 {
379 if v1 < v2 { v1 } else { v2 }
380 }
381
382 #[inline]
383 fn f64_max(v1: f64, v2: f64) -> f64 {
384 if v1 > v2 { v1 } else { v2 }
385 }
386
387 if self.fuzzy_eq(&Rect::new_bbox()) {
388 r
389 } else {
390 let x1 = f64_min(self.x(), r.x());
391 let y1 = f64_min(self.y(), r.y());
392
393 let x2 = f64_max(self.right(), r.right());
394 let y2 = f64_max(self.bottom(), r.bottom());
395
396 Rect::new(x1, y1, x2 - x1, y2 - y1).unwrap()
397 }
398 }
399
400 /// Transforms the `Rect` using the provided `bbox`.
bbox_transform(&self, bbox: Rect) -> Self401 pub fn bbox_transform(&self, bbox: Rect) -> Self {
402 let x = self.x() * bbox.width() + bbox.x();
403 let y = self.y() * bbox.height() + bbox.y();
404 let w = self.width() * bbox.width();
405 let h = self.height() * bbox.height();
406 Rect::new(x, y, w, h).unwrap()
407 }
408
409 /// Transforms the `Rect` using the provided `Transform`.
410 ///
411 /// This method is expensive.
transform(&self, ts: &tree::Transform) -> Option<Self>412 pub fn transform(&self, ts: &tree::Transform) -> Option<Self> {
413 if !ts.is_default() {
414 let path = &[
415 tree::PathSegment::MoveTo {
416 x: self.x(), y: self.y()
417 },
418 tree::PathSegment::LineTo {
419 x: self.right(), y: self.y()
420 },
421 tree::PathSegment::LineTo {
422 x: self.right(), y: self.bottom()
423 },
424 tree::PathSegment::LineTo {
425 x: self.x(), y: self.bottom()
426 },
427 tree::PathSegment::ClosePath,
428 ];
429
430 tree::SubPathData(path).bbox_with_transform(*ts, None)
431 } else {
432 Some(*self)
433 }
434 }
435
436 /// Returns rect's size in screen units.
437 #[inline]
to_screen_size(&self) -> ScreenSize438 pub fn to_screen_size(&self) -> ScreenSize {
439 self.size().to_screen_size()
440 }
441
442 /// Returns rect in screen units.
443 #[inline]
to_screen_rect(&self) -> ScreenRect444 pub fn to_screen_rect(&self) -> ScreenRect {
445 ScreenRect::new(
446 self.x() as i32,
447 self.y() as i32,
448 cmp::max(1, self.width().round() as u32),
449 cmp::max(1, self.height().round() as u32),
450 ).unwrap()
451 }
452 }
453
454 impl FuzzyEq for Rect {
455 #[inline]
fuzzy_eq(&self, other: &Self) -> bool456 fn fuzzy_eq(&self, other: &Self) -> bool {
457 self.x.fuzzy_eq(&other.x)
458 && self.y.fuzzy_eq(&other.y)
459 && self.width.fuzzy_eq(&other.width)
460 && self.height.fuzzy_eq(&other.height)
461 }
462 }
463
464 impl fmt::Debug for Rect {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result465 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466 write!(f, "Rect({} {} {} {})", self.x, self.y, self.width, self.height)
467 }
468 }
469
470 impl fmt::Display for Rect {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result471 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
472 write!(f, "{:?}", self)
473 }
474 }
475
476
477 /// A 2D screen rect representation.
478 ///
479 /// Width and height are guarantee to be > 0.
480 #[allow(missing_docs)]
481 #[derive(Clone, Copy, PartialEq)]
482 pub struct ScreenRect {
483 x: i32,
484 y: i32,
485 width: u32,
486 height: u32,
487 }
488
489 impl ScreenRect {
490 /// Creates a new `Rect` from values.
491 #[inline]
new(x: i32, y: i32, width: u32, height: u32) -> Option<Self>492 pub fn new(x: i32, y: i32, width: u32, height: u32) -> Option<Self> {
493 if width > 0 && height > 0 {
494 Some(ScreenRect { x, y, width, height })
495 } else {
496 None
497 }
498 }
499
500 /// Returns rect's size.
501 #[inline]
size(&self) -> ScreenSize502 pub fn size(&self) -> ScreenSize {
503 // Can't fail, because `ScreenSize` is always valid.
504 ScreenSize::new(self.width, self.height).unwrap()
505 }
506
507 /// Returns rect's X position.
508 #[inline]
x(&self) -> i32509 pub fn x(&self) -> i32 {
510 self.x
511 }
512
513 /// Returns rect's Y position.
514 #[inline]
y(&self) -> i32515 pub fn y(&self) -> i32 {
516 self.y
517 }
518
519 /// Returns rect's width.
520 #[inline]
width(&self) -> u32521 pub fn width(&self) -> u32 {
522 self.width
523 }
524
525 /// Returns rect's height.
526 #[inline]
height(&self) -> u32527 pub fn height(&self) -> u32 {
528 self.height
529 }
530
531 /// Returns rect's left edge position.
532 #[inline]
left(&self) -> i32533 pub fn left(&self) -> i32 {
534 self.x
535 }
536
537 /// Returns rect's right edge position.
538 #[inline]
right(&self) -> i32539 pub fn right(&self) -> i32 {
540 self.x + self.width as i32
541 }
542
543 /// Returns rect's top edge position.
544 #[inline]
top(&self) -> i32545 pub fn top(&self) -> i32 {
546 self.y
547 }
548
549 /// Returns rect's bottom edge position.
550 #[inline]
bottom(&self) -> i32551 pub fn bottom(&self) -> i32 {
552 self.y + self.height as i32
553 }
554
555 /// Translates the rect by the specified offset.
556 #[inline]
translate(&self, tx: i32, ty: i32) -> Self557 pub fn translate(&self, tx: i32, ty: i32) -> Self {
558 ScreenRect {
559 x: self.x + tx,
560 y: self.y + ty,
561 width: self.width,
562 height: self.height,
563 }
564 }
565
566 /// Translates the rect to the specified position.
567 #[inline]
translate_to(&self, x: i32, y: i32) -> Self568 pub fn translate_to(&self, x: i32, y: i32) -> Self {
569 ScreenRect {
570 x,
571 y,
572 width: self.width,
573 height: self.height,
574 }
575 }
576
577 /// Checks that rect contains a point.
578 #[inline]
contains(&self, x: i32, y: i32) -> bool579 pub fn contains(&self, x: i32, y: i32) -> bool {
580 if x < self.x || x > self.x + self.width as i32 - 1 {
581 return false;
582 }
583
584 if y < self.y || y > self.y + self.height as i32 - 1 {
585 return false;
586 }
587
588 true
589 }
590
591 /// Fits the current rect into the specified bounds.
592 #[inline]
fit_to_rect(&self, bounds: ScreenRect) -> Self593 pub fn fit_to_rect(&self, bounds: ScreenRect) -> Self {
594 let mut r = *self;
595
596 if r.x < 0 { r.x = 0; }
597 if r.y < 0 { r.y = 0; }
598
599 if r.right() > bounds.width as i32 {
600 r.width = cmp::max(1, bounds.width as i32 - r.x) as u32;
601 }
602
603 if r.bottom() > bounds.height as i32 {
604 r.height = cmp::max(1, bounds.height as i32 - r.y) as u32;
605 }
606
607 r
608 }
609
610 /// Converts into `Rect`.
611 #[inline]
to_rect(&self) -> Rect612 pub fn to_rect(&self) -> Rect {
613 // Can't fail, because `ScreenRect` is always valid.
614 Rect::new(self.x as f64, self.y as f64, self.width as f64, self.height as f64).unwrap()
615 }
616 }
617
618 impl fmt::Debug for ScreenRect {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result619 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
620 write!(f, "ScreenRect({} {} {} {})", self.x, self.y, self.width, self.height)
621 }
622 }
623
624 impl fmt::Display for ScreenRect {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result625 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
626 write!(f, "{:?}", self)
627 }
628 }
629
630
631 #[cfg(test)]
632 mod tests {
633 use super::*;
634
635 #[test]
bbox_transform_1()636 fn bbox_transform_1() {
637 let r = Rect::new(10.0, 20.0, 30.0, 40.0).unwrap();
638 assert!(r.bbox_transform(Rect::new(0.2, 0.3, 0.4, 0.5).unwrap())
639 .fuzzy_eq(&Rect::new(4.2, 10.3, 12.0, 20.0).unwrap()));
640 }
641 }
642