mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-07-07 15:55:00 +00:00
New node: 'Split Segments'
This commit is contained in:
parent
fdb09e334b
commit
11ba2cc0fe
4 changed files with 70 additions and 27 deletions
|
@ -441,30 +441,6 @@ fn color_value(_: impl Ctx, _primary: (), #[default(Color::BLACK)] color: Option
|
||||||
color
|
color
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Aims for interoperable compatibility with:
|
|
||||||
// // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27grdm%27%20%3D%20Gradient%20Map
|
|
||||||
// // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Gradient%20settings%20(Photoshop%206.0)
|
|
||||||
// #[node_macro::node(category("Raster: Adjustment"))]
|
|
||||||
// async fn gradient_map<T: Adjust<Color>>(
|
|
||||||
// _: impl Ctx,
|
|
||||||
// #[implementations(
|
|
||||||
// Color,
|
|
||||||
// RasterDataTable<CPU>,
|
|
||||||
// GradientStops,
|
|
||||||
// )]
|
|
||||||
// mut image: T,
|
|
||||||
// gradient: GradientStops,
|
|
||||||
// reverse: bool,
|
|
||||||
// ) -> T {
|
|
||||||
// image.adjust(|color| {
|
|
||||||
// let intensity = color.luminance_srgb();
|
|
||||||
// let intensity = if reverse { 1. - intensity } else { intensity };
|
|
||||||
// gradient.evaluate(intensity as f64)
|
|
||||||
// });
|
|
||||||
|
|
||||||
// image
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// Gets the color at the specified position along the gradient, given a position from 0 (left) to 1 (right).
|
/// Gets the color at the specified position along the gradient, given a position from 0 (left) to 1 (right).
|
||||||
#[node_macro::node(category("Color"))]
|
#[node_macro::node(category("Color"))]
|
||||||
fn sample_gradient(_: impl Ctx, _primary: (), gradient: GradientStops, position: Fraction) -> Color {
|
fn sample_gradient(_: impl Ctx, _primary: (), gradient: GradientStops, position: Fraction) -> Color {
|
||||||
|
|
|
@ -412,7 +412,7 @@ impl VectorData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn other_colinear_handle(&self, handle: HandleId) -> Option<HandleId> {
|
pub fn other_colinear_handle(&self, handle: HandleId) -> Option<HandleId> {
|
||||||
let pair = self.colinear_manipulators.iter().find(|pair| pair.iter().any(|&val| val == handle))?;
|
let pair = self.colinear_manipulators.iter().find(|pair| pair.contains(&handle))?;
|
||||||
let other = pair.iter().copied().find(|&val| val != handle)?;
|
let other = pair.iter().copied().find(|&val| val != handle)?;
|
||||||
if handle.to_manipulator_point().get_anchor(self) == other.to_manipulator_point().get_anchor(self) {
|
if handle.to_manipulator_point().get_anchor(self) == other.to_manipulator_point().get_anchor(self) {
|
||||||
Some(other)
|
Some(other)
|
||||||
|
|
|
@ -132,6 +132,11 @@ impl PointDomain {
|
||||||
self.position.push(position);
|
self.position.push(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn push_unchecked(&mut self, id: PointId, position: DVec2) {
|
||||||
|
self.id.push(id);
|
||||||
|
self.position.push(position);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn positions(&self) -> &[DVec2] {
|
pub fn positions(&self) -> &[DVec2] {
|
||||||
&self.position
|
&self.position
|
||||||
}
|
}
|
||||||
|
|
|
@ -1314,8 +1314,11 @@ async fn sample_points(_: impl Ctx, vector_data: VectorDataTable, spacing: f64,
|
||||||
result_table
|
result_table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Splits a path at a given progress from 0 to 1 along the path, creating two new subpaths from the original one (if the path is initially open) or one open subpath (if the path is initially closed).
|
||||||
|
///
|
||||||
|
/// 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(category("Vector: Modifier"), path(graphene_core::vector))]
|
#[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))]
|
||||||
async fn split_path(_: impl Ctx, mut vector_data: VectorDataTable, t_value: f64, parameterized_distance: bool, reverse: bool) -> VectorDataTable {
|
async fn split_path(_: impl Ctx, mut vector_data: VectorDataTable, progress: Fraction, parameterized_distance: bool, reverse: bool) -> VectorDataTable {
|
||||||
let euclidian = !parameterized_distance;
|
let euclidian = !parameterized_distance;
|
||||||
|
|
||||||
let bezpaths = vector_data
|
let bezpaths = vector_data
|
||||||
|
@ -1325,7 +1328,7 @@ async fn split_path(_: impl Ctx, mut vector_data: VectorDataTable, t_value: f64,
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let bezpath_count = bezpaths.len() as f64;
|
let bezpath_count = bezpaths.len() as f64;
|
||||||
let t_value = t_value.clamp(0., bezpath_count);
|
let t_value = progress.clamp(0., bezpath_count);
|
||||||
let t_value = if reverse { bezpath_count - t_value } else { t_value };
|
let t_value = if reverse { bezpath_count - t_value } else { t_value };
|
||||||
let index = if t_value >= bezpath_count { (bezpath_count - 1.) as usize } else { t_value as usize };
|
let index = if t_value >= bezpath_count { (bezpath_count - 1.) as usize } else { t_value as usize };
|
||||||
|
|
||||||
|
@ -1353,7 +1356,65 @@ async fn split_path(_: impl Ctx, mut vector_data: VectorDataTable, t_value: f64,
|
||||||
vector_data
|
vector_data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Splits path segments into separate disconnected pieces where each is a distinct subpath.
|
||||||
|
#[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))]
|
||||||
|
async fn split_segments(_: impl Ctx, mut vector_data: VectorDataTable) -> VectorDataTable {
|
||||||
|
// Iterate through every segment and make a copy of each of its endpoints, then reassign each segment's endpoints to its own unique point copy
|
||||||
|
for vector_data_instance in vector_data.instance_mut_iter() {
|
||||||
|
let points_count = vector_data_instance.instance.point_domain.ids().len();
|
||||||
|
let segments_count = vector_data_instance.instance.segment_domain.ids().len();
|
||||||
|
|
||||||
|
let mut point_usages = vec![0_usize; points_count];
|
||||||
|
|
||||||
|
// Count how many times each point is used as an endpoint of the segments
|
||||||
|
let start_points = vector_data_instance.instance.segment_domain.start_point().iter();
|
||||||
|
let end_points = vector_data_instance.instance.segment_domain.end_point().iter();
|
||||||
|
for (&start, &end) in start_points.zip(end_points) {
|
||||||
|
point_usages[start] += 1;
|
||||||
|
point_usages[end] += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut new_points = PointDomain::new();
|
||||||
|
let mut offset_sum: usize = 0;
|
||||||
|
let mut points_with_new_offsets = Vec::with_capacity(points_count);
|
||||||
|
|
||||||
|
// Build a new point domain with the original points, but with duplications based on their extra usages by the segments
|
||||||
|
for (index, (point_id, point)) in vector_data_instance.instance.point_domain.iter().enumerate() {
|
||||||
|
// Ensure at least one usage to preserve free-floating points not connected to any segments
|
||||||
|
let usage_count = point_usages[index].max(1);
|
||||||
|
|
||||||
|
new_points.push_unchecked(point_id, point);
|
||||||
|
|
||||||
|
for i in 1..usage_count {
|
||||||
|
new_points.push_unchecked(point_id.generate_from_hash(i as u64), point);
|
||||||
|
}
|
||||||
|
|
||||||
|
points_with_new_offsets.push(offset_sum);
|
||||||
|
offset_sum += usage_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reconcile the segment domain with the new points
|
||||||
|
vector_data_instance.instance.point_domain = new_points;
|
||||||
|
for original_segment_index in 0..segments_count {
|
||||||
|
let original_point_start_index = vector_data_instance.instance.segment_domain.start_point()[original_segment_index];
|
||||||
|
let original_point_end_index = vector_data_instance.instance.segment_domain.end_point()[original_segment_index];
|
||||||
|
|
||||||
|
point_usages[original_point_start_index] -= 1;
|
||||||
|
point_usages[original_point_end_index] -= 1;
|
||||||
|
|
||||||
|
let start_usage = points_with_new_offsets[original_point_start_index] + point_usages[original_point_start_index];
|
||||||
|
let end_usage = points_with_new_offsets[original_point_end_index] + point_usages[original_point_end_index];
|
||||||
|
|
||||||
|
vector_data_instance.instance.segment_domain.set_start_point(original_segment_index, start_usage);
|
||||||
|
vector_data_instance.instance.segment_domain.set_end_point(original_segment_index, end_usage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector_data
|
||||||
|
}
|
||||||
|
|
||||||
/// Determines the position of a point on the path, given by its progress from 0 to 1 along the path.
|
/// 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.
|
/// 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: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(name("Position on Path"), category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
async fn position_on_path(
|
async fn position_on_path(
|
||||||
|
@ -1390,6 +1451,7 @@ async fn position_on_path(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the angle of the tangent at a point on the path, given by its progress from 0 to 1 along the path.
|
/// 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.
|
/// 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: Measure"), path(graphene_core::vector))]
|
#[node_macro::node(name("Tangent on Path"), category("Vector: Measure"), path(graphene_core::vector))]
|
||||||
async fn tangent_on_path(
|
async fn tangent_on_path(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue