Страницы

Поиск по вопросам

вторник, 17 декабря 2019 г.

Принадлежность каждой клетки к многоугольнику

#cpp


У меня есть многоугольник, точнее координаты его вершин, я его как будто накладываю
на клетчатую бумагу, так, что он делиться на клеточки. Поскольку он у меня не ложиться
точь-в-точь по клеточкам, то он будет их пересекать. Я ищу площадь каждой клетки, лежащей,
хотя бы частично, в многоугольнике. Итогом программы должна быть матрица, элементами
которой будут площади каждой клетки. Скажу на всякий случай, все клетки одного размера,
а площадь клетки - это площадь той части клетки, которая находится внутри многоугольника.

#include "pch.h"
#include 
#include 
#include 
#include 
#include  
#include 
using namespace std;
struct flat_vector
{
    double x;
    double y;
};

double angle(flat_vector xy1, flat_vector xy2) {
    return xy1.x * xy2.y - xy2.x*xy1.y;
}

flat_vector max(flat_vector& pxy1, flat_vector& pxy2) {
    if (pxy1.x >= pxy2.x && pxy1.y >= pxy2.y) {
        return flat_vector{
            pxy1.x, pxy1.y
        };
    }
    else if (pxy1.x >= pxy2.x && pxy1.y <= pxy2.y) {
        return flat_vector{
            pxy1.x,pxy2.y
        };
    }
    else if (pxy1.x <= pxy2.x && pxy1.y <= pxy2.y) {
        return flat_vector{
            pxy2.x, pxy2.y
        };
    }
    else {
        return flat_vector{
            pxy2.x, pxy1.y
        };
    }
}
flat_vector min(flat_vector& pxy1, flat_vector& pxy2) {
    if (pxy1.x >= pxy2.x && pxy1.y >= pxy2.y) {
        return flat_vector{
            pxy2.x, pxy2.y
        };
    }
    else if (pxy1.x >= pxy2.x && pxy1.y <= pxy2.y) {
        return flat_vector{
            pxy2.x,pxy1.y
        };
    }
    else if (pxy1.x <= pxy2.x && pxy1.y <= pxy2.y) {
        return flat_vector{
            pxy1.x, pxy1.y
        };
    }
    else {
        return flat_vector{
            pxy1.x, pxy2.y
        };
    }
}
flat_vector pere(flat_vector& xyp1, flat_vector& xyp2, flat_vector& pxy1, flat_vector&
pxy2) {
    double eps = 0.001;
    double z = ((xyp1.x - xyp2.x)*(pxy1.y - pxy2.y) - (xyp1.y - xyp2.y)*(pxy1.x -
pxy2.x));
    double q = ((xyp1.x*xyp2.y - xyp1.y * xyp2.x)*(pxy1.x - pxy2.x) - (xyp1.x - xyp2.x)*(pxy1.x*pxy2.y
- pxy1.y * pxy2.x)) / z;
    double e = ((xyp1.x*xyp2.y - xyp1.y * xyp2.x)*(pxy1.y - pxy2.y) - (xyp1.y - xyp2.y)*(pxy1.x*pxy2.y
- pxy1.y * pxy2.x)) / z;
    if (abs(z) >= eps) {
        return flat_vector{
            q,
            e
        };
    }
    else {
        return flat_vector{
            NULL,NULL
        };
    }
}


double are(vector& xyp, vector& pxy) {

    //vector xyp{ {0,0},{2,0},{5,1}, {4,4},{2,4} };// координаты многоугольника
    double n = xyp.size();
    //vector pxy{ {4,0.5},{5,0.5},{5,1.5},{4,1.5} };//координаты клетки
    double b = pxy.size();
    xyp.push_back(xyp[0]);
    pxy.push_back(pxy[0]);
    flat_vector nu{ NULL,NULL };
    vector xxyy;
    vector lxy;
    int k = 0;
    int m = 0;

    for (int i = 0; i < n; i++) {
        //cout << "Точки пересечения " << i + 1 << "-й вершины многоугольника с пикселем"
<< endl;
        //cout << "----------------------------------------------------------" << endl;
        for (int j = 0; j < b; j++) {
            xxyy.push_back(pere(xyp[i], xyp[i + 1], pxy[j], pxy[j + 1]));
            if (xxyy[k] != nu) {
                if (xxyy[k] < max(pxy[j], pxy[j + 1]) && xxyy[k] > min(pxy[j], pxy[j
+ 1])) {
                    lxy.push_back(xxyy[k]);
                    //cout << lxy[m] << endl;
                    m++;
                }
            }
            k++;
        }
    }

    vector xxy;
    vector x_y;
    double *ug;
    int s = 0;
    int l = 0;
    int full = 0;
    ug = new double[n];
    for (int j = 0; j < b; j++) { //определяю какие точки клетки лежат в многоугольнике
        for (int i = 0; i < n; i++) {
            xxy.push_back(pxy[j] - xyp[i]);
            x_y.push_back(xyp[i + 1] - pxy[j]);
        }
        for (int i = 0; i < n; i++) {
            ug[i] = angle(xxy[l], x_y[l]);
            l++;
            if (ug[i] > 0) {
                s = 1;
            }
        }
        //cout << "Точка " << pxy[j];
        if (s == 0) {
            lxy.push_back(pxy[j]);
            //cout << " внутри" << endl;
            full++;
            m++;
        }
        else
        {
            //cout << ") снаружи" << endl;
        }
        s = 0;
    }
    foo(lxy);
    sort(lxy);
    //lxy.push_back(lxy[0]);
    m = lxy.size();
    double *ar;
    ar = new double[m];
    double area = 0;
    for (int i = 0; i < m; i++) {
        //cout << lxy[i] << endl;
    }
    for (int i = 0; i < m - 1; i++) {
        ar[i] = angle(lxy[i], lxy[i + 1]);
        area += ar[i];
    }
    area /= 2;
    if (full == 4) {//если все 4 вершины пикселя лежат в мн-ке, то площадь клетки
автоматически равна 1
        area = 1;
    }
    else {
        area = abs(area);
    }
    //cout << area << endl;
    return area;
}
double Max(double& x, double& y) {
    if (x >= y) {
        return x;
    }
    else {
        return y;
    }
}
double Min(double& x, double& y) {
    if (x >= y) {
        return y;
    }
    else {
        return x;
    }
}
int main()
{
    //double x_data[4] = { 3.6,5.4,1.9,0.1 };//координаты многоугольника
    //double y_data[4] = { 0.2,1.2,7.3,6.3 };
    vector xy_data = { {3.6,0.2},{5.4,1.2},{1.9,7.3},{0.1,6.3} };
    //int shear_vector_x=6; //вектор сдвига
    //int shear_vector_y=1;
    flat_vector shear_vector = { 6,1 };
    int num_steps = 20;//количество шагов
    flat_vector D_shear_vector = shear_vector / num_steps;
    int numel = 4;//количество вершин многоугольника
    double x_max = 5.4;//крайние точки
    double y_max = 7.3;
    double x_min = 0.1;
    double y_min = 0.2;
    x_max = x_max - x_min;
    y_max = y_max - y_min;
    double nx = ceil(x_max);//количество столбцов матрицы
    double my = ceil(y_max);//количество строк
    double nu = 0;//это нужно, чтобы лишний раз Max и Min не переделывать с типами
    double pix_matr[6][8];//матрица в которую записывается площадь клетки, принадлежащей
многоугольнику, то бишь от 0 до 1
    pix_matr[0][0] = 0;
    double area;
    double background = 0.01;
    double contrast = 0.7;
    double target = background * (1 + contrast) / (1 - contrast);
    for (int t = 0; t < +num_steps; t++) {
        for (int p = 0; p < numel; p++) {
            x_max += shear_vector.x; //сдвигаем по х
            x_min = floor(x_min);
            x_min = Max(nu, x_min);
            x_max = ceil(x_max);
            nu = nx - 1;
            x_max = Min(x_max, nu);
            y_max += shear_vector.y;//сдвигаем по у
            y_min = floor(y_min);
            nu = 0;
            y_min = Max(nu, y_min);
            for (int iy = 0; iy < my; iy++) {
                cout << endl;
                for (int jx = 0; jx < nx; jx++) {
                    // (iy, jx) = координаты ЛЕВОГО НИЖНЕГО угла ЯЧЕЙКИ с
                    //клеткой, соответствующие индексам массива(iy + 1, jx + 1)
                    if (y_min <= iy && iy <= y_max && x_min <= jx && jx <= x_max)
{//проверяем, проецируется ли многоугольник на клетку хотя бы частично
                        double jjx = jx;
                        double iiy = iy;
                        vector pxy = { {jjx,iiy},{jjx + 1,iiy}, {jjx
+ 1,iiy + 1}, {jjx,iiy + 1} };
                        area = are(xy_data, pxy); 
                        pix_matr[iy + 1][jx + 1] = pix_matr[iy + 1][jx + 1] + area
* target + (1 - area)*background;

                    }
                    else {
                        pix_matr[iy + 1][jx + 1] = pix_matr[iy + 1][jx + 1] + background;
                    }
                    cout << pix_matr[iy + 1][jx + 1] << " ";
                }
            }
        }

        move(xy_data, D_shear_vector);
    }
    return 0;
}


