An Introduction to

weaverchurchΛογισμικό & κατασκευή λογ/κού

15 Αυγ 2012 (πριν από 4 χρόνια και 10 μήνες)

487 εμφανίσεις

Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
1

of
10

Xiangyang Ju

15/03/13

Note
:


Please use local driver (either C driver or your own pen driver) to write your java
code. You
must copy

your code into H drive in the end of each lab session to keep
your code safe.


Please do not use H drive to write and compile your code. H driver is a remote one
which can cause problem when you try to compile your java code.


Aim (s):

The lecture looks
at how drawing colours on raster graphics and framebuffer and drawing with mouse
events.

Outcomes:

After the lecture, the students will be able to




Describe how drawing colours on raster graphics;



Describe how drawing colours on framebuffer;



Write
Java program to draw with mouse
.


In
week one you learned how to use
NetBeans
. This week you will build on what you have used and
add interaction with the mouse.


You are strongly advised to keep copies of all the code you do in the labs, for later use.
These notes
advise on how to do


Lab2
.1

USING A JPANEL ON TH
E JF
RAME
:

FIRSTMOUSE


We can

d
raw

directly onto a JFrame
. This is not ideal for several reasons, not least of which being
that we want to put buttons etc. on the form. The best thing for us is
to put a JPanel on the form and
use that for drawing.


Created a new JF
rame called FirstMouse and add
generated resize code by changing the

forms resize
policy as before. Create it in our usual project, but in a new package called
c
m3063.
Lab2
.

Right
clic
k JFrame at Inspector window and set the layout to
BorderLayout
.
A border layout lays out a
container, arranging and resizing its components to fit in five regions: north, south, east, west, and
center. Each region may contain no more than one component, a
nd is identified by a corresponding
constant: NORTH, SOUTH, EAST, WEST, and CENTER.



(d
etails

of Borderlayout

can be seen in
http://java.sun.com/javase/6/docs/api/
)


Go to

Form Editor
(Design)

window

for
FirstMouse

(if it is not open, click on Design button)
, c
lick
on the
Swing
Containers

and add two
JP
anels

to the frame, one in North the other in Center (
select
one of the panels and select its
Properties
tab
>
Direction,
to see where it is on the
frame
).
Resize the
top
(North)
one using
Other Properties >
Preferred Size
. Add a couple of
JB
uttons to the top panel
and set border

property

to
line border

for both panels so

you can see them. Set the layout of
northPanel to
FlowL
ayout
.
Change the
backgr
ound colour of both panels to show which is which.
Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
2

of
10

Xiangyang Ju

15/03/13

Running this you should get the form with 2 panels and 2 buttons.

Change the names of the new
panels to “northPanel” and “mainPanel” to help remember which is which.




But h
ow do you draw to it? Answer
,
you have to grab the graphics context of
the panel

when you
override
the paint
()

routine of the
JFrame
.

Note that in file FirstMouse.java, the jPanels have been
defined

by the system when you added them using the GUI
:


jPanel1 = new javax.swing.JPanel();


So, you can get their graphics context by using the getGraphics( ) method:



p
ublic void paint(Graphics g
) {


Graphics2D g2North = (Graphics2D) northPanel.getGraphics();


g2North.drawLine(0,0, 200,150);


Graphics2D g2Main = (Graphics2D) mainPanel.getGraphics();


g2Main.setColor(Color.green);


g2Main.drawLine(0,0, 200,150);

}



You can use Source > reformat code to
reformat your code.

Also use
Source >

fix imports to call

the
“fix imports” tool to save you importing things like Graphics2D yourself. [And it is no longer
considered good style to import using *.]


What you have done is to get the graphics context of each panel in turn and draw to t
hat panel. A
black line to
northPanel and a green one to mainPanel
.


Be careful to understand why the black line drew to the top panel and the green one to the bottom
panel. Add some code just before the closing } bracket to draw a red circle on the top
panel to show
you understand. This is very important!


