この記事は、Processing 3.5.4 の java言語における実装を忘備録として書いています。
現在制作中のWebアプリ”ColorModeII” プロジェクトで必要になったUndo/Redo機能をProcessing_Javaで実装してみた。
ツール系ソフトではマストな機能なので当たり前のようにサクッとできると思っていたが、しかし、この仕組みが初学者には案外ハードだったので簡単に解説。
(ちなみにColorModeの一作目はとうに廃盤になったiOSアプリだったんだぜ、、余談。)
https://www.facebook.com/Color-Mode-754921934535008/?view_public_for=754921934535008
さて、
アンドゥ・リドゥとは【初心者向け】
言わずもがな、デジタルで行った作業をやり直し・元に戻すための機能。
テキストや描画系のツールではマストな機能だ。
今回のアンドゥ・リドゥ処理の特徴
描画された図形の現状を消して一つ前の状態に戻したり、また再度元に戻す(図形復活)させたりする。
- アンドゥ・リドゥさせたいオブジェクトをクラスで用意
- 用意したオブジェクトをアンドゥ・リドゥ機能を持つクラス(*1)でリスト化するよう設定
- Mainのdraw関数では上記オブジェクトを生成した後、それを消したり呼び戻したりできる
今回コード実装で引用させてもらったとてもわかりやすい ”ザワプロ!” さんの参照リンクを張っておきます。(*1) “アンドゥ・リドゥ機能を持つクラス”の構造を理解したい方はぜひ拝見してください。マジ勉強になりますぜ!
Referenced Link : https://zawapro.com/?p=1065
サンプルコードでのアンドゥ・リドゥ使用例
制作中の”ColorModeII”の中で、ユーザーが書いた線を消したり戻したりするためにアンドゥ・リドゥ機能が欲しかった。このサンプルコードでは例題としてわかりやすくイメージ化した。
サンプルコードについて、
- まずアンドゥ・リドゥさせたいオブジェクト”Stamp”クラスを作成。
”Stamp”クラスは、色、ポジション(XY座標)、大きさのフィールドを持っています。 - ”ザワプロ!”さんからの引用コード(若干加筆あり)にあるアンドゥ・リドゥ機能のクラスに、先ほど用意したStampクラスを入れています。そしてアンドゥリスト・リドゥリストを別々のリストとして作ります。アンドゥ用リストとリドゥ用リスト。
- mainのdraw()関数では、画面上でマウスが押された時にStampオブジェクトがインスタンス化され呼び出されます。マウスを押すたびにStampオブジェクト(色付き四角)が生成され、アンドゥリストに追加されていきます。
- [command] + [z]でアンドゥ、また[command] + [x] でリドゥします。
サンプルコードの実行結果の解説
- サンプルコードをRunすると小さな画面がでてきます。
- 画面上でマウスをクリックすると色のついた四角がスタンプされます。
- キーコマンドでアンドゥ・リドゥができます。
[command] + [z] : アンドゥ
[command] + [x] : リドゥ
ログにはアンドゥ・リドゥを合わせた2つのリストのインデックスをそれぞれで返しています。
リストの中身が空になった場合は、”~ is empty”の表示を返します。
以下、サンプルコード【開発環境 : Processing 3.5.4】
//Test_UndoRedo_Example.pde
// Development environment by Processing 3.5.4
/*
2020 @author sharagu(c) <https://sharagublog.post-past.com>
Date 2020/12/08 (c) PostPast
keyController
[Mouse Click] : Stamp
[command] + [Z] : Undo
[command] + [X] : Redo
*/
import java.util.Collections;
import java.util.List;
private boolean[] pressed = new boolean[256];
Stamp st;
color c1, c2, c3;
StackList<Stamp> history = new StackList<Stamp>();
Stamp undo;
Stamp redo;
int bkg = 6;
int x, y;
void setup() {
size(400,400);
background(bkg);
rectMode(CENTER);
color(HSB);
}
void draw() {
x = mouseX;
y = mouseY;
//key [command] + [Z] : undo
if(pressed[157] && pressed[90]) {
println("\n" + "UNDO!!");
noLoop();
if(history.undoAccept()) {
undo = history.undo();
deleteStamp(undo);
} else {
println(":: RedoStack is full");
println("\n" + "-- UndoStack is empty" + "\n");
}
}
//key [command] + [X] : redo
if(pressed[157] && pressed[88]) {
println("\n" + "REDO!!");
noLoop();
if(history.redoAccept()) {
redo = history.redo();
restoreStamp(redo);
} else {
println(":: UndoStack is full");
println("\n" + "-- RedoStack is empty" + "\n");
}
}
}
void mousePressed() {
noLoop();
c1 = (int)random(255);
c2 = (int)random(255);
c3 = (int)random(255);
st = new Stamp(x, y, c1, c2, c3);
st.stamp();
history.add(st);
}
void mouseReleased() {
loop();
}
void keyPressed() {
pressed[keyCode] = true;
}
void keyReleased() {
pressed[keyCode] = false;
loop();
}
//ListManager.pde
void deleteStamp(Stamp undo) {
noLoop();
int ux = undo.x;
int uy = undo.y;
color uc1 = bkg;
color uc2 = bkg;
color uc3 = bkg;
Stamp uSt = new Stamp(ux, uy, uc1, uc2, uc3);
uSt.stamp();
}
void restoreStamp(Stamp redo) {
noLoop();
int ux = redo.x;
int uy = redo.y;
color uc1 = redo.col1;
color uc2 = redo.col2;
color uc3 = redo.col3;
Stamp reStamp = new Stamp(ux, uy, uc1, uc2, uc3);
reStamp.stamp();
}
//Stamp_class.pde
public class Stamp {
private color col1, col2, col3;
private int x, y;
private int w = 50;
private int h = 50;
Stamp(int x, int y, color c1, color c2, color c3) {
this.x = x;
this.y = y;
this.col1 = c1;
this.col2 = c2;
this.col3 = c3;
}
void stamp() {
noStroke();
fill(col1, col2, col3);
rect(x, y, w, h);
}
}
//StackList_class.pde
import java.util.Stack;
public class StackList<T> {
private final Stack<T> undoStack = new Stack<T>();
private final Stack<T> redoStack = new Stack<T>();
boolean undoAccept() {
return !undoStack.empty();
}
boolean redoAccept() {
return !redoStack.empty();
}
//UNDO
public T undo(){
T result = null;
if(!undoStack.empty()) {
result = undoStack.pop();
redoStack.push(result);
noLoop();
}
for(int i=0; i<redoStack.size(); i++){
println(">> RedoStackList Index: " + redoStack.indexOf(redoStack.get(i)));
}
return result;
}
//REDO
public T redo(){
T result = null;
if( !redoStack.empty() ){
result = redoStack.pop();
undoStack.push(result);
noLoop();
}
for(int i=0; i<undoStack.size(); i++){
println("<< UndoStackList Index: " + undoStack.indexOf(undoStack.get(i)));
}
return result;
}
public void add(T history){
undoStack.push(history);
redoStack.clear();
for(int i=0; i<undoStack.size(); i++) {
println("== UndoStackList Index: " + undoStack.indexOf(undoStack.get(i)));
}
println("\n");
}
}
以上、アンドゥ・リドゥサンプルコードでした。
ではでは
コメント