围绕一个中心质点创建圆形路径的算法?

3
我正在尝试简单地让物体围绕一个中心点轨道运动,例如: Orbiting Objects 绿色和蓝色的物体代表应该在一定距离内围绕中心点旋转的物体,这个距离是基于一个角度传入方法中计算得出的。
我已经尝试用Objective-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的赋值上。我错在哪里了?


如果您愿意,可以更改视图的bounds以使(0,0)位于旋转中心:view.bounds = CGRectMake(-width/2, -height/2, width, height) - bshirley
1
当你说“nowhere close”时,你是什么意思?是否翻转了原点?(在许多图形库中,(0,0)通常位于左上角。)是否超出屏幕?(检查一下是否发生了整数/浮点数算术的奇怪情况。)是否在图像的角落而不是中心?另外:这些注释的数字是你期望的还是你通过调试器观察到的?(如果你没有通过调试器观察到它们,可以尝试一下。) - fearlesstost
4个回答

1

你可以很容易地找到点距离中心的距离:

半径 = sqrt((160 - x)^2 + (240 - y)^2)

其中 (x, y) 是你的物体初始位置的中心。然后只需用半径替换 50。

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


你可以使用三角函数计算出初始角度(tan = 对边 / 邻边,因此绘制一个以质心和轨道物体中心为顶点的直角三角形来可视化这个过程):

angle = arctan((y - 240) / (x - 160))

如果 x > 160,则:

angle = arctan((y - 240) / (x - 160)) + 180

如果 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);

因此,我们只执行上面的代码一次(当对象被创建时)。我们需要记住半径调整以备后用。然后,我们改变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)];

顺便说一句,一旦您成功实现这个功能,我强烈建议您将所有角度转换为弧度,这样可以使代码更整洁、易于理解!

@George,根据你输入的数值,你的半径似乎是正确的。问题在于你没有考虑到对象开始的角度(就像我回答中的第二部分一样),你仍然只使用了“heading”。因此,它不会从你放置对象的位置开始,而是跳到1度的起始角度并从那个位置开始运行轨道(当你运行程序时,这是你看到的吗?)。另一个问题是,你需要在rotateGear方法之外计算起始角度,否则似乎没有任何办法跟踪起始角度。 - user837324
@George 我添加了一个编辑,其中包含一些(诚然有点糟糕的)Obj-C代码,希望这能让我的意思更清楚。 - user837324

0

基于弧度、半径和中心点的圆上一点的X和Y坐标公式:

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

你可以使用HappyPixel的公式来找到半径。

一旦你确定了半径和中心点,你只需要改变角度就可以得到你想要的圆上所有的点。


@George:你想要使用你旋转物体的中心点,而不是画布的中心点。我认为在你的代码中它们是 x1y1?计算距离时也是同样的道理。 - Claudiu
我的画布中心是我旋转的中心。 - George Johnston
哦,嗯。heading应该做什么呢?如果将其设置为1,则它将使用从画布中心到右侧的射线作为基础,将对象捕捉到1度处。如果您只是不断调用函数,使heading变为1、2、3、4、5、6...,它应该会旋转。但对于每个输入,它都会给出相同的结果(重复给它1不会使它移动)。这不是你想要的吗? - Claudiu

0
如果我理解正确,您想执行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

你的代码看起来没问题,除了需要跟踪原始角度(就像我的示例和@HappyPixel的一样)。你需要一个“init”方法,它将给你“initial_angle”(如果你不想不断重新计算半径,则加上半径)。然后,你应该能够将“initial_angle + heading”传递给你的函数。 - AShelly

0
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

那就使用你已经有的代码,一切都会好的。不过你似乎每次都从当前位置计算半径?这个和角度一样,应该只在物体被放下时计算一次,否则半径可能不是恒定的。

*如果你需要起始角度小于1/100度的精度,可以考虑使用极坐标反正切函数,而不是处理dx=0的三种特殊情况,可以参考谷歌上的相关资料。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接