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