読者です 読者をやめる 読者になる 読者になる

離島プログラマの雑記

島根県の離島、隠岐・西ノ島に移住して子育て中のフリープログラマです。

ScratchブロックをHTMLで描画する方法

プログラミング教室の教材を作るために Scratch ブロックの画像が欲しかったんですが、いちいちスクリーンショットを撮るのも面倒だし、ラスタ画像ファイルだと解像度の問題で取り回しが不便なので、ベクタ画像出力かつ更新が簡単な方法を探していたらドンピシャのライブラリがありました。

ScratchのWikiやフォーラムでも使われている blob8108 氏作の Block Plugin(scratchblocks)です。このライブラリはブラウザ上で動作するブロック画像ジェネレータとしても公開されていますが、Webページに組み込めば Scratchスクリプトからブロック画像を生成してHTMLにSVGとして埋め込むことができます。

教材の製本も視野に入れると、画像編集ツールを介さずにHTMLで完結できればCSS組版とも相性が良さそうです。

使い方

ライブラリのインクルード

現在の最新バージョンはver3.1なので、以下のJSファイルをインクルードします。

<script src="https://scratchblocks.github.io/js/scratchblocks-v3.1-min.js"></script>

ブロック表記を英語以外(今回は日本語)にするためにはさらに以下の翻訳データをインクルードします。

<script src="https://scratchblocks.github.io/js/translations-v3.1-min.js"></script>
描画

実際に描画してみたサンプルを GitHub Pages で公開しておきました。

https://ag-k.github.io/scratchblocks-html-sample-ja/

サンプルコード全体は以下で参照できます。

https://github.com/ag-k/scratchblocks-html-sample-ja/blob/master/index.html

以下は各描画方法の説明です。

ベーシックな描画方法

HTMLの要素としてScratchスクリプトを記述しておき、scratchblocks.renderMatching() で指定したセレクタに該当する要素をまとめて描画します。 指定するセレクタは自由ですが、preタグだとそのままスクリプトが書けるので便利です。

<pre id="blocks1" class="blocks">
  @greenFlag がクリックされたとき
  x座標を (0) 、y座標を (0) にする
  ずっと
    (10)回繰り返す
      @turnright (10) 度回す
      (5) 歩動かす
    end
    次のコスチュームにする
  end
</pre>
<pre id="blocks2" class="blocks">
  このスプライトがクリックされたとき
  もし <(向き) = [90]> なら
    ペンを下ろす
  でなければ
    ペンを上げる
  end
</pre>
// 'pre.blocks' セレクタで指定された要素をまとめて描画
scratchblocks.renderMatching('pre.blocks', {
  languages: ['ja', 'en'],
  render: function(doc, cb) {
    doc.render(function(svg) {
      var el = document.createElement('div');
      el.appendChild(svg);
      cb(el);
    });
  },
});
インラインで描画

scratchblocks.renderMatching() の第2引数 option に inline: true を指定するとインラインでブロックを描画できます。

scratchblocks.renderMatching() の第2引数 option に inline: true を指定すると、<code class=b>スタンプ</code>のようにインラインでブロックを描画できます。
// インラインで描画
scratchblocks.renderMatching("code.b", {
  languages: ['ja', 'en'],
  inline: true
});
サイズを拡大して描画

今のところブロックが描画サイズは固定になっているようですが、大きくして使いたい場面があったので、描画結果のサイズを変更する方法を考えました。ブロックはSVG形式で描画されるので、Document.render() から取得できるSVG要素を拡大してやれば良さそうなのですが、scratchblocks.renderMatching() に渡した場合、SVG要素のサイズが取得できませんでした(clientWidth, clientHeight, viewBox が0)。

一方、Document.render() を単体で使う場合は SVG要素のサイズが取得できたので、scratchblocks.parse() でスクリプトをパースしたオブジェクトを Document.render() でレンダリングし、結果のSVG要素を指定されたスケールに拡大するようにしました。

<code id="big_blocks">
  @greenFlag がクリックされたとき
  x座標を (0) 、y座標を (0) にする
  ずっと
    (10)回繰り返す
      @turnright (10) 度回す
      (5) 歩動かす
    end
    次のコスチュームにする
  end
</code>
<code id="middle_blocks">
  @greenFlag がクリックされたとき
  x座標を (0) 、y座標を (0) にする
  ずっと
    (10)回繰り返す
      @turnright (10) 度回す
      (5) 歩動かす
    end
    次のコスチュームにする
  end
</code>
<code id="small_blocks">
  @greenFlag がクリックされたとき
  x座標を (0) 、y座標を (0) にする
  ずっと
    (10)回繰り返す
      @turnright (10) 度回す
      (5) 歩動かす
    end
    次のコスチュームにする
  end
</code>
// スケールを変更する場合のブロック描画関数
//   renderMatching()で呼び出される doc.render()ではSVGのサイズが取得できないため、
//   parse() と doc.render() を使用して描画する。
var renderScaledBlocks = function(target, scale) {
  var doc = scratchblocks.parse(target.innerHTML, {
    languages: ['ja', 'en'],
  });
  doc.render(function(svg) {
    target.innerHTML = "";
    target.appendChild(svg);
    //SVGの拡大処理
    svg.setAttribute("width", svg.clientWidth * scale);
    svg.setAttribute("height", svg.clientHeight * scale);
    svg.setAttribute("viewBox", "0 0 " + svg.clientWidth / scale + " " + svg.clientHeight / scale );
  });
};

//各HTML要素に記述したスクリプトを元にブロックを描画
var titleBlocks = document.getElementById('big_blocks');
renderScaledBlocks(titleBlocks, 2.0);
var middleBlocks = document.getElementById('middle_blocks');
renderScaledBlocks(middleBlocks, 1.5);
var smallBlocks = document.getElementById('small_blocks');
renderScaledBlocks(smallBlocks, 1.0);

SVGの拡大方法については以下の記事を参照しました。

ASCII.jp:HTML5のInline SVGをJavaScriptで操作 (2/5)|古籏一浩のJavaScriptラボ

[SVG] viewBoxについての考察 - Qiita

つまづきどころ

  • 画像を含むブロックの入力方法

    これらのブロックはブロック名に画像が含まれているので、文字列で表現する時はエイリアス文字列が設定されています。

    ブロック 日本語スクリプト
    f:id:k-aeg:20170227224919p:plain @greenflag がクリックされたとき
    f:id:k-aeg:20170227225645p:plain @turnright (15) 度回す
    f:id:k-aeg:20170227225720p:plain @turnleft (15) 度回す
  • 日本語に翻訳されていないブロックがある

    「() 回繰り返す」などのループは閉じるコマンド(英語版では「end」)が必要なんですが、日本語に翻訳されていないので、languages に"ja"と"en"の両方を指定した上で「end」と記述する必要があります。

おすすめ作業フロー

HTMLでスクリプト手書きは結構面倒なので、Scratch プロジェクトのブロックからscratchblockのスクリプトを生成するジェネレータを活用するとラクです。以下の手順で作業するのが良さそうです。

  1. Scratchでプロジェクトを作成
  2. ブロックを組んで動作確認
  3. generator :: scratchblocks でScratchプロジェクトをscratchblockスクリプトに変換
  4. scratchblockスクリプトをHTMLに貼り付け

サックスのメンテナンス用リークライトを自作してみる

離島という環境ゆえ、近くに楽器屋さんがありません。 楽器のメンテナンスのたびにフェリーで3時間かけて本土まで行くのも大変なので、簡単なメンテナンスは自分でできるようにしようと思いたちました。

