Skip to content

Commit

Permalink
Add clipping to the click xray function
Browse files Browse the repository at this point in the history
  • Loading branch information
0HyperCube authored and Keavon committed Oct 12, 2024
1 parent 3b0e958 commit 92f463e
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 9 deletions.
7 changes: 6 additions & 1 deletion editor/src/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,19 @@ impl Dispatcher {
self.message_queues.extend(buffered_queue);
};

let graphene_std::renderer::RenderMetadata { footprints, click_targets } = render_metadata;
let graphene_std::renderer::RenderMetadata {
footprints,
click_targets,
clip_targets,
} = render_metadata;

let mut update_upstream_transform = VecDeque::new();
update_upstream_transform.push_back(DocumentMessage::UpdateUpstreamTransforms { upstream_transforms: footprints }.into());
self.message_queues.push(update_upstream_transform);

let mut update_click_targets = VecDeque::new();
update_click_targets.push_back(DocumentMessage::UpdateClickTargets { click_targets }.into());
update_click_targets.push_back(DocumentMessage::UpdateClipTargets { clip_targets }.into());
self.message_queues.push(update_click_targets);
}
Message::NoOp => {}
Expand Down
3 changes: 3 additions & 0 deletions editor/src/messages/portfolio/document/document_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ pub enum DocumentMessage {
UpdateClickTargets {
click_targets: HashMap<NodeId, Vec<ClickTarget>>,
},
UpdateClipTargets {
clip_targets: HashSet<NodeId>,
},
UpdateVectorModify {
vector_modify: HashMap<NodeId, VectorData>,
},
Expand Down
63 changes: 56 additions & 7 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,9 @@ impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessag
.collect();
self.network_interface.update_click_targets(layer_click_targets);
}
DocumentMessage::UpdateClipTargets { clip_targets } => {
self.network_interface.update_clip_targets(clip_targets);
}
DocumentMessage::UpdateVectorModify { vector_modify } => {
self.network_interface.update_vector_modify(vector_modify);
}
Expand Down Expand Up @@ -1351,13 +1354,12 @@ impl DocumentMessageHandler {
pub fn click_xray(&self, ipp: &InputPreprocessorMessageHandler) -> impl Iterator<Item = LayerNodeIdentifier> + '_ {
let document_to_viewport = self.navigation_handler.calculate_offset_transform(ipp.viewport_bounds.center(), &self.document_ptz);
let point = document_to_viewport.inverse().transform_point2(ipp.mouse.position);
self.metadata()
.all_layers()
.filter(|&layer| self.network_interface.selected_nodes(&[]).unwrap().layer_visible(layer, &self.network_interface))
.filter(|&layer| !self.network_interface.selected_nodes(&[]).unwrap().layer_locked(layer, &self.network_interface))
.filter_map(|layer| self.metadata().click_targets(layer).map(|targets| (layer, targets)))
.filter(move |(layer, target)| target.iter().any(|target| target.intersect_point(point, self.metadata().transform_to_document(*layer))))
.map(|(layer, _)| layer)

ClickXRayIter {
next_layer: LayerNodeIdentifier::ROOT_PARENT.first_child(self.metadata()),
network_interface: &self.network_interface,
point,
}
}

/// Find the deepest layer given in the sorted array (by returning the one which is not a folder from the list of layers under the click location).
Expand Down Expand Up @@ -2099,3 +2101,50 @@ fn default_document_network_interface() -> NodeNetworkInterface {
network_interface.add_export(TaggedValue::ArtboardGroup(graphene_core::ArtboardGroup::EMPTY), -1, "".to_string(), &[]);
network_interface
}

#[derive(Clone)]
pub struct ClickXRayIter<'a> {
next_layer: Option<LayerNodeIdentifier>,
network_interface: &'a NodeNetworkInterface,
point: DVec2,
}

impl ClickXRayIter<'_> {
fn check_clicked_and_use_children(&self, layer: LayerNodeIdentifier) -> (bool, bool) {
let (mut clicked, mut use_children) = (true, true);
let selected_layers = self.network_interface.selected_nodes(&[]).unwrap();
if !selected_layers.layer_visible(layer, &self.network_interface) || selected_layers.layer_locked(layer, &self.network_interface) {
return (false, false); // Skip this layer and children if the layer is invisible or locked
}
let click_targets = self.network_interface.document_metadata().click_targets(layer);
let transform = self.network_interface.document_metadata().transform_to_document(layer);
let intersects = click_targets.map_or(false, |targets| targets.iter().any(|target| target.intersect_point(self.point, transform)));

if !intersects {
clicked = false;
}

if self.network_interface.document_metadata().is_clip(layer.to_node()) && !intersects {
use_children = false;
}
(clicked, use_children)
}
}

