Retire layer paths used throughout the code (#1531)

* Part 1

* Part 2

* Part 3

* Part 4

* Part 5

* Part 6

* Part 7

* Part 8
This commit is contained in:
Keavon Chambers 2023-12-21 19:32:46 -08:00 committed by GitHub
parent 5c7e04a725
commit 7bfe0ce55b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
73 changed files with 532 additions and 798 deletions

View file

@ -38,10 +38,9 @@ const SIDE_EFFECT_FREE_MESSAGES: &[MessageDiscriminant] = &[
PropertiesPanelMessageDiscriminant::Refresh,
))),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::DocumentStructureChanged)),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerTreeStructure),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::UpdateDocumentLayerStructure),
MessageDiscriminant::Frontend(FrontendMessageDiscriminant::TriggerFontLoad),
MessageDiscriminant::Broadcast(BroadcastMessageDiscriminant::TriggerEvent(BroadcastEventDiscriminant::DocumentIsDirty)),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::InputFrameRasterizeRegionBelowLayer)),
MessageDiscriminant::Portfolio(PortfolioMessageDiscriminant::Document(DocumentMessageDiscriminant::Overlays(OverlaysMessageDiscriminant::Draw))),
];
@ -262,6 +261,7 @@ mod test {
use crate::messages::tool::tool_messages::tool_prelude::ToolType;
use crate::test_utils::EditorTestUtils;
use graph_craft::document::NodeId;
use graphene_core::raster::color::Color;
fn init_logger() {
@ -359,7 +359,7 @@ mod test {
fn copy_paste_folder() {
let mut editor = create_editor_with_three_layers();
const FOLDER_ID: u64 = 3;
const FOLDER_ID: NodeId = 3;
editor.handle_message(GraphOperationMessage::NewCustomLayer {
id: FOLDER_ID,

View file

@ -78,7 +78,7 @@ impl LayoutHolder for ExportDialogMessageHandler {
NumberInput::new(Some(self.scale_factor))
.unit("")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.disabled(self.file_type == FileType::Svg)
.on_update(|number_input: &NumberInput| ExportDialogMessage::ScaleFactor(number_input.value.unwrap()).into())
.min_width(200)

View file

@ -95,7 +95,7 @@ impl LayoutHolder for NewDocumentDialogMessageHandler {
.label("W")
.unit(" px")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.is_integer(true)
.disabled(self.infinite)
.min_width(100)
@ -106,7 +106,7 @@ impl LayoutHolder for NewDocumentDialogMessageHandler {
.label("H")
.unit(" px")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.is_integer(true)
.disabled(self.infinite)
.min_width(100)

View file

@ -57,7 +57,7 @@ impl PreferencesDialogMessageHandler {
NumberInput::new(Some(preferences.imaginate_refresh_frequency))
.unit(" seconds")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.min_width(200)
.on_update(|number_input: &NumberInput| PreferencesMessage::ImaginateRefreshFrequency { seconds: number_input.value.unwrap() }.into())
.widget_holder(),

View file

@ -5,7 +5,7 @@ use crate::messages::prelude::*;
/// A dialog for confirming the closing a document with unsaved changes.
pub struct CloseDocumentDialog {
pub document_name: String,
pub document_id: u64,
pub document_id: DocumentId,
}
impl DialogLayoutHolder for CloseDocumentDialog {

View file

@ -80,7 +80,7 @@ pub enum FrontendMessage {
TriggerImport,
TriggerIndexedDbRemoveDocument {
#[serde(rename = "documentId")]
document_id: u64,
document_id: DocumentId,
},
TriggerIndexedDbWriteDocument {
document: String,
@ -111,7 +111,7 @@ pub enum FrontendMessage {
// Update prefix: give the frontend a new value or state for it to use
UpdateActiveDocument {
#[serde(rename = "documentId")]
document_id: u64,
document_id: DocumentId,
},
UpdateDialogButtons {
#[serde(rename = "layoutTarget")]
@ -139,11 +139,11 @@ pub enum FrontendMessage {
UpdateDocumentLayerDetails {
data: LayerPanelEntry,
},
UpdateDocumentLayerTreeStructure {
UpdateDocumentLayerStructure {
#[serde(rename = "dataBuffer")]
data_buffer: RawBuffer,
},
UpdateDocumentLayerTreeStructureJs {
UpdateDocumentLayerStructureJs {
#[serde(rename = "dataBuffer")]
data_buffer: JsRawBuffer,
},
@ -180,7 +180,7 @@ pub enum FrontendMessage {
},
UpdateImageData {
#[serde(rename = "documentId")]
document_id: u64,
document_id: DocumentId,
#[serde(rename = "imageData")]
image_data: Vec<FrontendImageData>,
},

View file

@ -1,5 +1,5 @@
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use serde::{Deserialize, Serialize};
@ -10,18 +10,14 @@ pub struct FrontendDocumentDetails {
#[serde(rename = "isSaved")]
pub is_saved: bool,
pub name: String,
pub id: u64,
pub id: DocumentId,
}
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize, specta::Type)]
pub struct FrontendImageData {
pub path: Vec<LayerId>,
pub mime: String,
#[serde(skip)]
pub image_data: std::sync::Arc<Vec<u8>>,
pub transform: Option<[f64; 6]>,
#[serde(rename = "nodeId")]
pub node_id: Option<graph_craft::document::NodeId>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, Deserialize, specta::Type)]

View file

@ -1,7 +1,6 @@
use crate::messages::input_mapper::utility_types::input_keyboard::Key;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis};
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use graph_craft::document::{NodeId, NodeNetwork};
@ -42,14 +41,14 @@ pub enum DocumentMessage {
BackupDocument {
network: NodeNetwork,
},
ClearLayerTree,
ClearLayersPanel,
CommitTransaction,
CreateEmptyFolder {
parent: LayerNodeIdentifier,
},
DebugPrintDocument,
DeleteLayer {
layer_path: Vec<LayerId>,
id: NodeId,
},
DeleteSelectedLayers,
DeselectAllLayers,
@ -61,20 +60,11 @@ pub enum DocumentMessage {
flip_axis: FlipAxis,
},
GroupSelectedLayers,
ImaginateClear {
layer_path: Vec<LayerId>,
},
ImaginateGenerate {
layer_path: Vec<LayerId>,
},
ImaginateGenerate,
ImaginateRandom {
layer_path: Vec<LayerId>,
imaginate_node: Vec<NodeId>,
then_generate: bool,
},
InputFrameRasterizeRegionBelowLayer {
layer_path: Vec<LayerId>,
},
MoveSelectedLayersTo {
parent: LayerNodeIdentifier,
insert_index: isize,
@ -106,7 +96,7 @@ pub enum DocumentMessage {
relative_index_offset: isize,
},
SelectLayer {
layer_path: Vec<LayerId>,
id: NodeId,
ctrl: bool,
shift: bool,
},
@ -132,7 +122,7 @@ pub enum DocumentMessage {
},
StartTransaction,
ToggleLayerExpansion {
layer: NodeId,
id: NodeId,
},
Undo,
UndoFinished,

View file

@ -10,7 +10,6 @@ use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::document::utility_types::document_metadata::{is_artboard, DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::layer_panel::RawBuffer;
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, DocumentMode, FlipAxis, PTZ};
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils::{get_blend_mode, get_opacity};
@ -224,7 +223,7 @@ fn root_network() -> NodeNetwork {
}
pub struct DocumentInputs<'a> {
pub document_id: u64,
pub document_id: DocumentId,
pub ipp: &'a InputPreprocessorMessageHandler,
pub persistent_data: &'a PersistentData,
pub executor: &'a mut NodeGraphExecutor,
@ -324,7 +323,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
};
let translation = (aggregated - center) * axis;
responses.add(GraphOperationMessage::TransformChange {
layer: layer.to_path(),
layer,
transform: DAffine2::from_translation(translation),
transform_in: TransformIn::Viewport,
skip_rerender: false,
@ -333,10 +332,10 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(BroadcastEvent::DocumentIsDirty);
}
BackupDocument { network } => self.backup_with_document(network, responses),
ClearLayerTree => {
// Send an empty layer tree
let data_buffer: RawBuffer = Self::default().serialize_root().as_slice().into();
responses.add(FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer });
ClearLayersPanel => {
// Send an empty layer list
let data_buffer: RawBuffer = Self::default().serialize_root();
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
// Clear the options bar
responses.add(LayoutMessage::SendLayout {
@ -359,8 +358,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
DebugPrintDocument => {
info!("{:#?}", self.network);
}
DeleteLayer { layer_path } => {
responses.add(GraphOperationMessage::DeleteLayer { id: layer_path[0] });
DeleteLayer { id } => {
responses.add(GraphOperationMessage::DeleteLayer { id });
responses.add_front(BroadcastEvent::ToolAbort);
}
DeleteSelectedLayers => {
@ -368,9 +367,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add_front(BroadcastEvent::SelectionChanged);
for path in self.metadata().shallowest_unique_layers(self.metadata().selected_layers()) {
responses.add_front(DocumentMessage::DeleteLayer {
layer_path: path.last().unwrap().to_path(),
});
responses.add_front(DocumentMessage::DeleteLayer { id: path.last().unwrap().to_node() });
}
responses.add(BroadcastEvent::DocumentIsDirty);
@ -384,8 +381,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
DocumentStructureChanged => {
self.update_layers_panel_options_bar_widgets(responses);
let data_buffer: RawBuffer = self.serialize_root().as_slice().into();
responses.add(FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer })
let data_buffer: RawBuffer = self.serialize_root();
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer })
}
DuplicateSelectedLayers => {
// TODO: Reimplement selected layer duplication
@ -406,7 +403,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
let bbox_trans = DAffine2::from_translation(-center);
for layer in self.metadata().selected_layers() {
responses.add(GraphOperationMessage::TransformChange {
layer: layer.to_path(),
layer,
transform: DAffine2::from_scale(scale),
transform_in: TransformIn::Scope { scope: bbox_trans },
skip_rerender: false,
@ -438,13 +435,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![folder_id] });
}
ImaginateClear { layer_path } => responses.add(InputFrameRasterizeRegionBelowLayer { layer_path }),
ImaginateGenerate { layer_path } => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path }),
ImaginateRandom {
layer_path,
imaginate_node,
then_generate,
} => {
ImaginateGenerate => responses.add(PortfolioMessage::SubmitGraphRender { document_id }),
ImaginateRandom { imaginate_node, then_generate } => {
// Generate a random seed. We only want values between -2^53 and 2^53, because integer values
// outside of this range can get rounded in f64
let random_bits = generate_uuid();
@ -459,10 +451,9 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
// Generate the image
if then_generate {
responses.add(DocumentMessage::ImaginateGenerate { layer_path });
responses.add(DocumentMessage::ImaginateGenerate);
}
}
InputFrameRasterizeRegionBelowLayer { layer_path } => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path }),
MoveSelectedLayersTo { parent, insert_index } => {
let selected_layers = self.metadata().selected_layers().collect::<Vec<_>>();
@ -496,7 +487,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
// Nudge translation
if !ipp.keyboard.key(resize) {
responses.add(GraphOperationMessage::TransformChange {
layer: layer.to_path(),
layer,
transform: DAffine2::from_translation(delta),
transform_in: TransformIn::Local,
skip_rerender: false,
@ -525,7 +516,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
let original_transform = self.metadata().upstream_transform(layer.to_node());
let new = to.inverse() * transformation * to * original_transform;
responses.add(GraphOperationMessage::TransformSet {
layer: layer.to_path(),
layer,
transform: new,
transform_in: TransformIn::Local,
skip_rerender: false,
@ -561,7 +552,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![layer.to_node()] });
responses.add(GraphOperationMessage::TransformSet {
layer: layer.to_path(),
layer,
transform,
transform_in: TransformIn::Local,
skip_rerender: false,
@ -655,16 +646,15 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
SelectedLayersReorder { relative_index_offset } => {
self.selected_layers_reorder(relative_index_offset, responses);
}
SelectLayer { layer_path, ctrl, shift } => {
let clicked_node = *layer_path.last().expect("Cannot select root");
let layer = LayerNodeIdentifier::new(clicked_node, self.network());
SelectLayer { id, ctrl, shift } => {
let layer = LayerNodeIdentifier::new(id, self.network());
let mut nodes = vec![];
// If we have shift pressed and a layer already selected then fill the range
if let Some(last_selected) = self.layer_range_selection_reference.filter(|_| shift) {
nodes.push(last_selected.to_node());
nodes.push(clicked_node);
nodes.push(id);
// Fill the selection range
self.metadata()
@ -677,13 +667,13 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
if ctrl {
// Toggle selection when holding ctrl
if self.metadata().selected_layers_contains(layer) {
responses.add_front(NodeGraphMessage::SelectedNodesRemove { nodes: vec![clicked_node] });
responses.add_front(NodeGraphMessage::SelectedNodesRemove { nodes: vec![id] });
} else {
responses.add_front(NodeGraphMessage::SelectedNodesAdd { nodes: vec![clicked_node] });
responses.add_front(NodeGraphMessage::SelectedNodesAdd { nodes: vec![id] });
}
responses.add(BroadcastEvent::SelectionChanged);
} else {
nodes.push(clicked_node);
nodes.push(id);
}
// Set our last selection reference
@ -703,7 +693,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
SetBlendModeForSelectedLayers { blend_mode } => {
self.backup(responses);
for layer in self.metadata().selected_layers_except_artboards() {
responses.add(GraphOperationMessage::BlendModeSet { layer: layer.to_path(), blend_mode });
responses.add(GraphOperationMessage::BlendModeSet { layer, blend_mode });
}
}
SetOpacityForSelectedLayers { opacity } => {
@ -711,7 +701,7 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
let opacity = opacity.clamp(0., 1.) as f32;
for layer in self.metadata().selected_layers_except_artboards() {
responses.add(GraphOperationMessage::OpacitySet { layer: layer.to_path(), opacity });
responses.add(GraphOperationMessage::OpacitySet { layer, opacity });
}
}
SetOverlaysVisibility { visible } => {
@ -742,8 +732,8 @@ impl MessageHandler<DocumentMessage, DocumentInputs<'_>> for DocumentMessageHand
responses.add_front(NodeGraphMessage::RunDocumentGraph);
}
StartTransaction => self.backup(responses),
ToggleLayerExpansion { layer } => {
let layer = LayerNodeIdentifier::new(layer, self.network());
ToggleLayerExpansion { id } => {
let layer = LayerNodeIdentifier::new(id, self.network());
if self.collapsed.contains(&layer) {
self.collapsed.retain(|&collapsed_layer| collapsed_layer != layer);
} else {
@ -908,23 +898,23 @@ impl DocumentMessageHandler {
std::iter::empty()
}
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure: &mut Vec<u64>, data: &mut Vec<LayerId>, path: &mut Vec<LayerId>) {
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure: &mut Vec<LayerNodeIdentifier>, data: &mut Vec<LayerNodeIdentifier>, path: &mut Vec<LayerNodeIdentifier>) {
let mut space = 0;
for layer_node in folder.children(self.metadata()) {
data.push(layer_node.to_node());
data.push(layer_node);
space += 1;
if layer_node.has_children(self.metadata()) && !self.collapsed.contains(&layer_node) {
path.push(layer_node.to_node());
path.push(layer_node);
// TODO: Skip if folder is not expanded.
structure.push(space);
structure.push(LayerNodeIdentifier::new_unchecked(space));
self.serialize_structure(layer_node, structure, data, path);
space = 0;
path.pop();
}
}
structure.push(space | 1 << 63);
structure.push(LayerNodeIdentifier::new_unchecked(space | 1 << 63));
}
/// Serializes the layer structure into a condensed 1D structure.
@ -959,13 +949,15 @@ impl DocumentMessageHandler {
/// [3427872634365736244,18115028555707261608,15878401910454357952]
/// [3427872634365736244,18115028555707261608,449479075714955186]
/// ```
pub fn serialize_root(&self) -> Vec<u64> {
let (mut structure, mut data) = (vec![0], Vec::new());
pub fn serialize_root(&self) -> RawBuffer {
let mut structure = vec![LayerNodeIdentifier::ROOT];
let mut data = Vec::new();
self.serialize_structure(self.metadata().root(), &mut structure, &mut data, &mut vec![]);
structure[0] = structure.len() as u64 - 1;
structure[0] = LayerNodeIdentifier::new_unchecked(structure.len() as NodeId - 1);
structure.extend(data);
structure
structure.iter().map(|id| id.to_node()).collect::<Vec<_>>().as_slice().into()
}
/// Places a document into the history system
@ -1064,10 +1056,9 @@ impl DocumentMessageHandler {
///
/// This function updates the insert index so that it points to the same place after the specified `layers` are deleted.
fn update_insert_index(&self, layers: &[LayerNodeIdentifier], parent: LayerNodeIdentifier, insert_index: isize) -> isize {
let layer_ids_above = parent.children(self.metadata()).take(if insert_index < 0 { usize::MAX } else { insert_index as usize });
let new_insert_index = layer_ids_above.filter(|layer_id| !layers.contains(layer_id)).count() as isize;
new_insert_index
let take_amount = if insert_index < 0 { usize::MAX } else { insert_index as usize };
let layer_ids_above = parent.children(self.metadata()).take(take_amount);
layer_ids_above.filter(|layer_id| !layers.contains(layer_id)).count() as isize
}
pub fn new_layer_parent(&self) -> LayerNodeIdentifier {

View file

@ -1,5 +1,4 @@
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use bezier_rs::Subpath;
@ -16,56 +15,54 @@ use graphene_core::{Artboard, Color};
use glam::{DAffine2, DVec2, IVec2};
pub type LayerIdentifier = Vec<LayerId>;
#[impl_message(Message, DocumentMessage, GraphOperation)]
#[derive(PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum GraphOperationMessage {
FillSet {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
fill: Fill,
},
OpacitySet {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
opacity: f32,
},
BlendModeSet {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
blend_mode: BlendMode,
},
UpdateBounds {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
old_bounds: [DVec2; 2],
new_bounds: [DVec2; 2],
},
StrokeSet {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
stroke: Stroke,
},
TransformChange {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
transform: DAffine2,
transform_in: TransformIn,
skip_rerender: bool,
},
TransformSet {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
transform: DAffine2,
transform_in: TransformIn,
skip_rerender: bool,
},
TransformSetPivot {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
pivot: DVec2,
},
Vector {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
modification: VectorDataModification,
},
Brush {
layer: LayerIdentifier,
layer: LayerNodeIdentifier,
strokes: Vec<BrushStroke>,
},

View file

@ -1,6 +1,5 @@
use super::{resolve_document_node_type, VectorDataModification};
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use bezier_rs::Subpath;
@ -26,7 +25,6 @@ struct ModifyInputsContext<'a> {
document_network: &'a mut NodeNetwork,
node_graph: &'a mut NodeGraphMessageHandler,
responses: &'a mut VecDeque<Message>,
layer: &'a [LayerId],
outwards_links: HashMap<NodeId, Vec<NodeId>>,
layer_node: Option<NodeId>,
}
@ -38,27 +36,25 @@ impl<'a> ModifyInputsContext<'a> {
document_network,
node_graph,
responses,
layer: &[],
layer_node: None,
document_metadata,
}
}
fn new_with_layer(
layer: &'a [LayerId],
id: NodeId,
document_network: &'a mut NodeNetwork,
document_metadata: &'a mut DocumentMetadata,
node_graph: &'a mut NodeGraphMessageHandler,
responses: &'a mut VecDeque<Message>,
) -> Option<Self> {
let mut document = Self::new(document_network, document_metadata, node_graph, responses);
let Some(mut id) = layer.last().copied() else {
error!("Tried to modify root layer");
return None;
};
let mut id = id;
while !document.document_network.nodes.get(&id)?.is_layer() {
id = document.outwards_links.get(&id)?.first().copied()?;
}
document.layer_node = Some(id);
Some(document)
}
@ -325,10 +321,9 @@ impl<'a> ModifyInputsContext<'a> {
self.node_graph.network.clear();
self.responses.add(PropertiesPanelMessage::Refresh);
let layer_path = self.layer.to_vec();
if !skip_rerender {
self.responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
self.responses.add(NodeGraphMessage::RunDocumentGraph);
} else {
// Code was removed from here which cleared the frame
}
@ -354,10 +349,9 @@ impl<'a> ModifyInputsContext<'a> {
}
self.responses.add(PropertiesPanelMessage::Refresh);
let layer_path = self.layer.to_vec();
if !skip_rerender {
self.responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
self.responses.add(NodeGraphMessage::RunDocumentGraph);
} else {
// Code was removed from here which cleared the frame
}
@ -509,8 +503,8 @@ impl<'a> ModifyInputsContext<'a> {
self.update_bounds([old_bounds_min, old_bounds_max], [new_bounds_min, new_bounds_max]);
if empty {
if let Some(layer) = self.layer_node {
self.responses.add(DocumentMessage::DeleteLayer { layer_path: vec![layer] })
if let Some(id) = self.layer_node {
self.responses.add(DocumentMessage::DeleteLayer { id })
}
}
}
@ -594,27 +588,27 @@ impl MessageHandler<GraphOperationMessage, (&mut NodeNetwork, &mut DocumentMetad
) {
match message {
GraphOperationMessage::FillSet { layer, fill } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.fill_set(fill);
}
}
GraphOperationMessage::OpacitySet { layer, opacity } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.opacity_set(opacity);
}
}
GraphOperationMessage::BlendModeSet { layer, blend_mode } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.blend_mode_set(blend_mode);
}
}
GraphOperationMessage::UpdateBounds { layer, old_bounds, new_bounds } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.update_bounds(old_bounds, new_bounds);
}
}
GraphOperationMessage::StrokeSet { layer, stroke } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.stroke_set(stroke);
}
}
@ -624,10 +618,9 @@ impl MessageHandler<GraphOperationMessage, (&mut NodeNetwork, &mut DocumentMetad
transform_in,
skip_rerender,
} => {
let layer_identifier = LayerNodeIdentifier::new(*layer.last().unwrap(), document_network);
let parent_transform = document_metadata.downstream_transform_to_viewport(layer_identifier);
let bounds = LayerBounds::new(document_network, document_metadata, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
let parent_transform = document_metadata.downstream_transform_to_viewport(layer);
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.transform_change(transform, transform_in, parent_transform, bounds, skip_rerender);
}
}
@ -637,28 +630,27 @@ impl MessageHandler<GraphOperationMessage, (&mut NodeNetwork, &mut DocumentMetad
transform_in,
skip_rerender,
} => {
let layer_identifier = LayerNodeIdentifier::new(*layer.last().unwrap(), document_network);
let parent_transform = document_metadata.downstream_transform_to_viewport(layer_identifier);
let parent_transform = document_metadata.downstream_transform_to_viewport(layer);
let current_transform = Some(document_metadata.transform_to_viewport(layer_identifier));
let bounds = LayerBounds::new(document_network, document_metadata, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
let current_transform = Some(document_metadata.transform_to_viewport(layer));
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.transform_set(transform, transform_in, parent_transform, current_transform, bounds, skip_rerender);
}
}
GraphOperationMessage::TransformSetPivot { layer, pivot } => {
let bounds = LayerBounds::new(document_network, document_metadata, &layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
let bounds = LayerBounds::new(document_metadata, layer);
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.pivot_set(pivot, bounds);
}
}
GraphOperationMessage::Vector { layer, modification } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.vector_modify(modification);
}
}
GraphOperationMessage::Brush { layer, strokes } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&layer, document_network, document_metadata, node_graph, responses) {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(layer.to_node(), document_network, document_metadata, node_graph, responses) {
modify_inputs.brush_modify(strokes);
}
}
@ -744,7 +736,7 @@ impl MessageHandler<GraphOperationMessage, (&mut NodeNetwork, &mut DocumentMetad
load_network_structure(document_network, document_metadata, collapsed);
}
GraphOperationMessage::ResizeArtboard { id, location, dimensions } => {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(&[id], document_network, document_metadata, node_graph, responses) {
if let Some(mut modify_inputs) = ModifyInputsContext::new_with_layer(id, document_network, document_metadata, node_graph, responses) {
modify_inputs.resize_artboard(location, dimensions);
}
}

View file

@ -2,7 +2,6 @@ use crate::messages::portfolio::document::node_graph::VectorDataModification;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use bezier_rs::{ManipulatorGroup, Subpath};
use graph_craft::document::NodeNetwork;
use graph_craft::document::{value::TaggedValue, NodeInput};
use graphene_core::uuid::ManipulatorGroupId;
use graphene_core::vector::{ManipulatorPointId, SelectedType};
@ -53,8 +52,7 @@ pub struct LayerBounds {
impl LayerBounds {
/// Extract the layer bounds and their transform for a layer.
pub fn new(document_network: &NodeNetwork, document_metadata: &DocumentMetadata, layer: &[u64]) -> Self {
let layer = LayerNodeIdentifier::new(*layer.last().unwrap(), document_network);
pub fn new(document_metadata: &DocumentMetadata, layer: LayerNodeIdentifier) -> Self {
Self {
bounds: document_metadata.nonzero_bounding_box(layer),
bounds_transform: DAffine2::IDENTITY,

View file

@ -1,4 +1,3 @@
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use graph_craft::document::value::TaggedValue;
@ -11,14 +10,13 @@ pub enum NodeGraphMessage {
Init,
SelectedNodesUpdated,
ConnectNodesByLink {
output_node: u64,
output_node: NodeId,
output_node_connector_index: usize,
input_node: u64,
input_node: NodeId,
input_node_connector_index: usize,
},
Copy,
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>,
node_type: String,
x: i32,
@ -83,7 +81,6 @@ pub enum NodeGraphMessage {
input: NodeInput,
},
SetQualifiedInputValue {
layer_path: Vec<LayerId>,
node_path: Vec<NodeId>,
input_index: usize,
value: TaggedValue,

View file

@ -3,9 +3,7 @@ use super::load_network_structure;
use crate::messages::input_mapper::utility_types::macros::action_keys;
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use crate::node_graph_executor::GraphIdentifier;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{DocumentNode, NodeId, NodeInput, NodeNetwork, NodeOutput};
@ -99,13 +97,13 @@ pub struct FrontendNode {
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
pub struct FrontendNodeLink {
#[serde(rename = "linkStart")]
pub link_start: u64,
pub link_start: NodeId,
#[serde(rename = "linkStartOutputIndex")]
pub link_start_output_index: usize,
#[serde(rename = "linkEnd")]
pub link_end: u64,
pub link_end: NodeId,
#[serde(rename = "linkEndInputIndex")]
pub link_end_input_index: u64,
pub link_end_input_index: usize,
}
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, specta::Type)]
@ -124,10 +122,9 @@ impl FrontendNodeType {
#[derive(Debug, Clone, PartialEq)]
pub struct NodeGraphMessageHandler {
pub layer_path: Option<Vec<LayerId>>,
pub network: Vec<NodeId>,
has_selection: bool,
pub widgets: [LayoutGroup; 2],
widgets: [LayoutGroup; 2],
}
impl Default for NodeGraphMessageHandler {
@ -137,7 +134,6 @@ impl Default for NodeGraphMessageHandler {
let add_nodes_label_row = LayoutGroup::Row { widgets: vec![add_nodes_label] };
Self {
layer_path: None,
network: Vec::new(),
has_selection: false,
widgets: [add_nodes_label_row, LayoutGroup::default()],
@ -261,15 +257,13 @@ impl NodeGraphMessageHandler {
}
}
fn send_graph(network: &NodeNetwork, layer_path: &Option<Vec<LayerId>>, graph_view_overlay_open: bool, responses: &mut VecDeque<Message>) {
fn send_graph(network: &NodeNetwork, graph_view_overlay_open: bool, responses: &mut VecDeque<Message>) {
responses.add(PropertiesPanelMessage::Refresh);
if !graph_view_overlay_open {
return;
}
let layer_id = layer_path.as_ref().and_then(|path| path.last().copied());
// List of links in format (link_start, link_end, link_end_input_index)
let links = network
.nodes
@ -278,16 +272,16 @@ impl NodeGraphMessageHandler {
.filter_map(|(input, &link_end, link_end_input_index)| {
if let NodeInput::Node {
node_id: link_start,
output_index: link_start_index,
output_index: link_start_output_index,
// TODO: add ui for lambdas
lambda: _,
} = *input
{
Some(FrontendNodeLink {
link_start,
link_start_output_index: link_start_index,
link_start_output_index,
link_end,
link_end_input_index: link_end_input_index as u64,
link_end_input_index,
})
} else {
None
@ -318,8 +312,6 @@ impl NodeGraphMessageHandler {
});
let primary_output = if node.has_primary_output { outputs.next() } else { None };
let _graph_identifier = GraphIdentifier::new(layer_id);
nodes.push(FrontendNode {
is_layer: node.is_layer(),
id: *id,
@ -446,7 +438,7 @@ impl NodeGraphMessageHandler {
pub struct NodeGraphHandlerData<'a> {
pub document_network: &'a mut NodeNetwork,
pub document_metadata: &'a mut DocumentMetadata,
pub document_id: u64,
pub document_id: DocumentId,
pub document_name: &'a str,
pub collapsed: &'a mut Vec<LayerNodeIdentifier>,
pub input: &'a InputPreprocessorMessageHandler,
@ -495,7 +487,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
error!("No network");
return;
};
let Some(input_node) = network.nodes.get(&input_node) else {
let Some(input_node) = network.nodes.get(&node_id) else {
error!("No to");
return;
};
@ -569,11 +561,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
if let Some(network) = document_network.nested_network(&self.network) {
// Only generate node graph if one of the selected nodes is connected to the output
if metadata.selected_nodes().any(|&node_id| network.connected_to_output(node_id)) {
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
responses.add(NodeGraphMessage::RunDocumentGraph);
}
responses.add(NodeGraphMessage::RunDocumentGraph);
}
}
}
@ -612,7 +600,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
if let Some(network) = document_network.nested_network(&self.network) {
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
Self::send_graph(network, graph_view_overlay_open, responses);
}
self.update_selected(document_network, metadata, responses);
}
@ -640,7 +628,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::InsertNode { node_id, document_node });
}
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
Self::send_graph(network, graph_view_overlay_open, responses);
self.update_selected(document_network, metadata, responses);
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
}
@ -653,7 +641,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
self.network.pop();
}
if let Some(network) = document_network.nested_network(&self.network) {
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
Self::send_graph(network, graph_view_overlay_open, responses);
}
self.update_selected(document_network, metadata, responses);
}
@ -703,7 +691,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
node.metadata.position += IVec2::new(displacement_x, displacement_y)
}
}
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
Self::send_graph(network, graph_view_overlay_open, responses);
}
NodeGraphMessage::PasteNodes { serialized_nodes } => {
let Some(network) = document_network.nested_network(&self.network) else {
@ -752,7 +740,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::SendGraph { should_rerender: false });
}
NodeGraphMessage::RunDocumentGraph => responses.add(PortfolioMessage::SubmitGraphRender { document_id, layer_path: Vec::new() }),
NodeGraphMessage::RunDocumentGraph => responses.add(PortfolioMessage::SubmitGraphRender { document_id }),
NodeGraphMessage::SelectedNodesAdd { nodes } => {
metadata.add_selected_nodes(nodes);
responses.add(BroadcastEvent::SelectionChanged);
@ -768,13 +756,9 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
NodeGraphMessage::SendGraph { should_rerender } => {
if let Some(network) = document_network.nested_network(&self.network) {
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
Self::send_graph(network, graph_view_overlay_open, responses);
if should_rerender {
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
responses.add(NodeGraphMessage::RunDocumentGraph);
}
responses.add(NodeGraphMessage::RunDocumentGraph);
}
}
}
@ -787,11 +771,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
responses.add(NodeGraphMessage::SetNodeInput { node_id, input_index, input });
responses.add(PropertiesPanelMessage::Refresh);
if (node.name != "Imaginate" || input_index == 0) && network.connected_to_output(node_id) {
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
responses.add(NodeGraphMessage::RunDocumentGraph);
}
responses.add(NodeGraphMessage::RunDocumentGraph);
}
}
}
@ -811,12 +791,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
}
}
NodeGraphMessage::SetQualifiedInputValue {
layer_path,
node_path,
input_index,
value,
} => {
NodeGraphMessage::SetQualifiedInputValue { node_path, input_index, value } => {
let Some((node_id, node_path)) = node_path.split_last() else {
error!("Node path is empty");
return;
@ -832,7 +807,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
}
node.inputs[input_index] = NodeInput::Value { tagged_value: value, exposed: false };
if network.connected_to_output(*node_id) {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
responses.add(NodeGraphMessage::RunDocumentGraph);
}
}
}
@ -908,7 +883,7 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
} else if !network.inputs.contains(&node_id) && !network.original_outputs().iter().any(|output| output.node_id == node_id) {
network.disabled.push(node_id);
}
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
Self::send_graph(network, graph_view_overlay_open, responses);
// Only generate node graph if one of the selected nodes is connected to the output
if network.connected_to_output(node_id) {
@ -944,21 +919,17 @@ impl<'a> MessageHandler<NodeGraphMessage, NodeGraphHandlerData<'a>> for NodeGrap
} else {
return;
}
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
Self::send_graph(network, graph_view_overlay_open, responses);
}
self.update_selection_action_buttons(document_network, metadata, responses);
if let Some(layer_path) = self.layer_path.clone() {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path });
} else {
responses.add(NodeGraphMessage::RunDocumentGraph);
}
responses.add(NodeGraphMessage::RunDocumentGraph);
}
NodeGraphMessage::UpdateNewNodeGraph => {
if let Some(network) = document_network.nested_network(&self.network) {
metadata.clear_selected_nodes();
responses.add(BroadcastEvent::SelectionChanged);
Self::send_graph(network, &self.layer_path, graph_view_overlay_open, responses);
Self::send_graph(network, graph_view_overlay_open, responses);
let node_types = document_node_types::collect_node_types();
responses.add(FrontendMessage::UpdateNodeTypes { node_types });

View file

@ -2,7 +2,6 @@ use super::{node_properties, FrontendGraphDataType, FrontendNodeType};
use crate::consts::{DEFAULT_FONT_FAMILY, DEFAULT_FONT_STYLE};
use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::utility_types::document_metadata::DocumentMetadata;
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::portfolio::utility_types::PersistentData;
use crate::messages::prelude::Message;
use crate::node_graph_executor::NodeGraphExecutor;
@ -74,7 +73,6 @@ impl DocumentOutputType {
pub struct NodePropertiesContext<'a> {
pub persistent_data: &'a PersistentData,
pub responses: &'a mut VecDeque<Message>,
pub layer_path: &'a [LayerId],
pub nested_path: &'a [NodeId],
pub executor: &'a mut NodeGraphExecutor,
pub network: &'a NodeNetwork,
@ -279,7 +277,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
name: "Image Frame",
data_type: FrontendGraphDataType::Raster,
}],
properties: node_properties::input_properties,
properties: node_properties::node_no_properties,
..Default::default()
},
DocumentNodeDefinition {
@ -1069,7 +1067,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
name: "OutputBuffer",
data_type: FrontendGraphDataType::General,
}],
properties: node_properties::input_properties,
properties: node_properties::node_no_properties,
..Default::default()
},
#[cfg(feature = "gpu")]
@ -1137,7 +1135,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
name: "CommandBuffer",
data_type: FrontendGraphDataType::General,
}],
properties: node_properties::input_properties,
properties: node_properties::node_no_properties,
..Default::default()
},
#[cfg(feature = "gpu")]
@ -1171,7 +1169,7 @@ fn static_nodes() -> Vec<DocumentNodeDefinition> {
name: "PipelineLayout",
data_type: FrontendGraphDataType::General,
}],
properties: node_properties::input_properties,
properties: node_properties::node_no_properties,
..Default::default()
},
#[cfg(feature = "gpu")]
@ -2377,7 +2375,7 @@ pub static IMAGINATE_NODE: Lazy<DocumentNodeDefinition> = Lazy::new(|| DocumentN
name: "Imaginate",
category: "Image Synthesis",
implementation: NodeImplementation::DocumentNode(NodeNetwork {
inputs: vec![0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
inputs: vec![0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
outputs: vec![NodeOutput::new(1, 0)],
nodes: [
(
@ -2404,14 +2402,13 @@ pub static IMAGINATE_NODE: Lazy<DocumentNodeDefinition> = Lazy::new(|| DocumentN
NodeInput::Network(concrete!(String)),
NodeInput::Network(concrete!(bool)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(Option<Vec<u64>>)),
NodeInput::Network(concrete!(bool)),
NodeInput::Network(concrete!(f32)),
NodeInput::Network(concrete!(ImaginateMaskStartingFill)),
NodeInput::Network(concrete!(bool)),
NodeInput::Network(concrete!(bool)),
],
implementation: DocumentNodeImplementation::proto("graphene_std::raster::ImaginateNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>"),
implementation: DocumentNodeImplementation::proto("graphene_std::raster::ImaginateNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>"),
..Default::default()
},
),
@ -2436,7 +2433,6 @@ pub static IMAGINATE_NODE: Lazy<DocumentNodeDefinition> = Lazy::new(|| DocumentN
DocumentInputType::value("Negative Prompt", TaggedValue::String(String::new()), false),
DocumentInputType::value("Adapt Input Image", TaggedValue::Bool(false), false),
DocumentInputType::value("Image Creativity", TaggedValue::F32(66.), false),
DocumentInputType::value("Masking Layer", TaggedValue::LayerPath(None), false),
DocumentInputType::value("Inpaint", TaggedValue::Bool(true), false),
DocumentInputType::value("Mask Blur", TaggedValue::F32(4.), false),
DocumentInputType::value("Mask Starting Fill", TaggedValue::ImaginateMaskStartingFill(ImaginateMaskStartingFill::Fill), false),

View file

@ -144,16 +144,16 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
NumberInput::new(Some(dvec2.x))
.label(x)
.unit(unit)
.min(min.unwrap_or(-((1u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(input.value.unwrap(), dvec2.y)), node_id, index))
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
NumberInput::new(Some(dvec2.y))
.label(y)
.unit(unit)
.min(min.unwrap_or(-((1u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(move |input: &NumberInput| TaggedValue::DVec2(DVec2::new(dvec2.x, input.value.unwrap())), node_id, index))
.widget_holder(),
]);
@ -170,8 +170,8 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
.int()
.label(x)
.unit(unit)
.min(min.unwrap_or(-((1u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(update_x, node_id, index))
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
@ -179,8 +179,8 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
.int()
.label(y)
.unit(unit)
.min(min.unwrap_or(-((1u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.min(min.unwrap_or(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64)))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(update_y, node_id, index))
.widget_holder(),
]);
@ -198,7 +198,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
.label(x)
.unit(unit)
.min(min.unwrap_or(0.))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(update_x, node_id, index))
.widget_holder(),
Separator::new(SeparatorType::Related).widget_holder(),
@ -207,7 +207,7 @@ fn vec2_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name
.label(y)
.unit(unit)
.min(min.unwrap_or(0.))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(update_y, node_id, index))
.widget_holder(),
]);
@ -359,7 +359,7 @@ fn number_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, na
}
//TODO Generalize this instead of using a separate function per dropdown menu enum
fn color_channel(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
fn color_channel(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::RedGreenBlue(mode),
@ -382,7 +382,7 @@ fn color_channel(document_node: &DocumentNode, node_id: u64, index: usize, name:
}
// TODO Generalize this instead of using a separate function per dropdown menu enum
fn noise_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
fn noise_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::NoiseType(noise_type),
@ -403,7 +403,7 @@ fn noise_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &s
}
// TODO Generalize this instead of using a separate function per dropdown menu enum
fn fractal_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
fn fractal_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::FractalType(fractal_type),
@ -424,7 +424,7 @@ fn fractal_type(document_node: &DocumentNode, node_id: u64, index: usize, name:
}
// TODO Generalize this instead of using a separate function per dropdown menu enum
fn cellular_distance_function(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
fn cellular_distance_function(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::CellularDistanceFunction(cellular_distance_function),
@ -450,7 +450,7 @@ fn cellular_distance_function(document_node: &DocumentNode, node_id: u64, index:
}
// TODO Generalize this instead of using a separate function per dropdown menu enum
fn cellular_return_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
fn cellular_return_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::CellularReturnType(cellular_return_type),
@ -471,7 +471,7 @@ fn cellular_return_type(document_node: &DocumentNode, node_id: u64, index: usize
}
// TODO Generalize this instead of using a separate function per dropdown menu enum
fn domain_warp_type(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
fn domain_warp_type(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool, disabled: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::DomainWarpType(domain_warp_type),
@ -492,7 +492,7 @@ fn domain_warp_type(document_node: &DocumentNode, node_id: u64, index: usize, na
}
// TODO: Generalize this instead of using a separate function per dropdown menu enum
fn blend_mode(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
fn blend_mode(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::BlendMode(blend_mode),
@ -520,7 +520,7 @@ fn blend_mode(document_node: &DocumentNode, node_id: u64, index: usize, name: &s
}
// TODO: Generalize this for all dropdowns (also see blend_mode and channel_extration)
fn luminance_calculation(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
fn luminance_calculation(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::LuminanceCalculation(calculation),
@ -542,7 +542,7 @@ fn luminance_calculation(document_node: &DocumentNode, node_id: u64, index: usiz
LayoutGroup::Row { widgets }.with_tooltip("Formula used to calculate the luminance of a pixel")
}
fn line_cap_widget(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
fn line_cap_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::LineCap(line_cap),
@ -562,7 +562,7 @@ fn line_cap_widget(document_node: &DocumentNode, node_id: u64, index: usize, nam
LayoutGroup::Row { widgets }
}
fn line_join_widget(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
fn line_join_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let &NodeInput::Value {
tagged_value: TaggedValue::LineJoin(line_join),
@ -582,7 +582,7 @@ fn line_join_widget(document_node: &DocumentNode, node_id: u64, index: usize, na
LayoutGroup::Row { widgets }
}
fn fill_type_widget(document_node: &DocumentNode, node_id: u64, index: usize) -> LayoutGroup {
fn fill_type_widget(document_node: &DocumentNode, node_id: NodeId, index: usize) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, "Fill Type", FrontendGraphDataType::General, true);
if let &NodeInput::Value {
tagged_value: TaggedValue::FillType(fill_type),
@ -607,7 +607,7 @@ fn fill_type_widget(document_node: &DocumentNode, node_id: u64, index: usize) ->
LayoutGroup::Row { widgets }
}
fn gradient_type_widget(document_node: &DocumentNode, node_id: u64, index: usize) -> LayoutGroup {
fn gradient_type_widget(document_node: &DocumentNode, node_id: NodeId, index: usize) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, "Gradient Type", FrontendGraphDataType::General, true);
if let &NodeInput::Value {
tagged_value: TaggedValue::GradientType(gradient_type),
@ -699,7 +699,7 @@ fn gradient_row(row: &mut Vec<WidgetHolder>, positions: &Vec<(f64, Option<Color>
}
}
fn gradient_positions(rows: &mut Vec<LayoutGroup>, document_node: &DocumentNode, name: &str, node_id: u64, input_index: usize) {
fn gradient_positions(rows: &mut Vec<LayoutGroup>, document_node: &DocumentNode, name: &str, node_id: NodeId, input_index: usize) {
let mut widgets = vec![expose_widget(node_id, input_index, FrontendGraphDataType::General, document_node.inputs[input_index].is_exposed())];
widgets.push(Separator::new(SeparatorType::Unrelated).widget_holder());
if let NodeInput::Value {
@ -742,7 +742,7 @@ fn gradient_positions(rows: &mut Vec<LayoutGroup>, document_node: &DocumentNode,
}
}
fn color_widget(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, color_props: ColorButton, blank_assist: bool) -> LayoutGroup {
fn color_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, color_props: ColorButton, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::Number, blank_assist);
if let NodeInput::Value { tagged_value, exposed: false } = &document_node.inputs[index] {
@ -767,7 +767,7 @@ fn color_widget(document_node: &DocumentNode, node_id: u64, index: usize, name:
LayoutGroup::Row { widgets }
}
fn curves_widget(document_node: &DocumentNode, node_id: u64, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
fn curves_widget(document_node: &DocumentNode, node_id: NodeId, index: usize, name: &str, blank_assist: bool) -> LayoutGroup {
let mut widgets = start_widgets(document_node, node_id, index, name, FrontendGraphDataType::General, blank_assist);
if let NodeInput::Value {
@ -785,17 +785,6 @@ fn curves_widget(document_node: &DocumentNode, node_id: u64, index: usize, name:
LayoutGroup::Row { widgets }
}
/// Properties for the input node, with information describing how frames work and a refresh button
pub fn input_properties(_document_node: &DocumentNode, _node_id: NodeId, context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let information = TextLabel::new("The graph's input frame is the rasterized artwork under the layer").widget_holder();
let layer_path = context.layer_path.to_vec();
let refresh_button = TextButton::new("Refresh Input")
.tooltip("Refresh the artwork under the layer")
.on_update(move |_| DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path: layer_path.clone() }.into())
.widget_holder();
vec![LayoutGroup::Row { widgets: vec![information] }, LayoutGroup::Row { widgets: vec![refresh_button] }]
}
pub fn levels_properties(document_node: &DocumentNode, node_id: NodeId, _context: &mut NodePropertiesContext) -> Vec<LayoutGroup> {
let input_shadows = number_widget(document_node, node_id, 1, "Shadows", NumberInput::default().min(0.).max(100.).unit("%"), true);
let input_midtones = number_widget(document_node, node_id, 2, "Midtones", NumberInput::default().min(0.).max(100.).unit("%"), true);
@ -1629,12 +1618,10 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
.tooltip("Generate with a new random seed")
.on_update({
let imaginate_node = imaginate_node.clone();
let layer_path = context.layer_path.to_vec();
let controller = controller.clone();
move |_| {
controller.trigger_regenerate();
DocumentMessage::ImaginateRandom {
layer_path: layer_path.clone(),
imaginate_node: imaginate_node.clone(),
then_generate: true,
}
@ -1646,11 +1633,10 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
TextButton::new("Generate")
.tooltip("Fill layer frame by generating a new image")
.on_update({
let layer_path = context.layer_path.to_vec();
let controller = controller.clone();
move |_| {
controller.trigger_regenerate();
DocumentMessage::ImaginateGenerate { layer_path: layer_path.clone() }.into()
DocumentMessage::ImaginateGenerate.into()
}
})
.widget_holder(),
@ -1659,11 +1645,10 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
.tooltip("Remove generated image from the layer frame")
.disabled(!matches!(imaginate_status, ImaginateStatus::ReadyDone))
.on_update({
let layer_path = context.layer_path.to_vec();
let controller = controller.clone();
move |_| {
controller.set_status(ImaginateStatus::Ready);
DocumentMessage::ImaginateClear { layer_path: layer_path.clone() }.into()
DocumentMessage::ImaginateGenerate.into()
}
})
.widget_holder(),
@ -1687,10 +1672,8 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
.tooltip("Set a new random seed")
.on_update({
let imaginate_node = imaginate_node.clone();
let layer_path = context.layer_path.to_vec();
move |_| {
DocumentMessage::ImaginateRandom {
layer_path: layer_path.clone(),
imaginate_node: imaginate_node.clone(),
then_generate: false,
}
@ -1701,8 +1684,8 @@ pub fn imaginate_properties(document_node: &DocumentNode, node_id: NodeId, conte
Separator::new(SeparatorType::Unrelated).widget_holder(),
NumberInput::new(Some(seed))
.int()
.min(-((1u64 << f64::MANTISSA_DIGITS) as f64))
.max((1u64 << f64::MANTISSA_DIGITS) as f64)
.min(-((1_u64 << f64::MANTISSA_DIGITS) as f64))
.max((1_u64 << f64::MANTISSA_DIGITS) as f64)
.on_update(update_value(move |input: &NumberInput| TaggedValue::F64(input.value.unwrap()), node_id, seed_index))
.mode(NumberInputMode::Increment)
.widget_holder(),

View file

@ -37,7 +37,6 @@ impl<'a> MessageHandler<PropertiesPanelMessage, (&PersistentData, PropertiesPane
persistent_data,
responses,
nested_path: &node_graph_message_handler.network,
layer_path: &[],
executor,
network: document_network,
metadata: document_metadata,

View file

@ -107,8 +107,9 @@ impl DocumentMetadata {
layer_path
})
.collect::<Vec<_>>();
sorted_layers.sort();
// Sorting here creates groups of similar UUID paths
sorted_layers.sort();
sorted_layers.dedup_by(|a, b| a.starts_with(b));
sorted_layers
}
@ -386,32 +387,27 @@ impl LayerNodeIdentifier {
u64::from(self.0) - 1
}
/// Convert layer to layer path
pub fn to_path(self) -> Vec<NodeId> {
vec![self.to_node()]
}
/// Access the parent layer if possible
pub fn parent(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
document_metadata.get_relations(self).and_then(|relations| relations.parent)
}
/// Access the previous sibling of this layer (up the layer tree)
/// Access the previous sibling of this layer (up the Layers panel)
pub fn previous_sibling(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
document_metadata.get_relations(self).and_then(|relations| relations.previous_sibling)
}
/// Access the next sibling of this layer (down the layer tree)
/// Access the next sibling of this layer (down the Layers panel)
pub fn next_sibling(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
document_metadata.get_relations(self).and_then(|relations| relations.next_sibling)
}
/// Access the first child of this layer (top most in layer tree)
/// Access the first child of this layer (top most in Layers panel)
pub fn first_child(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
document_metadata.get_relations(self).and_then(|relations| relations.first_child)
}
/// Access the last child of this layer (bottom most in layer tree)
/// Access the last child of this layer (bottom most in Layers panel)
pub fn last_child(self, document_metadata: &DocumentMetadata) -> Option<LayerNodeIdentifier> {
document_metadata.get_relations(self).and_then(|relations| relations.last_child)
}
@ -457,7 +453,7 @@ impl LayerNodeIdentifier {
}
}
/// Add a child towards the top of the layer tree
/// Add a child towards the top of the Layers panel
pub fn push_front_child(self, document_metadata: &mut DocumentMetadata, new: LayerNodeIdentifier) {
assert!(!document_metadata.structure.contains_key(&new), "Cannot add already existing layer");
let parent = document_metadata.get_structure_mut(self);
@ -470,7 +466,7 @@ impl LayerNodeIdentifier {
document_metadata.get_structure_mut(new).parent = Some(self);
}
/// Add a child towards the bottom of the layer tree
/// Add a child towards the bottom of the Layers panel
pub fn push_child(self, document_metadata: &mut DocumentMetadata, new: LayerNodeIdentifier) {
assert!(!document_metadata.structure.contains_key(&new), "Cannot add already existing layer");
let parent = document_metadata.get_structure_mut(self);
@ -483,7 +479,7 @@ impl LayerNodeIdentifier {
document_metadata.get_structure_mut(new).parent = Some(self);
}
/// Add sibling above in the layer tree
/// Add sibling above in the Layers panel
pub fn add_before(self, document_metadata: &mut DocumentMetadata, new: LayerNodeIdentifier) {
assert!(!document_metadata.structure.contains_key(&new), "Cannot add already existing layer");
document_metadata.get_structure_mut(new).next_sibling = Some(self);
@ -501,7 +497,7 @@ impl LayerNodeIdentifier {
}
}
/// Add sibling below in the layer tree
/// Add sibling below in the Layers panel
pub fn add_after(self, document_metadata: &mut DocumentMetadata, new: LayerNodeIdentifier) {
assert!(!document_metadata.structure.contains_key(&new), "Cannot add already existing layer");
document_metadata.get_structure_mut(new).previous_sibling = Some(self);

View file

@ -1,4 +1,4 @@
use crate::messages::portfolio::document::utility_types::LayerId;
use graph_craft::document::NodeId;
use serde::ser::SerializeStruct;
use serde::{Deserialize, Serialize};
@ -45,6 +45,9 @@ pub struct LayerPanelEntry {
pub layer_classification: LayerClassification,
pub selected: bool,
pub expanded: bool,
pub path: Vec<LayerId>,
#[serde(rename = "parentId")]
pub parent_id: Option<NodeId>,
pub id: NodeId,
pub depth: usize,
pub thumbnail: String,
}

View file

@ -1,7 +1,4 @@
pub use super::layer_panel::LayerPanelEntry;
use crate::messages::portfolio::document::utility_types::LayerId;
use graphene_core::raster::color::Color;
use glam::DVec2;
use serde::{Deserialize, Serialize};
@ -54,12 +51,6 @@ impl DocumentMode {
}
}
pub enum DocumentRenderMode<'a> {
Root,
OnlyBelowLayerInFolder(&'a [LayerId]),
LayerCutout(&'a [LayerId], Color),
}
#[derive(Clone, Debug)]
/// SnappingState determines the current individual snapping states
pub struct SnappingState {

View file

@ -4,8 +4,3 @@ pub mod error;
pub mod layer_panel;
pub mod misc;
pub mod transformation;
// TODO: Remove this entirely
/// A number that identifies a layer.
/// This does not technically need to be unique globally, only within a folder.
pub type LayerId = u64;

View file

@ -376,7 +376,7 @@ impl<'a> Selected<'a> {
let to = document_metadata.downstream_transform_to_viewport(layer);
let new = to.inverse() * transformation * to * original_transform;
responses.add(GraphOperationMessage::TransformSet {
layer: layer.to_path(),
layer,
transform: new,
transform_in: TransformIn::Local,
skip_rerender: false,
@ -404,7 +404,7 @@ impl<'a> Selected<'a> {
let position = new_pos_viewport;
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
}
@ -438,17 +438,17 @@ impl<'a> Selected<'a> {
OriginalTransforms::Layer(hash) => {
let Some(matrix) = hash.get(&layer) else { continue };
self.responses.add(GraphOperationMessage::TransformSet {
layer: layer.to_path(),
layer,
transform: *matrix,
transform_in: TransformIn::Local,
skip_rerender: false,
});
}
OriginalTransforms::Path(path) => {
for (layer, points) in path {
for (&layer, points) in path {
for &(point, position) in points {
self.responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
}

View file

@ -1,8 +1,8 @@
use super::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::frontend::utility_types::{ExportBounds, FileType};
use crate::messages::portfolio::document::utility_types::clipboards::Clipboard;
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use graphene_core::text::Font;
use serde::{Deserialize, Serialize};
@ -22,21 +22,21 @@ pub enum PortfolioMessage {
// Messages
#[remain::unsorted]
DocumentPassMessage {
document_id: u64,
document_id: DocumentId,
message: DocumentMessage,
},
AutoSaveActiveDocument,
AutoSaveDocument {
document_id: u64,
document_id: DocumentId,
},
CloseActiveDocumentWithConfirmation,
CloseAllDocuments,
CloseAllDocumentsWithConfirmation,
CloseDocument {
document_id: u64,
document_id: DocumentId,
},
CloseDocumentWithConfirmation {
document_id: u64,
document_id: DocumentId,
},
Copy {
clipboard: Clipboard,
@ -45,7 +45,7 @@ pub enum PortfolioMessage {
clipboard: Clipboard,
},
DeleteDocument {
document_id: u64,
document_id: DocumentId,
},
DestroyAllDocuments,
FontLoaded {
@ -65,7 +65,7 @@ pub enum PortfolioMessage {
ImaginateServerHostname,
Import,
LoadDocumentResources {
document_id: u64,
document_id: DocumentId,
},
LoadFont {
font: Font,
@ -81,7 +81,7 @@ pub enum PortfolioMessage {
document_serialized_content: String,
},
OpenDocumentFileWithId {
document_id: u64,
document_id: DocumentId,
document_name: String,
document_is_auto_saved: bool,
document_is_saved: bool,
@ -97,10 +97,10 @@ pub enum PortfolioMessage {
},
PrevDocument,
SelectDocument {
document_id: u64,
document_id: DocumentId,
},
SetActiveDocument {
document_id: u64,
document_id: DocumentId,
},
SubmitDocumentExport {
file_name: String,
@ -110,8 +110,7 @@ pub enum PortfolioMessage {
transparent_background: bool,
},
SubmitGraphRender {
document_id: u64,
layer_path: Vec<LayerId>,
document_id: DocumentId,
},
ToggleRulers,
UpdateDocumentWidgets,

View file

@ -19,9 +19,9 @@ use std::sync::Arc;
#[derive(Debug, Default)]
pub struct PortfolioMessageHandler {
menu_bar_message_handler: MenuBarMessageHandler,
documents: HashMap<u64, DocumentMessageHandler>,
document_ids: Vec<u64>,
active_document_id: Option<u64>,
documents: HashMap<DocumentId, DocumentMessageHandler>,
document_ids: Vec<DocumentId>,
active_document_id: Option<DocumentId>,
graph_view_overlay_open: bool,
copy_buffer: [Vec<CopyBufferEntry>; INTERNAL_CLIPBOARD_COUNT as usize],
pub persistent_data: PersistentData,
@ -109,7 +109,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
// Clear relevant UI layouts if there are no documents
responses.add(PropertiesPanelMessage::Clear);
responses.add(DocumentMessage::ClearLayerTree);
responses.add(DocumentMessage::ClearLayersPanel);
let hint_data = HintData(vec![HintGroup(vec![])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
}
@ -133,7 +133,7 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
if self.documents.len() == 1 && self.document_ids[0] == document_id {
// Clear UI layouts that assume the existence of a document
responses.add(PropertiesPanelMessage::Clear);
responses.add(DocumentMessage::ClearLayerTree);
responses.add(DocumentMessage::ClearLayersPanel);
let hint_data = HintData(vec![HintGroup(vec![])]);
responses.add(FrontendMessage::UpdateInputHints { hint_data });
}
@ -506,10 +506,9 @@ impl MessageHandler<PortfolioMessage, (&InputPreprocessorMessageHandler, &Prefer
});
}
}
PortfolioMessage::SubmitGraphRender { document_id, layer_path } => {
PortfolioMessage::SubmitGraphRender { document_id } => {
let result = self.executor.submit_node_graph_evaluation(
self.documents.get_mut(&document_id).expect("Tried to render no existent Document"),
layer_path,
ipp.viewport_bounds.size().as_uvec2(),
);
@ -591,11 +590,11 @@ impl PortfolioMessageHandler {
self.executor.introspect_node(node_path)
}
pub fn document(&self, document_id: u64) -> Option<&DocumentMessageHandler> {
pub fn document(&self, document_id: DocumentId) -> Option<&DocumentMessageHandler> {
self.documents.get(&document_id)
}
pub fn document_mut(&mut self, document_id: u64) -> Option<&mut DocumentMessageHandler> {
pub fn document_mut(&mut self, document_id: DocumentId) -> Option<&mut DocumentMessageHandler> {
self.documents.get_mut(&document_id)
}
@ -607,7 +606,7 @@ impl PortfolioMessageHandler {
self.active_document_id.and_then(|id| self.documents.get_mut(&id))
}
pub fn active_document_id(&self) -> Option<u64> {
pub fn active_document_id(&self) -> Option<DocumentId> {
self.active_document_id
}
@ -637,7 +636,7 @@ impl PortfolioMessageHandler {
}
// TODO: Fix how this doesn't preserve tab order upon loading new document from *File > Load*
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: u64, responses: &mut VecDeque<Message>) {
fn load_document(&mut self, new_document: DocumentMessageHandler, document_id: DocumentId, responses: &mut VecDeque<Message>) {
self.document_ids.push(document_id);
new_document.update_layers_panel_options_bar_widgets(responses);
@ -667,7 +666,7 @@ impl PortfolioMessageHandler {
self.document_ids.iter().map(|id| self.documents.get(id).expect("document id was not found in the document hashmap"))
}
fn document_index(&self, document_id: u64) -> usize {
fn document_index(&self, document_id: DocumentId) -> usize {
self.document_ids.iter().position(|id| id == &document_id).expect("Active document is missing from document ids")
}

View file

@ -56,6 +56,9 @@ pub use graphite_proc_macros::*;
pub use std::collections::{HashMap, HashSet, VecDeque};
// TODO: Convert from a type alias to a newtype
pub type DocumentId = u64;
pub trait Responses {
fn add(&mut self, message: impl Into<Message>);

View file

@ -38,7 +38,7 @@ pub fn new_image_layer(image_frame: ImageFrame<Color>, id: NodeId, parent: Layer
pub fn set_manipulator_mirror_angle(manipulator_groups: &[ManipulatorGroup<ManipulatorGroupId>], layer: LayerNodeIdentifier, mirror_angle: bool, responses: &mut VecDeque<Message>) {
for manipulator_group in manipulator_groups {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring {
id: manipulator_group.id,
mirror_angle,
@ -216,7 +216,7 @@ impl<'a> NodeGraphLayer<'a> {
}
/// Return an iterator up the primary flow of the layer
pub fn primary_layer_flow(&self) -> impl Iterator<Item = (&'a DocumentNode, u64)> {
pub fn primary_layer_flow(&self) -> impl Iterator<Item = (&'a DocumentNode, NodeId)> {
self.node_graph.upstream_flow_back_from_nodes(vec![self.layer_node], true)
}

View file

@ -105,7 +105,6 @@ impl Pivot {
let pivot = transform.inverse().transform_point2(position);
// Only update the pivot when computed position is finite. Infinite can happen when scale is 0.
if pivot.is_finite() {
let layer = layer.to_path();
responses.add(GraphOperationMessage::TransformSetPivot { layer, pivot });
}
}

View file

@ -17,7 +17,6 @@ impl Resize {
/// Starts a resize, assigning the snap targets and snapping the starting position.
pub fn start(&mut self, responses: &mut VecDeque<Message>, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let root_transform = document.metadata().document_to_viewport;
self.drag_start = root_transform.inverse().transform_point2(self.snap_manager.snap_position(responses, document, input.mouse.position));
}
@ -25,7 +24,6 @@ impl Resize {
/// Recalculates snap targets without snapping the starting position.
pub fn recalculate_snaps(&mut self, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
self.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
}
/// Calculate the drag start position in viewport space.
@ -65,7 +63,7 @@ impl Resize {
Some(
GraphOperationMessage::TransformSet {
layer: layer.to_path(),
layer,
transform: DAffine2::from_scale_angle_translation(size, 0., start),
transform_in: TransformIn::Viewport,
skip_rerender,

View file

@ -192,7 +192,7 @@ impl ShapeState {
if point.manipulator_type.is_handle() {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring { id: group.id, mirror_angle: false },
});
}
@ -202,7 +202,7 @@ impl ShapeState {
return;
};
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position: (position + delta) },
});
};
@ -270,7 +270,7 @@ impl ShapeState {
// Mirror the angle but not the distance
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring {
id: manipulator.id,
mirror_angle: true,
@ -291,7 +291,7 @@ impl ShapeState {
if let Some(in_handle) = length_previous.map(|length| anchor_position + handle_vector * length) {
let point = ManipulatorPointId::new(manipulator.id, SelectedType::InHandle);
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position: in_handle },
});
}
@ -299,7 +299,7 @@ impl ShapeState {
if let Some(out_handle) = length_next.map(|length| anchor_position - handle_vector * length) {
let point = ManipulatorPointId::new(manipulator.id, SelectedType::OutHandle);
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position: out_handle },
});
}
@ -338,7 +338,7 @@ impl ShapeState {
let out_handle = ManipulatorPointId::new(point.group, SelectedType::OutHandle);
if let Some(position) = group.out_handle {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point: out_handle, position },
});
}
@ -347,7 +347,7 @@ impl ShapeState {
let in_handle = ManipulatorPointId::new(point.group, SelectedType::InHandle);
if let Some(position) = group.in_handle {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point: in_handle, position },
});
}
@ -391,7 +391,7 @@ impl ShapeState {
let Some(previous_position) = point.manipulator_type.get_position(group) else { return };
let position = previous_position + delta;
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
};
@ -410,7 +410,7 @@ impl ShapeState {
// and set angle mirroring to true.
if !mirror && point.manipulator_type.opposite().get_position(group).is_none() {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring { id: group.id, mirror_angle: true },
});
mirror = true;
@ -428,7 +428,7 @@ impl ShapeState {
}
let position = group.anchor - (original_handle_position - group.anchor);
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
}
@ -471,7 +471,7 @@ impl ShapeState {
if (anchor_position - point_position).length() < DRAG_THRESHOLD {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::RemoveManipulatorPoint { point },
});
@ -481,7 +481,7 @@ impl ShapeState {
if let Some(lengths) = opposing_handle_lengths {
if lengths.contains_key(&point.group) {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::RemoveManipulatorPoint { point: opposite_point },
});
}
@ -567,7 +567,7 @@ impl ShapeState {
let Some(opposing_handle_length) = opposing_handle_length else {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::RemoveManipulatorPoint {
point: ManipulatorPointId::new(manipulator_group.id, single_selected_handle.opposite()),
},
@ -586,7 +586,7 @@ impl ShapeState {
assert!(position.is_finite(), "Opposing handle not finite!");
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
}
@ -596,10 +596,10 @@ impl ShapeState {
/// Dissolve the selected points.
pub fn delete_selected_points(&self, responses: &mut VecDeque<Message>) {
for (layer, state) in &self.selected_shape_state {
for (&layer, state) in &self.selected_shape_state {
for &point in &state.selected_points {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::RemoveManipulatorPoint { point },
})
}
@ -608,10 +608,10 @@ impl ShapeState {
/// Toggle if the handles should mirror angle across the anchor position.
pub fn toggle_handle_mirroring_on_selected(&self, responses: &mut VecDeque<Message>) {
for (layer, state) in &self.selected_shape_state {
for (&layer, state) in &self.selected_shape_state {
for point in &state.selected_points {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::ToggleManipulatorHandleMirroring { id: point.group },
})
}
@ -620,10 +620,10 @@ impl ShapeState {
/// Toggle if the handles should mirror angle across the anchor position.
pub fn set_handle_mirroring_on_selected(&self, mirror_angle: bool, responses: &mut VecDeque<Message>) {
for (layer, state) in &self.selected_shape_state {
for (&layer, state) in &self.selected_shape_state {
for point in &state.selected_points {
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring { id: point.group, mirror_angle },
});
}
@ -732,7 +732,7 @@ impl ShapeState {
let point = ManipulatorPointId::new(start, SelectedType::OutHandle);
let position = first.handle_start().unwrap_or(first.start());
let out_handle = GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position },
};
responses.add(out_handle);
@ -740,7 +740,7 @@ impl ShapeState {
// Insert a new manipulator group between the existing ones
let manipulator_group = ManipulatorGroup::new(first.end(), first.handle_end(), second.handle_start());
let insert = GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::AddManipulatorGroup { manipulator_group, after_id: start },
};
responses.add(insert);
@ -749,7 +749,7 @@ impl ShapeState {
let point = ManipulatorPointId::new(end, SelectedType::InHandle);
let position = second.handle_end().unwrap_or(second.end());
let in_handle = GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position },
};
responses.add(in_handle);
@ -797,16 +797,16 @@ impl ShapeState {
} else {
let point = ManipulatorPointId::new(manipulator.id, SelectedType::InHandle);
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position: anchor_position },
});
let point = ManipulatorPointId::new(manipulator.id, SelectedType::OutHandle);
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position: anchor_position },
});
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring {
id: manipulator.id,
mirror_angle: false,

View file

@ -1,6 +1,4 @@
use super::shape_editor::ManipulatorPointInfo;
use crate::consts::{SNAP_AXIS_TOLERANCE, SNAP_POINT_TOLERANCE};
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use glam::DVec2;
@ -100,23 +98,6 @@ impl SnapManager {
}
}
/// Adds all of the shape handles in the document, including bézier handles of the points specified
pub fn add_all_document_handles(
&mut self,
_document_message_handler: &DocumentMessageHandler,
_input: &InputPreprocessorMessageHandler,
_include_handles: &[&[LayerId]],
_exclude: &[&[LayerId]],
_ignore_points: &[ManipulatorPointInfo],
) {
// for path in document_message_handler.all_layers() {
// if !exclude.contains(&path) {
// let layer = document_message_handler.document_legacy.layer(path).expect("Could not get layer for snapping");
// self.add_snap_path(document_message_handler, input, layer, path, include_handles.contains(&path), ignore_points);
// }
// }
}
/// Finds the closest snap from an array of layers to the specified snap targets in viewport coords.
/// Returns 0 for each axis that there is no snap less than the snap tolerance.
pub fn snap_layers(&mut self, responses: &mut VecDeque<Message>, document_message_handler: &DocumentMessageHandler, snap_anchors: Vec<DVec2>, mouse_delta: DVec2) -> DVec2 {

View file

@ -16,13 +16,13 @@ pub struct ToolMessageHandler {
pub shape_editor: ShapeState,
}
impl MessageHandler<ToolMessage, (&DocumentMessageHandler, u64, &InputPreprocessorMessageHandler, &PersistentData, &NodeGraphExecutor)> for ToolMessageHandler {
impl MessageHandler<ToolMessage, (&DocumentMessageHandler, DocumentId, &InputPreprocessorMessageHandler, &PersistentData, &NodeGraphExecutor)> for ToolMessageHandler {
#[remain::check]
fn process_message(
&mut self,
message: ToolMessage,
responses: &mut VecDeque<Message>,
(document, document_id, input, persistent_data, node_graph): (&DocumentMessageHandler, u64, &InputPreprocessorMessageHandler, &PersistentData, &NodeGraphExecutor),
(document, document_id, input, persistent_data, node_graph): (&DocumentMessageHandler, DocumentId, &InputPreprocessorMessageHandler, &PersistentData, &NodeGraphExecutor),
) {
let font_cache = &persistent_data.font_cache;

View file

@ -120,7 +120,6 @@ impl ArtboardToolData {
let snap_y = selected_edges.0 || selected_edges.1;
self.snap_manager.start_snap(document, input, document.bounding_boxes(), snap_x, snap_y);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
if let Some(bounds) = &mut self.bounding_box_manager {
bounds.center_of_transformation = (bounds.bounds[0] + bounds.bounds[1]) / 2.;
@ -139,7 +138,6 @@ impl ArtboardToolData {
self.selected_artboard = Some(intersection);
self.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
self.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
true
} else {
@ -279,7 +277,6 @@ impl Fsm for ArtboardToolFsmState {
tool_data.selected_artboard = Some(LayerNodeIdentifier::new_unchecked(id));
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
responses.add(GraphOperationMessage::NewArtboard {
id,

View file

@ -301,7 +301,7 @@ impl BrushToolData {
fn update_strokes(&self, responses: &mut VecDeque<Message>) {
let Some(layer) = self.layer else { return };
let strokes = self.strokes.clone();
responses.add(GraphOperationMessage::Brush { layer: layer.to_path(), strokes });
responses.add(GraphOperationMessage::Brush { layer, strokes });
}
}

View file

@ -80,7 +80,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
.unit(" px")
.label("Weight")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| EllipseToolMessage::UpdateOptions(EllipseOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder()
}
@ -211,12 +211,12 @@ impl Fsm for EllipseToolFsmState {
let fill_color = tool_options.fill.active_color();
responses.add(GraphOperationMessage::FillSet {
layer: layer.to_path(),
layer,
fill: if let Some(color) = fill_color { Fill::Solid(color) } else { Fill::None },
});
responses.add(GraphOperationMessage::StrokeSet {
layer: layer.to_path(),
layer,
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
});

View file

@ -72,8 +72,6 @@ impl Fsm for FillToolFsmState {
let Some(layer_identifier) = document.click(input.mouse.position, &document.network) else {
return self;
};
let layer = layer_identifier.to_path();
let color = match event {
FillToolMessage::LeftPointerDown => global_tool_data.primary_color,
FillToolMessage::RightPointerDown => global_tool_data.secondary_color,
@ -81,7 +79,7 @@ impl Fsm for FillToolFsmState {
let fill = Fill::Solid(color);
responses.add(DocumentMessage::StartTransaction);
responses.add(GraphOperationMessage::FillSet { layer, fill });
responses.add(GraphOperationMessage::FillSet { layer: layer_identifier, fill });
responses.add(DocumentMessage::CommitTransaction);
FillToolFsmState::Ready

View file

@ -87,7 +87,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
.unit(" px")
.label("Weight")
.min(1.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| FreehandToolMessage::UpdateOptions(FreehandOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder()
}
@ -215,12 +215,12 @@ impl Fsm for FreehandToolFsmState {
tool_data.layer = Some(layer);
responses.add(GraphOperationMessage::FillSet {
layer: layer.to_path(),
layer,
fill: if let Some(color) = tool_options.fill.active_color() { Fill::Solid(color) } else { Fill::None },
});
responses.add(GraphOperationMessage::StrokeSet {
layer: layer.to_path(),
layer,
stroke: Stroke::new(tool_options.stroke.active_color(), tool_data.weight),
});
@ -234,7 +234,7 @@ impl Fsm for FreehandToolFsmState {
if tool_data.last_point != pos {
let manipulator_group = ManipulatorGroup::new_anchor(pos);
let modification = VectorDataModification::AddEndManipulatorGroup { subpath_index: 0, manipulator_group };
responses.add(GraphOperationMessage::Vector { layer: layer.to_path(), modification });
responses.add(GraphOperationMessage::Vector { layer, modification });
tool_data.dragged = true;
tool_data.last_point = pos;
}

View file

@ -1,5 +1,4 @@
use super::tool_prelude::*;
use crate::application::generate_uuid;
use crate::consts::{LINE_ROTATE_SNAP_ANGLE, MANIPULATOR_GROUP_MARKER_SIZE, SELECTION_THRESHOLD};
use crate::messages::portfolio::document::overlays::utility_types::OverlayContext;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
@ -235,9 +234,10 @@ impl SelectedGradient {
/// Update the layer fill to the current gradient
pub fn render_gradient(&mut self, responses: &mut VecDeque<Message>) {
self.gradient.transform = self.transform;
let fill = Fill::Gradient(self.gradient.clone());
let layer = self.layer.to_path();
responses.add(GraphOperationMessage::FillSet { layer, fill });
responses.add(GraphOperationMessage::FillSet {
layer: self.layer,
fill: Fill::Gradient(self.gradient.clone()),
});
}
}
@ -269,7 +269,6 @@ struct GradientToolData {
pub fn start_snap(snap_manager: &mut SnapManager, document: &DocumentMessageHandler, input: &InputPreprocessorMessageHandler) {
snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
}
impl Fsm for GradientToolFsmState {
@ -338,9 +337,10 @@ impl Fsm for GradientToolFsmState {
// The gradient has only one point and so should become a fill
if selected_gradient.gradient.positions.len() == 1 {
let fill = Fill::Solid(selected_gradient.gradient.positions[0].1.unwrap_or(Color::BLACK));
let layer = selected_gradient.layer.to_path();
responses.add(GraphOperationMessage::FillSet { layer, fill });
responses.add(GraphOperationMessage::FillSet {
layer: selected_gradient.layer,
fill: Fill::Solid(selected_gradient.gradient.positions[0].1.unwrap_or(Color::BLACK)),
});
return self;
}
@ -467,7 +467,6 @@ impl Fsm for GradientToolFsmState {
DVec2::ONE,
global_tool_data.primary_color,
DAffine2::IDENTITY,
generate_uuid(),
tool_options.gradient_type,
)
};

View file

@ -172,10 +172,6 @@ impl Fsm for ImaginateToolFsmState {
state
}
(ImaginateToolFsmState::Drawing, ImaginateToolMessage::DragStop) => {
if let Some(layer) = &shape_data.layer {
responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path: layer.to_path() });
}
input.mouse.finish_transaction(shape_data.viewport_drag_start(document), responses);
shape_data.cleanup(responses);

View file

@ -81,7 +81,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
.unit(" px")
.label("Weight")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| LineToolMessage::UpdateOptions(LineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder()
}
@ -175,7 +175,6 @@ impl Fsm for LineToolFsmState {
match (self, event) {
(LineToolFsmState::Ready, LineToolMessage::DragStart) => {
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let viewport_start = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
tool_data.drag_start = document.metadata().document_to_viewport.inverse().transform_point2(viewport_start);
@ -186,7 +185,7 @@ impl Fsm for LineToolFsmState {
let layer = graph_modification_utils::new_vector_layer(vec![subpath], generate_uuid(), document.new_layer_parent(), responses);
responses.add(GraphOperationMessage::StrokeSet {
layer: layer.to_path(),
layer,
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
});
tool_data.layer = Some(layer);
@ -280,7 +279,7 @@ fn generate_transform(tool_data: &mut LineToolData, document_to_viewport: DAffin
}
GraphOperationMessage::TransformSet {
layer: tool_data.layer.unwrap().to_path(),
layer: tool_data.layer.unwrap(),
transform: glam::DAffine2::from_scale_angle_translation(DVec2::new(line_length, 1.), angle, start),
transform_in: TransformIn::Viewport,
skip_rerender: false,

View file

@ -91,8 +91,8 @@ impl LayoutHolder for PathTool {
.label("X")
.min_width(120)
.disabled(x.is_none())
.min(-((1u64 << std::f64::MANTISSA_DIGITS) as f64))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.min(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(move |number_input: &NumberInput| {
let new_x = number_input.value.unwrap_or(x.unwrap());
PathToolMessage::SelectedPointXChanged { new_x }.into()
@ -104,8 +104,8 @@ impl LayoutHolder for PathTool {
.label("Y")
.min_width(120)
.disabled(y.is_none())
.min(-((1u64 << std::f64::MANTISSA_DIGITS) as f64))
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.min(-((1_u64 << std::f64::MANTISSA_DIGITS) as f64))
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(move |number_input: &NumberInput| {
let new_y = number_input.value.unwrap_or(y.unwrap());
PathToolMessage::SelectedPointYChanged { new_y }.into()
@ -275,9 +275,6 @@ impl PathToolData {
}
selected_points.points.extend(additional_selected_points);
//let include_handles: Vec<_> = selected_layers.iter().map(|x| x.as_slice()).collect();
//self.snap_manager.add_all_document_handles(document, input, &include_handles, &[], &selected_points.points);
self.drag_start_pos = input.mouse.position;
self.previous_mouse_position = input.mouse.position - selected_points.offset;
}

View file

@ -102,7 +102,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
.unit(" px")
.label("Weight")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| PenToolMessage::UpdateOptions(PenOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder()
}
@ -226,7 +226,7 @@ impl PenToolData {
};
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring {
id: last_handle.id,
mirror_angle: false,
@ -259,12 +259,12 @@ impl PenToolData {
self.layer = Some(layer);
responses.add(GraphOperationMessage::FillSet {
layer: layer.to_path(),
layer,
fill: if let Some(color) = fill_color { Fill::Solid(color) } else { Fill::None },
});
responses.add(GraphOperationMessage::StrokeSet {
layer: layer.to_path(),
layer,
stroke: Stroke::new(stroke_color, line_weight),
});
@ -298,21 +298,21 @@ impl PenToolData {
}
// Remove the point that has just been placed
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::RemoveManipulatorGroup { id: last_manipulator_group.id },
});
// Move the in handle of the previous anchor to on top of the previous position
let point = ManipulatorPointId::new(previous_manipulator_group.id, outwards_handle);
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position: previous_anchor },
});
// Stop the handles on the last point from mirroring
let id = previous_manipulator_group.id;
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring { id, mirror_angle: false },
});
@ -353,26 +353,26 @@ impl PenToolData {
// Move the in handle of the first point to where the user has placed it
let point = ManipulatorPointId::new(first_manipulator_group.id, inwards_handle);
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position: last_in },
});
// Stop the handles on the first point from mirroring
let id = first_manipulator_group.id;
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorHandleMirroring { id, mirror_angle: false },
});
// Remove the point that has just been placed
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::RemoveManipulatorGroup { id: last_manipulator_group.id },
});
// Push a close path node
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetClosed { index: 0, closed: true },
});
@ -387,7 +387,7 @@ impl PenToolData {
}
// Add a new manipulator for the next anchor that we will place
if let Some(out_handle) = outwards_handle.get_position(last_manipulator_group) {
responses.add(add_manipulator_group(&self.layer, self.from_start, bezier_rs::ManipulatorGroup::new_anchor(out_handle)));
responses.add(add_manipulator_group(self.layer, self.from_start, bezier_rs::ManipulatorGroup::new_anchor(out_handle)));
}
Some(PenToolFsmState::PlacingAnchor)
@ -419,7 +419,7 @@ impl PenToolData {
// Update points on current segment (to show preview of new handle)
let point = ManipulatorPointId::new(last_manipulator_group.id, outwards_handle);
responses.add(GraphOperationMessage::Vector {
layer: self.layer?.to_path(),
layer: self.layer?,
modification: VectorDataModification::SetManipulatorPosition { point, position: pos },
});
@ -430,7 +430,7 @@ impl PenToolData {
let pos = last_anchor - (pos - last_anchor);
let point = ManipulatorPointId::new(last_manipulator_group.id, inwards_handle);
responses.add(GraphOperationMessage::Vector {
layer: self.layer?.to_path(),
layer: self.layer?,
modification: VectorDataModification::SetManipulatorPosition { point, position: pos },
});
}
@ -438,7 +438,7 @@ impl PenToolData {
// Update the mirror status of the currently modifying point
let id = last_manipulator_group.id;
responses.add(GraphOperationMessage::Vector {
layer: self.layer?.to_path(),
layer: self.layer?,
modification: VectorDataModification::SetManipulatorHandleMirroring { id, mirror_angle: should_mirror },
});
@ -483,7 +483,7 @@ impl PenToolData {
for manipulator_type in [SelectedType::Anchor, SelectedType::InHandle, SelectedType::OutHandle] {
let point = ManipulatorPointId::new(last_manipulator_group.id, manipulator_type);
responses.add(GraphOperationMessage::Vector {
layer: layer.to_path(),
layer,
modification: VectorDataModification::SetManipulatorPosition { point, position: pos },
});
}
@ -516,7 +516,7 @@ impl PenToolData {
// Remove the unplaced anchor if in anchor placing mode
if fsm == PenToolFsmState::PlacingAnchor {
responses.add(GraphOperationMessage::Vector {
layer: self.layer?.to_path(),
layer: self.layer?,
modification: VectorDataModification::RemoveManipulatorGroup { id: last_manipulator_group.id },
});
last_manipulator_group = previous_manipulator_group;
@ -526,7 +526,7 @@ impl PenToolData {
let point = ManipulatorPointId::new(last_manipulator_group.id, outwards_handle);
let position = last_manipulator_group.anchor;
responses.add(GraphOperationMessage::Vector {
layer: self.layer?.to_path(),
layer: self.layer?,
modification: VectorDataModification::SetManipulatorPosition { point, position },
});
@ -591,7 +591,6 @@ impl Fsm for PenToolFsmState {
// Initialize snapping
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
// Disable this tool's mirroring
tool_data.should_mirror = false;
@ -703,11 +702,10 @@ fn compute_snapped_angle(cached_angle: &mut f64, lock_angle: bool, snap_angle: b
}
/// Pushes a [ManipulatorGroup] to the current layer via a [GraphOperationMessage].
fn add_manipulator_group(layer: &Option<LayerNodeIdentifier>, from_start: bool, manipulator_group: bezier_rs::ManipulatorGroup<ManipulatorGroupId>) -> Message {
fn add_manipulator_group(layer: Option<LayerNodeIdentifier>, from_start: bool, manipulator_group: bezier_rs::ManipulatorGroup<ManipulatorGroupId>) -> Message {
let Some(layer) = layer else {
return Message::NoOp;
};
let layer = layer.to_path();
let modification = if from_start {
VectorDataModification::AddStartManipulatorGroup { subpath_index: 0, manipulator_group }
} else {

View file

@ -111,7 +111,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
.unit(" px")
.label("Weight")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| PolygonToolMessage::UpdateOptions(PolygonOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder()
}
@ -251,12 +251,12 @@ impl Fsm for PolygonToolFsmState {
let fill_color = tool_options.fill.active_color();
responses.add(GraphOperationMessage::FillSet {
layer: layer.to_path(),
layer,
fill: if let Some(color) = fill_color { Fill::Solid(color) } else { Fill::None },
});
responses.add(GraphOperationMessage::StrokeSet {
layer: layer.to_path(),
layer,
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
});

View file

@ -68,7 +68,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
.unit(" px")
.label("Weight")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| RectangleToolMessage::UpdateOptions(RectangleOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder()
}
@ -220,12 +220,12 @@ impl Fsm for RectangleToolFsmState {
let fill_color = tool_options.fill.active_color();
responses.add(GraphOperationMessage::FillSet {
layer: layer.to_path(),
layer,
fill: if let Some(color) = fill_color { Fill::Solid(color) } else { Fill::None },
});
responses.add(GraphOperationMessage::StrokeSet {
layer: layer.to_path(),
layer,
stroke: Stroke::new(tool_options.stroke.active_color(), tool_options.line_weight),
});

View file

@ -302,10 +302,10 @@ impl SelectToolData {
// Duplicate each previously selected layer and select the new ones.
for layer_ancestors in document.metadata().shallowest_unique_layers(self.layers_dragging.iter().copied()) {
let layer = layer_ancestors.last().unwrap();
let layer = *layer_ancestors.last().unwrap();
// Moves the original back to its starting position.
responses.add_front(GraphOperationMessage::TransformChange {
layer: layer.to_path(),
layer,
transform: DAffine2::from_translation(self.drag_start - self.drag_current),
transform_in: TransformIn::Viewport,
skip_rerender: true,
@ -336,11 +336,6 @@ impl SelectToolData {
// layer_metadata,
// });
}
// // Since the selected layers have now moved back to their original transforms before the drag began, we rerender them to be displayed as if they weren't touched.
// for layer_path in self.not_duplicated_layers.iter().flatten() {
// responses.add(DocumentMessage::InputFrameRasterizeRegionBelowLayer { layer_path: layer_path.clone() });
// }
}
/// Removes the duplicated layers. Called when Alt is released and the layers have previously been duplicated.
@ -361,7 +356,7 @@ impl SelectToolData {
// Move the original to under the mouse
for layer_ancestors in document.metadata().shallowest_unique_layers(originals.iter().copied()) {
responses.add_front(GraphOperationMessage::TransformChange {
layer: layer_ancestors.last().unwrap().to_path(),
layer: *layer_ancestors.last().unwrap(),
transform: DAffine2::from_translation(self.drag_current - self.drag_start),
transform_in: TransformIn::Viewport,
skip_rerender: true,
@ -482,22 +477,11 @@ impl Fsm for SelectToolFsmState {
responses.add(DocumentMessage::StartTransaction);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
SelectToolFsmState::DraggingPivot
} else if let Some(_selected_edges) = dragging_bounds {
responses.add(DocumentMessage::StartTransaction);
// let snap_x = selected_edges.2 || selected_edges.3;
// let snap_y = selected_edges.0 || selected_edges.1;
//
// tool_data
// .snap_manager
// .start_snap(document, input, document.bounding_boxes(Some(&selected), None, font_cache), snap_x, snap_y);
// tool_data
// .snap_manager
// .add_all_document_handles(document, input, &[], &selected.iter().map(|x| x.as_slice()).collect::<Vec<_>>(), &[]);
tool_data.layers_dragging = selected;
if let Some(bounds) = &mut tool_data.bounding_box_manager {
@ -606,7 +590,7 @@ impl Fsm for SelectToolFsmState {
// TODO: Cache the result of `shallowest_unique_layers` to avoid this heavy computation every frame of movement, see https://github.com/GraphiteEditor/Graphite/pull/481
for layer_ancestors in document.metadata().shallowest_unique_layers(tool_data.layers_dragging.iter().copied()) {
responses.add_front(GraphOperationMessage::TransformChange {
layer: layer_ancestors.last().unwrap().to_path(),
layer: *layer_ancestors.last().unwrap(),
transform: DAffine2::from_translation(mouse_delta + closest_move),
transform_in: TransformIn::Viewport,
skip_rerender: false,

View file

@ -89,7 +89,7 @@ fn create_weight_widget(line_weight: f64) -> WidgetHolder {
.unit(" px")
.label("Weight")
.min(0.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| SplineToolMessage::UpdateOptions(SplineOptionsUpdate::LineWeight(number_input.value.unwrap())).into())
.widget_holder()
}
@ -215,7 +215,6 @@ impl Fsm for SplineToolFsmState {
let transform = document.metadata().transform_to_viewport(parent);
tool_data.snap_manager.start_snap(document, input, document.bounding_boxes(), true, true);
tool_data.snap_manager.add_all_document_handles(document, input, &[], &[], &[]);
let snapped_position = tool_data.snap_manager.snap_position(responses, document, input.mouse.position);
let pos = transform.inverse().transform_point2(snapped_position);
@ -228,12 +227,12 @@ impl Fsm for SplineToolFsmState {
let layer = graph_modification_utils::new_vector_layer(vec![], generate_uuid(), parent, responses);
responses.add(GraphOperationMessage::FillSet {
layer: layer.to_path(),
layer,
fill: if let Some(color) = tool_options.fill.active_color() { Fill::Solid(color) } else { Fill::None },
});
responses.add(GraphOperationMessage::StrokeSet {
layer: layer.to_path(),
layer,
stroke: Stroke::new(tool_options.stroke.active_color(), tool_data.weight),
});
tool_data.layer = Some(layer);
@ -329,5 +328,5 @@ fn update_spline(tool_data: &SplineToolData, show_preview: bool, responses: &mut
graph_modification_utils::set_manipulator_mirror_angle(subpath.manipulator_groups(), layer, true, responses);
let subpaths = vec![subpath];
let modification = VectorDataModification::UpdateSubpaths { subpaths };
responses.add_front(GraphOperationMessage::Vector { layer: layer.to_path(), modification });
responses.add_front(GraphOperationMessage::Vector { layer, modification });
}

View file

@ -113,7 +113,7 @@ fn create_text_widgets(tool: &TextTool) -> Vec<WidgetHolder> {
.label("Size")
.int()
.min(1.)
.max((1u64 << std::f64::MANTISSA_DIGITS) as f64)
.max((1_u64 << std::f64::MANTISSA_DIGITS) as f64)
.on_update(|number_input: &NumberInput| TextToolMessage::UpdateOptions(TextOptionsUpdate::FontSize(number_input.value.unwrap() as u32)).into())
.widget_holder();
vec![
@ -298,11 +298,11 @@ impl TextToolData {
insert_index: -1,
});
responses.add(GraphOperationMessage::FillSet {
layer: self.layer.to_path(),
layer: self.layer,
fill: if editing_text.color.is_some() { Fill::Solid(editing_text.color.unwrap()) } else { Fill::None },
});
responses.add(GraphOperationMessage::TransformSet {
layer: self.layer.to_path(),
layer: self.layer,
transform: editing_text.transform,
transform_in: TransformIn::Viewport,
skip_rerender: true,
@ -310,7 +310,7 @@ impl TextToolData {
self.set_editing(true, font_cache, document, responses);
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: self.layer.to_path() });
responses.add(NodeGraphMessage::SelectedNodesSet { nodes: vec![self.layer.to_node()] });
TextToolFsmState::Editing
} else {
@ -331,10 +331,11 @@ impl TextToolData {
}
fn fix_text_bounds(&self, new_text: &str, _document: &DocumentMessageHandler, font_cache: &FontCache, responses: &mut VecDeque<Message>) -> Option<()> {
let layer = self.layer.to_path();
let old_bounds = self.get_bounds(&self.editing_text.as_ref()?.text, font_cache)?;
let new_bounds = self.get_bounds(new_text, font_cache)?;
responses.add(GraphOperationMessage::UpdateBounds { layer, old_bounds, new_bounds });
responses.add(GraphOperationMessage::UpdateBounds {
layer: self.layer,
old_bounds: self.get_bounds(&self.editing_text.as_ref()?.text, font_cache)?,
new_bounds: self.get_bounds(new_text, font_cache)?,
});
Some(())
}
@ -437,7 +438,6 @@ impl Fsm for TextToolFsmState {
(TextToolFsmState::Editing, TextToolMessage::TextChange { new_text }) => {
tool_data.fix_text_bounds(&new_text, document, font_cache, responses);
responses.add(NodeGraphMessage::SetQualifiedInputValue {
layer_path: Vec::new(),
node_path: vec![graph_modification_utils::get_text_id(tool_data.layer, &document.network).unwrap()],
input_index: 1,
value: TaggedValue::String(new_text),

View file

@ -20,7 +20,7 @@ use std::fmt::{self, Debug};
pub struct ToolActionHandlerData<'a> {
pub document: &'a DocumentMessageHandler,
pub document_id: u64,
pub document_id: DocumentId,
pub global_tool_data: &'a DocumentToolData,
pub input: &'a InputPreprocessorMessageHandler,
pub font_cache: &'a FontCache,
@ -30,7 +30,7 @@ pub struct ToolActionHandlerData<'a> {
impl<'a> ToolActionHandlerData<'a> {
pub fn new(
document: &'a DocumentMessageHandler,
document_id: u64,
document_id: DocumentId,
global_tool_data: &'a DocumentToolData,
input: &'a InputPreprocessorMessageHandler,
font_cache: &'a FontCache,

View file

@ -5,23 +5,22 @@ use crate::messages::portfolio::document::node_graph::wrap_network_in_scope;
use crate::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use crate::messages::portfolio::document::utility_types::layer_panel::LayerClassification;
use crate::messages::portfolio::document::utility_types::misc::LayerPanelEntry;
use crate::messages::portfolio::document::utility_types::LayerId;
use crate::messages::prelude::*;
use graph_craft::concrete;
use graph_craft::document::value::TaggedValue;
use graph_craft::document::{generate_uuid, DocumentNodeImplementation, NodeId, NodeNetwork};
use graph_craft::graphene_compiler::Compiler;
use graph_craft::imaginate_input::ImaginatePreferences;
use graph_craft::{concrete, Type};
use graphene_core::application_io::{ApplicationIo, NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
use graphene_core::application_io::{NodeGraphUpdateMessage, NodeGraphUpdateSender, RenderConfig};
use graphene_core::memo::IORecord;
use graphene_core::raster::{Image, ImageFrame};
use graphene_core::renderer::{ClickTarget, GraphicElementRendered, SvgSegment, SvgSegmentList};
use graphene_core::renderer::{ClickTarget, GraphicElementRendered, SvgSegmentList};
use graphene_core::text::FontCache;
use graphene_core::transform::{Footprint, Transform};
use graphene_core::vector::style::ViewMode;
use graphene_core::vector::VectorData;
use graphene_core::{Color, GraphicElement, SurfaceFrame, SurfaceId};
use graphene_core::{Color, GraphicElement, SurfaceFrame};
use graphene_std::wasm_application_io::{WasmApplicationIo, WasmEditorApi};
use interpreted_executor::dynamic_executor::DynamicExecutor;
@ -31,22 +30,6 @@ use std::rc::Rc;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::Arc;
/// Identifies a node graph, either the document graph or a node graph associated with a legacy layer.
#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
pub enum GraphIdentifier {
DocumentGraph,
LayerGraph(LayerId),
}
impl GraphIdentifier {
pub const fn new(layer_id: Option<LayerId>) -> Self {
match layer_id {
Some(layer_id) => Self::LayerGraph(layer_id),
None => Self::DocumentGraph,
}
}
}
pub struct NodeRuntime {
pub(crate) executor: DynamicExecutor,
font_cache: FontCache,
@ -58,7 +41,6 @@ pub struct NodeRuntime {
pub(crate) click_targets: HashMap<NodeId, Vec<ClickTarget>>,
pub(crate) upstream_transforms: HashMap<NodeId, (Footprint, DAffine2)>,
graph_hash: Option<u64>,
canvas_cache: HashMap<Vec<LayerId>, SurfaceId>,
monitor_nodes: Vec<Vec<NodeId>>,
}
@ -81,7 +63,6 @@ pub struct ExportConfig {
pub(crate) struct GenerationRequest {
generation_id: u64,
graph: NodeNetwork,
path: Vec<LayerId>,
render_config: RenderConfig,
}
@ -129,7 +110,6 @@ impl NodeRuntime {
imaginate_preferences: Default::default(),
thumbnails: Default::default(),
wasm_io: None,
canvas_cache: HashMap::new(),
click_targets: HashMap::new(),
graph_hash: None,
upstream_transforms: HashMap::new(),
@ -142,7 +122,7 @@ impl NodeRuntime {
// This should be avoided in the future.
requests.reverse();
requests.dedup_by_key(|x| match x {
NodeRuntimeMessage::GenerationRequest(x) => Some(x.path.clone()),
NodeRuntimeMessage::GenerationRequest(x) => Some(x.graph.current_hash()),
_ => None,
});
requests.reverse();
@ -151,17 +131,13 @@ impl NodeRuntime {
NodeRuntimeMessage::FontCacheUpdate(font_cache) => self.font_cache = font_cache,
NodeRuntimeMessage::ImaginatePreferencesUpdate(preferences) => self.imaginate_preferences = preferences,
NodeRuntimeMessage::GenerationRequest(GenerationRequest {
generation_id,
graph,
render_config,
path,
..
generation_id, graph, render_config, ..
}) => {
let transform = render_config.viewport.transform;
let result = self.execute_network(&path, graph, render_config).await;
let result = self.execute_network(graph, render_config).await;
let mut responses = VecDeque::new();
self.update_thumbnails(&path, &mut responses);
self.update_thumbnails(&mut responses);
self.update_upstream_transforms();
let response = GenerationResponse {
@ -179,7 +155,7 @@ impl NodeRuntime {
}
}
async fn execute_network<'a>(&'a mut self, path: &[LayerId], graph: NodeNetwork, render_config: RenderConfig) -> Result<TaggedValue, String> {
async fn execute_network<'a>(&'a mut self, graph: NodeNetwork, render_config: RenderConfig) -> Result<TaggedValue, String> {
if self.wasm_io.is_none() {
self.wasm_io = Some(WasmApplicationIo::new().await);
}
@ -245,21 +221,21 @@ impl NodeRuntime {
Err(e) => return Err(e),
};
if let TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform: _ }) = result {
let old_id = self.canvas_cache.insert(path.to_vec(), surface_id);
if let Some(old_id) = old_id {
if old_id != surface_id {
if let Some(io) = self.wasm_io.as_ref() {
io.destroy_surface(old_id)
}
}
}
}
// if let TaggedValue::SurfaceFrame(SurfaceFrame { surface_id, transform: _ }) = result {
// let old_id = self.canvas_cache.insert(path.to_vec(), surface_id);
// if let Some(old_id) = old_id {
// if old_id != surface_id {
// if let Some(io) = self.wasm_io.as_ref() {
// io.destroy_surface(old_id)
// }
// }
// }
// }
Ok(result)
}
/// Recomputes the thumbnails for the layers in the graph, modifying the state and updating the UI.
pub fn update_thumbnails(&mut self, layer_path: &[LayerId], responses: &mut VecDeque<Message>) {
pub fn update_thumbnails(&mut self, responses: &mut VecDeque<Message>) {
let mut image_data: Vec<_> = Vec::new();
self.thumbnails.retain(|id, _| self.monitor_nodes.iter().any(|node_path| node_path.contains(id)));
for node_path in &self.monitor_nodes {
@ -299,8 +275,7 @@ impl NodeRuntime {
}
let resize = Some(DVec2::splat(100.));
let create_image_data = |(node_id, image)| NodeGraphExecutor::to_frontend_image_data(image, None, layer_path, Some(node_id), resize).ok();
image_data.extend(render.image_data.into_iter().filter_map(create_image_data))
image_data.extend(render.image_data.into_iter().filter_map(|(_, image)| NodeGraphExecutor::to_frontend_image_data(image, resize).ok()))
}
if !image_data.is_empty() {
responses.add(FrontendMessage::UpdateImageData { document_id: 0, image_data });
@ -370,15 +345,11 @@ pub async fn run_node_graph() {
pub struct NodeGraphExecutor {
sender: Sender<NodeRuntimeMessage>,
receiver: Receiver<NodeGraphUpdate>,
// TODO: This is a memory leak since layers are never removed
pub(crate) last_output_type: HashMap<Vec<LayerId>, Option<Type>>,
pub(crate) thumbnails: HashMap<NodeId, SvgSegmentList>,
futures: HashMap<u64, ExecutionContext>,
}
#[derive(Debug, Clone)]
struct ExecutionContext {
layer_path: Vec<LayerId>,
export_config: Option<ExportConfig>,
}
@ -394,18 +365,15 @@ impl Default for NodeGraphExecutor {
futures: Default::default(),
sender: request_sender,
receiver: response_receiver,
last_output_type: Default::default(),
thumbnails: Default::default(),
}
}
}
impl NodeGraphExecutor {
/// Execute the network by flattening it and creating a borrow stack.
fn queue_execution(&self, network: NodeNetwork, layer_path: Vec<LayerId>, render_config: RenderConfig) -> u64 {
fn queue_execution(&self, network: NodeNetwork, render_config: RenderConfig) -> u64 {
let generation_id = generate_uuid();
let request = GenerationRequest {
path: layer_path,
graph: network,
generation_id,
render_config,
@ -470,23 +438,17 @@ impl NodeGraphExecutor {
}
/// Generate a new [`FrontendImageData`] from the [`Image`].
fn to_frontend_image_data(image: Image<Color>, transform: Option<[f64; 6]>, layer_path: &[LayerId], node_id: Option<u64>, resize: Option<DVec2>) -> Result<FrontendImageData, String> {
fn to_frontend_image_data(image: Image<Color>, resize: Option<DVec2>) -> Result<FrontendImageData, String> {
let (image_data, _size) = Self::encode_img(image, resize, image::ImageOutputFormat::Bmp)?;
let mime = "image/bmp".to_string();
let image_data = std::sync::Arc::new(image_data);
Ok(FrontendImageData {
path: layer_path.to_vec(),
node_id,
image_data,
mime,
transform,
})
Ok(FrontendImageData { image_data, mime })
}
/// Evaluates a node graph, computing the entire graph
pub fn submit_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, layer_path: Vec<LayerId>, viewport_resolution: UVec2) -> Result<(), String> {
pub fn submit_node_graph_evaluation(&mut self, document: &mut DocumentMessageHandler, viewport_resolution: UVec2) -> Result<(), String> {
// Get the node graph layer
let network = document.network().clone();
@ -506,9 +468,9 @@ impl NodeGraphExecutor {
};
// Execute the node graph
let generation_id = self.queue_execution(network, layer_path.clone(), render_config);
let generation_id = self.queue_execution(network, render_config);
self.futures.insert(generation_id, ExecutionContext { layer_path, export_config: None });
self.futures.insert(generation_id, ExecutionContext { export_config: None });
Ok(())
}
@ -541,11 +503,8 @@ impl NodeGraphExecutor {
export_config.size = size;
// Execute the node graph
let generation_id = self.queue_execution(network, Vec::new(), render_config);
let execution_context = ExecutionContext {
layer_path: Vec::new(),
export_config: Some(export_config),
};
let generation_id = self.queue_execution(network, render_config);
let execution_context = ExecutionContext { export_config: Some(export_config) };
self.futures.insert(generation_id, execution_context);
Ok(())
@ -626,16 +585,17 @@ impl NodeGraphExecutor {
},
expanded: layer.has_children(document_metadata) && !collapsed.contains(&layer),
selected: document_metadata.selected_layers_contains(layer),
path: vec![node_id],
parent_id: layer.parent(document_metadata).map(|parent| parent.to_node()),
id: node_id,
depth: layer.ancestors(document_metadata).count() - 1,
thumbnail: svg.to_string(),
},
});
}
self.thumbnails = new_thumbnails;
document_metadata.update_transforms(new_upstream_transforms);
document_metadata.update_click_targets(new_click_targets);
responses.extend(updates);
self.process_node_graph_output(node_graph_output, execution_context.layer_path.clone(), transform, responses)?;
self.process_node_graph_output(node_graph_output, transform, responses)?;
responses.add(DocumentMessage::RenderDocument);
responses.add(DocumentMessage::DocumentStructureChanged);
responses.add(BroadcastEvent::DocumentIsDirty);
@ -665,8 +625,7 @@ impl NodeGraphExecutor {
responses.add(FrontendMessage::UpdateDocumentArtwork { svg });
}
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, layer_path: Vec<LayerId>, transform: DAffine2, responses: &mut VecDeque<Message>) -> Result<(), String> {
self.last_output_type.insert(layer_path.clone(), Some(node_graph_output.ty()));
fn process_node_graph_output(&mut self, node_graph_output: TaggedValue, transform: DAffine2, responses: &mut VecDeque<Message>) -> Result<(), String> {
match node_graph_output {
TaggedValue::SurfaceFrame(SurfaceFrame { surface_id: _, transform: _ }) => {
// TODO: Reimplement this now that document-legacy is gone
@ -709,19 +668,4 @@ impl NodeGraphExecutor {
};
Ok(())
}
/// When a blob url for a thumbnail is loaded, update the state and the UI.
pub fn insert_thumbnail_blob_url(&mut self, blob_url: String, node_id: NodeId, responses: &mut VecDeque<Message>) {
for segment_list in self.thumbnails.values_mut() {
if let Some(segment) = segment_list.iter_mut().find(|segment| **segment == SvgSegment::BlobUrl(node_id)) {
*segment = SvgSegment::String(blob_url);
responses.add(FrontendMessage::UpdateNodeThumbnail {
id: node_id,
value: segment_list.to_string(),
});
return;
}
}
warn!("Received blob url for invalid segment")
}
}

View file

@ -4,36 +4,35 @@ use graphite_editor::application::Editor;
use graphite_editor::messages::frontend::utility_types::FrontendImageData;
use graphite_editor::messages::prelude::*;
use axum::body::StreamBody;
use axum::extract::Path;
use axum::http;
use axum::response::IntoResponse;
// use axum::body::StreamBody;
// use axum::extract::Path;
// use axum::http;
// use axum::response::IntoResponse;
use axum::routing::get;
use axum::Router;
use fern::colors::{Color, ColoredLevelConfig};
use http::{Response, StatusCode};
// use http::{Response, StatusCode};
use std::cell::RefCell;
use std::collections::HashMap;
// use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
// use std::sync::Mutex;
static IMAGES: Mutex<Option<HashMap<String, FrontendImageData>>> = Mutex::new(None);
thread_local! {
static EDITOR: RefCell<Option<Editor>> = RefCell::new(None);
}
async fn respond_to(id: Path<String>) -> impl IntoResponse {
let builder = Response::builder().header("Access-Control-Allow-Origin", "*").status(StatusCode::OK);
// 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();
// 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()
}
// 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()
// }
#[tokio::main]
async fn main() {
@ -56,10 +55,10 @@ async fn main() {
.apply()
.unwrap();
*(IMAGES.lock().unwrap()) = Some(HashMap::new());
// *(IMAGES.lock().unwrap()) = Some(HashMap::new());
graphite_editor::application::set_uuid_seed(0);
EDITOR.with(|editor| editor.borrow_mut().replace(Editor::new()));
let app = Router::new().route("/", get(|| async { "Hello, World!" })).route("/image/:id", get(respond_to));
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 {
@ -78,8 +77,7 @@ async fn main() {
}
#[tauri::command]
fn set_random_seed(seed: f64) {
let seed = seed as u64;
graphite_editor::application::set_uuid_seed(seed);
graphite_editor::application::set_uuid_seed(seed as u64);
}
#[tauri::command]
@ -96,20 +94,11 @@ fn handle_message(message: String) -> String {
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();
let transform = image.transform;
images.insert(format!("{:?}_{}", image.path, document_id), image);
stub_data.push(FrontendImageData {
path,
node_id: None,
mime,
mime: image.mime.clone(),
image_data: Arc::new(Vec::new()),
transform,
});
}
FrontendMessage::UpdateImageData { document_id, image_data: stub_data }

View file

@ -4,7 +4,7 @@
import { beginDraggingElement } from "@graphite/io-managers/drag";
import { platformIsMac } from "@graphite/utility-functions/platform";
import type { Editor } from "@graphite/wasm-communication/editor";
import { defaultWidgetLayout, patchWidgetLayout, UpdateDocumentLayerDetails, UpdateDocumentLayerTreeStructureJs, UpdateLayersPanelOptionsLayout } from "@graphite/wasm-communication/messages";
import { defaultWidgetLayout, patchWidgetLayout, UpdateDocumentLayerDetails, UpdateDocumentLayerStructureJs, UpdateLayersPanelOptionsLayout } from "@graphite/wasm-communication/messages";
import type { LayerClassification, LayerPanelEntry } from "@graphite/wasm-communication/messages";
import LayoutCol from "@graphite/components/layout/LayoutCol.svelte";
@ -27,8 +27,9 @@
type DraggingData = {
select?: () => void;
insertFolder: BigUint64Array;
insertIndex: number;
insertParentId: bigint | undefined;
insertDepth: number;
insertIndex: number | undefined;
highlightFolder: boolean;
markerHeight: number;
};
@ -42,7 +43,7 @@
// Interactive dragging
let draggable = true;
let draggingData: undefined | DraggingData = undefined;
let fakeHighlight: undefined | BigUint64Array[] = undefined;
let fakeHighlight: undefined | bigint = undefined;
let dragInPanel = false;
// Layouts
@ -54,24 +55,24 @@
layersPanelOptionsLayout = layersPanelOptionsLayout;
});
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerTreeStructureJs, (updateDocumentLayerTreeStructure) => {
rebuildLayerTree(updateDocumentLayerTreeStructure);
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerStructureJs, (updateDocumentLayerStructure) => {
rebuildLayerHierarchy(updateDocumentLayerStructure);
});
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerDetails, (updateDocumentLayerDetails) => {
const targetLayer = updateDocumentLayerDetails.data;
const targetPath = targetLayer.path;
const targetId = targetLayer.id;
updateLayerInTree(targetPath, targetLayer);
updateLayerInTree(targetId, targetLayer);
});
});
function toggleLayerVisibility(path: BigUint64Array) {
editor.instance.toggleLayerVisibility(path);
function toggleLayerVisibility(id: bigint) {
editor.instance.toggleLayerVisibility(id);
}
function handleExpandArrowClick(path: BigUint64Array) {
editor.instance.toggleLayerExpansion(path);
function handleExpandArrowClick(id: bigint) {
editor.instance.toggleLayerExpansion(id);
}
async function onEditLayerName(listing: LayerListingInfo) {
@ -97,7 +98,7 @@
layers = layers;
const name = (e.target instanceof HTMLInputElement && e.target.value) || "";
editor.instance.setLayerName(listing.entry.path, name);
editor.instance.setLayerName(listing.entry.id, name);
listing.entry.name = name;
}
@ -120,16 +121,16 @@
const [accel, oppositeAccel] = platformIsMac() ? [meta, ctrl] : [ctrl, meta];
// Select the layer only if the accel and/or shift keys are pressed
if (!oppositeAccel && !alt) selectLayer(accel, shift, listing);
if (!oppositeAccel && !alt) selectLayer(listing, accel, shift);
e.stopPropagation();
}
function selectLayer(accel: boolean, shift: boolean, listing: LayerListingInfo) {
function selectLayer(listing: LayerListingInfo, accel: boolean, shift: boolean) {
// Don't select while we are entering text to rename the layer
if (listing.editingName) return;
editor.instance.selectLayer(listing.entry.path, accel, shift);
editor.instance.selectLayer(listing.entry.id, accel, shift);
}
async function deselectAllLayers() {
@ -148,10 +149,11 @@
let closest = Infinity;
// Folder to insert into
let insertFolder = new BigUint64Array();
let insertParentId: bigint | undefined = undefined;
let insertDepth = 0;
// Insert index
let insertIndex = -1;
// Insert index (starts at the end, essentially infinity)
let insertIndex = undefined;
// Whether you are inserting into a folder and should show the folder outline
let highlightFolder = false;
@ -171,7 +173,8 @@
// Inserting above current row
if (distance > 0 && distance < closest) {
insertFolder = layer.path.slice(0, layer.path.length - 1);
insertParentId = layer.parentId;
insertDepth = layer.depth - 1;
insertIndex = folderIndex;
highlightFolder = false;
closest = distance;
@ -179,15 +182,24 @@
}
// Inserting below current row
else if (distance > -closest && distance > -RANGE_TO_INSERT_WITHIN_BOTTOM_FOLDER_NOT_ROOT && distance < 0) {
insertFolder = isNestingLayer(layer.layerClassification) ? layer.path : layer.path.slice(0, layer.path.length - 1);
insertIndex = isNestingLayer(layer.layerClassification) ? 0 : folderIndex + 1;
highlightFolder = isNestingLayer(layer.layerClassification);
if (isNestingLayer(layer.layerClassification)) {
insertParentId = layer.id;
insertDepth = layer.depth;
insertIndex = 0;
highlightFolder = true;
} else {
insertParentId = layer.parentId;
insertDepth = layer.depth - 1;
insertIndex = folderIndex + 1;
highlightFolder = false;
}
closest = -distance;
markerHeight = index === treeChildren.length - 1 ? rect.bottom - INSERT_MARK_OFFSET : rect.bottom;
}
// Inserting with no nesting at the end of the panel
else if (closest === Infinity) {
if (layer.path.length === 1) insertIndex = folderIndex + 1;
if (layer.parentId === undefined) insertIndex = folderIndex + 1;
markerHeight = rect.bottom - INSERT_MARK_OFFSET;
}
@ -199,7 +211,8 @@
return {
select,
insertFolder,
insertParentId,
insertDepth,
insertIndex,
highlightFolder,
markerHeight,
@ -210,10 +223,10 @@
const layer = listing.entry;
dragInPanel = true;
if (!layer.selected) {
fakeHighlight = [layer.path];
fakeHighlight = layer.id;
}
const select = () => {
if (!layer.selected) selectLayer(false, false, listing);
if (!layer.selected) selectLayer(listing, false, false);
};
const target = (event.target instanceof HTMLElement && event.target) || undefined;
@ -240,58 +253,49 @@
async function drop() {
if (draggingData && dragInPanel) {
const { select, insertFolder, insertIndex } = draggingData;
const { select, insertParentId, insertIndex } = draggingData;
select?.();
editor.instance.moveLayerInTree(insertFolder, insertIndex);
editor.instance.moveLayerInTree(insertParentId, insertIndex);
}
draggingData = undefined;
fakeHighlight = undefined;
dragInPanel = false;
}
function rebuildLayerTree(updateDocumentLayerTreeStructure: UpdateDocumentLayerTreeStructureJs) {
function rebuildLayerHierarchy(updateDocumentLayerStructure: UpdateDocumentLayerStructureJs) {
const layerWithNameBeingEdited = layers.find((layer: LayerListingInfo) => layer.editingName);
const layerPathWithNameBeingEdited = layerWithNameBeingEdited?.entry.path;
const layerIdWithNameBeingEdited = layerPathWithNameBeingEdited?.slice(-1)[0];
const path: bigint[] = [];
const layerIdWithNameBeingEdited = layerWithNameBeingEdited?.entry.id;
// Clear the layer tree before rebuilding it
// Clear the layer hierarchy before rebuilding it
layers = [];
// Build the new layer tree
const recurse = (folder: UpdateDocumentLayerTreeStructureJs) => {
// Build the new layer hierarchy
const recurse = (folder: UpdateDocumentLayerStructureJs) => {
folder.children.forEach((item, index) => {
// TODO: fix toString
const layerId = BigInt(item.layerId.toString());
path.push(layerId);
const mapping = layerCache.get([path[path.length - 1]].toString());
const mapping = layerCache.get(String(item.layerId));
if (mapping) {
mapping.path = new BigUint64Array(path);
mapping.id = item.layerId;
layers.push({
folderIndex: index,
bottomLayer: index === folder.children.length - 1,
entry: mapping,
editingName: layerIdWithNameBeingEdited === layerId,
editingName: layerIdWithNameBeingEdited === item.layerId,
});
}
// Call self recursively if there are any children
if (item.children.length >= 1) recurse(item);
path.pop();
});
};
recurse(updateDocumentLayerTreeStructure);
recurse(updateDocumentLayerStructure);
layers = layers;
}
function updateLayerInTree(targetPath: BigUint64Array, targetLayer: LayerPanelEntry) {
const path = targetPath.toString();
layerCache.set(path, targetLayer);
function updateLayerInTree(targetId: bigint, targetLayer: LayerPanelEntry) {
layerCache.set(String(targetId), targetLayer);
const layer = layers.find((layer: LayerListingInfo) => layer.entry.path.toString() === path);
const layer = layers.find((layer: LayerListingInfo) => layer.entry.id === targetId);
if (layer) {
layer.entry = targetLayer;
layers = layers;
@ -305,15 +309,15 @@
</LayoutRow>
<LayoutRow class="list-area" scrollableY={true}>
<LayoutCol class="list" bind:this={list} on:click={() => deselectAllLayers()} on:dragover={(e) => draggable && updateInsertLine(e)} on:dragend={() => draggable && drop()}>
{#each layers as listing, index (String(listing.entry.path.slice(-1)))}
{#each layers as listing, index (String(listing.entry.id))}
<LayoutRow
class="layer"
classes={{
selected: fakeHighlight ? fakeHighlight.includes(listing.entry.path) : listing.entry.selected,
"insert-folder": (draggingData?.highlightFolder || false) && draggingData?.insertFolder === listing.entry.path,
selected: fakeHighlight !== undefined ? fakeHighlight === listing.entry.id : listing.entry.selected,
"insert-folder": (draggingData?.highlightFolder || false) && draggingData?.insertParentId === listing.entry.id,
}}
styles={{ "--layer-indent-levels": `${listing.entry.path.length - 1}` }}
data-layer={String(listing.entry.path)}
styles={{ "--layer-indent-levels": `${listing.entry.depth - 1}` }}
data-layer
data-index={index}
tooltip={listing.entry.tooltip}
{draggable}
@ -321,7 +325,7 @@
on:click={(e) => selectLayerWithModifiers(e, listing)}
>
{#if isNestingLayer(listing.entry.layerClassification)}
<button class="expand-arrow" class:expanded={listing.entry.expanded} on:click|stopPropagation={() => handleExpandArrowClick(listing.entry.path)} tabindex="0" />
<button class="expand-arrow" class:expanded={listing.entry.expanded} on:click|stopPropagation={() => handleExpandArrowClick(listing.entry.id)} tabindex="0" />
{#if listing.entry.layerClassification === "Artboard"}
<IconLabel icon="Artboard" class={"layer-type-icon"} />
{:else if listing.entry.layerClassification === "Folder"}
@ -347,7 +351,7 @@
</LayoutRow>
<IconButton
class={"visibility"}
action={(e) => (toggleLayerVisibility(listing.entry.path), e?.stopPropagation())}
action={(e) => (toggleLayerVisibility(listing.entry.id), e?.stopPropagation())}
size={24}
icon={(() => true)() ? "EyeVisible" : "EyeHidden"}
tooltip={(() => true)() ? "Visible" : "Hidden"}
@ -356,7 +360,7 @@
{/each}
</LayoutCol>
{#if draggingData && !draggingData.highlightFolder && dragInPanel}
<div class="insert-mark" style:left={`${4 + draggingData.insertFolder.length * 16}px`} style:top={`${draggingData.markerHeight}px`} />
<div class="insert-mark" style:left={`${4 + draggingData.insertDepth * 16}px`} style:top={`${draggingData.markerHeight}px`} />
{/if}
</LayoutRow>
</LayoutCol>
@ -387,7 +391,7 @@
}
}
// Layer tree
// Layer hierarchy
.list-area {
margin: 4px 0;
position: relative;

View file

@ -116,8 +116,8 @@
const from = connectorToNodeIndex(linkInProgressFromConnector);
const to = linkInProgressToConnector instanceof SVGSVGElement ? connectorToNodeIndex(linkInProgressToConnector) : undefined;
const linkStart = $nodeGraph.nodes.find((node) => node.id === from?.nodeId)?.isLayer;
const linkEnd = $nodeGraph.nodes.find((node) => node.id === to?.nodeId)?.isLayer && to?.index !== 0;
const linkStart = $nodeGraph.nodes.find((node) => node.id === from?.nodeId)?.isLayer || false;
const linkEnd = ($nodeGraph.nodes.find((node) => node.id === to?.nodeId)?.isLayer && to?.index !== 0) || false;
return createWirePath(linkInProgressFromConnector, linkInProgressToConnector, linkStart, linkEnd);
}
return undefined;
@ -158,8 +158,8 @@
const { nodeInput, nodeOutput } = resolveLink(link);
if (!nodeInput || !nodeOutput) return [];
if (disconnecting?.linkIndex === index) return [];
const linkStart = $nodeGraph.nodes.find((node) => node.id === link.linkStart)?.isLayer;
const linkEnd = $nodeGraph.nodes.find((node) => node.id === link.linkEnd)?.isLayer && link.linkEndInputIndex !== 0n;
const linkStart = $nodeGraph.nodes.find((node) => node.id === link.linkStart)?.isLayer || false;
const linkEnd = ($nodeGraph.nodes.find((node) => node.id === link.linkEnd)?.isLayer && link.linkEndInputIndex !== 0n) || false;
return [createWirePath(nodeOutput, nodeInput.getBoundingClientRect(), linkStart, linkEnd)];
});
@ -673,7 +673,7 @@
<div class="layers-and-nodes" style:transform={`scale(${transform.scale}) translate(${transform.x}px, ${transform.y}px)`} style:transform-origin={`0 0`} bind:this={nodesContainer}>
<!-- Layers -->
{#each $nodeGraph.nodes.flatMap((node, nodeIndex) => (node.isLayer ? [{ node, nodeIndex }] : [])) as { node, nodeIndex } (nodeIndex)}
{@const clipPathId = `${Math.random()}`.substring(2)}
{@const clipPathId = String(Math.random()).substring(2)}
{@const stackDatainput = node.exposedInputs[0]}
<div
class="layer"
@ -756,7 +756,7 @@
<!-- Nodes -->
{#each $nodeGraph.nodes.flatMap((node, nodeIndex) => (node.isLayer ? [] : [{ node, nodeIndex }])) as { node, nodeIndex } (nodeIndex)}
{@const exposedInputsOutputs = [...node.exposedInputs, ...node.exposedOutputs]}
{@const clipPathId = `${Math.random()}`.substring(2)}
{@const clipPathId = String(Math.random()).substring(2)}
<div
class="node"
class:selected={selected.includes(node.id)}

View file

@ -15,7 +15,7 @@
let inputElement: HTMLInputElement | undefined;
let id = `${Math.random()}`.substring(2);
let id = String(Math.random()).substring(2);
$: displayIcon = (!checked && icon === "Checkmark" ? "Empty12px" : icon) as IconName;

View file

@ -29,7 +29,7 @@
export let hideContextMenu = false;
let inputOrTextarea: HTMLInputElement | HTMLTextAreaElement | undefined;
let id = `${Math.random()}`.substring(2);
let id = String(Math.random()).substring(2);
let macKeyboardLayout = platformIsMac();
$: inputValue = value;

View file

@ -176,7 +176,7 @@
function onTextFocused() {
if (value === undefined) text = "";
else if (unitIsHiddenWhenEditing) text = `${value}`;
else if (unitIsHiddenWhenEditing) text = String(value);
else text = `${value}${unPluralize(unit, value)}`;
editing = true;

View file

@ -14,7 +14,7 @@ export function createLocalizationManager(editor: Editor) {
const dateString = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
const timeString = `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
const timezoneNameString = timezoneName?.value;
return { timestamp: `${dateString} ${timeString} ${timezoneNameString}`, year: `${date.getFullYear()}` };
return { timestamp: `${dateString} ${timeString} ${timezoneNameString}`, year: String(date.getFullYear()) };
}
// Subscribe to process backend event

View file

@ -33,7 +33,7 @@ export function panicProxy<T extends object>(module: T): T {
result = targetValue.apply(this, args);
} catch (err) {
// Suppress `unreachable` WebAssembly.RuntimeError exceptions
if (!`${err}`.startsWith("RuntimeError: unreachable")) throw err;
if (!String(err).startsWith("RuntimeError: unreachable")) throw err;
}
return result;
};

View file

@ -10,35 +10,6 @@ export type Editor = Readonly<ReturnType<typeof createEditor>>;
// `wasmImport` starts uninitialized because its initialization needs to occur asynchronously, and thus needs to occur by manually calling and awaiting `initWasm()`
let wasmImport: WebAssembly.Memory | undefined;
export async function updateImage(path: BigUint64Array, nodeId: bigint, mime: string, imageData: Uint8Array, _transform: Float64Array, _documentId: bigint) {
const blob = new Blob([imageData], { type: mime });
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();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// (window as any).editorInstance?.setImageBlobURL(documentId, path, nodeId, blobURL, image.naturalWidth, image.naturalHeight, transform);
}
export async function fetchImage(_path: BigUint64Array, _nodeId: bigint, _mime: string, _documentId: bigint, url: string) {
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();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
// (window as any).editorInstance?.setImageBlobURL(documentId, path, nodeId, blobURL, image.naturalWidth, image.naturalHeight, undefined);
}
const tauri = "__TAURI_METADATA__" in window && import("@tauri-apps/api");
export async function dispatchTauri(message: unknown) {
if (!tauri) return;

View file

@ -560,10 +560,10 @@ export class TriggerSavePreferences extends JsMessage {
export class DocumentChanged extends JsMessage {}
export class UpdateDocumentLayerTreeStructureJs extends JsMessage {
export class UpdateDocumentLayerStructureJs extends JsMessage {
constructor(
readonly layerId: bigint,
readonly children: UpdateDocumentLayerTreeStructureJs[],
readonly children: UpdateDocumentLayerStructureJs[],
) {
super();
}
@ -574,7 +574,7 @@ type DataBuffer = {
length: bigint;
};
export function newUpdateDocumentLayerTreeStructure(input: { dataBuffer: DataBuffer }, wasm: WasmRawInstance): UpdateDocumentLayerTreeStructureJs {
export function newUpdateDocumentLayerStructure(input: { dataBuffer: DataBuffer }, wasm: WasmRawInstance): UpdateDocumentLayerStructureJs {
const pointerNum = Number(input.dataBuffer.pointer);
const lengthNum = Number(input.dataBuffer.length);
@ -591,7 +591,7 @@ export function newUpdateDocumentLayerTreeStructure(input: { dataBuffer: DataBuf
const layerIdsSection = new DataView(wasmMemoryBuffer, pointerNum + 8 + structureSectionLength * 8);
let layersEncountered = 0;
let currentFolder = new UpdateDocumentLayerTreeStructureJs(BigInt(-1), []);
let currentFolder = new UpdateDocumentLayerStructureJs(BigInt(-1), []);
const currentFolderStack = [currentFolder];
for (let i = 0; i < structureSectionLength; i += 1) {
@ -606,7 +606,7 @@ export function newUpdateDocumentLayerTreeStructure(input: { dataBuffer: DataBuf
const layerId = layerIdsSection.getBigUint64(layersEncountered * 8, true);
layersEncountered += 1;
const childLayer = new UpdateDocumentLayerTreeStructureJs(layerId, []);
const childLayer = new UpdateDocumentLayerStructureJs(layerId, []);
currentFolder.children.push(childLayer);
}
@ -650,8 +650,8 @@ export class DisplayEditableTextboxTransform extends JsMessage {
export class UpdateImageData extends JsMessage {
readonly documentId!: bigint;
@Type(() => RenderedImageData)
readonly imageData!: RenderedImageData[];
@Type(() => FrontendImageData)
readonly imageData!: FrontendImageData[];
}
export class DisplayRemoveEditableTextbox extends JsMessage {}
@ -669,8 +669,12 @@ export class LayerPanelEntry {
layerClassification!: LayerClassification;
@Transform(({ value }: { value: bigint[] }) => new BigUint64Array(value))
path!: BigUint64Array;
parentId!: bigint | undefined;
id!: bigint;
@Transform(({ value }: { value: bigint }) => Number(value))
depth!: number;
expanded!: boolean;
@ -681,16 +685,10 @@ export class LayerPanelEntry {
export type LayerClassification = "Folder" | "Artboard" | "Layer";
export class RenderedImageData {
readonly path!: BigUint64Array;
readonly nodeId!: bigint;
export class FrontendImageData {
readonly mime!: string;
readonly imageData!: Uint8Array;
readonly transform!: Float64Array;
}
export class DisplayDialogDismiss extends JsMessage {}
@ -1381,7 +1379,7 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateDocumentArtwork,
UpdateDocumentBarLayout,
UpdateDocumentLayerDetails,
UpdateDocumentLayerTreeStructureJs: newUpdateDocumentLayerTreeStructure,
UpdateDocumentLayerStructureJs: newUpdateDocumentLayerStructure,
UpdateDocumentModeLayout,
UpdateDocumentRulers,
UpdateDocumentScrollbars,

View file

@ -12,7 +12,6 @@ use editor::consts::{FILE_SAVE_SUFFIX, GRAPHITE_DOCUMENT_VERSION};
use editor::messages::input_mapper::utility_types::input_keyboard::ModifierKeys;
use editor::messages::input_mapper::utility_types::input_mouse::{EditorMouseState, ScrollDelta, ViewportBounds};
use editor::messages::portfolio::document::utility_types::document_metadata::LayerNodeIdentifier;
use editor::messages::portfolio::document::utility_types::LayerId;
use editor::messages::portfolio::utility_types::Platform;
use editor::messages::prelude::*;
use graph_craft::document::NodeId;
@ -35,8 +34,6 @@ pub fn set_random_seed(seed: u64) {
/// This avoids creating a json with a list millions of numbers long.
#[wasm_bindgen(module = "/../src/wasm-communication/editor.ts")]
extern "C" {
fn updateImage(path: Vec<u64>, nodeId: Option<u64>, mime: String, imageData: &[u8], transform: js_sys::Float64Array, document_id: u64);
fn fetchImage(path: Vec<u64>, nodeId: Option<u64>, mime: String, document_id: u64, identifier: String);
//fn dispatchTauri(message: String) -> String;
fn dispatchTauri(message: String);
}
@ -160,29 +157,28 @@ impl JsEditorHandle {
// Sends a FrontendMessage to JavaScript
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"))]
{
let transform = if let Some(transform_val) = image.transform {
let transform = js_sys::Float64Array::new_with_length(6);
transform.copy_from(&transform_val);
transform
} else {
js_sys::Float64Array::default()
};
updateImage(image.path, image.node_id, image.mime, &image.image_data, transform, document_id);
}
#[cfg(feature = "tauri")]
{
let identifier = format!("http://localhost:3001/image/{:?}_{}", image.path, document_id);
fetchImage(image.path.clone(), image.node_id, image.mime, document_id, identifier);
}
}
if let FrontendMessage::UpdateImageData { document_id: _, image_data: _ } = message {
// for image in image_data {
// #[cfg(not(feature = "tauri"))]
// {
// let transform = if let Some(transform_val) = image.transform {
// let transform = js_sys::Float64Array::new_with_length(6);
// transform.copy_from(&transform_val);
// transform
// } else {
// js_sys::Float64Array::default()
// };
// }
// #[cfg(feature = "tauri")]
// {
// let identifier = format!("http://localhost:3001/image/{:?}_{}", image.path, document_id);
// fetchImage(image.path.clone(), image.node_id, image.mime, document_id, identifier);
// }
// }
return;
}
if let FrontendMessage::UpdateDocumentLayerTreeStructure { data_buffer } = message {
message = FrontendMessage::UpdateDocumentLayerTreeStructureJs { data_buffer: data_buffer.into() };
if let FrontendMessage::UpdateDocumentLayerStructure { data_buffer } = message {
message = FrontendMessage::UpdateDocumentLayerStructureJs { data_buffer: data_buffer.into() };
}
let message_type = message.to_discriminant().local_name();
@ -298,7 +294,7 @@ impl JsEditorHandle {
}
#[wasm_bindgen(js_name = selectDocument)]
pub fn select_document(&self, document_id: u64) {
pub fn select_document(&self, document_id: DocumentId) {
let message = PortfolioMessage::SelectDocument { document_id };
self.dispatch(message);
}
@ -331,7 +327,7 @@ impl JsEditorHandle {
}
#[wasm_bindgen(js_name = openAutoSavedDocument)]
pub fn open_auto_saved_document(&self, document_id: u64, document_name: String, document_is_saved: bool, document_serialized_content: String) {
pub fn open_auto_saved_document(&self, document_id: DocumentId, document_name: String, document_is_saved: bool, document_serialized_content: String) {
let message = PortfolioMessage::OpenDocumentFileWithId {
document_id,
document_name,
@ -343,13 +339,13 @@ impl JsEditorHandle {
}
#[wasm_bindgen(js_name = triggerAutoSave)]
pub fn trigger_auto_save(&self, document_id: u64) {
pub fn trigger_auto_save(&self, document_id: DocumentId) {
let message = PortfolioMessage::AutoSaveDocument { document_id };
self.dispatch(message);
}
#[wasm_bindgen(js_name = closeDocumentWithConfirmation)]
pub fn close_document_with_confirmation(&self, document_id: u64) {
pub fn close_document_with_confirmation(&self, document_id: DocumentId) {
let message = PortfolioMessage::CloseDocumentWithConfirmation { document_id };
self.dispatch(message);
}
@ -532,8 +528,8 @@ impl JsEditorHandle {
/// Modify the layer selection based on the layer which is clicked while holding down the <kbd>Ctrl</kbd> and/or <kbd>Shift</kbd> modifier keys used for range selection behavior
#[wasm_bindgen(js_name = selectLayer)]
pub fn select_layer(&self, layer_path: Vec<LayerId>, ctrl: bool, shift: bool) {
let message = DocumentMessage::SelectLayer { layer_path, ctrl, shift };
pub fn select_layer(&self, id: NodeId, ctrl: bool, shift: bool) {
let message = DocumentMessage::SelectLayer { id, ctrl, shift };
self.dispatch(message);
}
@ -544,20 +540,23 @@ impl JsEditorHandle {
self.dispatch(message);
}
/// Move a layer to be next to the specified neighbor
/// Move a layer to within a folder and placed down at the given index.
/// If the folder is `None`, it is inserted into the document root.
/// If the insert index is `None`, it is inserted at the end of the folder (equivalent to index infinity).
#[wasm_bindgen(js_name = moveLayerInTree)]
pub fn move_layer_in_tree(&self, folder_path: Vec<LayerId>, insert_index: isize) {
let parent = folder_path.last().copied().map(LayerNodeIdentifier::new_unchecked).unwrap_or(LayerNodeIdentifier::ROOT);
let message = DocumentMessage::MoveSelectedLayersTo { parent, insert_index };
pub fn move_layer_in_tree(&self, insert_parent_id: Option<NodeId>, insert_index: Option<usize>) {
let parent = insert_parent_id.map(|id| LayerNodeIdentifier::new_unchecked(id)).unwrap_or(LayerNodeIdentifier::default());
let message = DocumentMessage::MoveSelectedLayersTo {
parent,
insert_index: insert_index.map(|x| x as isize).unwrap_or(-1),
};
self.dispatch(message);
}
/// Set the name for the layer
#[wasm_bindgen(js_name = setLayerName)]
pub fn set_layer_name(&self, layer_path: Vec<LayerId>, name: String) {
let node_id = *layer_path.last().unwrap();
let message = NodeGraphMessage::SetName { node_id, name };
pub fn set_layer_name(&self, id: NodeId, name: String) {
let message = NodeGraphMessage::SetName { node_id: id, name };
self.dispatch(message);
}
@ -575,23 +574,9 @@ impl JsEditorHandle {
self.dispatch(message);
}
// /// Sends the blob URL generated by JS to the Image layer
// #[wasm_bindgen(js_name = setImageBlobURL)]
// pub fn set_image_blob_url(&self, document_id: u64, layer_path: Vec<LayerId>, node_id: Option<NodeId>, blob_url: String, width: f64, height: f64, _transform: Option<js_sys::Float64Array>) {
// let resolution = (width, height);
// let message = PortfolioMessage::SetImageBlobUrl {
// document_id,
// layer_path: layer_path.clone(),
// node_id,
// blob_url,
// resolution,
// };
// self.dispatch(message);
// }
/// Notifies the backend that the user connected a node's primary output to one of another node's inputs
#[wasm_bindgen(js_name = connectNodesByLink)]
pub fn connect_nodes_by_link(&self, output_node: u64, output_node_connector_index: usize, input_node: u64, input_node_connector_index: usize) {
pub fn connect_nodes_by_link(&self, output_node: NodeId, output_node_connector_index: usize, input_node: NodeId, input_node_connector_index: usize) {
let message = NodeGraphMessage::ConnectNodesByLink {
output_node,
output_node_connector_index,
@ -603,14 +588,14 @@ impl JsEditorHandle {
/// Shifts the node and its children to stop nodes going on top of each other
#[wasm_bindgen(js_name = shiftNode)]
pub fn shift_node(&self, node_id: u64) {
pub fn shift_node(&self, node_id: NodeId) {
let message = NodeGraphMessage::ShiftNode { node_id };
self.dispatch(message);
}
/// Notifies the backend that the user disconnected a node
#[wasm_bindgen(js_name = disconnectNodes)]
pub fn disconnect_nodes(&self, node_id: u64, input_index: usize) {
pub fn disconnect_nodes(&self, node_id: NodeId, input_index: usize) {
let message = NodeGraphMessage::DisconnectNodes { node_id, input_index };
self.dispatch(message);
}
@ -629,7 +614,7 @@ impl JsEditorHandle {
/// Creates a new document node in the node graph
#[wasm_bindgen(js_name = createNode)]
pub fn create_node(&self, node_type: String, x: i32, y: i32) -> u64 {
pub fn create_node(&self, node_type: String, x: i32, y: i32) -> NodeId {
let id = generate_uuid();
let message = NodeGraphMessage::CreateNode { node_id: Some(id), node_type, x, y };
self.dispatch(message);
@ -638,7 +623,7 @@ impl JsEditorHandle {
/// Notifies the backend that the user selected a node in the node graph
#[wasm_bindgen(js_name = selectNodes)]
pub fn select_nodes(&self, nodes: Option<Vec<u64>>) {
pub fn select_nodes(&self, nodes: Option<Vec<NodeId>>) {
let nodes = nodes.unwrap_or_default();
let message = NodeGraphMessage::SelectedNodesSet { nodes };
self.dispatch(message);
@ -653,7 +638,7 @@ impl JsEditorHandle {
/// Notifies the backend that the user double clicked a node
#[wasm_bindgen(js_name = doubleClickNode)]
pub fn double_click_node(&self, node: u64) {
pub fn double_click_node(&self, node: NodeId) {
let message = NodeGraphMessage::DoubleClickNode { node };
self.dispatch(message);
}
@ -686,15 +671,15 @@ impl JsEditorHandle {
/// Toggle visibility of a layer from the layer list
#[wasm_bindgen(js_name = toggleLayerVisibility)]
pub fn toggle_layer_visibility(&self, layer_path: Vec<LayerId>) {
let message = NodeGraphMessage::ToggleHidden { node_id: *layer_path.last().unwrap() };
pub fn toggle_layer_visibility(&self, id: NodeId) {
let message = NodeGraphMessage::ToggleHidden { node_id: id };
self.dispatch(message);
}
/// Toggle expansions state of a layer from the layer list
#[wasm_bindgen(js_name = toggleLayerExpansion)]
pub fn toggle_layer_expansion(&self, layer_path: Vec<LayerId>) {
let message = DocumentMessage::ToggleLayerExpansion { layer: *layer_path.last().unwrap() };
pub fn toggle_layer_expansion(&self, id: NodeId) {
let message = DocumentMessage::ToggleLayerExpansion { id };
self.dispatch(message);
}

View file

@ -19,6 +19,7 @@ use wasm_bindgen::prelude::*;
pub static EDITOR_HAS_CRASHED: AtomicBool = AtomicBool::new(false);
pub static LOGGER: WasmLog = WasmLog;
thread_local! {
// TODO: Remove the concept of multiple editor instances to simplify all of this
pub static EDITOR_INSTANCES: RefCell<HashMap<u64, editor::application::Editor>> = RefCell::new(HashMap::new());
pub static JS_EDITOR_HANDLES: RefCell<HashMap<u64, editor_api::JsEditorHandle>> = RefCell::new(HashMap::new());
}

View file

@ -66,7 +66,7 @@ fn add_network() -> NodeNetwork {
}]
.into_iter()
.enumerate()
.map(|(i, n)| (i as u64, n))
.map(|(id, node)| (id as NodeId, node))
.collect(),
}
}

View file

@ -16,7 +16,8 @@ use alloc::vec::Vec;
/// This data structure is somewhat similar to a linked list in terms of invariants.
/// The downside is that currently it requires a lot of iteration.
type ElementId = u64;
// TODO: Convert from a type alias to a newtype
pub type ElementId = u64;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, specta::Type, Hash)]
pub struct IdBackedVec<T> {
/// Contained elements
@ -124,7 +125,7 @@ impl<T> IdBackedVec<T> {
}
/// Enumerate the ids and elements in this container `(&ElementId, &T)`
pub fn enumerate(&self) -> core::iter::Zip<core::slice::Iter<u64>, core::slice::Iter<T>> {
pub fn enumerate(&self) -> core::iter::Zip<core::slice::Iter<ElementId>, core::slice::Iter<T>> {
self.element_ids.iter().zip(self.elements.iter())
}

View file

@ -54,7 +54,7 @@ impl core::hash::Hash for Gradient {
}
impl Gradient {
/// Constructs a new gradient with the colors at 0 and 1 specified.
pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, transform: DAffine2, _uuid: u64, gradient_type: GradientType) -> Self {
pub fn new(start: DVec2, start_color: Color, end: DVec2, end_color: Color, transform: DAffine2, gradient_type: GradientType) -> Self {
Gradient {
start,
end,

View file

@ -1,7 +1,7 @@
use super::consts::ManipulatorType;
use super::id_vec::IdBackedVec;
use super::manipulator_group::ManipulatorGroup;
use super::manipulator_point::ManipulatorPoint;
use super::{consts::ManipulatorType, id_vec::ElementId};
use crate::uuid::ManipulatorGroupId;
use alloc::string::String;
@ -204,7 +204,7 @@ impl Subpath {
/// Delete the selected points from the [Subpath]
pub fn delete_selected(&mut self) {
let mut ids_to_delete: Vec<u64> = vec![];
let mut ids_to_delete: Vec<ElementId> = vec![];
for (id, manipulator_group) in self.manipulator_groups_mut().enumerate_mut() {
if manipulator_group.is_anchor_selected() {
ids_to_delete.push(*id);
@ -228,7 +228,7 @@ impl Subpath {
// ** SELECTION OF POINTS **
/// Set a single point to a chosen selection state by providing `(manipulator group ID, manipulator type)`.
pub fn select_point(&mut self, point: (u64, ManipulatorType), selected: bool) -> Option<&mut ManipulatorGroup> {
pub fn select_point(&mut self, point: (ElementId, ManipulatorType), selected: bool) -> Option<&mut ManipulatorGroup> {
let (manipulator_group_id, point_id) = point;
if let Some(manipulator_group) = self.manipulator_groups_mut().by_id_mut(manipulator_group_id) {
manipulator_group.select_point(point_id as usize, selected);
@ -240,7 +240,7 @@ impl Subpath {
}
/// Set points in the [Subpath] to a chosen selection state, given by `(manipulator group ID, manipulator type)`.
pub fn select_points(&mut self, points: &[(u64, ManipulatorType)], selected: bool) {
pub fn select_points(&mut self, points: &[(ElementId, ManipulatorType)], selected: bool) {
points.iter().for_each(|point| {
self.select_point(*point, selected);
});

View file

@ -12,11 +12,12 @@ use std::hash::{Hash, Hasher};
pub mod value;
// TODO: Convert from a type alias to a newtype
pub type NodeId = u64;
/// Hash two IDs together, returning a new ID that is always consistant for two input IDs in a specific order.
/// This is used during [`NodeNetwork::flatten`] in order to ensure consistant yet non-conflicting IDs for inner networks.
fn merge_ids(a: u64, b: u64) -> u64 {
fn merge_ids(a: NodeId, b: NodeId) -> NodeId {
let mut hasher = DefaultHasher::new();
a.hash(&mut hasher);
b.hash(&mut hasher);
@ -639,7 +640,7 @@ impl NodeNetwork {
}
/// Gives an iterator to all nodes connected to the given nodes by all inputs (primary or primary + secondary depending on `only_follow_primary` choice), traversing backwards upstream starting from the given node's inputs.
pub fn upstream_flow_back_from_nodes(&self, node_ids: Vec<NodeId>, only_follow_primary: bool) -> impl Iterator<Item = (&DocumentNode, u64)> {
pub fn upstream_flow_back_from_nodes(&self, node_ids: Vec<NodeId>, only_follow_primary: bool) -> impl Iterator<Item = (&DocumentNode, NodeId)> {
FlowIter {
stack: node_ids,
network: self,
@ -654,13 +655,13 @@ impl NodeNetwork {
/// Check there are no cycles in the graph (this should never happen).
pub fn is_acyclic(&self) -> bool {
let mut dependencies: HashMap<u64, Vec<u64>> = HashMap::new();
let mut dependencies: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
for (node_id, node) in &self.nodes {
dependencies.insert(
*node_id,
node.inputs
.iter()
.filter_map(|input| if let NodeInput::Node { node_id: ref_id, .. } = input { Some(*ref_id) } else { None })
.filter_map(|input| if let NodeInput::Node { node_id, .. } = input { Some(*node_id) } else { None })
.collect(),
);
}
@ -734,11 +735,12 @@ impl NodeNetwork {
/// Collect a hashmap of nodes with a list of the nodes that use it as input
pub fn collect_outwards_links(&self) -> HashMap<NodeId, Vec<NodeId>> {
let mut outwards_links: HashMap<u64, Vec<u64>> = HashMap::new();
for (node_id, node) in &self.nodes {
let mut outwards_links: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
for (current_node_id, node) in &self.nodes {
for input in &node.inputs {
if let NodeInput::Node { node_id: ref_id, .. } = input {
outwards_links.entry(*ref_id).or_default().push(*node_id)
if let NodeInput::Node { node_id, .. } = input {
let outward_links_entry = outwards_links.entry(*node_id).or_default();
outward_links_entry.push(*current_node_id);
}
}
}

View file

@ -38,7 +38,6 @@ pub enum TaggedValue {
ImaginateSamplingMethod(ImaginateSamplingMethod),
ImaginateMaskStartingFill(ImaginateMaskStartingFill),
ImaginateController(ImaginateController),
LayerPath(Option<Vec<u64>>),
VectorData(graphene_core::vector::VectorData),
Fill(graphene_core::vector::style::Fill),
Stroke(graphene_core::vector::style::Stroke),
@ -104,7 +103,6 @@ impl Hash for TaggedValue {
Self::ImaginateSamplingMethod(x) => x.hash(state),
Self::ImaginateMaskStartingFill(x) => x.hash(state),
Self::ImaginateController(x) => x.hash(state),
Self::LayerPath(x) => x.hash(state),
Self::ImageFrame(x) => x.hash(state),
Self::VectorData(x) => x.hash(state),
Self::Fill(x) => x.hash(state),
@ -179,7 +177,6 @@ impl<'a> TaggedValue {
TaggedValue::ImaginateSamplingMethod(x) => Box::new(x),
TaggedValue::ImaginateMaskStartingFill(x) => Box::new(x),
TaggedValue::ImaginateController(x) => Box::new(x),
TaggedValue::LayerPath(x) => Box::new(x),
TaggedValue::VectorData(x) => Box::new(x),
TaggedValue::Fill(x) => Box::new(x),
TaggedValue::Stroke(x) => Box::new(x),
@ -252,7 +249,6 @@ impl<'a> TaggedValue {
TaggedValue::ImaginateSamplingMethod(_) => concrete!(ImaginateSamplingMethod),
TaggedValue::ImaginateMaskStartingFill(_) => concrete!(ImaginateMaskStartingFill),
TaggedValue::ImaginateController(_) => concrete!(ImaginateController),
TaggedValue::LayerPath(_) => concrete!(Option<Vec<u64>>),
TaggedValue::DAffine2(_) => concrete!(DAffine2),
TaggedValue::LuminanceCalculation(_) => concrete!(LuminanceCalculation),
TaggedValue::VectorData(_) => concrete!(graphene_core::vector::VectorData),
@ -316,7 +312,6 @@ impl<'a> TaggedValue {
x if x == TypeId::of::<ImaginateSamplingMethod>() => Ok(TaggedValue::ImaginateSamplingMethod(*downcast(input).unwrap())),
x if x == TypeId::of::<ImaginateMaskStartingFill>() => Ok(TaggedValue::ImaginateMaskStartingFill(*downcast(input).unwrap())),
x if x == TypeId::of::<ImaginateController>() => Ok(TaggedValue::ImaginateController(*downcast(input).unwrap())),
x if x == TypeId::of::<Option<Vec<u64>>>() => Ok(TaggedValue::LayerPath(*downcast(input).unwrap())),
x if x == TypeId::of::<DAffine2>() => Ok(TaggedValue::DAffine2(*downcast(input).unwrap())),
x if x == TypeId::of::<LuminanceCalculation>() => Ok(TaggedValue::LuminanceCalculation(*downcast(input).unwrap())),
x if x == TypeId::of::<graphene_core::vector::VectorData>() => Ok(TaggedValue::VectorData(*downcast(input).unwrap())),

View file

@ -207,7 +207,8 @@ pub struct ProtoNode {
pub identifier: ProtoNodeIdentifier,
pub document_node_path: Vec<NodeId>,
pub skip_deduplication: bool,
/// Represents a global state on which the node depends. This is a hack, TODO: figure out a proper solution
// TODO: This is a hack, figure out a proper solution
/// Represents a global state on which the node depends.
pub world_state_hash: u64,
}
@ -412,7 +413,7 @@ impl ProtoNetwork {
}
/// Update all of the references to a node ID in the graph with a new ID named `compose_node_id`.
fn replace_node_id(&mut self, outwards_edges: &HashMap<u64, Vec<u64>>, node_id: u64, compose_node_id: u64, skip_lambdas: bool) {
fn replace_node_id(&mut self, outwards_edges: &HashMap<NodeId, Vec<NodeId>>, node_id: NodeId, compose_node_id: NodeId, skip_lambdas: bool) {
// Update references in other nodes to use the new compose node
if let Some(referring_nodes) = outwards_edges.get(&node_id) {
for &referring_node_id in referring_nodes {

View file

@ -246,7 +246,7 @@ async fn create_compute_pass_descriptor<T: Clone + Pixel + StaticTypeSized>(
]
.into_iter()
.enumerate()
.map(|(i, n)| (i as u64, n))
.map(|(id, node)| (id as NodeId, node))
.collect(),
..Default::default()
};
@ -464,7 +464,7 @@ async fn blend_gpu_image(foreground: ImageFrame<Color>, background: ImageFrame<C
}]
.into_iter()
.enumerate()
.map(|(i, n)| (i as u64, n))
.map(|(id, node)| (id as NodeId, node))
.collect(),
..Default::default()
};

View file

@ -274,7 +274,6 @@ pub async fn imaginate<'a, P: Pixel>(
negative_prompt: impl Future<Output = String>,
adapt_input_image: impl Future<Output = bool>,
image_creativity: impl Future<Output = f32>,
masking_layer: impl Future<Output = Option<Vec<u64>>>,
inpaint: impl Future<Output = bool>,
mask_blur: impl Future<Output = f32>,
mask_starting_fill: impl Future<Output = ImaginateMaskStartingFill>,
@ -305,7 +304,6 @@ pub async fn imaginate<'a, P: Pixel>(
negative_prompt,
adapt_input_image,
image_creativity,
masking_layer,
inpaint,
mask_blur,
mask_starting_fill,
@ -343,7 +341,6 @@ async fn imaginate_maybe_fail<'a, P: Pixel, F: Fn(ImaginateStatus)>(
negative_prompt: impl Future<Output = String>,
adapt_input_image: impl Future<Output = bool>,
image_creativity: impl Future<Output = f32>,
_masking_layer: impl Future<Output = Option<Vec<u64>>>,
_inpaint: impl Future<Output = bool>,
_mask_blur: impl Future<Output = f32>,
_mask_starting_fill: impl Future<Output = ImaginateMaskStartingFill>,

View file

@ -533,7 +533,6 @@ generate_imaginate_node! {
negative_prompt: NegativePrompt: String,
adapt_input_image: AdaptInputImage: bool,
image_creativity: ImageCreativity: f32,
masking_layer: MaskingLayer: Option<Vec<u64>>,
inpaint: Inpaint: bool,
mask_blur: MaskBlur: f32,
mask_starting_fill: MaskStartingFill: ImaginateMaskStartingFill,

View file

@ -605,14 +605,14 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
NodeIOTypes::new(concrete!(()), concrete!(WasmEditorApi), vec![fn_type!(Option<WasmEditorApi>, WasmEditorApi)]),
),
(
ProtoNodeIdentifier::new("graphene_std::raster::ImaginateNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>"),
ProtoNodeIdentifier::new("graphene_std::raster::ImaginateNode<_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _>"),
|args: Vec<graph_craft::proto::SharedNodeContainer>| {
Box::pin(async move {
use graphene_std::raster::ImaginateNode;
macro_rules! instantiate_imaginate_node {
($($i:expr,)*) => { ImaginateNode::new($(graphene_std::any::input_node(args[$i].clone()),)* ) };
}
let node: ImaginateNode<Color, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _> = instantiate_imaginate_node!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,);
let node: ImaginateNode<Color, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _> = instantiate_imaginate_node!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,);
let any = graphene_std::any::DynAnyNode::new(node);
any.into_type_erased()
})
@ -632,7 +632,6 @@ fn node_registry() -> HashMap<ProtoNodeIdentifier, HashMap<NodeIOTypes, NodeCons
fn_type!(String),
fn_type!(bool),
fn_type!(f32),
fn_type!(Option<Vec<u64>>),
fn_type!(bool),
fn_type!(f32),
fn_type!(ImaginateMaskStartingFill),

View file

@ -40,7 +40,9 @@ A message is an enum variant of a certain message sub-type like `FrontendMessage
DocumentMessage::DeleteSelectedLayers
// Carries a layer path and a string as data
DocumentMessage::RenameLayer(Vec<LayerId>, String)
DocumentMessage::DeleteLayer {
id: NodeId,
}
```
Message sub-types hierarchically wrap other message sub-types; for example, `DocumentMessage` is wrapped by `PortfolioMessage` via:
@ -66,3 +68,8 @@ instead of:
```rs
Message(PortfolioMessage::Document(DocumentMessage::DeleteSelectedLayers))
```
And when pushing a message to the queue, we have the `add` and `add_front` functions which call `.into()` for you. Therefore it's as simple as writing:
```rs
responses.add(DocumentMessage::DeleteSelectedLayers.into());
```

View file

@ -25,6 +25,6 @@ To show `trace!()` logs, activate *Help* > *Debug: Print Trace Logs*.
To also view logs of the messages dispatched by the message bus system, activate *Help* > *Debug: Print Messages* > *Only Names*. Or use *Full Contents* for more verbose insight with the actual data being passed. This is an invaluable window into the activity of the message flow and works well together with `debug!()` printouts for tracking down message-related issues.
## Layer paths and document IDs
## Node/layer and document IDs
In debug mode, hover over a layer's name in the Layer Tree panel to view a tooltip with its `u64` path. Likewise, document IDs may be read by hovering over their tabs.
In debug mode, hover over a layer's name in the Layers panel, or a layer/node in the node graph, to view a tooltip with its ID. Likewise, document IDs may be read by hovering over their tabs.