調べた限り、分解&組み立てレベルのサックスメンテナンスに関する日本語の文献はこの本がベスト(というかこれしかない)のようです。

サクソフォン マニュアル 日本語版

サクソフォン マニュアル 日本語版

さっそく買って読んでみましたが、日常のメンテナンスからタンポの交換まで、全ページカラー写真でとても丁寧に書かれていて良書でした。さすがに自力でタンポ交換は無理そうでしたが…。 本書では息漏れチェックの手段として2つ紹介されており、1つは薄い紙で作った隙間ゲージをタンポに挟んで引っ張るというものと、もう1つはリークライトをボアに挿入して光が漏れる場所をチェックするというものです。

後者のリークライトは、LEAK LIGHTSのようなところで買えるのですが、大体$100以上とお高めなので、LEDテープで自作することにしました。作り方は、奥津サックスマウスピース製作さんのブログで紹介されている通りです。

okutsumouthpieces.com

用意するもの

作り方

まずはLEDテープを切り出します。

f:id:k-aeg:20170225150112j:plain

出来上がりの長さは市販品と同じ21インチ(約53cm)にします。半分で折り返してくっつけるので、倍の長さを切り出します。今回使用したLEDテープは5cm間隔で切断可能になっているので、105cmとしました。

f:id:k-aeg:20170225150102j:plain

切断可能な部分は丸い端子が4つ並んでいるところの真ん中の線です。 切断したら、裏面の両面テープを剥がして真ん中で折って貼り合わせます。

f:id:k-aeg:20170225150533j:plain

今回はクリアタイプの熱収縮チューブがなかったので、根本だけ短いチューブで固定することにしました。チューブをLEDテープの根本にかぶせてライターやヒートガン、はんだごてなどで熱を加えて収縮させます。

f:id:k-aeg:20170225151147j:plain

最後に、DCコネクター変換プラグにドライバーで赤線と黒線を接続します。LEDは極性があるので注意が必要です。赤線は+、黒線は−につながないとLEDが壊れます(たぶん)。

f:id:k-aeg:20170225153407j:plain

これで完成です。ACアダプタを接続していざ点灯! ちゃんと尽きました。カメラのホワイトバランスで暗めに写ってますが、肉眼で見ると結構明るいです。

f:id:k-aeg:20170225153528j:plain

サックスに入れてみるとこんな感じになります。

f:id:k-aeg:20170225153815j:plain

総工費としては2000円以下で自作できました。めでたしめでたし。

子供向けプログラミング教室の開講とインターンシップを受け入れた話

今週は、西ノ島デジタルラボと称して子供向けのプログラミング教室を開講したり、近くの高校からインターン生を受け入れるなど、プログラミング教育について色々と知見を得られたので記録しておこうと思います。

プログラミング教室

昨年末から準備を進めていた子供向けプログラミング教室ですが、1月末に申込者があったため2月より開講の運びとなりました。一応「西ノ島デジタルラボ」という名前をつけているのは、今後プログラミング講座以外に、ファブ系(CAD、3Dプリンタ等デジタル工作機)の講座なども展開したいという事と、今は固定の活動場所は無いけれど、将来的にはPCやデジタル工作機などを常設して活動できるような拠点があると良いなーという想いからです。

インフラとしては町内の船越地区の公会堂をお借りして、お古のデスクトップパソコン数台に軽量 Linux を入れて、Scratch のオフラインエディタをインストールして実施しました。ネット環境が無いのがネックですが、データ保存用のUSBメモリを配布したりして、当面はオフラインで進められそうです。第1回の授業はインターン期間と重なったので、会場設営などインターン生に少しお手伝いしてもらいました。

低学年向けの配慮について

そんなわけで、第1回の授業は小学2年生と3年生の2名でのスタートでした。内容としては、ビジュアル言語のScratchを使ってゲームを作ろう、というスタンダードなものです。元々は小学4年生以上という条件で始めようと考えていましたが、低学年の子のほうがプログラミング(というよりゲーム制作?)に興味があるようで、希望者や興味がある層は軒並み低学年の子でした。当初、小学4年生という区切りにしていたのは、主に以下のような理由によります。

  1. 抽象的思考ができるようになる年齢(と言われているらしい)
  2. 低学年の子にとってはマウスやキーボードの操作自体が難しい(以前に実施したプログラミング体験講座での観察から)
  3. 低学年に読めない漢字が多い(教材に配慮が必要)

逆に上記をなんとかできれば、低学年向けにも実施できるだろうということで、せっかく希望者がいるのだしと、挑戦してみることにしました。

実際取り組んでみた結果、

  • 1については、第1回実施時点では内容もそこまで踏み込んでおらず、N数も2なので何とも言えませんが、想定よりもずっと積極的に探究心をもって取り組んでくれているので、経験を積んで土台を固めていくことで壁を乗り越えられるかも、という感触です。

  • 2については、マウスをクリックした時にカーソルがずれてしまったり、アルファベットが読めないのでローマ字が入力できないといった問題になりますが、対策としては子供の手に合うように小さめマウスを用意したり、マウス操作練習のゲームをScratchで作ったり、ローマ字入力表を配布したりしました。ただ、マウス操作は若干ぷるぷるしているものの、操作自体には支障がなかったのでマウス操作練習ゲームの出番はなかったり、アルファベットが読めなくてもローマ字入力表とにらめっこして、象形文字を読むかの如くに該当する文字を見つけ出してタイプしたりと、思ったより適応力が高く杞憂だった部分も多かったです。

  • 3については、プリント教材にはすべてルビを振るようにしました。画面上ではルビは表示されませんが、読めない漢字があっても絵として覚えられるようなので、特に操作のストレスになっている感じは見受けられませんでした。Scratch はすべての表示をひらがなにするモードがあるんですが、全部ひらがなだと逆に読みづらいし、前述のとおり漢字モードでも支障はなさそうなので、そのまま使ってもらっています。

という感じで、このまま継続していけそうな気がしています。

授業の進め方について

まず前提として、このプログラミング教室では何を目指すのかということですが、若年層向けのプログラミング教育の方向性としては大きく分けて以下の2つの方向性があると考えています。

  1. 教養としてのプログラミング
    • プログラミングができるようになることよりも、プログラミングというプロセスを通して論理的思考や創造性、問題解決能力といったものを身につけることを目的とする
  2. 実用としてのプログラミング
    • 職業や趣味として実用的なソフトウェアを制作できる能力を身につけることを目的とする

この教室では基本的にプログラミング自体に興味があることを前提として、2に重きを置いた方向性を目指しています。教養という意味では、今後小学校でも必修化されるプログラミングの授業で扱われるべきかなと思います。ただ、プログラムを書くことそのものは表現の一手段でしかないので、結局はどちらの方向性をとるにしても(プログラムによって動作を指示される対象となる)コンピュータというものの性質や振舞いを理解した上で、

  • どんなことができるのか
  • 何をしたいのか
  • どのようにやるのか

といったことを考えるのが本質なのかなとも思います。プログラミング言語というだけあって、あくまでコンピュータと対話するためのコミュニケーション手段でしかないので、例えば英語だけ勉強しても(言語自体への興味は別として)、肝心の伝えたい情報や欲しい情報がなければ意味がないのと同じです。 というわけで、授業の進め方としては以下の3つのフェーズを繰り返しながら発展させていくイメージです。

  1. 探求フェーズ
    • 自由にプログラムを組みながら、色々な機能を試したり、必要な概念を身につける
  2. 構想フェーズ
    • 紙のワークシートなどを使いながら、どんなものを作るかじっくり考える
  3. 実践フェーズ
    • 考えたものをプログラムとして形にする

