using System;
using System.Collections.Generic;
using System.IO;

namespace MyGeometry
{
	// Using class to define vertex (Vector3d:struct)
	public class Vertex
	{
		public Vector3d Coord;
		public Vertex(Vector3d pnt)
		{
			Coord = pnt;
		}
		public Vertex(double[] pnt)
		{
			Coord.x = pnt[0];
			Coord.y = pnt[1];
			Coord.z = pnt[2];
		}
		public Vertex(Vertex pnt)
		{
			Coord = pnt.Coord;
		}
		public Vertex(double v1, double v2, double v3)
		{
			Coord.x = v1;
			Coord.y = v2;
			Coord.z = v3;
		}
	}

	public class Plane
	{
		public List<Vertex> vertices = new List<Vertex>();
		public List<Vertex> currVertices = new List<Vertex>();
		public List<Vector3d> screenCoord = new List<Vector3d>();
		public Vector3d normal, currNormal;
		public Vector3d center, currCenter;
		public double depth;
		public MatrixNd vertexMat;
		public Vector3d[] currCoord = new Vector3d[3]; //XYZ coordinates
		public float[] color = {0.2f, 0.2f, 0.2f, 1.0f}; //color = {0.96f, 0.98f, 1.0f, 0.8f};
		public int texture;
		public int slabtype;
		public int wallInd = -1;
		public bool castVisit = false;

		public Plane(Plane plane)
		{
			for (int i = 0; i < plane.vertices.Count; ++i)
			{
				this.vertices.Add(plane.vertices[i]);
				this.currVertices.Add(plane.currVertices[i]);
			}
			this.ComputeNormal();
			// Initilize screen coordinates
			for (int i = 0; i < plane.screenCoord.Count; i++)
				this.screenCoord.Add(new Vector3d(plane.screenCoord[i]));
			// 4*N matrix, homogeneous coordinates in column
			double[,] array = new double[4, vertices.Count];
			for (int i = 0; i < plane.vertices.Count; i++)
			{
				array[0, i] = plane.vertices[i].Coord.x;
				array[1, i] = plane.vertices[i].Coord.y;
				array[2, i] = plane.vertices[i].Coord.z;
				array[3, i] = 1.0d;
			}
			vertexMat = new MatrixNd(array, 4, plane.vertices.Count);
			this.getCurrCoord();
		}
		public Plane(List<Vertex> pnts, List<Vertex> currPnts)
        {
            for (int i = 0; i < pnts.Count; ++i)
            {
                this.vertices.Add(pnts[i]);
				currVertices.Add(new Vertex(currPnts[i]));
            }
            this.ComputeNormal();
			// Initilize screen coordinates
			for (int i = 0; i < vertices.Count; i++)
				this.screenCoord.Add(new Vector3d(pnts[i].Coord.x, pnts[i].Coord.y, pnts[i].Coord.z));
			// 4*N matrix, homogeneous coordinates in column
			double[,] array = new double[4, vertices.Count];
			for (int i = 0; i < vertices.Count; i++)
			{
				array[0, i] = vertices[i].Coord.x;
				array[1, i] = vertices[i].Coord.y;
				array[2, i] = vertices[i].Coord.z;
				array[3, i] = 1.0d;
			}
			vertexMat = new MatrixNd(array, 4, vertices.Count);
			this.getCurrCoord();
        }

		public void ComputeNormal()
		{
			int mid = (int)(currVertices.Count / 2);
			center = (vertices[0].Coord + vertices[mid].Coord) / 2;
			currCenter = (currVertices[0].Coord + currVertices[mid].Coord) / 2;

			Vector3d v1 = this.vertices[1].Coord - this.vertices[0].Coord;
			Vector3d v2 = this.vertices[this.vertices.Count - 1].Coord - this.vertices[0].Coord;
			this.normal = v1.Cross(v2).Normalize();

			v1 = this.currVertices[1].Coord - this.currVertices[0].Coord;
			v2 = this.currVertices[vertices.Count - 1].Coord - this.currVertices[0].Coord;
			this.currNormal = v1.Cross(v2).Normalize();
		}

		public void UpdateVertexRelatedInfo() 
		{
			for (int i = 0; i < vertices.Count; i++)
			{
				vertexMat[0, i] = vertices[i].Coord.x;
				vertexMat[1, i] = vertices[i].Coord.y;
				vertexMat[2, i] = vertices[i].Coord.z;
				vertexMat[3, i] = 1.0d;
			}
			this.ComputeNormal();
			this.getCurrCoord();
		}
		public void UpdateVertices(List<Vertex> pnts, List<Vertex> currPnts)
		{
			for (int i = 0; i < pnts.Count; i++)
			{
				this.vertices[i] = pnts[i];
				this.currVertices[i] = currPnts[i];
			}
			this.UpdateVertexRelatedInfo();
		}

		public void UpdateCurrVertices(MatrixNd newMat)
		{
			for (int i = 0; i < vertices.Count; i++)
			{
				currVertices[i].Coord.x = newMat[0, i] / newMat[3, i];
				currVertices[i].Coord.y = newMat[1, i] / newMat[3, i];
				currVertices[i].Coord.z = newMat[2, i] / newMat[3, i];
			}
			this.ComputeNormal();
			this.getCurrCoord();
		}

		public void SetScreenCoordinate(Vector3d[] screenPos)
		{
			double sum = 0.0;
			for (int i = 0; i < vertices.Count; i++)
			{
				screenCoord[i] = screenPos[i];
				sum += screenPos[i].z;
			}
			this.depth = sum / vertices.Count;
		}

