Моделирование одновременного вращения объекта вокруг фиксированной точки при ограниченных ресурсах.

Извините, если название немного загадочно. Это лучшее, что я мог придумать.

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

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

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

Общие правила вращения заключаются в том, что если ящик движется только вправо, то он должен вращаться вокруг оси Y так, чтобы он вращался вправо, а если ящик движется только вверх, он должен вращаться вокруг оси Y. Икс -ось так, чтобы казалось, что она катится прямо вверх.

Другими словами, если коробка выглядит так:

введите описание изображения здесь

Тогда, если я скажу: rotate2D(45,0)я должен получить:

введите описание изображения здесь

и если я скажу, rotate2D(0,45)что должен получить:

введите описание изображения здесь

(для наглядности объявление функции для rotate2D выглядит так rotate2D(horizontal, vertical))

Обработка упрощает это с помощью функций вращения, которые она предоставляет, а именно:

rotateX();
rotateY();
rotatez();

Проблема возникает, когда вы хотите вращаться в двух измерениях одновременно. Видите ли, в Обработке, если я позвоню rotateX(), чтобы повернуть объект вокруг Икс -ось, на самом деле смещается сама система координат, и все остальные оси идут вместе с ней, поэтому, когда я иду делать rotateY()после выполнения, rotateX()я не получу результат, который я хочу, потому что Д -ось, вокруг которой я вращаюсь, теперь перекошена. Вы можете увидеть это на картинках выше.

Итак, если я сделаю:

rotateX(radians(45));
rotateY(radians(45));

Я получил:

введите описание изображения здесь

и если я сделаю:

rotateY(radians(45));
rotateX(radians(45));

Я получил:

введите описание изображения здесь

Но я хочу что-то вроде:

введите описание изображения здесь

который я нарисовал, вызвав свою функциюrotate2D()

Проблема в том, что результаты, которые дает моя функция, немного нелогичны. В приведенном выше примере это не очевидно, но, скажем, я хочу сделать: rotate2D(90,90);. Я думаю, что это создаст коробку, которая выглядит так, как будто она вообще не была повернута, верно? Однако на самом деле он производит:

введите описание изображения здесь

Это моя текущая реализацияrotate2D()


Функция:

void rotate2D(float horizontal, float vertical)
{  
  float[] rotations ={horizontal, vertical};
  if(rotations[0]>rotations[1])
  {
    float tmp = rotations[1];
    rotations[1]=rotations[0];
    rotations[0]=tmp;
  } 

  if(rotations[1]==vertical) rotateX(radians(rotations[1]-rotations[0]));
  else rotateY(radians(rotations[1]-rotations[0]));

  float hIncr= (horizontal<0) ? -0.1 : 0.1;
  float vIncr= (vertical<0) ? -0.1 : 0.1;
  for(float i=0; i<=abs(rotations[0]);i+=0.1)
  {    
    rotateY(radians(hIncr));
    rotateX(radians(vIncr));
  }   

}

Объяснение функции: функция делает следующее (и это все, что вам действительно нужно для решения вопроса с математической точки зрения).

Функция медленно выполняет кучу Икс и Д оси вращаются сразу друг за другом на небольшую величину каждый раз (0,1 градуса), пока не достигнет верхней границы. Эта верхняя граница является меньшим из значений, которые были переданы функции. Коробка уже будет повернута вокруг правильной оси на разницу между большим и меньшим значением, прежде чем эта последовательность будет иметь место.

Чтобы еще больше уточнить, я ищу математическое объяснение того, почему этот процесс дает противоречивые результаты, которые, надеюсь, прольют свет на решение.

Идеальная конечная цель здесь — найти формулу, которую я могу использовать для имитации вращения вокруг фиксированной оси в нескольких измерениях, когда все, что я могу сделать, — это вращать «мир» по одной оси за раз, как описано выше (что, по сути, эквивалентно вращению относительные оси объектов)

пожалуйста, не стесняйтесь просить меня уточнить дальше. Заранее спасибо.

РЕДАКТИРОВАТЬ

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

То, что я вычисляю и передаю в свою функцию, - это чистое вращение в каждом направлении.

Итак, можно ли отредактировать мою формулу, чтобы она производила это чистое вращение? Например, в примере 90/90, если я продолжу последовательность примерно на 37 дополнительных шагов (всего 127), я получу результат, который интуитивно хочу получить) Однако это должно работать для всех комбинаций входных данных.

