Алгоритм создания круговой траектории вокруг центра масс?

Я пытаюсь просто заставить объекты вращаться вокруг центральной точки, например

Орбитальные объекты

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

Я попытался создать функцию в target-c, но она не работает без статического числа. например (Он вращается вокруг центра, но не от истинной начальной точки или расстояния от объекта.)

-(void) rotateGear: (UIImageView*) view heading:(int)heading
{
    // int distanceX = 160 - view.frame.origin.x;
    // int distanceY = 240 - view.frame.origin.y;

    float x = 160 - view.image.size.width / 2 + (50 * cos(heading * (M_PI / 180)));
    float y = 240 - view.image.size.height / 2 + (50 * sin(heading * (M_PI / 180)));
    view.frame = CGRectMake(x, y, view.image.size.width, view.image.size.height);
}

Мои магические числа 160 и 240 - это центр холста, на котором я рисую изображения. 50 - статическое число (и проблема), которое позволяет функции работать частично правильно - без сохранения начального положения объекта или правильного расстояния. К сожалению, я не знаю, что здесь поставить.

heading - это параметр, который передается в степени от 0 до 359. Он вычисляется таймером и увеличивается за пределами этого класса.

По сути, я хотел бы иметь возможность перетащить любое изображение на свой холст, и в зависимости от начальной точки изображения оно будет вращаться вокруг центра моего круга. Это означает, что если бы я поместил изображение в Точку (10,10), расстояние до центра круга сохранялось бы, используя (10,10) в качестве отправной точки. Объект повернется на 360 градусов вокруг центра и достигнет своей исходной начальной точки.

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

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

Обновление при сбое

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

heading установлен на 1 градус.

-(void) rotateGear: (UIImageView*) view heading:(int)heading
{
    float y1 = view.frame.origin.y + (view.frame.size.height/2); // 152
    float x1 = view.frame.origin.x + (view.frame.size.width/2); // 140.5

    float radius = sqrtf(powf(160 - x1 ,2.0f) + powf(240 - y1, 2.0f)); // 90.13

    // I know that I need to calculate 90.13 pixels from my center, at 1 degree.      
    float x = 160 + radius * (cos(heading * (M_PI / 180.0f))); // 250.12
    float y = 240 + radius * (sin(heading * (M_PI / 180.0f))); // 241.57

    // The numbers are very skewed.
    view.frame = CGRectMake(x, y, view.image.size.width, view.image.size.height);
}

Я получаю результаты, которые далеко не там, где должна быть точка. Проблема заключается в присвоении x и y. Где я ошибаюсь?


person George Johnston    schedule 12.07.2011    source источник
comment
если хотите, вы можете изменить bounds вашего представления так, чтобы (0,0) находилось в центре вращения: view.bounds = CGRectMake(-width/2, -height/2, width, height)   -  person bshirley    schedule 12.07.2011
comment
когда вы говорите «нигде не близко», что вы имеете в виду? перевернул происхождение? (во многих графических библиотеках (0, 0) неправильно вверху слева.) с экрана? (убедитесь, что что-то странное с арифметикой int / float не происходит.) в углу изображения, а не в центре? также: эти прокомментированные числа - то, что вы ожидаете, или то, что вы наблюдали в отладчике? (если вы не наблюдали их через отладчик, вы можете попробовать это.)   -  person fearlesstost    schedule 13.07.2011


Ответы (4)


Вы можете легко определить расстояние точки от центра:

радиус = sqrt ((160 - x) ^ 2 + (240 - y) ^ 2)

где (x, y) - начальное положение центра вашего объекта. Затем просто замените 50 на радиус.

http://en.wikipedia.org/wiki/Pythagorean_theorem


Затем вы можете определить начальный угол с помощью тригонометрии (tan = противоположный / смежный, поэтому нарисуйте прямоугольный треугольник, используя центр масс и центр вашего орбитального объекта, чтобы визуализировать это):

угол = арктангенс ((y - 240) / (x - 160))

if x > 160, or:

угол = арктангенс ((y - 240) / (x - 160)) + 180

if x < 160

http://en.wikipedia.org/wiki/Inverse_trigonometric_functions


Изменить: имейте в виду, что я на самом деле не знаю никакого Objective-C, но это в основном то, что я думаю, вы должны сделать (вы сможете довольно легко перевести это, чтобы исправить Obj-C, это просто для демонстрации):

// Your object gets created here somewhere

float x1 = view.frame.origin.x + (view.frame.size.width/2); // 140.5
float y1 = view.frame.origin.y + (view.frame.size.height/2); // 152

float radius = sqrtf(powf(160 - x1 ,2.0f) + powf(240 - y1, 2.0f)); // 90.13

// Calculate the initial angle here, as per the first part of my answer
float initialAngle = atan((y1 - 240) / (x1 - 160)) * 180.0f / M_PI;
if(x1 < 160)
    initialAngle += 180;

// Calculate the adjustment we need to add to heading
int adjustment = (int)(initialAngle - heading);