		public void getCurrCoord()
		{
			// X-Y-Z-axis unit vector			
			currCoord[0] = (currCenter - this.currVertices[0].Coord).Normalize();
			currCoord[2] = this.currNormal;
			currCoord[1] = currCoord[0].Cross(currCoord[2]).Normalize();
		}

		public void UpdateColor(float[] color)
		{
			for (int i = 0; i < 4; i++)
				this.color[i] = color[i];
		}
	}

	public class Cuboid
	{
		public List<Vertex> vertices = new List<Vertex>();
		// After translation/rotation/resize, current 3D location
		public List<Vertex> currVertices = new List<Vertex>();
		public List<Vertex> planeVertices = new List<Vertex>();  // Record the vertex change in 2D
		public List<Plane> planes = new List<Plane>();
		public List<Vertex> endPoints = new List<Vertex>();
		public Vector3d direction, normal;
		public double thickness, width, height, elevation;
		public int label, index, kind;
		public double leftThick, rightThick;
		public int wallIndex = -1, doorIndex = -1;

		public Cuboid()
		{ }

		public Cuboid(Cuboid cub)
		{
			for (int i = 0; i < cub.vertices.Count; i++)
				this.vertices.Add(new Vertex(cub.vertices[i]));
			for (int i = 0; i < cub.currVertices.Count; i++)
				this.vertices.Add(new Vertex(cub.currVertices[i]));
			for (int i = 0; i < cub.planeVertices.Count; i++)
				this.vertices.Add(new Vertex(cub.planeVertices[i]));
			this.endPoints.Add(new Vertex(cub.endPoints[0]));
			this.endPoints.Add(new Vertex(cub.endPoints[1]));
			this.direction = new Vector3d(cub.direction);
			this.normal = new Vector3d(cub.normal);
			this.thickness = cub.thickness;
			this.width = cub.width;
			this.height = cub.height;
			this.elevation = cub.elevation;
			this.label = cub.label;
			this.index = cub.index;
			this.wallIndex = cub.wallIndex;
			this.doorIndex = cub.doorIndex;
		}
		public Cuboid(int label, int index, Vertex pnt1, Vertex pnt2, List<Vertex> vertices,
			double thickness, double height, double elevation, Vector3d direction, Vector3d normal, Matrix4d mat)
		{
			// label = 1: wall; label = 2: door/window
			// index: the index in its class
			int i;
			Vector3d vec;
			endPoints.Add(pnt1);
			endPoints.Add(pnt2);
			this.thickness = thickness;
			this.leftThick = thickness;
			this.rightThick = thickness;
			this.height = height;
			this.elevation = elevation;
			this.label = label;
			this.width = (endPoints[1].Coord - endPoints[0].Coord).Length();
			if (label == 1)
				this.wallIndex = index;
			else if (label == 2)
				this.doorIndex = index;

			this.direction = direction;
			this.normal = normal;
			
			for (i = 0; i < vertices.Count; i++)
			{
				this.vertices.Add(vertices[i]);
				this.vertices[i].Coord.z = elevation;
				this.planeVertices.Add(new Vertex(vertices[i]));
			}
			SetUpperVertices();
			for (i = 0; i < this.vertices.Count; i++)
			{
				vec = (mat * new Vector4d(this.vertices[i].Coord, 1)).ToVector3d();
				currVertices.Add(new Vertex(vec));
			}
			this.InitialzePlanes();
		}

		public void UpdatePlaneVertices()
		{
			int i;
			vertices = new List<Vertex>();
			for (i = 0; i < planeVertices.Count; i++)
				vertices.Add(new Vertex(planeVertices[i]));
			SetUpperVertices();
			currVertices = new List<Vertex>();
			for (i = 0; i < this.vertices.Count; i++)
				currVertices.Add(new Vertex(vertices[i]));
			this.InitialzePlanes();
		}
			

		// Given two end points, thickenss and height to get a cuboid
		// Num of vertex must be 4*2
		public Cuboid(int kind, int label, int index, Vertex pnt1, Vertex pnt2, 
			double thickness, double height, double elevation, Vector3d direction, Vector3d normal, Matrix4d mat)
		{
			// label: which kind of cuboid: wall, door?
			// index: the index in its class
			int i;
			Vector3d vec;
			endPoints.Add(pnt1);
			endPoints.Add(pnt2);
			this.thickness = thickness;
			this.leftThick = thickness;
			this.rightThick = thickness;
			this.height = height;
			this.elevation = elevation;
			this.index = index;
			this.kind = kind;
			this.label = label;
			this.width = (endPoints[1].Coord - endPoints[0].Coord).Length();

			if (!direction.IsNull())
				this.direction = direction.Normalize();
			else
				this.direction = (pnt2.Coord - pnt1.Coord).Normalize();
			this.normal = normal.Normalize();
			//normal = new Vector3d(-1.0 * this.direction.y, this.direction.x, 0.0d);
			for (i = 0; i < 8; i++)
				vertices.Add(new Vertex(0.0d, 0.0d, 0.0d));
			SetBaseVertices();
			if (label == 1)
				SetUpperVertices();
			else if (label == 2)
				SetUpperVerticesDoor();
			for (i = 0; i < vertices.Count; i++)
			{
				vec = (mat * new Vector4d(vertices[i].Coord, 1)).ToVector3d();
				currVertices.Add(new Vertex(vec));
			}
			this.InitialzePlanes();
		}

