Прежде всего, в случае выровненных по оси прямоугольников ответ Кевина Рейда является лучшим, а алгоритм - самым быстрым.
Во-вторых, для простых форм используйте относительные скорости (как показано ниже) и теорему о разделительной оси для обнаружения столкновений. Это будет сказать, происходит ли столкновение в случае линейного движения (без вращения). И если есть вращение, вам нужен маленький временной шаг, чтобы быть точным. Теперь, чтобы ответить на вопрос:
Как определить в общем случае, пересекаются ли две выпуклые фигуры?
Я дам вам алгоритм, который работает для всех выпуклых фигур, а не только для шестиугольников.
Предположим, X и Y две выпуклые формы. Они пересекаются тогда и только тогда, когда у них есть общая точка, т. Е. Существует точка x ∈ X и точка y ∈ Y, такая что x = y . Если вы рассматриваете пространство как векторное пространство, то это означает, что x - y = 0 . И теперь мы добрались до этого дела Минковского:
Сумма Минковского из X и Y представляет собой множество всех х + у для х ∈ X и Y ∈ Y .
Пример для X и Y
X, Y и их сумма Минковского, X + Y
Предположим, что (-Y) является множеством всех -y для y ∈ Y , тогда, учитывая предыдущий абзац, X и Y пересекаются тогда и только тогда, когда X + (-Y) содержит 0 , то есть начало координат .
Дополнительное замечание: почему я пишу X + (-Y) вместо X - Y ? Ну, потому что в математике есть операция, называемая разностью Минковского для A и B, которая иногда пишется X - Y, но не имеет ничего общего с множеством всех x - y для x ∈ X и y ∈ Y (действительное значение Минковского). разница немного сложнее).
Итак, мы хотели бы вычислить сумму Минковского X и -Y и выяснить, содержит ли она источник. Начало координат не является особенным по сравнению с любой другой точкой, поэтому, чтобы определить, находится ли начало в определенной области, мы используем алгоритм, который может сказать нам, принадлежит ли какая-либо данная точка этой области.
Сумма Минковского X и Y обладает классным свойством: если X и Y выпуклые, то X + Y тоже. И найти, принадлежит ли точка выпуклому множеству, намного легче, чем если бы это множество не было (как известно, выпуклым).
Мы не можем вычислить все x - y для x ∈ X и y ∈ Y, потому что существует бесконечность таких точек x и y , так что, надеюсь, поскольку X , Y и X + Y выпуклые, мы можем просто использовать «крайние» точки, определяющие формы X и Y , которые являются их вершинами, и мы получим крайние точки X + Y , а также некоторые другие.
Эти дополнительные точки «окружены» внешними точками X + Y, так что они не способствуют определению вновь полученной выпуклой формы. Мы говорим, что они не определяют « выпуклую оболочку » множества точек. Так что мы делаем то, что мы избавляемся от них при подготовке к окончательному алгоритму, который сообщает нам, находится ли источник в выпуклой оболочке.
Выпуклая оболочка X + Y. Мы удалили "внутренние" вершины.
Поэтому мы получаем
Первый, наивный алгоритм
boolean intersect(Shape X, Shape Y) {
SetOfVertices minkowski = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
minkowski.addVertice(x-y);
}
}
return contains(convexHull(minkowski), Vector2D(0,0));
}
Циклы, очевидно, имеют сложность O (mn), где m и n - количество вершин каждой фигуры. minkoswki
Набор млн на большинство элементов. convexHull
Алгоритм имеет сложность , которая зависит от алгоритма, используемым , и вы можете стремиться к O (к логу (к)) , где к является размером множества точек, так что в нашем случае мы получаем O (млн журнала (млн) ) . contains
Алгоритм имеет сложность, линейно с числом ребер (в 2D) или лицо (в 3D) выпуклой оболочки, так что это действительно зависит от ваших исходных форм, но это будет не больше , чем O (млн) .
Я позволю вам погуглить contains
алгоритм выпуклых фигур, он довольно распространенный. Я могу поставить это здесь, если у меня будет время.
Но мы делаем обнаружение столкновений, поэтому мы можем оптимизировать это
Первоначально у нас было два тела A и B, которые двигались без вращения в течение временного шага dt ( насколько я могу судить по вашим фотографиям). Давайте назовем v A и v B соответствующими скоростями A и B , которые являются постоянными в течение нашего временного шага длительности dt . Мы получаем следующее:
и, как вы указали на своих снимках, эти тела по мере движения проникают через области (или объемы):
и они заканчиваются как A ' и B' после временного шага.
Чтобы применить наш наивный алгоритм здесь, нам нужно только вычислить развернутые объемы. Но мы этого не делаем.
В системе отсчета B , B не двигается (Дух!). И A имеет определенную скорость относительно B, которую вы получаете, вычисляя v A - v B (вы можете сделать обратное, вычислить относительную скорость B в системе отсчета A ).
Слева направо: скорости в базовой системе отсчета; относительные скорости; вычисление относительных скоростей.
Рассматривая B как неподвижен в своей собственной системе отсчета, вы только вычислить объем, А метет через , как она движется во время ДТ с его относительной скорости об - об B .
Это уменьшает количество вершин, которые будут использоваться при вычислении суммы Минковского (иногда сильно).
Другая возможная оптимизация - это то, где вы вычисляете объем, охватываемый одним из тел, скажем, A. Вам не нужно переводить все вершины, составляющие A. Только те, которые принадлежат ребрам (граням в 3D), чьи внешнее нормальное «лицо» в направлении подметания. Конечно, вы уже заметили это, когда вычислили свои площади для площадей. Вы можете определить, является ли нормаль в направлении развертки, используя ее точечное произведение с направлением развертки, которое должно быть положительным.
Последняя оптимизация, которая не имеет никакого отношения к вашему вопросу о пересечениях, действительно полезна в нашем случае. Он использует те относительные скорости, которые мы упомянули, и так называемый метод разделительной оси. Конечно, вы уже знаете об этом.
Предположим , вы знаете радиусы в A и B относительно их центров масс (то есть, расстояние между центром масс и вершиной дальнему от него), как это:
Столкновение может произойти только в том случае, возможно , что ограничивающая круг А встречаются у B . Здесь мы видим , что он не будет, и так сказать , компьютер , который должен вычислить расстояние от C B до I , как на рисунке ниже и убедитесь , что это больше , чем сумма радиусов A и B . Если оно больше, столкновения нет. Если он меньше, то столкновение.
Это не очень хорошо работает с фигурами, которые довольно длинные, но в случае квадратов или других подобных фигур, это очень хорошая эвристика, чтобы исключить столкновение .
Однако теорема о разделительной оси, примененная к B и объему, охватываемому A , говорит о том, произошло ли столкновение. Сложность связанного алгоритма линейна с суммой чисел вершин каждой выпуклой формы, но она менее волшебна, когда наступает время, чтобы действительно обработать столкновение.
Наш новый, лучший алгоритм, который использует пересечения, чтобы помочь обнаружить столкновения, но все же не так хорош, как теорема о разделительной оси, для фактического определения того, произошло ли столкновение
boolean mayCollide(Body A, Body B) {
Vector2D relativeVelocity = A.velocity - B.velocity;
if (radiiHeuristic(A, B, relativeVelocity)) {
return false; // there is a separating axis between them
}
Volume sweptA = sweptVolume(A, relativeVelocity);
return contains(convexHull(minkowskiMinus(sweptA, B)), Vector2D(0,0));
}
boolean radiiHeuristic(A, B, relativeVelocity)) {
// the code here
}
Volume convexHull(SetOfVertices s) {
// the code here
}
boolean contains(Volume v, Vector2D p) {
// the code here
}
SetOfVertices minkowskiMinus(Body X, Body Y) {
SetOfVertices result = new SetOfVertices();
for (Vertice x in X) {
for (Vertice y in Y) {
result.addVertice(x-y);
}
}
return result;
}