Як я можу сказати, чи перетинаються коло та прямокутник у 2D евклідовому просторі? (тобто класична двовимірна геометрія)
Як я можу сказати, чи перетинаються коло та прямокутник у 2D евклідовому просторі? (тобто класична двовимірна геометрія)
Відповіді:
Є лише два випадки, коли коло перетинається з прямокутником:
Зауважте, що для цього не потрібно, щоб прямокутник був паралельним осі.
(Один із способів побачити це: якщо жоден з ребер не має точки в колі (якщо всі ребра повністю «поза» кола), єдиний спосіб, коли коло все ще може перетинати полігон, це якщо він повністю лежить всередині багатокутник.)
З цим розумінням, що - щось подібне до наступного буде працювати, де коло має центр P
і радіус R
, а прямокутник має вершини A
, B
, C
,D
в такому порядку (не повний код):
def intersect(Circle(P, R), Rectangle(A, B, C, D)):
S = Circle(P, R)
return (pointInRectangle(P, Rectangle(A, B, C, D)) or
intersectCircle(S, (A, B)) or
intersectCircle(S, (B, C)) or
intersectCircle(S, (C, D)) or
intersectCircle(S, (D, A)))
Якщо ви пишете будь-яку геометрію, ви, мабуть, вже маєте вищевказані функції у своїй бібліотеці. В іншому випадку pointInRectangle()
може бути реалізований декількома способами; будь-яка із загальних точок багатокутника методах буде працювати, але для прямокутника ви можете просто перевірити, чи працює це:
0 ≤ AP·AB ≤ AB·AB and 0 ≤ AP·AD ≤ AD·AD
І intersectCircle()
це також легко здійснити: одним із способів було б перевірити, чи підніжжя перпендикуляра від P
лінії є достатньо близько та між кінцевими точками, і перевірити кінцеві точки в іншому випадку.
Прикольне те, що ця сама ідея працює не просто для прямокутників, а для перетину кола з будь-яким простим багатокутником - навіть не повинно бути опуклим!
Ось як я це зробив:
bool intersects(CircleType circle, RectType rect)
{
circleDistance.x = abs(circle.x - rect.x);
circleDistance.y = abs(circle.y - rect.y);
if (circleDistance.x > (rect.width/2 + circle.r)) { return false; }
if (circleDistance.y > (rect.height/2 + circle.r)) { return false; }
if (circleDistance.x <= (rect.width/2)) { return true; }
if (circleDistance.y <= (rect.height/2)) { return true; }
cornerDistance_sq = (circleDistance.x - rect.width/2)^2 +
(circleDistance.y - rect.height/2)^2;
return (cornerDistance_sq <= (circle.r^2));
}
Ось як це працює:
Перша пара ліній обчислює абсолютні значення різниці x і y між центром кола і центром прямокутника. Це згортає чотири квадрати вниз, в один, так що обчислення не потрібно робити чотири рази. На зображенні показана область, в якій зараз повинен лежати центр кола. Зверніть увагу, що показано лише один квадрант. Прямокутник є сірою областю, а червона рамка окреслює критичну область, яка знаходиться рівно на один радіус від країв прямокутника. Центр кола повинен бути в межах цієї червоної межі, щоб відбулося перехрестя.
Друга пара ліній усуває легкі випадки, коли коло знаходиться досить далеко від прямокутника (в будь-якому напрямку), що жодне перетин не можливе. Це відповідає зеленій зоні на зображенні.
Третя пара ліній стосується легких випадків, коли коло достатньо близько до прямокутника (в будь-якому напрямку), що гарантовано перетин. Це відповідає помаранчевому та сірому ділянкам зображення. Зауважте, що цей крок повинен бути виконаний після кроку 2, щоб логіка мала сенс.
Решта рядків обчислюють складний випадок, коли коло може перетинати кут прямокутника. Щоб вирішити, обчисліть відстань від центру кола та кута, а потім перевірте, що відстань не більше радіуса кола. Цей обчислення повертає хибність для всіх кіл, центр яких знаходиться в межах затіненої червоною точкою, і повертає істинність для всіх кіл, центр яких знаходиться в зоні білого відтінку.
;)
circleDistance_x = abs(circle.x - (rect.x-rect.w/2)); circleDistance_y = abs(circle.y - (rect.y-rect.h/2));
Ось ще одне рішення, яке досить просто здійснити (і досить швидко теж). Він зачепить усі перехрестя, в тому числі, коли сфера повністю увійшла до прямокутника.
// clamp(value, min, max) - limits value to the range min..max
// Find the closest point to the circle within the rectangle
float closestX = clamp(circle.X, rectangle.Left, rectangle.Right);
float closestY = clamp(circle.Y, rectangle.Top, rectangle.Bottom);
// Calculate the distance between the circle's center and this closest point
float distanceX = circle.X - closestX;
float distanceY = circle.Y - closestY;
// If the distance is less than the circle's radius, an intersection occurs
float distanceSquared = (distanceX * distanceX) + (distanceY * distanceY);
return distanceSquared < (circle.Radius * circle.Radius);
З будь-якою гідною бібліотекою з математики її можна скоротити до 3 або 4 рядків.
ваша сфера та пряма перетинають IIF,
відстань між центром кола та однією вершиною прямої ваги менша за радіус вашої сфери
АБО
відстань між центром кола та одним краєм вашої прямої є меншою за радіус вашої сфери ( [ відстань точкової лінії ])
АБО
центр кола знаходиться всередині прямої
відстані точки:
P1 = [x1, y1] P2 = [x2, y2] Відстань = sqrt (abs (x1 - x2) + abs (y1-y2))
відстань від точки:
L1 = [x1, y1], L2 = [x2, y2] (дві точки вашої лінії, тобто точки вершин) P1 = [px, py] деяка точка Відстань d = abs ((x2-x1) (y1-py) - (x1-px) (y2-y1)) / Відстань (L1, L2)
центр кола всередині прямої:
візьміть відокремлювальну вісь наближення: якщо є проекція на лінію, яка відокремлює прямокутник від точки, вони не перетинаються
Ви проектуєте точку на лінії, паралельні сторонам прямої кишки, а потім можете легко визначити, чи перетинаються вони. якщо вони перетинаються не на всіх 4 проекціях, вони (точка і прямокутник) не можуть перетинатися.
вам просто потрібен внутрішній продукт (x = [x1, x2], y = [y1, y2], x * y = x1 * y1 + x2 * y2)
ваш тест виглядав би так:
// краї прямокутника: TL (верхній лівий), TR (верхній правий), BL (знизу лівий), BR (нижній правий) // Точка на тест: POI відокремлений = хибний для egde в {{TL, TR}, {BL, BR}, {TL, BL}, {TR-BR}}: // краї D = край [0] - край [1] innerProd = D * POI Interval_min = min (D * край [0], D * край [1]) Interval_max = max (D * край [0], D * край [1]) якщо ні (Interval_min ≤ innerProd ≤ Interval_max) відокремлений = правда перерва // кінець для циклу закінчується, якщо кінець для якщо (відокремлено істинно) повернути "немає перехрестя" ще повернути "перехрестя" закінчується, якщо
це не передбачає прямокутника, орієнтованого на вісь, і легко розширюється для тестування перетинів між опуклими множинами.
Це найшвидше рішення:
public static boolean intersect(Rectangle r, Circle c)
{
float cx = Math.abs(c.x - r.x - r.halfWidth);
float xDist = r.halfWidth + c.radius;
if (cx > xDist)
return false;
float cy = Math.abs(c.y - r.y - r.halfHeight);
float yDist = r.halfHeight + c.radius;
if (cy > yDist)
return false;
if (cx <= r.halfWidth || cy <= r.halfHeight)
return true;
float xCornerDist = cx - r.halfWidth;
float yCornerDist = cy - r.halfHeight;
float xCornerDistSq = xCornerDist * xCornerDist;
float yCornerDistSq = yCornerDist * yCornerDist;
float maxCornerDistSq = c.radius * c.radius;
return xCornerDistSq + yCornerDistSq <= maxCornerDistSq;
}
Зверніть увагу на порядок виконання, а половина ширини / висоти попередньо обчислюється. Також квадратування проводиться "вручну", щоб зберегти деякі цикли годин.
Найпростіше рішення, яке я придумав, досить просте.
Він працює, знаходячи точку в прямокутнику, найближчій до кола, а потім порівнюючи відстань.
Ви можете зробити все це за допомогою декількох операцій і навіть уникнути функції sqrt.
public boolean intersects(float cx, float cy, float radius, float left, float top, float right, float bottom)
{
float closestX = (cx < left ? left : (cx > right ? right : cx));
float closestY = (cy < top ? top : (cy > bottom ? bottom : cy));
float dx = closestX - cx;
float dy = closestY - cy;
return ( dx * dx + dy * dy ) <= radius * radius;
}
І це все! Вищевказане рішення передбачає початок у верхньому лівому куті світу, а вісь x спрямована вниз.
Якщо ви хочете вирішити проблеми зіткнення між рухомим колом і прямокутником, це набагато складніше і висвітлено іншу мою відповідь.
Власне, це набагато простіше. Вам потрібно лише дві речі.
Спочатку потрібно знайти чотири ортогональних відстані від центру кола до кожної лінії прямокутника. Тоді ваше коло не буде перетинати прямокутник, якщо будь-які три з них більші за радіус кола.
По-друге, вам потрібно знайти відстань між центром кола та центром прямокутника, тоді коло ви не будете всередині прямокутника, якщо відстань більша половини довжини діагоналі прямокутника.
Удачі!
Ось мій код C для вирішення зіткнення між сферою та полем, не вирівненим по осі. Він покладається на пару моїх власних процедур бібліотеки, але може виявитися корисним для деяких. Я використовую його в грі, і вона чудово працює.
float physicsProcessCollisionBetweenSelfAndActorRect(SPhysics *self, SPhysics *actor)
{
float diff = 99999;
SVector relative_position_of_circle = getDifference2DBetweenVectors(&self->worldPosition, &actor->worldPosition);
rotateVector2DBy(&relative_position_of_circle, -actor->axis.angleZ); // This aligns the coord system so the rect becomes an AABB
float x_clamped_within_rectangle = relative_position_of_circle.x;
float y_clamped_within_rectangle = relative_position_of_circle.y;
LIMIT(x_clamped_within_rectangle, actor->physicsRect.l, actor->physicsRect.r);
LIMIT(y_clamped_within_rectangle, actor->physicsRect.b, actor->physicsRect.t);
// Calculate the distance between the circle's center and this closest point
float distance_to_nearest_edge_x = relative_position_of_circle.x - x_clamped_within_rectangle;
float distance_to_nearest_edge_y = relative_position_of_circle.y - y_clamped_within_rectangle;
// If the distance is less than the circle's radius, an intersection occurs
float distance_sq_x = SQUARE(distance_to_nearest_edge_x);
float distance_sq_y = SQUARE(distance_to_nearest_edge_y);
float radius_sq = SQUARE(self->physicsRadius);
if(distance_sq_x + distance_sq_y < radius_sq)
{
float half_rect_w = (actor->physicsRect.r - actor->physicsRect.l) * 0.5f;
float half_rect_h = (actor->physicsRect.t - actor->physicsRect.b) * 0.5f;
CREATE_VECTOR(push_vector);
// If we're at one of the corners of this object, treat this as a circular/circular collision
if(fabs(relative_position_of_circle.x) > half_rect_w && fabs(relative_position_of_circle.y) > half_rect_h)
{
SVector edges;
if(relative_position_of_circle.x > 0) edges.x = half_rect_w; else edges.x = -half_rect_w;
if(relative_position_of_circle.y > 0) edges.y = half_rect_h; else edges.y = -half_rect_h;
push_vector = relative_position_of_circle;
moveVectorByInverseVector2D(&push_vector, &edges);
// We now have the vector from the corner of the rect to the point.
float delta_length = getVector2DMagnitude(&push_vector);
float diff = self->physicsRadius - delta_length; // Find out how far away we are from our ideal distance
// Normalise the vector
push_vector.x /= delta_length;
push_vector.y /= delta_length;
scaleVector2DBy(&push_vector, diff); // Now multiply it by the difference
push_vector.z = 0;
}
else // Nope - just bouncing against one of the edges
{
if(relative_position_of_circle.x > 0) // Ball is to the right
push_vector.x = (half_rect_w + self->physicsRadius) - relative_position_of_circle.x;
else
push_vector.x = -((half_rect_w + self->physicsRadius) + relative_position_of_circle.x);
if(relative_position_of_circle.y > 0) // Ball is above
push_vector.y = (half_rect_h + self->physicsRadius) - relative_position_of_circle.y;
else
push_vector.y = -((half_rect_h + self->physicsRadius) + relative_position_of_circle.y);
if(fabs(push_vector.x) < fabs(push_vector.y))
push_vector.y = 0;
else
push_vector.x = 0;
}
diff = 0; // Cheat, since we don't do anything with the value anyway
rotateVector2DBy(&push_vector, actor->axis.angleZ);
SVector *from = &self->worldPosition;
moveVectorBy2D(from, push_vector.x, push_vector.y);
}
return diff;
}
Для візуалізації візьміть цифрову клавіатуру. Якщо ключ "5" представляє ваш прямокутник, то всі клавіші 1-9 представляють 9 квадрантів простору, розділених на лінії, що складають ваш прямокутник (5 - це внутрішня частина).
1) Якщо центр кола знаходиться в квадранті 5 (тобто всередині прямокутника), то дві форми перетинаються.
Якщо це не виходить, є два можливі випадки: а) Коло перетинається з двома або більше сусідніми краями прямокутника. б) Коло перетинається з одним краєм прямокутника.
Перший випадок простий. Якщо коло перетинається з двома сусідніми краями прямокутника, він повинен містити кут, що з'єднує ці два краї. (Це або його центр лежить у квадранті 5, який ми вже висвітлювали. Також зауважимо, що випадок, коли коло перетинається лише з двома протилежними краями прямокутника, охоплюється також.)
2) Якщо будь-який з кутів A, B, C, D прямокутника лежить всередині кола, то дві форми перетинаються.
Другий випадок хитріший. Слід зазначити, що це може статися лише тоді, коли центр кола лежить в одному з квадрантів 2, 4, 6 або 8. (Насправді, якщо центр знаходиться на будь-якому з квадрантів 1, 3, 7, 8, відповідний кут буде найближчою точкою до нього.)
Тепер ми маємо випадок, що центр кола знаходиться в одному з «крайових» квадрантів, і він перетинається лише з відповідним ребром. Потім точка на ребрі, яка знаходиться найближче до центру кола, повинна лежати всередині кола.
3) Для кожного рядка AB, BC, CD, DA побудуйте перпендикулярні прямі p (AB, P), p (BC, P), p (CD, P), p (DA, P) через центр кола P. Для кожна перпендикулярна лінія, якщо перетин з початковим краєм лежить всередині кола, то дві форми перетинаються.
Існує ярлик для цього останнього кроку. Якщо центр кола знаходиться в квадранті 8, а край AB - верхній край, то точка перетину матиме y-координати A і B, а x-координату центру P.
Ви можете побудувати чотири перетину ліній і перевірити, чи лежать вони на відповідних краях, або дізнатись, у якому квадранті Р знаходиться, і перевірити відповідний перетин. Обидва повинні спрощуватись до одного булевого рівняння. Будьте обережні, що на етапі 2 вище не виключено, що P опинився в одному з «кутових» квадрантів; воно просто шукало перехрестя.
Редагувати: Як виявилося, я не помітив простого факту, що №2 є підпунктом №3 вище. Адже кути теж є точками по краях. Дивіться відповідь @ ShreevatsaR нижче для великого пояснення. А тим часом забудьте номер 2 вище, якщо ви не хочете швидкої, але зайвої перевірки.
Ця функція виявляє зіткнення (перетини) між Колом та Прямокутником. Він працює як метод e.James у своїй відповіді, але цей виявляє зіткнення для всіх кутів прямокутника (не лише правого кута).
ПРИМІТКА:
aRect.origin.x і aRect.origin.y - координати нижнього лівого кута прямокутника!
aCircle.x і aCircle.y - координати Центру кола!
static inline BOOL RectIntersectsCircle(CGRect aRect, Circle aCircle) {
float testX = aCircle.x;
float testY = aCircle.y;
if (testX < aRect.origin.x)
testX = aRect.origin.x;
if (testX > (aRect.origin.x + aRect.size.width))
testX = (aRect.origin.x + aRect.size.width);
if (testY < aRect.origin.y)
testY = aRect.origin.y;
if (testY > (aRect.origin.y + aRect.size.height))
testY = (aRect.origin.y + aRect.size.height);
return ((aCircle.x - testX) * (aCircle.x - testX) + (aCircle.y - testY) * (aCircle.y - testY)) < aCircle.radius * aCircle.radius;
}
У мене є метод, який дозволяє уникнути дорогих піфагор, якщо не потрібно - тобто. коли обмежувальні коробки прямокутника і кола не перетинаються.
І він буде працювати і для неевклідового:
class Circle {
// create the bounding box of the circle only once
BBox bbox;
public boolean intersect(BBox b) {
// test top intersect
if (lat > b.maxLat) {
if (lon < b.minLon)
return normDist(b.maxLat, b.minLon) <= normedDist;
if (lon > b.maxLon)
return normDist(b.maxLat, b.maxLon) <= normedDist;
return b.maxLat - bbox.minLat > 0;
}
// test bottom intersect
if (lat < b.minLat) {
if (lon < b.minLon)
return normDist(b.minLat, b.minLon) <= normedDist;
if (lon > b.maxLon)
return normDist(b.minLat, b.maxLon) <= normedDist;
return bbox.maxLat - b.minLat > 0;
}
// test middle intersect
if (lon < b.minLon)
return bbox.maxLon - b.minLon > 0;
if (lon > b.maxLon)
return b.maxLon - bbox.minLon > 0;
return true;
}
}
dLat=(lat-circleY); dLon=(lon-circleX); normed=dLat*dLat+dLon*dLon
. Звичайно, якщо ви використовуєте метод normDist, вам потрібно створити normedDist = dist*dist;
колоДивіться повний код BBox та Circle мого проекту GraphHopper .
Я створив клас для роботи з фігурами, сподіваюся, вам сподобається
public class Geomethry {
public static boolean intersectionCircleAndRectangle(int circleX, int circleY, int circleR, int rectangleX, int rectangleY, int rectangleWidth, int rectangleHeight){
boolean result = false;
float rectHalfWidth = rectangleWidth/2.0f;
float rectHalfHeight = rectangleHeight/2.0f;
float rectCenterX = rectangleX + rectHalfWidth;
float rectCenterY = rectangleY + rectHalfHeight;
float deltax = Math.abs(rectCenterX - circleX);
float deltay = Math.abs(rectCenterY - circleY);
float lengthHypotenuseSqure = deltax*deltax + deltay*deltay;
do{
// check that distance between the centerse is more than the distance between the circumcircle of rectangle and circle
if(lengthHypotenuseSqure > ((rectHalfWidth+circleR)*(rectHalfWidth+circleR) + (rectHalfHeight+circleR)*(rectHalfHeight+circleR))){
//System.out.println("distance between the centerse is more than the distance between the circumcircle of rectangle and circle");
break;
}
// check that distance between the centerse is less than the distance between the inscribed circle
float rectMinHalfSide = Math.min(rectHalfWidth, rectHalfHeight);
if(lengthHypotenuseSqure < ((rectMinHalfSide+circleR)*(rectMinHalfSide+circleR))){
//System.out.println("distance between the centerse is less than the distance between the inscribed circle");
result=true;
break;
}
// check that the squares relate to angles
if((deltax > (rectHalfWidth+circleR)*0.9) && (deltay > (rectHalfHeight+circleR)*0.9)){
//System.out.println("squares relate to angles");
result=true;
}
}while(false);
return result;
}
public static boolean intersectionRectangleAndRectangle(int rectangleX, int rectangleY, int rectangleWidth, int rectangleHeight, int rectangleX2, int rectangleY2, int rectangleWidth2, int rectangleHeight2){
boolean result = false;
float rectHalfWidth = rectangleWidth/2.0f;
float rectHalfHeight = rectangleHeight/2.0f;
float rectHalfWidth2 = rectangleWidth2/2.0f;
float rectHalfHeight2 = rectangleHeight2/2.0f;
float deltax = Math.abs((rectangleX + rectHalfWidth) - (rectangleX2 + rectHalfWidth2));
float deltay = Math.abs((rectangleY + rectHalfHeight) - (rectangleY2 + rectHalfHeight2));
float lengthHypotenuseSqure = deltax*deltax + deltay*deltay;
do{
// check that distance between the centerse is more than the distance between the circumcircle
if(lengthHypotenuseSqure > ((rectHalfWidth+rectHalfWidth2)*(rectHalfWidth+rectHalfWidth2) + (rectHalfHeight+rectHalfHeight2)*(rectHalfHeight+rectHalfHeight2))){
//System.out.println("distance between the centerse is more than the distance between the circumcircle");
break;
}
// check that distance between the centerse is less than the distance between the inscribed circle
float rectMinHalfSide = Math.min(rectHalfWidth, rectHalfHeight);
float rectMinHalfSide2 = Math.min(rectHalfWidth2, rectHalfHeight2);
if(lengthHypotenuseSqure < ((rectMinHalfSide+rectMinHalfSide2)*(rectMinHalfSide+rectMinHalfSide2))){
//System.out.println("distance between the centerse is less than the distance between the inscribed circle");
result=true;
break;
}
// check that the squares relate to angles
if((deltax > (rectHalfWidth+rectHalfWidth2)*0.9) && (deltay > (rectHalfHeight+rectHalfHeight2)*0.9)){
//System.out.println("squares relate to angles");
result=true;
}
}while(false);
return result;
}
}
Ось модифікований код на 100% працює:
public static bool IsIntersected(PointF circle, float radius, RectangleF rectangle)
{
var rectangleCenter = new PointF((rectangle.X + rectangle.Width / 2),
(rectangle.Y + rectangle.Height / 2));
var w = rectangle.Width / 2;
var h = rectangle.Height / 2;
var dx = Math.Abs(circle.X - rectangleCenter.X);
var dy = Math.Abs(circle.Y - rectangleCenter.Y);
if (dx > (radius + w) || dy > (radius + h)) return false;
var circleDistance = new PointF
{
X = Math.Abs(circle.X - rectangle.X - w),
Y = Math.Abs(circle.Y - rectangle.Y - h)
};
if (circleDistance.X <= (w))
{
return true;
}
if (circleDistance.Y <= (h))
{
return true;
}
var cornerDistanceSq = Math.Pow(circleDistance.X - w, 2) +
Math.Pow(circleDistance.Y - h, 2);
return (cornerDistanceSq <= (Math.Pow(radius, 2)));
}
Bassam Alugili
Ось швидкий однолінійний тест для цього:
if (length(max(abs(center - rect_mid) - rect_halves, 0)) <= radius ) {
// They intersect.
}
Це вирівняний по осі випадок, коли rect_halves
позитивний вектор вказує від середини прямокутника до кута. Вираз всередині length()
є дельта-вектором від center
найближчої точки прямокутника. Це працює в будь-якому вимірі.
Це ефективно, тому що:
працював для мене (робота лише тоді, коли кут прямокутника дорівнює 180)
function intersects(circle, rect) {
let left = rect.x + rect.width > circle.x - circle.radius;
let right = rect.x < circle.x + circle.radius;
let top = rect.y < circle.y + circle.radius;
let bottom = rect.y + rect.height > circle.y - circle.radius;
return left && right && bottom && top;
}
Трохи вдосконалюючи відповідь e.James :
double dx = abs(circle.x - rect.x) - rect.w / 2,
dy = abs(circle.y - rect.y) - rect.h / 2;
if (dx > circle.r || dy > circle.r) { return false; }
if (dx <= 0 || dy <= 0) { return true; }
return (dx * dx + dy * dy <= circle.r * circle.r);
Це віднімається rect.w / 2
і rect.h / 2
один раз замість до трьох разів.
Для тих, хто повинен обчислити зіткнення кола / прямокутника в географічних координатах з SQL,
це моя реалізація в Oracle 11 алгоритму, запропонованого e.James .
Для введення потрібні координати кола, радіус кола в км і дві координати вершини прямокутника:
CREATE OR REPLACE FUNCTION "DETECT_CIRC_RECT_COLLISION"
(
circleCenterLat IN NUMBER, -- circle Center Latitude
circleCenterLon IN NUMBER, -- circle Center Longitude
circleRadius IN NUMBER, -- circle Radius in KM
rectSWLat IN NUMBER, -- rectangle South West Latitude
rectSWLon IN NUMBER, -- rectangle South West Longitude
rectNELat IN NUMBER, -- rectangle North Est Latitude
rectNELon IN NUMBER -- rectangle North Est Longitude
)
RETURN NUMBER
AS
-- converts km to degrees (use 69 if miles)
kmToDegreeConst NUMBER := 111.045;
-- Remaining rectangle vertices
rectNWLat NUMBER;
rectNWLon NUMBER;
rectSELat NUMBER;
rectSELon NUMBER;
rectHeight NUMBER;
rectWIdth NUMBER;
circleDistanceLat NUMBER;
circleDistanceLon NUMBER;
cornerDistanceSQ NUMBER;
BEGIN
-- Initialization of remaining rectangle vertices
rectNWLat := rectNELat;
rectNWLon := rectSWLon;
rectSELat := rectSWLat;
rectSELon := rectNELon;
-- Rectangle sides length calculation
rectHeight := calc_distance(rectSWLat, rectSWLon, rectNWLat, rectNWLon);
rectWidth := calc_distance(rectSWLat, rectSWLon, rectSELat, rectSELon);
circleDistanceLat := abs( (circleCenterLat * kmToDegreeConst) - ((rectSWLat * kmToDegreeConst) + (rectHeight/2)) );
circleDistanceLon := abs( (circleCenterLon * kmToDegreeConst) - ((rectSWLon * kmToDegreeConst) + (rectWidth/2)) );
IF circleDistanceLon > ((rectWidth/2) + circleRadius) THEN
RETURN -1; -- -1 => NO Collision ; 0 => Collision Detected
END IF;
IF circleDistanceLat > ((rectHeight/2) + circleRadius) THEN
RETURN -1; -- -1 => NO Collision ; 0 => Collision Detected
END IF;
IF circleDistanceLon <= (rectWidth/2) THEN
RETURN 0; -- -1 => NO Collision ; 0 => Collision Detected
END IF;
IF circleDistanceLat <= (rectHeight/2) THEN
RETURN 0; -- -1 => NO Collision ; 0 => Collision Detected
END IF;
cornerDistanceSQ := POWER(circleDistanceLon - (rectWidth/2), 2) + POWER(circleDistanceLat - (rectHeight/2), 2);
IF cornerDistanceSQ <= POWER(circleRadius, 2) THEN
RETURN 0; -- -1 => NO Collision ; 0 => Collision Detected
ELSE
RETURN -1; -- -1 => NO Collision ; 0 => Collision Detected
END IF;
RETURN -1; -- -1 => NO Collision ; 0 => Collision Detected
END;
Працює, щойно я вияснив це тиждень тому, і тільки зараз перейшов до тестування.
double theta = Math.atan2(cir.getX()-sqr.getX()*1.0,
cir.getY()-sqr.getY()*1.0); //radians of the angle
double dBox; //distance from box to edge of box in direction of the circle
if((theta > Math.PI/4 && theta < 3*Math.PI / 4) ||
(theta < -Math.PI/4 && theta > -3*Math.PI / 4)) {
dBox = sqr.getS() / (2*Math.sin(theta));
} else {
dBox = sqr.getS() / (2*Math.cos(theta));
}
boolean touching = (Math.abs(dBox) >=
Math.sqrt(Math.pow(sqr.getX()-cir.getX(), 2) +
Math.pow(sqr.getY()-cir.getY(), 2)));
def colision(rect, circle):
dx = rect.x - circle.x
dy = rect.y - circle.y
distance = (dy**2 + dx**2)**0.5
angle_to = (rect.angle + math.atan2(dx, dy)/3.1415*180.0) % 360
if((angle_to>135 and angle_to<225) or (angle_to>0 and angle_to<45) or (angle_to>315 and angle_to<360)):
if distance <= circle.rad/2.+((rect.height/2.0)*(1.+0.5*abs(math.sin(angle_to*math.pi/180.)))):
return True
else:
if distance <= circle.rad/2.+((rect.width/2.0)*(1.+0.5*abs(math.cos(angle_to*math.pi/180.)))):
return True
return False
Якщо припустити, що у вас є чотири ребра прямокутника, перевірте відстань від країв до центру кола, якщо його менше радіуса, то форми перетинаються.
if sqrt((rectangleRight.x - circleCenter.x)^2 +
(rectangleBottom.y - circleCenter.y)^2) < radius
// then they intersect
if sqrt((rectangleRight.x - circleCenter.x)^2 +
(rectangleTop.y - circleCenter.y)^2) < radius
// then they intersect
if sqrt((rectangleLeft.x - circleCenter.x)^2 +
(rectangleTop.y - circleCenter.y)^2) < radius
// then they intersect
if sqrt((rectangleLeft.x - circleCenter.x)^2 +
(rectangleBottom.y - circleCenter.y)^2) < radius
// then they intersect
Якщо прямокутник перетинається з колом, одна чи кілька кутових точок прямокутника повинні бути всередині кола. Припустимо, чотири точки прямокутника - A, B, C, D. принаймні одна з них повинна перетинати коло. тому якщо відстань від однієї точки до центру кола менша за радіус кола, вона повинна перетинати коло. Щоб отримати відстань, ви можете використовувати теорему Піфагора,
H^2 = A^2 + B^2
Ця методика має деякі межі. Але це буде краще працювати для розробників ігор. особливо виявлення зіткнень
Це гарне оновлення до Алгоритму Арво