/*

【ソ フ ト名】 動画『鳥の羽根』Version-2.0
【作  成  者】 ＴＥＲＵ   NIFTY-Serve ID:ISN00527
【掲  載  日】 92/05/27
【 開発言語 】 TURBO-C++1.0
【 対象機種 】 NEC PC-9801シリ−ズ（ハイレゾ,LT除く）
               EPSON PC-286,386,486シリ−ズ
【 確認機種 】 NEC PC-9801RA2, PC-9801VM2, EPSON PC-386M
【 梱包ﾌｧｲﾙ 】 ｶｵｽ3.EXE  （実行形式プログラム）
               ｶｵｽ3.C    （Ｃ言語ソースプログラム）
               ｶｵｽ3.DOC  （本ドキュメント）

　カオスのグラフィックスです．

　『動画鳥の羽根』(DL2#121)のバージョンアップ版です．
　改良点を以下に示します．

　【１】数値演算プロセッサ未搭載機への対応．
　【２】"MYU" の開始点，終了点，変化間隔のユーザによる設定．
　【３】１つの"MYU" に対する描画点数のユーザによる設定．
　【４】１６色モードを用いた明度の強化．
　【５】ｘ０，ｙ０の初期化．
　【６】一時停止 & 再開 & 途中終了
　です．

　【１】は，単なるバグの修正です．
　【２】は，プログラムを実行すればわかると思いますが，最初に各パラメータ
を入力するようになっています．"ｶｵｽ2"では，順に-0.08，-0.98，0.001 という
固定値でした．
　開始点，終了点は-1.0〜+1.0の範囲で実数を入力してください．
　【３】に関する説明は少し長くなります．
　"MYU" は段階（増分毎）的に変化します．この１つの"MYU" について，描く点
の数が"ｶｵｽ2"では1250点に固定でした．これを5000〜250 まで段階的（5000，25
00，1250，1000，625，500，250 ）に，ユーザが選択できるようにしました．
　これは多くすれば１つの"MYU" に対して多くの点を描く事になり，図形が細か
くきれいに表示されます．その代わり，実行時間が多く必要になり，動画として
は遅くなります．逆に値を小さくすれば，図形はドット数の少ないぼんやりした
ものとなりますが，高速に表示されます．
　今回は，この値を７段階の内から選択できるようにしました．"MYU" 関連の入
力の後に"SPEED" を入力する部分があります．ここで０〜６の数値を入力しま
す．これがこの5000〜250 の事であり，順に０〜６に対応します．
　"ｶｵｽ2"と同じにするには，SPEED=２と入力します．

　ここで，カラーグラフィック表示について簡単に説明します．静止画の時(DL2
#118) は点の密度をカラー番号（明るさ）に対応させていたのですが，動画では
高速化のために単純に青（Ｂ），赤（Ｒ），緑（Ｇ）の内，いずれかのプレーン
に描画します．ＢＲＧの組み合わせによりデジタル８色のグラフィックを表示し
ています．
　各プレーンにおいてそれぞれ5000個の点を描きます．5000点を描き終える毎に，
描画するプレーンを，Ｂ→Ｒ→Ｇ→Ｂ→・・・の順に変更して行きます．
　この描いた点の座標データを，配列変数を用いて過去15000 点のデータを保持，
表示しています．描画する際に15000 点前に描いた点を消去します．つまり同時
に15000 点を表示しつつ，最も古い点を消去してから，次の計算結果を描いてい
ます．
　"ｶｵｽ2"では，１つの"MYU" について1250点を描いていたので，合計１２画面
（12段階の"MYU" ）を同時に表示していました．今回，SPEED の値として０を選
択すれば３画面，６を選択すれば６０画面を同時表示する事になります．

　【４】上述のように15000 点前の座標データにより点の消去を行います．もし，
15000 点前の座標と同一プレーンに描いた最近の座標が同一の場合，その点を消
去してしまいます．この現象を防ぐため，１６色モードを用い，４プレーン使っ
て，書き込むプレーンと消去するプレーンを別にしています．その際にパレット
番号を変更する事により，書き込み＆消去プレーンを同一色としています．ただ，
この機能を使うためには特殊なライブラリ[*1]を使っていますので公開するソー
スリストからは削除します（実行形式ファイルには機能あり）．
　この機能は，１６色モードの使用可能な機種でのみ機能します．それ以外の場
合は，デジタル８色として動作します．
　なお，私がテストした感じとしては，あまり違いがみられませんでした（結構
時間を掛けてプログラミングしたのですが・・・）．
　【５】"ｶｵｽ2"では，ｘ，ｙの初期値は最初に一度だけ初期化し，"MYU" の値を
変更した際には初期化していませんでした．これを"MYU" を変更する毎に初期化
します．このことにより，"MYU" の値によっては，明らかにグラフィックが異な
る部分があります．
　【６】実行中にスペースキーを押すと，一時停止します．再開するには，何か
のキーを押して下さい．
　実行中に終了するには，ＥＳＣキーを押してください．

　さて，ソースプログラムに関して述べます．
　ソースプログラムを公開する際の問題といたしまして，Ｃの処理系の違い（MS
-CとTurbo-C） によるグラフィックライブラリの違いがあります．そこで汎用性
を持たせるために，参考書[*2]を基に改良した独自のグラフィックルーチンをソ
ースプログラム中に入れてあります．なお，ＶＲＡＭを直接アクセスするので，
再コンパイルにはデータ用ポインタが３２ビット幅になるメモリモデル（コンパ
クト，ラージ，ヒュージ）でコンパイルする必要があります．
　現在までに，Turbo-C++1.0，Turbo-C2.0，MS-C6.0Aでコンパイルできる事を確
認しています．
　なお，ＭＳ−Ｃによるコンパイルの確認を，本フォーラムのシスオペである村
上雅章氏にお願いいたしました．この場をお借りしてお礼を述べさせて頂きます．

*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <conio.h>
#include <dos.h>
#include <string.h>

#define XMAX  640U  /* 横ドット数 */
#define YMAX  400U  /* 縦ドット数 */
#define PLANE1 ((unsigned char *)0xa8000000L) /* Ｂ プレーン */
#define PLANE2 ((unsigned char *)0xb0000000L) /* Ｒ プレーン */
#define PLANE3 ((unsigned char *)0xb8000000L) /* Ｇ プレーン */
#define PLANE4 ((unsigned char *)0xe0000000L) /* Ｉ プレーン */
static union REGS regs , inregs , outregs ;   /* 8086レジスタ */

