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

关于删除对象 (delete_objs) 和对象之间的内部结构有关, 因此先笔记对象之间的结构.

  几何对象的起点是点(Point)对象. 通过连接两个点构成线(Line), 以一个点为圆心, 圆上另一
点可以做圆(Circle), 线段(Segment)可以取中点(Midpoint), 两线或圆(Path)可以取交点
(Intersection) 等等. 详细的在研究各对象时候再仔细分析. 那么对象和对象之间有什么关系
呢?

对象之间关系在程序中总结和使用的有三种:
1. 一个对象可以有 0-* 个子对象. 这里 * 表示多个的意思.
2. 一个对象可以有 0-* 个父对象.
3. 一个对象可以有 0-* 个移动相关的控制对象.

关于第3种关系对象, 在研究Move 的时候用到, 现在暂时略. 如下分析父子对象关系与实现.

一个对象可以有多个子对象, 举例子如下: 
  两个点A, B可连接构成一条线l, 则 l 是A的子对象(l 也是B的子对象);
  还可以以A 为圆心, B 为圆上一点做圆c, 则 c 是A,B 的子对象.

这个例子中, A有 l,c 两个子对象; B 也有 l,c 两个子对象; l 有 A,B 两个父对象; c 也有 A,B
两个父对象.

一个对象的父对象的数量理论上没有限制, 例如一个多边形, 每个顶点都是多边形的父对象,
而多边形的边数可以很多很多.

一个对象的子对象的数量理论上也没有限制, 例如一个点, 可以过该点做无数条直线, 而这些
直线都是这个点的子对象.

存在的约束关系是, 如果 x 是 y 的父对象, 则 y 是 x 的子对象.
TODO: 当前建立这种父子对象关系的代码似乎不太严谨, 两种关系不太对称, 忘记了当时为什么
两种关系没有同步建立的原因了. 建议好好思考一下, 如何同步建立和维护这两种关系.
TODO: 由于这两种关系存在约束关系, 可以考虑实现 self_check() 方法约束的验证.

根据以上分析, 在几何对象基类 ObjBase 中实现父对象关系的数据结构和相关函数如下:
数据: protected Object[] _parent_objs -- 用于存放此对象的所有父对象. 其可能为 null 或 [].
使用 _ 做名字开头, 意思是一般不直接使用/访问该数据. 子类对象在构造的时候, 通常
设置自己的父对象数据 _parent_objs.

public Object[] get_parent_objs() -- 得到此对象的父对象集合, ObjBase 基类的缺省实现是返回
this._parent_objs, (这样设计是使得)子类可以重载此实现.

举例如下, 通过两个点构造一个新的线段(Segment)时设置父对象:
public static Line Line.new_seg(Point p1, Point p2) {
  this._parent_objs = [p1,p2]; // 设置点p1,p2 为此线段对象的父对象. 
  ...... 其它代码略.
}

注意: 此时只是子对象知道父对象(_parent_objs), 也即建立了单向的关系. 此时不满足
约束关系. 当前程序的实现是在 append_obj() 时才建立另一方向父->子的关系, 所以
问题是, 两种关系未同时建立, 而且如果子对象未加入到链表或不需要加入到链表, 则这种
关系也就未完整建立. 思考: 那怎么做比较好呢?

父关系的使用(已知的可能地方):
1. 一个对象的移动控制对象集合(move ctrl), 缺省是这个对象的父对象集合.
2. 子对象关系是通过父对象关系反向建立的(当前在 append_obj() 中实现)
3. 对象属性对话框中, 会显示出此对象的父对象和子对象.
4. (几何画板中)Alt+↑选中当前对象的父对象, Alt+↓选中当前对象的子对象.
  我们不一定实现此功能, 但可适当了解此功能.

====

下面继续笔记子对象. 子对象关系的建立当前在 append_obj() 函数中实现.

public void GeoPad.append_obj(ObjBase obj) {
  for (var i = 0; i < par_objs.length; ++i)
    par_objs[i].add_child(obj);  // 告知每个父对象, 它增加了一个子对象.
  ... 其它代码略 ...
}

子对象关系的消除当前在 delete_obj() 函数中实现:
public void GeoPad.delete_obj(obj) {
  for (var i = 0; i < par_objs.length; ++i)
    par_objs[i].remove_child(obj); // 通知每个父对象, 这个子对象被删除了.
}

从更精细的代码任务分解角度来说, append_obj(), delete_obj() 都执行了多种任务, 从而
把事情弄混乱了, 如果分解为几个小任务, 如 link/unlink chain, add/remove child, 等等
可能更清晰一些?

子对象关系的使用:
1. 在移动操作之后, 要更新所有被移动对象的子对象(及子子对象)的几何数据.
2. 删除对象: 同时删除所有子及子子对象.
3. 在某些查询中, 通过限定查询某个对象的子对象, 可以大大减少查询范围, 从而提高查询速度.
4. 如父对象关系中, Alt+↓可选择子对象, 对象属性对话框中可列出子对象.

移动, 删除操作对这种关系的具体使用, 以后笔记.

子对象的数据结构和相关函数:
private? Object[] ObjBase._child_objs -- 表示此对象的所有子对象. 缺省为 undefined.

public Object[] ObjBase.get_child_objs() -- 返回此对象的所有子对象. 缺省实现为返回 this._child_objs.
  尽管子对象可以重载, 不过由于需要维护 _child_objs 集合, 比较麻烦所以不重载为好.
void ObjBase.add_child(ObjBase child) -- 添加指定子对象. 如果存在则不重复添加.
void ObjBase.remove_child(ObjBase child) -- 删除指定子对象. 不存在则返回 -1. (一般忽略)

这里的 add_child(), remove_child() 函数, 是在 GeoPad.append_obj(), delete_obj() 中调用的.

 

转载于:https://my.oschina.net/u/232554/blog/173381

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值