#region Using directives

using System;
using System.Collections;
using System.Drawing;

using OpenTK;
using OpenTK.Graphics;
using OpenTK.Math;
using OpenTK.Input;
using OpenTK.Platform;

#endregion

namespace DataProcessing
{
    public class Mesh
    {
        #region variable definitions

        private ArrayList vertices;         //kolekce bodu
        private ArrayList houghVertices;    //kolekce bodu pro Houghovu transformaci
        private ArrayList lines;            //kolekce car
        private ArrayList surfaces;         //kolekce ploch
        private ArrayList segments;         //kolekce segmentu

        private HoughTransformation meshHough;  //Houghova transformace
        private Octree meshOctree;              //Octree

        private float positionX = 0, positionY = 0, positionZ = 0;                          //aktualni pozice
        private float currentRotationX = 0, currentRotationY = 0, currentRotationZ = 0;     //aktualni rotace
        private float previousRotationX = 0, previousRotationY = 0, previousRotationZ = 0;  //predchozi rotace

        private int rows;                                   //pocet naskenovanych radku                                  
        private float minX, maxX, minY, maxY, minZ, maxZ;   //maximalni rozmery

        private bool isSelected;                            //je vybran uzivatelem?
        private bool showReducedVerts = true;               //zobrazovat redukovane body

        private Vector3 vertexColor = new Vector3(0.1f, 0.1f, 0.1f);

        public ArrayList Vertices
        {
            get { return vertices; }
            set { vertices = value; }
        }

        public ArrayList HoughVertices
        {
            get { return houghVertices; }
            set { houghVertices = value; }
        }

        public ArrayList Lines
        {
            get { return lines; }
            set { lines = value; }
        }

        public ArrayList Surfaces
        {
            get { return surfaces; }
            set { surfaces = value; }
        }

        public ArrayList Segments
        {
            get { return segments; }
            set { segments = value; }
        }

        public float PositionX
        {
            get { return positionX; }
            set { positionX = value; }
        }

        public float PositionY
        {
            get { return positionY; }
            set { positionY = value; }
        }

        public float PositionZ
        {
            get { return positionZ; }
            set { positionZ = value; }
        }

        public float CurrentRotationX
        {
            get { return currentRotationX; }
            set { currentRotationX = value; }
        }

        public float CurrentRotationY
        {
            get { return currentRotationY; }
            set { currentRotationY = value; }
        }

        public float CurrentRotationZ
        {
            get { return currentRotationZ; }
            set { currentRotationZ = value; }
        }

        public float PreviousRotationX
        {
            get { return previousRotationX; }
            set { previousRotationX = value; }
        }

        public float PreviousRotationY
        {
            get { return previousRotationY; }
            set { previousRotationY = value; }
        }

        public float PreviousRotationZ
        {
            get { return previousRotationZ; }
            set { previousRotationZ = value; }
        }

        public int Rows
        {
            get { return rows; }
            set { rows = value; }
        }

        public bool IsSelected
        {
            get { return isSelected; }
            set { isSelected = value; }
        }

        #endregion define variables

        #region Zakladni funkce pro vytvareni a odstranovani
        // konstruktor
        public Mesh()
        {
            vertices = new ArrayList();
            lines = new ArrayList();
            surfaces = new ArrayList();
            segments = new ArrayList();

            houghVertices = new ArrayList();
        }

        //pretizeny konstruktor
        public Mesh(int newRows)
        {
            vertices = new ArrayList();
            lines = new ArrayList();
            surfaces = new ArrayList();
            segments = new ArrayList();
            rows = newRows;

            houghVertices = new ArrayList();
        }

        //pridani bodu
        public void AddVertex(float X, float Y, float Z)
        {
            Vertex vertex = new Vertex(X, Y, Z);
            vertices.Add(vertex);
        }

        //pridani cele kolekce bodů
        public void AddVertices(ArrayList verticesList)
        {
            vertices.AddRange(verticesList);
        }

        //smazat oznacene body
        public void DeleteSelected()
        {
            for (int i = vertices.Count - 1; i != -1; i--)
            {
                Vertex vertex = (Vertex)vertices[i];
                if (vertex.IsSelected)
                    vertices.RemoveAt(i);
            }
            CalculateBoundingBox();
        }

        //smazat redukovane body
        public void DeleteReduction()
        {
            houghVertices.Clear();
        }

        //smazat usečky
        public void DeleteLines()
        {
            lines.Clear();
        }

