二維幾何圖形自由變換的思路(平移、縮放、旋轉)

目錄

圖形自由變換

圖形的存儲方式

鼠標交互

圖形拓撲算法

總結


       

常見的二維幾何圖形包括點、線、面三種類型,而某些的地圖標準規範中還區分多點、多線、多面、環等。本文,主要講述簡單的幾何圖形的自由變換(平移、縮放、旋轉)的核心思路。至於複雜的幾何圖形則可以通過簡單的幾何圖形組裝變換得到。後面的文章會講述如何拖拽增加修改頂點和編寫輔助線的核心思路。

圖形自由變換

       圖形自由變換是指可以通過自由旋轉、比例、傾斜、扭曲、透視和變形來變換對象。例如在photoshop和illustrator中的自由變換功能。

                               

圖形的存儲方式

        在計算機中幾何圖形的點的存儲方式以x,y座標的形式存儲,座標系統可以是屏幕的像素座標系統,可以是地理座標系統,也可以是自定義的單位的座標系統。但存儲方式必須能夠表示二維兩個方向的值。例如:point: Array<2>或者point:Object<x, y>

        而比點圖形稍微複雜的線圖形和麪圖形,則是以點爲單位存儲的集合圖形,line: Array<point> 和 polygon: Array<point>

     

鼠標交互

      自由變換涉及的鼠標交互比較簡單,大致爲如下:

  1. 鼠標點擊圖形,標記開始;
  2. 鼠標拖拽,記錄偏移量
  3. 鼠標鬆開,標記結束

     我們知道,鼠標的每一個動作,都能記錄當前的鼠標在屏幕上的一個狀態,包括屏幕像素座標和鼠標操作事件。在鼠標與圖形的交互中,所使用的東西,莫非是 鼠標的左右鍵點擊和鬆開,以及一個 delta值

delta = f(deltaX, deltaY)。可以理解爲delta是與屏幕的橫軸X和縱軸Y的差值有F的關聯關係。

圖形拓撲算法

  • 圖形的平移

圖形的平移比較簡單,就是給每一個點加上delta。公式如下: 新的座標 += delta

const newCoordinate = event.coordinate;
const deltaX = newCoordinate[0] - this.lastCoordinate_[0]; 
const deltaY = newCoordinate[1] - this.lastCoordinate_[1];

const graphics = this.graphics_ // 圖形對象

graphics.forEach(function(graphic) {

    const geom = graphic.getGeometry(); // 圖形的座標數據
    const coordinates = geom.getCoordinates()

    translate(coordinates , 0, coordinates.length, 2, deltaX, deltaY, coordinates);

    graphic.setGeometry(geom);

}); 


function translate(coordinates, offset, end, stride, deltaX, deltaY, opt_dest) {
  const dest = opt_dest ? opt_dest : [];
  let i = 0;
  for (let j = offset; j < end; j += stride) {
    dest[i++] = coordinates[j] + deltaX;
    dest[i++] = coordinates[j + 1] + deltaY;
    for (let k = j + 2; k < j + stride; ++k) {
      dest[i++] = coordinates[k];
    }
  }
  if (opt_dest && dest.length != i) {
    dest.length = i;
  }
  return dest;
}
  • 圖形的縮放

圖形的縮放比圖形的平移稍微複雜一點點,需要先定義如下兩個參數:

1. 根據鼠標交互得到delta,定義縮放的比例與delta的關係;

2. 定義圖形比例縮放的anchor(錨點),通常取圖形的幾何中心點,錨點不同會影響縮放後的座標的結果。比如有等比例縮放,向上向下縮放等。

 


/**
 * 縮放座標
 * @param {Array<number>} coordinates 座標信息.
 * @param {number} offset 座標數組的起點索引,默認是0.
 * @param {number} end 座標數組的起點索引的終點索引.
 * @param {number} stride 座標的維度,二維圖形是2.
 * @param {number} sx 相對於x軸的縮放比例.
 * @param {number} sy 相對於y軸的縮放比例.
 * @param {Array<number>} anchor 縮放的錨點.
 * @param {Array<number>=} opt_dest 目標數組中.
 * @return {Array<number>} 經過變形的座標.
 */
function scale(coordinates, offset, end, stride, sx, sy, anchor, opt_dest) {
  const dest = opt_dest ? opt_dest : [];
  const anchorX = anchor[0];
  const anchorY = anchor[1];
  let i = 0;
  for (let j = offset; j < end; j += stride) {
    const deltaX = coordinates[j] - anchorX;
    const deltaY = coordinates[j + 1] - anchorY;
    dest[i++] = anchorX + sx * deltaX;
    dest[i++] = anchorY + sy * deltaY;
    for (let k = j + 2; k < j + stride; ++k) {
      dest[i++] = coordinates[k];
    }
  }
  if (opt_dest && dest.length != i) {
    dest.length = i;
  }
  return dest;
}

/** example **/
const coordinates= this.coordinates_;
let anchor = getCenter(coordinates); // 獲取幾何中心點

const stride = 2;
const sx = 2;
const sy = 2;

scale(coordinates, 0, coordinates.length,
        stride, sx, sy, anchor, coordinates);
  • 圖形的旋轉

幾何圖形的旋轉又比圖形的縮放複雜那麼一丟丟,仍然需要先定義如下兩個參數:

1. 根據鼠標交互得到delta,定義旋轉角度的比例與delta的關係;

2. 定義圖形比例縮放的anchor(錨點),通常取圖形的幾何中心點,錨點不同會影響旋轉後的座標的結果。

經過旋轉後,新的座標與舊座標的關係的公式如下:CXnew = CXanthor +\left ( CXold - CXanthor\right )\ast \cos \alpha - \left ( CXold - CXanthor\right )\ast \sin \alphaCYnew = CYanthor +\left ( CYold - CYanthor\right )\ast \cos \alpha + \left ( CYold - CYanthor\right )\ast \sin \alpha

/**
 * @param {Array<number>} coordinates 座標數組.
 * @param {number} offset 座標數組起始的座標的偏移.
 * @param {number} end 標數組終點的座標的索引.
 * @param {number} stride 座標的維度,二維的爲2.
 * @param {number} angle 旋轉角度.
 * @param {Array<number>} anchor 旋轉的錨點.
 * @param {Array<number>=} opt_dest 目標數組.
 * @return {Array<number>} 已經轉換的座標.
 */
export function rotate(coordinates, offset, end, stride, angle, anchor, opt_dest) {
  const dest = opt_dest ? opt_dest : [];
  const cos = Math.cos(angle);
  const sin = Math.sin(angle);
  const anchorX = anchor[0];
  const anchorY = anchor[1];
  let i = 0;
  for (let j = offset; j < end; j += stride) {
    const deltaX = coordinates[j] - anchorX;
    const deltaY = coordinates[j + 1] - anchorY;
    dest[i++] = anchorX + deltaX * cos - deltaY * sin;
    dest[i++] = anchorY + deltaX * sin + deltaY * cos;
    for (let k = j + 2; k < j + stride; ++k) {
      dest[i++] = coordinates[k];
    }
  }
  if (opt_dest && dest.length != i) {
    dest.length = i;
  }
  return dest;
}

/**example**/
const coordinates = this.coordinates_
const stride =2
const angle = 45 * Math.PI / 180
const anchor = getCenter(coordinates)
rotate(coordinates, 0, coordinates.length,
     stride, angle, anchor, coordinates);

總結

幾何圖形的自由變換的核心思路比較簡單,上述描述的情況都是基於二維幾何圖形的,但是也適合三維幾何圖形的情況。後面的文章會講述幾何圖形的修改,變形以及貝塞爾曲線實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章