見出し画像

OpenGLで矩形選択

画像編集するアプリケーションでは、画像の特定の部分を選んで何かしたい場合がある。たとえば文章の画像があって、2行目と3行目を黒塗りにしたい、など。この場合には、マウスで2行目と3行目全体を含めるように矩形(長方形)を描いてその中を黒く塗りつぶせばいい。この記事には、OpenGLを使ってその矩形選択した領域を画面上に示すコードのメモを残す。


矩形領域は、ウィンドウ内の2点をマウスで指示することで決める。その2点を結ぶ線分を対角線として、辺は画面水平垂直方向とするように矩形を決める。

マウスで指示した点の位置はMouseEventArgs.Locationで取れる。

// C++/CLI
ref class CustomControl : System::Windows::Forms::UserControl
{
protected:
  virtual void OnMouseMove( 
        System::Windows::Forms::MouseEventArgs^ e ) override
  {
    System::Drawing::Point p = e->Location;
  }
};

MouseEventArgs.Locationは、ウィンドウの左上隅を原点として画面の水平右方向を$${x}$$軸、画面の垂直下方向を$${y}$$軸とする2次元座標系上の点として得られる。マウスで選んだ2点を$${P_1}$$、$${P_2}$$として下の図のようになる。

マウスで指定した位置P1とP2


一方でOpenGLは最終的に、

$$
-1 \leqq x \leqq 1, -1 \leqq y \leqq 1, -1 \leqq z \leqq 1
$$

の領域内にあるものを、視線方向を$${z}$$軸に合わせた状態で画面に描き出す。

ウィンドウの幅を$${w}$$、高さを$${h}$$とすると、左上隅を原点とする$${w \times h}$$の長方形を、左下隅を原点とする$${2 \times 2}$$の正方形にぴったり重なるように変形させれば、マウスでウィンドウ上に指定した点$${P_1}$$、$${P_2}$$もOpenGLの世界に投影できる。あとはこの2点を頂点とする四角形(GL_LINE_LOOPまたはGL_QUADS)をOpenGLで描けば、矩形選択した領域が画面上に表示される。


この変換は、

$$
\begin{pmatrix}
2.0/w                   0               0      -1   \\
            0  -2.0/h        0               1  \\
          0               0                  1               0 \\
          0               0                  0               1
\end{pmatrix}
$$

という行列で表現できるので、変換行列の設定処理は以下のようになる。OpenGL の行列は転置して設定する必要があることに注意。

void SetupProjection()
{
  int viewport[4];
  memset( viewport, 0x00, sizeof( int ) * 4 );
  glGetIntegerv( GL_VIEWPORT, viewport );

  int w = viewport[2] - viewport[0];
  int h = viewport[3] - viewport[1];
  float m11 = 2.0f / (float)w;
  float m22 = -2.0f / (float)h;
  float prj[] = {
    m11,   0, 0, 0,
      0, m22, 0, 0,
      0,   0, 1, 0,
     -1,   1, 0, 1 };

  glMatrixMode( GL_PROJECTION );
  glLoadIdentity();
  glMultMatrixf( prj );
}


矩形領域を描く処理は以下のコード。引数で与えている $${P_1}$$、$${P_2}$$がマウスで指定した位置。MouseEventArgs.Location。

  void DrawSelectedRect( 
        System::Drawing::Point P1, System::Drawing::Point P2 )
  {
    glDisable( GL_LIGHTING );

    int lx = System::Math::Min( P1.X, P2.X );
    int ux = System::Math::Max( P1.X, P2.X );
    int ly = System::Math::Min( P1.Y, P2.Y );
    int uy = System::Math::Max( P1.Y, P2.Y );
    
    glColor4ub( (byte)128, (byte)128, (byte)128, (byte)32 );
    glBegin( GL_QUADS );
    glVertex2i( lx, ly );
    glVertex2i( ux, ly );
    glVertex2i( ux, uy );
    glVertex2i( lx, uy );
    glEnd();    
    glLineWidth( 3 );
    glColor4ub( 0, 0, 0, (byte)255 );
    glBegin( GL_LINE_LOOP );
    glVertex2i( lx, ly );
    glVertex2i( ux, ly );
    glVertex2i( ux, uy );
    glVertex2i( lx, uy );
    glEnd();

    glEnable( GL_LIGHTING );
  }

下の図のように、矩形選択した領域が、枠線付きで半透明の長方形で表示される。





いいなと思ったら応援しよう!