« お詫び | トップページ | C4D ポイントやポリゴンをランダム選択するユーザスクリプト。R10 & R9 »

C4D C.O.F.F.E.E. Randomクラスを使ってポリゴンやポイントをランダムに選択する。

さて、ポリゴンやポイントをランダムに選択すには、どうしますか?
もっとも簡単な方法は、2種類あります。

では、1つ目は、確立選択。
全ての選択対象のエレメントを捜査し、その1エレメントの選択状況をランダム値の確立で選択/非選択を決定する。
簡単に言えば、100人にサイコロを振ってもらい、出た目が1なら合格。この場合は1/6の確立になり、16人が合格です。
が、偶然にも全員が1を出すかも知れません。
確立なので、実際の選択数は、きっちり確立の割合にはならず前後します。

/*
50%の確立でポリゴンを選択します。
2009.6.20
*/


main(doc , op)
{
  if(!instanceof(op , PolygonObject))return;

  var sel = op->GetPolygonSelection();
  var pgc = op->GetPolygonCount();
  var rnd = new(Random);
  var i;

  sel->DeselectAll();
  for(i = 0 ; i < pgc ; i++){
    if(rnd->Get01() < 0.5)sel->Select(i);
  }

  op->SetPolygonSelection(sel);
}

平面オブジェクトを分割20の400ポリゴンでスクリプトを実行。
属性マネージャは「ウィンドウ」メニューの「選択/構造情報」です。

Romdom07

選択枚数は195枚です。400枚の50%の200枚には5枚足りませんが、ほぼ50%です。

RandomクラスのGet01()で0.0~1.0のランダム値を取得し、0.5未満の場合を選択にしました。割合なので0.5以上でもかまいません。

2つ目は、ランダム抽出。
先に、選択数を決めて、選択数の数だけ選択対象のエレメントのインデックス番号を乱数で決めます。
例えば、100枚の番号カードを用意し、1/3を抽出するとします。33枚と0.3333...
シャッフルして無作為に33枚を選択。選択されたカードの番号が対象。
この場合、最初から対象数が決められているので、全体の選択比率の誤差は最小限(0.3333....)になります。
ただし、全体の枚数が少ないと誤差が大きくなります。

/*
全体の
50%を抽出してポリゴンを選択します。
2009.6.20
*/


main(doc , op)
{
  if(!instanceof(op , PolygonObject))return;

  var sel = op->GetPolygonSelection();
  var pgc = op->GetPolygonCount();
  var rnd = new(Random);
  var i;
  var max = pgc * 0.5;
  var number;

  sel->DeselectAll();
  for(i = 0 ; i < max ; i++){
    do{
      number = int(rnd->Get01() * pgc);
    }while(sel->IsSelected(number));
    sel->Select(number);
  }

  op->SetPolygonSelection(sel);
}

先ほどと同じオブジェクトで実行しました。

Romdom08

今度は、確実に400枚中の50%の200枚が選択されています。

ランダム値を使って、同じ番号が選択されたら、違う番号が出るまで繰り返しています。

1つ目のスクリプトはポリゴンの枚数分繰り返します。
が、2つ目の場合は、ダブりが出なければ選択枚数のみの繰り返しですみます。
ただし、選択割合が大きくなれば、ダブリが出る確率が増え、繰り返し回数が増えます。

ポリゴンの選択状況を取得してBaseSelectを得ていますが、

var sel = new(BaseSelect);

これでも、でもかまいません。

現在の選択状況を考慮したうえで、ランダム選択するのであれば、GetPolygonSelection()をする必要がありますけどね。

上のスクリプトはポリゴンですが、ポイントを選択する場合も同じようにできますね。
多分、このブログを読んでいただいている方は、説明しなくても変更できることでしょう。
なので、説明は省きます。

ここからは、2個目のスクリプトを使って進めていきます。

では、このスクリプトを、モード対応させます。
ポイントモード/ポリゴンモードで対象エレメントをポイント/ポリゴンに切り替えます。

/*
全体の50%を抽出してモードを考慮してエレメントを選択します。
2009.6.20
*/


