replate-camera 0.5.1 → 0.6.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.
|
@@ -4,6 +4,7 @@ import UIKit
|
|
|
4
4
|
import AVFoundation
|
|
5
5
|
import ImageIO
|
|
6
6
|
import MobileCoreServices
|
|
7
|
+
import CoreMotion
|
|
7
8
|
|
|
8
9
|
// MARK: - RCTViewManager
|
|
9
10
|
@objc(ReplateCameraViewManager)
|
|
@@ -53,6 +54,8 @@ class ReplateCameraView: UIView, ARSessionDelegate {
|
|
|
53
54
|
static var anchorEntity: AnchorEntity?
|
|
54
55
|
static var model: Entity!
|
|
55
56
|
static var sessionId: UUID!
|
|
57
|
+
static var motionManager: CMMotionManager!
|
|
58
|
+
static var gravityVector: [String : Double] = [:]
|
|
56
59
|
static var INSTANCE: ReplateCameraView!
|
|
57
60
|
|
|
58
61
|
// Scene Configuration
|
|
@@ -485,6 +488,7 @@ class ReplateCameraView: UIView, ARSessionDelegate {
|
|
|
485
488
|
spheresHeight = 0.10
|
|
486
489
|
dragSpeed = 7000
|
|
487
490
|
dotAnchors.removeAll()
|
|
491
|
+
gravityVector = [:]
|
|
488
492
|
}
|
|
489
493
|
|
|
490
494
|
private static func setupNewARView() {
|
|
@@ -494,6 +498,12 @@ class ReplateCameraView: UIView, ARSessionDelegate {
|
|
|
494
498
|
arView.backgroundColor = instance.hexStringToUIColor(hexColor: "#32a852")
|
|
495
499
|
instance.addSubview(arView)
|
|
496
500
|
arView.session.delegate = instance
|
|
501
|
+
motionManager = CMMotionManager()
|
|
502
|
+
if motionManager.isDeviceMotionAvailable {
|
|
503
|
+
ReplateCameraView.INSTANCE.startDeviceMotionUpdates()
|
|
504
|
+
} else {
|
|
505
|
+
print("Device motion is not available")
|
|
506
|
+
}
|
|
497
507
|
setupAR()
|
|
498
508
|
}
|
|
499
509
|
|
|
@@ -567,10 +577,12 @@ class ReplateCameraView: UIView, ARSessionDelegate {
|
|
|
567
577
|
|
|
568
578
|
func sessionWasInterrupted(_ session: ARSession) {
|
|
569
579
|
print("SESSION INTERRUPTED")
|
|
580
|
+
ReplateCameraView.motionManager.stopDeviceMotionUpdates()
|
|
570
581
|
}
|
|
571
582
|
|
|
572
583
|
func sessionInterruptionEnded(_ session: ARSession) {
|
|
573
584
|
print("SESSION RESUMED")
|
|
585
|
+
ReplateCameraView.INSTANCE.startDeviceMotionUpdates()
|
|
574
586
|
}
|
|
575
587
|
|
|
576
588
|
func generateImpactFeedback(strength: UIImpactFeedbackGenerator.FeedbackStyle) {
|
|
@@ -582,6 +594,21 @@ class ReplateCameraView: UIView, ARSessionDelegate {
|
|
|
582
594
|
print("Error when sending feedback")
|
|
583
595
|
}
|
|
584
596
|
}
|
|
597
|
+
|
|
598
|
+
func startDeviceMotionUpdates() {
|
|
599
|
+
ReplateCameraView.motionManager.deviceMotionUpdateInterval = 0.1 // Update interval in seconds
|
|
600
|
+
ReplateCameraView.motionManager.startDeviceMotionUpdates(to: .main) { (deviceMotion, error) in
|
|
601
|
+
if let deviceMotion = deviceMotion {
|
|
602
|
+
let gravity = deviceMotion.gravity
|
|
603
|
+
ReplateCameraView.gravityVector = [
|
|
604
|
+
"x": gravity.x,
|
|
605
|
+
"y": gravity.y,
|
|
606
|
+
"z": gravity.z
|
|
607
|
+
]
|
|
608
|
+
print("Gravity vector: x = \(gravity.x), y = \(gravity.y), z = \(gravity.z)")
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
585
612
|
|
|
586
613
|
}
|
|
587
614
|
|
|
@@ -598,6 +625,7 @@ enum ARError: Error {
|
|
|
598
625
|
case savingError
|
|
599
626
|
case transformError
|
|
600
627
|
case lightingError
|
|
628
|
+
case notInRange
|
|
601
629
|
case unknown
|
|
602
630
|
|
|
603
631
|
var localizedDescription: String {
|
|
@@ -611,6 +639,7 @@ enum ARError: Error {
|
|
|
611
639
|
case .savingError: return "[ReplateCameraController] Error saving photo"
|
|
612
640
|
case .transformError: return "[ReplateCameraController] Camera transform data not available"
|
|
613
641
|
case .lightingError: return "[ReplateCameraController] Image too dark"
|
|
642
|
+
case .notInRange: return "[ReplateCameraController] Camera not in range"
|
|
614
643
|
case .unknown: return "[ReplateCameraController] Unknown error occurred"
|
|
615
644
|
}
|
|
616
645
|
}
|
|
@@ -875,8 +904,25 @@ class ReplateCameraController: NSObject {
|
|
|
875
904
|
guard let self = self else { return }
|
|
876
905
|
|
|
877
906
|
self.updateCircleFocus(targetIndex: deviceTargetInfo.targetIndex)
|
|
878
|
-
self.checkCameraDistance(deviceTargetInfo: deviceTargetInfo)
|
|
879
|
-
|
|
907
|
+
let isInRange = self.checkCameraDistance(deviceTargetInfo: deviceTargetInfo)
|
|
908
|
+
if !isInRange {
|
|
909
|
+
callbackHandler.reject(.notInRange)
|
|
910
|
+
return
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
guard let frame = ReplateCameraView.arView?.session.currentFrame else {
|
|
914
|
+
callbackHandler.reject(.captureError)
|
|
915
|
+
return
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// Check lighting conditions
|
|
919
|
+
if let lightEstimate = frame.lightEstimate {
|
|
920
|
+
guard lightEstimate.ambientIntensity >= Self.MIN_AMBIENT_INTENSITY else {
|
|
921
|
+
callbackHandler.reject(.lightingError)
|
|
922
|
+
return
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
880
926
|
self.updateSpheres(
|
|
881
927
|
deviceTargetInfo: deviceTargetInfo,
|
|
882
928
|
cameraTransform: deviceTargetInfo.transform
|
|
@@ -886,7 +932,7 @@ class ReplateCameraController: NSObject {
|
|
|
886
932
|
return
|
|
887
933
|
}
|
|
888
934
|
|
|
889
|
-
self.
|
|
935
|
+
self.processAndSaveImage(frame.capturedImage, callbackHandler: callbackHandler)
|
|
890
936
|
}
|
|
891
937
|
}
|
|
892
938
|
}
|
|
@@ -900,7 +946,7 @@ class ReplateCameraController: NSObject {
|
|
|
900
946
|
}
|
|
901
947
|
}
|
|
902
948
|
|
|
903
|
-
|
|
949
|
+
private func checkCameraDistance(deviceTargetInfo: DeviceTargetInfo) -> Bool {
|
|
904
950
|
let distance = isCameraWithinRange(
|
|
905
951
|
cameraTransform: deviceTargetInfo.transform,
|
|
906
952
|
anchorEntity: ReplateCameraView.anchorEntity!
|
|
@@ -909,31 +955,17 @@ class ReplateCameraController: NSObject {
|
|
|
909
955
|
switch distance {
|
|
910
956
|
case 1:
|
|
911
957
|
ReplateCameraController.tooFarCallback?([])
|
|
958
|
+
ReplateCameraController.tooFarCallback = nil
|
|
959
|
+
return false
|
|
912
960
|
case -1:
|
|
913
961
|
ReplateCameraController.tooCloseCallback?([])
|
|
962
|
+
ReplateCameraController.tooCloseCallback = nil
|
|
963
|
+
return false
|
|
914
964
|
default:
|
|
915
|
-
|
|
965
|
+
return true
|
|
916
966
|
}
|
|
917
967
|
}
|
|
918
968
|
|
|
919
|
-
// MARK: - Image Processing
|
|
920
|
-
private func captureAndProcessImage(callbackHandler: SafeCallbackHandler) {
|
|
921
|
-
guard let frame = ReplateCameraView.arView?.session.currentFrame else {
|
|
922
|
-
callbackHandler.reject(.captureError)
|
|
923
|
-
return
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
// Check lighting conditions
|
|
927
|
-
if let lightEstimate = frame.lightEstimate {
|
|
928
|
-
guard lightEstimate.ambientIntensity >= Self.MIN_AMBIENT_INTENSITY else {
|
|
929
|
-
callbackHandler.reject(.lightingError)
|
|
930
|
-
return
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
processAndSaveImage(frame.capturedImage, callbackHandler: callbackHandler)
|
|
935
|
-
}
|
|
936
|
-
|
|
937
969
|
private func processAndSaveImage(_ pixelBuffer: CVPixelBuffer, callbackHandler: SafeCallbackHandler) {
|
|
938
970
|
let ciImage = CIImage(cvImageBuffer: pixelBuffer)
|
|
939
971
|
|
|
@@ -1260,20 +1292,20 @@ class ReplateCameraController: NSObject {
|
|
|
1260
1292
|
let source = CGImageSourceCreateWithData(imageData as CFData, nil) else {
|
|
1261
1293
|
return nil
|
|
1262
1294
|
}
|
|
1263
|
-
|
|
1295
|
+
|
|
1264
1296
|
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
|
|
1265
1297
|
let uniqueFilename = "image_\(Date().timeIntervalSince1970).jpg"
|
|
1266
1298
|
let fileURL = temporaryDirectoryURL.appendingPathComponent(uniqueFilename)
|
|
1267
|
-
|
|
1299
|
+
|
|
1268
1300
|
guard let imageProperties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [CFString: Any] else {
|
|
1269
1301
|
return nil
|
|
1270
1302
|
}
|
|
1271
|
-
|
|
1303
|
+
|
|
1272
1304
|
var mutableMetadata = imageProperties
|
|
1273
1305
|
mutableMetadata[kCGImagePropertyExifDictionary] = [
|
|
1274
1306
|
kCGImagePropertyExifUserComment: getTransformJSON(session: ReplateCameraView.arView.session)
|
|
1275
1307
|
]
|
|
1276
|
-
|
|
1308
|
+
|
|
1277
1309
|
guard let destination = CGImageDestinationCreateWithURL(
|
|
1278
1310
|
fileURL as CFURL,
|
|
1279
1311
|
kUTTypeJPEG,
|
|
@@ -1282,21 +1314,21 @@ class ReplateCameraController: NSObject {
|
|
|
1282
1314
|
) else {
|
|
1283
1315
|
return nil
|
|
1284
1316
|
}
|
|
1285
|
-
|
|
1317
|
+
|
|
1286
1318
|
CGImageDestinationAddImageFromSource(
|
|
1287
1319
|
destination,
|
|
1288
1320
|
source,
|
|
1289
1321
|
0,
|
|
1290
1322
|
mutableMetadata as CFDictionary
|
|
1291
1323
|
)
|
|
1292
|
-
|
|
1324
|
+
|
|
1293
1325
|
guard CGImageDestinationFinalize(destination) else {
|
|
1294
1326
|
return nil
|
|
1295
1327
|
}
|
|
1296
|
-
|
|
1328
|
+
|
|
1297
1329
|
return fileURL
|
|
1298
1330
|
}
|
|
1299
|
-
|
|
1331
|
+
|
|
1300
1332
|
func getTransformJSON(session: ARSession) -> String {
|
|
1301
1333
|
let transform = session.currentFrame?.camera.transform ?? simd_float4x4()
|
|
1302
1334
|
// Extract translation
|
|
@@ -1305,21 +1337,21 @@ class ReplateCameraController: NSObject {
|
|
|
1305
1337
|
"y": transform.columns.3.y,
|
|
1306
1338
|
"z": transform.columns.3.z
|
|
1307
1339
|
]
|
|
1308
|
-
|
|
1340
|
+
|
|
1309
1341
|
// Extract scale by calculating the length of each column vector
|
|
1310
1342
|
let scale = [
|
|
1311
1343
|
"x": simd_length(simd_float3(transform.columns.0.x, transform.columns.0.y, transform.columns.0.z)),
|
|
1312
1344
|
"y": simd_length(simd_float3(transform.columns.1.x, transform.columns.1.y, transform.columns.1.z)),
|
|
1313
1345
|
"z": simd_length(simd_float3(transform.columns.2.x, transform.columns.2.y, transform.columns.2.z))
|
|
1314
1346
|
]
|
|
1315
|
-
|
|
1347
|
+
|
|
1316
1348
|
// Extract rotation by normalizing each axis and converting it into a 3x3 rotation matrix
|
|
1317
1349
|
let rotationMatrix = simd_float3x3(columns: (
|
|
1318
1350
|
simd_float3(transform.columns.0.x / scale["x"]!, transform.columns.0.y / scale["x"]!, transform.columns.0.z / scale["x"]!),
|
|
1319
1351
|
simd_float3(transform.columns.1.x / scale["y"]!, transform.columns.1.y / scale["y"]!, transform.columns.1.z / scale["y"]!),
|
|
1320
1352
|
simd_float3(transform.columns.2.x / scale["z"]!, transform.columns.2.y / scale["z"]!, transform.columns.2.z / scale["z"]!)
|
|
1321
1353
|
))
|
|
1322
|
-
|
|
1354
|
+
|
|
1323
1355
|
// Convert rotation matrix to quaternion
|
|
1324
1356
|
let quaternion = simd_quatf(rotationMatrix)
|
|
1325
1357
|
let rotation = [
|
|
@@ -1328,26 +1360,15 @@ class ReplateCameraController: NSObject {
|
|
|
1328
1360
|
"z": quaternion.vector.z,
|
|
1329
1361
|
"w": quaternion.vector.w
|
|
1330
1362
|
]
|
|
1331
|
-
|
|
1332
|
-
// Get the gravity vector from the AR session
|
|
1333
|
-
var gravityVector: [String: Any] = [:]
|
|
1334
|
-
if let currentFrame = session.currentFrame {
|
|
1335
|
-
let gravityEulerAngles = currentFrame.camera.eulerAngles
|
|
1336
|
-
gravityVector = [
|
|
1337
|
-
"x": gravityEulerAngles.x,
|
|
1338
|
-
"y": gravityEulerAngles.y,
|
|
1339
|
-
"z": gravityEulerAngles.z
|
|
1340
|
-
]
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1363
|
+
|
|
1343
1364
|
// Format as JSON
|
|
1344
1365
|
let jsonObject: [String: Any] = [
|
|
1345
1366
|
"translation": translation,
|
|
1346
1367
|
"rotation": rotation,
|
|
1347
1368
|
"scale": scale,
|
|
1348
|
-
"gravityVector": gravityVector
|
|
1369
|
+
"gravityVector": ReplateCameraView.gravityVector
|
|
1349
1370
|
]
|
|
1350
|
-
|
|
1371
|
+
|
|
1351
1372
|
// Convert dictionary to JSON string
|
|
1352
1373
|
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted),
|
|
1353
1374
|
let jsonString = String(data: jsonData, encoding: .utf8) {
|