Beispiel #1
0
        /// <summary>
        /// Finds the intersection (a circle) between us and another sphere.
        /// Returns null if sphere centers are coincident or no intersection exists.
        /// Does not currently work for planes.
        /// </summary>
        public Circle3D Intersection(Sphere s)
        {
            if (this.IsPlane || s.IsPlane)
            {
                throw new System.NotImplementedException();
            }

            double r = s.Radius;
            double R = this.Radius;

            Vector3D diff = this.Center - s.Center;
            double   d    = diff.Abs();

            if (Tolerance.Zero(d) || d > r + R)
            {
                return(null);
            }

            double x = (d * d + r * r - R * R) / (2 * d);
            double y = Math.Sqrt(r * r - x * x);

            Circle3D result = new Circle3D();

            diff.Normalize();
            result.Normal = diff;
            result.Center = s.Center + diff * x;
            result.Radius = y;
            return(result);
        }
Beispiel #2
0
        public H3.Cell.Edge[] Hyperboloid()
        {
            // Draw to circles of fibers, then twist them.
            List<H3.Cell.Edge> fiberList = new List<H3.Cell.Edge>();

            Vector3D cen = new Vector3D( 0, 0, 0.5 );
            double rad = .3;
            Circle3D c1 = new Circle3D { Center = cen, Radius = rad };
            Circle3D c2 = new Circle3D { Center = -cen, Radius = rad };

            int n = 50;
            Vector3D[] points1 = c1.Subdivide( n );
            Vector3D[] points2 = c2.Subdivide( n );

            double twist = 2 * Math.PI / 3;
            for( int i = 0; i < points2.Length; i++ )
            {
                points2[i].RotateXY( twist );

                Vector3D e1, e2;
                H3Models.Ball.GeodesicIdealEndpoints( points1[i], points2[i], out e1, out e2 );

                e1 = Transform( e1 );
                e2 = Transform( e2 );

                fiberList.Add( new H3.Cell.Edge( e1, e2 ) );
            }

            return fiberList.ToArray();
        }
Beispiel #3
0
        private static Circle3D HoneycombEdgeUHS(int p, int q, int r)
        {
            Sphere[] simplex = Mirrors(p, q, r, moveToBall: false);
            Sphere   s1      = simplex[0].Clone();
            Sphere   s2      = s1.Clone();

            s2.Reflect(simplex[1]);
            Circle3D intersection = s1.Intersection(s2);

            return(intersection);
        }
Beispiel #4
0
        public static Circle3D FromCenterAnd2Points( Vector3D cen, Vector3D p1, Vector3D p2 )
        {
            Circle3D circle = new Circle3D();
            circle.Center = cen;
            circle.Radius = ( p1 - cen ).Abs();

            if( !Tolerance.Equal( circle.Radius, ( p2 - cen ).Abs() ) )
                throw new System.ArgumentException( "Points are not on the same circle." );

            Vector3D normal = ( p2 - cen ).Cross( p1 - cen );
            normal.Normalize();
            circle.Normal = normal;
            return circle;
        }
