view src/net/kryshen/charamega/field.mirah @ 6:9d940c7ee4c0

Adjusted defaults and limits.
author Mikhail Kryshen <mikhail@kryshen.net>
date Mon, 16 Jul 2012 06:30:28 +0400
parents 1e64a109812f
children 2efb0973ab83
line wrap: on
line source

#
#  Copyright 2012 Mikhail Kryshen <mikhail@kryshen.net>
#
#  This file is part of Charamega.
#
#  Charamega is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  Charamega is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with Charamega.  If not, see <http://www.gnu.org/licenses/>.
#

package net.kryshen.charamega

import java.awt.*
import java.awt.geom.*
import java.awt.event.*
import javax.swing.*
import java.util.List

class Field < JComponent

  def initialize(game:Game)
    @field_color = Color.WHITE
    @border_color = Color.GRAY
    @active_border_color = Color.DARK_GRAY
    @symbol_color = Color.BLACK
    @back_color = Color.LIGHT_GRAY
    @face_color = Color.new(0xFFFDDD)

    @game = game 

    setOpaque true
    setDoubleBuffered true
    setPreferredSize Dimension.new(790, 500)

    begin
      source = getClass.getResource("DejaVuSans.ttf").openStream
      font = Font.createFont(Font.TRUETYPE_FONT, source)
    ensure
      source.close  unless source.nil?
    end
    setFont font

    enableEvents AWTEvent.MOUSE_EVENT_MASK
    enableEvents AWTEvent.MOUSE_MOTION_EVENT_MASK
  end

  def processMouseEvent(event)
    if isEnabled
      if event.getID == MouseEvent.MOUSE_PRESSED
        
        @hold = hit(event.getX, event.getY)
        
      elsif event.getID == MouseEvent.MOUSE_RELEASED and
          !@hold.nil? and @hold == hit(event.getX, event.getY)
        
        @game.open @hold
        repaint
      elsif event.getID == MouseEvent.MOUSE_EXITED and
          !@hovered.nil?
        
        @hovered = nil
        repaint
      end
    end

    super
  end
  
  def processMouseMotionEvent(event)
    if isEnabled
      h = hit(event.getX, event.getY)
      
      if event.getID == MouseEvent.MOUSE_DRAGGED
        if h == @hold and h != @hovered
          @hovered = h
          repaint
        end
        
        if h != @hold and @hold == @hovered
          @hovered = nil
          repaint
        end
        
      elsif h != @hovered
        @hovered = h
        repaint
      end
    end

    super
  end

  def paintComponent(g1)
    g = Graphics2D(g1)

    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                        RenderingHints.VALUE_ANTIALIAS_ON)
    g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                       RenderingHints.VALUE_FRACTIONALMETRICS_ON)

    w = getWidth
    h = getHeight
   
    g.setColor @field_color
    g.fillRect 0, 0, w, h

    insets = getInsets
    w -= insets.left + insets.right
    h -= insets.top + insets.bottom

    layout = @game.layout(w, h)
    time = System.nanoTime

    max_cols = Math.ceil(double(@game.cards.size) / layout.size)
    rh = double(h) / layout.size
    card_size = Math.min(rh, double(w) / max_cols) / 1.4
    font_size = card_size
    font = g.getFont.deriveFont(float(font_size))
    frc = g.getFontRenderContext

    rm_scale = 2.0

    outer = Rectangle2D.Double.new
    inner = Rectangle2D.Double.new
    border = Path2D.Double.new(Path2D.WIND_EVEN_ODD)

    save_t = g.getTransform

    y = rh / 2 + insets.top
    layout.each do |row|
      cw = double(w) / List(row).size
      x = cw / 2 + insets.left

      List(row).each do |e|
        card = Card(e)

        v = card.visible_state(time)

        # Compute area potentially affected by the card.
        if v < 1.0
          paint_x = int(Math.floor(x - cw * rm_scale / 2 - 1))
          paint_y = int(Math.floor(y - rh * rm_scale / 2 - 1))
          paint_w = int(Math.ceil(cw * rm_scale + 2))
          paint_h = int(Math.ceil(rh * rm_scale + 2))
        else
          paint_x = int(Math.floor(x - cw / 2 - 1))
          paint_y = int(Math.floor(y - rh / 2 - 1))
          paint_w = int(Math.ceil(cw + 2))
          paint_h = int(Math.ceil(rh + 2))
        end

        if v > 0.0 and g.hitClip(paint_x, paint_y, paint_w, paint_h)
          f = card.flip_state(time)

          if v < 1.0 or (f > -1.0 and f < 1.0)
            # Animation is in progress.
            repaint 30, paint_x, paint_y, paint_w, paint_h
          end

          g.translate x, y

          v_scale = rm_scale - Math.sqrt(v) * rm_scale
          g.scale Math.abs(f) + v_scale, 1.0 + v_scale

          g.rotate card.angle * v

          outer.setRect(-card_size / 2, -card_size / 2,
                        card_size, card_size)
          inner.setRect(1 - card_size / 2, 1 - card_size / 2,
                        card_size - 2, card_size - 2)
          border.reset
          border.append outer, false
          border.append inner, false
          
          if f > 0
            g.setColor with_alpha(@face_color, v)
          else
            g.setColor with_alpha(@back_color, v)
          end

          g.fill inner

          if f > 0
            g.setColor with_alpha(@symbol_color, Math.sqrt(v))

            gv = font.createGlyphVector(frc, String.valueOf(card.symbol))
            sb = gv.getVisualBounds

            # Scale down if the glyph does not fit.
            tolerance = 0.95
            scale = Math.min(inner.getWidth * tolerance / sb.getWidth,
                             inner.getHeight * tolerance / sb.getHeight)
            save_t_2 = g.getTransform
            g.scale scale, scale  if scale < 1.0

            g.drawGlyphVector(gv, 
                              -float(sb.getX + sb.getWidth / 2),
                              -float(sb.getY + sb.getHeight / 2))

            g.setTransform save_t_2
          end

          if card == @hovered
            g.setColor with_alpha(@active_border_color, v)
          else
            g.setColor with_alpha(@border_color, v)
          end

          g.fill border

          g.setTransform save_t
        end

        x += cw
      end
      
      y += rh
    end
  end

  private
  
  def hit(x:int, y:int)
    insets = getInsets
    size = getSize
    @game.hit(x - insets.left,
              y - insets.top,
              size.width - insets.left - insets.right,
              size.height - insets.top - insets.bottom)
  end

  def with_alpha(c:Color, alpha:double)
    Color.new c.getRed, c.getGreen, c.getBlue, int(alpha * 255)
  end
end