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:
@ -10,16 +10,56 @@ import simd
|
||||
|
||||
/// Eine Rohrführung als geordnete Folge von Wegpunkten (Welt-Koordinaten), die zu einem
|
||||
/// zusammenhängenden Pfad verbunden werden – visualisiert die geplante Rohrverlegung.
|
||||
struct PipeRoute {
|
||||
struct PipeRoute: Codable {
|
||||
private(set) var waypoints: [SIMD3<Float>] = []
|
||||
private(set) var removedSegmentIndices: Set<Int> = []
|
||||
|
||||
var count: Int { waypoints.count }
|
||||
|
||||
/// Gesamtlänge des Pfads über alle Segmente in Metern.
|
||||
/// Gesamtlänge des Pfads über alle aktiven Segmente in Metern.
|
||||
var totalLength: Float {
|
||||
guard waypoints.count > 1 else { return 0 }
|
||||
return zip(waypoints, waypoints.dropFirst())
|
||||
.reduce(0) { $0 + simd_distance($1.0, $1.1) }
|
||||
.enumerated()
|
||||
.filter { !removedSegmentIndices.contains($0.offset) }
|
||||
.reduce(0) { $0 + simd_distance($1.element.0, $1.element.1) }
|
||||
}
|
||||
|
||||
func isSegmentRemoved(at index: Int) -> Bool {
|
||||
removedSegmentIndices.contains(index)
|
||||
}
|
||||
|
||||
mutating func removeSegmentAndOrphanedWaypoints(at index: Int) {
|
||||
guard index >= 0, index < waypoints.count - 1 else { return }
|
||||
removedSegmentIndices.insert(index)
|
||||
|
||||
var connected = Array(repeating: false, count: waypoints.count)
|
||||
for segmentIndex in 0..<(waypoints.count - 1)
|
||||
where !removedSegmentIndices.contains(segmentIndex) {
|
||||
connected[segmentIndex] = true
|
||||
connected[segmentIndex + 1] = true
|
||||
}
|
||||
|
||||
let oldToNew = connected.enumerated().reduce(into: [Int: Int]()) { result, item in
|
||||
if item.element {
|
||||
result[item.offset] = result.count
|
||||
}
|
||||
}
|
||||
|
||||
let oldRemovedSegments = removedSegmentIndices
|
||||
waypoints = waypoints.enumerated()
|
||||
.filter { connected[$0.offset] }
|
||||
.map(\.element)
|
||||
|
||||
removedSegmentIndices = Set(
|
||||
oldRemovedSegments.compactMap { oldIndex in
|
||||
guard let newIndex = oldToNew[oldIndex],
|
||||
oldToNew[oldIndex + 1] != nil else {
|
||||
return nil
|
||||
}
|
||||
return newIndex
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
var formattedLength: String {
|
||||
@ -32,11 +72,20 @@ struct PipeRoute {
|
||||
|
||||
@discardableResult
|
||||
mutating func removeLast() -> SIMD3<Float>? {
|
||||
waypoints.isEmpty ? nil : waypoints.removeLast()
|
||||
guard !waypoints.isEmpty else { return nil }
|
||||
let removedIndex = waypoints.count - 2
|
||||
removedSegmentIndices.remove(removedIndex)
|
||||
removedSegmentIndices = Set(
|
||||
removedSegmentIndices.map { index in
|
||||
index > removedIndex ? index - 1 : index
|
||||
}
|
||||
)
|
||||
return waypoints.removeLast()
|
||||
}
|
||||
|
||||
mutating func clear() {
|
||||
waypoints.removeAll()
|
||||
removedSegmentIndices.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user