fixed Web::Display #width=, #height=

fixed Web::Display #width=, #height=

module Yeah
module Web
class Display
  attr_reader :text_font, :text_size

  def initialize(options = {})
    canvas_selector = options.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = options.fetch(:size, DEFAULT_DISPLAY_SIZE)
    self.text_font = Font['']
    self.text_size = DEFAULT_DISPLAY_TEXT_SIZE
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def width
    `#@canvas.width`
  end
  def width=(value)
    `#@canvas.width =  #{value}`
  end

  def height
    `#@canvas.height`
  end
  def height=(value)
    `#@canvas.height =  #{value}`
  end

  def fill_color
    C[`#@context.fillStyle`]
  end
  def fill_color=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def stroke_color
    C[`#@context.strokeStyle`]
  end
  def stroke_color=(color)
    `#@context.strokeStyle = #{color.to_hex}`
  end

  def stroke_width
    `#@context.lineWidth`
  end
  def stroke_width=(numeric)
    `#@context.lineWidth = #{numeric}`
  end

  def text_font=(font)
    @text_font = font

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def text_size=(size)
    @text_size = size

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def translate(displacement)
    @transform[4] += `#{@transform[0]} * #{displacement.x} +
                      #{@transform[2]} * #{displacement.y}`
    @transform[5] += `#{@transform[1]} * #{displacement.x} +
                      #{@transform[3]} * #{displacement.y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_x(displacement)
    @transform[4] += `#{@transform[0]} * #{displacement} + #{@transform[2]}`
    @transform[5] += `#{@transform[1]} * #{displacement} + #{@transform[3]}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_y(displacement)
    @transform[4] += `#{@transform[0]} + #{@transform[2]} * #{displacement}`
    @transform[5] += `#{@transform[1]} + #{@transform[3]} * #{displacement}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_z(displacement)
  end

  def scale(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier.x},
                       #{@transform[1]} * #{multiplier.x},
                       #{@transform[2]} * #{multiplier.y},
                       #{@transform[3]} * #{multiplier.y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_x(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier},
                       #{@transform[1]} * #{multiplier},
                       #{@transform[2]}, #{@transform[3]},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_y(multiplier)
    %x{
      #{@transform} = [#{@transform[0]}, #{@transform[1]},
                       #{@transform[2]} * #{multiplier},
                       #{@transform[3]} * #{multiplier},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_z(multiplier)
  end

  def rotate(radians)
    rotate_z(radians.z)
  end

  def rotate_x(radians)
  end

  def rotate_y(radians)
  end

  def rotate_z(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def stroke_line(start_pos, end_pos)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_pos.x}, #{start_pos.y});
      #@context.lineTo(#{end_pos.x}, #{end_pos.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_curve(start_pos, end_pos, control)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_pos.x}, #{start_pos.y});
      #@context.quadraticCurveTo(#{control.x}, #{control.y},
                                 #{end_pos.x}, #{end_pos.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_curve2(start_pos, end_pos, control1, control2)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_pos.x}, #{start_pos.y});
      #@context.bezierCurveTo(#{control1.x}, #{control1.y},
                              #{control2.x}, #{control2.y},
                              #{end_pos.x}, #{end_pos.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_rectangle(position, size)
    `#@context.strokeRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def fill_rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def stroke_ellipse(center, radius)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center.x} - #{radius.x},
                          #{center.y} - #{radius.y});
      #@context.scale(#{radius.x}, #{radius.y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.stroke();
    }
  end

  def fill_ellipse(center, radius)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center.x} - #{radius.x},
                          #{center.y} - #{radius.y});
      #@context.scale(#{radius.x}, #{radius.y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.fill();
    }
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(position)
    `#@context.moveTo(#{position.x}, #{position.y})`
  end

  def line_to(position)
    `#@context.lineTo(#{position.x}, #{position.y})`
  end

  def curve_to(position, control)
    `#@context.quadraticCurveTo(#{control.x}, #{control.y},
                                #{position.x}, #{position.y})`
  end

  def curve2_to(position, control1, control2)
    `#@context.bezierCurveTo(#{control1.x}, #{control1.y},
                             #{control2.x}, #{control2.y},
                             #{position.x}, #{position.y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def image(image, position)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(image, position, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end

  def fill_text(text, position)
    `#@context.fillText(#{text}, #{position.x}, #{position.y})`
  end

  def stroke_text(text, position)
    `#@context.strokeText(#{text}, #{position.x}, #{position.y})`
  end
end
end
end

module Yeah
module Web
class Display
  attr_reader :text_font, :text_size

  def initialize(options = {})
    canvas_selector = options.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = options.fetch(:size, DEFAULT_DISPLAY_SIZE)
    self.text_font = Font['']
    self.text_size = DEFAULT_DISPLAY_TEXT_SIZE
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def width
    `#@canvas.width`
  end
  def width=(value)
    `#@canvas.width =  #{value.x}`
  end

  def height
    `#@canvas.height`
  end
  def height=(value)
    `#@canvas.height =  #{value.x}`
  end

  def fill_color
    C[`#@context.fillStyle`]
  end
  def fill_color=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def stroke_color
    C[`#@context.strokeStyle`]
  end
  def stroke_color=(color)
    `#@context.strokeStyle = #{color.to_hex}`
  end

  def stroke_width
    `#@context.lineWidth`
  end
  def stroke_width=(numeric)
    `#@context.lineWidth = #{numeric}`
  end

  def text_font=(font)
    @text_font = font

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def text_size=(size)
    @text_size = size

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def translate(displacement)
    @transform[4] += `#{@transform[0]} * #{displacement.x} +
                      #{@transform[2]} * #{displacement.y}`
    @transform[5] += `#{@transform[1]} * #{displacement.x} +
                      #{@transform[3]} * #{displacement.y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_x(displacement)
    @transform[4] += `#{@transform[0]} * #{displacement} + #{@transform[2]}`
    @transform[5] += `#{@transform[1]} * #{displacement} + #{@transform[3]}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_y(displacement)
    @transform[4] += `#{@transform[0]} + #{@transform[2]} * #{displacement}`
    @transform[5] += `#{@transform[1]} + #{@transform[3]} * #{displacement}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_z(displacement)
  end

  def scale(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier.x},
                       #{@transform[1]} * #{multiplier.x},
                       #{@transform[2]} * #{multiplier.y},
                       #{@transform[3]} * #{multiplier.y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_x(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier},
                       #{@transform[1]} * #{multiplier},
                       #{@transform[2]}, #{@transform[3]},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_y(multiplier)
    %x{
      #{@transform} = [#{@transform[0]}, #{@transform[1]},
                       #{@transform[2]} * #{multiplier},
                       #{@transform[3]} * #{multiplier},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_z(multiplier)
  end

  def rotate(radians)
    rotate_z(radians.z)
  end

  def rotate_x(radians)
  end

  def rotate_y(radians)
  end

  def rotate_z(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def stroke_line(start_pos, end_pos)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_pos.x}, #{start_pos.y});
      #@context.lineTo(#{end_pos.x}, #{end_pos.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_curve(start_pos, end_pos, control)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_pos.x}, #{start_pos.y});
      #@context.quadraticCurveTo(#{control.x}, #{control.y},
                                 #{end_pos.x}, #{end_pos.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_curve2(start_pos, end_pos, control1, control2)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_pos.x}, #{start_pos.y});
      #@context.bezierCurveTo(#{control1.x}, #{control1.y},
                              #{control2.x}, #{control2.y},
                              #{end_pos.x}, #{end_pos.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_rectangle(position, size)
    `#@context.strokeRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def fill_rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def stroke_ellipse(center, radius)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center.x} - #{radius.x},
                          #{center.y} - #{radius.y});
      #@context.scale(#{radius.x}, #{radius.y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.stroke();
    }
  end

  def fill_ellipse(center, radius)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center.x} - #{radius.x},
                          #{center.y} - #{radius.y});
      #@context.scale(#{radius.x}, #{radius.y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.fill();
    }
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(position)
    `#@context.moveTo(#{position.x}, #{position.y})`
  end

  def line_to(position)
    `#@context.lineTo(#{position.x}, #{position.y})`
  end

  def curve_to(position, control)
    `#@context.quadraticCurveTo(#{control.x}, #{control.y},
                                #{position.x}, #{position.y})`
  end

  def curve2_to(position, control1, control2)
    `#@context.bezierCurveTo(#{control1.x}, #{control1.y},
                             #{control2.x}, #{control2.y},
                             #{position.x}, #{position.y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def image(image, position)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(image, position, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end

  def fill_text(text, position)
    `#@context.fillText(#{text}, #{position.x}, #{position.y})`
  end

  def stroke_text(text, position)
    `#@context.strokeText(#{text}, #{position.x}, #{position.y})`
  end
