福野泰介の一日一創 - create every day

見る人すべてがネコミミになるメガネ「nekomimiworld」の作り方、一番好きな三角関数は何ですか?

2018/07/14
#swift #megane 

見る人すべてがめがねをかけて見える「meganeworld
MR(Mixed Reality)による近未来、たった800円のスマホVRで開発可能です。

見えている人が「かわいい」と、人はやさしくなれるかも?

安直な「かわいい」の実現「ネコミミ」を見ている人側で勝手に付加するMRアプリを作ってみましょう。

Hana道場、若宮さんと学ぶIoTとIchigoJamプログラミングと地域フィールドラボ第5期スタート!」より

GitHubにあるiPhone用、プログラミング言語Swiftで書かれた、「meganeworld - megane」 にネコミミ表示を足します。 右目位置、左目位置を、顔認識APIから受け取って、三角関数を使って角度を求め、ネコミミ位置を計算し、黒の三角形を描画!(プログラム全文はGitHubで

// nekomimi let dx = left.x - right.x let dy = left.y - right.y let len = sqrt(dx * dx + dy * dy) let th = atan2(dy, dx); let ox = [ right.x, left.x ] let oy = [ right.y, left.y ] for n in 0...1 { let dir = CGFloat(n == 0 ? 1 : -1) let deg0 = 90 + 20 * dir let th0 = th + CGFloat.pi / 180 * CGFloat(deg0) let len0 = len * 1.3 let x0 = ox[n] + cos(th0) * len0 let y0 = oy[n] + sin(th0) * len0 g.beginPath() g.setLineWidth(4) g.setFillColor(UIColor.white.cgColor) let th1 = th0 + CGFloat.pi / 180 * 20 * dir let len1 = len * 0.4 let x1 = x0 + cos(th1) * len1 let y1 = y0 + sin(th1) * len1 g.move(to:CGPoint(x:x1, y:y1)) let th2 = th1 + CGFloat.pi / 180 * 120 * dir let len2 = len * 0.6 let x2 = x0 + cos(th2) * len2 let y2 = y0 + sin(th2) * len2 g.addLine(to:CGPoint(x:x2, y:y2)) let th3 = th1 - CGFloat.pi / 180 * 120 * dir let len3 = len * 0.6 let x3 = x0 + cos(th3) * len3 let y3 = y0 + sin(th3) * len3 g.addLine(to:CGPoint(x:x3, y:y3)) g.addLine(to:CGPoint(x:x1, y:y1)) g.fillPath() }

MSX BASICの三角関数であれこれ遊んだ小中学生時代。
シューティングゲームや、優雅な動き、3Dプログラミングなど、よく出てくるので遊びながら覚えちゃいます。
一番好きな三角関数は、atan2こと、yとxをパラメータとして渡す逆タンジェント(Arctangent)
atan(y / x) として使うため、引数の順序も普通じゃない atan2(y, x) というのもお気に入り。

Swiftでプログラミングしていて驚いたコンパイルエラー
「Expression was too complex to be solved in reasonable time; ...」
IchigoJamのも計算用のスタックに多くのメモリを使えないため「Too complex」というエラーがありますが、現代言語でも目にかかるとは!

IchigoJamと同様、式を分割するか、型の明確化で対応が可能しましょう。

links
- Ichigojam Basicで円を描く – chobitte
- 2015-08-26 [IchigoJam]円を描く - SHIROのモバイル日記
- 三角関数が好き sin cos tan

Welcome to Megane World! めがねxIT at めがねフェス2018

2018/06/08
#megane #vr #swift 

Welcome to Megane World! めがねをかけないと損する時代は、もうまもなく!

一足先にスマホMRで体験、見えている人、全員が赤いメガネをかける世界。


6/9-10の2日間はステキなメガネに会える「めがねフェス2018
ステージ、グルメ、ショップ、ワークショップなど、イベント盛りだくさん!隣のめがね会館へもどうぞ!


オススメは800円から買えちゃう、各種スマホVR、ぜひ試着してお気に入りのVRを手に入れてください。
一段上のVR体験、Oculus Go も展示!
発売開始から3分で完売 Oculus Go中国国内モデル、入荷待ちは5万人以上 | Mogura VR - 国内外のVR/AR/MR最新情報


めがねxIT」ブースで体験できるメガネアプリ開発者は鯖江在住の現役高校生プログラマー!
ITの基本、プログラミングを子供も大人も学べる格安パソコン「IchigoJam」の体験コーナーもあります。限定品の販売も!?


めがねのまちさばえ!

links
- めがねフェス2018

QRコードが読め、ズームでき、写真が撮れるメガネ開発!オープンソースなスマホMR in Swift4

2018/05/26
#swift 

外カメラ x スマホVR = デジタルメガネ

JSで手軽なWebMeganeもオススメながら、iPhoneネイティブアプリとして作ると、顔認識や、QRコード認識などが高速で楽しい!

リアルタイムにQRコード内容をデコードして、視界をオーバーレイ(上書き)するメガネができた。
megane - qrdetector on GitHub」 Swift4で実装

ウィンクシャッターが便利だったGoogle Glassのように、手軽に写真撮影もついでに実装。
ボリュームダウンキーで、シャッター(初回のみダイアログがでるので、VRにする前に押しておこう)
ボリュームアップキーで、視界をズームすることもできる(現在は、1920x1080の画像をデジタルズーム)

こちら「オタマートで売ってるVRゴーグル」の一番安いもの。ボリュームアップダウンが上からアクセスできる!
ボリュームアップダウンキーのついたイヤホンを接続すると、手元でも操作できるようになって便利。
デジタルメガネなUXづくり、いろいろ試していきましょう。

鯖江の道の駅、西山公園前のバス停。日本語表記しか無い標識にQRコードをつけておけば、リアルタイムに見ている人の母国語に変換できそう。

Let's hack your sight!

AppDelegate.swift

import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { return true } func applicationWillResignActive(_ application: UIApplication) { } func applicationDidEnterBackground(_ application: UIApplication) { NotificationCenter.default.post(name: NSNotification.Name(rawValue: "applicationDidEnterBackground"), object: nil) } func applicationWillEnterForeground(_ application: UIApplication) { NotificationCenter.default.post(name: NSNotification.Name(rawValue: "applicationWillEnterForeground"), object: nil) } func applicationDidBecomeActive(_ application: UIApplication) { } func applicationWillTerminate(_ application: UIApplication) { } }

