Files
plantplan/server/main.py
2026-04-16 16:31:23 +02:00

135 lines
3.4 KiB
Python

"""
PlantPlan FastAPI Backend
- Symbolbibliothek ausliefern
- Koordinaten normalisieren
- JSON/SVG Export
- Shape-Änderungen empfangen (Puffer + Kreisel)
"""
import json
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
app = FastAPI(title="PlantPlan API")
# CORS: Erlaubt Zugriff vom lokalen Frontend Dev-Server
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"],
allow_methods=["*"],
allow_headers=["*"],
)
CATALOG_DIR = Path(__file__).parent / "catalog"
# ---------------------------------------------------------------------------
# Pydantic Models
# ---------------------------------------------------------------------------
class SketchPayload(BaseModel):
shapes: list[dict]
grid_size: int = 10
class PufferUpdate(BaseModel):
"""Puffer-Symbol: Rechteck mit Breite und Höhe."""
id: str
x: float
y: float
w: float # Breite
h: float # Höhe
class KreiselUpdate(BaseModel):
"""Kreisel-Symbol: Zwei Kreise mit Abstand und Radius."""
id: str
x: float
y: float
abstand: float # Abstand zwischen den Kreismittelpunkten
radius: float # Radius beider Kreise
class ShapeUpdatePayload(BaseModel):
"""Payload für Shape-Änderungen vom Client."""
puffer: list[PufferUpdate] = []
kreisel: list[KreiselUpdate] = []
# ---------------------------------------------------------------------------
# Endpoints
# ---------------------------------------------------------------------------
@app.get("/symbols")
async def get_symbols():
"""Symbolbibliothek als JSON ausliefern."""
catalog_file = CATALOG_DIR / "symbols.json"
with open(catalog_file, encoding="utf-8") as f:
return json.load(f)
@app.post("/normalize")
async def normalize(payload: SketchPayload):
"""Koordinaten auf Grid snappen und SVG-Pfade normalisieren."""
cleaned = []
for shape in payload.shapes:
s = dict(shape)
if "x" in s:
s["x"] = round(s["x"] / payload.grid_size) * payload.grid_size
if "y" in s:
s["y"] = round(s["y"] / payload.grid_size) * payload.grid_size
cleaned.append(s)
return {"shapes": cleaned}
@app.post("/export")
async def export_sketch(payload: SketchPayload):
"""Finales Projektfile als JSON exportieren."""
return {
"json": {
"version": "1.0",
"shapes": payload.shapes,
}
}
@app.post("/shapes/update")
async def update_shapes(payload: ShapeUpdatePayload):
"""
Shape-Änderungen vom Client empfangen.
Gibt für jeden Puffer Breite/Höhe und für jeden Kreisel
Abstand/Radius + berechnete Gesamtlänge zurück.
"""
result = {
"received": {
"puffer_count": len(payload.puffer),
"kreisel_count": len(payload.kreisel),
},
"puffer": [],
"kreisel": [],
}
for p in payload.puffer:
result["puffer"].append({
"id": p.id,
"x": p.x,
"y": p.y,
"breite": p.w,
"hoehe": p.h,
})
for k in payload.kreisel:
result["kreisel"].append({
"id": k.id,
"x": k.x,
"y": k.y,
"abstand": k.abstand,
"radius": k.radius,
"gesamtlaenge": 2 * k.radius + k.abstand,
})
return result