| // 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 |