void locate(int x,int y)	/*  カーソル移動  */
{
	printf("\x1b[%d;%dH",y,x) ;
}

static int dgetc(void)
{
	regs.h.ah = 6;  regs.h.dl = 0xff;
	int86(0x21, &regs, &regs);
	return regs.h.al;
}

void hitanykey(void)  /* キーを押すまで待つ */
{
	fputc('\a', stderr) ;    /* beep */
	while (dgetc() == 0) ;   /* wait for any key */
}

void gr_dot(unsigned int x, unsigned int y,unsigned int color)  /* 点を表示 */
{
	unsigned int i ;
	unsigned char m1 , m2 ;
	static unsigned char
		mask1[] = { 128, 64, 32, 16,  8,  4,  2,  1 } ,
		mask2[] = { 127,191,223,239,247,251,253,254 } ;
	if (x >= XMAX || y >= YMAX) return ;
	i = 80 * y + (x >> 3) ;
	m1 = mask1[x & 7] ;  m2 = mask2[x & 7] ;
	if (color==0) PLANE1[i] |= m1 ; /*  Ｂプレーン描画  */
	if (color==1) PLANE2[i] |= m1 ; /*  Ｒプレーン描画  */
	if (color==2) PLANE3[i] |= m1 ; /*  Ｇプレーン描画  */
	if (color==3) PLANE4[i] |= m1 ; /*  Ｉプレーン描画  */
	if (color==4) PLANE1[i] &= m2 ; /*  Ｂプレーン消去  */
	if (color==5) PLANE2[i] &= m2 ; /*  Ｒプレーン消去  */
	if (color==6) PLANE3[i] &= m2 ; /*  Ｇプレーン消去  */
	if (color==7) PLANE4[i] &= m2 ; /*  Ｉプレーン消去  */
}

void gr_off(void)  /* グラフィック画面表示停止 */
{
	regs.h.ah = 0x41;
	int86(0x18, &regs, &regs);  /* PC-9801 BIOS */
	fputs("\x1b[>5l", stderr);  /* カーソル表示 */
}

