我看到你正在改变频率f0 -> f1 -> f0,其中滚动条V 是f1 所在的位置。
f(-Ve) = f0 if V!=-Ve
f( V ) = f1
f(+Ve) = f0 if V!=+Ve
将间隔划分为f0 -> f1 和f1 -> f0。先尝试线性插值:
f(x) = f0 + (f1-f0)*(x+Ve)/(V+Ve) x = <-Ve, V >
f(x) = f1 + (f0-f1)*(x-V )/(Ve-V) x = < V ,+Ve>
现在只需在此处简单地生成您的波形 C++/VCL 我为此失败了:
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
float y0=ys/2,A=ys/3; // zero is in middle of image and amplitudes is 1/3 image height
float f0= 5.0*(2.0*M_PI/float(xs)); // 5 periods per image width
float f1=25.0*(2.0*M_PI/float(xs)); // 25 periods per image width
float V=ScrollBar1->Position; // scroll bar is from -Ve to +Ve
const float Ve=1000;
float f,t,x,xx,yy;
t=0; yy=y0+A*sin(t);
bmp->Canvas->Pen->Color=clWhite;
bmp->Canvas->MoveTo(0,yy);
if (V>-Ve) for (x=-Ve;x<=V;x++)
{
xx=(x+Ve)*xs/(2.0*Ve); // convert x to screen position
yy=y0+A*sin(t); // compute actual amplitude
bmp->Canvas->LineTo(xx,yy); // render it
f=f0+(f1-f0)*(x+Ve)/(V+Ve); // compute actual frequency
t+=f; // update actual time with it
}
if (V<+Ve) for (x=V;x<+Ve;x++)
{
xx=(x+Ve)*xs/(2.0*Ve); // convert x to screen position
yy=y0+A*sin(t); // compute actual amplitude
bmp->Canvas->LineTo(xx,yy); // render it
f=f1+(f0-f1)*(x-V )/(Ve-V); // compute actual frequency
t+=f; // update actual time with it
}
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
只需忽略渲染内容(将其替换为您的环境编码风格)xs,ys 是图像bmp 分辨率和M_PI=3.1415 此处预览:
这里捕获了动画GIF(稍微降低f0,f1):
有关详细信息,请参阅类似的QA:
[Edit1] 以防你真的需要交点
然后只需通过检测 y 轴上的符号变化来检测过零并从 2 个结果点插入位置。程序略有变化:
// clear buffer
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
float y0=ys/2,A=ys/3;
float f0= 1.5*(2.0*M_PI/float(xs)); // 1.5 periods per image width
float f1=10.0*(2.0*M_PI/float(xs)); // 10 periods per image width
float V=ScrollBar1->Position;
const float Ve=1000;
float f,t,x,xx,xx0,yy,yy0,r=4;
bmp->Canvas->Pen->Color=clWhite;
bmp->Canvas->Brush->Color=clWhite;
x=-Ve; t=0;
xx=(x+Ve)*xs/(2.0*Ve); // convert x to screen position
yy=y0+A*sin(t); // compute actual amplitude
bmp->Canvas->MoveTo(xx,yy); // render it
if (V>-Ve) for (x=-Ve;x<=V;x++)
{
xx0=xx; yy0=yy; // remember last point
xx=(x+Ve)*xs/(2.0*Ve); // convert x to screen position
yy=y0+A*sin(t); // compute actual amplitude
bmp->Canvas->LineTo(xx,yy); // render it
f=f0+(f1-f0)*(x+Ve)/(V+Ve); // compute actual frequency
t+=f; // update actual time with it
if ((yy0-y0)*(yy-y0)<0.0) // zero crossing?
{
if (yy!=yy0) xx0=xx0+(xx-xx0)*(yy-y0)/(yy-yy0); // interpolate crossing position
bmp->Canvas->Ellipse(xx0-r,y0-r,xx0+r,y0+r); // render it
}
}
if (V<+Ve) for (x=V;x<+Ve;x++)
{
xx0=xx; yy0=yy; // remember last point
xx=(x+Ve)*xs/(2.0*Ve); // convert x to screen position
yy=y0+A*sin(t); // compute actual amplitude
bmp->Canvas->LineTo(xx,yy); // render it
f=f1+(f0-f1)*(x-V )/(Ve-V); // compute actual frequency
t+=f; // update actual time with it
if ((yy0-y0)*(yy-y0)<0.0) // zero crossing?
{
if (yy!=yy0) xx0=xx0+(xx-xx0)*(yy-y0)/(yy-yy0); // interpolate crossing position
bmp->Canvas->Ellipse(xx0-r,y0-r,xx0+r,y0+r); // render it
}
}
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
[Edit2] 三次拟合交点
如前所述,我使用屏幕截图并使用超宽设置为0%,10%,20%,30%,40%,50% 密度滚动条位置创建一个交点表。
以像素为单位,它导致这张以像素为单位的交点表:
int xi[6][9]=
{
{ 0, 48, 102, 160, 223, 289, 358, 429, 502 }, // 0%
{ 0, 77, 124, 177, 235, 299, 364, 434, 506 }, // 10%
{ 0, 98, 150, 197, 250, 309, 372, 439, 510 }, // 20%
{ 0, 107, 175, 222, 268, 319, 379, 444, 513 }, // 30%
{ 0, 114, 190, 247, 289, 334, 389, 450, 517 }, // 40%
{ 0, 120, 200, 263, 313, 354, 400, 457, 521 } // 50%
};
然后我使用我的approximation search 最小化平均误差来应用三次多项式拟合:
const int N=6,M=9;
double aj[M][4];
void fit_data() // fit polynomials
{
int i,j;
double a0,t,dt;
approx a1,a2,a3; double ee,e;
dt=1.0/double(N-1);
// fit points
for (j=0;j<M;j++)
{
a0=xi[0][j]; // first coefficient is start point
// a0, a1, da,n, ee
for (a1.init(-500.0,+500.0,10.0,3,&ee); !a1.done; a1.step())
for (a2.init(-500.0,+500.0,10.0,3,&ee); !a2.done; a2.step())
for (a3.init(-500.0,+500.0,10.0,3,&ee); !a3.done; a3.step())
for (ee=0.0,t=0.0,i=0;i<N;i++,t+=dt)
ee+=fabs(double(xi[i][j])-(a0+(a1.a*t)+(a2.a*t*t)+(a3.a*t*t*t)));
aj[j][0]=a0;
aj[j][1]=a1.aa;
aj[j][2]=a2.aa;
aj[j][3]=a3.aa;
}
j=0;
}
所以拟合的交点是这样计算的:
double compute_point(int ix,double t)
{
double a[9][4]=
{
{ 0, 1.9, -9.10000000000001, 6.2 },
{ 48, 181.5, -184.1, 74.1999999999999 },
{ 102, 93.4999999999999, 105.5, -101.8 },
{ 160, 49.8, 139.9, -87.8000000000001 },
{ 223, 54.9, 26.9, 6.2 },
{ 289, 57.8, -41.2, 48.2 },
{ 358, 28.7, 3.8, 8.2 },
{ 429, 29, -18.1, 16.2 },
{ 502, 21.7, -13.1, 9.2 }
};
if ((ix<0)||(ix>=9)) return 0.0;
return (a[ix][0]+(a[ix][1]*t)+(a[ix][2]*t*t)+(a[ix][3]*t*t*t));
}
其中t=<0.0,1.0>代表滚动条设置<0%,50%>,ix=<0,8>是你要获取的交点索引。 a[j][4],aj[j][4] 表示xi[][j] 表列的三次多项式系数。
现在您只需缩放输出以匹配您的视图,并为 50% 以上的滚动条设置添加镜像。在这里覆盖测试我检查了准确性:
红点是xi 的交点,青色点是为实际滚动条设置计算的交点。如您所见,它们非常匹配(GIF 中缺少最后一点,但也可以,我只是忘了渲染它)。 Y 轴表示滚动条设置 0% 在顶部,50% 在底部。