11/25の課題について


問題のポイント


解答例(1)

/*********************************************************************
  月と雪だるま                     

  マウスをクリックすると雪の画像を配置します。
  ドラッグすることで配置する画像のサイズを変更できます。
  雪の画像のサイズを決めるときに、雪の結晶の見た目の半径が
  ドラッグ開始点からドラッグ終了点までの距離になるよう工夫しました。
**********************************************************************/

import java.awt.*;       //AWTを使うためにインポート
import java.awt.event.*; //イベント用にインポート
import java.util.*;      //乱数、Vectorのためにインポート

class Snow{
  // ドラッグの始点,終点のX座標,Y座標を int で保持する.
  public int snowX, snowY, snowSizeX, snowSizeY;
  // Snowのコンストラクタ
  public Snow(int x1,int x2,int x3,int x4){
    snowX = x1;
    snowY = x2;
    snowSizeX = x3;
    snowSizeY = x4;
  }
}

class Kadai1125 extends Frame
       implements KeyListener, MouseListener, MouseMotionListener{

  Vector snowArray;// snowの配列を保持する変数を宣言する
  Random r=new Random(); //雪の描画用に乱数を宣言する
  Image snow, backImage, offScreenImage; //Imageクラスの変数を宣言する
  Graphics offScreenGraphics;

  public Kadai1125(String title){
    super(title);
    snowArray=new Vector();
    snow=Toolkit.getDefaultToolkit().getImage("snow.GIF");
    addKeyListener(this);
    addMouseListener(this);
    addMouseMotionListener(this);
  }

  public static void main(String[] args){
    Kadai1125 frame=new Kadai1125("Kadai1125");
    frame.setSize(400,400); //ウインドウのサイズを400,400にする
    frame.setVisible(true); //ウインドウを見せる
  }

  public void update(Graphics g){
    if(offScreenImage==null){
      offScreenImage=createImage(400,400); // オフスクリーンイメージを400x400のサイズで作成
      offScreenGraphics=offScreenImage.getGraphics(); // オフスクリーンイメージに描画するための Graphics オブジェクト
    }
    paint(offScreenGraphics); // 次の画面のイメージを作る.
    g.drawImage(offScreenImage,0,0,this); // イメージを本物のスクリーンに書き込む
  }

  public void paint(Graphics g){
    if(backImage==null){     // 400x400のサイズの背景用イメージを作成
      backImage=createImage(400,400); 
      Graphics g1=backImage.getGraphics(); 
      g1.setColor(Color.black); //黒色
      g1.fillRect(0,0,400,400); //全体を塗り潰す

      //月の描画
      g1.setColor(new Color(220,220,100)); //薄い黄色
      g1.fillOval(75,75,50,50);
      g1.setColor(Color.black);
      g1.fillOval(100,100,30,30);

      //雪原(背面)の描画
      for (int i=0;i<200;i++){
        g1.setColor(new Color(i+55,i+55,i+55)); //グラデーション
        g1.drawLine(0,i+200,400,i+200);
      }

      //雪だるま下の描画
      for (int i=1;i<=20;i++){
        g1.setColor(new Color(i*3+195, i*3+195, i*3+195)); //グラデーション
        g1.fillOval(200-(int)((25-i)*2.5*.8), 300-(int)((25-i)*2.5*.8), (int)((25-i)*2.5*2), (int)((25-i)*2.5*2));
      }

      //雪だるま上の描画
      for (int i=1;i<=20;i++){
        g1.setColor(new Color(i*3+195, i*3+195, i*3+195)); //グラデーション
        g1.fillOval(200-(int)((25-i)*2*.8), 220-(int)((25-i)*2*.8), (int)((25-i)*2*2), (int)((25-i)*2*2));
      }

      //顔の描画
      g1.setColor(new Color(250,200,150)); //オレンジ
      int[] xs={170,195,200,202,200,195};
      int[] ys={241,231,234,241,249,252};
      g1.fillPolygon(xs,ys,6); //鼻の描画
      g1.setColor(Color.black); //黒色
      g1.fillArc(180,210,10,20,20,320); //左目の描画
      g1.fillArc(210,210,10,20,20,320); //右目の描画
    }
    g.drawImage(backImage,0,0,this);
    int size=snowArray.size();
    for(int i=0;i< size;i++){
      Snow s=(Snow)snowArray.elementAt(i);
      //ドラッグ開始点から終了点までの距離を求めて雪の結晶の見た目の半径にする
      int snowSize = (int)Math.sqrt(Math.pow(s.snowX-s.snowSizeX,2)+Math.pow(s.snowY-s.snowSizeY,2))+1;
      //雪の画像のサイズを上で求めた半径の2倍にして
      //ドラッグ開始点が中心になるように画像を配置する
      g.drawImage(snow, s.snowX-snowSize, s.snowY-snowSize, snowSize*2, snowSize*2, this);
    }
  }

  //--------- Key event ----------------------------------------
  public void keyPressed(KeyEvent e){
    int key=e.getKeyChar();
    if(key=='q') System.exit(0); //qを押したら終了
  }
  public void keyReleased(KeyEvent e){}
  public void keyTyped(KeyEvent e){}
  //------------------------------------------------------------

  //--------- Mouse event --------------------------------------
  public void mousePressed(MouseEvent e){
    int mx=e.getX();  //マウスのX座標を得る
    int my=e.getY();  //マウスのY座標を得る
    snowArray.addElement(new Snow(mx,my,mx,my));  // 配列SnowsのsnowCount番目に登録
    repaint();   // 再表示をおこなう
  }
  public void mouseReleased(MouseEvent e){
    int mx=e.getX();  //マウスのX座標を得る
    int my=e.getY();  //マウスのY座標を得る
    Snow s=(Snow)snowArray.elementAt(snowArray.size()-1);
    s.snowSizeX = mx;
    s.snowSizeY = my;
    repaint();   // 再表示をおこなう
  }
  public void mouseClicked(MouseEvent e){}
  public void mouseEntered(MouseEvent e){}
  public void mouseExited(MouseEvent e){}
  public void mouseDragged(MouseEvent e){
    int mx=e.getX();  //マウスのX座標を得る
    int my=e.getY();  //マウスのY座標を得る
    Snow s=(Snow)snowArray.elementAt(snowArray.size()-1);
    s.snowSizeX = mx;
    s.snowSizeY = my;
    repaint();   // 再表示をおこなう
  }
  public void mouseMoved(MouseEvent e){}
  //------------------------------------------------------------
}
コメント
背景を書く部分を,最初にオフスクリーンイメージに蓄えておくことにより,毎回の書き換えを 高速におこなうように工夫していますね.

