protected virtual void InitializeVertexQuadrics() { int NT = mesh.MaxTriangleID; QuadricError[] triQuadrics = new QuadricError[NT]; double[] triAreas = new double[NT]; gParallel.ForEach(mesh.TriangleIndices(), (tid) => { Vector3d c, n; mesh.GetTriInfo(tid, out n, out triAreas[tid], out c); triQuadrics[tid] = new QuadricError(n, c); }); int NV = mesh.MaxVertexID; vertQuadrics = new QuadricError[NV]; gParallel.ForEach(mesh.VertexIndices(), (vid) => { vertQuadrics[vid] = QuadricError.Zero; foreach (int tid in mesh.VtxTrianglesItr(vid)) { vertQuadrics[vid].Add(triAreas[tid], ref triQuadrics[tid]); } //Util.gDevAssert(MathUtil.EpsilonEqual(0, vertQuadrics[i].Evaluate(mesh.GetVertex(i)), MathUtil.Epsilon * 10)); }); }
// return point that minimizes quadric error for edge [ea,eb] Vector3d OptimalPoint(QuadricError q, int ea, int eb) { if (MinimizeQuadricPositionError == false) { return((mesh.GetVertex(ea) + mesh.GetVertex(eb)) * 0.5); } else { try { return(q.OptimalPoint()); } catch { // degenerate matrix, evaluate quadric at edge end and midpoints // (could do line search here...) Vector3d va = mesh.GetVertex(ea); Vector3d vb = mesh.GetVertex(eb); Vector3d c = (va + vb) * 0.5; double fa = q.Evaluate(va); double fb = q.Evaluate(vb); double fc = q.Evaluate(c); double m = MathUtil.Min(fa, fb, fc); if (m == fa) { return(va); } else if (m == fb) { return(vb); } return(c); } } }
protected virtual void InitializeQueue() { int NE = mesh.EdgeCount; Nodes = new QEdge[2 * NE]; // [RMS] do we need this many? NodePool = new MemoryPool <QEdge>(NE); EdgeQueue = new g3ext.FastPriorityQueue <QEdge>(NE); int cur_eid = start_edges(); bool done = false; do { if (mesh.IsEdge(cur_eid)) { Index2i ev = mesh.GetEdgeV(cur_eid); QuadricError Q = new QuadricError(ref vertQuadrics[ev.a], ref vertQuadrics[ev.b]); Vector3d opt = OptimalPoint(Q, ev.a, ev.b); double err = Q.Evaluate(opt); QEdge ee = NodePool.Allocate(); ee.Initialize(cur_eid, Q, opt); Nodes[cur_eid] = ee; EdgeQueue.Enqueue(ee, (float)err); } cur_eid = next_edge(cur_eid, out done); } while (done == false); }
protected virtual void InitializeQueue() { int NE = mesh.EdgeCount; int MaxEID = mesh.MaxEdgeID; EdgeQuadrics = new QEdge[MaxEID]; EdgeQueue = new IndexPriorityQueue(MaxEID); float[] edgeErrors = new float[MaxEID]; // vertex quadrics can be computed in parallel gParallel.BlockStartEnd(0, MaxEID - 1, (start_eid, end_eid) => { for (int eid = start_eid; eid <= end_eid; eid++) { if (mesh.IsEdge(eid)) { Index2i ev = mesh.GetEdgeV(eid); QuadricError Q = new QuadricError(ref vertQuadrics[ev.a], ref vertQuadrics[ev.b]); Vector3d opt = OptimalPoint(eid, ref Q, ev.a, ev.b); edgeErrors[eid] = (float)Q.Evaluate(ref opt); EdgeQuadrics[eid] = new QEdge(eid, ref Q, ref opt); } } }); // sorted pq insert is faster, so sort edge errors array and index map int[] indices = new int[MaxEID]; for (int i = 0; i < MaxEID; ++i) { indices[i] = i; } Array.Sort(edgeErrors, indices); // now do inserts for (int i = 0; i < edgeErrors.Length; ++i) { int eid = indices[i]; if (mesh.IsEdge(eid)) { QEdge edge = EdgeQuadrics[eid]; EdgeQueue.Insert(edge.eid, edgeErrors[i]); } } /* * // previous code that does unsorted insert. This is marginally slower, but * // might get even slower on larger meshes? have only tried up to about 350k. * // (still, this function is not the bottleneck...) * int cur_eid = start_edges(); * bool done = false; * do { * if (mesh.IsEdge(cur_eid)) { * QEdge edge = EdgeQuadrics[cur_eid]; * double err = errList[cur_eid]; * EdgeQueue.Enqueue(cur_eid, (float)err); * } * cur_eid = next_edge(cur_eid, out done); * } while (done == false); */ }
// return point that minimizes quadric error for edge [ea,eb] protected Vector3d OptimalPoint(int eid, ref QuadricError q, int ea, int eb) { // if we would like to preserve boundary, we need to know that here // so that we properly score these edges if (HaveBoundary && PreserveBoundaryShape) { if (mesh.IsBoundaryEdge(eid)) { return((mesh.GetVertex(ea) + mesh.GetVertex(eb)) * 0.5); } else { if (IsBoundaryV(ea)) { return(mesh.GetVertex(ea)); } else if (IsBoundaryV(eb)) { return(mesh.GetVertex(eb)); } } } // [TODO] if we have constraints, we should apply them here, for same reason as bdry above... if (MinimizeQuadricPositionError == false) { return(project((mesh.GetVertex(ea) + mesh.GetVertex(eb)) * 0.5)); } else { Vector3d result = Vector3d.Zero; if (q.OptimalPoint(ref result)) { return(project(result)); } // degenerate matrix, evaluate quadric at edge end and midpoints // (could do line search here...) Vector3d va = mesh.GetVertex(ea); Vector3d vb = mesh.GetVertex(eb); Vector3d c = project((va + vb) * 0.5); double fa = q.Evaluate(ref va); double fb = q.Evaluate(ref vb); double fc = q.Evaluate(ref c); double m = MathUtil.Min(fa, fb, fc); if (m == fa) { return(va); } else if (m == fb) { return(vb); } return(c); } }
public void Add(double w, ref QuadricError b) { Axx += w * b.Axx; Axy += w * b.Axy; Axz += w * b.Axz; Ayy += w * b.Ayy; Ayz += w * b.Ayz; Azz += w * b.Azz; bx += w * b.bx; by += w * b.by; bz += w * b.bz; c += w * b.c; }
public QuadricError(ref QuadricError a, ref QuadricError b) { Axx = a.Axx + b.Axx; Axy = a.Axy + b.Axy; Axz = a.Axz + b.Axz; Ayy = a.Ayy + b.Ayy; Ayz = a.Ayz + b.Ayz; Azz = a.Azz + b.Azz; bx = a.bx + b.bx; by = a.by + b.by; bz = a.bz + b.bz; c = a.c + b.c; }
// update queue weight for each edge in vertex one-ring protected virtual void UpdateNeighbours(int vid) { foreach (int eid in mesh.VtxEdgesItr(vid)) { Index2i nev = mesh.GetEdgeV(eid); QuadricError Q = new QuadricError(ref vertQuadrics[nev.a], ref vertQuadrics[nev.b]); Vector3d opt = OptimalPoint(eid, ref Q, nev.a, nev.b); double err = Q.Evaluate(ref opt); EdgeQuadrics[eid] = new QEdge(eid, ref Q, ref opt); if (EdgeQueue.Contains(eid)) { EdgeQueue.Update(eid, (float)err); } else { EdgeQueue.Insert(eid, (float)err); } } }
protected virtual void InitializeVertexQuadrics() { int NT = mesh.MaxTriangleID; var triQuadrics = new QuadricError[NT]; double[] triAreas = new double[NT]; gParallel.BlockStartEnd(0, mesh.MaxTriangleID - 1, (start_tid, end_tid) => { Vector3d c, n; for (int tid = start_tid; tid <= end_tid; tid++) { if (mesh.IsTriangle(tid)) { mesh.GetTriInfo(tid, out n, out triAreas[tid], out c); triQuadrics[tid] = new QuadricError(ref n, ref c); } } }); int NV = mesh.MaxVertexID; vertQuadrics = new QuadricError[NV]; gParallel.BlockStartEnd(0, mesh.MaxVertexID - 1, (start_vid, end_vid) => { for (int vid = start_vid; vid <= end_vid; vid++) { vertQuadrics[vid] = QuadricError.Zero; if (mesh.IsVertex(vid)) { foreach (int tid in mesh.VtxTrianglesItr(vid)) { vertQuadrics[vid].Add(triAreas[tid], ref triQuadrics[tid]); } //Util.gDevAssert(MathUtil.EpsilonEqual(0, vertQuadrics[i].Evaluate(mesh.GetVertex(i)), MathUtil.Epsilon * 10)); } } }); }
// update queue weight for each edge in vertex one-ring protected virtual void UpdateNeighbours(int vid) { foreach (int eid in mesh.VtxEdgesItr(vid)) { Index2i nev = mesh.GetEdgeV(eid); QuadricError Q = new QuadricError(ref vertQuadrics[nev.a], ref vertQuadrics[nev.b]); Vector3d opt = OptimalPoint(Q, nev.a, nev.b); double err = Q.Evaluate(opt); QEdge eid_node = Nodes[eid]; if (eid_node != null) { eid_node.q = Q; eid_node.collapse_pt = opt; EdgeQueue.UpdatePriority(eid_node, (float)err); } else { QEdge ee = NodePool.Allocate(); ee.Initialize(eid, Q, opt); Nodes[eid] = ee; EdgeQueue.Enqueue(ee, (float)err); } } }
public QEdge(int edge_id, ref QuadricError qin, ref Vector3d pt) { eid = edge_id; q = qin; collapse_pt = pt; }
public void Initialize(int edge_id, QuadricError qin, Vector3d pt) { eid = edge_id; q = qin; collapse_pt = pt; }
public QEdge(int edge_id, QuadricError qin, Vector3d pt) { Initialize(edge_id, qin, pt); }