@webspatial/platform-visionos 1.7.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/web-spatial/JSBCommand.swift +1 -0
- package/web-spatial/WebMsgCommand.swift +26 -2
- package/web-spatial/WebSpatialApp.swift +26 -8
- package/web-spatial/manifest.swift +1 -1
- package/web-spatial/model/SpatialScene.swift +178 -39
- package/web-spatial/model/Spatialized2DElement.swift +2 -11
- package/web-spatial/model/SpatializedElement.swift +38 -0
- package/web-spatial/model/SpatializedStatic3DElement.swift +27 -8
- package/web-spatial/model/dynamic3d/EntityAnimationManager.swift +356 -0
- package/web-spatial/model/dynamic3d/EntityAnimationSession.swift +162 -0
- package/web-spatial/model/element-motion/SpatializedElementAnimationCommand.swift +15 -0
- package/web-spatial/model/element-motion/SpatializedElementAnimationManager.swift +263 -0
- package/web-spatial/model/element-motion/SpatializedElementAnimationObject.swift +399 -0
- package/web-spatial/model/element-motion/SpatializedElementAnimationTypes.swift +45 -0
- package/web-spatial/model/element-motion/SpatializedElementAnimationWriteAdapter.swift +65 -0
- package/web-spatial/model/element-motion/SpatializedElementMotionBridgeTypes.swift +43 -0
- package/web-spatial/model/element-motion/SpatializedElementMotionTimelineSampler.swift +99 -0
- package/web-spatial/model/element-motion/SpatializedElementMotionTiming.swift +99 -0
- package/web-spatial/model/element-motion/SpatializedElementMotionTransformTypes.swift +22 -0
- package/web-spatial/protocol/SpatializedElementContainer.swift +1 -1
- package/web-spatial/view/SpatialNavView.swift +37 -12
- package/web-spatial/view/SpatialSceneContentView.swift +9 -12
- package/web-spatial/view/Spatialized2DElementView.swift +10 -17
- package/web-spatial/view/SpatializedDynamic3DView.swift +2 -6
- package/web-spatial/view/SpatializedElementView.swift +3 -2
- package/web-spatial/view/SpatializedStatic3DView.swift +32 -33
- package/web-spatial/view/view-modifier/OrbitModifier.swift +84 -0
- package/web-spatial/webview/SpatialWebController.swift +26 -18
- package/web-spatial/webview/SpatialWebViewModel.swift +12 -7
- package/web-spatial.xcodeproj/project.pbxproj +29 -8
- package/web-spatial.xcodeproj/xcshareddata/xcschemes/web-spatial.xcscheme +1 -1
- package/web-spatialTests/NavigationCleanupTests.swift +2 -1
- package/web-spatialTests/SpatializedElementAnimationManagerTests.swift +1466 -0
- package/web-spatialTests/SpatializedElementMotionTimelineSamplerTests.swift +117 -0
|
@@ -2,31 +2,31 @@ import RealityKit
|
|
|
2
2
|
import SwiftUI
|
|
3
3
|
|
|
4
4
|
struct SpatializedStatic3DView: View {
|
|
5
|
-
@
|
|
5
|
+
@Bindable var spatializedStatic3DElement: SpatializedStatic3DElement
|
|
6
6
|
@Environment(SpatialScene.self) var spatialScene: SpatialScene
|
|
7
7
|
|
|
8
8
|
@State private var loadState: LoadState = .idle
|
|
9
9
|
|
|
10
|
-
private var spatializedStatic3DElement: SpatializedStatic3DElement {
|
|
11
|
-
return spatializedElement as! SpatializedStatic3DElement
|
|
12
|
-
}
|
|
13
|
-
|
|
14
10
|
private var asset: Model3DAsset? {
|
|
15
11
|
if case let .loaded(asset, _) = loadState { return asset }
|
|
16
12
|
return nil
|
|
17
13
|
}
|
|
18
14
|
|
|
19
15
|
func onLoadSuccess(src: String) {
|
|
20
|
-
spatialScene.sendWebMsg(
|
|
16
|
+
spatialScene.sendWebMsg(spatializedStatic3DElement.id, ModelLoadSuccess(src: src))
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
func onLoadFailure() {
|
|
24
|
-
spatialScene.sendWebMsg(
|
|
20
|
+
spatialScene.sendWebMsg(spatializedStatic3DElement.id, ModelLoadFailure())
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private func onOrbit(_ transform: AffineTransform3D) {
|
|
24
|
+
spatialScene.sendWebMsg(spatializedStatic3DElement.id, EntityTransformChangeEvent(transform))
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
var body: some View {
|
|
28
|
-
let depth =
|
|
29
|
-
let transform = spatializedStatic3DElement.
|
|
28
|
+
let depth = spatializedStatic3DElement.depth
|
|
29
|
+
let transform = spatializedStatic3DElement.entityTransform
|
|
30
30
|
let translation = transform.translation
|
|
31
31
|
let scale = transform.scale
|
|
32
32
|
let rotation = transform.rotation!
|
|
@@ -34,8 +34,8 @@ struct SpatializedStatic3DView: View {
|
|
|
34
34
|
let y = translation.y
|
|
35
35
|
let z = translation.z
|
|
36
36
|
|
|
37
|
-
let enableGesture =
|
|
38
|
-
if
|
|
37
|
+
let enableGesture = spatializedStatic3DElement.enableGesture
|
|
38
|
+
if spatializedStatic3DElement.loading == .eager {
|
|
39
39
|
Group {
|
|
40
40
|
switch loadState {
|
|
41
41
|
case .idle, .loading:
|
|
@@ -44,24 +44,23 @@ struct SpatializedStatic3DView: View {
|
|
|
44
44
|
Model3D(asset: asset) { resolvedModel3D in
|
|
45
45
|
resolvedModel3D
|
|
46
46
|
.resizable(true)
|
|
47
|
-
.aspectRatio(
|
|
48
|
-
nil,
|
|
49
|
-
contentMode: .fit
|
|
50
|
-
)
|
|
47
|
+
.aspectRatio(nil, contentMode: .fit)
|
|
51
48
|
.if(!depth.isZero) { view in view.scaledToFit3D() }
|
|
52
49
|
.if(enableGesture) { view in view.hoverEffect() }
|
|
53
50
|
}
|
|
54
51
|
case .failed:
|
|
55
|
-
posterView {
|
|
52
|
+
posterView {
|
|
53
|
+
// Transparent view is required so that lifecycle modifiers like .task still work
|
|
54
|
+
Color.clear
|
|
55
|
+
}
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
.scaleEffect(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
rotation
|
|
58
|
+
.scaleEffect(scale)
|
|
59
|
+
.rotation3DEffect(rotation)
|
|
60
|
+
.orbit(
|
|
61
|
+
enabled: spatializedStatic3DElement.stagemode == .orbit && asset != nil,
|
|
62
|
+
entityTransform: $spatializedStatic3DElement.entityTransform,
|
|
63
|
+
onChange: onOrbit
|
|
65
64
|
)
|
|
66
65
|
.offset(x: x, y: y)
|
|
67
66
|
.offset(z: z)
|
|
@@ -80,7 +79,9 @@ struct SpatializedStatic3DView: View {
|
|
|
80
79
|
}
|
|
81
80
|
}
|
|
82
81
|
.onChange(of: spatializedStatic3DElement.animationPaused) { onPlayback(isPaused: $1) }
|
|
83
|
-
.onChange(of: spatializedStatic3DElement.playbackRate) {
|
|
82
|
+
.onChange(of: spatializedStatic3DElement.playbackRate) {
|
|
83
|
+
asset?.animationPlaybackController?.speed = spatializedStatic3DElement.animationPaused ? 0 : Float($1)
|
|
84
|
+
}
|
|
84
85
|
.onChange(of: spatializedStatic3DElement.pendingSeekTime) { _, time in onSeek(time: time) }
|
|
85
86
|
.task(id: spatializedStatic3DElement.allSources) { await loadSources() }
|
|
86
87
|
} else {
|
|
@@ -122,7 +123,7 @@ struct SpatializedStatic3DView: View {
|
|
|
122
123
|
asset.selectedAnimation = asset.availableAnimations.first
|
|
123
124
|
}
|
|
124
125
|
let controller = asset.animationPlaybackController
|
|
125
|
-
controller?.speed = Float(spatializedStatic3DElement.playbackRate)
|
|
126
|
+
controller?.speed = isPaused ? 0 : Float(spatializedStatic3DElement.playbackRate)
|
|
126
127
|
if let time = spatializedStatic3DElement.pendingSeekTime, let controller {
|
|
127
128
|
controller.time = time
|
|
128
129
|
spatializedStatic3DElement.pendingSeekTime = nil
|
|
@@ -149,7 +150,7 @@ struct SpatializedStatic3DView: View {
|
|
|
149
150
|
let duration = controller?.duration ?? 0
|
|
150
151
|
let currentTime = controller?.time ?? 0
|
|
151
152
|
spatialScene.sendWebMsg(
|
|
152
|
-
|
|
153
|
+
spatializedStatic3DElement.id,
|
|
153
154
|
AnimationStateChangeEvent(
|
|
154
155
|
detail: AnimationStateChangeDetail(
|
|
155
156
|
paused: isPaused,
|
|
@@ -183,13 +184,11 @@ struct SpatializedStatic3DView: View {
|
|
|
183
184
|
if let result {
|
|
184
185
|
loadState = .loaded(result.asset, result.url.absoluteString)
|
|
185
186
|
onLoadSuccess(src: result.url.absoluteString)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
} else { onPlayback(isPaused: false) }
|
|
192
|
-
}
|
|
187
|
+
// If animationPaused didn't change then SwiftUI will not trigger onChange so manually trigger playback
|
|
188
|
+
// This happens when play is called before load and autoplay is enabled
|
|
189
|
+
if spatializedStatic3DElement.autoplay, spatializedStatic3DElement.animationPaused {
|
|
190
|
+
spatializedStatic3DElement.animationPaused = false
|
|
191
|
+
} else { onPlayback(isPaused: !spatializedStatic3DElement.autoplay) }
|
|
193
192
|
} else {
|
|
194
193
|
loadState = .failed
|
|
195
194
|
onLoadFailure()
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import SwiftUI
|
|
2
|
+
|
|
3
|
+
private let degreesPerPoint = 0.5
|
|
4
|
+
private let pitchLimitDegrees = 100.0
|
|
5
|
+
private let springBack = Animation.spring(response: 0.45, dampingFraction: 0.7)
|
|
6
|
+
|
|
7
|
+
/// Adds an orbital camera-style rotation gesture to a 3D model.
|
|
8
|
+
///
|
|
9
|
+
/// Both horizontal yaw (around the Y-axis) and vertical pitch (around the
|
|
10
|
+
/// X-axis, clamped to ±100° so the model can't flip past upright) are folded
|
|
11
|
+
/// directly into the bound `entityTransform` on each drag tick, so the web
|
|
12
|
+
/// layer always observes an accurate transform. On release, the pitch
|
|
13
|
+
/// elastically springs back to 0: the data is set to the unpitched final state
|
|
14
|
+
/// immediately while SwiftUI animates the existing `.rotation3DEffect` from the
|
|
15
|
+
/// old (pitched) rotation to the new (unpitched) rotation for a smooth visual.
|
|
16
|
+
struct OrbitModifier: ViewModifier {
|
|
17
|
+
let enabled: Bool
|
|
18
|
+
@Binding var entityTransform: AffineTransform3D
|
|
19
|
+
let onChange: (AffineTransform3D) -> Void
|
|
20
|
+
|
|
21
|
+
@State private var dragStartTransform: AffineTransform3D?
|
|
22
|
+
@State private var dragYawDegrees: Double = 0
|
|
23
|
+
|
|
24
|
+
func body(content: Content) -> some View {
|
|
25
|
+
content
|
|
26
|
+
.simultaneousGesture(enabled ? orbitGesture : nil)
|
|
27
|
+
.onChange(of: enabled) { _, isEnabled in
|
|
28
|
+
if !isEnabled { releaseElastic() }
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private var orbitGesture: some Gesture {
|
|
33
|
+
DragGesture(minimumDistance: 0)
|
|
34
|
+
.onChanged { value in
|
|
35
|
+
let base = dragStartTransform ?? entityTransform
|
|
36
|
+
if dragStartTransform == nil { dragStartTransform = base }
|
|
37
|
+
|
|
38
|
+
dragYawDegrees = Double(value.translation.width) * degreesPerPoint
|
|
39
|
+
let pitchTarget = Double(-value.translation.height) * degreesPerPoint
|
|
40
|
+
let pitchDegrees = max(-pitchLimitDegrees, min(pitchLimitDegrees, pitchTarget))
|
|
41
|
+
|
|
42
|
+
let new = compose(base: base, yawDegrees: dragYawDegrees, pitchDegrees: pitchDegrees)
|
|
43
|
+
entityTransform = new
|
|
44
|
+
onChange(new)
|
|
45
|
+
}
|
|
46
|
+
.onEnded { _ in releaseElastic() }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private func releaseElastic() {
|
|
50
|
+
guard let base = dragStartTransform else { return }
|
|
51
|
+
let unpitched = compose(base: base, yawDegrees: dragYawDegrees, pitchDegrees: 0)
|
|
52
|
+
withAnimation(springBack) { entityTransform = unpitched }
|
|
53
|
+
onChange(unpitched)
|
|
54
|
+
dragStartTransform = nil
|
|
55
|
+
dragYawDegrees = 0
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private func compose(
|
|
59
|
+
base: AffineTransform3D,
|
|
60
|
+
yawDegrees: Double,
|
|
61
|
+
pitchDegrees: Double
|
|
62
|
+
) -> AffineTransform3D {
|
|
63
|
+
let translation = AffineTransform3D(translation: base.translation)
|
|
64
|
+
let yaw = AffineTransform3D(rotation: Rotation3D(angle: .degrees(yawDegrees), axis: .y))
|
|
65
|
+
let pitch = AffineTransform3D(rotation: Rotation3D(angle: .degrees(pitchDegrees), axis: .x))
|
|
66
|
+
let baseRotation = AffineTransform3D(rotation: base.rotation ?? .identity)
|
|
67
|
+
let scale = AffineTransform3D(scale: base.scale)
|
|
68
|
+
return translation
|
|
69
|
+
.concatenating(yaw)
|
|
70
|
+
.concatenating(pitch)
|
|
71
|
+
.concatenating(baseRotation)
|
|
72
|
+
.concatenating(scale)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
extension View {
|
|
77
|
+
func orbit(
|
|
78
|
+
enabled: Bool,
|
|
79
|
+
entityTransform: Binding<AffineTransform3D>,
|
|
80
|
+
onChange: @escaping (AffineTransform3D) -> Void
|
|
81
|
+
) -> some View {
|
|
82
|
+
modifier(OrbitModifier(enabled: enabled, entityTransform: entityTransform, onChange: onChange))
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -5,6 +5,9 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
5
5
|
weak var model: SpatialWebViewModel?
|
|
6
6
|
var webview: WKWebView?
|
|
7
7
|
private var isObserving = false
|
|
8
|
+
private var urlObservation: NSKeyValueObservation?
|
|
9
|
+
private var canGoBackObservation: NSKeyValueObservation?
|
|
10
|
+
private var canGoForwardObservation: NSKeyValueObservation?
|
|
8
11
|
private var navigationInvoke: ((_ data: URL) -> Bool)?
|
|
9
12
|
private var openWindowInvoke: ((_ data: URL) -> WebViewElementInfo?)?
|
|
10
13
|
private var webviewStateChangeInvoke: ((_ type: SpatialWebViewState) -> Void)?
|
|
@@ -205,32 +208,37 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
205
208
|
|
|
206
209
|
func startObserving() {
|
|
207
210
|
guard !isObserving else { return }
|
|
208
|
-
webview?.
|
|
211
|
+
urlObservation = webview?.observe(\.url, options: [.new]) { [weak self] webView, _ in
|
|
212
|
+
guard let self, let url = webView.url?.absoluteString else { return }
|
|
213
|
+
Task { @MainActor in
|
|
214
|
+
self.model?.url = url
|
|
215
|
+
self.webviewStateChangeInvoke?(.didUpdateURL)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
canGoBackObservation = webview?.observe(\.canGoBack, options: [.new]) { [weak self] _, _ in
|
|
219
|
+
Task { @MainActor in
|
|
220
|
+
self?.webviewStateChangeInvoke?(.didUpdateNavigationState)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
canGoForwardObservation = webview?.observe(\.canGoForward, options: [.new]) { [weak self] _, _ in
|
|
224
|
+
Task { @MainActor in
|
|
225
|
+
self?.webviewStateChangeInvoke?(.didUpdateNavigationState)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
209
228
|
isObserving = true
|
|
210
229
|
}
|
|
211
230
|
|
|
212
231
|
func stopObserving() {
|
|
213
232
|
guard isObserving else { return }
|
|
214
|
-
|
|
233
|
+
urlObservation?.invalidate()
|
|
234
|
+
canGoBackObservation?.invalidate()
|
|
235
|
+
canGoForwardObservation?.invalidate()
|
|
236
|
+
urlObservation = nil
|
|
237
|
+
canGoBackObservation = nil
|
|
238
|
+
canGoForwardObservation = nil
|
|
215
239
|
isObserving = false
|
|
216
240
|
}
|
|
217
241
|
|
|
218
|
-
override func observeValue(
|
|
219
|
-
forKeyPath keyPath: String?,
|
|
220
|
-
of object: Any?,
|
|
221
|
-
change: [NSKeyValueChangeKey: Any]?,
|
|
222
|
-
context: UnsafeMutableRawPointer?
|
|
223
|
-
) {
|
|
224
|
-
if keyPath == #keyPath(WKWebView.url),
|
|
225
|
-
let url = (object as? WKWebView)?.url?.absoluteString
|
|
226
|
-
{
|
|
227
|
-
DispatchQueue.main.async {
|
|
228
|
-
// print("url change", url)
|
|
229
|
-
self.model?.url = url
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
242
|
func destroy() {
|
|
235
243
|
destroyView()
|
|
236
244
|
navigationInvoke = nil
|
|
@@ -10,7 +10,7 @@ class SpatialWebViewModel {
|
|
|
10
10
|
private var navigationList: [String: (_ data: URL) -> Bool] = [:]
|
|
11
11
|
private var openWindowList: [String: (_ data: URL) -> WebViewElementInfo?] = [:]
|
|
12
12
|
private var stateListeners: [SpatialWebViewState: [() -> Void]] = [:]
|
|
13
|
-
private var stateChangeListeners: [(_ type: SpatialWebViewState) -> Void] = []
|
|
13
|
+
private var stateChangeListeners: [(id: UUID, listener: (_ type: SpatialWebViewState) -> Void)] = []
|
|
14
14
|
private var scrollUpdateListeners: [(_ type: ScrollState, _ point: CGPoint) -> Void] = []
|
|
15
15
|
private var backgroundTransparent: Bool = false
|
|
16
16
|
|
|
@@ -150,8 +150,11 @@ class SpatialWebViewModel {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
/// webview state event
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
@discardableResult
|
|
154
|
+
func addStateListener(_ event: @escaping (_ type: SpatialWebViewState) -> Void) -> UUID {
|
|
155
|
+
let id = UUID()
|
|
156
|
+
stateChangeListeners.append((id: id, listener: event))
|
|
157
|
+
return id
|
|
155
158
|
}
|
|
156
159
|
|
|
157
160
|
func addStateListener(_ state: SpatialWebViewState, _ event: @escaping () -> Void) {
|
|
@@ -161,13 +164,13 @@ class SpatialWebViewModel {
|
|
|
161
164
|
stateListeners[state]?.append(event)
|
|
162
165
|
}
|
|
163
166
|
|
|
164
|
-
func removeStateListener(_
|
|
167
|
+
func removeStateListener(_ id: UUID) {
|
|
165
168
|
stateChangeListeners.removeAll(where: {
|
|
166
|
-
$0
|
|
169
|
+
$0.id == id
|
|
167
170
|
})
|
|
168
171
|
}
|
|
169
172
|
|
|
170
|
-
func removeStateListener(_ state: SpatialWebViewState, _ event: @escaping (
|
|
173
|
+
func removeStateListener(_ state: SpatialWebViewState, _ event: @escaping () -> Void) {
|
|
171
174
|
stateListeners[state]?.removeAll(where: {
|
|
172
175
|
$0 as AnyObject === event as AnyObject
|
|
173
176
|
})
|
|
@@ -235,7 +238,7 @@ class SpatialWebViewModel {
|
|
|
235
238
|
load()
|
|
236
239
|
}
|
|
237
240
|
for onStateChange in stateChangeListeners {
|
|
238
|
-
onStateChange(state)
|
|
241
|
+
onStateChange.listener(state)
|
|
239
242
|
}
|
|
240
243
|
stateListeners[state]?.forEach { onStateChange in
|
|
241
244
|
onStateChange()
|
|
@@ -300,6 +303,8 @@ enum SpatialWebViewState: String, CaseIterable {
|
|
|
300
303
|
case didReceive
|
|
301
304
|
case didFinishLoad
|
|
302
305
|
case didFailLoad
|
|
306
|
+
case didUpdateURL
|
|
307
|
+
case didUpdateNavigationState
|
|
303
308
|
case didUnload
|
|
304
309
|
case didClose
|
|
305
310
|
case didMakeView
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
2B06AEE62E4C1AE8000327E9 /* JSBCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B06AEC02E4C1AE8000327E9 /* JSBCommand.swift */; };
|
|
15
15
|
2B06AEE92E4C1AE8000327E9 /* EventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B06AEBF2E4C1AE8000327E9 /* EventEmitter.swift */; };
|
|
16
16
|
2B1008A22F0B000800000002 /* NavigationCleanupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1008A12F0B000800000001 /* NavigationCleanupTests.swift */; };
|
|
17
|
+
2B1008A52F0B000800000005 /* SpatializedElementMotionTimelineSamplerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1008A42F0B000800000004 /* SpatializedElementMotionTimelineSamplerTests.swift */; };
|
|
18
|
+
2B1008A72F0B000800000007 /* SpatializedElementAnimationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B1008A62F0B000800000006 /* SpatializedElementAnimationManagerTests.swift */; };
|
|
17
19
|
2B2F1D712BEBFAAC006897EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2B2F1D702BEBFAAC006897EE /* Assets.xcassets */; };
|
|
18
20
|
2B2F1D742BEBFAAC006897EE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2B2F1D732BEBFAAC006897EE /* Preview Assets.xcassets */; };
|
|
19
21
|
2B89E38C2E699104004079AA /* WebMsgCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B89E38B2E699104004079AA /* WebMsgCommand.swift */; };
|
|
@@ -38,12 +40,14 @@
|
|
|
38
40
|
2B06AEC32E4C1AE8000327E9 /* WebSpatialApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSpatialApp.swift; sourceTree = "<group>"; };
|
|
39
41
|
2B06AEC42E4C1AE8000327E9 /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
|
|
40
42
|
2B1008A12F0B000800000001 /* NavigationCleanupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCleanupTests.swift; sourceTree = "<group>"; };
|
|
43
|
+
2B1008A42F0B000800000004 /* SpatializedElementMotionTimelineSamplerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpatializedElementMotionTimelineSamplerTests.swift; sourceTree = "<group>"; };
|
|
44
|
+
2B1008A62F0B000800000006 /* SpatializedElementAnimationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpatializedElementAnimationManagerTests.swift; sourceTree = "<group>"; };
|
|
41
45
|
2B2F1D632BEBFAAA006897EE /* WebSpatial.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebSpatial.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
42
46
|
2B2F1D672BEBFAAA006897EE /* RealityKitContent */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = RealityKitContent; sourceTree = "<group>"; };
|
|
43
47
|
2B2F1D702BEBFAAC006897EE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
|
44
48
|
2B2F1D732BEBFAAC006897EE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
|
45
49
|
2B2F1D752BEBFAAC006897EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
|
46
|
-
2B2F1D7A2BEBFAAD006897EE /* WebSpatial.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0;
|
|
50
|
+
2B2F1D7A2BEBFAAD006897EE /* WebSpatial.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = WebSpatial.xctest; path = "web-spatialTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
47
51
|
2B89E38B2E699104004079AA /* WebMsgCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebMsgCommand.swift; sourceTree = "<group>"; };
|
|
48
52
|
2BDBF9B52C4ED9F600D269D7 /* static-web */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "static-web"; sourceTree = "<group>"; };
|
|
49
53
|
/* End PBXFileReference section */
|
|
@@ -63,6 +67,8 @@
|
|
|
63
67
|
2B06AF312E4C1AF8000327E9 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
|
64
68
|
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
|
65
69
|
membershipExceptions = (
|
|
70
|
+
dynamic3d/EntityAnimationManager.swift,
|
|
71
|
+
dynamic3d/EntityAnimationSession.swift,
|
|
66
72
|
dynamic3d/Geometry.swift,
|
|
67
73
|
dynamic3d/SpatialComponent.swift,
|
|
68
74
|
dynamic3d/SpatialEntity.swift,
|
|
@@ -71,6 +77,15 @@
|
|
|
71
77
|
dynamic3d/SpatialModelResource.swift,
|
|
72
78
|
dynamic3d/SpatialRootEntity.swift,
|
|
73
79
|
dynamic3d/SpatialTextureResource.swift,
|
|
80
|
+
"element-motion/SpatializedElementAnimationCommand.swift",
|
|
81
|
+
"element-motion/SpatializedElementAnimationManager.swift",
|
|
82
|
+
"element-motion/SpatializedElementAnimationObject.swift",
|
|
83
|
+
"element-motion/SpatializedElementAnimationTypes.swift",
|
|
84
|
+
"element-motion/SpatializedElementAnimationWriteAdapter.swift",
|
|
85
|
+
"element-motion/SpatializedElementMotionBridgeTypes.swift",
|
|
86
|
+
"element-motion/SpatializedElementMotionTimelineSampler.swift",
|
|
87
|
+
"element-motion/SpatializedElementMotionTiming.swift",
|
|
88
|
+
"element-motion/SpatializedElementMotionTransformTypes.swift",
|
|
74
89
|
MaterialSceneRefresh.swift,
|
|
75
90
|
SpatialApp.swift,
|
|
76
91
|
Spatialized2DElement.swift,
|
|
@@ -113,6 +128,7 @@
|
|
|
113
128
|
SpatialSceneView.swift,
|
|
114
129
|
"view-modifier/HideViewModifier.swift",
|
|
115
130
|
"view-modifier/MaterialWithBorderCornerModifier.swift",
|
|
131
|
+
"view-modifier/OrbitModifier.swift",
|
|
116
132
|
);
|
|
117
133
|
target = 2B2F1D622BEBFAAA006897EE /* web-spatial */;
|
|
118
134
|
};
|
|
@@ -159,6 +175,8 @@
|
|
|
159
175
|
isa = PBXGroup;
|
|
160
176
|
children = (
|
|
161
177
|
2B1008A12F0B000800000001 /* NavigationCleanupTests.swift */,
|
|
178
|
+
2B1008A42F0B000800000004 /* SpatializedElementMotionTimelineSamplerTests.swift */,
|
|
179
|
+
2B1008A62F0B000800000006 /* SpatializedElementAnimationManagerTests.swift */,
|
|
162
180
|
);
|
|
163
181
|
path = "web-spatialTests";
|
|
164
182
|
sourceTree = "<group>";
|
|
@@ -346,6 +364,8 @@
|
|
|
346
364
|
buildActionMask = 2147483647;
|
|
347
365
|
files = (
|
|
348
366
|
2B1008A22F0B000800000002 /* NavigationCleanupTests.swift in Sources */,
|
|
367
|
+
2B1008A52F0B000800000005 /* SpatializedElementMotionTimelineSamplerTests.swift in Sources */,
|
|
368
|
+
2B1008A72F0B000800000007 /* SpatializedElementAnimationManagerTests.swift in Sources */,
|
|
349
369
|
);
|
|
350
370
|
runOnlyForDeploymentPostprocessing = 0;
|
|
351
371
|
};
|
|
@@ -542,13 +562,14 @@
|
|
|
542
562
|
GENERATE_INFOPLIST_FILE = YES;
|
|
543
563
|
MARKETING_VERSION = 1.0;
|
|
544
564
|
PRODUCT_BUNDLE_IDENTIFIER = com.webspatial.test;
|
|
545
|
-
|
|
565
|
+
PRODUCT_MODULE_NAME = WebSpatialTests;
|
|
566
|
+
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
546
567
|
SUPPORTED_PLATFORMS = "xros xrsimulator";
|
|
547
568
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
|
548
569
|
SWIFT_VERSION = 5.0;
|
|
549
570
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
|
550
|
-
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/
|
|
551
|
-
XROS_DEPLOYMENT_TARGET =
|
|
571
|
+
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebSpatial.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/WebSpatial";
|
|
572
|
+
XROS_DEPLOYMENT_TARGET = 26.0;
|
|
552
573
|
};
|
|
553
574
|
name = Debug;
|
|
554
575
|
};
|
|
@@ -561,13 +582,14 @@
|
|
|
561
582
|
GENERATE_INFOPLIST_FILE = YES;
|
|
562
583
|
MARKETING_VERSION = 1.0;
|
|
563
584
|
PRODUCT_BUNDLE_IDENTIFIER = com.webspatial.test;
|
|
564
|
-
|
|
585
|
+
PRODUCT_MODULE_NAME = WebSpatialTests;
|
|
586
|
+
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
565
587
|
SUPPORTED_PLATFORMS = "xros xrsimulator";
|
|
566
588
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
|
567
589
|
SWIFT_VERSION = 5.0;
|
|
568
590
|
TARGETED_DEVICE_FAMILY = "1,2,7";
|
|
569
|
-
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/
|
|
570
|
-
XROS_DEPLOYMENT_TARGET =
|
|
591
|
+
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebSpatial.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/WebSpatial";
|
|
592
|
+
XROS_DEPLOYMENT_TARGET = 26.0;
|
|
571
593
|
};
|
|
572
594
|
name = Release;
|
|
573
595
|
};
|
|
@@ -602,7 +624,6 @@
|
|
|
602
624
|
defaultConfigurationName = Release;
|
|
603
625
|
};
|
|
604
626
|
/* End XCConfigurationList section */
|
|
605
|
-
|
|
606
627
|
};
|
|
607
628
|
rootObject = 2B2F1D5B2BEBFAAA006897EE /* Project object */;
|
|
608
629
|
}
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
<BuildableReference
|
|
37
37
|
BuildableIdentifier = "primary"
|
|
38
38
|
BlueprintIdentifier = "2B2F1D792BEBFAAD006897EE"
|
|
39
|
-
BuildableName = "
|
|
39
|
+
BuildableName = "web-spatialTests.xctest"
|
|
40
40
|
BlueprintName = "web-spatialTests"
|
|
41
41
|
ReferencedContainer = "container:web-spatial.xcodeproj">
|
|
42
42
|
</BuildableReference>
|
|
@@ -18,7 +18,8 @@ final class NavigationCleanupTests: XCTestCase {
|
|
|
18
18
|
id: "test-attachment",
|
|
19
19
|
parentEntityId: "test-parent-entity",
|
|
20
20
|
position: SIMD3<Float>(0, 0, 0),
|
|
21
|
-
size: CGSize(width: 100, height: 100)
|
|
21
|
+
size: CGSize(width: 100, height: 100),
|
|
22
|
+
webViewModel: SpatialWebViewModel(url: "http://localhost:5173/")
|
|
22
23
|
)
|
|
23
24
|
XCTAssertFalse(scene.attachmentManager.attachments.isEmpty)
|
|
24
25
|
|