【计算机动画】实验 路径曲线与运动物体控制 设计
要求 - 路径曲线与运动物体控制
- 掌握Cardinal样条曲线的表示和算法,了解控制参数对曲线形状的影响。
- 对照Cardinal样条曲线的数学表示和程序代码的对应关系。
- 在路径曲线上放置一小汽车,使其在路径上运动起来,汽车运动速度可调。
1.Cardinal spline
.三阶B样条插值
介绍
http://www.cnblogs.com/caster99/p/4746652.html
http://www.cnblogs.com/icmzn/p/5101323.html
.Bezier曲线
2.弧长计算方法
对应时间统计 QTimer
精度设置
1. Simpson
2. Newton-Raphson + 高斯求积
3. 向前差分的近似计算方法
小车
计算倾斜角 QtMath
int Line::calculate_theta_single(const int deltax,const int deltay)
{
double angle;
double length =sqrt(deltax*deltax+deltay*deltay);
angle = asin(deltay/length);
if(deltax<0)
angle = 3.1415926-angle;
return angle/3.1415926*180;
}
速度统计
- 弧长参数化
- 等时间间距点
速度曲线
二维 -> 三维
实现
类
整条曲线、记录点的位置
class line
{
mat *points;
int point_num;//record the number in the line
public:
line(){}
~line(){}
addpoint(mat point){}//insert new point
init_all(){}//change the value of \tau will cause a change for all the subline
init_single(){}//change the position of a certain point
记录每条分段的线
class subline
{
int index;//record line index
mat factor;
public:
subline(int i)index(i):{}
~subline(){}
init(){//special attention should be payed to the starting & ending point}//acording to index and \tau
getS(){}//return the length according to radio with intergal
getu(){}//return the radio according to the length with numerical method
}
Qt相关类
呈现
QGraphicsView
QLabel - QMovie可以呈现动图
实现过程
类的确定
mainWindow(主界面) - mainscene(场景管理) - points(点)&line(插值)
界面
图标
http://www.cnblogs.com/davesla/archive/2011/01/17/1937343.html
QLabel显示gif - QMovie
http://www.cnblogs.com/hnrainll/archive/2011/05/22/2053701.html
Wegiet处理
http://blog.sina.com.cn/s/blog_791f544a0100r2ru.html
http://blog.csdn.net/anialy/article/details/8181038
插入点
这里要比较熟悉QGraphicView\Scene\Item的操作
http://www..com/kevinzhwl/archive/2012/09/06/2673439.html
QGraphicsView重写 - 接受鼠标点击事件
https://yq.aliyun.com/wenzhang/show_32192
http://blog.sina.com.cn/s/blog_4b9ee9e501011xx1.html
http://blog.sina.com.cn/s/blog_4ed318310101fhfa.html
QGraphicsView显示点
固定QGraphicsScene的坐标
并且对resizeEvent进行处理
(不这样的话 会自己移动调整到最适合显示……冷漠.jpg)
(然后用mapToScene什么估计很麻烦,就酱)
void MainWindow::resizeShape(qreal w,qreal h)
{
myscene.Scene.setSceneRect(0, 0, w, h);
}
然后QGraphicItem有直接的setPen这种的函数继承下来(我一直想用QPainter类但是不会= =)
初步绘制直线 - 对起始点的特殊处理(第一个点不用添加直线)
后来我发现如果想的话可以把Point设置成可以拖动
路径插值
如果不做参数化的话,应该问题不大
参数化的话,这个好像没太做好,感觉想做的话还要新开数组去记录每一行的长度。
现在我做的是比较偷懒,就,先统计整条插值后的曲线的长度,然后算出我应该隔多少距离插一个点,然后对每条单独的曲线都按这个插值(所以端点处的计算肯定有问题)。
然后细节计算也不是用二分法而是 ,扫过去……
下面的大概就是,在两点之间取样的总次数是grain*16,然后一段一段累加sum,直到sum大于传入的某个step值之后记录这个点。
for(int i = 0; i < grain*16 ; i++)
{
tx = get_length(px,step_record);
ty = get_length(py,step_record);
double temp = sqrt(tx*tx+ty*ty);
sum += temp;
step_record += step;
if(sum>record/step)
{
tweenX[count] = get_pos(px,step_record);
tweenY[count++] = get_pos(py,step_record);
sum = sum-record/step;
}
}
double Line::get_length(double *p,double u) const
{
return p[2]+u*(2.*p[1]+3.*u*p[0]);
}
角色移动
角色移动的时候,设置所有角色的精灵都是某个Item的子结点。
因为QGraphicItem里child的位置是相对于Parent的位置,所以直接修改Parent会方便很多(毕竟我这动画帧有8*2(往左往右)=16个)
这里没有在QT内找到对应的(水平翻转)函数,所以是靠PS的,然后QGraphItem也不会显示gif所以是靠自己控制图片显示不同加的。
因为希望角色的gif和移动是独立开来的,所以是由俩个不同的QTimer类控制的
对于垂直特殊处理(不修改状态),然后为了防止速度的一些突然变换设置的是,记录前一个点的状态然后连续两个点是同一个方向才改他的方向(虽然好像没啥道理)
if(theta == 90 || theta == -90)
{
//对第一帧的特殊处理
if(1==mov_flame)
{
group_character->setRotation(theta+180);
if(theta==90)
{
char_index= true;
}
else
{
char_index= false;
}
}
}
else if( theta>90 || theta<-90 )
{
if( char_index_before== true)
{
if(true == char_index){}
else
{
char_index= true;
character[ch_flame][0]->setVisible(false);
group_character->setRotation(theta+180);
character[ch_flame][1]->setVisible(true);
}
group_character->setRotation(theta+180);
}
else
char_index_before = true;
}
else
{
//......
}
如果按课本的话,应该是u-t的关系是正弦。
然后我写的成了,v-t的关系。这就造成了加速sin曲线的第一个点死活走不动(因为速度是0……)
定时器
http://blog.163.com/bingcaihuang@126/blog/static/198942122010103104026183/