Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions node-graph/libraries/vector-types/src/vector/vector_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,38 @@ impl<Upstream> Vector<Upstream> {
}
}

pub fn detect_colinear_manipulators(&mut self)
where
Upstream: 'static,
{
self.colinear_manipulators.clear();
for index in 0..self.point_domain.ids().len() {
let point_pos = self.point_domain.positions()[index];

let mut connected = Vec::new();
for seg_id in self.segment_domain.start_connected(index) {
connected.push(HandleId::primary(seg_id));
}
for seg_id in self.segment_domain.end_connected(index) {
connected.push(HandleId::end(seg_id));
}

if connected.len() == 2 {
let h1 = connected[0];
let h2 = connected[1];

if let (Some(pos1), Some(pos2)) = (h1.to_manipulator_point().get_position(self), h2.to_manipulator_point().get_position(self)) {
let vec1 = (pos1 - point_pos).normalize_or_zero();
let vec2 = (pos2 - point_pos).normalize_or_zero();

if vec1.dot(vec2) < -0.99999 {
self.colinear_manipulators.push([h1, h2]);
}
}
}
}
}

pub fn concat(&mut self, additional: &Self, transform_of_additional: DAffine2, collision_hash_seed: u64) {
let point_map = additional
.point_domain
Expand Down
1 change: 1 addition & 0 deletions node-graph/nodes/path-bool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ fn boolean_operation_on_vector_table<'a>(vector: impl DoubleEndedIterator<Item =
for subpath in from_bez_paths(contours.contours().map(|c| &c.path)) {
row.element.append_subpath(subpath, false);
}
row.element.detect_colinear_manipulators();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Colinear metadata is computed before point merging, which mutates the geometry and invalidates the metadata. detect_colinear_manipulators() should run after merge_by_distance_spatial() or be re-run after the merge.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At node-graph/nodes/path-bool/src/lib.rs, line 144:

<comment>Colinear metadata is computed before point merging, which mutates the geometry and invalidates the metadata. `detect_colinear_manipulators()` should run after `merge_by_distance_spatial()` or be re-run after the merge.</comment>

<file context>
@@ -141,6 +141,7 @@ fn boolean_operation_on_vector_table<'a>(vector: impl DoubleEndedIterator<Item =
 	for subpath in from_bez_paths(contours.contours().map(|c| &c.path)) {
 		row.element.append_subpath(subpath, false);
 	}
+	row.element.detect_colinear_manipulators();
 
 	table.push(row);
</file context>


table.push(row);
table
Expand Down
35 changes: 23 additions & 12 deletions node-graph/nodes/vector/src/generator_nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ impl CornerRadius for [f64; 4] {
}
}

/// Sets colinear_manipulators for elliptical/circular shapes, pairing the end handle
/// of each segment with the primary handle of the next segment in a wrapping fashion.
fn set_ellipse_colinear_manipulators(vector: &mut Vector) {
let len = vector.segment_domain.ids().len();
for i in 0..len {
vector
.colinear_manipulators
.push([HandleId::end(vector.segment_domain.ids()[i]), HandleId::primary(vector.segment_domain.ids()[(i + 1) % len])]);
}
}

/// Generates a circle shape with a chosen radius.
#[node_macro::node(category("Vector: Shape"))]
fn circle(
Expand All @@ -49,7 +60,9 @@ fn circle(
radius: f64,
) -> Table<Vector> {
let radius = radius.abs();
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius))))
let mut circle = Vector::from_subpath(subpath::Subpath::new_ellipse(DVec2::splat(-radius), DVec2::splat(radius)));
set_ellipse_colinear_manipulators(&mut circle);
Table::new_from_element(circle)
}

/// Generates an arc shape forming a portion of a circle which may be open, closed, or a pie slice.
Expand All @@ -66,7 +79,7 @@ fn arc(
sweep_angle: Angle,
arc_type: ArcType,
) -> Table<Vector> {
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_arc(
let mut vector = Vector::from_subpath(subpath::Subpath::new_arc(
radius,
start_angle / 360. * std::f64::consts::TAU,
sweep_angle / 360. * std::f64::consts::TAU,
Expand All @@ -75,7 +88,9 @@ fn arc(
ArcType::Closed => subpath::ArcType::Closed,
ArcType::PieSlice => subpath::ArcType::PieSlice,
},
)))
));
vector.detect_colinear_manipulators();
Table::new_from_element(vector)
}

/// Generates a spiral shape that winds from an inner to an outer radius.
Expand All @@ -90,14 +105,16 @@ fn spiral(
#[default(25)] outer_radius: f64,
#[default(90.)] angular_resolution: f64,
) -> Table<Vector> {
Table::new_from_element(Vector::from_subpath(subpath::Subpath::new_spiral(
let mut vector = Vector::from_subpath(subpath::Subpath::new_spiral(
inner_radius,
outer_radius,
turns,
start_angle.to_radians(),
angular_resolution.to_radians(),
spiral_type,
)))
));
vector.detect_colinear_manipulators();
Table::new_from_element(vector)
}

/// Generates an ellipse shape (an oval or stretched circle) with the chosen radii.
Expand All @@ -117,13 +134,7 @@ fn ellipse(
let corner2 = radius;

let mut ellipse = Vector::from_subpath(subpath::Subpath::new_ellipse(corner1, corner2));

let len = ellipse.segment_domain.ids().len();
for i in 0..len {
ellipse
.colinear_manipulators
.push([HandleId::end(ellipse.segment_domain.ids()[i]), HandleId::primary(ellipse.segment_domain.ids()[(i + 1) % len])]);
}
set_ellipse_colinear_manipulators(&mut ellipse);

Table::new_from_element(ellipse)
}
Expand Down
Loading
Loading