全覆盖路径规划算法之BCD源码实现(The Boustrophedon Cellular Decomposition)

一、前言

这两天情绪很失控,但越是这种情况下越要保持理智,冷静面对问题、努力解决问题、坦然接受任何结果。

言归正传,BCD覆盖规划算法并不是一个新算法,早在2000年就被提出,目前很多覆盖类的机器人都用到了此算法,例如常见的家用扫地机、火爆国外的智能割草机、不温不火的商用清洁都逃不开这个算法。

论文名称:

Choset H .Coverage of Known Spaces: The Boustrophedon Cellular Decomposition[J].Autonomous Robots, 2000, 9(3):247-253.DOI:10.1023/A:1008958800904.

论文链接:

https://www.ri.cmu.edu/pub_files/pub4/choset_howie_2000_3/choset_howie_2000_3.pdf

二、仿真视频

视频最能直观感受

BCD-demo

三、算法原理

对于原理不做过多的解释,推荐三篇认为不错的博客、文章。

https://blog.csdn.net/jiayoushijie/article/details/139535908

https://zhuanlan.zhihu.com/p/573401895

https://zhuanlan.zhihu.com/p/690024574

四、源码

源码的主体来自博主 RicheyHuang,我本人只进行了少量修改、注释。

站在大佬的肩膀上可以走的更远更快,开源本身能够加快技术发展、技术落地,这里希望能够给予机器人算法方向从业者一丝帮助。

项目源码链接:https://github.com/lijun-mce/Coverage_Path_Planning

代码只依赖OpenCV库,非常适合做工程化移植。

cpp 复制代码
/**
 * @author lijun
 * @date 2025-1-1
 * @brief Coverage Path Planning  test
 */

#include "../include/coverage_planner.hpp"
using namespace std;
using namespace coverageplanner;

int main()
{
    cv::Mat1b map = cv::imread("../data/test.png", CV_8U);
    if (map.empty())
    {
        cout << " get map failed! " << endl;
        return 0;
    }
    else
    {
        CoveragePlanner cover_test;
        int robot_radius = 4;
        // 一般情况下 弓字间距 小于或者等于 2*robot_radius
        int clean_distance = 8;
        std::deque<Point2D> cover_path = cover_test.planner(map, robot_radius, clean_distance);
    }
    return 0;
}
cpp 复制代码
/**
 * @author lijun
 * @date 2025-1-1
 * @brief Coverage Path Planning Using BCD Algorithm
 * 源代码是借鉴 Richey Huang  进行了少量了修改整理!
 * 站在前人的肩膀上可以走的更快!!!
 */
#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
#include <numeric>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

namespace coverageplanner
{

    const int TOP_LEFT = 0;     // 左上角
    const int BOTTOM_LEFT = 1;  // 左下角
    const int BOTTOM_RIGHT = 2; // 右下角
    const int TOP_RIGHT = 3;    // 右上角

    // 用于存储边界(外边界、障碍物边界)点集属性
    enum EventType
    {
        IN,
        IN_TOP,
        IN_BOTTOM,
        OUT,
        OUT_TOP,
        OUT_BOTTOM,

        INNER_IN,
        INNER_IN_TOP,
        INNER_IN_BOTTOM,
        INNER_OUT,
        INNER_OUT_TOP,
        INNER_OUT_BOTTOM,

        IN_EX,
        IN_TOP_EX,
        IN_BOTTOM_EX,
        OUT_EX,
        OUT_TOP_EX,
        OUT_BOTTOM_EX,
        INNER_IN_EX,
        INNER_IN_TOP_EX,
        INNER_IN_BOTTOM_EX,
        INNER_OUT_EX,
        INNER_OUT_TOP_EX,
        INNER_OUT_BOTTOM_EX,

        MIDDLE,
        CEILING,
        FLOOR,
        UNALLOCATED // 未分配的(主要是用于初始化)
    };

    // 定义二维点集
    class Point2D
    {
    public:
        Point2D()
        {
            x = INT_MAX;
            y = INT_MAX;
        }
        Point2D(int x_pos, int y_pos)
        {
            x = x_pos;
            y = y_pos;
        }
        Point2D(const Point2D &point)
        {
            x = point.x;
            y = point.y;
        }
        int x;
        int y;

        
        bool operator<(const Point2D &p2) const
        {
            return (this->x < p2.x || (this->x == p2.x && this->y < p2.y));
        }

        bool operator==(const Point2D &p2) const
        {
            return (this->x == p2.x && this->y == p2.y);
        }

        bool operator!=(const Point2D &p2) const
        {
            return !(*this == p2);
        }
    };

    /** 多边形顶点按照逆时针旋转排序 **/
    typedef std::vector<Point2D> Polygon;
    typedef std::vector<Polygon> PolygonList;
    typedef std::deque<Point2D> Edge;

    // 存放所有轮廓点集
    class Event
    {
    public:
        Event(int obstacle_idx, int x_pos, int y_pos, EventType type = UNALLOCATED)
        {
            obstacle_index = obstacle_idx;
            x = x_pos;
            y = y_pos;
            event_type = type;
            original_index_in_slice = INT_MAX;
            isUsed = false;
        }

        int x;
        int y;
        int original_index_in_slice; // 原始索引
        int obstacle_index;
        EventType event_type;
        bool isUsed; // 是否使用过

        bool operator<(const Event &e2)
        {
            return (this->x < e2.x || (this->x == e2.x && this->y < e2.y) || (this->x == e2.x && this->y == e2.y && this->obstacle_index < e2.obstacle_index));
        }
    };
    // 也可以用仿函数实现类中运算符重载的功能
    // bool operator<(const Event &e1, const Event &e2)
    // {
    //     return (e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y) || (e1.x == e2.x && e1.y == e2.y && e1.obstacle_index < e2.obstacle_index));
    // }

    class CellNode
    {
    public:
        CellNode()
        {
            isVisited = false;
            isCleaned = false;
            parentIndex = INT_MAX;
            cellIndex = INT_MAX;
        }
        bool isVisited;
        bool isCleaned;
        Edge ceiling; // 天花板点集
        Edge floor;   // 地平面点集
        int parentIndex;
        std::deque<int> neighbor_indices;
        int cellIndex;
    };

    /**
     * @brief 覆盖规划算法的核心
     *
     */
    class CoveragePlanner
    {
    public:
        /**
         * @brief clean_map 需要清洁覆盖的地图
         * @param robot_radius 机器人半径(默认是圆形)
         * @param clean_distance 清扫间距(覆盖弓字形半径)
         * @return std::deque<Point2D> 路径点集
         */
        std::deque<Point2D> planner(const cv::Mat &clean_map, int robot_radius, int clean_distance);

        ///
    private:
        /**
         * @brief 提取障碍物轮廓 和 外边界轮廓
         * @param original_map 原始地图
         * @param wall_contours 沿边轮廓(外)
         * @param obstacle_contours 障碍物轮廓(内)
         * @param robot_radius 机器人半径(用于膨胀地图)
         * @return void
         */
        void extractContours(const cv::Mat &original_map, std::vector<std::vector<cv::Point>> &wall_contours, std::vector<std::vector<cv::Point>> &obstacle_contours, int robot_radius = 0);

        /**
         * @brief 显示提取到的轮廓
         * @param map 原始底图
         * @param contours 需要显示的轮廓
         * @return void
         */
        void showExtractedContours(const cv::Mat &map, const std::vector<std::vector<cv::Point>> &contours);

        /**
         * @brief 将障碍物点集密集化
         * @param original_map 原始地图
         * @param obstacle_contours 障碍物轮廓
         * @return PolygonList 密集点轮廓
         */
        PolygonList constructObstacles(const cv::Mat &original_map, const std::vector<std::vector<cv::Point>> &obstacle_contours);

        /**
         * @brief 将外轮廓点集密集化
         * @param original_map 原始地图
         * @param wall_contour 墙体轮廓
         * @return Polygon 密集墙体轮廓
         */
        Polygon constructWall(const cv::Mat &original_map, std::vector<cv::Point> &wall_contour);

        // 得到区域分解轮廓
        /**
         * @brief 得到区域分解轮廓
         * @param wall_contours 墙体轮廓
         * @param obstacle_contours 障碍物轮廓
         * @param wall 密集墙体
         * @param obstacles 密集障碍物
         * @return std::vector<CellNode> 区域轮廓集合
         */
        std::vector<CellNode> constructCellGraph(const cv::Mat &original_map, const std::vector<std::vector<cv::Point>> &wall_contours, const std::vector<std::vector<cv::Point>> &obstacle_contours, const Polygon &wall, const PolygonList &obstacles);

        /**
         * @brief 直行区域分解的具体函数
         * @param cell_graph 引用返回计算结果
         * @param slice_list 以相同x为一组的特征点
         * @return void
         */
        void executeCellDecomposition(std::vector<CellNode> &cell_graph, const std::deque<std::deque<Event>> &slice_list);

        // 一个简单的条件计数器
        int countCells(const std::deque<Event> &slice, int curr_idx);
         ============== bcd 分解过程 ==============
        void executeOpenOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, Point2D in, Point2D c, Point2D f, bool rewrite = false);

        void executeOpenOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, Point2D in_top, Point2D in_bottom, Point2D c, Point2D f, bool rewrite = false);

        void executeFloorOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, const Point2D &floor_point);

        void executeCeilOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, const Point2D &ceil_point);

        void executeInnerCloseOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, Point2D inner_out);

        void executeInnerCloseOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, Point2D inner_out_top, Point2D inner_out_bottom);

        void executeInnerOpenOperation(std::vector<CellNode> &cell_graph, Point2D inner_in);

        void executeInnerOpenOperation(std::vector<CellNode> &cell_graph, Point2D inner_in_top, Point2D inner_in_bottom);

        void executeCloseOperation(std::vector<CellNode> &cell_graph, int top_cell_idx, int bottom_cell_idx, Point2D c, Point2D f, bool rewrite = false);

        /**
         * @brief 生产障碍物轮廓点集的事件类型
         * @param map 原始地图
         * @param polygons 障碍物轮廓点集
         * @return std::vector<Event> 障碍物关键点的属性
         */
        std::vector<Event> generateObstacleEventList(const cv::Mat &map, const PolygonList &polygons);

        /**
         * @brief 生产墙体点集的事件类型
         * @param map 原始地图
         * @param external_contour 墙体轮廓点集
         * @return std::vector<Event> 墙体关键点的属性
         */
        std::vector<Event> generateWallEventList(const cv::Mat &map, const Polygon &external_contour);

        /**
         * @brief 对轮廓的属性进行标记(障碍物、边界)
         * @param map 原始地图
         * @param event_list 初始化的属性
         * @return void
         */
        void allocateWallEventType(const cv::Mat &map, std::vector<Event> &event_list);
        void allocateObstacleEventType(const cv::Mat &map, std::vector<Event> &event_list);

        /**
         * @brief 初始化轮廓
         * @param polygon 轮廓
         * @param polygon_index 轮廓需要被标记的序号
         * @return std::vector<Event> 轮廓的属性
         */
        std::vector<Event> initializeEventList(const Polygon &polygon, int polygon_index);

        /**
         * @brief 对所有点集进行切片分类 (以x相同作为同一组数据)
         * @param wall_event_list 所有墙体特征点集
         * @param obstacle_event_list 所有障碍物特征点集
         * @return std::deque<std::deque<Event>> 分类好的点集序列
         */
        std::deque<std::deque<Event>> sliceListGenerator(const std::vector<Event> &wall_event_list, const std::vector<Event> &obstacle_event_list);

        /**
         * @brief 测试函数 画出障碍物关键点类型
         * @param map 原始地图
         * @param obstacle 障碍物轮廓点
         * @return void
         */
        void drawObstaclePointType(cv::Mat &map, const Polygon &obstacle);

        /**
         * @brief 测试函数 画出墙体关键点类型
         * @param map 原始地图
         * @param wall 墙体类型
         * @return void
         */
        void drawWallPointType(cv::Mat &map, const Polygon &wall);

        /**
         * @brief  滤除中间和处于初始状态的分组点集
         * @param slice 需要过滤的点集
         * @return std::deque<Event> 返回过滤后的点集
         */
        std::deque<Event> filterSlice(const std::deque<Event> &slice);

        /**
         * @brief 测试代码 显示障碍物 and 墙体的点集类型
         * @param map 地图
         * @param wall 墙体轮廓
         * @param obstacles 障碍物轮廓
         * @return void
         */
        void checkPointType(const cv::Mat &map, const Polygon &wall, const PolygonList &obstacles);

        /**
         * @brief 测试代码 显示分割区域轮廓
         * @param map 地图
         * @param cell_graph 区域轮廓点集
         * @return void
         */
        void checkGeneratedCells(const cv::Mat &map, const std::vector<CellNode> &cell_graph);

        /**
         * @brief 画出区域轮廓
         * @param map 底图
         * @param cell 轮廓
         * @param color 画图的颜色
         * @return void
         */
        void drawCells(cv::Mat &map, const CellNode &cell, cv::Scalar color = cv::Scalar(100, 100, 100));

        /**
         * @brief 寻找覆盖起点所对应的bcd分解轮廓id
         * @param cell_graph 分解得到的所有轮廓
         * @param point 起点
         * @return std::vector<int> 虽然返回的是Vector,但实际上只使用Vector.front()
         */
        std::vector<int> determineCellIndex(std::vector<CellNode> &cell_graph, const Point2D &point);

        /**
         * @brief 计算bcd分区轮廓的四个角点
         * @param cell 区域轮廓
         * @return std::vector<Point2D> 返回四个角点轮廓
         */
        std::vector<Point2D> computeCellCornerPoints(const CellNode &cell);

        /**
         * @brief 得到分解轮廓的访问顺序(深度优先遍历)
         * @param cell_graph 所有需要访问的轮廓
         * @param cell_index 初始轮廓
         * @param unvisited_counter 轮廓数量
         * @param path 轮廓的访问顺序(以引用的方式输出)
         */
        void walkThroughGraph(std::vector<CellNode> &cell_graph, int cell_index, int &unvisited_counter, std::deque<CellNode> &path);

        /**
         * @brief 得到区域访问顺序(实际上是调用 walkThroughGraph 函数得到 )
         * @param cell_graph 所有需要访问的轮廓
         * @param first_cell_index 第一个访问的轮廓
         * @return std::deque<CellNode> 轮廓的访问顺序
         */
        std::deque<CellNode> getVisittingPath(std::vector<CellNode> &cell_graph, int first_cell_index);

        /**
         * @brief 初始化颜色队列
         * @param color_deque 以引用的形式输出颜色队列(输出)
         * @param repeat_times 同一个颜色重复次数
         * @return void
         */
        void initializeColorMap(std::deque<cv::Scalar> &color_deque, int repeat_times);

        /**
         * @brief 起点至起始轮廓第一个点 中规划的路径
         * @param cell 覆盖轮廓
         * @param start 起点
         * @param end 终点
         * @return std::deque<Point2D> 路径
         */
        std::deque<Point2D> walkInsideCell(CellNode cell, const Point2D &start, const Point2D &end);

        /**
         * @brief 计算连接路径
         * @param curr_exit 当前出口
         * @param next_entrance 下一个轮廓入口
         * @param corner_indicator 参考角点
         * @param curr_cell 当前轮廓
         * @param next_cell 下个要连接的轮廓
         * @return std::deque<std::deque<Point2D>> 计算得到的路径点
         */
        std::deque<std::deque<Point2D>> findLinkingPath(const Point2D &curr_exit, Point2D &next_entrance, int &corner_indicator, CellNode curr_cell, const CellNode &next_cell);

        /**
         * @brief 得到弓字切割路径
         * @param cell_graph 分割得到的所有轮廓
         * @param cell 当前需要规划的轮廓
         * @param corner_indicator 参考角点
         * @param robot_radius 规划半径
         * @return std::deque<Point2D> 输出弓字型路径
         */
        std::deque<Point2D> getBoustrophedonPath(std::vector<CellNode> &cell_graph, CellNode cell, int corner_indicator, int robot_radius);

        /**
         * @brief 寻找下个弓字区域的入口(起点)
         * @param curr_point 当前点
         * @param next_cell 下个区域轮廓
         * @param corner_indicator 参考角点
         * @return Point2D 下个弓字形 起点
         */
        Point2D findNextEntrance(const Point2D &curr_point, const CellNode &next_cell, int &corner_indicator);

        /**
         * @brief 计算整个区域的弓字形路径
         * @param cell_graph 所有分解得到的区域
         * @param start_point 起点
         * @param robot_radius 弓字形间距
         * @return std::deque<std::deque<Point2D>> 整个区域弓字形路径
         */
        std::deque<std::deque<Point2D>> staticPathPlanning(std::vector<CellNode> &cell_graph, const Point2D &start_point, int robot_radius);

        /**
         * @brief 打印所有弓字形路径
         * @param path 路径点集
         * @return void
         */
        void printPathNodes(const std::deque<std::deque<Point2D>> &path);

        /**
         * @brief 过滤弓字形路径(只做了一件事,去重)
         * @param raw_trajectory 原始路径
         * @return std::deque<Point2D> 过滤后的路径
         */
        std::deque<Point2D> filterTrajectory(const std::deque<std::deque<Point2D>> &raw_trajectory);

        /**
         * @brief 检测路径的一致性(是否有断点 或者 重复点)
         * @param path 弓字形路径
         * @return void
         */
        void checkPathConsistency(const std::deque<Point2D> &path);

        /**
         * @brief 可视化轨迹
         * @param original_map 原始地图
         * @param path 原始路径
         * @param robot_radius 机器人半径
         * @param vis_mode 显示模式
         * @param time_interval、 colors 跟显示颜色变化有关
         * @return void
         */
        void visualizeTrajectory(const cv::Mat &original_map, const std::deque<Point2D> &path, int robot_radius, int vis_mode, int time_interval = 10, int colors = 1530);

        /**
         * @brief 正则化容器的索引值
         * @param index 索引值
         * @param list_length 容器大小
         * @return int 正则化后的索引值
         */
        int wrappedIndex(int index, int list_length);

        /**
         * @brief  更新显示颜色(将颜色队列中的第一个元素放入最后一个)
         * @param  color_deque 颜色队列
         * @return void
         */
        void updateColor(std::deque<cv::Scalar> &color_deque);
    };
}
cpp 复制代码
/**
 * @author lijun
 * @date 2025-1-1
 * @brief Coverage Path Planning Using BCD Algorithm
 * 源代码是借鉴 Richey Huang  进行了少量了修改整理!
 * 站在前人的肩膀上可以走的更快!!!
 */