		public void SetBaseVertices()
		{
			Vector3d vec;
			double[] vertex = new double[3];
			double halfThick = thickness / 2;
			double halfLeftThick = leftThick / 2;
			double halfRightThick = rightThick / 2;
			this.width = (endPoints[1].Coord - endPoints[0].Coord).Length();
			//kind: 1, normal wall, 2, the first wall by split, 3, the second wall by split 4, the middle wall by split
			// Normal wall
			vec = endPoints[0].Coord - halfThick * normal;
			vec -= direction * halfLeftThick;
			vertices[0].Coord = vec;
			vec = endPoints[0].Coord + halfThick * normal;
			vec -= direction * halfLeftThick;
			vertices[1].Coord = vec;
			vec = endPoints[1].Coord + halfThick * normal;
			vec += direction * halfRightThick;
			vertices[2].Coord = vec;
			vec = endPoints[1].Coord - halfThick * normal;
			vec += direction * halfRightThick;
			vertices[3].Coord = vec;
		}

		public void SetUpperVertices()
		{
			int n = vertices.Count;
			for (int i = 0; i < n; i++)
			{
				vertices.Add(new Vertex(vertices[i].Coord.x,
					vertices[i].Coord.y, vertices[i].Coord.z + height));
			}
		}

		public void SetUpperVerticesDoor()
		{
			for (int i = 0; i < 4; i++)
			{
				vertices[i + 4].Coord = vertices[i].Coord;
				vertices[i].Coord.z = elevation;
				vertices[i + 4].Coord.z = height + elevation;
			}
		}

		public void UpdateUpperVertices()
		{
			int n = vertices.Count / 2;
			for (int i = 0; i < n; i++)
			{
				vertices[i + n].Coord.x = vertices[i].Coord.x;
				vertices[i + n].Coord.y = vertices[i].Coord.y;
				vertices[i + n].Coord.z = vertices[i].Coord.z + height;
			}
		}

		public void UpdateVertices(int leftRight, int upDown)
		{
			double halfThick = thickness / 2;
			double halfLeftThick = leftThick / 2;
			double halfRightThick = rightThick / 2;
			if (leftRight == 0) // right side, update vertices[0] & [1]
			{
				if (upDown == 0) // Down
				{
					vertices[0].Coord = endPoints[0].Coord - halfThick * normal;
					vertices[0].Coord += direction * halfLeftThick;
					vertices[1].Coord = endPoints[0].Coord + halfThick * normal;
					vertices[1].Coord -= direction * halfLeftThick;
				}
				else if (upDown == 1)
				{
					vertices[0].Coord = endPoints[0].Coord - halfThick * normal;
					vertices[0].Coord += direction * halfLeftThick;
					vertices[1].Coord = endPoints[0].Coord + halfThick * normal;
					vertices[1].Coord -= direction * halfLeftThick;
				}
			}
			else
			{
				if (upDown == 0)
				{
					vertices[2].Coord = endPoints[1].Coord + halfThick * normal;
					vertices[2].Coord += direction * halfRightThick;
					vertices[3].Coord = endPoints[1].Coord - halfThick * normal;
					vertices[3].Coord -= direction * halfRightThick;
				}
				else if (upDown == 1)
				{
					vertices[2].Coord = endPoints[1].Coord + halfThick * normal;
					vertices[2].Coord -= direction * halfRightThick;
					vertices[3].Coord = endPoints[1].Coord - halfThick * normal;
					vertices[3].Coord += direction * halfRightThick;
				}
			}
			
		}

		public void InitialzePlanes()
		{
			//口lower left: 0, upper left: 1, upper right: 2, lower right: 3; (Plane 0)
			//4-7 built from 0-3 (Plane 1); 0154 (P2), 1265 (P3), 2376(P4), 3047 (P5)

			List<Vertex> verts = new List<Vertex>();
			List<Vertex> currVerts = new List<Vertex>();
			int i, num = (int)(vertices.Count/2);
			planes = new List<Plane>();
			for (i = 0; i < num; i++)
			{
				verts.Add(vertices[i]);
				currVerts.Add(currVertices[i]);
			}
			this.planes.Add(new Plane(verts, currVerts));
			verts = new List<Vertex>();
			currVerts = new List<Vertex>();
			for (i = 0; i < num; i++)
			{
				verts.Add(vertices[i + num]);
				currVerts.Add(currVertices[i + num]);
			}
			this.planes.Add(new Plane(verts, currVerts));
			for (i = 0; i < num; i++)
			{
				int[] currIndex = { i, (i + 1) % num, num + (i + 1) % num, num + i };
				//int[] currIndex = { i, num + i, num + (i + 1) % num,(i + 1) % num };
				verts = new List<Vertex>();
				currVerts = new List<Vertex>();
				for (int j = 0; j < 4; j++)
				{
					verts.Add(vertices[currIndex[j]]);
					currVerts.Add(currVertices[currIndex[j]]);
				}
				this.planes.Add(new Plane(verts, currVerts));
			}
			//planes[2].color[3] = 0.0f;
			//planes[4].color[3] = 0.0f;
		}

		public void UpdateHeight(double newHeight, Matrix4d mat)
		{
			int i, half = (int)(vertices.Count / 2);
			for (i = half; i < vertices.Count; i++)
				vertices[i].Coord.z = newHeight + elevation;
			this.height = newHeight;
			UpdateCurrVertices(mat);
			UpdateDirection();
			for (i = 1; i < planes.Count; i++)
				planes[i].UpdateVertexRelatedInfo();
		}

		public void UpdateThickness(double newThickness, double leftThick, double rightThick, Matrix4d mat)
		{
			this.thickness = newThickness;
			if (kind != 0)
			{
				this.endPoints[0].Coord = (this.planes[0].vertices[0].Coord
					- normal * (newThickness - thickness) / 2 +
					this.planes[0].vertices[1].Coord
					+ normal * (newThickness - thickness) / 2) / 2;
				this.endPoints[1].Coord = (this.planes[0].vertices[2].Coord
					+ normal * (newThickness - thickness) / 2 +
					this.planes[0].vertices[3].Coord
					- normal * (newThickness - thickness) / 2) / 2;
			}
			this.leftThick = leftThick;
			this.rightThick = rightThick;
			SetBaseVertices();
			if (label == 1)
				SetUpperVertices();
			else if (label == 2)
				SetUpperVerticesDoor();
			for (int i = 0; i < planes.Count; i++)
				this.planes[i].UpdateVertexRelatedInfo();
		}

