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); }
/// <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, int desiredCount) { Settings s = new Settings(); s.ThreshType = EdgeThreshType.Radial; // 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; double logDesiredCount = Math.Log(desiredCount); hDist = 5.5 + (logDesiredCount - Math.Log(count2)) / slope; s.Threshold = DonHatch.h2eNorm(hDist); return(CalcEdges(simplex, edges, s)); }
private Tile TemplateTile() { double inRadiusHyp = InRadius; double inRadiusEuclidean = DonHatch.h2eNorm(inRadiusHyp); double faceRadius = FaceRadius(inRadiusEuclidean); // Calc the midpoint, and project to plane. Vector3D midPoint = MidPoint(inRadiusEuclidean, faceRadius); midPoint.Z *= -1; midPoint = Sterographic.SphereToPlane(midPoint); //double midPointSpherical = MidPoint( inRadiusEuclidean, faceRadius ); //double midPoint = Spherical2D.s2eNorm( midPointSpherical ); // Create and scale based on our midpoint. Polygon poly = new Polygon(); poly.CreateRegular(Q, R); double standardMidpointAbs = poly.Segments[0].Midpoint.Abs(); m_shrink = midPoint.Abs() / standardMidpointAbs; poly.Scale(m_shrink); Matrix4D m = Matrix4D.MatrixToRotateinCoordinatePlane(-Math.PI / Q, 0, 1); poly.Rotate(m); return(new Tile(poly, poly.Clone(), Geometry.Hyperbolic)); }
// 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> /// Get the length of the side of a triangle opposite alpha, given the three angles of the triangle. /// NOTE: This does not work in Euclidean geometry! /// </summary> public static double GetTriangleSide(Geometry g, double alpha, double beta, double gamma) { switch (g) { case Geometry.Spherical: { // Spherical law of cosines return(Math.Acos((Math.Cos(alpha) + Math.Cos(beta) * Math.Cos(gamma)) / (Math.Sin(beta) * Math.Sin(gamma)))); } case Geometry.Euclidean: { // Not determined in this geometry. Debug.Assert(false); return(0.0); } case Geometry.Hyperbolic: { // Hyperbolic law of cosines // http://en.wikipedia.org/wiki/Hyperbolic_law_of_cosines return(DonHatch.acosh((Math.Cos(alpha) + Math.Cos(beta) * Math.Cos(gamma)) / (Math.Sin(beta) * Math.Sin(gamma)))); } } // Not determined in this geometry. Debug.Assert(false); return(0.0); }
private static Sphere[] GetSpheres(Sphere[] facets, Vector3D[] verts, Vector3D interiorPoint, double inSphereHRad) { // Get relevant points (near) inSphere. Vector3D[] transformed = verts.Select(v => { v = H3Models.Transform_PointToOrigin(v, interiorPoint); v.Normalize(); v *= DonHatch.h2eNorm(inSphereHRad * .5); v = H3Models.Transform_PointToOrigin(v, -interiorPoint); return(v); }).ToArray(); List <Sphere> result = new List <R3.Geometry.Sphere>(); result.Add(ConstructSphere(facets, transformed[0], new int[] { 1, 2, 3 })); result.Add(ConstructSphere(facets, transformed[1], new int[] { 0, 2, 3 })); result.Add(ConstructSphere(facets, transformed[2], new int[] { 0, 1, 3 })); result.Add(ConstructSphere(facets, transformed[3], new int[] { 0, 1, 2 })); return(result.ToArray()); /*Vector3D[] verts = SimplexCalcs.VertsBall( p, q, r ); * for( int i = 0; i < 4; i++ ) * { * double hDist = H3Models.Ball.HDist( cen, verts[i] ) - .05; * System.Diagnostics.Trace.WriteLine( hDist + " " + DonHatch.h2eNorm( hDist ) ); * }*/ }
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)); }
public static Vector3D Dini(Vector3D uv, double a, double b) { uv = DiskToUpper(uv); // Eq 1.86 on p36 of book Backlund and Darboux Transformations double eta = Math.PI / 2 - Math.PI / 20; //double eta = Math.PI / 2; double p = 1; // curvature double x = DonHatch.acosh(uv.Y); // Used info on mathworld for tractrix to figure this out. //double x = DonHatch.acosh( Math.Exp( DonHatch.acosh( ( uv.Y * uv.Y + 1 ) / ( 2 * uv.Y ) ) ) ); //double x = Math.Log( uv.Y ); double y = uv.X; double pSinEta = p * Math.Sin(eta); double chi = (x - y * Math.Cos(eta)) / pSinEta; if (x <= -4 || x > 4 || y < -3 * Math.PI || y > 3 * Math.PI) { return(Infinity.InfinityVector); } Vector3D result = new Vector3D( pSinEta * Sech(chi) * Math.Cos(y / p), pSinEta * Sech(chi) * Math.Sin(y / p), x - pSinEta * Math.Tanh(chi)); return(result); /* * System.Func<double, Complex> tractrix = new System.Func<double, Complex>( * ( t ) => * { * //return new Complex( t - Math.Tanh( t ), 1.0 / Math.Cosh( t ) ); * return new Complex( - Math.Sqrt( 1 - 1 / (t*t) ) + DonHatch.acosh( t ), 1.0 / t ); * } ); * * double logy = Math.Log( uv.Y ); * //Complex tract = tractrix( logy ); * Complex tract = tractrix( uv.Y ); * return new Vector3D( * a * Math.Cos( uv.X ) * tract.Imaginary, * a * Math.Sin( uv.X ) * tract.Imaginary, * a * tract.Real + b * uv.X ); */ /* * return new Vector3D( * a * Math.Cos( uv.X ) / Math.Cosh( uv.Y ), * a * Math.Sin( uv.X ) / Math.Cosh( uv.Y ), * a * (uv.Y - Math.Tanh( uv.Y )) + b * uv.X ); */ /*return new Vector3D( * a * Math.Cos( uv.X ) * Math.Sin( uv.Y ), * a * Math.Sin( uv.X ) * Math.Sin( uv.Y ), * a * (Math.Cos( uv.Y ) + Math.Log( Math.Tan( 0.5 * uv.Y ) )) + b * uv.X );*/ }
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); } }
/// <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 static double EdgeLength(int p, int q, int r) { double pip = PiOverNSafe(p); double pir = PiOverNSafe(r); double pi_hqr = Pi_hpq(q, r); double edgeLength = 2 * DonHatch.acosh(Math.Cos(pip) * Math.Sin(pir) / Math.Sin(pi_hqr)); return(edgeLength); }
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); }
private static Sphere ConstructSphere(Vector3D p, double hDist) { double eDist = DonHatch.h2eNorm(hDist); Vector3D cen; double rad; H3Models.Ball.DupinCyclideSphere(p, eDist, out cen, out rad); Sphere s = new Sphere() { Center = cen, Radius = rad }; return(s); }
/// <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()); }
static public double OffsetInModel(Tiler.Settings settings, double p = 0, double q = 0, double r = 1) { double off = OffsetInSpace(settings, p, q, r); switch (settings.Geometry) { case Geometry.Spherical: off = Spherical2D.s2eNorm(off); break; case Geometry.Hyperbolic: off = DonHatch.h2eNorm(off); break; } return(off); }
internal static void CalcSelfSimilarityScale() { double inRadius = DonHatch.h2eNorm(Honeycomb.InRadius(4, 3, 7)); Vector3D facePoint = new Vector3D(0, 0, -inRadius); Sphere s = H3Models.Ball.OrthogonalSphereInterior(facePoint); Vector3D facePoint2 = new Vector3D(0, 0, inRadius); facePoint2 = s.ReflectPoint(facePoint2); facePoint = H3Models.BallToUHS(facePoint); facePoint2 = H3Models.BallToUHS(facePoint2); double scale = facePoint.Z / facePoint2.Z; scale += 0; }
/// <summary> /// Returns the mid-radius, in the induced geometry. /// </summary> public static double MidRadius(int p, int q, int r) { double pir = PiOverNSafe(r); double inRadius = InRadius(p, q, r); double midRadius = DonHatch.sinh(inRadius) / Math.Sin(pir); switch (GetGeometry(p, q, r)) { case Geometry.Hyperbolic: return(DonHatch.asinh(midRadius)); case Geometry.Spherical: return(Math.Asin(midRadius)); } throw new System.NotImplementedException(); }
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); }
/// <summary> /// Returns the in-radius, in the induced geometry. /// </summary> public static double InRadius(int p, int q, int r) { double pip = PiOverNSafe(p); double pir = PiOverNSafe(r); double pi_hpq = Pi_hpq(p, q); double inRadius = Math.Sin(pip) * Math.Cos(pir) / Math.Sin(pi_hpq); switch (GetGeometry(p, q, r)) { case Geometry.Hyperbolic: return(DonHatch.acosh(inRadius)); case Geometry.Spherical: return(Math.Acos(inRadius)); } throw new System.NotImplementedException(); }
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 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); }
public static double SphereToCyl(double x) { return(DonHatch.atanh(Math.Sin(x))); }
private static double EqualAreaToPoincare(double dist) { double h = 2 * DonHatch.asinh(dist); return(DonHatch.h2eNorm(h)); }
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 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"); }
/// <summary> /// Calculate basePoints along a circle defined by a center on the z axis, and going through the point (0,0,1). /// .5 is horosphere, 1 is geodesic /// Result is in UHS model /// </summary> private static Vector3D[] BasePointsCircle(double centerUHS) { if (centerUHS > 1) { centerUHS = 1; } if (centerUHS < 0) { centerUHS = 0; } if (centerUHS == 0) { return new Vector3D[] { new Vector3D() } } ; bool hyperbolicOffsets = true; if (hyperbolicOffsets) { // h-distance between each point. double d = 0.1; // We need to work in 2D first, then we'll switch to xz plane. Circle circle = new Circle(new Vector3D(0, 1), new Vector3D(0, 1 - 2 * centerUHS), new Vector3D(centerUHS, 1 - centerUHS)); // XXX - check to make sure total distance won't wrap around the circle. List <Vector3D> points = new List <Vector3D>(); int count = 75; for (int i = -count; i <= count; i++) { double currentD = d * i; // Angle t around a circle with center c and radius r to get an h-distance. // https://en.wikipedia.org/wiki/Poincar%C3%A9_half-plane_model // http://www.wolframalpha.com/input/?i=d+%3D+arcosh%281%2B%28%28r*sin%28t%29%29%5E2%2B%28r*cos%28t%29-r%29%5E2%29%2F%282*%28c%2Br%29*%28c%2Br*cos%28t%29%29%29%29%2C+solve+for+t double c = circle.Center.Y; double r = circle.Radius; double coshd = DonHatch.cosh(currentD); double numerator = c * c - c * (c + r) * coshd + c * r + r * r; double denominator = r * ((c + r) * coshd - c); double angle = Math.Acos(numerator / denominator); if (i < 0) { angle *= -1; } points.Add(new Vector3D(r * Math.Sin(angle), 0, c + r * Math.Cos(angle))); /* * // XXX - This was my first attempt, but this code only works for geodesics, not general arcs! * // Equidistant lines in UHS will all be lines through the origin. * // In the following formula, x is the angle to use to get h-spaced equidistant line a distance d away. * // http://www.wolframalpha.com/input/?i=d+%3D+arccosh%28sec%28x%29%29%2C+solve+for+x * double angle = Math.Acos( 1.0 / DonHatch.cosh( currentD ) ); * angle = Math.PI/2 - angle; * if( i < 0 ) * angle *= -1; * * Vector3D p1, p2; * Euclidean2D.IntersectionLineCircle( new Vector3D(), new Vector3D( Math.Cos(angle), Math.Sin(angle) ), circle, out p1, out p2 ); * Vector3D highest = p1.Y > p2.Y ? p1 : p2; * points.Add( new Vector3D( highest.X, 0, highest.Y ) ); */ } return(points.ToArray()); } else { // equal euclidean spacing. Circle3D c = new Circle3D(new Vector3D(0, 0, 1), new Vector3D(0, 0, 1 - 2 * centerUHS), new Vector3D(centerUHS, 0, 1 - centerUHS)); return(c.Subdivide(125)); } }
// In hyperboloid model public static double GeodesicPlaneHSDF(Vector3D samplePoint, Vector3D dualPoint, double offset = 0) { double dot = -DotInGeometry(Geometry.Hyperbolic, samplePoint, dualPoint); return(DonHatch.asinh(dot) - offset); }
/// <summary> /// Calculates a mesh for a standard euclidean catenoid. /// This will need to be transformed to the various locations later. /// /// Like above, but we adjust the xy components of the mesh using one of the mappings described here: /// https://arxiv.org/ftp/arxiv/papers/1509/1509.06344.pdf /// I found that paper here: /// https://stackoverflow.com/questions/13211595/how-can-i-convert-coordinates-on-a-circle-to-coordinates-on-a-square /// This is so we can connect up to the RLD mesh later. /// </summary> private static Mesh CatenoidSquared(double waist, double height) { Mesh mesh = new Mesh(); int res = m_params.Res * 2; double diskRad = waist * Math.Cosh(height / 2 / waist);; // NOTE: A band is *not* a constant height slice, // so the input z value is the height at the edge midpoints of the square. Func <double, Vector3D[]> oneCircle = z => { bool neg = z < 0; z = Math.Abs(z); // Radius on disk at a starting edge midpoint. double r = waist * Math.Cosh(z / waist); Vector3D start = new Vector3D(r, 0); Vector3D axis = new Vector3D(0, 0, 1); List <Vector3D> points = new List <Vector3D>(); double angleInc = 2 * Math.PI / res; double angle = 0; for (int i = 0; i < res; i++) { Vector3D point = start; point.RotateAboutAxis(axis, angle); point = DiskToSquare(point, diskRad); double zi = waist * DonHatch.acosh(point.Abs() / waist); if (double.IsNaN(zi)) { zi = 0; } if (neg) { zi *= -1; } Vector3D newPoint = new Vector3D(point.X, point.Y, zi); if (newPoint.DNE) { throw new System.Exception(); } points.Add(newPoint); angle += angleInc; } return(points.ToArray()); }; double inc = height / (res * 2); for (int i = 0; i < res; i++) { double z1 = inc * i; double z2 = inc * (i + 1); mesh.AddBand(oneCircle(z1), oneCircle(z2)); mesh.AddBand(oneCircle(-z1), oneCircle(-z2)); } return(mesh); }
private static double EquidistantToPoincare(double dist) { return(DonHatch.h2eNorm(dist)); }