diff options
| -rw-r--r-- | flake.nix | 28 | ||||
| -rw-r--r-- | package-lock.json | 148 | ||||
| -rw-r--r-- | src/nodes/Plot.tsx | 73 |
3 files changed, 237 insertions, 12 deletions
diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..231aaa2 --- /dev/null +++ b/flake.nix @@ -0,0 +1,28 @@ +{ + description = "Celeste key overlay"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; + outputs = { self, nixpkgs }: let + systems = ["aarch64-darwin" "aarch64-linux" "x86_64-darwin" "x86_64-linux"]; + forEachSystem = fn: lib.genAttrs systems (system: fn (import nixpkgs {inherit system;})); + inherit (nixpkgs) lib; + in { + devShells = forEachSystem (pkgs: { + default = pkgs.mkShell { + name = "key-overlay"; + packages = with pkgs; [ + bun + typescript + assemblyscript + esbuild + ]; + }; + }); + packages = forEachSystem (pkgs: { + default = pkgs.buildNpmPackage { + pname = "webnodes"; + version = "0.1.0"; + src = ./.; + }; + }); + }; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..8fe0af2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,148 @@ +{ + "name": "webnodes", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "webnodes", + "dependencies": { + "@preact/signals": "^1.2.2", + "preact": "^10.19.6" + }, + "devDependencies": { + "assemblyscript": "^0.27.24", + "esbuild": "^0.20.1", + "typescript": "^5.3.3" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.20.1", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@preact/signals": { + "version": "1.2.2", + "license": "MIT", + "dependencies": { + "@preact/signals-core": "^1.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact": "10.x" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.5.1", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/assemblyscript": { + "version": "0.27.24", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "binaryen": "116.0.0-nightly.20240114", + "long": "^5.2.1" + }, + "bin": { + "asc": "bin/asc.js", + "asinit": "bin/asinit.js" + }, + "engines": { + "node": ">=16", + "npm": ">=7" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/assemblyscript" + } + }, + "node_modules/binaryen": { + "version": "116.0.0-nightly.20240114", + "dev": true, + "license": "Apache-2.0", + "bin": { + "wasm-opt": "bin/wasm-opt", + "wasm2js": "bin/wasm2js" + } + }, + "node_modules/esbuild": { + "version": "0.20.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.20.1", + "@esbuild/android-arm": "0.20.1", + "@esbuild/android-arm64": "0.20.1", + "@esbuild/android-x64": "0.20.1", + "@esbuild/darwin-arm64": "0.20.1", + "@esbuild/darwin-x64": "0.20.1", + "@esbuild/freebsd-arm64": "0.20.1", + "@esbuild/freebsd-x64": "0.20.1", + "@esbuild/linux-arm": "0.20.1", + "@esbuild/linux-arm64": "0.20.1", + "@esbuild/linux-ia32": "0.20.1", + "@esbuild/linux-loong64": "0.20.1", + "@esbuild/linux-mips64el": "0.20.1", + "@esbuild/linux-ppc64": "0.20.1", + "@esbuild/linux-riscv64": "0.20.1", + "@esbuild/linux-s390x": "0.20.1", + "@esbuild/linux-x64": "0.20.1", + "@esbuild/netbsd-x64": "0.20.1", + "@esbuild/openbsd-x64": "0.20.1", + "@esbuild/sunos-x64": "0.20.1", + "@esbuild/win32-arm64": "0.20.1", + "@esbuild/win32-ia32": "0.20.1", + "@esbuild/win32-x64": "0.20.1" + } + }, + "node_modules/long": { + "version": "5.2.3", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/preact": { + "version": "10.19.6", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/typescript": { + "version": "5.4.2", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/src/nodes/Plot.tsx b/src/nodes/Plot.tsx index fbec1f4..649246a 100644 --- a/src/nodes/Plot.tsx +++ b/src/nodes/Plot.tsx @@ -1,4 +1,5 @@ import { NodeShell, InputAny, InputNumber, NodeComponentProps, NodeInfo } from '../node.tsx'; +import { useComputed } from '@preact/signals'; export interface PlotInputs { data: any; @@ -11,32 +12,80 @@ export interface PlotInputs { export interface PlotOutputs {} export const Plot = ({ id, x, y, inputs }: NodeComponentProps<PlotInputs>) => { + const { minX, maxX, minY, maxY } = inputs; const data = inputs.data.value; const width = 400; const height = 400; - const dx = inputs.maxX.value - inputs.minX.value; - const dy = inputs.maxY.value - inputs.minY.value; + const xtickCount = 5; + const ytickCount = 5; + const dx = useComputed(() => maxX.value - minX.value); + const dy = useComputed(() => maxY.value - minY.value); let path = ''; if (data !== null && data.length > 3) { for (let i = 0; i < data.length; i += Math.max(2, Math.floor(data.length / 1000))) { if (i >= data.length) break; - const X = (data[i] - inputs.minX.value) / dx * width; - const Y = height - (data[i+1] - inputs.minY.value) / dy * height; + const X = (data[i] - minX.value) / dx.value * width; + const Y = height - (data[i+1] - minY.value) / dy.value * height; path += (i ? 'L' : 'M') + X + ' ' + Y; } } - //alert(dx); + + let xticks = []; + for (let x = minX.value; x <= maxX.value; x += dx.value / xtickCount) { + xticks.push([x.toFixed(1), (x - minX.value) / dx.value * width]); + } + let yticks = []; + for (let y = minY.value; y <= maxY.value; y += dy.value / ytickCount) { + yticks.push([y.toFixed(1), height - (y - minY.value) / dy.value * height]); + } + + const onMouseDown = (event: MouseEvent) => { + event.stopPropagation(); + const onMouseMove = (event: MouseEvent) => { + minX.value -= event.movementX / width * dx.value; + minY.value += event.movementY / height * dy.value; + }; + const onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + }; + return ( <NodeShell name="Plot" id={id} x={x} y={y}> <InputAny name="data" label="Data" /> - <InputNumber name="minX" label="Min X" value={inputs.minX} /> - <InputNumber name="minY" label="Min Y" value={inputs.minY} /> - <InputNumber name="maxX" label="Max X" value={inputs.maxX} /> - <InputNumber name="maxY" label="Max Y" value={inputs.maxY} /> + <InputNumber name="minX" label="Min X" value={minX} /> + <InputNumber name="minY" label="Min Y" value={minY} /> + <InputNumber name="maxX" label="Max X" value={maxX} /> + <InputNumber name="maxY" label="Max Y" value={maxY} /> <li> - <svg width={width} height={height} style="background-color:white;margin:4px 8px 0 8px;"> - <path fill="none" stroke="blue" stroke-width="2" d={path} /> + <svg + width={width + 64} + height={height + 64} + onMouseDown={onMouseDown} + style="background-color:white;margin:4px 8px 0 8px;" + > + {xticks.map(([lbl, x]) => ( + <> + <path fill="none" stroke="black" stroke-width="1.5" d={`M ${x+32} ${height+32} v8`} /> + <path fill="none" stroke="#aaa" stroke-width="1.5" d={`M ${x+32} ${height+32} v${-height}`} /> + <text x={x+24} y={height+56}>{lbl}</text> + </> + ))} + {yticks.map(([lbl, y]) => ( + <> + <path fill="none" stroke="black" stroke-width="1.5" d={`M ${32} ${y+32} h-8`} /> + <path fill="none" stroke="#aaa" stroke-width="1.5" d={`M ${32} ${y+32} h${width}`} /> + <text x={4} y={y+36}>{lbl}</text> + </> + ))} + <path fill="none" stroke="black" stroke-width="1.5" d={`M ${-minX.value/dx.value*width+32} ${height+32} v${-height}`} /> + <path fill="none" stroke="black" stroke-width="1.5" d={`M ${32} ${height+minY.value/dy.value*height+32} h${width}`} /> + <rect fill="none" stroke="black" stroke-width="2" x="32" y="32" width={width} height={height} /> + <path fill="none" stroke="blue" stroke-width="2" transform="translate(32,32)" d={path} /> </svg> </li> </NodeShell> @@ -47,4 +96,4 @@ export const PlotNode: NodeInfo<PlotInputs, PlotOutputs> = { component: Plot, func: () => ({}), inputs: { data: null, minX: -10, minY: -10, maxX: 10, maxY: 10 }, -};
\ No newline at end of file +}; |
