网页版几何画板开发笔记(四)

由于各部分的关系有相互交织, 欲要删除, 需先创建. 而且在 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 类好呢?

现在我采用的是只使用一个点 Point 类, 来表示多种点的方法. 这样做的好处:
1. 代码显得(或真实?)少一点.
2. 类的继承层次少一层, 因不需要 FreePoint extends Point extends ObjBase ...
  由于性能担心, 减少继承层次比较好吗?

但的确几个函数需要根据点的 sub_type 不同的处理, 这样也就有分解为多个子类的动机, 到底
哪种方式好, 现在不能完全确定.

下面研究不同子类型的点的构造, 以及不同子类型点的坐标计算.

自由位置的点(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)
函数进行.

交点(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: 有空了推导并补上.

你可能感兴趣的:(网页版几何画板开发笔记(四))