diff --git a/TreeMeasure.xcodeproj/project.pbxproj b/TreeMeasure.xcodeproj/project.pbxproj index fff7adf..1c6c410 100644 --- a/TreeMeasure.xcodeproj/project.pbxproj +++ b/TreeMeasure.xcodeproj/project.pbxproj @@ -292,13 +292,17 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 774CCSDT3V; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = TreeMeasure/Info.plist; + INFOPLIST_KEY_NSCameraUsageDescription = "Cam for images"; + INFOPLIST_KEY_NSMotionUsageDescription = "uses motion"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -319,13 +323,17 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 774CCSDT3V; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = TreeMeasure/Info.plist; + INFOPLIST_KEY_NSCameraUsageDescription = "Cam for images"; + INFOPLIST_KEY_NSMotionUsageDescription = "uses motion"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = ""; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = UIInterfaceOrientationPortrait; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/TreeMeasure/Base.lproj/Main.storyboard b/TreeMeasure/Base.lproj/Main.storyboard index 25a7638..2ff2740 100644 --- a/TreeMeasure/Base.lproj/Main.storyboard +++ b/TreeMeasure/Base.lproj/Main.storyboard @@ -1,24 +1,146 @@ - + + - + + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TreeMeasure/ViewController.swift b/TreeMeasure/ViewController.swift index 3e36c9b..5acbdb2 100644 --- a/TreeMeasure/ViewController.swift +++ b/TreeMeasure/ViewController.swift @@ -6,14 +6,223 @@ // import UIKit +import AVFoundation +import CoreMotion class ViewController: UIViewController { - + + @IBOutlet weak var textView: UITextView! + @IBOutlet weak var inputRefHeight: UITextField! + @IBOutlet weak var statusText: UITextView! + @IBOutlet weak var centerButton: UIButton! + @IBOutlet weak var previewView: UIView! + @IBOutlet var mainView: UIView! + + // Cam Variables + var captureSession: AVCaptureSession! + var videoPreviewLayer: AVCaptureVideoPreviewLayer! + var globalQueue: OperationQueue! + var backCamera: AVCaptureDevice! + + // VariablesForCalculations + var globalDegrees: Double! + var globalAttitude: Double! + var globalGravityAngle: Double! + var topPole: Double! + var bottomPole: Double! + var topTree: Double! + + // Motion Variables + var motionManager: CMMotionManager! + override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. + + // init motion manager + motionManager = CMMotionManager(); + + // init vars for calculations + topPole = 0; + bottomPole = 0; + topTree = 0; + + statusText.text = "To start, please snapshot the top of the reference height." + + // enable tap out of edit for ref. heigth + let tap = UITapGestureRecognizer(target: self, action: #selector(UIInputViewController.dismissKeyboard)) + tap.cancelsTouchesInView = false + view.addGestureRecognizer(tap) } + + @objc func dismissKeyboard() { + view.endEditing(true) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + // start and prepare cam related things + captureSession = AVCaptureSession() + backCamera = AVCaptureDevice.default(for: AVMediaType.video) + + do { + let input = try AVCaptureDeviceInput(device: backCamera) + if(captureSession.canAddInput(input)) { + captureSession.addInput(input) + setupLivePreview() + } + + } + catch let error { + print("Error Unable to initialize back camera: \(error.localizedDescription)") + } + + // start monitoring the motion sensor + startMotionMonitor(); + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + self.captureSession.stopRunning() + motionManager.stopDeviceMotionUpdates() + } + + @IBAction func addMarker() { + + let l = Double(inputRefHeight.text ?? "1.0") ?? 1.0 + + if(topPole == 0) { + topPole = globalAttitude + + if globalGravityAngle < 0 { + topPole = globalAttitude * -1 + } + + statusText.text = "Snaphost Angle: " + String(format: "%.2f", globalDegrees) + "°.\nNext, snapshot the bottom of the reference height." + return; + } + + if(bottomPole == 0) { + bottomPole = globalAttitude + + if globalGravityAngle < 0 { + bottomPole = bottomPole * -1 + } + statusText.text = "Snapshot Angle: " + String(format: "%.2f", globalDegrees) + "°\nFinally, snapshot the top of the tree." + return; + } + + if(topTree == 0) { + topTree = globalAttitude + if globalGravityAngle < 0 { + topTree = topTree * -1 + } + } + + if((topPole != 0) && (bottomPole != 0) && (topTree != 0)) { + let alpha3 = topPole - Double.pi / 2 + let alpha2 = bottomPole - Double.pi / 2 + let alpha1 = topTree - Double.pi / 2 + let e = abs(l / ( tan(alpha3) - tan(alpha2))) + + let h1 = e * tan(alpha1) + let h2 = e * tan(alpha2) + + + let h = abs(h1 - h2) + statusText.text = "Height: " + String( format: "%.4f", h) + " m\nSnapshot top of the reference height to measure again." + + //reset + topTree = 0 + bottomPole = 0 + topPole = 0 + } + + } + + @IBAction func changeZoom() { + if(backCamera.videoZoomFactor > 1) { + setZoomFactor(factor: backCamera.videoZoomFactor + 10) + } else { + setZoomFactor(factor: backCamera.maxAvailableVideoZoomFactor) + } + } + + @IBAction func zoomOut() { + if(backCamera.videoZoomFactor > 1) { + setZoomFactor(factor: backCamera.videoZoomFactor - 1) + } + } + + @IBAction func zoomIn() { + if(backCamera.videoZoomFactor < backCamera.maxAvailableVideoZoomFactor) { + setZoomFactor(factor: backCamera.videoZoomFactor + 1) + } + } + + func setZoomFactor(factor: CGFloat) { + do { + try backCamera.lockForConfiguration() + if(factor <= backCamera.maxAvailableVideoZoomFactor) { + backCamera.videoZoomFactor = factor + } + + backCamera.unlockForConfiguration() + } catch let error { + print("Error Unable to set zoom for back camera: \(error.localizedDescription)") + } + + } + + func startMotionMonitor() { + globalQueue = OperationQueue() + if motionManager.isDeviceMotionAvailable == true { + motionManager.deviceMotionUpdateInterval = 0.5 + motionManager.startDeviceMotionUpdates(using: .xMagneticNorthZVertical, to: self.globalQueue, withHandler: { (data, error) in + + if let attitude = data?.attitude { + // Get the pitch (in radians) and convert to degrees for display. + self.globalDegrees = (attitude.pitch * 180.0/Double.pi) + self.globalAttitude = attitude.pitch + + DispatchQueue.main.async { + self.textView.text = "Angle: " + String(format: "%.2f", self.globalDegrees) + "°"; + } + } + + if let gravity = data?.gravity { + + self.globalGravityAngle = gravity.z; + } + + // we are looking up! + if self.globalGravityAngle > 0 { + self.globalDegrees = 180 - self.globalDegrees; + } + }) + + + } + } + + + + func setupLivePreview() { + + videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession) + + videoPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill + videoPreviewLayer.connection?.videoOrientation = .portrait + previewView.layer.addSublayer(videoPreviewLayer) + + DispatchQueue.global(qos: .userInitiated).async { //[weak self] in + self.captureSession.startRunning() + DispatchQueue.main.async { + self.videoPreviewLayer.frame = self.previewView.bounds + } + } + } }