|  | // 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()); | 
|  | } | 
|  | } | 
|  |  |