Beispiel #5
0
        /// <summary>
        /// NOTE: Not general, and assumes some things we know about this problem domain, 
        /// e.g. that c1 and c2 live on the same sphere of radius 1, and have two intersection points.
        /// </summary>
        public static void IntersectionCircleCircle( Vector3D sphereCenter, Circle3D c1, Circle3D c2, out Vector3D i1, out Vector3D i2 )
        {
            // Spherical analogue of our flat circle-circle intersection.
            // Spherical pythagorean theorem for sphere where r=1: cos(hypt) = cos(A)*cos(B)

            Circle3D clone1 = c1.Clone(), clone2 = c2.Clone();
            //clone1.Center -= sphereCenter;
            //clone2.Center -= sphereCenter;

            // Great circle (denoted by normal vector), and distance between the centers.
            Vector3D gc = clone2.Normal.Cross( clone1.Normal );
            double d = clone2.Normal.AngleTo( clone1.Normal );
            double r1 = clone1.Normal.AngleTo( clone1.PointOnCircle );
            double r2 = clone2.Normal.AngleTo( clone2.PointOnCircle );

            // Calculate distances we need.  So ugly!
            // http://www.wolframalpha.com/input/?i=cos%28r1%29%2Fcos%28r2%29+%3D+cos%28x%29%2Fcos%28d-x%29%2C+solve+for+x
            double t1 = Math.Pow( Math.Tan( d / 2 ), 2 );
            double t2 = Math.Cos( r1 ) / Math.Cos( r2 );
            double t3 = Math.Sqrt( (t1 + 1) * (t1 * t2 * t2 + 2 * t1 * t2 + t1 + t2 * t2 - 2 * t2 + 1) ) - 2 * t1 * t2;
            double x = 2 * Math.Atan( t3 / (t1 * t2 + t1 - t2 + 1) );
            double y = Math.Acos( Math.Cos( r1 ) / Math.Cos( x ) );

            i1 = clone1.Normal;
            i1.RotateAboutAxis( gc, x );
            i2 = i1;

            // Perpendicular to gc through i1.
            Vector3D gc2 = i1.Cross( gc );
            i1.RotateAboutAxis( gc2, y );
            i2.RotateAboutAxis( gc2, -y );
            i1 += sphereCenter;
            i2 += sphereCenter;

            /*
            // It would be nice to do the spherical analogue of circle-circle intersections, like here:
            // http://mathworld.wolfram.com/Circle-CircleIntersection.html
            // But I don't want to jump down that rabbit hole and am going to sacrifice some speed to use
            // my existing euclidean function.

            // Stereographic projection to the plane.  XXX - Crap, circles may become lines, and this isn't being handled well.
            Circle3D c1Plane = H3Models.BallToUHS( clone1 );
            Circle3D c2Plane = H3Models.BallToUHS( clone2 );
            if( 2 != Euclidean2D.IntersectionCircleCircle( c1Plane.ToFlatCircle(), c2Plane.ToFlatCircle(), out i1, out i2 ) )
                throw new System.Exception( "Expected two intersection points" );
            i1 = H3Models.UHSToBall( i1 ); i1 += sphereCenter;
            i2 = H3Models.UHSToBall( i2 ); i2 += sphereCenter;
            */
        }
Beispiel #6
0
        /// <summary>
        /// Returns null if no intersection, or a Tuple.
        /// Currently does not work in tangent case.
        /// </summary>
        public System.Tuple <Vector3D, Vector3D> Intersection(Circle3D c)
        {
            Circle3D intersectionCircle = Intersection(new Sphere()
            {
                Center = c.Center, Radius = c.Radius
            });

            if (intersectionCircle == null)
            {
                return(null);
            }

            Vector3D vCross = c.Normal.Cross(intersectionCircle.Normal);

            vCross.Normalize();
            vCross *= intersectionCircle.Radius;
            return(new System.Tuple <Vector3D, Vector3D>(intersectionCircle.Center + vCross, intersectionCircle.Center - vCross));
        }
Beispiel #7
0
        public static Circle3D FromCenterAnd2Points(Vector3D cen, Vector3D p1, Vector3D p2)
        {
            Circle3D circle = new Circle3D();

            circle.Center = cen;
            circle.Radius = (p1 - cen).Abs();

            if (!Tolerance.Equal(circle.Radius, (p2 - cen).Abs()))
            {
                throw new System.ArgumentException("Points are not on the same circle.");
            }

            Vector3D normal = (p2 - cen).Cross(p1 - cen);

            normal.Normalize();
            circle.Normal = normal;
            return(circle);
        }
Beispiel #8
0
        /// <summary>
        /// Finds the intersection (a circle) between us and another sphere.
        /// Returns null if sphere centers are coincident or no intersection exists.
        /// Does not currently work for planes.
        /// </summary>
        public Circle3D Intersection(Sphere s)
        {
            if (this.IsPlane || s.IsPlane)
            {
                throw new System.NotImplementedException();
            }

            double r = s.Radius;
            double R = this.Radius;

            Vector3D diff = this.Center - s.Center;
            double   d    = diff.Abs();

            if (Tolerance.Equal(d, r + R))
            {
                diff.Normalize();
                return(new Circle3D()
                {
                    Center = s.Center + diff * s.Radius,
                    Radius = 0
                });
            }

            if (Tolerance.Zero(d) || d > r + R)
            {
                return(null);
            }

            // Sphere's inside spheres and not touching.
            //if( d < Math.Abs( R - r ) )
            //	return null;

            double x = (d * d + r * r - R * R) / (2 * d);
            double y = Math.Sqrt(r * r - x * x);

            Circle3D result = new Circle3D();

            diff.Normalize();
            result.Normal = diff;
            result.Center = s.Center + diff * x;
            result.Radius = y;
            return(result);
        }
