private static IEnumerable <HoneycombDef> GetEuclidImageSet() { for (int p = 3; p <= 8; p++) { for (int q = 3; q <= 8; q++) { for (int r = 3; r <= 8; r++) { if (!(Geometry2D.GetGeometry(p, q) == Geometry.Euclidean || Geometry2D.GetGeometry(q, r) == Geometry.Euclidean)) { continue; } // Do the last as infinity System.Func <int, int> iSafe = input => input == 8 ? -1 : input; yield return(new HoneycombDef() { P = iSafe(p), Q = iSafe(q), R = iSafe(r) }); } } } }
public static void DoStuff(Settings settings) { HoneycombDef imageData = new HoneycombDef(settings.P, settings.Q, settings.R); ////////////////////////////////////////////////////////////// Various things we've run over time. //Sandbox.CalcSelfSimilarityScale(); //Sandbox.Check_pq_Distances(); //HyperidealSquares(); //S3.Hypercube(); //R3.Geometry.Euclidean.GenEuclidean(); //HoneycombGen.OneHoneycombOldCode(); //AnimateCell( imageData ); //CreateCellPovRay( imageData, "cell.pov" ); //CreateSimplex( imageData ); //HoneycombGen_old.OneHoneycombNew( new HoneycombDef() { P = imageData.P, Q = imageData.Q, R = imageData.R } ); //SphericalAnimate( imageData ); OneImage(settings); HoneycombDef[] scaleLarger = GetImageSet().Where(h => Geometry2D.GetGeometry(h.P, h.Q) == Geometry.Euclidean || Geometry2D.GetGeometry(h.P, h.Q) == Geometry.Spherical).ToArray(); int count = scaleLarger.Length; //foreach( HoneycombAndView h in scaleLarger ) // Trace.WriteLine( h.FormatFilename() ); //BatchRun( settings ); }
private static IEnumerable <HoneycombDef> GetCoxeterSet() { yield return(new HoneycombDef() { P = 5, Q = 3, R = 4 }); yield return(new HoneycombDef() { P = 4, Q = 3, R = 5 }); yield return(new HoneycombDef() { P = 5, Q = 3, R = 5 }); yield return(new HoneycombDef() { P = 3, Q = 5, R = 3 }); for (int p = 3; p <= 6; p++) { for (int q = 3; q <= 6; q++) { for (int r = 3; r <= 6; r++) { if (Geometry2D.GetGeometry(p, q) == Geometry.Spherical && Geometry2D.GetGeometry(q, r) == Geometry.Spherical) { continue; } if (Geometry2D.GetGeometry(p, q) == Geometry.Hyperbolic || Geometry2D.GetGeometry(q, r) == Geometry.Hyperbolic) { continue; } yield return(new HoneycombDef() { P = p, Q = q, R = r }); } } } }
// CHEAT! (would be better to do a geometrical construction) // We are going to iterate to the starting point that will make all edge lengths the same. public static Vector3D IterateToStartingPoint(HoneycombDef?def, int[] activeMirrors, Simplex simplex) { if (activeMirrors.Length == 1) { return(simplex.Verts[activeMirrors[0]]); } // We are minimizing the output of this function, // because we want all edge lengths to be as close as possible. // Input vector should be in the Ball Model. Func <Vector3D, double> diffFunc = v => { List <double> lengths = new List <double>(); for (int i = 0; i < activeMirrors.Length; i++) { Vector3D reflected = simplex.ReflectInFacet(v, activeMirrors[i]); lengths.Add(H3Models.Ball.HDist(v, reflected)); } double result = 0; double average = lengths.Average(); foreach (double length in lengths) { result += Math.Abs(length - average); } if (Infinity.IsInfinite(result)) { result = double.PositiveInfinity; } return(result); }; // So that we can leverage Euclidean barycentric coordinates, we will first convert our simplex to the Klein model. // We will need to take care to properly convert back to the Ball as needed. Vector3D[] kleinVerts = simplex.Verts.Select(v => HyperbolicModels.PoincareToKlein(v)).ToArray(); if (def != null) { HoneycombDef d = def.Value; Geometry vertexGeometry = Geometry2D.GetGeometry(d.Q, d.R); if (vertexGeometry == Geometry.Hyperbolic) { kleinVerts[3] = SimplexCalcs.VertexPointKlein(d.P, d.Q, d.R); } } // Normalizing barycentric coords amounts to making sure the 4 coords add to 1. Func <Vector3D, Vector3D> baryNormalize = b => { return(b / (b.X + b.Y + b.Z + b.W)); }; // Bary Coords to Euclidean Func <Vector3D[], Vector3D, Vector3D> baryToEuclidean = (kv, b) => { Vector3D result = kv[0] * b.X + kv[1] * b.Y + kv[2] * b.Z + kv[3] * b.W; return(result); }; // Our starting barycentric coords (halfway between all active mirrors). Vector3D bary = new Vector3D(); foreach (int a in activeMirrors) { bary[a] = 0.5; } bary = baryNormalize(bary); // For each iteration, we'll shrink this search offset. // NOTE: The starting offset and decrease factor I'm using don't guarantee convergence, // but it seems to be working pretty well (even when varying these parameters). //double searchOffset = 1.0 - bary[activeMirrors[0]]; //double searchOffset = bary[activeMirrors[0]]; double factor = 1.5; // Adjusting this helps get some to converge, e.g. 4353-1111 double searchOffset = bary[activeMirrors[0]] / factor; double min = double.MaxValue; int iterations = 1000; for (int i = 0; i < iterations; i++) { min = diffFunc(HyperbolicModels.KleinToPoincare(baryToEuclidean(kleinVerts, bary))); foreach (int a in activeMirrors) { Vector3D baryTest1 = bary, baryTest2 = bary; baryTest1[a] += searchOffset; baryTest2[a] -= searchOffset; baryTest1 = baryNormalize(baryTest1); baryTest2 = baryNormalize(baryTest2); double t1 = diffFunc(HyperbolicModels.KleinToPoincare(baryToEuclidean(kleinVerts, baryTest1))); double t2 = diffFunc(HyperbolicModels.KleinToPoincare(baryToEuclidean(kleinVerts, baryTest2))); if (t1 < min) { min = t1; bary = baryTest1; } if (t2 < min) { min = t2; bary = baryTest2; } } if (Tolerance.Equal(min, 0.0, 1e-14)) { System.Console.WriteLine(string.Format("Converged in {0} iterations.", i)); break; } searchOffset /= factor; } if (!Tolerance.Equal(min, 0.0, 1e-14)) { System.Console.WriteLine("Did not converge: " + min); // Be a little looser before thrown an exception. if (!Tolerance.Equal(min, 0.0, 1e-12)) { System.Console.ReadKey(true); //throw new System.Exception( "Boo. We did not converge." ); return(Vector3D.DneVector()); } } Vector3D euclidean = baryToEuclidean(kleinVerts, bary); return(HyperbolicModels.KleinToPoincare(euclidean)); }
private static void CreateCellPovRay(HoneycombDef def, string filename, double t = 0) { int p = def.P; int q = def.Q; int r = def.R; //Vector3D trans = new Vector3D( 1.0/3, 0 ) * (2 + 2 * Math.Sin( Math.PI / 6 )) * t; //double scale = 1.8; Vector3D trans = new Vector3D(); double scale = 1.0; Vector3D[] sVerts = null; // SimplexCalcs.VertsBall( p, q, r ); Vector3D vUHS = H3Models.BallToUHS(SimplexCalcs.VertexPointBall(p, q, r)); // Just did this for everything. Non-general position working better and will make all heads consistent. scale = 2.0; if (Geometry2D.GetGeometry(q, r) != Geometry.Hyperbolic) // Vertex-centered if possible { scale = 1.0 / vUHS.Z; } //else if( Geometry2D.GetGeometry( p, q ) == Geometry.Hyperbolic ) // Make the biggest head somewhat smaller. // scale = 2.0; Vector3D cen = InteriorPointBall; /*var kleinVerts = sVerts.Select( v => HyperbolicModels.PoincareToKlein( v ) ); * Vector3D avg = new Vector3D(); * foreach( Vector3D v in kleinVerts ) * avg += v; * avg /= kleinVerts.Count(); * Vector3D cen = HyperbolicModels.KleinToPoincare( avg );*/ cen = H3Models.BallToUHS(cen); cen += trans; //cen *= scale; cen = H3Models.UHSToBall(cen); Sphere[] simplex = SimplexCalcs.Mirrors(p, q, r, moveToBall: false); // Apply transformations. simplex = simplex.Select(s => { Sphere.TranslateSphere(s, trans); Sphere.ScaleSphere(s, scale); return(H3Models.UHSToBall(s)); }).ToArray(); for (int i = 0; i < 4; i++) { if (simplex[i].IsPointInside(cen)) { simplex[i].Invert = true; } } Sphere[] simplexForColorScale = SimplexCalcs.Mirrors(p, q, r, moveToBall: true); CoxeterImages.Settings temp = AutoCalcScale(def, simplexForColorScale); int maxDepth = (int)temp.ColorScaling; //Random rand = new Random( p+q+r ); //int randOffset = rand.Next( maxDepth ); bool ball = true; bool dual = false; H3.Cell[] simplicesFinal = GenCell(simplex, null, cen, ball, dual); using (StreamWriter sw = File.CreateText(filename)) // We need to reuse this StreamWriter (vs. calling AppendSimplex) for performance. { sw.WriteLine("#include \"hyper_ball.pov\""); //int[] include = new int[] { 0, 1, 2, 3 }; int[] include = new int[] { 0 }; if (dual) { include = new int[] { 3 } } ; // Output the facets. foreach (H3.Cell cell in simplicesFinal) { Sphere[] facets = cell.Facets.Select(f => f.Sphere).ToArray(); if (m_toKlein) { facets = facets.Select(s => H3Models.BallToKlein(s)).ToArray(); } int depth = cell.Depths[0] + 1; Color c = Coloring.ColorAlongHexagon(maxDepth, depth); if (cell.Depths.Sum() % 2 == 0) { c = Coloring.Inverse(c); } PovRay.AddSimplex(sw, facets, cell.Center, include, filename, Coloring.ToVec(c)); } /*include = new int[] { 1, 2, 3 }; * foreach( H3.Cell cell in simplicesFinal ) * { * Sphere[] facets = cell.Facets.Select( f => f.Sphere ).ToArray(); * Color c = Color.Red; * Vector3D cv = Coloring.ToVec( c ); * cv.W = 0.9; * PovRay.AddSimplex( sw, facets, cell.Center, include, filename, cv ); * }*/ } // Output the edges/verts. bool includeEdges = false; if (includeEdges) { sVerts = sVerts.Select(v => { v = H3Models.BallToUHS(v); v += trans; v *= scale; return(H3Models.UHSToBall(v)); }).ToArray(); H3.Cell.Edge[] edges = Recurse.CalcEdges(simplex.Skip(1).ToArray(), new H3.Cell.Edge[] { new H3.Cell.Edge(sVerts[2], sVerts[3], order: false) }, new Recurse.Settings() { Threshold = 0.01 }); PovRay.WriteH3Edges(new PovRay.Parameters { AngularThickness = 0.01 }, edges, filename, append: true); HashSet <Vector3D> verts = new HashSet <Vector3D>(); foreach (H3.Cell.Edge e in edges) { verts.Add(e.End); } PovRay.WriteVerts(new PovRay.Parameters { AngularThickness = 0.02 }, Geometry.Hyperbolic, verts.ToArray(), filename, append: true); } }
private static void HoneycombFiniteVertexFig(HoneycombDef def, int lod, Dictionary <Vector3D, H3.Cell> complete) { int p = def.P; int q = def.Q; int r = def.R; double scale = 1.0; Vector3D vUHS = H3Models.BallToUHS(SimplexCalcs.VertexPointBall(p, q, r)); if (Geometry2D.GetGeometry(q, r) != Geometry.Hyperbolic) // Vertex-centered if possible { scale = 1.0 / vUHS.Z; } System.Func <Vector3D, Vector3D> trans = v => { v = H3Models.BallToUHS(v); v *= scale; v = H3Models.UHSToBall(v); return(v); }; bool ball = true; Sphere[] simplex = SimplexCalcs.Mirrors(p, q, r, moveToBall: ball); simplex = simplex.Select(s => { s = H3Models.BallToUHS(s); Sphere.ScaleSphere(s, scale); s = H3Models.UHSToBall(s); return(s); }).ToArray(); H3.Cell.Edge[] edges = SimplexCalcs.SimplexEdgesBall(p, q, r); // Two edges of the simplex facet. // NOTE: This contruction only works for material triangles, and matches the construction in the TextureHelper. m_div = TextureHelper.SetLevels(lod); int[] elementIndices = TextureHelper.TextureElements(1, lod); List <Vector3D> points = new List <Vector3D>(); H3.Cell.Edge e1 = edges[2]; H3.Cell.Edge e2 = edges[3]; Vector3D p1 = trans(e1.Start), p2 = trans(e1.End), p3 = trans(e2.End); Vector3D[] points1 = H3Models.Ball.GeodesicPoints(p2, p1, m_div); Vector3D[] points2 = H3Models.Ball.GeodesicPoints(p3, p1, m_div); for (int i = 0; i < m_div; i++) { points.AddRange(H3Models.Ball.GeodesicPoints(points1[i], points2[i], m_div - i)); } points.Add(p1); Mesh mesh = new Mesh(); for (int i = 0; i < elementIndices.Length / 3; i++) { int idx1 = i * 3; int idx2 = i * 3 + 1; int idx3 = i * 3 + 2; Vector3D v1 = points[elementIndices[idx1]]; Vector3D v2 = points[elementIndices[idx2]]; Vector3D v3 = points[elementIndices[idx3]]; mesh.Triangles.Add(new Mesh.Triangle(v1, v2, v3)); } // AuxPoints will be used for multiple things. // - The first is a definition point for a face, so we can check for duplicates. // - We'll also store the points for the 3 edges of our fundamental triangle. List <Vector3D> auxPoints = new List <Vector3D>(); { auxPoints.Add((p1 + p2 + p3) / 3); auxPoints.AddRange(points1); auxPoints.AddRange(points2.Reverse()); auxPoints.AddRange(H3Models.Ball.GeodesicPoints(points2[0], points1[0], m_div)); } Vector3D cen = HoneycombPaper.InteriorPointBall; H3.Cell[] simplices = GenCell(simplex, mesh, cen, auxPoints.ToArray(), ball); // Existing cells take precedence. foreach (H3.Cell c in simplices) { Vector3D t = c.AuxPoints[0]; H3.Cell dummy; if (!complete.TryGetValue(t, out dummy)) { complete[t] = c; } } }