# Stanford CS106A Assignment 2 Program Hierarchy Solution

The Program Hierarchy problem looks pretty intimidating at first glance, but it was actually fun once I got started. Here is the problem:

Here is my solution, which you can also find on gist:

The Programming Hierarchy Problem actually requires a little bit of basic math. I found it super helpful to draw out rectangles on a piece of white paper to figure out what I was doing. The part I had the most problem with was centering the words in each box. I got help from a friend, who suggested I think of a rectangle drawn around the words, so it’s just another rectangle I’m trying to center.

```&lt;pre&gt;/*
* File: ProgramHierarchy.java
* Name:
* Section Leader:
* ---------------------------
* This file is the starter file for the ProgramHierarchy problem.
*/

import acm.graphics.*;
import acm.program.*;
import java.awt.*;

public class ProgramHierarchy extends GraphicsProgram {

//width of each box in the picture
private static final int Width = 150;

// height of each box in the picture
private static final int Height = 60;

public void run() {
drawProgramBox();
drawConsoleLine();
drawConsoleBox();
drawGraphicsLine();
drawGraphicsBox();
drawDialogLine();
drawDialogBox();
}
private void drawProgramBox() {
int x = getWidth()/2 - Width/2; //finds center width and moves to starting point
int y = getHeight()/2 - Height; //finds center height and moves to starting point
GRect drawBox = new GRect (x, y, Width, Height);
add(drawBox); //adds the Program box
GLabel program = new GLabel(&quot;Program&quot;, x, y);
add(program); //adds the &quot;Program&quot; words, but in the wrong location
double boxCenterX = Width/2;
double boxCenterY = Height/2;
double halfProgramWidth = program.getWidth()/2; //finds the center of the width for &quot;program&quot;
double halfProgramHeight = program.getAscent()/2; //finds the center of the height for &quot;program&quot;
program.move( (boxCenterX - halfProgramWidth) , (boxCenterY + halfProgramHeight) );
}
private void drawConsoleLine() {
int x1 = getWidth()/2; //finds center width
int y1 = getHeight()/2; //finds center height
int x2 = getWidth()/2;  //width value remains the same
int y2 = getHeight()/2 + Height; //finds end point for height
GLine drawLine = new GLine (x1, y1, x2, y2);
add(drawLine);
}
private void drawConsoleBox() {
int x = getWidth()/2 - Width/2; //Width remains same as Program Box Width
int y = getHeight()/2 + Height; //Height changes
GRect drawBox = new GRect (x, y, Width, Height);
add(drawBox); //adds the Console box
GLabel console = new GLabel (&quot;ConsoleProgram&quot;, x, y);
add(console); //adds the &quot;Console&quot; words, but in the wrong location
double boxCenterX = Width / 2;
double boxCenterY = Height / 2;
double halfConsoleWidth = console.getWidth()/2;
double halfConsoleHeight = console.getAscent()/2;
console.move( (boxCenterX - halfConsoleWidth) , (boxCenterY + halfConsoleHeight) );
}
private void drawGraphicsLine() {
int x1 = getWidth()/2;
int y1 = getHeight()/2;
int x2 = getWidth()/2 - 3 * (Width/2);
int y2 = getHeight()/2 + Height;
GLine drawLine = new GLine (x1, y1, x2, y2);
add(drawLine);
}
private void drawGraphicsBox() {
int x = getWidth()/2 - 2*Width;
int y = getHeight()/2 + Height;
GRect drawBox = new GRect (x, y, Width, Height);
add(drawBox);
GLabel graphics = new GLabel (&quot;GraphicsProgram&quot;, x, y);
add(graphics);
double boxCenterX = Width / 2;
double boxCenterY = Height / 2;
double halfGraphicsWidth = graphics.getWidth()/2;
double halfGraphicsHeight = graphics.getAscent()/2;
graphics.move( (boxCenterX - halfGraphicsWidth) , (boxCenterY + halfGraphicsHeight) );
}
private void drawDialogLine() {
int x1 = getWidth()/2;
int y1 = getHeight()/2;
int x2 = getWidth()/2 + 3 * (Width/2);
int y2 = getHeight()/2 + Height;
GLine drawLine = new GLine (x1, y1, x2, y2);
add(drawLine);
}
private void drawDialogBox() {
int x = getWidth()/2 + Width;
int y = getHeight()/2 + Height;
GRect drawBox = new GRect (x, y, Width, Height);
add(drawBox);
GLabel dialog = new GLabel (&quot;DialogProgram&quot;, x, y);
add(dialog);
double boxCenterX = Width / 2;
double boxCenterY = Height / 2;
double halfDialogWidth = dialog.getWidth()/2;
double halfDialogHeight = dialog.getAscent()/2;
dialog.move( (boxCenterX - halfDialogWidth) , (boxCenterY + halfDialogHeight) );
}
}&lt;/pre&gt;
```

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.

