private static void OneHoneycombGoursat( int[] active, string baseName, int baseHue ) { CalcThickness( active ); // Create the simplex. Simplex simplex = new Simplex(); simplex.InitializeGoursat(); // Map of labels for mirrors consistent with input scheme to Goursat function. // Map is from wikipedia labeling scheme to the indices our function generates. // // wiki == our index // 0100 == 0 // 0001 == 1 // 1000 == 2 // 0010 == 3 Func<int, int> mapMirror = i => { switch( i ) { case 0: return 2; case 1: return 0; case 2: return 3; case 3: return 1; } throw new System.ArgumentException(); }; // We need to set this up before converting the mirrors. string mirrorsString = ActiveMirrorsString( active ); string suffix = "-" + mirrorsString; // Convert our active mirrors into the Goursat tet indices. int[] polyMirrors = new int[] { 1, 2, 3 }; active = active.Select( i => mapMirror( i ) ).OrderBy( i => i ).ToArray(); polyMirrors = polyMirrors.Select( i => mapMirror( i ) ).OrderBy( i => i ).ToArray(); Vector3D startingPoint = IterateToStartingPoint( active, simplex ); List<H3.Cell.Edge> startingEdges = new List<H3.Cell.Edge>(); foreach( int a in active ) { Vector3D reflected = simplex.ReflectInFacet( startingPoint, a ); startingEdges.Add( new H3.Cell.Edge( startingPoint, reflected ) ); } bool doEdges = true; bool doCells = false; // Generate the honeycomb. H3.Cell.Edge[] edges = null; if( doEdges ) edges = Recurse.CalcEdgesSmart( simplex.Facets, startingEdges.ToArray() ); // Highlighted cells. H3.Cell[] cellsToHighlight = null; if( doCells ) { H3.Cell startingCell = PolyhedronToHighlight( Geometry.Hyperbolic, polyMirrors, simplex, startingPoint ); cellsToHighlight = Recurse.CalcCells( simplex.Facets, new H3.Cell[] { startingCell } ); //cellsToHighlight = new H3.Cell[] { startingCell }; } // plugin Wendy's nonuniform calcs here... //Nonuniform.Wendy( simplex, edges ); // Trim out half the edges (the ones we won't see in our Pov-Ray view). Vector3D lookFrom = new Vector3D( 1, 1, 1 ) * 0.7; Vector3D lookAt = new Vector3D( ); // pov-ray lookat double thresh = -.01; if( doEdges ) edges = edges.Where( e => e.Start.Dot( lookAt ) > thresh || e.End.Dot( lookAt ) > thresh ).ToArray(); //if( doCells ) // cellsToHighlight = cellsToHighlight.Where( c => c.Center.Dot( lookAt ) > thresh ).ToArray(); // I don't think this works right // Setup Pov-ray stuff. // We have 16 possible mirror states. We'll calculate the hue by converting the binary state to decimal, and doubling. // So for a given family, the hue will range over 32 numbers. int hue = baseHue + 2 * Convert.ToInt32( mirrorsString, 2 ); string fileName = baseName + suffix; using( StreamWriter sw = File.CreateText( fileName + ".pov" ) ) { sw.WriteLine( string.Format( "#declare lookFrom = <{0},{1},{2}>;", lookFrom.X, lookFrom.Y, lookFrom.Z ) ); sw.WriteLine( string.Format( "#declare lookAt = <{0},{1},{2}>;", lookAt.X, lookAt.Y, lookAt.Z ) ); sw.WriteLine( "#include \"C:\\Users\\hrn\\Documents\\roice\\povray\\H3_uniform_faces\\H3_uniform_faces.pov\"" ); //sw.WriteLine( string.Format( "background {{ CHSL2RGB( <{0}, 1, .3> ) }}", hue ) ); //sw.WriteLine( string.Format( "background {{ rgb <.13,.37,.31> }}" ) ); sw.WriteLine( string.Format( "background {{ rgb 1 }}" ) ); } if( doEdges ) H3.SaveToFile( fileName, edges, finite: true, append: true ); if( doCells ) { HashSet<H3.Cell.Edge> cellEdges = new HashSet<H3.Cell.Edge>( new H3.Cell.EdgeEqualityComparer() ); foreach( H3.Cell cell in cellsToHighlight ) cell.AppendAllEdges( cellEdges ); edges = cellEdges.ToArray(); H3.SaveToFile( fileName, edges, finite: true, append: true ); H3.AppendFacets( fileName, cellsToHighlight ); } }
private static H3.Cell PolyhedronToHighlight( Geometry g, int[] mirrors, Simplex simplex, Vector3D startingPoint ) { if( mirrors.Length != 3 ) throw new System.Exception( "We need exactly three mirrors to generate a polyhedron." ); // In the general case, we can have 3 types of polygons, each being generated by choosing 2 of the 3 mirrors. // When fewer polygon types exist, GenFacet will return null, so we need to check that. List<H3.Cell.Facet> polyFacets = new List<H3.Cell.Facet>(); polyFacets.Add( GenFacet( g, mirrors[0], mirrors[1], simplex, startingPoint ) ); polyFacets.Add( GenFacet( g, mirrors[1], mirrors[2], simplex, startingPoint ) ); polyFacets.Add( GenFacet( g, mirrors[0], mirrors[2], simplex, startingPoint ) ); polyFacets.RemoveAll( f => f == null ); HashSet<Vector3D> completedFacetIds = new HashSet<Vector3D>(); foreach( H3.Cell.Facet f in polyFacets ) completedFacetIds.Add( f.ID ); Recurse.GenPolyhedron( mirrors.Select( m => simplex.Facets[m] ).ToArray(), polyFacets.ToArray(), polyFacets, completedFacetIds ); H3.Cell cell = new H3.Cell( polyFacets.ToArray() ); // Calc a center. XXX - totally wrong. Vector3D center = new Vector3D(); foreach( Vector3D v in cell.Verts ) center += v; center /= cell.Verts.Count(); cell.Center = center; // Calc the spheres. foreach( H3.Cell.Facet f in cell.Facets ) f.CalcSphereFromVerts( g ); return cell; }
// 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. private static Vector3D IterateToStartingPoint( 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 ); 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(); // 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(); } } return HyperbolicModels.KleinToPoincare( baryToEuclidean( kleinVerts, bary ) ); }
private static H3.Cell.Facet GenFacet( Geometry g, int mirror1, int mirror2, Simplex simplex, Vector3D startingPoint ) { List<Sphere> mirrors = new List<Sphere>(); mirrors.Add( simplex.Facets[mirror1] ); mirrors.Add( simplex.Facets[mirror2] ); List<H3.Cell.Edge> startingEdges = new List<H3.Cell.Edge>(); Vector3D reflected = simplex.ReflectInFacet( startingPoint, mirror1 ); startingEdges.Add( new H3.Cell.Edge( startingPoint, reflected ) ); reflected = simplex.ReflectInFacet( startingPoint, mirror2 ); startingEdges.Add( new H3.Cell.Edge( startingPoint, reflected ) ); startingEdges.RemoveAll( e => e.Start == e.End ); if( startingEdges.Count == 0 ) return null; H3.Cell.Edge[] completedEdges = Recurse.CalcEdges( mirrors.ToArray(), startingEdges.ToArray(), new Recurse.Settings() { G = g } ); if( completedEdges.Length == 1 ) return null; List<Vector3D> facetVerts = new List<Vector3D>(); H3.Cell.Edge edge = completedEdges.First(); Vector3D start = edge.Start; Vector3D current = edge.End; facetVerts.Add( edge.End ); while( current != start ) { edge = completedEdges.First( e => e != edge && ( e.Start == current || e.End == current ) ); current = edge.Start == current ? edge.End : edge.Start; facetVerts.Add( current ); } return new H3.Cell.Facet( facetVerts.ToArray() ); }
public static void Paracompact( HoneycombDef def, int[] active, int baseHue ) { string baseName = BaseName( def ); string mirrorsString = ActiveMirrorsString( active ); string suffix = "-" + mirrorsString; string fileName = baseName + suffix; if( File.Exists( fileName + ".pov" ) ) { Console.WriteLine( string.Format( "Skipping {0}", fileName ) ); return; } Console.WriteLine( string.Format( "Building {0}", fileName ) ); CalcThickness( active ); // The wiki mirrors are labeled in the reverse of ours. Func<int, int> mapMirror = i => 3 - i; active = active.Select( i => mapMirror( i ) ).OrderBy( i => i ).ToArray(); Simplex simplex = new Simplex(); simplex.Facets = SimplexCalcs.Mirrors( def.P, def.Q, def.R ); simplex.Verts = SimplexCalcs.VertsBall( def.P, def.Q, def.R ); Vector3D startingPoint = IterateToStartingPoint( active, simplex ); if( startingPoint.DNE ) return; List<H3.Cell.Edge> startingEdges = new List<H3.Cell.Edge>(); foreach( int a in active ) { Vector3D reflected = simplex.ReflectInFacet( startingPoint, a ); startingEdges.Add( new H3.Cell.Edge( startingPoint, reflected ) ); } SetupBaseHue( fileName, mirrorsString, baseHue ); Recurse.m_background = new Vector3D( baseHue, 1, .1 ); H3.Cell.Edge[] edges = Recurse.CalcEdgesSmart2( simplex.Facets, startingEdges.ToArray() ); H3.SaveToFile( fileName, edges, finite: true, append: true ); }
/// <summary> /// This generates a honeycomb by reflecting in 4 mirrors of the fundamental simplex. /// </summary> public static void OneHoneycombNew( HoneycombDef imageData ) { int p = imageData.P; int q = imageData.Q; int r = imageData.R; double thickness = 0.1; double thicknessSpherical = Spherical2D.s2eNorm( thickness ); double thicknessHyperbolic = R3.Math.DonHatch.h2eNorm( thickness ); double threshold = 1; H3.Cell.Edge[] edges = null; H3.Cell[] cellsToHighlight = null; Sphere[] simplex = null; Vector3D vertex = new Vector3D(); Geometry g = Util.GetGeometry( p, q, r ); if( g == Geometry.Spherical ) { thickness = thicknessSpherical /*.07 for 333*/ /* 0.05for 433*/ /*.025 for 533,335*/; threshold = 10000; simplex = SimplexCalcs.MirrorsSpherical( p, q, r ); vertex = SimplexCalcs.VertexSpherical( p, q, r ); // Ugly special casing for 333, since it has a vertex project to infinity. if( p == 3 && q == 3 && r == 3 ) SpecialCase333(); } else if( g == Geometry.Euclidean ) { thickness = thickness / 2; threshold = 5/*20*/; SimplexCalcs.CalcEScale(); simplex = SimplexCalcs.MirrorsEuclidean(); Vector3D[] verts = SimplexCalcs.VertsEuclidean(); vertex = verts[2]; } else { thickness = thicknessHyperbolic; threshold = 0.01; simplex = SimplexCalcs.Mirrors( p, q, r ); Vector3D[] verts = SimplexCalcs.VertsBall( p, q, r ); vertex = verts[2]; //Vector3D[] simplexVerts = SimplexCalcs.VertsBall( p, q, r ); //H3.Cell.Edge edge = new H3.Cell.Edge( simplexVerts[2], simplexVerts[3] ); //H3.Cell.Edge edge = SimplexCalcs.HoneycombEdgeBall( p, q, r ); //H3.Cell.Edge[] startingEdges = new H3.Cell.Edge[] { edge }; //H3.Cell.Edge[] edges = Recurse.CalcEdgesSmart2( simplex, startingEdges ); // Vertex Centered. bool vertexCentered = false; if( vertexCentered ) { Vector3D v = SimplexCalcs.VertexPointBall( p, q, r ); v = H3Models.BallToUHS( v ); double scale = 1.0 / v.Abs(); edges = edges.Select( e => { Vector3D start = H3Models.UHSToBall( H3Models.BallToUHS( e.Start ) * scale ); Vector3D end = H3Models.UHSToBall( H3Models.BallToUHS( e.End ) * scale ); return new H3.Cell.Edge( start, end ); } ).ToArray(); } // Code to show endpoints of 535 /*using( StreamWriter sw = File.CreateText( "535_points.pov" ) ) { HashSet<Vector3D> verts = new HashSet<Vector3D>(); foreach( H3.Cell.Edge e in edges ) { verts.Add( Sterographic.SphereToPlane( e.Start ) ); verts.Add( Sterographic.SphereToPlane( e.End ) ); } foreach( Vector3D vert in verts ) if( !Infinity.IsInfinite( vert ) ) sw.WriteLine( PovRay.Sphere( new Sphere() { Center = vert, Radius = 0.01 } ) ); }*/ } // Recurse bool dual = false; { H3.Cell.Edge[] startingEdges = null; if( dual ) startingEdges = new H3.Cell.Edge[] { SimplexCalcs.DualEdgeBall( simplex ) }; else startingEdges = new H3.Cell.Edge[] { SimplexCalcs.HoneycombEdgeBall( simplex, vertex ) }; edges = Recurse.CalcEdges( simplex, startingEdges, new Recurse.Settings() { G = g, Threshold = threshold } ); //CullHalfOfEdges( ref edges ); // No need to cull edges in spherical case. // This was just to generate some images for 350-cell paper. //edges = Cull120Cell( edges ); Simplex tet = new Simplex(); tet.Facets = simplex; if( dual ) { H3.Cell.Edge[] oneDualCell = edges.Where( e => e.Depths[2] == 0 ).ToArray(); simplex = simplex.Skip( 1 ).ToArray(); edges = Recurse.CalcEdges( simplex, oneDualCell, new Recurse.Settings() { G = g, Threshold = threshold } ); int[] polyMirrors = new int[] { 0, 1, 3 }; H3.Cell startingCell = PolyhedronToHighlight( g, polyMirrors, tet, new Vector3D() ); cellsToHighlight = Recurse.CalcCells( simplex, new H3.Cell[] { startingCell } ); //cellsToHighlight = new H3.Cell[] { startingCell }; //cellsToHighlight = cellsToHighlight.Skip( 7 ).ToArray(); } else { int[] polyMirrors = new int[] { 1, 2, 3 }; H3.Cell startingCell = PolyhedronToHighlight( g, polyMirrors, tet, vertex ); //cellsToHighlight = Recurse.CalcCells( simplex, new H3.Cell[] { startingCell } ); cellsToHighlight = new H3.Cell[] { startingCell }; } // Include just one cell? bool includeOne = false; if( includeOne ) { edges = edges.Where( e => e.Depths[0] == 0 ).ToArray(); //cellsToHighlight = cellsToHighlight.Where( c => c.Depths[0] == 0 ).ToArray(); } } // Write the file bool pov = false; if( pov ) { string filename = string.Format( "{0}{1}{2}.pov", p, q, r ); PovRay.WriteEdges( new PovRay.Parameters() { AngularThickness = thickness }, g, edges, filename, append: false ); //File.Delete( filename ); //PovRay.AppendFacets( cellsToHighlight, filename ); HashSet<Vector3D> verts = new HashSet<Vector3D>(); foreach( H3.Cell.Edge e in edges ) { verts.Add( e.Start ); verts.Add( e.End ); } foreach( Vector3D v in verts ) { Vector3D t = v; t.Normalize(); t *= 0.9; System.Diagnostics.Trace.WriteLine( string.Format( "light_source {{ <{0},{1},{2}> White*.2 }}", t.X, t.Y, t.Z ) ); } /* // Include the standard pov stuff, so we can batch this. string fileName = imageData.FormatFilename( string.Empty ); using( StreamWriter sw = File.CreateText( fileName + ".pov" ) ) { sw.WriteLine( "#include \"C:\\Users\\hrn\\Documents\\roice\\povray\\paper\\H3.pov\"" ); } bool dummy = true; // Doesn't matter for Pov-Ray, just Shapeways meshes. H3.SaveToFile( fileName, edges, dummy, append: true ); */ } else { if( g == Geometry.Spherical ) { edges = edges.Where( e => e.Start.Valid() && e.End.Valid() && !Infinity.IsInfinite( e.Start ) && !Infinity.IsInfinite( e.End ) ).ToArray(); S3.EdgesToStl( edges ); } else throw new System.NotImplementedException(); } }
/// <summary> /// Wendy's 77 /// </summary> public static void Wendy( Simplex simplex, H3.Cell.Edge[] edges ) { H3.Cell startingCell = null; Vector3D start = startingCell.Verts.First(); Func<Vector3D, Vector3D> findAntipode = input => { Vector3D antipode = new Vector3D(); double max = double.MinValue; foreach( Vector3D v in startingCell.Verts ) { double d = H3Models.Ball.HDist( v, input ); if( d > max ) { max = d; antipode = v; } } return antipode; }; H3.Cell.Edge[] diagonals = new H3.Cell.Edge[] { new H3.Cell.Edge( start, findAntipode( start ) ) }; diagonals = Recurse.CalcEdges( simplex.Facets, diagonals, new Recurse.Settings() { Threshold = 0.9983 } ); // diagonals includes too much at this point (it includes all icosahedra diagonals, but we only want one diagonal from each cell). // We need to begin at 4 start points, and branch out from each to find the ones we want. var vertsToDiagonals = FindConnectedEdges( diagonals ); var connectedEdges = FindConnectedEdges( edges ); // Get all edges (not diagonals) connected to start. List<H3.Cell.Edge> connectedToStart = connectedEdges[start]; Vector3D startOpp = connectedToStart[0].Opp( start ); List<H3.Cell.Edge> connectedToStartOpp = connectedEdges[startOpp]; // We need to pick 4 of these edges, arranged in a tetrahedron for our starting points. List<Vector3D> startingPoints = new List<Vector3D>(); List<double> distances = new List<double>(); // View1 //startingPoints.Add( start ); //foreach( Vector3D v in connectedToStartOpp.Select( e => e.Opp( startOpp ) ) ) // distances.Add( H3Models.Ball.HDist( startingPoints.First(), v ) ); //startingPoints.Add( connectedToStartOpp[10].Opp( startOpp ) ); //startingPoints.Add( connectedToStartOpp[13].Opp( startOpp ) ); //startingPoints.Add( connectedToStartOpp[14].Opp( startOpp ) ); // View2 startingPoints.Add( startOpp ); foreach( Vector3D v in connectedToStart.Select( e => e.Opp( start ) ) ) distances.Add( H3Models.Ball.HDist( startingPoints.First(), v ) ); startingPoints.Add( connectedToStart[10].Opp( start ) ); startingPoints.Add( connectedToStart[13].Opp( start ) ); startingPoints.Add( connectedToStart[14].Opp( start ) ); distances.Clear(); distances.Add( H3Models.Ball.HDist( startingPoints[1], startingPoints[2] ) ); distances.Add( H3Models.Ball.HDist( startingPoints[1], startingPoints[3] ) ); distances.Add( H3Models.Ball.HDist( startingPoints[2], startingPoints[3] ) ); distances.Add( H3Models.Ball.HDist( startingPoints[0], startingPoints[1] ) ); distances.Add( H3Models.Ball.HDist( startingPoints[0], startingPoints[2] ) ); distances.Add( H3Models.Ball.HDist( startingPoints[0], startingPoints[3] ) ); double dist = 3.097167; Func<Vector3D[], H3.Cell.Edge[]> RemoveVerts = starting => { List<H3.Cell.Edge> keepers = new List<H3.Cell.Edge>(); HashSet<Vector3D> removedVerts = new HashSet<Vector3D>(); Recurse.BranchAlongVerts( starting.ToArray(), vertsToDiagonals, removedVerts ); foreach( H3.Cell.Edge e in edges ) { if( removedVerts.Contains( e.Start ) || removedVerts.Contains( e.End ) ) continue; keepers.Add( e ); } return keepers.ToArray(); }; edges = RemoveVerts( startingPoints.ToArray() ); bool done = false; while( !done ) { done = true; var newConnectedEdges = FindConnectedEdges( edges ); foreach( Vector3D v in newConnectedEdges.Keys ) { List<H3.Cell.Edge> oldEdgeList = connectedEdges[v]; List<H3.Cell.Edge> newEdgeList = newConnectedEdges[v]; // Only work edges that were full originally. if( oldEdgeList.Count != 20 ) continue; // We need at least two to find the rest. int newCount = newEdgeList.Count; if( newCount > 16 && newCount < 19 ) { List<H3.Cell.Edge> removed = oldEdgeList.Except( newEdgeList, new H3.Cell.EdgeEqualityComparer() ).ToList(); H3.Cell.Edge[] toTrim = newEdgeList.FindAll( e => { foreach( H3.Cell.Edge alreadyRemoved in removed ) { double d = H3Models.Ball.HDist( alreadyRemoved.Opp( v ), e.Opp( v ) ); if( !Tolerance.Equal( dist, d, 0.00001 ) ) return false; } return true; } ).ToArray(); edges = RemoveVerts( toTrim.Select( e => e.Opp( v ) ).ToArray() ); done = false; } if( newCount == 20 ) done = false; } } }