tech memorandom

Webに関して調査したことや、メディアアート系(Max,Processing)で調査したことなどを書いていきます。

描画の同期をとるならpromise()が便利

レンダリング後に同期をとって処理を行わないとレンダリングコードの 後の処理が先に実行されてしまうケースが発生してしいます。

そういったケースがおきてはいけない場合、 特定のエレメントに書き込みが行われたかどうかを判定する必要があります。 (次の処理がエレメント内の値を参照している場合やエレメント内に書き込む場合など)

■よくない書き方

      $("#plan-photo").html(this.imageView.render()); //class指定されたエレメントに描画
      this.imageView.initPh(); //描画されたエレメントを参照してイベントなどをバインド

上記のケースの場合、レンダーが終わらないうちに先にinitPhが実行されてしまうケースがあります。 そのためinitPhは正常に実行されません

■同期をとった書き方

      $("#plan-photo").html(this.imageView.render()); //エレメントに対して描画
      $("#plan-photo").promise().done(function(){ //描画書き込み完了後にinitPhを実行
        that.imageView.initPh();
      });

promiseをつかってエレメントを監視し、 完了後に後続処理を実行することで同期をとることができます。

描画のタイミングで困っている場合は、setTimeoutや_.delayよりも上記方法のほうが確実です。

ご存知ないかた、お試しくださいませ。

xCode4.6.3でhomebrewでMakeエラーになる場合の対処

xCode v4.6.3でhomebrewでopen ni関連のライブラリをインストールしようとするとエラーになるようになった。あれこれ試したところ、gitなどもinstallできない。

/Users/coa% brew install git
==> Downloading http://git-core.googlecode.com/files/git-1.8.3.3.tar.gz Already downloaded: /Library/Caches/Homebrew/git-1.8.3.3.tar.gz ==> make prefix=/usr/local/Cellar/git/1.8.3.3 CC=cc CFLAGS= LDFLAGS= install ==> make CC=cc CFLAGS= LDFLAGS= git-credential-osxkeychain.c:114: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘{’ token git-credential-osxkeychain.c:155: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘{’ token git-credential-osxkeychain.c:173: error: old-style parameter declarations in prototyped function definition git-credential-osxkeychain.c:173: error: expected ‘{’ at end of input make: *** [git-credential-osxkeychain.o] Error 1

READ THIS: https://github.com/mxcl/homebrew/wiki/troubleshooting

These open issues may also help: https://github.com/mxcl/homebrew/pull/17713 https://github.com/mxcl/homebrew/pull/20260 https://github.com/mxcl/homebrew/pull/19552 https://github.com/mxcl/homebrew/pull/19763 <

brew doctorを叩くとCommand Line Toolsをいれろとwarningがでるが、はいっている。 xCodeを入れなおしてもだめ。すでにInstall済の表示になっている。

トラブルシューティングのリンクができたのでたどってみると以下の記事にたどり着いた。

http://www.cocoanetics.com/2012/07/you-dont-need-the-xcode-command-line-tools/

上記、記事のremove_CLI_tools.shのコードをシェルにして実行したところ、Command Line Toolsのuninstallに成功。

あらためてinstallしなおして、brew doctor

次はエラーがでない。

brew install gitを実行すると無事インストールできた。

ちなみにhomebrewにはxcode-selectというどこにxcodeがあるのかを定義している場所があり、そこも更新しないといけないようです。

/Users/coa% xcode-select --print-path <

/Applications/Xcode.app/Contents/Developerになっているか確認。もし別の場所になっていたら以下を実行。

/Users/coa% sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer <

なんだかんだで、ハマりました。

誰かの参考になるかと思い、いちを記事にしておきます。 この現象がおきるのはCommand Line Toolsに切り替わる前にbrewをつかっていた人のようです。

スマートフォンサイトのlightbox的なモーダルの実装

CSS3のみでModalを実装する機会があり、 こちらで公開していただいたいるサンプルが、 とてもスマートで参考なりました。

