New nodes: 'Position on Path' and 'Tangent on Path' (#2588)

This commit is contained in:
Keavon Chambers 2025-04-16 22:50:50 -07:00 committed by GitHub
parent c4484cb6cd
commit fa21385d2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 2 deletions

View file

@ -1237,6 +1237,79 @@ async fn sample_points(_: impl Ctx, vector_data: VectorDataTable, spacing: f64,
result
}
/// Determines the position of a point on the path, given by its progress from 0 to 1 along the path.
/// If multiple subpaths make up the path, the whole number part of the progress value selects the subpath and the decimal part determines the position along it.
#[node_macro::node(name("Position on Path"), category("Vector"), path(graphene_core::vector))]
async fn position_on_path(
_: impl Ctx,
/// The path to traverse.
vector_data: VectorDataTable,
/// The factor from the start to the end of the path, 01 for one subpath, 12 for a second subpath, and so on.
progress: f64,
/// Swap the direction of the path.
reverse: bool,
/// Traverse the path using each segment's Bézier curve parameterization instead of the Euclidean distance. Faster to compute but doesn't respect actual distances.
parameterized_distance: bool,
) -> DVec2 {
let euclidian = !parameterized_distance;
let vector_data_transform = vector_data.transform();
let vector_data = vector_data.one_instance().instance;
let subpaths_count = vector_data.stroke_bezier_paths().count() as f64;
let progress = progress.clamp(0., subpaths_count);
let progress = if reverse { subpaths_count - progress } else { progress };
let index = if progress >= subpaths_count { (subpaths_count - 1.) as usize } else { progress as usize };
vector_data.stroke_bezier_paths().nth(index).map_or(DVec2::ZERO, |mut subpath| {
subpath.apply_transform(vector_data_transform);
let t = if progress == subpaths_count { 1. } else { progress.fract() };
subpath.evaluate(if euclidian { SubpathTValue::GlobalEuclidean(t) } else { SubpathTValue::GlobalParametric(t) })
})
}
/// Determines the angle of the tangent at a point on the path, given by its progress from 0 to 1 along the path.
/// If multiple subpaths make up the path, the whole number part of the progress value selects the subpath and the decimal part determines the position along it.
#[node_macro::node(name("Tangent on Path"), category("Vector"), path(graphene_core::vector))]
async fn tangent_on_path(
_: impl Ctx,
/// The path to traverse.
vector_data: VectorDataTable,
/// The factor from the start to the end of the path, 01 for one subpath, 12 for a second subpath, and so on.
progress: f64,
/// Swap the direction of the path.
reverse: bool,
/// Traverse the path using each segment's Bézier curve parameterization instead of the Euclidean distance. Faster to compute but doesn't respect actual distances.
parameterized_distance: bool,
) -> f64 {
let euclidian = !parameterized_distance;
let vector_data_transform = vector_data.transform();
let vector_data = vector_data.one_instance().instance;
let subpaths_count = vector_data.stroke_bezier_paths().count() as f64;
let progress = progress.clamp(0., subpaths_count);
let progress = if reverse { subpaths_count - progress } else { progress };
let index = if progress >= subpaths_count { (subpaths_count - 1.) as usize } else { progress as usize };
vector_data.stroke_bezier_paths().nth(index).map_or(0., |mut subpath| {
subpath.apply_transform(vector_data_transform);
let t = if progress == subpaths_count { 1. } else { progress.fract() };
let mut tangent = subpath.tangent(if euclidian { SubpathTValue::GlobalEuclidean(t) } else { SubpathTValue::GlobalParametric(t) });
if tangent == DVec2::ZERO {
let t = t + if t > 0.5 { -0.001 } else { 0.001 };
tangent = subpath.tangent(if euclidian { SubpathTValue::GlobalEuclidean(t) } else { SubpathTValue::GlobalParametric(t) });
}
if tangent == DVec2::ZERO {
return 0.;
}
-tangent.angle_to(if reverse { -DVec2::X } else { DVec2::X })
})
}
#[node_macro::node(category(""), path(graphene_core::vector))]
async fn poisson_disk_points(
_: impl Ctx,