May 15, 2008

glwidget.rb




QtのOpenGLサンプルにあるglwidget.cppを qt4-qtruby向けに書き直してみました。ruby-openglを併用しています。
これ単体で動くようにしてありますので、
$ ruby -rubygems glwidget.rb
…とすれば起動します。
動作に必要なものは以下の通りです:
コード:
require 'qt'
require 'opengl'

class GLWidget < Qt::GLWidget
  signals 'xRotationChanged(int)'
  signals 'yRotationChanged(int)'
  signals 'zRotationChanged(int)'

  slots 'setXRotation(int)'
  slots 'setYRotation(int)'
  slots 'setZRotation(int)'

  def initialize
    super
    @object = 0
    @xRot = 0
    @yRot = 0
    @zRot = 0
    @lastPos = Qt::Point.new

    @trolltechGreen = Qt::Color::fromCmykF( 0.40, 0.0, 1.0, 0.0 )
    @trolltechPurple = Qt::Color::fromCmykF( 0.39, 0.39, 0.0, 0.0 )
  end

  def destroy
    makeCurrent()
    glDeleteLists( @object, 1 )
  end

  def minimumSizeHint
    return Qt::Size.new( 50, 50 )
  end

  def sizeHint
    return Qt::Size.new( 400, 400 )
  end

  def setXRotation( angle )
    normalizeAngle( angle )
    if ( angle != @xRot )
      @xRot = angle
      emit xRotationChanged( angle )
      updateGL()
    end
  end

  def setYRotation( angle )
    normalizeAngle( angle )
    if ( angle != @yRot )
      @yRot = angle
      emit yRotationChanged( angle )
      updateGL()
    end
  end

  def setZRotation( angle )
    normalizeAngle( angle )
    if ( angle != @zRot )
      @zRot = angle
      emit zRotationChanged( angle )
      updateGL()
    end
  end

  def initializeGL
    qglClearColor( @trolltechPurple.dark() )
    @object = makeObject()
    glShadeModel( GL_FLAT )
    glEnable( GL_DEPTH_TEST )
    glEnable( GL_CULL_FACE )
  end

  def paintGL
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
    glLoadIdentity()
    glTranslated( 0.0, 0.0, -10.0)
    glRotated( @xRot / 16.0, 1.0, 0.0, 0.0 )
    glRotated( @yRot / 16.0, 0.0, 1.0, 0.0 )
    glRotated( @zRot / 16.0, 0.0, 0.0, 1.0 )
    glCallList( @object )
  end

  def resizeGL( width, height )
    # [NOTE] +qMin+ in the original code is a C++ Template Function.
    # Ref.: $(QTDIR)/src/corelib/global/qglobal.h
    #   template 
    #   inline const T &qMin(const T &a, const T &b) { if (a < b) return a; return b; }
    side = [width, height].min
    glViewport( (width - side) / 2, (height - side) / 2, side, side )

    glMatrixMode( GL_PROJECTION )
    glLoadIdentity()
    glOrtho( -0.5, +0.5, +0.5, -0.5, 4.0, 15.0 )
    glMatrixMode( GL_MODELVIEW )
  end

  def mousePressEvent( event ) # event : Qt::MouseEvent
    @lastPos = event.pos
  end

  def mouseMoveEvent( event ) # event : Qt::MouseEvent
    dx = event.x - @lastPos.x
    dy = event.y - @lastPos.y

    if ( event.buttons() & Qt::LeftButton.to_i )
      setXRotation( @xRot + 8 * dy )
      setYRotation( @yRot + 8 * dx )
    elsif ( event.buttons() & Qt::RightButton.to_i )
      setXRotation( @xRot + 8 * dy )
      setZRotation( @zRot + 8 * dx )
    end
    @lastPos = event.pos
  end

  def makeObject
    list = glGenLists( 1 )
    glNewList( list, GL_COMPILE )

    glBegin( GL_QUADS )

    x1 = +0.06
    y1 = -0.14
    x2 = +0.14
    y2 = -0.06
    x3 = +0.08
    y3 = +0.00
    x4 = +0.30
    y4 = +0.22

    quad( x1, y1, x2, y2, y2, x2, y1, x1 )
    quad( x3, y3, x4, y4, y4, x4, y3, x3 )

    extrude( x1, y1, x2, y2 )
    extrude( x2, y2, y2, x2 )
    extrude( y2, x2, y1, x1 )
    extrude( y1, x1, x1, y1 )
    extrude( x3, y3, x4, y4 )
    extrude( x4, y4, y4, x4 )
    extrude( y4, x4, y3, x3 )

    num_sectors = 200

    for i in 0...num_sectors do
      angle1 = (i * 2 * Math::PI) / num_sectors
      x5 = 0.30 * Math::sin( angle1 )
      y5 = 0.30 * Math::cos( angle1 )
      x6 = 0.20 * Math::sin( angle1 )
      y6 = 0.20 * Math::cos( angle1 )

      angle2 = ((i + 1) * 2 * Math::PI) / num_sectors
      x7 = 0.20 * Math::sin( angle2 )
      y7 = 0.20 * Math::cos( angle2 )
      x8 = 0.30 * Math::sin( angle2 )
      y8 = 0.30 * Math::cos( angle2 )

      quad( x5, y5, x6, y6, x7, y7, x8, y8 )

      extrude( x6, y6, x7, y7 )
      extrude( x8, y8, x5, y5 )
    end

    glEnd()

    glEndList()
    return list

  end

  def quad( x1,  y1,  x2,  y2,  x3,  y3,  x4,  y4 )
    qglColor( @trolltechGreen )

    glVertex3d( x1, y1, -0.05 )
    glVertex3d( x2, y2, -0.05 )
    glVertex3d( x3, y3, -0.05 )
    glVertex3d( x4, y4, -0.05 )

    glVertex3d( x4, y4, +0.05 )
    glVertex3d( x3, y3, +0.05 )
    glVertex3d( x2, y2, +0.05 )
    glVertex3d( x1, y1, +0.05 )

  end

  def extrude( x1,  y1,  x2,  y2 )
    qglColor( @trolltechGreen.dark(250 + (100 * x1).to_i) )

    glVertex3d( x1, y1, +0.05 )
    glVertex3d( x2, y2, +0.05 )
    glVertex3d( x2, y2, -0.05 )
    glVertex3d( x1, y1, -0.05 )
  end

  def normalizeAngle( angle )
    while ( angle < 0 ) do
      angle += 360 * 16
    end
    while ( angle > 360 * 16 ) do
      angle -= 360 * 16
    end
  end

end


#
# Application starts here.
#
begin
  a = Qt::Application.new(ARGV)

  w = GLWidget.new
  w.show

  a.exec
ensure
  w.destroy
end