/// <summary> /// Slicing function used for earthquake puzzles. /// c should be geodesic (orthogonal to the disk boundary). /// </summary> public static void SlicePolygonWithHyperbolicGeodesic(Polygon p, CircleNE c, double thickness, out List <Polygon> output) { Geometry g = Geometry.Hyperbolic; Segment seg = null; if (c.IsLine) { Vector3D p1, p2; Euclidean2D.IntersectionLineCircle(c.P1, c.P2, new Circle(), out p1, out p2); seg = Segment.Line(p1, p2); } else { // Setup the two slicing circles. // These are cuts equidistant from the passed in geodesic. Vector3D closestToOrigin = H3Models.Ball.ClosestToOrigin(new Circle3D() { Center = c.Center, Radius = c.Radius, Normal = new Vector3D(0, 0, 1) }); Vector3D p1, p2; Euclidean2D.IntersectionCircleCircle(c, new Circle(), out p1, out p2); seg = Segment.Arc(p1, closestToOrigin, p2); } Circle c1 = H3Models.Ball.EquidistantOffset(g, seg, thickness / 2); Circle c2 = H3Models.Ball.EquidistantOffset(g, seg, -thickness / 2); CircleNE c1NE = c.Clone(), c2NE = c.Clone(); c1NE.Center = c1.Center; c2NE.Center = c2.Center; c1NE.Radius = c1.Radius; c2NE.Radius = c2.Radius; SlicePolygonHelper(p, c1NE, c2NE, out output); }
/// <summary> /// Reflect a point in us. /// ZZZ - This method is confusing in that it is opposite the above (we aren't reflecting ourselves here). /// </summary> /// <param name="p"></param> public Vector3D ReflectPoint(Vector3D p) { if (this.IsLine) { return(Euclidean2D.ReflectPointInLine(p, P1, P2)); } else { // Handle infinities. Vector3D infinityVector = Infinity.InfinityVector; if (p.Compare(Center)) { return(infinityVector); } if (p == infinityVector) { return(Center); } Vector3D v = p - Center; double d = v.Abs(); v.Normalize(); return(Center + v * (Radius * Radius / d)); } }
/// <summary> /// Checks to see if two points are ordered on this segment, that is: /// P1 -> test1 -> test2 -> P2 returns true. /// P1 -> test2 -> test1 -> P2 returns false; /// Also returns false if test1 or test2 are equal, not on the segment, or are an endpoint. /// </summary> /// <param name="p1"></param> /// <param name="p2"></param> /// <returns></returns> public bool Ordered(Vector3D test1, Vector3D test2) { if (test1.Compare(test2)) { Debug.Assert(false); return(false); } if (!IsPointOn(test1) || !IsPointOn(test2)) { Debug.Assert(false); return(false); } if (test1.Compare(P1) || test1.Compare(P2) || test2.Compare(P1) || test2.Compare(P2)) { return(false); } if (SegmentType.Arc == Type) { Vector3D t1 = P1 - Center; Vector3D t2 = test1 - Center; Vector3D t3 = test2 - Center; double a1 = Clockwise ? Euclidean2D.AngleToClock(t1, t2) : Euclidean2D.AngleToCounterClock(t1, t2); double a2 = Clockwise ? Euclidean2D.AngleToClock(t1, t3) : Euclidean2D.AngleToCounterClock(t1, t3); return(a1 < a2); } else { double d1 = (test1 - P1).MagSquared(); double d2 = (test2 - P1).MagSquared(); return(d1 < d2); } }
public static Vector3D SpiralToIsometric(Vector3D v, int p, int m, int n) { Complex vc = v.ToComplex(); v = new Vector3D(Math.Log(vc.Magnitude), vc.Phase); Vector3D e1 = new Vector3D(0, 1); Vector3D e2; switch (p) { case 3: e2 = new Vector3D(); break; case 4: e2 = new Vector3D(); break; case 6: e2 = new Vector3D(); break; default: throw new System.ArgumentException(); } double scale = Math.Sqrt(m * m + n * n); double a = Euclidean2D.AngleToClock(new Vector3D(0, 1), new Vector3D(m, n)); v.RotateXY(a); // Rotate v *= scale; // Scale v *= Math.Sqrt(2) * Geometry2D.EuclideanHypotenuse / (2 * Math.PI); v.RotateXY(Math.PI / 4); return(v); }
/// <summary> /// Construct a circle from 3 points /// </summary> /// <returns>false if the construction failed (if we are a line).</returns> public bool From3Points(Vector3D p1, Vector3D p2, Vector3D p3) { Reset(); // Check for any infinite points, in which case we are a line. // I'm not sure these checks are smart, since our IsInfinite check is so inclusive, // but Big Chop puzzle doesn't work if we don't do this. // ZZZ - Still, I need to think on this more. if (Infinity.IsInfinite(p1)) { this.From2Points(p2, p3); return(false); } else if (Infinity.IsInfinite(p2)) { this.From2Points(p1, p3); return(false); } else if (Infinity.IsInfinite(p3)) { this.From2Points(p1, p2); return(false); } /* Some links * http://mathforum.org/library/drmath/view/54323.html * http://delphiforfun.org/Programs/Math_Topics/circle_from_3_points.htm * There is lots of info out there about solving via equations, * but as with other code in this project, I wanted to use geometrical constructions. */ // Midpoints. Vector3D m1 = (p1 + p2) / 2; Vector3D m2 = (p1 + p3) / 2; // Perpendicular bisectors. Vector3D b1 = (p2 - p1) / 2; Vector3D b2 = (p3 - p1) / 2; b1.Normalize(); b2.Normalize(); b1.Rotate90(); b2.Rotate90(); Vector3D newCenter; int found = Euclidean2D.IntersectionLineLine(m1, m1 + b1, m2, m2 + b2, out newCenter); Center = newCenter; if (0 == found) { // The points are collinear, so we are a line. From2Points(p1, p2); return(false); } Radius = (p1 - Center).Abs(); Debug.Assert(Tolerance.Equal(Radius, (p2 - Center).Abs())); Debug.Assert(Tolerance.Equal(Radius, (p3 - Center).Abs())); return(true); }
public bool IsPointOn(Vector3D test) { if (this.IsLine) { return(Tolerance.Zero(Euclidean2D.DistancePointLine(test, P1, P2))); } return(Tolerance.Equal((test - Center).Abs(), Radius)); }
public Vector3D ReflectPoint(Vector3D input) { if (SegmentType.Arc == Type) { Circle c = this.Circle; return(c.ReflectPoint(input)); } else { return(Euclidean2D.ReflectPointInLine(input, P1, P2)); } }
public bool Intersects(Segment s) { Vector3D i1 = Vector3D.DneVector(), i2 = Vector3D.DneVector(); int numInt = 0; if (SegmentType.Arc == Type) { if (SegmentType.Arc == s.Type) { numInt = Euclidean2D.IntersectionCircleCircle(Circle, s.Circle, out i1, out i2); } else { numInt = Euclidean2D.IntersectionLineCircle(P1, P2, s.Circle, out i1, out i2); } } else { if (SegmentType.Arc == s.Type) { numInt = Euclidean2D.IntersectionLineCircle(s.P1, s.P2, Circle, out i1, out i2); } else { numInt = Euclidean2D.IntersectionLineLine(P1, P2, s.P1, s.P2, out i1); } } // -1 can denote conincident segments (I'm not consistent in the impls above :/), // and we are not going to include those for now. if (numInt <= 0) { return(false); } if (numInt > 0) { if (IsPointOn(i1) && s.IsPointOn(i1)) { return(true); } } if (numInt > 1) { if (IsPointOn(i2) && s.IsPointOn(i2)) { return(true); } } return(false); }
private static bool PointOnArcSegment(Vector3D p, Segment seg) { double maxAngle = seg.Angle; Vector3D v1 = seg.P1 - seg.Center; Vector3D v2 = p - seg.Center; Debug.Assert(Tolerance.Equal(v1.Abs(), v2.Abs())); double angle = seg.Clockwise ? Euclidean2D.AngleToClock(v1, v2) : Euclidean2D.AngleToCounterClock(v1, v2); return(Tolerance.LessThanOrEqual(angle, maxAngle)); }
// Get the intersection points with a segment. // Returns null if the segment is an arc coincident with the circle (infinite number of intersection points). public Vector3D[] GetIntersectionPoints(Segment segment) { Vector3D p1, p2; int result; // Are we a line? if (this.IsLine) { if (SegmentType.Arc == segment.Type) { Circle tempCircle = segment.Circle; result = Euclidean2D.IntersectionLineCircle(this.P1, this.P2, tempCircle, out p1, out p2); } else { result = Euclidean2D.IntersectionLineLine(this.P1, this.P2, segment.P1, segment.P2, out p1); p2 = Vector3D.DneVector(); } } else { if (SegmentType.Arc == segment.Type) { Circle tempCircle = segment.Circle; result = Euclidean2D.IntersectionCircleCircle(tempCircle, this, out p1, out p2); } else { result = Euclidean2D.IntersectionLineCircle(segment.P1, segment.P2, this, out p1, out p2); } } if (-1 == result) { return(null); } List <Vector3D> ret = new List <Vector3D>(); if (result >= 1 && segment.IsPointOn(p1)) { ret.Add(p1); } if (result >= 2 && segment.IsPointOn(p2)) { ret.Add(p2); } return(ret.ToArray()); }
private static double StereoToEquidistant(double dist) { if (Infinity.IsInfinite(dist)) { return(1); } double dot = dist * dist; // X^2 + Y^2 + Z^2 double w = (dot - 1) / (dot + 1); double x = Math.Sqrt(1 - w * w); double r = Euclidean2D.AngleToCounterClock(new Vector3D(0, -1), new Vector3D(x, w)); return(r / Math.PI); }
/// <summary> /// Apply a transform to us. /// </summary> private void TransformInternal <T>(T transform) where T : ITransform { // NOTES: // Arcs can go to lines, and lines to arcs. // Rotations may reverse arc directions as well. // Arc centers can't be transformed directly. // NOTE: We must calc this before altering the endpoints. Vector3D mid = Midpoint; if (Infinity.IsInfinite(mid)) { mid = Infinity.IsInfinite(P1) ? P2 * Infinity.FiniteScale : P1 * Infinity.FiniteScale; } P1 = transform.Apply(P1); P2 = transform.Apply(P2); mid = transform.Apply(mid); // Can we make a circle out of the transformed points? Circle temp = new Circle(); if (!Infinity.IsInfinite(P1) && !Infinity.IsInfinite(P2) && !Infinity.IsInfinite(mid) && temp.From3Points(P1, mid, P2)) { Type = SegmentType.Arc; Center = temp.Center; // Work out the orientation of the arc. Vector3D t1 = P1 - Center; Vector3D t2 = mid - Center; Vector3D t3 = P2 - Center; double a1 = Euclidean2D.AngleToCounterClock(t2, t1); double a2 = Euclidean2D.AngleToCounterClock(t3, t1); Clockwise = a2 > a1; } else { // The circle construction fails if the points // are colinear (if the arc has been transformed into a line). Type = SegmentType.Line; // XXX - need to do something about this. // Turn into 2 segments? //if( isInfinite( mid ) ) // Actually the check should just be whether mid is between p1 and p2. } }
/// <summary> /// Maps a point in a rhombus to a point in the unit square, via a simple affine transformation. /// b1 and b2 are the two basis vectors of the rhombus. /// Does not currently check the input point. /// </summary> public static Vector3D MapRhombusToUnitSquare(Vector3D b1, Vector3D b2, Vector3D v) { double a = Euclidean2D.AngleToCounterClock(b1, new Vector3D(1, 0)); v.RotateXY(a); b1.RotateXY(a); b2.RotateXY(a); // Shear v.X -= b2.X * (v.Y / b2.Y); // Scale x and y. v.X /= b1.X; v.Y /= b2.Y; return(v); }
/// <summary> /// Normalize so P1 is closest point to origin, /// and direction vector is of unit length. /// </summary> public void NormalizeLine() { if (!this.IsLine) { return; } Vector3D d = P2 - P1; d.Normalize(); P1 = Euclidean2D.ProjectOntoLine(new Vector3D(), P1, P2); // ZZZ - Could probably do something more robust to choose proper direction. if (Tolerance.GreaterThanOrEqual(Euclidean2D.AngleToClock(d, new Vector3D(1, 0)), Math.PI)) { d *= -1; } P2 = P1 + d; }
/// <summary> /// Checks to see if a point is inside us, in a non-Euclidean sense. /// This works if we are inverted, and even if we are a line! /// (if we are a line, half of the plane is still "inside"). /// </summary> public bool IsPointInsideNE(Vector3D testPoint) { if (this.IsLine) { // We are inside if the test point is on the same side // as the non-Euclidean center. return(Euclidean2D.SameSideOfLine(P1, P2, testPoint, CenterNE)); } else { // Whether we are inside in the Euclidean sense. bool pointInside = false; if (!Infinity.IsInfinite(testPoint)) { pointInside = this.IsPointInside(testPoint); } // And in the Non-Euclidean sense. bool inverted = this.Inverted; return((!inverted && pointInside) || (inverted && !pointInside)); } }
/// <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); }
/// <summary> /// Reflects a point in a line defined by two points. /// </summary> public static Vector3D ReflectPointInLine(Vector3D input, Vector3D p1, Vector3D p2) { Vector3D p = Euclidean2D.ProjectOntoLine(input, p1, p2); return(input + (p - input) * 2); }