Beispiel #9
0
        /// <summary>
        /// Calculates the point of our simplex that is at the middle of an edge.
        /// </summary>
        private static Vector3D MidEdgePointBall(int p, int q, int r)
        {
            // We need the mid-radius, but we have to do the calculation
            // with our Euclidean simplex mirrors (to avoid infinities that happen in the formulas).
            Circle3D edge = HoneycombEdgeUHS(p, q, r);

            if (edge.Radius == 0)
            {
                return(edge.Center);
            }

            Geometry cellGeometry = Geometry2D.GetGeometry(p, q);

            switch (cellGeometry)
            {
            case Geometry.Spherical:
            {
                Sphere sphereInBall = H3Models.UHSToBall(new Sphere()
                    {
                        Center = edge.Center, Radius = edge.Radius
                    });
                Vector3D mid = sphereInBall.ProjectToSurface(new Vector3D());                           // Project origin to sphere.
                return(mid);
            }

            case Geometry.Euclidean:
            {
                Vector3D mid = H3Models.UHSToBall(edge.Center + new Vector3D(0, 0, edge.Radius));
                return(mid);
            }

            case Geometry.Hyperbolic:
            {
                throw new System.NotImplementedException();
            }
            }

            throw new System.ArgumentException();
        }
Beispiel #10
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)
            {
                // 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);
        }
Beispiel #11
0
        /// <summary>
        /// Returns the 6 simplex edges in the UHS model.
        /// </summary>
        public static H3.Cell.Edge[] SimplexEdgesUHS(int p, int q, int r)
        {
            // Only implemented for honeycombs with hyperideal cells right now.
            if (!(Geometry2D.GetGeometry(p, q) == Geometry.Hyperbolic))
            {
                throw new System.NotImplementedException();
            }

            Sphere[] simplex = SimplexCalcs.Mirrors(p, q, r, moveToBall: false);

            Circle[] circles = simplex.Select(s => H3Models.UHS.IdealCircle(s)).ToArray();

            Vector3D[] defPoints = new Vector3D[6];
            Vector3D   dummy;

            Euclidean2D.IntersectionLineCircle(circles[1].P1, circles[1].P2, circles[0], out defPoints[0], out dummy);
            Euclidean2D.IntersectionLineCircle(circles[2].P1, circles[2].P2, circles[0], out defPoints[1], out dummy);
            Euclidean2D.IntersectionLineCircle(circles[1].P1, circles[1].P2, circles[3], out defPoints[2], out dummy);
            Euclidean2D.IntersectionLineCircle(circles[2].P1, circles[2].P2, circles[3], out defPoints[3], out dummy);

            Circle3D c = simplex[0].Intersection(simplex[3]);

            Vector3D normal = c.Normal;

            normal.RotateXY(Math.PI / 2);
            Vector3D intersection;
            double   height, off;

            Euclidean2D.IntersectionLineLine(c.Center, c.Center + normal, circles[1].P1, circles[1].P2, out intersection);
            off            = (intersection - c.Center).Abs();
            height         = Math.Sqrt(c.Radius * c.Radius - off * off);
            intersection.Z = height;
            defPoints[4]   = intersection;

            Euclidean2D.IntersectionLineLine(c.Center, c.Center + normal, circles[2].P1, circles[2].P2, out intersection);
            off            = (intersection - c.Center).Abs();
            height         = Math.Sqrt(c.Radius * c.Radius - off * off);
            intersection.Z = height;
            defPoints[5]   = intersection;

            // Hyperideal vertex too?
            bool order = false;

            H3.Cell.Edge[] edges = null;
            if (Geometry2D.GetGeometry(q, r) == Geometry.Hyperbolic)
            {
                edges = new H3.Cell.Edge[]
                {
                    new H3.Cell.Edge(new Vector3D(), new Vector3D(0, 0, 10)),
                    new H3.Cell.Edge(defPoints[4], defPoints[5], order),
                    new H3.Cell.Edge(defPoints[0], defPoints[4], order),
                    new H3.Cell.Edge(defPoints[1], defPoints[5], order),
                    new H3.Cell.Edge(defPoints[2], defPoints[4], order),
                    new H3.Cell.Edge(defPoints[3], defPoints[5], order),
                };
            }
            else
            {
                Vector3D vPointUHS = H3Models.BallToUHS(VertexPointBall(p, q, r));
                defPoints[0] = defPoints[1] = vPointUHS;
                edges        = new H3.Cell.Edge[]
                {
                    new H3.Cell.Edge(vPointUHS, new Vector3D(0, 0, 10)),
                    new H3.Cell.Edge(defPoints[4], defPoints[5], order),
                    new H3.Cell.Edge(defPoints[0], defPoints[4], order),
                    new H3.Cell.Edge(defPoints[1], defPoints[5], order),
                    new H3.Cell.Edge(defPoints[2], defPoints[4], order),
                    new H3.Cell.Edge(defPoints[3], defPoints[5], order),
                };
            }

            return(edges);
        }