		public void UpdateWidth(double newWidth, Matrix4d mat)
		{
			double addWidth = (newWidth - this.width) / 2;
			this.width = newWidth;
			endPoints[0].Coord -= addWidth * direction;
			endPoints[1].Coord += addWidth * direction;
			SetBaseVertices();
			if (label == 1)
				SetUpperVertices();
			else if (label == 2)
				SetUpperVerticesDoor();
			for (int i = 0; i < planes.Count; i++)
				this.planes[i].UpdateVertexRelatedInfo();
		}

		public void UpdateElevation(double newElevation, Matrix4d mat)
		{
			for (int i = 0; i < vertices.Count; i++)
			{
				vertices[i].Coord.z += newElevation - this.elevation;
			}
			this.endPoints[0].Coord.z += newElevation - this.elevation;
			this.endPoints[1].Coord.z += newElevation - this.elevation;
			this.elevation = newElevation;
			UpdateCurrVertices(mat);
			UpdateDirection();
			for (int i = 0; i < planes.Count; i++)
				this.planes[i].UpdateVertexRelatedInfo();
		}

		public void UpdatePlane()
		{
			for (int i = 0; i < planes.Count; i++)
				planes[i].UpdateVertexRelatedInfo();
		}

		public void UpdateCurrVertices(Matrix4d mat)
		{ 
			Vector3d vec;
			for (int i = 0; i < vertices.Count; i++)
			{
				vec = (mat * new Vector4d(vertices[i].Coord, 1)).ToVector3d();
				currVertices[i].Coord = vec;
			}
		}
		public void UpdateDirection()
		{
			this.direction = (endPoints[1].Coord - endPoints[0].Coord).Normalize();
			this.normal.x = -1.0 * this.direction.y;
			this.normal.y = this.direction.x;
		}
	}

	public class Wall
	{
		public List<Vertex> endPoints = new List<Vertex>();
		public List<Vertex> vertices = new List<Vertex>();
		public List<Vertex> currVertices = new List<Vertex>();
		public Cuboid cuboid;
		public List<Door> doors = new List<Door>();
		public int label = 1; // label:1 wall of a room; label:2 single wall
		public int texture;
		public int tag; //1
		public int num_front_cast = 0;
		public double width, height, elevation;
		public List<Plane> casts = new List<Plane>();
		public List<int> castType = new List<int>(); // record every cast type to draw different colors

		public List<Vector3d> castVerts = new List<Vector3d>();
		public List<List<Plane>> castPlanes = new List<List<Plane>>();
		public int[] slabType;
		public List<int> roomIndices;
		public int type; // walls of four directions 0口2
        public int index, cuboidIndex;
		public Vector3d direction, normal;
		public bool[] visit_h = {false, false}, visit_v = {false, false}; 
		//public bool special = false; // middle wall added (direction reversed endpoint 0 and 1)
		public bool[] special = {false, false};
		public bool open = false;
		public int[] vertexType = new int[2]; //1: two vertices at the endpoints; 2: 3 vertices
		// Given two end points, initialize the wall structure

		public Wall(Wall wall)
		{
			this.endPoints.Add(new Vertex(wall.endPoints[0]));
			this.endPoints.Add(new Vertex(wall.endPoints[1]));
			this.cuboid = new Cuboid(wall.cuboid);
			this.tag = wall.tag;
			this.width = wall.width;
			this.height = wall.height;
			this.elevation = wall.elevation;
			this.index = wall.index;
			this.open = wall.open;
			this.cuboidIndex = wall.cuboidIndex;
			for (int i = 0; i < wall.doors.Count; i++)
				this.doors.Add(new Door(wall.doors[i]));
			this.roomIndices = new List<int>();
			for (int i = 0; i < wall.roomIndices.Count; i++)
				this.roomIndices.Add(wall.roomIndices[i]);

		}
		public Wall(Vertex pnt1, Vertex pnt2)
		{
			this.endPoints.Add(pnt1);
			this.endPoints.Add(pnt2);
			GetNormal();
			roomIndices = new List<int>();
			GetWallType();
			cuboid = new Cuboid();
		}

		public Wall(Vertex pnt1, Vertex pnt2, int tag, int kind, int index, int label, double thickness, double height, double elevation, Vector3d direction, Matrix4d mat)
		{
			// kind = 1: normal wall
			// kind = 2: the middle wall produced by split
			// kind = 6: single wall
			this.endPoints.Add(pnt1);
			this.endPoints.Add(pnt2);
			cuboid = new Cuboid(kind, 1, index, pnt1, pnt2, thickness, height, elevation, direction, normal, mat);
			this.label = label;
			this.tag = tag;
			for (int i = 0; i < 3; i++)
				this.slabType[i] = 0;
		}

		public void GetWallType()
		{
			// Four types of walls (clockwise): 口
			// from left vertical wall to below horizontal wall
			Vertex p0 = endPoints[0], p1 = endPoints[1];
			double thresh = 1e-6;
			if (Math.Abs(p0.Coord.x - p1.Coord.x) < thresh)
			{
				if (p0.Coord.y < p1.Coord.y)
					type = 0;
				else if (p0.Coord.y > p1.Coord.y)
					type = 2;
			}
			else if (Math.Abs(p0.Coord.y - p1.Coord.y) < thresh)
			{
				if (p0.Coord.x < p1.Coord.x)
					type = 1;
				else if (p0.Coord.x > p1.Coord.x)
					type = 3;
			}
		}

