plotter drawing size and stop with key
This commit is contained in:
+134
-27
@@ -9,6 +9,8 @@ class WallPlotter {
|
||||
static VERSION = 'wait-ok+M400';
|
||||
static MAX_FEED_MM_MIN = 10000;
|
||||
static MIN_FEED_MM_MIN = 1;
|
||||
/** Offline overlay size (mm) — 150×150 cm centred on origin */
|
||||
static PREVIEW_SIZE_MM = 1500;
|
||||
|
||||
constructor(options = {}) {
|
||||
this.baudRate = options.baudRate ?? 115200;
|
||||
@@ -19,10 +21,11 @@ class WallPlotter {
|
||||
this.penDownGcode = options.penDownGcode ?? 'M280 P0 S50';
|
||||
this.penUpDelayMs = options.penUpDelayMs ?? 150;
|
||||
this.penDownDelayMs = options.penDownDelayMs ?? 50;
|
||||
this.machineXMin = options.machineXMin ?? -850;
|
||||
this.machineXMax = options.machineXMax ?? 850;
|
||||
this.machineYMin = options.machineYMin ?? -1000;
|
||||
this.machineYMax = options.machineYMax ?? 1000;
|
||||
this.machineXMin = options.machineXMin ?? -830;
|
||||
this.machineXMax = options.machineXMax ?? 830;
|
||||
this.machineYMin = options.machineYMin ?? -1200;
|
||||
this.machineYMax = options.machineYMax ?? 800;
|
||||
this.previewSizeMm = options.previewSizeMm ?? WallPlotter.PREVIEW_SIZE_MM;
|
||||
this.jogIntervalMs = options.jogIntervalMs ?? 150;
|
||||
this.maxLogLines = options.maxLogLines ?? 8;
|
||||
|
||||
@@ -143,6 +146,21 @@ class WallPlotter {
|
||||
this._pathAbort = true;
|
||||
}
|
||||
|
||||
/** Abort path; if a live plot is running, also quick-stop Marlin (M410). */
|
||||
async stopPlot() {
|
||||
if (!this.isPlotting) return;
|
||||
this.stopPath();
|
||||
if (this.connected && !this._plotTestOnly) {
|
||||
try {
|
||||
await this.send('M410', { waitOk: false, allowWithoutHome: true });
|
||||
} catch (_) {
|
||||
/* port may be busy */
|
||||
}
|
||||
}
|
||||
this.statusText = 'Stopped';
|
||||
this.onLog('plot stopped');
|
||||
}
|
||||
|
||||
_restartPathAfterAbort(mode) {
|
||||
this._pendingAfterAbort = mode;
|
||||
this._pathAbort = true;
|
||||
@@ -526,7 +544,8 @@ class WallPlotter {
|
||||
this.onStatusChange('connected');
|
||||
|
||||
await this.send('M17');
|
||||
await this.penUp();
|
||||
await this.penUp(true);
|
||||
await this.send('M665'); // polargraph draw area → updates machine bounds
|
||||
await this.send('M114');
|
||||
}
|
||||
|
||||
@@ -632,15 +651,15 @@ class WallPlotter {
|
||||
return false;
|
||||
}
|
||||
|
||||
async penUp() {
|
||||
if (!this.isPenDown) return;
|
||||
async penUp(force = false) {
|
||||
if (!force && !this.isPenDown) return;
|
||||
await this.send(this.penUpGcode);
|
||||
await this._delay(this.penUpDelayMs);
|
||||
this.isPenDown = false;
|
||||
}
|
||||
|
||||
async penDown() {
|
||||
if (this.isPenDown) return;
|
||||
async penDown(force = false) {
|
||||
if (!force && this.isPenDown) return;
|
||||
await this.send(this.penDownGcode);
|
||||
await this._delay(this.penDownDelayMs);
|
||||
this.isPenDown = true;
|
||||
@@ -715,6 +734,11 @@ class WallPlotter {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (k === 's') {
|
||||
if (this.isPlotting) await this.stopPlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this._keysEnabled || !this.connected) return;
|
||||
|
||||
if (k === 'h') {
|
||||
@@ -728,8 +752,8 @@ class WallPlotter {
|
||||
}
|
||||
|
||||
if (k === 'p') {
|
||||
if (this.isPenDown) await this.penUp();
|
||||
else await this.penDown();
|
||||
if (this.isPenDown) await this.penUp(true);
|
||||
else await this.penDown(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -806,18 +830,40 @@ class WallPlotter {
|
||||
}
|
||||
}
|
||||
|
||||
_getDisplayBounds() {
|
||||
if (this.connected) {
|
||||
return {
|
||||
xMin: this.machineXMin,
|
||||
xMax: this.machineXMax,
|
||||
yMin: this.machineYMin,
|
||||
yMax: this.machineYMax,
|
||||
preview: false,
|
||||
};
|
||||
}
|
||||
const half = this.previewSizeMm / 2;
|
||||
return {
|
||||
xMin: -half,
|
||||
xMax: half,
|
||||
yMin: -half,
|
||||
yMax: half,
|
||||
preview: true,
|
||||
};
|
||||
}
|
||||
|
||||
_getPlotLayout() {
|
||||
const margin = 60;
|
||||
const size = min(width, height) - margin * 2;
|
||||
const worldW = this.machineXMax - this.machineXMin;
|
||||
const worldH = this.machineYMax - this.machineYMin;
|
||||
const b = this._getDisplayBounds();
|
||||
const worldW = Math.max(1, b.xMax - b.xMin);
|
||||
const worldH = Math.max(1, b.yMax - b.yMin);
|
||||
// Fit full display rectangle; centre the view on work origin (0,0)
|
||||
const scale = min(size / worldW, size / worldH);
|
||||
return {
|
||||
cx: width / 2,
|
||||
cy: height / 2 - 20,
|
||||
scale,
|
||||
worldCx: (this.machineXMin + this.machineXMax) / 2,
|
||||
worldCy: (this.machineYMin + this.machineYMax) / 2,
|
||||
worldCx: 0,
|
||||
worldCy: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -856,22 +902,54 @@ class WallPlotter {
|
||||
|
||||
_drawPlotArea() {
|
||||
const layout = this._getPlotLayout();
|
||||
const bounds = this._getDisplayBounds();
|
||||
|
||||
push();
|
||||
translate(layout.cx, layout.cy);
|
||||
|
||||
stroke(50);
|
||||
strokeWeight(1);
|
||||
const tl = this._worldToScreen(this.machineXMin, this.machineYMax, layout);
|
||||
const br = this._worldToScreen(this.machineXMax, this.machineYMin, layout);
|
||||
const tl = this._worldToScreen(bounds.xMin, bounds.yMax, layout);
|
||||
const br = this._worldToScreen(bounds.xMax, bounds.yMin, layout);
|
||||
const w = br.x - tl.x;
|
||||
const h = br.y - tl.y;
|
||||
|
||||
noFill();
|
||||
rectMode(CORNER);
|
||||
rect(tl.x, tl.y, br.x - tl.x, br.y - tl.y);
|
||||
strokeWeight(1);
|
||||
if (bounds.preview) {
|
||||
stroke(90, 90, 110);
|
||||
drawingContext.setLineDash([8, 6]);
|
||||
} else {
|
||||
stroke(70);
|
||||
drawingContext.setLineDash([]);
|
||||
}
|
||||
rect(tl.x, tl.y, w, h);
|
||||
drawingContext.setLineDash([]);
|
||||
|
||||
stroke(60);
|
||||
line(tl.x, 0, br.x, 0);
|
||||
line(0, tl.y, 0, br.y);
|
||||
|
||||
// Size labels in cm along bottom and right edges
|
||||
const widthCm = (bounds.xMax - bounds.xMin) / 10;
|
||||
const heightCm = (bounds.yMax - bounds.yMin) / 10;
|
||||
const widthLabel = Number.isInteger(widthCm)
|
||||
? `${widthCm} cm`
|
||||
: `${widthCm.toFixed(1)} cm`;
|
||||
const heightLabel = Number.isInteger(heightCm)
|
||||
? `${heightCm} cm`
|
||||
: `${heightCm.toFixed(1)} cm`;
|
||||
noStroke();
|
||||
fill(bounds.preview ? 140 : 160);
|
||||
textFont('monospace', 12);
|
||||
textAlign(CENTER, TOP);
|
||||
text(widthLabel, (tl.x + br.x) / 2, br.y + 8);
|
||||
push();
|
||||
translate(br.x + 8, (tl.y + br.y) / 2);
|
||||
rotate(HALF_PI);
|
||||
textAlign(CENTER, BOTTOM);
|
||||
text(heightLabel, 0, 0);
|
||||
pop();
|
||||
|
||||
const rectBounds = this._getEffectiveDrawBounds();
|
||||
if (rectBounds) {
|
||||
const dtl = this._worldToScreen(rectBounds.left, rectBounds.top, layout);
|
||||
@@ -955,43 +1033,53 @@ class WallPlotter {
|
||||
92
|
||||
);
|
||||
text(`home ${this.homeSet ? 'set' : 'not set'}`, 16, 112);
|
||||
if (this.connected) {
|
||||
text(
|
||||
`machine X ${this.machineXMin.toFixed(0)}…${this.machineXMax.toFixed(0)} Y ${this.machineYMin.toFixed(0)}…${this.machineYMax.toFixed(0)} mm`,
|
||||
16,
|
||||
132
|
||||
);
|
||||
} else {
|
||||
const cm = this.previewSizeMm / 10;
|
||||
text(`preview ${cm} × ${cm} cm (connect for machine bounds)`, 16, 132);
|
||||
}
|
||||
|
||||
if (this.isPlotting) {
|
||||
fill(180, 220, 255);
|
||||
text(
|
||||
`plotting ${this.plotProgress}/${this.previewPath.length}`,
|
||||
16,
|
||||
132
|
||||
152
|
||||
);
|
||||
const elapsedMs = performance.now() - this._plotStartedAt;
|
||||
const remainMs = Math.max(0, this._plotEstimateMs - elapsedMs);
|
||||
text(
|
||||
`elapsed ${this._formatDuration(elapsedMs)} est ${this._formatDuration(this._plotEstimateMs)} remain ${this._formatDuration(remainMs)}`,
|
||||
16,
|
||||
152
|
||||
172
|
||||
);
|
||||
text(
|
||||
`seg wait ${this._lastSegmentMs.toFixed(0)} ms ×${this.timingMultiplier}`,
|
||||
16,
|
||||
172
|
||||
192
|
||||
);
|
||||
fill(220);
|
||||
}
|
||||
|
||||
if (this.connected && !this.homeSet && this._homeWarning) {
|
||||
fill(255, 180, 80);
|
||||
text(`! ${this._homeWarning}`, 16, this.isPlotting ? 192 : 132);
|
||||
text(`! ${this._homeWarning}`, 16, this.isPlotting ? 212 : 152);
|
||||
fill(220);
|
||||
}
|
||||
|
||||
text(
|
||||
`bounds L ${this._formatBound(this.drawBounds.left)} R ${this._formatBound(this.drawBounds.right)} T ${this._formatBound(this.drawBounds.top)} B ${this._formatBound(this.drawBounds.bottom)}`,
|
||||
16,
|
||||
this.isPlotting ? 212 : 152
|
||||
this.isPlotting ? 232 : 172
|
||||
);
|
||||
|
||||
const size = this._getDrawSize();
|
||||
const areaY = this.isPlotting ? 232 : 172;
|
||||
const areaY = this.isPlotting ? 252 : 192;
|
||||
if (size) {
|
||||
text(`area ${size.w.toFixed(1)} × ${size.h.toFixed(1)} mm`, 16, areaY);
|
||||
} else if (
|
||||
@@ -1012,7 +1100,8 @@ class WallPlotter {
|
||||
'+ / - step size',
|
||||
'H set current pos as home (G92) — required first',
|
||||
'0 go to origin (0, 0)',
|
||||
'P toggle pen',
|
||||
'P toggle pen (test up/down angles)',
|
||||
'S stop plot / test',
|
||||
'C connect / disconnect',
|
||||
];
|
||||
for (let i = 0; i < help.length; i++) {
|
||||
@@ -1074,6 +1163,7 @@ class WallPlotter {
|
||||
const next = this._okResolvers.shift();
|
||||
if (next) next.resolve();
|
||||
}
|
||||
this._parseMachineBounds(line);
|
||||
if (line.startsWith('X:')) {
|
||||
const match = line.match(/X:([-\d.]+)\s+Y:([-\d.]+)/);
|
||||
if (match) {
|
||||
@@ -1082,4 +1172,21 @@ class WallPlotter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse Marlin polargraph M665 report: M665 S… L… R… T… B… H… */
|
||||
_parseMachineBounds(line) {
|
||||
if (!/\bM665\b/i.test(line)) return;
|
||||
const l = line.match(/\bL(-?[\d.]+)/i);
|
||||
const r = line.match(/\bR(-?[\d.]+)/i);
|
||||
const t = line.match(/\bT(-?[\d.]+)/i);
|
||||
const b = line.match(/\bB(-?[\d.]+)/i);
|
||||
if (!l || !r || !t || !b) return;
|
||||
this.machineXMin = parseFloat(l[1]);
|
||||
this.machineXMax = parseFloat(r[1]);
|
||||
this.machineYMax = parseFloat(t[1]);
|
||||
this.machineYMin = parseFloat(b[1]);
|
||||
this.onLog(
|
||||
`machine bounds X ${this.machineXMin}…${this.machineXMax} Y ${this.machineYMin}…${this.machineYMax}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user