【问题标题】:Rotate a triangle around itself in c using three vertixes使用三个顶点在c中围绕自身旋转三角形
【发布时间】:2023-03-22 23:05:02
【问题描述】:

我需要围绕自身旋转一个三角形(称为船)。
这是我到目前为止得到的,但它不起作用。它越来越小,直到消失。

void RotatePoint(Point *P, float angle)
{
    float theta = angle * (180/3.1415);
    P->x = (P->x * cos(theta)) - (P->y * sin(theta));
    P->y = (P->y * cos(theta)) + (P->x * sin(theta));
}

void RotateShip(Ship *ship)
{
    Rotate(&ship->A, rotateAngle);
    Rotate(&ship->B, rotateAngle);
    Rotate(&ship->C, rotateAngle);
}

点P是我要旋转的点,点C是三角形的中心。我想如果我旋转所有三个顶点,三角形就会旋转。

就我而言,我是这样初始化的:

void initShip(Ship *ship)
{
    ship->center.x = (SCREEN_W)/2.0;
    ship->center.y = (SCREEN_H)/2.0;
    ship->A.x = 0;
    ship->A.y = -5;
    ship->B.x = 15;
    ship->B.y = 25;
    ship->C.x = -15;
    ship->C.y = 25;
    ship->color = al_map_rgb(255, 255, 255);
}

船舶 A、B 和 C 是到三角形中心的距离。我绘制它,将 A、B 和 C 添加到中心顶点。

A=-0.699857,-19.963261
A=-0.000857,-19.951065
A=-0.699001,-19.914387
A=-0.001712,-19.902250
A=-0.698147,-19.865631
A=-0.002565,-19.853554

我按一键后一键,使其顺时针和逆时针旋转。注意 A 是如何缩小的。 我不知道我在做什么。当它到达顶部时,我应该回到 20.00。这样我的三角形正在缩小。 我用的是cos(0.035)和sin(0.035),意思是2度。

【问题讨论】:

  • 为什么不起作用?你做了什么来调试它?
  • 它就消失了。绘图功能是这样的:void drawShip(Ship *ship) { al_draw_triangle(ship->center.x + ship->A.x, ship->center.y + ship->A.y, ship->center.x + ship->B.x, ship->center.y + ship->B.y, ship->center.x + ship->C.x, ship->center.y + ship->C.y, ship->color, 1); } 当我按键旋转它时,它就消失了。我的数学一定有问题,但我不知道是什么问题。
  • 请做一个minimal reproducible example,不需要整个绘图和其他图形的东西,只需要一个打印坐标几次的main();展示它们如何变小,尤其是速度如何。
  • 除其他外,我还想查看您正在使用的坐标类型。诠释?漂浮?双倍?
  • 坐标相对于中心是否变小了?还是这艘船也向 (0,0) 移动?

标签: c rotation allegro


【解决方案1】:

OP 有一个经典错误:使用临时(或中间)值,而应使用原始/初始值。

作为一个简化示例,假设您有三个变量 abc,并且想要将它们的值向左旋转一个变量:

    a = b;
    b = c;
    c = a;  /* Oops! Won't work! */    

最后的赋值有问题,因为a不再是原来的值了!您不能以可以避免此问题的方式对作业进行排序;唯一改变的是哪个变量会受到问题的影响。要解决这个问题,您需要使用一个新的临时变量来保存原始值:

    t = a;
    a = b;
    b = c;
    c = t;

在 OP 的情况下,船结构不应在相同的变量中混合船的当前形状和船的真实/未旋转形状。即使您避免了上述问题,您仍然会遭受累积的舍入误差;这可能需要几个小时的游戏时间,但最终你的船看起来会有所不同。

解决方案是用单独的变量来描述船的形状,或者在船更新函数中使用常量。)

假设我们有一个变量dir,它以弧度为单位指定方向,从向上逆时针旋转,0 向上(朝向负 y 轴),π/2(和 -3π/2)向左(朝向负 x 轴),向下 π(和 -π),向右 3π/2(和 -π/2),依此类推。如果deg 以度为单位,则dir = deg * 3.14159265358979323846 / 180.0。我们也可以使用atan2()函数找出dirdir = atan2(-x, y)

dir = 0 时,OP 需要A = { 0, -5 }B = { 15, 25 }C = { -15, 25 }。如果我们定义Adir = 3.14159Ar = 5Bdir = -0.54042Br = sqrt(15*15+25*25) = 29.15476Cdir = 0.54042Cr = 29.15476,那么船的顶点是

A.x = center.x + Ar*sin(dir + Adir);
A.y = center.y + Ar*cos(dir + Adir);
B.x = center.x + Br*sin(dir + Bdir);
B.y = center.y + Br*cos(dir + Bdir);
C.x = center.x + Cr*sin(dir + Cdir);
C.y = center.y + Cr*cos(dir + Cdir);

如果 OP 想在 rotateShip 函数中修复船形,那么

