import java.applet.*;
import java.awt.*;
import java.awt.event.*;

/*
<applet code="bezier.class" width=480 height=480>
</applet>
*/

public class bezier extends Applet
{
    static int WIDTH, HEIGHT;        // 画面サイズ
    Point point[][];                 // 制御点
    int point_num = 0;               // 制御点の番号
    int captured_point = -1;         // つかんでいる制御点の番号
                                     //（-1のときつかんでいない）
    boolean capture_mode_FLG;        // 線を制御できるかを表すフラグ

    static final int point_data[][][] =    // 点情報
	{
		{{75,250},{95,165},{140,175},{180,120}},
		{{172,135},{140,180},{115,180},{100,200}},
		{{75,250},{93,228},{82,230}},
		{{90,235},{95,230},{160,227}},
		{{93,248},{100,240},{157,230}},
		{{90,225},{95,215},{120,205}},
		{{122,197},{125,201}},
		{{125,201},{140,195}},
		{{140,195},{137,188}},
		{{137,188},{122,197}},
		//10
		{{160,165},{165,180},{140,167},{155,195}},
		{{150,200},{172,190},{185,175}},
		{{185,175},{180,165},{190,155}},
		{{190,155},{198,155}},
		{{198,155},{190,165},{195,175}},
		{{185,175},{190,170},{195,188}},
		{{195,188},{175,195},{165,210}},
		{{185,175},{197,170},{200,180}},
		{{200,180},{195,188}},
		{{200,180},{210,181},{220,185}},
		//20
		{{220,185},{212,187},{215,195}},
		{{215,195},{205,190},{195,188}},
		{{180,120},{205,145},{57,210},{200,200},{170,225}},
		{{170,225},{155,230},{195,285},{45,230},{175,325}},
		{{170,205},{190,215},{170,215},{190,255},{135,280}},
		{{170,255},{180,290},{250,280},{230,265}},
		{{75,250},{100,280},{135,295}},
		{{170,321},{270,320},{335,320},{340,265}},
		{{220,260},{239,275},{330,297},{303,248},{350,250}},
		{{220,260},{250,230}},
		//30
		{{252,232},{232,263}},
		{{250,230},{348,235},{295,170},{350,185},{345,215}},
		{{345,215},{335,237},{315,235}},
		{{345,215},{385,225},{385,190},{420,195}},
		{{325,200},{315,209},{325,217},{310,235}},
		{{275,250},{300,225},{375,320},{290,122},{390,280},{360,235},{425,195},{395,200}},
		{{420,195},{400,250},{375,230},{350,250}},
		{{415,210},{450,208},{420,190},{415,275},{375,210},{440,270},{330,260}},
		{{415,230},{475,280},{170,270},{465,460},{350,190},{295,420},{290,310},{150,340}},
		{{160,340},{160,320},{140,320}},
		//40
		{{75,320},{115,285}},
		{{80,325},{120,290}},
		{{75,320},{80,325}},
		{{105,295},{110,300}},
		{{140,320},{140,300},{120,290}},
		{{115,285},{80,270},{50,295}},
		{{50,295},{80,290},{97,297}},
		{{140,320},{125,305},{110,300}},
		{{54,295},{0,370},{136,415},{135,316}},
		{{94,301},{30,290},{35,418},{160,350},{99,304}},
		//50
		{{420,315},{350,315}},
		{{420,315},{425,320},{418,335}},
		{{418,335},{290,340},{330,328},{300,335}},
		{{415,315},{420,325},{410,330}},
		{{360,315},{355,320},{363,330}},
		{{363,330},{386,325},{410,330}},
		{{330,335},{340,385},{405,390},{415,335}},
		{{413,315},{401,287},{370,287}},
		{{345,335},{353,368},{397,362},{400,335}},
		{{398,316},{382,293},{352,308}},
		//60
		{{373,285},{400,295},{390,275},{375,290}},
		{{375,290},{400,300},{390,280},{375,295}},
		{{375,295},{400,305},{390,285},{375,300}},
		{{375,300},{400,310},{390,290},{375,305}},
		{{375,305},{400,315},{390,295},{375,310}},
		{{375,310},{400,320},{390,300},{375,315}},
		{{345,275},{370,260},{395,260}},
		{{395,250},{405,255},{420,240}},
		{{180,315},{355,315},{255,290},{320,295}},
		{{180,285},{255,277},{330,280}},
		//70
		{{390,240},{405,240},{410,220},{417,220}},
		{{408,258},{430,300}},
		{{406,260},{428,302}},
		{{430,300},{428,302}},
	};

    Label tf;

    Graphics offg, backg;
    Image    offi, backi;

    public void initialize()                   // 制御点の設定
    {
        int i;
        point = new Point[point_data.length][];

        for(i = 0; i < point.length; i++)
        {
            point[i] = new Point[point_data[i].length];
            for(int j = 0; j < point_data[i].length; j++)
                point[i][j] = new Point(point_data[i][j][0],
                                        point_data[i][j][1]);
        }
    }
    
