Δευτέρα 6 Φεβρουαρίου 2023

NewtonRaphson Find TimeOfCollision for 2Balls with Deceleration

 Download and run(with java) the .jar

https://drive.google.com/file/d/1gQGtCntPb3NsV14_5K9qgR3ewU--5DzK/view?usp=sharing










NewtonRaphson method for solution x^4









or compile and run....

/*

 * To change this license header, choose License Headers in Project Properties.

 * To change this template file, choose Tools | Templates

 * and open the template in the editor.

 */

package time2ballscollisionwithdeceleration;


import java.awt.BasicStroke;

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.FlowLayout;

import java.awt.Font;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.Point;

import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

import java.awt.event.MouseMotionListener;

import java.awt.event.MouseWheelEvent;

import java.awt.event.MouseWheelListener;

import javax.swing.JButton;

import javax.swing.JCheckBox;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

import javax.swing.JSpinner;

import javax.swing.SpinnerNumberModel;



/**

 *

 * @author dimitrak

 */

public class Time2BallsCollisionWithDeceleration extends JFrame implements MouseListener,MouseMotionListener,MouseWheelListener{

    public static void main(String[] args) {

        new Time2BallsCollisionWithDeceleration();

        Balla a=new Balla();

        System.out.println("epibradynsi: "+a.a);

        a.resetAtSpeed(8);

        System.out.println("speed Arxiko "+a.sp);

        System.out.println("distToStop "+a.Ddist(a.timeToStop));

        System.out.println("timeNow : "+a.timeNow);

        System.out.println("Now speed : "+a.spn);

    }

    

    

    Balla a=new Balla();

    Balla b=new Balla();


    Thread thrSim=new Thread(){

        public void run(){

            while(true){

                runSimStep();

            }

        }

    };

    

    

    JLabel jlDt=new JLabel("DT-sim millis");

    JSpinner jspDt=new JSpinner(new SpinnerNumberModel(100,10,1000,10));

    JPanel jpDt=new JPanel(new BorderLayout());

    

    JCheckBox jchPlayReturn=new JCheckBox("return trip",true);

    

    JPanel jpn=new JPanel(new FlowLayout());

    

    

    public Time2BallsCollisionWithDeceleration(){

        super("Find TIME of Collision between 2 balls with (radius,speed,angle , AND DECELARATION from friction)");

    

        a.col=Color.blue;

        b.col=Color.magenta;

        

        

        jpDt.add(BorderLayout.WEST,jlDt);

        jpDt.add(BorderLayout.CENTER,jspDt);

        

        

        jpn.add(jchPlayReturn);

        jpn.add(jbPlay);

        jpn.add(jbTimeZero);

        jpn.add(jpDt);

        

        getContentPane().add(BorderLayout.CENTER,jp);

        getContentPane().add(BorderLayout.NORTH,jpn);

        

        

        setBounds(0,0,777,666);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setVisible(true);

        

        jp.addMouseListener(this);

        jp.addMouseMotionListener(this);

        jp.addMouseWheelListener(this);

        

        thrSim.start();

        

        

        jbPlay.addActionListener(e->jbPlay());

        jbTimeZero.addActionListener(e->jbTimeZero());

        

        a.resetAtPos(350,140);

        a.resetAtAngle(Math.toRadians(45));

        a.resetAtSpeed(35);

    

        b.resetAtPos(a.r,400);

        b.resetAtAngle(-Math.toRadians(30));

        b.resetAtSpeed(46);

    

        recomputeCollisionTime();

    }

    

    Graphics2D g=null;

    JPanel jp=new JPanel(){

        public void paintComponent(Graphics gr){

            super.paintComponent(gr);

            g=(Graphics2D)gr;

            paintingBalls();

        }

    };

    Font font=new Font("Dialog",0,16);