初回授業の雑感

初回は探求フェーズということで、自由にプログラムを組んでいきますが、何も無い状態では右も左もわからない状態なので、適当なタイミングでちょっとした課題を出したり、同じ課題を別のやり方で実現してみたり、ということ繰り返しました。探求フェーズでは好奇心を重視して、課題からちょっと逸れたりしてもそのまま続けてもらい、その方向で発展できることがあれば新たに課題を設定することで、枝葉のように多方向に知識や経験を広げていきます。

一方、課題をこなすこと以上に大切なのは、遊ぶ時間をじっくり取ることでした。例えば、移動距離を指定する場所にありえないような大きい数値をいれたらどうなるか?とか、数値にマイナスを2つつけたらどうなるか?など直接課題に関係ないことでも、コンピュータの性質や振舞いを知るのには重要なことです。ただ、同じことを何度も繰り返して無限ループに入ってしまう場合があるので、そういう時には新しい機能や他のやり方のヒントを投下して興味を引きつけるとすんなり抜け出してくれました。

生徒同士の相互作用もなかなか有効に働きました。各々の好奇心ベースで進んでいくので、進んでいく方向も異なりますが、隣の人が自分の知らないことをやっていると真似してみたり、自分が経験したことで隣の人がつまづいていたら教えてあげたり、わからないところを一緒に考えたりというアクションが見られました。 情報の非対称性があることで、それを共有するモチベーションが生まれているようです。何より友達同士で教え合うほうが、先生が直接教えるよりも真剣に受け取っている感じがします(笑)。

インターンシップ受け入れ

話は変わって、隠岐島前地域唯一の高校である隠岐島前高校では、キャリア教育の一環として職業体験を実施しているそうです。今回は一週間のインターンシップ受け入れでした。自分はフリーランスプログラマなので、従業員採用のモチベーションも無ければ、技術職ゆえ手伝ってもらえるような仕事もほとんど無いのですが、隠岐島前地域にはエンジニア志望のインターン先が無いということなので、うちで引き取ってみることにしました。普通は技術職のフリーランスインターンする機会はあまり無いと思うので、ある意味離島ならではという気もします(笑)。

というわけで、せっかくなら色々体験してもらって興味の幅を広げつつ、こちらとしてはプログラミング教育の導入部に関するデータを収集させてもらおうと考えてスケジュールを組みました。

1日目: 業界や仕事内容についてのレクチャー、Scratch 入門 + プログラミング教室手伝い
2日目: Web開発入門(HTML + JavaScript)
3日目: Web開発入門(続き)、3Dプリンタによるお土産製作
4日目: Arduino による電子工作入門
5日目: Unity による3Dゲーム製作入門

本当はメインでやっているスマホアプリ開発も入れたかったのですが、インターン生のデバイスWindows + iPhone だったので、開発環境が無く断念しました。自分で書いたプログラムが手元のデバイスで動くのは結構感動するので、インターン向きの内容ではあるんですが条件がなかなか揃いません。

色々とメニューは用意しましたが、付きっきりになるのは仕事に支障が出るので、教材の評価も兼ねて基本的にはWebページや動画、本などの入門教材で自習してもらうことにしました。

1日目

業界や仕事内容について色々話しましたが、あまり興味はなさそうでした(笑)。まずソフトウェア開発というのがどういうことなのか分からない状態だったので、プログラミングを少しでも体験した後のほうが想像しやすかったかもしれません。 その後はプログラミング教室の手伝いをしてもらうために、スクラッチ2.0アイデアブックという本で予習をしてもらいました。

この本はなかなか良書だったようで、特につまづくところも無くスラスラと読みこなしていました。プログラミング教室の間は Scratch で自由にプログラム書いてもらっていましたが、ちゃんと応用してそれっぽい何か動くものを作っていました。

2日目

2日目はWeb開発入門ということで、ビジュアル言語を卒業して、プログラミング学習サイト ドットインストールの HTML 入門と JavaScript 入門の動画を見ながら自習してもらいました。結論から言うと動画ではほとんど理解に結びつかず、後で動画で説明されていた内容について質問してみてもまったく要領を得ませんでした。

動画教材は「とりあえず何か動くものを自分の手で作る」という体験に主眼が置かれていますが、初学者にとっては言語の基本的な文法などを理解していない状態では、何やっているのか分からずにとりあえず指示に従っているだけで、頭には入りにくいようです。他の言語を1つでも学習していれば、言語の差分で見られるのでスラスラ入ってくると思います。

また、Web上の JavaScript 入門は玉石混交で情報が古かったりということが多々あるため、初学者が自分で最適なページを見つけるのはかなり困難だなと感じました。あと、そもそもの話ですが、最近のデジタルネイティブの人はスマホを使えるけど、PCを使えないという噂を聞いていましたが、せいぜい学校の授業で Word、Excel に触るくらいしか経験がなく、ブラウザとYahoo(のページ)の区別がついてなかったので、噂は本当だということが分かりました(笑)。

まあ結論としては、ブラウザの説明から載っている、その時の最新の JavaScript 入門本を一冊読むというのが初学者にとっては良い選択肢かなと思いました。 ただ、Web開発は HTML/CSS など前提となる知識が色々必要でプログラミング始めるまでの道のりが長いので、最初は Processing などセットアップが容易な言語から始めたほうが良かったかもしれません。

3日目

3日目は前日のWeb開発入門のリカバーで直接講義してみました。ホワイトボードを使いながら変数やら文法やらの説明をして、なんとか FizzBuzz 問題を(半分くらい)自力で解いてもらうところまでは行きました。初学者あるあるですが、エラーメッセージの英語を全然読もうとしない(というか読めない)ので、Webで辞書を引いてちゃんと読むように指導しました。

JavaScript でだいぶ頭が疲れたようなので、午後は3Dプリンタで印刷しておいた活イカ活っちゃんストラップの仕上げ作業を体験してもらいました。ルーターでのバリ取り作業などはわりと無心でできるので、午前の作業とのバランスはとれたと思います(笑)。インターン生が自分で完成させたストラップはお持ち帰りいただきました。

4日目

4日目は趣向を変えて電子工作に取り組みました。この日も懲りずに ドットインストールArduino入門を活用してみました。 こちらはJavaScriptと違ってわりとスムーズにこなしているように見えましたが、ブレッドボード上の配線がどうなっているのかとか、コードの各行が意味するところの理解にはつながらなかったようです。結局かなりアフターフォローを入れつつ、光センサと圧電スピーカーを組合せてテルミンもどきみたいなデバイスができあがりました。

f:id:k-aeg:20170209172119j:plain

本当はこれをユニバーサル基板に移植して、CADでエンクロージャ(箱)を設計して、3Dプリンタでプリントしてミュージックデバイスとして完成させるところまでやれたら良かったんですが、そこまでは今回時間がありませんでした。

でもせっかくなので、後で完成させるつもりです。すでにCADでの設計を終えて箱をプリント済みなので、あとは回路をユニバーサル基板に移植して詰めるだけです(笑)。

5日目

最終日は Unity を使って3Dゲームを作りました。Unity がPCにインストールできないというハプニングがあり、午前中を消費してしまいましたが、こちらは公式のチュートリアルが優秀で、あまり手をかけずに自習できました。

tutorial.unity3d.jp