• Hi – I am just getting started with this class – and in looking for help I found your solutions. I am afraid to look at your solution, because I actually want to write the code myself so that I learn something. However, at first glance it looks a little challenging. The part that concerns me most, is how to center everything on the canvas. In other words, “the entire figure should be centered”. Any hints without spoiling it for me?

Thanks

• Sure, you can start by centering the Program box on the following x and y coordinates:

int x = getWidth()/2 – Width/2; //finds center width and moves to starting point

int y = getHeight()/2 – Height; //finds center height and moves to starting point

getWidth() and getHeight() gets the width and height of the whole application window

Width and Height get the width of the rectangle itself.

• KarelTheRobot

Hi Natasha,

I have the same concern as robg128 and that is the assignment’s requirement to center the whole diagram. What you have done was to center only the program box.

Anyways good software programming 🙂

• I am starting a later assignment using this solution (Section Assignment 7), and realized that you’re right. You need to make diagram into a GCompound (like his GFace program) and then center the GCompound as a whole.

• KarelTheRobot

You could do that ?? center the whole diagram as a whole? How?

• praven moorthy

/*
* File:ProgramHierarchy.java
* ————————–
* In this program it draws the program hierarchy of ACM library
* the whole graphics is centered on the screen
*/
import acm.graphics.*;
import acm.program.*;

public class ProgramHierarchy extends GraphicsProgram{

//width of the rectangle
private static final int RECT_WIDTH = 120;

//height of the rectangle
private static final int RECT_HEIGHT = 40;

public void run(){
//draws the graphics
drawProgramBox();
drawConsoleLine();
drawConsoleBox();
drawGraphicsLine();
drawGraphicsBox();
drawDialogLine();
drawDialogBox();

}

//draws the program box with the label
private void drawProgramBox(){

double x = getWidth()/2 – RECT_WIDTH/2;
double y = getHeight()/2 – RECT_HEIGHT;
GLabel programLabel = new GLabel(“Program”);
GRect programBox = new GRect(x,y,RECT_WIDTH,RECT_HEIGHT);
programLabel.setLocation((getWidth()/2 – programLabel.getWidth()/2),
(getHeight()/2-programLabel.getAscent()));
add(programLabel);
add(programBox);
}

//draws the line for console box
private void drawConsoleLine(){

double x1 = getWidth()/2;
double x2 = getHeight()/2;
double x3 = x1;
double x4 = getHeight()/2 + RECT_HEIGHT;
GLine consoleLine = new GLine(x1,x2,x3,x4);
add(consoleLine);
}

//draws console program box with label
private void drawConsoleBox(){

double x = getWidth()/2 – RECT_WIDTH/2;
double y = (getHeight()/2 – RECT_HEIGHT) + (2*RECT_HEIGHT);
GLabel consoleLabel = new GLabel(“Console Progam”);
GRect consoleBox = new GRect(x,y,RECT_WIDTH,RECT_HEIGHT);
consoleLabel.setLocation((getWidth()/2 – consoleLabel.getWidth()/2),
(getHeight()/2 – consoleLabel.getAscent())+2*RECT_HEIGHT);
add(consoleLabel);
add(consoleBox);
}

//draws the line for graphics box
private void drawGraphicsLine(){

double x1 = getWidth()/2;
double x2 = getHeight()/2;
double x3 = getWidth()/2 – 1.5*RECT_WIDTH;
double x4 = getHeight()/2 + RECT_HEIGHT;
GLine graphicsLine = new GLine(x1,x2,x3,x4);
add(graphicsLine);
}

//draws console graphics box with label
private void drawGraphicsBox(){

double x = getWidth()/2 -2*RECT_WIDTH;
double y = (getHeight()/2- RECT_HEIGHT) + (2*RECT_HEIGHT) ;
GLabel graphicsLabel = new GLabel(“Graphics Progam”);
GRect graphicsBox = new GRect(x,y,RECT_WIDTH,RECT_HEIGHT);
graphicsLabel.setLocation((getWidth()/2 – graphicsLabel.getWidth()/2)-(4.5*RECT_HEIGHT),
(getHeight()/2 – graphicsLabel.getAscent())+2*RECT_HEIGHT);
add(graphicsLabel);
add(graphicsBox);
}

//draws the line for dialog box
private void drawDialogLine(){

double x1 = getWidth()/2;
double x2 = getHeight()/2;
double x3 = getWidth()/2 + 1.5*RECT_WIDTH;
double x4 = getHeight()/2 + RECT_HEIGHT;
GLine dialogLine = new GLine(x1,x2,x3,x4);
add(dialogLine);
}

//draws console dialog box with label
private void drawDialogBox(){

double x = getWidth()/2 + RECT_WIDTH;
double y = (getHeight()/2- RECT_HEIGHT) + (2*RECT_HEIGHT) ;
GLabel dialogLabel = new GLabel(“Graphics Progam”);
GRect dialogBox = new GRect(x,y,RECT_WIDTH,RECT_HEIGHT);
dialogLabel.setLocation((getWidth()/2 – dialogLabel.getWidth()/2)+(4.5*RECT_HEIGHT),
(getHeight()/2 – dialogLabel.getAscent())+2*RECT_HEIGHT);
add(dialogLabel);
add(dialogBox);
}

}

