Really having trouble trying to draw a four leaf rose: This is the exercise:
Draw a picture of the “fourleaved rose” whose equation in polar coordinates is
r =cos(2θ) . Let θ go from 0 to 2*pi in 100 steps. Each time, compute r and then compute the (x, y) coordinates from the polar coordinates by using the formula
x = r ⋅ cos( θ) , y = r ⋅ sin(θ )
My Code:
public Rose(double awidth, double aheight)
{
width = awidth;
height = aheight;
theta = 0;
}
public void drawRose(Graphics2D g2)
{
Ellipse2D.Double test ;
double r = 0;
for(int i = 0; i <= 100; i++)
{
r = Math.cos(Math.toRadians(2*theta) );
x = r *( Math.cos( Math.toRadians(theta) ) * width ) + 300;
y = r * ( Math.sin( Math.toRadians(theta) ) * height ) + 300 ;
test = new Ellipse2D.Double(x, y, width, height);
theta += 3.6;
g2.draw(test);
}
}
}
Any help will be greatly appreciately.
Your biggest mistake is here:
test = new Ellipse2D.Double(x, y, width, height);
You're creating 100 Ellipses with the points that are on the rose, but that with heights and widths of the desired rose. You really don't want 100 ellipses, but rather you want to connect lines between the x and y points you've created, that is connect the current x, y with the previous ones created (as long as there is a previous x and y).
One way is via these suggestions, but there are other ways to do this:
Use a Path2D object, the concrete implementation would be a Path2D.Double, to hold your data points. Create this before creating data points.
Use a for loop that goes from 0 to 100, and do this in the class's constructor
set your double theta in the loop
set your double r variable in the loop
Calculate your x and y double points
Scale your x and y points by multiplying them with a scale factor so that the drawing has some size. I used 150.0
Translate your x and y values by adding a translation constant. I used 200 and it worked nicely in a 400 x 400 JPanel. Else the center of the rose will be at 0, 0 and only a fourth of it will be visible.
In the first iteration of the for loop call the Path2D's moveTo(...) method to add a starting point
In all other iterations call the lineTo(...) method. This will draw a line between neighboring points.
After the for loop, close the path by calling closePath() on it.
Draw the path in your JPanel's paintComponent method by casting your Graphics parameter into a Graphics2D object (actually you don't need this since your draw method gets a Grahpics2D object), and calling draw(path) with the Graphics2D object, passing in your Path2D object.
For example, this is created:
with this code:
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.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class RosePanel extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int MAX = 100;
private static final double SCALE = 150.0;
private static final double DELTA_X = 200;
private static final double DELTA_Y = DELTA_X;
private static final Color ROSE_COLOR = Color.red;
private static final Stroke ROSE_STROKE = new BasicStroke(8f);
private Path2D path = new Path2D.Double();
public RosePanel() {
for (int i = 0; i < MAX; i++) {
double theta = i * 2 * Math.PI / MAX;
double r = Math.cos(2 * theta);
double dX = SCALE * r * Math.cos(theta) + DELTA_X;
double dY = SCALE * r * Math.sin(theta) + DELTA_Y;
if (i == 0) {
path.moveTo(dX, dY);
} else {
path.lineTo(dX, dY);
}
}
path.closePath();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(ROSE_COLOR);
g2.setStroke(ROSE_STROKE);
g2.draw(path);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
RosePanel mainPanel = new RosePanel();
JFrame frame = new JFrame("RosePanel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that the key difference between my code and yours, other than the translations and the scaling is that I'm connecting the line between points created.
Related
I have to make a program that generates stars in random locations of random size. My code already plots the stars in random locations, but I can't manage to randomly change their sizes. I tried assigning a size factor to each point to alter the distance between them but the stars came out all messed up. Is there a scaling method I can use?
Here is what I have so far, it plots the stars in random locations.
final int MID = WIDTH / 2;
final int TOP = 50;
//sky
Color skyColor = new Color(0, 0, 0);
page.fillRect(0,0,getWidth(),getHeight());
//ground
Color groundColor = new Color(95,95,95);
page.setColor(groundColor);
page.fillRect(0,HEIGHT-20,getWidth(),getHeight());
//star
for (int i = 1; i <= starCount; i++)
{
int ranLocX = gen.nextInt(700 - 100) + 100;
int ranLocY = gen.nextInt(300 - 75) + 75;
int ranSize = gen.nextInt(8 - 1) + 1;
int sizeXA = (-10 * ranSize);
int sizeXB = (10 * ranSize);
int sizeXC = (-5 * ranSize);
int sizeXD = (-10 * ranSize);
int sizeXE = (-10 * ranSize);
int sizeXF = (-10 * ranSize);
int sizeYC = (10 * ranSize);
int sizeYD = (-10 * ranSize);
int sizeYE = (10 * ranSize);
page.drawPolygon(new int[] {xa + ranLocX, xb + ranLocX, xc + ranLocX, xd + ranLocX, xe + ranLocX, xf + ranLocX}, new int[] {ya + ranLocY, yb + ranLocY, yc + ranLocY, yd + ranLocY, ye + ranLocY, yf + ranLocY}, 6);
}
Here is a simple method you can use to create a Shape with any given number of points and radius:
public static Shape radiusShape(int points, int... radii)
{
Polygon polygon = new Polygon();
for (int i = 0; i < points; i++)
{
double radians = Math.toRadians(i * 360 / points);
int radius = radii[i % radii.length];
double x = Math.cos(radians) * radius;
double y = Math.sin(radians) * radius;
polygon.addPoint((int)x, (int)y);
}
Rectangle bounds = polygon.getBounds();
polygon.translate(-bounds.x, -bounds.y);
return polygon;
}
To create your 5 point star you would use code like:
Shape star = ShapeUtils.radiusShape(10, 30, 12);
It will create a star with 5 outer points and 5 inner points to give the star shape.
So to randomize the size of the star you would randomize the radius.
Check out Playing With Shapes for more examples of the types of Shapes you can create using this method. The above radiusShape(...) method was taken from the ShapeUtils class found in the above link.
I would then suggest you create a custom class with the properties 1) Shape 2) Point so you can paint the Star at different locations on the panel. Then you create an ArrayList to hold instances of the class. In your painting method you iterate through this ArrayList to paint each Shape. The above link will also provide basic code for this concept.
Here's an example of how to change the size of a Polygon. I drew squares but any Shape will work.
just create a scale instance of an AffineTransform and use that to scale the Shape.
I used ThreadLocalRandom to randomly choose the scale to be applied. I always copy the original polygon(template) and then scale that.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Polygons extends JPanel {
static int WIDTH = 500;
static int HEIGHT = 500;
JFrame f = new JFrame();
Polygon b =new Polygon();
ThreadLocalRandom r = ThreadLocalRandom.current();
List<Shape> polys = new ArrayList<>();
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> new Polygons().start());
}
public void start() {
f.add(this);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
Polygon template = new Polygon();
template.addPoint(0,0);
template.addPoint(0,100);
template.addPoint(100,100);
template.addPoint(100,0);
// AffineTransform rotate = AffineTransform.getRotateInstance(Math.toRadians(72.), )
for (int i = 0; i < 20; i++) {
Polygon p = new Polygon(template.xpoints,template.ypoints, template.npoints);
p.translate(r.nextInt(WIDTH), r.nextInt(HEIGHT));
double scale = r.nextInt(10,90)/100.;
AffineTransform scaleIt = AffineTransform.getScaleInstance(scale,scale);
polys.add(scaleIt.createTransformedShape(p));
}
}
public Dimension getPreferredSize() {
return new Dimension(WIDTH,HEIGHT);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Shape shape : polys) {
g2d.draw(shape);
}
}
}
Here is something I wrote a long time ago to create a 5 point star. It draws a single arm and then rotates 72 degrees and draws another, repeating the process.
Because it was written to allow the base to be changed, hence the star size, this might be as better option for scaling the size of your stars rather than using the AffineTransform mentioned above:
for (int i = 0; i < 50; i++) {
// get the base of the next star between 5 and 29 inclusive
int base = r.nextInt(5,30);
Polygon star = createStar(base);
// now randomly position it.
star.translate(r.nextInt(0,400),r.nextInt(0,400));
// and add to the list
polys.add(star);
}
Creating a 5 point star
int startx = 250; // arbitrary starting points
int starty = 250;
public Polygon createStar(int armBase) {
Polygon star = new Polygon();
// The armBase is equal to one side of the inner
// pentagon of the star
// The height of the arm is the distance from the middle of the
// base to the tip of the stars arm. Since the tangent computes
// ratio of the sides of a right triangle, multiplying by half
// the base gives the other side, hence the height.
int armHeight =
(int) (armBase / 2 * Math.tan(Math.toRadians(72)));
// The center offset is the distance from the middle of a given
// base to the center of the inner pentagon.
int centerOffset =
(int) (armBase / 2 * Math.tan(Math.toRadians(54)));
// this works by creating the first arm, rotating 72 degrees
// and then adding the other two coodinates of succeeding arms.
star.addPoint(startx, starty);
star.addPoint(startx + armBase / 2, starty - armHeight);
star.addPoint(startx + armBase, starty);
for (int j = 0; j < 4; j++) {
rotatePolygon(-Math.PI / 5 * 2, startx + armBase / 2,
starty + centerOffset, star);
star.addPoint(startx + armBase / 2, starty - armHeight);
star.addPoint(startx + armBase, starty);
}
star.npoints--;
star.translate(-star.getBounds().x,-star.getBounds().y);
return star;
}
// This is general purpose rotation that rotates about a center
// point. This can be derived using the double angle identities of
// for sin and cosine.
private void rotatePolygon(double ang, double sx, double sy,
Polygon poly) {
for (int j = 0; j < poly.npoints; j++) {
double x = poly.xpoints[j];
double y = poly.ypoints[j];
double xx = sx + (x - sx) * Math.cos(ang)
- (y - sy) * Math.sin(ang);
double yy = sy + (x - sx) * Math.sin(ang)
+ (y - sy) * Math.cos(ang);
poly.xpoints[j] = (int) xx;
poly.ypoints[j] = (int) yy;
}
}
Here's a GUI to draw one five-pointed star.
Here's the complete runnable code. I used polar coordinates to calculate the 10 points I needed to draw a star. I guessed the fraction to get the intermediate points correct.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class StarryNight2GUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new StarryNight2GUI());
}
#Override
public void run() {
JFrame frame = new JFrame("Starry Night");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setBackground(Color.BLACK);
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Color groundColor = new Color(95, 95, 95);
g.setColor(groundColor);
g.fillRect(0, getHeight() - 30, getWidth(), 30);
Polygon polygon = createStar(new Point(320, 240), 80);
g.setColor(Color.YELLOW);
g.fillPolygon(polygon);
}
private Polygon createStar(Point centerPoint, int radius) {
Polygon polygon = new Polygon();
// 72, 144, 216, 288, 360
// 36, 108, 180, 252, 324
// 54, 126, 198, 270, 342
// 18, 54, 90, 126, 162, 198, 234, 270, 306, 342
for (int angle = 18; angle < 360; angle += 72) {
double r = 0.42 * radius;
Point point = toCartesian(centerPoint, angle, r);
polygon.addPoint(point.x, point.y);
point = toCartesian(centerPoint, angle + 36, radius);
polygon.addPoint(point.x, point.y);
}
return polygon;
}
private Point toCartesian(Point centerPoint, int angle, double radius) {
double theta = Math.toRadians(angle);
int x = centerPoint.x + (int) Math.round(Math.cos(theta) * radius);
int y = centerPoint.y + (int) Math.round(Math.sin(theta) * radius);
return new Point(x, y);
}
}
}
I am trying to create a number line with labelled x-axis.Two problems:
Everything works fine for 0-9. But anything after that, the numbers get squashed together and not properly oriented on the scale.
My main axis line tends to disappear every time I try maximizing my window or at times it just wouldn't appear at all.Every time any of these happen, I have to re-compile my code and it works just fine.
Any help with the above problems will be greatly appreciated.
import java.awt.Graphics;
import javax.swing.JFrame;
/**
* #author Emil Shirima
*
*/
public class Drawing extends JFrame {
/**
* #param args
*/
int width = 300, height = 300, spacing = 10;
int x1 = 0, y1 = 150, x2 = 300, y2 = 150;
public Drawing() {
setTitle("Trial");
setSize(width, height);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
#Override
public void paint(Graphics brush) {
brush.drawLine(x1, y1, x2, y2);
x1 = 10;
y1 = 150;
x2 = 10;
y2 = 130;
// brush.drawLine(x1, y1, x2, y2);
for (int i = 0; i < 12; ++i) {
String ID = Integer.toString(i);
x1 = x2 += spacing;
brush.drawLine(x1, y1, x2, y2);
if (i < 10) {
brush.drawString(ID, x1 - 3, y2 + 40);
} else {
// With the below implementation, the numbers overlap each other
// and are not properly oriented on the axis
brush.drawString(ID, x1 - 3, y2 + 40);
// TODO: I need to resize the numbers after 10 so as they fit
// properly on the scale
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Drawing draw_object = new Drawing();
}
Current implementation:
Maximized GUI:
Your main problem:
You change x1, x2 within your paint method, and these changes will persist on the next painting. In other words, you're changing the state of the object within a rendering method, something that you must avoid doing.
You're using "magic" numbers making your program difficult to debug.
Other associated problems:
You're drawing directly in a JFrame, something that the Swing painting tutorials tell you exactly not to do since there are risks of significant side effects.
Instead draw in a JPanel's paintComponent method method.
You're not calling any super painting method, thus breaking the painting chain.
If you want the number line to extend through the component, get the component's size in the painting method (again, paintComponent) and use that to help determine the placement of the line.
Also consider sprinkling in a little FontMetrics to help place your numeric text. For example the following code creates a realizable number line:
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import javax.swing.*;
#SuppressWarnings("serial")
public class SimpleNumberLinePanel extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 300;
private static final int GAP = 10;
private static final int START = 0;
private static final int END = 12;
private static final int VERT_LINE_HEIGHT = 20;
private static final Font FONT = new Font(Font.MONOSPACED, Font.BOLD, 14);
private static final int TEXT_GAP = 2;
#Override
protected void paintComponent(Graphics g) {
// call super method
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
// initialize these guys each time paintComponent is called
int x1 = GAP;
int y1 = height / 2;
int x2 = width - 2 * GAP;
int y2 = y1;
g.drawLine(x1, y1, x2, y2);
for (int i = START; i <= END; i++) {
int x = (i * (x2 - x1)) / (END - START) + GAP;
drawNumberAndLine(g, i, x, y1, VERT_LINE_HEIGHT);
}
}
private void drawNumberAndLine(Graphics g, int number, int x, int y,
int vertLineHeight) {
int x1 = x;
int y1 = y;
int x2 = x;
int y2 = y - vertLineHeight;
g.drawLine(x1, y1, x2, y2);
String text = String.valueOf(number);
g.setFont(FONT);
FontMetrics fontMetrics = g.getFontMetrics();
int textX = x - fontMetrics.stringWidth(text) / 2;
int textY = y + fontMetrics.getHeight() + TEXT_GAP;
g.drawString(text, textX, textY);
}
#Override // make GUI bigger
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Number Line");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new SimpleNumberLinePanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I have a working code which basically paints 15 rectangles on the screen that you can drag around. I made it so that the rectangles falls to the bottom of the screen as time passes. While I have the thread.sleep method at bigger numbers such as 500, I can still drag the rectangles around the screen as they fall with no problems. But as I start to decrease the thread.sleep method to smaller numbers such as 50, suddenly problems arises. Problems such as I can only drag up to 2 rectangles before the rectangles start glitching back to the places where I did not drag them. Sometimes I can only drag up to one rectangles, and once I selected that rectangle, I can't select any other rectangles to drag. I know my codes are definitely right, since it works while the thread.sleep method is at at bigger number, so my question is: why does it start glitching when I make thread.sleep to smaller numbers? Here's part of my code.
while (true) {
for (int i = 0; i < 15; i++) {
P.fY[i]++;
}
Thread.sleep(500);
frame.repaint();
} //the 15 stands for 15 rectangles, and the P.fY stands for the position of y.
So based off of your comment, it seems like you just really need a hand with figuring out how to calculate the distance as a function of time.
By adding 1 each frame loop, you're really saying the speed of each square is 1 pixel / 1 frame.
Instead, you should utilize time and update the distance by a function of time, so that it will be 1 pixel / unit of time. This means the velocity of the squares will then be independent of the frames per second.
I whipped up a code example. The important method is the Square#doUpdate() method. This pertains to exactly what you're looking for.
The procedure it follows is:
Calculate time from last update, store it in delta.
Update the time of the last update to the current time
Calculate deltaX, which is deltaX = delta * velocityX
Calculate deltaY, which is deltaY = delta * velocityY
Add deltaX to x - this updates the x coordinate
Add deltaY to y - this updates the y coordinate
The code is as follows:
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
/**
* #author Obicere
*/
public class MovingSquare {
private volatile int viewportWidth;
private volatile int viewportHeight;
private final LinkedList<Square> squares = new LinkedList<>();
public MovingSquare() {
final JFrame frame = new JFrame("Moving Square");
final JPanel displayPanel = new JPanel() {
#Override
protected void paintComponent(final Graphics g) {
synchronized (squares) {
for (final Square square : squares) {
// Update the square's locations, ideally this will
// be separate of the painting thread
square.doUpdate();
final int x = (int) square.getX();
final int y = (int) square.getY();
g.setColor(square.getColor());
g.drawRect(x, y, square.squareSize, square.squareSize);
}
}
}
};
displayPanel.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(final MouseEvent e) {
final Color nextColor = Color.getHSBColor((float) Math.random(), 1, 0.5f);
final float speedX = (float) Math.random();
final float speedY = (float) Math.random();
synchronized (squares) {
final Square newSquare = new Square(nextColor, speedX, speedY);
squares.add(newSquare);
newSquare.x = e.getX();
newSquare.y = e.getY();
}
}
});
displayPanel.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
viewportWidth = displayPanel.getWidth();
viewportHeight = displayPanel.getHeight();
}
});
final Timer repaintTimer = new Timer(20, null);
repaintTimer.addActionListener(e -> {
if (!frame.isVisible()) {
repaintTimer.stop();
return;
}
frame.repaint();
});
repaintTimer.start();
displayPanel.setPreferredSize(new Dimension(200, 200)); // Sorry MadProgrammer
frame.add(displayPanel);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(MovingSquare::new);
}
private class Square {
private final int squareSize = 25;
private volatile float x;
private volatile float y;
private volatile long lastUpdateTime;
private volatile boolean negateX;
private volatile boolean negateY;
private final float speedX;
private final float speedY;
private final Color color;
public Square(final Color color, final float speedX, final float speedY) {
this.color = color;
this.speedX = speedX;
this.speedY = speedY;
lastUpdateTime = System.currentTimeMillis();
}
/**
* Important method here!!
* <p>
* This updates the location of the squares based off of a set
* velocity and the difference in times between updates.
*/
public void doUpdate() {
// Gets the change in time from last update
final long currentTime = System.currentTimeMillis();
final long delta = currentTime - lastUpdateTime;
if (delta == 0) {
return;
}
// be sure to update the last time it was updated
lastUpdateTime = currentTime;
// Calculate the speed based off of the change in time
final float deltaX = getSpeedX(delta);
final float deltaY = getSpeedY(delta);
// Move each square by the change of distance, calculated from
// the change in time and the velocity.
final float nextX = x + deltaX;
final float nextY = y + deltaY;
handleBouncing(nextX, nextY);
}
private void handleBouncing(final float nextX, final float nextY) {
if (nextX < 0) {
x = 0;
flipX();
} else if (nextX + squareSize >= viewportWidth) {
x = viewportWidth - squareSize;
flipX();
} else {
x = nextX;
}
if (nextY < 0) {
y = 0;
flipY();
} else if (nextY + squareSize >= viewportHeight) {
y = viewportHeight - squareSize;
flipY();
} else {
y = nextY;
}
}
private float getSpeedX(final long delta) {
return (negateX ? -1 : 1) * delta * speedX;
}
private float getSpeedY(final long delta) {
return (negateY ? -1 : 1) * delta * speedY;
}
protected void flipX() {
negateX = !negateX;
}
protected void flipY() {
negateY = !negateY;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public Color getColor() {
return color;
}
}
}
And it in action:
This might seem a bit overwhelming. Step through it, change some things up. Go crazy and see what the results are.
There are also some websites that can help with velocity and how to calculate things like this. If you need further help, just drop a comment down below and I'll see what I can do.
I want there to be 12 ellipses so they represent 5 minute intervals around the clock. However every time I change the linApproxLen for the path iterator, it always either draws too many ellipses or 9 ellipses. How can I make these ellipses represent the 5 minute mark?
private Ellipse2D ellipse = new Ellipse2D.Double();
public void setClockContour(int w, int h)
{
size = (w > h) ? h/6f : w/6f;
ellipse.setFrame(w/2-size*2-4.5f,h/2-size*2-4.5f,size*4,size*4);
double linApproxLen = 0.75 * size * 0.258819; // sin(15 degree)
PathIterator pi = ellipse.getPathIterator(null, linApproxLen);
Point2D[] points = new Point2D[100];
int num_pts = 0;
while ( !pi.isDone() )
{
float[] pt = new float[6];
switch ( pi.currentSegment(pt) ) {
case FlatteningPathIterator.SEG_MOVETO:
case FlatteningPathIterator.SEG_LINETO:
points[num_pts] = new Point2D.Float(pt[0], pt[1]);
num_pts++;
}
pi.next();
}
pts = new Point2D[num_pts];
System.arraycopy(points, 0, pts, 0, num_pts);
}
I think that approach will not work regardless of the linApproxLen parameter. This parameter only specifies the "flatness" of the line. While this will certainly influence the number of line segments that are returned, the actual number will at least depend on the radius of the ellipse that you are painting. So even when you manage to return exactly 12 points in one case, changing the radius (that is, the size given as w and h) will affect the number of returned points.
Computing the positions of 12 points in a circular formation is particularly easy. You can compute the position on the circle with the sine and cosine of the respective angle. The resulting points may then be scaled or moved in order to obtain the desired shape.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ClockCirclesTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new GridLayout(1, 0));
frame.getContentPane().add(new ClockCirclesPanel());
frame.setSize(400,400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class ClockCirclesPanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int w = getWidth() / 2;
int h = getHeight() / 2;
Point2D points[] = computePoints(w, h, 12);
int cx = getWidth() / 2;
int cy = getHeight() / 2;
g.translate(cx, cy);
for (Point2D p : points)
{
g.draw(new Ellipse2D.Double(
p.getX() - 5, p.getY() - 5,
10, 10));
}
}
private Point2D[] computePoints(double w, double h, int n)
{
Point2D points[] = new Point2D[n];
double angleDeltaRad = Math.PI * 2 / n;
for (int i=0; i<n; i++)
{
double angleRad = i * angleDeltaRad;
double ca = Math.cos(angleRad);
double sa = Math.sin(angleRad);
double x = sa * w/2;
double y = ca * h/2;
points[i] = new Point2D.Double(x,y);
}
return points;
}
}
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.