    void paintingBalls(){

        

        

        

        

        g.setColor(Color.black);

        g.setFont(font);

        int x=10;

        int y=font.getSize();

        g.drawString("Press IN some ball and dragg mouse to change ball location", x,y);

        y+=font.getSize();

        g.drawString("Mouse Roll a)to change ball Speed ,b)+SHIFT to change ball Angle, c)+SHIFT+CNTRL to change ball DE-CELARATION(friction*g)", x,y);

        y+=font.getSize();

        g.drawString(" "+ex+a.timeCollision, x,y);

        y+=font.getSize();

        g.drawString("Solution/Collision Time : "+a.timeCollision, x,y);

        

        if(a.timeCollision>a.timeToStop && a.timeCollision>b.timeToStop){

            g.setColor(Color.red);

            y+=font.getSize();

            g.drawString("Solution/Collision is on RETURN TRIP...not valid on billiards", x,y);

            

        }

        

        y+=font.getSize();g.setColor(Color.black);

        g.drawString("simTime:"+simTimeSecNow, x,y);

        

        y+=font.getSize();g.setColor(a.col);

        g.drawString("a timeToStop: "+a.timeToStop, x,y);

        y+=font.getSize();g.setColor(b.col);

        g.drawString("b timeToStop: "+b.timeToStop, x,y);

        y+=font.getSize();g.setColor(a.col);

        g.drawString("a dist till collision: "+a.distcol, x,y);

        y+=font.getSize();g.setColor(b.col);

        g.drawString("b dist till collision: "+b.distcol, x,y);

        y+=font.getSize();g.setColor(a.col);

        g.drawString("a acc: "+-a.a, x,y);

        y+=font.getSize();g.setColor(b.col);

        g.drawString("b acc: "+-b.a, x,y);

        

        a.paint(g);

        b.paint(g);

    }

    

    

    

    

    


    @Override

    public void mouseClicked(MouseEvent e) {

    }


    boolean pressedLeft,pressedRight;

    Balla ballNowPressed=a;

    @Override

    public void mousePressed(MouseEvent e) {

        pressedLeft=e.getButton()==MouseEvent.BUTTON1;

        pressedRight=e.getButton()==MouseEvent.BUTTON3;

        ballNowPressed=null;

        double dist1=a.dist(e.getX(),e.getY());

        double dist2=b.dist(e.getX(),e.getY());

        System.out.println(""+dist1+" , "+dist2);

        if(dist1<=a.r && dist1<=dist2){

            ballNowPressed=a;

        }

        if(dist2<=b.r && dist2<=dist1){

            ballNowPressed=b;

        }

        

    

    }


    @Override

    public void mouseReleased(MouseEvent e) {

    }


    @Override

    public void mouseEntered(MouseEvent e) {

    }


    @Override

    public void mouseExited(MouseEvent e) {

    }


    @Override

    public void mouseDragged(MouseEvent e) {

        if(ballNowPressed!=null){

            ballNowPressed.resetAtPos(e.getX(),e.getY());

            recomputeCollisionTime();

        }

    }


    @Override

    public void mouseMoved(MouseEvent e) {

    }


    @Override

    public void mouseWheelMoved(MouseWheelEvent e) {

        

        if(ballNowPressed!=null){

            double mul=1;

            if(e.isAltDown()){

                mul*=10;

            }

            if(!e.isShiftDown()){

                ballNowPressed.resetAtSpeedDif(-mul*e.getWheelRotation());

            }

            else{

                if(!e.isControlDown()){

                    ballNowPressed.resetAtAngleDif(Math.toRadians(-5*e.getWheelRotation()));

                }

                else{

                    ballNowPressed.resetAtAccDif(-mul*0.1*e.getWheelRotation());

                }

            }

            recomputeCollisionTime();

        }

    }

    

    Pol ex=new Pol(4);

    

    public double solNewtonRaphson(double Ax,double Ay,double Asp,double Aacc,double Aang,double Aradius,

                           double Bx,double By,double Bsp,double Bacc,double Bang,double Bradius){

        double dx=Bx-Ax;

        double dy=By-Ay;

        double R=Aradius+Bradius;//dy^2 + dx^2 =R^2

        double d=Aacc/2;

        double c=Bacc/2;

        double h=Asp;

        double e=Bsp;

        double m=Math.cos(Aang);

        double k=Math.cos(Bang);

        double f=Math.sin(Aang);

        double l=Math.sin(Bang);

        double ckdm=c*k-d*m;//coses

        double ekhm=e*k-h*m;//coses

        double cldf=c*l-d*f;//sins

        double elhf=e*l-h*f;//sins

        ex.p[0]=dx*dx + dy*dy - R*R;

        ex.p[1]=2*dx*ekhm + 2*dy*elhf;

        ex.p[2]=ekhm*ekhm + 2*dx*ckdm + elhf*elhf + 2*dy*cldf;

        ex.p[3]=2*ekhm*ckdm + 2*elhf*cldf;

        ex.p[4]=ckdm*ckdm + cldf*cldf;

        

        ex.showAllDerivatives();

        System.out.println("Possible: "+ex.findSomeQuickNewtonRoot());

        return ex.NewtonRaphson(ex.findSomeQuickNewtonRoot(), 100, 0.0000001);

    }

