blob: 4fa0d2214ed7d4a2bc079cba60bd419eda6d2285 [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 "include/private/base/SkAssert.h"
#include <algorithm>
#include <cstdint>
#include <utility>
namespace bentleyottmann {
// -- EventQueue -----------------------------------------------------------------------------------
std::optional<EventQueue> EventQueue::Make(SkSpan<const Segment> segments) {
Queue queue;
int32_t left = Point::Largest().x,
top = Point::Largest().y,
right = Point::Smallest().x,
bottom = Point::Smallest().y;
for(const Segment& s : segments) {
auto [l, t, r, b] = s.bounds();
left = std::min(l, left);
top = std::min(t, top);
right = std::max(r, right);
bottom = std::max(b, bottom);
queue.insert(Event{s.upper(), Upper{s}});
}
// If min max difference is too large, fail.
if (Point::DifferenceTooBig(Point{left, top}, Point{right, bottom})) {
return std::nullopt;
}
return EventQueue{std::move(queue)};
}
EventQueue::EventQueue(EventQueue::Queue&& queue) : fQueue{std::move(queue)} { }
void EventQueue::add(const Event& event) {
// New events must be up stream from the current event.
SkASSERT(fLastEventPoint < event.where);
fQueue.insert(event);
}
void EventQueue::addCrossing(Point crossingPoint, const Segment& s0, const Segment& s1) {
this->add({crossingPoint, Cross{s0, s1}});
fCrossings.push_back({s0, s1, crossingPoint});
}
bool EventQueue::hasMoreEvents() const {
return !fQueue.empty();
}
template<class... Ts>
struct Visitor : Ts... { using Ts::operator()...; };
template<class... Ts>
Visitor(Ts...) -> Visitor<Ts...>;
void EventQueue::handleNextEventPoint(SweepLineInterface* handler) {
SkASSERT(!fQueue.empty());
// Clear temp segment buffers.
fDeletionSet.clear();
fInsertionSet.clear();
// An events that are Lower points.
bool hasLower = false;
// Set up the visitors for the different event types.
auto handleLower = [&hasLower](const Lower& l) {
hasLower = true;
};
// Crossing Segments must be deleted and re-inserted in the sweep line.
auto handleCross = [this](const Cross& c) {
fDeletionSet.insert({c.s0, c.s1});
fInsertionSet.insert({c.s0, c.s1});
};
// Upper events are added to the sweep line, and a lower event is added to the event queue.
auto handleUpper = [this](const Upper& u) {
fInsertionSet.insert(u.s);
// Add the delete event for the inserted segment. Make sure we are not adding more events
// on this eventPoint.
SkASSERT(u.s.lower() != u.s.upper());
this->add(Event{u.s.lower(), Lower{}});
};
Visitor visitor{handleLower, handleCross, handleUpper};
const Point eventPoint = fQueue.begin()->where;
// We must make forward progress.
SkASSERT(fLastEventPoint < eventPoint);
fLastEventPoint = eventPoint;
// Accumulate changes for all events with the same event point.
auto cursor = fQueue.begin();
const auto queueEnd = fQueue.end();
for (; cursor != queueEnd && cursor->where == eventPoint;
++cursor) {
const Event& event = *cursor;
std::visit(visitor, event.type);
}
// Remove all accumulated events with the same event point.
fQueue.erase(fQueue.begin(), cursor);
if (hasLower || !fDeletionSet.empty()) {
// There are segments to delete.
handler->handleDeletions(eventPoint, fDeletionSet);
}
if (hasLower || !fDeletionSet.empty() || !fInsertionSet.empty()) {
// If there are insertions then insert them. If there are no insertions, but there were
// deletions we need to check for new crossings.
handler->handleInsertionsAndCheckForNewCrossings(eventPoint, fInsertionSet, this);
}
}
std::vector<Crossing> EventQueue::crossings() {
return std::vector<Crossing>{fCrossings.begin(), fCrossings.end()};
}
bool OrderBySlope::operator()(const bentleyottmann::Segment& s0,
const bentleyottmann::Segment& s1) const {
return compare_slopes(s0, s1) < 0;
}
} // namespace bentleyottmann