    public void init()
    {
        Dimension d = getSize();
        WIDTH = d.width; HEIGHT = d.height;

        Button increment, decrement, modify;
        increment = new Button("increment");          // 画面設定
        decrement = new Button("decrement");
        modify = new Button("modify");
        tf = new Label(Integer.toString(point_num));
        add(modify);
        add(increment);
        add(decrement);
        add(tf);

        increment.addActionListener(new ActionListener() // 制御点番号を増やす
        {
            public void actionPerformed(ActionEvent ae)
            {
                point_num++;
                if(point_num >= point.length)
                    point_num = 0;
                tf.setText(Integer.toString(point_num));
                repaint();
            }
        });

        decrement.addActionListener(new ActionListener()  // 制御点番号を減らす
        {
            public void actionPerformed(ActionEvent ae)
            {
                point_num--;
                if(point_num < 0) point_num = point.length-1;
                tf.setText(Integer.toString(point_num));
                repaint();
            }
        });

        modify.addActionListener(new ActionListener()  // モード変更
        {
            public void actionPerformed(ActionEvent ae)
            {
                capture_mode_FLG = !capture_mode_FLG;
                repaint();
            }
        });

        addMouseListener(new MouseAdapter()
        {
            public void mousePressed(MouseEvent me)    // 制御点をつかむ
            {
                int i, mx = me.getX(), my = me.getY();
                for(i = 0; i < point[point_num].length; i++)
                {
                    if(point[point_num][i].x-3 <= mx &&
                       point[point_num][i].x+3 >= mx &&
                       point[point_num][i].y-3 <= my &&
                       point[point_num][i].y+3 >= my)
                        break;
                }
                if(i < point[point_num].length) captured_point = i;
            }

            public void mouseReleased(MouseEvent me)
            {
                captured_point = -1;
            }
        });

        addMouseMotionListener(new MouseMotionAdapter() // 制御点を動かす
        {
            public void mouseDragged(MouseEvent me)
            {
                if(captured_point == -1) return;
                point[point_num][captured_point].setLocation(me.getX(), me.getY());
                repaint();
            }
        });

        initialize();

        offi = createImage(WIDTH, HEIGHT);
        offg = offi.getGraphics();
        backi = createImage(WIDTH, HEIGHT);
        backg = backi.getGraphics();
        setBackImage(backg);

     // test!
        addKeyListener(new KeyAdapter()
        {
            public void keyPressed(KeyEvent ke)
            {
                int c = ke.getKeyCode();
                if(c == 'P')
                {
                    System.out.println("{");
                    for(int i = 0; i < point[point_num].length; i++)
                        System.out.println(""+point[point_num][i].x
                                        +", "+point[point_num][i].y);
                    System.out.println("},");
                }
                else if(c == 'B')
                {
                    // BezierCurve(backg, Color.black);
                    repaint();
                }
            }
        });
    }

    public void setBackImage(Graphics g)    // 背景の用意
    {
        g.setColor(Color.white);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(Color.black);
        g.drawRect(0, 0, WIDTH-1, HEIGHT-1);  // 枠描画
    }

    public int kaijyo(int num)            // 階乗を計算
    {
        int total = 1;

        if(num == 0) return 1;

        for(; num > 1; num--)
            total *= num;
        return total;
    }

    public void BezierCurve(Graphics g, Color color, Point point[])  // 曲線描画
    {
        int i, j;
        double x1, x2, y1, y2;
        double bernstein[] = new double[point.length];
        x1 = point[0].x;
        y1 = point[0].y;

        g.setColor(color);

        for(double t = 0; t <= 1; t += 0.01)
        {
            for(i = 0; i < point.length; i++)        // bernstein関数を計算
            {
                bernstein[i] = (double)kaijyo(point.length-1)
                            / (kaijyo(i)*kaijyo(point.length-1-i));

                for(j = 1; j <= i; j++) bernstein[i] *= t;
                for(j = 1; j <= point.length-1-i; j++) bernstein[i] *= (1-t);
            }

            x2 = y2 = 0;
            for(i = 0; i < point.length; i++)
            {
                x2 += point[i].x * bernstein[i];
                y2 += point[i].y * bernstein[i];
            }

            g.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
            x1 = x2;
            y1 = y2;
        }
    }
    
    public void paint(Graphics g)
    {
        requestFocus();
        offg.drawImage(backi, 0, 0, this);  // 背景の描画

        if(capture_mode_FLG)           // 制御点の描画
        {
            offg.setColor(Color.red);
            for(int i = 0; i < point[point_num].length; i++)
            {
                offg.fillRect(point[point_num][i].x-3,
                              point[point_num][i].y-3, 7, 7);
                offg.drawString(Integer.toString(i+1),
                              point[point_num][i].x-3,
                              point[point_num][i].y-6);
            }
        }

        for(int i = 0; i < point.length; i++)     // 曲線描画
        {
            if(capture_mode_FLG && point_num == i)
                BezierCurve(offg, Color.blue, point[i]);
            else BezierCurve(offg, Color.black, point[i]);
        }

        g.drawImage(offi, 0, 0, this);
    }

    public void update(Graphics g)
    {
        paint(g);
    }
}

