private static List <Vector3D[]> BasePointsTiling() { TilingConfig config = new TilingConfig(3, 7, maxTiles: 100); Tiling tiling = new Tiling(); tiling.Generate(config); HashSet <H3.Cell.Edge> finished = new HashSet <H3.Cell.Edge>(new H3.Cell.EdgeEqualityComparer()); int numPerSeg = 25; List <Vector3D[]> basePoints = new List <Vector3D[]>(); foreach (Tile t in tiling.Tiles) { foreach (Segment s in t.Boundary.Segments) { H3.Cell.Edge e = new H3.Cell.Edge(s.P1, s.P2); if (finished.Contains(e)) { continue; } finished.Add(e); Vector3D[] points = s.Subdivide(numPerSeg).Select(p => { p = new Vector3D(p.X, 0, p.Y); return(H3Models.BallToUHS(p)); }).ToArray(); basePoints.Add(points); } } return(basePoints); }
public static void Experiment() { TilingConfig config = new TilingConfig(4, 3); Tiling tiling = new Tiling(); tiling.Generate(config); HashSet <H3.Cell.Edge> completed = new HashSet <H3.Cell.Edge>(new H3.Cell.EdgeEqualityComparer()); string fileName = "hopf.pov"; using (StreamWriter sw = File.CreateText(fileName)) { Tile[] tiles = tiling.Tiles.ToArray(); //foreach( Tile t in tiling.Tiles ) foreach (Tile t in new Tile[] { tiles[0] }) { foreach (Segment seg in t.Boundary.Segments) { H3.Cell.Edge e = new H3.Cell.Edge(seg.P1, seg.P2); if (completed.Contains(e)) { continue; } HopfLink(sw, Sterographic.PlaneToSphereSafe(e.Start), Sterographic.PlaneToSphereSafe(e.End), anti: false); completed.Add(e); } } } }
private static bool EdgeOk(Edge edge, Settings s) { if (s.G == Geometry.Spherical) { return(true); } double thresh = s.Threshold; switch (s.ThreshType) { case EdgeThreshType.Length: // This will also work for ideal edges. return(edge.Start.Dist(edge.End) > thresh); case EdgeThreshType.Radial: return (edge.Start.Abs() < thresh && edge.End.Abs() < thresh); } return(false); }
private static List <H3.Cell.Edge> CopyAndProject(List <H3.Cell.Edge> regionEdges, Tiling tiling, double scale, Vector3D offset) { HashSet <H3.Cell.Edge> newEdges = new HashSet <H3.Cell.Edge>(new H3.Cell.EdgeEqualityComparer()); //foreach( Tile tile in tiling.Tiles ) // Needed for doing full ball (rather than just half of it) Tile tile = tiling.Tiles.First(); { foreach (H3.Cell.Edge edge in regionEdges) { // Translation // The isometry is necessary for the 363, but seems to mess up 636 Vector3D start = tile.Isometry.Apply(edge.Start) + offset; Vector3D end = tile.Isometry.Apply(edge.End) + offset; //Vector3D start = edge.Start + tile.Center + offset; //Vector3D end = edge.End + tile.Center + offset; // Scaling start *= scale; end *= scale; // Projections start = H3Models.UHSToBall(start); end = H3Models.UHSToBall(end); H3.Cell.Edge transformed = new H3.Cell.Edge(start, end); if (EdgeOkBall(transformed)) { newEdges.Add(transformed); } } } return(newEdges.ToList()); }
public static Vector3D EdgeToPlane(Geometry g, H3.Cell.Edge edge) { if (g != Geometry.Hyperbolic) { throw new System.NotImplementedException(); } Vector3D b1, b2; H3Models.Ball.GeodesicIdealEndpoints(edge.Start, edge.End, out b1, out b2); if (((b2 + b1) / 2).IsOrigin) { Vector3D lineNormal = b2 - b1; lineNormal.RotateXY(Math.PI / 2); lineNormal.Normalize(); return(new Vector3D(lineNormal.X, lineNormal.Y, lineNormal.Z, 0)); } Vector3D center, normal; double radius, angleTot; H3Models.Ball.Geodesic(edge.Start, edge.End, out center, out radius, out normal, out angleTot); Vector3D closest = H3Models.Ball.ClosestToOrigin(new Circle3D() { Center = center, Radius = radius, Normal = normal }); Vector3D closestKlein = HyperbolicModels.PoincareToKlein(closest); center.Normalize(); return(new Vector3D(center.X, center.Y, center.Z, closestKlein.Abs())); }
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"); }
private static bool EdgeOkUHS(H3.Cell.Edge edge, Circle region) { if (Tolerance.GreaterThan(edge.Start.Abs(), region.Radius) || Tolerance.GreaterThan(edge.End.Abs(), region.Radius)) { return(false); } return(EdgeOk(edge, m_params.UhsCutoff)); }
H3.Cell.Edge Transform(H3.Cell.Edge edge, double t) { if (edge == null) { return(edge); } H3.Cell.Edge result = new H3.Cell.Edge(Transform(edge.Start, t), Transform(edge.End, t)); result.Color = edge.Color; return(result); }
void AddRow(Mesh mesh, H3.Cell.Edge edge1, H3.Cell.Edge edge2) { int div = 60; // Has to be the same for all edges? if (edge1 == null && edge2 == null) { return; } System.Func <Vector3D, Vector3D, int, Vector3D[]> geoPoints = null; if (Ball) { geoPoints = H3Models.Ball.GeodesicPoints; } else { geoPoints = H3Models.UHS.GeodesicPoints; } if (edge1 == null || edge2 == null) { H3.Cell.Edge e = edge1 == null ? edge2 : edge1; if (e.Start == e.End) { return; } Vector3D[] verts = geoPoints(e.Start, e.End, div); Vector3D center, dummy; double radius, dummyD; if (Ball) { H3Models.Ball.Geodesic(e.Start, e.End, out center, out radius, out dummy, out dummyD); } else { H3Models.UHS.Geodesic(e.Start, e.End, out center, out radius, out dummy, out dummyD); } for (int i = 0; i < verts.Length - 1; i++) { mesh.Triangles.Add(new Mesh.Triangle(verts[i], verts[i + 1], center)); // Radius will be small, so reasonable. } return; } Vector3D[] verts1 = geoPoints(edge1.Start, edge1.End, div); Vector3D[] verts2 = geoPoints(edge2.Start, edge2.End, div); for (int i = 0; i < verts1.Length - 1; i++) { mesh.Triangles.Add(new Mesh.Triangle(verts1[i], verts1[i + 1], verts2[i])); mesh.Triangles.Add(new Mesh.Triangle(verts2[i], verts1[i + 1], verts2[i + 1])); } }
void CalcFibers(Vector3D[] basePoints, List <H3.Cell.Edge> fibers, double t) { foreach (Vector3D v in basePoints) { H3.Cell.Edge e = CalcFiberFromBase(v); if (e != null) { e.Color = Color(Transform(H3Models.UHSToBall(v), t)); } fibers.Add(Transform(e, t)); } }
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" ); }
private static string H3Edge(Parameters parameters, H3.Cell.Edge edge) { Vector3D v1 = edge.Start, v2 = edge.End; Vector3D[] points = null; Func <Vector3D, Sphere> sizeFunc = v => new Sphere() { Center = v, Radius = H3Models.SizeFuncConst(v, parameters.Scale) }; if (parameters.Halfspace) { points = H3Models.UHS.GeodesicPoints(v1, v2); if (!parameters.ThinEdges) { sizeFunc = v => { // XXX, inexact return(new Sphere() { Center = v, Radius = H3Models.UHS.SizeFunc(v, parameters.AngularThickness) }); } } ; } else { points = H3Models.Ball.GeodesicPoints(v1, v2); if (!parameters.ThinEdges) { sizeFunc = v => { Vector3D c; double r; H3Models.Ball.DupinCyclideSphere(v, parameters.AngularThickness / 2, out c, out r); return(new Sphere() { Center = c, Radius = r }); //return new Sphere() { Center = v, Radius = H3Models.Ball.SizeFunc( v, parameters.AngularThickness ) }; // inexact } } ; } return(H3EdgeSphereSweep(points, sizeFunc)); }
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 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)); } } }
private static void ThinMesh(H3.Cell.Edge[] edges, System.Func <H3.Cell.Edge, Vector3D[]> divider, out Mesh thinMesh, out List <Vector3D[]> boundaryPoints) { thinMesh = new Mesh(); boundaryPoints = new List <Vector3D[]>(); List <Vector3D> starts = new List <Vector3D>(); List <Vector3D> ends = new List <Vector3D>(); for (int i = 0; i < edges.Length - 1; i++) { int idx1 = i; int idx2 = i + 1; H3.Cell.Edge e1 = edges[idx1]; H3.Cell.Edge e2 = edges[idx2]; Vector3D[] points1 = divider(e1); Vector3D[] points2 = divider(e2); thinMesh.AddBand(points1, points2, close: false); starts.Add(e1.Start); ends.Add(e1.End); if (idx1 == 0) { boundaryPoints.Add(points1); } if (idx2 == edges.Length - 1) { boundaryPoints.Add(points2); starts.Add(e2.Start); ends.Add(e2.End); } } starts.Reverse(); boundaryPoints.Add(starts.ToArray()); boundaryPoints.Add(ends.ToArray()); }
/// <summary> /// Helper to do one step of reflections. /// Returns a new list of region edges. /// </summary> private static List <H3.Cell.Edge> DoOneStep(List <H3.Cell.Edge> regionEdges, Tiling tiling, Circle region) { HashSet <H3.Cell.Edge> newEdges = new HashSet <H3.Cell.Edge>(new H3.Cell.EdgeEqualityComparer()); foreach (Tile tile in tiling.Tiles) { foreach (H3.Cell.Edge edge in regionEdges) { H3.Cell.Edge toAdd = null; if (!Tolerance.Zero(tile.Center.Abs())) { // Translate // The isometry is necessary for the 363, but seems to mess up 636 Vector3D start = tile.Isometry.Apply(edge.Start); Vector3D end = tile.Isometry.Apply(edge.End); //Vector3D start = edge.Start + tile.Center; //Vector3D end = edge.End + tile.Center; // Reflect start = region.ReflectPoint(start); end = region.ReflectPoint(end); toAdd = new H3.Cell.Edge(start, end); } else { toAdd = edge; } if (EdgeOkUHS(toAdd, region)) { newEdges.Add(toAdd); } } } return(newEdges.ToList()); }
private static void AddEuclideanEdge(Shapeways mesh, HashSet <H3.Cell.Edge> completed, Vector3D start, Vector3D end) { H3.Cell.Edge edge = new H3.Cell.Edge(start, end); if (completed.Contains(edge)) { return; } Shapeways tempMesh = new Shapeways(); Segment seg = Segment.Line(start, end); int div = 20 - (int)(start.Abs() * 4); if (div < 1) { div = 1; } tempMesh.AddCurve(seg.Subdivide(div), .05); Transform(tempMesh.Mesh); mesh.Mesh.Triangles.AddRange(tempMesh.Mesh.Triangles); completed.Add(edge); }
private static List<H3.Cell.Edge> CopyAndProject( List<H3.Cell.Edge> regionEdges, Tiling tiling, double scale, Vector3D offset ) { HashSet<H3.Cell.Edge> newEdges = new HashSet<H3.Cell.Edge>( new H3.Cell.EdgeEqualityComparer() ); //foreach( Tile tile in tiling.Tiles ) // Needed for doing full ball (rather than just half of it) Tile tile = tiling.Tiles.First(); { foreach( H3.Cell.Edge edge in regionEdges ) { // Translation // The isometry is necessary for the 363, but seems to mess up 636 Vector3D start = tile.Isometry.Apply( edge.Start ) + offset; Vector3D end = tile.Isometry.Apply( edge.End ) + offset; //Vector3D start = edge.Start + tile.Center + offset; //Vector3D end = edge.End + tile.Center + offset; // Scaling start *= scale; end *= scale; // Projections start = H3Models.UHSToBall( start ); end = H3Models.UHSToBall( end ); H3.Cell.Edge transformed = new H3.Cell.Edge( start, end ); if( EdgeOkBall( transformed ) ) newEdges.Add( transformed ); } } return newEdges.ToList(); }
private static bool EdgeOk(H3.Cell.Edge edge, double cutoff) { return(edge.Start.Dist(edge.End) > cutoff); }
private static bool EdgeOkBall(H3.Cell.Edge edge) { return(EdgeOk(edge, m_params.BallCutoff)); }
/// <summary> /// Helper to do one step of reflections. /// Returns a new list of region edges. /// </summary> private static List<H3.Cell.Edge> DoOneStep( List<H3.Cell.Edge> regionEdges, Tiling tiling, Circle region ) { HashSet<H3.Cell.Edge> newEdges = new HashSet<H3.Cell.Edge>( new H3.Cell.EdgeEqualityComparer() ); foreach( Tile tile in tiling.Tiles ) { foreach( H3.Cell.Edge edge in regionEdges ) { H3.Cell.Edge toAdd = null; if( !Tolerance.Zero( tile.Center.Abs() ) ) { // Translate // The isometry is necessary for the 363, but seems to mess up 636 Vector3D start = tile.Isometry.Apply( edge.Start ); Vector3D end = tile.Isometry.Apply( edge.End ); //Vector3D start = edge.Start + tile.Center; //Vector3D end = edge.End + tile.Center; // Reflect start = region.ReflectPoint( start ); end = region.ReflectPoint( end ); toAdd = new H3.Cell.Edge( start, end ); } else toAdd = edge; if( EdgeOkUHS( toAdd, region ) ) newEdges.Add( toAdd ); } } return newEdges.ToList(); }
private static void CompoundOfFive24Cells(ref H3.Cell.Edge[] edges) { List <H3.Cell.Edge> allEdges = new List <H3.Cell.Edge>(); Vector3D v600 = Sterographic.R3toS3(SimplexCalcs.VertexSpherical(3, 3, 5)); Vector3D v24 = Sterographic.R3toS3(SimplexCalcs.VertexSpherical(3, 4, 3)); Sphere[] mirrors600 = SimplexCalcs.MirrorsSpherical(3, 3, 5); double a24 = v24.AngleTo(Sterographic.R3toS3(new Vector3D())); double a600 = v600.AngleTo(Sterographic.R3toS3(new Vector3D())); Matrix4D m600 = Matrix4D.MatrixToRotateinCoordinatePlane(a600, 2, 3); Matrix4D m600_ = Matrix4D.MatrixToRotateinCoordinatePlane(-a600, 2, 3); Matrix4D m24 = Matrix4D.MatrixToRotateinCoordinatePlane(a24, 2, 3); Matrix4D m24_ = Matrix4D.MatrixToRotateinCoordinatePlane(-a24, 2, 3); double eLength = 2 * Math.PI / 10; // 600-cell edge length double a_id = Math.Asin(Math.Sin(eLength / 2) / Math.Sin(Math.PI / 3) * Math.Sin(Math.PI / 2)); eLength = 1.0 / Math.Sin(2 * Math.PI / 5); // icosahedron edge length double a_i = Math.Asin(Math.Sin(eLength / 2) / Math.Sin(Math.PI / 3) * Math.Sin(Math.PI / 5)); Func <Vector3D, Vector3D> rot600 = v => { v = Sterographic.R3toS3(v); v = m600.RotateVector(v); v = Sterographic.S3toR3(v); return(v); }; Func <Vector3D, int, Vector3D> rotOne = (v, idx) => { v = Sterographic.R3toS3(v); v = m24.RotateVector(v); v = Sterographic.S3toR3(v); v.RotateAboutAxis(new Vector3D(1, 0, 0), -a_id); // Vertex to cell center. v = Sterographic.R3toS3(v); v = m600_.RotateVector(v); v = Sterographic.S3toR3(v); List <int> reflections = new List <int>(); if (idx == 0) { reflections.Add(2); reflections.Add(1); reflections.Add(2); reflections.Add(0); reflections.Add(1); reflections.Add(2); reflections.Add(1); reflections.Add(2); } if (idx != 0) { reflections.Add(3); } if (idx == 2) { reflections.Add(1); reflections.Add(2); } if (idx == 3) { reflections.Add(2); reflections.Add(1); } if (idx == 4) { reflections.Add(1); reflections.Add(0); reflections.Add(1); reflections.Add(2); reflections.Add(0); reflections.Add(1); reflections.Add(0); reflections.Add(1); } foreach (int reflection in reflections) { v = mirrors600[reflection].ReflectPoint(v); } v = Sterographic.R3toS3(v); v = m600.RotateVector(v); v = Sterographic.S3toR3(v); //v.RotateAboutAxis( new Vector3D( 0, 0, 1 ), Math.PI/3 ); //v.RotateAboutAxis( new Vector3D( 1, 0, 0 ), -a_i*2 ); v = Sterographic.R3toS3(v); //v = m24_.RotateVector( v ); v = Sterographic.S3toR3(v); return(v); }; for (int i = 0; i < 5; i++) { //if( i == 0 ) // continue; allEdges.AddRange(edges.Select(e => { H3.Cell.Edge newEdge = new H3.Cell.Edge(rotOne(e.Start, i), rotOne(e.End, i)); //H3.Cell.Edge newEdge = new H3.Cell.Edge( rot600( e.Start ), rot600( e.End ) ); switch (i) { case 0: newEdge.Color = new Vector3D(1, 0, 0, 1); //newEdge.Color = new Vector3D( 1, 1, 1, 0 ); break; case 1: newEdge.Color = new Vector3D(0, 1, 0, 2); break; case 2: newEdge.Color = new Vector3D(0, 0, 1, 3); break; case 3: newEdge.Color = new Vector3D(1, 0, 1, 4); break; case 4: newEdge.Color = new Vector3D(0, 1, 1, 5); break; } return(newEdge); })); } edges = allEdges.ToArray(); //edges = edges.Where( e => Tolerance.Equal( 1, e.Start.Abs() ) || Tolerance.Equal( 1, e.End.Abs() ) ).ToArray(); HashSet <Vector3D> uniqueVerts = new HashSet <Vector3D>(); foreach (H3.Cell.Edge e in edges) { uniqueVerts.Add(e.Start); uniqueVerts.Add(e.End); } System.Diagnostics.Trace.WriteLine("Number of verts = " + uniqueVerts.Count); /*edges = edges.Where( e => * { * Vector3D v = Tolerance.Equal( 1, e.Start.Abs() ) ? e.End : e.Start; * if( v.Abs() >= 0.8 || v.Abs() <= 0.7 ) * return false; * * if( Tolerance.LessThanOrEqual( v.X, 0 ) || Tolerance.GreaterThanOrEqual( v.Y, 0 ) || Tolerance.GreaterThanOrEqual( v.Z, 0 ) ) * return false; * * return true; * } ).ToArray(); * * edges = edges.OrderBy( e => Tolerance.Equal( 1, e.Start.Abs() ) ? e.End.Abs() : e.Start.Abs() ).ToArray();*/ }
/// <summary> /// Create an STL file for a cell. /// Currently only works for cells with both hyperideal vertices and cells. /// </summary> public static void HoneycombHyperidealLegs(HoneycombDef def, int lod, Dictionary <Vector3D, H3.Cell> complete) { int p = def.P; int q = def.Q; int r = def.R; m_div = TextureHelper.SetLevels(lod); bool ball = false; Sphere[] simplex = SimplexCalcs.Mirrors(p, q, r, moveToBall: ball); H3.Cell.Edge[] edges; if (ball) { edges = SimplexCalcs.SimplexEdgesBall(p, q, r); } else { edges = SimplexCalcs.SimplexEdgesUHS(p, q, r); } // Two edges of one simplex facet. H3.Cell.Edge e1 = edges[2]; H3.Cell.Edge e2 = edges[3]; Vector3D[] points1, points2; if (ball) { points1 = H3Models.Ball.GeodesicPoints(e1.Start, e1.End, 2 * m_div); points2 = H3Models.Ball.GeodesicPoints(e2.Start, e2.End, 2 * m_div); } else { points1 = H3Models.UHS.GeodesicPoints(e1.Start, e1.End, 2 * m_div); points2 = H3Models.UHS.GeodesicPoints(e2.Start, e2.End, 2 * m_div); } Sphere cellSphere = simplex[0]; Sphere vertexSphere = simplex[3]; // Because one vertex the facet triangle is hyperideal, it will actually look like a square. List <Vector3D[]> allPoints = new List <Vector3D[]>(); for (int i = 0; i < points1.Length; i++) { Vector3D p1 = points1[i]; Vector3D p2 = points2[i]; Vector3D[] arcPoints; if (i == points1.Length - 1) //if( false ) { // NOTE: This arc is not generally geodesic! // Or is it? arcPoints = ball ? H3Models.Ball.GeodesicPoints(p1, p2, m_div) : H3Models.UHS.GeodesicPoints(p1, p2, m_div); /*Circle3D arc = cellSphere.Intersection( vertexSphere ); * double angleTot = (p1 - arc.Center).AngleTo( p2 - arc.Center ); * arcPoints = Shapeways.CalcArcPoints( arc.Center, arc.Radius, p1, arc.Normal, -angleTot, div );*/ } else { Circle3D c = Circle3D.FromCenterAnd2Points(cellSphere.Center, p1, p2); double angleTot = (p1 - c.Center).AngleTo(p2 - c.Center); arcPoints = Shapeways.CalcArcPoints(cellSphere.Center, cellSphere.Radius, p1, c.Normal, -angleTot, m_div); } //Vector3D[] arcPoints = new Vector3D[] { p1, p2 }; allPoints.Add(arcPoints); } // Create the triangles for the patch. Mesh mesh = new Mesh(); for (int i = 0; i < allPoints.Count - 1; i++) { Vector3D[] arc1 = allPoints[i]; Vector3D[] arc2 = allPoints[i + 1]; for (int j = 0; j < arc1.Length - 1; j++) { // Points of (i,j) box; Vector3D p1 = arc1[j]; Vector3D p2 = arc2[j]; Vector3D p3 = arc1[j + 1]; Vector3D p4 = arc2[j + 1]; Mesh.Triangle tri1 = new Mesh.Triangle(p1, p2, p3); Mesh.Triangle tri2 = new Mesh.Triangle(p2, p4, p3); // We need to thicken after reflecting around, otherwise we can't apply a min thickness. /*Sphere normal = cellSphere; * Mesh.Triangle[] thickened1 = Thicken( tri1, normal ); * Mesh.Triangle[] thickened2 = Thicken( tri2, normal ); * mesh.Triangles.AddRange( thickened1 ); * mesh.Triangles.AddRange( thickened2 );*/ mesh.Triangles.Add(tri1); mesh.Triangles.Add(tri2); } } // AuxPoints will be used for multiple things. // - The first two points are for an an that will fill the gap where there is a missing face. // - We'll also store the points for the 4 edges of our fundamental triangle. List <Vector3D> auxPoints = new List <Vector3D>(); { var edge1 = allPoints.First(); var edge2 = allPoints.Last(); List <Vector3D> edge3 = new List <Vector3D>(), edge4 = new List <Vector3D>(); for (int i = 0; i < allPoints.Count; i++) { edge3.Add(allPoints[i][0]); edge4.Add(allPoints[i][allPoints[i].Length - 1]); } edge4.Reverse(); auxPoints.Add(e1.Start); auxPoints.Add(e1.End); auxPoints.AddRange(edge1.Reverse()); auxPoints.AddRange(edge2); auxPoints.AddRange(edge3); auxPoints.AddRange(edge4); } Vector3D cen = HoneycombPaper.InteriorPointBall; /* Reorientation code. Move this elsewhere. * * // Face centered orientation. * bool faceCentered = false; * if( faceCentered ) * SimplexCalcs.PrepForFacetCentering( p, q, simplex, ref cen ); * * Mobius mUHS = SimplexCalcs.FCOrientMobius( p, q ); * Mobius mBall = HoneycombPaper.FCOrientMobius( H3Models.UHSToBall( cellSphere ) ); * * simplex = simplex.Select( s => * { * s = H3Models.UHSToBall( s ); * //H3Models.TransformInBall2( s, mBall ); * return s; * } ).ToArray(); * * * { * for( int i = 0; i < mesh.Triangles.Count; i++ ) * { * Mesh.Triangle tri = mesh.Triangles[i]; * * if( faceCentered ) * { * tri.a = mUHS.ApplyToQuaternion( tri.a ); * tri.b = mUHS.ApplyToQuaternion( tri.b ); * tri.c = mUHS.ApplyToQuaternion( tri.c ); * } * * tri.a = H3Models.UHSToBall( tri.a ); * tri.b = H3Models.UHSToBall( tri.b ); * tri.c = H3Models.UHSToBall( tri.c ); * * if( faceCentered ) * { * tri.a = H3Models.TransformHelper( tri.a, mBall ); * tri.b = H3Models.TransformHelper( tri.b, mBall ); * tri.c = H3Models.TransformHelper( tri.c, mBall ); * } * mesh.Triangles[i] = tri; * } * * if( faceCentered ) * cen = H3Models.TransformHelper( cen, mBall ); * } */ // Now we need to reflect around this fundamental patch. H3.Cell[] simplices = GenCell(simplex, mesh, cen, auxPoints.ToArray(), ball); // Existing cells take precedence. foreach (H3.Cell c in simplices) { Vector3D t = c.Center; H3.Cell dummy; if (!complete.TryGetValue(t, out dummy)) { complete[t] = c; } } }
private static void ReflectEdgesRecursive( Sphere[] simplex, Edge[] edges, Settings settings, HashSet<Edge> completedEdges ) { if( 0 == edges.Length ) return; HashSet<Edge> newEdges = new HashSet<Edge>( new H3.Cell.EdgeEqualityComparer() ); foreach( Edge edge in edges ) //foreach( Sphere mirror in simplex ) for( int m=0; m<simplex.Length; m++ ) { Sphere mirror = simplex[m]; if( completedEdges.Count > settings.MaxEdges ) throw new System.Exception( "Maxing out edges - will result in uneven filling." ); Vector3D r1 = mirror.ReflectPoint( edge.Start ); Vector3D r2 = mirror.ReflectPoint( edge.End ); Edge newEdge = new Edge( r1, r2 ); newEdge.CopyDepthsFrom( edge ); if( !EdgeOk( newEdge, settings ) ) continue; // This tracks reflections across the cell facets. newEdge.Depths[m]++; // Edge color. // Make the threshold length black, or the background color. double percentWhite = ( r1.Dist( r2 ) - settings.Threshold ) / 0.015; if( percentWhite < 0 ) percentWhite = 0; if( percentWhite > 1 ) percentWhite = 1; //newEdge.Color = new Vector3D( percentWhite, percentWhite, percentWhite ); newEdge.Color = m_background; newEdge.Color.Z = 0.1 + 0.9 * percentWhite; if( completedEdges.Add( newEdge ) ) { // Haven't seen this edge yet, so // we'll need to recurse on it. newEdges.Add( newEdge ); } } ReflectEdgesRecursive( simplex, newEdges.ToArray(), settings, completedEdges ); }
/// <summary> /// Attempts to calculate approx 1.3M edges when the threshold is a minimum edge length. /// This is required for honeycombs with ideal or ultra-ideal cells /// </summary> public static Edge[] CalcEdgesSmart2( Sphere[] simplex, Edge[] edges ) { Settings s = new Settings(); // I found that log(1/thresh)/log(count) was relatively constant, // so we'll extrapolate that to get close to the right number of edges. double OneOverThresh = 60; s.Threshold = 1 / OneOverThresh; Edge[] result = CalcEdges( simplex, edges, s ); int count1 = result.Length; OneOverThresh = 80; s.Threshold = 1 / OneOverThresh; result = CalcEdges( simplex, edges, s ); int count2 = result.Length; double slope = ( Math.Log( count2 ) - Math.Log( count1 ) ) / ( Math.Log( 80 ) - Math.Log( 60 ) ); // Why 1.3M? We'll get 650k after we half this. double desiredCount = 2e6; //double desiredCount = 3e4; // For testing double logDesiredCount = Math.Log( desiredCount ); double temp = Math.Log( 80 ) + ( logDesiredCount - Math.Log( count2 ) ) / slope; s.Threshold = 1 / Math.Exp( temp ); return CalcEdges( simplex, edges, s ); }
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; } } }
/// <summary> /// Returns the 6 simplex edges in the UHS model. /// </summary> public static H3.Cell.Edge[] SimplexEdgesUHS( int p, int q, int r ) { // Only implemented for honeycombs with both hyperideal edges/vertices right now. if( !( Geometry2D.GetGeometry( p, q ) == Geometry.Hyperbolic && Geometry2D.GetGeometry( q, r ) == Geometry.Hyperbolic ) ) throw new System.NotImplementedException(); Sphere[] simplex = SimplexCalcs.Mirrors( p, q, r, moveToBall: false ); Circle[] circles = simplex.Select( s => H3Models.UHS.IdealCircle( s ) ).ToArray(); Vector3D[] defPoints = new Vector3D[6]; Vector3D dummy; Euclidean2D.IntersectionLineCircle( circles[1].P1, circles[1].P2, circles[0], out defPoints[0], out dummy ); Euclidean2D.IntersectionLineCircle( circles[2].P1, circles[2].P2, circles[0], out defPoints[1], out dummy ); Euclidean2D.IntersectionLineCircle( circles[1].P1, circles[1].P2, circles[3], out defPoints[2], out dummy ); Euclidean2D.IntersectionLineCircle( circles[2].P1, circles[2].P2, circles[3], out defPoints[3], out dummy ); Circle3D c = simplex[0].Intersection( simplex[3] ); Vector3D normal = c.Normal; normal.RotateXY( Math.PI / 2 ); Vector3D intersection; double height, off; Euclidean2D.IntersectionLineLine( c.Center, c.Center + normal, circles[1].P1, circles[1].P2, out intersection ); off = ( intersection - c.Center ).Abs(); height = Math.Sqrt( c.Radius * c.Radius - off * off ); intersection.Z = height; defPoints[4] = intersection; Euclidean2D.IntersectionLineLine( c.Center, c.Center + normal, circles[2].P1, circles[2].P2, out intersection ); off = ( intersection - c.Center ).Abs(); height = Math.Sqrt( c.Radius * c.Radius - off * off ); intersection.Z = height; defPoints[5] = intersection; bool order = false; H3.Cell.Edge[] edges = new H3.Cell.Edge[] { new H3.Cell.Edge( new Vector3D(), new Vector3D( 0, 0, 10 ) ), new H3.Cell.Edge( defPoints[4], defPoints[5], order ), new H3.Cell.Edge( defPoints[0], defPoints[4], order ), new H3.Cell.Edge( defPoints[1], defPoints[5], order ), new H3.Cell.Edge( defPoints[2], defPoints[4], order ), new H3.Cell.Edge( defPoints[3], defPoints[5], order ), }; return edges; }
/// <summary> /// Returns the 6 simplex edges in the UHS model. /// </summary> public static H3.Cell.Edge[] SimplexEdgesUHS(int p, int q, int r) { // Only implemented for honeycombs with hyperideal cells right now. if (!(Geometry2D.GetGeometry(p, q) == Geometry.Hyperbolic)) { throw new System.NotImplementedException(); } Sphere[] simplex = SimplexCalcs.Mirrors(p, q, r, moveToBall: false); Circle[] circles = simplex.Select(s => H3Models.UHS.IdealCircle(s)).ToArray(); Vector3D[] defPoints = new Vector3D[6]; Vector3D dummy; Euclidean2D.IntersectionLineCircle(circles[1].P1, circles[1].P2, circles[0], out defPoints[0], out dummy); Euclidean2D.IntersectionLineCircle(circles[2].P1, circles[2].P2, circles[0], out defPoints[1], out dummy); Euclidean2D.IntersectionLineCircle(circles[1].P1, circles[1].P2, circles[3], out defPoints[2], out dummy); Euclidean2D.IntersectionLineCircle(circles[2].P1, circles[2].P2, circles[3], out defPoints[3], out dummy); Circle3D c = simplex[0].Intersection(simplex[3]); Vector3D normal = c.Normal; normal.RotateXY(Math.PI / 2); Vector3D intersection; double height, off; Euclidean2D.IntersectionLineLine(c.Center, c.Center + normal, circles[1].P1, circles[1].P2, out intersection); off = (intersection - c.Center).Abs(); height = Math.Sqrt(c.Radius * c.Radius - off * off); intersection.Z = height; defPoints[4] = intersection; Euclidean2D.IntersectionLineLine(c.Center, c.Center + normal, circles[2].P1, circles[2].P2, out intersection); off = (intersection - c.Center).Abs(); height = Math.Sqrt(c.Radius * c.Radius - off * off); intersection.Z = height; defPoints[5] = intersection; // Hyperideal vertex too? bool order = false; H3.Cell.Edge[] edges = null; if (Geometry2D.GetGeometry(q, r) == Geometry.Hyperbolic) { edges = new H3.Cell.Edge[] { new H3.Cell.Edge(new Vector3D(), new Vector3D(0, 0, 10)), new H3.Cell.Edge(defPoints[4], defPoints[5], order), new H3.Cell.Edge(defPoints[0], defPoints[4], order), new H3.Cell.Edge(defPoints[1], defPoints[5], order), new H3.Cell.Edge(defPoints[2], defPoints[4], order), new H3.Cell.Edge(defPoints[3], defPoints[5], order), }; } else { Vector3D vPointUHS = H3Models.BallToUHS(VertexPointBall(p, q, r)); defPoints[0] = defPoints[1] = vPointUHS; edges = new H3.Cell.Edge[] { new H3.Cell.Edge(vPointUHS, new Vector3D(0, 0, 10)), new H3.Cell.Edge(defPoints[4], defPoints[5], order), new H3.Cell.Edge(defPoints[0], defPoints[4], order), new H3.Cell.Edge(defPoints[1], defPoints[5], order), new H3.Cell.Edge(defPoints[2], defPoints[4], order), new H3.Cell.Edge(defPoints[3], defPoints[5], order), }; } return(edges); }
/// <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; } } } }
private static string Edge(Parameters parameters, Geometry g, H3.Cell.Edge edge) { Vector3D v1 = edge.Start, v2 = edge.End; Vector3D[] points = null; Func <Vector3D, Sphere> sizeFunc = v => new Sphere() { Center = v, Radius = H3Models.SizeFuncConst(v, parameters.Scale) }; //double minRad = 0.0005; double minRad = 0.0004; //double minRad = 0.0017; // STL //minRad = 0.8 / 100; if (parameters.Halfspace) { //v1 = H3Models.BallToUHS( v1 ); //v2 = H3Models.BallToUHS( v2 ); points = H3Models.UHS.GeodesicPoints(v1, v2); if (!parameters.ThinEdges) { sizeFunc = v => { // XXX, inexact return(new Sphere() { Center = v, Radius = Math.Max(H3Models.UHS.SizeFunc(v, parameters.AngularThickness), minRad) }); } } ; } else { if (g == Geometry.Hyperbolic) { points = H3Models.Ball.GeodesicPoints(v1, v2, edge.Color.Z); } else if (g == Geometry.Spherical) { points = S3.GeodesicPoints(v1, v2); //points = points.Select( p => { p.Normalize(); return p; } ).ToArray(); } else { //points = new Vector3D[] { v1, v2 }; List <Vector3D> interpolated = new List <Vector3D>(); int count = 20; for (int i = 0; i <= count; i++) { interpolated.Add(v1 + (v2 - v1) * ((double)i / count)); } points = interpolated.ToArray(); } if (!parameters.ThinEdges) { sizeFunc = v => { Vector3D c; double r; H3Models.Ball.DupinCyclideSphere(v, parameters.AngularThickness / 2, g, out c, out r); return(new Sphere() { Center = c, Radius = Math.Max(r, minRad) }); } } ; } //if( g == Geometry.Euclidean ) // return EdgeCylinder( points, sizeFunc ); return(EdgeSphereSweep(points, sizeFunc, edge.Color)); }
/// <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(); } }
public static Edge[] CalcEdges( Sphere[] simplex, Edge[] edges, Settings settings ) { HashSet<Edge> completedEdges = new HashSet<Edge>( edges, new H3.Cell.EdgeEqualityComparer() ); ReflectEdgesRecursive( simplex, completedEdges.ToArray(), settings, completedEdges ); return completedEdges.ToArray(); }
private static bool EdgeOk( Edge edge, Settings s ) { if( s.G == Geometry.Spherical ) return true; double thresh = s.Threshold; bool useEdgeLength = s.G == Geometry.Hyperbolic; if( useEdgeLength ) { // This will also work for ideal edges. return edge.Start.Dist( edge.End ) > thresh; } else { return edge.Start.Abs() < thresh && edge.End.Abs() < thresh; } }
/// <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; } } }
public static Edge[] CalcEdges( Sphere[] simplex, Edge[] edges ) { Settings settings = new Settings(); return CalcEdges( simplex, edges, settings ); }
private static void ReflectEdgesRecursive(Sphere[] simplex, Edge[] edges, Settings settings, HashSet <Edge> completedEdges) { if (0 == edges.Length) { return; } HashSet <Edge> newEdges = new HashSet <Edge>(new H3.Cell.EdgeEqualityComparer()); foreach (Edge edge in edges) { //foreach( Sphere mirror in simplex ) for (int m = 0; m < simplex.Length; m++) { Sphere mirror = simplex[m]; if (completedEdges.Count > settings.MaxEdges) { throw new System.Exception("Maxing out edges - will result in uneven filling."); } Vector3D r1 = mirror.ReflectPoint(edge.Start); Vector3D r2 = mirror.ReflectPoint(edge.End); Edge newEdge = new Edge(r1, r2); newEdge.CopyDepthsFrom(edge); if (!EdgeOk(newEdge, settings)) { continue; } // This tracks reflections across the cell facets. newEdge.Depths[m]++; // Edge color. // This also controls resolution of the edges, and can have a big effect on file size. // Make the threshold cutoff black, or the background color. double percentWhite = 1; if (settings.ThreshType == EdgeThreshType.Length) { percentWhite = (r1.Dist(r2) - settings.Threshold) / 0.015; } else { double closestToOrigin = Math.Min(r1.Abs(), r2.Abs()); // Mainly ranges from 0 to 1 if (closestToOrigin < 0.9) { percentWhite = 1.0; } else { percentWhite = 1.0 - Math.Pow(closestToOrigin - 0.9, 1.3) / 0.1; } } if (percentWhite < 0) { percentWhite = 0; } if (percentWhite > 1) { percentWhite = 1; } //newEdge.Color = new Vector3D( percentWhite, percentWhite, percentWhite ); newEdge.Color = m_background; newEdge.Color.Z = 0.1 + 0.9 * percentWhite; if (completedEdges.Add(newEdge)) { // Haven't seen this edge yet, so // we'll need to recurse on it. newEdges.Add(newEdge); } } } ReflectEdgesRecursive(simplex, newEdges.ToArray(), settings, completedEdges); }
/// <summary> /// Attempts to calculate approx 1.3M edges when the threshold is a distance from origin in the ball model. /// This works for honeycombs with finite cells. /// </summary> public static Edge[] CalcEdgesSmart( Sphere[] simplex, Edge[] edges ) { Settings s = new Settings(); // The number of cells increase exponentially with hyperbolic distance, // so linear on a log scale. // We'll do to test runs to get the line, then run at the extrapolated value. double hDist = 5; s.Threshold = DonHatch.h2eNorm( hDist ); Edge[] result = CalcEdges( simplex, edges, s ); int count1 = result.Length; hDist = 5.5; s.Threshold = DonHatch.h2eNorm( hDist ); result = CalcEdges( simplex, edges, s ); int count2 = result.Length; double slope = ( Math.Log( count2 ) - Math.Log( count1 ) ) / 0.5; // Why 1.3M? We'll get 650k after we half this. double desiredCount = 1.0e4; // for testing //double desiredCount = 1.3e6; //double desiredCount = 0.4e6; // Mid-range double logDesiredCount = Math.Log( desiredCount ); hDist = 5.5 + ( logDesiredCount - Math.Log( count2 ) ) / slope; s.Threshold = DonHatch.h2eNorm( hDist ); return CalcEdges( simplex, edges, s ); }
/// <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(); } } }