/// <summary> /// Same as above, but works with all geometries. /// </summary> public static Circle EquidistantOffset(Geometry g, Segment seg, double offset) { Mobius m = new Mobius(); Vector3D direction; if (seg.Type == SegmentType.Line) { direction = seg.P2 - seg.P1; direction.RotateXY(Math.PI / 2); } else { direction = seg.Circle.Center; } direction.Normalize(); m.Isometry(g, 0, direction * offset); // Transform 3 points on segment. Vector3D p1 = m.Apply(seg.P1); Vector3D p2 = m.Apply(seg.Midpoint); Vector3D p3 = m.Apply(seg.P2); return(new Circle(p1, p2, p3)); }
private static Vector3D DiskToUpper(Vector3D input) { Mobius m = new Mobius(); m.UpperHalfPlane(); return(m.Apply(input)); }
/// <summary> /// Subdivides a segment from p1->p2 with the two endpoints not on the origin, in the respective geometry. /// </summary> public static Vector3D[] SubdivideSegmentInGeometry(Vector3D p1, Vector3D p2, int divisions, Geometry g) { // Handle this specially, so we can keep things 3D if needed. if (g == Geometry.Euclidean) { Segment seg = Segment.Line(p1, p2); return(seg.Subdivide(divisions)); } Mobius p1ToOrigin = new Mobius(); p1ToOrigin.Isometry(g, 0, -p1); Mobius inverse = p1ToOrigin.Inverse(); Vector3D newP2 = p1ToOrigin.Apply(p2); Segment radial = Segment.Line(new Vector3D(), newP2); Vector3D[] temp = SubdivideRadialInGeometry(radial, divisions, g); List <Vector3D> result = new List <Vector3D>(); foreach (Vector3D v in temp) { result.Add(inverse.Apply(v)); } return(result.ToArray()); }
public int Closest(Vector3D p) { // Needs to be non-euclidean calc, // Moving the hex to the center will make that be the case. Mobius m = MobiusToCenter; Polygon poly = Hexagon.Clone(); poly.Transform(m); p = m.Apply(p); double d1 = poly.Segments[1].Midpoint.Dist(p); double d2 = poly.Segments[3].Midpoint.Dist(p); double d3 = poly.Segments[5].Midpoint.Dist(p); double min = Math.Min(d1, Math.Min(d2, d3)); if (min == d1) { return(4); } if (min == d2) { return(0); } if (min == d3) { return(2); } return(-1); }
public static Vector3D LoxodromicToIsometric(Vector3D v, int p, int m, int n) { Mobius mob = Mobius.CreateFromIsometry(Geometry.Spherical, 0, new System.Numerics.Complex(1, 0)); v = mob.Apply(v); return(SpiralToIsometric(v, p, m, n)); }
// 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> /// Apply a Mobius transform to us. /// </summary> public void Transform(Mobius m) { foreach (Segment s in this.Segments) { s.Transform(m); } Center = m.Apply(Center); }
public static void Test() { S3.HopfOrbit(); Mobius m = new Mobius(); m.UpperHalfPlane(); Vector3D test = m.Apply(new Vector3D()); test *= 1; }
public Vector3D TinyOffset(int awayFromSeg) { // Center; Mobius m = MobiusToCenter; Vector3D p = Hexagon.Center; Polygon poly = Hexagon.Clone(); poly.Transform(m); p = m.Apply(p); // Do the offset. p -= poly.Segments[awayFromSeg].Midpoint / 10; // Go back. p = m.Inverse().Apply(p); return(p); }
public static void DrawElements(HyperbolicModel model, Vector3D[] textureCoords, Vector3D[] textureVerts, int[] elements, Isometry mouseIsometry, double textureScale) { ////////////////////// ZZZ - Use VBOs GL.Begin(BeginMode.Triangles); { double factor = textureScale; int skipped = 0; for (int i = 0; i < elements.Length; i++) { int idx = elements[i]; // In Poincare model. GL.TexCoord2((textureCoords[idx].X * factor + 1) / 2, (textureCoords[idx].Y * factor + 1) / 2); Complex transformed = textureVerts[idx].ToComplex(); if (mouseIsometry != null) { transformed = mouseIsometry.Apply(transformed); } switch (model) { case HyperbolicModel.Poincare: { Vertex(transformed); break; } case HyperbolicModel.Klein: { Vector3D temp = Vector3D.FromComplex(transformed); Vertex(PoincareToKlein(temp)); break; } case HyperbolicModel.Pseudosphere: { Mobius m = new Mobius(); m.UpperHalfPlane(); Complex u = m.Apply(transformed); double x = u.Real; double y = u.Imaginary; double max = 1 * System.Math.PI; double min = -1 * System.Math.PI; if (0 == i % 3 && (x < min - 1 || x > max + 1 || y < 0)) { skipped = 1; continue; } if (skipped > 0 && skipped < 3) { skipped++; continue; } skipped = 0; GL.TexCoord2((textureCoords[idx].X * factor + 1) / 2, (textureCoords[idx].Y * factor + 1) / 2); // Pseudosphere Func <double, Complex> tractrix = new Func <double, Complex>( (t) => { return(new Complex(t - Math.Tanh(t), 1.0 / Math.Cosh(t))); }); //Vector3D temp1 = Vector3D.FromComplex( u ); if (x < min) { x = min; } if (x > max) { x = max; } if (y < 1) { y = 1; } Vector3D temp1 = new Vector3D(x, y); double logy = Math.Log(temp1.Y); Complex tract = tractrix(logy); Vector3D temp2 = new Vector3D( Math.Cos(temp1.X) * tract.Imaginary, Math.Sin(temp1.X) * tract.Imaginary, tract.Real); GL.Vertex3(temp2.X, temp2.Y, temp2.Z); //temp1 = m.Inverse().Apply( temp1 ); //GL.Vertex3( temp1.X, temp1.Y, temp1.Z ); //Vertex( temp1 ); break; } case HyperbolicModel.Hyperboloid: { Vector3D hyper = Sterographic.PlaneToHyperboloid(Vector3D.FromComplex(transformed)); // Hyperboloid GL.Vertex3(hyper.X, hyper.Y, hyper.Z); break; } default: { System.Diagnostics.Debug.Assert(false); break; } } /* // PETALS * int petals = 7; * double newMag = transformed.Magnitude * ( 1 + 0.5 * Math.Sin( transformed.Phase * petals ) ); * double newPhase = transformed.Phase + ( -0.2 * newMag * Math.Pow( Math.Sin( newMag * 3 ), 1 ) * Math.Cos( transformed.Phase * petals ) ); * transformed = Complex.FromPolarCoordinates( newMag, newPhase ); * * Vertex( transformed ); * */ //double mag = System.Math.Pow( transformed.Magnitude, 3 ) / transformed.Magnitude; // nice //double mag = System.Math.Pow( transformed.Magnitude - 3, 2 ) + .0; // looks spherical //double mag = transformed.Magnitude + 0.1* System.Math.Sin( transformed.Magnitude * 15 ); // Fun warping (20 is cool too) //Vertex( transformed * mag ); /*double xmag = 1; * double ymag = transformed.Imaginary + 0.1 * System.Math.Sin( transformed.Imaginary * 15 ); * xmag = System.Math.Abs( xmag ); * ymag = System.Math.Abs( ymag ); * Vertex( new Complex( transformed.Real * xmag, transformed.Imaginary * ymag ) ); */ //Vertex( 2 / System.Math.PI * Complex.Log( ( 1 + transformed ) / ( 1 - transformed ) ) ); // Band model //Vertex( Complex.Pow( transformed, 3 ) / transformed.Magnitude ); // Spikey // Spiral //Complex band = 2 / System.Math.PI * Complex.Log( ( 1 + transformed ) / ( 1 - transformed ) ); //band = new Complex( band.Real, band.Imaginary + 0.3 * System.Math.Sin( band.Real * 2 ) ); //band = new Complex( band.Real * .5, band.Imaginary ); //band += new Complex( 0, .5 ); //Vertex( band ); /* * double x = band.Real; * double y = band.Imaginary; * * double r = System.Math.Exp( x ); * double theta = 3*( x + y/1.75 ); */ //Vertex( new Complex( r * System.Math.Sin( theta ), r * System.Math.Cos( theta ) ) ); // Spiral } } GL.End(); }
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); }
private static void AddSymmetryTriangles(Mesh mesh, Tiling tiling, Polygon boundary) { // Assume template centered at the origin. Polygon template = tiling.Tiles.First().Boundary; List <Triangle> templateTris = new List <Triangle>(); foreach (Segment seg in template.Segments) { int num = 1 + (int)(seg.Length * m_divisions); Vector3D a = new Vector3D(); Vector3D b = seg.P1; Vector3D c = seg.Midpoint; Vector3D centroid = (a + b + c) / 3; Polygon poly = new Polygon(); Segment segA = Segment.Line(new Vector3D(), seg.P1); Segment segB = seg.Clone(); segB.P2 = seg.Midpoint; Segment segC = Segment.Line(seg.Midpoint, new Vector3D()); poly.Segments.Add(segA); poly.Segments.Add(segB); poly.Segments.Add(segC); Vector3D[] coords = TextureHelper.TextureCoords(poly, Geometry.Hyperbolic); int[] elements = TextureHelper.TextureElements(3, LOD: 3); for (int i = 0; i < elements.Length / 3; i++) { int idx1 = i * 3; int idx2 = i * 3 + 1; int idx3 = i * 3 + 2; Vector3D v1 = coords[elements[idx1]]; Vector3D v2 = coords[elements[idx2]]; Vector3D v3 = coords[elements[idx3]]; templateTris.Add(new Triangle(v1, v2, v3)); } /* * * // Need to shrink a little, so we won't * // get intersections among neighboring faces. * a = Shrink( a, centroid ); * b = Shrink( b, centroid ); * c = Shrink( c, centroid ); * * Vector3D[] list = seg.Subdivide( num * 2 ); * list[0] = b; * list[list.Length / 2] = c; * for( int i = 0; i < list.Length / 2; i++ ) * templateTris.Add( new Triangle( centroid, list[i], list[i + 1] ) ); * * for( int i = num - 1; i >= 0; i-- ) * templateTris.Add( new Triangle( centroid, a + (c - a) * (i + 1) / num, a + (c - a) * i / num ) ); * * for( int i = 0; i < num; i++ ) * templateTris.Add( new Triangle( centroid, a + (b - a) * i / num, a + (b - a) * (i + 1) / num ) ); */ } foreach (Tile tile in tiling.Tiles) { Vector3D a = tile.Boundary.Segments[0].P1; Vector3D b = tile.Boundary.Segments[1].P1; Vector3D c = tile.Boundary.Segments[2].P1; Mobius m = new Mobius(); if (tile.Isometry.Reflected) { m.MapPoints(template.Segments[0].P1, template.Segments[1].P1, template.Segments[2].P1, c, b, a); } else { m.MapPoints(template.Segments[0].P1, template.Segments[1].P1, template.Segments[2].P1, a, b, c); } foreach (Triangle tri in templateTris) { Triangle transformed = new Triangle( m.Apply(tri.a), m.Apply(tri.b), m.Apply(tri.c)); CheckAndAdd(mesh, transformed, boundary); } } }
public void SetupHexagonForKQ() { Polygon centralTile = new Polygon(); centralTile.CreateRegular(7, 3); Vector3D vertex0 = centralTile.Segments[0].P1; CircleNE[] otherThreeSides = OtherThreeSides(); CircleNE[] systoles = SystolesForKQ(); // Calc verts. List <Vector3D> verts = new List <Vector3D>(); Vector3D t1, t2; Euclidean2D.IntersectionCircleCircle(otherThreeSides[0], systoles[0], out t1, out t2); Vector3D intersection = t1.Abs() < 1 ? t1 : t2; verts.Add(intersection); intersection.Y *= -1; verts.Add(intersection); Mobius m = RotMobius(vertex0); verts.Add(m.Apply(verts[0])); verts.Add(m.Apply(verts[1])); verts.Add(m.Apply(verts[2])); verts.Add(m.Apply(verts[3])); // Setup all the segments. bool clockwise = true; Hexagon.Segments.AddRange(new Segment[] { Segment.Arc(verts[0], verts[1], otherThreeSides[0].Center, clockwise), Segment.Arc(verts[1], verts[2], systoles[1].Center, clockwise), Segment.Arc(verts[2], verts[3], otherThreeSides[1].Center, clockwise), Segment.Arc(verts[3], verts[4], systoles[2].Center, clockwise), Segment.Arc(verts[4], verts[5], otherThreeSides[2].Center, clockwise), Segment.Arc(verts[5], verts[0], systoles[0].Center, clockwise), }); Hexagon.Center = vertex0; // Setup the test circle. m.Isometry(Geometry.Hyperbolic, 0, -vertex0); Polygon clone = Hexagon.Clone(); clone.Transform(m); Circle temp = new Circle( clone.Segments[0].Midpoint, clone.Segments[2].Midpoint, clone.Segments[4].Midpoint); CircleNE tempNE = new CircleNE(temp, new Vector3D()); tempNE.Transform(m.Inverse()); TestCircle = tempNE; temp = new Circle( clone.Segments[0].P1, clone.Segments[1].P1, clone.Segments[2].P1); tempNE = new CircleNE(temp, new Vector3D()); tempNE.Transform(m.Inverse()); CircumCircle = tempNE; }
/// <summary> /// Using this to move the view around in interesting ways. /// </summary> private Vector3D ApplyTransformation(Vector3D v, double t = 0.0) { //v.RotateXY( Math.PI / 4 + 0.01 ); bool applyNone = true; if (applyNone) { return(v); } Mobius m0 = new Mobius(), m1 = new Mobius(), m2 = new Mobius(), m3 = new Mobius(); Sphere unitSphere = new Sphere(); v.Y -= .8; v *= 7; m0.UpperHalfPlane(); v = m0.Apply(v); return(v); // self-similar scale for 437 //v*= 4.259171776329806; double s = 6.5; v *= s; v += new Vector3D(s / 3, -s / 3); v = unitSphere.ReflectPoint(v); v.RotateXY(Math.PI / 6); //v /= 3; //v.RotateXY( Math.PI ); //v.RotateXY( Math.PI/2 ); return(v); //v.Y = v.Y / Math.Cos( Math.PI / 6 ); // 637 repeatable //return v; // 12,12,12 m0.Isometry(Geometry.Hyperbolic, 0, new Complex(.0, .0)); m1 = Mobius.Identity(); m2 = Mobius.Identity(); m3 = Mobius.Identity(); v = (m0 * m1 * m2 * m3).Apply(v); return(v); // i64 m0.Isometry(Geometry.Hyperbolic, 0, new Complex(.5, .5)); m1.UpperHalfPlane(); m2 = Mobius.Scale(1.333333); m3.Isometry(Geometry.Euclidean, 0, new Vector3D(0, -1.1)); v = (m1 * m2 * m3).Apply(v); return(v); // 464 // NOTE: Also, don't apply rotations during simplex generation. m1.UpperHalfPlane(); m2 = Mobius.Scale(1.3); m3.Isometry(Geometry.Euclidean, 0, new Vector3D(1.55, -1.1)); v = (m1 * m2 * m3).Apply(v); return(v); // iii m1.Isometry(Geometry.Hyperbolic, 0, new Complex(0, Math.Sqrt(2) - 1)); m2.Isometry(Geometry.Euclidean, -Math.PI / 4, 0); m3 = Mobius.Scale(5); //v = ( m1 * m2 * m3 ).Apply( v ); // Vertical Line /*v = unitSphere.ReflectPoint( v ); * m1.MapPoints( new Vector3D(-1,0), new Vector3D(), new Vector3D( 1, 0 ) ); * m2 = Mobius.Scale( .5 ); * v = (m1*m2).Apply( v ); */ /* * m1 = Mobius.Scale( 0.175 ); * v = unitSphere.ReflectPoint( v ); * v = m1.Apply( v ); * */ // Inversion //v = unitSphere.ReflectPoint( v ); //return v; /*Mobius m1 = new Mobius(), m2 = new Mobius(), m3 = new Mobius(); * m1.Isometry( Geometry.Spherical, 0, new Complex( 0, 1 ) ); * m2.Isometry( Geometry.Euclidean, 0, new Complex( 0, -1 ) ); * m3 = Mobius.Scale( 0.5 ); * v = (m1 * m3 * m2).Apply( v );*/ //Mobius m = new Mobius(); //m.Isometry( Geometry.Hyperbolic, 0, new Complex( -0.88, 0 ) ); //m.Isometry( Geometry.Hyperbolic, 0, new Complex( 0, Math.Sqrt(2) - 1 ) ); //m = Mobius.Scale( 0.17 ); //m.Isometry( Geometry.Spherical, 0, new Complex( 0, 3.0 ) ); //v = m.Apply( v ); // 63i, 73i m1 = Mobius.Scale(6.0); // Scale {3,i} to unit disk. m1 = Mobius.Scale(1.0 / 0.14062592996431983); // 73i (constant is abs of midpoint of {3,7} tiling, if we want to calc later for other tilings). m2.MapPoints(Infinity.InfinityVector, new Vector3D(1, 0), new Vector3D()); // swap interior/exterior m3.UpperHalfPlane(); v *= 2.9; // iii /*m1.MapPoints( new Vector3D(), new Vector3D(1,0), new Vector3D( Math.Sqrt( 2 ) - 1, 0 ) ); * m2.Isometry( Geometry.Euclidean, -Math.PI / 4, 0 ); * m3 = Mobius.Scale( 0.75 );*/ Mobius m = m3 * m2 * m1; v = m.Inverse().Apply(v); // Strange that we have to do inverse here. return(v); }
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"); }
public override void Transform(Mobius m) { base.Transform(m); CenterNE = m.Apply(CenterNE); }