#!/usr/bin/env ruby
#
# Data set visualization for Ruby Quiz #157 - The Smallest Circle
# using OpenGL.
#
# This program tries to load your quiz solution, defaulting to
# "157_smallest_circle.rb".  You may pass in a different
# filename as the first argument. (Omit the trailing ".rb")
#
# The visualizer expects your solution to provide two methods
# at the outer scope: encircle() and generate_samples(n)
#
# The visualizer is dumb and expects your generated point
# coordinates to fall in the range [-1.0..1.0]
#
# Press SPACE while viewing to generate and display a new
# dataset.
#
# Author: Bill Kelly
# License: This software is placed into the public domain.
# Warranty: Absolutely none. Enjoy!
#
# Version:
#   1.1   Added alpha blending on points, circle now drawn
#         on top of points
#   1.0   Initial release

require 'opengl'
require 'glut'

quiz_impl_filename = ARGV.first || "157_smallest_circle"
load "#{quiz_impl_filename}.rb"

WINDOW_WIDTH = 800
WINDOW_HEIGHT = 600

class SmallestCircleVis

  def initialize(width=WINDOW_WIDTH, height=WINDOW_WIDTH)
    @width, @height = width, height
  end
  
  def run
    GLUT.Init
    init_renderer
    GLUT.InitDisplayMode(GLUT::DOUBLE | GLUT::RGB | GLUT::ALPHA)
    GLUT.InitWindowSize(@width, @height)
    GLUT.CreateWindow($0)
    GLUT.ReshapeFunc(self.method(:reshape_func).to_proc)
    GLUT.DisplayFunc(self.method(:display_func).to_proc)
    GLUT.KeyboardFunc(self.method(:keyboard_func).to_proc)
    GLUT.MainLoop
  end

  protected

  def init_renderer
    @disk_quadric = GLU.NewQuadric
    new_random_encircle
  end

  def new_random_encircle
    @points = generate_samples(20)
    @circ = encircle(@points)
  end

  def render_circle
    GL.PushMatrix
    o = @circ.center
    GL.Translate(o.x, o.y, 0)
    r = @circ.radius
    GLU.Disk(@disk_quadric, r - 0.005, r + 0.005, 128, 1)
    GL.PopMatrix
  end

  def render_point(pt)
    GL.PushMatrix
    GL.Translate(pt.x, pt.y, 0)
    GLU.Disk(@disk_quadric, 0.0, 0.05, 128, 1)
    GL.PopMatrix
  end

  def render_points
    @points.each {|pt| render_point(pt)}
  end

  def display_func
    GL.MatrixMode(GL::MODELVIEW)
    GL.LoadIdentity

    GL.Scale(0.8, 0.8, 1.0)
    
    GL.ClearColor(0.2, 0.2, 0.2, 0.0)
    GL.Clear(GL::COLOR_BUFFER_BIT)

    GL.BlendFunc(GL::SRC_ALPHA, GL::ONE_MINUS_SRC_ALPHA)
    GL.Enable(GL::BLEND)

    GL.Color(1.0, 0.0, 0.0, 0.8)
    render_points
    
    GL.Color(0.0, 1.0, 0.0, 1.0)
    render_circle

    GLUT.SwapBuffers
  end

  def reshape_func(w, h)
    @width, @height = w, h
    
    GL.Viewport(0, 0,  w,  h)
    GL.MatrixMode(GL::PROJECTION)
    GL.LoadIdentity
    
    w, h = w.to_f, h.to_f
    if w <= h
       GLU.Ortho2D(-1.0, 1.0, -h/w, h/w)
    else 
       GLU.Ortho2D(w/h, w/h, -1.0, 1.0)
    end
  
    display_func
  end

  KEY_ESC = 27
  KEY_SPACE = 32

  def keyboard_func(key, x, y)
    case key
    when KEY_ESC
      exit(0)
    when KEY_SPACE
      new_random_encircle
      display_func
    end
  end
end

puts "Press SPACE for new dataset, ESC to exit..."
vis = SmallestCircleVis.new
vis.run



