Exemplo n.º 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;
        }
Exemplo n.º 2
0
        public static Mobius CreateFromIsometry(Geometry g, double angle, Complex P)
        {
            Mobius m = new Mobius();

            m.Isometry(g, angle, P);
            return(m);
        }
Exemplo n.º 3
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);
        }
Exemplo n.º 4
0
        public static Mobius Identity()
        {
            Mobius m = new Mobius();

            m.Unity();
            return(m);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Slices a polygon by a circle with some thickness.
        /// Input circle may be a line.
        /// </summary>
        /// <remarks>The input polygon might get reversed</remarks>
        public static void SlicePolygon( Polygon p, CircleNE c, Geometry g, double thickness, out List<Polygon> output )
        {
            output = new List<Polygon>();

            // Setup the two slicing circles.
            CircleNE c1 = c.Clone(), c2 = c.Clone();
            Mobius m = new Mobius();
            Vector3D pointOnCircle = c.IsLine ? c.P1 : c.Center + new Vector3D( c.Radius, 0 );
            m.Hyperbolic2( g, c1.CenterNE, pointOnCircle, thickness / 2 );
            c1.Transform( m );
            m.Hyperbolic2( g, c2.CenterNE, pointOnCircle, -thickness / 2 );
            c2.Transform( m );

            // ZZZ - alter Clip method to work on Polygons and use that.

            // Slice it up.
            List<Polygon> sliced1, sliced2;
            Slicer.SlicePolygon( p, c1, out sliced1 );
            Slicer.SlicePolygon( p, c2, out sliced2 );

            // Keep the ones we want.
            foreach( Polygon newPoly in sliced1 )
            {
                bool outside = !c1.IsPointInsideNE( newPoly.CentroidApprox );
                if( outside )
                    output.Add( newPoly );
            }

            foreach( Polygon newPoly in sliced2 )
            {
                bool inside = c2.IsPointInsideNE( newPoly.CentroidApprox );
                if( inside )
                    output.Add( newPoly );
            }
        }
Exemplo n.º 6
0
        /// <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;
        }
Exemplo n.º 7
0
        /// <summary>
        /// Returns a new Mobius transformation that is the inverse of us.
        /// </summary>
        public Mobius Inverse()
        {
            // See http://en.wikipedia.org/wiki/Möbius_transformation
            Mobius result = new Mobius(D, -B, -C, A);

            result.Normalize();
            return(result);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Add a finite (truncated) banana to our mesh.  Passed in edge should be in Ball model.
        /// </summary>
        public static void AddBanana( Shapeways mesh, Vector3D e1, Vector3D e2, H3.Settings settings )
        {
            Vector3D e1UHS = H3Models.BallToUHS( e1 );
            Vector3D e2UHS = H3Models.BallToUHS( e2 );

            // Endpoints of the goedesic on the z=0 plane.
            Vector3D z1, z2;
            H3Models.UHS.GeodesicIdealEndpoints( e1UHS, e2UHS, out z1, out z2 );

            // XXX - Do we want to do a better job worrying about rotation here?
            // (multiply by complex number with certain imaginary part as well)
            //Vector3D z3 = ( z1 + z2 ) / 2;
            //if( Infinity.IsInfinite( z3 ) )
            //	z3 = new Vector3D( 1, 0 );
            Vector3D z3 = new Vector3D( Math.E, Math.PI );	// This should vary the rotations a bunch.

            // Find the Mobius we need.
            // We'll do this in two steps.
            // (1) Find a mobius taking z1,z2 to origin,inf
            // (2) Deal with scaling e1 to a height of 1.
            Mobius m1 = new Mobius( z1, z3, z2 );
            Vector3D e1UHS_transformed = m1.ApplyToQuaternion( e1UHS );
            double scale = 1.0 / e1UHS_transformed.Z;
            Mobius m2 = Mobius.Scale( scale );
            Mobius m = m2 * m1;	// Compose them (multiply in reverse order).
            Vector3D e2UHS_transformed = m.ApplyToQuaternion( e2UHS );

            // Make our truncated cone.
            // For regular tilings, we really would only need to do this once for a given LOD.
            List<Vector3D> points = new List<Vector3D>();
            double logHeight = Math.Log( e2UHS_transformed.Z );
            if( logHeight < 0 )
                throw new System.Exception( "impl issue" );
            int div1, div2;
            H3Models.Ball.LOD_Finite( e1, e2, out div1, out div2, settings );
            double increment = logHeight / div1;
            for( int i=0; i<=div1; i++ )
            {
                double h = increment * i;

                // This is to keep different bananas from sharing exactly coincident vertices.
                double tinyOffset = 0.001;
                if( i == 0 )
                    h -= tinyOffset;
                if( i == div1 )
                    h += tinyOffset;

                Vector3D point = new Vector3D( 0, 0, Math.Exp( h ) );
                points.Add( point );
            }
            Shapeways tempMesh = new Shapeways();
            tempMesh.Div = div2;
            tempMesh.AddCurve( points.ToArray(), v => H3Models.UHS.SizeFunc( v, settings.AngularThickness ) );

            // Unwind the transforms.
            TakePointsBack( tempMesh.Mesh, m.Inverse(), settings );
            mesh.Mesh.Triangles.AddRange( tempMesh.Mesh.Triangles );
        }
Exemplo n.º 9
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);
 }
