/// <summary> /// Compute the convex hull in 3-D space /// </summary> /// <returns>surfaces of the convex hull</returns> public List <TriangularFace> Compute() { NumFace = 0; if (NumVertex < 4) { return(null); } Prepare(); for (int i = 4; i < NumVertex; i++) { for (int j = 0; j < NumFace; j++) { if (HullFace[j].Valid && HullFace[j].VOutFace(Vertices[i])) { Horizon.Clear(); ComputeHorizon(HullFace[j], Vertices[i]); SortHorizons(); TriangularFace first = null; TriangularFace last = null; DualSite v = Vertices[i]; for (int ii = 0; ii < Horizon.Count; ii++) { Edge e = Horizon[ii]; TriangularFace f = e.NeighborFace; TriangularFace add = new TriangularFace(e.Vertex0, e.Vertex1, v); HullFace.Add(add); NumFace++; f.SetNeighbor(add, e.Vertex0, e.Vertex1); if (first == null) { first = add; } else { add.SetNeighbor(last, e.Vertex0, v); } last = add; } last.SetNeighbor(first, last.Edge0.Vertex1, Vertices[i]); break; } } } List <TriangularFace> result = new List <TriangularFace>(); for (int i = 0; i < HullFace.Count; i++) { if (HullFace[i].Valid) { result.Add(HullFace[i]); } } return(result); }
/// <summary> /// let the first three vertices noncollinear and the first four vertices noncoplanar /// </summary> private void Prepare() { Vector3D tmp_vector = new Vector3D(); for (int i = 2; i < NumVertex; i++) { tmp_vector = Vector3D.CrossProduct(Vertices[1].Coordinate - Vertices[0].Coordinate, Vertices[i].Coordinate - Vertices[0].Coordinate); if (tmp_vector.LengthSquared > Eps)//三点不共线 { if (i != 2) { DualSite tmp = Vertices[i]; Vertices[i] = Vertices[2]; Vertices[2] = tmp; } break; } } for (int i = 3; i < NumVertex; i++) { if (Math.Abs(Vector3D.DotProduct(tmp_vector, Vertices[i].Coordinate - Vertices[0].Coordinate)) > Eps)//四点不共面 { if (i != 3) { DualSite tmp = Vertices[i]; Vertices[i] = Vertices[3]; Vertices[3] = tmp; } break; } } //为使面法向量方向指向凸包外 TriangularFace tmp_face = new TriangularFace(Vertices[0], Vertices[1], Vertices[2]); if (tmp_face.VOutFace(Vertices[3])) { DualSite tmp = Vertices[2]; Vertices[2] = Vertices[3]; Vertices[3] = tmp; } HullFace.Add(new TriangularFace(Vertices[0], Vertices[1], Vertices[2])); HullFace.Add(new TriangularFace(Vertices[1], Vertices[3], Vertices[2])); HullFace.Add(new TriangularFace(Vertices[0], Vertices[2], Vertices[3])); HullFace.Add(new TriangularFace(Vertices[0], Vertices[3], Vertices[1])); HullFace[0].SetNeighbor(HullFace[1], Vertices[1], Vertices[2]); HullFace[0].SetNeighbor(HullFace[2], Vertices[0], Vertices[2]); HullFace[0].SetNeighbor(HullFace[3], Vertices[0], Vertices[1]); HullFace[1].SetNeighbor(HullFace[2], Vertices[2], Vertices[3]); HullFace[1].SetNeighbor(HullFace[3], Vertices[1], Vertices[3]); HullFace[2].SetNeighbor(HullFace[3], Vertices[0], Vertices[3]); NumFace = 4; }
private void MainLoop() { int count = 0; while (faceQueue.Count > 0) { count++; HullFace face = faceQueue.Dequeue(); if (face.active == false) { continue; } if (face.furthestIndex != -1) { int p0 = face.furthestIndex; this.hullVertices.Add(p0); // collect visible faces, boundary edges and assoicated vertices List <HullFace> holeFace = new List <HullFace>(); HashSet <EdgeRecord> holeBoundary = new HashSet <EdgeRecord>(); HashSet <int> assoicatedVertex = new HashSet <int>(); foreach (HullFace f in this.surfaceSet) { Vector3d v = this.p[p0] - this.p[f.p1]; if (v.Dot(f.normal) > 0) { holeFace.Add(f); int p1 = f.p1; int p2 = f.p2; int p3 = f.p3; EdgeRecord r1 = new EdgeRecord(p1, p2); EdgeRecord r2 = new EdgeRecord(p2, p3); EdgeRecord r3 = new EdgeRecord(p3, p1); if (holeBoundary.Contains(r1)) { holeBoundary.Remove(r1); } else { holeBoundary.Add(r1); } if (holeBoundary.Contains(r2)) { holeBoundary.Remove(r2); } else { holeBoundary.Add(r2); } if (holeBoundary.Contains(r3)) { holeBoundary.Remove(r3); } else { holeBoundary.Add(r3); } foreach (int index in f.associatedPoints) { assoicatedVertex.Add(index); } } } if (holeFace.Count == 0) { throw new Exception(); } // remove add visible faces foreach (HullFace f in holeFace) { this.surfaceSet.Remove(f); f.active = false; } // add new faces foreach (EdgeRecord edge in holeBoundary) { HullFace newFace = new HullFace(p0, edge.p1, edge.p2); newFace.normal = ((p[edge.p1] - p[p0]).Cross(p[edge.p2] - p[p0])).normalize(); this.surfaceSet.Add(newFace); this.faceQueue.Enqueue(newFace); // add assoicated vertices foreach (int index in assoicatedVertex) { if (index == p0) { continue; } double dis = (p[index] - p[p0]).Dot(newFace.normal); if (dis > 0) { newFace.AddPoint(index, dis); } } } } } computeCenter(); }
private void InitHullFaces() { // get init points int[] initPts = FindInitPoints(); this.initIndex = initPts; int p0 = initPts[0]; int p1 = initPts[1]; int p2 = initPts[2]; int p3 = initPts[3]; double vol = (p[p1] - p[p0]).Dot((p[p2] - p[p0]).Cross(p[p3] - p[p0])); if (vol > 0) { int t = p0; p0 = p1; p1 = t; } this.hullVertices.Add(p0); this.hullVertices.Add(p1); this.hullVertices.Add(p2); this.hullVertices.Add(p3); // create hull faces HullFace f1 = new HullFace(p0, p1, p2); this.faceQueue.Enqueue(f1); HullFace f2 = new HullFace(p3, p1, p0); this.faceQueue.Enqueue(f2); HullFace f3 = new HullFace(p3, p2, p1); this.faceQueue.Enqueue(f3); HullFace f4 = new HullFace(p3, p0, p2); this.faceQueue.Enqueue(f4); f1.normal = ((p[p1] - p[p0]).Cross(p[p2] - p[p0])).normalize(); f2.normal = ((p[p1] - p[p3]).Cross(p[p0] - p[p3])).normalize(); f3.normal = ((p[p2] - p[p3]).Cross(p[p1] - p[p3])).normalize(); f4.normal = ((p[p0] - p[p3]).Cross(p[p2] - p[p3])).normalize(); this.surfaceSet.Add(f1); this.surfaceSet.Add(f2); this.surfaceSet.Add(f3); this.surfaceSet.Add(f4); // assoicate vertices outside current hull foreach (int i in this.pointIndex) { if (i == p0 || i == p1 || i == p2 || i == p3) { continue; } double d1 = (p[i] - p[p0]).Dot(f1.normal); double d2 = (p[i] - p[p3]).Dot(f2.normal); double d3 = (p[i] - p[p3]).Dot(f3.normal); double d4 = (p[i] - p[p3]).Dot(f4.normal); if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) { continue; } if (d1 > 0) { f1.AddPoint(i, d1); } if (d2 > 0) { f2.AddPoint(i, d2); } if (d3 > 0) { f3.AddPoint(i, d3); } if (d4 > 0) { f4.AddPoint(i, d4); } } }
public List<HullEdge> GetSharedEdges(HullFace other) { List<HullEdge> result = new List<HullEdge>(); if (ab.IsContainedIn(other)) result.Add(ab); if (ca.IsContainedIn(other)) result.Add(ca); if (bc.IsContainedIn(other)) result.Add(bc); return result; }
public bool IsContainedIn(HullFace other) { return Equivalent(other.ab) || Equivalent(other.ca) || Equivalent(other.bc); }