• I would write a method to draw a labelled box in order to avoid duplicate code.

For example:

• I tried doing this, but i find that unless i declare the X GRect x and y inside each box, i can’t get the whole thing to center correctly. It does not get the getWidth() and getHeight() correctly… am i doing something wrong?

• I don’t see the problem. My method calls look like this:

• The whole program looks like this:

• …well, I did not really finish it, but it shows the solution.

• Hi!! I used quite a different technique by writing methods for doing the line, rectangle, and labels..
Here is my code.. What do you think..?

```
public class GraphicsHierarchy extends GraphicsProgram {

private static final int REC_WIDTH = 100;
private static final int REC_HEIGHT = 35;
private static final int DIFF = (REC_HEIGHT / 2) + 10;

public void run() {
GRect rect1 = drawRect(0,-DIFF);
GRect rect2 = drawRect(0, DIFF);
GRect rect3 = drawRect(REC_WIDTH + (DIFF-10), DIFF); //Draws several rectangle with parameters
GRect rect4 = drawRect(-REC_WIDTH - (DIFF-10), DIFF);  // is its difference from mid-point
addLabelLineRect(rect1,rect2,rect3, rect4); //redirects program to a method which adds all objects
}

/*This method generates a rectangle according to constant height and width specified above
* and finds the actual coordinates from parameters (difference from mid-point)*/
private GRect drawRect(int xMid, int yMid){
int x = ((getWidth()/2) + xMid) - (REC_WIDTH/2);
int y = ((getHeight()/2) + yMid) - (REC_HEIGHT/2);
GRect rect = new GRect(x, y, REC_WIDTH, REC_HEIGHT);
return rect;

}

/*This method just adds all objects in the program*/
private void addLabelLineRect(GObject obj1, GObject obj2, GObject obj3, GObject obj4){
add(putLabel(obj1, "Program"));
add(putLabel(obj2, "ConsoleProgram"));
add(putLabel(obj3, "DialogProgram"));
add(putLabel(obj4, "GraphicsProgram"));
add(lineObj(obj1, obj2));
add(lineObj(obj1, obj3));
add(lineObj(obj1, obj4));
add(obj1); add(obj2); add(obj3); add(obj4);
}

/*This method connects the center of two rectangle (origin and end) with a line
* The parameters are 2 GObects which we want to connect with a line*/
private GLine lineObj(GObject origin, GObject end){
int xOrg = (int) (origin.getX() + (origin.getWidth()/2));
int yOrg = (int) (origin.getY() + origin.getHeight());
int xEnd = (int) (end.getX() + end.getWidth()/2);
int yEnd = (int) end.getY();
GLine line = new GLine(xOrg, yOrg, xEnd, yEnd);
return line;

}

/*This method centers and puts a label on a rectangle.
* The parameters are the object(which we want to put a label) and the text*/
private GLabel putLabel(GObject object, String text){
GLabel label = new GLabel(text);
int x = (int) ((object.getX() + object.getWidth()/2) - label.getWidth()/2);
int y = (int) ((object.getY() + object.getHeight()/2) + label.getAscent()/2);
label.setLocation(x, y);
return label;
}

}
```
• zwip778

