Data Structures and Algorithms

Lab 3: Shapes

Due on Wednesday, September 25th at 11:59pm. This is an individual lab. You are to complete this lab on your own, although you may discuss the lab concepts with your classmates. Remember the Academic Integrity Policy: do not show your code to anyone outside of the course staff and do not look at anyone else’s code for this lab. If you need help, please post on the Courselore forum or reach out to the course staff. If you have any doubts about what is okay and what is not, it’s much safer to ask than to risk violating the Academic Integrity Policy.

Overview

In this lab, you will design a hierarchy of classes. You will become familiar with the following in C++:

  • Classes and Objects

  • Inheritance

  • Polymorphism

  • Memory management

As with the previous labs, you should clone your repository. The URL is:

git@github.swarthmore.edu:cs35-f24/lab03-<your-username>

We will describe this lab in three parts, the last of which has been provided for you. You are strongly encouraged to read this entire lab write-up when you begin this assignment! Later in the write-up, we give suggestions on how to get started.

Part 1: Creating Shape Classes

The first part of this lab involves defining classes that represent different kinds of shapes. Each of our shapes may look different, but they all have something in common: we can draw them onto a grid.

By default the grid will be 20 wide and 10 high. You may assume that the user always provides valid coordinates that lie within the grid’s boundaries.

Hierarchy of Classes

A Shape class is provided for you in shape.h. This is a purely virtual class and therefore we do not need a shape.cpp file.

You need to do the following:

  • Define a Point class in point.h and point.cpp

  • Define a HorizontalLine class in horizontalLine.h and horizontalLine.cpp

  • Define a VerticalLine class in verticalLine.h and verticalLine.cpp

  • Define a Rectangle class in rectangle.h and rectangle.cpp

Class declarations go in the header files (.h) while class implementations go in the source files (.cpp).

The following sections describe specifications for the Rectangle, HorizontalLine, VerticalLine, and Point classes. You must abide by these specifications.

Point Class

Your Point class is a subclass of Shape. It must contain a constructor that takes as input (order matters):

  • two int parameters for x position and y position

  • one char parameter for display symbol

It must contain the following private member variables:

  • int variables x and y

  • char variable symbol

Because the Point class is a subclass of Shape, it must contain a public method called draw with one input argument of type Grid* and return type void. The draw method places the display symbol onto the grid at the appropriate location.

For example, consider a Point with x position 5, y position 6, and display symbol 'O'. We create this point using the following code:

Shape* myShape = new Point(5, 6, 'O');

When drawn on the grid, the point will appear as follows. Recall that the upper left corner of the grid has coordinate (0,0), just as in the PicFilter lab.

* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * O * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *

HorizontalLine and VerticalLine Classes

Your HorizontalLine and VerticalLine classes are also subclasses of Shape. They must contain constructors that take as input (order matters):

  • three int parameters for x position, y position, and length

  • one char parameter for display symbol

They must contain the following private member variables:

  • int variables x, y, and length

  • char variable symbol

As with Point, the HorizontalLine and VerticalLine classes must implement the draw method. A HorizontalLine object starts at the given x and y coordinates and extends to the right occupying cells on the grid. The number of cells that the HorizontalLine occupies is equal to its length. A VerticalLine object is the same but extends down occupying cells.

For example, let us consider a grid with two shapes:

  • a HorizontalLine with x position 2, y position 5, length 3, and display symbol '$'; and

  • a VerticalLine with x position 5, y position 0, length 4, and display symbol '@'

We create these shapes using the following code:

Shape* myShape1 = new HorizontalLine(2, 5, 3, '$');
Shape* myShape2 = new VerticalLine(5, 0, 4, '@');

The lines would appear on the grid as follows:

* * * * * @ * * * * * * * * * * * * * *
* * * * * @ * * * * * * * * * * * * * *
* * * * * @ * * * * * * * * * * * * * *
* * * * * @ * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * $ $ $ * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *

Rectangle Class

Your Rectangle class is also a subclass of Shape. It must contain a constructor that takes as input (order matters):

  • four int parameters for x position, y position, width, and height

  • one char parameter for display symbol

It must contain the following private member variables:

  • int variables x, y, width, and height

  • char variable symbol

As a Rectangle is also a subclass of Shape, it too must implement the draw method correctly. We will draw solid rectangles that are filled-in with the rectangle’s symbol.

For example, consider a Rectangle with x position 3, y position 0, width 5, height 3, and display symbol 'X'.

We create this rectangle using the following code:

Shape* myShape = new Rectangle(3, 0, 5, 3, 'X');

The rectangle would appear on the grid as follows:

* * * X X X X X * * * * * * * * * * * *
* * * X X X X X * * * * * * * * * * * *
* * * X X X X X * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *

Part 2: The Picture Class

With an idea of what shapes are, we can define the idea of a "picture": a set of shapes drawn in specified order. The shapes in a picture have a specified order because we may draw shapes on top of other shapes.

Class Structure

The design for the Picture class is provided for you in picture.h. It’s your job to fill in the implementation for the Picture class in picture.cpp.

The Picture class contains the following private member variables:

  • int numberOfShapes

  • Shape** shapes

The numberOfShapes variable keeps track of how many shapes have been added to the picture. The shapes variable keeps track of those shapes.

