Using Objects - Trajectory Calculator
Using java classes, methods, and math to graph a trajectory.
Code
See the source code here or below: https://github.com/supermengman/using-objects/tree/main/src/main/java/com/hacks/trajectoryCalculator
package com.hacks.trajectoryCalculator; // maven build
// import the graphs
import org.jfree.data.function.Function2D;
import org.jfree.data.general.*;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYDataset;
// import the other files
import com.hacks.trajectoryCalculator.*;
// import swing and awt
import javax.swing.JFrame;
import java.awt.Dimension;
public class TrajectoryGraph extends JFrame {
// Constructor of the graph that will be displayed
public TrajectoryGraph() {
InputInitial newInputs = new InputInitial(); // start input object from separate file
newInputs.spawnInputs(); // start the process of collecting user input
drawGraph(InputInitial.initialVelocity, InputInitial.initialDegrees, InputInitial.initialHeight); // call method to draw the graph with the user input taken
}
// graph drawer
public void drawGraph(double velocity, double degrees, double height) {
Function2D test = new TrajectoryMath(velocity, degrees, height); // TrajectoryMath implements function2d, so use those values create new function2d object
TrajectoryMath testGetter = new TrajectoryMath(velocity, degrees, height); // creates TrajectoryMath object from the other file to call custom methods
XYDataset dataset = DatasetUtils.sampleFunction2D(test, 0.0, testGetter.getRoot(), 50, "Function"); // generates the dataset of xy values with the function
final JFreeChart chart = ChartFactory.createXYLineChart("Trajectory Equation", "X Position (meters)", "Y Position (meters)", dataset, PlotOrientation.VERTICAL, true, true, false); // creates the actual graph with attributes
// initializing the display
ChartPanel cp = new ChartPanel(chart) {
@Override
public Dimension getPreferredSize() {
return new Dimension(500, 500); // set initial dimension
}
};
// allow mouse wheel scrolling
cp.setMouseWheelEnabled(true);
add(cp);
// Finalize the building of the graph
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
}
public static void main(String[] args) {
// runs the creation of the graph with a queue in a different thread and posts the gui after events are processed
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
// initialize new TrajectoryGraph object, setvisible to display the graph
new TrajectoryGraph().setVisible(true);
}
});
}
}
package com.hacks.trajectoryCalculator;
import org.jfree.data.function.Function2D;
/*
* Actual math for calculating trajectory
*
* Calculates equation as a string and returns it
*
*
*/
public class TrajectoryMath implements Function2D {
// Initialize variables
private double velocity;
private double degrees;
private double height;
private String equation;
private double coefficientA;
private double coefficientB;
private double coefficientC;
// Constructor
public TrajectoryMath(double velocity, double degrees, double height) {
this.velocity = velocity;
this.degrees = degrees;
this.height = height;
this.calculateEquation();
}
// Getters and setters
public double getVelocity() {
return this.velocity;
}
public double getDegrees() {
return this.degrees;
}
public double getHeight() {
return this.height;
}
public String getEquation() {
return this.equation;
}
public double getCoefficientA() {
return this.coefficientA;
}
public double getCoefficientB() {
return this.coefficientB;
}
public double getCoefficientC() {
return this.coefficientC;
}
public double getRoot() {
double rootOne = (-this.coefficientB + Math.sqrt(Math.pow(coefficientB, 2) - 4 * (-this.coefficientC) * this.coefficientA)) / (2 * (-this.coefficientC));
double rootTwo = (-this.coefficientB - Math.sqrt(Math.pow(coefficientB, 2) - 4 * (-this.coefficientC) * this.coefficientA)) / (2 * (-this.coefficientC));
if (rootOne > 0) {
return rootOne;
} else if (rootTwo > 0) {
return rootTwo;
} else {
return 0.0;
}
}
public void setVelocity(double velocity) {
this.velocity = velocity;
}
public void setDegrees(double degrees) {
this.degrees = degrees;
}
public void setHeight(double height) {
this.height = height;
}
// Array should be in order of velocity, degrees, and height
public void setParameters(double[] values) {
this.velocity = values[0];
this.degrees = values[1];
this.height = values[2];
}
// Math for equation
private void calculateEquation() {
this.coefficientA = height;
this.coefficientB = Math.tan(degrees * Math.PI/180);
this.coefficientC = 9.8 / (2 * Math.pow(velocity, 2) * Math.pow(Math.cos(degrees * Math.PI/180), 2));
String precheckedEquation = "y = " + String.valueOf(coefficientA) + " + " + String.valueOf(coefficientB) + "x - " +
String.valueOf(coefficientC) + "x^2";
// Fix double negative, but later
//if (precheckedEquation.contains("- -")) {
//}
//Add threshold
this.equation = precheckedEquation;
}
public double getValue(double v) {
return coefficientA + coefficientB * v - coefficientC * Math.pow(v, 2);
}
/*
* public static void main(String[] args) {
TrajectoryMath example = new TrajectoryMath(15, 60, 4);
System.out.println(example.getRoot());
}
*/
}
package com.hacks.trajectoryCalculator;
import javax.swing.JOptionPane; // library to display options
import javax.swing.JTextField; // library to create a text field to render on GUI
public class InputInitial {
// instance variables to be used
public static Double initialVelocity;
public static Double initialDegrees;
public static Double initialHeight;
// create the GUI element that users input into
public void spawnInputs() {
// while the values have not changed yet, keep going (for error handling)
while (initialVelocity == null || initialDegrees == null || initialHeight == null) {
// text field initialization
JTextField inputVelocity = new JTextField();
JTextField inputDegrees = new JTextField();
JTextField inputHeight = new JTextField();
// organizing the input text to display + the text field in object
Object[] inputs = {
"Initial Velocity (m/s):", inputVelocity,
"Initial Degrees:", inputDegrees,
"Initial Height (m):", inputHeight
};
JOptionPane.showConfirmDialog(null, inputs, "Input the initial values for your object (numbers only):", JOptionPane.OK_CANCEL_OPTION); // creates the option menu with the 3 inputs
// take the input, assign it to the public variables
initialVelocity = parseInput(inputVelocity);
initialDegrees = parseInput(inputDegrees);
initialHeight = parseInput(inputHeight);
}
}
// change JTextField into Double, also error handling
public Double parseInput(JTextField inputValue) {
String placeholder = inputValue.getText(); // get the string out of the input
// error handling + edge cases
try {
double initialValue = Double.parseDouble(placeholder);
// if negative, cannot be valid so throw error --> reinput values bc still null
if (initialValue < 0.0) {
JOptionPane.showMessageDialog(null, "Inputs must be greater than 0", "Invalid Input", JOptionPane.WARNING_MESSAGE);
return null;
} else { // if everything ok, return the value
return initialValue;
}
} catch (Exception e) { // if cannot be cased as a double, throw error --> reinput values
JOptionPane.showMessageDialog(null, "There was an invalid input for " + placeholder + ", please try again. " + e, "Unwanted Input", JOptionPane.WARNING_MESSAGE);
return null;
}
}
}
Project Description
In this project, I worked with Bailey to create this program. The code is split into 3 files: one to draw the graph, one to gather user input, and one to calculate the trajectory equation. The overall code structure flows like so:
- initialize TrajectoryGraph object, and call the user input method
- the user inputs the initial velocity, initial height, and initial degrees into a JOptionsPane
- the data is fed into the calculator, which returns height given a x value
- the graph is initialized, and drawn using Function2D
- the graph is then presented to the user as a GUI element
public double getRoot() {
double rootOne = (-this.coefficientB + Math.sqrt(Math.pow(coefficientB, 2) - 4 * (-this.coefficientC) * this.coefficientA)) / (2 * (-this.coefficientC));
double rootTwo = (-this.coefficientB - Math.sqrt(Math.pow(coefficientB, 2) - 4 * (-this.coefficientC) * this.coefficientA)) / (2 * (-this.coefficientC));
if (rootOne > 0) {
return rootOne;
} else if (rootTwo > 0) {
return rootTwo;
} else {
return 0.0;
}
}
In this section from the TrajectoryMath.java file, the Math class is used to calculate the quadratic formula. This can be seen with the Math.sqrt(Math.pow(coefficientB, 2) - 4 * (-this.coefficientC) * this.coefficientA))
, which uses the class methods of square root and exponents.
private void calculateEquation() {
this.coefficientA = height;
this.coefficientB = Math.tan(degrees * Math.PI/180);
this.coefficientC = 9.8 / (2 * Math.pow(velocity, 2) * Math.pow(Math.cos(degrees * Math.PI/180), 2));
String precheckedEquation = "y = " + String.valueOf(coefficientA) + " + " + String.valueOf(coefficientB) + "x - " +
String.valueOf(coefficientC) + "x^2";
// Fix double negative, but later
//if (precheckedEquation.contains("- -")) {
//}
//Add threshold
this.equation = precheckedEquation;
}
In this section from the TrajectoryMath.java file, the Math class is used again to calculate coefficientC, with both exponent and using the trig function of cosine.
// TrajectoryGraph.java
public TrajectoryGraph() {
InputInitial newInputs = new InputInitial(); // start input object from separate file
newInputs.spawnInputs(); // start the process of collecting user input
drawGraph(InputInitial.initialVelocity, InputInitial.initialDegrees, InputInitial.initialHeight); // call method to draw the graph with the user input taken
}
// TrajectoryMath.java
public TrajectoryMath(double velocity, double degrees, double height) {
this.velocity = velocity;
this.degrees = degrees;
this.height = height;
this.calculateEquation();
}
As seen above, these are two different constructors from different files. One of them initiates a graph object, while the other is used to calculate the function to display.
// InputInitial.java
public static Double initialVelocity;
public static Double initialDegrees;
public static Double initialHeight;
The wrapper class of Double defines the static data of the initialValues used in InputInitial. I used the wrapper class in order to be able to use the default null value to do error handling, as without being a wrapper class the double has to be a number.
Working as a Team
To complete this, me and my partner Bailey Say split up the work in order to be more efficient. Bailey worked on creating the TrajectoryMath.java file, which took care of getting and setting the variables to create the function. It also created the function itself, and he did research to find the formula to calculate a trajectory.
As for myself, I worked on getting GUI to display as well as creating dialog boxes for the user to input. For this, I worked on the TrajectoryGraph.java as well as InputInitial.java. I also worked on error handling in input, cutting off negative inputs as well as non double inputs.
In addition to working on GUI, I also attempted to use spring boot and maven to build the project. Even though I could not exactly figure out how to put it on a website, I at least resolved issues with building through maven. I learned that the package in front of the files has to correspond with the specific folder in order for maven to know what to import, and that allowed us to use these multiple files to run a single program.
Conclusion
Overall, this project helped me and Bailey to understand more about how projects in Java are built, having to figure out how to use tools like maven. It also allowed us to experiment with multi file java programs, finding out how calling methods from other files works as well as classes. In addition to being good learning for CSA, it also contributes to me and Bailey's experience in AP Physics.