I'm having trouble finding what's wrong with my program. When I execute it, it appears to get stuck into an infinite loop (or something similar) and I can't figure out what's wrong with my program. Here's what I have so far:
public class Spiral extends JComponent{
int WIDTH = 0;
int HEIGHT = 0;
public Spiral(int WIDTH, int HEIGHT) {
this.WIDTH = WIDTH;
this.HEIGHT = HEIGHT;
}
public void paintSpiral(Graphics g){
double a = 3;
double b = 0;
double t = 0;
double theta = Math.toRadians(t);
double r = theta * a + b;
double pi = Math.PI/180;
double end = 720 * pi;
int middle_x = WIDTH / 2;
int middle_y = HEIGHT / 2;
for (theta = 0; theta < end; theta += pi) {
double x = Math.cos(theta) * r + middle_x;
double y = Math.sin(theta) * r + middle_y;
int xx = (int) Math.round(x);
int yy = (int) Math.round(y);
g.drawLine(xx, yy, xx + 10, yy + 20);
}
}
public void paintComponent(Graphics g) {
paintSpiral(g);
}
public static void main(String[] args) {
int WINDOW_WIDTH = 1024;
int WINDOW_HEIGHT = 1024;
JFrame frame = new JFrame();
frame.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
// Set the title of the window
frame.setTitle("Archimedean Spiral");
// Make a new Spiral, add it to the window, and make it visible
Spiral d = new Spiral(1024, 1024);
frame.add(d);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
I have to use Graphics which is why I have the Math.round changing the x values to integers so I can actually draw the lines. That is what I suspect is the problem, but I can't seem to fix it. Any suggestions?
The loop variable t is of type int, so t += pi is effectively a no-op, resulting in an infinite loop.
t should be of type double. Furthermore, it should be local to paintSpiral and not a member of the class. I don't understand why you're using t (which is zero) to initialize r.
Also, your degrees and radians seem all confused.
Related
y=mx+b graph equation sliders
In the given picture, it shows that the slope painted by the program is incorrect.
when the slider bar for the m coefficient is negative the slope still shows positive.
I'm pretty sure it has something to do with the for loop when painting each dot on the graph but I cant seem to figure it out. When I change the x to negative it shows only the negative slope. do I need an if statement and if so what should it say?
private final int X_MIN = -10, Y_MIN = -10, X_MAX = 10, Y_MAX = 10;
private final int REAL_WORLD_WIDTH = X_MAX - X_MIN;
private final int REAL_WORLD_HEIGHT = Y_MAX - Y_MIN;
private final double TICK_LENGTH = 0.2;
private Equation equation;
//-----------------------------------------------------------------
// Constructor: Sets up this panel.
//-----------------------------------------------------------------
public EquationViewportPanel()
{
equation = new Equation();
setBorder(new LineBorder(Color.black, 4));
setPreferredSize(new Dimension(500, 500));
}
//-----------------------------------------------------------------
// Sets the equation.
//-----------------------------------------------------------------
void setEquation(Equation newEquation)
{
equation = newEquation;
}
//-----------------------------------------------------------------
// Converts world X coordinate to screen X coordinate.
//-----------------------------------------------------------------
private int convertX(double x)
{
double offset = x - X_MIN;
double result = offset * getSize().width / REAL_WORLD_WIDTH;
return (int) Math.round(result);
}
//-----------------------------------------------------------------
// Converts world Y coordinate to screen Y coordinate.
//-----------------------------------------------------------------
private int convertY(double y)
{
double offset = Y_MAX - y;
double result = offset * getSize().height / REAL_WORLD_HEIGHT;
return (int) Math.round(result);
}
//-----------------------------------------------------------------
// Draws a line in world coordinates on the screen.
//-----------------------------------------------------------------
private void drawScreenLine(Graphics page, double xMin, double yMin,
double xMax, double yMax)
{
page.drawLine(convertX(xMin), convertY(yMin), convertX(xMax), convertY(yMax));
}
//-----------------------------------------------------------------
// Draws a point in world coordinates on the screen.
//-----------------------------------------------------------------
private void drawScreenPoint(Graphics page, double x, double y)
{
page.drawLine(convertX(x), convertY(y), convertX(x), convertY(y));
}
//-----------------------------------------------------------------
// Draws the graph axes and equation.
//-----------------------------------------------------------------
public void paintComponent(Graphics page)
{
page.setColor(Color.white);
page.fillRect(0,0,getSize().width, getSize().height);
// draw the x and y axis
page.setColor(Color.red);
drawScreenLine(page, X_MIN, 0, X_MAX, 0);
drawScreenLine(page, 0, Y_MIN, 0, Y_MAX);
// draw tick marks
for (int x=X_MIN; x<X_MAX; x++)
drawScreenLine(page, x, -TICK_LENGTH, x, TICK_LENGTH);
for (int y=Y_MIN; y<Y_MAX; y++)
drawScreenLine(page, -TICK_LENGTH, y, TICK_LENGTH, y);
// draw the graph of the equation
page.setColor(Color.black);
double x = X_MIN;
double y;
double stepSize = (double)(X_MAX - X_MIN) / getSize().width;
int screenX = getSize().width;
for (int i = 0; i <= screenX; i++)
{
y = equation.computeValue(x);
drawScreenPoint(page, x, y);
x += stepSize;
}
}
}
I am attempting to simulate a sphere, and shade it realistically given an origin vector for the light, and the sphere being centered around the origin. Moreover, the light's vector is the normal vector on a larger invisible sphere at a chosen point. The sphere looks off.
https://imgur.com/a/IDIwQQF
The problem, is that it is very difficult to bug fix this kind of program. Especially considering that I know how I want it to look in my head, but when looking at the numbers in my program there is very little meaning attached to them.
Since I don't know where the issue is, I'm forced to paste all of it here.
public class SphereDrawing extends JPanel {
private static final long serialVersionUID = 1L;
private static final int ADJ = 320;
private static final double LIGHT_SPHERE_RADIUS = 5;
private static final double LIGHT_X = 3;
private static final double LIGHT_Y = 4;
private static final double LIGHT_Z = 0;
private static final double DRAWN_SPHERE_RADIUS = 1;
private static final int POINT_COUNT = 1000000;
private static Coord[] points;
private static final double SCALE = 200;
public SphereDrawing() {
setPreferredSize(new Dimension(640, 640));
setBackground(Color.white);
points = new Coord[POINT_COUNT];
initializePoints();
for (int i = 0; i < points.length; i++) {
points[i].scale();
}
new Timer(17, (ActionEvent e) -> {
repaint();
}).start();
}
public void initializePoints() { //finding the points on the surface of the sphere (hopefully somewhat equidistant)
double random = Math.random() * (double)POINT_COUNT;
double offset = 2/(double)POINT_COUNT;
double increment = Math.PI * (3 - Math.sqrt(5));
for (int i = 0; i < POINT_COUNT; i++) {
double y = ((i * offset) - 1) + (offset / 2);
double r = Math.sqrt(1 - Math.pow(y, 2));
double phi = ((i + random) % (double)POINT_COUNT) * increment;
double x = Math.cos(phi) * r;
double z = Math.sin(phi) * r;
points[i] = new Coord(x, y, z);
}
}
public void drawSphere(Graphics2D g) {
g.translate(ADJ, ADJ); //shifting from origin for drawing purposes
Arrays.sort(points); //sorting points by their z coordinates
double iHat = -2 * LIGHT_X;
double jHat = -2 * LIGHT_Y; //Light vector
double kHat = -2 * LIGHT_Z;
double angL1 = 0;
if (Math.abs(iHat) != 0.0)
angL1 = Math.atan(jHat / iHat); //converting light vector to spherical coordinates
else
angL1 = Math.PI/2;
double angL2 = Math.atan(Math.sqrt(Math.pow(iHat, 2) + Math.pow(jHat, 2))/ kHat);
double maxArcLength = LIGHT_SPHERE_RADIUS * Math.PI; // maximum arc length
for (int i = 0; i < points.length; i++) {
if(points[i].checkValid()) {
double siHat = -2 * points[i].x;
double sjHat = -2 * points[i].y; //finding normal vector for the given point on the sphere
double skHat = -2 * points[i].z;
double angSF1 = -1 * Math.abs(Math.atan(sjHat / siHat)); // converting vector to spherical coordinates
double angSF2 = Math.atan(Math.sqrt(Math.pow(siHat, 2) + Math.pow(sjHat, 2))/ skHat);
double actArcLength = LIGHT_SPHERE_RADIUS * Math.acos(Math.cos(angL1) * Math.cos(angSF1) + Math.sin(angL1) * Math.sin(angSF1) * Math.cos(angL2 - angSF2)); //calculating arc length at this point
double comp = actArcLength / maxArcLength; // comparing the maximum arc length to the calculated arc length for this vector
int col = (int)(comp * 255);
col = Math.abs(col);
g.setColor(new Color(col, col, col));
double ovalDim = (4 * Math.PI * Math.pow(DRAWN_SPHERE_RADIUS, 2))/POINT_COUNT; //using surface area to determine how large size of each point should be drawn
if (ovalDim < 1) // if it too small, make less small
ovalDim = 2;
g.fillOval((int)points[i].x, (int)points[i].y, (int)ovalDim, (int)ovalDim); //draw this oval
}
}
}
#Override
public void paintComponent(Graphics gg) {
super.paintComponent(gg);
Graphics2D g = (Graphics2D) gg;
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawSphere(g);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setTitle("Sphere");
f.setResizable(false);
f.add(new SphereDrawing(), BorderLayout.CENTER);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
#SuppressWarnings("rawtypes")
private class Coord implements Comparable {
public double x;
public double y;
public double z;
public Coord(double x2, double y2, double z2) {
x = x2;
y = y2;
z = z2;
}
public void scale() {
x *= SCALE;
y *= SCALE; //drawing purposes
z *= SCALE;
}
public String toString() {
return x + " " + y + " " + z;
}
public int compareTo(Object c) {
double diff = this.z - ((Coord)c).z;
if (diff < 0)
return -1;
else if (diff > 0) //for sorting the array of points
return 1;
else
return 0;
}
public boolean checkValid() {
return (z > 0); //checks if need to draw this point
}
}
}
I was hoping to at least draw a realistic looking sphere, even if not completely accurate, and I couldn't tell you what exactly is off with mine
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()));
Below is my code. I am trying to make the center sphere rotate part of the way around its orbit, then when it hits the point on its orbit (-orbitRadius, 0) it simply moves off to the left. I used an if statement, but it does not work properly. The issue is that I need to somehow make the code such that once the if statement is invoked, the coordinates are calculated using the version in the if loop and not the original formulas. It only does this for one frame as is. Can anyone help?
import java.util.*;
import java.awt.*;
public class Spiral
{
public static void main(String[] args)
{
int rcircle = 200;
int radius = 0;
escape(rcircle, radius);
}
public static void escape (int rcircle, int radius)
{
StdDraw.setCanvasSize(600, 400);
StdDraw.setXscale(0, 600);
StdDraw.setYscale(0, 400);
double xprof;
double yprof;
long T0 = System.currentTimeMillis();
final int FRAME_TIME = 50;
for (int t = 0; t<= 1000; t++) { // note: Prof. Yang starts in his optimal position
double x = (Math.cos(Math.toRadians(0+(3)*4)*t)*rcircle/12 + 300);
double y = (Math.sin(Math.toRadians(0+(3)*4)*t)*rcircle/12 + 200);
if (y == 0) {
x = (300 - rcircle/12) - 12*t;
y = 0;
}
StdDraw.circle(x, y, 10);
xprof = (Math.cos(Math.toRadians(0+4*t))*rcircle + 300);
yprof = (Math.sin(Math.toRadians(0+4*t))*rcircle + 200);
StdDraw.filledCircle(xprof, yprof, 10);
StdDraw.show(FRAME_TIME);
StdDraw.clear();
}
}
}
You have a number of problems:
There is an offset of 200 added to y so y will never be zero
There will most likely be some rounding error so checking to 200 is not much better. Try something like:
if (Math.abs(y-200) < 0.0001)
Your code will not make the sphere continue going left, as y will be recalculated next time round the loop. Once the
The sphere starts at (orbitRadius, 200) so if you fix point 3, the sphere will just move to the left (as y == 200)
Here is a solution with 1-3 fixed:
import java.util.*;
import java.awt.*;
public class Spiral
{
public static void main(String[] args)
{
int rcircle = 200;
int radius = 0;
escape(rcircle, radius);
}
public static void escape (int rcircle, int radius)
{
StdDraw.setCanvasSize(600, 400);
StdDraw.setXscale(0, 600);
StdDraw.setYscale(0, 400);
double xprof;
double yprof;
long T0 = System.currentTimeMillis();
final int FRAME_TIME = 50;
double y = 0;
for (int t = 0; t<= 1000; t++) { // note: Prof. Yang starts in his optimal position
double x = (Math.cos(Math.toRadians(0+(3)*4)*t)*rcircle/12 + 300);
if (Math.abs(y-200) < 0.0001) {
x = (300 - rcircle/12) - 12*t;
y = 200;
} else {
y = (Math.sin(Math.toRadians(0+(3)*4)*t)*rcircle/12 + 200);
}
StdDraw.circle(x, y, 10);
xprof = (Math.cos(Math.toRadians(0+4*t))*rcircle + 300);
yprof = (Math.sin(Math.toRadians(0+4*t))*rcircle + 200);
StdDraw.filledCircle(xprof, yprof, 10);
StdDraw.show(FRAME_TIME);
StdDraw.clear();
}
}
}
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);
}