Refactor some vector nodes to use loops instead of one_instance, as a start to #1834 part 6

This commit is contained in:
Keavon Chambers 2025-04-22 18:17:56 -07:00
parent a6ff221c1e
commit db34ac3f53
2 changed files with 544 additions and 456 deletions

View File

@ -760,7 +760,15 @@ async fn gradient_map<T: Adjust<Color>>(
//
// Algorithm based on:
// https://stackoverflow.com/questions/33966121/what-is-the-algorithm-for-vibrance-filters
// The results of this implementation are very close to correct, but not quite perfect
// The results of this implementation are very close to correct, but not quite perfect.
//
// A bit of additional analysis can be found at here:
// https://www.photo-mark.com/notes/analyzing-photoshop-vibrance-and-saturation/
//
// This algorithm is currently lacking a "Saturation" parameter which is needed for interoperability.
// It's not the same as the saturation component of Hue/Saturation/Value. Vibrance and Saturation are both separable.
// When both parameters are set, it is equivalent to running this adjustment twice, with only vibrance set and then only saturation set.
// (Except for some noise probably due to rounding error.)
#[node_macro::node(category("Raster: Adjustment"))]
async fn vibrance<T: Adjust<Color>>(
_: impl Ctx,

View File

@ -446,8 +446,13 @@ async fn round_corners(
) -> VectorDataTable {
let source_transform = source.transform();
let source_transform_inverse = source_transform.inverse();
let source = source.one_instance_ref().instance;
let upstream_graphics_group = source.upstream_graphic_group.clone();
let mut result_table = VectorDataTable::empty();
for source in source.instance_ref_iter() {
let source = source.instance;
let upstream_graphic_group = source.upstream_graphic_group.clone();
// Flip the roundness to help with user intuition
let roundness = 1. - roundness;
@ -531,9 +536,16 @@ async fn round_corners(
result.append_subpath(rounded_subpath, false);
}
result.upstream_graphic_group = upstream_graphics_group;
let mut result_table = VectorDataTable::new(result);
*result_table.transform_mut() = source_transform;
result.upstream_graphic_group = upstream_graphic_group;
result_table.push(Instance {
instance: result,
transform: source_transform,
alpha_blending: Default::default(),
source_node_id: None,
});
}
result_table
}
@ -545,8 +557,12 @@ async fn spatial_merge_by_distance(
#[hard_min(0.0001)]
distance: f64,
) -> VectorDataTable {
let vector_data_transform = vector_data.transform();
let vector_data = vector_data.one_instance_ref().instance;
let mut result_table = VectorDataTable::empty();
for vector_data in vector_data.instance_ref_iter() {
let vector_data_transform = *vector_data.transform;
let vector_data = vector_data.instance;
let point_count = vector_data.point_domain.positions().len();
// Find min x and y for grid cell normalization
@ -659,18 +675,28 @@ async fn spatial_merge_by_distance(
result.segment_domain = new_segment_domain;
// Create and return the result
let mut result_table = VectorDataTable::new(result);
*result_table.transform_mut() = vector_data_transform;
result_table.push(Instance {
instance: result,
transform: vector_data_transform,
alpha_blending: Default::default(),
source_node_id: None,
});
}
result_table
}
#[node_macro::node(category("Debug"), path(graphene_core::vector))]
async fn box_warp(_: impl Ctx, vector_data: VectorDataTable, #[expose] rectangle: VectorDataTable) -> VectorDataTable {
let vector_data_transform = vector_data.transform();
let vector_data = vector_data.one_instance_ref().instance.clone();
let Some((target, target_transform)) = rectangle.get(0).map(|rect| (rect.instance, rect.transform)) else {
return vector_data;
};
let target_transform = rectangle.transform();
let target = rectangle.one_instance_ref().instance;
let mut result_table = VectorDataTable::empty();
for vector_data in vector_data.instance_ref_iter() {
let vector_data_transform = *vector_data.transform;
let vector_data = vector_data.instance;
// Get the bounding box of the source vector data
let source_bbox = vector_data.bounding_box_with_transform(vector_data_transform).unwrap_or([DVec2::ZERO, DVec2::ONE]);
@ -727,11 +753,14 @@ async fn box_warp(_: impl Ctx, vector_data: VectorDataTable, #[expose] rectangle
result.style.set_stroke_transform(DAffine2::IDENTITY);
// Create a new VectorDataTable with the result
let mut result_table = VectorDataTable::new(result);
// Reset the transform since we've applied it directly to the points
*result_table.transform_mut() = DAffine2::IDENTITY;
// Add this to the table and reset the transform since we've applied it directly to the points
result_table.push(Instance {
instance: result,
transform: DAffine2::IDENTITY,
alpha_blending: Default::default(),
source_node_id: None,
});
}
result_table
}
@ -755,8 +784,11 @@ async fn remove_handles(
#[soft_min(0.)]
max_handle_distance: f64,
) -> VectorDataTable {
let vector_data_transform = vector_data.transform();
let mut vector_data = vector_data.one_instance_ref().instance.clone();
let mut result_table = VectorDataTable::empty();
for vector_data in vector_data.instance_iter() {
let vector_data_transform = vector_data.transform;
let mut vector_data = vector_data.instance;
for (_, handles, start, end) in vector_data.segment_domain.handles_mut() {
// Only convert to linear if handles are within the threshold distance
@ -788,9 +820,15 @@ async fn remove_handles(
}
}
let mut result = VectorDataTable::new(vector_data);
*result.transform_mut() = vector_data_transform;
result
result_table.push(Instance {
instance: vector_data,
transform: vector_data_transform,
alpha_blending: Default::default(),
source_node_id: None,
});
}
result_table
}
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
@ -801,8 +839,11 @@ async fn generate_handles(
#[range((0., 1.))]
curvature: f64,
) -> VectorDataTable {
let source_transform = source.transform();
let source = source.one_instance_ref().instance;
let mut result_table = VectorDataTable::empty();
for source in source.instance_ref_iter() {
let source_transform = *source.transform;
let source = source.instance;
let mut result = VectorData::empty();
result.style = source.style.clone();
@ -872,8 +913,14 @@ async fn generate_handles(
result.append_subpath(softened_subpath, true);
}
let mut result_table = VectorDataTable::new(result);
*result_table.transform_mut() = source_transform;
result_table.push(Instance {
instance: result,
transform: source_transform,
alpha_blending: Default::default(),
source_node_id: None,
});
}
result_table
}
@ -887,37 +934,6 @@ async fn generate_handles(
// #[soft_max(8.)]
// subdivisions: f64,
// ) -> VectorDataTable {
// let source_transform = source.transform();
// let source_vector_data = source.one_instance().instance;
// let subdivisions = subdivisions as usize;
// let mut result = VectorData::empty();
// result.style = source_vector_data.style.clone();
// for mut subpath in source_vector_data.stroke_bezier_paths() {
// subpath.apply_transform(source_transform);
// if subpath.manipulator_groups().len() < 2 {
// // Not enough points to subdivide
// result.append_subpath(subpath, true);
// continue;
// }
// // Apply subdivisions recursively
// let mut current_subpath = subpath;
// for _ in 0..subdivisions {
// current_subpath = subdivide_once(&current_subpath);
// }
// current_subpath.apply_transform(source_transform.inverse());
// result.append_subpath(current_subpath, true);
// }
// let mut result_table = VectorDataTable::new(result);
// *result_table.transform_mut() = source_transform;
// result_table
// }
// fn subdivide_once(subpath: &Subpath<PointId>) -> Subpath<PointId> {
// let original_groups = subpath.manipulator_groups();
// let mut new_groups = Vec::new();
@ -936,7 +952,7 @@ async fn generate_handles(
// let current_bezier = original_groups[start_idx].to_bezier(&original_groups[end_idx]);
// // Create modified start point with original ID, but updated in_handle & out_handle
// let mut start_point = original_groups[start_idx].clone();
// let mut start_point = original_groups[start_idx];
// let [first, _] = current_bezier.split(TValue::Euclidean(0.5));
// start_point.out_handle = first.handle_start();
// start_point.in_handle = last_in_handle;
@ -966,7 +982,7 @@ async fn generate_handles(
// // Handle the final point for open paths
// if !is_closed && !original_groups.is_empty() {
// let mut last_point = original_groups.last().unwrap().clone();
// let mut last_point = *original_groups.last().unwrap();
// last_point.in_handle = last_in_handle;
// if new_groups.contains(&last_point) {
// debug!("last_point already in");
@ -981,10 +997,50 @@ async fn generate_handles(
// Subpath::new(new_groups, is_closed)
// }
// let mut result_table = VectorDataTable::empty();
// for source_vector_data in source.instances() {
// let source_transform = *source_vector_data.transform;
// let source_vector_data = source_vector_data.instance;
// let subdivisions = subdivisions as usize;
// let mut result = VectorData::empty();
// result.style = source_vector_data.style.clone();
// for mut subpath in source_vector_data.stroke_bezier_paths() {
// subpath.apply_transform(source_transform);
// if subpath.manipulator_groups().len() < 2 {
// // Not enough points to subdivide
// result.append_subpath(subpath, true);
// continue;
// }
// // Apply subdivisions recursively
// let mut current_subpath = subpath;
// for _ in 0..subdivisions {
// current_subpath = subdivide_once(&current_subpath);
// }
// current_subpath.apply_transform(source_transform.inverse());
// result.append_subpath(current_subpath, true);
// }
// let pushed = result_table.push(result);
// *pushed.transform = source_transform;
// }
// result_table
// }
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
async fn bounding_box(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable {
let vector_data_transform = vector_data.transform();
let vector_data = vector_data.one_instance_ref().instance;
let mut result_table = VectorDataTable::empty();
for vector_data in vector_data.instance_ref_iter() {
let vector_data_transform = *vector_data.transform;
let vector_data = vector_data.instance;
let mut result = vector_data
.bounding_box()
@ -993,25 +1049,34 @@ async fn bounding_box(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTa
result.style = vector_data.style.clone();
result.style.set_stroke_transform(DAffine2::IDENTITY);
let mut result = VectorDataTable::new(result);
*result.transform_mut() = vector_data_transform;
result
result_table.push(Instance {
instance: result,
transform: vector_data_transform,
alpha_blending: Default::default(),
source_node_id: None,
});
}
result_table
}
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
async fn dimensions(_: impl Ctx, vector_data: VectorDataTable) -> DVec2 {
let vector_data_transform = vector_data.transform();
let vector_data = vector_data.one_instance_ref().instance;
vector_data
.bounding_box_with_transform(vector_data_transform)
.instance_ref_iter()
.filter_map(|vector_data| vector_data.instance.bounding_box_with_transform(*vector_data.transform))
.reduce(|[acc_top_left, acc_bottom_right], [top_left, bottom_right]| [acc_top_left.min(top_left), acc_bottom_right.max(bottom_right)])
.map(|[top_left, bottom_right]| bottom_right - top_left)
.unwrap_or_default()
}
#[node_macro::node(category("Vector"), path(graphene_core::vector), properties("offset_path_properties"))]
async fn offset_path(_: impl Ctx, vector_data: VectorDataTable, distance: f64, line_join: LineJoin, #[default(4.)] miter_limit: f64) -> VectorDataTable {
let vector_data_transform = vector_data.transform();
let vector_data = vector_data.one_instance_ref().instance;
let mut result_table = VectorDataTable::empty();
for vector_data in vector_data.instance_ref_iter() {
let vector_data_transform = *vector_data.transform;
let vector_data = vector_data.instance;
let subpaths = vector_data.stroke_bezier_paths();
let mut result = VectorData::empty();
@ -1039,15 +1104,24 @@ async fn offset_path(_: impl Ctx, vector_data: VectorDataTable, distance: f64, l
result.append_subpath(subpath_out, false);
}
let mut result = VectorDataTable::new(result);
*result.transform_mut() = vector_data_transform;
result
result_table.push(Instance {
instance: result,
transform: vector_data_transform,
alpha_blending: Default::default(),
source_node_id: None,
});
}
result_table
}
#[node_macro::node(category("Vector"), path(graphene_core::vector))]
async fn solidify_stroke(_: impl Ctx, vector_data: VectorDataTable) -> VectorDataTable {
let vector_data_transform = vector_data.transform();
let vector_data = vector_data.one_instance_ref().instance;
let mut result_table = VectorDataTable::empty();
for vector_data in vector_data.instance_ref_iter() {
let vector_data_transform = *vector_data.transform;
let vector_data = vector_data.instance;
let stroke = vector_data.style.stroke().clone().unwrap_or_default();
let bezpaths = vector_data.stroke_bezpath_iter();
@ -1090,9 +1164,15 @@ async fn solidify_stroke(_: impl Ctx, vector_data: VectorDataTable) -> VectorDat
result.style.set_stroke(Stroke::default());
}
let mut result = VectorDataTable::new(result);
*result.transform_mut() = vector_data_transform;
result
result_table.push(Instance {
instance: result,
transform: vector_data_transform,
alpha_blending: Default::default(),
source_node_id: None,
});
}
result_table
}
#[node_macro::node(category("Vector"), path(graphene_core::vector))]