Skip to content

Commit

Permalink
Merge pull request #410 from linebender/stroke-join-styles
Browse files Browse the repository at this point in the history
Refactor transform handling in flatten & support miter join
  • Loading branch information
armansito authored Nov 21, 2023
2 parents a697c93 + d4fd839 commit 4eb0f51
Show file tree
Hide file tree
Showing 3 changed files with 339 additions and 172 deletions.
225 changes: 119 additions & 106 deletions examples/scenes/src/test_scenes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,17 @@ pub fn test_scenes() -> SceneSet {
let scenes = vec![
scene!(splash_with_tiger(), "splash_with_tiger", false),
scene!(funky_paths),
scene!(stroke_styles),
scene!(stroke_styles(Affine::IDENTITY), "stroke_styles", false),
scene!(
stroke_styles(Affine::scale_non_uniform(1.2, 0.7)),
"stroke_styles (non-uniform scale)",
false
),
scene!(
stroke_styles(Affine::skew(1., 0.)),
"stroke_styles (skew)",
false
),
scene!(tricky_strokes),
scene!(fill_types),
scene!(cardioid_and_friends),
Expand Down Expand Up @@ -98,142 +108,145 @@ fn funky_paths(sb: &mut SceneBuilder, _: &mut SceneParams) {
);
}

fn stroke_styles(sb: &mut SceneBuilder, params: &mut SceneParams) {
fn stroke_styles(transform: Affine) -> impl FnMut(&mut SceneBuilder, &mut SceneParams) {
use PathEl::*;
let colors = [
Color::rgb8(140, 181, 236),
Color::rgb8(246, 236, 202),
Color::rgb8(201, 147, 206),
Color::rgb8(150, 195, 160),
];
let simple_stroke = [MoveTo((0., 0.).into()), LineTo((100., 0.).into())];
let join_stroke = [
MoveTo((0., 0.).into()),
CurveTo((20., 0.).into(), (42.5, 5.).into(), (50., 25.).into()),
CurveTo((57.5, 5.).into(), (80., 0.).into(), (100., 0.).into()),
];
let miter_stroke = [
MoveTo((0., 0.).into()),
LineTo((90., 21.).into()),
LineTo((0., 42.).into()),
];
let closed_strokes = [
MoveTo((0., 0.).into()),
LineTo((90., 21.).into()),
LineTo((0., 42.).into()),
ClosePath,
MoveTo((200., 0.).into()),
CurveTo((100., 42.).into(), (300., 42.).into(), (200., 0.).into()),
ClosePath,
MoveTo((290., 0.).into()),
CurveTo((200., 42.).into(), (400., 42.).into(), (310., 0.).into()),
ClosePath,
];
let cap_styles = [Cap::Butt, Cap::Square, Cap::Round];
let join_styles = [Join::Bevel, Join::Miter, Join::Round];
let miter_limits = [4., 5., 0.1, 10.];
move |sb, params| {
let colors = [
Color::rgb8(140, 181, 236),
Color::rgb8(246, 236, 202),
Color::rgb8(201, 147, 206),
Color::rgb8(150, 195, 160),
];
let simple_stroke = [MoveTo((0., 0.).into()), LineTo((100., 0.).into())];
let join_stroke = [
MoveTo((0., 0.).into()),
CurveTo((20., 0.).into(), (42.5, 5.).into(), (50., 25.).into()),
CurveTo((57.5, 5.).into(), (80., 0.).into(), (100., 0.).into()),
];
let miter_stroke = [
MoveTo((0., 0.).into()),
LineTo((90., 16.).into()),
LineTo((0., 31.).into()),
LineTo((90., 46.).into()),
];
let closed_strokes = [
MoveTo((0., 0.).into()),
LineTo((90., 21.).into()),
LineTo((0., 42.).into()),
ClosePath,
MoveTo((200., 0.).into()),
CurveTo((100., 72.).into(), (300., 72.).into(), (200., 0.).into()),
ClosePath,
MoveTo((290., 0.).into()),
CurveTo((200., 72.).into(), (400., 72.).into(), (310., 0.).into()),
ClosePath,
];
let cap_styles = [Cap::Butt, Cap::Square, Cap::Round];
let join_styles = [Join::Bevel, Join::Miter, Join::Round];
let miter_limits = [4., 6., 0.1, 10.];

// Simple strokes with cap combinations
let t = Affine::translate((60., 40.)) * Affine::scale(2.);
let mut y = 0.;
let mut color_idx = 0;
for start in cap_styles {
for end in cap_styles {
// Simple strokes with cap combinations
let t = Affine::translate((60., 40.)) * Affine::scale(2.);
let mut y = 0.;
let mut color_idx = 0;
for start in cap_styles {
for end in cap_styles {
params.text.add(
sb,
None,
12.,
None,
Affine::translate((0., y)) * t,
&format!("Start cap: {:?}, End cap: {:?}", start, end),
);
sb.stroke(
&Stroke::new(20.).with_start_cap(start).with_end_cap(end),
Affine::translate((0., y + 30.)) * t * transform,
colors[color_idx],
None,
&simple_stroke,
);
y += 180.;
color_idx = (color_idx + 1) % colors.len();
}
}

// Cap and join combinations
let t = Affine::translate((500., 0.)) * t;
y = 0.;
for cap in cap_styles {
for join in join_styles {
params.text.add(
sb,
None,
12.,
None,
Affine::translate((0., y)) * t,
&format!("Caps: {:?}, Joins: {:?}", cap, join),
);
sb.stroke(
&Stroke::new(20.).with_caps(cap).with_join(join),
Affine::translate((0., y + 30.)) * t * transform,
colors[color_idx],
None,
&join_stroke,
);
y += 185.;
color_idx = (color_idx + 1) % colors.len();
}
}

// Miter limit
let t = Affine::translate((500., 0.)) * t;
y = 0.;
for ml in miter_limits {
params.text.add(
sb,
None,
12.,
None,
Affine::translate((0., y)) * t,
&format!("Start cap: {:?}, End cap: {:?}", start, end),
&format!("Miter limit: {}", ml),
);
sb.stroke(
&Stroke::new(20.).with_start_cap(start).with_end_cap(end),
Affine::translate((0., y + 30.)) * t,
&Stroke::new(10.)
.with_caps(Cap::Butt)
.with_join(Join::Miter)
.with_miter_limit(ml),
Affine::translate((0., y + 30.)) * t * transform,
colors[color_idx],
None,
&simple_stroke,
&miter_stroke,
);
y += 180.;
color_idx = (color_idx + 1) % colors.len();
}
}

// Cap and join combinations
let t = Affine::translate((500., 0.)) * t;
y = 0.;
for cap in cap_styles {
for join in join_styles {
// Closed paths
for (i, join) in join_styles.iter().enumerate() {
params.text.add(
sb,
None,
12.,
None,
Affine::translate((0., y)) * t,
&format!("Caps: {:?}, Joins: {:?}", cap, join),
&format!("Closed path with join: {:?}", join),
);
// The cap style is not important since a closed path shouldn't have any caps.
sb.stroke(
&Stroke::new(20.).with_caps(cap).with_join(join),
Affine::translate((0., y + 30.)) * t,
&Stroke::new(10.)
.with_caps(cap_styles[i])
.with_join(*join)
.with_miter_limit(5.),
Affine::translate((0., y + 30.)) * t * transform,
colors[color_idx],
None,
&join_stroke,
&closed_strokes,
);
y += 185.;
y += 180.;
color_idx = (color_idx + 1) % colors.len();
}
}

// Miter limit
let t = Affine::translate((500., 0.)) * t;
y = 0.;
for ml in miter_limits {
params.text.add(
sb,
None,
12.,
None,
Affine::translate((0., y)) * t,
&format!("Miter limit: {}", ml),
);
sb.stroke(
&Stroke::new(10.)
.with_caps(Cap::Butt)
.with_join(Join::Miter)
.with_miter_limit(ml),
Affine::translate((0., y + 30.)) * t,
colors[color_idx],
None,
&miter_stroke,
);
y += 180.;
color_idx = (color_idx + 1) % colors.len();
}

// Closed paths
for (i, join) in join_styles.iter().enumerate() {
params.text.add(
sb,
None,
12.,
None,
Affine::translate((0., y)) * t,
&format!("Closed path with join: {:?}", join),
);
// The cap style is not important since a closed path shouldn't have any caps.
sb.stroke(
&Stroke::new(10.)
.with_caps(cap_styles[i])
.with_join(*join)
.with_miter_limit(5.),
Affine::translate((0., y + 30.)) * t,
colors[color_idx],
None,
&closed_strokes,
);
y += 180.;
color_idx = (color_idx + 1) % colors.len();
}
}

// This test has been adapted from Skia's "trickycubicstrokes" GM slide which can be found at
Expand Down
Loading

0 comments on commit 4eb0f51

Please sign in to comment.