    void recomputeCollisionTime(){

        maxTimeSec=Math.max(a.timeToStop, b.timeToStop);

        if(jchPlayReturn.isSelected()){

            maxTimeSec*=2;

        }

        double t=solNewtonRaphson(

                a.xf,a.yf,a.sp,-a.a,a.an,a.r

                ,b.xf,b.yf,b.sp,-b.a,b.an,b.r

        );

        System.out.println("Collision AT Time: "+t);

        a.resetAtTimeCollision(t);

        b.resetAtTimeCollision(t);


        jp.repaint();

    }

    /**

     * Searching t (collision time)

     * AendX=AstartX+(t*Aspeed)cos(Aangle)

     * AendY=AstartY+(t*Aspeed)sin(Aangle)

     * BendX=BstartX+(t*Bspeed)cos(Bangle)

     * BendY=BstartY+(t*Bspeed)sin(Bangle)

     * 

     * (BendY-AendY)^2 +(BendX-AendX)^2 = (ARadius+BRadius)^2

     *//*

    void recomputeCollisionTime0(){

    

        //System.out.println("recomputing collision time");

        double tLess=-1;

        double aco=a.sp*Math.cos(a.an);

        double asi=a.sp*Math.sin(a.an);

        double bco=b.sp*Math.cos(b.an);

        double bsi=b.sp*Math.sin(b.an);

        double dsi=bsi-asi;

        double dco=bco-aco;

        double dy=b.yf-a.yf;

        double dx=b.xf-a.xf;

        

        double A=dsi*dsi+dco*dco;

        double B=2*((dsi)*(dy)+(dco)*(dx));

        double C=dy*dy+dx*dx-Math.pow(a.r+b.r,2);

        double D=B*B-4*A*C;//System.out.println("D = "+D);

        if(D<0){System.out.println("No solutions");}

        else{

            double t1=(-B-Math.sqrt(D))/(2*A);

            double t2=(-B+Math.sqrt(D))/(2*A);//System.out.println("t1 : "+t1+"   ,   t2: "+t2);

            tLess=t1;

            if(t2<t1){tLess=t2;}

        }

        //a.resetAtTimeCollision(tLess*1000);

        //b.resetAtTimeCollision(tLess*1000);

        

        

        jp.repaint();

    }*/

    

    JButton jbPlay=new JButton("Play");

    JButton jbTimeZero=new JButton("TimeZero");

    

    boolean simPaused=true;

    long pauseMillis=60000;

    long stepMillis=(int)jspDt.getValue();

    double stepSec=((double)stepMillis)/1000d;

    double simTimeMillisNow=0;

    double simTimeSecNow=0;

    double maxTimeSec=10;

    void runSimStep(){

        long sl=pauseMillis;

        if(!simPaused){

            boolean simend=false;

            sl=stepMillis;

            if(jchPlayReturn.isSelected()){

                a.resetAtTimeNow(simTimeSecNow);

                b.resetAtTimeNow(simTimeSecNow);

            }

            else{

                if(a.timeToStop>=simTimeSecNow){

                    a.resetAtTimeNow(simTimeSecNow);

                }

                if(b.timeToStop>=simTimeSecNow){

                    b.resetAtTimeNow(simTimeSecNow);

                }

            }

            

            jp.repaint();

            

            simTimeMillisNow+=stepMillis;

            simTimeSecNow+=stepSec;

            

            if(simTimeSecNow>maxTimeSec){

                simend=true;

                simPause();

                jbTimeZero();

            }

        }

        try{thrSim.sleep(sl);}catch(InterruptedException inter){}

    }

    void jbPlay(){

        if(simPaused){

            simUnPause();

        }

        else{

            simPause();

        }

    }

    void simPause(){

        jbPlay.setText("Play");

        simPaused=true;

        

    }

    

    void simUnPause(){

        jbPlay.setText("Simulating");

        simPaused=false;

        thrSim.interrupt();

    }

    void jbTimeZero(){

        simTimeMillisNow=0;

        simTimeSecNow=0;

    }

    

    

    

    

}


class Balla{

    double coefFriction=0.2;

    double mass=0.2;

    double g=10;

    double a=coefFriction*g;

    

    

    Color col=Color.BLUE;

    double r=50;

    double d=2*r;

