Skip to main content

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