From 6a8a0ec6ec8bb8cf346ee0dd2c45ddcfbee7fbe6 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Tue, 3 May 2022 11:40:27 +0200 Subject: [PATCH] Code Review: Heap is Stack. Unsafe is Good. Spaghetti Code is Style. --- src/frame.rs | 8 ++ src/geom/angle.rs | 45 ---------- src/geom/path.rs | 45 ++++++++++ src/geom/rect.rs | 134 ++++++++++++------------------ src/geom/sides.rs | 13 +++ src/geom/transform.rs | 18 +--- src/library/graphics/shape.rs | 32 +++---- src/library/layout/page.rs | 5 +- src/model/styles.rs | 23 +---- tests/ref/layout/page.png | Bin 7390 -> 7401 bytes tests/typ/graphics/shape-rect.typ | 8 +- tests/typ/layout/page-margin.typ | 8 +- tests/typeset.rs | 2 +- 13 files changed, 146 insertions(+), 195 deletions(-) diff --git a/src/frame.rs b/src/frame.rs index dcaa7581e..80e25f3b4 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -40,6 +40,14 @@ impl Frame { self.elements.insert(0, (pos, element)); } + /// Add multiple elements at a position in the background. + pub fn prepend_multiple(&mut self, insert: I) + where + I: IntoIterator, + { + self.elements.splice(0 .. 0, insert); + } + /// Add an element at a position in the foreground. pub fn push(&mut self, pos: Point, element: Element) { self.elements.push((pos, element)); diff --git a/src/geom/angle.rs b/src/geom/angle.rs index 65270ebd5..888442f7a 100644 --- a/src/geom/angle.rs +++ b/src/geom/angle.rs @@ -64,51 +64,6 @@ impl Angle { pub fn cos(self) -> f64 { self.to_rad().cos() } - - /// Get the control points for a bezier curve that describes a circular arc - /// of this angle with the given radius. - pub fn bezier_arc( - self, - radius: Length, - rotate: bool, - mirror_x: bool, - mirror_y: bool, - ) -> [Point; 4] { - let end = Point::new(self.cos() * radius - radius, self.sin() * radius); - let center = Point::new(-radius, Length::zero()); - - let mut ts = if mirror_y { - Transform::mirror_y() - } else { - Transform::identity() - }; - - if mirror_x { - ts = ts.pre_concat(Transform::mirror_x()); - } - - if rotate { - ts = ts.pre_concat(Transform::rotate(Angle::deg(90.0))); - } - - let a = center * -1.0; - let b = end - center; - - let q1 = a.x.to_raw() * a.x.to_raw() + a.y.to_raw() * a.y.to_raw(); - let q2 = q1 + a.x.to_raw() * b.x.to_raw() + a.y.to_raw() * b.y.to_raw(); - let k2 = (4.0 / 3.0) * ((2.0 * q1 * q2).sqrt() - q2) - / (a.x.to_raw() * b.y.to_raw() - a.y.to_raw() * b.x.to_raw()); - - let control_1 = Point::new(center.x + a.x - k2 * a.y, center.y + a.y + k2 * a.x); - let control_2 = Point::new(center.x + b.x + k2 * b.y, center.y + b.y - k2 * b.x); - - [ - Point::zero(), - control_1.transform(ts), - control_2.transform(ts), - end.transform(ts), - ] - } } impl Numeric for Angle { diff --git a/src/geom/path.rs b/src/geom/path.rs index 836be1b49..721cc20bd 100644 --- a/src/geom/path.rs +++ b/src/geom/path.rs @@ -71,3 +71,48 @@ impl Path { self.0.push(PathElement::ClosePath); } } + +/// Get the control points for a bezier curve that describes a circular arc +/// of this angle with the given radius. +pub fn bezier_arc( + angle: Angle, + radius: Length, + rotate: bool, + mirror_x: bool, + mirror_y: bool, +) -> [Point; 4] { + let end = Point::new(angle.cos() * radius - radius, angle.sin() * radius); + let center = Point::new(-radius, Length::zero()); + + let mut ts = if mirror_y { + Transform::mirror_y() + } else { + Transform::identity() + }; + + if mirror_x { + ts = ts.pre_concat(Transform::mirror_x()); + } + + if rotate { + ts = ts.pre_concat(Transform::rotate(Angle::deg(90.0))); + } + + let a = center * -1.0; + let b = end - center; + + let q1 = a.x.to_raw() * a.x.to_raw() + a.y.to_raw() * a.y.to_raw(); + let q2 = q1 + a.x.to_raw() * b.x.to_raw() + a.y.to_raw() * b.y.to_raw(); + let k2 = (4.0 / 3.0) * ((2.0 * q1 * q2).sqrt() - q2) + / (a.x.to_raw() * b.y.to_raw() - a.y.to_raw() * b.x.to_raw()); + + let control_1 = Point::new(center.x + a.x - k2 * a.y, center.y + a.y + k2 * a.x); + let control_2 = Point::new(center.x + b.x + k2 * b.y, center.y + b.y - k2 * b.x); + + [ + Point::zero(), + control_1.transform(ts), + control_2.transform(ts), + end.transform(ts), + ] +} diff --git a/src/geom/rect.rs b/src/geom/rect.rs index f0da2db61..aa670f0a5 100644 --- a/src/geom/rect.rs +++ b/src/geom/rect.rs @@ -3,7 +3,7 @@ use super::*; use std::mem; /// A rectangle with rounded corners. -#[derive(Debug, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub struct Rect { size: Size, radius: Sides, @@ -19,7 +19,7 @@ impl Rect { /// in the foreground. The function will output multiple items if the stroke /// properties differ by side. pub fn shapes( - &self, + self, fill: Option, stroke: Sides>, ) -> Vec { @@ -28,48 +28,64 @@ impl Rect { res.push(Shape { geometry: self.fill_geometry(), fill, - stroke: stroke.is_uniform().then(|| stroke.top).flatten(), + stroke: if stroke.is_uniform() { stroke.top } else { None }, }); } if !stroke.is_uniform() { - for (path, stroke) in self.stroke_segments(Some(stroke)) { - if !stroke.is_some() { - continue; + for (path, stroke) in self.stroke_segments(stroke) { + if stroke.is_some() { + res.push(Shape { + geometry: Geometry::Path(path), + fill: None, + stroke, + }); } - res.push(Shape { - geometry: Geometry::Path(path), - fill: None, - stroke, - }); } } res } + /// Output the shape of the rectangle as a path or primitive rectangle, + /// depending on whether it is rounded. + fn fill_geometry(self) -> Geometry { + if self.radius.iter().copied().all(Length::is_zero) { + Geometry::Rect(self.size) + } else { + let mut paths = self.stroke_segments(Sides::splat(None)); + assert_eq!(paths.len(), 1); + + Geometry::Path(paths.pop().unwrap().0) + } + } + /// Output the minimum number of paths along the rectangles border. fn stroke_segments( - &self, - strokes: Option>>, + self, + strokes: Sides>, ) -> Vec<(Path, Option)> { - let strokes = strokes.unwrap_or_else(|| Sides::splat(None)); let mut res = vec![]; - let mut connection = Connection::None; + let mut connection = Connection::default(); let mut path = Path::new(); let mut always_continuous = true; for side in [Side::Top, Side::Right, Side::Bottom, Side::Left] { - let radius = [self.radius.get(side.next_ccw()), self.radius.get(side)]; + let is_continuous = strokes.get(side) == strokes.get(side.next_cw()); + connection = connection.advance(is_continuous && side != Side::Left); + always_continuous &= is_continuous; - let stroke_continuity = strokes.get(side) == strokes.get(side.next_cw()); - connection = connection.advance(stroke_continuity && side != Side::Left); - always_continuous &= stroke_continuity; + draw_side( + &mut path, + side, + self.size, + self.radius.get(side.next_ccw()), + self.radius.get(side), + connection, + ); - draw_side(&mut path, side, self.size, radius[0], radius[1], connection); - - if !stroke_continuity { + if !is_continuous { res.push((mem::take(&mut path), strokes.get(side))); } } @@ -84,19 +100,6 @@ impl Rect { res } - - /// Output the shape of the rectangle as a path or primitive rectangle, - /// depending on whether it is rounded. - fn fill_geometry(&self) -> Geometry { - if self.radius.iter().copied().all(Length::is_zero) { - Geometry::Rect(self.size) - } else { - let mut paths = self.stroke_segments(None); - assert_eq!(paths.len(), 1); - - Geometry::Path(paths.pop().unwrap().0) - } - } } /// Draws one side of the rounded rectangle. Will always draw the left arc. The @@ -110,19 +113,18 @@ fn draw_side( connection: Connection, ) { let reversed = |angle: Angle, radius, rotate, mirror_x, mirror_y| { - let [a, b, c, d] = angle.bezier_arc(radius, rotate, mirror_x, mirror_y); + let [a, b, c, d] = bezier_arc(angle, radius, rotate, mirror_x, mirror_y); [d, c, b, a] }; - let angle_left = Angle::deg(if connection.left() { 90.0 } else { 45.0 }); - let angle_right = Angle::deg(if connection.right() { 90.0 } else { 45.0 }); + let angle_left = Angle::deg(if connection.prev { 90.0 } else { 45.0 }); + let angle_right = Angle::deg(if connection.next { 90.0 } else { 45.0 }); let (arc1, arc2) = match side { Side::Top => { let arc1 = reversed(angle_left, radius_left, true, true, false) .map(|x| x + Point::with_x(radius_left)); - let arc2 = (-angle_right) - .bezier_arc(radius_right, true, true, false) + let arc2 = bezier_arc(-angle_right, radius_right, true, true, false) .map(|x| x + Point::with_x(size.x - radius_right)); (arc1, arc2) @@ -131,8 +133,7 @@ fn draw_side( let arc1 = reversed(-angle_left, radius_left, false, false, false) .map(|x| x + Point::new(size.x, radius_left)); - let arc2 = angle_right - .bezier_arc(radius_right, false, false, false) + let arc2 = bezier_arc(angle_right, radius_right, false, false, false) .map(|x| x + Point::new(size.x, size.y - radius_right)); (arc1, arc2) @@ -141,8 +142,7 @@ fn draw_side( let arc1 = reversed(-angle_left, radius_left, true, false, false) .map(|x| x + Point::new(size.x - radius_left, size.y)); - let arc2 = angle_right - .bezier_arc(radius_right, true, false, false) + let arc2 = bezier_arc(angle_right, radius_right, true, false, false) .map(|x| x + Point::new(radius_right, size.y)); (arc1, arc2) @@ -151,15 +151,14 @@ fn draw_side( let arc1 = reversed(angle_left, radius_left, false, false, true) .map(|x| x + Point::with_y(size.y - radius_left)); - let arc2 = (-angle_right) - .bezier_arc(radius_right, false, false, true) + let arc2 = bezier_arc(-angle_right, radius_right, false, false, true) .map(|x| x + Point::with_y(radius_right)); (arc1, arc2) } }; - if !connection.left() { + if !connection.prev { path.move_to(if radius_left.is_zero() { arc1[3] } else { arc1[0] }); } @@ -169,51 +168,24 @@ fn draw_side( path.line_to(arc2[0]); - if !connection.right() && !radius_right.is_zero() { + if !connection.next && !radius_right.is_zero() { path.cubic_to(arc2[1], arc2[2], arc2[3]); } } /// A state machine that indicates which sides of the border strokes in a 2D /// polygon are connected to their neighboring sides. -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -enum Connection { - None, - Left, - Right, - Both, +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +struct Connection { + prev: bool, + next: bool, } impl Connection { /// Advance to the next clockwise side of the polygon. The argument /// indicates whether the border is connected on the right side of the next /// edge. - pub fn advance(self, right: bool) -> Self { - match self { - Self::Right | Self::Both => { - if right { - Self::Both - } else { - Self::Left - } - } - Self::Left | Self::None => { - if right { - Self::Right - } else { - Self::None - } - } - } - } - - /// Whether there is a connection on the left. - fn left(self) -> bool { - matches!(self, Self::Left | Self::Both) - } - - /// Whether there is a connection on the right. - fn right(self) -> bool { - matches!(self, Self::Right | Self::Both) + pub fn advance(self, next: bool) -> Self { + Self { prev: self.next, next } } } diff --git a/src/geom/sides.rs b/src/geom/sides.rs index f214a1bf3..555bbd62b 100644 --- a/src/geom/sides.rs +++ b/src/geom/sides.rs @@ -45,6 +45,19 @@ impl Sides { } } + /// Zip two instances into an instance. + pub fn zip(self, other: Sides, mut f: F) -> Sides + where + F: FnMut(T, V, Side) -> W, + { + Sides { + left: f(self.left, other.left, Side::Left), + top: f(self.top, other.top, Side::Top), + right: f(self.right, other.right, Side::Right), + bottom: f(self.bottom, other.bottom, Side::Bottom), + } + } + /// Returns an iterator over the sides. pub fn iter(&self) -> impl Iterator { [&self.left, &self.top, &self.right, &self.bottom].into_iter() diff --git a/src/geom/transform.rs b/src/geom/transform.rs index de2a97818..961ba4877 100644 --- a/src/geom/transform.rs +++ b/src/geom/transform.rs @@ -26,26 +26,12 @@ impl Transform { /// Transform by mirroring along the x-axis. pub fn mirror_x() -> Self { - Self { - sx: Ratio::one(), - ky: Ratio::zero(), - kx: Ratio::zero(), - sy: -Ratio::one(), - tx: Length::zero(), - ty: Length::zero(), - } + Self::scale(Ratio::one(), -Ratio::one()) } /// Transform by mirroring along the y-axis. pub fn mirror_y() -> Self { - Self { - sx: -Ratio::one(), - ky: Ratio::zero(), - kx: Ratio::zero(), - sy: Ratio::one(), - tx: Length::zero(), - ty: Length::zero(), - } + Self::scale(-Ratio::one(), Ratio::one()) } /// A translate transform. diff --git a/src/library/graphics/shape.rs b/src/library/graphics/shape.rs index a5523a2ea..40b6e1e3e 100644 --- a/src/library/graphics/shape.rs +++ b/src/library/graphics/shape.rs @@ -78,7 +78,7 @@ impl ShapeNode { styles.set_opt(Self::INSET, args.named("inset")?); styles.set_opt(Self::OUTSET, args.named("outset")?); - if S != CIRCLE { + if !is_round(S) { styles.set_opt(Self::RADIUS, args.named("radius")?); } @@ -97,10 +97,7 @@ impl Layout for ShapeNode { if let Some(child) = &self.0 { let mut inset = styles.get(Self::INSET); if is_round(S) { - inset = inset.map(|mut side| { - side.rel += Ratio::new(0.5 - SQRT_2 / 4.0); - side - }); + inset = inset.map(|side| side + Ratio::new(0.5 - SQRT_2 / 4.0)); } // Pad the child. @@ -158,18 +155,8 @@ impl Layout for ShapeNode { } }; - let outset = styles.get(Self::OUTSET); - let outset = Sides { - left: outset.left.relative_to(frame.size.x), - top: outset.top.relative_to(frame.size.y), - right: outset.right.relative_to(frame.size.x), - bottom: outset.bottom.relative_to(frame.size.y), - }; - - let size = Spec::new( - frame.size.x + outset.left + outset.right, - frame.size.y + outset.top + outset.bottom, - ); + let outset = styles.get(Self::OUTSET).relative_to(frame.size); + let size = frame.size + outset.sum_by_axis(); let radius = styles .get(Self::RADIUS) @@ -186,11 +173,12 @@ impl Layout for ShapeNode { }; frame.prepend(pos, Element::Shape(shape)); } else { - for shape in - Rect::new(size, radius).shapes(fill, stroke).into_iter().rev() - { - frame.prepend(pos, Element::Shape(shape)); - } + frame.prepend_multiple( + Rect::new(size, radius) + .shapes(fill, stroke) + .into_iter() + .map(|x| (pos, Element::Shape(x))), + ) } } diff --git a/src/library/layout/page.rs b/src/library/layout/page.rs index 801a137d9..c8495e646 100644 --- a/src/library/layout/page.rs +++ b/src/library/layout/page.rs @@ -20,7 +20,8 @@ impl PageNode { /// The page margin. #[property(fold)] - pub const MARGINS: Sides>> = Sides::splat(Smart::Auto); + pub const MARGINS: Sides>>> = + Sides::splat(Smart::Auto); /// How many columns the page has. pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap(); @@ -48,9 +49,7 @@ impl PageNode { styles.set_opt(Self::WIDTH, args.named("width")?); styles.set_opt(Self::HEIGHT, args.named("height")?); - styles.set_opt(Self::MARGINS, args.named("margins")?); - styles.set_opt(Self::FLIPPED, args.named("flipped")?); styles.set_opt(Self::FILL, args.named("fill")?); styles.set_opt(Self::COLUMNS, args.named("columns")?); diff --git a/src/model/styles.rs b/src/model/styles.rs index 1fddfd0ed..ae4c1586e 100644 --- a/src/model/styles.rs +++ b/src/model/styles.rs @@ -466,12 +466,7 @@ where type Output = Sides; fn fold(self, outer: Self::Output) -> Self::Output { - Sides { - left: self.left.fold(outer.left), - top: self.top.fold(outer.top), - right: self.right.fold(outer.right), - bottom: self.bottom.fold(outer.bottom), - } + self.zip(outer, |inner, outer, _| inner.fold(outer)) } } @@ -479,25 +474,15 @@ impl Fold for Sides>> { type Output = Sides>; fn fold(self, outer: Self::Output) -> Self::Output { - Sides { - left: self.left.unwrap_or(outer.left), - top: self.top.unwrap_or(outer.top), - right: self.right.unwrap_or(outer.right), - bottom: self.bottom.unwrap_or(outer.bottom), - } + self.zip(outer, |inner, outer, _| inner.unwrap_or(outer)) } } -impl Fold for Sides>> { +impl Fold for Sides>>> { type Output = Sides>>; fn fold(self, outer: Self::Output) -> Self::Output { - Sides { - left: self.left.or(outer.left), - top: self.top.or(outer.top), - right: self.right.or(outer.right), - bottom: self.bottom.or(outer.bottom), - } + self.zip(outer, |inner, outer, _| inner.unwrap_or(outer)) } } diff --git a/tests/ref/layout/page.png b/tests/ref/layout/page.png index e0618e589a9cb342f5bbb435ef5207126400a8d7..35716e4deca33ecca7c038f9a6938c594597ddfa 100644 GIT binary patch literal 7401 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 F{|D)1AkF{) literal 7390 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 diff --git a/tests/typ/graphics/shape-rect.typ b/tests/typ/graphics/shape-rect.typ index 3d1576753..a29550b52 100644 --- a/tests/typ/graphics/shape-rect.typ +++ b/tests/typ/graphics/shape-rect.typ @@ -35,20 +35,20 @@ // Different strokes. [ - #set rect(stroke: (right: red,)) + #set rect(stroke: (right: red)) #rect(width: 100%, fill: lime, stroke: (x: 5pt, y: 1pt)) ] --- // Outset padding. -#let inline-code(body) = [ +#show node: raw as [ #set text("IBM Plex Mono", 8pt) #h(.7em, weak: true) - #rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243), body) + #rect(radius: 3pt, outset: (y: 3pt, x: 2.5pt), fill: rgb(239, 241, 243))[{node.text}] #h(.7em, weak: true) ] -Use the #inline-code[\*const ptr] pointer. +Use the `*const ptr` pointer. --- // Error: 15-38 unexpected key "cake" diff --git a/tests/typ/layout/page-margin.typ b/tests/typ/layout/page-margin.typ index e30518b02..290c40819 100644 --- a/tests/typ/layout/page-margin.typ +++ b/tests/typ/layout/page-margin.typ @@ -11,10 +11,10 @@ --- // Set individual margins. #set page(height: 40pt) -[#set page(margins: (left: 0pt,)); #align(left)[Left]] -[#set page(margins: (right: 0pt,)); #align(right)[Right]] -[#set page(margins: (top: 0pt,)); #align(top)[Top]] -[#set page(margins: (bottom: 0pt,)); #align(bottom)[Bottom]] +[#set page(margins: (left: 0pt)); #align(left)[Left]] +[#set page(margins: (right: 0pt)); #align(right)[Right]] +[#set page(margins: (top: 0pt)); #align(top)[Top]] +[#set page(margins: (bottom: 0pt)); #align(bottom)[Bottom]] // Ensure that specific margins override general margins. [#set page(margins: (rest: 0pt, left: 20pt)); Overriden] diff --git a/tests/typeset.rs b/tests/typeset.rs index d3f7844b1..b0452163b 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -66,7 +66,7 @@ fn main() { styles.set(PageNode::HEIGHT, Smart::Auto); styles.set( PageNode::MARGINS, - Sides::splat(Smart::Custom(Length::pt(10.0).into())), + Sides::splat(Some(Smart::Custom(Length::pt(10.0).into()))), ); styles.set(TextNode::SIZE, TextSize(Length::pt(10.0).into()));