Круг і Прямокутник

Останній приклад цього розділу поєднає разом код для круга та прямокутника. У нас є круг з позицією (cx, cy) і радіусом r та квадрат у положенні (rx, ry) з шириною/висотою (rw, rh).

Цей підхід визначення колізії буде складатися з двох послідовних дій. Спочатку ми перевіримо та дізнаємося, який край прямокутника ближче до круга, а потім за допомогою теореми Піфагора перевіримо, чи є зіткнення з цим ребром. Спочатку створимо тимчасові змінні для збереження найближчих X/Y значень країв квадрата. На початку ініціалізуємо їх значення початковим положенням центра круга:

let testX = cx;
let testY = cy;

Потім зробимо наступні перевірки:

Якщо круг ПРАВОРУЧ від квадрата, звіряємося з ПРАВИМ краєм квадрата.

Якщо круг ЛІВОРУЧ від квадрата, звіряємося з ЛІВИМ краєм квадрата.

Якщо круг НАД квадратом, звіряємося з ВЕРХНІМ краєм квадрата.

Якщо круг НИЖЧЕ квадрата, звіряємося з НИЖНІМ краєм квадрата.

Ось як це виглядає в коді з оператором if. Зверніть увагу на дещо інший if/else формат. Вам не потрібні фігурні дужки, якщо вам потрібно виконати лише оператор. Іноді це може здатися зручним і заощадливим способом, але будьте обережні, бо насправді ви не заощадите місце за рахунок читабельності коду, тому бажано так не робити!

if (cx < rx)           testX = rx;       // ліва сторона
else if (cx > rx + rw) testX = rx + rw;  // права сторона

if (cy < ry)           testY = ry;       // верхня сторона
else if (cy > ry + rh) testY = ry + rh;  // нижня сторона

Коли умови виконуються, то значення тестових змінних перезаписуються значеннями сторони квадрата. Якщо ж вищезазначені умови не виконуються, тоді значення тестових координат залишаються значеннями центру круга. Іноді одна координата матиме x або y значення якоїсь сторони квадрата, а інша x або y значення центру. Тобто у тестових значеннях будуть або значення якоїсь зі сторін квадрата або значення центру круга.

Щоб краще зрозуміти логіку і суть того, що тут відбувається, бажано знову взяти олівець і аркуш паперу та помалювати, щоб визначити які результати будуть при різних положеннях круга відносно квадрата. Тут ми по суті спочатку хочемо зрозуміти з якого боку від прямокутника знаходиться круг. Це своєю чергою допоможе визначити точку на цій стороні з якою ми й перевіримо наявність перетину з кругом. Кінець кінцем ми робимо вже знайому перевірку на колізію з точкою.

Тепер, коли ми знаємо, яке ребро квадрата потрібно перевіряти, ми запускаємо теорему Піфагора, використовуючи центр кола та значення координат, які ми знайшли вище:

const distX = cx - testX;
const distY = cy - testY;
const distance = sqrt((distX * distX) + (distY * distY));

Нарешті, ми порівняємо цю відстань з радіусом кола:

if (distance <= radius) {
    return true;
}

return false;

Here's a full example:

Ось повний приклад:
// змінні для положення та радіусу круга
let cx = 0;
let cy = 0;
let r = 30;

// змінні для положення та розмірів прямокутника
let sx;
let sy;
let sw = 200;
let sh = 200;

function setup() {
  createCanvas(window.innerWidth, window.innerHeight);
  noStroke();
  // визначення положення координат для лівого верхнього кута, щоб квадрат був по центру полотна
  sx = (width - sw) / 2;
  sy = (height - sh) / 2;
}

function draw() {
  background(255);

  // оновлення координат рухомого круга координатами курсора
  cx = mouseX;
  cy = mouseY;

  // результат перевірки на зіткнення
  const isHit = isCircleWithRectCollides(cx, cy, r, sx, sy, sw, sh);

  // при зіткненні змінюємо колір заливки
  if (isHit) {
    fill(255, 150, 0);
  } else {
    fill(0, 150, 255);
  }

  // малюємо квадрат
  rect(sx, sy, sw, sh);

  // малюємо круг
  fill(0, 150);
  ellipse(cx, cy, r * 2, r * 2);
}

// перевірка на перетин між кругом і прямокутником
function isCircleWithRectCollides(cx, cy, radius, rx, ry, rw, rh) {
  // тестові змінні точки, з якою буде відбуватися перевірка на перетин
  let testX = cx;
  let testY = cy;

  // які координати квадрата знаходяться найближче до круга?
  if (cx < rx) {
    testX = rx;       // якщо круг лівіше прямокутника
  } else if (cx > rx + rw) {
    testX = rx + rw;  // якщо круг правіше прямокутника
  }

  if (cy < ry) {
    testY = ry;       // якщо круг вище прямокутника
  } else if (cy > ry + rh) {
    testY = ry + rh;  // якщо круг нижче прямокутника
  }

  // визначення відстані до найближчої точки ребра прямокутника, якщо круг за межами прямокутника
  let distX = cx - testX;
  let distY = cy - testY;
  let distance = sqrt((distX * distX) + (distY * distY));

  // якщо відстань менша за радіус круга це колізія!
  if (distance <= radius) {
    return true;
  }

  return false;
}

Цей приклад створено на основі коду Мета Вордена (дякую!).

До речі, оскільки ми вже маємо функцію на перевірку перетину кругу з точкою, то її можна повторно використати в кінці нашої функції:

// змінні для положення та радіусу круга
// перевірка на перетин між кругом і прямокутником
function isCircleWithRectCollides(cx, cy, radius, rx, ry, rw, rh) {
  // тестові змінні точки, з якою буде відбуватися перевірка на перетин
  let testX = cx;
  let testY = cy;

  // які координати квадрата знаходяться найближче до круга?
  if (cx < rx) {
    testX = rx;       // якщо круг лівіше прямокутника
  } else if (cx > rx + rw) {
    testX = rx + rw;  // якщо круг правіше прямокутника
  }

  if (cy < ry) {
    testY = ry;       // якщо круг вище прямокутника
  } else if (cy > ry + rh) {
    testY = ry + rh;  // якщо круг нижче прямокутника
  }

   return isPointWithCircleCollides(testX, testY, cx, cy, radius);
}

У якості додаткової вправи ви можете намалювати точку тестових координат, що в результаті буде ковзати вздовж сторін квадрата або всередині нього і показувати вам позицію тої самої точки з якої відбувається перевірка на колізію з кругом. Якщо з такої програми прибрати малювання круга, то ви отримаєте свого роду точку, що завжди буде максимально наближеною до положення курсора, але ніколи не виходитиме за його межі. Також код з виявлення найближчих координат можна винести у окрему функцію, щоб використовувати її незалежно.

Далі: Завдання 2