blob: 24fd600b4d7506ef9723235f265a22a3597c6b16 [file] [log] [blame]
// Copyright 2023 Google LLC
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
#include "modules/bentleyottmann/include/SweepLine.h"
#include "modules/bentleyottmann/include/EventQueueInterface.h"
#include "modules/bentleyottmann/include/Point.h"
#include "tests/Test.h"
#include <iterator>
using namespace bentleyottmann;
namespace bentleyottmann {
struct SweepLineTestingPeer {
SweepLineTestingPeer(SweepLine* sl) : fSL{sl} {}
void verifySweepLine(int32_t y) const {
fSL->verify(y);
}
void insertSegment(int i, const Segment& s) {
auto& v = fSL->fSweepLine;
v.insert(v.begin() + i, s);
}
size_t size() const {
return fSL->fSweepLine.size();
}
const std::vector<Segment>& sweepLine() const {
return fSL->fSweepLine;
}
SweepLine* const fSL;
};
} // namespace bentleyottmann
using TP = SweepLineTestingPeer;
DEF_TEST(BO_SweepLineSearch, reporter) {
{
SweepLine sweepLine;
TP tp{&sweepLine};
Point p0 = {-100, -100},
p1 = { 100, 100},
p2 = { 100, -100},
p3 = {-100, 100};
Segment s0 = {p0, p1},
s1 = {p2, p3};
tp.insertSegment(1, s0);
tp.insertSegment(2, s1);
auto& sl = tp.sweepLine();
const Point crossing = {0, 0};
const auto l = std::lower_bound(sl.begin(), sl.end(), crossing,
rounded_point_less_than_segment_in_x_lower);
const auto r = std::lower_bound(l, sl.end(), crossing,
rounded_point_less_than_segment_in_x_upper);
// Remember that the index is off by 1 because of the left sentinel.
REPORTER_ASSERT(reporter, std::distance(sl.begin(), l) == 1);
REPORTER_ASSERT(reporter, std::distance(sl.begin(), r) == 3);
}
{
SweepLine sweepLine;
TP tp{&sweepLine};
// No longer cross at {0, 0}, but still round to {0, 0}.
Point p0 = {-100, -100},
p1 = { 99, 100},
p2 = { 100, -100},
p3 = {-101, 100};
Segment s0 = {p0, p1},
s1 = {p2, p3};
tp.insertSegment(1, s0);
tp.insertSegment(2, s1);
auto& sl = tp.sweepLine();
const Point crossing = {0, 0};
const auto l = std::lower_bound(sl.begin(), sl.end(), crossing,
rounded_point_less_than_segment_in_x_lower);
const auto r = std::lower_bound(l, sl.end(), crossing,
rounded_point_less_than_segment_in_x_upper);
// Remember that the index is off by 1 because of the left sentinel.
REPORTER_ASSERT(reporter, std::distance(sl.begin(), l) == 1);
REPORTER_ASSERT(reporter, std::distance(sl.begin(), r) == 3);
}
}
DEF_TEST(BO_SweepLineInsert, reporter) {
{
SweepLine sweepLine;
TP tp{&sweepLine};
tp.verifySweepLine(0);
}
{ // Handle insert
SweepLine sweepLine;
TP tp{&sweepLine};
class FailIfEventQueueAdd : public EventQueueInterface {
public:
void addCrossing(Point crossingPoint, const Segment& s0, const Segment& s1) override {
SK_ABORT("There should be no crossings.");
}
} eventQueue;
InsertionSegmentSet insertions;
Segment s = {{0, -100}, {0, 100}};
insertions.insert(s);
tp.verifySweepLine(-99);
sweepLine.handleInsertionsAndCheckForNewCrossings(
Point{0, -100}, insertions, &eventQueue);
REPORTER_ASSERT(reporter, tp.size() == 3);
tp.verifySweepLine(-99);
}
{ // Handle 3 segments where removing middle segment introduces crossing
SweepLine sweepLine;
TP tp{&sweepLine};
Point p0 = {-100, -100},
p1 = { 100, 100},
p2 = { 100, -100},
p3 = {-100, 100},
p4 = { 0, -100},
p5 = { 0, -50};
Segment s0 = {p0, p1},
s1 = {p2, p3},
s2 = {p4, p5};
class CollectCrossings : public EventQueueInterface {
public:
void addCrossing(Point crossingPoint, const Segment& s0, const Segment& s1) override {
fCrossing.push_back({s0, s1, crossingPoint});
}
std::vector<Crossing> fCrossing;
} eventQueue;
{ // Simulate handling Upper s0
InsertionSegmentSet insertions;
insertions.insert(s0);
tp.verifySweepLine(-99);
sweepLine.handleInsertionsAndCheckForNewCrossings(
p0, insertions, &eventQueue);
REPORTER_ASSERT(reporter, tp.size() == 3);
REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 0);
tp.verifySweepLine(-99);
}
{ // Simulate handling Upper s2
InsertionSegmentSet insertions;
insertions.insert(s2);
tp.verifySweepLine(-99);
sweepLine.handleInsertionsAndCheckForNewCrossings(
p4, insertions, &eventQueue);
REPORTER_ASSERT(reporter, tp.size() == 4);
REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 0);
tp.verifySweepLine(-99);
}
{ // Simulate handling Upper s1
InsertionSegmentSet insertions;
insertions.insert(s1);
tp.verifySweepLine(-99);
sweepLine.handleInsertionsAndCheckForNewCrossings(
p2, insertions, &eventQueue);
REPORTER_ASSERT(reporter, tp.size() == 5);
REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 0);
tp.verifySweepLine(-99);
}
{ // Simulate handling Lower s2 which introduces a crossing
DeletionSegmentSet deletions; // empty set because this will be a lower event
InsertionSegmentSet insertions;
tp.verifySweepLine(-51);
sweepLine.handleDeletions(p5, deletions);
REPORTER_ASSERT(reporter, tp.size() == 4);
sweepLine.handleInsertionsAndCheckForNewCrossings(
p5, insertions, &eventQueue);
REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 1);
tp.verifySweepLine(-51);
}
{ // Handle crossing
DeletionSegmentSet deletions{s0, s1}; // empty set because this will be a lower event
InsertionSegmentSet insertions{s0, s1};
tp.verifySweepLine(-1); // Check above the crossing
sweepLine.handleDeletions({0,0}, deletions);
sweepLine.handleInsertionsAndCheckForNewCrossings(
{0,0}, insertions, &eventQueue);
REPORTER_ASSERT(reporter, tp.size() == 4);
REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 1);
tp.verifySweepLine(1); // Make sure things are correct after deletion
}
{ // Handle deletion s1
DeletionSegmentSet deletions{}; // empty set because this will be a lower event
InsertionSegmentSet insertions{};
tp.verifySweepLine(99); // Check above the crossing
sweepLine.handleDeletions(p3, deletions);
sweepLine.handleInsertionsAndCheckForNewCrossings(
p3, insertions, &eventQueue);
REPORTER_ASSERT(reporter, tp.size() == 3);
REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 1);
tp.verifySweepLine(99); // Make sure sentinels are correct.
}
{ // Handle deletion s0
DeletionSegmentSet deletions{}; // empty set because this will be a lower event
InsertionSegmentSet insertions{};
tp.verifySweepLine(99); // Check above the crossing
sweepLine.handleDeletions(p1, deletions);
sweepLine.handleInsertionsAndCheckForNewCrossings(
p1, insertions, &eventQueue);
REPORTER_ASSERT(reporter, tp.size() == 2);
REPORTER_ASSERT(reporter, eventQueue.fCrossing.size() == 1);
tp.verifySweepLine(99); // Make sure sentinels are correct.
}
}
}