impl<'a> Iterator for ClickXRayIter<'a> {
type Item = LayerNodeIdentifier;

fn next(&mut self) -> Option<Self::Item> {
while let Some(layer) = self.next_layer.take() {
let (clicked, use_children) = self.check_clicked_and_use_children(layer);
let metadata = self.network_interface.document_metadata();
let child = use_children.then(|| layer.first_child(metadata)).flatten();
self.next_layer = child.or_else(|| layer.ancestors(metadata).find_map(|ancestor| ancestor.next_sibling(metadata)));

if clicked {
return Some(layer);
}
}
None
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use graphene_std::vector::VectorData;

use glam::{DAffine2, DVec2};
use std::collections::HashMap;
use std::collections::HashSet;
use std::num::NonZeroU64;

// ================
Expand All @@ -21,6 +22,7 @@ pub struct DocumentMetadata {
pub upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
pub structure: HashMap<LayerNodeIdentifier, NodeRelations>,
pub click_targets: HashMap<LayerNodeIdentifier, Vec<ClickTarget>>,
pub clip_targets: HashSet<NodeId>,
pub vector_modify: HashMap<NodeId, VectorData>,
/// Transform from document space to viewport space.
pub document_to_viewport: DAffine2,
Expand All @@ -33,6 +35,7 @@ impl Default for DocumentMetadata {
structure: HashMap::new(),
vector_modify: HashMap::new(),
click_targets: HashMap::new(),
clip_targets: HashSet::new(),
document_to_viewport: DAffine2::IDENTITY,
}
}
Expand Down Expand Up @@ -149,6 +152,10 @@ impl DocumentMetadata {
let click_targets = self.click_targets.get(&layer).unwrap_or(&EMPTY);
click_targets.iter().map(ClickTarget::subpath)
}

pub fn is_clip(&self, node: NodeId) -> bool {
self.clip_targets.contains(&node)
}
}

// ===================
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2930,6 +2930,11 @@ impl NodeNetworkInterface {
self.document_metadata.click_targets = new_click_targets;
}

/// Update the cached clip targets of the layers
pub fn update_clip_targets(&mut self, new_clip_targets: HashSet<NodeId>) {
self.document_metadata.clip_targets = new_clip_targets;
}

/// Update the vector modify of the layers
pub fn update_vector_modify(&mut self, new_vector_modify: HashMap<NodeId, VectorData>) {
self.document_metadata.vector_modify = new_vector_modify;
Expand Down
6 changes: 5 additions & 1 deletion node-graph/gcore/src/graphic_element/renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use dyn_any::DynAny;
use base64::Engine;
use glam::{DAffine2, DVec2};
use num_traits::Zero;
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
#[cfg(feature = "vello")]
use vello::*;
Expand Down Expand Up @@ -277,6 +277,7 @@ pub fn to_transform(transform: DAffine2) -> usvg::Transform {
pub struct RenderMetadata {
pub footprints: HashMap<NodeId, (Footprint, DAffine2)>,
pub click_targets: HashMap<NodeId, Vec<ClickTarget>>,
pub clip_targets: HashSet<NodeId>,
}

pub trait GraphicElementRendered {
Expand Down Expand Up @@ -650,6 +651,9 @@ impl GraphicElementRendered for Artboard {
let subpath = Subpath::new_rect(DVec2::ZERO, self.dimensions.as_dvec2());
metadata.click_targets.insert(element_id, vec![ClickTarget::new(subpath, 0.)]);
metadata.footprints.insert(element_id, (footprint, DAffine2::from_translation(self.location.as_dvec2())));
if self.clip {
metadata.clip_targets.insert(element_id);
}
}
footprint.transform *= self.transform();
self.graphic_group.collect_metadata(metadata, footprint, None);
Expand Down
2 changes: 2 additions & 0 deletions node-graph/gstd/src/wasm_application_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use base64::Engine;
#[cfg(target_arch = "wasm32")]
use glam::DAffine2;
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::Clamped;
Expand Down Expand Up @@ -225,6 +226,7 @@ async fn render<'a: 'n, T: 'n + GraphicElementRendered + WasmNotSend>(
let mut metadata = RenderMetadata {
footprints: HashMap::new(),
click_targets: HashMap::new(),
clip_targets: HashSet::new(),
};
data.collect_metadata(&mut metadata, footprint, None);

Expand Down

0 comments on commit 92f463e

Please sign in to comment.