    double xf=2*r+Math.random()*300,yf=2*r+Math.random()*300;

    double an=Math.toRadians(Math.random()*360);

    double sp=10+Math.random()*10;

    double xl,yl;


    double timeToStop=sp/a;

    double xe,ye;

    

    double timeNow=timeToStop/2;

    double spn=sp-a*timeNow;

    double xn,yn;

    

    double timeCollision=-1;

    double spcol=sp-a*timeCollision;

    double distcol=(Ddist(timeCollision));

    double xc,yc;

    

    public Balla(){

        resetAtPos();

    }

    void resetAtPos(){

        resetAtPos(xf, yf);

    }

    void resetAtPosold(double x,double y){

        xf=x;

        yf=y;

        xl=xf+r*Math.cos(an);

        yl=yf+r*Math.sin(an);

        //1000 millis sp

        //timeEnd  x?

        double distMaxTime=(sp*timeToStop)/1000;

        xe=xf+distMaxTime*Math.cos(an);

        ye=yf+distMaxTime*Math.sin(an);

        

        double distNowTime=(sp*timeNow)/1000;

        xn=xf+distNowTime*Math.cos(an);

        yn=yf+distNowTime*Math.sin(an);

        

        if(timeCollision>0){

            xc=xf+distcol*Math.cos(an);

            yc=yf+distcol*Math.sin(an);

            

        }

    }

    

    public double Ddist(double t){

        double out=sp*t-((a*t*t)/2);

        return out;

    }

    void resetAtPos(double x,double y){

        xf=x;

        yf=y;

        xl=xf+r*Math.cos(an);

        yl=yf+r*Math.sin(an);

        //1000 millis sp

        //timeEnd  x?

        double distMaxTime=(Ddist(timeToStop));

        //System.out.println("distMaxTime: " +distMaxTime+" for timeToStop="+timeToStop);

        xe=xf+distMaxTime*Math.cos(an);

        ye=yf+distMaxTime*Math.sin(an);

        

        double distNowTime=(Ddist(timeNow));

        xn=xf+distNowTime*Math.cos(an);

        yn=yf+distNowTime*Math.sin(an);

        

        if(timeCollision>0){

            double distAtCollision=(Ddist(timeCollision));

            xc=xf+distAtCollision*Math.cos(an);

            yc=yf+distAtCollision*Math.sin(an);

            

        }

    }

    void resetAtAngle(double newAngle){

        an=newAngle;

        resetAtPos();

    }

    void resetAtAngleDif(double difAngle){

        resetAtAngle(difAngle+an);

    }

    void resetAtSpeed(double newSpeed){

        sp=newSpeed;

        timeToStop=sp/a;

        spn=sp-a*timeNow;

        System.out.println(sp+" m/ss -> TimeToStop = "+timeToStop);

        resetAtPos();

    }

    void resetAtSpeedDif(double difSpeed){

        resetAtSpeed(difSpeed+sp);

    }

    void resetAtTimeNow(double newNowTime){

        timeNow=newNowTime;

        spn=sp-a*timeNow;

        resetAtPos();

    }

    void resetAtTimeCollision(double newCollisionTime){

        timeCollision=newCollisionTime;

        spcol=sp-a*timeCollision;

        distcol=(sp*timeCollision)/1000;

        resetAtPos();

    }

    void resetAtAcc(double accNew){

        accNew=Math.abs(accNew);

        a=accNew;

        timeToStop=sp/a;

        spn=sp-a*timeNow;

        resetAtPos();

    }

    void resetAtAccDif(double accDif){

        resetAtAcc(a+accDif);

        System.out.println(""+a);

    }

    

    public double dist(double x,double y){

        return Point.distance(xf, yf, x, y);

    }


    static BasicStroke strokeNormal=new BasicStroke(1);

    static BasicStroke strokeMoving=new BasicStroke(1,0,0,10,new float[]{10,10},0);

