Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Single Line Diagram Simulator</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/feather-icons"></script> | |
| <style> | |
| .workspace { | |
| background-color: #f8fafc; | |
| background-image: linear-gradient(#e2e8f0 1px, transparent 1px), | |
| linear-gradient(90deg, #e2e8f0 1px, transparent 1px); | |
| background-size: 20px 20px; | |
| } | |
| .component { | |
| cursor: move; | |
| transition: all 0.2s; | |
| } | |
| .component:hover { | |
| filter: drop-shadow(0 0 4px rgba(0, 0, 0, 0.2)); | |
| } | |
| .busbar { | |
| height: 10px; | |
| background-color: #4B5563; | |
| } | |
| .transformer { | |
| width: 60px; | |
| height: 60px; | |
| position: relative; | |
| } | |
| .circuit-breaker { | |
| width: 30px; | |
| height: 30px; | |
| } | |
| .generator { | |
| width: 60px; | |
| height: 30px; | |
| border-radius: 4px; | |
| } | |
| .load { | |
| width: 40px; | |
| height: 40px; | |
| } | |
| .wire { | |
| stroke: #4B5563; | |
| stroke-width: 2; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <header class="mb-8 text-center"> | |
| <h1 class="text-4xl font-bold text-gray-800 mb-2">Single Line Diagram Simulator</h1> | |
| <p class="text-gray-600">Design and simulate electrical power systems</p> | |
| </header> | |
| <div class="grid grid-cols-1 lg:grid-cols-4 gap-6"> | |
| <!-- Toolbox --> | |
| <div class="bg-white rounded-lg shadow-md p-4 lg:col-span-1"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800">Components</h2> | |
| <div class="space-y-3"> | |
| <div class="bg-gray-100 p-3 rounded-lg flex flex-col items-center cursor-move component" draggable="true" data-type="busbar"> | |
| <svg width="60" height="10"> | |
| <rect width="60" height="10" fill="#4B5563" /> | |
| </svg> | |
| <span class="mt-2">Busbar</span> | |
| </div> | |
| <div class="bg-gray-100 p-3 rounded-lg flex flex-col items-center cursor-move component" draggable="true" data-type="transformer"> | |
| <svg width="60" height="60" viewBox="0 0 60 60"> | |
| <rect x="10" y="10" width="40" height="40" fill="#F59E0B" /> | |
| <text x="30" y="35" text-anchor="middle" fill="white">T</text> | |
| </svg> | |
| <span class="mt-2">Transformer</span> | |
| </div> | |
| <div class="bg-gray-100 p-3 rounded-lg flex flex-col items-center cursor-move component" draggable="true" data-type="generator"> | |
| <svg width="60" height="30" viewBox="0 0 60 30"> | |
| <rect width="60" height="30" rx="4" fill="#10B981" /> | |
| <text x="30" y="18" text-anchor="middle" fill="white">G</text> | |
| </svg> | |
| <span class="mt-2">Generator</span> | |
| </div> | |
| <div class="bg-gray-100 p-3 rounded-lg flex flex-col items-center cursor-move component" draggable="true" data-type="circuit-breaker"> | |
| <svg width="30" height="30" viewBox="0 0 30 30"> | |
| <circle cx="15" cy="15" r="12" fill="#EF4444" /> | |
| <line x1="8" y1="8" x2="22" y2="22" stroke="white" stroke-width="2" /> | |
| </svg> | |
| <span class="mt-2">Circuit Breaker</span> | |
| </div> | |
| <div class="bg-gray-100 p-3 rounded-lg flex flex-col items-center cursor-move component" draggable="true" data-type="load"> | |
| <svg width="40" height="40" viewBox="0 0 40 40"> | |
| <circle cx="20" cy="20" r="15" fill="#3B82F6" /> | |
| <text x="20" y="25" text-anchor="middle" fill="white">L</text> | |
| </svg> | |
| <span class="mt-2">Load</span> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Workspace --> | |
| <div class="bg-white rounded-lg shadow-md p-4 lg:col-span-3"> | |
| <div class="flex justify-between items-center mb-4"> | |
| <h2 class="text-xl font-semibold text-gray-800">Diagram Workspace</h2> | |
| <div class="flex space-x-2"> | |
| <button class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-lg flex items-center"> | |
| <i data-feather="play" class="mr-2"></i> | |
| Simulate | |
| </button> | |
| <button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg flex items-center"> | |
| <i data-feather="save" class="mr-2"></i> | |
| Save | |
| </button> | |
| <button class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-lg flex items-center"> | |
| <i data-feather="trash-2" class="mr-2"></i> | |
| Clear | |
| </button> | |
| </div> | |
| </div> | |
| <div class="workspace border-2 border-gray-300 rounded-lg p-6 min-h-[500px]" id="diagram-workspace"> | |
| <svg id="diagram-svg" width="100%" height="100%"></svg> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Properties Panel --> | |
| <div class="mt-8 bg-white rounded-lg shadow-md p-4"> | |
| <h2 class="text-xl font-semibold mb-4 text-gray-800">Component Properties</h2> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label class="block text-gray-700 mb-2">Name</label> | |
| <input type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md" placeholder="Component name"> | |
| </div> | |
| <div> | |
| <label class="block text-gray-700 mb-2">Voltage (kV)</label> | |
| <input type="number" class="w-full px-3 py-2 border border-gray-300 rounded-md" placeholder="11"> | |
| </div> | |
| <div> | |
| <label class="block text-gray-700 mb-2">Power (MVA)</label> | |
| <input type="number" class="w-full px-3 py-2 border border-gray-300 rounded-md" placeholder="50"> | |
| </div> | |
| <div> | |
| <label class="block text-gray-700 mb-2">Status</label> | |
| <select class="w-full px-3 py-2 border border-gray-300 rounded-md"> | |
| <option>Online</option> | |
| <option>Offline</option> | |
| <option>Fault</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| feather.replace(); | |
| const svg = document.getElementById('diagram-svg'); | |
| let selectedElement = null; | |
| // Simple drag and drop | |
| const components = document.querySelectorAll('[draggable="true"]'); | |
| const workspace = document.getElementById('diagram-workspace'); | |
| components.forEach(component => { | |
| component.addEventListener('dragstart', function(e) { | |
| e.dataTransfer.setData('type', this.dataset.type); | |
| }); | |
| }); | |
| workspace.addEventListener('dragover', function(e) { | |
| e.preventDefault(); | |
| }); | |
| workspace.addEventListener('drop', function(e) { | |
| e.preventDefault(); | |
| const type = e.dataTransfer.getData('type'); | |
| const rect = workspace.getBoundingClientRect(); | |
| const x = e.clientX - rect.left - 30; | |
| const y = e.clientY - rect.top - 30; | |
| switch(type) { | |
| case 'busbar': | |
| createBusbar(x, y); | |
| break; | |
| case 'transformer': | |
| createTransformer(x, y); | |
| break; | |
| case 'generator': | |
| createGenerator(x, y); | |
| break; | |
| case 'circuit-breaker': | |
| createCircuitBreaker(x, y); | |
| break; | |
| case 'load': | |
| createLoad(x, y); | |
| break; | |
| } | |
| }); | |
| function createBusbar(x, y) { | |
| const busbar = document.createElementNS("http://www.w3.org/2000/svg", "rect"); | |
| busbar.setAttribute('x', x); | |
| busbar.setAttribute('y', y); | |
| busbar.setAttribute('width', '120'); | |
| busbar.setAttribute('height', '10'); | |
| busbar.setAttribute('fill', '#4B5563'); | |
| busbar.setAttribute('class', 'component'); | |
| busbar.addEventListener('click', selectElement); | |
| svg.appendChild(busbar); | |
| } | |
| function createTransformer(x, y) { | |
| const group = document.createElementNS("http://www.w3.org/2000/svg", "g"); | |
| group.setAttribute('transform', `translate(${x}, ${y})`); | |
| const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); | |
| rect.setAttribute('x', '10'); | |
| rect.setAttribute('y', '10'); | |
| rect.setAttribute('width', '40'); | |
| rect.setAttribute('height', '40'); | |
| rect.setAttribute('fill', '#F59E0B'); | |
| const text = document.createElementNS("http://www.w3.org/2000/svg", "text"); | |
| text.setAttribute('x', '30'); | |
| text.setAttribute('y', '35'); | |
| text.setAttribute('text-anchor', 'middle'); | |
| text.setAttribute('fill', 'white'); | |
| text.textContent = 'T'; | |
| group.appendChild(rect); | |
| group.appendChild(text); | |
| group.setAttribute('class', 'component'); | |
| group.addEventListener('click', selectElement); | |
| svg.appendChild(group); | |
| } | |
| function createGenerator(x, y) { | |
| const group = document.createElementNS("http://www.w3.org/2000/svg", "g"); | |
| group.setAttribute('transform', `translate(${x}, ${y})`); | |
| const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); | |
| rect.setAttribute('width', '60'); | |
| rect.setAttribute('height', '30'); | |
| rect.setAttribute('rx', '4'); | |
| rect.setAttribute('fill', '#10B981'); | |
| const text = document.createElementNS("http://www.w3.org/2000/svg", "text"); | |
| text.setAttribute('x', '30'); | |
| text.setAttribute('y', '18'); | |
| text.setAttribute('text-anchor', 'middle'); | |
| text.setAttribute('fill', 'white'); | |
| text.textContent = 'G'; | |
| group.appendChild(rect); | |
| group.appendChild(text); | |
| group.setAttribute('class', 'component'); | |
| group.addEventListener('click', selectElement); | |
| svg.appendChild(group); | |
| } | |
| function createCircuitBreaker(x, y) { | |
| const group = document.createElementNS("http://www.w3.org/2000/svg", "g"); | |
| group.setAttribute('transform', `translate(${x}, ${y})`); | |
| const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); | |
| circle.setAttribute('cx', '15'); | |
| circle.setAttribute('cy', '15'); | |
| circle.setAttribute('r', '12'); | |
| circle.setAttribute('fill', '#EF4444'); | |
| const line = document.createElementNS("http://www.w3.org/2000/svg", "line"); | |
| line.setAttribute('x1', '8'); | |
| line.setAttribute('y1', '8'); | |
| line.setAttribute('x2', '22'); | |
| line.setAttribute('y2', '22'); | |
| line.setAttribute('stroke', 'white'); | |
| line.setAttribute('stroke-width', '2'); | |
| group.appendChild(circle); | |
| group.appendChild(line); | |
| group.setAttribute('class', 'component'); | |
| group.addEventListener('click', selectElement); | |
| svg.appendChild(group); | |
| } | |
| function createLoad(x, y) { | |
| const group = document.createElementNS("http://www.w3.org/2000/svg", "g"); | |
| group.setAttribute('transform', `translate(${x}, ${y})`); | |
| const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); | |
| circle.setAttribute('cx', '20'); | |
| circle.setAttribute('cy', '20'); | |
| circle.setAttribute('r', '15'); | |
| circle.setAttribute('fill', '#3B82F6'); | |
| const text = document.createElementNS("http://www.w3.org/2000/svg", "text"); | |
| text.setAttribute('x', '20'); | |
| text.setAttribute('y', '25'); | |
| text.setAttribute('text-anchor', 'middle'); | |
| text.setAttribute('fill', 'white'); | |
| text.textContent = 'L'; | |
| group.appendChild(circle); | |
| group.appendChild(text); | |
| group.setAttribute('class', 'component'); | |
| group.addEventListener('click', selectElement); | |
| svg.appendChild(group); | |
| } | |
| function selectElement(e) { | |
| if (selectedElement) { | |
| selectedElement.setAttribute('stroke', 'none'); | |
| } | |
| selectedElement = e.target; | |
| selectedElement.setAttribute('stroke', '#6366F1'); | |
| selectedElement.setAttribute('stroke-width', '2'); | |
| e.stopPropagation(); | |
| } | |
| // Click on empty space to deselect | |
| svg.addEventListener('click', function(e) { | |
| if (e.target === svg && selectedElement) { | |
| selectedElement.setAttribute('stroke', 'none'); | |
| selectedElement = null; | |
| } | |
| }); | |
| }); | |
| </script> | |
| <script>feather.replace();</script> | |
| </body> | |
| </html> |