Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions editor/src/messages/input_mapper/default_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,11 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(Lmb); action_dispatch=FillToolMessage::LeftPointerDown),
entry!(KeyDown(Rmb); action_dispatch=FillToolMessage::RightPointerDown),
//
// BrushToolMessage
entry!(PointerMove; action_dispatch=BrushToolMessage::PointerMove),
entry!(KeyDown(Lmb); action_dispatch=BrushToolMessage::DragStart),
entry!(KeyUp(Lmb); action_dispatch=BrushToolMessage::DragStop),
//
// ToolMessage
entry!(KeyDown(KeyV); action_dispatch=ToolMessage::ActivateToolSelect),
entry!(KeyDown(KeyZ); action_dispatch=ToolMessage::ActivateToolNavigate),
Expand All @@ -219,6 +224,7 @@ pub fn default_mapping() -> Mapping {
entry!(KeyDown(KeyM); action_dispatch=ToolMessage::ActivateToolRectangle),
entry!(KeyDown(KeyE); action_dispatch=ToolMessage::ActivateToolEllipse),
entry!(KeyDown(KeyY); action_dispatch=ToolMessage::ActivateToolShape),
entry!(KeyDown(KeyB); action_dispatch=ToolMessage::ActivateToolBrush),
entry!(KeyDown(KeyX); modifiers=[Shift, Accel], action_dispatch=ToolMessage::ResetColors),
entry!(KeyDown(KeyX); modifiers=[Shift], action_dispatch=ToolMessage::SwapColors),
entry!(KeyDown(KeyC); modifiers=[Alt], action_dispatch=ToolMessage::SelectRandomPrimaryColor),
Expand Down
50 changes: 26 additions & 24 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ use document_legacy::layers::style::{Fill, RenderData, ViewMode};
use document_legacy::layers::text_layer::Font;
use document_legacy::{DocumentError, DocumentResponse, LayerId, Operation as DocumentOperation};
use graph_craft::document::NodeId;
use graph_craft::{concrete, Type, TypeDescriptor};
use graphene_core::raster::{Color, ImageFrame};
use graphene_core::Cow;

use glam::{DAffine2, DVec2};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -1050,39 +1052,39 @@ impl DocumentMessageHandler {
return None;
};

// Skip processing under node graph frame input if not connected
if !node_network.connected_to_output(node_network.inputs[0], false) {
return Some(
PortfolioMessage::ProcessNodeGraphFrame {
// Find the primary input type of the node graph
let primary_input_type = node_network.input_types().next().clone();
let response = match primary_input_type {
// Only calclate the frame if the primary input is an image
Some(ty) if ty == concrete!(ImageFrame) => {
// Calculate the size of the region to be exported
let old_transforms = self.remove_document_transform();
let transform = self.document_legacy.multiply_transforms(&layer_path).unwrap();
let size = DVec2::new(transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length());

let svg = self.render_document(size, transform.inverse(), persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path));
self.restore_document_transform(old_transforms);

FrontendMessage::TriggerNodeGraphFrameGenerate {
document_id,
layer_path,
image_data: Default::default(),
size: (0, 0),
svg,
size,
imaginate_node,
}
.into(),
);
}

// Calculate the size of the region to be exported

let old_transforms = self.remove_document_transform();
let transform = self.document_legacy.multiply_transforms(&layer_path).unwrap();
let size = DVec2::new(transform.transform_vector2(DVec2::new(1., 0.)).length(), transform.transform_vector2(DVec2::new(0., 1.)).length());

let svg = self.render_document(size, transform.inverse(), persistent_data, DocumentRenderMode::OnlyBelowLayerInFolder(&layer_path));
self.restore_document_transform(old_transforms);

Some(
FrontendMessage::TriggerNodeGraphFrameGenerate {
.into()
}
// Skip processing under node graph frame input if not connected
_ => PortfolioMessage::ProcessNodeGraphFrame {
document_id,
layer_path,
svg,
size,
image_data: Default::default(),
size: (0, 0),
imaginate_node,
}
.into(),
)
};
Some(response)
}

/// Remove the artwork and artboard pan/tilt/zoom to render it without the user's viewport navigation, and save it to be restored at the end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,16 @@ impl<'a> ModifyInputsContext<'a> {
});
}

