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

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].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

Lines changed: 127 additions & 0 deletions
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

Lines changed: 12 additions & 0 deletions
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

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy