網頁版幾何畫板開發筆記(四)

因爲各部分的關係有相互交織, 欲要刪除, 需先建立. 並且在 js 中建立對象也是個有點小麻煩的事情. 算法

下面詳細研究幾種核心幾何對象的建立: 點,線,圓. 這裏先不涉及UI部分, 僅從純數據結構方面研究. 數據結構

點是最開始的對象, 如今以類 Point 來實現, 僞代碼可描述爲:
class Point extends ObjBase {
  double x, y;  // 點的座標, 不管自由點或其它點, 都最終要計算出此座標.
     // 其它類型的點, 還有別的一些字段. 稍後細緻分析.
  string type = 'point'; // Point 對象, 類型是 'point'
  string sub_type;  // 點的子類型, 當前有 freept, midpt, oopt, intpt, xfmpt 等.

  private Point(); // 通常不直接調用構造, 而是經過 Point.new_xxx() 方法.
  // 下面各類 new_xxx() 方法用於構造各類點.
  public static Point new_xy(double x, double y) // 構造 'freept' 子類型的點, 座標爲 x,y
  public static Point new_midpt(seg)  // 構造 seg 的中點, 子類型 'midpt'.
  public static Point new_oopt(path, t) // 構造 path 對象上參數位置爲 t 的點. 子類型 'oopt'
  public static Point new_intpt(path1,path2,isel) // 構造交點, 子類型 'intpt' 
  public static Point new_xformed(pt, xformer) // 構造點 pt 經  xformer 變換後的點, 子類型 'xfmpt' 
 
  public void update_geov();  // 更新幾何信息.
  public void draw(pad); // 繪製此點.
  public void hit_test(x, y); // 點擊測試.   
  // ... 其它函數稍後須要再研究...
} 函數

問題: 究竟是每種子類型的點一個類, 仍是用一個統一的 Point 類好呢? oop

如今我採用的是隻使用一個點 Point 類, 來表示多種點的方法. 這樣作的好處:
1. 代碼顯得(或真實?)少一點.
2. 類的繼承層次少一層, 因不須要 FreePoint extends Point extends ObjBase ...
  因爲性能擔憂, 減小繼承層次比較好嗎? 性能

但的確幾個函數須要根據點的 sub_type 不一樣的處理, 這樣也就有分解爲多個子類的動機, 到底
哪一種方式好, 如今不能徹底肯定. 測試

下面研究不一樣子類型的點的構造, 以及不一樣子類型點的座標計算. orm

自由位置的點(Point.FREE_POINT):
  函數 Point.new_xy(double x, double y) 用於構造自由位置點. x, y 表示其初始位置.
自由位置的點 sub_type=Point.FREE_POINT. 自由度量=2. 其它值不變或不須要.
  自由位置點座標在建立時給定, 後面可經過 move 操做改變, 其 update_geov() 不須要
計算其點位置. 對象

中點 (Point.MID_POINT):
  函數 Point.new_midpt(Line seg) 用於構造線段 seg 的中點. TODO: 因爲兩個點 A, B
就能夠產生中點, 提供爲兩個點實現的中點類型會不會比較好? 但幾何畫板倒是隻有線段中點
功能的(?爲何). 中點的 sub_type=Point.MID_POINT, 父對象是線段 seg, 自由度量=0,
建立後須要調用 update_geov() 根據線段幾何信息計算此中點的位置信息.
  具體算法: x = (p1.x + p2.x)/2; y = (p1.y + p2.y)/2. 其中 p1,p2 是線段的起點, 終點. 繼承

對象上的點(Point.POINT_ON_OBJ):
  函數 Point.new_oopt(path, t) 用於構造位於路徑path 上參數位置爲 t 的點. 路徑, 當前
包括線(line)和圓(circle). 參數 t 是和路徑有關的一個double 型的參數. 例如對於線段, t
取值範圍爲 (0, 1), 射線爲 (0, 1) (1, +無窮); 對於圓爲 [0, 2*PI).
  對象上點的 sub_type=Point.POINT_ON_OBJ, 父對象是路徑對象path, 自由度量=1.
  在 update_geov() 中要計算出此點的座標, 這計算委託給路徑對象的 calc_point_on(t)
函數進行. ci

交點(Point.INTER_POINT):
  函數 Point.new_intpt(path1, path2, isel) 用於構造路徑 path1,path2的交點, 若是有兩個
交點, 則參數isel取值1,2 用於指定選擇哪個交點. 交點的計算略有複雜, 稍後有空單獨筆記.
  交點 sub_type=Point.INTER_POINT, 父對象是 path1,path2, 自由度量=0.

幾何變換的新點(Point.XFORM_POINT):
  函數 Point.new_xformed(pt, xformer) 用於構造點 pt 通過 xformer 變換後產生的新點.
關於幾何變換, 之後在單獨研究幾何變換的實現時候再看. 如今先認爲點通過變換後是另外一個
點對象便可.
  變換後的點 sub_type=Point.XFORM_POINT.

上述幾種點的幾何信息更新計算的調用都實如今函數 Point.update_geov() 中, 按照子類型
分別調用不一樣的子方法.

 下面順便研究一下幾種新的點的位置計算:

 1. 已知點 p1,p2, 求p1->p2方向上的延長線上的點, 並知足 p1p2=p2p.
  也即便得點 p2 成爲 p1,p 的中點.

  則位置爲: p = p2 + (p2 - p1)

 2. 已知點 p1, p2, p3, 求點 p 使得 p3p 平行且等於 p1p2. 也即p3p 線段等於p1p2線段長.

  則 p = p3 + (p2 - p1).    能夠將 1 看作 p3=p2 的特例.

3. 已知點 p1, p2, 求點 p 使得 p2p垂直且等於p1p2.

  則 p = p2 + (p2 - p1)*i 或 p = p2 - (p2 - p1)*i.  這裏 i 表示虛數 i.
  其中選擇 + 或 - 要根據座標系和旋轉方向. 按照如今屏幕座標系(y 軸向下), 以及逆時針旋轉,
則應選擇負號(-).

4. 已知點 p1 p2, p3, 求點 p 使得 p3p垂直且等於p1p2. 與 3 很是相似:

  則 p = p3 +- (p2 - p1)*i

5. 已知點 p1, p2, 求點 p 使得 p1, p2, p 構成等邊三角形. 至關於向量旋轉 60°角

  則60°單位旋轉向量的複數表示爲 R= cos(60°)+i*sin(60°) = 1/2 + i*sqrt(3)/2
  點 p = (p2 - p1)*R + p1 或 p = (p2 - p1)*R* + p1. 根據方向選擇 R或R*.

6. 已知點p1, p2 構成的線 l1, 點 p3 是線外一點, 求點 p 是 p3 到 p1, p2 的垂足.

 公式這裏有: http://hi.baidu.com/sofiner/item/3cac1a972503e79c581461af
 有空也能夠本身推導一下.

7. 已知點 p1, p2, p3 構成角, 求p2的角平分線與 p1,p3 的交點, 可稱之爲角平分線點.

 TODO: 有空了推導並補上.

相關文章
相關標籤/搜索