fn transform_change(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, skip_rerender: bool) {
fn transform_change(&mut self, transform: DAffine2, transform_in: TransformIn, parent_transform: DAffine2, bounds: LayerBounds, skip_rerender: bool) {
self.modify_inputs("Transform", skip_rerender, |inputs| {
let layer_transform = transform_utils::get_current_transform(inputs);
let to = match transform_in {
TransformIn::Local => DAffine2::IDENTITY,
TransformIn::Scope { scope } => scope * parent_transform,
TransformIn::Viewport => parent_transform,
};
let transform = to.inverse() * transform * to * layer_transform;
let pivot = DAffine2::from_translation(bounds.layerspace_pivot(transform_utils::get_current_normalized_pivot(inputs)));
let transform = pivot.inverse() * to.inverse() * transform * to * pivot * layer_transform;
transform_utils::update_transform(inputs, transform);
});
}
Expand All @@ -140,7 +141,7 @@ impl<'a> ModifyInputsContext<'a> {
TransformIn::Viewport => parent_transform,
};
let pivot = DAffine2::from_translation(bounds.layerspace_pivot(transform_utils::get_current_normalized_pivot(inputs)));
let transform = to.inverse() * transform * pivot;
let transform = pivot.inverse() * to.inverse() * transform * pivot;
transform_utils::update_transform(inputs, transform);
});
}
Expand All @@ -150,7 +151,7 @@ impl<'a> ModifyInputsContext<'a> {
let layer_transform = transform_utils::get_current_transform(inputs);
let old_pivot_transform = DAffine2::from_translation(bounds.local_pivot(transform_utils::get_current_normalized_pivot(inputs)));
let new_pivot_transform = DAffine2::from_translation(bounds.local_pivot(new_pivot));
let transform = layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
transform_utils::update_transform(inputs, transform);
inputs[5] = NodeInput::value(TaggedValue::DVec2(new_pivot), false);
});
Expand Down Expand Up @@ -184,6 +185,7 @@ impl<'a> ModifyInputsContext<'a> {

[new_bounds_min, new_bounds_max] = transform_utils::nonzero_subpath_bounds(subpaths);
});

