Compare commits

...

6 Commits

Author SHA1 Message Date
mistangl
f44949f0af erste Studie zur Verwendung von tldraw 2026-04-13 14:45:27 +02:00
mistangl
c0d37da24c erstes Konzept bei Absprache mit Simon und Yuelin erstellt 2026-04-13 13:45:33 +02:00
mistangl
2281866eb2 icons bunt dazu 2026-04-13 12:32:41 +02:00
mistangl
3e33e11be6 ppt von hand ergänzt 2026-04-13 12:14:20 +02:00
mistangl
b7ac119b05 Bilder für Cockburn dazu 2026-04-13 09:59:34 +02:00
mistangl
644bb47a4c erste Fassung mit erster Seite und Bildern 2026-04-13 09:59:18 +02:00
8 changed files with 406 additions and 17 deletions

View File

@@ -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 &amp; 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

Binary file not shown.

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

BIN
doc/img/photo-mista.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

40
doc/konzept.md Normal file
View 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
View 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 | 12 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*