Initial commit for first version of project
This commit is contained in:
81
GasTankAR/Model/Project.swift
Normal file
81
GasTankAR/Model/Project.swift
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// Project.swift
|
||||
// GasTankAR
|
||||
//
|
||||
// Created by Dennis Nemec on 25.08.26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftData
|
||||
import ARKit
|
||||
|
||||
@Model
|
||||
final class Project {
|
||||
/// Identifier given by the user for the project. In DOCUframe terms
|
||||
/// it is the LeadID.
|
||||
var leadID: String
|
||||
|
||||
/// Customer name to link the project to.
|
||||
var customerName: String
|
||||
|
||||
var creationDate: Date
|
||||
|
||||
/// Der für das Projekt ausgewählte Tank. Jedes Projekt hat genau einen Tank.
|
||||
/// `Tank` ist `Codable`, daher persistiert SwiftData das direkt als Attribut.
|
||||
var tank: Tank
|
||||
|
||||
/// Archivierte `ARWorldMap` (via `NSKeyedArchiver`). Wegen der Größe extern gespeichert.
|
||||
@Attribute(.externalStorage) var worldMapData: Data?
|
||||
|
||||
/// Welt-Transform des platzierten Tanks (Position, Rotation, Skalierung) als
|
||||
/// 16 Floats (spaltenweise). Zusammen mit der WorldMap wird der Tank so wiederhergestellt.
|
||||
var tankTransform: [Float]?
|
||||
|
||||
/// Im AR-Modus aufgenommene Fotos. Werden beim Löschen des Projekts mitentfernt.
|
||||
@Relationship(deleteRule: .cascade, inverse: \ProjectPhoto.project)
|
||||
var photos: [ProjectPhoto] = []
|
||||
|
||||
init(
|
||||
leadID: String,
|
||||
customerName: String,
|
||||
creationDate: Date,
|
||||
tank: Tank = Tank(),
|
||||
worldMapData: Data? = nil,
|
||||
tankTransform: [Float]? = nil
|
||||
) {
|
||||
self.leadID = leadID
|
||||
self.customerName = customerName
|
||||
self.creationDate = creationDate
|
||||
self.tank = tank
|
||||
self.worldMapData = worldMapData
|
||||
self.tankTransform = tankTransform
|
||||
}
|
||||
|
||||
/// Human-readable title combining the lead identifier and customer name.
|
||||
var title: String {
|
||||
"\(leadID) – \(customerName)"
|
||||
}
|
||||
|
||||
/// Up to two uppercased initials derived from the customer name, for use as an avatar.
|
||||
var initials: String {
|
||||
let letters = customerName
|
||||
.split(whereSeparator: { $0 == " " || $0 == "&" })
|
||||
.prefix(2)
|
||||
.compactMap { $0.first }
|
||||
return String(letters).uppercased()
|
||||
}
|
||||
|
||||
/// Gespeicherte Session der getrackten Welt. (De-)serialisiert `worldMapData`.
|
||||
/// Berechnete Property – wird von SwiftData nicht persistiert.
|
||||
var worldMap: ARWorldMap? {
|
||||
get {
|
||||
guard let worldMapData else { return nil }
|
||||
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: ARWorldMap.self, from: worldMapData)
|
||||
}
|
||||
set {
|
||||
worldMapData = newValue.flatMap {
|
||||
try? NSKeyedArchiver.archivedData(withRootObject: $0, requiringSecureCoding: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
GasTankAR/Model/ProjectPhoto.swift
Normal file
49
GasTankAR/Model/ProjectPhoto.swift
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// ProjectPhoto.swift
|
||||
// GasTankAR
|
||||
//
|
||||
// Created by Dennis Nemec on 27.08.26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftData
|
||||
import CoreLocation
|
||||
|
||||
/// Ein im AR-Modus aufgenommenes Foto (Snapshot der Kamera-Sicht), das zu einem Projekt gehört.
|
||||
/// Die Bilddaten liegen wegen ihrer Größe extern (`.externalStorage`).
|
||||
@Model
|
||||
final class ProjectPhoto {
|
||||
/// Aufnahmezeitpunkt – Grundlage für die Gruppierung in der Galerie.
|
||||
var creationDate: Date
|
||||
|
||||
/// JPEG-kodierte Bilddaten.
|
||||
@Attribute(.externalStorage) var imageData: 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.
|
||||
var longitude: Double?
|
||||
|
||||
/// Zugehöriges Projekt (Rückbeziehung zu `Project.photos`).
|
||||
var project: Project?
|
||||
|
||||
init(
|
||||
creationDate: Date,
|
||||
imageData: Data,
|
||||
latitude: Double? = nil,
|
||||
longitude: Double? = nil,
|
||||
project: Project? = nil
|
||||
) {
|
||||
self.creationDate = creationDate
|
||||
self.imageData = imageData
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.project = project
|
||||
}
|
||||
|
||||
/// Aufnahmeort als Koordinate – `nil`, wenn kein Standort gespeichert wurde.
|
||||
var coordinate: CLLocationCoordinate2D? {
|
||||
guard let latitude, let longitude else { return nil }
|
||||
return CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
|
||||
}
|
||||
}
|
||||
80
GasTankAR/Model/Tank.swift
Normal file
80
GasTankAR/Model/Tank.swift
Normal file
@ -0,0 +1,80 @@
|
||||
//
|
||||
// Tank.swift
|
||||
// GasTankAR
|
||||
//
|
||||
// Created by Dennis Nemec on 25.08.26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Verfügbare Tankgrößen laut Projektplan Phase 4 (Modell + korrekter Maßstab).
|
||||
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 {
|
||||
case white
|
||||
case green
|
||||
|
||||
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 {
|
||||
case aboveGround
|
||||
case belowGround
|
||||
|
||||
var id: String { rawValue }
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .aboveGround: "Oberirdisch"
|
||||
case .belowGround: "Unterirdisch"
|
||||
}
|
||||
}
|
||||
|
||||
/// Name des Asset-Icons für diese Aufstellungsart.
|
||||
var imageName: String {
|
||||
switch self {
|
||||
case .aboveGround: "tank-upper"
|
||||
case .belowGround: "tank-lower"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
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)"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user