end
end
end


web canvas gl setup, render test

web canvas gl setup, render test

module Yeah
module Web

class Context
  include Opal

  VERTEX_SHADER = <<-glsl
    attribute vec3 a_vertex_position;
    attribute vec4 a_vertex_color;

    uniform mat4 u_mv_matrix;
    uniform mat4 u_p_matrix;

    varying vec4 v_color;

    void main(void) {
      gl_Position = u_p_matrix * u_mv_matrix * vec4(a_vertex_position, 1.0);
      v_color = a_vertex_color;
    }
  glsl

  FRAGMENT_SHADER = <<-glsl
    precision mediump float;

    varying vec4 v_color;

    void main(void) {
      gl_FragColor = v_color;
    }
  glsl

  def initialize
    @canvas = Native::Object.new(`document.getElementsByTagName('canvas')[0]`)
    @gl = @canvas.getContext('webgl')

    setup_shaders

    @gl.enable(@gl.DEPTH_TEST)

    @mat4 = Native::Object.new(`mat4`)
    @mv_matrix = @mat4.create
    @p_matrix = @mat4.create
  end

  def resolution
    V[@canvas.width, @canvas.height]
  end
  def resolution=(value)
    value = V[value]

    @canvas.width = value[0]
    @canvas.height = value[1]
  end

  def each_tick(&block)
    yield
  end

  def render(level)
    @gl.viewport(0, 0, @canvas.width, @canvas.height)

    background(level.background)

    test
  end

  def test
    @mat4.perspective(45, @canvas.width / @canvas.height, 0.1, 100, @p_matrix)

    @mat4.identity(@mv_matrix)

    square_vertex_pos_buffer = @gl.createBuffer
    @gl.bindBuffer(@gl.ARRAY_BUFFER, square_vertex_pos_buffer)
    square_vertices = [
       1,  1, 0,
      -1,  1, 0,
       1, -1, 0,
      -1, -1, 0
    ]
    gl_square_vertices = Native::Object.new(`new Float32Array(#{square_vertices})`)
    @gl.bufferData(@gl.ARRAY_BUFFER, gl_square_vertices, @gl.STATIC_DRAW)

    square_vertex_col_buffer = @gl.createBuffer
    @gl.bindBuffer(@gl.ARRAY_BUFFER, square_vertex_col_buffer)
    square_colors = [1, 0.3, 0.3, 1] * 4
    gl_square_colors = Native::Object.new(`new Float32Array(#{square_colors})`)
    @gl.bufferData(@gl.ARRAY_BUFFER, gl_square_colors, @gl.STATIC_DRAW)

    @mat4.translate(@mv_matrix, @mv_matrix, [-1, -1, 0])

    @gl.bindBuffer(@gl.ARRAY_BUFFER, square_vertex_pos_buffer)
    @gl.vertexAttribPointer(@vertex_pos_attr, 3, @gl.FLOAT, false, 0, 0)

    @gl.bindBuffer(@gl.ARRAY_BUFFER, square_vertex_col_buffer)
    @gl.vertexAttribPointer(@vertex_col_attr, 4, @gl.FLOAT, false, 0, 0)

    @gl.uniformMatrix4fv(@p_matrix_unif, false, @p_matrix)
    @gl.uniformMatrix4fv(@mv_matrix_unif, false, @mv_matrix)

    @gl.drawArrays(@gl.TRIANGLE_STRIP, 0, 4)
  end

  def background(*color)
    rgb = Color[*color].rgb

    @gl.clearColor(*rgb, 1)
    @gl.clear(@gl.COLOR_BUFFER_BIT)
  end

  private

  def setup_shaders
    @shader_program = @gl.createProgram

    vertex_shader = @gl.createShader(@gl.VERTEX_SHADER)
    @gl.shaderSource(vertex_shader, VERTEX_SHADER)
    @gl.compileShader(vertex_shader)
    @gl.attachShader(@shader_program, vertex_shader)

    fragment_shader = @gl.createShader(@gl.FRAGMENT_SHADER)
    @gl.shaderSource(fragment_shader, FRAGMENT_SHADER)
    @gl.compileShader(fragment_shader)
    @gl.attachShader(@shader_program, fragment_shader)

    @gl.linkProgram(@shader_program)
    @gl.useProgram(@shader_program)

    @vertex_pos_attr = @gl.getAttribLocation(@shader_program, 'a_vertex_position')
    @gl.enableVertexAttribArray(@vertex_pos_attr)

    @vertex_col_attr = @gl.getAttribLocation(@shader_program, 'a_vertex_color')
    @gl.enableVertexAttribArray(@vertex_col_attr)

    @p_matrix_unif = @gl.getUniformLocation(@shader_program, 'u_p_matrix')
    @mv_matrix_unif = @gl.getUniformLocation(@shader_program, 'u_mv_matrix')
  end
end

end
end

module Yeah
module Web

