live plot example and screenToPlot helper

This commit is contained in:
Martin Fuchs
2026-09-09 12:06:56 +02:00
parent c129222b78
commit ce288bd35d
6 changed files with 198 additions and 2 deletions
+55
View File
@@ -320,6 +320,38 @@ class WallPlotter {
this.timingMultiplier = Math.max(0.1, Number(mult) || 1);
}
/**
* Convert canvas pixel coordinates to plot coordinates in mm.
* By default this uses machine bounds so live-click targeting stays stable.
* @param {number} px
* @param {number} py
* @param {{clamp?: boolean, useDisplayBounds?: boolean}} [options]
*/
screenToPlot(px, py, options = {}) {
const layout = this._getPlotLayout();
const bounds =
options.useDisplayBounds === true
? this._getDisplayBounds()
: {
xMin: this.machineXMin,
xMax: this.machineXMax,
yMin: this.machineYMin,
yMax: this.machineYMax,
};
const scale = Math.max(layout.scale || 0, 1e-9);
const worldX = (px - layout.cx) / scale + layout.worldCx;
const worldY = -(py - layout.cy) / scale + layout.worldCy;
const clamp = options.clamp !== false;
if (!clamp) return { x: worldX, y: worldY };
return {
x: Math.min(bounds.xMax, Math.max(bounds.xMin, worldX)),
y: Math.min(bounds.yMax, Math.max(bounds.yMin, worldY)),
};
}
// ===========================================================================
// Public API — path buffer (queue only; nothing is sent until runPath)
// ===========================================================================
@@ -929,19 +961,42 @@ class WallPlotter {
/** Pen up and travel to (x, y) mm immediately. Requires home. */
async moveTo(x, y) {
if (!this._requireHome()) return;
const startX = this.x;
const startY = this.y;
await this.penUp();
await this.send(`G1 X${this._fmt(x)} Y${this._fmt(y)} F${this.feedRateTravel}`);
this.x = x;
this.y = y;
await this._waitForMotionComplete(
Math.hypot((x ?? 0) - (startX ?? 0), (y ?? 0) - (startY ?? 0)),
this.feedRateTravel
);
}
/** Pen down and draw to (x, y) mm immediately. Requires home. */
async drawTo(x, y) {
if (!this._requireHome()) return;
const startX = this.x;
const startY = this.y;
await this.penDown();
await this.send(`G1 X${this._fmt(x)} Y${this._fmt(y)} F${this.feedRateDraw}`);
this.x = x;
this.y = y;
await this._waitForMotionComplete(
Math.hypot((x ?? 0) - (startX ?? 0), (y ?? 0) - (startY ?? 0)),
this.feedRateDraw
);
}
async _waitForMotionComplete(distanceMm, feedRateMmPerMin) {
const distance = Number.isFinite(distanceMm) ? Math.max(0, distanceMm) : 0;
const speedMmPerS = Math.max(1, Number(feedRateMmPerMin) || 1) / 60;
const etaMs = Math.round((distance / speedMmPerS) * 1000);
await this.send('M400', {
timeoutMs: Math.max(WallPlotter.OK_TIMEOUT_MS, etaMs + 15000),
// M400 legitimately blocks until mechanics are done moving.
waitLogAfterMs: Math.max(WallPlotter.OK_WAIT_LOG_MS, etaMs + 1500),
});
}
/** Travel to (0, 0). */