为什么旋转时三角形会变大?

Why does triangle expands when i rotate it?

我一直在尝试用 ncurses 在 c 中进行碰撞模拟,但有一个主要问题。 似乎我的三角形随着旋转运动而膨胀,我不知道是什么原因造成的。 当我注释掉 updateTri() 函数时它也不会扩展,但它也不会旋转。

#include<stdlib.h>
#include<stdio.h>
#include<curses.h>
#include<math.h>
#include<time.h>
#include<sys/ioctl.h>
#include<sys/time.h>
#include<unistd.h>
    
struct winsize win;
    
typedef struct vector2d{
    double x;
    double y;
} Vec2;
    
typedef struct triangle{
    Vec2 *p1;
    Vec2 *p2;
    Vec2 *p3;
}Tri;
    
//
//DRAWING STUFF
//
    
void swapp(Vec2 *p1,Vec2 *p2){Vec2 tmp = *p1;*p1 = *p2;*p2 = tmp;}
    
double inpolx(Vec2 p1,Vec2 p2,double y){ //interpolate x from p1 to p2 with y variable
    double x = p1.x + (p2.x - p1.x)*(y - p1.y)/(p2.y - p1.y);
    return x;
}
    
void DSline(double x1,double x2,double y ){ //Draws Simple line
        
    double x,x2l;
    if(x1<x2){
        x=x1;x2l = x2;
    }else{
        x=x2;x2l = x1;
    }
    do{mvprintw(y/2,x,"#");
    } while(x++<x2l);
}
    
void drawtup(Tri t){
    Vec2 *p1 = t.p1,*p2 = t.p2,*p3 = t.p3;
    double invslope1 = (p2->x - p1->x) / (p2->y - p1->y),invslope2 = (p3->x - p1->x) / (p3->y - p1->y),curx1 = p1->x,curx2 = p1->x;
    for(double Y = p1->y; Y < p2->y; Y++){
        DSline(curx1,curx2,Y);
        curx1 += invslope1;
        curx2 += invslope2;
    }
}

void drawtdown(Tri t){
    Vec2 *p1 = t.p1,*p2 = t.p2,*p3 = t.p3;
    double invslope1 = (p3->x - p1->x) / (p3->y - p1->y),invslope2 = (p3->x - p2->x) / (p3->y - p2->y),curx1 = p3->x,curx2 = p3->x;
    for(double Y = p3->y; Y > p1->y; Y--){
        DSline(curx1,curx2,Y);
        curx1 -= invslope1;
        curx2 -= invslope2;
    }
}
    
void scanln(Tri tr){
    if(tr.p2->y < tr.p1->y){ swapp(tr.p2, tr.p1); }if(tr.p3->y <tr.p1->y){ swapp(tr.p3, tr.p1); }if(tr.p3->y < tr.p2->y){ swapp(tr.p3, tr.p2); }
    
    if(tr.p3->y == tr.p2->y){
        drawtup(tr);
    }
    else if(tr.p1->y == tr.p2->y){
        drawtdown(tr);
    }
    else{
        Vec2 p4 = {inpolx(*tr.p1,*tr.p3,tr.p2->y),tr.p2->y};
        Tri tu = {tr.p1,tr.p2,&p4};
        Tri td = {tr.p2,&p4,tr.p3};
        drawtup(tu);
        drawtdown(td);
    }
}
    
//
//SIMULATION CALCULATIONS
//
    
Vec2 crossd(Vec2 p ,double d){
    Vec2 r = {-1*d*p.y,d*p.x};
    return r;
}

Vec2 addVec(Vec2 p,Vec2 offs){
    Vec2 v;
    v.x =p.x+ offs.x;
    v.y =p.y+ offs.y;
    return v;
}

Vec2 mulv(Vec2 p,double u){
    Vec2 pr = {p.x*u,p.y*u};
    return pr;
}
    
void moveVec(Vec2 *v,Vec2 offs){ //brooom
    v->x += offs.x;
    v->y += offs.y;
}
        
Vec2 cenTri(Tri t){
    Vec2 v ={(t.p1->x+t.p2->x+t.p3->x)/3,(t.p1->y+t.p2->y+t.p3->y)/3};
    return v;
}

