/// <summary> /// Move from a point p1 -> p2 along a geodesic. /// Also somewhat from Don. /// factor can be used to only go some fraction of the distance from p1 to p2. /// </summary> public void Geodesic(Geometry g, Complex p1, Complex p2, double factor = 1.0) { Mobius t = new Mobius(); t.Isometry(g, 0, p1 * -1); Complex p2t = t.Apply(p2); // Only implemented for hyperbolic so far. if (factor != 1.0 && g == Geometry.Hyperbolic) { double newMag = DonHatch.h2eNorm(DonHatch.e2hNorm(p2t.Magnitude) * factor); Vector3D temp = Vector3D.FromComplex(p2t); temp.Normalize(); temp *= newMag; p2t = temp.ToComplex(); } Mobius m1 = new Mobius(), m2 = new Mobius(); m1.Isometry(g, 0, p1 * -1); m2.Isometry(g, 0, p2t); Mobius m3 = m1.Inverse(); this = m3 * m2 * m1; }
/// <summary> /// This transform will map the z points to the respective w points. /// </summary> public void MapPoints(Complex z1, Complex z2, Complex z3, Complex w1, Complex w2, Complex w3) { Mobius m1 = new Mobius(), m2 = new Mobius(); m1.MapPoints(z1, z2, z3); m2.MapPoints(w1, w2, w3); this = m2.Inverse() * m1; }
/// <summary> /// Does a circle inversion in an arbitrary, generalized circle. /// IOW, the three points may be collinear, in which case we are talking about a reflection. /// </summary> private void CacheCircleInversion(Complex c1, Complex c2, Complex c3) { Mobius toUnitCircle = new Mobius(); toUnitCircle.MapPoints( c1, c2, c3, new Complex(1, 0), new Complex(-1, 0), new Complex(0, 1)); m_cache1 = toUnitCircle; m_cache2 = m_cache1.Inverse(); }
public void Elliptic(Geometry g, Complex fixedPlus, double angle) { // To the origin. Mobius origin = new Mobius(); origin.Isometry(g, 0, fixedPlus * -1); // Rotate. Mobius rotate = new Mobius(); rotate.Isometry(g, angle, new Complex()); // Conjugate. this = origin.Inverse() * rotate * origin; }
/// <summary> /// Move from a point p1 -> p2 along a geodesic. /// Also somewhat from Don. /// </summary> public void Geodesic(Geometry g, Complex p1, Complex p2) { Mobius t = new Mobius(); t.Isometry(g, 0, p1 * -1); Complex p2t = t.Apply(p2); Mobius m1 = new Mobius(), m2 = new Mobius(); m1.Isometry(g, 0, p1 * -1); m2.Isometry(g, 0, p2t); Mobius m3 = m1.Inverse(); this = m3 * m2 * m1; }
/// <summary> /// This transform will map the z points to the respective w points. /// </summary> public void MapPoints( Complex z1, Complex z2, Complex z3, Complex w1, Complex w2, Complex w3 ) { Mobius m1 = new Mobius(), m2 = new Mobius(); m1.MapPoints( z1, z2, z3 ); m2.MapPoints( w1, w2, w3 ); this = m2.Inverse() * m1; }
/// <summary> /// Move from a point p1 -> p2 along a geodesic. /// Also somewhat from Don. /// </summary> public void Geodesic( Geometry g, Complex p1, Complex p2 ) { Mobius t = new Mobius(); t.Isometry( g, 0, p1 * -1 ); Complex p2t = t.Apply( p2 ); Mobius m1 = new Mobius(), m2 = new Mobius(); m1.Isometry( g, 0, p1 * -1 ); m2.Isometry( g, 0, p2t ); Mobius m3 = m1.Inverse(); this = m3 * m2 * m1; }
public void Elliptic( Geometry g, Complex fixedPlus, double angle ) { // To the origin. Mobius origin = new Mobius(); origin.Isometry( g, 0, fixedPlus * -1 ); // Rotate. Mobius rotate = new Mobius(); rotate.Isometry( g, angle, new Complex() ); // Conjugate. this = origin.Inverse() * rotate * origin; }
/// <summary> /// Subdivides a segment from p1->p2 with the two endpoints not on the origin, in the respective geometry. /// </summary> private 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(); }
/// <summary> /// Does a circle inversion in an arbitrary, generalized circle. /// IOW, the three points may be collinear, in which case we are talking about a reflection. /// </summary> private void CacheCircleInversion( Complex c1, Complex c2, Complex c3 ) { Mobius toUnitCircle = new Mobius(); toUnitCircle.MapPoints( c1, c2, c3, new Complex( 1, 0 ), new Complex( -1, 0 ), new Complex( 0, 1 ) ); m_cache1 = toUnitCircle; m_cache2 = m_cache1.Inverse(); }
/// <summary> /// Add an ideal banana to our mesh. Passed in edge should be in Ball model. /// </summary> public static void AddIdealBanana( Shapeways mesh, Vector3D e1, Vector3D e2, H3.Settings settings ) { Vector3D z1 = H3Models.BallToUHS( e1 ); Vector3D z2 = H3Models.BallToUHS( e2 ); // Mobius taking z1,z2 to origin,inf Complex dummy = new Complex( Math.E, Math.PI ); Mobius m = new Mobius( z1, dummy, z2 ); // Make our truncated cone. We need to deal with the two ideal endpoints specially. List<Vector3D> points = new List<Vector3D>(); double logHeight = 2; // XXX - magic number, and going to cause problems for infinity checks if too big. int div1, div2; H3Models.Ball.LOD_Ideal( e1, e2, out div1, out div2, settings ); double increment = logHeight / div1; for( int i=-div1; i<=div1; i+=2 ) points.Add( new Vector3D( 0, 0, Math.Exp( increment * i ) ) ); Shapeways tempMesh = new Shapeways(); tempMesh.Div = div2; System.Func<Vector3D, double> sizeFunc = v => H3Models.UHS.SizeFunc( v, settings.AngularThickness ); //Mesh.OpenCylinder... pass in two ideal endpoints? tempMesh.AddCurve( points.ToArray(), sizeFunc, new Vector3D(), Infinity.InfinityVector ); // Unwind the transforms. TakePointsBack( tempMesh.Mesh, m.Inverse(), settings ); mesh.Mesh.Triangles.AddRange( tempMesh.Mesh.Triangles ); }
/// <summary> /// Calculate the hyperbolic midpoint of an edge. /// Only works for non-ideal edges at the moment. /// </summary> public static Vector3D Midpoint( H3.Cell.Edge edge ) { // Special case if edge has endpoint on origin. // XXX - Really this should be special case anytime edge goes through origin. Vector3D e1 = edge.Start; Vector3D e2 = edge.End; if( e1.IsOrigin || e2.IsOrigin ) { if( e2.IsOrigin ) Utils.Swap<Vector3D>( ref e1, ref e2 ); return HalfTo( e2 ); } // No doubt there is a much better way, but // work in H2 slice transformed to xy plane, with e1 on x-axis. double angle = e1.AngleTo( e2 ); // always <= 180 e1 = new Vector3D( e1.Abs(), 0 ); e2 = new Vector3D( e2.Abs(), 0 ); e2.RotateXY( angle ); // Mobius that will move e1 to origin. Mobius m = new Mobius(); m.Isometry( Geometry.Hyperbolic, 0, -e1 ); e2 = m.Apply( e2 ); Vector3D midOnPlane = HalfTo( e2 ); midOnPlane= m.Inverse().Apply( midOnPlane ); double midAngle = e1.AngleTo( midOnPlane ); Vector3D mid = edge.Start; mid.RotateAboutAxis( edge.Start.Cross( edge.End ), midAngle ); mid.Normalize( midOnPlane.Abs() ); return mid; }
private Vector3D ApplyTransformationToSphere( Vector3D v, double t ) { v = H3Models.BallToUHS( v ); // 437 (hyperbolic) //v *= Math.Pow( 4.259171776329806, t*10-5 ); // 36i (parabolic) //v += new Vector3D( Math.Cos( Math.PI / 6 ), Math.Sin( Math.PI / 6 ) ) * t; // iii (loxodromic) //Complex c = v.ToComplex(); //double x = Math.Sqrt( 2 ) - 1; //Mobius m = new Mobius( new Complex( x, 0 ), Complex.One, new Complex( -x, 0 ) ); // 12,12,12 loxodromic //m = new Mobius( new Complex( 0, 1 ), Complex.One, new Complex( 0, -1 ) ); /* c = m.Apply( c ); c *= Complex.Exp( new Complex( 2.5, 4 * Math.PI ) * t ); c = m.Inverse().Apply( c ); v = Vector3D.FromComplex( c ); */ // Center cell head in KolorEyes. Mobius m = new Mobius(); m.UpperHalfPlane(); v = m.Inverse().Apply( v ); return H3Models.UHSToBall( v ); }
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 ) return SimplexCalcs.MirrorsSpherical( p, q, r ); 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. Sphere[] result = surfaces.Select( s => moveToBall ? H3Models.UHSToBall( s ) : s ).ToArray(); cellCenter = moveToBall ? H3Models.UHSToBall( cellCenter ) : cellCenter; return result; }