Related
As a project, I'm attempting to create an emulation of the game Asteroids. Currently, I'm trying to make it so then the spaceship appears on the opposite side of the GUI if the player ever sends himself out of bounds.
However, I've become quite confused between how AffineTransformation's rotate and createTransformedShape function interacts with the x and y points of the polygon.
Currently, I have the spaceship working; it flies in the angle the user specifies it and all the movement involved works fine. However, when I tried to get the lowest X coordinate of the spaceship to compare if it ever goes bigger than the constant WIDTH, it returns 780. This happens all the time whether or not I am on one side of the map to the other, it always returns 780. This I find really strange because shouldn't it return the smallest X coordinate of where it currently is?
Here is a screenshot of the console displaying that the "smallest x coordinate" of the polygon is 780, despite not being at 780
Can someone explain to me why the X coordinates of the polygon are not changing? I have 2 classes. One, which is the driver classes, and the other which is the ship class which extends Polygon.
public class AsteroidGame implements ActionListener, KeyListener{
public static AsteroidGame game;
public Renderer renderer;
public boolean keyDown = false;
public int playerAngle = 0;
public boolean left = false;
public boolean right = false;
public boolean go = false;
public boolean back = false;
public boolean still = true;
public double angle = 0;
public int turnRight = 5;
public int turnLeft = -5;
public Shape transformed;
public Shape transformedLine;
public Point p1;
public Point p2;
public Point center;
public Point p4;
public final int WIDTH = 1600;
public final int HEIGHT = 800;
public Ship ship;
public AffineTransform transform = new AffineTransform();
public AsteroidGame(){
JFrame jframe = new JFrame();
Timer timer = new Timer(20, this);
renderer = new Renderer();
jframe.add(renderer);
jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jframe.setSize(WIDTH, HEIGHT);
jframe.setVisible(true);
jframe.addKeyListener(this);
jframe.setResizable(false);
int xPoints[] = {800, 780, 800, 820};
int yPoints[] = {400, 460, 440, 460};
p1 = new Point(400,400);
p2 = new Point(380, 460);
center = new Point(400,440);//center
p4 = new Point(420, 460);
ship = new Ship(xPoints, yPoints, 4, 0);
transformed = transform.createTransformedShape(ship);
timer.start();
}
public void repaint(Graphics g){
g.setColor(Color.BLACK);
g.fillRect(0, 0, WIDTH, HEIGHT);
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.WHITE);
g2d.draw(transformed);
/*
g2d.draw(r2);
Path2D.Double path = new Path2D.Double();
path.append(r, false);
AffineTransform t = new AffineTransform();
t.rotate(Math.toRadians(45));
path.transform(t);
g2d.draw(path);
Rectangle test = new Rectangle(WIDTH/2, HEIGHT/2, 200, 100);
Rectangle test2 = new Rectangle(WIDTH/2, HEIGHT/2, 200, 100);
g2d.draw(test2);
AffineTransform at = AffineTransform.getTranslateInstance(100, 100);
g2d.rotate(Math.toRadians(45));
g2d.draw(test);
*/
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
if (right){
ship.right();
transform.rotate(Math.toRadians(turnRight), ship.getCenterX(), ship.getCenterY());
System.out.println(ship.getCenterY());
}
else if (left){
ship.left();
transform.rotate(Math.toRadians(turnLeft), ship.getCenterX(), ship.getCenterY());
}
if (go){
ship.go();
//ship.x += Math.sin(Math.toRadians(angle)) * 5;
//ship.y
/*
ship.x += (int) Math.sin(Math.toRadians(angle));
ship.y += (int) Math.cos(Math.toRadians(angle));
*/
//System.out.println(Math.sin(Math.toRadians(ship.angle)) * 5 + "y" + Math.cos(Math.toRadians(ship.angle)) * 5);
}
else if (back){
ship.reverse();
}
ship.move();
//ship.decrement();
transformed = transform.createTransformedShape(ship);
if (ship.smallestX() >= WIDTH){
System.out.println("out");
}
renderer.repaint();
System.out.println("Smallest x coordinate: " + ship.smallestX());
}
public static void main(String[] args){
game = new AsteroidGame();
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
System.out.println("I am down");
right = true;
keyDown = true;
}else if (e.getKeyCode() == KeyEvent.VK_LEFT){
left = true;
System.out.println("I am down");
keyDown = true;
}
if (e.getKeyCode() == KeyEvent.VK_UP){
go = true;
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN){
back = true;
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
right = false;
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
left = false;
}
if (e.getKeyCode() == KeyEvent.VK_UP){
go = false;
}
if (e.getKeyCode() == KeyEvent.VK_DOWN){
back = false;
}
still = true;
keyDown = false;
System.out.println("up");
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
Ship Class:
public class Ship extends Polygon{
/**
*
*/
private double currSpeed = 0;
private static final long serialVersionUID = 1L;
public double angle;
public Ship(int[] x, int[] y, int points, double angle){
super(x, y, points);
this.angle= angle;
}
public void right(){
angle += 5;
}
public void left(){
angle -= 5;
}
public void move(){
for (int i = 0; i < super.ypoints.length; i++){
super.ypoints[i] -= currSpeed;
//System.out.println(super.ypoints[i]);
//System.out.println(super.xpoints[i]);
}
}
public void reverse(){
if (currSpeed > -15) currSpeed -= 0.2;
}
public void go(){
if (currSpeed < 25) currSpeed += 0.5;
}
public int smallestX(){
int min = super.xpoints[0];
for (int i = 0; i < super.xpoints.length; i++){
if (min > super.xpoints[i]){
min = super.xpoints[i];
}
}
return min;
}
public int smallestY(){
int min = super.ypoints[0];
for (int i = 0; i < super.ypoints.length; i++){
if (min < super.ypoints[i]){
min = super.ypoints[i];
}
}
return min;
}
public int getCenterX(){
return super.xpoints[2];
}
public int getCenterY(){
return super.ypoints[2];
}
public double getAng(){
return angle;
}
Why it doesn't work
The affine transformation doesn't actually affect the ship instance, instead it created an entirely new instance, this is why your method doesn't seem to work. To prove this, i have the following code:
int[] xPoints = {800, 780, 800, 820};
int[] yPoints = {400, 460, 440, 460};
Ship ship = new Ship(xPoints, yPoints, 4, 0);
System.out.println("old points:");
System.out.println(Arrays.toString(ship.xpoints));
System.out.println(Arrays.toString(ship.ypoints));
AffineTransform transform = new AffineTransform();
transform.translate(20, 20);
Shape transformed = transform.createTransformedShape(ship);
System.out.println("new points (unchanged):");
System.out.println(Arrays.toString(ship.xpoints));
System.out.println(Arrays.toString(ship.ypoints));
Both times, the points are the same.
Solution
What i suggest you do is instead of thinking of the point as actual points on the screen, think of them as a model, that will also make the physics part easier. We'll center the points around (0,0). And when we need to render it, tranform it to the correct position and rotation.
The points would then be something like this:
int[] xPoints = {0, -20, 0, 20};
int[] yPoints = {-40, 20, 0, 20};
So now, you don't need to edit all the points when you move the ship, only the rotation and the center. Note that you should translate first, and then rotate.
The fact that these point are around (0,0) is important, because this is the center of your rotation. This is probably why you seem to be able to move at the moment, your rotation is not centered around the center of the ship. And when you rotate and move up, you also move a bit to the right.
This solves our problem, because now you don't need to look at all the points anymore, you can just check the center of the ship. If the center goes out of bounds, move it to the other side.
Resulting in the folliwing simple code (i use 300 here because that's the width and height in my example, change it to your width and height in the actual code)
if(center_x > 300)
center_x = 0;
if(center_x < 0)
center_x = 300;
if(center_y > 300)
center_y = 0;
if(center_y < 0)
center_y = 300;
This will take the ship to the other side of the screen when needed. However, we still need to show part of the ship while the other part is on the other side. For that reason, we can just render the ship twice. I hope the following code speaks for itself.
Rectangle2D box = transformed.getBounds2D();
//wrap in x direction
if(box.getX() + box.getWidth() > 300){
AffineTransform transform2 = new AffineTransform();
transform2.translate(ship.center_x - 300, ship.center_y);
transform2.rotate(Math.toRadians(ship.angle));
Shape transformed2 = transform2.createTransformedShape(ship);
g2.draw(transformed2);
}else if(box.getX() < 0){
AffineTransform transform2 = new AffineTransform();
transform2.translate(ship.center_x + 300, ship.center_y);
transform2.rotate(Math.toRadians(ship.angle));
Shape transformed2 = transform2.createTransformedShape(ship);
g2.draw(transformed2);
}
//wrap in y direction
if(box.getY() + box.getHeight() > 300){
AffineTransform transform2 = new AffineTransform();
transform2.translate(ship.center_x, ship.center_y - 300);
transform2.rotate(Math.toRadians(ship.angle));
Shape transformed2 = transform2.createTransformedShape(ship);
g2.draw(transformed2);
}else if(box.getY() < 0){
AffineTransform transform2 = new AffineTransform();
transform2.translate(ship.center_x, ship.center_y + 300);
transform2.rotate(Math.toRadians(ship.angle));
Shape transformed2 = transform2.createTransformedShape(ship);
g2.draw(transformed2);
}
Physics
You also requested more info on the actual physics, i'll just give you the formulas (let me know if you need more help). Here, theta is the angle of the ship in radians, where if theta is 0, the ship points up. The x-axis points to the right, and the y-axis points up.
a_x = Math.sin(theta)*a;
a_y = -Math.cos(theta)*a;
v_x_new = v_x_old + a_x*timeDiff;
v_y_new = v_y_old + a_y*timeDiff;
x_new = x_old + v_x_old*timeDiff + a_x*timeDiff*timeDiff/2;
y_new = y_old + v_y_old*timeDiff + a_y*timeDiff*timeDiff/2;
x_old = x_new;
y_old = y_new;
v_x_old = v_x_new;
v_y_old = v_y_new;
Final result
I've implemented how the ship class should look, and also tested if the rendering method works.
public class AsteroidsTest {
public static void main(String[] args){
int[] xPoints = {0, -20, 0, 20};
int[] yPoints = {-40, 20, 0, 20};
Ship ship = new Ship(xPoints, yPoints, 4, 0);
ship.center_x = 100;
ship.center_y = 100;
ship.angle = 45;
ship.speed_x = 30;
ship.speed_y = -30;
System.out.println("running...");
JFrame window = new JFrame();
window.setBounds(30, 30, 300, 300);
window.getContentPane().add(new MyCanvas(ship));
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
long time1 = System.currentTimeMillis();
while(true){
long time2 = System.currentTimeMillis();
double timeDiff = (time2-time1)/1000f;
time1 = time2;
ship.move(timeDiff);
window.getContentPane().repaint();
}
}
}
class MyCanvas extends JComponent {
private Ship ship;
public MyCanvas(Ship ship){
this.ship = ship;
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
AffineTransform transform = new AffineTransform();
transform.translate(ship.center_x, ship.center_y);
transform.rotate(Math.toRadians(ship.angle));
Shape transformed = transform.createTransformedShape(ship);
g2.draw(transformed);
Rectangle2D box = transformed.getBounds2D();
//wrap in x direction
if(box.getX() + box.getWidth() > 300){
AffineTransform transform2 = new AffineTransform();
transform2.translate(ship.center_x - 300, ship.center_y);
transform2.rotate(Math.toRadians(ship.angle));
Shape transformed2 = transform2.createTransformedShape(ship);
g2.draw(transformed2);
}else if(box.getX() < 0){
AffineTransform transform2 = new AffineTransform();
transform2.translate(ship.center_x + 300, ship.center_y);
transform2.rotate(Math.toRadians(ship.angle));
Shape transformed2 = transform2.createTransformedShape(ship);
g2.draw(transformed2);
}
//wrap in y direction
if(box.getY() + box.getHeight() > 300){
AffineTransform transform2 = new AffineTransform();
transform2.translate(ship.center_x, ship.center_y - 300);
transform2.rotate(Math.toRadians(ship.angle));
Shape transformed2 = transform2.createTransformedShape(ship);
g2.draw(transformed2);
}else if(box.getY() < 0){
AffineTransform transform2 = new AffineTransform();
transform2.translate(ship.center_x, ship.center_y + 300);
transform2.rotate(Math.toRadians(ship.angle));
Shape transformed2 = transform2.createTransformedShape(ship);
g2.draw(transformed2);
}
}
}
Ship class:
import java.awt.Polygon;
public class Ship extends Polygon{
private static final long serialVersionUID = 1L;
public double center_x = 0;
public double center_y = 0;
public double speed_x = 0;
public double speed_y = 0;
//the angle of the ship
public double angle = 0;
//the acceleration of the ship in the direction defined by angle
public double acceleration = 0;
public Ship(int[] x, int[] y, int points, double angle){
super(x, y, points);
this.angle= angle;
}
public void right(){
angle += 5;
}
public void left(){
angle -= 5;
}
public void move(double timeDiff){
double a_x = Math.sin(Math.toRadians(angle))*acceleration;
double a_y = -Math.cos(Math.toRadians(angle))*acceleration;
center_x = center_x + speed_x*timeDiff + a_x*timeDiff*timeDiff/2;
center_y = center_y + speed_y*timeDiff + a_y*timeDiff*timeDiff/2;
speed_x = speed_x + a_x*timeDiff;
speed_y = speed_y + a_y*timeDiff;
if(center_x > 300)
center_x = 0;
if(center_x < 0)
center_x = 300;
if(center_y > 300)
center_y = 0;
if(center_y < 0)
center_y = 300;
}
public void reverse(){
acceleration = -1;
}
public void go(){
acceleration = 1;
}
public void stop(){
acceleration = 0;
}
public int getCenterX(){
return (int) Math.round(center_x);
}
public int getCenterY(){
return (int) Math.round(center_y);
}
public double getAng(){
return angle;
}
}
so i have a little Problem and i try to solve it since hours.
I have a BufferedImage and i want to change the colour in a fluent way f.e. from red to white.
My main:
public static void main(String[] args) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x != width; x++) {
for (int y = 0; y != height; y++) {
image.setRGB(x, y, color12(x, y));
try {
,,,
My method to change the color:
static int color12(int x, int y) {
int size = 100;
if (Math.abs(width / 2 - x) < size / 2 && Math.abs(height / 2 - y) < size / 2)
return new Color(255, 0, 0).getRGB();
else
return new Color(200, 200, 255).getRGB();
}
}
So i played around with the method, but i cant get it done.
My best "solution" was this:
int r = 0 ;
int b = 0;
int g = 0;
for (int i = 1; i < 255; i++)
r++;
else return new Color(r,g,b).getRGB();
Can anyone help me?
I'm not sure how you want the gradient (e.g. horizontal, vertical or diagonal), but here's an example using linear interpolation for a horizontal or vertical gradient.
import java.awt.Color;
import java.awt.image.BufferedImage;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class ExampleFrame extends JFrame {
private static enum GradientOrientation {
HORIZONTAL, VERTICAL
};
private static BufferedImage createGradientImg(int width, int height, Color startColor, Color endColor, GradientOrientation o) {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int pos = o.equals(GradientOrientation.HORIZONTAL) ? x : y;
int size = o.equals(GradientOrientation.HORIZONTAL) ? width : height;
image.setRGB(x, y, getGradientRGB(startColor, endColor, pos, size));
}
}
return image;
}
private static int getGradientRGB(Color startColor, Color endColor, int pos, int size) {
double perc = (double) pos / size;
int newRed = (int) (endColor.getRed() * perc + startColor.getRed() * (1 - perc));
int newGreen = (int) (endColor.getGreen() * perc + startColor.getGreen() * (1 - perc));
int newBlue = (int) (endColor.getBlue() * perc + startColor.getBlue() * (1 - perc));
return new Color(newRed, newGreen, newBlue).getRGB();
}
public void createAndShow() {
BufferedImage img1 = createGradientImg(200, 100, Color.RED,
Color.WHITE, GradientOrientation.HORIZONTAL);
BufferedImage img2 = createGradientImg(200, 100, Color.BLUE,
Color.YELLOW, GradientOrientation.HORIZONTAL);
BufferedImage img3 = createGradientImg(200, 100, Color.WHITE,
Color.YELLOW, GradientOrientation.VERTICAL);
BufferedImage img4 = createGradientImg(200, 100, Color.BLACK,
Color.WHITE, GradientOrientation.VERTICAL);
BoxLayout layout = new BoxLayout(getContentPane(), BoxLayout.Y_AXIS);
getContentPane().setLayout(layout);
getContentPane().add(new JLabel(new ImageIcon(img1)));
getContentPane().add(new JLabel(new ImageIcon(img2)));
getContentPane().add(new JLabel(new ImageIcon(img3)));
getContentPane().add(new JLabel(new ImageIcon(img4)));
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ExampleFrame ef = new ExampleFrame();
ef.createAndShow();
}
});
}
}
I'm working on a java programming exercise where we have to draw a circular spiral using the drawArc method so that the result looks similar to this:
I've been working on this for a while and this is what I have so far:
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class CircSpiral extends JPanel {
public void paintComponent(Graphics g) {
int x = 100;
int y = 120;
int width = 40;
int height = 60;
int startAngle = 20;
int arcAngle = 80;
for (int i = 0; i < 5; i++) {
g.drawArc(x, y, width, height, startAngle, arcAngle);
g.drawArc(x + 10, y + 10, width, height, startAngle + 10, arcAngle);
x = x + 5;
y = y + 5;
startAngle = startAngle - 10;
arcAngle = arcAngle + 10;
}
}
public static void main(String[] args) {
CircSpiral panel = new CircSpiral();
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.setSize(300, 300);
application.setVisible(true);
}
}
My code gives me this result:
I know the problem lies in my arguments for the drawArc method because the numbers aren't right, but I don't know how to go about making the numbers go in a circular manner. Any help is appreciated. Thank you!
Your idea is almost right. I did some modifications. You need to inverse the angle to draw the other side of the spiral and use fixed point to startAngle.
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class CircSpiral extends JPanel {
public void paintComponent(Graphics g) {
int x = getSize().width / 2 - 10;
int y = getSize().height/ 2 - 10;
int width = 20;
int height = 20;
int startAngle = 0;
int arcAngle = 180;
int depth = 10;
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
// g.drawArc(x + 10, y + 10, width, height, startAngle + 10, -arcAngle);
// x = x - 5;
y = y - depth;
width = width + 2 * depth;
height = height + 2 * depth;
g.drawArc(x, y, width, height, startAngle, -arcAngle);
} else {
// g.drawArc(x + 10, y + 10, width, height, startAngle + 10, arcAngle);
x = x - 2 * depth;
y = y - depth;
width = width + 2 * depth;
height = height + 2 * depth;
g.drawArc(x, y, width, height, startAngle, arcAngle);
}
}
}
public static void main(String[] args) {
CircSpiral panel = new CircSpiral();
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.setSize(300, 300);
application.setVisible(true);
}
}
If this were my project, yes I'd draw my arcs in a loop, but within the loop, I'd try to make the arc's bounding box bigger but still centered over the same location. To do this I'd decrement x and y by some small constant, say DELTA (which I'd set to == 1), and I'd increment width and height by 2 * DELTA. I'd also leave my arcAngle unchanged but rather would change my startAngle in the loop like so: startAngle = startAngle - arcAngle;.
For example, this:
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class CircSpiral extends JPanel {
private static final int DELTA = 1;
private static final int ARC_ANGLE = 20;
private static final int PREF_W = 300;
private static final int PREF_H = PREF_W;
private static final int LOOP_MAX = 400;
public void paintComponent(Graphics g) {
int x = PREF_W / 2;
int y = PREF_H / 2;
int width = 1;
int height = 1;
int startAngle = 0;
int arcAngle = ARC_ANGLE;
for (int i = 0; i < LOOP_MAX; i++) {
g.drawArc(x, y, width, height, startAngle, arcAngle);
x = x - DELTA;
y = y - DELTA;
width += 2 * DELTA;
height += 2 * DELTA;
startAngle = startAngle - arcAngle;
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
public static void main(String[] args) {
CircSpiral panel = new CircSpiral();
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.pack();
application.setLocationRelativeTo(null);
application.setVisible(true);
}
}
Would result in this:
The following code will output this image:
import java.awt.Graphics;
import javax.swing.JPanel;
import javax.swing.JFrame;
public class CircSpiral extends JPanel {
public void paintComponent(Graphics g) {
int centerX = getWidth() / 2;
int centerY = getHeight() / 2;
int numIterations = 5;
int arcWidth = 10;
int arcGrowDelta = 30;
for (int i = 0; i < numIterations; i++) {
g.drawArc(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth, 2 * arcWidth, 0, 180);
arcWidth += arcGrowDelta;
g.drawArc(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth - arcGrowDelta, 2 * arcWidth, 180, 180);
}
}
public static void main(String[] args) {
CircSpiral panel = new CircSpiral();
JFrame application = new JFrame();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
application.add(panel);
application.setSize(300, 300);
application.setVisible(true);
}
}
The idea is very simple, just draw the upper half of a circle, like this:
Then increment the arc size by a constant factor and draw the bottom half of the circle but making the end point of this circle and the upper circle match, for it, just substrate the arcGrowDelta from the bottom circle width:
And repeat.
This is my solution:
package mainpack;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JPanel;
public class SpiralPanel extends JPanel {
private static final long serialVersionUID = 1L;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
int width = 10;
int height = 10;
int startAngle = 0;
int arcAngle = 180;
int x = (getWidth() - width) / 2;
int y = (getHeight() - height) / 2;
int i = 0;
int t = 0;
while (i < 36) {
g2d.drawArc(x + t, y, width, height, startAngle, arcAngle);
if (i % 2 == 0) {
t -= 10;
}
y -= 5;
width += 10;
height += 10;
startAngle += 180;
i++;
}
}
}
I am a beginner to java and finally managed how to create the spiral.
Here is my code:
int lineLength = 20; //starting line length
int x = getWidth() / 2; //start drawing from center of JPanel
int y = getHeight() / 2; //start drawing from center of JPanel
for( int counter = 0; counter < 10; counter++ )
{
g.drawArc( x, y, lineLength, lineLength, 0, 180 ); //draws top semicircle of equal width and height
lineLength += 20; //increases arc diameter
x -= 20; //moves x coordinate left
g.drawArc( x, y - 10, lineLength, lineLength, 0, -180 ); //draws bottom semicircle; 'y - 10' joins the 2 semicircles
lineLength += 20; //increases arc diameter
y -= 20; //moves y coordinate up
}
If you're willing to let some good old trigonometry do the work, you could use this:
import java.awt. *;
import javax.swing. *;
import java.math.*;
public class Spiral extends JFrame {
public Spiral()
{
// Set Window
setTitle("Spirale");
setSize(1500, 1500);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
}
public void paint(Graphics g)
{
super.paint(g);
for(double i = 1; i < 50000; i++)
{
int locY = 600 - (int) (Math.cos((Math.PI*i)/1800)*i/50);
int locX = 600 - (int) (Math.sin((Math.PI*i)/1800)*i/50);
g.drawLine(locX, locY, locX, locY);
}
}
public static void main(String[] args) {
new Spiral();
}
}
I have to write a program that will generate 20 random circles with random radius lengths. If any of these circles intersect with another, the circle must be blue, and if it does not intersect, the color is red. I must also place a button on the JFrame. If this button is pressed, it needs to clear out the JFrame, and generate a new set of 20 circles following the same color rules. I am extremely new to Java Swing and am really stuck. I have everything working except the button. I cannot get a new set of circles to generate. Any help would be greatly appreciated. Thank You.
import java.awt.Graphics;
import javax.swing.JPanel;
import java.util.Random;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class IntersectingCircles extends JPanel
{
private int[] xAxis = new int [20]; // array to hold x axis points
private int[] yAxis = new int [20]; // array to hold y axis points
private int[] radius = new int [20]; // array to hold radius length
public static void main (String[] args)
{
JFrame frame = new JFrame("Random Circles");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add (new IntersectingCircles());
frame.pack();
frame.setVisible(true);
}
public IntersectingCircles()
{
setPreferredSize(new Dimension(1000, 800)); // set window size
Random random = new Random();
// Create coordinates for circles
for (int i = 0; i < 20; i++)
{
xAxis[i] = random.nextInt(700) + 100;
yAxis[i] = random.nextInt(500) + 100;
radius[i] = random.nextInt(75) + 10;
}
}
public void paintComponent(Graphics g)
{
// Add button to run again
JButton btnAgain = new JButton("Run Again");
btnAgain.setBounds(850, 10, 100, 30);
add(btnAgain);
btnAgain.addActionListener(new ButtonClickListener());
// Determine if circles intersect, create circles, color circles
for (int i = 0; i < 20; i++)
{
int color = 0;
for (int h = 0; h < 20; h++)
{
if(i != h)
{
double x1 = 0, x2 = 0, y1 = 0, y2 = 0, d = 0;
x1 = (xAxis[i] + radius[i]);
y1 = (yAxis[i] + radius[i]);
x2 = (xAxis[h] + radius[h]);
y2 = (yAxis[h] + radius[h]);
d = (Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1)*(y2 - y1))));
if (d > radius[i] + radius[h] || d < (Math.abs(radius[i] - radius[h])))
{
color = 0;
}
else
{
color = 1;
break;
}
}
}
if (color == 0)
{
g.setColor(Color.RED);
g.drawOval(xAxis[i], yAxis[i], radius[i] * 2, radius[i] * 2);
}
else
{
g.setColor(Color.BLUE);
g.drawOval(xAxis[i], yAxis[i], radius[i] * 2, radius[i] * 2);
}
}
}
private class ButtonClickListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String action = e.getActionCommand();
if(action.equals("Run Again"))
{
new IntersectingCircles();
}
}
}
}
Suggestions:
Give your class a method that creates the random circles and calls repaint()
This method should create the circles and add them into an ArrayList.
Consider using Ellipse2D to represent your circles, and so you'd have an ArrayList<Ellipse2D>.
Call this method in your class constructor.
Call it again in the button's ActionListener.
Never add button's or change the state of your class from within your paintComponent method. This method is for drawing the circles and drawing them only and nothing more. Your way you will be creating the button each time the paintComponent method is called, so you could be potentially needlessly creating many JButtons
and needlessly slowing down your time-critical painting method.
Instead add the button in the constructor.
Be sure to call super.paintComponent(g) in your paintComponent method as its first call. This will clear the old circles when need be.
Also in paintComponent, iterate through the ArrayList of circles, drawing each one.
After some searching on this website, i found what i needed. Everything seems to work now. Thanks.
import java.awt.Graphics;
import javax.swing.JPanel;
import java.util.Random;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class IntersectingCircles extends JPanel
{
private int[] xAxis = new int [20]; // array to hold x axis points
private int[] yAxis = new int [20]; // array to hold y axis points
private int[] radius = new int [20]; // array to hold radius length
public static void main (String[] args)
{
JFrame frame = new JFrame("Random Circles");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(1000, 800));
ActionListener runAgain = new ActionListener()
{
#Override
public void actionPerformed(ActionEvent c)
{
frame.getContentPane().add(new IntersectingCircles());
frame.pack();
}
};
JButton btnAgain = new JButton("Run Again");
btnAgain.setBounds(850, 10, 100, 30);
btnAgain.addActionListener(runAgain);
frame.add(btnAgain);
frame.getContentPane().add (new IntersectingCircles());
frame.pack();
frame.setVisible(true);
}
public IntersectingCircles()
{
Random random = new Random();
// Create coordinates for circles
for (int i = 0; i < 20; i++)
{
xAxis[i] = random.nextInt(700) + 100;
yAxis[i] = random.nextInt(500) + 100;
radius[i] = random.nextInt(75) + 10;
}
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
// Determine if circles intersect, create circles, color circles
for (int i = 0; i < 20; i++)
{
int color = 0;
for (int h = 0; h < 20; h++)
{
if(i != h)
{
double x1 = 0, x2 = 0, y1 = 0, y2 = 0, d = 0;
x1 = (xAxis[i] + radius[i]);
y1 = (yAxis[i] + radius[i]);
x2 = (xAxis[h] + radius[h]);
y2 = (yAxis[h] + radius[h]);
d = (Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1)*(y2 - y1))));
if (d > radius[i] + radius[h] || d < (Math.abs(radius[i] - radius[h])))
{
color = 0;
}
else
{
color = 1;
break;
}
}
}
if (color == 0)
{
g.setColor(Color.RED);
g.drawOval(xAxis[i], yAxis[i], radius[i] * 2, radius[i] * 2);
}
else
{
g.setColor(Color.BLUE);
g.drawOval(xAxis[i], yAxis[i], radius[i] * 2, radius[i] * 2);
}
}
}
}
private class ButtonClickListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
Maybe you can have a try...
I want to change the height of each bar (for example 10 for red part and 20 for blue part). But when I increase the height value it will increase the chart from bottom whereas I want the change to top! Do you know what is wrong with it?
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ChartPanel extends JPanel {
private double[] values;
private String[] names;
private String title;
public ChartPanel(double[] v, String[] n, String t) {
names = n;
values = v;
title = t;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (values == null || values.length == 0)
return;
double minValue = 0;
double maxValue = 0;
for (int i = 0; i < values.length; i++) {
if (minValue > values[i])
minValue = values[i];
if (maxValue < values[i])
maxValue = values[i];
}
Dimension d = getSize();
int clientWidth = d.width;
int clientHeight = d.height;
int barWidth = clientWidth / values.length;
Font titleFont = new Font("SansSerif", Font.BOLD, 20);
FontMetrics titleFontMetrics = g.getFontMetrics(titleFont);
Font labelFont = new Font("SansSerif", Font.PLAIN, 10);
FontMetrics labelFontMetrics = g.getFontMetrics(labelFont);
int titleWidth = titleFontMetrics.stringWidth(title);
int y = titleFontMetrics.getAscent();
int x = (clientWidth - titleWidth) / 2;
g.setFont(titleFont);
g.drawString(title, x, y);
int top = titleFontMetrics.getHeight();
int bottom = labelFontMetrics.getHeight();
if (maxValue == minValue)
return;
double scale = (clientHeight - top - bottom) / (maxValue - minValue);
y = clientHeight - labelFontMetrics.getDescent();
g.setFont(labelFont);
for (int i = 0; i < values.length; i++) {
int valueX = i * barWidth + 1;
int valueY = top;
int height = (int) (values[i] * scale);
if (values[i] >= 0)
valueY += (int) ((maxValue - values[i]) * scale);
else {
valueY += (int) (maxValue * scale);
height = -height;
}
g.setColor(Color.red);
g.fillRect(valueX, valueY, barWidth - 80, height);
g.setColor(Color.black);
g.drawRect(valueX, valueY, barWidth - 80, height);
g.setColor(Color.blue);
g.fillRect(valueX, valueY + 20, barWidth - 80, height-20 );
g.setColor(Color.black);
g.drawRect(valueX, valueY + 20, barWidth - 80, height-20 );
int labelWidth = labelFontMetrics.stringWidth(names[i]);
x = i * barWidth + (barWidth - labelWidth) / 2;
g.drawString(names[i], x, y);
}
}
public static void main(String[] argv) {
JFrame f = new JFrame();
f.setSize(400, 300);
double[] values = new double[3];
String[] names = new String[3];
values[0] = 1;
names[0] = "Item 1";
values[1] = 2;
names[1] = "Item 2";
values[2] = 4;
names[2] = "Item 3";
f.getContentPane().add(new ChartPanel(values, names, "title"));
WindowListener wndCloser = new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
};
f.addWindowListener(wndCloser);
f.setVisible(true);
}
}
Because Java's graphics uses the top left corner as the origin, when you add to the height it will increase down instead of up. Try changing this:
g.setColor(Color.red);
g.fillRect(valueX, valueY, barWidth - 80, height);
g.setColor(Color.black);
g.drawRect(valueX, valueY, barWidth - 80, height);
To this:
g.setColor(Color.red);
g.fillRect(valueX, valueY - 20, barWidth - 80, height);
g.setColor(Color.black);
g.drawRect(valueX, valueY - 20, barWidth - 80, height);
I tried this and it added to the red portion of the bar at the top.
Here is a shot of the original code:
And with the change: