1. Not finding help here? Sign up for a free 30min tutor trial with Chegg Tutors
    Dismiss Notice
Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

2D collision detection / determining final velocity - Java

  1. Aug 2, 2015 #1
    1. The problem statement, all variables and given/known data

    Determine if two circular objects (both moving or one stationary), collide and if so, determine their final velocity.
    The two objects are being rendered using Graphics's Java library class, updating their position 60 times per second. Their position (x,y) is determined based on the Width and Length of the frame, and their velocity is given in pixels (eg: -1 , 2; the object moves 1 pixel to the right and 2 pixels upwards (Y-Axis is inverted), the objects have a mass and a radius.

    2. Relevant equations

    Here.

    Note: In the code below there is a modification to the equation (theta of the other object is replaced by Phi - 2PI), because I was told that the equation wasn't accurate (I may be mistaked though, regardless this does not fix the problem either way).

    3. The attempt at a solution

    Here is the class that tries to do the math but fails miserably. Sometimes the objects collide properly but sometimes very awkwardly. I think that my main issue is the inverted Y-Axis.

    Code (Java):
    import com.game.src.gameObjects.GameObject;
    import java.util.LinkedList;

    public class Physics {
        private Controller1 c1;
        private Controller2 c2;
        private LinkedList<GameObject> gameObjects;


        public Physics(Controller1 c1, Controller2 c2){
            this.c1 = c1;
            this.c2 = c2;
            gameObjects = new LinkedList<>();
     
        }

        public void tick(){
     
        gameObjects.add(c1.getShip());
        gameObjects.add(c1.getWeapon1());
        gameObjects.add(c1.getWeapon2());
        gameObjects.add(c1.getWeapon3());
        gameObjects.addAll(c2.getAsteroids());
        collisions(gameObjects);
        gameObjects.clear();

     
        }

        public void collisions(LinkedList<GameObject> modules){
     
     
            double m1Vxf;
            double m1Vyf;
            double m2Vxf;
            double m2Vyf;
     
     
            for(int i = 0; i < modules.size(); i++){
         
                for (int j = 0; j < modules.size(); j++){
             
                    GameObject m1 = modules.get(i);
                    GameObject m2 = modules.get(j);
             
                    if(m1.intersects(m2)){
                 
                        if (m1.getFaction() != m2.getFaction()){
                     
                            m1Vxf = (( ( Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * (Math.cos(m1.theta()-m1.phi(m2))) * (m1.getMass() - m2.getMass()) + 2 * m2.getMass() * Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * (Math.cos(Math.PI/2 - m1.phi(m2))) ) * Math.cos(m1.phi(m2))) / (m1.getMass()+m2.getMass()) + Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * Math.sin(m1.theta()-m1.phi(m2))*Math.cos(m1.phi(m2)+Math.PI/2) );
                                           
                            m1Vyf = (( ( Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * (Math.cos(m1.theta()-m1.phi(m2))) * (m1.getMass()- m2.getMass()) + 2 * m2.getMass() * Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * (Math.sin(Math.PI/2-m1.phi(m2))) ) * Math.sin(m1.phi(m2))) / (m1.getMass()+m2.getMass()) + Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * Math.sin(m1.theta()-m1.phi(m2))*Math.sin(m1.phi(m2)+Math.PI/2) );
                     
                            m2Vxf = (( ( Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * (Math.cos(m2.theta()  - m2.phi(m1))) * (m2.getMass()- m1.getMass()) + 2 * m1.getMass() * Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * (Math.cos(Math.PI/2 -  m2.phi(m1))) ) * Math.cos(m2.phi(m1)))/ (m2.getMass()+m1.getMass()) + Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * Math.sin(m2.theta() - m2.phi(m1))*Math.cos( m2.phi(m1)+Math.PI/2) );
                 
                            m2Vyf = (( ( Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * (Math.cos(m2.theta()  - m2.phi(m1))) * (m2.getMass()- m1.getMass()) + 2 * m1.getMass() * Math.sqrt( Math.pow(m1.getVelX(),2) + Math.pow(m1.getVelY(),2) ) * (Math.sin(Math.PI/2 -  m2.phi(m1))) ) * Math.sin(m2.phi(m1)))/ (m2.getMass()+m1.getMass()) + Math.sqrt( Math.pow(m2.getVelX(),2) + Math.pow(m2.getVelY(),2) ) * Math.sin(m2.theta() - m2.phi(m1))* Math.sin( m2.phi(m1)+Math.PI/2) );
                         
                            m1.setVelX(m1Vxf);
                   
                            m1.setVelY(m1Vyf);
                     
                            m2.setVelX(m2Vxf);
                     
                            m2.setVelY(m2Vyf);
                           
                            //Temporary solution to the objects getting stuck () they only collide once).  
                            m2.setFaction(m1.getFaction());
                        }
             
                    }
         
                }
     
            }

     
        }


    }
    Here are the methods used by the previous class, which are all in the GameObject abstract class.
    Code (Java):
     public boolean intersects(GameObject m){

            if( Math.pow(this.getXr() - m.getXr(), 2 ) + Math.pow(this.getYr() - m.getYr() , 2 ) <= Math.pow(this.getRadius() + m.getRadius(), 2) )
                 
                return true;
            else
                return false;

        }


    public double theta(){
       
               //si this.y - m.y = 0 y this.x-m.x < 0 ->  0.
            if(this.getVelY() == 0 && this.getVelX() < 0){
                return Math.PI;
            //si this.y - m.y > 0 y this.x-m.x = 0 ->  90.
            }else if(this.getVelY() > 0 && this.getVelX() == 0){
                return 3*Math.PI/2;
            //si this.y - m.y = 0 y this.x-m.x > 0 ->  180.
            }else if(this.getVelY() == 0 && this.getVelX() > 0){
                return 0;
            //si this.y - m.y < 0 y this.x-m.x = 0 ->  270.
            }else if(this.getVelY() < 0 && this.getVelX() == 0){
                return Math.PI/2;
         
            }else if(this.getVelY() > 0 && this.getVelX() < 0){
                return Math.PI + Math.atan2(this.getVelY()  , this.getVelX()) ;
       
            }else if(this.getVelY() > 0 && this.getVelX() > 0){
                return 2*Math.PI - Math.atan2(this.getVelY()  , this.getVelX()) ;
       
            }else if(this.getVelY() < 0 && this.getVelX() > 0){
                return Math.atan2(this.getVelY()  , this.getVelX() );
       
            }else if(this.getVelY() < 0 && this.getVelX() < 0){
                return Math.PI - Math.atan2(this.getVelY()  , this.getVelX()) ;
         
            }else
                return Math.atan2(this.getVelY()  , this.getVelX() );
         
        }

        public double phi(GameObject m){
            //si this.y - m.y = 0 y this.x-m.x < 0 ->  0.
            if(this.getYr() - m.getYr() == 0 && this.getXr() - m.getXr() < 0){
                return 0;
            //si this.y - m.y > 0 y this.x-m.x = 0 ->  90.
            }else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() == 0){
                return Math.PI/2;
            //si this.y - m.y = 0 y this.x-m.x > 0 ->  180.
            }else if(this.getYr() - m.getYr() == 0 && this.getXr() - m.getXr() > 0){
                return Math.PI;
            //si this.y - m.y < 0 y this.x-m.x = 0 ->  270.
            }else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() == 0){
                return 3*Math.PI/2;
         
            }else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() < 0){
                return Math.atan2((this.getYr() - m.getYr()) , this.getXr() - m.getXr() );
       
            }else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() > 0){
                return Math.PI - Math.atan2((this.getYr() - m.getYr()) , this.getXr() - m.getXr()) ;
       
            }else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() > 0){
                return Math.PI + Math.atan2((this.getYr() - m.getYr()) , this.getXr() - m.getXr() );
       
            }else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() < 0){
                return 2*Math.PI - Math.atan2(this.getYr() - m.getYr() , this.getXr() - m.getXr() );
         
            }else
                return Math.atan2((this.getYr() - m.getYr()) , this.getXr() - m.getXr() );

        public double getXr() {
            return getX()+ getRadius();
        }

        public double getYr() {
            return getY()+ getRadius();
        }
    This problem is driving me mad, please help me. Thanks in advance. :)
     
    Last edited: Aug 2, 2015
  2. jcsd
  3. Aug 2, 2015 #2

    Borg

    User Avatar
    Science Advisor
    Gold Member

    It looks like you're using the Pythagorean theorem to solve for coordinate positions. That will cause issues when you cross an axis.
     
  4. Aug 2, 2015 #3
    Indeed, I was planning to add a condition so that collisions can only be checked within the frame, to prevent that. However, this should not cause problems when their positions are +x, and +y, isn't it?(unless that 2 objects enter the frame on top of eachother, but that's another issue)

    I think my problem is related to theta and phi and the inverted Y-Axis. I can't find the correct angles.

    I have been reading other equations using vectors, but I am not sure about how to implement them.
     
    Last edited: Aug 2, 2015
  5. Aug 2, 2015 #4
    Scratch the last code it was a mess.

    theta and phi use Math.atan now.

    Code (Java):

    /**
         *
         * @return  theta: the angle of the direction of the object with the X axis.
         * currently the conditions are only useful to prevent Y/0 division (pretty sure it
         * does not use the best solution to it - adding 0.001). It calculates the angle with
         * Math.atan(dy/dx). So, the conditions are there to correct certain angles due to that
         * it does not cover the full 2 PI range. But, what are those corrections? Im clueless.
         */

        public double theta(){
                 
            if(this.getVelY() == 0 && this.getVelX() < 0){
         
                return Math.atan(this.getVelY()  / this.getVelX()) ;
         
            }else if(this.getVelY() > 0 && this.getVelX() == 0){
         
                return Math.atan(this.getVelY()  / (this.getVelX()+0.001)) ;
                 
            }else if(this.getVelY() == 0 && this.getVelX() > 0){
         
                return Math.atan(this.getVelY()  / this.getVelX()) ;
                         
            }else if(this.getVelY() < 0 && this.getVelX() == 0){
             
                return Math.atan(this.getVelY()  / (this.getVelX()+0.001)) ;
         
            }else if(this.getVelY() > 0 && this.getVelX() < 0){
             
                return Math.atan(this.getVelY()  / this.getVelX()) ;
         
            }else if(this.getVelY() > 0 && this.getVelX() > 0){
             
                return Math.atan(this.getVelY()  / this.getVelX()) ;
             
            }else if(this.getVelY() < 0 && this.getVelX() > 0){
             
                return Math.atan(this.getVelY()  / this.getVelX() );
         
            }else if(this.getVelY() < 0 && this.getVelX() < 0){
             
                return Math.atan(this.getVelY()  / this.getVelX()) ;
         
            }else
             
                return Math.atan(this.getVelY()  / this.getVelX() );  
           
            }
        /**
         *
         * @param m
         * @return phi: the angle of the collision of the objects with the X axis.
         * currently the conditions are only useful to prevent Y/0 division (pretty sure it
         * does not use the best solution to it - adding 0.001). It calculates the angle with
         * Math.atan(dy/dx). So, the conditions are there to correct certain angles due to that
         * it does not cover the full 2 PI range. But, what are those corrections? Im clueless.
         * Note: the position of the objects is based on x+radius, to check where is the center,
         * because x,y point is located at the upper left of the object.
         */

        public double phi(GameObject m){
         
            if(this.getYr() - m.getYr() == 0 && this.getXr() - m.getXr() < 0){
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
         
            }else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() == 0){
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()+0.001) );
         
            }else if(this.getYr() - m.getYr() == 0 && this.getXr() - m.getXr() > 0){
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
         
            }else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() == 0){
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()+0.001) );
         
            }else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() < 0){
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
         
            }else if(this.getYr() - m.getYr() > 0 && this.getXr() - m.getXr() > 0){
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
         
            }else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() > 0){
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
         
            }else if(this.getYr() - m.getYr() < 0 && this.getXr() - m.getXr() < 0){
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
                     
            }else
             
                return Math.atan((this.getYr() - m.getYr()) / (this.getXr() - m.getXr()) );
            }
     
        public double getXr() {
            return getX()+ getRadius();
        }

        public double getYr() {
            return getY()+ getRadius();
        }
     

    Code (Java):



        /**
         *
         * @param gameObjects
         * Every time the game updates, this method determines whether or not any
         * object contained in the list collides with eachother. If they collide, it
         * changes the direction and speed of both objects based on their position,
         * angle of impact (Phi), speed vector angle at the moment of collision (Theta),
         * the mass and radius of each object (the objects are circle shaped).
        */

        public void collisions(LinkedList<GameObject> gameObjects){
       
            //Declaration of the final velocity values of
            double gO1Vxf;
            double gO1Vyf;
            double gO2Vxf;
            double gO2Vyf;
       
            //the loop.
            for(int i = 0; i < gameObjects.size(); i++){
                for (int j = i+1; j < gameObjects.size(); j++){
               
                    //Assigns the object located in i position to gO1 and j position to gO2          
                    GameObject gO1 = gameObjects.get(i);
                    GameObject gO2 = gameObjects.get(j);
               
                    //checks if the objects intersect using a method of gameObject's class.
                    if(gO1.intersects(gO2)){
                        //checks if the objects intersecting eachother have different faction,
                        //added this in case that I want a specific object not to be checked for collisions.
                        if (gO1.getFaction() != gO2.getFaction()){
                       
                            //debug info.
                            System.out.println("");
                            System.out.println("Phi"+Math.toDegrees(gO1.phi(gO2)));
                            System.out.println("");
                            System.out.println("M1");
                            System.out.println("x:"+gO1.getXr());
                            System.out.println("y:"+gO1.getYr());
                            System.out.println("Vx:"+gO1.getVelX());
                            System.out.println("Vy:"+gO1.getVelY());
                            System.out.println("theta:"+Math.toDegrees(gO1.theta()));
                            System.out.println("");
                            System.out.println("M2");
                            System.out.println("x:"+gO2.getXr());
                            System.out.println("y:"+gO2.getYr());
                            System.out.println("Vx:"+gO2.getVelX());
                            System.out.println("Vy:"+gO2.getVelY());
                            System.out.println("theta:"+Math.toDegrees(gO2.theta()));
                       
                       
                            //calculates the final Vx of gO1.
                            gO1Vxf = (( ( Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(gO1.theta() - gO1.phi(gO2))) * (gO1.getMass() - gO2.getMass()) + 2 * gO2.getMass() * Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(Math.PI/2 - gO2.theta())) ) * Math.cos(gO1.phi(gO2))) / (gO1.getMass()+gO2.getMass()) + Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * Math.sin(gO1.theta() - gO1.phi(gO2)) * Math.cos(gO1.phi(gO2)+Math.PI/2) );
                            //calculates the final Vy of gO1.                  
                            gO1Vyf = (( ( Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(gO1.theta() - gO1.phi(gO2))) * (gO1.getMass() - gO2.getMass()) + 2 * gO2.getMass() * Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.sin(Math.PI/2 - gO2.theta())) ) * Math.sin(gO1.phi(gO2))) / (gO1.getMass()+gO2.getMass()) + Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * Math.sin(gO1.theta() - gO1.phi(gO2)) * Math.sin(gO1.phi(gO2)+Math.PI/2) );
                            //calculates the final Vx of gO2.
                            gO2Vxf = (( ( Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(gO2.theta() - gO1.phi(gO2))) * (gO2.getMass() - gO1.getMass()) + 2 * gO1.getMass() * Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.cos(Math.PI/2 - gO1.theta())) ) * Math.cos(gO1.phi(gO2))) / (gO2.getMass()+gO1.getMass()) + Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * Math.sin(gO2.theta() - gO1.phi(gO2)) * Math.cos(gO1.phi(gO2)+Math.PI/2) );
                            //calculates the final Vy of gO2.
                            gO2Vyf = (( ( Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * (Math.cos(gO2.theta() - gO1.phi(gO2))) * (gO2.getMass() - gO1.getMass()) + 2 * gO1.getMass() * Math.sqrt( Math.pow(gO1.getVelX(),2) + Math.pow(gO1.getVelY(),2) ) * (Math.sin(Math.PI/2 - gO1.theta())) ) * Math.sin(gO1.phi(gO2))) / (gO2.getMass()+gO1.getMass()) + Math.sqrt( Math.pow(gO2.getVelX(),2) + Math.pow(gO2.getVelY(),2) ) * Math.sin(gO2.theta() - gO1.phi(gO2)) * Math.sin(gO1.phi(gO2)+Math.PI/2) );
                       
                            //sets the final velocity of each object.
                            gO1.setVelX(gO1Vxf);
                            gO1.setVelY(gO1Vyf);
                            gO2.setVelX(gO2Vxf);
                            gO2.setVelY(gO2Vyf);
                     
                            //those objects can no longer collide (temporary "solution", to prevent objects getting stuck)
                            gO2.setFaction(gO1.getFaction());
                       
                            //More debug info.
                            System.out.println("");
                            System.out.println("M1");
                            System.out.println("x:"+gO1.getXr());
                            System.out.println("y:"+gO1.getYr());
                            System.out.println("Vx:"+gO1.getVelX());
                            System.out.println("Vy:"+gO1.getVelY());
                            System.out.println("theta:"+Math.toDegrees(gO1.theta()));
                            System.out.println("");
                            System.out.println("M2");
                            System.out.println("x:"+gO2.getXr());
                            System.out.println("y:"+gO2.getYr());
                            System.out.println("Vx:"+gO2.getVelX());
                            System.out.println("Vy:"+gO2.getVelY());
                            System.out.println("theta:"+Math.toDegrees(gO2.theta()));
                       
                        }
               
                    }
           
                }
       
            }

       
        }


    }


     
    I ran 4 collisions, here you can see the results:

    Code (Text):

    Initial position, direction and speed

    Angle of collision
    Phi44.55387947558248

    gO1
    x:610.0
    y:307.0
    Vx:-1.0
    Vy:-1.0
    theta, deg:45.0
    theta, rad:0.7853981633974483

    gO2
    x:564.7000000000047
    y:262.39999999999765
    Vx:0.1
    Vy:0.2
    theta, deg:63.43494882292201
    theta, rad:1.1071487177940904

    gO1
    x:610.0
    y:307.0
    Vx:0.1347928708643486
    Vy:0.0780045497865444
    theta, deg:30.057939577479935
    theta, rad:0.5246100119925381

    gO2
    x:564.7000000000047
    y:262.39999999999765
    Vx:0.6618245460937124
    Vy:0.753142930591247
    theta, deg:48.6926021887038
    theta, rad:0.8498462295566787

    Result: awkward collision



    Initial position, direction and speed

    Angle of collision
    Phi-38.54716002467078

    gO1
    x:518.0
    y:308.0
    Vx:1.0
    Vy:-1.0
    theta, deg:-45.0
    theta, rad:-0.7853981633974483

    gO2
    x:567.7000000000054
    y:268.3999999999973
    Vx:0.1
    Vy:0.2
    theta, deg:63.43494882292201
    theta, rad:1.1071487177940904

    gO1
    x:518.0
    y:308.0
    Vx:0.05737619804002281
    Vy:-0.18661970335408506
    theta, deg:-72.9099627395368
    theta, rad:-1.272518907311302

    gO2
    x:567.7000000000054
    y:268.3999999999973
    Vx:-0.6457889329971941
    Vy:-0.45208696110824664
    theta, deg:34.994086837063115
    theta, rad:0.6107620340355598

    Result: Collision seemed correct



    Initial position, direction and speed

    Angle of collision
    Phi:59.37821096161859

    gO1
    x:549.0
    y:241.0
    Vx:1.0
    Vy:1.0
    theta, deg:45.0
    theta, rad:0.7853981633974483

    gO2
    x:581.2000000000085
    y:295.39999999999577
    Vx:0.1
    Vy:0.2
    theta, deg:63.43494882292201
    theta, rad:1.1071487177940904

    gO1
    x:549.0
    y:241.0
    Vx:0.4040808389247806
    Vy:-0.09282509782172968
    theta, deg:-12.937479313695539
    theta, rad:-0.22580161093264345

    gO2
    x:581.2000000000085
    y:295.39999999999577
    Vx:0.49575577862969233
    Vy:0.8686060359455879
    theta, deg:60.28449181093118
    theta, rad:1.052162869992308

    Result: awkward collision



    Initial position, direction and speed

    Angle of collision
    Phi-55.11124190812558

    M1
    x:619.0
    y:246.0
    Vx:-1.0
    Vy:1.0
    theta, deg:-45.0
    theta, rad:-0.7853981633974483

    M2
    x:582.6000000000088
    y:298.1999999999956
    Vx:0.1
    Vy:0.2
    theta, deg:63.43494882292201
    theta, rad:1.1071487177940904

    M1
    x:619.0
    y:246.0
    Vx:0.318051489820771
    Vy:0.059985539221356285
    theta, deg:10.680706869495856
    theta, rad:0.18641350131307896

    M2
    x:582.6000000000088
    y:298.1999999999956
    Vx:-0.4108658742107923
    Vy:-0.7079128926404634
    theta, deg:59.869530325480376
    theta, rad:1.0449204258022249

    Result: awkward collision
     
    Last edited: Aug 2, 2015
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook

Have something to add?
Draft saved Draft deleted