/////////////////////////////////////////////////
//						OpenTK_Primitives.cs
//						OpenTKの基本図形集
//						Copyright (c) 2024 by Ysama
//共通仕様：
//(1) 第１引数がソリッド・ワイアーのフラグ(bool)
//(2) 以降の引数に既定値を与える
/////////////////////////////////////////////////
using System;
using OpenTK;									//OpenTKを使用する際に必須
using OpenTK.Graphics;						//OpenTKのグラフィックを使用する際に必須
using OpenTK.Graphics.OpenGL;			//OpenGLを使用する際に必要

namespace OpenTK_Primitives
{
	public class OTKPrims
	{
		//立（直）方体（引数：ソリッドまたはワイアー、横、縦、高さ）
		public void Cube(bool SorW = true, double hor = 1.0, double ver = 1.0, double dep = 1.0)
		{
			//立（直）方体データ
			double[][] Qvertex = new double[8][];	//四角形の8頂点（奥左下から反時計回り4点、手前左下から反時計回り4点）
			Qvertex[0] = new double[3] {-hor / 2, -ver / 2, -dep / 2};
			Qvertex[1] = new double[3] { hor / 2, -ver / 2, -dep / 2};
			Qvertex[2] = new double[3] { hor / 2,  ver / 2, -dep / 2};
			Qvertex[3] = new double[3] {-hor / 2,  ver / 2, -dep / 2};
			Qvertex[4] = new double[3] {-hor / 2, -ver / 2,  dep / 2};
			Qvertex[5] = new double[3] { hor / 2, -ver / 2,  dep / 2};
			Qvertex[6] = new double[3] { hor / 2,  ver / 2,  dep / 2};
			Qvertex[7] = new double[3] {-hor / 2,  ver / 2,  dep / 2};
			int[][] Qedge = new int[12][];			//頂点を結ぶ12線分
			Qedge[0] = new int[2] {0, 1};
			Qedge[1] = new int[2] {1, 2};
			Qedge[2] = new int[2] {2, 3};
			Qedge[3] = new int[2] {3, 0};
			Qedge[4] = new int[2] {4, 5};
			Qedge[5] = new int[2] {5, 6};
			Qedge[6] = new int[2] {6, 7};
			Qedge[7] = new int[2] {7, 4};
			Qedge[8] = new int[2] {0, 4};
			Qedge[9] = new int[2] {1, 5};
			Qedge[10] = new int[2] {2, 6};
			Qedge[11] = new int[2] {3, 7};
			int[][] Qface = new int[6][];			//四角形6面
			Qface[0] = new int[4] {0, 1, 2, 3};		//奥面
			Qface[1] = new int[4] {1, 5, 6, 2};		//右面
			Qface[2] = new int[4] {5, 4, 7, 6};		//手前面
			Qface[3] = new int[4] {4, 0, 3, 7};		//左面
			Qface[4] = new int[4] {4, 5, 1, 0};		//底面
			Qface[5] = new int[4] {3, 2, 6, 7};		//天面
			Vector3[] vecNormal = new Vector3[6] {-Vector3.UnitZ, Vector3.UnitX, Vector3.UnitZ, -Vector3.UnitX, -Vector3.UnitY, Vector3.UnitY};
			//立（直）方体描画
			GL.PushMatrix();
			if(SorW)
			{
				GL.Begin(PrimitiveType.Quads);		//四角面を描画(Solid)
					for(int j = 0; j < 6; j++)
					{
						GL.Normal3(vecNormal[j]);	//法線の設定
						for(int i = 0; i < 4; i++)
							GL.Vertex3(Qvertex[Qface[j][i]][0], Qvertex[Qface[j][i]][1], Qvertex[Qface[j][i]][2]);
					}
				GL.End();
			}
			else
			{
				GL.Begin(PrimitiveType.Lines);		//四角面を描画(Wire)
					for(int j = 0; j < 12; j++)
					{
						for(int i = 0; i < 2; i++)
							GL.Vertex3(Qvertex[Qedge[j][i]][0], Qvertex[Qedge[j][i]][1], Qvertex[Qedge[j][i]][2]);
					}
				GL.End();
			}
			GL.PopMatrix();
		}

