Add tank catalog with USDZ models and tank selection
Introduce TankDefinition/TankCatalog loaded from Tanks.json, bundle the Drachengas USDZ tank models, add tank selection and preview views, and update the AR experience, editor and project views to use the catalog. Includes tests for catalog loading and model selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@ -31,6 +31,12 @@ final class Project {
|
||||
/// 16 Floats (spaltenweise). Zusammen mit der WorldMap wird der Tank so wiederhergestellt.
|
||||
var tankTransform: [Float]?
|
||||
|
||||
/// Persistierte, frei eingetragene Strecken als Codable-Daten.
|
||||
@Attribute(.externalStorage) var measurementsData: Data?
|
||||
|
||||
/// Persistierte Rohr-Wegpunkte und ihre Verbindungen als Codable-Daten.
|
||||
@Attribute(.externalStorage) var pipeRouteData: Data?
|
||||
|
||||
/// Im AR-Modus aufgenommene Fotos. Werden beim Löschen des Projekts mitentfernt.
|
||||
@Relationship(deleteRule: .cascade, inverse: \ProjectPhoto.project)
|
||||
var photos: [ProjectPhoto] = []
|
||||
@ -41,7 +47,9 @@ final class Project {
|
||||
creationDate: Date,
|
||||
tank: Tank = Tank(),
|
||||
worldMapData: Data? = nil,
|
||||
tankTransform: [Float]? = nil
|
||||
tankTransform: [Float]? = nil,
|
||||
measurementsData: Data? = nil,
|
||||
pipeRouteData: Data? = nil
|
||||
) {
|
||||
self.leadID = leadID
|
||||
self.customerName = customerName
|
||||
@ -49,6 +57,8 @@ final class Project {
|
||||
self.tank = tank
|
||||
self.worldMapData = worldMapData
|
||||
self.tankTransform = tankTransform
|
||||
self.measurementsData = measurementsData
|
||||
self.pipeRouteData = pipeRouteData
|
||||
}
|
||||
|
||||
/// Human-readable title combining the lead identifier and customer name.
|
||||
|
||||
@ -16,9 +16,15 @@ final class ProjectPhoto {
|
||||
/// Aufnahmezeitpunkt – Grundlage für die Gruppierung in der Galerie.
|
||||
var creationDate: Date
|
||||
|
||||
/// JPEG-kodierte Bilddaten.
|
||||
/// Aktuelle Bilddaten. Nach einer Bearbeitung enthält diese Property die gerenderte PNG-Datei.
|
||||
@Attribute(.externalStorage) var imageData: Data
|
||||
|
||||
/// Unveränderte Originaldaten für die Funktion „Zeichnung zurücksetzen“.
|
||||
@Attribute(.externalStorage) var originalImageData: Data?
|
||||
|
||||
/// Gespeicherte Version des Fotos mit Zeichnung.
|
||||
@Attribute(.externalStorage) var annotatedImageData: Data?
|
||||
|
||||
/// Geografische Breite des Aufnahmeorts, sofern beim Aufnehmen ein Standort vorlag.
|
||||
var latitude: Double?
|
||||
/// Geografische Länge des Aufnahmeorts, sofern beim Aufnehmen ein Standort vorlag.
|
||||
@ -30,12 +36,16 @@ final class ProjectPhoto {
|
||||
init(
|
||||
creationDate: Date,
|
||||
imageData: Data,
|
||||
originalImageData: Data? = nil,
|
||||
annotatedImageData: Data? = nil,
|
||||
latitude: Double? = nil,
|
||||
longitude: Double? = nil,
|
||||
project: Project? = nil
|
||||
) {
|
||||
self.creationDate = creationDate
|
||||
self.imageData = imageData
|
||||
self.originalImageData = originalImageData
|
||||
self.annotatedImageData = annotatedImageData
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.project = project
|
||||
|
||||
@ -7,40 +7,30 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Verfügbare Tankgrößen laut Projektplan Phase 4 (Modell + korrekter Maßstab).
|
||||
enum TankSize: String, CaseIterable, Identifiable, Codable, Hashable {
|
||||
/// Verfügbare Tankgrößen. Die Enum-Fälle sind reine, stabile Identitäten
|
||||
/// (werden so persistiert). Anzeigenamen und 3D-Modelle liefert der
|
||||
/// `TankCatalog` (aus `Tanks.json`) – siehe `TankDefinition`.
|
||||
nonisolated enum TankSize: String, CaseIterable, Identifiable, Codable, Hashable {
|
||||
case t1_2
|
||||
case t2_1
|
||||
case t2_9
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .t1_2: "1,2 t"
|
||||
case .t2_1: "2,1 t"
|
||||
case .t2_9: "2,9 t"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Verfügbare Tankfarben (Farbvariante mit Logo).
|
||||
enum TankColor: String, CaseIterable, Identifiable, Codable, Hashable {
|
||||
/// Verfügbare Tankfarben. Welche Farben tatsächlich auswählbar sind, ergibt
|
||||
/// sich dynamisch aus dem `TankCatalog` (abhängig von Größe & Aufstellungsart).
|
||||
nonisolated enum TankColor: String, CaseIterable, Identifiable, Codable, Hashable {
|
||||
case white
|
||||
case green
|
||||
case olive
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .white: "Weiß"
|
||||
case .green: "Grün"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Aufstellungsart des Tanks.
|
||||
enum TankPlacement: String, CaseIterable, Identifiable, Codable, Hashable {
|
||||
/// Aufstellungsart des Tanks. Steuert neben der Modellauswahl auch das
|
||||
/// AR-Verhalten (Grube, Occlusion, „nur Deckel").
|
||||
nonisolated enum TankPlacement: String, CaseIterable, Identifiable, Codable, Hashable {
|
||||
case aboveGround
|
||||
case belowGround
|
||||
|
||||
@ -62,19 +52,14 @@ enum TankPlacement: String, CaseIterable, Identifiable, Codable, Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
/// Ein für ein Projekt ausgewählter Tank. Kombiniert die in Phase 4 des
|
||||
/// Projektplans vorgesehenen Auswahlmöglichkeiten: Größe, Farbe und
|
||||
/// Aufstellungsart sowie die optionale halbtransparente Darstellung.
|
||||
struct Tank: Hashable, Codable {
|
||||
/// Die für ein Projekt getroffene Tank-Auswahl. `Tank` ist die persistierte
|
||||
/// Identität (Größe, Farbe, Aufstellungsart); die zugehörigen Anzeigenamen und
|
||||
/// das 3D-Modell werden über den `TankCatalog` aufgelöst.
|
||||
nonisolated struct Tank: Hashable, Codable {
|
||||
var size: TankSize = .t1_2
|
||||
var color: TankColor = .white
|
||||
var placement: TankPlacement = .aboveGround
|
||||
|
||||
/// Optionale Darstellung, bei der nur der Deckel sichtbar ist.
|
||||
var showLidOnly: Bool = false
|
||||
|
||||
/// Kurzbeschreibung für Listen und Detailanzeigen.
|
||||
var displayName: String {
|
||||
"\(size.displayName) · \(color.displayName) · \(placement.displayName)"
|
||||
}
|
||||
}
|
||||
|
||||
38
GasTankAR/Model/TankDefinition.swift
Normal file
38
GasTankAR/Model/TankDefinition.swift
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// TankDefinition.swift
|
||||
// GasTankAR
|
||||
//
|
||||
// Created by Dennis Nemec on 14.09.26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Eine im Katalog (`Tanks.json`) hinterlegte Tank-Variante. Verbindet die
|
||||
/// Identität eines Tanks (Größe, Farbe, Aufstellungsart) mit dem Pfad zum
|
||||
/// 3D-Modell. Die Anzeigenamen werden separat gepflegt (siehe `TankCatalog`).
|
||||
nonisolated struct TankDefinition: Codable, Hashable, Identifiable {
|
||||
let size: TankSize
|
||||
let color: TankColor
|
||||
let placement: TankPlacement
|
||||
/// Pfad bzw. Dateiname des 3D-Modells im App-Bundle (z. B. `DG-2700_wh.usdz`).
|
||||
let modelPath: String
|
||||
|
||||
/// Stabile Identität aus der Kombination der Auswahlmerkmale.
|
||||
var id: String { "\(size.rawValue)_\(color.rawValue)_\(placement.rawValue)" }
|
||||
|
||||
/// Ressourcenname ohne Dateiendung, wie ihn RealityKit/SceneKit zum Laden
|
||||
/// aus dem Bundle erwarten (z. B. `DG-2700_wh`).
|
||||
var modelResourceName: String {
|
||||
(modelPath as NSString).deletingPathExtension
|
||||
}
|
||||
|
||||
/// Dateiendung des Modells (z. B. `usdz`).
|
||||
var modelFileExtension: String {
|
||||
(modelPath as NSString).pathExtension
|
||||
}
|
||||
|
||||
/// Entspricht diese Definition der getroffenen Tank-Auswahl?
|
||||
func matches(_ tank: Tank) -> Bool {
|
||||
size == tank.size && color == tank.color && placement == tank.placement
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user