Add WallPlotter p5 library with zigzag example.
Standalone package for Marlin polargraph control over Web Serial, including API docs and a runnable zigzag sketch. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.log
|
||||
.idea/
|
||||
.vscode/
|
||||
@@ -0,0 +1,71 @@
|
||||
# p5-wallplotter
|
||||
|
||||
p5.js library for a Marlin polargraph (wall plotter) over Web Serial, plus a zigzag example.
|
||||
|
||||
```
|
||||
p5-wallplotter/
|
||||
├── lib/
|
||||
│ ├── WallPlotter.js # library
|
||||
│ └── README.md # public API
|
||||
└── examples/
|
||||
└── zickzack/ # example sketch
|
||||
├── index.html
|
||||
└── sketch.js
|
||||
```
|
||||
|
||||
## Run the example
|
||||
|
||||
Chrome or Edge. Serve over localhost (Web Serial does not work from `file://`).
|
||||
|
||||
```bash
|
||||
cd p5-wallplotter
|
||||
python -m http.server 8080
|
||||
```
|
||||
|
||||
Open http://localhost:8080/examples/zickzack/
|
||||
|
||||
Press **C** to connect, **H** to set home, then **Test** (preview) or **Plot** (send G-code).
|
||||
|
||||
## Use in a sketch
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
|
||||
<script src="../../lib/WallPlotter.js"></script>
|
||||
<script src="sketch.js"></script>
|
||||
```
|
||||
|
||||
```javascript
|
||||
let wp;
|
||||
|
||||
function setup() {
|
||||
createCanvas(windowWidth, windowHeight).parent('sketch-host');
|
||||
wp = new WallPlotter();
|
||||
wp.setSpeed(3000, 1500);
|
||||
wp.enableKeyInputs();
|
||||
wp.enablePlotButtons();
|
||||
}
|
||||
|
||||
function draw() {
|
||||
background(28, 28, 32);
|
||||
wp.showInfos();
|
||||
}
|
||||
|
||||
async function plot() {
|
||||
await drawArt(false);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
await drawArt(true);
|
||||
}
|
||||
|
||||
async function drawArt(testOnly) {
|
||||
wp.startPath();
|
||||
wp.move(-200, -200);
|
||||
wp.draw(200, -200);
|
||||
await wp.runPath(testOnly);
|
||||
}
|
||||
```
|
||||
|
||||
Full API: [lib/README.md](lib/README.md).
|
||||
|
||||
Baud **115200**. Pen servo: `M280 P0 S90` up, `M280 P0 S50` down. Coordinates in mm, origin at home (`G92`).
|
||||
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Wall Plotter — zigzag</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
background: #1c1c20;
|
||||
color: #ddd;
|
||||
font-family: system-ui, sans-serif;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
h1 {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.75rem;
|
||||
opacity: 0.85;
|
||||
}
|
||||
main {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
p.note {
|
||||
max-width: 800px;
|
||||
margin-top: 1rem;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
opacity: 0.7;
|
||||
}
|
||||
code {
|
||||
background: #2a2a30;
|
||||
padding: 0.1em 0.35em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
|
||||
<script src="../../lib/WallPlotter.js"></script>
|
||||
<script src="sketch.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Wall Plotter — zigzag example</h1>
|
||||
<main id="sketch-host"></main>
|
||||
<p class="note">
|
||||
Serve from the <code>p5-wallplotter</code> folder:
|
||||
<code>python -m http.server 8080</code>
|
||||
then open <code>http://localhost:8080/examples/zickzack/</code>.
|
||||
Chrome or Edge. Press <code>C</code> to connect, <code>H</code> to set home.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
let wp;
|
||||
|
||||
let area = 400;
|
||||
|
||||
function setup() {
|
||||
const canvas = createCanvas(windowWidth, windowHeight);
|
||||
canvas.parent('sketch-host');
|
||||
|
||||
wp = new WallPlotter();
|
||||
wp.setSpeed(10000, 10000);
|
||||
wp.setTimingMultiplier(1); // tune if machine is slower than speed estimate, the higher the slower
|
||||
wp.enableKeyInputs();
|
||||
wp.enablePlotButtons();
|
||||
}
|
||||
|
||||
function draw() {
|
||||
background(28, 28, 32);
|
||||
wp.showInfos();
|
||||
}
|
||||
|
||||
function windowResized() {
|
||||
resizeCanvas(windowWidth, windowHeight);
|
||||
}
|
||||
|
||||
async function plot() {
|
||||
await drawArt(false);
|
||||
}
|
||||
|
||||
async function test() {
|
||||
await drawArt(true);
|
||||
}
|
||||
|
||||
async function drawArt(testOnly) {
|
||||
wp.startPath();
|
||||
|
||||
|
||||
wp.move(-area, -area);
|
||||
wp.wait(1000 * 10);
|
||||
|
||||
let step = area / 20;
|
||||
|
||||
for (let y = -area; y < area; y+=step){
|
||||
for (let x = -area; x < area; x+=step){
|
||||
if(random(1) < 0.8){
|
||||
wp.move(x, y);
|
||||
wp.draw(x, y+step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await wp.runPath(testOnly);
|
||||
}
|
||||
+215
@@ -0,0 +1,215 @@
|
||||
# WallPlotter library
|
||||
|
||||
p5 helper for a Marlin polargraph over Web Serial. Coordinates are **mm**. Origin is machine home (`G92`). Y positive is up. Chrome or Edge, page on localhost or HTTPS.
|
||||
|
||||
Load from this package:
|
||||
|
||||
```html
|
||||
<script src="../../lib/WallPlotter.js"></script>
|
||||
```
|
||||
|
||||
Example sketch: `examples/zickzack/`.
|
||||
|
||||
---
|
||||
|
||||
## `new WallPlotter(options?)`
|
||||
|
||||
All options are optional.
|
||||
|
||||
| Option | Default | What it is |
|
||||
|--------|---------|------------|
|
||||
| `baudRate` | `115200` | Serial baud (must match Marlin) |
|
||||
| `stepSize` | `10` | Keyboard jog step, mm |
|
||||
| `feedRateTravel` | `1500` | Pen-up speed, **mm/min** (G-code `F`) |
|
||||
| `feedRateDraw` | `300` | Pen-down speed, **mm/min** |
|
||||
| `penUpGcode` | `M280 P0 S90` | Command to lift pen |
|
||||
| `penDownGcode` | `M280 P0 S50` | Command to lower pen |
|
||||
| `penUpDelayMs` | `150` | Pause after pen up, ms |
|
||||
| `penDownDelayMs` | `50` | Pause after pen down, ms |
|
||||
| `machineXMin` / `machineXMax` | `-850` / `850` | Overlay X extents, mm |
|
||||
| `machineYMin` / `machineYMax` | `-1000` / `1000` | Overlay Y extents, mm |
|
||||
| `jogIntervalMs` | `150` | Repeat delay while holding arrows, ms |
|
||||
| `maxLogLines` | `8` | Serial lines shown on the HUD |
|
||||
| `previewMinDelayMs` | `30` | Floor for preview segment wait, ms |
|
||||
| `previewFrameMs` | `16` | Preview animation frame time, ms |
|
||||
| `timingMultiplier` | `1` | Scale **Test** preview waits (`2` = twice as slow) |
|
||||
| `onLog(line)` | no-op | Extra callback for each log line |
|
||||
| `onStatusChange(status)` | no-op | `'connected'` or `'disconnected'` |
|
||||
|
||||
---
|
||||
|
||||
## Setup / UI
|
||||
|
||||
### `enableKeyInputs()`
|
||||
|
||||
Keyboard controls. Call from `setup()`. No arguments.
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `C` | Connect / disconnect |
|
||||
| `H` | Set home here (`G92 X0 Y0`) — required before Plot |
|
||||
| `0` | Travel to `(0, 0)` |
|
||||
| `P` | Toggle pen |
|
||||
| `← → ↑ ↓` | Jog one step, pen up |
|
||||
| `Shift + arrows` | Jog one step, pen down |
|
||||
| `+` / `-` | Jog step size 1–100 mm |
|
||||
| `L` `R` `T` `B` | Mark left / right / top / bottom bound at current position |
|
||||
|
||||
### `enablePlotButtons(options?)`
|
||||
|
||||
Adds Plot and Test buttons. Expects global `plot()` and `test()` in the sketch.
|
||||
|
||||
| Option | Default | What it is |
|
||||
|--------|---------|------------|
|
||||
| `parentId` | `'wallplotter-controls'` | DOM id for the button row |
|
||||
| `plotLabel` | `'Plot'` | Plot button text |
|
||||
| `testLabel` | `'Test'` | Test button text |
|
||||
| `stopLabel` | `'Stop'` | Test button while a test is running |
|
||||
|
||||
### `showInfos()`
|
||||
|
||||
Draw HUD, path preview, bounds, and key help. Call every frame from `draw()`. Also runs held-arrow jogging when keys are enabled.
|
||||
|
||||
---
|
||||
|
||||
## Speed
|
||||
|
||||
### `setSpeed(travel, draw)`
|
||||
|
||||
Feed rates in **mm/min**. Used as G-code `F` and for Test preview timing. Clamped to **1–10000**.
|
||||
|
||||
- `travel` (number): pen-up / `move()` speed. Or `{ travel, draw }`.
|
||||
- `draw` (number, optional): pen-down / `draw()` speed. Omit to leave draw rate unchanged.
|
||||
|
||||
Practical range about **100–10000**. Values above **10000** are clamped. Do not use `0`.
|
||||
|
||||
```javascript
|
||||
wp.setSpeed(6000, 2500);
|
||||
wp.setSpeed({ travel: 6000, draw: 2500 });
|
||||
```
|
||||
|
||||
### `setTimingMultiplier(mult)`
|
||||
|
||||
Scales **Test** preview waits only. Plot is paced by Marlin (`ok` + `M400`).
|
||||
|
||||
- `mult` (number): `1` = match `setSpeed` estimate, `2` = twice as slow, `0.5` = twice as fast. Clamped to ≥ `0.1`.
|
||||
|
||||
---
|
||||
|
||||
## Path (buffered drawing)
|
||||
|
||||
`move` / `draw` / `wait` / `speed` only **queue**. Nothing is sent until `runPath`.
|
||||
|
||||
### `startPath()`
|
||||
|
||||
Clear the path buffer. Returns `this`.
|
||||
|
||||
### `move(x, y)`
|
||||
|
||||
Queue pen-up travel to `(x, y)` mm. Returns `this`.
|
||||
|
||||
### `draw(x, y)`
|
||||
|
||||
Queue pen-down stroke to `(x, y)` mm. Returns `this`.
|
||||
|
||||
### `wait(ms)`
|
||||
|
||||
Queue a pause. `ms` is milliseconds. On Plot, sends `G4 P<ms>`. Returns `this`.
|
||||
|
||||
### `speed(travel, drawRate)`
|
||||
|
||||
Queue a mid-path speed change (mm/min, clamped 1–10000). Either argument may be omitted. Restored after `runPath`. Returns `this`.
|
||||
|
||||
### `runPath(testOnly)` / `runPath(commands, testOnly?)`
|
||||
|
||||
Run the path. **async**.
|
||||
|
||||
- `testOnly` (boolean): `true` = preview only, no serial. `false` = send G-code. Home must be set for Plot.
|
||||
- `commands` (array, optional): explicit list instead of the `startPath()` buffer.
|
||||
|
||||
Plot: wait for Marlin `ok`, then `M400` (move finished). Test: animate using distance / `setSpeed` × `timingMultiplier`.
|
||||
|
||||
```javascript
|
||||
wp.startPath();
|
||||
wp.move(-200, -200);
|
||||
wp.draw(200, -200);
|
||||
wp.wait(500);
|
||||
await wp.runPath(false); // plot
|
||||
await wp.runPath(true); // test
|
||||
```
|
||||
|
||||
Sketch: `plot()` → `runPath(false)`, `test()` → `runPath(true)`.
|
||||
|
||||
### `estimatePathMs(commands?)`
|
||||
|
||||
Returns estimated duration in **milliseconds** for the current path buffer, or for `commands` if given. Uses `setSpeed`, queued `speed()`, `wait()`, and `timingMultiplier`. The HUD shows elapsed / est / remain while plotting.
|
||||
|
||||
### `stopPath()`
|
||||
|
||||
Abort the current `runPath`. No arguments.
|
||||
|
||||
---
|
||||
|
||||
## Serial / machine
|
||||
|
||||
All **async**. Coordinate moves on Plot require home (`H`) first.
|
||||
|
||||
### `connect()`
|
||||
|
||||
Web Serial port picker, enable steppers (`M17`), pen up, `M114`.
|
||||
|
||||
### `disconnect()`
|
||||
|
||||
Close the serial port.
|
||||
|
||||
### `send(line, options?)`
|
||||
|
||||
Write one G-code line and wait for Marlin `ok`.
|
||||
|
||||
- `line` (string): command without newline, e.g. `'M114'`.
|
||||
- `options.allowWithoutHome` (boolean): allow `G0`/`G1` before home. Default `false`.
|
||||
- `options.waitOk` (boolean): wait for `ok`. Default `true`.
|
||||
- `options.timeoutMs` (number): `ok` timeout, default `300000`.
|
||||
|
||||
### `penUp()` / `penDown()`
|
||||
|
||||
Send pen G-code and wait the pen delay. No-op if already in that state.
|
||||
|
||||
### `moveTo(x, y)`
|
||||
|
||||
Pen up, travel to `(x, y)` mm immediately (not queued). Requires home.
|
||||
|
||||
### `drawTo(x, y)`
|
||||
|
||||
Pen down, draw to `(x, y)` mm immediately. Requires home.
|
||||
|
||||
### `goToZero()`
|
||||
|
||||
`moveTo(0, 0)`.
|
||||
|
||||
### `step(dx, dy, draw?)`
|
||||
|
||||
Relative move in mm.
|
||||
|
||||
- `dx`, `dy` (number): delta mm
|
||||
- `draw` (boolean, default `false`): `true` → `drawTo`, else `moveTo`
|
||||
|
||||
### `setHomeHere()`
|
||||
|
||||
`G92 X0 Y0`. Sets local `x,y` to 0 and marks home as set.
|
||||
|
||||
---
|
||||
|
||||
## Fields (read)
|
||||
|
||||
| Field | Meaning |
|
||||
|-------|---------|
|
||||
| `connected` | Serial open |
|
||||
| `homeSet` | Home was set with `H` / `setHomeHere()` |
|
||||
| `x`, `y` | Current position, mm |
|
||||
| `isPenDown` | Pen state |
|
||||
| `isPlotting` | `runPath` in progress |
|
||||
| `feedRateTravel` / `feedRateDraw` | Current speeds, mm/min |
|
||||
| `timingMultiplier` | Preview wait scale |
|
||||
| `stepSize` | Jog step, mm |
|
||||
| `drawBounds` | `{ left, right, top, bottom }` or `null` |
|
||||
+1085
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user