Таким образом, мы выполняем приведенный выше код только один раз (при создании объекта). Нам нужно помнить radius и adjustment на будущее. Затем мы изменяем rotateGear, чтобы в качестве входных данных брали угол и радиус вместо heading (в любом случае это гораздо более гибко):

-(void) rotateGear: (UIImageView*) view radius:(float)radius angle:(int)angle
{
    float x = 160 + radius * (cos(angle * (M_PI / 180.0f)));
    float y = 240 + radius * (sin(angle * (M_PI / 180.0f)));

    // The numbers are very skewed.
    view.frame = CGRectMake(x, y, view.image.size.width, view.image.size.height);
}

И каждый раз, когда мы хотим обновить позицию, мы делаем такой вызов:

[objectName rotateGear radius:radius angle:(adjustment + heading)];

Между прочим, как только вам удастся заставить это работать, я настоятельно рекомендую преобразовать все ваши углы, чтобы вы использовали радианы на всем протяжении, это делает его намного более аккуратным / легким для понимания!

person Community    schedule 12.07.2011
comment
@George Ваш радиус кажется правильным, учитывая введенные вами значения. Проблема в том, что вы не учли угол, под которым начинается ваш объект (как во второй половине моего ответа), вы все еще используете заголовок. Таким образом, вместо того, чтобы начинать с того места, где вы разместили объект, он переместится на начальный угол в 1 градус и начнет вращаться по орбите с этого места (это то, что вы видите, когда запускаете программу?). Другая проблема заключается в том, что вам нужно рассчитать начальный угол вне метода rotateGear, иначе, похоже, нет никакого способа отслеживать начальный угол. - person ; 13.07.2011
comment
@George Я добавил правку с некоторым (по общему признанию хитрым) кодом Obj-C, надеюсь, это немного проясняет то, что я имею в виду - person ; 13.07.2011

Формула для координат x и y точки на окружности, основанная на радианах, радиусе и центральной точке:

x = cos(angle) * radius + center_x
y = sin(angle) * radius + center_y

Вы можете найти радиус с помощью HappyPixel < Формула / a>.

Как только вы определите радиус и центральную точку, вы можете просто изменить angle, чтобы получить все точки на окружности, которые вам нужны.

person Claudiu    schedule 12.07.2011
comment
@ Джордж: вы хотите использовать центр объекта, вокруг которого вы вращаете, а не центр вашего холста. я думаю, они x1 и y1 в вашем коде? То же самое и при расчете расстояния. - person Claudiu; 13.07.2011
comment
Центр моего холста - это то, что я вращаю вокруг - person George Johnston; 13.07.2011
comment
ой хм. что именно heading должен делать? если вы установите его на 1, то объект будет привязан к объекту на 1 градусах, используя луч, идущий от центра холста вправо в качестве основы. если вы просто продолжаете вызывать функцию, заставляя heading перейти на 1, 2, 3, 4, 5, 6 ..., она должна вращаться. но он даст одинаковый результат для каждого ввода. (повторное присвоение ему 1 не заставит его двигаться). разве это не то, что вы хотите? - person Claudiu; 13.07.2011

Если я правильно понял, вы хотите сделать InitObject(x,y)., а затем UpdateObject(angle), где угол изменяется от 0 до 360. (Но используйте радианы вместо градусов для математических расчетов)
Итак, вам нужно отслеживать угол и радиус для каждого объекта .:

InitObject(x,y)
    relative_x = x-center.x
    relative_y = y-center.y
    object.radius = sqrt((relative_x)^2, (relative_y)^2)
    object.initial_angle = atan(relative_y,relative_x);

И

UpdateObject(angle)
    newangle = (object.initial_angle + angle) % (2*PI )
    object.x = cos(newangle) * object.radius + center.x
    object.y = sin(newangle) * object.radius + center.y
person AShelly    schedule 12.07.2011
comment
ваш код выглядит правильно, за исключением того, что вам нужно отслеживать исходный угол (как в моем примере и в @ HappyPixel). Вам нужен init метод, который даст вам initial_angle (+ радиус, если вы не хотите продолжать его пересчитывать). Тогда вы сможете передать initial_angle + heading своей функции. - person AShelly; 13.07.2011

dx=dropx-centerx; //target-source
dy=-(dropy-centery); //minus = invert screen coords to cartesian coords
radius=sqrt(dy*dy+dx*dx); //faster if your compiler optimizer is bad

if dx=0 then dx=0.000001; //hackpatchfudgenudge*
angle=atan(dy/dx); //set this as start angle for the angle-incrementer

Затем используйте тот код, который у вас есть, и все будет в порядке. Кажется, вы каждый раз вычисляете радиус от текущей позиции? Это, как и угол, следует делать только один раз, когда объект падает, иначе радиус может быть непостоянным.

* вместо обработки трех особых случаев для dx = 0, если вам нужна ‹точность 1/100 градуса для начального угла, воспользуйтесь этим, используйте Google Polar Arctan.

person Henrik Erlandsson    schedule 16.08.2011