public void Gen() { Vector3D[] verts = Verts(m_m, m_k); Sphere[] tet = Tetrahedron(verts); Quad quad = new Quad() { Verts = verts }; // We need to avoid infinities. //tet = tet.Select( s => H3Models.UHSToBall( s ) ).ToArray(); //for( int i=0; i<quad.Verts.Length; i++ ) // quad.Verts[i] = H3Models.UHSToBall( quad.Verts[i] ); // Reflect it around. //Quad[] quads = new Quad[] { quad }; Quad[] quads = CalcQuads(tet, quad); List <H3.Cell.Edge> edges = new List <H3.Cell.Edge>(); foreach (Quad q in quads) { q.R3toS3(); edges.AddRange(q.GenEdges()); } string filename = string.Format("lawson_{0}_{1}.pov", m_m, m_k); PovRay.WriteEdges(new PovRay.Parameters() { AngularThickness = 0.01 }, Geometry.Spherical, edges.ToArray(), filename, append: false); }
public static void HopfOrbit() { List <Vector3D> s2Points = new List <Vector3D>(); for (double theta = Math.PI * .1; theta <= Math.PI * .9; theta += Math.PI * .2) { for (double lon = -Math.PI; lon <= Math.PI; lon += Math.PI / 10) { s2Points.Add(SphericalCoords.SphericalToCartesian(new Vector3D(1.0, theta, lon))); } } using (StreamWriter sw = File.CreateText(@".\out.pov")) { System.Func <Vector3D, Sphere> sizeFunc = v => new Sphere() { Center = v, Radius = 0.01 }; foreach (Vector3D s2Point in s2Points) { Vector3D[] circlePoints = OneHopfCircle(s2Point); //for( int i = 0; i < circlePoints.Length; i++ ) // circlePoints[i] = circlePoints[i].ProjectTo3DSafe( 1.0 ); // Note: effectively orthogonal projects here because EdgeSphereSweep doesn't write W coord. string circleString = PovRay.EdgeSphereSweep(circlePoints, sizeFunc); sw.WriteLine(circleString); } } }
private string StartRenderVaryRatio(string dirname) { var anchorsFilename = GetAnchorsFilename(); Utility.CreateDirectory(dirname, Settings.Calculation.Overwrite); WriteAnchorsFile(dirname); var dataFiles = new List <string>(); var minR = Settings.Calculation.RatioMin; var maxR = Settings.Calculation.RatioMax; for (var i = 0; i < Settings.Calculation.FrameCount; i++) { var step = i / (double)Settings.Calculation.FrameCount; var r = maxR * step + minR; Settings.Calculation.Ratio = r; //var file = StartRender(dirname); var file = WriteDataPoints(dirname); dataFiles.Add(file); } var povFile = PovRay.PreparePovRayFilesWithIni(Settings, dataFiles, anchorsFilename, dirname); Console.WriteLine("Written " + povFile); return(povFile); }
/// <summary> /// This was used during batching. /// </summary> private static void SetupBaseHue(string fileName, string mirrorsString, int baseHue) { return; // 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); using (StreamWriter sw = File.CreateText(fileName + ".pov")) { if (baseHue == -1) { sw.WriteLine("background { rgb 1 }"); } else { sw.WriteLine(string.Format("background {{ CHSL2RGB( <{0}, 1, .1> ) }}", hue)); } if (ViewPath != null) { sw.WriteLine(string.Format("#declare lookAt = {0};", PovRay.FormatVec(ViewPath.LookAt))); } // This needs to come last, since it relies on the lookAt. sw.WriteLine("#include \"D:\\roice\\povray\\H3_paracompact.pov\""); } }
/// <summary> /// Hopf Link between two points on S^2. /// </summary> public static void HopfLink(StreamWriter sw, Vector3D s2_1, Vector3D s2_2, bool anti) { Vector3D[] circlePoints; string circleString; circlePoints = OneHopfCircleProjected(s2_1, anti); circleString = PovRay.EdgeSphereSweep(circlePoints, SizeFunc); sw.WriteLine(circleString); circlePoints = OneHopfCircleProjected(s2_2, anti); circleString = PovRay.EdgeSphereSweep(circlePoints, SizeFunc); sw.WriteLine(circleString); Mesh mesh = new Mesh(); Vector3D[] interpolated = S3.GeodesicPoints(s2_1, s2_2); for (int i = 0; i < interpolated.Length - 1; i++) { Vector3D v1 = interpolated[i]; Vector3D v2 = interpolated[i + 1]; Vector3D[] p1 = OneHopfCircleProjected(v1, anti); Vector3D[] p2 = OneHopfCircleProjected(v2, anti); for (int j = 0; j < p1.Length - 1; j++) { Mesh.Triangle t1 = new Mesh.Triangle(p1[j], p1[j + 1], p2[j]); Mesh.Triangle t2 = new Mesh.Triangle(p2[j], p1[j + 1], p2[j + 1]); mesh.Triangles.Add(t1); mesh.Triangles.Add(t2); } } PovRay.WriteMesh(sw, mesh, append: true); }
public static void Cell633() { TilingConfig config = new TilingConfig(6, 3, maxTiles: 20000); Tiling tiling = new Tiling(); tiling.GenerateInternal(config, Polytope.Projection.VertexCentered); double edgeLength = Honeycomb.EdgeLength(6, 3, 3); double z = 0.25; double offset = H3Models.UHS.ToEHorizontal(edgeLength, z); double scale = offset / tiling.Tiles.First().Boundary.Segments.First().Length; foreach (Tile tile in tiling.Tiles) { tile.Transform(Mobius.Scale(scale)); } Vector3D dummy; double radius; H3Models.UHS.Geodesic(new Vector3D(0, 0, z), new Vector3D(scale, 0, z), out dummy, out radius); Vector3D midradius = H3Models.UHSToBall(new Vector3D(0, 0, radius)); double temp = midradius.Z; double temp2 = (1 - temp) / 2; double temp3 = temp + temp2; double temp4 = temp3; Vector3D circumradius = H3Models.UHSToBall(new Vector3D(0, 0, z)); temp = circumradius.Z; temp2 = (1 - temp) / 2; temp3 = temp + temp2; temp4 = temp3; // Checking /* * Vector3D test = new Vector3D( offset, 0, z ); * test = H3Models.UHSToBall( test ); * double edgeLength2 = DonHatch.e2hNorm( test.Abs() ); * edgeLength2 += 0; */ HashSet <H3.Cell.Edge> edges = new HashSet <H3.Cell.Edge>(); foreach (Tile tile in tiling.Tiles) { foreach (Segment seg in tile.Boundary.Segments) { H3.Cell.Edge edge = new H3.Cell.Edge( H3Models.UHSToBall(seg.P1 + new Vector3D(0, 0, z)), H3Models.UHSToBall(seg.P2 + new Vector3D(0, 0, z))); edges.Add(edge); } } PovRay.WriteH3Edges(new PovRay.Parameters(), edges.ToArray(), "edges.pov"); }
public void GenPovRay() { //H3.Cell.Edge[] fibers = Helicoid(); H3.Cell.Edge[] fibers = Hyperboloid(); PovRay.WriteH3Edges(new PovRay.Parameters { AngularThickness = 0.015 }, fibers, "ruled.pov"); }
private void AppendMesh(List <H3.Cell.Edge> fiberList, string filename) { Mesh mesh = new Mesh(); for (int j = 0; j < fiberList.Count - 1; j++) { AddRow(mesh, fiberList[j], fiberList[j + 1]); } //AddRow( mesh, fiberList.Last(), fiberList.First() ); PovRay.WriteMesh(mesh, filename, append: true); }
// https://plus.google.com/u/0/117663015413546257905/posts/BnCEkdNiTZ2 public static void TwinDodecs() { Tiling tiling = new Tiling(); TilingConfig config = new TilingConfig(5, 3); tiling.GenerateInternal(config, Polytope.Projection.VertexCentered); // Vertex-centered makes infinities tricky Dodec dodec = new Dodec(); foreach (Tile tile in tiling.Tiles) { foreach (Segment seg in tile.Boundary.Segments) { Vector3D p1 = seg.P1, p2 = seg.P2; if (Infinity.IsInfinite(p1)) { p1 = Infinity.InfinityVector; } if (Infinity.IsInfinite(p2)) { p2 = Infinity.InfinityVector; } dodec.Verts.Add(p1); dodec.Verts.Add(p2); dodec.Midpoints.Add(Halfway(p1, p2)); } } // Now recursively add more vertices. HashSet <Vector3D> allVerts = new HashSet <Vector3D>(); foreach (Vector3D v in dodec.Verts) { allVerts.Add(v); } RecurseTwins(allVerts, dodec, 0); using (StreamWriter sw = File.CreateText("dual_dodecs_points_sphere.pov")) { foreach (Vector3D vert in allVerts) { Vector3D onSphere = Sterographic.PlaneToSphereSafe(vert); sw.WriteLine(PovRay.Sphere(new Sphere() { Center = onSphere, Radius = 0.01 })); //if( !Infinity.IsInfinite( vert ) ) // sw.WriteLine( PovRay.Sphere( new Sphere() { Center = vert, Radius = 0.01 } ) ); } } }
public static void Create(HoneycombDef def, string filename) { int p = def.P; int q = def.Q; int r = def.R; double scale = 5.0; Vector3D cen = HoneycombPaper.InteriorPointBall; Sphere[] simplex = SimplexCalcs.Mirrors(p, q, r, moveToBall: false); // Apply transformations. simplex = simplex.Select(s => { 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 = HoneycombPaper.AutoCalcScale(def, simplexForColorScale); int maxDepth = (int)temp.ColorScaling; bool ball = true; bool dual = false; H3.Cell[] simplicesFinal = HoneycombPaper.GenCell(simplex, null, cen, ball, dual); simplicesFinal = simplicesFinal.Where(s => s.Depths[0] < 1).ToArray(); //simplicesFinal = simplicesFinal.Where( s => s.) // Output the facets. 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 }; foreach (H3.Cell cell in simplicesFinal) { Sphere[] facets = cell.Facets.Select(f => f.Sphere).ToArray(); int depth = cell.Depths[0] + 1; Color c = Coloring.ColorAlongHexagon(maxDepth, depth); PovRay.AddSimplex(sw, facets, cell.Center, include, filename, Coloring.ToVec(c)); } } }
private string StartRenderWithAngle(string dirname) { Utility.CreateDirectory(dirname, Settings.Calculation.Overwrite); var anchorsFilename = GetAnchorsFilename(); WriteAnchorsFile(dirname); var datapointFiles = VaryAngle.WriteDataPointsVaryAngle(ShapeName, Settings, AnchorPoints, dirname); var povFile = PovRay.PreparePovRayFilesWithIni(Settings, datapointFiles, anchorsFilename, dirname); return(povFile); }
public static void Hypercube() { List <Vector3D> vertices = new List <Vector3D>(); vertices.Add(new Vector3D(1, 1, 1, 1)); vertices.Add(new Vector3D(1, 1, 1, -1)); vertices.Add(new Vector3D(1, 1, -1, 1)); vertices.Add(new Vector3D(1, 1, -1, -1)); vertices.Add(new Vector3D(1, -1, 1, 1)); vertices.Add(new Vector3D(1, -1, 1, -1)); vertices.Add(new Vector3D(1, -1, -1, 1)); vertices.Add(new Vector3D(1, -1, -1, -1)); vertices.Add(new Vector3D(-1, 1, 1, 1)); vertices.Add(new Vector3D(-1, 1, 1, -1)); vertices.Add(new Vector3D(-1, 1, -1, 1)); vertices.Add(new Vector3D(-1, 1, -1, -1)); vertices.Add(new Vector3D(-1, -1, 1, 1)); vertices.Add(new Vector3D(-1, -1, 1, -1)); vertices.Add(new Vector3D(-1, -1, -1, 1)); vertices.Add(new Vector3D(-1, -1, -1, -1)); HashSet <H3.Cell.Edge> edges = new HashSet <H3.Cell.Edge>(new H3.Cell.EdgeEqualityComparer()); foreach (Vector3D v1 in vertices) { foreach (Vector3D v2 in vertices) { if (v1.Dist(v2) == 2) { edges.Add(new H3.Cell.Edge(v1, v2)); } } } // Radial project to S3, then stereographic to R3. foreach (H3.Cell.Edge edge in edges) { edge.Start.Normalize(); edge.Start = Sterographic.S3toR3(edge.Start); edge.End.Normalize(); edge.End = Sterographic.S3toR3(edge.End); } PovRay.WriteEdges(new PovRay.Parameters() { AngularThickness = .05 }, Geometry.Spherical, edges.ToArray(), "433.pov", append: false); }
private string StartRenderNoRepeatNearest(string dirname) { var dataPointsFilename = Utility.GetDatapointsFilename(ShapeName, Settings); var anchorsFilename = GetAnchorsFilename(); Utility.CreateDirectory(dirname, Settings.Calculation.Overwrite); WriteAnchorsFile(dirname); NoRepeatNearest.WriteDataPointsNoRepeatAnchor(Settings, AnchorPoints, dirname, dataPointsFilename); var dataFiles = new List <string> { dataPointsFilename }; var povFile = PovRay.PreparePovRayFilesWithIni(Settings, dataFiles, anchorsFilename, dirname); Console.WriteLine("Written " + povFile); return(dataPointsFilename); }
public static void ElevenCell() { Graph g = new Graph(); g.SetupCompleteGraph(11); GraphRelaxation relaxer = new GraphRelaxation(); relaxer.Graph = g; relaxer.NodeRepulsion = 0.01; relaxer.EdgeAttraction = 0.0; relaxer.EdgeRepulsion = 0.0; relaxer.Relax(20000); Vector3D northPole = new Vector3D(0, 0, 0, 1); H3.Cell.Edge[] edges = g.Edges.Select(e => { Vector3D v1 = g.Nodes[e.V1].Position.ToVec3D(); Vector3D v2 = g.Nodes[e.V2].Position.ToVec3D(); //if( v1.Compare( northPole, .5 ) || v2.Compare( northPole, 0.5 ) ) // Cull edges near north pole. // return null; v1 = Sterographic.S3toR3(v1); v2 = Sterographic.S3toR3(v2); return(new H3.Cell.Edge(v1, v2)); }).ToArray(); edges = edges.Where(e => e != null).ToArray(); bool povray = false; if (povray) { string filename = "test.pov"; System.IO.File.Delete(filename); using (StreamWriter sw = new StreamWriter(filename)) sw.WriteLine("#include \"C:\\Users\\hrn\\Documents\\roice\\povray\\H3\\horosphere\\633.pov\""); PovRay.WriteEdges(new PovRay.Parameters { AngularThickness = 0.03 }, Geometry.Spherical, edges, filename, append: true); } else { S3.EdgesToStl(edges); } }
public void Generate(int X, int Y, int Z) { // The starting 3 polygons, connected up at a vertex. Polygon[] starting = GenerateStarting(X, Y, Z); starting.ToList().ForEach(p => AddPoly(p)); for (int i = 0; i < 20; i++) { OneChain(); } //m_polygons = m_polygons.Skip( 55 ).Take(3).ToList(); // Write it out so we can see it. File.Delete("test.pov"); PovRay.AppendEuclideanPolygons(m_polygons.ToArray(), "test.pov"); }
void WriteFibers(List <H3.Cell.Edge> fiberList, string fileName) { H3.Cell.Edge[] fibers = fiberList.Where(f => f != null).ToArray(); if (Ball) { PovRay.WriteH3Edges(new PovRay.Parameters { AngularThickness = 0.020 }, fibers, fileName, append: true); } else { PovRay.WriteH3Edges(new PovRay.Parameters { AngularThickness = 0.010, Halfspace = true }, fibers, fileName, append: true); } }
public static void Polarity() { TilingConfig config = new TilingConfig(3, 7, 50); Tiling tiling = new Tiling(); tiling.GenerateInternal(config); List <Vector3D> points = new List <Vector3D>(); List <H3.Cell.Edge> edges = new List <H3.Cell.Edge>(); foreach (Polygon p in tiling.Tiles.Select(t => t.Boundary)) { foreach (Segment s in p.Segments) { foreach (Vector3D v in s.Subdivide(25)) { Vector3D klein = HyperbolicModels.PoincareToKlein(v); H3.Cell.Edge e = Dual(klein); points.Add(klein); edges.Add(e); } } } using (StreamWriter sw = File.CreateText("polarity.pov")) { double rad = 0.01; foreach (Vector3D vert in points) { sw.WriteLine(PovRay.Sphere(new Sphere() { Center = vert, Radius = rad })); } foreach (H3.Cell.Edge edge in edges) { sw.WriteLine(PovRay.Cylinder(edge.Start, edge.End, rad / 2)); } } }
public static void RLD_Surface() { RLD_outputs outputs; Mesh mesh = new Mesh(); SurfaceInternal(out outputs); double scale = m_params.Scale; // Now add in all the catenoids. double mInc = Math.PI * 2 / m_params.M; for (int k = 1; k < outputs.x_i.Length; k++) { for (int m = 0; m < m_params.M; m++) { Vector3D loc = SphericalCoords.SphericalToCartesian(new Vector3D(1, Math.PI / 2 - outputs.x_i[k], m * mInc)); mesh.Append(Catenoid(scale, loc, outputs.phi_i[k], outputs.t_i[k])); } } PovRay.WriteMesh(mesh, "RLD.pov"); }
public static void PovRayModel() { Vector3D cen = HoneycombPaper.InteriorPointBall; cen = H3Models.BallToUHS(cen); Sphere[] simplex = Simplex(ref cen); H3.Cell[] simplicesFinal = GenTruss(simplex, null, cen, ball: true); string fileName = "350cell.pov"; System.IO.File.Delete(fileName); foreach (H3.Cell cell in simplicesFinal) { //int[] include = new int[] { 0, 1, 2, 3 }; int[] include = new int[] { 0 }; PovRay.AppendSimplex(cell.Facets.Select(f => f.Sphere).ToArray(), cell.Center, include, fileName); } /* * HashSet<H3.Cell.Edge> axes = new HashSet<H3.Cell.Edge>( new H3.Cell.EdgeEqualityComparer() ); * H3.Cell.Edge axis = new H3.Cell.Edge( new Vector3D( 0, 0, -1 ), new Vector3D( 0, 0, 1 ) ); * axes.Add( axis ); * foreach( int[] reflections in TrussReflections() ) * { * Vector3D s = axis.Start, e = axis.End; * foreach( int ri in reflections ) * { * s = simplex[ri].ReflectPoint( s ); * e = simplex[ri].ReflectPoint( e ); * } * axes.Add( new H3.Cell.Edge( s, e ) ); * } * PovRay.WriteH3Edges( new PovRay.Parameters { AngularThickness = 0.045 }, axes.ToArray(), "axes.pov" ); * * H3.Cell.Edge[] fibers = new H3.Cell.Edge[] { axis }; * fibers = Recurse.CalcEdges( simplex.Skip(1).ToArray(), fibers ); * PovRay.WriteH3Edges( new PovRay.Parameters { AngularThickness = 0.025 }, fibers, "axes.pov" );*/ }
private static void CreateSimplex(HoneycombDef imageData) { int p = imageData.P; int q = imageData.Q; int r = imageData.R; Vector3D cen = InteriorPointBall; bool ball = true; Sphere[] simplex = SimplexCalcs.Mirrors(p, q, r, ref cen, moveToBall: ball); // Offset as we do for the boundary images. //Sphere s = H3Models.UHSToBall( simplex[0] ); //s = CoxeterImages.GeodesicOffset( s, 0.02, ball: true ); if (m_toKlein) { simplex = simplex.Select(s => H3Models.BallToKlein(s)).ToArray(); } int[] include = new int[] { 0, 1, 2, 3 }; // All facets //int[] include = new int[] { 1 }; File.Delete("simplex.pov"); PovRay.AppendSimplex(simplex, cen, include, "simplex.pov"); bool includeEdges = false; if (includeEdges) { H3.Cell.Edge[] edges = SimplexCalcs.SimplexEdgesUHS(p, q, r); PovRay.WriteEdges(new PovRay.Parameters { Halfspace = true, AngularThickness = 0.03 }, Geometry.Hyperbolic, edges, "simplex.pov", append: true); } }
/// <summary> /// This is like the GenCell method, but super hacked up for the Catacombs image with Henry. /// </summary> internal static void GenCellCatacombs(Sphere[] simplex, bool ball) { // We don't want to include the first mirror (which reflects across cells). Sphere[] mirrors = simplex.Skip(1).ToArray(); Sphere[] allMirrors = simplex.ToArray(); // Simplices will be the "cells" in Recurse.CalcCells. H3.Cell.Facet[] simplexFacets = simplex.Select(m => new H3.Cell.Facet(m)).ToArray(); // Offset cell boundary ever so slightly, to avoid artifacts of adjacent cells. Sphere toReflectLater = simplexFacets[0].Sphere.Clone(); //simplexFacets[0].Sphere = CoxeterImages.GeodesicOffset( simplexFacets[0].Sphere, ball ? -1e-6 : 1e-7, ball ); H3.Cell startingCell = new H3.Cell(simplexFacets); startingCell = startingCell.Clone(); // So our mirrors don't get munged after we reflect around later. Vector3D cen = new Vector3D(0.05, 0.01, -0.05); // 373, 438 //Vector3D cen = new Vector3D( 0.05, 0.01, 100 ); // 637 //cen.RotateXY( Math.PI / 2 ); // only if we also rotate simplex mirrors. XXX - make a setting. startingCell.Center = cen; H3.Cell[] simplices = Recurse.CalcCells(mirrors, new H3.Cell[] { startingCell }, new Recurse.Settings() { Ball = ball }); List <H3.Cell> simplicesFinal = new List <H3.Cell>(); List <int[]> reflectionSets = new List <int[]>(); // 1 reflects in x-axis // 3, 1 rotates right // 1, 3 rotates left reflectionSets.Add(new int[] { 0, 3, 1, 3, 1, 0, 1, 3 }); reflectionSets.Add(new int[] { 0, 3, 1, 2, 3, 1, 0, 3, 1, 2, 0, 3 }); // 2 reflectionSets.Add(new int[] { 0, 3, 1, 3, 1, 3, 1, 0, 3, 1, 3, 1, 3, 1, 2, 3, 1, 3, 2, 3, 1 }); //reflectionSets.Add( new int[] { 0, 3, 1, 3, 1, 3, 1, 0, 3, 1, 3, 1, 0, 3, 1, 3, 1, 3, 1, 2, 3, 1, 3, 2, 3, 1 } ); // 3 //reflectionSets.Add( new int[] { 0, 3, 1, 3, 1, 3, 1, 0, 3, 1, 3, 1, 0, 3, 1, 2 } ); reflectionSets.Add(new int[] { 0, 3, 1, 3, 1, 3, 0, 3, 1, 3, 1, 0, 2 }); //reflectionSets.Add( new int[] { 0, 3, 1, 3, 1, 3, 1, 0, 3, 1, 2 } ); // 5 //reflectionSets.Add( new int[] { 0, 3, 1, 3, 1, 3, 1, 0, 1, 2, 3, 1 } ); //reflectionSets.Add( new int[] { 0, 3, 1, 3, 1, 0, 2, 3, 1 } ); //reflectionSets.Add( new int[] { 0, 3, 1, 3, 1, 0, 3, 1, 3, 1, 3, 1, 2, 3, 1, 3, 1, 0, 1, 3 } ); // baby //reflectionSets.Add( new int[] { 0, 3, 1, 3, 1, 0, 3, 1, 3, 1, 3, 1, 2 } ); // maybe //reflectionSets.Add( new int[] { 0, 3, 1, 2, 3, 1, 0, 2, 1, 3 } ); //reflectionSets.Add( new int[] { 0, 3, 1, 2, 3, 1, 0, 3, 1, 2 } ); // not great orientation // reflectionSets.Add( new int[] { 0, 3, 1, 3, 1, 2 } ); // big bool ceiling = true; if (ceiling) { simplicesFinal = simplices.ToList(); } else { foreach (int[] set in reflectionSets) { List <H3.Cell> copy = simplices.Select(s => s.Clone()).ToList(); foreach (int r in set) { foreach (H3.Cell cell in copy) { cell.Reflect(allMirrors[r]); } } simplicesFinal.AddRange(copy); } } /* * // A second cell. * //toReflectLater = simplices[45].Facets[0].Sphere.Clone(); * //toReflectLater = simplices.First( s => s.Depth == 2 ).Facets[0].Sphere.Clone(); * foreach( H3.Cell cell in simplices ) * cell.Reflect( toReflectLater ); * * // A third cell. * toReflectLater = simplices[40].Facets[0].Sphere.Clone(); * //toReflectLater = simplices.First( s => s.Depth == 4 ).Facets[0].Sphere.Clone(); * foreach( H3.Cell cell in simplices ) * cell.Reflect( toReflectLater ); * * foreach( H3.Cell cell in simplices ) * cell.Depths = new int[4]; * List<H3.Cell> simplicesFinal = Recurse.CalcCells2( mirrors, simplices ).ToList(); * simplicesFinal = simplicesFinal.Where( s => s.Depths[0] % 3 == 1 && s.Depths[1] % 2 == 0 && s.Depths[2] % 2 == 1 ).ToList(); */ /* * List<H3.Cell> simplicesFinal = new List<H3.Cell>(); * //for( int d = 0; d < 1; d+=2 ) * int d = 0; * { * //Sphere toReflect = simplices.First( s => s.Depth == d ).Facets[0].Sphere.Clone(); * //Sphere toReflect = simplices.Where( s => s.Depth == d ).Skip(1).Take(1).First().Facets[0].Sphere.Clone(); * List<H3.Cell> reflectionCells = simplices.Where( s => s.Depths[1] == d && s.Depths[0] % 2 == 0 ).Skip(0).Take(1).ToList(); * foreach( Sphere toReflect in reflectionCells.Select( c => c.Facets[0].Sphere.Clone() ) ) * { * List<H3.Cell> thisCell = new List<H3.Cell>(); * foreach( H3.Cell cell in simplices ) * { * H3.Cell clone = cell.Clone(); * clone.Reflect( toReflect ); * thisCell.Add( clone ); * } * * //Sphere toReflect2 = thisCell.First( s => s.Depth1 == d + 3 && s.Depth0 % 2 == 0 ).Facets[0].Sphere.Clone(); * //List<H3.Cell> reflectionCellsTemp = simplices.Where( s => Math.Abs( s.Depths[1] - d ) == 2 && s.Depths[0] % 2 == 0 ).ToList(); * List<H3.Cell> reflectionCellsTemp = simplices.Where( s => s.Depths[1] == 2 && s.Depths[1] == s.Depths[0] + s.Depths[2] ).ToList(); * List<H3.Cell> reflectionCells2 = reflectionCellsTemp;//.Where( ( x, i ) => i % 3 == 0 ).ToList(); // .Skip( 5 ).Take( 5 ).ToList(); * foreach( Sphere toReflect2 in reflectionCells2.Select( c => c.Facets[0].Sphere.Clone() ) ) * //Sphere toReflect2 = toReflectLater; * { * foreach( H3.Cell cell in thisCell ) * { * H3.Cell clone = cell.Clone(); * clone.Reflect( toReflect2 ); * simplicesFinal.Add( clone ); * } * } * } * }*/ int count = 0; foreach (H3.Cell cell in simplicesFinal) { count++; //if( count % 2 == 0 ) // continue; /*if( count < 1 ) * continue; * if( count > 30 ) * return; */ //int[] include = new int[] { 0, 1, 2, 3 }; int[] include = new int[] { 0 }; PovRay.AppendSimplex(cell.Facets.Select(f => f.Sphere).ToArray(), cell.Center, include, "cell.pov"); } }
/// <summary> /// This generates a honeycomb by reflecting in 4 mirrors of the fundamental simplex. /// This "new" method is now old. /// </summary> public static void OneHoneycombNew(HoneycombDef imageData) { int p = imageData.P; int q = imageData.Q; int r = imageData.R; double thickness = 0.05; 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 = 1 /*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 ) }; Vector3D[] verts = SimplexCalcs.VertsEuclidean(); Vector3D v1 = verts[0] + 2 * verts[2]; // adjacent cube center Vector3D corner = verts[3]; startingEdges = new H3.Cell.Edge[] { new H3.Cell.Edge(v1, corner) }; } edges = Recurse.CalcEdges(simplex, startingEdges, new Recurse.Settings() { G = g, Threshold = threshold }); edges = edges.Where(e => { int sum = e.Depths.Count(d => d == 0); return(true); }).ToArray(); //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 = HoneycombGen.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 = HoneycombGen.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(); } } // Rotate bool rotate = false; if (rotate) { CompoundOfFive24Cells(ref edges); } // Write the file bool pov = true; 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> /// Our approach will be: /// (1) Generate a portion of one cell. /// (2) Reflect all facets in the central facet, to get things filled-in inside the central facet. (Trim small edges here?) /// (3) Copy this region around the plane, and go back to step (2) if density is not high enough. /// (4) Map to Ball, trimming edges that become too small. /// NOTE: All verts are on the boundary, so we can reflect around // in circles on the plane at infinity, rather than spheres. /// </summary> public static void GenerateExotic(EHoneycomb honeycomb, H3.Settings settings) { settings.AngularThickness = 0.17; Tiling tiling; Tile baseTile; GetAssociatedTiling(honeycomb, out tiling, out baseTile); List <H3.Cell.Edge> edges = new List <H3.Cell.Edge>(); foreach (Segment seg in baseTile.Boundary.Segments) { edges.Add(new H3.Cell.Edge(seg.P1, seg.P2)); } settings.Position = Polytope.Projection.FaceCentered; double scale = 1; Vector3D offset = new Vector3D(); if (settings.Position == Polytope.Projection.FaceCentered) { scale = FaceCenteredScale(baseTile.VertexCircle); offset = new Vector3D(); } else if (settings.Position == Polytope.Projection.EdgeCentered) { scale = EdgeCenteredScale(baseTile); offset = baseTile.Boundary.Segments[0].Midpoint; } int iterations = m_params.Iterations; for (int i = 0; i < iterations; i++) { edges = DoOneStep(edges, tiling, baseTile.VertexCircle); } edges = CopyAndProject(edges, tiling, scale, offset); if (m_params.RemoveDangling) { Dictionary <H3.Cell.Edge, int> edgeDict = edges.ToDictionary(e => e, e => 1); H3.RemoveDanglingEdgesRecursive(edgeDict); edges = edgeDict.Keys.ToList(); } string outputFileName = H3.m_baseDir + Honeycomb.String(honeycomb, false); System.IO.File.Delete(outputFileName); if (m_params.Output == H3.Output.STL) { outputFileName += ".stl"; // Now mesh the edges. Shapeways mesh = new Shapeways(); foreach (H3.Cell.Edge edge in edges) { // Append to the file vs. writing out all at once because I was running out of memory otherwise. mesh = new Shapeways(); int div; H3Models.Ball.LODThin(edge.Start, edge.End, out div); mesh.Div = div; H3.Util.AddToMeshInternal(mesh, edge.Start, edge.End); mesh.Mesh.Scale(settings.Scale); STL.AppendMeshToSTL(mesh.Mesh, outputFileName); } } else { outputFileName += ".pov"; PovRay.WriteH3Edges(new PovRay.Parameters() { AngularThickness = settings.AngularThickness, Halfspace = settings.Halfspace, ThinEdges = settings.ThinEdges, }, edges.ToArray(), outputFileName); } }
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); } }
public static void ToPovRay(Sphere[] mirrors) { System.IO.File.Delete("simplex.pov"); PovRay.CreateSimplex(mirrors, "simplex.pov"); }
public void Gen(int p, int q, int r) { Geometry g = Util.GetGeometry(p, q, r); if (g != Geometry.Spherical) { throw new System.Exception("Point group code only for spherical geometry."); } Simplex simplex = new Simplex(); simplex.Facets = SimplexCalcs.Mirrors(p, q, r); Vector3D cen = new Vector3D(); Vector3D faceCenter = SimplexCalcs.FaceCenterSpherical(p, q, r); Vector3D edgeMid = SimplexCalcs.EdgeMidpointSpherical(p, q, r); Vector3D vertex = SimplexCalcs.VertexSpherical(p, q, r); List <Vector3D> startingPoles = new List <Vector3D>(); startingPoles.Add(Sterographic.S3toR3(GreatSphere.FromSphere(simplex.Facets[0]).Pole)); GreatSphere[] spheres = CalcSpheres(simplex.Facets, startingPoles.ToArray()); string filename = "point_group.pov"; using (StreamWriter sw = File.CreateText(filename)) { //foreach( var greatSphere in spheres ) //sw.WriteLine( PovRay.Sphere( greatSphere.ToSphere() ) ); } /* * List<H3.Cell.Edge> startingEdges = new List<H3.Cell.Edge>(); * startingEdges.Add( new H3.Cell.Edge( cen, faceCenter ) ); * H3.Cell.Edge[] edges = Recurse.CalcEdges( simplex.Facets, startingEdges.ToArray(), new Recurse.Settings() { G = Geometry.Spherical, Threshold = 0.001 } ); * //edges = edges.Where( e => !( Infinity.IsInfinite( e.Start ) || Infinity.IsInfinite( e.End ) ) ).ToArray(); * * PovRay.WriteEdges( new PovRay.Parameters() { AngularThickness = 0.01 }, Geometry.Spherical, edges, filename, append: true ); */ double minRad = 0; double thick = 0.005; System.Func <Vector3D, Sphere> sizeFunc = v => { Vector3D c; double rad; H3Models.Ball.DupinCyclideSphere(v, thick / 2, g, out c, out rad); return(new Sphere() { Center = c, Radius = Math.Max(rad, minRad) }); }; // All geodesics List <Circle3D> startingCircles = new List <Circle3D>(); startingCircles.Add(GeodesicFrom2Points(cen, edgeMid)); Circle3D[] geodesics = CalcGeodesics(simplex.Facets, startingCircles.ToArray()); Vector3D color = new Vector3D(0, 0, 1); using (StreamWriter sw = File.CreateText(filename)) { Shapeways shapeways = new Shapeways(); foreach (Circle3D geodesic in geodesics) { Vector3D[] points; if (Infinity.IsInfinite(geodesic.Radius)) { double cutoff = 15; Segment seg = Segment.Line(geodesic.Normal * cutoff, geodesic.Normal * -cutoff); points = seg.Subdivide(42); } else { List <Vector3D> tempPoints = geodesic.Subdivide(150).ToList(); tempPoints.Add(tempPoints[0]); tempPoints.Add(tempPoints[1]); points = tempPoints.ToArray(); } List <Vector3D> ePoints = new List <Vector3D>(); List <double> eRadii = new List <double>(); foreach (Vector3D pNE in points) { Sphere sphere = sizeFunc(pNE); ePoints.Add(sphere.Center); eRadii.Add(sphere.Radius); } shapeways.AddCurve(ePoints.ToArray(), eRadii.ToArray()); sw.WriteLine(PovRay.EdgeSphereSweep(points, sizeFunc, color)); } STL.SaveMeshToSTL(shapeways.Mesh, "533.stl"); } }
public static void CatenoidBasedSurface() { RLD_outputs outputs; SurfaceInternal(out outputs); double scale = m_params.Scale; // Map a point for a given k/m from the hemihypersphere to the complex plane. // You can also pass in -1 for k to get a point on the equator of the hemihypersphere. double mInc = Math.PI * 2 / m_params.M; Func <RLD_outputs, int, int, Vector3D> onPlane = (o, k, m) => { double theta = k == -1 ? 0 : outputs.x_i[k]; theta += Math.PI / 2; return (Sterographic.SphereToPlane( SphericalCoords.SphericalToCartesian( new Vector3D(1, theta, m * mInc) ) )); }; // Setup texture coords on fundamental triangle. // We'll use a fundamental triangle in the southern hemisphere, // with stereographically projected coords at (0,0), (1,0), and CCW on the unit circle depending on M. Polygon p = new Polygon(); p.Segments.Add(Segment.Line(new Vector3D(), new Vector3D(1, 0))); p.Segments.Add(Segment.Arc(new Vector3D(1, 0), onPlane(outputs, 1, 1), onPlane(outputs, -1, 1))); p.Segments.Add(Segment.Line(onPlane(outputs, -1, 1), new Vector3D())); int levels = 9; TextureHelper.SetLevels(levels); Vector3D[] coords = TextureHelper.TextureCoords(p, Geometry.Spherical, doGeodesicDome: true); int[] elementIndices = TextureHelper.TextureElements(1, levels); // Setup a nearTree for the catenoid locations (on the plane). NearTree nearTree = new NearTree(Metric.Spherical); for (int k = 1; k < outputs.x_i.Length; k++) { for (int m = 0; m <= 1; m++) { Vector3D loc = onPlane(outputs, k, m); nearTree.InsertObject(new NearTreeObject() { ID = k, Location = loc }); } } // Given a point on the plane, find the nearest catenoid center and calculate the height of the surface based on that. // This also calculates the locking of the point. Func <Vector3D, Tuple <double, Vector3D, Vector3D> > heightAndLocking = coord => { NearTreeObject closest; if (!nearTree.FindNearestNeighbor(out closest, coord, double.MaxValue)) { throw new System.Exception(); } Vector3D locked = new Vector3D(); if (p.Segments[0].IsPointOn(coord) || p.Segments[2].IsPointOn(coord)) { locked = new Vector3D(1, 1, 0, 0); } //if( p.Segments[1].IsPointOn( v ) ) // Not working right for some reason, but line below will work. if (Tolerance.Equal(coord.Abs(), 1)) { locked = new Vector3D(1, 1, 1, 0); } Vector3D vSphere = Sterographic.PlaneToSphere(coord); Vector3D cSphere = Sterographic.PlaneToSphere(closest.Location); double dist = vSphere.AngleTo(cSphere); int k = (int)closest.ID; double waist = outputs.t_i[k]; double rld_height = outputs.phi_i[k]; double h = waist * 3.5 * 2; // height where catenoid will meet rld_height. double factor = scale * rld_height * 2 / h; // Artifical scaling so we can see things. dist /= factor; double z = double.NaN; if (dist >= waist) { z = waist * DonHatch.acosh(dist / waist); } else if (dist >= 0.7 * waist) { z = 0; // Move the coord to the thinnest waist circle. Mobius m = new Mobius(); m.Hyperbolic(Geometry.Spherical, coord.ToComplex(), waist / dist); coord = m.Apply(coord); } if (dist < waist * 20) { locked = new Vector3D(1, 1, 1, 1); } return(new Tuple <double, Vector3D, Vector3D>(z * factor, locked, coord)); }; // Calculate all the coordinates. Vector3D[] locks = new Vector3D[coords.Length]; for (int i = 0; i < coords.Length; i++) { Vector3D coord = coords[i]; var hl = heightAndLocking(coord); locks[i] = hl.Item2; coord = hl.Item3; coords[i] = Normal(Sterographic.PlaneToSphere(coord), (double)hl.Item1); } // Relax it. Relax(coords, elementIndices, locks); Mesh mesh = new Mesh(); Sphere s = new Sphere(); for (int i = 0; i < elementIndices.Length; i += 3) { Vector3D a = coords[elementIndices[i]]; Vector3D b = coords[elementIndices[i + 1]]; Vector3D c = coords[elementIndices[i + 2]]; if (a.DNE || b.DNE || c.DNE) { continue; } for (int m = 0; m <= 0; m++) { mesh.Triangles.Add(new Mesh.Triangle(a, b, c)); mesh.Triangles.Add(new Mesh.Triangle( s.ReflectPoint(a), s.ReflectPoint(b), s.ReflectPoint(c))); a.RotateXY(mInc); b.RotateXY(mInc); c.RotateXY(mInc); } } PovRay.WriteMesh(mesh, "RLD.pov"); }