Рассчитайте х / у точку, что 2 движущихся шара столкнутся - PullRequest
9 голосов
/ 12 сентября 2010

Я пытаюсь сделать то, что (по сути) простой игрой в бильярд, и хотел бы иметь возможность предсказать, куда попадет удар, когда он попадет в другой шар.

Первая часть, я полагаю,чтобы вычислить, ударит ли битник что-либо, и если это произойдет, где он сталкивается.Я могу определить точки столкновения для линии и шара, но не для 2 шаров.

Итак, учитывая x / y позиции и скорости 2 шаров, как рассчитать точку, в которой они сталкиваются?

(PS: я знаю, что я могу сделать это, вычисляя расстояние между двумя шарами на каждом этапе пути, но я надеялся на что-то более элегантное и оптимальное.)

Пример настройки: пытается вычислить красную точку

http://dl.dropbox.com/u/6202117/circle.PNG

Ответы [ 3 ]

14 голосов
/ 12 сентября 2010

Некоторые вещи, на которые стоит обратить внимание:

  • Когда два шара каждого радиуса r сталкиваются, их центры находятся на расстоянии 2r.
  • Можно предположить, что ваш первый шар движется по прямой линии (ну, первое приближение, но начните с этого), и вы можете найти угол alpha между этим путем и направлением от первого шара ко второму. .
  • Вы знаете центр неподвижного шара, нет?

Теперь у вас есть геометрия.

Сделать эту конструкцию:

  1. Пометить текущий центр первого (движущегося) шара как точку A.
  2. Отметить центр неподвижного шара как точку B.
  3. Построить отрезок AB.
  4. Построить луч, R, от A в направлении движения.
  5. Построить круг радиуса 2r вокруг B.
  6. Отбросить отрезок от B перпендикулярно до R, назвать точку пересечения C.
  7. Вы знаете расстояние AB, и вы можете найти угол alpha между AB и R, с помощью закона синусов найдите длину BC.
  8. По этой длине определите, есть ли 0, 1 или 2 решения. Если есть 0 или 1, вы сделали.
  9. Постройте точку D, где круг встречается R ближе к A, и снова используйте закон синусов, чтобы найти расстояние AD.
  10. Точка столкновения является средней точкой BD

и теперь ты все знаешь.

Создание эффективного кода из этого оставлено в качестве упражнения.


Кстати: эта конструкция не будет работать, если оба шара движутся, но вы можете превратиться в кадр, где один неподвижен, решить его таким образом, а затем преобразовать обратно. Просто убедитесь, что решение находится в разрешенной области после обратного преобразования ...

/ Физики не могут не комментировать подобные. Я пытался сопротивляться. Я действительно сделал.

12 голосов
/ 12 сентября 2010

Рисунок ответа @ dmckee

alt text

Редактировать

Только вВ ответ на ответ некроманта @ArtB решения для точки D на приведенном выше графике можно записать так:

1/2 {(Ax+Bx+2 d Dx Cos[alpha]- Dx Cos[2 alpha]+ 2 Dy (Cos[alpha]-d) Sin[alpha]), 
     (Ay+By+2 d Dy Cos[alpha]- Dy Cos[2 alpha]- 2 Dx (Cos[alpha]-d) Sin[alpha])
     }  

Где

Dx = Ax - Bx 
Dy = Ay - By   

И

d = Sqrt[4 r^2 - (Dx^2 + Dy^2) Sin[alpha]^2]/Sqrt[Dx^2 + Dy^2]  

HTH!

1 голос
/ 13 января 2011