解答例(2)


// XXXX
// XXXXXX
//空に流れ星を飛ばそうとしました。
//時間切れで流れ星っぽくないものを排除するプログラムは
//つくれませんでした


// AWTを使うので java.awt.*を import する
import java.awt.*;
// イベント駆動関係のクラスを用いるため
import java.awt.event.*;
// Vectorクラスを用いるため
import java.util.*;

//線分のクラスを定義する
class Line{
    //始点、終点のX座標、Y座標を int で保持する
    public int start_x,start_y,end_x,end_y;
    //Lineのコンストラクタ
    public Line(int x1,int x2,int x3,int x4){
        start_x=x1;
	start_y=x2;
	end_x=x3;
	end_y=x4;
    }
}

// 独立したウィンドウを開くので,Frameクラスのサブクラスにする
class Kadai1125 extends Frame implements KeyListener, MouseListener, MouseMotionListener{
    Image image;
    Vector lineArray;
    boolean dragging;
    Color lineColor;
    public Kadai1125(String title){
	super(title);
	image=Toolkit.getDefaultToolkit().getImage("star1.gif");
	lineArray=new Vector();
	dragging=false;
	lineColor=Color.black;
	// GUI部品と,Event Listenerを関連づける
	addKeyListener(this);
	addMouseListener(this);
	addMouseMotionListener(this);
    }
    public static void main(String[] args){
	Kadai1125 frame=new Kadai1125("Kadai1125");
	frame.setSize(400,400);
	frame.setVisible(true);    
    }
    Image offScreenImage;
    Graphics offScreenGraphics;
    public void update(Graphics g){
	if(offScreenImage==null){
	    offScreenImage=createImage(400,400);
	    offScreenGraphics=offScreenImage.getGraphics();
	}
	paint(offScreenGraphics);
	g.drawImage(offScreenImage,0,0,this);
    }
    public void paint(Graphics g){
	int i;
	//長方形の空を新しい青っぽい黒で作る
	g.setColor(new Color(10,0,80));
	g.fillRect(0,0,400,200);
	//地面の色を準備し、長方形で手前を塗り潰す
	g.setColor(Color.lightGray);
        g.fillRect(0,200,400,200);
	//海の作成、まずは濃いめの青を作る
	g.setColor(new Color(0,100,200));
	//波打ち際を楕円で作り、塗り潰す
	g.fillOval(0,150,400,100);
	//楕円と空の隙間を長方形で塗り潰す
        g.fillRect(0,100,400,100);
	//雪だるまを作る。まずは白を用意
	g.setColor(Color.white);
	//二つの円を重ねて並べる
	g.fillOval(260,180,80,80);
	g.fillOval(240,250,120,120);
	//月をつくる。まずは空に浮かんだ方。
	g.setColor(Color.yellow);
	g.fillOval(120,20,60,60);
	//最後に海に映った月を作る。
	g.setColor(new Color(240,240,100));
	g.fillOval(120,120,60,60);
	g.setColor(lineColor.white);
	int size=lineArray.size();
	if(dragging) size--;
	for(i=0;i< size;i++){
	    Line l=(Line)lineArray.elementAt(i);
	    g.drawLine(l.start_x,l.start_y,l.end_x,l.end_y);
	    g.drawLine(l.start_x,l.start_y+6,l.end_x,l.end_y+6);
	    g.drawImage(image,l.end_x,l.end_y+2,this);
	}
	if(dragging){
	    g.setColor(Color.white);
	    Line l=(Line)lineArray.elementAt(i);
	    g.drawLine(l.start_x,l.start_y,l.end_x,l.end_y);
	}
    }
    public void keyPressed(KeyEvent e){
	int key=e.getKeyChar();
	System.out.println("keyPressed("+e+","+key+")");
	if(key=='q') System.exit(0);
    }
    public void keyReleased(KeyEvent e){}
    public void keyTyped(KeyEvent e){}
    public void mousePressed(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	System.out.println("mousePressed("+e+","+mx+","+my+")");
	lineArray.addElement(new Line(mx,my,mx,my));
	dragging=true;
	repaint();
    }
    public void mouseReleased(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	System.out.println("mouseReleased("+e+","+mx+","+my+")");
	Line l=(Line)lineArray.elementAt(lineArray.size()-1);
	l.end_x=mx;
	l.end_y=my;
	dragging=false;
	repaint();
    }
    public void mouseClicked(MouseEvent e){}
    public void mouseEntered(MouseEvent e){}
    public void mouseExited(MouseEvent e){}
    public void mouseDragged(MouseEvent e){
	int mx=e.getX(),my=e.getY();
	System.out.println("mouseDragged("+e+","+mx+","+my+")");
	Line l=(Line)lineArray.elementAt(lineArray.size()-1);
	l.end_x=mx;
	l.end_y=my;
	repaint();
    }
    public void mouseMoved(MouseEvent e){}
}

コメント
線を書くだけでなく,星をイメージで描いて流れ星にしたのがポイントですね.星と尾がずれて しまっているのが気になりますが.

解答例(3)

// XXXX XXXX
// 11/25の課題 雪だるまと月(改)
//
// 空をクリックすると月が回転移動し、地面をクリックすると木が生えていく。
// 回転は、1つのピクセルを参照するのではなく、
// 4ピクセルを参照して重みをつけて平均を取った。
// 月の画像ファイルはは101*101で、
// そのうち月が描かれているのは中心の71*71部分である。
// 木は、遠近感を出すために奥行きによって高さを変え、奥から描画するようにした。
// アンチエイリアスを効かせ、画質の向上を図った。 

