ここのサイトにUIでの線の描き方が載っていたので、パスを読み込むスクリプトを作ってみました。簡単な使い方、解説はコードの下に書きます。
以下のコードをテキストにコピーして、拡張子を.jsxにすれば完成です。
(function (aGbl) {
//--------------------------------------------------------------------------------------------------------------------------------------------------
function mCreateUI(aObj) {
var mPorW = (aObj instanceof Panel) ? aObj : new Window("palette", "GetPathUI", undefined,{resizeable: true});
mPorW.preferredSize = [300, 400];
mPorW.margins = [10, 10, 10, 10];
mPorW.spacing = 5;
mPorW.mGpBt = mPorW.add("group { alignment : [ 'left','top' ] ,preferredSize : [-1,20],margins : [0, 0, 0, 0],spacing : 0}");
mPorW.mBtGet = mPorW.mGpBt.add("button { text : 'Get' , alignment : [ 'left','top' ] ,preferredSize : [40,20]}");
mPorW.mBtSet = mPorW.mGpBt.add("button { text : 'Set' , alignment : [ 'left','top' ] ,preferredSize : [40,20]}");
mPorW.mGpEt = mPorW.add("group { alignment : [ 'left','top' ] ,preferredSize : [-1,20],margins : [0, 0, 0, 0],spacing : 10}");
mPorW.mGpEt.add("staticText { text : 'Seg' , alignment : [ 'left','top' ] ,preferredSize : [-1,20]}");
mPorW.mEtSeg = mPorW.mGpEt.add("editText { text : '20' , alignment : [ 'left','top' ] ,preferredSize : [40,20]}");
mPorW.mGpEt.add("staticText { text : 'Strk' , alignment : [ 'left','top' ] ,preferredSize : [-1,20]}");
mPorW.mEtStrk = mPorW.mGpEt.add("editText { text : '2' , alignment : [ 'left','top' ] ,preferredSize : [40,20]}");
mPorW.mGpEt.add("staticText { text : 'IdxSc' , alignment : [ 'left','top' ] ,preferredSize : [-1,20]}");
mPorW.mEtIdxSc = mPorW.mGpEt.add("editText { text : '20' , alignment : [ 'left','top' ] ,preferredSize : [40,20]}");
mPorW.mEtDmy = mPorW.add("editText { text : '' , alignment : [ 'fill','top' ] ,preferredSize : [-1,20],properties:{readonly:true} }");
mPorW.mEtDmy.visible = false;
mPorW.mPlAll = mPorW.add("panel {orientation : 'stack' ,alignment : [ 'fill','fill' ] ,preferredSize : [-1,-1],margins : [0, 0, 0, 0],spacing : 0}");
mPorW.mPl = mPorW.mPlAll.add("group { alignment : [ 'fill','fill' ] ,preferredSize : [-1,-1],margins : [0, 0, 0, 0],spacing : 0}");
var mPlAllGpc = mPorW.mPlAll.graphics;
var brush1 = mPlAllGpc.newBrush( mPlAllGpc.BrushType.SOLID_COLOR, [0,0,0] );
mPlAllGpc.backgroundColor = brush1;
//読み込むパスProp。
mPorW.mPathProp = null;
//クローズか否か。
mPorW.mIsCls = null;
//センター位置を[0,0]とした座標。
mPorW.mVtxs = [];
mPorW.mIns = [];
mPorW.mOuts = [];
mPorW.mClcRng = 6;
mPorW.mVwrCtrPt = null;
mPorW.mPlCtrPt = null;
mPorW.mNowIdx = null;
mPorW.mNowDrg = false;
mPorW.mNowVIO = null;
mPorW.mPl.addEventListener("mouseover", mMseOvr);
mPorW.mPl.addEventListener("mousedown", mMseDwn);
mPorW.mPl.addEventListener("mouseup", mMseUp);
mPorW.mPl.addEventListener("mousemove", mMseMove);
//倍率。
mPorW.mScRatio = 1/2;
mPorW.mNowSpc = false;
mPorW.mNowSpcAndKey = false;
mPorW.mSpcVec = null;
mPorW.addEventListener("keydown", mKeyDwn);
mPorW.addEventListener("keyup", mKeyUp);
mPorW.mCntr = 0;
return mPorW;
}
//-----------------------------------------------------------------------
//mPnlという名でメインウインドウを作成。
var mPnl = mCreateUI(aGbl);
if (mPnl instanceof Window) {
mPnl.center();
mPnl.show();
} else if (mPnl instanceof Panel) {
//UIパネルの場合は以下をしないと自動レイアウトされない。
mPnl.layout.layout(true);
}
//-----------------------------------------------------------------------
//各種数値を変えたら描画し直す。
mPnl.mEtSeg.onChange = mPnl.mEtStrk.onChange =mPnl.mEtIdxSc.onChange = function () {
mFireOnDraw();
}
//-----------------------------------------------------------------------
//以下、キー&マウス操作した結果のイベント関数。
function mMseDwn(e) {
if( mPnl.mNowSpc ){
mPnl.mNowSpcAndKey = true;
//マウス位置から現在のパネル中央までのベクトルを出す。
mPnl.mSpcVec = mPnl.mPlCtrPt - [e.clientX, e.clientY];
}else{
var mClcPtX = e.clientX;
var mClcPtY = e.clientY;
var mClcRng = mPnl.mClcRng;
var mScRatio = mPnl.mScRatio;
//原点をパネル中央にし、VIOの拡大率も合わせた座標+-Rngをクリックしたならば、
//ドラッグ準備OKチェックをtrueにし、該当(選択)インデックスも規定する。
var mVtxs = mPnl.mVtxs.slice();
var mIns = mPnl.mIns.slice();
var mOuts = mPnl.mOuts.slice();
//まずはIOを座標にする。
for (var i = 0; i < mVtxs.length; i++) {
mIns[i] = mIns[i] + mVtxs[i];
mOuts[i] = mOuts[i] + mVtxs[i];
}
//拡大率を適用する。
for (var i = 0; i < mVtxs.length; i++) {
mVtxs[i] = mVtxs[i] * mScRatio;
mIns[i] = mIns[i] * mScRatio;
mOuts[i] = mOuts[i] * mScRatio;
}
//+-Rng内にあるか判断する。
for (var i = 0; i < mVtxs.length; i++) {
var mVtx = mVtxs[i] + mPnl.mPlCtrPt;
var mMinX = mVtx[0] - mClcRng;
var mMaxX = mVtx[0] + mClcRng;
var mMinY = mVtx[1] - mClcRng;
var mMaxY = mVtx[1] + mClcRng;
if (mMinX < mClcPtX && mClcPtX < mMaxX
&& mMinY < mClcPtY && mClcPtY < mMaxY) {
mPnl.mNowDrg = true;
mPnl.mNowIdx = i;
mPnl.mNowVIO = 0;
return;
}
}
for (var i = 0; i < mIns.length; i++) {
var mIn = mIns[i] + mPnl.mPlCtrPt;
var mMinX = mIn[0] - mClcRng;
var mMaxX = mIn[0] + mClcRng;
var mMinY = mIn[1] - mClcRng;
var mMaxY = mIn[1] + mClcRng;
if (mMinX < mClcPtX && mClcPtX < mMaxX
&& mMinY < mClcPtY && mClcPtY < mMaxY) {
mPnl.mNowDrg = true;
mPnl.mNowIdx = i;
mPnl.mNowVIO = 1;
return;
}
}
for (var i = 0; i < mOuts.length; i++) {
var mOut = mOuts[i] + mPnl.mPlCtrPt;
var mMinX = mOut[0] - mClcRng;
var mMaxX = mOut[0] + mClcRng;
var mMinY = mOut[1] - mClcRng;
var mMaxY = mOut[1] + mClcRng;
if (mMinX < mClcPtX && mClcPtX < mMaxX
&& mMinY < mClcPtY && mClcPtY < mMaxY) {
mPnl.mNowDrg = true;
mPnl.mNowIdx = i;
mPnl.mNowVIO = 2;
return;
}
}
}
}
//マウスを上げたら各種状態判断値を初期値に戻す。
function mMseUp(e) {
mPnl.mNowDrg = false;
mPnl.mNowIdx = null;
mPnl.mNowVIO = null;
mPnl.mNowSpc = false;
mPnl.mNowSpcAndKey= false;
mPnl.mSpcVec = null;
mPnl.mEtDmy.active = false;
mPnl.mEtDmy.active = true;
}
function mMseMove(e) {
if (mPnl.mNowDrg) {
var mNowIdx = mPnl.mNowIdx;
var mClcPt = [e.clientX, e.clientY];
//mNowIdxのmVtxをマウス位置に変更する。ただし、
//中心原点を[0,0]に変更、クリック位置を拡大率1の場合へ変え、それを保存用パス情報として更新する。
var mZeroStdPt = mClcPt - mPnl.mPlCtrPt;
var mScRatio = mPnl.mScRatio;
var mScRatioRev = 1 / mScRatio;
var mRstPt = mZeroStdPt * mScRatioRev;
//選択したものがVIOのいずれかによって分岐させる。
if( mPnl.mNowVIO === 0 ){ mPnl.mVtxs[mNowIdx] = mRstPt;}
else if( mPnl.mNowVIO === 1 ){ mPnl.mIns[mNowIdx] = mRstPt - mPnl.mVtxs[mNowIdx];}
else if( mPnl.mNowVIO === 2 ){ mPnl.mOuts[mNowIdx] = mRstPt- mPnl.mVtxs[mNowIdx];}
//要素変数パス情報を使ったonDrawを起こす。
mFireOnDraw();
//スペースキーが押しっぱの場合。
}else if(mPnl.mNowSpcAndKey){
//現在のマウス位置から、mSpcVec(マウスダウン時のマウス位置からパネル中央へのベクトル)まで行ったところが新パネル中央になる。
var mMsePt = [e.clientX, e.clientY];
var mNewPlCtrPt = mMsePt + mPnl.mSpcVec;
mPnl.mPlCtrPt = mNewPlCtrPt;
mFireOnDraw();
}
}
//パネル内にマウスが入ったら、ダミー用の非表示エディットテキストをアクティブにする。
//なんらかのパーツがアクティブでないとキーダウン&アップが効かず、しかしパネルにアクティブというプロパティはないため、ダミーを使う。
function mMseOvr(e) {
mPnl.mEtDmy.active = false;
mPnl.mEtDmy.active = true;
}
function mKeyDwn(e) {
var mKeyNm = e.keyName;
if(mKeyNm === "Space"){
mPnl.mNowSpc = true;
//スケーリングしてonDraw描画する。
}else if( mKeyNm === "Period" ){
mPnl.mScRatio = mPnl.mScRatio *2;
mFireOnDraw();
}else if( mKeyNm === "Comma" ){
mPnl.mScRatio = mPnl.mScRatio /2;
mFireOnDraw();
}
}
//キーを上げたら各種状態判断値を初期値に戻す。
function mKeyUp(e) {
mPnl.mNowSpc = false;
mPnl.mNowSpcAndKey= false;
mPnl.mSpcVec = null;
}
//リサイズ用。MacOSXはonResizeも必要らしい。
mPnl.onResizing = mPnl.onResize = function () {
//パネル内センター位置を割り出す。
var mSize = mPnl.mPl.size;
var mPlCtrPt = [mSize[0]/2,mSize[1]/2];
mPnl.mPlCtrPt = mPlCtrPt;
this.layout.resize ();
mFireOnDraw();
}
//--------------------------------------------------------------------------------------------------------------------------------------------------
mPnl.mBtGet.onClick = function () {
var mAi = app.project.activeItem;
var mSl = mAi.selectedLayers[0];
var mSp = mSl.selectedProperties[0];
mPnl.mPathProp = mSp;
var mPathVal = mSp.path.value;
var mVtxs = mPathVal.vertices;
var mIns = mPathVal.inTangents;
var mOuts = mPathVal.outTangents;
//クローズか否かを保存しておく。
mPnl.mIsCls = mPathVal.closed;
//センター位置(ビューワー座標)を出す。
var mXs = [];
var mYs = [];
for (var i = 0; i < mVtxs.length; i++) {
var mVtx = mVtxs[i];
mXs.push( mVtx[0] );
mYs.push( mVtx[1] );
}
mXs.sort( function( a , b ){ return a - b; } );
mYs.sort( function( a , b ){ return a - b; } );
var mMinX= mXs[0];
var mMaxX= mXs[mXs.length-1];
var mMinY= mYs[0];
var mMaxY= mYs[mYs.length-1];
var mVwrCtrPt = [(mMaxX+mMinX)/2,(mMaxY+mMinY)/2];
mPnl.mVwrCtrPt = mVwrCtrPt;
//センター位置を原点[0,0]とした座標にする。
for (var i = 0; i < mVtxs.length; i++) {
mVtxs[i] = mVtxs[i] - mVwrCtrPt;
}
//パネルObjにパス情報を保存しておく。
mPnl.mVtxs = mVtxs;
mPnl.mIns = mIns;
mPnl.mOuts = mOuts;
//パネル内センター位置を割り出す。
var mSize = mPnl.mPl.size;
var mPlCtrPt = [mSize[0]/2,mSize[1]/2];
mPnl.mPlCtrPt = mPlCtrPt;
//要素変数パス情報を使ったonDrawを起こす。
mFireOnDraw();
}
//--------------------------------------------------------------------------------
mPnl.mPl.onDraw = function () {
if (mPnl.mCntr === 0) { mPnl.mCntr = 1; return;}
var mVtxs = mPnl.mVtxs.slice();
var mIns = mPnl.mIns.slice();
var mOuts = mPnl.mOuts.slice();
//倍率通りにスケーリングする。
var mScRatio = mPnl.mScRatio;
//IOを座標にしたものを作る。
var mInAbsls = mPnl.mIns.slice();
var mOutAbsls = mPnl.mOuts.slice();
for (var i = 0; i < mVtxs.length; i++) {
mInAbsls[i] = mInAbsls[i] +mVtxs[i];
mOutAbsls[i] = mOutAbsls[i] +mVtxs[i];
}
//スケーリングする。
for (var i = 0; i < mVtxs.length; i++) {
mVtxs[i] = mVtxs[i] * mScRatio;
mInAbsls[i] = mInAbsls[i] * mScRatio;
mOutAbsls[i] = mOutAbsls[i] * mScRatio;
}
//IOをベクトルに戻したものも作る。
for (var i = 0; i < mVtxs.length; i++) {
mIns[i] = mInAbsls[i] - mVtxs[i];
mOuts[i] = mOutAbsls[i] - mVtxs[i];
}
//座標を、中央を原点[0,0]としたものからパネル中央を原点としたものに変更する。
for (var i = 0; i < mVtxs.length; i++) {
mVtxs[i] = mVtxs[i] + mPnl.mPlCtrPt;
mInAbsls[i] = mInAbsls[i] + mPnl.mPlCtrPt;
mOutAbsls[i] = mOutAbsls[i] + mPnl.mPlCtrPt;
}
//各種値。
var mSeg = parseInt(mPnl.mEtSeg.text);
if( isNaN(mSeg) ){ mSeg = 20;}
var mAccuracy = 1/mSeg;
var mStrk = parseInt(mPnl.mEtStrk.text);
if( isNaN(mStrk) ){ mStrk = 2;}
var mIdxSc = parseInt(mPnl.mEtIdxSc.text);
if( isNaN(mIdxSc) ){ mIdxSc = 20;}
var mIsCls = mPnl.mIsCls;
//パネルのグラフィックスObjを指定する。
var mGpc = mPnl.mPl.graphics;
//ペンを作っておく。
var mLineGrayLvl = 0.5;
var mPen = mGpc.newPen(mGpc.PenType.SOLID_COLOR, [mLineGrayLvl, mLineGrayLvl, mLineGrayLvl], mStrk);
//グラフィックスにパスを作る。
var mPath = mGpc.newPath();
//始点に移動する。
var mVtxInit = mVtxs[0];
mGpc.moveTo(mVtxInit[0], mVtxInit[1]);
//ラインを作る。
//クローズパスならば最後まで、オープンパスならば最後の1個前まで処理する。
if( mIsCls ){ mVtxLgt = mVtxs.length;}
else{ mVtxLgt = mVtxs.length-1;}
for (var i = 0; i < mVtxLgt; i++) {
if (i === mVtxs.length - 1) { var mNex = 0; }
else { mNex = i + 1; }
var mVtxF = mVtxs[i];
var mVtxL = mVtxs[mNex];
var mOutF = mOuts[i];
var mInL = mIns[mNex];
for (var j = 0; j < 1; j += mAccuracy) {
var mPt = mGetBezierPt(mVtxF, mOutF, mInL, mVtxL, j);
mGpc.lineTo(mPt[0], mPt[1]);
}
}
//クローズパスなら一応閉じる。
if( mIsCls ){mGpc.closePath();}
//パスを実際に描く。
mGpc.strokePath(mPen);
//各点を描く。
//まずはパスObjを更新する。
mGpc.newPath();
//ペンを作っておく。
var mPen = mGpc.newPen(mGpc.PenType.SOLID_COLOR, [mLineGrayLvl, mLineGrayLvl, mLineGrayLvl], mStrk);
//始点に移動する。
mGpc.moveTo(mVtxInit[0], mVtxInit[1]);
//四角形パスを点の数ぶん作る。
var mSclRng = 2;
for (var i = 0; i < mVtxs.length; i++) {
var mVtx = mVtxs[i];
mGpc.rectPath(mVtx[0]-mSclRng , mVtx[1]-mSclRng, mSclRng*2, mSclRng*2);
//パスを実際に描く。
mGpc.strokePath(mPen);
mGpc.newPath();
}
//インアウトラインを描く。
//まずはパスObjを更新する。
mGpc.newPath();
//ペンを作っておく。
var mPen = mGpc.newPen(mGpc.PenType.SOLID_COLOR, [mLineGrayLvl, mLineGrayLvl, mLineGrayLvl], mStrk);
//ラインを作る。
// 1つなぎのIV&VOラインを複数作る。
for (var i = 0; i < mVtxs.length; i++) {
var mVtx = mVtxs[i];
var mIn = mInAbsls[i];
var mOut = mOutAbsls[i];
//mInに移動する。
mGpc.moveTo(mIn[0], mIn[1]);
mGpc.lineTo(mVtx[0], mVtx[1]);
mGpc.lineTo(mOut[0], mOut[1]);
mGpc.strokePath(mPen);
mGpc.newPath();
}
//インアウト点を描く。
//まずはパスObjを更新する。
mGpc.newPath();
//ペンを作っておく。
var mPen = mGpc.newPen(mGpc.PenType.SOLID_COLOR, [mLineGrayLvl, mLineGrayLvl, mLineGrayLvl], mStrk);
//四角形パスを点の数ぶん作る。
var mSclRng = 2;
for (var i = 0; i < mVtxs.length; i++) {
var mIn = mInAbsls[i];
mGpc.rectPath(mIn[0]-mSclRng , mIn[1]-mSclRng, mSclRng*2, mSclRng*2);
//パスを実際に描く。
mGpc.strokePath(mPen);
mGpc.newPath();
var mOut = mOutAbsls[i];
mGpc.rectPath(mOut[0]-mSclRng , mOut[1]-mSclRng, mSclRng*2, mSclRng*2);
//パスを実際に描く。
mGpc.strokePath(mPen);
mGpc.newPath();
}
//インデックスを描く。
//ペンを作っておく。
var mIdxGrayLvl = 1;
var mPen = mGpc.newPen(mGpc.PenType.SOLID_COLOR, [mIdxGrayLvl, mIdxGrayLvl, mIdxGrayLvl], mStrk);
var mOglFnt = mGpc.font;
var mFnt = ScriptUI.newFont( mOglFnt.name,"BOLD",mIdxSc );
for (var i = 0; i < mVtxs.length; i++) {
var mVtx = mVtxs[i];
mGpc.drawString(i.toString() , mPen, mVtx[0] , mVtx[1],mFnt);
}
}
//--------------------------------------------------------------------------------
mPnl.mBtSet.onClick = function () {
//var mAi = app.project.activeItem;
//var mSl = mAi.selectedLayers[0];
//var mSp = mSl.selectedProperties[0];
var mSp = mPnl.mPathProp;
var mPathVal = mSp.path.value;
//パネルObjに保存してあるのは倍率1の、中央を原点[0,0]とした座標なので、それにAEビューワーでの中央位置を足せばよい。
//値適用後もパネル内の形状を保持するため、コピーを使う。
var mVtxs = mPnl.mVtxs.slice();
for (var i = 0; i < mVtxs.length; i++) {
mVtxs[i] = mVtxs[i] + mPnl.mVwrCtrPt;
}
mPathVal.vertices = mVtxs;
mPathVal.inTangents = mPnl.mIns;
mPathVal.outTangents = mPnl.mOuts;
mPathVal.closed = mPnl.mIsCls;
mSp.path.setValue(mPathVal);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------
function mGetBezierPt(aP1, aO1, aI2, aP2, aT) {
var mPt1 = aP1;
var mPt2 = [aP1[0] + aO1[0], aP1[1] + aO1[1]];
var mPt3 = [aP2[0] + aI2[0], aP2[1] + aI2[1]];
var mPt4 = aP2;
var mT = aT;
var m1Mt = 1 - mT;
var mT1 = m1Mt * m1Mt * m1Mt;
var mT2 = 3 * m1Mt * m1Mt * mT;
var mT3 = 3 * m1Mt * mT * mT;
var mT4 = mT * mT * mT;
var mX = mT1 * mPt1[0] + mT2 * mPt2[0] + mT3 * mPt3[0] + mT4 * mPt4[0];
var mY = mT1 * mPt1[1] + mT2 * mPt2[1] + mT3 * mPt3[1] + mT4 * mPt4[1];
return [mX, mY];
}
//--------------------------------------------------------------------------------------------------------------------------------------------------
//オンドローを起こす関数。パネルサイズを変えて元に戻しているだけ。
function mFireOnDraw(){
var mSize = mPnl.mPl.size;
mPnl.mPl.size = [mSize[0] + 1, mSize[1] + 1];
mPnl.mPl.size = [mSize[0], mSize[1]];
}
//--------------------------------------------------------------------------------------------------------------------------------------------------
})(this);
使い方
なかなか長いスクリプトですが、『パス点の番号を目視する』用途で使うものです。
あるいは、パス操作スクリプトやイージングカーブスクリプトを作りたい場合、このコードが役に立つやもです。
一応、形状を変えたりそれを適用したりできますが、ビューワー上でできないことではないので、このスクリプトのままだとあんまし意味ないですね…。
スクリプトを実行するとUIが出ます。
パスを1つ選択してGetボタンを押すと、描画されます。
パネル内でスペースを押しながらドラッグすると画面移動ができます。
パス点やインアウトハンドルを動かすことが可能です。
,でズームアウト、.でズームインします(AEと一緒で感覚的には<と>ですが、シフトはいりません)。
Seg:パスをどれだけ詳細に描くか(実は細か~く見ると曲線ではなく直線です)
Strk:線の太さ
IdxSc:番号のスケール
Setボタンで、変えた形を適用できます。
*点の複数選択はできません。
*複数パスを読み込むことはできません。
mPnl.mBtSet.onClickの箇所のコメントアウトしているところを生かし、逆にその直下の1行をコメントアウトすれば、Getで得たパスではなく新たに選択しているパスに計上を適用できます。
*シェイプグループトランスフォームによる形状には対応していません。
*最初のほうのpreferredSizeを変えれば、出現時のパネルの大きさを変えられます。
解説
パス点を得る用途で作ったのですが、色々操作できるようにした結果、やけに長いスクリプトになってしまいました…。
以下、作っていて引っかかった点を挙げていきます。
パス点の場所自体は通常のpath.value.verticesなどで得られるのですが、ビューワーでの座標です。なのでそれらの点の中心をアバウトに出して、中心が[0,0]になるように全ての点を引き算しておきます。そして、パネルの中心を出してそこが[0,0]となるよう足し算、移動しています。
UIに線を描写できるのはonDrawのみのようなので、パネルのスケールをちょい足し&ちょい引きで再描画するようにしてあります。
再描画というか、自動レイアウトに関するメソッドは
layout.layout()
layout.layout(true)
layout.resize()
の3つのようですが、違いがなんともわからないですね…。確認できたのは、
layout.layout(true) にすると、手動でリサイズしたときにpreferredSizeに戻ってしまう(逆に言えば preferredSize を再参照してくれる)、
layout.resize()はonResizeとかonResizingで使う
layout.layout()はボタン等を後で追加するようなスクリプトに使うもの?
といった点です。
layout.layout() しても、結果サイズが変わらない場合はonDraw扱いにならないので、明示的なサイズのちょい足し&ちょい引きにしました。
キーダウンやアップイベントはなぜか「別パーツでもよいから何かがアクティブになっていないと効かない」場合があり、かつ「パーツとしてのパネルにはアクティブというプロパティは無い」ので、パネル内にマウスが入ったら自動的にダミーの隠してあるエディットテキストがアクティブになる仕様にしてあります。テキストはリードオンリーにしてあるので、アクティブ状態でもそこに何か書き込まれることはない、といった具合ですね。
コード内でやっているようにマウスダウンなどのイベントを駆使すれば、新たに『複数選択』や『選択部分の色変え』なども実装可能かもですね。
最初に仕様にしてしまったのでアレなんですが、読み込んで保存するインアウトハンドルはベクトルじゃなく座標にしとけばもう少しコードが簡単になるかもです…。
以上です!この記事が誰かの何かの役に立てば…。
参考サイト
さかもとのサイト『好きな形を描く』
AEP PROJECT bryful様の投稿『After Effectsユーザーのための、プログラミング入門 その9 リマップ編集スクリプト。AEスクリプトでのイベント処理』