/// <summary> /// A correct implementation of shrink tile. /// hmmmm, is "scaling" even well defined in non-E geometries? Am I really looking for an equidistant curve? /// Sadly, even if I figure out what is best, I fear changing out usage of the incorrect one below in MagicTile, /// because of the possibility of breaking existing puzzles. /// </summary> internal static void ShrinkTileCorrect(ref Tile tile, double shrinkFactor) { System.Func <Vector3D, double, Vector3D> scaleFunc = null; switch (tile.Geometry) { case Geometry.Euclidean: { scaleFunc = (v, s) => v * s; break; } case Geometry.Spherical: { scaleFunc = (v, s) => { // Move to spherical norm, scale, then move back to euclidean. double scale = Spherical2D.s2eNorm((Spherical2D.e2sNorm(v.Abs()) * s)); v.Normalize(); return(v * scale); }; break; } case Geometry.Hyperbolic: { throw new System.NotImplementedException(); } } }
private static void HopfFibration(Tiling tiling) { int segDivisions = 10; Shapeways mesh = new Shapeways(); HashSet <Vector3D> done = new HashSet <Vector3D>(); foreach (Tile tile in tiling.Tiles) { foreach (Segment seg in tile.Boundary.Segments) { if (done.Contains(seg.Midpoint)) { continue; } // Subdivide the segment, and project points to S2. Vector3D[] points = seg.Subdivide(segDivisions).Select(v => Spherical2D.PlaneToSphere(v)).ToArray(); foreach (Vector3D point in points) { Vector3D[] circlePoints = OneHopfCircle(point); ProjectAndAddS3Points(mesh, circlePoints, shrink: false); } done.Add(seg.Midpoint); } } STL.SaveMeshToSTL(mesh.Mesh, @"D:\p4\R3\sample\out1.stl"); }
public static double GetNormalizedCircumRadius(int p, double q) { double hypot = GetTriangleHypotenuse(p, q); switch (Geometry2D.GetGeometry(p, q)) { case Geometry.Spherical: return(Spherical2D.s2eNorm(hypot) * DiskRadius); case Geometry.Euclidean: return(EuclideanHypotenuse); case Geometry.Hyperbolic: { if (Infinity.IsInfinite(hypot)) { return(DiskRadius); } return(DonHatch.h2eNorm(hypot) * DiskRadius); } } Debug.Assert(false); return(1); }
// Gets the distance between two points. private double Dist(Vector3D p1, Vector3D p2) { switch (this.Metric) { case Metric.Spherical: { // ZZZ - Is it too expensive to build up a mobius every time? // I wonder if there is a better way. Mobius m = new Mobius(); m.Isometry(Geometry.Spherical, 0, -p1); Vector3D temp = m.Apply(p2); return(Spherical2D.e2sNorm(temp.Abs())); } case Metric.Euclidean: { return((p2 - p1).Abs()); } case Metric.Hyperbolic: { // ZZZ - Is it too expensive to build up a mobius every time? // I wonder if there is a better way. Mobius m = new Mobius(); m.Isometry(Geometry.Hyperbolic, 0, -p1); Vector3D temp = m.Apply(p2); return(DonHatch.e2hNorm(temp.Abs())); } } throw new System.NotImplementedException(); }
public static void CalcEScale() { // Euclidean scale is arbitrary, but put it in the middle of the projections of 433 and 435. double r3 = Spherical2D.s2eNorm(Honeycomb.CircumRadius(4, 3, 3)); double r5 = DonHatch.h2eNorm(Honeycomb.CircumRadius(4, 3, 5)); m_eScale = (r3 + r5) / (2 * Math.Sqrt(3)); }
private static void HopfFibration(Tiling tiling) { int segDivisions = 10; int circleDivisions = 125; Shapeways mesh = new Shapeways(); HashSet <Vector3D> done = new HashSet <Vector3D>(); foreach (Tile tile in tiling.Tiles) { foreach (Segment seg in tile.Boundary.Segments) { if (done.Contains(seg.Midpoint)) { continue; } // Subdivide the segment, and project points to S2. Vector3D[] points = seg.Subdivide(segDivisions).Select(v => Spherical2D.PlaneToSphere(v)).ToArray(); foreach (Vector3D point in points) { // Get the hopf circle and add to mesh. // http://en.wikipedia.org/wiki/Hopf_fibration#Explicit_formulae double a = point.X; double b = point.Y; double c = point.Z; double factor = 1 / (Math.Sqrt(1 + c)); if (Tolerance.Equal(c, -1)) { continue; } List <Vector3D> circlePoints = new List <Vector3D>(); double angleInc = 2 * Math.PI / circleDivisions; double angle = 0; for (int i = 0; i <= circleDivisions; i++) { double sinTheta = Math.Sin(angle); double cosTheta = Math.Cos(angle); circlePoints.Add(new Vector3D( (1 + c) * cosTheta, a * sinTheta - b * cosTheta, a * cosTheta + b * sinTheta, (1 + c) * sinTheta)); angle += angleInc; } bool shrink = false; ProjectAndAddS3Points(mesh, circlePoints.ToArray(), shrink); } done.Add(seg.Midpoint); } } STL.SaveMeshToSTL(mesh.Mesh, @"D:\p4\R3\sample\out1.stl"); }
/// <summary> /// Outputs edges to an stl file. /// </summary> public void Output() { System.Func <Vector3D, Vector3D> p2s = v => Spherical2D.PlaneToSphere(v); System.Func <Vector3D, Vector3D> transform = v => H3Models.Ball.ApplyMobius(Mobius.Scale(3), v); double min = double.MaxValue; Cell cell = m_cells.First(); foreach (Vector3D v1 in cell.Tiles[0].Boundary.Vertices) { foreach (Vector3D v2 in cell.Tiles[1].Boundary.Vertices) { min = Math.Min(min, p2s(v1).Dist(p2s(v2))); } } // XXX - code below so ugly to read! Dictionary <TileVertex, TileVertex> vMap = new Dictionary <TileVertex, TileVertex>(); for (int tile_i = 0; tile_i < cell.Tiles.Length; tile_i++) { for (int tile_j = tile_i + 1; tile_j < cell.Tiles.Length; tile_j++) { for (int vertex_i = 0; vertex_i < cell.Tiles[tile_i].Boundary.Vertices.Length; vertex_i++) { for (int vertex_j = 0; vertex_j < cell.Tiles[tile_j].Boundary.Vertices.Length; vertex_j++) { Vector3D v1 = cell.Tiles[tile_i].Boundary.Vertices[vertex_i]; Vector3D v2 = cell.Tiles[tile_j].Boundary.Vertices[vertex_j]; if (Tolerance.Equal(p2s(v1).Dist(p2s(v2)), min)) { vMap[new TileVertex(tile_i, vertex_i)] = new TileVertex(tile_j, vertex_j); } } } } } HashSet <H3.Cell.Edge> edges = new HashSet <H3.Cell.Edge>(new H3.Cell.EdgeEqualityComparer()); foreach (Cell c in m_cells) { foreach (KeyValuePair <TileVertex, TileVertex> kvp in vMap) { Vector3D v1 = transform(p2s(c.Tiles[kvp.Key.Item1].Boundary.Vertices[kvp.Key.Item2])); Vector3D v2 = transform(p2s(c.Tiles[kvp.Value.Item1].Boundary.Vertices[kvp.Value.Item2])); edges.Add(new H3.Cell.Edge(v1, v2)); } } //H3.m_settings.ThinEdges = true; H3.SaveToFile("ultrainf", edges.ToArray(), finite: false); }
public static Vector3D EdgeMidpointSpherical(int p, int q, int r) { // Get a {q,p} tiling on the z=0 plane. Segment[] baseTileSegments = BaseTileSegments(q, p); Vector3D direction = H3Models.UHSToBall(baseTileSegments.First().Midpoint); direction.Normalize(); double midRadius = Spherical2D.s2eNorm(Honeycomb.MidRadius(p, q, r)); return(direction * midRadius); }
public static Vector3D FaceCenterSpherical(int p, int q, int r) { // Get a {q,p} tiling on the z=0 plane. Segment[] baseTileSegments = BaseTileSegments(q, p); // This will be unit length. Vector3D pFaceDirection = H3Models.UHSToBall(baseTileSegments.First().P1); // In-radius is in conformal model double inRadius = Spherical2D.s2eNorm(Honeycomb.InRadius(p, q, r)); return(pFaceDirection * inRadius); }
/// <summary> /// Helper to project points from S3 -> S2, then add an associated curve. /// </summary> private static void ProjectAndAddS3Points(Shapeways mesh, Vector3D[] pointsS3, bool shrink) { // Project to S3, then to R3. List <Vector3D> projected = new List <Vector3D>(); foreach (Vector3D v in pointsS3) { v.Normalize(); Vector3D c = v.ProjectTo3DSafe(1.0); // Pull R3 into a smaller open disk. if (shrink) { double mag = Math.Atan(c.Abs()); c.Normalize(); c *= mag; } projected.Add(c); } System.Func <Vector3D, double> sizeFunc = v => { // Constant thickness. // return 0.08; double sphericalThickness = 0.002; double abs = v.Abs(); if (shrink) { abs = Math.Tan(abs); // The unshrunk abs. } // The thickness at this vector location. double result = Spherical2D.s2eNorm(Spherical2D.e2sNorm(abs) + sphericalThickness) - abs; if (shrink) { result *= Math.Atan(abs) / abs; // shrink it back down. } return(result); }; mesh.AddCurve(projected.ToArray(), sizeFunc); }
public static double GetNormalizedCircumRadius(int p, int q) { Geometry g = Geometry2D.GetGeometry(p, q); double hypot = GetTriangleHypotenuse(p, q); switch (g) { case Geometry.Spherical: return(Spherical2D.s2eNorm(hypot) * DiskRadius); case Geometry.Euclidean: return(EuclideanHypotenuse); case Geometry.Hyperbolic: return(DonHatch.h2eNorm(hypot) * DiskRadius); } Debug.Assert(false); return(1); }
/// <summary> /// Mirrors for Spherical geometry, in the ball model. /// </summary> public static Sphere[] MirrorsSpherical(int p, int q, int r) { // Get a {q,p} tiling on the z=0 plane. Segment[] baseTileSegments = BaseTileSegments(q, p); // This will be unit length. Vector3D pFaceDirection = H3Models.UHSToBall(baseTileSegments.First().P1); // In-radius is in conformal model double inRadius = Spherical2D.s2eNorm(Honeycomb.InRadius(p, q, r)); double centerOfSphereNE = (1 - inRadius) / (1 + inRadius); Vector3D center; double radius; H3Models.Ball.DupinCyclideSphere(-pFaceDirection * centerOfSphereNE, 1.0 /*geodesic circle*/, Geometry.Spherical, out center, out radius); Sphere cellBoundary = new Sphere() { Center = center, Radius = radius, Invert = true }; Sphere[] interior = InteriorMirrors(p, q); interior = interior.Select(s => H3Models.UHSToBall(s)).ToArray(); Sphere[] surfaces = new Sphere[] { cellBoundary, interior[0], interior[1], interior[2] }; // Apply rotations. bool applyRotations = false; if (applyRotations) { double rotation = Math.PI / 2; foreach (Sphere s in surfaces) { RotateSphere(s, rotation); } } return(surfaces); }
public static Sphere[] Mirrors(int p, int q, int r, ref Vector3D cellCenter, bool moveToBall = true, double scaling = -1) { Geometry g = Util.GetGeometry(p, q, r); if (g == Geometry.Spherical) { // These are in the ball model. Sphere[] result = SimplexCalcs.MirrorsSpherical(p, q, r); return(result); } else if (g == Geometry.Euclidean) { return(SimplexCalcs.MirrorsEuclidean()); } // This is a rotation we'll apply to the mirrors at the end. // This is to try to make our image outputs have vertical bi-lateral symmetry and the most consistent in all cases. // NOTE: + is CW, not CCW. (Because the way I did things, our images have been reflected vertically, and I'm too lazy to go change this.) double rotation = Math.PI / 2; // Some construction points we need. Vector3D p1, p2, p3; Segment seg = null; TilePoints(p, q, out p1, out p2, out p3, out seg); // // Construct in UHS // Geometry cellGeometry = Geometry2D.GetGeometry(p, q); Vector3D center = new Vector3D(); double radius = 0; if (cellGeometry == Geometry.Spherical) { // Finite or Infinite r // Spherical trig double halfSide = Geometry2D.GetTrianglePSide(q, p); double mag = Math.Sin(halfSide) / Math.Cos(Util.PiOverNSafe(r)); mag = Math.Asin(mag); // e.g. 43j //mag *= 0.95; // Move mag to p1. mag = Spherical2D.s2eNorm(mag); H3Models.Ball.DupinCyclideSphere(p1, mag, Geometry.Spherical, out center, out radius); } else if (cellGeometry == Geometry.Euclidean) { center = p1; radius = p1.Dist(p2) / Math.Cos(Util.PiOverNSafe(r)); } else if (cellGeometry == Geometry.Hyperbolic) { if (Infinite(p) && Infinite(q) && FiniteOrInfinite(r)) { //double iiiCellRadius = 2 - Math.Sqrt( 2 ); //Circle3D iiiCircle = new Circle3D() { Center = new Vector3D( 1 - iiiCellRadius, 0, 0 ), Radius = iiiCellRadius }; //radius = iiiCellRadius; // infinite r //center = new Vector3D( 1 - radius, 0, 0 ); // For finite r, it was easier to calculate cell facet in a more symmetric position, // then move into position with the other mirrors via a Mobius transformation. double rTemp = 1 / (Math.Cos(Util.PiOverNSafe(r)) + 1); Mobius m = new Mobius(); m.Isometry(Geometry.Hyperbolic, -Math.PI / 4, new Vector3D(0, Math.Sqrt(2) - 1)); Vector3D c1 = m.Apply(new Vector3D(1 - 2 * rTemp, 0, 0)); Vector3D c2 = c1; c2.Y *= -1; Vector3D c3 = new Vector3D(1, 0); Circle3D c = new Circle3D(c1, c2, c3); radius = c.Radius; center = c.Center; } else if (Infinite(p) && Finite(q) && FiniteOrInfinite(r)) { // http://www.wolframalpha.com/input/?i=r%2Bx+%3D+1%2C+sin%28pi%2Fp%29+%3D+r%2Fx%2C+solve+for+r // radius = 2 * Math.Sqrt( 3 ) - 3; // Appolonian gasket wiki page //radius = Math.Sin( Math.PI / q ) / ( Math.Sin( Math.PI / q ) + 1 ); //center = new Vector3D( 1 - radius, 0, 0 ); // For finite r, it was easier to calculate cell facet in a more symmetric position, // then move into position with the other mirrors via a Mobius transformation. double rTemp = 1 / (Math.Cos(Util.PiOverNSafe(r)) + 1); Mobius m = new Mobius(); m.Isometry(Geometry.Hyperbolic, 0, p2); Vector3D findingAngle = m.Inverse().Apply(new Vector3D(1, 0)); double angle = Math.Atan2(findingAngle.Y, findingAngle.X); m.Isometry(Geometry.Hyperbolic, angle, p2); Vector3D c1 = m.Apply(new Vector3D(1 - 2 * rTemp, 0, 0)); Vector3D c2 = c1; c2.Y *= -1; Vector3D c3 = new Vector3D(1, 0); Circle3D c = new Circle3D(c1, c2, c3); radius = c.Radius; center = c.Center; } else if (Finite(p) && Infinite(q) && FiniteOrInfinite(r)) { radius = p2.Abs(); // infinite r radius = DonHatch.asinh(Math.Sinh(DonHatch.e2hNorm(p2.Abs())) / Math.Cos(Util.PiOverNSafe(r))); // hyperbolic trig // 4j3 //m_jOffset = radius * 0.02; //radius += m_jOffset ; radius = DonHatch.h2eNorm(radius); center = new Vector3D(); rotation *= -1; } else if (/*Finite( p ) &&*/ Finite(q)) { // Infinite r //double mag = Geometry2D.GetTrianglePSide( q, p ); // Finite or Infinite r double halfSide = Geometry2D.GetTrianglePSide(q, p); double mag = DonHatch.asinh(Math.Sinh(halfSide) / Math.Cos(Util.PiOverNSafe(r))); // hyperbolic trig H3Models.Ball.DupinCyclideSphere(p1, DonHatch.h2eNorm(mag), out center, out radius); } else { throw new System.NotImplementedException(); } } Sphere cellBoundary = new Sphere() { Center = center, Radius = radius }; Sphere[] interior = InteriorMirrors(p, q); Sphere[] surfaces = new Sphere[] { cellBoundary, interior[0], interior[1], interior[2] }; // Apply rotations. bool applyRotations = true; if (applyRotations) { foreach (Sphere s in surfaces) { RotateSphere(s, rotation); } p1.RotateXY(rotation); } // Apply scaling bool applyScaling = scaling != -1; if (applyScaling) { //double scale = 1.0/0.34390660467269524; //scale = 0.58643550768408892; foreach (Sphere s in surfaces) { Sphere.ScaleSphere(s, scaling); } } bool facetCentered = false; if (facetCentered) { PrepForFacetCentering(p, q, surfaces, ref cellCenter); } // Move to ball if needed. if (moveToBall) { surfaces = MoveToBall(surfaces, ref cellCenter); } return(surfaces); }
public static Vector3D VertexSpherical(int p, int q, int r) { double circumRadius = Spherical2D.s2eNorm(Honeycomb.CircumRadius(p, q, r)); return(new Vector3D(0, 0, -circumRadius)); }