protected List <Vertex2D> GetSpinePoints(Chord2D chord) { var points = new List <Vertex2D>(); GetSpinePointsRoutine(points, chord, null); return(points); }
// triangles are divided at the spine protected void Subdivide(Chord2D root) { var rTriangles = new List <Triangle2D>(); var rSegments = new List <Segment2D>(); SubdivideRoutine(rTriangles, rSegments, root, null); rTriangles.ForEach(t => triangulation.RemoveTriangle(t)); rSegments.ForEach(s => triangulation.RemoveTriangle(s)); }
protected void GetSpinePointsRoutine(List <Vertex2D> points, Chord2D current, Chord2D from) { if (!current.Pruned) { points.Add(current.Src); } current.Connection.ForEach(to => { if (to != from) { GetSpinePointsRoutine(points, to, current); } }); }
protected void Sew(Triangulation2D triangulation, Chord2D chord, Dictionary <Vertex2D, float> heightTable, int division) { var triangles = triangulation.Triangles.ToList(); Dictionary <Triangle2D, bool> flags = new Dictionary <Triangle2D, bool>(); // 既にSew済みのTriangle2Dを再度Sewする必要があるケースが存在する // 特定のChord2DのSrcとDstを含むTriangle2Dなのに,それ以外のChord2Dと共通点を持っているがために // Spineと共通エッジを持つ場合のSewの処理がかけられず適切な分割ができていないケース Dictionary <Triangle2D, List <Triangle2D> > sews = new Dictionary <Triangle2D, List <Triangle2D> >(); triangles.ForEach(t => flags.Add(t, false)); SewRoutine(triangles, flags, sews, chord, null, division); }
protected List <Chord2D> Traverse(Chord2D cur, Chord2D from, Func <Chord2D, bool> check) { var chords = new List <Chord2D>(); if (check(cur)) { chords.Add(cur); } cur.Connection.ForEach(to => { if (to != from) { chords.AddRange(Traverse(to, cur, check)); } }); return(chords); }
List <Vertex2D> GetSpinePoints(Chord2D chord) { var points = new List <Vertex2D>(); Action <Chord2D, Chord2D> Recursion; Recursion = (Chord2D current, Chord2D from) => { if (!current.Pruned) { points.Add(current.Src); } current.Connection.ForEach(to => { if (to != from) { Recursion(to, current); } }); }; Recursion(chord, null); return(points); }
/* * The chordal axis is obtained by connecting the midpoints of the internal edges. */ protected Chord2D GetChordalAxis(Face2D external, List <Face2D> faces) { var t = external.Triangle; Vertex2D src, dst; Segment2D dstEdge; bool e0 = ExternalSegment(t.s0); bool e1 = ExternalSegment(t.s1); bool e2 = ExternalSegment(t.s2); if (e0 && e1) { src = t.s0.HasPoint(t.s1.a) ? t.s1.a : t.s1.b; dst = triangulation.CheckAndAddVertex(t.s2.Midpoint()); dstEdge = t.s2; } else if (e1 && e2) { src = t.s1.HasPoint(t.s2.a) ? t.s2.a : t.s2.b; dst = triangulation.CheckAndAddVertex(t.s0.Midpoint()); dstEdge = t.s0; } else { src = t.s2.HasPoint(t.s0.a) ? t.s0.a : t.s0.b; dst = triangulation.CheckAndAddVertex(t.s1.Midpoint()); dstEdge = t.s1; } var chord = new Chord2D(src, dst, external); chord.SetDstEdge(dstEdge); var connection = new Connection2D(faces); ChordalAxisRoutine(chord, connection); return(chord); }
void Init(Triangulation2D triangulation) { contourSegments = BuildContourSegments(triangulation); var triangles = triangulation.Triangles.ToList(); faces = Categorize(triangles); var terminal = faces.Find(f => f.Type == Face2DType.Terminal); chord = GetChordalAxis(terminal, faces); var terminalChords = Traverse(chord, null, (Chord2D c) => { return(c.Face.Type == Face2DType.Terminal); }); var convergence = new List <Vertex2D>(); terminalChords.ForEach(c => { Prune(c, convergence); }); Subdivide(chord); // var spine = GetSpinePoints(chord); var networkTable = BuildTable(triangulation); networks = BuildNetworks(triangulation, networkTable); Elevate(networks); heightTable = new Dictionary <Vertex2D, float>(); foreach (Vertex2D v in networkTable.Keys) { heightTable.Add(v, networkTable[v].Height); } Sew(triangulation, chord, heightTable, 3); }
protected void SewRoutine(List <Triangle2D> triangles, Dictionary <Triangle2D, bool> flags, Dictionary <Triangle2D, List <Triangle2D> > sews, Chord2D current, Chord2D from, int division) { if (!current.Pruned) { triangles.FindAll(t => { return(t.HasPoint(current.Src) && (t.HasPoint(current.Dst) || !sews.ContainsKey(t))); }).ForEach(t => { Segment2D[] segments; if (sews.ContainsKey(t)) { foreach (Triangle2D st in sews[t]) { triangulation.RemoveTriangle(st); } sews.Remove(t); } if (t.HasPoint(current.Dst)) { Segment2D s = t.CommonSegment(current.Src, current.Dst); segments = t.ExcludeSegment(s); } else { segments = t.CommonSegments(current.Src); } SewTriangle(flags, sews, t, segments, division); }); } var next = current.Connection.FindAll(con => con != from); // Prune後のSpineで末端にあたる頂点 bool terminal = next.All(con => con.Pruned); if (terminal && !current.Pruned) { triangles.FindAll(t => { return(!sews.ContainsKey(t) && t.HasPoint(current.Dst)); }).ForEach(t => { var segments = t.CommonSegments(current.Dst); SewTriangle(flags, sews, t, segments, division); }); } next.ForEach(to => { SewRoutine(triangles, flags, sews, to, current, division); }); }
protected void SubdivideRoutine(List <Triangle2D> rTriangles, List <Segment2D> rSegments, Chord2D chord, Chord2D from) { switch (chord.Face.Type) { case Face2DType.Sleeve: if (!chord.Pruned) { Vertex2D top, lb, rb; Segment2D s0 = chord.SrcEdge, s1 = chord.DstEdge; if (s0.a == s1.a || s0.a == s1.b) { top = s0.a; lb = s0.b; } else { top = s0.b; lb = s0.a; } rb = (s1.a == top) ? s1.b : s1.a; Vertex2D tl = chord.Src, tr = chord.Dst, bottom = triangulation.CheckAndAddVertex((lb.Coordinate + rb.Coordinate) * 0.5f); // triangulation.RemoveTriangle(chord.Face.Triangle); rTriangles.Add(chord.Face.Triangle); triangulation.AddTriangle(top, tl, tr); triangulation.AddTriangle(tl, lb, bottom); triangulation.AddTriangle(tr, bottom, rb); triangulation.AddTriangle(tl, bottom, tr); } break; case Face2DType.Junction: if (!chord.Pruned) { Vertex2D top, lb, rb, bottom; if (chord.SrcEdge != null) { top = chord.Dst; lb = chord.SrcEdge.a; rb = chord.SrcEdge.b; bottom = chord.Src; // triangulation.RemoveTriangle(chord.SrcEdge); rSegments.Add(chord.SrcEdge); } else { top = chord.Src; lb = chord.DstEdge.a; rb = chord.DstEdge.b; bottom = chord.Dst; // triangulation.RemoveTriangle(chord.DstEdge); rSegments.Add(chord.DstEdge); } // triangulation.RemoveTriangle(chord.Face.Triangle); triangulation.AddTriangle(top, lb, bottom); triangulation.AddTriangle(top, rb, bottom); } break; } chord.Connection.ForEach(to => { if (to != from) { SubdivideRoutine(rTriangles, rSegments, to, chord); } }); }
/* * BUG * 既にPrune済みのJunctionを再度Pruneしてしまうバグ */ protected void Prune(Chord2D chord, List <Vertex2D> convergence, Chord2D from = null, List <Chord2D> past = null) { if (chord.Face.Type == Face2DType.Junction) { past.Add(chord); var centroid = chord.Face.Centroid(); var points = new List <Vertex2D>(); var ignores = new List <Vertex2D>(); for (int i = 0, n = past.Count - 1; i < n; i++) { var c0 = past[i]; var c1 = past[i + 1]; points.Add(c0.Face.GetUncommonPoint(c1.Face)); } var t = chord.Face.Triangle; Vertex2D dividePoint = null; if (!chord.Face.Pruned) { points.Add(t.a); points.Add(t.b); points.Add(t.c); var coord = chord.Src.Coordinate; if (coord == (t.a.Coordinate + t.b.Coordinate) * 0.5f) { dividePoint = t.c; } else if (coord == (t.b.Coordinate + t.c.Coordinate) * 0.5f) { dividePoint = t.a; } else { dividePoint = t.b; } } else { points.Add(t.a); points.Add(t.b); points.Add(t.c); var divides = chord.Face.Divides; Triangle2D t0 = divides[0], t1 = divides[1]; Vertex2D[] ps0 = t0.ExcludePoint(centroid), ps1 = t1.ExcludePoint(centroid); if (chord.Dst.Coordinate == (ps0[0].Coordinate + ps0[1].Coordinate) * 0.5f) { triangulation.RemoveTriangle(t0); if (t.a != ps0[0] && t.a != ps0[1]) { ignores.Add(t.a); } else if (t.b != ps0[0] && t.b != ps0[1]) { ignores.Add(t.b); } else { ignores.Add(t.c); } } else if (chord.Dst.Coordinate == (ps1[0].Coordinate + ps1[1].Coordinate) * 0.5f) { triangulation.RemoveTriangle(t1); if (t.a != ps1[0] && t.a != ps1[1]) { ignores.Add(t.a); } else if (t.b != ps1[0] && t.b != ps1[1]) { ignores.Add(t.b); } else { ignores.Add(t.c); } } else { // Debug.LogWarning("error!"); return; } } var vertices = points.OrderBy(p => Angle(centroid, p.Coordinate)).ToList(); var cv = triangulation.CheckAndAddVertex(centroid); var newTriangles = new List <Triangle2D>(); for (int i = 0, n = vertices.Count; i < n; i++) { Vertex2D a = cv, b = vertices[i], c = vertices[(i + 1) % n]; if (ignores.Contains(b) || ignores.Contains(c)) { continue; } var nt = triangulation.AddTriangle(a, b, c); newTriangles.Add(nt); } if (!chord.Face.Pruned && dividePoint != null) { var divides = newTriangles.FindAll(nt => nt.HasPoint(dividePoint)); chord.Face.SetDivides(divides.ToList()); } // finish current loop convergence.Add(cv); Prune(past); return; } Segment2D diameter; if (chord.Face.Type == Face2DType.Terminal) { var t = chord.Face.Triangle; if (!ExternalSegment(t.s0)) { diameter = t.s0; } else if (!ExternalSegment(t.s1)) { diameter = t.s1; } else { diameter = t.s2; } } else { var segments = chord.Face.GetUncommonSegments(from.Face); if (!ExternalSegment(segments[0])) { diameter = segments[0]; } else { diameter = segments[1]; } } if (past == null) { past = new List <Chord2D>(); } past.Add(chord); Vector2 center = diameter.Midpoint(); float radius = Vector2.Distance(center, diameter.a.Coordinate); var found = past.Find(ch => { var t = ch.Face.Triangle; Vector2 a = t.a.Coordinate, b = t.b.Coordinate, c = t.c.Coordinate; return ((!diameter.HasPoint(a) && Vector2.Distance(a, center) - radius > float.Epsilon) || (!diameter.HasPoint(b) && Vector2.Distance(b, center) - radius > float.Epsilon) || (!diameter.HasPoint(c) && Vector2.Distance(c, center) - radius > float.Epsilon)); }); if (found != null) { var points = new List <Vertex2D>(); for (int i = 0, n = past.Count - 1; i < n; i++) { var c0 = past[i]; var c1 = past[i + 1]; points.Add(c0.Face.GetUncommonPoint(c1.Face)); } points.Add(chord.Face.GetUncommonPoint(diameter)); var basis = diameter.a.Coordinate; var vertices = points.OrderBy(p => Angle(center, basis, p.Coordinate)).ToList(); vertices.Insert(0, diameter.a); vertices.Add(diameter.b); var cv = triangulation.CheckAndAddVertex(center); for (int i = 0, n = vertices.Count - 1; i < n; i++) { Vertex2D a = cv, b = vertices[i], c = vertices[i + 1]; triangulation.AddTriangle(a, b, c); } convergence.Add(cv); Prune(past); return; } chord.Connection.ForEach(to => { if (to != from) { Prune(to, convergence, chord, past.ToList()); } }); }
protected void ChordalAxisRoutine(Chord2D chord, Connection2D connection) { var origin = chord.Face; var neighbors = connection.GetNeighbors(origin); connection.Remove(origin); // prevent overlaping to traverse neighbors.ForEach(neighbor => { var face = neighbor.face; var segment = neighbor.joint; Vertex2D destination; Segment2D destinationEdge = null; switch (face.Type) { case Face2DType.Junction: destination = triangulation.CheckAndAddVertex(face.Centroid()); break; case Face2DType.Sleeve: var others = face.Triangle.ExcludeSegment(segment); if (!ExternalSegment(others[0])) { destination = triangulation.CheckAndAddVertex(others[0].Midpoint()); destinationEdge = others[0]; } else { destination = triangulation.CheckAndAddVertex(others[1].Midpoint()); destinationEdge = others[1]; } break; // case Face2DType.Terminal: default: destination = face.GetUncommonPoint(origin); break; } Chord2D nextChord; if (origin.Type == Face2DType.Junction) { var interval = new Chord2D(chord.Dst, triangulation.CheckAndAddVertex(segment.Midpoint()), origin); interval.SetSrcEdge(chord.DstEdge); // maybe null interval.SetDstEdge(segment); nextChord = new Chord2D(interval.Dst, destination, face); nextChord.SetSrcEdge(segment); nextChord.SetDstEdge(destinationEdge); // null or exist chord.Connect(interval); interval.Connect(nextChord); } else { nextChord = new Chord2D(chord.Dst, destination, face); nextChord.SetSrcEdge(chord.DstEdge); nextChord.SetDstEdge(destinationEdge); // null or exist chord.Connect(nextChord); } ChordalAxisRoutine(nextChord, connection); }); }
void Sew(Triangulation2D triangulation, Chord2D chord, Dictionary <Vertex2D, float> heightTable, int division) { var triangles = triangulation.Triangles.ToList(); Dictionary <Triangle2D, bool> flags = new Dictionary <Triangle2D, bool>(); // 既にSew済みのTriangle2Dを再度Sewする必要があるケースが存在する // 特定のChord2DのSrcとDstを含むTriangle2Dなのに,それ以外のChord2Dと共通点を持っているがために // Spineと共通エッジを持つ場合のSewの処理がかけられず適切な分割ができていないケース Dictionary <Triangle2D, List <Triangle2D> > sews = new Dictionary <Triangle2D, List <Triangle2D> >(); triangles.ForEach(t => flags.Add(t, false)); Action <Triangle2D, Segment2D[]> SewTriangle = (Triangle2D t, Segment2D[] segments) => { triangulation.RemoveTriangle(t); var sewTriangles = Sew(heightTable, triangulation, segments[0], segments[1], division); sews.Add(t, sewTriangles); flags[t] = true; }; Action <Chord2D, Chord2D> SewRoutine; SewRoutine = (Chord2D current, Chord2D from) => { if (!current.Pruned) { triangles.FindAll(t => { return(t.HasPoint(current.Src) && (t.HasPoint(current.Dst) || !sews.ContainsKey(t))); }).ForEach(t => { Segment2D[] segments; if (sews.ContainsKey(t)) { foreach (Triangle2D st in sews[t]) { triangulation.RemoveTriangle(st); } sews.Remove(t); } if (t.HasPoint(current.Dst)) { Segment2D s = t.CommonSegment(current.Src, current.Dst); segments = t.ExcludeSegment(s); } else { segments = t.CommonSegments(current.Src); } SewTriangle(t, segments); }); } var next = current.Connection.FindAll(con => con != from); // Prune後のSpineで末端にあたる頂点 bool terminal = next.All(con => con.Pruned); if (terminal && !current.Pruned) { triangles.FindAll(t => { return(!sews.ContainsKey(t) && t.HasPoint(current.Dst)); }).ForEach(t => { var segments = t.CommonSegments(current.Dst); SewTriangle(t, segments); }); } next.ForEach(to => { SewRoutine(to, current); }); }; SewRoutine(chord, null); }
public void Disconnect(Chord2D c) { this.connection.Remove(c); c.connection.Remove(this); }
public void Connect(Chord2D c) { this.connection.Add(c); c.connection.Add(this); }