ページ 11

レーダ-チャートの作成と描画について

Posted: 2011年9月29日(木) 05:55
by 緑色のたぬき
はじめまして、プログラミングは初心者です。よろしくお願いします。

育成ゲームの自作に挑戦しているのですが、レーダーチャートの描画について教えてください。
 ・グラフの項目は8つです。(攻撃・防御・スタミナ・素早さ・根性・理性・技術・運)
 ・範囲内は塗りつぶしたい
以上です。
正直、見当もつかず何をどうしてよいのか困っています。

360度をレーダーの項目数で割り、1項目あたりの角度を求め、
この角度と中心からの距離を元に、三角関数を使って座標を出す
・・・との書き込みを他の掲示板で見かけたのですが、内容に理解が及ばず、
分からない事(中心からの距離はどうやって求める?)が増えた次第です。

あつかましい質問ではありますが、どうぞよろしくお願いします。

Re: レーダ-チャートの作成と描画について

Posted: 2011年9月29日(木) 07:58
by h2so5
中心からの距離は チャートの半径 × (項目値 ÷ 項目の最大値) で求まりますね。

各方向の単位ベクトルを求めて、中心からの距離を掛ければ項目ごとの点の座標が求められます。

グラフの塗りつぶしについては、塗りつぶされた三角形が描画できれば問題ないでしょう。
各項目の点からチャートの中心に線を引けば、三角形の集合であることが分かります。

Re: レーダ-チャートの作成と描画について

Posted: 2011年9月29日(木) 10:19
by ろん
とりあえず最大値100ピクセルと考えた場合のチャート作成してみました。
作った本人が【double rad = 15 * j* PI / 180.0;】の1文を理解していない為、j= j +3;の所で何故+3するとうまく動くのか分かってないです。
それと開始位置を上方向からにしたかったのですが、右方向からの時計周りになってます。
また、360°を8つの要素で均等角で使うんだからと360/8で15って数字直接使ってます。

コード:

#include "DxLib.h"
#include <math.h>

//プロトタイプ宣言
void background();

//パラメータ要素[8]攻撃・防御・スタミナ・素早さ・根性・理性・技術・運
double sta[8] = { 90, 80, 70, 60, 50, 40, 30, 20};
//基準点座標( x 200, y 200)
//頂点記憶配列
int top[2][8]={};
int Cr;

//円周率
#define PI 3.14159265358979323846

int WINAPI WinMain( HINSTANCE hI, HINSTANCE hP, LPSTR lpC, int nC){
	ChangeWindowMode(TRUE);
	if( DxLib_Init() == -1 ) return -1;	// エラーが起きたら直ちに終了

	SetDrawScreen(DX_SCREEN_BACK);
	while(ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0){		
		ClsDrawScreen();
		background();
		//メイン処理
		int j=0;
		for(int i=0; i < 8; i++){
			double rad = 15 *  j* PI / 180.0;
			double x= cos(rad*1.0)*sta[i];
			double y= sin(rad*1.0)*sta[i];
			j= j +3;
			top[0][i]=200+x;
			top[1][i]=200+y;	
		}
		
		for(int i=0; i < 8 ; i++){
			Cr = GetColor( 0 , 200 , 200  );
			DrawTriangle( 200, 200, (int)(top[0][i%8]), (int)(top[1][i%8]), (int)(top[0][(i+1)%8]), (int)(top[1][(i+1)%8])  ,Cr , TRUE );
			Cr = GetColor( 255 , 0 , 0  );
			DrawLine( 200, 200, (int)(top[0][i%8]), (int)(top[1][i%8]), Cr);
			DrawFormatString( (int)(top[0][i%8]), (int)(top[1][i%8]), Cr, "%+1.lf", sta[i]);
		}
		ScreenFlip();
	}
	DxLib_End() ;				// DXライブラリ使用の終了処理
	return 0 ;				// ソフトの終了 
}
void background(){
	Cr = GetColor( 255 , 255 , 255 );
	DrawBox ( 100,  100 , 300 , 300 , Cr ,TRUE );
	Cr = GetColor( 0 , 0 , 255 );	
	DrawLine( 195, 100, 195, 300, Cr);
	DrawLine( 205, 100, 205, 300, Cr);
	DrawLine( 100, 195, 300, 195, Cr);
	DrawLine( 100, 205, 300, 205, Cr);
}

Re: レーダ-チャートの作成と描画について

Posted: 2011年9月29日(木) 13:15
by 緑色のたぬき
h2so5様、ろん様、ご助言ありがとうございます。
お二人のアドバイスを踏まえて自分なりにソースを書いてみようと思います。
が、実はまだ理解し切れていない点がいくつかあります。
お差し支えなければ、ご教授願います。

まず、h2so5様に質問です。
 座標位置の求め方について
  "単位ベクトルを求めて、中心からの距離を掛け算する"

 と、あるのですが、仮に最大値を100、項目1の値を48とした場合、

  100(48/100)=48 //中心からの距離

  単位ベクトルA=(ax,ay)

  (ax,ay)×48 = 48x,48y //各座標の位置

 ・・・?

 単位ベクトルの求め方をググってみたのですが、
 成分表で座標が分かっているものばかり紹介されていて、
 今回のような、座標を求めるために単位ベクトルを求める方法が
 確認出来ませんでした。

 プログラミングではなく、算数の話になりますので、
 私個人が努力すべき範囲かもしれませんが、よろしければ教えてください。