ViewController.swift

// // ViewController.swift // megane, QR code detector glass with VR goggle // // CC BY taisukef on 2018/05/24. // http://fukuno.jig.jp/2133 // import UIKit import AVFoundation import MediaPlayer class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate { override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = UIColor.black self.imageView1 = UIImageView() self.imageView2 = UIImageView() let w = self.view.frame.width let h = self.view.frame.height let w2 = w / 2 let h2 = w2 * 1080 / 1920 let y = (h - h2) / 2 self.imageView1.frame = CGRect(x:0, y:y, width:w2, height:h2) self.imageView2.frame = CGRect(x:self.view.frame.width / 2, y:y, width:w2, height:h2) self.view.addSubview(self.imageView1) self.view.addSubview(self.imageView2) self.initNotificationsFromAppDelegate() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } var input:AVCaptureDeviceInput! var output:AVCaptureVideoDataOutput! var session:AVCaptureSession! var camera:AVCaptureDevice! var imageView1:UIImageView! var imageView2:UIImageView! override func viewWillAppear(_ animated: Bool) { self.configureCamera() self.listenVolumeButton() } // notifications foreground and background func initNotificationsFromAppDelegate() { NotificationCenter.default.addObserver(self, selector: #selector(type(of: self).viewWillEnterForeground(notification:)), name: NSNotification.Name(rawValue: "applicationWillEnterForeground"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(type(of: self).viewDidEnterBackground(notification:)), name: NSNotification.Name(rawValue: "applicationDidEnterBackground"), object: nil) } @objc func viewWillEnterForeground(notification: NSNotification?) { print("foreground") self.listenVolumeButton() } @objc func viewDidEnterBackground(notification: NSNotification?) { print("background") self.removeVolumeButton() } // override func viewDidDisappear(_ animated: Bool) { session.stopRunning() for output in session.outputs { session.removeOutput(output) } for input in session.inputs { session.removeInput(input) } session = nil camera = nil } let DETECT_QRCODE = false func configureCamera() { session = AVCaptureSession() // iPhone Xで実験 //session.sessionPreset = AVCaptureSession.Preset.cif352x288 // 34% 荒い //session.sessionPreset = AVCaptureSession.Preset.vga640x480 // 47% 4:3 なかなかきれい //session.sessionPreset = AVCaptureSession.Preset.iFrame1280x720 // CPU50% 16:9 かわらない? //session.sessionPreset = AVCaptureSession.Preset.hd1280x720 // CPU50% 16:9 きれい session.sessionPreset = AVCaptureSession.Preset.hd1920x1080 // CPU88% 16:9 かわらない? iPhone6でもQRcode offならOK! //session.sessionPreset = AVCaptureSession.Preset.hd4K3840x2160 // CPU93% 16:9 かわらない? QRcode offなら実用的 camera = AVCaptureDevice.default( AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType.video, position: .back) // position: .front do { input = try AVCaptureDeviceInput(device: camera) } catch let error as NSError { print(error) } if (session.canAddInput(input)) { session.addInput(input) } output = AVCaptureVideoDataOutput() // AVCapturePhotoOutput() 写真用 output?.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable : Int(kCVPixelFormatType_32BGRA)] as! [String : Any] let queue:DispatchQueue = DispatchQueue(label: "myqueue", attributes: .concurrent) output.setSampleBufferDelegate(self, queue: queue) output.alwaysDiscardsLateVideoFrames = true // 間に合わないものは処理しない if (session.canAddOutput(output)) { session.addOutput(output) } session.startRunning() } var zoom:CGFloat = 1.0 func captureOutput(_: AVCaptureOutput, didOutput: CMSampleBuffer, from: AVCaptureConnection) { // from.videoOrientation = .portrait //デバイスの向きを設定、縦の時 from.videoOrientation = .landscapeLeft //デバイスの向きを設定、landscape left の時 DispatchQueue.main.sync(execute: { var image = self.imageFromSampleBuffer(sampleBuffer: didOutput) image = resizeImage(image: image, ratio: zoom) if DETECT_QRCODE { image = drawQR(image: image) } self.imageView1.image = image self.imageView2.image = image }) } func resizeImage(image: UIImage, ratio: CGFloat) -> UIImage { if ratio == 1.0 { return image } let iw = image.size.width / ratio let ih = image.size.height / ratio let size = CGSize(width: iw, height: ih) UIGraphicsBeginImageContext(size) image.draw(in: CGRect(origin: CGPoint(x:-(image.size.width - iw) / 2, y:-(image.size.height - ih) / 2), size: image.size)) let resimage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return resimage } func drawQR(image: UIImage) -> UIImage { UIGraphicsBeginImageContext(image.size) let rect = CGRect(x:0, y:0, width:image.size.width, height:image.size.height) image.draw(in: rect) let g = UIGraphicsGetCurrentContext()! g.setStrokeColor(UIColor.white.cgColor) g.setLineWidth(1) let font = UIFont.boldSystemFont(ofSize: 14) let textStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle let textFontAttributes = [ NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: UIColor.black, NSAttributedStringKey.paragraphStyle: textStyle ] // 顔認識もおもしろい // let detector : CIDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options:[CIDetectorAccuracy: CIDetectorAccuracyLow] )! // 読める四角は今のところひとつだけ // let detector : CIDetector = CIDetector(ofType: CIDetectorTypeRectangle, context: nil, options:[CIDetectorAccuracy: CIDetectorAccuracyHigh, CIDetectorAspectRatio: 1.0] )! let detector : CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options:[CIDetectorAccuracy: CIDetectorAccuracyHigh] )! let features : NSArray = detector.features(in: CIImage(image: image)!) as NSArray if features.count > 0 { for feature in features as! [CIQRCodeFeature] { var rect: CGRect = (feature as AnyObject).bounds rect.origin.y = image.size.height - rect.origin.y - rect.size.height // QRコードを上書き! g.beginPath() g.setFillColor(UIColor.white.cgColor) g.addRect(rect) g.fillPath() feature.messageString?.draw(in: rect, withAttributes: textFontAttributes) } } let resimage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return resimage! } func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> UIImage { let imageBuffer: CVImageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0)) let baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0) let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) let width = CVPixelBufferGetWidth(imageBuffer) let height = CVPixelBufferGetHeight(imageBuffer) let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = (CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) let context = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) let imageRef = context!.makeImage() CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0)) return UIImage(cgImage: imageRef!) } // volume switch var initialVolume = 0.0 var volumeView: MPVolumeView? func listenVolumeButton() { volumeView = MPVolumeView(frame: CGRect(x:-3000, y:0, width:0, height:0)) view.addSubview(volumeView!) let audioSession = AVAudioSession.sharedInstance() do { try audioSession.setActive(true) let vol = audioSession.outputVolume initialVolume = Double(vol.description)! if initialVolume > 0.9 { initialVolume = 0.9 } else if initialVolume < 0.1 { initialVolume = 0.1 } } catch { print("error: \(error)") } audioSession.addObserver(self, forKeyPath: "outputVolume", options: NSKeyValueObservingOptions.new, context: nil) } func removeVolumeButton() { AVAudioSession.sharedInstance().removeObserver(self, forKeyPath: "outputVolume") volumeView?.removeFromSuperview() } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "outputVolume" { let volume = (change?[NSKeyValueChangeKey.newKey] as! NSNumber).floatValue let newVolume = Double(volume) if newVolume > initialVolume + 0.05 { if let view = volumeView?.subviews.first as? UISlider { // volume up pressed view.value = Float(initialVolume) if zoom < 12.0 { zoom *= 1.2 } else { zoom = 1.0 } print("zoom: \(zoom)") } } else if newVolume < initialVolume - 0.05 { if let view = volumeView?.subviews.first as? UISlider { // volume down pressed view.value = Float(initialVolume) /* if zoom > 1.0 { zoom /= 1.2 } */ if let image = self.imageView1.image { saveImage(image: image) print("save image") } } } } } // save private func saveImage(image: UIImage) { UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil) } }

