I'm creating a simple Java circle calculator but am having issues getting my KeyListeners to respond quickly. The x and y values in the Circle class are not stored immediately but rather when I delete the input. For example if I input 3 for x and 5 for y the Circle class is returning values of 0 until I delete the values, then it will return the proper values. My goal is to immediately display data related to this coordinates when the user enters values. I can't do this while the KeyListeners are lagging. Thank you.
**Edit: Just realized that the values will also be correct if I hit another key afterwards (like ctrl for example)
**2nd Edit: Added Circle code at bottom
**3rd Edit: Added Point code at bottom
Here is my code:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.text.DecimalFormat;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class CircleGUI extends JFrame
{
Circle c;
JFrame frame;
DecimalFormat df;
private JPanel panel;
private JLabel radiusLabel, areaLabel, coordinateLabel, containsPointLabel;
private JButton calculateButton;
private JTextField radiusTextField, xTextField, yTextField;
final int WINDOW_WIDTH = 500;
final int WINDOW_HEIGHT = 300;
public static void main(String[] args)
{
Circle c = new Circle();
CircleGUI test = new CircleGUI(c);
}
public CircleGUI(Circle c)
{
this.c = c;
setTitle("Circle Calculator");
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
buildPanel();
add(panel);
df = new DecimalFormat("#.##");
setVisible(true);
}
private JPanel buildPanel()
{
radiusLabel = new JLabel("Enter radius:");
areaLabel = new JLabel();
radiusTextField = new JTextField(15);
calculateButton = new JButton("Calculate Area");
calculateButton.addActionListener(new CalculateAreaButtonListener());
coordinateLabel = new JLabel("Origin of the Circle:");
containsPointLabel = new JLabel("");
xTextField = new JTextField(4);
xTextField.addKeyListener(new xCoordinateTextFieldListener());
yTextField = new JTextField(4);
yTextField.addKeyListener(new yCoordinateTextFieldListener());
panel = new JPanel();
//panel.setLayout(new FlowLayout());
panel.add(radiusLabel);
panel.add(radiusTextField);
panel.add(calculateButton);
panel.add(areaLabel);
panel.add(coordinateLabel);
panel.add(xTextField);
panel.add(yTextField);
panel.add(containsPointLabel);
return panel;
}
private class CalculateAreaButtonListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
areaLabel.setText(String.valueOf(df.format(calculateArea())));
}
public double calculateArea()
{
double radius = Double.parseDouble(radiusTextField.getText());
c.setRadius(radius);
return c.getArea();
}
}
private class xCoordinateTextFieldListener implements KeyListener
{
#Override
public void keyPressed(KeyEvent e)
{
double x;
if (!xTextField.getText().equals(null) && !xTextField.getText().equals(""))
{
x = Double.parseDouble(xTextField.getText());
c.setX(x);
}
}
#Override
public void keyReleased(KeyEvent e)
{
System.out.println("x: " + c.getX());
}
#Override
public void keyTyped(KeyEvent arg0)
{
}
}
private class yCoordinateTextFieldListener implements KeyListener
{
#Override
public void keyPressed(KeyEvent e)
{
double y;
if (!yTextField.getText().equals(null) && !yTextField.getText().equals(""))
{
y = Double.parseDouble(yTextField.getText());
c.setY(y);
}
;
}
#Override
public void keyReleased(KeyEvent e)
{
System.out.println("y: " + c.getY());
}
#Override
public void keyTyped(KeyEvent arg0)
{
}
}
}
Circle code:
public class Circle
{
//Instantiate and declare instance fields
Point origin = new Point();
double radius;
Circle(double radius)
{
this.radius = radius;
origin.setX(0);
origin.setY(0);
}
//Two arg-constructor
Circle(Point origin, double radius)
{
this.origin = origin;
this.radius = radius;
}
//Three-arg constructor
Circle(double xValue, double yValue, double radius)
{
origin.setX(xValue);
origin.setY(yValue);
this.radius = radius;
}
//Default constructor
Circle()
{
setX(0);
setY(0);
radius = 0;
}
//Copy constructor
Circle(Circle c)
{
setX(c.getX());
setY(c.getY());
radius = c.getRadius();
}
//Get origin of the circle
Point getOrigin()
{
return origin;
}
//Set origin of the circle
void setOrigin(Point point)
{
this.origin = point;
}
//Set x value
void setX(double x)
{
origin.setX(x);
}
//Get x value
double getX()
{
return origin.getX();
}
//Set y value
void setY(double y)
{
this.origin.setY(y);
}
//Get y value
double getY()
{
return origin.getY();
}
//Set radius field
void setRadius(double radius)
{
this.radius = radius;
}
//Get radius field
double getRadius()
{
return radius;
}
//Get area of the circle
double getArea()
{
//Use constant provided in the static method Math.PI
return Math.PI * (radius * radius);
}
//Translate instance fields to a human readable String
public String toString()
{
return "x: " + this.getX() + ", y: " + this.getY() + ", radius: " + this.radius;
}
//Compare whether two circles instance fields are equal
boolean equals(Circle c)
{
if (getX() == c.getX() && getY() == c.getY() && getRadius() == c.getRadius())
return true;
else
return false;
}
double getDistance(double xValue, double yValue)
{
double x = xValue - this.getX();
double y = yValue - this.getY();
return Math.sqrt((x * x) + (y * y));
}
double getDistance(Circle c)
{
//Initialize variables to x^2-x^1 and y^2-y^1
double x = c.getX() - this.getX();
double y = c.getY() - this.getY();
//Calculate distance to center of the circle
return Math.sqrt((x * x) + (y * y));
}
boolean containsPoint(double xValue, double yValue)
{
if (getDistance(xValue, yValue) <= radius)
return true;
else
return false;
}
//Test if two circle overlap
boolean doesOverlap(Circle c)
{
//Initialize sumOfRadii to the sum of both circles radius
double sumOfRadii = c.getRadius() + radius;
//If the distance to the center of the circle is less than or equal to the sum of the two circles radius then return true, otherwise return false.
if (getDistance(c) <= sumOfRadii)
return true;
else
return false;
}
}
Point Code:
public class Point
{
private double x;
private double y;
public Point(double xValue, double yValue)
{
x = xValue;
y = yValue;
}
public Point(Point p) {
this(p.x, p.y);
}
public Point() {
this(0, 0);
}
public void setX(double xValue)
{
this.x = xValue;
}
public double getX()
{
return x;
}
public void setY(double yValue)
{
this.y = yValue;
}
public double getY()
{
return y;
}
public boolean equals(Point otherPoint)
{
return (this.x == otherPoint.x) && (this.y == otherPoint.y);
}
public String toString() {
return "(" + x + ", " + y + ")";
}
}
Keylistener is not the right choice for responding to changes in a text field. See How to Write a Document Listener for an alternate approach.
Firstly, I wouldn't use a KeyListener to monitor changes to a text field, a DocumentListener would provide more useful information to the changes of the underlying field, but an ActionListener would probably be more appropriate under these conditions.
Take a look at How to write an Action Listener and How to use text fields for more details
Related
I'm making a simple Java program to bounce a ball up and down. The problem is that the ball bounces up higher than its starting point with each bounce. I expect the ball to bounce back up exactly to the height that it started from.
The ball physics can be found in the circle class in the doPhysics() method where I suspect the problem can be found
import java.awt.*;
import java.util.*;
public class Main{
public static Frame frame = new Frame();
public static Physics physics = new Physics();
public static ArrayList<Circle> circles = new ArrayList<Circle>(); //array for the points
public static void main(String args[]) {
Circle circle = new Circle(100, 300, 50, Color.BLACK);
circles.add(circle);
run();
}
public static void run() {
physics.timer.start();
}
}
import java.awt.*;
public class Circle {
private int x;
private int y;
private double xAccel= 0;
private double yAccel = 0;
private double xVel= 0;
private double yVel = 0;
private Color colour;
private int radius;
public Circle(int x, int y, int radius, Color colour) {
setX(x);
setY(y);
setRadius(radius);
setColour(colour);
}
public void draw(Graphics2D g2d) {
g2d.setColor(colour);
g2d.fillOval(x, y, radius*2, radius*2);
}
public void doPhysics() {
hitGround();
System.out.println(yVel);
yVel += Physics.getGravity();
y -= yVel;
}
public void hitGround() {
if(y + radius*2 > Frame.panel.h ) {
yVel = -yVel;
}
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setColour(Color colour) {
this.colour = colour;
}
public void setRadius(int radius) {
this.radius = radius;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Color getColour() {
return colour;
}
public int getRadius() {
return radius;
}
}
import java.awt.*;
import javax.swing.*;
class Frame extends JFrame {
public static Panel panel;
public Frame() {
panel = new Panel();
this.setTitle("Fun");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.add(panel);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
}
class Panel extends JPanel {
public int w = 500;
public int h = 500;
public Panel() {
this.setPreferredSize(new Dimension(w, h));
this.setBackground(Color.red);
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
for(Circle circle : Main.circles) {
circle.draw(g2d);
}
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Physics implements ActionListener {
private static double gravity = -.1;
public Timer timer;
public Physics() {
timer = new Timer(1, this);
}
public static double getGravity() {
return gravity;
}
#Override
public void actionPerformed(ActionEvent e) {
for(Circle circle : Main.circles) {
circle.doPhysics();
}
Main.frame.repaint();
}
}
The problem is mainly caused by using integer values for position (x and y). On each iteration the values are rounded and the errors get accumulated.
Solution: declare double x and double y and only use the rounded integer values for drawing.
Above should reduce the problem, but not completely solve it. The code is doing a rough integration over timeĀ¹ by using the velocity calculated after the time interval (see Numerical Integration). This can be improved by doing an average of the velocities before and after it was changed. Roughly:
double preVel = yVel;
yVel += Physics.getGravity();
y -= (preVel + yVel)/2;
which can be simplified (pure math) to:
yVel += Physics.getGravity();
y -= yVel - Physics.getGravity()/2;
This should work fine since the acceleration is constant. Not the case if the acceleration is also changing. And it is also susceptible to precision errors being accumulated over time.
1 - see Numerical integration and Temporal discretization
Could someone explain to me why I am getting these errors on my coding when it comes to the main method project5 class. thearray[count++] keeps giving me errors, and I am not entirely sure where I made my error. I've been stuck on it for awhile now. Some guidance would be nice.
Here is my main method:
public class Project5 {
private Shape [] thearray = new Shape[100];
public static void main (String [] args) {
Project5 tpo = new Project5();
tpo.run();
}
public void run () {
int count = 0;
thearray[count++] = new Circle(20, 20, 40);
thearray[count++] = new Triangle(70, 70, 20, 30);
thearray[count++] = new Rectangle(150, 150, 40, 40);
for (int i = 0; i < count; i ++ ) {
thearray[i].display();
}
int offset = 0;
double totalarea = 0.0;
while (thearray[offset] != null) {
totalarea = totalarea + thearray[offset].area();
offset++;
}
System.out.println("The total area for " + offset + " Shape objects is " + totalarea);
}
}
Here is my Circle class:
public class Circle {
private double radius;
public Circle() {
radius = 1.0;
}
public Circle(double newRadius) {
radius = 1.0;
setRadius(newRadius);
}
public void setRadius(double newRadius) {
if(newRadius > 0) {
radius = newRadius;
}
else {
System.out.println("Error: "
+ newRadius + " is a bad radius value.");
}
}
public double getRadius() {
return radius;
}
public double getArea() {
return radius * radius * Math.PI;
}
}
and here is my Shape class:
abstract class Shape{
int x = 1;
int y = 1;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public Shape(int x, int y) {
this.x = x;
this.y = y;
}
public void display() {
}
public abstract double area();
}
I would post my triangle and rectangle class, but I figured this would be enough to at least have it explained where I am messing up.
I looked at your Circle class and the constructor currently only takes one argument. However, when you create a Circle you are specifying 3 arguments.
Circle should probably extend from Shape and the first two parameters are then the x and y position of the shape, correct? If this is the case, change your class as follows:
public class Circle extends Shape {
private double radius;
public Circle(int x, int y) {
super(x, y);
radius = 1.0;
}
public Circle(int x, int y, double newRadius) {
super(x, y);
setRadius(newRadius);
}
...etc
I am having trouble using setter methods from the main class with a JFrame "position". The active parts of the JFrame(triangles called "turtles" moving about) are leaving the JFrame and my current method of changing the coordinates when this happens is not working. Any help would be greatly appreciated, as I have been stuck for days.
The method wrapPosition is designed to change the coordinate if they're off the "screen" which is (800x600)pixels. This check is in the loop so a turtle never goes off the screen.
here is the Main Class:
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
class Lab7b
{
public static void main(String [ ] args)
{
int deltaTime = 50;
JFrame frame = new JFrame();
Canvas canvas = new Canvas();
frame.setTitle("Welcome to turtle land!");
frame.setSize(800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.add(canvas);
ArrayList<DynamicTurtle> turtles = new ArrayList<DynamicTurtle>(); //An ArrayList containing Turtle elemenets
turtles.add(new RandomTurtleA(turtleCanvas, 400, 300));
turtles.add(new RandomTurtleA(turtleCanvas, 400, 300));
turtles.add(new RandomTurtleA(turtleCanvas, 400, 300));
while(true)
{
for (int i = 0; i < turtles.size(); i++)
{
(turtles.get(i)).unDrawTurtle();
hello.setText("X: " + (turtles.get(i)).getPositionX() + " Y: " + (turtles.get(i)).getPositionY()); //print to JFRAME
System.out.println("X: " + (turtles.get(i)).getPositionX() + " Y: " + (turtles.get(i)).getPositionY()); //print to CMD
(turtles.get(i)).wrapPosition((turtles.get(i)).getPositionX(), (turtles.get(i)).getPositionY()); //this is the wrapPosition method that does not work
}
for (int i = 0; i < turtles.size(); i++)
{
(turtles.get(i)).update(1000);
}
for (int i = 0; i < turtles.size(); i++)
{
(turtles.get(i)).drawTurtle();
}
Utils.pause(deltaTime/2);
}
}
}
the Turtle class with the troublesome wrapPosition method:
class Turtle
{
protected Canvas canvas; // private field reference to a canvas private
private CartesianCoordinate myLocation, oldLocation;
private boolean penDown = true;
private double Angle, maxX, maxY, x, y;
public double d, e, first, second;
public Turtle(Canvas canvas, CartesianCoordinate initLocation)
{
this.canvas = canvas;
this.myLocation = new CartesianCoordinate(0,0);
Angle = 0;
penDown = true;
myLocation = initLocation.copy();
}
public void wrapPosition(double x, double y)
{
this.x = maxX;
this.y = maxY;
if(maxX < 0)
{
this.setPositionX(800);
}
if(maxX > 800)
{
this.setPositionX(0);
}
if(maxY < 0)
{
this.setPositionY(600);
}
if(maxY > 600)
{
this.setPositionY(0);
}
}
public double getPositionX()
{
double getPosX;
getPosX = myLocation.getX();
return getPosX;
}
public double getPositionY()
{
double getPosY;
getPosY = myLocation.getY();
return getPosY;
}
public void setPositionX(double x)
{
myLocation.setX(x);
}
public void setPositionY( double y)
{
myLocation.setY(y);
}
}
Also, if it is any help the coordinate class:
class CartesianCoordinate
{
private double xPosition, yPosition, setterX, setterY;
public CartesianCoordinate(double x, double y)
{
this.xPosition = x;
this.yPosition = y;
}
public double getX()
{
return this.xPosition;
}
public double getY()
{
return this.yPosition;
}
public void setX(double setterX)
{
this.setterX = xPosition;
}
public void setY(double setterY)
{
this.setterY = yPosition;
}
}
Any Help would be greatly appreciated. Thank you in advance.
The "setters" in class CartesianCoordinate do virtually nothing because they just assign to member variables which are not read.
public void setX(double setterX)
{
this.setterX = xPosition;
}
public void setY(double setterY)
{
this.setterY = yPosition;
}
Try these instead to have coordinates to be read be updated:
public void setX(double setterX)
{
this.xPosition= setterX;
}
public void setY(double setterY)
{
this.yPosition= setterY;
}
So, right now, I'm able to create multiple balls and update them all properly through a Vector of objects and a for loop to update each object independently. The problem is that for every ball after the first, the ball that is created seems to affect the momentum and position of the other balls, causing all the balls to abruptly change their flight paths.
Code for creating frame and managing ball creation based on mouselistener:
public class FrameCreation extends JFrame{
static Vector<BallCreate> ballObjects = new Vector<BallCreate>();
static Timer timer;
Point m1;
Point m2;
static JFrame frame1;
static mouseHandler mouse;
public static void main(String args[]){
FrameCreation frame = new FrameCreation();
frame1 = new JFrame("Phyiscs Test");
frame1.setVisible(true);
frame1.setSize(520,530);
frame1.setDefaultCloseOperation(frame1.EXIT_ON_CLOSE);
mouse = frame.new mouseHandler();
frame1.addMouseListener(mouse);
}
public class mouseHandler extends JPanel implements MouseListener{
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
m1 = e.getPoint();
System.out.println("Mouse pressed");
}
#Override
public void mouseReleased(MouseEvent e) {
m2 = e.getPoint();
Thread queryThread = new Thread(){
double vX = m2.getX() - m1.getX();
double vY = m2.getY() - m1.getY();
public void run(){
createBall(m1.getX(),m1.getY(),vX,vY);
}
};
queryThread.start();
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
}
public void createBall(double posX, double posY, double vX, double vY){
BallCreate newBall = new BallCreate(posX,posY,50,vX,vY);
ballObjects.add(newBall);
frame1.add(newBall);
frame1.revalidate();
newBall.repaint();
}
}
Code for drawing and updating balls:
public class BallCreate extends JPanel {
Timer timer;
int diam;
Color color;
double vX, vY, posX, posY;
final double G = 30;
public BallCreate(double posX, double posY, int diam, double vX, double vY){
this.posX = posX;
this.posY = posY;
this.vX = vX;
this.vY = vY;
this.diam = diam;
color = getRandomColor();
timer = new Timer(100, new MovementUpdate());
timer.start();
}
public void drawing(){
repaint();
}
public void motionUpdate(){
for(BallCreate ball : FrameCreation.ballObjects){
double t = 0.1;
double dX = ball.vX*t;
double dY = (ball.vY + G*t)*t;
double v2Y = G*t + ball.vY;
System.out.println("Ball v2y: " + ball.vY);
ball.posX = ball.posX + dX;
ball.posY = ball.posY + dY;
vY = v2Y;
drawing();
wallCollisionCheck();
}
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
for(BallCreate ball : FrameCreation.ballObjects){
g.setColor(ball.color);
//System.out.println("pos X: " + posX);
//System.out.println("pos Y: " + posY);
g.fillOval((int)ball.posX, (int)ball.posY,
ball.diam ,ball.diam);
}
}
public void wallCollisionCheck(){
for(BallCreate ball : FrameCreation.ballObjects){
double botBound = 500-(ball.posY + ball.diam);
if((botBound <=0 && vY>=0)|| (ball.posY <=0 && ball.vY <=0 )){
//System.out.println("Collision Noted");
//System.out.println("Prev vY: " + ball.vY);
ball.vY = ball.vY * -0.65;
//System.out.println("Post vY: " + ball.vY);
ball.posY = 460;
}
if((ball.posX <= 0 && ball.vX <= 0) || ((Math.abs(500 - ball.posX) <= 40)&& ball.vX >=0)){
ball.vX = ball.vX * -0.65;
}
drawing();
}
}
public Color getRandomColor(){
int R = (int)(255*Math.random());
int G = (int)(255*Math.random());
int B = (int)(255*Math.random());
Color c = new Color(R, G , B);
return c;
}
public class MovementUpdate implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
for(BallCreate ball : FrameCreation.ballObjects){
ball.motionUpdate();
}
}
}
}
I realize that the problem is slightly vague, but it's simply hard to describe without being able to show an image of what is happening. Any help is appreciated, and clarification of code can be done if necessary. Thanks.
I'm having a hard time figuring out why when I create an Circle instance with no arguments and adding the values after with setters isn't working? When I delete the line Circle c3 = new Circle() my program works fine. I also can't call the getters to see if the setters worked.
public class MyCircle {
private double radius;
private double x;
private double y;
public MyCircle()
{
x = 0;
y = 0;
radius = 0;
}
public MyCircle(double X, double Y, double rad)
{
x = X;
y = Y;
radius = rad;
}
public void setX(double value)
{
x = value;
}
public void setY(double value)
{
y = value;
}
public void setRadius(double value)
{
radius = value;
}
public double getX()
{
return x;
}
public double getY()
{
return y;
}
public double getRadius()
{
return radius;
}
public double getArea()
{
return (radius * radius) * Math.PI;
}
public boolean doesOverlap(MyCircle oC)
{
double distance = Math.sqrt((Math.pow(x - oC.x, 2) + Math.pow(y-oC.y, 2)));
if ((radius + oC.radius) > distance)
{
return true;
}
else
{
return false;
}
}
public static void main(String[] args) {
Circle c3 = new Circle();
Circle c1 = new Circle(1.0, 2.0, 3.0);
Circle c4 = new Circle(1.0, 6.0, .5);
c3.setX(1.0);
c3.setY(2.0);
c3.setRadius(5.0);
System.out.println(c3.getArea());
System.out.println(c1.getRadius());
System.out.println(c1.toString());
System.out.println(c4.toString());
if (c1.equals(c4))
{
System.out.println("c1 equals c4");
}
else
{
System.out.println("c1 does not equal c4");
}
if (c1.doesOverlap(c4))
{
System.out.println("c1 Overlaps c4");
}
else
{
System.out.println("c1 Does Not Overlap c4");
}
}
}
Your class is called MyCircle not Circle. You are trying to create Circle objects.
Replace:
Circle c3 = new Circle();
With:
MyCircle c3 = new MyCircle();
your class name is MyCircle instead of Circle
change the name of the class for Circle
Or change where you instantiate for MyCircle