Files
rubenvillarreal cf264d7018 Simplify PoseAlign node to manual-only mode
- Remove all automatic alignment logic and keypoint detection
- Convert to pure manual transformation node using widget sliders
- Remove manual/auto mode toggle and Live UI toggle
- Ensure canvas preview exactly matches output transformations
- Simplify codebase by removing 400+ lines of complex auto-alignment code

Key changes:
- Python: Always use widget values (tx_A, ty_A, scale_A, angle_deg_A, etc.)
- JavaScript: Remove automatic mode detection and Live toggle UI
- Canvas interactions directly update widget values for immediate effect
- Output transformations now perfectly match canvas preview

Fixes issue where manual canvas adjustments weren't reflected in final output.
2025-08-02 09:34:43 +00:00

231 lines
6.9 KiB
JavaScript

/* modules/interaction_handler.js - Fixed for immediate live updates */
export class InteractionHandler {
constructor(canvas, transformManager, renderer) {
this.canvas = canvas;
this.transformManager = transformManager;
this.renderer = renderer;
this.state = null; // Will be set by main widget
this.isInteracting = false; // Track if user is actively interacting
}
setState(state) {
this.state = state;
}
// Track interaction state
startInteraction() {
this.isInteracting = true;
console.log("[Interaction] Started");
}
endInteraction() {
this.isInteracting = false;
console.log("[Interaction] Ended");
}
// Set up all event listeners
setupEventListeners() {
this.setupMouseEvents();
this.setupKeyboardEvents();
}
// Set up mouse event listeners with immediate feedback
setupMouseEvents() {
// Prevent context menu
this.canvas.addEventListener("contextmenu", e => e.preventDefault());
// Mouse down - start dragging
this.canvas.addEventListener("mousedown", (e) => {
this.startInteraction();
this.state.dragging = true;
this.state.which = e.button === 2 ? "B" : "A"; // Right click = B, Left click = A
const coords = this.renderer.getCanvasCoordinates(e);
this.state.lastX = coords.x;
this.state.lastY = coords.y;
this.canvas.style.cursor = "grabbing";
console.log(`[Interaction] Mouse down - pose ${this.state.which} active`);
// IMMEDIATE redraw when starting drag
this.renderer.draw();
});
// Mouse up - stop dragging
this.canvas.addEventListener("mouseup", () => {
this.state.dragging = false;
this.canvas.style.cursor = "crosshair";
console.log("[Interaction] Mouse up - dragging stopped");
// IMMEDIATE redraw when ending drag
this.renderer.draw();
// End interaction after a delay
setTimeout(() => this.endInteraction(), 100);
});
// Mouse leave - stop dragging and hovering
this.canvas.addEventListener("mouseleave", () => {
this.state.dragging = false;
this.state.hovering = false;
this.canvas.style.cursor = "crosshair";
this.endInteraction();
this.renderer.draw();
});
// Mouse enter - start hovering
this.canvas.addEventListener("mouseenter", () => {
this.state.hovering = true;
this.renderer.draw();
});
// Mouse move - handle dragging with immediate updates
this.canvas.addEventListener("mousemove", (e) => {
if (!this.state.dragging) return;
const coords = this.renderer.getCanvasCoordinates(e);
const dx = coords.x - this.state.lastX;
const dy = coords.y - this.state.lastY;
this.state.lastX = coords.x;
this.state.lastY = coords.y;
// Convert canvas pixel movement to reference image coordinate movement
const coordSys = this.renderer.calculateCoordinateSystem();
const { canvasScale } = coordSys;
// Scale the movement to match the reference coordinate system
const scaledDx = dx / canvasScale;
const scaledDy = dy / canvasScale;
const pose = this.state.which;
const currentTx = this.transformManager.getProperty(`tx_${pose}`, 0);
const currentTy = this.transformManager.getProperty(`ty_${pose}`, 0);
// Update properties
this.transformManager.setProperty(`tx_${pose}`, currentTx + scaledDx);
this.transformManager.setProperty(`ty_${pose}`, currentTy + scaledDy);
console.log(`[Interaction] Move ${pose}: tx=${(currentTx + scaledDx).toFixed(1)}, ty=${(currentTy + scaledDy).toFixed(1)}`);
// CRITICAL: Immediate redraw with forced manual mode
this.renderer.draw();
});
// Mouse wheel - handle scaling and rotation with immediate updates
this.canvas.addEventListener("wheel", (e) => {
e.preventDefault();
this.startInteraction();
const pose = this.state.which;
if (e.shiftKey) {
// Rotation with shift key
const rotationStep = e.deltaY > 0 ? 5 : -5;
const currentAngle = this.transformManager.getProperty(`angle_deg_${pose}`, 0);
const newAngle = currentAngle + rotationStep;
this.transformManager.setProperty(`angle_deg_${pose}`, newAngle);
console.log(`[Interaction] Rotate ${pose}: ${newAngle.toFixed(1)}°`);
} else {
// Scaling without shift key
const currentScale = this.transformManager.getProperty(`scale_${pose}`, 1);
const scaleStep = e.deltaY > 0 ? -0.05 : 0.05;
const newScale = Math.max(0.1, currentScale + scaleStep);
this.transformManager.setProperty(`scale_${pose}`, newScale);
console.log(`[Interaction] Scale ${pose}: ${newScale.toFixed(2)}`);
}
// CRITICAL: Immediate redraw
this.renderer.draw();
// End interaction after wheel event
setTimeout(() => this.endInteraction(), 500);
});
}
// Set up keyboard event listeners
setupKeyboardEvents() {
this.canvas.addEventListener("keydown", (e) => {
if (!this.state.hovering) return;
// Start interaction for keyboard events
this.startInteraction();
// Calculate step size based on reference image coordinate system
const coordSys = this.renderer.calculateCoordinateSystem();
const { canvasScale } = coordSys;
const step = (e.shiftKey ? 10 : 1) / canvasScale; // Convert to reference coordinates
const pose = this.state.which;
switch(e.key) {
case 'ArrowLeft':
e.preventDefault();
this.adjustProperty(`tx_${pose}`, -step);
break;
case 'ArrowRight':
e.preventDefault();
this.adjustProperty(`tx_${pose}`, step);
break;
case 'ArrowUp':
e.preventDefault();
this.adjustProperty(`ty_${pose}`, -step);
break;
case 'ArrowDown':
e.preventDefault();
this.adjustProperty(`ty_${pose}`, step);
break;
case 'a':
case 'A':
e.preventDefault();
this.state.which = "A";
console.log("[Interaction] Switched to pose A");
this.renderer.draw();
break;
case 'b':
case 'B':
e.preventDefault();
this.state.which = "B";
console.log("[Interaction] Switched to pose B");
this.renderer.draw();
break;
case 'r':
case 'R':
e.preventDefault();
this.resetCurrentPose();
break;
}
// End interaction after keyboard event
setTimeout(() => this.endInteraction(), 500);
});
}
// Helper method to adjust a property by a delta value
adjustProperty(propertyName, delta) {
const currentValue = this.transformManager.getProperty(propertyName, 0);
const newValue = currentValue + delta;
this.transformManager.setProperty(propertyName, newValue);
console.log(`[Interaction] Adjust ${propertyName}: ${currentValue.toFixed(2)} → ${newValue.toFixed(2)}`);
this.renderer.draw();
}
// Reset the current active pose to default values
resetCurrentPose() {
const pose = this.state.which;
this.transformManager.setProperty(`tx_${pose}`, 0);
this.transformManager.setProperty(`ty_${pose}`, 0);
this.transformManager.setProperty(`scale_${pose}`, 1.0);
this.transformManager.setProperty(`angle_deg_${pose}`, 0);
console.log(`[Interaction] Reset pose ${pose} to defaults`);
this.renderer.draw();
}
}