/*
 * *****************************************************************
 * *                                                               *
 * *       Copyright (c) Fast Corporation, 1997                    *
 * *                                                               *
 * *   All Rights Reserved.  Unpublished rights  reserved  under   *
 * *   the copyright laws of the Japan.                            *
 * *                                                               *
 * *   The software contained on this media  is  proprietary  to   *
 * *   and  embodies  the  confidential  technology  of  Fast      *
 * *   Corporation.  Possession, use,  duplication  or             *
 * *   dissemination of the software and media is authorized only  *
 * *   pursuant to a valid written license from Fast Corporation.  *
 * *                                                               *
 * *****************************************************************
 */

/* CSC90X ボロノイ図・ルーチン  vrni.c */
/*[作成者]Y.Hori */
/*
目的:ボロノイ図の作成
関数:
履歴:	Ver 1.0		99/07/02
注記:m_menu.hをインクルードして下さい。
      m_menu.c及びm_note.cをリンクして下さい。
*/

/*********************************************************
** Include compiler runtime library                     **
*********************************************************/
#include <string.h>
#include <math.h>


/*********************************************************
** Include CSC90X library                               **
*********************************************************/
#include "f_stdlib.h"
#include "f_time.h"
#include "f_gui.h"
#include "f_stdio.h"
#include "f_graph.h"
#include "f_image.h"
#include "f_pinf.h"
#include "f_video.h"
#include "f_system.h"
#include "f_cmpgeo.h"		/* 計算幾何学ライブラリ */

/*********************************************************
** Include CSC90X common local                          **
*********************************************************/
#include "m_menu.h"


/*********************************************************
** プロトタイプ宣言                                     **
*********************************************************/
void  main( void );
void  main_menu_disp( void );
void  disp_help( void );
extern int  message_note( void );
static int	cursor( WPNT2_T	i_data[] );
static void exec( void );
static int	random( WPNT2_T point[], int seed );
static void set( void );
static void draw_point( int x, int y );
static int  restricted_cursor( WPNT2_T  *pnt );
static void prdm_srtpt( int timing, int n, PARADIGM val[] );
static void prdm_size( int timing, int n, PARADIGM val[] );
static void disp_info( void *dscrpt,  WPNT2_T *o_data );


/*********************************************************
** メニュー項目                                         **
*********************************************************/
#define LANG_N       2
#define MAIN_MENU_N  4
static const char *str_main_menu[MAIN_MENU_N][LANG_N] = {
	{ "   SET    ", "  設 定  " },
	{ "   EXEC   ", " 試 行  " },
	{ "  PROC3   ", "  処理3  " },
	{ "  PROC4   ", "  処理4  " }
};


#define  INIT_CUR_POS_X		255
#define  INIT_CUR_POS_Y		239

#define  MAX_POINT			1024
/*********************************************************
** グローバル変数                                       **
*********************************************************/
int		rand_or_cursor = 0;
int		rand_value = 30;
int		w_start_x = 0, w_start_y = 0;
int		w_size_x = 512, w_size_y = 480;
int		on_off = 1;
unsigned int		time = 0;
PVAL	pad_value[5];

