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:
@ -14,21 +14,22 @@ struct TankManipulationPanel: View {
|
||||
let controller: ARSessionController
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 14) {
|
||||
HStack {
|
||||
Text("Tank anpassen")
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
Button {
|
||||
controller.deselectTank()
|
||||
} label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.font(.title2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel("Schließen")
|
||||
Group {
|
||||
if controller.isUndergroundTank {
|
||||
splitPanel
|
||||
} else {
|
||||
standardPanel
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(.ultraThinMaterial, in: .rect(cornerRadius: 16))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
|
||||
private var standardPanel: some View {
|
||||
VStack(spacing: 14) {
|
||||
dismissHandle
|
||||
panelTitle
|
||||
|
||||
HStack(alignment: .center, spacing: 24) {
|
||||
heightGroup
|
||||
@ -36,13 +37,111 @@ struct TankManipulationPanel: View {
|
||||
rotateGroup
|
||||
}
|
||||
|
||||
actionButtons
|
||||
}
|
||||
}
|
||||
|
||||
private var splitPanel: some View {
|
||||
VStack(spacing: 14) {
|
||||
dismissHandle
|
||||
|
||||
HStack(alignment: .top, spacing: 0) {
|
||||
VStack(spacing: 24) {
|
||||
panelTitle
|
||||
|
||||
HStack(alignment: .bottom, spacing: 24) {
|
||||
heightGroup
|
||||
moveGroup
|
||||
rotateGroup
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.horizontal, 12)
|
||||
|
||||
Divider()
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Toggle(
|
||||
"Grube anzeigen",
|
||||
isOn: Binding(
|
||||
get: { controller.showsPit },
|
||||
set: { controller.setShowsPit($0) }
|
||||
)
|
||||
)
|
||||
.toggleStyle(.switch)
|
||||
|
||||
transparencyControl
|
||||
.disabled(controller.showsPit)
|
||||
|
||||
Spacer(minLength: 0)
|
||||
|
||||
actionButtons
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.horizontal, 12)
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 10)
|
||||
}
|
||||
|
||||
private var dismissHandle: some View {
|
||||
Capsule()
|
||||
.fill(.secondary.opacity(0.7))
|
||||
.frame(width: 36, height: 4)
|
||||
.padding(.vertical, 2)
|
||||
.contentShape(Rectangle())
|
||||
.gesture(
|
||||
DragGesture(minimumDistance: 10)
|
||||
.onEnded { value in
|
||||
if value.translation.height > 60 {
|
||||
controller.deselectTank()
|
||||
}
|
||||
}
|
||||
)
|
||||
.accessibilityLabel("Zum Schließen nach unten wischen")
|
||||
}
|
||||
|
||||
private var panelTitle: some View {
|
||||
Text("Tank anpassen")
|
||||
.font(.headline)
|
||||
}
|
||||
|
||||
private var actionButtons: some View {
|
||||
HStack(spacing: 10) {
|
||||
Button("Skalierung zurücksetzen", systemImage: "arrow.counterclockwise") {
|
||||
controller.resetTankScale()
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
|
||||
Button("Neu platzieren", systemImage: "arrow.up.and.down.and.arrow.left.and.right") {
|
||||
controller.reposition()
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
}
|
||||
.padding()
|
||||
.background(.ultraThinMaterial, in: .rect(cornerRadius: 16))
|
||||
}
|
||||
|
||||
private var transparencyControl: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text("Transparenz unter Boden")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
Slider(
|
||||
value: Binding(
|
||||
get: { controller.undergroundTransparency },
|
||||
set: { controller.setUndergroundTransparency($0) }
|
||||
),
|
||||
in: 0...1
|
||||
)
|
||||
HStack {
|
||||
Text("sichtbar")
|
||||
Spacer()
|
||||
Text("transparent")
|
||||
}
|
||||
.font(.caption2)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.frame(minWidth: 180)
|
||||
}
|
||||
|
||||
// MARK: - Gruppen
|
||||
@ -79,7 +178,7 @@ struct TankManipulationPanel: View {
|
||||
}
|
||||
|
||||
private func controlGroup<Content: View>(title: String, @ViewBuilder content: () -> Content) -> some View {
|
||||
VStack(spacing: 8) {
|
||||
VStack(spacing: 10) {
|
||||
content()
|
||||
Text(title)
|
||||
.font(.caption)
|
||||
@ -113,7 +212,7 @@ private struct HoldRepeatButton: View {
|
||||
var body: some View {
|
||||
Image(systemName: systemImage)
|
||||
.font(.title3)
|
||||
.frame(width: 48, height: 48)
|
||||
.frame(width: 54, height: 54)
|
||||
.background(.ultraThinMaterial, in: .circle)
|
||||
.contentShape(.circle)
|
||||
.gesture(
|
||||
|
||||
Reference in New Issue
Block a user