bounding box 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.15"></script>
|
<script src="../../lib/WallPlotter.js?v=2.3.24"></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.15"></script>
|
<script src="../../lib/WallPlotter.js?v=2.3.24"></script>
|
||||||
<script src="sketch.js"></script>
|
<script src="sketch.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -46,10 +46,8 @@ async function drawPlot(testOnly) {
|
|||||||
|
|
||||||
for (let y = -area; y <= area; y+=step){
|
for (let y = -area; y <= area; y+=step){
|
||||||
for (let x = -area; x <= area; x+=step){
|
for (let x = -area; x <= area; x+=step){
|
||||||
//if(random(1) < 0.8){
|
wp.move(x, y);
|
||||||
wp.draw(x, y);
|
wp.draw(x+step, y+step);
|
||||||
wp.draw(x+(random(1)<0.9), y+random(step*1.5));
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await wp.runPath(testOnly);
|
await wp.runPath(testOnly);
|
||||||
|
|||||||
+1
-1
@@ -40,6 +40,6 @@ async function drawPlot(testOnly) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Press **C** to connect, **H** to set home, then **Test** or **Plot**.
|
Press **C** to connect, **H** to set home, then **Test**. **Plot** and **Bounding box** appear after a preview exists.
|
||||||
|
|
||||||
Example sketches: `examples/zickzack/`, `examples/rechteck/`. Full API: [doc.md](doc.md).
|
Example sketches: `examples/zickzack/`, `examples/rechteck/`. Full API: [doc.md](doc.md).
|
||||||
|
|||||||
+249
-74
@@ -18,7 +18,7 @@ class WallPlotter {
|
|||||||
// Static constants
|
// Static constants
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
static VERSION = '2.3.15';
|
static VERSION = '2.3.24';
|
||||||
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,16 @@ class WallPlotter {
|
|||||||
this._connectBtn = null;
|
this._connectBtn = null;
|
||||||
this._plotBtn = null;
|
this._plotBtn = null;
|
||||||
this._testBtn = null;
|
this._testBtn = null;
|
||||||
|
this._bboxBtn = null;
|
||||||
this._abortBtn = 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._bboxLabel = 'Bounding box';
|
||||||
this._abortLabel = 'Abort';
|
this._abortLabel = 'Abort';
|
||||||
|
this._bboxRunning = false;
|
||||||
this._lastTestBtnClick = 0;
|
this._lastTestBtnClick = 0;
|
||||||
this._lastPlotBtnClick = 0;
|
this._lastPlotBtnClick = 0;
|
||||||
this._pathStartX = 0;
|
this._pathStartX = 0;
|
||||||
@@ -225,10 +227,12 @@ class WallPlotter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add Connect / Plot / Test buttons. Expects global sketch functions plot() and test().
|
* Add Connect / Test / Plot / Bounding box / Abort.
|
||||||
* Connect is shown only while disconnected. Plot becomes Pause while running, Resume while paused.
|
* No sketch yet: Test (+ Connect if disconnected). After Test, Bounding box
|
||||||
* Abort stops and resets the Test preview to the start.
|
* appears; Plot appears once connected. Plot sends that path (does not regenerate) and
|
||||||
* @param {{parentId?: string, connectLabel?: string, plotLabel?: string, pauseLabel?: string, resumeLabel?: string, testLabel?: string, stopLabel?: string, abortLabel?: string}} [options]
|
* becomes Pause / Resume while plotting. Abort only while plotting or when a plot is saved.
|
||||||
|
* Bounding box travels pen-up around the drawing extents.
|
||||||
|
* @param {{parentId?: string, connectLabel?: string, plotLabel?: string, pauseLabel?: string, resumeLabel?: string, testLabel?: string, bboxLabel?: string, abortLabel?: string}} [options]
|
||||||
*/
|
*/
|
||||||
enablePlotButtons(options = {}) {
|
enablePlotButtons(options = {}) {
|
||||||
if (this._plotButtonsEnabled) return;
|
if (this._plotButtonsEnabled) return;
|
||||||
@@ -249,7 +253,7 @@ class WallPlotter {
|
|||||||
this._pauseLabel = options.pauseLabel ?? 'Pause';
|
this._pauseLabel = options.pauseLabel ?? 'Pause';
|
||||||
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._bboxLabel = options.bboxLabel ?? 'Bounding box';
|
||||||
this._abortLabel = options.abortLabel ?? 'Abort';
|
this._abortLabel = options.abortLabel ?? 'Abort';
|
||||||
|
|
||||||
const plotter = this;
|
const plotter = this;
|
||||||
@@ -258,19 +262,24 @@ class WallPlotter {
|
|||||||
connectBtn.mousePressed(() => plotter._onConnectButton());
|
connectBtn.mousePressed(() => plotter._onConnectButton());
|
||||||
this._connectBtn = connectBtn;
|
this._connectBtn = connectBtn;
|
||||||
|
|
||||||
const plotBtn = createButton(this._plotLabel);
|
|
||||||
plotBtn.parent(parent);
|
|
||||||
plotBtn.mousePressed(() => plotter._onPlotButton());
|
|
||||||
this._plotBtn = plotBtn;
|
|
||||||
|
|
||||||
const testBtn = createButton(this._testLabel);
|
const testBtn = createButton(this._testLabel);
|
||||||
testBtn.parent(parent);
|
testBtn.parent(parent);
|
||||||
testBtn.mousePressed(() => plotter._onTestButton());
|
testBtn.mousePressed(() => plotter._onTestButton());
|
||||||
this._testBtn = testBtn;
|
this._testBtn = testBtn;
|
||||||
|
|
||||||
|
const plotBtn = createButton(this._plotLabel);
|
||||||
|
plotBtn.parent(parent);
|
||||||
|
plotBtn.mousePressed(() => plotter._onPlotButton());
|
||||||
|
this._plotBtn = plotBtn;
|
||||||
|
|
||||||
|
const bboxBtn = createButton(this._bboxLabel);
|
||||||
|
bboxBtn.parent(parent);
|
||||||
|
bboxBtn.mousePressed(() => plotter._onBoundsButton());
|
||||||
|
this._bboxBtn = bboxBtn;
|
||||||
|
|
||||||
const abortBtn = createButton(this._abortLabel);
|
const abortBtn = createButton(this._abortLabel);
|
||||||
abortBtn.parent(parent);
|
abortBtn.parent(parent);
|
||||||
abortBtn.mousePressed(() => plotter.abortToStart());
|
abortBtn.mousePressed(() => plotter._abortLivePlot());
|
||||||
this._abortBtn = abortBtn;
|
this._abortBtn = abortBtn;
|
||||||
this._updatePlotButtons();
|
this._updatePlotButtons();
|
||||||
}
|
}
|
||||||
@@ -954,6 +963,10 @@ class WallPlotter {
|
|||||||
|
|
||||||
/** G92 X0 Y0 — set current pose as home. Refused while plotting or waiting for Marlin. */
|
/** G92 X0 Y0 — set current pose as home. Refused while plotting or waiting for Marlin. */
|
||||||
async setHomeHere() {
|
async setHomeHere() {
|
||||||
|
if (this._bboxRunning) {
|
||||||
|
this.onLog('! cannot set home while travelling the bounding box');
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (this._job !== 'idle') {
|
if (this._job !== 'idle') {
|
||||||
this.onLog('! cannot set home while plotting — resume or abort first');
|
this.onLog('! cannot set home while plotting — resume or abort first');
|
||||||
this.statusText = 'Finish or abort plot before H';
|
this.statusText = 'Finish or abort plot before H';
|
||||||
@@ -1530,7 +1543,7 @@ class WallPlotter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_isMachineIdle() {
|
_isMachineIdle() {
|
||||||
return this._job === 'idle' && !this._okWaiter;
|
return this._job === 'idle' && !this._okWaiter && !this._bboxRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
_showHomeRequiredWarning() {
|
_showHomeRequiredWarning() {
|
||||||
@@ -1561,17 +1574,26 @@ class WallPlotter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (k === 's') {
|
if (k === 's') {
|
||||||
await this.stopPlot();
|
if (!this._isLivePlot()) return false;
|
||||||
|
if (this._job === 'paused') this.resumePlot();
|
||||||
|
else if (this._job === 'running') this.pausePlot();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (k === 'a') {
|
if (k === 'a') {
|
||||||
this.abortToStart();
|
if (!this._isLivePlot() && !this._hasSavedPlot()) return false;
|
||||||
|
this._abortLivePlot();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (k === 't') {
|
if (k === 't') {
|
||||||
|
if (this._isLivePlot() || this._bboxRunning) return false;
|
||||||
this._onTestButton();
|
this._onTestButton();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (k === 'b') {
|
||||||
|
if (this._isLivePlot() || this._bboxRunning) return false;
|
||||||
|
this._onBoundsButton();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this._keysEnabled || !this.connected) return;
|
if (!this._keysEnabled || !this.connected) return;
|
||||||
|
|
||||||
@@ -1676,41 +1698,136 @@ class WallPlotter {
|
|||||||
this._toggleConnection();
|
this._toggleConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isLivePlot() {
|
||||||
|
return this._job !== 'idle' && !this._plotTestOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
_hasSavedPlot() {
|
||||||
|
return !!(this._interrupted && !this._interrupted.testOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
_hasDrawnSketch() {
|
||||||
|
return this.previewPath.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_abortLivePlot() {
|
||||||
|
if (this._hasSavedPlot() && this._job === 'idle') {
|
||||||
|
this._clearResumeState();
|
||||||
|
this.previewPath = [];
|
||||||
|
this._path = [];
|
||||||
|
this.plotProgress = 0;
|
||||||
|
this.onLog('saved plot discarded — Test this sketch before Plot');
|
||||||
|
this._syncStatus();
|
||||||
|
this._updatePlotButtons();
|
||||||
|
this._redrawSketch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this._isLivePlot()) return;
|
||||||
|
this._clearResumeState();
|
||||||
|
this._abortJob();
|
||||||
|
this.onLog(
|
||||||
|
'plot aborted — motors finish the in-flight move (unplug power for a hard stop)'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async _plotExistingPath() {
|
||||||
|
const commands = this.previewPath;
|
||||||
|
if (!commands.length || !this.connected) return;
|
||||||
|
if (this._pathLoopActive) this._abortJob();
|
||||||
|
while (this._pathLoopActive) await this._delay(16);
|
||||||
|
await this._executePath(commands, false);
|
||||||
|
}
|
||||||
|
|
||||||
_onPlotButton() {
|
_onPlotButton() {
|
||||||
if (this._job === 'paused') {
|
if (this._bboxRunning) return;
|
||||||
|
if (this._isLivePlot() && this._job === 'paused') {
|
||||||
this.resumePlot();
|
this.resumePlot();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._job === 'running') {
|
if (this._isLivePlot() && this._job === 'running') {
|
||||||
this.pausePlot();
|
this.pausePlot();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._job !== 'idle' || !this.connected) return;
|
if (this._job === 'aborting') return;
|
||||||
const now = typeof millis === 'function' ? millis() : Date.now();
|
if (this._job !== 'idle' && this._plotTestOnly) {
|
||||||
if (this._interrupted && !this._interrupted.testOnly) {
|
this._plotExistingPath();
|
||||||
if (now - this._lastPlotBtnClick < 450) {
|
|
||||||
this._lastPlotBtnClick = now;
|
|
||||||
this._clearResumeState();
|
|
||||||
this._triggerSketchFn('plot');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._lastPlotBtnClick = now;
|
if (this._job !== 'idle' || !this.connected) return;
|
||||||
|
if (this._interrupted && !this._interrupted.testOnly) {
|
||||||
this._resumeInterruptedPath();
|
this._resumeInterruptedPath();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._lastPlotBtnClick = now;
|
this._plotExistingPath();
|
||||||
this._triggerSketchFn('plot');
|
}
|
||||||
|
|
||||||
|
_previewBounds() {
|
||||||
|
let minX = Infinity;
|
||||||
|
let minY = Infinity;
|
||||||
|
let maxX = -Infinity;
|
||||||
|
let maxY = -Infinity;
|
||||||
|
let n = 0;
|
||||||
|
for (const cmd of this.previewPath) {
|
||||||
|
if (cmd.type !== 'move' && cmd.type !== 'draw') continue;
|
||||||
|
minX = Math.min(minX, cmd.x);
|
||||||
|
maxX = Math.max(maxX, cmd.x);
|
||||||
|
minY = Math.min(minY, cmd.y);
|
||||||
|
maxY = Math.max(maxY, cmd.y);
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
if (!n) return null;
|
||||||
|
return { minX, minY, maxX, maxY };
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onBoundsButton() {
|
||||||
|
if (this._isLivePlot() || this._bboxRunning) return;
|
||||||
|
if (!this.connected || !this._requireHome()) return;
|
||||||
|
const box = this._previewBounds();
|
||||||
|
if (!box) return;
|
||||||
|
if (this._job !== 'idle' && this._plotTestOnly) {
|
||||||
|
this._abortJob();
|
||||||
|
while (this._pathLoopActive) await this._delay(16);
|
||||||
|
}
|
||||||
|
if (this._job !== 'idle') return;
|
||||||
|
this._bboxRunning = true;
|
||||||
|
this._updatePlotButtons();
|
||||||
|
const { minX, minY, maxX, maxY } = box;
|
||||||
|
this.onLog(
|
||||||
|
`bounding box ${minX.toFixed(0)},${minY.toFixed(0)} … ${maxX.toFixed(0)},${maxY.toFixed(0)} mm (pen up)`
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await this.penUp();
|
||||||
|
const corners = [
|
||||||
|
{ type: 'move', x: minX, y: minY },
|
||||||
|
{ type: 'move', x: maxX, y: minY },
|
||||||
|
{ type: 'move', x: maxX, y: maxY },
|
||||||
|
{ type: 'move', x: minX, y: maxY },
|
||||||
|
{ type: 'move', x: minX, y: minY },
|
||||||
|
];
|
||||||
|
let px = this.x;
|
||||||
|
let py = this.y;
|
||||||
|
this.statusText = 'Bounding box (pen up)';
|
||||||
|
for (const cmd of corners) {
|
||||||
|
const duration = this._segmentDurationMs(px, py, cmd);
|
||||||
|
this._lastSegmentMs = duration;
|
||||||
|
await Promise.all([
|
||||||
|
this._sendSegmentAndWait(cmd),
|
||||||
|
this._animatePosition(px, py, cmd, duration),
|
||||||
|
]);
|
||||||
|
px = cmd.x;
|
||||||
|
py = cmd.y;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err.name !== 'AbortError') this.onLog('! ' + err.message);
|
||||||
|
} finally {
|
||||||
|
this._bboxRunning = false;
|
||||||
|
this._syncStatus();
|
||||||
|
this._updatePlotButtons();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onTestButton() {
|
_onTestButton() {
|
||||||
const now = millis();
|
if (this._isLivePlot() || this._bboxRunning) return;
|
||||||
if (this._job === 'running' && this._plotTestOnly) {
|
|
||||||
if (now - this._lastTestBtnClick < 450) this._restartPath('test');
|
|
||||||
else this.stopPlot();
|
|
||||||
this._lastTestBtnClick = now;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._lastTestBtnClick = now;
|
|
||||||
this._restartPath('test');
|
this._restartPath('test');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1721,31 +1838,44 @@ class WallPlotter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_updatePlotButtons() {
|
_updatePlotButtons() {
|
||||||
|
const livePlot = this._isLivePlot();
|
||||||
|
const savedPlot = this._hasSavedPlot();
|
||||||
|
const sketchVisible = this._hasDrawnSketch() || livePlot || savedPlot;
|
||||||
|
|
||||||
if (this._connectBtn) {
|
if (this._connectBtn) {
|
||||||
if (this.connected) this._connectBtn.hide();
|
if (this.connected) this._connectBtn.hide();
|
||||||
else this._connectBtn.show();
|
else this._connectBtn.show();
|
||||||
this._connectBtn.elt.disabled = this._link === 'connecting';
|
this._connectBtn.elt.disabled = this._link === 'connecting';
|
||||||
}
|
}
|
||||||
|
if (this._testBtn) {
|
||||||
|
if (livePlot) this._testBtn.hide();
|
||||||
|
else this._testBtn.show();
|
||||||
|
this._testBtn.html(this._testLabel);
|
||||||
|
this._testBtn.elt.disabled = this._bboxRunning;
|
||||||
|
}
|
||||||
if (this._plotBtn) {
|
if (this._plotBtn) {
|
||||||
|
if (this.connected && sketchVisible) this._plotBtn.show();
|
||||||
|
else this._plotBtn.hide();
|
||||||
let label = this._plotLabel;
|
let label = this._plotLabel;
|
||||||
if (this._job === 'paused') label = this._resumeLabel;
|
if (this._job === 'paused' && livePlot) label = this._resumeLabel;
|
||||||
else if (this._interrupted && this._job === 'idle') label = this._resumeLabel;
|
else if (savedPlot && this._job === 'idle') label = this._resumeLabel;
|
||||||
else if (this._job === 'running' || this._job === 'aborting') {
|
else if (livePlot && (this._job === 'running' || this._job === 'aborting')) {
|
||||||
label = this._pauseLabel;
|
label = this._pauseLabel;
|
||||||
}
|
}
|
||||||
this._plotBtn.html(label);
|
this._plotBtn.html(label);
|
||||||
this._plotBtn.elt.disabled =
|
this._plotBtn.elt.disabled =
|
||||||
this._job === 'aborting' || (!this.connected && this._job === 'idle');
|
this._job === 'aborting' || this._bboxRunning;
|
||||||
}
|
}
|
||||||
if (this._testBtn) {
|
if (this._bboxBtn) {
|
||||||
this._testBtn.html(
|
if (sketchVisible && !livePlot) this._bboxBtn.show();
|
||||||
this._job !== 'idle' && this._plotTestOnly ? this._stopLabel : this._testLabel
|
else this._bboxBtn.hide();
|
||||||
);
|
this._bboxBtn.elt.disabled =
|
||||||
|
this._bboxRunning || !this.connected || !this.homeSet;
|
||||||
}
|
}
|
||||||
if (this._abortBtn) {
|
if (this._abortBtn) {
|
||||||
const active =
|
if (livePlot || savedPlot) this._abortBtn.show();
|
||||||
this._job !== 'idle' || !!this._interrupted || this.previewPath.length > 0;
|
else this._abortBtn.hide();
|
||||||
this._abortBtn.elt.disabled = !active;
|
this._abortBtn.elt.disabled = this._job === 'aborting';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1762,9 +1892,12 @@ class WallPlotter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_injectPlotButtonStyles() {
|
_injectPlotButtonStyles() {
|
||||||
if (document.getElementById('wallplotter-btn-styles')) return;
|
let style = document.getElementById('wallplotter-btn-styles');
|
||||||
const style = document.createElement('style');
|
if (!style) {
|
||||||
|
style = document.createElement('style');
|
||||||
style.id = 'wallplotter-btn-styles';
|
style.id = 'wallplotter-btn-styles';
|
||||||
|
document.head.appendChild(style);
|
||||||
|
}
|
||||||
style.textContent = `
|
style.textContent = `
|
||||||
#wallplotter-controls {
|
#wallplotter-controls {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -1772,10 +1905,14 @@ class WallPlotter {
|
|||||||
right: 0.75rem;
|
right: 0.75rem;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
max-width: calc(100vw - 1.5rem);
|
||||||
}
|
}
|
||||||
#wallplotter-controls button {
|
#wallplotter-controls button,
|
||||||
|
#wallplotter-log-toggle {
|
||||||
background: rgba(42, 42, 48, 0.9);
|
background: rgba(42, 42, 48, 0.9);
|
||||||
border: 1px solid #444;
|
border: 1px solid #444;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
@@ -1786,7 +1923,8 @@ class WallPlotter {
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
padding: 0.4rem 1rem;
|
padding: 0.4rem 1rem;
|
||||||
}
|
}
|
||||||
#wallplotter-controls button:hover {
|
#wallplotter-controls button:hover,
|
||||||
|
#wallplotter-log-toggle:hover {
|
||||||
background: #35353c;
|
background: #35353c;
|
||||||
border-color: #666;
|
border-color: #666;
|
||||||
color: #eee;
|
color: #eee;
|
||||||
@@ -1806,29 +1944,38 @@ class WallPlotter {
|
|||||||
right: 0.75rem;
|
right: 0.75rem;
|
||||||
bottom: 0.75rem;
|
bottom: 0.75rem;
|
||||||
z-index: 20;
|
z-index: 20;
|
||||||
max-width: min(52rem, calc(100vw - 1.5rem));
|
box-sizing: border-box;
|
||||||
|
width: min(40rem, calc(100vw - 1.5rem));
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
background: #141418;
|
background: #141418;
|
||||||
border: none;
|
border: 1px solid #333;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
#wallplotter-log-toggle {
|
#wallplotter-log.collapsed {
|
||||||
display: block;
|
width: auto;
|
||||||
width: 100%;
|
background: transparent;
|
||||||
background: #141418;
|
|
||||||
border: none;
|
border: none;
|
||||||
color: #aaa;
|
}
|
||||||
cursor: pointer;
|
#wallplotter-log-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.35rem 0.4rem 0.35rem 0.65rem;
|
||||||
|
}
|
||||||
|
#wallplotter-log.collapsed #wallplotter-log-header {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#wallplotter-log-title {
|
||||||
|
color: #ccc;
|
||||||
font-family: system-ui, sans-serif;
|
font-family: system-ui, sans-serif;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
text-align: left;
|
|
||||||
padding: 0.4rem 0.65rem;
|
|
||||||
}
|
}
|
||||||
#wallplotter-log-toggle:hover {
|
#wallplotter-log.collapsed #wallplotter-log-title {
|
||||||
color: #eee;
|
display: none;
|
||||||
}
|
}
|
||||||
#wallplotter-log-body {
|
#wallplotter-log-body {
|
||||||
max-height: 38vh;
|
max-height: 38vh;
|
||||||
@@ -1844,13 +1991,13 @@ class WallPlotter {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 0.65rem 0.5rem;
|
padding: 0 0.65rem 0.5rem;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
}
|
}
|
||||||
#wallplotter-log.collapsed #wallplotter-log-body {
|
#wallplotter-log.collapsed #wallplotter-log-body {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -1932,6 +2079,7 @@ class WallPlotter {
|
|||||||
this._drawBoundSizeLabels(bounds, tl, br, w, h);
|
this._drawBoundSizeLabels(bounds, tl, br, w, h);
|
||||||
|
|
||||||
this._drawPreviewPath(layout);
|
this._drawPreviewPath(layout);
|
||||||
|
this._drawSketchBounds(layout);
|
||||||
this._drawGondola(layout);
|
this._drawGondola(layout);
|
||||||
|
|
||||||
pop();
|
pop();
|
||||||
@@ -1958,6 +2106,20 @@ class WallPlotter {
|
|||||||
pop();
|
pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_drawSketchBounds(layout) {
|
||||||
|
const box = this._previewBounds();
|
||||||
|
if (!box) return;
|
||||||
|
const tl = this._worldToScreen(box.minX, box.maxY, layout);
|
||||||
|
const br = this._worldToScreen(box.maxX, box.minY, layout);
|
||||||
|
noFill();
|
||||||
|
stroke(90, 200, 255, 180);
|
||||||
|
strokeWeight(1);
|
||||||
|
drawingContext.setLineDash([6, 5]);
|
||||||
|
rectMode(CORNER);
|
||||||
|
rect(tl.x, tl.y, br.x - tl.x, br.y - tl.y);
|
||||||
|
drawingContext.setLineDash([]);
|
||||||
|
}
|
||||||
|
|
||||||
_drawPreviewPath(layout) {
|
_drawPreviewPath(layout) {
|
||||||
if (!this.previewPath.length) return;
|
if (!this.previewPath.length) return;
|
||||||
|
|
||||||
@@ -1990,6 +2152,7 @@ class WallPlotter {
|
|||||||
|
|
||||||
_drawGondola(layout) {
|
_drawGondola(layout) {
|
||||||
const pos = this._worldToScreen(this.x, this.y, layout);
|
const pos = this._worldToScreen(this.x, this.y, layout);
|
||||||
|
const moving = this.isPlotting || this._bboxRunning;
|
||||||
if (this.isPenDown) {
|
if (this.isPenDown) {
|
||||||
stroke(255, 90, 90);
|
stroke(255, 90, 90);
|
||||||
fill(255, 90, 90);
|
fill(255, 90, 90);
|
||||||
@@ -1998,8 +2161,8 @@ class WallPlotter {
|
|||||||
fill(90, 200, 255);
|
fill(90, 200, 255);
|
||||||
}
|
}
|
||||||
strokeWeight(2);
|
strokeWeight(2);
|
||||||
circle(pos.x, pos.y, this.isPlotting ? 18 : 12);
|
circle(pos.x, pos.y, moving ? 18 : 12);
|
||||||
if (this.isPlotting) {
|
if (moving) {
|
||||||
noFill();
|
noFill();
|
||||||
stroke(255, 255, 255, 120);
|
stroke(255, 255, 255, 120);
|
||||||
strokeWeight(1);
|
strokeWeight(1);
|
||||||
@@ -2085,8 +2248,9 @@ 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',
|
'B travel drawing bounds (pen up)',
|
||||||
'S stop sending (in-flight stroke still finishes)',
|
'A abort plot (in-flight stroke still finishes)',
|
||||||
|
'S pause / resume plot',
|
||||||
'C connect / disconnect',
|
'C connect / disconnect',
|
||||||
];
|
];
|
||||||
for (let i = 0; i < help.length; i++) {
|
for (let i = 0; i < help.length; i++) {
|
||||||
@@ -2099,28 +2263,39 @@ class WallPlotter {
|
|||||||
this._injectPlotButtonStyles();
|
this._injectPlotButtonStyles();
|
||||||
let wrap = document.getElementById('wallplotter-log');
|
let wrap = document.getElementById('wallplotter-log');
|
||||||
let body = document.getElementById('wallplotter-log-body');
|
let body = document.getElementById('wallplotter-log-body');
|
||||||
if (wrap && body) return body;
|
const header = document.getElementById('wallplotter-log-header');
|
||||||
|
if (wrap && body && header) return body;
|
||||||
if (wrap) wrap.remove();
|
if (wrap) wrap.remove();
|
||||||
|
|
||||||
wrap = document.createElement('div');
|
wrap = document.createElement('div');
|
||||||
wrap.id = 'wallplotter-log';
|
wrap.id = 'wallplotter-log';
|
||||||
wrap.setAttribute('aria-label', 'Serial log');
|
wrap.setAttribute('aria-label', 'Serial log');
|
||||||
|
|
||||||
|
const headerEl = document.createElement('div');
|
||||||
|
headerEl.id = 'wallplotter-log-header';
|
||||||
|
|
||||||
|
const title = document.createElement('span');
|
||||||
|
title.id = 'wallplotter-log-title';
|
||||||
|
title.textContent = 'Serial';
|
||||||
|
|
||||||
const toggle = document.createElement('button');
|
const toggle = document.createElement('button');
|
||||||
toggle.id = 'wallplotter-log-toggle';
|
toggle.id = 'wallplotter-log-toggle';
|
||||||
toggle.type = 'button';
|
toggle.type = 'button';
|
||||||
toggle.textContent = 'Serial';
|
toggle.textContent = 'Hide';
|
||||||
|
toggle.setAttribute('aria-expanded', 'true');
|
||||||
toggle.addEventListener('click', () => {
|
toggle.addEventListener('click', () => {
|
||||||
wrap.classList.toggle('collapsed');
|
const collapsed = wrap.classList.toggle('collapsed');
|
||||||
toggle.textContent = wrap.classList.contains('collapsed') ? 'Serial ▸' : 'Serial ▾';
|
toggle.textContent = collapsed ? 'serial-log' : 'Hide';
|
||||||
|
toggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
body = document.createElement('pre');
|
body = document.createElement('pre');
|
||||||
body.id = 'wallplotter-log-body';
|
body.id = 'wallplotter-log-body';
|
||||||
wrap.appendChild(toggle);
|
headerEl.appendChild(title);
|
||||||
|
headerEl.appendChild(toggle);
|
||||||
|
wrap.appendChild(headerEl);
|
||||||
wrap.appendChild(body);
|
wrap.appendChild(body);
|
||||||
document.body.appendChild(wrap);
|
document.body.appendChild(wrap);
|
||||||
toggle.textContent = 'Serial ▾';
|
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-8
@@ -39,8 +39,10 @@ Keyboard controls. Call from `setup()`. No arguments.
|
|||||||
| Key | Action |
|
| Key | Action |
|
||||||
|-----|--------|
|
|-----|--------|
|
||||||
| `C` | Connect / disconnect |
|
| `C` | Connect / disconnect |
|
||||||
| `T` | Run Test (preview) |
|
| `T` | Run Test (preview). Press again to redraw |
|
||||||
| `A` | Abort / reset Test to start |
|
| `B` | Travel the drawing bounding box, pen up |
|
||||||
|
| `A` | Abort live Plot, or discard a restored plot after reload |
|
||||||
|
| `S` | Pause / resume live Plot |
|
||||||
| `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 |
|
||||||
@@ -50,9 +52,9 @@ Keyboard controls. Call from `setup()`. No arguments.
|
|||||||
|
|
||||||
### `enablePlotButtons(options?)`
|
### `enablePlotButtons(options?)`
|
||||||
|
|
||||||
Adds Connect, Plot, Test, and Abort buttons. Expects global `plot()` and `test()` in the sketch.
|
Adds Connect, Test, Plot, Bounding box, and Abort. 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. **Abort** stops and resets the Test preview to the start (key **A**).
|
**Connect** is shown only while disconnected (same as key **C**). With no sketch yet, only **Test** is shown. After Test, **Bounding box** appears. **Plot** appears once connected (and a preview exists). **Plot** sends the existing preview path (it does not call `plot()` again). While plotting, Plot becomes **Pause**, then **Resume**. **Abort** is shown during a live Plot, and after a reload if a plot can be resumed (so you can drop it after switching sketches). **Bounding box** (key **B**) is shown when a sketch is visible and nothing is plotting: the gondola travels the drawing extents with the pen up, without replacing the preview.
|
||||||
|
|
||||||
| Option | Default | What it is |
|
| Option | Default | What it is |
|
||||||
|--------|---------|------------|
|
|--------|---------|------------|
|
||||||
@@ -62,8 +64,8 @@ Adds Connect, Plot, Test, and Abort buttons. Expects global `plot()` and `test()
|
|||||||
| `pauseLabel` | `'Pause'` | Plot button while a path is running |
|
| `pauseLabel` | `'Pause'` | Plot button while a path is running |
|
||||||
| `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 |
|
| `bboxLabel` | `'Bounding box'` | Travel drawing extents, pen up |
|
||||||
| `abortLabel` | `'Abort'` | Reset test/plot preview to the start |
|
| `abortLabel` | `'Abort'` | Stop sending a live Plot |
|
||||||
|
|
||||||
### `showInfos()`
|
### `showInfos()`
|
||||||
|
|
||||||
@@ -151,9 +153,9 @@ Returns estimated duration in **milliseconds** for the current path buffer, or f
|
|||||||
|
|
||||||
### `pausePlot()` / `resumePlot()` / `abortToStart()`
|
### `pausePlot()` / `resumePlot()` / `abortToStart()`
|
||||||
|
|
||||||
**Pause** / first **Stop** wait for the current segment to finish (`M400`), then lift the pen. **Resume** continues from there.
|
**Pause** waits for the current segment to finish (`M400`), then lifts 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.
|
**Abort** is shown during a live Plot, and after reload if a plot was saved to resume. Button or **A**: while plotting it stops sending further moves (the machine finishes the in-flight `G1`). For a restored plot it discards that saved path, so Plot will not continue a previous sketch. During Test, press **Test** again to redraw.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+36
-15
@@ -91,7 +91,7 @@ p5-wallplotter/
|
|||||||
|
|
||||||
Dann öffne darin *sketch.js*. Den Rest (HTML, Buttons, Verbinden) kannst du lassen. Ändere die Inhalte in der Funktion *drawPlot*: dort steht, was gezeichnet wird.
|
Dann öffne darin *sketch.js*. Den Rest (HTML, Buttons, Verbinden) kannst du lassen. Ändere die Inhalte in der Funktion *drawPlot*: dort steht, was gezeichnet wird.
|
||||||
|
|
||||||
Chrome, gleicher Server: [http://localhost:8080/sketches/mein-sketch/](http://localhost:8080/sketches/mein-sketch/) (Ordnername in der URL anpassen). Nach dem Speichern die Seite neu laden (*F5*). Der Python-Server muss nicht neu starten.
|
Chrome, gleicher Server: [http://localhost:8080/sketches/mein-sketch/](http://localhost:8080/sketches/mein-sketch/) (Ordnername in der URL anpassen). Wie du die Vorschau startest und nach Änderungen neu lädst: Abschnitt 3.
|
||||||
|
|
||||||
### Was *sketch.js* macht
|
### Was *sketch.js* macht
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ Chrome, gleicher Server: [http://localhost:8080/sketches/mein-sketch/](http://lo
|
|||||||
- *new WallPlotter({ … })* — Verbindung zum Plotter. *penUpGcode* / *penDownGcode* sind die Servo-Winkel für Stift hoch und runter
|
- *new WallPlotter({ … })* — Verbindung zum Plotter. *penUpGcode* / *penDownGcode* sind die Servo-Winkel für Stift hoch und runter
|
||||||
- *setSpeed(3000, 1500)* — Tempo in mm/min: zuerst leer fahren (Travel), dann zeichnen (Draw)
|
- *setSpeed(3000, 1500)* — Tempo in mm/min: zuerst leer fahren (Travel), dann zeichnen (Draw)
|
||||||
- *enableKeyInputs* — Tastatur: C, H, Pfeile, …
|
- *enableKeyInputs* — Tastatur: C, H, Pfeile, …
|
||||||
- *enablePlotButtons* — Buttons Connect, Plot, Test, Abort
|
- *enablePlotButtons* — Buttons Connect, Test, Plot, Bounding box, Abort
|
||||||
|
|
||||||
*plot* und *test* rufen dieselbe Zeichnung auf. *false* = G-code an den Plotter, *true* = nur Vorschau im Browser.
|
*plot* und *test* rufen dieselbe Zeichnung auf. *false* = G-code an den Plotter, *true* = nur Vorschau im Browser.
|
||||||
|
|
||||||
@@ -145,7 +145,24 @@ async function drawPlot(testOnly) {
|
|||||||
|
|
||||||
Mehr Befehle: [lib/doc.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/lib/doc.md).
|
Mehr Befehle: [lib/doc.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/lib/doc.md).
|
||||||
|
|
||||||
## 3.1 Plotter anschliessen
|
## 3. Testen
|
||||||
|
|
||||||
|
Der Plotter bleibt aus. **Test** zeigt die Zeichnung nur im Browser — kein G-code, keine Motoren.
|
||||||
|
|
||||||
|
1. Sketch in Chrome öffnen, z. B. [http://localhost:8080/sketches/mein-sketch/](http://localhost:8080/sketches/mein-sketch/) (oder *examples/zickzack/*).
|
||||||
|
2. Button **Test** oder Taste **T**. Die Gondel-Linie läuft als Vorschau über den Bildschirm.
|
||||||
|
3. Nochmals **Test** oder **T** zeichnet die Vorschau neu (kein Abort während des Tests).
|
||||||
|
|
||||||
|
Nach jeder Änderung in *sketch.js*: in VS Code speichern, dann in Chrome **neu laden**, sonst siehst du die alte Version (Cache).
|
||||||
|
|
||||||
|
- Hart neu laden: *Ctrl+Shift+R* (Windows/Linux) bzw. *Cmd+Shift+R* (macOS).
|
||||||
|
- Oder die Entwicklertools öffnen (*F12* bzw. *Cmd+Option+I*), oben **Netzwerk**, Häkchen **Cache deaktivieren**. Die Tools müssen **offen bleiben**. Dann reicht ein normales Neuladen (*F5* / *Cmd+R*).
|
||||||
|
|
||||||
|
Danach wieder **Test** oder **T** — jetzt gilt der geänderte Sketch.
|
||||||
|
|
||||||
|
## 4. Plotten
|
||||||
|
|
||||||
|
## 4.1 Plotter anschliessen
|
||||||
|
|
||||||
### Hardware-Reihenfolge
|
### Hardware-Reihenfolge
|
||||||
|
|
||||||
@@ -156,7 +173,7 @@ Mehr Befehle: [lib/doc.md](https://git.collectivemedia.space/martin/p5-wallplott
|
|||||||
5. **Connect** klicken oder Taste **C**.
|
5. **Connect** klicken oder Taste **C**.
|
||||||
6. Im Dialog den Serial-Port der Steuerung wählen.
|
6. Im Dialog den Serial-Port der Steuerung wählen.
|
||||||
|
|
||||||
## 3.2 Home setzen (wichtig)
|
## 4.2 Home setzen (wichtig)
|
||||||
|
|
||||||
Marlin kennt nach dem Einschalten **keine** absolute Wand-Position. Du sagst ihm, wo „jetzt“ der Ursprung (Blattmitte = Koordinate 0/0) ist. Die Pfeiltasten funktionieren erst nach dem ersten Home:
|
Marlin kennt nach dem Einschalten **keine** absolute Wand-Position. Du sagst ihm, wo „jetzt“ der Ursprung (Blattmitte = Koordinate 0/0) ist. Die Pfeiltasten funktionieren erst nach dem ersten Home:
|
||||||
|
|
||||||
@@ -171,7 +188,7 @@ Ohne Home bleibt Plot gesperrt.
|
|||||||
|
|
||||||
Diese Firmware hat keinen *EMERGENCY_PARSER*. Ein Stopp im Browser bricht nur das *weitere Senden* ab; die bereits laufende *G1*-Fahrt läuft weiter. Für einen harten Stopp: **Strom trennen**.
|
Diese Firmware hat keinen *EMERGENCY_PARSER*. Ein Stopp im Browser bricht nur das *weitere Senden* ab; die bereits laufende *G1*-Fahrt läuft weiter. Für einen harten Stopp: **Strom trennen**.
|
||||||
|
|
||||||
## 3.3 Bedienung
|
## 4.3 Bedienung
|
||||||
|
|
||||||
### Tasten, Buttons, HUD
|
### Tasten, Buttons, HUD
|
||||||
|
|
||||||
@@ -179,18 +196,21 @@ Shortcuts:
|
|||||||
|
|
||||||
- **C** — Verbinden / trennen (Port-Dialog)
|
- **C** — Verbinden / trennen (Port-Dialog)
|
||||||
- **H** — Hier ist Home (*G92 X0 Y0*): zuerst an der Gondel, nach dem Joggen nochmals in der Blattmitte
|
- **H** — Hier ist Home (*G92 X0 Y0*): zuerst an der Gondel, nach dem Joggen nochmals in der Blattmitte
|
||||||
- **T** — Test (Vorschau)
|
- **T** — Test (Vorschau). Nochmals **T** zeichnet neu
|
||||||
- **A** — Abort: Test auf Start zurück
|
- **B** — Bounding box: Gondel fährt Stift oben um die Ausdehnung der Zeichnung
|
||||||
|
- **A** — Abort: während eines Plots (laufendes *G1* fährt zu Ende), oder nach einem Reload den gespeicherten Plot verwerfen
|
||||||
- **P** — Stift umschalten
|
- **P** — Stift umschalten
|
||||||
- **0** — Nach (0, 0) fahren (Stift oben)
|
- **0** — Nach (0, 0) fahren (Stift oben)
|
||||||
- **← → ↑ ↓** — Joggen, Stift oben
|
- **← → ↑ ↓** — Joggen, Stift oben
|
||||||
- **Shift + Pfeile** — Joggen, Stift unten
|
- **Shift + Pfeile** — Joggen, Stift unten
|
||||||
- **+** / **−** — Schrittweite 1–100 mm
|
- **+** / **−** — Schrittweite 1–100 mm
|
||||||
- **S** — Plot stoppen / pausieren
|
- **S** — Plot pausieren / fortsetzen
|
||||||
|
|
||||||
|
Nach **Test** erscheint **Bounding box**. **Plot** nur wenn verbunden. **Plot** schickt genau diese Vorschau an den Plotter — die Zeichnung wird nicht neu gewürfelt. **Bounding box** fährt Stift oben um das Rechteck der Zeichnung, damit du siehst, ob alles aufs Blatt passt. Während eines Plots wird **Plot** zu **Pause** / **Resume**, und **Abort** erscheint. Nach einem Reload mit gespeichertem Plot bleibt **Abort** sichtbar, falls du einen anderen Sketch geladen hast und den alten Pfad nicht fortsetzen willst. **Test** bleibt während des Tests **Test** (nochmals drücken = neu zeichnen).
|
||||||
|
|
||||||
Das HUD (unten und als Overlay) zeigt Verbindung, Home, Position, Stift, Serial-Log.
|
Das HUD (unten und als Overlay) zeigt Verbindung, Home, Position, Stift, Serial-Log.
|
||||||
|
|
||||||
## 4. Polargraph und Marlin
|
## 5. Polargraph und Marlin
|
||||||
|
|
||||||
### Der Plotter und seine Teile
|
### Der Plotter und seine Teile
|
||||||
|
|
||||||
@@ -250,7 +270,7 @@ Befehlsliste: [gcode.md](https://git.collectivemedia.space/martin/p5-wallplotter
|
|||||||
|
|
||||||
Marlin ist keine Zeichen-App. Es weiss nichts von p5 oder deinem Sketch. Es bekommt nur Koordinaten und Servo-Winkel. Pfadplanung, Stiftlogik und die Vorschau liegen in *WallPlotter.js*. Die Firmware ist der Bewegungscontroller: parsen, in Riemenlängen umrechnen, Schritte erzeugen, *ok* sagen.
|
Marlin ist keine Zeichen-App. Es weiss nichts von p5 oder deinem Sketch. Es bekommt nur Koordinaten und Servo-Winkel. Pfadplanung, Stiftlogik und die Vorschau liegen in *WallPlotter.js*. Die Firmware ist der Bewegungscontroller: parsen, in Riemenlängen umrechnen, Schritte erzeugen, *ok* sagen.
|
||||||
|
|
||||||
## 5. Fehlerbehebung
|
## 6. Fehlerbehebung
|
||||||
|
|
||||||
- **Kein Port-Dialog, oder „Serial not supported“** — **Chrome**, nicht Firefox oder Safari. Seite über *http://localhost:…* öffnen, nicht als Datei.
|
- **Kein Port-Dialog, oder „Serial not supported“** — **Chrome**, nicht Firefox oder Safari. Seite über *http://localhost:…* öffnen, nicht als Datei.
|
||||||
- **Port erscheint nicht** — Strom der Steuerung an? Daten-USB-Kabel? Linux: Gruppe *dialout* oder *uucp*, neu einloggen; im Terminal nach *ttyACM* oder *ttyUSB* schauen. Windows: Geräte-Manager → COM-Port. macOS: Tab schliessen und neu öffnen (Port oft blockiert).
|
- **Port erscheint nicht** — Strom der Steuerung an? Daten-USB-Kabel? Linux: Gruppe *dialout* oder *uucp*, neu einloggen; im Terminal nach *ttyACM* oder *ttyUSB* schauen. Windows: Geräte-Manager → COM-Port. macOS: Tab schliessen und neu öffnen (Port oft blockiert).
|
||||||
@@ -266,15 +286,16 @@ Marlin ist keine Zeichen-App. Es weiss nichts von p5 oder deinem Sketch. Es beko
|
|||||||
|
|
||||||
Serial-Log im HUD lesen: Marlin-Fehler stehen dort als Text, nicht als Browser-Alert.
|
Serial-Log im HUD lesen: Marlin-Fehler stehen dort als Text, nicht als Browser-Alert.
|
||||||
|
|
||||||
## 6. Mini-Ablauf zum Merken
|
## 7. Mini-Ablauf zum Merken
|
||||||
|
|
||||||
1. ZIP herunterladen, in VS Code den Ordner *p5-wallplotter* öffnen.
|
1. ZIP herunterladen, in VS Code den Ordner *p5-wallplotter* öffnen.
|
||||||
2. Terminal in VS Code öffnen, *python3 -m http.server 8080* (Windows: *py -m http.server 8080*).
|
2. Terminal in VS Code öffnen, *python3 -m http.server 8080* (Windows: *py -m http.server 8080*).
|
||||||
3. Chrome: [http://localhost:8080/examples/zickzack/](http://localhost:8080/examples/zickzack/).
|
3. Chrome: [http://localhost:8080/examples/zickzack/](http://localhost:8080/examples/zickzack/).
|
||||||
4. Eigenen Ordner unter *sketches/* anlegen, *drawPlot* anpassen.
|
4. Eigenen Ordner unter *sketches/* anlegen, *drawPlot* anpassen.
|
||||||
5. Plotter mit Strom und USB verbinden.
|
5. **Test** oder **T**. Nach Änderungen speichern, hart neu laden (*Ctrl+Shift+R*), nochmals **T**.
|
||||||
6. **C** → Port wählen.
|
6. Plotter mit Strom und USB verbinden.
|
||||||
7. **H** (aktuelle Position), zur Blattmitte joggen, nochmals **H**.
|
7. **C** → Port wählen.
|
||||||
8. **Test**, dann **Plot**.
|
8. **H** (aktuelle Position), zur Blattmitte joggen, nochmals **H**.
|
||||||
|
9. **Plot**.
|
||||||
|
|
||||||
API-Referenz: [lib/doc.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/lib/doc.md). G-code: [gcode.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/gcode.md).
|
API-Referenz: [lib/doc.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/lib/doc.md). G-code: [gcode.md](https://git.collectivemedia.space/martin/p5-wallplotter/src/branch/main/gcode.md).
|
||||||
|
|||||||
Reference in New Issue
Block a user