我正在研究如何编写支持撤消的绘制程序,并且很可能看到命令模式是我想要的。但是,有些事情仍然让我失望,我希望有人可以提供一个简单的答案或确认。
基本上,如果我要体现撤消命令的能力,例如在屏幕上标记一个实心圆,这是否意味着我需要将圆覆盖的帧缓冲区基本上复制到这个命令对象中?我没有看到任何其他方法可以撤消可能的内容,例如,标记一堆随机像素颜色。
我听说过一种方法只是跟踪前进动作,当执行撤消时,你只需从步骤1开始向前绘制到撤消前的步骤,但如果你要这样做,这似乎是不可行的支持大型撤销堆栈。
也许解决方案介于每个15-20个动作的位图之间,并从最后一个“保存”转发开始。
在这种情况下,有人可以提供有关典型接受方法的任何见解,要么在命令中保存缓冲区矩形,重做每个操作前进,要么我完全错过了什么?
更新:很多好的回复。感谢大家。我正在考虑我正在阅读的内容,我将通过每N次操作保存缓冲区以及当用户发出撤消命令重做来自最近保存的缓冲区的所有命令来解决此问题。我可以将N调整到尽可能高的值,这并不会显着降低需要响应式撤消的用户体验(为了最大限度地减少内存使用),但我怀疑此时并不确定,我应该是能够逃脱在一个框架中执行相当多的动作,这样做不是太糟糕。希望这种方法可以让我快速确定是否转向另一个方向,而不是为之前的状态保存位图rects以用于需要它的操作。
答案 0 :(得分:10)
首先,要注意过度设计:如果您的应用程序不复杂且图像很小,您可能会发现“只是存储所有内容”,以便快速,便宜和可行。但假设情况并非如此:
对于每次撤消,从第1步向前重绘整个画布是不正确的。除非您的绘图程序非常简单,否则某些操作只需要太长时间。此外,可能不会调用无限的撤消缓冲区(并且可能非常耗费空间来存储)。
如果您的艺术计划很复杂,我实际上是从混合方法开始,以处理各种操作。每隔一段时间保存帧缓冲区(你建议的每15-20个命令似乎没问题;我可能从10开始并在我工作后调整)并从上次保存开始前进。但是不要让'每15个操作'变得僵硬,因为可能会有一些额外的经验法则使得似乎对用户更加流畅。
例如,一些耗时或棘手的反向操作总能创建一个新的保存点:
- 任何画布调整大小(裁剪等)
- 任何保存。 (“我刚刚保存”是一个非常可能让用户撤回的地方。)
- 任何非常耗时的操作都应该在之后创建一个新的保存点,而不是在操作之前;即它应该标记 next 操作以保存缓冲区以撤消。 (为什么?如果操作需要30秒,那么不希望堆栈中的每个撤消都需要额外的30秒以上。)
- 相反,任何具有容易执行的数学否定或自反转(如光阴性)的操作都不需要费心去保存帧缓冲区,并且不应计入下一次保存。
所有这些都不包括层次的问题;如果你的程序有它们,那么只保存那些改变的层就足够了。
绝对是我的最高优先级建议:无论使用何种方法,都应该始终为执行的最近操作保存帧缓冲区。 “哎呀,并不意味着”是撤销的最可能原因,所以你总是希望撤销一步做出回应。如果不是你要保留的那个,你可以在下一个命令执行后丢弃这个缓冲区。
您还需要考虑什么构成一个原子撤消操作。 (例如,使用单个画笔工具进行一次或多次操作的一组笔画?两者都有优点和缺点。)
答案 1 :(得分:3)
也许解决方案介于每个15-20个动作的位图之间,并从最后一个“保存”转发开始。
我会选择像这样的东西。无论如何,您必须在某个时刻绑定命令堆栈,因此如果用户清空它,您将需要一个起点。
当你到达界限时你可以变得聪明并保存缓冲区并将其用作保存点,因为你必须从堆栈中删除命令。本质上,您的保存点缓冲区是已删除操作的表示,因此当您从撤消堆栈中删除操作时,只需将它们写入该缓冲区即可。
答案 2 :(得分:2)
我听说过一种方法只是跟踪前进动作,当执行撤消时,您只需从步骤1开始,然后向前绘制到撤消前的步骤
这不是一个好主意。用户通常只撤消一些最近的操作,他们希望它很快,所以最好能够立即恢复,而不是从一开始就重做所有内容。
在这种情况下,有人可以提供有关典型接受方法的任何见解,要么在命令中保存缓冲区矩形,重做每个操作前进,要么我完全错过了什么?
您不必以相同的方式存储所有命令。根据操作类型,您可以使用一种或多种技术,例如:
绘图/绘画操作通常无法直接恢复,因此您别无选择,只能保存原始图像内容。但是,您可以通过仅存储已更改的部分图像而不是整个图像来节省空间。
反转颜色等一些操作本质上是可逆的,因此在这种情况下,您只需要在撤消堆栈上存储操作类型,并且可以在任一方向上重放操作。
答案 3 :(得分:1)
如果您可能不会绘制巨大的位图,那么您的方法似乎完全可以。
为了进一步简化,将整个图片写入tmp目录到磁盘上,看看它对用户来说是什么样的。
一开始不要过度设计 - 毫无疑问,还有其他问题需要解决。
答案 4 :(得分:0)
根据我的理解,用于实现撤消/重做系统的命令模式只记录堆栈中的操作,而不是这些操作的实际结果(因为这些操作将按顺序重新创建/删除)。我想你提到了这一点,但是你说你认为大型撤销堆栈是不可行的。你可以说得更详细点吗?我相信这是可能的。