Draw a Triangle on the Screen, Part I
This function draws a triangle on the screen. It becomes the core of an applet that allows the user to pick the three endpoints by clicking three times on the screen, which will be completed in the next example.
The algorithm assumes that the three points are ordered by x coordinate. It fills the triangle by drawing a series of vertical lines, 1 pixel wide. To do this, it splits the triangle into a "left" and "right" half; that is, the part from the x coordinate of the first point to the x coordinate of the second point, and the part from the x coordinate of the second point to the x coordinate of the third point. This algorithm won't work well with triangles that are extremely tall and thin, so to cover those cases, the function also draws a line between each pair of endpoints.
Applets draw to the screen by overriding a member method called paint(). This method is passed a Graphics class, which supports two methods that are used here: fillOval() (used to draw a circle) and drawLine(). The meaning of the parameters can be inferred from their use (assume that they are passed in the correct order).
In the declaration of the Triangle class, it specifies that it implements the MouseListener interface. This is explained in the next program.
Source Code
1. import java.awt.event.*; 2. import java.awt.*; 3. 4. public class Triangle extends java.applet.Applet 5. implements MouseListener { 6. 7. // The rest of the applet will be in the next 8. // example. 9. 10. Point[] pt = new Point[3]; 11. int ptCount = 0; 12. 13. public void paint(Graphics g) { 14. 15. int i; 16. 17. // Draw the points that have been selected 18. 19. for (i = 0; i < ptCount; i++) { 20. g.fillOval(pt[i].x - 10, pt[i].y - 10, 21. 20, 20); 22. } 23. 24. if (ptCount == 3) { 25. 26. // Connect the endpoints to handle 27. // tall thin triangles. 28. 29. g.drawLine(pt[0].x, pt[0].y, 30. pt[1].x, pt[1].y); 31. g.drawLine(pt[1].x, pt[1].y, 32. pt[2].x, pt[2].y); 33. g.drawLine(pt[0].x, pt[0].y, 34. pt[2].x, pt[2].y); 35. 36. // Calculate x and y diffs between points. 37. 38. int x0to1 = pt[1].x - pt[0].x; 39. int x0to2 = pt[2].x - pt[0].x; 40. int x1to2 = pt[2].x - pt[1].x; 41. int y0to1 = pt[1].y - pt[0].y; 42. int y0to2 = pt[2].y - pt[0].y; 43. int y1to2 = pt[2].y - pt[1].y; 44. 45. // Left part of the triangle. 46. 47. if (x0to1 > 0) { 48. for (i = pt[0].x; i <= pt[1].x; i++) { 49. g.drawLine( 50. i, 51. pt[0].y + 52. ((y0to1 * (i - pt[0].x)) / x0to1), 53. i, 54. pt[0].y + 55. ((y0to2 * (i - pt[0].x)) / x0to2) 56. ); 57. } 58. } 59. 60. // Right part of the triangle. 61. 62. for (i = pt[1].x+1; i <= pt[2].x; i++) { 63. g.drawLine( 64. i, 65. pt[1].y + 66. ((y1to2 * (i - pt[1].x)) / x1to2), 67. i, 68. pt[1].y + 69. ((y0to2 * (i - pt[0].x)) / x0to2) 70. ); 71. } 72. } 73. } 74. }
Suggestions
There are several places with repetitive statements, such as lines 2934 and 3843. Check these lines carefully to ensure that they are correct.
Although the points are ordered by x coordinate, it's possible that two or three of them will have the same x coordinate. As a result, x0to1, x0to2, or x1to2 could be 0. Examine the code to ensure that the division operations on lines 52, 55, 66, and 69 would never result in an ArithmeticException due to divide by zero.
Look at the loops on lines 4857 and lines 6271. Determine what values will be passed to g.drawLine() on the first and last iteration of each of these loops to make sure that they seem reasonable. Remember how the values are related; for example, the expression pt[0].y + y0to1 is equal to pt[1].y.
Hints
Walk through the function with ptCount == 3 and the points as follows:
- A triangle with nothing unusual:
pt[0].x == 0; pt[0].y == 20; pt[1].x == 2; pt[1].y == 18; pt[2].x == 4; pt[2].y == 28;
- A triangle with points that are the same in the x or y
coordinate:
pt[0].x == 0; pt[0].y == 10; pt[1].x == 4; pt[1].y == 10; pt[2].x == 4; pt[2].y == 0;
Explanation of the Bug
A B.variable error exists in the calculation of the second y coordinate in the call to g.drawLine() in the second loop. Lines 6869, which read as follows
pt[1].y + ((y0to2 * (i - pt[0].x)) / x0to2)
should be
pt[0].y + ((y0to2 * (i - pt[0].x)) / x0to2)
The problem can be spotted by considering the last iteration of the loop, when i is equal to pt[2].x. In this situation, the expression as initially written becomes
pt[1].y + ((y0to2 * (pt[2].x - pt[0].x)) / x0to2)
which, because pt[2].x pt[0].x is equal to x0to2, becomes
pt[1].y + y0to2
This does not make any particular sense because pt[1].y and y0to2 are not related. With the fix, the expression is instead
pt[0].y + y0to2
This equals pt[2].y, a reasonable y coordinate for the second endpoint of the last vertical line (in fact, the y coordinate of the first endpoint of the line also evaluates to pt[2].y, so the "line" is actually just a single pixel drawn at the point pt[2]).