private void BuildInner() { var tri = D.LastTri; var dIdxsDone = new HashSet <int>(); var hullIdxs = new HashSet <int>(D.HullEdges.Select(_edge => _edge.OriginIdx)); var progState = "Voronoi Build Inner Sites..."; var prog = 0f; var trisTotal = D.Triangles().Length; //TODO not fast var triProgCnt = 0; //Build Inner Sites while (tri.PrevTri != null) { prog = (float)triProgCnt++ / trisTotal; progState = "Voronoi Inner Tri " + triProgCnt + " of " + trisTotal; D.Prog.Update(prog, progState); var edges = new[] { tri.Edge0, tri.Edge1, tri.Edge2 }; for (var eIdx = 0; eIdx < 3; ++eIdx) { var edge = edges[eIdx]; var edgeOIdx = edge.OriginIdx; if (dIdxsDone.Contains(edgeOIdx)) { continue; } dIdxsDone.Add(edgeOIdx); if (hullIdxs.Contains(edgeOIdx)) { continue; } var ptsCW = new List <Vector2>(); var twins = new List <Site.HalfEdge>(); var scanEdge = edge; do { var cc = new Vector2(scanEdge.Triangle.CCX, scanEdge.Triangle.CCY); ptsCW.Add(cc); Bounds.Encapsulate(cc); //Get and set twin scanEdge = scanEdge.NextEdge.NextEdge; Site.HalfEdge nbrScan = null; if (SitesByDIdx.ContainsKey(scanEdge.OriginIdx)) { var nbrSite = SitesByDIdx[scanEdge.OriginIdx]; //Get nbr Origin match nbrScan = nbrSite.FirstEdge; while (nbrScan.NextEdge.Origin != cc) { nbrScan = nbrScan.NextEdge; } } twins.Add(nbrScan); scanEdge = scanEdge.Twin; } while (scanEdge != edge); var site = new Site(ptsCW, twins, true); SitesByDIdx.Add(edgeOIdx, site); } tri = tri.PrevTri; } }
private void BuildOuter() { var progState = "Voronoi Build Outer..."; var prog = 0f; var infEdgeLen = (D.BoundsRect.width + D.BoundsRect.height) / 4f; //TODO var hullEdges = D.HullEdges; Site.HalfEdge lastTwin = null; for (var heIdx = 0; heIdx < hullEdges.Count; ++heIdx) { prog = (float)heIdx / hullEdges.Count; D.Prog.Update(prog, progState); var edge = hullEdges[heIdx]; var nextEdgeOriginPos = edge.NextEdge.OriginPos; var edgeOIdx = edge.OriginIdx; var ptsCW = new List <Vector2>(); var twins = new List <Site.HalfEdge> { null }; var scanEdge = edge; //Project first edge perpendicular to hull edge var perpHullVec = GetPerpendicularLeft(edge.OriginPos, nextEdgeOriginPos).normalized; perpHullVec *= infEdgeLen; var cc1 = new Vector2(scanEdge.Triangle.CCX, scanEdge.Triangle.CCY); var firstEdgePos = cc1 + perpHullVec; Bounds.Encapsulate(firstEdgePos); ptsCW.Add(firstEdgePos); //Build middle edges while (true) { var cc = new Vector2(scanEdge.Triangle.CCX, scanEdge.Triangle.CCY); ptsCW.Add(cc); Bounds.Encapsulate(cc); //Get and set twin scanEdge = scanEdge.NextEdge.NextEdge; if (scanEdge.Twin == null) { break; } Site.HalfEdge nbrScan = null; if (SitesByDIdx.ContainsKey(scanEdge.OriginIdx)) { var nbrSite = SitesByDIdx[scanEdge.OriginIdx]; //Get nbr Origin match nbrScan = nbrSite.FirstEdge; while (nbrScan.NextEdge.Origin != cc) { nbrScan = nbrScan.NextEdge; } } twins.Add(nbrScan); scanEdge = scanEdge.Twin; } //Add last twin twins.Add(lastTwin); //Project last edge perpendicular to hull edge perpHullVec = GetPerpendicularLeft(scanEdge.OriginPos, scanEdge.NextEdge.OriginPos).normalized; perpHullVec *= infEdgeLen; var ccLast = new Vector2(scanEdge.Triangle.CCX, scanEdge.Triangle.CCY); var lastEdgePos = ccLast + perpHullVec; Bounds.Encapsulate(lastEdgePos); ptsCW.Add(lastEdgePos); twins.Add(null); var site = new Site(ptsCW, twins, false); //TODO store dIDX? lastTwin = twins[0]; SitesByDIdx.Add(edgeOIdx, site); } }
public void TrimSitesToBndry(Rect _bndry, bool _closeAtTrim = true) { var trimBnds = _bndry; //Scan for sites needing trim and for removal var sitesToTrim = new HashSet <int>(); var sitesToRemove = new HashSet <int>(); //Progress var idxCnt = 0; var idxCntTot = SitesByDIdx.Keys.Count; var progState = "Voronoi Trimming Sites - Scanning"; var prog = 0f; foreach (var dIdx in SitesByDIdx.Keys) { prog = (float)idxCnt++ / idxCntTot; progState = "Voronoi Trimming Sites - Scanning " + idxCnt + " of " + idxCntTot; D.Prog.Update(prog, progState); var site = SitesByDIdx[dIdx]; var wasClosed = site.Closed; site.Closed = true; var scanEdge = site.FirstEdge; var hasIn = false; var hasOut = false; do { if (trimBnds.Contains(scanEdge.Origin)) { hasIn = true; } else { hasOut = true; } scanEdge = scanEdge.NextEdge; } while (scanEdge != site.FirstEdge); if (hasIn && hasOut) { sitesToTrim.Add(dIdx); } else if (hasOut && !hasIn) { sitesToRemove.Add(dIdx); } site.Closed = wasClosed; } var bndyOrigin = new Dictionary <BndSide, Vector2> { { BndSide.Up, trimBnds.min }, { BndSide.Right, new Vector2(trimBnds.xMin, trimBnds.yMax) }, { BndSide.Down, trimBnds.max }, { BndSide.Left, new Vector2(trimBnds.xMax, trimBnds.yMin) } }; //Trim Sites //Progress var idxTrimCur = 0; var idxTrimTot = sitesToTrim.Count; foreach (var dIdx in sitesToTrim) { prog = (float)idxTrimCur / idxTrimTot; progState = "Voronoi Trimming Sites - Trimming " + idxTrimCur + " of " + idxTrimTot; D.Prog.Update(prog, progState); var site = SitesByDIdx[dIdx]; site.Closed = true; var scanEdge = site.FirstEdge; while (!trimBnds.Contains(scanEdge.Origin)) { scanEdge = scanEdge.NextEdge; } //Find exit bnds intersection while (trimBnds.Contains(scanEdge.NextEdge.Origin)) { scanEdge = scanEdge.NextEdge; } var exitEdge = scanEdge; var intFrom = exitEdge.Origin; var intVec = exitEdge.NextEdge.Origin - intFrom; BndSide exitSide; var exitIntPt = GetIntersectionToBndy(intFrom, intVec, trimBnds, out exitSide); //Find enter bnds interection while (!trimBnds.Contains(scanEdge.NextEdge.Origin)) { scanEdge = scanEdge.NextEdge; } var enterEdge = scanEdge; intFrom = enterEdge.NextEdge.Origin; intVec = enterEdge.Origin - intFrom; BndSide enterSide; var enterIntPt = GetIntersectionToBndy(intFrom, intVec, trimBnds, out enterSide); if (exitSide == enterSide || !_closeAtTrim) { var newBndEdge = new Site.HalfEdge(exitIntPt); exitEdge.NextEdge = newBndEdge; newBndEdge.NextEdge = enterEdge; enterEdge.Origin = enterIntPt; site.FirstEdge = enterEdge; } else { var newBndEdgeA = new Site.HalfEdge(exitIntPt); var newBndEdgeB = new Site.HalfEdge(bndyOrigin[enterSide]); exitEdge.NextEdge = newBndEdgeA; newBndEdgeA.NextEdge = newBndEdgeB; enterEdge.Origin = enterIntPt; newBndEdgeB.NextEdge = enterEdge; site.FirstEdge = enterEdge; } site.Closed = _closeAtTrim; } //Clean up sites outside of boundary foreach (var dIdx in sitesToRemove) //TODO need to do more than remove from sites list? (Memory) { var site = SitesByDIdx[dIdx]; foreach (var edge in site.Edges) { edge.NextEdge = null; edge.Twin = null; } site.FirstEdge = null; SitesByDIdx.Remove(dIdx); } }