Skip to main content

style/values/computed/
corner_shape.rs

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 https://mozilla.org/MPL/2.0/. */
4
5//! Computed type for the `corner-shape` family of properties.
6
7use crate::derives::*;
8use crate::values::animated::{Animate, Procedure, ToAnimatedZero};
9use crate::values::distance::{ComputeSquaredDistance, SquaredDistance};
10use crate::values::generics::border::GenericCornerShapeRect;
11use std::fmt::{self, Write};
12use style_traits::{CssWriter, ToCss};
13
14/// The computed value for a single corner shape.
15///
16/// Per the spec, the computed value is always `superellipse(K)` where `K` may
17/// be any real value, including +infinity (for `square`) and -infinity
18/// (for `notch`).
19#[derive(
20    Clone, Copy, Debug, MallocSizeOf, PartialEq, ToShmem, ToTyped, ToAnimatedValue, ToResolvedValue,
21)]
22#[repr(C)]
23#[typed(todo_derive_fields)]
24pub struct CornerShape {
25    /// The K parameter from the `superellipse()` function.
26    pub k: f32,
27}
28
29impl CornerShape {
30    /// `round` -> superellipse(1).
31    #[inline]
32    pub fn round() -> Self {
33        Self { k: 1.0 }
34    }
35    /// `bevel` -> superellipse(0).
36    #[inline]
37    pub fn bevel() -> Self {
38        Self { k: 0.0 }
39    }
40    /// `square` -> superellipse(infinity).
41    #[inline]
42    pub fn square() -> Self {
43        Self { k: f32::INFINITY }
44    }
45    /// `notch` -> superellipse(-infinity).
46    #[inline]
47    pub fn notch() -> Self {
48        Self {
49            k: f32::NEG_INFINITY,
50        }
51    }
52    /// `scoop` -> superellipse(-1).
53    #[inline]
54    pub fn scoop() -> Self {
55        Self { k: -1.0 }
56    }
57    /// `squircle` -> superellipse(2).
58    #[inline]
59    pub fn squircle() -> Self {
60        Self { k: 2.0 }
61    }
62
63    /// Whether this corner is the default `round` shape (K == 1).
64    #[inline]
65    pub fn is_round(&self) -> bool {
66        self.k == 1.0
67    }
68}
69
70impl ToCss for CornerShape {
71    fn to_css<W: Write>(&self, dest: &mut CssWriter<W>) -> fmt::Result {
72        dest.write_str("superellipse(")?;
73        if self.k == f32::INFINITY {
74            dest.write_str("infinity")?;
75        } else if self.k == f32::NEG_INFINITY {
76            dest.write_str("-infinity")?;
77        } else {
78            self.k.to_css(dest)?;
79        }
80        dest.write_char(')')
81    }
82}
83
84/// Compute the "normalized superellipse half corner" for a superellipse
85/// parameter `s`, per
86/// <https://drafts.csswg.org/css-borders-4/#corner-shape-interpolation>.
87fn s_to_interpolation_value(s: f32) -> f32 {
88    if s == f32::NEG_INFINITY {
89        return 0.0;
90    }
91    if s == f32::INFINITY {
92        return 1.0;
93    }
94    let k = 0.5f32.powf(s.abs());
95    let convex_half_corner = 0.5f32.powf(k);
96    if s < 0.0 {
97        1.0 - convex_half_corner
98    } else {
99        convex_half_corner
100    }
101}
102
103/// Inverse of `s_to_interpolation_value`.
104fn interpolation_value_to_s(v: f32) -> f32 {
105    if v <= 0.0 {
106        return f32::NEG_INFINITY;
107    }
108    if v >= 1.0 {
109        return f32::INFINITY;
110    }
111    let convex_half_corner = if v < 0.5 { 1.0 - v } else { v };
112    let k = 0.5f32.ln() / convex_half_corner.ln();
113    let s = k.log2();
114    if v < 0.5 {
115        -s
116    } else {
117        s
118    }
119}
120
121impl Animate for CornerShape {
122    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
123        let a = s_to_interpolation_value(self.k);
124        let b = s_to_interpolation_value(other.k);
125        let interp = a.animate(&b, procedure)?;
126        Ok(CornerShape {
127            k: interpolation_value_to_s(interp),
128        })
129    }
130}
131
132impl ComputeSquaredDistance for CornerShape {
133    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
134        let a = s_to_interpolation_value(self.k);
135        let b = s_to_interpolation_value(other.k);
136        a.compute_squared_distance(&b)
137    }
138}
139
140impl ToAnimatedZero for CornerShape {
141    fn to_animated_zero(&self) -> Result<Self, ()> {
142        Ok(CornerShape::round())
143    }
144}
145
146/// The four computed corner shapes for an element.
147pub type CornerShapeRect = GenericCornerShapeRect<CornerShape>;
148
149impl CornerShapeRect {
150    /// Initial value: `round` on every corner.
151    #[inline]
152    pub fn round_all() -> Self {
153        Self::all(CornerShape::round())
154    }
155}