So, w
hy do the buttons not draw? Because you have overwritten the paint method of the JFrame
which drew them! To bring them back add this line after the above code:



super.paint(g
);


g is th
e graphics context of the JFrame and super means the object (in this case the JFrame), so we are
here repainting the frame and the panels on it. Thus the buttons draw.
This however (probably)
overpaints the lines, so you cannot see them. You can move th
e
super.paint(g);

statement to the start
of the paint routine and it might then show the lines.


To avoid such difficulties,
we will create a separate JPanel, on which we can do all our drawing
without ambiguity and then place it on the JForm in the CENT
RE as above.

We can use
NetBeans

to
create a J
Frame
, but we hav
e to do the attaching ourselves, in the code.


Lab2
.2

ADDING

A PANEL TO A FRAME


C
reate a
JFrame

called MyFrame
.
The class name could even by MyJFrame; since it would be in a
separate package from the last such class (which was in
c
m3063.lab1
) there would be no conflict; but
MyFrame is shorter, now you are convinced you are working with Swing components.
As above, pu
t
a
panel along the top (NORTH) with a couple of
buttons on it.

Please MyFrame use BorderLayout for
Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
3

of
10

Xiangyang Ju

15/03/13

MyFrame and FlowLayout for the panel in MyFrame.

Do not put any paint or graphics code in it. Get
it to compile and run.

Now
, quite separate to the

JFrame
,

create a new JPanel called MyPanel again in
the usual way

(File > New File > Swing GUI Forms > JPanel Form)
.



We
are going to arrange for a MyPanel object to go on each MyFrame instance

i
n the CENTER
position, so, in MyFrame

add the code

(red lines)
:




public
MyFrame
() {


MyPanel panel = new MyPanel();


getContentPane().add(panel, java.awt.BorderLayout.CENTER);


initComponents();


}


This
simply
create
s

an instance of
MyPanel
called
panel

and add
s

it to the CENTER of

the frame
object in MyFrame. All this simply adds a panel, on which
we can draw
,

to the frame, whi
ch also
contains a button panel
.


You may wish to change the background colour of the
form and the panel,
so you can tell
the panel
is
on the form when you
run

Now add the
code
in
MyPanel

(
NOT in MyF
rame
)
to draw
on the panel
.
Note a JPanel uses paintComponent, not paint!!!




public voi
d paintComponent(Graphics g
){


Graphics2D g2 = (Graphics2D) g;


g2.setPaint(Color.red);


g2.drawLine(0,0, 500,500);


}


Note how the line from (0, 0)
now
starts at the top left of the panel and is not hidden at all by the edges
or border. You have drawn a line, but it is pre
-
programmed. What we want to do is to use the mouse
to dr
aw a line.



Question:

why has the background colour you set for the panel disappeared?


Question:

why do we need to make the panel separately and program it onto the form? What
happens if you try to put it onto the form directly using the form builder?


We are now going to use the mouse to choose the points that fix the line. So we become interested in
the

MouseClicked

event. To do this

Open

Form Editor [MyPanel
]

window, s
elect
Events

tab

>
MouseClicked

> RETURN
.


We want to pick up the coordinates o
f the mouseclick and pass them to the paint
Component

routine,
so we can draw a line to this point.

Now, evt is a MouseEvent (see MouseListener

> MouseEvent

under Java website) and it has methods:


getX( int ); getY
( int )

// (X, Y) coordinate of eve
nt position

getPoint( Point );



// Point p = (X, Y)


Since drawLine( ) uses integers not points, we use the former to get the point
where the mouse was
clicked
. Add the following in
:



private void formMouseClicked(java.awt.event.MouseEvent evt) {

Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
4

of
10

Xiangyang Ju

15/03/13



xMouse = evt.getX();


// add to mouse clicked routine


yMouse = evt.getY();



}

This picks up the mouse coordinates when it is clicked.




public void paintComponent(Graphics g){


Graphics2D g2;


g2 = (Graphics2D) g;


g2.setPaint(Color.red);



g2.drawLine(0, 0, 500, 500);

replace this line with the next

line


g2.drawLine(0, 0, xMouse, yMouse);

// draw in paintComponent

}


