Skip to content

Commit 9ca352a

Browse files
committed
Test gradient drawing with transformations
1 parent bd97c15 commit 9ca352a

File tree

5 files changed

+152
-8
lines changed

5 files changed

+152
-8
lines changed

editor/src/messages/tool/common_functionality/graph_modification_utils.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -396,11 +396,14 @@ impl<'a> NodeGraphLayer<'a> {
396396

397397
/// Node id of a protonode if it exists in the layer's primary flow
398398
pub fn upstream_node_id_from_protonode(&self, protonode_identifier: &'static str) -> Option<NodeId> {
399-
self.horizontal_layer_flow().find(move |node_id| {
400-
self.network_interface
401-
.implementation(node_id, &[])
402-
.is_some_and(move |implementation| *implementation == graph_craft::document::DocumentNodeImplementation::proto(protonode_identifier))
403-
})
399+
self.horizontal_layer_flow()
400+
// Take until a different layer is reached
401+
.take_while(|&node_id| node_id == self.layer_node || !self.network_interface.is_layer(&node_id, &[]))
402+
.find(move |node_id| {
403+
self.network_interface
404+
.implementation(node_id, &[])
405+
.is_some_and(move |implementation| *implementation == graph_craft::document::DocumentNodeImplementation::proto(protonode_identifier))
406+
})
404407
}
405408

406409
/// Find all of the inputs of a specific node within the layer's primary flow, up until the next layer is reached.

editor/src/messages/tool/tool_messages/fill_tool.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,10 @@ mod test_fill {
175175
let mut editor = EditorTestUtils::create();
176176
editor.new_document().await;
177177
editor.drag_tool(ToolType::Rectangle, 0., 0., 100., 100., ModifierKeys::empty()).await;
178-
let color = Color::YELLOW;
179-
editor.handle_message(ToolMessage::SelectSecondaryColor { color }).await;
178+
editor.select_secondary_color(Color::YELLOW).await;
180179
editor.click_tool(ToolType::Fill, MouseKeys::LEFT, DVec2::new(2., 2.), ModifierKeys::SHIFT).await;
181180
let fills = get_fills(&mut editor).await;
182181
assert_eq!(fills.len(), 1);
183-
assert_eq!(fills[0], Fill::Solid(color));
182+
assert_eq!(fills[0], Fill::Solid(Color::YELLOW));
184183
}
185184
}