main(doc , op)
{
  if(!instanceof(op , PolygonObject))return;

  var sel;
  var poc;

  if(IsCommandChecked(12139)){//ポイントモード
    sel = op->GetPointSelection();
    poc = op->GetPointCount();
  }else if(IsCommandChecked(12187)){//ポリゴンモード
    sel = op->GetPolygonSelection();
    poc = op->GetPolygonCount();
  }else return;

  var rnd = new(Random);
  var i;
  var max = poc * 0.5;
  var number;

  //エレメントの選択
  sel->DeselectAll();
  for(i = 0 ; i < max ; i++){
    do{
      number = int(rnd->Get01() * poc);
    }while(sel->IsSelected(number));
    sel->Select(number);
  }

  //選択の適用
  if(IsCommandChecked(12139)){//ポイントモード
    op->SetPointSelection(sel);
  }else if(IsCommandChecked(12187)){//ポリゴンモード
    op->SetPolygonSelection(sel);
  }
}

これで、モード毎に選択エレメントが変わります。

Romdom09

ポイントも同じように、50%選択されました。

ここまでのスクリプトは、現在の選択範囲には関係なく、強制的に全体からの割合で選択しています。

では、現在の選択範囲を考慮した選択をしてみましょう。

/*
全体の50%を抽出してモードを考慮してエレメントを選択します。
現在の選択範囲も考慮
2009.6.20
*/


main(doc , op)
{
  if(!instanceof(op , PolygonObject))return;

  var sel;
  var poc;
  var p_sel = new(BaseSelect);
  p_sel->DeselectAll();

  if(IsCommandChecked(12139)){//ポイントモード
    sel = op->GetPointSelection();
    poc = op->GetPointCount();
  }else if(IsCommandChecked(12187)){//ポリゴンモード
    sel = op->GetPolygonSelection();
    poc = op->GetPolygonCount();
  }else return;

  if(!sel->GetCount())sel->SelectAll(0 , poc - 1);

  var rnd = new(Random);
  var i;
  var max = sel->GetCount() * 0.5;
  var number;

  //エレメントの選択
  for(i = 0 ; i < max ; i++){
    do{
      number = int(rnd->Get01() * poc);
    }while(p_sel->IsSelected(number) || !sel->IsSelected(number));
    p_sel->Select(number);
  }

  //選択の適用
  if(IsCommandChecked(12139)){//ポイントモード
    op->SetPointSelection(p_sel);
  }else if(IsCommandChecked(12187)){//ポリゴンモード
    op->SetPolygonSelection(p_sel);
  }
}

これで、現在の選択範囲を考慮した、ランダムセレクタになりました。

ツール実行前です。選択ポリゴンが136枚です。50%は68枚です。

Romdom10

実行後の選択範囲です。

Romdom11

計算通り68枚。選択範囲は、実行前の選択範囲外には及んでいません。
ポイントも問題ないようです。

現在、このスクリプトはmain()関数のみですが、オブジェクトのエレメントをランダムに選択する関数にします。
渡す値は、オブジェクト、選択割合、ランダムシード。

/*
全体の50%を抽出してモードを考慮してエレメントを選択します。
現在の選択範囲も考慮
2009.6.20
*/


ObjectElementsRandomSelection(obj , rate , seed)
{
  if(!instanceof(obj , PolygonObject))return;

  var sel;
  var poc;
  var p_sel = new(BaseSelect);
  p_sel->DeselectAll();

  if(IsCommandChecked(12139)){//ポイントモード
    sel = obj->GetPointSelection();
    poc = obj->GetPointCount();
  }else if(IsCommandChecked(12187)){//ポリゴンモード
    sel = obj->GetPolygonSelection();
    poc = obj->GetPolygonCount();
  }else return;

  if(!sel->GetCount())sel->SelectAll(0 , poc - 1);

  var rnd = new(Random);
  rnd->Init(seed);
  var i;
  var max = sel->GetCount() * rate;
  var number;

  //エレメントの選択
  for(i = 0 ; i < max ; i++){
    do{
      number = int(rnd->Get01() * poc);
    }while(p_sel->IsSelected(number) || !sel->IsSelected(number));
    p_sel->Select(number);
  }

  //選択の適用
  if(IsCommandChecked(12139)){//ポイントモード
    obj->SetPointSelection(p_sel);
  }else if(IsCommandChecked(12187)){//ポリゴンモード
    obj->SetPolygonSelection(p_sel);
  }
}

