mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 11:55:23 +00:00
Merge pull request #6820 from dotCooCoo/feat/brushes-ink-wash-library
Brushes: built-in Ink and Wash library with tapered ink, brush pen and wash brushes
This commit is contained in:
@@ -170,6 +170,7 @@ pub struct BuiltinBrushLibrary {
|
||||
/// The built-in brush libraries, in menu order: original brushes generated in code.
|
||||
pub const BUILTIN: &[BuiltinBrushLibrary] = &[
|
||||
BuiltinBrushLibrary { id: "art-ribbons", name: "Art Ribbons", make: art_ribbons },
|
||||
BuiltinBrushLibrary { id: "ink-and-wash", name: "Ink and Wash", make: ink_and_wash },
|
||||
BuiltinBrushLibrary { id: "calligraphic-nibs", name: "Calligraphic Nibs", make: calligraphic_nibs },
|
||||
BuiltinBrushLibrary { id: "bristle-brushes", name: "Bristle Brushes", make: bristle_brushes },
|
||||
BuiltinBrushLibrary { id: "pattern-borders", name: "Pattern Borders", make: pattern_borders },
|
||||
@@ -268,6 +269,78 @@ fn art_ribbons() -> BrushLibrary {
|
||||
built("Art Ribbons", doc, brushes)
|
||||
}
|
||||
|
||||
/// A band between the edges y = `top(x)` and y = `bottom(x)` for x in `from`..`to`, in `n` steps.
|
||||
fn between(from: f64, to: f64, n: usize, top: impl Fn(f64) -> f64, bottom: impl Fn(f64) -> f64) -> PathData {
|
||||
let xs: Vec<f64> = (0..=n).map(|i| from + (to - from) * i as f64 / n as f64).collect();
|
||||
let mut pts: Vec<Point> = xs.iter().map(|&x| Point::new(x, top(x))).collect();
|
||||
pts.extend(xs.iter().rev().map(|&x| Point::new(x, bottom(x))));
|
||||
PathData::single(SubPath::polyline(&pts, true))
|
||||
}
|
||||
|
||||
/// Path `p` filled with gray `hex` at `opacity`: one layer of a wash.
|
||||
fn wash(p: PathData, hex: &str, opacity: f32) -> Node {
|
||||
let mut n = filled(p, Paint::solid(Color::from_hex(hex).unwrap_or(Color::BLACK)));
|
||||
n.opacity = opacity;
|
||||
n
|
||||
}
|
||||
|
||||
/// How much of a wash's width is left at x: rounded ends, full width from 8 pt in.
|
||||
fn wash_ends(x: f64) -> f64 {
|
||||
let ends = ((x / 8.0).min(1.0) * ((100.0 - x) / 8.0).min(1.0)).max(0.0);
|
||||
0.4 + 0.6 * ends.sqrt()
|
||||
}
|
||||
|
||||
/// Ink and Wash: Tapered Ink, broad near its start and thinning to a fine tip; Brush Pen, a
|
||||
/// rounded nib whose size follows the pen pressure; and two washes of translucent grays, Soft Wash
|
||||
/// with faint edges and Pooling Wash with a darker rim and two light breaks. Black and grays take
|
||||
/// the stroke color as tints of it.
|
||||
fn ink_and_wash() -> BrushLibrary {
|
||||
let tapered = {
|
||||
let half = |x: f64| 3.0 * (x / 22.0).min(1.0).powf(0.6) * ((100.0 - x) / 78.0).min(1.0).powf(1.4);
|
||||
black(between(0.0, 100.0, 48, |x| -0.85 * half(x), |x| 1.15 * half(x)))
|
||||
};
|
||||
let pen = Brush {
|
||||
name: "Brush Pen".into(),
|
||||
kind: BrushKind::Calligraphic(Calligraphic {
|
||||
angle: 40.0,
|
||||
roundness: 75.0,
|
||||
size: 5.0,
|
||||
variation: [0.0, 0.0, 2.5],
|
||||
modes: Some([Variation::Fixed, Variation::Fixed, Variation::Pressure]),
|
||||
}),
|
||||
};
|
||||
let soft = {
|
||||
let top = |x: f64| -(4.5 + 0.5 * (x * 0.21).sin() + 0.3 * (x * 0.57 + 1.3).sin()) * wash_ends(x);
|
||||
let bottom = |x: f64| (4.5 + 0.45 * (x * 0.26 + 2.1).sin() + 0.3 * (x * 0.69 + 0.4).sin()) * wash_ends(x);
|
||||
group(vec![
|
||||
wash(between(0.0, 100.0, 40, top, bottom), "#404040", 0.4),
|
||||
wash(between(2.0, 98.0, 32, |x| top(x) - 0.7 - 0.3 * (x * 0.43).sin().abs(), |x| top(x) + 0.5), "#404040", 0.2),
|
||||
wash(between(3.0, 97.0, 32, |x| bottom(x) - 0.5, |x| bottom(x) + 0.6 + 0.35 * (x * 0.37 + 1.0).sin().abs()), "#404040", 0.18),
|
||||
wash(shapes::ellipse(Rect::new(21.0, -2.4, 39.0, 0.8)), "#e6e6e6", 0.22),
|
||||
wash(shapes::ellipse(Rect::new(61.0, -0.3, 75.0, 2.3)), "#e6e6e6", 0.18),
|
||||
])
|
||||
};
|
||||
let pooling = {
|
||||
let top = |x: f64| -(4.0 + 0.4 * (x * 0.18 + 0.7).sin() + 0.2 * (x * 0.53).sin()) * wash_ends(x);
|
||||
let bottom = |x: f64| (4.0 + 0.35 * (x * 0.23 + 1.9).sin() + 0.25 * (x * 0.61 + 0.2).sin()) * wash_ends(x);
|
||||
let rim = |x: f64, k: f64, phase: f64| 0.35 + k * (0.5 + 0.5 * (x * 0.29 + phase).sin());
|
||||
group(vec![
|
||||
wash(between(0.0, 100.0, 40, top, bottom), "#4d4d4d", 0.35),
|
||||
wash(between(1.0, 99.0, 40, top, |x| top(x) + rim(x, 0.45, 0.0)), "#262626", 0.55),
|
||||
wash(between(1.0, 99.0, 40, |x| bottom(x) - rim(x, 0.5, 1.1), bottom), "#262626", 0.55),
|
||||
wash(polygon(&[(34.0, -2.6), (39.0, -2.8), (36.0, 2.7), (31.0, 2.5)]), "#e6e6e6", 0.2),
|
||||
wash(polygon(&[(70.0, -2.4), (74.0, -2.5), (72.0, 2.6), (68.0, 2.4)]), "#e6e6e6", 0.18),
|
||||
])
|
||||
};
|
||||
let brushes = vec![
|
||||
art("Tapered Ink", tapered, Colorization::Tints),
|
||||
pen,
|
||||
art("Soft Wash", soft, Colorization::Tints),
|
||||
art("Pooling Wash", pooling, Colorization::Tints),
|
||||
];
|
||||
built("Ink and Wash", Document::new(100.0, 100.0), brushes)
|
||||
}
|
||||
|
||||
fn nib(name: &str, angle: f64, roundness: f64, size: f64) -> Brush {
|
||||
Brush { name: name.into(), kind: BrushKind::Calligraphic(Calligraphic { angle, roundness, size, ..Default::default() }) }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user