1. What is Rubber Banding in Drawing?
Rubber Banding is a technique to track the drawing while user is dragging the mouse. Say, for example, you have a rubber band cut from its circular shape as a line. Hold one end of a rubber band between left thumb and left index finger and hold other end between thumb and index finger of right hand. Now, keeping your left-hand stand still, move the other end of the rubber band on your right hand. You can see a line stretching. While drawing this aid the user to see how their drawing primitive (Say Line, Rectangle, circle) looks while they still drag the mouse.
In this example, we will use this rubber banding technique to draw line and rectangle. The user should first place a check mark on the rubber band checkbox and then pick either line or rectangle as drawing mode. Now, we will proceed with the example.
2. Use Mouse Drag to Track Drawing
In the mouseDragged
handler, we set the color of the Graphics
object to light Gray. Then, we set the second AWT Point
instance with the value which is coming as part of the MouseEvent
argument. Then, we call
paint method to connect the First Point which was stored during the mouse press and the second point which we retrieved here.
1 2 3 4 5 6 7 8 9 |
//Sample 15: Draw Line Rubber-Banding if(DrawMode.compareTo("LineD") == 0 && chkDragMode.getState() == true) { Graphics gr = getGraphics(); gr.setColor(Color.LIGHT_GRAY); SecondPoint.setLocation(e.getX(), e.getY()); paint(gr); } |
3. Rubber Banding Issue
Now, when we draw the line with Rubber-banding turned on, we get multiple lines. Here, the lines are connecting the fixed start point with the variable number of endpoints which is encountered as part of the current mouse position. The below picture shows how it looks like:
If we try the rubber stretch for the rectangle, the same issue will exist. To avoid this, we should erase the previous line. If it is a rectangle, we must erase the previous rectangle.
4. Resolve Rubber Banding Issue – setXORMode
4.1 The setXORMode API
Java Graphics API function setXORMode will help in resolving the issue for us. Once we set this mode, the drawing will invert the pixels between the background and foreground color. For Example, let us say the background is in white and foreground drawing is in Gray color. When we draw a line between P1 and P2 for the first time, the line will be drawn in gray color. It is because the pixels in the straight path between P1 & P2 are in white. Now, if we draw the line again between P1 and P2, the pixels in the path are in Gray color. The AWT setXORMode will invert the pixels, or we can say it draws the line in white color there by erasing the Gray color line which already exists on the path P1-P2.
4.2 Rubber Banding of Line
Now our Line drawing for Rubber-Banding goes like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
//Sample 15: Draw Line Rubber-Banding if(DrawMode.compareTo("LineD") == 0 && chkDragMode.getState() == true) { Graphics gr = getGraphics(); gr.setColor(Color.LIGHT_GRAY); //Sample 16: Resolve Rubber Banding Issue => if (SecondPoint.x != 0 && SecondPoint.y != 0) { gr.setXORMode(getBackground()); gr.drawLine( FirstPoint.x, FirstPoint.y, SecondPoint.x, SecondPoint.y); } //Sample 16 <= SecondPoint.setLocation(e.getX(), e.getY()); paint(gr); } |
In the above code, first we set the XOR Mode via setXORMode function call. Then we draw two lines. First line connects Fixed P1 with the P2 of the previous drag event. So it erases the gray line drawn as part of previous drag event. The call to paint method draws the next line between P1 and P2. Here, P1 is fixed and P2 is from the current drag location. Because of XOR mode, the second line will show in gray color as it has the underlying pixels in white color. Note: Once user releases the mouse button, we will draw the line in black color.
4.3 Rubber Banding of Rectangle
The technique is same for the rectangle drawing as well. In the below picture, P1, P2 denotes the finalized position for the drawing the Rectangle. The point P2a and P2b are reported by AWT Event system while dragging the mouse. The user can see how the rectangle looks when they drag the mouse. This is the usage of performing rubber banding while drawing shapes like Line, rectangle, Circle, etc.
Code for the rectangle rubber banding is below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//Sample 17: Draw Rect Rubber-Banding if(DrawMode.compareTo("RectD") == 0 && chkDragMode.getState() == true) { Graphics gr = getGraphics(); gr.setColor(Color.LIGHT_GRAY); if (SecondPoint.x != 0 && SecondPoint.y != 0) { gr.setXORMode(getBackground()); Point TopLeft = GetTopLeft(); int width = Math.abs(SecondPoint.x - FirstPoint.x); int height = Math.abs(SecondPoint.y - FirstPoint.y); gr.drawRect(TopLeft.x, TopLeft.y, width, height); } SecondPoint.setLocation(e.getX(), e.getY()); paint(gr); } |
5. AWT Drawing Part 5: Rubber-Banding – Youtube Video
6. Complete Coding from All 5 Parts
FrameWin.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
package AwtDemoPkg; import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.Color; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Label; import java.awt.Panel; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; //Sample 01: Implement the Listeneres public class FrameWin extends Frame implements WindowListener, ActionListener, MouseListener, MouseMotionListener { //Sample 02: Member Variables String DrawMode = ""; Point FirstPoint = new Point(0,0); Point SecondPoint = new Point(0,0); Label lblModeDisplay; Checkbox chkDragMode; public FrameWin(String FrameTitle) { //Display the Frame Window super(FrameTitle); setSize(600, 300); setLocation(100,100); addWindowListener(this); //Sample 03: Prepare Command Panel Panel CommandPanel = new Panel(new GridLayout(6,1)); CommandPanel.setBackground(new Color(240, 240, 240)); //3.1 Prepare Button & Checkbox Button btnDrawLine = new Button("Draw Line"); Button btnDrawRect = new Button("Draw Rect"); Button btnFreeHand = new Button("Free Hand"); btnDrawLine.setActionCommand("LineD"); btnDrawRect.setActionCommand("RectD"); btnFreeHand.setActionCommand("FreeH"); btnDrawLine.addActionListener(this); btnDrawRect.addActionListener(this); btnFreeHand.addActionListener(this); chkDragMode = new Checkbox("Rubber Band"); //3.2 Prepare Labels Label lblMode = new Label("Mode"); lblModeDisplay = new Label(); Font f1 = new Font("Verdana", Font.PLAIN, 20); lblMode.setFont(f1); lblModeDisplay.setFont(f1); lblModeDisplay.setForeground(Color.BLUE); //3.3 Pack the Panel & give to Frame CommandPanel.add(btnDrawLine); CommandPanel.add(btnDrawRect); CommandPanel.add(btnFreeHand); CommandPanel.add(chkDragMode); CommandPanel.add(lblMode); CommandPanel.add(lblModeDisplay); add(BorderLayout.EAST, CommandPanel); //Sample 07: Register with Mouse Listener addMouseListener(this); //Sample 12: Register Mouse Motion Listener addMouseMotionListener(this); } public void windowOpened(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowActivated(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} public void windowClosing(WindowEvent e) { this.dispose(); } public void mouseDragged(MouseEvent e) { //Sample 14: Perform Free Hand Drawing if (DrawMode.compareTo("FreeH") == 0) { if (SecondPoint.x != 0 && SecondPoint.y != 0) { FirstPoint.x = SecondPoint.x; FirstPoint.y = SecondPoint.y; } SecondPoint.setLocation(e.getX(), e.getY()); paint(getGraphics()); } //Sample 15: Draw Line Rubber-Banding if(DrawMode.compareTo("LineD") == 0 && chkDragMode.getState() == true) { Graphics gr = getGraphics(); gr.setColor(Color.LIGHT_GRAY); //Sample 16: Resolve Rubber Banding Issue => if (SecondPoint.x != 0 && SecondPoint.y != 0) { gr.setXORMode(getBackground()); gr.drawLine( FirstPoint.x, FirstPoint.y, SecondPoint.x, SecondPoint.y); } //Sample 16 <= SecondPoint.setLocation(e.getX(), e.getY()); paint(gr); } //Sample 17: Draw Rect Rubber-Banding if(DrawMode.compareTo("RectD") == 0 && chkDragMode.getState() == true) { Graphics gr = getGraphics(); gr.setColor(Color.LIGHT_GRAY); if (SecondPoint.x != 0 && SecondPoint.y != 0) { gr.setXORMode(getBackground()); Point TopLeft = GetTopLeft(); int width = Math.abs(SecondPoint.x - FirstPoint.x); int height = Math.abs(SecondPoint.y - FirstPoint.y); gr.drawRect(TopLeft.x, TopLeft.y, width, height); } SecondPoint.setLocation(e.getX(), e.getY()); paint(gr); } } public void mouseMoved(MouseEvent e) { } public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) { //Sample 05: Set First Point FirstPoint.setLocation(0, 0); SecondPoint.setLocation(0, 0); FirstPoint.setLocation(e.getX(), e.getY()); } public void mouseReleased(MouseEvent e) { //Sample 06: Set Second Point //Sample 13: Introduce the Condition if (DrawMode.compareTo("FreeH") != 0) { SecondPoint.setLocation(e.getX(), e.getY()); repaint(); } } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void actionPerformed(ActionEvent e) { //Sample 04: Set the Draw Mode DrawMode = e.getActionCommand(); lblModeDisplay.setText(DrawMode); FirstPoint.setLocation(0, 0); SecondPoint.setLocation(0, 0); } public void paint(Graphics g) { if (FirstPoint.equals(SecondPoint)) return; switch(DrawMode) { case "LineD": //Sample 08: Draw Line g.drawLine( FirstPoint.x, FirstPoint.y, SecondPoint.x, SecondPoint.y); break; case "RectD": //Sample 10: Draw Rectangle Point TopLeft = GetTopLeft(); int width = Math.abs(SecondPoint.x - FirstPoint.x); int height = Math.abs(SecondPoint.y - FirstPoint.y); g.drawRect(TopLeft.x, TopLeft.y, width, height); break; case "FreeH": //Sample 11: Free Hand Drawing g.drawLine( FirstPoint.x, FirstPoint.y, SecondPoint.x, SecondPoint.y); break; } } //Sample 09: Relocate Rectangle Start Point private Point GetTopLeft() { Point TopLeft = new Point(); TopLeft.x = Math.min(FirstPoint.x,SecondPoint.x ); TopLeft.y = Math.min(FirstPoint.y,SecondPoint.y ); return TopLeft; } } |
Categories: AWT
Tags: AWT Drawing, AWT paint, Rubber-Banding, setXORMode