Compare commits
6 Commits
8997aa3d0e
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f44949f0af | ||
|
|
c0d37da24c | ||
|
|
2281866eb2 | ||
|
|
3e33e11be6 | ||
|
|
b7ac119b05 | ||
|
|
644bb47a4c |
@@ -3,11 +3,11 @@ marp: true
|
|||||||
html: true
|
html: true
|
||||||
paginate: true
|
paginate: true
|
||||||
style: |
|
style: |
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;700&display=swap');
|
@import url('https://fonts.googleapis.com/css2?family=Ubuntu:wght@400;700&display=swap');
|
||||||
section {
|
section {
|
||||||
font-family: 'Ubuntu', sans-serif;
|
font-family: 'Ubuntu', Arial, sans-serif;
|
||||||
padding-top: 55px;
|
padding-top: 55px;
|
||||||
font-size: 30px; /* Normaler Text */
|
font-size: 30px;
|
||||||
}
|
}
|
||||||
.columns {
|
.columns {
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -17,35 +17,79 @@ style: |
|
|||||||
.grau, .grau p, .grau li, .grau ol { color: #888 !important; }
|
.grau, .grau p, .grau li, .grau ol { color: #888 !important; }
|
||||||
section h1 {
|
section h1 {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0; left: 0; right: 0;
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 8px 30px;
|
padding: 8px 30px;
|
||||||
font-size: 1.0em;
|
font-size: 1.2em;
|
||||||
background-color: #2c3e50;
|
background-color: #274E93;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
footer {
|
footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 15px;
|
bottom: 15px; left: 30px;
|
||||||
left: 30px;
|
font-size: 0.85em;
|
||||||
font-size: 0.85em
|
|
||||||
color: #888;
|
color: #888;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
section::after {
|
section::after {
|
||||||
content: "S." attr(data-marpit-pagination) " / " attr(data-marpit-pagination-total);
|
content: "Seite " attr(data-marpit-pagination) " / " attr(data-marpit-pagination-total);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 15px;
|
bottom: 15px; right: 30px;
|
||||||
right: 30px;
|
|
||||||
font-size: 0.75em;
|
font-size: 0.75em;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
/* Cover Slide */
|
||||||
|
section.cover { padding: 0; background: #ffffff; }
|
||||||
|
section.cover h1, section.cover::after { display: none; }
|
||||||
|
.cover-wrap { display: flex; width: 100%; height: 100%; }
|
||||||
|
.cover-left {
|
||||||
|
flex: 0 0 60%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: flex-end;
|
||||||
|
padding: 40px 80px 40px 40px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
.cover-left img {
|
||||||
|
width: 550px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
.cover-subtitle {
|
||||||
|
align-self: flex-start;
|
||||||
|
margin-left: 23%;
|
||||||
|
color: #737373;
|
||||||
|
font-size: 1.0em;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
}
|
||||||
|
.cover-right { flex: 0 0 40%; position: relative; overflow: hidden; }
|
||||||
|
.cover-right img {
|
||||||
|
width: 150%; height: 150%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: 75% center;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.cover-right::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
background: rgba(255, 255, 255, 0.68);
|
||||||
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<!-- _class: cover -->
|
||||||
|
<div class="cover-wrap">
|
||||||
|
<div class="cover-left">
|
||||||
|
<img src="img/logo-mista.png" alt="Mista GmbH Logo">
|
||||||
|
<p class="cover-subtitle">Digitalisierung & Engineering</p>
|
||||||
|
</div>
|
||||||
|
<div class="cover-right">
|
||||||
|
<img src="img/photo-mista-2.jpeg" alt="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Zentraler Projekt-Anker: Use Cases (Epics, Sollprozesse ...)
|
# Zentraler Projekt-Anker: Use Cases (Epics, Sollprozesse ...)
|
||||||
|
|
||||||
@@ -66,9 +110,9 @@ style: |
|
|||||||
|
|
||||||
- Bankkunde hebt an einem Geldautomaten einen beliebigen Geldbetrag von einem Konto ab.
|
- Bankkunde hebt an einem Geldautomaten einen beliebigen Geldbetrag von einem Konto ab.
|
||||||
|
|
||||||
## Designer creates CAD Document
|
## Konstrukteur erzeugt CAD Document
|
||||||
|
|
||||||
- Designer creates a new CAD document from a template.
|
- Designer erzeugt eins neues CAD document aus einem template.
|
||||||
|
|
||||||
## Tester führt Testcase durch
|
## Tester führt Testcase durch
|
||||||
|
|
||||||
|
|||||||
BIN
doc/Cockburn.pptx
Normal file
BIN
doc/Cockburn.pptx
Normal file
Binary file not shown.
BIN
doc/img/logo-mista.png
Normal file
BIN
doc/img/logo-mista.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
1
doc/img/logo-mista.svg
Normal file
1
doc/img/logo-mista.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 19 KiB |
BIN
doc/img/photo-mista-2.jpeg
Normal file
BIN
doc/img/photo-mista-2.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 234 KiB |
BIN
doc/img/photo-mista.jpeg
Normal file
BIN
doc/img/photo-mista.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 KiB |
40
doc/konzept.md
Normal file
40
doc/konzept.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Story
|
||||||
|
Grobauslegung einer Schönenberger ILS Anlage mit Anbindung an SIVAS
|
||||||
|
|
||||||
|
# Stakeholder
|
||||||
|
|
||||||
|
## Geschäftsführung und Vertrieb
|
||||||
|
- Preis (Genauigkeit durch Koppelung an Sivas)
|
||||||
|
- Schnelligkeit um ein Angebot
|
||||||
|
- Vorstellung des Konzeptes, besser als eine Bleistiftskizze
|
||||||
|
- Möglichkeit zur Weitergabe
|
||||||
|
- Digitale Datenverwaltung
|
||||||
|
|
||||||
|
## Kunde
|
||||||
|
- Konzept
|
||||||
|
- Budget Preis
|
||||||
|
- Visualierung in 2D, professioneller als Handskizze
|
||||||
|
|
||||||
|
|
||||||
|
# Standardablauf
|
||||||
|
1. Prozessablauf wird erfragt, bzw. festgelegt (Eingang, Ausgang, Zwischenstationen)
|
||||||
|
2. Erfassung der Materialflussdaten wenn vorhanden
|
||||||
|
- Art der Betriebsmittel (minitrolley, single carrier, trolley -> durchschnittliche Teiledicke)
|
||||||
|
- Stückzahlen / Durchsatz an einzelnen Zwischenstationen
|
||||||
|
3. Skizzierung von Grundelementen der Anlage
|
||||||
|
- Kreisel
|
||||||
|
- Pufferbereiche (Modifikation der Kapazität, Strecke/Fläche aus der Kappa, oder umgekehrt)
|
||||||
|
- Arbeitsplätze
|
||||||
|
- Schnittstelle zu anderen Systemen, Be- und Entladung
|
||||||
|
- Sorter (Art aus Durchsatz: Matrix, Tuple, Sequenzierkreisel)
|
||||||
|
- Anbaugruppen (RFID Leser, )
|
||||||
|
4. Verbindung der Elemente wenn nötig
|
||||||
|
- Verbindungsstrecken
|
||||||
|
- Förderer
|
||||||
|
5. Export einer csv Zur Preisbestimmung in Sivas (oder direkte Abfrage über Serverprozess)
|
||||||
|
6. Rahmenangaben für die Kalkulation von Programmierung, Reisekosten, Montagestundenplan
|
||||||
|
7. Bestimmung eines Endpreises aus Sivas Abfrage und Rahmenangaben
|
||||||
|
|
||||||
|
|
||||||
|
# Ausnahmen
|
||||||
|
|
||||||
304
doc/tldraw_technologie.md
Normal file
304
doc/tldraw_technologie.md
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
# Technologie-Zusammenfassung: iPad Skizzen-Tool mit Symbolbibliothek
|
||||||
|
|
||||||
|
**Datum:** 09. April 2026
|
||||||
|
**Thema:** Hybride iPad-Anwendung für technische Skizzen (Schienen/Weichen/Kreisel) mit Snap-to-Grid, Symbolbibliothek und JSON/SVG-Export
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Anforderungen
|
||||||
|
|
||||||
|
- iPad als Eingabegerät (Bleistift-Look)
|
||||||
|
- ~10 Grundsymbole (Weichen, Kreisel, etc.) aus Katalog
|
||||||
|
- Snap-to-Grid
|
||||||
|
- Output: **JSON + SVG**
|
||||||
|
- Symbole parametrisch veränderbar (z.B. Kreiselab stand)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Gewählte Architektur: Option C (Hybrid)
|
||||||
|
|
||||||
|
```
|
||||||
|
iPad (PWA/Safari) Hetzner Server
|
||||||
|
───────────────── ──────────────
|
||||||
|
tldraw (Canvas) FastAPI (Python)
|
||||||
|
rough.js (Bleistift) │
|
||||||
|
Custom Shapes ├── GET /symbols → Bibliothek ausliefern
|
||||||
|
│ ├── POST /normalize → Skizze → SVG
|
||||||
|
│ JSON (Shapes + Meta) └── POST /export → finales JSON/SVG
|
||||||
|
└──────────────────────────────────────►
|
||||||
|
```
|
||||||
|
|
||||||
|
**Warum Hybrid:**
|
||||||
|
- iPad nur für Eingabe + lokales Snap/Rendering (kein App Store, kein Swift)
|
||||||
|
- Server für Bibliotheksverwaltung, Normalisierung und Export (Python/FastAPI)
|
||||||
|
- PWA läuft direkt im Safari, gehostet auf Hetzner via nginx
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Kerntechnologien
|
||||||
|
|
||||||
|
| Schicht | Technologie | Zweck |
|
||||||
|
|---|---|---|
|
||||||
|
| iPad-Frontend | **tldraw** (PWA) | Canvas, Snap-to-Grid, SVG-Export |
|
||||||
|
| Bleistift-Look | **rough.js** | Skizzenhafter Renderstil |
|
||||||
|
| Symbolbibliothek | **JSON + SVG-Pfade** | Katalog mit Metadaten |
|
||||||
|
| Backend | **FastAPI (Python)** | Bibliothek, Normalisierung, Export |
|
||||||
|
| Output | **SVG + JSON** | Weiterverarbeitung / CAD |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Symbolbibliothek-Format (JSON)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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": "...",
|
||||||
|
"snap_points": [[0,0], [0,160]]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Metadaten an tldraw-Shapes anhängen
|
||||||
|
|
||||||
|
### Option A: `meta`-Feld (schnell, für Prototypen)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
editor.createShape({
|
||||||
|
type: 'geo',
|
||||||
|
x: 100,
|
||||||
|
y: 200,
|
||||||
|
props: {
|
||||||
|
geo: 'rectangle',
|
||||||
|
w: 80,
|
||||||
|
h: 40,
|
||||||
|
},
|
||||||
|
meta: {
|
||||||
|
symbol_id: 'weiche_links_r300',
|
||||||
|
symbol_type: 'weiche',
|
||||||
|
radius: 300,
|
||||||
|
direction: 'left',
|
||||||
|
ports: ['in', 'out_gerade', 'out_abzweig'],
|
||||||
|
catalog_version: '1.2'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Custom Shape Type (produktiv, empfohlen)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const WeicheShapeUtil = defineShape({
|
||||||
|
type: 'weiche',
|
||||||
|
props: {
|
||||||
|
radius: T.number,
|
||||||
|
direction: T.string,
|
||||||
|
ports: T.arrayOf(T.string),
|
||||||
|
svg_path: T.string,
|
||||||
|
},
|
||||||
|
component(shape) {
|
||||||
|
return (
|
||||||
|
<SVGContainer>
|
||||||
|
<path d={shape.props.svg_path} stroke="black" fill="none" />
|
||||||
|
</SVGContainer>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
getBounds(shape) { /* ... */ }
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
**JSON-Export einer Custom Shape:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "shape:abc123",
|
||||||
|
"type": "weiche",
|
||||||
|
"x": 100,
|
||||||
|
"y": 200,
|
||||||
|
"props": {
|
||||||
|
"radius": 300,
|
||||||
|
"direction": "left",
|
||||||
|
"ports": ["in", "out_gerade", "out_abzweig"],
|
||||||
|
"svg_path": "M 0,0 L 100,0 Q 150,0 200,50"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Parametrische Shapes: Kreisel-Beispiel
|
||||||
|
|
||||||
|
Ziel: Nutzer kann nur den **Abstand der zwei Kreise** verändern, alles andere ist gesperrt.
|
||||||
|
|
||||||
|
### 6.1 Shape-Definition mit eingeschränkten Props
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const KreiselShapeUtil = defineShape({
|
||||||
|
type: 'kreisel',
|
||||||
|
props: {
|
||||||
|
abstand: T.number, // ← einziger veränderbarer Parameter
|
||||||
|
radius: T.number, // fest (aus Katalog)
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.2 Custom Handle (nur vertikale Bewegung)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
getHandles(shape) {
|
||||||
|
return {
|
||||||
|
mitte: {
|
||||||
|
id: 'mitte',
|
||||||
|
type: 'vertex',
|
||||||
|
x: 0,
|
||||||
|
y: shape.props.abstand / 2, // Handle mittig zwischen den Kreisen
|
||||||
|
canBind: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onHandleDrag(shape, { handle, delta }) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
// Nur abstand ändern, x-Bewegung ignorieren, Minimum erzwingen
|
||||||
|
abstand: Math.max(20, shape.props.abstand + delta.y * 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.3 Resize einschränken (nur Höhe)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
onResize(shape, info) {
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
abstand: Math.max(20, info.newPoint.y * 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getResizeSnapGeometry(shape) {
|
||||||
|
return { points: [[0, 0], [0, shape.props.abstand]] }
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.4 SVG-Rendering (reagiert auf `abstand`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
component(shape) {
|
||||||
|
const { abstand, radius } = shape.props
|
||||||
|
const r = radius
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SVGContainer>
|
||||||
|
{/* Oberer Kreis */}
|
||||||
|
<circle cx={0} cy={0} r={r} stroke="black" fill="none" />
|
||||||
|
|
||||||
|
{/* Unterer Kreis – position hängt von abstand ab */}
|
||||||
|
<circle cx={0} cy={abstand} r={r} stroke="black" fill="none" />
|
||||||
|
|
||||||
|
{/* Zwei vertikale Verbindungslinien */}
|
||||||
|
<line x1={-r} y1={0} x2={-r} y2={abstand} stroke="black" />
|
||||||
|
<line x1={ r} y1={0} x2={ r} y2={abstand} stroke="black" />
|
||||||
|
</SVGContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6.5 Freiheitsgrade im Überblick
|
||||||
|
|
||||||
|
| Aktion | Erlaubt |
|
||||||
|
|---|---|
|
||||||
|
| Kreisel verschieben | ✓ |
|
||||||
|
| Abstand der Kreise ändern | ✓ (Handle mittig) |
|
||||||
|
| Breite / Radius ändern | ✗ gesperrt |
|
||||||
|
| Drehen | konfigurierbar |
|
||||||
|
| Im JSON-Export | `"abstand": 120` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. FastAPI-Backend (3 Endpunkte)
|
||||||
|
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
# 1. Symbolbibliothek ausliefern
|
||||||
|
@app.get("/symbols")
|
||||||
|
async def get_symbols():
|
||||||
|
with open("catalog/symbols.json") as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
# 2. Koordinaten normalisieren / bereinigen
|
||||||
|
class SketchPayload(BaseModel):
|
||||||
|
shapes: list[dict]
|
||||||
|
grid_size: int = 10
|
||||||
|
|
||||||
|
@app.post("/normalize")
|
||||||
|
async def normalize(payload: SketchPayload):
|
||||||
|
# Snap-Koordinaten auf Grid runden, SVG-Pfade normalisieren
|
||||||
|
...
|
||||||
|
return {"svg": normalized_svg, "shapes": cleaned_shapes}
|
||||||
|
|
||||||
|
# 3. Finales Projektfile exportieren
|
||||||
|
@app.post("/export")
|
||||||
|
async def export(payload: SketchPayload):
|
||||||
|
return {
|
||||||
|
"json": build_project_json(payload.shapes),
|
||||||
|
"svg": build_final_svg(payload.shapes)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Umsetzungsplan (Phasen)
|
||||||
|
|
||||||
|
| Phase | Inhalt | Aufwand |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | tldraw als PWA auf Hetzner (nginx + Docker) | 1 Tag |
|
||||||
|
| 2 | Symbolbibliothek JSON + SVG-Pfade für 10 Symbole | 1–2 Tage |
|
||||||
|
| 3 | Custom Shape Types in tldraw registrieren | 1 Tag |
|
||||||
|
| 4 | FastAPI-Backend mit 3 Endpunkten | 1 Tag |
|
||||||
|
| 5 | Parametrische Handles (Kreisel etc.) | 1 Tag |
|
||||||
|
| 6 | Export → JSON + SVG finalisieren | 1 Tag |
|
||||||
|
|
||||||
|
**Geschätzter Gesamtaufwand Prototyp: ~1 Woche**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Entscheidungsmatrix: `meta` vs. Custom Shape
|
||||||
|
|
||||||
|
| Kriterium | `meta`-Feld | Custom Shape Type |
|
||||||
|
|---|---|---|
|
||||||
|
| Implementierungsaufwand | minimal | mittel |
|
||||||
|
| Eigenes SVG-Rendering | ✗ | ✓ |
|
||||||
|
| Typvalidierung der Props | ✗ | ✓ |
|
||||||
|
| Parametrische Handles | ✗ | ✓ |
|
||||||
|
| Im JSON-Export enthalten | ✓ | ✓ |
|
||||||
|
| Empfehlung | Prototyp Phase 1 | Produktiv ab Phase 3 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Erstellt mit Claude (Anthropic) – Technologiegespräch vom 09.04.2026*
|
||||||
Reference in New Issue
Block a user