mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Change rectangle clip to use the inside of the stroke, not the middle. (#2626)
This commit is contained in:
parent
46846a337e
commit
ba05164bb6
@ -155,7 +155,7 @@ impl Layout for BoxElem {
|
|||||||
let outset = self.outset(styles).relative_to(frame.size());
|
let outset = self.outset(styles).relative_to(frame.size());
|
||||||
let size = frame.size() + outset.sum_by_axis();
|
let size = frame.size() + outset.sum_by_axis();
|
||||||
let radius = self.radius(styles);
|
let radius = self.radius(styles);
|
||||||
frame.clip(path_rect(size, radius, &stroke));
|
frame.clip(clip_rect(size, radius, &stroke));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add fill and/or stroke.
|
// Add fill and/or stroke.
|
||||||
@ -421,7 +421,7 @@ impl Layout for BlockElem {
|
|||||||
let outset = self.outset(styles).relative_to(frame.size());
|
let outset = self.outset(styles).relative_to(frame.size());
|
||||||
let size = frame.size() + outset.sum_by_axis();
|
let size = frame.size() + outset.sum_by_axis();
|
||||||
let radius = self.radius(styles);
|
let radius = self.radius(styles);
|
||||||
frame.clip(path_rect(size, radius, &stroke));
|
frame.clip(clip_rect(size, radius, &stroke));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ pub use self::paint::Paint;
|
|||||||
pub use self::path::{Path, PathItem};
|
pub use self::path::{Path, PathItem};
|
||||||
pub use self::point::Point;
|
pub use self::point::Point;
|
||||||
pub use self::ratio::Ratio;
|
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::rel::Rel;
|
||||||
pub use self::scalar::Scalar;
|
pub use self::scalar::Scalar;
|
||||||
pub use self::shape::{Geometry, Shape};
|
pub use self::shape::{Geometry, Shape};
|
||||||
|
@ -43,16 +43,41 @@ impl PathExtension for Path {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new rectangle as a path.
|
/// Creates a new rectangle as a path.
|
||||||
pub fn path_rect(
|
pub fn clip_rect(
|
||||||
size: Size,
|
size: Size,
|
||||||
radius: Corners<Rel<Abs>>,
|
radius: Corners<Rel<Abs>>,
|
||||||
stroke: &Sides<Option<FixedStroke>>,
|
stroke: &Sides<Option<FixedStroke>>,
|
||||||
) -> Path {
|
) -> Path {
|
||||||
if stroke.is_uniform() && radius.iter().cloned().all(Rel::is_zero) {
|
let stroke_widths = stroke
|
||||||
Path::rect(size)
|
.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 {
|
} 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.
|
/// 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
|
/// Use stroke and fill for the rectangle
|
||||||
fn segmented_rect(
|
fn segmented_rect(
|
||||||
size: Size,
|
size: Size,
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 32 KiB |
@ -54,3 +54,15 @@ First!
|
|||||||
clip: true,
|
clip: true,
|
||||||
image("/files/rhino.png", width: 30pt)
|
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)
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user