为什么旋转时三角形会变大?
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(θ);
其中 a
和 b
是描述顶点在 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));
}
实际上,这是执行以下步骤:
- 获取从原点到三角形中心的向量
cen
。
- 为方便起见,将矢量
cen
添加到增量位移矢量 v
中(因为这个矢量和将添加到旋转矢量 r1
、r2
、r3
以后)。
- 将
r1
、r2
、r3
设置为顶点p1
、p2
、p3
减cen
、因此 r1
、r2
、r3
是相对于三角形中心的顶点。
- 将
p1
、p2
、p3
设置为 r1
、r2
、r3
的旋转版本(旋转增量角d
)添加到v
(原来的v
加上cen
)。
总结:
- 平移三角形,使旋转中心位于原点。
- 将径向向量(从中心到顶点)旋转增量角。
- 将三角形平移回原始位置。
- 通过增量位移平移三角形。
显然,还有优化的余地。所有的顶点都旋转了相同的量,所以正弦和余弦可以计算一次(也许作为参数传递而不是增量角)。
我一直在尝试用 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(θ);
其中 a
和 b
是描述顶点在 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));
}
实际上,这是执行以下步骤:
- 获取从原点到三角形中心的向量
cen
。 - 为方便起见,将矢量
cen
添加到增量位移矢量v
中(因为这个矢量和将添加到旋转矢量r1
、r2
、r3
以后)。 - 将
r1
、r2
、r3
设置为顶点p1
、p2
、p3
减cen
、因此r1
、r2
、r3
是相对于三角形中心的顶点。 - 将
p1
、p2
、p3
设置为r1
、r2
、r3
的旋转版本(旋转增量角d
)添加到v
(原来的v
加上cen
)。
总结:
- 平移三角形,使旋转中心位于原点。
- 将径向向量(从中心到顶点)旋转增量角。
- 将三角形平移回原始位置。
- 通过增量位移平移三角形。
显然,还有优化的余地。所有的顶点都旋转了相同的量,所以正弦和余弦可以计算一次(也许作为参数传递而不是增量角)。