        //smazat plochy
        public void DeleteSurfaces()
        {
            surfaces.Clear();
        }

        //smazat segmenty
        public void DeleteSegments()
        {
            segments.Clear();
        }

        //oznacit bod jako vybrany
        public void SelectVertex(int index)
        {
            Vertex vertex = (Vertex)vertices[index];
            vertex.IsSelected = true;
            vertices[index] = vertex;
        }

        //odznacit bod jako vybrany
        public void UnSelectVertex(int index)
        {
            Vertex vertex = (Vertex)vertices[index];
            vertex.IsSelected = false;
            vertices[index] = vertex;
        }

        //odznacit vsechny body
        public void UnSelectAll()
        {
            for (int i = vertices.Count - 1; i != -1; i--)
            {
                Vertex vertex = (Vertex)vertices[i];
                vertex.IsSelected = false;
                vertices[i] = vertex;
            }
        }
        #endregion

        #region Translate and Rotate - Posuv a rotace

        //posuv a rotace
        private void TranslateAndRotate()
        {
            GL.Translate(positionX, positionY, positionZ);

            if (currentRotationX != previousRotationX)
            {
                RotateX();
            }
            else if (currentRotationY != previousRotationY)
            {
                RotateY();
            }
            else if (currentRotationZ != previousRotationZ)
            {
                RotateZ();
            }
        }

        //rotace v ose X
        private void RotateX()
        {
            float difference = Functions.DegreesToRadians(currentRotationX - previousRotationX);

            foreach (Vertex vertex in vertices)
            {
                float Y = vertex.Y;
                float Z = vertex.Z;

                vertex.Y = (float)(Math.Cos(difference) * Y - Math.Sin(difference) * Z);
                vertex.Z = (float)(Math.Sin(difference) * Y + Math.Cos(difference) * Z);
            }
            
            previousRotationX = currentRotationX;
            CalculateBoundingBox();
        }

        //rotace v ose Y
        private void RotateY()
        {
            float difference = Functions.DegreesToRadians(currentRotationY - previousRotationY);

            foreach (Vertex vertex in vertices)
            {
                float X = vertex.X;
                float Z = vertex.Z;

                vertex.X = (float)(Math.Cos(difference) * X + Math.Sin(difference) * Z);
                vertex.Z = (float)(-Math.Sin(difference) * X + Math.Cos(difference) * Z);
            }
            previousRotationY = currentRotationY;
            CalculateBoundingBox();
        }

        //rotace v ose Z
        private void RotateZ()
        {
            float difference = Functions.DegreesToRadians(currentRotationZ - previousRotationZ);

            foreach (Vertex vertex in vertices)
            {
                float X = vertex.X;
                float Y = vertex.Y;

                vertex.X = (float)(Math.Cos(difference) * X - Math.Sin(difference) * Y);
                vertex.Y = (float)(Math.Sin(difference) * X + Math.Cos(difference) * Y);
            }
            previousRotationZ = currentRotationZ;
            CalculateBoundingBox();
        }

        //zarovnani objektu s mrizkou
        public void AlignWithGrid()
        {
            float minY = 100000;

            foreach (Vertex vertex in vertices)
            {
                if (vertex.Y < minY)
                {
                    minY = vertex.Y;
                }
            }
            positionY = -minY;
        }

        #endregion translate and rotate

        #region Drawing - Vykreslovani

        //vykreslovani bodu
        public void DrawVertices()
        {
            GL.PushMatrix();
            TranslateAndRotate();
            if (!showReducedVerts || houghVertices.Count < 1)
            {
                GL.Begin(BeginMode.Points);
                foreach (Vertex vertex in vertices)
                {
                    if (vertex.IsSelected == true)
                    {                       
                        GL.Color3(1.0, 0.0, 1.0);
                    }
                    else
                    {                        
                        GL.Color3(vertexColor);
                    }
                    GL.Vertex3(vertex.X, vertex.Y, vertex.Z);
                }
                GL.End();
            }
            else
            {
                GL.Color3(vertexColor);
                GL.Begin(BeginMode.Points);
                foreach (Vertex vertex in houghVertices)
                {
                    GL.Vertex3(vertex.X, vertex.Y, vertex.Z);
                }
                GL.End();
            }
            GL.PopMatrix();
        }