class Context
  include Opal

  VERTEX_SHADER = <<-glsl
    attribute vec3 a_vertex_position;
    attribute vec4 a_vertex_color;

    uniform mat4 u_mv_matrix;
    uniform mat4 u_p_matrix;

    varying vec4 v_color;

    void main(void) {
      gl_Position = u_p_matrix * u_mv_matrix * vec4(a_vertex_position, 1.0);
      v_color = a_vertex_color;
    }
  glsl

  FRAGMENT_SHADER = <<-glsl
    precision mediump float;

    varying vec4 v_color;

    void main(void) {
      gl_FragColor = v_color;
    }
  glsl

  def initialize
    @canvas = Native::Object.new(`document.getElementsByTagName('canvas')[0]`)
    @gl = @canvas.getContext('webgl')

    setup_shaders
  end

  def resolution
    V[@canvas.width, @canvas.height]
  end
  def resolution=(value)
    value = V[value]

    @canvas.width = value[0]
    @canvas.height = value[1]
  end

  def each_tick(&block)
    yield
  end

  def render(level)
    background(level.background)
  end

  def background(*color)
    rgb = Color[*color].rgb

    @gl.clearColor(*rgb, 1)
    @gl.clear(@gl.COLOR_BUFFER_BIT)
  end

  private

  def setup_shaders
    @shader_program = @gl.createProgram

    vertex_shader = @gl.createShader(@gl.VERTEX_SHADER)
    @gl.shaderSource(vertex_shader, VERTEX_SHADER)
    @gl.compileShader(vertex_shader)
    @gl.attachShader(@shader_program, vertex_shader)

    fragment_shader = @gl.createShader(@gl.FRAGMENT_SHADER)
    @gl.shaderSource(fragment_shader, FRAGMENT_SHADER)
    @gl.compileShader(fragment_shader)
    @gl.attachShader(@shader_program, fragment_shader)

    @gl.linkProgram(@shader_program)
    @gl.useProgram(@shader_program)
  end
end

end
end


fixed Display w/ updated Color interface

module Yeah
module Web
class Display
  attr_reader :text_font, :text_size

  def initialize(options = {})
    canvas_selector = options.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.text_font = Font['']
    self.text_size = DEFAULT_DISPLAY_TEXT_SIZE
    self.size = options.fetch(:size, nil)
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []

    `DISPLAY = #{self}`
  end

  def width
    `#@canvas.width`
  end
  def width=(value)
    @width = value
    `#@canvas.width = #{value}` unless value.nil?

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def height
    `#@canvas.height`
  end
  def height=(value)
    @height = value
    `#@canvas.height = #{value}` unless value.nil?

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def size
    [`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    @width, @height = value

    unless value.nil?
      `#@canvas.width = #{value[0]}`
      `#@canvas.height = #{value[1]}`
    end

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def fill_color
    Color.new(`#@context.fillStyle`)
  end
  def fill_color=(color)
    %x{
      var value = #{color.value};
      #@context.fillStyle = 'rgba(' + value + ')';
    }
  end

  def stroke_color
    Color.new(`#@context.strokeStyle`)
  end
  def stroke_color=(color)
    %x{
      var value = #{color.value};
      #@context.strokeStyle = 'rgba(' + value + ')';
    }
  end

  def stroke_width
    `#@context.lineWidth`
  end
  def stroke_width=(numeric)
    `#@context.lineWidth = #{numeric}`
  end

  def text_font=(font)
    @text_font = font

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def text_size=(size)
    @text_size = size

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def color_at(x, y)
    data = `#@context.getImageData(#{x}, #{y}, 1, 1).data`
    Color.new(`data[0]`, `data[1]`, `data[2]`)
  end

  def translate(x, y)
    @transform[4] += `#{@transform[0]} * #{x} + #{@transform[2]} * #{y}`
    @transform[5] += `#{@transform[1]} * #{x} + #{@transform[3]} * #{y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_x(x)
    @transform[4] += `#{@transform[0]} * #{x} + #{@transform[2]}`
    @transform[5] += `#{@transform[1]} * #{x} + #{@transform[3]}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_y(y)
    @transform[4] += `#{@transform[0]} + #{@transform[2]} * #{y}`
    @transform[5] += `#{@transform[1]} + #{@transform[3]} * #{y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale(x, y)
    %x{
      #{@transform} = [#{@transform[0]} * #{x},
                       #{@transform[1]} * #{x},
                       #{@transform[2]} * #{y},
                       #{@transform[3]} * #{y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_x(x)
    %x{
      #{@transform} = [#{@transform[0]} * #{x},
                       #{@transform[1]} * #{x},
                       #{@transform[2]}, #{@transform[3]},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_y(y)
    %x{
      #{@transform} = [#{@transform[0]}, #{@transform[1]},
                       #{@transform[2]} * #{y},
                       #{@transform[3]} * #{y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def rotate(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def stroke_line(start_x, start_y, end_x, end_y)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_x}, #{start_y});
      #@context.lineTo(#{end_x}, #{end_y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_curve(start_x, start_y, end_x, end_y, control_x, control_y)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_x}, #{start_y});
      #@context.quadraticCurveTo(#{control_x}, #{control_y},
                                 #{end_x}, #{end_y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_curve2(start_x, start_y, end_x, end_y, control1_x, control1_y, control2_x, control2_y)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_x}, #{start_y});
      #@context.bezierCurveTo(#{control1_x}, #{control1_y},
                              #{control2_x}, #{control2_y},
                              #{end_x}, #{end_y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_rectangle(x, y, width, height)
    `#@context.strokeRect(#{x}, #{y}, #{width}, #{height})`
  end

  def fill_rectangle(x, y, width, height)
    `#@context.fillRect(#{x}, #{y}, #{width}, #{height})`
  end

  def stroke_ellipse(center_x, center_y, radius_x, radius_y)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center_x} - #{radius_x},
                          #{center_y} - #{radius_y});
      #@context.scale(#{radius_x}, #{radius_y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.stroke();
    }
  end

  def fill_ellipse(center_x, center_y, radius_x, radius_y)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center_x} - #{radius_x},
                          #{center_y} - #{radius_y});
      #@context.scale(#{radius_x}, #{radius_y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.fill();
    }
  end

  def clear
    `#@context.fillRect(0, 0, #{width}, #{height})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(x, y)
    `#@context.moveTo(#{x}, #{y})`
  end

  def line_to(x, y)
    `#@context.lineTo(#{x}, #{y})`
  end

  def curve_to(x, y, control_x, control_y)
    `#@context.quadraticCurveTo(#{control_x}, #{control_y},
                                #{x}, #{y})`
  end

  def curve2_to(x, y, control1_x, control1_y, control2_x, control2_y)
    `#@context.bezierCurveTo(#{control1_x}, #{control1_y},
                             #{control2_x}, #{control2_y},
                             #{x}, #{y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def draw_image(image, x, y)
    `#@context.drawImage(#{image.to_n}, #{x}, #{y})`
  end

  def draw_image_cropped(image, x, y, crop_x, crop_y, crop_width, crop_height)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_x}, #{crop_y},
                           #{crop_width}, #{crop_height},
                           #{x}, #{y},
                           #{crop_x}, #{crop_y})}
  end

  def fill_text(text, x, y)
    `#@context.fillText(#{text}, #{x}, #{y})`
  end

  def stroke_text(text, x, y)
    `#@context.strokeText(#{text}, #{x}, #{y})`
  end

  private

  def scale_to_window
    %x{
      var canvas = document.getElementsByTagName('canvas')[0];

      if (#{@width.nil?} && #{@height.nil?}) {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        canvas.setAttribute('style', "");
      } else {
        var widthScale = window.innerWidth / canvas.width,
            heightScale = window.innerHeight / canvas.height;
        window.displayScale = Math.min(widthScale, heightScale);

        if (PRESERVE_PIXELS && displayScale >= 1) {
          displayScale = Math.floor(displayScale);
        }

        var width = canvas.width * displayScale,
            height = canvas.height * displayScale,
            sizeStyle = "width:"+width+"px; height:"+height+"px";

        canvas.setAttribute('style', sizeStyle);
      }
    }

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end
end
end
end