megane - qrdetector on GitHub」 in Swift4

links
- WebMegane - your new digital sight / デジタルな視界 on iPhone Safari x VR goggles (JS、webアプリ版)

Swift4でつくるQRコードをリアルタイムに読むアプリ

2018/05/24
#swift #megane 

iPhoneの本当のおもしろさはプログラミングしないと分からない。
画像認識APIのひとつ、QRコード認識を使って、リアルタイムにQRコードが読めるアプリを作ってみました。

QRコードを認識すると、埋め込まれている文字として上書きします。
(QRコード生成アプリ qrmakeriPhone風スマホ画像素材使用)

Swift4で書く、QRコードの認識プログラムは簡単!カメラからの画像を CIDetector に渡すだけ。
画面内の複数のQRコードをまとめて認識してくれるのがおもしろい!

let detector : CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options:[CIDetectorAccuracy: CIDetectorAccuracyHigh] )! let features : NSArray = detector.features(in: ciimage) as NSArray

まちの標識の先頭につけておき、その人が分かる文字にリアルタイムに、視界を上書きするメガネが簡単にできちゃいそうです。

現実とネットをつなぐインターフェイス、メガネ。
いよいよ実現が近づいてきました!

links
- WebMeganeでMR、JSで手軽に実現、外カメラ映像とCGをミックス!