Beispiel #12
0
        /// <summary>
        /// This will return an altered facet to create true apparent 2D tilings (proper bananas) on the boundary.
        /// Notes:
        ///		The input simplex must be in the ball model.
        ///		The first mirror of the simplex (the one that mirrors across cells) is the one we end up altering.
        /// </summary>
        public static Sphere AlteredFacetForTrueApparent2DTilings( Sphere[] simplex )
        {
            // We first need to find the size of the apparent 2D disk surrounding the leg.
            // This is also the size of the apparent cell head disk of the dual.
            // So we want to get the midsphere (insphere would also work) of the dual cell with that head,
            // then calculate the intersection of that with the boundary.
            Sphere cellMirror = simplex[0];
            if( cellMirror.IsPlane )
                throw new System.NotImplementedException();

            // The point centered on a face is the closest point of the cell mirror to the origin.
            // This will be the point centered on an edge on the dual cell.
            Vector3D facePoint = cellMirror.ProjectToSurface( new Vector3D() );

            // Reflect it to get 3 more points on our midsphere.
            Vector3D reflected1 = simplex[1].ReflectPoint( facePoint );
            Vector3D reflected2 = simplex[2].ReflectPoint( reflected1 );
            Vector3D reflected3 = simplex[0].ReflectPoint( reflected2 );
            Sphere midSphere = Sphere.From4Points( facePoint, reflected1, reflected2, reflected3 );

            // Get the ideal circles of the cell mirror and midsphere.
            // Note: The midsphere is not geodesic, so we can't calculate it the same.
            Sphere cellMirrorUHS = H3Models.BallToUHS( cellMirror );
            Circle cellMirrorIdeal = H3Models.UHS.IdealCircle( cellMirrorUHS );

            Sphere ball = new Sphere();
            Circle3D midSphereIdealBall = ball.Intersection( midSphere ); // This should exist because we've filtered for honeycombs with hyperideal verts.
            Circle3D midSphereIdealUHS = H3Models.BallToUHS( midSphereIdealBall );
            Circle midSphereIdeal = new Circle { Center = midSphereIdealUHS.Center, Radius = midSphereIdealUHS.Radius };

            // The intersection point of our cell mirror and the disk of the apparent 2D tiling
            // gives us "ideal" points on the apparent 2D boundary. These points will be on the new cell mirror.
            Vector3D i1, i2;
            if( 2 != Euclidean2D.IntersectionCircleCircle( cellMirrorIdeal, midSphereIdeal, out i1, out i2 ) )
            {
                //throw new System.ArgumentException( "Since we have hyperideal vertices, we should have an intersection with 2 points." );

                // Somehow works in the euclidean case.
                // XXX - Make this better.
                return H3Models.UHSToBall( new Sphere() { Center = Vector3D.DneVector(), Radius = double.NaN } );
            }

            double bananaThickness = 0.025;
            //bananaThickness = 0.15;
            //bananaThickness = 0.04;

            // Transform the intersection points to a standard Poincare disk.
            // The midsphere radius is the scale of the apparent 2D tilings.
            double scale = midSphereIdeal.Radius;
            Vector3D offset = midSphereIdeal.Center;
            i1 -= offset;
            i2 -= offset;
            i1 /= scale;
            i2 /= scale;
            Circle3D banana = H3Models.Ball.OrthogonalCircle( i1, i2 );
            Vector3D i3 = H3Models.Ball.ClosestToOrigin( banana );
            i3 = Hyperbolic2D.Offset( i3, -bananaThickness );

            // Transform back.
            i1 *= scale; i2 *= scale; i3 *= scale;
            i1 += offset; i2 += offset; i3 += offset;

            // Construct our new simplex mirror with these 3 points.
            Circle3D c = new Circle3D( i1, i2, i3 );

            Sphere result = new Sphere() { Center = c.Center, Radius = c.Radius };
            return H3Models.UHSToBall( result );
        }
