网页版几何画板开发笔记(七) 关于点击测试

点击测试(hit_test), 用于查找(检测)指定鼠标点(x, y) 位置有什么对象, 以及如果存在
多个对象的情况下, 以什么样的策略进行处理. 现在先看如何检测到有哪个对象.

为检测而提供的函数当前为 GeoPad.hit_test(x, y, mode). 其中参数 x,y 表示鼠标位置,
mode 是一个对象或null, 表示一些选项.

作为 hit_test() 函数的返回结果, 也是一个对象, 而且由于有多重需求, 这一返回需要仔细
分析. 下面先列出对 hit_test() 函数有哪些需求:

1. 当鼠标移动的时候, 未按下, 将根据在当前鼠标可进行的操作(一般是选中), 或在何种对象
  上面, 需要给出 a)新的鼠标指针样式; b)可能的statusText,根据鼠标下对象和操作.
  此时需要检测到鼠标位置有哪个(或哪些)对象, 并判断有哪些可能的操作.

2. 当选中某一工具操作时, 如画点, 画线, 画圆等工具时, 区分为鼠标按下, 移动, 弹起几种
  不同事件, 对鼠标当前位置的对象进行检测. 将根据检测结果产生不同操作.

3. 新的需求, 如近期想做的移动标签(Label)功能, 也与 hit_test 有关, 具体如何做, 与原有
  功能如何结合是需要仔细考虑的.

如果以类来描述 hit_test 返回结果, 假设称之为 HitTestResult 类, 则伪代码可如下:

class HitTestResult {
  int length;  // 此次 hit_test 找到的对象数.
  operator [];  // 通过索引 0..length-1 可以访问到被点击中的第 i 个对象.
     // 相当于用对象模拟数组的访问方式, 原因是(过去)此对象是当一个数组用的.
  Point[] points;  // 所有被点击中的点的数组.
  Object[] hit_ret; // 通过此数组可访问到第 i 个对象的 hit_test() 返回值.
}

当前使用这样的(相对有点冗余有点乱)的结构是为了满足多个不同地方的需求, 也许将这个
返回的对象真的变成类以及重构, 可以减少点这种混乱的.

(*) HitTestResult 类中数据的产生.
  在函数 GeoPad.hit_test() 中, 遍历所有对象(不含 tmp 的, 这是一个问题), 对它们调用
  各自的 hit_test() 函数, 返回非空(非假)值的, 就认为被击中, 放入 [] 数组中; 返回值
  放入 hit_ret[] 数组中; 如果此对象是一个点(Point), 则还放入 points[] 数组中.

比如说, 如果一次点击测试, 鼠标在两条线l1,l2和一个点p1上面, 则这个对象看起来应如下:

{ length:3, 0:l1, 1:l2, 2:p1, points: [p1], hit_ret[true,true,true] ... }

的确是有点乱... 如果不包装为一个更清晰的对象, 似乎不好再改进或增加什么新功能了.

 

(*) hit_test() 的使用.

1. 在 def_mouse_move() ---  缺省的鼠标移动处理, 此时未选中任何工具, 或称是缺省
 SELECT 工具也可. 此时:
    var hit_objs = pad.hit_test(x, y, ...); // 执行点击测试.
    if (hit_objs.length > 0)  // 有至少一个对象在当前鼠标位置.
      pad.set_cursor(pointer) -- 鼠标切换为手形(表示可选择,移动等操作)

2. def_mouse_down() --- 缺省鼠标按下, 未选中任何工具时.
    var hit_objs = pad.hit_test(x, y, ...);  // 点击测试.
    if (hit_objs.length > 0)
      根据被点击中的对象执行... 选中,取消选中 操作
    else
      表示点到空白位置, 此时取消所有对象的选中.

TODO: 由于鼠标各种情况下的处理比较多样和复杂, 分解它们到合适的类中.
  然后优化 hit_test(), 为移动 Label 做准备.

 

你可能感兴趣的:(网页版几何画板开发笔记(七) 关于点击测试)