Friday, March 25, 2011

How to draw a taichi image ?

We may draw a taichi by the following steps :
  1. Draw 3 circles.
  2. Erase the left half of the upper circle and the right half of the lower circle.
  3. Add two "eyes".
  4. Fill regions with appropriate color.

Straight forward implementation


public void drawTaichi(BufferedImage image)
{
  java.awt.Graphics2D gr = (java.awt.Graphics2D) image.getGraphics();
  gr.setColor(java.awt.Color.WHITE);
  gr.fillRect(0,0,300,300);

  gr.setStroke(new java.awt.BasicStroke(2));  // pen width
  gr.setColor(java.awt.Color.BLACK);

  // draw a big circle with radius 100 with center at (150,150)
  int r=100;  // radius
  int xc=150, yc=150;  // center
  // the bounding rectangle (x,y,w,h) of the circle is defined by :
  // x=xc-r,  y=yc-r,  w=r*2,  h=r*2;
  gr.drawArc(xc-r,yc-r,r*2,r*2,0,360);  // (x,y,w,h,startDegree,arcDegree)

  // draw a small half circle with radius 50 with center at (150,100)
  xc=150; yc=100; r=50;
  gr.drawArc(xc-r,yc-r,r*2,r*2,270,180);  // (x,y,w,h,startDegree,arcDegree)

  // draw another small half circle with radius 50 with center at (150,200)
  xc=150; yc=200; r=50;
  gr.drawArc(xc-r,yc-r,r*2,r*2,90,180);  // (x,y,w,h,startDegree,arcDegree)

  // draw two more small circles ("eyes")
  xc=150; yc=100; r=12;
  gr.drawArc(xc-r,yc-r,r*2,r*2,0,360);  // (x,y,w,h,startDegree,arcDegree)

  xc=150; yc=200; r=12;
  gr.drawArc(xc-r,yc-r,r*2,r*2,0,360);  // (x,y,w,h,startDegree,arcDegree)

  // fill with appropriate color
  int black=packRgb(0,0,0);
  floodFill(image,150,100,black);

  floodFill(image,150,160,black);
}


The above implementation uses the floodFill() method described in the previous article.

Improvement : removing the hard coded values

The above implementation is full of hard coded values such as 300,150,100...etc. A way to remove those hard coded values is to introduce magic constants such as :
int BIG_RADIUS=100;
int HARD_RADIUS=BIG_RADIUS/2;
...

Another approach is to redefine the drawing routine to accept parameters. Full runnable sample follows :

/******************************************************************************
* File : Taichi.java
* Author : http://java.macteki.com/
* Description :
*   Draw a Taichi image in a graphics panel.
* Tested with : JDK 1.6
******************************************************************************/

import java.awt.image.BufferedImage;
import java.awt.Point;

class Taichi extends javax.swing.JPanel
{
  private BufferedImage taichiImage;

  public static Taichi getInstance()
  {
    Taichi panel=new Taichi();
    panel.setPreferredSize(new java.awt.Dimension(320,320));

    panel.taichiImage = new BufferedImage(300,300,BufferedImage.TYPE_INT_RGB);
    panel.drawTaichi(panel.taichiImage,150,150,100);

    return panel;
  }

  // draw a taichi image
  public void drawTaichi(BufferedImage image,int xCenter,int yCenter, int radius)
  {
    java.awt.Graphics2D gr = (java.awt.Graphics2D) image.getGraphics();
    gr.setColor(java.awt.Color.WHITE);
    gr.fillRect(0,0,image.getWidth(this),image.getWidth(this));

    gr.setStroke(new java.awt.BasicStroke(2));  // pen width
    gr.setColor(java.awt.Color.BLACK);

    // draw the big circle
    int r=radius;  // radius
    int xc=xCenter, yc=yCenter;  // center
    // the bounding rectangle (x,y,w,h) of the circle is defined by :
    // x=xc-r,  y=yc-r,  w=r*2,  h=r*2;
    gr.drawArc(xc-r,yc-r,r*2,r*2,0,360);  // (x,y,w,h,startDegree,arcDegree)

    // draw a half circle inside the big circle
    xc=xCenter; yc=yCenter-radius/2; r=radius/2;
    gr.drawArc(xc-r,yc-r,r*2,r*2,270,180);  // (x,y,w,h,startDegree,arcDegree)

    // draw another half circle inside the big circle
    xc=xCenter; yc=yCenter+radius/2; r=radius/2;
    gr.drawArc(xc-r,yc-r,r*2,r*2,90,180);  // (x,y,w,h,startDegree,arcDegree)

    // draw two more small circles
    xc=xCenter; yc=yCenter-radius/2; r=radius/8;
    gr.drawArc(xc-r,yc-r,r*2,r*2,0,360);  // (x,y,w,h,startDegree,arcDegree)

    xc=xCenter; yc=yCenter+radius/2; r=radius/8+1;
    gr.drawArc(xc-r,yc-r,r*2,r*2,0,360);  // (x,y,w,h,startDegree,arcDegree)

    // fill with appropriate color
    int black=packRgb(0,0,0);
    floodFill(image,xCenter,yCenter-radius/2,black);

    floodFill(image,xCenter,yCenter+radius/16,black);
  }


  // override the paint method
  // just paint the taichi image here
  public void paintComponent(java.awt.Graphics graphics)
  {
    super.paintComponent(graphics);  

    // Graphics2D is a better choice to the default Graphics object
    java.awt.Graphics2D gr=(java.awt.Graphics2D) graphics;

    gr.drawImage(taichiImage,10,10,this);
  }

  public static int packRgb(int r,int g,int b)
  {
    return (r*256+g)*256+b;
  }


  // implements the flood fill algorithm
  public static void floodFill(BufferedImage image, int x,int y, int fillColor)
  {
    java.util.ArrayList<Point> examList=new java.util.ArrayList<Point>();

    int initialColor=image.getRGB(x,y);
    examList.add(new Point(x,y));

    while (examList.size()>0)
    {
      Point p = examList.remove(0);  // get and remove the first point in the list
      if (image.getRGB(p.x,p.y)==initialColor) 
      {
        x = p.x;  y = p.y;
        image.setRGB(x, y, fillColor);  // fill current pixel

        if (image.getRGB(x-1,y)==initialColor) // check west neighbor
        {
          examList.add(new Point(x-1,y));        
        }
        if (image.getRGB(x+1,y)==initialColor) // check east neighbor
        {
          examList.add(new Point(x+1,y));        
        }
        if (image.getRGB(x,y-1)==initialColor) // check north neighbor
        {
          examList.add(new Point(x,y-1));        
        }
        if (image.getRGB(x,y+1)==initialColor) // check south neighbor
        {
          examList.add(new Point(x,y+1));       
        }

      }
    }

  }



  public static void main(String[] args) throws Exception
  {
    Taichi graphicPanel = Taichi.getInstance();

    javax.swing.JFrame window=new javax.swing.JFrame();
    window.add(graphicPanel);
    window.pack();

    window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
    window.setTitle("Macteki Taichi Panel");
    window.setVisible(true);
  }
}

The next article will make a rotating Taichi animation.

Thanks for reading. Comments are welcome.

No comments:

Post a Comment