2015年4月
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
無料ブログはココログ

« 2014年4月 | トップページ | 2014年6月 »

2014年5月の1件の記事

2014年5月 6日 (火)

コピペシール update:また失敗

やはり、サンプルコードに勝る資料なし

IssueTracker#244 に、「ストローク操作に関するサンプルコードが欲しい」とリクエストを挙げましたら、「ストロークを全て消去するシール」と、「シール上のストロークをページ上に写すシール」を早速 Skylabβに upload してくださいました。担当者の方、多謝です。

手続きについて非常に参考になります。

ストローク全消しについては、

var backingId = MOON.getCurrentPage().backing,
    extStrokes = MOON.getStrokeJSON(backingId);

extStrokes.forEach(function(extStroke) {
    extStroke.strokes = [];
});

MOON.setStrokeJSON(backingId, extStrokes);

なるほど、backingInfo.externalStrokes = [] でもなく、extStrokes = [] でもなく、extStrokes[ i ].strokes = [] のレベルでクリアするんですね。

ストロークを写すシールに関しては、もしページ上に既存のストロークが一本も存在しなかった場合、( つまり、backingInfo.externalStrokes == [] あるいは、MOON.getStrokeJSON( backingID ) == [] ということ) どのように対応すべきか記述されています。

var backingExtStrokes,
    backingId = MOON.getCurrentPage().backing,
    backingInfo = MOON.getPaperJSON(backingId);

if (backingInfo.externalStrokes.length === 0) {
    // とりあえずページ全体のサイズで外部ストローク情報を作っておく.
    backingInfo.externalStrokes = [{ x: 0, y: 0, width: 768, height: 1024, size: 0, name: 'stroke1.json' }];
    MOON.setPaperJSON(backingId, backingInfo);
}

backingExtStrokes = MOON.getStrokeJSON(backingId);

情報が増えたので、「コピペシール」を update しましたが、また失敗しました。とりあえず、途中経過としてこっそり公開。

「コピペシールの update」:但し、不完全です。

上記情報を踏まえて、コピペシールを update しました。後述のように、一部不満は残りますが、一応使えますのでないよりはマシ、と公開することにします。不備を治せるのは先になりそうなので。

Download : 「コピペシール:ver.2.9.0 対応失敗版」ちゃんと更新できたら、削除します。

使用方法は旧版とほぼ同じなので、まずはこちらの記事を参照ください。そのうえで、ひとつだけ機能追加しています。コピったシールをシール台帳に save して、別のページに貼り付けられるようにしました。これで、面倒ながらもページをまたいだコピペが可能になったつもりなのですが、再現条件のよくわからない動きをします。この辺は後述。

処理の中核となる、ストローク情報を操作しているコードを以下に供覧します。(ちなみに、ver.2.8.0 版の古いコードはこちら)

