Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: nornagon/saxi
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v0.15.0
Choose a base ref
...
head repository: nornagon/saxi
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: main
Choose a head ref

Commits on Jun 8, 2022

  1. ci: use github actions

    nornagon authored Jun 8, 2022
    Copy the full SHA
    f31840f View commit details

Commits on Jun 30, 2023

  1. Copy the full SHA
    f4a526b View commit details
  2. Copy the full SHA
    00cef01 View commit details
  3. Copy the full SHA
    94e489c View commit details
  4. chore: fix imports (#145)

    alexrudd2 authored Jun 30, 2023
    Copy the full SHA
    b1996eb View commit details

Commits on Jul 1, 2023

  1. Copy the full SHA
    a272d78 View commit details
  2. Copy the full SHA
    c22d11a View commit details
  3. chore(deps-dev): bump semver from 5.6.0 to 7.5.2 (#152)

    Bumps [semver](https://github.com/npm/node-semver) from 5.6.0 to 7.5.2.
    - [Release notes](https://github.com/npm/node-semver/releases)
    - [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
    - [Commits](npm/node-semver@v5.6.0...v7.5.2)
    
    ---
    updated-dependencies:
    - dependency-name: semver
      dependency-type: direct:development
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jul 1, 2023
    Copy the full SHA
    e0cd773 View commit details
  4. test: fix ebb tests

    nornagon committed Jul 1, 2023
    Copy the full SHA
    45ee2f0 View commit details
  5. chore: fix bad autoimports

    grrr vscode
    nornagon committed Jul 1, 2023
    Copy the full SHA
    f1285d9 View commit details
  6. 1
    Copy the full SHA
    04d7e28 View commit details

Commits on Jul 10, 2023

  1. fix: set executable bit to fix 'npx' (#153)

    * chmod +x
    
    * ignore VSCode settings
    alexrudd2 authored Jul 10, 2023
    Copy the full SHA
    a55539d View commit details
  2. chore(deps): bump json5 from 1.0.1 to 1.0.2 (#146)

    Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
    - [Release notes](https://github.com/json5/json5/releases)
    - [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
    - [Commits](json5/json5@v1.0.1...v1.0.2)
    
    ---
    updated-dependencies:
    - dependency-name: json5
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jul 10, 2023
    Copy the full SHA
    98f7fa5 View commit details
  3. chore: upgrade style-loader to fix CSS (#155)

    * chore: upgrade style-loader to fix CSS
    
    * update package-lock
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 10, 2023
    Copy the full SHA
    f8ca2cd View commit details
  4. fix(deps): update dependency ws to v8 (#158)

    * chore(deps): upgrade websockets to v8
    
    * update package-lock
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 10, 2023
    Copy the full SHA
    28fb900 View commit details
  5. chore(deps): upgrade typescript to v5 (#159)

    * chore(deps): upgrade typescript to v5
    
    * update package-lock
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 10, 2023
    Copy the full SHA
    b0f76df View commit details
  6. chore(deps): update react monorepo to v18 (major) (#156)

    * chore: update react monorepo to v18 (major)
    
    * update package-lock
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 10, 2023
    Copy the full SHA
    7931503 View commit details
  7. chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#128)

    Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
    - [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
    - [Commits](SamVerschueren/decode-uri-component@v0.2.0...v0.2.2)
    
    ---
    updated-dependencies:
    - dependency-name: decode-uri-component
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jul 10, 2023
    Copy the full SHA
    c9b1504 View commit details
  8. chore(deps): bump qs and express (#161)

    Bumps [qs](https://github.com/ljharb/qs) to 6.11.0 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together.
    
    
    Updates `qs` from 6.5.2 to 6.11.0
    - [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
    - [Commits](ljharb/qs@v6.5.2...v6.11.0)
    
    Updates `express` from 4.16.4 to 4.18.2
    - [Release notes](https://github.com/expressjs/express/releases)
    - [Changelog](https://github.com/expressjs/express/blob/master/History.md)
    - [Commits](expressjs/express@4.16.4...4.18.2)
    
    ---
    updated-dependencies:
    - dependency-name: qs
      dependency-type: indirect
    - dependency-name: express
      dependency-type: direct:production
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jul 10, 2023
    Copy the full SHA
    9294030 View commit details

Commits on Jul 12, 2023

  1. Copy the full SHA
    33fbd4f View commit details
  2. chore: Switch React API from v17 to v18 (#168)

    * (fix): Switch React API from v17 to v18
    
    * Update src/ui.tsx
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 12, 2023
    Copy the full SHA
    42bee3a View commit details
  3. chore(deps): update dependency ts-loader to v9 (#163)

    * chore(deps): update dependency ts-loader to v9
    
    * update package-lock
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 12, 2023
    Copy the full SHA
    4a1d1b0 View commit details
  4. chore(deps): update dependency rimraf to v5 (#164)

    * chore(deps): update dependency rimraf to v5
    
    * update package-lock
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 12, 2023
    Copy the full SHA
    f6b7c1d View commit details
  5. chore(deps): update dependency worker-loader to v3 (#166)

    * chore(deps): update dependency worker-loader to v3
    
    * update package-lock
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 12, 2023
    Copy the full SHA
    f821a09 View commit details
  6. chore(deps): update dependency file-loader to v6 (#165)

    * chore(deps): update dependency file-loader to v6
    
    * update package-lock
    
    * revert
    
    * update package-lock
    
    ---------
    
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    alexrudd2 and nornagon authored Jul 12, 2023
    Copy the full SHA
    a11c89a View commit details

Commits on Jul 14, 2023

  1. chore(deps): update dependency @types/node to 14.x (#170)

    * chore(deps): update dependency @types/node to 14.x
    
    * Specifiy minimum node version
    alexrudd2 authored Jul 14, 2023
    Copy the full SHA
    5f5dcf3 View commit details
  2. Copy the full SHA
    53b14a0 View commit details

Commits on Oct 26, 2023

  1. chore(deps-dev): bump @babel/traverse from 7.22.5 to 7.23.2 (#196)

    Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.5 to 7.23.2.
    - [Release notes](https://github.com/babel/babel/releases)
    - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
    - [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)
    
    ---
    updated-dependencies:
    - dependency-name: "@babel/traverse"
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Oct 26, 2023
    Copy the full SHA
    da69dfd View commit details
  2. chore(deps-dev): bump postcss from 8.4.24 to 8.4.31 (#190)

    Bumps [postcss](https://github.com/postcss/postcss) from 8.4.24 to 8.4.31.
    - [Release notes](https://github.com/postcss/postcss/releases)
    - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
    - [Commits](postcss/postcss@8.4.24...8.4.31)
    
    ---
    updated-dependencies:
    - dependency-name: postcss
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Oct 26, 2023
    Copy the full SHA
    62b7ef0 View commit details
  3. fix: --firmware-version not opening port (#176)

    * Call port.open() before querying it
    
    * Close the port
    alexrudd2 authored Oct 26, 2023
    Copy the full SHA
    09ccd5f View commit details
  4. Copy the full SHA
    3b770c3 View commit details
  5. fix: only attempt to acquire wakelock on macos (#189)

    Co-authored-by: Jonathan Dahan <github@jonathan.is>
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    3 people authored Oct 26, 2023
    Copy the full SHA
    6c9b463 View commit details
  6. Copy the full SHA
    6923fe9 View commit details
  7. Copy the full SHA
    73ebd74 View commit details
  8. feat: UI tweaks (#193)

    alexrudd2 authored Oct 26, 2023
    Copy the full SHA
    46cfa7a View commit details
  9. chore(deps): Update to serialport v11 (#160)

    * Update to node-serialport v11
    
    * package-lock to pass CI
    alexrudd2 authored Oct 26, 2023
    Copy the full SHA
    d28d2c3 View commit details

Commits on Nov 15, 2023

  1. Pi-Zero: OTG configuration (#180)

    Document how to setup the Pi Zero with a special "OTG" cable to power the
    Pi from the AxiDraw's servo regulator and connect the Pi's Micro USB to
    the AxiDraw's Micro USB.
    
    Signed-off-by: Trammell Hudson <hudson@trmm.net>
    osresearch authored Nov 15, 2023
    Copy the full SHA
    0b42938 View commit details

Commits on Apr 2, 2024

  1. docs: README: use systemd to start saxi on the pi (#181)

    This patch adds documentation for starting saxi as a systemd unit
    on the Raspberry Pi so that it runs at boot time and will restart if
    the process exits for some reason.
    
    Signed-off-by: Trammell Hudson <hudson@trmm.net>
    Co-authored-by: Jeremy Rose <nornagon@nornagon.net>
    osresearch and nornagon authored Apr 2, 2024
    Copy the full SHA
    9b6829e View commit details

Commits on Apr 4, 2024

  1. chore(deps): bump express from 4.18.2 to 4.19.2 (#207)

    Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
    - [Release notes](https://github.com/expressjs/express/releases)
    - [Changelog](https://github.com/expressjs/express/blob/master/History.md)
    - [Commits](expressjs/express@4.18.2...4.19.2)
    
    ---
    updated-dependencies:
    - dependency-name: express
      dependency-type: direct:production
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Apr 4, 2024
    Copy the full SHA
    ef29e59 View commit details
  2. chore(deps): bump es5-ext from 0.10.53 to 0.10.63 (#206)

    Bumps [es5-ext](https://github.com/medikoo/es5-ext) from 0.10.53 to 0.10.63.
    - [Release notes](https://github.com/medikoo/es5-ext/releases)
    - [Changelog](https://github.com/medikoo/es5-ext/blob/main/CHANGELOG.md)
    - [Commits](medikoo/es5-ext@v0.10.53...v0.10.63)
    
    ---
    updated-dependencies:
    - dependency-name: es5-ext
      dependency-type: indirect
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Apr 4, 2024
    Copy the full SHA
    31f59ac View commit details

Commits on Jan 20, 2025

  1. new maintainer notice

    alexrudd2 committed Jan 20, 2025
    Copy the full SHA
    48bc62b View commit details
Showing with 8,052 additions and 15,039 deletions.
  1. +0 −24 .circleci/config.yml
  2. +7 −1 .eslintrc.js
  3. +30 −0 .github/workflows/node.js.yml
  4. +1 −0 .gitignore
  5. +57 −0 README.md
  6. 0 cli.js
  7. BIN docs/pi-zero.jpg
  8. +7,740 −14,767 package-lock.json
  9. +28 −26 package.json
  10. +20 −64 src/__tests__/ebb.test.ts
  11. +0 −1 src/background-planner.ts
  12. +7 −6 src/ebb.ts
  13. +0 −1 src/global.d.ts
  14. +1 −1 src/paper-size.ts
  15. +4 −4 src/planning.ts
  16. +1 −0 src/regex-transform-stream.ts
  17. +11 −6 src/serialport-serialport.ts
  18. +40 −32 src/server.ts
  19. +8 −4 src/style.css
  20. +93 −81 src/ui.tsx
  21. +2 −2 src/util.ts
  22. +1 −1 src/vec.ts
  23. +1 −0 tsconfig.web.json
  24. +0 −18 tslint.json
24 changes: 0 additions & 24 deletions .circleci/config.yml

This file was deleted.

8 changes: 7 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
],
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
project: './tsconfig.json',
},
root: true,
rules: {
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
// e.g. "@typescript-eslint/explicit-function-return-type": "off",
@@ -27,4 +29,8 @@ module.exports = {
version: 'detect',
},
},
ignorePatterns: [
'*.js',
'__tests__',
],
};
30 changes: 30 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [16.x, 18.x, 20.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: npm test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/
dist/
.eslintcache
.vscode
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# saxi
##### make plot good

### ARCHIVED
!! NOTE !! Development has moved to https://github.com/alexrudd2/saxi

saxi is a tool for interacting with the [AxiDraw
drawing machine](https://axidraw.com/) by Evil Mad Scientist. It comes with an
easy-to-use interface, and is exactingly precise.
@@ -34,6 +37,60 @@ $ sudo apt-get install -y nodejs

and then proceed as above :) If you connect to the raspberry pi over ssh, you might want to run the `saxi` server inside a tmux or screen session to have it stay running even if your ssh session disconnects.

If you want `saxi` to run at boot on the Pi you can use a systemd unit file and enable the service:

```bash
sudo tee /lib/systemd/system/saxi.service <<EOF
[Unit]
Description=Saxi
After=network.target
[Service]
ExecStart=saxi
Restart=always
User=pi
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl enable saxi.service
```

To watch the logs while it is running, use:
```bash
journalctl -f -u saxi
```

#### Raspberry Pi Zero OTG

![Pi Zero on an AxiDraw with a Y-shaped USB cable](docs/pi-zero.jpg)

For the Pi Zero you can make a USB "OTG" cable out of two Micro-B cables and two 0.1" headers
to tap into the AxiDraw's 5V servo supply to power the Pi. This makes for a more compact
installation without the need for an additional wall-wart.


```
+------ Center pin on servo rail
| +---- Ground pin on servo rail
| |
| |
Red -----+-|---- Red
Black -------+---- Black
White ------------ White
Green ------------ Green (sometimes Blue)
```

The Pi will also need to have the `dr_mode=host` parameter set in
`config.txt` to force host mode, since normal USB Micro cables do not
include the `ID` pin that would be used to signal that it is an OTG
connection.

```
echo dtoverly=dwc2,dr_mode=host | sudo tee -a /boot/config.txt
```


#### CORS
If you want to connect to saxi from a web page that isn't served by saxi
itself, you'll need to enable
Empty file modified cli.js
100644 → 100755
Empty file.
Binary file added docs/pi-zero.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22,507 changes: 7,740 additions & 14,767 deletions package-lock.json

Large diffs are not rendered by default.

54 changes: 28 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
@@ -35,49 +35,51 @@
"@rehooks/component-size": "^1.0.2",
"@types/cors": "^2.8.4",
"@types/express": "^4.16.1",
"@types/jest": "^26.0.22",
"@types/node": "^10.17.28",
"@types/react": "^16.7.20",
"@types/react-dom": "^16.0.11",
"@types/serialport": "^8.0.0",
"@types/jest": "^29.0.0",
"@types/node": "^14.0.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/w3c-web-serial": "^1.0.2",
"@types/ws": "^6.0.1",
"@types/ws": "8.0.0 - 8.5.4",
"@types/yargs": "^12.0.8",
"@typescript-eslint/eslint-plugin": "^1.5.0",
"@typescript-eslint/parser": "^1.5.0",
"@typescript-eslint/eslint-plugin": "^5.15.0",
"@typescript-eslint/parser": "^5.15.0",
"color-interpolate": "^1.0.5",
"colormap": "^2.3.2",
"css-loader": "^2.1.1",
"eslint": "^5.15.3",
"css-loader": "^6.0.0",
"eslint": "^8.0.0",
"eslint-plugin-react": "^7.12.4",
"file-loader": "^3.0.1",
"file-loader": "^6.0.0",
"gh-pages": "^3.2.3",
"html-webpack-plugin": "^3.2.0",
"jest": "^26.6.3",
"react": "^16.8.0-alpha.1",
"react-dom": "^16.8.0-alpha.1",
"rimraf": "^3.0.2",
"semver": "^5.6.0",
"style-loader": "^0.23.1",
"ts-jest": "^26.0.0",
"ts-loader": "^5.3.3",
"typescript": "^3.8.3",
"webpack": "^4.23.1",
"webpack-cli": "^3.3.12",
"worker-loader": "^2.0.0"
"html-webpack-plugin": "^5.0.0",
"jest": "^29.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"rimraf": "^5.0.0",
"semver": "^7.5.2",
"style-loader": "^3.0.0",
"ts-jest": "^29.0.0",
"ts-loader": "^9.0.0",
"typescript": "~5.0",
"webpack": "^5.0.0",
"webpack-cli": "^5.0.0",
"worker-loader": "^3.0.0"
},
"dependencies": {
"cors": "^2.8.5",
"express": "^4.16.4",
"flatten-svg": "^0.2.1",
"optimize-paths": "^1.2.2",
"serialport": "^9.2.0",
"serialport": "^11.0.0",
"svgdom": "0.0.21",
"wake-lock": "^0.2.0",
"web-streams-polyfill": "^3.0.3",
"ws": "^7.4.6",
"ws": "^8.0.0",
"yargs": "^15.4.1"
},
"engines": {
"node": ">=16.0.0"
},
"jest": {
"preset": "ts-jest"
},
84 changes: 20 additions & 64 deletions src/__tests__/ebb.test.ts
Original file line number Diff line number Diff line change
@@ -1,87 +1,43 @@
import {EBB} from "../ebb";
import SerialPort from "serialport";
import MockBinding from "@serialport/binding-mock";

(() => {
let oldBinding: any;
beforeAll(() => {
oldBinding = SerialPort.Binding;
SerialPort.Binding = MockBinding;
});
afterAll(() => {
SerialPort.Binding = oldBinding;
MockBinding.reset();
});
})();

describe("EBB.list", () => {
afterEach(() => {
MockBinding.reset();
})

it("is empty when no serial ports are available", async () => {
expect(await EBB.list()).toEqual([])
})

it("doesn't return a port that doesn't look like an EBB", async () => {
MockBinding.createPort('/dev/nonebb');
expect(await EBB.list()).toEqual([])
})
const { SerialPortMock: SerialPort } = require('serialport')
import { MockBinding } from '@serialport/binding-mock';

it("returns a port that does look like an EBB", async () => {
MockBinding.createPort('/dev/ebb', { manufacturer: "SchmalzHaus" });
expect(await EBB.list()).toEqual(["/dev/ebb"])
})

it("handles 'SchmalzHaus LLC'", async () => {
MockBinding.createPort('/dev/ebb', { manufacturer: "SchmalzHaus LLC" });
expect(await EBB.list()).toEqual(["/dev/ebb"])
})

it("handles no manufacturer but vendor id / product id", async () => {
MockBinding.createPort('/dev/ebb', { vendorId: "04D8", productId: "FD92" });
expect(await EBB.list()).toEqual(["/dev/ebb"])
})
})
jest.doMock('serialport', () => ({
SerialPort: SerialPort,
}));
import {EBB} from "../ebb";
import {SerialPortSerialPort} from "../serialport-serialport";

describe("EBB", () => {
afterEach(() => {
MockBinding.reset();
})

type TestPort = SerialPort & {
binding: SerialPort.BaseBinding & {
recording: Buffer;
emitData: (data: Buffer) => void;
};
};

async function openTestPort(path = '/dev/ebb'): Promise<TestPort> {
async function openTestPort(path = '/dev/ebb'): Promise<SerialPort> {
MockBinding.createPort(path, {record: true});
const port = new SerialPort(path);
await new Promise(resolve => port.on('open', resolve));
const port = new SerialPortSerialPort(path);
await port.open({ baudRate: 9600 })
return port as any;
}

it("firmware version", async () => {
const port = await openTestPort();
const ebb = new EBB(port);
port.binding.emitData(Buffer.from('aoeu\r\n'));
((port as any)._port.port).emitData(Buffer.from('aoeu\r\n'));
expect(await ebb.firmwareVersion()).toEqual('aoeu');
expect(port.binding.recording).toEqual(Buffer.from("V\r"));
expect((port as any)._port.port.recording).toEqual(Buffer.from("V\r"));
})

it("enable motors", async () => {
const port = await openTestPort();
const ebb = new EBB(port);
const oldWrite = port.write
port.write = (data: string | Buffer | number[], ...args: any[]) => {
if (data === 'V\r')
port.binding.emitData(Buffer.from('test 2.5.3\r\n'))
return oldWrite.apply(port, [data, ...args])
}
port.binding.emitData(Buffer.from('OK\r\n'));
const oldWrite = (port as any)._port.write;
(port as any)._port.write = (data: string | Buffer | number[], ...args: any[]) => {
if (data.toString() === 'V\r')
(port as any)._port.port.emitData(Buffer.from('test 2.5.3\r\n'))
return oldWrite.apply((port as any)._port, [data, ...args])
};
(port as any)._port.port.emitData(Buffer.from('OK\r\n'));
await ebb.enableMotors(2);
expect(port.binding.recording).toEqual(Buffer.from("EM,2,2\rV\r"));
expect((port as any)._port.port.recording).toEqual(Buffer.from("EM,2,2\rV\r"));
})
})
1 change: 0 additions & 1 deletion src/background-planner.ts
Original file line number Diff line number Diff line change
@@ -9,7 +9,6 @@ self.addEventListener("message", (m) => {
(self as any).postMessage(serialized);
});

// eslint-disable-next-line @typescript-eslint/no-object-literal-type-assertion
export default {} as typeof Worker & {
new(): Worker;
};
13 changes: 7 additions & 6 deletions src/ebb.ts
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ export class EBB {
private writer: WritableStreamDefaultWriter<Uint8Array>;
private readableClosed: Promise<void>

private microsteppingMode: number = 0;
private microsteppingMode = 0;

/** Accumulated XY error, used to correct for movements with sub-step resolution */
private error: Vec2 = {x: 0, y: 0};
@@ -24,7 +24,7 @@ export class EBB {

public constructor(port: SerialPort) {
this.port = port;
this.writer = this.port.writable.getWriter()
this.writer = this.port.writable.getWriter();
this.commandQueue = [];
this.readableClosed = port.readable
.pipeThrough(new RegexParser({ regex: /[\r\n]+/ }))
@@ -66,7 +66,7 @@ export class EBB {
}

public async close(): Promise<void> {
throw new Error("TODO")
return await this.port.close()
}

private write(str: string): Promise<void> {
@@ -155,7 +155,7 @@ export class EBB {
await this.command(`SR,${(timeout * 1000) | 0}${power != null ? `,${power ? 1 : 0}` : ''}`)
}

public setPenHeight(height: number, rate: number, delay: number = 0): Promise<void> {
public setPenHeight(height: number, rate: number, delay = 0): Promise<void> {
return this.command(`S2,${height},4,${rate},${delay}`);
}

@@ -221,6 +221,7 @@ export class EBB {
}

public async waitUntilMotorsIdle(): Promise<void> {
// eslint-disable-next-line no-constant-condition
while (true) {
const [, commandStatus, _motor1Status, _motor2Status, fifoStatus] = (await this.query("QM")).split(",");
if (commandStatus === "0" && fifoStatus === "0") {
@@ -260,7 +261,7 @@ export class EBB {
* This is less accurate than using LM, since acceleration will only be adjusted every timestepMs milliseconds,
* where LM can adjust the acceleration at a much higher rate, as it executes on-board the EBB.
*/
public async executeXYMotionWithXM(plan: XYMotion, timestepMs: number = 15): Promise<void> {
public async executeXYMotionWithXM(plan: XYMotion, timestepMs = 15): Promise<void> {
const timestepSec = timestepMs / 1000;
let t = 0;
while (t < plan.duration()) {
@@ -310,7 +311,7 @@ export class EBB {
}
}

public async executePlan(plan: Plan, microsteppingMode: number = 2): Promise<void> {
public async executePlan(plan: Plan, microsteppingMode = 2): Promise<void> {
await this.enableMotors(microsteppingMode);

for (const m of plan.motions) {
1 change: 0 additions & 1 deletion src/global.d.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ declare module '*.svg' {
}
declare module 'svgdom';
declare module 'wake-lock';
declare module '@serialport/binding-mock';
declare module 'color-interpolate';
declare module 'colormap';

2 changes: 1 addition & 1 deletion src/paper-size.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Vec2, vmul} from "./vec";

function vround(v: Vec2, digits: number = 2): Vec2 {
function vround(v: Vec2, digits = 2): Vec2 {
return { x: Number(v.x.toFixed(digits)), y: Number(v.y.toFixed(digits)) };
}

8 changes: 4 additions & 4 deletions src/planning.ts
Original file line number Diff line number Diff line change
@@ -151,7 +151,7 @@ export class Block {

public get vFinal(): number { return Math.max(0, this.vInitial + this.accel * this.duration); }

public instant(tU: number, dt: number= 0, ds: number= 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;
@@ -277,7 +277,7 @@ export class Plan {
public constructor(motions: Motion[]) {
this.motions = motions;
}
public duration(start: number = 0): number {
public duration(start = 0): number {
return this.motions.slice(start).map((m) => m.duration()).reduce((a, b) => a + b, 0);
}
public motion(i: number) { return this.motions[i]; }
@@ -313,8 +313,8 @@ export class Plan {
class Segment {
public p1: Vec2;
public p2: Vec2;
public maxEntryVelocity: number = 0;
public entryVelocity: number = 0;
public maxEntryVelocity = 0;
public entryVelocity = 0;
public blocks: Block[];

public constructor(p1: Vec2, p2: Vec2) {
1 change: 1 addition & 0 deletions src/regex-transform-stream.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "web-streams-polyfill/es2018"
export class RegexParser extends TransformStream {
public constructor(opts: { regex: RegExp }) {
if (opts.regex === undefined) {
17 changes: 11 additions & 6 deletions src/serialport-serialport.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { EventEmitter } from "events";
import { default as NodeSerialPort } from "serialport";
import { SerialPort as NodeSerialPort } from "serialport";
import { OpenOptions } from "@serialport/bindings-interface"

function readableStreamFromAsyncIterable<T>(iterable: AsyncIterable<T>) {
const it = iterable[Symbol.asyncIterator]();
@@ -17,6 +18,9 @@ function readableStreamFromAsyncIterable<T>(iterable: AsyncIterable<T>) {
}
}, { highWaterMark: 0 });
}
interface SerialPortOpenOptions extends Omit<OpenOptions, 'parity'> {
parity?: ParityType;
}

export class SerialPortSerialPort extends EventEmitter implements SerialPort {
private _path: string;
@@ -33,8 +37,9 @@ export class SerialPortSerialPort extends EventEmitter implements SerialPort {
public writable: WritableStream<Uint8Array>;

public open(options: SerialOptions): Promise<void> {
const opts: NodeSerialPort.OpenOptions = {
const opts: SerialPortOpenOptions = {
baudRate: options.baudRate,
path: this._path,
}
if (options.dataBits != null)
opts.dataBits = options.dataBits as any
@@ -49,7 +54,7 @@ export class SerialPortSerialPort extends EventEmitter implements SerialPort {
flowControl?: FlowControlType | undefined;
*/
return new Promise((resolve, reject) => {
this._port = new NodeSerialPort(this._path, opts, (err) => {
this._port = new NodeSerialPort(opts, (err: any) => {
this._port.once('close', () => this.emit('disconnect'))
if (err) reject(err)
else {
@@ -62,7 +67,7 @@ export class SerialPortSerialPort extends EventEmitter implements SerialPort {
this.writable = new WritableStream({
write: (chunk) => {
return new Promise((resolve, reject) => {
this._port.write(Buffer.from(chunk), (err, _bytesWritten) => {
this._port.write(Buffer.from(chunk), (err: Error) => {
if (err) reject(err)
else resolve()
// TODO: check bytesWritten?
@@ -78,7 +83,7 @@ export class SerialPortSerialPort extends EventEmitter implements SerialPort {
dtr: signals.dataTerminalReady,
rts: signals.requestToSend,
brk: signals.break
}, (err) => {
}, (err: Error) => {
if (err) reject(err)
else resolve()
})
@@ -92,7 +97,7 @@ export class SerialPortSerialPort extends EventEmitter implements SerialPort {
}
public close(): Promise<void> {
return new Promise((resolve, reject) => {
this._port.close((err) => {
this._port.close((err: Error) => {
if (err) reject(err)
else resolve()
})
72 changes: 40 additions & 32 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -3,15 +3,16 @@ import "web-streams-polyfill/es2018"
import express from "express";
import http from "http";
import path from "path";
import { default as NodeSerialPort } from "serialport";
import { PortInfo } from "@serialport/bindings-interface"
import { WakeLock } from "wake-lock";
import WebSocket from "ws";
import { SerialPortSerialPort } from "./serialport-serialport";
import { EBB } from "./ebb";
import { Device, PenMotion, Motion, Plan } from "./planning";
import { formatDuration } from "./util";
import { autoDetect } from '@serialport/bindings-cpp';

export function startServer(port: number, device: string | null = null, enableCors: boolean = false, maxPayloadSize: string = "200mb") {
export function startServer(port: number, device: string | null = null, enableCors = false, maxPayloadSize = "200mb") {
const app = express();

app.use("/", express.static(path.join(__dirname, "..", "ui")));
@@ -35,26 +36,24 @@ export function startServer(port: number, device: string | null = null, enableCo
wss.on("connection", (ws) => {
clients.push(ws);
ws.on("message", (message) => {
if (typeof message === "string") {
const msg = JSON.parse(message);
switch (msg.c) {
case "ping":
ws.send(JSON.stringify({c: "pong"}));
break;
case "limp":
if (ebb) { ebb.disableMotors(); }
break;
case "setPenHeight":
if (ebb) {
(async () => {
if (await ebb.supportsSR()) {
await ebb.setServoPowerTimeout(10000, true)
}
await ebb.setPenHeight(msg.p.height, msg.p.rate);
})();
}
break;
}
const msg = JSON.parse(message.toString());
switch (msg.c) {
case "ping":
ws.send(JSON.stringify({c: "pong"}));
break;
case "limp":
if (ebb) { ebb.disableMotors(); }
break;
case "setPenHeight":
if (ebb) {
(async () => {
if (await ebb.supportsSR()) {
await ebb.setServoPowerTimeout(10000, true)
}
await ebb.setPenHeight(msg.p.height, msg.p.rate);
})();
}
break;
}
});

@@ -87,10 +86,13 @@ export function startServer(port: number, device: string | null = null, enableCo

const begin = Date.now();
let wakeLock: any;
try {
wakeLock = new WakeLock("saxi plotting");
} catch (e) {
console.warn("Couldn't acquire wake lock. Ensure your machine does not sleep during plotting");
// The wake-lock module is macOS-only.
if (process.platform === 'darwin') {
try {
wakeLock = new WakeLock("saxi plotting");
} catch (e) {
console.warn("Couldn't acquire wake lock. Ensure your machine does not sleep during plotting");
}
}

try {
@@ -149,7 +151,7 @@ export function startServer(port: number, device: string | null = null, enableCo
executeMotion: (m: Motion, progress: [number, number]) => Promise<void>;
postCancel: () => Promise<void>;
postPlot: () => Promise<void>;
};
}

const realPlotter: Plotter = {
async prePlot(initialPenHeight: number): Promise<void> {
@@ -169,6 +171,7 @@ export function startServer(port: number, device: string | null = null, enableCo
};

const simPlotter: Plotter = {
// eslint-disable-next-line @typescript-eslint/no-empty-function
async prePlot(_initialPenHeight: number): Promise<void> {
},
async executeMotion(motion: Motion, progress: [number, number]): Promise<void> {
@@ -178,6 +181,7 @@ export function startServer(port: number, device: string | null = null, enableCo
async postCancel(): Promise<void> {
console.log("Plot cancelled");
},
// eslint-disable-next-line @typescript-eslint/no-empty-function
async postPlot(): Promise<void> {
},
};
@@ -245,16 +249,18 @@ function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

function isEBB(p: NodeSerialPort.PortInfo): boolean {
function isEBB(p: PortInfo): boolean {
return p.manufacturer === "SchmalzHaus" || p.manufacturer === "SchmalzHaus LLC" || (p.vendorId == "04D8" && p.productId == "FD92");
}

async function listEBBs() {
const ports = await NodeSerialPort.list();
return ports.filter(isEBB).map((p) => p.path);
const Binding = autoDetect()
const ports = await Binding.list();
return ports.filter(isEBB).map((p: { path: any; }) => p.path);
}

async function waitForEbb() {
// eslint-disable-next-line no-constant-condition
while (true) {
const ebbs = await listEBBs();
if (ebbs.length) {
@@ -287,11 +293,13 @@ async function* ebbs(path?: string) {

export async function connectEBB(path: string | undefined): Promise<EBB | null> {
if (path) {
return new EBB(new SerialPortSerialPort(path));
const port = await tryOpen(path);
return new EBB(port);
} else {
const ebbs = await listEBBs();
if (ebbs.length) {
return new EBB(new SerialPortSerialPort(ebbs[0]));
const port = await tryOpen(ebbs[0]);
return new EBB(port);
} else {
return null;
}
12 changes: 8 additions & 4 deletions src/style.css
Original file line number Diff line number Diff line change
@@ -119,7 +119,7 @@ path {
flex: 0 0 var(--panel-width);
width: var(--panel-width);
background-color: white;
overflow-y: scroll;
overflow-y: auto;

box-shadow: 3px 3px 15px rgba(0, 0, 0, 0.2);
}
@@ -150,7 +150,7 @@ path {

.section-header {
margin-bottom: 1rem;
padding: 1rem 0;
padding: 0.5rem 0;
border-bottom: 1px solid var(--purple-light);
font-size: 18px;
font-weight: 900;
@@ -164,6 +164,11 @@ path {

/* INPUTS */

form {
display: grid;
grid-template-columns: 1fr 1fr;
}

label {
font-size: 10px;
text-align: center;
@@ -281,7 +286,6 @@ select[multiple]:focus option:checked {
.flex-checkbox {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 0.5rem;
}

@@ -315,7 +319,7 @@ select[multiple]:focus option:checked {
}

.layer-select {
height: 50px;
height: 100px;
background-color: white;
border: 1px solid var(--purple);
color: var(--purple);
174 changes: 93 additions & 81 deletions src/ui.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import useComponentSize from "@rehooks/component-size";
import React, { ChangeEvent, Fragment, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useReducer } from "react";
import ReactDOM from "react-dom";
import * as interpolator from "color-interpolate"
import * as colormap from "colormap"
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";
@@ -67,6 +67,7 @@ function reducer(state: State, action: any): State {
case "SET_PAUSED":
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}};
case "SET_PROGRESS":
@@ -115,7 +116,7 @@ class WebSerialDriver implements Driver {

private _unpaused: Promise<void> = null;
private _signalUnpause: () => void = null;
private _cancelRequested: boolean = false;
private _cancelRequested = false;

public static async connect(port?: SerialPort) {
if (!port)
@@ -239,7 +240,7 @@ class SaxiDriver implements Driver {

const websocketProtocol = document.location.protocol === "https:" ? "wss" : "ws";
this.socket = new WebSocket(`${websocketProtocol}://${document.location.host}/chat`);

this.socket.addEventListener("open", () => {
console.log(`Connected to EBB server.`);
this.connected = true;
@@ -249,46 +250,44 @@ class SaxiDriver implements Driver {
this.pingInterval = window.setInterval(() => this.ping(), 30000);
});
this.socket.addEventListener("message", (e: MessageEvent) => {
if (typeof e.data === "string") {
const msg = JSON.parse(e.data);
switch (msg.c) {
case "pong": {
// nothing
} break;
case "progress": {
if (this.onprogress != null) {
this.onprogress(msg.p.motionIdx);
}
} break;
case "cancelled": {
if (this.oncancelled != null) {
this.oncancelled();
}
} break;
case "finished": {
if (this.onfinished != null) {
this.onfinished();
}
} break;
case "dev": {
if (this.ondevinfo != null) {
this.ondevinfo(msg.p);
}
} break;
case "pause": {
if (this.onpause != null) {
this.onpause(msg.p.paused)
}
} break;
case "plan": {
if (this.onplan != null) {
this.onplan(Plan.deserialize(msg.p.plan))
}
} break;
default: {
console.log("Unknown message from server:", msg);
} break;
}
const msg = JSON.parse(e.data);
switch (msg.c) {
case "pong": {
// nothing
} break;
case "progress": {
if (this.onprogress != null) {
this.onprogress(msg.p.motionIdx);
}
} break;
case "cancelled": {
if (this.oncancelled != null) {
this.oncancelled();
}
} break;
case "finished": {
if (this.onfinished != null) {
this.onfinished();
}
} break;
case "dev": {
if (this.ondevinfo != null) {
this.ondevinfo(msg.p);
}
} break;
case "pause": {
if (this.onpause != null) {
this.onpause(msg.p.paused)
}
} break;
case "plan": {
if (this.onplan != null) {
this.onplan(Plan.deserialize(msg.p.plan))
}
} break;
default: {
console.log("Unknown message from server:", msg);
} break;
}
});
this.socket.addEventListener("error", () => {
@@ -558,7 +557,7 @@ function PaperConfig({state}: {state: State}) {
<img src={rotateDrawingIcon} alt="rotate drawing (degrees)"/>
<input type="number" min="-90" step="90" max="360" placeholder="0" value={state.planOptions.rotateDrawing}
onInput={(e) => {
let value = (e.target as HTMLInputElement).value;
const value = (e.target as HTMLInputElement).value;
if (Number(value) < 0) { (e.target as HTMLInputElement).value = "270"; }
if (Number(value) > 270) { (e.target as HTMLInputElement).value = "0"; }
}}
@@ -782,6 +781,7 @@ function LayerSelector({state}: {state: State}) {
value={[...selectedLayers]}
onChange={layersChanged}
size={3}
disabled={state.progress != null}
>
{layers.map((layer) => <option key={layer}>{layer}</option>)}
</select>
@@ -855,40 +855,42 @@ function ResetToDefaultsButton() {
function PlanOptions({state}: {state: State}) {
const dispatch = useContext(DispatchContext);
return <div>
<label className="flex-checkbox" title="Re-order paths to minimize pen-up travel time">
<input
type="checkbox"
checked={state.planOptions.sortPaths}
onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {sortPaths: !!e.target.checked}})}
/>
sort paths
</label>
<label className="flex-checkbox" title="Re-scale and position the image to fit on the page">
<input
type="checkbox"
checked={state.planOptions.fitPage}
onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {fitPage: !!e.target.checked}})}
/>
fit page
</label>
{!state.planOptions.fitPage ?
<label className="flex-checkbox" title="Remove lines that fall outside the margins">
<form>
<label className="flex-checkbox" title="Re-order paths to minimize pen-up travel time">
<input
type="checkbox"
checked={state.planOptions.cropToMargins}
onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {cropToMargins: !!e.target.checked}})}
checked={state.planOptions.sortPaths}
onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {sortPaths: !!e.target.checked}})}
/>
crop to margins
sort paths
</label>
: null}
<label className="flex-checkbox" title="Split into layers according to group ID, instead of stroke">
<input
type="checkbox"
checked={state.planOptions.layerMode === 'group'}
onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {layerMode: e.target.checked ? 'group' : 'stroke'}})}
/>
layer by group
</label>
<label className="flex-checkbox" title="Split into layers according to group ID, instead of stroke">
<input
type="checkbox"
checked={state.planOptions.layerMode === 'group'}
onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {layerMode: e.target.checked ? 'group' : 'stroke'}})}
/>
layer by group
</label>
<label className="flex-checkbox" title="Re-scale and position the image to fit on the page">
<input
type="checkbox"
checked={state.planOptions.fitPage}
onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {fitPage: !!e.target.checked}})}
/>
fit page
</label>
{!state.planOptions.fitPage ?
<label className="flex-checkbox" title="Remove lines that fall outside the margins">
<input
type="checkbox"
checked={state.planOptions.cropToMargins}
onChange={(e) => dispatch({type: "SET_PLAN_OPTION", value: {cropToMargins: !!e.target.checked}})}
/>
crop to margins
</label>
: null}
</form>
<div className="horizontal-labels">

<label title="point-joining radius (mm)" >
@@ -1044,9 +1046,18 @@ function PortSelector({driver, setDriver}: {driver: Driver; setDriver: (d: Drive
}

function Root() {
const [driver, setDriver] = useState(
IS_WEB ? null as Driver | null : SaxiDriver.connect()
)
const [driver, setDriver] = useState<Driver | null>(null);
const [isDriverConnected, setIsDriverConnected] = useState(false);
useEffect(() => {
if (isDriverConnected) return
if (IS_WEB) {
setDriver(null as Driver);
} else {
setDriver(SaxiDriver.connect());
}
setIsDriverConnected(true);
}, [isDriverConnected]);

const [state, dispatch] = useReducer(reducer, initialState);
const [isPlanning, plan, setPlan] = usePlan(state.paths, state.planOptions);
const [isLoadingFile, setIsLoadingFile] = useState(false);
@@ -1118,7 +1129,7 @@ function Root() {
document.body.removeEventListener("dragleave", ondragleave);
document.removeEventListener("paste", onpaste);
};
});
}, []);

// Each time new motion is started, save the start time
const currentMotionStartedTime = useMemo(() => {
@@ -1191,7 +1202,8 @@ function DragTarget() {
</div>;
}

ReactDOM.render(<Root />, document.getElementById("app"));
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
createRoot(document.getElementById("app")!).render(<Root />);

function withSVG<T>(svgString: string, fn: (svg: SVGSVGElement) => T): T {
const div = document.createElement("div");
4 changes: 2 additions & 2 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -69,8 +69,8 @@ function liangBarsky(aabb: [Vec2, Vec2], seg: [Vec2, Vec2]): Vec2 | null {
const delta = vsub(b, a)
const p = [-delta.x, delta.x, -delta.y, delta.y]
const q = [a.x - lower.x, upper.x - a.x, a.y - lower.y, upper.y - a.y]
var u1 = -Infinity
var u2 = Infinity
let u1 = -Infinity
let u2 = Infinity

for (let i = 0; i < 4; i++) {
if (p[i] == 0) {
2 changes: 1 addition & 1 deletion src/vec.ts
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ export function vdot(a: Vec2, b: Vec2): number {
export function vrot(v: Vec2, c: Vec2, a: number): Vec2 {
if (a === 0) return v;

let radians = (Math.PI / 180) * a,
const radians = (Math.PI / 180) * a,
cos = Math.cos(radians),
sin = Math.sin(radians),
nx = cos * (v.x - c.x) - sin * (v.y - c.y) + c.x,
1 change: 1 addition & 0 deletions tsconfig.web.json
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
"noImplicitAny": true,
"module": "es6",
"target": "es6",
"moduleResolution": "node",
"jsx": "react",
"allowJs": true
},
18 changes: 0 additions & 18 deletions tslint.json

This file was deleted.