    public void paint(Graphics2D g){

        g.setStroke(strokeNormal);

        g.setColor(col);

        g.drawOval((int)(xf-r),(int)(yf-r), (int)(d),(int)( d));

        g.drawLine((int)(xf),(int)(yf),(int)(xl),(int)(yl));

        g.drawOval((int)(xe-r),(int)(ye-r), (int)(d),(int)( d));

        g.drawLine((int)(xf),(int)(yf),(int)(xe),(int)(ye));

        

        g.drawString("V0="+(nice(sp))+" m/s", (int)(xf),(int)(yf));

        

        

        g.setStroke(strokeMoving);

        g.drawOval((int)(xn-r),(int)(yn-r), (int)(d),(int)( d));

        g.drawString("V="+(nice(spn))+" m/s", (int)(xn),(int)(yn));

        

        if(timeCollision>0){

            g.setColor(Color.RED);

            g.setStroke(strokeNormal);

            int ucx=(int)(xc-r);

            int ucy=(int)(yc-r);

            g.drawOval(ucx,ucy, (int)(d),(int)( d));

            //g.drawLine(ucx,ucy,(int)(ucx+d),(int)(ucy+d));

        }

    }

    

    static double nice(double d){

        d*=1000;

        d=(long)d;

        d/=1000;

        return d;

    }

}

class JNewtonRaphson {

    public static void main(String[] args) {


    }

    static void test(){

        Pol p=new Pol(3,-7,1,2,-19);

        System.out.println(""+p);

        System.out.println(""+p.strf(-1));

        System.out.println(""+p.strf(0));

        System.out.println(""+p.strf(1));

        System.out.println(""+p.hasSolutionAt(-100, 100, 1));

        System.out.println(""+p.findSomeQuickNewtonRoot());

        p.showAllDerivatives();

        System.out.println("--------------");

        p.NewtonRaphson(0, 100, 0.0000001);

        

        

    }

    

    

}


class Pol{

    double[]p;

    

    public Pol(int maxPower){

        p=new double[maxPower+1];

    }

    public Pol(double []ps){

        this(ps,false);

    }

    public Pol(double []ps,boolean assign){

        if(assign){

            p=ps;

        }

        else{

            p=new double[ps.length];

            System.arraycopy(ps, 0, p, 0, ps.length);

        }

    }

    

    public Pol(double p2,double p1,double c){

        this(2);

        p[0]=c;

        p[1]=p1;

        p[2]=p2;

    }

    public Pol(double p3,double p2,double p1,double c){

        this(3);

        p[0]=c;

        p[1]=p1;

        p[2]=p2;

        p[3]=p3;

    }

    public Pol(double p4,double p3,double p2,double p1,double c){

        this(4);

        p[0]=c;

        p[1]=p1;

        p[2]=p2;

        p[3]=p3;

        p[4]=p4;

    }

    

    public double NewtonRaphson(double xPossible,int maxsteps,double epsilon){

        

        Pol ft=getfDerivative();

        double x=xPossible;

        double h = fHorner(x) / ft.fHorner(x);

        int i=0;

        while (i<maxsteps && Math.abs(h) >= epsilon){

            x -=h;

            h = fHorner(x) / ft.fHorner(x);

            //System.out.println(i+" : "+x);

            ++i;

        }

        //System.out.println("sol: "+x);

        System.out.println(""+strf(x));

        return x;

    }


    public void showAllDerivatives(){

        Pol a=this;

        int ton=0;

        while(a.p.length>1){

            System.out.println("f["+(ton)+"](x)="+a);

            a=a.getfDerivative();

            ++ton;

        }

        System.out.println("f["+(ton)+"](x)="+a);

    }

    public Pol getfDerivative(){

        int l=p.length;

        if(p.length<2){

            return new Pol(0);//y=0;

        }

        l-=1;

        double ps[]=new double[l];

        int pos=l-1;

        while(l>0){

            ps[pos]=p[l]*l;

            --pos;

            --l;

        }

        return new Pol(ps,true);

    }

    public double findSomeQuickNewtonRoot(){

        double out=hasSolutionAt(-10, 10,0.1);//200 max

        if(Double.isNaN(out)){

            out=hasSolutionAt(-100, 100,1);//200 max

            if(Double.isNaN(out)){

                out=hasSolutionAt(-1000, 1000,10);//200 max

                if(Double.isNaN(out)){

                    out=hasSolutionAt(-10000, 10000,100);//200 max

                }

            }

        }

        return out;

    }

    public double hasSolutionAt(double x1,double x2,double xstep){

        if(x2<x1){

            double temp=x2;

            x2=x1;

            x1=temp;

        }

        else if(x1<x2){

            

        }

        else{

            

        }

        if(xstep<0){

            xstep=-xstep;

        }

        else if(xstep>0){}

        else{

            xstep=x2-x1;

            xstep/=100;

        }

        

        double x=x1;

        double y=fHorner(x);

        boolean startNeg=false;

        if(y<0){

            startNeg=true;

        }

        else if(y>0){

            

        }

        else{

            return x;

        }

        x+=xstep;

        x2+=xstep;

        while(x<x2){

            y=fHorner(x);

            if(y<0){

                if(!startNeg){

                    return x-xstep/2;

                }

            }

            else if(y>0){

                if(startNeg){

                    return x-xstep/2;

                }

            }

            else{

                return x;

            }

            x+=xstep;

        }

        

        

        return Double.NaN;

    }

    

