Skip to content

Commit e11b57a

Browse files
0HyperCubeKeavon
andauthored
Add tests for gradient drawing with transformations (#2481)
* Test gradient drawing with transformations * Fix bad import * Fix merge conflicts --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent f003d5d commit e11b57a

File tree

5 files changed

+153
-8
lines changed

5 files changed

+153
-8
lines changed

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

+8-5
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

+2-3
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].as_solid().unwrap().to_rgba8_srgb(), color.to_rgba8_srgb());
182+
assert_eq!(fills[0].as_solid().unwrap().to_rgba8_srgb(), Color::YELLOW.to_rgba8_srgb());
184183
}
185184
}

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

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

editor/src/node_graph_executor.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use crate::consts::FILE_SAVE_SUFFIX;
22
use crate::messages::animation::TimingInformation;
33
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
4+
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
5+
use crate::messages::portfolio::document::utility_types::network_interface::NodeNetworkInterface;
46
use crate::messages::prelude::*;
7+
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
58
use glam::{DAffine2, DVec2, UVec2};
69
use graph_craft::concrete;
710
use graph_craft::document::value::{RenderOutput, TaggedValue};
@@ -920,4 +923,13 @@ impl Instrumented {
920923

921924
Self::downcast::<Input>(dynamic)
922925
}
926+
927+
pub fn grab_input_from_layer<Input: graphene_std::NodeInputDecleration>(&self, layer: LayerNodeIdentifier, network_interface: &NodeNetworkInterface, runtime: &NodeRuntime) -> Option<Input::Result>
928+
where
929+
Input::Result: Send + Sync + Clone + 'static,
930+
{
931+
let node_graph_layer = NodeGraphLayer::new(layer, network_interface);
932+
let node = node_graph_layer.upstream_node_id_from_protonode(Input::identifier())?;
933+
self.grab_protonode_input::<Input>(&vec![node], runtime)
934+
}
923935
}

editor/src/test_utils.rs

+4
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)