Manual stitching default point locations fixed in Flip Target mode
This commit is contained in:
parent
0022cb6a3a
commit
d69d18d797
2 changed files with 58 additions and 24 deletions
|
|
@ -9,9 +9,6 @@ class StitchEditor {
|
||||||
this.points = [];
|
this.points = [];
|
||||||
this.colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff', '#ffffff', '#ff8800', '#88ff00'];
|
this.colors = ['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#00ffff', '#ff00ff', '#ffffff', '#ff8800', '#88ff00'];
|
||||||
|
|
||||||
// FIX: We pass 'null' as the 3rd argument (onClick) to prevent adding new points.
|
|
||||||
// Points are only generated via setGrid() and moved via dragging.
|
|
||||||
|
|
||||||
this.viewSrc = new PanZoomCanvas('stitch-canvas-src',
|
this.viewSrc = new PanZoomCanvas('stitch-canvas-src',
|
||||||
(c, k) => this.drawPts(c, k, 's'),
|
(c, k) => this.drawPts(c, k, 's'),
|
||||||
null,
|
null,
|
||||||
|
|
@ -50,6 +47,15 @@ class StitchEditor {
|
||||||
this.viewDst.setMirror(newVal);
|
this.viewDst.setMirror(newVal);
|
||||||
btn.style.background = newVal ? '#e0f2fe' : '';
|
btn.style.background = newVal ? '#e0f2fe' : '';
|
||||||
btn.style.borderColor = newVal ? '#2563eb' : '';
|
btn.style.borderColor = newVal ? '#2563eb' : '';
|
||||||
|
|
||||||
|
// FIX: Invert point coordinates so they stay visually in place
|
||||||
|
if (this.viewDst.bmp) {
|
||||||
|
const w = this.viewDst.bmp.width;
|
||||||
|
this.points.forEach(p => {
|
||||||
|
p.d.x = w - p.d.x;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,6 +125,13 @@ class StitchEditor {
|
||||||
}
|
}
|
||||||
const sGrid = this.getGridCoords(this.viewSrc.bmp.width, this.viewSrc.bmp.height, n);
|
const sGrid = this.getGridCoords(this.viewSrc.bmp.width, this.viewSrc.bmp.height, n);
|
||||||
const dGrid = this.getGridCoords(this.viewDst.bmp.width, this.viewDst.bmp.height, n);
|
const dGrid = this.getGridCoords(this.viewDst.bmp.width, this.viewDst.bmp.height, n);
|
||||||
|
|
||||||
|
// Adjust destination grid for mirroring so visual position matches
|
||||||
|
if (this.viewDst.isMirrored) {
|
||||||
|
const w = this.viewDst.bmp.width;
|
||||||
|
dGrid.forEach(p => { p.x = w - p.x; });
|
||||||
|
}
|
||||||
|
|
||||||
for(let i=0; i<sGrid.length; i++) {
|
for(let i=0; i<sGrid.length; i++) {
|
||||||
this.points.push({ s: sGrid[i], d: dGrid[i], color: this.colors[i % this.colors.length] });
|
this.points.push({ s: sGrid[i], d: dGrid[i], color: this.colors[i % this.colors.length] });
|
||||||
}
|
}
|
||||||
|
|
@ -149,12 +162,11 @@ class StitchEditor {
|
||||||
const bS = await createImageBitmap(i1.blob);
|
const bS = await createImageBitmap(i1.blob);
|
||||||
const bD = await createImageBitmap(i2.blob);
|
const bD = await createImageBitmap(i2.blob);
|
||||||
|
|
||||||
this.modal.style.display = 'flex'; // Trigger Layout
|
this.modal.style.display = 'flex';
|
||||||
|
|
||||||
this.viewSrc.setImage(bS);
|
this.viewSrc.setImage(bS);
|
||||||
this.viewDst.setImage(bD);
|
this.viewDst.setImage(bD);
|
||||||
|
|
||||||
// Reset Zoom (Delay to ensure Canvas dimensions are updated by Observer)
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this.viewSrc.fit();
|
this.viewSrc.fit();
|
||||||
|
|
@ -167,6 +179,39 @@ class StitchEditor {
|
||||||
|
|
||||||
let shouldFlip = false;
|
let shouldFlip = false;
|
||||||
|
|
||||||
|
// 1. Determine Flip State
|
||||||
|
if (existing) {
|
||||||
|
if (existing.isManual && existing.manualPoints) {
|
||||||
|
const pointsForSolve = existing.manualPoints.map(p => {
|
||||||
|
// Ensure order is src->dst for solve
|
||||||
|
return (existing.fromImageId === this.srcId) ? {s:p.s, d:p.d} : {s:p.d, d:p.s};
|
||||||
|
});
|
||||||
|
if (pointsForSolve.length >= 3) {
|
||||||
|
const tempRes = this.cv.solveManual(pointsForSolve);
|
||||||
|
if (tempRes && this.isReflection(tempRes.hData)) {
|
||||||
|
shouldFlip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!existing.isManual && existing.homography) {
|
||||||
|
let hToUse = existing.homography;
|
||||||
|
if (existing.fromImageId !== this.srcId) {
|
||||||
|
hToUse = existing.inverseHomography;
|
||||||
|
}
|
||||||
|
if (this.isReflection(hToUse)) {
|
||||||
|
shouldFlip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Apply Flip State BEFORE generating points
|
||||||
|
this.viewDst.setMirror(shouldFlip);
|
||||||
|
const flipBtn = document.getElementById('btn-stitch-flip');
|
||||||
|
if(flipBtn) {
|
||||||
|
flipBtn.style.background = shouldFlip ? '#e0f2fe' : '';
|
||||||
|
flipBtn.style.borderColor = shouldFlip ? '#2563eb' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Generate Points
|
||||||
if (existing) {
|
if (existing) {
|
||||||
if (existing.isManual && existing.manualPoints) {
|
if (existing.isManual && existing.manualPoints) {
|
||||||
existing.manualPoints.forEach((p, i) => {
|
existing.manualPoints.forEach((p, i) => {
|
||||||
|
|
@ -175,12 +220,6 @@ class StitchEditor {
|
||||||
else { ptS=p.d; ptD=p.s; }
|
else { ptS=p.d; ptD=p.s; }
|
||||||
this.points.push({ s: {x:ptS.x, y:ptS.y}, d: {x:ptD.x, y:ptD.y}, color: this.colors[i % this.colors.length] });
|
this.points.push({ s: {x:ptS.x, y:ptS.y}, d: {x:ptD.x, y:ptD.y}, color: this.colors[i % this.colors.length] });
|
||||||
});
|
});
|
||||||
if (this.points.length >= 3) {
|
|
||||||
const tempRes = this.cv.solveManual(this.points);
|
|
||||||
if (tempRes && this.isReflection(tempRes.hData)) {
|
|
||||||
shouldFlip = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (!existing.isManual && existing.homography) {
|
} else if (!existing.isManual && existing.homography) {
|
||||||
let hToUse = existing.homography;
|
let hToUse = existing.homography;
|
||||||
let invHToUse = existing.inverseHomography;
|
let invHToUse = existing.inverseHomography;
|
||||||
|
|
@ -188,20 +227,11 @@ class StitchEditor {
|
||||||
hToUse = existing.inverseHomography;
|
hToUse = existing.inverseHomography;
|
||||||
invHToUse = existing.homography;
|
invHToUse = existing.homography;
|
||||||
}
|
}
|
||||||
if (this.isReflection(hToUse)) {
|
|
||||||
shouldFlip = true;
|
|
||||||
}
|
|
||||||
this.setGrid(3, hToUse, invHToUse);
|
this.setGrid(3, hToUse, invHToUse);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setGrid(3);
|
// FIX: Default to 2x2 (4 points) for fresh stitch
|
||||||
}
|
this.setGrid(2);
|
||||||
|
|
||||||
this.viewDst.setMirror(shouldFlip);
|
|
||||||
const flipBtn = document.getElementById('btn-stitch-flip');
|
|
||||||
if(flipBtn) {
|
|
||||||
flipBtn.style.background = shouldFlip ? '#e0f2fe' : '';
|
|
||||||
flipBtn.style.borderColor = shouldFlip ? '#2563eb' : '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
@ -242,12 +272,16 @@ class StitchEditor {
|
||||||
if(mode==='check') {
|
if(mode==='check') {
|
||||||
for(let i=this.points.length-1; i>=0; i--) {
|
for(let i=this.points.length-1; i>=0; i--) {
|
||||||
const pt = (side==='s')?this.points[i].s:this.points[i].d;
|
const pt = (side==='s')?this.points[i].s:this.points[i].d;
|
||||||
|
// FIX: Use raw image coordinates (PanZoomCanvas handles mirror logic in getImgCoords)
|
||||||
if(Math.hypot(x-pt.x, y-pt.y) < 20) return i;
|
if(Math.hypot(x-pt.x, y-pt.y) < 20) return i;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
} else if(mode==='move') {
|
} else if(mode==='move') {
|
||||||
const pt = (side==='s')?this.points[idx].s:this.points[idx].d;
|
const pt = (side==='s')?this.points[idx].s:this.points[idx].d;
|
||||||
pt.x+=x; pt.y+=y; this.refresh();
|
// FIX: Standard addition works because getImgCoords returns inverted delta for mirrored images
|
||||||
|
pt.x += x;
|
||||||
|
pt.y += y;
|
||||||
|
this.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -352,7 +352,7 @@ async function saveBoardSettings() {
|
||||||
if (newName) {
|
if (newName) {
|
||||||
p.name = newName; p.section = newSec; p.deviceId = newDevId;
|
p.name = newName; p.section = newSec; p.deviceId = newDevId;
|
||||||
await db.addProject(p);
|
await db.addProject(p);
|
||||||
document.getElementById('board-settings-modal').style.display = 'none';
|
history.back();
|
||||||
if (newDevId !== currentDeviceId) {
|
if (newDevId !== currentDeviceId) {
|
||||||
currentDeviceId = newDevId;
|
currentDeviceId = newDevId;
|
||||||
updateDeviceDropdown();
|
updateDeviceDropdown();
|
||||||
|
|
@ -377,7 +377,7 @@ async function saveDeviceSettings() {
|
||||||
deviceList = await db.getDevices();
|
deviceList = await db.getDevices();
|
||||||
updateDeviceDropdown();
|
updateDeviceDropdown();
|
||||||
}
|
}
|
||||||
document.getElementById('device-settings-modal').style.display = 'none';
|
history.back();
|
||||||
}
|
}
|
||||||
async function deleteCurrentDevice() {
|
async function deleteCurrentDevice() {
|
||||||
if (!currentDeviceId) return;
|
if (!currentDeviceId) return;
|
||||||
|
|
@ -402,7 +402,7 @@ async function deleteCurrentDevice() {
|
||||||
}
|
}
|
||||||
|
|
||||||
await db._tx('devices', 'readwrite', s => s.delete(currentDeviceId));
|
await db._tx('devices', 'readwrite', s => s.delete(currentDeviceId));
|
||||||
document.getElementById('device-settings-modal').style.display = 'none';
|
history.back();
|
||||||
|
|
||||||
deviceList = await db.getDevices();
|
deviceList = await db.getDevices();
|
||||||
if (deviceList.length === 0) {
|
if (deviceList.length === 0) {
|
||||||
|
|
@ -1268,7 +1268,7 @@ async function deleteBom() {
|
||||||
if(bomList.length < 1) return;
|
if(bomList.length < 1) return;
|
||||||
if(await confirmAction("Delete this Board and all its content?", "Delete Board")){
|
if(await confirmAction("Delete this Board and all its content?", "Delete Board")){
|
||||||
await db.deleteProject(currentBomId);
|
await db.deleteProject(currentBomId);
|
||||||
document.getElementById('board-settings-modal').style.display = 'none';
|
history.back();
|
||||||
await loadDeviceBoms();
|
await loadDeviceBoms();
|
||||||
resetStickyEditor();
|
resetStickyEditor();
|
||||||
}
|
}
|
||||||
|
|
@ -1544,7 +1544,7 @@ async function saveImageSettings() {
|
||||||
if(connName) connName.innerText = newName;
|
if(connName) connName.innerText = newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('image-settings-modal').style.display = 'none';
|
history.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete Image (Close modal on success)
|
// Delete Image (Close modal on success)
|
||||||
|
|
@ -1565,7 +1565,7 @@ async function deleteCurrentImage() {
|
||||||
bomImages = bomImages.filter(i => i.id !== currentImgId);
|
bomImages = bomImages.filter(i => i.id !== currentImgId);
|
||||||
|
|
||||||
// Close the settings modal if open
|
// Close the settings modal if open
|
||||||
document.getElementById('image-settings-modal').style.display = 'none';
|
history.back();
|
||||||
|
|
||||||
currentImgId = null;
|
currentImgId = null;
|
||||||
await loadProjectData();
|
await loadProjectData();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue