Алгоритм триангуляции

Я решил создать простую демонстрацию, разделив многоугольник на множество треугольников. Вот что у меня есть до сих пор:

Задается последовательный список вершин (P1), образующих ребра многоугольника (в большинстве случаев многоугольник не является выпуклым); необходим набор треугольников

Перебрать все вершины внутри многоугольника P1 и найти ту (v), которая удовлетворяет следующим условиям:

  1. удалить v из многоугольника и сохранить новый в P2 предыдущая вершина v, соединенная со следующей, образует линию, которая не пересекает ни одно из P2 ребер

  2. v не находится внутри P2

Если они выполняются, мы можем заменить P1 на (P2 + triangle(prev(v), v, next(v))) и повторять это действие до тех пор, пока P1 не будет содержать более 3 вершин.

Итак, вопросы: верен ли этот алгоритм и как его можно реализовать на C/C++ наиболее очевидным и простым способом?


person shybovycha    schedule 17.06.2011    source источник
comment
Любое ограничение на многоугольник (например, вогнутое/выпуклое) или вам нужен общий случай?   -  person Vincent Mimoun-Prat    schedule 17.06.2011
comment
Ах, прошу прощения. совершенно забыл упомянуть этот факт... просто общий случай - как для выпуклых, так и для вогнутых. кроме дырок конечно   -  person shybovycha    schedule 17.06.2011


Ответы (3)


Я думаю, вы описываете метод обрезки ушей. Код для этого метода есть на http://cs.smith.edu/~orourke/books/ftp.html ; это код, описанный в книге Вычислительная геометрия на C.

person lhf    schedule 17.06.2011

Кажется, я закончил с реализацией этого алгоритма. Пожалуйста, проверьте это кто-нибудь. Спасибо!

typedef struct Point
{
  float x, y;
};

class MooPolygon
{
    private:
        vector<Point> points;

        int isVertexEar(int n, const vector<Point> &p)
        {
            return (isVertexInsideNewPoly(n, p) && !isEdgeIntersect(n, p));
        }

        int isEdgeIntersect(int n, const vector<Point> &p)
        {
            Point v = p[n];
            vector<Point> a;

            for (size_t i = 0; i < p.size(); i++)
                if (i != n)
                    a.push_back(p[i]);

            int c = 0, cnt = a.size(), prev = (cnt + (n - 1)) % cnt, next = n % cnt;

            Point v1 = a[prev], v2 = a[next];

            for (size_t i = 0, j = cnt - 1; i < cnt; j = i++)
            {
                if (prev == i || prev == j || next == i || next == j)
                    continue;

                Point v4 = a[j], v3 = a[i];

                float denominator = ((v4.y - v3.y) * (v2.x - v1.x)) - ((v4.x - v3.x) * (v2.y - v1.y));

                if (!denominator)
                    continue;

                float ua = (((v4.x - v3.x) * (v1.y - v3.y)) - ((v4.y - v3.y) * (v1.x - v3.x))) / denominator;
                float ub = (((v2.x - v1.x) * (v1.y - v3.y)) - ((v2.y - v1.y) * (v1.x - v3.x))) / denominator;

                //float x = v1.x + (ua * (v2.x - v1.x)), y = v1.y + (ua * (v2.y - v1.y));

                if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1)
                {
                    c = 1;
                    break;
                }
            }

            return c;
        }

        int isVertexInsideNewPoly(int n, const vector<Point> &p)
        {
            Point v = p[n];
            vector<Point> a;

            for (size_t i = 0; i < p.size(); i++)
                if (i != n)
                    a.push_back(p[i]);

            int c = 1;

            for (size_t i = 0, j = a.size() - 1; i < a.size(); j = i++) 
            {
                if ((((a[i].y <= v.y) && (v.y < a[j].y)) || ((a[j].y <= v.y) && (v.y < a[i].y))) && (v.x > (a[j].x - a[i].x) * (v.y - a[i].y) / (a[j].y - a[i].y) + a[i].x))
                    c = !c;
            }

            return c;
        }

        float dist(Point a, Point b)
        {
            return sqrt(  ((a.x - b.x) * (a.x - b.x)) + (((a.y - b.y) * (a.y - b.y)))  );
        }

    public:
        void push(const Point &p)
        {
            for (size_t i = 0; i < points.size(); i++)
            {
                if (dist(points[i], p) < 7.f)
                {
                    points.push_back(points[i]);

                    return;
                }
            }

            points.push_back(p);
        }

        void pop()
        {
            if (points.size() > 0)
                points.pop_back();
        }

        void clear()
        {
            points.clear();
        }

        Point v(int index)
        {
            return points[index];
        }

        size_t size()
        {
            return points.size();
        }

        void triangulate()
        {
            vector<Point> a;

            for (size_t i = 0; i < points.size(); i++)
            {
                a.push_back(points[i]);
            }

            points.clear();

            for (size_t t = a.size() - 1, i = 0, j = 1; i < a.size(); t = i++, j = (i + 1) % a.size())
            {
                if (a.size() == 3)
                {
                    points.push_back(a[0]);
                    points.push_back(a[1]);
                    points.push_back(a[2]);

                    break;
                }

                if (isVertexEar(i, a))
                {
                    points.push_back(a[t]);
                    points.push_back(a[i]);
                    points.push_back(a[j]);

                    a.erase(a.begin() + i, a.begin() + i + 1);

                    t = a.size() - 1;
                    i = 0;
                    j = 1;
                }
            }
        }
};
person shybovycha    schedule 18.06.2011

В коде есть ошибка в строке ниже. Строка находится в цикле for в функции push() вашего класса:

points.push_back(points[i]);

Вы передаете не нажатую Point, а пустой элемент самого вектора. Я изменил строку на

points.push_back(p);

и это сработало.

person Antonio Molinaro    schedule 24.08.2016