import java.awt.*;// AWTを使うため
import java.awt.geom.*; // Graphics2を用いるため
import java.awt.event.*; // イベント駆動関係のクラスを用いるため
import java.awt.image.*; // イメージを使うため
import java.util.*; // random,Vectorを使うため

// 木クラス
class Tree{
	// x,y座標および高さの値をintで保持する
	public int x,y,h;
	// Treeのコンストラクタ
	public Tree(int n1,int n2,int n3){
		x=n1;
		y=n2;
		h=n3;
	}
}

// 独立したウィンドウを開くので,Frameクラスのサブクラスにする
// KeyListener,MouseListenerをインプリメント
class Kadai1125 extends Frame implements KeyListener, MouseListener{
	// イメージを表す Image クラスの変数 image の宣言
	Image image,showImage;
	// Treeの配列を保持するVectorクラスの変数 treeArrayの宣言
	Vector treeArray;
	// 木の高さ決定用ランダム
	Random ran=new Random();
	// 月描画用変数の宣言
	int moonX,moonY;
	// メイン
	public static void main(String[] args){
	Kadai1125 frame=new Kadai1125("Kadai1125");
		frame.setSize(600,600);
		frame.setVisible(true);
	}
	
	// コンストラクタの宣言
		public Kadai1125(String title){
		super(title);
		// 月画像読込
		image=Toolkit.getDefaultToolkit().getImage("moon.png");
		// treeArrayのインスタンスを作成
		treeArray=new Vector();
		// 月の初期位置は550
		rotateImage(550);
		
		// EventListnerを関連づける
		addKeyListener(this);
		addMouseListener(this);
	}
	
	// KeyListenerを実装するためのメソッド
	public void keyPressed(KeyEvent e){
		// イベントからキーのコードを取り出す
		int key=e.getKeyChar();
		// デバッグ用の表示
		System.out.println("keyPressed("+e+","+key+")");
		// 入力が 'q'の時は終了する
		if(key=='q') System.exit(0);
	}
	// その他Keyイベント
	public void keyReleased(KeyEvent e){}
	public void keyTyped(KeyEvent e){}

	// MouseListenerを実装するためのメソッド
	// マウスのボタンを押した際に発生するイベントを以下のメソッドで捕まえる.
	public void mousePressed(MouseEvent e){
		Tree t;
		int addNum,size=treeArray.size();
		if(e.getY()>300){
			// 下半分をクリックした時は、奥から順に木を追加する
			for(addNum=0;addNum< size;addNum++){
				t=(Tree)treeArray.elementAt(addNum);
				if(t.y>e.getY())break;

			}
			treeArray.add(addNum,new Tree(e.getX(),e.getY(),
			// 木の高さは、奥行きに応じた標準サイズに対し標準偏差6.7%で分布
			(int)((e.getY()*4/3-350)*(ran.nextGaussian()+15)/15)));
			repaint();
		}else if(e.getX()>35&&e.getX()<565){
			// 上半分をクリックした時は、そのx座標の直上・直下に月を回転移動する
			rotateImage(e.getX());
			repaint();
 		}
	}	
	// その他Mouseイベント
	public void mouseReleased(MouseEvent e){}
	public void mouseClicked(MouseEvent e){}
	public void mouseEntered(MouseEvent e){}
	public void mouseExited(MouseEvent e){}
	
	// オフスクリーン描画用変数の宣言
	Image offScreenImage;
	Graphics offScreenGraphics;
	
	// updateメソッド
	public void update(Graphics g){
		if(offScreenImage==null){
			offScreenImage=createImage(600,600);
			offScreenGraphics=offScreenImage.getGraphics();
		}
		paint(offScreenGraphics);
		g.drawImage(offScreenImage,0,0,this);
	}

	// paintメソッド
	public void paint(Graphics g){
		// Grapchis2Dを保存
		Graphics2D g2=(Graphics2D)g;
		// アンチエイリアスを効かせる
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
		// 背景塗りつぶし
		g2.setColor(new Color(30,20,90));
		g2.fillRect(0,0,600,300);
		g2.setColor(new Color(90,110,150));
		g2.fillRect(0,300,600,600);
		// 月描画
		g2.drawImage(showImage,moonX,moonY,this);
		// 木描画
		drawTrees(g2);
		// 雪だるま2体描画
		drawSnowmans(g2);
	}

	// 月回転メソッド
	void rotateImage(int pressedX){
		// 画像情報を入出力する配列の宣言
		int[] pixels = new int[101*101];
		int[] showPixels = new int[71*71];
		// 月のx座標におけるcos,sinを宣言・代入
		double cos=pressedX/300.0-1.0,sin=Math.sin(Math.acos(cos));
		// 新しい座標とその小数点以下の変数の宣言
		double newx,newy,dx,dy;
		// 各チャンネル値データを入れる計算用変数の宣言
		double pix;
		
		// 月の描画開始位置を決定
		moonX=pressedX-35;
		moonY=(int)(365-300*sin);
		
		// imageからpixelsにgrabするためのオブジェクトを生成する
		PixelGrabber pg = new PixelGrabber(image, 0, 0, 101, 101, pixels, 0, 101);
		try {
			// grabを開始する.
			pg.grabPixels();
		} catch (InterruptedException ie) {
			System.err.println("interrupted waiting for pixels!");
			System.exit(1);
		}
		// 失敗したかどうか?
		if ((pg.getStatus() & ImageObserver.ABORT) != 0) {
			System.err.println("image fetch aborted or errored");
			return;
		}

		// 補完処理を働かせた回転処理
		for (int i=0;i<71;i++){
			for (int j=0;j<71;j++){
				newx=(j-35)*cos-(i-35)*sin+50;
				newy=(j-35)*sin+(i-35)*cos+50;
				dx=newx-(int)newx;
				dy=newy-(int)newy;
				showPixels[i*71+j]=0;
				for (int m=0;m<4;m++){// alpha,r,g,b各チャンネルのループ
					pix=0;
					for (int k=0;k<2;k++){ // 参照するy座標のループ
						for (int l=0;l< 2;l++){ // 参照するx座標のループ
							// dx,dyに応じて、参照するピクセルの値をどれくらい加重するか決める
							pix=pix+((1-l)+(2*l-1)*dx)*((1-k)+(2*k-1)*dy)*
							// 参照するピクセルでの、現在のチャンネルの値を取り出す
							((pixels[(int)(newy+k)*101+(int)(newx+l)]>>(8*m)) & 0xff);
						}
					}
					// 現在のチャンネルの値を、表示するピクセルに入れる
					showPixels[i*71+j]+=(int)pix<< (8*m);
				}
			}
		}
		// 表示する月のImageを作る
		showImage=createImage(new MemoryImageSource(71,71,showPixels,0,71));
	}
	