Exemplo n.º 10
0
        private void CalculateFromTwoPolygonsInternal(Polygon home, Polygon boundary, CircleNE homeVertexCircle, Geometry g)
        {
            // ZZZ - We have to use the boundary, but that can be projected to infinity for some of the spherical tilings.
            //		 Trying to use the Drawn tile produced weird (yet interesting) results.
            Polygon poly1 = boundary;
            Polygon poly2 = home;

            if (poly1.Segments.Count < 3 ||
                poly2.Segments.Count < 3)                       // Poor poor digons.
            {
                Debug.Assert(false);
                return;
            }

            // Same?
            Vector3D p1 = poly1.Segments[0].P1, p2 = poly1.Segments[1].P1, p3 = poly1.Segments[2].P1;
            Vector3D w1 = poly2.Segments[0].P1, w2 = poly2.Segments[1].P1, w3 = poly2.Segments[2].P1;

            if (p1 == w1 && p2 == w2 && p3 == w3)
            {
                this.Mobius = Mobius.Identity();
                return;
            }

            Mobius m = new Mobius();

            m.MapPoints(p1, p2, p3, w1, w2, w3);
            this.Mobius = m;

            // Worry about reflections as well.
            if (g == Geometry.Spherical)
            {
                // If inverted matches the orientation, we need a reflection.
                bool inverted = poly1.IsInverted;
                if (!(inverted ^ poly1.Orientation))
                {
                    this.Reflection = homeVertexCircle;
                }
            }
            else
            {
                if (!poly1.Orientation)
                {
                    this.Reflection = homeVertexCircle;
                }
            }

            // Some testing.
            Vector3D test = this.Apply(boundary.Center);

            if (test != home.Center)
            {
                // ZZZ: What is happening here is that the mobius can project a point to infinity before the reflection brings it back to the origin.
                //		It hasn't been much of a problem in practice yet, but will probably need fixing at some point.
                //Trace.WriteLine( "oh no!" );
            }
        }
Exemplo n.º 11
0
        public static Mobius operator *(Mobius m1, Mobius m2)
        {
            Mobius result = new Mobius(
                m1.A * m2.A + m1.B * m2.C,
                m1.A * m2.B + m1.B * m2.D,
                m1.C * m2.A + m1.D * m2.C,
                m1.C * m2.B + m1.D * m2.D);

            result.Normalize();
            return(result);
        }
Exemplo n.º 12
0
        /// <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();
        }
