0

I have been doing some code on projectile motion. I initiated a timer t and used the constructor 10 for it, every action event I add 0.01 to a time variable initialised as 0.00, to simulate time going past. Using this time I calculate the height/distance etc. I cannot figure out 2 problems:

1) When printing the current time into the console it keeps going to many decimal places, run code. 2) Height will keep increasing even though it shouldn't, I feel as if it is due to the positive and negative of acceleration.

(My panel is 1920x1080 and the panel animating takes up 80pixels - that's why it is 1000-50) (1000-radius)

Images for use: https://i.stack.imgur.com/dho5L.jpg

From responses: 1) Forgot to multiply vy by t. 2) Use DecimalFormat to truncate the value

Thanks to @G_H ; @Brick ; @Tom ; @Diginoise Code:

package projectV1;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;


public class Animate extends JPanel implements ActionListener {

double x = 0;
double time = 0.00;
String timeText2 = "0";
double y = 1000;
double velY = 0.0;
double velX = 0.0;
public static Timer t;

public Animate() { // Constructor
    super(); 
}

public void start(String acceleration, String initialVelocity, String angle){



    new Ball(acceleration, angle, initialVelocity);

    t = new Timer(10, this);
    t.start();

    }


@Override
 public void actionPerformed(ActionEvent e) {
    updateTime(); 
    move();


      }

 public void move(){ // HOlds the repaint method
        x = x + Ball.getVelX();
        Simulation.distanceText.setText(Double.toString(Ball.getDistance(time))); // Some of these are just checks for me in the gui
        Simulation.velocityText.setText(Double.toString(time));
        y = y- Ball.getVelY(time);
        Simulation.heightText.setText(Double.toString(Ball.getHeight(time)));



        repaint();
    }

 public void updateTime(){
     time= time + 0.01;

     System.out.println(Double.toString(time));  // Using this as a test to check the time output.
     timeText2 = Double.toString(time);
     Simulation.timeText.setText(timeText2);
     }


public void paintComponent(Graphics gg){
    super.paintComponent(gg);
    gg.drawRect(0, 0, 1920, 1000);
    Graphics2D g = (Graphics2D) gg;

     Ellipse2D.Double shape = new Ellipse2D.Double(x, y-50.0, 50, 50);
        g.fill(shape);

    // g.fillOval(x, y-50, 50, 50);

     }
}

Code from '' Ball Class ''

public Ball(String acceleration, String angle, String initialVelocity ) {

    Ball.acceleration = acceleration;
    Ball.initialVelocity = initialVelocity;
    Ball.angle = angle;
}


public static double getVelX(){ //  This stays constant throughout the program
    double velX = 0.0;
    int u = Integer.parseInt(initialVelocity);
    double ang = Double.parseDouble(angle);
    velX = u*(Math.cos(ang));
    return velX;

}

public static double getVelY(double time ){

    double velY = 0.0 ;
    double ang, acc = 0;
    int u = 0;
    ang = Double.parseDouble(angle);
    acc = Double.parseDouble(acceleration);
    u = Integer.parseInt(initialVelocity);

    velY = u*(Math.sin(ang))- (acc*time);
    return velY;
}

public static double getDistance(double time){
    double distance = 0;
    double ang = 0;
    int u = 0;
    ang = Double.parseDouble(angle);

    u = Integer.parseInt(initialVelocity);
    distance = (u*time)*(Math.cos(ang));

    return distance;


}
public static double predictedTime(){
    double ptime = 0;
    double ang = 0;
    ang = Double.parseDouble(angle);
    int u = Integer.parseInt(initialVelocity);
    ptime = ((u*u)*(Math.sin(ang)*Math.sin(ang))/(2*9.81));

    return ptime;
}

public static double getHeight(double time ){
    double height;
    double ang, acc;
    int u = 0;
    ang = Double.parseDouble(angle);
    acc = Double.parseDouble(acceleration);
    u = Integer.parseInt(initialVelocity);
    height = u*(Math.sin(ang))-(0.5*acc*(time*time));

    return height;


}
  • 3
    Possible duplicate of [Double decimal formatting in Java](http://stackoverflow.com/questions/12806278/double-decimal-formatting-in-java) – Tom Apr 24 '17 at 14:04
  • I do not think it is a rounding problem, to be honest I have seen that before and I don't think it helped. If it is, then I am wrong but as you can see it is not the only problem here. I have tried: Math.round(time*100)/100 but it does not work – Haris Rabbani Apr 24 '17 at 14:07
  • If you want help with the physics part, I suggest you write out the equations that you think you're using. I cannot quickly make sense of your code when compared with the problem that you said you were trying to solve. – Brick Apr 24 '17 at 14:09
  • The problem you state with the number of decimal points is almost sure to round-off error and could be controlled following the instructions at the link provided by @Tom. – Brick Apr 24 '17 at 14:11
  • Yeah I will add a screenshot of the 5 I am using within the next 5 minutes. I tried to round it, but it just stayed at 0.0. If you could help with how I should instantiate the timer and relate that to 'real time' milliseconds it would be amazing. The screens will be up in the next 5 minutes – Haris Rabbani Apr 24 '17 at 14:24
  • I have added them @Brick – Haris Rabbani Apr 24 '17 at 14:27
  • I have also edited the question for better understanding – Haris Rabbani Apr 24 '17 at 14:28
  • your time is entirely virtual, so you can count in increments of 1 and use int or long for the time tracking – diginoise Apr 24 '17 at 14:32
  • I don't click links to 3rd party sites - Defeats self-contained question and subjects me to nastiness from bad players. I did notice that you seem to be be missing a factor of `time` on the first term in this expression `height = u*(Math.sin(ang))-(0.5*acc*(time*time));` from `getHeight`. – Brick Apr 24 '17 at 14:33
  • Yes, sorry I was looking to make a virtual time which was the same as milliseconds. and @Brick Sorry, I guess I will do that and yes I did miss that and didn't see it, thank you very much. Also thank you very much everyone else for the help so far, really appreciate it. – Haris Rabbani Apr 24 '17 at 14:49

1 Answers1

0

1) When printing the current time into the console it keeps going to many decimal places, run code.

