Java Dragging Drawn Balls - java

I have created a program which draws a circle when I click on the screen. I have it working so that I can draw as many circles as I want. I can even drag one circle and not the others if I hard code which circle I am dragging. The code is:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.List;
import java.util.ArrayList;
public class DrawBall extends JPanel implements MouseListener, MouseMotionListener {
private List<Ball> balls;
private int x, y;
private int numBalls = 0;
boolean drag = false;
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("Draw Ball");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JComponent newContentPane = new DrawBall();
newContentPane.setOpaque(true);
frame.setContentPane(newContentPane);
frame.setSize(300, 300);
frame.setVisible(true);
}
public DrawBall() {
super(new BorderLayout());
balls = new ArrayList<Ball>(10);
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RederingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for(Ball ball: balls) {
ball.paint(g2d);
}
g2d.dispose();
}
public void mouseClicked(MouseEvent event) {
x = (int) event.getPoint().getX();
y = (int) event.getPoint().getY();
Ball ball = new Ball(Color.red, numBalls, 30);
ball.setX(x);
ball.setY(y);
balls.add(ball);
numBalls = numBalls + 1;
repaint();
}
public void mousePressed(MouseEvent event) {
drag = true;
}
public void mouseReleased(MouseEvent event) {
drag = false;
}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mouseDragged(MouseEvent event) {
if(drag == true) {
x = (int) event.getPoint().getX();
y = (int) event.getPoint().getY();
if(event.getSource() == balls.get(0)) {
Ball ball = balls.get(0);
ball.setX(x);
ball.setY(y);
balls.set(0,ball);
}
repaing();
}
}
public void mouseMoved(MouseEvent event) {}
public class Ball {
private Color color;
private int x, y, diameter, id;
public Ball(Color color, int id, int diameter) {
setColor(color);
setID(id);
setDiameter(diameter);
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setID(int id) {
this.id = id;
}
public void setDiameter(int diameter) {
this.diameter = diameter;
}
public void setColor(Color color) {
this.color = color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getID() {
return id;
}
public int getDiameter() {
return diameter;
}
public Color getColor() {
return color;
}
protected void paint(Graphics2D g2d) {
int x = getX();
int y = getY();
g2d.setColor(getColor());
g2d.fillOval(x, y, getDiameter(), getDiameter());
}
}
}
My question is this: In my mouseDragged method, is there an easy way to tell which circle I am hovering over? I have played around with the event.getSource() but it doesn't seem to be working for my circles, at least not in the way I would expect. Thanks for any help.

Modify your Ball class so it creates a circle based on the center point and the radius, rather than the upper left point and the diameter.
Then, you can calculate if you click inside of a circle by applying the distance formula to the point you've clicked and the center point of each of the balls in turn.
The first ball where the distance is less than the radius is the ball you've clicked on. Or, if you want to be more sophisticated, the ball with the smallest distance less than the radius is the ball you've clicked on.

In this case, the event source is the JPanel, not the Ball. Try adding a System.out.println(event.getSource()); in your mouseDragged() method to see for yourself. As others have suggested, just calculate distance from the mouse pressed point to your circles. Basing it on center/radius will make the math easier.

You could modify you Ball class to take advantage of the Shape's API and use an Ellipse2D to maintain the coordinates of the circle and render it.
You could then simply use the Ellipse2D#contains to determine if the shape was clicked or not.
You will have to loop through the collection of balls checking each one until you either reach the end or find the ball you are looking for

Related

My Java Bouncing Ball Bounces up with a larger velocity

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

How do you rotate a circle about a point in JPanel?

I am trying to rotate a circle around a separate point in a program. right now I can get the circle to rotate but it slowly starts getting closer and closer to the point it's rotating from. I am trying to do this using JPanel and implementing it as a rectangle.
package WoffindenZone;
import java.awt.*;
import javax.swing.*;
import java.util.*;
import java.awt.event.*;
import java.lang.Math;
public class Protector extends Rectangle{
double Velocity;
int speed = 3;
Protector(int x, int y, int PROTECTOR_DIAMETER){
super(x,y,PROTECTOR_DIAMETER,PROTECTOR_DIAMETER);
}
public void keyPressed(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_A) {
setDirection(speed);
move();
}
if(e.getKeyCode()==KeyEvent.VK_D) {
setDirection(speed);
move();
}
}
public void keyReleased(KeyEvent e){
if(e.getKeyCode()==KeyEvent.VK_A) {
setDirection(0);
move();
}
if(e.getKeyCode()==KeyEvent.VK_D) {
setDirection(0);
move();
}
}
public void setDirection(int Direction){
Velocity = Direction*Math.PI/180;
}
public void move(){
x = (int)Math.round(500 + Math.cos(Velocity) * (x-500) - Math.sin(Velocity) * (y-((1000*0.5555)/2)));
y = (int)Math.round(((1000*0.5555)/2) + Math.sin(Velocity) * (x-500) + Math.cos(Velocity) * (y-((1000*0.5555)/2)));
System.out.println(x);
System.out.println(y);
}
public void draw(Graphics g){
g.setColor(Color.blue);
g.fillOval(x,y,width,height);
}
Use a rotation instance of an AffineTransform. See getRotateInstance(theta,anchorx,anchory) for details.
Returns a transform that rotates coordinates around an anchor point. This operation is equivalent to translating the coordinates so that the anchor point is at the origin (S1), then rotating them about the new origin (S2), and finally translating so that the intermediate origin is restored to the coordinates of the original anchor point (S3).
How do you rotate a circle about a point in JPanel?
Here's how I rotate a circle about a point in a JPanel.
I don't know how to make an animated GIF. Just imagine the blue circle rotating clockwise around the center of the drawing JPanel.
So, let's start at the beginning. Basically, I have a circle rotating on the circumference of another circle. So, I create a Circle model class from plain Java.
public class Circle {
private final int radius;
private final Color color;
private Point center;
public Circle(int radius, Color color) {
this.radius = radius;
this.color = color;
}
public Point calculateCircumferencePoint(int theta) {
double radians = Math.toRadians(theta);
int x = center.x + (int) Math.round(Math.cos(radians) * radius);
int y = center.y + (int) Math.round(Math.sin(radians) * radius);
return new Point(x, y);
}
public void setCenter(int x, int y) {
this.center = new Point(x, y);
}
public void setCenter(Point center) {
this.center = center;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
The class consists of basic getters and setters. I make the radius and color final because they don't change value in this Java application.
The calculateCircumferencePoint method is the only interesting method. It takes an int angle in degrees, and calculates the point on the circumference represented by that angle, rounded to the nearest X and Y integer points.
Next, we create two Circle instances, an inner circle and an outer circle. Here's the class constructor setting the preferred size of the drawing area, the inner circle, and the outer circle. We start the outer circle at zero degrees (to the right);
private Circle innerCircle;
private Circle outerCircle;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
Now, we can start coding the GUI. First, we start the Java application by calling the SwingUtilities invokeLater method. This method ensures that we create and execute the Swing components on the Event Dispatch Thread.
Next, we define the JFrame. Here's the code we have so far.
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateCircle());
}
private Animation animation;
private Circle innerCircle;
private Circle outerCircle;
private DrawingPanel drawingPanel;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
#Override
public void run() {
JFrame frame = new JFrame("Rotate Circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingPanelSize,
outerCircle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(0);
new Thread(animation).start();
}
The JFrame methods must be called in a certain order. This is the order I use for most of my SWwing applications.
I pack the JFrame. I don't set a JFrame size. I let the Swing layout managers set the size of my JFrame. The default layout of a JFrame content pane is a BorderLayout. I put my drawing JPanel in the center of the BorderLayout.
Next, I create the drawing JPanel.
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
public DrawingPanel(Dimension size, Circle circle) {
this.circle = circle;
this.setBackground(Color.WHITE);
this.setPreferredSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Point center = circle.getCenter();
int radius = circle.getRadius();
int diameter = radius + radius;
g2d.setColor(circle.getColor());
g2d.fillOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
All the drawing JPanel does is paint a Circle object. Pretty straightforward.
The fillOval method paints an oval from the upper left hand corner. We calculate the upper left hand point from the center point.
The responsibility for calculating and updating the outer circle center point falls to my controller class, the Animation class. I use a simple loop to update the theta angle, calculate the new outer circle center point, paint the outer circle, and wait a period of time.
Here's that code.
public class Animation implements Runnable {
private int theta;
public Animation(int theta) {
this.theta = theta;
}
#Override
public void run() {
while (true) {
theta++;
theta = (theta >= 360) ? 0 : theta;
Point center = innerCircle.calculateCircumferencePoint(theta);
outerCircle.setCenter(center);
repaint();
sleep(30L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
The Animation repaint method makes the call to the drawing JPanel repaint method inside another SwingUtilities invokeLater method. This method ensures that the drawing happens on the Event Dispatch Thread.
Finally, here's the complete, runnable, example. I used inner classes so I could post the code as one block, and you can copy and run this code as one block. Generally, classes should be in separate files and for a more complex GUI, separate packages.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class RotateCircle implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new RotateCircle());
}
private Animation animation;
private Circle innerCircle;
private Circle outerCircle;
private DrawingPanel drawingPanel;
private Dimension drawingPanelSize;
public RotateCircle() {
this.drawingPanelSize = new Dimension(400, 400);
int innerCircleRadius = drawingPanelSize.width / 4;
int centerX = drawingPanelSize.width / 2;
int centerY = drawingPanelSize.height / 2;
int outerCircleRadius = drawingPanelSize.width / 10;
this.innerCircle = new Circle(innerCircleRadius, null);
this.innerCircle.setCenter(centerX, centerY);
this.outerCircle = new Circle(outerCircleRadius, Color.BLUE);
Point point = innerCircle.calculateCircumferencePoint(0);
this.outerCircle.setCenter(point);
}
#Override
public void run() {
JFrame frame = new JFrame("Rotate Circle");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingPanelSize,
outerCircle);
frame.add(drawingPanel, BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
animation = new Animation(0);
new Thread(animation).start();
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
private Circle circle;
public DrawingPanel(Dimension size, Circle circle) {
this.circle = circle;
this.setBackground(Color.WHITE);
this.setPreferredSize(size);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Point center = circle.getCenter();
int radius = circle.getRadius();
int diameter = radius + radius;
g2d.setColor(circle.getColor());
g2d.fillOval(center.x - radius, center.y - radius,
diameter, diameter);
}
}
public class Animation implements Runnable {
private int theta;
public Animation(int theta) {
this.theta = theta;
}
#Override
public void run() {
while (true) {
theta++;
theta = (theta >= 360) ? 0 : theta;
Point center = innerCircle.calculateCircumferencePoint(theta);
outerCircle.setCenter(center);
repaint();
sleep(30L);
}
}
private void repaint() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
drawingPanel.repaint();
}
});
}
private void sleep(long duration) {
try {
Thread.sleep(duration);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Circle {
private final int radius;
private final Color color;
private Point center;
public Circle(int radius, Color color) {
this.radius = radius;
this.color = color;
}
public Point calculateCircumferencePoint(int theta) {
double radians = Math.toRadians(theta);
int x = center.x + (int) Math.round(Math.cos(radians) * radius);
int y = center.y + (int) Math.round(Math.sin(radians) * radius);
return new Point(x, y);
}
public void setCenter(int x, int y) {
this.center = new Point(x, y);
}
public void setCenter(Point center) {
this.center = center;
}
public int getRadius() {
return radius;
}
public Color getColor() {
return color;
}
public Point getCenter() {
return center;
}
}
}

Why is java copying instead of creating a new Instant When i use different variables?

I made a fairly simple code and i got into an error which confused me.
So I have a class that creates two totally different variables and creating them using the new keyword
Player playerLeft = new Player(5,150);
Player playerRight = new Player( 150,150);
Player class:
import javax.swing.*;
import java.awt.*;
public class Player extends JComponent {
private int posY;
private int posX;
public Player(int x, int y) {
posX = x;
posY = y;
//repaint();
}
public float getMovementY() {
return movementY;
}
public void setMovementY(int movementY) {
this.movementY = movementY;
}
int movementY = 0;
public void paintComponent(Graphics g) {
Graphics2D _g2 = (Graphics2D) g;
Rectangle rect = new Rectangle(posX, posY, 20, 150);
_g2.fill(rect);
}
public void setLocation(int x, int y) {
posY = y;
posX = x;
repaint();
}
public void move() {
setLocation(posX, posY + movementY);
}
}
It's probably me not knowing something about Java but for me when I try to instantiate playerRight it just overwrites player left and drawsOut playerRight only.
Here is the complete code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Timer;
import java.util.TimerTask;
public class mainJFrame extends JFrame implements KeyListener {
int relativeTimeMillsec = 0;
Player playerLeft = new Player(5, 150);
Player playerRight = new Player(150, 150);
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
relativeTimeMillsec++;
refreshTimeText(relativeTimeMillsec);
calcMovements();
}
};
//components
JLabel timeCounterLabel = new JLabel("Time: " + 0, SwingConstants.CENTER);
public mainJFrame() {
createComponents();
addKeyListener(this);
}
public void createComponents() {
this.setTitle("The title");
this.setSize(800, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
timer.scheduleAtFixedRate(task, 0, 10);
JButton testButton = new JButton("Label");
testButton.setSize(100, 25);
testButton.setLocation(this.getWidth() / 2 - testButton.getWidth() / 2, this.getHeight() / 2 - testButton.getHeight() / 2);
timeCounterLabel.setSize(200, 25);
timeCounterLabel.setLocation(this.getWidth() / 2 - timeCounterLabel.getWidth() / 2, 10);
//playerRight = new Player(this.getWidth()-45,this.getHeight()/2);
// this.add(testButton);
this.add(timeCounterLabel);
this.add(playerLeft);
this.add(playerRight);
}
public void paintComponent(Graphics g) {
{
super.repaint();
}
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_S) {
playerLeft.movementY = +2;
} else if (e.getKeyCode() == KeyEvent.VK_W) {
playerLeft.movementY = -2;
}
if (e.getKeyCode() == KeyEvent.VK_UP) {
playerRight.movementY = +2;
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
playerRight.movementY = -2;
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
private double calcRealRelativeTime(int _relTime) {
return relativeTimeMillsec / (double) 100;
}
private void refreshTimeText(int _relTime) {
timeCounterLabel.setText("Time: " + Math.round(calcRealRelativeTime(_relTime)));
}
private void calcMovements() {
playerLeft.move();
playerRight.move();
}
}
Understand that a JFrame's contentPane (the container that holds its components) uses BorderLayout by default, and this code:
this.add(timeCounterLabel);
this.add(playerLeft);
this.add(playerRight);
is adding all components to the same default BorderLayout.CENTER position, meaning any components added will replace components added previously.
But more importantly, yours is a common problem and stems from your having your Player class extend from a GUI component. Don't do this, as then you will have a great deal of difficulty drawing multiple Player objects and having them interact easily (as you're finding out). Instead have Player be a logical (non-component) class, and have only one class extend JPanel and do all the drawing. This class can hold Player objects, perhaps held in a collection such as an ArrayList<Player>, and then iterate through the collection within its paintComponent method.
Other issues:
Do not use java.util.Timer and java.util.TimerTask for Swing animations since these classes do not follow Swing threading rules. Use instead a javax.swing.Timer.
Learn and use Java naming conventions. Variable names should all begin with a lower letter while class names with an upper case letter. Learning this and following this will allow us to better understand your code, and would allow you to better understand the code of others
If/when you do override a painting method such as paintComponent, be sure to call the super's method within your override, usually on the first line, so as not to break the painting chain. Also, use the #Override annotation before this method and any other methods that you think that you may be overriding so that the compiler catches possible errors with this.
For example (but not a complete example)
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
#SuppressWarnings("serial")
public class SimpleAnimation extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 600;
private static final int TIMER_DELAY = 20;
private Player2 playerLeft = new Player2(5, 150, Color.RED);
private Player2 playerRight = new Player2(150, 150, Color.BLUE);
public SimpleAnimation() {
playerLeft.setySpeed(1);
playerRight.setySpeed(-1);
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
playerLeft.draw(g);
playerRight.draw(g);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
playerRight.move();
playerLeft.move();
repaint();
}
}
private static void createAndShowGui() {
SimpleAnimation mainPanel = new SimpleAnimation();
JFrame frame = new JFrame("SimpleAnimation");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class Player2 {
private static final int RECT_WIDTH = 20;
private static final int RECT_HEIGHT = 50;
private int x;
private int y;
private int ySpeed;
private Color color;
public Player2(int x, int y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setySpeed(int ySpeed) {
this.ySpeed = ySpeed;
}
public int getySpeed() {
return ySpeed;
}
public void setLocation(int x, int y) {
setX(x);
setY(y);
}
public void move() {
setLocation(x, y + ySpeed);
}
public void draw(Graphics g) {
g.setColor(color);
g.fillRect(x, y, RECT_WIDTH, RECT_HEIGHT);
}
}

How to get rid of circle inside rectangle when switching between objects [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I have a combobox in which I can choose to draw either a rectangle, a circle or by freehand.
If I choose to draw a circle it draws it perfectly. If I then switch to draw a rectangle it draws a circle inside the rectangle. The same happens if I first choose to draw a rectangle and then a circle. (See Picture below)
My questions are:
How can I switch between drawing a circle and a rectangle without the circle appearing inside the rectangle?
How can I get the rectangle/circle to show while I'm dragging the mouse. What I mean is how can the lines show until I release the mouse click?
Why doesn't it work drawing by free hand?
This is my testclass:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class Lab6 extends JFrame implements ActionListener {
int startX, startY, endX, endY, w, h;
ArrayList<Shape> shapeList = new ArrayList<Shape>();
Container cp = getContentPane();
private JPanel topPanel;
private JComboBox comboBox;
private final String[] boxOptions = new String[] {"Rektangel", "Cirkel", "Frihand"};
public Lab6(String title) {
super(title);
this.setLayout(new BorderLayout());
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.setSize(840, 500);
this.initComponents();
this.setVisible(true);
}
private void initComponents() {
topPanel = new JPanel(new GridLayout(1,2));
topPanel.setPreferredSize(new Dimension(0,40));
comboBox = new JComboBox(boxOptions);
comboBox.setSelectedIndex(0);
comboBox.addActionListener(this);
topPanel.add(comboBox);
this.add(topPanel, BorderLayout.PAGE_START);
}
#Override
public void paint(Graphics g) {
for (Shape s : shapeList) {
s.draw(g);
}
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource().equals(comboBox)) {
JComboBox cb = (JComboBox)e.getSource();
if (cb.getSelectedItem().equals("Rektangel")) {
cp.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
}
#Override
public void mouseReleased(MouseEvent e) {
endX = e.getX();
endY = e.getY();
int width = startX - endX;
int height = startY - endY;
w = Math.abs(width);
h = Math.abs(height);
Rectangle r = new Rectangle(startX, startY, w, h);
shapeList.add(r);
repaint();
}
});
}
else if (cb.getSelectedItem().equals("Cirkel")) {
cp.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
}
#Override
public void mouseReleased(MouseEvent e) {
endX = e.getX();
endY = e.getY();
int width = startX - endX;
int height = startY - endY;
w = Math.abs(width);
h = Math.abs(height);
Circle c = new Circle(startX, startY, w, h);
shapeList.add(c);
repaint();
}
});
}
else if (cb.getSelectedItem().equals("Frihand")) { //I need help with this part
cp.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
startX = e.getX();
startY = e.getY();
}
#Override
public void mouseDragged(MouseEvent e) {
FreeHand fh = new FreeHand(startX, startY, e.getX(), e.getY());
shapeList.add(fh);
repaint();
}
});
}
}
}
public static void main(String args[]) {
new Lab6("Drawing Program");
}
}
In class Rectangle (class Circle looks the same):
import java.awt.*;
public class Rectangle extends Shape {
public Rectangle(int x, int y, int width, int height) {
super(x, y, width, height);
}
public Rectangle() {
super();
}
#Override
public void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.RED);
g2.setStroke(new BasicStroke(4));
g.drawRect(getX(), getY(), getWidth(), getHeight());
}
}
In class FreeHand (I need help with this part):
import java.awt.*;
public class FreeHand extends Shape {
public FreeHand(int x, int y, int width, int height) {
super(x, y, width, height);
}
public FreeHand() {
super();
}
#Override
public void draw(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.setStroke(new BasicStroke(4));
g2.drawLine(getX(), getY(), getWidth(), getHeight());
}
}
In class shape:
import java.awt.Graphics;
import javax.swing.JPanel;
public abstract class Shape extends JPanel {
private int startX, startY, width, height;
public Shape() {
this(0, 0, 1, 1);
}
public Shape(int startX, int startY, int width, int height) {
this.startX = startX;
this.startY = startY;
this.width = width;
this.height = height;
}
public abstract void draw(Graphics g);
#Override
public int getX() {
return startX;
}
#Override
public int getY() {
return startY;
}
#Override
public int getWidth() {
return width;
}
#Override
public int getHeight() {
return height;
}
}
There are a multitude of things going on...
Overriding paint of JFrame
Not calling super.paint before performing custom painting.
Adding a new MosueListener EVERY time you change the shape
Instead, create a custom component, extending from something like JPanel and override it's paintComponent method. Use this component has your basic drawing surface (your controls should contained in another component).
Make sure you call super.paintComponent before performing any custom painting so you don't break the paint chain
See Performing Custom Painting and Painting in AWT and Swing for more details
Create a SINGLE MouseListener and register it to the panel. When the use selects a different shape, change a state variable within the panel (via a setter) which tells the MouseListener what it should do when the user starts drawing.
Updated...
Create a custom class that extends from JPanel...
public static class ShapePane extends JPanel {
}
Override the classes paintComponent method...
public static class ShapePane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Custom Painting here...
}
}
Provide some sizing hints for the layout manager...
public static class ShapePane extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Custom Painting here...
}
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
Provide a means by which the type of shape can be changed...so you know what to paint...
public static class ShapePane extends JPanel {
public enum ShapeType {
CIRCLE,
RECTANGLE
}
private ShapeType currentShapeType;
public void setCurrentShapeType(ShapeType currentShapeType) {
this.currentShapeType = currentShapeType;
}
public ShapeType getCurrentShapeType() {
return currentShapeType;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Custom Painting here...
}
}
Add a SINGLE MouseListener to the custom class to create the required type of shapes...
public static class ShapePane extends JPanel {
public enum ShapeType {
CIRCLE,
RECTANGLE
}
private ShapeType currentShapeType;
public ShapePane() {
addMouseListener(new MouseAdapter() {
private Point clickPoint;
#Override
public void mousePressed(MouseEvent e) {
clickPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
Point releasePoint = e.getPoint();
int x = Math.min(releasePoint.x, clickPoint.x);
int y = Math.min(releasePoint.y, clickPoint.y);
int width = Math.abs(clickPoint.x - releasePoint.x);
int height = Math.abs(clickPoint.y - releasePoint.y);
switch (getCurrentShapeType()) {
case CIRCLE:
// Make a circle
break;
case RECTANGLE:
// Make a rectangle...
break;
}
repaint();
}
});
}
public void setCurrentShapeType(ShapeType currentShapeType) {
this.currentShapeType = currentShapeType;
}
public ShapeType getCurrentShapeType() {
return currentShapeType;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Custom Painting here...
}
}
Fill in the blanks...
Create another JPanel (you can simply create an instance this time), add your controls to it
Create an instance of a JFrame, add the custom class to it and the controls panel (make sure they are laid out correctly so they don't override each other - see Laying Out Components Within a Container for more details)
Use appropriate listeners to the controls to determine the type of shape the user wants to draw and set the currentShapeType property accordingly...

Circle collision detection

I have created a program that drawing 2 circle on the screen and using keyboard ASWD and arrow key to move around..here is the code
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class BallObject {
private int x;
private int y;
private int radius;
BallObject() {
x=0;
y=0;
radius=0;
}
BallObject (int x,int y,int radius) {
this.x=x;
this.y=y;
this.radius=radius;
}
public void setX(int x) {this.x=x;}
public void setY(int y) {this.y=y;}
public void setRadius(int r) {radius=r;}
public int getX() {return x;}
public int getY() {return y;}
public int getRadius() {return radius;}
}
class Ball extends JFrame implements KeyListener {
BallObject ball1;
BallObject ball2;
Ball() {
super("Simple Ball");
setSize(800,600); //set screen resolution
ball1 = new BallObject(getWidth()/2,getHeight()/2,20);
ball2 = new BallObject(40,40,20);
addKeyListener(this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLACK);
g2d.fill(new Rectangle(0,0,800,600));
//drawing ball1
g2d.setColor(Color.RED);
g2d.fillOval(ball1.getX(),ball1.getY(),ball1.getRadius()*2,ball1.getRadius()*2);
//drawing ball2
g2d.setColor(Color.GREEN);
g2d.fillOval(ball2.getX(),ball2.getY(),ball2.getRadius()*2,ball2.getRadius()*2);
}
public static void main (String args[]) {
new Ball();
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==KeyEvent.VK_LEFT)
ball1.setX(ball1.getX()-2);
if(e.getKeyCode()==KeyEvent.VK_RIGHT)
ball1.setX(ball1.getX()+2);
if(e.getKeyCode()==KeyEvent.VK_UP)
ball1.setY(ball1.getY()-2);
if(e.getKeyCode()==KeyEvent.VK_DOWN)
ball1.setY(ball1.getY()+2);
if(e.getKeyCode()==KeyEvent.VK_A){
ball2.setX(ball2.getX()-2);
//System.out.println("Hello");
}
if(e.getKeyCode()==KeyEvent.VK_D)
ball2.setX(ball2.getX()+2);
if(e.getKeyCode()==KeyEvent.VK_W)
ball2.setY(ball2.getY()-2);
if(e.getKeyCode()==KeyEvent.VK_S)
ball2.setY(ball2.getY()+2);
repaint();
}
//redraw the screen to show the updated ball location
public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
}
Now i need to test the collision..Once two balls are touching each other.it will show a message "COLLISION DETECTED"..pls help...
Isn't it simply: if the distance of both center points is less or equal to the sum of the radiuses, then there is a collision?

Categories