    public double fHorner(double x){

        int i=p.length-1;

        double out=p[i];

        --i;

        while(i>=0){

            out*=x;

            out+=p[i];

            --i;

        }

        return out;

    }

    

    public double fOld(double x){

        int i=0;

        double v=p[i];

        double out=v;

        double xs=1;

        ++i;

        while(i<p.length){

            xs*=x;

            out+=xs*p[i];

            ++i;

        }

        return out;

    }

    public double f(double x){

        return fHorner(x);

    }

    public String strf(double x){

        return "f("+x+") = "+f(x);

    }


    

    public String toString(){

        String out="";

        int i=p.length-1;

        double v;

        while(i>0){

            v=p[i];

            if(v>0){

                out+="+"+v+"(x^"+i+") ";

            }

            else if(v<0){

                out+=""+v+"(x^"+i+") ";

            }

            --i;

        }

        v=p[i];

        if(v>0){

            out+="+"+v;

        }

        else if(v<0){

            out+=""+v;

        }

        else{

            if(p.length==1){

                out+="+0";

            }

        }

        

        return out;

    }

}


Κυριακή 5 Φεβρουαρίου 2023

Collision Time For Two Balls

download and run this

https://drive.google.com/file/d/1_IPdpTKzLNTJYxBxxbtJyAkH3dnFwSZD/view?usp=sharing


//or....stand alone / ready to compile and run simple code










/*

 * To change this license header, choose License Headers in Project Properties.

 * To change this template file, choose Tools | Templates

 * and open the template in the editor.

 */

package j2ballscollisionfounder;


import java.awt.BasicStroke;

import java.awt.BorderLayout;

import java.awt.Color;

import java.awt.FlowLayout;

import java.awt.Font;

import java.awt.Graphics;

import java.awt.Graphics2D;

import java.awt.Point;

import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

import java.awt.event.MouseMotionListener;

import java.awt.event.MouseWheelEvent;

import java.awt.event.MouseWheelListener;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

import javax.swing.JSpinner;

import javax.swing.SpinnerNumberModel;


/**

 *

 * @author dimitrak

 */

public class J2BallsCollisionFounder extends JFrame implements MouseListener,MouseMotionListener,MouseWheelListener{

    public static void main(String[] args) {

        new J2BallsCollisionFounder();

    }

    

    

    Balla a=new Balla();

    Balla b=new Balla();


    Thread thrSim=new Thread(){

        public void run(){

            while(true){

                runSimStep();

            }

        }

    };

    

    

    JLabel jlDt=new JLabel("DT-sim millis");

    JSpinner jspDt=new JSpinner(new SpinnerNumberModel(100,10,1000,10));

    JPanel jpDt=new JPanel(new BorderLayout());

    

    JPanel jpn=new JPanel(new FlowLayout());

    

    

    public J2BallsCollisionFounder(){

        super("Found TIME of Collision between 2 balls with (radius,speed,angle)");

    

        b.col=Color.black;

        

        

        jpDt.add(BorderLayout.WEST,jlDt);

        jpDt.add(BorderLayout.CENTER,jspDt);

        

        

        jpn.add(jbPlay);

        jpn.add(jbTimeZero);

        jpn.add(jpDt);

        

        getContentPane().add(BorderLayout.CENTER,jp);

        getContentPane().add(BorderLayout.NORTH,jpn);

        

        

        setBounds(0,0,777,666);

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setVisible(true);

        

        jp.addMouseListener(this);

        jp.addMouseMotionListener(this);

        jp.addMouseWheelListener(this);

        

        thrSim.start();

        

        

        jbPlay.addActionListener(e->jbPlay());

        jbTimeZero.addActionListener(e->jbTimeZero());

        

        a.resetAtPos(300,100);

        a.resetAtAngle(Math.PI/2);

        a.resetAtSpeed(60);

    

        b.resetAtPos(100,300);

        b.resetAtAngle(-Math.PI/8);

        b.resetAtSpeed(60);

    

        recomputeCollisionTime();

    }

    

