/** * 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); } }