Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Path Planner - RoboRover Commander</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700;900&family=Roboto:wght@300;400;500;700&display=swap'); | |
| body { | |
| font-family: 'Roboto', sans-serif; | |
| } | |
| .title-font { | |
| font-family: 'Orbitron', sans-serif; | |
| } | |
| .control-btn { | |
| transition: all 0.3s ease; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| } | |
| .control-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); | |
| } | |
| #drawing-canvas { | |
| border: 2px solid #4B5563; | |
| cursor: crosshair; | |
| background-color: #1F2937; | |
| } | |
| .path-point { | |
| position: absolute; | |
| width: 8px; | |
| height: 8px; | |
| background: #3B82F6; | |
| border-radius: 50%; | |
| transform: translate(-50%, -50%); | |
| pointer-events: none; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gradient-to-br from-gray-900 to-gray-800 min-h-screen text-white"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- Header --> | |
| <header class="text-center mb-8"> | |
| <h1 class="title-font text-4xl md:text-5xl font-bold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-500"> | |
| Path Planner | |
| </h1> | |
| <p class="text-lg text-gray-300 max-w-2xl mx-auto"> | |
| Draw your glue application path and generate the corresponding control program | |
| </p> | |
| </header> | |
| <!-- Main Content --> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
| <!-- Left Panel - Controls --> | |
| <div class="lg:col-span-1 space-y-6"> | |
| <!-- Drawing Tools --> | |
| <div class="bg-gray-800 bg-opacity-70 backdrop-blur-lg rounded-2xl p-6 border border-gray-700"> | |
| <h2 class="title-font text-xl mb-4 text-blue-400">Drawing Tools</h2> | |
| <div class="grid grid-cols-2 gap-3 mb-4"> | |
| <button id="draw-mode" class="control-btn bg-blue-600 hover:bg-blue-700 text-white rounded-lg py-3 flex items-center justify-center"> | |
| <i data-feather="edit-3" class="w-4 h-4 mr-2"></i> | |
| <span>Draw</span> | |
| </button> | |
| <button id="erase-mode" class="control-btn bg-gray-600 hover:bg-gray-700 text-white rounded-lg py-3 flex items-center justify-center"> | |
| <i data-feather="trash-2" class="w-4 h-4 mr-2"></i> | |
| <span>Erase</span> | |
| </button> | |
| </div> | |
| <!-- Brush Settings --> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-gray-300 mb-2">Brush Size</label> | |
| <input id="brush-size" type="range" min="1" max="10" value="3" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"> | |
| </div> | |
| <div> | |
| <label class="block text-gray-300 mb-2">Glue Flow Rate</label> | |
| <input id="flow-rate" type="range" min="1" max="100" value="50" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Action Buttons --> | |
| <div class="bg-gray-800 bg-opacity-70 backdrop-blur-lg rounded-2xl p-6 border border-gray-700"> | |
| <h2 class="title-font text-xl mb-4 text-green-400">Actions</h2> | |
| <div class="space-y-3"> | |
| <button id="clear-canvas" class="control-btn bg-red-600 hover:bg-red-700 text-white rounded-lg py-3 w-full flex items-center justify-center"> | |
| <i data-feather="x" class="w-4 h-4 mr-2"></i> | |
| <span>Clear Canvas</span> | |
| </button> | |
| <button id="generate-code" class="control-btn bg-green-600 hover:bg-green-700 text-white rounded-lg py-3 w-full flex items-center justify-center"> | |
| <i data-feather="code" class="w-4 h-4 mr-2"></i> | |
| <span>Generate Program</span> | |
| </button> | |
| <button id="save-path" class="control-btn bg-purple-600 hover:bg-purple-700 text-white rounded-lg py-3 w-full flex items-center justify-center"> | |
| <i data-feather="save" class="w-4 h-4 mr-2"></i> | |
| <span>Save Path</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Status --> | |
| <div class="bg-gray-800 bg-opacity-70 backdrop-blur-lg rounded-2xl p-6 border border-gray-700"> | |
| <h2 class="title-font text-xl mb-4 text-yellow-400">Status</h2> | |
| <div class="space-y-2 text-sm"> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-300">Points:</span> | |
| <span id="point-count" class="text-blue-400">0</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-300">Path Length:</span> | |
| <span id="path-length" class="text-green-400">0 px</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-300">Mode:</span> | |
| <span id="current-mode" class="text-purple-400">Drawing</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Right Panel - Canvas and Code --> | |
| <div class="lg:col-span-2 space-y-6"> | |
| <!-- Drawing Canvas --> | |
| <div class="bg-gray-800 bg-opacity-70 backdrop-blur-lg rounded-2xl p-6 border border-gray-700"> | |
| <h2 class="title-font text-xl mb-4 text-white">Drawing Area</h2> | |
| <div class="relative"> | |
| <canvas id="drawing-canvas" width="800" height="500"></canvas> | |
| </div> | |
| </div> | |
| <!-- Generated Code --> | |
| <div class="bg-gray-800 bg-opacity-70 backdrop-blur-lg rounded-2xl p-6 border border-gray-700"> | |
| <h2 class="title-font text-xl mb-4 text-white">Generated Program</h2> | |
| <div class="bg-gray-900 rounded-lg p-4"> | |
| <pre id="generated-code" class="text-green-400 text-sm overflow-auto max-h-64"> | |
| // Your generated program will appear here | |
| </pre> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Navigation --> | |
| <div class="mt-8 text-center"> | |
| <a href="index.html" class="inline-flex items-center text-blue-400 hover:text-blue-300"> | |
| <i data-feather="arrow-left" class="w-4 h-4 mr-2"></i> | |
| Back to Main Control | |
| </a> | |
| </div> | |
| </div> | |
| <script> | |
| // Canvas setup | |
| const canvas = document.getElementById('drawing-canvas'); | |
| const ctx = canvas.getContext('2d'); | |
| let isDrawing = false; | |
| let isErasing = false; | |
| let lastX = 0; | |
| let lastY = 0; | |
| let pathPoints = []; | |
| let currentMode = 'draw'; | |
| // Initialize canvas | |
| function initCanvas() { | |
| ctx.strokeStyle = '#3B82F6'; | |
| ctx.lineWidth = 3; | |
| ctx.lineCap = 'round'; | |
| ctx.lineJoin = 'round'; | |
| ctx.fillStyle = '#1F2937'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| } | |
| // Drawing functions | |
| function startDrawing(e) { | |
| isDrawing = true; | |
| const rect = canvas.getBoundingClientRect(); | |
| lastX = e.clientX - rect.left; | |
| lastY = e.clientY - rect.top; | |
| if (currentMode === 'draw') { | |
| pathPoints.push({x: lastX, y: lastY}); | |
| updateStatus(); | |
| } | |
| draw(e); | |
| } | |
| function draw(e) { | |
| if (!isDrawing) return; | |
| const rect = canvas.getBoundingClientRect(); | |
| const currentX = e.clientX - rect.left; | |
| const currentY = e.clientY - rect.top; | |
| if (currentMode === 'draw') { | |
| ctx.strokeStyle = '#3B82F6'; | |
| ctx.globalCompositeOperation = 'source-over'; | |
| ctx.beginPath(); | |
| ctx.moveTo(lastX, lastY); | |
| ctx.lineTo(currentX, currentY); | |
| ctx.stroke(); | |
| pathPoints.push({x: currentX, y: currentY}); | |
| } else if (currentMode === 'erase') { | |
| ctx.strokeStyle = '#1F2937'; | |
| ctx.globalCompositeOperation = 'destination-out'; | |
| ctx.lineWidth = 20; | |
| ctx.beginPath(); | |
| ctx.moveTo(lastX, lastY); | |
| ctx.lineTo(currentX, currentY); | |
| ctx.stroke(); | |
| } | |
| lastX = currentX; | |
| lastY = currentY; | |
| updateStatus(); | |
| } | |
| function stopDrawing() { | |
| isDrawing = false; | |
| ctx.globalCompositeOperation = 'source-over'; | |
| ctx.lineWidth = document.getElementById('brush-size').value; | |
| } | |
| // Event listeners for canvas | |
| canvas.addEventListener('mousedown', startDrawing); | |
| canvas.addEventListener('mousemove', draw); | |
| canvas.addEventListener('mouseup', stopDrawing); | |
| canvas.addEventListener('mouseout', stopDrawing); | |
| // Mode switching | |
| document.getElementById('draw-mode').addEventListener('click', () => { | |
| currentMode = 'draw'; | |
| updateModeDisplay(); | |
| }); | |
| document.getElementById('erase-mode').addEventListener('click', () => { | |
| currentMode = 'erase'; | |
| updateModeDisplay(); | |
| }); | |
| // Clear canvas | |
| document.getElementById('clear-canvas').addEventListener('click', () => { | |
| initCanvas(); | |
| pathPoints = []; | |
| updateStatus(); | |
| }); | |
| // Update status display | |
| function updateStatus() { | |
| document.getElementById('point-count').textContent = pathPoints.length; | |
| // Calculate approximate path length | |
| let length = 0; | |
| for (let i = 1; i < pathPoints.length; i++) { | |
| const dx = pathPoints[i].x - pathPoints[i-1].x; | |
| const dy = pathPoints[i].y - pathPoints[i-1].y; | |
| length += Math.sqrt(dx*dx + dy*dy); | |
| } | |
| document.getElementById('path-length').textContent = Math.round(length) + ' px'; | |
| } | |
| function updateModeDisplay() { | |
| document.getElementById('current-mode').textContent = currentMode === 'draw' ? 'Drawing' : 'Erasing'; | |
| } | |
| // Generate program code | |
| document.getElementById('generate-code').addEventListener('click', () => { | |
| if (pathPoints.length === 0) { | |
| document.getElementById('generated-code').textContent = "// No path drawn yet. Please draw a path first."; | |
| return; | |
| } | |
| const flowRate = document.getElementById('flow-rate').value; | |
| let program = `// Glue Application Program | |
| // Generated from ${pathPoints.length} path points | |
| // Flow rate: ${flowRate}% | |
| // Initialize system | |
| INIT_GLUE_SYSTEM(); | |
| SET_FLOW_RATE(${flowRate}); | |
| // Move commands\n`; | |
| // Generate G-code like commands | |
| pathPoints.forEach((point, index) => { | |
| const x = (point.x / canvas.width * 100).toFixed(2); | |
| const y = (point.y / canvas.height * 100).toFixed(2); | |
| if (index === 0) { | |
| program += `MOVE_TO(${x}, ${y}); // Start position\n`; | |
| } else { | |
| program += `MOVE_TO(${x}, ${y});\n`; | |
| } | |
| }); | |
| program += `\n// End program | |
| STOP_GLUE(); | |
| HOME_POSITION();`; | |
| document.getElementById('generated-code').textContent = program; | |
| }); | |
| // Save path (placeholder) | |
| document.getElementById('save-path').addEventListener('click', () => { | |
| if (pathPoints.length === 0) { | |
| alert("No path to save. Please draw a path first."); | |
| return; | |
| } | |
| alert("Path saved successfully!"); | |
| }); | |
| // Brush size control | |
| document.getElementById('brush-size').addEventListener('input', (e) => { | |
| ctx.lineWidth = e.target.value; | |
| }); | |
| // Initialize | |
| initCanvas(); | |
| feather.replace(); | |
| updateModeDisplay(); | |
| </script> | |
| </body> | |
| </html> |