saveがどこで確保されたバッファを使っているかが見当ついてないので,かなり大雑把な推測が含まれている.問題の切り分け用メモ
問題
エンターを押すたびにランダムな要素数の円を描画してPNGに保存するプログラムを作ろうとして以下のようにすると
save()
の時点でArrayIndexOutOfBoundsException: Coordinate out of bounds!
というエラーが出る.
プログラムではdraw()
のたびにsurface.setSize()
でWindowサイズを変えているが,これによりおそらくsave
のため確保されたバッファを超える(あるいは異なる)データを保存しようとしているために起こっている.
int serial_id = 0;
int nofRow = 1;
int nofCol = 1;
final int diameter = 50;
final int interval = diameter / 2;
final int margin = diameter / 2;
void setup() {
nofRow = 1;
nofCol = 1;
surface.setResizable(true);
surface.setSize(
diameter * nofCol + interval * (nofCol - 1) + margin * 2,
diameter * nofRow + interval * (nofRow - 1) + margin * 2
);
noLoop();
ellipseMode(CENTER);
colorMode(RGB, 255);
background(255, 255, 255);
noStroke();
}
void draw() {
nofRow = round(random(1, 10));
nofCol = round(random(1, 10));
println("row: " + nofRow + "\t col: " + nofCol);
surface.setSize(
diameter * nofCol + interval * (nofCol - 1) + margin * 2,
diameter * nofRow + interval * (nofRow - 1) + margin * 2
);
drawCircles();
save("img_" + (serial_id++) + ".png");
}
void keyPressed() {
if (key == ENTER) {
redraw();
} else if (key == 'S') {
} else if (key == 'E') {
exit();
}
}
void drawCircles() {
for (int row = 0; row < nofRow; row++) {
for (int col = 0; col < nofCol; col++) {
int val = round(random(0, 255));
//print(im + ",");
fill(0, (int)(255 - val) * 3, (int)val);
ellipse(
margin + diameter / 2 + (diameter + interval) * (col % nofCol),
margin + diameter / 2 + (diameter + interval) * (row % nofRow),
diameter, diameter
);
}
}
}
解決方法その1
save()
をdraw()
の先頭に持ってくる.というかsurface.setSize()
の前にもってくる.これで前回のdraw()
の内容を保存する.最もシンプル.
void draw() {
nofRow = round(random(1, 10));
nofCol = round(random(1, 10));
println("row: " + nofRow + "\t col: " + nofCol);
+
+ save("img_" + (serial_id++) + ".png");
surface.setSize(
diameter * nofCol + interval * (nofCol - 1) + margin * 2,
diameter * nofRow + interval * (nofRow - 1) + margin * 2
);
drawCircles();
-
- save("img_" + (serial_id++) + ".png");
}
解決方法その2
S
を押したときに保存するようにする.save()
用のバッファはおそらくsetup()
やdraw()
が終わった後に用意・決定されている?ただし
redraw()
の後にすぐ,あるいはdelay()
をしてからsave()
してもエラー.(解決方法その3)
なのでENTER
おしてから十分待ってS
を押せばそのころにはいい感じになってる.
void draw() {
...
drawCircles();
-
- save("img_" + (serial_id++) + ".png");
}
void keyPressed() {
if (key == ENTER) {
redraw();
} else if (key == 'S') {
+ save("img_" + (serial_id++) + ".png");
} else if (key == 'E') {
exit();
}
}
解決方法その3(WIP)
draw()
のなかでsurface.setSize()
しない.たぶんsurface.setSize()
はそもそもdraw()
の中でマイフレーム呼ぶものじゃなく,イベント時に設定する.redraw()
とkeyPressed()
は別スレッドだから?かredraw()
の後にちょっと待つ必要あり.2sec待ってるのは画像確認用(のつもりだったけど,deleyでdrawもフリーズしちゃうしやっぱ別の問題?)
redraw() はdraw()
が実行されるためのフラグを立てるだけらしい.一定周期(frameRate)でdraw()
(の親?)が呼ばれていてそこでフラグが立ってれば描画するという事なんだろうか.Loop()
とnoLoop()
はそれらのフラグを常に立てるか否かを設定している?
まあ少なくともredraw()
してちゃんとdraw()
が実行し終わったという確証があったのちsaveをすればよいので,適当なフラグを作って待ってみたらうまくいったまだたまにエラー吐く……(WIP).
+ boolean isDrawn = false;
void draw() {
- nofRow = round(random(1, 10));
- nofCol = round(random(1, 10));
-
- println("row: " + nofRow + "\t col: " + nofCol);
-
- surface.setSize(
- diameter * nofCol + interval * (nofCol - 1) + margin * 2,
- diameter * nofRow + interval * (nofRow - 1) + margin * 2
- );
drawCircles();
+ isDrawn = true;
-
- save("img_" + (serial_id++) + ".png");
}
void keyPressed() {
if (key == ENTER) {
+ nofRow = round(random(1, 10));
+ nofCol = round(random(1, 10));
+
+ println("row: " + nofRow + "\t col: " + nofCol);
+ surface.setSize(
+ diameter * nofCol + interval * (nofCol - 1) + margin * 2,
+ diameter * nofRow + interval * (nofRow - 1) + margin * 2
+ );
redraw();
+ while(!isDrawn)delay(100);
+ isDrawn = false;
+ save("img_" + (serial_id++) + ".png");
} else if (key == 'S') {
} else if (key == 'E') {
exit();
}
}