		public void GetNormal() // For initialzation of walls
		{
			this.direction = endPoints[1].Coord - endPoints[0].Coord;
			this.direction = direction.Normalize();
			// Make the normals keep the same direction corresponding to the wall (clock-wise)
			if (Math.Abs(endPoints[1].Coord.x - endPoints[0].Coord.x) < 1e-6)
			{
				if (endPoints[1].Coord.y > endPoints[0].Coord.y)
					this.normal = new Vector3d(-1, 0, 0);
				else
					this.normal = new Vector3d(1, 0, 0);
			}
			else
			{
				if (endPoints[1].Coord.x > endPoints[0].Coord.x)
					this.normal = new Vector3d(0, 1, 0);
				else
					this.normal = new Vector3d(0, -1, 0);
			}
		}
		public void UpdateWall(Matrix4d mat)
		{
			//cuboid.SetBaseVertices();
			//cuboid.SetUpperVertices();
			cuboid.UpdateUpperVertices();
			cuboid.UpdateCurrVertices(mat);
			cuboid.UpdatePlane();
		}
	}

	public class Room
	{
		public List<Vertex> endPoints = new List<Vertex>();
		public List<Vertex> currEndPoints = new List<Vertex>();
		public Floor floor;
		public List<Polygon> tiles = new List<Polygon>();
		public List<Wall> walls = new List<Wall>();
		public List<bool> wallType; // wallType = true: normal walls; wallType = false: open wall
		public double weight = 0;
		public List<int> wallIndices;
		public List<Vertex> vertices = new List<Vertex>();
		// A room is a rectangle defined by a center 
		public string name;
		public Vector2d center;
		public List<Vector2d> vertex = new List<Vector2d>();
		public double area, length, width, asratio;
		public List<int> conns = new List<int>();
		public List<int> types = new List<int>();
		public Vector2d[] rect = new Vector2d[4];

		public Room()
		{ }

		public Room(String name, Vector2d center, double len, double wid, List<int> conns, List<int> types)
		{
			Vector2d x = new Vector2d(1, 0),
				y = new Vector2d(0, 1);
			this.name = name;
			this.center = center;
			this.length = len;
			this.width = wid;
			this.asratio = len / wid;
			this.area = len * wid;
			this.vertex.Add(new Vector2d(center.x + wid / 2, center.y - len / 2));
			this.vertex.Add(new Vector2d(center.x + wid / 2, center.y + len / 2));
			this.vertex.Add(new Vector2d(center.x - wid / 2, center.y + len / 2));
			this.vertex.Add(new Vector2d(center.x - wid / 2, center.y - len / 2));
			this.conns = conns;
			this.types = types;
		}

		public void UpdateVertex()
		{
			this.vertex[0] = new Vector2d(center.x + length / 2, center.y - width / 2);
			this.vertex[1] = new Vector2d(center.x + length / 2, center.y + width / 2);
			this.vertex[2] = new Vector2d(center.x - length / 2, center.y + width / 2);
			this.vertex[3] = new Vector2d(center.x - length / 2, center.y - width / 2);
		}

		public Room(Room room)
		{
			this.center = room.center;
			this.name = room.name;
			this.length = room.length;
			this.width = room.width;
			this.asratio = room.asratio;
			this.area = room.area;
			this.vertex = new List<Vector2d>();
			for (int i = 0; i < room.vertex.Count; i++)
				this.vertex.Add(new Vector2d(room.vertex[i]));
			this.conns = room.conns;
			this.types = room.types;
		}

		public Room(List<Vertex> pnts)
		{
			for (int i = 0; i < pnts.Count; i++)
				vertices.Add(pnts[i]);
		}

		public Room(List<Vertex> pnts, List<Vertex> currPnts, double thickness, double height, double elevation, Matrix4d mat)
		{
			int i;
			for (i = 0; i < pnts.Count; i++)
			{
				endPoints.Add(pnts[i]);
				currEndPoints.Add(currPnts[i]);
			}
		}
		// Update neighbor walls 
		public void UpdateRoom(int ind, Matrix4d mat)
		{
			int left = (ind - 1 + walls.Count) % walls.Count;
			int right = (ind + 1) % walls.Count;
			int lleft = (ind - 2 + walls.Count) % walls.Count;
			int rright = (ind + 2) % walls.Count;
			UpdateTheWall(ind, mat);
			if (walls[(lleft - 1 + walls.Count) % walls.Count].tag == 2)
			{
				if (walls[(lleft - 1 + walls.Count) % walls.Count].cuboid.kind == 2)
				{
					if (walls[lleft].cuboid.kind == 1)
					{
						walls[lleft].cuboid.kind = 3;
						walls[lleft].UpdateWall(mat);
					}
					else if (walls[lleft].cuboid.kind == 2)
					{
						walls[lleft].cuboid.kind = 5;
						walls[lleft].UpdateWall(mat);
					}
				}
			}

			if (walls[(rright+1)%walls.Count].tag == 2)
			{
				if (walls[(rright+1) % walls.Count].cuboid.kind == 3)
				{
					if (walls[rright].cuboid.kind == 3)
					{
						walls[rright].cuboid.kind = 5;
						walls[rright].UpdateWall(mat);
					}
					else if (walls[rright].cuboid.kind == 1)
					{
						walls[rright].cuboid.kind = 2;
						walls[rright].UpdateWall(mat);
					}
				}
			}
		}