        //vykreslovani usecek
        public void DrawLines()
        {
            GL.PushMatrix();
            TranslateAndRotate();

            GL.Color3(0.0, 0.0, 1.0);

            GL.Begin(BeginMode.Lines);
            foreach (Line line in lines)
            {
                GL.Vertex3(line.StartPoint.X, line.StartPoint.Y, line.StartPoint.Z);
                GL.Vertex3(line.EndPoint.X, line.EndPoint.Y, line.EndPoint.Z);
            }
            GL.End();

            GL.PointSize(2.0f);
            GL.Begin(BeginMode.Points);
            foreach (Line line in lines)
            {
                GL.Vertex3(line.StartPoint.X, line.StartPoint.Y, line.StartPoint.Z);
                GL.Vertex3(line.EndPoint.X, line.EndPoint.Y, line.EndPoint.Z);
            }
            GL.End();
            GL.PointSize(1.0f);

            GL.PopMatrix();
        }

        //vykreslovani ploch
        public void DrawSurfaces()
        {
            GL.PushMatrix();
            TranslateAndRotate();

            foreach (Surface surface in surfaces)
            {
                Line firstLine = (Line)surface.Lines[0];
                Line endLine = (Line)surface.Lines[surface.Lines.Count - 1];

                float distance;

                distance = (Math.Abs(firstLine.StartPoint.X / 1000) + Math.Abs(firstLine.StartPoint.Y / 1000) + Math.Abs(firstLine.StartPoint.Z / 1000));

                if (distance > 0.7)
                    distance = 0.7f;

                GL.Color3(1 - distance, 1 - distance, 1 - distance);

                GL.Begin(BeginMode.TriangleStrip);
                GL.Normal3(surface.Normal.X, surface.Normal.Y, surface.Normal.Z);
                foreach (Line line in surface.Lines)
                {
                    GL.Vertex3(line.StartPoint.X, line.StartPoint.Y, line.StartPoint.Z);  
                    GL.Vertex3(line.EndPoint.X, line.EndPoint.Y, line.EndPoint.Z);
                }
                GL.End();
            }
            GL.PopMatrix();
        }

        //vykreslovani segmentu
        public void DrawSegments()
        {
            GL.PushMatrix();
            TranslateAndRotate();
            GL.Color3(1.0, 1.0, 1.0);

            GL.Enable(EnableCap.PolygonOffsetFill);
            GL.PolygonOffset(1.0f, 1.0f);
            foreach (Segment segment in segments)
            {
               float distance = Calculate.Distance3(0, 0, 0, segment.CentreX, segment.CentreY, segment.CentreZ) / 1300;               
                distance = 1 - distance;
                if (distance < 0.3)
                    distance = 0.3f;

                if (distance > 0.9)
                    distance = 0.9f;

                GL.Color3(distance, distance, distance);

                segment.DrawSegment("semantic");
            }
            GL.Disable(EnableCap.PolygonOffsetFill);

            GL.Color3(0.0, 0.0, 0.0);
            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
            foreach (Segment segment in segments)
            {
                segment.DrawSegment("edges");
            }
            GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
            GL.PopMatrix();
        }

        //vykresleni v "selection modu" (pri vybirani mysi)
        public void DrawInSelectionMode()
        {
            DrawBoundingBox("solid");
        }

        //vykresleni v "subSelection modu" (pri vybirani bodů pomoci mysi)
        public void DrawInSubSelectionMode()
        {
            GL.PushMatrix();
            GL.Translate(positionX, positionY, positionZ);
            int i = 0;
            foreach (Vertex vertex in vertices)
            {
                GL.PushName(i);
                GL.Begin(BeginMode.Points);
                GL.Vertex3(vertex.X, vertex.Y, vertex.Z);
                GL.End();
                GL.PopName();
                i++;
            }
            GL.PopMatrix();
        }

