小白都能看懂的兩點連線曲線算法

有個需求要實現兩點間多條連線,效果以下:git

這裏的曲線能夠直接經過二階貝塞爾曲線來實現,曲線的控制點在兩點連線的垂直線上,從直覺上我第一個想到的解法是要用到三角函數,因而就開始瞭如下推理過程github

基礎知識

弧度 = 角度 * Math.PI / 180
角度 = 弧度 * 180 / Math.PI

Math.atan2(y, x): 返回從 點 (x,y) 到 x軸 的弧度
Math.cos(弧度): 直角三角形中,相鄰直角邊/斜邊 的值
Math.sin(弧度): 直角三角形中,相對直角邊/斜邊 的值

二階貝塞爾曲線:由兩個數據點(A 和 B),一個控制點(C)來描述曲線狀態
複製代碼

畫圖

推理邏輯

目標:假定控制點 CA B 連線中點的距離是固定的 10, 計算出紅色直角三角形的兩個直角邊的長度算法

  • 經過 Math.atan2(y, x) 計算 A(x0, y0) 點到 B(x1, y1) 點的連線與 X 軸的弧度,這裏須要用 y1-y0 獲得 y 值,x1-x0 獲得 x 值,它的做用如圖上的藍色線,能夠理解爲把 x y 軸平移到 A 點,經過計算獲得了 角1 的弧度 h
  • 經過三角形原理能夠推斷出 角1 === 角2
  • 計算 角2 的相對直角邊長度:L1 = Math.sin(h) * 10
  • 計算 角2 的相鄰直角邊長度:L2 = Math.cos(h) * 10
  • A B 連線中點座標的計算方式 mx = (x0+x1)/2 my = (y0+y1)/2
  • 控制點 C 的座標最終爲 cx = mx + L1 cy = mx - L2

最終算法

function getXY(a, b, distance) {
    const y = b.y - a.y;
    const x = b.x - a.x;
    const radian = Math.atan2(y, x);
    const L1 = Math.sin(radian) * distance;
    const L2 = Math.cos(radian) * distance;
    const mx = (a.x + b.x)/2;
    const my = (a.y + b.y)/2;
    const cx = mx + L1;
    const cy = mx - L2;
    return { x: cx, y: cy };
}

const A = { x: 10, y: 30 };
const B = { x: 300, y: 200 };
const C = getXY(A, B, 10);
複製代碼

Echarts算法

按本身的想法寫完以後,我在 Echarts 的源碼中中看到了這種曲線的另一種超簡單的算法apache

var x2 = (x0 + x1) / 2 - (y0 - y1) * 係數;
var y2 = (y0 + y1) / 2 - (x1 - x0) * 係數;
複製代碼

簡單的分析了一下,仍是經過三角形的方法,能夠判斷 C 的座標能夠和 A B 點座標差值產生關聯,在不一樣的係數下能夠獲得全部中垂線上的點座標bash

以上是從結果推算出來的結論,但它背後對應的公式原理,還不是很明白,但願能有掘友提供一些思路echarts

參考資料


I'm Gafish 原創文章,首發於 個人博客,內容若有錯誤,還望指正,謝謝您的閱讀。svg

相關文章
相關標籤/搜索