@webspatial/platform-visionos 1.0.4 → 1.0.5

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.
Files changed (74) hide show
  1. package/package.json +2 -2
  2. package/web-spatial/EventEmitter.swift +56 -0
  3. package/web-spatial/JSBCommand.swift +348 -0
  4. package/web-spatial/SpatialObject.swift +108 -0
  5. package/web-spatial/WebMsgCommand.swift +119 -0
  6. package/web-spatial/WebSpatialApp.swift +111 -0
  7. package/web-spatial/{libs/uiKitDelegate/Window.swift → Window.swift} +1 -0
  8. package/web-spatial/manager/Dynamic3DManager.swift +114 -0
  9. package/web-spatial/manager/JSBManager.swift +148 -0
  10. package/web-spatial/manager/WKWebViewManager.swift +39 -0
  11. package/web-spatial/{libs/webView/manifest.swift → manifest.swift} +16 -5
  12. package/web-spatial/model/SpatialApp.swift +190 -0
  13. package/web-spatial/model/SpatialScene.swift +1042 -0
  14. package/web-spatial/model/Spatialized2DElement.swift +128 -0
  15. package/web-spatial/model/SpatializedDynamic3DElement.swift +34 -0
  16. package/web-spatial/model/SpatializedElement.swift +95 -0
  17. package/web-spatial/model/SpatializedStatic3DElement.swift +19 -0
  18. package/web-spatial/model/dynamic3d/Geometry.swift +95 -0
  19. package/web-spatial/model/dynamic3d/SpatialComponent.swift +72 -0
  20. package/web-spatial/model/dynamic3d/SpatialEntity.swift +211 -0
  21. package/web-spatial/model/dynamic3d/SpatialMaterial.swift +39 -0
  22. package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +39 -0
  23. package/web-spatial/model/dynamic3d/SpatialModelResource.swift +38 -0
  24. package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +18 -0
  25. package/web-spatial/protocol/ScrollAbleSpatialElementContainer.swift +1 -0
  26. package/web-spatial/protocol/SpatialScrollAble.swift +8 -0
  27. package/web-spatial/protocol/SpatializedElementContainer.swift +8 -0
  28. package/web-spatial/protocol/WebMsgSender.swift +5 -0
  29. package/web-spatial/types/Vec2.swift +12 -0
  30. package/web-spatial/types/Vec3.swift +7 -0
  31. package/web-spatial/view/SceneHandlerUIView.swift +92 -0
  32. package/web-spatial/{views/ui/NavView.swift → view/SpatialNavView.swift} +149 -77
  33. package/web-spatial/view/SpatialSceneContentView.swift +218 -0
  34. package/web-spatial/view/SpatialSceneView.swift +53 -0
  35. package/web-spatial/view/Spatialized2DElementView.swift +96 -0
  36. package/web-spatial/view/SpatializedDynamic3DView.swift +104 -0
  37. package/web-spatial/view/SpatializedElementView.swift +178 -0
  38. package/web-spatial/view/SpatializedStatic3DView.swift +70 -0
  39. package/web-spatial/{views → view/view-modifier}/MaterialWithBorderCornerModifier.swift +28 -8
  40. package/web-spatial/webview/SpatialWebController.swift +300 -0
  41. package/web-spatial/webview/SpatialWebView.swift +34 -0
  42. package/web-spatial/webview/SpatialWebViewModel.swift +307 -0
  43. package/web-spatial.xcodeproj/project.pbxproj +126 -207
  44. package/web-spatial/libs/EventEmitter.swift +0 -25
  45. package/web-spatial/libs/SpatialComponent.swift +0 -24
  46. package/web-spatial/libs/SpatialEntity.swift +0 -172
  47. package/web-spatial/libs/SpatialInputComponent.swift +0 -19
  48. package/web-spatial/libs/SpatialMeshResource.swift +0 -12
  49. package/web-spatial/libs/SpatialModel3DComponent.swift +0 -51
  50. package/web-spatial/libs/SpatialModelComponent.swift +0 -25
  51. package/web-spatial/libs/SpatialObject.swift +0 -140
  52. package/web-spatial/libs/SpatialPhysicallyBasedMaterial.swift +0 -19
  53. package/web-spatial/libs/SpatialViewComponent.swift +0 -8
  54. package/web-spatial/libs/SpatialWindowComponent.swift +0 -454
  55. package/web-spatial/libs/SpatialWindowContainer.swift +0 -153
  56. package/web-spatial/libs/Utils/CommandManager.swift +0 -823
  57. package/web-spatial/libs/Utils/PerfClock.swift +0 -43
  58. package/web-spatial/libs/Utils/SceneManager.swift +0 -101
  59. package/web-spatial/libs/Utils/WindowContainerMgr.swift +0 -122
  60. package/web-spatial/libs/json/JsonParser.swift +0 -45
  61. package/web-spatial/libs/webView/UpdateSystem.swift +0 -26
  62. package/web-spatial/libs/webView/backend/NativeWebView.swift +0 -350
  63. package/web-spatial/views/ImmersiveView.swift +0 -17
  64. package/web-spatial/views/OpenDismissHandlerUI.swift +0 -45
  65. package/web-spatial/views/PlainWindowContainerView.swift +0 -132
  66. package/web-spatial/views/SpatialModel3DView.swift +0 -187
  67. package/web-spatial/views/SpatialViewUI.swift +0 -168
  68. package/web-spatial/views/SpatialWebViewUI.swift +0 -179
  69. package/web-spatial/views/VolumetricWindowContainerView.swift +0 -30
  70. package/web-spatial/web_spatialApp.swift +0 -141
  71. /package/web-spatial/{libs/Utils → Utils}/ColorExtension.swift +0 -0
  72. /package/web-spatial/{libs/Utils → Utils}/Logger.swift +0 -0
  73. /package/web-spatial/{views → view}/LoadingView.swift +0 -0
  74. /package/web-spatial/{views → view/view-modifier}/HideViewModifier.swift +0 -0
