水無月の余韻 開発Sc.

プログラミング関連の雑記

加賀ゆびぬきの模様シミュレータを作った

「加賀ゆびぬき」の刺し模様をシミュレートするツールを作りました。

以下のURLに公開しています。

加賀ゆびぬき 刺し模様シミュレータ - 水無月の余韻

「加賀ゆびぬき」とは

加賀ゆびぬきとは、石川県金沢に伝わる、裁縫道具です。

縫い物をするときに使う、ゆびぬきはわかりますか? 家庭科の授業で見たのは、革や金属で作られたものが多いでしょう。

加賀ゆびぬきは、裁縫道具としてのゆびぬきに、糸をめぐらせ、丈夫に、そして美しくなったものです。

どんなものかというと、見るのが早いです。 ググってもたくさんの画像が出てきますが、拙作を載せておきます。

f:id:ichiko_revjune:20150105103727j:plain

数がないので、一番はじめに作ったものから。(なかなかひどいw) 右にいくほど新しいです。

加賀ゆびぬきは、針の運びは単純でいながら、幾何学模様を作り出せるおもしろいものです。

キットの販売もありますが、もともとは端切れや、余った糸を使っていたであろう副道具です。 はじめのひとつは、家にあるものでできるかもしれません。

参考図書

今回、参考にした本。

開発過程のメモ

図形の描画と、各種入力があるものの、アクセスが簡単なところに公開したかったので、Webアプリにしました。

ただし、バックグラウンドはなくて、javascriptで完結させています。

Knockout.jsを使いつつ、コードはCoffeeScriptで記述しました。

Knockout.jsは、MVVMパターンのUIフレームワークです。 本家のチュートリアルが充実していてよいです。

アプリケーションのコードは、githubで公開しています。

http://github.com/ichiko/kgyubinuki

ひっかかったこと

使う上で、ひっかかったのは、Knockout.jsを経由したアクション呼び出しの際に、thisの中身が変わるということ。 javascriptorには当然なのかもしれないが、CoffeeScriptなどで便利に記述しているとつい忘れてしまう。

画面には、ViewModel KomaVMの配列をもとにリストを生成しています。 リストの各要素の KomaVM のメソッド addNewIto を呼び出す際、thisの参照解決ができずにはまりました。 (addNewIto のメソッドは見つかるものの、実行時エラー)

アクションに指定するメソッドは、独立したfunctionとして成立するように定義したほうがよいようです。

# viewmodel.coffee (だめパターン)

class KomaVM
    (略)

    addNewIto: ->
        @addIto(DefaultIto.Color, DefaultIto.Round)

    (略)

    addIto: (color, roundNum) ->
        ito = new ItoVM(color, roundNum)
        @itoArray.push ito
        return ito
# viewmodel.coffee (使えるパターン)

class KomaVM
    constructor: (offset, type, komaKagari, config, setDefault = true) ->
        (略)

        @addNewIto = ->
            self.addIto(DefaultIto.Color, DefaultIto.Round)

        (略)

    addIto: (color, roundNum) ->
        ito = new ItoVM(color, roundNum)
        @itoArray.push ito
        return ito

実装上の工夫

始めのバージョンでは、ViewModel-Viewの2階層で考えていました。 しかし、Knockout.jsは、監視対象のオブジェクトに専用のオブジェクトを使用するため、jsオブジェクトの中身が煩雑になりがちです。

Knockout.jsは、ko.observable()などの関数を通して監視対象オブジェクトを生成するため、バインディングがあるメンバには関数を通したアクセスが必要で、バインディングがないメンバには直接の値アクセスができるという状態になります。 バインディングの有無が入り乱れていると、かなり煩雑です。

そのため、だいたいの見通しがたったところで、Model-ViewModel-Viewの3階層で考え直しました。

アプリの根幹は、Modelオブジェクトに保持した設定にしたがって、View(HTML)に模様を描画することにあります。 入力のバインディングを一度排除して、Modelを構成し、それをラップする形でViewModelでバインディングを定義することにしました。

ラップすることによる冗長さはあるものの、Model層で機能の担保がし易くなりました。

(MVVMパターンを習得した人にとっては、これが当然のかたちなのかもしれないな ^ ^;)