# Drawing in JPanel not working as expected

• May 23rd, 2013, 04:24 AM
brocode
Drawing in JPanel not working as expected
First of all, I'd like to apologize if this is not in the correct section, this is my first post to the board. If I was wrong to post here, I welcome any criticism so long as you also teach me something ;)

Anyways, I'm currently working on a practice problem from the textbook "Java - How to Program (9th edition)" at the end of chapter 4. (Fig 4.20)
The problem requires that I use 4 seperate while loops (one for each corner) to drawing 15 lines coming from each corner of the panel in a manner that would resemble a hand fan.
This is a typical case of a problem that should take no more than 5 minutes to complete yet for some reason has become quite an ordeal for me.

The issue is that while the top corners display in the manner that the book requires, the bottom ones seem to have a bit of an offset and do not end up meeting the top ones (eg: bottom left fan should extend so that each line's endpoint connects with the endpoints of the top right fan, meeting halfway.)

I've removed the bottom-right fan for the time being, to make it easier to look at.

Here are my two top corners' while loops:

Code :

``` int x; int y; int count; int width = getWidth(); // total width int height = getHeight(); // total height   x = 0; y = height; count = 1; while(count <= 15) { g.drawLine(0, 0, x, y); x += width / 15; y -= height / 15; ++count; } // end while   x = width; y = height; count = 1; while(count <= 15) { g.drawLine(width, 0, x, y); x -= width / 15; y -= height / 15; ++count; } // end while```

Here is the while loop for the bottom left-corner:
Code :

``` x = 0; y = 0; count = 1; while(count <= 15) { g.drawLine(0, height, x, y); x += width / 15; y += height / 15; ++count; } // end while```

Here is a picture of what currently displays:
Attachment 2097

Hopefully I've provided enough information about my problem. Although I'm 99% sure that the problem does not lie elsewhere in my code, 1% is a risk I don't want to take so here are pastebin links to my frame and panel classes (I already had them in pastebin from another board, and am unsure of how clean code snippets appear on this board so I don't want to take chances putting it all up here in case it causes a mess).
Just add these to the end of pastebin(dot)com/
x703CsGv
Yi1cuNZY

Thank you :)
• May 23rd, 2013, 07:44 AM
KevinWorkman
Re: Drawing in JPanel not working as expected
Can you post an SSCCE that demonstrates the problem that we can copy and paste into our own IDE and run and debug ourselves? It's hard to debug code with only disconnected snippets, and looking at your whole problem is overkill (plus I'm behind a firewall that blocks pastebin).
• May 23rd, 2013, 09:19 AM
theoriginalanomaly
Re: Drawing in JPanel not working as expected
I think probably it is because, when you divide an int it rounds down to the nearest whole number.

--- Update ---

What is the width and height set at?
• May 23rd, 2013, 09:35 AM
brocode
Re: Drawing in JPanel not working as expected
I mean, I could copy what's in pastebin to code snippets, but the code itself really doesn't get any more concise, each file is no longer than about 20 lines and all that's being done is a JFrame is being used to drawLines, while the other class creates a Panel to display the JFrame.
• May 23rd, 2013, 09:44 AM
KevinWorkman
Re: Drawing in JPanel not working as expected
Quote:

Originally Posted by brocode
I mean, I could copy what's in pastebin to code snippets, but the code itself really doesn't get any more concise, each file is no longer than about 20 lines and all that's being done is a JFrame is being used to drawLines, while the other class creates a Panel to display the JFrame.

