prototype and icons
@@ -0,0 +1,130 @@
|
||||
test/flutter_test_project/.gitignore
|
||||
test/flutter_test_project/.metadata
|
||||
test/flutter_test_project/analysis_options.yaml
|
||||
test/flutter_test_project/pubspec.lock
|
||||
test/flutter_test_project/pubspec.yaml
|
||||
test/flutter_test_project/README.md
|
||||
test/flutter_test_project/android/.gitignore
|
||||
test/flutter_test_project/android/build.gradle.kts
|
||||
test/flutter_test_project/android/gradle.properties
|
||||
test/flutter_test_project/android/settings.gradle.kts
|
||||
test/flutter_test_project/android/app/build.gradle.kts
|
||||
test/flutter_test_project/android/app/src/debug/AndroidManifest.xml
|
||||
test/flutter_test_project/android/app/src/main/AndroidManifest.xml
|
||||
test/flutter_test_project/android/app/src/main/kotlin/com/example/flutter_test_project/MainActivity.kt
|
||||
test/flutter_test_project/android/app/src/main/res/drawable/launch_background.xml
|
||||
test/flutter_test_project/android/app/src/main/res/drawable-v21/launch_background.xml
|
||||
test/flutter_test_project/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
|
||||
test/flutter_test_project/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
|
||||
test/flutter_test_project/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
|
||||
test/flutter_test_project/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
|
||||
test/flutter_test_project/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
|
||||
test/flutter_test_project/android/app/src/main/res/values/styles.xml
|
||||
test/flutter_test_project/android/app/src/main/res/values-night/styles.xml
|
||||
test/flutter_test_project/android/app/src/profile/AndroidManifest.xml
|
||||
test/flutter_test_project/android/gradle/wrapper/gradle-wrapper.properties
|
||||
test/flutter_test_project/ios/.gitignore
|
||||
test/flutter_test_project/ios/Flutter/AppFrameworkInfo.plist
|
||||
test/flutter_test_project/ios/Flutter/Debug.xcconfig
|
||||
test/flutter_test_project/ios/Flutter/Release.xcconfig
|
||||
test/flutter_test_project/ios/Runner/AppDelegate.swift
|
||||
test/flutter_test_project/ios/Runner/Info.plist
|
||||
test/flutter_test_project/ios/Runner/Runner-Bridging-Header.h
|
||||
test/flutter_test_project/ios/Runner/SceneDelegate.swift
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
|
||||
test/flutter_test_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
|
||||
test/flutter_test_project/ios/Runner/Base.lproj/LaunchScreen.storyboard
|
||||
test/flutter_test_project/ios/Runner/Base.lproj/Main.storyboard
|
||||
test/flutter_test_project/ios/Runner.xcodeproj/project.pbxproj
|
||||
test/flutter_test_project/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
|
||||
test/flutter_test_project/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
|
||||
test/flutter_test_project/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
|
||||
test/flutter_test_project/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
|
||||
test/flutter_test_project/ios/Runner.xcworkspace/contents.xcworkspacedata
|
||||
test/flutter_test_project/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
|
||||
test/flutter_test_project/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
|
||||
test/flutter_test_project/ios/RunnerTests/RunnerTests.swift
|
||||
test/flutter_test_project/lib/main.dart
|
||||
test/flutter_test_project/linux/.gitignore
|
||||
test/flutter_test_project/linux/CMakeLists.txt
|
||||
test/flutter_test_project/linux/flutter/CMakeLists.txt
|
||||
test/flutter_test_project/linux/flutter/generated_plugin_registrant.cc
|
||||
test/flutter_test_project/linux/flutter/generated_plugin_registrant.h
|
||||
test/flutter_test_project/linux/flutter/generated_plugins.cmake
|
||||
test/flutter_test_project/linux/runner/CMakeLists.txt
|
||||
test/flutter_test_project/linux/runner/main.cc
|
||||
test/flutter_test_project/linux/runner/my_application.cc
|
||||
test/flutter_test_project/linux/runner/my_application.h
|
||||
test/flutter_test_project/macos/.gitignore
|
||||
test/flutter_test_project/macos/Flutter/Flutter-Debug.xcconfig
|
||||
test/flutter_test_project/macos/Flutter/Flutter-Release.xcconfig
|
||||
test/flutter_test_project/macos/Flutter/GeneratedPluginRegistrant.swift
|
||||
test/flutter_test_project/macos/Runner/AppDelegate.swift
|
||||
test/flutter_test_project/macos/Runner/DebugProfile.entitlements
|
||||
test/flutter_test_project/macos/Runner/Info.plist
|
||||
test/flutter_test_project/macos/Runner/MainFlutterWindow.swift
|
||||
test/flutter_test_project/macos/Runner/Release.entitlements
|
||||
test/flutter_test_project/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
|
||||
test/flutter_test_project/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
|
||||
test/flutter_test_project/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
|
||||
test/flutter_test_project/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
|
||||
test/flutter_test_project/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
|
||||
test/flutter_test_project/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
|
||||
test/flutter_test_project/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
|
||||
test/flutter_test_project/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
|
||||
test/flutter_test_project/macos/Runner/Base.lproj/MainMenu.xib
|
||||
test/flutter_test_project/macos/Runner/Configs/AppInfo.xcconfig
|
||||
test/flutter_test_project/macos/Runner/Configs/Debug.xcconfig
|
||||
test/flutter_test_project/macos/Runner/Configs/Release.xcconfig
|
||||
test/flutter_test_project/macos/Runner/Configs/Warnings.xcconfig
|
||||
test/flutter_test_project/macos/Runner.xcodeproj/project.pbxproj
|
||||
test/flutter_test_project/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
|
||||
test/flutter_test_project/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
|
||||
test/flutter_test_project/macos/Runner.xcworkspace/contents.xcworkspacedata
|
||||
test/flutter_test_project/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
|
||||
test/flutter_test_project/macos/RunnerTests/RunnerTests.swift
|
||||
test/flutter_test_project/test/widget_test.dart
|
||||
test/flutter_test_project/web/favicon.png
|
||||
test/flutter_test_project/web/index.html
|
||||
test/flutter_test_project/web/manifest.json
|
||||
test/flutter_test_project/web/icons/Icon-192.png
|
||||
test/flutter_test_project/web/icons/Icon-512.png
|
||||
test/flutter_test_project/web/icons/Icon-maskable-192.png
|
||||
test/flutter_test_project/web/icons/Icon-maskable-512.png
|
||||
test/flutter_test_project/windows/.gitignore
|
||||
test/flutter_test_project/windows/CMakeLists.txt
|
||||
test/flutter_test_project/windows/flutter/CMakeLists.txt
|
||||
test/flutter_test_project/windows/flutter/generated_plugin_registrant.cc
|
||||
test/flutter_test_project/windows/flutter/generated_plugin_registrant.h
|
||||
test/flutter_test_project/windows/flutter/generated_plugins.cmake
|
||||
test/flutter_test_project/windows/runner/CMakeLists.txt
|
||||
test/flutter_test_project/windows/runner/flutter_window.cpp
|
||||
test/flutter_test_project/windows/runner/flutter_window.h
|
||||
test/flutter_test_project/windows/runner/main.cpp
|
||||
test/flutter_test_project/windows/runner/resource.h
|
||||
test/flutter_test_project/windows/runner/runner.exe.manifest
|
||||
test/flutter_test_project/windows/runner/Runner.rc
|
||||
test/flutter_test_project/windows/runner/utils.cpp
|
||||
test/flutter_test_project/windows/runner/utils.h
|
||||
test/flutter_test_project/windows/runner/win32_window.cpp
|
||||
test/flutter_test_project/windows/runner/win32_window.h
|
||||
test/flutter_test_project/windows/runner/resources/app_icon.ico
|
||||
@@ -0,0 +1,289 @@
|
||||
# Drone Mission Control (DMC) — Architektur- und Design-Dokumentation
|
||||
|
||||
> Zusammenfassung aller bisher in diesem Projekt getroffenen Architektur- und Design-Entscheidungen: vom HTML/JS-Demonstrator ("Drone Mission Command.html") bis zur geplanten Flutter-Zielarchitektur.
|
||||
|
||||
---
|
||||
|
||||
## 1. Projektüberblick
|
||||
|
||||
**Ziel:** Mobile App zur Missionsplanung und Flugsteuerung für einen Fixed-Wing-Drohnen-Aufbau (Heewing T1 Ranger, iNAV-Firmware), mit Fokus auf Wegpunkt-Missionen, Live-Fly-Modus und Point-and-Fly (PnF)-Einzelziel-Anflug.
|
||||
|
||||
**Zwei Entwicklungsstränge:**
|
||||
1. **HTML/JS-Demonstrator** (`Drone Mission Command.html`, vormals `taf_mission_planner_osm_v13.html`) — iteratives Rapid-Prototyping zur Validierung von Workflow und Feature-Umfang, lauffähig im Browser.
|
||||
2. **Flutter-Zielarchitektur** — die für die produktive App geplante Struktur, die den Demonstrator ablösen soll.
|
||||
|
||||
**Sprachregelung:** Konversation und Dokumentation auf Deutsch, Code und UI-Texte auf Englisch.
|
||||
|
||||
---
|
||||
|
||||
## 2. Systemarchitektur (Flutter-Zielarchitektur)
|
||||
|
||||
Fünf-Schichten-Architektur, gemeinsam festgelegt:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ UI Layer (Screens: Plan / Fly / Settings / │
|
||||
│ Library) │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ App-Mode State Machine │
|
||||
│ (Plan → Connect → Upload → Fly) │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Mission Domain Layer │
|
||||
│ (Strategy Pattern: TrackMission, │
|
||||
│ SurveyAreaMission*, SearchAreaMission*) │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Mission-Sync Service │ Telemetry Service │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ Transport / Protokoll-Layer │
|
||||
│ (FlightControllerLink-Abstraktion) │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
\* geplant, noch nicht implementiert
|
||||
|
||||
### 2.1 Kernprinzip: Protokoll-Abstraktion
|
||||
Ein einheitliches Interface `FlightControllerLink` kapselt die Kommunikation mit dem Flightcontroller, unabhängig davon, ob iNAV (via MSP) oder ArduPilot (via MAVLink) angesprochen wird. Die Mission-Domain-Schicht kennt nur eine protokollneutrale `FlatWaypointList`; die Divergenz zu MSP bzw. MAVLink findet ausschließlich im letzten Schritt (Encoder) statt.
|
||||
|
||||
### 2.2 App-Mode State Machine
|
||||
Explizite Zustandsfolge: **Plan → Connect → Upload → Fly**, inkl. definiertem "Ready-to-Fly"-Gate:
|
||||
> Mission hochgeladen **und** verifiziert **und** Drohne noch disarmed.
|
||||
|
||||
Grund: iNAV (und MAVLink bei iNAV) akzeptiert `MSP_SET_WP`-Uploads grundsätzlich nur im **unarmed**-Zustand. Der Handover-Prozess (Connect → Verify → Upload → Ready-to-Fly) muss dieses Gate technisch erzwingen, nicht nur als UI-Konvention behandeln.
|
||||
|
||||
---
|
||||
|
||||
## 3. Komponenten
|
||||
|
||||
### 3.1 `FlightControllerLink` (Interface)
|
||||
Zentrale Abstraktion für alle FC-Interaktionen.
|
||||
|
||||
**Methoden/Properties:**
|
||||
- `connect()`, `disconnect()`
|
||||
- `capabilities` → `FcCapabilities`-Struct (Feature-Flags: `supportsMultiMission`, `supportsInFlightUpload`, `maxWaypoints`, …)
|
||||
- `uploadMission(FlatWaypointList)`
|
||||
- `setFlightMode(mode)` — abstrakte Modus-Konstanten: `MISSION_RUN`, `GUIDED_POINT`, `RETURN_HOME`, `HOLD`
|
||||
- `arm()`
|
||||
- `subscribeTelemetry()`
|
||||
- `readActiveWaypointIndex()`
|
||||
|
||||
**Implementierungen:**
|
||||
- `MspFlightControllerLink` — für iNAV, eigenständiger MSP-Adapter (kein fertiges Dart-Paket verfügbar, komplett selbst implementiert)
|
||||
- `MavlinkFlightControllerLink` — für ArduPilot (perspektivisch, nicht MVP)
|
||||
- `MockFlightControllerLink` — synthetische Telemetrie für Tests ohne Hardware/SITL
|
||||
|
||||
### 3.2 Mission Domain Layer
|
||||
Strategy-Pattern für erweiterbare Missionstypen:
|
||||
- `TrackMission` — aktuell implementiert (Wegpunkt-Strecke)
|
||||
- `SurveyAreaMission` — geplant (Flächenabflug)
|
||||
- `SearchAreaMission` — geplant (Suchmuster)
|
||||
|
||||
Gemeinsame Datenstruktur: `FlatWaypointList` (protokollunabhängig), am Ende protokollspezifisch codiert durch:
|
||||
- `MspWaypointEncoder` (iNAV)
|
||||
- `MavlinkMissionEncoder` (ArduPilot)
|
||||
|
||||
### 3.3 Mission-Sync Service
|
||||
Orchestriert den Ablauf Connect → Verify → Upload → Ready-to-Fly. Erzwingt das Ready-to-Fly-Gate.
|
||||
|
||||
### 3.4 Telemetry Service
|
||||
Abonniert Live-Telemetrie vom FC, liefert normalisierte `TelemetryFrame`-Objekte an die UI. Läuft in einem eigenen **Isolate**, um Kartenrendering nicht zu blockieren.
|
||||
|
||||
### 3.5 Drohnen-Profil (Settings)
|
||||
Enthält u. a.:
|
||||
- **FC-Typ** — bewusst als explizites Setting im Drohnen-Profil verankert, **nicht** aus dem Transportkanal abgeleitet (siehe Abschnitt 4.3)
|
||||
- Flugleistungsdaten: min/max Speed, min. Kurvenradius, max. Steig-/Sinkrate, max. Reichweite, max. Flugdauer, Cruise Speed
|
||||
- Standardprofil: **T1 Ranger** (Heewing)
|
||||
- Liste mehrerer Profile, aufrufbar über Tap, Teil des Vollbild-Menüs (gemeinsam mit Missionsübersicht, per Tabs umschaltbar)
|
||||
|
||||
### 3.6 Waypoint-Liste (UI-Komponente)
|
||||
- Spalten: **Execute** (Dropdown: —/Loiter/Landing) und **Action**
|
||||
- Swipe-adjustierbare Wertechips für Alt/Speed/Catch-Radius (vertikal ±10, horizontal ±1, 14px/Step, Achsen-Lock bei 8px)
|
||||
- Zeilen-Edit-Modus bewusst **entkoppelt** vom Karten-Edit-Modus (siehe Abschnitt 4.4)
|
||||
- Zeile antippen → Karte zentriert auf diesen Wegpunkt, Liste schließt sich
|
||||
- Liste zeigt englische Spaltenüberschriften, ausreichend Randabstand zu den Drehrädern
|
||||
- Automatisches Beenden des Zeilen-Edit-Modus beim Schließen der Liste (Sicherung gegen inkonsistenten Zustand)
|
||||
|
||||
### 3.7 Flugpfad-Darstellung
|
||||
- Kein einfacher Spline durch alle Punkte, sondern physikalisch fundierte Geometrie: **Geradenstücke + tangentiale Kreisbögen (Fillets)** pro Kursänderung, abgeleitet aus Geschwindigkeit + max. Schräglage (25°)
|
||||
- Machbarkeitsprüfung kombiniert Tangentenlänge der Kurve mit dem Fangradius (60 m): überschreitet die Tangentenlänge den Fangradius, wird der Turn rot markiert
|
||||
- Höhenprofil-Anzeige in der unteren Leiste
|
||||
|
||||
### 3.8 Wind-System
|
||||
- `buildCombinedWindLevels()` — vereint Druckflächendaten und feste Nabenhöhen (10/80/120/180 m AGL, in ASL umgerechnet) als Stützpunkte für Interpolation
|
||||
- `windUrlsFor()` — baut zwei getrennte, fokussierte Open-Meteo-Requests
|
||||
- Alle drei Konsumenten (Header, Waypoints, Validierungspanel) nutzen denselben Mechanismus
|
||||
- Böen (`wind_gusts_10m`) nur im Validierungspanel, nicht in der Wegpunktanzeige
|
||||
|
||||
### 3.9 Terrain-System
|
||||
- Distanzbasierte Sampling-Strategie: alle 30 m (`TERRAIN_SAMPLE_SPACING`), min. 10 / max. 2000 Samples (`TERRAIN_MAX_SAMPLES`)
|
||||
- Einzelpunkt-Höhenabfrage (`fetchElevationAt`) für Point-and-Fly-Kollisionsprüfung
|
||||
- Datenquelle: AWS Terrarium Tiles
|
||||
|
||||
### 3.10 Point-and-Fly (PnF) Modus
|
||||
- Zentrales Fadenkreuz (amber) auf dem Bildschirm
|
||||
- Setzt transienten Einzel-Wegpunkt (Loiter-Aktion) mit aktuellen Radwerten für Alt/Speed
|
||||
- Terrain-Kollisionsprüfung: mind. 60 m Bodenabstand, referenziert gegen Home-Wegpunkt-Höhe
|
||||
- Zielpunkt übernimmt beim Setzen die zu diesem Zeitpunkt gültigen Radwerte dauerhaft (ändert sich nicht rückwirkend)
|
||||
- Zielpunkt verschwindet beim Moduswechsel oder Verlassen des Fly-Modus
|
||||
- Zentrale Sichtbarkeitslogik über ein gemeinsames Prädikat (Räder, Fadenkreuz, Readouts konsistent)
|
||||
|
||||
### 3.11 Fly-Modus allgemein
|
||||
- `setFlightMode()` zentralisiert
|
||||
- Eintritt in Fly-Modus setzt immer auf Standardmodus **Waypoint** zurück
|
||||
- Verlassen des Fly-Modus klappt Dropdown automatisch ein
|
||||
- Readouts: drei Zeilen (Titel, SET in Blau, LIVE in Grün)
|
||||
|
||||
### 3.12 Branding/Logo
|
||||
- M-/D-/C-Monogramm-Konstruktion (Drone Mission Command)
|
||||
- Finale Lösung: vertikal gespiegeltes "C" mit kurzem vertikalem Schließstrich statt verjüngtem "D" — mathematisch als Sonderfall derselben Tangentenkonstruktion bestätigt (Stammlänge 75,2 Einheiten, 92,4 % bilaterale Symmetrie)
|
||||
|
||||
---
|
||||
|
||||
## 4. Getroffene Design-Entscheidungen inkl. Begründung
|
||||
|
||||
| # | Entscheidung | Begründung |
|
||||
|---|---|---|
|
||||
| 4.1 | **MVP-Scope: nur Android, nur iNAV via MSP, nur WiFi/TCP** | Reduziert Komplexität für ersten Release; MAVLink, USB-Seriell und iOS werden bewusst zurückgestellt |
|
||||
| 4.2 | **MSP für iNAV, MAVLink für ArduPilot**, beide hinter `FlightControllerLink` | Jedes Protokoll spricht die native Sprache seines FC; einheitliche Domain-Schicht bleibt protokollunabhängig |
|
||||
| 4.3 | **FC-Typ als explizites Setting im Drohnen-Profil**, nicht aus Transport abgeleitet | Zukunftssicher: falls iNAV später testweise über MAVLink läuft, bleibt Zuordnung eindeutig steuerbar |
|
||||
| 4.4 | **Zeilen-Edit-Modus der Wegpunktliste von Karten-Edit-Modus entkoppelt** | Nutzer-Feedback: gekoppeltes Verhalten war unerwartet/unerwünscht — Bearbeiten in der Liste soll nicht automatisch den Karten-Modus verändern |
|
||||
| 4.5 | **"Ready-to-Fly"-Gate = Upload + Verifiziert + Disarmed** | iNAV/MAVLink erlauben grundsätzlich keinen Missions-Upload im armed-Zustand; das Gate muss dies technisch erzwingen |
|
||||
| 4.6 | **Physikalisch fundierter Flugpfad (Geraden + Kreisbögen) statt Spline** | Ehrlichere Darstellung dessen, was ein realer Autopilot (ArduPlane/iNAV) tatsächlich fliegt; verhindert falsches Sicherheitsgefühl bei engen Kurven |
|
||||
| 4.7 | **Tangentenkonstruktion statt frei gezogener Linien beim Logo** | Vermeidet sichtbaren Knick zwischen Gerade und Bogen; mathematisch konsistent mit der ursprünglichen D-Form |
|
||||
| 4.8 | **Gespiegeltes C statt verjüngtem D als Logo-Grundform** | Wirkt symmetrischer, ist aber dieselbe Konstruktion nur gespiegelt und geschlossen |
|
||||
| 4.9 | **Böen nur im Validierungspanel, nicht in der Wegpunktanzeige** | 10-m-Böendaten sind auf Flughöhe nicht aussagekräftig |
|
||||
| 4.10 | **Zwei getrennte Open-Meteo-Requests statt einem kombinierten** | Ein kombinierter 35-Variablen-Request verursachte Modellauswahl-Fehler; auch ein einzelner Request für die Validierung schlug fehl — zwei fokussierte Requests bestätigt als robustes Muster |
|
||||
| 4.11 | **`typeof x === 'number'`-Guards überall im Wind-Code** | Open-Meteo liefert bei fehlenden Werten `null`, nicht `undefined` — ungeschützter Code führte zu drei aufeinanderfolgenden Rendering-Fehlern |
|
||||
| 4.12 | **Distanzbasiertes statt festes Terrain-Sampling (30 m Spacing)** | Gleichmäßigere Auflösung unabhängig von der Missionslänge, mit sinnvollen Ober-/Untergrenzen (10–2000 Samples) |
|
||||
| 4.13 | **Fester Dateiname "Drone Mission Command.html"** | Bewusst beibehalten, um `localStorage`-Daten über Updates hinweg zu erhalten |
|
||||
| 4.14 | **Klettersteigungsprüfung nutzt geradlinige statt Bogen-Distanz** | Bewusste Vereinfachung; als vernachlässigbar bewertet, aber offen dokumentiert (siehe Abschnitt 6) |
|
||||
| 4.15 | **Drift statt Hive für Missions-Persistenz (Flutter-Ziel)** | Indexierte Abfragen nötig (Suche nach Name, Ort, Datum); zudem kompatibel mit künftigem local-first Server-Sync-Muster (Dirty-Flags, Server-IDs) |
|
||||
| 4.16 | **Bloc/Riverpod-Kombination für State-Management (Flutter-Ziel)** | Bloc passt zur expliziten App-Mode-State-Machine (erzwungene Event→State-Struktur, Debug-Tooling); Riverpod ist ergonomischer für viele kleine reaktive Zustände wie Telemetriewerte |
|
||||
| 4.17 | **`flutter_map` statt `google_maps_flutter` (Flutter-Ziel)** | Freies OSM-Tile-Modell entspricht dem Leaflet-Demonstrator; anderes Lizenz-/API-Modell bei Google Maps unerwünscht; ermöglicht direkte Portierung der Tangenten-Bogen-Geometrie |
|
||||
| 4.18 | **`flutter_map_tile_caching` (FMTC) für Offline-Kacheln (Flutter-Ziel)** | Automatisches Caching pro Mission, gekeyt über die Bounding-Box der Mission |
|
||||
| 4.19 | **Eigener MSP-Encoder/Decoder in Dart (Flutter-Ziel)** | Kein fertiges Dart-Paket verfügbar; Struktur bereits aus MSP-Recherche bekannt |
|
||||
| 4.20 | **Foreground Service (Android) für Telemetrie im Hintergrund (Flutter-Ziel)** | Ohne Foreground Service + Wake Lock würde Android die Verbindung bei App-Wechsel/Display-Sperre kappen |
|
||||
| 4.21 | **Protokoll-Parsing in eigenem Isolate (Flutter-Ziel)** | Verhindert UI-Ruckeln bei hoher Telemetrie-Rate; Isolates kommunizieren nur über Message-Passing, kein Shared Memory |
|
||||
| 4.22 | **Mission-Domain-Layer + Protokoll-Adapter als eigenständiges reines Dart-Package (Flutter-Ziel)** | Isoliert unit-testbar ohne Emulator; wiederverwendbar für z. B. CLI-Tools oder Backend-Simulatoren |
|
||||
| 4.23 | **`go_router` für Navigation (Flutter-Ziel)** | Typisierte Übergänge, passend zum expliziten Mode-Wechsel-Trigger der State-Machine |
|
||||
|
||||
---
|
||||
|
||||
## 5. Verworfene Alternativen
|
||||
|
||||
| Verworfene Option | Warum nicht gewählt |
|
||||
|---|---|
|
||||
| **Spline/Glättungskurve durch alle Wegpunkte** | Suggeriert unrealistische Kurvenradien; ersetzt durch Geraden+Kreisbögen-Modell (4.6) |
|
||||
| **Freie Linienführung beim Logo (statt Tangentenkonstruktion)** | Hätte sichtbaren Knick zwischen Gerade und Bogen erzeugt |
|
||||
| **Verjüngtes "D" als finale Logoform** | Wirkte weniger symmetrisch als gespiegeltes/geschlossenes "C" (Farbschwerpunkt-Analyse: −27,2 → −22,7 Einheiten Verbesserung) |
|
||||
| **Kombinierter 35-Variablen-Wetter-Request** | Verursachte Modellauswahl-Fehler bei Open-Meteo |
|
||||
| **Einzelner Request für das Wind-Validierungspanel** | Ebenfalls fehlgeschlagen; bestätigte die Notwendigkeit des Zwei-Request-Musters |
|
||||
| **Gekoppelter Zeilen-/Karten-Edit-Modus** | Erste Implementierung koppelte beide Modi; nach Nutzer-Feedback explizit wieder entkoppelt |
|
||||
| **Böen-Anzeige auch in der Wegpunktliste** | Als nicht aussagekräftig auf Flughöhe verworfen, in Validierungspanel belassen |
|
||||
| **`dart_mavlink`-Community-Paket unkritisch übernehmen** | Nicht offiziell gelistet, wirkt wenig gepflegt — Alternative (eigener Generator aus MAVLink-XML) als Option offengehalten, aber noch nicht entschieden |
|
||||
| **Feste Anzahl Terrain-Samples (z. B. 50)** | Ungleichmäßige Auflösung je nach Missionslänge; durch distanzbasiertes Sampling ersetzt |
|
||||
| **USB-Seriell als Transportkanal (aktuell)** | Auf iOS ohne MFi-Zertifizierung praktisch nicht machbar; für aktuellen WiFi-Bridge-Plan nicht kritisch, aber als Plattformrisiko vermerkt, falls später gewünscht |
|
||||
| **Auto-Erkennung des FC-Typs aus der Transportverbindung** | Weniger robust/eindeutig als explizites Profil-Setting (4.3) |
|
||||
| **Hive für Missions-Persistenz (ursprünglich vorgeschlagen)** | Ausreichend für aktuellen Funktionsumfang, aber keine indexierten Abfragen — zugunsten von Drift verworfen, sobald Suche/Sync-Anforderungen klar wurden |
|
||||
|
||||
---
|
||||
|
||||
## 6. Offene Fragen / TODOs
|
||||
|
||||
- **Klettersteigungsprüfung gegen tatsächliche Bogenlänge statt Geradenverbindung berechnen** — aktuell bewusste Vereinfachung, könnte bei engen Kombinationen aus Höhenänderung und scharfer Kurve leicht zu optimistisch sein.
|
||||
- **iNAV-10-Änderungen an Mission-/Wegpunktsystem** — zum Recherchezeitpunkt keine bestätigten grundlegenden Änderungen gegenüber v9 gefunden; Release-Termin (evtl. Ende 2026) und Feature-Freeze noch offen, vor App-Release erneut prüfen.
|
||||
- **Experimenteller iNAV-Community-Branch für Wegpunkt-Upload im Flug (bei nicht aktivem NAV-WP-Modus)** — Status unklar, ob in einen stabilen Release gewandert; vor Release gegen aktuelle 9.x-Firmware verifizieren, nicht darauf verlassen.
|
||||
- **`dart_mavlink`-Paket vs. eigener Generator aus MAVLink-XML-Dialektdateien** — Entscheidung für die MAVLink-Anbindung (nicht MVP-kritisch, aber für spätere ArduPilot-Unterstützung relevant) noch offen.
|
||||
- **XR1-Empfänger-Kompatibilität mit mLRS** — XR4 ist vom mLRS-Team offiziell validiert, XR1 nur wahrscheinlich kompatibel (über generisches Firmware-Target), nicht bestätigt.
|
||||
- **iOS-Hintergrundverbindung** — Konzept (Background Modes / "voip"/"location"-Capability) benannt, aber nicht MVP-relevant (MVP ist Android-only) und nicht im Detail ausgearbeitet.
|
||||
- **USB-Seriell als späterer Fallback-Transportkanal** — Plattformrisiko (iOS ohne MFi quasi unmöglich) früh klären, falls dies später doch gewünscht wird.
|
||||
- **Melos-Monorepo vs. Einzelpaket-Struktur** — konkreter Ordner-/Package-Vorschlag für das Flutter-Projekt wurde als nächster Schritt vorgeschlagen, aber noch nicht ausgearbeitet.
|
||||
- **`SurveyAreaMission` und `SearchAreaMission`** — als Missionstypen im Strategy-Pattern vorgesehen, aber inhaltlich noch nicht spezifiziert oder implementiert.
|
||||
- **Winkelannahmen der Flugpfad-Machbarkeitsprüfung (25° Schräglage, 20° Steigwinkel)** — zur Diskussion gestellt, ob diese Werte angepasst werden sollen; keine endgültige Entscheidung dokumentiert.
|
||||
- **Migration vom HTML-Demonstrator zur Flutter-App** — Reihenfolge/Umfang der Portierung (z. B. wann welches Feature 1:1 übernommen wird) ist nicht Schritt-für-Schritt festgelegt.
|
||||
|
||||
---
|
||||
|
||||
## 7. Umfangreiche Feature-Liste
|
||||
|
||||
### 7.1 Missionsplanung
|
||||
- Wegpunkt-Missionen (Track-Missionen) auf OSM-Kartenbasis (Leaflet.js im Demonstrator)
|
||||
- Wegpunkt hinzufügen/verschieben/löschen direkt auf der Karte
|
||||
- Wegpunktliste mit swipe-adjustierbaren Werten für Höhe, Geschwindigkeit, Fangradius (Catch-Radius)
|
||||
- Execute-Aktion pro Wegpunkt: —, Loiter, Landing
|
||||
- Zeilen-Edit-Modus unabhängig vom Karten-Edit-Modus
|
||||
- Tippen auf Listenzeile zentriert Karte auf diesen Wegpunkt
|
||||
- Automatisches Schließen des Edit-Modus beim Schließen der Liste
|
||||
- Höhenprofil-Anzeige der gesamten Wegpunktliste in der unteren Leiste
|
||||
- Physikalisch realistische Flugpfad-Darstellung (Geraden + Kreisbögen statt Spline)
|
||||
- Farbliche Markierung nicht machbarer Kurven (Tangentenlänge > Fangradius)
|
||||
- Klettersteigungs-/Sinkraten-Prüfung pro Streckenabschnitt
|
||||
- JUMP-Wegpunkt-Unterstützung für Patrouillen-/Loop-Missionen (iNAV Action-Code 6, mit Wiederholzähler inkl. Endlos-Option)
|
||||
- Mehrfach-Missionsverwaltung (bis zu 9 Missionen im FC speicherbar, iNAV-seitig)
|
||||
- Aktiven Wegpunkt live aus FC auslesen und in der Liste hervorheben (`MSP_NAV_STATUS`/`wp_number`)
|
||||
- Adressensuche/Geocoding (Nominatim)
|
||||
- Terrainprofil entlang der Mission (AWS Terrarium Tiles, distanzbasiertes Sampling)
|
||||
|
||||
### 7.2 Flugbetrieb (Fly-Modus)
|
||||
- Umschaltbare Flugmodi: Waypoint, Point-and-Fly, weitere abstrahiert über `MISSION_RUN`/`GUIDED_POINT`/`RETURN_HOME`/`HOLD`
|
||||
- Standard-Rückstellung auf Waypoint-Modus beim Eintritt in Fly-Modus
|
||||
- Live-Telemetrie-Anzeige (SET- vs. LIVE-Werte für Höhe/Geschwindigkeit)
|
||||
- Drehräder (Wheels) zur Live-Anpassung von Soll-Höhe/-Geschwindigkeit
|
||||
- Point-and-Fly: zentrales Fadenkreuz, Einzelziel setzen ohne bestehende Mission zu verändern
|
||||
- Terrain-Kollisionsprüfung bei PnF (Mindestabstand 60 m über Grund)
|
||||
- Persistente Zielpunkt-Beschriftung mit zum Setzzeitpunkt gültigen Werten
|
||||
- Wind-Anzeige nach Höhe interpoliert (10/80/120/180 m AGL + Druckflächendaten)
|
||||
- Wind-Validierungspanel mit Böen-Anzeige
|
||||
- Home-Button (zentriert an der oberen Bildschirmkante)
|
||||
- Maximieren-Button mit Mindestabstand zu den Drehrädern
|
||||
|
||||
### 7.3 Drohnen-Profile
|
||||
- Mehrere Drohnenprofile verwaltbar, Standardprofil T1 Ranger
|
||||
- Profilliste als Tab im Vollbild-Menü (gemeinsam mit Missionsübersicht)
|
||||
- Profildaten: min/max Speed, min. Kurvenradius, max. Steig-/Sinkrate, max. Reichweite, max. Flugdauer, Cruise Speed
|
||||
- FC-Typ als explizites Profil-Setting (MSP/MAVLink)
|
||||
|
||||
### 7.4 Konnektivität (geplant/MVP)
|
||||
- WiFi/TCP-Verbindung zum Flightcontroller (DroneBridge, mLRS-WiFi-Bridge)
|
||||
- MSP-Protokoll nativ für iNAV
|
||||
- MAVLink-Protokoll für ArduPilot (Post-MVP)
|
||||
- Feature-Capability-Abfrage je FC (`FcCapabilities`: Multi-Mission, In-Flight-Upload, max. Wegpunkte)
|
||||
- Ready-to-Fly-Gate mit Upload-/Verifikations-/Arm-Status
|
||||
|
||||
### 7.5 Persistenz & Datenhaltung
|
||||
- Lokale Speicherung von Missionen, Profilen, Einstellungen
|
||||
- Autosave/Mission-Drafts
|
||||
- Suche nach Missionen nach Name, Ort, Datum (Flutter-Ziel, Drift)
|
||||
- Vorbereitung für lokal-first Server-Sync (Dirty-Flags, Server-IDs)
|
||||
|
||||
### 7.6 Branding/Visuelles Design
|
||||
- Eigenes Monogramm-Logo (M/D/C-Konstruktion, gespiegeltes C mit Schließstrich)
|
||||
- Konsistentes Farbschema für SET/LIVE-Anzeigen (Blau/Grün)
|
||||
- Einheitliche Chip-Optik für swipe-adjustierbare Werte und Execute-Dropdown
|
||||
|
||||
### 7.7 Nicht-funktionale Eigenschaften
|
||||
- Android-Landscape-Ausrichtung (MVP)
|
||||
- Offline-Kartenkachel-Caching pro Mission (Flutter-Ziel)
|
||||
- Hintergrund-Telemetrie ohne Verbindungsabbruch bei App-Wechsel (Flutter-Ziel)
|
||||
- Testbarkeit ohne physische Hardware via Mock-FC-Link
|
||||
- Reine, UI-unabhängige Testbarkeit der Mission-Domain-Logik (separates Dart-Package, Flutter-Ziel)
|
||||
|
||||
---
|
||||
|
||||
## 8. Technologie-Stack im Überblick
|
||||
|
||||
| Bereich | Demonstrator (HTML/JS) | Flutter-Ziel |
|
||||
|---|---|---|
|
||||
| Karten | Leaflet.js + OSM-Tiles | `flutter_map` + `flutter_map_tile_caching` |
|
||||
| Wind | Open-Meteo API | Open-Meteo API |
|
||||
| Geocoding | Nominatim | Nominatim |
|
||||
| Terrain | AWS Terrarium Tiles | AWS Terrarium Tiles |
|
||||
| Persistenz | `localStorage` | Drift (SQLite) |
|
||||
| State-Management | (imperativ, Vanilla JS) | Bloc + Riverpod (kombiniert) |
|
||||
| MSP-Protokoll | eigene JS-Implementierung | eigene Dart-Implementierung |
|
||||
| MAVLink | — (nicht MVP) | `dart_mavlink` (Risiko prüfen) oder eigener Generator |
|
||||
| Hintergrundbetrieb | — | `flutter_foreground_task` (Android) |
|
||||
| Navigation | — | `go_router` |
|
||||
| Nebenläufigkeit | — | Dart Isolates für Protokoll-Parsing |
|
||||
|
||||
---
|
||||
|
||||
*Dieses Dokument fasst den Stand der bisherigen Projektkonversationen zusammen. Es wird empfohlen, es bei künftigen Architekturentscheidungen fortzuschreiben.*
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200"><path d="M100,22 L164,172 L100,138 Z" fill="#FF2E93"/><path d="M100,22 L36,172 L100,138 Z" fill="#B01464"/><path d="M100,22 L164,172 L100,138 L36,172 Z" fill="none" stroke="#fff" stroke-width="7" stroke-linejoin="round"/></svg>
|
||||
|
After Width: | Height: | Size: 314 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="k10"><circle cx="256" cy="256" r="256"/></clipPath></defs><g clip-path="url(#k10)"><rect width="512" height="512" fill="#7B2FD4"/><g transform="scale(0.644025,-0.729345) translate(-62,-702)"><path d="M857 702V0H686V421L529 0H391L233 422V0H62V702H264L461 216L656 702Z" fill="#FF2E93"/></g><g transform="translate(256,256) scale(1.461221) translate(-157.226,-104.000)"><path d="M 52.19,141.60 A 58.5,58.5 0 1 0 52.19,66.40 Z" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linejoin="round"/><path d="M 269.81,141.60 A 58.5,58.5 0 1 1 269.81,66.40" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linecap="butt"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 748 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="k11"><rect width="512" height="512" rx="112"/></clipPath></defs><g clip-path="url(#k11)"><rect width="512" height="512" fill="#7B2FD4"/><g transform="scale(0.644025,-0.729345) translate(-62,-702)"><path d="M857 702V0H686V421L529 0H391L233 422V0H62V702H264L461 216L656 702Z" fill="#FF2E93"/></g><g transform="translate(256,256) scale(1.461221) translate(-157.226,-104.000)"><path d="M 52.19,141.60 A 58.5,58.5 0 1 0 52.19,66.40 Z" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linejoin="round"/><path d="M 269.81,141.60 A 58.5,58.5 0 1 1 269.81,66.40" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linecap="butt"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 754 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="k20"><circle cx="256" cy="256" r="256"/></clipPath></defs><g clip-path="url(#k20)"><rect width="512" height="512" fill="#160E24"/><g transform="scale(0.644025,-0.729345) translate(-62,-702)"><path d="M857 702V0H686V421L529 0H391L233 422V0H62V702H264L461 216L656 702Z" fill="#FF2E93"/></g><g transform="translate(256,256) scale(1.461221) translate(-157.226,-104.000)"><path d="M 52.19,141.60 A 58.5,58.5 0 1 0 52.19,66.40 Z" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linejoin="round"/><path d="M 269.81,141.60 A 58.5,58.5 0 1 1 269.81,66.40" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linecap="butt"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 748 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="k30"><circle cx="256" cy="256" r="256"/></clipPath></defs><g clip-path="url(#k30)"><rect width="512" height="512" fill="#00D6C4"/><g transform="scale(0.644025,-0.729345) translate(-62,-702)"><path d="M857 702V0H686V421L529 0H391L233 422V0H62V702H264L461 216L656 702Z" fill="#7B2FD4"/></g><g transform="translate(256,256) scale(1.461221) translate(-157.226,-104.000)"><path d="M 52.19,141.60 A 58.5,58.5 0 1 0 52.19,66.40 Z" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linejoin="round"/><path d="M 269.81,141.60 A 58.5,58.5 0 1 1 269.81,66.40" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linecap="butt"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 748 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="k31"><rect width="512" height="512" rx="112"/></clipPath></defs><g clip-path="url(#k31)"><rect width="512" height="512" fill="#00D6C4"/><g transform="scale(0.644025,-0.729345) translate(-62,-702)"><path d="M857 702V0H686V421L529 0H391L233 422V0H62V702H264L461 216L656 702Z" fill="#7B2FD4"/></g><g transform="translate(256,256) scale(1.461221) translate(-157.226,-104.000)"><path d="M 52.19,141.60 A 58.5,58.5 0 1 0 52.19,66.40 Z" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linejoin="round"/><path d="M 269.81,141.60 A 58.5,58.5 0 1 1 269.81,66.40" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linecap="butt"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 754 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="k40"><circle cx="256" cy="256" r="256"/></clipPath></defs><g clip-path="url(#k40)"><rect width="512" height="512" fill="#FFD91C"/><g transform="scale(0.644025,-0.729345) translate(-62,-702)"><path d="M857 702V0H686V421L529 0H391L233 422V0H62V702H264L461 216L656 702Z" fill="#7B2FD4"/></g><g transform="translate(256,256) scale(1.461221) translate(-157.226,-104.000)"><path d="M 52.19,141.60 A 58.5,58.5 0 1 0 52.19,66.40 Z" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linejoin="round"/><path d="M 269.81,141.60 A 58.5,58.5 0 1 1 269.81,66.40" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linecap="butt"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 748 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="k50"><circle cx="256" cy="256" r="256"/></clipPath></defs><g clip-path="url(#k50)"><rect width="512" height="512" fill="#7B2FD4"/><g transform="scale(0.644025,-0.729345) translate(-62,-702)"><path d="M857 702V0H686V421L529 0H391L233 422V0H62V702H264L461 216L656 702Z" fill="#00D6C4"/></g><g transform="translate(256,256) scale(1.461221) translate(-157.226,-104.000)"><path d="M 52.19,141.60 A 58.5,58.5 0 1 0 52.19,66.40 Z" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linejoin="round"/><path d="M 269.81,141.60 A 58.5,58.5 0 1 1 269.81,66.40" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linecap="butt"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 748 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512"><defs><clipPath id="k60"><circle cx="256" cy="256" r="256"/></clipPath></defs><g clip-path="url(#k60)"><rect width="512" height="512" fill="#160E24"/><g transform="scale(0.644025,-0.729345) translate(-62,-702)"><path d="M857 702V0H686V421L529 0H391L233 422V0H62V702H264L461 216L656 702Z" fill="#00D6C4"/></g><g transform="translate(256,256) scale(1.461221) translate(-157.226,-104.000)"><path d="M 52.19,141.60 A 58.5,58.5 0 1 0 52.19,66.40 Z" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linejoin="round"/><path d="M 269.81,141.60 A 58.5,58.5 0 1 1 269.81,66.40" fill="none" stroke="#FFFFFF" stroke-width="49.0" stroke-linecap="butt"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 748 B |