Apply() public method

Applies a Mobius transformation to a complex number.
public Apply ( System.Numerics.Complex z ) : System.Numerics.Complex
z System.Numerics.Complex
return System.Numerics.Complex
Example #1
0
        /// <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;
        }
Example #2
0
        /// <summary>
        /// Allow a hyperbolic transformation using an absolute offset.
        /// offset is specified in the respective geometry.
        /// </summary>
        public void Hyperbolic2(Geometry g, Complex fixedPlus, Complex point, double offset)
        {
            // To the origin.
            Mobius m = new Mobius();

            m.Isometry(g, 0, fixedPlus * -1);
            double eRadius = m.Apply(point).Magnitude;

            double scale = 1;

            switch (g)
            {
            case Geometry.Spherical:
                double sRadius = Spherical2D.e2sNorm(eRadius);
                sRadius += offset;
                scale    = Spherical2D.s2eNorm(sRadius) / eRadius;
                break;

            case Geometry.Euclidean:
                scale = (eRadius + offset) / eRadius;
                break;

            case Geometry.Hyperbolic:
                double hRadius = DonHatch.e2hNorm(eRadius);
                hRadius += offset;
                scale    = DonHatch.h2eNorm(hRadius) / eRadius;
                break;
            }

            Hyperbolic(g, fixedPlus, scale);
        }
Example #3
0
        private Complex ApplyCachedCircleInversion(Complex input)
        {
            Complex result = m_cache1.Apply(input);

            result = CircleInversion(result);
            result = m_cache2.Apply(result);
            return(result);
        }
Example #4
0
 /// <summary>
 /// Applies an isometry to a complex number.
 /// </summary>
 public Complex Apply(Complex z)
 {
     z = Mobius.Apply(z);
     if (Reflection != null)
     {
         z = ApplyCachedCircleInversion(z);
     }
     return(z);
 }
Example #5
0
        /// <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;
        }
Example #6
0
        /// <summary>
        /// Allow a hyperbolic transformation using an absolute offset.
        /// offset is specified in the respective geometry.
        /// </summary>
        public void Hyperbolic2( Geometry g, Complex fixedPlus, Complex point, double offset )
        {
            // To the origin.
            Mobius m = new Mobius();
            m.Isometry( g, 0, fixedPlus * -1 );
            double eRadius = m.Apply( point ).Magnitude;

            double scale = 1;
            switch( g )
            {
                case Geometry.Spherical:
                    double sRadius = Spherical2D.e2sNorm( eRadius );
                    sRadius += offset;
                    scale = Spherical2D.s2eNorm( sRadius ) / eRadius;
                    break;
                case Geometry.Euclidean:
                    scale = (eRadius + offset) / eRadius;
                    break;
                case Geometry.Hyperbolic:
                    double hRadius = DonHatch.e2hNorm( eRadius );
                    hRadius += offset;
                    scale = DonHatch.h2eNorm( hRadius ) / eRadius;
                    break;
            }

            Hyperbolic( g, fixedPlus, scale );
        }
Example #7
0
        /// <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;
        }
Example #8
0
 private static Vector3D DiskToUpper( Vector3D input )
 {
     Mobius m = new Mobius();
     m.UpperHalfPlane();
     return m.Apply( input );
 }
Example #9
0
        /// <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();
        }
Example #10
0
        public static void Test()
        {
            S3.HopfOrbit();

            Mobius m = new Mobius();
            m.UpperHalfPlane();

            Vector3D test = m.Apply( new Vector3D() );
            test *= 1;
        }
Example #11
0
        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 );
                }
            }
        }
Example #12
0
            /// <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;
            }
Example #13
0
        /// <summary>
        /// This applies the the Mobius transform to the plane at infinity.
        /// Any Mobius is acceptable.
        /// NOTE: s must be geodesic! (orthogonal to boundary).
        /// </summary>
        public static Sphere TransformInUHS( Sphere s, Mobius m )
        {
            Vector3D s1, s2, s3;
            if( s.IsPlane )
            {
                // It must be vertical (because it is orthogonal).
                Vector3D direction = s.Normal;
                direction.RotateXY( Math.PI / 2 );
                s1 = s.Offset;
                s2 = s1 + direction;
                s3 = s1 - direction;
            }
            else
            {
                Vector3D offset = new Vector3D( s.Radius, 0, 0 );
                s1 = s.Center + offset;
                s2 = s.Center - offset;
                s3 = offset;
                s3.RotateXY( Math.PI / 2 );
                s3 += s.Center;
            }

            Vector3D b1 = m.Apply( s1 );
            Vector3D b2 = m.Apply( s2 );
            Vector3D b3 = m.Apply( s3 );
            Circle3D boundaryCircle = new Circle3D( b1, b2, b3 );

            Vector3D cen = boundaryCircle.Center;
            Vector3D off = new Vector3D();
            if( Infinity.IsInfinite( boundaryCircle.Radius ) )
            {
                boundaryCircle.Radius = double.PositiveInfinity;
                Vector3D normal = b2 - b1;
                normal.Normalize();
                normal.RotateXY( -Math.PI / 2 );	// XXX - The direction isn't always correct.
                cen = normal;
                off = Euclidean2D.ProjectOntoLine( new Vector3D(), b1, b2 );
            }

            return new Sphere
            {
                Center = cen,
                Radius = boundaryCircle.Radius,
                Offset = off
            };
        }
Example #14
0
        /// <summary>
        /// NOTE! This should only be used if m is a transform that preserves the imaginary axis!
        /// </summary>
        public static Vector3D TransformHelper( Vector3D v, Mobius m )
        {
            Vector3D spherical = SphericalCoords.CartesianToSpherical( v );
            Complex c1 = Complex.FromPolarCoordinates( spherical.X, Math.PI/2 - spherical.Y );
            Complex c2 = m.Apply( c1 );

            /*
            if( c2.Phase > Math.PI / 2 || c2.Phase < -Math.PI / 2 )
            {
                // Happens when v is origin, and causes runtime problems in release.
                System.Diagnostics.Debugger.Break();
            }
            */

            Vector3D s2 = new Vector3D( c2.Magnitude, Math.PI/2 - c2.Phase, spherical.Z );
            return SphericalCoords.SphericalToCartesian( s2 );
        }
Example #15
0
        /// <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 );
        }
Example #16
0
        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;
        }