@@ -0,0 +1,218 @@
1
+ import RealityKit
2
+ import SwiftUI
3
+
4
+ struct SpatialSceneContentView: View {
5
+ @State var sceneId: String
6
+ var width: Double
7
+ var height: Double
8
+
9
+ var body: some View {
10
+ if let spatialScene = SpatialApp.Instance.getScene(sceneId) {
11
+ ZStack(alignment: Alignment.topLeading) {
12
+ // Display the main webview
13
+ if spatialScene.didFailLoad {
14
+ VStack {
15
+ Text("Failed to load webpage. Is the server running?")
16
+ .foregroundColor(.white)
17
+ Button("Reload") {
18
+ let url = spatialScene.spatialWebViewModel.url
19
+
20
+ spatialScene.spatialWebViewModel.load(url)
21
+ }
22
+ .foregroundColor(.white)
23
+ }
24
+ .frame(maxWidth: .infinity, maxHeight: .infinity).glassBackgroundEffect()
25
+ } else {
26
+ spatialScene.getView()
27
+ .materialWithBorderCorner(
28
+ spatialScene.backgroundMaterial,
29
+ spatialScene.cornerRadius,
30
+ spatialScene.windowStyle
31
+ )
32
+ .frame(width: width, height: height)
33
+ .offset(z: -1)
34
+
35
+ let childrenOfSpatialized2DElement: [SpatializedElement] = Array(spatialScene.getChildrenOfType(.Spatialized2DElement).values)
36
+
37
+ ForEach(childrenOfSpatialized2DElement, id: \.id) { child in
38
+ SpatializedElementView(parentScrollOffset: spatialScene.scrollOffset) {
39
+ Spatialized2DElementView()
40
+ }
41
+ .environment(child)
42
+ }
43
+
44
+ let childrenOfSpatializedStatic3DElement: [SpatializedElement] = Array(spatialScene.getChildrenOfType(.SpatializedStatic3DElement).values)
45
+
46
+ ForEach(childrenOfSpatializedStatic3DElement, id: \.id) { child in
47
+ SpatializedElementView(parentScrollOffset: spatialScene.scrollOffset) {
48
+ SpatializedStatic3DView()
49
+ }
50
+ .environment(child)
51
+ }
52
+
53
+ let childrenOfSpatializedDynamic3DElement: [SpatializedElement] = Array(spatialScene.getChildrenOfType(.SpatializedDynamic3DElement).values)
54
+
55
+ ForEach(childrenOfSpatializedDynamic3DElement, id: \.id) { child in
56
+ SpatializedElementView(parentScrollOffset: spatialScene.scrollOffset) {
57
+ SpatializedDynamic3DView()
58
+ }
59
+ .environment(child)
60
+ }
61
+ }
62
+ }
63
+ .opacity(spatialScene.opacity)
64
+ .environment(spatialScene)
65
+ .coordinateSpace(name: "SpatialScene")
66
+ }
67
+ }
68
+ }
69
+
70
+ struct PreviewSpatializedStatic3DElement: View {
71
+ var sceneId: String
72
+
73
+ init() {
74
+ let spatialScene = SpatialApp.Instance.createScene(
75
+ "http://localhost:5173/",
76
+ .window,
77
+ .visible
78
+ )
79
+
80
+ let spatializedStatic3DElement: SpatializedStatic3DElement = spatialScene.createSpatializedElement(
81
+ .SpatializedStatic3DElement
82
+ )
83
+ spatializedStatic3DElement.transform.translation.x = 200
84
+ spatializedStatic3DElement.transform.translation.y = 300
85
+ spatializedStatic3DElement.transform.translation.z = 0
86
+ spatializedStatic3DElement.name = "jack"
87
+
88
+ spatializedStatic3DElement.width = 200
89
+ spatializedStatic3DElement.height = 100
90
+
91
+ spatializedStatic3DElement.modelURL = "http://localhost:5173/public/modelasset/cone.usdz"
92
+ spatializedStatic3DElement.setParent(spatialScene)
93
+
94
+ let spatializedStatic3DElementB: SpatializedStatic3DElement = spatialScene.createSpatializedElement(
95
+ .SpatializedStatic3DElement
96
+ )
97
+ spatializedStatic3DElementB.transform.translation.x = 700
98
+ spatializedStatic3DElementB.transform.translation.y = 300
99
+ spatializedStatic3DElementB.transform.translation.z = 0
100
+ spatializedStatic3DElementB.width = 200
101
+ spatializedStatic3DElementB.height = 200
102
+ spatializedStatic3DElementB.name = "tom"
103
+
104
+ spatializedStatic3DElementB.modelURL = "http://localhost:5173/public/modelasset/vehicle-speedster.usdz"
105
+ spatializedStatic3DElementB.setParent(spatialScene)
106
+
107
+ sceneId = spatialScene.id
108
+
109
+ print("spatialScene \(spatialScene)")
110
+
111
+ let spatializedElement: SpatializedDynamic3DElement = spatialScene.createSpatializedElement(
112
+ .SpatializedDynamic3DElement
113
+ )
114
+
115
+ spatializedElement.name = "dynamicCubes"
116
+ spatializedElement.transform.translation.x = 400
117
+ spatializedElement.transform.translation.y = 400
118
+ spatializedElement.transform.translation.z = 0
119
+ spatializedElement.width = 200
120
+ spatializedElement.height = 200
121
+ spatializedElement.setParent(spatialScene)
122
+ }
123
+
124
+ var body: some View {
125
+ SpatialSceneContentView(sceneId: sceneId, width: 1200, height: 1000)
126
+ }
127
+ }
128
+
129
+ struct PreviewSpatialized2DElement: View {
130
+ var sceneId: String
131
+
132
+ init() {
133
+ let spatialScene = SpatialApp.Instance.createScene(
134
+ "http://localhost:5173/",
135
+ .window,
136
+ .visible
137
+ )
138
+
139
+ let spatializedElementA: Spatialized2DElement = spatialScene.createSpatializedElement(
140
+ .Spatialized2DElement
141
+ )
142
+ spatializedElementA.transform.translation.x = 200
143
+ spatializedElementA.transform.translation.y = 300
144
+ spatializedElementA.transform.translation.z = 0
145
+ spatializedElementA.name = "jack"
146
+
147
+ spatializedElementA.width = 200
148
+ spatializedElementA.height = 100
149
+ spatializedElementA.load("http://localhost:5173/src/")
150
+
151
+ spatializedElementA.setParent(spatialScene)
152
+
153
+ let spatializedElementB: Spatialized2DElement = spatialScene.createSpatializedElement(
154
+ .Spatialized2DElement
155
+ )
156
+ spatializedElementB.transform.translation.x = 400
157
+ spatializedElementB.transform.translation.y = 300
158
+ spatializedElementB.transform.translation.z = 0
159
+ spatializedElementB.name = "jack"
160
+
161
+ spatializedElementB.width = 200
162
+ spatializedElementB.height = 100
163
+ spatializedElementB.load("http://localhost:5173/src/embed/")
164
+ spatializedElementB.setParent(spatialScene)
165
+
166
+ sceneId = spatialScene.id
167
+
168
+ print("spatialScene \(spatialScene)")
169
+ }
170
+
171
+ var body: some View {
172
+ SpatialSceneContentView(sceneId: sceneId, width: 1200, height: 1000)
173
+ }
174
+ }
175
+
176
+ struct PreviewSpatializedDynamic3DElement: View {
177
+ var sceneId: String
178
+
179
+ init() {
180
+ let spatialScene = SpatialApp.Instance.createScene(
181
+ "http://localhost:5173/",
182
+ .window,
183
+ .visible
184
+ )
185
+
186
+ let spatializedElement: SpatializedDynamic3DElement = spatialScene.createSpatializedElement(
187
+ .SpatializedDynamic3DElement
188
+ )
189
+
190
+ spatializedElement.name = "dynamicCubes"
191
+ spatializedElement.transform.translation.x = 100
192
+ spatializedElement.transform.translation.y = 100
193
+ spatializedElement.transform.translation.z = 0
194
+ spatializedElement.width = 200
195
+ spatializedElement.height = 200
196
+ spatializedElement.setParent(spatialScene)
197
+
198
+ sceneId = spatialScene.id
199
+
200
+ print("spatialScene \(spatialScene)")
201
+ }
202
+
203
+ var body: some View {
204
+ SpatialSceneContentView(sceneId: sceneId, width: 1200, height: 1000)
205
+ }
206
+ }
207
+
208
+ #Preview("PreviewSpatializedDynamic3DElement") {
209
+ PreviewSpatializedDynamic3DElement()
210
+ }
211
+
212
+ #Preview("PreviewSpatializedStatic3DElementWithRotation") {
213
+ PreviewSpatializedStatic3DElement()
214
+ }
215
+
216
+ #Preview("PreviewSpatialized2DElement") {
217
+ PreviewSpatialized2DElement()
218
+ }
@@ -0,0 +1,53 @@
1
+ import _RealityKit_SwiftUI
2
+ import SwiftUI
3
+
4
+ struct SpatialSceneView: View {
5
+ @State var spatialScene: SpatialScene
6
+ @State private var windowResizeInProgress = false
7
+ @State private var timer: Timer?
8
+
9
+ var body: some View {
10
+ GeometryReader3D { proxy3D in
11
+ let width = proxy3D.size.width
12
+ let height = proxy3D.size.height
13
+
14
+ SceneHandlerUIView(spatialScene: spatialScene).onChange(of: proxy3D.size) {
15
+ windowResizeInProgress = true
16
+ if timer != nil {
17
+ timer!.invalidate()
18
+ }
19
+ // If we don't detect resolution change after x seconds we treat the resize as complete
20
+ timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: false) { _ in
21
+ windowResizeInProgress = false
22
+ timer = nil
23
+ }
24
+ spatialScene.updateSize3D(proxy3D.size)
25
+ }
26
+ .onAppear {
27
+ spatialScene.moveToState(.visible, nil)
28
+ spatialScene.updateSize3D(proxy3D.size)
29
+ }
30
+
31
+ if windowResizeInProgress {
32
+ let x = width / 2
33
+ let y = height / 2
34
+ VStack {}.frame(width: width, height: height).glassBackgroundEffect().padding3D(.front, -100_000)
35
+ .position(x: x, y: y)
36
+ } else {
37
+ SpatialSceneContentView(sceneId: spatialScene.id, width: width, height: height)
38
+ .ornament(attachmentAnchor: .scene(.top), contentAlignment: .center) {
39
+ if pwaManager.display != .fullscreen {
40
+ ZStack {
41
+ SpatialNavView(
42
+ spatialScene: spatialScene
43
+ )
44
+ .offset(y: -15)
45
+ }.frame(height: 100)
46
+ }
47
+ }.volumeBaseplateVisibility(
48
+ spatialScene.sceneConfig?.baseplateVisibility ?? .automatic
49
+ )
50
+ }
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,96 @@
1
+ import RealityKit
2
+ import SwiftUI
3
+
4
+ extension View {
5
+ @ViewBuilder
6
+ func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View {
7
+ if condition {
8
+ transform(self)
9
+ } else {
10
+ self
11
+ }
12
+ }
13
+ }
14
+
15
+ class Spatialized2DViewGestureData {
16
+ var dragStarted = false
17
+ var dragStart: CGFloat = 0.0
18
+ var dragVelocity: CGFloat = 0.0
19
+ }
20
+
21
+ struct Spatialized2DElementView: View {
22
+ @Environment(SpatializedElement.self) var spatializedElement: SpatializedElement
23
+ @Environment(SpatialScene.self) var spatialScene: SpatialScene
24
+
25
+ private var spatialized2DElement: Spatialized2DElement {
26
+ return spatializedElement as! Spatialized2DElement
27
+ }
28
+
29
+ @State private var gestureData = Spatialized2DViewGestureData()
30
+
31
+ var body: some View {
32
+ // Display child spatialized2DElements
33
+ ZStack(alignment: Alignment.topLeading) {
34
+ // Display the main webview
35
+ spatialized2DElement.getView()
36
+ .materialWithBorderCorner(
37
+ spatialized2DElement.backgroundMaterial,
38
+ spatialized2DElement.cornerRadius,
39
+ .window
40
+ )
41
+ .simultaneousGesture(spatialized2DElement.scrollPageEnabled ? dragWebGesture : nil)
42
+
43
+ let childrenOfSpatialized2DElement: [SpatializedElement] = Array(spatialized2DElement.getChildrenOfType(.Spatialized2DElement).values)
44
+
45
+ ForEach(childrenOfSpatialized2DElement, id: \.id) { child in
46
+ SpatializedElementView(parentScrollOffset: spatialized2DElement.scrollOffset) {
47
+ Spatialized2DElementView()
48
+ }
49
+ .environment(child)
50
+ }
51
+
52
+ let childrenOfSpatializedStatic3DElement: [SpatializedElement] = Array(spatialized2DElement.getChildrenOfType(.SpatializedStatic3DElement).values)
53
+ ForEach(childrenOfSpatializedStatic3DElement, id: \.id) { child in
54
+ SpatializedElementView(parentScrollOffset: spatialized2DElement.scrollOffset) {
55
+ SpatializedStatic3DView()
56
+ }
57
+ .environment(child)
58
+ }
59
+
60
+ let childrenOfSpatializedDynamic3DElement: [SpatializedElement] = Array(spatialized2DElement.getChildrenOfType(.SpatializedDynamic3DElement).values)
61
+
62
+ ForEach(childrenOfSpatializedDynamic3DElement, id: \.id) { child in
63
+ SpatializedElementView(parentScrollOffset: spatialized2DElement.scrollOffset) {
64
+ SpatializedDynamic3DView()
65
+ }
66
+ .environment(child)
67
+ }
68
+ }
69
+ }
70
+
71
+ private var dragWebGesture: some Gesture {
72
+ DragGesture()
73
+ .onChanged { gesture in
74
+ print("\(spatialized2DElement.name) dragWebGesture")
75
+ if spatialized2DElement.scrollPageEnabled {
76
+ if !gestureData.dragStarted {
77
+ gestureData.dragStarted = true
78
+ gestureData.dragStart = (gesture.translation.height)
79
+ }
80
+
81
+ // TODO: this should have velocity
82
+ let delta = gestureData.dragStart - gesture.translation.height
83
+ gestureData.dragStart = gesture.translation.height
84
+ spatialScene.updateDeltaScrollOffset(Vec2(x: 0, y: delta))
85
+ }
86
+ }
87
+ .onEnded { _ in
88
+ print("\(spatialized2DElement.name) dragWebGestureEnd")
89
+ if spatialized2DElement.scrollPageEnabled {
90
+ gestureData.dragStarted = false
91
+ gestureData.dragStart = 0
92
+ spatialScene.stopScrolling()
93
+ }
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,104 @@
1
+ import RealityKit
2
+ import SwiftUI
3
+
4
+ struct SpatializedDynamic3DView: View {
5
+ @Environment(SpatializedElement.self) var spatializedElement: SpatializedElement
6
+ @Environment(SpatialScene.self) var spatialScene: SpatialScene
7
+ @State private var isDrag = false
8
+ @State private var isRotate = false
9
+ @State private var isScale = false
10
+
11
+ private var spatializedDynamic3DElement: SpatializedDynamic3DElement {
12
+ return spatializedElement as! SpatializedDynamic3DElement
13
+ }
14
+
15
+ var spatialTapEvent: some Gesture{
16
+ SpatialTapGesture(count: 1).targetedToAnyEntity()
17
+ .onEnded{ value in
18
+ if let entity = value.entity as? SpatialEntity{
19
+ spatialScene.sendWebMsg(entity.spatialId, WebSpatialTapGuestureEvent(detail: WebSpatialTapGuestureEventDetail(location3D: value.location3D)))
20
+ }
21
+ else {
22
+ if let spatialEntity = SpatialEntity.findNearestParent(entity: value.entity){
23
+ spatialScene.sendWebMsg(spatialEntity.spatialId, WebSpatialTapGuestureEvent(detail: WebSpatialTapGuestureEventDetail(location3D: value.location3D)))
24
+ }
25
+ }
26
+ }
27
+ }
28
+
29
+ var rotate3dEvent: some Gesture{
30
+ RotateGesture3D().targetedToAnyEntity().onChanged{ value in
31
+ print(value.gestureValue)
32
+ if let entity = value.entity as? SpatialEntity{
33
+ if entity.enableTap == true {
34
+ // if(isRotate == false){
35
+ // print("start rotate")
36
+ // }
37
+ // else{
38
+ // print("rotating")
39
+ // }
40
+ // isRotate = true
41
+ return
42
+ }
43
+ }
44
+ isRotate = false
45
+ }.onEnded{ value in
46
+ print(value.rotation)
47
+ print("rotate end")
48
+ // isRotate = false
49
+ }
50
+ }
51
+
52
+ var magnifyEvent: some Gesture{
53
+ MagnifyGesture().targetedToAnyEntity().onChanged{ value in
54
+ print(value)
55
+ if let entity = value.entity as? SpatialEntity{
56
+ if entity.enableTap == true {
57
+ // if(isScale == false){
58
+ // print("start scale")
59
+ // }
60
+ // else{
61
+ // print("scaling")
62
+ // }
63
+ // isScale = true
64
+ return
65
+ }
66
+ }
67
+ }.onEnded{ value in
68
+ print("scale end")
69
+ // isScale = false
70
+ }
71
+ }
72
+
73
+ var dragEvent: some Gesture{
74
+ DragGesture().targetedToAnyEntity().onChanged{ value in
75
+ if let entity = value.entity as? SpatialEntity{
76
+ if entity.enableTap == true {
77
+ // if(isDrag == false){
78
+ // print("start drag")
79
+ // }
80
+ // else{
81
+ // print("dragging")
82
+ // }
83
+ // isDrag = true
84
+ return
85
+ }
86
+ }
87
+
88
+ }.onEnded{ value in
89
+ print("drag end")
90
+ // isDrag = false
91
+ }
92
+ }
93
+
94
+ var body: some View {
95
+ RealityView(make: { content in
96
+ let rootEntity = spatializedDynamic3DElement.getRoot()
97
+ content.add(rootEntity)
98
+ })
99
+ .simultaneousGesture(spatialTapEvent)
100
+ .simultaneousGesture(rotate3dEvent)
101
+ .simultaneousGesture(dragEvent)
102
+ .simultaneousGesture(magnifyEvent)
103
+ }
104
+ }
@@ -0,0 +1,178 @@
1
+ import SwiftUI
2
+
3
+ // zIndex() have some bug, so use zOrderBias to simulate zIndex effect
4
+ let zOrderBias = 0.001
5
+
6
+ struct SpatializedElementView<Content: View>: View {
7
+ @Environment(SpatializedElement.self) var spatializedElement: SpatializedElement
8
+ @Environment(SpatialScene.self) var spatialScene: SpatialScene
9
+
10
+ var parentScrollOffset: Vec2
11
+ var content: Content
12
+
13
+ init(parentScrollOffset: Vec2, @ViewBuilder content: () -> Content) {
14
+ self.parentScrollOffset = parentScrollOffset
15
+ self.content = content()
16
+ }
17
+
18
+ // Begin Interaction
19
+ var gesture: some Gesture {
20
+ DragGesture()
21
+ .onChanged(onDragging)
22
+ .onEnded(onDraggingEnded)
23
+ .simultaneously(with:
24
+ RotateGesture3D()
25
+ .onChanged(onRotateGesture3D)
26
+ .onEnded(onRotateGesture3DEnd)
27
+ )
28
+ .simultaneously(with:
29
+ MagnifyGesture()
30
+ .onChanged(onMagnifyGesture)
31
+ .onEnded(onMagnifyGestureEnd)
32
+ )
33
+ .simultaneously(with:
34
+ SpatialTapGesture(count: 1)
35
+ .onEnded(onTapEnded)
36
+ )
37
+ }
38
+
39
+ private func onRotateGesture3D(_ event: RotateGesture3D.Value) {
40
+ if spatializedElement.enableRotateGesture || spatializedElement.enableRotateStartGesture {
41
+ let gestureEvent = WebSpatialRotateGuestureEvent(
42
+ detail: .init(
43
+ rotation: event.rotation,
44
+ startAnchor3D: event.startAnchor3D,
45
+ startLocation3D: event.startLocation3D
46
+ ))
47
+ spatialScene.sendWebMsg(spatializedElement.id, gestureEvent)
48
+ }
49
+ }
50
+
51
+ private func onRotateGesture3DEnd(_ event: RotateGesture3D.Value) {
52
+ if spatializedElement.enableRotateEndGesture {
53
+ let gestureEvent = WebSpatialRotateEndGuestureEvent(
54
+ detail: .init(
55
+ rotation: event.rotation,
56
+ startAnchor3D: event.startAnchor3D,
57
+ startLocation3D: event.startLocation3D
58
+ ))
59
+ spatialScene.sendWebMsg(spatializedElement.id, gestureEvent)
60
+ }
61
+ }
62
+
63
+ private func onDragging(_ event: DragGesture.Value) {
64
+ if spatializedElement.enableDragStartGesture || spatializedElement.enableDragGesture {
65
+ let gestureEvent = WebSpatialDragGuestureEvent(detail: .init(
66
+ location3D: event.location3D,
67
+ startLocation3D: event.startLocation3D,
68
+ translation3D: event.translation3D,
69
+ predictedEndTranslation3D: event.predictedEndTranslation3D,
70
+ predictedEndLocation3D: event.predictedEndLocation3D,
71
+ velocity: event.velocity
72
+ ))
73
+ spatialScene.sendWebMsg(spatializedElement.id, gestureEvent)
74
+ }
75
+ }
76
+
77
+ private func onDraggingEnded(_ event: DragGesture.Value) {
78
+ if spatializedElement.enableDragEndGesture {
79
+ let gestureEvent = WebSpatialDragEndGuestureEvent(
80
+ detail: .init(
81
+ location3D: event.location3D,
82
+ startLocation3D: event.startLocation3D,
83
+ translation3D: event.translation3D,
84
+ predictedEndTranslation3D: event.predictedEndTranslation3D,
85
+ predictedEndLocation3D: event.predictedEndLocation3D,
86
+ velocity: event.velocity
87
+ ))
88
+ spatialScene.sendWebMsg(spatializedElement.id, gestureEvent)
89
+ }
90
+ }
91
+
92
+ private func onTapEnded(_ event: SpatialTapGesture.Value) {
93
+ if spatializedElement.enableTapGesture {
94
+ spatialScene.sendWebMsg(spatializedElement.id, WebSpatialTapGuestureEvent(detail: .init(location3D: event.location3D)))
95
+ }
96
+ }
97
+
98
+ private func onMagnifyGesture(_ event: MagnifyGesture.Value) {
99
+ if spatializedElement.enableMagnifyGesture || spatializedElement.enableMagnifyStartGesture {
100
+ let gestureEvent = WebSpatialMagnifyGuestureEvent(
101
+ detail: .init(
102
+ magnification: event.magnification,
103
+ velocity: event.velocity,
104
+ startLocation3D: event.startLocation3D,
105
+ startAnchor3D: event.startAnchor3D
106
+ ))
107
+ spatialScene.sendWebMsg(spatializedElement.id, gestureEvent)
108
+ }
109
+ }
110
+
111
+ private func onMagnifyGestureEnd(_ event: MagnifyGesture.Value) {
112
+ if spatializedElement.enableMagnifyEndGesture {
113
+ let gestureEvent = WebSpatialMagnifyEndGuestureEvent(
114
+ detail: .init(
115
+ magnification: event.magnification,
116
+ velocity: event.velocity,
117
+ startLocation3D: event.startLocation3D,
118
+ startAnchor3D: event.startAnchor3D
119
+ ))
120
+ spatialScene.sendWebMsg(spatializedElement.id, gestureEvent)
121
+ }
122
+ }
123
+
124
+ // End Interaction
125
+
126
+ @ViewBuilder
127
+ var body: some View {
128
+ let transform = spatializedElement.transform
129
+ let translation = transform.translation
130
+ let scale = transform.scale
131
+ let rotation = transform.rotation!
132
+
133
+ let width = spatializedElement.width
134
+ let height = spatializedElement.height
135
+ let depth = spatializedElement.depth
136
+ let anchor = spatializedElement.rotationAnchor
137
+
138
+ let centerX = spatializedElement.clientX - (spatializedElement.scrollWithParent ? parentScrollOffset.x : 0)
139
+ let centerY = spatializedElement.clientY - (spatializedElement.scrollWithParent ? parentScrollOffset.y : 0)
140
+
141
+ let opacity = spatializedElement.opacity
142
+ let visible = spatializedElement.visible
143
+ let enableGesture = spatializedElement.enableGesture
144
+
145
+ let z = translation.z + (spatializedElement.zIndex * zOrderBias)
146
+ let smallOffset = z == 0.0 ? 0.0001 : 0
147
+
148
+ content.simultaneousGesture(enableGesture ? gesture : nil)
149
+ .frame(width: width, height: height)
150
+ .frame(depth: depth, alignment: .back)
151
+ .onGeometryChange3D(for: AffineTransform3D.self) { proxy in
152
+ let rect3d = proxy.frame(in: .named("SpatialScene"))
153
+ spatialScene.sendWebMsg(spatializedElement.id, SpatiaizedContainerClientCube(origin: rect3d.origin, size: rect3d.size))
154
+ return proxy.transform(in: .named("SpatialScene"))!
155
+ } action: { _, new in
156
+ spatialScene.sendWebMsg(spatializedElement.id, SpatiaizedContainerTransform(detail: new))
157
+ }
158
+ .frame(depth: 0, alignment: .back)
159
+ // use .offset(smallVal) to workaround for glassEffect not working and small width/height spatialDiv not working
160
+ .offset(z: smallOffset)
161
+ .scaleEffect(
162
+ x: scale.width,
163
+ y: scale.height,
164
+ z: scale.depth,
165
+ anchor: anchor
166
+ )
167
+ .rotation3DEffect(
168
+ rotation,
169
+ anchor: anchor
170
+ )
171
+ .offset(x: translation.x, y: translation.y)
172
+ .offset(z: z)
173
+ .position(x: centerX + width / 2, y: centerY + height / 2)
174
+ .offset(z: spatializedElement.backOffset)
175
+ .opacity(opacity)
176
+ .hidden(!visible)
177
+ }
178
+ }