basic_shape.rs (10144B)
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 10 use crate::values::animated::{Animate, Procedure}; 11 use crate::values::computed::angle::Angle; 12 use crate::values::computed::url::ComputedUrl; 13 use crate::values::computed::{Image, LengthPercentage, Position}; 14 use crate::values::generics::basic_shape as generic; 15 use crate::values::generics::basic_shape::ShapePosition; 16 use crate::values::specified::svg_path::{CoordPair, PathCommand}; 17 use crate::values::CSSFloat; 18 19 /// A computed alias for FillRule. 20 pub use crate::values::generics::basic_shape::FillRule; 21 22 /// A computed `clip-path` value. 23 pub type ClipPath = generic::GenericClipPath<BasicShape, ComputedUrl>; 24 25 /// A computed `shape-outside` value. 26 pub type ShapeOutside = generic::GenericShapeOutside<BasicShape, Image>; 27 28 /// A computed basic shape. 29 pub type BasicShape = generic::GenericBasicShape<Angle, Position, LengthPercentage, InsetRect>; 30 31 /// The computed value of `inset()`. 32 pub type InsetRect = generic::GenericInsetRect<LengthPercentage>; 33 34 /// A computed circle. 35 pub type Circle = generic::Circle<LengthPercentage>; 36 37 /// A computed ellipse. 38 pub type Ellipse = generic::Ellipse<LengthPercentage>; 39 40 /// The computed value of `ShapeRadius`. 41 pub type ShapeRadius = generic::GenericShapeRadius<LengthPercentage>; 42 43 /// The computed value of `shape()`. 44 pub type Shape = generic::Shape<Angle, Position, LengthPercentage>; 45 46 /// The computed value of `ShapeCommand`. 47 pub type ShapeCommand = generic::GenericShapeCommand<Angle, Position, LengthPercentage>; 48 49 /// The computed value of `PathOrShapeFunction`. 50 pub type PathOrShapeFunction = 51 generic::GenericPathOrShapeFunction<Angle, Position, LengthPercentage>; 52 53 /// The computed value of `CoordinatePair`. 54 pub type CoordinatePair = generic::CoordinatePair<LengthPercentage>; 55 56 /// The computed value of 'ControlPoint'. 57 pub type ControlPoint = generic::ControlPoint<Position, LengthPercentage>; 58 59 /// The computed value of 'RelativeControlPoint'. 60 pub type RelativeControlPoint = generic::RelativeControlPoint<LengthPercentage>; 61 62 /// The computed value of 'CommandEndPoint'. 63 pub type CommandEndPoint = generic::CommandEndPoint<Position, LengthPercentage>; 64 65 /// The computed value of hline and vline's endpoint. 66 pub type AxisEndPoint = generic::AxisEndPoint<LengthPercentage>; 67 68 /// Animate from `Shape` to `Path`, and vice versa. 69 macro_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 // Check fill-rule. 78 if $from.fill != $to.fill { 79 return Err(()); 80 } 81 82 // Check the list of commands. (This is a specialized lists::by_computed_value::animate().) 83 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 103 impl Animate for PathOrShapeFunction { 104 #[inline] 105 fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> { 106 // Per spec, commands are "the same" if they use the same command keyword, and use the same 107 // <by-to> keyword. For curve and smooth, they also must have the same number of control 108 // points. Therefore, we don't have to do normalization here. (Note that we do 109 // normalization if we animate from path() to path(). See svg_path.rs for more details.) 110 // 111 // https://drafts.csswg.org/css-shapes-2/#interpolating-shape 112 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 from shape() to path(). We convert each PathCommand into ShapeCommand, 121 // and return shape(). 122 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 from path() to shape(). We convert each PathCommand into ShapeCommand, 133 // and return shape(). 134 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 147 impl 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 203 impl 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 214 impl 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 225 impl 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 235 impl 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 254 impl 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 267 impl 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 }