6

I have a 3D vector adapted for Boost Geometry as a 2D point, and as a ring:

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

Then:

  1. Draw some non-convex polygon (ring)
  2. Draw line segment, that cuts the non-convex polygon and divides it into 2 (the smaller one will be most often a triangle)
  3. Mirror smaller of the new 2 polygons over the line-segment

Resulting are two polygons, that are overlapping and have 1 tangent edge.

I then check for intersection of the two polygons. In 15% cases, the intersection result is empty, which is a surprise (the smaller polygon can have area 1.0f..10.f, so it's not a corner case)

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

What can be the reason? I tried doing boost::geometry::correct() on each polygon before calling intersection(), but it did not help. I am running out of ideas

EDIT::

I have tested if creating new rings, with Boost Geometry types and double storage type will help:

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 ) );
    }
    ...
}

I do two intersection() calls, for polyA, polyB (my initial float Vector3), and for ringA, ringB. Then, inconsistency appears:

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

The "1" means: output deque has size() == 1, so intersection occurs (this is for ringA/ringB intersection). "0" is for Vector3 -- empty result.

EDIT2:

Using boost models with float storage type causes incorrect results being returned also for the ringA & ringB calls. I have this confirmed. I got once confused that doubles do not change error's "logic", but it was because accidental removal of correct() calls. With the correct() calls and double storage type for ringA/ringB redundant rings I was unable to obtain empty intersection.

EDIT3:

Here are 5 cases in which intersection() returns:

  • empty result for the initial two rings (std::vector< Vector3 >),
  • correct result of size() == 1, when first creating double-typed copy of the std::vector<> rings (using boost::geometry models).

Case 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 )

Rings 1

Case 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 )

Rings 2

Case 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 )

Rings 3

Case 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 )

Rings 4

Case 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 )

Rings 5

EDIT4:

Here is a function, that I use to mirror polygon over the crossing line (x0,y0)-(x1,y1). The tangent edge is created using this function -- after mirroring, point lands in the same place.

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 );
}
Dtruck
  • 179
  • 5
  • Can you add a sample (of input of a failing combination)? – Barend Gehrels Dec 19 '12 at 21:32
  • @BarendGehrels: I have included example 2 rings, printed out with full float precision. Loading them into double based rings removes the doubtful behavior – Dtruck Dec 19 '12 at 21:41
  • Great images. I will look to the float's soon. – Barend Gehrels Dec 22 '12 at 23:09
  • @BarendGehrels: cool. I have added a function that might create some "special" error-prone points (using printed out cases did not reproduce for me, even though I use 16 digit precission in cout; however I was reading into POLYGON(()) not into ring). Maybe it will help – Dtruck Dec 23 '12 at 12:11

1 Answers1

5

My analysis on your input:

The second polygon (starting with 24.57) is counter clockwise. Also the second polygon of the second set (starting with 90.61) is counter clockwise. So boost::geometry::correct should certainly be called. And it makes difference.

So, if I use geometry::correct, I get the following results:

1) first combination, using double: intersection area=12.3854, one geometry, 4 points 2) first combination, using float: intersection area=12.3854, one geometry, 4 points (the same) This result is identical to the results of SQL Server

3) second combination, using double: intersection area=34.7862, one geometry, 4 points 4) second combination, using float: intersection area=34.7862, one geometry, 4 points This result is identical to the results of SQL Server.

Note that in both cases the second polygon is within the first polygon (in the second case non touching, in the first case it is touching - according to SQL Server).

So all output seems correct. You mention: "eliminate the empty intersections", that has been solved recently in Boost.Geometry. That fix is not yet released in 1.52, but will be in 1.53. So if that is the specific problem, you have to use the version of Boost.Trunk.

However, that would not cause empty output.

Barend Gehrels
  • 967
  • 5
  • 8
  • @BarendGehrels: The post is now rebuild. I have clarified that double does solve the problem. I removed redundant parts from my post. The data that you analysed was partly created when I accidently removed correct() callse. Sorry for that. I attached 5 well tested cases, in which doing copy from float ring to double ring causes no empty intersection cases (and copying to new float rings doesn't help). – Dtruck Dec 21 '12 at 14:03
  • Hello again. I hope my post will not be ignored. I recreate geometries before calling intersection(). If I recreate with float (model::d2::point_xy) -- pictures like the above will return empty intersections. If I recreate with double -- correct intersection geometries are being returned. Original data can be float or double. The recreation is intended as isolation from possible heap / stack disruptions (I could have some pointers incorrectly written, meaning anything can happen) – Dtruck Dec 22 '12 at 23:10