インターン生がFPS(一人称視点のシューティングゲーム)好きなのもあって、Unity なら比較的簡単に FPS が作れそうという体感を得ると、これまでの開発に比べ俄然やる気が見られました。チュートリアルの途中で時間切れになってしまいましたが、まだ続けたそうだったので、プログラミング教室の小学生もそうでしたが、やはり好奇心駆動開発は強いなと思いました(笑)。

まとめ

一口にプログラミング教育と言っても、プログラミングという行為自体の目的が多様化しているのもあり、どこを目指すのかという方向は教育者側では決められないので、各自の興味をきっかけとしてある程度のレベルまでサポートして、自分で学習できるところまでつないであげるのが教育者としての役目かな、と思いました。

一方で、諸外国ではCS(Computer science: 計算機科学)教育に力点を置いて、プログラミングに限らずコンピュータへの理解を深めようという動きが主流になっているのに対して、日本ではプログラミング教育という行為のみが切り出されてクローズアップされている感があり、コンピュータの本質的な理解に結びつかないのではないかという懸念もあります。コンピュータの可能性を最大限に引き出すには、やはりコンピュータ自体への理解が不可欠です。というわけで、プログラミング教室を名乗ってはいますが、プログラミングをきっかけとしてコンピュータに対する理解を深めるという目的も重視していこうと思います。

と、いきなり話が大きくなったりしてまとまりませんでした(笑)。

ディープラーニングことはじめ

最近はやりの人工知能で注目されているディープラーニングですが、どんなものか良く知らなかったので本を一冊読んでみました。

TensorFlowなどのフレームワークをいきなり触るのはなんだか難しそうだなー、と思っていたところに「ゼロから作る」というコンセプトの本を見つけたので手に取りました。結論から言うとわかりやすくて良い本だったので、入門にはおすすめです。

読んでみて理解したことを備忘録として羅列しておきます。

必要な前提知識

数学

数学の知識としては行列と微分が分かればOKという感じです。行列や微分の定義についてもちゃんと説明されているので、なんとなく分かってる程度でも読み進めることはできます。そこそこ数式も出てきますが、Python のコードを動かしながら進んでいくのでそんなに躓くことはありませんでした。

プログラミング言語(Python)

Python はほとんど使ったことないですが、演算や関数の定義などから説明があるので、他の言語をかじっていれば問題ないレベルです。 多次元配列や行列の計算には NumPy という数値計算ライブラリを使うので、ややこしい演算などはライブラリ側でやってくれます。

理解できたこと

自分が理解した内容の覚書なので、間違っているところもあるかもしれません。 本ではもう少しアルゴリズムの実装寄りの話が詳細に説明されており、実際にコードを動かして手元で確認しながら進みます。

パーセプトロン

ニューラルネットワークのもととなるアルゴリズム。 以下の特徴がある。

  • 複数の信号を入力として受け取り1つの信号を出力する
  • 信号の総和がある閾値を超えると発火(=1を出力)
  • 入力信号に重みバイアスというパラメータを設定
    重み
    各入力信号の重要性をコントロールするパラメータで。各入力信号にそれぞれの重みを乗算する。
    バイアス
    ニューロンの発火しやすさをコントロールするパラメータ。重みを乗算した入力信号の総和にバイアスを加算する。
  • 重みとバイアスを調整することで AND、NAND、ORのような単純な論理回路を表現できる

単層のパーセプトロンでは非線形な表現(XORゲートなど)ができない。
パーセプトロンを重ねることで非線形表現ができる

ニューラルネットワーク = 多層パーセプトロン

パーセプトロンの出力を次のパーセプトロンの入力信号とすることで、多層化することができる(多層パーセプトロン)。 多層化により、XORゲートなどの非線形表現が可能になる。 ニューラルネットワークは多層パーセプトロンとほぼ同じで、以下の特徴を持つ。

  • 入力層、中間層(隠れ層)、出力層に分類される層からなるニューロンのネットワーク
  • 各層には複数のニューロンが存在し、入力層〜中間層(1層以上)〜出力層の順に信号を伝達する(順方向伝播)
  • ある層の各ニューロンが出力する信号は、次の層の各ニューロンの入力となる
    • 例えば、ある層に存在する2つのニューロンの各出力(x1, x2)と重み(w)の和にバイアス(b)を足した値が、次層の3つのニューロン(y1, y2, y3)にそれぞれ伝播する
    • ニューロンから次層のニューロンへの各経路には、それぞれ異なる重みを設定する
    • 重み付き入力信号とバイアスの総和には活性化関数(h)を適用する
    • x1 → y1、x1 → y2 などの経路ごとに重みが違うため、y1, y2, y3 のニューロンへの入力はそれぞれ異なった値となる
      • y1 = h(x1 * (x1 → y1の重み) + x2 * (x2 → y1の重み) + b)
      • y2 = h(x1 * (x1 → y2の重み) + x2 * (x2 → y2の重み) + b)
      • y3 = h(x1 * (x1 → y3の重み) + x2 * (x2 → y3の重み) + b)
  • 活性化関数 入力信号の総和を出力信号に変換する関数 = 活性化関数 → 閾値を境にして出力が切り替わる性質を持ち、非線形関数である必要がある

出力層の設計

機械学習は分類問題と回帰問題に大別されるが、ニューラルネットワークはどちらにも使える。 出力層の活性化関数は問題に合わせて使い分ける必要がある。

  • 出力層の活性化関数
    • 恒等関数 → 回帰問題
    • シグモイド関数 → 2クラス分類問題
    • ソフトマックス関数 → 多クラス分類問題
      • 出力の総和が1になる = 確率として解釈可能
      • 出力層のニューロン数 = 分類したいクラスの数

ニューラルネットワークの推論(順方向伝播)

MNIST データセットを例に推論処理を実装する。

  • MNIST データセット
    • 0ら9までの手書きの数字画像
    • 訓練画像60000枚、テスト画像10000枚を含む
    • 画像データは 28 x 28 のグレー画像(256階調)
    • 各画像には対応するラベルが与えられている

ニューラルネットワークの設計は以下のようにする。

  • 前処理
    • ピクセルの値を 255 で除算(0.0〜1.0の範囲に正規化)
  • 入力層
  • 中間層(隠れ層)
    • 層の数やニューロン数は任意
      • 2つの隠れ層で、1層目が50個、2層目が100個、など
  • 出力層
    • ニューロン数 = 分類するクラス数
      • 10クラス(数字の0から9)に分類 = 10個
  • バッチ処理
    • NumPyなどの数値計算ライブラリは配列の計算を効率良く処理できるように最適化されている
      • 入力データをまとめて処理(バッチ処理)すると効率が良い
      • 入力データから100枚ずつ取り出してNumPy配列として処理する(NumPy配列の演算は配列のすべての値に適用される)

ニューラルネットワークの学習

特徴量 + 機械学習のアプローチでは問題に応じた(人力での)特徴量の設定が必要だが、ニューラルネットワークでは特徴量も機械が学習する。 ニューラルネットワークでは損失関数の値が小さくなるように最適なパラメータ(重みとバイアス)を探索する。

ニューラルネットワークでは、パラメータ解空間の探索方法として勾配法(確率的勾配降下法 = SGD)がよく用いられる。

  • 勾配法
    1. 偏微分により現在のパラメータにおける損失関数の勾配を求めて、最小値の方向を予測する
    2. 勾配から予測した最小値の方向へパラメータを更新する(移動する量 = 学習率)
    3. 1と2を繰り返して最小値へ向かって降下しながら、最小値を探索する