Я не прошу смотреть весь код полностью, скажу, что ошибка у меня в 362 строке  area
= are(xy_data, pxy);
Я совместила 2 кода в один, и функция are, которая ищет площадь, была mainом в другом
коде. Я пока новичок и не придумала ничего умнее. Можно ли так создавать функции и
как поступить?

UPD: Поправила код, теперь выводит матрицу с площадями, только часть из них не верные

    #include "pch.h"
#include 
#include 
#include 
#include 
#include 
using namespace std;
struct flat_vector
{
    double x;
    double y;
};
....
int main()
{
    locale::global(locale(""));
    vector xyp{ {0,0},{2,0},{5,1}, {4,4},{2,4} };// координаты многоугольника
    int n = xyp.size();
    vector pxy{ {0,0},{1,0},{1,1},{0,1} };//координаты пикселя
    double b = pxy.size();
    xyp.push_back(xyp[0]);
    pxy.push_back(pxy[0]);
    flat_vector nu{ std::nan(""),std::nan("") };
    vector xxyy;
    int m = 0;
    int s = 0;
    int l = 0;
    int k = 0;
    double are[5][4];
    double area;
    for (int q = 0; q < 4; q++) {//столбцы
        for (int a = 0; a < 5; a++) {//строки
            vector lxy;//сюда записываем вершины клетки и точки пересечения
клетки и многоугольника
            for (int i = 0; i < n; i++) {//программа поиска точек пересечения, работает
правильно
                for (int j = 0; j < b; j++) {
                    xxyy.push_back(pere(xyp[i], xyp[i + 1], pxy[j], pxy[j + 1]));
                    if (xxyy[k] != nu) {
                        if (xxyy[k] < max(pxy[j], pxy[j + 1]) && xxyy[k] > min(pxy[j],
pxy[j + 1])) {
                            lxy.push_back(xxyy[k]);
                            //cout << lxy[m] << endl;
                            //m++;
                        }
                    }
                    k++;
                }
            }
            double *ug;//с помощью данного элемента будем проверять положительность угла
            ug = new double[n];
            int full = 0; //переменная, если будет равняться 4 (т.е. все вершины
клетки принадлежат мн-ку), то площадь равна 1
            l = 0;//счётчик отрезков, которые представлены ниже
            //Данные 2 вектора предназначены для того, чтобы проверять углы между
отрезками от точки (вершины клетки) до двух соседних сторон
            //Не буду много тут объяснять, потому что циклы ниже работают правильно
            vector xxy;//отрезок от
            vector x_y;
            for (int j = 0; j < b; j++) { //определяю какие точки пикселя лежат в
многоугольнике
                for (int i = 0; i < n; i++) {
                    xxy.push_back(pxy[j] - xyp[i]);
                    x_y.push_back(xyp[i + 1] - pxy[j]);
                }
                for (int i = 0; i < n; i++) {
                    ug[i] = angle(xxy[l], x_y[l]);
                    l++;
                    if (ug[i] > 0) {
                        s = 1;
                    }
                }
                //cout << "Точка " << pxy[j];
                if (s == 0) {
                    lxy.push_back(pxy[j]);
                    //cout << " внутри" << endl;
                    full++;
                }
                //else
                //{
                    //cout << ") снаружи" << endl;
                //}
                s = 0;
            }
            foo(lxy);//удаляем повторяющиеся координаты
            sort(lxy);//выстраиваем оставшиеся координаты последовательно
            lxy.push_back(lxy[0]);
            m = lxy.size();
            double *ar;//вспомогательный массив для нахождения площади
            ar = new double[m];
            area = 0;
            //for (int i = 0; i < m; i++) {
            //          cout << lxy[i] << endl;//этот цикл производит все точки клетки,
которые принадлежат многоугольнику
            //}
            for (int i = 0; i < m - 1; i++) {
                ar[i] = angle(lxy[i], lxy[i + 1]);
                area += ar[i];
            }
            area /= 2;//площадь
            if (full == 4) {//если все 4 вершины пикселя лежат в мн-ке, то площадь
пикселя автоматически равна 1
                area = 1;
            }
            else {
                area = abs(area);
            }
            are[a][q] = area;//записываем площадь всех клеток в матрицу
            cout << are[a][q] << " ";
            move(pxy, { 1,0 });//сдвигаем, чтобы перейти к следующей клетки справа
        }
        cout << endl;
        move(pxy, { -5,1 });//поскольку ряд проработан, переходим на строчку выше
    }
    return 0;
}


