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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions editor/src/messages/frontend/frontend_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
use crate::messages::portfolio::document::node_graph::utility_types::{
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, NodeGraphErrorDiagnostic, Transform,
};
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
use crate::messages::portfolio::document::utility_types::nodes::{LayerPanelEntry, LayerStructure};
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
use crate::messages::prelude::*;
use glam::IVec2;
Expand Down Expand Up @@ -236,11 +236,7 @@ pub enum FrontendMessage {
},
UpdateDocumentLayerStructure {
#[serde(rename = "dataBuffer")]
data_buffer: RawBuffer,
},
UpdateDocumentLayerStructureJs {
#[serde(rename = "dataBuffer")]
data_buffer: JsRawBuffer,
data_buffer: Vec<LayerStructure>,
},
UpdateDocumentRulers {
origin: (f64, f64),
Expand Down
73 changes: 13 additions & 60 deletions editor/src/messages/portfolio/document/document_message_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::messages::portfolio::document::properties_panel::properties_panel_mes
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, PTZ};
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate};
use crate::messages::portfolio::document::utility_types::nodes::RawBuffer;
use crate::messages::portfolio::document::utility_types::nodes::LayerStructure;
use crate::messages::portfolio::utility_types::{PanelType, PersistentData};
use crate::messages::prelude::*;
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_blend_mode, get_fill, get_opacity};
Expand Down Expand Up @@ -317,7 +317,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
DocumentMessage::ClearLayersPanel => {
// Send an empty layer list
if layers_panel_open {
let data_buffer: RawBuffer = Self::default().serialize_root();
let data_buffer: Vec<LayerStructure> = Self::default().serialize_root();
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
}

Expand Down Expand Up @@ -380,7 +380,7 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
DocumentMessage::DocumentStructureChanged => {
if layers_panel_open {
self.network_interface.load_structure();
let data_buffer: RawBuffer = self.serialize_root();
let data_buffer: Vec<LayerStructure> = self.serialize_root();

self.update_layers_panel_control_bar_widgets(layers_panel_open, responses);
self.update_layers_panel_bottom_bar_widgets(layers_panel_open, responses);
Expand Down Expand Up @@ -1892,69 +1892,22 @@ impl DocumentMessageHandler {
}

/// Called recursively by the entry function [`serialize_root`].
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure_section: &mut Vec<u64>, data_section: &mut Vec<u64>, path: &mut Vec<LayerNodeIdentifier>) {
let mut space = 0;
fn serialize_structure(&self, folder: LayerNodeIdentifier) -> Vec<LayerStructure> {
let mut children = Vec::new();
for layer_node in folder.children(self.metadata()) {
data_section.push(layer_node.to_node().0);
space += 1;
let id = layer_node.to_node();
let mut layer_structure = LayerStructure { id, children: Vec::new() };
if layer_node.has_children(self.metadata()) && !self.collapsed.0.contains(&layer_node) {
path.push(layer_node);

// TODO: Skip if folder is not expanded.
structure_section.push(space);
self.serialize_structure(layer_node, structure_section, data_section, path);
space = 0;

path.pop();
layer_structure.children = self.serialize_structure(layer_node);
}
children.push(layer_structure);
}
structure_section.push(space | (1 << 63));
children
}

/// Serializes the layer structure into a condensed 1D structure.
///
/// # Format
/// It is a string of numbers broken into three sections:
///
/// | Data | Description | Length |
/// |------------------------------------------------------------------------------------------------------------------------------ |---------------------------------------------------------------|------------------|
/// | `4,` `2, 1, -2, -0,` `16533113728871998040,3427872634365736244,18115028555707261608,15878401910454357952,449479075714955186` | Encoded example data | |
/// | _____________________________________________________________________________________________________________________________ | _____________________________________________________________ | ________________ |
/// | **Length** section: `4` | Length of the **Structure** section (`L` = `structure.len()`) | First value |
/// | **Structure** section: `2, 1, -2, -0` | The **Structure** section | Next `L` values |
/// | **Data** section: `16533113728871998040, 3427872634365736244, 18115028555707261608, 15878401910454357952, 449479075714955186` | The **Data** section (layer IDs) | Remaining values |
///
/// The data section lists the layer IDs for all folders/layers in the tree as read from top to bottom.
/// The structure section lists signed numbers. The sign indicates a folder indentation change (`+` is down a level, `-` is up a level).
/// The numbers in the structure block encode the indentation. For example:
/// - `2` means read two elements from the data section, then place a `[`.
/// - `-x` means read `x` elements from the data section and then insert a `]`.
///
/// ```text
/// 2 V 1 V -2 A -0 A
/// 16533113728871998040,3427872634365736244, 18115028555707261608, 15878401910454357952,449479075714955186
/// 16533113728871998040,3427872634365736244,[ 18115028555707261608,[15878401910454357952,449479075714955186] ]
/// ```
///
/// Resulting layer panel:
/// ```text
/// 16533113728871998040
/// 3427872634365736244
/// [3427872634365736244,18115028555707261608]
/// [3427872634365736244,18115028555707261608,15878401910454357952]
/// [3427872634365736244,18115028555707261608,449479075714955186]
/// ```
pub fn serialize_root(&self) -> RawBuffer {
let mut structure_section = vec![NodeId(0).0];
let mut data_section = Vec::new();
self.serialize_structure(LayerNodeIdentifier::ROOT_PARENT, &mut structure_section, &mut data_section, &mut vec![]);

// Remove the ROOT element. Prepend `L`, the length (excluding the ROOT) of the structure section (which happens to be where the ROOT element was).
structure_section[0] = structure_section.len() as u64 - 1;
// Append the data section to the end.
structure_section.extend(data_section);

structure_section.as_slice().into()
/// Serializes the layer structure into a condensed tree structure.
pub fn serialize_root(&self) -> Vec<LayerStructure> {
self.serialize_structure(LayerNodeIdentifier::ROOT_PARENT)
}

pub fn undo_with_history(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) {
Expand Down
27 changes: 4 additions & 23 deletions editor/src/messages/portfolio/document/utility_types/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,10 @@ use graph_craft::document::{NodeId, NodeNetwork};
use serde::ser::SerializeStruct;

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct RawBuffer(Vec<u8>);

impl From<&[u64]> for RawBuffer {
fn from(iter: &[u64]) -> Self {
let v_from_raw: Vec<u8> = iter.iter().flat_map(|x| x.to_ne_bytes()).collect();
Self(v_from_raw)
}
}
#[derive(Debug, Clone, serde::Deserialize, PartialEq, Eq, specta::Type)]
pub struct JsRawBuffer(Vec<u8>);

impl From<RawBuffer> for JsRawBuffer {
fn from(buffer: RawBuffer) -> Self {
Self(buffer.0)
}
}
impl serde::Serialize for JsRawBuffer {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut buffer = serializer.serialize_struct("Buffer", 2)?;
buffer.serialize_field("pointer", &(self.0.as_ptr() as usize))?;
buffer.serialize_field("length", &(self.0.len()))?;
buffer.end()
}
pub struct LayerStructure {
#[serde(rename = "layerId")]
pub id: NodeId,
pub children: Vec<LayerStructure>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
Expand Down
82 changes: 11 additions & 71 deletions frontend/src/components/panels/Layers.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
import {
patchLayout,
UpdateDocumentLayerDetails,
UpdateDocumentLayerStructureJs,
UpdateDocumentLayerStructure,
UpdateLayersPanelControlBarLeftLayout,
UpdateLayersPanelControlBarRightLayout,
UpdateLayersPanelBottomBarLayout,
} from "@graphite/messages";
import type { DataBuffer, LayerPanelEntry, Layout } from "@graphite/messages";
import type { LayerPanelEntry, Layout, LayerStructure } from "@graphite/messages";
import type { NodeGraphState } from "@graphite/state-providers/node-graph";
import type { TooltipState } from "@graphite/state-providers/tooltip";
import { pasteFile } from "@graphite/utility-functions/files";
Expand Down Expand Up @@ -91,9 +91,8 @@
layersPanelBottomBarLayout = layersPanelBottomBarLayout;
});

editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerStructureJs, (data) => {
const structure = newUpdateDocumentLayerStructure(data.dataBuffer);
rebuildLayerHierarchy(structure);
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerStructure, (data) => {
rebuildLayerHierarchy(data.dataBuffer);
});

editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerDetails, (data) => {
Expand All @@ -117,7 +116,7 @@
editor.subscriptions.unsubscribeJsMessage(UpdateLayersPanelControlBarLeftLayout);
editor.subscriptions.unsubscribeJsMessage(UpdateLayersPanelControlBarRightLayout);
editor.subscriptions.unsubscribeJsMessage(UpdateLayersPanelBottomBarLayout);
editor.subscriptions.unsubscribeJsMessage(UpdateDocumentLayerStructureJs);
editor.subscriptions.unsubscribeJsMessage(UpdateDocumentLayerStructure);
editor.subscriptions.unsubscribeJsMessage(UpdateDocumentLayerDetails);

removeEventListener("pointerup", draggingPointerUp);
Expand All @@ -130,65 +129,6 @@
removeEventListener("keyup", clippingKeyPress);
});

type DocumentLayerStructure = {
layerId: bigint;
children: DocumentLayerStructure[];
};

function newUpdateDocumentLayerStructure(dataBuffer: DataBuffer): DocumentLayerStructure {
const pointerNum = Number(dataBuffer.pointer);
const lengthNum = Number(dataBuffer.length);

const wasmMemoryBuffer = editor.raw.buffer;

// Decode the folder structure encoding
const encoding = new DataView(wasmMemoryBuffer, pointerNum, lengthNum);

// The structure section indicates how to read through the upcoming layer list and assign depths to each layer
const structureSectionLength = Number(encoding.getBigUint64(0, true));
const structureSectionMsbSigned = new DataView(wasmMemoryBuffer, pointerNum + 8, structureSectionLength * 8);

// The layer IDs section lists each layer ID sequentially in the tree, as it will show up in the panel
const layerIdsSection = new DataView(wasmMemoryBuffer, pointerNum + 8 + structureSectionLength * 8);

let layersEncountered = 0;
let currentFolder: DocumentLayerStructure = { layerId: BigInt(-1), children: [] };
const currentFolderStack = [currentFolder];

for (let i = 0; i < structureSectionLength; i += 1) {
const msbSigned = structureSectionMsbSigned.getBigUint64(i * 8, true);
const msbMask = BigInt(1) << BigInt(64 - 1);

// Set the MSB to 0 to clear the sign and then read the number as usual
const numberOfLayersAtThisDepth = msbSigned & ~msbMask;

// Store child folders in the current folder (until we are interrupted by an indent)
for (let j = 0; j < numberOfLayersAtThisDepth; j += 1) {
const layerId = layerIdsSection.getBigUint64(layersEncountered * 8, true);
layersEncountered += 1;

const childLayer: DocumentLayerStructure = { layerId, children: [] };
currentFolder.children.push(childLayer);
}

// Check the sign of the MSB, where a 1 is a negative (outward) indent
const subsequentDirectionOfDepthChange = (msbSigned & msbMask) === BigInt(0);
// Inward
if (subsequentDirectionOfDepthChange) {
currentFolderStack.push(currentFolder);
currentFolder = currentFolder.children[currentFolder.children.length - 1];
}
// Outward
else {
const popped = currentFolderStack.pop();
if (!popped) throw Error("Too many negative indents in the folder structure");
if (popped) currentFolder = popped;
}
}

return currentFolder;
}

function toggleNodeVisibilityLayerPanel(id: bigint) {
editor.handle.toggleNodeVisibilityLayerPanel(id);
}
Expand Down Expand Up @@ -515,32 +455,32 @@
dragInPanel = false;
}

function rebuildLayerHierarchy(updateDocumentLayerStructure: DocumentLayerStructure) {
function rebuildLayerHierarchy(layerStructure: LayerStructure[]) {
const layerWithNameBeingEdited = layers.find((layer: LayerListingInfo) => layer.editingName);
const layerIdWithNameBeingEdited = layerWithNameBeingEdited?.entry.id;

// Clear the layer hierarchy before rebuilding it
layers = [];

// Build the new layer hierarchy
const recurse = (folder: DocumentLayerStructure) => {
folder.children.forEach((item, index) => {
const recurse = (list: LayerStructure[]) => {
list.forEach((item, index) => {
const mapping = layerCache.get(String(item.layerId));
if (mapping) {
mapping.id = item.layerId;
layers.push({
folderIndex: index,
bottomLayer: index === folder.children.length - 1,
bottomLayer: index === list.length - 1,
entry: mapping,
editingName: layerIdWithNameBeingEdited === item.layerId,
});
}

// Call self recursively if there are any children
if (item.children.length >= 1) recurse(item);
if (item.children.length >= 1) recurse(item.children);
});
};
recurse(updateDocumentLayerStructure);
recurse(layerStructure);
layers = layers;
}

Expand Down
17 changes: 10 additions & 7 deletions frontend/src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -796,13 +796,16 @@ export class TriggerSaveActiveDocument extends JsMessage {

export class DocumentChanged extends JsMessage {}

export type DataBuffer = {
pointer: bigint;
length: bigint;
};
export class LayerStructure {
readonly layerId!: bigint;

@Type(() => LayerStructure)
readonly children!: LayerStructure[];
}

export class UpdateDocumentLayerStructureJs extends JsMessage {
readonly dataBuffer!: DataBuffer;
export class UpdateDocumentLayerStructure extends JsMessage {
@Type(() => LayerStructure)
readonly dataBuffer!: LayerStructure[];
}

export type TextAlign = "Left" | "Center" | "Right" | "JustifyLeft";
Expand Down Expand Up @@ -1717,7 +1720,7 @@ export const messageMakers: Record<string, MessageMaker> = {
UpdateDocumentArtwork,
UpdateDocumentBarLayout,
UpdateDocumentLayerDetails,
UpdateDocumentLayerStructureJs,
UpdateDocumentLayerStructure,
UpdateDocumentRulers,
UpdateDocumentScrollbars,
UpdateExportReorderIndex,
Expand Down
4 changes: 0 additions & 4 deletions frontend/wasm/src/editor_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,6 @@ impl EditorHandle {
return;
}

if let FrontendMessage::UpdateDocumentLayerStructure { data_buffer } = message {
message = FrontendMessage::UpdateDocumentLayerStructureJs { data_buffer: data_buffer.into() };
}

let message_type = message.to_discriminant().local_name();

let serializer = serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true);
Expand Down
Loading