feat(noise): preserve segment lengths during displacement

The noise node previously displaced each path point's XYZ independently,
which stretched/compressed segments and produced kinked edges. After
displacement, re-project each point onto the sphere of radius
seg_lens[i-1] centered at the previous point — same pattern used by the
gravity node. Total path length is now preserved; noise bends the path
rather than stretching it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 23:43:44 +02:00
parent 49746c6079
commit 68ae62527f
3 changed files with 62 additions and 13 deletions
Generated
+1
View File
@@ -117,6 +117,7 @@ dependencies = [
name = "noise" name = "noise"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"glam",
"nodarium_macros", "nodarium_macros",
"nodarium_utils", "nodarium_utils",
"noise 0.9.0", "noise 0.9.0",
+1
View File
@@ -8,6 +8,7 @@ edition = "2018"
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
glam = "0.30.10"
nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" } nodarium_macros = { version = "0.1.0", path = "../../../../packages/macros" }
nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" } nodarium_utils = { version = "0.1.0", path = "../../../../packages/utils" }
noise = "0.9.0" noise = "0.9.0"
+60 -13
View File
@@ -1,3 +1,4 @@
use glam::Vec3;
use nodarium_macros::nodarium_definition_file; use nodarium_macros::nodarium_definition_file;
use nodarium_macros::nodarium_execute; use nodarium_macros::nodarium_execute;
use nodarium_utils::{ use nodarium_utils::{
@@ -65,24 +66,70 @@ pub fn execute(input: &[i32]) -> Vec<i32> {
let length = path.get_length() as f64; let length = path.get_length() as f64;
for i in 0..path.length { // Record original segment lengths so we can re-project after displacement
let seg_lens: Vec<f32> = (0..path.length - 1)
.map(|k| {
let p0 = Vec3::new(
path.points[k * 4],
path.points[k * 4 + 1],
path.points[k * 4 + 2],
);
let p1 = Vec3::new(
path.points[(k + 1) * 4],
path.points[(k + 1) * 4 + 1],
path.points[(k + 1) * 4 + 2],
);
(p1 - p0).length()
})
.collect();
// Displace the first point (fix_bottom=1 → scale=0 here, anchoring the base)
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 a = i as f64 / (path.length - 1) as f64;
let px = j as f64 + a * length * scale; let px = j as f64 + a * length * scale;
let py = a * scale as f64; let py = a * scale as f64;
path.points[i * 4] += noise_x.get([px, py]) as f32 let sf = lerp(1.0, a as f32, fix_bottom);
* directional_strength[0] path.points[i * 4] +=
* strength noise_x.get([px, py]) as f32 * directional_strength[0] * strength * sf;
* lerp(1.0, a as f32, fix_bottom); path.points[i * 4 + 1] +=
path.points[i * 4 + 1] += noise_y.get([px, py]) as f32 noise_y.get([px, py]) as f32 * directional_strength[1] * strength * sf;
* directional_strength[1] path.points[i * 4 + 2] +=
* strength noise_z.get([px, py]) as f32 * directional_strength[2] * strength * sf;
* lerp(1.0, a as f32, fix_bottom);
path.points[i * 4 + 2] += noise_z.get([px, py]) as f32 // Re-project onto sphere of radius seg_lens[i-1] centered at prev
* directional_strength[2] let cur = Vec3::new(
* strength path.points[i * 4],
* lerp(1.0, a as f32, fix_bottom); 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;
}
} }
path_data path_data
}) })