The problem with your answer Natasha (of course, I am a beginner so it’s just my opinion) is that you centered the first box but the whole figure (the three boxes).

If I am wrong, I would appreciate some help. Great blog, keeps me motivated, thank you

• I was just a beginner like you when I posted these solutions, so there is definitely a big chance that this solution is not correct. There are definitely better ways to do this problem.

• Hi:

I’m doing this class via the free online version and your example helped me understand this question a lot better but also made me realize that my solution was probably overkill 😉

Unfortunately, I was too far along to go back so I finished using a more algorithmic solution for calculating the x and y coordinates. Interestingly it works no matter how many boxes are in the second row.

I’d paste the code here but the formatting seems to get screwed up when I do that. Any ideas?

• Hi Paul,

I’d love to see your code! Here is how you can format it in wordpress: http://natashatherobot.com/2012/02/03/how-to-embed-code-into-your-wordpress-com-blog/. If that doesn’t work, just paste in your code, and I’ll format it for you in the admin secion (which is what I’ve been doing for everyone else).

Great job for finishing this!
Natasha

• It doesn’t seem to work. Perhaps it’s because you can edit html as the admin and these reply boxes don’t seem to let you do that.

Anyway, my version is below. Hope that someone finds it useful.

```/*
* File: GraphicsHierarchy.java
* ----------------------------
* This program is a solution for GraphicsHierarchy problem for Stanford's online version of CS106A.
*/

import acm.program.*;
import acm.graphics.*;

public class GraphicsHierarchy extends GraphicsProgram {

private static final double BOX_WIDTH = 100; // needs to be at least 100 to fit all labels.  if NUM_BOXES &gt; 7 all boxes will not fit width of diplay for BOX_WIDTH &gt; 100
private static final double BOX_HEIGHT = 40;
private static final double BOX_SPACER = 20;  //spacer between boxes
private static final double NUM_BOXES = 4;  //Total number of boxes.   One in first row plus others in second row. this will actually work for any NUM_BOXES but can exceed the screen width as numbers get larger

public void run() {

for (int i=0; i0) {
double lineTermX = getWidth()/2; // find center to top box for line terminus
double lineTermY = getHeight()/2 - BOX_HEIGHT;  //Adjusts up to bottom of top row
double lineStartX = xCoord + BOX_WIDTH/2;
GLine boxline = new GLine(lineStartX,yCoord,lineTermX,lineTermY);
add(boxline);
}
}

/*
* method writeLabel adds a label to each box after finding the center of the box and hte appropriate place to start.
* Label is centered in middle of the box both horizontally and vertically.  To figure the centers you need to both figure out the middle of the box and the middle of the label.
* So for the x direction you start at the left side of the label move right half the width of the box +BOX_WIDTH/2 the move back left half the width of the label -boxlabel.getWidth()/2
* You do basically the same in the y direction except adjusting for half the height of the box +BOX_HEIGHT/2 and half the height of the text +boxlabel.getAscent()/2
* REMEMBER that Y increases as you go down, not up!
*/

private void writeLabel(int i, double xCoord, double yCoord) {
String class1 = ("");

if (i==0) class1 = ("Program");
else if (i==1) class1 = ("GraphicsProgram");
else if (i==2) class1 = ("ConsoleProgram");
else if (i==3) class1 = ("DialogProgram");
else if (i==4) class1 = ("Label 4");  //extra labels if you want more boxes
else if (i==5) class1 = ("Label 5");
else if (i==6) class1 = ("Label 6");
else if (i==7) class1 = ("Label 7");

GLabel boxlabel = new GLabel(class1);
double labelXCoord = xCoord + (BOX_WIDTH/2 - boxlabel.getWidth()/2); // adjusts for center of box and the adjusts for width of label
double labelYCoord = yCoord + BOX_HEIGHT/2 + boxlabel.getAscent()/2; // adjusts for center of box and the adjusts for height of label
add(boxlabel,labelXCoord,labelYCoord);
}

}
```
• Thanks! Just added the formatting 🙂

