From 5bbd0e9e780897010f691a94e7e4a6486e3f2201 Mon Sep 17 00:00:00 2001 From: Julian Badillo Date: Mon, 20 Jan 2025 12:28:11 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=90=20formatting=20rules=20(#153)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :rule: formatting rules * :pick: infix ops * lockfiles are dumb >_< --------- Co-authored-by: Alex Ruddick --- .eslintrc.js | 7 +- package-lock.json | 223 +++++++++++++++++++++++++++++++++++++- package.json | 3 +- src/background-planner.ts | 2 +- src/cli.ts | 14 +-- src/ebb.ts | 6 +- src/massager.ts | 8 +- src/paper-size.ts | 24 ++-- src/planning.ts | 8 +- src/server.ts | 22 ++-- src/ui.tsx | 138 +++++++++++------------ src/util.ts | 16 +-- src/vec.ts | 8 +- 13 files changed, 353 insertions(+), 126 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index db5c9427..7591ec60 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,6 @@ module.exports = { parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], + plugins: ['@typescript-eslint', '@stylistic/eslint-plugin'], extends: [ 'eslint:recommended', 'plugin:react/recommended', @@ -22,6 +22,11 @@ module.exports = { argsIgnorePattern: "^_", varsIgnorePattern: "^_", }], + '@stylistic/indent': ['error', 2], + "@stylistic/space-infix-ops": 1, + '@stylistic/object-curly-spacing': ['error', 'always'], + // "semi": [1, "always"], + // "consistent-return": 2, }, settings: { react: { diff --git a/package-lock.json b/package-lock.json index 3c0b607a..1dc978ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@craftamap/esbuild-plugin-html": "^0.7.0 || ^0.8.0", "@rehooks/component-size": "^1.0.2", "@serialport/bindings-cpp": "^12.0.0 || ^13.0.0", + "@stylistic/eslint-plugin": "^2.10.1", "@types/cors": "^2.8.4", "@types/express": "^4.17.9", "@types/jest": "^29.0.0", @@ -1928,6 +1929,224 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@stylistic/eslint-plugin": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.10.1.tgz", + "integrity": "sha512-U+4yzNXElTf9q0kEfnloI9XbOyD4cnEQCxjUI94q0+W++0GAEQvJ/slwEj9lwjDHfGADRSr+Tco/z0XJvmDfCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.12.2", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "estraverse": "^5.3.0", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "eslint": ">=8.40.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.14.0.tgz", + "integrity": "sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.14.0.tgz", + "integrity": "sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.14.0.tgz", + "integrity": "sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/visitor-keys": "8.14.0", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.14.0.tgz", + "integrity": "sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.14.0", + "@typescript-eslint/types": "8.14.0", + "@typescript-eslint/typescript-estree": "8.14.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.14.0.tgz", + "integrity": "sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.14.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@stylistic/eslint-plugin/node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/@swc/helpers": { "version": "0.4.36", "license": "Apache-2.0", @@ -2449,7 +2668,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", "bin": { diff --git a/package.json b/package.json index f26ae286..a2081303 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "scripts": { "prebuild": "npm run lint", - "lint": "eslint --cache --ext .ts,.tsx src", + "lint": "eslint --cache --ext .ts,.tsx src && eslint --format stylish --fix src", "build": "npm run build:server && npm run build:ui", "build:server": "tsc", "build:ui": "node --experimental-modules build.mjs", @@ -36,6 +36,7 @@ "@craftamap/esbuild-plugin-html": "^0.7.0 || ^0.8.0", "@rehooks/component-size": "^1.0.2", "@serialport/bindings-cpp": "^12.0.0 || ^13.0.0", + "@stylistic/eslint-plugin": "^2.10.1", "@types/cors": "^2.8.4", "@types/express": "^4.17.9", "@types/jest": "^29.0.0", diff --git a/src/background-planner.ts b/src/background-planner.ts index f1211f3b..6e5715cd 100644 --- a/src/background-planner.ts +++ b/src/background-planner.ts @@ -1,7 +1,7 @@ import { replan } from './massager'; self.addEventListener("message", (m) => { - const {paths, planOptions} = m.data; + const { paths, planOptions } = m.data; const plan = replan(paths, planOptions); console.time("serializing"); const serialized = plan.serialize(); diff --git a/src/cli.ts b/src/cli.ts index 767700e4..3bf6bdc6 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,9 +1,9 @@ import yargs from "yargs"; -import {connectEBB, startServer} from "./server"; -import {replan} from "./massager"; -import {Window} from "svgdom"; +import { connectEBB, startServer } from "./server"; +import { replan } from "./massager"; +import { Window } from "svgdom"; import * as fs from "node:fs"; -import {flattenSVG} from "flatten-svg"; +import { flattenSVG } from "flatten-svg"; import type { Vec2 } from "./vec"; import { formatDuration } from "./util"; import { Device, type PlanOptions, defaultPlanOptions } from "./planning"; @@ -45,7 +45,7 @@ export function cli(argv: string[]): void { } else { const m = /^([0-9]*(?:\.[0-9]+)?)\s*x\s*([0-9]*(?:\.[0-9]+)?)\s*(cm|mm|in)$/i.exec(String(value).trim()) if (m) { - return new PaperSize({x: Number(m[1]), y: Number(m[2])}) + return new PaperSize({ x: Number(m[1]), y: Number(m[2]) }) } } throw new Error(`Paper size should be a standard size (${Object.keys(PaperSize.standard).join(", ")}) or a custom size such as "100x100mm" or "16x10in"`) @@ -219,7 +219,7 @@ export function cli(argv: string[]): void { } ) .command('pen [percent]', 'put the pen to [percent]', yargs => yargs - .positional('percent', { type: 'number', description: 'percent height between 0 and 100', required: true}) + .positional('percent', { type: 'number', description: 'percent height between 0 and 100', required: true }) .check(args => args.percent >= 0 && args.percent <= 100), async args => { console.log('connecting to plotter...') @@ -260,7 +260,7 @@ export function cli(argv: string[]): void { function linesToVecs(lines: any[]): Vec2[][] { return lines.map((line) => { - const a = line.points.map(([x, y]: [number, number]) => ({x, y})); + const a = line.points.map(([x, y]: [number, number]) => ({ x, y })); (a as any).stroke = line.stroke; (a as any).groupId = line.groupId; return a; diff --git a/src/ebb.ts b/src/ebb.ts index b27d9746..1fc51e61 100644 --- a/src/ebb.ts +++ b/src/ebb.ts @@ -1,6 +1,6 @@ -import {type Block, type Motion, PenMotion, type Plan, XYMotion} from "./planning"; +import { type Block, type Motion, PenMotion, type Plan, XYMotion } from "./planning"; import { RegexParser } from "./regex-transform-stream"; -import {type Vec2, vsub} from "./vec"; +import { type Vec2, vsub } from "./vec"; /** Split d into its fractional and integral parts */ function modf(d: number): [number, number] { @@ -21,7 +21,7 @@ export class EBB { private microsteppingMode = 0; /** Accumulated XY error, used to correct for movements with sub-step resolution */ - private error: Vec2 = {x: 0, y: 0}; + private error: Vec2 = { x: 0, y: 0 }; private cachedFirmwareVersion: [number, number, number] | undefined = undefined; diff --git a/src/massager.ts b/src/massager.ts index 8d548ef9..03b96049 100644 --- a/src/massager.ts +++ b/src/massager.ts @@ -1,7 +1,7 @@ import * as Optimization from "optimize-paths"; -import {Device, type Plan, type PlanOptions, plan} from "./planning"; -import {dedupPoints, scaleToPaper, cropToMargins} from "./util"; -import {type Vec2, vmul, vrot} from "./vec"; +import { Device, type Plan, type PlanOptions, plan } from "./planning"; +import { dedupPoints, scaleToPaper, cropToMargins } from "./util"; +import { type Vec2, vmul, vrot } from "./vec"; // CSS, and thus SVG, defines 1px = 1/96th of 1in // https://www.w3.org/TR/css-values-4/#absolute-lengths @@ -19,7 +19,7 @@ export function replan(inPaths: Vec2[][], planOptions: PlanOptions): Plan { if (planOptions.rotateDrawing !== 0) { console.time("rotating paths"); paths = paths.map((pl) => pl.map((p) => vrot(p, - vmul({x:planOptions.paperSize.size.x/2, y: planOptions.paperSize.size.y/2}, 1/mmPerSvgUnit), + vmul({ x:planOptions.paperSize.size.x / 2, y: planOptions.paperSize.size.y / 2 }, 1 / mmPerSvgUnit), planOptions.rotateDrawing) )); console.timeEnd("rotating paths"); diff --git a/src/paper-size.ts b/src/paper-size.ts index a228fc90..5345862b 100644 --- a/src/paper-size.ts +++ b/src/paper-size.ts @@ -1,4 +1,4 @@ -import {type Vec2, vmul} from "./vec"; +import { type Vec2, vmul } from "./vec"; function vround(v: Vec2, digits = 2): Vec2 { return { x: Number(v.x.toFixed(digits)), y: Number(v.y.toFixed(digits)) }; @@ -24,17 +24,17 @@ export class PaperSize { return this.size.x === Math.max(this.size.x, this.size.y); } - public static standard: {[name: string]: PaperSize} = { - "USLetter": new PaperSize(vround(vmul({x: 8.5, y: 11}, 25.4))), - "USLegal": new PaperSize(vround(vmul({x: 8.5, y: 14}, 25.4))), - "ArchA": new PaperSize(vround(vmul({x: 9, y: 12}, 25.4))), - "A3": new PaperSize({x: 297, y: 420}), - "A4": new PaperSize({x: 210, y: 297}), - "A5": new PaperSize({x: 148, y: 210}), - "A6": new PaperSize({x: 105, y: 148}), - "6x8": new PaperSize(vround(vmul({x: 6, y: 8}, 25.4))), - "5x7": new PaperSize(vround(vmul({x: 5, y: 7}, 25.4))), - "11x14": new PaperSize(vround(vmul({x: 11, y: 14}, 25.4))), + public static standard: { [name: string]: PaperSize } = { + "USLetter": new PaperSize(vround(vmul({ x: 8.5, y: 11 }, 25.4))), + "USLegal": new PaperSize(vround(vmul({ x: 8.5, y: 14 }, 25.4))), + "ArchA": new PaperSize(vround(vmul({ x: 9, y: 12 }, 25.4))), + "A3": new PaperSize({ x: 297, y: 420 }), + "A4": new PaperSize({ x: 210, y: 297 }), + "A5": new PaperSize({ x: 148, y: 210 }), + "A6": new PaperSize({ x: 105, y: 148 }), + "6x8": new PaperSize(vround(vmul({ x: 6, y: 8 }, 25.4))), + "5x7": new PaperSize(vround(vmul({ x: 5, y: 7 }, 25.4))), + "11x14": new PaperSize(vround(vmul({ x: 11, y: 14 }, 25.4))), }; public size: Vec2; public constructor(size: Vec2) { diff --git a/src/planning.ts b/src/planning.ts index d2988246..57c6f60d 100644 --- a/src/planning.ts +++ b/src/planning.ts @@ -193,13 +193,13 @@ export class Block { public get vFinal(): number { return Math.max(0, this.vInitial + this.accel * this.duration); } - public instant(tU: number, dt= 0, ds= 0): Instant { + public instant(tU: number, dt = 0, ds = 0): Instant { const t = Math.max(0, Math.min(this.duration, tU)); const a = this.accel; const v = this.vInitial + this.accel * t; const s = Math.max(0, Math.min(this.distance, this.vInitial * t + a * t * t / 2)); const p = vadd(this.p1, vmul(vnorm(vsub(this.p2, this.p1)), s)); - return {t: t + dt, p, s: s + ds, v, a}; + return { t: t + dt, p, s: s + ds, v, a }; } public serialize(): any { @@ -439,7 +439,7 @@ function computeTriangle( const t1 = (vMax - initialVel) / accel; const t2 = (finalVel - vMax) / -accel; const p2 = vadd(p1, vmul(vnorm(vsub(p3, p1)), acceleratingDistance)); - return {s1: acceleratingDistance, s2: deceleratingDistance, t1, t2, vMax, p1, p2, p3}; + return { s1: acceleratingDistance, s2: deceleratingDistance, t1, t2, vMax, p1, p2, p3 }; } /** Represents a trapezoidal velocity profile for moving in a straight line. @@ -492,7 +492,7 @@ function computeTrapezoid( const dir = vnorm(vsub(p4, p1)); const p2 = vadd(p1, vmul(dir, s1)); const p3 = vadd(p1, vmul(dir, (distance - s3))); - return {s1, s2, s3, t1, t2, t3, p1, p2, p3, p4}; + return { s1, s2, s3, t1, t2, t3, p1, p2, p3, p4 }; } function dedupPoints(points: Vec2[], epsilon: number): Vec2[] { diff --git a/src/server.ts b/src/server.ts index ab816ed1..4e44b856 100644 --- a/src/server.ts +++ b/src/server.ts @@ -46,7 +46,7 @@ export async function startServer (port: number, hardware: Hardware = 'v3', com: const msg = JSON.parse(message.toString()); switch (msg.c) { case "ping": - ws.send(JSON.stringify({c: "pong"})); + ws.send(JSON.stringify({ c: "pong" })); break; case "limp": if (ebb) { ebb.disableMotors(); } @@ -64,14 +64,14 @@ export async function startServer (port: number, hardware: Hardware = 'v3', com: } }); - ws.send(JSON.stringify({c: 'dev', p: getDeviceInfo(ebb, com)})) + ws.send(JSON.stringify({ c: 'dev', p: getDeviceInfo(ebb, com) })) - ws.send(JSON.stringify({c: "pause", p: {paused: !!unpaused}})); + ws.send(JSON.stringify({ c: "pause", p: { paused: !!unpaused } })); if (motionIdx != null) { - ws.send(JSON.stringify({c: "progress", p: {motionIdx}})); + ws.send(JSON.stringify({ c: "progress", p: { motionIdx } })); } if (currentPlan != null) { - ws.send(JSON.stringify({c: "plan", p: {plan: currentPlan}})); + ws.send(JSON.stringify({ c: "plan", p: { plan: currentPlan } })); } ws.on("close", () => { @@ -131,7 +131,7 @@ export async function startServer (port: number, hardware: Hardware = 'v3', com: unpaused = new Promise(resolve => { signalUnpause = resolve; }); - broadcast({c: "pause", p: {paused: true}}); + broadcast({ c: "pause", p: { paused: true } }); } res.status(200).end(); }); @@ -208,14 +208,14 @@ export async function startServer (port: number, hardware: Hardware = 'v3', com: let penIsUp = true; for (const motion of plan.motions) { - broadcast({c: "progress", p: {motionIdx}}); + broadcast({ c: "progress", p: { motionIdx } }); await plotter.executeMotion(motion, [motionIdx, plan.motions.length]); if (motion instanceof PenMotion) { penIsUp = motion.initialPos < motion.finalPos; } if (unpaused && penIsUp) { await unpaused; - broadcast({c: "pause", p: {paused: false}}); + broadcast({ c: "pause", p: { paused: false } }); } if (cancelRequested) { break; } motionIdx += 1; @@ -224,10 +224,10 @@ export async function startServer (port: number, hardware: Hardware = 'v3', com: currentPlan = null; if (cancelRequested) { await plotter.postCancel(); - broadcast({c: "cancelled"}); + broadcast({ c: "cancelled" }); cancelRequested = false; } else { - broadcast({c: "finished"}); + broadcast({ c: "finished" }); } await plotter.postPlot(); } @@ -242,7 +242,7 @@ export async function startServer (port: number, hardware: Hardware = 'v3', com: } } connect(); - const {family, address, port} = server.address() as any; + const { family, address, port } = server.address() as any; const addr = `${family === "IPv6" ? `[${address}]` : address}:${port}`; console.log(`Server listening on http://${addr}`); resolve(server); diff --git a/src/ui.tsx b/src/ui.tsx index 510b33ca..011023ec 100644 --- a/src/ui.tsx +++ b/src/ui.tsx @@ -4,11 +4,11 @@ import { createRoot } from 'react-dom/client'; import interpolator from "color-interpolate" import colormap from "colormap" -import {flattenSVG} from "flatten-svg"; -import {PaperSize} from "./paper-size"; -import {Device, Plan, type PlanOptions, defaultPlanOptions, XYMotion, PenMotion} from "./planning"; -import {formatDuration} from "./util"; -import type {Vec2} from "./vec"; +import { flattenSVG } from "flatten-svg"; +import { PaperSize } from "./paper-size"; +import { Device, Plan, type PlanOptions, defaultPlanOptions, XYMotion, PenMotion } from "./planning"; +import { formatDuration } from "./util"; +import type { Vec2 } from "./vec"; import PlanWorker from "./plan.worker"; @@ -62,21 +62,21 @@ const DispatchContext = React.createContext(nullDispatch); function reducer(state: State, action: any): State { switch (action.type) { case "SET_PLAN_OPTION": - return {...state, planOptions: {...state.planOptions, ...action.value}}; + return { ...state, planOptions: { ...state.planOptions, ...action.value } }; case "SET_VISUALIZATION_OPTION": - return {...state, visualizationOptions: {...state.visualizationOptions, ...action.value}}; + return { ...state, visualizationOptions: { ...state.visualizationOptions, ...action.value } }; case "SET_DEVICE_INFO": - return {...state, deviceInfo: action.value}; + return { ...state, deviceInfo: action.value }; case "SET_PAUSED": - return {...state, paused: action.value}; + return { ...state, paused: action.value }; case "SET_PATHS": // eslint-disable-next-line no-case-declarations - const {paths, strokeLayers, selectedStrokeLayers, groupLayers, selectedGroupLayers, layerMode} = action; - return {...state, paths, groupLayers, strokeLayers, planOptions: {...state.planOptions, selectedStrokeLayers, selectedGroupLayers, layerMode}}; + const { paths, strokeLayers, selectedStrokeLayers, groupLayers, selectedGroupLayers, layerMode } = action; + return { ...state, paths, groupLayers, strokeLayers, planOptions: { ...state.planOptions, selectedStrokeLayers, selectedGroupLayers, layerMode } }; case "SET_PROGRESS": - return {...state, progress: action.motionIdx}; + return { ...state, progress: action.motionIdx }; case "SET_CONNECTED": - return {...state, connected: action.connected}; + return { ...state, connected: action.connected }; default: console.warn(`Unrecognized action type '${action.type}'`); return state; @@ -348,7 +348,7 @@ class SaxiDriver implements Driver { } public setPenHeight(height: number, rate: number) { - this.send({ c: "setPenHeight", p: {height, rate} }); + this.send({ c: "setPenHeight", p: { height, rate } }); } public limp() { this.send({ c: "limp" }); } @@ -400,7 +400,7 @@ const usePlan = (paths: Vec2[][] | null, planOptions: PlanOptions) => { const worker = new (PlanWorker as any)(); setIsPlanning(true); console.time("posting to worker"); - worker.postMessage({paths, planOptions}); + worker.postMessage({ paths, planOptions }); console.timeEnd("posting to worker"); const listener = (m: any) => { console.time("deserializing"); @@ -432,14 +432,14 @@ const setPaths = (paths: Vec2[][]) => { const layerMode = groups.size > 1 ? 'group' : 'stroke' const groupLayers = Array.from(groups).sort() const strokeLayers = Array.from(strokes).sort() - return {type: "SET_PATHS", paths, groupLayers, strokeLayers, selectedGroupLayers: new Set(groupLayers), selectedStrokeLayers: new Set(strokeLayers), layerMode}; + return { type: "SET_PATHS", paths, groupLayers, strokeLayers, selectedGroupLayers: new Set(groupLayers), selectedStrokeLayers: new Set(strokeLayers), layerMode }; }; function PenHeight({ state, driver }: { state: State; driver: Driver }) { const { penUpHeight, penDownHeight, hardware } = state.planOptions; const dispatch = useContext(DispatchContext); - const setPenUpHeight = (x: number) => dispatch({type: "SET_PLAN_OPTION", value: {penUpHeight: x}}); - const setPenDownHeight = (x: number) => dispatch({type: "SET_PLAN_OPTION", value: {penDownHeight: x}}); + const setPenUpHeight = (x: number) => dispatch({ type: "SET_PLAN_OPTION", value: { penUpHeight: x } }); + const setPenDownHeight = (x: number) => dispatch({ type: "SET_PLAN_OPTION", value: { penDownHeight: x } }); const device = Device(hardware); const penUp = () => { @@ -494,14 +494,14 @@ function VisualizationOptions({ state }: { state: State }) { min="0" max="10" step="0.1" - onChange={(e) => dispatch({type: "SET_VISUALIZATION_OPTION", value: {penStrokeWidth: Number(e.target.value)}})} + onChange={(e) => dispatch({ type: "SET_VISUALIZATION_OPTION", value: { penStrokeWidth: Number(e.target.value) } })} /> @@ -524,20 +524,20 @@ function SwapPaperSizesButton({ onClick }: { onClick: () => void }) { ; } -function PaperConfig({state}: {state: State}) { +function PaperConfig({ state }: { state: State }) { const dispatch = useContext(DispatchContext); const landscape = state.planOptions.paperSize.isLandscape; function setPaperSize(e: ChangeEvent) { const name = (e.target as HTMLInputElement).value; if (name !== "Custom") { const ps = PaperSize.standard[name][landscape ? "landscape" : "portrait"]; - dispatch({type: "SET_PLAN_OPTION", value: {paperSize: ps}}); + dispatch({ type: "SET_PLAN_OPTION", value: { paperSize: ps } }); } } function setCustomPaperSize(x: number, y: number) { - dispatch({type: "SET_PLAN_OPTION", value: {paperSize: new PaperSize({x, y})}}); + dispatch({ type: "SET_PLAN_OPTION", value: { paperSize: new PaperSize({ x, y }) } }); } - const {paperSize} = state.planOptions; + const { paperSize } = state.planOptions; const paperSizeName = Object.keys(PaperSize.standard).find((psName) => { const ps = PaperSize.standard[psName].size; return (ps.x === paperSize.size.x && ps.y === paperSize.size.y) @@ -565,7 +565,7 @@ function PaperConfig({state}: {state: State}) { { dispatch({ type: "SET_PLAN_OPTION", - value: {paperSize: paperSize.isLandscape ? paperSize.portrait : paperSize.landscape} + value: { paperSize: paperSize.isLandscape ? paperSize.portrait : paperSize.landscape } }); }} /> ; } -function MotorControl({driver}: {driver: Driver}) { +function MotorControl({ driver }: { driver: Driver }) { return
; } -function PlanStatistics({plan}: {plan: Plan}) { +function PlanStatistics({ plan }: { plan: Plan }) { return
Duration
{plan && plan.duration ? formatDuration(plan.duration()) : "-"}
; } -function TimeLeft({plan, progress, currentMotionStartedTime, paused}: { +function TimeLeft({ plan, progress, currentMotionStartedTime, paused }: { plan: Plan; progress: number | null; currentMotionStartedTime: Date | null; @@ -652,9 +652,9 @@ function TimeLeft({plan, progress, currentMotionStartedTime, paused}: { } function PlanPreview( - {state, previewSize, plan}: { + { state, previewSize, plan }: { state: State; - previewSize: {width: number; height: number}; + previewSize: { width: number; height: number }; plan: Plan | null; } ) { @@ -665,7 +665,7 @@ function PlanPreview( const memoizedPlanPreview = useMemo(() => { if (plan) { const palette = colorPathsByStrokeOrder - ? interpolator(colormap({colormap: 'spring'})) + ? interpolator(colormap({ colormap: 'spring' })) : () => 'rgba(0, 0, 0, 0.8)' const lines = plan.motions.map((m) => { if (m instanceof XYMotion) { @@ -676,8 +676,8 @@ function PlanPreview( {lines.map((line, i) => m + `${j === 0 ? "M" : "L"}${x} ${y}`, "")} - style={i % 2 === 0 ? {stroke: "rgba(0, 0, 0, 0.3)", strokeWidth: 0.5} : { stroke: palette(1 - i / lines.length), strokeWidth }} + d={line.reduce((m, { x, y }, j) => m + `${j === 0 ? "M" : "L"}${x} ${y}`, "")} + style={i % 2 === 0 ? { stroke: "rgba(0, 0, 0, 0.3)", strokeWidth: 0.5 } : { stroke: palette(1 - i / lines.length), strokeWidth }} /> )} ; @@ -687,9 +687,9 @@ function PlanPreview( // w/h of svg. // first try scaling so that h = area.h. if w < area.w, then ok. // otherwise, scale so that w = area.w. - const {width, height} = ps.size.x / ps.size.y * previewSize.height <= previewSize.width - ? {width: ps.size.x / ps.size.y * previewSize.height, height: previewSize.height} - : {height: ps.size.y / ps.size.x * previewSize.width, width: previewSize.width}; + const { width, height } = ps.size.x / ps.size.y * previewSize.height <= previewSize.width + ? { width: ps.size.x / ps.size.y * previewSize.height, height: previewSize.height } + : { height: ps.size.y / ps.size.x * previewSize.width, width: previewSize.width }; const [microprogress, setMicroprogress] = useState(0); useLayoutEffect(() => { @@ -736,11 +736,11 @@ function PlanPreview( ; @@ -771,7 +771,7 @@ function PlanPreview( } function PlanLoader( - {isLoadingFile, isPlanning}: { + { isLoadingFile, isPlanning }: { isLoadingFile: boolean; isPlanning: boolean; } @@ -785,7 +785,7 @@ function PlanLoader( return null; } -function LayerSelector({state}: {state: State}) { +function LayerSelector({ state }: { state: State }) { const dispatch = useContext(DispatchContext); const layers = state.planOptions.layerMode === 'group' ? state.groupLayers : state.strokeLayers const selectedLayers = state.planOptions.layerMode === 'group' ? state.planOptions.selectedGroupLayers : state.planOptions.selectedStrokeLayers @@ -793,11 +793,11 @@ function LayerSelector({state}: {state: State}) { const layersChanged = state.planOptions.layerMode === 'group' ? (e: ChangeEvent) => { const selectedLayers = new Set([...(e.target as HTMLSelectElement).selectedOptions].map((o) => o.value)); - dispatch({type: "SET_PLAN_OPTION", value: {selectedGroupLayers: selectedLayers}}); + dispatch({ type: "SET_PLAN_OPTION", value: { selectedGroupLayers: selectedLayers } }); } : (e: ChangeEvent) => { const selectedLayers = new Set([...(e.target as HTMLSelectElement).selectedOptions].map((o) => o.value)); - dispatch({type: "SET_PLAN_OPTION", value: {selectedStrokeLayers: selectedLayers}}); + dispatch({ type: "SET_PLAN_OPTION", value: { selectedStrokeLayers: selectedLayers } }); }; return
@@ -960,7 +960,7 @@ function PlanOptions({state}: {state: State}) { value={state.planOptions.penDownAcceleration} step="0.1" min="0" - onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {penDownAcceleration: Number(e.target.value)}})} + onChange={(e) => dispatch({ type: "SET_PLAN_OPTION", value: { penDownAcceleration: Number(e.target.value) } })} />
@@ -981,7 +981,7 @@ function PlanOptions({state}: {state: State}) { value={state.planOptions.penDownCorneringFactor} step="0.01" min="0" - onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {penDownCorneringFactor: Number(e.target.value)}})} + onChange={(e) => dispatch({ type: "SET_PLAN_OPTION", value: { penDownCorneringFactor: Number(e.target.value) } })} />
@@ -992,7 +992,7 @@ function PlanOptions({state}: {state: State}) { value={state.planOptions.penUpAcceleration} step="0.1" min="0" - onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {penUpAcceleration: Number(e.target.value)}})} + onChange={(e) => dispatch({ type: "SET_PLAN_OPTION", value: { penUpAcceleration: Number(e.target.value) } })} />
@@ -1014,7 +1014,7 @@ function PlanOptions({state}: {state: State}) { value={state.planOptions.penLiftDuration} step="0.01" min="0" - onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {penLiftDuration: Number(e.target.value)}})} + onChange={(e) => dispatch({ type: "SET_PLAN_OPTION", value: { penLiftDuration: Number(e.target.value) } })} />
@@ -1103,19 +1103,19 @@ function Root() { useEffect(() => { if (driver == null) return; driver.onprogress = (motionIdx: number) => { - dispatch({type: "SET_PROGRESS", motionIdx}); + dispatch({ type: "SET_PROGRESS", motionIdx }); }; driver.oncancelled = driver.onfinished = () => { - dispatch({type: "SET_PROGRESS", motionIdx: null}); + dispatch({ type: "SET_PROGRESS", motionIdx: null }); }; driver.onconnectionchange = (connected: boolean) => { - dispatch({type: "SET_CONNECTED", connected}); + dispatch({ type: "SET_CONNECTED", connected }); }; driver.ondevinfo = (devInfo: DeviceInfo) => { - dispatch({type: "SET_DEVICE_INFO", value: devInfo}); + dispatch({ type: "SET_DEVICE_INFO", value: devInfo }); }; driver.onpause = (paused: boolean) => { - dispatch({type: "SET_PAUSED", value: paused}); + dispatch({ type: "SET_PAUSED", value: paused }); }; driver.onplan = (plan: Plan) => { setPlan(plan); @@ -1219,7 +1219,7 @@ function Root() {
@@ -1256,7 +1256,7 @@ function withSVG(svgString: string, fn: (svg: SVGSVGElement) => T): T { function readSvg(svgString: string): Vec2[][] { return withSVG(svgString, flattenSVG).map((line) => { - const a = line.points.map(([x, y]: [number, number]) => ({x, y})); + const a = line.points.map(([x, y]: [number, number]) => ({ x, y })); (a as any).stroke = line.stroke; (a as any).groupId = line.groupId; return a; diff --git a/src/util.ts b/src/util.ts index f14bc416..5fe69d9f 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,5 @@ -import type {PaperSize} from "./paper-size"; -import {vadd, type Vec2, vlen2, vmul, vsub} from "./vec"; +import type { PaperSize } from "./paper-size"; +import { vadd, type Vec2, vlen2, vmul, vsub } from "./vec"; /** Format a smallish duration in 2h30m15s form */ export function formatDuration(seconds: number): string { @@ -28,7 +28,7 @@ function extent(pointLists: Vec2[][]): [Vec2, Vec2] { if (p.y < minY) { minY = p.y; } } } - return [{x: minX, y: minY}, {x: maxX, y: maxY}]; + return [{ x: minX, y: minY }, { x: maxX, y: maxY }]; } /** @@ -54,8 +54,8 @@ function scaleToFit(pointLists: Vec2[][], targetMin: Vec2, targetMax: Vec2): Vec export function scaleToPaper(pointLists: Vec2[][], paperSize: PaperSize, marginMm: number): Vec2[][] { return scaleToFit( pointLists, - {x: marginMm, y: marginMm}, - vsub(paperSize.size, {x: marginMm, y: marginMm}) + { x: marginMm, y: marginMm }, + vsub(paperSize.size, { x: marginMm, y: marginMm }) ); } @@ -123,7 +123,7 @@ function cropLineToAabb(pointList: Vec2[], aabb: [Vec2, Vec2]): Vec2[][] { const truncatedPointLists: Vec2[][] = [] let currentPointList: Vec2[] | null = null for (let i = 1; i < pointList.length; i++) { - const [a, b] = [pointList[i-1], pointList[i]] + const [a, b] = [pointList[i - 1], pointList[i]] const truncated = truncate(aabb, [a, b]) if (truncated) { if (!currentPointList) { @@ -147,8 +147,8 @@ function cropLineToAabb(pointList: Vec2[], aabb: [Vec2, Vec2]): Vec2[][] { * Crops a drawing so it is kept entirely within the given margin. */ export function cropToMargins(pointLists: Vec2[][], paperSize: PaperSize, marginMm: number): Vec2[][] { - const pageAabb: [Vec2, Vec2] = [{x: 0, y: 0}, paperSize.size] - const margin = {x: marginMm, y: marginMm} + const pageAabb: [Vec2, Vec2] = [{ x: 0, y: 0 }, paperSize.size] + const margin = { x: marginMm, y: marginMm } const insetAabb: [Vec2, Vec2] = [vadd(pageAabb[0], margin), vsub(pageAabb[1], margin)] const truncatedPointLists: Vec2[][] = [] for (const pointList of pointLists) { diff --git a/src/vec.ts b/src/vec.ts index e4c6bf16..52163bdd 100644 --- a/src/vec.ts +++ b/src/vec.ts @@ -10,16 +10,16 @@ export function vlen(a: Vec2): number { return Math.sqrt(vlen2(a)); } export function vsub(a: Vec2, b: Vec2): Vec2 { - return {x: a.x - b.x, y: a.y - b.y}; + return { x: a.x - b.x, y: a.y - b.y }; } export function vmul(a: Vec2, s: number): Vec2 { - return {x: a.x * s, y: a.y * s}; + return { x: a.x * s, y: a.y * s }; } export function vnorm(a: Vec2): Vec2 { return vmul(a, 1 / vlen(a)); } export function vadd(a: Vec2, b: Vec2): Vec2 { - return {x: a.x + b.x, y: a.y + b.y}; + return { x: a.x + b.x, y: a.y + b.y }; } export function vdot(a: Vec2, b: Vec2): number { return a.x * b.x + a.y * b.y; @@ -34,5 +34,5 @@ export function vrot(v: Vec2, c: Vec2, a: number): Vec2 { nx = cos * (v.x - c.x) - sin * (v.y - c.y) + c.x, ny = cos * (v.y - c.y) + sin * (v.x - c.x) + c.y; - return {x:nx, y:ny}; + return { x:nx, y:ny }; }