Bundle Graphite using Tauri (#873)
* Setup tauri component for graphite editor Integrate graphite into tauri app Split interpreted-executor out of graph-craft * Add gpu execution node * General Cleanup
6
.github/workflows/ci.yml
vendored
|
@ -27,6 +27,12 @@ jobs:
|
|||
cd frontend
|
||||
npm ci
|
||||
|
||||
- name: 🔧 Install libgtk
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: libgtk-3-dev libssl-dev build-essential curl wget libayatana-appindicator3-dev librsvg2-dev libsoup2.4-dev libjavascriptcoregtk-4.0-dev libwebkit2gtk-4.0-dev
|
||||
version: 1.0
|
||||
|
||||
- name: 🔼 Update Rust to latest stable
|
||||
run: |
|
||||
rustc --version
|
||||
|
|
3145
Cargo.lock
generated
|
@ -4,9 +4,11 @@ members = [
|
|||
"graphene",
|
||||
"proc-macros",
|
||||
"frontend/wasm",
|
||||
"frontend/src-tauri",
|
||||
"node-graph/gcore",
|
||||
"node-graph/gstd",
|
||||
"node-graph/graph-craft",
|
||||
"node-graph/interpreted-executor",
|
||||
"node-graph/borrow_stack",
|
||||
"libraries/dyn-any",
|
||||
"libraries/bezier-rs",
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
accepted = [
|
||||
"Apache-2.0",
|
||||
"MIT",
|
||||
"MIT-0",
|
||||
"BSD-2-Clause",
|
||||
"BSD-3-Clause",
|
||||
"BSD-2-Clause",
|
||||
"Zlib",
|
||||
"Unicode-DFS-2016",
|
||||
"ISC",
|
||||
"MPL-2.0",
|
||||
]
|
||||
ignore-build-dependencies = true
|
||||
ignore-dev-dependencies = true
|
||||
|
|
|
@ -49,6 +49,7 @@ notice = "warn"
|
|||
# output a note when they are encountered.
|
||||
ignore = [
|
||||
#"RUSTSEC-0000-0000",
|
||||
"RUSTSEC-2020-0071", # This has been fixed in the version of chrono we use
|
||||
]
|
||||
# Threshold for security vulnerabilities, any vulnerability with a CVSS score
|
||||
# lower than the range specified will be ignored. Note that ignored advisories
|
||||
|
@ -71,13 +72,14 @@ unlicensed = "deny"
|
|||
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
|
||||
allow = [
|
||||
"MIT",
|
||||
"MIT-0",
|
||||
"Apache-2.0",
|
||||
"BSD-3-Clause",
|
||||
"BSD-2-Clause",
|
||||
"Zlib",
|
||||
"Zlib",
|
||||
"Unicode-DFS-2016",
|
||||
"ISC",
|
||||
"MPL-2.0",
|
||||
#"Apache-2.0 WITH LLVM-exception",
|
||||
]
|
||||
# List of explicitly disallowed licenses
|
||||
|
|
|
@ -10,6 +10,9 @@ homepage = "https://graphite.rs"
|
|||
repository = "https://github.com/GraphiteEditor/Graphite"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[features]
|
||||
gpu = ["graph-craft/gpu", "interpreted-executor/gpu"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
bitflags = "1.2.1"
|
||||
|
@ -18,7 +21,7 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
serde_json = { version = "1.0" }
|
||||
graphite-proc-macros = { path = "../proc-macros" }
|
||||
bezier-rs = { path = "../libraries/bezier-rs" }
|
||||
glam = { version="0.17", features = ["serde"] }
|
||||
glam = { version="0.22", features = ["serde"] }
|
||||
rand_chacha = "0.3.1"
|
||||
spin = "0.9.2"
|
||||
kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
|
||||
|
@ -31,6 +34,7 @@ once_cell = "1.13.0" # Remove when `core::cell::OnceCell` is stabilized (<https:
|
|||
# Node graph
|
||||
image = { version = "0.24", default-features = false, features = ["bmp"] }
|
||||
graph-craft = { path = "../node-graph/graph-craft" }
|
||||
interpreted-executor = { path = "../node-graph/interpreted-executor" }
|
||||
borrow_stack = { path = "../node-graph/borrow_stack" }
|
||||
dyn-any = { path = "../libraries/dyn-any" }
|
||||
graphene-core = { path = "../node-graph/gcore" }
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::messages::layout::utility_types::layout_widget::SubLayout;
|
|||
use crate::messages::layout::utility_types::misc::LayoutTarget;
|
||||
use crate::messages::layout::utility_types::widgets::menu_widgets::MenuBarEntry;
|
||||
use crate::messages::portfolio::document::node_graph::{FrontendNode, FrontendNodeLink, FrontendNodeType};
|
||||
use crate::messages::portfolio::document::utility_types::layer_panel::{LayerPanelEntry, RawBuffer};
|
||||
use crate::messages::portfolio::document::utility_types::layer_panel::{JsRawBuffer, LayerPanelEntry, RawBuffer};
|
||||
use crate::messages::prelude::*;
|
||||
use crate::messages::tool::utility_types::HintData;
|
||||
|
||||
|
@ -156,6 +156,10 @@ pub enum FrontendMessage {
|
|||
#[serde(rename = "dataBuffer")]
|
||||
data_buffer: RawBuffer,
|
||||
},
|
||||
UpdateDocumentLayerTreeStructureJs {
|
||||
#[serde(rename = "dataBuffer")]
|
||||
data_buffer: JsRawBuffer,
|
||||
},
|
||||
UpdateDocumentModeLayout {
|
||||
#[serde(rename = "layoutTarget")]
|
||||
layout_target: LayoutTarget,
|
||||
|
|
|
@ -16,7 +16,7 @@ pub struct FrontendImageData {
|
|||
pub path: Vec<LayerId>,
|
||||
pub mime: String,
|
||||
#[serde(skip)]
|
||||
pub image_data: std::rc::Rc<Vec<u8>>,
|
||||
pub image_data: std::sync::Arc<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
|
|
|
@ -4,7 +4,6 @@ use crate::messages::prelude::*;
|
|||
pub use graphene::DocumentResponse;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
|
||||
|
@ -51,7 +50,7 @@ bitflags! {
|
|||
// (although we ignore the shift key, so the user doesn't have to press `Ctrl Shift +` on a US keyboard), even if the keyboard layout
|
||||
// is for a different locale where the `+` key is somewhere entirely different, shifted or not. This would then also work for numpad `+`.
|
||||
#[impl_message(Message, InputMapperMessage, KeyDown)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
|
||||
pub enum Key {
|
||||
// Writing system keys
|
||||
Digit0,
|
||||
|
@ -210,18 +209,6 @@ pub enum Key {
|
|||
NumKeys,
|
||||
}
|
||||
|
||||
impl Serialize for Key {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let key = format!("{:?}", self);
|
||||
let label = self.to_string();
|
||||
|
||||
let mut state = serializer.serialize_struct("KeyWithLabel", 2)?;
|
||||
state.serialize_field("key", &key)?;
|
||||
state.serialize_field("label", &label)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Key {
|
||||
// TODO: Relevant key labels should be localized when we get around to implementing localization/internationalization
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
|
||||
|
@ -308,6 +295,35 @@ impl fmt::Display for Key {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Key> for LayoutKey {
|
||||
fn from(key: Key) -> Self {
|
||||
Self {
|
||||
key: format!("{:?}", key),
|
||||
label: key.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
struct LayoutKey {
|
||||
key: String,
|
||||
label: String,
|
||||
}
|
||||
/*
|
||||
impl Serialize for Key {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let key = format!("{:?}", self.0);
|
||||
let label = self.0.to_string();
|
||||
|
||||
assert_eq!(serde_json::to_string(Key::KeyEscape), {"key": KeyEscape, "label": "Esc"});
|
||||
|
||||
let mut state = serializer.serialize_struct("KeyWithLabel", 2)?;
|
||||
state.serialize_field("key", &key)?;
|
||||
state.serialize_field("label", &label)?;
|
||||
state.end()
|
||||
}
|
||||
}*/
|
||||
|
||||
pub const NUMBER_OF_KEYS: usize = Key::NumKeys as usize;
|
||||
|
||||
/// Only `Key`s that exist on a physical keyboard should be used.
|
||||
|
@ -342,6 +358,22 @@ impl fmt::Display for KeysGroup {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<KeysGroup> for String {
|
||||
fn from(keys: KeysGroup) -> Self {
|
||||
let layout_keys: LayoutKeysGroup = keys.into();
|
||||
serde_json::to_string(&layout_keys).expect("Failed to serialize KeysGroup")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct LayoutKeysGroup(Vec<LayoutKey>);
|
||||
|
||||
impl From<KeysGroup> for LayoutKeysGroup {
|
||||
fn from(keys_group: KeysGroup) -> Self {
|
||||
Self(keys_group.0.into_iter().map(|key| key.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub enum MouseMotion {
|
||||
None,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::input_keyboard::{all_required_modifiers_pressed, KeysGroup};
|
||||
use super::input_keyboard::{all_required_modifiers_pressed, KeysGroup, LayoutKeysGroup};
|
||||
use crate::messages::input_mapper::default_mapping::default_mapping;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::{KeyStates, NUMBER_OF_KEYS};
|
||||
use crate::messages::prelude::*;
|
||||
|
@ -81,24 +81,27 @@ pub struct MappingEntry {
|
|||
pub enum ActionKeys {
|
||||
Action(MessageDiscriminant),
|
||||
#[serde(rename = "keys")]
|
||||
Keys(KeysGroup),
|
||||
Keys(LayoutKeysGroup),
|
||||
}
|
||||
|
||||
impl ActionKeys {
|
||||
pub fn to_keys(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) {
|
||||
pub fn to_keys(&mut self, action_input_mapping: &impl Fn(&MessageDiscriminant) -> Vec<KeysGroup>) -> String {
|
||||
match self {
|
||||
ActionKeys::Action(action) => {
|
||||
if let Some(keys) = action_input_mapping(action).get_mut(0) {
|
||||
let mut taken_keys = KeysGroup::default();
|
||||
std::mem::swap(keys, &mut taken_keys);
|
||||
|
||||
*self = ActionKeys::Keys(taken_keys);
|
||||
let description = taken_keys.to_string();
|
||||
*self = ActionKeys::Keys(taken_keys.into());
|
||||
description
|
||||
} else {
|
||||
*self = ActionKeys::Keys(KeysGroup::default());
|
||||
*self = ActionKeys::Keys(KeysGroup::default().into());
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
ActionKeys::Keys(keys) => {
|
||||
warn!("Calling `.to_keys()` on a `ActionKeys::Keys` is a mistake/bug. Keys are: {:?}.", keys);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::messages::layout::utility_types::misc::LayoutTarget;
|
|||
use crate::messages::prelude::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait PropertyHolder {
|
||||
fn properties(&self) -> Layout {
|
||||
|
@ -39,11 +39,9 @@ impl Layout {
|
|||
if let Layout::WidgetLayout(mut widget_layout) = self {
|
||||
// Function used multiple times later in this code block to convert `ActionKeys::Action` to `ActionKeys::Keys` and append its shortcut to the tooltip
|
||||
let apply_shortcut_to_tooltip = |tooltip_shortcut: &mut ActionKeys, tooltip: &mut String| {
|
||||
tooltip_shortcut.to_keys(action_input_mapping);
|
||||
|
||||
if let ActionKeys::Keys(keys) = tooltip_shortcut {
|
||||
let shortcut_text = keys.to_string();
|
||||
let shortcut_text = tooltip_shortcut.to_keys(action_input_mapping);
|
||||
|
||||
if let ActionKeys::Keys(_keys) = tooltip_shortcut {
|
||||
if !shortcut_text.is_empty() {
|
||||
if !tooltip.is_empty() {
|
||||
tooltip.push(' ');
|
||||
|
@ -279,12 +277,12 @@ impl WidgetHolder {
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct WidgetCallback<T> {
|
||||
pub callback: Rc<dyn Fn(&T) -> Message + 'static>,
|
||||
pub callback: Arc<dyn Fn(&T) -> Message + 'static + Send + Sync>,
|
||||
}
|
||||
|
||||
impl<T> WidgetCallback<T> {
|
||||
pub fn new(callback: impl Fn(&T) -> Message + 'static) -> Self {
|
||||
Self { callback: Rc::new(callback) }
|
||||
pub fn new(callback: impl Fn(&T) -> Message + 'static + Send + Sync) -> Self {
|
||||
Self { callback: Arc::new(callback) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ impl MenuBarEntry {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_action(callback: impl Fn(&()) -> Message + 'static) -> WidgetHolder {
|
||||
pub fn create_action(callback: impl Fn(&()) -> Message + 'static + Send + Sync) -> WidgetHolder {
|
||||
WidgetHolder::new(Widget::InvisibleStandinInput(InvisibleStandinInput {
|
||||
on_update: WidgetCallback::new(callback),
|
||||
}))
|
||||
|
|
|
@ -548,7 +548,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
|||
}
|
||||
.into(),
|
||||
);
|
||||
let image_data = std::rc::Rc::new(image_data);
|
||||
let image_data = std::sync::Arc::new(image_data);
|
||||
responses.push_back(
|
||||
FrontendMessage::UpdateImageData {
|
||||
document_id,
|
||||
|
|
|
@ -162,7 +162,7 @@ impl MessageHandler<NavigationMessage, (&Document, &InputPreprocessorMessageHand
|
|||
responses.push_back(
|
||||
FrontendMessage::UpdateInputHints {
|
||||
hint_data: HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
|
@ -244,7 +244,7 @@ impl MessageHandler<NavigationMessage, (&Document, &InputPreprocessorMessageHand
|
|||
responses.push_back(
|
||||
FrontendMessage::UpdateInputHints {
|
||||
hint_data: HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap Increments"),
|
||||
|
|
|
@ -18,6 +18,8 @@ pub enum FrontendGraphDataType {
|
|||
Raster,
|
||||
#[serde(rename = "color")]
|
||||
Color,
|
||||
#[serde(rename = "text")]
|
||||
Text,
|
||||
#[serde(rename = "vector")]
|
||||
Subpath,
|
||||
#[serde(rename = "number")]
|
||||
|
|
|
@ -2,14 +2,11 @@ use super::{node_properties, FrontendGraphDataType, FrontendNodeType};
|
|||
use crate::messages::layout::utility_types::layout_widget::{LayoutGroup, Widget, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::label_widgets::TextLabel;
|
||||
|
||||
use glam::DVec2;
|
||||
use graph_craft::concrete;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||
use graph_craft::proto::{NodeIdentifier, Type};
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_std::vector::subpath::Subpath;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use graphene_core::raster::Image;
|
||||
|
||||
pub struct DocumentInputType {
|
||||
pub name: &'static str,
|
||||
|
@ -39,7 +36,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Identity",
|
||||
category: "General",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
data_type: FrontendGraphDataType::General,
|
||||
|
@ -58,7 +55,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Input",
|
||||
category: "Meta",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
|
@ -70,7 +67,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Output",
|
||||
category: "Meta",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
|
@ -87,6 +84,21 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::no_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "GpuImage",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeIdentifier::new("graphene_std::executor::MapGpuSingleImageNode", &[concrete!("&TypeErasedNode")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType {
|
||||
name: "Path",
|
||||
data_type: FrontendGraphDataType::Text,
|
||||
default: NodeInput::value(TaggedValue::String(String::new()), true),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::gpu_map_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Invert RGB",
|
||||
category: "Image Adjustments",
|
||||
|
@ -98,7 +110,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Hue/Saturation",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::HueSaturationNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::HueSaturationNode", &[concrete!("&TypeErasedNode")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Hue Shift", TaggedValue::F64(0.), false),
|
||||
|
@ -111,7 +123,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Brightness/Contrast",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::BrightnessContrastNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::BrightnessContrastNode", &[concrete!("&TypeErasedNode")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Brightness", TaggedValue::F64(0.), false),
|
||||
|
@ -123,7 +135,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Gamma",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::GammaNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::GammaNode", &[concrete!("&TypeErasedNode")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Gamma", TaggedValue::F64(1.), false),
|
||||
|
@ -134,7 +146,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Opacity",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::OpacityNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::OpacityNode", &[concrete!("&TypeErasedNode")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Factor", TaggedValue::F64(1.), false),
|
||||
|
@ -145,7 +157,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Posterize",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::PosterizeNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::PosterizeNode", &[concrete!("&TypeErasedNode")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Value", TaggedValue::F64(5.), false),
|
||||
|
@ -156,7 +168,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Exposure",
|
||||
category: "Image Adjustments",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ExposureNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ExposureNode", &[concrete!("&TypeErasedNode")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Image", TaggedValue::Image(Image::empty()), true),
|
||||
DocumentInputType::new("Value", TaggedValue::F64(0.), false),
|
||||
|
@ -167,7 +179,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Add",
|
||||
category: "Math",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[concrete!("&TypeErasedNode")]),
|
||||
inputs: &[
|
||||
DocumentInputType::new("Input", TaggedValue::F64(0.), true),
|
||||
DocumentInputType::new("Addend", TaggedValue::F64(0.), true),
|
||||
|
@ -194,7 +206,7 @@ static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
|||
DocumentNodeType {
|
||||
name: "Path Generator",
|
||||
category: "Vector",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[concrete!("Any<'_>")]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "Path Data",
|
||||
data_type: FrontendGraphDataType::Subpath,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::messages::layout::utility_types::layout_widget::{LayoutGroup, Widget, WidgetCallback, WidgetHolder};
|
||||
use crate::messages::layout::utility_types::widgets::button_widgets::ParameterExposeButton;
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{NumberInput, NumberInputMode};
|
||||
use crate::messages::layout::utility_types::widgets::input_widgets::{NumberInput, NumberInputMode, TextInput};
|
||||
use crate::messages::prelude::NodeGraphMessage;
|
||||
|
||||
use glam::DVec2;
|
||||
|
@ -14,7 +14,7 @@ pub fn string_properties(text: impl Into<String>) -> Vec<LayoutGroup> {
|
|||
vec![LayoutGroup::Row { widgets: vec![widget] }]
|
||||
}
|
||||
|
||||
fn update_value<T, F: Fn(&T) -> TaggedValue + 'static>(value: F, node_id: NodeId, input_index: usize) -> WidgetCallback<T> {
|
||||
fn update_value<T, F: Fn(&T) -> TaggedValue + 'static + Send + Sync>(value: F, node_id: NodeId, input_index: usize) -> WidgetCallback<T> {
|
||||
WidgetCallback::new(move |number_input: &T| {
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node: node_id,
|
||||
|
@ -42,6 +42,32 @@ fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphDataType
|
|||
}))
|
||||
}
|
||||
|
||||
fn text_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str) -> Vec<WidgetHolder> {
|
||||
let input: &NodeInput = document_node.inputs.get(index).unwrap();
|
||||
|
||||
let mut widgets = vec![
|
||||
expose_widget(node_id, index, FrontendGraphDataType::Number, input.is_exposed()),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::text_widget(name),
|
||||
];
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::String(x),
|
||||
exposed: false,
|
||||
} = &document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::new(Widget::TextInput(TextInput {
|
||||
value: x.clone(),
|
||||
on_update: update_value(|x: &TextInput| TaggedValue::String(x.value.clone()), node_id, index),
|
||||
..TextInput::default()
|
||||
})),
|
||||
])
|
||||
}
|
||||
widgets
|
||||
}
|
||||
|
||||
fn number_range_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, range_min: Option<f64>, range_max: Option<f64>, unit: String, is_integer: bool) -> Vec<WidgetHolder> {
|
||||
let input: &NodeInput = document_node.inputs.get(index).unwrap();
|
||||
|
||||
|
@ -98,6 +124,12 @@ pub fn adjust_gamma_properties(document_node: &DocumentNode, node_id: NodeId) ->
|
|||
vec![LayoutGroup::Row { widgets: gamma }]
|
||||
}
|
||||
|
||||
pub fn gpu_map_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let map = text_widget(document_node, node_id, 1, "Map");
|
||||
|
||||
vec![LayoutGroup::Row { widgets: map }]
|
||||
}
|
||||
|
||||
pub fn multiply_opacity(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let gamma = number_range_widget(document_node, node_id, 1, "Factor", Some(0.), Some(1.), "".into(), false);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ use graphene::layers::text_layer::{FontCache, TextLayer};
|
|||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::f64::consts::PI;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn apply_transform_operation(layer: &Layer, transform_op: TransformOp, value: f64, font_cache: &FontCache) -> [f64; 6] {
|
||||
let transformation = match transform_op {
|
||||
|
@ -1457,7 +1457,7 @@ fn node_gradient_type(gradient: &Gradient) -> LayoutGroup {
|
|||
}
|
||||
|
||||
fn node_gradient_color(gradient: &Gradient, position: usize) -> LayoutGroup {
|
||||
let gradient_clone = Rc::new(gradient.clone());
|
||||
let gradient_clone = Arc::new(gradient.clone());
|
||||
let gradient_2 = gradient_clone.clone();
|
||||
let gradient_3 = gradient_clone.clone();
|
||||
let send_fill_message = move |new_gradient: Gradient| PropertiesPanelMessage::ModifyFill { fill: Fill::Gradient(new_gradient) }.into();
|
||||
|
|
|
@ -7,7 +7,7 @@ use glam::{DAffine2, DVec2};
|
|||
use serde::ser::SerializeStruct;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub struct RawBuffer(Vec<u8>);
|
||||
|
||||
impl From<Vec<u64>> for RawBuffer {
|
||||
|
@ -22,8 +22,15 @@ impl From<Vec<u64>> for RawBuffer {
|
|||
Self(v_from_raw)
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
|
||||
pub struct JsRawBuffer(Vec<u8>);
|
||||
|
||||
impl Serialize for RawBuffer {
|
||||
impl From<RawBuffer> for JsRawBuffer {
|
||||
fn from(buffer: RawBuffer) -> Self {
|
||||
Self(buffer.0)
|
||||
}
|
||||
}
|
||||
impl Serialize for JsRawBuffer {
|
||||
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
let mut buffer = serializer.serialize_struct("Buffer", 2)?;
|
||||
buffer.serialize_field("pointer", &(self.0.as_ptr() as usize))?;
|
||||
|
|
|
@ -420,12 +420,12 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
|||
size,
|
||||
} => {
|
||||
fn read_image(document: Option<&DocumentMessageHandler>, layer_path: &[LayerId], image_data: Vec<u8>, (width, height): (u32, u32)) -> Result<Vec<u8>, String> {
|
||||
use graphene_std::raster::Image;
|
||||
use graphene_core::raster::Image;
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use std::io::Cursor;
|
||||
|
||||
let data = image_data.chunks_exact(4).map(|v| graphene_core::raster::color::Color::from_rgba8(v[0], v[1], v[2], v[3])).collect();
|
||||
let image = graphene_std::raster::Image { width, height, data };
|
||||
let image = graphene_core::raster::Image { width, height, data };
|
||||
|
||||
let document = document.ok_or_else(|| "Invalid document".to_string())?;
|
||||
let layer = document.graphene_document.layer(layer_path).map_err(|e| format!("No layer: {e:?}"))?;
|
||||
|
@ -452,7 +452,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
|||
assert_ne!(proto_network.nodes.len(), 0, "No protonodes exist?");
|
||||
for (_id, node) in proto_network.nodes {
|
||||
info!("Inserting proto node {:?}", node);
|
||||
graph_craft::node_registry::push_node(node, &stack);
|
||||
interpreted_executor::node_registry::push_node(node, &stack);
|
||||
}
|
||||
|
||||
use borrow_stack::BorrowStack;
|
||||
|
@ -484,7 +484,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
|
|||
.into(),
|
||||
);
|
||||
let mime = "image/bmp".to_string();
|
||||
let image_data = std::rc::Rc::new(image_data);
|
||||
let image_data = std::sync::Arc::new(image_data);
|
||||
responses.push_back(
|
||||
FrontendMessage::UpdateImageData {
|
||||
document_id,
|
||||
|
|
|
@ -453,7 +453,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Backspace])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Backspace]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Delete Artboard"),
|
||||
|
@ -461,7 +461,7 @@ impl Fsm for ArtboardToolFsmState {
|
|||
}]),
|
||||
]),
|
||||
ArtboardToolFsmState::Dragging => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain to Axis"),
|
||||
|
@ -469,14 +469,14 @@ impl Fsm for ArtboardToolFsmState {
|
|||
}])]),
|
||||
ArtboardToolFsmState::Drawing | ArtboardToolFsmState::ResizingBounds => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
|
|
@ -191,14 +191,14 @@ impl Fsm for EllipseToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Circular"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
@ -207,14 +207,14 @@ impl Fsm for EllipseToolFsmState {
|
|||
])]),
|
||||
EllipseToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Circular"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
|
|
@ -178,7 +178,7 @@ impl Fsm for EyedropperToolFsmState {
|
|||
},
|
||||
])]),
|
||||
EyedropperToolFsmState::SamplingPrimary | EyedropperToolFsmState::SamplingSecondary => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Escape])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Escape]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Cancel"),
|
||||
|
|
|
@ -518,7 +518,7 @@ impl Fsm for GradientToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
|
@ -526,7 +526,7 @@ impl Fsm for GradientToolFsmState {
|
|||
},
|
||||
])]),
|
||||
GradientToolFsmState::Drawing => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
|
|
|
@ -190,14 +190,14 @@ impl Fsm for ImaginateToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
@ -206,14 +206,14 @@ impl Fsm for ImaginateToolFsmState {
|
|||
])]),
|
||||
ImaginateToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
|
|
@ -247,21 +247,21 @@ impl Fsm for LineToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Lock Angle"),
|
||||
|
@ -270,21 +270,21 @@ impl Fsm for LineToolFsmState {
|
|||
])]),
|
||||
LineToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Lock Angle"),
|
||||
|
|
|
@ -201,7 +201,7 @@ impl Fsm for NavigateToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Zoom Out"),
|
||||
|
@ -217,7 +217,7 @@ impl Fsm for NavigateToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap Increments"),
|
||||
|
@ -240,7 +240,7 @@ impl Fsm for NavigateToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
|
@ -249,14 +249,14 @@ impl Fsm for NavigateToolFsmState {
|
|||
]),
|
||||
]),
|
||||
NavigateToolFsmState::Tilting => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
}])]),
|
||||
NavigateToolFsmState::Zooming => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap Increments"),
|
||||
|
|
|
@ -190,14 +190,14 @@ impl Fsm for NodeGraphToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
@ -206,14 +206,14 @@ impl Fsm for NodeGraphToolFsmState {
|
|||
])]),
|
||||
NodeGraphToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
|
|
@ -322,7 +322,7 @@ impl Fsm for PathToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grow/Shrink Selection"),
|
||||
|
@ -339,10 +339,10 @@ impl Fsm for PathToolFsmState {
|
|||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![
|
||||
KeysGroup(vec![Key::ArrowUp]),
|
||||
KeysGroup(vec![Key::ArrowRight]),
|
||||
KeysGroup(vec![Key::ArrowDown]),
|
||||
KeysGroup(vec![Key::ArrowLeft]),
|
||||
KeysGroup(vec![Key::ArrowUp]).into(),
|
||||
KeysGroup(vec![Key::ArrowRight]).into(),
|
||||
KeysGroup(vec![Key::ArrowDown]).into(),
|
||||
KeysGroup(vec![Key::ArrowLeft]).into(),
|
||||
],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
|
@ -350,7 +350,7 @@ impl Fsm for PathToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Big Increment Nudge"),
|
||||
|
@ -359,21 +359,21 @@ impl Fsm for PathToolFsmState {
|
|||
]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyG])],
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyG]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grab Selected (coming soon)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyR])],
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyR]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Rotate Selected (coming soon)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyS])],
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyS]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Scale Selected (coming soon)"),
|
||||
|
@ -383,14 +383,14 @@ impl Fsm for PathToolFsmState {
|
|||
]),
|
||||
PathToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Split/Align Handles (Toggle)"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Share Lengths of Aligned Handles"),
|
||||
|
|
|
@ -636,21 +636,21 @@ impl Fsm for PenToolFsmState {
|
|||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Break Handle"),
|
||||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Enter])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Enter]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("End Path"),
|
||||
|
|
|
@ -192,14 +192,14 @@ impl Fsm for RectangleToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
@ -208,14 +208,14 @@ impl Fsm for RectangleToolFsmState {
|
|||
])]),
|
||||
RectangleToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain Square"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
|
|
@ -793,21 +793,21 @@ impl Fsm for SelectToolFsmState {
|
|||
}]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyG])],
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyG]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grab Selected"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyR])],
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyR]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Rotate Selected"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyS])],
|
||||
key_groups: vec![KeysGroup(vec![Key::KeyS]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Scale Selected"),
|
||||
|
@ -823,14 +823,14 @@ impl Fsm for SelectToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command])]),
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command]).into()]),
|
||||
mouse: None,
|
||||
label: String::from("Innermost"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grow/Shrink Selection"),
|
||||
|
@ -846,7 +846,7 @@ impl Fsm for SelectToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Grow/Shrink Selection"),
|
||||
|
@ -856,10 +856,10 @@ impl Fsm for SelectToolFsmState {
|
|||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![
|
||||
KeysGroup(vec![Key::ArrowUp]),
|
||||
KeysGroup(vec![Key::ArrowRight]),
|
||||
KeysGroup(vec![Key::ArrowDown]),
|
||||
KeysGroup(vec![Key::ArrowLeft]),
|
||||
KeysGroup(vec![Key::ArrowUp]).into(),
|
||||
KeysGroup(vec![Key::ArrowRight]).into(),
|
||||
KeysGroup(vec![Key::ArrowDown]).into(),
|
||||
KeysGroup(vec![Key::ArrowLeft]).into(),
|
||||
],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
|
@ -867,7 +867,7 @@ impl Fsm for SelectToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Big Increment Nudge"),
|
||||
|
@ -876,15 +876,15 @@ impl Fsm for SelectToolFsmState {
|
|||
]),
|
||||
HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: Some(MouseMotion::LmbDrag),
|
||||
label: String::from("Move Duplicate"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control, Key::KeyD])],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::KeyD])]),
|
||||
key_groups: vec![KeysGroup(vec![Key::Control, Key::KeyD]).into()],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::KeyD]).into()]),
|
||||
mouse: None,
|
||||
label: String::from("Duplicate"),
|
||||
plus: false,
|
||||
|
@ -893,14 +893,14 @@ impl Fsm for SelectToolFsmState {
|
|||
]),
|
||||
SelectToolFsmState::Dragging => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain to Axis"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap to Points (coming soon)"),
|
||||
|
@ -910,7 +910,7 @@ impl Fsm for SelectToolFsmState {
|
|||
SelectToolFsmState::DrawingBox => HintData(vec![]),
|
||||
SelectToolFsmState::ResizingBounds => HintData(vec![]),
|
||||
SelectToolFsmState::RotatingBounds => HintData(vec![HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Control]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Snap 15°"),
|
||||
|
|
|
@ -235,14 +235,14 @@ impl Fsm for ShapeToolFsmState {
|
|||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain 1:1 Aspect"),
|
||||
plus: true,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
@ -251,14 +251,14 @@ impl Fsm for ShapeToolFsmState {
|
|||
])]),
|
||||
ShapeToolFsmState::Drawing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Shift]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Constrain 1:1 Aspect"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Alt]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("From Center"),
|
||||
|
|
|
@ -267,7 +267,7 @@ impl Fsm for SplineToolFsmState {
|
|||
plus: false,
|
||||
}]),
|
||||
HintGroup(vec![HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Enter])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Enter]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("End Spline"),
|
||||
|
|
|
@ -474,14 +474,14 @@ impl Fsm for TextToolFsmState {
|
|||
])]),
|
||||
TextToolFsmState::Editing => HintData(vec![HintGroup(vec![
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Control, Key::Enter])],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::Enter])]),
|
||||
key_groups: vec![KeysGroup(vec![Key::Control, Key::Enter]).into()],
|
||||
key_groups_mac: Some(vec![KeysGroup(vec![Key::Command, Key::Enter]).into()]),
|
||||
mouse: None,
|
||||
label: String::from("Commit Edit"),
|
||||
plus: false,
|
||||
},
|
||||
HintInfo {
|
||||
key_groups: vec![KeysGroup(vec![Key::Escape])],
|
||||
key_groups: vec![KeysGroup(vec![Key::Escape]).into()],
|
||||
key_groups_mac: None,
|
||||
mouse: None,
|
||||
label: String::from("Discard Edit"),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::tool_messages::*;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::KeysGroup;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::LayoutKeysGroup;
|
||||
use crate::messages::input_mapper::utility_types::input_keyboard::MouseMotion;
|
||||
use crate::messages::input_mapper::utility_types::macros::action_keys;
|
||||
use crate::messages::input_mapper::utility_types::misc::ActionKeys;
|
||||
|
@ -21,7 +21,7 @@ pub type ToolActionHandlerData<'a> = (&'a DocumentMessageHandler, u64, &'a Docum
|
|||
pub trait ToolCommon: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder + ToolTransition + ToolMetadata {}
|
||||
impl<T> ToolCommon for T where T: for<'a> MessageHandler<ToolMessage, ToolActionHandlerData<'a>> + PropertyHolder + ToolTransition + ToolMetadata {}
|
||||
|
||||
type Tool = dyn ToolCommon;
|
||||
type Tool = dyn ToolCommon + Send + Sync;
|
||||
|
||||
pub trait Fsm {
|
||||
type ToolData;
|
||||
|
@ -442,10 +442,10 @@ pub struct HintInfo {
|
|||
/// A `KeysGroup` specifies all the keys pressed simultaneously to perform an action (like "Ctrl C" to copy).
|
||||
/// Usually at most one is given, but less commonly, multiple can be used to describe additional hotkeys not used simultaneously (like the four different arrow keys to nudge a layer).
|
||||
#[serde(rename = "keyGroups")]
|
||||
pub key_groups: Vec<KeysGroup>,
|
||||
pub key_groups: Vec<LayoutKeysGroup>,
|
||||
/// `None` means that the regular `key_groups` should be used for all platforms, `Some` is an override for a Mac-only input hint.
|
||||
#[serde(rename = "keyGroupsMac")]
|
||||
pub key_groups_mac: Option<Vec<KeysGroup>>,
|
||||
pub key_groups_mac: Option<Vec<LayoutKeysGroup>>,
|
||||
/// An optional `MouseMotion` that can indicate the mouse action, like which mouse button is used and whether a drag occurs.
|
||||
/// No such icon is shown if `None` is given, and it can be combined with `key_groups` if desired.
|
||||
pub mouse: Option<MouseMotion>,
|
||||
|
|
1380
frontend/package-lock.json
generated
|
@ -7,7 +7,9 @@
|
|||
"serve": "vue-cli-service serve || echo 'Graphite project failed to build. Did you remember to `npm install` the dependencies?'",
|
||||
"build": "vue-cli-service build || echo 'Graphite project failed to build. Did you remember to `npm install` the dependencies?'",
|
||||
"lint": "vue-cli-service lint || echo 'Graphite project had lint errors or otherwise failed. In the latter case, did you remember to `npm install` the dependencies?'",
|
||||
"lint-no-fix": "vue-cli-service lint --no-fix || echo 'Graphite project had lint errors or otherwise failed. In the latter case, did you remember to `npm install` the dependencies?'"
|
||||
"lint-no-fix": "vue-cli-service lint --no-fix || echo 'Graphite project had lint errors or otherwise failed. In the latter case, did you remember to `npm install` the dependencies?'",
|
||||
"tauri:build": "vue-cli-service tauri:build",
|
||||
"tauri:serve": "vue-cli-service tauri:serve"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -17,6 +19,7 @@
|
|||
"license": "Apache-2.0",
|
||||
"homepage": "https://graphite.rs",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.2.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"idb-keyval": "^6.2.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
|
@ -42,6 +45,7 @@
|
|||
"sass": "^1.56.1",
|
||||
"sass-loader": "^13.2.0",
|
||||
"typescript": "^4.9.3",
|
||||
"vue-cli-plugin-tauri": "~1.0.0",
|
||||
"vue-loader": "^17.0.1",
|
||||
"vue-template-compiler": "^2.7.14"
|
||||
},
|
||||
|
|
3
frontend/src-tauri/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Generated by Cargo
|
||||
# will have compiled files and executables
|
||||
/target/
|
38
frontend/src-tauri/Cargo.toml
Normal file
|
@ -0,0 +1,38 @@
|
|||
[package]
|
||||
name = "graphite-desktop"
|
||||
version = "0.1.0"
|
||||
description = "Graphite Desktop"
|
||||
authors = ["Graphite Authors <contact@graphite.rs>"]
|
||||
license = "Apache-2.0"
|
||||
repository = ""
|
||||
default-run = "graphite-desktop"
|
||||
edition = "2021"
|
||||
rust-version = "1.59"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.2", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.2", features = ["api-all", "devtools"] }
|
||||
axum = "0.6.1"
|
||||
graphite-editor = { version = "0.0.0", path = "../../editor" }
|
||||
chrono = "^0.4.23"
|
||||
ron = "0.8"
|
||||
|
||||
log = "0.4"
|
||||
fern = {version = "0.6", features = ["colored"] }
|
||||
futures = "0.3.25"
|
||||
|
||||
|
||||
[features]
|
||||
gpu = ["graphite-editor/gpu"]
|
||||
# by default Tauri runs in production mode
|
||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||
default = [ "custom-protocol" ]
|
||||
# this feature is used for production builds where `devPath` points to the filesystem
|
||||
# DO NOT remove this
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
3
frontend/src-tauri/build.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
tauri_build::build()
|
||||
}
|
BIN
frontend/src-tauri/icons/128x128.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
frontend/src-tauri/icons/128x128@2x.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
frontend/src-tauri/icons/32x32.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
frontend/src-tauri/icons/Square107x107Logo.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
frontend/src-tauri/icons/Square142x142Logo.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
frontend/src-tauri/icons/Square150x150Logo.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
frontend/src-tauri/icons/Square284x284Logo.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
frontend/src-tauri/icons/Square30x30Logo.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
frontend/src-tauri/icons/Square310x310Logo.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
frontend/src-tauri/icons/Square44x44Logo.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
frontend/src-tauri/icons/Square71x71Logo.png
Normal file
After Width: | Height: | Size: 7 KiB |
BIN
frontend/src-tauri/icons/Square89x89Logo.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
frontend/src-tauri/icons/StoreLogo.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
frontend/src-tauri/icons/icon.icns
Normal file
BIN
frontend/src-tauri/icons/icon.ico
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
frontend/src-tauri/icons/icon.png
Normal file
After Width: | Height: | Size: 34 KiB |
125
frontend/src-tauri/src/main.rs
Normal file
|
@ -0,0 +1,125 @@
|
|||
#![cfg_attr(all(not(debug_assertions), target_os = "windows"), windows_subsystem = "windows")]
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use axum::body::StreamBody;
|
||||
use axum::extract::Path;
|
||||
use axum::http;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::{routing::get, Router};
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
use graphite_editor::application::Editor;
|
||||
use graphite_editor::messages::frontend::utility_types::FrontendImageData;
|
||||
use graphite_editor::messages::prelude::*;
|
||||
use http::{Response, StatusCode};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use tauri::Manager;
|
||||
|
||||
static IMAGES: Mutex<Option<HashMap<String, FrontendImageData>>> = Mutex::new(None);
|
||||
static EDITOR: Mutex<Option<Editor>> = Mutex::new(None);
|
||||
|
||||
async fn respond_to(id: Path<String>) -> impl IntoResponse {
|
||||
let builder = Response::builder().header("Access-Control-Allow-Origin", "*").status(StatusCode::OK);
|
||||
|
||||
let guard = IMAGES.lock().unwrap();
|
||||
let images = guard;
|
||||
let image = images.as_ref().unwrap().get(&id.0).unwrap();
|
||||
|
||||
println!("image: {:#?}", image.path);
|
||||
let result: Result<Vec<u8>, &str> = Ok((*image.image_data).clone());
|
||||
let stream = futures::stream::once(async move { result });
|
||||
builder.body(StreamBody::new(stream)).unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("Starting server...");
|
||||
|
||||
let colors = ColoredLevelConfig::new().debug(Color::Magenta).info(Color::Green).error(Color::Red);
|
||||
|
||||
fern::Dispatch::new()
|
||||
.chain(std::io::stdout())
|
||||
.level(log::LevelFilter::Trace)
|
||||
.format(move |out, message, record| {
|
||||
out.finish(format_args!(
|
||||
"[{}]{} {}",
|
||||
// This will color the log level only, not the whole line. Just a touch.
|
||||
colors.color(record.level()),
|
||||
chrono::Utc::now().format("[%Y-%m-%d %H:%M:%S]"),
|
||||
message
|
||||
))
|
||||
})
|
||||
.apply()
|
||||
.unwrap();
|
||||
|
||||
*(IMAGES.lock().unwrap()) = Some(HashMap::new());
|
||||
graphite_editor::application::set_uuid_seed(0);
|
||||
*(EDITOR.lock().unwrap()) = Some(Editor::new());
|
||||
let app = Router::new().route("/", get(|| async { "Hello, World!" })).route("/image/:id", get(respond_to));
|
||||
|
||||
// run it with hyper on localhost:3000
|
||||
tauri::async_runtime::spawn(async {
|
||||
axum::Server::bind(&"0.0.0.0:3001".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
||||
});
|
||||
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![set_random_seed, handle_message])
|
||||
.setup(|app| {
|
||||
app.get_window("main").unwrap().open_devtools();
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
#[tauri::command]
|
||||
fn set_random_seed(seed: f64) {
|
||||
let seed = seed as u64;
|
||||
graphite_editor::application::set_uuid_seed(seed);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn handle_message(message: String) -> String {
|
||||
let Ok(message) = ron::from_str::<graphite_editor::messages::message::Message>(&message) else {
|
||||
panic!("Error parsing message: {}", message)
|
||||
};
|
||||
let mut guard = EDITOR.lock().unwrap();
|
||||
let editor = (*guard).as_mut().unwrap();
|
||||
let responses = editor.handle_message(message);
|
||||
|
||||
// Sends a FrontendMessage to JavaScript
|
||||
fn send_frontend_message_to_js(message: FrontendMessage) -> FrontendMessage {
|
||||
// Special case for update image data to avoid serialization times.
|
||||
if let FrontendMessage::UpdateImageData { document_id, image_data } = message {
|
||||
let mut guard = IMAGES.lock().unwrap();
|
||||
let images = (*guard).as_mut().unwrap();
|
||||
let mut stub_data = Vec::with_capacity(image_data.len());
|
||||
for image in image_data {
|
||||
let path = image.path.clone();
|
||||
let mime = image.mime.clone();
|
||||
images.insert(format!("{:?}_{}", &image.path, document_id), image);
|
||||
stub_data.push(FrontendImageData {
|
||||
path,
|
||||
mime,
|
||||
image_data: Arc::new(Vec::new()),
|
||||
});
|
||||
}
|
||||
FrontendMessage::UpdateImageData { document_id, image_data: stub_data }
|
||||
} else {
|
||||
message
|
||||
}
|
||||
}
|
||||
|
||||
for response in &responses {
|
||||
let serialized = ron::to_string(&send_frontend_message_to_js(response.clone())).unwrap();
|
||||
if let Err(error) = ron::from_str::<FrontendMessage>(&serialized) {
|
||||
log::error!("Error deserializing message: {}", error);
|
||||
log::debug!("{:#?}", response);
|
||||
log::debug!("{}", serialized);
|
||||
}
|
||||
}
|
||||
|
||||
// Process any `FrontendMessage` responses resulting from the backend processing the dispatched message
|
||||
let result: Vec<_> = responses.into_iter().map(send_frontend_message_to_js).collect();
|
||||
|
||||
ron::to_string(&result).expect("Failed to serialize FrontendMessage")
|
||||
}
|
66
frontend/src-tauri/tauri.conf.json
Normal file
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
|
||||
"build": {
|
||||
"beforeBuildCommand": "npm run build",
|
||||
"beforeDevCommand": "npm start",
|
||||
"distDir": "../dist",
|
||||
"devPath": "http://127.0.0.1:8080"
|
||||
},
|
||||
"package": {
|
||||
"productName": "graphite-tauri",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": true
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"category": "DeveloperTool",
|
||||
"copyright": "",
|
||||
"deb": {
|
||||
"depends": ["librustc_codegen_spirv"]
|
||||
},
|
||||
"externalBin": [],
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
],
|
||||
"identifier": "rs.graphite.editor",
|
||||
"longDescription": "",
|
||||
"macOS": {
|
||||
"entitlements": null,
|
||||
"exceptionDomain": "",
|
||||
"frameworks": [],
|
||||
"providerShortName": null,
|
||||
"signingIdentity": null
|
||||
},
|
||||
"resources": [],
|
||||
"shortDescription": "",
|
||||
"targets": "all",
|
||||
"windows": {
|
||||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": "sha256",
|
||||
"timestampUrl": ""
|
||||
}
|
||||
},
|
||||
"security": {
|
||||
"csp": null
|
||||
},
|
||||
"updater": {
|
||||
"active": false
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"fullscreen": false,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"title": "Graphite",
|
||||
"width": 800
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -280,7 +280,7 @@ import {
|
|||
type LayerPanelEntry,
|
||||
defaultWidgetLayout,
|
||||
UpdateDocumentLayerDetails,
|
||||
UpdateDocumentLayerTreeStructure,
|
||||
UpdateDocumentLayerTreeStructureJs,
|
||||
UpdateLayerTreeOptionsLayout,
|
||||
layerTypeData,
|
||||
} from "@/wasm-communication/messages";
|
||||
|
@ -483,14 +483,14 @@ export default defineComponent({
|
|||
this.fakeHighlight = undefined;
|
||||
this.dragInPanel = false;
|
||||
},
|
||||
rebuildLayerTree(updateDocumentLayerTreeStructure: UpdateDocumentLayerTreeStructure) {
|
||||
rebuildLayerTree(updateDocumentLayerTreeStructure: UpdateDocumentLayerTreeStructureJs) {
|
||||
const layerWithNameBeingEdited = this.layers.find((layer: LayerListingInfo) => layer.editingName);
|
||||
const layerPathWithNameBeingEdited = layerWithNameBeingEdited?.entry.path;
|
||||
const layerIdWithNameBeingEdited = layerPathWithNameBeingEdited?.slice(-1)[0];
|
||||
const path = [] as bigint[];
|
||||
this.layers = [] as LayerListingInfo[];
|
||||
|
||||
const recurse = (folder: UpdateDocumentLayerTreeStructure, layers: LayerListingInfo[], cache: Map<string, LayerPanelEntry>): void => {
|
||||
const recurse = (folder: UpdateDocumentLayerTreeStructureJs, layers: LayerListingInfo[], cache: Map<string, LayerPanelEntry>): void => {
|
||||
folder.children.forEach((item, index) => {
|
||||
// TODO: fix toString
|
||||
const layerId = BigInt(item.layerId.toString());
|
||||
|
@ -520,7 +520,7 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
mounted() {
|
||||
this.editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerTreeStructure, (updateDocumentLayerTreeStructure) => {
|
||||
this.editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerTreeStructureJs, (updateDocumentLayerTreeStructure) => {
|
||||
this.rebuildLayerTree(updateDocumentLayerTreeStructure);
|
||||
});
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@
|
|||
import { defineComponent } from "vue";
|
||||
|
||||
import { platformIsMac } from "@/utility-functions/platform";
|
||||
import { type KeyRaw, type KeysGroup, type MenuBarEntry, type MenuListEntry, UpdateMenuBarLayout } from "@/wasm-communication/messages";
|
||||
import { type KeyRaw, type LayoutKeysGroup, type MenuBarEntry, type MenuListEntry, UpdateMenuBarLayout } from "@/wasm-communication/messages";
|
||||
|
||||
import MenuList from "@/components/floating-menus/MenuList.vue";
|
||||
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
|
@ -92,7 +92,7 @@ export default defineComponent({
|
|||
mounted() {
|
||||
this.editor.subscriptions.subscribeJsMessage(UpdateMenuBarLayout, (updateMenuBarLayout) => {
|
||||
const arraysEqual = (a: KeyRaw[], b: KeyRaw[]): boolean => a.length === b.length && a.every((aValue, i) => aValue === b[i]);
|
||||
const shortcutRequiresLock = (shortcut: KeysGroup): boolean => {
|
||||
const shortcutRequiresLock = (shortcut: LayoutKeysGroup): boolean => {
|
||||
const shortcutKeys = shortcut.map((keyWithLabel) => keyWithLabel.key);
|
||||
|
||||
// If this shortcut matches any of the browser-reserved shortcuts
|
||||
|
|
|
@ -127,7 +127,7 @@ import { defineComponent, type PropType } from "vue";
|
|||
|
||||
import { type IconName } from "@/utility-functions/icons";
|
||||
import { platformIsMac } from "@/utility-functions/platform";
|
||||
import { type KeyRaw, type KeysGroup, type Key, type MouseMotion } from "@/wasm-communication/messages";
|
||||
import { type KeyRaw, type LayoutKeysGroup, type Key, type MouseMotion } from "@/wasm-communication/messages";
|
||||
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
import IconLabel from "@/components/widgets/labels/IconLabel.vue";
|
||||
|
@ -158,7 +158,7 @@ const ICON_WIDTHS = {
|
|||
export default defineComponent({
|
||||
inject: ["fullscreen"],
|
||||
props: {
|
||||
keysWithLabelsGroups: { type: Array as PropType<KeysGroup[]>, default: () => [] },
|
||||
keysWithLabelsGroups: { type: Array as PropType<LayoutKeysGroup[]>, default: () => [] },
|
||||
mouseMotion: { type: String as PropType<MouseMotion | undefined>, required: false },
|
||||
requiresLock: { type: Boolean as PropType<boolean>, default: false },
|
||||
},
|
||||
|
@ -182,7 +182,7 @@ export default defineComponent({
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
keyTextOrIconList(keyGroup: KeysGroup): LabelData[] {
|
||||
keyTextOrIconList(keyGroup: LayoutKeysGroup): LabelData[] {
|
||||
return keyGroup.map((key) => this.keyTextOrIcon(key));
|
||||
},
|
||||
keyTextOrIcon(keyWithLabel: Key): LabelData {
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
import { defineComponent } from "vue";
|
||||
|
||||
import { platformIsMac } from "@/utility-functions/platform";
|
||||
import { type HintData, type HintInfo, type KeysGroup, UpdateInputHints } from "@/wasm-communication/messages";
|
||||
import { type HintData, type HintInfo, type LayoutKeysGroup, UpdateInputHints } from "@/wasm-communication/messages";
|
||||
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
import Separator from "@/components/widgets/labels/Separator.vue";
|
||||
|
@ -63,7 +63,7 @@ export default defineComponent({
|
|||
};
|
||||
},
|
||||
methods: {
|
||||
inputKeysForPlatform(hint: HintInfo): KeysGroup[] {
|
||||
inputKeysForPlatform(hint: HintInfo): LayoutKeysGroup[] {
|
||||
if (platformIsMac() && hint.keyGroupsMac) return hint.keyGroupsMac;
|
||||
return hint.keyGroups;
|
||||
},
|
||||
|
|
|
@ -214,7 +214,7 @@ import { defineComponent, nextTick, type PropType } from "vue";
|
|||
|
||||
import { platformIsMac } from "@/utility-functions/platform";
|
||||
|
||||
import { type KeysGroup, type Key } from "@/wasm-communication/messages";
|
||||
import { type LayoutKeysGroup, type Key } from "@/wasm-communication/messages";
|
||||
|
||||
import LayoutCol from "@/components/layout/LayoutCol.vue";
|
||||
import LayoutRow from "@/components/layout/LayoutRow.vue";
|
||||
|
@ -258,7 +258,7 @@ export default defineComponent({
|
|||
openDocument() {
|
||||
this.editor.instance.documentOpen();
|
||||
},
|
||||
platformModifiers(reservedKey: boolean): KeysGroup {
|
||||
platformModifiers(reservedKey: boolean): LayoutKeysGroup {
|
||||
// TODO: Remove this by properly feeding these keys from a layout provided by the backend
|
||||
|
||||
const ALT: Key = { key: "Alt", label: "Alt" };
|
||||
|
|
|
@ -30,7 +30,7 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
|||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const listeners: { target: EventListenerTarget; eventName: EventName; action: (event: any) => void; options?: boolean | AddEventListenerOptions }[] = [
|
||||
{ target: window, eventName: "resize", action: (): void => onWindowResize(container) },
|
||||
{ target: window, eventName: "beforeunload", action: (e: BeforeUnloadEvent): void => onBeforeUnload(e) },
|
||||
{ target: window, eventName: "beforeunload", action: (e: BeforeUnloadEvent): Promise<void> => onBeforeUnload(e) },
|
||||
{ target: window.document, eventName: "contextmenu", action: (e: MouseEvent): void => e.preventDefault() },
|
||||
{ target: window.document, eventName: "fullscreenchange", action: (): void => fullscreen.fullscreenModeChanged() },
|
||||
{ target: window, eventName: "keyup", action: (e: KeyboardEvent): Promise<void> => onKeyUp(e) },
|
||||
|
@ -235,15 +235,15 @@ export function createInputManager(editor: Editor, container: HTMLElement, dialo
|
|||
if (boundsOfViewports.length > 0) editor.instance.boundsOfViewports(data);
|
||||
}
|
||||
|
||||
function onBeforeUnload(e: BeforeUnloadEvent): void {
|
||||
async function onBeforeUnload(e: BeforeUnloadEvent): Promise<void> {
|
||||
const activeDocument = document.state.documents[document.state.activeDocumentIndex];
|
||||
if (activeDocument && !activeDocument.isAutoSaved) editor.instance.triggerAutoSave(activeDocument.id);
|
||||
|
||||
// Skip the message if the editor crashed, since work is already lost
|
||||
if (editor.instance.hasCrashed()) return;
|
||||
if (await editor.instance.hasCrashed()) return;
|
||||
|
||||
// Skip the message during development, since it's annoying when testing
|
||||
if (editor.instance.inDevelopmentMode()) return;
|
||||
if (await editor.instance.inDevelopmentMode()) return;
|
||||
|
||||
const allDocumentsSaved = document.state.documents.reduce((acc, doc) => acc && doc.isSaved, true);
|
||||
if (!allDocumentsSaved) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { invoke } from "@tauri-apps/api";
|
||||
|
||||
import type WasmBindgenPackage from "@/../wasm/pkg";
|
||||
import { panicProxy } from "@/utility-functions/panic-proxy";
|
||||
import { type JsMessageType } from "@/wasm-communication/messages";
|
||||
|
@ -24,6 +26,30 @@ export async function updateImage(path: BigUint64Array, mime: string, imageData:
|
|||
editorInstance?.setImageBlobURL(documentId, path, blobURL, image.naturalWidth, image.naturalHeight);
|
||||
}
|
||||
|
||||
export async function fetchImage(path: BigUint64Array, mime: string, documentId: bigint, url: string): Promise<void> {
|
||||
const data = await fetch(url);
|
||||
const blob = await data.blob();
|
||||
|
||||
const blobURL = URL.createObjectURL(blob);
|
||||
|
||||
// Pre-decode the image so it is ready to be drawn instantly once it's placed into the viewport SVG
|
||||
const image = new Image();
|
||||
image.src = blobURL;
|
||||
await image.decode();
|
||||
|
||||
editorInstance?.setImageBlobURL(documentId, path, blobURL, image.naturalWidth, image.naturalHeight);
|
||||
}
|
||||
|
||||
// export async function dispatchTauri(message: string): Promise<string> {
|
||||
export async function dispatchTauri(message: any): Promise<void> {
|
||||
try {
|
||||
const response = await invoke("handle_message", { message });
|
||||
editorInstance?.tauriResponse(response);
|
||||
} catch {
|
||||
console.error("Failed to dispatch Tauri message");
|
||||
}
|
||||
}
|
||||
|
||||
// Should be called asynchronously before `createEditor()`
|
||||
export async function initWasm(): Promise<void> {
|
||||
// Skip if the WASM module is already initialized
|
||||
|
@ -34,8 +60,14 @@ export async function initWasm(): Promise<void> {
|
|||
wasmImport = await import("@/../wasm/pkg").then(panicProxy);
|
||||
|
||||
// Provide a random starter seed which must occur after initializing the WASM module, since WASM can't generate its own random numbers
|
||||
const randomSeed = BigInt(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
||||
const randomSeedFloat = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
||||
const randomSeed = BigInt(randomSeedFloat);
|
||||
wasmImport?.setRandomSeed(randomSeed);
|
||||
try {
|
||||
await invoke("set_random_seed", { seed: randomSeedFloat });
|
||||
} catch {
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
|
||||
// Should be called after running `initWasm()` and its promise resolving
|
||||
|
|
|
@ -136,9 +136,9 @@ export type HintData = HintGroup[];
|
|||
export type HintGroup = HintInfo[];
|
||||
|
||||
export class HintInfo {
|
||||
readonly keyGroups!: KeysGroup[];
|
||||
readonly keyGroups!: LayoutKeysGroup[];
|
||||
|
||||
readonly keyGroupsMac!: KeysGroup[] | undefined;
|
||||
readonly keyGroupsMac!: LayoutKeysGroup[] | undefined;
|
||||
|
||||
readonly mouse!: MouseMotion | undefined;
|
||||
|
||||
|
@ -151,8 +151,8 @@ export class HintInfo {
|
|||
export type KeyRaw = string;
|
||||
// Serde converts a Rust `Key` enum variant into this format (via a custom serializer) with both the `Key` variant name (called `RawKey` in TS) and the localized `label` for the key
|
||||
export type Key = { key: KeyRaw; label: string };
|
||||
export type KeysGroup = Key[];
|
||||
export type ActionKeys = { keys: KeysGroup };
|
||||
export type LayoutKeysGroup = Key[];
|
||||
export type ActionKeys = { keys: LayoutKeysGroup };
|
||||
|
||||
export type MouseMotion = string;
|
||||
|
||||
|
@ -596,8 +596,8 @@ export class TriggerSavePreferences extends JsMessage {
|
|||
|
||||
export class DocumentChanged extends JsMessage {}
|
||||
|
||||
export class UpdateDocumentLayerTreeStructure extends JsMessage {
|
||||
constructor(readonly layerId: bigint, readonly children: UpdateDocumentLayerTreeStructure[]) {
|
||||
export class UpdateDocumentLayerTreeStructureJs extends JsMessage {
|
||||
constructor(readonly layerId: bigint, readonly children: UpdateDocumentLayerTreeStructureJs[]) {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
@ -607,7 +607,7 @@ type DataBuffer = {
|
|||
length: bigint;
|
||||
};
|
||||
|
||||
export function newUpdateDocumentLayerTreeStructure(input: { dataBuffer: DataBuffer }, wasm: WasmRawInstance): UpdateDocumentLayerTreeStructure {
|
||||
export function newUpdateDocumentLayerTreeStructure(input: { dataBuffer: DataBuffer }, wasm: WasmRawInstance): UpdateDocumentLayerTreeStructureJs {
|
||||
const pointerNum = Number(input.dataBuffer.pointer);
|
||||
const lengthNum = Number(input.dataBuffer.length);
|
||||
|
||||
|
@ -624,7 +624,7 @@ export function newUpdateDocumentLayerTreeStructure(input: { dataBuffer: DataBuf
|
|||
const layerIdsSection = new DataView(wasmMemoryBuffer, pointerNum + 8 + structureSectionLength * 8);
|
||||
|
||||
let layersEncountered = 0;
|
||||
let currentFolder = new UpdateDocumentLayerTreeStructure(BigInt(-1), []);
|
||||
let currentFolder = new UpdateDocumentLayerTreeStructureJs(BigInt(-1), []);
|
||||
const currentFolderStack = [currentFolder];
|
||||
|
||||
for (let i = 0; i < structureSectionLength; i += 1) {
|
||||
|
@ -639,7 +639,7 @@ export function newUpdateDocumentLayerTreeStructure(input: { dataBuffer: DataBuf
|
|||
const layerId = layerIdsSection.getBigUint64(layersEncountered * 8, true);
|
||||
layersEncountered += 1;
|
||||
|
||||
const childLayer = new UpdateDocumentLayerTreeStructure(layerId, []);
|
||||
const childLayer = new UpdateDocumentLayerTreeStructureJs(layerId, []);
|
||||
currentFolder.children.push(childLayer);
|
||||
}
|
||||
|
||||
|
@ -1361,7 +1361,7 @@ export const messageMakers: Record<string, MessageMaker> = {
|
|||
UpdateDocumentArtwork,
|
||||
UpdateDocumentBarLayout,
|
||||
UpdateDocumentLayerDetails,
|
||||
UpdateDocumentLayerTreeStructure: newUpdateDocumentLayerTreeStructure,
|
||||
UpdateDocumentLayerTreeStructureJs: newUpdateDocumentLayerTreeStructure,
|
||||
UpdateDocumentModeLayout,
|
||||
UpdateDocumentOverlays,
|
||||
UpdateDocumentRulers,
|
||||
|
|
|
@ -10,6 +10,10 @@ homepage = "https://graphite.rs"
|
|||
repository = "https://github.com/GraphiteEditor/Graphite"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[features]
|
||||
tauri = ["ron"]
|
||||
default = []
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
|
@ -22,6 +26,8 @@ serde = { version = "1.0", features = ["derive"] }
|
|||
wasm-bindgen = { version = "0.2.73" }
|
||||
serde-wasm-bindgen = "0.4.1"
|
||||
js-sys = "0.3.55"
|
||||
wasm-bindgen-futures = "0.4.33"
|
||||
ron = {version = "0.8", optional = true}
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.22"
|
||||
|
|
|
@ -33,6 +33,9 @@ pub fn set_random_seed(seed: u64) {
|
|||
#[wasm_bindgen(module = "@/wasm-communication/editor")]
|
||||
extern "C" {
|
||||
fn updateImage(path: Vec<u64>, mime: String, imageData: &[u8], document_id: u64);
|
||||
fn fetchImage(path: Vec<u64>, mime: String, document_id: u64, identifier: String);
|
||||
//fn dispatchTauri(message: String) -> String;
|
||||
fn dispatchTauri(message: String);
|
||||
}
|
||||
|
||||
/// Provides a handle to access the raw WASM memory
|
||||
|
@ -73,37 +76,53 @@ impl JsEditorHandle {
|
|||
return;
|
||||
}
|
||||
|
||||
// Get the editor instances, dispatch the message, and store the `FrontendMessage` queue response
|
||||
let frontend_messages = EDITOR_INSTANCES.with(|instances| {
|
||||
// Mutably borrow the editors, and if successful, we can access them in the closure
|
||||
instances.try_borrow_mut().map(|mut editors| {
|
||||
// Get the editor instance for this editor ID, then dispatch the message to the backend, and return its response `FrontendMessage` queue
|
||||
editors
|
||||
.get_mut(&self.editor_id)
|
||||
.expect("EDITOR_INSTANCES does not contain the current editor_id")
|
||||
.handle_message(message.into())
|
||||
})
|
||||
});
|
||||
#[cfg(feature = "tauri")]
|
||||
{
|
||||
let message: Message = message.into();
|
||||
let message = ron::to_string(&message).unwrap();
|
||||
|
||||
// Process any `FrontendMessage` responses resulting from the backend processing the dispatched message
|
||||
if let Ok(frontend_messages) = frontend_messages {
|
||||
// Send each `FrontendMessage` to the JavaScript frontend
|
||||
for message in frontend_messages.into_iter() {
|
||||
self.send_frontend_message_to_js(message);
|
||||
dispatchTauri(message);
|
||||
}
|
||||
#[cfg(not(feature = "tauri"))]
|
||||
{
|
||||
// Get the editor instances, dispatch the message, and store the `FrontendMessage` queue response
|
||||
let frontend_messages = EDITOR_INSTANCES.with(|instances| {
|
||||
// Mutably borrow the editors, and if successful, we can access them in the closure
|
||||
instances.try_borrow_mut().map(|mut editors| {
|
||||
// Get the editor instance for this editor ID, then dispatch the message to the backend, and return its response `FrontendMessage` queue
|
||||
editors
|
||||
.get_mut(&self.editor_id)
|
||||
.expect("EDITOR_INSTANCES does not contain the current editor_id")
|
||||
.handle_message(message.into())
|
||||
})
|
||||
});
|
||||
|
||||
// Process any `FrontendMessage` responses resulting from the backend processing the dispatched message
|
||||
if let Ok(frontend_messages) = frontend_messages {
|
||||
// Send each `FrontendMessage` to the JavaScript frontend
|
||||
for message in frontend_messages.into_iter() {
|
||||
self.send_frontend_message_to_js(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the editor cannot be borrowed then it has encountered a panic - we should just ignore new dispatches
|
||||
}
|
||||
|
||||
// Sends a FrontendMessage to JavaScript
|
||||
fn send_frontend_message_to_js(&self, message: FrontendMessage) {
|
||||
fn send_frontend_message_to_js(&self, mut message: FrontendMessage) {
|
||||
// Special case for update image data to avoid serialization times.
|
||||
if let FrontendMessage::UpdateImageData { document_id, image_data } = message {
|
||||
for image in image_data {
|
||||
#[cfg(not(feature = "tauri"))]
|
||||
updateImage(image.path, image.mime, &image.image_data, document_id);
|
||||
#[cfg(feature = "tauri")]
|
||||
fetchImage(image.path.clone(), image.mime, document_id, format!("http://localhost:3001/image/{:?}_{}", &image.path, document_id));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if let FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer } = message {
|
||||
message = FrontendMessage::UpdateDocumentLayerTreeStructureJs { data_buffer: data_buffer.into() };
|
||||
}
|
||||
|
||||
let message_type = message.to_discriminant().local_name();
|
||||
|
||||
|
@ -139,6 +158,21 @@ impl JsEditorHandle {
|
|||
self.dispatch(Message::Init);
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = tauriResponse)]
|
||||
pub fn tauri_response(&self, message: JsValue) {
|
||||
#[cfg(feature = "tauri")]
|
||||
match ron::from_str::<Vec<FrontendMessage>>(&message.as_string().unwrap()) {
|
||||
Ok(response) => {
|
||||
for message in response {
|
||||
self.send_frontend_message_to_js(message);
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
log::error!("tauri response: {:?}\n{:?}", error, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays a dialog with an error message
|
||||
#[wasm_bindgen(js_name = errorDialog)]
|
||||
pub fn error_dialog(&self, title: String, description: String) {
|
||||
|
|
|
@ -23,7 +23,7 @@ kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
|
|||
] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
base64 = "0.13"
|
||||
glam = { version = "0.17", features = ["serde"] }
|
||||
glam = { version = "0.22", features = ["serde"] }
|
||||
|
||||
# Font rendering
|
||||
rustybuzz = "*"
|
||||
|
|
|
@ -600,7 +600,7 @@ impl Document {
|
|||
image_data,
|
||||
mime,
|
||||
} => {
|
||||
let image_data = std::rc::Rc::new(image_data);
|
||||
let image_data = std::sync::Arc::new(image_data);
|
||||
let layer = Layer::new(LayerDataType::Image(ImageLayer::new(mime, image_data)), transform);
|
||||
|
||||
self.set_layer(&path, layer, insert_index)?;
|
||||
|
@ -624,7 +624,7 @@ impl Document {
|
|||
Operation::SetNodeGraphFrameImageData { layer_path, image_data } => {
|
||||
let layer = self.layer_mut(&layer_path).expect("Setting NodeGraphFrame image data for invalid layer");
|
||||
if let LayerDataType::NodeGraphFrame(node_graph_frame) = &mut layer.data {
|
||||
let image_data = std::rc::Rc::new(image_data);
|
||||
let image_data = std::sync::Arc::new(image_data);
|
||||
node_graph_frame.image_data = Some(crate::layers::nodegraph_layer::ImageData { image_data });
|
||||
} else {
|
||||
panic!("Incorrectly trying to set image data for a layer that is not an NodeGraphFrame layer type");
|
||||
|
@ -850,7 +850,7 @@ impl Document {
|
|||
Operation::ImaginateSetImageData { layer_path, image_data } => {
|
||||
let layer = self.layer_mut(&layer_path).expect("Setting Imaginate image data for invalid layer");
|
||||
if let LayerDataType::Imaginate(imaginate) = &mut layer.data {
|
||||
let image_data = std::rc::Rc::new(image_data);
|
||||
let image_data = std::sync::Arc::new(image_data);
|
||||
imaginate.image_data = Some(ImaginateImageData { image_data });
|
||||
} else {
|
||||
panic!("Incorrectly trying to set image data for a layer that is not an Imaginate layer type");
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
pub fn as_base64<S>(key: &std::rc::Rc<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
pub fn as_base64<S>(key: &std::sync::Arc<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&base64::encode(key.as_slice()))
|
||||
}
|
||||
|
||||
pub fn from_base64<'a, D>(deserializer: D) -> Result<std::rc::Rc<Vec<u8>>, D::Error>
|
||||
pub fn from_base64<'a, D>(deserializer: D) -> Result<std::sync::Arc<Vec<u8>>, D::Error>
|
||||
where
|
||||
D: Deserializer<'a>,
|
||||
{
|
||||
|
@ -17,6 +17,6 @@ where
|
|||
|
||||
String::deserialize(deserializer)
|
||||
.and_then(|string| base64::decode(string).map_err(|err| Error::custom(err.to_string())))
|
||||
.map(std::rc::Rc::new)
|
||||
.map(std::sync::Arc::new)
|
||||
.map_err(serde::de::Error::custom)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use std::fmt::Write;
|
|||
pub struct ImageLayer {
|
||||
pub mime: String,
|
||||
#[serde(serialize_with = "base64_serde::as_base64", deserialize_with = "base64_serde::from_base64")]
|
||||
pub image_data: std::rc::Rc<Vec<u8>>,
|
||||
pub image_data: std::sync::Arc<Vec<u8>>,
|
||||
// TODO: Have the browser dispose of this blob URL when this is dropped (like when the layer is deleted)
|
||||
#[serde(skip)]
|
||||
pub blob_url: Option<String>,
|
||||
|
@ -75,7 +75,7 @@ impl LayerData for ImageLayer {
|
|||
}
|
||||
|
||||
impl ImageLayer {
|
||||
pub fn new(mime: String, image_data: std::rc::Rc<Vec<u8>>) -> Self {
|
||||
pub fn new(mime: String, image_data: std::sync::Arc<Vec<u8>>) -> Self {
|
||||
Self {
|
||||
mime,
|
||||
image_data,
|
||||
|
|
|
@ -57,7 +57,7 @@ pub enum ImaginateStatus {
|
|||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct ImaginateImageData {
|
||||
#[serde(serialize_with = "base64_serde::as_base64", deserialize_with = "base64_serde::from_base64")]
|
||||
pub image_data: std::rc::Rc<Vec<u8>>,
|
||||
pub image_data: std::sync::Arc<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
|
|
|
@ -31,7 +31,7 @@ pub struct NodeGraphFrameLayer {
|
|||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub struct ImageData {
|
||||
#[serde(serialize_with = "base64_serde::as_base64", deserialize_with = "base64_serde::from_base64")]
|
||||
pub image_data: std::rc::Rc<Vec<u8>>,
|
||||
pub image_data: std::sync::Arc<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl LayerData for NodeGraphFrameLayer {
|
||||
|
|
|
@ -11,4 +11,4 @@ homepage = "https://graphite.rs/libraries/bezier-rs"
|
|||
repository = "https://github.com/GraphiteEditor/Graphite/libraries/bezier-rs"
|
||||
|
||||
[dependencies]
|
||||
glam = { version = "0.17", features = ["serde"] }
|
||||
glam = { version = "0.22", features = ["serde"] }
|
||||
|
|
|
@ -201,7 +201,7 @@ impl Bezier {
|
|||
// Check if the bounding boxes overlap
|
||||
if utils::do_rectangles_overlap(bounding_box1, bounding_box2) {
|
||||
// If bounding boxes are within the error threshold (i.e. are small enough), we have found an intersection
|
||||
if (bounding_box1[1] - bounding_box1[0]).lt(&error_threshold) && (bounding_box2[1] - bounding_box2[0]).lt(&error_threshold) {
|
||||
if (bounding_box1[1] - bounding_box1[0]).cmplt(error_threshold).all() && (bounding_box2[1] - bounding_box2[0]).cmplt(error_threshold).all() {
|
||||
// Use the middle t value, return the corresponding `t` value for `self` and `other`
|
||||
return vec![[self_mid_t, other_mid_t]];
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ documentation = "https://docs.rs/dyn-any"
|
|||
[dependencies]
|
||||
dyn-any-derive = { path = "derive", version = "0.2.0", optional = true }
|
||||
log = { version = "0.4", optional = true }
|
||||
glam = { version = "0.17", optional = true }
|
||||
glam = { version = "0.22", optional = true }
|
||||
|
||||
[features]
|
||||
derive = ["dyn-any-derive"]
|
||||
|
@ -23,6 +23,10 @@ log-bad-types = ["log"]
|
|||
rc = []
|
||||
# Opt into impls for some glam types
|
||||
glam = ["dep:glam"]
|
||||
alloc = []
|
||||
large-atomics = []
|
||||
std = ["alloc", "rc"]
|
||||
default = ["std", "large-atomics"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#![doc(html_root_url = "http://docs.rs/const-default/1.0.0")]
|
||||
#![cfg_attr(feature = "unstable-docs", feature(doc_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "derive")]
|
||||
#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "derive")))]
|
||||
|
@ -9,6 +13,7 @@ pub use dyn_any_derive::DynAny;
|
|||
pub trait UpcastFrom<T: ?Sized> {
|
||||
fn up_from(value: &T) -> &Self;
|
||||
fn up_from_mut(value: &mut T) -> &mut Self;
|
||||
#[cfg(feature = "alloc")]
|
||||
fn up_from_box(value: Box<T>) -> Box<Self>;
|
||||
}
|
||||
|
||||
|
@ -16,6 +21,7 @@ pub trait UpcastFrom<T: ?Sized> {
|
|||
pub trait Upcast<U: ?Sized> {
|
||||
fn up(&self) -> &U;
|
||||
fn up_mut(&mut self) -> &mut U;
|
||||
#[cfg(feature = "alloc")]
|
||||
fn up_box(self: Box<Self>) -> Box<U>;
|
||||
}
|
||||
|
||||
|
@ -29,12 +35,13 @@ where
|
|||
fn up_mut(&mut self) -> &mut U {
|
||||
U::up_from_mut(self)
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
fn up_box(self: Box<Self>) -> Box<U> {
|
||||
U::up_from_box(self)
|
||||
}
|
||||
}
|
||||
|
||||
use std::any::TypeId;
|
||||
use core::any::TypeId;
|
||||
|
||||
impl<'a, T: DynAny<'a> + 'a> UpcastFrom<T> for dyn DynAny<'a> + 'a {
|
||||
fn up_from(value: &T) -> &(dyn DynAny<'a> + 'a) {
|
||||
|
@ -43,6 +50,7 @@ impl<'a, T: DynAny<'a> + 'a> UpcastFrom<T> for dyn DynAny<'a> + 'a {
|
|||
fn up_from_mut(value: &mut T) -> &mut (dyn DynAny<'a> + 'a) {
|
||||
value
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
fn up_from_box(value: Box<T>) -> Box<Self> {
|
||||
value
|
||||
}
|
||||
|
@ -55,16 +63,16 @@ pub trait DynAny<'a> {
|
|||
}
|
||||
|
||||
impl<'a, T: StaticType> DynAny<'a> for T {
|
||||
fn type_id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<T::Static>()
|
||||
fn type_id(&self) -> core::any::TypeId {
|
||||
core::any::TypeId::of::<T::Static>()
|
||||
}
|
||||
#[cfg(feature = "log-bad-types")]
|
||||
fn type_name(&self) -> &'static str {
|
||||
std::any::type_name::<T>()
|
||||
core::any::type_name::<T>()
|
||||
}
|
||||
}
|
||||
pub fn downcast_ref<'a, V: StaticType>(i: &'a dyn DynAny<'a>) -> Option<&'a V> {
|
||||
if i.type_id() == std::any::TypeId::of::<<V as StaticType>::Static>() {
|
||||
if i.type_id() == core::any::TypeId::of::<<V as StaticType>::Static>() {
|
||||
// SAFETY: caller guarantees that T is the correct type
|
||||
let ptr = i as *const dyn DynAny<'a> as *const V;
|
||||
Some(unsafe { &*ptr })
|
||||
|
@ -73,9 +81,10 @@ pub fn downcast_ref<'a, V: StaticType>(i: &'a dyn DynAny<'a>) -> Option<&'a V> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub fn downcast<'a, V: StaticType>(i: Box<dyn DynAny<'a> + 'a>) -> Option<Box<V>> {
|
||||
let type_id = DynAny::type_id(i.as_ref());
|
||||
if type_id == std::any::TypeId::of::<<V as StaticType>::Static>() {
|
||||
if type_id == core::any::TypeId::of::<<V as StaticType>::Static>() {
|
||||
// SAFETY: caller guarantees that T is the correct type
|
||||
let ptr = Box::into_raw(i) as *mut dyn DynAny<'a> as *mut V;
|
||||
Some(unsafe { Box::from_raw(ptr) })
|
||||
|
@ -85,26 +94,24 @@ pub fn downcast<'a, V: StaticType>(i: Box<dyn DynAny<'a> + 'a>) -> Option<Box<V>
|
|||
log::error!("Tried to downcast a {} to a {}", DynAny::type_name(i.as_ref()), core::any::type_name::<V>());
|
||||
}
|
||||
|
||||
if type_id == std::any::TypeId::of::<&dyn DynAny<'static>>() {
|
||||
panic!("downcast error: type_id == std::any::TypeId::of::<dyn DynAny<'a>>()");
|
||||
if type_id == core::any::TypeId::of::<&dyn DynAny<'static>>() {
|
||||
panic!("downcast error: type_id == core::any::TypeId::of::<dyn DynAny<'a>>()");
|
||||
}
|
||||
println!("expected: {:?}", std::any::TypeId::of::<<V as StaticType>::Static>());
|
||||
println!("actual one: {:?}", type_id);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StaticType {
|
||||
type Static: 'static + ?Sized;
|
||||
fn type_id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<Self::Static>()
|
||||
fn type_id(&self) -> core::any::TypeId {
|
||||
core::any::TypeId::of::<Self::Static>()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StaticTypeSized {
|
||||
type Static: 'static;
|
||||
fn type_id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<Self::Static>()
|
||||
fn type_id(&self) -> core::any::TypeId {
|
||||
core::any::TypeId::of::<Self::Static>()
|
||||
}
|
||||
}
|
||||
impl<T: StaticType + Sized> StaticTypeSized for T
|
||||
|
@ -115,8 +122,8 @@ where
|
|||
}
|
||||
pub trait StaticTypeClone {
|
||||
type Static: 'static + Clone;
|
||||
fn type_id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<Self::Static>()
|
||||
fn type_id(&self) -> core::any::TypeId {
|
||||
core::any::TypeId::of::<Self::Static>()
|
||||
}
|
||||
}
|
||||
impl<T: StaticType + Clone> StaticTypeClone for T
|
||||
|
@ -135,8 +142,10 @@ macro_rules! impl_type {
|
|||
)*
|
||||
};
|
||||
}
|
||||
impl<'a, T: StaticTypeClone + Clone> StaticType for std::borrow::Cow<'a, T> {
|
||||
type Static = std::borrow::Cow<'static, T::Static>;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'a, T: StaticTypeClone + Clone> StaticType for Cow<'a, T> {
|
||||
type Static = Cow<'static, T::Static>;
|
||||
}
|
||||
impl<T: StaticTypeSized> StaticType for *const [T] {
|
||||
type Static = *const [<T as StaticTypeSized>::Static];
|
||||
|
@ -163,19 +172,31 @@ impl<T: StaticTypeSized, const N: usize> StaticType for [T; N] {
|
|||
impl<'a> StaticType for dyn DynAny<'a> + '_ {
|
||||
type Static = dyn DynAny<'static>;
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
pub trait IntoDynAny<'n>: Sized + StaticType + 'n {
|
||||
fn into_dyn(self) -> Box<dyn DynAny<'n> + 'n> {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<'n, T: StaticType + 'n> IntoDynAny<'n> for T {}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<()> for Box<dyn DynAny<'static>> {
|
||||
fn from(_: ()) -> Box<dyn DynAny<'static>> {
|
||||
Box::new(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::{
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque},
|
||||
string::String,
|
||||
vec::Vec,
|
||||
};
|
||||
use core::sync::atomic::*;
|
||||
use core::{
|
||||
cell::{Cell, RefCell, UnsafeCell},
|
||||
iter::Empty,
|
||||
|
@ -184,26 +205,38 @@ use core::{
|
|||
num::Wrapping,
|
||||
time::Duration,
|
||||
};
|
||||
use std::{
|
||||
collections::*,
|
||||
sync::{atomic::*, *},
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
impl_type!(
|
||||
Option<T>, Result<T, E>, Cell<T>, UnsafeCell<T>, RefCell<T>, MaybeUninit<T>,
|
||||
Vec<T>, String, BTreeMap<K,V>,BTreeSet<V>, LinkedList<T>, VecDeque<T>,
|
||||
BinaryHeap<T>, ManuallyDrop<T>, PhantomData<T>, PhantomPinned, Empty<T>,
|
||||
Wrapping<T>, Duration, Once, Mutex<T>, RwLock<T>, bool, f32, f64, char,
|
||||
u8, AtomicU8, u16, AtomicU16, u32, AtomicU32, u64, AtomicU64, usize, AtomicUsize,
|
||||
i8, AtomicI8, i16, AtomicI16, i32, AtomicI32, i64, AtomicI64, isize, AtomicIsize,
|
||||
ManuallyDrop<T>, PhantomData<T>, PhantomPinned, Empty<T>,
|
||||
Wrapping<T>, Duration, bool, f32, f64, char,
|
||||
u8, AtomicU8, u16, AtomicU16, u32, AtomicU32, u64, usize, AtomicUsize,
|
||||
i8, AtomicI8, i16, AtomicI16, i32, AtomicI32, i64, isize, AtomicIsize,
|
||||
i128, u128, AtomicBool, AtomicPtr<T>
|
||||
);
|
||||
#[cfg(feature = "large-atomics")]
|
||||
impl_type!(AtomicU64, AtomicI64);
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl_type!(
|
||||
Vec<T>, String, BTreeMap<K,V>,BTreeSet<V>, LinkedList<T>, VecDeque<T>,
|
||||
BinaryHeap<T>
|
||||
);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::*;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl_type!(Once, Mutex<T>, RwLock<T>);
|
||||
|
||||
#[cfg(feature = "rc")]
|
||||
use std::{rc::Rc, sync::Arc};
|
||||
use std::rc::Rc;
|
||||
#[cfg(feature = "rc")]
|
||||
impl_type!(Rc<T>, Arc<T>);
|
||||
impl_type!(Rc<T>);
|
||||
#[cfg(all(feature = "rc", feature = "alloc"))]
|
||||
use std::sync::Arc;
|
||||
#[cfg(all(feature = "rc", feature = "alloc"))]
|
||||
impl_type!(Arc<T>);
|
||||
|
||||
#[cfg(feature = "glam")]
|
||||
use glam::*;
|
||||
|
@ -216,6 +249,7 @@ impl_type!(
|
|||
Quat, Affine2, Affine3A, DAffine2, DAffine3, DQuat
|
||||
);
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<T: crate::StaticType + ?Sized> crate::StaticType for Box<T> {
|
||||
type Static = Box<<T as crate::StaticType>::Static>;
|
||||
}
|
||||
|
|
|
@ -9,17 +9,26 @@ license = "MIT OR Apache-2.0"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
std = ["dyn-any"]
|
||||
default = ["async", "serde"]
|
||||
gpu = ["spirv-std", "bytemuck"]
|
||||
async = ["async-trait"]
|
||||
std = ["dyn-any", "dyn-any/std"]
|
||||
default = ["async", "serde", "kurbo", "log"]
|
||||
log = ["dep:log"]
|
||||
serde = ["dep:serde", "glam/serde"]
|
||||
gpu = ["spirv-std", "bytemuck", "glam/bytemuck", "dyn-any"]
|
||||
async = ["async-trait", "alloc"]
|
||||
nightly = []
|
||||
serde = ["dep:serde"]
|
||||
alloc = ["dyn-any", "bezier-rs"]
|
||||
|
||||
[dependencies]
|
||||
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"], optional = true}
|
||||
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"], optional = true, default-features = false }
|
||||
|
||||
spirv-std = { git = "https://github.com/EmbarkStudios/rust-gpu", features = ["glam"] , optional = true}
|
||||
bytemuck = {version = "1.8", features = ["derive"], optional = true}
|
||||
async-trait = {version = "0.1", optional = true}
|
||||
serde = {version = "1.0", features = ["derive"], optional = true}
|
||||
serde = {version = "1.0", features = ["derive"], optional = true, default-features = false }
|
||||
log = {version = "0.4", optional = true}
|
||||
|
||||
bezier-rs = { path = "../../libraries/bezier-rs", optional = true }
|
||||
kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
|
||||
"serde",
|
||||
], optional = true }
|
||||
glam = { version = "^0.22", default-features = false, features = ["scalar-math", "libm"]}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
#![no_std]
|
||||
#[cfg(feature = "async")]
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg_attr(feature = "log", macro_use)]
|
||||
#[cfg(feature = "log")]
|
||||
extern crate log;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use alloc::boxed::Box;
|
||||
#[cfg(feature = "async")]
|
||||
|
@ -17,6 +22,9 @@ pub mod gpu;
|
|||
|
||||
pub mod raster;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod vector;
|
||||
|
||||
pub trait Node<T> {
|
||||
type Output;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::Node;
|
||||
|
||||
pub mod color;
|
||||
use self::color::Color;
|
||||
pub use self::color::Color;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct GrayscaleColorNode;
|
||||
|
@ -21,6 +21,12 @@ impl<'n> Node<Color> for &'n GrayscaleColorNode {
|
|||
}
|
||||
}
|
||||
|
||||
impl GrayscaleColorNode {
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct BrightenColorNode<N: Node<(), Output = f32>>(N);
|
||||
|
||||
|
@ -48,8 +54,10 @@ impl<N: Node<(), Output = f32> + Copy> BrightenColorNode<N> {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
pub struct HueShiftColorNode<N: Node<(), Output = f32>>(N);
|
||||
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
impl<N: Node<(), Output = f32>> Node<Color> for HueShiftColorNode<N> {
|
||||
type Output = Color;
|
||||
fn eval(self, color: Color) -> Color {
|
||||
|
@ -58,6 +66,7 @@ impl<N: Node<(), Output = f32>> Node<Color> for HueShiftColorNode<N> {
|
|||
Color::from_hsla(hue + hue_shift / 360., saturation, lightness, alpha)
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
impl<N: Node<(), Output = f32> + Copy> Node<Color> for &HueShiftColorNode<N> {
|
||||
type Output = Color;
|
||||
fn eval(self, color: Color) -> Color {
|
||||
|
@ -67,6 +76,7 @@ impl<N: Node<(), Output = f32> + Copy> Node<Color> for &HueShiftColorNode<N> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
impl<N: Node<(), Output = f32> + Copy> HueShiftColorNode<N> {
|
||||
pub fn new(node: N) -> Self {
|
||||
Self(node)
|
||||
|
@ -85,6 +95,48 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use image::Image;
|
||||
#[cfg(feature = "alloc")]
|
||||
mod image {
|
||||
use super::Color;
|
||||
use alloc::vec::Vec;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
#[derive(Clone, Debug, PartialEq, DynAny, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Image {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub data: Vec<Color>,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
width: 0,
|
||||
height: 0,
|
||||
data: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Image {
|
||||
type Item = Color;
|
||||
type IntoIter = alloc::vec::IntoIter<Color>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.data.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Image {
|
||||
type Item = &'a Color;
|
||||
type IntoIter = alloc::slice::Iter<'a, Color>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.data.iter()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*pub struct MutWrapper<N>(pub N);
|
||||
|
||||
impl<'n, T: Clone, N> Node<&'n mut T> for &'n MutWrapper<N>
|
||||
|
|
|
@ -3,13 +3,23 @@ use dyn_any::{DynAny, StaticType};
|
|||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::float::Float;
|
||||
|
||||
#[cfg(target_arch = "spirv")]
|
||||
use spirv_std::num_traits::Euclid;
|
||||
|
||||
#[cfg(feature = "gpu")]
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
/// Structure that represents a color.
|
||||
/// Internally alpha is stored as `f32` that ranges from `0.0` (transparent) to `1.0` (opaque).
|
||||
/// The other components (RGB) are stored as `f32` that range from `0.0` up to `f32::MAX`,
|
||||
/// the values encode the brightness of each channel proportional to the light intensity in cd/m² (nits) in HDR, and `0.0` (black) to `1.0` (white) in SDR color.
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "std", derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize, DynAny))]
|
||||
#[cfg_attr(not(feature = "std"), derive(Debug, Clone, Copy, PartialEq, Default))]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, DynAny))]
|
||||
#[cfg_attr(feature = "gpu", derive(Pod, Zeroable))]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||
pub struct Color {
|
||||
red: f32,
|
||||
green: f32,
|
||||
|
@ -92,6 +102,7 @@ impl Color {
|
|||
/// use graphene_core::raster::color::Color;
|
||||
/// let color = Color::from_hsla(0.5, 0.2, 0.3, 1.);
|
||||
/// ```
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
pub fn from_hsla(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Color {
|
||||
let temp1 = if lightness < 0.5 {
|
||||
lightness * (saturation + 1.)
|
||||
|
@ -219,7 +230,10 @@ impl Color {
|
|||
} else {
|
||||
4. + (self.red - self.green) / (max_channel - min_channel)
|
||||
} / 6.;
|
||||
#[cfg(not(target_arch = "spirv"))]
|
||||
let hue = hue.rem_euclid(1.);
|
||||
#[cfg(target_arch = "spirv")]
|
||||
let hue = hue.rem_euclid(&1.);
|
||||
|
||||
[hue, saturation, lightness, self.alpha]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::ops::{Index, IndexMut};
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::Node;
|
||||
use glam::{DAffine2, DVec2};
|
||||
use graphene_core::Node;
|
||||
|
||||
use super::subpath::Subpath;
|
||||
|
||||
|
@ -40,8 +38,10 @@ impl Node<()> for &UnitSquareGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: I removed the Arc requirement we shouuld think about when it makes sense to use its
|
||||
// vs making a generic value node
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PathGenerator(Arc<Subpath>);
|
||||
pub struct PathGenerator(Subpath);
|
||||
|
||||
impl Node<()> for PathGenerator {
|
||||
type Output = VectorData;
|
||||
|
@ -53,7 +53,7 @@ impl Node<()> for PathGenerator {
|
|||
impl Node<()> for &PathGenerator {
|
||||
type Output = VectorData;
|
||||
fn eval(self, _input: ()) -> Self::Output {
|
||||
(*self.0).clone()
|
||||
(self.0).clone()
|
||||
}
|
||||
}
|
||||
use crate::raster::Image;
|
||||
|
@ -65,7 +65,7 @@ impl<N: Node<(), Output = Subpath>> Node<Image> for BlitSubpath<N> {
|
|||
type Output = Image;
|
||||
fn eval(self, input: Image) -> Self::Output {
|
||||
let subpath = self.0.eval(());
|
||||
info!("Blitting subpath {subpath:?}");
|
||||
log::info!("Blitting subpath {subpath:?}");
|
||||
input
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ impl<N: Node<(), Output = Subpath> + Copy> Node<Image> for &BlitSubpath<N> {
|
|||
type Output = Image;
|
||||
fn eval(self, input: Image) -> Self::Output {
|
||||
let subpath = self.0.eval(());
|
||||
info!("Blitting subpath {subpath:?}");
|
||||
log::info!("Blitting subpath {subpath:?}");
|
||||
input
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
use core::ops::{Deref, DerefMut};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Brief description: A vec that allows indexing elements by both index and an assigned unique ID
|
||||
/// Goals of this Data Structure:
|
||||
|
@ -121,7 +124,7 @@ impl<T> IdBackedVec<T> {
|
|||
}
|
||||
|
||||
/// Enumerate the ids and elements in this container `(&ElementId, &T)`
|
||||
pub fn enumerate(&self) -> std::iter::Zip<core::slice::Iter<u64>, core::slice::Iter<T>> {
|
||||
pub fn enumerate(&self) -> core::iter::Zip<core::slice::Iter<u64>, core::slice::Iter<T>> {
|
||||
self.element_ids.iter().zip(self.elements.iter())
|
||||
}
|
||||
|
|
@ -3,6 +3,9 @@ use super::id_vec::IdBackedVec;
|
|||
use super::manipulator_group::ManipulatorGroup;
|
||||
use super::manipulator_point::ManipulatorPoint;
|
||||
|
||||
use alloc::string::String;
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use glam::{DAffine2, DVec2};
|
||||
use kurbo::{BezPath, PathEl, Shape};
|
||||
|
@ -92,7 +95,7 @@ impl Subpath {
|
|||
pub fn new_ngon(center: DVec2, sides: u64, radius: f64) -> Self {
|
||||
let mut manipulator_groups = vec![];
|
||||
for i in 0..sides {
|
||||
let angle = (i as f64) * std::f64::consts::TAU / (sides as f64);
|
||||
let angle = (i as f64) * core::f64::consts::TAU / (sides as f64);
|
||||
let center = center + DVec2::ONE * radius;
|
||||
let position = ManipulatorGroup::new_with_anchor(DVec2::new(center.x + radius * f64::cos(angle), center.y + radius * f64::sin(angle)) * 0.5);
|
||||
|
||||
|
@ -346,7 +349,7 @@ impl Subpath {
|
|||
/// Generate an SVG `path` elements's `d` attribute: `<path d="...">`.
|
||||
pub fn to_svg(&mut self) -> String {
|
||||
fn write_positions(result: &mut String, values: [Option<DVec2>; 3]) {
|
||||
use std::fmt::Write;
|
||||
use core::fmt::Write;
|
||||
let count = values.into_iter().flatten().count();
|
||||
for (index, pos) in values.into_iter().flatten().enumerate() {
|
||||
write!(result, "{},{}", pos.x, pos.y).unwrap();
|
||||
|
@ -442,7 +445,7 @@ impl BezierId {
|
|||
|
||||
/// An iterator over [`bezier_rs::Bezier`] segments constructable via [`Subpath::bezier_iter`].
|
||||
pub struct PathIter<'a> {
|
||||
path: std::iter::Zip<core::slice::Iter<'a, u64>, core::slice::Iter<'a, ManipulatorGroup>>,
|
||||
path: core::iter::Zip<core::slice::Iter<'a, u64>, core::slice::Iter<'a, ManipulatorGroup>>,
|
||||
|
||||
last_anchor: Option<DVec2>,
|
||||
last_out_handle: Option<DVec2>,
|
|
@ -8,13 +8,12 @@ license = "MIT OR Apache-2.0"
|
|||
default = []
|
||||
profiling = ["nvtx", "gpu"]
|
||||
gpu = ["serde", "vulkano", "spirv-builder", "tera", "graphene-core/gpu"]
|
||||
serde = ["dep:serde", "graphene-std/serde", "glam/serde"]
|
||||
serde = ["dep:serde", "graphene-core/serde", "glam/serde"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
graphene-core = { path = "../gcore", features = ["async", "std" ] }
|
||||
graphene-std = { path = "../gstd" }
|
||||
graphene-core = { path = "../gcore", features = ["async", "std", "alloc"] }
|
||||
dyn-any = { path = "../../libraries/dyn-any", features = ["log-bad-types", "rc", "glam"] }
|
||||
num-traits = "0.2"
|
||||
borrow_stack = { path = "../borrow_stack" }
|
||||
|
@ -22,7 +21,7 @@ dyn-clone = "1.0"
|
|||
rand_chacha = "0.3.1"
|
||||
log = "0.4"
|
||||
serde = { version = "1", features = ["derive", "rc"], optional = true }
|
||||
glam = { version = "0.17" }
|
||||
glam = { version = "0.22" }
|
||||
|
||||
vulkano = {git = "https://github.com/GraphiteEditor/vulkano", branch = "fix_rust_gpu", optional = true}
|
||||
bytemuck = {version = "1.8" }
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::sync::Mutex;
|
|||
|
||||
pub mod value;
|
||||
|
||||
use dyn_any::{DynAny, StaticType};
|
||||
use rand_chacha::{
|
||||
rand_core::{RngCore, SeedableRng},
|
||||
ChaCha20Rng,
|
||||
|
@ -138,7 +139,7 @@ pub enum DocumentNodeImplementation {
|
|||
Unresolved(NodeIdentifier),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Clone, Debug, Default, PartialEq, DynAny)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct NodeNetwork {
|
||||
pub inputs: Vec<NodeId>,
|
||||
|
|
|
@ -15,10 +15,10 @@ pub enum TaggedValue {
|
|||
F64(f64),
|
||||
Bool(bool),
|
||||
DVec2(DVec2),
|
||||
Image(graphene_std::raster::Image),
|
||||
Image(graphene_core::raster::Image),
|
||||
Color(graphene_core::raster::color::Color),
|
||||
Subpath(graphene_std::vector::subpath::Subpath),
|
||||
RcSubpath(Arc<graphene_std::vector::subpath::Subpath>),
|
||||
Subpath(graphene_core::vector::subpath::Subpath),
|
||||
RcSubpath(Arc<graphene_core::vector::subpath::Subpath>),
|
||||
}
|
||||
|
||||
impl TaggedValue {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use std::error::Error;
|
||||
|
||||
use borrow_stack::{BorrowStack, FixedSizeStack};
|
||||
use graphene_core::Node;
|
||||
use graphene_std::any::{Any, TypeErasedNode};
|
||||
use dyn_any::DynAny;
|
||||
|
||||
use crate::{document::NodeNetwork, node_registry::push_node, proto::ProtoNetwork};
|
||||
use crate::{document::NodeNetwork, proto::ProtoNetwork};
|
||||
|
||||
pub struct Compiler {}
|
||||
|
||||
|
@ -25,30 +23,8 @@ impl Compiler {
|
|||
proto_network
|
||||
}
|
||||
}
|
||||
pub type Any<'a> = Box<dyn DynAny<'a> + 'a>;
|
||||
|
||||
pub trait Executor {
|
||||
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn Error>>;
|
||||
}
|
||||
|
||||
pub struct DynamicExecutor {
|
||||
stack: FixedSizeStack<TypeErasedNode<'static>>,
|
||||
}
|
||||
|
||||
impl DynamicExecutor {
|
||||
pub fn new(proto_network: ProtoNetwork) -> Self {
|
||||
assert_eq!(proto_network.inputs.len(), 1);
|
||||
let node_count = proto_network.nodes.len();
|
||||
let stack = FixedSizeStack::new(node_count);
|
||||
for (_id, node) in proto_network.nodes {
|
||||
push_node(node, &stack);
|
||||
}
|
||||
Self { stack }
|
||||
}
|
||||
}
|
||||
|
||||
impl Executor for DynamicExecutor {
|
||||
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn Error>> {
|
||||
let result = unsafe { self.stack.get().last().unwrap().eval(input) };
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@ pub fn create_files(matadata: &Metadata, network: &ProtoNetwork, compile_dir: &P
|
|||
let cargo_toml = create_cargo_toml(matadata)?;
|
||||
std::fs::write(cargo_file, cargo_toml)?;
|
||||
|
||||
let toolchain_file = compile_dir.join("rust-toolchain");
|
||||
let toolchain = include_str!("templates/rust-toolchain");
|
||||
std::fs::write(toolchain_file, toolchain)?;
|
||||
|
||||
// create src dir
|
||||
match std::fs::create_dir(&src) {
|
||||
Ok(_) => {}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::path::Path;
|
||||
|
||||
use super::{compiler::Metadata, context::Context};
|
||||
use crate::gpu::compiler;
|
||||
use crate::{executor::Any, gpu::compiler};
|
||||
use bytemuck::Pod;
|
||||
use dyn_any::StaticTypeSized;
|
||||
use vulkano::{
|
||||
|
@ -44,7 +44,7 @@ impl<I: StaticTypeSized, O> GpuExecutor<I, O> {
|
|||
}
|
||||
|
||||
impl<I: StaticTypeSized + Sync + Pod + Send, O: StaticTypeSized + Send + Sync + Pod> crate::executor::Executor for GpuExecutor<I, O> {
|
||||
fn execute(&self, input: graphene_std::any::Any<'static>) -> Result<graphene_std::any::Any<'static>, Box<dyn std::error::Error>> {
|
||||
fn execute(&self, input: Any<'static>) -> Result<Any<'static>, Box<dyn std::error::Error>> {
|
||||
let input = dyn_any::downcast::<Vec<I>>(input).expect("Wrong input type");
|
||||
let context = &self.context;
|
||||
let result: Vec<O> = execute_shader(
|
||||
|
@ -91,7 +91,7 @@ fn execute_shader<I: Pod + Send + Sync, O: Pod + Send + Sync>(
|
|||
.bind_pipeline_compute(compute_pipeline.clone())
|
||||
.bind_descriptor_sets(PipelineBindPoint::Compute, compute_pipeline.layout().clone(), 0, set)
|
||||
.push_constants(compute_pipeline.layout().clone(), 0, constants)
|
||||
.dispatch([1024, 1, 1])
|
||||
.dispatch([((constants.n as isize - 1) / 1024 + 1) as u32 * 1024, 1, 1])
|
||||
.unwrap();
|
||||
let command_buffer = builder.build().unwrap();
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod node_registry;
|
||||
|
||||
pub mod document;
|
||||
pub mod proto;
|
||||
|
||||
|
@ -10,124 +8,3 @@ pub mod executor;
|
|||
|
||||
#[cfg(feature = "gpu")]
|
||||
pub mod gpu;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use graphene_core::value::ValueNode;
|
||||
use graphene_core::{structural::*, RefNode};
|
||||
|
||||
use borrow_stack::BorrowStack;
|
||||
use dyn_any::{downcast, IntoDynAny};
|
||||
use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode};
|
||||
use graphene_std::ops::AddNode;
|
||||
|
||||
#[test]
|
||||
fn borrow_stack() {
|
||||
let stack = borrow_stack::FixedSizeStack::new(256);
|
||||
unsafe {
|
||||
let dynanynode: DynAnyNode<ValueNode<u32>, (), _, _> = DynAnyNode::new(ValueNode(2_u32));
|
||||
stack.push(dynanynode.into_box());
|
||||
}
|
||||
stack.push_fn(|nodes| {
|
||||
let pre_node = nodes.get(0).unwrap();
|
||||
let downcast: DowncastNode<&TypeErasedNode, &u32> = DowncastNode::new(pre_node);
|
||||
let dynanynode: DynAnyNode<ConsNode<_, Any<'_>>, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData));
|
||||
dynanynode.into_box()
|
||||
});
|
||||
stack.push_fn(|_| {
|
||||
let dynanynode: DynAnyNode<_, (u32, &u32), _, _> = DynAnyNode::new(AddNode);
|
||||
dynanynode.into_box()
|
||||
});
|
||||
stack.push_fn(|nodes| {
|
||||
let compose_node = nodes[1].after(&nodes[2]);
|
||||
TypeErasedNode(Box::new(compose_node))
|
||||
});
|
||||
|
||||
let result = unsafe { &stack.get()[0] }.eval_ref(().into_dyn());
|
||||
assert_eq!(*downcast::<&u32>(result).unwrap(), &2_u32);
|
||||
let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn());
|
||||
assert_eq!(*downcast::<(u32, &u32)>(result).unwrap(), (4_u32, &2_u32));
|
||||
let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn());
|
||||
let add = unsafe { &stack.get()[2] }.eval_ref(result);
|
||||
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
|
||||
let add = unsafe { &stack.get()[3] }.eval_ref(4_u32.into_dyn());
|
||||
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_add() {
|
||||
use crate::document::*;
|
||||
use crate::proto::*;
|
||||
|
||||
fn add_network() -> NodeNetwork {
|
||||
NodeNetwork {
|
||||
inputs: vec![0, 0],
|
||||
output: 1,
|
||||
nodes: [
|
||||
(
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "Cons".into(),
|
||||
inputs: vec![NodeInput::Network, NodeInput::Network],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new(
|
||||
"graphene_core::structural::ConsNode",
|
||||
&[Type::Concrete(std::borrow::Cow::Borrowed("u32")), Type::Concrete(std::borrow::Cow::Borrowed("u32"))],
|
||||
)),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
(
|
||||
1,
|
||||
DocumentNode {
|
||||
name: "Add".into(),
|
||||
inputs: vec![NodeInput::Node(0)],
|
||||
implementation: DocumentNodeImplementation::Unresolved(NodeIdentifier::new(
|
||||
"graphene_core::ops::AddNode",
|
||||
&[Type::Concrete(std::borrow::Cow::Borrowed("u32")), Type::Concrete(std::borrow::Cow::Borrowed("u32"))],
|
||||
)),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
let network = NodeNetwork {
|
||||
inputs: vec![0],
|
||||
output: 0,
|
||||
nodes: [(
|
||||
0,
|
||||
DocumentNode {
|
||||
name: "Inc".into(),
|
||||
inputs: vec![
|
||||
NodeInput::Network,
|
||||
NodeInput::Value {
|
||||
tagged_value: value::TaggedValue::U32(1),
|
||||
exposed: false,
|
||||
},
|
||||
],
|
||||
implementation: DocumentNodeImplementation::Network(add_network()),
|
||||
metadata: DocumentNodeMetadata::default(),
|
||||
},
|
||||
)]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
use crate::executor::{Compiler, DynamicExecutor, Executor};
|
||||
|
||||
let compiler = Compiler {};
|
||||
let protograph = compiler.compile(network, false);
|
||||
|
||||
let exec = DynamicExecutor::new(protograph);
|
||||
|
||||
let result = exec.execute(32_u32.into_dyn()).unwrap();
|
||||
let val = *dyn_any::downcast::<u32>(result).unwrap();
|
||||
assert_eq!(val, 33_u32);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -321,17 +321,12 @@ impl ProtoNetwork {
|
|||
}
|
||||
|
||||
fn replace_node_references(&mut self, lookup: &HashMap<u64, u64>) {
|
||||
self.nodes.iter_mut().for_each(|(sid, node)| {
|
||||
self.nodes.iter_mut().for_each(|(_, node)| {
|
||||
node.map_ids(|id| *lookup.get(&id).expect("node not found in lookup table"));
|
||||
});
|
||||
self.inputs = self.inputs.iter().map(|id| *lookup.get(id).unwrap()).collect();
|
||||
self.output = *lookup.get(&self.output).unwrap();
|
||||
}
|
||||
|
||||
fn replace_ids_with_lookup(&mut self, lookup: HashMap<u64, u64>) {
|
||||
self.nodes.iter_mut().for_each(|(id, _)| *id = *lookup.get(id).unwrap());
|
||||
self.replace_node_references(&lookup);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -12,13 +12,17 @@ license = "MIT OR Apache-2.0"
|
|||
derive = ["graph-proc-macros"]
|
||||
memoization = ["once_cell"]
|
||||
default = ["derive", "memoization"]
|
||||
gpu = ["graph-craft/gpu", "graphene-core/gpu"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
graphene-core = {path = "../gcore", features = ["async", "std"], default-features = false}
|
||||
graphene-core = {path = "../gcore", features = ["async", "std" ], default-features = false}
|
||||
borrow_stack = {path = "../borrow_stack"}
|
||||
dyn-any = {path = "../../libraries/dyn-any", features = ["derive"]}
|
||||
graph-proc-macros = {path = "../proc-macro", optional = true}
|
||||
graph-craft = {path = "../graph-craft"}
|
||||
bytemuck = {version = "1.8" }
|
||||
tempfile = "3"
|
||||
once_cell = {version= "1.10", optional = true}
|
||||
#pretty-token-stream = {path = "../../pretty-token-stream"}
|
||||
syn = {version = "1.0", default-features = false, features = ["parsing", "printing"]}
|
||||
|
@ -32,7 +36,7 @@ bezier-rs = { path = "../../libraries/bezier-rs" }
|
|||
kurbo = { git = "https://github.com/linebender/kurbo.git", features = [
|
||||
"serde",
|
||||
] }
|
||||
glam = { version = "0.17", features = ["serde"] }
|
||||
glam = { version = "0.22", features = ["serde"] }
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
|
|
|
@ -30,7 +30,9 @@ where
|
|||
input.borrow().hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
self.map.get_or_create_with(&hash, || CacheNode::new(self.node))
|
||||
self.map.get_or_create_with(&hash, ||{
|
||||
trace!("Creating new cache node");
|
||||
CacheNode::new(self.node)})
|
||||
}
|
||||
}
|
||||
|
||||
|
|