ベジェ曲線の制御点

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Tangeθ

ベジェ曲線の制御点

#1

投稿記事 by Tangeθ » 11年前

こんばんは、いつもお世話になっております。
今、私は、ベジェツールを作っています。

どんなものかというと、

・マウスでクリックした箇所全てをなめらかな曲線が通るようにする。

というものです。

以下が、理想となるフローチャートの図になります。

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 ;					// ソフトの終了
}


Tangeθ

Re: ベジェ曲線の制御点

#2

投稿記事 by Tangeθ » 11年前

すいません、文章が読みにくかったので、質問を簡略化します。

・5つの制御点を使ったベジェ曲線で、そのうち、4つが分かっています。残りの1つを求めることによって点Pを曲線が通るようにしたいのですが、どうすれば残りの1つが求まるでしょうか?

ということです。何かヒントでももらえれば幸いです。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: ベジェ曲線の制御点

#3

投稿記事 by みけCAT » 11年前

Tangeθ さんが書きました:5つの制御点を使ったベジェ曲線で、そのうち、4つが分かっています。残りの1つを求めることによって点Pを曲線が通るようにしたいのですが、どうすれば残りの1つが求まるでしょうか?
・「残りの1つ」をベジェ曲線の端点にし、かつそれを点Pに設定すれば、(「通る」の定義にもよるが)条件を満たすと思います。
・端点でない制御点を求めたいのであれば、もしかしたら二分探索が使えるかもしれません。
添付ファイル
bezyekyokusen_ann.png
適当な案
bezyekyokusen_ann.png (4.95 KiB) 閲覧数: 3726 回
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: ベジェ曲線の制御点

#4

投稿記事 by usao » 11年前

質問文の意味がいまいち読み取れませんが,

>マウスでクリックした箇所全てをなめらかな曲線が通るようにする。

「制御点の上を通る性質を持つ曲線」を使うのではダメなのでしょうか?
ベジェ曲線は「制御点のうち端点でないものについては通らない(まぁ通るパターンもあるだろうけど)」ので
じゃあ目的の場所を曲線が通るようにするには制御点をどこに置けばいい? という問題が出てきているわけで
話が難しくなっていますよね.

[追記]
>ベジェツール
まぁ 目的がそもそもコレだ,ということであればベジェであることは譲れないのでしょうけど.

Tangeθ

Re: ベジェ曲線の制御点

#5

投稿記事 by Tangeθ » 11年前

みけCAT様、usao様、返信ありがとうございます。

>みけCAT様
>「残りの1つ」をベジェ曲線の端点にし、かつそれを点Pに設定すれば、(「通る」の定義にもよるが)条件を満たすと思います。
その方法は非常に良いアプローチと感じました。3点通らないといけない曲線から、2点ずつ通すようにしてむらが出ないかどうか試してみることにします。

>usao様
>「制御点の上を通る性質を持つ曲線」を使うのではダメなのでしょうか?
そんなことはありません。しかし、ペイントソフトの曲線を描くのに、ベジェ曲線が必要だと思っていた私にはそれは盲点でした。
Wikipediaで早速、曲線のページを見てみた所、スプライン曲線という曲線が複数の制御点を通るということで、役に立つと感じましたので、これも試してみることにします。

お二方、どうもありがとうございました。早速やってみます。

Tangeθ

Re: ベジェ曲線の制御点

#6

投稿記事 by Tangeθ » 11年前

報告します。
結論からいうと、出来そうではあるけれども、機能の拡張性を見越した結果処理が複雑になりそうなので、曲線と曲線同士の補完は行わないことにしました。
つまり、残念ながら、一番最初のプログラムとほぼ同じで実装します。

次に、これまでやった所を記載します。

☆みけCAT様の案
>「残りの1つ」をベジェ曲線の端点・・・
あくまで、私のプログラム上での話ですが、曲線と曲線の補完をやってしまうと、
(↓httpを半角に直して下さい)
http://s2.gazo.cc/up/24701.png
から
http://s2.gazo.cc/up/24702.png
に移る時に、元の曲線の形が変わってしまいました。点Bと点Cが同じ座標なら曲線の形が変わらないということが理想なので、この方法はひとまず保留です。
(ただし、1つのベジェ曲線を2つに分けて同じ軌跡で描く方法があれば、この案は即採用です)

