/// <summary> /// Reflect a point in us. /// </summary> public Vector3D ReflectPoint(Vector3D p) { if (IsPlane) { // We used to call ProjectOntoPlane, but optimized it away. // This is faster because we already know our normal is normalized, // and it avoids some extra Vector3D operations. double dist = Euclidean3D.DistancePointPlane(this.Normal, this.Offset, p); Vector3D offset = this.Normal * dist * -2; return(p + offset); } else { if (p == Center) { return(Infinity.InfinityVector); } if (Infinity.IsInfinite(p)) { return(Center); } Vector3D v = p - Center; double d = v.Abs(); v.Normalize(); return(Center + v * (Radius * Radius / d)); } }
public static double GetNormalizedCircumRadius(int p, double q) { double hypot = GetTriangleHypotenuse(p, q); switch (Geometry2D.GetGeometry(p, q)) { case Geometry.Spherical: return(Spherical2D.s2eNorm(hypot) * DiskRadius); case Geometry.Euclidean: return(EuclideanHypotenuse); case Geometry.Hyperbolic: { if (Infinity.IsInfinite(hypot)) { return(DiskRadius); } return(DonHatch.h2eNorm(hypot) * DiskRadius); } } Debug.Assert(false); return(1); }
/// <summary> /// Reflect a point in us. /// </summary> public Vector3D ReflectPoint(Vector3D p) { if (IsPlane) { //Debug.Assert( !Infinity.IsInfinite( p ) ); Vector3D v = Euclidean3D.ProjectOntoPlane(this.Normal, this.Offset, p); v = p + (v - p) * 2; return(v); } else { if (p == Center) { return(Infinity.InfinityVector); } if (Infinity.IsInfinite(p)) { return(Center); } Vector3D v = p - Center; double d = v.Abs(); v.Normalize(); return(Center + v * (Radius * Radius / d)); } }
/// <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 static Vector3D InfinitySafe(Vector3D input) { if (Infinity.IsInfinite(input)) { return(Infinity.LargeFiniteVector); } return(input); }
public static Vector3D PlaneToSphereSafe(Vector3D planePoint) { if (Infinity.IsInfinite(planePoint)) { return(new Vector3D(0, 0, 1)); } return(PlaneToSphere(planePoint)); }
/// <summary> /// https://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection /// </summary> private static double StereoToEqualArea(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 r = Math.Sqrt(2 * (1 + w)); return(r / 2); }
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); }
private bool NewTetAfterReflect(Cell cell, Segment s, HashSet <Vector3D> completed) { foreach (Tile tile in cell.Tiles) { CircleNE newVertexCircle = tile.VertexCircle.Clone(); newVertexCircle.Reflect(s); if (completed.Contains(Infinity.InfinitySafe(newVertexCircle.CenterNE))) { return(false); } } return(true); }
public void Reflect(Segment seg) { foreach (Tile tile in this.Tiles) { tile.Reflect(seg); if (Infinity.IsInfinite(tile.Boundary.Center)) { tile.Boundary.Center = Infinity.LargeFiniteVector; } if (Infinity.IsInfinite(tile.Drawn.Center)) { tile.Drawn.Center = Infinity.LargeFiniteVector; } } }
/// <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. } }
public static Vector3D R3toS3(Vector3D p) { if (Infinity.IsInfinite(p)) { return(new Vector3D(0, 0, 0, 1)); } p.W = 0; double dot = p.Dot(p); // X^2 + Y^2 + Z^2 return(new Vector3D( 2 * p.X / (dot + 1), 2 * p.Y / (dot + 1), 2 * p.Z / (dot + 1), (dot - 1) / (dot + 1))); }
private static double StereoToEqualVolume(double dist) { if (Infinity.IsInfinite(dist)) { return(1); } double dot = dist * dist; // X^2 + Y^2 + Z^2 double w = (dot - 1) / (dot + 1); w = -w; // Because I derived formula from north pole. double t = Math.PI / 2 - w * Math.Sqrt(1 - w * w) - Math.Asin(w); double r = Math.Pow(t * 3 / 2, 1.0 / 3); return(r); }
private static double Pi_hpq(int p, int q) { double pi = Math.PI; double pip = PiOverNSafe(p); double piq = PiOverNSafe(q); double temp = Math.Pow(Math.Cos(pip), 2) + Math.Pow(Math.Cos(piq), 2); double hab = pi / Math.Acos(Math.Sqrt(temp)); // Infinity safe. double pi_hpq = pi / hab; if (Infinity.IsInfinite(hab)) { pi_hpq = 0; } return(pi_hpq); }
public Vector3D[] CalcEdgePoints(double arcResolution, int minSegs, bool checkForInfinities) { List <Vector3D> points = new List <Vector3D>(); for (int i = 0; i < NumSides; i++) { Segment s = Segments[i]; // First point. // ZZZ - getting lazy //Debug.Assert( ! (isInfinite( s.m_p1 ) && isInfinite( s.m_p2 )) ); Vector3D p1 = checkForInfinities && Infinity.IsInfinite(s.P1) ? s.P2 * Infinity.FiniteScale : s.P1; points.Add(p1); // For arcs, add in a bunch of extra points. if (SegmentType.Arc == s.Type) { double maxAngle = s.Angle; Vector3D vs = s.P1 - s.Center; int numSegments = (int)(maxAngle / (arcResolution)); if (numSegments < minSegs) { numSegments = minSegs; } double angle = maxAngle / numSegments; for (int j = 1; j < numSegments; j++) { vs.RotateXY(s.Clockwise ? -angle : angle); points.Add(vs + s.Center); } } // Last point. Vector3D p2 = checkForInfinities && Infinity.IsInfinite(s.P2) ? s.P1 * Infinity.FiniteScale : s.P2; points.Add(p2); } return(points.ToArray()); }
private void ReflectRecursive(int level, List <Cell> cells, HashSet <Vector3D> completed) { level++; // Breadth first recursion. if (0 == cells.Count) { return; } List <Cell> reflected = new List <Cell>(); foreach (Cell cell in cells) { foreach (Segment seg in cell.Segments) { // Are we done? if (m_cells.Count >= m_cellCount) { return; } if (!NewTetAfterReflect(cell, seg, completed)) { continue; } Cell newBase = cell.Clone(); newBase.Level = level; newBase.Reflect(seg); m_cells.Add(newBase); reflected.Add(newBase); foreach (Tile tile in newBase.Tiles) { completed.Add(Infinity.InfinitySafe(tile.VertexCircle.CenterNE)); } } } ReflectRecursive(level, reflected, completed); }
// ZZZ - I wonder if we want to do normalization of lines before comparing. public bool Equals(CircleNE c1, CircleNE c2) { bool radiusEqual = Tolerance.Equal(c1.Radius, c2.Radius) || (Infinity.IsInfinite(c1.Radius) && Infinity.IsInfinite(c2.Radius)); if (c1.IsLine) { return(c1.P1 == c2.P1 && c1.P2 == c2.P2 && radiusEqual); } else { return (c1.Center == c2.Center && c1.CenterNE == c2.CenterNE && radiusEqual); } }
public Circle3D(Vector3D t1, Vector3D t2, Vector3D t3) { Vector3D center; double radius; From3Points(t1, t2, t3, out center, out radius); Center = center; Radius = radius; Vector3D normal = (t2 - t1).Cross(t3 - t1); if (Infinity.IsInfinite(Radius)) { Center = Vector3D.DneVector(); normal = !t1.IsOrigin ? t1 : !t2.IsOrigin ? t2 : t3; // Hacky rep of line. } normal.Normalize(); Normal = normal; }
/// <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> /// Transform a mesh in the Poincare model to Dini's surface. /// </summary> public static Mesh Dini(Mesh mesh) { System.Func <Vector3D, Vector3D> transform = v => { //v = DiskToUpper( v ); //v.Y = Math.Log( v.Y ); //if( v.Y < 1 || v.Y > 10 ) // return Infinity.InfinityVector; //if( v.X < -Math.PI || v.X > Math.PI ) //if( v.X < -3*Math.PI || v.X > 3*Math.PI ) // return Infinity.InfinityVector; //v.Y = Math.Log( v.Y ); //return v; return(Dini(v)); }; Mesh result = new Mesh(); for (int i = 0; i < mesh.Triangles.Count; i++) { Vector3D a = transform(mesh.Triangles[i].a); Vector3D b = transform(mesh.Triangles[i].b); Vector3D c = transform(mesh.Triangles[i].c); if (Infinity.IsInfinite(a) || Infinity.IsInfinite(b) || Infinity.IsInfinite(c)) { continue; } result.Triangles.Add(new Mesh.Triangle(a, b, c)); } return(result); }
/// <summary> /// Reflect ourselves about another sphere. /// </summary> public void Reflect(Sphere sphere) { // An interior point used to calculate whether we get inverted. Vector3D interiorPoint; if (IsPlane) { Debug.Assert(!this.Normal.IsOrigin); interiorPoint = this.Offset - this.Normal; } else { // We don't want it to be the center, because that will reflect to infinity. interiorPoint = (this.Center + new Vector3D(this.Radius / 2, 0)); } if (Invert) { interiorPoint = ReflectPoint(interiorPoint); } Debug.Assert(IsPointInside(interiorPoint)); interiorPoint = sphere.ReflectPoint(interiorPoint); Debug.Assert(!interiorPoint.DNE); if (this.Equals(sphere)) { if (IsPlane) { //this.Center = -this.Center; // Same as inverting, but we need to do it this way because of Pov-Ray this.Invert = !this.Invert; } else { this.Invert = !this.Invert; } Debug.Assert(this.IsPointInside(interiorPoint)); return; } // Both planes? if (IsPlane && sphere.IsPlane) { // XXX - not general, but I know the planes I'll be dealing with go through the origin. //if( !sphere.Offset.IsOrigin ) // throw new System.NotImplementedException(); /*Vector3D p1 = this.Normal.Cross( sphere.Normal ); * if( !p1.Normalize() ) * { * this.Center *= -1; * return; * } * * Vector3D p2 = p1.Cross( this.Normal ); * p2.Normalize(); * p1 = sphere.ReflectPoint( p1 ); * p2 = sphere.ReflectPoint( p2 ); * Vector3D newNormal = p2.Cross( p1 ); * if( !newNormal.Normalize() ) * throw new System.Exception( "Reflection impl" ); * this.Center = newNormal;*/ // Reflect the normal relative to the plane (conjugate with sphere.Offset). Vector3D newNormal = this.Normal + sphere.Offset; newNormal = sphere.ReflectPoint(newNormal); newNormal -= sphere.Offset; newNormal.Normalize(); this.Center = newNormal; // Calc the new offset (so far we have considered planes through origin). this.Offset = sphere.ReflectPoint(this.Offset); //Debug.Assert( Offset.IsOrigin ); // XXX - should handle more generality. Debug.Assert(this.IsPointInside(interiorPoint)); return; } // We are a plane and reflecting in a sphere. if (IsPlane) { // Think of 2D case here (circle and line)... Vector3D projected = Euclidean3D.ProjectOntoPlane(this.Normal, this.Offset, sphere.Center); Vector3D p = sphere.ReflectPoint(projected); if (Infinity.IsInfinite(p)) { // This can happen if we go through sphere.Center. // This reflection does not change our orientation (does not invert us). return; } Center = sphere.Center + (p - sphere.Center) / 2; Radius = Center.Dist(sphere.Center); // Did this invert us? if (!this.IsPointInside(interiorPoint)) { Invert = !Invert; } return; } // Is mirror a plane? if (sphere.IsPlane) { Vector3D projected = Euclidean3D.ProjectOntoPlane(sphere.Normal, sphere.Offset, Center); Vector3D diff = Center - projected; Center -= 2 * diff; // Radius remains unchanged. // NOTE: This does not invert us. Debug.Assert(this.IsPointInside(interiorPoint)); return; } // // Now sphere reflecting in a sphere. // // Reflecting to a plane? if (IsPointOn(sphere.Center)) { // Concentric spheres? if (Center == sphere.Center) { throw new System.Exception(); } // Center Vector3D center = Center - sphere.Center; // Offset Vector3D direction = center; direction.Normalize(); Vector3D offset = direction * Radius * 2; offset = sphere.ReflectPoint(offset); // We are a line now. Center = center; //Offset = offset; // Not working?? Caused issues in old generation code for 435. Radius = double.PositiveInfinity; // Did this invert us? if (!this.IsPointInside(interiorPoint)) { this.Invert = !this.Invert; } Debug.Assert(this.IsPointInside(interiorPoint)); return; } // XXX - Could try to share code below with Circle class. // NOTE: We can't just reflect the center. // See http://mathworld.wolfram.com/Inversion.html double a = Radius; double k = sphere.Radius; Vector3D v = Center - sphere.Center; double s = k * k / (v.MagSquared() - a * a); Center = sphere.Center + v * s; Radius = Math.Abs(s) * a; // Did this invert us? if (!this.IsPointInside(interiorPoint)) { Invert = !Invert; } }