        //vykresleni bounding boxu
        public void DrawBoundingBox(string type)
        {
            GL.PushMatrix();
            GL.Translate(positionX, positionY, positionZ);

            if (IsSelected)
            {
                GL.Color3(1.0, 0.0, 0.0);
                GL.LineWidth(2.0f);
            }
            else
            {
                GL.Color3(0.4, 0.4, 0.4);
            }

            int lenghtX = (int)(maxX - minX) / 10;
            int lenghtY = (int)(maxY - minY) / 10;
            int lenghtZ = (int)(maxZ - minZ) / 10;

            if (type == "wire")
            {
                GL.Begin(BeginMode.Lines);

                GL.Vertex3(minX, minY, minZ);
                GL.Vertex3(minX + lenghtX, minY, minZ);

                GL.Vertex3(minX, minY, minZ);
                GL.Vertex3(minX, minY + lenghtY, minZ);

                GL.Vertex3(minX, minY, minZ);
                GL.Vertex3(minX, minY, minZ + lenghtZ);

                GL.Vertex3(maxX, minY, minZ);
                GL.Vertex3(maxX - lenghtX, minY, minZ);

                GL.Vertex3(maxX, minY, minZ);
                GL.Vertex3(maxX, minY + lenghtY, minZ);

                GL.Vertex3(maxX, minY, minZ);
                GL.Vertex3(maxX, minY, minZ + lenghtZ);

                GL.Vertex3(minX, maxY, minZ);
                GL.Vertex3(minX + lenghtX, maxY, minZ);

                GL.Vertex3(minX, maxY, minZ);
                GL.Vertex3(minX, maxY - lenghtY, minZ);

                GL.Vertex3(minX, maxY, minZ);
                GL.Vertex3(minX, maxY, minZ + lenghtZ);

                GL.Vertex3(minX, minY, maxZ);
                GL.Vertex3(minX + lenghtX, minY, maxZ);

                GL.Vertex3(minX, minY, maxZ);
                GL.Vertex3(minX, minY + lenghtY, maxZ);

                GL.Vertex3(minX, minY, maxZ);
                GL.Vertex3(minX, minY, maxZ - lenghtZ);

                GL.Vertex3(maxX, maxY, minZ);
                GL.Vertex3(maxX - lenghtX, maxY, minZ);

                GL.Vertex3(maxX, maxY, minZ);
                GL.Vertex3(maxX, maxY - lenghtY, minZ);

                GL.Vertex3(maxX, maxY, minZ);
                GL.Vertex3(maxX, maxY, minZ + lenghtZ);

                GL.Vertex3(maxX, minY, maxZ);
                GL.Vertex3(maxX - lenghtX, minY, maxZ);

                GL.Vertex3(maxX, minY, maxZ);
                GL.Vertex3(maxX, minY + lenghtY, maxZ);

                GL.Vertex3(maxX, minY, maxZ);
                GL.Vertex3(maxX, minY, maxZ - lenghtZ);

                GL.Vertex3(minX, maxY, maxZ);
                GL.Vertex3(minX + lenghtX, maxY, maxZ);

                GL.Vertex3(minX, maxY, maxZ);
                GL.Vertex3(minX, maxY - lenghtY, maxZ);

                GL.Vertex3(minX, maxY, maxZ);
                GL.Vertex3(minX, maxY, maxZ - lenghtZ);

                GL.Vertex3(maxX, maxY, maxZ);
                GL.Vertex3(maxX - lenghtX, maxY, maxZ);

                GL.Vertex3(maxX, maxY, maxZ);
                GL.Vertex3(maxX, maxY - lenghtY, maxZ);

                GL.Vertex3(maxX, maxY, maxZ);
                GL.Vertex3(maxX, maxY, maxZ - lenghtZ);
            }
            else
            {
                GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
                GL.Begin(BeginMode.Quads);
                // Spodní stěna
                GL.Normal3(0.0f, -1.0f, 0.0f);
                GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(maxX, minY, minZ);
                GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(minX, minY, minZ);
                GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(minX, minY, maxZ);
                GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(maxX, minY, maxZ);
                // Přední stěna
                GL.Normal3(0.0f, 0.0f, 1.0f);
                GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(minX, minY, maxZ);
                GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(maxX, minY, maxZ);
                GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(maxX, maxY, maxZ);
                GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(minX, maxY, maxZ);
                // Zadní stěna
                GL.Normal3(0.0f, 0.0f, -1.0f);
                GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(minX, minY, minZ);
                GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(minX, maxY, minZ);
                GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(maxX, maxY, minZ);
                GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(maxX, minY, minZ);
                // Pravá stěna
                GL.Normal3(1.0f, 0.0f, 0.0f);
                GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(maxX, minY, minZ);
                GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(maxX, maxY, minZ);
                GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(maxX, maxY, maxZ);
                GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(maxX, minY, maxZ);
                // Levá stěna
                GL.Normal3(-1.0f, 0.0f, 0.0f);
                GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(minX, minY, minZ);
                GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(minX, minY, maxZ);
                GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(minX, maxY, maxZ);
                GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(minX, maxY, minZ);
                // Horní stěna
                GL.Normal3(0.0f, 1.0f, 0.0f);
                GL.TexCoord2(0.0f, 1.0f); GL.Vertex3(minX, maxY, minZ);
                GL.TexCoord2(0.0f, 0.0f); GL.Vertex3(minX, maxY, maxZ);
                GL.TexCoord2(1.0f, 0.0f); GL.Vertex3(maxX, maxY, maxZ);
                GL.TexCoord2(1.0f, 1.0f); GL.Vertex3(maxX, maxY, minZ);
                GL.End();// Done Cube
                GL.PopMatrix();
            }
            GL.End();// Done Cube
            GL.PopMatrix();
        }

