feat: Ipad app production readiness, Colony orchestration, Social posting
This commit is contained in:
@@ -3,6 +3,7 @@ import CoreLocation
|
||||
import CoreMotion
|
||||
import SceneKit
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
// MARK: - ARSunOverlayView
|
||||
|
||||
@@ -22,6 +23,13 @@ struct ARSunOverlayView: UIViewRepresentable {
|
||||
|
||||
let config = ARWorldTrackingConfiguration()
|
||||
config.worldAlignment = .gravityAndHeading // north = -Z axis
|
||||
config.planeDetection = [.horizontal, .vertical]
|
||||
if ARWorldTrackingConfiguration.supportsSceneReconstruction(.mesh) {
|
||||
config.sceneReconstruction = .mesh
|
||||
}
|
||||
if ARWorldTrackingConfiguration.supportsFrameSemantics(.sceneDepth) {
|
||||
config.frameSemantics.insert(.sceneDepth)
|
||||
}
|
||||
view.session.run(config)
|
||||
|
||||
context.coordinator.attach(to: view)
|
||||
@@ -46,7 +54,9 @@ struct ARSunOverlayView: UIViewRepresentable {
|
||||
// Scene node containers (replaced on each rebuild)
|
||||
private var arcRootNode = SCNNode()
|
||||
private var currentSunNode = SCNNode()
|
||||
private var measurementRootNode = SCNNode()
|
||||
private var isSceneBuilt = false
|
||||
private var pendingMeasurementPoint: SCNVector3?
|
||||
|
||||
// Fallback timer for CoreMotion-only mode
|
||||
private var fallbackTimer: Timer?
|
||||
@@ -61,6 +71,10 @@ struct ARSunOverlayView: UIViewRepresentable {
|
||||
self.sceneView = sceneView
|
||||
sceneView.scene.rootNode.addChildNode(arcRootNode)
|
||||
sceneView.scene.rootNode.addChildNode(currentSunNode)
|
||||
sceneView.scene.rootNode.addChildNode(measurementRootNode)
|
||||
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(handleMeasurementTap(_:)))
|
||||
sceneView.addGestureRecognizer(tap)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
@@ -107,6 +121,59 @@ struct ARSunOverlayView: UIViewRepresentable {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Measurement
|
||||
|
||||
@objc private func handleMeasurementTap(_ recognizer: UITapGestureRecognizer) {
|
||||
guard let sceneView else { return }
|
||||
let point = recognizer.location(in: sceneView)
|
||||
guard let query = sceneView.raycastQuery(
|
||||
from: point,
|
||||
allowing: .estimatedPlane,
|
||||
alignment: .any
|
||||
),
|
||||
let result = sceneView.session.raycast(query).first else { return }
|
||||
let transform = result.worldTransform
|
||||
let worldPoint = SCNVector3(transform.columns.3.x, transform.columns.3.y, transform.columns.3.z)
|
||||
addMeasurementPoint(worldPoint)
|
||||
}
|
||||
|
||||
private func addMeasurementPoint(_ point: SCNVector3) {
|
||||
measurementRootNode.addChildNode(makeMeasurementMarker(at: point))
|
||||
|
||||
if let start = pendingMeasurementPoint {
|
||||
let distance = start.distance(to: point)
|
||||
measurementRootNode.addChildNode(makeLineNode(through: [start, point], color: UIColor.white.withAlphaComponent(0.82)))
|
||||
|
||||
let midpoint = SCNVector3(
|
||||
(start.x + point.x) / 2,
|
||||
(start.y + point.y) / 2 + 0.045,
|
||||
(start.z + point.z) / 2
|
||||
)
|
||||
let label = makeTextNode(
|
||||
text: "\(String(format: "%.2f m", Double(distance))) \(String(format: "%.1f ft", Double(distance * 3.28084)))",
|
||||
color: .white,
|
||||
fontSize: 0.052
|
||||
)
|
||||
label.position = midpoint
|
||||
measurementRootNode.addChildNode(label)
|
||||
pendingMeasurementPoint = nil
|
||||
UIImpactFeedbackGenerator(style: .rigid).impactOccurred()
|
||||
} else {
|
||||
pendingMeasurementPoint = point
|
||||
UIImpactFeedbackGenerator(style: .soft).impactOccurred()
|
||||
}
|
||||
}
|
||||
|
||||
private func makeMeasurementMarker(at position: SCNVector3) -> SCNNode {
|
||||
let sphere = SCNSphere(radius: 0.018)
|
||||
sphere.firstMaterial?.diffuse.contents = UIColor.white
|
||||
sphere.firstMaterial?.emission.contents = UIColor.systemBlue.withAlphaComponent(0.65)
|
||||
sphere.firstMaterial?.lightingModel = .constant
|
||||
let node = SCNNode(geometry: sphere)
|
||||
node.position = position
|
||||
return node
|
||||
}
|
||||
|
||||
// MARK: - Scene Building
|
||||
|
||||
private func buildScene() {
|
||||
@@ -274,3 +341,12 @@ struct ARSunOverlayView: UIViewRepresentable {
|
||||
private extension Double {
|
||||
var radians: Double { self * .pi / 180.0 }
|
||||
}
|
||||
|
||||
private extension SCNVector3 {
|
||||
func distance(to other: SCNVector3) -> Float {
|
||||
let dx = other.x - x
|
||||
let dy = other.y - y
|
||||
let dz = other.z - z
|
||||
return sqrtf(dx * dx + dy * dy + dz * dz)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user