Beispiel #13
0
 /// <summary>
 /// Given a geodesic circle, find the point closest to the origin.
 /// </summary>
 public static Vector3D ClosestToOrigin( Circle3D c )
 {
     Sphere s = new Sphere { Center = c.Center, Radius = c.Radius };
     return ClosestToOrigin( s );
 }
Beispiel #14
0
        /// <summary>
        /// Returns intersection points between a circle and a great circle.
        /// There may be 0, 1, or 2 intersection points.
        /// Returns false if the circle is the same as gc.
        /// </summary>
        static bool IntersectionCircleGC( Circle3D c, Vector3D gc, List<Vector3D> iPoints )
        {
            double radiusCosAngle = CosAngle( c.Normal, c.PointOnCircle );
            double radiusAngle = Math.Acos( radiusCosAngle );
            double radius = radiusAngle;	// Since sphere radius is 1.

            // Find the great circle perpendicular to gc, and through the circle center.
            Vector3D gcPerp = c.Normal.Cross( gc );
            if( !gcPerp.Normalize() )
            {
                // Circles are parallel => Zero or infinity intersections.
                if( Tolerance.Equal( radius, Math.PI / 2 ) )
                    return false;

                return true;
            }

            // Calculate the offset angle from the circle normal to the gc normal.
            double offsetAngle = c.Normal.AngleTo( gc );
            if( Tolerance.GreaterThan( offsetAngle, Math.PI / 2 ) )
            {
                gc *= -1;
                offsetAngle = c.Normal.AngleTo( gc );
            }
            double coAngle = Math.PI / 2 - offsetAngle;

            // No intersections.
            if( radiusAngle < coAngle )
                return true;

            // Here is the perpendicular point on the great circle.
            Vector3D pointOnGC = c.Normal;
            pointOnGC.RotateAboutAxis( gcPerp, coAngle );

            // 1 intersection.
            if( Tolerance.Equal( radiusAngle, coAngle ) )
            {
                iPoints.Add( pointOnGC );
                return true;
            }

            // 2 intersections.

            // Spherical pythagorean theorem
            // http://en.wikipedia.org/wiki/Pythagorean_theorem#Spherical_geometry
            // We know the hypotenuse and one side.  We need the third leg.
            // We do this calculation on a unit sphere, to get the result as a normalized cosine of an angle.
            double sideCosA = radiusCosAngle / Math.Cos( coAngle );
            double rot = Math.Acos( sideCosA );

            Vector3D i1 = pointOnGC, i2 = pointOnGC;
            i1.RotateAboutAxis( gc, rot );
            i2.RotateAboutAxis( gc, -rot );
            iPoints.Add( i1 );
            iPoints.Add( i2 );

            Circle3D test = new Circle3D { Normal = gc, Center = new Vector3D(), Radius = 1 };

            return true;
        }
Beispiel #15
0
 static bool IsGC( Circle3D c )
 {
     return c.Center.IsOrigin;
 }
Beispiel #16
0
 public static Circle3D BallToUHS( Circle3D c )
 {
     Vector3D[] points = c.RepresentativePoints;
     for( int i=0; i<3; i++ )
         points[i] = H3Models.BallToUHS( points[i] );
     return new Circle3D( points[0], points[1], points[2] );
 }
Beispiel #17
0
        /// <summary>
        /// Finds the intersection (a circle) between us and another sphere.
        /// Returns null if sphere centers are coincident or no intersection exists.
        /// Does not currently work for planes.
        /// </summary>
        public Circle3D Intersection( Sphere s )
        {
            if( this.IsPlane || s.IsPlane )
                throw new System.NotImplementedException();

            double r = s.Radius;
            double R = this.Radius;

            Vector3D diff = this.Center - s.Center;
            double d = diff.Abs();
            if( Tolerance.Zero( d ) || d > r + R )
                return null;

            double x = ( d*d + r*r - R*R ) / ( 2*d );
            double y = Math.Sqrt( r*r - x*x );

            Circle3D result = new Circle3D();
            diff.Normalize();
            result.Normal = diff;
            result.Center = s.Center + diff * x;
            result.Radius = y;
            return result;
        }