Exemplo n.º 13
0
        /// <summary>
        /// Does a Euclidean reflection across a line.
        /// </summary>

        /*public void ReflectAcrossLine( Vector3D p1, Vector3D p2 )
         * {
         *      // Do a circle inversion using a generalized circle (third point is at infinity).
         *      //Complex p3 = Complex.ImaginaryOne * Math.Pow( 10, 10 );
         *      //Vector3D p3 = p1 + (p2 - p1) * Math.Pow( 10, 10 );
         *      Vector3D p3 = (p1 + p2) / 2;
         *      p3 *= 1000;
         *      //CircleInversion( p1, p2, p3 );
         *      //CircleInversion( new Complex( 1, 0 ), new Complex( -1, 0 ), new Complex( 0, 1 ) );
         *      Mobius m1 = new Mobius();
         *      m1.MapPoints( p1, p2, p3,
         *              new Complex( 1, 0 ), new Complex( -1, 0 ), new Complex( 0, 1 ) );
         *
         *      this = m1;
         * }*/

        /// <summary>
        /// Returns a new Isometry that is the inverse of us.
        /// </summary>
        public Isometry Inverse()
        {
            Mobius inverse = this.Mobius.Inverse();

            if (Reflection == null)
            {
                return(new Isometry(inverse, null));
            }
            else
            {
                Circle reflection = Reflection.Clone();
                reflection.Transform(inverse);
                return(new Isometry(inverse, reflection));
            }
        }
Exemplo n.º 14
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;
        }
Exemplo n.º 15
0
        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;
        }
Exemplo n.º 16
0
        /// <summary>
        /// Composition operator.
        /// </summary>>
        public static Isometry operator *(Isometry i1, Isometry i2)
        {
            // ZZZ - Probably a better way.
            // We'll just apply both isometries to a canonical set of points,
            // Then calc which isometry makes that.

            Complex p1 = new Complex(1, 0);
            Complex p2 = new Complex(-1, 0);
            Complex p3 = new Complex(0, 1);
            Complex w1 = p1, w2 = p2, w3 = p3;

            // Compose (apply in reverse order).
            w1 = i2.Apply(w1);
            w2 = i2.Apply(w2);
            w3 = i2.Apply(w3);
            w1 = i1.Apply(w1);
            w2 = i1.Apply(w2);
            w3 = i1.Apply(w3);

            Mobius m = new Mobius();

            m.MapPoints(p1, p2, p3, w1, w2, w3);

            Isometry result = new Isometry();

            result.Mobius = m;

            // Need to reflect at end?
            bool r1 = i1.Reflection != null;
            bool r2 = i2.Reflection != null;

            if (r1 ^ r2)                // One and only one reflection.
            {
                result.Reflection = new Circle(
                    Vector3D.FromComplex(w1),
                    Vector3D.FromComplex(w2),
                    Vector3D.FromComplex(w3));
            }

            return(result);
        }
Exemplo n.º 17
0
        public void Hyperbolic(Geometry g, Complex fixedPlus, double scale)
        {
            // To the origin.
            Mobius m1 = new Mobius();

            m1.Isometry(g, 0, fixedPlus * -1);

            // Scale.
            Mobius m2 = new Mobius();

            m2.A = scale;
            m2.B = m2.C = 0;
            m2.D = 1;

            // Back.
            //Mobius m3 = m1.Inverse();	// Doesn't work well if fixedPlus is on disk boundary.
            Mobius m3 = new Mobius();

            m3.Isometry(g, 0, fixedPlus);

            // Compose them (multiply in reverse order).
            this = m3 * m2 * m1;
        }
Exemplo n.º 18
0
 /// <summary>
 /// Helper to apply a Mobius to the ball model.
 /// Vector is taken to UHS, mobius applied, then taken back.
 /// </summary>
 public static Vector3D ApplyMobius( Mobius m, Vector3D v )
 {
     v = BallToUHS( v );
     v = m.ApplyToQuaternion( v );
     return UHSToBall( v );
 }
