描画処理、結構複雑なことしていました。
一つづつ処理をおっていかないと今後作れないからね〜
「Interface Design」の「Customise the look and feel of your app」チュートリアル、「Custom look-and-feel」の項目のプログラムを前回の記事で実装しましたので、プログラムの処理内容を見ていきます。
公式のチュートリアルページはこちらになります。
プログラムを実装した前回の記事もご参照ください。
こんな人の役に立つかも
・JUCEプログラミングを勉強している人
・JUCEチュートリアル「Customise the look and feel of your app」をやっている人
・lookAndFeelクラスをカスタマイズしたい人
drawRotarySlider関数
関数の全体として、次のような処理の流れとなっています。
[1]:描画のためのパラメータの計算
[2]:ダイヤルのベース部分描画
[3]:ダイヤルの枠線の描画
[4]:ダイヤルのポインタ部分の描画
複雑に見えるのは、[1]と[4]の部分ですね〜。
以下のプログラムは前回実装したものの引用です。
class OtherLookAndFeel : public juce::LookAndFeel_V4
{
public:
OtherLookAndFeel()
{
setColour(juce::Slider::thumbColourId, juce::Colours::red);
}
//以下の関数を追加しました。
void drawRotarySlider(juce::Graphics& g, int x, int y, int width, int height, float sliderPos,
const float rotaryStartAngle, const float rotaryEndAngle, juce::Slider&) override
{
//[1]描画位置のためのパラメータを計算します。
auto radius = (float)juce::jmin(width / 2, height / 2) - 4.0f;
auto centreX = (float)x + (float)width * 0.5f;
auto centreY = (float)y + (float)height * 0.5f;
auto rx = centreX - radius;
auto ry = centreY - radius;
auto rw = radius * 2.0f;
auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
//[2]ダイヤルのベース部分を塗りつぶします。
g.setColour(juce::Colours::orange);
g.fillEllipse(rx, ry, rw, rw);
//[3]ダイヤルの枠線を描画します。
g.setColour(juce::Colours::red);
g.drawEllipse(rx, ry, rw, rw, 1.0f);
//[4]ポインタ部分(ダイヤルの現在値)の描画です。
juce::Path p;
auto pointerLength = radius * 0.33f;
auto pointerThickness = 2.0f;
p.addRectangle(-pointerThickness * 0.5f, -radius, pointerThickness, pointerLength);
p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY));
g.setColour(juce::Colours::yellow);
g.fillPath(p);
}
};
[1]描画のためのパラメータの計算
radiusは、ダイヤルの円の半径を計算します。jmin関数で、ダイヤルの配置領域が長方形になっている場合、短い方の辺を取得して、-4した長さを半径としています。そのため、コンポーネントの配置領域が長方形でも、常にダイヤルは正円となります。
auto radius = (float)juce::jmin(width / 2, height / 2) - 4.0f;
centreXとcentreYは、次の計算で、ダイヤルの円の中心の座標を計算します。
auto centreX = (float)x + (float)width * 0.5f;
auto centreY = (float)y + (float)height * 0.5f;
rx、ryには、ダイヤルのコンポーネント配置座標を算出します。これは、コンポーネント配置領域が長方形の時(や、半径に-4しているため)は、正円のダイヤルの配置原点がコンポーネントの配置原点x、yとはずれるため、このように計算しています。rwは、円の直径を算出しています。
auto rx = centreX - radius;
auto ry = centreY - radius;
auto rw = radius * 2.0f;
次の図のようなイメージで、配置の矩形領域の中央にコンポーネントを配置するため、rx,ryを求めます。
最後の算出パラメータはangleで、スライダーの現在値の角度です。360°のうちの可動域を(rotaryEndAngle-rotaryStartAngle)で計算し、sliderPosは0~1のスライダーの相対的な位置を表しているので、かけることでスライダーの現在値を角度に変換します。最後に、rotaryStartAngleを足すことで実際の見た目の角度に合わせます。
auto angle = rotaryStartAngle + sliderPos * (rotaryEndAngle - rotaryStartAngle);
[2]ダイヤルのベース部分描画
setColor関数でオレンジ色を設定し、fillEllipse関数で塗りつぶした円を描きます。ここでrx、ry、rwのパラメータを使用します。
[3]ダイヤルの枠線の描画
setColor関数で赤色を設定し、drawEllipse関数で1pxの枠のみの円を描画しています。
[4]:ダイヤルのポインタ部分の描画
最後に、Pathクラスでのダイヤルの現在値を指し示す線を描画します。(線というよりは細い矩形です。)
pointerLengthは、描画する線の長さです。半径の0.33倍の長さで作ります。pointerThicknessは、線の太さです。2としています。
次に、Pathの作成と回転を行います。
addRectangle関数で、矩形のpathをpに追加します。addRectangleに与える座標などを理解するには、先に次の行の回転部分も一緒に理解する必要があります。
p.addRectangle(-pointerThickness * 0.5f, -radius, pointerThickness, pointerLength);
p.applyTransform(juce::AffineTransform::rotation(angle).translated(centreX, centreY));
applyTransform関数は、パスに変形などを適用する関数です。引数として、AffineTransformクラスをとります。AffinTransformクラスのrotation関数を利用することで、回転を行います。この回転は、[1]で算出したangleです。また続けてtranslated関数で原点位置の移動を行っています。ということで、パスに描画する矩形としては、原点をマイナス方向にとった長方形を設定しておくことになります。文章だとわかりにくいので、図にしたいと思います。
ダイヤルのメモリのところが意外と複雑ですね~