JRubyでFilthyRichClients その4

色々と自分でShapeを作れれば、Grapics2Dのfillやdrawを使って、これらのShapeを描画することができる。
Filthy Rich Clientsでは、

  • Area、Elipse2Dを使ったドーナツ型
  • GeneratePathを使った星型

などが紹介されている。このカスタムShapeをJRubyで書いてみた。

include Java

include_class %w( JFrame JComponent SwingUtilities ).map{|s| 'javax.swing.' + s}
include_class %w( Color GradientPaint RadialGradientPaint RenderingHints ).map{|s| 'java.awt.' + s}
include_class %w( MouseAdapter ).map{|s| 'java.awt.event.' + s}
include_class %w( Area Ellipse2D Point2D GeneralPath ).map{|s| 'java.awt.geom.' + s}

class Canvas < JComponent
  def initialize()
    super()
    addMouseListener(ClickReceiver.new)
    
    @shapes = []
    @colors = []
    @get_star = true
  end
  
  def paintComponent(g)
    draw_background(g)
    g.setRenderingHint(RenderingHints::KEY_ANTIALIASING, RenderingHints::VALUE_ANTIALIAS_ON)
    draw_shape(g)
  end
  
  def create_shape(e)
    inner_size = 1 + rand(25)
    outer_size = inner_size + 10 + rand(15)
    if @get_star
      branch_count = (rand(8) + 5).to_i
      @shapes << generate_star(e.getX, e.getY, inner_size, outer_size, branch_count)
    else
      @shapes << generate_donut(e.getX-outer_size/2, e.getY-outer_size/2, inner_size, outer_size)
    end
    2.times do
      @colors << Color.new(rand(0xFFFFFF))
    end
    @get_star = !@get_star
    repaint()
  end
  
  private
  
  def draw_background(g)
    background = GradientPaint.new(0, 0, Color::GRAY.darker(),
                                   0, getHeight(), Color::GRAY.brighter())
    g.setPaint(background)
    g.fillRect(0, 0, getWidth(), 4*getHeight()/5)
    
    background = GradientPaint.new(0, 4*getHeight()/5, Color::BLACK,
                                   0, getHeight(), Color::GRAY.darker());
    g.setPaint(background)
    g.fillRect(0, 4*getHeight()/5, getWidth(), getHeight()/5)    
  end
  
  def draw_shape(g)
    @shapes.each_with_index do |shape, i|
      rect = shape.getBounds()
      center = Point2D::Float.new(rect.x + rect.width/2.0, rect.y + rect.height/2.0)
      radius = rect.width/2.0
      dist = [0.1, 0.9].to_java(:float)
      colors = [@colors[i*2], @colors[i*2+1]].to_java('java.awt.Color')   
      g.setPaint(RadialGradientPaint.new(center, radius, dist, colors))
      g.fill(shape)
    end
  end
  
  def generate_donut(x, y, inner_radius, outer_radius)
    a1 = Area.new(Ellipse2D::Double.new(x, y, outer_radius, outer_radius))
    inner_offset = (outer_radius - inner_radius)/2.0
    a2 = Area.new(Ellipse2D::Double.new(x + inner_offset, y + inner_offset, inner_radius, inner_radius))
    a1.subtract(a2)
    a1    
  end
  
  def generate_star(center_x, center_y, inner_radius, outer_radius, branch_count)
    path = GeneralPath.new
    path.moveTo(center_x + outer_radius, center_y)
    
    outer_angle_increment = 2.0 * Math::PI / branch_count
    outer_angle = 0.0
    inner_angle = outer_angle_increment / 2.0
    branch_count.times do
      x = Math.cos(outer_angle) * outer_radius + center_x
      y = Math.sin(outer_angle) * outer_radius + center_y
      path.lineTo(x, y)
      
      x = Math.cos(inner_angle) * inner_radius + center_x
      y = Math.sin(inner_angle) * inner_radius + center_y
      path.lineTo(x, y)
      
      outer_angle += outer_angle_increment
      inner_angle += outer_angle_increment
    end
    
    path.closePath()
    path
  end
end

class ClickReceiver < MouseAdapter
  def mouseClicked(e)
    c = e.getSource
    c.create_shape(e)
  end
end

SwingUtilities.invokeLater do
  f = JFrame.new
  f.add(Canvas.new)
  f.setSize(500, 500)
  f.setDefaultCloseOperation(JFrame::EXIT_ON_CLOSE)
  f.setLocationRelativeTo(nil)
  f.setVisible(true)  
end

実行結果


画面をクリックすると、ドーナツ型と星型の図形が交互にフレームに追加される。
generate_donutでドーナツ型、generate_starで星型のShapeを作っている。

generate_donutでは、大きい円a1と小さい円a2のShapeを作って、
a1からa2の領域を除く(a1.subtract(a2))ことでドーナツ型を作っている。