From 1020eb68357709c14bd57ffa1888593891749e79 Mon Sep 17 00:00:00 2001 From: Dennis Kobert Date: Sat, 29 Apr 2023 01:31:14 +0200 Subject: [PATCH] Implement the Brush without relying on a stamp texture Test Plan: Test the BrushNode in the editor Reviewers: Keavon Reviewed By: Keavon Pull Request: https://github.com/GraphiteEditor/Graphite/pull/1184 --- Cargo.lock | 74 ++++++------- Cargo.toml | 4 +- .../graph_operation_message_handler.rs | 2 +- .../document_node_types.rs | 12 +-- .../tool/common_functionality/shape_editor.rs | 4 +- .../messages/tool/tool_messages/brush_tool.rs | 2 +- .../tool/tool_messages/freehand_tool.rs | 2 +- .../messages/tool/tool_messages/line_tool.rs | 2 +- .../messages/tool/tool_messages/path_tool.rs | 11 +- .../messages/tool/tool_messages/pen_tool.rs | 2 +- .../tool/tool_messages/select_tool.rs | 22 ++-- .../messages/tool/tool_messages/text_tool.rs | 14 +-- .../transform_layer_message_handler.rs | 4 +- frontend/src-tauri/src/main.rs | 2 +- libraries/bezier-rs/src/subpath/structs.rs | 8 +- node-graph/gcore/src/gpu.rs | 2 +- node-graph/gcore/src/ops.rs | 2 +- node-graph/gcore/src/raster.rs | 2 +- node-graph/gcore/src/raster/color.rs | 3 + node-graph/gcore/src/raster/image.rs | 7 +- node-graph/gcore/src/structural.rs | 2 +- node-graph/gcore/src/value.rs | 26 +++++ node-graph/gcore/src/vector/vector_nodes.rs | 2 +- node-graph/gpu-executor/src/lib.rs | 8 +- node-graph/graph-craft/src/document.rs | 4 +- node-graph/gstd/src/brush.rs | 100 ++++++++++-------- node-graph/gstd/src/memo.rs | 8 +- node-graph/gstd/src/raster.rs | 31 ++++-- .../interpreted-executor/src/executor.rs | 9 +- .../interpreted-executor/src/node_registry.rs | 20 ++-- .../other/bezier-rs-demos/wasm/src/subpath.rs | 8 +- 31 files changed, 221 insertions(+), 178 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b69c5e3..7f9080a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,9 +232,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.6.16" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113713495a32dd0ab52baf5c10044725aa3aec00b31beda84218e469029b72a3" +checksum = "b70caf9f1b0c045f7da350636435b775a9733adf2df56e8aa2a29210fbc335d4" dependencies = [ "async-trait", "axum-core", @@ -1122,12 +1122,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", - "miniz_oxide 0.6.2", + "miniz_oxide", ] [[package]] @@ -2266,8 +2266,8 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.9.3" -source = "git+https://github.com/linebender/kurbo.git#615ed7ede2d80ae0f3509165c5a2dee6cef69251" +version = "0.9.4" +source = "git+https://github.com/linebender/kurbo.git#01b52cd85c3a9b1be2c6e39ab97fcf401a8911c4" dependencies = [ "arrayvec", "serde", @@ -2342,9 +2342,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "b64f40e5e03e0d54f03845c8197d0291253cdbedfb1cb46b13c2c117554a9f4c" [[package]] name = "litrs" @@ -2493,15 +2493,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.7.1" @@ -2864,9 +2855,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.51" +version = "0.10.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ea2d98598bf9ada7ea6ee8a30fb74f9156b63bbe495d64ec2b87c269d2dda3" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" dependencies = [ "bitflags", "cfg-if", @@ -2896,9 +2887,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.86" +version = "0.9.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "992bac49bdbab4423199c654a5515bd2a6c6a23bf03f2dd3bdb7e5ae6259bc69" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" dependencies = [ "cc", "libc", @@ -3169,7 +3160,7 @@ dependencies = [ "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.1", + "miniz_oxide", ] [[package]] @@ -3457,9 +3448,9 @@ checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" [[package]] name = "reqwest" -version = "0.11.16" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254" +checksum = "13293b639a097af28fc8a90f22add145a9c954e49d77da06263d58cf44d5fb91" dependencies = [ "base64 0.21.0", "bytes", @@ -3564,9 +3555,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.14" +version = "0.37.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b864d3c18a5785a05953adeed93e2dca37ed30f18e69bba9f30079d51f363f" +checksum = "a0661814f891c57c930a610266415528da53c4933e6dea5fb350cbfe048a9ece" dependencies = [ "bitflags", "errno", @@ -4319,9 +4310,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.6" +version = "0.12.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" +checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" [[package]] name = "tauri" @@ -4670,9 +4661,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.27.0" +version = "1.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", "bytes", @@ -4684,14 +4675,14 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", @@ -4721,9 +4712,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.7" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" dependencies = [ "bytes", "futures-core", @@ -4806,11 +4797,10 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.37" +version = "0.1.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "cf9cf6a813d3f40c88b0b6b6f29a5c95c6cdbf97c1f9cc53fb820200f5ad814d" dependencies = [ - "cfg-if", "log", "pin-project-lite", "tracing-attributes", @@ -4819,13 +4809,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.15", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9f926e97..574b8d34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,6 @@ specta = { git = "https://github.com/oscartbeaumont/rspc", rev = "9725ddbfe40183 ] } xxhash-rust = { version = "0.8.4", features = ["xxh3"] } - [profile.dev.package.graphite-editor] opt-level = 1 @@ -54,6 +53,9 @@ opt-level = 3 [profile.dev.package.image] opt-level = 3 +[profile.dev.package.png] +opt-level = 3 + [profile.dev.package.xxhash-rust] opt-level = 3 diff --git a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs index ccf7dc62..ec202d56 100644 --- a/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs +++ b/editor/src/messages/portfolio/document/node_graph/graph_operation_message_handler.rs @@ -24,7 +24,7 @@ struct ModifyInputsContext<'a> { impl<'a> ModifyInputsContext<'a> { /// Get the node network from the document fn new(layer: &'a [LayerId], document: &'a mut Document, node_graph: &'a mut NodeGraphMessageHandler, responses: &'a mut VecDeque) -> Option { - document.layer_mut(&layer).ok().and_then(|layer| layer.as_node_graph_mut().ok()).map(|network| Self { + document.layer_mut(layer).ok().and_then(|layer| layer.as_node_graph_mut().ok()).map(|network| Self { network, node_graph, responses, diff --git a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs index e1b48254..e07ec2c8 100644 --- a/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs +++ b/editor/src/messages/portfolio/document/node_graph/node_graph_message_handler/document_node_types.rs @@ -1048,19 +1048,19 @@ pub fn wrap_network_in_scope(network: NodeNetwork) -> NodeNetwork { let nodes = vec![ resolve_document_node_type("Begin Scope") .expect("Begin Scope node type not found") - .to_document_node(vec![input_type.clone()], DocumentNodeMetadata::default()), + .to_document_node(vec![input_type], DocumentNodeMetadata::default()), inner_network, resolve_document_node_type("End Scope") .expect("End Scope node type not found") .to_document_node(vec![NodeInput::node(0, 0), NodeInput::node(1, 0)], DocumentNodeMetadata::default()), ]; - let network = NodeNetwork { + + NodeNetwork { inputs: vec![0], outputs: vec![NodeOutput::new(2, 0)], nodes: nodes.into_iter().enumerate().map(|(id, node)| (id as NodeId, node)).collect(), ..Default::default() - }; - network + } } pub fn new_image_network(output_offset: i32, output_node_id: NodeId) -> NodeNetwork { @@ -1121,8 +1121,8 @@ pub fn new_text_network(text: String, font: Font, size: f64) -> NodeNetwork { text_generator.to_document_node( [ NodeInput::Network(concrete!(graphene_core::EditorApi)), - NodeInput::value(TaggedValue::String(text.clone()), false), - NodeInput::value(TaggedValue::Font(font.clone()), false), + NodeInput::value(TaggedValue::String(text), false), + NodeInput::value(TaggedValue::Font(font), false), NodeInput::value(TaggedValue::F64(size), false), ], DocumentNodeMetadata::position((0, 4)), diff --git a/editor/src/messages/tool/common_functionality/shape_editor.rs b/editor/src/messages/tool/common_functionality/shape_editor.rs index 1034fae1..5d67beaa 100644 --- a/editor/src/messages/tool/common_functionality/shape_editor.rs +++ b/editor/src/messages/tool/common_functionality/shape_editor.rs @@ -149,10 +149,10 @@ impl ShapeState { /// Move the selected points by dragging the mouse. pub fn move_selected_points(&self, document: &Document, delta: DVec2, mirror_distance: bool, responses: &mut VecDeque) { for (layer_path, state) in &self.selected_shape_state { - let Ok(layer) = document.layer(&layer_path) else { continue }; + let Ok(layer) = document.layer(layer_path) else { continue }; let Some(vector_data) = layer.as_vector_data() else { continue }; - let transform = document.multiply_transforms(&layer_path).unwrap_or_default(); + let transform = document.multiply_transforms(layer_path).unwrap_or_default(); let delta = transform.inverse().transform_vector2(delta); for &point in state.selected_points.iter() { diff --git a/editor/src/messages/tool/tool_messages/brush_tool.rs b/editor/src/messages/tool/tool_messages/brush_tool.rs index d6029a0e..69135f31 100644 --- a/editor/src/messages/tool/tool_messages/brush_tool.rs +++ b/editor/src/messages/tool/tool_messages/brush_tool.rs @@ -315,7 +315,7 @@ fn add_brush_render(data: &BrushToolData, tool_data: &DocumentToolData, response }; let mut network = NodeNetwork::value_network(brush_node); network.push_output_node(); - graph_modification_utils::new_custom_layer(network, layer_path.clone(), responses); + graph_modification_utils::new_custom_layer(network, layer_path, responses); } fn load_existing_points(document: &DocumentMessageHandler) -> Option<(Vec, Vec)> { diff --git a/editor/src/messages/tool/tool_messages/freehand_tool.rs b/editor/src/messages/tool/tool_messages/freehand_tool.rs index 3b2867a1..64f9e2a7 100644 --- a/editor/src/messages/tool/tool_messages/freehand_tool.rs +++ b/editor/src/messages/tool/tool_messages/freehand_tool.rs @@ -223,7 +223,7 @@ fn add_polyline(data: &FreehandToolData, tool_data: &DocumentToolData, responses graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses); responses.add(GraphOperationMessage::StrokeSet { - layer: layer_path.clone(), + layer: layer_path, stroke: Stroke::new(tool_data.primary_color, data.weight), }); } diff --git a/editor/src/messages/tool/tool_messages/line_tool.rs b/editor/src/messages/tool/tool_messages/line_tool.rs index 9be4f5d0..1ea9ec35 100644 --- a/editor/src/messages/tool/tool_messages/line_tool.rs +++ b/editor/src/messages/tool/tool_messages/line_tool.rs @@ -164,7 +164,7 @@ impl Fsm for LineToolFsmState { tool_data.path = Some(layer_path.clone()); graph_modification_utils::new_vector_layer(vec![subpath], layer_path.clone(), responses); responses.add(GraphOperationMessage::StrokeSet { - layer: layer_path.clone(), + layer: layer_path, stroke: Stroke::new(global_tool_data.primary_color, tool_options.line_weight), }); diff --git a/editor/src/messages/tool/tool_messages/path_tool.rs b/editor/src/messages/tool/tool_messages/path_tool.rs index 6b759d35..7c6a9d64 100644 --- a/editor/src/messages/tool/tool_messages/path_tool.rs +++ b/editor/src/messages/tool/tool_messages/path_tool.rs @@ -273,11 +273,9 @@ impl Fsm for PathToolFsmState { if tool_data.opposing_handle_lengths.is_none() { tool_data.opposing_handle_lengths = Some(shape_editor.opposing_handle_lengths(&document.document_legacy)); } - } else { - if let Some(opposing_handle_lengths) = &tool_data.opposing_handle_lengths { - shape_editor.reset_opposing_handle_lengths(&document.document_legacy, opposing_handle_lengths, responses); - tool_data.opposing_handle_lengths = None; - } + } else if let Some(opposing_handle_lengths) = &tool_data.opposing_handle_lengths { + shape_editor.reset_opposing_handle_lengths(&document.document_legacy, opposing_handle_lengths, responses); + tool_data.opposing_handle_lengths = None; } // Move the selected points by the mouse position @@ -291,8 +289,7 @@ impl Fsm for PathToolFsmState { (_, PathToolMessage::DragStop { shift_mirror_distance }) => { let nearest_point = shape_editor .find_nearest_point_indices(&document.document_legacy, input.mouse.position, SELECTION_THRESHOLD) - .map(|(_, nearest_point)| nearest_point) - .clone(); + .map(|(_, nearest_point)| nearest_point); let shift_pressed = input.keyboard.get(shift_mirror_distance as usize); if tool_data.drag_start_pos.distance(input.mouse.position) <= DRAG_THRESHOLD && !shift_pressed { diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index ae5209d5..c8db9f61 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -475,7 +475,7 @@ impl PenToolData { modification: VectorDataModification::SetManipulatorPosition { point, position }, }); - return Some(DocumentMessage::CommitTransaction); + Some(DocumentMessage::CommitTransaction) } } diff --git a/editor/src/messages/tool/tool_messages/select_tool.rs b/editor/src/messages/tool/tool_messages/select_tool.rs index 3963e22f..d981f5f5 100644 --- a/editor/src/messages/tool/tool_messages/select_tool.rs +++ b/editor/src/messages/tool/tool_messages/select_tool.rs @@ -762,7 +762,7 @@ impl Fsm for SelectToolFsmState { if let Some(path) = intersection.last() { // let folders: Vec<_> = (1..path.len() + 1).map(|i| &path[0..i]).collect(); // let replacement_selected_layers: Vec<_> = document.selected_layers().filter(|&layer| !folders.contains(&layer)).map(|path| path.to_vec()).collect(); - let replacement_selected_layers: Vec<_> = document.selected_layers().filter(|&layer| !path.starts_with(&layer)).map(|path| path.to_vec()).collect(); + let replacement_selected_layers: Vec<_> = document.selected_layers().filter(|&layer| !path.starts_with(layer)).map(|path| path.to_vec()).collect(); tool_data.layers_dragging.clear(); tool_data.layers_dragging.append(replacement_selected_layers.clone().as_mut()); @@ -999,7 +999,7 @@ fn drag_shallowest_manipulation( // Checks if the incoming layer's root parent is already selected // If so we need to update the selected layer to the deeper of the two let mut layers_without_incoming_parent: Vec> = document.selected_layers().filter(|&layer| layer != [incoming_parent].as_slice()).map(|path| path.to_vec()).collect(); - if layers.contains(&&[incoming_parent].as_slice()) { + if layers.contains(&[incoming_parent].as_slice()) { // Add incoming layer tool_data.layers_dragging.clear(); responses.add(DocumentMessage::DeselectAllLayers); @@ -1028,7 +1028,7 @@ fn drag_shallowest_manipulation( // Check if the intersected layer path is already selected let previous_parents: Vec<_> = (0..layers.len()).map(|i| &layers.get(i).unwrap()[..1]).collect(); - let already_selected_parent = previous_parents.contains(&&[incoming_parent].as_slice()); + let already_selected_parent = previous_parents.contains(&[incoming_parent].as_slice()); let selected_layers: Vec<_> = document.selected_layers().collect(); let mut search = previous_layer_path.to_vec(); @@ -1042,7 +1042,7 @@ fn drag_shallowest_manipulation( selected_layer_path_parent = selected_layer_path_parent[..selected_layer_path_parent.len() - 1].to_vec(); } - while selected_layer_path_parent.len() > 0 && !is_parent && !recursive_found { + while !selected_layer_path_parent.is_empty() && !is_parent && !recursive_found { let selected_children_layer_paths = document.document_legacy.folder_children_paths(&selected_layer_path_parent); for child in selected_children_layer_paths { if child == *incoming_layer_path_vector { @@ -1062,7 +1062,7 @@ fn drag_shallowest_manipulation( // Check if new layer is already selected let mut already_selected = false; - if selected_layers.contains(&search.clone().as_slice()) { + if selected_layers.contains(&search.as_slice()) { already_selected = true; } @@ -1086,7 +1086,7 @@ fn drag_shallowest_manipulation( } else { // Previous selected layers with the intersect layer path appended to it let mut combined_layers = selected_layers.clone(); - let intersection_temp = intersection.clone(); + let intersection_temp = intersection; let intersection_temp_slice = intersection_temp.as_slice(); combined_layers.push(intersection_temp_slice); let layers_iter = combined_layers.into_iter(); @@ -1162,7 +1162,7 @@ fn edit_layer_shallowest_manipulation(document: &DocumentMessageHandler, interse let incoming_parent = *intersect_layer_path.first().unwrap(); let previous_parents: Vec<_> = (0..selected_layers.len()).map(|i| &selected_layers.get(i).unwrap()[..1]).collect(); let mut incoming_parent_selected = false; - if previous_parents.contains(&&[incoming_parent].as_slice()) { + if previous_parents.contains(&[incoming_parent].as_slice()) { incoming_parent_selected = true; } if incoming_parent_selected { @@ -1225,11 +1225,9 @@ fn recursive_search(document: &DocumentMessageHandler, layer_path: &Vec, in for path in layer_paths { if path == *incoming_layer_path_vector { return true; - } else if document.document_legacy.is_folder(path.clone()) { - if recursive_search(document, &path, incoming_layer_path_vector) { - return true; - } + } else if document.document_legacy.is_folder(path.clone()) && recursive_search(document, &path, incoming_layer_path_vector) { + return true; } } - return false; + false } diff --git a/editor/src/messages/tool/tool_messages/text_tool.rs b/editor/src/messages/tool/tool_messages/text_tool.rs index df2dbb43..c92559bd 100644 --- a/editor/src/messages/tool/tool_messages/text_tool.rs +++ b/editor/src/messages/tool/tool_messages/text_tool.rs @@ -280,7 +280,7 @@ impl TextToolData { else if let Some(editing_text) = self.editing_text.as_ref().filter(|_| state == TextToolFsmState::Ready) { responses.add(DocumentMessage::StartTransaction); - let network = new_text_network(String::new(), editing_text.font.clone(), editing_text.font_size as f64); + let network = new_text_network(String::new(), editing_text.font.clone(), editing_text.font_size); responses.add(Operation::AddFrame { path: self.layer_path.clone(), @@ -320,7 +320,7 @@ impl TextToolData { resize_overlays(&mut self.overlays, responses, 1); let editing_text = self.editing_text.as_ref()?; - let buzz_face = render_data.font_cache.get(&editing_text.font).map(|data| load_face(&data)); + let buzz_face = render_data.font_cache.get(&editing_text.font).map(|data| load_face(data)); let far = graphene_core::text::bounding_box(&self.new_text, buzz_face, editing_text.font_size, None); let quad = Quad::from_box([DVec2::ZERO, far]); @@ -337,8 +337,8 @@ impl TextToolData { fn get_bounds(&self, text: &str, render_data: &RenderData) -> Option<[DVec2; 2]> { let editing_text = self.editing_text.as_ref()?; - let buzz_face = render_data.font_cache.get(&editing_text.font).map(|data| load_face(&data)); - let subpaths = graphene_core::text::to_path(&text, buzz_face, editing_text.font_size, None); + let buzz_face = render_data.font_cache.get(&editing_text.font).map(|data| load_face(data)); + let subpaths = graphene_core::text::to_path(text, buzz_face, editing_text.font_size, None); let bounds = subpaths.iter().filter_map(|subpath| subpath.bounding_box()); let combined_bounds = bounds.reduce(|a, b| [a[0].min(b[0]), a[1].max(b[1])]).unwrap_or_default(); Some(combined_bounds) @@ -383,8 +383,8 @@ fn update_overlays(document: &DocumentMessageHandler, tool_data: &mut TextToolDa let node_id = get_text_node_id(node_graph)?; let document_node = node_graph.nodes.get(&node_id)?; let (text, font, font_size) = TextToolData::extract_text_node_inputs(document_node)?; - let buzz_face = render_data.font_cache.get(font).map(|data| load_face(&data)); - let far = graphene_core::text::bounding_box(&text, buzz_face, font_size, None); + let buzz_face = render_data.font_cache.get(font).map(|data| load_face(data)); + let far = graphene_core::text::bounding_box(text, buzz_face, font_size, None); let quad = Quad::from_box([DVec2::ZERO, far]); let multiplied = document.document_legacy.multiply_transforms(path).ok()? * quad; Some(multiplied.bounding_box()) @@ -474,7 +474,7 @@ impl Fsm for TextToolFsmState { tool_data.new_text = String::new(); tool_data.layer_path = document.get_path_for_new_layer(); - tool_data.interact(state, input.mouse.position, document, &render_data, responses) + tool_data.interact(state, input.mouse.position, document, render_data, responses) } (state, TextToolMessage::EditSelected) => { if let Some(layer_path) = can_edit_selected(document) { diff --git a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs index 19dd9fdd..e2c5ec51 100644 --- a/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs +++ b/editor/src/messages/tool/transform_layer/transform_layer_message_handler.rs @@ -65,10 +65,10 @@ impl<'a> MessageHandler> for TransformL } if using_path_tool { - if let Ok(layer) = document.document_legacy.layer(&selected_layers[0]) { + if let Ok(layer) = document.document_legacy.layer(selected_layers[0]) { if let Some(vector_data) = layer.as_vector_data() { *selected.original_transforms = OriginalTransforms::default(); - let viewspace = &mut document.document_legacy.generate_transform_relative_to_viewport(&selected_layers[0]).ok().unwrap_or_default(); + let viewspace = &mut document.document_legacy.generate_transform_relative_to_viewport(selected_layers[0]).ok().unwrap_or_default(); let mut point_count: usize = 0; let count_point = |position| { diff --git a/frontend/src-tauri/src/main.rs b/frontend/src-tauri/src/main.rs index 810fa462..9ccbc054 100644 --- a/frontend/src-tauri/src/main.rs +++ b/frontend/src-tauri/src/main.rs @@ -98,7 +98,7 @@ fn handle_message(message: String) -> String { for image in image_data { let path = image.path.clone(); let mime = image.mime.clone(); - let transform = image.transform.clone(); + let transform = image.transform; images.insert(format!("{:?}_{}", &image.path, document_id), image); stub_data.push(FrontendImageData { path, diff --git a/libraries/bezier-rs/src/subpath/structs.rs b/libraries/bezier-rs/src/subpath/structs.rs index 656aa417..89728010 100644 --- a/libraries/bezier-rs/src/subpath/structs.rs +++ b/libraries/bezier-rs/src/subpath/structs.rs @@ -38,9 +38,13 @@ impl Hash for ManipulatorGroup(&self, state: &mut H) { self.anchor.to_array().iter().for_each(|x| x.to_bits().hash(state)); self.in_handle.is_some().hash(state); - self.in_handle.map(|in_handle| in_handle.to_array().iter().for_each(|x| x.to_bits().hash(state))); + if let Some(in_handle) = self.in_handle { + in_handle.to_array().iter().for_each(|x| x.to_bits().hash(state)); + } self.out_handle.is_some().hash(state); - self.out_handle.map(|out_handle| out_handle.to_array().iter().for_each(|x| x.to_bits().hash(state))); + if let Some(out_handle) = self.out_handle { + out_handle.to_array().iter().for_each(|x| x.to_bits().hash(state)); + } self.id.hash(state); } } diff --git a/node-graph/gcore/src/gpu.rs b/node-graph/gcore/src/gpu.rs index 8ee105d4..c6405818 100644 --- a/node-graph/gcore/src/gpu.rs +++ b/node-graph/gcore/src/gpu.rs @@ -13,7 +13,7 @@ pub struct PushConstants { impl Sample for SampledImage { type Pixel = Color; - fn sample(&self, pos: glam::DVec2) -> Option { + fn sample(&self, pos: glam::DVec2, _area: glam::DVec2) -> Option { let color = self.sample(pos); Color::from_rgbaf32(color.x, color.y, color.z, color.w) } diff --git a/node-graph/gcore/src/ops.rs b/node-graph/gcore/src/ops.rs index ad4342c1..59642761 100644 --- a/node-graph/gcore/src/ops.rs +++ b/node-graph/gcore/src/ops.rs @@ -252,7 +252,7 @@ mod test { let value: ClonedNode> = ClonedNode(Ok(&4u32)); assert_eq!(value.eval(()), Ok(&4u32)); //let type_erased_clone = clone as &dyn for<'a> Node<'a, &'a u32, Output = u32>; - let map_result = MapResultNode::new(ValueNode::new(FnNode::new(|x: &u32| x.clone()))); + let map_result = MapResultNode::new(ValueNode::new(FnNode::new(|x: &u32| *x))); //et type_erased = &map_result as &dyn for<'a> Node<'a, Result<&'a u32, ()>, Output = Result>; assert_eq!(map_result.eval(Ok(&4u32)), Ok(4u32)); let fst = value.then(map_result); diff --git a/node-graph/gcore/src/raster.rs b/node-graph/gcore/src/raster.rs index 48c9a81f..1e0fd5dd 100644 --- a/node-graph/gcore/src/raster.rs +++ b/node-graph/gcore/src/raster.rs @@ -171,7 +171,7 @@ pub trait Luminance { pub trait Sample { type Pixel: Pixel; // TODO: Add an area parameter - fn sample(&self, pos: DVec2) -> Option; + fn sample(&self, pos: DVec2, area: DVec2) -> Option; } // TODO: We might rename this to Bitmap at some point diff --git a/node-graph/gcore/src/raster/color.rs b/node-graph/gcore/src/raster/color.rs index f6b6d637..223bb2cc 100644 --- a/node-graph/gcore/src/raster/color.rs +++ b/node-graph/gcore/src/raster/color.rs @@ -715,6 +715,9 @@ impl Color { } pub fn to_unassociated_alpha(&self) -> Self { + if self.alpha == 0. { + return *self; + } let unmultiply = 1. / self.alpha; Self { red: self.red * unmultiply, diff --git a/node-graph/gcore/src/raster/image.rs b/node-graph/gcore/src/raster/image.rs index 052a67b9..d5d04416 100644 --- a/node-graph/gcore/src/raster/image.rs +++ b/node-graph/gcore/src/raster/image.rs @@ -26,7 +26,7 @@ mod base64_serde { { use serde::de::Error; - let color_from_chunk = |chunk: &[u8]| P::from_bytes(chunk.try_into().unwrap()).clone(); + let color_from_chunk = |chunk: &[u8]| P::from_bytes(chunk.try_into().unwrap()); let colors_from_bytes = |bytes: Vec| bytes.chunks_exact(P::byte_size()).map(color_from_chunk).collect(); @@ -129,7 +129,7 @@ where pub fn into_flat_u8(self) -> (Vec, u32, u32) { let Image { width, height, data } = self; - let to_gamma = |x| SRGBGammaFloat::from_linear(x); + let to_gamma = SRGBGammaFloat::from_linear; let to_u8 = |x| (num_cast::<_, f32>(x).unwrap() * 255.) as u8; let result_bytes = data @@ -201,7 +201,8 @@ pub struct ImageFrame { impl Sample for ImageFrame

