/// <summary> /// insert new point into segment eid at parameter value t /// If t is within tol of endpoint of segment, we use that instead. /// </summary> protected Index2i split_segment_at_t(int eid, double t, double tol) { Index2i ev = Graph.GetEdgeV(eid); Segment2d seg = new Segment2d(Graph.GetVertex(ev.a), Graph.GetVertex(ev.b)); int use_vid = -1; int new_eid = -1; if (t < -(seg.Extent - tol)) { use_vid = ev.a; } else if (t > (seg.Extent - tol)) { use_vid = ev.b; } else { DGraph2.EdgeSplitInfo splitInfo; MeshResult result = Graph.SplitEdge(eid, out splitInfo); if (result != MeshResult.Ok) { throw new Exception("insert_into_segment: edge split failed?"); } use_vid = splitInfo.vNew; new_eid = splitInfo.eNewBN; Vector2d pt = seg.PointAt(t); Graph.SetVertex(use_vid, pt); PointHash.InsertPointUnsafe(splitInfo.vNew, pt); } return(new Index2i(use_vid, new_eid)); }
public void CollapseToMinEdgeLength(double fMinLen) { double sharp_threshold_deg = 140.0f; double minLenSqr = fMinLen * fMinLen; bool done = false; int max_passes = 100; int pass_count = 0; while (done == false && pass_count++ < max_passes) { done = true; // [RMS] do modulo-indexing here to avoid pathological cases where we do things like // continually collapse a short edge adjacent to a long edge (which will result in crazy over-collapse) int N = Graph.MaxEdgeID; const int nPrime = 31337; // any prime will do... int cur_eid = 0; do { int eid = cur_eid; cur_eid = (cur_eid + nPrime) % N; if (!Graph.IsEdge(eid)) { continue; } if (FixedEdgeFilterF(eid)) { continue; } Index2i ev = Graph.GetEdgeV(eid); Vector2d va = Graph.GetVertex(ev.a); Vector2d vb = Graph.GetVertex(ev.b); double distSqr = va.DistanceSquared(vb); if (distSqr < minLenSqr) { int vtx_idx = -1; // collapse to this vertex // check valences. want to preserve positions of non-valence-2 int na = Graph.GetVtxEdgeCount(ev.a); int nb = Graph.GetVtxEdgeCount(ev.b); if (na != 2 && nb != 2) { continue; } if (na != 2) { vtx_idx = 0; } else if (nb != 2) { vtx_idx = 1; } // check opening angles. want to preserve sharp(er) angles if (vtx_idx == -1) { double opena = Math.Abs(Graph.OpeningAngle(ev.a)); double openb = Math.Abs(Graph.OpeningAngle(ev.b)); if (opena < sharp_threshold_deg && openb < sharp_threshold_deg) { continue; } else if (opena < sharp_threshold_deg) { vtx_idx = 0; } else if (openb < sharp_threshold_deg) { vtx_idx = 1; } } Vector2d newPos = (vtx_idx == -1) ? 0.5 * (va + vb) : ((vtx_idx == 0) ? va : vb); int keep = ev.a, remove = ev.b; if (vtx_idx == 1) { remove = ev.a; keep = ev.b; } DGraph2.EdgeCollapseInfo collapseInfo; if (Graph.CollapseEdge(keep, remove, out collapseInfo) == MeshResult.Ok) { Graph.SetVertex(collapseInfo.vKept, newPos); done = false; } } } while (cur_eid != 0); } }
protected virtual void do_split(Line2d line, bool insert_edges, int insert_gid) { if (EdgeSigns.Length < Graph.MaxVertexID) { EdgeSigns.resize(Graph.MaxVertexID); } foreach (int vid in Graph.VertexIndices()) { EdgeSigns[vid] = line.WhichSide(Graph.GetVertex(vid), OnVertexTol); } hits.Clear(); foreach (int eid in Graph.EdgeIndices()) { Index2i ev = Graph.GetEdgeV(eid); var signs = new Index2i(EdgeSigns[ev.a], EdgeSigns[ev.b]); if (signs.a * signs.b > 0) { continue; // both positive or negative, ignore } var hit = new edge_hit() { hit_eid = eid, vtx_signs = signs, hit_vid = -1 }; Vector2d a = Graph.GetVertex(ev.a); Vector2d b = Graph.GetVertex(ev.b); // parallel-edge case (both are zero) if (signs.a == signs.b) { if (a.DistanceSquared(b) > MathUtil.Epsilon) { // we need to somehow not insert a new segment for this span below. // so, insert two hit points for the ray-interval, with same eid. // This will result in this span being skipped by the same-eid test below // *however*, if other edges self-intersect w/ this segment, this will *not work* // and duplicate edges will be inserted hit.hit_vid = ev.a; hit.line_t = line.Project(a); hits.Add(hit); hit.hit_vid = ev.b; hit.line_t = line.Project(b); hits.Add(hit); } else { // degenerate edge - fall through to a == 0 case below signs.b = 1; } } if (signs.a == 0) { hit.hit_pos = a; hit.hit_vid = ev.a; hit.line_t = line.Project(a); } else if (signs.b == 0) { hit.hit_pos = b; hit.hit_vid = ev.b; hit.line_t = line.Project(b); } else { var intr = new IntrLine2Segment2(line, new Segment2d(a, b)); if (intr.Find() == false) { throw new Exception("GraphSplitter2d.Split: signs are different but ray did not it?"); } if (intr.IsSimpleIntersection) { hit.hit_pos = intr.Point; hit.line_t = intr.Parameter; } else { throw new Exception("GraphSplitter2d.Split: got parallel edge case!"); } } hits.Add(hit); } // sort by increasing ray-t hits.Sort((hit0, hit1) => { return(hit0.line_t.CompareTo(hit1.line_t)); }); // insert segments between successive intersection points int N = hits.Count; for (int i = 0; i < N - 1; ++i) { int j = i + 1; // note: skipping parallel segments depends on this eid == eid test (see above) if (hits[i].line_t == hits[j].line_t || hits[i].hit_eid == hits[j].hit_eid) { continue; } int vi = hits[i].hit_vid; int vj = hits[j].hit_vid; if (vi == vj && vi >= 0) { continue; } if (vi >= 0 && vj >= 0) { int existing = Graph.FindEdge(vi, vj); if (existing >= 0) { continue; } } if (vi == -1) { DGraph2.EdgeSplitInfo split; var result = Graph.SplitEdge(hits[i].hit_eid, out split); if (result != MeshResult.Ok) { throw new Exception("GraphSplitter2d.Split: first edge split failed!"); } vi = split.vNew; Graph.SetVertex(vi, hits[i].hit_pos); edge_hit tmp = hits[i]; tmp.hit_vid = vi; hits[i] = tmp; } if (vj == -1) { DGraph2.EdgeSplitInfo split; var result = Graph.SplitEdge(hits[j].hit_eid, out split); if (result != MeshResult.Ok) { throw new Exception("GraphSplitter2d.Split: second edge split failed!"); } vj = split.vNew; Graph.SetVertex(vj, hits[j].hit_pos); edge_hit tmp = hits[j]; tmp.hit_vid = vj; hits[j] = tmp; } // check if we actually want to add this segment if (InsideTestF != null) { Vector2d midpoint = 0.5 * (Graph.GetVertex(vi) + Graph.GetVertex(vj)); if (InsideTestF(midpoint) == false) { continue; } } if (insert_edges) { Graph.AppendEdge(vi, vj, insert_gid); } } }