public ReflectInFacet ( Vector3D v, int facet ) : Vector3D | ||
v | Vector3D | |
facet | int | |
리턴 | Vector3D |
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.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() ); }
// 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 ) ); }
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 ); }