/*********************************************************
** メイン                                               **
*********************************************************/
void  main( void )
{
	int  sts;
	int  xpos;
	int  ypos;
	int  s_xpos;
	int  s_ypos;

	PRIVATE WORD		wXs, wYs, wXe, wYe;


	/* 初期メッセージ表示 */
	if( NORMAL_RETURN != message_note() )
		return;

	/* パラメタ初期化 */
	xpos = INIT_CUR_POS_X;
	ypos = INIT_CUR_POS_Y;
	s_xpos = INIT_CUR_POS_X;
	s_ypos = INIT_CUR_POS_Y;

	/* 入力ビデオ制御 */
	Lib_input_video_control( GRAY_PLANE );

	/* ビデオ出力表示項目制御 */
	Lib_display_control( GRAY_PLANE | LINE_PLANE | CHAR_PLANE );

	/* ビデオ表示項目クリア */
	Lib_memory_clear( LINE_PLANE | CHAR_PLANE );

	/* カーソル初期化 */
	Lib_init_cursor();

	Lib_get_stage_window( &wXs, &wYs, &wXe, &wYe );

	/* メインメニューの表示 */
	main_menu_disp();

	/* マウスカーソルの表示 */
	Lib_draw_cursor( INIT_CUR_POS_X, INIT_CUR_POS_Y );

	Lib_strtclk( );
	/* メニュー制御 */
	for (;;)
	{
		sts = 0;

		/* マウス位置読みとり */
		sts = Lib_see_current_position( &xpos, &ypos );
		if ( s_xpos != xpos || s_ypos != ypos )
		{
			/* マウス表示位置移動 */
			Lib_move_cursor( xpos, ypos );
			s_xpos = xpos;
			s_ypos = ypos;
		}

		/* 処理振り分け */
		if( CURSOR_EXECUTE == sts )
		{
			if ( xpos > MENU_1_XS && xpos < MENU_1_XE && ypos > MENU_1_YS && ypos < MENU_1_YE )
			{
				Lib_memory_clear( LINE_PLANE | CHAR_PLANE );
				set( );												/* 設定 */
				Lib_memory_clear( LINE_PLANE | CHAR_PLANE );
				main_menu_disp();
			}
			else if ( xpos > MENU_2_XS && xpos < MENU_2_XE && ypos > MENU_2_YS && ypos < MENU_2_YE )
			{
				Lib_memory_clear( LINE_PLANE | CHAR_PLANE );
				exec( );											/* 試行 */
				Lib_memory_clear( LINE_PLANE | CHAR_PLANE );
				main_menu_disp();
			}
			else if ( xpos > MENU_H_XS && xpos < MENU_H_XE && ypos > MENU_H_YS && ypos < MENU_H_YE )
			{
				Lib_memory_clear( LINE_PLANE | CHAR_PLANE );
				disp_help();										/* ヘルプ */
				main_menu_disp();
			}
			else if ( xpos > MENU_E_XS && xpos < MENU_E_XE && ypos > MENU_E_YS && ypos < MENU_E_YE )
			{
				break;                                              /* 終了 */
			}
		}

	}
	Lib_set_stage_window( wXs, wYs, wXe, wYe );
}


/*********************************************************
** メインメニュー表示                                   **
*********************************************************/
static void  main_menu_disp( void )
{
	int iLanguage;

	/* 日本語/英語表示文字列切替情報取得 */
	iLanguage = Lib_get_disp_language();

	/* 整列キー表示 -> ( m_menu.c ) */
	SUB_menu_disp4he( (char *)str_main_menu[0][iLanguage],
					(char *)str_main_menu[1][iLanguage],
                    (char *)NULL,
                    (char *)NULL
					);
}


/*********************************************************
** ヘルプ表示                                           **
*********************************************************/
static void  disp_help( void )
{
	int		i=8;

    Lib_chrdisp( 10, i, "【機能\概要】                           VRNI" ); i++;
    Lib_chrdisp( 11, i, "このプログラムはボロノイ図を作成するものです。" );	i++;
    Lib_chrdisp( 11, i, "設定で乱数を使って点を作り出すか、カーソ\ルによ" );	i++;
	Lib_chrdisp( 11, i, "って、自ら打ち込むか選択できます。また乱数の場" );	i++;
    Lib_chrdisp( 11, i, "合、点の個数も設定できます。登録された点でボロ" );	i++;
    Lib_chrdisp( 11, i, "ノイ図を作成、表示させたあと、カーソ\ル操作で各" );	i++;
    Lib_chrdisp( 11, i, "母点を囲む面積や直線の本数、その直線の方程式の" );	i++;
    Lib_chrdisp( 11, i, "表\示を行なうことができます" );	i++;	i++;
    Lib_chrdisp( 10, i, "【注意点】" ); i++;
    Lib_chrdisp( 11, i, "点が重なっているとボロノイ図が作成できないので" );	i++;
    Lib_chrdisp( 11, i, "ソ\ートを行なうことによって、重なっている点を除" );	i++;
    Lib_chrdisp( 11, i, "去しています。これによって、打ちこんだ点の数よ" );	i++;
    Lib_chrdisp( 11, i, "りも、少なくなることがあります。" );	i++;
	Lib_chrdisp( 11, i, "また入力できる点の数は最大1024個です。");

}