module Yeah
module Web
class Display
  attr_reader :text_font, :text_size

  def initialize(options = {})
    canvas_selector = options.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.text_font = Font['']
    self.text_size = DEFAULT_DISPLAY_TEXT_SIZE
    self.size = options.fetch(:size, nil)
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []

    `DISPLAY = #{self}`
  end

  def width
    `#@canvas.width`
  end
  def width=(value)
    @width = value
    `#@canvas.width = #{value}` unless value.nil?

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def height
    `#@canvas.height`
  end
  def height=(value)
    @height = value
    `#@canvas.height = #{value}` unless value.nil?

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def size
    [`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    @width, @height = value

    unless value.nil?
      `#@canvas.width = #{value[0]}`
      `#@canvas.height = #{value[1]}`
    end

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def fill_color
    Color[`#@context.fillStyle`]
  end
  def fill_color=(color)
    %x{
      var value = #{color.value};
      #@context.fillStyle = 'rgba(' + value + ')';
    }
  end

  def stroke_color
    Color[`#@context.strokeStyle`]
  end
  def stroke_color=(color)
    %x{
      var value = #{color.value};
      #@context.strokeStyle = 'rgba(' + value + ')';
    }
  end

  def stroke_width
    `#@context.lineWidth`
  end
  def stroke_width=(numeric)
    `#@context.lineWidth = #{numeric}`
  end

  def text_font=(font)
    @text_font = font

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def text_size=(size)
    @text_size = size

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def color_at(x, y)
    data = `#@context.getImageData(#{x}, #{y}, 1, 1).data`
    Color[`data[0]`, `data[1]`, `data[2]`]
  end

  def translate(x, y)
    @transform[4] += `#{@transform[0]} * #{x} + #{@transform[2]} * #{y}`
    @transform[5] += `#{@transform[1]} * #{x} + #{@transform[3]} * #{y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_x(x)
    @transform[4] += `#{@transform[0]} * #{x} + #{@transform[2]}`
    @transform[5] += `#{@transform[1]} * #{x} + #{@transform[3]}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_y(y)
    @transform[4] += `#{@transform[0]} + #{@transform[2]} * #{y}`
    @transform[5] += `#{@transform[1]} + #{@transform[3]} * #{y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale(x, y)
    %x{
      #{@transform} = [#{@transform[0]} * #{x},
                       #{@transform[1]} * #{x},
                       #{@transform[2]} * #{y},
                       #{@transform[3]} * #{y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_x(x)
    %x{
      #{@transform} = [#{@transform[0]} * #{x},
                       #{@transform[1]} * #{x},
                       #{@transform[2]}, #{@transform[3]},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_y(y)
    %x{
      #{@transform} = [#{@transform[0]}, #{@transform[1]},
                       #{@transform[2]} * #{y},
                       #{@transform[3]} * #{y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def rotate(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def stroke_line(start_x, start_y, end_x, end_y)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_x}, #{start_y});
      #@context.lineTo(#{end_x}, #{end_y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_curve(start_x, start_y, end_x, end_y, control_x, control_y)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_x}, #{start_y});
      #@context.quadraticCurveTo(#{control_x}, #{control_y},
                                 #{end_x}, #{end_y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_curve2(start_x, start_y, end_x, end_y, control1_x, control1_y, control2_x, control2_y)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_x}, #{start_y});
      #@context.bezierCurveTo(#{control1_x}, #{control1_y},
                              #{control2_x}, #{control2_y},
                              #{end_x}, #{end_y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_rectangle(x, y, width, height)
    `#@context.strokeRect(#{x}, #{y}, #{width}, #{height})`
  end

  def fill_rectangle(x, y, width, height)
    `#@context.fillRect(#{x}, #{y}, #{width}, #{height})`
  end

  def stroke_ellipse(center_x, center_y, radius_x, radius_y)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center_x} - #{radius_x},
                          #{center_y} - #{radius_y});
      #@context.scale(#{radius_x}, #{radius_y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.stroke();
    }
  end

  def fill_ellipse(center_x, center_y, radius_x, radius_y)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center_x} - #{radius_x},
                          #{center_y} - #{radius_y});
      #@context.scale(#{radius_x}, #{radius_y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.fill();
    }
  end

  def clear
    `#@context.fillRect(0, 0, #{width}, #{height})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(x, y)
    `#@context.moveTo(#{x}, #{y})`
  end

  def line_to(x, y)
    `#@context.lineTo(#{x}, #{y})`
  end

  def curve_to(x, y, control_x, control_y)
    `#@context.quadraticCurveTo(#{control_x}, #{control_y},
                                #{x}, #{y})`
  end

  def curve2_to(x, y, control1_x, control1_y, control2_x, control2_y)
    `#@context.bezierCurveTo(#{control1_x}, #{control1_y},
                             #{control2_x}, #{control2_y},
                             #{x}, #{y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def draw_image(image, x, y)
    `#@context.drawImage(#{image.to_n}, #{x}, #{y})`
  end

  def draw_image_cropped(image, x, y, crop_x, crop_y, crop_width, crop_height)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_x}, #{crop_y},
                           #{crop_width}, #{crop_height},
                           #{x}, #{y},
                           #{crop_x}, #{crop_y})}
  end

  def fill_text(text, x, y)
    `#@context.fillText(#{text}, #{x}, #{y})`
  end

  def stroke_text(text, x, y)
    `#@context.strokeText(#{text}, #{x}, #{y})`
  end

  private

  def scale_to_window
    %x{
      var canvas = document.getElementsByTagName('canvas')[0];

      if (#{@width.nil?} && #{@height.nil?}) {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;
        canvas.setAttribute('style', "");
      } else {
        var widthScale = window.innerWidth / canvas.width,
            heightScale = window.innerHeight / canvas.height;
        window.displayScale = Math.min(widthScale, heightScale);

        if (PRESERVE_PIXELS && displayScale >= 1) {
          displayScale = Math.floor(displayScale);
        }

        var width = canvas.width * displayScale,
            height = canvas.height * displayScale,
            sizeStyle = "width:"+width+"px; height:"+height+"px";

        canvas.setAttribute('style', sizeStyle);
      }
    }

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end
end
end
end


