replate-camera 0.1.39 → 0.1.41
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/ios/ReplateCameraViewManager.swift +172 -87
- package/package.json +1 -1
|
@@ -140,37 +140,62 @@ class ReplateCameraView: UIView, ARSessionDelegate {
|
|
|
140
140
|
|
|
141
141
|
switch gestureRecognizer.state {
|
|
142
142
|
case .changed:
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
ReplateCameraView.anchorEntity.removeChild(ReplateCameraView.focusModel)
|
|
153
|
-
ReplateCameraView.spheresModels = []
|
|
154
|
-
ReplateCameraView.sphereRadius *= scale
|
|
155
|
-
ReplateCameraView.spheresRadius *= scale
|
|
156
|
-
ReplateCameraView.sphereAngle *= scale
|
|
157
|
-
createSpheres(y: ReplateCameraView.spheresHeight)
|
|
158
|
-
createSpheres(y: ReplateCameraView.distanceBetweenCircles + ReplateCameraView.spheresHeight)
|
|
159
|
-
createFocusSphere()
|
|
160
|
-
for i in 0...71 {
|
|
161
|
-
let material = SimpleMaterial(color: .green, roughness: 1, isMetallic: false)
|
|
162
|
-
if (ReplateCameraView.upperSpheresSet[i]) {
|
|
163
|
-
let entity = ReplateCameraView.spheresModels[72 + i]
|
|
164
|
-
entity.model?.materials[0] = material
|
|
143
|
+
// Ensure execution on the main thread
|
|
144
|
+
DispatchQueue.main.async {
|
|
145
|
+
// Calculate the scale based on the gesture recognizer's scale
|
|
146
|
+
let scale = Float(gestureRecognizer.scale)
|
|
147
|
+
|
|
148
|
+
// Ensure anchor entity is not nil before proceeding
|
|
149
|
+
guard let anchorEntity = ReplateCameraView.anchorEntity else {
|
|
150
|
+
print("[handlePinch] Anchor entity is nil.")
|
|
151
|
+
return
|
|
165
152
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
153
|
+
|
|
154
|
+
// Remove all child entities safely
|
|
155
|
+
ReplateCameraView.spheresModels.forEach { entity in
|
|
156
|
+
anchorEntity.removeChild(entity)
|
|
157
|
+
}
|
|
158
|
+
if let focusModel = ReplateCameraView.focusModel {
|
|
159
|
+
anchorEntity.removeChild(focusModel)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Clear spheres models array
|
|
163
|
+
ReplateCameraView.spheresModels = []
|
|
164
|
+
|
|
165
|
+
// Update the scales
|
|
166
|
+
ReplateCameraView.sphereRadius *= scale
|
|
167
|
+
ReplateCameraView.spheresRadius *= scale
|
|
168
|
+
ReplateCameraView.sphereAngle *= scale
|
|
169
|
+
|
|
170
|
+
// Recreate spheres and the focus sphere
|
|
171
|
+
self.createSpheres(y: ReplateCameraView.spheresHeight)
|
|
172
|
+
self.createSpheres(y: ReplateCameraView.distanceBetweenCircles + ReplateCameraView.spheresHeight)
|
|
173
|
+
self.createFocusSphere()
|
|
174
|
+
|
|
175
|
+
// Update the material of the spheres based on their state
|
|
176
|
+
for i in 0..<72 {
|
|
177
|
+
let material = SimpleMaterial(color: .green, roughness: 1, isMetallic: false)
|
|
178
|
+
if ReplateCameraView.upperSpheresSet[i] {
|
|
179
|
+
if 72 + i < ReplateCameraView.spheresModels.count {
|
|
180
|
+
let entity = ReplateCameraView.spheresModels[72 + i]
|
|
181
|
+
entity.model?.materials[0] = material
|
|
182
|
+
} else {
|
|
183
|
+
print("[handlePinch] Upper sphere index out of bounds: \(72 + i)")
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if ReplateCameraView.lowerSpheresSet[i] {
|
|
187
|
+
if i < ReplateCameraView.spheresModels.count {
|
|
188
|
+
let entity = ReplateCameraView.spheresModels[i]
|
|
189
|
+
entity.model?.materials[0] = material
|
|
190
|
+
} else {
|
|
191
|
+
print("[handlePinch] Lower sphere index out of bounds: \(i)")
|
|
192
|
+
}
|
|
193
|
+
}
|
|
169
194
|
}
|
|
170
195
|
|
|
196
|
+
// Reset the gesture recognizer's scale to 1 to avoid cumulative scaling
|
|
197
|
+
gestureRecognizer.scale = 1.0
|
|
171
198
|
}
|
|
172
|
-
// Reset the gesture recognizer's scale to 1 to avoid cumulative scaling
|
|
173
|
-
gestureRecognizer.scale = 1.0
|
|
174
199
|
default:
|
|
175
200
|
break
|
|
176
201
|
}
|
|
@@ -247,18 +272,45 @@ class ReplateCameraView: UIView, ARSessionDelegate {
|
|
|
247
272
|
}
|
|
248
273
|
|
|
249
274
|
func createFocusSphere() {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
275
|
+
DispatchQueue.main.async {
|
|
276
|
+
// Generate the sphere mesh
|
|
277
|
+
let sphereMesh = MeshResource.generateSphere(radius: ReplateCameraView.sphereRadius * 1.5)
|
|
278
|
+
|
|
279
|
+
// Create the sphere entity with initial material
|
|
280
|
+
let sphereEntity = ModelEntity(mesh: sphereMesh, materials: [SimpleMaterial(color: .white.withAlphaComponent(0.7), roughness: 1, isMetallic: false)])
|
|
281
|
+
|
|
282
|
+
// Set the position for the sphere entity
|
|
283
|
+
sphereEntity.position = SIMD3(x: 0, y: ReplateCameraView.spheresHeight + (ReplateCameraView.distanceBetweenCircles / 2), z: 0)
|
|
284
|
+
|
|
285
|
+
// Update the material of the sphere entity
|
|
286
|
+
sphereEntity.model?.materials = [SimpleMaterial(color: .green.withAlphaComponent(1), roughness: 1, isMetallic: false)]
|
|
287
|
+
|
|
288
|
+
// Set the focus model for the global state
|
|
289
|
+
ReplateCameraView.focusModel = sphereEntity
|
|
290
|
+
|
|
291
|
+
// Safely add the sphere entity to the anchor entity
|
|
292
|
+
ReplateCameraView.anchorEntity?.addChild(sphereEntity)
|
|
293
|
+
}
|
|
256
294
|
}
|
|
257
295
|
|
|
258
296
|
func createSphere(position: SIMD3<Float>) -> ModelEntity {
|
|
297
|
+
// Ensure execution on the main thread
|
|
298
|
+
guard Thread.isMainThread else {
|
|
299
|
+
return DispatchQueue.main.sync {
|
|
300
|
+
return createSphere(position: position)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Generate sphere mesh safely
|
|
259
305
|
let sphereMesh = MeshResource.generateSphere(radius: ReplateCameraView.sphereRadius)
|
|
306
|
+
|
|
307
|
+
// Create sphere entity with the specified material
|
|
260
308
|
let sphereEntity = ModelEntity(mesh: sphereMesh, materials: [SimpleMaterial(color: .white.withAlphaComponent(1), roughness: 1, isMetallic: false)])
|
|
309
|
+
|
|
310
|
+
// Set the position for the sphere entity
|
|
261
311
|
sphereEntity.position = position
|
|
312
|
+
|
|
313
|
+
// Return the created sphere entity
|
|
262
314
|
return sphereEntity
|
|
263
315
|
}
|
|
264
316
|
|
|
@@ -619,64 +671,97 @@ class ReplateCameraController: NSObject {
|
|
|
619
671
|
}
|
|
620
672
|
|
|
621
673
|
func updateSpheres(deviceTargetInFocus: Int) -> Bool {
|
|
622
|
-
//
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
return false
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Get the camera's pose
|
|
633
|
-
guard let frame = ReplateCameraView.arView.session.currentFrame else {
|
|
634
|
-
return false
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
let cameraTransform = frame.camera.transform
|
|
638
|
-
|
|
639
|
-
// Calculate the angle between the camera and the anchor
|
|
640
|
-
let angleDegrees = ReplateCameraController.angleBetweenAnchorXAndCamera(anchor: anchorNode, cameraTransform: cameraTransform)
|
|
641
|
-
let sphereIndex = max(Int(round(angleDegrees / 5.0)), 0) % 72 // Ensure sphereIndex stays within 0-71 bounds
|
|
642
|
-
|
|
643
|
-
var mesh: ModelEntity?
|
|
644
|
-
var newAngle = false
|
|
645
|
-
var callback: RCTResponseSenderBlock? = nil
|
|
646
|
-
print("Sphere index \(sphereIndex) - Spheres length \(ReplateCameraView.spheresModels.count)")
|
|
647
|
-
if deviceTargetInFocus == 1 {
|
|
648
|
-
if !ReplateCameraView.upperSpheresSet[sphereIndex] {
|
|
649
|
-
ReplateCameraView.upperSpheresSet[sphereIndex] = true
|
|
650
|
-
ReplateCameraView.photosFromDifferentAnglesTaken += 1
|
|
651
|
-
newAngle = true
|
|
652
|
-
mesh = ReplateCameraView.spheresModels[72 + sphereIndex]
|
|
653
|
-
if ReplateCameraView.upperSpheresSet.allSatisfy({ $0 }) {
|
|
654
|
-
callback = ReplateCameraController.completedUpperSpheresCallback
|
|
655
|
-
ReplateCameraController.completedUpperSpheresCallback = nil
|
|
656
|
-
}
|
|
674
|
+
// Ensure we're on the main thread
|
|
675
|
+
DispatchQueue.main.sync {
|
|
676
|
+
// When the user pinches the screen, spheres are recreated,
|
|
677
|
+
// we have to make sure all spheres have been recreated before proceeding
|
|
678
|
+
if (ReplateCameraView.spheresModels.count < 144) {
|
|
679
|
+
print("[updateSpheres] Spheres not fully initialized. Count: \(ReplateCameraView.spheresModels.count)")
|
|
680
|
+
return false
|
|
657
681
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
682
|
+
|
|
683
|
+
guard let anchorNode = ReplateCameraView.anchorEntity else {
|
|
684
|
+
print("[updateSpheres] No anchor entity found.")
|
|
685
|
+
return false
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Get the camera's pose
|
|
689
|
+
guard let frame = ReplateCameraView.arView.session.currentFrame else {
|
|
690
|
+
print("[updateSpheres] No current frame available.")
|
|
691
|
+
return false
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
let cameraTransform = frame.camera.transform
|
|
695
|
+
|
|
696
|
+
// Calculate the angle between the camera and the anchor
|
|
697
|
+
let angleDegrees = ReplateCameraController.angleBetweenAnchorXAndCamera(anchor: anchorNode, cameraTransform: cameraTransform)
|
|
698
|
+
let sphereIndex = max(Int(round(angleDegrees / 5.0)), 0) % 72 // Ensure sphereIndex stays within 0-71 bounds
|
|
699
|
+
|
|
700
|
+
var mesh: ModelEntity?
|
|
701
|
+
var newAngle = false
|
|
702
|
+
var callback: RCTResponseSenderBlock? = nil
|
|
703
|
+
print("Sphere index \(sphereIndex) - Spheres length \(ReplateCameraView.spheresModels.count)")
|
|
704
|
+
|
|
705
|
+
if deviceTargetInFocus == 1 {
|
|
706
|
+
if sphereIndex >= ReplateCameraView.upperSpheresSet.count {
|
|
707
|
+
print("[updateSpheres] Sphere index out of range. Index: \(sphereIndex), Count: \(ReplateCameraView.upperSpheresSet.count)")
|
|
708
|
+
return false
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if !ReplateCameraView.upperSpheresSet[sphereIndex] {
|
|
712
|
+
ReplateCameraView.upperSpheresSet[sphereIndex] = true
|
|
713
|
+
ReplateCameraView.photosFromDifferentAnglesTaken += 1
|
|
714
|
+
newAngle = true
|
|
715
|
+
|
|
716
|
+
if 72 + sphereIndex >= ReplateCameraView.spheresModels.count {
|
|
717
|
+
print("[updateSpheres] Upper spheresModels index out of range. Index: \(72 + sphereIndex), Count: \(ReplateCameraView.spheresModels.count)")
|
|
718
|
+
return false
|
|
719
|
+
}
|
|
720
|
+
mesh = ReplateCameraView.spheresModels[72 + sphereIndex]
|
|
721
|
+
|
|
722
|
+
if ReplateCameraView.upperSpheresSet.allSatisfy({ $0 }) {
|
|
723
|
+
callback = ReplateCameraController.completedUpperSpheresCallback
|
|
724
|
+
ReplateCameraController.completedUpperSpheresCallback = nil
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
} else if deviceTargetInFocus == 0 {
|
|
728
|
+
if sphereIndex >= ReplateCameraView.lowerSpheresSet.count {
|
|
729
|
+
print("[updateSpheres] Lower sphere index out of range. Index: \(sphereIndex), Count: \(ReplateCameraView.lowerSpheresSet.count)")
|
|
730
|
+
return false
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if !ReplateCameraView.lowerSpheresSet[sphereIndex] {
|
|
734
|
+
ReplateCameraView.lowerSpheresSet[sphereIndex] = true
|
|
735
|
+
ReplateCameraView.photosFromDifferentAnglesTaken += 1
|
|
736
|
+
newAngle = true
|
|
737
|
+
|
|
738
|
+
if sphereIndex >= ReplateCameraView.spheresModels.count {
|
|
739
|
+
print("[updateSpheres] Lower spheresModels index out of range. Index: \(sphereIndex), Count: \(ReplateCameraView.spheresModels.count)")
|
|
740
|
+
return false
|
|
741
|
+
}
|
|
742
|
+
mesh = ReplateCameraView.spheresModels[sphereIndex]
|
|
743
|
+
|
|
744
|
+
if ReplateCameraView.lowerSpheresSet.allSatisfy({ $0 }) {
|
|
745
|
+
callback = ReplateCameraController.completedLowerSpheresCallback
|
|
746
|
+
ReplateCameraController.completedLowerSpheresCallback = nil
|
|
747
|
+
}
|
|
667
748
|
}
|
|
668
749
|
}
|
|
750
|
+
|
|
751
|
+
if let mesh = mesh {
|
|
752
|
+
let material = SimpleMaterial(color: .green, roughness: 1, isMetallic: false)
|
|
753
|
+
mesh.model?.materials[0] = material
|
|
754
|
+
ReplateCameraView.generateImpactFeedback(strength: .light)
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Ensure callback execution doesn't interfere with array access
|
|
758
|
+
guard let validCallback = callback else {
|
|
759
|
+
return newAngle
|
|
760
|
+
}
|
|
761
|
+
validCallback([])
|
|
762
|
+
|
|
763
|
+
return newAngle
|
|
669
764
|
}
|
|
670
|
-
|
|
671
|
-
if let mesh = mesh {
|
|
672
|
-
let material = SimpleMaterial(color: .green, roughness: 1, isMetallic: false)
|
|
673
|
-
mesh.model?.materials[0] = material
|
|
674
|
-
ReplateCameraView.generateImpactFeedback(strength: .light)
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
callback?([])
|
|
678
|
-
|
|
679
|
-
return newAngle
|
|
680
765
|
}
|
|
681
766
|
|
|
682
767
|
static func angleBetweenAnchorXAndCamera(anchor: AnchorEntity, cameraTransform: simd_float4x4) -> Float {
|