/*********************************************************
** 設定                                                 **
*********************************************************/
static void set( void )
{
	int		pad_level;				/* パッドレベル */
	int		no;						/* メニュー実行の戻り値 */
	int		height, width;			/* キーサイズ */
	char	*boten_select[2] = { " 乱数 ", "カーソ\ル" };		/* 選択属性 */

	/* 現在値代入 */
	pad_value[0].select_type = rand_or_cursor;
	pad_value[1].value_type = rand_value;
	pad_value[2].box_type.x = w_start_x;
	pad_value[2].box_type.y = w_start_y;
	pad_value[3].box_type.x = w_size_x;
	pad_value[3].box_type.y = w_size_y;
	pad_value[4].on_off_type = on_off;

	/* 現在のウィンドウ範囲を表示 */
	Lib_box( pad_value[2].box_type.x, pad_value[2].box_type.y,
			pad_value[2].box_type.x+pad_value[3].box_type.x - 1,
			pad_value[2].box_type.y+pad_value[3].box_type.y - 1, SOLID_LINE );

	/* パッドのオープン */
	pad_level = Lib_view_open( );
	/* メニューサイズの取得 */
	Lib_set_pad_maxstring( 12 );
	Lib_get_standard_key_size( &width, &height );
	/* パッドのタイトル */
	Lib_view_set_title( pad_level, "設 定" );
	/* メニューの登録 */
	Lib_view_set_select( pad_level, 5, 5, "母点作成方法",
										pad_value[0].select_type, 2, boten_select, 0 );
	Lib_view_set_uniq_numeral( pad_level, 5, 8+height, "乱数の母点数",
										pad_value[1].value_type, 2, 300, 1 );
	Lib_view_set_box( pad_level, 5, 11+height*2, "W始点", pad_value[2].box_type.x,
								pad_value[2].box_type.y, 3, 3, 2 );
	Lib_view_set_box( pad_level, 5, 14+height*3, "Wサイズ", pad_value[3].box_type.x,
								pad_value[3].box_type.y, 3, 2, 3 );
	Lib_view_set_uniq_alter( pad_level, 5, 17+height*4, "方程式表\示", pad_value[4].on_off_type, 4 );

	/* パラダイムの登録 /*/
	Lib_set_paradigm( pad_level, 2, ST_AND_ED_TIMING, prdm_srtpt );
	Lib_set_paradigm( pad_level, 3, ST_AND_ED_TIMING, prdm_size );

	/* パッドの表示位置 */
	Lib_view_set_size( pad_level, 30, 50, 5, 5 );
	/* パッドの表示 */
	Lib_draw_menu( pad_level );

	if(ERROR_RETURN != ( no = Lib_process_menu( pad_level, pad_value )) )
	{
		switch( no )
		{
	/* 実行ボタンが押されたら */
			case	101:	rand_or_cursor = pad_value[0].select_type;
							rand_value = pad_value[1].value_type;
							w_start_x = pad_value[2].box_type.x;
							w_start_y = pad_value[2].box_type.y;
							w_size_x = pad_value[3].box_type.x;
							w_size_y = pad_value[3].box_type.y;
							on_off = pad_value[4].on_off_type;
							break;
	/* 取り消しボタンが押されたら */
			case	102:	break;
	/* 異常終了 */
			case	-1:		break;			/* 異常終了 */
		}
	}
	else
		Lib_display_message( 100, 100, "Error", "Menu Pad Error" );	/* エラー表示 */

	Lib_erase_menu(pad_level);						/* メニューパッドを消す */
	Lib_view_close(pad_level);						/* パッドのクローズ */
	Lib_memory_clear( LINE_PLANE );					/* ラインを消す */
}


