summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--flake.nix28
-rw-r--r--package-lock.json148
-rw-r--r--src/nodes/Plot.tsx73
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
+};