mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-22 05:48:13 +08:00
Add some additional image effect nodes (#869)
* Move the Subpath type to graphene-std * Add the transform subpath node * Delete selected nodes * Inserting node list on right click * Add several bitmap manipulator nodes * Convert add node to use f64 * Add posterize node * Rename names randomly * Fix naming * Exposure node * Fix typo * Adjust exposure node range * Comment out vector nodes * Adjust exposure range again * Posterise as ints * Rename input * Use >= in the to hsl function
This commit is contained in:
@@ -27,6 +27,10 @@ pub fn default_mapping() -> Mapping {
|
||||
),
|
||||
// NORMAL PRIORITY:
|
||||
//
|
||||
// NodeGraphMessage
|
||||
entry!(KeyDown(Delete); action_dispatch=NodeGraphMessage::DeleteSelectedNodes),
|
||||
entry!(KeyDown(Backspace); action_dispatch=NodeGraphMessage::DeleteSelectedNodes),
|
||||
//
|
||||
// TransformLayerMessage
|
||||
entry!(KeyDown(Enter); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
|
||||
entry!(KeyDown(Lmb); action_dispatch=TransformLayerMessage::ApplyTransformOperation),
|
||||
|
||||
@@ -263,6 +263,18 @@ impl WidgetHolder {
|
||||
pub fn new(widget: Widget) -> Self {
|
||||
Self { widget_id: generate_uuid(), widget }
|
||||
}
|
||||
pub fn unrelated_seperator() -> Self {
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
}))
|
||||
}
|
||||
pub fn text_widget(text: impl Into<String>) -> Self {
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: text.into(),
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -29,8 +29,8 @@ use graphene::layers::imaginate_layer::{ImaginateBaseImage, ImaginateGenerationP
|
||||
use graphene::layers::layer_info::{LayerDataType, LayerDataTypeDiscriminant};
|
||||
use graphene::layers::style::{Fill, RenderData, ViewMode};
|
||||
use graphene::layers::text_layer::{Font, FontCache};
|
||||
use graphene::layers::vector::subpath::Subpath;
|
||||
use graphene::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
|
||||
use graphene_std::vector::subpath::Subpath;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -928,6 +928,7 @@ impl MessageHandler<DocumentMessage, (u64, &InputPreprocessorMessageHandler, &Pe
|
||||
}
|
||||
common.extend(self.navigation_handler.actions());
|
||||
common.extend(self.transform_layer_handler.actions());
|
||||
common.extend(self.node_graph_handler.actions());
|
||||
common
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@ pub enum NodeGraphMessage {
|
||||
CreateNode {
|
||||
// Having the caller generate the id means that we don't have to return it. This can be a random u64.
|
||||
node_id: Option<NodeId>,
|
||||
// I don't really know what this is for (perhaps a user identifiable name).
|
||||
node_type: String,
|
||||
x: i32,
|
||||
y: i32,
|
||||
},
|
||||
DeleteNode {
|
||||
node_id: NodeId,
|
||||
},
|
||||
DeleteSelectedNodes,
|
||||
ExposeInput {
|
||||
node_id: NodeId,
|
||||
input_index: usize,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::messages::layout::utility_types::layout_widget::LayoutGroup;
|
||||
use crate::messages::prelude::*;
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, DocumentNodeMetadata, NodeInput, NodeNetwork};
|
||||
use graph_craft::document::{DocumentNode, DocumentNodeImplementation, DocumentNodeMetadata, NodeId, NodeInput, NodeNetwork};
|
||||
use graphene::document::Document;
|
||||
use graphene::layers::layer_info::LayerDataType;
|
||||
use graphene::layers::nodegraph_layer::NodeGraphFrameLayer;
|
||||
@@ -18,9 +18,13 @@ pub enum FrontendGraphDataType {
|
||||
#[serde(rename = "color")]
|
||||
Color,
|
||||
#[serde(rename = "vector")]
|
||||
Vector,
|
||||
Subpath,
|
||||
#[serde(rename = "number")]
|
||||
Number,
|
||||
#[serde(rename = "boolean")]
|
||||
Boolean,
|
||||
#[serde(rename = "vec2")]
|
||||
Vector,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
@@ -57,10 +61,14 @@ pub struct FrontendNodeLink {
|
||||
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FrontendNodeType {
|
||||
pub name: String,
|
||||
pub category: String,
|
||||
}
|
||||
impl FrontendNodeType {
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self { name: name.to_string() }
|
||||
pub fn new(name: &'static str, category: &'static str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
category: category.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,6 +156,54 @@ impl NodeGraphMessageHandler {
|
||||
log::debug!("Nodes:\n{:#?}\n\nFrontend Nodes:\n{:#?}\n\nLinks:\n{:#?}", network.nodes, nodes, links);
|
||||
responses.push_back(FrontendMessage::UpdateNodeGraph { nodes, links }.into());
|
||||
}
|
||||
|
||||
fn remove_node(&mut self, network: &mut NodeNetwork, node_id: NodeId) -> bool {
|
||||
fn remove_from_network(network: &mut NodeNetwork, node_id: NodeId) -> bool {
|
||||
if network.inputs.iter().any(|&id| id == node_id) {
|
||||
warn!("Deleting input node");
|
||||
return false;
|
||||
}
|
||||
if network.output == node_id {
|
||||
warn!("Deleting the output node!");
|
||||
return false;
|
||||
}
|
||||
for (id, node) in network.nodes.iter_mut() {
|
||||
if *id == node_id {
|
||||
continue;
|
||||
}
|
||||
for (input_index, input) in node.inputs.iter_mut().enumerate() {
|
||||
let NodeInput::Node(id) = input else {
|
||||
continue;
|
||||
};
|
||||
if *id != node_id {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(node_type) = document_node_types::resolve_document_node_type(&node.name) else{
|
||||
warn!("Removing input of invalid node type '{}'", node.name);
|
||||
return false;
|
||||
};
|
||||
if let NodeInput::Value { tagged_value, .. } = &node_type.inputs[input_index].default {
|
||||
*input = NodeInput::Value {
|
||||
tagged_value: tagged_value.clone(),
|
||||
exposed: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
if let DocumentNodeImplementation::Network(network) = &mut node.implementation {
|
||||
remove_from_network(network, node_id);
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
if remove_from_network(network, node_id) {
|
||||
network.nodes.remove(&node_id);
|
||||
self.selected_nodes.retain(|&id| id != node_id);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageHandler)> for NodeGraphMessageHandler {
|
||||
@@ -188,7 +244,7 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||
Self::send_graph(network, responses);
|
||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||
}
|
||||
NodeGraphMessage::CreateNode { node_id, node_type } => {
|
||||
NodeGraphMessage::CreateNode { node_id, node_type, x, y } => {
|
||||
let node_id = node_id.unwrap_or_else(crate::application::generate_uuid);
|
||||
let Some(network) = self.get_active_network_mut(document) else{
|
||||
warn!("No network");
|
||||
@@ -218,7 +274,6 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
let far_right_node = network.nodes.iter().map(|node| node.1.metadata.position).max_by_key(|pos| pos.0).unwrap_or_default();
|
||||
|
||||
network.nodes.insert(
|
||||
node_id,
|
||||
@@ -227,19 +282,29 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||
inputs: document_node_type.inputs.iter().map(|input| input.default.clone()).collect(),
|
||||
// TODO: Allow inserting nodes that contain other nodes.
|
||||
implementation: DocumentNodeImplementation::Network(inner_network),
|
||||
metadata: graph_craft::document::DocumentNodeMetadata {
|
||||
// TODO: Better position default
|
||||
position: (far_right_node.0 + 7, far_right_node.1 + 2),
|
||||
},
|
||||
metadata: graph_craft::document::DocumentNodeMetadata { position: (x, y) },
|
||||
},
|
||||
);
|
||||
Self::send_graph(network, responses);
|
||||
}
|
||||
NodeGraphMessage::DeleteNode { node_id } => {
|
||||
if let Some(network) = self.get_active_network_mut(document) {
|
||||
network.nodes.remove(&node_id);
|
||||
Self::send_graph(network, responses);
|
||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||
if self.remove_node(network, node_id) {
|
||||
Self::send_graph(network, responses);
|
||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::DeleteSelectedNodes => {
|
||||
if let Some(network) = self.get_active_network_mut(document) {
|
||||
let mut modified = false;
|
||||
for node_id in self.selected_nodes.clone() {
|
||||
modified = modified || self.remove_node(network, node_id);
|
||||
}
|
||||
if modified {
|
||||
Self::send_graph(network, responses);
|
||||
responses.push_back(DocumentMessage::NodeGraphFrameGenerate.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
NodeGraphMessage::ExposeInput { node_id, input_index, new_exposed } => {
|
||||
@@ -314,5 +379,11 @@ impl MessageHandler<NodeGraphMessage, (&mut Document, &InputPreprocessorMessageH
|
||||
}
|
||||
}
|
||||
|
||||
advertise_actions!(NodeGraphMessageDiscriminant; DeleteNode,);
|
||||
fn actions(&self) -> ActionList {
|
||||
if self.layer_path.is_some() && !self.selected_nodes.is_empty() {
|
||||
actions!(NodeGraphMessageDiscriminant; DeleteSelectedNodes,)
|
||||
} else {
|
||||
actions!(NodeGraphMessageDiscriminant;)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+227
-68
@@ -1,11 +1,13 @@
|
||||
use super::{FrontendGraphDataType, FrontendNodeType};
|
||||
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::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;
|
||||
|
||||
@@ -15,8 +17,19 @@ pub struct DocumentInputType {
|
||||
pub default: NodeInput,
|
||||
}
|
||||
|
||||
impl DocumentInputType {
|
||||
pub const fn none() -> Self {
|
||||
Self {
|
||||
name: "None",
|
||||
data_type: FrontendGraphDataType::General,
|
||||
default: NodeInput::value(TaggedValue::None, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DocumentNodeType {
|
||||
pub name: &'static str,
|
||||
pub category: &'static str,
|
||||
pub identifier: NodeIdentifier,
|
||||
pub inputs: &'static [DocumentInputType],
|
||||
pub outputs: &'static [FrontendGraphDataType],
|
||||
@@ -24,9 +37,10 @@ pub struct DocumentNodeType {
|
||||
}
|
||||
|
||||
// TODO: Dynamic node library
|
||||
static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [
|
||||
static DOCUMENT_NODE_TYPES: &[DocumentNodeType] = &[
|
||||
DocumentNodeType {
|
||||
name: "Identity",
|
||||
category: "Meta",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
@@ -45,6 +59,7 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Input",
|
||||
category: "Meta",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
@@ -52,129 +67,273 @@ static DOCUMENT_NODE_TYPES: [DocumentNodeType; 7] = [
|
||||
default: NodeInput::Network,
|
||||
}],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: |_document_node, _node_id| {
|
||||
vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "The input to the graph is the bitmap under the frame".to_string(),
|
||||
..Default::default()
|
||||
}))],
|
||||
}]
|
||||
},
|
||||
properties: |_document_node, _node_id| node_properties::string_properties("The input to the graph is the bitmap under the frame".to_string()),
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Output",
|
||||
category: "Meta",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "In",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::Value {
|
||||
tagged_value: TaggedValue::Image(Image::empty()),
|
||||
exposed: true,
|
||||
},
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
}],
|
||||
outputs: &[],
|
||||
properties: |_document_node, _node_id| {
|
||||
vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "The output to the graph is rendered in the frame".to_string(),
|
||||
..Default::default()
|
||||
}))],
|
||||
}]
|
||||
},
|
||||
properties: |_document_node, _node_id| node_properties::string_properties("The output to the graph is rendered in the frame".to_string()),
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Grayscale Image",
|
||||
category: "Image Color Correction",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::GrayscaleImageNode", &[]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::Value {
|
||||
tagged_value: TaggedValue::Image(Image::empty()),
|
||||
exposed: true,
|
||||
},
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
}],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: |_document_node, _node_id| {
|
||||
vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "The output to the graph is rendered in the frame".to_string(),
|
||||
..Default::default()
|
||||
}))],
|
||||
}]
|
||||
},
|
||||
properties: node_properties::no_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Brighten Image",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::BrightenImageNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
name: "Invert Image Color",
|
||||
category: "Image Color Correction",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::InvertImageColorNode", &[]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
}],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::no_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Shift Image HSL",
|
||||
category: "Image Color Correction",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ShiftImageHSLNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::Value {
|
||||
tagged_value: TaggedValue::Image(Image::empty()),
|
||||
exposed: true,
|
||||
},
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Amount",
|
||||
name: "Hue Shift",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::Value {
|
||||
tagged_value: TaggedValue::F32(10.),
|
||||
exposed: false,
|
||||
},
|
||||
default: NodeInput::value(TaggedValue::F64(0.), false),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Saturation Shift",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::value(TaggedValue::F64(0.), false),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Luminance Shift",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::value(TaggedValue::F64(0.), false),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: super::node_properties::brighten_image_properties,
|
||||
properties: node_properties::adjust_hsl_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Hue Shift Image",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::HueShiftImage", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
name: "Image Contrast and Brightness",
|
||||
category: "Image Color Correction",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ImageBrightnessAndContrast", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::Value {
|
||||
tagged_value: TaggedValue::Image(Image::empty()),
|
||||
exposed: true,
|
||||
},
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Amount",
|
||||
name: "Brightness",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::Value {
|
||||
tagged_value: TaggedValue::F32(10.),
|
||||
exposed: false,
|
||||
},
|
||||
default: NodeInput::value(TaggedValue::F64(0.), false),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Contrast",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::value(TaggedValue::F64(0.), false),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: super::node_properties::hue_shift_image_properties,
|
||||
properties: node_properties::brighten_image_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Adjust Image Gamma",
|
||||
category: "Image Color Correction",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ImageGammaNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Gamma",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::value(TaggedValue::F64(1.), false),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::adjust_gamma_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Multiply Image Opactiy",
|
||||
category: "Image Color Correction",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ImageOpacityNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Factor",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::value(TaggedValue::F64(1.), false),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::multiply_opacity,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Posterize",
|
||||
category: "Image Filters",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::Posterize", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Value",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::value(TaggedValue::F64(5.), false),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::posterize_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Exposure",
|
||||
category: "Image Color Correction",
|
||||
identifier: NodeIdentifier::new("graphene_std::raster::ExposureNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Value",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::value(TaggedValue::F64(1.), false),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::exposure_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Add",
|
||||
category: "Mathmatics",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::AddNode", &[Type::Concrete(Cow::Borrowed("&TypeErasedNode"))]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Input",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::Value {
|
||||
tagged_value: TaggedValue::F32(0.),
|
||||
exposed: true,
|
||||
},
|
||||
default: NodeInput::value(TaggedValue::F64(0.), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Addend",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::Value {
|
||||
tagged_value: TaggedValue::F32(0.),
|
||||
exposed: true,
|
||||
},
|
||||
default: NodeInput::value(TaggedValue::F64(0.), true),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Number],
|
||||
properties: super::node_properties::add_properties,
|
||||
properties: node_properties::add_properties,
|
||||
},
|
||||
/*DocumentNodeType {
|
||||
name: "Unit Circle Generator",
|
||||
category: "Vector",
|
||||
identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitCircleGenerator", &[]),
|
||||
inputs: &[DocumentInputType::none()],
|
||||
outputs: &[FrontendGraphDataType::Subpath],
|
||||
properties: node_properties::no_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Unit Square Generator",
|
||||
category: "Vector",
|
||||
identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::UnitSquareGenerator", &[]),
|
||||
inputs: &[DocumentInputType::none()],
|
||||
outputs: &[FrontendGraphDataType::Subpath],
|
||||
properties: node_properties::no_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Path Generator",
|
||||
category: "Vector",
|
||||
identifier: NodeIdentifier::new("graphene_core::ops::IdNode", &[Type::Concrete(Cow::Borrowed("Any<'_>"))]),
|
||||
inputs: &[DocumentInputType {
|
||||
name: "Path Data",
|
||||
data_type: FrontendGraphDataType::Subpath,
|
||||
default: NodeInput::value(TaggedValue::Subpath(Subpath::new()), false),
|
||||
}],
|
||||
outputs: &[FrontendGraphDataType::Subpath],
|
||||
properties: node_properties::no_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Transform Subpath",
|
||||
category: "Vector",
|
||||
identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::TransformSubpathNode", &[]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Subpath",
|
||||
data_type: FrontendGraphDataType::Subpath,
|
||||
default: NodeInput::value(TaggedValue::Subpath(Subpath::new()), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Translation",
|
||||
data_type: FrontendGraphDataType::Vector,
|
||||
default: NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Rotation",
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
default: NodeInput::value(TaggedValue::F64(0.), false),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Scale",
|
||||
data_type: FrontendGraphDataType::Vector,
|
||||
default: NodeInput::value(TaggedValue::DVec2(DVec2::ONE), false),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Skew",
|
||||
data_type: FrontendGraphDataType::Vector,
|
||||
default: NodeInput::value(TaggedValue::DVec2(DVec2::ZERO), false),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Subpath],
|
||||
properties: node_properties::transform_properties,
|
||||
},
|
||||
DocumentNodeType {
|
||||
name: "Blit Subpath",
|
||||
category: "Vector",
|
||||
identifier: NodeIdentifier::new("graphene_std::vector::generator_nodes::BlitSubpath", &[]),
|
||||
inputs: &[
|
||||
DocumentInputType {
|
||||
name: "Image",
|
||||
data_type: FrontendGraphDataType::Raster,
|
||||
default: NodeInput::value(TaggedValue::Image(Image::empty()), true),
|
||||
},
|
||||
DocumentInputType {
|
||||
name: "Subpath",
|
||||
data_type: FrontendGraphDataType::Subpath,
|
||||
default: NodeInput::value(TaggedValue::Subpath(Subpath::new()), true),
|
||||
},
|
||||
],
|
||||
outputs: &[FrontendGraphDataType::Raster],
|
||||
properties: node_properties::no_properties,
|
||||
},*/
|
||||
];
|
||||
|
||||
pub fn resolve_document_node_type(name: &str) -> Option<&DocumentNodeType> {
|
||||
@@ -185,6 +344,6 @@ pub fn collect_node_types() -> Vec<FrontendNodeType> {
|
||||
DOCUMENT_NODE_TYPES
|
||||
.iter()
|
||||
.filter(|node_type| !matches!(node_type.name, "Input" | "Output"))
|
||||
.map(|node_type| FrontendNodeType { name: node_type.name.to_string() })
|
||||
.map(|node_type| FrontendNodeType::new(node_type.name, node_type.category))
|
||||
.collect()
|
||||
}
|
||||
|
||||
+206
-141
@@ -1,163 +1,128 @@
|
||||
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::label_widgets::{Separator, SeparatorDirection, SeparatorType, TextLabel};
|
||||
use crate::messages::prelude::NodeGraphMessage;
|
||||
|
||||
use glam::DVec2;
|
||||
use graph_craft::document::value::TaggedValue;
|
||||
use graph_craft::document::{DocumentNode, NodeId, NodeInput};
|
||||
|
||||
use super::FrontendGraphDataType;
|
||||
|
||||
pub fn hue_shift_image_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let index = 1;
|
||||
pub fn string_properties(text: impl Into<String>) -> Vec<LayoutGroup> {
|
||||
let widget = WidgetHolder::text_widget(text);
|
||||
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> {
|
||||
WidgetCallback::new(move |number_input: &T| {
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node: node_id,
|
||||
input_index,
|
||||
value: value(number_input),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
fn expose_widget(node_id: NodeId, index: usize, data_type: FrontendGraphDataType, exposed: bool) -> WidgetHolder {
|
||||
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
|
||||
exposed,
|
||||
data_type,
|
||||
tooltip: "Expose input parameter in node graph".into(),
|
||||
on_update: WidgetCallback::new(move |_parameter| {
|
||||
NodeGraphMessage::ExposeInput {
|
||||
node_id,
|
||||
input_index: index,
|
||||
new_exposed: !exposed,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
}))
|
||||
}
|
||||
|
||||
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();
|
||||
let exposed = input.is_exposed();
|
||||
|
||||
let mut widgets = vec![
|
||||
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
|
||||
exposed,
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
tooltip: "Expose input parameter in node graph".into(),
|
||||
on_update: WidgetCallback::new(move |_parameter| {
|
||||
NodeGraphMessage::ExposeInput {
|
||||
node_id,
|
||||
input_index: index,
|
||||
new_exposed: !exposed,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Shift Degrees".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
expose_widget(node_id, index, FrontendGraphDataType::Number, input.is_exposed()),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::text_widget(name),
|
||||
];
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F32(x),
|
||||
tagged_value: TaggedValue::F64(x),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(x as f64),
|
||||
unit: "°".into(),
|
||||
mode: NumberInputMode::Range,
|
||||
range_min: Some(-180.),
|
||||
range_max: Some(180.),
|
||||
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node: node_id,
|
||||
input_index: 1,
|
||||
value: TaggedValue::F32(number_input.value.unwrap() as f32),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
value: Some(x),
|
||||
mode: if range_max.is_some() { NumberInputMode::Range } else { NumberInputMode::Increment },
|
||||
range_min,
|
||||
range_max,
|
||||
unit,
|
||||
is_integer,
|
||||
on_update: update_value(|x: &NumberInput| TaggedValue::F64(x.value.unwrap()), node_id, index),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
])
|
||||
}
|
||||
widgets
|
||||
}
|
||||
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
pub fn adjust_hsl_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let hue_shift = number_range_widget(document_node, node_id, 1, "Hue Shift", Some(-180.), Some(180.), "°".into(), false);
|
||||
let saturation_shift = number_range_widget(document_node, node_id, 2, "Saturation Shift", Some(-100.), Some(100.), "%".into(), false);
|
||||
let luminance_shift = number_range_widget(document_node, node_id, 3, "Luminance Shift", Some(-100.), Some(100.), "%".into(), false);
|
||||
|
||||
vec![
|
||||
LayoutGroup::Row { widgets: hue_shift },
|
||||
LayoutGroup::Row { widgets: saturation_shift },
|
||||
LayoutGroup::Row { widgets: luminance_shift },
|
||||
]
|
||||
}
|
||||
|
||||
pub fn brighten_image_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let index = 1;
|
||||
let input: &NodeInput = document_node.inputs.get(index).unwrap();
|
||||
let exposed = input.is_exposed();
|
||||
let brightness = number_range_widget(document_node, node_id, 1, "Brightness", Some(-255.), Some(255.), "".into(), false);
|
||||
let contrast = number_range_widget(document_node, node_id, 2, "Contrast", Some(-255.), Some(255.), "".into(), false);
|
||||
|
||||
let mut widgets = vec![
|
||||
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
|
||||
exposed,
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
tooltip: "Expose input parameter in node graph".into(),
|
||||
on_update: WidgetCallback::new(move |_parameter| {
|
||||
NodeGraphMessage::ExposeInput {
|
||||
node_id,
|
||||
input_index: index,
|
||||
new_exposed: !exposed,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: "Brighten Amount".into(),
|
||||
..Default::default()
|
||||
})),
|
||||
];
|
||||
vec![LayoutGroup::Row { widgets: brightness }, LayoutGroup::Row { widgets: contrast }]
|
||||
}
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F32(x),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(x as f64),
|
||||
mode: NumberInputMode::Range,
|
||||
range_min: Some(-255.),
|
||||
range_max: Some(255.),
|
||||
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node: node_id,
|
||||
input_index: 1,
|
||||
value: TaggedValue::F32(number_input.value.unwrap() as f32),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
])
|
||||
}
|
||||
pub fn adjust_gamma_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let gamma = number_range_widget(document_node, node_id, 1, "Gamma", Some(0.01), None, "".into(), false);
|
||||
|
||||
vec![LayoutGroup::Row { widgets }]
|
||||
vec![LayoutGroup::Row { widgets: gamma }]
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: gamma }]
|
||||
}
|
||||
|
||||
pub fn posterize_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let value = number_range_widget(document_node, node_id, 1, "Levels", Some(2.), Some(255.), "".into(), true);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: value }]
|
||||
}
|
||||
|
||||
pub fn exposure_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let value = number_range_widget(document_node, node_id, 1, "Value", Some(-3.), Some(3.), "".into(), false);
|
||||
|
||||
vec![LayoutGroup::Row { widgets: value }]
|
||||
}
|
||||
|
||||
pub fn add_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let operand = |name: &str, index| {
|
||||
let input: &NodeInput = document_node.inputs.get(index).unwrap();
|
||||
let exposed = input.is_exposed();
|
||||
let mut widgets = vec![
|
||||
WidgetHolder::new(Widget::ParameterExposeButton(ParameterExposeButton {
|
||||
exposed,
|
||||
data_type: FrontendGraphDataType::Number,
|
||||
tooltip: "Expose input parameter in node graph".into(),
|
||||
on_update: WidgetCallback::new(move |_parameter| {
|
||||
NodeGraphMessage::ExposeInput {
|
||||
node_id,
|
||||
input_index: index,
|
||||
new_exposed: !exposed,
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
..Default::default()
|
||||
})),
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: name.into(),
|
||||
..Default::default()
|
||||
})),
|
||||
expose_widget(node_id, index, FrontendGraphDataType::Number, input.is_exposed()),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::text_widget(name),
|
||||
];
|
||||
|
||||
if let NodeInput::Value {
|
||||
@@ -166,21 +131,11 @@ pub fn add_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<Layo
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
WidgetHolder::new(Widget::Separator(Separator {
|
||||
separator_type: SeparatorType::Unrelated,
|
||||
direction: SeparatorDirection::Horizontal,
|
||||
})),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(x as f64),
|
||||
mode: NumberInputMode::Increment,
|
||||
on_update: WidgetCallback::new(move |number_input: &NumberInput| {
|
||||
NodeGraphMessage::SetInputValue {
|
||||
node: node_id,
|
||||
input_index: index,
|
||||
value: TaggedValue::F32(number_input.value.unwrap() as f32),
|
||||
}
|
||||
.into()
|
||||
}),
|
||||
on_update: update_value(|number_input: &NumberInput| TaggedValue::F32(number_input.value.unwrap() as f32), node_id, index),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
]);
|
||||
@@ -191,13 +146,123 @@ pub fn add_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<Layo
|
||||
vec![operand("Input", 0), operand("Addend", 1)]
|
||||
}
|
||||
|
||||
pub fn transform_properties(document_node: &DocumentNode, node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
let translation = {
|
||||
let index = 1;
|
||||
let input: &NodeInput = document_node.inputs.get(index).unwrap();
|
||||
|
||||
let mut widgets = vec![
|
||||
expose_widget(node_id, index, FrontendGraphDataType::Vector, input.is_exposed()),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::text_widget("Translation"),
|
||||
];
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(vec2),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(vec2.x),
|
||||
label: "X".into(),
|
||||
unit: " px".into(),
|
||||
on_update: update_value(move |number_input: &NumberInput| TaggedValue::DVec2(DVec2::new(number_input.value.unwrap(), vec2.y)), node_id, index),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(vec2.y),
|
||||
label: "Y".into(),
|
||||
unit: " px".into(),
|
||||
on_update: update_value(move |number_input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, number_input.value.unwrap())), node_id, index),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
]);
|
||||
}
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
};
|
||||
|
||||
let rotation = {
|
||||
let index = 2;
|
||||
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("Rotation"),
|
||||
];
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::F64(val),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(val.to_degrees()),
|
||||
unit: "°".into(),
|
||||
mode: NumberInputMode::Range,
|
||||
range_min: Some(-180.),
|
||||
range_max: Some(180.),
|
||||
on_update: update_value(|number_input: &NumberInput| TaggedValue::F64(number_input.value.unwrap().to_radians()), node_id, index),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
]);
|
||||
}
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
};
|
||||
|
||||
let scale = {
|
||||
let index = 3;
|
||||
let input: &NodeInput = document_node.inputs.get(index).unwrap();
|
||||
|
||||
let mut widgets = vec![
|
||||
expose_widget(node_id, index, FrontendGraphDataType::Vector, input.is_exposed()),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::text_widget("Scale"),
|
||||
];
|
||||
|
||||
if let NodeInput::Value {
|
||||
tagged_value: TaggedValue::DVec2(vec2),
|
||||
exposed: false,
|
||||
} = document_node.inputs[index]
|
||||
{
|
||||
widgets.extend_from_slice(&[
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(vec2.x),
|
||||
label: "X".into(),
|
||||
unit: "".into(),
|
||||
on_update: update_value(move |number_input: &NumberInput| TaggedValue::DVec2(DVec2::new(number_input.value.unwrap(), vec2.y)), node_id, index),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
WidgetHolder::unrelated_seperator(),
|
||||
WidgetHolder::new(Widget::NumberInput(NumberInput {
|
||||
value: Some(vec2.y),
|
||||
label: "Y".into(),
|
||||
unit: "".into(),
|
||||
on_update: update_value(move |number_input: &NumberInput| TaggedValue::DVec2(DVec2::new(vec2.x, number_input.value.unwrap())), node_id, index),
|
||||
..NumberInput::default()
|
||||
})),
|
||||
]);
|
||||
}
|
||||
|
||||
LayoutGroup::Row { widgets }
|
||||
};
|
||||
vec![translation, rotation, scale]
|
||||
}
|
||||
|
||||
fn unknown_node_properties(document_node: &DocumentNode) -> Vec<LayoutGroup> {
|
||||
vec![LayoutGroup::Row {
|
||||
widgets: vec![WidgetHolder::new(Widget::TextLabel(TextLabel {
|
||||
value: format!("Node '{}' cannot be found in library", document_node.name),
|
||||
..Default::default()
|
||||
}))],
|
||||
}]
|
||||
string_properties(format!("Node '{}' cannot be found in library", document_node.name))
|
||||
}
|
||||
|
||||
pub fn no_properties(document_node: &DocumentNode, _node_id: NodeId) -> Vec<LayoutGroup> {
|
||||
string_properties(format!("The {} node requires no properties.", document_node.name.to_lowercase()))
|
||||
}
|
||||
|
||||
pub fn generate_node_properties(document_node: &DocumentNode, node_id: NodeId) -> LayoutGroup {
|
||||
|
||||
+1
@@ -136,6 +136,7 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
responses.push_back(NodeGraphMessage::CloseNodeGraph.into());
|
||||
}
|
||||
}
|
||||
ResendActiveProperties => {
|
||||
|
||||
@@ -6,11 +6,11 @@ use crate::messages::prelude::*;
|
||||
use graphene::color::Color;
|
||||
use graphene::document::Document;
|
||||
use graphene::layers::style::{self, Fill, Stroke};
|
||||
use graphene::layers::vector::consts::ManipulatorType;
|
||||
use graphene::layers::vector::manipulator_group::ManipulatorGroup;
|
||||
use graphene::layers::vector::manipulator_point::ManipulatorPoint;
|
||||
use graphene::layers::vector::subpath::Subpath;
|
||||
use graphene::{LayerId, Operation};
|
||||
use graphene_std::vector::consts::ManipulatorType;
|
||||
use graphene_std::vector::manipulator_group::ManipulatorGroup;
|
||||
use graphene_std::vector::manipulator_point::ManipulatorPoint;
|
||||
use graphene_std::vector::subpath::Subpath;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ use graphene::intersection::Quad;
|
||||
use graphene::layers::layer_info::LayerDataType;
|
||||
use graphene::layers::style::{self, Fill, Stroke};
|
||||
use graphene::layers::text_layer::FontCache;
|
||||
use graphene::layers::vector::subpath::Subpath;
|
||||
use graphene::{LayerId, Operation};
|
||||
use graphene_std::vector::subpath::Subpath;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::messages::prelude::*;
|
||||
|
||||
use bezier_rs::ComputeType;
|
||||
use graphene::layers::vector::consts::ManipulatorType;
|
||||
use graphene::layers::vector::manipulator_group::ManipulatorGroup;
|
||||
use graphene::layers::vector::manipulator_point::ManipulatorPoint;
|
||||
use graphene::layers::vector::subpath::{BezierId, Subpath};
|
||||
use graphene::{LayerId, Operation};
|
||||
use graphene_std::vector::consts::ManipulatorType;
|
||||
use graphene_std::vector::manipulator_group::ManipulatorGroup;
|
||||
use graphene_std::vector::manipulator_point::ManipulatorPoint;
|
||||
use graphene_std::vector::subpath::{BezierId, Subpath};
|
||||
|
||||
use glam::DVec2;
|
||||
use graphene::document::Document;
|
||||
|
||||
@@ -7,8 +7,8 @@ use crate::messages::prelude::*;
|
||||
|
||||
use graphene::layers::layer_info::{Layer, LayerDataType};
|
||||
use graphene::layers::style::{self, Stroke};
|
||||
use graphene::layers::vector::consts::ManipulatorType;
|
||||
use graphene::{LayerId, Operation};
|
||||
use graphene_std::vector::consts::ManipulatorType;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use std::f64::consts::PI;
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHan
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use graphene::intersection::Quad;
|
||||
use graphene::layers::vector::consts::ManipulatorType;
|
||||
use graphene_std::vector::consts::ManipulatorType;
|
||||
|
||||
use glam::DVec2;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -10,10 +10,10 @@ use crate::messages::tool::utility_types::{EventToMessageMap, Fsm, ToolActionHan
|
||||
use crate::messages::tool::utility_types::{HintData, HintGroup, HintInfo};
|
||||
|
||||
use graphene::layers::style;
|
||||
use graphene::layers::vector::consts::ManipulatorType;
|
||||
use graphene::layers::vector::manipulator_group::ManipulatorGroup;
|
||||
use graphene::LayerId;
|
||||
use graphene::Operation;
|
||||
use graphene_std::vector::consts::ManipulatorType;
|
||||
use graphene_std::vector::manipulator_group::ManipulatorGroup;
|
||||
|
||||
use glam::{DAffine2, DVec2};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
Reference in New Issue
Block a user