ただし、勾配が指す方向に必ず最小値があるとは限らない(極小値の可能性が高い)。 また、数値微分による勾配の計算は非常に時間がかかる。
誤差逆伝播(バックプロパゲーション)による勾配算出の高速化

誤差逆伝播法については、簡潔に説明するのが難しいので省略しますが、数値微分より大幅に少ない計算で勾配を算出できます。
本では一章丸々使って、計算グラフという表現方法で説明されています。

学習のテクニック
  • パラメータの更新方法
    パラメータの更新方法は学習の効率に大きく影響する。
    • SGD
      • シンプルだが関数の形状によっては探索が非効率
    • Momentum
      • 物理法則に従ってパラメータの移動場所を決める(お椀にボールを転がすイメージ)
    • AdaGrad
      • 学習率を徐々に減衰させる
    • Adam
      • Momentum + AdaGrad
  • 重みパラメータの初期値
    重みパラメータの初期値はモデルの表現力や学習の効率に大きく影響する。
    • Xavier の初期値
    • He の初期値
      • 活性化関数が ReLU の場合に特化した初期値
  • 過学習対策(正則化)
    • Weight decay(荷重減衰)
      • 大きな重みに対してペナルティを課す
    • Dropout
      • 訓練時に隠れ層のニューロンをランダムに消去しながら学習する
      • 実質的に複数のモデルに個別に学習させた平均をとることと同義なので、アンサンブル学習に類似
  • Batch Normalization
    • 2015年に提案された新しい手法
      • 学習の効率が良い
      • 初期値に依存しにくい
      • 過学習の抑制
    • 重みのパラメータの分布を強制的に分散させる
      • 活性化関数の前か後にデータ分布の正規化処理を行う
  • ハイパーパラメータの検証と最適化
    • 自動で学習できない(手動で設定する必要がある)パラメータ = ハイパーパラメータ
      • 各層のニューロン
      • バッチサイズ
      • パラメータ更新の学習率
      • Weight decay 係数
      • その他手法に応じて設定が必要なもの
    • 検証方法
      • ハイパーパラメータがテストデータに対して過学習を起こさないように、ハイパーパラメータ調整用の検証データを用意して検証
        • 訓練データから一部分離するなどして検証データを用意しておく
    • 最適化方法
      • ハイパーパラメータはモデルの認識精度で評価する
      • 試行を繰り返しながら良い値の範囲を絞り込んでいく
畳み込みニューラルネットワーク(CNN)

前述のニューラルネットワークでは全結合層を用いていたため、データの形状は無視されていた。 CNN ではデータの形状を維持する畳み込み層とプーリング層が追加される。 多段の畳み込み層では、前層の情報を元に段階的に高度な情報を認識することができるため、画像認識等の精度が向上する。

  • 畳み込み層

    • 入力データに対してフィルターを適用する
      • 入力データとフィルターの要素を乗算して和を求める(積和演算)
      • 3次元データ(奥行き = チャンネル方向が存在する)の場合、チャンネル毎に畳み込み演算した後、結果を合算して1つの出力を得る(チャンネルは1つになる)
    • パディング
      • 入力データの周囲に固定データを埋め込むことで、出力サイズを調整する
      • パディングを大きくすると出力サイズは大きくなる
    • ストライド
      • フィルターを適用する位置の間隔
      • ストライドを大きくすると出力サイズは小さくなる
  • プーリング層

    • 学習するパラメータがない
    • チャンネル数が変化しない

ディープラーニング(= 多層構造のニューラルネットワーク)

層を深くした多層構造のニューラルネットワークディープラーニングと呼ばれている。

  • 層を深くする意味
    • 理論的にはあまり分かっていないが、実践的には意味のある結果が出ている
      • (コンペティションの結果から)層が深まるほど認識精度が向上している
      • 層を深くすることで、パラメータ数を少なくできる
      • 層を深くすることで、学習データを少なくできる(=学習が高速化する)  
  • ディープラーニングの実装

  • 高速化

  • 実用例

    • 物体検出
      • R-CNN
    • セグメンテーション
      • FCN (Fully Convolutional Network)
    • 画像キャプション生成
      • NIC (Neural Image Caption) = Deep CNN + RNN (Recurrent Neural Network)
    • 画像スタイル変換
    • 画像生成
      • DCGAN (Deep Convolutional Generative Adversarial Network)
    • 自動運転
      • SegNet
    • Deep Q-Network (強化学習)

五線譜ノートアプリ PhraseNote ver1.2 リリース

iPhone/iPad向けアプリ PhraseNote を久々にアップデートしました。

PhraseNote - 五線譜ノート

PhraseNote - 五線譜ノート

  • Eiji Koyama
  • ミュージック
  • ¥480

ちゃんとブログに書いたことがありませんでしたが、PhraseNote はフレーズのメモやリードシート(メロディ+コード譜)の作成に特化した音楽ノートアプリです。ピアノ鍵盤でささっと音符が入力できたり、コードから伴奏を自動生成できたりするのが売りです。

f:id:k-aeg:20161203044443j:plain

ピアノ鍵盤に慣れている方なら、手書きタイプの記譜アプリよりスピーディーに入力できます。フルスコアなどきっちりとした譜面を作るのには向いていませんが、コードを入力すれば伴奏を自動生成するので、ジャズやポップス曲の作曲には役立つかと思います。 また、シラブルの表示機能(移動ドのドレミを表示)など、楽器の練習やソルフェージュ向きの機能もあります。

詳細はApp Storeの説明をご参照ください。

バージョン 1.2 の新機能

今回はリードシートの作成に必要な機能を中心にアップデートを図りました。

  • iPhone6/6 plus/7/7 plus 対応
    • スコアの解像度が上がって前よりきれいに表示されます
    • 小節幅も固定だったのを自動調節するようにしました
    • 小節数が増えると動作が重くなる問題を解消しました
  • 歌詞入力
    • それぞれの音符の位置に歌詞をつけられます
    • 最大で5番まで入力できます
  • 小節単位のコピー/カット/ペースト
    • テキストのメモ帳のような感じでコピペできるようになりました
    • 音符単位でもコピペできるようにしたいですが、今のところ未対応です
  • PDFエクスポート
    • 画像エクスポートも合わせてプレビュー表示がつきました
  • ヘ音記号
  • ダイアトニックコードの簡単入力パレット
    • 入力頻度の高いメジャー/マイナーのダイアトニックコードをボタンで簡単に入力できるようにしました
    • トライアドまたは7thコードを選択できます f:id:k-aeg:20161203044930j:plain
  • コードの入力履歴
    • 一度入力したコードは履歴ボタンに登録されて再度の入力が簡単になります
  • 繰り返し記号およびリハーサルマーク
    • 1カッコ、2カッコなどの繰り返し記号やリハーサルマークを追加しました
  • 付点音符入力の改善
    • 付点音符が選択中の音符の半分の長さの位置からも置けるようになりました
    • 小節からはみ出す場合は音符を自動分割して配置できるようになりました
余談

もともとはジャズのアドリブ練習用にフレーズをストックするために作り始めたアプリなので、それっぽい機能が備わっています。ご活用ください。

  • コードのルート音からのインターバル表示
    • インターバルがぱっと分かるのでフレーズのアナライズがしやすくなります
  • キー相対のシラブル表示
    • 移動ドでフレーズを覚えたりするのに便利です
  • 移調機能
  • 管楽器のための移調機能
    • Bb管、Eb管などの管楽器向けに曲のキーは変えずに表示上だけ移調できます

隠岐のフェリー乗換案内アプリを作ってみた 〜Webアプリ編〜

