erste Fassung von Client und Server rein
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -87,3 +87,7 @@ ipython_config.py
|
||||
/work
|
||||
/log
|
||||
/results
|
||||
|
||||
# Node.js (client)
|
||||
node_modules/
|
||||
client/dist/
|
||||
|
||||
74
bin/client.bat
Normal file
74
bin/client.bat
Normal file
@@ -0,0 +1,74 @@
|
||||
@echo off
|
||||
|
||||
REM ================================================================
|
||||
REM PLANTPLAN - Client (Vite Dev-Server) verwalten
|
||||
REM Verwendung: client.bat [start|stop|reload|status]
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
if "%1"=="" goto usage
|
||||
|
||||
if /i "%1"=="start" goto start
|
||||
if /i "%1"=="stop" goto stop
|
||||
if /i "%1"=="reload" goto reload
|
||||
if /i "%1"=="status" goto status
|
||||
goto usage
|
||||
|
||||
:start
|
||||
echo Starting PlantPlan Client (Vite) ...
|
||||
|
||||
REM Pruefen ob Client bereits laeuft
|
||||
tasklist /FI "WINDOWTITLE eq PlantPlan-Client" 2>nul | find /i "cmd.exe" >nul
|
||||
if not errorlevel 1 (
|
||||
echo Client laeuft bereits!
|
||||
goto end
|
||||
)
|
||||
|
||||
REM Pruefen ob node_modules existiert
|
||||
if not exist "%PV_CLIENT%\node_modules" (
|
||||
echo FEHLER: node_modules nicht gefunden! Bitte zuerst install_npm.bat ausfuehren.
|
||||
goto end
|
||||
)
|
||||
|
||||
start "PlantPlan-Client" cmd /k "cd /d "%PV_CLIENT%" && npm run dev"
|
||||
echo Client gestartet auf %PV_CLIENT_URL%
|
||||
goto end
|
||||
|
||||
:stop
|
||||
echo Stopping PlantPlan Client ...
|
||||
taskkill /FI "WINDOWTITLE eq PlantPlan-Client" /T /F >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo Client war nicht gestartet.
|
||||
) else (
|
||||
echo Client gestoppt.
|
||||
)
|
||||
goto end
|
||||
|
||||
:reload
|
||||
echo Reloading PlantPlan Client ...
|
||||
call :stop
|
||||
timeout /t 2 /nobreak >nul
|
||||
call :start
|
||||
goto end
|
||||
|
||||
:status
|
||||
tasklist /FI "WINDOWTITLE eq PlantPlan-Client" 2>nul | find /i "cmd.exe" >nul
|
||||
if not errorlevel 1 (
|
||||
echo Client: LAEUFT
|
||||
) else (
|
||||
echo Client: GESTOPPT
|
||||
)
|
||||
goto end
|
||||
|
||||
:usage
|
||||
echo.
|
||||
echo Verwendung: client.bat [start^|stop^|reload^|status]
|
||||
echo.
|
||||
echo start - Client starten (Vite Dev-Server)
|
||||
echo stop - Client stoppen
|
||||
echo reload - Client neu starten
|
||||
echo status - Pruefen ob Client laeuft
|
||||
echo.
|
||||
|
||||
:end
|
||||
18
bin/install_npm.bat
Normal file
18
bin/install_npm.bat
Normal file
@@ -0,0 +1,18 @@
|
||||
@echo off
|
||||
REM ================================================================
|
||||
REM PLANTPLAN - Node.js Packages fuer Client installieren
|
||||
REM ================================================================
|
||||
REM Fuehrt npm install im client/ Verzeichnis aus
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
if not exist "%PV_CLIENT%\node_modules" (
|
||||
echo Installiere Node.js Packages fuer Client...
|
||||
cd /d "%PV_CLIENT%"
|
||||
npm install
|
||||
echo Erfolgreich.
|
||||
) else (
|
||||
echo Node.js Packages bereits installiert!
|
||||
echo Zum Aktualisieren: cd client ^& npm install
|
||||
)
|
||||
19
bin/install_npm.sh
Normal file
19
bin/install_npm.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
# ================================================================
|
||||
# PLANTPLAN - Node.js Packages fuer Client installieren
|
||||
# ================================================================
|
||||
# Fuehrt npm install im client/ Verzeichnis aus
|
||||
# ================================================================
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/setenv.sh"
|
||||
|
||||
if [ ! -d "$PV_CLIENT/node_modules" ]; then
|
||||
echo "Installiere Node.js Packages fuer Client..."
|
||||
cd "$PV_CLIENT"
|
||||
npm install
|
||||
echo "Erfolgreich."
|
||||
else
|
||||
echo "Node.js Packages bereits installiert!"
|
||||
echo "Zum Aktualisieren: cd client && npm install"
|
||||
fi
|
||||
74
bin/server.bat
Normal file
74
bin/server.bat
Normal file
@@ -0,0 +1,74 @@
|
||||
@echo off
|
||||
|
||||
REM ================================================================
|
||||
REM PLANTPLAN - Server (FastAPI/Uvicorn) verwalten
|
||||
REM Verwendung: server.bat [start|stop|reload|status]
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
if "%1"=="" goto usage
|
||||
|
||||
if /i "%1"=="start" goto start
|
||||
if /i "%1"=="stop" goto stop
|
||||
if /i "%1"=="reload" goto reload
|
||||
if /i "%1"=="status" goto status
|
||||
goto usage
|
||||
|
||||
:start
|
||||
echo Starting PlantPlan Server (uvicorn) ...
|
||||
|
||||
REM Pruefen ob Server bereits laeuft
|
||||
tasklist /FI "WINDOWTITLE eq PlantPlan-Server" 2>nul | find /i "cmd.exe" >nul
|
||||
if not errorlevel 1 (
|
||||
echo Server laeuft bereits!
|
||||
goto end
|
||||
)
|
||||
|
||||
REM Pruefen ob venv existiert
|
||||
if not exist "%PROJECT%\.venv\Scripts\activate.bat" (
|
||||
echo FEHLER: .venv nicht gefunden! Bitte zuerst install_py.bat ausfuehren.
|
||||
goto end
|
||||
)
|
||||
|
||||
start "PlantPlan-Server" cmd /k "cd /d "%PV_SERVER%" && "%PROJECT%\.venv\Scripts\activate.bat" && uvicorn main:app --reload --host %PV_SERVER_HOST% --port %PV_SERVER_PORT%"
|
||||
echo Server gestartet auf %PV_SERVER_URL%
|
||||
goto end
|
||||
|
||||
:stop
|
||||
echo Stopping PlantPlan Server ...
|
||||
taskkill /FI "WINDOWTITLE eq PlantPlan-Server" /T /F >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo Server war nicht gestartet.
|
||||
) else (
|
||||
echo Server gestoppt.
|
||||
)
|
||||
goto end
|
||||
|
||||
:reload
|
||||
echo Reloading PlantPlan Server ...
|
||||
call :stop
|
||||
timeout /t 2 /nobreak >nul
|
||||
call :start
|
||||
goto end
|
||||
|
||||
:status
|
||||
tasklist /FI "WINDOWTITLE eq PlantPlan-Server" 2>nul | find /i "cmd.exe" >nul
|
||||
if not errorlevel 1 (
|
||||
echo Server: LAEUFT
|
||||
) else (
|
||||
echo Server: GESTOPPT
|
||||
)
|
||||
goto end
|
||||
|
||||
:usage
|
||||
echo.
|
||||
echo Verwendung: server.bat [start^|stop^|reload^|status]
|
||||
echo.
|
||||
echo start - Server starten (uvicorn mit --reload)
|
||||
echo stop - Server stoppen
|
||||
echo reload - Server neu starten
|
||||
echo status - Pruefen ob Server laeuft
|
||||
echo.
|
||||
|
||||
:end
|
||||
@@ -20,6 +20,15 @@ set "PV_LOG=%PROJECT%\log"
|
||||
set "PV_TESTS=%PROJECT%\tests"
|
||||
set "PV_RESULTS=%PROJECT%\results"
|
||||
set "PV_EXAMPLES=%PROJECT%\examples"
|
||||
set "PV_CLIENT=%PROJECT%\client"
|
||||
set "PV_SERVER=%PROJECT%\server"
|
||||
|
||||
REM Netzwerk-Konfiguration
|
||||
set "PV_SERVER_HOST=127.0.0.1"
|
||||
set "PV_SERVER_PORT=8000"
|
||||
set "PV_CLIENT_PORT=5173"
|
||||
set "PV_SERVER_URL=http://%PV_SERVER_HOST%:%PV_SERVER_PORT%"
|
||||
set "PV_CLIENT_URL=http://localhost:%PV_CLIENT_PORT%"
|
||||
|
||||
REM Python-Pfad erweitern (nur wenn noch nicht vorhanden)
|
||||
echo %PYTHONPATH% | find /i "%PV_LIB%" >nul
|
||||
@@ -35,6 +44,8 @@ if not exist "%PV_DATA%" mkdir "%PV_DATA%"
|
||||
if not exist "%PV_LOG%" mkdir "%PV_LOG%"
|
||||
if not exist "%PV_RESULTS%" mkdir "%PV_RESULTS%"
|
||||
if not exist "%PV_EXAMPLES%" mkdir "%PV_EXAMPLES%"
|
||||
if not exist "%PV_CLIENT%" mkdir "%PV_CLIENT%"
|
||||
if not exist "%PV_SERVER%" mkdir "%PV_SERVER%"
|
||||
|
||||
REM Umgebungsvariablen anzeigen
|
||||
echo.
|
||||
@@ -47,6 +58,10 @@ echo PV_DATA = %PV_DATA%
|
||||
echo PV_RESULTS = %PV_RESULTS%
|
||||
echo PV_LOG = %PV_LOG%
|
||||
echo PV_EXAMPLES = %PV_EXAMPLES%
|
||||
echo PV_CLIENT = %PV_CLIENT%
|
||||
echo PV_SERVER = %PV_SERVER%
|
||||
echo PV_SERVER_URL = %PV_SERVER_URL%
|
||||
echo PV_CLIENT_URL = %PV_CLIENT_URL%
|
||||
echo PYTHONPATH = %PYTHONPATH%
|
||||
echo ================================================================
|
||||
echo.
|
||||
|
||||
@@ -19,8 +19,17 @@ export PV_CFG="$PROJECT/cfg"
|
||||
export PV_LOG="$PROJECT/log"
|
||||
export PV_TESTS="$PROJECT/tests"
|
||||
export PV_RESULTS="$PROJECT/results"
|
||||
export PV_CLIENT="$PROJECT/client"
|
||||
export PV_SERVER="$PROJECT/server"
|
||||
export PV_EXAMPLES="$PROJECT/examples"
|
||||
|
||||
# Netzwerk-Konfiguration
|
||||
export PV_SERVER_HOST="127.0.0.1"
|
||||
export PV_SERVER_PORT="8000"
|
||||
export PV_CLIENT_PORT="5173"
|
||||
export PV_SERVER_URL="http://$PV_SERVER_HOST:$PV_SERVER_PORT"
|
||||
export PV_CLIENT_URL="http://localhost:$PV_CLIENT_PORT"
|
||||
|
||||
# Python-Pfad erweitern (nur wenn noch nicht vorhanden)
|
||||
if [[ ":$PYTHONPATH:" != *":$PV_LIB:"* ]]; then
|
||||
export PYTHONPATH="$PV_LIB:$PYTHONPATH"
|
||||
@@ -40,7 +49,11 @@ echo "PV_LIB = $PV_LIB"
|
||||
echo "PV_DATA = $PV_DATA"
|
||||
echo "PV_RESULTS = $PV_RESULTS"
|
||||
echo "PV_LOG = $PV_LOG"
|
||||
echo "PV_CLIENT = $PV_CLIENT"
|
||||
echo "PV_SERVER = $PV_SERVER"
|
||||
echo "PV_EXAMPLES = $PV_EXAMPLES"
|
||||
echo "PV_SERVER_URL = $PV_SERVER_URL"
|
||||
echo "PV_CLIENT_URL = $PV_CLIENT_URL"
|
||||
echo "PYTHONPATH = $PYTHONPATH"
|
||||
echo "================================================================"
|
||||
echo ""
|
||||
|
||||
21
bin/start.bat
Normal file
21
bin/start.bat
Normal file
@@ -0,0 +1,21 @@
|
||||
@echo off
|
||||
|
||||
REM ================================================================
|
||||
REM PLANTPLAN - Server und Client gemeinsam starten
|
||||
REM ================================================================
|
||||
|
||||
call "%~dp0setenv.bat"
|
||||
|
||||
echo ================================================================
|
||||
echo PlantPlan - Starte Server und Client ...
|
||||
echo ================================================================
|
||||
echo.
|
||||
|
||||
call "%~dp0server.bat" start
|
||||
call "%~dp0client.bat" start
|
||||
|
||||
echo.
|
||||
echo ================================================================
|
||||
echo Server: %PV_SERVER_URL%
|
||||
echo Client: %PV_CLIENT_URL%
|
||||
echo ================================================================
|
||||
17
bin/stop.bat
Normal file
17
bin/stop.bat
Normal file
@@ -0,0 +1,17 @@
|
||||
@echo off
|
||||
|
||||
REM ================================================================
|
||||
REM PLANTPLAN - Server und Client gemeinsam stoppen
|
||||
REM ================================================================
|
||||
|
||||
echo ================================================================
|
||||
echo PlantPlan - Stoppe Server und Client ...
|
||||
echo ================================================================
|
||||
echo.
|
||||
|
||||
call "%~dp0server.bat" stop
|
||||
call "%~dp0client.bat" stop
|
||||
|
||||
echo.
|
||||
echo Alle Prozesse gestoppt.
|
||||
echo ================================================================
|
||||
12
client/index.html
Normal file
12
client/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>PlantPlan</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
23
client/package.json
Normal file
23
client/package.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "plantplan-client",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"tldraw": "^2.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"typescript": "^5.7.0",
|
||||
"vite": "^6.0.0"
|
||||
}
|
||||
}
|
||||
10
client/src/App.tsx
Normal file
10
client/src/App.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Tldraw } from 'tldraw'
|
||||
import 'tldraw/tldraw.css'
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div style={{ position: 'fixed', inset: 0 }}>
|
||||
<Tldraw />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
9
client/src/main.tsx
Normal file
9
client/src/main.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './App'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
17
client/tsconfig.json
Normal file
17
client/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"strict": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
16
client/vite.config.ts
Normal file
16
client/vite.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173,
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8000',
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
152
doc/architektur.md
Normal file
152
doc/architektur.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# PlantPlan – Architektur
|
||||
|
||||
**Datum:** 16. April 2026
|
||||
|
||||
---
|
||||
|
||||
## 1. Überblick
|
||||
|
||||
PlantPlan ist eine hybride Webanwendung für technische Gleisplan-Skizzen. Das iPad dient als Eingabegerät (PWA im Safari), ein Python-Backend übernimmt Bibliotheksverwaltung, Normalisierung und Export.
|
||||
|
||||
```
|
||||
┌─────────────────────────┐ ┌─────────────────────────┐
|
||||
│ client/ │ │ server/ │
|
||||
│ (tldraw + React + Vite) │ HTTP │ (FastAPI + Python) │
|
||||
│ │────────▶│ │
|
||||
│ - Canvas & Zeichnung │ JSON │ - Symbolbibliothek │
|
||||
│ - Custom Shapes │◀────────│ - Normalisierung │
|
||||
│ - Snap-to-Grid │ │ - JSON/SVG Export │
|
||||
└─────────────────────────┘ └─────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Projektstruktur
|
||||
|
||||
```
|
||||
plantplan/
|
||||
├── bin/ Skripte (setenv, install, activate)
|
||||
├── cfg/ Konfigurationsdateien
|
||||
├── client/ Frontend – wird auf Server deployed
|
||||
│ ├── src/
|
||||
│ │ ├── main.tsx React Entry Point
|
||||
│ │ └── App.tsx tldraw Canvas
|
||||
│ ├── index.html
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json
|
||||
│ └── vite.config.ts Dev-Proxy /api → localhost:8000
|
||||
├── server/ Backend – wird auf Server kopiert
|
||||
│ ├── catalog/
|
||||
│ │ └── symbols.json Symbolbibliothek
|
||||
│ ├── main.py FastAPI Anwendung
|
||||
│ └── requirements.txt Python-Abhängigkeiten
|
||||
├── doc/ Dokumentation
|
||||
├── lib/ Python-Bibliotheken (eigene Module)
|
||||
├── tests/ Tests
|
||||
└── examples/ Beispieldaten
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Client (Frontend)
|
||||
|
||||
| Aspekt | Detail |
|
||||
|---|---|
|
||||
| Framework | React 18 + TypeScript |
|
||||
| Canvas | tldraw v2 |
|
||||
| Bundler | Vite |
|
||||
| Dev-Server | `localhost:5173` |
|
||||
| Build-Output | `client/dist/` (statische Dateien) |
|
||||
|
||||
Der Vite Dev-Server leitet `/api/*`-Requests per Proxy an das lokale Backend weiter. Im Produktivbetrieb liefert nginx die statischen Dateien aus und proxied API-Calls.
|
||||
|
||||
---
|
||||
|
||||
## 4. Server (Backend)
|
||||
|
||||
| Aspekt | Detail |
|
||||
|---|---|
|
||||
| Framework | FastAPI |
|
||||
| ASGI-Server | Uvicorn |
|
||||
| Python | >= 3.11 |
|
||||
| Dev-Server | `localhost:8000` |
|
||||
|
||||
### API-Endpunkte
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---|---|---|
|
||||
| GET | `/symbols` | Symbolbibliothek als JSON ausliefern |
|
||||
| POST | `/normalize` | Koordinaten auf Grid snappen |
|
||||
| POST | `/export` | Finales JSON/SVG-Projektfile erzeugen |
|
||||
|
||||
### Datenmodell (Request)
|
||||
|
||||
```json
|
||||
{
|
||||
"shapes": [
|
||||
{
|
||||
"id": "shape:abc",
|
||||
"type": "weiche",
|
||||
"x": 100,
|
||||
"y": 200,
|
||||
"props": { "radius": 300, "direction": "left" }
|
||||
}
|
||||
],
|
||||
"grid_size": 10
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Lokale Entwicklung
|
||||
|
||||
Beide Komponenten laufen parallel auf einer Maschine:
|
||||
|
||||
```
|
||||
Terminal 1 (Server):
|
||||
cd server
|
||||
pip install -r requirements.txt
|
||||
uvicorn main:app --reload
|
||||
|
||||
Terminal 2 (Client):
|
||||
cd client
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Kein externer Server nötig. Der Vite-Proxy sorgt dafür, dass Frontend und Backend nahtlos kommunizieren.
|
||||
|
||||
---
|
||||
|
||||
## 6. Deployment (Hetzner)
|
||||
|
||||
Beim Deployment werden zwei Teile auf den Server kopiert:
|
||||
|
||||
| Was | Wohin | Wie |
|
||||
|---|---|---|
|
||||
| `client/dist/` (nach `npm run build`) | nginx Document Root | Statische Dateien |
|
||||
| `server/` | Python-Umgebung | uvicorn hinter nginx reverse proxy |
|
||||
|
||||
```
|
||||
nginx
|
||||
├── / → client/dist/ (statische Dateien)
|
||||
└── /api/* → proxy_pass http://127.0.0.1:8000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Umgebungsvariablen (setenv.bat / setenv.sh)
|
||||
|
||||
| Variable | Pfad |
|
||||
|---|---|
|
||||
| `PROJECT` | Projekt-Root |
|
||||
| `PV_CLIENT` | `client/` |
|
||||
| `PV_SERVER` | `server/` |
|
||||
| `PV_BIN` | `bin/` |
|
||||
| `PV_LIB` | `lib/` |
|
||||
| `PV_CFG` | `cfg/` |
|
||||
| `PV_DATA` | `data/` |
|
||||
| `PV_LOG` | `log/` |
|
||||
| `PV_RESULTS` | `results/` |
|
||||
| `PV_EXAMPLES` | `examples/` |
|
||||
| `PV_TESTS` | `tests/` |
|
||||
26
server/catalog/symbols.json
Normal file
26
server/catalog/symbols.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"symbols": [
|
||||
{
|
||||
"id": "weiche_links_r300",
|
||||
"label": "Weiche links R300",
|
||||
"type": "weiche",
|
||||
"radius": 300,
|
||||
"direction": "left",
|
||||
"svg_path": "M 0,0 L 100,0 Q 150,0 200,50",
|
||||
"snap_points": [[0,0], [100,0], [200,50]],
|
||||
"ports": ["in", "out_gerade", "out_abzweig"]
|
||||
},
|
||||
{
|
||||
"id": "kreisel_standard",
|
||||
"label": "Kreisel",
|
||||
"type": "kreisel",
|
||||
"geometry": {
|
||||
"circle_top_r": 40,
|
||||
"circle_bottom_r": 40,
|
||||
"straight_length": 80
|
||||
},
|
||||
"svg_path": "M -40,0 A 40,40 0 1,1 40,0 A 40,40 0 1,1 -40,0 M -40,160 A 40,40 0 1,1 40,160 A 40,40 0 1,1 -40,160 M -40,0 L -40,160 M 40,0 L 40,160",
|
||||
"snap_points": [[0,0], [0,160]]
|
||||
}
|
||||
]
|
||||
}
|
||||
66
server/main.py
Normal file
66
server/main.py
Normal file
@@ -0,0 +1,66 @@
|
||||
"""
|
||||
PlantPlan FastAPI Backend
|
||||
- Symbolbibliothek ausliefern
|
||||
- Koordinaten normalisieren
|
||||
- JSON/SVG Export
|
||||
"""
|
||||
|
||||
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"
|
||||
|
||||
|
||||
# --- Models ---
|
||||
|
||||
class SketchPayload(BaseModel):
|
||||
shapes: list[dict]
|
||||
grid_size: int = 10
|
||||
|
||||
|
||||
# --- 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,
|
||||
}
|
||||
}
|
||||
3
server/requirements.txt
Normal file
3
server/requirements.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
fastapi>=0.115.0
|
||||
uvicorn[standard]>=0.34.0
|
||||
pydantic>=2.0.0
|
||||
Reference in New Issue
Block a user