Skip to content

Commit 9ae1363

Browse files
Ayush2k02Keavon
andauthored
Simplify the Layers panel tree structure Rust -> JS encoding (#3744)
* replace custom layer structure encoding with simple JSON tree * add leftover files * Renames --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent bb6fea9 commit 9ae1363

File tree

6 files changed

+48
-183
lines changed

6 files changed

+48
-183
lines changed

editor/src/messages/frontend/frontend_message.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::messages::layout::utility_types::widget_prelude::*;
66
use crate::messages::portfolio::document::node_graph::utility_types::{
77
BoxSelection, ContextMenuInformation, FrontendClickTargets, FrontendGraphInput, FrontendGraphOutput, FrontendNode, FrontendNodeType, NodeGraphErrorDiagnostic, Transform,
88
};
9-
use crate::messages::portfolio::document::utility_types::nodes::{JsRawBuffer, LayerPanelEntry, RawBuffer};
9+
use crate::messages::portfolio::document::utility_types::nodes::{LayerPanelEntry, LayerStructureEntry};
1010
use crate::messages::portfolio::document::utility_types::wires::{WirePath, WirePathUpdate};
1111
use crate::messages::prelude::*;
1212
use glam::IVec2;
@@ -235,12 +235,8 @@ pub enum FrontendMessage {
235235
data: LayerPanelEntry,
236236
},
237237
UpdateDocumentLayerStructure {
238-
#[serde(rename = "dataBuffer")]
239-
data_buffer: RawBuffer,
240-
},
241-
UpdateDocumentLayerStructureJs {
242-
#[serde(rename = "dataBuffer")]
243-
data_buffer: JsRawBuffer,
238+
#[serde(rename = "layerStructure")]
239+
layer_structure: Vec<LayerStructureEntry>,
244240
},
245241
UpdateDocumentRulers {
246242
origin: (f64, f64),

editor/src/messages/portfolio/document/document_message_handler.rs

Lines changed: 21 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use super::node_graph::utility_types::Transform;
33
use super::utility_types::error::EditorError;
44
use super::utility_types::misc::{GroupFolderType, SNAP_FUNCTIONS_FOR_BOUNDING_BOXES, SNAP_FUNCTIONS_FOR_PATHS, SnappingOptions, SnappingState};
55
use super::utility_types::network_interface::{self, NodeNetworkInterface, TransactionStatus};
6-
use super::utility_types::nodes::{CollapsedLayers, SelectedNodes};
6+
use super::utility_types::nodes::{CollapsedLayers, LayerStructureEntry, SelectedNodes};
77
use crate::application::{GRAPHITE_GIT_COMMIT_HASH, generate_uuid};
88
use crate::consts::{ASYMPTOTIC_EFFECT, COLOR_OVERLAY_GRAY, DEFAULT_DOCUMENT_NAME, FILE_EXTENSION, SCALE_EFFECT, SCROLLBAR_SPACING, VIEWPORT_ROTATE_SNAP_INTERVAL};
99
use crate::messages::input_mapper::utility_types::macros::action_shortcut;
@@ -19,7 +19,6 @@ use crate::messages::portfolio::document::properties_panel::properties_panel_mes
1919
use crate::messages::portfolio::document::utility_types::document_metadata::{DocumentMetadata, LayerNodeIdentifier};
2020
use crate::messages::portfolio::document::utility_types::misc::{AlignAggregate, AlignAxis, FlipAxis, PTZ};
2121
use crate::messages::portfolio::document::utility_types::network_interface::{FlowType, InputConnector, NodeTemplate};
22-
use crate::messages::portfolio::document::utility_types::nodes::RawBuffer;
2322
use crate::messages::portfolio::utility_types::{PanelType, PersistentData};
2423
use crate::messages::prelude::*;
2524
use crate::messages::tool::common_functionality::graph_modification_utils::{self, get_blend_mode, get_fill, get_opacity};
@@ -317,8 +316,8 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
317316
DocumentMessage::ClearLayersPanel => {
318317
// Send an empty layer list
319318
if layers_panel_open {
320-
let data_buffer: RawBuffer = Self::default().serialize_root();
321-
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
319+
let layer_structure = Self::default().build_layer_structure(LayerNodeIdentifier::ROOT_PARENT);
320+
responses.add(FrontendMessage::UpdateDocumentLayerStructure { layer_structure });
322321
}
323322

324323
// Clear the control bar
@@ -380,12 +379,12 @@ impl MessageHandler<DocumentMessage, DocumentMessageContext<'_>> for DocumentMes
380379
DocumentMessage::DocumentStructureChanged => {
381380
if layers_panel_open {
382381
self.network_interface.load_structure();
383-
let data_buffer: RawBuffer = self.serialize_root();
382+
let layer_structure = self.build_layer_structure(LayerNodeIdentifier::ROOT_PARENT);
384383

385384
self.update_layers_panel_control_bar_widgets(layers_panel_open, responses);
386385
self.update_layers_panel_bottom_bar_widgets(layers_panel_open, responses);
387386

388-
responses.add(FrontendMessage::UpdateDocumentLayerStructure { data_buffer });
387+
responses.add(FrontendMessage::UpdateDocumentLayerStructure { layer_structure });
389388
}
390389
}
391390
DocumentMessage::DrawArtboardOverlays { context: overlay_context } => {
@@ -1891,70 +1890,22 @@ impl DocumentMessageHandler {
18911890
Ok(document_message_handler)
18921891
}
18931892

1894-
/// Called recursively by the entry function [`serialize_root`].
1895-
fn serialize_structure(&self, folder: LayerNodeIdentifier, structure_section: &mut Vec<u64>, data_section: &mut Vec<u64>, path: &mut Vec<LayerNodeIdentifier>) {
1896-
let mut space = 0;
1897-
for layer_node in folder.children(self.metadata()) {
1898-
data_section.push(layer_node.to_node().0);
1899-
space += 1;
1900-
if layer_node.has_children(self.metadata()) && !self.collapsed.0.contains(&layer_node) {
1901-
path.push(layer_node);
1902-
1903-
// TODO: Skip if folder is not expanded.
1904-
structure_section.push(space);
1905-
self.serialize_structure(layer_node, structure_section, data_section, path);
1906-
space = 0;
1907-
1908-
path.pop();
1909-
}
1910-
}
1911-
structure_section.push(space | (1 << 63));
1912-
}
1913-
1914-
/// Serializes the layer structure into a condensed 1D structure.
1915-
///
1916-
/// # Format
1917-
/// It is a string of numbers broken into three sections:
1918-
///
1919-
/// | Data | Description | Length |
1920-
/// |------------------------------------------------------------------------------------------------------------------------------ |---------------------------------------------------------------|------------------|
1921-
/// | `4,` `2, 1, -2, -0,` `16533113728871998040,3427872634365736244,18115028555707261608,15878401910454357952,449479075714955186` | Encoded example data | |
1922-
/// | _____________________________________________________________________________________________________________________________ | _____________________________________________________________ | ________________ |
1923-
/// | **Length** section: `4` | Length of the **Structure** section (`L` = `structure.len()`) | First value |
1924-
/// | **Structure** section: `2, 1, -2, -0` | The **Structure** section | Next `L` values |
1925-
/// | **Data** section: `16533113728871998040, 3427872634365736244, 18115028555707261608, 15878401910454357952, 449479075714955186` | The **Data** section (layer IDs) | Remaining values |
1926-
///
1927-
/// The data section lists the layer IDs for all folders/layers in the tree as read from top to bottom.
1928-
/// The structure section lists signed numbers. The sign indicates a folder indentation change (`+` is down a level, `-` is up a level).
1929-
/// The numbers in the structure block encode the indentation. For example:
1930-
/// - `2` means read two elements from the data section, then place a `[`.
1931-
/// - `-x` means read `x` elements from the data section and then insert a `]`.
1932-
///
1933-
/// ```text
1934-
/// 2 V 1 V -2 A -0 A
1935-
/// 16533113728871998040,3427872634365736244, 18115028555707261608, 15878401910454357952,449479075714955186
1936-
/// 16533113728871998040,3427872634365736244,[ 18115028555707261608,[15878401910454357952,449479075714955186] ]
1937-
/// ```
1938-
///
1939-
/// Resulting layer panel:
1940-
/// ```text
1941-
/// 16533113728871998040
1942-
/// 3427872634365736244
1943-
/// [3427872634365736244,18115028555707261608]
1944-
/// [3427872634365736244,18115028555707261608,15878401910454357952]
1945-
/// [3427872634365736244,18115028555707261608,449479075714955186]
1946-
/// ```
1947-
pub fn serialize_root(&self) -> RawBuffer {
1948-
let mut structure_section = vec![NodeId(0).0];
1949-
let mut data_section = Vec::new();
1950-
self.serialize_structure(LayerNodeIdentifier::ROOT_PARENT, &mut structure_section, &mut data_section, &mut vec![]);
1951-
1952-
// Remove the ROOT element. Prepend `L`, the length (excluding the ROOT) of the structure section (which happens to be where the ROOT element was).
1953-
structure_section[0] = structure_section.len() as u64 - 1;
1954-
// Append the data section to the end.
1955-
structure_section.extend(data_section);
1956-
1957-
structure_section.as_slice().into()
1893+
/// Recursively builds the layer structure tree for a folder.
1894+
fn build_layer_structure(&self, folder: LayerNodeIdentifier) -> Vec<LayerStructureEntry> {
1895+
folder
1896+
.children(self.metadata())
1897+
.map(|layer_node| {
1898+
let children = if layer_node.has_children(self.metadata()) && !self.collapsed.0.contains(&layer_node) {
1899+
self.build_layer_structure(layer_node)
1900+
} else {
1901+
Vec::new()
1902+
};
1903+
LayerStructureEntry {
1904+
layer_id: layer_node.to_node(),
1905+
children,
1906+
}
1907+
})
1908+
.collect()
19581909
}
19591910

19601911
pub fn undo_with_history(&mut self, viewport: &ViewportMessageHandler, responses: &mut VecDeque<Message>) {

editor/src/messages/portfolio/document/utility_types/nodes.rs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,14 @@ use super::network_interface::NodeNetworkInterface;
33
use crate::messages::tool::common_functionality::graph_modification_utils;
44
use glam::DVec2;
55
use graph_craft::document::{NodeId, NodeNetwork};
6-
use serde::ser::SerializeStruct;
76

7+
/// Represents an entry in the layer tree hierarchy, sent to the frontend.
8+
/// Each entry contains its layer ID and a list of its visible children.
89
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]
9-
pub struct RawBuffer(Vec<u8>);
10-
11-
impl From<&[u64]> for RawBuffer {
12-
fn from(iter: &[u64]) -> Self {
13-
let v_from_raw: Vec<u8> = iter.iter().flat_map(|x| x.to_ne_bytes()).collect();
14-
Self(v_from_raw)
15-
}
16-
}
17-
#[derive(Debug, Clone, serde::Deserialize, PartialEq, Eq, specta::Type)]
18-
pub struct JsRawBuffer(Vec<u8>);
19-
20-
impl From<RawBuffer> for JsRawBuffer {
21-
fn from(buffer: RawBuffer) -> Self {
22-
Self(buffer.0)
23-
}
24-
}
25-
impl serde::Serialize for JsRawBuffer {
26-
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
27-
let mut buffer = serializer.serialize_struct("Buffer", 2)?;
28-
buffer.serialize_field("pointer", &(self.0.as_ptr() as usize))?;
29-
buffer.serialize_field("length", &(self.0.len()))?;
30-
buffer.end()
31-
}
10+
pub struct LayerStructureEntry {
11+
#[serde(rename = "layerId")]
12+
pub layer_id: NodeId,
13+
pub children: Vec<LayerStructureEntry>,
3214
}
3315

3416
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, specta::Type)]

frontend/src/components/panels/Layers.svelte

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@
66
import {
77
patchLayout,
88
UpdateDocumentLayerDetails,
9-
UpdateDocumentLayerStructureJs,
9+
UpdateDocumentLayerStructure,
1010
UpdateLayersPanelControlBarLeftLayout,
1111
UpdateLayersPanelControlBarRightLayout,
1212
UpdateLayersPanelBottomBarLayout,
1313
} from "@graphite/messages";
14-
import type { DataBuffer, LayerPanelEntry, Layout } from "@graphite/messages";
14+
import type { LayerPanelEntry, LayerStructureEntry, Layout } from "@graphite/messages";
1515
import type { NodeGraphState } from "@graphite/state-providers/node-graph";
1616
import type { TooltipState } from "@graphite/state-providers/tooltip";
1717
import { pasteFile } from "@graphite/utility-functions/files";
@@ -91,9 +91,8 @@
9191
layersPanelBottomBarLayout = layersPanelBottomBarLayout;
9292
});
9393
94-
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerStructureJs, (data) => {
95-
const structure = newUpdateDocumentLayerStructure(data.dataBuffer);
96-
rebuildLayerHierarchy(structure);
94+
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerStructure, (data) => {
95+
rebuildLayerHierarchy(data.layerStructure);
9796
});
9897
9998
editor.subscriptions.subscribeJsMessage(UpdateDocumentLayerDetails, (data) => {
@@ -117,7 +116,7 @@
117116
editor.subscriptions.unsubscribeJsMessage(UpdateLayersPanelControlBarLeftLayout);
118117
editor.subscriptions.unsubscribeJsMessage(UpdateLayersPanelControlBarRightLayout);
119118
editor.subscriptions.unsubscribeJsMessage(UpdateLayersPanelBottomBarLayout);
120-
editor.subscriptions.unsubscribeJsMessage(UpdateDocumentLayerStructureJs);
119+
editor.subscriptions.unsubscribeJsMessage(UpdateDocumentLayerStructure);
121120
editor.subscriptions.unsubscribeJsMessage(UpdateDocumentLayerDetails);
122121
123122
removeEventListener("pointerup", draggingPointerUp);
@@ -130,65 +129,6 @@
130129
removeEventListener("keyup", clippingKeyPress);
131130
});
132131
133-
type DocumentLayerStructure = {
134-
layerId: bigint;
135-
children: DocumentLayerStructure[];
136-
};
137-
138-
function newUpdateDocumentLayerStructure(dataBuffer: DataBuffer): DocumentLayerStructure {
139-
const pointerNum = Number(dataBuffer.pointer);
140-
const lengthNum = Number(dataBuffer.length);
141-
142-
const wasmMemoryBuffer = editor.raw.buffer;
143-
144-
// Decode the folder structure encoding
145-
const encoding = new DataView(wasmMemoryBuffer, pointerNum, lengthNum);
146-
147-
// The structure section indicates how to read through the upcoming layer list and assign depths to each layer
148-
const structureSectionLength = Number(encoding.getBigUint64(0, true));
149-
const structureSectionMsbSigned = new DataView(wasmMemoryBuffer, pointerNum + 8, structureSectionLength * 8);
150-
151-
// The layer IDs section lists each layer ID sequentially in the tree, as it will show up in the panel
152-
const layerIdsSection = new DataView(wasmMemoryBuffer, pointerNum + 8 + structureSectionLength * 8);
153-
154-
let layersEncountered = 0;
155-
let currentFolder: DocumentLayerStructure = { layerId: BigInt(-1), children: [] };
156-
const currentFolderStack = [currentFolder];
157-
158-
for (let i = 0; i < structureSectionLength; i += 1) {
159-
const msbSigned = structureSectionMsbSigned.getBigUint64(i * 8, true);
160-
const msbMask = BigInt(1) << BigInt(64 - 1);
161-
162-
// Set the MSB to 0 to clear the sign and then read the number as usual
163-
const numberOfLayersAtThisDepth = msbSigned & ~msbMask;
164-
165-
// Store child folders in the current folder (until we are interrupted by an indent)
166-
for (let j = 0; j < numberOfLayersAtThisDepth; j += 1) {
167-
const layerId = layerIdsSection.getBigUint64(layersEncountered * 8, true);
168-
layersEncountered += 1;
169-
170-
const childLayer: DocumentLayerStructure = { layerId, children: [] };
171-
currentFolder.children.push(childLayer);
172-
}
173-
174-
// Check the sign of the MSB, where a 1 is a negative (outward) indent
175-
const subsequentDirectionOfDepthChange = (msbSigned & msbMask) === BigInt(0);
176-
// Inward
177-
if (subsequentDirectionOfDepthChange) {
178-
currentFolderStack.push(currentFolder);
179-
currentFolder = currentFolder.children[currentFolder.children.length - 1];
180-
}
181-
// Outward
182-
else {
183-
const popped = currentFolderStack.pop();
184-
if (!popped) throw Error("Too many negative indents in the folder structure");
185-
if (popped) currentFolder = popped;
186-
}
187-
}
188-
189-
return currentFolder;
190-
}
191-
192132
function toggleNodeVisibilityLayerPanel(id: bigint) {
193133
editor.handle.toggleNodeVisibilityLayerPanel(id);
194134
}
@@ -515,32 +455,32 @@
515455
dragInPanel = false;
516456
}
517457
518-
function rebuildLayerHierarchy(updateDocumentLayerStructure: DocumentLayerStructure) {
458+
function rebuildLayerHierarchy(layerStructure: LayerStructureEntry[]) {
519459
const layerWithNameBeingEdited = layers.find((layer: LayerListingInfo) => layer.editingName);
520460
const layerIdWithNameBeingEdited = layerWithNameBeingEdited?.entry.id;
521461
522462
// Clear the layer hierarchy before rebuilding it
523463
layers = [];
524464
525465
// Build the new layer hierarchy
526-
const recurse = (folder: DocumentLayerStructure) => {
527-
folder.children.forEach((item, index) => {
466+
const recurse = (children: LayerStructureEntry[]) => {
467+
children.forEach((item, index) => {
528468
const mapping = layerCache.get(String(item.layerId));
529469
if (mapping) {
530470
mapping.id = item.layerId;
531471
layers.push({
532472
folderIndex: index,
533-
bottomLayer: index === folder.children.length - 1,
473+
bottomLayer: index === children.length - 1,
534474
entry: mapping,
535475
editingName: layerIdWithNameBeingEdited === item.layerId,
536476
});
537477
}
538478
539479
// Call self recursively if there are any children
540-
if (item.children.length >= 1) recurse(item);
480+
if (item.children.length >= 1) recurse(item.children);
541481
});
542482
};
543-
recurse(updateDocumentLayerStructure);
483+
recurse(layerStructure);
544484
layers = layers;
545485
}
546486

frontend/src/messages.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -796,13 +796,13 @@ export class TriggerSaveActiveDocument extends JsMessage {
796796

797797
export class DocumentChanged extends JsMessage {}
798798

799-
export type DataBuffer = {
800-
pointer: bigint;
801-
length: bigint;
799+
export type LayerStructureEntry = {
800+
layerId: bigint;
801+
children: LayerStructureEntry[];
802802
};
803803

804-
export class UpdateDocumentLayerStructureJs extends JsMessage {
805-
readonly dataBuffer!: DataBuffer;
804+
export class UpdateDocumentLayerStructure extends JsMessage {
805+
readonly layerStructure!: LayerStructureEntry[];
806806
}
807807

808808
export type TextAlign = "Left" | "Center" | "Right" | "JustifyLeft";
@@ -1717,7 +1717,7 @@ export const messageMakers: Record<string, MessageMaker> = {
17171717
UpdateDocumentArtwork,
17181718
UpdateDocumentBarLayout,
17191719
UpdateDocumentLayerDetails,
1720-
UpdateDocumentLayerStructureJs,
1720+
UpdateDocumentLayerStructure,
17211721
UpdateDocumentRulers,
17221722
UpdateDocumentScrollbars,
17231723
UpdateExportReorderIndex,

frontend/wasm/src/editor_api.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ impl EditorHandle {
154154
}
155155

156156
// Sends a FrontendMessage to JavaScript
157-
fn send_frontend_message_to_js(&self, mut message: FrontendMessage) {
157+
fn send_frontend_message_to_js(&self, message: FrontendMessage) {
158158
if let FrontendMessage::UpdateImageData { ref image_data } = message {
159159
let new_hash = calculate_hash(image_data);
160160
let prev_hash = IMAGE_DATA_HASH.load(Ordering::Relaxed);
@@ -166,10 +166,6 @@ impl EditorHandle {
166166
return;
167167
}
168168

169-
if let FrontendMessage::UpdateDocumentLayerStructure { data_buffer } = message {
170-
message = FrontendMessage::UpdateDocumentLayerStructureJs { data_buffer: data_buffer.into() };
171-
}
172-
173169
let message_type = message.to_discriminant().local_name();
174170

175171
let serializer = serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true);

0 commit comments

Comments
 (0)