LibreCAD源码阅读:撤销和重做

本文详细介绍了LibreCAD中的撤销和重做功能,包括RS系列类的设计与使用,以及撤销操作的具体实现,如通过改变实体显示状态而非物理删除。同时讨论了这种实现方式在复杂场景下的局限性,如三维布尔运算和非原子实体修剪功能的缺失。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

撤销和重做

撤销和重做操作主要的两种实现方式:

  1. 记录每一步操作后的当前数据库,撤销时把保存的数据库恢复。这种方式比较消耗内存;
  2. 记录每一步操作前后的差异,撤销时对差异进行“减法”操作,重做时对差异进行“加法”操作。但是有时难以计算“差异”。

撤销类

RS_Undoable

RS_Undoable继承了标志类RS_Flags,添加了一些设置标志类中的撤销标志位的方法。并且预留了一个虚函数接口void undoStateChanged(bool undone);,用于设置撤销标志位变换之后的操作。

继承自RS_Undoable类是实体类RS_Entity,这表明每一个实体类都是支持Undo/Redo操作的。在RS_Entity类中,undoStateChanged()函数主要就是将实体的选中标志位设置为false,然后调用update()虚函数。RS_Entity::update()只提供了一个空实现,而派生类中重写了update()函数的类不多,例如基本的绘图实体(直线、圆、圆弧)都没有重写,所以一般实体在执行Undo操作时,就是将自身的选中标志位设置为false就完了。

void RS_Entity::undoStateChanged(bool undone)
{
    Q_UNUSED( undone);
    setSelected(false);	// 为什么不使用传入参数undone??
    update();
}

RS_UndoCycle

撤销循环类RS_UndoCycle相当于执行一个特征对应的节点RS_UndoCycle的定义如下,类中有一个RS_Undoable*的集合,这个集合里面存放的实际上是实体的指针。在实际操作过程中,每执行一个特征后,都会添加一个撤销循环对象,这里面记录了该特征实际产生了多少个可撤销的步骤。因为有些特征包含多个步骤,所以这里使用了集合来存放RS_Undoable。例如添加一条直线段,撤销循环中就只会有一个RS_Undoable;如果选中多个实体,然后删除掉它们,撤销循环中就会有多个RS_Undoable,每个RS_Undoable对应一个实体删除的记录。

class RS_UndoCycle {
public:
	RS_UndoCycle(/*RS2::UndoType type*/)=default;
    void addUndoable(RS_Undoable* u);
    void removeUndoable(RS_Undoable* u);
    size_t size(void);
    void changeUndoState();		// 各自执行undo操作
    // ...
private:
    std::set<RS_Undoable*> undoables;	// 实际存放的是实体指针
};

RS_Undo

RS_Undo类是与撤销操作相关的管理类,所有的撤销操作都是从这里开始执行的。RS_Undo类提供了undo()redo()两个接口,分别用来执行撤销和重做的操作。RS_Undo类内存储了一个撤销列表,撤销列表是vector类型,表明撤销列表有顺序的要求;里面的元素是RS_UndoCycle的共享指针,表明每一个撤销节点。提供了一个计数器undoPointer用于表示当前撤销节点的位置,默认情况下执行最后一个撤销节点。

class RS_Undo {
public:
	virtual ~RS_Undo() = default;

    bool undo();
    bool redo();

    int countUndoCycles();	// 可以undo的步骤的数量
    int countRedoCycles();	// 可以redo的步骤的数量
    bool hasUndoable();		// 是否可以执行undo

    void startUndoCycle();	// 开始添加新的Undo,将已经撤销过的节点删掉
    void addUndoable(RS_Undoable* u);
    virtual void endUndoCycle();

    virtual void removeUndoable(RS_Undoable* u) = 0;
	// ...
private:
	std::vector<std::shared_ptr<RS_UndoCycle>> undoList;	// 撤销列表
	int undoPointer = -1;	// 当前节点指针
    // ... 
};

RS_Undo类派生的类是RS_Document文档类。文档类在执行撤销特征的时候,会调用undo()函数。在undo()函数中,在获取当前撤销节点的同时,更新当前撤销节点指针,然后更新界面上的撤销和重做图标,最后调用当前撤销节点的执行撤销操作的函数。redo()函数的操作与该函数类似。

bool RS_Undo::undo() {
    // ...
    std::shared_ptr<RS_UndoCycle> uc = undoList[undoPointer--];		// 获取当前撤销节点,更新当前撤销节点指针
	setGUIButtons();		/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值