main(doc , op)
{
  ObjectElementsRandomSelection(op , 0.5 , 0);
}

これで、ポリゴンオブジェクトのエレメントをランダムセレクトする関数になりましたが…
関数名が長いなぁ…

次に複数オブジェクトの対応です。
main()関数を変更します。

main(doc , op)
{
  if(!object(0))return;
  var i;

  for(i = 0 ; object(i) ; i++){
    ObjectElementsRandomSelection(object(i) , 0.5 , 0);
  }
}

複数オブジェクトにも対応しました。

では、最後にダイアログも使ってみましょう。
Undoにも対応させましょう。

/*
ポリゴンオブジェクトのエレメントをランダムセレクトします。
2009.6.20
*/


class tools_dialog : GeModalDialog
{
  public:
    var seed;
    var rate;

    tools_dialog();
    Init();
    CreateLayout();
    AskClose();
}

tools_dialog::tools_dialog()
{
  super();
  seed = 0;
  rate = 0.5;
}

tools_dialog::Init()
{
  SetInt(1002 , seed , 0 , 2000000000 , 1);
  SetFloat(1004 , rate , 0.0 , 1.0 , 1);
}

tools_dialog::CreateLayout()
{
  SetTitle("ポリゴンオブジェクトの変形");
  AddGroupBeginV(1000 , BFV_SCALEFIT , 2 , "tools group" , 0);
    AddStaticText(1001 , 0 , 0 , 0 , "シード" , 0);
    AddEditNumber(1002, 0, 150, 0);
    AddStaticText(1003 , 0 , 0 , 0 , "選択率" , 0);
    AddEditNumber(1004, 0, 150, 0);
  AddGroupEnd();
  AddDlgGroup(DR_DLGGROUP_OK | DR_DLGGROUP_CANCEL);
}

tools_dialog::AskClose()
{
  seed = GetInt(1002);
  rate = GetFloat(1004);
}

ToolsExecution(obj , rate , seed)
{
  if(!instanceof(obj , PolygonObject))return;

  var sel;
  var poc;
  var p_sel = new(BaseSelect);
  p_sel->DeselectAll();

  if(IsCommandChecked(12139)){//ポイントモード
    sel = obj->GetPointSelection();
    poc = obj->GetPointCount();
  }else if(IsCommandChecked(12187)){//ポリゴンモード
    sel = obj->GetPolygonSelection();
    poc = obj->GetPolygonCount();
  }else return;

  if(!sel->GetCount())sel->SelectAll(0 , poc - 1);

  var rnd = new(Random);
  rnd->Init(seed);
  var i;
  var max = sel->GetCount() * rate;
  var number;

  //エレメントの選択
  for(i = 0 ; i < max ; i++){
    do{
      number = int(rnd->Get01() * poc);
    }while(p_sel->IsSelected(number) || !sel->IsSelected(number));
    p_sel->Select(number);
  }

  //選択の適用
  if(IsCommandChecked(12139)){//ポイントモード
    obj->SetPointSelection(p_sel);
  }else if(IsCommandChecked(12187)){//ポリゴンモード
    obj->SetPolygonSelection(p_sel);
  }
}



main(doc , op)
{
  if(!object(0))return;

  var t_dialog = new(tools_dialog);
  t_dialog->Open(-1 , -1);
  if(!(t_dialog->GetResult()))return;

  var i;

  doc->StartUndo();
  for(i = 0 ; object(i) ; i++){
    doc->AddUndo(UNDO_OBJECT_BASEDATA , object(i));

    ToolsExecution(object(i) , t_dialog->rate , t_dialog->seed);
  }
  doc->EndUndo();
}

ダイアログも組み込みました。
機能はシンプルですが、これで実用的になりました。

|

« お詫び | トップページ | C4D ポイントやポリゴンをランダム選択するユーザスクリプト。R10 & R9 »