/*********************************************************
** パラダイム ボックス始点                             **
*********************************************************/
static void prdm_srtpt( int timing, int n, PARADIGM val[] )
{
	if( timing == START_TIMING )		/* ボックスを消す */
	{
		Lib_box( pad_value[2].box_type.x, pad_value[2].box_type.y,
			pad_value[2].box_type.x+pad_value[3].box_type.x,
			pad_value[2].box_type.y+pad_value[3].box_type.y, BLACK_LINE );
	}

	if( timing == END_TIMING )			/* ボックスを描く */
		Lib_box( val[0].int_type, val[1].int_type, val[0].int_type + val[2].int_type,
					val[1].int_type + val[3].int_type, SOLID_LINE );
}


/*********************************************************
** パラダイム ボックスサイズ                           **
*********************************************************/
static void prdm_size( int timing, int n, PARADIGM val[] )
{
	if( timing == START_TIMING )		/* ボックスを消す */
	{
		Lib_box( pad_value[2].box_type.x, pad_value[2].box_type.y,
			pad_value[2].box_type.x+pad_value[3].box_type.x,
			pad_value[2].box_type.y+pad_value[3].box_type.y, BLACK_LINE );
	}

	if( timing == END_TIMING )			/* ボックスを描く */
		Lib_box( val[0].int_type, val[1].int_type, val[0].int_type + val[2].int_type,
					val[1].int_type + val[3].int_type, SOLID_LINE );
}


/*********************************************************
** 乱数の発生                                           **
*********************************************************/
static int random( WPNT2_T point[], int seed )
{
	int		i, j;
	int		bunbo, bunshi, amari;		/* 乱数発生の元となる分数を入れる */
	int		temp;

	bunshi = 98765;
	bunbo = 17341;
	j = 0;

	for( i = 0; i < seed+rand_value; i++ )
	{
		temp = bunshi / bunbo;
		amari = bunshi % bunbo;
		bunshi = amari * 1000;
		if( i >= seed - 1 )				/* iがseed以上になったら代入をはじめる */
		{
			point[j].x = temp;
			temp = bunshi / bunbo;
			amari = bunshi % bunbo;
			bunshi = amari * 1000;
			point[j].y = temp;
			j++;
		}
	}

	for( i=0; i < rand_value; i++ )		/* ウィンドウサイズに収まるように値を変換 */
	{
			point[i].x = point[i].x % w_size_x;
			point[i].x += w_start_x;
			point[i].y = point[i].y % w_size_y;
			point[i].y += w_start_y;
	}

	return( rand_value );				/* 点の個数を戻す */
}