iPhoneでためしてみたところ、スクロールするとモーダルの半透明背景が、 height100%がうまくきかず、背景がきれてしまう、という事象に遭遇しました。

いろいろやった結果、回避策です。

abosoluteをfixedに変更

ただiPhoneでのFixedサポートはつい最近ということもあり、ちらつきが発生します。 iOS4.3以下ではうごきません。 AndroidはFixedがサポートされていることもあり、安定してうごきます。

.close_overlay {
    top: 0;
    left: 0;
    width: 100%;
    height: 100%; 
    display: block;
    position: fixed; // absolute fixedに変更
    background: #000;
    opacity: 0.5;
    z-index: 5;
}

heightを100%より長くする

300%あたりまでのばしたところのばしてもスクロール時に切れることはなくなりました。

.close_overlay {
    top: 0;
    left: 0;
    width: 100%;
    height: 300%; // 100%を300%に変更
    display: block;
    position: absolute;
    background: #000;
    opacity: 0.5;
    z-index: 5;
}

二つ目の方法が今のところベターかなと思うのですが、 最善とはとても思えないのです。 これ以外に今のところ方法が浮かばなかったりします。

もっといい方法あるよ!!って人いらっしゃいましたらコメントとかいただけると嬉しいです。

追記: 最終的にfixedにしてオーバレイのscrollイベントを無効にすることで完璧ではありませんがそこそこ良い感じになりました。 どこかのタイミングでパッケージ化してGithubにでもあげようと思います。

OpenFrameworks FFT解析 マルチチャンネル

複数チャンネルのFFT解析をやりたかったのですがofxFFTではできなかったので、少しコードをいじってできるようにしてみました。

ofxFFT Classをチャンネル分生成

void ofxFFTLiveMutilChannel::setup(ofSoundStream * soundStream,int input) {
    this->soundStream = soundStream;
    nInput = input;
    soundStream->setup(
                       this,                   // callback obj.
                       0,                      // out channels.
                       nInput,                      // in channels.
                       44100,                  // sample rate.
                       buffersize,         // buffer size.
                       4                       // number of buffers.
                       );
    for (int i=0; i<nInput; i++) {
        FFTLive *fftLive = new FFTLive();
        fftLives.push_back(fftLive);
    }
    
}

audioInのfftLiveに自身に該当するオーディオデータは格納

inputのなかにはチャンネル分のオーディオ配列がならんでいます。

例えば4chであれば

input[0] 1ch input[1] 2ch input[2] 3ch input[3] 4ch input[5] 1ch input[6] 2ch

以下、buffersizeまで同配列です。 複数のチャンネルのデータが入り混じったinput配列をチャンネル別に別にデータとしていれてあげる必要があります。

void ofxFFTLiveMutilChannel::audioIn(float * input, int bufferSize, int nChannels) {
     for (int i=0; i<nInput; i++) {
        int cnt = 0;
        for (int j=0; j < bufferSize; j=(j+1)*(nChannels-1)) {
            fftLives[i]->specData[cnt] = input[j];
            cnt++;
            
        }
    }
 }

このへんがofxFFTの実装がなかったので複数のofxFFTを管理するマネージャークラスのようなものを追加で作成してみました。

利用する場合は、setup内で以下のようなコードで生成します。

    //FFT設定
    ss = new ofSoundStream();
    ss->setDeviceID(3); //デバイスを設定
    ss->listDevices(); //利用可能なデバイスを出力 不要なら削除
    am.setup(ss,2); // amはofxFFTLiveMutilChannel headerで宣言済

updateで更新します。

    am.update();
    am.fftLives[0]->getFftPeakData(mesh1->fftdata, 256);
    am.fftLives[1]->getFftPeakData(mesh2->fftdata, 256);

手荒なつくりですが、ヒントにでもなるかと思うので、こちらからダウンロードしてください。

https://github.com/coa00/ofxFFT-1

Backbone.js