And add the following instance variable declaration

just before the constructor
:


p
rivate
int
xMouse, yMouse;


Sadly nothing happens

when you click the mouse
! Why? Simply because we never told the panel to
repaint itself!
This is an event driven system and will only redraw the picture when an event tells it to.
If you click the mouse, then resi
ze the frame, you will force a redraw


although it may not do what
you expect if you click and resize a few times.
To
make the code
force a redraw for every line, add
the following after the
yMouse =
evt.getY( );

line:



paintComponent(getGraphics());


// force panel to redraw itself


paintComponent needs a graphics context, which is the JPanel. This is found by asking for the context
of “this”, that is, the current
instance of MyPanel
. Adding this line mea
ns we get a line for each click.


Question
:

if
“click >

resize to force redraw


only shows the latest line to be drawn, why does this
method show all the lines?


What we really want is to be able to
start
the line where we press the mouse down (MousePressed)
and end it where we release the mouse
(MouseReleased), so add these two events to your form and
remove MouseClicked

(use the Events tab, find the event you want to delete and hit the …. bit to
open up the handlers window. Then simply select the remove button in the handler window).


Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
5

of
10

Xiangyang Ju

15/03/13

F
ig. 1
Add mouse
event
s


Now add code to draw the line:



private void formMouseReleased(java.awt.event.MouseEvent evt) {


xEnd = evt.getX();


// add to mouse released routine


yEnd = evt.getY();


paintComponent(getGraphics());




}



private

void formMousePressed(java.awt.event.MouseEvent evt) {


xStart = evt.getX();


// add to mouse pressed routine


yStart = evt.getY();



}




public void paintComponent(Graphics gPanel){


Graphics2D
g2
;


g2

= (Graphics2D) gPanel;


g2
.setPaint(Color.red);


g2
.drawLine(0, 0, xMouse, yMouse); // draw in paintComponent



g2
.drawLine(xStart, yStart, xEnd, yEnd);



}


And change the instance variable declaration to



private i
nt

xStart, yStart;


private int
xEnd, yEnd;



This works! B
ut there is a problem. You can’t see the line until you finish drawing it! Really you
want to see it as you move the mouse (“rubber banding” as it

s called). That is, you need to do
something with the mouse
movement. See MouseMotionListener on the web, to work out what to put
in


mouseDragged( ) seems a likely candidate. Can you adapt the code using this?


In actual fact, it

s not a great challenge as the code is the same as we had for mouse released. Thi
s
now draws a line to the mouse every time we move the mouse. However, this ends up with lines all
over the screen!













Fig. 2 Lines as the mouse is dragged


So, how do we both show the line as we drag the mouse and also not leave lots of lines on the screen?
Clearly we need to erase the line each time the mouse moves and redraw the new line. So how do you
Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
6

of
10

Xiangyang Ju

15/03/13

erase the line?

You cannot redraw the whole picture
without a lot of trouble. Also, you cannot redraw
the line in
background colour
to overwrite it because this will also overwrite any bits of the picture the
line goes across.


Theoretically you should be able to use the composite functions, as SRC_OUT rub
s out a line.
However,
tests have failed so far
to achieve this and there is an easier way anyway as follows. The
setXORMode( )

method is designed to do Basically it inverts the colour on the form to set the colour
you want. The whole secret however i
s that it sets the colour you want back to the colour of the form.
This means that:





g2
.setColor(Color.white);




g2
.setXORMode(Color.blue);


g2
.drawLine(xStart, yStart, xOld, yOld);


g2
.drawLine(xStart, yStart, xEnd, yEnd);


xOld = xEnd;


yOld = yEnd;


works as follows:


A line is drawn to the point (Old) and this will draw in blue, deleting the line which was drawn
the last time.

A line is then drawn to the point

(End), again in blue

Next time round the loop, (Old) is the previous value of (End), so it redraws the same line in
XOR mode, that is, it deletes it. The new line to (End) is then drawn.