		//（正）三角錐（引数：ソリッドまたはワイアー、辺長さ、縦辺（ver - 既定1.0）の倍数）
		public void Triangular(bool SorW = true, double len = 1.225, double ratio = 1.0)
		{
			double hor = len / 2;
			double dep = len * Math.Sin(Math.PI / 3);				//Sin(60)
			double ver = len * ratio * Math.Sqrt(2.0 / 3.0);	//sqrt(power(len, 2.0) + power(dep * 2.0 / 3.0, 2.0))
			//（正）三角錐データ
			double[][] Tvertex = new double[4][];	//三角錐の4頂点
			Tvertex[0] = new double[3] {0.0, ver * 2 / 3, 0.0};			//頂天
			Tvertex[1] = new double[3] {hor, -ver / 3, -dep / 3};			//右奥
			Tvertex[2] = new double[3] {-hor,  -ver / 3, -dep / 3};	//左奥
			Tvertex[3] = new double[3] {0.0,  -ver / 3, dep * 2 / 3};	//手前下
			int[][] Tedge = new int[6][];				//頂点を結ぶ5線分
			Tedge[0] = new int[2] {0, 1};
			Tedge[1] = new int[2] {1, 2};
			Tedge[2] = new int[2] {2, 0};
			Tedge[3] = new int[2] {0, 3};
			Tedge[4] = new int[2] {3, 2};
			Tedge[5] = new int[2] {1, 3};
			int[][] Tface = new int[4][];				//三角錐の4面
			Tface[0] = new int[3] {0, 1, 2};
			Tface[1] = new int[3] {0, 2, 3};
			Tface[2] = new int[3] {0, 3, 1};
			Tface[3] = new int[3] {1, 2, 3};
				if(SorW) {
				GL.Begin(PrimitiveType.Triangles);
						for(int j = 0; j < 4; ++j)
						{
							Vector3 v1 = new Vector3((float)Tvertex[Tface[j][0]][0], (float)Tvertex[Tface[j][0]][1], (float)Tvertex[Tface[j][0]][2]);
							Vector3 v2 = new Vector3((float)Tvertex[Tface[j][1]][0], (float)Tvertex[Tface[j][1]][1], (float)Tvertex[Tface[j][1]][2]);
							Vector3 v3 = new Vector3((float)Tvertex[Tface[j][2]][0], (float)Tvertex[Tface[j][2]][1], (float)Tvertex[Tface[j][2]][2]);
							GL.Normal3(Vector3.Normalize(Vector3.Cross(v2 - v1, v3 - v1)));
							for(int i = 0; i < 3; ++i)
									GL.Vertex3(Tvertex[Tface[j][i]][0], Tvertex[Tface[j][i]][1], Tvertex[Tface[j][i]][2]);
						}
				GL.End();
			}
			else {
				GL.Begin(PrimitiveType.Lines);
						for(int i = 0; i < 6; i++)
						{
							GL.Vertex3(Tvertex[Tedge[i][0]]);
							GL.Vertex3(Tvertex[Tedge[i][1]]);
						}
				GL.End();
			}
		}

		//球（引数：ソリッドまたはワイアー、半径、360°の分割数）
		public void Sphere(bool SorW = true, double radius = 0.5, int slices = 36)
		{
			double angle = Math.PI / slices * 2;	//angle(radian) = (360 / slices) * (Math.PI / 180);
			if(SorW)
				GL.Begin(PrimitiveType.TriangleStrip);			//球体を描画（Solid）
			else
				GL.Begin(PrimitiveType.LineStrip);				//球体を描画（Wire）
			{
				for(int j = -slices / 2; j < slices / 2; j++)	//slices along Z軸
				{
						for(int i = 0; i <= slices; i++)	//slices around Z軸
						{
							Vector3d vec1 = new Vector3d(radius * Math.Cos(angle * j) * Math.Cos(angle * i), radius * Math.Cos(angle * j) * Math.Sin(angle * i), radius * Math.Sin(angle * j));
							GL.Normal3(vec1);
							GL.Vertex3(vec1);
							Vector3d vec2 = new Vector3d(radius * Math.Cos(angle * (j - 1)) * Math.Cos(angle * i), radius * Math.Cos(angle * (j - 1)) * Math.Sin(angle * i), radius * Math.Sin(angle * (j - 1)));
							GL.Normal3(vec2);
							GL.Vertex3(vec2);
						}
				}
			}
			GL.End();
		}

		//円環（引数：ソリッドまたはワイアー、半径、円環の太さ、面の肌理）
		public void Torus(bool SorW = true, double r1 = 1.0, double r2 = 0.2, int count = 128)
		{
			if(!SorW) count = 32;	//ワイアーの場合、肌理を粗くする
			GL.Material(MaterialFace.Front, MaterialParameter.Ambient, Color4.Cyan);
			double x = r1 * Math.Sqrt(2 * (1 - Math.Cos((Math.PI / 180.0) * (360f / count))));
			for(int k = 0; k < count; k++)
			{
				GL.PushMatrix();
				GL.Rotate(360f / count * k, 0, 1, 0);			//Y軸を中心として一周する
				GL.Translate(0, 0, r1 - r2);					//Z軸方向へ半径 - 円環太さ移動
				for(int i = 0; i <= count; i++)
				{
					GL.Rotate(360f / count, 1, 0, 0);			//X軸を中心に指定角回転
					if(SorW)
						GL.Begin(PrimitiveType.Quads);			//四角面を描画(Solid) count = 128;
					else
						GL.Begin(PrimitiveType.LineStrip);	//四角面を描画(Wire) count = 32;
					{
						Vector3 p1 = new Vector3((float)(-x / 2), (float)r2, (float)(x / 2));
						Vector3 p2 = new Vector3((float)(x / 2), (float)r2, (float)(x / 2));
						Vector3 p3 = new Vector3((float)(x / 2), (float)r2, (float)(-x / 2));
						GL.Normal3(Vector3.Normalize(Vector3.Cross(p2 - p1, p3 - p1)));
						GL.Vertex3(-x / 2, r2, x / 2);
						GL.Vertex3(x / 2, r2, x / 2);
						GL.Vertex3(x / 2, r2, -x / 2);
						GL.Vertex3(-x / 2, r2, -x / 2);
					}
					GL.End();
				}
				GL.PopMatrix();
			}
		}

