18.
Selecting Objects
So far we are able to create shapes by calling the MyShape constructor. We store the shapes in an array of shapes (which form a MyGroup objects we called “group”) and then we draw them on the screen.
//*****************************************
public void init(){
for(int i=0; i<numShapes; i++){
for(int j=0; j<numShapes; j++){
shape[i*numShapes+j] = new MyShape(5,9.,i*20.,
j*20.);
}
}
group = new MyGroup(numShapes*numShapes, shape);
}
Since these shapes are stored in an array we should be able to select them. All we need is an x and y mouse point and the array of shapes. What we do is, to first find which are the closest points to the x,y mouse point and then find which shape do those points belong. Then, we could color the shape with a red color to visualize it picked.
This means that we need to create two more data members on each of the four classes we created (MyPoint, MySegment, MyShape, and MyGroup). These are
Color color = Color.blue;
boolean isSelected = false;
and we also need to create a method called select. So at the level of the MyPoint we get:
import java.awt.*;
public class MyPoint {
// members of class
double x, y;
// the coordinates
Color color = Color.blue; // default color
boolean isSelected = false; // is this point selected?
//********** Constructor
public MyPoint(double xin, double yin){
x = xin;
y = yin;
}
//********** Move
public void move(double xoff, double yoff){
x = x + xoff;
y = y + yoff;
}
//************* Select
public boolean select(double xpick, double ypick, double
tolerance){
if(Math.abs(x - xpick) <
tolerance &&
Math.abs(y - ypick) < tolerance
) {
isSelected = true;
return true;
}
else {
isSelected = false;
}
return false;
}
}
The select(..) method gets two coordinates xpick and ypick (coming from the mouseDown) and a tolerance of how far can a point be to be selected. So first we get the absolute distance of the xpick and ypick from the local x and y coordinates of the point and we check to see whether they are less than the tolerance value. If yes then we set the isSelected Boolean to true else false. Then we return to the system whether the point is selected or not.
On the level of MySegment we do almost the same thing:
import java.awt.*;
public class MySegment {
// members of class
MyPoint start = new MyPoint(0., 0.); // start point
MyPoint end = new
MyPoint(0., 0.); // end point
Color color = Color.blue; // default color
boolean isSelected = false; // is this segment selected?
//********** Constructor
public MySegment(MyPoint p1, MyPoint p2){
start.x = p1.x;
start.y = p1.y;
end.x = p2.x;
end.y = p2.y;
}
//********** Move
public void move(double xoff, double yoff){
start.move(xoff, yoff);
end.move(xoff, yoff);
}
//*********** draw
public void draw(Graphics g){
if(isSelected)
g.setColor(Color.red);
else
g.setColor(color);
g.drawLine((int)start.x, (int)start.y,
(int)end.x, (int)end.y);
}
//******** Select
public boolean select(double xpick,
double ypick, double tolerance){
if(start.select(xpick, ypick,
tolerance)==true ||
end.select(xpick, ypick,
tolerance)==true) {
isSelected = true;
return true;
}
else {
isSelected = false;
}
return false;
}
}
The select(..) method gets two coordinates xpick and ypick (coming from the mouseDown) and a tolerance value. It then transfers the process to the select(..) method of the two points start and end. If either one is yes then we set the isSelected Boolean to true else false. Then we return to the system whether the segment is selected or not.
In addition, here, in the draw(..) method we set the color to red if isSelected is true, else we leave it with the default color.
At the level of the Shape we do almost the same thing as for the segment:
import java.awt.*;
public class MyShape {
// members of class
MySegment[] segs;
// array of segments
int numSegments;
// number of segments
Color color =
Color.blue; // default color
boolean isSelected = false; // is this shape selected?
//********** Constructor
public MyShape(int numInputSegments, MySegment[]
inputSegments){
. . . . .
}
//*************** An alternative constructor
//*************** Creates
public MyShape(int numSides, double radius,
double xoff, double yoff){
. . . . .
}
//********** Move
public void move(double xoff, double yoff){
for(int i=0; i<numSegments; i++)
segs[i].move(xoff, yoff);
}
//*********** draw
public void draw(Graphics g){
for(int i=0; i<numSegments; i++)
segs[i].draw(g);
}
//******** Select
public boolean select(double xpick,
double ypick, double tolerance){
for(int i=0; i<numSegments; i++){
if(segs[i].select(xpick, ypick,
tolerance)==true){
isSelected = true;
for(int j=0; j<numSegments; j++)
segs[j].isSelected = true;
return true;
}
else {
isSelected = false;
}
}
return false;
}
}
The select(..) method gets two coordinates xpick and ypick (coming from the mouseDown) and a tolerance value. It then transfers the process to the select(..) method of the segments. If a segment returns true then we set the isSelected Boolean to true else false. If a segment found that means that the whole shape should be selected, therefore we loop for all the segments of that shape and set their isSelecetd value to true. Then we return to the system whether the shape is selected or not.
At the level of MyGroup it is almost the same. I omit it here but can be found in the code gridSimple.java.
Now in the main code we need to call the select(…) methods passing the x and y coordinates of the mouse to get back whether a shape was selected within that tolerance:
//*****************************************
public boolean mouseDown(Event evt, int x, int y){
xfirst = x; //
remember this point
yfirst = y;
// Pick a shape
for(int i=0; i<group.numShapes;
i++)
if(group.shapes[i].select((double)x, (double)y, 10.) == true)
System.out.println("Selected = " + i);
repaint();
return true;
}
All we do is to go through all the shapes of the group and call the select(..) method passing the x and y coordinates of the mouse and the tolerance (in this case 10.). This will go all the way down into the shape, segment and point classes and set the isSelected values to true.
When we draw anything selected will be drawn red otherwise blue.
If we want to move the selected shapes while dragging the mouse we need to write the following code:
//*****************************************
public boolean mouseDrag(Event evt, int x, int y){
int xoff = x - xfirst; // get
the offset
int yoff = y - yfirst;
for(int i=0; i<group.numShapes; i++)
if(group.shapes[i].isSelected)
group.shapes[i].move((double)xoff, (double)yoff);
xfirst = x; //remember this
point
yfirst = y;
repaint();
return true;
}
We go through all the shapes and if they are selected we move them. Here is the result:

19.
Debugging
Debugging refers to the process of finding mistakes (bugs) in the code. Mistakes are mostly logical. The compiler will catch spelling and basic syntax mistakes but not logical mistakes. Logical mistakes refers to mistakes such as:
int x = new int[5];
for (int i=0; i< 6;
i++)
x[i] = 0.;
I allocated memory for 5 integers and I loop for 6. The compiler will not see it but when you run the program it will give you an exception mistake because it is trying to write beyond the array’s size.
Logical mistakes can become pretty complex, when the programs gets pretty complicated too. For example:
MyPoint start;
MyPoint end;
//********** Constructor
public MySegment(MyPoint p1, MyPoint p2){
start = p1;
end = p2;
}
The classes start and end were never initialized (no memory allocated for them). Therefore later on when we use them they will behave like arrays with the size of 1 causing troubles and headaches. The compiler will not catch it and when we run there will be no exception but the results we expect to see on the screen will be unexpected and weird.
To find bugs we usually need to print out the value of variables at the place where we suspect the problem may be. A statement of the type:
System.out.println("x = " + x);
will print the value of x.
Other times we put dummy print statement between blocks of the code to find whether a block of code is being reached. For example:
System.out.println("1111");
. . . .
System.out.println("2222");
May be useful. If 2222 is not printed in the output stream
that means that the code is crashing before 2222.
In our code so far it may
be useful to be able to printout all the classes we have created. A method such as:
//********** Print
public void print(){
System.out.println("x = " + x
+ " y = " + y);
}
will print out the values of x and y in the MyPoint class. That method can be put in a similar way in the MySegment class to print all the segments there:
//************
public void print(){
start.print();
end.print();
}
and so on.
Debugging is one of the most difficult tasks in code development mainly because bugs are hard to detect. Especially, when the code is not yours! Software companies dedicate almost twice the time they spend in producing a software in debugging it and making it bug-free (even though theoretically that is almost not possible). In the best scenario almost-bug-free software is referred to as beta. A no-bug software is referred to as release.
Experience, careful reading of the code, and asking around are other ways of debugging.
20.
Basic GUI (Graphic User Interface)
GUI (pronounced gou-ee) stands for Graphical User Interface and it refers to all the buttons, dialog boxes, menus, etc on the screen. These elements are supposed to convey information to the user about the status of the program and to accept use inputted information to the program. We will build here a set of buttons and have the user select whether s/he wants to move, rotate or scale the selected objects on the screen.
First we construct a class called control. In side that class we create three buttons. Here is the code:
import java.applet.*;
import java.awt.*;
public class MyControl {
public int MOVE = 0;
public int ROTATE = 1;
public int SCALE = 2;
public int status = MOVE;
Button bmove;
Button brotate;
Button bscale;
public MyControl(Applet applet){
bmove = new Button("Move");
brotate = new Button("Rotate");
bscale = new Button("Scale");
applet.add(bmove);
applet.add(brotate);
applet.add(bscale);
}
We define three variables MOVE, ROTATE, and SCALE and a variable status. Status is initially set to MOVE. We do this to make the code more readable and understandable (for those who do not know that 0 stands for MOVE.
Then we define three Button objects bmove, brotate, and bscale. In the constructor we create the buttons and add them to the applet. (The button class is documented in the java awt API).
This will create three button on the screen when we create the control class:
MyControl control = new MyControl(this);
It will look like this (but the buttons will be unresponsive):

Now we need to give life to the buttons. So far the mouse responds to the mouseDown and the mouseDrag. There is another java method called action(..) that allows GUI objects to return back information from the screen. So we add that method to the main code (simple):
//******************************************************************
public boolean action(Event evt, Object obj){
control.doButtons(evt, obj);
return true;
}
This means that the buttons will be handled in the control class through a method called doButtons (that is how we will call it).
So in the control class we add the method:
//******************************************************************
public void doButtons(Event e, Object o){
if(e.target instanceof Button){
if(e.target==bmove)
status = MOVE;
else if(e.target==brotate)
status = ROTATE;
else if(e.target==bscale)
status = SCALE;
}
System.out.println("status" + status);
}
this method get the Event object from the main code and uses the statement instanceof to find out what kind of GUI thing was clicked. If it is of a type Button the we set the status variable to MOVE, ROTATE, or SCALE and printout the value (for debugging purposes).
Now using the status variable, we can modify the main code to handle movement, rotation, or scale:
import
java.applet.*;
import
java.awt.*;
public class simple extends Applet{
MyPoint[] p = new MyPoint[8];
MySegment[] s = new MySegment[4];
int numShapes = 10; //num on side of grid of shapes
MyShape[] shape = new
MyShape[numShapes*numShapes];
MyGroup group;
double side = 10.;
MyControl control; // the GUI
int xfirst = 0; // the first point clicked
int yfirst = 0;
//*****************************************
public void init(){
for(int i=0; i<numShapes; i++){
for(int j=0; j<numShapes;
j++){
shape[i*numShapes+j] = new
MyShape(5,9.,i*20., j*20.);
}
}
group = new MyGroup(numShapes*numShapes,
shape);
control = new MyControl(this);
}
//*****************************************
public void paint(Graphics g){
group.draw(g);
}
//*****************************************
public boolean mouseDown(Event evt, int
x, int y){
xfirst = x; // remember this point
yfirst = y;
// Pick a shape
for(int i=0; i<group.numShapes;
i++)
if(group.shapes[i].select((double)x, (double)y, 10.) == true)
System.out.println("Selected = " + i);
repaint();
return true;
}
//*****************************************
public boolean mouseDrag(Event evt, int
x, int y){
int xoff = x - xfirst; // get the offset
int yoff = y - yfirst;
MyPoint ref = new MyPoint(x, y);
for(int i=0; i<group.numShapes;
i++)
if(group.shapes[i].isSelected){
if(control.status ==
control.MOVE)
group.shapes[i].move((double)xoff, (double)yoff);
if(control.status ==
control.ROTATE)
group.shapes[i].rotate((double)xoff, ref);
}
xfirst = x; //remember this point
yfirst = y;
repaint();
return true;
}
//******************************************************************
public boolean action(Event evt, Object
obj){
control.doButtons(evt, obj);
return true;
}
}
Selecting points/segments/shapes/groups
File import