// 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); } } }
// 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); } }
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); */ }
// 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); } } }
// 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); } } }