        #endregion drawing        

        #region Rekonstrukce povrchu

        public float maxSurfDistance;

        //rekonstrukce povrchu
        public void SurfacesFromRawData(int tolerance)
        {
            ArrayList rowVertices1 = new ArrayList();
            ArrayList rowVertices2 = new ArrayList();

            for (int i = 0; (i < rows - 1); i++)
            {
                rowVertices1.Clear();
                rowVertices2.Clear();

                foreach (Vertex vertex in vertices)
                {
                    if (vertex.Row == i)
                    {
                        rowVertices1.Add(vertex);
                    }

                    if (vertex.Row == i + 1)
                    {
                        rowVertices2.Add(vertex);
                    }
                }

                for (int j = 0; j < (rowVertices1.Count - 1); j++)
                {
                    Vertex row1vertex1 = (Vertex)rowVertices1[j];
                    Vertex row1vertex2 = (Vertex)rowVertices1[j + 1];

                    if ((row1vertex2.Id - row1vertex1.Id) > 1)
                        continue;

                    int indexFirstLine = row1vertex1.Id;

                    for (int k = 0; k < (rowVertices2.Count - 1); k++)
                    {
                        Vertex row2vertex1 = (Vertex)rowVertices2[k];
                        Vertex row2vertex2 = (Vertex)rowVertices2[k + 1];

                        int indexSecondLine = row2vertex1.Id;

                        if ((indexSecondLine - 1000) > indexFirstLine)
                            break;

                        if ((row2vertex2.Id - row2vertex1.Id) > 1)
                            continue;

                        if ((indexSecondLine - indexFirstLine) == 1000)
                        {
                            if (Calculate.VertexDistance2(row1vertex1, row1vertex2) > tolerance)
                                continue;

                            if (Calculate.VertexDistance2(row2vertex1, row2vertex2) > tolerance)
                                continue;

                            if (Calculate.VertexDistance2(row1vertex1, row2vertex1) > tolerance)
                                continue;

                            if (Calculate.VertexDistance2(row1vertex2, row2vertex2) > tolerance)
                                continue;

                            Line firstLine = new Line();
                            firstLine.StartPoint = row1vertex1;
                            firstLine.EndPoint = row1vertex2;

                            Line secondLine = new Line();
                            secondLine.StartPoint = row2vertex1;
                            secondLine.EndPoint = row2vertex2;

                            CreateSurface(firstLine, secondLine);
                        }
                    }                
                }
            }
        }
        #endregion

        #region Octree funkce

        //fce pro vytvoreni Octree
        public int MakeOctree(int parameter, string method, int minPoints, bool merge)
        {
            float diffX = maxX - minX;
            float diffY = maxY - minY;
            float diffZ = maxZ - minZ;

            float edgeLenght = Math.Abs(diffX);

            if (edgeLenght < Math.Abs(diffY))
                edgeLenght = diffY;

            if (edgeLenght < Math.Abs(diffZ))
                edgeLenght = diffZ;

            if (method == "Node size")
            {
                parameter = (int)(edgeLenght / parameter);
                parameter = Convert.ToString(parameter, 2).Length;
            }

            meshOctree = new Octree(maxX - diffX / 2, maxY - diffY / 2, maxZ - diffZ / 2, edgeLenght, parameter);
            meshOctree.ParentNode.Vertices = (ArrayList)vertices.Clone();
            meshOctree.Merge = merge;
            meshOctree.MinPoints = minPoints;
            meshOctree.Subdivide(meshOctree.ParentNode, parameter, 1);

            segments.Clear();
            foreach (OctreeNode node in meshOctree.Nodes)
            {
                Segment segment = new Segment(node.CentreX, node.CentreY, node.CentreZ, node.EdgeLenght);
                segments.Add(segment);
            }

            return segments.Count;
        }

