public Geometry Intersection(Edge edge) { // If no coplanar, cannot intersect if (!edge.IsCoplanarTo(this.Origin, this.Direction)) { return(null); } // Intersection can be an Edge or null if (this.Direction.IsParallelTo(edge.Direction)) { bool containsStart = this.Contains(edge.StartVertex); bool containsEnd = this.Contains(edge.EndVertex); if (!containsStart && !containsEnd) { return(null); } else if (containsStart) { return(Edge.ByStartVertexEndVertex(edge.StartVertex, this.Origin)); } else { return(Edge.ByStartVertexEndVertex(this.Origin, edge.EndVertex)); } } // No coincident nor same extremes var b = this.Direction; var a = edge.Direction; var c = Vector.ByTwoVertices(edge.StartVertex, this.Origin); var cxb = c.Cross(b); var axb = a.Cross(b); var dot = cxb.Dot(axb); double t = (dot) / Math.Pow(axb.Length, 2); if (t < 0 && !t.AlmostEqualTo(0)) { return(null); } if (t > 1 && !t.AlmostEqualTo(1)) { return(null); } var intersection = edge.StartVertex.Translate(edge.Direction.Scale(t)); return(this.Contains(intersection) ? intersection : null); }
/// <summary> /// TODO: To be reviewed /// Returns the orientation of vertices p1-p2-p3 /// 0 => Vertices are colinear /// 1 => Counterclock-wise, left turn /// -1 => Clock-wise, right turn /// </summary> /// <param name="p1"></param> /// <param name="p2"></param> /// <param name="p3"></param> /// <param name="plane"></param> /// <returns></returns> internal static int Orientation(Vertex p1, Vertex p2, Vertex p3, string plane = "xy") { // See https://www.geeksforgeeks.org/orientation-3-ordered-points/ // for details of below formula. double value = 0; switch (plane) { case "xy": value = (p2.X - p1.X) * (p3.Y - p2.Y) - (p2.Y - p1.Y) * (p3.X - p2.X); break; case "xz": value = (p2.X - p1.X) * (p3.Z - p2.Z) - (p2.Z - p1.Z) * (p3.X - p2.X); break; case "yz": value = (p2.Y - p1.Y) * (p3.Z - p2.Z) - (p2.Z - p1.Z) * (p3.Y - p2.Y); break; default: throw new Exception("Plane not defined"); } //Rounding due to floating point error. if (value.AlmostEqualTo(0)) { return(0); } //Points are colinear return((value > 0) ? 1 : -1); //Counter clock or clock wise }
public static List <Vertex> ConvexHull(List <Vertex> vertices) { var verticesCount = vertices.Count(); //If less than 3, cannot be created if (verticesCount < 3) { throw new ArgumentOutOfRangeException("vertices", verticesCount, "No ConvexHull can be created with less than 3 vertices"); } // TODO: Check if they are colinear //if (Vertex.Colinear(vertices)) //{ // throw new ArgumentException("Vertices are colinear", "vertices"); //} Vertex minVertex = Vertex.MinimumVertex(vertices); List <Vertex> sorted = Vertex.OrderByRadianAndDistance(vertices, minVertex); Stack <Vertex> stack = new Stack <Vertex>(); // Adding fist vertex to stack. stack.Push(minVertex); //First vertex on sorted should be the minVertex itself // so just skip it. for (int i = 1; i < verticesCount; i++) { int nextIndex = (i + 1) % verticesCount; var vertex = sorted[i]; var nextVertex = sorted[nextIndex]; double vertexAngle = Vertex.RadAngle(minVertex, vertex); double nextVertexAngle = Vertex.RadAngle(minVertex, nextVertex); // If the angle is same as next, skip as we'll only use the fartest // vertex with same angle. if (vertexAngle.AlmostEqualTo(nextVertexAngle)) { continue; } // If stack is still less than 3, keep adding. if (stack.Count < 3) { stack.Push(vertex); } else { // While it is not a left turn, remove top on stack while (Vertex.Orientation(GetNextToTop <Vertex>(stack), stack.Peek(), vertex) != 1) { stack.Pop(); } stack.Push(vertex); } } return(stack.Reverse().ToList()); }
public override IResultValue Evaluate() { IResultValue num1 = First.Evaluate(); IResultValue num2 = Second.Evaluate(); double d1 = (double)num1.ToDecimal(); double d2 = (double)num2.ToDecimal(); return(new ResultBoolean(!d1.AlmostEqualTo(d2))); }
/// <summary> /// Determins if a Vertex lies on the left-hand side of an edge on the XY plane. /// 1 => for vertex left of the edge /// 0 => for vertex on the edge /// -1 => for vertex right of the edege /// </summary> /// <param name="edge"></param> /// <param name="vertex"></param> /// <returns> /// /// </returns> public int IsLeftFrom(Edge edge) { double value = (edge.EndVertex.X - edge.StartVertex.X) * (this.Y - edge.StartVertex.Y) - (edge.EndVertex.Y - edge.StartVertex.Y) * (this.X - edge.StartVertex.X); if (value.AlmostEqualTo(0)) { return(0); } return(value > 0 ? 1 : -1); }
/// <summary> /// TODO: To be reviewed /// </summary> /// <param name="centre"></param> /// <param name="vertex"></param> /// <returns></returns> public static double RadAngle(Vertex centre, Vertex vertex) { //Rad angles http://math.rice.edu/~pcmi/sphere/drg_txt.html double dx = vertex.X - centre.X; double dy = vertex.Y - centre.Y; bool onYAxis = dx.AlmostEqualTo(0); bool onXAxis = dy.AlmostEqualTo(0); //TODO: Implement Z angle? that would becom UV coordinates. //double dz = vertex.point.Z - centre.point.Z; if (onYAxis && onXAxis) { return(0); } if (onYAxis) // both Vertices on Y axis { if (dy < 0) //vertex below X axis { return(Math.PI * 3 / 2); } else//vertex above X Axis { return(Math.PI / 2); } } if (onXAxis) // both Vertices on X Axis { if (dx < 0) // vertex on the left of Y axis { return(Math.PI); } else//vertex on the right of Y axis { return(0); } } if (dx < 0) { return(Math.PI + Math.Atan(dy / dx)); } if (dy < 0) { return(2 * Math.PI + Math.Atan(dy / dx)); } return(Math.Atan(dy / dx)); }
public string VolumeToSymbol(double d) { if (d.AlmostEqualTo(0)) { return("\uE198"); } if (d < 33.333333) { return("\uE993"); } if (d < 66.66666667) { return("\uE994"); } return("\uE995"); }
protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); if (!_oldScaleFactor.AlmostEqualTo(ScaleFactor)) { Image = ScaleImage(_originalImage); } if (!DesignMode && !ShowBorder) { return; } var pen = new Pen(Color.FromArgb(45, 68, 108), 1); var rectangle = new Rectangle(0, 0, Size.Width - 1, Size.Height - 1); e.Graphics.DrawRectangle(pen, rectangle); }
/// <summary> /// Checks if the given Vertex is contained on the Ray. /// </summary> /// <param name="vertex"></param> /// <returns></returns> public bool Contains(Vertex vertex) { // If the Vector from Ray's origin to the given // vertex is parallel to Ray's Direction, return true. var vector = Vector.ByTwoVertices(this.Origin, vertex); if (!this.Direction.IsParallelTo(vector)) { return(false); } // Need to check if point falls on the visible path of Ray. // Vertex = Origin + t * Direction; t = (V-O)/D // If t < 0, it doesn't double t = Double.PositiveInfinity; if (this.Direction.X != 0) { t = (vertex.X - this.Origin.X) / this.Direction.X; } else if (this.Direction.Y != 0) { t = (vertex.Y - this.Origin.Y) / this.Direction.Y; } else if (this.Direction.Z != 0) { t = (vertex.Z - this.Origin.Z) / this.Direction.Z; } else { throw new Exception("Something when wrong, contact Author"); } return(t > 0 && !t.AlmostEqualTo(0)); }
/// <summary> /// Checks if the polygon is convex /// </summary> /// <returns></returns> public bool IsConvex() { //https://math.stackexchange.com/questions/1743995/determine-whether-a-polygon-is-convex-based-on-its-vertices/1745427#1745427 double wSign = 0; // First non-zero orientation int xSign = 0; int xFirstSign = 0; int xFlips = 0; int ySign = 0; int yFirstSign = 0; int yFlips = 0; int vertexCount = this.Vertices.Count(); Vertex current = this.Vertices[vertexCount - 2]; Vertex next = this.Vertices[vertexCount - 1]; for (int i = 0; i < vertexCount; i++) { var prev = current; current = next; next = this.Vertices[i]; // Previous edge vector double prevX = current.X - prev.X; double prevY = current.Y - prev.Y; // Next edge vector double nextX = next.X - current.X; double nextY = next.Y - current.Y; if (!nextX.AlmostEqualTo(0)) { if (nextX > 0) { if (xSign == 0) { xFirstSign = 1; } else if (xSign < 0) { xFlips += 1; } xSign = 1; } else // nextX < 0 { if (xSign == 0) { xFirstSign = -1; } else if (xSign > 0) { xFlips += 1; } xSign = -1; } } if (xFlips > 2) { return(false); } if (!nextY.AlmostEqualTo(0)) { if (nextY > 0) { if (ySign == 0) { yFirstSign = 1; } else if (ySign < 0) { yFlips += 1; } ySign = 1; } else // nextY < 0 { if (ySign == 0) { yFirstSign = -1; } else if (ySign > 0) { yFlips += 1; } ySign = -1; } } if (yFlips > 2) { return(false); } // Find out the orientation of this pair of edges // and ensure ir does not differ from previous ones double w = prevX * nextY - nextX * prevY; bool wIsZero = w.AlmostEqualTo(0); bool wSignIsZero = wSign.AlmostEqualTo(0); if (wSignIsZero && !wIsZero) { wSign = w; } else if (!wSignIsZero && !wIsZero) { if ((wSign > 0 && w < 0) || (wSign > 0 && w < 0)) { return(false); } } } // Final/wraparound sign flips if (xSign != 0 && xFirstSign != 0 && xSign != xFirstSign) { xFlips += 1; } if (ySign != 0 && yFirstSign != 0 && ySign != yFirstSign) { yFlips += 1; } // Concave polygons have two sign flips along each axis if (xFlips != 2 || yFlips != 2) { return(false); } // This is a convex polygon return(true); }
public void AlmostEqualTo(double num1, double num2, double tolerance, bool expected) { Assert.Equal(expected, num1.AlmostEqualTo(num2, tolerance)); }
public static bool IsLowerEqualThan(this double value1, double value2, double precision = 0.000001) { return(value1 < value2 || value1.AlmostEqualTo(value2, precision)); }
public GeometryBase Intersection(Edge other) { // http://mathworld.wolfram.com/Line-LineIntersection.html if (!this.BoundingBox.Intersects(other.BoundingBox)) { return(null); } if (!this.IsCoplanarTo(other)) { return(null); } if (this.Equals(other)) { return(this); } // Issues if same polygon id??? var a = this.Direction; var b = other.Direction; if (a.IsParallelTo(b)) { // Fully contains the test edge if (other.StartVertex.OnEdge(this) && other.EndVertex.OnEdge(this)) { return(other); } // Is fully contained by test edge else if (this.StartVertex.OnEdge(other) && this.EndVertex.OnEdge(other)) { return(this); } // Not fully inclusive but overlapping else if (this.StartVertex.OnEdge(other) || this.EndVertex.OnEdge(other)) { Vertex[] vertices = new Vertex[4] { this.StartVertex, this.EndVertex, other.StartVertex, other.EndVertex }; var sorted = vertices.OrderBy(v => v.Y).ThenBy(v => v.X).ThenBy(v => v.Z).ToList(); return(Edge.ByStartVertexEndVertex(sorted[1], sorted[2])); } // Not intersecting else { return(null); } } // No parallels but intersecting on one of the extreme vertices if (other.Contains(this.StartVertex)) { return(this.StartVertex); } else if (other.Contains(this.EndVertex)) { return(this.EndVertex); } // No coincident nor same extremes var c = Vector.ByTwoVertices(this.StartVertex, other.StartVertex); var cxb = c.Cross(b); var axb = a.Cross(b); var dot = cxb.Dot(axb); // If dot == 0 it means that other edge contains at least a vertex from this edge // and they are parallel or perpendicular. Cannot be parallel as that was tested before. // It might also mean they don't intersect but the would if extending the projections double s = (dot) / Math.Pow(axb.Length, 2); if (s.AlmostEqualTo(0)) { if (this.StartVertex.OnEdge(other)) { return(this.StartVertex); } else if (this.EndVertex.OnEdge(other)) { return(this.EndVertex); } else if (other.StartVertex.OnEdge(this)) { return(other.StartVertex); } else if (other.EndVertex.OnEdge(this)) { return(other.EndVertex); } else { return(null); } } // s > 1, means that "intersection" vertex is not on either edge // s == NaN means they are parallels so never intersect if (s < 0 || s > 1 || Double.IsNaN(s)) { return(null); } Vertex intersection = this.StartVertex.Translate(a.Scale(s)); if (intersection.Equals(other.StartVertex)) { return(other.StartVertex); } if (intersection.Equals(other.EndVertex)) { return(other.EndVertex); } if (!intersection.OnEdge(other)) { return(null); } return(intersection); }