/**
         * Canonicalizes a polyhedron by adjusting its vertices iteratively.
         *
         * @param poly          The polyhedron whose vertices to adjust.
         * @param numIterations The number of iterations to adjust for.
         */
        public static void Adjust(ConwayPoly poly, int numIterations)
        {
            var dual = poly.Dual();

            for (int i = 0; i < numIterations; i++)
            {
                var newDualPositions = ReciprocalCenters(poly);
                dual.SetVertexPositions(newDualPositions);
                var newPositions = ReciprocalCenters(dual);
                poly.SetVertexPositions(newPositions);
            }
        }
        /**
         * A helper method for threshold-based termination in both planarizing and
         * adjusting. If a vertex moves by an unexpectedly large amount, or if the
         * new vertex position has an NaN component, the algorithm automatically
         * terminates.
         *
         * @param poly      The polyhedron to canonicalize.
         * @param threshold The threshold of vertex movement after an iteration.
         * @param planarize True if we are planarizing, false if we are adjusting.
         * @return The number of iterations that were executed.
         */
        private static int _Canonicalize(ConwayPoly poly, double threshold, bool planarize)
        {
            var dual             = poly.Dual();
            var currentPositions = poly.Vertices.Select(x => x.Position).ToList();

            int iterations = 0;

            while (true)
            {
                var newDualPositions = planarize ? ReciprocalVertices(poly) : ReciprocalCenters(poly);
                dual.SetVertexPositions(newDualPositions);
                var newPositions = planarize ? ReciprocalVertices(dual) : ReciprocalCenters(dual);

                double maxChange = 0.0;
                for (int i = 0; i < currentPositions.Count; i++)
                {
                    var newPos = poly.Vertices[i].Position;
                    var diff   = newPos - currentPositions[i];
                    maxChange = Math.Max(maxChange, diff.magnitude);
                }

                // Check if an error occurred in computation. If so, terminate
                // immediately
                if (Double.IsNaN(newPositions[0].x) || Double.IsNaN(newPositions[0].y) ||
                    Double.IsNaN(newPositions[0].z))
                {
                    break;
                }

                // Check if the position changed by a significant amount so as to
                // be erroneous. If so, terminate immediately
                if (planarize && maxChange > MAX_VERTEX_CHANGE)
                {
                    break;
                }

                poly.SetVertexPositions(newPositions);

                if (maxChange < threshold)
                {
                    break;
                }

                currentPositions = poly.Vertices.Select(x => x.Position).ToList();
                iterations++;
            }

            return(iterations);
        }
        /**
         * Modifies a polyhedron's vertices such that faces are closer to planar.
         * The more iterations, the closer the faces are to planar. If a vertex
         * moves by an unexpectedly large amount, or if the new vertex position
         * has an NaN component, the algorithm automatically terminates.
         *
         * @param poly          The polyhedron whose faces to planarize.
         * @param numIterations The number of iterations to planarize for.
         */
        public static void Planarize(ConwayPoly poly, int numIterations)
        {
            var dual = poly.Dual();

            for (int i = 0; i < numIterations; i++)
            {
                var newDualPositions = ReciprocalVertices(poly);
                dual.SetVertexPositions(newDualPositions);
                var newPositions = ReciprocalVertices(dual);

                double maxChange = 0.0;

                for (int j = 0; j < poly.Vertices.Count; j++)
                {
                    var newPos = poly.Vertices[j].Position;
                    var diff   = newPos - poly.Vertices[j].Position;
                    maxChange = Math.Max(maxChange, diff.magnitude);
                }

                // Check if an error occurred in computation. If so, terminate
                // immediately. This likely occurs when faces are already planar.
                if (Double.IsNaN(newPositions[0].x) || Double.IsNaN(newPositions[0].y) ||
                    Double.IsNaN(newPositions[0].z))
                {
                    break;
                }

                // Check if the position changed by a significant amount so as to
                // be erroneous. If so, terminate immediately
                if (maxChange > MAX_VERTEX_CHANGE)
                {
                    break;
                }

                poly.SetVertexPositions(newPositions);
            }
        }