#include "../include/coverage_planner.hpp"

// 是否打卡过程显示
#define Process_Visual 1

// 可视化显示模式
enum VisualizationMode
{
    PATH_MODE,
    ROBOT_MODE
};

namespace coverageplanner
{
    // 覆盖路径规划器
    std::deque<Point2D> CoveragePlanner::planner(const cv::Mat &clean_map, int robot_radius, int clean_distance)
    {
        if (clean_map.empty())
        {
            std::cout << "Error, clean map is empty!" << std::endl;
            return std::deque<Point2D>();
        }
        else
        {
            cv::Mat1b map = clean_map.clone();
            cv::threshold(map, map, 128, 255, cv::THRESH_BINARY);
            // 外边界轮廓
            std::vector<std::vector<cv::Point>> wall_contours;
            // 障碍物轮廓
            std::vector<std::vector<cv::Point>> obstacle_contours;
            // 提取并进行显示
            extractContours(map, wall_contours, obstacle_contours, robot_radius);
            if (Process_Visual)
            {
                showExtractedContours(map, wall_contours);
                showExtractedContours(map, obstacle_contours);
            }

            // 将障碍物轮廓、边界轮廓进行上采样处理
            PolygonList obstacles = constructObstacles(map, obstacle_contours);
            Polygon wall = constructWall(map, wall_contours.front());
            // 得到区域分解轮廓
            std::vector<CellNode> cell_graph = constructCellGraph(map, wall_contours, obstacle_contours, wall, obstacles);
            if (Process_Visual)
            {
                // checkPointType(map, wall, obstacles);
                checkGeneratedCells(map, cell_graph);
            }

            // 起点为第一个轮廓的第一个天花板点
            Point2D start = cell_graph.front().ceiling.front();
            std::deque<std::deque<Point2D>> original_planning_path = staticPathPlanning(cell_graph, start, clean_distance);
            if (Process_Visual)
            {
                printPathNodes(original_planning_path);
            }

            std::deque<Point2D> path = filterTrajectory(original_planning_path);
            if (Process_Visual)
            {
                checkPathConsistency(path);
                int time_interval = 1;
                visualizeTrajectory(map, path, robot_radius, 0, time_interval);
            }
            std::cout << "path.size(): " << path.size() << std::endl;
            return path;
        }
    }

