今、私は、ベジェツールを作っています。
どんなものかというと、
・マウスでクリックした箇所全てをなめらかな曲線が通るようにする。
というものです。
以下が、理想となるフローチャートの図になります。
1.点Aをクリックして点Bをクリック
↓httpを半角に直して下さい
http://s2.gazo.cc/up/24700.png
2.点Cをクリック
http://s2.gazo.cc/up/24701.png
3.1.点Aをクリック
http://s2.gazo.cc/up/24702.png
以下、点D,点Eとつづく・・・
図を見ると、3番目の画像では、曲線BCAを描画していますが、曲線ABは特に変化していません。
つまり、2番目の画像の曲線ABと3番目の画像の曲線ABは等しいと考えます。
よって、2番目の画像で描いた曲線ABCのうち、BC間の座標を制御点として3番目の画像の曲線BCAに使用していると考えました。
しかし、3番目の図でいう曲線BCAを描く時の制御点の取り方でつまづいてしまい、今回質問させて頂きました。
以下がソースです。ソース内のベジェ曲線の制御点である、Pnt.x[2]とPnt.y[2]の値の求め方が分かりません
(”// *質問しているのはこの箇所です*”と記載してある場所です)
#include <DxLib.h>
#include <math.h>
#define CONTROL_PNT_MAX 5// 基準点のマックス(線分を結ぶのに点が2つ必要なため、『2』が最小、『20』が最大)
#define STROKE_PNT_MAX 255
typedef struct
{
float x[CONTROL_PNT_MAX],y[CONTROL_PNT_MAX];
int max;
}pnt_t;
typedef struct
{
int x[STROKE_PNT_MAX],y[STROKE_PNT_MAX];
}stroke_t;
const static float nPasTgl[20][20]={
{1},
{1,1},
{1,2,1},
{1,3,3,1},
{1,4,6,4,1},
{1,5,10,10,5,1},
{1,6,15,20,15,6,1},
{1,7,21,35,35,21,7,1},
{1,8,28,56,70,56,28,8,1},
{1,9,36,84,126,126,84,36,9,1},
{1,10,45,120,210,252,210,120,45,10,1},
{1,11,55,165,330,462,462,330,165,55,11,1},
{1,12,66,220,495,792,924,792,495,220,66,12,1},
{1,13,78,286,715,1287,1716,1716,1287,715,286,78,13,1},
{1,14,91,364,1001,2002,3003,3432,3003,2002,1001,364,91,14,1},
{1,15,105,455,1365,3003,5005,6435,6435,5005,3003,1365,455,105,15,1},
{1,16,120,560,1820,4368,8008,11440,12870,11440,8008,4368,1820,560,120,16,1},
{1,17,136,680,2380,6188,12376,19448,24310,24310,19448,12376,6188,2380,680,136,17,1},
{1,18,153,816,3060,8568,18564,31824,43758,48620,43758,31824,18564,8568,3060,816,153,18,1},
{1,19,171,969,3876,11628,27132,50388,75582,92378,92378,75582,50388,27132,11628,3876,969,171,19,1}
};
float NowBezPos( float t,float x[CONTROL_PNT_MAX], int max ){
float b = t > 1 ? 1 : (t < 0 ? 0 : t);
float a = 1 - b;
float ay=0;
int n=max-1;
for( int i=0; i<=n; i++ ){
ay += nPasTgl[n][i]*pow(a,n-i)*pow(b,i)*x[i];//※1
}
return ay;
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
int mouseX,mouseY,MouseInput;
pnt_t Pnt;
stroke_t Stroke;
int no=0;
char L='a',R='a';
ChangeWindowMode( TRUE ) ;
if( DxLib_Init() == -1 ) // DXライブラリ初期化処理
{
return -1; // エラーが起きたら直ちに終了
}
SetMouseDispFlag( TRUE );
for (int i=0;i<CONTROL_PNT_MAX;i++){Pnt.x[i]=0;Pnt.y[i]=0;}
for(int i=0;i<STROKE_PNT_MAX;i++){Stroke.x[i]=0;Stroke.y[i]=0;}
while( ProcessMessage() == 0 )
{
GetMousePoint( &mouseX, &mouseY );
MouseInput=GetMouseInput();
if(L=='a'&&( MouseInput & MOUSE_INPUT_LEFT ) != 0)L='b';
else if(L=='b'&&( MouseInput & MOUSE_INPUT_LEFT ) != 0)L='c';
else if(L=='b'&&( MouseInput & MOUSE_INPUT_LEFT ) == 0)L='d';
else if(L=='c'&&( MouseInput & MOUSE_INPUT_LEFT ) == 0)L='d';
else if(L=='d')L='a';
if(R=='a'&&( MouseInput & MOUSE_INPUT_RIGHT ) != 0)R='b';
else if(R=='b'&&( MouseInput & MOUSE_INPUT_RIGHT ) != 0)R='c';
else if(R=='b'&&( MouseInput & MOUSE_INPUT_RIGHT ) == 0)R='d';
else if(R=='c'&&( MouseInput & MOUSE_INPUT_RIGHT ) == 0)R='d';
else if(R=='d')R='a';
ClsDrawScreen();
// 分かりやすいように円を描画
DrawCircle(100,100,50,GetColor(100,100,100));
DrawCircle(200,100,50,GetColor(100,100,100));
if(R=='b')
{
no--;
if(no<0)no=0;
}
Stroke.x[no]=mouseX;Stroke.y[no]=mouseY;
if(L=='b')
{
if(no<STROKE_PNT_MAX-1)
{
no++;
Stroke.x[no]=mouseX;Stroke.y[no]=mouseY;
}
else printfDx("制御点がマックスに達しました %d\n",no);
}
if(no<1){}
else if(no==1)DrawLine(Stroke.x[0],Stroke.y[0],mouseX,mouseY,GetColor(255,255,255));
else// ベジェ曲線描画
{
float px,py;
Pnt.x[0]=(float)Stroke.x[0];Pnt.y[0]=(float)Stroke.y[0];
for(int h=0;h<no-1;h++)
{
Pnt.x[2]=(float)Stroke.x[h+2];Pnt.y[2]=(float)Stroke.y[h+2];
Pnt.x[1]=-((Pnt.x[2]-Pnt.x[0])/2.0f+Pnt.x[0])+(float)Stroke.x[h+1]*2.0f;
Pnt.y[1]=-((Pnt.y[2]-Pnt.y[0])/2.0f+Pnt.y[0])+(float)Stroke.y[h+1]*2.0f;
{
float x[3],y[3];
float keisuu=-1.3333f;// 制御点が5個ある場合、3点通らせたいなら、-1.33333となる(謎)
x[0]=NowBezPos( 0.75f,Pnt.x,3 );y[0]=NowBezPos( 0.75f,Pnt.y,3 );
x[1]=NowBezPos( 0.25f,Pnt.x,3 );y[1]=NowBezPos( 0.25f,Pnt.y,3 );
x[2]=NowBezPos( 0.5f,Pnt.x,3 );y[2]=NowBezPos( 0.5f,Pnt.y,3 );
Pnt.x[0]=(float)Stroke.x[h];Pnt.y[0]=(float)Stroke.y[h];
Pnt.x[4]=(float)Stroke.x[h+2];Pnt.y[4]=(float)Stroke.y[h+2];
Pnt.x[2]=((Pnt.x[0]-Pnt.x[4])/2.0f+Pnt.x[4]-x[2])*keisuu+(Pnt.x[0]-Pnt.x[4])/2.0f+Pnt.x[4];
Pnt.y[2]=((Pnt.y[0]-Pnt.y[4])/2.0f+Pnt.y[4]-y[2])*keisuu+(Pnt.y[0]-Pnt.y[4])/2.0f+Pnt.y[4];
Pnt.x[3]=(((Pnt.x[0]-Pnt.x[4])/2.0f+Pnt.x[4])-x[0])*keisuu+((Pnt.x[0]-Pnt.x[4])/2.0f+Pnt.x[4]);
Pnt.y[3]=(((Pnt.y[0]-Pnt.y[4])/2.0f+Pnt.y[4])-y[0])*keisuu+((Pnt.y[0]-Pnt.y[4])/2.0f+Pnt.y[4]);
if(h==0)
{
Pnt.x[1]=(((Pnt.x[0]-Pnt.x[4])/2.0f+Pnt.x[4])-x[1])*keisuu+((Pnt.x[0]-Pnt.x[4])/2.0f+Pnt.x[4]);
Pnt.y[1]=(((Pnt.y[0]-Pnt.y[4])/2.0f+Pnt.y[4])-y[1])*keisuu+((Pnt.y[0]-Pnt.y[4])/2.0f+Pnt.y[4]);
}
else
{
Pnt.x[1]=px;
Pnt.y[1]=py;
// *質問しているのはこの箇所です*
// Stroke.x[h+1]とStroke.y[h+1](クリックした座標)を通る曲線を描きたいのですが、
// そのためのPnt.x[2]とPnt.y[2]の値をどうすれば求められるのかが分かりません
// ↓2行は、失敗です
//Pnt.x[2]=((Pnt.x[0]-Pnt.x[4])/2.0f+Pnt.x[4]-x[2])*keisuu+(Pnt.x[0]-Pnt.x[4])/2.0f+Pnt.x[4];
//Pnt.y[2]=((Pnt.y[0]-Pnt.y[4])/2.0f+Pnt.y[4]-y[2])*keisuu+(Pnt.y[0]-Pnt.y[4])/2.0f+Pnt.y[4];
}
for(int i=0;i<99;i++)// iが0に近いほど根本(白)。99に近づくほど、先っちょ(赤)
{
float t=(float)i/100.0f;
x[0]=NowBezPos( t,Pnt.x,5 );x[1]=NowBezPos( t+0.01f,Pnt.x,5 );
y[0]=NowBezPos( t,Pnt.y,5 );y[1]=NowBezPos( t+0.01f,Pnt.y,5 );
DrawLine(x[0],y[0],x[1],y[1],GetColor(255,255-i*2,255-i*2));
//DrawLine(x[0],y[0],x[1],y[1],GetColor(100,100,255));
// デバッグ用ガイドライン(赤=0、緑=1、青=2、白=3)
DrawLine((int)Pnt.x[0],(int)Pnt.y[0],(int)Pnt.x[1],(int)Pnt.y[1],GetColor(200,100,100));
DrawLine((int)Pnt.x[2],(int)Pnt.y[2],(int)Pnt.x[1],(int)Pnt.y[1],GetColor(100,200,100));
DrawLine((int)Pnt.x[2],(int)Pnt.y[2],(int)Pnt.x[3],(int)Pnt.y[3],GetColor(100,100,200));
DrawLine((int)Pnt.x[4],(int)Pnt.y[4],(int)Pnt.x[3],(int)Pnt.y[3],GetColor(255,255,255));
if(i==98)// 曲線の終端
{
// なめらかな曲線にする
px=Pnt.x[3];py=Pnt.y[3];
// 次のループに備え、初期化する
Pnt.x[0]=(float)Stroke.x[h+2];Pnt.y[0]=(float)Stroke.y[h+2];
// 間が空いてしまうのを防ぐ
DrawLine((int)x[1],(int)y[1],(int)Pnt.x[4],(int)Pnt.y[4],GetColor(100,100,255));
}
}
}
}
}
for(int i=0;i<no;i++)//デバッグ用。クリックした所に円を描いておく
{
DrawCircle(Stroke.x[i],Stroke.y[i],3,GetColor(255,100,100));
}
ScreenFlip();
}
DxLib_End() ; // DXライブラリ使用の終了処理
return 0 ; // ソフトの終了
}