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.captureAndProcessImage(callbackHandler: callbackHandler)
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
- private func checkCameraDistance(deviceTargetInfo: DeviceTargetInfo) {
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
- break
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replate-camera",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Camera component for Replate Manager",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",