すぐ作れる力で備える。一日一創のきっかけ、3.11 - Swift4で点字テーブルアプリ

2018/03/11
#swift #braille 

7年前の2011年、必要なものをすぐに作ることができなかった悔しさが、2012年の一日一創の原動力。
あのときには存在しなかったオープンデータが、今は日本中にある。
開発力を持った子供たちがどんどん誕生している。

久々にiPhoneネイティブアプリをSwiftで作ってみる。
Swift入門から3年、当時Swift2だったものはSwift4になっている。
時代に合わせてどんどん変わる言語を使うには、webと自ら学ぶ力、大事。

動作確認を兼ねて、点字を覚えるためのアプリを作成。
点字と書いた点字アイコン。

タップしたら、体表点字化させるなど、何か作ると、追加でいろいろ改造したくなる。


Xcodeの新規プロジェクト作成画面、"Argumented Reality App"を選ぶと、すぐにできちゃうARアプリ。

当初は有料でしか開発できなかったiPhoneアプリ、今は無料なのでMacとiPhoneを持っていれば子供でも気軽に開発可能。
[Xcode][iOS] 有料ライセンスなしでの実機インストール 全工程解説! | Developers.IO
あると楽しい、すぐ作れる力!

点字テーブルアプリのプログラム(ViewController.swift)

