0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Processing (3.5.4) で surface.setSize() してから saveするとArrayIndexOutOfBoundsException: Coordinate out of bounds! となる問題

Last updated at Posted at 2021-10-02

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();
  }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?