From f9505d783767d0aa6aad6bbdb1d6cc52b2b2f3d6 Mon Sep 17 00:00:00 2001 From: Adaline Simonian Date: Wed, 31 Jan 2024 17:48:47 -0800 Subject: [PATCH] refactor: reduce size of node positions in url --- src/providers/app-state.ts | 27 +++++------------------- src/rendering/node-graph.ts | 17 +++++++++------ src/types/index.ts | 1 + src/types/node-positions.ts | 41 +++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 28 deletions(-) create mode 100644 src/types/node-positions.ts diff --git a/src/providers/app-state.ts b/src/providers/app-state.ts index df850b0..08ed3de 100644 --- a/src/providers/app-state.ts +++ b/src/providers/app-state.ts @@ -1,6 +1,5 @@ import { Article } from './api-client.js'; -import { Dictionary, Vector2D } from '../types/index.js'; -import * as msgpack from '@msgpack/msgpack'; +import { Dictionary, NodePositions, Vector2D } from '../types/index.js'; /** * Manages state for the application. Allows for state to be shared between @@ -35,7 +34,7 @@ export class AppState { /** * The node positions for the current graph view. */ - nodePositions?: { [id: string]: Vector2D }; + nodePositions?: NodePositions; /** * State of keyboard modifiers. @@ -93,12 +92,7 @@ export class AppState { if (this.debug) addParam('debug', 'true'); if (all && this.nodePositions) { - const nodePositions: { [id: string]: { x: number; y: number } } = {}; - for (const [key, value] of Object.entries(this.nodePositions)) { - nodePositions[key] = { x: value.x, y: value.y }; - } - const encoded = msgpack.encode(nodePositions); - addParam('nodePositions', btoa(String.fromCharCode(...encoded))); + addParam('nodePositions', this.nodePositions.toString()); } return params.toString(); @@ -122,7 +116,7 @@ export class AppState { try { state[key] = valueFn(value); } catch (e) { - console.warn(`Failed to deserialize ${key}: ${e}`); + console.warn(`Failed to deserialize ${key}:`, e); } }; @@ -131,18 +125,7 @@ export class AppState { trySet('zoomLevel', (value) => Number.parseInt(value, 10)); trySet('translation', (value) => Vector2D.parse(value)); trySet('debug', (value) => value === 'true'); - trySet('nodePositions', (value) => { - const decoded = msgpack.decode( - Uint8Array.from(atob(value), (c) => c.charCodeAt(0)), - ); - const nodePositions: { [id: string]: Vector2D } = {}; - for (const [key, value] of Object.entries( - decoded as Record, - )) { - nodePositions[key] = new Vector2D(value.x, value.y); - } - return nodePositions; - }); + trySet('nodePositions', (value) => NodePositions.fromString(value)); return state; } diff --git a/src/rendering/node-graph.ts b/src/rendering/node-graph.ts index 8512acd..b0fbc1f 100644 --- a/src/rendering/node-graph.ts +++ b/src/rendering/node-graph.ts @@ -7,7 +7,12 @@ import { ScopedAppStateManager, } from '../providers/index.js'; import { NodeSelection } from './node-selection.js'; -import { IndexedSet, TwoKeyMap, Vector2D } from '../types/index.js'; +import { + IndexedSet, + NodePositions, + TwoKeyMap, + Vector2D, +} from '../types/index.js'; import { GraphBehaviourManager, GraphDebugBehaviour, @@ -156,11 +161,11 @@ export class NodeGraph { }); this.#appStateManager.on('request-node-positions', () => { - const nodePositions: { [id: number]: Vector2D } = {}; + const nodePositions = new NodePositions(); this.#simulation.nodes().forEach((node) => { - nodePositions[node.id] = new Vector2D( - Math.round(node.x!), - Math.round(node.y!), + nodePositions.set( + node.id, + new Vector2D(Math.round(node.x!), Math.round(node.y!)), ); }); this.#appStateManager.set('nodePositions', nodePositions); @@ -252,7 +257,7 @@ export class NodeGraph { let missed = false; for (const node of this.#simulation.nodes()) { - const position = nodePositions[node.id]; + const position = nodePositions.get(node.id); if (position) { node.x = position.x; node.y = position.y; diff --git a/src/types/index.ts b/src/types/index.ts index d18ac5f..cc0f0a0 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,7 @@ export * from './dictionary.js'; export * from './indexed-set.js'; export * from './inflection-tag.js'; +export * from './node-positions.js'; export * from './graphql-error.js'; export * from './rect-2d.js'; export * from './two-key-map.js'; diff --git a/src/types/node-positions.ts b/src/types/node-positions.ts new file mode 100644 index 0000000..957c999 --- /dev/null +++ b/src/types/node-positions.ts @@ -0,0 +1,41 @@ +import { Vector2D } from './vector-2d.js'; +import { encode, decode } from '@msgpack/msgpack'; + +/** + * Data type for storing node positions. + */ +export class NodePositions extends Map { + /** + * Converts a NodePositions object to a string. + */ + toString(): string { + const flatPositions: number[] = []; + + for (const [id, { x, y }] of this) { + flatPositions.push(id, x, y); + } + + const encoded = encode(flatPositions); + + console.log(JSON.stringify(flatPositions)); + + return btoa(String.fromCharCode(...encoded)); + } + + /** + * Converts a string to a NodePositions object. + */ + static fromString(str: string): NodePositions { + const decoded = decode( + Uint8Array.from(atob(str), (c) => c.charCodeAt(0)), + ) as number[]; + + const positions = new NodePositions(); + + for (let i = 0; i < decoded.length; i += 3) { + positions.set(decoded[i], new Vector2D(decoded[i + 1], decoded[i + 2])); + } + + return positions; + } +}