It's really up to you, but it seems to me that making it easier for us to help you would be a good idea. Your call. Posting an SSCCE will get you help in a manner of minutes, whereas posting code snippets or multiple files will probably take longer for somebody to find the time. Depends on how long you feel like waiting for help.
• May 23rd, 2013, 10:28 AM
brocode
Re: Drawing in JPanel not working as expected
I was thinking it was something along these lines as well, but could not figure out a way to work around it, because if I try to provide float values as arguments for the drawLine functions, I get errors. The function requires 4 ints. I feel like at some point I understood a work around for this involving the modulus operator.
Oh, by the way the width and height are set to the value of getWidth() and getHeight() (respectively of course), because one of the requirements (plus why wouldn't you want to do it this way) is to have the drawing actually scale with the window size if you stretch or shrink it.

--- Update ---

Is this better? I combined them into a single file that you can quickly compile and run to see where I'm at, along with some comments stating where I think the issue lies and describing what does what:
Code :

```import java.awt.Graphics; import javax.swing.JPanel; import javax.swing.JFrame;   public class DrawFans extends JPanel { public void paintComponent(Graphics g) { super.paintComponent( g );   int x; // variable representing a lines end point x co-ordinate int y; // same as x, but for the y co-ordinate int count; // counter-control variable int width = getWidth(); // total width int height = getHeight(); // total height   // values set before each while loop represent the end point // for the first line to be drawn (first line always follows border)   // top left corner x = 0; y = height; count = 1; while(count <= 15) { g.drawLine(0, 0, x, y);   // I believe the problem lies in the division resulting in a decimal value which is truncated. // method drawLine however does not accept any type other than int x += width / 15; // Move the next endpoint's x-coordinate to the right by 1/15th of frame y -= height / 15;// Same as above but for y-coordinate, moving co-ordinate upwards ++count; } // end while   // top right corner x = width; y = height; count = 1; while(count <= 15) { g.drawLine(width, 0, x, y); x -= width / 15; y -= height / 15; ++count; } // end while   // bottom left corner, here is where the problem occurs x = 0; y = 0; count = 1; while(count <= 15) { g.drawLine(0, height, x, y); x += width / 15; y += height / 15; ++count; } // end while } // end method paintComponent   public static void main(String [] args) { DrawFans fan = new DrawFans();   JFrame application = new JFrame();   application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   application.add(fan); application.setSize(250, 250); application.setVisible(true); } // end method main } // end class DrawFans```

P.S - At this point in the book, I feel I should mention I don't think the reader is expected to be figuring out workarounds with modulus. The point of the exercise is simply to gain an understanding for the drawLine function. They never make mention or hints that you may need to deal with floating points or anything like that.
• May 23rd, 2013, 11:51 AM
KevinWorkman
Re: Drawing in JPanel not working as expected
Thanks for posting the SSCCE.

You've got a couple things going on here, which aren't exactly intuitive.

First, theoriginalanomaly is right about the integer division screwing you up. Your height and width won't usually be divisible by 15. What you're basically trying to represent with your division is the length to move the next line's endpoint to, so it'll always be the same. Think about what would happen if that number was just slightly off: the offset wouldn't be that noticeable the first few iterations, but by the last iteration you'll be quite a ways off. This is further exacerbated by the order you're drawing your lines, which isn't really "wrong" but it does obscure what's happening. This can be seen by slowly changing the window width; notice how the lines seem to snap into place, every time you get to a width that's evenly divisible by 15.

Secondly, why are you adding 1 in your increment statements? Those will also add up- you add one every iteration, so by the 15th iteration, you're 15 pixels off.

I would recommend using doubles for x, y, width, and height, and losing the +1s.
• May 23rd, 2013, 11:56 AM
brocode
Re: Drawing in JPanel not working as expected
I had thought I'd edited that code, those + 1's should not be there. I have taken them out in my code, I guess I uploaded the wrong code in which I was testing if rounding up might fix the offset.
I'll try the doubles, however when I tried float I got an error stating that drawLine requires 4 int values.
• May 23rd, 2013, 11:58 AM
KevinWorkman
Re: Drawing in JPanel not working as expected
Quote:

Originally Posted by brocode
I had thought I'd edited that code, those + 1's should not be there. I have taken them out in my code, I guess I uploaded the wrong code in which I was testing if rounding up might fix the offset.
I'll try the doubles, however when I tried float I got an error stating that drawLine requires 4 int values.

Yeah, that's expected. You'll have to cast them to ints before passing them into the method. Something like:

g.drawLine(0, 0, (int)x, (int)y);
• May 23rd, 2013, 12:04 PM
brocode
Re: Drawing in JPanel not working as expected
Surprise surprise, it compiled with the double values and the offset is less noticeable but it's still there. Now I'm beginning to think that in the book the example happens to show the lines from opposite corners connecting at their endpoints, but that they don't necessarily have to do so. Really, if I'm to do this using while loops and the knowledge obtained solely from the book, at this point I feel this is all that's required.
The only other thing I could think to do is do one corner, then the opposite corner immediately afterwards and simply assign the first endpoint values to those of the last endpoint from the opposite corner. I hope that explanation isn't too convoluted.
You mentioned I was drawing my lines in a strange order? What would you recommend instead, maybe it'll help make something click for me?
• May 23rd, 2013, 12:10 PM
brocode
Re: Drawing in JPanel not working as expected
Eureka! Thank you very much kind sir. Do you mind explaining to me however, why it works to cast a double as an int. Would that not still end up truncating the decimals, causing the slight offset?
P.S - What you said worked, the problem was that I compiled the right code but ran the wrong program, I have a file called DrawFan.java and DrawFans.java, it was confusing.
Attachment 2098
• May 23rd, 2013, 12:31 PM
KevinWorkman
Re: Drawing in JPanel not working as expected
Let me see if I can cobble something together. Take this code:

Code java:

```import java.awt.Dimension; import java.awt.Graphics; import javax.swing.JPanel; import javax.swing.JFrame;   public class DrawFans extends JPanel { public void paintComponent(Graphics g) { super.paintComponent( g );   double x; // variable representing a lines end point x co-ordinate double y; // same as x, but for the y co-ordinate int count; // counter-control variable int width = getWidth(); // total width int height = getHeight(); // total height   System.out.println(width);   // values set before each while loop represent the end point // for the first line to be drawn (first line always follows border)   int xDistanceBetweenLines = width/15; int yDistanceBetweenLines = height/15;   // top right corner x = width; y = height; count = 1; while(count <= 15) { g.drawLine(width, 0, (int)x, (int)y); x -= xDistanceBetweenLines; y -= yDistanceBetweenLines; ++count; } // end while   // bottom left corner, here is where the problem occurs x = 0; y = 0; count = 1; while(count <= 15) { g.drawLine(0, height, (int)x, (int)y); x += xDistanceBetweenLines; y += yDistanceBetweenLines; ++count; } // end while   } // end method paintComponent   public static void main(String [] args) { DrawFans fan = new DrawFans();   JFrame application = new JFrame();   application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   application.add(fan); fan.setPreferredSize(new Dimension(255, 250)); application.pack(); application.setVisible(true); } // end method main } // end class DrawFans```

...which is just the two corners of your code, with the increment value saved to a variable instead of recalculated each time, since it will always be the same.

Notice that when drawing the fan from the upper-right corner, you start with the line that ends in the bottom-right corner. Also notice that it appears to be closer to the bottom-right than the last line in that fan appears to be to the top-left. This is because your increment value is just a little too small because of integer truncation; after 15 increments that difference adds up and your lines are way off, which is why the last line is further away from the top-left corner than the first line is away from the bottom-right. I hope that makes sense (especially after running this code), but I can post pictures if it doesn't.

Now notice that the fan you're drawing from the bottom-left starts with the line closest to the top-left. That line should match up with the last line from the other fan, but that line is offset, as I explained above. That's what I meant when I said the order you're drawing your lines hides what's happening. If we switch the order so you draw the lines of each fan in the same order:

Code java:

```import java.awt.Dimension; import java.awt.Graphics; import javax.swing.JPanel; import javax.swing.JFrame;   public class DrawFans extends JPanel { public void paintComponent(Graphics g) { super.paintComponent( g );   double x; // variable representing a lines end point x co-ordinate double y; // same as x, but for the y co-ordinate int count; // counter-control variable int width = getWidth(); // total width int height = getHeight(); // total height   System.out.println(width);   // values set before each while loop represent the end point // for the first line to be drawn (first line always follows border)   int xDistanceBetweenLines = width/15; int yDistanceBetweenLines = height/15;   // top right corner x = width; y = height; count = 1; while(count <= 15) { g.drawLine(width, 0, (int)x, (int)y); x -= xDistanceBetweenLines; y -= yDistanceBetweenLines; ++count; } // end while   // bottom left corner, here is where the problem occurs x = width; y = height; count = 1; while(count <= 15) { g.drawLine(0, height, (int)x, (int)y); x -= xDistanceBetweenLines; y -= yDistanceBetweenLines; ++count; } // end while   } // end method paintComponent   public static void main(String [] args) { DrawFans fan = new DrawFans();   JFrame application = new JFrame();   application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   application.add(fan); fan.setPreferredSize(new Dimension(255, 250)); application.pack(); application.setVisible(true); } // end method main } // end class DrawFans```

...you should see that the lines match up now, only because the accumulated offset is the same in each matching line. If you resize your window, you'll notice the lines still snapping when you hit a width or height that's divisible by 15. To fix that, your increment value needs to be a double, not an int:

Code java:

```double xDistanceBetweenLines = width/15.0; double yDistanceBetweenLines = height/15.0;```

And voila, your lines match up perfectly. Note that it's not necessary to change the order you draw your lines once you have the increment working correctly, I just did that to show what's going on.

Does that make any more sense? If you run each of the programs I posted above and change the size, you should see what I'm talking about.

--- Update ---

Quote:

Originally Posted by brocode
Eureka! Thank you very much kind sir. Do you mind explaining to me however, why it works to cast a double as an int. Would that not still end up truncating the decimals, causing the slight offset?

If we add some print statements to the modified version of the program I posted above:

Code java:

```double xDistanceBetweenLines = width/15.0; double yDistanceBetweenLines = height/15.0;   System.out.println("xd: " + xDistanceBetweenLines); System.out.println("yd: " + yDistanceBetweenLines);```

You'll see that the increments are generally not whole numbers.

Now let's say the xDistance is 5.5. As you draw more lines, they should be further away from the original point, right? So starting at 0, the x value of your points should be 0, 5.5, 11, 16.5, 22.... all the way up to 77 for the 15th line (14*5.5 = 77). Rounding those numbers doesn't really matter, since there's no such thing as half a pixel.

However, if you truncate the 5.5 right away (as you were doing with your integer division), the x values of your points will be 0, 5, 10, 15, 10... all the way up to 70 for the 15th line (14 * 5 = 70). That last line will be 7 pixels off from the value you actually want. And that's only in this example; with bigger windows your offset will be much larger, which is why it's so noticeable.

So truncating at the beginning is so bad because you're using that truncated value in an increment, so each increment gets you further away from the value you actually want. Truncating after using the real value doesn't really hurt you, since you can't be precise to half a pixel anyway.

Edit: Sorry, there's a bug in the forums that causes this nonsense to happen when you post twice in a row.
• May 23rd, 2013, 12:44 PM
brocode
Re: Drawing in JPanel not working as expected
Most of what you said made sense to me, except I still don't quite understand the whole casting situation.

So, I'm receiving for example height / 15 as an endpoint, let's say it equates to 5.5. When I'm passing that to drawLine cast as an int, is it not still going to obtain the value, "5"? Or does it first....oh wait...I see now. No matter what, when I cast as an int, it's going to receive a truncated value, but those truncated values will have a consistent difference because when I originally calculated them, they retained their decimal values so the addition was more accurate....yeah, sorry I just rambled. I get what you're saying now, may not have worded it the best way but I see it.
Thank you again, you've been very helpful.