self.modify_inputs("Transform", false, |inputs| {
let layer_transform = transform_utils::get_current_transform(inputs);
let normalized_pivot = transform_utils::get_current_normalized_pivot(inputs);
Expand All @@ -193,7 +195,7 @@ impl<'a> ModifyInputsContext<'a> {
let new_pivot_transform = DAffine2::from_translation(new_layerspace_pivot);
let old_pivot_transform = DAffine2::from_translation(old_layerspace_pivot);

let transform = layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
let transform = new_pivot_transform.inverse() * old_pivot_transform * layer_transform * old_pivot_transform.inverse() * new_pivot_transform;
transform_utils::update_transform(inputs, transform);
});
}
Expand Down Expand Up @@ -225,8 +227,9 @@ impl MessageHandler<GraphOperationMessage, (&mut Document, &mut NodeGraphMessage
skip_rerender,
} => {
let parent_transform = document.multiply_transforms(&layer[..layer.len() - 1]).unwrap_or_default();
let bounds = LayerBounds::new(document, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new(&layer, document, node_graph, responses) {
modify_inputs.transform_change(transform, transform_in, parent_transform, skip_rerender);
modify_inputs.transform_change(transform, transform_in, parent_transform, bounds, skip_rerender);
}

let transform = transform.to_cols_array();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use graph_craft::document::*;
use graph_craft::imaginate_input::ImaginateSamplingMethod;
use graph_craft::NodeIdentifier;
use graphene_core::raster::{BlendMode, Color, Image, ImageFrame, LuminanceCalculation};
use graphene_core::vector::VectorData;
use graphene_core::*;

use once_cell::sync::Lazy;
Expand Down Expand Up @@ -448,6 +449,35 @@ fn static_nodes() -> Vec<DocumentNodeType> {
}],
properties: node_properties::blur_image_properties,
},
DocumentNodeType {
name: "Brush",
category: "Brush",
identifier: NodeImplementation::proto("graphene_std::brush::BrushNode"),
inputs: vec![
DocumentInputType::value("None", TaggedValue::None, false),
DocumentInputType::value("Trace", TaggedValue::VecDVec2((0..2).map(|x| DVec2::new(x as f64 * 10., 0.)).collect()), true),
DocumentInputType::value("Diameter", TaggedValue::F64(40.), false),
DocumentInputType::value("Hardness", TaggedValue::F64(50.), false),
DocumentInputType::value("Flow", TaggedValue::F64(100.), false),
DocumentInputType::value("Color", TaggedValue::Color(Color::BLACK), false),
],
outputs: vec![DocumentOutputType {
name: "Image",
data_type: FrontendGraphDataType::Raster,
}],
properties: node_properties::brush_node_properties,
},
DocumentNodeType {
name: "Extract Vector Points",
category: "Brush",
identifier: NodeImplementation::proto("graphene_std::brush::VectorPointsNode"),
inputs: vec![DocumentInputType::value("VectorData", TaggedValue::VectorData(VectorData::empty()), true)],
outputs: vec![DocumentOutputType {
name: "Vector Points",
data_type: FrontendGraphDataType::General,
}],
properties: node_properties::no_properties,
},
DocumentNodeType {
name: "Cache",
category: "Structural",
Expand Down Expand Up @@ -490,6 +520,14 @@ fn static_nodes() -> Vec<DocumentNodeType> {
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: |_document_node, _node_id, _context| node_properties::string_properties("A bitmap image embedded in this node"),
},
DocumentNodeType {
name: "Ref",
category: "Structural",
identifier: NodeImplementation::proto("graphene_std::memo::CacheNode"),
inputs: vec![DocumentInputType::value("Image", TaggedValue::ImageFrame(ImageFrame::empty()), true)],
outputs: vec![DocumentOutputType::new("Image", FrontendGraphDataType::Raster)],
properties: node_properties::no_properties,
},
#[cfg(feature = "gpu")]
DocumentNodeType {
name: "GpuImage",
Expand Down Expand Up @@ -841,15 +879,26 @@ impl DocumentNodeType {
}

pub fn wrap_network_in_scope(network: NodeNetwork) -> NodeNetwork {
// if the network has no inputs, it doesn't need to be wrapped in a scope
if network.inputs.is_empty() {
return network;
}

assert_eq!(network.inputs.len(), 1, "Networks wrapped in scope must have exactly one input");
let input_type = network.nodes[&network.inputs[0]].inputs.iter().find(|&i| matches!(i, NodeInput::Network(_))).unwrap().clone();
let input = network.nodes[&network.inputs[0]].inputs.iter().find(|&i| matches!(i, NodeInput::Network(_))).cloned();

// if the network has no network inputs, it doesn't need to be wrapped in a scope either
let Some(input_type) = input else {
return network;
};

let inner_network = DocumentNode {
name: "Scope".to_string(),
implementation: DocumentNodeImplementation::Network(network),
inputs: vec![NodeInput::node(0, 1)],
metadata: DocumentNodeMetadata::default(),
};

// wrap the inner network in a scope
let nodes = vec![
resolve_document_node_type("Begin Scope")
Expand Down Expand Up @@ -890,7 +939,6 @@ pub fn new_image_network(output_offset: i32, output_node_id: NodeId) -> NodeNetw
}

pub fn new_vector_network(subpaths: Vec<bezier_rs::Subpath<uuid::ManipulatorGroupId>>) -> NodeNetwork {
let input = resolve_document_node_type("Input Frame").expect("Input Frame node does not exist");
let path_generator = resolve_document_node_type("Path Generator").expect("Path Generator node does not exist");
let transform = resolve_document_node_type("Transform").expect("Transform node does not exist");
let fill = resolve_document_node_type("Fill").expect("Fill node does not exist");
Expand All @@ -905,15 +953,14 @@ pub fn new_vector_network(subpaths: Vec<bezier_rs::Subpath<uuid::ManipulatorGrou
};

NodeNetwork {
inputs: vec![0],
outputs: vec![NodeOutput::new(5, 0)],
inputs: vec![],
outputs: vec![NodeOutput::new(4, 0)],
nodes: [
input.to_document_node_default_inputs([], next_pos()),
path_generator.to_document_node_default_inputs([Some(NodeInput::value(TaggedValue::Subpaths(subpaths), false))], next_pos()),
transform.to_document_node_default_inputs([Some(NodeInput::node(1, 0))], next_pos()),
fill.to_document_node_default_inputs([Some(NodeInput::node(2, 0))], next_pos()),
stroke.to_document_node_default_inputs([Some(NodeInput::node(3, 0))], next_pos()),
output.to_document_node_default_inputs([Some(NodeInput::node(4, 0))], next_pos()),
transform.to_document_node_default_inputs([Some(NodeInput::node(0, 0))], next_pos()),
fill.to_document_node_default_inputs([Some(NodeInput::node(1, 0))], next_pos()),
stroke.to_document_node_default_inputs([Some(NodeInput::node(2, 0))], next_pos()),
output.to_document_node_default_inputs([Some(NodeInput::node(3, 0))], next_pos()),
]
.into_iter()
.enumerate()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,16 @@ pub fn blur_image_properties(document_node: &DocumentNode, node_id: NodeId, _con
vec![LayoutGroup::Row { widgets: radius }, LayoutGroup::Row { widgets: sigma }]
}

pub fn brush_node_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let color = color_widget(document_node, node_id, 5, "Color", ColorInput::default(), true);

let size = number_widget(document_node, node_id, 2, "Diameter", NumberInput::default().min(0.).max(100.).unit(" px"), true);
let hardness = number_widget(document_node, node_id, 3, "Hardness", NumberInput::default().min(0.).max(100.).unit("%"), true);
let flow = number_widget(document_node, node_id, 4, "Flow", NumberInput::default().min(1.).max(100.).unit("%"), true);

vec![color, LayoutGroup::Row { widgets: size }, LayoutGroup::Row { widgets: hardness }, LayoutGroup::Row { widgets: flow }]
}

pub fn adjust_threshold_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let thereshold_min = number_widget(document_node, node_id, 1, "Min Luminance", NumberInput::default().min(0.).max(100.).unit("%"), true);
let thereshold_max = number_widget(document_node, node_id, 2, "Max Luminance", NumberInput::default().min(0.).max(100.).unit("%"), true);
Expand Down
1 change: 1 addition & 0 deletions editor/src/messages/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub use crate::messages::workspace::{WorkspaceMessage, WorkspaceMessageDiscrimin
pub use crate::messages::broadcast::broadcast_event::{BroadcastEvent, BroadcastEventDiscriminant};
pub use crate::messages::message::{Message, MessageDiscriminant};
pub use crate::messages::tool::tool_messages::artboard_tool::{ArtboardToolMessage, ArtboardToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::brush_tool::{BrushToolMessage, BrushToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::ellipse_tool::{EllipseToolMessage, EllipseToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::eyedropper_tool::{EyedropperToolMessage, EyedropperToolMessageDiscriminant};
pub use crate::messages::tool::tool_messages::fill_tool::{FillToolMessage, FillToolMessageDiscriminant};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@ use crate::messages::prelude::*;

use bezier_rs::Subpath;
use document_legacy::{LayerId, Operation};
use graph_craft::document::NodeNetwork;
use graphene_core::uuid::ManipulatorGroupId;

use glam::DAffine2;
use std::collections::VecDeque;

/// Create a new vector layer from a vector of [`bezier_rs::Subpath`].
pub fn new_vector_layer(subpaths: Vec<Subpath<ManipulatorGroupId>>, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
responses.push_back(DocumentMessage::DeselectAllLayers.into());

let network = node_graph::new_vector_network(subpaths);
new_custom_layer(network, layer_path, responses);
}

pub fn new_custom_layer(network: NodeNetwork, layer_path: Vec<LayerId>, responses: &mut VecDeque<Message>) {
responses.push_back(DocumentMessage::DeselectAllLayers.into());
responses.push_back(
Operation::AddNodeGraphFrame {
path: layer_path.clone(),
Expand Down
8 changes: 5 additions & 3 deletions editor/src/messages/tool/tool_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ pub enum ToolMessage {
#[child]
Text(TextToolMessage),

// #[remain::unsorted]
// #[child]
// Brush(BrushToolMessage),
#[remain::unsorted]
#[child]
Brush(BrushToolMessage),
// #[remain::unsorted]
// #[child]
// Heal(HealToolMessage),
Expand Down Expand Up @@ -119,6 +119,8 @@ pub enum ToolMessage {
#[remain::unsorted]
ActivateToolShape,

#[remain::unsorted]
ActivateToolBrush,
#[remain::unsorted]
ActivateToolImaginate,
#[remain::unsorted]
Expand Down
3 changes: 3 additions & 0 deletions editor/src/messages/tool/tool_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
#[remain::unsorted]
ToolMessage::ActivateToolShape => responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Shape }.into()),

#[remain::unsorted]
ToolMessage::ActivateToolBrush => responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Brush }.into()),
#[remain::unsorted]
ToolMessage::ActivateToolImaginate => responses.push_front(ToolMessage::ActivateTool { tool_type: ToolType::Imaginate }.into()),

Expand Down Expand Up @@ -276,6 +278,7 @@ impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocess
ActivateToolEllipse,
ActivateToolShape,

ActivateToolBrush,
ActivateToolImaginate,

SelectRandomPrimaryColor,
Expand Down
Loading