Отменить для программы рисования

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

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

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

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

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

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

Ответы

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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