• Nathan

I think the most difficult part of this was centering the text. The getAscent and getWidth functions were definitely not correctly centering them like they should. For some reason my text was 3 pixels off from the rectangle with same x coordinates and the getAscent function was definitely not working correctly for me. Here’s my code, I ended up eyeballing the centering.

```import acm.graphics.*; import acm.program.*; import java.awt.*;```

``` public class DefaultGraphics extends GraphicsProgram{ public int boxSizeX=200; public int boxSizeY=75; public void run(){ int startX=(getWidth()-boxSizeX)/2; int startY=(getHeight()-boxSizeY)/2; layBox(startX, startY-boxSizeY*2, "Program"); layBox(38.5, startY, "GraphicsProgram"); connectBox(38.5+(boxSizeX/2), startY, startX+boxSizeX/2 , startY-boxSizeY); // Connect Lines for Heirarchy layBox(startX, startY, "ConsoleProgram"); connectBox(startX+boxSizeX/2, startY, startX+boxSizeX/2, startY-boxSizeY ); layBox(startX+38.5+boxSizeX, startY, "DialogProgram"); connectBox(startX+boxSizeX*1.5, startY, startX+boxSizeX/2, startY-boxSizeY); } ```

``` public void layBox(double x, double y, String msg) { GRect label = new GRect(x, y, boxSizeX, boxSizeY); add(label); GLabel text = new GLabel(msg, x, y); text.move(-3,0); // adjustment to line text up with left side of rectangle; // Centers Text in Box Sort Of - Don't ask me what I did, getAscent and getWidth were not adjusting text linearly text.move((boxSizeX-text.getWidth()*1.5)/2, (boxSizeY-text.getAscent()/.5)); text.setFont("Helvetica-20"); add(text); } public void connectBox(double x, double y, double dx, double dy) { GLine line = new GLine(x, y, dx, dy); add(line); } }```

• Joshua Farsdale

Nice. I was trying to pass a string in to an object as a parameter and was having a hard time.
Thank you for posting your code.

• Guest

Here is mine

public class ProgramHierarchy extends GraphicsProgram {

/* Rectangle widht */

private static final int RECT_WIDTH = 120;

/* Rectangle height */

private static final int RECT_HEIGHT = 60;

private static final int MAX_RECT = 3;

public void run() {

int Xspacing= 50;

int x = (getWidth() – (3*RECT_WIDTH+ 2*Xspacing))/2;

int y = getHeight()/2;

for (int i=0; i<MAX_RECT; i++) { //creating a loop

GRect rect =new GRect(x, y,RECT_WIDTH, RECT_HEIGHT); // setting up rectangles

rect.setFilled(false);

rect.setColor(Color.black);

add(rect);

switch (i){ // Writing text in the rectangles

case 0:

GLabel text =new GLabel("GraphicsProgram", x, y);

text.move(+ rect.getWidth()/2-text.getWidth()/2,rect.getHeight()/2);

add(text);

break;

case 1:

GLabel text2 =new GLabel("ConsoleProgram", x, y);

text2.move(+ rect.getWidth()/2-text2.getWidth()/2,rect.getHeight()/2);

GLabel text4 =new GLabel("Program", x, y*2/4);

text4.move(+ rect.getWidth()/2-text4.getWidth()/2,rect.getHeight()/2);

add(text2);

add(text4);

break;

case 2:

GLabel text3 =new GLabel("DialogPragram", x, y);

text3.move(+ rect.getWidth()/2-text3.getWidth()/2,rect.getHeight()/2);

add(text3);

break;

}

GLine line =new GLine(x+RECT_WIDTH/2, y, getWidth()/2, y*2/4+RECT_HEIGHT);

add (line); //linking rectangles to the top center rectangle

if (i==1){ // setting up the center up rectangle

GRect rect2 =new GRect(x, y*2/4,RECT_WIDTH, RECT_HEIGHT);

rect2.setFilled(false);

rect2.setColor(Color.black);

add(rect2);

}

x = x + RECT_WIDTH + Xspacing; // making the x evolve

}

}

}