/*
*  CopyPaste
*   by @H_Kuruno - http://kuruno.cocolog-nifty.com/blog/
*                                   last modified on 2013/11/30
*/
(function(global){
    var CONST = {
         ScreenWidth: window.innerWidth
        ,ScreenHeight: window.innerHeight
    };

    var rimStrokes = [
      {width:4,color:0,type:"pen",data:[]}
    ];

    function adjustRimStrokes( width, height ) {
        var COLOR = MOON.rgba2int(145,255,255,255);
        var P = 1;  // 筆圧値
        rimStrokes[0].color = COLOR;
        rimStrokes[0].data = [0,0,P, width - 1,0,P, width - 1,height - 1,P, 0,height - 1,P, 0,0,P];
        return rimStrokes;
    }

    function isStickerStrokeModified() {
        var stickerID = InfoUtil.getStickerID(),
            extStrokes = MOON.getStrokeJSON( stickerID ),
            data = extStrokes[0].strokes[0].data;
        if ( data[0] === 0 && data[1] === 0 ) {
            return true;
        } else {
            return false;
        }
    }

    function showParticesOnAreaBorder( minX, minY, maxX, maxY ) {
        var pList = [];
        var pLife = 60;
        var deltaX = (maxX - minX)/10;
        var deltaY = (maxY - minY)/10;
        for ( var x =  minX; x < maxX; x += deltaX ) {
            pList.push( x, minY, (Math.random()-0.5)/10.0, (Math.random()-0.5)/10.0, pLife );
            pList.push( x, maxY, (Math.random()-0.5)/10.0, (Math.random()-0.5)/10.0, pLife );
        }
        for ( var y = minY; y < maxY; y += deltaY ) {
            pList.push( minX, y, (Math.random()-0.5)/10.0, (Math.random()-0.5)/10.0, pLife );
            pList.push( maxX, y, (Math.random()-0.5)/10.0, (Math.random()-0.5)/10.0, pLife );
        }
        MOON.showParticles( pList );
    }


    function filterExtStrokes( baseX, baseY, isInside ) {
        var cutStrokes = [],
            restExtStrokes = [];

        var page = MOON.getCurrentPage();
        var extStrokes = MOON.getStrokeJSON( page.backing );

        for ( var i = 0, eLen = extStrokes.length; i < eLen; i++ ) {
            var strokes = extStrokes[i].strokes,
                strokesThrough = [];

            for ( var j = 0, sLen = strokes.length; j < sLen; j++ ) {
                var s = strokes[j],
                    dLen = s.data.length;

                if ( isInside( s.data[0], s.data[1] ) && isInside( s.data[dLen-3], s.data[dLen-2] ) ) {
                    for ( var k = 0; k < dLen; k += 3 ) {
                        s.data[k] -= baseX;
                        s.data[k + 1] -= baseY;
                    }
                    cutStrokes.push( s );
                } else {
                    strokesThrough.push( s );
                }
            }
            restExtStrokes.push( {strokes: strokesThrough} );
        }
        return { cut: cutStrokes, rest: restExtStrokes };
        /*
        *   cut  は stroke 形式 [{... data:[]}, {...data:[]},...]
        *   rest は extStroke 形式 [ {stroke: [{...data:[]}, {...data:[]},...]}, [stroke:...],...]
        */
    }


    function addStrokesToSticker( minX, minY, maxX, maxY, strokes ) {
        var width = Math.ceil( maxX - minX );
        var height = Math.ceil( maxY - minY );

        var myStickerID = InfoUtil.getStickerID();

        var orgJSON = MOON.getPaperJSON( myStickerID );
        var paperJSON = MOON.getPaperJSON( myStickerID );
        // MOONPhase ver.2.9.0 以降、シールの表示座標変更が可能になった。
        paperJSON.x = Math.floor( minX );
        paperJSON.y = Math.floor( minY );
        // 一方、MOONPhase ver.2.9.0 以降、シールの「台紙」のサイズ変更ができなくなった。
        // paperJSON.width = width;
        // paperJSON.height = height;
        MOON.setPaperJSON( myStickerID, paperJSON );

        var stickerExtStrokes = MOON.getStrokeJSON( myStickerID );
        stickerExtStrokes[0].strokes = adjustRimStrokes( width, height ).concat( strokes );
        MOON.setStrokeJSON( myStickerID, stickerExtStrokes );
    }

    function addStrokesFromStickerToBacking( baseX, baseY ) {
        var myStickerID = InfoUtil.getStickerID(),
            stickerJSON = MOON.getPaperJSON( myStickerID ),
            stickerX = baseX || stickerJSON.x,
            stickerY = baseY || stickerJSON.y,
            sExtStrokes = MOON.getStrokeJSON( myStickerID ),
            strokes = [];


        for ( var i = 0, eLen = sExtStrokes.length; i < eLen; i++ ){
            var ss = sExtStrokes[i].strokes;
            for ( var j = 0, sLen = ss.length; j < sLen; j++ ){
                for ( var k = 0, dLen = ss[j].data.length; k < dLen; k += 3 ){
                    ss[j].data[k] += stickerX;
                    ss[j].data[k +1] += stickerY;
                }
                strokes.push( ss[j] );
            }
        }
        // 周囲を囲む stroke を捨てる
        strokes = strokes.slice( rimStrokes.length );

        var backingID = MOON.getCurrentPage().backing,
            backingInfo = MOON.getPaperJSON( backingID );
        if ( backingInfo.externalStrokes.length === 0 ) {
            // ページにストロークが一本もない場合、とりあえずページ全体のサイズで外部ストローク情報を作っておく.
            backingInfo.externalStrokes = [
              { x: 0, y: 0, width: CONST.ScreenWidth, height: CONST.ScreenHeight, size: 0, name: 'stroke1.json' }
            ];
        }
        MOON.setPaperJSON( backingID, backingInfo );

        var bExtStrokes = MOON.getStrokeJSON( backingID ),
            beLen = bExtStrokes.length,
            maxX = stickerX + stickerJSON.width - 1,
            maxY = stickerY + stickerJSON.height - 1;

        showParticesOnAreaBorder( stickerX, stickerY, maxX, maxY );
        bExtStrokes[ beLen -1 ].strokes = bExtStrokes[ beLen -1 ].strokes.concat( strokes );
        MOON.setStrokeJSON( backingID, bExtStrokes );
    }

    var cut = function( minX, minY, maxX, maxY, isInside ) {
        var cookedStrokes = filterExtStrokes( minX, minY, isInside );

        showParticesOnAreaBorder( minX, minY, maxX, maxY );
        addStrokesToSticker( minX, minY, maxX, maxY, cookedStrokes.cut );

        var page = MOON.getCurrentPage();
        MOON.setStrokeJSON( page.backing, cookedStrokes.rest );
    };

    var copy = function( minX, minY, maxX, maxY, isInside ) {
        var cookedStrokes = filterExtStrokes( minX, minY, isInside );

        showParticesOnAreaBorder( minX, minY, maxX, maxY );
        addStrokesToSticker( minX, minY, maxX, maxY, cookedStrokes.cut );
    };

    var paste = function() {
        addStrokesFromStickerToBacking();
    };


    var resetCut = function( orgX, orgY ) {
        addStrokesFromStickerToBacking( orgX, orgY );
    };

    var clearSelf = function(){
        var myStickerID = InfoUtil.getStickerID();

        var stickerExtStrokes = MOON.getStrokeJSON( myStickerID );
        for ( i = 0, eLen = stickerExtStrokes.length; i < eLen; i++ ){
            stickerExtStrokes[i].strokes = [];
        }
        MOON.setStrokeJSON( myStickerID, stickerExtStrokes );
    };

    global.CopyPaste = {
         copy: copy
        ,cut: cut
        ,paste: paste
        ,resetCut: resetCut
        ,clearSelf: clearSelf
        ,isStickerStrokeModified: isStickerStrokeModified
    };
})(this);

