""" 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