		public void UpdateTheWall(int ind, Matrix4d mat)
		{
			walls[ind].UpdateWall(mat);
			int left = (ind - 1 + walls.Count) % walls.Count;
			int right = (ind + 1) % walls.Count;
			double c1 = 0, c2 = 0, c3 = 0;

			if (walls[right].tag == 2 && walls[left].tag == 2)
			{
				if (walls[ind].cuboid.normal.y != 0)
				{
					if (walls[ind].cuboid.normal.y == 1)
					{
						c1 = walls[ind].endPoints[0].Coord.y;
						c2 = walls[(right + 1) % walls.Count].endPoints[0].Coord.y;
						c3 = walls[(left-1+walls.Count) % walls.Count].endPoints[1].Coord.y;
					}
					else
					{
						c1 = -walls[ind].endPoints[0].Coord.y;
						c2 = -walls[(right + 1) % walls.Count].endPoints[0].Coord.y;
						c3 = -walls[(left - 1 + walls.Count) % walls.Count].endPoints[1].Coord.y;
					}
				}
				else
				{
					if (walls[ind].cuboid.normal.x == 1)
					{
						c1 = walls[ind].endPoints[0].Coord.x;
						c2 = walls[(right + 1) % walls.Count].endPoints[0].Coord.x;
						c3 = walls[(left - 1 + walls.Count) % walls.Count].endPoints[1].Coord.x;
					}
					else
					{
						c1 = -walls[ind].endPoints[0].Coord.x;
						c2 = -walls[(right + 1) % walls.Count].endPoints[0].Coord.x;
						c3 = -walls[(left - 1 + walls.Count) % walls.Count].endPoints[1].Coord.x;
					}
				}

				if (c1 > c2 && c1 > c3)
				{
					walls[right].cuboid.kind = 2;
					walls[left].cuboid.kind = 3;
					walls[ind].cuboid.kind = 1;
					walls[(right + 1) % walls.Count].cuboid.kind = 3;
					walls[(left - 1 + walls.Count) % walls.Count].cuboid.kind = 2;
					walls[right].UpdateWall(mat);
					walls[ind].UpdateWall(mat);
					walls[(right + 1) % walls.Count].UpdateWall(mat);
					walls[right].cuboid.UpdateDirection();
					walls[left].UpdateWall(mat);
					walls[(left - 1 + walls.Count) % walls.Count].UpdateWall(mat);
				}
				else if (c1 < c2 && c1 < c3)
				{
					walls[right].cuboid.kind = 3;
					walls[left].cuboid.kind = 2;
					walls[ind].cuboid.kind = 5;
					walls[(right + 1) % walls.Count].cuboid.kind = 1;
					walls[(left - 1 + walls.Count) % walls.Count].cuboid.kind = 1;
					walls[right].UpdateWall(mat);
					walls[ind].UpdateWall(mat);
					walls[(right + 1) % walls.Count].UpdateWall(mat);
					walls[right].cuboid.UpdateDirection();
					walls[left].UpdateWall(mat);
					walls[(left - 1 + walls.Count) % walls.Count].UpdateWall(mat);
				}
				else if (c1 > c2 && c1 < c3)
				{
					walls[right].cuboid.kind = 2;
					walls[left].cuboid.kind = 2;
					walls[ind].cuboid.kind = 3;
					walls[(right + 1) % walls.Count].cuboid.kind = 5;
					if (walls[(right + 2) % walls.Count].cuboid.kind == 1 || walls[(right + 2) % walls.Count].cuboid.kind == 2)
						walls[(right + 1) % walls.Count].cuboid.kind = 3;
					walls[(left - 1 + walls.Count) % walls.Count].cuboid.kind = 1;
					walls[right].UpdateWall(mat);
					walls[ind].UpdateWall(mat);
					walls[(right + 1) % walls.Count].UpdateWall(mat);
					walls[right].cuboid.UpdateDirection();
					walls[left].cuboid.UpdateDirection();
					walls[left].UpdateWall(mat);
					walls[(left - 1 + walls.Count) % walls.Count].UpdateWall(mat);
				}
				else if (c1 < c2 && c1 > c3)
				{
					walls[right].cuboid.kind = 3;
					walls[left].cuboid.kind = 3;
					walls[ind].cuboid.kind = 2;
					walls[(right + 1) % walls.Count].cuboid.kind = 1;
					walls[(left - 1 + walls.Count) % walls.Count].cuboid.kind = 2;
					walls[right].UpdateWall(mat);
					walls[ind].UpdateWall(mat);
					walls[(right + 1) % walls.Count].UpdateWall(mat);
					walls[right].cuboid.UpdateDirection();
					walls[left].cuboid.UpdateDirection();
					walls[left].UpdateWall(mat);
					walls[(left - 1 + walls.Count) % walls.Count].UpdateWall(mat);
				}
			}
			else if (walls[right].tag == 2)
			{
				if (walls[ind].cuboid.normal.y != 0)
				{
					if (walls[ind].cuboid.normal.y == 1)
					{
						c1 = walls[ind].endPoints[0].Coord.y;
						c2 = walls[(right + 1) % walls.Count].endPoints[1].Coord.y;
					}
					else
					{
						c1 = -walls[ind].endPoints[0].Coord.y;
						c2 = -walls[(right + 1) % walls.Count].endPoints[1].Coord.y;
					}
				}
				else
				{
					if (walls[ind].cuboid.normal.x == 1)
					{
						c1 = walls[ind].endPoints[0].Coord.x;
						c2 = walls[(right + 1) % walls.Count].endPoints[1].Coord.x;
					}
					else
					{
						c1 = -walls[ind].endPoints[0].Coord.x;
						c2 = -walls[(right + 1) % walls.Count].endPoints[1].Coord.x;
					}
				}

				if (c1 > c2)
				{
					walls[right].cuboid.kind = 2;
					walls[ind].cuboid.kind = 1;
					walls[(right + 1) % walls.Count].cuboid.kind = 3;
					walls[right].UpdateWall(mat);
					walls[ind].UpdateWall(mat);
					walls[(right + 1) % walls.Count].UpdateWall(mat);
					walls[right].cuboid.UpdateDirection();
					walls[left].cuboid.UpdateDirection();
					walls[left].UpdateWall(mat);
				}
				else 
				{
					walls[right].cuboid.kind = 3;
					walls[ind].cuboid.kind = 2;
					walls[(right + 1) % walls.Count].cuboid.kind = 1;
					walls[right].UpdateWall(mat);
					walls[ind].UpdateWall(mat);
					walls[(right + 1) % walls.Count].UpdateWall(mat);
					walls[right].cuboid.UpdateDirection();
					walls[left].cuboid.UpdateDirection();
					walls[left].UpdateWall(mat);
				}
			}
			else if (walls[left].tag == 2)
			{
				if (walls[ind].cuboid.normal.y != 0)
				{
					if (walls[ind].cuboid.normal.y == 1)
					{
						c1 = walls[ind].endPoints[0].Coord.y;
						c2 = walls[(left - 1 + walls.Count) % walls.Count].endPoints[1].Coord.y;
					}
					else
					{
						c1 = -walls[ind].endPoints[0].Coord.y;
						c2 = -walls[(left - 1 + walls.Count) % walls.Count].endPoints[1].Coord.y;
					}
				}
				else
				{
					if (walls[ind].cuboid.normal.x == 1)
					{
						c1 = walls[ind].endPoints[0].Coord.x;
						c2 = walls[(left - 1 + walls.Count) % walls.Count].endPoints[1].Coord.x;
					}
					else
					{
						c1 = -walls[ind].endPoints[0].Coord.x;
						c2 = -walls[(left - 1 + walls.Count) % walls.Count].endPoints[1].Coord.x;
					}
				}

				if (c1 < c2)
				{
					walls[left].cuboid.kind = 2;
					walls[ind].cuboid.kind = 3;
					walls[(left - 1 + walls.Count) % walls.Count].cuboid.kind = 1;
					walls[left].UpdateWall(mat);
					walls[ind].UpdateWall(mat);
					walls[(left - 1 + walls.Count) % walls.Count].UpdateWall(mat);
					walls[left].cuboid.UpdateDirection();
					walls[right].cuboid.UpdateDirection();
					walls[right].UpdateWall(mat);
				}
				else
				{
					walls[left].cuboid.kind = 3;
					walls[ind].cuboid.kind = 1;
					walls[(left - 1 + walls.Count) % walls.Count].cuboid.kind = 2;
					walls[left].UpdateWall(mat);
					walls[ind].UpdateWall(mat);
					walls[(left - 1 + walls.Count) % walls.Count].UpdateWall(mat);
					walls[left].cuboid.UpdateDirection();
					walls[right].cuboid.UpdateDirection();
					walls[right].UpdateWall(mat);
				}
			}
			else
			{
				walls[left].UpdateWall(mat);
				walls[right].UpdateWall(mat);
			}
		}

	}