できたこと、できなかったこと、不満・不可解なこと。

MOONPhase ver.2.8.0 → ver.2.9.0 で可能になったこととして、コードからシールの座標を変更できるようになったことがあります。そのため、今回の update で、ページ上のストロークをペンで囲んだその位置に、コピったあとのシールが移動するようになりました。

ところが、最近の記事でも書いているように、逆にシール(の台紙?)のサイズが変更できなくなりました。元々のシールよりも広い範囲のストロークをコピると、シールからはみ出してしまい、画面に表示されません。

ストローク情報自体は内部に残っているので、ペーストするとちゃんとページ上にストロークが出現するのですが、なんとも不細工です。そのうちなんとかしたいですね。

今回は、とりあえずのその場しのぎとして、シール(台紙)のサイズを最初から大き目に作っています。これでも広い範囲をコピるとはみ出すのですが、それなりには使えるでしょう。

あと、なんとも不可解というか、気持ちの悪い挙動として、MOON.setStrokeJSON() と MOON.setPaperJSON() の両方を実行する場合、その順序によって不思議な挙動をするようです。

たとえば、今回のコピペシール、手順として以下のような操作をしています。

  1. シールを貼りつけて、切り出し GUI を表示した時点で、シール自身が表示されると邪魔になるので、自分のシールのストロークを、「ストローク全消しシール」を参考に、消去。( MOON.setStrokeJSON )
  2. ストロークを切り出したら、シールを切り出したストロークの位置へ移動。( MOON.setPaperJSON )
  3. 移動したシールの上に、切り出したストロークを書き込んで表示する。( MOON.setStrokeJSON )

自然に考えると、透明なシールができて、そのシールが移動して、ストロークを書き込んで初めてシールが見える、という流れを期待するのですが、シールを移動させた時点で一旦、「全消ししたはず」のもともとのシールのストロークが表示されてしまうのです。恐らく、MOON.setPaperJSON() した段階で、ストロークが空でも、canvas.png の内容を画面に表示してしまうのではないかと考えています。

これ以外にも、一旦 cut した後に paste すると、シールの場所にストロークが paste されるのは当然として、cut したはずの、元の場所にもストロークが復活したり、他のページにシールを貼ってペーストすると、ストロークが二重三重に paste されたりします。paste 直後のストロークを指で囲っても認識されず、隣のページに移ってから戻ってくると認識されるような場合もあります。

MOON.setStrokeJSON() のあとに MOON.setPaperJSON() を実行すると、setStrokeJSON() で変更したはずの画面表示が一部無効になるように見えたり、extStrokes の更新と、canvas.png の更新の前後関係や時間差が影響しているように見えたりもします。システムの refactoring で何か複雑なことが起こっているんでしょうか。

今後のメジャーバージョンアップでは、大幅な機能強化とともに、こういう地味なところも改善されるとうれしいですね。

« 2014年4月 | トップページ | 2014年6月 »