UNPKG

@ndbx/runtime

Version:

The `@ndbx/runtime` package provides a runtime environment to embed NodeBox visualizations directly into React applications. NodeBox is a powerful tool for creating interactive and generative visualizations, and this runtime allows you to integrate those

228 lines (200 loc) 7.51 kB
import JSON5 from "json5"; import { Context, FunctionItem, Project, PortType, ParameterType } from "../src"; import { createProject, createNetwork, createNode } from "../src/mutation"; import { parseNodeStatements } from "../src/loaders"; import { Color } from "@ndbx/g"; export function createFunctionItem({ name, category, description, source, }: { name: string; category: string; description: string; source: string; }): FunctionItem { const { parameters, sections, inputPorts, outputPorts } = parseNodeStatements(source.split("\n")); return { type: "FUNCTION", id: name, name, category, description, inputPorts, outputPorts, parameters, sections, source, width: 1000, height: 1000, background: Color.black(), }; } export function createValueFn(): FunctionItem { const source = ` export default function(node) { const valueIn = node.numberIn({ name: "value" }); const attributeIn = node.stringIn({ name: "attribute", value: "value" }); const tableOut = node.tableOut({ name: "out" }); node.onRender = () => { const table = [{ [attributeIn.value]: valueIn.value }]; tableOut.set(table); }; } `; return createFunctionItem({ name: "value", category: "Math", description: "Generates a simple value", source }); } export function createAddFn(): FunctionItem { const source = ` import { tableFromArrays, makeVector } from 'https://esm.sh/apache-arrow'; export default function(node) { const tableIn = node.tableIn({ name: "table" }); const attribute1In = node.stringIn({ name: "attribute", defaultValue: "value" }); const attribute2In = node.stringIn({ name: "attribute", defaultValue: "value" }); const targetAttributeIn = node.stringIn({ name: "targetAttribute", defaultValue: "value" }); const tableOut = node.tableOut({ name: "out" }); node.onRender = () => { const [table, attribute1, attribute2, targetAttribute] = [tableIn.value, attribute1In.value, attribute2In.value, targetAttributeIn.value]; const vector1 = table.getChild(attribute1); const vector2 = table.getChild(attribute2); const vectorOut = makeVector(new Float64Array(table.numRows)); for (let i = 0; i < table.numRows; i++) { const v1 = i < vector1.length ? vector1.get(i) : null; const v2 = i < vector2.length ? vector2.get(i) : null; if (typeof v1 !== "number" || typeof v2 !== "number") { vectorOut.set(i, null); } else { vectorOut.set(i, v1 + v2); } } const tableMap = table.schema.fields.reduce((acc, field) => { acc[field.name] = table.getChild(field.name); return acc; }, {}); tableMap[targetAttribute] = vectorOut; tableOut.set(makeTable(tableMap)); }; } `; return { type: "FUNCTION", id: "add", name: "add", category: "Math", description: "Adds two columns", inputPorts: [{ type: PortType.Table, name: "table" }], outputPorts: [{ type: PortType.Table, name: "out" }], parameters: [ { type: ParameterType.String, name: "attribute1", label: "Attribute 1", defaultValue: "value" }, { type: ParameterType.String, name: "attribute2", label: "Attribute 2", defaultValue: "value" }, { type: ParameterType.String, name: "targetAttribute", label: "Target Attribute", defaultValue: "value" }, ], source, }; } export function createNegateFn(): FunctionItem { const source = ` export default function(node) { const tableIn = node.tableIn({ name: "table" }); const attributeIn = node.stringIn({ name: "attribute", value: "value" }); const targetAttributeIn = node.stringIn({ name: "targetAttribute", value: "value" }); const tableOut = node.tableOut({ name: "out" }); node.onRender = () => { const [table, attribute, targetAttribute] = [tableIn.value, attributeIn.value, targetAttributeIn.value]; const newTable = []; for (let i = 0; i < table.length; i++) { const row = table[i]; const v = row[attribute]; if (typeof v === "number") { newTable.push({...row, [targetAttribute]: -v}); } else { newTable.push({...row, [targetAttribute]: null}); } } tableOut.set(newTable); }; } `; return createFunctionItem({ name: "negate", category: "Math", description: "Negates a column", source }); } export function createMakeNumbersFn(): FunctionItem { const source = ` import { tableFromArrays, makeVector } from 'https://esm.sh/apache-arrow'; export default function(node) { const templateIn = node.stringIn({ name: "template", defaultValue: "11;22;33" }); const targetAttributeIn = node.stringIn({ name: "targetAttribute", defaultValue: "value" }); const tableOut = node.tableOut({ name: "out" }); node.onRender = () => { const [template, targetAttribute] = [templateIn.value, targetAttributeIn.value]; const array = template.split(";").map(x => parseFloat(x)); const table = tableFromArrays({ [targetAttribute]: array }); tableOut.set(table); }; } `; return { type: "FUNCTION", id: "make-numbers", name: "make-numbers", category: "Math", description: "Parse numbers from a string", inputPorts: [], outputPorts: [{ type: PortType.Table, name: "out" }], parameters: [ { type: ParameterType.String, name: "template", label: "Template", defaultValue: "11;22;33" }, { type: ParameterType.String, name: "targetAttribute", label: "Target Attribute", defaultValue: "value" }, ], source, }; } export function createRectFn(): FunctionItem { const source = ` import { Rect, Group } from "@ndbx/g"; export default function(node) { const xIn = node.numberIn({ name: "x", value: 0 }); const yIn = node.numberIn({ name: "y", value: 0 }); const widthIn = node.numberIn({ name: "width", value: 100, min: 0 }); const heightIn = node.numberIn({ name: "height", value: 100, min: 0 }); const fillIn = node.colorIn({ name: "fill", value: "black" }); const shapeOut = node.shapeOut({ name: "Out" }); node.onRender = () => { const rect = new Rect(xIn.value, yIn.value, widthIn.value, heightIn.value); rect.fill = fillIn.value; shapeOut.set(rect); }; } `; return createFunctionItem({ name: "rect", category: "Graphics", description: "Draws a rectangle", source }); } export function createMathProject(): Project { const project = createProject("math"); project.items.push(createAddFn()); project.items.push(createNegateFn()); project.items.push(createValueFn()); project.items.push(createMakeNumbersFn()); return project; } export function createGraphicsProject(): Project { const project = createProject("g"); project.items.push(createRectFn()); return project; } export function createTestContext() { const project = createProject("test"); const cx = new Context( project, new Map(), new Map([ ["test/math", createMathProject()], ["test/g", createGraphicsProject()], ]), ); const network = createNetwork(cx, project, "test"); // plan = createNetwork(plan, "test"); // plan = createNode(plan, "test", "math.add"); // for (const project of plan.dependencies) { // project.functions.forEach((fn) => loadFunction(plan, project, fn)); // } return { cx, project, network }; }