public static IPoly RemoveDuplicateVerticies(IPoly poly)
        {
            List <Vector3> output = new List <Vector3>();

            for (int i = 0; i < poly.Resolution; i++)
            {
                if (output.Count == 0)
                {
                    output.Add(poly.GetPoint(i));
                }
                else
                {
                    Vector3 point = poly.GetPoint(i);
                    Vector3 last  = output[output.Count - 1];
                    if (!Math3d.Compare(point, last))
                    {
                        output.Add(point);
                    }
                }
            }
            if (Math3d.Compare(output[0], output[output.Count - 1]))
            {
                output.RemoveAt(0);
            }
            return(poly.Clone(output.ToArray()));
        }
        /// <summary>
        /// Create a polygon from a set of edges
        /// For this algorithm to work as expected, the edges must wrap to form a polygon
        /// Optimize output will merge consecutive edges with have the same line
        /// </summary>
        /// <param name="edges"></param>
        /// <param name="constructor"></param>
        /// <param name="optimizeOutput"></param>
        /// <returns></returns>
        public static IPoly GetPolygonFromEdges(IEnumerable <IEdge> edges, Func <IEnumerable <IEdge>, IPoly> constructor = null, bool optimizeOutput = true)
        {
            if (constructor == null)
            {
                constructor = (x) => new EdgePoly(x);
            }

            //create a hash set of all edges
            HashSet <IEdge> hashSet = new HashSet <IEdge>(edges);

            IEdge current = hashSet.First();
            DoubleLinkNode <IEdge> currentNode = new DoubleLinkNode <IEdge>(current);
            DoubleLinkNode <IEdge> firstNode   = currentNode;

            hashSet.Remove(current);
            while (hashSet.Count > 0)
            {
                //find next node
                IEdge next = hashSet.FirstOrDefault(x => Math3d.Compare(current.B, x.A));
                //error check
                if (next == null)
                {
                    return(null);
                }

                //remove the node from the hash set
                hashSet.Remove(next);

                //create the next link node
                currentNode.next          = new DoubleLinkNode <IEdge>(next);
                currentNode.next.previous = currentNode;

                //advance the search space
                current     = next;
                currentNode = currentNode.next;
            }

            //link ends
            currentNode.next   = firstNode;
            firstNode.previous = currentNode;

            if (optimizeOutput)
            {
                //rotate list to find good canidate for first node
                for (currentNode = firstNode.next; currentNode != firstNode; currentNode = currentNode.next)
                {
                    if (!IsParallel(currentNode.previous.value, currentNode.value))
                    {
                        break;
                    }
                }
                firstNode = currentNode;

                //break link to prevent infinate loops over bad geometry
                firstNode.previous.next = null;
                firstNode.previous      = null;

                var last = firstNode;

                //eliminate extra edges
                for (var node = firstNode; node != null; node = node.next)
                {
                    if (node != null)
                    {
                        last = node;
                    }
                    while (node.next != null && IsParallel(node.value, node.next.value))
                    {
                        node.value = new Edge(node.value.A, node.next.value.B);
                        node.next  = node.next.next;
                    }
                }

                //relink ends
                last.next          = firstNode;
                firstNode.previous = last;
            }

            //construct output to ngon
            List <IEdge> outputList = new List <IEdge>();

            outputList.Add(firstNode.value);
            for (var node = firstNode.next; node != firstNode; node = node.next)
            {
                outputList.Add(node.value);
            }

            return(constructor(outputList));
        }