	// 雪だるまリフレクト描画用変数・メソッドの宣言
	int r;
	float x(float xx){
	return r*xx + 355f;
	}
	int ix(float xx){
	return (int)x(xx);
	}
	// 雪だるま描画メソッド
	void drawSnowmans(Graphics2D g2){
		for(r=-1;r<=1;r=r+2){
			// ボディ
			g2.setColor(Color.white);
			g2.fillOval(ix(100)-75,428,150,150);
			g2.fillOval(ix(100)-60,338,120,120);
			// 腕
			GeneralPath arm = new GeneralPath(); 
			arm.moveTo(x(0),447.7f); 
			arm.lineTo(x(50.2f),472.3f); 
			arm.lineTo(x(45.5f),481.8f); 
			arm.lineTo(x(-4.6f),457.2f); 
			arm.closePath(); 
			g2.setColor(new Color(130,80,40)); 
			g2.fill(arm);
			// 鼻
			GeneralPath nose = new GeneralPath();
			nose.moveTo(x(30.0f),374.9f);
			nose.lineTo(x(42.7f),384.9f);
			nose.lineTo(x(46.1f),373.6f);
			nose.closePath();
			g2.setColor(Color.black);
			g2.fill(nose);
			// 目
			GeneralPath eye = new GeneralPath();
			eye.moveTo(x(62.3789f),371.4912f);
			eye.curveTo(x(61.3252f),374.6484f,x(58.9043f),376.6846f,x(56.9736f),376.04f);
			eye.curveTo(x(55.0439f),375.3955f,x(54.333f),372.3125f,x(55.3877f),369.1563f);
			eye.curveTo(x(56.4414f),366f,x(58.8623f),363.9639f,x(60.792f),364.6094f);
			eye.curveTo(x(62.7227f),365.2529f,x(63.4336f),368.3359f,x(62.3789f),371.4912f);
			eye.closePath();
			g2.setColor(Color.black);
			g2.fill(eye);
			// バケツここから
			// 側面
			GeneralPath bucket1 = new GeneralPath();
			bucket1.moveTo(x(114.6f),308.8f);
			bucket1.lineTo(x(156.1f),327.0f);
			bucket1.lineTo(x(150.1f),364.8f);
			bucket1.lineTo(x(90.8f),338.7f);
			bucket1.closePath();
			g2.setColor(new Color(80-r*70,10,80+r*70));
			g2.fill(bucket1);
			// 上面
			GeneralPath bucket2 = new GeneralPath();
			bucket2.moveTo(x(149.9395f),365.3574f);
			bucket2.curveTo(x(148.2129f),369.291f,x(133.5107f),366.6367f,x(117.1035f),359.4307f);
			bucket2.curveTo(x(100.6963f),352.2246f,x(88.7959f),333.1953f,x(90.5225f),339.2637f);
			bucket2.curveTo(x(92.25f),335.3291f,x(106.9512f),337.9834f,x(123.3604f),345.1885f);
			bucket2.curveTo(x(139.7666f),352.3945f,x(151.668f),361.4238f,x(149.9395f),365.3574f);
			bucket2.closePath();
			g2.setColor(new Color(80-r*70,10,80+r*70));
			g2.fill(bucket2);
			// 下面
			GeneralPath bucket3 = new GeneralPath();
			bucket3.moveTo(x(156.0068f),327.3721f);
			bucket3.curveTo(x(157.2334f),324.5791f,x(148.9102f),318.2256f,x(137.418f),313.1777f);
			bucket3.curveTo(x(125.9238f),308.1299f,x(115.6133f),306.2998f,x(114.3867f),309.0938f);
			bucket3.curveTo(x(113.1602f),311.8857f,x(121.4814f),318.2412f,x(132.9775f),323.2891f);
			bucket3.curveTo(x(144.4697f),328.3369f,x(154.7813f),330.165f,x(156.0068f),327.3721f);
			bucket3.closePath();
			g2.setColor(new Color(80-r*70,10,80+r*70));
			g2.fill(bucket3);
			// バケツここまで
		}
	}
	// trees 描画メソッド
	void drawTrees(Graphics2D g2){
		int size=treeArray.size();
		Tree t;
		for(int i=0;i< size;i++){
			t=(Tree)treeArray.elementAt(i);
			// 幹の描画
			g2.setColor(new Color(120,87,26));
			g2.fillRect(t.x-(int)(t.h*0.03),t.y-(int)(t.h*0.24),(int)(t.h*0.06),(int)(t.h*0.24));
			// 葉の描画
			GeneralPath leaves = new GeneralPath();
			leaves.moveTo(t.x,(float)(t.y-1*t.h));
			leaves.lineTo((float)(t.x-0.14*t.h),(float)(t.y-t.h*0.67));
			leaves.lineTo((float)(t.x-0.05*t.h),(float)(t.y-t.h*0.70));
			leaves.lineTo((float)(t.x-0.17*t.h),(float)(t.y-t.h*0.42));
			leaves.lineTo((float)(t.x-0.08*t.h),(float)(t.y-t.h*0.45));
			leaves.lineTo((float)(t.x-0.2*t.h),(float)(t.y-t.h*0.16));
			leaves.lineTo(t.x,(float)(t.y-t.h*0.22));
			leaves.lineTo((float)(t.x+0.2*t.h),(float)(t.y-t.h*0.16));
			leaves.lineTo((float)(t.x+0.08*t.h),(float)(t.y-t.h*0.45));
			leaves.lineTo((float)(t.x+0.17*t.h),(float)(t.y-t.h*0.42));
			leaves.lineTo((float)(t.x+0.05*t.h),(float)(t.y-t.h*0.70));
			leaves.lineTo((float)(t.x+0.14*t.h),(float)(t.y-t.h*0.67));
			leaves.closePath();
			g2.setColor(new Color(26,120,29));
			g2.fill(leaves);
		}
	}
}
コメント
非常に力の入ったプログラムです.イメージをただ表示するのではなく,回転操作をした上で表 示していて,しかもその際にサブピクセルまで考慮するなどこだわりを感じます.

