summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/typst-library/src/layout/container.rs4
-rw-r--r--crates/typst/src/geom/mod.rs2
-rw-r--r--crates/typst/src/geom/rect.rs73
-rw-r--r--tests/ref/layout/clip.pngbin29586 -> 33092 bytes
-rw-r--r--tests/typ/layout/clip.typ12
5 files changed, 44 insertions, 47 deletions
diff --git a/crates/typst-library/src/layout/container.rs b/crates/typst-library/src/layout/container.rs
index 36b62864..2e6ccdd2 100644
--- a/crates/typst-library/src/layout/container.rs
+++ b/crates/typst-library/src/layout/container.rs
@@ -155,7 +155,7 @@ impl Layout for BoxElem {
let outset = self.outset(styles).relative_to(frame.size());
let size = frame.size() + outset.sum_by_axis();
let radius = self.radius(styles);
- frame.clip(path_rect(size, radius, &stroke));
+ frame.clip(clip_rect(size, radius, &stroke));
}
// Add fill and/or stroke.
@@ -421,7 +421,7 @@ impl Layout for BlockElem {
let outset = self.outset(styles).relative_to(frame.size());
let size = frame.size() + outset.sum_by_axis();
let radius = self.radius(styles);
- frame.clip(path_rect(size, radius, &stroke));
+ frame.clip(clip_rect(size, radius, &stroke));
}
}
diff --git a/crates/typst/src/geom/mod.rs b/crates/typst/src/geom/mod.rs
index 8ad6cea0..1b2a79bc 100644
--- a/crates/typst/src/geom/mod.rs
+++ b/crates/typst/src/geom/mod.rs
@@ -46,7 +46,7 @@ pub use self::paint::Paint;
pub use self::path::{Path, PathItem};
pub use self::point::Point;
pub use self::ratio::Ratio;
-pub use self::rect::{path_rect, styled_rect};
+pub use self::rect::{clip_rect, styled_rect};
pub use self::rel::Rel;
pub use self::scalar::Scalar;
pub use self::shape::{Geometry, Shape};
diff --git a/crates/typst/src/geom/rect.rs b/crates/typst/src/geom/rect.rs
index 37b94527..230f8e8c 100644
--- a/crates/typst/src/geom/rect.rs
+++ b/crates/typst/src/geom/rect.rs
@@ -43,16 +43,41 @@ impl PathExtension for Path {
}
/// Creates a new rectangle as a path.
-pub fn path_rect(
+pub fn clip_rect(
size: Size,
radius: Corners<Rel<Abs>>,
stroke: &Sides<Option<FixedStroke>>,
) -> Path {
- if stroke.is_uniform() && radius.iter().cloned().all(Rel::is_zero) {
- Path::rect(size)
+ let stroke_widths = stroke
+ .as_ref()
+ .map(|s| s.as_ref().map_or(Abs::zero(), |s| s.thickness / 2.0));
+
+ let max_radius = (size.x.min(size.y)) / 2.0
+ + stroke_widths.iter().cloned().min().unwrap_or(Abs::zero());
+
+ let radius = radius.map(|side| side.relative_to(max_radius * 2.0).min(max_radius));
+
+ let corners = corners_control_points(size, radius, stroke, stroke_widths);
+
+ let mut path = Path::new();
+ if corners.top_left.arc_inner() {
+ path.arc_move(
+ corners.top_left.start_inner(),
+ corners.top_left.center_inner(),
+ corners.top_left.end_inner(),
+ );
} else {
- segmented_path_rect(size, radius, stroke)
+ path.move_to(corners.top_left.center_inner());
+ }
+ for corner in [&corners.top_right, &corners.bottom_right, &corners.bottom_left] {
+ if corner.arc_inner() {
+ path.arc_line(corner.start_inner(), corner.center_inner(), corner.end_inner())
+ } else {
+ path.line_to(corner.center_inner());
+ }
}
+ path.close_path();
+ path
}
/// Create a styled rectangle with shapes.
@@ -110,46 +135,6 @@ fn corners_control_points(
})
}
-fn segmented_path_rect(
- size: Size,
- radius: Corners<Rel<Abs>>,
- strokes: &Sides<Option<FixedStroke>>,
-) -> Path {
- let stroke_widths = strokes
- .as_ref()
- .map(|s| s.as_ref().map_or(Abs::zero(), |s| s.thickness / 2.0));
-
- let max_radius = (size.x.min(size.y)) / 2.0
- + stroke_widths.iter().cloned().min().unwrap_or(Abs::zero());
-
- let radius = radius.map(|side| side.relative_to(max_radius * 2.0).min(max_radius));
-
- // insert stroked sides below filled sides
- let mut path = Path::new();
- let corners = corners_control_points(size, radius, strokes, stroke_widths);
- let current = corners.iter().find(|c| !c.same).map(|c| c.corner);
- if let Some(mut current) = current {
- // multiple segments
- // start at a corner with a change between sides and iterate clockwise all other corners
- let mut last = current;
- for _ in 0..4 {
- current = current.next_cw();
- if corners.get_ref(current).same {
- continue;
- }
- // create segment
- let start = last;
- let end = current;
- last = current;
- path_segment(start, end, &corners, &mut path);
- }
- } else if strokes.top.is_some() {
- // single segment
- path_segment(Corner::TopLeft, Corner::TopLeft, &corners, &mut path);
- }
- path
-}
-
/// Use stroke and fill for the rectangle
fn segmented_rect(
size: Size,
diff --git a/tests/ref/layout/clip.png b/tests/ref/layout/clip.png
index c847fc63..f37bf9ad 100644
--- a/tests/ref/layout/clip.png
+++ b/tests/ref/layout/clip.png
Binary files differ
diff --git a/tests/typ/layout/clip.typ b/tests/typ/layout/clip.typ
index d05fdb74..d20609d8 100644
--- a/tests/typ/layout/clip.typ
+++ b/tests/typ/layout/clip.typ
@@ -54,3 +54,15 @@ First!
clip: true,
image("/files/rhino.png", width: 30pt)
)
+---
+// Test clipping with `radius`, but without `stroke`.
+
+#set page(height: 60pt)
+
+#box(
+ radius: 5pt,
+ width: 20pt,
+ height: 20pt,
+ clip: true,
+ image("/files/rhino.png", width: 30pt)
+)