This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 years ago.
Ok so I have this program which creates a
NanOrg
object that acts as a moving ball that bounces off the walls and other NanOrgs
`public class NanOrg extends Ellipse2D.Float{
public int x_speed, y_speed;
private int d;
private int width = MainWindow.WIDTH;
private int height = MainWindow.HEIGHT;
private ArrayList<NanOrg> nanorgs;
Rectangle2D r = new Rectangle2D.Float(super.x, super.y, d, d);
public NanOrg(int diameter, ArrayList<NanOrg> nanorgs){
super((int) (Math.random() * (MainWindow.WIDTH - 20) + 1), (int) (Math.random() * (MainWindow.HEIGHT - 20) + 1), diameter, diameter);
this.d = diameter;
this.x_speed = (int) (Math.random() * 5 + 1);
this.y_speed = (int) (Math.random() * 5 + 1);
this.nanorgs = nanorgs;
}
public void move(){
for(NanOrg nanorg : nanorgs){
if(nanorg != this && nanorg.intersects(r)){
int tempx = x_speed;
int tempy = y_speed;
x_speed = nanorg.x_speed;
y_speed = nanorg.y_speed;
nanorg.x_speed = tempx;
nanorg.y_speed = tempy;
break;
}
}
if(super.x < 0){
super.x = 0;
x_speed = Math.abs(x_speed);
}
else if(super.x > width - d){
super.x = width - d;
x_speed = -Math.abs(x_speed);
}
if(super.y < 0){
super.y = 0;
y_speed = Math.abs(y_speed);
}
else if(super.y > height - d){
super.y = height - d;
y_speed = -Math.abs(y_speed);
}
super.x += x_speed;
super.y += y_speed;
}
}` and a
Oil
object which represents unmoving droplets of oil.
public class Oil extends Ellipse2D.Float {
private int width = MainWindow.WIDTH;
private int height = MainWindow.HEIGHT;
private int d;
private ArrayList<Oil> oils;
Rectangle2D rect = new Rectangle2D.Float(super.x, super.y, d, d);
public Oil(int diameter){
super((int) (Math.random() * (MainWindow.WIDTH - 20) + 1), (int) (Math.random() * (MainWindow.HEIGHT - 20) + 1), diameter, diameter);
this.d = diameter;
}
}
The NanOrgs are spawn at a random location and move in a random direction and speed. The NanOrg's are able to bounce of eachother, but I need help figuring out how to deal with the collision between the Oil and NanOrg, specifically so the NanOrg can "eat" the oil by removing it. I have two other Classes:
MainWindow and PaintSurface
The MainWindow Class creates the window as a JApplet.
`public class MainWindow extends JApplet{
public static final int WIDTH = 500;
public static final int HEIGHT = 500;
private PaintSurface canvas;
public void init(){
this.setSize(WIDTH, HEIGHT);
this.setName("NanOrg Program");
canvas = new PaintSurface();
this.add(canvas, BorderLayout.CENTER);
Timer t = new Timer(20, e -> {canvas.repaint();});
t.start();
}
}
The PaintSurface Class "draws" the Oil and NanOrgspublic class PaintSurface extends JComponent{
public ArrayList<NanOrg> nanorgs = new ArrayList<NanOrg>();
public ArrayList<Oil> oils = new ArrayList<Oil>();
public PaintSurface(){
for(int i = 0; i < 5; i++)
nanorgs.add(new NanOrg(10, nanorgs));
for(int o = 0; o < 30; o++)
oils.add(new Oil(10));
}
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.MAGENTA);
for(NanOrg nanorg : nanorgs){
nanorg.move();
g2.fill(nanorg);
}
for(Oil oil : oils){
g2.setColor(Color.BLACK);
g2.fill(oil);
}
}
}`
That's my program, so just to recap: I have created it so the NanOrgs bounce off each other and the walls, but I need help on what I need to code for the NanOrg to "eat" the Oil(e.g remove it) and where to place the code that removes the Oil. I have tried
public void eatOil(){
for(Oil oil : oils){
if(oil.intersects(r)){
oils.remove(oil);
}
}
}
in the NanOrg Class and then called the method in my PaintSurface class, but when I ran it, I got the NullPointerException. Then I tried doing that same code in the Oil and PaintSurface Class, but I couldn't get either of the two classes to use "r" as the variable defined in NanOrg. So if you could just help me figure out what to do that would be great or tell me what to do if i'm doing something wrong. If you have questions to try and clarify ypur knowledge please ask and i'll answer to the best of my abilities.
You have to create a method inside NanOrgs to detect if a given NanOrg and given Oil intersect each other. You'll need to maintain a list of Oil instances in Oil class just like the list of NanOrgs in NanOrg class.
Then on every call to move() of NanOrg you'll have to check if that NanOrg intersects any Oil droplets in the Oil list. There if you find they intersect, you'll have to remove Oil from the list in Oil.
That's all.
Related
I have two objects in a 2D space. I expect object1 to begin orbiting object2. I derived my methods from the equation
f = G * (m1 * m2 / r*r)
and
dx1 += x1 - x2 * f
etc. However, I am struggling because the object is only moving in the pos pos direction. Here is the class for each object:
Mass.java
import java.awt.Point;
public class Mass {
public static float G = 0.1f;
public Point center;
public float mass, radius, dx = 0, dy = 0;
public boolean locked = false;
public Mass(Point center, float[] vect, float mass) {
this.center = center;
this.dx = vect[0];
this.dy = vect[1];
this.mass = mass;
this.radius = mass;
}
public void moveVector(float[] vector) {
if(!this.locked) {
this.dx += vector[0];
this.dy += vector[1];
}
}
public void lock() {
this.locked = true;
}
public static float distance(Mass obj1, Mass obj2) {
float dX = obj1.center.x - obj2.center.x;
float dY = obj1.center.y - obj2.center.y;
double ans = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));
return (float) ans;
}
public static float force(Mass obj1, Mass obj2) {
double ans = ((obj1.mass * obj2.mass) / Math.pow(distance(obj1, obj2), 2)) * G;
return (float) ans;
}
public static float[] vector(Mass obj1, Mass obj2) {
// total change between the two objects
float force = force(obj1, obj2);
float totalX = Math.abs(obj1.center.x - obj2.center.x);
float totalY = Math.abs(obj1.center.y - obj2.center.y);
float x = totalX * force;
float y = totalY * force;
float[] vector = {x, y};
return vector;
}
}
This is the main class.
Sim.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Sim extends JPanel {
private static final long serialVersionUID = -2669101810074157675L;
public static final int PREF_W = 800, PREF_H = 600;
private Mass object1, object2;
private Sim() {
this.setFocusable(true);
this.setBackground(Color.WHITE);
float[] vect1 = {0, -1}, vect2 = {0, 0};
object1 = new Mass(new Point(PREF_W / 2 - 100, PREF_H / 2 - 100), vect1, 10);
object2 = new Mass(new Point(PREF_W / 2 + 100, PREF_H / 2 + 100), vect2, 30);
gameTimer.start();
}
private Timer gameTimer = new Timer(1000/30, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
object1.moveVector(Mass.vector(object1, object2));
object1.center.x += object1.dx;
object1.center.y += object1.dy;
System.out.println("[" + object1.dx + "," + object1.dy + "]");
}
});
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fillOval(
(int) object1.center.x - (int) object1.radius,
(int) object1.center.y - (int) object1.radius,
(int) object1.radius,
(int) object1.radius
);
g2.fillOval(
(int) object2.center.x - (int) object2.radius,
(int) object2.center.y - (int) object2.radius,
(int) object2.radius,
(int) object2.radius
);
g2.drawLine(object1.center.x, object1.center.y, object2.center.x, object2.center.y);
repaint();
}
/* METHODS FOR CREATING JFRAME AND JPANEL */
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame("Gravity Simulation");
JPanel gamePanel = new Sim();
frame.getContentPane().add(gamePanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
I have it printing out the DX and DY of object1 (the unlocked object) at all times. It seems to get flung super fast, as expected, but it never slows down. Instead, the dx is just increasing slower and slower. I'm not very mathy, but it seems to be logistic. I wonder why this is happening.
So far I have tried rewriting my formula and using a different equation. I have also attempted using different datatypes, and making some things negative. Nothing works, though.
TLDR, the problem:
Objects are not changing DX / DY as expected.
Thank you in advance! Sorry if this was posted somewhere else, I could not find any duplicates.
OK, let's try to derive formulas.
You already have difference vector dX, dY, and make also normalized vector
udX = dX / distance
udY = dY / distance
You also have force magnitude. To get force vector for object 1, just multiply normalized difference components by this magnitude (note minus sign because force direction is TO object2 (while dx, dy is vector FROM object 2))
fx1 = - udX * force
fy1 = - udY * force
(and force vector for object2 if needed)
fx2 = - fx1
fy2 = - fy1
First object velocity vector is (vx1, vy1). At every step you have to modify it with acceleration, where deltaT is time period between cadrs.
vx1 = vx1 + fx1 / mass1 * deltaT
vy1 = vy1 + fy1 / mass1 * deltaT
Now you can modify position with velocity
x1 = x1 + vx * deltaT
y1 = y1 + vy * deltaT
I'm trying to create a simple Java program that takes a math function as input and displays a 2D (x and y) graph in a JPanel. I've got the graph displaying correctly initially.
I tried adding scrolling to the canvas (I called the JPanel subclass I made a GraphCanvas), by which I mean that when the mouse is dragged across it, the 2D plane scrolls as well and the graph is updated for the newly revealed areas.
To display this actual function, I am using a Path2D object that is made up of segments connecting enough points so that the graph is in maximum resolution, and I've been using AffineTransforms to translate the path.
It's worth noting that I've only tested the program so far with functions in the form f(x), and not parametrics, even though I've created a class and supporting code in the GraphedInstance class for parametric functions.
I didn't want to include all of the code for the project, but I pushed it to a GitHub repository here (I have very little knowledge of how GitHub works but that should be right).
My problem is that at the moment, whenever I try to scroll the graph, the function just kind of does its own thing and doesn't connect correctly with the initialized piece. It isn't displaying the newly generated parts correctly.
I've done some work on it and improved the problem significantly by waiting to transform the path describing the function until after I've added the new pieces to it, but it still doesn't connect correctly.
This is the method that is supposed to create and stitch together the new parts of the function path, and it's where I think the issue probably lies. This and the rest of the project can also be found in the GitHub repository. Also, sorry for any messy code and/or lack of comments, I'm only a freshman in college and haven't really had any formal training in Java, I've just kind of picked it up as I go along. I would be happy to take any suggestions or recommendations of better ways to do anything as well!
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
public class Minimal {
private static int[] lastPointer = new int[2];
private static double xMin, xMax, yMin, yMax;
private static double xScale, yScale;
private static double minXGraphed, maxXGraphed;
public static Path2D.Double function;
//The frame enclosing the graphing canvas
private static JFrame frame;
//The graphing canvas
private static JPanel canvas = new JPanel() {
private static final int DEFAULT_CANVAS_SIZE = 600;
//Ensures the frame initialized to the right size
#Override
public Dimension getPreferredSize() {
return new Dimension(DEFAULT_CANVAS_SIZE, DEFAULT_CANVAS_SIZE);
}
//Paints the graph whenever it needs updating
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
xScale = getWidth() / (xMax - xMin);
yScale = getHeight() / (yMax - yMin);
g2.setColor(Color.BLUE);
try {
g2.draw(function);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
};
/**
* I'm using the function y = exp(x) as an example. Feel free to replace it.
* #param x the x-value
* #return exp(x), the y-value
*/
private static double getYVal(double x) {
return Math.exp(x);
}
//Initializes the function path
public static void initPath() {
final double STEP_X = (xMax - xMin) / canvas.getWidth();
function = new Path2D.Double();
double x = xMin;
double y = getYVal(x);
function.moveTo(getGraphX(x), getGraphY(y));
for(x = xMin + STEP_X; x <= xMax; x += STEP_X) {
y = getYVal(x);
function.lineTo(getGraphX(x), getGraphY(y));
}
}
/**
* Gets the pixel x-coordinate on the canvas
* that corresponds to a given x-value of the function
* #param x the x-value of the function
* #return the adjusted x-coordinate
*/
private static int getGraphX(double x) {
return (int) (canvas.getWidth() * (x - xMin) / (xMax - xMin));
}
//Same thing as getGraphX except for y-coords
private static int getGraphY(double y) {
return (int) (canvas.getHeight() * (yMax - y) / (yMax - yMin));
}
/*
* This is probably where the problem is, this extends the path so that it
* covers the entire visible range
*/
public static void updateFunctionBounds(double dx) {
double newXMin = xMin - dx / xScale;
double newXMax = xMax - dx / xScale;
final int WIDTH = canvas.getWidth();
final double STEP_X = (xMax - xMin) / WIDTH;
double drawTo;
if((drawTo = newXMin) < xMin && drawTo < minXGraphed) {
minXGraphed = drawTo;
double x = drawTo;
function.moveTo(getGraphX(x), getGraphY(getYVal(x)));
for(x = drawTo + STEP_X; x < xMin; x += STEP_X) {
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
x = xMin;
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
} else if((drawTo = newXMax) > xMax && drawTo > maxXGraphed) {
maxXGraphed = drawTo;
double x = xMax;
function.moveTo(getGraphX(x), getGraphY(getYVal(x)));
for(x = xMax + STEP_X; x < drawTo; x += STEP_X) {
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
x = drawTo;
function.lineTo(getGraphX(x), getGraphY(getYVal(x)));
}
}
public static void main(String[] args) {
initBounds();
initFrame();
}
//Initializes the graph boundaries. Feel free to change this
private static void initBounds() {
xMin = yMin = minXGraphed = -10;
xMax = yMax = maxXGraphed = 10;
}
//Initializes the frame and a MouseAdapter that controls the scrolling
private static void initFrame() {
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
lastPointer[0] = e.getX();
lastPointer[1] = e.getY();
}
#Override
public void mouseDragged(MouseEvent e) {
super.mouseDragged(e);
AffineTransform t = new AffineTransform();
double dx = e.getX() - lastPointer[0];
double dy = e.getY() - lastPointer[1];
t.translate(dx, dy);
updateFunctionBounds(dx);
function.transform(t);
xMin -= dx / xScale;
xMax -= dx / xScale;
yMin -= dy / yScale;
yMax -= dy / yScale;
canvas.repaint();
lastPointer[0] = e.getX();
lastPointer[1] = e.getY();
}
};
canvas.addMouseListener(adapter);
canvas.addMouseMotionListener(adapter);
frame = new JFrame("Minimal Reproducible Approach");
frame.setContentPane(canvas);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
initPath();
canvas.repaint();
}
}
If it was working correctly, the stitching would be seamless and the function would smoothly scroll as the mouse pointer was dragged across the canvas, sort of like it does in Desmos. In reality, it doesn't retain the correct height (y-value) and doesn't actually connect to the previous segments in all cases.
Thank you for any and all help!
Edit: I've updated the code into what I think is a minimal reproducible example.
to start off, I'm making a simple game in Java that involves a blue rectangle that can be moved with arrow keys and seven falling circles of varying color, radius, and falling speed. Essentially, whenever the rectangle comes in contact with one of these circles, the rectangle will "lose" a life, which will be indicated by 3 rectangles on the top right of a JFrame that I haven't drawn yet. Every time the rectangle is hit by one of these circles, one of the rectangles will disappear, and when the blue rectangle is hit once more, a red "Game Over" text will appear in the middle of the frame.
Now then, although I'm having trouble getting the colors and speed to randomize each time the circles hit the bottom, I'll leave those for a future question. My main concern is the hit detection between the circles and the blue rectangle. I know that I need to define a certain method, but I'm unsure on how to go about doing it, and how to test it for each of the seven circles over and over while they're falling and having their Y value constantly change.
How could I go about doing this? Anyway, here's my main and circles classes for this project. I'm aware that there's a lot of junk code that isn't being used in the main class as well. I'll clean it up after I just figure this out.
**Main class (Keyexample)
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.Random;
public class Keyexample extends JPanel implements ActionListener, KeyListener{
Timer t = new Timer(5, this);
private Circle[] Circles = new Circle[7];
private javax.swing.Timer t2;
private Circle newc, c1, c2, c3, c4, c5, c6, c7;
double x = 100, y = 100;
double changeX = 0, changeY = 0;
private int cx = 10, cy = 0;
private int newcx = 0, newcy = 0;
private Random rand = new Random();
private Random colorc = new Random();
private int n = rand.nextInt(8);
public keyExample() {
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
Random colorc = new Random();
Random radiusc = new Random();
int r1 = radiusc.nextInt(12);
int r2 = radiusc.nextInt(12);
int r3 = radiusc.nextInt(12);
int r4 = radiusc.nextInt(12);
int r5 = radiusc.nextInt(12);
int r6 = radiusc.nextInt(12);
int r7 = radiusc.nextInt(12);
Color cc1 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc2 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc3 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc4 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc5 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc6 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
Color cc7 = new Color(colorc.nextInt(255), colorc.nextInt(255),
colorc.nextInt(255));
//creating the 7 circles and spacing them out
c1 = new Circle(cx, cy, r1, cc1);
c2 = new Circle(cx + 50, cy, r2, cc2);
c3 = new Circle(cx + 100, cy, r3, cc3);
c4 = new Circle(cx + 150, cy, r4, cc4);
c5 = new Circle(cx + 200, cy, r5, cc5);
c6 = new Circle(cx + 300, cy, r6, cc6);
c7 = new Circle(cx + 400, cy, r7, cc7);
Circles[0] = c1;
Circles[1] = c2;
Circles[2] = c3;
Circles[3] = c4;
Circles[4] = c5;
Circles[5] = c6;
Circles[6] = c7;
t2 = new javax.swing.Timer(33, new CircleListener());
t2.start();
}
//painting rectangle and circles
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.fill(new Rectangle2D.Double(x, y, 40, 40));
for (int i = 0; i < Circles.length; i++){
Color circlecolor = new Color(rand.nextInt(255), rand.nextInt(255),
rand.nextInt(255));
//circle color starts spazzing out here. constantly changing while falling
g2.setColor(circlecolor);
Circles[i].fill(g);
}
}
public void createCircle(){
}
public void actionPerformed(ActionEvent e) {
repaint();
x += changeX;
y += changeY;
changeX = 0;
changeY = 0;
}
private class CircleListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
Random rand = new Random();
int move = 2 + rand.nextInt(10);
int move2 =2 + rand.nextInt(10);
int move3 =2 + rand.nextInt(10);
int move4 =2 + rand.nextInt(10);
int move5 =2 + rand.nextInt(10);
int move6 =2 + rand.nextInt(10);
c1.move(0, n);
position(c1);
c2.move(0, move);
position(c2);
c3.move(0, move2);
position(c3);
c4.move(0, move3);
position(c4);
c5.move(0, move4);
position(c5);
c6.move(0, move5);
position(c6);
c7.move(0, move6);
position(c7);
repaint();
}
public void position(Circle cp) {
int height = getHeight();
int loc = cp.centerX;
int speed = 3 + rand.nextInt(10);
int radiuss = cp.radius;
Rectangle bound = cp.Bounds();
if (bound.topY + bound.width > height){
cp.centerY = 0;
//moving circle back to the top
cp.move(0, speed);
//randomizing speed of circle after moving to top, not working
cp.radius = 5 + rand.nextInt(20);
//randomizing radius of circle after moving to top, does work
}
}
}
public void up() {
if (y != 0){
changeY = -3.5;
changeX = 0;
}
}
public void down() {
if (y <= 350) {
changeY = 3.5;
changeX = 0;
}
}
public void left() {
if (x >=0) {
changeX = -3.5;
changeY = 0;
}
}
public void right() {
if (x <= 550) {
changeX = 3.5;
changeY = 0;
}
}
public void keyPressed(KeyEvent e) {
int code = e.getKeyCode();
if (code == KeyEvent.VK_UP) {
up();
}
if (code == KeyEvent.VK_DOWN) {
down();
}
if (code == KeyEvent.VK_RIGHT) {
right();
}
if (code == KeyEvent.VK_LEFT) {
left();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
**Circle class
import java.awt.*;
import java.util.Random;
public class Circle{
public int centerX, centerY, radius;
public Color color;
public Circle (int x, int y, int r, Color c){
centerX = x;
centerY = y;
radius = r;
Random random = new Random();
}
public void draw(Graphics g){
Color oldColor = g.getColor();
g.setColor(color);
g.drawOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.setColor(oldColor);
}
public void fill(Graphics g){
Color oldColor = g.getColor();
g.setColor(color);
g.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2);
g.setColor(oldColor);
}
public boolean containsPoint(int x, int y){
int xSquared = (x - centerX) * (x - centerX);
int ySquared = (y - centerY) * (y - centerY);
int radiusSquared = radius * radius;
return xSquared + ySquared - radiusSquared <=0;
}
public void move(int xAmount, int yAmount){
centerX = centerX + xAmount;
centerY = centerY + yAmount;
}
public Rectangle Bounds(){
int x = centerX - radius;
int y = centerY - radius;
return new Rectangle(x, y, radius * 2, radius * 2, Color.red);
}
}
You want to break the problem into two parts: the first - see if there is "definitely no collision". This happens when the center of the circle is more than a radius away from the edges. It's a very fast test, and will be true most of the time:
if(left > x + radius ||
right < x - radius ||
etc.) { // no collision }
The second - if you fail this test, you may still not hit. That depends on whether both x and y are sufficient for overlap. Within this, there are two situations:
A corner of the rectangle is inside the circle: easy to compute (distance from one corner to center of circle < r)
An edge is inside the circle. This means that in one direction (say X) the center lies between the edges; and in the other direction an edge is less than radius away.
This is general, without actual code. I assume you can write the code from here.
You can detect collisions by simple if else conditions on the circle's and rectangle's co ordinates on screen.
distance(circle, Rectangle) <= circle.radius + Rectangle.radius
You can implement distance helper function using the simple distance formula between two points.
link
The problem I'm trying to solve is building a pyramid centered in the window built with individual bricks. With the code blow I expect the there should be 1 brick on the 1st row, two bricks on the 2nd row, 3 on the 3rd row all the way up to 12 bricks on the base of the pyramid. Instead only 1 brick is appearing in the center of the screen. What could I do to correct my code?
import acm.program.*;
import acm.graphics.*;
public class BrickPyramid extends GraphicsProgram{
public void run() {
/** xBrick, yBrick trying to calculate for the center of the window **/
double xBrick = (getWidth() - BRICK_WIDTH) / 2 ;
double yBrick = (getHeight() - BRICK_HEIGHT) /2 ;
/** getting the size of the brick by multiplying the brick width by brick height **/
double buildingBrick = BRICK_WIDTH * BRICK_HEIGHT;
for (int i = 0 ; i <= 12; i ++ ){
for (int j = 0; j < BRICKS_IN_BASE; j++){
double x = i * buildingBrick;
double y = j * buildingBrick;
/* adding the brick with starting point xbrick, ybrick **/
GRect brick = new GRect (xBrick, yBrick, BRICK_WIDTH, BRICK_HEIGHT);
add(brick);
}
}
}
private static final int BRICK_WIDTH = 100;
private static final int BRICK_HEIGHT = 50;
private static final int BRICKS_IN_BASE = 12;
}
You construct all of your bricks with this line executed many times:
GRect brick = new GRect (xBrick, yBrick, BRICK_WIDTH, BRICK_HEIGHT);
but you never change the values of any of the variables passed to the constructor so the bricks are all drawn in the same place.
You need to change these values for each brick. Maybe use i and j to determine those values?
don't know if you are still looking for an answer but this code does the job:
public class Pyramid extends GraphicsProgram {
private static final int BRICK_WIDTH = 30;
private static final int BRICK_HEIGHT = 12;
public void run() {
double xBrick = (getWidth() - BRICK_WIDTH) / 2 ;
double yBrick = (getHeight() - BRICK_HEIGHT) /2 ;
for (int n=1;n<15;n++){
for (int m=0;m<=n-1;m++){
GRect Brick =new GRect(xBrick-(BRICK_WIDTH*(n-1))/2+m*BRICK_WIDTH,yBrick+BRICK_HEIGHT*(n-1),BRICK_WIDTH,BRICK_HEIGHT);
add(Brick);
}
}
}
}
I am creating line graph using outputs from a thread, the threads are simulations of incoming and outgoing bill that run over a course of 52 seconds and this will be dipicted on a line graph as shown below to show the bank balance over the 52 seconds!
Currently the program runs fine, when I click start the point gets updated but every a new point is placed on the graph the previous one disappears. How i can i keep all the points on the graph.
I would assume i would have to create 2 new int variable... prevX & prevY.
import java.awt.*;
import javax.swing.*;
public class DrawPanel extends JPanel {
private static final int X_AXIS_LENGTH = 700;
private static final int Y_AXIS_LENGTH = 230; // could be changed
private static final int X_AXIS_OFFSET = 200;
private static final int Y_AXIS_OFFSET = 85;
private static final int PanelHeight = 365;
private static final int PanelWidth = 1000;
public DrawPanel() {
this.setBackground(Color.white);
this.setPreferredSize(new Dimension(PanelWidth, PanelHeight));
}
public void paintComponent(Graphics g) {
int y = ControlPanel.bk1.getBalance(); // balance
int x = ControlPanel.bk1.getWeek(); // weeks //
int prevX, prevY;
int maxX = ContentPanel.controlPanel.getDuration();
int maxY = 100000;
int Xleft = 200;
int Xright = 900;
int Ytop = 50;
int Ybottom = 330;// defining axis
while (ControlPanel.bk1.getBalance() > maxY) {
int i = maxY / 4;
maxY = maxY + i;
}
Graphics2D g2 = (Graphics2D) g;
super.paintComponent(g2);
g2.setColor(Color.BLUE);
BasicStroke pen = new BasicStroke(4F);
g2.setStroke(pen);
g2.drawLine(Xleft, Ytop, Xleft, Ybottom); // set axis
g2.drawLine(Xleft, 280, Xright, 280);
int i = X_AXIS_OFFSET + (X_AXIS_LENGTH / 2);
int ii = X_AXIS_OFFSET + (X_AXIS_LENGTH / 4);
int iii = ((X_AXIS_LENGTH / 4)) * 3 + X_AXIS_OFFSET;
BasicStroke spaces = new BasicStroke(1F);
g2.setStroke(spaces);
g2.drawLine(i, 280, i, 300);
g2.drawLine(ii, 280, ii, 300);
g2.drawLine(iii, 280, iii, 300);
g2.setStroke(pen);
Font f = new Font("Serif", Font.BOLD, 14);
g2.setFont(f);
g2.drawString("Account Balance (£)", 35, 200);
g2.drawString("Elapsed Time (Weeks)", 475, 340);
g2.setColor(Color.BLACK);
String maxXDisplay = Integer.toString(maxX);
String maxYDisplay = Integer.toString(maxY);
g2.drawString(maxYDisplay, 160, 45);
g2.drawString(maxXDisplay, 900, 300);
// retrieve values from your model for the declared variables
// calculate the coords line on the canvas
double balance = PanelHeight
- ((((double) y / maxY) * Y_AXIS_LENGTH) + Y_AXIS_OFFSET);
double weeks = (((double) x / maxX) * X_AXIS_LENGTH) + X_AXIS_OFFSET;
int xPos = (int) Math.round(weeks);
int yPos = (int) Math.round(balance); // changing back to int to be used
// in drawing oval
g2.setColor(Color.RED);
g.drawOval(xPos, yPos, 2, 2);
}
public void reDraw() {
repaint();
}
}
You appear to be only trying to plot one point in your paintComponent method:
g.drawOval(xPos, yPos, 2, 2);
Usually you'll loop through a for loop drawing all the points in this method. For example something like:
for (int j = 0; j < maxPointCount; j++) {
x = someMethodToGetX(j);
y = someMethodToGetY(j);
double balance = PanelHeight - ((((double) y / maxY) *
Y_AXIS_LENGTH) + Y_AXIS_OFFSET);
double weeks = (((double) x / maxX) * X_AXIS_LENGTH) +
X_AXIS_OFFSET;
int xPos = (int) Math.round(weeks);
int yPos = (int) Math.round(balance);
g2.setColor(Color.RED);
g.drawOval(xPos, yPos, 2, 2);
}
Edit 1
Regarding your recent comment:
Tryed that for loop and it makes no difference to the program
My code above is certainly not code that can be cut and pasted into your program and be expected to work, but rather is only to be seen as an example of a concept. A for loop will work if implemented correctly as it's worked for me many times, but yours is not working, so we have to fix your implementation, and in order to do that, we need more information:
How are you generating your data points?
Are you using a Swing Timer to imitate real-time collection of data?
You will need to store your data points as you collect them so your paintComponent can iterate over them. How are you storing your data points? Is it in an ArrayList? Can we see that code?
Once we see all this, can we see the code where you try to implement a for loop to draw all the data points?
You will need to make an edit to your question to show this new information. If you do this, please notify me by commenting in this answer.
Edit 2
This is a more complete example of what I'm describing, one with a functioning for loop that draws all scaled data points. Of course none of this code can be copied and pasted into your app, but hopefully the concepts contained can be transferred. Please ask if anything looks confusing:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.*;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class TestShowGraph {
private static final int MAX_POINTS = 30;
private static final int TIMER_DELAY = 800;
private static void createAndShowGui() {
ShowGraph showGraphPanel = new ShowGraph(MAX_POINTS);
TimerListener timerListener = new TimerListener(MAX_POINTS, showGraphPanel);
JFrame frame = new JFrame("TestShowGraph");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(showGraphPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
new Timer(TIMER_DELAY, timerListener).start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class TimerListener implements ActionListener {
private static final double EXP_MULTIPLIER = 0.2;
// array of points created in constructor to hold data that
// will eventually be displayed in real time.
// A Swing Timer will copy a point into the pointsList above.
private Point2D[] initPoints;
private int maxPoints;
private int count = 0;
private ShowGraph showGraph;
public TimerListener(int maxPoints, ShowGraph showGraph) {
initPoints = new Point2D[maxPoints];
this.maxPoints = maxPoints;
this.showGraph = showGraph;
// create all data points that will eventually be
// graphed. This is to simulate real-time data collection
for (int i = 0; i < initPoints.length; i++) {
double x = (double) i / initPoints.length;
double y = 1.0 - Math.exp(-1.0 * i * EXP_MULTIPLIER);
initPoints[i] = new Point2D.Double(x, y);
}
}
public void actionPerformed(ActionEvent e) {
if (count < maxPoints) {
// simply push data from initPoints into the list that will
// be used to draw the graph
showGraph.addPoint(initPoints[count]);
count++;
} else {
// unless we've run out of points. Then simply start over
count = 0;
showGraph.clearPointsList();
}
// repaint so that the GUI will show the points
showGraph.repaint();
}
}
#SuppressWarnings("serial")
class ShowGraph extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 600;
private static final int BORDER_GAP = 50;
private static final Color AXIS_COLOR = Color.blue;
private static final Color POINTS_COLOR = Color.red;
private static final Color BACKGRND_COLOR = Color.white;
private static final Stroke AXIS_STROKE = new BasicStroke(3f);
private static final Stroke POINTS_STROKE = new BasicStroke(2f);
private static final double X_SCALE = PREF_W - 2 * BORDER_GAP;
private static final double Y_SCALE = PREF_H - 2 * BORDER_GAP;
private static final int POINT_RADIUS = 3;
// list that the paintComponent method loops through to
// draw points
private List<Point2D> pointsList = new ArrayList<Point2D>();
public ShowGraph(int maxPoints) {
setBackground(BACKGRND_COLOR);
}
public void addPoint(Point2D point2d) {
pointsList.add(point2d);
}
public void clearPointsList() {
pointsList.clear();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawAxises(g2);
drawPoints(g2);
}
private void drawAxises(Graphics g2) {
// derive a Graphics2D object from the one provided by the
// JVM so we can change settings on it without effecting
// the Graphics object provided by the JVM
Graphics2D g2Axises = (Graphics2D) g2.create();
g2Axises.setStroke(AXIS_STROKE);
g2Axises.setColor(AXIS_COLOR);
int x1XAxis = BORDER_GAP;
int y1XAxis = PREF_H - BORDER_GAP;
int x2XAxis = PREF_W - BORDER_GAP;
int y2XAxis = PREF_H - BORDER_GAP;
g2Axises.drawLine(x1XAxis, y1XAxis, x2XAxis, y2XAxis);
int x1YAxis = BORDER_GAP;
int y1YAxis = BORDER_GAP;
int x2YAxis = BORDER_GAP;
int y2YAxis = PREF_H - BORDER_GAP;
g2Axises.drawLine(x1YAxis, y1YAxis, x2YAxis, y2YAxis);
g2Axises.dispose(); // because we derived this we must dispose it
}
private void drawPoints(Graphics2D g2) {
Graphics2D g2Points = (Graphics2D) g2.create();
g2Points.setStroke(POINTS_STROKE);
g2Points.setColor(POINTS_COLOR);
for (Point2D p : pointsList) {
// p points hold data between 0 and 1
// we must scale our points to fit the display
// before displaying them
int pX = (int)(X_SCALE * p.getX()) + BORDER_GAP;
int pY = PREF_H - (int)(Y_SCALE * p.getY()) - BORDER_GAP;
// displayed the scaled points
int radius = POINT_RADIUS;
g2Points.drawOval(pX - radius, pY - radius, 2 * radius, 2 * radius);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
Luck.