Пример #1
0
    //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);
    }
Пример #2
0
 public SegmentTilePoint(int seg_i, Vector2F p, SegmentTilePoint othersegidx)
 {
     this.seg_i       = seg_i;
     this.p           = p;
     this.othersegidx = othersegidx;
 }