Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
| <title>DCM Assembly Visualizer</title> | |
| <script src="https://cdn.plot.ly/plotly-latest.min.js"></script> | |
| <style> | |
| body { font-family: sans-serif; padding: 20px; margin: 0; box-sizing: border-box; } | |
| #plot-wrapper { | |
| width: 100%; | |
| height: calc(100vh - 200px); | |
| border: 2px dashed #999; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background-color: #fafafa; | |
| } | |
| #plot { width: 100%; height: 100%; } | |
| .controls { width: 200px; margin-right: 20px; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>DCM Assembly Visualizer</h1> | |
| <input type="file" id="file1" accept=".txt"> File A<br> | |
| <input type="file" id="file2" accept=".txt"> File B<br> | |
| <button onclick="uploadFiles()">Visualize</button> | |
| <div style="display: flex; margin-top: 20px;"> | |
| <div class="controls"> | |
| <h3>Controls</h3> | |
| <div id="partControls"></div> | |
| <label><input type="checkbox" id="toggleConstraints" checked onchange="toggleConstraints()"> Show Constraints</label> | |
| </div> | |
| <div style="flex: 1;"> | |
| <div id="plot-wrapper"> | |
| <div id="plot"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| let allTraces = []; | |
| let constraintTraces = []; | |
| let partMap = {}; | |
| async function uploadFiles() { | |
| const file1 = document.getElementById("file1").files[0]; | |
| const file2 = document.getElementById("file2").files[0]; | |
| if (!file1 && !file2) return alert("Please select at least one file."); | |
| allTraces = []; | |
| constraintTraces = []; | |
| partMap = []; | |
| const payload = []; | |
| if (file1) { | |
| const data = await sendFile(file1); | |
| payload.push(["red", "blue", data]); | |
| } | |
| if (file2) { | |
| const data = await sendFile(file2); | |
| payload.push(["orange", "purple", data]); | |
| } | |
| for (const [color, label, data] of payload) { | |
| extractTraces(data, color, label); | |
| } | |
| renderPartControls(); | |
| Plotly.newPlot('plot', [...allTraces, ...constraintTraces], { | |
| margin: { t: 30 }, | |
| scene: { aspectmode: 'data' }, | |
| title: '3D Assembly Viewer' | |
| }); | |
| } | |
| async function sendFile(file) { | |
| const formData = new FormData(); | |
| formData.append("file", file); | |
| const res = await fetch("/parse", { | |
| method: "POST", | |
| body: formData | |
| }); | |
| return await res.json(); | |
| } | |
| function extractTraces(data, baseColor, normalColor) { | |
| for (const [tag, g] of Object.entries(data.geometry)) { | |
| const [x, y, z] = g.base_point; | |
| const [nx, ny, nz] = g.normal; | |
| const label = g.label || tag; | |
| const part = label.split('@').pop()?.split('-')[0] || "unknown"; | |
| const baseTrace = { | |
| type: 'scatter3d', mode: 'markers+text', | |
| x: [x], y: [y], z: [z], | |
| marker: { color: baseColor, size: 5 }, | |
| text: [label], textposition: 'top center', | |
| name: `Base: ${label}` | |
| }; | |
| const normalTrace = { | |
| type: 'scatter3d', mode: 'lines', | |
| x: [x, x + nx], y: [y, y + ny], z: [z, z + nz], | |
| line: { color: normalColor, width: 2 }, | |
| name: `Normal: ${label}` | |
| }; | |
| const baseIndex = allTraces.length; | |
| allTraces.push(baseTrace); | |
| allTraces.push(normalTrace); | |
| if (!partMap[part]) partMap[part] = []; | |
| partMap[part].push(baseIndex, baseIndex + 1); | |
| } | |
| for (const c of data.constraints) { | |
| const g1 = data.geometry[c.from]; | |
| const g2 = data.geometry[c.to]; | |
| if (g1 && g2) { | |
| const [x1, y1, z1] = g1.base_point; | |
| const [x2, y2, z2] = g2.base_point; | |
| constraintTraces.push({ | |
| type: 'scatter3d', mode: 'lines', | |
| x: [x1, x2], y: [y1, y2], z: [z1, z2], | |
| line: { color: 'green', dash: 'dot', width: 2 }, | |
| name: `Constraint: ${c.label || ''}` | |
| }); | |
| } | |
| } | |
| } | |
| function renderPartControls() { | |
| const container = document.getElementById("partControls"); | |
| container.innerHTML = ''; | |
| for (const part in partMap) { | |
| const id = `part_${part}`; | |
| container.innerHTML += `<label><input type='checkbox' id='${id}' checked onchange='togglePart("${part}")'> ${part}</label><br>`; | |
| } | |
| } | |
| function togglePart(part) { | |
| const indices = partMap[part]; | |
| const visible = document.getElementById(`part_${part}`).checked; | |
| for (const i of indices) { | |
| Plotly.restyle('plot', { visible: visible }, [i]); | |
| } | |
| } | |
| function toggleConstraints() { | |
| const show = document.getElementById("toggleConstraints").checked; | |
| const start = allTraces.length; | |
| const end = start + constraintTraces.length; | |
| for (let i = start; i < end; i++) { | |
| Plotly.restyle('plot', { visible: show }, [i]); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |