Apple Pencilを使ってみる

ねんがんの iPad mini (第5世代) を手に入れたぞ。 ついでに Apple Pencil (第1世代) も買った。 えらい出費である。

よくあるiPad miniのこじゃれたレビューとかはしない。

Apple Pencilのプログラミング的な使い方はSDKサンプルの Leveraging Touch Input for Drawing Apps をDownloadして見てだいたいわかった。

指のタッチのときと同じく touchesBegan(_:with) の仲間で受け取ればいいけど、 推定値が含まれていることがあるので(PencilはiOS実機とは別デバイスだから)、 touchesEstimatedPropertiesUpdated(_:) で更新すればよいということらしい。

やってみたのが下の図

f:id:hrt1ro:20190515100105p:plain

タッチイベントでやってくるすべての点を折れ線で描いただけ、 なのにPencilのほうはすごく精緻。点の数が多い。 点の数は 60Hz : 240Hz 比、というわけでもないっぽい。 なんとなく1:2くらいのようす。

touchesなんとかは UIResponder のメソッドなので UIView でoverrideするのが ふつうかと思いこんでいたけどSDKサンプルは UIGestureRecognizer でやってる。 「そういうのもあるのか」 UIGestureRecognizer には allowedTouchTypes があるので 「Pencilだけに反応するやつ」などと簡単に指定できるのでこいつがぼくのほしいやつに違いない。

Core Text で縦書き(3)

前回 Core Text で縦書き(2) - 錯綜 のコメント欄で90度回転しなくていいやつを教えてもらえた。 こんな誰も読んでないようなブログでも親切な人が教えてくれたりするものである。 ありがたいことである。

というわけでやってみた。 まず普通に CTFrame を使って横書き。

        …
        let astr = NSAttributedString(string: string)
        let setter = CTFramesetterCreateWithAttributedString(astr)
        let path = CGPath(rect: rect, transform: nil)
        let frame = CTFramesetterCreateFrame(setter, CFRange(), path, nil)
        …

f:id:hrt1ro:20190307144126p:plain
横書き

次に文字列のアトリビュートに縦書きグリフを、 フレームのアトリビュートに進行方向右から左を、指定する。

        …
        let stringAttrs: [NSAttributedString.Key : Any] = [
            .verticalGlyphForm: true,
        ]
        let astr = NSAttributedString(string: string, attributes: stringAttrs)
        let setter = CTFramesetterCreateWithAttributedString(astr)
        let path = CGPath(rect: rect, transform: nil)
        let frameAttrs = [
            kCTFrameProgressionAttributeName: CTFrameProgression.rightToLeft.rawValue,
        ]
        let frame = CTFramesetterCreateFrame(setter, CFRange(), path, frameAttrs as CFDictionary)
        …

f:id:hrt1ro:20190307144205p:plain
縦書き

最初 CFDictionary に CTFrameProgression.rightToLeft を入れていて動かなくて困った。 rawValueがキモだった。分かってみれば当然だけど。

縦書きはたぶんこれで決まりだ。

Scratchからmicro:bitにつないでみた

2か月くらい前にmicro:bitを購入してから ちょくちょく遊んでいるのだけど これがけっこう楽しくて 久しぶりに LEDを買ってきたり はんだごてを買ってきたり している。 けどやっぱりぼくはソフトウエアな人なので電子工作よりもプログラムを組むのが楽しいようだ。

micro:bitのプログラミングは ふつうはMakeCode Editorで作ったプログラムをmicro:bitに転送するのだけど いちいちUSBケーブルを外したり繋いだりするのがちょっとめんどい。 まあ繋ぎっぱなしにすりゃいいのだけれども。 このめんどくささが、プログラム書く→試すサイクルを止めるくらい嫌になることもある。

BLEで転送する方法もあるようだけど、 これによると Scratch から制御できると知った。

www.watch.impress.co.jp

この構成では自分が作ったプログラムは Scratch 側で動いていて micro:bit 側ではScratchと通信するプログラムがずっと動いている、 ので micro:bitをScratch の入出力装置として使える。

手順はこれの通りなのだけど

sanuki-tech.net

これはWindows,ChromeでやってるけどmacOS,Safariでもやってみたらできたというだけの話。

で、やってみたけど、これがすごく面白い。 micro:bitを傾けた方向にスプライトが動くだけのプログラムだけ、なのだけど、 これでAボタン押したら弾が出るようにして…などといろいろアイデアが出てくるところが、 そういうところが創造的で面白がるポイントだと思った。

f:id:hrt1ro:20181206140629j:plain

