sophia_controller/nodeeditor/NodeSerializer.js

224 lines
7.5 KiB
JavaScript

import { Node, ServoNode, VariableNode, CurveNode, MathNode, MapNode, NoiseNode } from './Node.js';
export const NODE_TYPES = {
Node: 0x01,
Servo: 0x02,
Curve: 0x03,
Noise: 0x04,
Variable: 0x05,
Math: 0x06,
Map: 0x07
};
function GetNodeType(node) {
//console.log(node.constructor.name);
switch (node.constructor.name) {
case "ServoNode":
return NODE_TYPES.Servo;
case "CurveNode":
return NODE_TYPES.Curve;
case "NoiseNode":
return NODE_TYPES.Noise;
case "VariableNode":
return NODE_TYPES.Variable;
case "MathNode":
return NODE_TYPES.Math;
case "MapNode":
return NODE_TYPES.Map;
default:
return NODE_TYPES.Node;
}
}
export function encodeNodeGraph(nodes, connections) {
const bufferSize = 1024; // adjust based on expected graph size
const buffer = new ArrayBuffer(bufferSize);
const view = new DataView(buffer);
let offset = 0;
// Node count (1 byte)
view.setUint8(offset++, nodes.length);
//console.log(ndoes);
// Encode nodes
nodes.forEach((node, index) => {
node.id = index;
node.type = GetNodeType(node);
view.setUint8(offset++, node.type); // Node type
view.setUint8(offset++, node.id); // Node ID
view.setUint16(offset, node.x, true); offset += 2;
view.setUint16(offset, node.y, true); offset += 2;
switch (node.type) {
case NODE_TYPES.Servo:
view.setUint8(offset++, node.controls[0].getValue());
break;
case NODE_TYPES.Curve:
view.setUint8(offset++, node.controls[0].getValue());
break;
case NODE_TYPES.Noise:
view.setFloat32(offset, node.controls[0].getValue(), true); offset += 4;
view.setUint16(offset, node.controls[1].getValue()); offset += 2;
break;
case NODE_TYPES.Variable:
view.setUint8(offset++, node.controls[0].getValue());
view.setUint8(offset++, node.controls[1].getValue());
break;
case NODE_TYPES.Math:
view.setUint8(offset++, node.controls[0].getValue());
view.setFloat32(offset, node.controls[1].getValue(), true); offset += 4;
break;
case NODE_TYPES.Map:
view.setFloat32(offset, node.controls[0].getValue(), true); offset += 4;
view.setFloat32(offset, node.controls[1].getValue(), true); offset += 4;
view.setFloat32(offset, node.controls[2].getValue(), true); offset += 4;
view.setFloat32(offset, node.controls[3].getValue(), true); offset += 4;
//console.log(node.inMinInput.numericValue, node.inMaxInput.numericValue, node.outMinInput.numericValue, node.outMaxInput.numericValue);
break;
default:
console.warn("Unknown node type:", node);
}
});
// Connection count (1 byte)
view.setUint8(offset++, connections.length);
// Encode connections
connections.forEach(conn => {
view.setUint8(offset++, conn.from.id);
view.setUint8(offset++, conn.to.id);
});
// Slice the buffer to actual used size
return new Uint8Array(buffer.slice(0, offset));
}
export function loadFromBinary(editor, data) {
let nodes = []
let connections = []
const view = new DataView(data.buffer);
let offset = 0;
const nodeCount = view.getUint8(offset++);
const idMap = {}; // Map node IDs to actual node instances
for (let i = 0; i < nodeCount; i++) {
const type = view.getUint8(offset++);
const id = view.getUint8(offset++);
const x = view.getUint16(offset, true); offset += 2;
const y = view.getUint16(offset, true); offset += 2;
let node = null;
switch (type) {
case NODE_TYPES.Servo: {
const motorID = view.getUint8(offset++);
const newNode = new ServoNode(x, y, motorID);
editor.nodes.push(newNode);
node = newNode;
break;
}
case NODE_TYPES.Curve: {
const curveID = view.getUint8(offset++);
const newNode = new CurveNode(x, y, curveID);
editor.nodes.push(newNode);
node = newNode;
break;
}
case NODE_TYPES.Noise: {
const frequency = view.getFloat32(offset, true); offset += 4;
const seed = view.getUint16(offset, true); offset += 2;
const newNode = new NoiseNode(x, y);
editor.nodes.push(newNode);
node = newNode;
node.controls[0].text = String(frequency);
node.controls[1].text = String(seed);
break;
}
case NODE_TYPES.Variable: {
const source = view.getUint8(offset++);
const arg0 = view.getUint8(offset++);
const newNode = new VariableNode(x, y);
editor.nodes.push(newNode);
node = newNode;
node.controls[0].selectedIndex = source;
node.controls[1].value = arg0;
break;
}
case NODE_TYPES.Math: {
const op = view.getUint8(offset++);
const value = view.getFloat32(offset, true); offset += 4;
const newNode = new MathNode(x, y);
editor.nodes.push(newNode);
node = newNode;
node.controls[0].selectedIndex = op;
node.controls[1].text = String(value);
break;
}
case NODE_TYPES.Map: {
const inMin = view.getFloat32(offset, true); offset += 4;
const inMax = view.getFloat32(offset, true); offset += 4;
const outMin = view.getFloat32(offset, true); offset += 4;
const outMax = view.getFloat32(offset, true); offset += 4;
const newNode = new MapNode(x, y);
editor.nodes.push(newNode);
node = newNode;
node.controls[0].text = String(inMin);
node.controls[1].text = String(inMax);
node.controls[2].text = String(outMin);
node.controls[3].text = String(outMax);
break;
}
default: {
console.error("UNKNOWN NODE");
break;
}
}
if (node) {
node.id = id;
idMap[id] = node;
}
//editor.nodes.push(node);
}
//console.log(this.getNodeByID(0));
// 🔗 Load connections
const connectionCount = view.getUint8(offset++);
for (let i = 0; i < connectionCount; i++) {
const fromID = view.getUint8(offset++);
const toID = view.getUint8(offset++);
const fromNode = idMap[fromID];
const toNode = idMap[toID];
if (fromNode && toNode) {
editor.connections.push({ from: fromNode, to: toNode });
}
}
// Reencode all positions to SIGNED ints
editor.nodes.forEach(node => {
node.x = (node.x << 16) >> 16;
node.y = (node.y << 16) >> 16;
});
//editor.nodes = nodes;
//editor.connections = connections;
console.log(editor.nodes);
console.log(editor.connections);
editor.draw();
//this._redraw();
}