Initial commit for first version of project

This commit is contained in:
Dennis Nemec
2026-09-02 20:54:36 +02:00
parent 3d9f1ab6bc
commit 992bb32025
56 changed files with 5139 additions and 31 deletions

View File

@ -0,0 +1,98 @@
//
// MeasurementEntityFactory.swift
// GasTankAR
//
// Created by Dennis Nemec on 28.08.26.
//
import RealityKit
import UIKit
/// Baut die AR-Visualisierung einer Strecke: zwei Endpunkt-Kugeln, die verbindende Linie
/// sowie ein Text-Label (Bezeichnung + Länge) am Mittelpunkt der Strecke.
///
/// Enthält außerdem wiederverwendbare Bausteine für die *Live*-Vorschau während des Messens
/// (Cursor-Kugel, skalierbare Linie, aktualisierbares Label).
@MainActor
enum MeasurementEntityFactory {
/// Name des Label-Halters – wird zum Wiederfinden fürs Billboarding genutzt.
static let labelName = "MeasurementLabel"
private static let color = UIColor.systemYellow
private static let pointRadius: Float = 0.012
private static let lineThickness: Float = 0.006
private static let labelHeight: Float = 0.06
private static let labelYOffset: Float = 0.03
/// Kleine Kugel für einen (auch vorläufigen) Messpunkt.
static func makePoint(at position: SIMD3<Float>) -> ModelEntity {
let sphere = ModelEntity(
mesh: .generateSphere(radius: pointRadius),
materials: [UnlitMaterial(color: color)]
)
sphere.position = position
return sphere
}
/// Vollständige Strecken-Visualisierung (Punkte + Linie + Label).
static func makeMeasurement(_ measurement: Measurement) -> Entity {
let container = Entity()
container.name = "Measurement-\(measurement.id.uuidString)"
container.addChild(makePoint(at: measurement.start))
container.addChild(makePoint(at: measurement.end))
let line = makeLineBase()
configureLine(line, from: measurement.start, to: measurement.end)
container.addChild(line)
let label = makeLabelHolder()
label.position = labelPosition(from: measurement.start, to: measurement.end)
label.addChild(makeText("\(measurement.name) (\(measurement.formattedDistance))"))
container.addChild(label)
return container
}
/// Basis-Linie der Länge 1 m (entlang +Z). Für die Live-Linie wird sie via ``configureLine`` skaliert/rotiert.
static func makeLineBase() -> ModelEntity {
ModelEntity(
mesh: .generateBox(size: SIMD3(lineThickness, lineThickness, 1)),
materials: [UnlitMaterial(color: color)]
)
}
/// Richtet eine per ``makeLineBase`` erzeugte Linie zwischen zwei Punkten aus (Länge über Z-Skalierung).
static func configureLine(_ line: Entity, from start: SIMD3<Float>, to end: SIMD3<Float>) {
let length = max(simd_distance(start, end), 0.0001)
line.transform = Transform(
scale: SIMD3(1, 1, length),
rotation: simd_quatf(from: SIMD3(0, 0, 1), to: normalize(end - start)),
translation: (start + end) / 2
)
}
/// Leerer Label-Halter (Billboard-Anker). Der Text wird als zentriertes Kind eingehängt.
static func makeLabelHolder() -> Entity {
let holder = Entity()
holder.name = labelName
return holder
}
/// Zentriertes Text-Entity für einen Label-Halter.
static func makeText(_ string: String) -> ModelEntity {
let mesh = MeshResource.generateText(
string,
extrusionDepth: 0.001,
font: .systemFont(ofSize: CGFloat(labelHeight), weight: .semibold)
)
var material = UnlitMaterial(color: .white)
material.faceCulling = .none
let text = ModelEntity(mesh: mesh, materials: [material])
text.position = -text.visualBounds(relativeTo: nil).center
return text
}
/// Position des Labels (Streckenmitte, leicht angehoben).
static func labelPosition(from start: SIMD3<Float>, to end: SIMD3<Float>) -> SIMD3<Float> {
(start + end) / 2 + SIMD3(0, labelYOffset, 0)
}
}