    void CoveragePlanner::extractContours(const cv::Mat &original_map, std::vector<std::vector<cv::Point>> &wall_contours, std::vector<std::vector<cv::Point>> &obstacle_contours, int robot_radius)
    {

        cv::Mat erode_map = original_map.clone();
        cv::threshold(erode_map, erode_map, 128, 255, cv::THRESH_BINARY);

        if (robot_radius != 0)
        {
            cv::Mat erod_struct = getStructuringElement(cv::MORPH_ELLIPSE, cv::Size((robot_radius * 2 + 1), (robot_radius * 2 + 1)));
            cv::erode(erode_map, erode_map, erod_struct);
        }

        std::vector<std::vector<cv::Point>> contours;
        cv::findContours(erode_map, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
        std::vector<int> wall_cnt_indices(contours.size());
        std::iota(wall_cnt_indices.begin(), wall_cnt_indices.end(), 0);
        std::sort(wall_cnt_indices.begin(), wall_cnt_indices.end(), [&contours](int lhs, int rhs)
                  { return cv::contourArea(contours[lhs]) > cv::contourArea(contours[rhs]); });

        // 找到外轮廓(外轮廓最大)
        std::vector<cv::Point> raw_wall_contour = contours[wall_cnt_indices.front()];
        wall_contours = {raw_wall_contour};

        // 寻找障碍物轮廓
        cv::Mat mask = cv::Mat(erode_map.size(), erode_map.type(), 255);
        cv::fillPoly(mask, wall_contours, 0);
        cv::Mat base = erode_map.clone();
        base += mask;
        // cv::THRESH_BINARY_INV 阈值类型反转,大于等于128会变成黑色,小于128会变成白色
        cv::threshold(base, base, 128, 255, cv::THRESH_BINARY_INV);
        cv::findContours(base, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
        obstacle_contours = contours;

        // 做多边形的拟合
        std::vector<cv::Point> processed_wall_contour;
        cv::approxPolyDP(cv::Mat(wall_contours.front()), processed_wall_contour, 1, true);
        wall_contours = {processed_wall_contour};

        std::vector<std::vector<cv::Point>> processed_obstacle_contours(obstacle_contours.size());
        for (int i = 0; i < obstacle_contours.size(); i++)
        {
            cv::approxPolyDP(cv::Mat(obstacle_contours[i]), processed_obstacle_contours[i], 1, true);
        }
        obstacle_contours = processed_obstacle_contours;
    }

    // 显示提取到的轮廓
    void CoveragePlanner::showExtractedContours(const cv::Mat &map, const std::vector<std::vector<cv::Point>> &contours)
    {
        cv::namedWindow("map", cv::WINDOW_NORMAL);
        cv::Mat3b canvas = cv::Mat3b(map.size(), CV_8U);
        canvas.setTo(cv::Scalar(0, 0, 0));
        for (int i = 0; i <= contours.size() - 1; i++)
        {
            cv::drawContours(canvas, contours, i, cv::Scalar(255, 0, 0));
            cv::imshow("map", canvas);
            cv::waitKey(0);
        }
    }

    // 将障碍物点集密集化
    PolygonList CoveragePlanner::constructObstacles(const cv::Mat &original_map, const std::vector<std::vector<cv::Point>> &obstacle_contours)
    {
        PolygonList obstacles;
        for (const auto &obstacle_contour : obstacle_contours)
        {
            Polygon obstacle;
            int obs_size = obstacle_contour.size();
            for (int j = 0; j < obs_size; j++)
            {
                cv::LineIterator line(original_map, obstacle_contour[j], obstacle_contour[(j + 1) % obs_size]);
                for (int k = 0; k < line.count - 1; k++)
                {
                    obstacle.emplace_back(Point2D(line.pos().x, line.pos().y));
                    line++;
                }
            }

            obstacles.emplace_back(obstacle);
        }
        return obstacles;
    }

    // 将外轮廓点集密集化
    Polygon CoveragePlanner::constructWall(const cv::Mat &original_map, std::vector<cv::Point> &wall_contour)
    {
        Polygon wall;
        if (!wall_contour.empty())
        {
            int wall_size = wall_contour.size();
            for (int i = 0; i < wall_size; i++)
            {
                cv::LineIterator line(original_map, wall_contour[i], wall_contour[(i + 1) % wall_size]);
                for (int j = 0; j < line.count - 1; j++)
                {
                    wall.emplace_back(Point2D(line.pos().x, line.pos().y));
                    line++;
                }
            }
            return wall;
        }
        else
        {
            // 外轮廓为空的情况下使用地图边界
            std::vector<cv::Point> default_wall_contour = {cv::Point(0, 0), cv::Point(0, original_map.rows - 1), cv::Point(original_map.cols - 1, original_map.rows - 1), cv::Point(original_map.cols - 1, 0)};
            std::vector<std::vector<cv::Point>> default_wall_contours = {default_wall_contour};
            wall = constructObstacles(original_map, default_wall_contours).front();
            for (const auto &point : wall)
            {
                wall_contour.emplace_back(cv::Point(point.x, point.y));
            }
            return wall;
        }
    }

    std::vector<CellNode> CoveragePlanner::constructCellGraph(const cv::Mat &original_map, const std::vector<std::vector<cv::Point>> &wall_contours, const std::vector<std::vector<cv::Point>> &obstacle_contours, const Polygon &wall, const PolygonList &obstacles)
    {
        cv::Mat3b map = cv::Mat3b(original_map.size());
        map.setTo(cv::Scalar(0, 0, 0));
        cv::fillPoly(map, wall_contours, cv::Scalar(255, 255, 255));
        cv::fillPoly(map, obstacle_contours, cv::Scalar(0, 0, 0));

        std::vector<Event> obstacle_event_list = generateObstacleEventList(map, obstacles);
        std::vector<Event> wall_event_list = generateWallEventList(map, wall);

        std::deque<std::deque<Event>> slice_list = sliceListGenerator(wall_event_list, obstacle_event_list);

        std::vector<CellNode> cell_graph;
        executeCellDecomposition(cell_graph, slice_list);
        std::cout << "cell_graph.size(): " << cell_graph.size() << std::endl;
        return cell_graph;
    }

    void CoveragePlanner::checkPointType(const cv::Mat &map, const Polygon &wall, const PolygonList &obstacles)
    {
        cv::Mat vis_map = map.clone();
        cv::cvtColor(vis_map, vis_map, cv::COLOR_GRAY2BGR);

        cv::namedWindow("map", cv::WINDOW_NORMAL);

        for (const auto &obstacle : obstacles)
        {
            drawObstaclePointType(vis_map, obstacle);
            cv::imshow("map", vis_map);
            cv::waitKey(0);
            std::cout << std::endl;
        }

        drawWallPointType(vis_map, wall);
        cv::imshow("map", vis_map);
        cv::waitKey(0);
    }

    void CoveragePlanner::checkGeneratedCells(const cv::Mat &map, const std::vector<CellNode> &cell_graph)
    {
        cv::Mat vis_map = map.clone();
        cv::cvtColor(vis_map, vis_map, cv::COLOR_GRAY2BGR);

        cv::namedWindow("map", cv::WINDOW_NORMAL);

        for (const auto &cell : cell_graph)
        {
            drawCells(vis_map, cell, cv::Scalar(255, 0, 255));
            cv::imshow("map", vis_map);
            cv::waitKey(0);
        }
    }

    std::deque<std::deque<Point2D>> CoveragePlanner::staticPathPlanning(std::vector<CellNode> &cell_graph, const Point2D &start_point, int robot_radius)
    {
        // 寻找起始轮廓id
        int start_cell_index = determineCellIndex(cell_graph, start_point).front();
        // 起点至起始轮廓第一个点 规划的路径
        std::deque<Point2D> init_path = walkInsideCell(cell_graph[start_cell_index], start_point, computeCellCornerPoints(cell_graph[start_cell_index])[TOP_LEFT]);
        std::deque<Point2D> local_path;
        local_path.assign(init_path.begin(), init_path.end());

        // 计算所有轮廓的访问顺序
        std::deque<CellNode> cell_path = getVisittingPath(cell_graph, start_cell_index);

        std::deque<Point2D> inner_path;
        std::deque<std::deque<Point2D>> link_path;
        Point2D curr_exit;
        Point2D next_entrance;

        std::deque<int> return_cell_path;
        std::deque<Point2D> return_path;

        int corner_indicator = TOP_LEFT;
        std::deque<std::deque<Point2D>> global_path;

        for (int i = 0; i < cell_path.size(); i++)
        {
            inner_path = getBoustrophedonPath(cell_graph, cell_path[i], corner_indicator, robot_radius);
            local_path.insert(local_path.end(), inner_path.begin(), inner_path.end());
            // 标记为清洁过
            cell_graph[cell_path[i].cellIndex].isCleaned = true;
            if (i < (cell_path.size() - 1))
            {
                curr_exit = inner_path.back();
                // 寻找下一个弓字形入口(起点)
                next_entrance = findNextEntrance(curr_exit, cell_path[i + 1], corner_indicator);
                // 计算当前点(出口) 至 弓字形入口 的 连接路径(跟walkInsideCell功能一致)
                link_path = findLinkingPath(curr_exit, next_entrance, corner_indicator, cell_path[i], cell_path[i + 1]);
                local_path.insert(local_path.end(), link_path.front().begin(), link_path.front().end());
                global_path.emplace_back(local_path);
                local_path.clear();
                local_path.insert(local_path.end(), link_path.back().begin(), link_path.back().end());
            }
        }
        global_path.emplace_back(local_path);

        return global_path;
    }

    std::deque<Point2D> CoveragePlanner::filterTrajectory(const std::deque<std::deque<Point2D>> &raw_trajectory)
    {
        std::deque<Point2D> trajectory;

        for (const auto &sub_trajectory : raw_trajectory)
        {
            for (const auto &position : sub_trajectory)
            {
                if (!trajectory.empty())
                {
                    if (position != trajectory.back())
                    {
                        trajectory.emplace_back(position);
                    }
                }
                else
                {
                    trajectory.emplace_back(position);
                }
            }
        }

        return trajectory;
    }

    void CoveragePlanner::checkPathConsistency(const std::deque<Point2D> &path)
    {
        // 断点
        int breakpoints = 0;
        // 重复点
        int duplicates = 0;

        for (int i = 1; i < path.size(); i++)
        {
            if (std::abs(path[wrappedIndex((i - 1), path.size())].x - path[i].x) > 1 || std::abs(path[wrappedIndex((i - 1), path.size())].y - path[i].y) > 1)
            {
                breakpoints++;
                std::cout << "break points :" << path[wrappedIndex((i - 1), path.size())].x << ", " << path[wrappedIndex((i - 1), path.size())].y
                          << "---->" << path[i].x << ", " << path[i].y << std::endl;
            }
            if (path[wrappedIndex((i - 1), path.size())] == path[i])
            {
                duplicates++;
            }
        }
        std::cout << "breakpoints: " << breakpoints << std::endl;
        std::cout << "duplicates: " << duplicates << std::endl;
    }

    void CoveragePlanner::visualizeTrajectory(const cv::Mat &original_map, const std::deque<Point2D> &path, int robot_radius, int vis_mode, int time_interval, int colors)
    {
        cv::Mat3b vis_map;
        cv::cvtColor(original_map, vis_map, cv::COLOR_GRAY2BGR);

        cv::namedWindow("map", cv::WINDOW_NORMAL);

        std::deque<cv::Scalar> color_deque;
        int color_repeated_times = path.size() / colors + 1;
        initializeColorMap(color_deque, color_repeated_times);

        switch (vis_mode)
        {
        case PATH_MODE:
            vis_map.at<cv::Vec3b>(path.front().y, path.front().x) = cv::Vec3b(uchar(color_deque.front()[0]), uchar(color_deque.front()[1]), uchar(color_deque.front()[2]));
            cv::imshow("map", vis_map);
            cv::waitKey(0);

            for (const auto &position : path)
            {
                vis_map.at<cv::Vec3b>(position.y, position.x) = cv::Vec3b(uchar(color_deque.front()[0]), uchar(color_deque.front()[1]), uchar(color_deque.front()[2]));
                updateColor(color_deque);
                cv::imshow("map", vis_map);
                cv::waitKey(time_interval);
            }
            break;
        case ROBOT_MODE:
            cv::circle(vis_map, cv::Point(path.front().x, path.front().y), robot_radius, cv::Scalar(255, 204, 153), -1);
            cv::imshow("map", vis_map);
            cv::waitKey(0);

            for (const auto &position : path)
            {
                cv::circle(vis_map, cv::Point(position.x, position.y), robot_radius, cv::Scalar(255, 204, 153), -1);
                cv::imshow("map", vis_map);
                cv::waitKey(time_interval);

                cv::circle(vis_map, cv::Point(position.x, position.y), robot_radius, cv::Scalar(255, 229, 204), -1);
            }
            break;
        default:
            break;
        }

        cv::waitKey(0);
    }

    /
    int CoveragePlanner::wrappedIndex(int index, int list_length)
    {
        int wrapped_index = (index % list_length + list_length) % list_length;
        return wrapped_index;
    }

    std::vector<int> CoveragePlanner::determineCellIndex(std::vector<CellNode> &cell_graph, const Point2D &point)
    {
        std::vector<int> cell_index;

        for (int i = 0; i < cell_graph.size(); i++)
        {
            for (int j = 0; j < cell_graph[i].ceiling.size(); j++)
            {
                if (point.x == cell_graph[i].ceiling[j].x && point.y >= cell_graph[i].ceiling[j].y && point.y <= cell_graph[i].floor[j].y)
                {
                    cell_index.emplace_back(int(i));
                }
            }
        }
        return cell_index;
    }

    std::deque<Point2D> CoveragePlanner::walkInsideCell(CellNode cell, const Point2D &start, const Point2D &end)
    {
        std::deque<Point2D> inner_path = {start};
        int start_ceiling_index_offset = start.x - cell.ceiling.front().x;
        int first_ceiling_delta_y = cell.ceiling[start_ceiling_index_offset].y - start.y;
        int end_ceiling_index_offset = end.x - cell.ceiling.front().x;
        int second_ceiling_delta_y = end.y - cell.ceiling[end_ceiling_index_offset].y;

        int start_floor_index_offset = start.x - cell.floor.front().x;
        int first_floor_delta_y = cell.floor[start_floor_index_offset].y - start.y;
        int end_floor_index_offset = end.x - cell.floor.front().x;
        int second_floor_delta_y = end.y - cell.floor[end_floor_index_offset].y;

        // 选择是从天花板开始还是底部开始
        if ((abs(first_ceiling_delta_y) + abs(second_ceiling_delta_y)) < (abs(first_floor_delta_y) + abs(second_floor_delta_y))) // to ceiling
        {
            int first_increment_y = 0;
            if (first_ceiling_delta_y != 0)
            {
                first_increment_y = first_ceiling_delta_y / abs(first_ceiling_delta_y);

                for (int i = 1; i <= abs(first_ceiling_delta_y); i++)
                {
                    inner_path.emplace_back(Point2D(start.x, start.y + (first_increment_y * i)));
                }
            }

            int delta_x = cell.ceiling[end_ceiling_index_offset].x - cell.ceiling[start_ceiling_index_offset].x;
            int increment_x = 0;
            if (delta_x != 0)
            {
                increment_x = delta_x / abs(delta_x);
            }
            for (int i = 0; i < abs(delta_x); i++)
            {
                // 提前转
                if ((cell.ceiling[start_ceiling_index_offset + increment_x * (i + 1)].y - cell.ceiling[start_ceiling_index_offset + increment_x * (i)].y >= 2) && (i + 1 <= abs(delta_x)) && (i <= abs(delta_x)))
                {
                    int delta = cell.ceiling[start_ceiling_index_offset + increment_x * (i + 1)].y - cell.ceiling[start_ceiling_index_offset + increment_x * (i)].y;
                    int increment = delta / abs(delta);
                    for (int j = 0; j <= abs(delta); j++)
                    {
                        inner_path.emplace_back(Point2D(cell.ceiling[start_ceiling_index_offset + increment_x * i].x, cell.ceiling[start_ceiling_index_offset + increment_x * i].y + increment * (j)));
                    }
                }
                // 滞后转
                else if ((cell.ceiling[start_ceiling_index_offset + increment_x * (i)].y - cell.ceiling[start_ceiling_index_offset + increment_x * (i + 1)].y >= 2) && (i <= abs(delta_x)) && (i + 1 <= abs(delta_x)))
                {
                    inner_path.emplace_back(cell.ceiling[start_ceiling_index_offset + increment_x * (i)]);

                    int delta = cell.ceiling[start_ceiling_index_offset + increment_x * (i + 1)].y - cell.ceiling[start_ceiling_index_offset + increment_x * (i)].y;

                    int increment = delta / abs(delta);
                    for (int k = 0; k <= abs(delta); k++)
                    {
                        inner_path.emplace_back(Point2D(cell.ceiling[start_ceiling_index_offset + increment_x * (i + 1)].x, cell.ceiling[start_ceiling_index_offset + increment_x * (i + 1)].y + abs(delta) + increment * (k)));
                    }
                }
                else
                {
                    inner_path.emplace_back(cell.ceiling[start_ceiling_index_offset + (increment_x * i)]);
                }
            }

            int second_increment_y = 0;
            if (second_ceiling_delta_y != 0)
            {
                second_increment_y = second_ceiling_delta_y / abs(second_ceiling_delta_y);

                for (int i = 1; i <= abs(second_ceiling_delta_y); i++)
                {
                    inner_path.emplace_back(Point2D(cell.ceiling[end_ceiling_index_offset].x, cell.ceiling[end_ceiling_index_offset].y + (second_increment_y * i)));
                }
            }
        }
        else // to floor
        {
            int first_increment_y = 0;
            if (first_floor_delta_y != 0)
            {
                first_increment_y = first_floor_delta_y / abs(first_floor_delta_y);

                for (int i = 1; i <= abs(first_floor_delta_y); i++)
                {
                    inner_path.emplace_back(Point2D(start.x, start.y + (first_increment_y * i)));
                }
            }

            int delta_x = cell.floor[end_floor_index_offset].x - cell.floor[start_floor_index_offset].x;
            int increment_x = 0;
            if (delta_x != 0)
            {
                increment_x = delta_x / abs(delta_x);
            }
            for (int i = 0; i < abs(delta_x); i++)
            {
                // 提前转
                if ((cell.floor[start_floor_index_offset + increment_x * (i)].y - cell.floor[start_floor_index_offset + increment_x * (i + 1)].y >= 2) && (i <= abs(delta_x)) && (i + 1 <= abs(delta_x)))
                {
                    int delta = cell.floor[start_floor_index_offset + increment_x * (i + 1)].y - cell.floor[start_floor_index_offset + increment_x * (i)].y;
                    int increment = delta / abs(delta);
                    for (int j = 0; j <= abs(delta); j++)
                    {
                        inner_path.emplace_back(Point2D(cell.floor[start_floor_index_offset + increment_x * (i)].x, cell.floor[start_floor_index_offset + increment_x * (i)].y + increment * (j)));
                    }
                }
                // 滞后转
                else if ((cell.floor[start_floor_index_offset + increment_x * (i + 1)].y - cell.floor[start_floor_index_offset + increment_x * (i)].y >= 2) && (i + 1 <= abs(delta_x)) && (i <= abs(delta_x)))
                {
                    inner_path.emplace_back(Point2D(cell.floor[start_floor_index_offset + increment_x * (i)].x, cell.floor[start_floor_index_offset + increment_x * (i)].y));

                    int delta = cell.floor[start_floor_index_offset + increment_x * (i + 1)].y - cell.floor[start_floor_index_offset + increment_x * (i)].y;

                    int increment = delta / abs(delta);
                    for (int k = 0; k <= abs(delta); k++)
                    {
                        inner_path.emplace_back(Point2D(cell.floor[start_floor_index_offset + increment_x * (i + 1)].x, cell.floor[start_floor_index_offset + increment_x * (i + 1)].y - abs(delta) + increment * (k)));
                    }
                }
                else
                {
                    inner_path.emplace_back(cell.floor[start_floor_index_offset + (increment_x * i)]);
                }
            }

            int second_increment_y = 0;
            if (second_floor_delta_y != 0)
            {
                second_increment_y = second_floor_delta_y / abs(second_floor_delta_y);

                for (int i = 1; i <= abs(second_floor_delta_y); i++)
                {
                    inner_path.emplace_back(Point2D(cell.floor[end_floor_index_offset].x, cell.floor[end_floor_index_offset].y + (second_increment_y * i)));
                }
            }
        }
        return inner_path;
    }

    void CoveragePlanner::walkThroughGraph(std::vector<CellNode> &cell_graph, int cell_index, int &unvisited_counter, std::deque<CellNode> &path)
    {
        if (!cell_graph[cell_index].isVisited)
        {
            cell_graph[cell_index].isVisited = true;
            unvisited_counter--;
        }
        path.emplace_front(cell_graph[cell_index]);

        CellNode neighbor;
        int neighbor_idx = INT_MAX;

        for (int i = 0; i < cell_graph[cell_index].neighbor_indices.size(); i++)
        {
            neighbor = cell_graph[cell_graph[cell_index].neighbor_indices[i]];
            neighbor_idx = cell_graph[cell_index].neighbor_indices[i];
            if (!neighbor.isVisited)
            {
                break;
            }
        }

        if (!neighbor.isVisited) // unvisited neighbor found
        {
            cell_graph[neighbor_idx].parentIndex = cell_graph[cell_index].cellIndex;
            walkThroughGraph(cell_graph, neighbor_idx, unvisited_counter, path);
        }
        else // unvisited neighbor not found
        {

            if (cell_graph[cell_index].parentIndex == INT_MAX) // cannot go on back-tracking
            {
                return;
            }
            else if (unvisited_counter == 0)
            {
                return;
            }
            else
            {
                walkThroughGraph(cell_graph, cell_graph[cell_index].parentIndex, unvisited_counter, path);
            }
        }
    }

    std::deque<CellNode> CoveragePlanner::getVisittingPath(std::vector<CellNode> &cell_graph, int first_cell_index)
    {
        std::deque<CellNode> visitting_path;

        if (cell_graph.size() == 1)
        {
            visitting_path.emplace_back(cell_graph.front());
        }
        else
        {
            int unvisited_counter = cell_graph.size();
            walkThroughGraph(cell_graph, first_cell_index, unvisited_counter, visitting_path);
            std::reverse(visitting_path.begin(), visitting_path.end());
        }

        return visitting_path;
    }

    void CoveragePlanner::drawCells(cv::Mat &map, const CellNode &cell, cv::Scalar color)
    {
        std::cout << "cell " << cell.cellIndex << ": " << std::endl;
        std::cout << "cell's ceiling points: " << cell.ceiling.size() << std::endl;
        std::cout << "cell's floor points: " << cell.floor.size() << std::endl;

        for (const auto &ceiling_point : cell.ceiling)
        {
            map.at<cv::Vec3b>(ceiling_point.y, ceiling_point.x) = cv::Vec3b(255, 0, 0);
        }

        for (const auto &floor_point : cell.floor)
        {
            map.at<cv::Vec3b>(floor_point.y, floor_point.x) = cv::Vec3b(255, 0, 0);
        }

        cv::line(map, cv::Point(cell.ceiling.front().x, cell.ceiling.front().y), cv::Point(cell.floor.front().x, cell.floor.front().y), color);
        cv::line(map, cv::Point(cell.ceiling.back().x, cell.ceiling.back().y), cv::Point(cell.floor.back().x, cell.floor.back().y), color);
    }

    void CoveragePlanner::updateColor(std::deque<cv::Scalar> &color_deque)
    {
        cv::Scalar color = color_deque.front();
        color_deque.pop_front();
        color_deque.emplace_back(color);
    }

    std::vector<Point2D> CoveragePlanner::computeCellCornerPoints(const CellNode &cell)
    {
        // 顶左 顶右
        Point2D topleft = cell.ceiling.front();
        Point2D topright = cell.ceiling.back();
        // 底左 底右
        Point2D bottomleft = cell.floor.front();
        Point2D bottomright = cell.floor.back();
        // 按照TOPLEFT、BOTTOMLEFT、BOTTOMRIGHT、TOPRIGHT的顺序储存corner points(逆时针)
        std::vector<Point2D> corner_points = {topleft, bottomleft, bottomright, topright};
        return corner_points;
    }

    Point2D CoveragePlanner::findNextEntrance(const Point2D &curr_point, const CellNode &next_cell, int &corner_indicator)
    {
        Point2D next_entrance;

        int front_x = next_cell.ceiling.front().x;
        int back_x = next_cell.ceiling.back().x;

        std::vector<Point2D> corner_points = computeCellCornerPoints(next_cell);

        if (abs(curr_point.x - front_x) < abs(curr_point.x - back_x))
        {
            if (abs(curr_point.y - next_cell.ceiling.front().y) < abs(curr_point.y - next_cell.floor.front().y))
            {
                next_entrance = corner_points[TOP_LEFT];
                corner_indicator = TOP_LEFT;
            }
            else
            {
                next_entrance = corner_points[BOTTOM_LEFT];
                corner_indicator = BOTTOM_LEFT;
            }
        }
        else
        {
            if (abs(curr_point.y - next_cell.ceiling.back().y) < abs(curr_point.y - next_cell.floor.back().y))
            {
                next_entrance = corner_points[TOP_RIGHT];
                corner_indicator = TOP_RIGHT;
            }
            else
            {
                next_entrance = corner_points[BOTTOM_RIGHT];
                corner_indicator = BOTTOM_RIGHT;
            }
        }

        return next_entrance;
    }

    std::deque<std::deque<Point2D>> CoveragePlanner::findLinkingPath(const Point2D &curr_exit, Point2D &next_entrance, int &corner_indicator, CellNode curr_cell, const CellNode &next_cell)
    {
        std::deque<std::deque<Point2D>> path;
        std::deque<Point2D> path_in_curr_cell;
        std::deque<Point2D> path_in_next_cell;

        int exit_corner_indicator = INT_MAX;
        Point2D exit = findNextEntrance(next_entrance, curr_cell, exit_corner_indicator);
        path_in_curr_cell = walkInsideCell(curr_cell, curr_exit, exit);

        next_entrance = findNextEntrance(exit, next_cell, corner_indicator);

        int delta_x = next_entrance.x - exit.x;
        int delta_y = next_entrance.y - exit.y;

        int increment_x = 0;
        int increment_y = 0;

        if (delta_x != 0)
        {
            increment_x = delta_x / std::abs(delta_x);
        }
        if (delta_y != 0)
        {
            increment_y = delta_y / std::abs(delta_y);
        }

        int upper_bound = INT_MIN;
        int lower_bound = INT_MAX;

        if (exit.x >= curr_cell.ceiling.back().x)
        {
            upper_bound = curr_cell.ceiling.back().y;
            lower_bound = curr_cell.floor.back().y;
        }
        if (exit.x <= curr_cell.ceiling.front().x)
        {
            upper_bound = curr_cell.ceiling.front().y;
            lower_bound = curr_cell.floor.front().y;
        }

        if ((next_entrance.y >= upper_bound) && (next_entrance.y <= lower_bound))
        {
            for (int y = exit.y; y != next_entrance.y; y += increment_y)
            {
                path_in_curr_cell.emplace_back(Point2D(exit.x, y));
            }
            for (int x = exit.x; x != next_entrance.x; x += increment_x)
            {
                path_in_curr_cell.emplace_back(Point2D(x, next_entrance.y));
            }
        }
        else
        {
            for (int x = exit.x; x != next_entrance.x; x += increment_x)
            {
                path_in_curr_cell.emplace_back(Point2D(x, exit.y));
            }
            for (int y = exit.y; y != next_entrance.y; y += increment_y)
            {
                path_in_next_cell.emplace_back(Point2D(next_entrance.x, y));
            }
        }

        path = {path_in_curr_cell, path_in_next_cell};

        return path;
    }

    void CoveragePlanner::initializeColorMap(std::deque<cv::Scalar> &color_deque, int repeat_times)
    {
        for (int i = 0; i <= 255; i++)
        {
            for (int j = 0; j < repeat_times; j++)
            {
                color_deque.emplace_back(cv::Scalar(0, i, 255));
            }
        }

        for (int i = 254; i >= 0; i--)
        {
            for (int j = 0; j < repeat_times; j++)
            {
                color_deque.emplace_back(cv::Scalar(0, 255, i));
            }
        }

        for (int i = 1; i <= 255; i++)
        {
            for (int j = 0; j < repeat_times; j++)
            {
                color_deque.emplace_back(cv::Scalar(i, 255, 0));
            }
        }

        for (int i = 254; i >= 0; i--)
        {
            for (int j = 0; j < repeat_times; j++)
            {
                color_deque.emplace_back(cv::Scalar(255, i, 0));
            }
        }

        for (int i = 1; i <= 255; i++)
        {
            for (int j = 0; j < repeat_times; j++)
            {
                color_deque.emplace_back(cv::Scalar(255, 0, i));
            }
        }

        for (int i = 254; i >= 1; i--)
        {
            for (int j = 0; j < repeat_times; j++)
            {
                color_deque.emplace_back(cv::Scalar(i, 0, 255));
            }
        }
    }

    std::deque<Point2D> CoveragePlanner::getBoustrophedonPath(std::vector<CellNode> &cell_graph, CellNode cell, int corner_indicator, int robot_radius)
    {
        int delta, increment;

        std::deque<Point2D> path;

        std::vector<Point2D> corner_points = computeCellCornerPoints(cell);

        std::vector<Point2D> ceiling, floor;
        ceiling.assign(cell.ceiling.begin(), cell.ceiling.end());
        floor.assign(cell.floor.begin(), cell.floor.end());

        if (cell_graph[cell.cellIndex].isCleaned)
        {
            if (corner_indicator == TOP_LEFT)
            {
                path.emplace_back(corner_points[TOP_LEFT]);
            }
            if (corner_indicator == TOP_RIGHT)
            {
                path.emplace_back(corner_points[TOP_RIGHT]);
            }
            if (corner_indicator == BOTTOM_LEFT)
            {
                path.emplace_back(corner_points[BOTTOM_LEFT]);
            }
            if (corner_indicator == BOTTOM_RIGHT)
            {
                path.emplace_back(corner_points[BOTTOM_RIGHT]);
            }
        }
        else
        {
            if (corner_indicator == TOP_LEFT)
            {
                int x, y, y_start, y_end;
                bool reverse = false;

                for (int i = 0; i < ceiling.size(); i = i + (robot_radius + 1))
                {
                    x = ceiling[i].x;

                    if (!reverse)
                    {
                        y_start = ceiling[i].y;
                        y_end = floor[i].y;

                        for (y = y_start; y <= y_end; y++)
                        {
                            path.emplace_back(Point2D(x, y));
                        }

                        if ((std::abs(floor[i + 1].y - floor[i].y) >= 2) && (i + 1 < floor.size()))
                        {
                            delta = floor[i + 1].y - floor[i].y;
                            increment = delta / abs(delta);
                            for (int k = 1; k <= abs(delta); k++)
                            {
                                path.emplace_back(Point2D(floor[i].x, floor[i].y + increment * (k)));
                            }
                        }

                        if (robot_radius != 0)
                        {
                            for (int j = 1; j <= robot_radius + 1; j++)
                            {
                                // 沿着floor从左往右
                                if (x + j >= floor.back().x)
                                {
                                    i = i - (robot_radius - (j - 1));
                                    break;
                                }

                                // 提前转
                                else if ((floor[i + (j)].y - floor[i + (j + 1)].y >= 2) && (j <= robot_radius + 1) && (j + 1 <= robot_radius + 1))
                                {
                                    delta = floor[i + (j + 1)].y - floor[i + (j)].y;
                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(floor[i + (j)].x, floor[i + (j)].y + increment * (k)));
                                    }
                                }
                                // 滞后转
                                else if ((floor[i + (j + 1)].y - floor[i + (j)].y >= 2) && (j + 1 <= robot_radius + 1) && (j <= robot_radius + 1))
                                {
                                    path.emplace_back(Point2D(floor[i + (j)].x, floor[i + (j)].y));

                                    delta = floor[i + (j + 1)].y - floor[i + (j)].y;

                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(floor[i + (j + 1)].x, cell.floor[i + (j + 1)].y - abs(delta) + increment * (k)));
                                    }
                                }
                                else
                                {
                                    path.emplace_back(floor[i + (j)]);
                                }
                            }
                        }

                        reverse = !reverse;
                    }
                    else
                    {
                        y_start = floor[i].y;
                        y_end = ceiling[i].y;

                        for (y = y_start; y >= y_end; y--)
                        {
                            path.emplace_back(Point2D(x, y));
                        }

                        if ((std::abs(ceiling[i + 1].y - ceiling[i].y) >= 2) && (i + 1 < ceiling.size()))
                        {
                            delta = ceiling[i + 1].y - ceiling[i].y;
                            increment = delta / abs(delta);
                            for (int k = 1; k <= abs(delta); k++)
                            {
                                path.emplace_back(Point2D(ceiling[i].x, ceiling[i].y + increment * (k)));
                            }
                        }

                        if (robot_radius != 0)
                        {
                            for (int j = 1; j <= robot_radius + 1; j++)
                            {
                                // 沿着ceiling从左往右
                                if (x + j >= ceiling.back().x)
                                {
                                    i = i - (robot_radius - (j - 1));
                                    break;
                                }

                                // 提前转
                                else if ((ceiling[i + (j + 1)].y - ceiling[i + (j)].y >= 2) && (j + 1 <= robot_radius + 1) && (j <= robot_radius + 1))
                                {
                                    delta = ceiling[i + (j + 1)].y - ceiling[i + (j)].y;
                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(ceiling[i + j].x, ceiling[i + j].y + increment * (k)));
                                    }
                                }
                                // 滞后转
                                else if ((ceiling[i + (j)].y - ceiling[i + (j + 1)].y >= 2) && (j <= robot_radius + 1) && (j + 1 <= robot_radius + 1))
                                {
                                    path.emplace_back(ceiling[i + (j)]);

                                    delta = ceiling[i + (j + 1)].y - ceiling[i + (j)].y;

                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(ceiling[i + (j + 1)].x, ceiling[i + (j + 1)].y + abs(delta) + increment * (k)));
                                    }
                                }
                                else
                                {
                                    path.emplace_back(ceiling[i + j]);
                                }
                            }
                        }

                        reverse = !reverse;
                    }
                }
            }

            if (corner_indicator == TOP_RIGHT)
            {
                int x = 0, y = 0, y_start = 0, y_end = 0;
                bool reverse = false;

                for (int i = ceiling.size() - 1; i >= 0; i = i - (robot_radius + 1))
                {
                    x = ceiling[i].x;

                    if (!reverse)
                    {
                        y_start = ceiling[i].y;
                        y_end = floor[i].y;

                        for (y = y_start; y <= y_end; y++)
                        {
                            path.emplace_back(Point2D(x, y));
                        }

                        if ((std::abs(floor[i - 1].y - floor[i].y) >= 2) && (i - 1 >= 0))
                        {
                            delta = floor[i - 1].y - floor[i].y;
                            increment = delta / abs(delta);
                            for (int k = 1; k <= abs(delta); k++)
                            {
                                path.emplace_back(Point2D(floor[i].x, floor[i].y + increment * (k)));
                            }
                        }

                        if (robot_radius != 0)
                        {
                            for (int j = 1; j <= robot_radius + 1; j++)
                            {
                                // 沿着floor从右往左
                                if (x - j <= floor.front().x)
                                {
                                    i = i + (robot_radius - (j - 1));
                                    break;
                                }
                                // 提前转
                                else if ((floor[i - (j)].y - floor[i - (j + 1)].y >= 2) && (j <= robot_radius + 1) && (j + 1 <= robot_radius + 1))
                                {
                                    delta = floor[i - (j + 1)].y - floor[i - (j)].y;
                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(floor[i - (j)].x, floor[i - (j)].y + increment * (k)));
                                    }
                                }
                                // 滞后转
                                else if ((floor[i - (j + 1)].y - floor[i - (j)].y >= 2) && (j + 1 <= robot_radius + 1) && (j <= robot_radius + 1))
                                {
                                    path.emplace_back(Point2D(floor[i - (j)].x, floor[i - (j)].y));

                                    delta = floor[i - (j + 1)].y - floor[i - (j)].y;

                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(floor[i - (j + 1)].x, cell.floor[i - (j + 1)].y - abs(delta) + increment * (k)));
                                    }
                                }
                                else
                                {
                                    path.emplace_back(floor[i - (j)]);
                                }
                            }
                        }

                        reverse = !reverse;
                    }
                    else
                    {
                        y_start = floor[i].y;
                        y_end = ceiling[i].y;

                        for (y = y_start; y >= y_end; y--)
                        {
                            path.emplace_back(Point2D(x, y));
                        }

                        if ((std::abs(ceiling[i - 1].y - ceiling[i].y) >= 2) && (i - 1 >= 0))
                        {
                            delta = ceiling[i - 1].y - ceiling[i].y;
                            increment = delta / abs(delta);
                            for (int k = 1; k <= abs(delta); k++)
                            {
                                path.emplace_back(Point2D(ceiling[i].x, ceiling[i].y + increment * (k)));
                            }
                        }

                        if (robot_radius != 0)
                        {
                            for (int j = 1; j <= robot_radius + 1; j++)
                            {
                                // 沿着ceiling从右往左
                                if (x - j <= ceiling.front().x)
                                {
                                    i = i + (robot_radius - (j - 1));
                                    break;
                                }
                                // 提前转
                                else if ((ceiling[i - (j + 1)].y - ceiling[i - (j)].y >= 2) && (j + 1 <= robot_radius + 1) && (j <= robot_radius + 1))
                                {
                                    delta = ceiling[i - (j + 1)].y - ceiling[i - (j)].y;
                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(ceiling[i - j].x, ceiling[i - j].y + increment * (k)));
                                    }
                                }
                                // 滞后转
                                else if ((ceiling[i - (j)].y - ceiling[i - (j + 1)].y >= 2) && (j <= robot_radius + 1) && (j + 1 <= robot_radius + 1))
                                {
                                    path.emplace_back(ceiling[i - (j)]);

                                    delta = ceiling[i - (j + 1)].y - ceiling[i - (j)].y;

                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(ceiling[i - (j + 1)].x, ceiling[i - (j + 1)].y + abs(delta) + increment * (k)));
                                    }
                                }
                                else
                                {
                                    path.emplace_back(ceiling[i - j]);
                                }
                            }
                        }

                        reverse = !reverse;
                    }
                }
            }

            if (corner_indicator == BOTTOM_LEFT)
            {
                int x = 0, y = 0, y_start = 0, y_end = 0;
                bool reverse = false;

                for (int i = 0; i < ceiling.size(); i = i + (robot_radius + 1))
                {
                    x = ceiling[i].x;

                    if (!reverse)
                    {
                        y_start = floor[i].y;
                        y_end = ceiling[i].y;

                        for (y = y_start; y >= y_end; y--)
                        {
                            path.emplace_back(Point2D(x, y));
                        }

                        if ((std::abs(ceiling[i + 1].y - ceiling[i].y) >= 2) && (i + 1 < ceiling.size()))
                        {
                            delta = ceiling[i + 1].y - ceiling[i].y;
                            increment = delta / abs(delta);
                            for (int k = 1; k <= abs(delta); k++)
                            {
                                path.emplace_back(Point2D(ceiling[i].x, ceiling[i].y + increment * (k)));
                            }
                        }

                        if (robot_radius != 0)
                        {
                            for (int j = 1; j <= robot_radius + 1; j++)
                            {
                                // 沿着ceiling从左往右
                                if (x + j >= ceiling.back().x)
                                {
                                    i = i - (robot_radius - (j - 1));
                                    break;
                                }
                                // 提前转
                                else if ((ceiling[i + (j + 1)].y - ceiling[i + (j)].y >= 2) && (j + 1 <= robot_radius + 1) && (j <= robot_radius + 1))
                                {
                                    delta = ceiling[i + (j + 1)].y - ceiling[i + (j)].y;
                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(ceiling[i + j].x, ceiling[i + j].y + increment * (k)));
                                    }
                                }
                                // 滞后转
                                else if ((ceiling[i + (j)].y - ceiling[i + (j + 1)].y >= 2) && (j <= robot_radius + 1) && (j + 1 <= robot_radius + 1))
                                {
                                    path.emplace_back(ceiling[i + (j)]);

                                    delta = ceiling[i + (j + 1)].y - ceiling[i + (j)].y;

                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(ceiling[i + (j + 1)].x, ceiling[i + (j + 1)].y + abs(delta) + increment * (k)));
                                    }
                                }
                                else
                                {
                                    path.emplace_back(ceiling[i + j]);
                                }
                            }
                        }

                        reverse = !reverse;
                    }
                    else
                    {
                        y_start = ceiling[i].y;
                        y_end = floor[i].y;

                        for (y = y_start; y <= y_end; y++)
                        {
                            path.emplace_back(Point2D(x, y));
                        }

                        if ((std::abs(floor[i + 1].y - floor[i].y) >= 2) && (i + 1 < floor.size()))
                        {
                            delta = floor[i + 1].y - floor[i].y;
                            increment = delta / abs(delta);
                            for (int k = 1; k <= abs(delta); k++)
                            {
                                path.emplace_back(Point2D(floor[i].x, floor[i].y + increment * (k)));
                            }
                        }

                        if (robot_radius != 0)
                        {
                            for (int j = 1; j <= robot_radius + 1; j++)
                            {
                                // 沿着floor从左往右
                                if (x + j >= floor.back().x)
                                {
                                    i = i - (robot_radius - (j - 1));
                                    break;
                                }

                                // 提前转
                                else if ((floor[i + (j)].y - floor[i + (j + 1)].y >= 2) && (j <= robot_radius + 1) && (j + 1 <= robot_radius + 1))
                                {
                                    delta = floor[i + (j + 1)].y - floor[i + (j)].y;
                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(floor[i + (j)].x, floor[i + (j)].y + increment * (k)));
                                    }
                                }
                                // 滞后转
                                else if ((floor[i + (j + 1)].y - floor[i + (j)].y >= 2) && (j + 1 <= robot_radius + 1) && (j <= robot_radius + 1))
                                {
                                    path.emplace_back(Point2D(floor[i + (j)].x, floor[i + (j)].y));

                                    delta = floor[i + (j + 1)].y - floor[i + (j)].y;

                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(floor[i + (j + 1)].x, cell.floor[i + (j + 1)].y - abs(delta) + increment * (k)));
                                    }
                                }
                                else
                                {
                                    path.emplace_back(floor[i + (j)]);
                                }
                            }
                        }

                        reverse = !reverse;
                    }
                }
            }

            if (corner_indicator == BOTTOM_RIGHT)
            {
                int x = 0, y = 0, y_start = 0, y_end = 0;
                bool reverse = false;

                for (int i = ceiling.size() - 1; i >= 0; i = i - (robot_radius + 1))
                {
                    x = ceiling[i].x;

                    if (!reverse)
                    {
                        y_start = floor[i].y;
                        y_end = ceiling[i].y;

                        for (y = y_start; y >= y_end; y--)
                        {
                            path.emplace_back(Point2D(x, y));
                        }

                        if ((std::abs(ceiling[i - 1].y - ceiling[i].y) >= 2) && (i - 1 >= 0))
                        {
                            delta = ceiling[i - 1].y - ceiling[i].y;
                            increment = delta / abs(delta);
                            for (int k = 1; k <= abs(delta); k++)
                            {
                                path.emplace_back(Point2D(ceiling[i].x, ceiling[i].y + increment * (k)));
                            }
                        }

                        if (robot_radius != 0)
                        {
                            for (int j = 1; j <= robot_radius + 1; j++)
                            {
                                // 沿着ceiling从右往左
                                if (x - j <= ceiling.front().x)
                                {
                                    i = i + (robot_radius - (j - 1));
                                    break;
                                }
                                // 提前转
                                else if ((ceiling[i - (j + 1)].y - ceiling[i - (j)].y >= 2) && (j + 1 <= robot_radius + 1) && (j <= robot_radius + 1))
                                {
                                    delta = ceiling[i - (j + 1)].y - ceiling[i - (j)].y;
                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(ceiling[i - j].x, ceiling[i - j].y + increment * (k)));
                                    }
                                }
                                // 滞后转
                                else if ((ceiling[i - (j)].y - ceiling[i - (j + 1)].y >= 2) && (j <= robot_radius + 1) && (j + 1 <= robot_radius + 1))
                                {
                                    path.emplace_back(ceiling[i - (j)]);

                                    delta = ceiling[i - (j + 1)].y - ceiling[i - (j)].y;

                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(ceiling[i - (j + 1)].x, ceiling[i - (j + 1)].y + abs(delta) + increment * (k)));
                                    }
                                }
                                else
                                {
                                    path.emplace_back(ceiling[i - j]);
                                }
                            }
                        }

                        reverse = !reverse;
                    }
                    else
                    {
                        y_start = ceiling[i].y;
                        y_end = floor[i].y;

                        for (y = y_start; y <= y_end; y++)
                        {
                            path.emplace_back(Point2D(x, y));
                        }

                        if ((std::abs(floor[i - 1].y - floor[i].y) >= 2) && (i - 1 >= 0))
                        {
                            delta = floor[i - 1].y - floor[i].y;
                            increment = delta / abs(delta);
                            for (int k = 1; k <= abs(delta); k++)
                            {
                                path.emplace_back(Point2D(floor[i].x, floor[i].y + increment * (k)));
                            }
                        }

                        if (robot_radius != 0)
                        {
                            for (int j = 1; j <= robot_radius + 1; j++)
                            {
                                // 沿着floor从右往左
                                if (x - j <= floor.front().x)
                                {
                                    i = i + (robot_radius - (j - 1));
                                    break;
                                }
                                // 提前转
                                else if ((floor[i - (j)].y - floor[i - (j + 1)].y >= 2) && (j <= robot_radius + 1) && (j + 1 <= robot_radius + 1))
                                {
                                    delta = floor[i - (j + 1)].y - floor[i - (j)].y;
                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(floor[i - (j)].x, floor[i - (j)].y + increment * (k)));
                                    }
                                }
                                // 滞后转
                                else if ((floor[i - (j + 1)].y - floor[i - (j)].y >= 2) && (j + 1 <= robot_radius + 1) && (j <= robot_radius + 1))
                                {
                                    path.emplace_back(Point2D(floor[i - (j)].x, floor[i - (j)].y));

                                    delta = floor[i - (j + 1)].y - floor[i - (j)].y;

                                    increment = delta / abs(delta);
                                    for (int k = 0; k <= abs(delta); k++)
                                    {
                                        path.emplace_back(Point2D(floor[i - (j + 1)].x, cell.floor[i - (j + 1)].y - abs(delta) + increment * (k)));
                                    }
                                }
                                else
                                {
                                    path.emplace_back(floor[i - (j)]);
                                }
                            }
                        }

                        reverse = !reverse;
                    }
                }
            }
        }

        return path;
    }

    // 初始化事件列表
    std::vector<Event> CoveragePlanner::initializeEventList(const Polygon &polygon, int polygon_index)
    {
        std::vector<Event> event_list;

        for (const auto &point : polygon)
        {
            event_list.emplace_back(Event(polygon_index, point.x, point.y));
        }

        return event_list;
    }

    void CoveragePlanner::allocateWallEventType(const cv::Mat &map, std::vector<Event> &event_list)
    {
        int index_offset;
        // 只存放各种in和out的index
        std::deque<int> in_out_index_list;

        int N = event_list.size();

        // 检查 in and out and middle
        for (int i = 0; i < N; i++)
        {
            if (event_list[i].x < event_list[((i - 1) % N + N) % N].x && event_list[i].x < event_list[((i + 1) % N + N) % N].x)
            {
                event_list[i].event_type = IN_EX;
                in_out_index_list.emplace_back(i);
            }
            if (event_list[i].x < event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x && event_list[i].y < event_list[((i + 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i + index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x < event_list[((i + index_offset) % N + N) % N].x && event_list[i].y < event_list[((i + index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = IN_TOP_EX;
                    in_out_index_list.emplace_back(i);
                }
            }

            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x < event_list[((i + 1) % N + N) % N].x && event_list[i].y < event_list[((i - 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i - index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x < event_list[((i - index_offset) % N + N) % N].x && event_list[i].y < event_list[((i - index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = IN_TOP_EX;
                    in_out_index_list.emplace_back(i);
                }
            }

            if (event_list[i].x < event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x && event_list[i].y > event_list[((i + 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i + index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x < event_list[((i + index_offset) % N + N) % N].x && event_list[i].y > event_list[((i + index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = IN_BOTTOM_EX;
                    in_out_index_list.emplace_back(i);
                }
            }

            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x < event_list[((i + 1) % N + N) % N].x && event_list[i].y > event_list[((i - 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i - index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x < event_list[((i - index_offset) % N + N) % N].x && event_list[i].y > event_list[((i - index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = IN_BOTTOM_EX;
                    in_out_index_list.emplace_back(i);
                }
            }

            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x)
            {
                event_list[i].event_type = MIDDLE;
            }

            if (event_list[i].x > event_list[((i - 1) % N + N) % N].x && event_list[i].x > event_list[((i + 1) % N + N) % N].x)
            {
                event_list[i].event_type = OUT_EX;
                in_out_index_list.emplace_back(i);
            }

            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x > event_list[((i + 1) % N + N) % N].x && event_list[i].y < event_list[((i - 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i - index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x > event_list[((i - index_offset) % N + N) % N].x && event_list[i].y < event_list[((i - index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = OUT_TOP_EX;
                    in_out_index_list.emplace_back(i);
                }
            }

            if (event_list[i].x > event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x && event_list[i].y < event_list[((i + 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i + index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x > event_list[((i + index_offset) % N + N) % N].x && event_list[i].y < event_list[((i + index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = OUT_TOP_EX;
                    in_out_index_list.emplace_back(i);
                }
            }

            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x > event_list[((i + 1) % N + N) % N].x && event_list[i].y > event_list[((i - 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i - index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x > event_list[((i - index_offset) % N + N) % N].x && event_list[i].y > event_list[((i - index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = OUT_BOTTOM_EX;
                    in_out_index_list.emplace_back(i);
                }
            }

            if (event_list[i].x > event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x && event_list[i].y > event_list[((i + 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i + index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x > event_list[((i + index_offset) % N + N) % N].x && event_list[i].y > event_list[((i + index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = OUT_BOTTOM_EX;
                    in_out_index_list.emplace_back(i);
                }
            }
        }

        // determine inner 内点
        Point2D neighbor_point;
        int temp_index;
        for (auto in_out_index : in_out_index_list)
        {
            if (event_list[in_out_index].event_type == OUT_EX)
            {
                neighbor_point = Point2D(event_list[in_out_index].x + 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(255, 255, 255) && neighbor_point.x < map.cols)
                {
                    event_list[in_out_index].event_type = INNER_OUT_EX;
                }
            }

            if (event_list[in_out_index].event_type == OUT_TOP_EX)
            {
                neighbor_point = Point2D(event_list[in_out_index].x + 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(255, 255, 255) && neighbor_point.x < map.cols)
                {
                    event_list[in_out_index].event_type = INNER_OUT_TOP_EX;
                }
            }

            if (event_list[in_out_index].event_type == OUT_BOTTOM_EX)
            {
                neighbor_point = Point2D(event_list[in_out_index].x + 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(255, 255, 255) && neighbor_point.x < map.cols)
                {
                    event_list[in_out_index].event_type = INNER_OUT_BOTTOM_EX;
                }
            }

            if (event_list[in_out_index].event_type == IN_EX)
            {
                neighbor_point = Point2D(event_list[in_out_index].x - 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(255, 255, 255) && neighbor_point.x >= 0)
                {
                    event_list[in_out_index].event_type = INNER_IN_EX;
                }
            }

            if (event_list[in_out_index].event_type == IN_TOP_EX)
            {
                neighbor_point = Point2D(event_list[in_out_index].x - 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(255, 255, 255) && neighbor_point.x >= 0)
                {
                    event_list[in_out_index].event_type = INNER_IN_TOP_EX;
                }
            }

            if (event_list[in_out_index].event_type == IN_BOTTOM_EX)
            {
                neighbor_point = Point2D(event_list[in_out_index].x - 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(255, 255, 255) && neighbor_point.x >= 0)
                {
                    event_list[in_out_index].event_type = INNER_IN_BOTTOM_EX;
                }
            }
        }

        // determine floor and ceiling 检测顶部和底部
        std::deque<int> ceiling_floor_index_list;

        for (int i = 0; i < in_out_index_list.size(); i++)
        {
            if (
                (event_list[in_out_index_list[0]].event_type == OUT_EX || event_list[in_out_index_list[0]].event_type == OUT_TOP_EX || event_list[in_out_index_list[0]].event_type == OUT_BOTTOM_EX || event_list[in_out_index_list[0]].event_type == INNER_OUT_EX || event_list[in_out_index_list[0]].event_type == INNER_OUT_TOP_EX || event_list[in_out_index_list[0]].event_type == INNER_OUT_BOTTOM_EX) &&
                (event_list[in_out_index_list[1]].event_type == IN_EX || event_list[in_out_index_list[1]].event_type == IN_TOP_EX || event_list[in_out_index_list[1]].event_type == IN_BOTTOM_EX || event_list[in_out_index_list[1]].event_type == INNER_IN_EX || event_list[in_out_index_list[1]].event_type == INNER_IN_TOP_EX || event_list[in_out_index_list[1]].event_type == INNER_IN_BOTTOM_EX))
            {
                if (in_out_index_list[0] < in_out_index_list[1])
                {
                    for (int j = in_out_index_list[0] + 1; j < in_out_index_list[1]; j++)
                    {
                        if (event_list[j].event_type != MIDDLE)
                        {
                            event_list[j].event_type = CEILING;
                            ceiling_floor_index_list.emplace_back(j);
                        }
                    }
                }
                else
                {
                    for (int j = in_out_index_list[0] + 1; j < event_list.size(); j++)
                    {
                        if (event_list[j].event_type != MIDDLE)
                        {
                            event_list[j].event_type = CEILING;
                            ceiling_floor_index_list.emplace_back(j);
                        }
                    }
                    for (int k = 0; k < in_out_index_list[1]; k++)
                    {
                        if (event_list[k].event_type != MIDDLE)
                        {
                            event_list[k].event_type = CEILING;
                            ceiling_floor_index_list.emplace_back(k);
                        }
                    }
                }
            }

            if (
                (event_list[in_out_index_list[0]].event_type == IN_EX || event_list[in_out_index_list[0]].event_type == IN_TOP_EX || event_list[in_out_index_list[0]].event_type == IN_BOTTOM_EX || event_list[in_out_index_list[0]].event_type == INNER_IN_EX || event_list[in_out_index_list[0]].event_type == INNER_IN_TOP_EX || event_list[in_out_index_list[0]].event_type == INNER_IN_BOTTOM_EX) &&
                (event_list[in_out_index_list[1]].event_type == OUT_EX || event_list[in_out_index_list[1]].event_type == OUT_TOP_EX || event_list[in_out_index_list[1]].event_type == OUT_BOTTOM_EX || event_list[in_out_index_list[1]].event_type == INNER_OUT_EX || event_list[in_out_index_list[1]].event_type == INNER_OUT_TOP_EX || event_list[in_out_index_list[1]].event_type == INNER_OUT_BOTTOM_EX))
            {
                if (in_out_index_list[0] < in_out_index_list[1])
                {
                    for (int j = in_out_index_list[0] + 1; j < in_out_index_list[1]; j++)
                    {
                        if (event_list[j].event_type != MIDDLE)
                        {
                            event_list[j].event_type = FLOOR;
                            ceiling_floor_index_list.emplace_back(j);
                        }
                    }
                }
                else
                {
                    for (int j = in_out_index_list[0] + 1; j < event_list.size(); j++)
                    {
                        if (event_list[j].event_type != MIDDLE)
                        {
                            event_list[j].event_type = FLOOR;
                            ceiling_floor_index_list.emplace_back(j);
                        }
                    }
                    for (int k = 0; k < in_out_index_list[1]; k++)
                    {
                        if (event_list[k].event_type != MIDDLE)
                        {
                            event_list[k].event_type = FLOOR;
                            ceiling_floor_index_list.emplace_back(k);
                        }
                    }
                }
            }

            temp_index = in_out_index_list.front();
            in_out_index_list.pop_front();
            in_out_index_list.emplace_back(temp_index);
        }

        // filter ceiling and floor  过滤顶部和底部
        for (int i = 0; i < ceiling_floor_index_list.size() - 1; i++)
        {
            if (event_list[ceiling_floor_index_list[i]].event_type == CEILING && event_list[ceiling_floor_index_list[i + 1]].event_type == CEILING && event_list[ceiling_floor_index_list[i]].x == event_list[ceiling_floor_index_list[i + 1]].x)
            {
                if (event_list[ceiling_floor_index_list[i]].y > event_list[ceiling_floor_index_list[i + 1]].y)
                {
                    event_list[ceiling_floor_index_list[i + 1]].event_type = MIDDLE;
                }
                else
                {
                    event_list[ceiling_floor_index_list[i]].event_type = MIDDLE;
                }
            }
            if (event_list[ceiling_floor_index_list[i]].event_type == FLOOR && event_list[ceiling_floor_index_list[i + 1]].event_type == FLOOR && event_list[ceiling_floor_index_list[i]].x == event_list[ceiling_floor_index_list[i + 1]].x)
            {
                if (event_list[ceiling_floor_index_list[i]].y < event_list[ceiling_floor_index_list[i + 1]].y)
                {
                    event_list[ceiling_floor_index_list[i + 1]].event_type = MIDDLE;
                }
                else
                {
                    event_list[ceiling_floor_index_list[i]].event_type = MIDDLE;
                }
            }
        }
        if (event_list[ceiling_floor_index_list.back()].event_type == CEILING && event_list[ceiling_floor_index_list.front()].event_type == CEILING && event_list[ceiling_floor_index_list.back()].x == event_list[ceiling_floor_index_list.front()].x)
        {
            if (event_list[ceiling_floor_index_list.back()].y > event_list[ceiling_floor_index_list.front()].y)
            {
                event_list[ceiling_floor_index_list.front()].event_type = MIDDLE;
            }
            else
            {
                event_list[ceiling_floor_index_list.back()].event_type = MIDDLE;
            }
        }
        if (event_list[ceiling_floor_index_list.back()].event_type == FLOOR && event_list[ceiling_floor_index_list.front()].event_type == FLOOR && event_list[ceiling_floor_index_list.back()].x == event_list[ceiling_floor_index_list.front()].x)
        {
            if (event_list[ceiling_floor_index_list.back()].y < event_list[ceiling_floor_index_list.front()].y)
            {
                event_list[ceiling_floor_index_list.front()].event_type = MIDDLE;
            }
            else
            {
                event_list[ceiling_floor_index_list.back()].event_type = MIDDLE;
            }
        }
    }

    // 对每个轮廓点做属性分类(特征点)点集默认是逆时针
    void CoveragePlanner::allocateObstacleEventType(const cv::Mat &map, std::vector<Event> &event_list)
    {
        int index_offset;
        // 存放各种in和out的index
        std::deque<int> in_out_index_list;

        int N = event_list.size();
        for (int i = 0; i < N; i++)
        {
            /** // 当前点的x 比 左右两个点的x 都要小
             *    ----- (逆时针)
             *  -          IN
             *    -----
             */
            if (event_list[i].x < event_list[((i - 1) % N + N) % N].x && event_list[i].x < event_list[((i + 1) % N + N) % N].x)
            {
                event_list[i].event_type = IN;
                in_out_index_list.emplace_back(i);
            }

            /** // 当前的 x 比 前面的x要小 且 等于后面的x 且 小于后面的y值,  向后寻找 直到找到x值更大 且 y值更大
             *    --------  (逆时针)
             *   -           IN_TOP
             *   -
             *    --------
             */
            if (event_list[i].x < event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x && event_list[i].y < event_list[((i + 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i + index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x < event_list[((i + index_offset) % N + N) % N].x && event_list[i].y < event_list[((i + index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = IN_TOP;
                    in_out_index_list.emplace_back(i);
                }
            }

            /** // 当前的 x 等于的前面x 且小于后面的x 且 小于前面的y值, 向前寻找 直到找到x值更大 且 y值更大
             *    -------  (顺时针)
             *   -          IN_TOP
             *   -
             *    -------
             */
            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x < event_list[((i + 1) % N + N) % N].x && event_list[i].y < event_list[((i - 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i - index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x < event_list[((i - index_offset) % N + N) % N].x && event_list[i].y < event_list[((i - index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = IN_TOP;
                    in_out_index_list.emplace_back(i);
                }
            }

            /**
             *    -------  (顺时针)
             *   -
             *   -          IN_BOTTOM
             *    -------
             */
            if (event_list[i].x < event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x && event_list[i].y > event_list[((i + 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i + index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x < event_list[((i + index_offset) % N + N) % N].x && event_list[i].y > event_list[((i + index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = IN_BOTTOM;
                    in_out_index_list.emplace_back(i);
                }
            }

            /**
             *    -------  (逆时针)
             *   -
             *   -          IN_BOTTOM
             *    -------
             */
            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x < event_list[((i + 1) % N + N) % N].x && event_list[i].y > event_list[((i - 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i - index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x < event_list[((i - index_offset) % N + N) % N].x && event_list[i].y > event_list[((i - index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = IN_BOTTOM;
                    in_out_index_list.emplace_back(i);
                }
            }

            /**
             *    -
             *    -    MIDDLE
             *    -
             */
            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x)
            {
                event_list[i].event_type = MIDDLE;
            }

            /**
             *    --------
             *            -  OUT
             *    --------
             */
            if (event_list[i].x > event_list[((i - 1) % N + N) % N].x && event_list[i].x > event_list[((i + 1) % N + N) % N].x)
            {
                event_list[i].event_type = OUT;
                in_out_index_list.emplace_back(i);
            }

            /**
             *   ----------     (逆时针)
             *             -    OUT_TOP
             *             -
             *   ----------
             */
            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x > event_list[((i + 1) % N + N) % N].x && event_list[i].y < event_list[((i - 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i - index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x > event_list[((i - index_offset) % N + N) % N].x && event_list[i].y < event_list[((i - index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = OUT_TOP;
                    in_out_index_list.emplace_back(i);
                }
            }

            /**
             *   ----------     (顺时针)
             *             -    OUT_TOP
             *             -
             *   ----------
             */
            if (event_list[i].x > event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x && event_list[i].y < event_list[((i + 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i + index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x > event_list[((i + index_offset) % N + N) % N].x && event_list[i].y < event_list[((i + index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = OUT_TOP;
                    in_out_index_list.emplace_back(i);
                }
            }

            /**
             *   ----------     (顺时针)
             *             -
             *             -    OUT_BOTTOM
             *   ----------
             */
            if (event_list[i].x == event_list[((i - 1) % N + N) % N].x && event_list[i].x > event_list[((i + 1) % N + N) % N].x && event_list[i].y > event_list[((i - 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i - index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x > event_list[((i - index_offset) % N + N) % N].x && event_list[i].y > event_list[((i - index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = OUT_BOTTOM;
                    in_out_index_list.emplace_back(i);
                }
            }
            /**
             *   ----------     (逆时针)
             *             -
             *             -    OUT_BOTTOM
             *   ----------
             */
            if (event_list[i].x > event_list[((i - 1) % N + N) % N].x && event_list[i].x == event_list[((i + 1) % N + N) % N].x && event_list[i].y > event_list[((i + 1) % N + N) % N].y)
            {
                index_offset = 2;
                while (event_list[i].x == event_list[((i + index_offset) % N + N) % N].x)
                {
                    index_offset++;
                }
                if (event_list[i].x > event_list[((i + index_offset) % N + N) % N].x && event_list[i].y > event_list[((i + index_offset) % N + N) % N].y)
                {
                    event_list[i].event_type = OUT_BOTTOM;
                    in_out_index_list.emplace_back(i);
                }
            }
        }

        // determine inner 判断内部点或者是外部点
        Point2D neighbor_point;
        int temp_index;
        for (auto in_out_index : in_out_index_list)
        {

            if (event_list[in_out_index].event_type == OUT)
            {
                neighbor_point = Point2D(event_list[in_out_index].x + 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(0, 0, 0))
                {
                    event_list[in_out_index].event_type = INNER_OUT;
                }
            }

            if (event_list[in_out_index].event_type == OUT_TOP)
            {
                neighbor_point = Point2D(event_list[in_out_index].x + 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(0, 0, 0))
                {
                    event_list[in_out_index].event_type = INNER_OUT_TOP;
                }
            }

            if (event_list[in_out_index].event_type == OUT_BOTTOM)
            {
                neighbor_point = Point2D(event_list[in_out_index].x + 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(0, 0, 0))
                {
                    event_list[in_out_index].event_type = INNER_OUT_BOTTOM;
                }
            }

            if (event_list[in_out_index].event_type == IN)
            {
                neighbor_point = Point2D(event_list[in_out_index].x - 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(0, 0, 0))
                {
                    event_list[in_out_index].event_type = INNER_IN;
                }
            }

            if (event_list[in_out_index].event_type == IN_TOP)
            {
                neighbor_point = Point2D(event_list[in_out_index].x - 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(0, 0, 0))
                {
                    event_list[in_out_index].event_type = INNER_IN_TOP;
                }
            }

            if (event_list[in_out_index].event_type == IN_BOTTOM)
            {
                neighbor_point = Point2D(event_list[in_out_index].x - 1, event_list[in_out_index].y);
                if (map.at<cv::Vec3b>(neighbor_point.y, neighbor_point.x) == cv::Vec3b(0, 0, 0))
                {
                    event_list[in_out_index].event_type = INNER_IN_BOTTOM;
                }
            }
        }

        // determine floor and ceiling 判断下面点、上面点
        std::deque<int> ceiling_floor_index_list;
        for (int i = 0; i < in_out_index_list.size(); i++)
        {
            if (
                (event_list[in_out_index_list[0]].event_type == OUT || event_list[in_out_index_list[0]].event_type == OUT_TOP || event_list[in_out_index_list[0]].event_type == OUT_BOTTOM || event_list[in_out_index_list[0]].event_type == INNER_OUT || event_list[in_out_index_list[0]].event_type == INNER_OUT_TOP || event_list[in_out_index_list[0]].event_type == INNER_OUT_BOTTOM) &&
                (event_list[in_out_index_list[1]].event_type == IN || event_list[in_out_index_list[1]].event_type == IN_TOP || event_list[in_out_index_list[1]].event_type == IN_BOTTOM || event_list[in_out_index_list[1]].event_type == INNER_IN || event_list[in_out_index_list[1]].event_type == INNER_IN_TOP || event_list[in_out_index_list[1]].event_type == INNER_IN_BOTTOM))
            {
                if (in_out_index_list[0] < in_out_index_list[1])
                {
                    for (int j = in_out_index_list[0] + 1; j < in_out_index_list[1]; j++)
                    {
                        if (event_list[j].event_type != MIDDLE)
                        {
                            event_list[j].event_type = FLOOR;
                            ceiling_floor_index_list.emplace_back(j);
                        }
                    }
                }
                else
                {
                    for (int j = in_out_index_list[0] + 1; j < event_list.size(); j++)
                    {
                        if (event_list[j].event_type != MIDDLE)
                        {
                            event_list[j].event_type = FLOOR;
                            ceiling_floor_index_list.emplace_back(j);
                        }
                    }
                    for (int k = 0; k < in_out_index_list[1]; k++)
                    {
                        if (event_list[k].event_type != MIDDLE)
                        {
                            event_list[k].event_type = FLOOR;
                            ceiling_floor_index_list.emplace_back(k);
                        }
                    }
                }
            }

            if (
                (event_list[in_out_index_list[0]].event_type == IN || event_list[in_out_index_list[0]].event_type == IN_TOP || event_list[in_out_index_list[0]].event_type == IN_BOTTOM || event_list[in_out_index_list[0]].event_type == INNER_IN || event_list[in_out_index_list[0]].event_type == INNER_IN_TOP || event_list[in_out_index_list[0]].event_type == INNER_IN_BOTTOM) &&
                (event_list[in_out_index_list[1]].event_type == OUT || event_list[in_out_index_list[1]].event_type == OUT_TOP || event_list[in_out_index_list[1]].event_type == OUT_BOTTOM || event_list[in_out_index_list[1]].event_type == INNER_OUT || event_list[in_out_index_list[1]].event_type == INNER_OUT_TOP || event_list[in_out_index_list[1]].event_type == INNER_OUT_BOTTOM))
            {
                if (in_out_index_list[0] < in_out_index_list[1])
                {
                    for (int j = in_out_index_list[0] + 1; j < in_out_index_list[1]; j++)
                    {
                        if (event_list[j].event_type != MIDDLE)
                        {
                            event_list[j].event_type = CEILING;
                            ceiling_floor_index_list.emplace_back(j);
                        }
                    }
                }
                else
                {
                    for (int j = in_out_index_list[0] + 1; j < event_list.size(); j++)
                    {
                        if (event_list[j].event_type != MIDDLE)
                        {
                            event_list[j].event_type = CEILING;
                            ceiling_floor_index_list.emplace_back(j);
                        }
                    }
                    for (int k = 0; k < in_out_index_list[1]; k++)
                    {
                        if (event_list[k].event_type != MIDDLE)
                        {
                            event_list[k].event_type = CEILING;
                            ceiling_floor_index_list.emplace_back(k);
                        }
                    }
                }
            }

            temp_index = in_out_index_list.front();
            in_out_index_list.pop_front();
            in_out_index_list.emplace_back(temp_index);
        }

        // filter ceiling and floor  进一步区分上面点和下面点 滤出中间点
        for (int i = 0; i < ceiling_floor_index_list.size() - 1; i++)
        {
            if (event_list[ceiling_floor_index_list[i]].event_type == CEILING && event_list[ceiling_floor_index_list[i + 1]].event_type == CEILING && event_list[ceiling_floor_index_list[i]].x == event_list[ceiling_floor_index_list[i + 1]].x)
            {
                if (event_list[ceiling_floor_index_list[i]].y > event_list[ceiling_floor_index_list[i + 1]].y)
                {
                    event_list[ceiling_floor_index_list[i + 1]].event_type = MIDDLE;
                }
                else
                {
                    event_list[ceiling_floor_index_list[i]].event_type = MIDDLE;
                }
            }
            if (event_list[ceiling_floor_index_list[i]].event_type == FLOOR && event_list[ceiling_floor_index_list[i + 1]].event_type == FLOOR && event_list[ceiling_floor_index_list[i]].x == event_list[ceiling_floor_index_list[i + 1]].x)
            {
                if (event_list[ceiling_floor_index_list[i]].y < event_list[ceiling_floor_index_list[i + 1]].y)
                {
                    event_list[ceiling_floor_index_list[i + 1]].event_type = MIDDLE;
                }
                else
                {
                    event_list[ceiling_floor_index_list[i]].event_type = MIDDLE;
                }
            }
        }
        if (event_list[ceiling_floor_index_list.back()].event_type == CEILING && event_list[ceiling_floor_index_list.front()].event_type == CEILING && event_list[ceiling_floor_index_list.back()].x == event_list[ceiling_floor_index_list.front()].x)
        {
            if (event_list[ceiling_floor_index_list.back()].y > event_list[ceiling_floor_index_list.front()].y)
            {
                event_list[ceiling_floor_index_list.front()].event_type = MIDDLE;
            }
            else
            {
                event_list[ceiling_floor_index_list.back()].event_type = MIDDLE;
            }
        }
        if (event_list[ceiling_floor_index_list.back()].event_type == FLOOR && event_list[ceiling_floor_index_list.front()].event_type == FLOOR && event_list[ceiling_floor_index_list.back()].x == event_list[ceiling_floor_index_list.front()].x)
        {
            if (event_list[ceiling_floor_index_list.back()].y < event_list[ceiling_floor_index_list.front()].y)
            {
                event_list[ceiling_floor_index_list.front()].event_type = MIDDLE;
            }
            else
            {
                event_list[ceiling_floor_index_list.back()].event_type = MIDDLE;
            }
        }
    }

    // 对障碍物的点集属性进行标记
    std::vector<Event> CoveragePlanner::generateObstacleEventList(const cv::Mat &map, const PolygonList &polygons)
    {
        std::vector<Event> event_list;
        std::vector<Event> event_sublist;
        for (int i = 0; i < polygons.size(); i++)
        {
            event_sublist = initializeEventList(polygons[i], i);
            allocateObstacleEventType(map, event_sublist);
            event_list.insert(event_list.end(), event_sublist.begin(), event_sublist.end());
            event_sublist.clear();
        }
        std::sort(event_list.begin(), event_list.end());
        return event_list;
    }

    // 对墙体的点集属性进行标记
    std::vector<Event> CoveragePlanner::generateWallEventList(const cv::Mat &map, const Polygon &external_contour)
    {
        std::vector<Event> event_list;
        event_list = initializeEventList(external_contour, INT_MAX);
        allocateWallEventType(map, event_list);
        std::sort(event_list.begin(), event_list.end());
        return event_list;
    }

    // 简单根据x 的值进行分组
    std::deque<std::deque<Event>> CoveragePlanner::sliceListGenerator(const std::vector<Event> &wall_event_list, const std::vector<Event> &obstacle_event_list)
    {
        std::vector<Event> event_list;
        event_list.insert(event_list.end(), obstacle_event_list.begin(), obstacle_event_list.end());
        event_list.insert(event_list.end(), wall_event_list.begin(), wall_event_list.end());
        std::sort(event_list.begin(), event_list.end());
        std::cout << "event_list.size():" << event_list.size() << std::endl;

        std::deque<std::deque<Event>> slice_list;
        std::deque<Event> slice;
        // 最小的x
        int x = event_list.front().x;
        // 找出相同的x,进行粗暴的分组
        for (auto event : event_list)
        {
            if (event.x != x)
            {
                slice_list.emplace_back(slice);
                x = event.x;
                slice.clear();
                slice.emplace_back(event);
            }
            else
            {
                slice.emplace_back(event);
            }
        }
        slice_list.emplace_back(slice);
        return slice_list;
    }

    void CoveragePlanner::executeOpenOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, Point2D in, Point2D c, Point2D f, bool rewrite)
    {

        CellNode top_cell, bottom_cell;

        top_cell.ceiling.emplace_back(c);
        top_cell.floor.emplace_back(in);

        bottom_cell.ceiling.emplace_back(in);
        bottom_cell.floor.emplace_back(f);

        if (!rewrite)
        {
            int top_cell_index = cell_graph.size();
            int bottom_cell_index = cell_graph.size() + 1;

            top_cell.cellIndex = top_cell_index;
            bottom_cell.cellIndex = bottom_cell_index;
            cell_graph.emplace_back(top_cell);
            cell_graph.emplace_back(bottom_cell);

            cell_graph[top_cell_index].neighbor_indices.emplace_back(curr_cell_idx);
            cell_graph[bottom_cell_index].neighbor_indices.emplace_front(curr_cell_idx);

            cell_graph[curr_cell_idx].neighbor_indices.emplace_front(top_cell_index);
            cell_graph[curr_cell_idx].neighbor_indices.emplace_front(bottom_cell_index);
        }
        else
        {
            cell_graph[curr_cell_idx].ceiling.assign(top_cell.ceiling.begin(), top_cell.ceiling.end());
            cell_graph[curr_cell_idx].floor.assign(top_cell.floor.begin(), top_cell.floor.end());

            int bottom_cell_index = cell_graph.size();
            bottom_cell.cellIndex = bottom_cell_index;
            cell_graph.emplace_back(bottom_cell);

            cell_graph[cell_graph[curr_cell_idx].neighbor_indices.back()].neighbor_indices.emplace_back(bottom_cell_index);
            cell_graph[bottom_cell_index].neighbor_indices.emplace_back(cell_graph[curr_cell_idx].neighbor_indices.back());
        }
    }

    void CoveragePlanner::executeOpenOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, Point2D in_top, Point2D in_bottom, Point2D c, Point2D f, bool rewrite)
    {

        CellNode top_cell, bottom_cell;

        top_cell.ceiling.emplace_back(c);
        top_cell.floor.emplace_back(in_top);

        bottom_cell.ceiling.emplace_back(in_bottom);
        bottom_cell.floor.emplace_back(f);

        if (!rewrite)
        {
            int top_cell_index = cell_graph.size();
            int bottom_cell_index = cell_graph.size() + 1;

            top_cell.cellIndex = top_cell_index;
            bottom_cell.cellIndex = bottom_cell_index;
            cell_graph.emplace_back(top_cell);
            cell_graph.emplace_back(bottom_cell);

            cell_graph[top_cell_index].neighbor_indices.emplace_back(curr_cell_idx);
            cell_graph[bottom_cell_index].neighbor_indices.emplace_front(curr_cell_idx);

            cell_graph[curr_cell_idx].neighbor_indices.emplace_front(top_cell_index);
            cell_graph[curr_cell_idx].neighbor_indices.emplace_front(bottom_cell_index);
        }
        else
        {
            cell_graph[curr_cell_idx].ceiling.assign(top_cell.ceiling.begin(), top_cell.ceiling.end());
            cell_graph[curr_cell_idx].floor.assign(top_cell.floor.begin(), top_cell.floor.end());

            int bottom_cell_index = cell_graph.size();
            bottom_cell.cellIndex = bottom_cell_index;
            cell_graph.emplace_back(bottom_cell);

            cell_graph[cell_graph[curr_cell_idx].neighbor_indices.back()].neighbor_indices.emplace_back(bottom_cell_index);
            cell_graph[bottom_cell_index].neighbor_indices.emplace_back(cell_graph[curr_cell_idx].neighbor_indices.back());
        }
    }

    void CoveragePlanner::executeFloorOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, const Point2D &floor_point)
    {
        cell_graph[curr_cell_idx].floor.emplace_back(floor_point);
    }

    void CoveragePlanner::executeCeilOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, const Point2D &ceil_point)
    {
        cell_graph[curr_cell_idx].ceiling.emplace_back(ceil_point);
    }

    int CoveragePlanner::countCells(const std::deque<Event> &slice, int curr_idx)
    {
        int cell_num = 0;
        for (int i = 0; i < curr_idx; i++)
        {
            if (
                (slice[i].event_type == IN) || (slice[i].event_type == IN_TOP) || (slice[i].event_type == INNER_IN) || (slice[i].event_type == INNER_IN_BOTTOM) || (slice[i].event_type == FLOOR) || (slice[i].event_type == IN_BOTTOM_EX) || (slice[i].event_type == INNER_IN_EX) || (slice[i].event_type == INNER_IN_TOP_EX))
            {
                cell_num++;
            }
        }
        return cell_num;
    }

    void CoveragePlanner::executeInnerCloseOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, Point2D inner_out)
    {
        cell_graph[curr_cell_idx].ceiling.emplace_back(inner_out);
        cell_graph[curr_cell_idx].floor.emplace_back(inner_out);
    }

    void CoveragePlanner::executeInnerCloseOperation(std::vector<CellNode> &cell_graph, int curr_cell_idx, Point2D inner_out_top, Point2D inner_out_bottom)
    {
        cell_graph[curr_cell_idx].ceiling.emplace_back(inner_out_top);
        cell_graph[curr_cell_idx].floor.emplace_back(inner_out_bottom);
    }

    void CoveragePlanner::executeInnerOpenOperation(std::vector<CellNode> &cell_graph, Point2D inner_in)
    {
        CellNode new_cell;
        new_cell.ceiling.emplace_back(inner_in);
        new_cell.floor.emplace_back(inner_in);
        new_cell.cellIndex = cell_graph.size();
        cell_graph.emplace_back(new_cell);
    }

    void CoveragePlanner::executeInnerOpenOperation(std::vector<CellNode> &cell_graph, Point2D inner_in_top, Point2D inner_in_bottom)
    {
        CellNode new_cell;

        new_cell.ceiling.emplace_back(inner_in_top);
        new_cell.floor.emplace_back(inner_in_bottom);

        int new_cell_index = cell_graph.size();

        new_cell.cellIndex = new_cell_index;
        cell_graph.emplace_back(new_cell);
    }

    void CoveragePlanner::executeCloseOperation(std::vector<CellNode> &cell_graph, int top_cell_idx, int bottom_cell_idx, Point2D c, Point2D f, bool rewrite)
    {
        CellNode new_cell;

        new_cell.ceiling.emplace_back(c);
        new_cell.floor.emplace_back(f);

        if (!rewrite)
        {
            int new_cell_idx = cell_graph.size();
            new_cell.cellIndex = new_cell_idx;

            cell_graph.emplace_back(new_cell);

            cell_graph[new_cell_idx].neighbor_indices.emplace_back(top_cell_idx);
            cell_graph[new_cell_idx].neighbor_indices.emplace_back(bottom_cell_idx);

            cell_graph[top_cell_idx].neighbor_indices.emplace_front(new_cell_idx);
            cell_graph[bottom_cell_idx].neighbor_indices.emplace_back(new_cell_idx);
        }
        else
        {
            cell_graph[top_cell_idx].ceiling.assign(new_cell.ceiling.begin(), new_cell.ceiling.end());
            cell_graph[top_cell_idx].floor.assign(new_cell.floor.begin(), new_cell.floor.end());

            cell_graph[top_cell_idx].neighbor_indices.emplace_back(bottom_cell_idx);
            cell_graph[bottom_cell_idx].neighbor_indices.emplace_back(top_cell_idx);
        }
    }

    std::deque<Event> CoveragePlanner::filterSlice(const std::deque<Event> &slice)
    {
        std::deque<Event> filtered_slice;
        for (auto event : slice)
        {
            // 滤除中间和处于初始状态的
            if (event.event_type != MIDDLE && event.event_type != UNALLOCATED)
            {
                filtered_slice.emplace_back(event);
            }
        }
        return filtered_slice;
    }

    // 区域分解
    void CoveragePlanner::executeCellDecomposition(std::vector<CellNode> &cell_graph, const std::deque<std::deque<Event>> &slice_list)
    {
        std::vector<int> cell_index_slice;
        std::vector<int> original_cell_index_slice;
        int curr_cell_idx = INT_MAX;
        int top_cell_idx = INT_MAX;
        int bottom_cell_idx = INT_MAX;

        Point2D c, f;
        int c_index = INT_MAX;
        int f_index = INT_MAX;
        int min_dist = INT_MAX;

        int event_y = INT_MAX;

        bool rewrite = false;

        std::vector<int> sub_cell_index_slices;
        std::deque<Event> curr_slice;

        int cell_counter = 0;

        for (const auto &raw_slice : slice_list)
        {
            curr_slice = filterSlice(raw_slice);

            original_cell_index_slice.assign(cell_index_slice.begin(), cell_index_slice.end());

            for (int j = 0; j < curr_slice.size(); j++)
            {
                if (curr_slice[j].event_type == INNER_IN_EX)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 0; k < cell_index_slice.size(); k++)
                    {
                        if (event_y > cell_graph[cell_index_slice[k]].ceiling.back().y && event_y < cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            rewrite = std::find(original_cell_index_slice.begin(), original_cell_index_slice.end(), cell_index_slice[k]) == original_cell_index_slice.end(); // 若为true,则覆盖

                            min_dist = INT_MAX;
                            for (int m = 0; m < curr_slice.size(); m++)
                            {
                                if (abs(curr_slice[m].y - cell_graph[cell_index_slice[k]].ceiling.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[m].y - cell_graph[cell_index_slice[k]].ceiling.back().y);
                                    c_index = m;
                                    c = Point2D(curr_slice[m].x, curr_slice[m].y);
                                }
                            }
                            curr_slice[c_index].isUsed = true;

                            min_dist = INT_MAX;
                            for (int n = 0; n < curr_slice.size(); n++)
                            {
                                if (abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y);
                                    f_index = n;
                                    f = Point2D(curr_slice[n].x, curr_slice[n].y);
                                }
                            }
                            curr_slice[f_index].isUsed = true;

                            curr_cell_idx = cell_index_slice[k];
                            executeOpenOperation(cell_graph, curr_cell_idx,
                                                 Point2D(curr_slice[j].x, curr_slice[j].y),
                                                 c,
                                                 f,
                                                 rewrite);

                            if (!rewrite)
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k);
                                sub_cell_index_slices.clear();
                                sub_cell_index_slices = {int(cell_graph.size() - 2), int(cell_graph.size() - 1)};
                                cell_index_slice.insert(cell_index_slice.begin() + k, sub_cell_index_slices.begin(), sub_cell_index_slices.end());
                            }
                            else
                            {
                                cell_index_slice.insert(cell_index_slice.begin() + k + 1, int(cell_graph.size() - 1));
                            }

                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }
                if (curr_slice[j].event_type == INNER_OUT_EX)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 1; k < cell_index_slice.size(); k++)
                    {
                        if (event_y > cell_graph[cell_index_slice[k - 1]].ceiling.back().y && event_y < cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            rewrite = std::find(original_cell_index_slice.begin(), original_cell_index_slice.end(), cell_index_slice[k - 1]) == original_cell_index_slice.end();

                            min_dist = INT_MAX;
                            for (int m = 0; m < curr_slice.size(); m++)
                            {
                                if (abs(curr_slice[m].y - cell_graph[cell_index_slice[k - 1]].ceiling.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[m].y - cell_graph[cell_index_slice[k - 1]].ceiling.back().y);
                                    c_index = m;
                                    c = Point2D(curr_slice[m].x, curr_slice[m].y);
                                }
                            }
                            curr_slice[c_index].isUsed = true;

                            min_dist = INT_MAX;
                            for (int n = 0; n < curr_slice.size(); n++)
                            {
                                if (abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y);
                                    f_index = n;
                                    f = Point2D(curr_slice[n].x, curr_slice[n].y);
                                }
                            }
                            curr_slice[f_index].isUsed = true;

                            top_cell_idx = cell_index_slice[k - 1];
                            bottom_cell_idx = cell_index_slice[k];

                            executeCloseOperation(cell_graph, top_cell_idx, bottom_cell_idx,
                                                  c,
                                                  f,
                                                  rewrite);

                            if (!rewrite)
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k - 1);
                                cell_index_slice.erase(cell_index_slice.begin() + k - 1);
                                cell_index_slice.insert(cell_index_slice.begin() + k - 1, int(cell_graph.size() - 1));
                            }
                            else
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k);
                            }

                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == INNER_IN_BOTTOM_EX)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 0; k < cell_index_slice.size(); k++)
                    {
                        if (event_y > cell_graph[cell_index_slice[k]].ceiling.back().y && event_y < cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            rewrite = std::find(original_cell_index_slice.begin(), original_cell_index_slice.end(), cell_index_slice[k]) == original_cell_index_slice.end();

                            min_dist = INT_MAX;
                            for (int m = 0; m < curr_slice.size(); m++)
                            {
                                if (abs(curr_slice[m].y - cell_graph[cell_index_slice[k]].ceiling.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[m].y - cell_graph[cell_index_slice[k]].ceiling.back().y);
                                    c_index = m;
                                    c = Point2D(curr_slice[m].x, curr_slice[m].y);
                                }
                            }
                            curr_slice[c_index].isUsed = true;

                            min_dist = INT_MAX;
                            for (int n = 0; n < curr_slice.size(); n++)
                            {
                                if (abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y);
                                    f_index = n;
                                    f = Point2D(curr_slice[n].x, curr_slice[n].y);
                                }
                            }
                            curr_slice[f_index].isUsed = true;

                            curr_cell_idx = cell_index_slice[k];
                            executeOpenOperation(cell_graph, curr_cell_idx,
                                                 Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), // in top
                                                 Point2D(curr_slice[j].x, curr_slice[j].y),         // in bottom
                                                 c,
                                                 f,
                                                 rewrite);

                            if (!rewrite)
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k);
                                sub_cell_index_slices.clear();
                                sub_cell_index_slices = {int(cell_graph.size() - 2), int(cell_graph.size() - 1)};
                                cell_index_slice.insert(cell_index_slice.begin() + k, sub_cell_index_slices.begin(),
                                                        sub_cell_index_slices.end());
                            }
                            else
                            {
                                cell_index_slice.insert(cell_index_slice.begin() + k + 1, int(cell_graph.size() - 1));
                            }

                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == INNER_OUT_BOTTOM_EX)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 1; k < cell_index_slice.size(); k++)
                    {
                        if (event_y > cell_graph[cell_index_slice[k - 1]].ceiling.back().y && event_y < cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            rewrite = std::find(original_cell_index_slice.begin(), original_cell_index_slice.end(), cell_index_slice[k - 1]) == original_cell_index_slice.end();

                            min_dist = INT_MAX;
                            for (int m = 0; m < curr_slice.size(); m++)
                            {
                                if (abs(curr_slice[m].y - cell_graph[cell_index_slice[k - 1]].ceiling.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[m].y - cell_graph[cell_index_slice[k - 1]].ceiling.back().y);
                                    c_index = m;
                                    c = Point2D(curr_slice[m].x, curr_slice[m].y);
                                }
                            }
                            curr_slice[c_index].isUsed = true;

                            min_dist = INT_MAX;
                            for (int n = 0; n < curr_slice.size(); n++)
                            {
                                if (abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y);
                                    f_index = n;
                                    f = Point2D(curr_slice[n].x, curr_slice[n].y);
                                }
                            }
                            curr_slice[f_index].isUsed = true;

                            top_cell_idx = cell_index_slice[k - 1];
                            bottom_cell_idx = cell_index_slice[k];
                            executeCloseOperation(cell_graph, top_cell_idx, bottom_cell_idx,
                                                  c,
                                                  f,
                                                  rewrite);

                            if (!rewrite)
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k - 1);
                                cell_index_slice.erase(cell_index_slice.begin() + k - 1);
                                cell_index_slice.insert(cell_index_slice.begin() + k - 1, int(cell_graph.size() - 1));
                            }
                            else
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k);
                            }

                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == IN_EX)
                {
                    event_y = curr_slice[j].y;

                    if (!cell_index_slice.empty())
                    {
                        for (int k = 1; k < cell_index_slice.size(); k++)
                        {
                            if (event_y >= cell_graph[cell_index_slice[k - 1]].floor.back().y && event_y <= cell_graph[cell_index_slice[k]].ceiling.back().y)
                            {
                                executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_in
                                cell_index_slice.insert(cell_index_slice.begin() + k, int(cell_graph.size() - 1));
                                curr_slice[j].isUsed = true;
                                break;
                            }
                        }
                        if (event_y <= cell_graph[cell_index_slice.front()].ceiling.back().y)
                        {
                            executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_in
                            cell_index_slice.insert(cell_index_slice.begin(), int(cell_graph.size() - 1));
                            curr_slice[j].isUsed = true;
                        }
                        if (event_y >= cell_graph[cell_index_slice.back()].floor.back().y)
                        {
                            executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_in
                            cell_index_slice.insert(cell_index_slice.end(), int(cell_graph.size() - 1));
                            curr_slice[j].isUsed = true;
                        }
                    }
                    else
                    {
                        executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_in
                        cell_index_slice.emplace_back(int(cell_graph.size() - 1));
                        curr_slice[j].isUsed = true;
                    }
                }

                if (curr_slice[j].event_type == IN_BOTTOM_EX)
                {
                    event_y = curr_slice[j].y;

                    if (!cell_index_slice.empty())
                    {
                        for (int k = 1; k < cell_index_slice.size(); k++)
                        {
                            if (event_y >= cell_graph[cell_index_slice[k - 1]].floor.back().y && event_y <= cell_graph[cell_index_slice[k]].ceiling.back().y)
                            {

                                executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), // inner_in_top,
                                                          Point2D(curr_slice[j].x, curr_slice[j].y));                    // inner_in_bottom

                                cell_index_slice.insert(cell_index_slice.begin() + k, int(cell_graph.size() - 1));

                                curr_slice[j - 1].isUsed = true;
                                curr_slice[j].isUsed = true;

                                break;
                            }
                        }
                        if (event_y <= cell_graph[cell_index_slice.front()].ceiling.back().y)
                        {

                            executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), // inner_in_top,
                                                      Point2D(curr_slice[j].x, curr_slice[j].y));                    // inner_in_bottom

                            cell_index_slice.insert(cell_index_slice.begin(), int(cell_graph.size() - 1));

                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;
                        }
                        if (event_y >= cell_graph[cell_index_slice.back()].floor.back().y)
                        {

                            executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), // inner_in_top,
                                                      Point2D(curr_slice[j].x, curr_slice[j].y));                    // inner_in_bottom

                            cell_index_slice.insert(cell_index_slice.end(), int(cell_graph.size() - 1));

                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;
                        }
                    }
                    else
                    {
                        executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), // inner_in_top,
                                                  Point2D(curr_slice[j].x, curr_slice[j].y));                    // inner_in_bottom

                        cell_index_slice.emplace_back(int(cell_graph.size() - 1));

                        curr_slice[j - 1].isUsed = true;
                        curr_slice[j].isUsed = true;
                    }
                }

                if (curr_slice[j].event_type == OUT_EX)
                {
                    event_y = curr_slice[j].y;

                    for (int k = 0; k < cell_index_slice.size(); k++)
                    {
                        if (event_y >= cell_graph[cell_index_slice[k]].ceiling.back().y && event_y <= cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            curr_cell_idx = cell_index_slice[k];
                            executeInnerCloseOperation(cell_graph, curr_cell_idx, Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_out
                            cell_index_slice.erase(cell_index_slice.begin() + k);
                            curr_slice[j].isUsed = true;
                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == OUT_BOTTOM_EX)
                {
                    event_y = curr_slice[j].y;

                    for (int k = 0; k < cell_index_slice.size(); k++)
                    {
                        if (event_y >= cell_graph[cell_index_slice[k]].ceiling.back().y && event_y <= cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            curr_cell_idx = cell_index_slice[k];
                            executeInnerCloseOperation(cell_graph, curr_cell_idx, Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_out_top, inner_out_bottom
                            cell_index_slice.erase(cell_index_slice.begin() + k);
                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;
                            break;
                        }
                    }
                }
            }

            for (int j = 0; j < curr_slice.size(); j++)
            {
                if (curr_slice[j].event_type == IN)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 0; k < cell_index_slice.size(); k++)
                    {
                        if (event_y > cell_graph[cell_index_slice[k]].ceiling.back().y && event_y < cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            rewrite = std::find(original_cell_index_slice.begin(), original_cell_index_slice.end(), cell_index_slice[k]) == original_cell_index_slice.end(); // 若为true,则覆盖

                            min_dist = INT_MAX;
                            for (int m = 0; m < curr_slice.size(); m++)
                            {
                                if (abs(curr_slice[m].y - cell_graph[cell_index_slice[k]].ceiling.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[m].y - cell_graph[cell_index_slice[k]].ceiling.back().y);
                                    c_index = m;
                                    c = Point2D(curr_slice[m].x, curr_slice[m].y);
                                }
                            }
                            curr_slice[c_index].isUsed = true;

                            min_dist = INT_MAX;
                            for (int n = 0; n < curr_slice.size(); n++)
                            {
                                if (abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y);
                                    f_index = n;
                                    f = Point2D(curr_slice[n].x, curr_slice[n].y);
                                }
                            }
                            curr_slice[f_index].isUsed = true;

                            curr_cell_idx = cell_index_slice[k];
                            executeOpenOperation(cell_graph, curr_cell_idx,
                                                 Point2D(curr_slice[j].x, curr_slice[j].y),
                                                 c,
                                                 f,
                                                 rewrite);

                            if (!rewrite)
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k);
                                sub_cell_index_slices.clear();
                                sub_cell_index_slices = {int(cell_graph.size() - 2), int(cell_graph.size() - 1)};
                                cell_index_slice.insert(cell_index_slice.begin() + k, sub_cell_index_slices.begin(), sub_cell_index_slices.end());
                            }
                            else
                            {
                                cell_index_slice.insert(cell_index_slice.begin() + k + 1, int(cell_graph.size() - 1));
                            }

                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }
                if (curr_slice[j].event_type == OUT)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 1; k < cell_index_slice.size(); k++)
                    {
                        if (event_y > cell_graph[cell_index_slice[k - 1]].ceiling.back().y && event_y < cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            rewrite = std::find(original_cell_index_slice.begin(), original_cell_index_slice.end(), cell_index_slice[k - 1]) == original_cell_index_slice.end();

                            min_dist = INT_MAX;
                            for (int m = 0; m < curr_slice.size(); m++)
                            {
                                if (abs(curr_slice[m].y - cell_graph[cell_index_slice[k - 1]].ceiling.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[m].y - cell_graph[cell_index_slice[k - 1]].ceiling.back().y);
                                    c_index = m;
                                    c = Point2D(curr_slice[m].x, curr_slice[m].y);
                                }
                            }
                            curr_slice[c_index].isUsed = true;

                            min_dist = INT_MAX;
                            for (int n = 0; n < curr_slice.size(); n++)
                            {
                                if (abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y);
                                    f_index = n;
                                    f = Point2D(curr_slice[n].x, curr_slice[n].y);
                                }
                            }
                            curr_slice[f_index].isUsed = true;

                            top_cell_idx = cell_index_slice[k - 1];
                            bottom_cell_idx = cell_index_slice[k];

                            executeCloseOperation(cell_graph, top_cell_idx, bottom_cell_idx,
                                                  c,
                                                  f,
                                                  rewrite);

                            if (!rewrite)
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k - 1);
                                cell_index_slice.erase(cell_index_slice.begin() + k - 1);
                                cell_index_slice.insert(cell_index_slice.begin() + k - 1, int(cell_graph.size() - 1));
                            }
                            else
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k);
                            }

                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == IN_BOTTOM)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 0; k < cell_index_slice.size(); k++)
                    {
                        if (event_y > cell_graph[cell_index_slice[k]].ceiling.back().y && event_y < cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            rewrite = std::find(original_cell_index_slice.begin(), original_cell_index_slice.end(), cell_index_slice[k]) == original_cell_index_slice.end();

                            min_dist = INT_MAX;
                            for (int m = 0; m < curr_slice.size(); m++)
                            {
                                if (abs(curr_slice[m].y - cell_graph[cell_index_slice[k]].ceiling.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[m].y - cell_graph[cell_index_slice[k]].ceiling.back().y);
                                    c_index = m;
                                    c = Point2D(curr_slice[m].x, curr_slice[m].y);
                                }
                            }
                            curr_slice[c_index].isUsed = true;

                            min_dist = INT_MAX;
                            for (int n = 0; n < curr_slice.size(); n++)
                            {
                                if (abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y);
                                    f_index = n;
                                    f = Point2D(curr_slice[n].x, curr_slice[n].y);
                                }
                            }
                            curr_slice[f_index].isUsed = true;

                            curr_cell_idx = cell_index_slice[k];
                            executeOpenOperation(cell_graph, curr_cell_idx,
                                                 Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), // in top
                                                 Point2D(curr_slice[j].x, curr_slice[j].y),         // in bottom
                                                 c,
                                                 f,
                                                 rewrite);

                            if (!rewrite)
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k);
                                sub_cell_index_slices.clear();
                                sub_cell_index_slices = {int(cell_graph.size() - 2), int(cell_graph.size() - 1)};
                                cell_index_slice.insert(cell_index_slice.begin() + k, sub_cell_index_slices.begin(),
                                                        sub_cell_index_slices.end());
                            }
                            else
                            {
                                cell_index_slice.insert(cell_index_slice.begin() + k + 1, int(cell_graph.size() - 1));
                            }

                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == OUT_BOTTOM)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 1; k < cell_index_slice.size(); k++)
                    {
                        if (event_y > cell_graph[cell_index_slice[k - 1]].ceiling.back().y && event_y < cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            rewrite = std::find(original_cell_index_slice.begin(), original_cell_index_slice.end(), cell_index_slice[k - 1]) == original_cell_index_slice.end();

                            min_dist = INT_MAX;
                            for (int m = 0; m < curr_slice.size(); m++)
                            {
                                if (abs(curr_slice[m].y - cell_graph[cell_index_slice[k - 1]].ceiling.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[m].y - cell_graph[cell_index_slice[k - 1]].ceiling.back().y);
                                    c_index = m;
                                    c = Point2D(curr_slice[m].x, curr_slice[m].y);
                                }
                            }
                            curr_slice[c_index].isUsed = true;

                            min_dist = INT_MAX;
                            for (int n = 0; n < curr_slice.size(); n++)
                            {
                                if (abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y) < min_dist)
                                {
                                    min_dist = abs(curr_slice[n].y - cell_graph[cell_index_slice[k]].floor.back().y);
                                    f_index = n;
                                    f = Point2D(curr_slice[n].x, curr_slice[n].y);
                                }
                            }
                            curr_slice[f_index].isUsed = true;

                            top_cell_idx = cell_index_slice[k - 1];
                            bottom_cell_idx = cell_index_slice[k];
                            executeCloseOperation(cell_graph, top_cell_idx, bottom_cell_idx,
                                                  c,
                                                  f,
                                                  rewrite);

                            if (!rewrite)
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k - 1);
                                cell_index_slice.erase(cell_index_slice.begin() + k - 1);
                                cell_index_slice.insert(cell_index_slice.begin() + k - 1, int(cell_graph.size() - 1));
                            }
                            else
                            {
                                cell_index_slice.erase(cell_index_slice.begin() + k);
                            }

                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == INNER_IN)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 1; k < cell_index_slice.size(); k++)
                    {
                        if (event_y >= cell_graph[cell_index_slice[k - 1]].floor.back().y && event_y <= cell_graph[cell_index_slice[k]].ceiling.back().y)
                        {
                            executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_in
                            cell_index_slice.insert(cell_index_slice.begin() + k, int(cell_graph.size() - 1));
                            curr_slice[j].isUsed = true;
                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == INNER_IN_BOTTOM)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 1; k < cell_index_slice.size(); k++)
                    {
                        if (event_y >= cell_graph[cell_index_slice[k - 1]].floor.back().y && event_y <= cell_graph[cell_index_slice[k]].ceiling.back().y)
                        {

                            executeInnerOpenOperation(cell_graph, Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), // inner_in_top,
                                                      Point2D(curr_slice[j].x, curr_slice[j].y));                    // inner_in_bottom

                            cell_index_slice.insert(cell_index_slice.begin() + k, int(cell_graph.size() - 1));

                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;

                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == INNER_OUT)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 0; k < cell_index_slice.size(); k++)
                    {
                        if (event_y >= cell_graph[cell_index_slice[k]].ceiling.back().y && event_y <= cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            curr_cell_idx = cell_index_slice[k];
                            executeInnerCloseOperation(cell_graph, curr_cell_idx, Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_out
                            cell_index_slice.erase(cell_index_slice.begin() + k);
                            curr_slice[j].isUsed = true;
                            break;
                        }
                    }
                }

                if (curr_slice[j].event_type == INNER_OUT_BOTTOM)
                {
                    event_y = curr_slice[j].y;
                    for (int k = 0; k < cell_index_slice.size(); k++)
                    {
                        if (event_y >= cell_graph[cell_index_slice[k]].ceiling.back().y && event_y <= cell_graph[cell_index_slice[k]].floor.back().y)
                        {
                            curr_cell_idx = cell_index_slice[k];
                            executeInnerCloseOperation(cell_graph, curr_cell_idx, Point2D(curr_slice[j - 1].x, curr_slice[j - 1].y), Point2D(curr_slice[j].x, curr_slice[j].y)); // inner_out_top, inner_out_bottom
                            cell_index_slice.erase(cell_index_slice.begin() + k);
                            curr_slice[j - 1].isUsed = true;
                            curr_slice[j].isUsed = true;
                            break;
                        }
                    }
                }
            }

            for (int j = 0; j < curr_slice.size(); j++)
            {
                if (curr_slice[j].event_type == CEILING)
                {
                    cell_counter = countCells(curr_slice, j);
                    curr_cell_idx = cell_index_slice[cell_counter];
                    if (!curr_slice[j].isUsed)
                    {
                        executeCeilOperation(cell_graph, curr_cell_idx, Point2D(curr_slice[j].x, curr_slice[j].y));
                    }
                }
                if (curr_slice[j].event_type == FLOOR)
                {
                    cell_counter = countCells(curr_slice, j);
                    curr_cell_idx = cell_index_slice[cell_counter];
                    if (!curr_slice[j].isUsed)
                    {
                        executeFloorOperation(cell_graph, curr_cell_idx, Point2D(curr_slice[j].x, curr_slice[j].y));
                    }
                }
            }
        }
    }

    /** 测试辅助函数 **/
    void CoveragePlanner::drawObstaclePointType(cv::Mat &map, const Polygon &obstacle)
    {
        PolygonList obstacles = {obstacle};

        std::vector<Event> event_list = generateObstacleEventList(map, obstacles);

        for (auto event : event_list)
        {
            // ----------------------- 绿色 ---------------------------
            {
                if (event.event_type == IN)
                {
                    std::cout << event.x << ", " << event.y << ", IN" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
                }
                if (event.event_type == IN_TOP)
                {
                    std::cout << event.x << ", " << event.y << ", IN_TOP" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
                }
                if (event.event_type == IN_BOTTOM)
                {
                    std::cout << event.x << ", " << event.y << ", IN_BOTTOM" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
                }
                if (event.event_type == INNER_IN)
                {
                    std::cout << event.x << ", " << event.y << ", INNER_IN" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
                }
                if (event.event_type == INNER_IN_TOP)
                {
                    std::cout << event.x << ", " << event.y << ", INNER_IN_TOP" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
                }
                if (event.event_type == INNER_IN_BOTTOM)
                {
                    std::cout << event.x << ", " << event.y << ", INNER_IN_BOTTOM" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
                }
            }
            // --------------------------------------------------

            // ----------------------- 红色 ---------------------------
            {
                if (event.event_type == OUT)
                {
                    std::cout << event.x << ", " << event.y << ", OUT" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
                }
                if (event.event_type == OUT_TOP)
                {
                    std::cout << event.x << ", " << event.y << ", OUT_TOP" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
                }
                if (event.event_type == OUT_BOTTOM)
                {
                    std::cout << event.x << ", " << event.y << ", OUT_BOTTOM" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
                }
                if (event.event_type == INNER_OUT)
                {
                    std::cout << event.x << ", " << event.y << ", INNER_OUT" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
                }
                if (event.event_type == INNER_OUT_TOP)
                {
                    std::cout << event.x << ", " << event.y << ", INNER_OUT_TOP" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
                }
                if (event.event_type == INNER_OUT_BOTTOM)
                {
                    std::cout << event.x << ", " << event.y << ", INNER_OUT_BOTTOM" << std::endl;
                    cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
                }
            }
            // --------------------------------------------------

            // ---------------------- 偏黑色 -----------------------
            if (event.event_type == MIDDLE)
            {
                map.at<cv::Vec3b>(event.y, event.x) = cv::Vec3b(50, 50, 50);
            }
            // ---------------------- 黄色 -----------------------
            if (event.event_type == CEILING)
            {
                map.at<cv::Vec3b>(event.y, event.x) = cv::Vec3b(0, 255, 255);
            }
            // ---------------------- 蓝色 -----------------------
            if (event.event_type == FLOOR)
            {
                map.at<cv::Vec3b>(event.y, event.x) = cv::Vec3b(255, 0, 0);
            }
        }
    }

    void CoveragePlanner::drawWallPointType(cv::Mat &map, const Polygon &wall)
    {
        std::vector<Event> event_list = generateWallEventList(map, wall);
        for (auto event : event_list)
        {
            if (event.event_type == IN_EX)
            {
                std::cout << event.x << ", " << event.y << ", IN_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
            }
            if (event.event_type == IN_TOP_EX)
            {
                std::cout << event.x << ", " << event.y << ", IN_TOP_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
            }
            if (event.event_type == IN_BOTTOM_EX)
            {
                std::cout << event.x << ", " << event.y << ", IN_BOTTOM_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
            }
            if (event.event_type == OUT_EX)
            {
                std::cout << event.x << ", " << event.y << ", OUT_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
            }
            if (event.event_type == OUT_TOP_EX)
            {
                std::cout << event.x << ", " << event.y << ", OUT_TOP_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
            }
            if (event.event_type == OUT_BOTTOM_EX)
            {
                std::cout << event.x << ", " << event.y << ", OUT_BOTTOM_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
            }
            if (event.event_type == INNER_IN_EX)
            {
                std::cout << event.x << ", " << event.y << ", INNER_IN_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
            }
            if (event.event_type == INNER_IN_TOP_EX)
            {
                std::cout << event.x << ", " << event.y << ", INNER_IN_TOP_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
            }
            if (event.event_type == INNER_IN_BOTTOM_EX)
            {
                std::cout << event.x << ", " << event.y << ", INNER_IN_BOTTOM_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 255, 0), -1);
            }
            if (event.event_type == INNER_OUT_EX)
            {
                std::cout << event.x << ", " << event.y << ", INNER_OUT_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
            }
            if (event.event_type == INNER_OUT_TOP_EX)
            {
                std::cout << event.x << ", " << event.y << ", INNER_OUT_TOP_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
            }
            if (event.event_type == INNER_OUT_BOTTOM_EX)
            {
                std::cout << event.x << ", " << event.y << ", INNER_OUT_BOTTOM_EX" << std::endl;
                cv::circle(map, cv::Point(event.x, event.y), 2, cv::Scalar(0, 0, 255), -1);
            }
            if (event.event_type == MIDDLE)
            {
                map.at<cv::Vec3b>(event.y, event.x) = cv::Vec3b(50, 50, 50);
            }
            if (event.event_type == CEILING)
            {
                map.at<cv::Vec3b>(event.y, event.x) = cv::Vec3b(0, 255, 255);
            }
            if (event.event_type == FLOOR)
            {
                map.at<cv::Vec3b>(event.y, event.x) = cv::Vec3b(255, 0, 0);
            }
        }
    }

    void CoveragePlanner::printPathNodes(const std::deque<std::deque<Point2D>> &path)
    {
        for (const auto &subpath : path)
        {
            for (const auto &point : subpath)
            {
                std::cout << point.x << ", " << point.y << std::endl;
            }
            std::cout << std::endl;
        }
    }

}
相关推荐
科研实践课堂(公众号名称)1 分钟前
基于Fluent和深度学习算法驱动的流体力学计算与应用
人工智能·深度学习·算法
lele_ne31 分钟前
【python】将字符串转换成整数类型
开发语言·python·算法
爱是小小的癌33 分钟前
Java-数据结构-链表-高频面试题(1)
java·数据结构·算法·链表
走在考研路上1 小时前
力扣709.转换成小写字母
算法·leetcode·职场和发展
‘’林花谢了春红‘’2 小时前
Leetcode::3019. 按键变更的次数
算法·leetcode
带多刺的玫瑰2 小时前
Leecode刷题C语言之按键变更的次数
数据结构·算法
苹果2 小时前
C++二十三种设计模式之原型模式
c++·设计模式·原型模式
kk无敌怕3 小时前
分布式主键ID生成方式-snowflake雪花算法
分布式·算法
EnticE1523 小时前
[项目实战2]贪吃蛇游戏
开发语言·数据结构·c++·游戏
Fishel-4 小时前
线性回归api再介绍
算法·回归·线性回归