//combine segments to paths //algorithm: make a tiled index with the endpoints of the segments //for each segment search in the tile and neighboring tiles for matches //only examine neighboring tiles if the tile is within distMax from current point public static List <SegmentPath> segments_to_paths__tiles2(List <Segment> segments, float distMax = 1e-10f) { List <SegmentPath> paths = new List <SegmentPath>(); //create SegmentTiles int tileLen = 30; //number of tiles per row/column Segment[] segs = segments.ToArray(); float xmin = float.MaxValue; float ymin = float.MaxValue; float xmax = float.MinValue; float ymax = float.MinValue; foreach (Segment s in segs) { foreach (Vector2F v in s.p) { if (xmin > v.X) { xmin = v.X; } if (xmax < v.X) { xmax = v.X; } if (ymin > v.Y) { ymin = v.Y; } if (ymax < v.Y) { ymax = v.Y; } } } float xstep = (xmax - xmin) / tileLen; float ystep = (ymax - ymin) / tileLen; List <SegmentTilePoint>[] segTiles = new List <SegmentTilePoint> [(tileLen + 2) * (tileLen + 2)]; //add extra row/column around perimeter for (int i = 0; i < (tileLen + 2) * (tileLen + 2); i++) { segTiles[i] = new List <SegmentTilePoint>(); } SegmentTilePoint[] sidx = new SegmentTilePoint[2]; for (int seg_i = 0; seg_i < segs.Length; seg_i++) { Segment s = segs[seg_i]; for (int j = 0; j < 2; j++) { int idx_x = (int)((s.p[j].X - xmin) / xstep); int idx_y = (int)((s.p[j].Y - ymin) / ystep); int idx = (idx_y + 1) * tileLen + (idx_x + 1); sidx[j] = new SegmentTilePoint(seg_i, s.p[j], null); segTiles[idx].Add(sidx[j]); } sidx[0].othersegidx = sidx[1]; sidx[1].othersegidx = sidx[0]; } //array near defines the tile offsets to: current tile, left-above, above, right-above, left, right, left-below, below, right-below int[] near = new int[] { 0, -tileLen - 1, -tileLen, -tileLen + 1, -1, 1, tileLen - 1, tileLen, tileLen + 1 }; //process each segment for (int seg_i = 0; seg_i < segs.Length; seg_i++) { Segment seg = segs[seg_i]; if (seg == null) { continue; } segs[seg_i] = null; SegmentPath path = new SegmentPath(seg.p); paths.Add(path); //find poins that match the end of the path bool pointadded; do { pointadded = false; Vector2F pathendpoint = path.p.Last.Value; int idx_x = (int)((pathendpoint.X - xmin) / xstep); int idx_y = (int)((pathendpoint.Y - ymin) / ystep); int idx = (idx_y + 1) * tileLen + (idx_x + 1); float x0 = xmin + idx_x * xstep; float x1 = xmin + (idx_x + 1) * xstep; float y0 = ymin + idx_y * ystep; float y1 = xmin + (idx_y + 1) * ystep; for (int near_i = 0; !pointadded && near_i < 9; near_i++) { //check if need to examine tile for possible matches switch (near_i) { case 0: break; case 1: if (pathendpoint.X - distMax > x0 && pathendpoint.Y - distMax > y0) { continue; } break; //left-above case 2: if (pathendpoint.Y - distMax > y0) { continue; } break; //above case 3: if (pathendpoint.X + distMax < x1 && pathendpoint.Y - distMax > y0) { continue; } break; //right-above case 4: if (pathendpoint.X - distMax > x0) { continue; } break; //left case 5: if (pathendpoint.X + distMax < x1) { continue; } break; //right case 6: if (pathendpoint.X - distMax > x0 && pathendpoint.Y + distMax < y1) { continue; } break; //left-below case 7: if (pathendpoint.Y + distMax < y1) { continue; } break; //below case 8: if (pathendpoint.X + distMax < x1 && pathendpoint.Y + distMax < y1) { continue; } break; //right-below } List <SegmentTilePoint> sgidx = segTiles[idx + near[near_i]]; foreach (SegmentTilePoint si in sgidx) { if (si.alive && Vector2F.DistanceManhattan(pathendpoint, si.p) < distMax) { path.p.AddLast(si.othersegidx.p); si.alive = false; si.othersegidx.alive = false; segs[si.seg_i] = null; pointadded = true; break; } } } } while (pointadded); //find poins that match the beginning of the path do { pointadded = false; Vector2F pathbeginpoint = path.p.First.Value; int idx_x = (int)((pathbeginpoint.X - xmin) / xstep); int idx_y = (int)((pathbeginpoint.Y - ymin) / ystep); int idx = (idx_y + 1) * tileLen + (idx_x + 1); float x0 = xmin + idx_x * xstep; float x1 = xmin + (idx_x + 1) * xstep; float y0 = ymin + idx_y * ystep; float y1 = xmin + (idx_y + 1) * ystep; for (int near_i = 0; !pointadded && near_i < 9; near_i++) { //check if need to examine tile for possible matches switch (near_i) { case 0: break; case 1: if (pathbeginpoint.X - distMax > x0 && pathbeginpoint.Y - distMax > y0) { continue; } break; //left-above case 2: if (pathbeginpoint.Y - distMax > y0) { continue; } break; //above case 3: if (pathbeginpoint.X + distMax < x1 && pathbeginpoint.Y - distMax > y0) { continue; } break; //right-above case 4: if (pathbeginpoint.X - distMax > x0) { continue; } break; //left case 5: if (pathbeginpoint.X + distMax < x1) { continue; } break; //right case 6: if (pathbeginpoint.X - distMax > x0 && pathbeginpoint.Y + distMax < y1) { continue; } break; //left-below case 7: if (pathbeginpoint.Y + distMax < y1) { continue; } break; //below case 8: if (pathbeginpoint.X + distMax < x1 && pathbeginpoint.Y + distMax < y1) { continue; } break; //right-below } List <SegmentTilePoint> sgidx = segTiles[idx + near[near_i]]; foreach (SegmentTilePoint si in sgidx) { if (si.alive && Vector2F.DistanceManhattan(pathbeginpoint, si.p) < distMax) { path.p.AddFirst(si.othersegidx.p); si.alive = false; si.othersegidx.alive = false; segs[si.seg_i] = null; pointadded = true; break; } } } } while (pointadded); } return(paths); }
public static List <SegmentPath> segments_to_paths__sorted_list(List <Segment> segments, float distMax = 1e-10f) { List <SegmentPath> paths = new List <SegmentPath>(); //create a linked list of points sorted by x coordinate List <SegmentPoint> segmentPoints = new List <SegmentPoint>(); foreach (Segment s in segments) { SegmentPoint sp0 = new SegmentPoint(s.p[0], null); SegmentPoint sp1 = new SegmentPoint(s.p[1], sp0); sp0.otherSegmentPoint = sp1; segmentPoints.Add(sp0); segmentPoints.Add(sp1); } segmentPoints.Sort(); for (int i = 0; i < segmentPoints.Count - 1; i++) { segmentPoints[i].next = segmentPoints[i + 1]; } for (int i = 1; i < segmentPoints.Count; i++) { segmentPoints[i].prev = segmentPoints[i - 1]; } //process each segment foreach (SegmentPoint spMainLoop in segmentPoints) { //only process segments that are alive if (!spMainLoop.alive) { continue; } //start a new path with this segment spMainLoop.alive = false; spMainLoop.otherSegmentPoint.alive = false; SegmentPath path = new SegmentPath(spMainLoop.point, spMainLoop.otherSegmentPoint.point); paths.Add(path); //append/prepend points to path SegmentPoint spPath; //the current path point to append/prepend to SegmentPoint spTry; //the point that is examined to be appended/prepended bool found; //find points to APPEND to path spPath = spMainLoop.otherSegmentPoint; //APPEND (last point of path) do { found = false; //go FORWARD through the sorted list spTry = spPath.next; //FORWARD while (spTry != null && spTry.X - spPath.X <= distMax) //FORWARD try.x >= path.x { if (spTry.alive && Vector2F.DistanceManhattan(spPath.point, spTry.point) <= distMax) { spTry.alive = false; spTry.otherSegmentPoint.alive = false; spPath = spTry.otherSegmentPoint; path.p.AddLast(spPath.point); //APPEND found = true; break; } else { spTry = spTry.next; //FORWARD } } //if nothing found, then go BACKWARD through the sorted list if (!found) { spTry = spPath.prev; //BACKWARD while (spTry != null && spPath.X - spTry.X <= distMax) //BACKWARD path.x >= try.x { if (spTry.alive && Vector2F.DistanceManhattan(spPath.point, spTry.point) <= distMax) { spTry.alive = false; spTry.otherSegmentPoint.alive = false; spPath = spTry.otherSegmentPoint; path.p.AddLast(spPath.point); //APPEND found = true; break; } else { spTry = spTry.prev; //BACKWARD } } } } while (found); //find points to PREPEND to path spPath = spMainLoop; //PREPEND (first point of path) do { found = false; //go FORWARD through the sorted list spTry = spPath.next; //FORWARD while (spTry != null && spTry.X - spPath.X <= distMax) //FORWARD try.x >= path.x { if (spTry.alive && Vector2F.DistanceManhattan(spPath.point, spTry.point) <= distMax) { spTry.alive = false; spTry.otherSegmentPoint.alive = false; spPath = spTry.otherSegmentPoint; path.p.AddFirst(spPath.point); //PREPEND found = true; break; } else { spTry = spTry.next; //FORWARD } } //if nothing found, then go BACKWARD through the sorted list if (!found) { spTry = spPath.prev; //BACKWARD while (spTry != null && spPath.X - spTry.X <= distMax) //BACKWARD path.x >= try.x { if (spTry.alive && Vector2F.DistanceManhattan(spPath.point, spTry.point) <= distMax) { spTry.alive = false; spTry.otherSegmentPoint.alive = false; spPath = spTry.otherSegmentPoint; path.p.AddFirst(spPath.point); //PREPEND found = true; break; } else { spTry = spTry.prev; //BACKWARD } } } } while (found); } return(paths); }