1
public class SimpleHarmonic {
    public static void main(String[] args) {
        StdDraw.setXscale(0,900);
        StdDraw.setYscale(0,700);

        while (true) {
            StdDraw.setPenColor(StdDraw.BLACK);
            StdDraw.line(0,350,900,350); // x-axis
            StdDraw.line(450,0,450,900); // y-axis
            StdDraw.setPenColor(StdDraw.RED);

            for (double x = -450; x <= 450; x += 0.5) {
                double y = 50 * Math.sin(x * (Math.PI / 180));
                int Y = (int) y;
                int X = (int) x;
                StdDraw.line(450 + X, 350 - Y, 450 + X, 350 - Y);
            }
            StdDraw.clear();

        }
    }
}

In this code I am attempting to simulate simple harmonic motion. However, I have only been able to draw a static graph, but I need it to move continously.

I believe I need to use a loop to contionusly redraw the points, but I am not sure how to do that.

How can I make my current sine graph move contionusly?

Edit: Voted to close as non-programming? what?

Registered User
  • 468
  • 1
  • 7
  • 31
  • Ok, you have a challenge with the chosen package. Normally you would shift the graph to the left and redraw the last pixel on the right. Here you will have to redraw completely. Draw a white rectangle and draw your axes and graph again every time. I doubt if it will be fluent – Norbert Jun 06 '16 at 03:25
  • @norbert so that means that we have to clear the screen and redraw using different points over and over again? – Registered User Jun 06 '16 at 03:36
  • With this package it looks like that (I googled and read the API). – Norbert Jun 06 '16 at 03:40
  • @norbert the current issue is that my current program draws the graph point by point instead of drawing the whole sine graph at once. that means when I move the next point, it will be hard to see the difference. I'm going to update my code – Registered User Jun 06 '16 at 03:41
  • The point by point is pretty much unavoidable with drawing curves which are not supported by the base graphical libraries and hardware (so that is going really deep there) – Norbert Jun 06 '16 at 03:43

1 Answers1

2

I took a look at the StdDraw class you are using and it looks like what you want is the

StdDRaw.show(int) method, this method comment states:

/**
 * Display on screen, pause for t milliseconds, and turn on
 * <em>animation mode</em>: subsequent calls to
 * drawing methods such as {@code line()}, {@code circle()}, and {@code square()}
 * will not be displayed on screen until the next call to {@code show()}.
 * This is useful for producing animations (clear the screen, draw a bunch of shapes,
 * display on screen for a fixed amount of time, and repeat). It also speeds up
 * drawing a huge number of shapes (call {@code show(0)} to defer drawing
 * on screen, draw the shapes, and call {@code show(0)} to display them all
 * on screen at once).
 * @param t number of milliseconds
 */

In this library any time you call a draw method such as line or circle it conditionally repaints the frame. By passing the int param to the draw method it will turn all painting methods into "animation mode" and defer repainting the frame until you call draw() (no params).


To make it animate you must make each iteration of your while loop 1 animation frame, each frame will need to differ from the previous one. You can do this by using a variable outside your loop to offset each frame by a small ammount. Ill call this offset

With this information you can alter your loop to look like:

    double offset = 0;
    while (true) {
        offset+=1; // move the frame slightly
        StdDraw.show(10); // defer repainting for 10 milisecoinds

        StdDraw.clear(); // clear before painting

        StdDraw.setPenColor(StdDraw.BLACK);
        StdDraw.line(0,350,900,350); // x-axis
        StdDraw.line(450,0,450,900); // y-axis
        StdDraw.setPenColor(StdDraw.RED);

        for (double x = -450; x <= 450; x += 0.5) {
            // apply the offset inside of calculation of Y only such that it 
            // slowly "moves" the sin wave
            double y = 50 * Math.sin((offset+x) * (Math.PI / 180));
            int Y = (int) y;
            int X = (int) x;
            StdDraw.line(450 + X, 350 - Y, 450 + X, 350 - Y);
        }

        StdDraw.show(); // end animation frame. force a repaint 
    }


A few improvements in your code

1 Inside your loop where you draw each "dot" you are increnting by .5. Because that X value is literally 1 pixel you arent gaining anything by going to .5 instead of 1. 1 is quite literally the smallest you can visually see in this enviroment. I recommend making it at least be x+=1

for (double x = -450; x <= 450; x += 1)

2 You are using the .line method but drawing to the same point. You could significantly speed up your program by only calculating every 3rd pixels Y value and connecting the dots. For instance

double prevX = -450;
double prevY = 50 * Math.sin((prevX+offset) * (Math.PI / 180)); // seed the previous Y to start
for (double x = 0; x <= 450; x += 3) {
    double y = 50 * Math.sin((x+offset) * (Math.PI / 180));
    StdDraw.line(450 + (int)prevX, 350 - (int)prevY, 450 + (int)x, 350 - (int)y);
    prevX = x;
    prevY = y;
}

3 This isnt your code but in the StdDraw.init method you can set some rendering hints to allow for cleaner lines. This should make it look alot nicer

offscreen.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, 
                           RenderingHints.VALUE_STROKE_PURE);

Combining all those things heres what I wrote

public static void main(String[] args) {
    StdDraw.setXscale(0,900);
    StdDraw.setYscale(0,700);

    double offset = 0;
    while (true) {
        StdDraw.show(10);
        StdDraw.clear();
        offset-=1;

        StdDraw.setPenColor(StdDraw.BLACK);
        StdDraw.line(0,350,900,350); // x-axis
        StdDraw.line(450,0,450,900); // y-axis
        StdDraw.setPenColor(StdDraw.RED);


        double prevX = 0;
        double prevY = 50 * Math.sin((prevX+offset) * (Math.PI / 180)); // seed the previous Y to start
        StdDraw.filledCircle(450 + prevX, 350 - prevY, 5);

        for (double x = 0; x <= 450; x += 3) {
            double y = 50 * Math.sin((x+offset) * (Math.PI / 180));
            StdDraw.line(450 + (int)prevX, 350 - (int)prevY, 450 + (int)x, 350 - (int)y);
            prevX = x;
            prevY = y;
        }
        StdDraw.show();

    }
}

I dont have an animation recorder so heres a picture Screenshot

ug_
  • 11,267
  • 2
  • 35
  • 52