线性插值,也就是给定初始点集合和终止点集合,然后给定一个映射关系。对一一对应的点的位置,即对x,y坐标进行线性插值。
x = x0*t + x1*(1-t);
y = y0*t + y1*(1-t);
而矢量线性插值,是在给定初始点和终止点集合后,将n个点转换为n-1个按顺序首尾相连的向量。然后,再将向量转化为极坐标,并对长度和角度进行线性插值。相当于将其转换到另外一个空间,维护了其角度和长度上的连续性。
a = a*t + a*(1-t);
p = p*t + p*(1-t);
线性插值
void interpolation::setLinearPoint()
{
interPoint = new point*[num+1];//num为中间插值的个数
for(int t=0;t<=num;t++){
interPoint[t] = new point[size];
for(int i=0;i<size;i++){//size为图形的点个数,需要对每个点都进行插值
interPoint[t][i].x = (1-1.0f*t/num)*startPoint[i].x
+ 1.0f*t/num*endPoint[i].x;
interPoint[t][i].y = (1-1.0f*t/num)*startPoint[i].y
+ 1.0f*t/num*endPoint[i].y;
}
}
}
而对于矢量线性插值,要考虑的问题则相对复杂。
(1)矢量是没有位置的,它只有大小和方向信息。所以在对极坐标两个参数插值后,还需要确定它的位置。要获取位置信息,我们采用的方法是对第一个点的坐标进行线性插值。
//对第一个点的位置线性插值
interPoint = new point*[num+1];
for(int t=0;t<=num;t++){
interPoint[t] = new point[size];
interPoint[t][0].x = (1-1.0f*t/num)*startPoint[0].x
+ 1.0f*t/num*endPoint[0].x;
interPoint[t][0].y = (1-1.0f*t/num)*startPoint[0].y
+ 1.0f*t/num*endPoint[0].y;
}
//根据矢量定义,依次求出其余点坐标
for(int t=0;t<=num;t++){
for(int i=1;i<size;i++){
interPoint[t][i].x = interPoint[t][i-1].x
+ interVec[t][i-1].p*cos(interVec[t][i-1].a);
interPoint[t][i].y = interPoint[t][i-1].y
+ interVec[t][i-1].p*sin(interVec[t][i-1].a);
}
}
(2)矢量线性插值会出现二义性
如图,从一个矢量到另外一个矢量,有两种可能的旋转方式。如果直接对角度进行插值,而不做任何处理,会出现错误(与预期不符合)的旋转方向。具体表现并不是按相反方向旋转,而是一种参差不齐的现象:有的边按预期方向旋转,但有的边却不按预期方向旋转。
一种解决方法:强制保证旋转角度小于180度,这样能在极大多数情况下避免旋转方向不统一的现象(虽然不一定保证按预期方向)。例外在于旋转角度在180度附近时,依然会出现问题。
//矢量线性插值
for(int t=0;t<=num;t++){
interVec[t] = new vec[size];
for(int i=0;i<size-1;i++){
//case 1:
if(endVec[i].a-startVec[i].a<-pi){
interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
+ 1.0f*t/num*(endVec[i].a+2*pi);
}
//case 2:
else if(endVec[i].a-startVec[i].a>pi){
interVec[t][i].a = (1-1.0f*t/num)*(startVec[i].a+2*pi)
+ 1.0f*t/num*endVec[i].a;
}
//case 3:
else{
interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
+ 1.0f*t/num*endVec[i].a;
}
interVec[t][i].p = (1-1.0f*t/num)*startVec[i].p
+ 1.0f*t/num*endVec[i].p;
}
}
首先需要说明的一点,计算角度时,是通过反正切得到的,获得角度的范围在[-pi,pi]。为了保证旋转角小于180度,需要在大于180度时对某一角度强制加上2*pi,相当于多旋转一个周期,这样就改变了它们的相对位置,也就保证了角度的差值将小于180度。
另外一种解决方法:既然程序无法判断我们理想中的旋转方向,可以让用户告诉程序是需要顺时针还是逆时针旋转。
指定顺时针旋转(保证末角度始终大于初角度)
//矢量线性插值
for(int t=0;t<=num;t++){
interVec[t] = new vec[size];
for(int i=0;i<size-1;i++){
if(endVec[i].a-startVec[i].a<0){
interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
+ 1.0f*t/num*(endVec[i].a+2*pi);
}
else{
interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
+ 1.0f*t/num*endVec[i].a;
}
interVec[t][i].p = (1-1.0f*t/num)*startVec[i].p
+ 1.0f*t/num*endVec[i].p;
}
}
指定逆时针旋转(保证末角度小于初角度)
for(int t=0;t<=num;t++){
interVec[t] = new vec[size];
for(int i=0;i<size-1;i++){
if(endVec[i].a-startVec[i].a>0){
interVec[t][i].a = (1-1.0f*t/num)*(startVec[i].a+2*pi)
+ 1.0f*t/num*(endVec[i].a);
}
else{
interVec[t][i].a = (1-1.0f*t/num)*startVec[i].a
+ 1.0f*t/num*endVec[i].a;
}
interVec[t][i].p = (1-1.0f*t/num)*startVec[i].p
+ 1.0f*t/num*endVec[i].p;
}
}