diff --git a/Cargo.lock b/Cargo.lock index 5afed9afa4..98949458f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "Inflector" @@ -1180,9 +1180,9 @@ checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] name = "darling" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -1190,9 +1190,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", @@ -1204,9 +1204,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", @@ -1221,9 +1221,9 @@ checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" [[package]] name = "deranged" -version = "0.4.0" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", "serde", @@ -4506,9 +4506,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" dependencies = [ "base64 0.22.1", "indexmap 2.7.1", @@ -5956,9 +5956,9 @@ dependencies = [ [[package]] name = "string_cache" -version = "0.8.9" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +checksum = "938d512196766101d333398efde81bc1f37b00cb42c2f8350e5df639f040bbbe" dependencies = [ "new_debug_unreachable", "parking_lot", @@ -6102,9 +6102,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.32.8" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1" +checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82" dependencies = [ "bitflags 2.9.0", "core-foundation 0.10.0", @@ -6133,8 +6133,8 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.1", + "windows-core 0.61.0", "windows-version", "x11-dl", ] @@ -6158,9 +6158,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d08db1ff9e011e04014e737ec022610d756c0eae0b3b3a9037bccaf3003173a" +checksum = "be03adf68fba02f87c4653da7bd73f40b0ecf9c6b7c2c39830f6981d0651912f" dependencies = [ "anyhow", "bytes", @@ -6181,6 +6181,7 @@ dependencies = [ "objc2 0.6.0", "objc2-app-kit", "objc2-foundation 0.3.0", + "objc2-ui-kit", "percent-encoding", "plist", "raw-window-handle", @@ -6203,14 +6204,14 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows 0.60.0", + "windows 0.61.1", ] [[package]] name = "tauri-build" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd20e4661c2cce65343319e6e8da256958f5af958cafc47c0d0af66a55dcd17" +checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc" dependencies = [ "anyhow", "cargo_toml", @@ -6230,9 +6231,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4" +checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47" dependencies = [ "base64 0.22.1", "brotli", @@ -6257,9 +6258,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d402813d3b9c773a0fa58697c457c771f10e735498fdcb7b343264d18e5a601f" +checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -6271,9 +6272,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4190775d6ff73fe66d9af44c012739a2659720efd9c0e1e56a918678038699d" +checksum = "37a5ebe6a610d1b78a94650896e6f7c9796323f408800cef436e0fa0539de601" dependencies = [ "anyhow", "glob", @@ -6356,29 +6357,31 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00ada7ac2f9276f09b8c3afffd3215fd5d9bff23c22df8a7c70e7ef67cacd532" +checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39" dependencies = [ "cookie", "dpi", "gtk", "http", "jni", + "objc2 0.6.0", + "objc2-ui-kit", "raw-window-handle", "serde", "serde_json", "tauri-utils", "thiserror 2.0.12", "url", - "windows 0.60.0", + "windows 0.61.1", ] [[package]] name = "tauri-runtime-wry" -version = "2.5.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2e5842c57e154af43a20a49c7efee0ce2578c20b4c2bdf266852b422d2e421" +checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2" dependencies = [ "gtk", "http", @@ -6397,15 +6400,15 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows 0.60.0", + "windows 0.61.1", "wry", ] [[package]] name = "tauri-utils" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447" +checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4" dependencies = [ "anyhow", "brotli", @@ -6542,9 +6545,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" dependencies = [ "deranged", "itoa 1.0.15", @@ -6557,15 +6560,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" dependencies = [ "num-conv", "time-core", @@ -7072,9 +7075,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ "getrandom 0.3.1", "serde", @@ -7488,15 +7491,15 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3" +checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.60.0", - "windows-core 0.60.1", - "windows-implement 0.59.0", + "windows 0.61.1", + "windows-core 0.61.0", + "windows-implement 0.60.0", "windows-interface 0.59.1", ] @@ -7513,13 +7516,13 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac" +checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295" dependencies = [ "thiserror 2.0.12", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.1", + "windows-core 0.61.0", ] [[package]] @@ -7719,12 +7722,12 @@ dependencies = [ [[package]] name = "windows" -version = "0.60.0" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" +checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.60.1", + "windows-core 0.61.0", "windows-future", "windows-link", "windows-numerics", @@ -7732,11 +7735,11 @@ dependencies = [ [[package]] name = "windows-collections" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.60.1", + "windows-core 0.61.0", ] [[package]] @@ -7763,24 +7766,24 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.60.1" +version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ - "windows-implement 0.59.0", + "windows-implement 0.60.0", "windows-interface 0.59.1", "windows-link", - "windows-result 0.3.1", - "windows-strings 0.3.1", + "windows-result 0.3.2", + "windows-strings 0.4.0", ] [[package]] name = "windows-future" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" +checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.60.1", + "windows-core 0.61.0", "windows-link", ] @@ -7797,9 +7800,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.59.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", @@ -7830,17 +7833,17 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-numerics" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.60.1", + "windows-core 0.61.0", "windows-link", ] @@ -7866,9 +7869,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] @@ -7885,9 +7888,9 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" dependencies = [ "windows-link", ] @@ -8214,9 +8217,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.50.5" +version = "0.51.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730" +checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2" dependencies = [ "base64 0.22.1", "block2 0.6.0", @@ -8250,8 +8253,8 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.60.0", - "windows-core 0.60.1", + "windows 0.61.1", + "windows-core 0.61.0", "windows-version", "x11-dl", ] diff --git a/editor/src/messages/input_mapper/input_mappings.rs b/editor/src/messages/input_mapper/input_mappings.rs index 0448726b0a..f6ae6bce3a 100644 --- a/editor/src/messages/input_mapper/input_mappings.rs +++ b/editor/src/messages/input_mapper/input_mappings.rs @@ -273,14 +273,6 @@ pub fn input_mappings() -> Mapping { entry!(KeyDown(MouseRight); action_dispatch=FreehandToolMessage::Abort), entry!(KeyDown(Escape); action_dispatch=FreehandToolMessage::Abort), // - // SplineToolMessage - entry!(PointerMove; action_dispatch=SplineToolMessage::PointerMove), - entry!(KeyDown(MouseLeft); action_dispatch=SplineToolMessage::DragStart { append_to_selected: Shift }), - entry!(KeyUp(MouseLeft); action_dispatch=SplineToolMessage::DragStop), - entry!(KeyDown(MouseRight); action_dispatch=SplineToolMessage::Confirm), - entry!(KeyDown(Escape); action_dispatch=SplineToolMessage::Confirm), - entry!(KeyDown(Enter); action_dispatch=SplineToolMessage::Confirm), - // // FillToolMessage entry!(KeyDown(MouseLeft); action_dispatch=FillToolMessage::FillPrimaryColor), entry!(KeyDown(MouseLeft); modifiers=[Shift], action_dispatch=FillToolMessage::FillSecondaryColor), diff --git a/editor/src/messages/prelude.rs b/editor/src/messages/prelude.rs index 8c03ac51fe..b649a36634 100644 --- a/editor/src/messages/prelude.rs +++ b/editor/src/messages/prelude.rs @@ -47,7 +47,6 @@ pub use crate::messages::tool::tool_messages::pen_tool::{PenToolMessage, PenTool pub use crate::messages::tool::tool_messages::polygon_tool::{PolygonToolMessage, PolygonToolMessageDiscriminant}; pub use crate::messages::tool::tool_messages::rectangle_tool::{RectangleToolMessage, RectangleToolMessageDiscriminant}; pub use crate::messages::tool::tool_messages::select_tool::{SelectToolMessage, SelectToolMessageDiscriminant}; -pub use crate::messages::tool::tool_messages::spline_tool::{SplineToolMessage, SplineToolMessageDiscriminant}; pub use crate::messages::tool::tool_messages::text_tool::{TextToolMessage, TextToolMessageDiscriminant}; // Helper diff --git a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs index ea9ab3bb60..d43a9fab31 100644 --- a/editor/src/messages/tool/common_functionality/graph_modification_utils.rs +++ b/editor/src/messages/tool/common_functionality/graph_modification_utils.rs @@ -86,74 +86,42 @@ pub fn merge_layers(document: &DocumentMessageHandler, first_layer: LayerNodeIde downstream_input: InputConnector::node(second_layer.to_node(), 1), input_connector: InputConnector::node(merge_node_id, 1), }); + // We delete the second layer node without deleting its child nodes responses.add(NodeGraphMessage::DeleteNodes { node_ids: vec![second_layer.to_node()], delete_children: false, }); // Add a flatten vector elements node after the merge - let flatten_node_id = NodeId::new(); - let flatten_node = document_node_definitions::resolve_document_node_type("Flatten Vector Elements") - .expect("Failed to create flatten node") - .default_node_template(); - responses.add(NodeGraphMessage::InsertNode { - node_id: flatten_node_id, - node_template: flatten_node, - }); - responses.add(NodeGraphMessage::MoveNodeToChainStart { - node_id: flatten_node_id, - parent: first_layer, - }); - + insert_node_to_layer(first_layer, "Flatten Vector Elements", responses); // Add a path node after the flatten node - let path_node_id = NodeId::new(); - let path_node = document_node_definitions::resolve_document_node_type("Path") - .expect("Failed to create path node") - .default_node_template(); - responses.add(NodeGraphMessage::InsertNode { - node_id: path_node_id, - node_template: path_node, - }); - responses.add(NodeGraphMessage::MoveNodeToChainStart { - node_id: path_node_id, - parent: first_layer, - }); + insert_node_to_layer(first_layer, "Path", responses); // Add a Spline node after the Path node if both the layers we are merging is spline. if current_and_other_layer_is_spline { - let spline_node_id = NodeId::new(); - let spline_node = document_node_definitions::resolve_document_node_type("Spline") - .expect("Failed to create Spline node") - .default_node_template(); - responses.add(NodeGraphMessage::InsertNode { - node_id: spline_node_id, - node_template: spline_node, - }); - responses.add(NodeGraphMessage::MoveNodeToChainStart { - node_id: spline_node_id, - parent: first_layer, - }); + insert_node_to_layer(first_layer, "Spline", responses); } + insert_node_to_layer(first_layer, "Fill", responses); + insert_node_to_layer(first_layer, "Stroke", responses); // Add a transform node to ensure correct tooling modifications - let transform_node_id = NodeId::new(); - let transform_node = document_node_definitions::resolve_document_node_type("Transform") - .expect("Failed to create transform node") - .default_node_template(); - responses.add(NodeGraphMessage::InsertNode { - node_id: transform_node_id, - node_template: transform_node, - }); - responses.add(NodeGraphMessage::MoveNodeToChainStart { - node_id: transform_node_id, - parent: first_layer, - }); + insert_node_to_layer(first_layer, "Transform", responses); responses.add(NodeGraphMessage::RunDocumentGraph); responses.add(Message::StartBuffer); responses.add(PenToolMessage::RecalculateLatestPointsPosition); } +fn insert_node_to_layer(layer: LayerNodeIdentifier, node_identifier: &str, responses: &mut VecDeque) { + // Add a transform node to ensure correct tooling modifications + let node_id = NodeId::new(); + let node = document_node_definitions::resolve_document_node_type(node_identifier) + .expect(&format!("Failed to create {} node.", node_identifier)) + .default_node_template(); + responses.add(NodeGraphMessage::InsertNode { node_id, node_template: node }); + responses.add(NodeGraphMessage::MoveNodeToChainStart { node_id, parent: layer }); +} + /// Merge the `first_endpoint` with `second_endpoint`. pub fn merge_points(document: &DocumentMessageHandler, layer: LayerNodeIdentifier, first_endpoint: PointId, second_endpont: PointId, responses: &mut VecDeque) { let transform = document.metadata().transform_to_document(layer); diff --git a/editor/src/messages/tool/tool_message.rs b/editor/src/messages/tool/tool_message.rs index ede4e4008d..8b7abb86e1 100644 --- a/editor/src/messages/tool/tool_message.rs +++ b/editor/src/messages/tool/tool_message.rs @@ -30,8 +30,6 @@ pub enum ToolMessage { #[child] Freehand(FreehandToolMessage), #[child] - Spline(SplineToolMessage), - #[child] Line(LineToolMessage), #[child] Rectangle(RectangleToolMessage), diff --git a/editor/src/messages/tool/tool_messages/mod.rs b/editor/src/messages/tool/tool_messages/mod.rs index 65ac871ab2..6ebb23c681 100644 --- a/editor/src/messages/tool/tool_messages/mod.rs +++ b/editor/src/messages/tool/tool_messages/mod.rs @@ -13,7 +13,6 @@ pub mod pen_tool; pub mod polygon_tool; pub mod rectangle_tool; pub mod select_tool; -pub mod spline_tool; pub mod text_tool; pub mod tool_prelude { diff --git a/editor/src/messages/tool/tool_messages/pen_tool.rs b/editor/src/messages/tool/tool_messages/pen_tool.rs index 1dbdad5c30..4a7b38c017 100644 --- a/editor/src/messages/tool/tool_messages/pen_tool.rs +++ b/editor/src/messages/tool/tool_messages/pen_tool.rs @@ -1,21 +1,26 @@ use super::tool_prelude::*; -use crate::consts::{DEFAULT_STROKE_WIDTH, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE}; +use crate::consts::{DEFAULT_STROKE_WIDTH, DRAG_THRESHOLD, HIDE_HANDLE_DISTANCE, LINE_ROTATE_SNAP_ANGLE, PATH_JOIN_THRESHOLD, SNAP_POINT_TOLERANCE}; use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; -use crate::messages::portfolio::document::overlays::utility_functions::path_overlays; +use crate::messages::portfolio::document::overlays::utility_functions::{path_endpoint_overlays, path_overlays}; use crate::messages::portfolio::document::overlays::utility_types::{DrawHandles, OverlayContext}; use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; use crate::messages::tool::common_functionality::auto_panning::AutoPanning; use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; -use crate::messages::tool::common_functionality::graph_modification_utils::{self, merge_layers}; +use crate::messages::tool::common_functionality::graph_modification_utils::{self, find_spline, merge_layers, merge_points}; use crate::messages::tool::common_functionality::shape_editor::ShapeState; use crate::messages::tool::common_functionality::snapping::{SnapCache, SnapCandidatePoint, SnapConstraint, SnapData, SnapManager, SnapTypeConfiguration}; use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend}; use bezier_rs::{Bezier, BezierHandles}; -use graph_craft::document::NodeId; +use graph_craft::document::{NodeId, NodeInput}; use graphene_core::Color; use graphene_core::vector::{PointId, VectorModificationType}; use graphene_std::vector::{HandleId, ManipulatorPointId, NoHashBuilder, SegmentId, VectorData}; +use spline_mode::*; +use std::fmt; + +// TODO: Refactor the code into new module for drawing a path +mod spline_mode; #[derive(Default)] pub struct PenTool { @@ -29,6 +34,7 @@ pub struct PenOptions { fill: ToolColorOptions, stroke: ToolColorOptions, pen_overlay_mode: PenOverlayMode, + tool_mode: ToolMode, } impl Default for PenOptions { @@ -38,6 +44,7 @@ impl Default for PenOptions { fill: ToolColorOptions::new_secondary(), stroke: ToolColorOptions::new_primary(), pen_overlay_mode: PenOverlayMode::FrontierHandles, + tool_mode: ToolMode::Path, } } } @@ -80,6 +87,7 @@ pub enum PenToolMessage { Redo, Undo, UpdateOptions(PenOptionsUpdate), + ToolModeChanged, RecalculateLatestPointsPosition, RemovePreviousHandle, GRS { @@ -90,6 +98,8 @@ pub enum PenToolMessage { FinalPosition { final_position: DVec2, }, + /// Specific to spline mode. + SplineMergeEndpoints, SwapHandles, } @@ -100,6 +110,8 @@ enum PenToolFsmState { DraggingHandle(HandleMode), PlacingAnchor, GRSHandle, + SplineDrawing, + SplineMergingEndpoints, } #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] @@ -117,6 +129,25 @@ pub enum PenOptionsUpdate { StrokeColorType(ToolColorType), WorkingColors(Option, Option), OverlayModeType(PenOverlayMode), + ToolMode(ToolMode), +} + +impl PenTool { + fn tool_mode_widget(&self) -> WidgetHolder { + let tool_mode_entries = [ToolMode::Path, ToolMode::Spline] + .iter() + .map(|mode| { + MenuListEntry::new(format!("{mode:?}")) + .label(mode.to_string()) + .on_commit(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::ToolMode(*mode)).into()) + }) + .collect(); + + DropdownInput::new(vec![tool_mode_entries]) + .selected_index(Some((self.options.tool_mode) as u32)) + .tooltip(format!("Path Mode:\n\n{}\n{}", ToolMode::Path.description(), ToolMode::Spline.description())) + .widget_holder() + } } impl ToolMetadata for PenTool { @@ -143,13 +174,15 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder { impl LayoutHolder for PenTool { fn layout(&self) -> Layout { - let mut widgets = self.options.fill.create_widgets( + let mut widgets = Vec::new(); + + widgets.append(&mut self.options.fill.create_widgets( "Fill", true, |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(None)).into(), |color_type: ToolColorType| WidgetCallback::new(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColorType(color_type.clone())).into()), |color: &ColorInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(), - ); + )); widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder()); @@ -167,20 +200,26 @@ impl LayoutHolder for PenTool { widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder()); - widgets.push( - RadioInput::new(vec![ - RadioEntryData::new("all") - .icon("HandleVisibilityAll") - .tooltip("Show all handles regardless of selection") - .on_update(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::OverlayModeType(PenOverlayMode::AllHandles)).into()), - RadioEntryData::new("frontier") - .icon("HandleVisibilityFrontier") - .tooltip("Show only handles at the frontiers of the segments connected to selected points") - .on_update(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::OverlayModeType(PenOverlayMode::FrontierHandles)).into()), - ]) - .selected_index(Some(self.options.pen_overlay_mode as u32)) - .widget_holder(), - ); + widgets.push(self.tool_mode_widget()); + + widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder()); + + if self.options.tool_mode == ToolMode::Path { + widgets.push( + RadioInput::new(vec![ + RadioEntryData::new("all") + .icon("HandleVisibilityAll") + .tooltip("Show all handles regardless of selection") + .on_update(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::OverlayModeType(PenOverlayMode::AllHandles)).into()), + RadioEntryData::new("frontier") + .icon("HandleVisibilityFrontier") + .tooltip("Show only handles at the frontiers of the segments connected to selected points") + .on_update(move |_| PenToolMessage::UpdateOptions(PenOptionsUpdate::OverlayModeType(PenOverlayMode::FrontierHandles)).into()), + ]) + .selected_index(Some(self.options.pen_overlay_mode as u32)) + .widget_holder(), + ); + } Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }])) } @@ -215,6 +254,10 @@ impl<'a> MessageHandler> for PenTool self.options.fill.primary_working_color = primary; self.options.fill.secondary_working_color = secondary; } + PenOptionsUpdate::ToolMode(tool_mode) => { + self.options.tool_mode = tool_mode; + responses.add(PenToolMessage::ToolModeChanged); + } } self.send_layout(responses, LayoutTarget::ToolOptions); @@ -241,6 +284,15 @@ impl<'a> MessageHandler> for PenTool GRS, SwapHandles ), + PenToolFsmState::SplineDrawing => actions!(PenToolMessageDiscriminant; + DragStop, + PointerMove, + Confirm, + Abort, + ), + PenToolFsmState::SplineMergingEndpoints => actions!(PenToolMessageDiscriminant; + SplineMergeEndpoints, + ), } } } @@ -283,6 +335,31 @@ enum HandleMode { ColinearEquidistant, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize, specta::Type)] +pub enum ToolMode { + #[default] + Path, + Spline, +} + +impl fmt::Display for ToolMode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ToolMode::Path => write!(f, "Path"), + ToolMode::Spline => write!(f, "Spline"), + } + } +} + +impl ToolMode { + fn description(&self) -> &'static str { + match self { + ToolMode::Path => "Path mode is the standard Pen tool behavior for drawing Bézier paths.", + ToolMode::Spline => "Spline mode is for placing the control points of a spline curve.", + } + } +} + /// The type of handle which is dragged by the cursor (under the cursor). /// /// ![Terminology](https://files.keavon.com/-/EachNotedLovebird/capture.png) @@ -324,6 +401,8 @@ enum TargetHandle { #[derive(Clone, Debug, Default)] struct PenToolData { + spline_mode_tool_data: SplineModeToolData, + snap_manager: SnapManager, latest_points: Vec, point_index: usize, @@ -1384,6 +1463,141 @@ impl Fsm for PenToolFsmState { let ToolMessage::Pen(event) = event else { return self }; match (self, event) { + (state, PenToolMessage::ToolModeChanged) => { + if !matches!(state, PenToolFsmState::Ready) { + responses.add(PenToolMessage::Abort); + responses.add(PenToolMessage::ToolModeChanged); + return state; + } + state + } + (PenToolFsmState::SplineDrawing, PenToolMessage::DragStop) => { + let tool_data = &mut tool_data.spline_mode_tool_data; + if tool_data.current_layer.is_none() { + return PenToolFsmState::Ready; + }; + // The first DragStop event will be ignored to prevent insertion of new point. + if tool_data.extend { + tool_data.extend = false; + return PenToolFsmState::SplineDrawing; + } + + responses.add(DocumentMessage::StartTransaction); + + tool_data.next_point = tool_data.snapped_point(document, input).snapped_point_document; + if tool_data.points.last().map_or(true, |last_pos| last_pos.1.distance(tool_data.next_point) > DRAG_THRESHOLD) { + let preview_point = tool_data.preview_point; + extend_spline(tool_data, false, responses); + tool_data.preview_point = preview_point; + + if try_merging_latest_endpoint(document, tool_data, preferences).is_some() { + responses.add(PenToolMessage::Confirm); + } + } + + PenToolFsmState::SplineDrawing + } + ( + PenToolFsmState::SplineDrawing, + PenToolMessage::PointerMove { + snap_angle, + break_handle, + lock_angle, + colinear, + move_anchor_with_handles, + }, + ) => { + let tool_data = &mut tool_data.spline_mode_tool_data; + let Some(layer) = tool_data.current_layer else { return PenToolFsmState::Ready }; + let ignore = |cp: PointId| tool_data.preview_point.is_some_and(|pp| pp == cp) || tool_data.points.last().is_some_and(|(ep, _)| *ep == cp); + let join_point = closest_point(document, input.mouse.position, PATH_JOIN_THRESHOLD, vec![layer].into_iter(), ignore, preferences); + + // Endpoints snapping + if let Some((_, _, point)) = join_point { + tool_data.next_point = point; + tool_data.snap_manager.clear_indicator(); + } else { + let snapped_point = tool_data.snapped_point(document, input); + tool_data.next_point = snapped_point.snapped_point_document; + tool_data.snap_manager.update_indicator(snapped_point); + } + + extend_spline(tool_data, true, responses); + + // Auto-panning + let messages = [ + PenToolMessage::PointerOutsideViewport { + snap_angle, + break_handle, + lock_angle, + colinear, + move_anchor_with_handles, + } + .into(), + PenToolMessage::PointerMove { + snap_angle, + break_handle, + lock_angle, + colinear, + move_anchor_with_handles, + } + .into(), + ]; + tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses); + + PenToolFsmState::SplineDrawing + } + (PenToolFsmState::SplineDrawing, PenToolMessage::PointerOutsideViewport { .. }) => { + let tool_data = &mut tool_data.spline_mode_tool_data; + // Auto-panning + let _ = tool_data.auto_panning.shift_viewport(input, responses); + + PenToolFsmState::SplineDrawing + } + (PenToolFsmState::SplineDrawing, PenToolMessage::Confirm) => { + let tool_data = &mut tool_data.spline_mode_tool_data; + if tool_data.points.len() >= 2 { + delete_preview(tool_data, responses); + } + responses.add(PenToolMessage::SplineMergeEndpoints); + PenToolFsmState::SplineMergingEndpoints + } + (PenToolFsmState::SplineDrawing, PenToolMessage::Abort) => { + let tool_data = &mut tool_data.spline_mode_tool_data; + delete_preview(tool_data, responses); + + responses.add(PenToolMessage::Abort); + PenToolFsmState::Ready + } + (PenToolFsmState::SplineMergingEndpoints, PenToolMessage::SplineMergeEndpoints) => { + let tool_data = &mut tool_data.spline_mode_tool_data; + let Some(current_layer) = tool_data.current_layer else { return PenToolFsmState::Ready }; + + if let Some(&layer) = tool_data.merge_layers.iter().last() { + merge_layers(document, current_layer, layer, responses); + tool_data.merge_layers.remove(&layer); + + responses.add(PenToolMessage::SplineMergeEndpoints); + return PenToolFsmState::SplineMergingEndpoints; + } + + let Some((start_endpoint, _)) = tool_data.points.first() else { return PenToolFsmState::Ready }; + let Some((last_endpoint, _)) = tool_data.points.last() else { return PenToolFsmState::Ready }; + + if let Some((position, second_endpoint)) = tool_data.merge_endpoints.pop() { + let first_endpoint = match position { + EndpointPosition::Start => *start_endpoint, + EndpointPosition::End => *last_endpoint, + }; + merge_points(document, current_layer, first_endpoint, second_endpoint, responses); + + responses.add(PenToolMessage::SplineMergeEndpoints); + return PenToolFsmState::SplineMergingEndpoints; + } + + responses.add(DocumentMessage::EndTransaction); + PenToolFsmState::Ready + } (PenToolFsmState::PlacingAnchor | PenToolFsmState::GRSHandle, PenToolMessage::GRS { grab, rotate, scale }) => { let Some(layer) = layer else { return PenToolFsmState::PlacingAnchor }; @@ -1521,6 +1735,12 @@ impl Fsm for PenToolFsmState { self } (_, PenToolMessage::Overlays(mut overlay_context)) => { + if tool_options.tool_mode == ToolMode::Spline { + let spline_tool_data = &mut tool_data.spline_mode_tool_data; + path_endpoint_overlays(document, shape_editor, &mut overlay_context, preferences); + spline_tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); + return self; + } let valid = |point: DVec2, handle: DVec2| point.distance_squared(handle) >= HIDE_HANDLE_DISTANCE * HIDE_HANDLE_DISTANCE; let transform = document.metadata().document_to_viewport * transform; @@ -1626,6 +1846,79 @@ impl Fsm for PenToolFsmState { self } (PenToolFsmState::Ready, PenToolMessage::DragStart { append_to_selected }) => { + if tool_options.tool_mode == ToolMode::Spline { + let tool_data = &mut tool_data.spline_mode_tool_data; + responses.add(DocumentMessage::StartTransaction); + + tool_data.snap_manager.cleanup(responses); + tool_data.cleanup(); + tool_data.weight = tool_options.line_weight; + + let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); + let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); + let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); + + let layers = LayerNodeIdentifier::ROOT_PARENT + .descendants(document.metadata()) + .filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[])); + + // Extend an endpoint of the selected path + if let Some((layer, point, position)) = should_extend(document, viewport, SNAP_POINT_TOLERANCE, layers, preferences) { + if find_spline(document, layer).is_some() { + // If the point is the part of Spline then we extend it. + tool_data.current_layer = Some(layer); + tool_data.points.push((point, position)); + tool_data.next_point = position; + tool_data.extend = true; + + extend_spline(tool_data, true, responses); + + return PenToolFsmState::SplineDrawing; + } else { + tool_data.merge_layers.insert(layer); + tool_data.merge_endpoints.push((EndpointPosition::Start, point)); + } + } + + let selected_nodes = document.network_interface.selected_nodes(); + let mut selected_layers_except_artboards = selected_nodes.selected_layers_except_artboards(&document.network_interface); + let selected_layer = selected_layers_except_artboards.next().filter(|_| selected_layers_except_artboards.next().is_none()); + + let append_to_selected_layer = input.keyboard.key(append_to_selected); + + // Create new path in the selected layer when shift is down + match (selected_layer, append_to_selected_layer) { + (Some(layer), true) => { + tool_data.current_layer = Some(layer); + + let transform = document.metadata().transform_to_viewport(layer); + let position = transform.inverse().transform_point2(input.mouse.position); + tool_data.next_point = position; + + return PenToolFsmState::SplineDrawing; + } + _ => {} + } + + responses.add(DocumentMessage::DeselectAllLayers); + + let parent = document.new_layer_bounding_artboard(input); + + let path_node_type = resolve_document_node_type("Path").expect("Path node does not exist"); + let path_node = path_node_type.default_node_template(); + let spline_node_type = resolve_document_node_type("Spline").expect("Spline node does not exist"); + let spline_node = spline_node_type.node_template_input_override([Some(NodeInput::node(NodeId(1), 0))]); + let nodes = vec![(NodeId(1), path_node), (NodeId(0), spline_node)]; + + let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, parent, responses); + tool_options.fill.apply_fill(layer, responses); + tool_options.stroke.apply_stroke(tool_data.weight, layer, responses); + tool_data.current_layer = Some(layer); + + responses.add(Message::StartBuffer); + + return PenToolFsmState::SplineDrawing; + } responses.add(DocumentMessage::StartTransaction); tool_data.handle_mode = HandleMode::Free; @@ -2077,6 +2370,12 @@ impl Fsm for PenToolFsmState { dragging_hint_data.0.push(HintGroup(hold_group)); dragging_hint_data } + PenToolFsmState::SplineDrawing => HintData(vec![ + HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), + HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Extend Spline")]), + HintGroup(vec![HintInfo::keys([Key::Enter], "End Spline")]), + ]), + PenToolFsmState::SplineMergingEndpoints => HintData(vec![]), }; responses.add(FrontendMessage::UpdateInputHints { hint_data }); diff --git a/editor/src/messages/tool/tool_messages/pen_tool/spline_mode.rs b/editor/src/messages/tool/tool_messages/pen_tool/spline_mode.rs new file mode 100644 index 0000000000..6eed25351e --- /dev/null +++ b/editor/src/messages/tool/tool_messages/pen_tool/spline_mode.rs @@ -0,0 +1,125 @@ +use super::super::tool_prelude::*; +use crate::consts::PATH_JOIN_THRESHOLD; +use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; +use crate::messages::tool::common_functionality::auto_panning::AutoPanning; +use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnapTypeConfiguration, SnappedPoint}; +use crate::messages::tool::common_functionality::utility_functions::closest_point; + +use graphene_std::vector::{PointId, SegmentId, VectorModificationType}; + +#[derive(Clone, Debug)] +pub(super) enum EndpointPosition { + Start, + End, +} + +#[derive(Clone, Debug, Default)] +pub(super) struct SplineModeToolData { + /// List of points inserted. + pub points: Vec<(PointId, DVec2)>, + /// Point to be inserted. + pub next_point: DVec2, + /// Point that was inserted temporarily to show preview. + pub preview_point: Option, + /// Segment that was inserted temporarily to show preview. + pub preview_segment: Option, + pub extend: bool, + pub weight: f64, + /// The layer we are editing. + pub current_layer: Option, + /// The layers to merge to the current layer before we merge endpoints in merge_endpoint field. + pub merge_layers: HashSet, + /// The endpoint IDs to merge with the spline's start/end endpoint after spline drawing is finished. + pub merge_endpoints: Vec<(EndpointPosition, PointId)>, + pub snap_manager: SnapManager, + pub auto_panning: AutoPanning, +} + +impl SplineModeToolData { + pub fn cleanup(&mut self) { + self.current_layer = None; + self.merge_layers = HashSet::new(); + self.merge_endpoints = Vec::new(); + self.preview_point = None; + self.preview_segment = None; + self.extend = false; + self.points = Vec::new(); + } + + /// Get the snapped point while ignoring current layer + pub fn snapped_point(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) -> SnappedPoint { + let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); + let ignore = if let Some(layer) = self.current_layer { vec![layer] } else { vec![] }; + let snap_data = SnapData::ignore(document, input, &ignore); + self.snap_manager.free_snap(&snap_data, &point, SnapTypeConfiguration::default()) + } +} + +pub(super) fn try_merging_latest_endpoint(document: &DocumentMessageHandler, tool_data: &mut SplineModeToolData, preferences: &PreferencesMessageHandler) -> Option<()> { + if tool_data.points.len() < 2 { + return None; + }; + let (last_endpoint, last_endpoint_position) = tool_data.points.last()?; + let preview_point = tool_data.preview_point; + let current_layer = tool_data.current_layer?; + + let layers = LayerNodeIdentifier::ROOT_PARENT + .descendants(document.metadata()) + .filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[])); + + let exclude = |p: PointId| preview_point.is_some_and(|pp| pp == p) || *last_endpoint == p; + let position = document.metadata().transform_to_viewport(current_layer).transform_point2(*last_endpoint_position); + + let (layer, endpoint, _) = closest_point(document, position, PATH_JOIN_THRESHOLD, layers, exclude, preferences)?; + tool_data.merge_layers.insert(layer); + tool_data.merge_endpoints.push((EndpointPosition::End, endpoint)); + + Some(()) +} + +pub(super) fn extend_spline(tool_data: &mut SplineModeToolData, show_preview: bool, responses: &mut VecDeque) { + delete_preview(tool_data, responses); + + let Some(layer) = tool_data.current_layer else { return }; + + let next_point_pos = tool_data.next_point; + let next_point_id = PointId::generate(); + let modification_type = VectorModificationType::InsertPoint { + id: next_point_id, + position: next_point_pos, + }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + + if let Some((last_point_id, _)) = tool_data.points.last() { + let points = [*last_point_id, next_point_id]; + let id = SegmentId::generate(); + let modification_type = VectorModificationType::InsertSegment { id, points, handles: [None, None] }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + + if show_preview { + tool_data.preview_segment = Some(id); + } + } + + if show_preview { + tool_data.preview_point = Some(next_point_id); + } else { + tool_data.points.push((next_point_id, next_point_pos)); + } +} + +pub(super) fn delete_preview(tool_data: &mut SplineModeToolData, responses: &mut VecDeque) { + let Some(layer) = tool_data.current_layer else { return }; + + if let Some(id) = tool_data.preview_point { + let modification_type = VectorModificationType::RemovePoint { id }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + if let Some(id) = tool_data.preview_segment { + let modification_type = VectorModificationType::RemoveSegment { id }; + responses.add(GraphOperationMessage::Vector { layer, modification_type }); + } + + tool_data.preview_point = None; + tool_data.preview_segment = None; +} diff --git a/editor/src/messages/tool/tool_messages/spline_tool.rs b/editor/src/messages/tool/tool_messages/spline_tool.rs deleted file mode 100644 index da98c9ab65..0000000000 --- a/editor/src/messages/tool/tool_messages/spline_tool.rs +++ /dev/null @@ -1,536 +0,0 @@ -use super::tool_prelude::*; -use crate::consts::{DEFAULT_STROKE_WIDTH, DRAG_THRESHOLD, PATH_JOIN_THRESHOLD, SNAP_POINT_TOLERANCE}; -use crate::messages::input_mapper::utility_types::input_mouse::MouseKeys; -use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type; -use crate::messages::portfolio::document::overlays::utility_functions::path_endpoint_overlays; -use crate::messages::portfolio::document::overlays::utility_types::OverlayContext; -use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier; -use crate::messages::tool::common_functionality::auto_panning::AutoPanning; -use crate::messages::tool::common_functionality::color_selector::{ToolColorOptions, ToolColorType}; -use crate::messages::tool::common_functionality::graph_modification_utils::{self, find_spline, merge_layers, merge_points}; -use crate::messages::tool::common_functionality::snapping::{SnapCandidatePoint, SnapData, SnapManager, SnapTypeConfiguration, SnappedPoint}; -use crate::messages::tool::common_functionality::utility_functions::{closest_point, should_extend}; -use graph_craft::document::{NodeId, NodeInput}; -use graphene_core::Color; -use graphene_std::vector::{PointId, SegmentId, VectorModificationType}; - -#[derive(Default)] -pub struct SplineTool { - fsm_state: SplineToolFsmState, - tool_data: SplineToolData, - options: SplineOptions, -} - -pub struct SplineOptions { - line_weight: f64, - fill: ToolColorOptions, - stroke: ToolColorOptions, -} - -impl Default for SplineOptions { - fn default() -> Self { - Self { - line_weight: DEFAULT_STROKE_WIDTH, - fill: ToolColorOptions::new_none(), - stroke: ToolColorOptions::new_primary(), - } - } -} - -#[impl_message(Message, ToolMessage, Spline)] -#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] -pub enum SplineToolMessage { - // Standard messages - Overlays(OverlayContext), - CanvasTransformed, - Abort, - WorkingColorChanged, - - // Tool-specific messages - Confirm, - DragStart { append_to_selected: Key }, - DragStop, - MergeEndpoints, - PointerMove, - PointerOutsideViewport, - Undo, - UpdateOptions(SplineOptionsUpdate), -} - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -enum SplineToolFsmState { - #[default] - Ready, - Drawing, - MergingEndpoints, -} - -#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, specta::Type)] -pub enum SplineOptionsUpdate { - FillColor(Option), - FillColorType(ToolColorType), - LineWeight(f64), - StrokeColor(Option), - StrokeColorType(ToolColorType), - WorkingColors(Option, Option), -} - -impl ToolMetadata for SplineTool { - fn icon_name(&self) -> String { - "VectorSplineTool".into() - } - fn tooltip(&self) -> String { - "Spline Tool".into() - } - fn tool_type(&self) -> crate::messages::tool::utility_types::ToolType { - ToolType::Spline - } -} - -fn create_weight_widget(line_weight: f64) -> WidgetHolder { - NumberInput::new(Some(line_weight)) - .unit(" px") - .label("Weight") - .min(0.) - .max((1_u64 << f64::MANTISSA_DIGITS) as f64) - .on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into()) - .widget_holder() -} - -impl LayoutHolder for SplineTool { - fn layout(&self) -> Layout { - let mut widgets = self.options.fill.create_widgets( - "Fill", - true, - |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(None)).into(), - |color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColorType(color_type.clone())).into()), - |color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::FillColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(), - ); - - widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder()); - - widgets.append(&mut self.options.stroke.create_widgets( - "Stroke", - true, - |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(None)).into(), - |color_type: ToolColorType| WidgetCallback::new(move |_| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColorType(color_type.clone())).into()), - |color: &ColorInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::StrokeColor(color.value.as_solid().map(|color| color.to_linear_srgb()))).into(), - )); - widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder()); - widgets.push(create_weight_widget(self.options.line_weight)); - - Layout::WidgetLayout(WidgetLayout::new(vec![LayoutGroup::Row { widgets }])) - } -} - -impl<'a> MessageHandler> for SplineTool { - fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque, tool_data: &mut ToolActionHandlerData<'a>) { - let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message else { - self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &self.options, responses, true); - return; - }; - match action { - SplineOptionsUpdate::LineWeight(line_weight) => self.options.line_weight = line_weight, - SplineOptionsUpdate::FillColor(color) => { - self.options.fill.custom_color = color; - self.options.fill.color_type = ToolColorType::Custom; - } - SplineOptionsUpdate::FillColorType(color_type) => self.options.fill.color_type = color_type, - SplineOptionsUpdate::StrokeColor(color) => { - self.options.stroke.custom_color = color; - self.options.stroke.color_type = ToolColorType::Custom; - } - SplineOptionsUpdate::StrokeColorType(color_type) => self.options.stroke.color_type = color_type, - SplineOptionsUpdate::WorkingColors(primary, secondary) => { - self.options.stroke.primary_working_color = primary; - self.options.stroke.secondary_working_color = secondary; - self.options.fill.primary_working_color = primary; - self.options.fill.secondary_working_color = secondary; - } - } - - self.send_layout(responses, LayoutTarget::ToolOptions); - } - - fn actions(&self) -> ActionList { - match self.fsm_state { - SplineToolFsmState::Ready => actions!(SplineToolMessageDiscriminant; - Undo, - DragStart, - DragStop, - PointerMove, - Confirm, - Abort, - ), - SplineToolFsmState::Drawing => actions!(SplineToolMessageDiscriminant; - DragStop, - PointerMove, - Confirm, - Abort, - ), - SplineToolFsmState::MergingEndpoints => actions!(SplineToolMessageDiscriminant; - MergeEndpoints, - ), - } - } -} - -impl ToolTransition for SplineTool { - fn event_to_message_map(&self) -> EventToMessageMap { - EventToMessageMap { - overlay_provider: Some(|overlay_context: OverlayContext| SplineToolMessage::Overlays(overlay_context).into()), - canvas_transformed: Some(SplineToolMessage::CanvasTransformed.into()), - tool_abort: Some(SplineToolMessage::Abort.into()), - working_color_changed: Some(SplineToolMessage::WorkingColorChanged.into()), - ..Default::default() - } - } -} - -#[derive(Clone, Debug)] -enum EndpointPosition { - Start, - End, -} - -#[derive(Clone, Debug, Default)] -struct SplineToolData { - /// List of points inserted. - points: Vec<(PointId, DVec2)>, - /// Point to be inserted. - next_point: DVec2, - /// Point that was inserted temporarily to show preview. - preview_point: Option, - /// Segment that was inserted temporarily to show preview. - preview_segment: Option, - extend: bool, - weight: f64, - /// The layer we are editing. - current_layer: Option, - /// The layers to merge to the current layer before we merge endpoints in merge_endpoint field. - merge_layers: HashSet, - /// The endpoint IDs to merge with the spline's start/end endpoint after spline drawing is finished. - merge_endpoints: Vec<(EndpointPosition, PointId)>, - snap_manager: SnapManager, - auto_panning: AutoPanning, -} - -impl SplineToolData { - fn cleanup(&mut self) { - self.current_layer = None; - self.merge_layers = HashSet::new(); - self.merge_endpoints = Vec::new(); - self.preview_point = None; - self.preview_segment = None; - self.extend = false; - self.points = Vec::new(); - } - - /// Get the snapped point while ignoring current layer - fn snapped_point(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) -> SnappedPoint { - let metadata = document.metadata(); - let transform = self.current_layer.map_or(metadata.document_to_viewport, |layer| metadata.transform_to_viewport(layer)); - let point = SnapCandidatePoint::handle(transform.inverse().transform_point2(input.mouse.position)); - let ignore = if let Some(layer) = self.current_layer { vec![layer] } else { vec![] }; - let snap_data = SnapData::ignore(document, input, &ignore); - self.snap_manager.free_snap(&snap_data, &point, SnapTypeConfiguration::default()) - } -} - -impl Fsm for SplineToolFsmState { - type ToolData = SplineToolData; - type ToolOptions = SplineOptions; - - fn transition(self, event: ToolMessage, tool_data: &mut Self::ToolData, tool_action_data: &mut ToolActionHandlerData, tool_options: &Self::ToolOptions, responses: &mut VecDeque) -> Self { - let ToolActionHandlerData { - document, - global_tool_data, - input, - shape_editor, - preferences, - .. - } = tool_action_data; - - let ToolMessage::Spline(event) = event else { return self }; - match (self, event) { - (_, SplineToolMessage::CanvasTransformed) => self, - (_, SplineToolMessage::Overlays(mut overlay_context)) => { - path_endpoint_overlays(document, shape_editor, &mut overlay_context, preferences); - tool_data.snap_manager.draw_overlays(SnapData::new(document, input), &mut overlay_context); - self - } - (SplineToolFsmState::MergingEndpoints, SplineToolMessage::MergeEndpoints) => { - let Some(current_layer) = tool_data.current_layer else { return SplineToolFsmState::Ready }; - - if let Some(&layer) = tool_data.merge_layers.iter().last() { - merge_layers(document, current_layer, layer, responses); - tool_data.merge_layers.remove(&layer); - - responses.add(SplineToolMessage::MergeEndpoints); - return SplineToolFsmState::MergingEndpoints; - } - - let Some((start_endpoint, _)) = tool_data.points.first() else { return SplineToolFsmState::Ready }; - let Some((last_endpoint, _)) = tool_data.points.last() else { return SplineToolFsmState::Ready }; - - if let Some((position, second_endpoint)) = tool_data.merge_endpoints.pop() { - let first_endpoint = match position { - EndpointPosition::Start => *start_endpoint, - EndpointPosition::End => *last_endpoint, - }; - merge_points(document, current_layer, first_endpoint, second_endpoint, responses); - - responses.add(SplineToolMessage::MergeEndpoints); - return SplineToolFsmState::MergingEndpoints; - } - - responses.add(DocumentMessage::EndTransaction); - SplineToolFsmState::Ready - } - (SplineToolFsmState::Ready, SplineToolMessage::DragStart { append_to_selected }) => { - responses.add(DocumentMessage::StartTransaction); - - tool_data.snap_manager.cleanup(responses); - tool_data.cleanup(); - tool_data.weight = tool_options.line_weight; - - let point = SnapCandidatePoint::handle(document.metadata().document_to_viewport.inverse().transform_point2(input.mouse.position)); - let snapped = tool_data.snap_manager.free_snap(&SnapData::new(document, input), &point, SnapTypeConfiguration::default()); - let viewport = document.metadata().document_to_viewport.transform_point2(snapped.snapped_point_document); - - let layers = LayerNodeIdentifier::ROOT_PARENT - .descendants(document.metadata()) - .filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[])); - - // Extend an endpoint of the selected path - if let Some((layer, point, position)) = should_extend(document, viewport, SNAP_POINT_TOLERANCE, layers, preferences) { - if find_spline(document, layer).is_some() { - // If the point is the part of Spline then we extend it. - tool_data.current_layer = Some(layer); - tool_data.points.push((point, position)); - tool_data.next_point = position; - tool_data.extend = true; - - extend_spline(tool_data, true, responses); - - return SplineToolFsmState::Drawing; - } else { - tool_data.merge_layers.insert(layer); - tool_data.merge_endpoints.push((EndpointPosition::Start, point)); - } - } - - let selected_nodes = document.network_interface.selected_nodes(); - let mut selected_layers_except_artboards = selected_nodes.selected_layers_except_artboards(&document.network_interface); - let selected_layer = selected_layers_except_artboards.next().filter(|_| selected_layers_except_artboards.next().is_none()); - - let append_to_selected_layer = input.keyboard.key(append_to_selected); - - // Create new path in the selected layer when shift is down - if let (Some(layer), true) = (selected_layer, append_to_selected_layer) { - tool_data.current_layer = Some(layer); - - let transform = document.metadata().transform_to_viewport(layer); - let position = transform.inverse().transform_point2(input.mouse.position); - tool_data.next_point = position; - - return SplineToolFsmState::Drawing; - } - - responses.add(DocumentMessage::DeselectAllLayers); - - let parent = document.new_layer_bounding_artboard(input); - - let path_node_type = resolve_document_node_type("Path").expect("Path node does not exist"); - let path_node = path_node_type.default_node_template(); - let spline_node_type = resolve_document_node_type("Spline").expect("Spline node does not exist"); - let spline_node = spline_node_type.node_template_input_override([Some(NodeInput::node(NodeId(1), 0))]); - let nodes = vec![(NodeId(1), path_node), (NodeId(0), spline_node)]; - - let layer = graph_modification_utils::new_custom(NodeId::new(), nodes, parent, responses); - tool_options.fill.apply_fill(layer, responses); - tool_options.stroke.apply_stroke(tool_data.weight, layer, responses); - tool_data.current_layer = Some(layer); - - responses.add(Message::StartBuffer); - - SplineToolFsmState::Drawing - } - (SplineToolFsmState::Drawing, SplineToolMessage::DragStop) => { - // The first DragStop event will be ignored to prevent insertion of new point. - if tool_data.extend { - tool_data.extend = false; - return SplineToolFsmState::Drawing; - } - if tool_data.current_layer.is_none() { - return SplineToolFsmState::Ready; - }; - tool_data.next_point = tool_data.snapped_point(document, input).snapped_point_document; - if tool_data.points.last().is_none_or(|last_pos| last_pos.1.distance(tool_data.next_point) > DRAG_THRESHOLD) { - let preview_point = tool_data.preview_point; - extend_spline(tool_data, false, responses); - tool_data.preview_point = preview_point; - - if try_merging_lastest_endpoint(document, tool_data, preferences).is_some() { - responses.add(SplineToolMessage::Confirm); - } - } - - SplineToolFsmState::Drawing - } - (SplineToolFsmState::Drawing, SplineToolMessage::PointerMove) => { - let Some(layer) = tool_data.current_layer else { return SplineToolFsmState::Ready }; - let ignore = |cp: PointId| tool_data.preview_point.is_some_and(|pp| pp == cp) || tool_data.points.last().is_some_and(|(ep, _)| *ep == cp); - let join_point = closest_point(document, input.mouse.position, PATH_JOIN_THRESHOLD, vec![layer].into_iter(), ignore, preferences); - - // Endpoints snapping - if let Some((_, _, point)) = join_point { - tool_data.next_point = point; - tool_data.snap_manager.clear_indicator(); - } else { - let snapped_point = tool_data.snapped_point(document, input); - tool_data.next_point = snapped_point.snapped_point_document; - tool_data.snap_manager.update_indicator(snapped_point); - } - - extend_spline(tool_data, true, responses); - - // Auto-panning - let messages = [SplineToolMessage::PointerOutsideViewport.into(), SplineToolMessage::PointerMove.into()]; - tool_data.auto_panning.setup_by_mouse_position(input, &messages, responses); - - SplineToolFsmState::Drawing - } - (_, SplineToolMessage::PointerMove) => { - tool_data.snap_manager.preview_draw(&SnapData::new(document, input), input.mouse.position); - responses.add(OverlaysMessage::Draw); - self - } - (SplineToolFsmState::Drawing, SplineToolMessage::PointerOutsideViewport) => { - if !input.mouse.mouse_keys.contains(MouseKeys::LEFT) { - return self; - } - // Auto-panning - let _ = tool_data.auto_panning.shift_viewport(input, responses); - - SplineToolFsmState::Drawing - } - (state, SplineToolMessage::PointerOutsideViewport) => { - // Auto-panning - let messages = [SplineToolMessage::PointerOutsideViewport.into(), SplineToolMessage::PointerMove.into()]; - tool_data.auto_panning.stop(&messages, responses); - - state - } - (SplineToolFsmState::Drawing, SplineToolMessage::Confirm) => { - if tool_data.points.len() >= 2 { - delete_preview(tool_data, responses); - } - responses.add(SplineToolMessage::MergeEndpoints); - SplineToolFsmState::MergingEndpoints - } - (SplineToolFsmState::Drawing, SplineToolMessage::Abort) => { - responses.add(DocumentMessage::AbortTransaction); - SplineToolFsmState::Ready - } - (_, SplineToolMessage::WorkingColorChanged) => { - responses.add(SplineToolMessage::UpdateOptions(SplineOptionsUpdate::WorkingColors( - Some(global_tool_data.primary_color), - Some(global_tool_data.secondary_color), - ))); - self - } - _ => self, - } - } - - fn update_hints(&self, responses: &mut VecDeque) { - let hint_data = match self { - SplineToolFsmState::Ready => HintData(vec![HintGroup(vec![ - HintInfo::mouse(MouseMotion::Lmb, "Draw Spline"), - HintInfo::keys([Key::Shift], "Append to Selected Layer").prepend_plus(), - ])]), - SplineToolFsmState::Drawing => HintData(vec![ - HintGroup(vec![HintInfo::mouse(MouseMotion::Rmb, ""), HintInfo::keys([Key::Escape], "Cancel").prepend_slash()]), - HintGroup(vec![HintInfo::mouse(MouseMotion::Lmb, "Extend Spline")]), - HintGroup(vec![HintInfo::keys([Key::Enter], "End Spline")]), - ]), - SplineToolFsmState::MergingEndpoints => HintData(vec![]), - }; - - responses.add(FrontendMessage::UpdateInputHints { hint_data }); - } - - fn update_cursor(&self, responses: &mut VecDeque) { - responses.add(FrontendMessage::UpdateMouseCursor { cursor: MouseCursorIcon::Default }); - } -} - -fn try_merging_lastest_endpoint(document: &DocumentMessageHandler, tool_data: &mut SplineToolData, preferences: &PreferencesMessageHandler) -> Option<()> { - if tool_data.points.len() < 2 { - return None; - }; - let (last_endpoint, last_endpoint_position) = tool_data.points.last()?; - let preview_point = tool_data.preview_point; - let current_layer = tool_data.current_layer?; - - let layers = LayerNodeIdentifier::ROOT_PARENT - .descendants(document.metadata()) - .filter(|layer| !document.network_interface.is_artboard(&layer.to_node(), &[])); - - let exclude = |p: PointId| preview_point.is_some_and(|pp| pp == p) || *last_endpoint == p; - let position = document.metadata().transform_to_viewport(current_layer).transform_point2(*last_endpoint_position); - - let (layer, endpoint, _) = closest_point(document, position, PATH_JOIN_THRESHOLD, layers, exclude, preferences)?; - tool_data.merge_layers.insert(layer); - tool_data.merge_endpoints.push((EndpointPosition::End, endpoint)); - - Some(()) -} - -fn extend_spline(tool_data: &mut SplineToolData, show_preview: bool, responses: &mut VecDeque) { - delete_preview(tool_data, responses); - - let Some(layer) = tool_data.current_layer else { return }; - - let next_point_pos = tool_data.next_point; - let next_point_id = PointId::generate(); - let modification_type = VectorModificationType::InsertPoint { - id: next_point_id, - position: next_point_pos, - }; - responses.add(GraphOperationMessage::Vector { layer, modification_type }); - - if let Some((last_point_id, _)) = tool_data.points.last() { - let points = [*last_point_id, next_point_id]; - let id = SegmentId::generate(); - let modification_type = VectorModificationType::InsertSegment { id, points, handles: [None, None] }; - responses.add(GraphOperationMessage::Vector { layer, modification_type }); - - if show_preview { - tool_data.preview_segment = Some(id); - } - } - - if show_preview { - tool_data.preview_point = Some(next_point_id); - } else { - tool_data.points.push((next_point_id, next_point_pos)); - } -} - -fn delete_preview(tool_data: &mut SplineToolData, responses: &mut VecDeque) { - let Some(layer) = tool_data.current_layer else { return }; - - if let Some(id) = tool_data.preview_point { - let modification_type = VectorModificationType::RemovePoint { id }; - responses.add(GraphOperationMessage::Vector { layer, modification_type }); - } - if let Some(id) = tool_data.preview_segment { - let modification_type = VectorModificationType::RemoveSegment { id }; - responses.add(GraphOperationMessage::Vector { layer, modification_type }); - } - - tool_data.preview_point = None; - tool_data.preview_segment = None; -} diff --git a/editor/src/messages/tool/utility_types.rs b/editor/src/messages/tool/utility_types.rs index 2fca871cdc..31eadd368e 100644 --- a/editor/src/messages/tool/utility_types.rs +++ b/editor/src/messages/tool/utility_types.rs @@ -366,7 +366,6 @@ fn list_tools_in_groups() -> Vec> { ToolAvailability::Available(Box::::default()), ToolAvailability::Available(Box::::default()), ToolAvailability::Available(Box::::default()), - ToolAvailability::Available(Box::::default()), ToolAvailability::Available(Box::::default()), ToolAvailability::Available(Box::::default()), ToolAvailability::Available(Box::::default()), @@ -402,7 +401,6 @@ pub fn tool_message_to_tool_type(tool_message: &ToolMessage) -> ToolType { ToolMessage::Path(_) => ToolType::Path, ToolMessage::Pen(_) => ToolType::Pen, ToolMessage::Freehand(_) => ToolType::Freehand, - ToolMessage::Spline(_) => ToolType::Spline, ToolMessage::Line(_) => ToolType::Line, ToolMessage::Rectangle(_) => ToolType::Rectangle, ToolMessage::Ellipse(_) => ToolType::Ellipse, diff --git a/frontend/assets/icon-24px-two-tone/vector-spline-tool.svg b/frontend/assets/icon-24px-two-tone/vector-spline-tool.svg deleted file mode 100644 index 359fd35e59..0000000000 --- a/frontend/assets/icon-24px-two-tone/vector-spline-tool.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/frontend/src/utility-functions/icons.ts b/frontend/src/utility-functions/icons.ts index ba0592cc64..55eac2a460 100644 --- a/frontend/src/utility-functions/icons.ts +++ b/frontend/src/utility-functions/icons.ts @@ -381,7 +381,6 @@ import VectorPathTool from "@graphite-frontend/assets/icon-24px-two-tone/vector- import VectorPenTool from "@graphite-frontend/assets/icon-24px-two-tone/vector-pen-tool.svg"; import VectorPolygonTool from "@graphite-frontend/assets/icon-24px-two-tone/vector-polygon-tool.svg"; import VectorRectangleTool from "@graphite-frontend/assets/icon-24px-two-tone/vector-rectangle-tool.svg"; -import VectorSplineTool from "@graphite-frontend/assets/icon-24px-two-tone/vector-spline-tool.svg"; import VectorTextTool from "@graphite-frontend/assets/icon-24px-two-tone/vector-text-tool.svg"; const TWO_TONE_24PX = { @@ -405,7 +404,6 @@ const TWO_TONE_24PX = { VectorPenTool: { svg: VectorPenTool, size: 24 }, VectorRectangleTool: { svg: VectorRectangleTool, size: 24 }, VectorPolygonTool: { svg: VectorPolygonTool, size: 24 }, - VectorSplineTool: { svg: VectorSplineTool, size: 24 }, VectorTextTool: { svg: VectorTextTool, size: 24 }, } as const;