処理の改良回になります。Arrayオブジェクトの使い方なども勉強になります。
自分で考えて実装したほうが覚えるよね
前回は、「Interface Design」の「The Point, Line, and Rectangle classes」チュートリアルの「Dealing with lines」項目のプログラム実装を行いました。実装のアルゴリズムが総当たりなので、2重に描画を行っているはずです。チュートリアルのエクササイズでも、処理をもっとスマートにできるとありましたので、この点を改善していきます。
公式のチュートリアルページはこちらになります。
こんな人の役に立つかも
・JUCEプログラミングを勉強している人
・JUCEチュートリアル「The Point, Line, and Rectangle classes」をやっている人
・JUCEの直線描画に関する詳細を知りたい人
問題点
前回のプログラムで問題だった部分は、すでに描画した交差座標をもう一度交差判定して描画しようとする点です。
例えば、1番目の直線は2~10番目のラインとの交差判定を行ったあと、2番目の直線の判定の際、1番目と3番目~10番目の直線と交差判定を行います。この時、1番目との判定はすでに不要なのですが、現状では総当たりのアルゴリズムなので、無駄な処理が発生していることになります。
改良点の実装
改良の方向性として、内側のループの初期値を自分(外側のループで指定された直線)の次の番号から始めれば重複なく交差点の判定処理ができます。
考えをシンプルにするために、numLinesを「3」としたときの処理イメージを書いてみると次のようになります。ループをjループ変数の外側のfor、kループ変数の内側のforとして実装したいと思います。
「j」が外側のループ変数、「k」が内側のループ変数として、「k」を「j+1」と表現すると、kは常にj番目の直線(比較中の直線)の次の直線を示すようになります。
また、このようにすることで、以前は比較する直線同士が同じ場合、という条件も不要となることがわかります。(j+1とするので、赤い×の部分は勝手に飛ばされます。)そして、最後のループj=2のときは、内側のループ変数kは「2+1」となるので、「k==numLines」となり、「k<numlines」の条件に一致しないので、内側のループは実行されません。
ということで、paint関数を次のように変更しました。
void MainComponent::paint (juce::Graphics& g)
{
//...略...
//for (auto lineI : lines)
for (auto j = 0; j < numLines; ++j)//[1]変更しました。
{
//for (auto lineJ : lines)
for (auto k = j+1; k < numLines; ++k)//[2]変更しました。
{
//if (lines.indexOf(lineI) != lines.indexOf(lineJ))//[3]不要となります。
//{
juce::Point<float> intersection;
//if (lineI.intersects(lineJ, intersection))
if(lines.getReference(j).intersects(lines.getReference(k), intersection))//[4]変更しました。
{
pointArea.setCentre(intersection);
g.fillEllipse(pointArea);
}
//}
}
}
}
[1]では、jという変数でループ回数をカウントしたかったので、範囲for文から、通常のforループへ変更しました。jをネストした内側のforループでも使用したかったからです。
[2]では、kでforループを回すのですが、初期値をj+1とすることで、jに指定した直線番号の次の要素からループが始まるようになります。そのため[3]の同じ直線かどうかの判定が不要となりました。
[4]では今まで範囲forでlineオブジェクト自体を利用できましたが、今回はjとkというループ変数でループしているので、lineクラスの関数、getReference関数で、要素番号を指定してlineオブジェクトを取り出しています。
JUCEというより、一般的なアルゴリズムの改善ですね。
できるだけ無駄な処理ははぶいていきたいよね~