diff --git a/src/eval/value.rs b/src/eval/value.rs index c32614df2..352906aab 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -596,7 +596,10 @@ impl Cast for Smart { } } -impl Cast for Sides { +impl Cast for Sides +where + T: Cast + Default + Clone, +{ fn is(value: &Value) -> bool { matches!(value, Value::Dict(_)) || T::is(value) } diff --git a/src/geom/rect.rs b/src/geom/rect.rs index 839feda46..f0da2db61 100644 --- a/src/geom/rect.rs +++ b/src/geom/rect.rs @@ -28,7 +28,7 @@ impl Rect { res.push(Shape { geometry: self.fill_geometry(), fill, - stroke: stroke.left, + stroke: stroke.is_uniform().then(|| stroke.top).flatten(), }); } @@ -174,6 +174,8 @@ fn draw_side( } } +/// 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, @@ -183,6 +185,9 @@ enum Connection { } 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 => { @@ -202,10 +207,12 @@ impl Connection { } } + /// 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) } diff --git a/src/library/graphics/shape.rs b/src/library/graphics/shape.rs index e6fd2b7da..a5523a2ea 100644 --- a/src/library/graphics/shape.rs +++ b/src/library/graphics/shape.rs @@ -171,13 +171,9 @@ impl Layout for ShapeNode { frame.size.y + outset.top + outset.bottom, ); - let radius = styles.get(Self::RADIUS); - let radius = Sides { - left: radius.left.relative_to(size.x / 2.0), - top: radius.top.relative_to(size.y / 2.0), - right: radius.right.relative_to(size.x / 2.0), - bottom: radius.bottom.relative_to(size.y / 2.0), - }; + let radius = styles + .get(Self::RADIUS) + .map(|side| side.relative_to(size.x.min(size.y) / 2.0)); let pos = Point::new(-outset.left, -outset.top); diff --git a/tests/ref/graphics/shape-ellipse.png b/tests/ref/graphics/shape-ellipse.png index 740f005f3..296fc14ee 100644 Binary files a/tests/ref/graphics/shape-ellipse.png and b/tests/ref/graphics/shape-ellipse.png differ diff --git a/tests/ref/graphics/shape-fill-stroke.png b/tests/ref/graphics/shape-fill-stroke.png index f2278c887..91cddcc2e 100644 Binary files a/tests/ref/graphics/shape-fill-stroke.png and b/tests/ref/graphics/shape-fill-stroke.png differ diff --git a/tests/ref/graphics/shape-rect.png b/tests/ref/graphics/shape-rect.png index 1fdb0dac9..5bbaf3db7 100644 Binary files a/tests/ref/graphics/shape-rect.png and b/tests/ref/graphics/shape-rect.png differ diff --git a/tests/typ/graphics/shape-ellipse.typ b/tests/typ/graphics/shape-ellipse.typ index 547acd382..ba4d0d0ab 100644 --- a/tests/typ/graphics/shape-ellipse.typ +++ b/tests/typ/graphics/shape-ellipse.typ @@ -21,3 +21,6 @@ Auto-sized ellipse. \ #set text(8pt) But, soft! what light through yonder window breaks? ] + + +An inline #ellipse(width: 8pt, height: 6pt, outset: (top: 3pt, rest: 5.5pt)) ellipse. \ No newline at end of file diff --git a/tests/typ/graphics/shape-fill-stroke.typ b/tests/typ/graphics/shape-fill-stroke.typ index c09cb065c..d14d0981b 100644 --- a/tests/typ/graphics/shape-fill-stroke.typ +++ b/tests/typ/graphics/shape-fill-stroke.typ @@ -38,3 +38,13 @@ #sq(stroke: blue) #sq(fill: teal, stroke: blue) #sq(fill: teal, stroke: 2pt + blue) + +--- +// Test stroke composition. +#set square(stroke: 4pt) +#set text("Roboto") +#square( + stroke: (left: red, top: yellow, right: green, bottom: blue), + radius: 100%, align(center+horizon)[*G*], + inset: 8pt +) diff --git a/tests/typ/graphics/shape-rect.typ b/tests/typ/graphics/shape-rect.typ index 52fe03eac..3d1576753 100644 --- a/tests/typ/graphics/shape-rect.typ +++ b/tests/typ/graphics/shape-rect.typ @@ -27,3 +27,30 @@ \{#rect(width: 0.5in, height: 7pt, fill: rgb("d6cd67")) #rect(width: 0.5in, height: 7pt, fill: rgb("edd466")) #rect(width: 0.5in, height: 7pt, fill: rgb("e3be62"))\} + +// Rounded corners. +#rect(width: 2cm, radius: 60%) +#rect(width: 1cm, radius: (x: 5pt, y: 10pt)) +#rect(width: 1.25cm, radius: (left: 2pt, top: 5pt, right: 8pt, bottom: 11pt)) + +// Different strokes. +[ + #set rect(stroke: (right: red,)) + #rect(width: 100%, fill: lime, stroke: (x: 5pt, y: 1pt)) +] + +--- +// Outset padding. +#let inline-code(body) = [ + #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) + #h(.7em, weak: true) +] + +Use the #inline-code[\*const ptr] pointer. + +--- +// Error: 15-38 unexpected key "cake" +#rect(radius: (left: 10pt, cake: 5pt)) +