/*********************************************************
** 試行                                                 **
*********************************************************/
static void exec( void )
{
	void	*dscrpt_vrni;			/* ボロノイ図のディスクリプタ */
	void	*dscrpt_sort;			/* 辞書的順序ソートのディスクリプタ */
	WPNT2_T	*i_data;				/* ソート前のデータを格納 */
	WPNT2_T *o_data;				/* ソート後のデータを格納 */
	int		pnt_num;				/* ソート前の点の数 */
	int		o_pnt_num;				/* ソート後の点の数 */
	int		*in_to_out_data;		/* ソートでどう点を動かしたかの情報 */
	int		*out_to_in_data;		/* ソートでどう点を動かしたかの情報 */
	int		i;
	char	str[64];

	/* ボロノイ図のオープン */
	if( NULL != (dscrpt_vrni = Lib_cg_2dVrniDgrm_open( MAX_POINT )) )
	{
	/* 辞書的順序ソートのオープン */
		if( NULL != ( dscrpt_sort = Lib_cg_ordering_data_open( MAX_POINT )) )
		{
	/* メモリの確保 */
			if(( WPNT2_T * )NULL != ( i_data = ( WPNT2_T * )Lib_mlalloc( sizeof( WPNT2_T )* MAX_POINT )) )
			{
	/*  画像を消す  */
				Lib_display_control( LINE_PLANE | CHAR_PLANE );
				Lib_box( w_start_x, w_start_y, w_start_x+w_size_x-1, w_start_y+w_size_y-1, SOLID_LINE );
				if( rand_or_cursor == 0 )	/* 乱数を選択していた場合 */
				{
					time += Lib_readclk( );
					while( time > 50000 )	/* timeが50000以上になったら、50000を引く */
					{
						time -= 50000;
					}
					pnt_num = random( i_data, time );	/* timeがseedになる */
				}
				else						/* カーソルを選択していた場合 */
					pnt_num = cursor( i_data );

	/* 点が重なっているかもしれないので、ソートを行なう */
				Lib_cg_ordering_data_exec( dscrpt_sort, i_data, pnt_num, &o_data, &o_pnt_num,
															&in_to_out_data, & out_to_in_data );


	/* ソートされた後の点を描く */
				for( i=0; i<o_pnt_num; i++ )
					draw_point( o_data[i].x, o_data[i].y );
				Lib_sprintf( str, "点の個数 = %d", o_pnt_num );
				Lib_chrdisp( 1, 30, str );
				Lib_display_keyinput( 0, 0, "確認" );
	/* 画面を消す */
				Lib_memory_clear( CHAR_PLANE | LINE_PLANE );
				Lib_box( w_start_x, w_start_y, w_start_x+w_size_x-1, w_start_y+w_size_y-1, SOLID_LINE );
	/* ボロノイ図を作る */
				if( 0 == Lib_cg_2dVrniDgrm_make( dscrpt_vrni, o_data, o_pnt_num, w_start_x, w_start_y,
																w_start_x+w_size_x, w_start_y+w_size_y ))
				{
	/* ボロノイ図を描写する */
					Lib_cg_2dVrniDgrm_draw( dscrpt_vrni );
	/* 情報を表示する */
					disp_info( dscrpt_vrni, o_data );
				}
				else
					Lib_display_message( 150, 150, "エラー", "ボロノイ図が作成できませんでした" );

	/* 画面を元に戻す */
				Lib_display_control( CHAR_PLANE | LINE_PLANE | GRAY_PLANE );
				Lib_freerun( );
	/* メモリの開放 */
				Lib_lfree( (char *)i_data );
			}
			else
				Lib_display_message( 150, 150, "エラー", "メモリが確保できませんでした" );
	/* ソートのクローズ */
			Lib_cg_ordering_data_close( dscrpt_sort );
		}
		else
			Lib_display_message( 130, 150, "エラー", "ソートがオープンできませんでした" );
	/* ボロノイ図のクローズ */
		Lib_cg_2dVrniDgrm_close( dscrpt_vrni );
	}
	else
		Lib_display_message( 100, 150, "エラー", " ボロノイ図がオープンできませんでした" );
}


/*********************************************************
** カーソル入力                                         **
*********************************************************/
static int	cursor( WPNT2_T	i_data[] )
{
	int		i = 0;
	char	str[64];
	int		rtn;
	WPNT2_T  pnt;

	/* キャンセルキーが押され、点の数が2以上になるまでつづける */
	while( rtn != 2 )
	{
	/* カーソルの動ける範囲で点を打たれるのを待つ */
		if( 1 == ( rtn = restricted_cursor( &pnt )) )
		{
			draw_point( pnt.x, pnt. y );
			i_data[i].x = pnt.x;
			i_data[i].y = pnt.y;
			i++;
			if( i >= MAX_POINT )
				break;
		}
		Lib_chrdisp( 1, 30, "CANCELキーで終了" );
		Lib_sprintf( str, "i = %d", i );
		Lib_chrdisp( 1, 29, str );
	}
	Lib_cls( CHAR_PLANE, BLACK_COLOR );
	return(i);
}


/*********************************************************
** 制限付カーソル(カーソルの動ける範囲を限定)           **
*********************************************************/
static  int  restricted_cursor( WPNT2_T  *pnt )
{
	int   key_stts;		/*  キー押下状況  	*/
	int   x, y;			/*  カーソルの位置	*/
	
	
	/*  キー押下状況の初期化  */
	key_stts = 0;
	
	/*  カーソル位置の初期化  */
	x = pnt->x;
	y = pnt->y;
	
	/*  カーソルの位置を取得  */
	key_stts = Lib_see_current_position( &x, &y );

	/*  カーソルの動きの制限  */
	if( x < w_start_x )	x = w_start_x;
	if( y < w_start_y )	y = w_start_y;
	if( w_start_x + w_size_x - 1 < x )	x = w_start_x + w_size_x - 1;
	if( w_start_y + w_size_y - 1 < y )	y = w_start_y + w_size_y - 1;
	
	/*  カーソルの移動  */
	Lib_move_cursor( x, y );
	
	/*  カーソルの描画  */
	Lib_draw_cursor( x, y );
	
	/*  カーソルの位置  */
	pnt->x = x;
	pnt->y = y;
	
	/*  返値;キー押下状態  */
	return( key_stts );
}


