版权声明:本文章原创于 RamboPan ,未经允许,请勿转载。
自定义View时,使用 Matlab 模拟运动曲线
-
- 事件起因
- 数据推测
- 数据拟合求曲线
- 数据插值求曲线
事件起因
最近自己在做一个 自定View,有一个功能,就是用手指拖动图片时是实时移动图片,如果抬起时移动有一定速度,那么那个图片还会再滑行一段时间,这个功能也很常见,网上也有很多参考,我想试试按自己的想法去试试,虽然没做过,但第一感觉是这个功能应该不复杂吧(吃了自信的亏,找资料花太久了 …(:з」∠)_ )。
我知道的思路有通过移动 View (View上的图片不移动),给人的感觉是图片在动,但是 View 没有动。那我想的是直接移动 View 中图片的位置,View 上的图片是通过原图片使用 Matrix 进行缩放和位移的,那么我只需要在一小段时间内计算好接下来滑动的 Matrix 值,就可以达到这种滑动的效果,简单的说:
找一个运动曲线函数,然后把对应时间传入,得出移动多少距离。
数据大概心里知道要怎么样的,可以到时候试下,但是寻找运动曲线,没有头绪。问了一个大学时的学霸,然后又跟着他提示摸索了段时间,算是有点思路,决定记录下这个坑,如果你也碰到了需要自定义运动曲线或者有段数据,自己想求出曲线函数,但又不是专业弄数据的,就可以参考下这篇文章。
数据推测
确定了可行性之后,就可以考虑下我们需要计算什么,已知的是可以通过 VelocityTracker 计算抬起时的速度,毕竟我们也需要速度是否足够大来判断满足滑动条件。因为通过速度来计算每个间隔时间运动多少,感觉有点麻烦,所以我打算通过总路程,再按一定的比例分到每段时间需要移动多少。
假设我们抬起手指时速度为 x ,还继续这个速度的 3 倍距离,就是 3x ,举个例子,比如抬起时 1000 的速度,那我们还让这个图片滑动 3000 的距离,至于多少倍可以后面调试看多少效果最好。那既然知道了滑动的总距离,那就要计算多少次滑动结束,为了方便计算,这里取 10 。
那么我们现在需要来计算每份时间移动多少距离,虽然说不上每份具体取多少,但肯定不会是平均,因为平均的话,就是匀速运动了,这样不太符合逻辑。按我们物理的常识,你加速肯定是有力作用并且力还要增大,如果没有力作用速度需要慢慢变小。所以这里假设是先加速运动一半后减速运动,在总运动距离中各占一半。
通过 Excel 先把大致的曲线模拟出来,得到对应的 x 与 y 的一些对应关系,才能拿去求对应的函数关系。这里先贴出来模拟的数据再进行说明。
x 为运动的时间,0 为开始 1 为结束;
y 为运动的距离,0 为开始 1 为结束,
紫色表格部分为 x 的坐标点,代表时间;
蓝色表格部分为 y 的坐标点,代表移动的距离比值。
因为这里实际上想算的是一个比值,所以用的 0 - 1 的区间,如果你想用其他区间,也可以修改对应的值,我们最后肯定会拿这个比值和移动总距离 (3000) 相乘,所以就用 0 - 1 的范围。
当准备好一组 x,y 对应关系的数据后,就可以插入表格,使用带平滑曲线的散点图,把 x y 对应放进去,就可以得到右图中根据这些数据模拟出来的曲线,如果途中你有些数据不满意,打算调一下,直接修改 Excel 中对应的数据,表格也会随着变化。
因为我们是表达时间与距离的关系,斜率反映的是距离的变化快慢,也侧面反映了速度。
前半段斜率由小变大( y 相对 x 的增加从小变大);
后半段斜率由大变小( y 相对 x 的增加从大变小)。
符合我们刚开始的要求。这里说下,如果你想的是要一个直接运动从快到慢的曲线,就类似后半段的曲线,这里演示采用我这条曲线。
当你确定这个曲线是你想要的,那我们就需要求这个曲线的函数表达式了。这里需要使用 matlab 来进行操作。因为不是专业用这个,如果有不对的地方,希望大佬提出更好的方式予以改进。
我尝试出来得分有两种方式去算出对应函数曲线:数据拟合求曲线 与 数据插值求曲线。
数据拟合求曲线
数据不一定完全过你给的参考点,但是大致曲线是符合的。
话不多说,上代码和曲线图,中间插入注释说明。
(代码不需要输入 >> ,我这里是复制的结果,所以有 >> )
//首先是 输入 x 与 y 的值,数组的写法。
>> x=[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0];
>> y=[0,0.03,0.07,0.15,0.28,0.5,0.72,0.85,0.93,0.97,1.0];
//把对应数据显示出来,g. 代表绿色的点,后面就是指定点的大小。
>> plot(x,y,'g.','markersize',25);
//保留绘图
>> hold on;
// 这里是采用高次方进行拟合数据。
//p3 代表得出来的系数,你也可以取另外的变量名。
//4 代表采用4次方的函数(多项式)来进行拟合。
>> p3=polyfit(x,y,4);
//这里这种表达式代表 0 开始 ,步进为 0.1 ,直到 1 结束。
>> x2=0:0.1:1;
//通过 x2 使用 p3 这个函数把(不知道怎么称呼)算出对应 y2 的值。
>> y2=polyval(p3,x2);
//把x2 y2 数据显示出来,这里 b 代表蓝色。
>> plot(x2,y2,'b');
//得到 p3 的多项式参数
>> p3
p3 =
//后面来解释用法
-0.0000 -3.2673 4.9009 -0.6707 0.0185
可以看到这里的蓝色曲线就是算出来的曲线,绿色的点是之前我们标记的参考点,可以看到蓝色曲线是大致符合,但是如果你需要精确的话,可能不太适合,比如我们查看对应 0 与 0.1 的 y 值,可以发现 0.1 比 0 的 y 值还低,如果要作为运动轨迹反映,那就是 0.1 的时候还比 0 的时候倒回一点距离,这就不合理的吧。
但如果你对曲线要求不这么高的话,这里就可以拿到曲线了,这里对应解释下 p3 的值需要怎么用。p3 是显示的 5 个值,那么对应的函数为。
y = - 0.0000 * x4 - 3.2673 * x3 + 4.9009 * x2 - 0.6707 * x1 + 0.0185
y 就是代表每项式对应的常数,因为之前 p3=polyfit(x,y,4) 这里传入的是 4 ,那么就是四次方,加上常数项为五个。所以这里 y 为 5 个值,可以看到第一项实际上没有,如果 p3=polyfit(x,y,3) 应该也是一样的效果,继续输入尝试一下。
>> x3=0:0.1:1;
>> p3=polyfit(x,y,3);
>> y3=polyval(p3,x3);
//这里用的红色线
>> plot(x3,y3,'r');
>> p3
p3 =
-3.2673 4.9009 -0.6707 0.0185
可以看到 p3 这次只有 4 项,得到参数与上面相同,并且图中红色曲线覆盖蓝色线。当然你尝试时可以多使用几个次方数来确定是否哪条会更好,比如我再次用蓝色线表示,采用 10 次方的多项式来拟合数据。
过了给的所有数据点,圆滑度相比 4 次方时也好一点,接下来就要介绍另一种方式了。
数据插值求曲线
数据完全过给的数据点,曲线相对来说也会圆滑些。
>> x=[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0];
>> y=[0,0.03,0.07,0.15,0.28,0.5,0.72,0.85,0.93,0.97,1.0];
>> x1=0:0.1:1;
//这里是 1 ,不是 l .
//第四个参数有 4 个选项,不过看效果还是这个最好,所以就不介绍其他的了。
//有兴趣可以了解 nearest(临近点) linear(线性) spline(三次采样) cubic(立方)
>> y1=interp1(x,y,x1,'spline');
>> plot(x,y,'g.','markersize',25);
>> hold on;
>> plot(x1,y1,'r');
可以看到曲线是过了所有的点的,不算特别圆滑,但也还行,需要求出这个表达式。我暂时没有找到好点的方式,使用 spline 对 x y 进行求参数操作,再使用 p 以及 p.coefs 查看所有参数(就是首先确定曲线满足要求,再调用这个方法去寻找对应的多项式参数,而不是在 interp1 那步就能拿到对应参数进行输出了,绕了一点)
>> p=spline(x,y)
//查看p.
>> p
p =
form: 'pp'
//这里的断点代表 0 每个区间两侧的值。
breaks: [0 0.1000 0.2000 0.3000 0.4000 0.5000 0.6000 0.7000 0.8000 0.9000 1]
//一共有10个区间,加上上面的 11 个断点。
//就是类似 0 - 0.1 ; 0.1 - 0.2 …… 0.9 - 1.0;
//就是一个分段函数,一共 10 段,每段是 3次方的多项式,所以是 4个参数。
coefs: [10x4 double]
pieces: 10
order: 4
dim: 1
//输入 对应参数.coefs 这个可以查看每项对应的参数值。
//每行代表每个分段函数,第一行就是 0 - 0.1 的分段函数。
//每列就是代表一个3次方的多项式.
>> p.coefs
ans =
6.6369 -1.4911 0.3827 0
6.6369 0.5000 0.2836 0.0300
-3.1845 2.4911 0.5827 0.0700
16.1012 1.5357 0.9854 0.1500
-21.2202 6.3661 1.7756 0.2800
-21.2202 -0.0000 2.4122 0.5000
16.1012 -6.3661 1.7756 0.7200
-3.1845 -1.5357 0.9854 0.8500
6.6369 -2.4911 0.5827 0.9300
6.6369 -0.5000 0.2836 0.9700
其实就是算了一个 10 段的分段函数给你,然后你拿到对应的值就也可以计算出一个函数出来。这里拿 0.1 - 0.2 来举例。
y = 6.6369 * (x - 0.1)3 + 0.5 * (x - 0.1)2 + 0.2836 * (x - 0.1)1 + 0.03
为什么不拿 0 - 0.1 来举例,就是怕忘了前面的节点值。因为是分段函数,那么 x 需要先减去这个区间的前阈值,再进行乘方,如果直接乘方就不对了。
两种方式就介绍到此,所以需要采用哪种方式,可以参考自己的情况。虽然能用一个方程就能表示曲线肯定省事,但是如果是多段三次方函数来表示一个曲线,可能会更圆滑。(理论上感觉)
因为恰好想寻找一个运动曲线,才误入拟合数据,只是简单用了下 Matlab 中的一个小方面,摸索简单,如果不妥指出欢迎指出。