This is due to the binary representation of fractional numbers. Suppose that we try to represent 1/3 in decimal, you get 0.333333... The expansion is infinite, there's no finite representation. 0.01 (1/100) has a finite representation in decimal, but not in binary. So if you write double d = 0.01;, know that the internal binary representation is an approximation, not the exact value. You'll want to truncate the output to some value with a certain number of decimal places. Check out class DecimalFormat for outputting the values in a specific format.

2) Height will keep increasing even though it shouldn't, I feel as if it is due to the positive and negative of acceleration.

Check this part: velY = u*(Math.sin(ang))- (acc*time);

u is a contant. So is sin(ang). acc*time is only going to increase. So it makes sense that velY is going to change linearly. You've posted this:

formula for vertical displacement

It states Sy = Vy*t - 0.5*g*t², then that Vy = u*sin(ang). There's a time component missing in the expansion of the equation. It should be this: velY = (u*(Math.sin(ang))- (acc*time)) * time; or (same thing theoretically) velY = u*(Math.sin(ang))*time - (acc*time*time); Note that acc must be 1/2 g. If you supply g as acceleration you'll need to multiply acc*time*time with 0.5 as per the formula for vertical displacement.

Final note, you make the various fields into static fields of class Ball, then assign those in the constructor. It's best not to do that. Make fields acceleration, initialVelocity and angle instance fields of Ball (they can be final because they won't change) and make the methods non-static. This would allow you to simulate multiple balls, for example, since their values remain local to them, rather than shared class state.

G_H
  • 11,739
  • 3
  • 38
  • 82
  • Thank you so much for your feedback to my questions, I seem to have forgotten the time element in the first one and that was a mistake by me. I did not know about decimal format before this, so thank you for making me aware of it. About the final point you made however, I was not able to access the methods in the Ball class from my move() method to get the velX and velY if I didn't make them static, but I could try this. Again thank you very much for your time, I will add these changes soon and get back to this comment section. This is my first major school project so sorry for the weird style – Haris Rabbani Apr 24 '17 at 14:55
  • In order to access the methods, you'll have to make an instance of Ball and call methods on that instance. For example: `Ball b1 = new Ball("9.81", "10", "0.78"); b1.getDistance(time);` A class is a template for instances (objects) of that class. The instances have their own local variables and methods called on them operate on that specific instance. This is the core aspect of object-oriented programming. After a while the concepts will "click" and you'll understand what their benefits are. For example, you can easily make multiple Ball instances with their own angles and velocities. – G_H Apr 24 '17 at 15:04
  • I did try this, however I because my move is not accessing the area where I would initialise b1, I was not getting the option to do b1.getDistance. I can give this a go and see how it goes, if I plan to add multiple balls. Again, thank you for the help, I would not have spotted the Vy*t if it was not for you guys. I have got the height to correctly work now and the ball doesn't fall under the screen. I would like to ask, do you happen to know how to scale the program, so that I could display the whole simulation in the JPanel? – Haris Rabbani Apr 24 '17 at 15:12
  • @HarisRabbani That is probably best asked in a separate question. However, it shouldn't be too hard. Do your calculations in some virtual coordinate system (it may be easier to have the y-axis start from 0 and have "up" as positive numbers, instead of the inverted JPanel coordinates), then before rendering transform them to the visible coordinates by scaling (multiplication/division), translation (addition/subtraction) and inverting the y axis. You'll want to keep the scaling of x and y coordinates the same to see a realistic trajectory. – G_H Apr 24 '17 at 15:22
  • Also, in order to fit everything on the panel without having to "pan" the camera, you'll need to calculate first where the ball would land and use that "real" distance relative to your panel width to know how to scale things. You'd need to do the same with the highest point and panel height to make sure the ball doesn't clip outside of the screen vertically. Use the maximum of horizontal/vertical scaling factors for both directions to maintain a realistic path. If you scale vertical and horizontal differently, it changes the visible trajectory. – G_H Apr 24 '17 at 15:26
  • @HarisRabbani if this or any answer has solved your question please consider [accepting it](https://meta.stackexchange.com/q/5234/179419) by clicking the check-mark. This indicates to the wider community that you've found a solution and gives some reputation to both the answerer and yourself. There is no obligation to do this. – G_H Apr 25 '17 at 07:00
  • I have done it @G_H - I am uploading a new question about the scaling soon, thanks for the help – Haris Rabbani Apr 25 '17 at 13:56