解答例(4)

// ===============================================
//  Kadai1125 "Snow Man - Ver 1.1"
//    for 25 Nov., 2003  PROGRAMMING I ( T.Tanaka )
//
//  Copyright(c) XXXXXXXX, all rights reserved.
// ===============================================
//
//  XXXXXX XXXXX ---
//  *
//  * [ver1.0 2003/11/11]
//  * 月と雪だるまを描画するJAVAプログラムです。
//  * Graphicsクラスに含まれていない陰影のついた円を描く
//  * 関数、fill3DOvalを作ることによって、ソフトな印象を
//  * もった絵を描くことができました。
//  *
//  * [ver1.1 2003/11/25]
//  * マウスの左クリックで星を、右クリックでカラフルな
//  * 雪だるま(色はランダム)が追加できるようになっています。
//  * 星の画像は外部ファイル"star.gif"からの読み込み。
//  * 雪だるまは標準関数のみで描画しています。
//  * 細かい修正として、ダブルバッファリングを採用しました。
//  *

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;

class Kadai1125 extends Frame {
  // ウィンドウのサイズ(タイトルバーを含む)
  static final int SIZE= 400;
  static final int X = SIZE, Y = SIZE;

  // 雪の個数
  static final int SNOW_NUMBER = 300;

  // fill3DOvalの陰影の強さ(0:なし, 1:線形, ...)
  static final int SHADE_STRENGTH = 20;

  // 雪だるまと星の管理用
  static Vector snowMen = new Vector();
  static Vector star = new Vector();

  /**
   * Kadai1125のコンストラクタ
   * @param title ウィンドウのタイトル
   *
   * 外部からのnewは不可
   */
  Kadai1125(String title) {
    super(title);
    addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent e){
	int key = e.getKeyChar();
	if(key == 'q') System.exit(0);
      }
    });
    addWindowListener(new WindowListener() {
      public void windowActivated(WindowEvent e) {}
      public void windowClosed(WindowEvent e) {}
      public void windowClosing(WindowEvent e) { dispose(); }
      public void windowDeactivated(WindowEvent e) {}
      public void windowDeiconified(WindowEvent e) {}
      public void windowIconified(WindowEvent e) {}
      public void windowOpened(WindowEvent e) {}
    });
    addMouseListener(new MouseListener() {
      public void mouseClicked(MouseEvent e) {
        switch(e.getButton()) {
          case 1: // 左クリック
            addStar((double)e.getX() / (double)X, (double)e.getY() / (double)Y);
            break;
          case 3: // 右クリック
            addSnowMan((double)e.getX() / (double)X, (double)e.getY() / (double)Y);
            break;
          default:
            repaint();
        }
      }
      public void mouseEntered(MouseEvent e) {}
      public void mouseExited(MouseEvent e) {}
      public void mousePressed(MouseEvent e) {}
      public void mouseReleased(MouseEvent e) {}
    });
  }

  /** アプリケーションのエントリポイント */
  public static void main(String[] args) {
    Kadai1125 frame = new Kadai1125("Kadai1125");
    frame.setSize(X, Y);
    frame.setVisible(true);
  }

  /** 画像の描画 */
  public void paint(Graphics g_real) {
    // ダブルバッファリング用バッファー
    Image buffer = createImage(X, Y);
    Graphics g = buffer.getGraphics();

    if(snowMen.size() == 0) addSnowMan(0.70, 0.65, Color.white);

    // 空
    for(int n = 0; n <= Y*.7; n++) {
      g.setColor(new Color(0, 0, (int)(255 * (1 - n / (Y*.7)))));
      g.drawLine(0, n, X, n);
    }

    // 雪面
    for(int n = 0; n <= Y*.3; n++) {
      int c = (int)(255 * Math.pow(n / (Y*.3), .7));
      g.setColor(new Color(c, c, c));
      g.drawLine(0, (int)(n + Y*.7), X, (int)(n + Y*.7));
    }

    // 雪だるま
    for(int n = 0; n < snowMen.size(); n++) {
      ((SnowMan)(snowMen.get(n))).paint(g);
    }

    // 星
    for(int n = 0; n < star.size(); n++) {
      ((Star)(star.get(n))).paint(g);
    }

    // 月
    g.setColor(new Color(255, 255, 96));
    g.fillArc((int)(X*.10), (int)(Y*.10), (int)(X*.12),
              (int)(Y*.12), 240, 180);

    // 雪
    for(int n = 0; n < SNOW_NUMBER; n++) {
      int r = (int)((1 + Math.random()) * SIZE/120);
      int x = (int)(Math.random() * X);
      int y = (int)(Math.random() * Y);
      int c = (int)(239 + Math.random() * 16);
      g.setColor(new Color(c, c, c));
      fill3DOval(g, (int)(x - r/2), (int)(y - r/2), r, r);
    }

    // 情報表示
    g.setColor(Color.black);
    g.drawString("星の個数:" + Integer.toString(star.size()) + " / " +
                 "雪だるまの個数:" + Integer.toString(snowMen.size()), 20, Y - 20);

    // バッファの転送
    g_real.drawImage(buffer, 0, 0, this);
  }

  /** 陰影付の円を描画 */
  static void fill3DOval(Graphics g, int x, int y, int xlen, int ylen) {
    Color c = g.getColor();
    int len = Math.max(xlen, ylen);
    int c_r, c_g, c_b;

    g.fillOval(x, y, xlen, ylen);

    x += xlen / 2;
    y += ylen / 2;

    for(float n = 0; n <= len / 2; n += 1.1 - n/len*2) {
      c_r = (int)(c.getRed() * (1 - .3*Math.pow(n / (len / 2), SHADE_STRENGTH)));
      c_g = (int)(c.getGreen() * (1 - .3*Math.pow(n / (len / 2), SHADE_STRENGTH)));
      c_b = (int)(c.getBlue() * (1 - .3*Math.pow(n / (len / 2), SHADE_STRENGTH)));
      // 周辺部にいくにしたがって、陰影が現れる
      g.setColor(new Color(c_r, c_g, c_b));
      g.drawOval((int)(x - n), (int)(y - n), (int)(n*2), (int)(n*2));
    }

    g.setColor(c);
  }

  void addSnowMan(double x, double y) {
    // R,G,Bの要素がそれぞれ224〜255の範囲で動くランダムな色を生成
    addSnowMan(x, y, new Color((int)(Math.random() * 32) + 224,
                               (int)(Math.random() * 32) + 224,
                               (int)(Math.random() * 32) + 224)
               );
  }

  void addSnowMan(double x, double y, Color color) {
    snowMen.add(new SnowMan(X, Y, SIZE, x, y, color));
    repaint();
  }

  void addStar(double x, double y) {
    star.add(new Star(X, Y, SIZE, x, y, this));
    repaint();
  }
}

