iPhone是卖的最好的手机?用Python照样把他玩弄鼓掌之间!

关于 iOS 的技术解读有很多,但是却鲜有设备可视化同步的介绍文章。本文一起了解下这个酷炫的 iOS 黑科技。

 

 

我们的任务很简单——如上图所示,实时获取设备的当前方向。

UIDevice.current.orientation

首先,需要调用

beginGeneratingDeviceOrientationNotifications() 

但仅仅这样还不行。因为如果设备上的旋转被锁定了,那么就不会产生以上通知。我的相机应用程序从头到尾都需要知道方向——所以我意识到我需要直接根据设备的加速度计算方向。

 

好了,现在有了这些值,我们该做些什么呢?这是一个较难的部分。如果将所有内容都输出到控制台,那么我们很快就会被大量数据淹没。我认为还是在屏幕上显示这些值比较好。

但是,等等,如果将数值显示在图表上,会怎么样?别想图表了,我们可以来用开源的 Blender 试试,它可以实现这些值的可视化,并且很容易扩展。

 

 

扫描二维码关注公众号,回复: 1477598 查看本文章

 

 

然而 Blender 并不是很好的代码编辑器,所以我们还是使用钟爱的外部编辑器吧。为了调用外部文件,我们可以将 print("hi") 替换成以下代码:

import bpy
import os
filename = os.path.join(os.path.dirname(bpy.data.filepath), "server.py")
exec(compile(open(filename).read(), filename, 'exec'))

下一步,我们需要在与 .blend 文件相同的文件夹中创建新的 server.py 文件,我们真正的代码就要保存在这里。现在我们可以用任何编辑器打开它,你可以选择 Atom、Sublime,甚至 Word 2007 都行。

 

 

找到该 Cube 对象,点击右键并选择重命名,重命名为 iPhone。现在让我们再来看一看 server.py。

import socket
import select
import json
import threading
import traceback
class ServerThread(threading.Thread):
 def __init__(self):
 threading.Thread.__init__(self)
 self.running = True
 def stopServer(self):
 self.running = False
 self.server.running = False
 def run(self):
 try:
 self.server = Server()
 while self.running:
 self.server.receive()
 except:
 pass
class Server:
 def __init__(self):
 self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 self.socket.setblocking(False)
 self.socket.bind((str(socket.INADDR_ANY), 9845))
 self.socket.listen(2)
 self.running = True
 def __exit__(self, exc_type, exc_value, traceback):
 self.socket.close()
 def receive(self):
 pairs = []
 timeout = 1
 while self.running:
 sockets = list(map(lambda x: x[0], pairs))
 if len(pairs) > 0:
 read_sockets, write_sockets, error_sockets = select.select(sockets, [], [], timeout)
 for sock in read_sockets:
 data = sock.recv(4096)
 if not data :
 print('Client disconnected')
 pairs = []
 else :
 self.connectionReceivedData(connection, data.decode())
 try:
 try:
 connection,address = self.socket.accept()
 print("new connection: ", connection)
 pairs.append((connection, address))
 except:
 pass
 except:
 pass
 for pair in pairs:
 (connection, address) = pair
 connection.close()
 def connectionReceivedData(self, connection, data):
 try:
 motionData = json.loads(data)
 except json.decoder.JSONDecodeError:
 print("Invalid JSON: ", data)
 return None
 receivedMotionData(motionData)
# This is a global so when we run the script again, we can keep the server alive
# but change how it works
import bpy
def receivedMotionData(motionData):
 phone = bpy.context.scene.objects["iPhone"]
 phone.rotation_quaternion.x = float(motionData['x'])
 phone.rotation_quaternion.y = 0 - float(motionData['z'])
 phone.rotation_quaternion.z = float(motionData['y'])
 phone.rotation_quaternion.w = float(motionData['w'])
 pass
try:
 if serverThread.running == False:
 serverThread = ServerThread()
 serverThread.start()
 print("Starting server")
 else:
 print("Server already running, using new motion handler.")
