Search code examples
javavariable-assignmentgraphics2d

Why is my brushstroke thickness changing?


My assignment is to draw horizontal lines random in width and random line thickness. When I run my program the thickness changes part way through. I don't understand how that is possible since I am setting the width then drawing the line. I don't update so I am not redrawing the line as far as I am aware. The only other code in the program is a init() as we have to do it in an applet.

public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setColor(Color.red);
    int increment = 70; // space between lines
    int linesNum = 5; // number of lines were drawing
    Random rand = new Random();
    int randW;

    Line2D.Float[] lines = new Line2D.Float[linesNum];
    for (int y = 0; y < linesNum; y++) {
        // where we are at on they axis
        int yoffset = (increment * y) + increment;
        // random width of line max 499 min 50
        randW = rand.nextInt((499 - 5) + 1) + 5;
        // random thick line
        g2.setStroke(new BasicStroke(rand.nextInt((increment - 10) + 1) + 5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
        lines[y] = new Line2D.Float(1, yoffset, randW, yoffset);
        g2.draw(lines[y]);

    }

}

}


Solution

  • Every time the applet is repainted, which could happen due to the window moving, being minimized and restored, or being obscured by another window and then revealed, the paint method will be called again, and thus will create a new Random() and will redraw the lines at new random thicknesses. If you don't want that to happen, there are a few approaches you can take.

    Off-screen Buffer

    You can use the createImage method to create a buffer to draw lines on. Use its getGraphics method to get the Graphics object, draw the lines, and then in your paint method just draw this buffer on the screen using the drawImage method of the Graphics object that is passed into paint. Declare the off-screen buffer as a field of your applet class. Since you'll only be drawing on the buffer once, it won't matter how many times your applet gets repainted.

    Array of Random Numbers

    There are two places in your paint method where you call rand.nextInt; you could create two int[] arrays of size linesNum, declare them as fields of your applet, and populate them in your init method using a Random object. Then, in your paint method, instead of generating random numbers, just re-use the ones you had previously stored in these arrays. That way, no matter how many times your applet is repainted, you'll get the same result.

    Seed the Random Number Generator

    In your paint method you create a new Random() object. You can seed the random number generator (RNG), either by calling the constructor that takes a seed as a parameter or by calling the setSeed method on it. If you use the same seed, you'll always get the same sequence of pseudorandom numbers. If you want each run of your applet to have different random line widths (but not for them to change on each repaint), you could create a Random object and use its nextLong method to pick a random seed when your applet starts, store that seed in a field of your applet class, and then use it to seed the RNG in your paint method. In this way, you'll get the same sequence of random numbers every time paint is called.