Exemplo n.º 19
0
        private void CalculateFromTwoPolygonsInternal( Polygon home, Polygon boundary, CircleNE homeVertexCircle, Geometry g )
        {
            // ZZZ - We have to use the boundary, but that can be projected to infinity for some of the spherical tilings.
            //		 Trying to use the Drawn tile produced weird (yet interesting) results.
            Polygon poly1 = boundary;
            Polygon poly2 = home;

            if( poly1.Segments.Count < 3 ||
                poly2.Segments.Count < 3 )	// Poor poor digons.
            {
                Debug.Assert( false );
                return;
            }

            // Same?
            Vector3D p1 = poly1.Segments[0].P1, p2 = poly1.Segments[1].P1, p3 = poly1.Segments[2].P1;
            Vector3D w1 = poly2.Segments[0].P1, w2 = poly2.Segments[1].P1, w3 = poly2.Segments[2].P1;
            if( p1 == w1 && p2 == w2 && p3 == w3 )
            {
                this.Mobius = Mobius.Identity();
                return;
            }

            Mobius m = new Mobius();
            m.MapPoints( p1, p2, p3, w1, w2, w3 );
            this.Mobius = m;

            // Worry about reflections as well.
            if( g == Geometry.Spherical )
            {
                // If inverted matches the orientation, we need a reflection.
                bool inverted = poly1.IsInverted;
                if( !(inverted ^ poly1.Orientation) )
                    this.Reflection = homeVertexCircle;
            }
            else
            {
                if( !poly1.Orientation )
                    this.Reflection = homeVertexCircle;
            }

            // Some testing.
            Vector3D test = this.Apply( boundary.Center );
            if( test != home.Center )
            {
                // ZZZ: What is happening here is that the mobius can project a point to infinity before the reflection brings it back to the origin.
                //		It hasn't been much of a problem in practice yet, but will probably need fixing at some point.
                //Trace.WriteLine( "oh no!" );
            }
        }
Exemplo n.º 20
0
        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;
        }
Exemplo n.º 21
0
 private static Vector3D DiskToUpper( Vector3D input )
 {
     Mobius m = new Mobius();
     m.UpperHalfPlane();
     return m.Apply( input );
 }
Exemplo n.º 22
0
 public static void TransformInUHS2( Sphere s, Mobius m )
 {
     Sphere newSphere = TransformInUHS( s, m );
     s.Center = newSphere.Center;
     s.Radius = newSphere.Radius;
     s.Offset = newSphere.Offset;
 }
Exemplo n.º 23
0
        public static Mobius operator *( Mobius m1, Mobius m2 )
        {
            Mobius result = new Mobius(
                m1.A * m2.A + m1.B * m2.C,
                m1.A * m2.B + m1.B * m2.D,
                m1.C * m2.A + m1.D * m2.C,
                m1.C * m2.B + m1.D * m2.D );

            result.Normalize();
            return result;
        }
Exemplo n.º 24
0
 /// <summary>
 /// Returns a new Mobius transformation that is the inverse of us.
 /// </summary>
 public Mobius Inverse()
 {
     // See http://en.wikipedia.org/wiki/Möbius_transformation
     Mobius result = new Mobius( D, -B, -C, A );
     result.Normalize();
     return result;
 }
Exemplo n.º 25
0
        public void Hyperbolic( Geometry g, Complex fixedPlus, double scale )
        {
            // To the origin.
            Mobius m1 = new Mobius();
            m1.Isometry( g, 0, fixedPlus * -1 );

            // Scale.
            Mobius m2 = new Mobius();
            m2.A = scale;
            m2.B = m2.C = 0;
            m2.D = 1;

            // Back.
            //Mobius m3 = m1.Inverse();	// Doesn't work well if fixedPlus is on disk boundary.
            Mobius m3 = new Mobius();
            m3.Isometry( g, 0, fixedPlus );

            // Compose them (multiply in reverse order).
            this = m3 * m2 * m1;
        }