display #draw_image -> #image

display #draw_image -> #image

module Yeah
module Web
class Display
  def initialize(args = {})
    canvas_selector = args.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = args.fetch(:size, DEFAULT_DISPLAY_SIZE)
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def fill
    C[`#@context.fillStyle`]
  end
  def fill=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def image(image, position)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(image, position, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end
end
end
end

module Yeah
module Web
class Display
  def initialize(args = {})
    canvas_selector = args.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = args.fetch(:size, DEFAULT_DISPLAY_SIZE)
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def fill
    C[`#@context.fillStyle`]
  end
  def fill=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def draw_image(image, position)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def draw_image_cropped(image, position, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end
end
end
end


vertically flipped web mouse

vertically flipped web mouse


display #text -> #fill_text & #stroke_text

module Yeah
module Web
class Display
  attr_reader :font_type, :font_size

  def initialize(args = {})
    canvas_selector = args.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = args.fetch(:size, DEFAULT_DISPLAY_SIZE)
    self.font_type = DEFAULT_DISPLAY_FONT_TYPE
    self.font_size = DEFAULT_DISPLAY_FONT_SIZE
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def width
    `#@canvas.width`
  end
  def width=(value)
    `#@canvas.width =  #{value.x}`
  end

  def height
    `#@canvas.height`
  end
  def height=(value)
    `#@canvas.height =  #{value.x}`
  end

  def fill_color
    C[`#@context.fillStyle`]
  end
  def fill_color=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def stroke_color
    C[`#@context.strokeStyle`]
  end
  def stroke_color=(color)
    `#@context.strokeStyle = #{color.to_hex}`
  end

  def stroke_width
    `#@context.lineWidth`
  end
  def stroke_width=(number)
    `#@context.lineWidth = #{number}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def transform
    @transform + [0, 0, 1] # appendage to fulfill signature
  end

  def translate(distance)
    @transform[4] += `#{@transform[0]} * #{distance.x} + #{@transform[2]} * #{distance.y}`
    @transform[5] += `#{@transform[1]} * #{distance.x} + #{@transform[3]} * #{distance.y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier.x},
                       #{@transform[1]} * #{multiplier.x},
                       #{@transform[2]} * #{multiplier.y},
                       #{@transform[3]} * #{multiplier.y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def rotate(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def line(pos1, pos2)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{pos1.x}, #{pos1.y});
      #@context.lineTo(#{pos2.x}, #{pos2.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_rectangle(position, size)
    `#@context.strokeRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def fill_rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(position)
    `#@context.moveTo(#{position.x}, #{position.y})`
  end

  def line_to(position)
    `#@context.lineTo(#{position.x}, #{position.y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def image(position, image)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(position, image, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end

  def font_type=(type)
    @font_type = type

    font = "#{@font_size}px #{@font_type}"
    `#@context.font = #{font}`
  end

  def font_size=(size)
    @font_size = size

    font = "#{@font_size}px #{@font_type}"
    `#@context.font = #{font}`
  end

  def fill_text(position, value)
    `#@context.fillText(#{value}, #{position.x}, #{position.y})`
  end

  def stroke_text(position, value)
    `#@context.strokeText(#{value}, #{position.x}, #{position.y})`
  end
end
end
end

module Yeah
module Web
class Display
  attr_reader :font_type, :font_size

  def initialize(args = {})
    canvas_selector = args.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = args.fetch(:size, DEFAULT_DISPLAY_SIZE)
    self.font_type = DEFAULT_DISPLAY_FONT_TYPE
    self.font_size = DEFAULT_DISPLAY_FONT_SIZE
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def width
    `#@canvas.width`
  end
  def width=(value)
    `#@canvas.width =  #{value.x}`
  end

  def height
    `#@canvas.height`
  end
  def height=(value)
    `#@canvas.height =  #{value.x}`
  end

  def fill_color
    C[`#@context.fillStyle`]
  end
  def fill_color=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def stroke_color
    C[`#@context.strokeStyle`]
  end
  def stroke_color=(color)
    `#@context.strokeStyle = #{color.to_hex}`
  end

  def stroke_width
    `#@context.lineWidth`
  end
  def stroke_width=(number)
    `#@context.lineWidth = #{number}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def transform
    @transform + [0, 0, 1] # appendage to fulfill signature
  end

  def translate(distance)
    @transform[4] += `#{@transform[0]} * #{distance.x} + #{@transform[2]} * #{distance.y}`
    @transform[5] += `#{@transform[1]} * #{distance.x} + #{@transform[3]} * #{distance.y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier.x},
                       #{@transform[1]} * #{multiplier.x},
                       #{@transform[2]} * #{multiplier.y},
                       #{@transform[3]} * #{multiplier.y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def rotate(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def line(pos1, pos2)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{pos1.x}, #{pos1.y});
      #@context.lineTo(#{pos2.x}, #{pos2.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_rectangle(position, size)
    `#@context.strokeRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def fill_rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(position)
    `#@context.moveTo(#{position.x}, #{position.y})`
  end

  def line_to(position)
    `#@context.lineTo(#{position.x}, #{position.y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def image(position, image)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(position, image, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end

  def font_type=(type)
    @font_type = type

    font = "#{@font_size}px #{@font_type}"
    `#@context.font = #{font}`
  end

  def font_size=(size)
    @font_size = size

    font = "#{@font_size}px #{@font_type}"
    `#@context.font = #{font}`
  end

  def text(position, value)
    `#@context.fillText(#{value}, #{position.x}, #{position.y})`
    `#@context.strokeText(#{value}, #{position.x}, #{position.y})`
  end
end
end
end


Web::Display#[translate|scale|rotate]_[x|y|z]

module Yeah
module Web
class Display
  attr_reader :text_font, :text_size

  def initialize(options = {})
    canvas_selector = options.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = options.fetch(:size, DEFAULT_DISPLAY_SIZE)
    self.text_font = Font['']
    self.text_size = DEFAULT_DISPLAY_TEXT_SIZE
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def width
    `#@canvas.width`
  end
  def width=(value)
    `#@canvas.width =  #{value.x}`
  end

  def height
    `#@canvas.height`
  end
  def height=(value)
    `#@canvas.height =  #{value.x}`
  end

  def fill_color
    C[`#@context.fillStyle`]
  end
  def fill_color=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def stroke_color
    C[`#@context.strokeStyle`]
  end
  def stroke_color=(color)
    `#@context.strokeStyle = #{color.to_hex}`
  end

  def stroke_width
    `#@context.lineWidth`
  end
  def stroke_width=(numeric)
    `#@context.lineWidth = #{numeric}`
  end

  def text_font=(font)
    @text_font = font

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def text_size=(size)
    @text_size = size

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def translate(displacement)
    @transform[4] += `#{@transform[0]} * #{displacement.x} +
                      #{@transform[2]} * #{displacement.y}`
    @transform[5] += `#{@transform[1]} * #{displacement.x} +
                      #{@transform[3]} * #{displacement.y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_x(displacement)
    @transform[4] += `#{@transform[0]} * #{displacement} + #{@transform[2]}`
    @transform[5] += `#{@transform[1]} * #{displacement} + #{@transform[3]}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_y(displacement)
    @transform[4] += `#{@transform[0]} + #{@transform[2]} * #{displacement}`
    @transform[5] += `#{@transform[1]} + #{@transform[3]} * #{displacement}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def translate_z(displacement)
  end

  def scale(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier.x},
                       #{@transform[1]} * #{multiplier.x},
                       #{@transform[2]} * #{multiplier.y},
                       #{@transform[3]} * #{multiplier.y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_x(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier},
                       #{@transform[1]} * #{multiplier},
                       #{@transform[2]}, #{@transform[3]},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_y(multiplier)
    %x{
      #{@transform} = [#{@transform[0]}, #{@transform[1]},
                       #{@transform[2]} * #{multiplier},
                       #{@transform[3]} * #{multiplier},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale_z(multiplier)
  end

  def rotate(radians)
    rotate_z(radians.z)
  end

  def rotate_x(radians)
  end

  def rotate_y(radians)
  end

  def rotate_z(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def stroke_line(start_pos, end_pos)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_pos.x}, #{start_pos.y});
      #@context.lineTo(#{end_pos.x}, #{end_pos.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_rectangle(position, size)
    `#@context.strokeRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def fill_rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def stroke_ellipse(center, radius)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center.x} - #{radius.x},
                          #{center.y} - #{radius.y});
      #@context.scale(#{radius.x}, #{radius.y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.stroke();
    }
  end

  def fill_ellipse(center, radius)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center.x} - #{radius.x},
                          #{center.y} - #{radius.y});
      #@context.scale(#{radius.x}, #{radius.y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.fill();
    }
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(position)
    `#@context.moveTo(#{position.x}, #{position.y})`
  end

  def line_to(position)
    `#@context.lineTo(#{position.x}, #{position.y})`
  end

  def curve_to(position, control)
    `#@context.quadraticCurveTo(#{control.x}, #{control.y},
                                #{position.x}, #{position.y})`
  end

  def curve2_to(position, control1, control2)
    `#@context.bezierCurveTo(#{control1.x}, #{control1.y},
                             #{control2.x}, #{control2.y},
                             #{position.x}, #{position.y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def image(image, position)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(image, position, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end

  def fill_text(text, position)
    `#@context.fillText(#{text}, #{position.x}, #{position.y})`
  end

  def stroke_text(text, position)
    `#@context.strokeText(#{text}, #{position.x}, #{position.y})`
  end
