rive_rs/
layout.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::math::{self, Aabb, Mat};
6
7#[derive(Clone, Copy, Debug)]
8pub enum Fit {
9    Fill,
10    Contain,
11    Cover,
12    FitWidth,
13    FitHeight,
14    None,
15    ScaleDown,
16}
17
18#[derive(Clone, Copy, Debug)]
19pub struct Alignment(math::Vec);
20
21impl Alignment {
22    pub const fn new(x: f32, y: f32) -> Self {
23        Self(math::Vec::new(x, y))
24    }
25
26    pub const fn top_left() -> Self {
27        Self::new(-1.0, -1.0)
28    }
29
30    pub const fn top_center() -> Self {
31        Self::new(0.0, -1.0)
32    }
33
34    pub const fn top_right() -> Self {
35        Self::new(1.0, -1.0)
36    }
37
38    pub const fn center_left() -> Self {
39        Self::new(-1.0, 0.0)
40    }
41
42    pub const fn center() -> Self {
43        Self::new(0.0, 0.0)
44    }
45
46    pub const fn center_right() -> Self {
47        Self::new(1.0, 0.0)
48    }
49
50    pub const fn bottom_left() -> Self {
51        Self::new(-1.0, 1.0)
52    }
53
54    pub const fn bottom_center() -> Self {
55        Self::new(0.0, 1.0)
56    }
57
58    pub const fn bottom_right() -> Self {
59        Self::new(1.0, 1.0)
60    }
61}
62
63pub fn align(fit: Fit, alignment: Alignment, frame: Aabb, content: Aabb) -> Mat {
64    let content_size = content.size();
65    let conent_half_size = content_size * 0.5;
66
67    let pos = -content.min - conent_half_size - alignment.0 * conent_half_size;
68
69    let scale_vec = match fit {
70        Fit::Fill => frame.size() / content_size,
71        Fit::Contain => {
72            let scaled = frame.size() / content_size;
73            let min_scale = scaled.x.min(scaled.y);
74
75            math::Vec::new(min_scale, min_scale)
76        }
77        Fit::Cover => {
78            let scaled = frame.size() / content_size;
79            let max_scale = scaled.x.max(scaled.y);
80
81            math::Vec::new(max_scale, max_scale)
82        }
83        Fit::FitWidth => {
84            let min_scale = frame.size().x / content_size.x;
85
86            math::Vec::new(min_scale, min_scale)
87        }
88        Fit::FitHeight => {
89            let min_scale = frame.size().y / content_size.y;
90
91            math::Vec::new(min_scale, min_scale)
92        }
93        Fit::None => math::Vec::new(1.0, 1.0),
94        Fit::ScaleDown => {
95            let scaled = frame.size() / content_size;
96            let min_scale = scaled.x.min(scaled.y).min(1.0);
97
98            math::Vec::new(min_scale, min_scale)
99        }
100    };
101
102    let frame_half_size = frame.size() * 0.5;
103    let translation_vec = frame.min + frame_half_size + alignment.0 * frame_half_size;
104
105    let translation = Mat {
106        translate_x: translation_vec.x,
107        translate_y: translation_vec.y,
108        ..Default::default()
109    };
110
111    let scale = Mat { scale_x: scale_vec.x, scale_y: scale_vec.y, ..Default::default() };
112
113    let translation_back = Mat { translate_x: pos.x, translate_y: pos.y, ..Default::default() };
114
115    translation * scale * translation_back
116}