private void vertex_PositionChanged(IShape sender, bool followUpMove) { if (!followUpMove) { if (closingPolygon) { closingPolygon = false; checkBoundingBox(); base.OnChanged(false); return; } if (tolerance.Equal(sender.RootX, points[0].RootX) && tolerance.Equal(sender.RootY, points[0].RootY)) { // the first point was changed closingPolygon = true; points[points.Count - 1].moveTo(sender.RootX, sender.RootY, false, false); } else if (tolerance.Equal(sender.RootX, points[points.Count - 1].RootX) && tolerance.Equal(sender.RootY, points[points.Count - 1].RootY) ) { // the last point was changed closingPolygon = true; points[0].moveTo(sender.RootX, sender.RootY, false, false); } else { checkBoundingBox(); base.OnChanged(false); } } //else // closingPolygon = false; }
/// <summary> /// Returns a stereographically sphere representing us. /// </summary> public Sphere ToSphere() { // Equatorial sphere? if (Pole == new Vector3D(0, 0, 0, 1)) { return(new Sphere()); } // A plane? Vector3D poleR3 = Sterographic.S3toR3(Pole); if (Tolerance.Equal(poleR3.Abs(), 1)) { return(Sphere.Plane(poleR3)); } // Get 4 points on the sphere. Vector3D e1 = new Vector3D(1, 0, 0, 0); Vector3D e2 = new Vector3D(0, 1, 0, 0); Vector3D e3 = new Vector3D(0, 0, 1, 0); Vector3D e4 = new Vector3D(0, 0, 0, 1); System.Func <Vector3D, Vector3D> one = v => { v = Euclidean3D.ProjectOntoPlane(Pole, new Vector3D(), v); v.Normalize(); return(Sterographic.S3toR3(v)); }; return(Sphere.From4Points(one(e1), one(e2), one(e3), one(e4))); }
/// <summary> /// Comparator used for ordering point first by ascending X, then by ascending Y. /// </summary> /// <param name="other">The <see cref="Point"/> to compare.</param> /// <returns> /// 0 if the points are spatially equal or both empty; 1 if <paramref name="other"/> is empty or /// if this point has a greater <see cref="X"/> value or equal X values and a greater <see cref="Y"/> value; /// -1 if this point is empty or if <paramref name="other"/> has a greater <see cref="X"/> value or equal X values /// and a greater <see cref="Y"/> value. /// </returns> /// <exception cref="ArgumentNullException">Thrown if <paramref name="other"/> is null.</exception> public virtual Int32 CompareTo(Point other) { if (other == null) { throw new ArgumentNullException("other"); } if (Equals(other)) // This handles the case where both are empty. { return(0); } else if (IsEmpty) { return(-1); } else if (other.IsEmpty) { return(1); } else if (Tolerance.Less(X, other.X) || Tolerance.Equal(X, other.X) && Tolerance.Less(Y, other.Y)) { return(-1); } else if (Tolerance.Greater(X, other.X) || Tolerance.Equal(X, other.X) && Tolerance.Greater(Y, other.Y)) { return(1); } throw new InvalidOperationException("Points cannot be compared."); }
/// <summary> /// Calculates the point of our simplex that is at a vertex. /// </summary> public static Vector3D VertexPointBall(int p, int q, int r) { Geometry vertexGeometry = Geometry2D.GetGeometry(q, r); if (vertexGeometry == Geometry.Hyperbolic) { // Outside the ball, and not in a good way. Use the Klein version. //throw new System.NotImplementedException(); return(new Vector3D(0, 0, -1)); } // Get in UHS first. Sphere cellFacet = Mirrors(p, q, r, moveToBall: false).First(); double rSquared = Math.Pow(cellFacet.Radius, 2); double cSquared = Math.Pow(cellFacet.Center.Abs(), 2); Vector3D uhs; if (Tolerance.Equal(rSquared, cSquared)) // e.g. 363 { uhs = new Vector3D(); } else { double height = Math.Sqrt(rSquared - cSquared); uhs = new Vector3D(0, 0, height); } return(H3Models.UHSToBall(uhs)); }
// XXX - Do we only want to compare the surface, or also the orientation? // This just does the surface. public override bool Equals(object obj) { Sphere s = (Sphere)obj; if (IsPlane) { if (!s.IsPlane) { return(false); } Vector3D n1 = this.Normal; n1.Normalize(); Vector3D n2 = s.Normal; n2.Normalize(); return ((n1 == n2 || n1 == -n2) && Offset == s.Offset); } if (s.IsPlane) { return(false); } return (Tolerance.Equal(Radius, s.Radius) && Center == s.Center /*&& * Offset == s.Offset && * Invert == s.Invert*/); }
private static H3.Cell.Edge CalcFiber(Vector3D boundaryPointBall) { bool parallel = false; if (parallel) { Vector3D v2 = new Vector3D(0, 0, -1); // Really we can pick any boundary point. return(new H3.Cell.Edge(boundaryPointBall, v2)); } else { // If the point is above the real line, it will have been connected to z. // If below the real line, it will have been connected to -i // If on the real line, it's degenerate. Vector3D pUHS = H3Models.BallToUHS(boundaryPointBall); if (Tolerance.Equal(pUHS.Y, 0)) { return(null); } Vector3D z = Vector3D.FromComplex(ZParam); Vector3D minusi = new Vector3D(0, -1); double dilation = double.NaN; double translation = double.NaN; if (pUHS.Y > 0) { dilation = pUHS.Y / z.Y; z *= dilation; translation = pUHS.X - z.X; z.X += translation; if (H3Models.UHSToBall(z) != boundaryPointBall) { throw new System.Exception(); } minusi *= dilation; minusi.X += translation; return(new H3.Cell.Edge(H3Models.UHSToBall(minusi), boundaryPointBall)); //return null; } else { dilation = pUHS.Y / -1; minusi *= dilation; translation = pUHS.X - minusi.X; minusi.X += translation; if (H3Models.UHSToBall(minusi) != boundaryPointBall) { throw new System.Exception(); } z *= dilation; z.X += translation; return(new H3.Cell.Edge(boundaryPointBall, H3Models.UHSToBall(z))); //return null; } } }
/// <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); }
private static void HopfFibration(Tiling tiling) { int segDivisions = 10; int circleDivisions = 125; Shapeways mesh = new Shapeways(); HashSet <Vector3D> done = new HashSet <Vector3D>(); foreach (Tile tile in tiling.Tiles) { foreach (Segment seg in tile.Boundary.Segments) { if (done.Contains(seg.Midpoint)) { continue; } // Subdivide the segment, and project points to S2. Vector3D[] points = seg.Subdivide(segDivisions).Select(v => Spherical2D.PlaneToSphere(v)).ToArray(); foreach (Vector3D point in points) { // Get the hopf circle and add to mesh. // http://en.wikipedia.org/wiki/Hopf_fibration#Explicit_formulae double a = point.X; double b = point.Y; double c = point.Z; double factor = 1 / (Math.Sqrt(1 + c)); if (Tolerance.Equal(c, -1)) { continue; } List <Vector3D> circlePoints = new List <Vector3D>(); double angleInc = 2 * Math.PI / circleDivisions; double angle = 0; for (int i = 0; i <= circleDivisions; i++) { double sinTheta = Math.Sin(angle); double cosTheta = Math.Cos(angle); circlePoints.Add(new Vector3D( (1 + c) * cosTheta, a * sinTheta - b * cosTheta, a * cosTheta + b * sinTheta, (1 + c) * sinTheta)); angle += angleInc; } bool shrink = false; ProjectAndAddS3Points(mesh, circlePoints.ToArray(), shrink); } done.Add(seg.Midpoint); } } STL.SaveMeshToSTL(mesh.Mesh, @"D:\p4\R3\sample\out1.stl"); }
public static int IntersectionLineCircle(Vector3D lineP1, Vector3D lineP2, Circle circle, out Vector3D p1, out Vector3D p2) { p1 = new Vector3D(); p2 = new Vector3D(); // Distance from the circle center to the closest point on the line. double d = DistancePointLine(circle.Center, lineP1, lineP2); // No intersection points. double r = circle.Radius; if (d > r) { return(0); } // One intersection point. p1 = ProjectOntoLine(circle.Center, lineP1, lineP2); if (Tolerance.Equal(d, r)) { return(1); } // Two intersection points. // Special case when the line goes through the circle center, // because we can see numerical issues otherwise. // // I had further issues where my default tolerance was too strict for this check. // The line was close to going through the center and the second block was used, // so I had to loosen the tolerance used by my comparison macros. if (Tolerance.Zero(d)) { Vector3D line = lineP2 - lineP1; line.Normalize(); line *= r; p1 = circle.Center + line; p2 = circle.Center - line; } else { // To origin. p1 -= circle.Center; p1.Normalize(); p1 *= r; p2 = p1; double angle = Math.Acos(d / r); p1.RotateXY(angle); p2.RotateXY(-angle); // Back out. p1 += circle.Center; p2 += circle.Center; } return(2); }
/// <summary> /// Checks if a point is anywhere on a segment. /// </summary> public static bool PointOnSegment(Vector3D s1, Vector3D s2, Vector3D point) { // Look for a degenerate triangle. double d1 = (point - s1).MagSquared(); double d2 = (s2 - point).MagSquared(); double d3 = (s2 - s1).MagSquared(); return(Tolerance.Equal(d1 + d2, d3)); }
public bool IsPointOn(Vector3D test) { if (this.IsLine) { return(Tolerance.Zero(Euclidean2D.DistancePointLine(test, P1, P2))); } return(Tolerance.Equal((test - Center).Abs(), Radius)); }
private static bool PointOnLineSegment(Vector3D p, Segment seg) { // This will be so if the point and the segment ends represent // the vertices of a degenerate triangle. double d1 = (seg.P2 - seg.P1).Abs(); double d2 = (p - seg.P1).Abs(); double d3 = (seg.P2 - p).Abs(); return(Tolerance.Equal(d1, d2 + d3)); }
public bool IsPointOn(Vector3D test) { if (IsPlane) { double dist = Euclidean3D.DistancePointPlane(this.Normal, this.Offset, test); return(Tolerance.Zero(dist)); } return(Tolerance.Equal((test - Center).Abs(), Radius)); }
/// <summary> /// Outputs edges to an stl file. /// </summary> public void Output() { System.Func <Vector3D, Vector3D> p2s = v => Spherical2D.PlaneToSphere(v); System.Func <Vector3D, Vector3D> transform = v => H3Models.Ball.ApplyMobius(Mobius.Scale(3), v); double min = double.MaxValue; Cell cell = m_cells.First(); foreach (Vector3D v1 in cell.Tiles[0].Boundary.Vertices) { foreach (Vector3D v2 in cell.Tiles[1].Boundary.Vertices) { min = Math.Min(min, p2s(v1).Dist(p2s(v2))); } } // XXX - code below so ugly to read! Dictionary <TileVertex, TileVertex> vMap = new Dictionary <TileVertex, TileVertex>(); for (int tile_i = 0; tile_i < cell.Tiles.Length; tile_i++) { for (int tile_j = tile_i + 1; tile_j < cell.Tiles.Length; tile_j++) { for (int vertex_i = 0; vertex_i < cell.Tiles[tile_i].Boundary.Vertices.Length; vertex_i++) { for (int vertex_j = 0; vertex_j < cell.Tiles[tile_j].Boundary.Vertices.Length; vertex_j++) { Vector3D v1 = cell.Tiles[tile_i].Boundary.Vertices[vertex_i]; Vector3D v2 = cell.Tiles[tile_j].Boundary.Vertices[vertex_j]; if (Tolerance.Equal(p2s(v1).Dist(p2s(v2)), min)) { vMap[new TileVertex(tile_i, vertex_i)] = new TileVertex(tile_j, vertex_j); } } } } } HashSet <H3.Cell.Edge> edges = new HashSet <H3.Cell.Edge>(new H3.Cell.EdgeEqualityComparer()); foreach (Cell c in m_cells) { foreach (KeyValuePair <TileVertex, TileVertex> kvp in vMap) { Vector3D v1 = transform(p2s(c.Tiles[kvp.Key.Item1].Boundary.Vertices[kvp.Key.Item2])); Vector3D v2 = transform(p2s(c.Tiles[kvp.Value.Item1].Boundary.Vertices[kvp.Value.Item2])); edges.Add(new H3.Cell.Edge(v1, v2)); } } //H3.m_settings.ThinEdges = true; H3.SaveToFile("ultrainf", edges.ToArray(), finite: false); }
private int PolyWithAngle(double angle) { for (int i = 3; i < m_max; i++) { if (Tolerance.Equal(angle, Polygon.InteriorAngle(i))) { return(i); } } return(0); }
/// <summary> /// Calculates the two poles of a great circle defined by two points. /// </summary> public static void GreatCirclePole(Vector3D sphereCenter, Vector3D p1, Vector3D p2, out Vector3D pole1, out Vector3D pole2) { double sphereRadius = p1.Dist(sphereCenter); Debug.Assert(Tolerance.Equal(sphereRadius, p2.Dist(sphereCenter))); Vector3D v1 = p1 - sphereCenter; Vector3D v2 = p2 - sphereCenter; pole1 = v1.Cross(v2) + sphereCenter; pole2 = v2.Cross(v1) + sphereCenter; }
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)); }
public static Vector3D S3toR3(Vector3D p) { double w = p.W; if (Tolerance.Equal(w, 1)) { return(Vector3D.DneVector()); } return(new Vector3D( p.X / (1 - w), p.Y / (1 - w), p.Z / (1 - w))); }
// Returns the geometry induced by a polygon with p points, q meeting at a vertex. public static Geometry GetGeometry(double p, double q) { double test = 1.0 / p + 1.0 / q; if (test > 0.5) { return(Geometry.Spherical); } else if (Tolerance.Equal(test, 0.5)) { return(Geometry.Euclidean); } return(Geometry.Hyperbolic); }
private static void AvoidNorthPole(ref Vector3D v, Vector3D direction) { if (!Tolerance.Equal(v.W, 1)) { return; } Vector3D cutEnd = v - direction; double abs = cutEnd.Abs(); abs -= 0.35; cutEnd.Normalize(); cutEnd *= abs; v = direction + cutEnd; v.Normalize(); }
public static Geometry GetGeometry(int p, int q, int r) { double t1 = Math.Sin(PiOverNSafe(p)) * Math.Sin(PiOverNSafe(r)); double t2 = Math.Cos(PiOverNSafe(q)); if (Tolerance.Equal(t1, t2)) { return(Geometry.Euclidean); } if (Tolerance.GreaterThan(t1, t2)) { return(Geometry.Spherical); } return(Geometry.Hyperbolic); }
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); }
/// <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); }
/// <summary> /// Checks whether this instance is spatially equal to <paramref name="p"/>. /// </summary> /// <param name="p">Point to compare to</param> /// <returns>True if the points are either both empty or have the same coordinates, false otherwise.</returns> public virtual Boolean Equals(Point p) { if (ReferenceEquals(p, null)) { return(false); } if (IsEmpty && p.IsEmpty) { return(true); } if (IsEmpty || p.IsEmpty) { return(false); } return(Tolerance.Equal(p.X, X) && Tolerance.Equal(p.Y, Y)); }
// 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 static Circle3D GeodesicFrom2Points(Vector3D a, Vector3D b) { if (a == b || a == -b) { throw new System.Exception("Geodesic not unique"); } System.Func <Vector3D, Circle3D> lineFunc = p => { Circle3D circ = new Circle3D(); circ.Radius = double.PositiveInfinity; p.Normalize(); circ.Normal = p; // Hacky representation of a line. return(circ); }; if (a.IsOrigin || b.IsOrigin) { Vector3D p = a.IsOrigin ? b : a; return(lineFunc(p)); } double mag1 = a.Abs(), mag2 = b.Abs(); if (Tolerance.Equal(mag1, 1) && Tolerance.Equal(mag2, 1)) { return(new Circle3D(a, b, a * -1)); } // The antipode in S^3 of a or b will give us a 3rd point. Vector3D antipode = Sterographic.S3toR3(Sterographic.R3toS3(a) * -1); // If the antipode is also an an antipode in R3, the points are colinear // (or they are on the equatorial 2-sphere, but that is checked above). if (a == antipode * -1) { return(lineFunc(a)); } return(new Circle3D(a, b, antipode)); }
private static H3.Cell.Edge[] Cull120Cell(H3.Cell.Edge[] edges) { Func <Vector3D, bool> passes = new Func <Vector3D, bool>(v => { //return // Math.Pow( v.Z, 2 ) < 0.13 && // Math.Pow( v.W, 2 ) < 0.13; return(Tolerance.Equal(v.W, 0.0)); }); H3.Cell.Edge[] result = edges.Where(e => { Vector3D start = Sterographic.R3toS3(e.Start); Vector3D end = Sterographic.R3toS3(e.End); return(passes(start) && passes(end)); }).ToArray(); // Now cull valence-2 edges. //result = CullValence2Edges( result ); return(result); }
public static Vector3D[] OneHopfCircle(Vector3D s2Point, bool anti = false) { int circleDivisions = 125; // Get the hopf circle. // http://en.wikipedia.org/wiki/Hopf_fibration#Explicit_formulae double a = s2Point.X; double b = s2Point.Y; double c = s2Point.Z; double factor = 1 / (Math.Sqrt(1 + c)); if (Tolerance.Equal(c, -1)) { return new Vector3D[] {} } ; List <Vector3D> circlePoints = new List <Vector3D>(); double angleInc = 2 * Math.PI / circleDivisions; double angle = 0; for (int i = 0; i <= circleDivisions; i++) { double sinTheta = Math.Sin(angle); double cosTheta = Math.Cos(angle); Vector3D point = new Vector3D( (1 + c) * cosTheta, anti ? -a * sinTheta - b * cosTheta : a * sinTheta - b * cosTheta, anti ? a * cosTheta - b * sinTheta : a * cosTheta + b * sinTheta, (1 + c) * sinTheta); point.Normalize(); circlePoints.Add(point); angle += angleInc; } return(circlePoints.ToArray()); }
public bool Compare(Vector3D other, double threshold) { // NOTE: This is here because when the vector is infinite, it fails the tolerance checks below. if ((X == other.X) && (Y == other.Y) && (Z == other.Z) && W == other.W) { return(true); } if (this.DNE && other.DNE) { return(true); } if (this.DNE || other.DNE) { return(false); } return(Tolerance.Equal(X, other.X, threshold) && Tolerance.Equal(Y, other.Y, threshold) && Tolerance.Equal(Z, other.Z, threshold) && Tolerance.Equal(W, other.W, threshold)); }
public static Segment Arc(Vector3D start, Vector3D mid, Vector3D end) { Segment newSeg = new Segment(); newSeg.Type = SegmentType.Arc; newSeg.P1 = start; newSeg.P2 = end; Circle c = new Circle(); c.From3Points(start, mid, end); newSeg.Center = c.Center; // Obtain vectors from center point of circle (as if at the origin) Vector3D startOrigin = start - c.Center; Vector3D midOrigin = mid - c.Center; Vector3D endOrigin = end - c.Center; // Calculate the normal vector and angle to traverse. // ZZZ - worry about failure of cross product here. Vector3D normalVector = startOrigin.Cross(endOrigin); newSeg.Clockwise = normalVector.Z < 0; double angleToTraverse = startOrigin.AngleTo(endOrigin); // The normal vector might need to be reversed and the angleToTraverse adjusted. // This happens depending on the location of the midpoint relative to the start and end points. double compareAngle = startOrigin.AngleTo(midOrigin) + midOrigin.AngleTo(endOrigin); bool reverse = !Tolerance.Equal(angleToTraverse, compareAngle); if (reverse) { newSeg.Clockwise = !newSeg.Clockwise; } return(newSeg); }