/*********************************************************
** 点を書く                                           **
*********************************************************/
static void draw_point( int x, int y )
{
	Lib_line( x-1, y-1, x+1, y+1, SOLID_LINE );
	Lib_line( x+1, y-1, x-1, y+1, SOLID_LINE );
}


/*********************************************************
** 情報を表示                                           **
*********************************************************/
static void disp_info( void *dscrpt,  WPNT2_T *o_data )
{
	WPNT2_T	pnt;				/* 点 */
	VG_INFO	*info;				/* 情報 */
	int		nearest_num;		/* 最も近い母点 */
	char	str[64];
	int		cursor_rtn;			/* カーソルの戻り値 */
	int		i, j;

	/* CANCELキーが押されるまで続ける */
	while( 2 != cursor_rtn )
	{
	/* カーソルの状態を見る */
		cursor_rtn = restricted_cursor( &pnt );
		Lib_sprintf( str, "CANCELキーで終了");
		Lib_chrdisp( 1, 30, str );
	/*  EXECUTEキーが押されたら */
		if( 1 == cursor_rtn )
		{
	/* もっとも近い母点を探す */
			if( -1 != ( nearest_num = Lib_cg_2dVrniDgrm_nearest_gnrt( dscrpt, pnt )) )
			{
	/* 一旦画面を消す */
				Lib_cls( CHAR_PLANE | LINE_PLANE, BLACK_COLOR );
	/* 枠線を描く */
				Lib_box( w_start_x, w_start_y, w_start_x+w_size_x-1, w_start_y+w_size_y-1, SOLID_LINE );

	/* ボロノイ図を描写 */
				Lib_cg_2dVrniDgrm_draw( dscrpt );
	/* 母点の周りを囲む */
				Lib_box( o_data[nearest_num].x-5, o_data[nearest_num].y-5,
						o_data[nearest_num].x+5, o_data[nearest_num].y+5, SOLID_LINE );
	/* 母点の情報を得る */
				Lib_cg_2dVrniDgrm_get_gnrt_info( dscrpt, nearest_num, &info );

	/* 情報の表示 */

				Lib_sprintf( str, "隣接母点数 = %d", info->num );
				Lib_chrdisp( 40, 3, str );
				Lib_sprintf( str, "領域面積  = %.1lf", info->area );
				Lib_chrdisp( 40, 4, str );

	/* 方程式表示がオンの場合 */
				if( on_off == 1 )
				{
					Lib_chrdisp( 40, 6, "母点を囲む直線の方程式" );
					j = 7;
					for( i = 0; i < info->num; i++ )
					{
						if( info->ve_info[i].b < 0 && info->ve_info[i].c > 0 )
							Lib_sprintf( str, "%dx%dy+%d = 0", info->ve_info[i].a, info->ve_info[i].b, info->ve_info[i].c );
						else if( info->ve_info[i].b > 0 && info->ve_info[i].c < 0 )
							Lib_sprintf( str, "%dx+%dy%d = 0", info->ve_info[i].a, info->ve_info[i].b, info->ve_info[i].c );
						else if( info->ve_info[i].b < 0 && info->ve_info[i].c < 0 )
							Lib_sprintf( str, "%dx%dy%d = 0", info->ve_info[i].a, info->ve_info[i].b, info->ve_info[i].c );
						else
							Lib_sprintf( str, "%dx+ %dy+ %d = 0", info->ve_info[i].a, info->ve_info[i].b, info->ve_info[i].c );
						Lib_chrdisp( 40, j, str );
						j++;
					}
				}
			}
		}
	}
}