Ejemplo n.º 1
0
        public static void DrawHyperbolicGeodesic(CircleNE c, Color color,
                                                  System.Func <Vector3D, Vector3D> transform)
        {
            GL.Color3(color);

            Segment seg = null;

            if (c.IsLine)
            {
                // It will go through the origin.
                Vector3D p = c.P1;
                p.Normalize();
                seg = Segment.Line(p, -p);
            }
            else
            {
                Vector3D p1, p2;
                Euclidean2D.IntersectionCircleCircle(c, new Circle(), out p1, out p2);
                seg = Segment.Arc(p1, H3Models.Ball.ClosestToOrigin(new Circle3D()
                {
                    Center = c.Center, Radius = c.Radius, Normal = new Vector3D(0, 0, 1)
                }), p2);
            }

            DrawSeg(seg, 75, transform);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Builds a segment going through the box (if we cross it).
        /// </summary>
        private static Segment BuildSegment(CircleNE c)
        {
            Vector3D[] iPoints = Box.GetIntersectionPoints(c);
            if (2 != iPoints.Length)
            {
                return(null);
            }

            if (c.IsLine)
            {
                return(Segment.Line(iPoints[0], iPoints[1]));
            }

            // Find the midpoint.  Probably a better way.
            Vector3D t1 = iPoints[0] - c.Center;
            Vector3D t2 = iPoints[1] - c.Center;
            double   angle1 = Euclidean2D.AngleToCounterClock(t1, t2);
            double   angle2 = Euclidean2D.AngleToClock(t1, t2);
            Vector3D mid1 = t1, mid2 = t1;

            mid1.RotateXY(angle1 / 2);
            mid2.RotateXY(-angle2 / 2);
            mid1 += c.Center;
            mid2 += c.Center;
            Vector3D mid = mid1;

            if (mid2.Abs() < mid1.Abs())
            {
                mid = mid2;
            }
            return(Segment.Arc(iPoints[0], mid, iPoints[1]));
        }
Ejemplo n.º 3
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 );
            }
        }
Ejemplo n.º 4
0
        /// <summary>
        /// This will setup systolic pants for the Klein Quartic.
        /// </summary>
        public static CircleNE[] SystolesForKQ()
        {
            // 0th vertex of the central heptagon will be the center of our first hexagon.
            // Arnaud's applet is helpful to think about this.
            // http://www.math.univ-toulouse.fr/~cheritat/AppletsDivers/Klein/

            Polygon centralTile = new Polygon();

            centralTile.CreateRegular(7, 3);
            Vector3D vertex0 = centralTile.Segments[0].P1;
            Vector3D mid1    = centralTile.Segments[1].Midpoint;
            Vector3D mid2    = centralTile.Segments[2].Midpoint;

            Circle3D orthogonal;

            H3Models.Ball.OrthogonalCircleInterior(mid1, mid2, out orthogonal);

            // We make the non-euclidean center the center of the hexagon.
            CircleNE c1 = new CircleNE()
            {
                Center = orthogonal.Center, Radius = orthogonal.Radius, CenterNE = vertex0
            };

            return(CycleCircles(c1, vertex0));
        }
Ejemplo n.º 5
0
 public Cell(Polygon boundary, CircleNE vertexCircle)
 {
     Boundary      = boundary;
     VertexCircle  = vertexCircle;
     Stickers      = new List <Sticker>();
     Isometry      = new Isometry();
     IndexOfMaster = -1;
     PickInfo      = new PickInfo[] { };
 }
