因为各部分的关系有相互交织, 欲要删除, 需先建立. 并且在 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: 有空了推导并补上.