2003年12月9日の課題



地面には左右対称で色違いの雪だるまを2体を、空には画像を取り込んだ半月を描画します。
空をクリックすると月が回転移動し、地面をクリックすると木が生えていきます。
右側のスクロールバーで木の葉の色を変えられます。また、描いた木を消すこともできます。
アンチエイリアスを効かせたり、回転時に補完処理をすることで、画質の向上を図っています。


使用画像(moon.png)

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

// 木クラス
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;
    }
}


// Canvasクラスのサブクラス、MyCanvas
// KeyListener,MouseListenerをインプリメント
class MyCanvas extends Canvas implements KeyListener, MouseListener{
    public Image image,showImage;
    // Vectorクラスの変数 treeArray,addNumArrayの宣言
    Vector treeArray;
    int[] addNum;
    int arraySize;
    // 木の高さ決定用ランダム
    Random ran=new Random();
    // 月描画用変数の宣言
    int moonX,moonY;
    // コンストラクタの宣言
    public MyCanvas(Image gotImage){
        super();
        image=gotImage;
        // treeArrayのインスタンスを作成
        treeArray=new Vector();
        addNum=new int[10];
        arraySize=0;
        // サイズは600*600
        setSize(600,600);
        // 月の初期位置は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){
        if(e.getY()>300&&e.getX()<600){
            // 下半分をクリックした時は、奥から順に木を追加する
            Tree t;
            // 描いた順を保存する配列が少ない時は倍にする
            if (arraySize>=addNum.length){
                int[] newaddNum=new int[arraySize*2];
                System.arraycopy(addNum,0,newaddNum,0,arraySize);
                addNum=newaddNum;
            }
            for(addNum[arraySize]=0;addNum[arraySize]e.getY())break;
            }
            treeArray.add(addNum[arraySize],new Tree(e.getX(),e.getY(),
            // 木の高さは、奥行きに応じた標準サイズに対し標準偏差6.7%で分布
            (int)((e.getY()*4/3-350)*(ran.nextGaussian()+15)/15)));
            arraySize++;
            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){}
    
    // "木を1本削除"ボタンが押された時の処理
    public void deleteTree(){
        if(arraySize>=0){
            treeArray.removeElementAt(addNum[arraySize-1]);
            arraySize--;
            repaint();
        }
    }
    
    // "木を全部削除"ボタンが押された時の処理
    public void clearTree(){
        treeArray.removeAllElements();
        arraySize=0;
        repaint();
    }

    
    
    // オフスクリーン描画用変数の宣言
    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){
        if(g.equals(offScreenGraphics)){
            // 木の追加・削除等でoffScreenを描き直すとき
            // 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);
            g2.setColor(new Color(160,165,170));
            g2.fillRect(600,0,150,600);
        }else if(offScreenImage==null){
            //最初はこちら
            update(g);
        }else{
            //単なる再描画のときはこちら
            g.drawImage(offScreenImage,0,0,this);
        }    
    }

    // 月回転メソッド
    void rotateImage(int pressedX){
        // 画像情報を入出力する配列の宣言
        int[] pixels = new int[101*101];
        int[] showPixels = new int[71*71];
        // 月の横位置から角度angleを宣言・代入
        double angle=Math.acos(pressedX/300.0-1.0);
        // angleのcos,sinを宣言・代入
        double sin=Math.sin(angle),cos=Math.cos(angle);
        // 新しい座標とその小数点以下の変数の宣言
        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);
            // バケツここまで
        }
    }

    public Color treeColor=new Color(26,120,29);
    // trees 描画メソッド
    void drawTrees(Graphics2D g2){
        Tree t;
        for(int i=0;i