import java.awt.*;
import java.awt.event.*;
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 charges extends JApplet{

    class Charges {

	class Charge {
	    int x;
	    int y;
	    int q;
	    Charge(int xx,int yy,int qq) { x=xx; y=yy; q=qq;}
	    public int X() { return x;}
	    public int Y() { return y;}
	    public int Q() { return q;}
	    public void setX(int xx) { x=xx;}
	    public void setY(int yy) { y=yy;}
	}

	java.util.List<Charge> chargeList;

        Charges() {
	    chargeList=Collections.synchronizedList(new ArrayList<Charge>());
            Clear();
        }

        void Clear() {
	    stopCalc();
	    synchronized(chargeList) {
		if( chargeList.size() > 0 ) {
		    chargeList.clear();
		}
	    }
	}

        void addNew(int xx,int yy,int qq)
        {
	    synchronized(chargeList) {
		chargeList.add( new Charge(xx,yy,qq) );
	    }
        }

        public int count() { 
	    synchronized(chargeList) {
		return chargeList.size();
	    }
        }

	public int X(int i) {
            return chargeList.get(i).X();
        }
        public int Y(int i) {
            return chargeList.get(i).Y();
        }
        public int Q(int i) {
            return chargeList.get(i).Q();
        }

        public double V(int xx,int yy) {
            double V=1.0;
            int i;
            for( i=0 ; i<Q.count() ; i++ ) {
		int x1=Q.X(i);
		int y1=Q.Y(i);
		if( Q(i) >0 ) {
		    V *= (double)((xx-x1)*(xx-x1)+(yy-y1)*(yy-y1));
                } else {
		    V /= (double)((xx-x1)*(xx-x1)+(yy-y1)*(yy-y1));
		}
	    }
	    return(-Math.log(V));
	}

        public void setPoint(int xx,int yy,int i)
        {
            if( i>=chargeList.size() ) {
                return;
            }
            if( xx < hankei ) {
                xx=hankei;
            }
            if( xx > w-hankei ) {
                xx=w-hankei;
            }
            if( yy < hankei ) {
                yy=hankei;
            }
            if( yy > h-hankei ) {
                yy=h-hankei;
            }
	    chargeList.get(i).setX(xx); 
	    chargeList.get(i).setY(yy);
        }

        public double Qangle(double xx,double yy)
        {
            double angle1;
            int i;
            int n=Q.count();
        
            if( n==0 ) {
                return 0.0;
            }
            double k1=0.0;
            double k2=0.0;

            for( i=0 ; i<n ; i++ ) {
		angle1=Math.atan2(xx-Q.X(i),yy-Q.Y(i));
                k1 += Q.Q(i)*angle1;
            }

            return(k1);
        } 


        public void drawMark(Graphics gc,int i) {
	    int x1=chargeList.get(i).X();
	    int y1=chargeList.get(i).Y();
	    int q1=chargeList.get(i).Q();
           if( q1<0 ) {
                gc.setColor(new Color(10,10,10));
            } else {
                gc.setColor(new Color(150,0,0));
            }

            gc.fillOval(x1-hankei,y1-hankei,hankei*2,hankei*2);
            
            gc.setColor(Color.WHITE);
	    gc.drawLine(x1-hankei+3,y1,x1+hankei-2,y1);
	    if( q1 > 0 ) {
		gc.drawLine(x1,y1-hankei+2,x1,y1+hankei-2);
	    }   
        }

        public void drawMarks(Graphics gc) {
            int i;
            for(i=0; i<Q.count() ; i++ ) {
                drawMark(gc,i);
            }
        }

        public boolean isNear(int x1,int y1) {
            int i;
            for( i=0 ; i<Q.count() ;i++ ) {
		int xx=chargeList.get(i).X();
		int yy=chargeList.get(i).Y();

                if( (xx-x1)*(xx-x1)+(yy-y1)*(yy-y1) <= hankei*hankei ) {
                    return true;
                }
            }
            return false;
        }

        public boolean isNear(int x1,int y1,int i) {
	    int xx=chargeList.get(i).X();
	    int yy=chargeList.get(i).Y();
            if( (xx-x1)*(xx-x1)+(yy-y1)*(yy-y1) <= hankei*hankei ) {
		return true;
            } else {
                return false;
            }
        }
	public void del(int i)
	{
	    chargeList.remove(i);
	}
    }

    int hankei=10;
    Charges Q;
    int www=64;
    double N=5.0;
    double Vstep=4.0;

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

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

    int nowDragQ;

    GraphPanel canvas;
    JSlider sbW,sbN,sbV;
    JCheckBox cbQ;
    JCheckBox cbE;
    JCheckBox cbV;
    JButton btC;
    JProgressBar pBar;

    JLabel l1;

    int w,h;
    int pw=-1,ph=-1;
    double delta;
    
    public void resetAll()
    {
        sbN.setValue(5);
        sbV.setValue(4);
        N=5.0;
    }
    
    public void init(){
	canvas=new GraphPanel();
        Q=new Charges();

        nowDragQ=-1;
	Container cont=getContentPane();
	cont.setLayout(new BorderLayout());

	pBar=new JProgressBar();

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

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


	
	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());
		}
	    });

        btC=new JButton("全電荷クリア");

        btC.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                Q.Clear();
                stopCalc();
		recalcFlg=true;
		repaint();
            }
        });
                
	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());
		}
	    });

        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();
	JPanel p1=new JPanel();
	JPanel p2=new JPanel();
	JPanel p3=new JPanel();
        p.setLayout(new GridLayout(3,1));
	
	cbQ=new JCheckBox("正電荷を加える（クリック）",true);

	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());
		}
	    });    

        p1.add(cbE);
        p1.add(sbN);
        p2.add(cbV);
        p2.add(sbV);
        p.add(p1);
        p.add(p2);
        p3.add(cbQ);
        p3.add(btC);
        p3.add(pBar);
        p.add(p3);


        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 i;
	if( javax.swing.SwingUtilities.isRightMouseButton(e) ) {
	    for( i=0 ; i< Q.count() ; i++ ) {
		if( Q.isNear(mx,my,i) ) {
		    stopCalc();
		    Q.del(i);
		    repaint();
		    return;
		}
	    }
	    return;
	}
        for( i=0 ; i< Q.count() ; i++ ) {
            if( Q.isNear(mx,my,i) ) {
                nowDragQ=i;
		stopCalc();
                return;
            }
        }
        if( cbQ.isSelected() ) {
            Q.addNew(mx,my,1);
        } else {
            Q.addNew(mx,my,-1);
        }
	recalcFlg=true;
	stopCalc(); repaint();
    }

    public void mReleased(MouseEvent e) {
        nowDragQ = -1;
	recalcFlg=true;
	repaint();
    }

    public void mDragged(MouseEvent e) {
        if( nowDragQ < 0 ) {
            return;
        } else {
            Q.setPoint(e.getX(),e.getY(),nowDragQ);
        }
        stopCalc();
	repaint();
    }

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

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

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

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

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

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

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

    boolean recalcFlg;

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

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

        int x,y;

	if( w !=pw || h !=ph ) { 
	    ph=h;pw=w;
	    recalcFlg=true;
	    A= new int[w+1][h+1];
	    V = new int[w+1][h+1];
	}


	stopCalc();

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

	if( nowDragQ < 0 ) {
	    if( recalcFlg) {
		recalcFlg=false;
		if( calcEV == null && runningCalc == 0 ) {
		    calcEV=new CalcEV();
		    calcEV.execute();
		}
	    } else {
		writeRikisen(gc);
		writeV(gc);
	    }
	    Q.drawMarks(gc);
	}
    }

    void stopCalc() {
	if( calcEV == null || calcEV.isDone() ) {
	    calcEV=null;
	    return;
	}
	stopFlg=true;
	calcEV.cancel(true);
	while(runningCalc >0 ) {

	}
	calcEV=null;
	stopFlg=false;
    }


    void writeRikisen(Graphics gc)
    {
	int x,y;

	if( !showEFlg ) {
	    return;
	}
	int cut[][];
	cut =new int[w+1][h];

	int i,j;
	for(i=0; i< w+1; i++ ) {
	    for(j=0; j< h;j++ ) {
		cut[i][j]=0;
	    }
	}
	    

	for( i=0 ; i<Q.count() ; i++ ) {
	    for( j=0 ; j< Q.Y(i) ; j++ ) {
		cut[Q.X(i)][j]+= Q.Q(i);
	    }
	}


	gc.setColor(Color.BLUE);
	for( x=0 ; x<w ; x++ ) {
	    for( y=0 ; y<h ; y++ ) {
		if( !Q.isNear(x,y) ) {
		    if( A[x][y] != A[x][y+1] 
			|| A[x][y] != A[x+1][y+1]-2*N*cut[x+1][y] 
			|| A[x][y] != A[x+1][y] -2*N*cut[x+1][y]
		       ){
			gc.fillRect(x,y,1,1);
		    }
		}
	    }
	}
    }

    void writeV(Graphics gc)
    {
	int x,y;

	if( !showVFlg ) {
	    return;
	}
	Vmin=Vmax=V[0][0];

	for( x=0 ; x< w ; x++ ) {
	    for( y=0 ; y< h ; y++ ) {
		if( !Q.isNear(x,y) ) {
		    if( Vmin > V[x][y] ) {
			Vmin=V[x][y];
		    }
		    if( Vmax < V[x][y] ) {
			Vmax=V[x][y];
		    }
		}
	    }
	}	

	for( x=0 ; x< w ; x++ ) {
	    for( y=0 ; y< h ; y++ ) {
		if( !Q.isNear(x,y) ) {
		    if( 
		       V[x][y] != V[x][y+1]
		       ||V[x][y] != V[x+1][y]
		       ||V[x][y] != V[x+1][y+1]
		       ){
			int c=(int)(255.0*((double)(V[x][y]-Vmin))/((double)(Vmax-Vmin)));
			gc.setColor(new Color(c,255-c,c));
			gc.fillRect(x,y,1,1);
		    }
		}
	    }
	}
    }


    CalcEV calcEV;
    int runningCalc=0;
    boolean stopFlg=false;

    int[][] V;
    int[][] A; 
    int Vmax;
    int Vmin;

    


    class CalcEV extends SwingWorker<Object,Object> {
	Graphics gc;

	public CalcEV() {
	    runningCalc++;
	    gc=canvas.getGraphics();
	}

	public Object doInBackground() {
	    int x,y;
	    pBar.setMinimum(0);
	    pBar.setMaximum(w);
	    
	    if( showEFlg ) {
		gc.setColor(Color.RED);
		for( x=0 ; x<= w ; x++ ) {
		    pBar.setValue(x);
		    if( stopFlg ) {
			normalEnd=false;
			return null;
		    }
		    for( y=0 ; y<= h ; y++ ) {
                        A[x][y]=(int)Math.floor(N*Q.Qangle(x,y)/Math.PI);
		    }
		}
	    }
	    
	    if( showVFlg ) {
		for( x=0 ; x<= w ; x++ ) {
		    pBar.setValue(x);
		    if( stopFlg ) {
			normalEnd=false;
			return null;
		    }
		    for( y=0 ; y<= h ; y++ ) {
			if( ! Q.isNear(x,y) ) {
			    V[x][y]=(int)Math.floor(0.25*Vstep*Q.V(x,y));
			}
		    }
		}
	    }
	    normalEnd=true;
	    return null;
	}	

	protected void done() 
	{
	    runningCalc--;
	    if( normalEnd ) {
		//		gc.setColor(Color.WHITE);
		// gc.fillRect(0,0,w,h);
		writeRikisen(gc);
		writeV(gc);
		Q.drawMarks(gc);
	    }
	}

	boolean normalEnd;
    }
}