	public class Door
	{
		public double width = 0.06;
		public double height = 0.24;
		public double elevation = 0.0;
		private double thickness;
		public Vector3d location;
		public int belong, index;
		public List<Vertex> vertices = new List<Vertex>();
		public List<Vertex> currVertices = new List<Vertex>();
		public List<Vertex> endPoints = new List<Vertex>();
		public Cuboid cuboid;
		public int texture;
		public int kind = 0;

		public Door()
		{ 
		}
		public Door(Door door)
		{
			this.thickness = door.thickness;
			this.index = door.index;
			this.belong = door.belong;
			this.cuboid = new Cuboid(door.cuboid);
			for (int i = 0; i < vertices.Count; i++)
			{
				this.currVertices.Add(new Vertex(door.currVertices[i]));
				this.vertices.Add(new Vertex(door.vertices[i]));
			}
			this.endPoints.Add(new Vertex(door.endPoints[0]));
			this.endPoints.Add(new Vertex(door.endPoints[1]));
		}
		public Door(Vector3d locate, Wall wall, int belong, int index, Matrix4d mat)
		{
			double dist;
			Vector3d direction = wall.cuboid.direction;
			this.index = index;
			this.belong = belong;
			this.thickness = wall.cuboid.thickness;// +1e-4;
			dist = (locate - wall.endPoints[0].Coord).Dot(direction);
			endPoints.Add(new Vertex(wall.endPoints[0].Coord + dist * direction));
			endPoints.Add(new Vertex(endPoints[0].Coord + (width-thickness) * direction));
			cuboid = new Cuboid(0, 2, index, endPoints[0], endPoints[1], thickness, height, elevation, 
				new Vector3d(0, 0, 0), new Vector3d(0, 0, 0), mat);
			float[] color = {0.8f, 1.0f, 1.0f, 1.0f};
			for (int i = 0; i < cuboid.planes.Count; i++)
			{
				Plane plane = cuboid.planes[i];
				plane.UpdateColor(color);
			}
		}