Ejemplo n.º 6
0
        private static CircleNE[] CycleCircles(CircleNE template, Vector3D vertex0)
        {
            Mobius          m      = RotMobius(vertex0);
            List <CircleNE> result = new List <CircleNE>();

            result.Add(template);
            for (int i = 0; i < 3; i++)
            {
                CircleNE next = result.Last().Clone();
                next.Transform(m);
                result.Add(next);
            }

            return(result.ToArray());
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Helper to check if we will affect a cell.
        /// </summary>
        public bool WillAffectCell(Cell cell, bool sphericalPuzzle)
        {
            if (Earthquake)
            {
                if (Pants.TestCircle.HasVertexInside(cell.Boundary))
                {
                    return(true);
                }

                for (int i = 0; i < 3; i++)
                {
                    CircleNE c = Pants.TestCircle.Clone();
                    c.Reflect(Pants.Hexagon.Segments[i * 2]);
                    if (c.HasVertexInside(cell.Boundary))
                    {
                        return(true);
                    }
                }

                return(false);

                /* This turned out too slow, and probably not robust too.
                 *
                 * if( Pants.Hexagon.Intersects( cell.Boundary ) )
                 *      return true;
                 *
                 * foreach( Polygon poly in Pants.AdjacentHexagons )
                 *      if( poly.Intersects( cell.Boundary ) )
                 *              return true;
                 *
                 * return false; */
            }

            foreach (CircleNE circleNE in this.Circles)
            {
                bool inside = sphericalPuzzle ?
                              circleNE.IsPointInsideNE(cell.Center) :
                              circleNE.IsPointInsideFast(cell.Center);

                if (inside ||
                    circleNE.Intersects(cell.Boundary))
                {
                    return(true);
                }
            }

            return(false);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Draws a generalized circle (may be a line) in OpenGL immediate mode,
        /// and safely handle circles with large radii.
        /// This is slower, so we only want to use it when necessary.
        /// </summary>
        public static void DrawCircleSafe(CircleNE c, Color color,
                                          System.Func <Vector3D, Vector3D> transform)
        {
            GL.Color3(color);

            if (c.IsLine)
            {
                Vector3D start = Euclidean2D.ProjectOntoLine(new Vector3D(), c.P1, c.P2);
                Vector3D d     = c.P2 - c.P1;
                d.Normalize();
                d *= 50;

                if (transform == null)
                {
                    GL.Begin(BeginMode.Lines);
                    GL.Vertex2(start.X + d.X, start.Y + d.Y);
                    GL.Vertex2(start.X - d.X, start.Y - d.Y);
                    GL.End();
                }
                else
                {
                    int      divisions = 500;
                    Vector3D begin = start + d, end = start - d, inc = (end - begin) / divisions;
                    GL.Begin(BeginMode.LineStrip);
                    for (int i = 0; i < divisions; i++)
                    {
                        Vector3D point = begin + inc * i;
                        GL.Vertex2(point.X, point.Y);
                    }
                    GL.End();
                }
            }
            else
            {
                Segment seg = BuildSegment(c);
                if (seg == null)
                {
                    DrawCircleInternal(c, color, 500, transform);
                }
                else
                {
                    DrawSeg(seg, 1000, transform);
                }
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Helper to check if we will affect a sticker, and cache in our list if so.
        /// </summary>
        public void WillAffectSticker(Sticker sticker, bool sphericalPuzzle)
        {
            if (AffectedStickers == null)
            {
                AffectedStickers = new List <StickerList>();
                for (int slice = 0; slice < this.NumSlices; slice++)
                {
                    AffectedStickers.Add(new List <Sticker>());
                }
            }

            if (Earthquake)
            {
                Vector3D cen = sticker.Poly.Center;
                if (Pants.IsPointInsideOptimized(cen))
                {
                    AffectedStickers[0].Add(sticker);
                }
                return;
            }

            // Slices are ordered by depth.
            // We cycle from the inner slice outward.
            for (int slice = 0; slice < this.Circles.Length; slice++)
            {
                CircleNE circle   = this.Circles[slice];
                bool     isInside = sphericalPuzzle ?
                                    circle.IsPointInsideNE(sticker.Poly.Center) :
                                    circle.IsPointInsideFast(sticker.Poly.Center);
                if (isInside)
                {
                    AffectedStickers[slice].Add(sticker);
                    return;
                }
            }

            // If we made it here for spherical puzzles, we are in the last slice.
            // Second check was needed for {3,5} 8C
            if (sphericalPuzzle &&
                (this.NumSlices != this.Circles.Length))
            {
                AffectedStickers[this.NumSlices - 1].Add(sticker);
            }
        }
Ejemplo n.º 10
0
        private static CircleNE[] OtherThreeSides()
        {
            Polygon centralTile = new Polygon();

            centralTile.CreateRegular(7, 3);
            Vector3D vertex0 = centralTile.Segments[0].P1;
            Vector3D p1      = centralTile.Segments[3].P1;
            Vector3D p2      = centralTile.Segments[3].P2;

            Circle3D orthogonal;

            H3Models.Ball.OrthogonalCircleInterior(p1, p2, out orthogonal);

            // We make the non-euclidean center the center of the hexagon.
            CircleNE c1 = new CircleNE()
            {
                Center = orthogonal.Center, Radius = orthogonal.Radius, CenterNE = vertex0
            };

            CircleNE[] otherThreeSides = CycleCircles(c1, vertex0);
            return(otherThreeSides);
        }
Ejemplo n.º 11
0
        public void SetupHexagonForKQ()
        {
            Polygon centralTile = new Polygon();

            centralTile.CreateRegular(7, 3);
            Vector3D vertex0 = centralTile.Segments[0].P1;

            CircleNE[] otherThreeSides = OtherThreeSides();
            CircleNE[] systoles        = SystolesForKQ();

            // Calc verts.
            List <Vector3D> verts = new List <Vector3D>();
            Vector3D        t1, t2;

            Euclidean2D.IntersectionCircleCircle(otherThreeSides[0], systoles[0], out t1, out t2);
            Vector3D intersection = t1.Abs() < 1 ? t1 : t2;

            verts.Add(intersection);
            intersection.Y *= -1;
            verts.Add(intersection);
            Mobius m = RotMobius(vertex0);

            verts.Add(m.Apply(verts[0]));
            verts.Add(m.Apply(verts[1]));
            verts.Add(m.Apply(verts[2]));
            verts.Add(m.Apply(verts[3]));

            // Setup all the segments.
            bool clockwise = true;

            Hexagon.Segments.AddRange(new Segment[]
            {
                Segment.Arc(verts[0], verts[1], otherThreeSides[0].Center, clockwise),
                Segment.Arc(verts[1], verts[2], systoles[1].Center, clockwise),
                Segment.Arc(verts[2], verts[3], otherThreeSides[1].Center, clockwise),
                Segment.Arc(verts[3], verts[4], systoles[2].Center, clockwise),
                Segment.Arc(verts[4], verts[5], otherThreeSides[2].Center, clockwise),
                Segment.Arc(verts[5], verts[0], systoles[0].Center, clockwise),
            });
            Hexagon.Center = vertex0;

            // Setup the test circle.
            m.Isometry(Geometry.Hyperbolic, 0, -vertex0);
            Polygon clone = Hexagon.Clone();

            clone.Transform(m);

            Circle temp = new Circle(
                clone.Segments[0].Midpoint,
                clone.Segments[2].Midpoint,
                clone.Segments[4].Midpoint);
            CircleNE tempNE = new CircleNE(temp, new Vector3D());

            tempNE.Transform(m.Inverse());
            TestCircle = tempNE;

            temp = new Circle(
                clone.Segments[0].P1,
                clone.Segments[1].P1,
                clone.Segments[2].P1);
            tempNE = new CircleNE(temp, new Vector3D());
            tempNE.Transform(m.Inverse());
            CircumCircle = tempNE;
        }
Ejemplo n.º 12
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!" );
            }
        }
Ejemplo n.º 13
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!" );
            }
        }