First instance source id

This commit is contained in:
hypercube 2025-07-07 00:41:30 +01:00
parent 24c6281644
commit 7bd6b2f3e0
10 changed files with 91 additions and 52 deletions

View file

@ -140,6 +140,7 @@ impl Dispatcher {
let graphene_std::renderer::RenderMetadata {
upstream_footprints: footprints,
local_transforms,
first_instance_source_id,
click_targets,
clip_targets,
} = render_metadata;
@ -149,6 +150,7 @@ impl Dispatcher {
DocumentMessage::UpdateUpstreamTransforms {
upstream_footprints: footprints,
local_transforms,
first_instance_source_id,
},
DocumentMessage::UpdateClickTargets { click_targets },
DocumentMessage::UpdateClipTargets { clip_targets },

View file

@ -182,6 +182,7 @@ pub enum DocumentMessage {
UpdateUpstreamTransforms {
upstream_footprints: HashMap<NodeId, Footprint>,
local_transforms: HashMap<NodeId, DAffine2>,
first_instance_source_id: HashMap<NodeId, Option<NodeId>>,
},
UpdateClickTargets {
click_targets: HashMap<NodeId, Vec<ClickTarget>>,

View file

@ -1305,8 +1305,10 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
DocumentMessage::UpdateUpstreamTransforms {
upstream_footprints,
local_transforms,
first_instance_source_id,
} => {
self.network_interface.update_transforms(upstream_footprints, local_transforms);
self.network_interface.update_first_instance_source_id(first_instance_source_id);
}
DocumentMessage::UpdateClickTargets { click_targets } => {
// TODO: Allow non layer nodes to have click targets

View file

@ -119,7 +119,7 @@ pub fn path_overlays(document: &DocumentMessageHandler, draw_handles: DrawHandle
for layer in document.network_interface.selected_nodes().selected_layers(document.metadata()) {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else { continue };
let transform = document.metadata().transform_to_viewport(layer);
let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
if display_path {
overlay_context.outline_vector(&vector_data, transform);
}
@ -196,7 +196,7 @@ pub fn path_endpoint_overlays(document: &DocumentMessageHandler, shape_editor: &
continue;
};
//let document_to_viewport = document.navigation_handler.calculate_offset_transform(overlay_context.size / 2., &document.document_ptz);
let transform = document.metadata().transform_to_viewport(layer);
let transform = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
let selected = shape_editor.selected_shape_state.get(&layer);
let is_selected = |selected: Option<&SelectedLayerState>, point: ManipulatorPointId| selected.is_some_and(|selected| selected.is_point_selected(point));

View file

@ -1,6 +1,8 @@
use super::network_interface::NodeNetworkInterface;
use crate::messages::portfolio::document::graph_operation::transform_utils;
use crate::messages::portfolio::document::graph_operation::utility_types::ModifyInputsContext;
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
use crate::messages::tool::common_functionality::graph_modification_utils;
use glam::{DAffine2, DVec2};
use graph_craft::document::NodeId;
use graphene_std::math::quad::Quad;
@ -16,10 +18,11 @@ use std::num::NonZeroU64;
// TODO: To avoid storing a stateful snapshot of some other system's state (which is easily to accidentally get out of sync),
// TODO: it might be better to have a system that can query the state of the node network on demand.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Default)]
pub struct DocumentMetadata {
pub upstream_footprints: HashMap<NodeId, Footprint>,
pub local_transforms: HashMap<NodeId, DAffine2>,
pub first_instance_source_ids: HashMap<NodeId, Option<NodeId>>,
pub structure: HashMap<LayerNodeIdentifier, NodeRelations>,
pub click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
pub clip_targets: HashSet<NodeId>,
@ -28,20 +31,6 @@ pub struct DocumentMetadata {
pub document_to_viewport: DAffine2,
}
impl Default for DocumentMetadata {
fn default() -> Self {
Self {
upstream_footprints: HashMap::new(),
local_transforms: HashMap::new(),
structure: HashMap::new(),
vector_modify: HashMap::new(),
click_targets: HashMap::new(),
clip_targets: HashSet::new(),
document_to_viewport: DAffine2::IDENTITY,
}
}
}
// =================================
// DocumentMetadata: Layer iterators
// =================================
@ -91,6 +80,38 @@ impl DocumentMetadata {
footprint * local_transform
}
pub fn transform_to_viewport_if_feeds(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 {
// We're not allowed to convert the root parent to a node id
if layer == LayerNodeIdentifier::ROOT_PARENT {
return self.document_to_viewport;
}
let footprint = self.upstream_footprints.get(&layer.to_node()).map(|footprint| footprint.transform).unwrap_or(self.document_to_viewport);
let mut use_local = true;
let graph_layer = graph_modification_utils::NodeGraphLayer::new(layer, network_interface);
info!("feeds");
if let Some(path_node) = graph_layer.upstream_node_id_from_name("Path") {
info!("Path node {path_node:?} ");
if let Some(&source) = self.first_instance_source_ids.get(&layer.to_node()) {
if !network_interface
.upstream_flow_back_from_nodes(vec![path_node], &[], FlowType::HorizontalFlow)
.any(|upstream| Some(upstream) == source)
{
use_local = false;
}
info!("Path node {path_node:?} source {source:?} {use_local}");
}
}
let local_transform = use_local.then(|| self.local_transforms.get(&layer.to_node()).copied()).flatten().unwrap_or_default();
footprint * local_transform
}
pub fn transform_to_document_if_feeds(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 {
self.document_to_viewport.inverse() * self.transform_to_viewport_if_feeds(layer, network_interface)
}
pub fn transform_to_viewport_with_first_transform_node_if_group(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface) -> DAffine2 {
let footprint = self.upstream_footprints.get(&layer.to_node()).map(|footprint| footprint.transform).unwrap_or(self.document_to_viewport);
let local_transform = self.local_transforms.get(&layer.to_node()).copied();

View file

@ -3513,6 +3513,11 @@ impl NodeNetworkInterface {
self.document_metadata.local_transforms = local_transforms;
}
/// Update the cached first instance source id of the layers
pub fn update_first_instance_source_id(&mut self, new: HashMap<NodeId, Option<NodeId>>) {
self.document_metadata.first_instance_source_ids = new;
}
/// Update the cached click targets of the layers
pub fn update_click_targets(&mut self, new_click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>) {
self.document_metadata.click_targets = new_click_targets;

View file

@ -212,15 +212,15 @@ impl ClosestSegment {
self.bezier_point_to_viewport
}
pub fn closest_point(&self, document_metadata: &DocumentMetadata) -> DVec2 {
let transform = document_metadata.transform_to_viewport(self.layer);
pub fn closest_point(&self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface) -> DVec2 {
let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface);
let bezier_point = self.bezier.evaluate(TValue::Parametric(self.t));
transform.transform_point2(bezier_point)
}
/// Updates this [`ClosestSegment`] with the viewport-space location of the closest point on the segment to the given mouse position.
pub fn update_closest_point(&mut self, document_metadata: &DocumentMetadata, mouse_position: DVec2) {
let transform = document_metadata.transform_to_viewport(self.layer);
pub fn update_closest_point(&mut self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface, mouse_position: DVec2) {
let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface);
let layer_mouse_pos = transform.inverse().transform_point2(mouse_position);
let t = self.bezier.project(layer_mouse_pos).clamp(0., 1.);
@ -239,9 +239,9 @@ impl ClosestSegment {
tolerance.powi(2) < self.distance_squared(mouse_position)
}
pub fn handle_positions(&self, document_metadata: &DocumentMetadata) -> (Option<DVec2>, Option<DVec2>) {
pub fn handle_positions(&self, document_metadata: &DocumentMetadata, network_interface: &NodeNetworkInterface) -> (Option<DVec2>, Option<DVec2>) {
// Transform to viewport space
let transform = document_metadata.transform_to_viewport(self.layer);
let transform = document_metadata.transform_to_viewport_if_feeds(self.layer, network_interface);
// Split the Bezier at the parameter `t`
let [first, second] = self.bezier.split(TValue::Parametric(self.t));
@ -307,7 +307,7 @@ impl ClosestSegment {
}
pub fn calculate_perp(&self, document: &DocumentMessageHandler) -> DVec2 {
let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata()) {
let tangent = if let (Some(handle1), Some(handle2)) = self.handle_positions(document.metadata(), &document.network_interface) {
(handle1 - handle2).try_normalize()
} else {
let [first_point, last_point] = self.points();
@ -339,7 +339,7 @@ impl ClosestSegment {
break_colinear_molding: bool,
temporary_adjacent_handles_while_molding: Option<[Option<HandleId>; 2]>,
) -> Option<[Option<HandleId>; 2]> {
let transform = document.metadata().transform_to_viewport(self.layer);
let transform = document.metadata().transform_to_viewport_if_feeds(self.layer, &document.network_interface);
let start = self.bezier.start;
let end = self.bezier.end;
@ -507,7 +507,7 @@ impl ShapeState {
continue;
};
let to_document = document.metadata().transform_to_document(*layer);
let to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface);
for &selected in &state.selected_points {
let source = match selected {
@ -564,7 +564,11 @@ impl ShapeState {
let already_selected = selected_shape_state.is_point_selected(manipulator_point_id);
// Offset to snap the selected point to the cursor
let offset = mouse_position - network_interface.document_metadata().transform_to_viewport(layer).transform_point2(point_position);
let offset = mouse_position
- network_interface
.document_metadata()
.transform_to_viewport_if_feeds(layer, network_interface)
.transform_point2(point_position);
// This is selecting the manipulator only for now, next to generalize to points
@ -621,7 +625,11 @@ impl ShapeState {
let already_selected = selected_shape_state.is_point_selected(manipulator_point_id);
// Offset to snap the selected point to the cursor
let offset = mouse_position - network_interface.document_metadata().transform_to_viewport(layer).transform_point2(point_position);
let offset = mouse_position
- network_interface
.document_metadata()
.transform_to_viewport_if_feeds(layer, network_interface)
.transform_point2(point_position);
// Gather current selection information
let points = self
@ -653,7 +661,7 @@ impl ShapeState {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
return;
};
let to_viewport = document.metadata().transform_to_viewport(layer);
let to_viewport = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
let layer_mouse = to_viewport.inverse().transform_point2(mouse);
let state = self.selected_shape_state.entry(layer).or_default();
@ -875,7 +883,7 @@ impl ShapeState {
}
let vector_data = network_interface.compute_modified_vector(layer)?;
let transform = network_interface.document_metadata().transform_to_document(layer).inverse();
let transform = network_interface.document_metadata().transform_to_document_if_feeds(layer, network_interface).inverse();
let position = transform.transform_point2(new_position);
let current_position = point.get_position(&vector_data)?;
let delta = position - current_position;
@ -1026,7 +1034,7 @@ impl ShapeState {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let transform = document.metadata().transform_to_document(layer);
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
for &point in layer_state.selected_points.iter() {
let Some(handles) = point.get_handle_pair(&vector_data) else { continue };
@ -1116,8 +1124,8 @@ impl ShapeState {
let opposing_handles = handle_lengths.as_ref().and_then(|handle_lengths| handle_lengths.get(&layer));
let transform_to_viewport_space = document.metadata().transform_to_viewport(layer);
let transform_to_document_space = document.metadata().transform_to_document(layer);
let transform_to_viewport_space = document.metadata().transform_to_viewport_if_feeds(layer, &document.network_interface);
let transform_to_document_space = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
let delta_transform = if in_viewport_space {
transform_to_viewport_space
} else {
@ -1210,7 +1218,7 @@ impl ShapeState {
.iter()
.filter_map(|(&layer, state)| {
let vector_data = document.network_interface.compute_modified_vector(layer)?;
let transform = document.metadata().transform_to_document(layer);
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
let opposing_handle_lengths = vector_data
.colinear_manipulators
.iter()
@ -1575,7 +1583,7 @@ impl ShapeState {
let mut manipulator_point = None;
let vector_data = network_interface.compute_modified_vector(layer)?;
let viewspace = network_interface.document_metadata().transform_to_viewport(layer);
let viewspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
// Handles
for (segment_id, bezier, _, _) in vector_data.segment_bezier_iter() {
@ -1611,7 +1619,7 @@ impl ShapeState {
/// Find the `t` value along the path segment we have clicked upon, together with that segment ID.
fn closest_segment(&self, network_interface: &NodeNetworkInterface, layer: LayerNodeIdentifier, position: glam::DVec2, tolerance: f64) -> Option<ClosestSegment> {
let transform = network_interface.document_metadata().transform_to_viewport(layer);
let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
let layer_pos = transform.inverse().transform_point2(position);
let tolerance = tolerance + 0.5;
@ -1785,7 +1793,7 @@ impl ShapeState {
pub fn flip_smooth_sharp(&self, network_interface: &NodeNetworkInterface, target: glam::DVec2, tolerance: f64, responses: &mut VecDeque<Message>) -> bool {
let mut process_layer = |layer| {
let vector_data = network_interface.compute_modified_vector(layer)?;
let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport(layer);
let transform_to_screenspace = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
let mut result = None;
let mut closest_distance_squared = tolerance * tolerance;
@ -1889,7 +1897,7 @@ impl ShapeState {
let vector_data = network_interface.compute_modified_vector(layer);
let Some(vector_data) = vector_data else { continue };
let transform = network_interface.document_metadata().transform_to_viewport(layer);
let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
assert_eq!(vector_data.segment_domain.ids().len(), vector_data.start_point().count());
assert_eq!(vector_data.segment_domain.ids().len(), vector_data.end_point().count());

View file

@ -718,7 +718,7 @@ impl PathToolData {
let Some(vector_data) = document.network_interface.compute_modified_vector(layer) else {
continue;
};
let transform = document.metadata().transform_to_document(layer);
let transform = document.metadata().transform_to_document_if_feeds(layer, &document.network_interface);
let mut layer_manipulators = HashSet::with_hasher(NoHashBuilder);
for point in state.selected_points() {
@ -828,7 +828,7 @@ impl PathToolData {
let selected_handle = selection.selected_points().next()?.as_handle()?;
let handle_id = selected_handle.to_manipulator_point();
let layer_to_document = document.metadata().transform_to_document(*layer);
let layer_to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface);
let vector_data = document.network_interface.compute_modified_vector(*layer)?;
let handle_position_local = selected_handle.to_manipulator_point().get_position(&vector_data)?;
@ -870,7 +870,7 @@ impl PathToolData {
let anchor = handle_id.get_anchor(&vector_data);
let (angle, anchor_position) = calculate_adjacent_anchor_tangent(handle_id, anchor, adjacent_anchor, &vector_data);
let layer_to_document = document.metadata().transform_to_document(*layer);
let layer_to_document = document.metadata().transform_to_document_if_feeds(*layer, &document.network_interface);
self.adjacent_anchor_offset = handle_id
.get_anchor_position(&vector_data)
@ -1017,7 +1017,7 @@ impl PathToolData {
}
// If already hovering on a segment, then recalculate its closest point
else if let Some(closest_segment) = &mut self.segment {
closest_segment.update_closest_point(document.metadata(), position);
closest_segment.update_closest_point(document.metadata(), &document.network_interface, position);
if closest_segment.too_far(position, SEGMENT_INSERTION_DISTANCE) {
self.segment = None;
@ -1084,7 +1084,7 @@ impl PathToolData {
let layer = sliding_point_info.layer;
let Some(vector_data) = network_interface.compute_modified_vector(layer) else { return };
let transform = network_interface.document_metadata().transform_to_viewport(layer);
let transform = network_interface.document_metadata().transform_to_viewport_if_feeds(layer, network_interface);
let layer_pos = transform.inverse().transform_point2(target_position);
let segments = sliding_point_info.connected_segments;
@ -1412,7 +1412,7 @@ impl Fsm for PathToolFsmState {
if let Some(closest_segment) = &tool_data.segment {
if tool_options.path_editing_mode.segment_editing_mode {
let transform = document.metadata().transform_to_viewport(closest_segment.layer());
let transform = document.metadata().transform_to_viewport_if_feeds(closest_segment.layer(), &document.network_interface);
overlay_context.outline_overlay_bezier(closest_segment.bezier(), transform);
@ -1430,7 +1430,7 @@ impl Fsm for PathToolFsmState {
}
} else {
let perp = closest_segment.calculate_perp(document);
let point = closest_segment.closest_point(document.metadata());
let point = closest_segment.closest_point(document.metadata(), &document.network_interface);
// Draw an X on the segment
if tool_data.delete_segment_pressed {
@ -2280,7 +2280,10 @@ fn get_selection_status(network_interface: &NodeNetworkInterface, shape_state: &
return SelectionStatus::None;
};
let coordinates = network_interface.document_metadata().transform_to_document(layer).transform_point2(local_position);
let coordinates = network_interface
.document_metadata()
.transform_to_document_if_feeds(layer, network_interface)
.transform_point2(local_position);
let manipulator_angle = if vector_data.colinear(point) { ManipulatorAngle::Colinear } else { ManipulatorAngle::Free };
return SelectionStatus::One(SingleSelectedPoint {

View file

@ -278,12 +278,7 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>(
#[cfg(all(feature = "vello", not(test)))]
let use_vello = use_vello && surface_handle.is_some();
let mut metadata = RenderMetadata {
upstream_footprints: HashMap::new(),
local_transforms: HashMap::new(),
click_targets: HashMap::new(),
clip_targets: HashSet::new(),
};
let mut metadata = RenderMetadata::default();
data.collect_metadata(&mut metadata, footprint, None);
let output_format = render_config.export_format;

View file

@ -198,6 +198,7 @@ pub fn to_transform(transform: DAffine2) -> usvg::Transform {
pub struct RenderMetadata {
pub upstream_footprints: HashMap<NodeId, Footprint>,
pub local_transforms: HashMap<NodeId, DAffine2>,
pub first_instance_source_id: HashMap<NodeId, Option<NodeId>>,
pub click_targets: HashMap<NodeId, Vec<ClickTarget>>,
pub clip_targets: HashSet<NodeId>,
}
@ -1090,6 +1091,7 @@ impl GraphicElementRendered for GraphicElement {
metadata.upstream_footprints.insert(element_id, footprint);
// TODO: Find a way to handle more than one row of the graphical data table
if let Some(vector_data) = vector_data.instance_ref_iter().next() {
metadata.first_instance_source_id.insert(element_id, *vector_data.source_node_id);
metadata.local_transforms.insert(element_id, *vector_data.transform);
}
}