void updateTri(Tri *t,Vec2 v,double w){
    Vec2 cen = cenTri(*t);
    Vec2 r1 = {t->p1->x-cen.x,t->p1->y-cen.y};
    moveVec(t->p1,addVec(v,crossd(r1,w)));
    
    Vec2 r2 = {t->p2->x-cen.x,t->p2->y-cen.y};
    moveVec(t->p2,addVec(v,crossd(r2,w)));
    
    Vec2 r3 = {t->p3->x-cen.x,t->p3->y-cen.y};
    moveVec(t->p3,addVec(v,crossd(r3,w)));
}
    
int main(){
    int ms = 10;
    ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
    time_t start, now, s_T, e_T;struct timespec delay;delay.tv_sec = 0;delay.tv_nsec = ms * 999999L;time(&start);
        
    initscr();
    clear();
    Vec2 p[3] = {{20,26},{22,45},{46,25}};Tri t = {&p[0],&p[1],&p[2]};
    
    
    double mass = 100;
    Vec2 v = {0,0};
    double w = 0.2;
    
    while(1){
        ioctl(STDOUT_FILENO, TIOCGWINSZ, &win);
        Vec2 screen = {win.ws_col,win.ws_row};
    
        nanosleep(&delay,NULL);

        updateTri(&t,v,w);
        clear();
        scanln(t);
        refresh();
    }
    getch();
    return 0;
}

三角形在 t

三角形位于 t+x

问题是

void updateTri(Tri *t,Vec2 v,double w){
    Vec2 cen = cenTri(*t);
    Vec2 r1 = {t->p1->x-cen.x,t->p1->y-cen.y};
    moveVec(t->p1,addVec(v,crossd(r1,w)));
    
    Vec2 r2 = {t->p2->x-cen.x,t->p2->y-cen.y};
    moveVec(t->p2,addVec(v,crossd(r2,w)));
    
    Vec2 r3 = {t->p3->x-cen.x,t->p3->y-cen.y};
    moveVec(t->p3,addVec(v,crossd(r3,w)));
}

根据顶点的瞬时速度在有限的时间内更新顶点(此处任意选择timeStep = 1)。

在任何时刻顶点的速度ri相对于旋转中心是

vi = ω × r

但是在下一个时刻粒子的位置是

ri* = r i + δt * vi

所以新速度应该是

vi = ω × ri*

等δt → 0.

但是对于有限的时间步长,你不能执行上述操作,因为它总是会在每一步 δt 中沿直线 移动点。所以你总是会有失真。

你需要做的是考虑每个顶点的位置考虑body

的整体旋转角度
p1->x = cen->x + a*cos(θ) - b*sin(θ);
p1->y = cen->y + a*sin(θ) + b*cos(θ);

其中 ab 是描述顶点在 body 坐标系(零旋转)中的位置的常量。


总结 只有旋转可以保持长度,因此问题的运动学(描述所有可能配置和运动的数量)需要包括每个 body,以及从这个角度导出的 positions/velocties 等。

对代码的这些修改将保留三角形的大小和形状:

// return vector p rotated by angle d
Vec2 crossd(Vec2 p ,double d){
    double s = sin(d);
    double c = cos(d);
    Vec2 r = { c*p.x - s*p.y, s*p.x + c*p.y };
    return r;
}
void updateTri(Tri *t,Vec2 v,double w){
    Vec2 cen = cenTri(*t);
    v = addVec(v,cen);

    Vec2 r1 = {t->p1->x-cen.x,t->p1->y-cen.y};
    *t->p1 = addVec(v,crossd(r1,w));
    
    Vec2 r2 = {t->p2->x-cen.x,t->p2->y-cen.y};
    *t->p2 = addVec(v,crossd(r2,w));
    
    Vec2 r3 = {t->p3->x-cen.x,t->p3->y-cen.y};
    *t->p3 = addVec(v,crossd(r3,w));
}

实际上,这是执行以下步骤:

  1. 获取从原点到三角形中心的向量cen
  2. 为方便起见,将矢量 cen 添加到增量位移矢量 v 中(因为这个矢量和将添加到旋转矢量 r1r2r3 以后)。
  3. r1r2r3设置为顶点p1p2p3cen、因此 r1r2r3 是相对于三角形中心的顶点。
  4. p1p2p3 设置为 r1r2r3 的旋转版本(旋转增量角d)添加到v(原来的v加上cen)。

总结:

  1. 平移三角形,使旋转中心位于原点。
  2. 将径向向量(从中心到顶点)旋转增量角。
  3. 将三角形平移回原始位置。
  4. 通过增量位移平移三角形。

显然,还有优化的余地。所有的顶点都旋转了相同的量,所以正弦和余弦可以计算一次(也许作为参数传递而不是增量角)。