class SnowMan {
  double x = 0, y = 0;
  int X = 0, Y = 0, SIZE = 0;
  Color color = null;

  public SnowMan(int X, int Y, int SIZE, double x, double y, Color color) {
    this.x = x;
    this.y = y;
    this.X = X;
    this.Y = Y;
    this.SIZE = SIZE;
    if(color == null) this.color = Color.white;
    else this.color = color;
  }

  void paint(Graphics g) {
    g.setColor(color);
    Kadai1125.fill3DOval(g, (int)(X*(x-.15)), (int)(Y*(y-.15)), (int)(SIZE*.30), (int)(SIZE*.30));
    Kadai1125.fill3DOval(g, (int)(X*(x-.10)), (int)(Y*(y-.33)), (int)(SIZE*.20), (int)(SIZE*.20));
    g.setColor(new Color(96, 96, 96));
    Kadai1125.fill3DOval(g, (int)(X*(x-.07)), (int)(Y*(y-.25)), (int)(SIZE*.04), (int)(SIZE*.04));
    Kadai1125.fill3DOval(g, (int)(X*(x+.03)), (int)(Y*(y-.25)), (int)(SIZE*.04), (int)(SIZE*.04));
    Kadai1125.fill3DOval(g, (int)(X*(x-.01)), (int)(Y*(y-.20)), (int)(SIZE*.02), (int)(SIZE*.02));
  }
}

class Star {
  double x = 0, y = 0;
  int X = 0, Y = 0, SIZE = 0;
  Color color = null;
  static Image image = null;
  static ImageObserver ob = null;

  public Star(int X, int Y, int SIZE, double x, double y, ImageObserver ob) {
    if(image == null) image = Toolkit.getDefaultToolkit().getImage("./star.gif");
    this.x = x;
    this.y = y;
    this.X = X;
    this.Y = Y;
    this.SIZE = SIZE;
    this.ob = ob;
  }

  void paint(Graphics g) {
    g.drawImage(image, (int)(X*x-16), (int)(Y*y-16), ob);
  }
}

解答例(5)

//XXXXX
//XXXXX
//月と雪だるまを描いて雪だるまをいぢめるプログラム
//qを押すと終了します。たぶん。
//ドラッグで指定した範囲に肉が出現。
//お腹を空かせた雪だるまは思わずよだれを垂らします。が、
//雪だるまなので動けません。だから食えません。嗚呼。
//右ボタンで肉をクリックすると消せます。
import java.awt.*;
import java.awt.event.*;
import java.util.*;

class Niku{
    int x,y,w,h;
    //雪だるまの視線の色は肉の味に依存すると言われている
    Color aji;
    //肉の絵
    static Image nikuimg=Toolkit.getDefaultToolkit().getImage("niku.png");
    public Niku(int xx,int yy, Color ajia){
	x=xx;y=yy;
	w=0;h=0;
	aji=ajia;
    }
}