// // ViewController.swift // tenji1 // // Created by Taisuke Fukuno on 2018/03/12. in Swift4 // CC BY http://fukuno.jig.jp/ // import UIKit class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { var tableView:UITableView! /* 2800 ⠀ ⠁ ⠂ ⠃ ⠄ ⠅ ⠆ ⠇ ⠈ ⠉ ⠊ ⠋ ⠌ ⠍ ⠎ ⠏ 2810 ⠐ ⠑ ⠒ ⠓ ⠔ ⠕ ⠖ ⠗ ⠘ ⠙ ⠚ ⠛ ⠜ ⠝ ⠞ ⠟ 2820 ⠠ ⠡ ⠢ ⠣ ⠤ ⠥ ⠦ ⠧ ⠨ ⠩ ⠪ ⠫ ⠬ ⠭ ⠮ ⠯ 2830 ⠰ ⠱ ⠲ ⠳ ⠴ ⠵ ⠶ ⠷ ⠸ ⠹ ⠺ ⠻ ⠼ ⠽ ⠾ ⠿ https://ja.wikipedia.org/wiki/%E7%82%B9%E5%AD%97 */ let sections = ["数字", "アルファベット", "ひらがな", "記号"] let data = [ ["1 ⠂", "2 ⠆", "3 ⠒", "4 ⠲", "5 ⠢", "6 ⠖", "7 ⠶", "8 ⠦", "9 ⠔", "0 ⠴" ], ["A ⠁", "B ⠃", "C ⠉", "D ⠙", "E ⠑", "F ⠋", "G ⠛", "H ⠓", "I ⠊", "J ⠚", "K ⠅", "L ⠇", "M ⠍ ", "N ⠝", "O ⠕", "P ⠏", "Q ⠟", "R ⠗", "S ⠎", "T ⠞", "U ⠥", "V ⠧", "W ⠺", "X ⠭", "Y ⠽", "Z ⠵" ], [ "あ ⠁", "い ⠃", "う ⠉", "え ⠖", "お ⠊", "か ⠡", "さ ⠱", "た ⠕", "な ⠅", "は ⠥", "ま ⠵", "や ⠌", "ら ⠑", "わ ⠄", "を ⠔", "ん ⠴" ], [ "数字 ⠼", "アルファベット(外字符) ⠰", "濁音符 ⠐", "半濁音符 ⠠", ] ] override func viewDidLoad() { super.viewDidLoad() tableView = UITableView() tableView.frame = view.frame tableView.delegate = self tableView.dataSource = self tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") tableView.tableFooterView = UIView(frame: .zero) view.addSubview(tableView) } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 40 // セクションヘッダの高さ } func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 20 // セルの高さ } func numberOfSections(in tableView: UITableView) -> Int { return sections.count } func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return sections[section] } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return data[section].count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = data[indexPath.section][indexPath.row] //cell.accessoryType = .disclosureIndicator //cell.accessoryView = UISwitch() // スィッチ return cell } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print(indexPath) // セルタップ時 let alert: UIAlertController = UIAlertController(title: "tenji", message: data[indexPath.section][indexPath.row], preferredStyle: UIAlertControllerStyle.alert) let btnok: UIAlertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: { (action: UIAlertAction!) -> Void in print("OK") }) alert.addAction(btnok) present(alert, animated: true, completion: nil) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }

iPhoneインラインアセンブラ入門、64bit Armマシン語はじめのいっぽ

2016/08/17
#Arm #swift 

IchigoJamもiPhoneも同じArmのCPU、IchigoJamでマシン語を覚えたらiPhoneでも使ってみましょう。今回の対象は、64bit化したArm64(Armv8)対応のiPhone5s以降です。ネットや書籍での資料には32bit時代のものも混在しているので注意が必要です。(iPhone Wikipedia)。

Swiftにはインラインアセンブラ機能はまだないようなので、Objective-CでGameをテンプレートに選び、次の行を viewDidLoad の [super viedDidLoad]; の後に追加します。

__asm__ volatile ("mov x0, x0");

