style/values/computed/
basic_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//! CSS handling for the computed value of
6//! [`basic-shape`][basic-shape]s
7//!
8//! [basic-shape]: https://drafts.csswg.org/css-shapes/#typedef-basic-shape
9
10use crate::values::animated::{Animate, Procedure};
11use crate::values::computed::angle::Angle;
12use crate::values::computed::url::ComputedUrl;
13use crate::values::computed::{Image, LengthPercentage, NonNegativeLengthPercentage, Position};
14use crate::values::generics::basic_shape as generic;
15use crate::values::specified::svg_path::{CoordPair, PathCommand};
16
17/// A computed alias for FillRule.
18pub use crate::values::generics::basic_shape::FillRule;
19
20/// A computed `clip-path` value.
21pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>;
22
23/// A computed `shape-outside` value.
24pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
25
26/// A computed basic shape.
27pub type BasicShape = generic::GenericBasicShape<
28    Angle,
29    Position,
30    LengthPercentage,
31    NonNegativeLengthPercentage,
32    InsetRect,
33>;
34
35/// The computed value of `inset()`.
36pub type InsetRect = generic::GenericInsetRect<LengthPercentage, NonNegativeLengthPercentage>;
37
38/// A computed circle.
39pub type Circle = generic::Circle<Position, NonNegativeLengthPercentage>;
40
41/// A computed ellipse.
42pub type Ellipse = generic::Ellipse<Position, NonNegativeLengthPercentage>;
43
44/// The computed value of `ShapeRadius`.
45pub type ShapeRadius = generic::GenericShapeRadius<NonNegativeLengthPercentage>;
46
47/// The computed value of `shape()`.
48pub type Shape = generic::Shape<Angle, LengthPercentage>;
49
50/// The computed value of `ShapeCommand`.
51pub type ShapeCommand = generic::GenericShapeCommand<Angle, LengthPercentage>;
52
53/// The computed value of `PathOrShapeFunction`.
54pub type PathOrShapeFunction = generic::GenericPathOrShapeFunction<Angle, LengthPercentage>;
55
56/// The computed value of `CoordinatePair`.
57pub type CoordinatePair = generic::CoordinatePair<LengthPercentage>;
58
59/// Animate from `Shape` to `Path`, and vice versa.
60macro_rules! animate_shape {
61    (
62        $from:ident,
63        $to:ident,
64        $procedure:ident,
65        $from_as_shape:tt,
66        $to_as_shape:tt
67    ) => {{
68        // Check fill-rule.
69        if $from.fill != $to.fill {
70            return Err(());
71        }
72
73        // Check the list of commands. (This is a specialized lists::by_computed_value::animate().)
74        let from_cmds = $from.commands();
75        let to_cmds = $to.commands();
76        if from_cmds.len() != to_cmds.len() {
77            return Err(());
78        }
79        let commands = from_cmds
80            .iter()
81            .zip(to_cmds.iter())
82            .map(|(from_cmd, to_cmd)| {
83                $from_as_shape(from_cmd).animate(&$to_as_shape(to_cmd), $procedure)
84            })
85            .collect::<Result<Vec<ShapeCommand>, ()>>()?;
86
87        Ok(Shape {
88            fill: $from.fill,
89            commands: commands.into(),
90        })
91    }};
92}
93
94impl Animate for PathOrShapeFunction {
95    #[inline]
96    fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
97        // Per spec, commands are "the same" if they use the same command keyword, and use the same
98        // <by-to> keyword. For curve and smooth, they also must have the same number of control
99        // points. Therefore, we don't have to do normalization here. (Note that we do
100        // normalization if we animate from path() to path(). See svg_path.rs for more details.)
101        //
102        // https://drafts.csswg.org/css-shapes-2/#interpolating-shape
103        match (self, other) {
104            (Self::Path(ref from), Self::Path(ref to)) => {
105                from.animate(to, procedure).map(Self::Path)
106            },
107            (Self::Shape(ref from), Self::Shape(ref to)) => {
108                from.animate(to, procedure).map(Self::Shape)
109            },
110            (Self::Shape(ref from), Self::Path(ref to)) => {
111                // Animate from shape() to path(). We convert each PathCommand into ShapeCommand,
112                // and return shape().
113                animate_shape!(
114                    from,
115                    to,
116                    procedure,
117                    (|shape_cmd| shape_cmd),
118                    (|path_cmd| ShapeCommand::from(path_cmd))
119                )
120                .map(Self::Shape)
121            },
122            (Self::Path(ref from), Self::Shape(ref to)) => {
123                // Animate from path() to shape(). We convert each PathCommand into ShapeCommand,
124                // and return shape().
125                animate_shape!(
126                    from,
127                    to,
128                    procedure,
129                    (|path_cmd| ShapeCommand::from(path_cmd)),
130                    (|shape_cmd| shape_cmd)
131                )
132                .map(Self::Shape)
133            },
134        }
135    }
136}
137
138impl From<&PathCommand> for ShapeCommand {
139    #[inline]
140    fn from(path: &PathCommand) -> Self {
141        use crate::values::computed::CSSPixelLength;
142        match path {
143            &PathCommand::Close => Self::Close,
144            &PathCommand::Move { by_to, ref point } => Self::Move {
145                by_to,
146                point: point.into(),
147            },
148            &PathCommand::Line { by_to, ref point } => Self::Move {
149                by_to,
150                point: point.into(),
151            },
152            &PathCommand::HLine { by_to, x } => Self::HLine {
153                by_to,
154                x: LengthPercentage::new_length(CSSPixelLength::new(x)),
155            },
156            &PathCommand::VLine { by_to, y } => Self::VLine {
157                by_to,
158                y: LengthPercentage::new_length(CSSPixelLength::new(y)),
159            },
160            &PathCommand::CubicCurve {
161                by_to,
162                ref point,
163                ref control1,
164                ref control2,
165            } => Self::CubicCurve {
166                by_to,
167                point: point.into(),
168                control1: control1.into(),
169                control2: control2.into(),
170            },
171            &PathCommand::QuadCurve {
172                by_to,
173                ref point,
174                ref control1,
175            } => Self::QuadCurve {
176                by_to,
177                point: point.into(),
178                control1: control1.into(),
179            },
180            &PathCommand::SmoothCubic {
181                by_to,
182                ref point,
183                ref control2,
184            } => Self::SmoothCubic {
185                by_to,
186                point: point.into(),
187                control2: control2.into(),
188            },
189            &PathCommand::SmoothQuad { by_to, ref point } => Self::SmoothQuad {
190                by_to,
191                point: point.into(),
192            },
193            &PathCommand::Arc {
194                by_to,
195                ref point,
196                ref radii,
197                arc_sweep,
198                arc_size,
199                rotate,
200            } => Self::Arc {
201                by_to,
202                point: point.into(),
203                radii: radii.into(),
204                arc_sweep,
205                arc_size,
206                rotate: Angle::from_degrees(rotate),
207            },
208        }
209    }
210}
211
212impl From<&CoordPair> for CoordinatePair {
213    #[inline]
214    fn from(p: &CoordPair) -> Self {
215        use crate::values::computed::CSSPixelLength;
216        Self::new(
217            LengthPercentage::new_length(CSSPixelLength::new(p.x)),
218            LengthPercentage::new_length(CSSPixelLength::new(p.y)),
219        )
220    }
221}