版權聲明:本文章原創于 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 中的一個小方面,摸索簡單,如果不妥指出歡迎指出。