環境:Houdini 20.0.751
円充填に取り組んでみました。
調べているとアルゴリズムは色々ありそうですが、まずは思いついた簡単そうなものからやってみました。隙間が気になるけど今回は多めに見ます。
アルゴリズム
Add SOP等で多角形のポリゴンをつくり、そこにScatter SOPであらかじめ大量のポイントを生成しておく。
ループ処理を使い、以下を設定した回数だけ繰り返す
1.Scatter SOPで生成したポイントをひとつ取り出す
2.多角形の各エッジとの距離を計算して、エッジに近ければエッジに隣接する半径にする。最小半径よりも近かったら消去。
3.周囲のポイント(円)と比較する。周りにポイントがなければ半径を最大距離にする。距離が近ければ相手の半径に隣接する値にする。遠ければ最大半径にする。相手の半径内だったら消去。
4.生き残ったポイントを生成
大きい円から配置していき、隙間を埋めていくように小さい円を配置していく、という流れです。
ノード構成
Block BeginのMethodはFetch Feedbackにする。Block EndのIteration MethodはBy Countに。
Null SOPにまとめたパラメータ類
・最小半径
・最大半径
・イテレーション回数
Wrangleでポイントを生成し、半径(radius)とスケール(pscale)アトリビュートを設定しておきます。最後にCopy to Points SOPで半径1の円を各ポイントに配置させています。
コード
//
// input0: -
// input1: scattered points
// input2: boundary primitive
// input3: block begin(iteration)
//
// RunOver: Detail
// 円の最大半径と最小半径
float maxRadius = `chs("../Parameters/maxRadius")`;
float minRadius = `chs("../Parameters/minRadius")`;
// イテレーションごとにScatter Pointからポイントを取得する
int iteration = detail(3, "iteration", 0);
vector new_pos = point(1, "P", iteration);
// エッジやポイントとの最短距離
float closeDist = 10000;
// 各ボーダーエッジとの最短距離を計算する
for(int i = 0; i < npoints(2); i++)
{
vector p0 = point(2, "P", i);
vector p1 = point(2, "P", (i+1)%npoints(2));
vector v = normalize(p1 - p0);
float d = dot(new_pos - p0, v);
// 直線の範囲内か判別
float dist = 0;
if(d >= 0 && d <= length(p1-p0))
{
vector p2 = p0 + v * d; // 最短距離の座標
dist = length(p2 - new_pos);
}
// 位置が始点より前の場合
else if(d < 0)
{
dist = length(new_pos - p0);
}
// 位置が終点より後ろの場合
else if(d > length(p1 - p0))
{
dist = length(new_pos - p1);
}
if(dist < closeDist)
closeDist = dist;
}
// ボーダーエッジまでの距離が最小半径より小さかったら終了
if (closeDist < minRadius)
return;
// ボーダーまでの距離を最大半径にクランプしておく
if(closeDist > maxRadius)
closeDist = maxRadius;
// 周りのポイント(円)との距離を比較する
int npts[] = nearpoints(0, new_pos, maxRadius*2);
int closeId = -1;
// まわりにポイントがないならポイントを生成して終了
if(len(npts) == 0)
{
// ポイントを生成する
int pt = addpoint(0, new_pos);
setpointattrib(0, "radius", pt, closeDist);
setpointattrib(0, "pscale", pt, closeDist);
}
else
{
// 他のポイントとの比較
for(int i = 0; i < len(npts); i++)
{
vector n_pos = point(0, "P", npts[i]);
float n_radius = point(0, "radius", npts[i]);
float dist = length(new_pos - n_pos);
float currentRadius = 0;
float rest = dist - n_radius;
// 相手の円の範囲内または円の最小半径以下なら終了
if(dist < n_radius || rest < minRadius)
return;
// 距離の差が最大半径以上なら最大値にクランプする
else if(rest > maxRadius)
currentRadius = maxRadius;
else
currentRadius = rest;
// 他の半径より小さいものを選ぶ
if(currentRadius < closeDist)
closeDist = currentRadius;
}
// ポイントを生成する
int pt = addpoint(0, new_pos);
setpointattrib(0, "radius", pt, closeDist);
setpointattrib(0, "pscale", pt, closeDist);
}
各エッジとポイントの距離はこの計算を使っています。
線分と点の距離