71歳の... ワイヤーフレームの回転と透視変換でアニメーションをやってみた

　ワイヤーフレームの回転、透視変換（2点透視？）をやってみました。80本ほどの線なので簡単にぐりぐり動きます。今回、私としては初の構造体、動的メモリの確保とかを試しました。

![WireFrame0.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3189926/6c2b241b-99e4-466f-b239-84fd87496ed4.jpeg)

![WireFrame1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3189926/6c6684fb-e19b-44a3-9a61-fae11d47c370.jpeg)

環境等は

:::note info
１．Windows 11 Pro
２．Visual Studio Community 2022
３．DXライブラリ使用 https://dxlib.xsrv.jp/index.html
４．「C言語による アルゴリズム入門（河西朝雄）」　
https://gihyo.jp/book/2008/978-4-7741-3618-9

:::
４．の第８章グラフィックス Dr59 を参考にしました。VS、DxLibで意図通り動くように少し改変してあります。Y軸周りの回転、目の位置の上下、ズームイン・アウトを、キーやマウスボタンで操作できるようにしてみました。DXライブラリ```DxLib```の使い方は、https://dxlib.xsrv.jp/use/dxuse_vscom2022.html
を参考に。
　コードを示します。

```c:Win_Dr59.c
#define _USE_MATH_DEFINES
#include "DxLib.h"
#include <math.h>

typedef struct {
	int f;
	double x, y, z;
}Data;

Data a[]
={ 
	0,40,82,50, 1,40,97,50, 1,40,95,57, 1,40,95,47, 1,40,92,40, 1,40,98,40, 1,40,95,47, 1,40,95,40,

	0,0,0,0, 1,0,50,0, 1,80,50,0, 1,80,0,0, 1,0,0,0,
	0,5,5,0, 1,5,40,0, 1,25,40,0, 1,25,5,0, 1,5,5,0,
	0,10,25,0, 1,10,35,0, 1,20,35,0, 1,20,25,0, 1,10,25,0,
	0,0,0,100, 1,0,50,100, 1,80,50,100, 1,80,0,100, 1,0,0,100,

	0,0,0,0,  1,0,0,100,
	0,0,50,0, 1,0,50,100,
	0,80,50,0, 1,80,50,100,
	0,80,0,0, 1,80,0,100,

	0,80,30,25, 1,80,30,60, 1,80,40,60, 1,80,40,25,1,80,30,25,

	0,60,15,40, 1,60,15,70, 1,30,15,70, 1,30,15,40, 1,60,15,40,
	0,60,0,40,  1,60,15,40,
	0,60,0,70,  1,60,15,70,
	0,30,0,70,  1,30,15,70,
	0,30,0,40,  1,30,15,40,

	0,0,50,100, 1,40,82,100, 1,80,50,100,
	0,0,50,0, 1,40,82,0, 1,80,50,0,
	0,40,82,0, 1,40,82,100,

	0,50,74,100, 1,50,90,100, 1,65,90,100, 1,65,62,100,
	0,50,74,85,  1,50,90,85,  1,65,90,85,  1,65,62,85,
	0,50,74,100, 1,50,74,85,  1,65,62,85,
	0,50,90,85, 1,50,90,100,
	0,65,90,85, 1,65,90,100,
	0,65,62,85, 1,65,62,100,		-999,0,0,0 };

void Z_yR_Parse(
	double ay, // Rotation around y-Axis
	double x, double y, double z,
	double* px, double* py,
	double vp, double l, double m, double n )
{
	double h = (-x * sin(ay) + z * cos(ay) + n - vp) /(0 -  vp) ; // distance ratio in pojecting to x-y plane
	*px = (x * cos(ay) + z * sin(ay) + l) / h;	// x on screen at z=0
	*py = (y + m) / h;							// y on screen at z=0
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	const int Width = 960, Height = 640, WHITE = GetColor(255, 255, 255);
	const int K = 3;		// 元データの拡大率
	const int WXC = Width / 2, WYC = Height / K + 100; // 表示基準位置
	const double RAD=M_PI / 180;
	bool au = false;
	const int H_X = 80 * K, H_Z = 100 * K, H_Y = 90 * K; // データ(家)の縦、横の長さ
	double vp = H_Z * 2;	// Eye position
	double l = 0.0, m = 0.0, n = 0.0; // x,y,z方向の平行移動量

	int a_size = sizeof a / sizeof a[0]; // データ数を知る
	Data *b;
	b = (Data*)malloc(a_size * sizeof(Data)); // 拡大データ格納用
	for (int i = 0; i < a_size;i++) { // 表示用にデータを拡大
		b[i].f = a[i].f;
		b[i].x = a[i].x * K; b[i].y = a[i].y * K; b[i].z = a[i].z * K;
	}

	SetWindowText("DxLib Sample. Home Wire Frame"); SetGraphMode(Width, Height, 32); ChangeWindowMode(TRUE);
	if (DxLib_Init() == -1) return -1;
	SetBackgroundColor(0, 0, 0); SetDrawScreen(DX_SCREEN_BACK);

	int beta = 15; // y軸周りの回転初期値
	double ay, bx, by, px=0.0, py=0.0;
	int dx=0, dy=0;
	while (1) {
		ClearDrawScreen();
		DrawFormatString(0, 0, WHITE, "%d°", beta);
		DrawFormatString(0, 16, WHITE, "(%d lines)", a_size);
		DrawFormatString(110, 0, GetColor(200, 0, 255), "[Mouse] L_Button  or R_Button Rotate  [SP]:Auto Right Rotate");
		DrawFormatString(110, 16, GetColor(200, 100, 200), "[up]:Eye Point Up   [down]:Eye Point Down");
		DrawFormatString(110, 32, GetColor(200, 200, 100), "[left]:Left translate   [right]:Right translate  [I]:Zoom In   [O]:Zoom Out");

		ay = beta * RAD;
		for (int k = 0;b[k].f != -999; k++) {
			Z_yR_Parse(ay, b[k].x - H_X / 2, b[k].y - 130, b[k].z - H_Z / 2, &px, &py, vp, l, m, n);
			if (a[k].f != 0) {
				DrawLine(bx + WXC + dx, WYC - by + m, px + WXC + dx , WYC - py + m, GetColor(255-8*k, 8*k, 128*4*k));
			}
			bx = px; by = py;
		}

		if (CheckHitKey(KEY_INPUT_LEFT)) dx = dx - 5;
		if (CheckHitKey(KEY_INPUT_RIGHT)) dx = dx + 5;
		if ((GetMouseInput() & MOUSE_INPUT_LEFT))beta = beta - 5;	// Left Rotation
		if ((GetMouseInput() & MOUSE_INPUT_RIGHT))beta = beta + 5;	// Right Rotation
		if (CheckHitKey(KEY_INPUT_SPACE)) au = !au;		// on/off for Right auto Rotation
		if (CheckHitKey(KEY_INPUT_I)) {
			vp = vp - 10;
			if (vp < H_Z + 30)vp = H_Z + 30;}	// Zoom In
		if (CheckHitKey(KEY_INPUT_O)) {
			vp = vp + 10;
			if (vp > H_Z * 3)vp = H_Z * 3;}		// Zoom Out
		if (CheckHitKey(KEY_INPUT_DOWN)) {
			m = m + 10;
			if (m > H_Y * 2)m = H_Y * 2;}		// Eye Point Up
		if (CheckHitKey(KEY_INPUT_UP)) {
			m = m - 10;
			if (m < -H_Y)m = -H_Y;}				// Eye Point Down

		if(au == true) beta++;
		if (beta > 359) beta = 0;
		else if (beta < 0)beta = 360;

		ScreenFlip();
		WaitTimer(66);
		if (ProcessMessage() == -1)break;
		if (CheckHitKey(KEY_INPUT_ESCAPE) == 1)break;
	}

	DxLib_End();
	return 0;
}
```
　以前からやってみたい透視変換のアニメーション(?)でした。変換の公式についてよくわかっていないですが、まあ意図通り動かせたので結果オーライというところです。最後まで見ていただきありがとうございました。