Exemplo n.º 26
0
        /// <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 );
        }
Exemplo n.º 27
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 );
                }
            }
        }
Exemplo n.º 28
0
        /// <summary>
        /// Composition operator.
        /// </summary>>
        public static Isometry operator *( Isometry i1, Isometry i2 )
        {
            // ZZZ - Probably a better way.
            // We'll just apply both isometries to a canonical set of points,
            // Then calc which isometry makes that.

            Complex p1 = new Complex( 1, 0 );
            Complex p2 = new Complex( -1, 0 );
            Complex p3 = new Complex( 0, 1 );
            Complex w1 = p1, w2 = p2, w3 = p3;

            // Compose (apply in reverse order).
            w1 = i2.Apply( w1 );
            w2 = i2.Apply( w2 );
            w3 = i2.Apply( w3 );
            w1 = i1.Apply( w1 );
            w2 = i1.Apply( w2 );
            w3 = i1.Apply( w3 );

            Mobius m = new Mobius();
            m.MapPoints( p1, p2, p3, w1, w2, w3 );

            Isometry result = new Isometry();
            result.Mobius = m;

            // Need to reflect at end?
            bool r1 = i1.Reflection != null;
            bool r2 = i2.Reflection != null;
            if( r1 ^ r2 )	// One and only one reflection.
            {
                result.Reflection = new Circle(
                    Vector3D.FromComplex( w1 ),
                    Vector3D.FromComplex( w2 ),
                    Vector3D.FromComplex( w3 ) );
            }

            return result;
        }
Exemplo n.º 29
0
 public Isometry( Mobius m, Circle r )
 {
     Mobius = m;
     Reflection = r;
 }
Exemplo n.º 30
0
 private static Mobius ToUpperHalfPlaneMobius()
 {
     Mobius m = new Mobius();
     m.UpperHalfPlane();
     return m;
 }
Exemplo n.º 31
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;
            }
Exemplo n.º 32
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();
        }
Exemplo n.º 33
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
            };
        }
Exemplo n.º 34
0
        /// <summary>
        /// ZZZ - needs to be part of performance setting?
        /// Returns true if the tile should be included after a Mobius transformation will be applied.
        /// If the tile is not be included, this method avoids applying the mobious transform to the entire tile.
        /// </summary>
        public bool IncludeAfterMobius( Mobius m )
        {
            switch( this.Geometry )
            {
                // Spherical tilings are finite, so we can always include everything.
                case Geometry.Spherical:
                    return true;

                case Geometry.Euclidean:
                    return true;	// We'll let the number of tiles specified in the tiling control this..

                case Geometry.Hyperbolic:
                {
                    //Polygon poly = Boundary.Clone();
                    //poly.Transform( m );

                    //bool use = (poly.Length > 0.01);

                    // ZZZ - DANGER! Some transforms can cause this to lead to stackoverflow (the ones that scale the tiling up).
                    //bool use = ( poly.Length > 0.01 ) && ( poly.Center.Abs() < 10 );
                    //bool use = ( poly.Center.Abs() < 0.9 );	// Only disk

                    CircleNE c = VertexCircle;
                    bool use = c.CenterNE.Abs() < 0.9999;

                    /*List<Vector3D> points = poly.GetEdgePoints();
                    double maxdist = points.Max( point => point.Abs() );
                    bool use = maxdist < 0.97;*/

                    return use;
                }
            }

            Debug.Assert( false );
            return false;
        }
Exemplo n.º 35
0
 public Isometry(Mobius m, Circle r)
 {
     Mobius     = m;
     Reflection = r;
 }
