feat(noise): add preserveLength toggle
When enabled (default), perturbs each segment's direction vector and rescales to original length — bends the path without stretching it or causing fold-back artifacts. When disabled, the original direct point displacement is used. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,11 @@
|
|||||||
"max": 5,
|
"max": 5,
|
||||||
"value": 1,
|
"value": 1,
|
||||||
"hidden": true
|
"hidden": true
|
||||||
|
},
|
||||||
|
"preserveLength": {
|
||||||
|
"type": "boolean",
|
||||||
|
"label": "Preserve length",
|
||||||
|
"value": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
|||||||
let depth = evaluate_int(args[6]);
|
let depth = evaluate_int(args[6]);
|
||||||
|
|
||||||
let octaves = evaluate_int(args[7]);
|
let octaves = evaluate_int(args[7]);
|
||||||
|
let preserve_length = evaluate_int(args[8]) != 0;
|
||||||
|
|
||||||
let noise_x: HybridMulti<OpenSimplex> =
|
let noise_x: HybridMulti<OpenSimplex> =
|
||||||
HybridMulti::new(seed as u32 + 1).set_octaves(octaves as usize);
|
HybridMulti::new(seed as u32 + 1).set_octaves(octaves as usize);
|
||||||
@@ -66,70 +67,82 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
|
|||||||
|
|
||||||
let length = path.get_length() as f64;
|
let length = path.get_length() as f64;
|
||||||
|
|
||||||
// Record original segment lengths so we can re-project after displacement
|
if preserve_length {
|
||||||
let seg_lens: Vec<f32> = (0..path.length - 1)
|
// Snapshot original positions so we can derive each segment's original
|
||||||
.map(|k| {
|
// direction even after we've modified earlier points.
|
||||||
let p0 = Vec3::new(
|
let orig: Vec<f32> = path.points[..path.length * 4].to_vec();
|
||||||
path.points[k * 4],
|
|
||||||
path.points[k * 4 + 1],
|
// Anchor the base (fix_bottom=1 → scale=0, no displacement at root)
|
||||||
path.points[k * 4 + 2],
|
let scale0 = lerp(1.0, 0.0, fix_bottom);
|
||||||
|
path.points[0] += noise_x.get([j as f64, 0.0]) as f32
|
||||||
|
* directional_strength[0]
|
||||||
|
* strength
|
||||||
|
* scale0;
|
||||||
|
path.points[1] += noise_y.get([j as f64, 0.0]) as f32
|
||||||
|
* directional_strength[1]
|
||||||
|
* strength
|
||||||
|
* scale0;
|
||||||
|
path.points[2] += noise_z.get([j as f64, 0.0]) as f32
|
||||||
|
* directional_strength[2]
|
||||||
|
* strength
|
||||||
|
* scale0;
|
||||||
|
let mut prev = Vec3::new(path.points[0], path.points[1], path.points[2]);
|
||||||
|
|
||||||
|
for i in 1..path.length {
|
||||||
|
let a = i as f64 / (path.length - 1) as f64;
|
||||||
|
let px = j as f64 + a * length * scale;
|
||||||
|
let py = a * scale as f64;
|
||||||
|
let sf = lerp(1.0, a as f32, fix_bottom);
|
||||||
|
|
||||||
|
let orig_dir = Vec3::new(
|
||||||
|
orig[i * 4] - orig[(i - 1) * 4],
|
||||||
|
orig[i * 4 + 1] - orig[(i - 1) * 4 + 1],
|
||||||
|
orig[i * 4 + 2] - orig[(i - 1) * 4 + 2],
|
||||||
);
|
);
|
||||||
let p1 = Vec3::new(
|
let orig_len = orig_dir.length();
|
||||||
path.points[(k + 1) * 4],
|
|
||||||
path.points[(k + 1) * 4 + 1],
|
let perturb = Vec3::new(
|
||||||
path.points[(k + 1) * 4 + 2],
|
noise_x.get([px, py]) as f32 * directional_strength[0] * strength * sf,
|
||||||
|
noise_y.get([px, py]) as f32 * directional_strength[1] * strength * sf,
|
||||||
|
noise_z.get([px, py]) as f32 * directional_strength[2] * strength * sf,
|
||||||
);
|
);
|
||||||
(p1 - p0).length()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Displace the first point (fix_bottom=1 → scale=0 here, anchoring the base)
|
// Perturb the original direction and rescale to original length.
|
||||||
let scale0 = lerp(1.0, 0.0, fix_bottom);
|
// Biasing toward orig_dir prevents the segment from folding back.
|
||||||
path.points[0] += noise_x.get([j as f64, 0.0]) as f32
|
let mut new_dir = orig_dir + perturb;
|
||||||
* directional_strength[0]
|
let nd_len = new_dir.length();
|
||||||
* strength
|
if nd_len > 0.0001 && orig_len > 0.0001 {
|
||||||
* scale0;
|
new_dir *= orig_len / nd_len;
|
||||||
path.points[1] += noise_y.get([j as f64, 0.0]) as f32
|
} else {
|
||||||
* directional_strength[1]
|
new_dir = orig_dir;
|
||||||
* strength
|
}
|
||||||
* scale0;
|
|
||||||
path.points[2] += noise_z.get([j as f64, 0.0]) as f32
|
|
||||||
* directional_strength[2]
|
|
||||||
* strength
|
|
||||||
* scale0;
|
|
||||||
let mut prev = Vec3::new(path.points[0], path.points[1], path.points[2]);
|
|
||||||
|
|
||||||
for i in 1..path.length {
|
let cur = prev + new_dir;
|
||||||
let a = i as f64 / (path.length - 1) as f64;
|
path.points[i * 4] = cur.x;
|
||||||
|
path.points[i * 4 + 1] = cur.y;
|
||||||
let px = j as f64 + a * length * scale;
|
path.points[i * 4 + 2] = cur.z;
|
||||||
let py = a * scale as f64;
|
|
||||||
|
|
||||||
let sf = lerp(1.0, a as f32, fix_bottom);
|
|
||||||
path.points[i * 4] +=
|
|
||||||
noise_x.get([px, py]) as f32 * directional_strength[0] * strength * sf;
|
|
||||||
path.points[i * 4 + 1] +=
|
|
||||||
noise_y.get([px, py]) as f32 * directional_strength[1] * strength * sf;
|
|
||||||
path.points[i * 4 + 2] +=
|
|
||||||
noise_z.get([px, py]) as f32 * directional_strength[2] * strength * sf;
|
|
||||||
|
|
||||||
// Re-project onto sphere of radius seg_lens[i-1] centered at prev
|
|
||||||
let cur = Vec3::new(
|
|
||||||
path.points[i * 4],
|
|
||||||
path.points[i * 4 + 1],
|
|
||||||
path.points[i * 4 + 2],
|
|
||||||
);
|
|
||||||
let dir = cur - prev;
|
|
||||||
let dir_len = dir.length();
|
|
||||||
if dir_len > 0.0001 {
|
|
||||||
let corrected = prev + dir * (seg_lens[i - 1] / dir_len);
|
|
||||||
path.points[i * 4] = corrected.x;
|
|
||||||
path.points[i * 4 + 1] = corrected.y;
|
|
||||||
path.points[i * 4 + 2] = corrected.z;
|
|
||||||
prev = corrected;
|
|
||||||
} else {
|
|
||||||
prev = cur;
|
prev = cur;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for i in 0..path.length {
|
||||||
|
let a = i as f64 / (path.length - 1) as f64;
|
||||||
|
let px = j as f64 + a * length * scale;
|
||||||
|
let py = a * scale as f64;
|
||||||
|
let sf = lerp(1.0, a as f32, fix_bottom);
|
||||||
|
|
||||||
|
path.points[i * 4] += noise_x.get([px, py]) as f32
|
||||||
|
* directional_strength[0]
|
||||||
|
* strength
|
||||||
|
* sf;
|
||||||
|
path.points[i * 4 + 1] += noise_y.get([px, py]) as f32
|
||||||
|
* directional_strength[1]
|
||||||
|
* strength
|
||||||
|
* sf;
|
||||||
|
path.points[i * 4 + 2] += noise_z.get([px, py]) as f32
|
||||||
|
* directional_strength[2]
|
||||||
|
* strength
|
||||||
|
* sf;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
path_data
|
path_data
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user