1use 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
19pub use crate::values::generics::basic_shape::FillRule;
21
22pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>;
24
25pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>;
27
28pub type BasicShape = generic::GenericBasicShape<Angle, Position, LengthPercentage, InsetRect>;
30
31pub type InsetRect = generic::GenericInsetRect<LengthPercentage>;
33
34pub type Circle = generic::Circle<LengthPercentage>;
36
37pub type Ellipse = generic::Ellipse<LengthPercentage>;
39
40pub type ShapeRadius = generic::GenericShapeRadius<LengthPercentage>;
42
43pub type Shape = generic::Shape<Angle, Position, LengthPercentage>;
45
46pub type ShapeCommand = generic::GenericShapeCommand<Angle, Position, LengthPercentage>;
48
49pub type PathOrShapeFunction =
51 generic::GenericPathOrShapeFunction<Angle, Position, LengthPercentage>;
52
53pub type CoordinatePair = generic::CoordinatePair<LengthPercentage>;
55
56pub type ControlPoint = generic::ControlPoint<Position, LengthPercentage>;
58
59pub type RelativeControlPoint = generic::RelativeControlPoint<LengthPercentage>;
61
62pub type CommandEndPoint = generic::CommandEndPoint<Position, LengthPercentage>;
64
65pub type AxisEndPoint = generic::AxisEndPoint<LengthPercentage>;
67
68macro_rules! animate_shape {
70 (
71 $from:ident,
72 $to:ident,
73 $procedure:ident,
74 $from_as_shape:tt,
75 $to_as_shape:tt
76 ) => {{
77 if $from.fill != $to.fill {
79 return Err(());
80 }
81
82 let from_cmds = $from.commands();
84 let to_cmds = $to.commands();
85 if from_cmds.len() != to_cmds.len() {
86 return Err(());
87 }
88 let commands = from_cmds
89 .iter()
90 .zip(to_cmds.iter())
91 .map(|(from_cmd, to_cmd)| {
92 $from_as_shape(from_cmd).animate(&$to_as_shape(to_cmd), $procedure)
93 })
94 .collect::<Result<Vec<ShapeCommand>, ()>>()?;
95
96 Ok(Shape {
97 fill: $from.fill,
98 commands: commands.into(),
99 })
100 }};
101}
102
103impl Animate for PathOrShapeFunction {
104 #[inline]
105 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
106 match (self, other) {
113 (Self::Path(ref from), Self::Path(ref to)) => {
114 from.animate(to, procedure).map(Self::Path)
115 },
116 (Self::Shape(ref from), Self::Shape(ref to)) => {
117 from.animate(to, procedure).map(Self::Shape)
118 },
119 (Self::Shape(ref from), Self::Path(ref to)) => {
120 animate_shape!(
123 from,
124 to,
125 procedure,
126 (|shape_cmd| shape_cmd),
127 (|path_cmd| ShapeCommand::from(path_cmd))
128 )
129 .map(Self::Shape)
130 },
131 (Self::Path(ref from), Self::Shape(ref to)) => {
132 animate_shape!(
135 from,
136 to,
137 procedure,
138 (|path_cmd| ShapeCommand::from(path_cmd)),
139 (|shape_cmd| shape_cmd)
140 )
141 .map(Self::Shape)
142 },
143 }
144 }
145}
146
147impl From<&PathCommand> for ShapeCommand {
148 #[inline]
149 fn from(path: &PathCommand) -> Self {
150 match path {
151 &PathCommand::Close => Self::Close,
152 &PathCommand::Move { ref point } => Self::Move {
153 point: point.into(),
154 },
155 &PathCommand::Line { ref point } => Self::Move {
156 point: point.into(),
157 },
158 &PathCommand::HLine { ref x } => Self::HLine { x: x.into() },
159 &PathCommand::VLine { ref y } => Self::VLine { y: y.into() },
160 &PathCommand::CubicCurve {
161 ref point,
162 ref control1,
163 ref control2,
164 } => Self::CubicCurve {
165 point: point.into(),
166 control1: control1.into(),
167 control2: control2.into(),
168 },
169 &PathCommand::QuadCurve {
170 ref point,
171 ref control1,
172 } => Self::QuadCurve {
173 point: point.into(),
174 control1: control1.into(),
175 },
176 &PathCommand::SmoothCubic {
177 ref point,
178 ref control2,
179 } => Self::SmoothCubic {
180 point: point.into(),
181 control2: control2.into(),
182 },
183 &PathCommand::SmoothQuad { ref point } => Self::SmoothQuad {
184 point: point.into(),
185 },
186 &PathCommand::Arc {
187 ref point,
188 ref radii,
189 arc_sweep,
190 arc_size,
191 rotate,
192 } => Self::Arc {
193 point: point.into(),
194 radii: radii.into(),
195 arc_sweep,
196 arc_size,
197 rotate: Angle::from_degrees(rotate),
198 },
199 }
200 }
201}
202
203impl From<&CoordPair> for CoordinatePair {
204 #[inline]
205 fn from(p: &CoordPair) -> Self {
206 use crate::values::computed::CSSPixelLength;
207 Self::new(
208 LengthPercentage::new_length(CSSPixelLength::new(p.x)),
209 LengthPercentage::new_length(CSSPixelLength::new(p.y)),
210 )
211 }
212}
213
214impl From<&ShapePosition<CSSFloat>> for Position {
215 #[inline]
216 fn from(p: &ShapePosition<CSSFloat>) -> Self {
217 use crate::values::computed::CSSPixelLength;
218 Self::new(
219 LengthPercentage::new_length(CSSPixelLength::new(p.horizontal)),
220 LengthPercentage::new_length(CSSPixelLength::new(p.vertical)),
221 )
222 }
223}
224
225impl From<&generic::CommandEndPoint<ShapePosition<CSSFloat>, CSSFloat>> for CommandEndPoint {
226 #[inline]
227 fn from(p: &generic::CommandEndPoint<ShapePosition<CSSFloat>, CSSFloat>) -> Self {
228 match p {
229 generic::CommandEndPoint::ToPosition(pos) => Self::ToPosition(pos.into()),
230 generic::CommandEndPoint::ByCoordinate(coord) => Self::ByCoordinate(coord.into()),
231 }
232 }
233}
234
235impl From<&generic::AxisEndPoint<CSSFloat>> for AxisEndPoint {
236 #[inline]
237 fn from(p: &generic::AxisEndPoint<CSSFloat>) -> Self {
238 use crate::values::computed::CSSPixelLength;
239 use generic::AxisPosition;
240 match p {
241 generic::AxisEndPoint::ToPosition(AxisPosition::LengthPercent(lp)) => Self::ToPosition(
242 AxisPosition::LengthPercent(LengthPercentage::new_length(CSSPixelLength::new(*lp))),
243 ),
244 generic::AxisEndPoint::ToPosition(AxisPosition::Keyword(_)) => {
245 unreachable!("Invalid state: SVG path commands cannot contain a keyword.")
246 },
247 generic::AxisEndPoint::ByCoordinate(pos) => {
248 Self::ByCoordinate(LengthPercentage::new_length(CSSPixelLength::new(*pos)))
249 },
250 }
251 }
252}
253
254impl From<&generic::ControlPoint<ShapePosition<CSSFloat>, CSSFloat>> for ControlPoint {
255 #[inline]
256 fn from(p: &generic::ControlPoint<ShapePosition<CSSFloat>, CSSFloat>) -> Self {
257 match p {
258 generic::ControlPoint::Absolute(pos) => Self::Absolute(pos.into()),
259 generic::ControlPoint::Relative(point) => Self::Relative(RelativeControlPoint {
260 coord: CoordinatePair::from(&point.coord),
261 reference: point.reference,
262 }),
263 }
264 }
265}
266
267impl From<&generic::ArcRadii<CSSFloat>> for generic::ArcRadii<LengthPercentage> {
268 #[inline]
269 fn from(p: &generic::ArcRadii<CSSFloat>) -> Self {
270 use crate::values::computed::CSSPixelLength;
271 Self {
272 rx: LengthPercentage::new_length(CSSPixelLength::new(p.rx)),
273 ry: p
274 .ry
275 .map(|v| LengthPercentage::new_length(CSSPixelLength::new(v))),
276 }
277 }
278}