一応動作確認をした、自家製版コードを以下に示す。
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 件のコメント:
コメントを投稿