abort plot

This commit is contained in:
Martin Fuchs
2026-09-05 12:59:28 +02:00
parent b802fd2a10
commit 82daebcdd0
4 changed files with 131 additions and 25 deletions
+1 -1
View File
@@ -42,7 +42,7 @@
}
</style>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<script src="../../lib/WallPlotter.js?v=2.3.13"></script>
<script src="../../lib/WallPlotter.js?v=2.3.15"></script>
<script src="sketch.js"></script>
</head>
<body>
+1 -1
View File
@@ -42,7 +42,7 @@
}
</style>
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<script src="../../lib/WallPlotter.js?v=2.3.13"></script>
<script src="../../lib/WallPlotter.js?v=2.3.15"></script>
<script src="sketch.js"></script>
</head>
<body>
+8 -4
View File
@@ -48,6 +48,7 @@ Keyboard controls. Call from `setup()`. No arguments.
|-----|--------|
| `C` | Connect / disconnect |
| `T` | Run Test (preview) |
| `A` | Abort / reset Test to start |
| `H` | Set home here (`G92 X0 Y0`) — required before Plot |
| `0` | Travel to `(0, 0)` |
| `P` | Toggle pen |
@@ -57,9 +58,9 @@ Keyboard controls. Call from `setup()`. No arguments.
### `enablePlotButtons(options?)`
Adds Connect, Plot, and Test buttons. Expects global `plot()` and `test()` in the sketch.
Adds Connect, Plot, Test, and Abort buttons. Expects global `plot()` and `test()` in the sketch.
**Connect** is shown only while disconnected (same as key **C**). While a path is running, Plot becomes **Pause**; while paused it becomes **Resume**. Test becomes **Stop** during a test.
**Connect** is shown only while disconnected (same as key **C**). While a path is running, Plot becomes **Pause**; while paused it becomes **Resume**. Test becomes **Stop** during a test. **Abort** stops and resets the Test preview to the start (key **A**).
| Option | Default | What it is |
|--------|---------|------------|
@@ -70,6 +71,7 @@ Adds Connect, Plot, and Test buttons. Expects global `plot()` and `test()` in th
| `resumeLabel` | `'Resume'` | Plot button while paused |
| `testLabel` | `'Test'` | Test button text |
| `stopLabel` | `'Stop'` | Test button while a test is running |
| `abortLabel` | `'Abort'` | Reset test/plot preview to the start |
### `showInfos()`
@@ -155,9 +157,11 @@ Returns estimated duration in **milliseconds** for the current path buffer, or f
`stopPlot()` first press is the same as **Pause**: finish the current stroke, then **Resume** continues. Second press stops the browser from sending more moves. The **in-flight** `G1` still runs — this firmware does not have `EMERGENCY_PARSER`, so `M410` cannot interrupt motion (and on a polargraph it desyncs belts). Unplug power for a hard stop.
### `pausePlot()` / `resumePlot()`
### `pausePlot()` / `resumePlot()` / `abortToStart()`
**Pause** / first **Stop** wait for the current segment to finish (`M400`), then lift the pen. Do not resume after emergency stop until you re-home. Elapsed / remaining HUD time freezes while paused.
**Pause** / first **Stop** wait for the current segment to finish (`M400`), then lift the pen. **Resume** continues from there.
**Abort** (button or **A**) stops the run and resets the Test preview to the start. Live Plot only stops sending further moves; the machine stays where it is.
---
+121 -19
View File
@@ -18,7 +18,7 @@ class WallPlotter {
// Static constants
// ---------------------------------------------------------------------------
static VERSION = '2.3.13';
static VERSION = '2.3.15';
static MIN_FEED_MM_MIN = 1;
static MAX_FEED_MM_MIN = 10000;
/** Offline overlay size when not connected (mm) — 150×150 cm centred on origin */
@@ -133,14 +133,19 @@ class WallPlotter {
this._connectBtn = null;
this._plotBtn = null;
this._testBtn = null;
this._abortBtn = null;
this._connectLabel = 'Connect';
this._plotLabel = 'Plot';
this._pauseLabel = 'Pause';
this._resumeLabel = 'Resume';
this._testLabel = 'Test';
this._stopLabel = 'Stop';
this._abortLabel = 'Abort';
this._lastTestBtnClick = 0;
this._lastPlotBtnClick = 0;
this._pathStartX = 0;
this._pathStartY = 0;
this._wantPathStartReset = false;
// --- Callbacks ---
const userOnLog = options.onLog ?? (() => {});
@@ -222,7 +227,8 @@ class WallPlotter {
/**
* Add Connect / Plot / Test buttons. Expects global sketch functions plot() and test().
* Connect is shown only while disconnected. Plot becomes Pause while running, Resume while paused.
* @param {{parentId?: string, connectLabel?: string, plotLabel?: string, pauseLabel?: string, resumeLabel?: string, testLabel?: string, stopLabel?: string}} [options]
* Abort stops and resets the Test preview to the start.
* @param {{parentId?: string, connectLabel?: string, plotLabel?: string, pauseLabel?: string, resumeLabel?: string, testLabel?: string, stopLabel?: string, abortLabel?: string}} [options]
*/
enablePlotButtons(options = {}) {
if (this._plotButtonsEnabled) return;
@@ -244,6 +250,7 @@ class WallPlotter {
this._resumeLabel = options.resumeLabel ?? 'Resume';
this._testLabel = options.testLabel ?? 'Test';
this._stopLabel = options.stopLabel ?? 'Stop';
this._abortLabel = options.abortLabel ?? 'Abort';
const plotter = this;
const connectBtn = createButton(this._connectLabel);
@@ -260,6 +267,11 @@ class WallPlotter {
testBtn.parent(parent);
testBtn.mousePressed(() => plotter._onTestButton());
this._testBtn = testBtn;
const abortBtn = createButton(this._abortLabel);
abortBtn.parent(parent);
abortBtn.mousePressed(() => plotter.abortToStart());
this._abortBtn = abortBtn;
this._updatePlotButtons();
}
@@ -382,6 +394,34 @@ class WallPlotter {
this.onLog('plot resumed');
}
/**
* Stop the current Test/Plot and reset the preview to the start.
* Test pose returns to where the path began. Live Plot only stops sending
* (the machine stays where it is).
*/
abortToStart() {
this._clearResumeState();
this._wantPathStartReset = true;
this._pathRunId += 1;
if (this._job === 'running' || this._job === 'paused') this._abortJob();
this._applyPathStartReset();
this.onLog('drawing cleared');
}
_applyPathStartReset() {
this.x = this._pathStartX;
this.y = this._pathStartY;
this.isPenDown = false;
this.plotProgress = 0;
this.previewPath = [];
this._path = [];
this._wantPathStartReset = false;
this._plotTestOnly = false;
this._setJob('idle');
this._redrawSketch();
this._updatePlotButtons();
}
/**
* First press: pause after the current stroke (safe to Resume).
* Second press: stop sending more moves. The in-flight G1 still finishes
@@ -1008,6 +1048,10 @@ class WallPlotter {
this._pathLoopActive = true;
this._plotTestOnly = testOnly;
this.previewPath = commands;
if (fromIndex === 0 && !timing) {
this._pathStartX = this.x;
this._pathStartY = this.y;
}
const startAt = Math.max(0, Math.min(fromIndex, commands.length));
this.plotProgress = startAt;
if (timing) {
@@ -1117,9 +1161,13 @@ class WallPlotter {
this.isPenDown = false;
}
this._plotTestOnly = false;
this._setJob('idle');
this._pathLoopActive = false;
if (this.plotProgress >= commands.length) this._clearResumeState();
if (this._wantPathStartReset || this.previewPath.length === 0) {
this._applyPathStartReset();
} else {
this._setJob('idle');
if (this.plotProgress >= commands.length) this._clearResumeState();
}
}
}
@@ -1516,6 +1564,10 @@ class WallPlotter {
await this.stopPlot();
return false;
}
if (k === 'a') {
this.abortToStart();
return false;
}
if (k === 't') {
this._onTestButton();
return false;
@@ -1690,6 +1742,11 @@ class WallPlotter {
this._job !== 'idle' && this._plotTestOnly ? this._stopLabel : this._testLabel
);
}
if (this._abortBtn) {
const active =
this._job !== 'idle' || !!this._interrupted || this.previewPath.length > 0;
this._abortBtn.elt.disabled = !active;
}
}
_triggerSketchFn(name) {
@@ -1750,22 +1807,47 @@ class WallPlotter {
bottom: 0.75rem;
z-index: 20;
max-width: min(52rem, calc(100vw - 1.5rem));
max-height: 42vh;
pointer-events: auto;
background: #141418;
border: none;
border-radius: 6px;
margin: 0;
overflow: hidden;
}
#wallplotter-log-toggle {
display: block;
width: 100%;
background: #141418;
border: none;
color: #aaa;
cursor: pointer;
font-family: system-ui, sans-serif;
font-size: 0.75rem;
letter-spacing: 0.04em;
text-align: left;
padding: 0.4rem 0.65rem;
}
#wallplotter-log-toggle:hover {
color: #eee;
}
#wallplotter-log-body {
max-height: 38vh;
overflow: auto;
user-select: text;
-webkit-user-select: text;
pointer-events: auto;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12px;
line-height: 1.4;
color: #c8c8d0;
background: rgba(18, 18, 22, 0.88);
border: 1px solid #3a3a42;
border-radius: 6px;
padding: 0.45rem 0.6rem;
background: #141418;
border: none;
margin: 0;
padding: 0 0.65rem 0.5rem;
white-space: pre-wrap;
word-break: break-word;
margin: 0;
}
#wallplotter-log.collapsed #wallplotter-log-body {
display: none;
}
`;
document.head.appendChild(style);
@@ -2003,6 +2085,7 @@ class WallPlotter {
'0 go to origin (0, 0)',
'P toggle pen (test up/down angles)',
'T run test (preview)',
'A abort / reset test to start',
'S stop sending (in-flight stroke still finishes)',
'C connect / disconnect',
];
@@ -2014,23 +2097,42 @@ class WallPlotter {
_ensureLogOverlay() {
this._injectPlotButtonStyles();
let el = document.getElementById('wallplotter-log');
if (el) return el;
el = document.createElement('pre');
el.id = 'wallplotter-log';
el.setAttribute('aria-label', 'Serial log');
document.body.appendChild(el);
return el;
let wrap = document.getElementById('wallplotter-log');
let body = document.getElementById('wallplotter-log-body');
if (wrap && body) return body;
if (wrap) wrap.remove();
wrap = document.createElement('div');
wrap.id = 'wallplotter-log';
wrap.setAttribute('aria-label', 'Serial log');
const toggle = document.createElement('button');
toggle.id = 'wallplotter-log-toggle';
toggle.type = 'button';
toggle.textContent = 'Serial';
toggle.addEventListener('click', () => {
wrap.classList.toggle('collapsed');
toggle.textContent = wrap.classList.contains('collapsed') ? 'Serial ▸' : 'Serial ▾';
});
body = document.createElement('pre');
body.id = 'wallplotter-log-body';
wrap.appendChild(toggle);
wrap.appendChild(body);
document.body.appendChild(wrap);
toggle.textContent = 'Serial ▾';
return body;
}
_syncLogOverlay() {
const el = this._ensureLogOverlay();
const wrap = document.getElementById('wallplotter-log');
const text = this.logLines.join('\n');
if (el.textContent !== text) {
el.textContent = text;
el.scrollTop = el.scrollHeight;
}
el.style.display = this.logLines.length ? 'block' : 'none';
if (wrap) wrap.style.display = this.logLines.length ? 'block' : 'none';
}
// ===========================================================================