mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-08-04 05:18:19 +00:00
Merge branch 'master' into fix-range
This commit is contained in:
commit
a3a85c86ec
65 changed files with 1367 additions and 285 deletions
67
.github/workflows/build-dev-and-ci.yml
vendored
67
.github/workflows/build-dev-and-ci.yml
vendored
|
@ -4,7 +4,7 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
pull_request: {}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
INDEX_HTML_HEAD_REPLACEMENT: <script defer data-domain="dev.graphite.rs" data-api="https://graphite.rs/visit/event" src="https://graphite.rs/visit/script.hash.js"></script>
|
||||
|
@ -13,9 +13,10 @@ jobs:
|
|||
build:
|
||||
runs-on: self-hosted
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
deployments: write
|
||||
pull-requests: write
|
||||
actions: write
|
||||
env:
|
||||
RUSTC_WRAPPER: /usr/bin/sccache
|
||||
CARGO_INCREMENTAL: 0
|
||||
|
@ -47,9 +48,11 @@ jobs:
|
|||
rustc --version
|
||||
|
||||
- name: ✂ Replace template in <head> of index.html
|
||||
if: github.ref != 'refs/heads/master'
|
||||
env:
|
||||
INDEX_HTML_HEAD_REPLACEMENT: ""
|
||||
run: |
|
||||
# Remove the INDEX_HTML_HEAD_REPLACEMENT environment variable for build links (not master deploys)
|
||||
git rev-parse --abbrev-ref HEAD | grep master > /dev/null || export INDEX_HTML_HEAD_REPLACEMENT=""
|
||||
sed -i "s|<!-- INDEX_HTML_HEAD_REPLACEMENT -->|$INDEX_HTML_HEAD_REPLACEMENT|" frontend/index.html
|
||||
|
||||
- name: 🌐 Build Graphite web code
|
||||
|
@ -70,6 +73,19 @@ jobs:
|
|||
projectName: graphite-dev
|
||||
directory: frontend/dist
|
||||
|
||||
- name: 💬 Comment build link URL to commit hash page on GitHub
|
||||
if: github.ref == 'refs/heads/master'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh api \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
/repos/${{ github.repository }}/commits/$(git rev-parse HEAD)/comments \
|
||||
-f body="| 📦 **Build Complete for** $(git rev-parse HEAD) |
|
||||
|-|
|
||||
| ${{ steps.cloudflare.outputs.url }} |"
|
||||
|
||||
- name: 👕 Lint Graphite web formatting
|
||||
env:
|
||||
NODE_ENV: production
|
||||
|
@ -91,6 +107,51 @@ jobs:
|
|||
run: |
|
||||
mold -run cargo test --all-features --workspace
|
||||
|
||||
- name: 📃 Generate code documentation info for website
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree
|
||||
mkdir -p artifacts-generated
|
||||
mv hierarchical_message_system_tree.txt artifacts-generated/hierarchical_message_system_tree.txt
|
||||
|
||||
- name: 💿 Obtain cache of auto-generated code docs artifacts, to check if they've changed
|
||||
if: github.ref == 'refs/heads/master'
|
||||
id: cache-website-code-docs
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: artifacts
|
||||
key: website-code-docs
|
||||
|
||||
- name: 🔍 Check if auto-generated code docs artifacts changed
|
||||
if: github.ref == 'refs/heads/master'
|
||||
id: website-code-docs-changed
|
||||
run: |
|
||||
if ! diff --brief --recursive artifacts-generated artifacts; then
|
||||
echo "Auto-generated code docs artifacts have changed."
|
||||
rm -rf artifacts
|
||||
mv artifacts-generated artifacts
|
||||
echo "changed=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "Auto-generated code docs artifacts have not changed."
|
||||
rm -rf artifacts
|
||||
rm -rf artifacts-generated
|
||||
fi
|
||||
|
||||
- name: 💾 Save cache of auto-generated code docs artifacts
|
||||
if: steps.website-code-docs-changed.outputs.changed == 'true'
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: artifacts
|
||||
key: ${{ steps.cache-website-code-docs.outputs.cache-primary-key }}
|
||||
|
||||
- name: ♻️ Trigger website rebuild if the auto-generated code docs artifacts have changed
|
||||
if: steps.website-code-docs-changed.outputs.changed == 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
rm -rf artifacts
|
||||
gh workflow run website.yml --ref master
|
||||
|
||||
# miri:
|
||||
# runs-on: self-hosted
|
||||
|
||||
|
|
|
@ -73,9 +73,10 @@ jobs:
|
|||
rustc --version
|
||||
|
||||
- name: ✂ Replace template in <head> of index.html
|
||||
env:
|
||||
INDEX_HTML_HEAD_REPLACEMENT: ""
|
||||
run: |
|
||||
# Remove the INDEX_HTML_HEAD_REPLACEMENT environment variable for build links (not master deploys)
|
||||
export INDEX_HTML_HEAD_REPLACEMENT=""
|
||||
sed -i "s|<!-- INDEX_HTML_HEAD_REPLACEMENT -->|$INDEX_HTML_HEAD_REPLACEMENT|" frontend/index.html
|
||||
|
||||
- name: ⌨ Set build command based on comment
|
||||
|
|
43
.github/workflows/website.yml
vendored
43
.github/workflows/website.yml
vendored
|
@ -9,6 +9,7 @@ on:
|
|||
pull_request:
|
||||
paths:
|
||||
- website/**
|
||||
workflow_dispatch: {}
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
INDEX_HTML_HEAD_INCLUSION: <script defer data-domain="graphite.rs" data-api="/visit/event" src="/visit/script.hash.js"></script>
|
||||
|
@ -30,6 +31,14 @@ jobs:
|
|||
with:
|
||||
tool: zola@0.20.0
|
||||
|
||||
- name: 🔍 Check if `website/other` directory changed
|
||||
uses: dorny/paths-filter@v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
website-other:
|
||||
- "website/other/**"
|
||||
|
||||
- name: ✂ Replace template in <head> of index.html
|
||||
run: |
|
||||
# Remove the INDEX_HTML_HEAD_INCLUSION environment variable for build links (not master deploys)
|
||||
|
@ -43,16 +52,8 @@ jobs:
|
|||
npm run install-fonts
|
||||
zola --config config.toml build --minify
|
||||
|
||||
- name: 🔍 Check if `website/other` directory changed
|
||||
uses: dorny/paths-filter@v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
other:
|
||||
- "website/other/**"
|
||||
|
||||
- name: 💿 Restore cache of `website/other/dist` directory, if available and `website/other` didn't change
|
||||
if: steps.changes.outputs.other != 'true'
|
||||
if: steps.changes.outputs.website-other != 'true'
|
||||
id: cache-website-other-dist
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
|
@ -80,8 +81,32 @@ jobs:
|
|||
|
||||
- name: 🚚 Move `website/other/dist` contents to `website/public`
|
||||
run: |
|
||||
mkdir -p website/public
|
||||
mv website/other/dist/* website/public
|
||||
|
||||
- name: 💿 Obtain cache of auto-generated code docs artifacts
|
||||
id: cache-website-code-docs
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: artifacts
|
||||
key: website-code-docs
|
||||
|
||||
- name: 📁 Fallback in case auto-generated code docs artifacts weren't cached
|
||||
if: steps.cache-website-code-docs.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
echo "🦀 Initial system version of Rust:"
|
||||
rustc --version
|
||||
rustup update stable
|
||||
echo "🦀 Latest updated version of Rust:"
|
||||
rustc --version
|
||||
cargo test --package graphite-editor --lib -- messages::message::test::generate_message_tree
|
||||
mkdir artifacts
|
||||
mv hierarchical_message_system_tree.txt artifacts/hierarchical_message_system_tree.txt
|
||||
|
||||
- name: 🚚 Move `artifacts` contents to `website/public`
|
||||
run: |
|
||||
mv artifacts/* website/public
|
||||
|
||||
- name: 📤 Publish to Cloudflare Pages
|
||||
id: cloudflare
|
||||
uses: cloudflare/pages-action@1
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -6,3 +6,4 @@ profile.json
|
|||
flamegraph.svg
|
||||
.idea/
|
||||
.direnv
|
||||
hierarchical_message_system_tree.txt
|
||||
|
|
|
@ -15,3 +15,4 @@ pub mod node_graph_executor;
|
|||
#[cfg(test)]
|
||||
pub mod test_utils;
|
||||
pub mod utility_traits;
|
||||
pub mod utility_types;
|
||||
|
|
|
@ -24,7 +24,7 @@ enum AnimationState {
|
|||
},
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq)]
|
||||
#[derive(Default, Debug, Clone, PartialEq, ExtractField)]
|
||||
pub struct AnimationMessageHandler {
|
||||
/// Used to re-send the UI on the next frame after playback starts
|
||||
live_preview_recently_zero: bool,
|
||||
|
@ -57,6 +57,7 @@ impl AnimationMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<AnimationMessage, ()> for AnimationMessageHandler {
|
||||
fn process_message(&mut self, message: AnimationMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct BroadcastMessageHandler {
|
||||
listeners: HashMap<BroadcastEvent, Vec<Message>>,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<BroadcastMessage, ()> for BroadcastMessageHandler {
|
||||
fn process_message(&mut self, message: BroadcastMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use super::utility_types::MessageLoggingVerbosity;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
pub struct DebugMessageHandler {
|
||||
pub message_logging_verbosity: MessageLoggingVerbosity,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<DebugMessage, ()> for DebugMessageHandler {
|
||||
fn process_message(&mut self, message: DebugMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -2,19 +2,21 @@ use super::simple_dialogs::{self, AboutGraphiteDialog, ComingSoonDialog, DemoArt
|
|||
use crate::messages::layout::utility_types::widget_prelude::*;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct DialogMessageData<'a> {
|
||||
pub portfolio: &'a PortfolioMessageHandler,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
}
|
||||
|
||||
/// Stores the dialogs which require state. These are the ones that have their own message handlers, and are not the ones defined in `simple_dialogs`.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
#[derive(Debug, Default, Clone, ExtractField)]
|
||||
pub struct DialogMessageHandler {
|
||||
export_dialog: ExportDialogMessageHandler,
|
||||
new_document_dialog: NewDocumentDialogMessageHandler,
|
||||
preferences_dialog: PreferencesDialogMessageHandler,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<DialogMessage, DialogMessageData<'_>> for DialogMessageHandler {
|
||||
fn process_message(&mut self, message: DialogMessage, responses: &mut VecDeque<Message>, data: DialogMessageData) {
|
||||
let DialogMessageData { portfolio, preferences } = data;
|
||||
|
|
|
@ -3,12 +3,13 @@ use crate::messages::layout::utility_types::widget_prelude::*;
|
|||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct ExportDialogMessageData<'a> {
|
||||
pub portfolio: &'a PortfolioMessageHandler,
|
||||
}
|
||||
|
||||
/// A dialog to allow users to customize their file export.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, ExtractField)]
|
||||
pub struct ExportDialogMessageHandler {
|
||||
pub file_type: FileType,
|
||||
pub scale_factor: f64,
|
||||
|
@ -31,6 +32,7 @@ impl Default for ExportDialogMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<ExportDialogMessage, ExportDialogMessageData<'_>> for ExportDialogMessageHandler {
|
||||
fn process_message(&mut self, message: ExportDialogMessage, responses: &mut VecDeque<Message>, data: ExportDialogMessageData) {
|
||||
let ExportDialogMessageData { portfolio } = data;
|
||||
|
|
|
@ -4,13 +4,14 @@ use glam::{IVec2, UVec2};
|
|||
use graph_craft::document::NodeId;
|
||||
|
||||
/// A dialog to allow users to set some initial options about a new document.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct NewDocumentDialogMessageHandler {
|
||||
pub name: String,
|
||||
pub infinite: bool,
|
||||
pub dimensions: UVec2,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<NewDocumentDialogMessage, ()> for NewDocumentDialogMessageHandler {
|
||||
fn process_message(&mut self, message: NewDocumentDialogMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -4,14 +4,16 @@ use crate::messages::portfolio::document::utility_types::wires::GraphWireStyle;
|
|||
use crate::messages::preferences::SelectionMode;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct PreferencesDialogMessageData<'a> {
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
}
|
||||
|
||||
/// A dialog to allow users to customize Graphite editor options
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct PreferencesDialogMessageHandler {}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<PreferencesDialogMessage, PreferencesDialogMessageData<'_>> for PreferencesDialogMessageHandler {
|
||||
fn process_message(&mut self, message: PreferencesDialogMessage, responses: &mut VecDeque<Message>, data: PreferencesDialogMessageData) {
|
||||
let PreferencesDialogMessageData { preferences } = data;
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
pub struct GlobalsMessageHandler {}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<GlobalsMessage, ()> for GlobalsMessageHandler {
|
||||
fn process_message(&mut self, message: GlobalsMessage, _responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -6,16 +6,18 @@ use crate::messages::portfolio::utility_types::KeyboardPlatformLayout;
|
|||
use crate::messages::prelude::*;
|
||||
use std::fmt::Write;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct InputMapperMessageData<'a> {
|
||||
pub input: &'a InputPreprocessorMessageHandler,
|
||||
pub actions: ActionList,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
pub struct InputMapperMessageHandler {
|
||||
mapping: Mapping,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<InputMapperMessage, InputMapperMessageData<'_>> for InputMapperMessageHandler {
|
||||
fn process_message(&mut self, message: InputMapperMessage, responses: &mut VecDeque<Message>, data: InputMapperMessageData) {
|
||||
let InputMapperMessageData { input, actions } = data;
|
||||
|
|
|
@ -2,16 +2,18 @@ use crate::messages::input_mapper::input_mapper_message_handler::InputMapperMess
|
|||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct KeyMappingMessageData<'a> {
|
||||
pub input: &'a InputPreprocessorMessageHandler,
|
||||
pub actions: ActionList,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
pub struct KeyMappingMessageHandler {
|
||||
mapping_handler: InputMapperMessageHandler,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<KeyMappingMessage, KeyMappingMessageData<'_>> for KeyMappingMessageHandler {
|
||||
fn process_message(&mut self, message: KeyMappingMessage, responses: &mut VecDeque<Message>, data: KeyMappingMessageData) {
|
||||
let KeyMappingMessageData { input, actions } = data;
|
||||
|
|
|
@ -6,11 +6,12 @@ use crate::messages::prelude::*;
|
|||
use glam::DVec2;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct InputPreprocessorMessageData {
|
||||
pub keyboard_platform: KeyboardPlatformLayout,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
pub struct InputPreprocessorMessageHandler {
|
||||
pub frame_time: FrameTimeInfo,
|
||||
pub time: u64,
|
||||
|
@ -19,6 +20,7 @@ pub struct InputPreprocessorMessageHandler {
|
|||
pub viewport_bounds: ViewportBounds,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<InputPreprocessorMessage, InputPreprocessorMessageData> for InputPreprocessorMessageHandler {
|
||||
fn process_message(&mut self, message: InputPreprocessorMessage, responses: &mut VecDeque<Message>, data: InputPreprocessorMessageData) {
|
||||
let InputPreprocessorMessageData { keyboard_platform } = data;
|
||||
|
|
|
@ -6,7 +6,7 @@ use graphene_std::text::Font;
|
|||
use graphene_std::vector::style::{FillChoice, GradientStops};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct LayoutMessageHandler {
|
||||
layouts: [Layout; LayoutTarget::LayoutTargetLength as usize],
|
||||
}
|
||||
|
@ -342,6 +342,15 @@ impl LayoutMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn custom_data() -> MessageData {
|
||||
// TODO: When <https://github.com/dtolnay/proc-macro2/issues/503> is resolved and released,
|
||||
// TODO: use <https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.line> to get
|
||||
// TODO: the line number instead of hardcoding it to the magic number on the following line.
|
||||
// TODO: Also, utilize the line number in the actual output, since it is currently unused.
|
||||
MessageData::new(String::from("Function"), vec![(String::from("Fn(&MessageDiscriminant) -> Option<KeysGroup>"), 350)], file!())
|
||||
}
|
||||
|
||||
#[message_handler_data(CustomData)]
|
||||
impl<F: Fn(&MessageDiscriminant) -> Option<KeysGroup>> MessageHandler<LayoutMessage, F> for LayoutMessageHandler {
|
||||
fn process_message(&mut self, message: LayoutMessage, responses: &mut std::collections::VecDeque<Message>, action_input_mapping: F) {
|
||||
match message {
|
||||
|
|
|
@ -45,3 +45,86 @@ impl specta::Type for MessageDiscriminant {
|
|||
specta::DataType::Any
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::io::Write;
|
||||
|
||||
#[test]
|
||||
fn generate_message_tree() {
|
||||
let result = Message::build_message_tree();
|
||||
let mut file = std::fs::File::create("../hierarchical_message_system_tree.txt").unwrap();
|
||||
file.write_all(format!("{} `{}`\n", result.name(), result.path()).as_bytes()).unwrap();
|
||||
if let Some(variants) = result.variants() {
|
||||
for (i, variant) in variants.iter().enumerate() {
|
||||
let is_last = i == variants.len() - 1;
|
||||
print_tree_node(variant, "", is_last, &mut file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_tree_node(tree: &DebugMessageTree, prefix: &str, is_last: bool, file: &mut std::fs::File) {
|
||||
// Print the current node
|
||||
let (branch, child_prefix) = if tree.has_message_handler_data_fields() || tree.has_message_handler_fields() {
|
||||
("├── ", format!("{}│ ", prefix))
|
||||
} else {
|
||||
if is_last {
|
||||
("└── ", format!("{} ", prefix))
|
||||
} else {
|
||||
("├── ", format!("{}│ ", prefix))
|
||||
}
|
||||
};
|
||||
|
||||
if tree.path().is_empty() {
|
||||
file.write_all(format!("{}{}{}\n", prefix, branch, tree.name()).as_bytes()).unwrap();
|
||||
} else {
|
||||
file.write_all(format!("{}{}{} `{}`\n", prefix, branch, tree.name(), tree.path()).as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
// Print children if any
|
||||
if let Some(variants) = tree.variants() {
|
||||
let len = variants.len();
|
||||
for (i, variant) in variants.iter().enumerate() {
|
||||
let is_last_child = i == len - 1;
|
||||
print_tree_node(variant, &child_prefix, is_last_child, file);
|
||||
}
|
||||
}
|
||||
|
||||
// Print handler field if any
|
||||
if let Some(data) = tree.message_handler_fields() {
|
||||
let len = data.fields().len();
|
||||
let (branch, child_prefix) = if tree.has_message_handler_data_fields() {
|
||||
("├── ", format!("{}│ ", prefix))
|
||||
} else {
|
||||
("└── ", format!("{} ", prefix))
|
||||
};
|
||||
if data.path().is_empty() {
|
||||
file.write_all(format!("{}{}{}\n", prefix, branch, data.name()).as_bytes()).unwrap();
|
||||
} else {
|
||||
file.write_all(format!("{}{}{} `{}`\n", prefix, branch, data.name(), data.path()).as_bytes()).unwrap();
|
||||
}
|
||||
for (i, field) in data.fields().iter().enumerate() {
|
||||
let is_last_field = i == len - 1;
|
||||
let branch = if is_last_field { "└── " } else { "├── " };
|
||||
|
||||
file.write_all(format!("{}{}{}\n", child_prefix, branch, field.0).as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Print data field if any
|
||||
if let Some(data) = tree.message_handler_data_fields() {
|
||||
let len = data.fields().len();
|
||||
if data.path().is_empty() {
|
||||
file.write_all(format!("{}{}{}\n", prefix, "└── ", data.name()).as_bytes()).unwrap();
|
||||
} else {
|
||||
file.write_all(format!("{}{}{} `{}`\n", prefix, "└── ", data.name(), data.path()).as_bytes()).unwrap();
|
||||
}
|
||||
for (i, field) in data.fields().iter().enumerate() {
|
||||
let is_last_field = i == len - 1;
|
||||
let branch = if is_last_field { "└── " } else { "├── " };
|
||||
file.write_all(format!("{}{}{}\n", format!("{} ", prefix), branch, field.0).as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ use graphene_std::vector::click_target::{ClickTarget, ClickTargetType};
|
|||
use graphene_std::vector::style::ViewMode;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct DocumentMessageData<'a> {
|
||||
pub document_id: DocumentId,
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
|
@ -48,7 +49,7 @@ pub struct DocumentMessageData<'a> {
|
|||
pub device_pixel_ratio: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, ExtractField)]
|
||||
#[serde(default)]
|
||||
pub struct DocumentMessageHandler {
|
||||
// ======================
|
||||
|
@ -168,6 +169,7 @@ impl Default for DocumentMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<DocumentMessage, DocumentMessageData<'_>> for DocumentMessageHandler {
|
||||
fn process_message(&mut self, message: DocumentMessage, responses: &mut VecDeque<Message>, data: DocumentMessageData) {
|
||||
let DocumentMessageData {
|
||||
|
|
|
@ -21,17 +21,19 @@ struct ArtboardInfo {
|
|||
merge_node: NodeId,
|
||||
}
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct GraphOperationMessageData<'a> {
|
||||
pub network_interface: &'a mut NodeNetworkInterface,
|
||||
pub collapsed: &'a mut CollapsedLayers,
|
||||
pub node_graph: &'a mut NodeGraphMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize)]
|
||||
#[derive(Debug, Clone, PartialEq, Default, serde::Serialize, serde::Deserialize, ExtractField)]
|
||||
pub struct GraphOperationMessageHandler {}
|
||||
|
||||
// GraphOperationMessageHandler always modified the document network. This is so changes to the layers panel will only affect the document network.
|
||||
// For changes to the selected network, use NodeGraphMessageHandler. No NodeGraphMessage's should be added here, since they will affect the selected nested network.
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<GraphOperationMessage, GraphOperationMessageData<'_>> for GraphOperationMessageHandler {
|
||||
fn process_message(&mut self, message: GraphOperationMessage, responses: &mut VecDeque<Message>, data: GraphOperationMessageData) {
|
||||
let network_interface = data.network_interface;
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
|||
use glam::{DAffine2, DVec2};
|
||||
use graph_craft::document::NodeId;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct NavigationMessageData<'a> {
|
||||
pub network_interface: &'a mut NodeNetworkInterface,
|
||||
pub breadcrumb_network_path: &'a [NodeId],
|
||||
|
@ -23,7 +24,7 @@ pub struct NavigationMessageData<'a> {
|
|||
pub preferences: &'a PreferencesMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
#[derive(Debug, Clone, PartialEq, Default, ExtractField)]
|
||||
pub struct NavigationMessageHandler {
|
||||
navigation_operation: NavigationOperation,
|
||||
mouse_position: ViewportPosition,
|
||||
|
@ -31,6 +32,7 @@ pub struct NavigationMessageHandler {
|
|||
abortable_pan_start: Option<f64>,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<NavigationMessage, NavigationMessageData<'_>> for NavigationMessageHandler {
|
||||
fn process_message(&mut self, message: NavigationMessage, responses: &mut VecDeque<Message>, data: NavigationMessageData) {
|
||||
let NavigationMessageData {
|
||||
|
|
|
@ -946,7 +946,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
|
|||
inputs: vec![
|
||||
NodeInput::value(TaggedValue::RasterData(RasterDataTable::default()), true),
|
||||
NodeInput::value(TaggedValue::BrushStrokes(Vec::new()), false),
|
||||
NodeInput::value(TaggedValue::BrushCache(BrushCache::new_proto()), false),
|
||||
NodeInput::value(TaggedValue::BrushCache(BrushCache::default()), false),
|
||||
],
|
||||
..Default::default()
|
||||
},
|
||||
|
|
|
@ -27,7 +27,7 @@ use graphene_std::*;
|
|||
use renderer::Quad;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, ExtractField)]
|
||||
pub struct NodeGraphHandlerData<'a> {
|
||||
pub network_interface: &'a mut NodeNetworkInterface,
|
||||
pub selection_network_path: &'a [NodeId],
|
||||
|
@ -41,7 +41,7 @@ pub struct NodeGraphHandlerData<'a> {
|
|||
pub preferences: &'a PreferencesMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, ExtractField)]
|
||||
pub struct NodeGraphMessageHandler {
|
||||
// TODO: Remove network and move to NodeNetworkInterface
|
||||
pub network: Vec<NodeId>,
|
||||
|
@ -92,6 +92,7 @@ pub struct NodeGraphMessageHandler {
|
|||
}
|
||||
|
||||
/// NodeGraphMessageHandler always modifies the network which the selected nodes are in. No GraphOperationMessages should be added here, since those messages will always affect the document network.
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGraphMessageHandler {
|
||||
fn process_message(&mut self, message: NodeGraphMessage, responses: &mut VecDeque<Message>, data: NodeGraphHandlerData<'a>) {
|
||||
let NodeGraphHandlerData {
|
||||
|
|
|
@ -1280,6 +1280,7 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||
|
||||
let mut corner_radius_row_2 = vec![Separator::new(SeparatorType::Unrelated).widget_holder()];
|
||||
corner_radius_row_2.push(TextLabel::new("").widget_holder());
|
||||
add_blank_assist(&mut corner_radius_row_2);
|
||||
|
||||
let document_node = match get_document_node(node_id, context) {
|
||||
Ok(document_node) => document_node,
|
||||
|
@ -1387,8 +1388,6 @@ pub(crate) fn rectangle_properties(node_id: NodeId, context: &mut NodeProperties
|
|||
// Size Y
|
||||
let size_y = number_widget(ParameterWidgetsInfo::new(node_id, HeightInput::INDEX, true, context), NumberInput::default());
|
||||
|
||||
add_blank_assist(&mut corner_radius_row_2);
|
||||
|
||||
// Clamped
|
||||
let clamped = bool_widget(ParameterWidgetsInfo::new(node_id, ClampedInput::INDEX, true, context), CheckboxInput::default());
|
||||
|
||||
|
@ -1442,7 +1441,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
|
|||
if let Some(field) = graphene_std::registry::NODE_METADATA
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&proto_node_identifier)
|
||||
.get(proto_node_identifier)
|
||||
.and_then(|metadata| metadata.fields.get(input_index))
|
||||
{
|
||||
number_options = (field.number_min, field.number_max, field.number_hard_min, field.number_hard_max, field.number_mode_range);
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use super::utility_types::{OverlayProvider, OverlaysVisibilitySettings};
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct OverlaysMessageData<'a> {
|
||||
pub visibility_settings: OverlaysVisibilitySettings,
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub device_pixel_ratio: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct OverlaysMessageHandler {
|
||||
pub overlay_providers: HashSet<OverlayProvider>,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
@ -16,6 +17,7 @@ pub struct OverlaysMessageHandler {
|
|||
context: Option<web_sys::CanvasRenderingContext2d>,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<OverlaysMessage, OverlaysMessageData<'_>> for OverlaysMessageHandler {
|
||||
fn process_message(&mut self, message: OverlaysMessage, responses: &mut VecDeque<Message>, data: OverlaysMessageData) {
|
||||
let OverlaysMessageData { visibility_settings, ipp, .. } = data;
|
||||
|
|
|
@ -814,6 +814,36 @@ impl OverlayContext {
|
|||
self.render_context.fill_text(text, 0., 0.).expect("Failed to draw the text at the calculated position");
|
||||
self.render_context.reset_transform().expect("Failed to reset the render context transform");
|
||||
}
|
||||
|
||||
pub fn translation_box(&mut self, translation: DVec2, quad: Quad, typed_string: Option<String>) {
|
||||
if translation.x.abs() > 1e-3 {
|
||||
self.dashed_line(quad.top_left(), quad.top_right(), None, None, Some(2.), Some(2.), Some(0.5));
|
||||
|
||||
let width = match typed_string {
|
||||
Some(ref typed_string) => typed_string,
|
||||
None => &format!("{:.2}", translation.x).trim_end_matches('0').trim_end_matches('.').to_string(),
|
||||
};
|
||||
let x_transform = DAffine2::from_translation((quad.top_left() + quad.top_right()) / 2.);
|
||||
self.text(width, COLOR_OVERLAY_BLUE, None, x_transform, 4., [Pivot::Middle, Pivot::End]);
|
||||
}
|
||||
|
||||
if translation.y.abs() > 1e-3 {
|
||||
self.dashed_line(quad.top_left(), quad.bottom_left(), None, None, Some(2.), Some(2.), Some(0.5));
|
||||
|
||||
let height = match typed_string {
|
||||
Some(ref typed_string) => typed_string,
|
||||
None => &format!("{:.2}", translation.y).trim_end_matches('0').trim_end_matches('.').to_string(),
|
||||
};
|
||||
let y_transform = DAffine2::from_translation((quad.top_left() + quad.bottom_left()) / 2.);
|
||||
let height_pivot = if translation.x > -1e-3 { Pivot::Start } else { Pivot::End };
|
||||
self.text(height, COLOR_OVERLAY_BLUE, None, y_transform, 3., [height_pivot, Pivot::Middle]);
|
||||
}
|
||||
|
||||
if translation.x.abs() > 1e-3 && translation.y.abs() > 1e-3 {
|
||||
self.line(quad.top_right(), quad.bottom_right(), None, None);
|
||||
self.line(quad.bottom_left(), quad.bottom_right(), None, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Pivot {
|
||||
|
|
|
@ -4,9 +4,10 @@ use crate::messages::portfolio::document::node_graph::document_node_definitions:
|
|||
use crate::messages::portfolio::utility_types::PersistentData;
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct PropertiesPanelMessageHandler {}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPanelMessageHandlerData<'_>)> for PropertiesPanelMessageHandler {
|
||||
fn process_message(&mut self, message: PropertiesPanelMessage, responses: &mut VecDeque<Message>, (persistent_data, data): (&PersistentData, PropertiesPanelMessageHandlerData)) {
|
||||
let PropertiesPanelMessageHandlerData {
|
||||
|
|
|
@ -9,6 +9,7 @@ use bezier_rs::Subpath;
|
|||
use glam::IVec2;
|
||||
use graph_craft::document::DocumentNode;
|
||||
use graph_craft::document::{DocumentNodeImplementation, NodeInput, value::TaggedValue};
|
||||
use graphene_std::ProtoNodeIdentifier;
|
||||
use graphene_std::text::TypesettingConfig;
|
||||
use graphene_std::uuid::NodeId;
|
||||
use graphene_std::vector::style::{PaintOrder, StrokeAlign};
|
||||
|
@ -20,156 +21,440 @@ const TEXT_REPLACEMENTS: &[(&str, &str)] = &[
|
|||
("graphene_core::vector::vector_nodes::SubpathSegmentLengthsNode", "graphene_core::vector::SubpathSegmentLengthsNode"),
|
||||
];
|
||||
|
||||
const REPLACEMENTS: &[(&str, &str)] = &[
|
||||
("graphene_core::AddArtboardNode", "graphene_core::graphic_element::AppendArtboardNode"),
|
||||
("graphene_core::ConstructArtboardNode", "graphene_core::graphic_element::ToArtboardNode"),
|
||||
("graphene_core::ToGraphicElementNode", "graphene_core::graphic_element::ToElementNode"),
|
||||
("graphene_core::ToGraphicGroupNode", "graphene_core::graphic_element::ToGroupNode"),
|
||||
pub struct NodeReplacement<'a> {
|
||||
node: ProtoNodeIdentifier,
|
||||
aliases: &'a [&'a str],
|
||||
}
|
||||
|
||||
const NODE_REPLACEMENTS: &[NodeReplacement<'static>] = &[
|
||||
// graphic element
|
||||
NodeReplacement {
|
||||
node: graphene_std::graphic_element::append_artboard::IDENTIFIER,
|
||||
aliases: &["graphene_core::AddArtboardNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::graphic_element::to_artboard::IDENTIFIER,
|
||||
aliases: &["graphene_core::ConstructArtboardNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::graphic_element::to_element::IDENTIFIER,
|
||||
aliases: &["graphene_core::ToGraphicElementNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::graphic_element::to_group::IDENTIFIER,
|
||||
aliases: &["graphene_core::ToGraphicGroupNode"],
|
||||
},
|
||||
// math_nodes
|
||||
("graphene_core::ops::MathNode", "graphene_math_nodes::MathNode"),
|
||||
("graphene_core::ops::AddNode", "graphene_math_nodes::AddNode"),
|
||||
("graphene_core::ops::SubtractNode", "graphene_math_nodes::SubtractNode"),
|
||||
("graphene_core::ops::MultiplyNode", "graphene_math_nodes::MultiplyNode"),
|
||||
("graphene_core::ops::DivideNode", "graphene_math_nodes::DivideNode"),
|
||||
("graphene_core::ops::ModuloNode", "graphene_math_nodes::ModuloNode"),
|
||||
("graphene_core::ops::ExponentNode", "graphene_math_nodes::ExponentNode"),
|
||||
("graphene_core::ops::RootNode", "graphene_math_nodes::RootNode"),
|
||||
("graphene_core::ops::LogarithmNode", "graphene_math_nodes::LogarithmNode"),
|
||||
("graphene_core::ops::SineNode", "graphene_math_nodes::SineNode"),
|
||||
("graphene_core::ops::CosineNode", "graphene_math_nodes::CosineNode"),
|
||||
("graphene_core::ops::TangentNode", "graphene_math_nodes::TangentNode"),
|
||||
("graphene_core::ops::SineInverseNode", "graphene_math_nodes::SineInverseNode"),
|
||||
("graphene_core::ops::CosineInverseNode", "graphene_math_nodes::CosineInverseNode"),
|
||||
("graphene_core::ops::TangentInverseNode", "graphene_math_nodes::TangentInverseNode"),
|
||||
("graphene_core::ops::RandomNode", "graphene_math_nodes::RandomNode"),
|
||||
("graphene_core::ops::ToU32Node", "graphene_math_nodes::ToU32Node"),
|
||||
("graphene_core::ops::ToU64Node", "graphene_math_nodes::ToU64Node"),
|
||||
("graphene_core::ops::ToF64Node", "graphene_math_nodes::ToF64Node"),
|
||||
("graphene_core::ops::RoundNode", "graphene_math_nodes::RoundNode"),
|
||||
("graphene_core::ops::FloorNode", "graphene_math_nodes::FloorNode"),
|
||||
("graphene_core::ops::CeilingNode", "graphene_math_nodes::CeilingNode"),
|
||||
("graphene_core::ops::MinNode", "graphene_math_nodes::MinNode"),
|
||||
("graphene_core::ops::MaxNode", "graphene_math_nodes::MaxNode"),
|
||||
("graphene_core::ops::ClampNode", "graphene_math_nodes::ClampNode"),
|
||||
("graphene_core::ops::EqualsNode", "graphene_math_nodes::EqualsNode"),
|
||||
("graphene_core::ops::NotEqualsNode", "graphene_math_nodes::NotEqualsNode"),
|
||||
("graphene_core::ops::LessThanNode", "graphene_math_nodes::LessThanNode"),
|
||||
("graphene_core::ops::GreaterThanNode", "graphene_math_nodes::GreaterThanNode"),
|
||||
("graphene_core::ops::LogicalOrNode", "graphene_math_nodes::LogicalOrNode"),
|
||||
("graphene_core::ops::LogicalAndNode", "graphene_math_nodes::LogicalAndNode"),
|
||||
("graphene_core::ops::LogicalNotNode", "graphene_math_nodes::LogicalNotNode"),
|
||||
("graphene_core::ops::BoolValueNode", "graphene_math_nodes::BoolValueNode"),
|
||||
("graphene_core::ops::NumberValueNode", "graphene_math_nodes::NumberValueNode"),
|
||||
("graphene_core::ops::PercentageValueNode", "graphene_math_nodes::PercentageValueNode"),
|
||||
("graphene_core::ops::CoordinateValueNode", "graphene_math_nodes::CoordinateValueNode"),
|
||||
("graphene_core::ops::ConstructVector2", "graphene_math_nodes::CoordinateValueNode"),
|
||||
("graphene_core::ops::Vector2ValueNode", "graphene_math_nodes::CoordinateValueNode"),
|
||||
("graphene_core::ops::ColorValueNode", "graphene_math_nodes::ColorValueNode"),
|
||||
("graphene_core::ops::GradientValueNode", "graphene_math_nodes::GradientValueNode"),
|
||||
("graphene_core::ops::SampleGradientNode", "graphene_math_nodes::SampleGradientNode"),
|
||||
("graphene_core::ops::StringValueNode", "graphene_math_nodes::StringValueNode"),
|
||||
("graphene_core::ops::DotProductNode", "graphene_math_nodes::DotProductNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::math::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::MathNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::add::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::AddNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::subtract::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::SubtractNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::multiply::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::MultiplyNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::divide::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::DivideNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::modulo::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::ModuloNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::exponent::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::ExponentNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::root::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::RootNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::logarithm::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::LogarithmNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::sine::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::SineNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::cosine::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::CosineNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::tangent::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::TangentNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::sine_inverse::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::SineInverseNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::cosine_inverse::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::CosineInverseNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::tangent_inverse::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::TangentInverseNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::random::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::RandomNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::to_u_32::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::ToU32Node"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::to_u_64::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::ToU64Node"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::to_f_64::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::ToF64Node"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::round::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::RoundNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::floor::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::FloorNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::ceiling::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::CeilingNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::min::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::MinNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::max::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::MaxNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::clamp::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::ClampNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::equals::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::EqualsNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::not_equals::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::NotEqualsNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::less_than::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::LessThanNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::greater_than::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::GreaterThanNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::logical_or::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::LogicalOrNode", "graphene_core::ops::LogicAndNode", "graphene_core::logic::LogicAndNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::logical_and::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::LogicalAndNode", "graphene_core::ops::LogicNotNode", "graphene_core::logic::LogicNotNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::logical_not::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::LogicalNotNode", "graphene_core::ops::LogicOrNode", "graphene_core::logic::LogicOrNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::bool_value::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::BoolValueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::number_value::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::NumberValueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::percentage_value::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::PercentageValueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::coordinate_value::IDENTIFIER,
|
||||
aliases: &[
|
||||
"graphene_core::ops::CoordinateValueNode",
|
||||
"graphene_core::ops::ConstructVector2",
|
||||
"graphene_core::ops::Vector2ValueNode",
|
||||
],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::color_value::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::ColorValueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::gradient_value::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::GradientValueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::sample_gradient::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::SampleGradientNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::string_value::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::StringValueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::math_nodes::dot_product::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::DotProductNode"],
|
||||
},
|
||||
// debug
|
||||
("graphene_core::ops::SizeOfNode", "graphene_core::debug::SizeOfNode"),
|
||||
("graphene_core::ops::SomeNode", "graphene_core::debug::SomeNode"),
|
||||
("graphene_core::ops::UnwrapNode", "graphene_core::debug::UnwrapNode"),
|
||||
("graphene_core::ops::CloneNode", "graphene_core::debug::CloneNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::debug::size_of::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::SizeOfNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::debug::some::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::SomeNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::debug::unwrap::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::UnwrapNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::debug::clone::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::CloneNode"],
|
||||
},
|
||||
// ???
|
||||
("graphene_core::ops::ExtractXyNode", "graphene_core::extract_xy::ExtractXyNode"),
|
||||
("graphene_core::logic::LogicAndNode", "graphene_core::ops::LogicAndNode"),
|
||||
("graphene_core::logic::LogicNotNode", "graphene_core::ops::LogicNotNode"),
|
||||
("graphene_core::logic::LogicOrNode", "graphene_core::ops::LogicOrNode"),
|
||||
("graphene_core::raster::BlendModeNode", "graphene_core::blending_nodes::BlendModeNode"),
|
||||
("graphene_core::raster::OpacityNode", "graphene_core::blending_nodes::OpacityNode"),
|
||||
("graphene_core::raster::BlendingNode", "graphene_core::blending_nodes::BlendingNode"),
|
||||
("graphene_core::vector::GenerateHandlesNode", "graphene_core::vector::AutoTangentsNode"),
|
||||
("graphene_core::vector::RemoveHandlesNode", "graphene_core::vector::AutoTangentsNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::extract_xy::extract_xy::IDENTIFIER,
|
||||
aliases: &["graphene_core::ops::ExtractXyNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::blending_nodes::blend_mode::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::BlendModeNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::blending_nodes::opacity::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::OpacityNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::blending_nodes::blending::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::BlendingNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::auto_tangents::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::GenerateHandlesNode", "graphene_core::vector::RemoveHandlesNode"],
|
||||
},
|
||||
// raster::adjustments
|
||||
("graphene_core::raster::adjustments::LuminanceNode", "graphene_raster_nodes::adjustments::LuminanceNode"),
|
||||
("graphene_core::raster::LuminanceNode", "graphene_raster_nodes::adjustments::LuminanceNode"),
|
||||
("graphene_core::raster::adjustments::ExtractChannelNode", "graphene_raster_nodes::adjustments::ExtractChannelNode"),
|
||||
("graphene_core::raster::ExtractChannelNode", "graphene_raster_nodes::adjustments::ExtractChannelNode"),
|
||||
("graphene_core::raster::adjustments::MakeOpaqueNode", "graphene_raster_nodes::adjustments::MakeOpaqueNode"),
|
||||
("graphene_core::raster::ExtractOpaqueNode", "graphene_raster_nodes::adjustments::MakeOpaqueNode"),
|
||||
(
|
||||
"graphene_core::raster::adjustments::BrightnessContrastNode",
|
||||
"graphene_raster_nodes::adjustments::BrightnessContrastNode",
|
||||
),
|
||||
("graphene_core::raster::adjustments::LevelsNode", "graphene_raster_nodes::adjustments::LevelsNode"),
|
||||
("graphene_core::raster::LevelsNode", "graphene_raster_nodes::adjustments::LevelsNode"),
|
||||
("graphene_core::raster::adjustments::BlackAndWhiteNode", "graphene_raster_nodes::adjustments::BlackAndWhiteNode"),
|
||||
("graphene_core::raster::BlackAndWhiteNode", "graphene_raster_nodes::adjustments::BlackAndWhiteNode"),
|
||||
("graphene_core::raster::adjustments::HueSaturationNode", "graphene_raster_nodes::adjustments::HueSaturationNode"),
|
||||
("graphene_core::raster::HueSaturationNode", "graphene_raster_nodes::adjustments::HueSaturationNode"),
|
||||
("graphene_core::raster::adjustments::InvertNode", "graphene_raster_nodes::adjustments::InvertNode"),
|
||||
("graphene_core::raster::InvertNode", "graphene_raster_nodes::adjustments::InvertNode"),
|
||||
("graphene_core::raster::InvertRGBNode", "graphene_raster_nodes::adjustments::InvertNode"),
|
||||
("graphene_core::raster::adjustments::ThresholdNode", "graphene_raster_nodes::adjustments::ThresholdNode"),
|
||||
("graphene_core::raster::ThresholdNode", "graphene_raster_nodes::adjustments::ThresholdNode"),
|
||||
("graphene_core::raster::adjustments::BlendNode", "graphene_raster_nodes::adjustments::BlendNode"),
|
||||
("graphene_core::raster::BlendNode", "graphene_raster_nodes::adjustments::BlendNode"),
|
||||
("graphene_core::raster::BlendColorPairNode", "graphene_raster_nodes::adjustments::BlendColorPairNode"),
|
||||
("graphene_core::raster::adjustments::BlendColorsNode", "graphene_raster_nodes::adjustments::BlendColorsNode"),
|
||||
("graphene_core::raster::BlendColorsNode", "graphene_raster_nodes::adjustments::BlendColorsNode"),
|
||||
("graphene_core::raster::adjustments::GradientMapNode", "graphene_raster_nodes::adjustments::GradientMapNode"),
|
||||
("graphene_core::raster::GradientMapNode", "graphene_raster_nodes::adjustments::GradientMapNode"),
|
||||
("graphene_core::raster::adjustments::VibranceNode", "graphene_raster_nodes::adjustments::VibranceNode"),
|
||||
("graphene_core::raster::VibranceNode", "graphene_raster_nodes::adjustments::VibranceNode"),
|
||||
("graphene_core::raster::adjustments::ChannelMixerNode", "graphene_raster_nodes::adjustments::ChannelMixerNode"),
|
||||
("graphene_core::raster::ChannelMixerNode", "graphene_raster_nodes::adjustments::ChannelMixerNode"),
|
||||
("graphene_core::raster::adjustments::SelectiveColorNode", "graphene_raster_nodes::adjustments::SelectiveColorNode"),
|
||||
("graphene_core::raster::adjustments::PosterizeNode", "graphene_raster_nodes::adjustments::PosterizeNode"),
|
||||
("graphene_core::raster::PosterizeNode", "graphene_raster_nodes::adjustments::PosterizeNode"),
|
||||
("graphene_core::raster::adjustments::ExposureNode", "graphene_raster_nodes::adjustments::ExposureNode"),
|
||||
("graphene_core::raster::ExposureNode", "graphene_raster_nodes::adjustments::ExposureNode"),
|
||||
("graphene_core::raster::adjustments::ColorOverlayNode", "graphene_raster_nodes::adjustments::ColorOverlayNode"),
|
||||
("graphene_raster_nodes::generate_curves::ColorOverlayNode", "graphene_raster_nodes::adjustments::ColorOverlayNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::luminance::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::LuminanceNode", "graphene_core::raster::LuminanceNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::extract_channel::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::ExtractChannelNode", "graphene_core::raster::ExtractChannelNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::make_opaque::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::MakeOpaqueNode", "graphene_core::raster::ExtractOpaqueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::brightness_contrast::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::BrightnessContrastNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::levels::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::LevelsNode", "graphene_core::raster::LevelsNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::black_and_white::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::BlackAndWhiteNode", "graphene_core::raster::BlackAndWhiteNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::hue_saturation::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::HueSaturationNode", "graphene_core::raster::HueSaturationNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::invert::IDENTIFIER,
|
||||
aliases: &[
|
||||
"graphene_core::raster::adjustments::InvertNode",
|
||||
"graphene_core::raster::InvertNode",
|
||||
"graphene_core::raster::InvertRGBNode",
|
||||
],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::threshold::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::ThresholdNode", "graphene_core::raster::ThresholdNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::blend::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::BlendNode", "graphene_core::raster::BlendNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::blend_color_pair::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::BlendColorPairNode"],
|
||||
},
|
||||
// this node doesn't seem to exist?
|
||||
// (graphene_std::raster_nodes::adjustments::blend_color::IDENTIFIER, &["graphene_core::raster::adjustments::BlendColorsNode","graphene_core::raster::BlendColorsNode"]),
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::gradient_map::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::GradientMapNode", "graphene_core::raster::GradientMapNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::vibrance::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::VibranceNode", "graphene_core::raster::VibranceNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::channel_mixer::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::ChannelMixerNode", "graphene_core::raster::ChannelMixerNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::selective_color::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::SelectiveColorNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::posterize::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::PosterizeNode", "graphene_core::raster::PosterizeNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::exposure::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::ExposureNode", "graphene_core::raster::ExposureNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::adjustments::color_overlay::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::ColorOverlayNode", "graphene_raster_nodes::generate_curves::ColorOverlayNode"],
|
||||
},
|
||||
// raster
|
||||
("graphene_core::raster::adjustments::GenerateCurvesNode", "graphene_raster_nodes::generate_curves::GenerateCurvesNode"),
|
||||
("graphene_std::dehaze::DehazeNode", "graphene_raster_nodes::dehaze::DehazeNode"),
|
||||
("graphene_std::filter::BlurNode", "graphene_raster_nodes::filter::BlurNode"),
|
||||
(
|
||||
"graphene_std::image_color_palette::ImageColorPaletteNode",
|
||||
"graphene_raster_nodes::image_color_palette::ImageColorPaletteNode",
|
||||
),
|
||||
("graphene_std::raster::SampleImageNode", "graphene_raster_nodes::std_nodes::SampleImageNode"),
|
||||
("graphene_std::raster::CombineChannelsNode", "graphene_raster_nodes::std_nodes::CombineChannelsNode"),
|
||||
("graphene_std::raster::MaskNode", "graphene_raster_nodes::std_nodes::MaskNode"),
|
||||
("graphene_std::raster::ExtendImageToBoundsNode", "graphene_raster_nodes::std_nodes::ExtendImageToBoundsNode"),
|
||||
("graphene_std::raster::EmptyImageNode", "graphene_raster_nodes::std_nodes::EmptyImageNode"),
|
||||
("graphene_std::raster::ImageValueNode", "graphene_raster_nodes::std_nodes::ImageValueNode"),
|
||||
("graphene_std::raster::NoisePatternNode", "graphene_raster_nodes::std_nodes::NoisePatternNode"),
|
||||
("graphene_std::raster::MandelbrotNode", "graphene_raster_nodes::std_nodes::MandelbrotNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::generate_curves::generate_curves::IDENTIFIER,
|
||||
aliases: &["graphene_core::raster::adjustments::GenerateCurvesNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::dehaze::dehaze::IDENTIFIER,
|
||||
aliases: &["graphene_std::dehaze::DehazeNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::filter::blur::IDENTIFIER,
|
||||
aliases: &["graphene_std::filter::BlurNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::image_color_palette::image_color_palette::IDENTIFIER,
|
||||
aliases: &["graphene_std::image_color_palette::ImageColorPaletteNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::std_nodes::sample_image::IDENTIFIER,
|
||||
aliases: &["graphene_std::raster::SampleImageNode", "graphene_std::raster::SampleNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::std_nodes::combine_channels::IDENTIFIER,
|
||||
aliases: &["graphene_std::raster::CombineChannelsNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::std_nodes::mask::IDENTIFIER,
|
||||
aliases: &["graphene_std::raster::MaskNode", "graphene_std::raster::MaskImageNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::std_nodes::extend_image_to_bounds::IDENTIFIER,
|
||||
aliases: &["graphene_std::raster::ExtendImageToBoundsNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::std_nodes::empty_image::IDENTIFIER,
|
||||
aliases: &["graphene_std::raster::EmptyImageNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::std_nodes::image_value::IDENTIFIER,
|
||||
aliases: &["graphene_std::raster::ImageValueNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::std_nodes::noise_pattern::IDENTIFIER,
|
||||
aliases: &["graphene_std::raster::NoisePatternNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::raster_nodes::std_nodes::mandelbrot::IDENTIFIER,
|
||||
aliases: &["graphene_std::raster::MandelbrotNode"],
|
||||
},
|
||||
// text
|
||||
("graphene_core::text::TextGeneratorNode", "graphene_core::text::TextNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::text::text::IDENTIFIER,
|
||||
aliases: &["graphene_core::text::TextGeneratorNode"],
|
||||
},
|
||||
// transform
|
||||
("graphene_core::transform::SetTransformNode", "graphene_core::transform_nodes::ReplaceTransformNode"),
|
||||
("graphene_core::transform::ReplaceTransformNode", "graphene_core::transform_nodes::ReplaceTransformNode"),
|
||||
("graphene_core::transform::TransformNode", "graphene_core::transform_nodes::TransformNode"),
|
||||
("graphene_core::transform::BoundlessFootprintNode", "graphene_core::transform_nodes::BoundlessFootprintNode"),
|
||||
("graphene_core::transform::FreezeRealTimeNode", "graphene_core::transform_nodes::FreezeRealTimeNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::transform_nodes::replace_transform::IDENTIFIER,
|
||||
aliases: &["graphene_core::transform::SetTransformNode", "graphene_core::transform::ReplaceTransformNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::transform_nodes::transform::IDENTIFIER,
|
||||
aliases: &["graphene_core::transform::TransformNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::transform_nodes::boundless_footprint::IDENTIFIER,
|
||||
aliases: &["graphene_core::transform::BoundlessFootprintNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::transform_nodes::freeze_real_time::IDENTIFIER,
|
||||
aliases: &["graphene_core::transform::FreezeRealTimeNode"],
|
||||
},
|
||||
// ???
|
||||
("graphene_core::vector::SplinesFromPointsNode", "graphene_core::vector::SplineNode"),
|
||||
("graphene_core::vector::generator_nodes::EllipseGenerator", "graphene_core::vector::generator_nodes::EllipseNode"),
|
||||
("graphene_core::vector::generator_nodes::LineGenerator", "graphene_core::vector::generator_nodes::LineNode"),
|
||||
("graphene_core::vector::generator_nodes::RectangleGenerator", "graphene_core::vector::generator_nodes::RectangleNode"),
|
||||
(
|
||||
"graphene_core::vector::generator_nodes::RegularPolygonGenerator",
|
||||
"graphene_core::vector::generator_nodes::RegularPolygonNode",
|
||||
),
|
||||
("graphene_core::vector::generator_nodes::StarGenerator", "graphene_core::vector::generator_nodes::StarNode"),
|
||||
("graphene_std::executor::BlendGpuImageNode", "graphene_std::gpu_nodes::BlendGpuImageNode"),
|
||||
("graphene_std::raster::SampleNode", "graphene_std::raster::SampleImageNode"),
|
||||
("graphene_core::transform::CullNode", "graphene_core::ops::IdentityNode"),
|
||||
("graphene_std::raster::MaskImageNode", "graphene_std::raster::MaskNode"),
|
||||
("graphene_core::vector::FlattenVectorElementsNode", "graphene_core::vector::FlattenPathNode"),
|
||||
("graphene_std::vector::BooleanOperationNode", "graphene_path_bool::BooleanOperationNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::spline::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::SplinesFromPointsNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::generator_nodes::ellipse::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::generator_nodes::EllipseGenerator"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::generator_nodes::line::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::generator_nodes::LineGenerator"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::generator_nodes::rectangle::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::generator_nodes::RectangleGenerator"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::generator_nodes::regular_polygon::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::generator_nodes::RegularPolygonGenerator"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::generator_nodes::star::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::generator_nodes::StarGenerator"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::ops::identity::IDENTIFIER,
|
||||
aliases: &["graphene_core::transform::CullNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::vector::flatten_path::IDENTIFIER,
|
||||
aliases: &["graphene_core::vector::FlattenVectorElementsNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::path_bool::boolean_operation::IDENTIFIER,
|
||||
aliases: &["graphene_std::vector::BooleanOperationNode"],
|
||||
},
|
||||
// brush
|
||||
("graphene_std::brush::BrushStampGeneratorNode", "graphene_brush::brush::BrushStampGeneratorNode"),
|
||||
("graphene_std::brush::BlitNode", "graphene_brush::brush::BlitNode"),
|
||||
("graphene_std::brush::BrushNode", "graphene_brush::brush::BrushNode"),
|
||||
NodeReplacement {
|
||||
node: graphene_std::brush::brush::brush_stamp_generator::IDENTIFIER,
|
||||
aliases: &["graphene_std::brush::BrushStampGeneratorNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::brush::brush::blit::IDENTIFIER,
|
||||
aliases: &["graphene_std::brush::BlitNode"],
|
||||
},
|
||||
NodeReplacement {
|
||||
node: graphene_std::brush::brush::brush::IDENTIFIER,
|
||||
aliases: &["graphene_std::brush::BrushNode"],
|
||||
},
|
||||
];
|
||||
|
||||
const REPLACEMENTS: &[(&str, &str)] = &[];
|
||||
|
||||
pub fn document_migration_string_preprocessing(document_serialized_content: String) -> String {
|
||||
TEXT_REPLACEMENTS
|
||||
.iter()
|
||||
|
@ -195,17 +480,26 @@ pub fn document_migration_upgrades(document: &mut DocumentMessageHandler, reset_
|
|||
|
||||
let network = document.network_interface.document_network().clone();
|
||||
|
||||
// Apply string replacements to each node
|
||||
// Apply string and node replacements to each node
|
||||
let mut replacements = HashMap::<&str, ProtoNodeIdentifier>::new();
|
||||
Iterator::chain(
|
||||
NODE_REPLACEMENTS.iter().flat_map(|NodeReplacement { node, aliases }| aliases.iter().map(|old| (*old, node.clone()))),
|
||||
REPLACEMENTS.iter().map(|(old, new)| (*old, ProtoNodeIdentifier::new(new))),
|
||||
)
|
||||
.for_each(|(old, new)| {
|
||||
if replacements.insert(old, new).is_some() {
|
||||
panic!("Duplicate old name `{old}`");
|
||||
}
|
||||
});
|
||||
|
||||
for (node_id, node, network_path) in network.recursive_nodes() {
|
||||
if let DocumentNodeImplementation::ProtoNode(protonode_id) = &node.implementation {
|
||||
for (old, new) in REPLACEMENTS {
|
||||
let node_path_without_type_args = protonode_id.name.split('<').next();
|
||||
let node_path_without_type_args = protonode_id.name.split('<').next();
|
||||
if let Some(new) = node_path_without_type_args.and_then(|node_path| replacements.get(node_path)) {
|
||||
let mut default_template = NodeTemplate::default();
|
||||
default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.to_string().into());
|
||||
if node_path_without_type_args == Some(old) {
|
||||
document.network_interface.replace_implementation(node_id, &network_path, &mut default_template);
|
||||
document.network_interface.set_manual_compostion(node_id, &network_path, Some(graph_craft::Type::Generic("T".into())));
|
||||
}
|
||||
default_template.document_node.implementation = DocumentNodeImplementation::ProtoNode(new.clone());
|
||||
document.network_interface.replace_implementation(node_id, &network_path, &mut default_template);
|
||||
document.network_interface.set_manual_compostion(node_id, &network_path, Some(graph_craft::Type::Generic("T".into())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -691,3 +985,20 @@ fn migrate_node(node_id: &NodeId, node: &DocumentNode, network_path: &[NodeId],
|
|||
|
||||
Some(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_no_duplicate_node_replacements() {
|
||||
let mut hashmap = HashMap::<ProtoNodeIdentifier, u32>::new();
|
||||
NODE_REPLACEMENTS.iter().for_each(|node| {
|
||||
*hashmap.entry(node.node.clone()).or_default() += 1;
|
||||
});
|
||||
let duplicates = hashmap.iter().filter(|(_, count)| **count > 1).map(|(node, _)| &node.name).collect::<Vec<_>>();
|
||||
if duplicates.len() > 0 {
|
||||
panic!("Duplicate entries in `NODE_REPLACEMENTS`: {:?}", duplicates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate,
|
|||
use crate::messages::prelude::*;
|
||||
use graphene_std::path_bool::BooleanOperation;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct MenuBarMessageHandler {
|
||||
pub has_active_document: bool,
|
||||
pub canvas_tilted: bool,
|
||||
|
@ -21,6 +21,7 @@ pub struct MenuBarMessageHandler {
|
|||
pub reset_node_definitions_on_open: bool,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<MenuBarMessage, ()> for MenuBarMessageHandler {
|
||||
fn process_message(&mut self, message: MenuBarMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -25,6 +25,7 @@ use graphene_std::renderer::Quad;
|
|||
use graphene_std::text::Font;
|
||||
use std::vec;
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct PortfolioMessageData<'a> {
|
||||
pub ipp: &'a InputPreprocessorMessageHandler,
|
||||
pub preferences: &'a PreferencesMessageHandler,
|
||||
|
@ -35,7 +36,7 @@ pub struct PortfolioMessageData<'a> {
|
|||
pub animation: &'a AnimationMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
pub struct PortfolioMessageHandler {
|
||||
menu_bar_message_handler: MenuBarMessageHandler,
|
||||
pub documents: HashMap<DocumentId, DocumentMessageHandler>,
|
||||
|
@ -52,6 +53,7 @@ pub struct PortfolioMessageHandler {
|
|||
pub reset_node_definitions_on_open: bool,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<PortfolioMessage, PortfolioMessageData<'_>> for PortfolioMessageHandler {
|
||||
fn process_message(&mut self, message: PortfolioMessage, responses: &mut VecDeque<Message>, data: PortfolioMessageData) {
|
||||
let PortfolioMessageData {
|
||||
|
|
|
@ -15,7 +15,7 @@ use std::any::Any;
|
|||
use std::sync::Arc;
|
||||
|
||||
/// The spreadsheet UI allows for instance data to be previewed.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone, ExtractField)]
|
||||
pub struct SpreadsheetMessageHandler {
|
||||
/// Sets whether or not the spreadsheet is drawn.
|
||||
pub spreadsheet_view_open: bool,
|
||||
|
@ -25,6 +25,7 @@ pub struct SpreadsheetMessageHandler {
|
|||
viewing_vector_data_domain: VectorDataDomain,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<SpreadsheetMessage, ()> for SpreadsheetMessageHandler {
|
||||
fn process_message(&mut self, message: SpreadsheetMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::messages::preferences::SelectionMode;
|
|||
use crate::messages::prelude::*;
|
||||
use graph_craft::wasm_application_io::EditorPreferences;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
|
||||
#[derive(Debug, PartialEq, Clone, serde::Serialize, serde::Deserialize, specta::Type, ExtractField)]
|
||||
pub struct PreferencesMessageHandler {
|
||||
pub selection_mode: SelectionMode,
|
||||
pub zoom_with_scroll: bool,
|
||||
|
@ -44,6 +44,7 @@ impl Default for PreferencesMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<PreferencesMessage, ()> for PreferencesMessageHandler {
|
||||
fn process_message(&mut self, message: PreferencesMessage, responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Root
|
||||
pub use crate::utility_traits::{ActionList, AsMessage, MessageHandler, ToDiscriminant, TransitiveChild};
|
||||
|
||||
pub use crate::utility_traits::{ActionList, AsMessage, HierarchicalTree, MessageHandler, ToDiscriminant, TransitiveChild};
|
||||
pub use crate::utility_types::{DebugMessageTree, MessageData};
|
||||
// Message, MessageData, MessageDiscriminant, MessageHandler
|
||||
pub use crate::messages::animation::{AnimationMessage, AnimationMessageDiscriminant, AnimationMessageHandler};
|
||||
pub use crate::messages::broadcast::{BroadcastMessage, BroadcastMessageDiscriminant, BroadcastMessageHandler};
|
||||
|
|
|
@ -1079,7 +1079,7 @@ impl ShapeState {
|
|||
|
||||
let mut normalized = handle_directions[0].and_then(|a| handle_directions[1].and_then(|b| (a - b).try_normalize()));
|
||||
|
||||
if normalized.is_none() {
|
||||
if normalized.is_none() || handle_directions.iter().any(|&d| d.is_some_and(|d| d.length_squared() < f64::EPSILON * 1e5)) {
|
||||
handle_directions = anchor_positions.map(|relative_anchor| relative_anchor.map(|relative_anchor| (relative_anchor - anchor) / 3.));
|
||||
normalized = handle_directions[0].and_then(|a| handle_directions[1].and_then(|b| (a - b).try_normalize()))
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use graphene_std::raster::color::Color;
|
|||
|
||||
const ARTBOARD_OVERLAY_PROVIDER: OverlayProvider = |context| DocumentMessage::DrawArtboardOverlays(context).into();
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct ToolMessageData<'a> {
|
||||
pub document_id: DocumentId,
|
||||
pub document: &'a mut DocumentMessageHandler,
|
||||
|
@ -21,7 +22,7 @@ pub struct ToolMessageData<'a> {
|
|||
pub preferences: &'a PreferencesMessageHandler,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, ExtractField)]
|
||||
pub struct ToolMessageHandler {
|
||||
pub tool_state: ToolFsmState,
|
||||
pub transform_layer_handler: TransformLayerMessageHandler,
|
||||
|
@ -29,6 +30,7 @@ pub struct ToolMessageHandler {
|
|||
pub tool_is_active: bool,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<ToolMessage, ToolMessageData<'_>> for ToolMessageHandler {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, data: ToolMessageData) {
|
||||
let ToolMessageData {
|
||||
|
|
|
@ -13,7 +13,7 @@ use crate::messages::tool::common_functionality::transformation_cage::*;
|
|||
use graph_craft::document::NodeId;
|
||||
use graphene_std::renderer::Quad;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct ArtboardTool {
|
||||
fsm_state: ArtboardToolFsmState,
|
||||
data: ArtboardToolData,
|
||||
|
@ -48,6 +48,7 @@ impl ToolMetadata for ArtboardTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for ArtboardTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &(), responses, false);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::tool_prelude::*;
|
||||
use crate::consts::DEFAULT_BRUSH_SIZE;
|
||||
use crate::messages::portfolio::document::graph_operation::transform_utils::{get_current_normalized_pivot, get_current_transform};
|
||||
use crate::messages::portfolio::document::graph_operation::transform_utils::get_current_transform;
|
||||
use crate::messages::portfolio::document::node_graph::document_node_definitions::resolve_document_node_type;
|
||||
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
|
||||
use crate::messages::portfolio::document::utility_types::network_interface::FlowType;
|
||||
|
@ -20,7 +20,7 @@ pub enum DrawMode {
|
|||
Restore,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct BrushTool {
|
||||
fsm_state: BrushToolFsmState,
|
||||
data: BrushToolData,
|
||||
|
@ -185,6 +185,7 @@ impl LayoutHolder for BrushTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for BrushTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
let ToolMessage::Brush(BrushToolMessage::UpdateOptions(action)) = message else {
|
||||
|
@ -286,9 +287,7 @@ impl BrushToolData {
|
|||
}
|
||||
|
||||
if *reference == Some("Transform".to_string()) {
|
||||
let upstream = document.metadata().upstream_transform(node_id);
|
||||
let pivot = DAffine2::from_translation(upstream.transform_point2(get_current_normalized_pivot(&node.inputs)));
|
||||
self.transform = pivot * get_current_transform(&node.inputs) * pivot.inverse() * self.transform;
|
||||
self.transform = get_current_transform(&node.inputs) * self.transform;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::tool_prelude::*;
|
||||
use crate::messages::tool::utility_types::DocumentToolData;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct EyedropperTool {
|
||||
fsm_state: EyedropperToolFsmState,
|
||||
data: EyedropperToolData,
|
||||
|
@ -39,6 +39,7 @@ impl LayoutHolder for EyedropperTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for EyedropperTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
self.fsm_state.process_event(message, &mut self.data, tool_data, &(), responses, true);
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::messages::portfolio::document::overlays::utility_types::OverlayContex
|
|||
use crate::messages::tool::common_functionality::graph_modification_utils::NodeGraphLayer;
|
||||
use graphene_std::vector::style::Fill;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct FillTool {
|
||||
fsm_state: FillToolFsmState,
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ impl LayoutHolder for FillTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FillTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
self.fsm_state.process_event(message, &mut (), tool_data, &(), responses, true);
|
||||
|
|
|
@ -13,7 +13,7 @@ use graphene_std::Color;
|
|||
use graphene_std::vector::VectorModificationType;
|
||||
use graphene_std::vector::{PointId, SegmentId};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct FreehandTool {
|
||||
fsm_state: FreehandToolFsmState,
|
||||
data: FreehandToolData,
|
||||
|
@ -116,6 +116,7 @@ impl LayoutHolder for FreehandTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for FreehandTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
let ToolMessage::Freehand(FreehandToolMessage::UpdateOptions(action)) = message else {
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::messages::tool::common_functionality::graph_modification_utils::{Node
|
|||
use crate::messages::tool::common_functionality::snapping::SnapManager;
|
||||
use graphene_std::vector::style::{Fill, Gradient, GradientType};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct GradientTool {
|
||||
fsm_state: GradientToolFsmState,
|
||||
data: GradientToolData,
|
||||
|
@ -53,6 +53,7 @@ impl ToolMetadata for GradientTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for GradientTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
let ToolMessage::Gradient(GradientToolMessage::UpdateOptions(action)) = message else {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::tool_prelude::*;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct NavigateTool {
|
||||
fsm_state: NavigateToolFsmState,
|
||||
tool_data: NavigateToolData,
|
||||
|
@ -38,6 +38,7 @@ impl LayoutHolder for NavigateTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for NavigateTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
self.fsm_state.process_event(message, &mut self.tool_data, tool_data, &(), responses, true);
|
||||
|
|
|
@ -24,7 +24,7 @@ use graphene_std::vector::{HandleExt, HandleId, NoHashBuilder, SegmentId, Vector
|
|||
use graphene_std::vector::{ManipulatorPointId, PointId, VectorModificationType};
|
||||
use std::vec;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct PathTool {
|
||||
fsm_state: PathToolFsmState,
|
||||
tool_data: PathToolData,
|
||||
|
@ -305,6 +305,7 @@ impl LayoutHolder for PathTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PathTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
let updating_point = message == ToolMessage::Path(PathToolMessage::SelectedPointUpdated);
|
||||
|
@ -1700,9 +1701,9 @@ impl Fsm for PathToolFsmState {
|
|||
break_molding,
|
||||
tool_data.temporary_adjacent_handles_while_molding,
|
||||
);
|
||||
}
|
||||
|
||||
return PathToolFsmState::Dragging(tool_data.dragging_state);
|
||||
return PathToolFsmState::Dragging(tool_data.dragging_state);
|
||||
}
|
||||
}
|
||||
|
||||
let anchor_and_handle_toggled = input.keyboard.get(move_anchor_with_handles as usize);
|
||||
|
|
|
@ -17,7 +17,7 @@ use graphene_std::Color;
|
|||
use graphene_std::vector::{HandleId, ManipulatorPointId, NoHashBuilder, SegmentId, StrokeId, VectorData};
|
||||
use graphene_std::vector::{PointId, VectorModificationType};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct PenTool {
|
||||
fsm_state: PenToolFsmState,
|
||||
tool_data: PenToolData,
|
||||
|
@ -186,6 +186,7 @@ impl LayoutHolder for PenTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for PenTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
let ToolMessage::Pen(PenToolMessage::UpdateOptions(action)) = message else {
|
||||
|
|
|
@ -29,7 +29,7 @@ use graphene_std::renderer::Rect;
|
|||
use graphene_std::transform::ReferencePoint;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct SelectTool {
|
||||
fsm_state: SelectToolFsmState,
|
||||
tool_data: SelectToolData,
|
||||
|
@ -272,6 +272,7 @@ impl LayoutHolder for SelectTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SelectTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
let mut redraw_reference_pivot = false;
|
||||
|
@ -973,6 +974,14 @@ impl Fsm for SelectToolFsmState {
|
|||
}
|
||||
}
|
||||
|
||||
if let Self::Dragging { .. } = self {
|
||||
let quad = Quad::from_box([tool_data.drag_start, tool_data.drag_current]);
|
||||
let document_start = document.metadata().document_to_viewport.inverse().transform_point2(quad.top_left());
|
||||
let document_current = document.metadata().document_to_viewport.inverse().transform_point2(quad.bottom_right());
|
||||
|
||||
overlay_context.translation_box(document_current - document_start, quad, None);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
(_, SelectToolMessage::EditLayer) => {
|
||||
|
|
|
@ -14,7 +14,7 @@ use graph_craft::document::{NodeId, NodeInput};
|
|||
use graphene_std::Color;
|
||||
use graphene_std::vector::{PointId, SegmentId, VectorModificationType};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct SplineTool {
|
||||
fsm_state: SplineToolFsmState,
|
||||
tool_data: SplineToolData,
|
||||
|
@ -123,6 +123,7 @@ impl LayoutHolder for SplineTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for SplineTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
let ToolMessage::Spline(SplineToolMessage::UpdateOptions(action)) = message else {
|
||||
|
|
|
@ -20,7 +20,7 @@ use graphene_std::renderer::Quad;
|
|||
use graphene_std::text::{Font, FontCache, TypesettingConfig, lines_clipping, load_font};
|
||||
use graphene_std::vector::style::Fill;
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, ExtractField)]
|
||||
pub struct TextTool {
|
||||
fsm_state: TextToolFsmState,
|
||||
tool_data: TextToolData,
|
||||
|
@ -170,6 +170,7 @@ impl LayoutHolder for TextTool {
|
|||
}
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl<'a> MessageHandler<ToolMessage, &mut ToolActionHandlerData<'a>> for TextTool {
|
||||
fn process_message(&mut self, message: ToolMessage, responses: &mut VecDeque<Message>, tool_data: &mut ToolActionHandlerData<'a>) {
|
||||
let ToolMessage::Text(TextToolMessage::UpdateOptions(action)) = message else {
|
||||
|
|
|
@ -21,7 +21,7 @@ const TRANSFORM_GRS_OVERLAY_PROVIDER: OverlayProvider = |context| TransformLayer
|
|||
const SLOW_KEY: Key = Key::Shift;
|
||||
const INCREMENTS_KEY: Key = Key::Control;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct TransformLayerMessageHandler {
|
||||
pub transform_operation: TransformOperation,
|
||||
|
||||
|
@ -175,6 +175,26 @@ fn update_colinear_handles(selected_layers: &[LayerNodeIdentifier], document: &D
|
|||
}
|
||||
|
||||
type TransformData<'a> = (&'a DocumentMessageHandler, &'a InputPreprocessorMessageHandler, &'a ToolData, &'a mut ShapeState);
|
||||
|
||||
pub fn custom_data() -> MessageData {
|
||||
MessageData::new(
|
||||
String::from("TransformData<'a>"),
|
||||
// TODO: When <https://github.com/dtolnay/proc-macro2/issues/503> is resolved and released,
|
||||
// TODO: use <https://doc.rust-lang.org/stable/proc_macro/struct.Span.html#method.line> to get
|
||||
// TODO: the line number instead of hardcoding it to the magic number on the following lines
|
||||
// TODO: which points to the line of the `type TransformData<'a> = ...` definition above.
|
||||
// TODO: Also, utilize the line number in the actual output, since it is currently unused.
|
||||
vec![
|
||||
(String::from("&'a DocumentMessageHandler"), 177),
|
||||
(String::from("&'a InputPreprocessorMessageHandler"), 177),
|
||||
(String::from("&'a ToolData"), 177),
|
||||
(String::from("&'a mut ShapeState"), 177),
|
||||
],
|
||||
file!(),
|
||||
)
|
||||
}
|
||||
|
||||
#[message_handler_data(CustomData)]
|
||||
impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayerMessageHandler {
|
||||
fn process_message(&mut self, message: TransformLayerMessage, responses: &mut VecDeque<Message>, (document, input, tool_data, shape_editor): TransformData) {
|
||||
let using_path_tool = tool_data.active_tool_type == ToolType::Path;
|
||||
|
@ -299,37 +319,15 @@ impl MessageHandler<TransformLayerMessage, TransformData<'_>> for TransformLayer
|
|||
let translation = translation.to_dvec(self.initial_transform, self.increments);
|
||||
let viewport_translate = document_to_viewport.transform_vector2(translation);
|
||||
let pivot = document_to_viewport.transform_point2(self.grab_target);
|
||||
let quad = Quad::from_box([pivot, pivot + viewport_translate]).0;
|
||||
let e1 = (self.layer_bounding_box.0[1] - self.layer_bounding_box.0[0]).normalize_or(DVec2::X);
|
||||
let quad = Quad::from_box([pivot, pivot + viewport_translate]);
|
||||
|
||||
responses.add(SelectToolMessage::PivotShift {
|
||||
offset: Some(viewport_translate),
|
||||
flush: false,
|
||||
});
|
||||
|
||||
if matches!(axis_constraint, Axis::Both | Axis::X) && translation.x != 0. {
|
||||
let end = if self.local { (quad[1] - quad[0]).rotate(e1) + quad[0] } else { quad[1] };
|
||||
overlay_context.dashed_line(quad[0], end, None, None, Some(2.), Some(2.), Some(0.5));
|
||||
|
||||
let x_transform = DAffine2::from_translation((quad[0] + end) / 2.);
|
||||
overlay_context.text(&format_rounded(translation.x, 3), COLOR_OVERLAY_BLUE, None, x_transform, 4., [Pivot::Middle, Pivot::End]);
|
||||
}
|
||||
|
||||
if matches!(axis_constraint, Axis::Both | Axis::Y) && translation.y != 0. {
|
||||
let end = if self.local { (quad[3] - quad[0]).rotate(e1) + quad[0] } else { quad[3] };
|
||||
overlay_context.dashed_line(quad[0], end, None, None, Some(2.), Some(2.), Some(0.5));
|
||||
let x_parameter = viewport_translate.x.clamp(-1., 1.);
|
||||
let y_transform = DAffine2::from_translation((quad[0] + end) / 2. + x_parameter * DVec2::X * 0.);
|
||||
let pivot_selection = if x_parameter >= -1e-3 { Pivot::Start } else { Pivot::End };
|
||||
if axis_constraint != Axis::Both || self.typing.digits.is_empty() || !self.transform_operation.can_begin_typing() {
|
||||
overlay_context.text(&format_rounded(translation.y, 2), COLOR_OVERLAY_BLUE, None, y_transform, 3., [pivot_selection, Pivot::Middle]);
|
||||
}
|
||||
}
|
||||
|
||||
if matches!(axis_constraint, Axis::Both) && translation.x != 0. && translation.y != 0. {
|
||||
overlay_context.line(quad[1], quad[2], None, None);
|
||||
overlay_context.line(quad[3], quad[2], None, None);
|
||||
}
|
||||
let typed_string = (!self.typing.digits.is_empty() && self.transform_operation.can_begin_typing()).then(|| self.typing.string.clone());
|
||||
overlay_context.translation_box(translation, quad, typed_string);
|
||||
}
|
||||
TransformOperation::Scaling(scale) => {
|
||||
let scale = scale.to_f64(self.increments);
|
||||
|
|
|
@ -18,6 +18,7 @@ use graphene_std::text::FontCache;
|
|||
use std::borrow::Cow;
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
#[derive(ExtractField)]
|
||||
pub struct ToolActionHandlerData<'a> {
|
||||
pub document: &'a mut DocumentMessageHandler,
|
||||
pub document_id: DocumentId,
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::messages::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, ExtractField)]
|
||||
pub struct WorkspaceMessageHandler {
|
||||
node_graph_visible: bool,
|
||||
}
|
||||
|
||||
#[message_handler_data]
|
||||
impl MessageHandler<WorkspaceMessage, ()> for WorkspaceMessageHandler {
|
||||
fn process_message(&mut self, message: WorkspaceMessage, _responses: &mut VecDeque<Message>, _data: ()) {
|
||||
match message {
|
||||
|
|
|
@ -328,6 +328,18 @@ impl NodeRuntime {
|
|||
return;
|
||||
}
|
||||
|
||||
// Skip thumbnails if the layer is too complex (for performance)
|
||||
if graphic_element.render_complexity() > 1000 {
|
||||
let old = thumbnail_renders.insert(parent_network_node_id, Vec::new());
|
||||
if old.is_none_or(|v| !v.is_empty()) {
|
||||
responses.push_back(FrontendMessage::UpdateNodeThumbnail {
|
||||
id: parent_network_node_id,
|
||||
value: "<svg viewBox=\"0 0 10 10\"><title>Dense thumbnail omitted for performance</title><line x1=\"0\" y1=\"10\" x2=\"10\" y2=\"0\" stroke=\"red\" /></svg>".to_string(),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let bounds = graphic_element.bounding_box(DAffine2::IDENTITY, true);
|
||||
|
||||
// Render the thumbnail from a `GraphicElement` into an SVG string
|
||||
|
|
|
@ -45,3 +45,19 @@ pub trait TransitiveChild: Into<Self::Parent> + Into<Self::TopParent> {
|
|||
pub trait Hint {
|
||||
fn hints(&self) -> HashMap<String, String>;
|
||||
}
|
||||
|
||||
pub trait HierarchicalTree {
|
||||
fn build_message_tree() -> DebugMessageTree;
|
||||
|
||||
fn message_handler_data_str() -> MessageData {
|
||||
MessageData::new(String::new(), Vec::new(), "")
|
||||
}
|
||||
|
||||
fn message_handler_str() -> MessageData {
|
||||
MessageData::new(String::new(), Vec::new(), "")
|
||||
}
|
||||
|
||||
fn path() -> &'static str {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
|
99
editor/src/utility_types.rs
Normal file
99
editor/src/utility_types.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
#[derive(Debug)]
|
||||
pub struct MessageData {
|
||||
name: String,
|
||||
fields: Vec<(String, usize)>,
|
||||
path: &'static str,
|
||||
}
|
||||
|
||||
impl MessageData {
|
||||
pub fn new(name: String, fields: Vec<(String, usize)>, path: &'static str) -> MessageData {
|
||||
MessageData { name, fields, path }
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn fields(&self) -> &Vec<(String, usize)> {
|
||||
&self.fields
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &'static str {
|
||||
self.path
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DebugMessageTree {
|
||||
name: String,
|
||||
variants: Option<Vec<DebugMessageTree>>,
|
||||
message_handler: Option<MessageData>,
|
||||
message_handler_data: Option<MessageData>,
|
||||
path: &'static str,
|
||||
}
|
||||
|
||||
impl DebugMessageTree {
|
||||
pub fn new(name: &str) -> DebugMessageTree {
|
||||
DebugMessageTree {
|
||||
name: name.to_string(),
|
||||
variants: None,
|
||||
message_handler: None,
|
||||
message_handler_data: None,
|
||||
path: "",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_path(&mut self, path: &'static str) {
|
||||
self.path = path;
|
||||
}
|
||||
|
||||
pub fn add_variant(&mut self, variant: DebugMessageTree) {
|
||||
if let Some(variants) = &mut self.variants {
|
||||
variants.push(variant);
|
||||
} else {
|
||||
self.variants = Some(vec![variant]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_message_handler_data_field(&mut self, message_handler_data: MessageData) {
|
||||
self.message_handler_data = Some(message_handler_data);
|
||||
}
|
||||
|
||||
pub fn add_message_handler_field(&mut self, message_handler: MessageData) {
|
||||
self.message_handler = Some(message_handler);
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &'static str {
|
||||
self.path
|
||||
}
|
||||
|
||||
pub fn variants(&self) -> Option<&Vec<DebugMessageTree>> {
|
||||
self.variants.as_ref()
|
||||
}
|
||||
|
||||
pub fn message_handler_data_fields(&self) -> Option<&MessageData> {
|
||||
self.message_handler_data.as_ref()
|
||||
}
|
||||
|
||||
pub fn message_handler_fields(&self) -> Option<&MessageData> {
|
||||
self.message_handler.as_ref()
|
||||
}
|
||||
|
||||
pub fn has_message_handler_data_fields(&self) -> bool {
|
||||
match self.message_handler_data_fields() {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_message_handler_fields(&self) -> bool {
|
||||
match self.message_handler_fields() {
|
||||
Some(_) => true,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -403,7 +403,7 @@ mod test {
|
|||
blend_mode: BlendMode::Normal,
|
||||
},
|
||||
}],
|
||||
BrushCache::new_proto(),
|
||||
BrushCache::default(),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(image.instance_ref_iter().next().unwrap().instance.width, 20);
|
||||
|
|
|
@ -6,11 +6,16 @@ use graphene_core::raster_types::CPU;
|
|||
use graphene_core::raster_types::Raster;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
use std::hash::Hasher;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default, serde::Serialize, serde::Deserialize)]
|
||||
// TODO: This is a temporary hack, be sure to not reuse this when the brush is being rewritten.
|
||||
static NEXT_BRUSH_CACHE_IMPL_ID: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
#[derive(Clone, Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
struct BrushCacheImpl {
|
||||
unique_id: u64,
|
||||
// The full previous input that was cached.
|
||||
prev_input: Vec<BrushStroke>,
|
||||
|
||||
|
@ -90,9 +95,29 @@ impl BrushCacheImpl {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for BrushCacheImpl {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
unique_id: NEXT_BRUSH_CACHE_IMPL_ID.fetch_add(1, Ordering::SeqCst),
|
||||
prev_input: Vec::new(),
|
||||
background: Default::default(),
|
||||
blended_image: Default::default(),
|
||||
last_stroke_texture: Default::default(),
|
||||
brush_texture_cache: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BrushCacheImpl {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.unique_id == other.unique_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for BrushCacheImpl {
|
||||
// Zero hash.
|
||||
fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {}
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.unique_id.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -103,46 +128,26 @@ pub struct BrushPlan {
|
|||
pub first_stroke_point_skip: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct BrushCache {
|
||||
inner: Arc<Mutex<BrushCacheImpl>>,
|
||||
proto: bool,
|
||||
}
|
||||
|
||||
impl Default for BrushCache {
|
||||
fn default() -> Self {
|
||||
Self::new_proto()
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Default, DynAny, serde::Serialize, serde::Deserialize)]
|
||||
pub struct BrushCache(Arc<Mutex<BrushCacheImpl>>);
|
||||
|
||||
// A bit of a cursed implementation to work around the current node system.
|
||||
// The original object is a 'prototype' that when cloned gives you a independent
|
||||
// new object. Any further clones however are all the same underlying cache object.
|
||||
impl Clone for BrushCache {
|
||||
fn clone(&self) -> Self {
|
||||
if self.proto {
|
||||
let inner_val = self.inner.lock().unwrap();
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(inner_val.clone())),
|
||||
proto: false,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
inner: Arc::clone(&self.inner),
|
||||
proto: false,
|
||||
}
|
||||
}
|
||||
Self(Arc::new(Mutex::new(self.0.lock().unwrap().clone())))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for BrushCache {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if Arc::ptr_eq(&self.inner, &other.inner) {
|
||||
if Arc::ptr_eq(&self.0, &other.0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let s = self.inner.lock().unwrap();
|
||||
let o = other.inner.lock().unwrap();
|
||||
let s = self.0.lock().unwrap();
|
||||
let o = other.0.lock().unwrap();
|
||||
|
||||
*s == *o
|
||||
}
|
||||
|
@ -150,35 +155,28 @@ impl PartialEq for BrushCache {
|
|||
|
||||
impl Hash for BrushCache {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.inner.lock().unwrap().hash(state);
|
||||
self.0.lock().unwrap().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl BrushCache {
|
||||
pub fn new_proto() -> Self {
|
||||
Self {
|
||||
inner: Default::default(),
|
||||
proto: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_brush_plan(&self, background: Instance<Raster<CPU>>, input: &[BrushStroke]) -> BrushPlan {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.compute_brush_plan(background, input)
|
||||
}
|
||||
|
||||
pub fn cache_results(&self, input: Vec<BrushStroke>, blended_image: Instance<Raster<CPU>>, last_stroke_texture: Instance<Raster<CPU>>) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.cache_results(input, blended_image, last_stroke_texture)
|
||||
}
|
||||
|
||||
pub fn get_cached_brush(&self, style: &BrushStyle) -> Option<Raster<CPU>> {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
let inner = self.0.lock().unwrap();
|
||||
inner.brush_texture_cache.get(style).cloned()
|
||||
}
|
||||
|
||||
pub fn store_brush(&self, style: BrushStyle, brush: Raster<CPU>) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.brush_texture_cache.insert(style, brush);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -490,7 +490,7 @@ async fn to_artboard<Data: Into<GraphicGroupTable> + 'n>(
|
|||
}
|
||||
|
||||
#[node_macro::node(category(""))]
|
||||
async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboard: Artboard, node_path: Vec<NodeId>) -> ArtboardGroupTable {
|
||||
pub async fn append_artboard(_ctx: impl Ctx, mut artboards: ArtboardGroupTable, artboard: Artboard, node_path: Vec<NodeId>) -> ArtboardGroupTable {
|
||||
// Get the penultimate element of the node path, or None if the path is too short.
|
||||
// This is used to get the ID of the user-facing "Artboard" node (which encapsulates this internal "Append Artboard" node).
|
||||
let encapsulating_node_id = node_path.get(node_path.len().wrapping_sub(2)).copied();
|
||||
|
|
|
@ -22,6 +22,7 @@ pub mod ops;
|
|||
pub mod raster;
|
||||
pub mod raster_types;
|
||||
pub mod registry;
|
||||
pub mod render_complexity;
|
||||
pub mod structural;
|
||||
pub mod text;
|
||||
pub mod transform;
|
||||
|
|
61
node-graph/gcore/src/render_complexity.rs
Normal file
61
node-graph/gcore/src/render_complexity.rs
Normal file
|
@ -0,0 +1,61 @@
|
|||
use crate::instances::Instances;
|
||||
use crate::raster_types::{CPU, GPU, Raster};
|
||||
use crate::vector::VectorData;
|
||||
use crate::{Artboard, Color, GraphicElement};
|
||||
use glam::DVec2;
|
||||
|
||||
pub trait RenderComplexity {
|
||||
fn render_complexity(&self) -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: RenderComplexity> RenderComplexity for Instances<T> {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.instance_ref_iter().map(|instance| instance.instance.render_complexity()).fold(0, usize::saturating_add)
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Artboard {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.graphic_group.render_complexity()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for GraphicElement {
|
||||
fn render_complexity(&self) -> usize {
|
||||
match self {
|
||||
Self::GraphicGroup(instances) => instances.render_complexity(),
|
||||
Self::VectorData(instances) => instances.render_complexity(),
|
||||
Self::RasterDataCPU(instances) => instances.render_complexity(),
|
||||
Self::RasterDataGPU(instances) => instances.render_complexity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for VectorData {
|
||||
fn render_complexity(&self) -> usize {
|
||||
self.segment_domain.ids().len()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Raster<CPU> {
|
||||
fn render_complexity(&self) -> usize {
|
||||
(self.width * self.height / 500) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for Raster<GPU> {
|
||||
fn render_complexity(&self) -> usize {
|
||||
// GPU textures currently can't have a thumbnail
|
||||
usize::MAX
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderComplexity for String {}
|
||||
impl RenderComplexity for bool {}
|
||||
impl RenderComplexity for f32 {}
|
||||
impl RenderComplexity for f64 {}
|
||||
impl RenderComplexity for DVec2 {}
|
||||
impl RenderComplexity for Option<Color> {}
|
||||
impl RenderComplexity for Vec<Color> {}
|
|
@ -10,6 +10,7 @@ use graphene_core::instances::Instance;
|
|||
use graphene_core::math::quad::Quad;
|
||||
use graphene_core::raster::Image;
|
||||
use graphene_core::raster_types::{CPU, GPU, RasterDataTable};
|
||||
use graphene_core::render_complexity::RenderComplexity;
|
||||
use graphene_core::transform::{Footprint, Transform};
|
||||
use graphene_core::uuid::{NodeId, generate_uuid};
|
||||
use graphene_core::vector::VectorDataTable;
|
||||
|
@ -204,7 +205,7 @@ pub struct RenderMetadata {
|
|||
}
|
||||
|
||||
// TODO: Rename to "Graphical"
|
||||
pub trait GraphicElementRendered: BoundingBox {
|
||||
pub trait GraphicElementRendered: BoundingBox + RenderComplexity {
|
||||
fn render_svg(&self, render: &mut SvgRender, render_params: &RenderParams);
|
||||
|
||||
#[cfg(feature = "vello")]
|
||||
|
@ -1151,7 +1152,7 @@ impl GraphicElementRendered for GraphicElement {
|
|||
}
|
||||
|
||||
/// Used to stop rust complaining about upstream traits adding display implementations to `Option<Color>`. This would not be an issue as we control that crate.
|
||||
trait Primitive: std::fmt::Display + BoundingBox {}
|
||||
trait Primitive: std::fmt::Display + BoundingBox + RenderComplexity {}
|
||||
impl Primitive for String {}
|
||||
impl Primitive for bool {}
|
||||
impl Primitive for f32 {}
|
||||
|
|
|
@ -61,7 +61,7 @@ pub fn combined_message_attrs_impl(attr: TokenStream, input_item: TokenStream) -
|
|||
<#parent as ToDiscriminant>::Discriminant
|
||||
};
|
||||
|
||||
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, TransitiveChild)] });
|
||||
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, TransitiveChild, HierarchicalTree)] });
|
||||
input.attrs.push(syn::parse_quote! { #[parent(#parent, #parent::#variant)] });
|
||||
if parent_is_top {
|
||||
input.attrs.push(syn::parse_quote! { #[parent_is_top] });
|
||||
|
@ -97,7 +97,7 @@ pub fn combined_message_attrs_impl(attr: TokenStream, input_item: TokenStream) -
|
|||
fn top_level_impl(input_item: TokenStream) -> syn::Result<TokenStream> {
|
||||
let mut input = syn::parse2::<ItemEnum>(input_item)?;
|
||||
|
||||
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant)] });
|
||||
input.attrs.push(syn::parse_quote! { #[derive(ToDiscriminant, HierarchicalTree)] });
|
||||
input.attrs.push(syn::parse_quote! { #[discriminant_attr(derive(Debug, Copy, Clone, PartialEq, Eq, Hash, AsMessage))] });
|
||||
|
||||
for var in &mut input.variants {
|
||||
|
|
57
proc-macros/src/extract_fields.rs
Normal file
57
proc-macros/src/extract_fields.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
use crate::helpers::clean_rust_type_syntax;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{ToTokens, format_ident, quote};
|
||||
use syn::{Data, DeriveInput, Fields, Type, parse2};
|
||||
|
||||
pub fn derive_extract_field_impl(input: TokenStream) -> syn::Result<TokenStream> {
|
||||
let input = parse2::<DeriveInput>(input)?;
|
||||
let struct_name = &input.ident;
|
||||
let generics = &input.generics;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let fields = match &input.data {
|
||||
Data::Struct(data) => match &data.fields {
|
||||
Fields::Named(fields) => &fields.named,
|
||||
_ => return Err(syn::Error::new(Span::call_site(), "ExtractField only works on structs with named fields")),
|
||||
},
|
||||
_ => return Err(syn::Error::new(Span::call_site(), "ExtractField only works on structs")),
|
||||
};
|
||||
|
||||
let mut field_line = Vec::new();
|
||||
// Extract field names and types as strings at compile time
|
||||
let field_info = fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let ident = field.ident.as_ref().unwrap();
|
||||
let name = ident.to_string();
|
||||
let ty = clean_rust_type_syntax(field.ty.to_token_stream().to_string());
|
||||
let line = ident.span().start().line;
|
||||
field_line.push(line);
|
||||
(name, ty)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let field_str = field_info.into_iter().map(|(name, ty)| (format!("{}: {}", name, ty)));
|
||||
|
||||
let res = quote! {
|
||||
impl #impl_generics #struct_name #ty_generics #where_clause {
|
||||
pub fn field_types() -> Vec<(String, usize)> {
|
||||
vec![
|
||||
#((String::from(#field_str), #field_line)),*
|
||||
]
|
||||
}
|
||||
|
||||
pub fn print_field_types() {
|
||||
for (field, line) in Self::field_types() {
|
||||
println!("{} at line {}", field, line);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path() -> &'static str {
|
||||
file!()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
|
@ -42,6 +42,58 @@ pub fn two_segment_path(left_ident: Ident, right_ident: Ident) -> Path {
|
|||
Path { leading_colon: None, segments }
|
||||
}
|
||||
|
||||
pub fn clean_rust_type_syntax(input: String) -> String {
|
||||
let mut result = String::new();
|
||||
let mut chars = input.chars().peekable();
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
match c {
|
||||
'&' => {
|
||||
result.push('&');
|
||||
while let Some(' ') = chars.peek() {
|
||||
chars.next();
|
||||
}
|
||||
}
|
||||
'<' => {
|
||||
while let Some(' ') = result.chars().rev().next() {
|
||||
result.pop();
|
||||
}
|
||||
result.push('<');
|
||||
while let Some(' ') = chars.peek() {
|
||||
chars.next();
|
||||
}
|
||||
}
|
||||
'>' => {
|
||||
while let Some(' ') = result.chars().rev().next() {
|
||||
result.pop();
|
||||
}
|
||||
result.push('>');
|
||||
while let Some(' ') = chars.peek() {
|
||||
chars.next();
|
||||
}
|
||||
}
|
||||
':' => {
|
||||
if let Some(':') = chars.peek() {
|
||||
while let Some(' ') = result.chars().rev().next() {
|
||||
result.pop();
|
||||
}
|
||||
}
|
||||
result.push(':');
|
||||
chars.next();
|
||||
result.push(':');
|
||||
while let Some(' ') = chars.peek() {
|
||||
chars.next();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
result.push(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
73
proc-macros/src/hierarchical_tree.rs
Normal file
73
proc-macros/src/hierarchical_tree.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{ToTokens, quote};
|
||||
use syn::{Data, DeriveInput, Fields, Type, parse2};
|
||||
|
||||
pub fn generate_hierarchical_tree(input: TokenStream) -> syn::Result<TokenStream> {
|
||||
let input = parse2::<DeriveInput>(input)?;
|
||||
let input_type = &input.ident;
|
||||
|
||||
let data = match &input.data {
|
||||
Data::Enum(data) => data,
|
||||
_ => return Err(syn::Error::new(Span::call_site(), "Tried to derive HierarchicalTree for non-enum")),
|
||||
};
|
||||
|
||||
let build_message_tree = data.variants.iter().map(|variant| {
|
||||
let variant_type = &variant.ident;
|
||||
|
||||
let has_child = variant
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path().get_ident().is_some_and(|ident| ident == "sub_discriminant" || ident == "child"));
|
||||
|
||||
if has_child {
|
||||
if let Fields::Unnamed(fields) = &variant.fields {
|
||||
let field_type = &fields.unnamed.first().unwrap().ty;
|
||||
quote! {
|
||||
{
|
||||
let mut variant_tree = DebugMessageTree::new(stringify!(#variant_type));
|
||||
let field_name = stringify!(#field_type);
|
||||
const message_string: &str = "Message";
|
||||
if message_string == &field_name[field_name.len().saturating_sub(message_string.len())..] {
|
||||
// The field is a Message type, recursively build its tree
|
||||
let sub_tree = #field_type::build_message_tree();
|
||||
variant_tree.add_variant(sub_tree);
|
||||
}
|
||||
message_tree.add_variant(variant_tree);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
message_tree.add_variant(DebugMessageTree::new(stringify!(#variant_type)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let res = quote! {
|
||||
impl HierarchicalTree for #input_type {
|
||||
fn build_message_tree() -> DebugMessageTree {
|
||||
let mut message_tree = DebugMessageTree::new(stringify!(#input_type));
|
||||
#(#build_message_tree)*
|
||||
let message_handler_str = #input_type::message_handler_str();
|
||||
if message_handler_str.fields().len() > 0 {
|
||||
message_tree.add_message_handler_field(message_handler_str);
|
||||
}
|
||||
|
||||
let message_handler_data_str = #input_type::message_handler_data_str();
|
||||
if message_handler_data_str.fields().len() > 0 {
|
||||
message_tree.add_message_handler_data_field(message_handler_data_str);
|
||||
}
|
||||
|
||||
message_tree.set_path(file!());
|
||||
|
||||
message_tree
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
|
@ -3,17 +3,23 @@
|
|||
mod as_message;
|
||||
mod combined_message_attrs;
|
||||
mod discriminant;
|
||||
mod extract_fields;
|
||||
mod helper_structs;
|
||||
mod helpers;
|
||||
mod hierarchical_tree;
|
||||
mod hint;
|
||||
mod message_handler_data_attr;
|
||||
mod transitive_child;
|
||||
mod widget_builder;
|
||||
|
||||
use crate::as_message::derive_as_message_impl;
|
||||
use crate::combined_message_attrs::combined_message_attrs_impl;
|
||||
use crate::discriminant::derive_discriminant_impl;
|
||||
use crate::extract_fields::derive_extract_field_impl;
|
||||
use crate::helper_structs::AttrInnerSingleString;
|
||||
use crate::hierarchical_tree::generate_hierarchical_tree;
|
||||
use crate::hint::derive_hint_impl;
|
||||
use crate::message_handler_data_attr::message_handler_data_attr_impl;
|
||||
use crate::transitive_child::derive_transitive_child_impl;
|
||||
use crate::widget_builder::derive_widget_builder_impl;
|
||||
use proc_macro::TokenStream;
|
||||
|
@ -281,6 +287,21 @@ pub fn derive_widget_builder(input_item: TokenStream) -> TokenStream {
|
|||
TokenStream::from(derive_widget_builder_impl(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
|
||||
}
|
||||
|
||||
#[proc_macro_derive(HierarchicalTree)]
|
||||
pub fn derive_hierarchical_tree(input_item: TokenStream) -> TokenStream {
|
||||
TokenStream::from(generate_hierarchical_tree(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
|
||||
}
|
||||
|
||||
#[proc_macro_derive(ExtractField)]
|
||||
pub fn derive_extract_field(input_item: TokenStream) -> TokenStream {
|
||||
TokenStream::from(derive_extract_field_impl(input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn message_handler_data(attr: TokenStream, input_item: TokenStream) -> TokenStream {
|
||||
TokenStream::from(message_handler_data_attr_impl(attr.into(), input_item.into()).unwrap_or_else(|err| err.to_compile_error()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
118
proc-macros/src/message_handler_data_attr.rs
Normal file
118
proc-macros/src/message_handler_data_attr.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use crate::helpers::{call_site_ident, clean_rust_type_syntax};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{ToTokens, quote};
|
||||
use syn::{ItemImpl, Type, parse2, spanned::Spanned};
|
||||
|
||||
pub fn message_handler_data_attr_impl(attr: TokenStream, input_item: TokenStream) -> syn::Result<TokenStream> {
|
||||
// Parse the input as an impl block
|
||||
let impl_block = parse2::<ItemImpl>(input_item.clone())?;
|
||||
|
||||
let self_ty = &impl_block.self_ty;
|
||||
|
||||
let path = match &**self_ty {
|
||||
Type::Path(path) => &path.path,
|
||||
_ => return Err(syn::Error::new(Span::call_site(), "Expected impl implementation")),
|
||||
};
|
||||
|
||||
let input_type = path.segments.last().map(|s| &s.ident).unwrap();
|
||||
|
||||
// Extract the message type from the trait path
|
||||
let trait_path = match &impl_block.trait_ {
|
||||
Some((_, path, _)) => path,
|
||||
None => return Err(syn::Error::new(Span::call_site(), "Expected trait implementation")),
|
||||
};
|
||||
|
||||
// Get the trait generics (should be MessageHandler<M, D>)
|
||||
if let Some(segment) = trait_path.segments.last() {
|
||||
if segment.ident != "MessageHandler" {
|
||||
return Err(syn::Error::new(segment.ident.span(), "Expected MessageHandler trait"));
|
||||
}
|
||||
if let syn::PathArguments::AngleBracketed(args) = &segment.arguments {
|
||||
if args.args.len() >= 2 {
|
||||
// Extract the message type (M) and data type (D) from the trait params
|
||||
let message_type = &args.args[0];
|
||||
let data_type = &args.args[1];
|
||||
|
||||
// Check if the attribute is "CustomData"
|
||||
let is_custom_data = attr.to_string().contains("CustomData");
|
||||
|
||||
let impl_item = match data_type {
|
||||
syn::GenericArgument::Type(t) => {
|
||||
match t {
|
||||
syn::Type::Path(type_path) if !type_path.path.segments.is_empty() => {
|
||||
// Get just the base identifier (ToolMessageData) without generics
|
||||
let type_name = &type_path.path.segments.first().unwrap().ident;
|
||||
|
||||
if is_custom_data {
|
||||
quote! {
|
||||
#input_item
|
||||
impl #message_type {
|
||||
pub fn message_handler_data_str() -> MessageData {
|
||||
custom_data()
|
||||
}
|
||||
pub fn message_handler_str() -> MessageData {
|
||||
MessageData::new(format!("{}",stringify!(#input_type)), #input_type::field_types(), #input_type::path())
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
#input_item
|
||||
impl #message_type {
|
||||
pub fn message_handler_data_str() -> MessageData
|
||||
{
|
||||
MessageData::new(format!("{}",stringify!(#type_name)), #type_name::field_types(), #type_name::path())
|
||||
|
||||
}
|
||||
pub fn message_handler_str() -> MessageData {
|
||||
MessageData::new(format!("{}",stringify!(#input_type)), #input_type::field_types(), #input_type::path())
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Type::Tuple(_) => quote! {
|
||||
#input_item
|
||||
impl #message_type {
|
||||
pub fn message_handler_str() -> MessageData {
|
||||
MessageData::new(format!("{}",stringify!(#input_type)), #input_type::field_types(), #input_type::path())
|
||||
}
|
||||
}
|
||||
},
|
||||
syn::Type::Reference(type_reference) => {
|
||||
let message_type = call_site_ident(format!("{input_type}Message"));
|
||||
let type_ident = match &*type_reference.elem {
|
||||
syn::Type::Path(type_path) => &type_path.path.segments.first().unwrap().ident,
|
||||
_ => return Err(syn::Error::new(type_reference.elem.span(), "Expected type path")),
|
||||
};
|
||||
let tr = clean_rust_type_syntax(type_reference.to_token_stream().to_string());
|
||||
quote! {
|
||||
#input_item
|
||||
impl #message_type {
|
||||
pub fn message_handler_data_str() -> MessageData {
|
||||
MessageData::new(format!("{}", #tr),#type_ident::field_types(), #type_ident::path())
|
||||
}
|
||||
|
||||
pub fn message_handler_str() -> MessageData {
|
||||
MessageData::new(format!("{}",stringify!(#input_type)), #input_type::field_types(), #input_type::path())
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return Err(syn::Error::new(t.span(), "Unsupported type format")),
|
||||
}
|
||||
}
|
||||
|
||||
_ => quote! {
|
||||
#input_item
|
||||
},
|
||||
};
|
||||
return Ok(impl_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(input_item)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue