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!
The problem is I cannot seem to get the Y Axis calculating properly.
The code below show output the red marker at the top right hand side of the axis but it does not
public void paintComponent(Graphics g) {
int y = 10000; // balance
int x = 52 ; // weeks
int prevX, prevY;
int maxX = 52;
int maxY = 10000;
int Xleft = 200;
int Xright = 900;
int Ytop = 50;
int Ybottom = 330;// defining axis
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);
g2.drawLine(Xleft,280,Xright,280);
Font f = new Font("Serif", Font.BOLD, 14);
g2.setFont(f);
g2.drawString("Account Balance (£)", 35, 200);
g2.drawString("Elapsed Time (Weeks)", 475, 340);
//retrieve values from your model for the declared variables
//calculate the coords line on the canvas
double balance = (((double)y / maxY) * Y_AXIS_LENGTH) - Y_AXIS_OFFSET; //floating point arithmetic
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);
System.out.println(xPos + " " + yPos);
}
Shouldn't this:
double balance = (((double)y / maxY) * Y_AXIS_LENGTH) - Y_AXIS_OFFSET;
be this?
double balance = Y_AXIS_OFFSET - (((double)y / maxY) * Y_AXIS_LENGTH);
Related
I have to draw a ring using lines (drawLine) in Java that should look like the attached picture. We are provided with the classDrawingPanel that can be found here.
I've made a regular circle using lines, but I'm unsure how to get the ring shape. I'm new to programming and this is my first post, so apologies if I've missed something important.
This is my code so far:
public static int panelSize = 400;
public static void drawCircle()
{
double radius = 200;
int x2 = 200;
int y2 = 200;
DrawingPanel dp = new DrawingPanel(panelSize, panelSize);
dp.setBackground(Color.CYAN);
Graphics dpGraphics = dp.getGraphics();
dpGraphics.setColor(Color.RED);
for (int circle = 0; circle <= 360; circle++)
{
int x = (int)(x2 + Math.sin(circle * (Math.PI / 180)) * radius);
int y = (int)(y2 + Math.cos (circle * (Math.PI / 180)) * radius);
dpGraphics.drawLine(x, y, x2, y2);
}
}
This is what the final result should look like:
Such a figure can be drawn by drawing a line from one point to a point farther away on the circle, passing the starting point several times.
This is what I came up with:
// Radius
int radius = 200;
// center of the circle
int centerX = 300, centerY = 300;
// The number of edges. Set to 5 for a pentagram
int mod = 136;
// The number of "points" to skip - set to 2 for a pentagram
int skip = 45;
// Precalculated multipier for sin/cos
double multi = skip * 2.0 * Math.PI / mod;
// First point, calculated by hand
int x1 = centerX; // sin(0) = 0
int y1 = centerY + radius; // cos(0) == 1
for (int circle = 1; circle <= mod; circle++)
{
// Calculate the end point of the line.
int x2 = (int) (centerX + radius * Math.sin(circle * multi));
int y2 = (int) (centerY + radius * Math.cos(circle * multi));
dpGraphics.drawLine(x1, y1, x2, y2);
// Next start point for the line is the current end point
x1 = x2;
y1 = y2;
}
The result looks like this:
I am unable to create several instances of the waveClock object even though I have put it in an array and marked the centre positions for each object. I would like to create 4 objects in one window, all responding to different sound frequencies/beat onsets etc
Could someone shed some light on how to go about this? I believe it may be an issue with the centerX and centerY variables in the waveClock class
ArrayList<waveClock> waveClocks = new ArrayList<waveClock>();
//global variables
float angnoise, radiusnoise;
float xnoise, ynoise;
float angle = -PI/6;
float radius;
float strokeCol = 254;
int strokeChange = -1;
int speed; //changes speed of visualisation once beat is detected?
void setup()
//for every waveClock we need 180 pixels width, then add 20 pixels for first gap
size(740, 650);
background(255);
//code is called
waveClocks.add(new waveClock(100, height/2, minRadius, bassColour, lowBassBand, highBassBand, numberOfLowOnsetsThreshold));
waveClocks.add(new waveClock(280, height/2, minRadius, midColour, lowMidBand, highMidBand, numberOfMidOnsetsThreshold));
waveClocks.add(new waveClock(460, height/2, minRadius, highColour, lowHighBand, highHighBand, numberOfHighOnsetsThreshold));
waveClocks.add(new waveClock(640, height/2, minRadius, veryHighColour, lowVeryHighBand, highVeryHighBand, numberOfVeryHighOnsetsThreshold));
//set the min and max radius of each of the viz circles
/* for (int i = 0; i < waveClocks.size(); i++) {
//go through the arraylist of waveClocks and set the min and max radius of each circle
waveClocks.get(i).setMinMaxRadius(minRadius, maxRadius);
}*/
song.play();
beat = new BeatDetect(song.bufferSize(), song.sampleRate());
bl = new BeatListener(beat, song);
}
void draw() {
//clear the screen by painting it black
//background(0);
for (int i = 0; i < waveClocks.size(); i++) {
//has there been a beat in the range? get(circle ID).low band, high band etc.
if (beat.isRange(waveClocks.get(i).getLowBand(), waveClocks.get(i).getHighBand(), waveClocks.get(i).getOnsetThreshold())) {
waveClocks.get(i).setMaxRadius();
}
//waveClocks.get(i).drawCircle();
waveClocks.get(i).drawWaveClock();
}
}
waveClock class in a separate tab
//class is an architecture blueprint
//objects are the actual buildings built from the methods (can make as many as you like)
//constructor is the builder/constructor literally
class waveClock {
float centerX; //co-ordinates of circle's position
float centerY; //co-ordinates of circle's position
float radius; //avg radius
// float minRadius; //smallest size it can be
// float maxRadius; //biggest size it can be
color col; //colour
int onsetThreshold; //
int lowBand; //looks at lowest band of frequency and makes circle sensitive to it
int highBand; //looks at highest band of frequency and makes circle sensitive to it
boolean onset; //has there been an onset (beat has occurred or not?)
//the constructor
waveClock(float x, float y, float r, color c, int lb, int hb, int t) {
centerX = x;
centerY = y;
radius = r;
col = c;
lowBand = lb;
highBand = hb;
onsetThreshold = t;
}
void drawWaveClock() {
radiusnoise += 0.005;
radius = (noise(radiusnoise)*350) + 1;
angnoise += 0.005;
angle += (noise(angnoise)*6) - 3;
if (angle > 360) {
angle -= 360;
} else if (angle < 0) {
angle += 360;
}
xnoise += 0.01;
ynoise =+ 0.01;
float centerX = width/2 + (noise(xnoise)*100) - 50;
float centerY = height/2 + (noise(ynoise)*100) - 50;
float rad = radians(angle);
float x1 = centerX + (radius*cos(rad));
float y1 = centerY + (radius*sin(rad));
float opprad = rad + PI;
float x2 = centerX + (radius*cos(opprad));
float y2 = centerY + (radius*sin(opprad));
strokeCol += strokeChange;
if (strokeCol > 354) {
strokeChange = -1;
} else if (strokeCol < 0) {
strokeChange = 1;
}
stroke(strokeCol, 60);
strokeWeight(1);
line(x1, y1, x2, y2);
}
}
You aren't ever using the class-level centerX and centerY variables. Instead, you're recalculating a new centerX and centerY in the drawWaveClock() function.
float centerX = width/2 + (noise(xnoise)*100) - 50;
float centerY = height/2 + (noise(ynoise)*100) - 50;
These are all drawn from the center of the screen, so the waves will end up in the same position.
In the future, please try to narrow your problem down to a MCVE that demonstrates the problem. Also please use proper naming conventions- classes start with an upper-case letter, for example. Good luck.
I'm drawing a circle in JPanel and using Swing Timer to update x,y co-ordinates of the circle.
How to move the circle in circular path.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
Shape planet = new Ellipse2D.Double(x, y, 20, 20);
g2d.fill(planet);
g2d.dispose();
}
public void actionPerformed(ActionEvent evt) {
double R = 200;
for (double t = 0; t < 2 * Math.PI; t += 0.01) {
x = R * Math.cos(t) + 0;
y = R * Math.sin(t) + 0;
revalidate();
repaint();
}
}
You really already have it there.
x = R * Math.cos(t) + 0;
y = R * Math.sin(t) + 0;
The 0s you have there represent the center (x, y respectively) of the circle. So to rotate the circle around in the path of another circle orbital of center 0,0 and radius R2. The orbital theta value (oTheta) will increase once per frame.
double R = 200;
double R2 = 1000;
oTheta += 0.1; // depending on your framerate, the more you add, the faster it will orbit
for (double t = 0; t < 2 * Math.PI; t += 0.01) {
x = R * Math.cos(t) + 0 + R2 * Math.cos(oTheta);
y = R * Math.sin(t) + 0 + R2 * Math.sin(oTheta);
revalidate();
repaint();
}
The loop is not needed. Timer will call this after a specified interval and update oTheta.
public void actionPerformed(ActionEvent evt) {
oTheta += 0.01;
x = radius * Math.cos(oTheta) + centerX;
y = radius * Math.sin(oTheta) + centerY;
revalidate();
repaint();
}
I'm making an application about space physics, so I do lots with orbits. Naturally, I encounter the Ellipse2D.Double to draw my orbits on the screen.
Whenever my JPanel refreshes, I draw the orbit of a body using an Ellipse2D, as well as the body itself with a different method.
Essentially, I discovered that when numbers get very large (whether it be the size of the orbits get large or the visualization is zoomed in very far), the position of the body and the Ellipse2D do not line up.
I calculate the position of the body using a conversion from polar coordinates to rectangular coordinates, and I leave the math for the Ellipse2D up to the geom package.
Take a look at this code sample. It's the most self-contained version of my problem that I can make, since scale of the circle has to be very large:
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.math.BigDecimal;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class EllipseDemo extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.add(new EllipseDemo());
frame.setVisible(true);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
// These values allow for a very zoomed in view of a piece of the circle
BigDecimal[] circleCenter = { new BigDecimal(-262842.5), new BigDecimal(-93212.8) };
BigDecimal circleRadius = new BigDecimal(279081.3);
// Draw the circle at the given center, with the given width and height
// x = centerx - radius, y = centery - radius, w = h = radius * 2
g2d.draw(new Ellipse2D.Double(circleCenter[0].subtract(circleRadius).doubleValue(),
circleCenter[1].subtract(circleRadius).doubleValue(), circleRadius.multiply(new BigDecimal(2)).doubleValue(),
circleRadius.multiply(new BigDecimal(2)).doubleValue()));
// Get a rectangular conversion of a point on the circle at this angle
BigDecimal angle = new BigDecimal(0.34117696217);
BigDecimal[] rectangular = convertPolarToRectangular(new BigDecimal[] {
circleRadius, angle });
// Draw a line from the center of the circle to the point
g2d.draw(new Line2D.Double(circleCenter[0].doubleValue(), circleCenter[1].doubleValue(),
circleCenter[0].add(rectangular[0]).doubleValue(), circleCenter[1]
.add(rectangular[1]).doubleValue()));
}
public BigDecimal[] convertPolarToRectangular(BigDecimal[] polar) {
BigDecimal radius = polar[0];
BigDecimal angle = polar[1];
BigDecimal x = radius.multiply(new BigDecimal(Math.cos(angle.doubleValue())));
BigDecimal y = radius.multiply(new BigDecimal(Math.sin(angle.doubleValue())));
return new BigDecimal[] { x, y };
}
}
The code above essentially draws a circle on the screen very far away with a large radius. I've picked the dimension so that a piece of the circle is visible in the small window.
Then it draws a line from the center of the circle to a point on the circle that's visible in the window: I picked an angle that was visible on the window and used geometry to convert that angle and the radius of the circle into rectangular coordinates.
This is what the program displays:
Notice that the line doesn't actually end up touching the ellipse. Now, I decided I had to find out whether it was the point I calculated or the ellipse that were incorrect. I did the math on my calculator, and found that the line was correct, and the ellipse incorrect:
Considering that the calculator is probably not wrong, I am led to believe the Ellipse2D is not drawing correctly. However, I tried many other angles, and this is the pattern I found:
And that leads me to believe the calculations are somehow wrong.
So that's my problem. Should I be using something other than Ellipse2D? Maybe Ellipse2D is not accurate enough? I used BigDecimals in my code sample because I thought it would give me more precision - is that the wrong approach? My ultimate goal is to be able to calculate the rectangular position of a point on an ellipse at a specific angle.
Thanks in advance.
You see this error because Ellipse2D is approximated by four cubic curves. To make sure just take a look at its path iterator defining shape border: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/awt/geom/EllipseIterator.java#187
To improve quality we should approximate ellipse by higher number of cubic curves. Here is an extention of standard java implementation with changeable number of segments:
class BetterEllipse extends Ellipse2D.Double {
private int segments;
public BetterEllipse(int segments, double x, double y, double w, double h) {
super(x, y, w, h);
this.segments = segments;
}
public int getSegments() {
return segments;
}
#Override
public PathIterator getPathIterator(final AffineTransform affine) {
return new PathIterator() {
private int index = 0;
#Override
public void next() {
index++;
}
#Override
public int getWindingRule() {
return WIND_NON_ZERO;
}
#Override
public boolean isDone() {
return index > getSegments() + 1;
}
#Override
public int currentSegment(double[] coords) {
int count = getSegments();
if (index > count)
return SEG_CLOSE;
BetterEllipse ellipse = BetterEllipse.this;
double x = ellipse.getCenterX() + Math.sin(2 * Math.PI * index / count) * ellipse.getWidth() / 2;
double y = ellipse.getCenterY() + Math.cos(2 * Math.PI * index / count) * ellipse.getHeight() / 2;
if (index == 0) {
coords[0] = x;
coords[1] = y;
if (affine != null)
affine.transform(coords, 0, coords, 0, 1);
return SEG_MOVETO;
}
double x0 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index - 2) / count) * ellipse.getWidth() / 2;
double y0 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index - 2) / count) * ellipse.getHeight() / 2;
double x1 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index - 1) / count) * ellipse.getWidth() / 2;
double y1 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index - 1) / count) * ellipse.getHeight() / 2;
double x2 = x;
double y2 = y;
double x3 = ellipse.getCenterX() + Math.sin(2 * Math.PI * (index + 1) / count) * ellipse.getWidth() / 2;
double y3 = ellipse.getCenterY() + Math.cos(2 * Math.PI * (index + 1) / count) * ellipse.getHeight() / 2;
double x1ctrl = x1 + (x2 - x0) / 6;
double y1ctrl = y1 + (y2 - y0) / 6;
double x2ctrl = x2 + (x1 - x3) / 6;
double y2ctrl = y2 + (y1 - y3) / 6;
coords[0] = x1ctrl;
coords[1] = y1ctrl;
coords[2] = x2ctrl;
coords[3] = y2ctrl;
coords[4] = x2;
coords[5] = y2;
if (affine != null)
affine.transform(coords, 0, coords, 0, 3);
return SEG_CUBICTO;
}
#Override
public int currentSegment(float[] coords) {
double[] temp = new double[6];
int ret = currentSegment(temp);
for (int i = 0; i < coords.length; i++)
coords[i] = (float)temp[i];
return ret;
}
};
}
}
And here is how you can use it in your code instead of standard one (I use 100 segments here):
g2d.draw(new BetterEllipse(100, circleCenter[0].subtract(circleRadius).doubleValue(),
circleCenter[1].subtract(circleRadius).doubleValue(), circleRadius.multiply(new BigDecimal(2)).doubleValue(),
circleRadius.multiply(new BigDecimal(2)).doubleValue()));
I have a diagonal line and I have also a circles having a 100 meters in distance. The problem is that the circles are not really to the center of the line. I know this is quiet easy but I'm just confused on how to do it.. Could someone help me how to put the circles at the center of the line?
Here's what I've tried so far :
public void paint(Graphics g)
{
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(Color.white);
int x0_pixel = 0;
int y0_pixel = 0;
int x1_pixel = getWidth();
int y1_pixel = getHeight();
int x0_world = 0;
int y0_world = 0;
double x1_world = 2000; // meters
double y1_world = 1125; // meters
double x_ratio = (double) x1_pixel / x1_world;
double y_ratio = (double) y1_pixel / y1_world;
int xFrom = 0;
int yFrom = 0;
double xTo = x1_world;
double yTo = y1_world;
int FromX_pixel = convertToPixelX(xFrom, x_ratio);
int FromY_pixel = convertToPixelY(y1_pixel, yFrom, y_ratio);
int ToX_pixel = convertToPixelX((int) xTo, x_ratio);
int ToY_pixel = convertToPixelY(y1_pixel, (int) yTo, y_ratio);
g2d.setColor(Color.RED);
g2d.drawLine(FromX_pixel, FromY_pixel, ToX_pixel, ToY_pixel);
double theta = Math.atan(yTo / xTo);
int len = (int) Math.sqrt(xTo * xTo + yTo * yTo);
int interval = 100;
final double cosTheta = Math.cos(theta);
final double sinTheta = Math.sin(theta);
for (int distance = xFrom; distance <= len; distance += interval)
{
double distance_x = distance * cosTheta;
double distance_y = distance * sinTheta;
int x_circle_pixel = convertToPixelX(distance_x, x_ratio);
int y_circle_pixel = convertToPixelY(y1_pixel, distance_y, y_ratio);
g2d.drawOval(x_circle_pixel, y_circle_pixel, 50, 50);
g2d.setColor(Color.BLUE);
}
Toolkit.getDefaultToolkit().
sync();
g2d.dispose();
}
private static int convertToPixelY(int y_offset, double y_world, double y_ratio)
{
return (int) (y_offset - (y_world * y_ratio));
}
private static int convertToPixelX(double x_world, double x_ratio)
{
return (int) (x_world * x_ratio);
}
When you draw an oval, the first two parameters are the upper-left corner of the rectangle that holds the oval. The next two parameters are the width and height of this same bounding rectangle. Your current code places the upper-left corner on the line itself, but what you actually want is that the center of the bounding rectangle be placed on the line. The solution to your problem is to simply shift the upper-left corner over by 1/2 the diameter. Your code should have something like so:
public class GraphicsFoo extends JPanel {
// avoid using magic numbers:
private static final int CIRCLE_DIAMETER = 50;
//....
// override a JComponent's paintComponent method, not its paint method
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setBackground(Color.white);
// make your graphics smooth
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// ...
final double cosTheta = Math.cos(theta);
final double sinTheta = Math.sin(theta);
for (int distance = xFrom; distance <= len; distance += interval)
{
//....
// *** here's the key: ***
g2d.drawOval(
x_circle_pixel - CIRCLE_DIAMETER / 2,
y_circle_pixel - CIRCLE_DIAMETER / 2,
CIRCLE_DIAMETER, CIRCLE_DIAMETER);
g2d.setColor(Color.BLUE);
}