前書き

久々にブログタイトル通りのプログラマっぽい記事です(笑)。

リリースから3ヶ月ほど経ちましたが、島根県の離島、隠岐諸島地域のフェリー乗換案内アプリを作ってみました。本土⇔隠岐(島前/島後)をつなぐフェリーと、島前3島をつなぐ内航船が対象です。最初にブラウザで動作するWebアプリ(もどき)版を開発して、それをベースにハイブリッドアプリ化して iPhone/iPad 版、Android版を作りました。

隠岐航路案内

f:id:k-aeg:20160601163638p:plain

Webアプリ版 http://naturebot-lab.com/ferry_transit/

iPhone/iPad

隠岐航路案内

隠岐航路案内

  • Eiji Koyama
  • ナビゲーション
  • 無料

Androidplay.google.com

普段はiPhoneアプリ開発を中心にやっていて、Webアプリベース(と言っても、ほとんどフロントエンドで処理するので、Webアプリと呼べるか怪しいですが…)のものを作るのは初めてだったので色々と調べながら作りました。Android は昔少しさわったことがありますが、Google Play ストアでのリリースは初めてです。それぞれの開発の記録やハマったところなどを書き残しておきます。3本立ての予定で、今回は Webアプリ版です。

開発の経緯

島根県隠岐諸島では、本土と隠岐をつなぐ大型フェリーと隠岐諸島の島前各島をつなぐ内航船が就航しており、それぞれ隠岐汽船隠岐観光が運営しています。隠岐に関係する港は全部で6つだけですが、大型フェリーと内航船の乗り継ぎルートも含めるとわりと複雑です。

f:id:k-aeg:20160901112300g:plain 西ノ島町観光協会HPより引用

さらに時期によって船がドック入りしたり、運行ルートが変わったりするので、紙の時刻表で確認するのはちょっと骨が折れます。また、内航船は大手の乗換案内サービスでは検索対象外です。

隠岐汽船公式サイトには時刻表検索機能があり、内航船を含めた乗り換えルートも検索できるのですが、スマホに対応していなかったり、時刻表の一覧はPDFベースだったりとちょいちょい不便なので、スマホで簡単に見れる時刻表/乗換案内アプリがあると便利そう、ということでWebアプリの勉強がてら作ってみることにしました。

アプリの仕様

  • いわゆる電車乗換案内アプリのフェリー版
    • 指定した港から出る船の時刻表(主に島民向け)
    • 指定した出発地〜目的地の乗換ルート検索(主に観光客向け)
  • 指定された日時に応じた時刻を表示する
    • 駅順が決まっている電車と違い、時期や時間によってルートが異なる
  • オフライン動作に対応できるようにする
    • 島内は電波が悪いところが多くしばしば圏外になるので、観光中などにネットにアクセスできないケースを想定
    • Webアプリ版では対応せず、ハイブリッドアプリでのオフライン動作を想定して設計
  • 外国人観光客向けの英語表記対応
    • 表示する言語はブラウザの言語設定から自動判別

配布形態の検討

HTML5のオフライン動作向けの機能は各ブラウザに普及してきたので、Webアプリで完結することもできそうですが、とりあえずHTML5 + JavaScriptで機能を作ってWeb版としてリリースした後、iPhone/Androidのハイブリッドアプリを作る方針としました。Webアプリは一般的なユーザにはあまり浸透していない(と思っている)形態なので、ブックマークやトップ画面への追加などができないユーザも多そう、という意味でスマホアプリとしてパッケージングしたほうが良さそうと考えました。広告の表示回数をアクセス解析代わりに使っていますが、実際にスマホアプリ版、特にiPhone/iPad版のアクセス数がダントツに多いです。

使った言語とフレームワーク

言語

オフライン対応のハイブリッドアプリ前提で作るので、なるべくフロントエンドで色々と処理してやる必要があります。なので基本的にはブラウザが実行できるJavaScript一択となりますが、昨今はAltJS(JavaScriptコンパイルして使う、JavaScript の代替となる言語の総称)が全盛ということで、その中でも定番になりつつあるTypeScriptを使ってみることにしました。

TypeScript は名前の通り JavaScript + 静的型付けが基本的なコンセプトですが、ECMAScript 6 に準拠した機能を取り入れており、クラスベースのオブジェクト指向的な書き方がしやすいのでObjective-C、Swift 使いとしては馴染みやすかったです。

qiita.com

エディタは atom を使っているので 、atom-typescriptプラグインをインストールしました。TypeScriptで書いたファイルを保存するだけで、文法チェックとJSへのコンパイルを自動でやってくれるので非常に便利です。

qiita.com

なお、TypeScript で JavaScript のライブラリを使う際は、型定義情報が必要になります。有名どころのライブラリに関しては型定義情報が提供されています。型定義情報は Typings という型定義管理ツールで取得することができます。以前はTSDというツールが使われていたようですが、現在は非推奨になったようです。

qiita.com

バックエンド側はほとんど処理がないので、PHPで適当に書きました(^^;)

フレームワーク

フロントエンドで時刻表データを整形するのにMVC(MVW?)フレームワークAngularJSのフィルタ機能が便利そう、ということでAngularJSを使ってみることにしました(後継の Angular 2 も開発されていますがまだ色々変わりそうなので…)。AngularJSは色々モジュールを組み込んで拡張できるので、多言語対応も angular-translate でできそうです。

デザイン方面は疎いので、素人でもそれなりにそれっぽい画面になると噂のTwitter社製 Bootstrapを今更ながら使ってみようかな、と思っていましたが、AngularJS と組み合わせて使えるようにポーティングされたUI-BootstrapがAngularJS開発チームによって提供されていたので、それを使ってみることにしました。

環境構築

TypeScript + AngularJS + UI-Bootstrap な環境を作ります。 色々依存があってややこしいですが、Macで一から構築する場合は以下の手順です。

  1. homebrew (Mac OSX のパッケージ管理ツール)をインストール

    qiita.com

  2. homebrew で node.js (ブラウザなしでJavaScriptを実行するためのツール)をインストール

    node.js で動作する各種ツールを使うために node.js をインストールします。 atom エディタも JavaScript で書かれているので node.js が必要です。

    qiita.com

  3. npm (node.js のパッケージ管理ツール) で bower (フロントエンド向けパッケージ管理ツール) をインストール

    easyramble.com

  4. npm で TypeScript をインストール

    docs.solab.jp

  5. bower でフロントエンド向けのライブラリ(今回は AngularJS と UI-Bootstrap)をインストール

    www.atmarkit.co.jp

    ちなみに UI-Bootstrap は Bootstrap のCSSだけ流用するので、Bootstrap もインストールが必要でした。

  6. bower で AngularJS のモジュールをインストール

    多言語対応用モジュール github.com

    便利フィルタ詰め合わせ www.npmjs.com

開発

時刻表のデータベース化

時刻表は各社からPDFで公開されていますが、それぞれフォーマットが違うので扱いやすい形に統一する必要があります。 今回は Google ドキュメントスプレッドシートに各社のフォーマットをそのまま入力できるシートを作って、Apps Script で統一したフォーマットに変換することにしました。出力されたものを csv としてダウンロードして、それを SQLite の DB に取り込んだものをサーバに配置しました。

統一フォーマットは以下のように、「ある港」から「次の港」へ向かう便の情報を最小単位として時刻表を分解したものです。