入出力装置として、と書いたけど 入力は振ったとき、傾けたとき、ボタンを押したとき、端子に触れたとき…と充分な気がする。 出力はLEDに表示するくらいしかないので せめてmicro:bitにつないだスピーカーの音を鳴らすくらいはできるようになってほしい。

まだβ なのでそのうちできるようになるといいなあ。

Swiftでファミコンエミュレータを書いた(3)

githubに上げた。

github.com

わかっている問題

  • 音は出ない
  • 遅い
  • iOS実機で動作確認してない
  • 対応している mapper は NROM だけ

遅い問題

Debug build だと 7 fpsくらいしか出ない。 (MacBook 2017, iOS Simulator)

Release build だと 40 fps くらい出るので optimize は非常によく効いている。 逆に言うと optimize に頼りすぎな書き方をしているかもしれない。

iOS 実機では試してないのでどのくらいの性能がでるのかわからない。 iOS 実機を持ってないので。

Instruments で見てみると PPU の rendering がほぼ 90% 以上、までは分かるのだけど どこがボトルネックなのかはよくわからない。 Instruments の見方がよくわからない。

音が出ない問題

APUを作ってないので。 たぶん iOS 実機を手に入れたらつくる、かもしれない。

ふりかえり

  1. CPU は nestest.nes をパスさせるのが大変だった。
  2. PPU は vblank と sprite 0 hit の実装が breakthrough だった

Swift製NESエミュレータその2

遅々として進まず。

nestest はまだたくさん失敗している。

f:id:hrt1ro:20181102195412p:plain

「テスト失敗」の結果を得るまでが大変だった。 実装のミスではなく、仕様を読み違えている。 6502 の CMP のキャリーフラグの仕様変だよね。 思い込みはバグのもと。

color_test はちょっと動いている様子。

f:id:hrt1ro:20181102195431p:plain

palette は画面が崩れまくっている。

f:id:hrt1ro:20181102195444p:plain

まだまだ先は長い。

Swiftでファミコンのエミュレータを書いてみる

先週読んだこのブログエントリ Kotlinでファミコンのエミュレータを書いた - ゆいきノート に刺激を受けたので自分でも書いてみることにした。 Swift4.2, iOS12.0, Xcode10.0で。

なるべく他人の書いたソースコードは読まないようにして、 できるだけ文書や記事だけ読んで実装してみた。 これってプログラムを書く能力よりも 仕様書を読み取る能力のほうが大事だと思った。

ようやくHELLO WORLD!が表示できたので記念にスクリーンショットを貼っておく。

macOS上のiPhoneシミュレータ上で動いているNESエミュレータの図

f:id:hrt1ro:20181025174428p:plain

ソースはそのうちGitHubあたりに公開するかもしれない。 だいぶ飽きてきたのでいつになるかわからない。

Core Graphics で半透明図形を描く

図形を塗りつぶす。

        UIColor.red.setFill()
        UIBezierPath(rect: CGRect(x: 80, y: 80, width: 100, height: 100)).fill()

重ねて描く。

        UIColor.blue.setFill()
        UIBezierPath(rect: CGRect(x: 100, y: 100, width: 100, height: 100)).fill()

ここから半透明で図形を描く。

まず現在の描画文脈 CGContext インスタンスを得る。

        let context = UIGraphicsGetCurrentContext()!

alpha値を変更するので後でもとに戻す。

        context.saveGState()
        defer { context.restoreGState() }

alpha値を指定する。

        context.setAlpha(0.5)

不透明のときと同じように重ねて描く。

        UIColor.yellow.setFill()
        UIBezierPath(rect: CGRect(x: 120, y: 40, width: 100, height: 100)).fill()

        UIColor.cyan.setFill()
        UIBezierPath(rect: CGRect(x: 140, y: 60, width: 100, height: 100)).fill()

半透明の図形が重なる。

f:id:hrt1ro:20181009112424p:plain

さて「半透明図形を重ねる」のではなく、 「不透明図形を重ねたやつを半透明で重ねたい」という要件がある、 ということもあるかもしれない。

CGContext.beginTransparencyLayer(auxiliaryInfo:)APIドキュメントによるとこう書いてある。

The global alpha is set to 1.

setAlpha() の後、TransparencyLayerを beginend で囲めば、 不透明で重ねた図形を 半透明で描くことができる。

        context.beginTransparencyLayer(in: CGRect(x: 120.0, y: 40.0, width: 120.0, height: 120.0), auxiliaryInfo: nil)
        defer { context.endTransparencyLayer() }

こんなかんじ

f:id:hrt1ro:20181009112442p:plain

beginendはネストできるので、beginendの間で setAlpha() すればネストした内と外の alpha値を乗算した半透明で描かれることになる。