except:
 serverThread = ServerThread()
 serverThread.start()
 print("Starting server")

 

检查 Blender,你应该看到 iPhone 根本没有改变。这是因为上面的脚本使用四元组设置了 iPhone 的旋转角度,并且它使用了欧拉角进行旋转。需要做一些修改。将 Python 控制台面切换到 “Properties”,然后单击该面板顶部的橙色立方体图标。中部 Transform 的下面,点击 XYZ Euler 并选择 Quaternion。现在尝试再次运行 client.py。

你应该看到 iPhone 立即翻转过来了。不要惊慌,这就是我们想要的。现在,我们需要让这个模型跟着实际的 iPhone 旋转。

 

我们需要将运动数据从 iPhone 发送到运行 Blender 的计算机。感谢上苍我们不需要深入到 Swift 中的原始 C 套接字级别,因为 Foundation 具有抽象。

我们可以将以下代码放入新的 iOS 项目中,以替换默认的 ViewController。请确保使用计算机的本地 IP 地址替换 host 变量。

import UIKit
import CoreMotion
class CoreMotionViewController: UIViewController, StreamDelegate {
 let motionManager = CMMotionManager()
 let queue = OperationQueue()
 let host = "192.168.1.2"
 override func viewDidLoad() {
 super.viewDidLoad()
 setUpStreams(host: host)
 motionManager.startDeviceMotionUpdates(to: queue) { (data: CMDeviceMotion?, error: Error?) in
 guard let data = data else {
 print("Error: \(error!)")
 return
 }
 let attitude: CMAttitude = data.attitude
 let quaternion = attitude.quaternion
 var motionData = MotionData()
 motionData.x = quaternion.x
 motionData.y = quaternion.y
 motionData.z = quaternion.z
 motionData.w = quaternion.w
 let encoder = JSONEncoder()
 do {
 let json = try encoder.encode(motionData)
 self.send(data: json)
 } catch let error {
 print("Couldn't send data, error: \(error)")
 }
 }
 }
 // MARK: - Streams
 var inputStream: InputStream?
 var outputStream: OutputStream?
 func setUpStreams(host: String) {
 var readStream: Unmanaged<CFReadStream>?
 var writeStream: Unmanaged<CFWriteStream>?
 CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
 host as CFString, 9845,
 &readStream,
 &writeStream)
 inputStream = readStream!.takeRetainedValue()
 outputStream = writeStream!.takeRetainedValue()
 guard let inputStream = inputStream, let outputStream = outputStream else {
 print("Failed to create streams")
 return
 }
 inputStream.delegate = self
 outputStream.delegate = self
 inputStream.schedule(in: .current, forMode: .commonModes)
 outputStream.schedule(in: .current, forMode: .commonModes)
 inputStream.open()
 outputStream.open()
 }
 func send(data: Data) {
 guard let outputStream = outputStream else {
 return
 }
 _ = data.withUnsafeBytes {
 outputStream.write($0, maxLength: data.count)
 }
 }
 func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
 if eventCode == .errorOccurred {
 inputStream = nil
 outputStream = nil
 print("Error: Stream error")
 } else if eventCode == .endEncountered {
 inputStream = nil
 outputStream = nil
 print("Error: Encountered end of stream")
 }
 let maxReadLength = 4096
 if eventCode == .hasBytesAvailable {
 guard let inputStream = inputStream else {
 return
 }
 while inputStream.hasBytesAvailable {
 let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: maxReadLength)
 inputStream.read(buffer, maxLength: maxReadLength)
 buffer.deallocate()
 }
 }
 }
}
// MARK: - Data Model
private struct MotionData: Codable {
 var x: Double = 0
 var y: Double = 0
 var z: Double = 0
 var w: Double = 0
}

 

 

那么最终我是如何从移动管理器获取方向信息的?

欢迎关注我的博客或者公众号:https://home.cnblogs.com/u/Python1234/ Python学习交流

 欢迎加入我的千人交流学习答疑群:125240963

猜你喜欢

转载自www.cnblogs.com/Python1234/p/9141605.html