#include "modules/bentleyottmann/include/Myers.h"
#include "include/core/SkSpan.h"
#include "include/private/base/SkAssert.h"
#include "include/private/base/SkTo.h"
#include "modules/bentleyottmann/include/Int96.h"
#include <algorithm>
#include <climits>
#include <cstdint>
#include <iterator>
#include <tuple>
#include <utility>
#include <vector>
namespace myers {
Point operator-(const Point& p0, const Point& p1) {
return {p0.x - p1.x, p0.y - p1.y};
}
std::tuple<int64_t, int64_t> point_to_s64(Point p) {
return std::make_tuple(SkToS64(p.x), SkToS64(p.y));
}
const Point& Segment::upper() const {
return fUpper;
}
const Point& Segment::lower() const {
return fLower;
}
std::tuple<int32_t, int32_t, int32_t, int32_t> Segment::bounds() const {
auto [left, right] = std::minmax(fUpper.x, fLower.x);
return std::make_tuple(left, fUpper.y, right, fLower.y);
}
bool Segment::isHorizontal() const {
return fUpper.y == fLower.y;
}
bool Segment::isVertical() const {
return fUpper.x == fLower.x;
}
int64_t cross(Point d0, Point d1) {
const auto [d0x, d0y] = point_to_s64(d0);
const auto [d1x, d1y] = point_to_s64(d1);
return d0x * d1y - d1x * d0y;
}
int64_t compare_slopes(const Segment& s0, const Segment& s1) {
if (s0.isHorizontal() || s1.isHorizontal()) {
if (s0.isHorizontal() && s1.isHorizontal()) {
return 0;
}
if (s0.isHorizontal()) {
return 1;
} else {
return -1;
}
}
const auto [u0, l0] = s0;
const auto [u1, l1] = s1;
const Point d0 = l0 - u0;
const Point d1 = l1 - u1;
SkASSERT(d0.y > 0 && d1.y > 0);
return cross(d0, d1);
}
bool slope_s0_less_than_slope_s1(const Segment& s0, const Segment& s1) {
return compare_slopes(s0, s1) < 0;
}
int64_t compare_point_to_segment(Point p, const Segment& s) {
const auto [u, l] = s;
SkASSERT(u.y <= p.y && p.y <= l.y);
{
const auto [left, right] = std::minmax(u.x, l.x);
if (p.x < left) {
return -1;
}
if (right < p.x) {
return 1;
}
}
if (s.isHorizontal()) {
return 0;
}
const Point dUToP = p - u;
const Point dS = l - u;
SkASSERT(dS.y > 0);
return cross(dUToP, dS);
}
bool segment_less_than_upper_to_insert(const Segment& segment, const Segment& to_insert) {
const int64_t compare = compare_point_to_segment(to_insert.upper(), segment);
return (compare > 0) || ((compare == 0) && slope_s0_less_than_slope_s1(segment, to_insert));
}
bool s0_less_than_s1_at_y(const Segment& s0, const Segment& s1, int32_t y) {
SkASSERT(!s0.isHorizontal() && !s1.isHorizontal());
const auto [u0, l0] = s0;
const auto [u1, l1] = s1;
const auto [left0, right0] = std::minmax(u0.x, l0.x);
const auto [left1, right1] = std::minmax(u1.x, l1.x);
if (right0 < left1) {
return true;
} else if (right1 < left0) {
return false;
}
const Point d0 = l0 - u0;
const Point d1 = l1 - u1;
SkASSERT(d0.y > 0 && d1.y > 0);
namespace bo = bentleyottmann;
using Int96 = bo::Int96;
const Int96 lhs = bo::multiply(d1.y, u0.x * SkToS64(d0.y) + (y - u0.y) * SkToS64(d0.x));
const Int96 rhs = bo::multiply(d0.y, u1.x * SkToS64(d1.y) + (y - u1.y) * SkToS64(d1.x));
return lhs < rhs || ((lhs == rhs) && slope_s0_less_than_slope_s1(s0, s1));
}
struct Event {
const int32_t y;
SkSpan<const Segment> begin;
SkSpan<const Segment> horizontal;
SkSpan<const Segment> end;
};
class EventQueue {
class Iterator {
public:
using value_type = Event;
using difference_type = ptrdiff_t;
using pointer = value_type*;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
Iterator(const EventQueue& eventQueue, size_t index)
: fEventQueue{eventQueue}
, fIndex{index} { }
Iterator(const Iterator& that) : Iterator{ that.fEventQueue, that.fIndex } { }
Iterator& operator++() { ++fIndex; return *this; }
Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
bool operator==(const Iterator& rhs) const { return fIndex == rhs.fIndex; }
bool operator!=(const Iterator& rhs) const { return fIndex != rhs.fIndex; }
value_type operator*() { return fEventQueue[fIndex]; }
friend difference_type operator-(Iterator lhs, Iterator rhs) {
return lhs.fIndex - rhs.fIndex;
}
private:
const EventQueue& fEventQueue;
size_t fIndex = 0;
};
class CompactEvent {
public:
const int32_t y;
const int32_t start;
const int32_t endOfBegin;
const int32_t endOfHorizontal;
const int32_t endOfEnd;
};
public:
static EventQueue Make(const SkSpan<const Segment> segments) {
SkASSERT(!segments.empty());
SkASSERT(segments.size() < INT32_MAX);
enum EventType {
kBegin = 0,
kHorizontal = 1,
kEnd = 2
};
struct SetupTuple {
int32_t yOrdering;
EventType type;
int32_t xTieBreaker;
Segment originalSegment;
bool operator==(const SetupTuple& r) const {
return
std::tie(this->yOrdering, this->type, this->xTieBreaker, this->originalSegment)
== std::tie(r.yOrdering, r.type, r.xTieBreaker, r.originalSegment);
}
};
std::vector<SetupTuple> eventOrdering;
for (const auto& s : segments) {
if (s.upper() == s.lower()) {
continue;
}
if (s.isHorizontal()) {
eventOrdering.push_back(SetupTuple{s.upper().y, kHorizontal, -s.upper().x, s});
} else {
eventOrdering.push_back(SetupTuple{s.upper().y, kBegin, -s.upper().x, s});
eventOrdering.push_back(SetupTuple{s.lower().y, kEnd, -s.lower().x, s});
}
}
auto eventLess = [](const SetupTuple& l, const SetupTuple& r) {
return std::tie(l.yOrdering, l.type, l.xTieBreaker) <
std::tie(r.yOrdering, r.type, r.xTieBreaker);
};
std::sort(eventOrdering.begin(), eventOrdering.end(), eventLess);
auto eraseFrom = std::unique(eventOrdering.begin(), eventOrdering.end());
eventOrdering.erase(eraseFrom, eventOrdering.end());
std::vector<CompactEvent> events;
std::vector<Segment> segmentStorage;
segmentStorage.reserve(eventOrdering.size());
int32_t currentY = eventOrdering.front().yOrdering;
int32_t start = 0,
endOfBegin = 0,
endOfHorizontal = 0,
endOfEnd = 0;
for (const auto& [y, type, _, s] : eventOrdering) {
if (currentY != y) {
events.push_back(CompactEvent{currentY,
start,
endOfBegin,
endOfHorizontal,
endOfEnd});
start = endOfBegin = endOfHorizontal = endOfEnd = segmentStorage.size();
currentY = y;
}
segmentStorage.push_back(s);
const size_t segmentCount = segmentStorage.size();
switch (type) {
case kBegin: endOfBegin = segmentCount;
[[fallthrough]];
case kHorizontal: endOfHorizontal = segmentCount;
[[fallthrough]];
case kEnd: endOfEnd = segmentCount;
}
}
events.push_back(CompactEvent{currentY,
start,
endOfBegin,
endOfHorizontal,
endOfEnd});
return EventQueue{std::move(events), std::move(segmentStorage)};
}
Event operator[](size_t i) const {
SkASSERT(i < fEvents.size());
auto& [y, start, endOfBegin, endOfHorizontal, endOfEnd] = fEvents[i];
SkSpan<const Segment> begin{&fSegmentStorage[start], endOfBegin - start};
SkSpan<const Segment>
horizontal{&fSegmentStorage[endOfBegin], endOfHorizontal - endOfBegin};
SkSpan<const Segment> end{&fSegmentStorage[endOfHorizontal], endOfEnd - endOfHorizontal};
return Event{y, begin, horizontal, end};
}
Iterator begin() const {
return Iterator{*this, 0};
}
Iterator end() const {
return Iterator{*this, fEvents.size()};
}
size_t size() const {
return fEvents.size();
}
bool empty() const {
return fEvents.empty();
}
private:
EventQueue(std::vector<CompactEvent>&& events, std::vector<Segment>&& segmentStorage)
: fEvents{std::move(events)}
, fSegmentStorage{std::move(segmentStorage)} {}
const std::vector<CompactEvent> fEvents;
const std::vector<Segment> fSegmentStorage;
};
class CrossingAccumulator {
public:
void recordCrossing(const Segment& s0, const Segment& s1) {
if (s0.upper() == s1.lower() || s0.lower() == s1.upper()) {
return;
}
if ((s0.upper() == s1.upper() || s0.lower() == s1.lower()) && compare_slopes(s0, s1) != 0) {
return;
}
fCrossings.emplace_back(s0, s1);
}
std::vector<Crossing> finishAndReleaseCrossings() {
return std::move(fCrossings);
}
private:
std::vector<Crossing> fCrossings;
};
class SweepLine {
static constexpr Segment kLeftStatusSentinel{{INT32_MIN, INT32_MIN}, {INT32_MIN, INT32_MAX}};
static constexpr Segment kRightStatusSentinel{{INT32_MAX, INT32_MIN}, {INT32_MAX, INT32_MAX}};
public:
SweepLine() {
fStatus.push_back(kLeftStatusSentinel);
fStatus.push_back(kRightStatusSentinel);
}
void handleEvent(Event e) {
auto& [y, beginnings, horizontals, endings] = e;
this->sortAndRecord(y);
this->handleBeginnings(y, beginnings);
this->handleHorizontals(y, horizontals);
this->handleEndings(endings);
}
std::vector<Crossing> finishAndReleaseCrossings() {
SkASSERT(this->statusEmpty());
return fCrossings.finishAndReleaseCrossings();
}
private:
using StatusLine = std::vector<Segment>;
bool statusEmpty() const {
return fStatus.size() == 2;
}
void sortAndRecord(int32_t y) {
if (fStatus.size() <= 3) {
return;
}
for (size_t i = 2; i < fStatus.size() - 1; ++i) {
const Segment t = fStatus[i];
size_t j = i;
for (; j > 1 && s0_less_than_s1_at_y(t, fStatus[j - 1], y); --j) {
fCrossings.recordCrossing(t, fStatus[j-1]);
fStatus[j] = fStatus[j-1];
}
fStatus[j] = t;
}
}
template <typename CrossingCheck>
void checkCrossingsLeftAndRight(
const Segment& segment, StatusLine::iterator insertionPoint, CrossingCheck check) {
for (auto cursor = std::make_reverse_iterator(insertionPoint); check(*cursor); ++cursor) {
fCrossings.recordCrossing(segment, *cursor);
}
for (auto cursor = insertionPoint; check(*cursor); ++cursor) {
fCrossings.recordCrossing(segment, *cursor);
}
}
void handleBeginnings(int32_t y, SkSpan<const Segment> inserting) {
for (const Segment& s : inserting) {
auto insertionPoint =
std::lower_bound(fStatus.begin(), fStatus.end(), s,
segment_less_than_upper_to_insert);
auto checkIntersect = [&](const Segment& toCheck) {
return compare_point_to_segment(s.upper(), toCheck) == 0;
};
this->checkCrossingsLeftAndRight(s, insertionPoint, checkIntersect);
fStatus.insert(insertionPoint, s);
}
}
void handleHorizontals(int32_t y, SkSpan<const Segment> horizontals) {
for (const Segment& s : horizontals) {
auto insertionPoint =
std::lower_bound(fStatus.begin(), fStatus.end(), s,
segment_less_than_upper_to_insert);
auto checkIntersection = [&](const Segment& toCheck) {
return compare_point_to_segment(s.upper(), toCheck) <= 0 &&
compare_point_to_segment(s.lower(), toCheck) >= 0;
};
this->checkCrossingsLeftAndRight(s, insertionPoint, checkIntersection);
fStatus.insert(insertionPoint, s);
}
this->handleEndings(horizontals);
}
void handleEndings(SkSpan<const Segment> removing) {
for (const Segment& s : removing) {
auto removedPoint = std::remove(fStatus.begin(), fStatus.end(), s);
SkASSERT(removedPoint != fStatus.end());
fStatus.erase(removedPoint, fStatus.end());
}
}
StatusLine fStatus;
CrossingAccumulator fCrossings;
};
std::vector<Crossing> myers_find_crossings(const SkSpan<const Segment> segments) {
const EventQueue eventQueue = EventQueue::Make(segments);
SweepLine sweepLine;
for (const Event& event : eventQueue) {
sweepLine.handleEvent(event);
}
return sweepLine.finishAndReleaseCrossings();
}
bool s0_intersects_s1(const Segment& s0, const Segment& s1) {
if (s1.upper().y < s0.upper().y
|| ((s1.upper().y == s0.upper().y) && (s1.lower().y > s0.lower().y))) {
return s0_intersects_s1(s1, s0);
}
SkASSERT(s0.upper().y <= s1.upper().y);
{
auto [left0, top0, right0, bottom0] = s0.bounds();
auto [left1, top1, right1, bottom1] = s1.bounds();
if (right1 < left0 || right0 < left1 || bottom1 < top0 || bottom0 < top1) {
return false;
}
}
auto [u0, l0] = s0;
auto [u1, l1] = s1;
const Point D0 = l0 - u0,
D1 = l1 - u1;
const Point U0toU1 = (u1 - u0);
const int64_t D0xU0toU1 = cross(D0, U0toU1);
if (D0xU0toU1 == 0) {
return true;
}
if (l1.y <= l0.y) {
const Point U0toL1 = (l1 - u0);
const int64_t D0xU0toL1 = cross(D0, U0toL1);
if (D0xU0toL1 == 0) {
return true;
}
return (D0xU0toU1 ^ D0xU0toL1) < 0;
} else {
const Point U1toL0 = (l0 - u1);
const int64_t D1xU1toL0 = cross(D1, U1toL0);
if (D1xU1toL0 == 0) {
return true;
}
return (D0xU0toU1 ^ D1xU1toL0) >= 0;
}
}
std::vector<Crossing> brute_force_crossings(SkSpan<Segment> segments) {
auto isNonZeroSegment = [](const Segment& segment) {
return segment.upper() != segment.lower();
};
const auto zeroSegments = std::partition(segments.begin(), segments.end(), isNonZeroSegment);
std::sort(segments.begin(), zeroSegments);
const auto duplicateSegments = std::unique(segments.begin(), zeroSegments);
SkSpan<const Segment> cleanSegments =
SkSpan{segments.data(), std::distance(segments.begin(), duplicateSegments)};
CrossingAccumulator crossings;
if (cleanSegments.size() >= 2) {
for (auto i = cleanSegments.begin(); i != std::prev(cleanSegments.end()); ++i) {
for (auto j = std::next(i); j != cleanSegments.end(); ++j) {
if (s0_intersects_s1(*i, *j)) {
crossings.recordCrossing(*i, *j);
}
}
}
}
return crossings.finishAndReleaseCrossings();
}
}