    Graphics2D g=null;

    JPanel jp=new JPanel(){

        public void paintComponent(Graphics gr){

            super.paintComponent(gr);

            g=(Graphics2D)gr;

            paintingBalls();

        }

    };

    Font font=new Font("Dialog",0,16);

    void paintingBalls(){

        

        

        

        

        g.setColor(Color.black);

        g.setFont(font);

        int x=10;

        int y=font.getSize();

        g.drawString("Press IN some ball and dragg mouse to change ball location", x,y);

        y+=font.getSize();

        g.drawString("Mouse Roll to change ball Speed", x,y);

        y+=font.getSize();

        g.drawString("Mouse Roll +SHIFT to change ball Angle", x,y);

        

        a.paint(g);

        b.paint(g);

    }

    

    

    

    

    


    @Override

    public void mouseClicked(MouseEvent e) {

    }


    boolean pressedLeft,pressedRight;

    Balla ballNowPressed=a;

    @Override

    public void mousePressed(MouseEvent e) {

        pressedLeft=e.getButton()==MouseEvent.BUTTON1;

        pressedRight=e.getButton()==MouseEvent.BUTTON3;

        ballNowPressed=null;

        double dist1=a.dist(e.getX(),e.getY());

        double dist2=b.dist(e.getX(),e.getY());

        System.out.println(""+dist1+" , "+dist2);

        if(dist1<=a.r && dist1<=dist2){

            ballNowPressed=a;

        }

        if(dist2<=b.r && dist2<=dist1){

            ballNowPressed=b;

        }

        

    

    }


    @Override

    public void mouseReleased(MouseEvent e) {

    }


    @Override

    public void mouseEntered(MouseEvent e) {

    }


    @Override

    public void mouseExited(MouseEvent e) {

    }


    @Override

    public void mouseDragged(MouseEvent e) {

        if(ballNowPressed!=null){

            ballNowPressed.resetAtPos(e.getX(),e.getY());

            recomputeCollisionTime();

        }

    }


    @Override

    public void mouseMoved(MouseEvent e) {

    }


    @Override

    public void mouseWheelMoved(MouseWheelEvent e) {

        

        if(ballNowPressed!=null){

            if(!e.isShiftDown()){

                ballNowPressed.resetAtSpeedDif(-5*e.getWheelRotation());

            }

            else{

                ballNowPressed.resetAtAngleDif(Math.toRadians(-5*e.getWheelRotation()));

            }

            recomputeCollisionTime();

        }

    }

    

    /**

     * Searching t (collision time)

     * AendX=AstartX+(t*Aspeed)cos(Aangle)

     * AendY=AstartY+(t*Aspeed)sin(Aangle)

     * BendX=BstartX+(t*Bspeed)cos(Bangle)

     * BendY=BstartY+(t*Bspeed)sin(Bangle)

     * 

     * (BendY-AendY)^2 +(BendX-AendX)^2 = (ARadius+BRadius)^2

     */

    void recomputeCollisionTime(){

        //System.out.println("recomputing collision time");

        double tLess=-1;

        double aco=a.sp*Math.cos(a.an);

        double asi=a.sp*Math.sin(a.an);

        double bco=b.sp*Math.cos(b.an);

        double bsi=b.sp*Math.sin(b.an);

        double dsi=bsi-asi;

        double dco=bco-aco;

        double dy=b.yf-a.yf;

        double dx=b.xf-a.xf;

        

        double A=dsi*dsi+dco*dco;

        double B=2*((dsi)*(dy)+(dco)*(dx));

        double C=dy*dy+dx*dx-Math.pow(a.r+b.r,2);

        double D=B*B-4*A*C;//System.out.println("D = "+D);

        if(D<0){System.out.println("No solutions");}

        else{

            double t1=(-B-Math.sqrt(D))/(2*A);

            double t2=(-B+Math.sqrt(D))/(2*A);//System.out.println("t1 : "+t1+"   ,   t2: "+t2);

            tLess=t1;

            if(t2<t1){tLess=t2;}

        }

        a.resetAtTimeCollision(tLess*1000);

        b.resetAtTimeCollision(tLess*1000);

        

        

        jp.repaint();

    }

    

    JButton jbPlay=new JButton("Play");

    JButton jbTimeZero=new JButton("TimeZero");

    

    boolean simPaused=true;

    long stepMillis=100;

    long pauseMillis=60000;

    double simTime=0;

