class InteractionManager { constructor(app) { this.app = app; } // Handle mouse down events handleMouseDown(e) { console.log(`Starting interaction with tool: ${this.app.toolManager.getCurrentTool()}`); const rect = this.app.canvas.getBoundingClientRect(); let x = e.clientX - rect.left; let y = e.clientY - rect.top; console.log(`Mouse position: ${x}, ${y}`); // Handle different tool behaviors switch (this.app.toolManager.getCurrentTool()) { case 'pointer': this.handlePointerStart(e, x, y); break; case 'hand': this.handleHandStart(e, x, y); break; case 'move': this.handleMoveStart(e, x, y); break; case 'rotate': this.handleRotateStart(e, x, y); break; case 'room': case 'wall': case 'floor': case 'door': case 'window': this.handleDrawingStart(e, x, y); break; default: this.handleDrawingStart(e, x, y); } } // Handle mouse move events handleMouseMove(e) { const rect = this.app.canvas.getBoundingClientRect(); let currentX = e.clientX - rect.left; let currentY = e.clientY - rect.top; // Handle different tool behaviors during mouse move switch (this.app.toolManager.getCurrentTool()) { case 'hand': this.handleHandMove(e, currentX, currentY); break; case 'move': this.handleMoveMove(e, currentX, currentY); break; case 'rotate': this.handleRotateMove(e, currentX, currentY); break; case 'room': case 'wall': case 'floor': case 'door': case 'window': this.handleDrawingMove(e, currentX, currentY); break; } } // Handle mouse up events handleMouseUp(e) { // Handle different tool behaviors on mouse up switch (this.app.toolManager.getCurrentTool()) { case 'hand': this.handleHandStop(e); break; case 'move': this.handleMoveStop(e); break; case 'rotate': this.handleRotateStop(e); break; case 'room': case 'wall': case 'floor': case 'door': case 'window': this.handleDrawingStop(e); break; } } // Handle pointer tool (selection) handlePointerStart(e, x, y) { const worldCoords = this.app.zoomPanManager.screenToWorld(x, y); this.selectElement({ clientX: e.clientX, clientY: e.clientY }); } // Handle hand tool (panning) handleHandStart(e, x, y) { this.app.toolManager.isPanning = true; this.app.toolManager.lastPanX = x; this.app.toolManager.lastPanY = y; this.app.canvas.style.cursor = 'grabbing'; } handleHandMove(e, currentX, currentY) { if (!this.app.toolManager.isPanning) return; const deltaX = currentX - this.app.toolManager.lastPanX; const deltaY = currentY - this.app.toolManager.lastPanY; this.app.zoomPanManager.pan(deltaX, deltaY); this.app.redraw(); this.app.toolManager.lastPanX = currentX; this.app.toolManager.lastPanY = currentY; } handleHandStop(e) { this.app.toolManager.isPanning = false; this.app.canvas.style.cursor = 'grab'; } // Handle move tool (dragging objects) handleMoveStart(e, x, y) { const worldCoords = this.app.zoomPanManager.screenToWorld(x, y); const selectedElement = this.findElementAt(worldCoords.x, worldCoords.y); if (selectedElement) { this.app.toolManager.isDraggingElement = true; this.app.toolManager.draggedElement = selectedElement; this.app.toolManager.dragStartX = worldCoords.x; this.app.toolManager.dragStartY = worldCoords.y; this.app.uiManager.setSelectedElement(selectedElement); this.app.canvas.style.cursor = 'grabbing'; } } handleMoveMove(e, currentX, currentY) { if (!this.app.toolManager.isDraggingElement || !this.app.toolManager.draggedElement) return; const worldCoords = this.app.zoomPanManager.screenToWorld(currentX, currentY); const deltaX = worldCoords.x - this.app.toolManager.dragStartX; const deltaY = worldCoords.y - this.app.toolManager.dragStartY; // Update element position this.app.toolManager.draggedElement.startX += deltaX; this.app.toolManager.draggedElement.startY += deltaY; this.app.toolManager.draggedElement.endX += deltaX; this.app.toolManager.draggedElement.endY += deltaY; this.app.toolManager.dragStartX = worldCoords.x; this.app.toolManager.dragStartY = worldCoords.y; this.app.redraw(); } handleMoveStop(e) { if (this.app.toolManager.isDraggingElement) { // Save state to history after moving element this.app.saveHistory(); } this.app.toolManager.isDraggingElement = false; this.app.toolManager.draggedElement = null; this.app.canvas.style.cursor = 'move'; } // Handle rotate tool handleRotateStart(e, x, y) { const worldCoords = this.app.zoomPanManager.screenToWorld(x, y); const selectedElement = this.findElementAt(worldCoords.x, worldCoords.y); if (selectedElement) { this.app.toolManager.isRotatingElement = true; this.app.toolManager.rotatedElement = selectedElement; this.app.toolManager.rotateStartAngle = Math.atan2( worldCoords.y - this.getElementCenter(selectedElement).y, worldCoords.x - this.getElementCenter(selectedElement).x ); this.app.uiManager.setSelectedElement(selectedElement); } } handleRotateMove(e, currentX, currentY) { if (!this.app.toolManager.isRotatingElement || !this.app.toolManager.rotatedElement) return; const worldCoords = this.app.zoomPanManager.screenToWorld(currentX, currentY); const center = this.getElementCenter(this.app.toolManager.rotatedElement); const currentAngle = Math.atan2(worldCoords.y - center.y, worldCoords.x - center.x); const deltaAngle = currentAngle - this.app.toolManager.rotateStartAngle; // Apply rotation (simplified) this.app.toolManager.rotatedElement.rotation = (this.app.toolManager.rotatedElement.rotation || 0) + deltaAngle; this.app.toolManager.rotateStartAngle = currentAngle; this.app.redraw(); } handleRotateStop(e) { if (this.app.toolManager.isRotatingElement) { // Save state to history after rotating element this.app.saveHistory(); } this.app.toolManager.isRotatingElement = false; this.app.toolManager.rotatedElement = null; this.app.canvas.style.cursor = 'crosshair'; } // Handle drawing tools handleDrawingStart(e, x, y) { this.app.toolManager.isDrawing = true; // Convert screen coordinates to world coordinates const worldCoords = this.app.zoomPanManager.screenToWorld(x, y); x = worldCoords.x; y = worldCoords.y; // Snap to grid if enabled if (this.app.toolManager.snapToGrid) { x = this.app.canvasManager.snapToGridPoint(x); y = this.app.canvasManager.snapToGridPoint(y); } this.app.toolManager.startX = x; this.app.toolManager.startY = y; // For walls, check for connection to existing walls if (this.app.toolManager.getCurrentTool() === 'wall') { const connection = this.app.wallManager.findWallConnection(x, y, this.app.designElements); if (connection) { this.app.toolManager.startX = connection.x; this.app.toolManager.startY = connection.y; } } } handleDrawingMove(e, currentX, currentY) { if (!this.app.toolManager.isDrawing) return; // Convert screen coordinates to world coordinates const worldCoords = this.app.zoomPanManager.screenToWorld(currentX, currentY); currentX = worldCoords.x; currentY = worldCoords.y; // Snap to grid if enabled if (this.app.toolManager.snapToGrid) { currentX = this.app.canvasManager.snapToGridPoint(currentX); currentY = this.app.canvasManager.snapToGridPoint(currentY); } this.app.redraw(); // Draw preview based on current tool const tool = this.app.toolManager.getCurrentTool(); if (tool === 'room') { this.app.roomManager.drawRoomPreview(this.app.toolManager.startX, this.app.toolManager.startY, currentX, currentY); if (this.app.canvasManager.showMeasurements) { this.app.roomManager.drawRoomArea(this.app.toolManager.startX, this.app.toolManager.startY, currentX, currentY); } } else if (tool === 'floor') { this.app.floorManager.drawFloorPreview(this.app.toolManager.startX, this.app.toolManager.startY, currentX, currentY); if (this.app.canvasManager.showMeasurements) { this.app.floorManager.drawFloorArea(this.app.toolManager.startX, this.app.toolManager.startY, currentX, currentY); } } else if (tool === 'wall') { this.app.wallManager.drawWallPreview(this.app.toolManager.startX, this.app.toolManager.startY, currentX, currentY); if (this.app.canvasManager.showMeasurements) { this.app.wallManager.drawWallMeasurement(this.app.toolManager.startX, this.app.toolManager.startY, currentX, currentY); } // Check for wall connections const connection = this.app.wallManager.findWallConnection(currentX, currentY, this.app.designElements); this.app.wallManager.drawConnectionIndicator(connection); } else if (tool === 'door' || tool === 'window') { // Show symbol preview attached to nearest wall const walls = this.app.designElements.filter(el => el.type === 'wall'); const nearestWall = this.app.symbolManager.findNearestWall(currentX, currentY, walls); if (nearestWall) { const symbolX = nearestWall.wall.startX + (nearestWall.wall.endX - nearestWall.wall.startX) * nearestWall.position; const symbolY = nearestWall.wall.startY + (nearestWall.wall.endY - nearestWall.wall.startY) * nearestWall.position; this.app.canvasManager.drawPreview(symbolX - 20, symbolY - 4, symbolX + 20, symbolY + 4, tool === 'door' ? '#8B4513' : '#0066cc'); } } } handleDrawingStop(e) { if (!this.app.toolManager.isDrawing) return; this.app.toolManager.isDrawing = false; const rect = this.app.canvas.getBoundingClientRect(); let endX = e.clientX - rect.left; let endY = e.clientY - rect.top; // Convert screen coordinates to world coordinates const worldCoords = this.app.zoomPanManager.screenToWorld(endX, endY); endX = worldCoords.x; endY = worldCoords.y; // Snap to grid if enabled if (this.app.toolManager.snapToGrid) { endX = this.app.canvasManager.snapToGridPoint(endX); endY = this.app.canvasManager.snapToGridPoint(endY); } // Create element based on tool this.createElementFromDrawing(endX, endY); } // Create element from drawing interaction createElementFromDrawing(endX, endY) { const tool = this.app.toolManager.getCurrentTool(); let element; // Get material and color const material = this.getMaterialValue() || 'wood'; const color = this.getColorValue() || '#ffffff'; if (tool === 'door' || tool === 'window') { element = this.createSymbolElement(tool, endX, endY, material, color); } else { element = this.createDrawingElement(tool, endX, endY, material, color); } if (element) { this.app.designElements.push(element); this.app.redraw(); // Save state to history after creating element this.app.saveHistory(); } } // Helper methods findElementAt(x, y) { for (let i = this.app.designElements.length - 1; i >= 0; i--) { const element = this.app.designElements[i]; if (this.app.isPointInElement(x, y, element)) { return element; } } return null; } getElementCenter(element) { return { x: (element.startX + element.endX) / 2, y: (element.startY + element.endY) / 2 }; } getMaterialValue() { const materialSelectTop = document.getElementById('materialSelectTop'); const materialSelectSidebar = document.getElementById('materialSelect'); return (materialSelectTop && materialSelectTop.value) || (materialSelectSidebar && materialSelectSidebar.value); } getColorValue() { const colorPickerTop = document.getElementById('colorPickerTop'); const colorPickerSidebar = document.getElementById('colorPicker'); return (colorPickerTop && colorPickerTop.value) || (colorPickerSidebar && colorPickerSidebar.value); } createSymbolElement(tool, endX, endY, material, color) { const walls = this.app.designElements.filter(el => el.type === 'wall'); const nearestWall = this.app.symbolManager.findNearestWall(endX, endY, walls); if (nearestWall && nearestWall.distance < 30) { const symbolX = nearestWall.wall.startX + (nearestWall.wall.endX - nearestWall.wall.startX) * nearestWall.position; const symbolY = nearestWall.wall.startY + (nearestWall.wall.endY - nearestWall.wall.startY) * nearestWall.position; const options = { color: color, material: material, attachedToWall: nearestWall.wall.id, wallPosition: nearestWall.position }; // Add type-specific options if (tool === 'door') { options.doorType = this.getDoorTypeValue() || 'single'; options.swingDirection = this.getDoorSwingValue() || 'right'; } else if (tool === 'window') { options.windowType = this.getWindowTypeValue() || 'casement'; } return this.app.symbolManager.createSymbolElement( tool, symbolX - 20, symbolY - 4, symbolX + 20, symbolY + 4, options ); } else { this.app.uiManager.showAlert(`${tool} must be placed near a wall!`); return null; } } createDrawingElement(tool, endX, endY, material, color) { const startX = this.app.toolManager.startX; const startY = this.app.toolManager.startY; // Check for wall connections at end point if (tool === 'wall') { const connection = this.app.wallManager.findWallConnection(endX, endY, this.app.designElements); if (connection) { endX = connection.x; endY = connection.y; } } // Only create element if there's actual movement (for drag tools) const distance = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2)); if (distance < 5 && (tool === 'room' || tool === 'wall' || tool === 'floor')) { return null; // Minimum distance threshold for drag tools } if (tool === 'room') { return this.app.roomManager.createRoomElement(startX, startY, endX, endY, this.app.designElements, material, color); } else if (tool === 'wall') { return this.app.wallManager.createWallElement(startX, startY, endX, endY, material, color); } else if (tool === 'floor') { return this.app.floorManager.createFloorElement(startX, startY, endX, endY, material, color); } return null; } getDoorTypeValue() { const doorTypeTop = document.getElementById('doorTypeSelectTop'); const doorTypeSidebar = document.getElementById('doorTypeSelect'); return (doorTypeTop && doorTypeTop.value) || (doorTypeSidebar && doorTypeSidebar.value); } getDoorSwingValue() { const doorSwingTop = document.getElementById('doorSwingSelectTop'); const doorSwingSidebar = document.getElementById('doorSwingSelect'); return (doorSwingTop && doorSwingTop.value) || (doorSwingSidebar && doorSwingSidebar.value); } getWindowTypeValue() { const windowTypeTop = document.getElementById('windowTypeSelectTop'); const windowTypeSidebar = document.getElementById('windowTypeSelect'); return (windowTypeTop && windowTypeTop.value) || (windowTypeSidebar && windowTypeSidebar.value); } // Handle element selection selectElement(e) { const rect = this.app.canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; // Convert screen coordinates to world coordinates const worldCoords = this.app.zoomPanManager.screenToWorld(x, y); const worldX = worldCoords.x; const worldY = worldCoords.y; // Find element at click position for (let i = this.app.designElements.length - 1; i >= 0; i--) { const element = this.app.designElements[i]; if (this.app.isPointInElement(worldX, worldY, element)) { this.app.uiManager.setSelectedElement(element); this.app.redraw(); return; } } this.app.uiManager.clearSelection(); this.app.redraw(); } }