void gr_on(void)  /* グラフィック画面初期化・表示開始 */
{
	static int first = 1;

	if (first) {  atexit(gr_off);  first = 0;  }
	memset(PLANE1, 0, 32000);  /* グラフィック画面クリア */
	memset(PLANE2, 0, 32000);
	memset(PLANE3, 0, 32000);
	memset(PLANE4, 0, 32000);
	/* 画面クリア, カーソル非表示, 最下行ユーザ使用 */
	fputs("\x1b[2J\x1b[>5h\x1b[>1h", stderr);
	regs.h.ah = 0x42;  /* スクリーンモード設定 */
	regs.h.ch = 0xc0;  /* 640×400 カラーモード */
	int86(0x18, &regs, &regs);  /* PC-9801 BIOS */
	regs.h.ah = 0x40;  /* グラフィック画面表示開始 */
	int86(0x18, &regs, &regs);  /* PC-9801 BIOS */
}

void gpcls(int i)
{
	if(i==0) memset(PLANE1, 0, 32000) ;
	if(i==1) memset(PLANE2, 0, 32000) ;
	if(i==2) memset(PLANE3, 0, 32000) ;
	if(i==3) memset(PLANE4, 0, 32000) ;
}

char inkey()
{
	inregs.h.ah=0x06 ; inregs.h.dl=0xff ;
	intdos(&inregs,&outregs) ;
	return(outregs.h.al) ;
}

void main()
{
	int i , i2 , j=0 , speed , s ;
	int col , colb ;
	int sp[7]={5000,2500,1250,1000,625,500,250} ;  /*  5000の約数  */
	float spoint , epoint , step ;
	double x0 , y0 , x1 , y1 , myu , myumin , myumax ;
	static int xp[15000] , yp[15000] ;
	long ts , te ;
	char k ;

	printf("\x1b*") ;
	printf("MYU 開始点 (ex -0.08 ) ？ ==> ") ; scanf("%f",&spoint) ;
	printf("MYU 終了点 (ex -0.98 ) ？ ==> ") ; scanf("%f",&epoint) ;
	printf("MYU  増分  (ex 0.001 ) ？ ==> ") ; scanf("%f",&step) ;
	printf("SPEED ( 遅: 0〜6 :速 ) ？ ==> ") ; scanf("%d",&s) ;
/*	spoint=-0.08 ; epoint=-0.98 ; step=0.001 ; s=2 ;  */
	if(spoint<epoint) {myumin=spoint ; myumax=epoint ; step=fabs(step) ; }
		else { myumin=epoint ; myumax=spoint ; step=-fabs(step) ; }
	if(s<0) s=0 ; if(s>6) s=6 ;
	speed=sp[s] ;
	myu=spoint ;
	gr_on() ;
	colb=col=2 ;
	time(&ts) ;

	while(1) {
	    k=inkey() ;
	    if(k!='\x00') {
		if(k==' ') {
		    locate (0,22) ; printf("STOP ... << HIT ANY KEY >> ") ;
		    hitanykey() ;
		    locate(0,22) ; printf("                           ") ;
		}
		if(k=='\x1b') {
		    locate (0,22) ; printf("END  ... << HIT ANY KEY >> ") ;
		    hitanykey() ;
		    locate(0,22) ; printf("                           ") ;
		    break ;
		}
	    }

	    colb=col=(col<2) ? (col+1) : 0 ;

	    for(i2=0;i2<5000/speed;i2++) {
		myu=myu+step ; if(myu>myumax || myu<myumin) break ;
		x0=1.0 ; y0=1.0 ;
		locate(0,0) ; printf("%9.5f",myu) ;
		for(i=0;i<speed;i++){
		    j=(j<(15000-1)) ? (j+1) : 0 ;
		    gr_dot(xp[j],yp[j],colb+4) ;
		    x1=y0+0.008*(1.0-0.05*y0*y0)*y0+(myu*x0+2.0*(1-myu)*x0*x0/(1.0+(x0*x0))) ;
		    y1=-x0+(myu*x1+2.0*(1-myu)*x1*x1/(1.0+(x1*x1))) ;
		    xp[j]=(int)(320+14*x1) ; yp[j]=(int)(200-14*y1) ;
		    gr_dot(xp[j],yp[j],col) ;
		    x0=x1 ; y0=y1 ;
		}
	    }
	    if(myu>myumax || myu<myumin) break ;
	}

	time(&te) ;
	locate(0,24) ;
	printf("Start Time = %s",ctime(&ts)) ;
	printf("End   Time = %s",ctime(&te)) ;
	printf("\n   <<< HIT ANY KEY ･･･ >>>\n") ;
	hitanykey() ;
	gr_off() ;
}