class Kadai1125 extends Frame{
    //左ボタンを押している間true
    boolean lpressed;
    //唾のたれぐあい
    int yodarel;
    //オフスクリーンイメージ
    Image offimg;
    //オフスクリーングラフィックス
    Graphics offgra;
    //肉の配列を管理
    Vector nikuniku;
    //乱数生成のもと
    static Random r=new Random();
    public Kadai1125(String title){
	super(title);
	lpressed=false;
	nikuniku=new Vector();
	yodarel=0;

	//GUI部品とEvent Listenerを関連づける。
	addKeyListener(new KeyAdapter(){
	    //qで終了
	    public void keyPressed(KeyEvent e){
	        int key=e.getKeyChar();
	        System.out.println(e + ", " + key);
	        if(key=='q') System.exit(0);
	    }
	});

	addMouseListener(new MouseAdapter(){
	    //新しい肉を出す。再描画なし。
	    public void mousePressed(MouseEvent e){
		if(e.getModifiers()==16){
		    lpressed=true;
		    nikuniku.addElement(new Niku(e.getX(),e.getY(),new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255))));
		}
		System.out.println(e);
		System.out.println(e.getModifiers());
	    }

	    //よだれを垂らす。
	    public void mouseReleased(MouseEvent e){
		if(e.getModifiers()==16){
		    lpressed=false;
		    yodarel+=r.nextInt(10);
		    repaint();
		}
		System.out.println(e);
		System.out.println(e.getModifiers());
	    }

	    //右クリックされた場所で一番手前にある肉を消す
	    public void mouseClicked(MouseEvent e){
		if(e.getModifiers()==4){
		    for(int i=nikuniku.size()-1;0<=i;i--){
			Niku n=(Niku)nikuniku.elementAt(i);
			if(Math.min(n.x, n.x+n.w)<=e.getX() && e.getX()<=Math.max(n.x, n.x+n.w) && Math.min(n.y, n.y+n.h)<=e.getY() && e.getY()<=Math.max(n.y, n.y+n.h) && !lpressed){
			    nikuniku.removeElementAt(i);
			    repaint();
			    break;
			}
		    }
		}
		System.out.println(e);
		System.out.println(e.getModifiers());
	    }
	});

	addMouseMotionListener(new MouseMotionAdapter(){
	    //肉の幅と高さをいぢる。
	    public void mouseDragged(MouseEvent e){
		if(e.getModifiers()==16){
		    Niku n=(Niku)nikuniku.elementAt(nikuniku.size()-1);
		    n.w=e.getX()-n.x; n.h=e.getY()-n.y;
		    repaint();
		}
		System.out.println(e);
		System.out.println(e.getModifiers());
	    }
	});
    }

    public static void main(String[] args){
	Kadai1125 mado=new Kadai1125("Kadai1125");
	mado.setSize(400,400);
	mado.setVisible(true);
    }

    public void update(Graphics g){
	if(offimg==null){
	    offimg=createImage(400,400);
	    offgra=offimg.getGraphics();
	}
	paint(offgra);
	g.drawImage(offimg,0,0,this);
    }

    public void paint(Graphics g){
	//夜空
	g.setColor(Color.black);
	g.fillRect(0,0,400,200);
	//地面
	g.setColor(Color.darkGray);
	g.fillRect(0,200,400,200);
	//月(三日月)
	g.setColor(Color.yellow);
	g.fillArc(350,50,30,30,135,180);
	g.setColor(Color.black);
	g.fillOval(354,36,40,40);
	//雪だるま
	g.setColor(Color.white);
	g.fillOval(50,170,90,90);
	g.fillOval(46,250,120,120);
	//肉を見ると目の色が変わる
	g.setColor(new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255)));
	g.fillOval(90,200,5,5);
	g.fillOval(120,196,5,5);
	g.setColor(Color.red);
	g.fillArc(102,210,20,20,190,180);
	g.setColor(new Color(200,150,0));
	g.drawLine(35,260,70,280);
	g.drawLine(155,258,134,278);
	//唾
	g.setColor(new Color(100,255,255));
	g.fillRoundRect(108,228,8,yodarel,4,4);
	//肉
	for(int i=0;i< nikuniku.size();i++){
	    Niku n=(Niku)nikuniku.elementAt(i);
	    g.drawImage(n.nikuimg,n.x,n.y,n.w,n.h,this);
	    g.setColor(n.aji);
	    g.drawLine(92,202,n.x+n.w/2,n.y+n.h/2);
	    g.drawLine(122,198,n.x+n.w/2,n.y+n.h/2);
	}
    }
}

解答例(6)

//氏名 XXXXX
//学生番号 XXXXX
//マウス入力に応じて月とだるましきに加えるプログラム 
//"a"を入力すると円弧を描く 
//"s"を入力すると雪を描く 

// AWTを使うので java.awt.*を import する 
import java.awt.*; 
// イベント駆動関係のクラスを用いるため 
import java.awt.event.*; 
//Vectorクラスを用いるため 
import java.util.*; 
import java.awt.geom.*;


//円弧のクラスを定義する 
class Arc { 
    // 円弧を覆う長方形の始点,終点のX座標,Y座標を int で保持する. 
    public int start1_x,start1_y,end1_x,end1_y; 
    // Arcのコンストラクタ 
    public Arc(int x1,int x2,int x3,int x4) { 
    start1_x=x1; 
    start1_y=x2; 
    end1_x=x3; 
    end1_y=x4; 
    } 
} 
//雪のクラスを定義する
class Snow{ 
      //始点の座標、直径を保持する 
    public int start2_x,start2_y,r2; 
    // Circleのコンストラクタ 
    public Snow(int x1,int x2,int x3){ 
    start2_x =x1; 
    start2_y=x2; 
    r2=x3; 
    } 
} 
// 独立したウィンドウを開くので,Frameクラスのサブクラスにする 
// インタフェースの実装では 「implements インタフェース名」と書く 
// 複数書く場合は,カンマで区切る 