editor/src/messages/tool/tool_messages/gradient_tool.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,3 +510,129 @@ impl Fsm for GradientToolFsmState {
510510
responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default });
511511
}
512512
}
513+
514+
#[cfg(test)]
515+
mod test_gradient {
516+
use crate::messages::portfolio::document::{graph_operation::utility_types::TransformIn, utility_types::misc::GroupFolderType};
517+
pub use crate::test_utils::test_prelude::*;
518+
use glam::DAffine2;
519+
use graphene_core::vector::fill;
520+
use graphene_std::vector::style::{Fill, Gradient, GradientStops, GradientType};
521+
522+
use super::gradient_space_transform;
523+
524+
async fn get_fills(editor: &mut EditorTestUtils) -> Vec<(Fill, DAffine2)> {
525+
let instrumented = editor.eval_graph().await;
526+
527+
let document = editor.active_document();
528+
let layers = document.metadata().all_layers();
529+
layers
530+
.filter_map(|layer| {
531+
let fill = instrumented.grab_input_from_layer::<fill::FillInput<Fill>>(layer, &document.network_interface, &editor.runtime)?;
532+
let transform = gradient_space_transform(layer, document);
533+
Some((fill, transform))
534+
})
535+
.collect()
536+
}
537+
538+
#[tokio::test]
539+
async fn ignore_artboard() {
540+
let mut editor = EditorTestUtils::create();
541+
editor.new_document().await;
542+
editor.drag_tool(ToolType::Artboard, 0., 0., 100., 100., ModifierKeys::empty()).await;
543+
editor.drag_tool(ToolType::Gradient, 2., 2., 4., 4., ModifierKeys::empty()).await;
544+
assert!(get_fills(&mut editor).await.is_empty());
545+
}
546+
547+
#[tokio::test]
548+
// TODO: remove once https://github.com/GraphiteEditor/Graphite/issues/2444 is fixed
549+
#[should_panic]
550+
async fn ignore_raster() {
551+
let mut editor = EditorTestUtils::create();
552+
editor.new_document().await;
553+
editor.create_raster_image(Image::new(100, 100, Color::WHITE), Some((0., 0.))).await;
554+
editor.drag_tool(ToolType::Gradient, 2., 2., 4., 4., ModifierKeys::empty()).await;
555+
assert!(get_fills(&mut editor).await.is_empty());
556+
}
557+
558+
#[tokio::test]
559+
async fn simple_draw() {
560+
let mut editor = EditorTestUtils::create();
561+
editor.new_document().await;
562+
editor.drag_tool(ToolType::Rectangle, -5., -3., 100., 100., ModifierKeys::empty()).await;
563+
editor.select_primary_color(Color::GREEN).await;
564+
editor.select_secondary_color(Color::BLUE).await;
565+
editor.drag_tool(ToolType::Gradient, 2., 3., 24., 4., ModifierKeys::empty()).await;
566+
let fills = get_fills(&mut editor).await;
567+
assert_eq!(fills.len(), 1);
568+
let (fill, transform) = fills.first().unwrap();
569+
let gradient = fill.as_gradient().unwrap();
570+
// Gradient goes from secondary colour to primary colour
571+
assert_eq!(gradient.stops, GradientStops(vec![(0., Color::BLUE), (1., Color::GREEN)]));
572+
assert!(transform.transform_point2(gradient.start).abs_diff_eq(DVec2::new(2., 3.), 1e-10));
573+
assert!(transform.transform_point2(gradient.end).abs_diff_eq(DVec2::new(24., 4.), 1e-10));
574+
}
575+
576+
#[tokio::test]
577+
async fn snap_simple_draw() {
578+
let mut editor = EditorTestUtils::create();
579+
editor.new_document().await;
580+
editor
581+
.handle_message(NavigationMessage::CanvasTiltSet {
582+
angle_radians: f64::consts::FRAC_PI_8,
583+
})
584+
.await;
585+
let start = DVec2::new(0., 0.);
586+
let end = DVec2::new(24., 4.);
587+
editor.drag_tool(ToolType::Rectangle, -5., -3., 100., 100., ModifierKeys::empty()).await;
588+
editor.drag_tool(ToolType::Gradient, start.x, start.y, end.x, end.y, ModifierKeys::SHIFT).await;
589+
let fills = get_fills(&mut editor).await;
590+
let (fill, transform) = fills.first().unwrap();
591+
let gradient = fill.as_gradient().unwrap();
592+
assert!(transform.transform_point2(gradient.start).abs_diff_eq(start, 1e-10));
593+
594+
// 15 degrees from horizontal
595+
let angle = f64::to_radians(15.);
596+
let direction = DVec2::new(angle.cos(), angle.sin());
597+
let expected = start + direction * (end - start).length();
598+
assert!(transform.transform_point2(gradient.end).abs_diff_eq(expected, 1e-10));
599+
}
600+
601+
#[tokio::test]
602+
async fn transformed_draw() {
603+
let mut editor = EditorTestUtils::create();
604+
editor.new_document().await;
605+
editor
606+
.handle_message(NavigationMessage::CanvasTiltSet {
607+
angle_radians: f64::consts::FRAC_PI_8,
608+
})
609+
.await;
610+
editor.drag_tool(ToolType::Rectangle, -5., -3., 100., 100., ModifierKeys::empty()).await;
611+
612+
// Group rectangle
613+
let group_folder_type = GroupFolderType::Layer;
614+
editor.handle_message(DocumentMessage::GroupSelectedLayers { group_folder_type }).await;
615+
let metadata = editor.active_document().metadata();
616+
let mut layers = metadata.all_layers();
617+
let folder = layers.next().unwrap();
618+
let rectangle = layers.next().unwrap();
619+
assert_eq!(rectangle.parent(metadata), Some(folder));
620+
// Transform the group
621+
editor
622+
.handle_message(GraphOperationMessage::TransformSet {
623+
layer: folder,
624+
transform: DAffine2::from_scale_angle_translation(DVec2::new(1., 2.), 0., -DVec2::X * 10.),
625+
transform_in: TransformIn::Local,
626+
skip_rerender: false,
627+
})
628+
.await;
629+
630+
editor.drag_tool(ToolType::Gradient, 2., 3., 24., 4., ModifierKeys::empty()).await;
631+
let fills = get_fills(&mut editor).await;
632+
assert_eq!(fills.len(), 1);
633+
let (fill, transform) = fills.first().unwrap();
634+
let gradient = fill.as_gradient().unwrap();
635+
assert!(transform.transform_point2(gradient.start).abs_diff_eq(DVec2::new(2., 3.), 1e-10));
636+
assert!(transform.transform_point2(gradient.end).abs_diff_eq(DVec2::new(24., 4.), 1e-10));
637+
}
638+
}

editor/src/node_graph_executor.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::consts::FILE_SAVE_SUFFIX;
22
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
3+
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
34
use crate::messages::prelude::*;
5+
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
6+
use crate::test_utils::test_prelude::LayerNodeIdentifier;
47
use glam::{DAffine2, DVec2, UVec2};
58
use graph_craft::concrete;
69
use graph_craft::document::value::{RenderOutput, TaggedValue};
@@ -806,4 +809,13 @@ impl Instrumented {
806809

807810
Self::downcast::<Input>(dynamic)
808811
}
812+
813+
pub fn grab_input_from_layer<Input: graphene_std::NodeInputDecleration>(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, runtime: &NodeRuntime) -> Option<Input::Result>
814+
where
815+
Input::Result: Send + Sync + Clone + 'static,
816+
{
817+
let node_graph_layer = NodeGraphLayer::new(layer, network_interface);
818+
let node = node_graph_layer.upstream_node_id_from_protonode(Input::identifier())?;
819+
self.grab_protonode_input::<Input>(&vec![node], runtime)
820+
}
809821
}

editor/src/test_utils.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ impl EditorTestUtils {
217217
self.handle_message(Message::Tool(ToolMessage::SelectPrimaryColor { color })).await;
218218
}
219219

220+
pub async fn select_secondary_color(&mut self, color: Color) {
221+
self.handle_message(Message::Tool(ToolMessage::SelectSecondaryColor { color })).await;
222+
}
223+
220224
pub async fn create_raster_image(&mut self, image: graphene_core::raster::Image<Color>, mouse: Option<(f64, f64)>) {
221225
self.handle_message(PortfolioMessage::PasteImage {
222226
name: None,

0 commit comments

Comments
 (0)