Exemplo n.º 36
0
 /// <summary>
 /// Apply a Mobius transform to us.
 /// </summary>
 public void Transform( Mobius m )
 {
     Boundary.Transform( m );
     Drawn.Transform( m );
     VertexCircle.Transform( m );
 }
Exemplo n.º 37
0
 public static Mobius Identity()
 {
     Mobius m = new Mobius();
     m.Unity();
     return m;
 }
Exemplo n.º 38
0
        /// <summary>
        /// This will trim back the tile using an equidistant curve.
        /// It assumes the tile is at the origin.
        /// </summary>
        internal static void ShrinkTile( ref Tile tile, double shrinkFactor )
        {
            // This code is not correct in non-Euclidean cases!
            // But it works reasonable well for small shrink factors.
            // For example, you can easily use this function to grow a hyperbolic tile beyond the disk.
            Mobius m = new Mobius();
            m.Hyperbolic( tile.Geometry, new Vector3D(), shrinkFactor );
            tile.Drawn.Transform( m );
            return;

            /*
            // ZZZ
            // Wow, all the work I did below was subsumed by 4 code lines above!
            // I can't bring myself to delete it yet.

            switch( tile.Geometry )
            {
                case Geometry.Spherical:
                {
                    List<Tile> clipped = new List<Tile>();
                    clipped.Add( tile );

                    Polygon original = tile.Drawn.Clone();
                    foreach( Segment seg in original.Segments )
                    {
                        Debug.Assert( seg.Type == SegmentType.Arc );

                        if( true )
                        {
                            // Unproject to sphere.
                            Vector3D p1 = Spherical2D.PlaneToSphere( seg.P1 );
                            Vector3D p2 = Spherical2D.PlaneToSphere( seg.P2 );

                            // Get the poles of the GC, and project them to the plane.
                            Vector3D pole1, pole2;
                            Spherical2D.GreatCirclePole( p1, p2, out pole1, out pole2 );
                            pole1 = Spherical2D.SphereToPlane( pole1 );
                            pole2 = Spherical2D.SphereToPlane( pole2 );

                            // Go hyperbolic, dude.
                            double scale = 1.065;	// ZZZ - needs to be configurable.
                            Complex fixedPlus = pole1;
                            Mobius hyperbolic = new Mobius();
                            hyperbolic.Hyperbolic( tile.Geometry, fixedPlus, scale );
                            Vector3D newP1 = hyperbolic.Apply( seg.P1 );
                            Vector3D newMid = hyperbolic.Apply( seg.Midpoint );
                            Vector3D newP2 = hyperbolic.Apply( seg.P2 );

                            Circle trimmingCircle = new Circle();
                            trimmingCircle.From3Points( newP1, newMid, newP2 );

                            Slicer.Clip( ref clipped, trimmingCircle, true );
                        }
                        else
                        {
                            // I think this block has logic flaws, but strangely it seems to work,
                            // so I'm leaving it in commented out for posterity.

                            Vector3D p1 = seg.P1;
                            Vector3D mid = seg.Midpoint;
                            Vector3D p2 = seg.P2;

                            //double offset = .1;
                            double factor = .9;
                            double f1 = Spherical2D.s2eNorm( (Spherical2D.e2sNorm( p1.Abs() ) * factor) );
                            double f2 = Spherical2D.s2eNorm( (Spherical2D.e2sNorm( mid.Abs() ) * factor) );
                            double f3 = Spherical2D.s2eNorm( (Spherical2D.e2sNorm( p2.Abs() ) * factor) );
                            p1.Normalize();
                            mid.Normalize();
                            p2.Normalize();
                            p1 *= f1;
                            mid *= f2;
                            p2 *= f3;

                            Circle trimmingCircle = new Circle();
                            trimmingCircle.From3Points( p1, mid, p2 );

                            Slicer.Clip( ref clipped, trimmingCircle, true );
                        }
                    }

                    Debug.Assert( clipped.Count == 1 );
                    tile = clipped[0];
                    return;
                }
                case Geometry.Euclidean:
                {
                    double scale = .95;

                    Mobius hyperbolic = new Mobius();
                    hyperbolic.Hyperbolic( tile.Geometry, new Vector3D(), scale );

                    tile.Drawn.Transform( hyperbolic );

                    return;
                }
                case Geometry.Hyperbolic:
                {
                    List<Tile> clipped = new List<Tile>();
                    clipped.Add( tile );

                    Circle infinity = new Circle();
                    infinity.Radius = 1.0;

                    Polygon original = tile.Drawn.Clone();
                    foreach( Segment seg in original.Segments )
                    {
                        Debug.Assert( seg.Type == SegmentType.Arc );
                        Circle segCircle = seg.GetCircle();

                        // Get the intersection points with the disk at infinity.
                        Vector3D p1, p2;
                        int count = Euclidean2D.IntersectionCircleCircle( infinity, segCircle, out p1, out p2 );
                        Debug.Assert( count == 2 );

                        Vector3D mid = seg.Midpoint;
                        //mid *= 0.75;	// ZZZ - needs to be configurable.

                        double offset = .03;
                        double f1 = DonHatch.h2eNorm( DonHatch.e2hNorm( mid.Abs() ) - offset );
                        mid.Normalize();
                        mid *= f1;

                        Circle trimmingCircle = new Circle();
                        trimmingCircle.From3Points( p1, mid, p2 );

                        Slicer.Clip( ref clipped, trimmingCircle, false );
                    }

                    Debug.Assert( clipped.Count == 1 );
                    tile = clipped[0];
                    return;
                }
            }
            */
        }