自分が思うBackbone.jsを使うと幸せになれるポイント
1. APIからの値取得がかなり楽。パースしてモデルに格納後にイベントを飛ばしてくれる。あとはそれをレンダリングするだけ。

  //通信用のモデルの定義
    app.model.song= Backbone.Model.extend();
    app.collection.playlist = BackBone.Collection.extend({
        model : app.model.song
        url : function() {
            return ”hoge”; //APIのURLを記述
        },

        parse : function(res) {
    //xmlが返される場合は
    //var json_res =$.xml2json(res);
            //jsonの中にsongというオブジェクトが1..nである想定
     return res.song;            
         },
        fetch : function(params, options) {
            var self = this;
            //リクエストパラメータ設定
            self.params = params;
            self.params.start = 1;
            self.params.count = self.maxCount;

            options || (options = {});
            options.dataType = "xml";
            options.timeout = this.timeout;

    //成功時、エラー時の処理
            if (!options.success) {
                options.success = self.handleSuccess;
            }
            if (!options.error) {
                options.error = self.handleError;
            }
            Backbone.Collection.prototype.fetch.call(self, options);
        }
    });
    //viewの定義 collectionからresetがきたらrender開始
   //collectionからmodelを取り出してappendするビューを生成
    app.view.playlist = Backbone.View.extend({
        el: '#playlist',
        events : {
        },
        initialize : function() {
            this.collection = new app.collection.playlist();
            this.collection.bind("reset", this.render, this);
        },
        //テンプレート定義
       template: _.template(…),
        //resetが発火されると自動的にレンダーがはしる。
        render : function() {
            var self = this;
            console.log(self.collection);
            $.when(
                self.$el.html(self.template(self.collectiontoJSON()));
            ).done(function(){
                self.collection.each(function(model){
                    var view = new app.view.song({
                        model : model
                    });
                    self.$el.find('#song-list').append(view.render());
                });
            });
            return false;
        }
    });
   app.view.song = Backbone.View.extend({
        tagName: 'li',
        template: _.template(…),
        events : {
            "click .openReply":"openReply"
        },
        initialize : function() {
        },
        render : function() {
            var self = this;
             this.$el.html(this.template(self.model.toJSON()));
            return this.el;
        },
        openReply: function(){
           //複数appendされた要素のうちクリックされたものだけは反応する。
       }   
    });  

みたいな感じです。jsonのパース処理とか全くしなくてもcollectionの中のmodelにいれてくれます。

2. リスト作成時にリストアイテム毎に動的にIDを振らなくてもいい。
生成されたviewはそれぞれ独立してイベントがバインドされているため、events内に定義されたイベントは、
同一のviewであっても、そのインスタンスのみ反応します。

ちょっと言葉では説明しずらいのですが、同様のことをjQueryで実装しようとするとulの下のliにはそれぞれ別のidないしは、
classを定義する必要があるのですが、veiwから生成した場合、そういったことをキにしなくても問題ありません。
生成時にエレメントに対してユニークなIDをふっているようです。


説明がざつなのでちゃんと伝わったか自信ないのですが、ajaxからとってきてリスト表示みたいな処理にはかなり楽できます。
fetch以外にもmodelやcollectionに変更があった場合、レンダリングできるようにいろんなカスタムイベントが用意されています。

例えば通信にかぎらず、modelの中の文字列をかえたら自動的にviewに反映とか簡単にできます。

他にもかなり色々できます。

眠くなってきたのでこのへんで終わります。


今年もよろしくお願いします。

JavaScript tips

ブログたくさん書こうと思っていたのに仕事やら趣味やらでかなり放置気味だったので今年最後の日に更新しておきます。

 

JavaScriptのTipsです。特に珍しい話ではありません。

 

1. JavaScriptの型判定

古いシステムの場合、xmlしか返さないようなシステムがあるため、xmlからJSONに変換をかけたりするかと思うのですが、その時にオブジェクト配列に変換されたり、Arrayに変換されたりしたります。

