blob: 34396f2ae93ba0ccf4b6a7cc80a0a35ec25e31d8 [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/EventQueue.h"
#include "tests/Test.h"
namespace bentleyottmann {
class EventQueueTestingPeer {
public:
static Event NextEvent(EventQueue* eq) {
SkASSERT(eq->hasMoreEvents());
auto firstElement = eq->fQueue.begin();
// Extract event at the beginning of the queue.
Event event = *firstElement;
// Remove the beginning element from the queue.
eq->fQueue.erase(firstElement);
return event;
}
};
} // namespace bentleyottmann
using namespace bentleyottmann;
DEF_TEST(BO_EventQueueOrdering, reporter) {
{ // Check that event types are ordered correctly.
EventQueue::Queue q;
// Insert the events in reverse order.
Point eventPoint = {100, 100};
Segment s = {{100, 100}, {200, 200}};
q.insert(Event{eventPoint, Upper{s}});
Segment s0 = {{50, 50}, {150, 150}},
s1 = {{150, 50}, {50, 150}};
q.insert(Event{eventPoint, Cross{s0, s1}});
q.insert(Event{eventPoint, Lower{}});
// Make sure that the events are in the right order.
auto cursor = q.begin();
REPORTER_ASSERT(reporter, std::holds_alternative<Lower>(cursor->type));
++cursor;
REPORTER_ASSERT(reporter, std::holds_alternative<Cross>(cursor->type));
++cursor;
REPORTER_ASSERT(reporter, std::holds_alternative<Upper>(cursor->type));
}
}
DEF_TEST(BO_EventQueueBasic, reporter) {
{
EventQueue::Queue q;
EventQueue eq{std::move(q)};
REPORTER_ASSERT(reporter, !eq.hasMoreEvents());
}
{
EventQueue::Queue q;
Point eventPoint = {100, 100};
q.insert({eventPoint, Lower{} });
EventQueue eq{std::move(q)};
{
REPORTER_ASSERT(reporter, eq.hasMoreEvents());
Event e = EventQueueTestingPeer::NextEvent(&eq);
REPORTER_ASSERT(reporter, e.where == eventPoint);
REPORTER_ASSERT(reporter, !eq.hasMoreEvents());
}
}
{ // Check that Lower events are de-duplicated.
EventQueue::Queue q;
Point eventPoint = {100, 100};
q.insert({eventPoint, Lower{}});
q.insert({eventPoint, Lower{}});
EventQueue eq{std::move(q)};
{
// There should be only one lower because of queue de-duplication
REPORTER_ASSERT(reporter, eq.hasMoreEvents());
auto [p, _] = EventQueueTestingPeer::NextEvent(&eq);
REPORTER_ASSERT(reporter, p == eventPoint);
REPORTER_ASSERT(reporter, !eq.hasMoreEvents());
}
}
{ // Check that Lower distinct Lower events are distinct.
EventQueue::Queue q;
Point eventPoint1 = {100, 100};
Point eventPoint2 = {100, 101};
q.insert({eventPoint1, Lower{}});
q.insert({eventPoint2, Lower{}});
EventQueue eq{std::move(q)};
{
// There should be only one lower because of queue de-duplication
REPORTER_ASSERT(reporter, eq.hasMoreEvents());
auto [p, _] = EventQueueTestingPeer::NextEvent(&eq);
REPORTER_ASSERT(reporter, p == eventPoint1);
}
{
// There should be only one lower because of queue de-duplication
REPORTER_ASSERT(reporter, eq.hasMoreEvents());
auto [p, _] = EventQueueTestingPeer::NextEvent(&eq);
REPORTER_ASSERT(reporter, p == eventPoint2);
REPORTER_ASSERT(reporter, !eq.hasMoreEvents());
}
}
{ // Check that non-Lower events are separate.
EventQueue::Queue q;
Segment s0 {{0, 0}, {100, 100}};
Segment s1 {{0, 0}, {-100, 100}};
q.insert({Point{0, 0}, Upper{s0}});
q.insert({Point{0, 0}, Upper{s1}});
EventQueue eq{std::move(q)};
{
REPORTER_ASSERT(reporter, eq.hasMoreEvents());
Event e = EventQueueTestingPeer::NextEvent(&eq);
Point upperPt = Point{0, 0};
REPORTER_ASSERT(reporter, e.where == upperPt);
REPORTER_ASSERT(reporter, e.type.index() == 2);
Upper upper = std::get<Upper>(e.type);
REPORTER_ASSERT(reporter, !(upper < Upper{s1}) && !(Upper{s1} < upper));
Event e2 = EventQueueTestingPeer::NextEvent(&eq);
REPORTER_ASSERT(reporter, e2.where == upperPt);
REPORTER_ASSERT(reporter, e2.type.index() == 2);
Upper upper2 = std::get<Upper>(e2.type);
REPORTER_ASSERT(reporter, !(upper2 < Upper{s0}) && !(Upper{s0} < upper2));
REPORTER_ASSERT(reporter, !eq.hasMoreEvents());
}
}
}
enum HasDeletions {
kHasDeletions, // The handleDeletions call should be called.
kHasNoDeletions // The handleDeletions call should not be called.
};
struct TestEventHandler : public SweepLineInterface {
TestEventHandler(skiatest::Reporter*r,
Point eventPoint,
SkSpan<const Segment> deletions,
SkSpan<const Segment> insertions,
SkSpan<const Crossing> crossings,
HasDeletions hasDeletions = kHasDeletions)
: fR(r)
, fCandidateEventPoint{eventPoint}
, fDeletions{deletions.begin(), deletions.end()}
, fInsertions{insertions.begin(), insertions.end()}
, fCrossings{crossings.begin(), crossings.end()}
, fHasDeletions{hasDeletions} {}
void handleDeletions(Point eventPoint,
const DeletionSegmentSet& removing) override {
if (fHasDeletions == kHasNoDeletions) {
REPORTER_ASSERT(fR, false, "There should be no deletions.");
return;
}
REPORTER_ASSERT(fR, eventPoint == fCandidateEventPoint);
REPORTER_ASSERT(fR, removing.size() == fDeletions.size());
for (const Segment& s : fDeletions) {
REPORTER_ASSERT(fR, removing.find(s) != removing.end());
}
}
void
handleInsertionsAndCheckForNewCrossings(Point eventPoint,
const InsertionSegmentSet& inserting,
EventQueueInterface* queue) override {
REPORTER_ASSERT(fR, eventPoint == fCandidateEventPoint);
REPORTER_ASSERT(fR, inserting.size() == fInsertions.size());
for (const Segment& s : fInsertions) {
REPORTER_ASSERT(fR, inserting.find(s) != inserting.end());
}
for (const Crossing& crossing : fCrossings) {
auto [s0, s1, pt] = crossing;
queue->addCrossing(pt, s0, s1);
}
}
skiatest::Reporter* const fR;
const Point fCandidateEventPoint;
std::vector<Segment> fDeletions;
std::vector<Segment> fInsertions;
std::vector<Crossing> fCrossings;
const HasDeletions fHasDeletions;
};
DEF_TEST(BO_EventQueueHandlerInterface, reporter) {
{ // Check that a Lower event is added while processing the Upper event.
EventQueue::Queue q;
static constexpr Point eventPoint = {100, 100};
static constexpr Point endPoint = {200, 200};
static constexpr Segment s = {eventPoint, endPoint};
q.insert(Event{eventPoint, Upper{s}});
EventQueue eq{std::move(q)};
REPORTER_ASSERT(reporter, eq.hasMoreEvents());
TestEventHandler eh1{reporter, eventPoint, {}, {s}, {}, kHasNoDeletions};
eq.handleNextEventPoint(&eh1);
TestEventHandler eh2{reporter, endPoint, {}, {}, {}};
eq.handleNextEventPoint(&eh2);
REPORTER_ASSERT(reporter, !eq.hasMoreEvents());
}
{ // Check an entire crossing event.
EventQueue::Queue q;
static constexpr Point b0 = {100, 100};
static constexpr Point e0 = {200, 200};
static constexpr Segment s0 = {b0, e0};
static constexpr Point b1 = {200, 100};
static constexpr Point e1 = {100, 200};
static constexpr Segment s1 = {b1, e1};
static constexpr Point crossingPoint = {150, 150};
// Load crossing segments into the queue
q.insert(Event{b0, Upper{s0}});
q.insert(Event{b1, Upper{s1}});
EventQueue eq{std::move(q)};
REPORTER_ASSERT(reporter, eq.hasMoreEvents());
TestEventHandler eh1{reporter, b0, {}, {s0}, {}, kHasNoDeletions};
eq.handleNextEventPoint(&eh1);
TestEventHandler eh2{reporter, b1, {}, {s1}, {{s0, s1, crossingPoint}}, kHasNoDeletions};
eq.handleNextEventPoint(&eh2);
TestEventHandler eh3{reporter, crossingPoint, {s0, s1}, {s0, s1}, {}};
eq.handleNextEventPoint(&eh3);
TestEventHandler eh4{reporter, e1, {}, {}, {}};
eq.handleNextEventPoint(&eh4);
TestEventHandler eh5{reporter, e0, {}, {}, {}};
eq.handleNextEventPoint(&eh5);
REPORTER_ASSERT(reporter, !eq.hasMoreEvents());
}
}