		public Door(double dist, Wall wall, int belong, int index, Matrix4d mat, double wid)
		{
			Vector3d direction = wall.direction;
			this.index = index;
			this.belong = belong;
			this.thickness = wall.cuboid.thickness;// +1e-4;
			endPoints.Add(new Vertex(wall.endPoints[0].Coord + dist * direction));
			if (wid > 0) width = wid;
			endPoints.Add(new Vertex(endPoints[0].Coord + (width - thickness) * direction));
			cuboid = new Cuboid(0, 2, index, endPoints[0], endPoints[1], 
				thickness, height, elevation, wall.direction, wall.normal, mat);
			float[] color = { 0.8f, 1.0f, 1.0f, 1.0f };
			for (int i = 0; i < cuboid.planes.Count; i++)
			{
				Plane plane = cuboid.planes[i];
				plane.UpdateColor(color);
			}
		}
		// for layout 5
		public Door(double dist, Wall wall, int belong, int index, Matrix4d mat, double wid, int ends)
		{
			Vector3d direction = wall.direction;
			this.index = index;
			this.belong = belong;
			this.thickness = wall.cuboid.thickness;// +1e-4;
			endPoints.Add(new Vertex(wall.endPoints[0].Coord + dist * direction));
			if (wid > 0) width = wid;
			endPoints.Add(new Vertex(endPoints[0].Coord + (width - thickness) * direction));
			cuboid = new Cuboid(0, 2, index, endPoints[0], endPoints[1],
				thickness, height, elevation, wall.direction, wall.normal, mat);
			float[] color = { 0.8f, 1.0f, 1.0f, 1.0f };
			for (int i = 0; i < cuboid.planes.Count; i++)
			{
				Plane plane = cuboid.planes[i];
				plane.UpdateColor(color);
			}
		}

		public Door(double dist, Wall wall, int belong, int index, Matrix4d mat, double width, double height, double elevation)
		{
			Vector3d direction = wall.direction;
			this.index = index;
			this.belong = belong;
			this.thickness = wall.cuboid.thickness;// +1e-4;
			endPoints.Add(new Vertex(wall.endPoints[0].Coord + dist * direction));
			endPoints.Add(new Vertex(endPoints[0].Coord + (width - thickness) * direction));
			endPoints[0].Coord.z = elevation;
			endPoints[1].Coord.z = elevation;
			this.width = width;
			this.height = height;
			this.elevation = elevation;
			cuboid = new Cuboid(0, 2, index, endPoints[0], endPoints[1],
				thickness, height, elevation, wall.direction, wall.normal, mat);
			float[] color = { 0.8f, 1.0f, 1.0f, 1.0f };
			for (int i = 0; i < cuboid.planes.Count; i++)
			{
				Plane plane = cuboid.planes[i];
				plane.UpdateColor(color);
			}
		}

		public void UpdateDoor(Matrix4d mat)
		{
			cuboid.SetBaseVertices();
			cuboid.SetUpperVerticesDoor();
			cuboid.UpdateCurrVertices(mat);
			cuboid.UpdatePlane();
			float[] color = {0.8f, 1.0f, 1.0f, 1.0f};
		}
	}

	public class Floor
	{
		public List<Vertex> vertices = new List<Vertex>();
		public List<Vertex> currVertices = new List<Vertex>();
		public Plane plane;
		public float[] color = { 0.8f, 0.5f, 0.3f, 0.8f };

		public Floor(Room room, Matrix4d mat)
		{
			int i;
			Vector3d vec;
			Cuboid cuboid;
			int num = room.walls.Count;
			vertices = new List<Vertex>();
			currVertices = new List<Vertex>();
			for (i = 0; i < room.walls.Count; i++)
			{
				cuboid = room.walls[i].cuboid;
				vec = cuboid.vertices[0].Coord;
				//thick = room.walls[(i - 1 + num) % num].cuboid.thickness;
				//vec = cuboid.endPoints[0].Coord - cuboid.normal * cuboid.thickness / 2 + cuboid.direction * thick / 2;
				vertices.Add(new Vertex(vec));
			}
			for (i = 0; i < vertices.Count; i++)
			{
				vec = (mat * new Vector4d(vertices[i].Coord, 1)).ToVector3d();
				currVertices.Add(new Vertex(vec));
			}
			plane = new Plane(vertices, currVertices);
			plane.UpdateColor(color);
		}

		public void UpdateFloor(Room room, Matrix4d mat)
		{
			int i;
			Vector3d vec;
			Cuboid cuboid;
			double thick;
			//int num = room.walls.Count;
            int num = 4;
			for (i = 0; i < num; i++)
			{
				cuboid = room.walls[i].cuboid;
				thick = room.walls[(i - 1 + num) % num].cuboid.thickness;
				vec = cuboid.endPoints[0].Coord - cuboid.normal * cuboid.thickness/2 + cuboid.direction * thick/2;
				vertices[i].Coord = vec;
			}
			for (i = 0; i < vertices.Count; i++)
			{
				vec = (mat * new Vector4d(vertices[i].Coord, 1)).ToVector3d();
				currVertices[i].Coord = vec;
			}
		}
		public void UpdateCurrVertices(Matrix4d mat)
		{
			currVertices = new List<Vertex>();
			Vector3d vec;
			for (int i = 0; i < vertices.Count; i++)
			{
				vec = (mat * new Vector4d(vertices[i].Coord, 1)).ToVector3d();
				currVertices.Add(new Vertex(vec));
			}
		}
	}

	public class Line
	{
		public Vector3d[] vertices = new Vector3d[2];
		public Line(Vector3d pnt1, Vector3d pnt2)
		{
			vertices[0] = pnt1;
			vertices[1] = pnt2;
		}
	}

	public class Polygon
	{
		public List<Vertex> vertices = new List<Vertex>();
		public List<Line> lines = new List<Line>();
		public Polygon(List<Vertex> pnts)
		{
			int i;
			for (i = 0; i < pnts.Count; i++)
				vertices.Add(new Vertex(pnts[i]));
			for (i = 0; i < pnts.Count; i++)
				lines.Add(new Line(vertices[i].Coord, vertices[(i + 1) % vertices.Count].Coord));
		}
	}

}