変換元のxmlの構造の問題なのですが・・・

 

返ってきた配列がArraryなのかオブジェクト配列なのかによって要素を取り出すロジックが変わってくるので判別したいなーと。

 

そんな時に必須なのがinstanceof演算子。この演算子を使うとプロトタイプチェーンも含めてインスタンスの型を判定してくれます。

 

o instanceof Array ? alert("Arrary"):alert("not Array")

 

ではオブジェクトが連想配列か文字列かを判定する方法は?

連想配列はオブジェクト型なので型が同じ、かつlengthにも要素数ははいっていない。

 

var i=0;

for i in o{

 i++;

}

てな感じで要素数をとるしかないようです。(やり方知っている人いたら教えてください。)

要素が大きい場合は処理負荷になるので何度も要素数を確認するような処理を書く場合はArrayにいれてしまったほうが幸せです。

 

2. 処理を続けさせたくない場合はreturnを必ず記述する。

例えばaタグにidをつけて、そのidの要素に対してイベントをバインドして何か処理を行う場合、returnしないとその次の処理が走ります。

 

<a href="#" id='link'>ボタン</a>

function clickA(){

   $('body').html('飛びました');

   return false; //これがないとアンカーの処理が走る

}

 

当然といえば当然なのですが、coffescriptからJavaScriptを書き始めた私にはちょっと不思議な挙動でした。

coffeescriptの場合、functionに強制的にreturnが入るのでこういったことには遭遇しなかったので・・・

というわけでJavaScriptのfuctionにはreturnを処理を意図的に続けたい、という意思がない限りいれておくべきかと思います。処理的にも無駄な処理が走らないので優しいですし。

 

たとえ意図的だったとしてもコードに書かれていない処理を走ることを想定した実装は他の人は理解できない可能性がかなり高いとおもわれるので避けるのが無難だと思いますが・・・・

 

最近使ってないですけどcoffeeScriptってよくできてるなぁーと思いました。便利なだけでなくJSの無意味に難易度の高いところを吸収してくれるので。

 

今年は激しく環境が変わり、プログラムをちゃんと勉強できた年でした。

ずいぶんプログラムから遠い仕事ばかりしていて、たまに本読んだり、簡単なプログラムを書くぐらいしていたのですが、それでもまともなスピードでコード書けない人になってました。

 

やはり難しいことにチャレンジできる状況に身をおかないとちゃんと身につかないのだと実感します。

 

来年はプログラミングを継続しつつ、英会話とかデザインもやりたいのです。うまく自分をそこにもっていけるかが勝負の分かれ目になりそうです。

英会話もデザインもゆるくスクール通って勉強するのは本当向いてないので、どうしても何かをアウトプットしないといけない場所に身をおかないといけないです。

 

テレビでイチローが難しいことにチャレンジしようとする思いがある限り、野球はうまくなる、とチビッコに言っていましたが、胸にくるものがありました。

 

本当そう思います。難しいことをしないとダメですね。

 
今年もたくさんの人のお世話になりました。ありがとうございました。
来年もいい年にしたいです。

Mountain Lionでrvmのbuildが失敗する

本当ただの備忘録なんですがxcodeいれなおしたらgccがllvmにかわり、rvmでbundle installしたらエラーでるようになってしまったので対応。

/Users/coa/work/bbstudy% ll /usr/bin/gcc-4.2
lrwxr-xr-x  1 root  wheel  12  7 30 02:35 /usr/bin/gcc-4.2 -> llvm-gcc-4.2

mac portsからapple-gcc-42をinstall

sudo port install apple-gcc42

シンボリックリンクの張替え

sudo ln -snf /opt/local/bin/gcc-apple-4.2 /usr/bin/gcc-4.2

昔VMで開発していて面倒になってきたのでMacで直接開発することにしたけど、バージョンアップのたびにいろいろ動かなくなるのが怖いのでまたVM上の開発に戻すことにしました。最近マシンもよく乗り換えるし