1use super::prelude::*;
2
3#[node]
9#[derive(
10 IntoCursor, ToSpan, SemanticEq, Parse, Peek, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
11)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
13#[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.types.angle"))]
14#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
15#[derive(csskit_derives::NodeWithMetadata)]
16#[metadata(node_kinds = Dimension)]
17pub enum Angle {
18 #[atom(CssAtomSet::Grad)]
19 Grad(T![Dimension]),
20 #[atom(CssAtomSet::Rad)]
21 Rad(T![Dimension]),
22 #[atom(CssAtomSet::Turn)]
23 Turn(T![Dimension]),
24 #[atom(CssAtomSet::Deg)]
25 Deg(T![Dimension]),
26}
27
28impl From<Angle> for f32 {
29 fn from(val: Angle) -> Self {
30 match val {
31 Angle::Grad(f) => f.into(),
32 Angle::Rad(f) => f.into(),
33 Angle::Turn(f) => f.into(),
34 Angle::Deg(f) => f.into(),
35 }
36 }
37}
38
39impl ToNumberValue for Angle {
40 fn to_number_value(&self) -> Option<f32> {
41 Some((*self).into())
42 }
43}
44
45impl ToNormalisedValue for Angle {
46 fn to_normalised_value(&self) -> Option<f32> {
47 Some(self.as_degrees())
48 }
49}
50
51impl Angle {
52 const DEG_GRAD: f32 = 0.9;
53 const DEG_RAD: f32 = 57.295_78;
54 const DEG_TURN: f32 = 360.0;
55
56 pub fn as_degrees(&self) -> f32 {
57 match self {
58 Self::Grad(d) => Into::<f32>::into(*d) * Self::DEG_GRAD,
59 Self::Rad(d) => Into::<f32>::into(*d) * Self::DEG_RAD,
60 Self::Turn(d) => Into::<f32>::into(*d) * Self::DEG_TURN,
61 Self::Deg(d) => (*d).into(),
62 }
63 }
64}
65
66#[node]
67#[derive(
68 IntoCursor, ToSpan, SemanticEq, Parse, Peek, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
69)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
71#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
72#[derive(csskit_derives::NodeWithMetadata)]
73pub enum AngleOrZero {
74 Angle(Angle),
75 #[cfg_attr(feature = "visitable", visit(skip))]
76 Zero(Exact<T![Number], 0>),
77}
78
79#[node]
80#[derive(
81 IntoCursor, ToSpan, SemanticEq, Parse, Peek, ToCursors, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
82)]
83#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
84#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(children))]
85#[derive(csskit_derives::NodeWithMetadata)]
86pub enum AngleOrNumber {
87 Angle(Angle),
88 #[cfg_attr(feature = "visitable", visit(skip))]
89 Number(T![Number]),
90}
91
92impl From<AngleOrZero> for f32 {
93 fn from(val: AngleOrZero) -> Self {
94 match val {
95 AngleOrZero::Angle(f) => f.into(),
96 AngleOrZero::Zero(f) => f.0.into(),
97 }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::CssAtomSet;
105 use css_parse::assert_parse;
106
107 #[test]
108 fn test_writes() {
109 assert_parse!(CssAtomSet::ATOMS, Angle, "0grad");
110 assert_parse!(CssAtomSet::ATOMS, Angle, "0deg");
111 assert_parse!(CssAtomSet::ATOMS, AngleOrZero, "0", AngleOrZero::Zero(_));
112 }
113}