| INDEX |

Tweenerの_bezierプロパティは制御点だけを指定するので動きとしては滑らかな軌道を描きますが(イラストレータでいうところのスムーズ)、コーナー連結のように滑らかでない動きができない。曲線のパラメータに連結点を指定すればよいので、_bezierプロパティを実装しているCurveModifiers.asに手を入れてみた。

とりあえず、できあがりはこんな感じ。上が通常_bezier、下が手を入れたもの。極端な例になりますが。上の曲線は始点、制御点、終点が直線上にならんでいるので窪んだコーナーにはいかないわけです。

で、手を入れたスクリプトです。
※「_bezierfull_modifier」はCurveModifiersと同じです。

/**
* CurveFullModifiers for Tweener
* @author nutsu
*
* original code is caurina.properties.CurveModifiers.
* about caurina.properties.CurveModifiers
* author Zeh Fernando, Nate Chatellier, Arthur Debert
*/

package {

  import caurina.transitions.Tweener;

  public class CurveFullModifiers {

    public function CurveFullModifiers () {
      trace ("This is an static class and should not be instantiated.")
    }
    
    public static function init(): void {
      Tweener.registerSpecialPropertyModifier("_bezierfull", _bezierfull_modifier, _bezierfull_get);
    }
    
    public static function _bezierfull_modifier (p_obj:*):Array {
      var mList:Array = [];
      var pList:Array;
      if (p_obj is Array) {
        pList = p_obj;
      } else {
        pList = [p_obj];
      }
      
      var i:uint;
      var istr:String;
      var mListObj:Object = {};
      
      for (i = 0; i < pList.length; i++) {
        for (istr in pList[i]) {
          if (mListObj[istr] == undefined) mListObj[istr] = [];
          mListObj[istr].push(pList[i][istr]);
        }
      }
      for (istr in mListObj) {
        mList.push({name:istr, parameters:mListObj[istr]});
      }
      return mList;
    }
    
    public static function _bezierfull_get (b:Number, e:Number, t:Number, p:Array):Number {
      if (p.length == 1) {
        return b + t*(2*(1-t)*(p[0]-b) + t*(e - b));
      } else {
        var plen:int = (p.length+1)/2;
        var t2:Number = Math.max( Math.min( t, 1.0 ), 0 );
        var ip:uint = Math.min( plen - 1, Math.floor(t2 * plen) );
        var it:Number = (t - (ip * (1 / plen))) * plen;
        var p1:Number, p2:Number;
        if (ip == 0) {
          p1 = b;
          p2 = p[1];
        } else if (ip == plen - 1) {
          p1 = p[ip*2-1];
          p2 = e;
        } else {
          p1 = p[ip*2-1];
          p2 = p[ip*2+1];
        }
        return p1+it*(2*(1-it)*(p[ip*2]-p1) + it*(p2 - p1));
      }
    }
  }
}

使う場合はこんな感じ。

import caurina.transitions.Tweener;

//初期化
CurveFullModifiers.init();

//通常の_bezier。サンプルでいう上
var opt1 = {x:317.95, y:57.3, _bezier:[{x:187,y:52.6},{x:236,y:52.6}]};
opt1["time"] = 2;
opt1["transition"] = "linear";

//手を入れたもの。_bezierfull。連結点も指定
var opt2:Object = {x:317.95, y:167.25, _bezierfull:[{x:187,y:162.55},{x:211.5,y:239.15},{x:236,y:162.55}]};
opt2["time"] = 2;
opt2["transition"] = "linear";

Tweener.addTween( moveObj1, opt1 );
Tweener.addTween( moveObj2, opt2 );

easeInOutBackのようにトランシジョンの値が0以下、1以上の場合、_bezierではうまくいかないのですが、その部分もなんとなく動くようにしてみた。

一応、今回の連結点付きのパラメータを書き出すJSFLも作ってみました。

//****************************************************************************
// Script for Tweener _bezierfull Option
//
// sample output : { x:460, y:100, _bezierfull:[{x:250, y:200}...] }
//****************************************************************************

var edgeArray = fl.getDocumentDOM().selection[0].edges;

var tmp  = [];
var tmp2 = [];
var turn = [];
for ( i=0; i<edgeArray.length; i++ )
  tmp.push( edgeArray[i] );

var e0 = tmp.shift();
tmp2.push( e0 );
turn.push( false );
joinX0 = e0.getControl(0).x;
joinY0 = e0.getControl(0).y;
joinX2 = e0.getControl(2).x;
joinY2 = e0.getControl(2).y;

