This commit is contained in:
Brandon Thomas committed 2026-10-31 10:54:51 -04:00
commit 8085ead73d
2 files changed
+49 -1

No files matched your search

+8 -1
View File
@@ -387,6 +387,13 @@ pub(super) fn vegas(ctx: &EffectCtx, mut b: Buf) -> Buf {
b
}
/// Sub-samples per Vegas segment: at least 12 (opacity ramp), more for long segments so each straight piece stays
/// short compared with the stroke width and the segment follows rounded corners smoothly.
pub(super) fn vegas_substeps(each: f64, width: f64) -> usize {
let step = width.max(2.0);
((each / step).ceil().max(12.0) as usize).min(512)
}
fn vegas_plan(ctx: &EffectCtx, b: &Buf) -> PaintPlan {
let pr = ctx.params;
let (w, h) = (b.img.width as usize, b.img.height as usize);
@@ -459,7 +466,7 @@ fn vegas_plan(ctx: &EffectCtx, b: &Buf) -> PaintPlan {
let base = if bunched { (i as f64 + (hash1(ci as u32, i as u32 + 7, seed) as f64 - 0.5) * 0.8) / nseg as f64 } else { i as f64 / nseg as f64 };
let s0 = (base + phase).rem_euclid(1.0) * len;
// Sub-sample the segment so opacity can ramp along it.
let sub = 12usize;
let sub = vegas_substeps(each, width);
let mut prev: Option<[f64; 2]> = None;
for k in 0..=sub {
let f = k as f64 / sub as f64;
+41
View File
@@ -395,3 +395,44 @@ fn scribble_caps_and_start_end_apply_to() {
let each = run_env("ec.generate.scribble", &[base.as_slice(), &seq, &[("startEndApplyTo", Value::Enum(1))]].concat(), img, 0.0, env2);
assert_ne!(all.img.data, each.img.data);
}
#[test]
fn vegas_long_segments_are_sampled_finely() {
// A short segment keeps the 12 samples; a long one gets more so it bends smoothly round rounded corners.
assert_eq!(super::paths::vegas_substeps(10.0, 4.0), 12);
let n = super::paths::vegas_substeps(400.0, 4.0);
assert!(n > 12, "{n}");
assert!(400.0 / n as f64 <= 4.0 + 1e-9);
assert_eq!(super::paths::vegas_substeps(1.0e9, 4.0), 512);
}
#[test]
fn vegas_long_segment_follows_curved_mask() {
// One segment as long as a round mask (radius 90 px): with only 12 straight pieces the
// chords cut ~3 px inside the curve, leaving gaps on the mask outline.
let (c, r) = (100.0, 90.0);
let mut points = Vec::new();
for i in 0..256 {
let a = i as f64 / 256.0 * std::f64::consts::TAU;
points.push([c + r * a.cos(), c + r * a.sin()]);
}
let masks = vec![MaskShape { name: "Mask 1".into(), points, closed: true, inverted: false }];
let env = EffectEnv { masks: &masks, ..Default::default() };
let set = [
("stroke", Value::Enum(1)),
("segments", num(1.0)),
("length", num(1.0)),
("segmentDistribution", Value::Enum(1)),
("blendMode", Value::Enum(0)),
("hardness", num(1.0)),
("midpointOpacity", num(1.0)),
("endOpacity", num(1.0)),
];
let out = run_env("ec.generate.vegas", &set, Image::new(200, 200), 0.5, env);
for k in 0..720 {
let a = k as f64 / 720.0 * std::f64::consts::TAU;
let (x, y) = ((c + r * a.cos()).floor() as usize, (c + r * a.sin()).floor() as usize);
let alpha = out.img.data[y * 200 + x][3];
assert!(alpha > 0.5, "gap on the curve at {k}/720 turn: alpha {alpha}");
}
}