From 1db01eaf7f1a0e06abe689c55f746518598d5f4c Mon Sep 17 00:00:00 2001 From: Sam Nystrom Date: Thu, 7 Mar 2024 21:47:56 -0500 Subject: Begin vectorizing the Math node --- src/nodes/Math.tsx | 144 +++++++++++++++++++++++++---------------------------- src/nodes/Plot.tsx | 2 +- src/nodes/index.ts | 1 + src/wasm.ts | 3 +- 4 files changed, 72 insertions(+), 78 deletions(-) (limited to 'src') diff --git a/src/nodes/Math.tsx b/src/nodes/Math.tsx index ec11277..a10aeaa 100644 --- a/src/nodes/Math.tsx +++ b/src/nodes/Math.tsx @@ -1,3 +1,4 @@ +import { mathS, mathV, mathSS, mathSV, mathVS, mathVV } from '../wasm.ts'; import { NodeShell, InputNumber, InputSelect, OutputNumber, NodeComponentProps, NodeInfo } from '../node.tsx'; export enum MathOpFunc { @@ -5,7 +6,7 @@ export enum MathOpFunc { Sub = 'Subtract', Mul = 'Multiply', Div = 'Divide', - Power = 'Power', + Pow = 'Power', Log = 'Logarithm', Sqrt = 'Square Root', Exp = 'Exponent', @@ -13,8 +14,8 @@ export enum MathOpFunc { export enum MathOpCmp { Min = 'Minimum', Max = 'Maximum', - Less = 'Less Than', - Greater = 'Greater Than', + Lt = 'Less Than', + Gt = 'Greater Than', Sign = 'Sign', } export enum MathOpRound { @@ -48,6 +49,52 @@ export enum MathOpConv { export const MathOp = { ...MathOpFunc, ...MathOpCmp, ...MathOpRound, ...MathOpTrig, ...MathOpConv }; export type MathOp = typeof MathOp; +const binaryOps = { + [MathOp.Add]: (a, b) => a + b, + [MathOp.Sub]: (a, b) => a - b, + [MathOp.Mul]: (a, b) => a * b, + [MathOp.Div]: (a, b) => a / b, + [MathOp.Pow]: (a, b) => a ** b, + [MathOp.Log]: (a, b) => Math.log(b) / Math.log(a), + + [MathOp.Max]: Math.max, + [MathOp.Min]: Math.min, + [MathOp.Lt]: (a, b) => a < b, + [MathOp.Gt]: (a, b) => a > b, + + [MathOp.Mod]: (a, b) => a % b, + [MathOp.Snap]: (a, b) => Math.round(a / b) * b, + + [MathOp.Atan2]: Math.atan2, +}; + +const unaryOps = { + [MathOp.Sqrt]: Math.sqrt, + [MathOp.Exp]: Math.exp, + + [MathOp.Sign]: Math.sign, + + [MathOp.Round]: Math.round, + [MathOp.Floor]: Math.floor, + [MathOp.Ceil]: Math.ceil, + [MathOp.Trunc]: Math.trunc, + [MathOp.Frac]: x => x - Math.trunc(x), + [MathOp.Clamp]: x => Math.max(0, Math.min(x, 1)), + + [MathOp.Sin]: Math.sin, + [MathOp.Cos]: Math.cos, + [MathOp.Tan]: Math.tan, + [MathOp.Asin]: Math.asin, + [MathOp.Acos]: Math.acos, + [MathOp.Atan]: Math.atan, + [MathOp.Sinh]: Math.sinh, + [MathOp.Cosh]: Math.cosh, + [MathOp.Tanh]: Math.tanh, + + [MathOp.ToRad]: x => x / 180 * Math.PI, + [MathOp.ToDeg]: x => x * 180 / Math.PI, +}; + export interface MathInputs { op: MathOp, a: number | Float32Array, @@ -55,7 +102,7 @@ export interface MathInputs { } export interface MathOutputs { - out: boolean | number | Float32Array, + out: number | Float32Array, } export const MathC = ({ id, x, y, inputs }: NodeComponentProps) => { @@ -66,87 +113,32 @@ export const MathC = ({ id, x, y, inputs }: NodeComponentProps) => { 'Trigonometric': Object.values(MathOpTrig), 'Conversion': Object.values(MathOpConv), }; + const isBinary = Object.keys(binaryOps).includes(inputs.op.value); return ( - - + + {isBinary && } ); }; -const doMathOp = (op: MathOp, a: number, b: number): number => { - switch (op) { - case MathOp.Add: return a + b; - case MathOp.Sub: return a - b; - case MathOp.Mul: return a * b; - case MathOp.Div: return a / b; - case MathOp.Power: return a ** b; - case MathOp.Log: return Math.log(a) / Math.log(b); - case MathOp.Sqrt: return Math.sqrt(a); - case MathOp.Exp: return Math.exp(a); - - case MathOp.Min: return Math.min(a, b); - case MathOp.Max: return Math.max(a, b); - case MathOp.Less: return a < b; - case MathOp.Greater: return a > b; - case MathOp.Sign: return Math.sign(a); - - case MathOp.Round: return Math.round(a); - case MathOp.Floor: return Math.floor(a); - case MathOp.Ceil: return Math.ceil(a); - case MathOp.Trunc: return Math.trunc(a); - case MathOp.Frac: return a - Math.trunc(a); - case MathOp.Mod: return a % b; - case MathOp.Snap: return Math.round(a * b) / b; - case MathOp.Clamp: return Math.max(0, Math.min(a, 1)); - - case MathOp.Sin: return Math.sin(a); - case MathOp.Cos: return Math.cos(a); - case MathOp.Tan: return Math.tan(a); - case MathOp.Asin: return Math.asin(a); - case MathOp.Acos: return Math.acos(a); - case MathOp.Atan: return Math.atan(a); - case MathOp.Atan2: return Math.atan2(a, b); - - case MathOp.Sinh: return Math.sinh(a); - case MathOp.Cosh: return Math.cosh(a); - case MathOp.Tanh: return Math.tanh(a); - - case MathOp.ToRad: return a / 180 * Math.PI; - case MathOp.ToDeg: return a * 180 / Math.PI; - - default: throw new TypeError(); - } -}; - const mathFunc = ({ op, a, b }: MathInputs): MathOutputs => { - const countScalar = (typeof a === 'number' ? 1 : 0) + (typeof b === 'number' ? 1 : 0); - if (typeof a === 'number') { - if (typeof b === 'number') { - return { out: doMathOp(op, a, b) }; - } else { - const out = new Float32Array(b.length); - for (let i = 0; i < out.length; i++) { - out[i] = doMathOp(op, a, b[i]); - } - return { out }; - } + const ta = typeof a === 'number'; + const tb = typeof b === 'number'; + const opNum = Object.values(MathOp).indexOf(op); + if (Object.keys(unaryOps).includes(op)) { + return { out: ta ? mathS(opNum, a) : mathV(opNum, a) as Float32Array }; + } + if (ta && tb) { + return { out: mathSS(opNum, a, b) }; + } else if (ta && !tb) { + return { out: mathSV(opNum, a, b) as Float32Array }; + } else if (!ta && tb) { + return { out: mathVS(opNum, a, b) as Float32Array }; } else { - if (typeof b === 'number') { - const out = new Float32Array(a.length); - for (let i = 0; i < out.length; i++) { - out[i] = doMathOp(op, a[i], b); - } - return { out }; - } else { - const out = new Float32Array(Math.min(a.length, b.length)); - for (let i = 0; i < out.length; i++) { - out[i] = doMathOp(op, a[i], b[i]); - } - return { out }; - } + return { out: mathVV(opNum, a, b) as Float32Array }; } }; @@ -154,4 +146,4 @@ export const MathNode: NodeInfo = { component: MathC, func: mathFunc, inputs: { op: MathOp.Add, a: 0, b: 0 }, -}; +}; \ No newline at end of file diff --git a/src/nodes/Plot.tsx b/src/nodes/Plot.tsx index eaba592..d7be295 100644 --- a/src/nodes/Plot.tsx +++ b/src/nodes/Plot.tsx @@ -13,7 +13,7 @@ export const Plot = ({ id, x, y, inputs }: NodeComponentProps) => { const dy = 0; let path = ''; if (data !== null && data.length > 3) { - for (let i = 0; i < data.length; i += Math.max(1, Math.floor(data.length / 1000))) { + for (let i = 0; i < data.length; i += Math.max(2, Math.floor(data.length / 1000))) { if (i >= data.length) break; path += (i ? 'L' : 'M') + (data[i] * scale + dx) + ' ' + (data[i+1] * scale + dy); } diff --git a/src/nodes/index.ts b/src/nodes/index.ts index 0e63d3a..a5678fa 100644 --- a/src/nodes/index.ts +++ b/src/nodes/index.ts @@ -1,3 +1,4 @@ +import type { NodeInfo } from '../node.tsx'; import { CombineXYZNode } from './CombineXYZ.tsx'; import { SeparateXYZNode } from './SeparateXYZ.tsx'; import { ViewerNode } from './Viewer.tsx'; diff --git a/src/wasm.ts b/src/wasm.ts index 3a308b2..311a088 100644 --- a/src/wasm.ts +++ b/src/wasm.ts @@ -2,8 +2,9 @@ import { instantiate } from '../build/out.js'; import url from '../build/out.wasm'; export const { memory, + mathS, mathV, mathSS, mathSV, mathVS, mathVV, linspace, intersperse, dft, fft, -} = await instantiate(await WebAssembly.compileStreaming(fetch(url))); \ No newline at end of file +} = await instantiate(await WebAssembly.compileStreaming(fetch(url)), {}); \ No newline at end of file -- cgit v1.2.3