// 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(); }
/// <summary> /// Offsets a vector by a hyperbolic distance. /// </summary> public static Vector3D Offset(Vector3D v, double hDist) { double mag = v.Abs(); mag = DonHatch.h2eNorm(DonHatch.e2hNorm(mag) + hDist); v.Normalize(); v *= mag; return(v); }
public void AnimationSections(Settings config) { HoneycombDef imageData = new HoneycombDef(config.P, config.Q, config.R); int p = imageData.P, q = imageData.Q, r = imageData.R; string filename = imageData.FormatFilename(); Sphere[] mirrors = SimplexCalcs.Mirrors(p, q, r); double bounds = 1.0; //config.UhsBoundary.Bounds; bounds = 9.0; // Calculate the color scale. int size = 200; CoxeterImages.Settings settings = new CoxeterImages.Settings() { Honeycomb = imageData, Width = size, Height = size, Bounds = bounds, Mirrors = mirrors, FileName = imageData.FormatFilename(), }; CoxeterImages imageCalculator = new CoxeterImages(); //imageCalculator.AutoCalcScale( settings ); if (settings.ColorScaling < 1) { settings.ColorScaling = 15; } settings.ColorScaling = 11; Program.Log("\nGenerating sections..."); size = 500; settings.Width = size; settings.Height = size; settings.FileName = filename; double max = Spherical2D.e2sNorm(15); double min = Spherical2D.e2sNorm(1.0 / 15); DonHatch.e2hNorm(max); int numSteps = 1800; // 1 minute double step = (max - min) / numSteps; for (int i = 0; i < 1; i++) { Program.Log("\nSection " + i); imageCalculator.m_z = 1.0 / 0.5; Spherical2D.s2eNorm(min + step * i); DonHatch.h2eNorm(step * i); settings.FileName = string.Format("533_{0:D4}.png", i); imageCalculator.GenImage(settings); } }
private void Transform(double anim, IEnumerable <Tile> tetTiles) { //TilingConfig config = new TilingConfig( 8, 3, 4 ); // Reproduces Tolerance issues with {3,3,7}, though not actually correct to be applying hyperbolic transforms anyway (only spherical). TilingConfig config = new TilingConfig(3, 3, 1); Mobius m = new Mobius(); m = Mobius.Identity(); // Invert Complex c1 = new Complex(0, 1); Complex c2 = new Complex(1, 0); Complex c3 = new Complex(0, -0.999999999999); // - 1 doesn't work //m.MapPoints( c1, c2, c3, c3, c2, c1 ); //Mobius m = config.DualMobius(); //m.Isometry( Geometry.Spherical, 0, new Complex( 1.2345, -0.4321 ) ); // nice one //m.Isometry( Geometry.Spherical, 0, new Complex( 0, 0.148125 ) ); // half plane // Animation. double p2 = DonHatch.e2hNorm(0.6); double p2Interp = DonHatch.h2eNorm(p2 * anim); //m.Isometry( Geometry.Spherical, 0, -p2Interp ); m.Isometry(Geometry.Hyperbolic, 0, new Complex(-p2Interp, 0)); Mobius m2 = new Mobius(); m2.Isometry(Geometry.Hyperbolic, 0, new Complex(-0.6, 0)); m2 = m_fixedCircleToStandardDisk.Inverse() * m2 * m_fixedCircleToStandardDisk; bool firstAnim = false; if (firstAnim) { m = m_fixedCircleToStandardDisk.Inverse() * m * m_fixedCircleToStandardDisk; m_animMobius = m; } else { m = m_neighborToStandardDisk.Inverse() * m * m_neighborToStandardDisk; m_animMobius = m2 * m; } m_animMobius.Normalize(); foreach (Tile t in tetTiles) { t.Transform(m_animMobius); } foreach (Tile t in m_tiles) { t.Transform(m_animMobius); } m_equator.Transform(m_animMobius); m_neighborCircle.Transform(m_animMobius); }
/// <summary> /// Equally subdivides a segment with a startpoint at the origin, in the respective geometry. /// </summary> private static Vector3D[] SubdivideRadialInGeometry(Segment radial, int divisions, Geometry g) { List <Vector3D> result = new List <Vector3D>(); if (radial.Type != SegmentType.Line) { Debug.Assert(false); return(result.ToArray()); } switch (g) { case Geometry.Spherical: { double eLength = radial.Length; double sLength = Spherical2D.e2sNorm(eLength); double divLength = sLength / divisions; for (int i = 0; i <= divisions; i++) { double temp = Spherical2D.s2eNorm(divLength * i); result.Add(radial.P2 * temp / eLength); } break; } case Geometry.Euclidean: return(radial.Subdivide(divisions)); case Geometry.Hyperbolic: { double eLength = radial.Length; double hLength = DonHatch.e2hNorm(eLength); double divLength = hLength / divisions; for (int i = 0; i <= divisions; i++) { double temp = DonHatch.h2eNorm(divLength * i); result.Add(radial.P2 * temp / eLength); } break; } } return(result.ToArray()); }
public static Sphere GeodesicOffset(Sphere s, double offset, bool ball = true) { Sphere offsetSphere; if (ball) { // Geodesic offset (ball). { // Hyperbolic honeycomb double mag = s.Center.Abs() - s.Radius; mag = s.IsPlane ? DonHatch.h2eNorm(offset) : DonHatch.h2eNorm(DonHatch.e2hNorm(mag) - offset); Vector3D closestPointToOrigin = s.IsPlane ? s.Normal : s.Center; closestPointToOrigin.Normalize(); closestPointToOrigin *= mag; offsetSphere = H3Models.Ball.OrthogonalSphereInterior(closestPointToOrigin); // There are multiple ultraparallel spheres. // This experiments with picking others. Mobius m = new Mobius(); m.Isometry(Geometry.Hyperbolic, 0, new Vector3D(0, -0.2)); //H3Models.TransformInBall2( offsetSphere, m ); } { // Spherical honeycomb //offset *= -1; double mag = -s.Center.Abs() + s.Radius; Spherical2D.s2eNorm(Spherical2D.e2sNorm(mag) + offset); offsetSphere = s.Clone(); offsetSphere.Radius += offset * 10; } } else { // Geodesic offset (UHS). // XXX - not scaled right. offsetSphere = s.Clone(); offsetSphere.Radius += offset; } return(offsetSphere); }
public H3.Cell.Edge[] Helicoid() { List <H3.Cell.Edge> fiberList = new List <H3.Cell.Edge>(); // These two params affect each other (changing numFibers will affect rotation rate). double rotationRate = Math.PI / 78.5; int numFibers = 1000; // Note: we need to increment a constant hyperbolic distance each step. int count = 0; double max = DonHatch.e2hNorm(0.998); double offset = max * 2 / (numFibers - 1); for (double z_h = -max; z_h <= max; z_h += offset) { double z = DonHatch.h2eNorm(z_h); Sphere s = H3Models.Ball.OrthogonalSphereInterior(new Vector3D(0, 0, z)); Circle3D c = H3Models.Ball.IdealCircle(s); // Two endpoints of our fiber. Vector3D v1 = new Vector3D(c.Radius, 0, c.Center.Z); Vector3D v2 = new Vector3D(-c.Radius, 0, c.Center.Z); v1.RotateXY(rotationRate * count); v2.RotateXY(rotationRate * count); v1 = Transform(v1); v2 = Transform(v2); Vector3D t = Transform(new Vector3D(0, 0, z)); double cutoff = 0.995; if (t.Abs() > cutoff) { continue; } fiberList.Add(new H3.Cell.Edge(v1, v2, order: false)); count++; } return(fiberList.ToArray()); }
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); }