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

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

Задано два відрізки (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));
}

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