Beispiel #18
0
            /// <summary>
            /// Find the sphere defined by 3 points on the unit sphere, and orthogonal to the unit sphere.
            /// Returns null if points are not on the unit sphere.
            /// </summary>
            public static Sphere OrthogonalSphere( Vector3D b1, Vector3D b2, Vector3D b3 )
            {
                Sphere unitSphere = new Sphere();
                if( !unitSphere.IsPointOn( b1 ) ||
                    !unitSphere.IsPointOn( b2 ) ||
                    !unitSphere.IsPointOn( b3 ) )
                    return null;

                Circle3D c = new Circle3D( b1, b2, b3 );

                // Same impl as orthogonal circles now.
                Vector3D center;
                double radius;
                OrthogonalCircle( b1, b1 + ( c.Center - b1 ) * 2, out center, out radius );

                Sphere sphere = new Sphere();
                if( Infinity.IsInfinite( radius ) )
                {
                    // Have the center act as a normal.
                    sphere.Center = c.Normal;
                    sphere.Radius = double.PositiveInfinity;
                }
                else
                {
                    sphere.Center = center;
                    sphere.Radius = radius;
                }
                return sphere;
            }
Beispiel #19
0
            /// <summary>
            /// Given 2 points in the interior of the ball, calculate the center and radius of the orthogonal circle.
            /// One point may optionally be on the boundary, but one shoudl be in the interior.
            /// If both points are on the boundary, we'll fall back on our other method.
            /// </summary>
            public static void OrthogonalCircleInterior( Vector3D v1, Vector3D v2, out Circle3D circle )
            {
                if( Tolerance.Equal( v1.Abs(), 1 ) &&
                    Tolerance.Equal( v2.Abs(), 1 ) )
                {
                    circle = OrthogonalCircle( v1, v2 );
                    return;
                }

                // http://www.math.washington.edu/~king/coursedir/m445w06/ortho/01-07-ortho-to3.html
                // http://www.youtube.com/watch?v=Bkvo09KE1zo

                Vector3D interior = Tolerance.Equal( v1.Abs(), 1 ) ? v2 : v1;

                Sphere ball = new Sphere();
                Vector3D reflected = ball.ReflectPoint( interior );
                circle = new Circle3D( reflected, v1, v2 );
            }
Beispiel #20
0
            /// <summary>
            /// Given 2 points on the boundary of a circle, calculate the orthogonal circle.
            /// </summary>
            public static Circle3D OrthogonalCircle( Circle3D c, Vector3D v1, Vector3D v2 )
            {
                // Needs testing.
                throw new System.NotImplementedException();

                // Move/Scale c to unit circle.
                Vector3D offset = c.Center;
                double scale = c.Radius;
                v1 -= offset;
                v2 -= offset;
                v1 /= scale;
                v2 /= scale;

                // Call the other method.
                Vector3D center;
                double rad;
                OrthogonalCircle( v1, v2, out center, out rad );
                rad *= scale;
                center += offset;

                return new Circle3D()
                {
                    Center = center,
                    Radius = rad
                };
            }
Beispiel #21
0
        /// <summary>
        /// This is a 2D function for now.
        /// Given an input geodesic in the plane, returns an equidistant circle.
        /// Offset would be the offset at the origin.
        /// </summary>
        public static Circle3D EquidistantOffset( Segment seg, double offset )
        {
            // Get the ideal endpoints of the input.
            Vector3D i1, i2;
            H3Models.Ball.GeodesicIdealEndpoints( seg.P1, seg.P2, out i1, out i2 );

            // Get the offset amount at the location.
            Vector3D center;
            double radius;
            H3Models.Ball.OrthogonalCircle( i1, i2, out center, out radius );
            Vector3D closest = center;
            closest.Normalize();
            closest *= (center.Abs() - radius);
            H3Models.Ball.DupinCyclideSphere( closest, offset, out center, out radius );
            double mag = center.Abs() - radius;
            closest.Normalize();
            closest *= mag;

            // Resulting circle will go through i1 and i2, but will be offset slightly towards the origin.
            Circle3D result = new Circle3D( i1, closest, i2 );
            return result;
        }