        //pretizena fce pro vytvoreni Octree pro redukci bodu
        public void MakeOctree(int depth, ArrayList vertsList)
        {
            float diffX = maxX - minX;
            float diffY = maxY - minY;
            float diffZ = maxZ - minZ;

            float edgeLenght = Math.Abs(diffX);

            if (edgeLenght < Math.Abs(diffY))
                edgeLenght = diffY;

            if (edgeLenght < Math.Abs(diffZ))
                edgeLenght = diffZ;

            meshOctree = new Octree(maxX - diffX / 2, maxY - diffY / 2, maxZ - diffZ / 2, edgeLenght, depth);
            meshOctree.ParentNode.Vertices = (ArrayList)vertsList.Clone();
            meshOctree.Subdivide(meshOctree.ParentNode, depth, 1);
        }

        //fce pro redukci bodu pomoci Octree
        public void OctreePointReduction()
        {
            if (meshOctree != null)
            {
                foreach (OctreeNode node in meshOctree.Nodes)
                {
                    Vertex original = (Vertex)node.Vertices[0];
                    Vertex vertex = new Vertex(node.CentreX, original.Y, node.CentreZ, original.Row, original.Id);
                    houghVertices.Add(vertex);
                }

                meshOctree = null;
                houghVertices.Sort();
            }
        }

        //vymazani Octree
        public void DeleteOctree()
        {
            if (meshOctree != null)
            {
                meshOctree = null;
            }
        }

        #endregion

        #region Houghova transformace

        //volani transformace
        public void Hough(int ratio, int newDegree, float distance, int points, int lenght)
        {
            lines.Clear();

            if (houghVertices.Count < 1)
            {
                foreach (Vertex vertex in vertices)
                {
                    Vertex newVertex = new Vertex(vertex.X, vertex.Y, vertex.Z, vertex.Row, vertex.Id);
                    houghVertices.Add(newVertex);
                }
            }

            CalculateBoundingBox();

            int width = (int)(maxX - minX);
            int height = (int)(maxZ - minZ);
            ArrayList tempLines = new ArrayList();

            meshHough = new HoughTransformation(width, height);

            meshHough.HoughRatio = ratio;
            meshHough.MaxDistance = distance;
            meshHough.MinPoints = points;
            meshHough.StepsPerDegree = newDegree;

            for (int j = 0; j < rows; j++)
            {
                meshHough.vertices.Clear();

                foreach (Vertex vertex in houghVertices)
                {
                    if (vertex.Row == j)
                    {
                        Vertex newVertex = new Vertex(vertex.X, vertex.Y, vertex.Z, vertex.Row, vertex.Id);
                        meshHough.vertices.Add(newVertex);
                    }
                }

                tempLines.AddRange(meshHough.Transformation(j, (int)minX, (int)minZ));
            }

            foreach (Line line in tempLines)
            {
                float lineLenght = Calculate.VertexDistance3(line.StartPoint, line.EndPoint);
                if (lineLenght >= lenght)
                {
                    lines.Add(line);
                }
            }
        }

        //predzpracovani bodu
        public void PointReduction(string type, int parameter)
        {
            houghVertices.Clear();

            if (type == "octree")
            {
                CalculateBoundingBox();
                for (int j = 0; j < rows; j++)
                {
                    ArrayList tempVertices = new ArrayList();

                    foreach (Vertex vertex in vertices)
                    {
                        if (vertex.Row == j)
                        {
                            Vertex newVertex = new Vertex(vertex.X, vertex.Y, vertex.Z, vertex.Row, vertex.Id);
                            tempVertices.Add(newVertex);
                        }
                    }

                    if (tempVertices.Count < 1)
                        continue;

                    MakeOctree(parameter, tempVertices);
                    OctreePointReduction();
                }
                return;
            }

            if (type == "lenght")
            {
                Vertex tempVert = (Vertex)vertices[0];

                for (int i = 1; i < vertices.Count; i++)
                {
                    float distance = Calculate.VertexDistance3(tempVert, (Vertex)vertices[i]);

                    if (distance < parameter)
                        continue;

                    Vertex newVertex = new Vertex(tempVert.X, tempVert.Y, tempVert.Z, tempVert.Row, tempVert.Id);

                    houghVertices.Add(newVertex);

                    tempVert = (Vertex)vertices[i];
                }
            }
        }

