Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- Dockerfile +18 -0
- app.py +90 -0
- index.html +85 -0
- requirements.txt +4 -0
Dockerfile
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
# Dockerfile
|
| 3 |
+
FROM python:3.11
|
| 4 |
+
|
| 5 |
+
# Install backend dependencies
|
| 6 |
+
COPY requirements.txt /app/requirements.txt
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
RUN pip install -r requirements.txt
|
| 9 |
+
|
| 10 |
+
# Copy backend and frontend
|
| 11 |
+
COPY app.py /app/app.py
|
| 12 |
+
COPY index.html /app/index.html
|
| 13 |
+
|
| 14 |
+
# Add CORS and serve index.html statically
|
| 15 |
+
RUN mkdir -p /app/static
|
| 16 |
+
|
| 17 |
+
# Run both backend (FastAPI) and static file server
|
| 18 |
+
CMD uvicorn app:app --host 0.0.0.0 --port 7860
|
app.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from fastapi import FastAPI, UploadFile, File
|
| 3 |
+
from fastapi.responses import JSONResponse, FileResponse
|
| 4 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 5 |
+
import os
|
| 6 |
+
|
| 7 |
+
app = FastAPI()
|
| 8 |
+
|
| 9 |
+
app.add_middleware(
|
| 10 |
+
CORSMiddleware,
|
| 11 |
+
allow_origins=["*"], allow_credentials=True,
|
| 12 |
+
allow_methods=["*"], allow_headers=["*"],
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
@app.get("/")
|
| 16 |
+
def serve_frontend():
|
| 17 |
+
return FileResponse("index.html")
|
| 18 |
+
|
| 19 |
+
@app.post("/parse")
|
| 20 |
+
async def parse_file(file: UploadFile = File(...)):
|
| 21 |
+
content = await file.read()
|
| 22 |
+
return JSONResponse(content=parse_dcm_assembly(content))
|
| 23 |
+
|
| 24 |
+
def parse_dcm_assembly(file_bytes):
|
| 25 |
+
lines = file_bytes.decode("utf-8").splitlines()
|
| 26 |
+
blocks, current = [], []
|
| 27 |
+
|
| 28 |
+
for line in lines:
|
| 29 |
+
if line.startswith("CLASS"):
|
| 30 |
+
if current:
|
| 31 |
+
blocks.append(current)
|
| 32 |
+
current = []
|
| 33 |
+
current.append(line.strip())
|
| 34 |
+
if current: blocks.append(current)
|
| 35 |
+
|
| 36 |
+
parts, geometry, constraints = [], {}, []
|
| 37 |
+
plane_map, tag_to_plane, label_map = {}, {}, {}
|
| 38 |
+
|
| 39 |
+
for block in blocks:
|
| 40 |
+
head = block[0]
|
| 41 |
+
if "CLASS SET " in head:
|
| 42 |
+
part, members = {}, []
|
| 43 |
+
for line in block:
|
| 44 |
+
if "DCM3_TEXT_LABEL" in line:
|
| 45 |
+
part['name'] = line.split("DCM3_TEXT_LABEL", 1)[1].strip()
|
| 46 |
+
elif "FMEMBER TAGS" in line:
|
| 47 |
+
members = list(map(int, line.split()[2:]))
|
| 48 |
+
if 'name' in part:
|
| 49 |
+
part['member_tags'] = members
|
| 50 |
+
parts.append(part)
|
| 51 |
+
|
| 52 |
+
elif "CLASS PLANE " in head:
|
| 53 |
+
pid = int(head.split()[-1])
|
| 54 |
+
base, norm = None, None
|
| 55 |
+
for line in block:
|
| 56 |
+
if "FBASE_POINT" in line:
|
| 57 |
+
base = tuple(map(float, line.split()[1:]))
|
| 58 |
+
elif "FNORMAL" in line:
|
| 59 |
+
norm = tuple(map(float, line.split()[1:]))
|
| 60 |
+
if base and norm:
|
| 61 |
+
plane_map[pid] = {'base_point': base, 'normal': norm}
|
| 62 |
+
|
| 63 |
+
elif "CLASS WORK_PLANE " in head:
|
| 64 |
+
wid = int(head.split()[-1])
|
| 65 |
+
tag, label = None, None
|
| 66 |
+
for line in block:
|
| 67 |
+
if "FMY_PLANE TAG" in line:
|
| 68 |
+
tag = int(line.split()[-1])
|
| 69 |
+
elif "DCM3_TEXT_LABEL" in line:
|
| 70 |
+
label = line.split("DCM3_TEXT_LABEL", 1)[1].strip()
|
| 71 |
+
if tag:
|
| 72 |
+
tag_to_plane[wid] = tag
|
| 73 |
+
label_map[wid] = label
|
| 74 |
+
|
| 75 |
+
elif "CLASS CONSTRAINT " in head:
|
| 76 |
+
con = {}
|
| 77 |
+
for line in block:
|
| 78 |
+
if "FEND_1 TAG" in line:
|
| 79 |
+
con['from'] = int(line.split()[-1])
|
| 80 |
+
elif "FEND_2 TAG" in line:
|
| 81 |
+
con['to'] = int(line.split()[-1])
|
| 82 |
+
elif "DCM3_TEXT_LABEL" in line:
|
| 83 |
+
con['label'] = line.split("DCM3_TEXT_LABEL", 1)[1].strip()
|
| 84 |
+
if con: constraints.append(con)
|
| 85 |
+
|
| 86 |
+
for wid, pid in tag_to_plane.items():
|
| 87 |
+
if pid in plane_map:
|
| 88 |
+
geometry[wid] = {**plane_map[pid], 'label': label_map.get(wid)}
|
| 89 |
+
|
| 90 |
+
return { "parts": parts, "geometry": geometry, "constraints": constraints }
|
index.html
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
<!DOCTYPE html>
|
| 4 |
+
<html lang="en">
|
| 5 |
+
<head>
|
| 6 |
+
<meta charset="UTF-8" />
|
| 7 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
| 8 |
+
<title>DCM Assembly Visualizer</title>
|
| 9 |
+
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
| 10 |
+
<style>
|
| 11 |
+
body { font-family: sans-serif; padding: 20px; }
|
| 12 |
+
#plot { width: 100%; height: 700px; }
|
| 13 |
+
</style>
|
| 14 |
+
</head>
|
| 15 |
+
<body>
|
| 16 |
+
<h1>DCM Assembly Visualizer</h1>
|
| 17 |
+
<p>This visualizer is now intended to be used with a Streamlit frontend for file upload.</p>
|
| 18 |
+
<div id="plot"></div>
|
| 19 |
+
|
| 20 |
+
<script>
|
| 21 |
+
let allTraces = [];
|
| 22 |
+
let normalTraces = [];
|
| 23 |
+
|
| 24 |
+
async function drawFromServer() {
|
| 25 |
+
const res = await fetch("/parsed_data.json");
|
| 26 |
+
const payload = await res.json();
|
| 27 |
+
allTraces = [];
|
| 28 |
+
normalTraces = [];
|
| 29 |
+
|
| 30 |
+
for (const [color, label, data] of payload.files) {
|
| 31 |
+
extractTraces(data, color, label);
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
Plotly.newPlot('plot', [...allTraces, ...normalTraces.filter(t => t.visible)], {
|
| 35 |
+
margin: { t: 30 },
|
| 36 |
+
scene: { aspectmode: 'data' },
|
| 37 |
+
title: '3D Assembly Viewer'
|
| 38 |
+
});
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
function extractTraces(data, baseColor, normalColor) {
|
| 42 |
+
for (const [tag, g] of Object.entries(data.geometry)) {
|
| 43 |
+
const [x, y, z] = g.base_point;
|
| 44 |
+
const [nx, ny, nz] = g.normal;
|
| 45 |
+
|
| 46 |
+
allTraces.push({
|
| 47 |
+
type: 'scatter3d',
|
| 48 |
+
mode: 'markers+text',
|
| 49 |
+
x: [x], y: [y], z: [z],
|
| 50 |
+
marker: { color: baseColor, size: 5 },
|
| 51 |
+
text: [g.label || tag],
|
| 52 |
+
textposition: 'top center'
|
| 53 |
+
});
|
| 54 |
+
|
| 55 |
+
normalTraces.push({
|
| 56 |
+
type: 'scatter3d',
|
| 57 |
+
mode: 'lines',
|
| 58 |
+
x: [x, x + nx],
|
| 59 |
+
y: [y, y + ny],
|
| 60 |
+
z: [z, z + nz],
|
| 61 |
+
line: { color: normalColor, width: 2 },
|
| 62 |
+
visible: true
|
| 63 |
+
});
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
for (const c of data.constraints) {
|
| 67 |
+
const g1 = data.geometry[c.from];
|
| 68 |
+
const g2 = data.geometry[c.to];
|
| 69 |
+
if (g1 && g2) {
|
| 70 |
+
const [x1, y1, z1] = g1.base_point;
|
| 71 |
+
const [x2, y2, z2] = g2.base_point;
|
| 72 |
+
allTraces.push({
|
| 73 |
+
type: 'scatter3d',
|
| 74 |
+
mode: 'lines',
|
| 75 |
+
x: [x1, x2], y: [y1, y2], z: [z1, z2],
|
| 76 |
+
line: { color: 'green', dash: 'dot', width: 2 }
|
| 77 |
+
});
|
| 78 |
+
}
|
| 79 |
+
}
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
drawFromServer();
|
| 83 |
+
</script>
|
| 84 |
+
</body>
|
| 85 |
+
</html>
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
fastapi
|
| 3 |
+
uvicorn
|
| 4 |
+
python-multipart
|