Beispiel #22
0
            /// <summary>
            /// Given two points (in the UHS model), find the endpoints 
            /// of the associated geodesic that lie on the z=0 plane.
            /// </summary>
            public static void GeodesicIdealEndpoints( Vector3D v1, Vector3D v2, out Vector3D z1, out Vector3D z2 )
            {
                // We have to special case when geodesic is vertical (parallel to z axis).
                Vector3D diff = v2 - v1;
                Vector3D diffFlat = new Vector3D( diff.X, diff.Y );
                if( Tolerance.Zero( diffFlat.Abs() ) )	// Vertical
                {
                    Vector3D basePoint = new Vector3D( v1.X, v1.Y );
                    z1 = diff.Z > 0 ? basePoint : Infinity.InfinityVector;
                    z2 = diff.Z < 0 ? basePoint : Infinity.InfinityVector;
                }
                else
                {
                    if( Tolerance.Zero( v1.Z ) && Tolerance.Zero( v2.Z ) )
                    {
                        z1 = v1;
                        z2 = v2;
                        return;
                    }

                    // If one point is ideal, we need to not reflect that one!
                    bool swapped = false;
                    if( Tolerance.Zero( v1.Z ) )
                    {
                        Utils.SwapPoints( ref v1, ref v2 );
                        swapped = true;
                    }

                    Vector3D v1_reflected = v1;
                    v1_reflected.Z *= -1;
                    Circle3D c = new Circle3D( v1_reflected, v1, v2 );
                    Vector3D radial = v1 - c.Center;
                    radial.Z = 0;
                    if( !radial.Normalize() )
                    {
                        radial = v2 - c.Center;
                        radial.Z = 0;
                        if( !radial.Normalize() )
                            System.Diagnostics.Debugger.Break();
                    }

                    radial *= c.Radius;
                    z1 = c.Center + radial;
                    z2 = c.Center - radial;

                    // Make sure the order will be right.
                    // (z1 closest to v1 along arc).
                    if( v1.Dist( z1 ) > v2.Dist( z1 ) )
                        Utils.SwapPoints( ref z1, ref z2 );
                    if( swapped )
                        Utils.SwapPoints( ref z1, ref z2 );
                }
            }
Beispiel #23
0
        /// <summary>
        /// Calculate the surfaces of the tetrahedron for the quadrilateral
        /// </summary>
        private static Sphere[] Tetrahedron( Vector3D[] quad )
        {
            // NOTE: For planes, the convention is that the normal vector points "outward"

            // The only non-planar sphere is incident with the last two points of the quad,
            // as well as the antipode of the last point. Fig 1 in Lawson's paper.
            Circle3D c = new Circle3D( quad[3], quad[2], -quad[3] );

            Vector3D plane3 = quad[3];
            plane3.RotateXY( Math.PI / 2 );

            return new Sphere[]
            {
                Sphere.Plane( new Vector3D( 0, -1 ) ),
                Sphere.Plane( new Vector3D( 0, 0, -1 ) ),
                Sphere.Plane( plane3 ),
                new Sphere( c.Center, c.Radius )
            };
        }
Beispiel #24
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
            };
        }
Beispiel #25
0
        /// <summary>
        /// Had to break the intersection method into separate cases (depending on when circles are great circles or not),
        /// because the spherical pythagorean theorem breaks down in GC cases.
        /// </summary>
        public static bool IntersectionSmart( Vector3D sphereCenter, Circle3D c1, Circle3D c2, out Vector3D i1, out Vector3D i2 )
        {
            i1 = i2 = Vector3D.DneVector();

            Circle3D clone1 = c1.Clone(), clone2 = c2.Clone();
            clone1.Center -= sphereCenter;
            clone2.Center -= sphereCenter;

            if( IsGC( clone1 ) && IsGC( clone2 ) )
            {
                if( !IntersectionGCGC( clone1.Normal, clone2.Normal, out i1, out i2 ) )
                    return false;
            }
            else if( IsGC( clone1 ) || IsGC( clone2 ) )
            {
                bool firstIsGC = IsGC( clone1 );
                Vector3D gc = firstIsGC ? clone1.Normal : clone2.Normal;
                Circle3D c = firstIsGC ? clone2 : clone1;

                List<Vector3D> iPoints = new List<Vector3D>();
                if( !IntersectionCircleGC( c, gc, iPoints ) )
                    return false;

                if( iPoints.Count != 2 )
                    throw new System.NotImplementedException();

                i1 = iPoints[0];
                i2 = iPoints[1];
            }
            else
                throw new System.NotImplementedException();

            // Move us back to the sphere center.
            i1 += sphereCenter;
            i2 += sphereCenter;
            return true;
        }
Beispiel #26
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;
        }