while( tmp2.length<edgeArray.length )
{
  flg_join = false;
  var min_value = -1;
  var min_pos   = -1;
  var min_index = -1;
  for( i=0; i<tmp.length; i++ )
  {
    var ed = tmp[i];
    var p0 = ed.getControl(0);
    var p2 = ed.getControl(2);
    if( p0.x==joinX2 && p0.y==joinY2 )
    {
      flg_join = true;
      tmp2.push( ed );
      turn.push( false );
      joinX2 = p2.x;
      joinY2 = p2.y;
      tmp.splice( i, 1 );
      i = 0;
    }
    else if( p2.x==joinX0 && p2.y==joinY0 )
    {
      flg_join = true;
      tmp2.unshift( ed );
      turn.push( false );
      joinX0 = p0.x;
      joinY0 = p0.y;
      tmp.splice( i, 1 );
      i = 0;
    }
    else if( p2.x==joinX2 && p2.y==joinY2 )
    {
      flg_join = true;
      tmp2.push( ed );
      turn.push( true );
      joinX2 = p0.x;
      joinY2 = p0.y;
      tmp.splice( i, 1 );
      i = 0;
    }
    else if( p0.x==joinX0 && p0.y==joinY0 )
    {
      flg_join = true;
      tmp2.unshift( ed );
      turn.push( true );
      joinX0 = p2.x;
      joinY0 = p2.y;
      tmp.splice( i, 1 );
      i = 0;
    }
    else
    {
      var min1 = (p0.x-joinX2)*(p0.x-joinX2) + (p0.y-joinY2)*(p0.y-joinY2);
      var min2 = (p2.x-joinX0)*(p2.x-joinX0) + (p2.y-joinY0)*(p2.y-joinY0);
      var min3 = (p2.x-joinX2)*(p2.x-joinX2) + (p2.y-joinY2)*(p2.y-joinY2);
      var min4 = (p0.x-joinX0)*(p0.x-joinX0) + (p0.y-joinY0)*(p0.y-joinY0);
      if( min1<min_value || min_value<0 )
      {
        min_value = min1;
        min_pos   = 1;
        min_index = i;
      }
      if( min2<min_value )
      {
        min_value = min2;
        min_pos   = 2;
        min_index = i;
      }
      if( min3<min_value )
      {
        min_value = min3;
        min_pos   = 3;
        min_index = i;
      }
      if( min4<min_value )
      {
        min_value = min4;
        min_pos   = 4;
        min_index = i;
      }
    }
  }
  //最近傍に連結
  if( flg_join==false )
  {
    if( min_index>= 0 )
    var ed = tmp[min_index];
    var p0 = ed.getControl(0);
    var p2 = ed.getControl(2);
    if( min_pos==1 )
    {
      tmp2.push( ed );
      turn.push( false );
      joinX2 = p2.x;
      joinY2 = p2.y;
    }
    else if( min_pos==2 )
    {
      tmp2.unshift( ed );
      turn.push( false );
      joinX0 = p0.x;
      joinY0 = p0.y;
    }
    else if( min_pos==3 )
    {
      tmp2.push( ed );
      turn.push( true );
      joinX2 = p0.x;
      joinY2 = p0.y;
    }
    else
    {
      tmp2.unshift( ed );
      turn.push( true );
      joinX0 = p2.x;
      joinY0 = p2.y;
    }
    tmp.splice( min_index, 1 );
  }
}


var lastpos = "";
var option = "_bezierfull:[";
if( confirm("Reverse the curve?")==false )
{
  for( i=0; i<tmp2.length; i++ )
  {
    var ed = tmp2[i];
    var p0 = ed.getControl(0);
    var p1 = ed.getControl(1);
    var p2 = ed.getControl(2);
    option += "{x:" + p1.x + ",y:" + p1.y + "}";
      if( i==tmp2.length-1 )
      lastpos = (turn[i]) ? "x:" + p0.x + ", y:" + p0.y : "x:" + p2.x + ", y:" + p2.y;
    else
      option += (turn[i]) ? ",{x:" + p0.x + ",y:" + p0.y + "}," : ",{x:" + p2.x + ",y:" + p2.y + "},";
  }
}
else
{
  for( i=(tmp2.length-1); i>-1; i-- )
  {
    var ed = tmp2[i];
    var p0 = ed.getControl(2);
    var p1 = ed.getControl(1);
    var p2 = ed.getControl(0);
    option += "{x:" + p1.x + ",y:" + p1.y + "}";
      if( i==0 )
      lastpos = (turn[i]) ? "x:" + p0.x + ", y:" + p0.y : "x:" + p2.x + ", y:" + p2.y;
    else
      option += (turn[i]) ? ",{x:" + p0.x + ",y:" + p0.y + "}," : ",{x:" + p2.x + ",y:" + p2.y + "},";
  }
}
option += "]";
fl.trace( "{" + lastpos + ", " + option + "}" );

わりとメジャーな話題かと思ったけど、やはりマイナーかなぁ。という印象。

| INDEX |