☆usao様の案
>「制御点の上を通る性質を持つ曲線」・・・
スプライン曲線を使ってみました。が、結構よれる曲線なので、難しい。しかし、これからヒントを得ました。

☆改善案
ベジェ曲線とスプライン曲線のハイブリッド。

つまり、マウスを座標A,B,C,Aとクリックしたとすると、まず、ベジェで曲線ABCをABまで描画する。
次に曲線BCAを描画する時は、スプライン曲線で描画します。
ただし、最初に計算した曲線ABCのt=0.51~0.55くらいの値もスプライン曲線で描画する制御点に加えて描画します(この制御点をX1~Xαとすると、曲線B X1~Xα CAとなります)。これである程度最初の曲線と同じ軌跡を描くはずです。
あとは、同じようにスプライン曲線で描画する、ということです。

ただし、デメリットもあります。
(これに気づいたことと、私のリソース不足により、改善案のスプライン描画はテストしていません。申し訳ありません。)

☆デメリット
・ベジェ曲線を分断する拡張機能を付ける時などに、この処理を行うと処理が煩雑化してやってられない。(ベジェだけなら制御点が4つくらいなので、切断処理も楽)

という訳で、処理の煩雑化という点から今回の曲線同士の補完は行わないことにしました。
色々、説明不足であった点、お詫びします。
みけCAT様、usao様、そして皆様、どうもありがとうございました。

最後に、ベジェ曲線を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[5],y[5];
				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 );
				x[4]=NowBezPos( 0.865f,Pnt.x,3 );y[4]=NowBezPos( 0.865f,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];
				

				x[3]=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]);
				y[3]=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[3]=(((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[3]=(((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]);
					Pnt.x[1]=px;
					Pnt.y[1]=py;
					
					Pnt.x[4]=(float)Stroke.x[h+1];
					Pnt.y[4]=(float)Stroke.y[h+1];
					Pnt.x[2]=(Pnt.x[0]-Pnt.x[4])/-2.0f+Pnt.x[0];
					Pnt.y[2]=(Pnt.y[0]-Pnt.y[4])/-2.0f+Pnt.y[0];
					Pnt.x[3]=x[1];
					Pnt.y[3]=y[1];
					

					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 );
						if(h!=0||h==0&&i<49||h==0&&no==2)
						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));

						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));
						}
					}
					Pnt.x[0]=Pnt.x[4];Pnt.y[0]=Pnt.y[4];
					Pnt.x[4]=(float)Stroke.x[h+2];Pnt.y[4]=(float)Stroke.y[h+2];
					
					Pnt.x[1]=x[3];Pnt.y[1]=y[3];
					Pnt.x[3]=x[4];Pnt.y[3]=y[4];
					Pnt.x[2]=(Pnt.x[1]-Pnt.x[4])/-2.0f+Pnt.x[1];
					Pnt.y[2]=(Pnt.y[1]-Pnt.y[4])/-2.0f+Pnt.y[1];
					Pnt.x[3]=(Pnt.x[2]-Pnt.x[4])/-2.0f+Pnt.x[2];
					Pnt.y[3]=(Pnt.y[2]-Pnt.y[4])/-2.0f+Pnt.y[2];

				

				}
				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 );
					if(h!=0||h==0&&i<49||h==0&&no==2)
					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)
					if(h<0)
					{
					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)// 曲線の終端
					{
					
						// なめらかな曲線にする
						if(h==0)
						{
						px=Pnt.x[3];py=Pnt.y[3];
						}else
						{
						px=Pnt.x[1];py=Pnt.y[1];
						}
						// 次のループに備え、初期化する
						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 ;					// ソフトの終了
}


アバター
usao
記事: 1889
登録日時: 12年前
連絡を取る:

Re: ベジェ曲線の制御点

#7

投稿記事 by usao » 11年前

オフトピック
よくわかりませんが
制御点を後から挿入する等して形を整えるような方法じゃダメなのだろうか?
(PowerPointとかの曲線みたく)

閉鎖

“C言語何でも質問掲示板” へ戻る