楕円

ellipse.png

楕円の方程式

(1)
\begin{align} \frac{x^2}{a^2}+\frac{y^2}{b^2}=1 \end{align}

楕円の焦点

(2)
\begin{align} F(\sqrt{a^2-b^2},0),F'(-\sqrt{a^2-b^2},0) \end{align}

媒介変数表現

(3)
\begin{align} x=a\cos t\\ y=b\sin t \end{align}

斜めの楕円

ellipse-tilt.png
θ度回転した楕円の方程式は以下のようになっている(4)
\begin{align} \frac{(x \cos\theta + y \sin\theta)^2}{a^2}+\frac{(-x \sin\theta + y \cos\theta)^2}{b^2}=1 \end{align}

この斜めの楕円を媒介変数で表現するにはどうしたらよいか?
斜めじゃない時の媒介変数表現

(5)
\begin{align} x=a\cos t, y=b\sin t \end{align}

の結果に2次元の回転行列

(6)
\begin{align} \left(\begin{matrix} cos\theta&-sin\theta\\ sin\theta&cos\theta\\ \end{matrix}\right) \end{align}

を掛け算すればよい。
つまり

(7)
\begin{align} \left(\begin{matrix} cos\theta&-sin\theta\\ sin\theta&cos\theta\\ \end{matrix}\right) \left(\begin{matrix} a\cos t\\ b\sin t\\ \end{matrix}\right) = \left(\begin{matrix} a\cos t \cos \theta - b \sin t \sin \theta\\ a\cos t \sin \theta + b \sin t \cos \theta\\ \end{matrix}\right) \end{align}

つまり

(8)
\begin{align} x=a\cos t \cos \theta - b \sin t \sin \theta\\ y=a\cos t \sin \theta + b \sin t \cos \theta \end{align}

となります。これでOpenGLで描画できそうですね

楕円を表現するには3つのパラメータだけでok

a x方向の半径
b y方向の半径
$\theta$ 楕円の回転角

楕円は2x2行列で表現することが出来る。

(9)
\begin{align} A = \left( \begin{array}{ccc} cos\theta&-sin\theta\\ sin\theta&cos\theta\\ \end{array} \right) \left( \begin{array}{ccc} a&0\\ 0&b\\ \end{array} \right) \end{align}

左側は2次元平面用の一般的な回転行列
右側は楕円の半径を現す行列です
掛け算した結果は以下

(10)
\begin{align} \left( \begin{array}{ccc} cos\theta a&-sin\theta b\\ sin\theta a&cos\theta b\\ \end{array} \right) \end{align}
各行列成分が何を表しているかを図示するとこうなります
2x2ellipse.png

2x2行列で表現された楕円を描画するには?

斜めの楕円を描画するには

(11)
\begin{align} x=a\cos t \cos \theta - b \sin t \sin \theta\\ y=a\cos t \sin \theta + b \sin t \cos \theta \end{align}

でした。2x2楕円の式

(12)
\begin{align} \left( \begin{array}{ccc} cos\theta a&-sin\theta b\\ sin\theta a&cos\theta b\\ \end{array} \right) = \left( \begin{array}{ccc} a00 & a01\\ a10 & a11\\ \end{array} \right) \end{align}

と置き換えると

(13)
\begin{align} x=a00\cos t + a01 \sin t \\ y=a10\cos t + a11 \sin t \end{align}

となります
しかし、tにそのまま角度を当てはめた場合、描画結果はその通りの角度にはなりません!
楕円は縦と横で半径の長さが違うからです。
円の場合

(14)
\begin{align} \frac{y}{x}= \frac{r\sin\theta}{r\cos\theta}=\tan \theta \end{align}

であるが、楕円の場合

(15)
\begin{align} \frac{y}{x}= \frac{b\sin\theta}{a\cos\theta}=\frac{b}{a}\tan \theta \end{align}

になります。

2x2行列で表現された楕円の角度と半径を求めるには?

(16)
\begin{align} \left( \begin{array}{ccc} cos\theta a&-sin\theta b\\ sin\theta a&cos\theta b\\ \end{array} \right) = \left( \begin{array}{ccc} a00 & a01\\ a10 & a11\\ \end{array} \right) \end{align}

なので

(17)
\begin{align} \frac{a10}{a00}=\frac{\sin \theta a}{\sin \theta a}=\frac{\sin \theta}{\cos \theta}=\tan \theta \end{align}

ここからatan関数を使って角度がわかります。
あとは芋づる式に行列式から半径のa,bがわかります

楕円の円周上の点をレンダリングするシェーダ

circum.png
#version 400
uniform mat4 uModelViewProj;
in vec3 aCenter;//楕円の中心点
uniform mat2 uEllipse;//楕円の2x2行列
uniform float uTheta;//表示したい円周上の点の位置
uniform float uSizeAdjust;
void main () {
    vec2 circum = uEllipse*vec2(cos(uTheta), sin(uTheta));
    circum *= uSizeAdjust;
    vec3 pos = aCenter + vec3(circum,0.0);
    gl_Position = uModelViewProj * vec4(pos, 1.0);
}

楕円は媒介変数で与えたとおりの角度にならない!?

楕円は縦横で半径の長さが違うので、媒介変数

(18)
\begin{align} x=a\cos t\\ y=b\sin t \end{align}

で描画したら、結果の点が偏角tに現れるとは限りません。現れるのは90度などの切りの良い角度の時だけ。
もし$\theta$上の楕円の点になるような媒介変数tをもとめたいならこうします

(19)
\begin{align} arctan(\frac{a}{b} tan{t}) \end{align}

arctanは$< |\frac{\pi}{2}|$までの値しか返さないので注意
ソースコード

//描画結果が偏角_thetaになるような媒介変数を返す
float CEllipse::getParameter(float _theta)
{
    float a = getRadiusA();
    float b = getRadiusB();
    if (abs(_theta) < M_PI / 2.0 || _theta>M_PI*3.0/2.0) {
        return atan(abs(a / b)*tan(_theta));
    }
    else {
        return atan(abs(a / b)*tan(_theta))+M_PI;
    }
}
Bibliography
1. Drawing Oblique Ellpses…斜めの楕円を描画する方法

サポートサイト Wikidot.com