• Sancho Sanchez

Here is mine

``` public class ProgramHierarchy extends GraphicsProgram { /* Rectangle widht */ private static final int RECT_WIDTH = 120; /* Rectangle height */ private static final int RECT_HEIGHT = 60; private static final int MAX_RECT = 3; public void run() { int Xspacing= 50; int x = (getWidth() - (3*RECT_WIDTH+ 2*Xspacing))/2; int y = getHeight()/2; for (int i=0; i<MAX_RECT; i++) { //creating a loop GRect rect =new GRect(x, y,RECT_WIDTH, RECT_HEIGHT); // setting up rectangles rect.setFilled(false); rect.setColor(Color.black); add(rect); switch (i){ // Writing text in the rectangles case 0: GLabel text =new GLabel("GraphicsProgram", x, y); text.move(+ rect.getWidth()/2-text.getWidth()/2,rect.getHeight()/2); add(text); break; case 1: GLabel text2 =new GLabel("ConsoleProgram", x, y); text2.move(+ rect.getWidth()/2-text2.getWidth()/2,rect.getHeight()/2); GLabel text4 =new GLabel("Program", x, y*2/4); text4.move(+ rect.getWidth()/2-text4.getWidth()/2,rect.getHeight()/2); add(text2); add(text4); break; case 2: GLabel text3 =new GLabel("DialogPragram", x, y); text3.move(+ rect.getWidth()/2-text3.getWidth()/2,rect.getHeight()/2); add(text3); break; } GLine line =new GLine(x+RECT_WIDTH/2, y, getWidth()/2, y*2/4+RECT_HEIGHT); add (line); //linking rectangles to the top center rectangle if (i==1){ // setting up the center up rectangle GRect rect2 =new GRect(x, y*2/4,RECT_WIDTH, RECT_HEIGHT); rect2.setFilled(false); rect2.setColor(Color.black); add(rect2); } x = x + RECT_WIDTH + Xspacing; // making the x evolve } } } ```

• Guest

Here is mine:

public class ProgramHierarchy extends GraphicsProgram {

/* Rectangle widht */

private static final int RECT_WIDTH = 120;

/* Rectangle height */

private static final int RECT_HEIGHT = 60;

private static final int MAX_RECT = 3;

public void run() {

int Xspacing= 50;

int x = (getWidth() – (3*RECT_WIDTH+ 2*Xspacing))/2;

int y = getHeight()/2;

for (int i=0; i<MAX_RECT; i++) { //creating a loop

GRect rect =new GRect(x, y,RECT_WIDTH, RECT_HEIGHT); // setting up rectangles

rect.setFilled(false);

rect.setColor(Color.black);

add(rect);

switch (i){ // Writing text in the rectangles

case 0:

GLabel text =new GLabel("GraphicsProgram", x, y);

text.move(+ rect.getWidth()/2-text.getWidth()/2,rect.getHeight()/2);

add(text);

break;

case 1:

GLabel text2 =new GLabel("ConsoleProgram", x, y);

text2.move(+ rect.getWidth()/2-text2.getWidth()/2,rect.getHeight()/2);

GLabel text4 =new GLabel("Program", x, y*2/4);

text4.move(+ rect.getWidth()/2-text4.getWidth()/2,rect.getHeight()/2);

add(text2);

add(text4);

break;

case 2:

GLabel text3 =new GLabel("DialogPragram", x, y);

text3.move(+ rect.getWidth()/2-text3.getWidth()/2,rect.getHeight()/2);

add(text3);

break;

}

GLine line =new GLine(x+RECT_WIDTH/2, y, getWidth()/2, y*2/4+RECT_HEIGHT);

add (line); //linking rectangles to the top center rectangle

if (i==1){ // setting up the center up rectangle

GRect rect2 =new GRect(x, y*2/4,RECT_WIDTH, RECT_HEIGHT);

rect2.setFilled(false);

rect2.setColor(Color.black);

add(rect2);

}

x = x + RECT_WIDTH + Xspacing; // making the x evolve

}

}

}