end
end
end

module Yeah
module Web
class Display
  attr_reader :text_font, :text_size

  def initialize(options = {})
    canvas_selector = options.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = options.fetch(:size, DEFAULT_DISPLAY_SIZE)
    self.text_font = Font['']
    self.text_size = DEFAULT_DISPLAY_TEXT_SIZE
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def width
    `#@canvas.width`
  end
  def width=(value)
    `#@canvas.width =  #{value.x}`
  end

  def height
    `#@canvas.height`
  end
  def height=(value)
    `#@canvas.height =  #{value.x}`
  end

  def fill_color
    C[`#@context.fillStyle`]
  end
  def fill_color=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def stroke_color
    C[`#@context.strokeStyle`]
  end
  def stroke_color=(color)
    `#@context.strokeStyle = #{color.to_hex}`
  end

  def stroke_width
    `#@context.lineWidth`
  end
  def stroke_width=(numeric)
    `#@context.lineWidth = #{numeric}`
  end

  def text_font=(font)
    @text_font = font

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def text_size=(size)
    @text_size = size

    font = "#{@text_size}px \"#{@text_font.path}\""
    `#@context.font = #{font}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def translate(displacement)
    @transform[4] += `#{@transform[0]} * #{displacement.x} +
                      #{@transform[2]} * #{displacement.y}`
    @transform[5] += `#{@transform[1]} * #{displacement.x} +
                      #{@transform[3]} * #{displacement.y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier.x},
                       #{@transform[1]} * #{multiplier.x},
                       #{@transform[2]} * #{multiplier.y},
                       #{@transform[3]} * #{multiplier.y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def rotate(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def stroke_line(start_pos, end_pos)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{start_pos.x}, #{start_pos.y});
      #@context.lineTo(#{end_pos.x}, #{end_pos.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def stroke_rectangle(position, size)
    `#@context.strokeRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def fill_rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def stroke_ellipse(center, radius)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center.x} - #{radius.x},
                          #{center.y} - #{radius.y});
      #@context.scale(#{radius.x}, #{radius.y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.stroke();
    }
  end

  def fill_ellipse(center, radius)
    %x{
      #@context.beginPath();
      #@context.save();
      #@context.beginPath();
      #@context.translate(#{center.x} - #{radius.x},
                          #{center.y} - #{radius.y});
      #@context.scale(#{radius.x}, #{radius.y});
      #@context.arc(1, 1, 1, 0, 2 * Math.PI, false);
      #@context.restore();
      #@context.fill();
    }
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(position)
    `#@context.moveTo(#{position.x}, #{position.y})`
  end

  def line_to(position)
    `#@context.lineTo(#{position.x}, #{position.y})`
  end

  def curve_to(position, control)
    `#@context.quadraticCurveTo(#{control.x}, #{control.y},
                                #{position.x}, #{position.y})`
  end

  def curve2_to(position, control1, control2)
    `#@context.bezierCurveTo(#{control1.x}, #{control1.y},
                             #{control2.x}, #{control2.y},
                             #{position.x}, #{position.y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def image(image, position)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(image, position, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end

  def fill_text(text, position)
    `#@context.fillText(#{text}, #{position.x}, #{position.y})`
  end

  def stroke_text(text, position)
    `#@context.strokeText(#{text}, #{position.x}, #{position.y})`
  end
end
end
end


Web::Context basic thing rendering

Web::Context basic thing rendering

module Yeah
module Web