Я не понимаю, почему rotate(90,90) должен давать результат, который не выглядит повернутым. Он будет чередовать небольшие повороты по x и y, так что каждый из них будет равен 90. Результат должен быть эквивалентен повороту вокруг оси на полпути между поворотами X и Y, но не на 180 градусов. Чистый поворот составляет где-то между 90 градусов. Рассмотрим путешествие от (0,0) на плоскости до (90,90) - даже если вы сделаете это с небольшими шагами по x и y, вы все равно пройдете около 127 по диагонали, когда закончите. Чистый угол здесь может быть не равен 127, так как повороты не складываются таким же образом.
Я не проверял тщательно, чтобы увидеть, полностью ли это объясняет несоответствие, которое вы видите с rotate2D(90,90), но повороты вокруг разных (даже перпендикулярных) осей не коммутируют. Расставание 90 градусов на маленькие шажки и вращаясь попеременно вокруг Икс - и у -оси для "общего" количества 90 градусов вокруг каждой оси не то же самое , что вращение 90 градусов относительно каждой оси отдельно. (Ваша интуиция может быть обманута географическими координатами?) Даже 90 градусов вращения вокруг Икс - и у -оси не коммутируют.
Итак, не уверен, что вы спрашиваете. Общее вращение можно выразить как «повернуть на угол A вокруг оси v», где v — единичный вектор. Непросто превратить это во вращения x/y/z, но это можно сделать... это то, что вы ищете?
Хорошо, тогда я попытаюсь переформулировать свой вопрос, потому что в конечном итоге я ищу способ смоделировать вращение вокруг фиксированной системы координат, но я ищу формулу, которую я могу использовать вместо этого решения, потому что это неэффективно, а также глючит.
Может быть полезно? engr.uvic.ca/~mech410/lectures/4_2_RotateArbi.pdf
@greggo, может быть, я не уверен, каким будет единичный вектор
Единичный вектор определяет ось вращения. поэтому (1,0,0) для X, или (0,1,0) для Y, или (0,7071,0,7071,0) для середины между ними.
Я отредактировал свой вопрос, проверьте внизу
@LukeP: Что касается ваших правок, то, чем глубже, «чистого вращения вокруг каждой оси» недостаточно для указания пространственного вращения . То есть вы не передаете достаточно данных (математически говоря), чтобы указать уникальное вращение. К сожалению, это факт из жизни о пространственном вращении, а не исправляемая ошибка в вашем коде. Однако, если вы укажете направление в плоскости и числовой угол поворота (пропорциональный расстоянию, на которое катился куб), можно указать уникальный поворот; это то, что вы после?
@user86418 user86418 Нельзя ли это решить с помощью теоремы Пифагора? Если вы думаете об условии проверки в цикле for как о задании величины гипотенузы прямоугольного треугольника, для которого я знаю длину двух других сторон (горизонтальные и вертикальные параметры функции), то я думаю, что это приведет к результату I хотеть! по крайней мере, я только что попробовал это с 90/90, и это сработало. Собираюсь реализовать и отчитаюсь.
@LukeP - справедливо ли сказать, что эти функции вращения не вращают куб, а вместо этого вращают глобальный фрейм? Так что после того, как вы сделаете «rotateX», последующий «rotateY» вращается вокруг нового направления Y, а не исходного?
@ Муфрид, да, точно. На самом деле я только что опубликовал свое решение, поэтому вы можете обновить страницу.

Ответы (2)

Извини, Люк, я не думаю, что твое решение работает. У вас должно быть строгое решение, основанное на математике вращений. Проще всего это сделать с помощью кватернионов или, что еще лучше, их аналогов из алгебры Клиффорда, называемых роторами .

Я кратко объясню роторы алгебры Клиффорда и то, как их можно использовать для преобразования последовательных вращений в чистое вращение.


Алгебра Клиффорда

Алгебра Клиффорда вводит произведение векторов, которое некоммутативно, но все же ассоциативно. Если вы знакомы с точечным и перекрестным произведением, оно включает в себя свойства обоих. Учитывая ортогональный базис е 1 , е 2 , е 3 , имеем следующее:

е я е Дж "=" { + 1 я "=" Дж е Дж е я я Дж

Опять же, это тоже ассоциативно, так что е 1 е 2 е 3 "=" ( е 1 е 2 ) е 3 "=" е 1 ( е 2 е 3 ) например. Это создает 8-мерное векторное пространство со следующими базовыми элементами:

1 е 1 , е 2 , е 3 е 1 е 2 , е 2 е 3 , е 3 е 1 е 1 е 2 е 3

Скалярные кратные 1 являются скалярами. Линейные комбинации е 1 , е 2 , е 3 являются векторами. Линейные комбинации е 1 е 2 , е 2 е 3 , е 3 е 1 называются бивекторами , и они представляют интерес для вращений. Вращения происходят в плоскостях (только в 3D мы можем сказать, что они вращаются вокруг оси, что эквивалентно), и бивекторы непосредственно описывают плоскости, в которых происходят вращения.


Роторы и вращения

Я заявлю следующее без доказательств, хотя это должно быть знакомо любому, кто хоть немного разбирается в кватернионах.

Задайте ротор как экспоненту бивектора (обычно определяемого через степенной ряд). Позволять Б быть некоторым единичным бивектором. Затем ротор д "=" опыт ( Б т ) для некоторого скаляра т является

д "=" опыт ( Б т ) "=" потому что т + Б грех т

Карта вращения р _ в плоскости, соответствующей Б под углом θ принимает форму

р _ ( а ) "=" е Б θ / 2 а е Б θ / 2

где а любой вектор.

(Примечание: для тех, у кого есть опыт работы с кватернионами, я "=" е 2 е 3 , Дж "=" е 3 е 1 , и к "=" е 1 е 2 , так что в этом определении нет расхождений по знаку.)


Применение: нахождение чистого вращения

Использование роторов упрощает математический анализ вращения. Например, давайте рассмотрим вашу проблему: ваши функции вращения вращают глобальную систему координат и сами используют глобальную систему координат в качестве системы отсчета для этих поворотов. Это делает последовательность вращений несколько нетривиальной, но роторы позволяют нам найти более простой способ делать вещи.

Позволять е Икс , е у , е г быть исходным кадром. Если вы хотите повернуть вокруг оригинала е Икс а потом про оригинал е у , тогда ваши роторы будут выглядеть так:

д сеть "=" опыт ( е г е Икс ф / 2 ) опыт ( е у е г θ / 2 ) "=" д у д Икс

Затем мы можем умножить это, чтобы найти чистую плоскость вращения и чистый угол.

д сеть "=" потому что ф 2 потому что θ 2 е г е Икс грех ф 2 потому что θ 2 е у е г потому что ф 2 грех θ 2 + е Икс е у грех θ 2 грех ф 2

Мы можем найти чистый угол поворота α к

потому что α 2 "=" потому что ф 2 потому что θ 2

Давайте рассмотрим случай, как вы предложили, ф "=" θ "=" π / 2 . Тогда у нас должно быть

потому что α 2 "=" ( потому что π 4 ) 2 "=" 1 2 α 2 "=" π 3

или α "=" 2 π / 3 "=" 120 .

Чистая плоскость вращения ( е у е г + е г е Икс е Икс е у ) / 3 , или вращение вокруг оси ( е Икс + е у е г ) / 3 .

Изменить: имея это в виду, вот что вам нужно сделать.

  • Для каждого поворота в последовательности поворотов определите угол поворота θ и единичная плоскость Б ^ . Например, если вы хотите вращаться вокруг е Икс направлении, то плоскость вращения е у е г .
  • Запишите соответствующий ротор для каждого из этих вращений как д "=" потому что θ / 2 Б ^ грех θ / 2 .
  • Напишите функцию умножения роторов. Каждый ротор состоит только из четырех компонентов и представляет собой линейную комбинацию 1 , е Икс е у , е у е г , е г е Икс . Используя правила, которые я изложил во втором разделе, вычислите таблицу умножения для этих базовых элементов. (Или схитрить и найти таблицу умножения кватернионов.)
  • Умножьте отдельные роторы, чтобы найти чистый ротор комбинированного вращения.
  • Учитывая этот ротор, найти угол ф и плоскость вращения б ^ для чистого оборота. Опять же, помните, что ротор можно записать как д "=" потому что ф / 2 б ^ грех ф / 2 . Это означает, что вы можете взять скалярную часть и использовать арккосинус, чтобы найти ф . Вы можете взять оставшиеся компоненты и нормализовать их как бивектор (так же, как и вектор), чтобы найти единичную плоскость вращения.
Потрясающий! большая часть этой математики сейчас у меня в голове, но (как только я это пойму), я, вероятно, смогу использовать ее для создания более эффективного алгоритма, но я верю, что мое решение работает. Я только что реализовал это в своем приложении, и оно выглядит великолепно. Вы только что говорили о моей реализации идеи (если да, проверьте мой обновленный код в моем ответе)?
Несмотря на это, спасибо, я проголосовал, но я подожду, чтобы увидеть, есть ли у большего количества людей мнения о моем решении по сравнению с вашим, прежде чем я выберу одно, чтобы «принять». Хотя, несмотря на это, технически ваш будет более точным, так как мне придется значительно замедлить свой алгоритм, чтобы превзойти точность до одного десятичного знака.
Я говорил конкретно о вашем ответе. Например, вы ссылаетесь на теорему Пифагора, чтобы сделать вывод, что угол должен быть 127 . Тем не менее, теорема Пифагора не участвует в этом вычислении, и поэтому я пришел к выводу, что угол поворота равен 120 и не 127 .
хм, одна из причин, по которой я был уверен в своем методе, заключается в том, что он совпал с тем, что @greggo сказал в своем комментарии. Действительно ли ваш метод приведет к чистому вращению на 90 градусов вправо и 90 градусов вверх?
Да. Я взял роторы для каждого из них. 90 вращения, перемножил их, чтобы выполнить эти вращения последовательно, а затем я просто проанализировал чистый ротор, чтобы найти его угол и плоскость вращения.
Прошу прощения, я действительно проверил еще раз, и мое решение не работает, вы правы. Хотя он создает визуально приятные повороты, он отлично подойдет в качестве замены, пока я пытаюсь выяснить, как это можно реализовать прагматично.
Да, я предполагаю, что у вас могут возникнуть проблемы с преобразованием этого вращения в последовательность вращений с использованием стандартных функций, которые вам даны. Я попробовал это и нашел результат несколько беспорядочным. Если вас это интересует, дайте мне знать, и если вы хотите сделать это как последовательность небольших вращений (для плавной анимации), я могу решить и это.
@Mumphrid Итак, оказывается, у Processing есть функция вращения, которой нет в стандартной документации, которая позволяет вам вращаться вокруг угла и оси, которые вы указываете, например, не могли бы вы помочь мне понять, как перевести информацию, которую я rotate(angle,x,y,z)передаю функцию в единичный вектор и угол? (в чате)

Мое решение

Всем в разделе комментариев я хотел бы поблагодарить вас за вашу помощь, поскольку вы привели меня к решению!

Итак, как вы сказали, моя интуиция была неверной, и вращения вокруг нескольких осей не коммутируют.

Чтобы было совершенно ясно, с чем я работаю, я хочу повторить, что значения, передаваемые в мою функцию, представляют желаемое чистое вращение в горизонтальном и вертикальном направлениях соответственно.

поэтому, когда я попробовал rotate2D(90,90), я не получил поворот на 90 градусов в каждом направлении, а скорее компромисс между ними. Другими словами, я пытался использовать длину одной из малых сторон треугольника в качестве его гипотенузы!

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

введите описание изображения здесь

Таким образом, используя теорию Пифагора, чтобы определить, как долго продолжать последовательность поворотов на 0,1 градуса на основе желаемых чистых поворотов в горизонтальном и вертикальном направлениях, я могу достичь желаемых результатов!

Вот новый код:

void rotate2D(float horizontal, float vertical)
{ 
  float[] rotations ={horizontal, vertical};
  if(rotations[0]>rotations[1])
  {
    float tmp = rotations[1];
    rotations[1]=rotations[0];
    rotations[0]=tmp;
  } 

  float c= sqrt(pow(rotations[0],2)+pow(rotations[1],2));
  float cIncr=0.1;
  float hIncr=(horizontal/(c/cIncr));
  float vIncr=(vertical/(c/cIncr));
  for(float i=0; i<c; i+=cIncr)
  {    
     rotateY(radians(hIncr));
     rotateX(radians(vIncr));
  }   
}

РЕДАКТИРОВАТЬ: оказалось, что этот метод не работает, в моем методе тестирования был недостаток, но он ДЕЙСТВИТЕЛЬНО производит визуально приятные вращения для моих целей.