import java.awt.*;
import java.applet.*;
import java.lang.Math.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;



public class denba extends JApplet{
    int hankei=10;
    int q1=3;
    int q2=2;
    int www=64;
    double x1=150;
    double x2=300;
    double y1=140;
    double y2=180;
    double N=5.0;
    double Vstep=4.0;

    boolean showFFlg=true;
    boolean showYFlg=false;
    boolean showY1Flg=false;
    boolean showY2Flg=false;
    boolean showEFlg=false;
    boolean showVFlg=false;

    int xmin,xmax,ymin,ymax,xv,yv;

    int nowDrag=0;

    GraphPanel canvas;
    JSlider sbQ1,sbQ2;
    JSlider sbW,sbN,sbV;

    JCheckBox cbE,cbY,cbY1,cbY2;
    JCheckBox cbV,cbF;
  

    JLabel l1;

    int w,h;
    int pw=-1,ph=-1;
    double delta;
    
    public void resetAll()
    {
	sbQ1.setValue(3);
	sbQ2.setValue(-2);
        sbN.setValue(5);
        sbV.setValue(4);
        q1=3;
        q2=-2;
        N=5.0;
    }
    
    public void init(){
	canvas=new GraphPanel();

	Container cont=getContentPane();
	cont.setLayout(new BorderLayout());

	sbW=new JSlider();
	sbW.setMaximum(128);
	sbW.setMinimum(32);
        sbW.setValue(64);

	sbW.addChangeListener(new ChangeListener() {
		public void stateChanged(ChangeEvent evt) {
		    setW(sbW.getValue());
		}
	    });


	
	sbQ1=new JSlider();
	sbQ1.setMaximum(5);
	sbQ1.setMinimum(-5);
        sbQ1.setMajorTickSpacing(5);
        sbQ1.setMinorTickSpacing(1);
        sbQ1.setPaintLabels(true);
        sbQ1.setPaintTicks(true);
        sbQ1.setSnapToTicks(true);

	sbQ2=new JSlider();
	sbQ2.setMaximum(5);
	sbQ2.setMinimum(-5);
        sbQ2.setMajorTickSpacing(5);
        sbQ2.setMinorTickSpacing(1);
        sbQ2.setPaintLabels(true);
        sbQ2.setPaintTicks(true);
        sbQ2.setSnapToTicks(true);
    
	sbN=new JSlider();
	sbN.setMaximum(8);
	sbN.setMinimum(1);
        sbN.setMajorTickSpacing(1);
        sbN.setMinorTickSpacing(1);
        sbN.setPaintLabels(true);
        sbN.setPaintTicks(true);
        sbN.setSnapToTicks(true);    


	sbN.addChangeListener(new ChangeListener() {
		public void stateChanged(ChangeEvent evt) {
		    setN(sbN.getValue());
		}
	    });

	sbV=new JSlider();
	sbV.setMaximum(8);
	sbV.setMinimum(1);
        sbV.setMajorTickSpacing(1);
        sbV.setMinorTickSpacing(1);
        sbV.setPaintLabels(true);
        sbV.setPaintTicks(true);
        sbV.setSnapToTicks(true);    


	sbV.addChangeListener(new ChangeListener() {
		public void stateChanged(ChangeEvent evt) {
		    setV(sbV.getValue());
		}
	    });

	sbQ1.addChangeListener(new ChangeListener() {
		public void stateChanged(ChangeEvent evt) {
		    setQ1(sbQ1.getValue());
		}
	    });

	sbQ2.addChangeListener(new ChangeListener() {
		public void stateChanged(ChangeEvent evt) {
		    setQ2(sbQ2.getValue());
		}
	    });
    
        canvas.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                mPressed(e);
            }
            public void mouseReleased(MouseEvent e) {
                mReleased(e);
            }
        });
        canvas.addMouseMotionListener(new MouseMotionAdapter() {
            public void mouseDragged(MouseEvent e) {
                mDragged(e);
            }
        });


	JPanel p=new JPanel();
	p.setLayout( new GridLayout(3,2));
    
	JPanel p11=new JPanel();
	p11.setLayout(new BorderLayout());
	
	l1=new JLabel("電荷１：");
        l1.setForeground(Color.BLUE);
    
	p11.add(l1,BorderLayout.WEST);
	p11.add(sbQ1,BorderLayout.CENTER);


	JPanel p12=new JPanel();
	p12.setLayout(new BorderLayout());

	JLabel l2=new JLabel("電荷２：");
        l2.setForeground(new Color(0,130,0));
    
	p12.add(l2,BorderLayout.WEST);
	p12.add(sbQ2,BorderLayout.CENTER);


	cbE=new JCheckBox("電気力線を描く",false);

	cbE.addItemListener(new java.awt.event.ItemListener() {
		public void itemStateChanged(java.awt.event.ItemEvent evt) {
		    ChangeEFlg(cbE.isSelected());
		}
	    });    

	cbV=new JCheckBox("等電位線を描く",false);

	cbV.addItemListener(new java.awt.event.ItemListener() {
		public void itemStateChanged(java.awt.event.ItemEvent evt) {
		    ChangeVFlg(cbV.isSelected());
		}
	    });    

        cbY=new JCheckBox("合成電場",false);

	cbY.addItemListener(new java.awt.event.ItemListener() {
		public void itemStateChanged(java.awt.event.ItemEvent evt) {
		    ChangeYFlg(cbY.isSelected());
		}
	    });    

        cbF=new JCheckBox("力を描く",true);

	cbF.addItemListener(new java.awt.event.ItemListener() {
		public void itemStateChanged(java.awt.event.ItemEvent evt) {
		    ChangeFFlg(cbF.isSelected());
		}
	    });    


        cbY1=new JCheckBox("電荷1の電場",false);

	cbY1.addItemListener(new java.awt.event.ItemListener() {
		public void itemStateChanged(java.awt.event.ItemEvent evt) {
		    ChangeY1Flg(cbY1.isSelected());
		}
	    });    

        cbY2=new JCheckBox("電荷2の電場",false);

	cbY2.addItemListener(new java.awt.event.ItemListener() {
		public void itemStateChanged(java.awt.event.ItemEvent evt) {
		    ChangeY2Flg(cbY2.isSelected());
		}
	    });    


	JPanel p13=new JPanel();
	p13.setLayout(new BorderLayout());
	p13.add(cbE,BorderLayout.WEST);
	p13.add(sbN,BorderLayout.CENTER);


        JPanel p14=new JPanel();
	p14.setLayout(new BorderLayout());
        p14.add(cbV,BorderLayout.WEST);
        p14.add(sbV,BorderLayout.CENTER);


	p.add(p11);
	p.add(p12);
	p.add(p13);
        p.add(p14);

        JPanel p15=new JPanel();
        
        p15.add(cbF);
        p15.add(cbY);
        p15.add(cbY1);
        p15.add(cbY2);

        p.add(p15);
        p.add(sbW);

        resetAll();
    	
	cont.add("South",p);
	cont.add("Center",canvas);

	start();
    }

    class GraphPanel extends JPanel {
	public void paintComponent(Graphics g){
            if ( g != null ) {
                writeCanvas(g);
            }
	}
    }
    
    

    public void mPressed(MouseEvent e) {
        int mx=e.getX();
        int my=e.getY();
        int xx=(int)x1;
        int yy=(int)y1;
        if( mx > xx-hankei && mx < xx+hankei && my > yy-hankei && my < yy+hankei ) {
            nowDrag=1;
        }

        xx=(int)x2;
        yy=(int)y2;
        if( mx > xx-hankei && mx < xx+hankei && my > yy-hankei && my < yy+hankei ) {
            nowDrag=2;
        }
    }

    public void mReleased(MouseEvent e) {
        nowDrag = 0;
    }

    public void mDragged(MouseEvent e) {
        if( nowDrag == 0 ) {
            return;
        } else if( nowDrag == 1 ) {
            x1=(double)e.getX();
            y1=(double)e.getY();
            if( x1 < hankei ) {
                x1=hankei;
            }
            if( x1 > w-hankei ) {
                x1= w-hankei;
            }
            if( y1 < hankei ) {
                y1=hankei;
            }
            if( y1 > h-hankei ) {
                y1= h-hankei;
            }
        } else if( nowDrag == 2 ) {
            x2=(double)e.getX();
            y2=(double)e.getY();
            if( x2 < hankei ) {
                x2=hankei;
            }
            if( x2 > w-hankei ) {
                x2= w-hankei;
            }
            if( y2 < hankei ) {
                y2=hankei;
            }
            if( y2 > h-hankei ) {
                y2= h-hankei;
            }
        }
        repaint();
    }

    public void ChangeEFlg(boolean a) {
	showEFlg=a;
	repaint();
    }

    public void ChangeYFlg(boolean a) {
	showYFlg=a;
	repaint();
    }

    public void ChangeY1Flg(boolean a) {
	showY1Flg=a;
	repaint();
    }

    public void ChangeY2Flg(boolean a) {
	showY2Flg=a;
	repaint();
    }


    public void ChangeVFlg(boolean a) {
	showVFlg=a;
	repaint();
    }

    public void ChangeFFlg(boolean a) {
	showFFlg=a;
	repaint();
    }

    public void setQ1(int n) {
	q1=n;
	repaint();
    }

    public void setN(int n) {
	N=n;
	repaint();
    }

    public void setW(int n) {
	www=n;
	repaint();
    }

    public void setV(int n) {
	Vstep=n;
	repaint();
    }

    public void setQ2(int n) {
	q2=n;
	repaint();
    }

    boolean SameSegment(double xx,double yy,double xxx,double yyy)
    {
        double angle1,angle2,angle3,angle4;
        if( Math.abs(xx-x1)< 2.0 && y1>yy) {
               // 角度がπや-πになる可能性のある、危険なところ。
            angle1=Math.atan2(x1-xx,y1-yy)+Math.PI;
            angle3=Math.atan2(x1-xxx,y1-yyy)+Math.PI;
        } else {
            angle1=Math.atan2(xx-x1,yy-y1);
            angle3=Math.atan2(xxx-x1,yyy-y1);
        }
        if( Math.abs(xx-x2) < 2.0 && y2>yy) {
            angle2=Math.atan2(x2-xx,y2-yy)+Math.PI;
            angle4=Math.atan2(x2-xxx,y2-yyy)+Math.PI;
        } else {
            angle2=Math.atan2(xx-x2,yy-y2);
            angle4=Math.atan2(xxx-x2,yyy-y2);
        }
        int inte = (int)Math.floor((q1*angle1+q2*angle2)/(2.0*Math.PI)*N);
        int inte2 = (int)Math.floor((q1*angle3+q2*angle4)/(2.0*Math.PI)*N);
        return(inte == inte2);
    } 

    void writeRikisenFromTop(int x,int y,Graphics gc)
    {
        double xs=(double)x;
        double xe=(double)(x+1);
        double ys=(double)y;
        double ye=(double)(y+1);

        if( SameSegment(xs,ys,xe,ys) ) {
            // 上から電気力線は通ってこなかった。
            return;
        }

        
        gc.setColor(Color.RED);
        gc.fillRect(x,y,1,1);
        if( (q1 != 0 &&(x-x1)*(x-x1)+(y-y1)*(y-y1) < hankei*hankei )
            ||(q2 !=0 && (x-x2)*(x-x2)+(y-y2)*(y-y2) < hankei*hankei )
            ) {
            // 電荷から近いところに来たら終了。
            return;
        }
        if( !SameSegment(xs,ye,xe,ye) ) {
            // 下に通った。
            if( y < h-1 ) {
                writeRikisenFromTop(x,y+1,gc);
            }
        }
        if( !SameSegment(xs,ys,xs,ye) ) {
            // 左に通った。
            if( x > 0 ) {
                writeRikisenFromRight(x-1,y,gc);
            }
        }
        if( !SameSegment(xe,ys,xe,ye) ) {
            if( x < w-1 ) {
                writeRikisenFromLeft(x+1,y,gc);
            }
        }
    }

    void writeRikisenFromBottom(int x,int y,Graphics gc)
    {
        double xs=(double)x;
        double xe=(double)(x+1);
        double ys=(double)y;
        double ye=(double)(y+1);

        if( SameSegment(xs,ye,xe,ye) ) {
            // 下から電気力線は通ってこなかった。
            return;
        }

        
        gc.setColor(Color.RED);
        gc.fillRect(x,y,1,1);
        if( (q1!=0 &&(x-x1)*(x-x1)+(y-y1)*(y-y1) < hankei*hankei )
            ||
            (q2!=0 && (x-x2)*(x-x2)+(y-y2)*(y-y2) < hankei*hankei )
            ) {
            // 電荷から近いところに来たら終了。
            return;
        }
        if( !SameSegment(xs,ys,xe,ys) ) {
            // 上に通った。
            if( y > 0  ) {
                writeRikisenFromBottom(x,y-1,gc);
            }
        }
        if( !SameSegment(xs,ys,xs,ye) ) {
            // 左に通った。
            if( x > 0 ) {
                writeRikisenFromRight(x-1,y,gc);
            }
        }
        if( !SameSegment(xe,ys,xe,ye) ) {
            // 右に通った。
            if( x < w-1 ) {
                writeRikisenFromLeft(x+1,y,gc);
            }
        }
    }

    void writeRikisenFromRight(int x,int y,Graphics gc)
    {
        double xs=(double)x;
        double xe=(double)(x+1);
        double ys=(double)y;
        double ye=(double)(y+1);

        if( SameSegment(xe,ys,xe,ye) ) {
            // 右から電気力線は通ってこなかった。
            return;
        }

        
        gc.setColor(Color.RED);
        gc.fillRect(x,y,1,1);

        if( (q1 != 0 &&(x-x1)*(x-x1)+(y-y1)*(y-y1) < hankei*hankei ) 
            ||(q2 !=0 &&(x-x2)*(x-x2)+(y-y2)*(y-y2) < hankei*hankei )) {
            // 電荷から近いところに来たら終了。
            return;
        }

        if( !SameSegment(xs,ye,xe,ye) ) {
            // 下に通った。
            if( y < h-1 ) {
                writeRikisenFromTop(x,y+1,gc);
            }
        }

        if( !SameSegment(xs,ys,xs,ye) ) {
            // 左に通った。
            if( x > 0 ) {
                writeRikisenFromRight(x-1,y,gc);
            }
        }
        if( !SameSegment(xs,ys,xe,ys) ) {
            // 上に通った。
            if( y > 0 ) {
                writeRikisenFromBottom(x,y-1,gc);
            }
        }
    }

    void writeRikisenFromLeft(int x,int y,Graphics gc)
    {
        double xs=(double)x;
        double xe=(double)(x+1);
        double ys=(double)y;
        double ye=(double)(y+1);

        if( SameSegment(xs,ys,xs,ye) ) {
            // 左から電気力線は通ってこなかった。
            return;
        }

        
        gc.setColor(Color.RED);
        gc.fillRect(x,y,1,1);

        if( (q1!=0 && (x-x1)*(x-x1)+(y-y1)*(y-y1) < hankei*hankei )
            ||(q2 !=0 && (x-x2)*(x-x2)+(y-y2)*(y-y2) < hankei*hankei )
            ) {
            // 電荷から近いところに来たら終了。
            return;
       }


        if( !SameSegment(xs,ye,xe,ye) ) {
            // 下に通った。
            if( y < h-1 ) {
                writeRikisenFromTop(x,y+1,gc);
            }
        }
        if( !SameSegment(xs,ys,xe,ys) ) {
            // 上に通った。
            if( y > 0  ) {
                writeRikisenFromBottom(x,y-1,gc);
            }
        }

        if( !SameSegment(xe,ys,xe,ye) ) {
            // 右に通った。
            if( x < w-1 ) {
                writeRikisenFromLeft(x+1,y,gc);
            }
        }
    }


    double V(int x,int y) {
        return( q1*Math.log((x-x1)*(x-x1)+(y-y1)*(y-y1))
                +q2*Math.log((x-x2)*(x-x2)+(y-y2)*(y-y2))
                );
    }

    void drawYajirusi(Graphics g,int x,int y,double angle,int len, int w)
    {
        Polygon p=new Polygon();

        int x1=(int)(-w*Math.sin(angle)/2);
        int y1=(int)(-w*Math.cos(angle)/2);
        int x2=(int)(len*Math.cos(angle)/2);
        int y2=(int)(-len*Math.sin(angle)/2);
        int xl=(int)(len*Math.cos(angle));
        int yl=(int)(-len*Math.sin(angle));

        g.drawLine(x,y,x+xl,y+yl);

        p.addPoint(x+xl,y+yl);
        p.addPoint(x+x2+x1,y+y2+y1);
        p.addPoint(x+x2-x1,y+y2-y1);
        
        g.fillPolygon(p);
    }


    public void writeCanvas(Graphics gc){
        if( gc == null ) {
            return;
        }

	w=canvas.getWidth();
	h=canvas.getHeight();

        int x,y;

	if( w !=pw ) { pw=w;}
	if( h !=ph ) { ph=h;}


        ymin=0;
        ymax=h;
        xmin=0;
        xmax=w;

	gc.setColor(Color.WHITE);
	gc.fillRect(0,0,w,h);

        double mitudo=1.0/N;
        double gauge=mitudo*0.5;
        double gaugev=0.0;

        int x1i=(int)x1;
        int x2i=(int)x2;
        int y1i=(int)y1;
        int y2i=(int)y2;

        xv= ((int)(x1+x2))/2;
        yv= ((int)(y1+y2))/2;


        if( showYFlg || showY1Flg || showY2Flg ) {
            int ww=www/12;

            for( x=www; x < w ; x+=www ) {
                for( y=www; y< h ; y+=www ) {
                    double r1 =1.0/((x-x1)*(x-x1)+(y-y1)*(y-y1));
                    double xxx=q1*1000*(x-x1)*r1;
                    double yyy=q1*1000*(y-y1)*r1;

                    if( showY1Flg ) {
                        gc.setColor(Color.BLUE);
                        drawYajirusi(gc,x,y,Math.atan2(-yyy,xxx),(int)Math.sqrt(xxx*xxx+yyy*yyy),ww);
                    }

                    double r2 =1.0/((x-x2)*(x-x2)+(y-y2)*(y-y2));
                    double xxx2=q2*1000*(x-x2)*r2;
                    double yyy2=q2*1000*(y-y2)*r2;

                    if( showY2Flg ) {
                        gc.setColor(new Color(0,130,0));
                        drawYajirusi(gc,x,y,Math.atan2(-yyy2,xxx2),(int)Math.sqrt(xxx2*xxx2+yyy2*yyy2),ww);
                    }


                    if( showYFlg ) {
                        gc.setColor(Color.RED);
                        drawYajirusi(gc,x,y,Math.atan2(-yyy-yyy2,xxx+xxx2),
                                     (int)Math.sqrt((xxx+xxx2)*(xxx+xxx2)+(yyy+yyy2)*(yyy+yyy2)),ww);
                    }

                }
            }
        }
        if( showEFlg ) {

            if( q1*q2 < 0 ) {
                if( Math.abs(x1-x2)>Math.abs(y1-y2) ) {
                    if( xv == x1i || xv == x2i ) {
                        // 二つの電荷が同じ場所にあるなら何もしなくていい。
                        xv =-1;
                        yv =-1;
                    } else {
                        yv =-1;
                    }
                } else {
                    if( yv == y1i || yv == y2i ) {
                        xv =-1;
                        yv =-1;
                    } else {
                        xv=-1;
                    }
            }
            } else {
                xv =-1;
                yv =-1;
            }
            
            
            
            for( x=0 ; x<w ; x++ ) {
                writeRikisenFromTop(x,0,gc);
            }
            for( y=0 ; y<h ; y++ ) {
                writeRikisenFromRight(w-1,y,gc);
            }
            for( x=w-1 ; x>=0 ; x-- ) {
                writeRikisenFromBottom(x,h-1,gc);
            }
            for( y=h-1 ; y>1 ; y-- ) {
                writeRikisenFromLeft(0,y,gc);
            }
            
            
            
            if( xv > 0 ) {
                for( y=0 ; y<h-1 ; y++ ) {
                    writeRikisenFromLeft(xv,y,gc);
                    writeRikisenFromRight(xv,y,gc);
                }
            }
            
            if( yv > 0 ) {
                for( x=w-1 ; x > 0 ; x-- ) {
                    writeRikisenFromTop(x,yv,gc);
                    writeRikisenFromBottom(x,yv,gc);
                }
            }
        }

        gc.setColor(Color.BLACK);
        if( showVFlg ) {
            int[][] V;
            V = new int[w+1][h+1];
            
            for( x=0 ; x<= w ; x++ ) {
                for( y=0 ; y<= h ; y++ ) {
                    if( (x-x1)*(x-x1)+(y-y1)*(y-y1) > hankei*hankei 
                        &&(x-x2)*(x-x2)+(y-y2)*(y-y2) > hankei*hankei ) {
                        V[x][y]=(int)Math.floor(0.25*Vstep*V(x,y));
                    }
                }
            }
            for( x=0 ; x< w ; x++ ) {
                for( y=0 ; y< h ; y++ ) {
                    if( (x-x1)*(x-x1)+(y-y1)*(y-y1) > hankei*hankei 
                        &&(x-x2)*(x-x2)+(y-y2)*(y-y2) > hankei*hankei ) {
                        if( 
                            V[x][y] != V[x][y+1]
                            ||V[x][y] != V[x+1][y]
                            ||V[x][y] != V[x+1][y+1]
                            ){
                                gc.fillRect(x,y,1,1);
                        }
                    }
                }
            }
        }

        gc.setColor(Color.BLUE);

        gc.fillOval((int)x1-hankei,(int)y1-hankei,hankei*2,hankei*2);

        gc.setColor(new Color(0,130,0));

        gc.fillOval((int)x2-hankei,(int)y2-hankei,hankei*2,hankei*2);

        gc.setColor(Color.WHITE);
        if( q1 != 0 ) {
            gc.drawLine((int)x1-hankei+3,(int)y1,(int)x1+hankei-2,(int)y1);
            if( q1 > 0 ) {
                gc.drawLine((int)x1,(int)y1-hankei+2,(int)x1,(int)y1+hankei-2);
            }
        }
        if( q2 != 0 ) {
            gc.drawLine((int)x2-hankei+2,(int)y2,(int)x2+hankei-2,(int)y2);
            if( q2 > 0 ) {
                gc.drawLine((int)x2,(int)y2-hankei+2,(int)x2,(int)y2+hankei-2);
            }
        }
        if( showFFlg ) {
            int f=(int)(200000.0*q1*q2/((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)));

            gc.setColor(Color.MAGENTA);
            drawYajirusi(gc,(int)x1,(int)y1,Math.atan2(y2-y1,x1-x2),f,8);
            gc.setColor(Color.GREEN);
            drawYajirusi(gc,(int)x2,(int)y2,Math.atan2(y1-y2,x2-x1),f,10);
        }
    }
}

