Отменить для программы рисования - PullRequest
15 голосов
/ 15 октября 2010

Я смотрю, как написать программу рисования, которая поддерживает отмену, и вижу, что, скорее всего, мне нужен шаблон команды. Что-то все еще ускользает от меня, и я надеюсь, что кто-то может дать простой ответ или подтверждение.

В принципе, если я хочу воплотить возможность отмены команды, например, нанесение сплошного круга на экран, означает ли это, что мне нужно по сути скопировать буфер кадра, который этот круг охватывает в память, в этот объект команды? Я не вижу другого способа отменить то, что может быть, например, печатью над множеством случайных цветов пикселей.

Я слышал, что один из подходов состоит в том, чтобы просто отслеживать действия вперед, и когда отмена выполняется, вы просто начинаете с шага 1 и переходите к шагу до отмены, но это кажется невозможным, если вы хотите поддержка большого стека отмены.

Возможно, решение - это нечто среднее между тем, что вы сохраняете растровое изображение каждые 15-20 действий и начинаете с последнего «сохранения» вперед.

Может ли кто-нибудь дать представление о том, что является типичным приемлемым подходом в этом случае, либо сохранить прямоугольники буфера в командах, переделать каждое действие вперед или что-то, что я вообще пропустил?

Обновление: множество хороших ответов. Спасибо всем. Из того, что я читаю, я думаю, что подойду к этому, сохраняя буфер каждые N действий, а когда пользователь запускает команду отмены, повторяю все команды из самого последнего сохраненного буфера. Я могу подкорректировать N до максимально возможного значения, которое заметно не ухудшает пользовательский опыт необходимости отзывчивой отмены (чтобы минимизировать использование памяти), но я подозреваю, не зная точно, на этом этапе, что мне следует может выполнить несколько действий в одном кадре, так что это не так уж плохо. Надеюсь, что этот подход позволит мне быстро определить, следует ли повернуть в другом направлении, и вместо этого пойти с сохранением растровых ректов для предыдущих состояний для действий, которые этого требуют.

Ответы [ 5 ]

10 голосов
/ 15 октября 2010

Во-первых, остерегайтесь чрезмерного дизайна: если ваше приложение не сложное, а изображения маленькие, вы можете найти «просто хранить все» быстро, дешево и выполнимо.Но предположим, что это не так:

Вы правы, что невозможно перерисовать весь холст с шага 1 вперед для каждой отмены;если ваша программа рисования не очень проста, некоторые операции просто занимают слишком много времени.Кроме того, бесконечный буфер отмены, вероятно, не требуется (и может занимать много места для хранения).

Если ваша художественная программа сложна, я бы фактически начал с гибридного подхода, чтобы иметь дело сРазнообразие операций.Периодически сохраняйте буфер кадров (каждые 15-20 команд, которые вы предлагаете, кажутся в порядке; я мог бы начать с 10 и настроить, как только он заработал) и перейти к последнему сохранению.Но не делайте «каждые 15 операций» жесткими, потому что вполне вероятно, что несколько дополнительных эмпирических правил сделают его кажущимся гораздо более плавным для пользователя.

Например,некоторые трудоемкие или хитрые операции всегда могут создать новую точку сохранения:
- любое изменение размера холста (обрезка и т. д.)
- любое сохранение.(«Я только что сохранил» - очень вероятное место, куда пользователь может отменить возврат.)
- Любая операция, которая требует очень много времени, должна создать новую точку сохранения после , а не до того, какоперация;то есть он должен пометить операцию next , чтобы сохранить буфер для отмены.(Почему? Если операция занимает 30 секунд, вы не хотите, чтобы каждая отмена в стеке впоследствии занимала дополнительные 30+ секунд.)
- И наоборот, любая операция, которая имеет легко выполняемое математическоеотрицательный или самоинвертирующий (например, фотонегативный) не должен беспокоиться о сохранении кадрового буфера и не должен учитываться при следующем сохранении.

Все это оставляет вопрос о слоях;если в вашей программе они есть, очевидно, достаточно сохранить только те слои, которые меняются.

Определенно мое предложение с наивысшим приоритетом: независимо от того, какой метод вы используете, вы всегда должны сохранять буфер кадров для самого последнего операция выполнена.«К сожалению, это не значит» - это наиболее вероятная причина отмены, так что вы всегда хотите, чтобы отменный шаг был отзывчивым.Вы можете отказаться от этого буфера после следующего выполнения команды, если он не принадлежит вам.

Вам также нужно будет рассмотреть, что составляет одну атомарную операцию отмены.(Например, является ли набор штрихов одним инструментом кисти одной операцией или несколькими? Оба имеют свои преимущества и недостатки.)

3 голосов
/ 15 октября 2010

Возможно, решение - это нечто среднее между тем, что вы сохраняете растровое изображение каждых 15-20 действий и начинаете с последнего «сохранения» вперед.

Я бы пошел с чем-то вроде этого. В любом случае вы должны ограничить свой стек команд в какой-то момент, поэтому вам понадобится начальная точка, если пользователь очистит его.

Вы можете стать умнее и сохранить буфер, когда достигнете границы, и использовать его в качестве точки сохранения, так как вам все равно придется отбрасывать команду из стека. По сути, ваш буфер точек сохранения является представлением пропущенных действий, поэтому, когда вы удаляете действия из стека отмены, вы просто записываете их в этот буфер.

2 голосов
/ 15 октября 2010

Я слышал, что один из подходов состоит в том, чтобы просто отслеживать действия вперед, и когда отмена выполняется, вы просто начинаете с шага 1 и переходите к шагу перед отменой

Это не очень хорошая идея.Пользователи обычно отменяют только несколько недавних действий и ожидают, что они будут быстрыми, поэтому лучше иметь возможность немедленно вернуться, чем повторить все с самого начала.

Может ли кто-нибудь дать какое-либо представление отипичный принятый подход в этом случае, либо сохранение прямоугольников буфера в командах, повторение каждого действия вперед или что-то, что я вообще пропустил?

Вам не нужно хранить все команды втак же.В зависимости от типа операции, вы можете использовать один или несколько методов, например:

  • Операции рисования / рисования обычно не могут быть отменены напрямую, поэтому у вас нет выбора, кроме как сохранитьисходное содержание изображения.Однако вы можете сэкономить место, сохранив только части изображения, которые изменились, а не все изображение.

  • Некоторые операции, такие как инвертирование цветов, по своей сути обратимы, поэтому в таких случаях вам нужно толькочтобы сохранить тип операции в стеке отмены, и вы можете воспроизвести операцию в любом направлении.

1 голос
/ 15 октября 2010

Если вы, вероятно, не будете рисовать гигантские растровые изображения, ваш подход кажется вполне подходящим.

Чтобы еще больше упростить, запишите целые картинки в каталог tmp на диск и посмотрите, как это будет выглядеть для пользователей.

Не переусердствуйте с самого начала - есть и другие вопросы, которые необходимо решить, без сомнения.

0 голосов
/ 15 октября 2010

Насколько я понимаю, шаблон команд для реализации систем отмен / повторов просто записывает действия в стеке, а не фактические результаты этих действий (поскольку они будут воссозданы / удалены последовательно). Я думаю, что вы намекали на это, но сказали, что считаете невозможным для большого стека отмены. Можете быть более конкретными? Я верю, что это возможно.

...