public void AddPoint(Vertex p) { min.x = Math.Min(min.x, p.x); max.x = Math.Max(max.x, p.x); min.y = Math.Min(min.y, p.y); max.y = Math.Max(max.y, p.y); }
public static float Orient(Vertex a, Vertex b, Vertex c) { Matrix3 m = new Matrix3(a.x, a.y, 1, b.x, b.y, 1, c.x, c.y, 1); return m.Determinant(); }
// returns > 0 if p is inside the circle described by A,B,C < 0 outside, = 0 on the circle public static float InCircle(Vertex a, Vertex b, Vertex c, Vertex p) { Matrix4 m = new Matrix4(a.x, a.y, a.x * a.x + a.y * a.y, 1, b.x, b.y, b.x * b.x + b.y * b.y, 1, c.x, c.y, c.x * c.x + c.y * c.y, 1, p.x, p.y, p.x * p.x + p.y * p.y, 1); float det = m.Determinant(); if (Orient(a, b, c) > 0.0f) { return det; } else { return -det; } }
public void BoundingCircunference(out Vertex center, out float radius) { center = (max + min) * 0.5f; radius = Vertex.Distance(center, min); }
public void SplitTriangle(int triIdx, Vertex p, out int[] result) { int newVertex = vertices.Count; vertices.Add(p); Triangle oldTri = triangles[triIdx]; Debug.Assert(oldTri.valid); int A = oldTri.vertices[0]; int B = oldTri.vertices[1]; int C = oldTri.vertices[2]; Debug.Assert(A != B && A != C && A != newVertex && B != C && B != newVertex && C != newVertex); RemoveTriangle(triIdx); result = new int[3]; result[0] = CreateTriangle(A, B, newVertex); result[1] = CreateTriangle(B, C, newVertex); result[2] = CreateTriangle(C, A, newVertex); #if DEBUG Debug.Assert(CheckConsistency()); #endif }
public void SplitEdge(int edgeIndex, Vertex p, out int[] result) { Edge edge = edges[edgeIndex]; float distance; Debug.Assert(PointOnSegment(vertices[edge.vertices[0]], vertices[edge.vertices[1]], p, POINT_ON_SEGMENT_DISTANCE_EPSILON, POINT_ON_SEGMENT_PARAMETRIC_EPSILON, out distance)); /* B = edge.v[0] /|\ / | \ T1 = A,B,C T2 = D,C,B / | \ / | \ T1' = A,B,P T2' = D,C,P T3' = A,P,C T4' = B,D,P / | \ A ---- P ---- D \ | / \ | / \ | / \ | / \|/ C = edge.v[1] */ result = new int[4]; for (int idx = 0; idx < 4; idx++) { result[idx] = -1; } int triIdx1 = edge.triangles[0]; int triIdx2 = edge.triangles[1]; int tri1edge = triIdx1 >= 0 ? triangles[triIdx1].LocalEdgeIndex(edgeIndex) : -1; Debug.Assert(triIdx1 < 0 || tri1edge >= 0); int tri2edge = triIdx2 >= 0 ? triangles[triIdx2].LocalEdgeIndex(edgeIndex) : -1; Debug.Assert(triIdx2 < 0 || tri2edge >= 0); int A = triIdx1 >= 0 ? VertexOutOfTriEdge(triIdx1, tri1edge) : -1; Debug.Assert(triIdx1 < 0 || A >= 0); int D = triIdx2 >= 0 ? VertexOutOfTriEdge(triIdx2, tri2edge) : -1; Debug.Assert(triIdx2 < 0 || D >= 0); if (A < 0 || D < 0) return; int B = edge.vertices[0]; int C = edge.vertices[1]; Debug.Assert(B >= 0 && C >= 0); if (B < 0 || C < 0) return; Debug.Assert(triangles[triIdx1].Contains(A, B, C)); Debug.Assert(triangles[triIdx2].Contains(C, B, D)); if (triIdx1 >= 0) RemoveTriangle(triIdx1); if (triIdx2 >= 0) RemoveTriangle(triIdx2); Debug.Assert(edge.triangles[0] < 0 && edge.triangles[1] < 0); vertices.Add(p); int newVertex = vertices.Count - 1; int index = 0; if (A >= 0 && B >= 0) { result[index++] = CreateTriangle(A, B, newVertex); } if (D >= 0 && C >= 0) { result[index++] = CreateTriangle(D, C, newVertex); } if (A >= 0 && C >= 0) { result[index++] = CreateTriangle(A, newVertex, C); } if (B >= 0 && D >= 0) { result[index++] = CreateTriangle(B, D, newVertex); } #if DEBUG Debug.Assert(CheckConsistency()); #endif }
public static bool SegmentIntersect(Vertex A, Vertex B, Vertex C, Vertex D) { Vertex p; return SegmentIntersect(A, B, C, D, out p); }
public bool CircumCircle(List<Vertex> verts, out Vertex center, out float radius) { // Calculate the circle that passes through 3 coplanar points // http://mathworld.wolfram.com/Circle.html Vertex p0 = verts[vertices[0]]; Vertex p1 = verts[vertices[1]]; Vertex p2 = verts[vertices[2]]; Matrix3 ma = new Matrix3(p0.x, p0.y, 1, p1.x, p1.y, 1, p2.x, p2.y, 1); float a = ma.Determinant(); if (Math.Abs(a) < 1e-5f) { center = new Vertex(0, 0); radius = 0; return false; } Matrix3 md = new Matrix3(p0.x * p0.x + p0.y * p0.y, p0.y, 1, p1.x * p1.x + p1.y * p1.y, p1.y, 1, p2.x * p2.x + p2.y * p2.y, p2.y, 1); float d = -md.Determinant(); Matrix3 me = new Matrix3(p0.x * p0.x + p0.y * p0.y, p0.x, 1, p1.x * p1.x + p1.y * p1.y, p1.x, 1, p2.x * p2.x + p2.y * p2.y, p2.x, 1); float e = me.Determinant(); Matrix3 mf = new Matrix3(p0.x * p0.x + p0.y * p0.y, p0.x, p0.y, p1.x * p1.x + p1.y * p1.y, p1.x, p1.y, p2.x * p2.x + p2.y * p2.y, p2.x, p2.y); float f = -mf.Determinant(); center = new Vertex(-d / (2 * a), -e / (2 * a)); radius = (float)Math.Sqrt((d * d + e * e) / (4 * a * a) - f / a); return true; }
public void ToImage(Image img, Adjacency adjacency) { SolidBrush vertexColor = new SolidBrush(Color.Green); Pen boundaryColor = new Pen(Color.Blue); Pen cellColor = new Pen(Color.Gray); Graphics g = Graphics.FromImage(img); g.Clear(Color.White); for (int e = 0; e < edges.Count; e++) { Vertex v0 = vertices[ edges[e].v[0] ]; Vertex v1 = vertices[ edges[e].v[1] ]; g.DrawLine(boundaryColor, v0.x * img.Width, v0.y * img.Height, v1.x * img.Width, v1.y * img.Height); Vertex cellA = adjacency.vertices[edges[e].cell[0]]; Vertex cellB = adjacency.vertices[edges[e].cell[1]]; Vertex centerV = new Vertex((v0.x + v1.x) * 0.5f, (v0.y + v1.y) * 0.5f); g.DrawLine(cellColor, centerV.x * img.Width, centerV.y * img.Height, cellA.x * img.Width, cellA.y * img.Height); g.DrawLine(cellColor, centerV.x * img.Width, centerV.y * img.Height, cellB.x * img.Width, cellB.y * img.Height); } for (int v = 0; v < vertices.Count; v++) { const float r = 3; g.FillEllipse(vertexColor, new RectangleF(vertices[v].x * img.Width - r, vertices[v].y * img.Height - r, 2 * r, 2 * r)); } }
public static bool PointInPolygon(Vertex p, List<Vertex> polygon) { if ( polygon.Count < 4 ) return false; // idx 0 and n are the same, so we need 2 extra points minimum // polygon is considered a closed shape made of edges linking vi and vi+1 Debug.Assert(polygon[0] == polygon[polygon.Count - 1]); // trace an "infinite" line from the source point and count the number // of intersections with the polygon edges. Even number = the point // is outside, odd number if it's inside. // We will use a horizontal line, but to avoid intersections with the // VERTICES on any segment, we'll sort the Y coordinates of every vertex // and find a gap Vertex rectEndPoint = null; List<float> y = new List<float>(); for (int i = 0; i < polygon.Count - 1; i++) { y.Add(polygon[i].y); } y.Sort(); if (p.y <= y[0] || p.y >= y[y.Count - 1]) return false; int index = y.BinarySearch(p.y); if ( index < 0 ) index = ~index; // if not found, index will be <0 with the closest greater number in bitwise complement // we've already ruled out the cases where p.y < all values and p.y > all values, so index must be within 1..n-1 float approxY = (y[index-1] + y[index]) * 0.5f; rectEndPoint = new Vertex( p.x + 10000.0f, //make it a big number, but not as much as float.MaxValue to avoid numerical issues approxY); // count intersections int intersections = 0; for (int i = 0; i < polygon.Count-1; i++) { if (SegmentIntersect(polygon[i], polygon[i + 1], p, rectEndPoint)) intersections++; } return intersections > 0 && intersections % 2 != 0; }
public static Vertex Normalize(Vertex v) { float f = v.Length(); return new Vertex(v.x / f, v.y / f); }
public static float Dot(Vertex a, Vertex b) { return a.x * b.x + a.y * b.y; }
public static float Distance(Vertex a, Vertex b) { return (float)Math.Sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); }
static float Signed2DTriangleArea(Vertex A, Vertex B, Vertex C) { return (A.x - C.x) * (B.y - C.y) - (A.y - C.y) * (B.x - C.x); }
// returns the triangle index of the triangle containing p public int PointInTriangle(Vertex p) { for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; if (!t.valid) { continue; } bool hit = false; // use barycentric coordinates to determine whether the point is inside the triangle // http://steve.hollasch.net/cgindex/math/barycentric.html Vertex v0 = vertices[t.vertices[0]]; Vertex v1 = vertices[t.vertices[1]]; Vertex v2 = vertices[t.vertices[2]]; float b0 = (v1.x - v0.x) * (v2.y - v0.y) - (v2.x - v0.x) * (v1.y - v0.y); if (b0 != 0) { float b1 = ((v1.x - p.x) * (v2.y - p.y) - (v2.x - p.x) * (v1.y - p.y)) / b0; float b2 = ((v2.x - p.x) * (v0.y - p.y) - (v0.x - p.x) * (v2.y - p.y)) / b0; float b3 = ((v0.x - p.x) * (v1.y - p.y) - (v1.x - p.x) * (v0.y - p.y)) / b0; hit = b1 >= 0 && b2 >= 0 && b3 >= 0; } if (hit) { return i; } } return -1; }
bool PointOnSegment(Vertex A, Vertex B, Vertex P, float distanceEpsilon, float parametricEpsilon, out float pointSegmentDistance) { // Determines whether P lies within the segment A-B float segmentLength = Vertex.Distance(A, B); float tangentABdist = Vertex.Dot(Vertex.Normalize(B - A), Vertex.Normalize(P - A)) * (P - A).Length(); float u = tangentABdist / segmentLength; if (u < parametricEpsilon || u > 1.0 - parametricEpsilon) { pointSegmentDistance = float.MaxValue; return false; } Vertex isect = A + (B - A) * u; float dist = (P - isect).Length(); pointSegmentDistance = dist; if (dist > distanceEpsilon * segmentLength) { return false; } return true; }
public int PointInTriangleEdge(Vertex p, int t) { Triangle triangle = triangles[t]; Debug.Assert(triangle.valid); int closestEdge = -1; float closestDistance = float.MaxValue; for (int i = 0; i < 3; i++) { Debug.Assert(triangle.edges[i] != Int32.MaxValue); int edgeIdx = Math.Abs(triangle.edges[i]) - 1; Edge edge = edges[edgeIdx]; float distance; if (PointOnSegment(vertices[edge.vertices[0]], vertices[edge.vertices[1]], p, POINT_ON_SEGMENT_DISTANCE_EPSILON, POINT_ON_SEGMENT_PARAMETRIC_EPSILON, out distance)) { if (distance < closestDistance) { closestDistance = distance; closestEdge = edgeIdx; } } } return closestEdge; }
public float InsideCircumcircle(Vertex p, List<Vertex> verts) { Vertex A = verts[vertices[0]]; Vertex B = verts[vertices[1]]; Vertex C = verts[vertices[2]]; float det = Predicates.InCircle(A, B, C, p); return det; }
public Bounds2D() { min = new Vertex(float.MaxValue, float.MaxValue); max = new Vertex(float.MinValue, float.MinValue); }
private static void Delaunay2DCreateSuperTriangle(List<Vertex> vertices, Adjacency adjacency, out int stIdx1, out int stIdx2, out int stIdx3) { // Calculate super triangle Bounds2D bounds = new Bounds2D(); for (int i = 0; i < vertices.Count; i++) { bounds.AddPoint(vertices[i]); } Vertex center; float radius; bounds.BoundingCircunference(out center, out radius); Vertex st1 = new Vertex(); Vertex st2 = new Vertex(); Vertex st3 = new Vertex(); const float DEG2RAD = (float)Math.PI / 180.0f; st1.x = center.x + 2.0f * radius * (float)Math.Cos(0.0f * DEG2RAD); st1.y = center.y + 2.0f * radius * (float)Math.Sin(0.0f * DEG2RAD); st2.x = center.x + 2.0f * radius * (float)Math.Cos(120.0f * DEG2RAD); st2.y = center.y + 2.0f * radius * (float)Math.Sin(120.0f * DEG2RAD); st3.x = center.x + 2.0f * radius * (float)Math.Cos(240.0f * DEG2RAD); st3.y = center.y + 2.0f * radius * (float)Math.Sin(240.0f * DEG2RAD); adjacency.vertices.Add(st1); adjacency.vertices.Add(st2); adjacency.vertices.Add(st3); stIdx1 = adjacency.vertices.Count - 3; stIdx2 = adjacency.vertices.Count - 2; stIdx3 = adjacency.vertices.Count - 1; adjacency.CreateTriangle(stIdx1, stIdx2, stIdx3); }
public static bool SegmentIntersect(Vertex A, Vertex B, Vertex C, Vertex D, out Vertex p ) { // using triangle areas (Real Time Collision Detection book) float a1 = Signed2DTriangleArea(A, B, D); // compute winding of ABD [ + or - ] float a2 = Signed2DTriangleArea(A, B, C); // to intersect, must have sign opposite of a1 // if c and d are on different sides of AB, areas have different signs if (Math.Abs(a1) > 1e-3f && Math.Abs(a2) > 1e-3f && a1 * a2 < 0.0f) { // Compute signs of a and b with respect to segment cd float a3 = Signed2DTriangleArea(C, D, A); // Compute winding of cda [ + or - ] // since area is ant a1 - a2 = a3 - a4, or a4 = a3 + a2 - a1 //float a4 = Signed2DTriangleArea( C, D, B ); float a4 = a3 + a2 - a1; // Points a and b on different sides of cd if areas have different signs if (a3 * a4 < 0.0f) { // Segments intersect. Find intersection poitn along L(t) = a + t * ( b - a ) float t = a3 / ( a3 - a4 ); p = A + ( B - A ) * t; return true; } } // segments not intersecting (or collinear) p = new Vertex(-1, -1); return false; }