Fix tool overlays and snap targets to use upstream Path node geometry, not downstream final geometry (#4114)

* Fix tool overlays and snap targets to use upstream Path node geometry not downstream final geometry

* Snap to upstream Path free points, dedupe the lookup via a helper
This commit is contained in:
Keavon Chambers
2026-05-06 03:00:38 -07:00
committed by GitHub
parent 22a6e0d767
commit ecf94258fa
6 changed files with 151 additions and 32 deletions
@@ -26,7 +26,7 @@ use graphene_std::ContextDependencies;
use graphene_std::math::quad::Quad;
use graphene_std::subpath::Subpath;
use graphene_std::transform::Footprint;
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
use graphene_std::vector::click_target::{ClickTarget, ClickTargetType, FreePoint};
use graphene_std::vector::{PointId, Vector, VectorModificationType};
use kurbo::BezPath;
use memo_network::MemoNetwork;
@@ -3191,8 +3191,8 @@ impl NodeNetworkInterface {
let nodes = network_metadata
.persistent_metadata
.node_metadata
.iter()
.filter_map(|(node_id, _)| if self.is_layer(node_id, network_path) { Some(*node_id) } else { None })
.keys()
.filter_map(|node_id| if self.is_layer(node_id, network_path) { Some(*node_id) } else { None })
.collect::<Vec<_>>();
let layer_widths = nodes
.iter()
@@ -3234,6 +3234,39 @@ impl NodeNetworkInterface {
self.document_metadata.layer_vector_data.get(&layer).map(|arc| arc.as_ref().clone())
}
/// The vector geometry an upstream Path node would surface for editing.
/// This is the result of `compute_modified_vector`, but only if a visible 'Path' node is actually upstream.
/// Useful for tool overlays and snap target collection usages that want to match the Path tool's view
/// (e.g. the pre-solidified centerline for a Solidify Stroke layer) and otherwise do nothing.
pub fn upstream_path_node_vector(&self, layer: LayerNodeIdentifier) -> Option<Vector> {
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, self);
graph_layer.upstream_visible_node_id_from_name_in_layer(&DefinitionIdentifier::Network("Path".into()))?;
self.compute_modified_vector(layer)
}
/// Outline targets for the Select tool's hover/selection overlay, mirroring the Path tool's view.
/// Returns `Some` when an upstream Path node exists so the outline matches what the Path tool edits
/// (e.g. the pre-solidified centerline for a Solidify Stroke layer); returns `None` otherwise so the
/// caller can fall back to the layer's recorded `outlines`/`click_targets`.
pub fn path_aware_outline_targets(&self, layer: LayerNodeIdentifier) -> Option<Vec<ClickTargetType>> {
let vector = self.upstream_path_node_vector(layer)?;
let mut targets = Vec::new();
let subpaths: Vec<Subpath<PointId>> = vector.stroke_bezier_paths().collect();
if !subpaths.is_empty() {
targets.push(ClickTargetType::CompoundPath(subpaths));
}
for &point_id in vector.point_domain.ids() {
if !vector.any_connected(point_id) {
let position = vector.point_domain.position_from_id(point_id).unwrap_or_default();
targets.push(ClickTargetType::FreePoint(FreePoint::new(point_id, position)));
}
}
Some(targets)
}
/// Loads the structure of layer nodes from a node graph.
pub fn load_structure(&mut self) {
self.document_metadata.structure = HashMap::from_iter([(LayerNodeIdentifier::ROOT_PARENT, NodeRelations::default())]);