    double simDtime(){return (int)jspDt.getValue();}

    double simMaxtime=5000;

    void runSimStep(){

        long sl=pauseMillis;

        if(!simPaused){

            boolean simend=false;

            sl=stepMillis;

            simTime+=simDtime();

            if(simTime>simMaxtime){

                simTime=simMaxtime;

                simend=true;

            }

            a.resetAtTimeNow(simTime);

            b.resetAtTimeNow(simTime);

            jp.repaint();

            

            if(simend){

                simPause();

                simTime=0;

            }

        }

        try{thrSim.sleep(sl);}catch(InterruptedException inter){}

    }

    void jbPlay(){

        if(simPaused){

            simUnPause();

        }

        else{

            simPause();

        }

    }

    void simPause(){

        jbPlay.setText("Play");

        simPaused=true;

        

    }

    

    void simUnPause(){

        jbPlay.setText("Simulating");

        simPaused=false;

        thrSim.interrupt();

    }

    void jbTimeZero(){

        simTime=0;

    }

    

    

    

    

}


class Balla{

    Color col=Color.BLUE;

    double r=50;

    double d=2*r;

    double xf=2*r+Math.random()*300,yf=2*r+Math.random()*300;

    double an=Math.toRadians(Math.random()*360);

    double sp=10+Math.random()*100;

    double xl,yl;

    

    double timeEnd=5000;

    double xe,ye;

    

    double timeNow=timeEnd/2;

    double xn,yn;

    

    double timeCollision=-1;

    double xc,yc;

    

    public Balla(){

        resetAtPos();

    }

    void resetAtPos(){

        resetAtPos(xf, yf);

    }

    void resetAtPos(double x,double y){

        xf=x;

        yf=y;

        xl=xf+r*Math.cos(an);

        yl=yf+r*Math.sin(an);

        //1000 millis sp

        //timeEnd  x?

        double distMaxTime=(sp*timeEnd)/1000;

        xe=xf+distMaxTime*Math.cos(an);

        ye=yf+distMaxTime*Math.sin(an);

        

        double distNowTime=(sp*timeNow)/1000;

        xn=xf+distNowTime*Math.cos(an);

        yn=yf+distNowTime*Math.sin(an);

        

        if(timeCollision>0){

            double distAtCollision=(sp*timeCollision)/1000;

            xc=xf+distAtCollision*Math.cos(an);

            yc=yf+distAtCollision*Math.sin(an);

            

        }

    }

    void resetAtAngle(double newAngle){

        an=newAngle;

        resetAtPos();

    }

    void resetAtAngleDif(double difAngle){

        resetAtAngle(difAngle+an);

    }

    void resetAtSpeed(double newSpeed){

        sp=newSpeed;

        resetAtPos();

    }

    void resetAtSpeedDif(double difSpeed){

        resetAtSpeed(difSpeed+sp);

    }

    void resetAtTimeNow(double newNowTime){

        timeNow=newNowTime;

        resetAtPos();

    }

    void resetAtTimeCollision(double newCollisionTime){

        timeCollision=newCollisionTime;

        resetAtPos();

    }

    

    public double dist(double x,double y){

        return Point.distance(xf, yf, x, y);

    }


    static BasicStroke strokeNormal=new BasicStroke(1);

    static BasicStroke strokeMoving=new BasicStroke(1,0,0,10,new float[]{10,10},0);

    public void paint(Graphics2D g){

        g.setStroke(strokeNormal);

        g.setColor(col);

        g.drawOval((int)(xf-r),(int)(yf-r), (int)(d),(int)( d));

        g.drawLine((int)(xf),(int)(yf),(int)(xl),(int)(yl));

        g.drawOval((int)(xe-r),(int)(ye-r), (int)(d),(int)( d));

        

        double spn=sp*1000;

        spn=(long)spn;

        spn/=1000;

        g.drawString("V="+(spn)+" px/s", (int)(xf),(int)(yf));

        

        

        g.setStroke(strokeMoving);

        g.drawOval((int)(xn-r),(int)(yn-r), (int)(d),(int)( d));

        

        if(timeCollision>0){

            g.setColor(Color.RED);

            g.setStroke(strokeNormal);

            int ucx=(int)(xc-r);

            int ucy=(int)(yc-r);

            g.drawOval(ucx,ucy, (int)(d),(int)( d));

            //g.drawLine(ucx,ucy,(int)(ucx+d),(int)(ucy+d));

        }

    }

}