我正在写一个画家程序,我想在其中实现基本操作的撤消和重做,如绘制线条,绘制椭圆等.
有没有好的方法呢?一项操作应包含哪些属性?
编辑:
其实我知道Command模式,但我想弄清楚的是,什么数据应该记录在Command对象中,以便完成撤消.
因此,在栅格应用程序中撤消/重做有点棘手.
首先,花25分钟看这个演讲(通过@chris).它可能会让你大吃一惊,而且速度很快.然后继续阅读.
最简单的方法是在每次操作之前简单地制作光栅画布的副本.将这些列表存储在撤消/重做缓冲区中,您可以愉快地前进和后退.(您可能还想存储工具状态).
这样做的缺点是它很大而且价格昂贵.有很多方法可以缓解这个问题.
首先,您可以平铺图像(无论如何),并使用copy-on-write.现在,通过撤消列表共享未修改的切片.
其次,而不是完整的栅格,记录重新创建效果所需的参数.只要你有一个合理的附近(在重建的时间)"之前"的图像你可以重建,这可以非常快.
第三,记录"擦除"效果所需的信息和信息.这有时比您触摸的所有图块的副本便宜(并且您触摸的所有图块的副本是实现此目的的一种方式).
您可以将撤消/重做堆栈与此混合.对于可重建的舞台,您需要光栅副本或先前的光栅副本以及一组可以重复以重新生成它的前向操作,或者将来的光栅副本和一组向后操作可以重复以重新生成它.
除此之外,您还可以将所述撤消信息推出内存并将其保存到磁盘.
在某些情况下,您还希望能够将多个步骤折叠为一个撤消/重做步骤.这可以让你为"最近"的操作保持细粒度的撤销/重做,同时仍然能够一直撤消到"你打开文件".
通常,您关心从当前步骤到达撤消/重做步骤(这意味着您只需要重做步骤的向前差异信息,向后撤销),但这可能会增加管理撤消/重做堆栈的复杂性(有时它是更容易存储两个).
还有一个问题是"您是否要在保存文件时保存撤消/重做操作数据"(是否可以序列化?).您想支持撤消/重做树还是仅支持线性历史记录?(即,如果你撤消3次,然后绘制一个像素,你应该能够"回到"原始历史,还是被像素绘制破坏?)
但是,实际上您从画布的简单光栅副本开始.这是必须测量任何其他实现的标准,因此对于单元测试目的,无论如何都需要"完整光栅"撤销/重做功能.添加"在撤消/重做上花费的总内存"设置和磁盘序列化(以及"撤消/重做时的总磁盘空间")和下一个折叠规则(因为这些是简单的步骤).然后平铺您的光栅图像并实现写入时复制图块,您的效率将达到90%.
之后,开始担心为某些工具进行优化的撤消/重做,以便可以对基于图块的栅格信息进行大规模打击.
现在,回到帖子开头的视频.再看一遍.使用暂停可在每个代码点停止视频.自己输入代码.试着理解你写的是什么.如果你不明白你写的是什么,那就去学习吧.你将成为一个更好的程序员,如果你在谈话结束时,你将编写一个更强大的撤销/重做更好的光栅绘画应用程序.
该实现的基础是文档数据的写入类型擦除系统的副本,使用类型擦除来允许值语义.它没有立即推广到文档模型,但即使你做的唯一事情就是在你的磁贴系统上使用类似的东西(use_count()==1导致你打破const写入时的复制),你将领先于游戏.