The shapes variable has the type Shape**, which is new to our labs. Just as an int* variable can be used to point to an array of int values, a Shape** variable can be used to point to an array of Shape* values. That is, the Shape** type of the shapes variable can be read as "a pointer to an array of pointers to Shape objects". Because the array does not require a specific type of Shape, the pointers may point to a mixed collection of shapes: Point, HorizontalLine, VerticalLine, and Rectangle objects.

Your Tasks

To complete the Picture class, you will need to complete the following tasks.

  1. Implement a Picture constructor. The constructor must prepare the Picture to be used, so you will need to initialize the numberOfShapes and shapes variables. Your Picture starts with no shapes in it but must be ready to add shapes in the future. You may assume that users will never add more than fifty shapes to a picture, so you may initialize the shapes variable to an array of size 50.

  2. Implement a Picture destructor (named ~Picture). The destructor must clean up everything in the picture. Since shapes added to a picture will be owned by that picture, the ~Picture destructor is responsible for deleting them.

  3. Implement an addShape method. This method takes a Shape* that points to a shape and adds it to the picture. (You’ll need to keep it in your array so you can draw it later.)

  4. Implement a toString method. This method should create a grid, draw all of the Shapes onto it, and then convert the grid to a string that you return. (Remember: if you dynamically allocate any objects during this process, you’ll need to clean them up!)

Part 3: The Artwork Program

The third part of this assignment is a program called artwork that allows the user to draw a picture using the classes you have defined. Don’t worry: the artwork program has been written for you! Once you have defined a Shape class and a Picture class, you can run make followed by ./artwork and experiment with the classes you have built.

Getting Started

When you first clone your Git repository for this assignment, it will contain the following starter files. Those that are highlighted require modification.

  • Makefile - instructions for compiling your program.

  • shape.h - design for virtual Shape class

  • rectangle.h, rectangle.cpp - design & implementation for Rectangle class

  • horizontalLine.h, horizontalLine.cpp - design & implementation for HorizontalLine class

  • verticalLine.h, verticalLine.cpp - design & implementation for VerticalLine class

  • point.h, point.cpp - design & implementation for Point class

  • picture.h, picture.cpp - design & implementation for Picture class

  • grid.h, grid.cpp - design & implementation for Grid class

  • artwork.cpp - main methods for running application that creates shapes

  • shapeFactory.h, shapeFactory.cpp - behind-the-scenes code for creating shapes

  • tests.cpp, manualTests.cpp - programs that can test your code

Note: A generally good strategy for tackling this lab is to start by implementing Point (the Shape with the shortest draw method), then to implement Picture (so you can start testing with the artwork program), and then to implement the remaining shapes.

Testing Your Code

There are at least three ways to test your code in this lab:

  1. You can write code in the main function of the file manualTests.cpp. Then, you can run make manualTests and ./manualTests. This gives you a playground in which you can experiment with the classes and objects you are writing. You can run valgrind on manualTests to track down bugs in your classes. See below for an example of how to draw multiple shapes onto a picture and print them.

  2. Once you have implemented Picture and at least one Shape, you can run make and ./artwork. This will allow you to draw shapes onto a picture. You can use this even if you haven’t written all of the Shape subclasses; it just won’t work for the shapes you haven’t finished yet.

  3. You can run make tests followed by ./tests to run some automated tests. You can run these tests before you’re finished with the lab; even though many tests will fail, the names of those tests should indicate whether the failure is because you have not yet finished the lab or because something is wrong with what you have written. For instance, if you have finished Point but not Rectangle, you can ignore failing tests with "rectangle" in their names, but failing tests with "point" in their names might indicate that you need to fix your code before moving on.

An Example for Drawing Multiple Shapes

The following creates three rectangles:

Shape* myShape1 = new Rectangle(0, 0, 7, 7, '$');
Shape* myShape2 = new Rectangle(1, 1, 6, 4, '-');
Shape* myShape3 = new Rectangle(3, 2, 4, 3, 'z');

The following adds the three shapes to a picture:

Picture myPicture;
myPicture.addShape(myShape1);
myPicture.addShape(myShape2);
myPicture.addShape(myShape3);

The following will draw the shapes one after another onto a Grid and print the result to the command line:

myPicture.print();

The result looks as follows:

$ $ $ $ $ $ $ * * * * * * * * * * * * *
$ - - - - - - * * * * * * * * * * * * *
$ - - z z z z * * * * * * * * * * * * *
$ - - z z z z * * * * * * * * * * * * *
$ - - z z z z * * * * * * * * * * * * *
$ $ $ $ $ $ $ * * * * * * * * * * * * *
$ $ $ $ $ $ $ * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * *

Coding Style Requirements

You will also be required to observe good coding practices:

  • Your C++ code must have proper and consistent indentations.

  • You must have proper and consistent usage of spacing and braces for if, else, for, and while conditions as well as class definitions and code blocks.

  • Always comment any declarations you introduce to .h files or elsewhere.

Summary

To summarize the requirements of this lab:

  • You must declare and define four subclasses of Shape: Point, HorizontalLine, VerticalLine, and Rectangle.

  • You must define a Picture class that contains many Shapes and draws them all onto a Grid.

  • You should be able to run make tests and ./tests on the lab machines without any errors.

  • Your program should not have any memory leaks or errors.

Questionnaire

Once you have submitted your lab, please make sure to complete its corresponding questionnaire. The questionnaire will typically take less than a minute and helps us to understand how much work the labs are so we can adjust appropriately. Completing the questionnaire is part of your participation grade, so don’t forget to fill it out!