1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use crate::math::{self, Aabb, Mat};

#[derive(Clone, Copy, Debug)]
pub enum Fit {
    Fill,
    Contain,
    Cover,
    FitWidth,
    FitHeight,
    None,
    ScaleDown,
}

#[derive(Clone, Copy, Debug)]
pub struct Alignment(math::Vec);

impl Alignment {
    pub const fn new(x: f32, y: f32) -> Self {
        Self(math::Vec::new(x, y))
    }

    pub const fn top_left() -> Self {
        Self::new(-1.0, -1.0)
    }

    pub const fn top_center() -> Self {
        Self::new(0.0, -1.0)
    }

    pub const fn top_right() -> Self {
        Self::new(1.0, -1.0)
    }

    pub const fn center_left() -> Self {
        Self::new(-1.0, 0.0)
    }

    pub const fn center() -> Self {
        Self::new(0.0, 0.0)
    }

    pub const fn center_right() -> Self {
        Self::new(1.0, 0.0)
    }

    pub const fn bottom_left() -> Self {
        Self::new(-1.0, 1.0)
    }

    pub const fn bottom_center() -> Self {
        Self::new(0.0, 1.0)
    }

    pub const fn bottom_right() -> Self {
        Self::new(1.0, 1.0)
    }
}

pub fn align(fit: Fit, alignment: Alignment, frame: Aabb, content: Aabb) -> Mat {
    let content_size = content.size();
    let conent_half_size = content_size * 0.5;

    let pos = -content.min - conent_half_size - alignment.0 * conent_half_size;

    let scale_vec = match fit {
        Fit::Fill => frame.size() / content_size,
        Fit::Contain => {
            let scaled = frame.size() / content_size;
            let min_scale = scaled.x.min(scaled.y);

            math::Vec::new(min_scale, min_scale)
        }
        Fit::Cover => {
            let scaled = frame.size() / content_size;
            let max_scale = scaled.x.max(scaled.y);

            math::Vec::new(max_scale, max_scale)
        }
        Fit::FitWidth => {
            let min_scale = frame.size().x / content_size.x;

            math::Vec::new(min_scale, min_scale)
        }
        Fit::FitHeight => {
            let min_scale = frame.size().y / content_size.y;

            math::Vec::new(min_scale, min_scale)
        }
        Fit::None => math::Vec::new(1.0, 1.0),
        Fit::ScaleDown => {
            let scaled = frame.size() / content_size;
            let min_scale = scaled.x.min(scaled.y).min(1.0);

            math::Vec::new(min_scale, min_scale)
        }
    };

    let frame_half_size = frame.size() * 0.5;
    let translation_vec = frame.min + frame_half_size + alignment.0 * frame_half_size;

    let translation = Mat {
        translate_x: translation_vec.x,
        translate_y: translation_vec.y,
        ..Default::default()
    };

    let scale = Mat { scale_x: scale_vec.x, scale_y: scale_vec.y, ..Default::default() };

    let translation_back = Mat { translate_x: pos.x, translate_y: pos.y, ..Default::default() };

    translation * scale * translation_back
}