/// <summary> /// Find and remove any junction (ie valence>2) vertices of the graph. /// At a junction, the pair of best-aligned (ie straightest) edges are left /// connected, and all the other edges are disconnected /// /// [TODO] currently there is no DGraph2.SetEdge(), so the 'other' edges /// are deleted and new edges inserted. Hence, edge IDs are not preserved. /// </summary> public static int DisconnectJunctions(DGraph2 graph) { var junctions = new List <int>(); // find all junctions foreach (int vid in graph.VertexIndices()) { if (graph.IsJunctionVertex(vid)) { junctions.Add(vid); } } foreach (int vid in junctions) { Vector2d v = graph.GetVertex(vid); int[] nbr_verts = graph.VtxVerticesItr(vid).ToArray(); // find best-aligned pair of edges connected to vid Index2i best_aligned = Index2i.Max; double max_angle = 0; for (int i = 0; i < nbr_verts.Length; ++i) { for (int j = i + 1; j < nbr_verts.Length; ++j) { double angle = Vector2d.AngleD( (graph.GetVertex(nbr_verts[i]) - v).Normalized, (graph.GetVertex(nbr_verts[j]) - v).Normalized); angle = Math.Abs(angle); if (angle > max_angle) { max_angle = angle; best_aligned = new Index2i(nbr_verts[i], nbr_verts[j]); } } } // for nbr verts that are not part of the best_aligned edges, // we remove those edges and add a new one connected to a new vertex for (int k = 0; k < nbr_verts.Length; ++k) { if (nbr_verts[k] == best_aligned.a || nbr_verts[k] == best_aligned.b) { continue; } int eid = graph.FindEdge(vid, nbr_verts[k]); graph.RemoveEdge(eid, true); if (graph.IsVertex(nbr_verts[k])) { var newpos = Vector2d.Lerp(graph.GetVertex(nbr_verts[k]), v, 0.99); int newv = graph.AppendVertex(newpos); graph.AppendEdge(nbr_verts[k], newv); } } } return(junctions.Count); }
private static void MergeAppendPolyline(DGraph2 graph, PolyLine2d polyline, double mergeThreshold, int gid = -1) { int previousVertexIndex = -1; foreach (var vertex in polyline) { int currentVertexIndex = MergeAppendVertex(graph, vertex, mergeThreshold); if (previousVertexIndex != -1) { graph.AppendEdge(previousVertexIndex, currentVertexIndex, gid); } previousVertexIndex = currentVertexIndex; } }
/// <summary> /// foreach edge [vid,b] connected to junction vertex vid, remove, add new vertex c, /// and then add new edge [b,c]. Optionally move c a bit back along edge from vid. /// </summary> public static void DisconnectJunction(DGraph2 graph, int vid, double shrinkFactor = 1.0) { Vector2d v = graph.GetVertex(vid); int[] nbr_verts = graph.VtxVerticesItr(vid).ToArray(); for (int k = 0; k < nbr_verts.Length; ++k) { int eid = graph.FindEdge(vid, nbr_verts[k]); graph.RemoveEdge(eid, true); if (graph.IsVertex(nbr_verts[k])) { Vector2d newpos = Vector2d.Lerp(graph.GetVertex(nbr_verts[k]), v, shrinkFactor); int newv = graph.AppendVertex(newpos); graph.AppendEdge(nbr_verts[k], newv); } } }
/// <summary> /// insert edge [a,b] into the arrangement, splitting existing edges as necessary /// </summary> protected bool insert_segment(ref Vector2d a, ref Vector2d b, int gid = -1, double tol = 0) { // handle degenerate edges int a_idx = find_existing_vertex(a); int b_idx = find_existing_vertex(b); if (a_idx == b_idx && a_idx >= 0) { return(false); } // ok find all intersections List <Intersection> hits = new List <Intersection>(); find_intersecting_edges(ref a, ref b, hits, tol); int N = hits.Count; // we are going to construct a list of <t,vertex_id> values along segment AB List <SegmentPoint> points = new List <SegmentPoint>(); Segment2d segAB = new Segment2d(a, b); // insert intersections into existing segments for (int i = 0; i < N; ++i) { Intersection intr = hits[i]; int eid = intr.eid; double t0 = intr.intr.Parameter0, t1 = intr.intr.Parameter1; // insert first point at t0 int new_eid = -1; if (intr.intr.Type == IntersectionType.Point || intr.intr.Type == IntersectionType.Segment) { Index2i new_info = split_segment_at_t(eid, t0, VertexSnapTol); new_eid = new_info.b; Vector2d v = Graph.GetVertex(new_info.a); points.Add(new SegmentPoint() { t = segAB.Project(v), vid = new_info.a }); } // if intersection was on-segment, then we have a second point at t1 if (intr.intr.Type == IntersectionType.Segment) { if (new_eid == -1) { // did not actually split edge for t0, so we can still use eid Index2i new_info = split_segment_at_t(eid, t1, VertexSnapTol); Vector2d v = Graph.GetVertex(new_info.a); points.Add(new SegmentPoint() { t = segAB.Project(v), vid = new_info.a }); } else { // find t1 was in eid, rebuild in new_eid Segment2d new_seg = Graph.GetEdgeSegment(new_eid); Vector2d p1 = intr.intr.Segment1.PointAt(t1); double new_t1 = new_seg.Project(p1); Util.gDevAssert(new_t1 <= Math.Abs(new_seg.Extent)); Index2i new_info = split_segment_at_t(new_eid, new_t1, VertexSnapTol); Vector2d v = Graph.GetVertex(new_info.a); points.Add(new SegmentPoint() { t = segAB.Project(v), vid = new_info.a }); } } } // find or create start and end points if (a_idx == -1) { a_idx = find_existing_vertex(a); } if (a_idx == -1) { a_idx = Graph.AppendVertex(a); PointHash.InsertPointUnsafe(a_idx, a); } if (b_idx == -1) { b_idx = find_existing_vertex(b); } if (b_idx == -1) { b_idx = Graph.AppendVertex(b); PointHash.InsertPointUnsafe(b_idx, b); } // add start/end to points list. These may be duplicates but we will sort that out after points.Add(new SegmentPoint() { t = segAB.Project(a), vid = a_idx }); points.Add(new SegmentPoint() { t = segAB.Project(b), vid = b_idx }); // sort by t points.Sort((pa, pb) => { return((pa.t < pb.t) ? -1 : ((pa.t > pb.t) ? 1 : 0)); }); // connect sequential points, as long as they aren't the same point, // and the segment doesn't already exist for (int k = 0; k < points.Count - 1; ++k) { int v0 = points[k].vid; int v1 = points[k + 1].vid; if (v0 == v1) { continue; } if (Math.Abs(points[k].t - points[k + 1].t) < MathUtil.Epsilonf) { System.Console.WriteLine("insert_segment: different points with same t??"); } if (Graph.FindEdge(v0, v1) == DGraph2.InvalidID) { Graph.AppendEdge(v0, v1, gid); } } return(true); }
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); } } }