Перейти до основного вмісту

Перевірка перетину двох відрізків

Задано два відрізки (a,b)(a, b) і (c,d)(c, d). Потрібно перевірити, чи вони перетинаються. Звісно, можна знайти їхній перетин і перевірити, чи він не порожній, але для відрізків з цілочисловими координатами цього не вдасться зробити в цілих числах. Підхід, описаний тут, працює в цілих числах.

Коли підходить цей алгоритм?
  • Потрібно перевірити перетин саме однієї пари відрізків, а не знайти перетин серед nn відрізків? (якщо ні → Пошук пари відрізків, що перетинаються)
  • Координати кінців цілочислові й важлива точна відповідь без похибок із рухомою комою?
  • Достатньо факту «перетинаються чи ні», а не координат самої точки перетину? (якщо ні → Точка перетину прямих)

Алгоритм​

Спершу розглянемо випадок, коли відрізки лежать на одній прямій. У цьому випадку достатньо перевірити, чи перетинаються їхні проєкції на OxOx і OyOy. В іншому випадку точки aa і bb не повинні лежати по один бік від прямої (c,d)(c, d), а точки cc і dd не повинні лежати по один бік від прямої (a,b)(a, b). Це можна перевірити за допомогою кількох векторних добутків.

Реалізація​

Наведений алгоритм реалізовано для цілочислових точок. Звісно, його легко змінити, щоб він працював із числами з рухомою комою (double).

struct pt {
long long x, y;
pt() {}
pt(long long _x, long long _y) : x(_x), y(_y) {}
pt operator-(const pt& p) const { return pt(x - p.x, y - p.y); }
long long cross(const pt& p) const { return x * p.y - y * p.x; }
long long cross(const pt& a, const pt& b) const { return (a - *this).cross(b - *this); }
};

int sgn(const long long& x) { return x >= 0 ? x ? 1 : 0 : -1; }

bool inter1(long long a, long long b, long long c, long long d) {
if (a > b)
swap(a, b);
if (c > d)
swap(c, d);
return max(a, c) <= min(b, d);
}

bool check_inter(const pt& a, const pt& b, const pt& c, const pt& d) {
if (c.cross(a, d) == 0 && c.cross(b, d) == 0)
return inter1(a.x, b.x, c.x, d.x) && inter1(a.y, b.y, c.y, d.y);
return sgn(a.cross(b, c)) != sgn(a.cross(b, d)) &&
sgn(c.cross(d, a)) != sgn(c.cross(d, b));
}

Відеоматеріали​