1*c8dee2aaSAndroid Build Coastguard Worker // Copyright 2023 Google LLC
2*c8dee2aaSAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3*c8dee2aaSAndroid Build Coastguard Worker
4*c8dee2aaSAndroid Build Coastguard Worker #include "modules/bentleyottmann/include/SweepLine.h"
5*c8dee2aaSAndroid Build Coastguard Worker
6*c8dee2aaSAndroid Build Coastguard Worker #include "modules/bentleyottmann/include/EventQueueInterface.h"
7*c8dee2aaSAndroid Build Coastguard Worker #include "modules/bentleyottmann/include/Point.h"
8*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include <iterator>
11*c8dee2aaSAndroid Build Coastguard Worker
12*c8dee2aaSAndroid Build Coastguard Worker using namespace bentleyottmann;
13*c8dee2aaSAndroid Build Coastguard Worker
14*c8dee2aaSAndroid Build Coastguard Worker namespace bentleyottmann {
15*c8dee2aaSAndroid Build Coastguard Worker struct SweepLineTestingPeer {
SweepLineTestingPeerbentleyottmann::SweepLineTestingPeer16*c8dee2aaSAndroid Build Coastguard Worker SweepLineTestingPeer(SweepLine* sl) : fSL{sl} {}
verifySweepLinebentleyottmann::SweepLineTestingPeer17*c8dee2aaSAndroid Build Coastguard Worker void verifySweepLine(int32_t y) const {
18*c8dee2aaSAndroid Build Coastguard Worker fSL->verify(y);
19*c8dee2aaSAndroid Build Coastguard Worker }
insertSegmentbentleyottmann::SweepLineTestingPeer20*c8dee2aaSAndroid Build Coastguard Worker void insertSegment(int i, const Segment& s) {
21*c8dee2aaSAndroid Build Coastguard Worker auto& v = fSL->fSweepLine;
22*c8dee2aaSAndroid Build Coastguard Worker v.insert(v.begin() + i, s);
23*c8dee2aaSAndroid Build Coastguard Worker }
sizebentleyottmann::SweepLineTestingPeer24*c8dee2aaSAndroid Build Coastguard Worker size_t size() const {
25*c8dee2aaSAndroid Build Coastguard Worker return fSL->fSweepLine.size();
26*c8dee2aaSAndroid Build Coastguard Worker }
27*c8dee2aaSAndroid Build Coastguard Worker
sweepLinebentleyottmann::SweepLineTestingPeer28*c8dee2aaSAndroid Build Coastguard Worker const std::vector<Segment>& sweepLine() const {
29*c8dee2aaSAndroid Build Coastguard Worker return fSL->fSweepLine;
30*c8dee2aaSAndroid Build Coastguard Worker }
31*c8dee2aaSAndroid Build Coastguard Worker
32*c8dee2aaSAndroid Build Coastguard Worker SweepLine* const fSL;
33*c8dee2aaSAndroid Build Coastguard Worker };
34*c8dee2aaSAndroid Build Coastguard Worker } // namespace bentleyottmann
35*c8dee2aaSAndroid Build Coastguard Worker
36*c8dee2aaSAndroid Build Coastguard Worker using TP = SweepLineTestingPeer;
37*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(BO_SweepLineSearch,reporter)38*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(BO_SweepLineSearch, reporter) {
39*c8dee2aaSAndroid Build Coastguard Worker {
40*c8dee2aaSAndroid Build Coastguard Worker SweepLine sweepLine;
41*c8dee2aaSAndroid Build Coastguard Worker TP tp{&sweepLine};
42*c8dee2aaSAndroid Build Coastguard Worker
43*c8dee2aaSAndroid Build Coastguard Worker Point p0 = {-100, -100},
44*c8dee2aaSAndroid Build Coastguard Worker p1 = { 100, 100},
45*c8dee2aaSAndroid Build Coastguard Worker p2 = { 100, -100},
46*c8dee2aaSAndroid Build Coastguard Worker p3 = {-100, 100};
47*c8dee2aaSAndroid Build Coastguard Worker Segment s0 = {p0, p1},
48*c8dee2aaSAndroid Build Coastguard Worker s1 = {p2, p3};
49*c8dee2aaSAndroid Build Coastguard Worker tp.insertSegment(1, s0);
50*c8dee2aaSAndroid Build Coastguard Worker tp.insertSegment(2, s1);
51*c8dee2aaSAndroid Build Coastguard Worker
52*c8dee2aaSAndroid Build Coastguard Worker auto& sl = tp.sweepLine();
53*c8dee2aaSAndroid Build Coastguard Worker
54*c8dee2aaSAndroid Build Coastguard Worker const Point crossing = {0, 0};
55*c8dee2aaSAndroid Build Coastguard Worker
56*c8dee2aaSAndroid Build Coastguard Worker const auto l = std::lower_bound(sl.begin(), sl.end(), crossing,
57*c8dee2aaSAndroid Build Coastguard Worker rounded_point_less_than_segment_in_x_lower);
58*c8dee2aaSAndroid Build Coastguard Worker
59*c8dee2aaSAndroid Build Coastguard Worker const auto r = std::lower_bound(l, sl.end(), crossing,
60*c8dee2aaSAndroid Build Coastguard Worker rounded_point_less_than_segment_in_x_upper);
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker // Remember that the index is off by 1 because of the left sentinel.
63*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, std::distance(sl.begin(), l) == 1);
64*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, std::distance(sl.begin(), r) == 3);
65*c8dee2aaSAndroid Build Coastguard Worker }
66*c8dee2aaSAndroid Build Coastguard Worker {
67*c8dee2aaSAndroid Build Coastguard Worker SweepLine sweepLine;
68*c8dee2aaSAndroid Build Coastguard Worker TP tp{&sweepLine};
69*c8dee2aaSAndroid Build Coastguard Worker
70*c8dee2aaSAndroid Build Coastguard Worker // No longer cross at {0, 0}, but still round to {0, 0}.
71*c8dee2aaSAndroid Build Coastguard Worker Point p0 = {-100, -100},
72*c8dee2aaSAndroid Build Coastguard Worker p1 = { 99, 100},
73*c8dee2aaSAndroid Build Coastguard Worker p2 = { 100, -100},
74*c8dee2aaSAndroid Build Coastguard Worker p3 = {-101, 100};
75*c8dee2aaSAndroid Build Coastguard Worker Segment s0 = {p0, p1},
76*c8dee2aaSAndroid Build Coastguard Worker s1 = {p2, p3};
77*c8dee2aaSAndroid Build Coastguard Worker tp.insertSegment(1, s0);
78*c8dee2aaSAndroid Build Coastguard Worker tp.insertSegment(2, s1);
79*c8dee2aaSAndroid Build Coastguard Worker
80*c8dee2aaSAndroid Build Coastguard Worker auto& sl = tp.sweepLine();
81*c8dee2aaSAndroid Build Coastguard Worker
82*c8dee2aaSAndroid Build Coastguard Worker const Point crossing = {0, 0};
83*c8dee2aaSAndroid Build Coastguard Worker
84*c8dee2aaSAndroid Build Coastguard Worker const auto l = std::lower_bound(sl.begin(), sl.end(), crossing,
85*c8dee2aaSAndroid Build Coastguard Worker rounded_point_less_than_segment_in_x_lower);
86*c8dee2aaSAndroid Build Coastguard Worker
87*c8dee2aaSAndroid Build Coastguard Worker const auto r = std::lower_bound(l, sl.end(), crossing,
88*c8dee2aaSAndroid Build Coastguard Worker rounded_point_less_than_segment_in_x_upper);
89*c8dee2aaSAndroid Build Coastguard Worker
90*c8dee2aaSAndroid Build Coastguard Worker // Remember that the index is off by 1 because of the left sentinel.
91*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, std::distance(sl.begin(), l) == 1);
92*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, std::distance(sl.begin(), r) == 3);
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker }
95*c8dee2aaSAndroid Build Coastguard Worker
DEF_TEST(BO_SweepLineInsert,reporter)96*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(BO_SweepLineInsert, reporter) {
97*c8dee2aaSAndroid Build Coastguard Worker {
98*c8dee2aaSAndroid Build Coastguard Worker SweepLine sweepLine;
99*c8dee2aaSAndroid Build Coastguard Worker TP tp{&sweepLine};
100*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(0);
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker { // Handle insert
103*c8dee2aaSAndroid Build Coastguard Worker SweepLine sweepLine;
104*c8dee2aaSAndroid Build Coastguard Worker TP tp{&sweepLine};
105*c8dee2aaSAndroid Build Coastguard Worker
106*c8dee2aaSAndroid Build Coastguard Worker class FailIfEventQueueAdd : public EventQueueInterface {
107*c8dee2aaSAndroid Build Coastguard Worker public:
108*c8dee2aaSAndroid Build Coastguard Worker void addCrossing(Point crossingPoint, const Segment& s0, const Segment& s1) override {
109*c8dee2aaSAndroid Build Coastguard Worker SK_ABORT("There should be no crossings.");
110*c8dee2aaSAndroid Build Coastguard Worker }
111*c8dee2aaSAndroid Build Coastguard Worker } eventQueue;
112*c8dee2aaSAndroid Build Coastguard Worker InsertionSegmentSet insertions;
113*c8dee2aaSAndroid Build Coastguard Worker Segment s = {{0, -100}, {0, 100}};
114*c8dee2aaSAndroid Build Coastguard Worker
115*c8dee2aaSAndroid Build Coastguard Worker insertions.insert(s);
116*c8dee2aaSAndroid Build Coastguard Worker
117*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-99);
118*c8dee2aaSAndroid Build Coastguard Worker
119*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleInsertionsAndCheckForNewCrossings(
120*c8dee2aaSAndroid Build Coastguard Worker Point{0, -100}, insertions, &eventQueue);
121*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, tp.size() == 3);
122*c8dee2aaSAndroid Build Coastguard Worker
123*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-99);
124*c8dee2aaSAndroid Build Coastguard Worker }
125*c8dee2aaSAndroid Build Coastguard Worker
126*c8dee2aaSAndroid Build Coastguard Worker { // Handle 3 segments where removing middle segment introduces crossing
127*c8dee2aaSAndroid Build Coastguard Worker SweepLine sweepLine;
128*c8dee2aaSAndroid Build Coastguard Worker TP tp{&sweepLine};
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker Point p0 = {-100, -100},
131*c8dee2aaSAndroid Build Coastguard Worker p1 = { 100, 100},
132*c8dee2aaSAndroid Build Coastguard Worker p2 = { 100, -100},
133*c8dee2aaSAndroid Build Coastguard Worker p3 = {-100, 100},
134*c8dee2aaSAndroid Build Coastguard Worker p4 = { 0, -100},
135*c8dee2aaSAndroid Build Coastguard Worker p5 = { 0, -50};
136*c8dee2aaSAndroid Build Coastguard Worker Segment s0 = {p0, p1},
137*c8dee2aaSAndroid Build Coastguard Worker s1 = {p2, p3},
138*c8dee2aaSAndroid Build Coastguard Worker s2 = {p4, p5};
139*c8dee2aaSAndroid Build Coastguard Worker
140*c8dee2aaSAndroid Build Coastguard Worker class CollectCrossings : public EventQueueInterface {
141*c8dee2aaSAndroid Build Coastguard Worker public:
142*c8dee2aaSAndroid Build Coastguard Worker void addCrossing(Point crossingPoint, const Segment& s0, const Segment& s1) override {
143*c8dee2aaSAndroid Build Coastguard Worker fCrossing.push_back({s0, s1, crossingPoint});
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker std::vector<Crossing> fCrossing;
146*c8dee2aaSAndroid Build Coastguard Worker } eventQueue;
147*c8dee2aaSAndroid Build Coastguard Worker
148*c8dee2aaSAndroid Build Coastguard Worker { // Simulate handling Upper s0
149*c8dee2aaSAndroid Build Coastguard Worker InsertionSegmentSet insertions;
150*c8dee2aaSAndroid Build Coastguard Worker insertions.insert(s0);
151*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-99);
152*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleInsertionsAndCheckForNewCrossings(
153*c8dee2aaSAndroid Build Coastguard Worker p0, insertions, &eventQueue);
154*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, tp.size() == 3);
155*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 0);
156*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-99);
157*c8dee2aaSAndroid Build Coastguard Worker }
158*c8dee2aaSAndroid Build Coastguard Worker { // Simulate handling Upper s2
159*c8dee2aaSAndroid Build Coastguard Worker InsertionSegmentSet insertions;
160*c8dee2aaSAndroid Build Coastguard Worker insertions.insert(s2);
161*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-99);
162*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleInsertionsAndCheckForNewCrossings(
163*c8dee2aaSAndroid Build Coastguard Worker p4, insertions, &eventQueue);
164*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, tp.size() == 4);
165*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 0);
166*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-99);
167*c8dee2aaSAndroid Build Coastguard Worker }
168*c8dee2aaSAndroid Build Coastguard Worker { // Simulate handling Upper s1
169*c8dee2aaSAndroid Build Coastguard Worker InsertionSegmentSet insertions;
170*c8dee2aaSAndroid Build Coastguard Worker insertions.insert(s1);
171*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-99);
172*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleInsertionsAndCheckForNewCrossings(
173*c8dee2aaSAndroid Build Coastguard Worker p2, insertions, &eventQueue);
174*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, tp.size() == 5);
175*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 0);
176*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-99);
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker { // Simulate handling Lower s2 which introduces a crossing
179*c8dee2aaSAndroid Build Coastguard Worker DeletionSegmentSet deletions; // empty set because this will be a lower event
180*c8dee2aaSAndroid Build Coastguard Worker InsertionSegmentSet insertions;
181*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-51);
182*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleDeletions(p5, deletions);
183*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, tp.size() == 4);
184*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleInsertionsAndCheckForNewCrossings(
185*c8dee2aaSAndroid Build Coastguard Worker p5, insertions, &eventQueue);
186*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 1);
187*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-51);
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker { // Handle crossing
190*c8dee2aaSAndroid Build Coastguard Worker DeletionSegmentSet deletions{s0, s1}; // empty set because this will be a lower event
191*c8dee2aaSAndroid Build Coastguard Worker InsertionSegmentSet insertions{s0, s1};
192*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(-1); // Check above the crossing
193*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleDeletions({0,0}, deletions);
194*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleInsertionsAndCheckForNewCrossings(
195*c8dee2aaSAndroid Build Coastguard Worker {0,0}, insertions, &eventQueue);
196*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, tp.size() == 4);
197*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 1);
198*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(1); // Make sure things are correct after deletion
199*c8dee2aaSAndroid Build Coastguard Worker }
200*c8dee2aaSAndroid Build Coastguard Worker { // Handle deletion s1
201*c8dee2aaSAndroid Build Coastguard Worker DeletionSegmentSet deletions{}; // empty set because this will be a lower event
202*c8dee2aaSAndroid Build Coastguard Worker InsertionSegmentSet insertions{};
203*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(99); // Check above the crossing
204*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleDeletions(p3, deletions);
205*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleInsertionsAndCheckForNewCrossings(
206*c8dee2aaSAndroid Build Coastguard Worker p3, insertions, &eventQueue);
207*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, tp.size() == 3);
208*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 1);
209*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(99); // Make sure sentinels are correct.
210*c8dee2aaSAndroid Build Coastguard Worker }
211*c8dee2aaSAndroid Build Coastguard Worker { // Handle deletion s0
212*c8dee2aaSAndroid Build Coastguard Worker DeletionSegmentSet deletions{}; // empty set because this will be a lower event
213*c8dee2aaSAndroid Build Coastguard Worker InsertionSegmentSet insertions{};
214*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(99); // Check above the crossing
215*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleDeletions(p1, deletions);
216*c8dee2aaSAndroid Build Coastguard Worker sweepLine.handleInsertionsAndCheckForNewCrossings(
217*c8dee2aaSAndroid Build Coastguard Worker p1, insertions, &eventQueue);
218*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, tp.size() == 2);
219*c8dee2aaSAndroid Build Coastguard Worker REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 1);
220*c8dee2aaSAndroid Build Coastguard Worker tp.verifySweepLine(99); // Make sure sentinels are correct.
221*c8dee2aaSAndroid Build Coastguard Worker }
222*c8dee2aaSAndroid Build Coastguard Worker }
223*c8dee2aaSAndroid Build Coastguard Worker }
224