void rotateShip(Ship *s, double rotateAngle)
{
    s->A.x = s->center.x +  5.00000 * sin(rotateAngle + 3.14159);
    s->A.y = s->center.y +  5.00000 * cos(rotateAngle + 3.14159);
    s->B.x = s->center.x + 29.15476 * sin(rotateAngle - 0.54042);
    s->B.y = s->center.y + 29.15476 * cos(rotateAngle - 0.54042);
    s->C.x = s->center.x + 29.15476 * sin(rotateAngle + 0.54042);
    s->C.y = s->center.y + 29.15476 * cos(rotateAngle + 0.54042);
}

就我个人而言,我会使用可变数量的顶点来定义船的形状:

typedef struct {
    double  x;
    double  y;
} vec2d;

typedef struct {
    vec2d        center;
    size_t       vertices;
    const vec2d *shape;     /* Un-rotated ship vertices */
    double       direction; /* Ship direction, in radians */
    vec2d       *vertex;    /* Rotated ship vertices */
} Ship;

const vec2d default_shape[] = {
    {   0.0, -5.0 },
    { -15.0, 25.0 },
    {  15.0, 25.0 },
};

void updateShip(Ship *ship)
{
    const double c = cos(ship->direction);
    const double s = sin(ship->direction);
    size_t       i;

    for (i = 0; i < ship->vertices; i++) {
        ship->vertex[i].x = ship->center.x + c*ship->shape[i].x - s*ship->shape[i].y;
        ship->vertex[i].y = ship->center.y + s*ship->shape[i].x + c*ship->shape[i].y;
    }
}

void initShip(Ship *ship, const size_t vertices, const vec2d *shape)
{
    ship->center.x = 0.5 * SCREEN_W;
    ship->center.y = 0.5 * SCREEN_H;

    if (vertices > 2 && shape != NULL) {
        ship->vertices = vertices;
        ship->shape    = shape;
    } else {
        ship->vertices = (sizeof default_shape) / (sizeof default_shape[0]);
        ship->shape    = default_shape;
    }

    ship->direction = 0;

    ship->vertex = malloc(ship->vertices * sizeof ship->vertex[0]);
    if (!ship->vertex) {
        fprintf(stderr, "Out of memory.\n");
        exit(EXIT_FAILURE);
    }

    updateShip(ship);
}

updateShip中,我们使用ship-&gt;direction的2D旋转,旋转shape[]中的顶点指定的船模型,将旋转和平移的坐标保存到vertex[]

    x_current = x_center + x_original * cos(direction) - y_original * sin(direction);
    y_current = y_center + x_original * sin(direction) + y_original * cos(direction);

如在例如rotation 上的维基百科文章。请注意,原始坐标 x_originaly_original(或 Ship 结构中 shape[] 数组中的值)永远不会被修改。

这样您就可以让玩家“升级”他们的船,只需将shape 更改为指向新的船型,并更改vertices 以反映该数字。

【讨论】:

  • 你想要default_shape --> default_ship?
  • @chux:很好,谢谢!实际上,因为我想确保 OP(和其他阅读此答案的人)了解船的当前顶点(在其当前的平移和方向)与船的(原始、未旋转和未平移)形状之间的区别,我决定使用default_shape。我不知道它是否有效(有助于区分原始/未旋转和当前/旋转和翻译),但这是我目前能做的最好的。
【解决方案2】:

我可以使用 int 中的坐标重现快速收缩(同时也可以旋转)。
(基于 MCVE 会容易得多......)。
使用浮动坐标时,它的收缩速度要慢得多,但它仍然会收缩。

我将这与您的实现以非常明显的方式收集所有数学错误(计算机总是犯)的事实联系起来。

为了完全避免收缩:
不要为了旋转而操纵相对坐标。而是将相对坐标存储为常数,并将船舶方向存储为双倍角度。

然后通过增加/减少角度旋转(环绕,保持在 -Pi ... +Pi 内)。
然后通过始终将变化的角度应用于恒定的相对坐标进行绘制。

(如果您提供 MCVE,我只能向您详细展示。)

这样,收集到的错误只会导致轻微且缓慢增长的方向错误,
这很可能不会被飞行员注意到 - 然后被飞行员纠正。
“嗯,船还没有完成我想要的360。哦,那我再转一点。”

附带说明,我不相信您使用角度作为 cos() 和 sin() 参数的方式。
或者换一种说法,我认为
theta = angle * (180/3.1415); -> theta = angle; 通过 Pi 掉头。
theta = angle * (180/3.1415); -> theta = angle * (3.1415/180); 通过 180 掉头。
对于您的实施,您会得到一个 U 形转弯角度 (Pi*3.1415/180),我看不出这是什么原因。

我还建议使用 math.h 中的适当常量(例如 M_PI),而不是您自己的带有 4 位小数的常量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-05-04
    • 2012-04-08
    • 1970-01-01
    • 1970-01-01
    • 2016-07-30
    • 1970-01-01
    • 2019-03-03
    相关资源
    最近更新 更多