class Kadai1125 extends Frame implements KeyListener,MouseListener,MouseMotionListener{ 
    // Arc,Snowの配列を保持するVectorクラスの変数 
    Vector arcArray,snowArray; 
    // マウスをドラッグ中かどうかを示す boolean型(真偽値)の変数 draggingの宣言 
    boolean dragging; 
    // 表示する色を保持する変数 
    Color arcColor,snowColor; 
    int bx,by,ch; 
    Image image;
    //コンストラクタの宣言 
    public Kadai1125(String title) { 
    // 親クラス Frameのコンストラクタの宣言 
    super(title);  
    image=Toolkit.getDefaultToolkit().getImage("kadai1111.png");
    arcArray=new Vector(); 
    snowArray=new Vector();
    // ドラッグ中ではない 
    dragging=false;  
    arcColor=Color.white; 
    snowColor=Color.white;
    // GUI部品と,Event Listenerを関連づける 
    addKeyListener(this); 
    addMouseListener(this); 
    addMouseMotionListener(this); 
    } 
    public static void main(String[] args){ 
    Kadai1125 frame=new Kadai1125("Kadai1125"); 
    System.out.println("円弧を描くにaを押し、雪を描くにsを押す"); 
    frame.setSize(400,400); 
    frame.setVisible(true);   
    } 
    Image offScreenImage; 
    Graphics offScreenGraphics; 
    public void update(Graphics g){ 
    if(offScreenImage==null){ 
        // オフスクリーンイメージを400x400のサイズで作成 
        offScreenImage=createImage(400,400); 
        // オフスクリーンイメージに描画するための Graphics オブジェクト 
        offScreenGraphics=offScreenImage.getGraphics(); 
    } 
    paint(offScreenGraphics); // 次の画面のイメージを作る. 
    g.drawImage(offScreenImage,0,0,this); // イメージを本物のスクリーンに書き込                                          //む 
    } 
    //雪を描くためメソッド
    void drawsnow(Graphics g,int x,int y,int r) { 
       Graphics2D g2=(Graphics2D)g;
	// 座標系を45度(π/4)回転させる
        g2.translate(x,y);
        for(int i1=1;i1<=8;i1++) {
	g2.rotate(Math.PI/4);
	g2.drawLine(0,0,r,0);
        }
        //はじめの座標系を返す
        g2.translate(-x,-y);
    }
    public void paint(Graphics g){  
     g.drawImage(image,0,0,this);
     int i;
    //chの値により、何をマウスで描くか判定する 
     switch(ch){ 
         //雪を描く 
        case 1: 
         g.setColor(snowColor); 
        int size=snowArray.size(); 
        if(dragging) size--; 
        for(i=0;i< size;i++){ 
        Snow s=(Snow)snowArray.elementAt(i); 
        drawsnow(g,s.start2_x,s.start2_y,(int) Math.round(s.r2/2));
        } 
        // マウスをドラッグ中の時は 
        if(dragging){ 
        g.setColor(Color.white); 
        // snow[snowCount] を描画する. 
        Snow s=(Snow)snowArray.elementAt(i); 
        drawsnow(g,s.start2_x,s.start2_y,(int) Math.round((s.r2)/2));
        } 
        //ここでを"break"書くと同時に雪と円弧を表させることができない 
        //円弧を描く 
        case 2: 
        g.setColor(arcColor); 
        int size1=arcArray.size(); 
        if(dragging) size1--; 
        for(i=0;i< size1;i++){ 
        Arc a=(Arc)arcArray.elementAt(i); 
        //簡単のため、startAngleとarcAngleを指定しておく 
        g.drawArc(a.start1_x,a.start1_y,(int)(a.end1_x-a.start1_x),(int)(a.end1_y-a.start1_y),150,210); 
        } 
        // マウスをドラッグ中の時は 
        if(dragging){ 
        g.setColor(Color.white); 
        // arcs[arcCount] を描画する. 
        Arc a=(Arc)arcArray.elementAt(i); 
        g.drawArc(a.start1_x,a.start1_y,(int)(a.end1_x-a.start1_x), 
              (int)(a.end1_y-a.start1_y),150,210); 
        } 
        break; 
    } 
    } 
    //KeyListenerを実装するためのメソッド 
    public void keyPressed(KeyEvent e){ 
    // イベントからキーのコードを取り出す 
    int key=e.getKeyChar(); 
    // デバッグ用の表示 
    System.out.println("keyPressed("+e+","+key+")"); 
    // 入力が 'q'の時は終了する 
    if(key=='q') System.exit(0); 
    // 入力が 's'のときは雪を描く 
    if(key=='s') ch=1; 
    // 入力が'a'のときは円弧を描く 
     if(key=='a') ch=2; 
    } 
    // 要らないイベントに対応するメソッドも中身は空 
    //で書いておく必要があ る. 
    public void keyReleased(KeyEvent e){} 
    public void keyTyped(KeyEvent e){} 
    //MouseListenerを実装するためのメソッド 
    public void mousePressed(MouseEvent e){ 
    // 押された時のマウスカーソルの位置を得る 
    int mx=e.getX(),my=e.getY();
       switch(ch) { 
       case 1: 
        bx=mx; 
        by=my; 
        // デバッグ用の表示 
        System.out.println("mousePressed("+e+","+mx+","+my+")"); 
        // 配列snowsのsnowCount番目に雪を登録 
        snowArray.addElement(new Snow(bx,by,0)); 
        // ドラッグ中であることを示す 
        dragging=true; 
        // 再表示をおこなう 
        repaint(); 
        
        case 2: 
        // デバッグ用の表示 
        System.out.println("mousePressed("+e+","+mx+","+my+")"); 
        // 配列arcsのarcCount番目に円弧を登録 
        arcArray.addElement(new Arc(mx,my,mx,my)); 
        // ドラッグ中であることを示す 
        dragging=true; 
        // 再表示をおこなう 
        repaint(); 
        break; 
        } 
    } 
    // マウスのボタンが離された時のイベント 
    public void mouseReleased(MouseEvent e){ 
    // マウスカーソルの位置を得る 
        int mx=e.getX(),my=e.getY();
        switch(ch) {
        case 1:  
        // デバッグ用の表示 
        System.out.println("mousePressed("+e+","+mx+","+my+")"); 
        // 配列snowsのsnowCount番目の始点を変更 
        Snow s=(Snow)snowArray.elementAt(snowArray.size()-1); 
        //雪の直径を選ぶ 
        s.r2=Math.min((int)Math.abs(mx-bx),(int)Math.abs(my-by)); 
        dragging=false; 
        // 再表示をおこなう 
        repaint(); 
        break;
        case 2: 
        // デバッグ用の表示 
        System.out.println("mousePressed("+e+","+mx+","+my+")"); 
        // 配列arcsのarcCount番目の始点を変更 
        Arc a=(Arc)arcArray.elementAt(arcArray.size()-1); 
        a.end1_x=mx; 
        a.end1_y=my; 
        dragging=false; 
        // 再表示をおこなう 
        repaint(); 
        break; 
    } 
    } 
    public void mouseClicked(MouseEvent e){} 
    public void mouseEntered(MouseEvent e){} 
    public void mouseExited(MouseEvent e){} 
    public void mouseDragged(MouseEvent e){ 
    // マウスカーソルの位置を得る 
    int mx=e.getX(),my=e.getY(); 
    switch(ch) {
        case 1:
        // デバッグ用の表示 
        System.out.println("mouseDrag("+e+","+mx+","+my+")"); 
        // 配列snowsのsnowCount番目の始点を変更 
        Snow s=(Snow)snowArray.elementAt(snowArray.size()-1); 
        s.r2=Math.min((int)Math.abs(mx-bx),(int)Math.abs(my-by)); 
        // 再表示をおこなう 
        repaint(); 
        break;
        case 2:
        System.out.println("mouseDrag("+e+","+mx+","+my+")"); 
        // 配列arcsのarcCount番目の始点を変更 
        Arc a=(Arc)arcArray.elementAt(arcArray.size()-1); 
        a.end1_x=mx; 
        a.end1_y=my; 
        // 再表示をおこなう 
        repaint(); 
        break; 
       } 
    } 
    public void mouseMoved(MouseEvent e){} 
} 

コメント
前回の課題をキャプチャーしたものを背景に使うとは考えましたね.