class Context
  include Opal

  VERTEX_SHADER = <<-glsl
    attribute vec2 a_position;

    uniform vec2 u_resolution;

    void main(void) {
      vec2 clipspace = a_position / u_resolution * 2.0 - 1.0;

      gl_Position = vec4(clipspace, 0, 1);
    }
  glsl

  FRAGMENT_SHADER = <<-glsl
    precision mediump float;

    void main(void) {
      gl_FragColor = vec4(0.2, 0.8, 0.2, 1.0);
    }
  glsl

  def initialize
    @window = Native::Object.new(`window`)
    @canvas = Native::Object.new(`document.getElementsByTagName('canvas')[0]`)
    @gl = @canvas.getContext('webgl')

    setup_shaders
  end

  def each_tick(&block)
    @window.requestAnimationFrame do
      yield
      each_tick(&block)
    end
  end

  def resolution
    V[@canvas.width, @canvas.height]
  end
  def resolution=(value)
    value = V[value]

    @canvas.width = value[0]
    @canvas.height = value[1]
  end

  def render(level)
    @gl.viewport(0, 0, @canvas.width, @canvas.height)

    background(level.background)

    level.things.each do |thing|
      rectangle(thing.position, thing.size)
    end
  end

  def rectangle(position, size)
    res = @gl.getUniformLocation(@shader_program, 'u_resolution')
    @gl.uniform2f(res, resolution[0], resolution[1])

    rect = {
      bottom: position[0],
      left: position[1],
      width: size[0],
      height: size[1]
    }
    vertices = [
      rect[:left], rect[:bottom],
      rect[:left], rect[:bottom] + rect[:height],
      rect[:left] + rect[:width], rect[:bottom],
      rect[:left] + rect[:width], rect[:bottom] + rect[:height]
    ]

    pos_buffer = @gl.createBuffer
    @gl.bindBuffer(@gl.ARRAY_BUFFER, pos_buffer)
    gl_vertices = Native::Object.new(`new Float32Array(#{vertices})`)
    @gl.bufferData(@gl.ARRAY_BUFFER, gl_vertices, @gl.STATIC_DRAW)
    @gl.vertexAttribPointer(@pos_attr, 2, @gl.FLOAT, false, 0, 0)
    @gl.drawArrays(@gl.TRIANGLE_STRIP, 0, 4)
  end

  def background(*color)
    rgb = Color[*color].rgb

    @gl.clearColor(*rgb, 1)
    @gl.clear(@gl.COLOR_BUFFER_BIT)
  end

  private

  def setup_shaders
    @shader_program = @gl.createProgram

    vertex_shader = @gl.createShader(@gl.VERTEX_SHADER)
    @gl.shaderSource(vertex_shader, VERTEX_SHADER)
    @gl.compileShader(vertex_shader)
    @gl.attachShader(@shader_program, vertex_shader)

    fragment_shader = @gl.createShader(@gl.FRAGMENT_SHADER)
    @gl.shaderSource(fragment_shader, FRAGMENT_SHADER)
    @gl.compileShader(fragment_shader)
    @gl.attachShader(@shader_program, fragment_shader)

    @gl.linkProgram(@shader_program)
    linked = @gl.getProgramParameter(@shader_program, @gl.LINK_STATUS)
    if !linked
      error = @gl.getProgramInfoLog(@shader_program)
      puts "GL program link error: #{error}"
      return
    end

    @gl.useProgram(@shader_program)

    @pos_attr = @gl.getAttribLocation(@shader_program, 'a_position')
    @gl.enableVertexAttribArray(@pos_attr)
  end
end

end
end

module Yeah
module Web

class Context
  include Opal

  VERTEX_SHADER = <<-glsl
    attribute vec2 a_position;

    uniform vec2 u_resolution;

    void main(void) {
      vec2 clipspace = a_position / u_resolution * 2.0 - 1.0;

      gl_Position = vec4(clipspace, 0, 1);
    }
  glsl

  FRAGMENT_SHADER = <<-glsl
    precision mediump float;

    void main(void) {
      gl_FragColor = vec4(0.2, 0.8, 0.2, 1.0);
    }
  glsl

  def initialize
    @window = Native::Object.new(`window`)
    @canvas = Native::Object.new(`document.getElementsByTagName('canvas')[0]`)
    @gl = @canvas.getContext('webgl')

    setup_shaders
  end

  def each_tick(&block)
    @window.requestAnimationFrame do
      yield
      each_tick(&block)
    end
  end

  def resolution
    V[@canvas.width, @canvas.height]
  end
  def resolution=(value)
    value = V[value]

    @canvas.width = value[0]
    @canvas.height = value[1]
  end

  def render(level)
    @gl.viewport(0, 0, @canvas.width, @canvas.height)

    background(level.background)

    test
  end

  def test
    res = @gl.getUniformLocation(@shader_program, 'u_resolution')
    @gl.uniform2f(res, resolution[0], resolution[1])

    @position ||= V[]
    @position += V[1, 1]
    rect = {
      bottom: @position[0],
      left: @position[1],
      width: 100,
      height: 100
    }
    vertices = [
      rect[:left], rect[:bottom],
      rect[:left], rect[:bottom] + rect[:height],
      rect[:left] + rect[:width], rect[:bottom],
      rect[:left] + rect[:width], rect[:bottom] + rect[:height]
    ]

    pos_buffer = @gl.createBuffer
    @gl.bindBuffer(@gl.ARRAY_BUFFER, pos_buffer)
    gl_vertices = Native::Object.new(`new Float32Array(#{vertices})`)
    @gl.bufferData(@gl.ARRAY_BUFFER, gl_vertices, @gl.STATIC_DRAW)
    @gl.vertexAttribPointer(@pos_attr, 2, @gl.FLOAT, false, 0, 0)
    @gl.drawArrays(@gl.TRIANGLE_STRIP, 0, 4)
  end

  def background(*color)
    rgb = Color[*color].rgb

    @gl.clearColor(*rgb, 1)
    @gl.clear(@gl.COLOR_BUFFER_BIT)
  end

  private

  def setup_shaders
    @shader_program = @gl.createProgram

    vertex_shader = @gl.createShader(@gl.VERTEX_SHADER)
    @gl.shaderSource(vertex_shader, VERTEX_SHADER)
    @gl.compileShader(vertex_shader)
    @gl.attachShader(@shader_program, vertex_shader)

    fragment_shader = @gl.createShader(@gl.FRAGMENT_SHADER)
    @gl.shaderSource(fragment_shader, FRAGMENT_SHADER)
    @gl.compileShader(fragment_shader)
    @gl.attachShader(@shader_program, fragment_shader)

    @gl.linkProgram(@shader_program)
    linked = @gl.getProgramParameter(@shader_program, @gl.LINK_STATUS)
    if !linked
      error = @gl.getProgramInfoLog(@shader_program)
      puts "GL program link error: #{error}"
      return
    end

    @gl.useProgram(@shader_program)

    @pos_attr = @gl.getAttribLocation(@shader_program, 'a_position')
    @gl.enableVertexAttribArray(@pos_attr)
  end
end

end
end


display #stroke/fill -> #stroke/fill_shape

