« C4D C.O.F.F.E.E. SendModelingCommand()とCallCommand()。どっちを使う? | トップページ | C4D C.O.F.F.E.E.SendModelingCommand()「始点の設定」 »

C4D C.O.F.F.E.E. スプラインのポイント間の長さって?

C.O.F.F.E.E.でスプラインの長さを取得するには、SplineObjectクラスのGetLength()メンバ関数を使うのですが…
GetLength()メンバ関数はセグメント毎の長さを取得できます。
スプラインの全長を取得するならば、全セグメントの長さを求めて、その総和を出せば良いのだ。

Length_03_2

では、ポイント間の長さを取得するにはどうするんだろうか?
スプラインには、ご存知の通り色々な種類があります。
必要なスプラインの事を学習し数式を理解し、スプラインの補間の仕方を習得すれば簡単に求められます。

では、知らない人や、数学が苦手な人は半永久的に求められないのだろうか?

そんな事はありませんよね。

気づいている人も多いと思いますが…
前回、前々回の記事がヒントです。

簡単に言えば、必要なポイント以外を全て削除して長さを取得する。
だけで良いのです。

即行で作りたいので、条件を絞ります。

オブジェクトは単独選択。
理由は、簡単だから。

スプラインは、ベジェに限ります。
理由はポイントを削除しても形が保たれているから。

B-スプラインは除外し、他のスプラインはベジェに変更後に処理します。
B-スプラインの除外の理由は、ポイントの通過は始点と終点のみだから。

セグメントは無し。
理由は、セグメントの処理を省けるから。

スプラインは開いている。
理由、閉じているとスプラインの始点の設定を新たにしないといけないから。

では、作成してみましょう。

/*
スプラインのポイント間の長さを求めるテスト
2009.6.23
*/