便ID 接続ID 開始日 終了日 便名 出発地 出発時刻 到着地 到着時刻
1 2 2016/01/01 2016/02/29 FERRY_OKI HONDO_SHICHIRUI 9:00 SAIGO 11:25
2 3 2016/01/01 2016/02/29 FERRY_OKI SAIGO 12:00 HISHIURA 13:10
3 4 2016/01/01 2016/02/29 FERRY_OKI HISHIURA 13:20 BEPPU 13:35

便IDは各便を一意に識別するための識別子、接続IDはその便が接続する別の便のIDです。時期によって便が変わるので有効期限(開始日、終了日)があります。

バックエンド

ハード面は安いプランのレンタルサーバです。隠岐諸島の人口(約2万人)と観光客(ツアー客を除く)のキャパシティを考慮しても、大量アクセスが見込まれるサービスではないので、数百アクセス/日を捌ければ良いという見込みです。

ソフト面では、オフライン対応のために前項の時刻表DBをまとめてクライアントに渡す処理が必要です。PHP で時刻表 DB の全内容を JSON 化してクライアントに送信する処理を書きました(数行だけですが)。 DBファイルを直接クライアントに投げちゃってもいいような気もしましたが、クライアント側で SQLite3 DB が扱いにくいようなので、そのまま処理できる JSON 形式にしています。

フロントエンド

クライアント側のプログラムは TypeScript も AngularJS + UI-Bootstrap も初めてなので探り探りの開発です。 将来的な Angular2 への移行やメンテナンス性を考えると、以下のような記事を参考にするのが良さそうです。

qiita.com

qiita.com

が、UI-Bootstrap 公式のサンプルコードは書き方が古めかしいので、それを参考にしているうちにあまりモダンでないコードになってしまいました…。いずれリファクタリングしたい(^^;)

UI(ユーザインタフェース)

アプリの主な機能は時刻表と乗換案内なので、タブで切り替えるのが良さそうでした。 UI-Bootstrap には Tabs というディレクティブ(=独自タグor要素)が用意されていて、簡単にタブの UI を作成できます。便利。

時刻表

時刻表の処理としては、以下のような感じです。

  1. サーバの時刻表DBからJSON化したデータ(数百KB)を取得
  2. AngularJS のフィルタで指定日時と出発地/到着地に一致するデータを取捨選択
  3. ngRepeat ディレクティブで表を生成

フィルタは拡張モジュールも合わせると様々な種類が用意されており、条件に一致するデータの抽出や排除、プレフィックスやサフィックスの追加などが簡単にできます。多言語対応モジュールもフィルタとして実装されており、登録したワードをフィルタにかけると言語設定に対応した文字列に置き換えてくれます。

SQLite3 や JSON オブジェクトには日時を表す型が存在しないので、時刻は文字列(mm:ss)として格納されており、JSON の文字列から JavaScript の Date 型に変換して保持しておきます。この時の変換元となる文字列のフォーマットは少し曲者で、ブラウザによって許容する表現が異なります。サポートしていないフォーマットを指定した場合、null が返ってしまうので全てのブラウザがサポートしているフォーマットを選択する必要があります。

ka2.org

というわけで、一番汎用的な"YYYY/MM/DD HH:mm:ss"フォーマットに加工してから Date 型に変換しています。

乗換案内

乗換案内のアルゴリズムは、以下のような感じです。

  1. 指定日時に従って有効期間外の便をフィルタで排除
  2. 指定出発地かつ指定時刻に最も近い便を抽出して、各経路を探索 2-1. 指定到着地にたどり着く便を抽出して結果リストに保存 2-2. たどり着けない便から接続可能な次の便を探す 2-2-1. 見つかった便から、指定到着地にたどり着く便を抽出して結果リストに保存 2-2-2. たどり着けない便から接続可能な次の便を探す …以上を再帰的に繰り返してすべての経路をリストアップ
  3. 経路リストを優先度順にソート
  4. ngRepeat ディレクティブで経路リストから表を生成

6つしか港が無い割に、かなり色々な経路をとることができるので、隣の島に行くのに数百種類の経路が出たりします(笑)。全部いっぺんに表示するのはアレなので、最初は上位5経路を表示するようにして「もっと見る」ボタンを押すと他の経路も見れるようにしました。 となると、どの経路を優先して表示するかが問題となり、ユーザに優先したい条件(到着時刻が早い順、移動時間が短い順、値段の安い順など)を選んで貰えば解決、なのですが、それらのインターフェースも増やすとごちゃごちゃして使いにくそうなので、そこはアプリ側で優先すべき経路を勝手に判断することにしました。

多言語対応

angular-translate モジュールを使って日/英に対応しました。 使い方は以下の記事がわかりやすかったです。

angularjsninja.com

言語設定の自動判別は自分でやってやる必要があります。 $translateProvider.determinePreferredLanguage() に言語コードを判別して返す関数を渡します。 JavaScriptでの判別コードは以下の記事を参考にしました。

JavaScript でブラウザの言語設定を取得 | EasyRamble

デプロイ

デプロイはgitHub経由でやってみたら楽で良かったです。 手順は以下の通り。

  1. gitHubリポジトリを作成
  2. ローカル git から gitHub リポジトリに push
  3. サーバに SSH ログインして gitHub リポジトリから pull

gitHub からサーバに通知して自動 pull みたいなこともできるらしいですが、ちょっと面倒なので今回はそこまでやりませんでした。 なお、bower でインストールしたパッケージ群はgitリポジトリに含めないので、予めFTP転送ソフトでサーバにアップしておきました。

ハマりどころ

クロスドメイン通信

各ブラウザでは、クロスサイトスクリプティング(XSS)防止のため、クロスドメイン通信がデフォルトで禁止されています。つまり、JavaScript の処理からのアクセス先(今回はPHPスクリプト)が同じサーバにあるWebアプリ形態の場合は問題ないですが、ハイブリッドアプリ化した場合、JavaScript は端末のアプリ側に置かれるので、サーバに対するアクセスはクロスドメイン通信となり失敗します。

これの解決方法は、JSONP(JSON with Padding) や CORS(Cross-Origin Resource Sharing)、XDM (Cross Document Messaging)などいくつか方法があるようです。

blogs.msdn.microsoft.com

JSONP は裏ワザっぽいし、XDM は大仰なので、今回は王道っぽい CORS で対応することにしました。 実装は PHP で一行追加するだけでした。

tadtak.jugem.jp

時刻入力

スマートフォンからアクセスする場合、UI-Bootstrap の Timepicker はボタンが非常に押しづらくて使いにくいです。代替として HTML5 の input type=“time” を使用すると OS ネイティブの入力インタフェースが出て入力しやすいのですが、IE11、 デスクトップ版の FireFoxSafari などでは未サポートでした。

Can I use... Support tables for HTML5, CSS3, etc

解決方法としては、AngularJS の ng-hide ディレクティブで、スマホ/タブレット向け(xs, sm)は input type=“time"、それ以外(md, lg)はTimepickerを表示するように切り分けました。(が、さらに Android 4.3 以下の標準ブラウザは input type="time” をサポートしていないことが判明したので、Android版では Cordova + Crosswalk でレンダリングエンジンを変更することで回避しました。詳細はAndroid版のエントリで…)

文字化け

JavaScript のコードで日本語を使っていたところ、iOSSafari で文字化けしました(charset=“uft-8” を指定してもダメ)。条件文で日本語を使っていたりすると、プログラムが動作しなかったりします。 回避方法は以下の記事で紹介されているとおり、UTF-8(BOM付き)で JavaScript を保存するというものですが、atom エディタは BOM 付き保存はサポートしていないので、JavaScript から日本語を排除するようにしました。DBに格納された日本語をif文で使ったりしていたので、DB内に日本語で格納していた船名や港名も英字に置き換えが必要でした…。