続いて、ろん様に質問です。
 まず、コードまで書いていただき大変恐縮です。

 さて、コードの中身ですが、h2so5様とは違う手法で描画をされいてると
 認識しているのですが相違ないでしょうか?

 また、記述内にありました―
  "360°を8つの要素で均等角で使うんだからと360/8で15って数字直接使ってます。"
 ―の、15という数字は45の誤りでしょうか、それとも私の勉強不足(ラジアン関数とか、
 sinθ、cosθなどが関係?)でしょうか?

 また、ソースについて、簡単な説明で構いませんので、よろしければ解説をお願いします。
 ソースの中身を参考に自分なりにも考えてみようと思います。

実践しながらプログラミングを勉強している身のため、
拾い読みした情報が頭の中で錯綜している状態です。
質問に不明瞭な点があるかもしれませんが、ご容赦願います。

Re: レーダ-チャートの作成と描画について

Posted: 2011年9月29日(木) 15:00
by ろん
すみません、頭がボケてたようで15は間違いです。360を要素数8で割ったら45°ですね(汗・・・
そこでjをradの式に入れ込んでj=j+3の一文を削除した所、下のように纏まりました。

度数法の360°を弧度法の2πへ変換し8で割っています。これが要素間の角度、すなわち45°になります。
その45°(PI * 2 / 8.0)で8回繰り返せば360°を全て使ったレーダチャートになるわけですね。お騒がせしました。

コード:

double rad =  i * PI * 2 / 8.0;

Re: レーダ-チャートの作成と描画について

Posted: 2011年9月29日(木) 18:45
by h2so5
ろん さんの方法と基本的に変わりはないです。

単位ベクトルは長さが1のベクトルですので、
任意のベクトルの各要素をそのベクトルの長さで割ることで作ることができますが、
今回は

double x = cos(radian);
double y = sin(radian);

で単位ベクトルを求めることができます。

簡単にソースを載せておきます。
分からない所があれば質問してください。

コード:

#include "DxLib.h"
#include <math.h>

#define CHART_SIZE 200			// チャート半径
#define CHART_CENTER_X 320	    // チャート中心座標 X
#define CHART_CENTER_Y 240	    // チャート中心座標 Y

#define MAX_STATUS 100			// ステータス最大値
#define ITEM_NUM 8				// ステータス個数

void buildChart(const int *status, VECTOR *points);
void drawChart(const VECTOR *points);

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
		 LPSTR lpCmdLine, int nCmdShow )
{

	// ウインドウモードに変更
	ChangeWindowMode( TRUE ) ;

	if( DxLib_Init() == -1 )	// DXライブラリ初期化処理
	{
		 return -1;	// エラーが起きたら直ちに終了
	}

	// ステータス一覧を収納する配列
	int status[ITEM_NUM] = {100, 20, 40 ,20 ,60, 30, 80, 40};

	// チャートのポリゴン座標を収納する配列
	VECTOR points[ITEM_NUM];

	// チャートを生成
	buildChart(status, points);

	while ( ProcessMessage() == 0 && ClearDrawScreen() == 0 ) {

		// チャートを描画
		drawChart(points);

	}

	DxLib_End() ;		// DXライブラリ使用の終了処理

	return 0 ;		// ソフトの終了
}

void buildChart(const int *status, VECTOR *points) {

	// 項目ごとの回転角度
	double radian = (360.0f / ITEM_NUM) * (PHI / 180.0f);

	// 開始角度
	double startRadian = -90.0f * (PHI / 180.0f);

	for (int i=0; i < ITEM_NUM; i++) {

		// 中心からの距離を求める
		double length = CHART_SIZE * ((double)status[i] / MAX_STATUS);

		// 単位ベクトルに距離を乗算
		double x = cos(startRadian + radian * i) * length;
		double y = sin(startRadian + radian * i) * length;

		points[i].x = (float)x;
		points[i].y = (float)y;

	}

}

void drawChart(const VECTOR *points) {

	int CrBlue = GetColor(0, 0, 255);
	int CrRed = GetColor(255, 0, 0);

	DrawCircle(CHART_CENTER_X,
			   CHART_CENTER_Y,
			   CHART_SIZE,
			   CrRed, FALSE);

	for (int i=0; i < ITEM_NUM; i++) {
		
		int x1 = (int)points[i].x;
		int y1 = (int)points[i].y;

		int x2 = (int)points[(i+1) % ITEM_NUM].x;
		int y2 = (int)points[(i+1) % ITEM_NUM].y;

		DrawTriangle((int)( x1 + CHART_CENTER_X ),
				     (int)( y1 + CHART_CENTER_Y ),
				     (int)( x2 + CHART_CENTER_X ),
				     (int)( y2 + CHART_CENTER_Y ),
				     (int)CHART_CENTER_X,
				     (int)CHART_CENTER_Y,
				     CrBlue, TRUE);
	}

}

Re: レーダ-チャートの作成と描画について

Posted: 2011年9月29日(木) 22:00
by 緑色のたぬき
h2so5様、ろん様、ご返信ありがとうございます。
ソースコードまで書いていただけるとは、当初、予想だにしていませんでした。
重ねて御礼申し上げます。

おかげさまでレーダーチャートの作成に何が必要であるかが一目瞭然となり、
理解が深まりました。
これから、お二人のソースコードを見て勉強させていただきたく思います。

掲示板が移転作業のため、しばらくアクセスできなくなるようなので、
取り急ぎ、御礼の報告をさせて頂きました。

ほぼ解決はしているのですが、実践して新たな疑問点が出てきましたら、
改めて、内容を絞り込んだ質問を投稿します。
この度は、本当にありがとうございました。