Esempio 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);
        }
Esempio n. 2
0
        private static Isometry SetupIsometry(Cell clickedCell, Vector3D clickedPoint, Puzzle puzzle)
        {
            int      p            = puzzle.Config.P;
            Geometry g            = puzzle.Config.Geometry;
            Isometry cellIsometry = clickedCell.Isometry.Clone();

            // Take out reflections.
            // ZZZ - Figuring out how to deal with these reflected isometries was a bit painful to figure out.
            //		 I wish I had just taken more care to not have any cell isometries with reflections.
            //		 Maybe I can rework that to be different at some point.
            if (cellIsometry.Reflection != null)
            {
                cellIsometry = Isometry.ReflectX() * cellIsometry;
            }

            // Round to nearest vertex.
            Vector3D centered         = cellIsometry.Apply(clickedPoint);
            double   angle            = Euclidean2D.AngleToCounterClock(centered, new Vector3D(1, 0));
            double   angleFromZeroToP = p * angle / (2 * Math.PI);

            angleFromZeroToP = Math.Round(angleFromZeroToP, 0);
            if (p == (int)angleFromZeroToP)
            {
                angleFromZeroToP = 0;
            }
            angle = 2 * Math.PI * angleFromZeroToP / p;

            // This will take vertex to canonical position.
            Mobius rotation = new Mobius();

            rotation.Isometry(g, angle, new Complex());
            Isometry rotIsometry = new Isometry(rotation, null);

            return(rotIsometry * cellIsometry);
        }
Esempio n. 3
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]));
        }
Esempio n. 4
0
        /// <summary>
        /// Generate a catenoid, then move it to a point on the hemisphere in S^3.
        /// </summary>
        private static Mesh Catenoid(double scale, Vector3D loc, double rld_height, double waist)
        {
            double h = waist * 3.5;
            //Mesh mesh = StandardCatenoid( waist, h );
            Mesh mesh = CatenoidSquared(waist, h);

            mesh.Scale(scale * rld_height * 2 / h);                     // To make the catenoid meet up with the RLD solution.

            // First move to north pole, then rotate down to the location.
            Func <Vector3D, Vector3D> transform = v =>
            {
                v = H3Models.BallToUHS(v);
                Vector3D northPole = new Vector3D(0, 0, 1);
                Vector3D axis      = loc.Cross(northPole);
                if (!axis.Normalize())  // North or south pole?
                {
                    return(v);
                }
                double anglXY = Euclidean2D.AngleToCounterClock(new Vector3D(1, 0), new Vector3D(loc.X, loc.Y));
                v.RotateXY(anglXY);
                double angleDown = loc.AngleTo(northPole);
                v.RotateAboutAxis(axis, angleDown);

                if (v.DNE || Infinity.IsInfinite(v))
                {
                    throw new System.Exception();
                }
                return(v);
            };

            mesh.Transform(transform);
            return(mesh);
        }
Esempio n. 5
0
        /// <summary>s
        /// Checks if the point is inside our hexagon,
        /// but takes advantage of things we know to make it faster.
        /// The generic polygon check is way too slow.
        /// This is meant to be used during puzzle building.
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public bool IsPointInsideOptimized(Vector3D p)
        {
            if (!CircumCircle.IsPointInsideFast(p))
            {
                return(false);
            }

            // We will check that we are on the same side of every segment as the center.
            Vector3D cen = Hexagon.Center;

            foreach (Segment s in Hexagon.Segments)
            {
                if (s.Type == SegmentType.Line)
                {
                    if (!Euclidean2D.SameSideOfLine(s.P1, s.P2, cen, p))
                    {
                        return(false);
                    }
                }
                else
                {
                    bool inside1 = s.Circle.IsPointInside(cen);
                    bool inside2 = s.Circle.IsPointInside(p);
                    if (inside1 ^ inside2)
                    {
                        return(false);
                    }
                }
            }

            return(true);
        }
Esempio n. 6
0
        private static string FormatSphereNoMaterialOffset(Sphere sphere, bool invert, bool includeClosingBracket = true)
        {
            bool   microOffset = true;
            double microOff    = 0.00001;

            //microOff = 0.000001;
            // Don't offset unless drawn!!!
            //microOff = -0.00005;
            if (invert)
            {
                microOff *= -1;
            }

            if (sphere.IsPlane)
            {
                Vector3D offsetOnNormal = Euclidean2D.ProjectOntoLine(sphere.Offset, new Vector3D(), sphere.Normal);
                double   offset         = offsetOnNormal.Abs();
                if (offsetOnNormal.Dot(sphere.Normal) < 0)
                {
                    offset *= -1;
                }

                if (microOffset)
                {
                    offset -= microOff;
                }

                return(string.Format("plane {{ {0}, {1:G6}{2} {3}",
                                     FormatVec(sphere.Normal), offset, invert ? " inverse" : string.Empty,
                                     includeClosingBracket ? "}" : string.Empty));
            }
            else
            {
                double radius = sphere.Radius;
                if (microOffset)
                {
                    if (radius < 20)
                    {
                        radius -= microOff;
                    }
                    else
                    {
                        radius *= (1 - microOff);
                    }
                }

                return(string.Format("sphere {{ {0}, {1:G6}{2} {3}",
                                     FormatVec(sphere.Center), radius, invert ? " inverse" : string.Empty,
                                     includeClosingBracket ? "}" : string.Empty));
            }
        }
Esempio n. 7
0
 private static string H3Facet(Sphere sphere)
 {
     if (sphere.IsPlane)
     {
         Vector3D offsetOnNormal = Euclidean2D.ProjectOntoLine(sphere.Offset, new Vector3D(), sphere.Normal);
         return(string.Format("plane {{ {0}, {1:G6} material {{ sphereMat }} clipped_by {{ ball }} }}",
                              FormatVec(sphere.Normal), offsetOnNormal.Abs()));
     }
     else
     {
         return(string.Format("sphere {{ {0}, {1:G6} material {{ sphereMat }} clipped_by {{ ball }} }}",
                              FormatVec(sphere.Center), sphere.Radius));
     }
 }
Esempio n. 8
0
 private static string FormatSphereNoMaterial(Sphere sphere, bool invert, bool includeClosingBracket = true)
 {
     if (sphere.IsPlane)
     {
         Vector3D offsetOnNormal = Euclidean2D.ProjectOntoLine(sphere.Offset, new Vector3D(), sphere.Normal);
         return(string.Format("plane {{ {0}, {1:G6}{2} {3}",
                              FormatVec(sphere.Normal), offsetOnNormal.Abs(), invert ? " inverse" : string.Empty,
                              includeClosingBracket ? "}" : string.Empty));
     }
     else
     {
         return(string.Format("sphere {{ {0}, {1:G6}{2} {3}",
                              FormatVec(sphere.Center), sphere.Radius, invert ? " inverse" : string.Empty,
                              includeClosingBracket ? "}" : string.Empty));
     }
 }
Esempio n. 9
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);
                }
            }
        }
Esempio n. 10
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;
        }
Esempio n. 11
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)
        {
            //Sphere m = H3Models.BallToUHS( simplex[0] );
            //Sphere t = new Sphere() { Center = m.Center, Radius = m.Radius*100 };
            //return H3Models.UHSToBall( t );

            // 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.05;

            // 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));
        }