Skip to content

Commit fa45efa

Browse files
authored
New node: Pointer Position (#3535)
* New node: Pointer Position * Fix test
1 parent 4ab75c9 commit fa45efa

File tree

11 files changed

+119
-18
lines changed

11 files changed

+119
-18
lines changed

editor/src/messages/portfolio/portfolio_message_handler.rs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,16 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
383383
for document_id in self.document_ids.iter() {
384384
let node_to_inspect = self.node_to_inspect();
385385

386+
let Some(document) = self.documents.get_mut(document_id) else {
387+
log::error!("Tried to render non-existent document");
388+
continue;
389+
};
390+
391+
let document_to_viewport = document
392+
.navigation_handler
393+
.calculate_offset_transform(viewport.center_in_viewport_space().into(), &document.document_ptz);
394+
let pointer_position = document_to_viewport.inverse().transform_point2(ipp.mouse.position);
395+
386396
let scale = viewport.scale();
387397
// Use exact physical dimensions from browser (via ResizeObserver's devicePixelContentBoxSize)
388398
let physical_resolution = viewport.size().to_physical().into_dvec2().round().as_uvec2();
@@ -395,6 +405,7 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
395405
timing_information,
396406
node_to_inspect,
397407
true,
408+
pointer_position,
398409
) {
399410
responses.add_front(message);
400411
}
@@ -1054,13 +1065,18 @@ impl MessageHandler<PortfolioMessage, PortfolioMessageContext<'_>> for Portfolio
10541065
return;
10551066
};
10561067

1068+
let document_to_viewport = document
1069+
.navigation_handler
1070+
.calculate_offset_transform(viewport.center_in_viewport_space().into(), &document.document_ptz);
1071+
let pointer_position = document_to_viewport.inverse().transform_point2(ipp.mouse.position);
1072+
10571073
let scale = viewport.scale();
10581074
// Use exact physical dimensions from browser (via ResizeObserver's devicePixelContentBoxSize)
10591075
let physical_resolution = viewport.size().to_physical().into_dvec2().round().as_uvec2();
10601076

10611077
let result = self
10621078
.executor
1063-
.submit_node_graph_evaluation(document, document_id, physical_resolution, scale, timing_information, node_to_inspect, ignore_hash);
1079+
.submit_node_graph_evaluation(document, document_id, physical_resolution, scale, timing_information, node_to_inspect, ignore_hash, pointer_position);
10641080