        //fce pro vytvoreni ploch 
        public void MakeSurfaces(int distance, int minSize)
        {
            surfaces.Clear();

            maxSurfDistance = distance;
            ArrayList tempLines = new ArrayList();

            for (int j = 0; j < rows; j++)
            {
                foreach (Line line in lines)
                {
                    if (line.Row == j)
                    {
                        if (checkLines(line, tempLines))
                            continue;

                        if (checkSurfaces(line))
                            continue;

                        tempLines.Add(line);
                    }
                }
            }

            for (int i = surfaces.Count - 1; i != -1; i--)
            {
                Surface surface = (Surface)surfaces[i];

                Vertex firstPoint = ((Line)surface.Lines[0]).StartPoint;
                Vertex lastPoint = ((Line)surface.Lines[surface.Lines.Count - 1]).EndPoint;

                int size = Calculate.SurfaceSize(firstPoint, lastPoint);
                if (size < minSize)
                    surfaces.RemoveAt(i);
            }
        }
        
        //zkousime mezi sebou usecky
        private bool checkLines(Line line, ArrayList tempLines)
        {
            foreach (Line tempLine in tempLines)
            {
                if (Math.Abs(line.Theta - tempLine.Theta) > 10)
                    continue;

                float startDistance = Calculate.VertexDistance3(line.StartPoint, tempLine.StartPoint);

                if (startDistance > maxSurfDistance)
                    continue;

                float endDistance = Calculate.VertexDistance3(line.EndPoint, tempLine.EndPoint);

                if (endDistance > maxSurfDistance)
                    continue;

                Vector3 newNormal = new Vector3();
                newNormal = Calculate.Normal(tempLine, line);

                CreateSurface(tempLine, line);
                tempLines.Remove(tempLine);

                return true;
            }
            return false;
        }

        //zkousime usecky na plochy
        private bool checkSurfaces(Line line)
        {
            ArrayList newSurfaceLines = new ArrayList();

            foreach (Surface surface in surfaces)
            {
                Line tempLine = (Line)surface.Lines[surface.Lines.Count - 1];


                if (Math.Abs(line.Theta - tempLine.Theta) > 10)
                    continue;

                float startDistance = Calculate.VertexDistance3(line.StartPoint, tempLine.StartPoint);

                if (startDistance > maxSurfDistance)
                    continue;

                float endDistance = Calculate.VertexDistance3(line.EndPoint, tempLine.EndPoint);

                if (endDistance > maxSurfDistance)
                    continue;

                Vector3 newNormal = new Vector3();
                newNormal = Calculate.Normal(tempLine, line);

                if (newNormal.X != surface.Normal.X && newNormal.Y != surface.Normal.Y && newNormal.Z != surface.Normal.Z)
                {
                    newSurfaceLines.Add(tempLine);
                    continue;
                }

                surface.Lines.Add(line);
                return true;
            }

            if (newSurfaceLines.Count > 0)
            {
                Line newLine = (Line)newSurfaceLines[0];
                CreateSurface(newLine, line);
                return true;
            }
            return false;
        }

        //vytvorime plochu
        private void CreateSurface(Line firstLine, Line secondLine)
        {
            Surface newSurface = new Surface();
            newSurface.Lines.Add(firstLine);
            newSurface.Lines.Add(secondLine);

            newSurface.Normal = Calculate.Normal(firstLine, secondLine);
            surfaces.Add(newSurface);
        }

        //segmentace objektu z ploch
        public void ObjectSegmentation(int tolerance)
        {
            segments.Clear();
            foreach (Surface surface in surfaces)
            {
                Segment segment = SegmentBoundingBox(surface);
                segments.Add(segment);
            }
            segments.Sort();

            CollapseSegments(1, tolerance);
        }

        //pohlcovani segmentu
        private void CollapseSegments(int index, int tolerance)
        {
            Segment segment = (Segment)segments[segments.Count - index];
            index++;
            
            for (int i = segments.Count - index; i != -1; i--)
            {
                Segment testSegment = (Segment)segments[i];
                if (segment.Contains(testSegment, tolerance))
                    segments.RemoveAt(i);
            }

            CollapseSegments(index, tolerance);
        }