UPD2: проблема была в этой строке

if (xxyy[k] < max(pxy[j], pxy[j + 1]) && xxyy[k] > min(pxy[j], pxy[j + 1])){


Проверялось лежит ли точка пересечения на стороне клетки, но не проверялось, лежит
ли точка пересечения ещё и на стороне многоугольника. Когда заменяю эту строку на следующую 

    if (xxyy[k] < max(pxy[j], pxy[j + 1]) && xxyy[k] > min(pxy[j], pxy[j + 1]) &&
xxyy[k] < max(xyp[i], xyp[i + 1]) && xxyy[k] > min(xyp[i], xyp[i + 1])) {


то ошибку "Необработанное исключение по адресу 0x0FC5E906 (ucrtbased.dll) в ConsoleApplication3.exe:
Недопустимый параметр был передан функции, для которой недопустимые параметры вызывают
неустранимую ошибку., произошло" выдаёт почему-то в 246 строке 

lxy.push_back(lxy[0]);


До этого тут ошибки не было, просто не все значения были правильно и всё.
http://cpp.sh/65v7n

UPD3: Исправила ошибку, чтобы после последней элемента строки не сдвигалось
было

move(pxy, { 1,0 });


стало  

        if(a<4){
            move(pxy, {1,0});
        }else
        {
            move(pxy, { -4,1 });
            cout << endl;
        }


Теперь выводит правильно, но только 3 строки из 4, на 4 он останавливается (опять
тут lxy.push_back(lxy[0]);). Есть предположение, что выдаёт ошибку, потому что ни одна
вершина первой клетки 4 строки не лежит в многоугольнике.
http://cpp.sh/6jpul

UPD4:
ошибка вот в чём, когда у нас ни одна точка клетки не принадлежит многоугольнику,
то массив lxy будет пустой, это значит, что я не могу записать первый элемент в конец
lxy.push_back(lxy[0]), поэтому мы должны проверить, не пустой ли массив.

if(lxy.size()>0){


Программа почти завершена.
    


Ответы

Ответ 1



Вот вам алгоритм подсчёта площадей. Перебираете все клетки, которые потенциально могут попасть в многоугольник. Для этого можно, например, описать вокруг него прямоугольник, взяв максимум/минимум абсциссы/ординаты вершин многоугольника, и все клетки в этом прямоугольнике и перебирать (кстати, такой прямоугольник называется "описывающий прямоугольник" или "bounding box"). Теперь анализируем каждую клетку. Анализ довольно прост. Если все 4 вершины клетки лежат в многоульнике - ответ 1. Если 3 вершины внутри, то из площади клетки (1) нужно вычесть площадь треугольника, который отрезается многоугольником. Для этого перебором всех рёбер многоугольника находим их пересечения с границами клетки, находим 2 точки пересечения, и после этого найти площадь отрезаемого треугольника не составит труда. Если только 2 вершины внутри, то действуем аналогично пункту 2, также находим 2 точки пересечения рёбер многоугольника с границами клетки, и отрезаем трапецию (или считаем площадь оставшейся трапеции). Если только 1 вершина внутри, то пересечения клетки с многоугольником является треугольником, аналогично пункту (1) ищется его площадь, которая и даст ответ.

Комментариев нет:

Отправить комментарий