Each time round the loop, the previous line is deleted and

the next one drawn.


Suppose however there is already a blue line on the page, will it not be erased? The answer is “no”,
because the first draw erases the common bit of the line so the second draw puts it back!



Fig. 3 Missing part of line at overlap


All you need to do when the line is in the right place is to draw it once more to fill in the gap.


Note
: the line “
g2
.setColor(Color.white);
” is to set the context colour to white, so that the XOR of
colours works properly. If you set it to red inst
ead, then (red) XOR (blue) = (
purple
), so strange things
happen!


Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
7

of
10

Xiangyang Ju

15/03/13

So,
the full, working system
boils down to:


On mouse press:

Store point (Start)

Put (Old) = (Start) to get correct first line

Set flag drawing = true to say we are drawing lines


On mouse d
rag:

Find point (End)

Force a redraw of the picture


On mouse release:

Find point (End)

Set flag mouseReleased = true to say we need the final line draw

Set flag drawing = false to say we are not drawing the lines any more

Force a redraw of the picture


In

the paintComponent routine:


If drawing is

true, i.e. we are doing the erase / draw cycle


Enter XORMode

drawLine from (Start) to (Old) point, to erase previous line

drawLine from (Start) to (End) point, to draw current line


Exit XORMode so other drawing is not upset


Copy point (End) to point (Old) so erase it on next cycle


End if




If mouseReleased is true want to draw final line


drawLine from (Start) to (End) point, to draw final line



set mouseRelease to false


End if.


The code to do this is as follows:



private void formMousePressed(java.awt.event.MouseEvent evt) {


xStart = evt.getX();


// store start point


yStart = evt.getY();


xOld = xStart;


yOld = yStart;


drawing = true;


}




private void formMouseDragged(java.awt.event.MouseEvent evt) {


xEnd = evt.getX();


// follow mouse as dragged


yEnd = evt.getY();




paintComponent(getGraphics());

// forc
e redraw to see effect


}




private void formMouseReleased(java.awt.event.MouseEvent evt) {


xEnd = evt.getX();


// final point


yEnd = evt.getY();


mouseRelease
d

= true;


// flag draw final line

Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
8

of
10

Xiangyang Ju

15/03/13


drawing = false;



//

stop doing the draw


paintComponent(getGraphics());

// force a redraw to see effect


}




public void paintComponent(Graphics gPanel){


Graphics2D
g2

= (Graphics2D) gPanel;


g2
.setStroke(new BasicStroke(8.0f));





if(drawing
) {


// ignore if not in drawing mode


g2
.setColor(Color.white);


g2
.setXORMode(Color.blue);

// go into the overwrite mode


g2
.drawLine(xStart, yStart, xOld, yOld); // undraw last


g2
.drawLine(xStart, yStart, xEnd, yEnd);

// draw new


g2
.setPaintMode();


// out of XOR overwrite mode


xOld = xEnd;




// store last end point


yOld = yEnd;


}




if(mouseRelease
d
) {


// final time
through


g2
.setColor(Color.blue);


g2
.drawLine(xStart, yStart, xEnd, yEnd);

// draw final


mouseRelease
d

= false;


}


}


You need to declare the instance variables:



private
int xStart = 0;


private
in
t yStart = 0;


private
int xEnd, yEnd;


private
int xOld = 0;


private
int yOld = 0;


private boolean drawing = false;


private boolean mouseRelease
d

= false;



U
sing the mouse to draw an ellipse


An ellipse is defined in Graphics2D by giving the top
left

point of its enclosing rectangle and its width
and height

(see Appendix 1 of Lab2)
:


Ellipse2D ellipse

= new Ellipse2D.Float(topX, topY, w, h);


However, there is no specific routine to draw an ell
ipse, instead you use:


g2
.draw(e1);


// outlined ellipse

g2
.fill(e1);


// solid ellipse


Comment out the calls to
drawLine
(you will need them later) and add equivalent calls to draw the
ellipse. You should now be able to drag
and draw
an ellipse. Note