Я смотрел на решение @ dmckee, и мне потребовалось немало работы, чтобы проработать его. Ниже приведены мои заметки для тех, кто, возможно, ищет более практичный ответ, он взят непосредственно с его поста, так что заслуга ему / ей, но любые ошибки мои. Я обычно использую Pascal-подобный оператор присваивания (то есть :=), чтобы различать показ моей работы и фактический необходимый код. Я использую стандартный формат Y = mX +b и квазипопадную запись. Я использую BC как для сегмента, так и для полученной линии. Тем не менее, это должен быть «почти» копируемый и вставляемый код Python (удалите «;», замените «sqrt» и «sqr» соответствующими версиями и т. д.).

  1. A.x и A.y - позиции x & y, A.r - радиус A, а A.v - скорость, где A.v.x - его компонента x, а A.v.y - его компонента y.
  2. B - то же самое, но без скорости (или, точнее, вычтите скорости B из A, так что B относительно условно стационарен).
  3. AB.m := (b.y - a.y) / (b.x - a.x); AB.b := A.y - AB.m * A.x;
  4. R.m := A.v.y / A.v.x; R.b := A.y - R.m * A.x;
  5. необязательно
  6. BC.m := -A.v.x/A.v.y;, которое является стандартным уравнением для перпендикулярного наклона, BC.b := B.y - BC.m * B.x; Теперь C - это то, где AB встречается BC, поэтому мы знаем, что они равны, поэтому давайте приравняем C.y, так C.y == AB.m * C.x + AB.b == BC.m * C.x + BC.b;, так C.x := (AB.m - BC.M) / (BC.b - AB.b);, затем просто подключите C.x, чтобы получить C.y := AB.m * C.x + AB.b;
  7. Вы можете игнорировать закон синуса, так как у нас есть AB и BC, поэтому мы можем просто использовать теорему Пифагора, чтобы получить длину BC, BC.l := sqrt( sqr(B.x-C.x) + sqr(B.y-C.y) );
  8. Если BC.l > A.r + B.r, есть ноль решений, и эти круги не соприкасаются, поскольку C - это путь A s perigee with respect to B . If BC.l == Ar + Br , there is only one solution, and C = = D . Otherwise, if BC.l then there are two solutions. You can think of this as such, if there are zero solutions the bullet missed, if there is one the bullet grazed, and if there are two then there is both an entry and exit wound. The one closer to A` - это то, что нам нужно.
    1. Теперь математика становится ужасной, поэтому я покажу свою работу на тот случай, если я сделаю что-то не так.
    2. D - это точка на AC, которая A.r + B.r (он же 2r) от B, поэтому: sqrt( sqr(D.x - B.x) + sqr(D.y - B.y) ) == 2r
    3. Следовательно sqr(D.x - B.x) + sqr(D.y - B.y) == 4*r*r. Теперь 2 переменные (т.е. D.x и D.y) с одним уравнением - это проблема, но мы также знаем, что D на линии AC так D.y == AC.m*D.x + AC.b.
    4. Мы можем заменить D.y, давая sqr(D.x - B.x) + sqr(AC.m*C.x + AC.b - B.y) == 4*r*r.
    5. Это расширяется до прекрасного: sqr(D.x) + 2*D.x - sqr(B.x) + sqr(AC.m*D.x) + 2*AC.b*D.x - 2*AC.m*D.x*B.y + sqr(AC.b) - 2*AC.b*B.y + sqr(B.y) == 4*r*r ( это - это та часть, где я, скорее всего, допустил ошибку , если я вообще сделал).
    6. Эти термины, которые мы можем собрать (помните, на данный момент только D.x неизвестно; остальное мы можем рассматривать как постоянные), чтобы получить sqr(D.x) + 2*D.x - sqr(B.x) + sqr(AC.m*D.x) + 2*AC.b*D.x - 2*AC.m*D.x*B.y + sqr(AC.b) - 2*AC.b*B.y + sqr(B.y) == 4*r*r
    7. переписано в аккуратнее (sqr(D.x) + sqr(AC.m*D.x)) + ( 2*D.x + 2*AC.b*D.x - 2*AC.m*B.y*D.x ) + ( - sqr(B.x) + sqr(AC.b) - 2*AC.b*B.y + sqr(B.y) ) == 4*r*r, который может быть изменен на (1 + sqr(AC.m)) * sqr(D.x) + 2*( 1 + AC.b - AC.m*B.y ) * D.x + ( sqr(B.y) - sqr(B.x) + sqr(AC.b) - 2*AC.b*B.y - 4*r*r ) == 0
    8. Теперь это хорошо вписывается в квадратную формулу (т.е. x == (-bb +- sqrt( sqr(bb) - 4*aa*cc ) / 2*aa) (используя aa, чтобы избежать путаницы с более ранними переменными), с aa := 1 + sqr(AC.m);, bb := 2*( 1 + AC.b - AC.m*B.y ); и cc := sqr(B.y) - sqr(B.x) + sqr(AC.b) - 2*AC.b*B.y - sqr(A.r+B.r);.
    9. Теперь мы можем получить два решения, поэтому давайте сохраним детали, используя -bb/2aa +- sqrt(sqr(bb)-4*aa*cc)/2*aa: first_term := -bb/(2*a); и second_term := sqrt(sqr(bb)-4*aa*cc)/2*aa;.
    10. Первое решение D1.x = first_term + second_term; с D1.y = AC.m * D1.x + AC.b, а второе решение D2.x = first_term + second_term; с D2.y = AC.m * D2.x + AC.b.
    11. Найдите расстояния до A: D1.l := sqrt( sqr(D1.x-A.x) + sqr(D1.y-A.y) ); и D2.l = sqrt( sqr(D2.x-A.x) + sqr(D2.y-A.y) ); (на самом деле эффективнее пропустить оба квадратных корня, но это не имеет значения).
    12. Чем ближе тот, который вам нужен D := D1 if D1.l < D2. l else D2;.
    1. Середина DB, назовем это E, является столкновением (я не знаю, как это обобщается, если радиусы не равны).
    2. Итак, постройте линию DB.m := (B.y-D.y)/(B.x-D.x); и DB.b = B.y - DB.m*B.x;.
    3. Нам не нужна длина, чтобы определить длину, поскольку она должна быть BD.l == A.r + B.r, поэтому sqrt( sqr(E.x-B.x) + sqr(E.y-B.y) ) == B.r.
    4. Опять же, мы можем заменить E, потому что мы знаем, что это на BD, поэтому E.y == BD.m * E.x + BD.b, получая sqrt( sqr(E.x-B.x) + sqr(BD.m * E.x + BD.b-B.y) ) == B.r.
    5. Расширение до sqr(E.x) - 2*E.x*B.x + sqr(B.x) + sqr(BD.m)*sqr(E.x) + 2*BD.m*E.x*BD.b - 2*BD.m*B.y + sqr(B.y) - 2*BD.b*B.y + sqr(B.y) == sqr(B.r)
    6. Который накапливается в

    sqr(E.x) + sqr(BD.m)*sqr(E.x) + 2*BD.m*E.x*BD.b - 2*E.x*B.x + sqr(B.x) - 2*BD.m*B.y + sqr(B.y) - 2*BD.b*B.y + sqr(B.y) == sqr(B.r) (1 + sqr(BD.m)) * sqr(E.x) + 2*(BD.m*BD.b - B.x) * E.x + sqr(B.x) + sqr(B.y) - 2*BD.m*B.y + sqr(B.y) - 2*BD.b*B.y + sqr(B.y) - sqr(B.r) == 0 aa := (1 + sqr(BD.m)); bb := 2*(BD.m*BD.b - B.x); cc := sqr(B.y) - 2*BD.m*B.y + sqr(B.y) - 2*BD.b*B.y + sqr(B.y) - sqr(B.r);, квадратная формула, а затем получите две точки и выберите ту, которая ближе к B.

Хотелось бы, чтобы я просто мечтал о карме или значке некроманта, но мне действительно нужно было разобраться с этим и подумать, что я поделюсь. Тьфу, думаю, мне нужно сейчас лечь.

...