		//円柱（引数：ソリッドまたはワイアー、半径、縦の長さ）
		public void Pipe(bool SorW = true, double radius = 0.5, double length = 2.0)
		{
			double step = 0.1;
			for(double rad = -Math.PI; rad <= Math.PI; rad += step)
			{
				if(SorW)
					GL.Begin(PrimitiveType.Quads);
				else
					GL.Begin(PrimitiveType.LineStrip);
					//側面法線
					Vector3 v0 = new Vector3((float)(radius * Math.Cos(rad)), (float)(length / 2), (float)(radius * Math.Sin(rad)));
					Vector3 v1 = new Vector3((float)(radius * Math.Cos(rad)), (float)-(length / 2), (float)(radius * Math.Sin(rad)));
					Vector3 v2 = new Vector3((float)(radius * Math.Cos(rad + step)), (float)-(length / 2), (float)(radius * Math.Sin(rad + step)));
					GL.Normal3(Vector3.Normalize(Vector3.Cross(v1 - v0, v2 - v0)));
					//側面
					GL.Vertex3(radius * Math.Cos(rad + step), length / 2, radius * Math.Sin(rad + step));
					GL.Vertex3(radius * Math.Cos(rad), length / 2, radius * Math.Sin(rad));
					GL.Vertex3(radius * Math.Cos(rad), -length / 2, radius * Math.Sin(rad));
					GL.Vertex3(radius * Math.Cos(rad + step), -length / 2, radius * Math.Sin(rad + step));
				GL.End();
				if(SorW)
					GL.Begin(PrimitiveType.Triangles);
				else
					GL.Begin(PrimitiveType.LineStrip);
				//天面法線
				GL.Normal3(Vector3.UnitY);
				//天面
					GL.Vertex3(0.0, length / 2, 0.0);
					GL.Vertex3(radius * Math.Cos(rad), length / 2, radius * Math.Sin(rad));
					GL.Vertex3(radius * Math.Cos(rad + step), length / 2, radius * Math.Sin(rad + step));
				GL.End();
				if(SorW)
						GL.Begin(PrimitiveType.Triangles);
				else
						GL.Begin(PrimitiveType.LineStrip);
					//底面法線
					GL.Normal3(-Vector3.UnitY);
					//底面
					GL.Vertex3(0.0, -length / 2, 0.0);
					GL.Vertex3(radius * Math.Cos(rad), -length / 2, radius * Math.Sin(rad));
					GL.Vertex3(radius * Math.Cos(rad + step), -length / 2, radius * Math.Sin(rad + step));
				GL.End();
			}
		}

		//円錐（引数：ソリッドまたはワイアー、半径、縦の高さ）
		public void Cone(bool SorW = true, double radius = 0.5, double height = 2.0)
		{
			double step = 0.1;
			for(double rad = -Math.PI; rad <= Math.PI; rad += step)
			{
				if(SorW)
					GL.Begin(PrimitiveType.Triangles);
				else
					GL.Begin(PrimitiveType.LineStrip);
					//側面法線
					Vector3 v1 = new Vector3((float)(radius * Math.Cos(rad)), (float)-(height / 2), (float)(radius * Math.Sin(rad)));
					Vector3 v2 = new Vector3((float)(radius * Math.Cos(rad + step)), (float)-(height / 2), (float)(radius * Math.Sin(rad + step)));
					GL.Normal3(Vector3.Normalize(Vector3.Cross(v1, v2)));
					//側面
					GL.Vertex3(0.0, height / 2, 0.0);
					GL.Vertex3(radius * Math.Cos(rad), -height / 2, radius * Math.Sin(rad));
					GL.Vertex3(radius * Math.Cos(rad + step), -height / 2, radius * Math.Sin(rad + step));
					//底面法線
					GL.Normal3(-Vector3.UnitY);
					//底面
					GL.Vertex3(0.0, -height / 2, 0.0);
					GL.Vertex3(radius * Math.Cos(rad), -height / 2, radius * Math.Sin(rad));
					GL.Vertex3(radius * Math.Cos(rad + step), -height / 2, radius * Math.Sin(rad + step));
				GL.End();
			}
		}
	}
}