that width

w

and height
h
must be
positive, so you might want to use the Math “abs” func
tion so things draw even if the values generated
by the mouse

are negative
:



ellipse = new Ellipse2D.Float(xStart, yStart,

Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
9

of
10

Xiangyang Ju

15/03/13


Math.abs(xOl
d
-

xStart), Math.abs(yOld
-

yStart));


g2
.draw(ellipse);


To actually follow the mouse when w or h is negative, you must do some arit
hme
tic.


Lab2
.3

USING A BUFFERED IMA
GE


There is one problem with the program as it stands. If you cover it ov
e
r and uncover it, you loose all
your
drawing. This is because the drawing is stored in the screens frame buffer and is overwritten
when you cover the window. Since the
paintComponent

routine only draws what the mouse tells it to,
it has not stored your previous drawing either.


The solution is to draw to a separate frame buffer, or picture, which can be copied to the screen as
required. This is done using a
BufferedImage

object.


M
ak
e copies of
MyFrame

and
MyPanel

in
Lab2

and rename them
BufferDrawFrame

and
BufferDrawPanel

and get it to run.

Be sure you that you
are not loading
MyPanel

by mistake!


Now, put in the buffered image:



public void paintComponent(Graphics gPanel) {


if (buffer == null) {


Dimension d = getSize();


buffer = new BufferedImage(d.width, d.height,


BufferedImage.TYPE_INT_ARGB_PRE);




g2Buffer = (Graphics2D) buffer.getGraphics();


g2Buffer.setColor(Color.white);


g2Buffer.fillRect(0, 0, d.width, d.height);


// This time we'll draw with a broad pen


g2Buffer.setStroke(new BasicStroke(8.0f));


}


Ellipse2D ellipse;


if
(drawing) {


g2Buffer.setColor(Color.white);


g2Buffer.setXORMode(Color.red); // into the overwrite mode


ellipse = new Ellipse2D.Float(xStart, yStart,


Math.abs(xOld
-

xStart), Math.abs(yOld
-

yStart)
);


g2Buffer.draw(ellipse);


ellipse = new Ellipse2D.Float(xStart, yStart,


Math.abs(xEnd
-

xStart), Math.abs(yEnd
-

yStart));


g2Buffer.draw(ellipse);


g2Buffer.setPaintMode();


// out of XOR
overwrite mode


xOld = xEnd;




// store last end point


yOld = yEnd;


}


if (mouseReleased) {


g2Buffer.setColor(Color.red);


ellipse = new Ellipse2D.Float(xStart, yStart,


Math.abs(xEnd
-

xStart), Math.abs(yEnd
-

yStart));


g2Buffer.draw(ellipse);


mouseReleased = false;


}


gPanel.drawImage(buffer, 0, 0, this);

Interactive 2D graphics

Lab 2

Interacting with the mouse


weaverchurch_74d1ab85
-
a11d
-
4141
-
9374
-
54fad1b1081d.docx


Page
10

of
10

Xiangyang Ju

15/03/13


}


// Variables declaration
-

do not modify



// End of variables declaration

}


You need to declare the instance variables and fix imports:



private BufferedImage buffer;


private Graphics2D g2Buffer;

The crux of this is the last line, where the stored
image is copied to the screen image. Since the

Buffer
is not affected when you cover the window, the picture now survives
in the buffer
and is
redrawn to
the panel
when the window is uncovered. The TYPE constant in the new statement says what type of
ima
ge we are using.

If you look up BufferedImage on the web page, you will see many different
formats.



F
ig. 4 The final ellipse drawing system


Exercise:



T
ry varying the line thickness and colour. Change the ellipse to a rectangle and to a round
recta
ngle. Try making them filled. The really enterprising might wish to play with
QuadCurve
2D or even CubicCurve2D as well.

Find

the methods in the Java2D API.



Instead of rubber banding with the mouse, draw a shape with the mouse and make the shape
follow th
e mouse round the screen.