Skip to content

Commit 11ba2cc

Browse files
committed
New node: 'Split Segments'
1 parent fdb09e3 commit 11ba2cc

File tree

4 files changed

+70
-27
lines changed

4 files changed

+70
-27
lines changed

node-graph/gcore/src/ops.rs

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -441,30 +441,6 @@ fn color_value(_: impl Ctx, _primary: (), #[default(Color::BLACK)] color: Option
441441
color
442442
}
443443

444-
// // Aims for interoperable compatibility with:
445-
// // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=%27grdm%27%20%3D%20Gradient%20Map
446-
// // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#:~:text=Gradient%20settings%20(Photoshop%206.0)
447-
// #[node_macro::node(category("Raster: Adjustment"))]
448-
// async fn gradient_map<T: Adjust<Color>>(
449-
// _: impl Ctx,
450-
// #[implementations(
451-
// Color,
452-
// RasterDataTable<CPU>,
453-
// GradientStops,
454-
// )]
455-
// mut image: T,
456-
// gradient: GradientStops,
457-
// reverse: bool,
458-
// ) -> T {
459-
// image.adjust(|color| {
460-
// let intensity = color.luminance_srgb();
461-
// let intensity = if reverse { 1. - intensity } else { intensity };
462-
// gradient.evaluate(intensity as f64)
463-
// });
464-
465-
// image
466-
// }
467-
468444
/// Gets the color at the specified position along the gradient, given a position from 0 (left) to 1 (right).
469445
#[node_macro::node(category("Color"))]
470446
fn sample_gradient(_: impl Ctx, _primary: (), gradient: GradientStops, position: Fraction) -> Color {

node-graph/gcore/src/vector/vector_data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ impl VectorData {
412412
}
413413

414414
pub fn other_colinear_handle(&self, handle: HandleId) -> Option<HandleId> {
415-
let pair = self.colinear_manipulators.iter().find(|pair| pair.iter().any(|&val| val == handle))?;
415+
let pair = self.colinear_manipulators.iter().find(|pair| pair.contains(&handle))?;
416416
let other = pair.iter().copied().find(|&val| val != handle)?;
417417
if handle.to_manipulator_point().get_anchor(self) == other.to_manipulator_point().get_anchor(self) {
418418
Some(other)

node-graph/gcore/src/vector/vector_data/attributes.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,11 @@ impl PointDomain {
132132
self.position.push(position);
133133
}
134134

135+
pub fn push_unchecked(&mut self, id: PointId, position: DVec2) {
136+
self.id.push(id);
137+
self.position.push(position);
138+
}
139+
135140
pub fn positions(&self) -> &[DVec2] {
136141
&self.position
137142
}

node-graph/gcore/src/vector/vector_nodes.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,8 +1314,11 @@ async fn sample_points(_: impl Ctx, vector_data: VectorDataTable, spacing: f64,
13141314
result_table
13151315
}
13161316

1317+
/// 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).
1318+
///
1319+
/// 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.
13171320
#[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))]
1318-
async fn split_path(_: impl Ctx, mut vector_data: VectorDataTable, t_value: f64, parameterized_distance: bool, reverse: bool) -> VectorDataTable {
1321+
async fn split_path(_: impl Ctx, mut vector_data: VectorDataTable, progress: Fraction, parameterized_distance: bool, reverse: bool) -> VectorDataTable {
13191322
let euclidian = !parameterized_distance;
13201323

13211324
let bezpaths = vector_data
@@ -1325,7 +1328,7 @@ async fn split_path(_: impl Ctx, mut vector_data: VectorDataTable, t_value: f64,
13251328
.collect::<Vec<_>>();
13261329

13271330
let bezpath_count = bezpaths.len() as f64;
1328-
let t_value = t_value.clamp(0., bezpath_count);
1331+
let t_value = progress.clamp(0., bezpath_count);
13291332
let t_value = if reverse { bezpath_count - t_value } else { t_value };
13301333
let index = if t_value >= bezpath_count { (bezpath_count - 1.) as usize } else { t_value as usize };
13311334

@@ -1353,7 +1356,65 @@ async fn split_path(_: impl Ctx, mut vector_data: VectorDataTable, t_value: f64,
13531356
vector_data
13541357
}
13551358

1359+
/// Splits path segments into separate disconnected pieces where each is a distinct subpath.
1360+
#[node_macro::node(category("Vector: Modifier"), path(graphene_core::vector))]
1361+
async fn split_segments(_: impl Ctx, mut vector_data: VectorDataTable) -> VectorDataTable {
1362+
// 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
1363+
for vector_data_instance in vector_data.instance_mut_iter() {
1364+
let points_count = vector_data_instance.instance.point_domain.ids().len();
1365+
let segments_count = vector_data_instance.instance.segment_domain.ids().len();
1366+
1367+
let mut point_usages = vec![0_usize; points_count];
1368+
1369+
// Count how many times each point is used as an endpoint of the segments
1370+
let start_points = vector_data_instance.instance.segment_domain.start_point().iter();
1371+
let end_points = vector_data_instance.instance.segment_domain.end_point().iter();
1372+
for (&start, &end) in start_points.zip(end_points) {
1373+
point_usages[start] += 1;
1374+
point_usages[end] += 1;
1375+
}
1376+
1377+
let mut new_points = PointDomain::new();
1378+
let mut offset_sum: usize = 0;
1379+
let mut points_with_new_offsets = Vec::with_capacity(points_count);
1380+
1381+
// Build a new point domain with the original points, but with duplications based on their extra usages by the segments
1382+
for (index, (point_id, point)) in vector_data_instance.instance.point_domain.iter().enumerate() {
1383+
// Ensure at least one usage to preserve free-floating points not connected to any segments
1384+
let usage_count = point_usages[index].max(1);
1385+
1386+
new_points.push_unchecked(point_id, point);
1387+
1388+
for i in 1..usage_count {
1389+
new_points.push_unchecked(point_id.generate_from_hash(i as u64), point);
1390+
}
1391+
1392+
points_with_new_offsets.push(offset_sum);
1393+
offset_sum += usage_count;
1394+
}
1395+
1396+
// Reconcile the segment domain with the new points
1397+
vector_data_instance.instance.point_domain = new_points;
1398+
for original_segment_index in 0..segments_count {
1399+
let original_point_start_index = vector_data_instance.instance.segment_domain.start_point()[original_segment_index];
1400+
let original_point_end_index = vector_data_instance.instance.segment_domain.end_point()[original_segment_index];
1401+
1402+
point_usages[original_point_start_index] -= 1;
1403+
point_usages[original_point_end_index] -= 1;
1404+
1405+
let start_usage = points_with_new_offsets[original_point_start_index] + point_usages[original_point_start_index];
1406+
let end_usage = points_with_new_offsets[original_point_end_index] + point_usages[original_point_end_index];
1407+
1408+
vector_data_instance.instance.segment_domain.set_start_point(original_segment_index, start_usage);
1409+
vector_data_instance.instance.segment_domain.set_end_point(original_segment_index, end_usage);
1410+
}
1411+
}
1412+
1413+
vector_data
1414+
}
1415+
13561416
/// Determines the position of a point on the path, given by its progress from 0 to 1 along the path.
1417+
///
13571418
/// 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.
13581419
#[node_macro::node(name("Position on Path"), category("Vector: Measure"), path(graphene_core::vector))]
13591420
async fn position_on_path(
@@ -1390,6 +1451,7 @@ async fn position_on_path(
13901451
}
13911452

13921453
/// Determines the angle of the tangent at a point on the path, given by its progress from 0 to 1 along the path.
1454+
///
13931455
/// 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.
13941456
#[node_macro::node(name("Tangent on Path"), category("Vector: Measure"), path(graphene_core::vector))]
13951457
async fn tangent_on_path(

0 commit comments

Comments
 (0)