Exemplo n.º 39
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;
        }
Exemplo n.º 40
0
        /// <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();
        }
Exemplo n.º 41
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 );
        }
Exemplo n.º 42
0
        public Vector3D Transform( Vector3D v )
        {
            v.RotateAboutAxis( new Vector3D( 1, 0 ), Math.PI / 2 );

            Mobius m = new Mobius();
            m.Isometry( Geometry.Hyperbolic, 0, new Complex( 0, 0.6 ) );
            v = H3Models.TransformHelper( v, m );

            v.RotateAboutAxis( new Vector3D( 1, 0 ), -Math.PI / 2 );
            return v;
        }
Exemplo n.º 43
0
 /// <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;
 }
Exemplo n.º 44
0
        public static void Test()
        {
            S3.HopfOrbit();

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

            Vector3D test = m.Apply( new Vector3D() );
            test *= 1;
        }
Exemplo n.º 45
0
 public static Mobius CreateFromIsometry( Geometry g, double angle, Complex P )
 {
     Mobius m = new Mobius();
     m.Isometry( g, angle, P );
     return m;
 }
Exemplo n.º 46
0
        // Rotates a dodec about a vertex or edge to get a dual dodec.
        private static Dodec GetDual( Dodec dodec, Vector3D rotationPoint )
        {
            //double rot = System.Math.PI / 2;	// Edge-centered
            double rot = 4 * ( - Math.Atan( ( 2 + Math.Sqrt( 5 ) - 2 * Math.Sqrt( 3 + Math.Sqrt( 5 ) ) ) / Math.Sqrt( 3 ) ) );	// Vertex-centered

            Mobius m = new Mobius();
            if( Infinity.IsInfinite( rotationPoint ) )
                m.Elliptic( Geometry.Spherical, new Vector3D(), -rot );
            else
                m.Elliptic( Geometry.Spherical, rotationPoint, rot );

            Dodec dual = new Dodec();
            foreach( Vector3D v in dodec.Verts )
            {
                Vector3D rotated = m.ApplyInfiniteSafe( v );
                dual.Verts.Add( rotated );
            }

            foreach( Vector3D v in dodec.Midpoints )
            {
                Vector3D rotated = m.ApplyInfiniteSafe( v );
                dual.Midpoints.Add( rotated );
            }

            return dual;
        }