※__asm__ はインラインアセンブラ、volatile で最適化を停止
エミュレーター向けだとコンパイルエラーになります。(IntelがArmライセンス取得とのことなので、MacBookへのArmv8組み込みにも期待!まずはApple-Aの生産から?詳しい解説記事


iPhoneでArm64マシン語、動きました!
(3Dの飛行機はサンプルのままで、今回のアセンブラでの動作とは無関係です)

mov x0, x0 は、レジスタX0の内容をレジスタX0に入れる(つまり、ほぼ何もしない)というマシン語を生成します。やっかいなことにこのコードはMacのiPhoneエミュレーターでは動きません。iPhoneエミュレーターはArmのCPUまでは動かしてくれないようです。実機向けにコンパイルすると動きます。(Arm64では64bitになったレジスタをRではなくXで呼ぶようになった様子)

Objective-Cでの変数との足し算をやってみます。

long num = 1; __asm__ volatile ( "mov x1, #1 \n\t" "add %[n], %[n], x1 \n\t" : [n] "+r" (num) : : "x1" ); NSLog(@"res: %ld\n", n);

コンソールに結果として 2 と表示されます。mov x1,#1 でレジスタX1に1を入れ、add命令で%[n]というコンパイラ任せなレジスタX?にX1の値を加えます。IchigoJamマシン語入門では X1=1 と表記していました。

最初のコロンの後 [n] "+r" (num) で、Objective-Cの変数numを%[n]というレジスタに割り当てて、読み書き"+"するよと指定しています。

三番目のコロンの後の "x1" で、インラインアセンブラ内で X1 レジスタを使用すると宣言して、前後のプログラムと干渉しないようにコンパイラに指定します。

これで基本はOKですね!
より詳しい解説は、こちら「Arm GCC Inline Assembler Cookbook」など参照ください

せっかくなので、アセンブラならでは機能を使った便利な関数を作ってみます。

- (unsigned long)rbit:(unsigned long)n { __asm__ volatile ( "rbit %[n], %[n] \n\t" : [n] "+r" (n) ); return n; }

rbitというのは、64のbit列を反転する命令です。普通に計算すると結構手間ですが、マシン語なら1クロック、0.1nsecで計算します!(参照、Arm64(Armv8) Assembly Programming (08) 分岐命令

iPhone(Armv8)とIchigoJam(Armv6-M)との比較です。

IchigoJamのCortex-M0のThumb-2命令とも互換性あるiPhone6sですが、浮動小数やSIMD命令など性能をフルに活かすならArm64マシン語プログラミングへのステップアップがオススメです!レジスタもたくさんあるので作るのも楽!

参考
- Arm64(Armv8) Assembly Programming (00)(とっても詳しいRAM64解説)
- 連載、IchigoJamではじめる、Armマシン語入門
- 連載、iPhoneゲームづくりからはじめる、Swift入門

スマホグラス、neo-plugがつないだメガネとスマホで目覚ましGlassをプロトタイピング

2016/05/30
#megane #swift #KidsIT 

メガネハッカソンでおもしろかった、スマホメガネ。
寝そうになったら震えるメガネのプロトタイプは、neo-plugと3Dプリンターでスマホをメガネにくっつけていました。


こちら動画へのインデックス
寝そうになったらメガネが震えます
加速度センサーとかついているしスマホをつけちゃえばいいじゃん(デモ直前)

実際試してみようと、傾いたら震えるアプリをiPhone用にSwiftでつくってみました。
MezamashiGlass - GitHub
CMMotionManagerを使った加速度センサーの値取得、AudioToolboxを使ったバイブのサンプルとしてどうぞ!
※背景色を変更しようとしていますが、なぜかうまくいってません!!

import UIKit import CoreMotion import AudioToolbox class ViewController: UIViewController { let motionManager = CMMotionManager() override func viewDidLoad() { super.viewDidLoad() // 加速度センサーON motionManager.accelerometerUpdateInterval = 0.3 // 0.3秒間隔で取得 motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue()) { data, error in guard data != nil else { print("err: \(error)") return } print("x: \(data!.acceleration.x) y: \(data!.acceleration.y) z: \(data!.acceleration.z)") // 加速度に応じて背景色を変える if (abs(data!.acceleration.x) > 0.2) { self.view.backgroundColor = UIColor.redColor() AudioServicesPlayAlertSound(kSystemSoundID_Vibrate); } else { self.view.backgroundColor = UIColor.whiteColor() } } } override func shouldAutorotate() -> Bool { return false // 自動回転OFF } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }

Swift、シンプルでいいですねっ!
2014年から急速に進んだ子供のパソコン離れ、こんなに楽しいツールに触れずに育つなんてもったいない!
小中高校生のパソコンや携帯電話利用率の実態(不破雷蔵) - 個人 - Yahoo!ニュース

「消コレ」以外の3チーム+αの発表動画、こちらです。

neo-plug+スマホのスマホメガネ、夢が広がりますね!
縦に接続すれば高画質カメラがついたメガネもシミュレート可能!
ちょっと軽めな iPhoneSE や、iPod touch が活躍しそう


今日のHana道場にはIntel、SAP、さくらインターネットからのお客様!(photo by Hana道場)


子供たちにも人気なネット接続デバイス、さくらIoT Platformの先行体験できちゃうかも!?

3Dプリンター、スマホアプリ、電子工作などで、わいわい遊ぶ子供たち。
創造する遊びは、最高の学びです!

iPhoneをIchigoJamのキーボードとして使う方法 - konashi(BLE)とSwiftとIchigoJam

2016/05/25
#konashi #IchigoJam #swift 

Bluetooth4.0を使ったお手軽フィジカルコンピューティングを実現するkonashiとの出会いが、電子工作再開のきっかけになりました! スーファミのコントローラーにつなげたり、いろいろやっている内に出会ったマイコンLPC1114、IchigoJam実現へとつながりました。


いよいよ今週末と迫ったメガネハッカソンに向けて、カドマックさん製、konashi互換のKoshianを使ったBLE小型ボードを入手したので早速接続!

以前SwiftでつくりかけていたKonashi接続用プログラムを現状Swiftに合わせてリメイク。konashiでは無事動作しました!(KoshianではなぜかUARTの通信速度設定が効かず難航中)

IchigoJamのコントロールやプログラムが、iPhoneからでもできちゃいます。BluetoothキーボードをiPhoneにつなげば普通にプログラミングもできそうですね!

特殊なキーは、ESCボタンだけつけていますが、どうぞいろいろと改造してみてください!
ソースコード(Swiftのプログラム)は、オープンソース、GitHub「IchigoRemote」に置きました!


新世代のメガネを創る!「メガネハッカソン
塚本教授に講評いただけます!(ウェアラブル新時代セミナー(HMDミーティング in 鯖江)連動)

ひなんメモ、避難所をローカルに保存するiOS9アプリ(Swift)

2016/04/15
#opendata #swift #SPARQL 

ひなんメモ(Webアプリ版)は、Webのオフラインアプリケーションの仕様頼みだったのに、 その機能はすでに廃止予定になっていて、iPhoneのSafariではもう動かなくなっていたので、Swift版つくってみました。(ソース on GitHub / Code for Fukui


現在位置をCLLocationManagerとiOS9で追加されたrequestLocationを使って位置情報を取得、その付近の避難所をodpからSAPRQLを使ってJSONで取得し、UITableViewを使ってリスト表示。 項目タップで地図が開きます。 取得したデータはCoreDataを使って端末内に保存しておき、クリアされない限り保持します。


ひさびさのSwift、以前書いた自分のブログ見てもアシスタントエディターを忘れていたので、図を追加。 実機で動作させようとしたら、バージョンが合わず、MacOSのアップデート、Xcodeのアップデート、設定、ライブラリ、Swiftの文法変更への対応など、なかなか手間取りました。 最近の開発環境は、がんがん更新されるので、初心者のプログラミング学習用途には向きません。
(伝統の++がSwift3では無くなる様子「【Swift3対応】Swift2.2以降のfor文法の書き方まとめ | ゆとりっち」)

実用化するにはまだ遠い「ひなんメモ」ですが、ぜひお好きなように改造しちゃってください!
ひなんメモ、ソース on GitHub / Code for Fukui

PREFIX geo: <http://www.w3.org/2003/01/geo/wgs84_pos#> PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> select ?s ?name ?lat ?lng { ?s rdf:type ; rdfs:label ?name; geo:lat ?lat; geo:long ?lng. filter(?lat<35.95 && ?lat>35.93 && ?lng<136.19 && ?lng>136.17) filter(lang(?name)="ja") }

(緯度経度の範囲を指定して避難所を日本語で取得するSARQLのクエリー)

5分でできるオリジナル鯖江論手川排水機場水位表示iPhoneアプリ / Swift2入門

2015/11/18
#KidsIT #opendata #swift #SPARQL 

5分でつくった前回のアプリ、複数の画面を持つタブアプリにして、河川水位を追加します。 今回は、SPARQLを使いますが、コピペするだけなので、とりあえずつくるだけなら簡単です。


前回は、Single View Application を選びましたが、今回は Tabbed Applictaion を選択します。

FirstViewController には、PM2.5を表示する形にして、SecondViewController に鯖江市からリアルタイムオープンデータとして公開されている「論手川排水機場」の水位を表示させましょう。

まずは、超簡単な SPARQL にアクセスするテストを動かしてみます。viewDidLoad 内に、下記を書いて実行してみます。

let sparql = "select * { ?s ?p ?o } limit 10" let base = "http://sparql.odp.jig.jp/data/sparql?output=json&app=dev2&query=" let surl = base + sparql.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())! if let url = NSURL(string: surl) { let session = NSURLSession.sharedSession() let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) in if let ns = NSString(data: data!, encoding: NSUTF8StringEncoding) { let s = String(ns) print(s) } }) task.resume() }

ポイント
- sparql.odp.jig を前回手順を参考に、アクセスできるようにしておきましょう
- sparql.stringByAdd.. で、URLのパラメータ用に変換しています (例 ? -> %3F)
あとは、前回の通信方法と一緒です。実行すると、JSONという構造化されたデータで結果が返ってきます。

最新の水位データを取得するSPARQLに差し替えて、実行してみましょう。
※SPARQLについて気になったら、SPARQL入門をどうぞ!

let sparql = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n" + "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n" + "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n" + "PREFIX dct: <http://purl.org/dc/terms/>\n" + "PREFIX odp: <http://odp.jig.jp/odp/1.0#>\n" + "\n" + "select ?created ?value {\n" + " ?s rdf:rest rdf:nil;\n" + " rdf:type odp:WaterLevel;\n" + " <http://schema.org/spatial> <http://odp.jig.jp/jp/fukui/sabae/waterlevelsensor/1>;\n" + " dct:created ?created;\n" + " rdf:value ?value.\n" + "}\n"

返って来たJSONから特定の値を取得するためのコードを、通信後呼び出される completionHandler 内に追加します。

let s = String(ns) print(s) do { let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as! [String: AnyObject] if let results = json["results"] as? NSDictionary { if let bindings = results["bindings"] as? NSArray { if let value = bindings[0]["value"] as? NSDictionary { if let value2 = value["value"] as? String { print(value2) dispatch_async(dispatch_get_main_queue()) { self.rmeter.text = value2 + "cm" } } } } } } catch let error as NSError { print("Failed to load: \(error.localizedDescription)") }

ポイント
- NSJSONSerialization というものを使ってJSONデータをプログラムに取り込みます
- if let を使ってツリー構造を順番にほどいていって、目的の値を取得し、表示しています

PM2.5と水位、タブで切り替えて最新データが見られるアプリ「sabaenow」の第一弾のできあがりです。

Swiftを使って、SPARQLでいろいろとデータを取得しアプリがつくれそうな気がしてきましたね!
odp SPARQLコンソールを使って、あれこれクエリーをお試しください!
データをざっと見てみるには、SPARQLKnockerがオススメです。

Swift2入門
- 5分でできるオリジナル鯖江PM2.5濃度表示iPhoneアプリ
- 5分でできるオリジナル鯖江論手川排水機場水位表示iPhoneアプリ

参考図書
- Amazon.co.jp: Swiftではじめる iPhoneアプリ開発の教科書 【Swift 2&Xcode 7対応】【特典PDF付き】 (教科書シリーズ): 森 巧尚: 本
- Amazon.co.jp: オープンデータ時代の標準Web API SPARQL (NextPublishing) 電子書籍: 加藤 文彦, 川島 秀一, 岡別府 陽子, 山本 泰智, 片山 俊明: Kindleストア

Tweet
クリエイティブ・コモンズ・ライセンス
この作品は「Creative Commons — CC BY 4.0」の下に提供されています。
CC BY 福野泰介 - Taisuke Fukuno / @taisukef / アイコン画像 / プロフィール画像