//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 SegmentTilePoint(int seg_i, Vector2F p, SegmentTilePoint othersegidx) { this.seg_i = seg_i; this.p = p; this.othersegidx = othersegidx; }