main(doc , op)
{
  //処理対象外オブジェクトの除外-------------------------------
  if(!instanceof(op , SplineObject))return;//スプラインオブジェクト以外対象外
  if(op#SPLINEOBJECT_CLOSED)return;        //閉じたスプラインは対象外
  if(op->GetSegmentCount() >= 2)return;    //セグメントがある場合は対象外
  if(op#SPLINEOBJECT_TYPE == SPLINEOBJECT_TYPE_BSPLINE)return;//B-スプラインは対象外
  if(op->GetPointSelection()->GetCount() != 1)return;//選択されたポイントは1個以外対象外

  //UNDOの記録---------------------------------------------
  doc->StartUndo();
  doc->AddUndo(UNDO_OBJECT_BASEDATA , op);
  doc->EndUndo();

  //処理対象のベジェに変換------------------------------------
  var ct = new(BaseContainer);
  var liner = FALSE;

  if(op#SPLINEOBJECT_TYPE == SPLINEOBJECT_TYPE_LINEAR)liner = TRUE;

  op#SPLINEOBJECT_TYPE = SPLINEOBJECT_TYPE_BEZIER;
  //スクリプト実行前のスプラインタイプが線形の場合は、ベジェのハード補間
  if(liner)SendModelingCommand(MCOMMAND_SPLINE_HARDINTERPOLATION , NULL , op , ct , MODIFY_ALL);

  //不必要な部分の削除の準備-----------------------------------
  var sel = op->GetPointSelection();
  var ptc = op->GetPointCount();
  var i;

  //選択されたポイントの隣のポイントを選択------------------------
  var comp = FALSE;
  for(i = 0 ; i < ptc - 1 ; i++){
    if(sel->IsSelected(i)){
      sel->Select(i + 1);
      comp = TRUE;
      break;
    }
  }
  if(!comp)return;
  op->SetPointSelection(sel);

  //不必要な部分を削除----------------------------------------
  ct = new(BaseContainer);
  //選択範囲を反転
  SendModelingCommand(MCOMMAND_SELECTINVERSE , NULL , op , ct , MODIFY_POINTSELECTION);
  //ポイントの削除
  SendModelingCommand(MCOMMAND_DELETE , NULL , op , ct , MODIFY_POINTSELECTION);

  //スプラインの長さを求めコンソールに表示する---------------------
  op->InitLength(0);
  println(op->GetLength());
}

では、ポイントを1つ選択してスクリプトを実行します。

Length_01_2

Call_sendmodeling_05

Length_02_2

このスクリプトは、セグメント無しのB-スプライン以外の開いたスプラインが対象です。
選択した1ポイントと次のポイントまでの長さをコンソールに表示します。
ただし、オブジェクトは加工されてしまいます。

238.065445

実行すると、ポイント間の長さが取得できます。

このままでは、必要なスプラインが加工されてしまいます。

どうするのか?

できるだけ簡単に処理したいので、スクリプト中でスプラインオブジェクトの複製を取得し、これを加工しポイント間の長さを取得してみましょう。

C.O.F.F.E.E.でオブジェクトを複製するにはBaseObject::GetClone()です。

BaseObjectクラスGetClone()メンバ関数の引数

CL_NO_BITS BaseList2DのBitは対象外
CL_ONLY_VISIBLETAGS オブジェクトマネージャのタグのみ
CL_NO_TRACKS トラックは対象外
CL_NO_HIERARCHY オブジェクトのみ(子オブジェクトは含まず)

選択したオブジェクトのみ、なのでCL_NO_HIERARCHYを渡します。

修正後のスクリプトは、Undoは必要ないので削除します。

スクリプトにGetClone()を追加し、それ以降のスプラインの処理は複製されたものを使います。

/*
スプラインのポイント間の長さを求めるテスト修正
2009.6.23
*/


main(doc , op)
{
  //処理対象外オブジェクトの除外--------------------------------
  if(!instanceof(op , SplineObject))return;//スプラインオブジェクト以外対象外
  if(op#SPLINEOBJECT_CLOSED)return;        //閉じたスプラインは対象外
  if(op->GetSegmentCount() >= 2)return;    //セグメントがある場合は対象外
  if(op#SPLINEOBJECT_TYPE == SPLINEOBJECT_TYPE_BSPLINE)return;//B-スプラインは対象外
  if(op->GetPointSelection()->GetCount() != 1)return;//選択されたポイントは1個以外対象外

  var dummy = op->GetClone(CL_NO_HIERARCHY);//作業用複製

  //処理対象のベジェに変換-------------------------------------
  var ct = new(BaseContainer);
  var liner = FALSE;

  if(dummy#SPLINEOBJECT_TYPE == SPLINEOBJECT_TYPE_LINEAR)liner = TRUE;

  dummy#SPLINEOBJECT_TYPE = SPLINEOBJECT_TYPE_BEZIER;
  //スクリプト実行前のスプラインタイプが線形の場合は、ベジェのハード補間
  if(liner)SendModelingCommand(MCOMMAND_SPLINE_HARDINTERPOLATION , NULL , dummy , ct , MODIFY_ALL);

  //不必要な部分の削除の準備-----------------------------------
  var sel = dummy->GetPointSelection();
  var ptc = dummy->GetPointCount();
  var i;

  //選択されたポイントの隣のポイントを選択------------------------
  var comp = FALSE;
  for(i = 0 ; i < ptc - 1 ; i++){
    if(sel->IsSelected(i)){
      sel->Select(i + 1);
      comp = TRUE;
      break;
    }
  }
  if(!comp)return;
  dummy->SetPointSelection(sel);

  //不必要な部分を削除----------------------------------------
  ct = new(BaseContainer);
  //選択範囲を反転
  SendModelingCommand(MCOMMAND_SELECTINVERSE , NULL , dummy , ct , MODIFY_POINTSELECTION);
  //ポイントの削除
  SendModelingCommand(MCOMMAND_DELETE , NULL , dummy , ct , MODIFY_POINTSELECTION);

  //スプラインの長さを求めコンソールに表示する---------------------
  dummy->InitLength(0);
  println(dummy->GetLength());
}

では、同じポイントを選択して実行します。
選択されているスプラインが、加工されずにコンソールにポイント間の長さを表示できれば問題解決です。

実行後、スプラインには何の変化もありません。

Length_01_2

238.065445

結果も同じです。

さて、このスプラインでは、本当にポイント間の長さを取得できているのかわかりません。
ですから、わかりやすいスプラインを試してみます。

Length_04_3

真っ直ぐなスプラインです。
白の数値がx座標。
ピンクの数値が隣までの距離です。直線なのでピンクの数値が取得できれば合格です。

では、ポイント1つづつ選択して実行してみます。

145.376434
164.554611
92.771729
97.297180

問題ないようです。

では、この状態では使い勝手がよくないので、関数にして見ましょう。
関数に渡す引数は、スプラインオブジェクトとポイント番号。
返ってくる値は指定したポイントと次のポイントの間の長さ。

//セグメント無しのBスプライン以外のスプラインのポイント間の長さを取得
GetBetweenLength(spline , number)
{
  if(!instanceof(spline , SplineObject))return;
  if(spline#SPLINEOBJECT_CLOSED)return;
  if(spline->GetSegmentCount() >= 2)return;
  if(spline#SPLINEOBJECT_TYPE == SPLINEOBJECT_TYPE_BSPLINE)return;
  if(number < 0 || number > spline->GetPointCount() - 2)return;

  var dummy = spline->GetClone(CL_NO_HIERARCHY);

  var ct = new(BaseContainer);
  var liner = FALSE;

  if(dummy#SPLINEOBJECT_TYPE == SPLINEOBJECT_TYPE_LINEAR)liner = TRUE;

  dummy#SPLINEOBJECT_TYPE = SPLINEOBJECT_TYPE_BEZIER;
  if(liner)SendModelingCommand(MCOMMAND_SPLINE_HARDINTERPOLATION , NULL , dummy , ct , MODIFY_ALL);

  var sel = dummy->GetPointSelection();
  var ptc = dummy->GetPointCount();
  var i;

  sel->DeselectAll();
  sel->Select(number);

  var comp = FALSE;
  for(i = 0 ; i < ptc - 1 ; i++){
    if(sel->IsSelected(i)){
      sel->Select(i + 1);
      comp = TRUE;
      break;
    }
  }
  if(!comp)return;
  dummy->SetPointSelection(sel);

  ct = new(BaseContainer);
  SendModelingCommand(MCOMMAND_SELECTINVERSE , NULL , dummy , ct , MODIFY_POINTSELECTION);
  SendModelingCommand(MCOMMAND_DELETE , NULL , dummy , ct , MODIFY_POINTSELECTION);

  dummy->InitLength(0);

  return dummy->GetLength();
}

若干変わりましたが、ほとんど同じです。
では、この関数を使ってみましょう。
数式を使って求めていないので、まるで機械仕掛けの関数のようだ…
ちょっと照れますね…

/*
スプラインのポイント間の長さを求めるテスト
2009.6.23
*/


//セグメント無しのBスプライン以外のスプラインのポイント間の長さを取得
GetBetweenLength(spline , number)
{
  if(!instanceof(spline , SplineObject))return;
  if(spline#SPLINEOBJECT_CLOSED)return;
  if(spline->GetSegmentCount() >= 2)return;
  if(spline#SPLINEOBJECT_TYPE == SPLINEOBJECT_TYPE_BSPLINE)return;
  if(number < 0 || number > spline->GetPointCount() - 2)return;

  var dummy = spline->GetClone(CL_NO_HIERARCHY);

  var ct = new(BaseContainer);
  var liner = FALSE;

  if(dummy#SPLINEOBJECT_TYPE == SPLINEOBJECT_TYPE_LINEAR)liner = TRUE;

  dummy#SPLINEOBJECT_TYPE = SPLINEOBJECT_TYPE_BEZIER;
  if(liner)SendModelingCommand(MCOMMAND_SPLINE_HARDINTERPOLATION , NULL , dummy , ct , MODIFY_ALL);

  var sel = dummy->GetPointSelection();
  var ptc = dummy->GetPointCount();
  var i;

  sel->DeselectAll();
  sel->Select(number);

  var comp = FALSE;
  for(i = 0 ; i < ptc - 1 ; i++){
    if(sel->IsSelected(i)){
      sel->Select(i + 1);
      comp = TRUE;
      break;
    }
  }
  if(!comp)return;
  dummy->SetPointSelection(sel);

  ct = new(BaseContainer);
  SendModelingCommand(MCOMMAND_SELECTINVERSE , NULL , dummy , ct , MODIFY_POINTSELECTION);
  SendModelingCommand(MCOMMAND_DELETE , NULL , dummy , ct , MODIFY_POINTSELECTION);

  dummy->InitLength(0);

  return dummy->GetLength();
}



main(doc , op)
{
  if(!instanceof(op , SplineObject))return;
  if(op->GetSegmentCount() >= 2)return;
  if(op#SPLINEOBJECT_CLOSED)return;

  var ptc = op->GetPointCount();
  var i;

  for(i = 0 ; i < ptc - 1 ; i++){
    println(i , " : " , GetBetweenLength(op , i));
  }
}

さて、先ほどのスプラインに実行してみます。

Length_04_3

0 : 145.376434
1 : 164.554611
2 : 92.771729
3 : 97.297180

同じように出力されました。
条件付きですが、ポイント間の長さが取得できました。
条件外のスプラインを渡した時には、nilが返ります。

|

« C4D C.O.F.F.E.E. SendModelingCommand()とCallCommand()。どっちを使う? | トップページ | C4D C.O.F.F.E.E.SendModelingCommand()「始点の設定」 »