module Yeah
module Web
class Display
  attr_reader :font_type, :font_size

  def initialize(args = {})
    canvas_selector = args.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = args.fetch(:size, DEFAULT_DISPLAY_SIZE)
    self.font_type = DEFAULT_DISPLAY_FONT_TYPE
    self.font_size = DEFAULT_DISPLAY_FONT_SIZE
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def fill
    C[`#@context.fillStyle`]
  end
  def fill=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def stroke
    C[`#@context.strokeStyle`]
  end
  def stroke=(color)
    `#@context.strokeStyle = #{color.to_hex}`
  end

  def line_width
    `#@context.lineWidth`
  end
  def line_width=(number)
    `#@context.lineWidth = #{number}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def transform
    @transform + [0, 0, 1] # appendage to fulfill signature
  end

  def translate(distance)
    @transform[4] += `#{@transform[0]} * #{distance.x} + #{@transform[2]} * #{distance.y}`
    @transform[5] += `#{@transform[1]} * #{distance.x} + #{@transform[3]} * #{distance.y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier.x},
                       #{@transform[1]} * #{multiplier.x},
                       #{@transform[2]} * #{multiplier.y},
                       #{@transform[3]} * #{multiplier.y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def rotate(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def line(pos1, pos2)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{pos1.x}, #{pos1.y});
      #@context.lineTo(#{pos2.x}, #{pos2.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
    `#@context.strokeRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(position)
    `#@context.moveTo(#{position.x}, #{position.y})`
  end

  def line_to(position)
    `#@context.lineTo(#{position.x}, #{position.y})`
  end

  def stroke_shape
    `#@context.stroke()`
  end

  def fill_shape
    `#@context.fill()`
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def image(position, image)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(position, image, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end

  def font_type=(type)
    @font_type = type
    update_font
  end

  def font_size=(size)
    @font_size = size
    update_font
  end

  def text(position, value)
    `#@context.fillText(#{value}, #{position.x}, #{position.y})`
    `#@context.strokeText(#{value}, #{position.x}, #{position.y})`
  end

  private

  def update_font
    font = "#{font_size}px #{font_type}"
    `#@context.font = #{font}`
  end
end
end
end

module Yeah
module Web
class Display
  attr_reader :font_type, :font_size

  def initialize(args = {})
    canvas_selector = args.fetch(:canvas_selector, DEFAULT_CANVAS_SELECTOR)

    @canvas = `document.querySelectorAll(#{canvas_selector})[0]`
    @context = `#@canvas.getContext('2d')`
    self.size = args.fetch(:size, DEFAULT_DISPLAY_SIZE)
    self.font_type = DEFAULT_DISPLAY_FONT_TYPE
    self.font_size = DEFAULT_DISPLAY_FONT_SIZE
    @transform = [1, 0, 0, 1, 0, 0]
    @transforms = []
  end

  def size
    V[`#@canvas.width`, `#@canvas.height`]
  end
  def size=(value)
    `#@canvas.width =  #{value.x}`
    `#@canvas.height = #{value.y}`
  end

  def fill
    C[`#@context.fillStyle`]
  end
  def fill=(color)
    `#@context.fillStyle = #{color.to_hex}`
  end

  def stroke
    C[`#@context.strokeStyle`]
  end
  def stroke=(color)
    `#@context.strokeStyle = #{color.to_hex}`
  end

  def line_width
    `#@context.lineWidth`
  end
  def line_width=(number)
    `#@context.lineWidth = #{number}`
  end

  def color_at(position)
    data = `#@context.getImageData(#{position.x}, #{position.y}, 1, 1).data`
    C[`data[0]`, `data[1]`, `data[2]`]
  end

  def transform
    @transform + [0, 0, 1] # appendage to fulfill signature
  end

  def translate(distance)
    @transform[4] += `#{@transform[0]} * #{distance.x} + #{@transform[2]} * #{distance.y}`
    @transform[5] += `#{@transform[1]} * #{distance.x} + #{@transform[3]} * #{distance.y}`

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def scale(multiplier)
    %x{
      #{@transform} = [#{@transform[0]} * #{multiplier.x},
                       #{@transform[1]} * #{multiplier.x},
                       #{@transform[2]} * #{multiplier.y},
                       #{@transform[3]} * #{multiplier.y},
                       #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def rotate(radians)
    %x{
      var cos = Math.cos(#{radians}),
          sin = Math.sin(#{radians}),
          e0 = #{@transform[0]} * cos + #{@transform[2]} * sin,
          e1 = #{@transform[1]} * cos + #{@transform[3]} * sin,
          e2 = #{@transform[0]} * -sin + #{@transform[2]} * cos,
          e3 = #{@transform[1]} * -sin + #{@transform[3]} * cos;

      #@transform = [e0, e1, e2, e3, #{@transform[4]}, #{@transform[5]}];

      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def push
    @transforms.push(@transform.dup)
  end

  def pop
    @transform = @transforms.pop

    %x{
      #@context.setTransform(#{@transform[0]}, #{@transform[1]},
                             #{@transform[2]}, #{@transform[3]},
                             #{@transform[4]}, #{@transform[5]}); }
  end

  def line(pos1, pos2)
    %x{
      #@context.beginPath();
      #@context.moveTo(#{pos1.x}, #{pos1.y});
      #@context.lineTo(#{pos2.x}, #{pos2.y});
      #@context.closePath();
      #@context.stroke();
    }
  end

  def rectangle(position, size)
    `#@context.fillRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
    `#@context.strokeRect(#{position.x}, #{position.y}, #{size.x}, #{size.y})`
  end

  def begin_shape
    `#@context.beginPath()`
  end

  def end_shape
    `#@context.closePath()`
  end

  def move_to(position)
    `#@context.moveTo(#{position.x}, #{position.y})`
  end

  def line_to(position)
    `#@context.lineTo(#{position.x}, #{position.y})`
  end

  def stroke
    `#@context.stroke()`
  end

  def fill
    `#@context.fill()`
  end

  def clear
    `#@context.fillRect(0, 0, #{size.x}, #{size.y})`
  end

  def image(position, image)
    `#@context.drawImage(#{image.to_n}, #{position.x}, #{position.y})`
  end

  def image_cropped(position, image, crop_position, crop_size)
    %x{#@context.drawImage(#{image.to_n},
                           #{crop_position.x}, #{crop_position.y},
                           #{crop_size.x}, #{crop_size.y},
                           #{position.x}, #{position.y},
                           #{crop_size.x}, #{crop_size.y})}
  end

  def font_type=(type)
    @font_type = type
    update_font
  end

  def font_size=(size)
    @font_size = size
    update_font
  end

  def text(position, value)
    `#@context.fillText(#{value}, #{position.x}, #{position.y})`
    `#@context.strokeText(#{value}, #{position.x}, #{position.y})`
  end

  private

  def update_font
    font = "#{font_size}px #{font_type}"
    `#@context.font = #{font}`
  end
end
end
end