        private Segment SegmentBoundingBox(Surface surface)
        {
            float maxX = -1000000;
            float minX = 1000000;
            float maxY = -1000000;
            float minY = 1000000;
            float maxZ = -1000000;
            float minZ = 1000000;

            foreach (Line line in surface.Lines)
            {
                float startX = line.StartPoint.X;
                float startY = line.StartPoint.Y;
                float startZ = line.StartPoint.Z;

                float endX = line.EndPoint.X;
                float endY = line.EndPoint.Y;
                float endZ = line.EndPoint.Z;

                if (startX > maxX)
                {
                    maxX = startX;
                }
                if (startX < minX)
                {
                    minX = startX;
                }

                if (startY > maxY)
                {
                    maxY = startY;
                }
                if (startY < minY)
                {
                    minY = startY;
                }

                if (startZ > maxZ)
                {
                    maxZ = startZ;
                }
                if (startZ < minZ)
                {
                    minZ = startZ;
                }

                if (endX > maxX)
                {
                    maxX = endX;
                }
                if (endX < minX)
                {
                    minX = endX;
                }

                if (endY > maxY)
                {
                    maxY = endY;
                }
                if (endY < minY)
                {
                    minY = endY;
                }

                if (endZ > maxZ)
                {
                    maxZ = endZ;
                }
                if (endZ < minZ)
                {
                    minZ = endZ;
                }
            }

            maxX++;
            maxY++;
            maxZ++;
            minX--;
            minY--;
            minZ--;

            Segment segment = new Segment(maxX, minX, maxY, minY, maxZ, minZ);
            return segment;
        }

        #endregion

        #region semantika

        //Mazani nebo obarvovani segmentu podle semantiky
        public void Semantic(int ceilingTop, int ceilingBottom, int floorTop, int floorBottom, string action, string feature)
        {
            if (action == "Decolorize")
            {
                foreach (Segment segment in segments)
                {
                    segment.Semantic = "none";
                }
                return;
            }

            if (action == "Colorize")
            {
                foreach (Segment segment in segments)
                {
                    if ((feature == "Ceiling" || feature == "Floor & Ceiling") && (segment.MinY > ceilingBottom && segment.MaxY < ceilingTop))
                    {
                        segment.Semantic = "ceiling";
                    }
                    else if ((feature == "Floor" || feature == "Floor & Ceiling") && (segment.MinY > floorBottom && segment.MaxY < floorTop))
                    {
                        segment.Semantic = "floor";
                    }
                    else
                    {
                        segment.Semantic = "none";
                    }
                }
            }

            if (action == "Remove")
            {
                for (int i = segments.Count - 1; i != -1; i--)
                {
                    Segment segment = (Segment)segments[i];

                    if ((feature == "Ceiling" || feature == "Floor & Ceiling") && (segment.MinY > ceilingBottom && segment.MaxY < ceilingTop))
                    {
                        segments.RemoveAt(i);
                    }
                    else if ((feature == "Floor" || feature == "Floor & Ceiling") && (segment.MinY > floorBottom && segment.MaxY < floorTop))
                    {
                        segments.RemoveAt(i);
                    }
                }
            }
        }

        #endregion

        #region pomocne funkce

        //vypocet bounding boxu (opsane krychle)
        public void CalculateBoundingBox()
        {
            maxX = -1000000;
            minX = 1000000;
            maxY = -1000000;
            minY = 1000000;
            maxZ = -1000000;
            minZ = 1000000;

            foreach (Vertex vertex in vertices)
            {
                float X = vertex.X;
                float Y = vertex.Y;
                float Z = vertex.Z;

                if (X > maxX)
                {
                    maxX = X;
                }
                if (X < minX)
                {
                    minX = X;
                }

                if (Y > maxY)
                {
                    maxY = Y;
                }
                if (Y < minY)
                {
                    minY = Y;
                }

                if (Z > maxZ)
                {
                    maxZ = Z;
                }
                if (Z < minZ)
                {
                    minZ = Z;
                }
            }
            maxX += 2;
            minX -= 2;
            maxY += 2;
            minY -= 2;
            maxZ += 2;
            minZ -= 2;
        }

        //zjisteni pozice středu objektu
        public float[] CentreCoords()
        {
            float[] coords = new float[3];

            coords[0] = (maxX + minX) / 2 + positionX;
            coords[1] = (maxY + minY) / 2 + positionY;
            coords[2] = (maxZ + minZ) / 2 + positionZ;

            return coords;
        }

        public void SetColors(Color backColor)
        {
            if (backColor.Name == "White")
            {
                vertexColor = new Vector3(0.1f, 0.1f, 0.1f);
            }
            else if (backColor.Name == "Black")
            {
                vertexColor = new Vector3(1.0f, 1.0f, 1.0f);
            }
        }
        #endregion        
    }
}
