Initial commit for first version of project
This commit is contained in:
98
GasTankAR/Views/TankAR/MeasurementEntityFactory.swift
Normal file
98
GasTankAR/Views/TankAR/MeasurementEntityFactory.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user