Увеличить геометрию / пересечение () кажется, что возвращает непоследовательный результат

У меня есть 3D-вектор, адаптированный для Boost Geometry как 2D-точка и как кольцо:

BOOST_GEOMETRY_REGISTER_POINT_2D(Vector3, float, cs::cartesian, x, y)
BOOST_GEOMETRY_REGISTER_RING( std::vector< Vector3 > )

Потом:

  1. Нарисуйте невыпуклый многоугольник (кольцо)
  2. Нарисуйте отрезок линии, который разрезает невыпуклый многоугольник и делит его на 2 (меньший чаще всего будет треугольником).
  3. Отразите меньшее из двух новых многоугольников над линейным сегментом

В результате получаются два перекрывающихся многоугольника с одной касательной кромкой.

Затем я проверяю пересечение двух многоугольников. В 15% случаев результат пересечения пустой, что является сюрпризом (меньший многоугольник может иметь площадь 1.0f..10.f, поэтому это не угловой случай)

std::deque< Polygon > output;
bg::intersection(bigger_Polygon, mirrored_over_cutting_lineseg_Polygon, output);
// output.size() == 0 in 15% of cases

В чем может быть причина? Я пробовал делать boost :: geometry :: right () для каждого многоугольника перед вызовом crossction (), но это не помогло. У меня заканчиваются идеи

РЕДАКТИРОВАТЬ ::

Я проверил, поможет ли создание новых колец с помощью типов Boost Geometry и двойного хранилища:

void my_intersection( std::vector<Vector3>& polyA, std::vector<Vector3>& polyB, std::deque< ... > & output ) {
    typedef bg::model::d2::point_xy<double> point_type;
    bg::model::ring< point_type > ringA;
    bg::model::ring< point_type > ringB;

    for( int i = 0; i < (int) polyA.size(); i ++ ) {
        bg::append( ringA, bg::make< point_type >( polyA[i].x, polyA[i].y ) );
    }

    for( int i = 0; i < (int) polyB.size(); i ++ ) {
        bg::append( ringB, bg::make< point_type >( polyB[i].x, polyB[i].y ) );
    }
    ...
}

Я делаю два вызова crossction () для polyA, polyB (мой начальный float Vector3) и для ringA, ringB. Затем появляется несоответствие:

A[6]( 58.20822143554688 100.0000076293945 , 89.18041229248047 100.0000076293945 , 100.0000076293945 93.08255767822266 , 100 80 , 64.98564147949219 80 , 58.20822143554688 100.0000076293945 )
B[4]( 89.18040466308594 100 , 100 93.08255004882812 , 93.72125244140625 90.17939758300781 , 89.18040466308594 100 )
INFO: ------ 1 vs 0 ------ INCONSISTENCY

«1» означает: выходная двухсторонняя очередь имеет size () == 1, поэтому происходит пересечение (это для пересечения ringA / ringB). «0» для Vector3 - пустой результат.

РЕДАКТИРОВАТЬ2:

Использование моделей повышения с типом хранения float приводит к тому, что неверные результаты также возвращаются для вызовов ringA и ringB. Я подтвердил это. Однажды я запутался, что двойники не изменяют "логику" ошибки, но это произошло из-за случайного удаления вызовов правильных (). С правильными вызовами () и двойным типом хранения для избыточных колец ringA / ringB мне не удалось получить пустое пересечение.

РЕДАКТИРОВАТЬ3:

Вот 5 случаев, в которых функция crossction () возвращается:

  • пустой результат для первых двух колец (std :: vector ‹Vector3>),
  • правильный результат size () == 1 при первом создании двойной типизированной копии колец std :: vector ‹> (с использованием моделей boost :: geometry).

Дело 1:

A[6]( 58.20822143554688 100.0000076293945 , 89.18041229248047 100.0000076293945 , 100.0000076293945 93.08255767822266 , 100 80 , 64.98564147949219 80 , 58.20822143554688 100.0000076293945 )

B[4]( 89.18040466308594 100 , 100 93.08255004882812 , 93.72125244140625 90.17939758300781 , 89.18040466308594 100 )

Кольца 1

Случай 2:

A[10]( 0 100 , 66.90238189697266 99.99999237060547 , 70.97279357910156 80 , 40 80 , 40 60 , 28.31221580505371 60 , 20 67.16078948974609 , 20 80 , 0 80 , 0 100 )

B[4]( 28.31221961975098 60.00000381469727 , 20.00000762939453 67.16079711914062 , 27.08192825317383 68.22066497802734 , 28.31221961975098 60.00000381469727 )

Кольца 2

Случай 3:

A[10]( 0 100 , 72.89675903320312 100 , 73.80842590332031 80 , 40 80 , 40 60 , 26.65167617797852 60 , 20 65.58068084716797 , 20 80 , 0 80 , 0 100 )

B[4]( 26.65167999267578 60.00000381469727 , 20.00000381469727 65.5806884765625 , 25.49577522277832 66.55047607421875 , 26.65167999267578 60.00000381469727 )

Кольца 3

Случай 4:

A[6]( 47.28099060058594 99.99999237060547 , 95.71660614013672 100 , 100 97.21295166015625 , 100 80 , 68.72442626953125 80.00000762939453 , 47.28099060058594 99.99999237060547 )

B[4]( 95.71659851074219 99.99999237060547 , 99.99998474121094 97.21293640136719 , 97.45189666748047 96.08384704589844 , 95.71659851074219 99.99999237060547 )

Кольца 4

Случай 5:

A[6]( 57.69097518920898 100 , 91.16551208496094 100 , 99.99999237060547 92.9193115234375 , 100 80 , 64.8609619140625 80 , 57.69097518920898 100 )

B[4]( 91.16550445556641 99.99999237060547 , 99.99998474121094 92.9193115234375 , 93.08920288085938 91.37748718261719 , 91.16550445556641 99.99999237060547 )

Кольца 5

РЕДАКТИРОВАТЬ4:

Вот функция, которую я использую для отражения многоугольника над линией пересечения (x0, y0) - (x1, y1). Касательная кромка создается с помощью этой функции - после зеркального отражения точка попадает в то же место.

Vector3 mirror_point( Vector3 p, float x0, float y0, float x1, float y1 ) {
    float dx = x1 - x0;
    float dy = y1 - y0;

    float a = ( dx * dx - dy * dy ) / ( dx * dx + dy * dy );
    float b = 2.0f * dx * dy / ( dx * dx + dy * dy );

    float x2 = a * ( p.x - x0 ) + b * ( p.y - y0 ) + x0;
    float y2 = b * ( p.x - x0 ) - a * ( p.y - y0 ) + y0;

    return Vector3( x2, y2, p.z );
}

person Dtruck    schedule 19.12.2012    source источник
comment
Можете ли вы добавить образец (ввода неудачной комбинации)?   -  person Barend Gehrels    schedule 20.12.2012
comment
@BarendGehrels: Я включил пример 2 колец, распечатанных с полной точностью. Загрузка их в кольца с двойным основанием устраняет сомнительное поведение   -  person Dtruck    schedule 20.12.2012
comment
Отличные изображения. Я скоро посмотрю на поплавок.   -  person Barend Gehrels    schedule 23.12.2012
comment
@BarendGehrels: круто. Я добавил функцию, которая может создавать некоторые специальные точки, подверженные ошибкам (использование распечатанных случаев не воспроизводилось для меня, хотя я использую 16-значную точность в cout; однако я читал в POLYGON (()), а не в кольцо). Может это поможет   -  person Dtruck    schedule 23.12.2012


Ответы (1)


Мой анализ вашего вклада:

Второй многоугольник (начиная с 24,57) направлен против часовой стрелки. Также второй многоугольник второго набора (начиная с 90,61) повернут против часовой стрелки. Поэтому обязательно следует вызвать boost :: geometry :: right. И это имеет значение.

Итак, если я использую geometry :: right, я получаю следующие результаты:

1) первая комбинация с использованием double: площадь пересечения = 12,3854, одна геометрия, 4 точки 2) первая комбинация с использованием float: зона пересечения = 12,3854, одна геометрия, 4 точки (то же самое) Этот результат идентичен результатам SQL Server

3) вторая комбинация с использованием двойной: площадь пересечения = 34,7862, одна геометрия, 4 балла 4) вторая комбинация с использованием float: площадь пересечения = 34,7862, одна геометрия, 4 балла. Этот результат идентичен результатам SQL Server.

Обратите внимание, что в обоих случаях второй многоугольник находится внутри первого многоугольника (во втором случае не касается, в первом случае касается - согласно SQL Server).

Таким образом, весь вывод кажется правильным. Вы упомянули: «устранить пустые пересечения», что недавно было решено в Boost.Geometry. Это исправление еще не выпущено в версии 1.52, но будет в версии 1.53. Так что, если это конкретная проблема, вы должны использовать версию Boost.Trunk.

Однако это не приведет к пустому выводу.

person Barend Gehrels    schedule 20.12.2012
comment
@BarendGehrels: Пост перестроен. Я уточнил, что двойник действительно решает проблему. Я удалил лишние части из своего сообщения. Данные, которые вы проанализировали, были частично созданы, когда я случайно удалил callse right (). Простите за это. Я приложил 5 хорошо протестированных случаев, в которых копирование из кольца с плавающей запятой в двойное кольцо не приводит к пустым случаям пересечения (а копирование в новые кольца с плавающей запятой не помогает). - person Dtruck; 21.12.2012
comment
И снова здравствуйте. Надеюсь, мой пост не останется без внимания. Я воссоздаю геометрию перед вызовом crossction (). Если я воссоздаю с помощью float (model :: d2 :: point_xy ‹float›) - изображения, подобные приведенному выше, вернут пустые пересечения. Если я воссоздаю с дважды правильной геометрией пересечения, возвращаются. Исходные данные могут быть плавающими или двойными. Реконструкция предназначена для изоляции от возможных сбоев кучи / стека (у меня могут быть неправильно написаны некоторые указатели, что означает, что все может случиться) - person Dtruck; 23.12.2012