abort plot
This commit is contained in:
@@ -42,7 +42,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
|
<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>
|
<script src="sketch.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
|
<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>
|
<script src="sketch.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
+8
-4
@@ -48,6 +48,7 @@ Keyboard controls. Call from `setup()`. No arguments.
|
|||||||
|-----|--------|
|
|-----|--------|
|
||||||
| `C` | Connect / disconnect |
|
| `C` | Connect / disconnect |
|
||||||
| `T` | Run Test (preview) |
|
| `T` | Run Test (preview) |
|
||||||
|
| `A` | Abort / reset Test to start |
|
||||||
| `H` | Set home here (`G92 X0 Y0`) — required before Plot |
|
| `H` | Set home here (`G92 X0 Y0`) — required before Plot |
|
||||||
| `0` | Travel to `(0, 0)` |
|
| `0` | Travel to `(0, 0)` |
|
||||||
| `P` | Toggle pen |
|
| `P` | Toggle pen |
|
||||||
@@ -57,9 +58,9 @@ Keyboard controls. Call from `setup()`. No arguments.
|
|||||||
|
|
||||||
### `enablePlotButtons(options?)`
|
### `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 |
|
| 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 |
|
| `resumeLabel` | `'Resume'` | Plot button while paused |
|
||||||
| `testLabel` | `'Test'` | Test button text |
|
| `testLabel` | `'Test'` | Test button text |
|
||||||
| `stopLabel` | `'Stop'` | Test button while a test is running |
|
| `stopLabel` | `'Stop'` | Test button while a test is running |
|
||||||
|
| `abortLabel` | `'Abort'` | Reset test/plot preview to the start |
|
||||||
|
|
||||||
### `showInfos()`
|
### `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.
|
`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
@@ -18,7 +18,7 @@ class WallPlotter {
|
|||||||
// Static constants
|
// Static constants
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
static VERSION = '2.3.13';
|
static VERSION = '2.3.15';
|
||||||
static MIN_FEED_MM_MIN = 1;
|
static MIN_FEED_MM_MIN = 1;
|
||||||
static MAX_FEED_MM_MIN = 10000;
|
static MAX_FEED_MM_MIN = 10000;
|
||||||
/** Offline overlay size when not connected (mm) — 150×150 cm centred on origin */
|
/** Offline overlay size when not connected (mm) — 150×150 cm centred on origin */
|
||||||
@@ -133,14 +133,19 @@ class WallPlotter {
|
|||||||
this._connectBtn = null;
|
this._connectBtn = null;
|
||||||
this._plotBtn = null;
|
this._plotBtn = null;
|
||||||
this._testBtn = null;
|
this._testBtn = null;
|
||||||
|
this._abortBtn = null;
|
||||||
this._connectLabel = 'Connect';
|
this._connectLabel = 'Connect';
|
||||||
this._plotLabel = 'Plot';
|
this._plotLabel = 'Plot';
|
||||||
this._pauseLabel = 'Pause';
|
this._pauseLabel = 'Pause';
|
||||||
this._resumeLabel = 'Resume';
|
this._resumeLabel = 'Resume';
|
||||||
this._testLabel = 'Test';
|
this._testLabel = 'Test';
|
||||||
this._stopLabel = 'Stop';
|
this._stopLabel = 'Stop';
|
||||||
|
this._abortLabel = 'Abort';
|
||||||
this._lastTestBtnClick = 0;
|
this._lastTestBtnClick = 0;
|
||||||
this._lastPlotBtnClick = 0;
|
this._lastPlotBtnClick = 0;
|
||||||
|
this._pathStartX = 0;
|
||||||
|
this._pathStartY = 0;
|
||||||
|
this._wantPathStartReset = false;
|
||||||
|
|
||||||
// --- Callbacks ---
|
// --- Callbacks ---
|
||||||
const userOnLog = options.onLog ?? (() => {});
|
const userOnLog = options.onLog ?? (() => {});
|
||||||
@@ -222,7 +227,8 @@ class WallPlotter {
|
|||||||
/**
|
/**
|
||||||
* Add Connect / Plot / Test buttons. Expects global sketch functions plot() and test().
|
* 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.
|
* 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 = {}) {
|
enablePlotButtons(options = {}) {
|
||||||
if (this._plotButtonsEnabled) return;
|
if (this._plotButtonsEnabled) return;
|
||||||
@@ -244,6 +250,7 @@ class WallPlotter {
|
|||||||
this._resumeLabel = options.resumeLabel ?? 'Resume';
|
this._resumeLabel = options.resumeLabel ?? 'Resume';
|
||||||
this._testLabel = options.testLabel ?? 'Test';
|
this._testLabel = options.testLabel ?? 'Test';
|
||||||
this._stopLabel = options.stopLabel ?? 'Stop';
|
this._stopLabel = options.stopLabel ?? 'Stop';
|
||||||
|
this._abortLabel = options.abortLabel ?? 'Abort';
|
||||||
|
|
||||||
const plotter = this;
|
const plotter = this;
|
||||||
const connectBtn = createButton(this._connectLabel);
|
const connectBtn = createButton(this._connectLabel);
|
||||||
@@ -260,6 +267,11 @@ class WallPlotter {
|
|||||||
testBtn.parent(parent);
|
testBtn.parent(parent);
|
||||||
testBtn.mousePressed(() => plotter._onTestButton());
|
testBtn.mousePressed(() => plotter._onTestButton());
|
||||||
this._testBtn = testBtn;
|
this._testBtn = testBtn;
|
||||||
|
|
||||||
|
const abortBtn = createButton(this._abortLabel);
|
||||||
|
abortBtn.parent(parent);
|
||||||
|
abortBtn.mousePressed(() => plotter.abortToStart());
|
||||||
|
this._abortBtn = abortBtn;
|
||||||
this._updatePlotButtons();
|
this._updatePlotButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,6 +394,34 @@ class WallPlotter {
|
|||||||
this.onLog('plot resumed');
|
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).
|
* First press: pause after the current stroke (safe to Resume).
|
||||||
* Second press: stop sending more moves. The in-flight G1 still finishes
|
* Second press: stop sending more moves. The in-flight G1 still finishes
|
||||||
@@ -1008,6 +1048,10 @@ class WallPlotter {
|
|||||||
this._pathLoopActive = true;
|
this._pathLoopActive = true;
|
||||||
this._plotTestOnly = testOnly;
|
this._plotTestOnly = testOnly;
|
||||||
this.previewPath = commands;
|
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));
|
const startAt = Math.max(0, Math.min(fromIndex, commands.length));
|
||||||
this.plotProgress = startAt;
|
this.plotProgress = startAt;
|
||||||
if (timing) {
|
if (timing) {
|
||||||
@@ -1117,9 +1161,13 @@ class WallPlotter {
|
|||||||
this.isPenDown = false;
|
this.isPenDown = false;
|
||||||
}
|
}
|
||||||
this._plotTestOnly = false;
|
this._plotTestOnly = false;
|
||||||
this._setJob('idle');
|
|
||||||
this._pathLoopActive = false;
|
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();
|
await this.stopPlot();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (k === 'a') {
|
||||||
|
this.abortToStart();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (k === 't') {
|
if (k === 't') {
|
||||||
this._onTestButton();
|
this._onTestButton();
|
||||||
return false;
|
return false;
|
||||||
@@ -1690,6 +1742,11 @@ class WallPlotter {
|
|||||||
this._job !== 'idle' && this._plotTestOnly ? this._stopLabel : this._testLabel
|
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) {
|
_triggerSketchFn(name) {
|
||||||
@@ -1750,22 +1807,47 @@ class WallPlotter {
|
|||||||
bottom: 0.75rem;
|
bottom: 0.75rem;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
max-width: min(52rem, calc(100vw - 1.5rem));
|
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;
|
overflow: auto;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
-webkit-user-select: text;
|
-webkit-user-select: text;
|
||||||
pointer-events: auto;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
color: #c8c8d0;
|
color: #c8c8d0;
|
||||||
background: rgba(18, 18, 22, 0.88);
|
background: #141418;
|
||||||
border: 1px solid #3a3a42;
|
border: none;
|
||||||
border-radius: 6px;
|
margin: 0;
|
||||||
padding: 0.45rem 0.6rem;
|
padding: 0 0.65rem 0.5rem;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
margin: 0;
|
}
|
||||||
|
#wallplotter-log.collapsed #wallplotter-log-body {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
@@ -2003,6 +2085,7 @@ class WallPlotter {
|
|||||||
'0 go to origin (0, 0)',
|
'0 go to origin (0, 0)',
|
||||||
'P toggle pen (test up/down angles)',
|
'P toggle pen (test up/down angles)',
|
||||||
'T run test (preview)',
|
'T run test (preview)',
|
||||||
|
'A abort / reset test to start',
|
||||||
'S stop sending (in-flight stroke still finishes)',
|
'S stop sending (in-flight stroke still finishes)',
|
||||||
'C connect / disconnect',
|
'C connect / disconnect',
|
||||||
];
|
];
|
||||||
@@ -2014,23 +2097,42 @@ class WallPlotter {
|
|||||||
|
|
||||||
_ensureLogOverlay() {
|
_ensureLogOverlay() {
|
||||||
this._injectPlotButtonStyles();
|
this._injectPlotButtonStyles();
|
||||||
let el = document.getElementById('wallplotter-log');
|
let wrap = document.getElementById('wallplotter-log');
|
||||||
if (el) return el;
|
let body = document.getElementById('wallplotter-log-body');
|
||||||
el = document.createElement('pre');
|
if (wrap && body) return body;
|
||||||
el.id = 'wallplotter-log';
|
if (wrap) wrap.remove();
|
||||||
el.setAttribute('aria-label', 'Serial log');
|
|
||||||
document.body.appendChild(el);
|
wrap = document.createElement('div');
|
||||||
return el;
|
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() {
|
_syncLogOverlay() {
|
||||||
const el = this._ensureLogOverlay();
|
const el = this._ensureLogOverlay();
|
||||||
|
const wrap = document.getElementById('wallplotter-log');
|
||||||
const text = this.logLines.join('\n');
|
const text = this.logLines.join('\n');
|
||||||
if (el.textContent !== text) {
|
if (el.textContent !== text) {
|
||||||
el.textContent = text;
|
el.textContent = text;
|
||||||
el.scrollTop = el.scrollHeight;
|
el.scrollTop = el.scrollHeight;
|
||||||
}
|
}
|
||||||
el.style.display = this.logLines.length ? 'block' : 'none';
|
if (wrap) wrap.style.display = this.logLines.length ? 'block' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user