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