{ type Pixel = P; - fn sample(&self, pos: DVec2) -> Option { + // TODO: Improve sampling logic + fn sample(&self, pos: DVec2, _area: DVec2) -> Option { let image_size = DVec2::new(self.image.width() as f64, self.image.height() as f64); let pos = (DAffine2::from_scale(image_size) * self.transform.inverse()).transform_point2(pos); if pos.x < 0. || pos.y < 0. || pos.x >= image_size.x || pos.y >= image_size.y { diff --git a/node-graph/gcore/src/structural.rs b/node-graph/gcore/src/structural.rs index 2787369a..314f63c7 100644 --- a/node-graph/gcore/src/structural.rs +++ b/node-graph/gcore/src/structural.rs @@ -81,7 +81,7 @@ mod test { fn test_ref_eval() { let value = ValueNode::new(5); - assert_eq!((&value).eval(()), &5); + assert_eq!(value.eval(()), &5); let id = IdNode::new(); let compose = ComposeNode::new(&value, &id); diff --git a/node-graph/gcore/src/value.rs b/node-graph/gcore/src/value.rs index 4162d9e3..6c6bf74f 100644 --- a/node-graph/gcore/src/value.rs +++ b/node-graph/gcore/src/value.rs @@ -7,6 +7,7 @@ pub struct IntNode; impl<'i, const N: u32> Node<'i, ()> for IntNode { type Output = u32; + #[inline(always)] fn eval(&'i self, _input: ()) -> Self::Output { N } @@ -17,6 +18,7 @@ pub struct ValueNode(pub T); impl<'i, T: 'i> Node<'i, ()> for ValueNode { type Output = &'i T; + #[inline(always)] fn eval(&'i self, _input: ()) -> Self::Output { &self.0 } @@ -45,6 +47,7 @@ pub struct ClonedNode(pub T); impl<'i, T: Clone + 'i> Node<'i, ()> for ClonedNode { type Output = T; + #[inline(always)] fn eval(&'i self, _input: ()) -> Self::Output { self.0.clone() } @@ -62,11 +65,34 @@ impl From for ClonedNode { } } +#[derive(Clone, Copy)] +/// The DebugClonedNode logs every time it is evaluated. +/// This is useful for debugging. +pub struct DebugClonedNode(pub T); + +impl<'i, T: Clone + 'i> Node<'i, ()> for DebugClonedNode { + type Output = T; + #[inline(always)] + fn eval(&'i self, _input: ()) -> Self::Output { + // KEEP THIS `debug!()` - It acts as the output for the debug node itself + log::debug!("DebugClonedNode::eval"); + + self.0.clone() + } +} + +impl DebugClonedNode { + pub const fn new(value: T) -> ClonedNode { + ClonedNode(value) + } +} + #[derive(Clone, Copy)] pub struct CopiedNode(pub T); impl<'i, T: Copy + 'i> Node<'i, ()> for CopiedNode { type Output = T; + #[inline(always)] fn eval(&'i self, _input: ()) -> Self::Output { self.0 } diff --git a/node-graph/gcore/src/vector/vector_nodes.rs b/node-graph/gcore/src/vector/vector_nodes.rs index 4fee1fb5..be4d6473 100644 --- a/node-graph/gcore/src/vector/vector_nodes.rs +++ b/node-graph/gcore/src/vector/vector_nodes.rs @@ -26,7 +26,7 @@ fn set_vector_data_fill( positions: Vec<(f64, Option)>, ) -> VectorData { vector_data.style.set_fill(match fill_type { - FillType::None | FillType::Solid => solid_color.map_or(Fill::None, |solid_color| Fill::Solid(solid_color)), + FillType::None | FillType::Solid => solid_color.map_or(Fill::None, Fill::Solid), FillType::Gradient => Fill::Gradient(Gradient { start, end, diff --git a/node-graph/gpu-executor/src/lib.rs b/node-graph/gpu-executor/src/lib.rs index 028de0d9..ca94d4b5 100644 --- a/node-graph/gpu-executor/src/lib.rs +++ b/node-graph/gpu-executor/src/lib.rs @@ -169,8 +169,7 @@ pub struct UniformNode { #[node_macro::node_fn(UniformNode)] fn uniform_node(data: T, executor: &'any_input E) -> ShaderInput { - let handle = executor.create_uniform_buffer(data).unwrap(); - handle + executor.create_uniform_buffer(data).unwrap() } pub struct StorageNode { @@ -179,7 +178,7 @@ pub struct StorageNode { #[node_macro::node_fn(StorageNode)] fn storage_node(data: T, executor: &'any_input E) -> ShaderInput { - let handle = executor + executor .create_storage_buffer( data, StorageBufferOptions { @@ -188,8 +187,7 @@ fn storage_node(data: T, executor: &'any_inp cpu_readable: false, }, ) - .unwrap(); - handle + .unwrap() } pub struct PushNode { diff --git a/node-graph/graph-craft/src/document.rs b/node-graph/graph-craft/src/document.rs index f5662724..4dcba194 100644 --- a/node-graph/graph-craft/src/document.rs +++ b/node-graph/graph-craft/src/document.rs @@ -292,7 +292,7 @@ impl NodeNetwork { } pub fn input_types<'a>(&'a self) -> impl Iterator + 'a { - self.inputs.iter().map(move |id| self.nodes[id].inputs.get(0).map(|i| i.ty().clone()).unwrap_or(concrete!(()))) + self.inputs.iter().map(move |id| self.nodes[id].inputs.get(0).map(|i| i.ty()).unwrap_or(concrete!(()))) } /// An empty graph @@ -500,7 +500,7 @@ impl NodeNetwork { } FlowIter { stack: self.outputs.iter().map(|output| output.node_id).collect(), - network: &self, + network: self, } } } diff --git a/node-graph/gstd/src/brush.rs b/node-graph/gstd/src/brush.rs index 794dcc6b..9984ad59 100644 --- a/node-graph/gstd/src/brush.rs +++ b/node-graph/gstd/src/brush.rs @@ -1,8 +1,8 @@ use std::marker::PhantomData; use glam::{DAffine2, DVec2}; -use graphene_core::raster::{Color, Image, ImageFrame, RasterMut}; -use graphene_core::transform::TransformMut; +use graphene_core::raster::{Alpha, Color, Pixel, Sample}; +use graphene_core::transform::{Transform, TransformMut}; use graphene_core::vector::VectorData; use graphene_core::Node; use node_macro::node_fn; @@ -73,8 +73,49 @@ fn vector_points(vector: VectorData) -> Vec { vector.subpaths.iter().flat_map(|subpath| subpath.manipulator_groups().iter().map(|group| group.anchor)).collect() } +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct BrushStampGenerator { + color: P, + feather_exponent: f32, + transform: DAffine2, +} + +impl Transform for BrushStampGenerator

{ + fn transform(&self) -> DAffine2 { + self.transform + } +} + +impl TransformMut for BrushStampGenerator

{ + fn transform_mut(&mut self) -> &mut DAffine2 { + &mut self.transform + } +} + +impl Sample for BrushStampGenerator

{ + type Pixel = P; + + #[inline] + fn sample(&self, position: DVec2, area: DVec2) -> Option

{ + let position = self.transform.inverse().transform_point2(position); + let area = self.transform.inverse().transform_vector2(area); + let center = DVec2::splat(0.5); + + let distance = (position + area / 2. - center).length() as f32 * 2.; + + let result = if distance < 1. { + 1. - distance.powf(self.feather_exponent) + } else { + return None; + }; + + use graphene_core::raster::Channel; + Some(self.color.multiplied_alpha(P::AlphaChannel::from_f32(result))) + } +} + #[derive(Clone, Debug, PartialEq)] -pub struct BrushTextureNode { +pub struct BrushStampGeneratorNode { pub color: ColorNode, pub hardness: Hardness, pub flow: Flow, @@ -92,17 +133,14 @@ fn erase(input: (Color, Color), flow: f64) -> Color { Color::from_unassociated_alpha(input.r(), input.g(), input.b(), alpha) } -#[node_fn(BrushTextureNode)] -fn brush_texture(diameter: f64, color: Color, hardness: f64, flow: f64) -> ImageFrame { +#[node_fn(BrushStampGeneratorNode)] +fn brush_stamp_generator_node(diameter: f64, color: Color, hardness: f64, flow: f64) -> BrushStampGenerator { // Diameter let radius = diameter / 2.; - // TODO: Remove the 4px padding after figuring out why the brush stamp gets randomly offset by 1px up/down/left/right when clicking with the Brush tool - let dimension = diameter.ceil() as u32 + 4; - let center = DVec2::splat(radius + (dimension as f64 - diameter) / 2.); // Hardness let hardness = hardness / 100.; - let feather_exponent = 1. / (1. - hardness); + let feather_exponent = 1. / (1. - hardness) as f32; // Flow let flow = flow / 100.; @@ -110,33 +148,8 @@ fn brush_texture(diameter: f64, color: Color, hardness: f64, flow: f64) -> Image // Color let color = color.apply_opacity(flow as f32); - // Initial transparent image - let mut image = Image::new(dimension, dimension, Color::TRANSPARENT); - - for y in 0..dimension { - for x in 0..dimension { - let summation = MULTISAMPLE_GRID.iter().fold(0., |acc, (offset_x, offset_y)| { - let position = DVec2::new(x as f64 + offset_x, y as f64 + offset_y); - let distance = (position - center).length(); - - if distance < radius { - acc + (1. - (distance / radius).powf(feather_exponent)).clamp(0., 1.) - } else { - acc - } - }); - - let pixel_fill = summation / MULTISAMPLE_GRID.len() as f64; - - let pixel = image.get_pixel_mut(x, y).unwrap(); - *pixel = color.apply_opacity(pixel_fill as f32); - } - } - - ImageFrame { - image, - transform: DAffine2::from_scale_angle_translation(DVec2::splat(dimension as f64), 0., -DVec2::splat(radius)), - } + let transform = DAffine2::from_scale_angle_translation(DVec2::splat(diameter), 0., -DVec2::splat(radius)); + BrushStampGenerator { color, feather_exponent, transform } } #[derive(Clone, Debug, PartialEq)] @@ -183,19 +196,17 @@ mod test { #[test] fn test_brush_texture() { - let brush_texture_node = BrushTextureNode::new(ClonedNode::new(Color::BLACK), ClonedNode::new(100.), ClonedNode::new(100.)); + let brush_texture_node = BrushStampGeneratorNode::new(ClonedNode::new(Color::BLACK), ClonedNode::new(100.), ClonedNode::new(100.)); let size = 20.; let image = brush_texture_node.eval(size); - assert_eq!(image.image.width, size.ceil() as u32 + 4); - assert_eq!(image.image.height, size.ceil() as u32 + 4); - assert_eq!(image.transform, DAffine2::from_scale_angle_translation(DVec2::splat(size.ceil() + 4.), 0., -DVec2::splat(size / 2.))); + assert_eq!(image.transform(), DAffine2::from_scale_angle_translation(DVec2::splat(size.ceil()), 0., -DVec2::splat(size / 2.))); // center pixel should be BLACK - assert_eq!(image.image.get_pixel(11, 11), Some(Color::BLACK)); + assert_eq!(image.sample(DVec2::splat(0.), DVec2::ONE), Some(Color::BLACK)); } #[test] fn test_brush() { - let brush_texture_node = BrushTextureNode::new(ClonedNode::new(Color::BLACK), ClonedNode::new(1.0), ClonedNode::new(1.0)); + let brush_texture_node = BrushStampGeneratorNode::new(ClonedNode::new(Color::BLACK), ClonedNode::new(1.0), ClonedNode::new(1.0)); let image = brush_texture_node.eval(20.); let trace = vec![DVec2::new(0.0, 0.0), DVec2::new(10.0, 0.0)]; let trace = ClonedNode::new(trace.into_iter()); @@ -203,7 +214,6 @@ mod test { let frames = MapNode::new(ValueNode::new(translate_node)); let frames = trace.then(frames).eval(()).collect::>(); assert_eq!(frames.len(), 2); - assert_eq!(frames[0].image.width, 24); let background_bounds = ReduceNode::new(ClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new())); let background_bounds = background_bounds.eval(frames.clone().into_iter()); let background_bounds = ClonedNode::new(background_bounds.unwrap().to_transform()); @@ -211,8 +221,8 @@ mod test { let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(BlendMode::Normal), ClonedNode::new(1.0)); let final_image = ReduceNode::new(background_image, ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node)))); let final_image = final_image.eval(frames.into_iter()); - assert_eq!(final_image.image.height, 24); - assert_eq!(final_image.image.width, 34); + assert_eq!(final_image.image.height, 20); + assert_eq!(final_image.image.width, 30); drop(final_image); } } diff --git a/node-graph/gstd/src/memo.rs b/node-graph/gstd/src/memo.rs index 825f726a..19ca6bab 100644 --- a/node-graph/gstd/src/memo.rs +++ b/node-graph/gstd/src/memo.rs @@ -28,12 +28,12 @@ where if let Some((_, cached_value, keep)) = self.cache.iter().find(|(h, _, _)| *h == hash) { keep.store(true, std::sync::atomic::Ordering::Relaxed); - return cached_value; + cached_value } else { trace!("Cache miss"); let output = self.node.eval(input); let index = self.cache.push((hash, output, AtomicBool::new(true))); - return &self.cache[index].1; + &self.cache[index].1 } } @@ -70,7 +70,7 @@ where fn serialize(&self) -> Option { let output = self.output.lock().unwrap(); - (&*output).as_ref().map(|output| serde_json::to_string(output).ok()).flatten() + (*output).as_ref().and_then(|output| serde_json::to_string(output).ok()) } } @@ -110,7 +110,7 @@ impl<'i, T: 'i + Hash> Node<'i, Option> for LetNode { } trace!("Cache miss"); let index = self.cache.push((hash, input)); - return &self.cache[index].1; + &self.cache[index].1 } None => &self.cache.iter().last().expect("Let node was not initialized").1, } diff --git a/node-graph/gstd/src/raster.rs b/node-graph/gstd/src/raster.rs index 9c13128d..41a889b3 100644 --- a/node-graph/gstd/src/raster.rs +++ b/node-graph/gstd/src/raster.rs @@ -2,7 +2,7 @@ use dyn_any::{DynAny, StaticType}; use glam::{DAffine2, DVec2}; use graphene_core::raster::{Alpha, Channel, Image, ImageFrame, Luminance, Pixel, RasterMut, Sample}; use graphene_core::transform::Transform; -use graphene_core::value::{ClonedNode, ValueNode}; + use graphene_core::Node; use std::fmt::Debug; @@ -240,6 +240,7 @@ fn mask_image< // Transforms a point from the background image to the forground image let bg_to_fg = image.transform() * DAffine2::from_scale(1. / image_size); + let area = bg_to_fg.transform_point2(DVec2::new(1., 1.)) - bg_to_fg.transform_point2(DVec2::ZERO); for y in 0..image.height() { for x in 0..image.width() { let image_point = DVec2::new(x as f64, y as f64); @@ -247,8 +248,8 @@ fn mask_image< let local_mask_point = stencil.transform().inverse().transform_point2(mask_point); mask_point = stencil.transform().transform_point2(local_mask_point.clamp(DVec2::ZERO, DVec2::ONE)); - let image_pixel = image.get_pixel_mut(x as u32, y as u32).unwrap(); - if let Some(mask_pixel) = stencil.sample(mask_point) { + let image_pixel = image.get_pixel_mut(x, y).unwrap(); + if let Some(mask_pixel) = stencil.sample(mask_point, area) { *image_pixel = image_pixel.multiplied_alpha(mask_pixel.l().to_channel()); } } @@ -258,20 +259,20 @@ fn mask_image< } #[derive(Debug, Clone, Copy)] -pub struct BlendImageTupleNode { +pub struct BlendImageTupleNode { map_fn: MapFn, _p: PhantomData

, + _fg: PhantomData, } -#[node_macro::node_fn(BlendImageTupleNode<_P>)] -fn blend_image_tuple<_P: Pixel + Debug, MapFn>(images: (ImageFrame<_P>, ImageFrame<_P>), map_fn: &'any_input MapFn) -> ImageFrame<_P> +#[node_macro::node_fn(BlendImageTupleNode<_P, _Fg>)] +fn blend_image_tuple<_P: Pixel + Debug, MapFn, _Fg: Sample + Transform>(images: (ImageFrame<_P>, _Fg), map_fn: &'any_input MapFn) -> ImageFrame<_P> where MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'input + Clone, { let (background, foreground) = images; - let node = BlendImageNode::new(ClonedNode::new(background), ValueNode::new(map_fn.clone())); - node.eval(foreground) + blend_image(foreground, background, map_fn) } #[derive(Debug, Clone, Copy)] @@ -283,13 +284,20 @@ pub struct BlendImageNode { // TODO: Implement proper blending #[node_macro::node_fn(BlendImageNode<_P>)] -fn blend_image<_P: Clone, MapFn, Frame: Sample + Transform, Background: RasterMut + Transform>( +fn blend_image_node<_P: Clone, MapFn, Frame: Sample + Transform, Background: RasterMut + Transform>( foreground: Frame, - mut background: Background, + background: Background, map_fn: &'any_input MapFn, ) -> Background where MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P> + 'input, +{ + blend_image(foreground, background, map_fn) +} + +fn blend_image<_P: Clone, MapFn, Frame: Sample + Transform, Background: RasterMut + Transform>(foreground: Frame, mut background: Background, map_fn: &MapFn) -> Background +where + MapFn: for<'any_input> Node<'any_input, (_P, _P), Output = _P>, { let background_size = DVec2::new(background.width() as f64, background.height() as f64); @@ -303,12 +311,13 @@ where let start = (bg_aabb.start * background_size).max(DVec2::ZERO).as_uvec2(); let end = (bg_aabb.end * background_size).min(background_size).as_uvec2(); + let area = bg_to_fg.transform_point2(DVec2::new(1., 1.)) - bg_to_fg.transform_point2(DVec2::ZERO); for y in start.y..end.y { for x in start.x..end.x { let bg_point = DVec2::new(x as f64, y as f64); let fg_point = bg_to_fg.transform_point2(bg_point); - if let Some(src_pixel) = foreground.sample(fg_point) { + if let Some(src_pixel) = foreground.sample(fg_point, area) { if let Some(dst_pixel) = background.get_pixel_mut(x, y) { *dst_pixel = map_fn.eval((src_pixel, dst_pixel.clone())); } diff --git a/node-graph/interpreted-executor/src/executor.rs b/node-graph/interpreted-executor/src/executor.rs index cc4de12a..659bf393 100644 --- a/node-graph/interpreted-executor/src/executor.rs +++ b/node-graph/interpreted-executor/src/executor.rs @@ -176,8 +176,13 @@ impl BorrowTree { } pub fn push_node(&mut self, id: NodeId, proto_node: ProtoNode, typing_context: &TypingContext) -> Result<(), String> { - let ProtoNode { construction_args, identifier, .. } = proto_node; - self.source_map.insert(proto_node.document_node_path, id); + let ProtoNode { + construction_args, + identifier, + document_node_path, + .. + } = proto_node; + self.source_map.insert(document_node_path, id); match construction_args { ConstructionArgs::Value(value) => { diff --git a/node-graph/interpreted-executor/src/node_registry.rs b/node-graph/interpreted-executor/src/node_registry.rs index 5a2989a7..5feb68fa 100644 --- a/node-graph/interpreted-executor/src/node_registry.rs +++ b/node-graph/interpreted-executor/src/node_registry.rs @@ -8,7 +8,7 @@ use std::collections::HashMap; use graphene_core::raster::color::Color; use graphene_core::raster::*; use graphene_core::structural::Then; -use graphene_core::value::{ClonedNode, ForgetNode, ValueNode}; +use graphene_core::value::{ClonedNode, CopiedNode, ForgetNode, ValueNode}; use graphene_core::{Node, NodeIO, NodeIOTypes}; use graphene_std::brush::*; use graphene_std::raster::*; @@ -175,6 +175,7 @@ fn node_registry() -> HashMap> = DowncastBothNode::new(args[0]); @@ -183,24 +184,23 @@ fn node_registry() -> HashMap = DowncastBothNode::new(args[3]); let color: DowncastBothNode<(), Color> = DowncastBothNode::new(args[4]); - let stamp = BrushTextureNode::new(color, ClonedNode::new(hardness.eval(())), ClonedNode::new(flow.eval(()))); + let stamp = BrushStampGeneratorNode::new(color, CopiedNode::new(hardness.eval(())), CopiedNode::new(flow.eval(()))); let stamp = stamp.eval(diameter.eval(())); - let frames = TranslateNode::new(ClonedNode::new(stamp)); + let frames = TranslateNode::new(CopiedNode::new(stamp)); let frames = MapNode::new(ValueNode::new(frames)); let frames = frames.eval(trace.eval(()).into_iter()).collect::>(); - let background_bounds = ReduceNode::new(ClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new())); + let background_bounds = ReduceNode::new(DebugClonedNode::new(None), ValueNode::new(MergeBoundingBoxNode::new())); let background_bounds = background_bounds.eval(frames.clone().into_iter()); - let background_bounds = ClonedNode::new(background_bounds.unwrap().to_transform()); + let background_bounds = DebugClonedNode::new(background_bounds.unwrap().to_transform()); - let background_image = background_bounds.then(EmptyImageNode::new(ClonedNode::new(Color::TRANSPARENT))); + let background_image = background_bounds.then(EmptyImageNode::new(CopiedNode::new(Color::TRANSPARENT))); - let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(BlendMode::Normal), ClonedNode::new(100.)); + let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(BlendMode::Normal), CopiedNode::new(100.)); let final_image = ReduceNode::new(background_image, ValueNode::new(BlendImageTupleNode::new(ValueNode::new(blend_node)))); - let final_image = final_image.eval(frames.into_iter()); - let final_image = ClonedNode::new(final_image); + let final_image = DebugClonedNode::new(frames.into_iter()).then(final_image); let any: DynAnyNode<(), _, _> = graphene_std::any::DynAnyNode::new(ValueNode::new(final_image)); Box::pin(any) @@ -241,7 +241,7 @@ fn node_registry() -> HashMap> = DowncastBothNode::new(args[0]); let blend_mode: DowncastBothNode<(), BlendMode> = DowncastBothNode::new(args[1]); let opacity: DowncastBothNode<(), f64> = DowncastBothNode::new(args[2]); - let blend_node = graphene_core::raster::BlendNode::new(ClonedNode::new(blend_mode.eval(())), ClonedNode::new(opacity.eval(()))); + let blend_node = graphene_core::raster::BlendNode::new(CopiedNode::new(blend_mode.eval(())), CopiedNode::new(opacity.eval(()))); let node = graphene_std::raster::BlendImageNode::new(image, ValueNode::new(blend_node)); let _ = &node as &dyn for<'i> Node<'i, ImageFrame, Output = ImageFrame>; let any: DynAnyNode, _, _> = graphene_std::any::DynAnyNode::new(graphene_core::value::ValueNode::new(node)); diff --git a/website/other/bezier-rs-demos/wasm/src/subpath.rs b/website/other/bezier-rs-demos/wasm/src/subpath.rs index f672e70f..05add934 100644 --- a/website/other/bezier-rs-demos/wasm/src/subpath.rs +++ b/website/other/bezier-rs-demos/wasm/src/subpath.rs @@ -207,13 +207,13 @@ impl WasmSubpath { // Line between pivot and start point on curve let original_dashed_line = format!( r#""#, - self.0.iter().nth(0).unwrap().start().x, - self.0.iter().nth(0).unwrap().start().y + self.0.iter().next().unwrap().start().x, + self.0.iter().next().unwrap().start().y ); let rotated_dashed_line = format!( r#""#, - rotated_subpath.iter().nth(0).unwrap().start().x, - rotated_subpath.iter().nth(0).unwrap().start().y + rotated_subpath.iter().next().unwrap().start().x, + rotated_subpath.iter().next().unwrap().start().y ); wrap_svg_tag(format!("{subpath_svg}{rotated_subpath_svg}{pivot}{original_dashed_line}{rotated_dashed_line}"))