Hatena::ブログ(Diary)

Imaginable Reality

2014-07-16

Processingでスクリーン座標系とローカル座標系の相互変換を行う

translate()やrotate()などの幾何変換によって移動・変形された座標系(ローカル座標系)と、スクリーンの左上隅を原点とするスクリーン座標系の相互変換を行う方法について説明します。


スクリーン座標系からローカル座標系への変換

スクリーン座標系における座標値を、現在の座標系での座標値に変換します。

PVector screenToLocal(float x, float y) {
  PVector in = new PVector(x, y);
  PVector out = new PVector();
  PMatrix2D current_matrix = new PMatrix2D();
  getMatrix(current_matrix);  
  current_matrix.invert();
  current_matrix.mult(in, out);
  return out;
}

ローカル座標系からスクリーン座標系への変換

現在の座標系における座標値を、スクリーン座標系での座標値に変換します。

PVector localToScreen(float x, float y) {
  PVector in = new PVector(x, y);
  PVector out = new PVector();
  PMatrix2D current_matrix = new PMatrix2D();
  getMatrix(current_matrix);  
  current_matrix.mult(in, out);
  return out;
}

使いどころ

このような変換は、移動や回転をしている図形をマウスカーソルで操作するような場合に有用です。以下に具体例を示します。

f:id:kougaku-navi:20140716212819g:image

マウスカーソルの当り判定は長方形のローカル座標上で計算したほうが楽なので、マウスカーソルの座標値(mouseX, mouseY)をいったんローカル座標系での値(mx, my)に変換した後、長方形の中にいるかどうかをif文で判定しています。ちなみにmouseXとmouseYは幾何変換の影響を受けず、つねにスクリーン座標系での値になるようです。

円もまた長方形と同じく回転と平行移動の影響を受けるローカル座標系の中で描かれています。この円の中心とスクリーン座標上の原点(0, 0)との間に線を引くために、ローカルな座標値 (local_x, local_y)をいったんスクリーン座標(screen_x, screen_y)に変換しています。

float rot_angle = 0;
float rect_width = 200;
float rect_height = 100;

void setup() {
  size(400, 300);
}

void draw() {
  background(200);  

  pushMatrix();
    translate(width/2, height/2);
    rotate(rot_angle);
    translate(-rect_width/2, -rect_height/2);

    PVector local_mouse = screenToLocal(mouseX, mouseY);  
    float mx = local_mouse.x;
    float my = local_mouse.y;

    if ( 0<=mx && mx<=rect_width && 0<=my && my<=rect_height ) {
      fill(255, 0, 0);
    } else {
      fill(0, 255, 0);
    }
    rect(0, 0, rect_width, rect_height);
    
    float local_x = 30;
    float local_y = 50;
    fill(255);
    ellipse( local_x, local_y, 30, 30 );

    PVector screen_pos = localToScreen( local_x, local_y );
    float screen_x = screen_pos.x;
    float screen_y = screen_pos.y;   
 popMatrix();
  
  line(0, 0, screen_x, screen_y );
  rot_angle += 0.01;
}

PVector screenToLocal(float x, float y) {
  PVector in = new PVector(x, y);
  PVector out = new PVector();
  PMatrix2D current_matrix = new PMatrix2D();
  getMatrix(current_matrix);  
  current_matrix.invert();
  current_matrix.mult(in, out);
  return out;
}

PVector localToScreen(float x, float y) {
  PVector in = new PVector(x, y);
  PVector out = new PVector();
  PMatrix2D current_matrix = new PMatrix2D();
  getMatrix(current_matrix);  
  current_matrix.mult(in, out);
  return out;
}