10651081
match result {
10661082
Err(description) => {

editor/src/node_graph_executor.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
22
use crate::messages::prelude::*;
3-
use glam::{DAffine2, UVec2};
3+
use glam::{DAffine2, DVec2, UVec2};
44
use graph_craft::document::value::{RenderOutput, TaggedValue};
55
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, NodeId, NodeInput};
66
use graph_craft::proto::GraphErrors;
@@ -81,6 +81,7 @@ impl NodeGraphExecutor {
8181
};
8282
(node_runtime, node_executor)
8383
}
84+
8485
/// Execute the network by flattening it and creating a borrow stack.
8586
fn queue_execution(&mut self, render_config: RenderConfig) -> u64 {
8687
let execution_id = self.current_execution_id;
@@ -140,6 +141,7 @@ impl NodeGraphExecutor {
140141
viewport_resolution: UVec2,
141142
viewport_scale: f64,
142143
time: TimingInformation,
144+
pointer: DVec2,
143145
) -> Result<Message, String> {
144146
let viewport = Footprint {
145147
transform: document.metadata().document_to_viewport,
@@ -150,6 +152,7 @@ impl NodeGraphExecutor {
150152
viewport,
151153
scale: viewport_scale,
152154
time,
155+
pointer,
153156
export_format: graphene_std::application_io::ExportFormat::Raster,
154157
render_mode: document.render_mode,
155158
hide_artboards: false,
@@ -175,9 +178,10 @@ impl NodeGraphExecutor {
175178
time: TimingInformation,
176179
node_to_inspect: Option<NodeId>,
177180
ignore_hash: bool,
181+
pointer: DVec2,
178182
) -> Result<Message, String> {
179183
self.update_node_graph(document, node_to_inspect, ignore_hash)?;
180-
self.submit_current_node_graph_evaluation(document, document_id, viewport_resolution, viewport_scale, time)
184+
self.submit_current_node_graph_evaluation(document, document_id, viewport_resolution, viewport_scale, time, pointer)
181185
}
182186

183187
/// Evaluates a node graph for export
@@ -208,6 +212,7 @@ impl NodeGraphExecutor {
208212
},
209213
scale: export_config.scale_factor,
210214
time: Default::default(),
215+
pointer: DVec2::ZERO,
211216
export_format,
212217
render_mode: document.render_mode,
213218
hide_artboards: export_config.transparent_background,

editor/src/test_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl EditorTestUtils {
4848
Err(e) => return Err(format!("update_node_graph_instrumented failed\n\n{e}")),
4949
};
5050

51-
if let Err(e) = exector.submit_current_node_graph_evaluation(document, DocumentId(0), UVec2::ONE, 1., Default::default()) {
51+
if let Err(e) = exector.submit_current_node_graph_evaluation(document, DocumentId(0), UVec2::ONE, 1., Default::default(), DVec2::ZERO) {
5252
return Err(format!("submit_current_node_graph_evaluation failed\n\n{e}"));
5353
}
5454
runtime.run().await;

node-graph/interpreted-executor/src/util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub fn wrap_network_in_scope(mut network: NodeNetwork, editor_api: Arc<WasmEdito
5757
implementation: DocumentNodeImplementation::ProtoNode(graphene_std::render_node::create_context::IDENTIFIER),
5858
context_features: graphene_std::ContextDependencies {
5959
extract: ContextFeatures::empty(),
60-
inject: ContextFeatures::REAL_TIME | ContextFeatures::ANIMATION_TIME | ContextFeatures::FOOTPRINT | ContextFeatures::VARARGS,
60+
inject: ContextFeatures::REAL_TIME | ContextFeatures::ANIMATION_TIME | ContextFeatures::POINTER | ContextFeatures::FOOTPRINT | ContextFeatures::VARARGS,
6161
},
6262
..Default::default()
6363
},

node-graph/libraries/application-io/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ pub struct RenderConfig {
239239
pub scale: f64,
240240
pub export_format: ExportFormat,
241241
pub time: TimingInformation,
242+
pub pointer: DVec2,
242243
#[serde(alias = "view_mode")]
243244
pub render_mode: RenderMode,
244245
pub hide_artboards: bool,

node-graph/libraries/core-types/src/context.rs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::transform::Footprint;
2+
use glam::DVec2;
23
pub use no_std_types::context::{ArcCtx, Ctx};
34
use std::any::Any;
45
use std::borrow::Borrow;
@@ -26,6 +27,10 @@ pub trait ExtractAnimationTime {
2627
fn try_animation_time(&self) -> Option<f64>;
2728
}
2829

30+
pub trait ExtractPointer {
31+
fn try_pointer(&self) -> Option<DVec2>;
32+
}
33+
2934
pub trait ExtractIndex {
3035
fn try_index(&self) -> Option<impl Iterator<Item = usize>>;
3136
}
@@ -47,30 +52,34 @@ pub trait CloneVarArgs: ExtractVarArgs {
4752
pub trait InjectFootprint {}
4853
pub trait InjectRealTime {}
4954
pub trait InjectAnimationTime {}
55+
pub trait InjectPointer {}
5056
pub trait InjectIndex {}
5157
pub trait InjectVarArgs {}
5258

5359
// Modify* marker traits for context-transparent nodes
5460
pub trait ModifyFootprint: ExtractFootprint + InjectFootprint {}
5561
pub trait ModifyRealTime: ExtractRealTime + InjectRealTime {}
5662
pub trait ModifyAnimationTime: ExtractAnimationTime + InjectAnimationTime {}
63+
pub trait ModifyPointer: ExtractPointer + InjectPointer {}
5764
pub trait ModifyIndex: ExtractIndex + InjectIndex {}
5865
pub trait ModifyVarArgs: ExtractVarArgs + InjectVarArgs {}
5966

60-
pub trait ExtractAll: ExtractFootprint + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs {}
67+
pub trait ExtractAll: ExtractFootprint + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractPointer + ExtractVarArgs {}
6168

62-
impl<T: ?Sized + ExtractFootprint + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractVarArgs> ExtractAll for T {}
69+
impl<T: ?Sized + ExtractFootprint + ExtractIndex + ExtractRealTime + ExtractAnimationTime + ExtractPointer + ExtractVarArgs> ExtractAll for T {}
6370

6471
impl<T: Ctx> InjectFootprint for T {}
6572
impl<T: Ctx> InjectRealTime for T {}
6673
impl<T: Ctx> InjectIndex for T {}
6774
impl<T: Ctx> InjectAnimationTime for T {}
75+
impl<T: Ctx> InjectPointer for T {}
6876
impl<T: Ctx> InjectVarArgs for T {}
6977

7078
impl<T: Ctx + InjectFootprint + ExtractFootprint> ModifyFootprint for T {}
7179
impl<T: Ctx + InjectRealTime + ExtractRealTime> ModifyRealTime for T {}
7280
impl<T: Ctx + InjectIndex + ExtractIndex> ModifyIndex for T {}
7381
impl<T: Ctx + InjectAnimationTime + ExtractAnimationTime> ModifyAnimationTime for T {}
82+
impl<T: Ctx + InjectPointer + ExtractPointer> ModifyPointer for T {}
7483
impl<T: Ctx + InjectVarArgs + ExtractVarArgs> ModifyVarArgs for T {}
7584

7685
// Public enum for flexible node macro codegen
@@ -79,11 +88,13 @@ pub enum ContextFeature {
7988
ExtractFootprint,
8089
ExtractRealTime,
8190
ExtractAnimationTime,
91+
ExtractPointer,
8292
ExtractIndex,
8393
ExtractVarArgs,
8494
InjectFootprint,
8595
InjectRealTime,
8696
InjectAnimationTime,
97+
InjectPointer,
8798
InjectIndex,
8899
InjectVarArgs,
89100
}
@@ -96,8 +107,9 @@ bitflags! {
96107
const FOOTPRINT = 1 << 0;
97108
const REAL_TIME = 1 << 1;
98109
const ANIMATION_TIME = 1 << 2;
99-
const INDEX = 1 << 3;
100-
const VARARGS = 1 << 4;
110+
const POINTER = 1 << 3;
111+
const INDEX = 1 << 4;
112+
const VARARGS = 1 << 5;
101113
}
102114
}
103115

@@ -116,6 +128,7 @@ impl From<&[ContextFeature]> for ContextDependencies {
116128
ContextFeature::ExtractFootprint => ContextFeatures::FOOTPRINT,
117129
ContextFeature::ExtractRealTime => ContextFeatures::REAL_TIME,
118130
ContextFeature::ExtractAnimationTime => ContextFeatures::ANIMATION_TIME,
131+
ContextFeature::ExtractPointer => ContextFeatures::POINTER,
119132
ContextFeature::ExtractIndex => ContextFeatures::INDEX,
120133
ContextFeature::ExtractVarArgs => ContextFeatures::VARARGS,
121134
_ => ContextFeatures::empty(),
@@ -124,6 +137,7 @@ impl From<&[ContextFeature]> for ContextDependencies {
124137
ContextFeature::InjectFootprint => ContextFeatures::FOOTPRINT,
125138
ContextFeature::InjectRealTime => ContextFeatures::REAL_TIME,
126139
ContextFeature::InjectAnimationTime => ContextFeatures::ANIMATION_TIME,
140+
ContextFeature::InjectPointer => ContextFeatures::POINTER,
127141
ContextFeature::InjectIndex => ContextFeatures::INDEX,
128142
ContextFeature::InjectVarArgs => ContextFeatures::VARARGS,
129143
_ => ContextFeatures::empty(),
@@ -174,6 +188,11 @@ impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Option<T> {
174188
self.as_ref().and_then(|x| x.try_animation_time())
175189
}
176190
}
191+
impl<T: ExtractPointer + Sync> ExtractPointer for Option<T> {
192+
fn try_pointer(&self) -> Option<DVec2> {
193+
self.as_ref().and_then(|x| x.try_pointer())
194+
}
195+
}
177196
impl<T: ExtractIndex> ExtractIndex for Option<T> {
178197
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
179198
self.as_ref().and_then(|x| x.try_index())
@@ -211,6 +230,11 @@ impl<T: ExtractAnimationTime + Sync> ExtractAnimationTime for Arc<T> {
211230
(**self).try_animation_time()
212231
}
213232
}
233+
impl<T: ExtractPointer + Sync> ExtractPointer for Arc<T> {
234+
fn try_pointer(&self) -> Option<DVec2> {
235+
(**self).try_pointer()
236+
}
237+
}
214238
impl<T: ExtractIndex> ExtractIndex for Arc<T> {
215239
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
216240
(**self).try_index()
@@ -303,6 +327,11 @@ impl ExtractAnimationTime for OwnedContextImpl {
303327
self.animation_time
304328
}
305329
}
330+
impl ExtractPointer for OwnedContextImpl {
331+
fn try_pointer(&self) -> Option<DVec2> {
332+
self.pointer
333+
}
334+
}
306335
impl ExtractIndex for OwnedContextImpl {
307336
fn try_index(&self) -> Option<impl Iterator<Item = usize>> {
308337
self.index.clone().map(|x| x.into_iter().rev())
@@ -363,6 +392,7 @@ pub struct OwnedContextImpl {
363392
index: Option<Vec<usize>>,
364393
real_time: Option<f64>,
365394
animation_time: Option<f64>,
395+
pointer: Option<DVec2>,
366396
}
367397

368398
impl std::fmt::Debug for OwnedContextImpl {
@@ -374,6 +404,7 @@ impl std::fmt::Debug for OwnedContextImpl {
374404
.field("index", &self.index)
375405
.field("real_time", &self.real_time)
376406
.field("animation_time", &self.animation_time)
407+
.field("pointer", &self.pointer)
377408
.finish()
378409
}
379410
}
@@ -392,6 +423,7 @@ impl Hash for OwnedContextImpl {
392423
self.index.hash(state);
393424
self.real_time.map(|x| x.to_bits()).hash(state);
394425
self.animation_time.map(|x| x.to_bits()).hash(state);
426+
self.pointer.map(|v| (v.x.to_bits(), v.y.to_bits())).hash(state);
395427
}
396428
}
397429

@@ -400,12 +432,14 @@ impl OwnedContextImpl {
400432
pub fn from<T: ExtractAll + CloneVarArgs>(value: T) -> Self {
401433
OwnedContextImpl::from_flags(value, ContextFeatures::all())
402434
}
435+
403436
#[track_caller]
404437
pub fn from_flags<T: ExtractAll + CloneVarArgs>(value: T, bitflags: ContextFeatures) -> Self {
405438
let footprint = bitflags.contains(ContextFeatures::FOOTPRINT).then(|| value.try_footprint().copied()).flatten();
406439
let index = bitflags.contains(ContextFeatures::INDEX).then(|| value.try_index()).flatten();
407440
let real_time = bitflags.contains(ContextFeatures::REAL_TIME).then(|| value.try_real_time()).flatten();
408441
let animation_time = bitflags.contains(ContextFeatures::ANIMATION_TIME).then(|| value.try_animation_time()).flatten();
442+
let pointer = bitflags.contains(ContextFeatures::POINTER).then(|| value.try_pointer()).flatten();
409443
let parent = bitflags
410444
.contains(ContextFeatures::VARARGS)
411445
.then(|| match value.varargs_len() {
@@ -421,8 +455,10 @@ impl OwnedContextImpl {
421455
index: index.map(|x| x.collect()),
422456
real_time,
423457
animation_time,
458+
pointer,
424459
}
425460
}
461+
426462
pub const fn empty() -> Self {
427463
OwnedContextImpl {
428464
footprint: None,
@@ -431,6 +467,7 @@ impl OwnedContextImpl {
431467
index: None,
432468
real_time: None,
433469
animation_time: None,
470+
pointer: None,
434471
}
435472
}
436473
}
@@ -475,6 +512,10 @@ impl OwnedContextImpl {
475512
self.animation_time = Some(animation_time);
476513
self
477514
}
515+
pub fn with_pointer(mut self, pointer: DVec2) -> Self {
516+
self.pointer = Some(pointer);
517+
self
518+
}
478519
pub fn with_vararg(mut self, value: Box<dyn AnyHash + Send + Sync>) -> Self {
479520
assert!(self.varargs.is_none_or(|value| value.is_empty()));
480521
self.varargs = Some(Arc::new([value]));

node-graph/libraries/vector-types/src/vector/vector_attributes.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1112,11 +1112,11 @@ impl<Upstream> Vector<Upstream> {
11121112
for neighbors in &mut adjacency {
11131113
neighbors.sort_by(|a, b| {
11141114
let angle = [a, b].map(|side| {
1115-
let curve = PathSeg::from(self.path_segment_from_index(
1115+
let curve = self.path_segment_from_index(
11161116
self.segment_domain.start_point[side.segment_index],
11171117
self.segment_domain.end_point[side.segment_index],
11181118
self.segment_domain.handles[side.segment_index],
1119-
));
1119+
);
11201120
let curve = if side.reversed { curve.reverse() } else { curve };
11211121
let tangent = curve.tangent_at_start();
11221122
tangent.y.atan2(tangent.x)
@@ -1140,20 +1140,19 @@ impl<Upstream> Vector<Upstream> {
11401140
}
11411141
}
11421142

1143-
return FaceIterator::new(faces, self);
1143+
FaceIterator::new(faces, self)
11441144
}
11451145

1146-
fn construct_face(&self, adjacency: &Vec<Vec<FaceSide>>, first: FaceSide, faces: &mut Faces, seen: &mut FaceSideSet) -> Option<()> {
1146+
fn construct_face(&self, adjacency: &[Vec<FaceSide>], first: FaceSide, faces: &mut Faces, seen: &mut FaceSideSet) -> Option<()> {
11471147
faces.start_new_face();
11481148
let max_iterations = self.segment_domain.id.len() * 2;
11491149
let mut side = first;
11501150
for _iteration in 1..max_iterations {
11511151
if seen.contains(side) {
1152-
log::debug!("Encountered seen side {:?}, aborting face construction", side);
11531152
return None;
11541153
}
11551154
seen.insert(side);
1156-
faces.add_side(side.clone());
1155+
faces.add_side(side);
11571156
let next_vertex = if side.reversed {
11581157
self.segment_domain.start_point[side.segment_index]
11591158
} else {

node-graph/node-macro/src/parsing.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,11 +414,13 @@ fn parse_context_feature_idents(ty: &Type) -> Vec<Ident> {
414414
"ExtractFootprint"
415415
| "ExtractRealTime"
416416
| "ExtractAnimationTime"
417+
| "ExtractPointer"
417418
| "ExtractIndex"
418419
| "ExtractVarArgs"
419420
| "InjectFootprint"
420421
| "InjectRealTime"
421422
| "InjectAnimationTime"
423+
| "InjectPointer"
422424
| "InjectIndex"
423425
| "InjectVarArgs" => {
424426
features.push(segment.ident.clone());

node-graph/nodes/gcore/src/animation.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use core_types::{Ctx, ExtractAnimationTime, ExtractRealTime};
1+
use core_types::{Ctx, ExtractAnimationTime, ExtractPointer, ExtractRealTime};
2+
use glam::DVec2;
23

34
const DAY: f64 = 1000. * 3600. * 24.;
45

@@ -47,6 +48,12 @@ fn animation_time(ctx: impl Ctx + ExtractAnimationTime) -> f64 {
4748
ctx.try_animation_time().unwrap_or_default()
4849
}
4950

51+
/// Produces the current position of the user's pointer within the document canvas.
52+
#[node_macro::node(category("Animation"))]
53+
fn pointer_position(ctx: impl Ctx + ExtractPointer) -> DVec2 {
54+
ctx.try_pointer().unwrap_or_default()
55+
}
56+
5057
// TODO: These nodes require more sophisticated algorithms for giving the correct result
5158
// #[node_macro::node(category("Animation"))]
5259
// fn month(ctx: impl Ctx + ExtractRealTime) -> f64 {

0 commit comments

Comments
 (0)