一応動作確認をした、自家製版コードを以下に示す。
cairo_arc_to()はすでに存在しているようなので、自分で作ったものはpseudo_arc_to()とした。
実際に、円弧ではなくベジエ曲線なので、擬似円弧だ。
#include <gtk/gtk.h> #include <glib.h> #include <math.h> /*----------------------------------------------*/ /* Get difference of angle between angles. */ /* Arguments: */ /* st: start angle. -π 〜 +π */ /* ed: end angle. -π 〜 +π */ /* dir: direction. */ /* 0: Normal direction. */ /* 1: Reverse direction. */ /* Return value: */ /* Difference of angle. */ /* if dir is zero, result will be positive */ /* value, otherwise, result will be negative. */ /*----------------------------------------------*/ static double get_angle_difference( double st, double ed, int dir ) { double angle = ed -st; if( dir == 0 ) { if( angle < 0.0 ) { angle += (M_PI *2.0); } } else { if( 0.0 < angle ) { angle -= (M_PI *2.0); } } return( angle ); } /*--------------------------------------------------------------*/ /* pseudo arc to */ /* Arguments: */ /* cr: a cairo context */ /* edX: the X coordinate of the end of the new line */ /* edY: the Y coordinate of the end of the new line */ /* ox: X position of the center of the arc */ /* oy: Y position of the center of the arc */ /* dir: direction of drawing arc */ /* 0: Normal direction */ /* 1: Reverse direction */ /* */ /*--------------------------------------------------------------*/ void pseudo_arc_to( cairo_t *cr, double edX, double edY, // End point double oX, double oY, // Origin of arc int dirFlag ) // Direction of rotation // 0: Normal direction // 1: Counter direction { double stX, stY; // Start point. double st, ed; // Angle of start point and end point. double r; // Radius of arc double angle; // Angle from st to ed. int n, i; double l; // Distance to Ctrl point for each divided arc. double prvX, prvY; // Prepare each value. cairo_get_current_point( cr, &stX, &stY ); // Get start point. st = atan2( (stY -oY), (stX -oX) ); // Angle of start point. ed = atan2( (edY -oY), (edX -oX) ); // Angle of end point. r = sqrt( ((edX -oX) *(edX -oX)) +((edY -oY) *(edY -oY)) ); // Radius of arc angle = get_angle_difference( st, ed, dirFlag ); // Angle from st to ed. // Draw divided arc. n = ceil( fabs(angle / (M_PI*0.5)) ); // Number of division. l = fabs( r *4.0 *tan((angle/n)/4.0) /3.0 ); // Distance to Ctrl point. prvX = stX; prvY = stY; for( i=1; i<=n; i++ ) { double c1X, c1Y; // Ctrl1 point. double c2X, c2Y; // Ctrl2 point. double a = st +(angle *i /n); double nowX = r *cos(a) +oX; double nowY = r *sin(a) +oY; if( i == n ) { // Last divided arc. // Only last part of arcs will be exactly drawn // with given end point. nowX = edX; nowY = edY; } if( dirFlag == 0 ) { c1X = prvX + (l * (-(prvY -oY)/r)); // cos(θ+π/2) = -sin(θ) c1Y = prvY + (l * ( (prvX -oX)/r)); // sin(θ+π/2) = cos(θ) c2X = nowX + (l * ( (nowY -oY)/r)); // cos(θ-π/2) = sin(θ) c2Y = nowY + (l * (-(nowX -oX)/r)); // sin(θ-π/2) = -cos(θ) } else { c1X = prvX + (l * ( (prvY -oY)/r)); // cos(θ-π/2) = sin(θ) c1Y = prvY + (l * (-(prvX -oX)/r)); // sin(θ-π/2) = -cos(θ) c2X = nowX + (l * (-(nowY -oY)/r)); // cos(θ+π/2) = -sin(θ) c2Y = nowY + (l * ( (nowX -oX)/r)); // sin(θ+π/2) = cos(θ) } cairo_curve_to( cr, c1X, c1Y, c2X, c2Y, nowX, nowY ); prvX = nowX; prvY = nowY; } // 半径は、原点と終点の距離から計算される。 // 回転方向は解りにくい。 // 数学的には左回り(CCW:時計と逆)が順方向であるが、コンピュータの座標系は // Y軸が反転しているため、右回り(CW:時計方向)が順方向になる。 // 回転方向は、表示装置の座標系のとり方に依存するもので、計算や処理に // 依存するものではない。 }コメントにも書いてあるが、回転方向には注意が必要だ。
多くの処理系、表示デバイスでは、下方向がY軸の増加になり、時計回りが順方向になる。
この向きは、cairo_arc()の描画方向に一致している。
実装はしたものの、迷っている部分がある。
始点と終点が一致している場合、全く書かないのが良いのか?全周を描くのが良いのか?
これについては、描画方向も考慮するのが良いかもしれない。
現在は、一番シンプルな形で実装している。
0 件のコメント:
コメントを投稿