d.hatena.ne.jp

また、上の問題とは別に iOS7 では英語設定でのローマ字表記に使うラテン文字がどうしても文字化けするのですが、こちらはアプリ使用上はほぼ問題にならないため、対応しないことにしました。

その他

Favicon と App Icon

Favicon はブラウザの URL の左側に表示されるミニアイコンで、App Icon はスマホでWebサイトをホーム画面に追加したときに表示されます。アプリっぽくするために、それっぽいアイコンをこさえて設定しておきました。

f:id:k-aeg:20160601163638p:plain

アイコンの作成には Affinity Designer というソフトを使っています。Adobe Illustrator の代替のベクターベースのグラフィックソフトとして人気が出ているようですが、廉価なのになかなか使い勝手が良くて重宝しています。

Affinity Designer

Affinity Designer

  • Serif Labs
  • グラフィック&デザイン
  • ¥6,000

フェリーのアイコンは以下のサイトで配布されているものを使わせてもらいました。 icooon-mono.com

以下のサイトに画像を投げると Favicon と App Icon を自動生成してくれます。 www.favicon-generator.org

3Dプリンタで模型製作 + エアブラシ塗装ことはじめ2

あらまし

タイトルが変わってしまいましたが、前回からの続きものです。 前回の記事では模型の塗装用にエアブラシ環境を整えました。 ag.hatenablog.com

今回はエアブラシで塗ってみたお話です。何を塗るのかは前回の記事に書いてませんでしたが、西ノ島の名産品である活イカの模型を塗りました。モデリングから始めて、3Dプリンタで印刷した上での、仕上げの塗りなのでそのあたりの流れも記録しておきます。

塗装までの工程

モデリング

有機物なのでポリゴンベースのモデリングソフトより、スカルプトモデラー(デジタル彫刻)のほうがやりやすそう、ということで Scruptris というソフトを使ってみることにしました。

Pixologic :: Sculptris :: OakCorp WEB

簡便な操作性で、初心者でもそれなりの形に仕上がる良ソフトですが、ありがたいことに無償で提供されています(有償版 ZBrush の入門版という位置づけ)。

さて、できたのがこちらになります(3分クッキング風)。

f:id:k-aeg:20160802002057j:plain

これを OBJ 形式でエクスポートします。

3Dプリント

モデリングしたデータを3Dプリンタでプリントしていきます。 使っている3Dプリンタはこちらです。

今回は塗装前提なので eSUN の白いPLAフィラメントを使用しました。

このフィラメントはプリント温度は205〜210℃が最適なようです。200℃だと失敗してボロボロになってしまいました。 また全長30cmを超える大物のため、一回ではプリントできないので FlashPrint の分割機能で2分割しました。

f:id:k-aeg:20160615121152j:plain

f:id:k-aeg:20160615121310j:plain

さて、プリントできました(プリント直後の写真は撮り忘れました)。サポート材をニッパーやカッターで取り除き、半日ほどかけてひたすら表面をヤスって積層痕を消します。 ちゃんと研磨しないと積層痕の溝に塗料が流れたりしてうまく塗れません。 今回のような曲面の多い形状の場合、粗めのスポンジタイプのヤスリから始めると曲面にフィットして削りやすいです。

SK 木工用スポンジヤスリ No.2 SF36/60

SK 木工用スポンジヤスリ No.2 SF36/60

最終的には紙やすりで #60 〜 #300 くらいまで順に研磨しました。 研磨後、分割された本体を結合します。今回は水に沈む必要があったので、内部に穴を開けて釣り用のオモリを入れて浮力を調節しました。

結合したのがこちらになります。

f:id:k-aeg:20160528181023j:plain

結合部の接着はプロ用耐衝撃というなんか強そうな接着剤を使いました。

ボンド アロンアルフア プロ用耐衝撃 20g #31701

ボンド アロンアルフア プロ用耐衝撃 20g #31701

パテ補修

結合部はどうしても段差ができてしまうので、パテで隙間を埋めます。 コスパが良さそうなこちらのパテを使いました。

ウェーブ・エポキシパテ 軽量タイプ

ウェーブ・エポキシパテ 軽量タイプ

橙色っぽい部分がパテによる補修部分です。結合部のほかに、研磨で穴が空いてしまった部分なども補修しました。

f:id:k-aeg:20160529003310j:plain

パテを盛ったあと、カッターやデザインナイフなどで余分なパテを削ぎ落とし、紙やすりで段差が滑らかになるように仕上げます。

塗装

さて、やっと塗装に入りますが、あまり写真がありません(^o^)

下地塗装

まず、サーフェイサーで下地を塗っていきます。 1段目は下地隠蔽力の高いグレータイプ。

Mr.サーフェイサー 1200 B515

Mr.サーフェイサー 1200 B515

2段目はクリアカラーの下地とするため、ホワイトタイプのサーフェイサーで塗ります。

Mr.ホワイトサーフェイサー 1000 B511

Mr.ホワイトサーフェイサー 1000 B511

下地が隠蔽されるまでそれぞれ2〜3回ほど塗り重ねます。さらに表と裏に分けて塗るので、塗装→半日乾燥を計10回ほど繰り返して一週間ほどかかりました。

エアブラシ塗装

準備については前回の記事の通りです。塗料はラッカー系塗料を使いました。 塗料の希釈具合などは以下のサイトがわかりやすかったです。

http://www.k4.dion.ne.jp/~gustav/air04.html

さて、一応失敗した3Dプリント物などで2〜3日練習してみた後、本番の塗装です。 と、思ったらエアブラシが詰まって塗料がでません\(^o^)/

ノズルのクリーニングなどいろいろ試してみましたが改善せず、2日ほど悩んで最終的にエアブラシの後ろのネジ(名前がわかりません)をゆるめたところ、ちゃんと出るようになりました。

f:id:k-aeg:20160612184209j:plain

右の写真がもらった資料で目標の色となりますが、ベースの色はクリアイエローを薄く塗ることで再現できました。 その上の茶色っぽい部分はオレンジと黒の調色です。

調色については、子ども向けですがこちらのサイトの「混ぜ合わせチャート」という表が非常に便利です。

print-kids.net

斯くして一旦塗り終えたわけですが、どうも本物はもらった資料ほど黄色くないそうで…。 実物を見せてもらってリトライです。

f:id:k-aeg:20160617150803j:plain

確かにちょっと黄土色っぽい感じですね。 というわけで、一旦色を剥がして塗り直したものがこちらです。

f:id:k-aeg:20160627190834j:plain

ベースの色はクリアイエローに黒を少し混ぜました。 目玉は筆塗りです。目の横のアイシャドウみたいな部分はクリアブルーを重ねています。

仕上げのウレタンニス

色はOKが出たので、最後にテカリと防水性(水に沈めて使うので)のためにウレタンニスで仕上げます。 今回は水性ウレタンニスというものを使いました。木工用ですが模型にも普通に使えます。

これを3層ほど塗り重ねますが、1層塗り終えるごとに#300くらいのヤスリで表面を研磨してやる必要があります(次の層の吸着を良くするため)。透明なので均一の厚さに塗るのはなかなか難しく、薄いところは研磨で下地の色ごと剥がれてしまったりしてなかなか難しかったです。

これも表裏×3層で3日くらいかかって、やっと完成です。

塗り終えた写真を撮らずに納品してしまったので、残念ながら完成品の写真はありません(^^;)