Extension 1 for this module:
For this extension, modify your Eyeball so that the pupil motion does not immediately move to look at the mouse cursor, but instead gradually moves toward the mouse cursor for a more creepy effect. In other words, if you move the cursor quickly from point A to point B, the eyeballs will slowly move to look at point B, stopping very briefly to look at points along the imaginary
line between A and B. More specifically, each eyeball will have a point that it is currently looking at, and will also have a point that it is moving towards. Whenever the mouse moves, the point that the eyeball is moving towards will change.
To accomplish this task, you will need to use a timer from the java swing package. Each eyeball will listen to its timer, and shift its focus gradually toward point B at each tick. (If it's already looking at point B, then it will ignore the timer.)
To get started, you can declare an instance variable of type Timer, and initialize it in your Eyeball constructor by calling the Timer constructor that takes two parameters. The first parameter is the number of milliseconds between ticks of the timer. (50 milliseconds per tick would provide about 20 animation frames per second.) The second parameter is the listener, which will be this so that the Eyeball will be notified of each tick of the timer.
Be sure to .start() the timer so it will tick and call your
listener. Take a look at the lab0 Robot code to see how this is done.
Notice that the Eyeball class is already declared to be of type ActionListener. This implies that it has an actionPerformed method. Since you have added the eyeball as a listener to the timer, at each "tick" of the timer, the eyeball's actionPerformed method will be called. Fill in the actionPerformed method body to move the pupil appropriately at each tick. It's important that you not move the pupil all at once to look at point B, but instead you will just shift the pupil's focus a little bit toward point B each time the actionPerformed method is called. You don't need to do anything with the parameter that is passed to the actionPerformed method.
For an example use of a timer for animating motion, you can look in the Robot.java class of the lab0 package. However, be aware that the Robot is more complicated due to the painting of the line that trails behind it, and the fact that there are two types of robot motion: moving forward and turning.
End of extension 1
Extension 2 for this module:
A Cloning Tool
The purpose of a cloning tool is to "paint" a section of an image onto another area of the same image or a different image.
Implement a cloning tool that works as follows.
For example, suppose the user loads a family photograph into NIP and
selects your cloning tool. If they release the mouse on someone's
nose, and then beging dragging slowly in the area around someone
else's nose, they will copy the first person's face on top of the
second person's face.
- The user selects the cloning tool from the list of buttons at the left edge of the NIP window.
- The user clicks on a pixel in the image. (When the user releases the mouse button, your tool will remember which pixel location this was.) We will call this pixel the source location.
- Then the user will move the mouse somewhere else, press down the mouse button at the destination location, and begin dragging the mouse around in the area around that destination. As they do so, the pixels in the area around the source location will be copied to where the mouse is being dragged. Copying happens continuously. That is, as long as the user holds down the mouse button, copying occurs at every pixel location they drag to. All copying is done relative to the source location and the destination location.
For example, if they drag to the area three pixels above the destination location, then the cloning tool will copy to that location the pixel that is three pixels above the source location. (You will use your Vector and Point classes from Lab 4 to figure out which pixels should be copied to the current mouse position.)
Take the following details into account
as you design and implement your cloning tool.
Copying within an image:
Users would find it tedious to have to drag the mouse over every
single pixel in the destination region. Therefore, your cloning tool
should copy a small rectangular area of image pixels each time the mouse
is dragged. (This will give the user the feeling that they are using
a "brush.") To learn how to do this, read the NIP documentation
about Image objects, and
regions of an image.
Getting the image from a panel: The mouse event methods have
as their parameter a MouseEvent. To access the panel on which the event occurred,
use the MouseEvent's getSource() method and cast the resulting object to
the GraphicsPanel type. However, your cloning tool will need to work with the image
contained within that panel. If gp is a GraphicsPanel
object, then gp.getMainImage() will return the Image object
from that GraphicsPanel.
- Read the above specification and background carefully. Then open the CloningTool.java file in the lab5 package in Eclipse.
- Think about what information your cloning tool will need to use, and create instance variables to hold that information. You might declare two Point variables to hold the sourceLocation and the destinationLocation, and also declare a Vector variable to hold the difference between the source and destination locations.
- Write a constructor that initializes the source location to (0,0), just in case the user starts dragging before they ever release the mouse button to set a source location.
- Implement the mouseReleased, mousePressed, and mouseDragged methods to satisfy the above specification. Use a "brush size" of 10x10 pixels. (For some recommendations on how to implement these methods, see the hints below.)
- Test your program. To run it, right click on the "Lab5.java" file and select "Run -> Java Application." Does the cloning tool work as specified? (Probably not the first try.) Try to diagnose problems based on the information available. If you get a "NullPointerException," this usually means that a variable was not initialized. In such cases, always look at the stack trace on the console and find the line in the file where it occured, and check that all the variables used on that line were properly initialized.
- If you move the brush so that you would be copying pixels from an area outside the image, you will get an "out of bounds" error. Try modifying your program to paint only when all of the source pixels are inside the image. Or, for a more difficult but more satisfying implementation, paint with only the portion of the brush that corresponds to pixels being read from within the image.
- Once your cloning tool is working, let's allow the user to adjust the brush size. To do so, declare an instance variable called brushSize of type int. In your mouseDragged method (where you do the copying), you are probably using a constant value of 10 as both the width and height of the region to be copied. Instead, use the value of the brushSize variable as the width and height, so the brush will be sized according to that variable.
- Add a mutator method for the brush size. This method should use NIP's getInteger method in the Dialog class to prompt the user for a new brush size, and then set brushSize accordingly. Don't forget to add an option to call this method in the Methods menu!
- You may notice a strange multiple duplication effect if the source and destination images of the cloning are the same image and the brush is not far from the source. This is to be expected because you are recopying areas that you copied from the original location.
- Since we want you to start getting the feel for software
design, we have purposely not provided detailed instructions.
The idea is to first think about what you need to
do, and then use the provided documentation to accomplish that result.
If you are feeling lost, start by writing down, in English, what you
want to keep track of at each step. For example, what do you need to
remember when the user first releases the mouse button? (Probably just the source location, as a point.) Then, what
needs to be remembered when the press the mouse button at the
destination? (Perhaps the destination location, as a point, and maybe an "offset" vector that is the difference of the source and destination.)
Finally, what should be done to the image each time they drag
the mouse? (Perhaps create a point with the current mouse position, and then compute where to copy pixels from by subtracting the "offset" vector from that point.)
- Problem solving usually involves three things: (1) realizing what
information you know already, (2) understanding what you need to do or
find out, and then (3) finding a good way to bridge that gap.
Becoming familiar with the documentation for provided methods can help
you find ways to bridge the gap. (Pay particular attention to the
documentation mentioned in the "background" section above.) For
example, once you have calculated which pixels should be copied, you
will want to read about the provided copy methods of the Image class
to learn how to actually do the copying.
- Feel free to ask for help if you need it, but you'll learn more
(and it will be more gratifying) if you figure it out yourself.
Expect that it may take an hour or two to figure out what you need to do, even before you start implementing it, so don't just
rush to get help right away. Spend some time thinking about it,
like you would any puzzle.
End of extension 2