CSC230 Homework 6: Turtle Graphics

This homework is to be done individually.

Only use ANSI C99 standard library functions (excluding system()). Other libraries, like GNU libraries, etc., are not allowed.

Update 2015-07-27: Update 2015-07-23: Update 2015-07-21: Update 2015-07-19:

Learning Outcomes

Introduction

Turtle graphics were developed in the 1960's to study robotics, and later adapted to educate students in computer programming in concert with the LOGO programming language. They are capable of rendering complex, interesting images with relatively simple algorithms. The concept is that there is a cursor called the "turtle" on a 2D plane representing an image. This cursor has a position and a direction, and can be told to go "forward" (drawing a line behind it) or to turn "left" or "right" (changing the angle that will be traversed when it goes forward). It can also do things like lift its virtual drawing "pen" up and down, change color, etc. An simple example of turtle graphics is shown to the right, where an interactive turtle is used to draw four boxes.

In this assignment, you're going to implement a shared library (.so file) that implements turtle graphics, and it's going to be many times faster than the animation shown, rendering complex images in a few milliseconds. Features of this library will include:

As image processing is not the focus of this course, this document will provide you algorithms for the non-trivial graphics algorithms.

Program Requirements

API

You are provided with the header file for the library, CTurtle.h. This file constitutes your contract with the user of the library; you may not modify content it in any way except to optionally add entirely new functions. The file is documented with the behavior of each supported function to be implemented in CTurtle.c. This API can be used to create all sorts of fancy graphics, such as the Hilbert curve fractal shown to the right.

Images structs will be created by the program in one of three ways:

FunctionPurpose
create_image()Create a new image of the given dimensions, all pixels defaulting to black.
read_tga()Reads the given file and returns a new Image struct representing it -- only certain kinds of .tga files are supported (see below).
read_ppm()Reads the given file and returns a new Image struct representing it -- only certain kinds of .ppm files are supported (see below).

If an error occurs during file IO, users can get a string representing this error by calling get_last_error(). In order to free these objects, users of the API will call destroy_image().

Users can write direct graphics onto the canvas using the following functions -- these have no effect on the turtle.

FunctionPurpose
do_pixel()Set the given pixel to the given color. If the pixel lies outside of the image dimensions, ignore the request.
do_line()Draw a line from (x0,y0) to (x1,y1) using the given color. If any pixel of the line lies outside of the image dimensions, ignore that pixel.
do_clear()Blank the entire canvas with the given color.
do_invert()Invert all pixels of the image, giving the "negative" image.
do_reverse_rgb()Switch red and blue channels in the image.

For turtle graphics, the supported functions are:

FunctionPurpose
turtle_set_xy()Move the turtle to the given position without drawing anything.
turtle_move()Move the turtle to the given position, drawing a line as it goes if the pen is down.
turtle_forward()Move the turtle in the current direction by d pixels, drawing as it goes if the pen is down.
turtle_face_north()Reset the angle of the turtle to north.
turtle_left()Turn left by the number of degrees given.
turtle_right()Turn right by the number of degrees given.
turtle_pen_up()Lift the "pen", causing turtle move and forward to not draw anything.
turtle_pen_down()Put the "pen" down, causing turtle move and forward to draw lines.
turtle_set_color()Change the current turtle color.

Error handling: There are a number of error cases whose precise result is not specified in this document. For IO related errors, the correct response is to return NULL or false and place an appropriate message to be returned by get_last_error(). If the IO error was from a system call (such as fopen), the error message should be the system-provided one, i.e. strerror(errno). If the IO error was format or parsing related, provide the error message given in the tables below, or if a specific message isn't given in this document, provide a descriptive error message of your choosing. For non-IO errors, simply return NULL or false as appropriate. You will not be tested on specific error conditions not described in this document except to verify that your program does not crash (e.g. segfault).

Library behavior:

  1. Good libraries don't print to stdout or stderr -- you don't know what the user is trying to do with the console, so don't interfere. It's okay to print stuff while you're developing, though -- just take it all out before you're done.
  2. Your library shouldn't care about command line arguments -- you don't know if you're part of a program that even uses command line arguments.
  3. In addition, good libraries don't exit() when there's an error -- you give that error back to the user of the library to decide what to do about it.

TGA support

Truevision TGA is an old but fairly well supported graphic format that has its origins in the TARGA video card of early IBM PC, which was the first graphics card to support true photorealistic color. It has several modes and options that will not be supported by your library, specifically:

FeatureDescriptionSupportRead behaviorWrite behavior
Image ID fieldFiles could store extra metadata in the image id field.UnsupportedIf the image reports an image ID field of non-zero length, fail with error "TGA id field not supported"Emit files without an image ID field (so image id field length is zero)
Color mapFiles could have a "palette" of fixed colors.UnsupportedIf the image has a non-zero value for color map type, color map length, or color map first entry, then fail with error "TGA color map not supported"Emit files with no color map (color map fields are zero)
Multiple image typesTGA supports a variety of image formats and compression levelsRGB uncompressed onlyIf the image has an image type of any value other than 2 (RGB uncompressed), fail with error "Unsupported image type, only uncompressed RGB is supported"Emit files with image type 2 (uncompressed RGB)
OriginFiles can specify where (0,0) is on their canvasIgnoreSimply ignore these fieldsSet these fields to zero
Pixel depthHow many bits per pixel?24-bit RGBThe bpp (bits per pixel) must be 24; if it isn't, fail with the message "Unsupported image type, bit depth must be 24"Emit files with bpp of 24.
Image descriptorTGA has a field to indicate the nubmer of bits of transparency as well as if the image is written top-to-bottom or bottom-to-top.No transparency, top-to-bottom onlyThe image descriptor must be 0x20 (zero transparency bits, bit 5 indicating that the image is read top-to-bottom). If it isn't, fail with the message "Unsupported image format, TGA image descriptor must be 0x20".Emit files with image descriptor set to 0x20.

Tips on supporting this file format are given in the Implementation section later in this document.

PPM support

The Portable Pixmap (PPM) format was developed to be easy to read and write on a variety of platforms. There are six formats in this family, two of which will be supported by your library.

FormatDescriptionSupported?
P1Portable bitmap (.pbm), 1 bit per pixel monochrome, ASCII formatNo
P2Portable greymap (.pgm), 8 bit per pixel greyscale, ASCII formatNo
P3Portable pixmap (.ppm), 24 bit per pixel true color, ASCII formatYes
P4Portable bitmap (.pbm), 1 bit per pixel monochrome, binary formatNo
P5Portable greymap (.pgm), 8 bit per pixel greyscale, binary formatNo
P6Portable pixmap (.ppm), 24 bit per pixel true color, binary formatYes

Error handling: If a file is provided which does not start with 'P', fail with the error message "Improperly formatted PPM file". If the character following the 'P' is something other than '3' or '6', fail with the error message "Only P3 and P6 formats are supported".

Tips on supporting this file format are given in the Implementation section later in this document.

Python bindings

Your library will be usable by C programs directly, but it will be able to be called from programs written in the Python programming language. As this is not a course on Python, almost all of the bindings have been written for you: CTurtle.py.

However, you will need to add the bindings for face_north(), left(), and right().

Artistic self-expression

Once you have completed the library, in either C or Python, you will write a client program that links this library, and use it to produce an image which you deem to be aesthetically pleasing and/or cool looking.

Requirements:

If you're looking for inspiration, note that turtle graphics are often used for linear fractals. See A Very Patient Turtle Who Draws Lines, L-Systems, and chapter 5 of Mathematical Problem Solving with Computers by Simanca & Sutherland.

Design

The starter kit includes the following files:

FileDescriptionChanges needed?
CTurtle.hInterface of the CTurtle libraryNone, unless you want to add optional functionality
CTurtle.cImplementation of the CTurtle libraryWrite from scratch
CTurtle.pyPython binding for the CTurtle libraryNeed to add a few function bindings
cturtle_test.cStudent test suite written in CNone (you may add to this file at your discretion)
pyturtle_test.pyStudent test suite written in PythonNone (you may add to this file at your discretion)
MakefileUsed to build the projectWrite from scratch
test.shStudent test script -- runs both C and Python testsNone (you may add to this file at your discretion)
test-in/*Test input filesNone (you may add test inputs at your discretion)
test-out/Test output directoryNone (the test script will deposit results here)
test-out/expected*Test expected outputsNone (the test script will compare results against these files)

In addition, you must create the following files:

FileDescription
cturtle_make_art.c
 OR
cturtle_make_art.py
Creates the artistic expression required. If written in C, compiles to cturtle_make_art.
fantastic_art.tga
 OR
fantastic_art.ppm
The artistic expression, in TGA format or binary PPM format. Do NOT submit in ASCII PPM format (it's too big).

The Makefile: Your Makefile should implement the following dependency graph:

Revised 2015-07-23. The two additional dependencies are shown in red.

The arrows are labeled with important gcc options that will be needed. Blue nodes represent source files, yellow are binaries, and pink are dummy targets.

Note: your dependency graph may differ slightly depending on if you make your art in Python versus C, and if your art is in PPM rather than TGA format.

Implementation

Workflow

Get your environment set up

Note: If you successfully cloned your GitHub repo for HW2, you can skip to the section "Get the starter files". Make sure that you're developing in the 6_homework directory!

Start by cloning the provided CSC230 GitHub repo in your local AFS (or development space) using the following commands:

$ unset SSH_ASKPASS
$ git clone https://<unity_id>@github.ncsu.edu/engr-csc230-summer2015/<repo_name>.git

This will create a directory with your repo's name. If you cd into the directory, you should see directories for each of the homeworks for the class. You'll want to do all of your development in 6_homework. If you do NOT see the 6_homework directory, enter the following command to make the directory:

$ mkdir 6_homework

Setting LD_LIBRARY_PATH so your library can be linked at runtime

Because we are building a library, we'll need to include our development area in the LD_LIBRARY_PATH environment variable. You can do this by executing the following in your development directory:

$ export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:`pwd`

If you get sick of typing that every time, put the command above into a file called prep (or whatever) and have your shell read it in with the source command: $ source prep

Note that the source command is so frequently used that it can be appreviated to a single dot: $ . prep

Get the starter files

Next, you will need to copy the hw6_starter.tgz from the course locker into your 6_homework directory. Use the following command:

$ wget

Untar using the command:

$ tar xvzf hw6_starter.tgz

Set up your Makefile

In addition, the Makefile is empty -- you will also need to write this. Requirements:

See the Design section above for tips on building your Makefile.

Write your code: CTurtle.c

Your code library code should be implemented in CTurtle.c. You must use the appropriate name for automated grading.

Complete the Python bindings: CTurtle.py

You will add the missing bindings for face_north(), left(), and right(). If you do not do this, then some of the Python test cases will not pass, even if your C code is complete.

Create art: cturtle_make_art.c or cturtle_make_art.py; fantastic_art.tga or fantastic_art.ppm

Use your library to create a C or Python program which generates a cool looking image.

Structs and typedefs

The color of individual pixels will be represented by a Color struct:

#pragma pack(push,1) // force compiler to not use padding in laying out this struct in memory /** * Represents a color with red, green, and blue components. */ struct Color { uint8_t r; uint8_t g; uint8_t b; }; typedef struct Color Color; // The Color type by itself refers to the struct as a whole. #pragma pack(pop) // undo the previous pack directive

Therefore, to represent an image, you simply need an array of Color structs. The typedef is present so that Color is a top-level name that is an alais for struct Color. The #pragma directives ensure that no padding is added to this struct by the compiler -- see the Implementing TGA section for details on how this works.

Images will be represented by a heap-allocated Image struct:

/** * Represents an image in memory, in addition to the current state of the turtle associated with it. */ struct Image { struct Color* pixels; // an heap-allocated array of Color structs, // each representing 3 channels of color -- number of elements is width*height int width; // width of the image, in pixels int height; // height of the image, in pixels // turtle settings: int turtle_x; // current x coordinate int turtle_y; // current y coordiante int turtle_angle; // current angle, in degrees, with 0 being right and 90 being up bool turtle_pen_down; // true if the drawing pen is "down" (enabled) Color turtle_color; // current color of the turtle }; typedef struct Image* Image; // The Image type by itself refers to a _pointer_ to a struct Image!

Note the typedef of struct Image* to Image. This will make Image function somewhat similar to a traditional OO object -- it's allocated at creation time and freed at destruction time. In addition, there's also a heap-allocated region for the pixels themselves. This is comprised of Color structs, each containing one byte for red, green, and blue. Therefore, each image actually consists of two heap-allocated regions -- a fixed-size Image struct and a variably sized array of Color structs pointed to by the Image member pixels.

Introduction to graphics

Images are two-dimensional matrices of colored dots called pixels. On a modern true-color system, each pixel is represented by three unsigned bytes: red, green, and blue. These colors correspond to the light receptors in your eyes. We refer to this as 24-bit RGB. These colors combine additively, as shown in the diagram on the right. For example, {0,0,0} represents black, {255,0,0} represents red, and {0,255,0} represents green. If you add red and green:

{255,0,0} + {0,255,0} = {255,255,0}
You get {255,255,0}, which is yellow. From this, we conclude that {255,255,255} is white.

Individual pixels are located on an XY plane. Different applications orient the coordinates differently. In this program, we're going to use a common format: the origin (0,0) is at the upper left of the image. This means that positive X values are to the right, and positive Y values are to the bottom.

To change a pixel's color, you simply change its value. Because we're using calloc to allocate the pixels array, you're going to end up with a one-dimensional array of Color structs which you can index with the expression:

image->pixels[ x + y*width ]

From there, you can access the r, g, and b members.

A line drawing algorithm for do_line()

It's actually surprisingly hard to draw a line out of pixels (see this article for an explanation why). Because this isn't a graphics course, we will simply provide a straightforward algorithm for line drawing, Bresenham's line algorithm:

function line(x0, y0, x1, y1) dx := abs(x1-x0) dy := abs(y1-y0) if x0 < x1 then sx := 1 else sx := -1 if y0 < y1 then sy := 1 else sy := -1 err := dx-dy loop plot(x0,y0) if x0 = x1 and y0 = y1 exit loop e2 := 2*err if e2 > -dy then err := err - dy x0 := x0 + sx end if if e2 < dx then err := err + dx y0 := y0 + sy end if end loop

Above, plot means set pixel to the desired color.

Implementing turtle_forward()

The turtle_forward() walks the turtle forward a given number of pixels along the current compass heading, the angle. To do this requires a bit of trigonometry that you might be rusty on. If you're at position (x,y) with an angle specified in integer degrees and you walk d units, then you can compute your new position with these formulas:

double rads = angle * PI/180; // convert to radians, which is what sin and cos expect int x_new = x + round(d*cos(rads)); int y_new = y + -round(d*sin(rads)); // we negate the delta because the image has +Y going down, but math has +Y going up

Note the difference in sign. In traditional math, the increasing Y dimension points up. However, in most graphics applications, the increasing Y dimension points down, because the graphical origin is the upper-left corner. We negate the adjustment to Y to accomodate this difference.

Once you have this, you can draw the line by relying on the do_line() function.

Implementing TGA support

TGA is a fully binary file format. TGA files start with a fixed-header, some optional fields your library won't support, then a dump of the raw pixel data.

Packed structs: It is recommended that you model the TGA header as a struct. However, you will need to tell the compiler not to insert any "padding" space in the struct's layout. On gcc, you can do this by using the #pragma directive, which sets compiler-specific settings. For example:

#pragma pack(push,1) struct TargaHeader { ... } #pragma pack(pop)

The first #pragma says "pack this struct to the nearest 1 byte (i.e. no padding), and push this setting onto a setting stack. The setting stack is used so you can append the following right after the struct to undo this change with the second #pragma directive, which pops the original pack setting from the setting stack.

Header layout: To determine the content of this struct, you can refer to the original 80's-era specification, the Wikipedia article for it, or this detailed discussion of the format. (Note that historical documentation may refer to 16-bit values as a "word", because this was the dominant word size at the time.)

Channel order: One important thing to note is the order of colors in a pixel -- TGA files store their pixels in BGR order rather than RGB. This means that you will need to swap the red and blue channels before writing the TGA pixel data, then swap them back the image remains stored properly in memory. This is an ideal place to use the do_reverse_rgb() function.

File IO: Use the C standard IO methods for binary file IO (fopen, fread, fwrite, etc.). When loading, read in the struct, parse it, allocate space for width*height the pixels, then read in the appropriate number of pixels into this space. You can read the whole TGA file in two fread calls. Similarly, you can write a whole TGA file with two fwrite calls. Also, because we're doing binary IO, be sure to include the "b" flag in your fopen call.

Implementing PPM support

This standard includes six formats: P1 through P6. We'll focus on just the P3 and P6 formats, which are the only two you will support.

Header format: Both of these formats share a single header, which is ASCII (even if the pixel data is in binary, as in format P6). This format is:

P<3 or 6> # comments are possible, they start with a pound sign <WIDTH> <HEGIHT> # more comments possible here <MAX_VALUE>

WIDTH and HEIGHT, predictably, refer to the dimensions of the image, in pixels. MAX_VALUE indicates what numeric value corresponds to full intensity. It is usually 255, the max unsigned byte value. However, if it isn't when reading a file in, you will need to scale all pixels by 255.0/MAX_VALUE.

After the newline following the MAX_VALUE, the actual pixel data is emitted. For P3 format, this is as a series of whitespace-delimited human-readable numbers suitable for reading with scanf. For P6 format, this is a binary dump of the image pixel data, suitable for slurping up with a single call to fread. However, when reading P6 format, make sure you explicitly read all the way to the newline following MAX_VALUE before reading binary data. If you read in the newline as part of the pixel data, your whole image will be off by one byte!

Channel order: Unlike TGA, PPM stores colors in RGB, meaning you can simply read the channel values in the order they appear. In fact, for P6 format, you can fread all the pixels at once.

For more information, see the detailed spec or Wikipedia's Netpbm article.

Testing

When you are ready to run the full suite of tests, execute the test script test.sh. We'll prepare to do this by ensuring that the script (as well as the Python front-end) are marked as being executable: $ chmod +x test.sh pyturtle_test.py

This sets the permissions of test.sh to allow for execution. It also enables execution of pyturtle_test.py, which is a Python script that can be executed directly on the command line. You only have to run this command once.

To actually run the tests: $ ./test.sh

Pushing changes

When you are ready to commit to your local repository, execute:

$ git add .
$ git commit -am "a meaningful message for future you"

When you are ready to submit to the remote repository for automated grading or teaching staff feedback, execute:

$ git push

Testing

Two test suite utilities are provided: cturtle_test.c and pyturtle_test.py. These are functionally identical in their outputs, but the Python version relies on the bindings in CPython.py in addition to your library.

The script test.sh uses these to launch a series of tests. Each test produces image file outputs in all three formats (TGA, ASCII PPM, and binary PPM) using both the C and Python test tools. Your library must emit nothing to stdout or stderr, and it must produce output files which match the expected results exactly for automated grading.

The syntax of both tools is:

./cturtle_test <test#> <outfile> [infile]

The <test#> corresponds to the T## test id numbers listed below.

Testing is divided into "test groups", where each group is seeking to produce a specific image. Each test group has test variants for the C and Python front-ends, and each of the three output formats (TGA, ASCII PPM, and binary PPM). The script will produce files with filenames of the following format: test-out/actual-T05-scaled.a.ppm-c.a.ppm ^^^^^^^^ ^^^^^^ ^^^ ^^^^^^^^^^^^ ^ ^^^^^ | | | | | | Output directory -------+ | | | | | Actual vs. Expected ------------+ | | | | Test algorithm number ---------------+ | | | Input file (if applicable) ------------------+ | | Front end ('c' for C, 'py' for Python) --------------+ | Output number (if applicable, not shown here) | Extension (.tga .a.ppm or .b.ppm) -----------------------+ The example filename above is produced by the following command: $ ./cturtle_test 05 test-out/actual-T05-scaled.a.ppm-c.a.ppm test-in/scaled.a.ppm

Further, when run by test.sh, the stdout and stderr of the program will be logged to "test-out/<test_id>.log". Because the test program writes errors from get_last_error() to stdout, this is how we will check error message output. The test.sh will compare all these outputs to the appropriate file of the form "test-out/expected-*".

Some of the test modes (5,6,7) take a parameter [infile], which is the filename to be read in and possibly manipulated in the test. As with the other cases, the resulting output is written in the test-out/ directory to be verified by the test.sh.

Grading will test additional inputs and outputs, so you should test your program with inputs and outputs beyond the provided examples. The test cases and testing script (e.g., test.sh) are provided for you in hw6_starter.tgz. See the Implementation section for details about setting up your work environment and how to use hw6_starter.tgz.

If your program does NOT pass the tests in test.sh then it will not be able to pass the teaching staff test cases. Use test.sh as a method to verify that at least the minimum requirements have been met for this assignment.

The details of the provided tests are:

Test IDsOutputsDescriptionImage
Test group 'T01'
  Test 'T01-c.tga'
  Test 'T01-c.a.ppm'
  Test 'T01-c.b.ppm'
  Test 'T01-py.tga'
  Test 'T01-py.a.ppm'
  Test 'T01-py.b.ppm'
Image outputs:
  T01-c.tga
  T01-c.a.ppm
  T01-c.b.ppm
  T01-py.tga
  T01-py.a.ppm
  T01-py.b.ppm

Console logs:
  T01-c.tga.log
  T01-c.a.ppm.log
  T01-c.b.ppm.log
  T01-py.tga.log
  T01-py.a.ppm.log
  T01-py.b.ppm.log
Simple 3x5 image, reg/green/blue dots going down the middle, white dots in the corners

Enlarged 10x
Test group 'T02'
  Test 'T02-c.tga'
  Test 'T02-c.a.ppm'
  Test 'T02-c.b.ppm'
  Test 'T02-py.tga'
  Test 'T02-py.a.ppm'
  Test 'T02-py.b.ppm'
Image outputs:
  T02-c.tga
  T02-c.a.ppm
  T02-c.b.ppm
  T02-py.tga
  T02-py.a.ppm
  T02-py.b.ppm

Console logs:
  T02-c.tga.log
  T02-c.a.ppm.log
  T02-c.b.ppm.log
  T02-py.tga.log
  T02-py.a.ppm.log
  T02-py.b.ppm.log
Same as test 1, but invert at the end

Enlarged 10x
Test group 'T03'
  Test 'T03-c.tga'
  Test 'T03-c.a.ppm'
  Test 'T03-c.b.ppm'
  Test 'T03-py.tga'
  Test 'T03-py.a.ppm'
  Test 'T03-py.b.ppm'
Image outputs:
  T03-c.tga
  T03-c.a.ppm
  T03-c.b.ppm
  T03-py.tga
  T03-py.a.ppm
  T03-py.b.ppm

Console logs:
  T03-c.tga.log
  T03-c.a.ppm.log
  T03-c.b.ppm.log
  T03-py.tga.log
  T03-py.a.ppm.log
  T03-py.b.ppm.log
Same as test 1, but reverse rgb at the end

Enlarged 10x
Test group 'T04'
  Test 'T04-c.tga'
  Test 'T04-c.a.ppm'
  Test 'T04-c.b.ppm'
  Test 'T04-py.tga'
  Test 'T04-py.a.ppm'
  Test 'T04-py.b.ppm'
Image outputs:
  T04-c.tga
  T04-c.a.ppm
  T04-c.b.ppm
  T04-py.tga
  T04-py.a.ppm
  T04-py.b.ppm

Console logs:
  T04-c.tga.log
  T04-c.a.ppm.log
  T04-c.b.ppm.log
  T04-py.tga.log
  T04-py.a.ppm.log
  T04-py.b.ppm.log
A colored triforce pattern on the left of a 150x60 dark blue background
Test group 'T05-toucan.tga'
  Test 'T05-toucan.tga-c.tga'
  Test 'T05-toucan.tga-c.a.ppm'
  Test 'T05-toucan.tga-c.b.ppm'
  Test 'T05-toucan.tga-py.tga'
  Test 'T05-toucan.tga-py.a.ppm'
  Test 'T05-toucan.tga-py.b.ppm'
Image outputs:
  T05-toucan.tga-c.tga
  T05-toucan.tga-c.a.ppm
  T05-toucan.tga-c.b.ppm
  T05-toucan.tga-py.tga
  T05-toucan.tga-py.a.ppm
  T05-toucan.tga-py.b.ppm

Console logs:
  T05-toucan.tga-c.tga.log
  T05-toucan.tga-c.a.ppm.log
  T05-toucan.tga-c.b.ppm.log
  T05-toucan.tga-py.tga.log
  T05-toucan.tga-py.a.ppm.log
  T05-toucan.tga-py.b.ppm.log
Read toucan.tga and output it (no changes)
Test group 'T05-toucan.a.ppm'
  Test 'T05-toucan.a.ppm-c.tga'
  Test 'T05-toucan.a.ppm-c.a.ppm'
  Test 'T05-toucan.a.ppm-c.b.ppm'
  Test 'T05-toucan.a.ppm-py.tga'
  Test 'T05-toucan.a.ppm-py.a.ppm'
  Test 'T05-toucan.a.ppm-py.b.ppm'
Image outputs:
  T05-toucan.a.ppm-c.tga
  T05-toucan.a.ppm-c.a.ppm
  T05-toucan.a.ppm-c.b.ppm
  T05-toucan.a.ppm-py.tga
  T05-toucan.a.ppm-py.a.ppm
  T05-toucan.a.ppm-py.b.ppm

Console logs:
  T05-toucan.a.ppm-c.tga.log
  T05-toucan.a.ppm-c.a.ppm.log
  T05-toucan.a.ppm-c.b.ppm.log
  T05-toucan.a.ppm-py.tga.log
  T05-toucan.a.ppm-py.a.ppm.log
  T05-toucan.a.ppm-py.b.ppm.log
Read toucan.a.ppm (ASCII PPM) and output it (no changes)
Test group 'T05-toucan.b.ppm'
  Test 'T05-toucan.b.ppm-c.tga'
  Test 'T05-toucan.b.ppm-c.a.ppm'
  Test 'T05-toucan.b.ppm-c.b.ppm'
  Test 'T05-toucan.b.ppm-py.tga'
  Test 'T05-toucan.b.ppm-py.a.ppm'
  Test 'T05-toucan.b.ppm-py.b.ppm'
Image outputs:
  T05-toucan.b.ppm-c.tga
  T05-toucan.b.ppm-c.a.ppm
  T05-toucan.b.ppm-c.b.ppm
  T05-toucan.b.ppm-py.tga
  T05-toucan.b.ppm-py.a.ppm
  T05-toucan.b.ppm-py.b.ppm

Console logs:
  T05-toucan.b.ppm-c.tga.log
  T05-toucan.b.ppm-c.a.ppm.log
  T05-toucan.b.ppm-c.b.ppm.log
  T05-toucan.b.ppm-py.tga.log
  T05-toucan.b.ppm-py.a.ppm.log
  T05-toucan.b.ppm-py.b.ppm.log
Read toucan.b.ppm (binary PPM) and output it (no changes)
Test group 'T05-scaled.a.ppm'
  Test 'T05-scaled.a.ppm-c.tga'
  Test 'T05-scaled.a.ppm-c.a.ppm'
  Test 'T05-scaled.a.ppm-c.b.ppm'
  Test 'T05-scaled.a.ppm-py.tga'
  Test 'T05-scaled.a.ppm-py.a.ppm'
  Test 'T05-scaled.a.ppm-py.b.ppm'
Image outputs:
  T05-scaled.a.ppm-c.tga
  T05-scaled.a.ppm-c.a.ppm
  T05-scaled.a.ppm-c.b.ppm
  T05-scaled.a.ppm-py.tga
  T05-scaled.a.ppm-py.a.ppm
  T05-scaled.a.ppm-py.b.ppm

Console logs:
  T05-scaled.a.ppm-c.tga.log
  T05-scaled.a.ppm-c.a.ppm.log
  T05-scaled.a.ppm-c.b.ppm.log
  T05-scaled.a.ppm-py.tga.log
  T05-scaled.a.ppm-py.a.ppm.log
  T05-scaled.a.ppm-py.b.ppm.log
Read scaled.a.ppm (an ASCII PPM with a non-255 max value) and output it as the test output files (no changes)

Enlarged 10x
Test group 'T05-pgm_unsupp_grey.ppm'
  Test 'T05-pgm_unsupp_grey.ppm-c.tga'
  Test 'T05-pgm_unsupp_grey.ppm-py.tga'
Image outputs:
  (none)
Console logs:
  T05-pgm_unsupp_grey.ppm-c.tga.log
  T05-pgm_unsupp_grey.ppm-py.tga.log
Attempt to read pgm_unsupp_grey.ppm, failing because it is an unsupported type n/a
Test group 'T05-tga_unsupp_colormap.tga'
  Test 'T05-tga_unsupp_colormap.tga-c.tga'
  Test 'T05-tga_unsupp_colormap.tga-py.tga'
Image outputs:
  (none)
Console logs:
  T05-tga_unsupp_colormap.tga-c.tga.log
  T05-tga_unsupp_colormap.tga-py.tga.log
Attempt to read tga_unsupp_colormap.tga, failing because it has a color map, which is unsupported n/a
Test group 'T05-tga_unsupp_compression.tga'
  Test 'T05-tga_unsupp_compression.tga-c.tga'
  Test 'T05-tga_unsupp_compression.tga-py.tga'
Image outputs:
  (none)
Console logs:
  T05-tga_unsupp_compression.tga-c.tga.log
  T05-tga_unsupp_compression.tga-py.tga.log
Attempt to read tga_unsupp_compression.tga, failing because it is compressed, which is unsupported n/a
Test group 'T05-tga_unsupp_image_desc.tga'
  Test 'T05-tga_unsupp_image_desc.tga-c.tga'
  Test 'T05-tga_unsupp_image_desc.tga-py.tga'
Image outputs:
  (none)
Console logs:
  T05-tga_unsupp_image_desc.tga-c.tga.log
  T05-tga_unsupp_image_desc.tga-py.tga.log
Attempt to read tga_unsupp_image_desc.tga, failing because it has an unsupported image descriptor value n/a
Test group 'T06-toucan.a.ppm'
  Test 'T06-toucan.a.ppm-c.tga'
  Test 'T06-toucan.a.ppm-c.a.ppm'
  Test 'T06-toucan.a.ppm-c.b.ppm'
  Test 'T06-toucan.a.ppm-py.tga'
  Test 'T06-toucan.a.ppm-py.a.ppm'
  Test 'T06-toucan.a.ppm-py.b.ppm'
Image outputs:
  T06-toucan.a.ppm-c.tga
  T06-toucan.a.ppm-c.a.ppm
  T06-toucan.a.ppm-c.b.ppm
  T06-toucan.a.ppm-py.tga
  T06-toucan.a.ppm-py.a.ppm
  T06-toucan.a.ppm-py.b.ppm

Console logs:
  T06-toucan.a.ppm-c.tga.log
  T06-toucan.a.ppm-c.a.ppm.log
  T06-toucan.a.ppm-c.b.ppm.log
  T06-toucan.a.ppm-py.tga.log
  T06-toucan.a.ppm-py.a.ppm.log
  T06-toucan.a.ppm-py.b.ppm.log
Read toucan.a.ppm (ASCII PPM) and output it with rgb reversed
Test group 'T07-toucan.a.ppm'
  Test 'T07-toucan.a.ppm-c.tga'
  Test 'T07-toucan.a.ppm-c.a.ppm'
  Test 'T07-toucan.a.ppm-c.b.ppm'
  Test 'T07-toucan.a.ppm-py.tga'
  Test 'T07-toucan.a.ppm-py.a.ppm'
  Test 'T07-toucan.a.ppm-py.b.ppm'
Image outputs:
  T07-toucan.a.ppm-c.tga
  T07-toucan.a.ppm-c.a.ppm
  T07-toucan.a.ppm-c.b.ppm
  T07-toucan.a.ppm-py.tga
  T07-toucan.a.ppm-py.a.ppm
  T07-toucan.a.ppm-py.b.ppm

Console logs:
  T07-toucan.a.ppm-c.tga.log
  T07-toucan.a.ppm-c.a.ppm.log
  T07-toucan.a.ppm-c.b.ppm.log
  T07-toucan.a.ppm-py.tga.log
  T07-toucan.a.ppm-py.a.ppm.log
  T07-toucan.a.ppm-py.b.ppm.log
Read toucan.a.ppm (ASCII PPM) and invert it, then draw most of test 2 on top of it
Test group 'T08'
  Test 'T08-c.tga'
  Test 'T08-c.a.ppm'
  Test 'T08-c.b.ppm'
  Test 'T08-py.tga'
  Test 'T08-py.a.ppm'
  Test 'T08-py.b.ppm'
Image outputs:
  T08-c.tga
  T08-c.a.ppm
  T08-c.b.ppm
  T08-py.tga
  T08-py.a.ppm
  T08-py.b.ppm

Console logs:
  T08-c.tga.log
  T08-c.a.ppm.log
  T08-c.b.ppm.log
  T08-py.tga.log
  T08-py.a.ppm.log
  T08-py.b.ppm.log
The "hello world" of turtle graphics -- drawing one line with the default settings in the default direction
Test group 'T09'
  Test 'T09-c.tga'
  Test 'T09-c.a.ppm'
  Test 'T09-c.b.ppm'
  Test 'T09-py.tga'
  Test 'T09-py.a.ppm'
  Test 'T09-py.b.ppm'
Image outputs:
  T09-c.tga
  T09-c.a.ppm
  T09-c.b.ppm
  T09-py.tga
  T09-py.a.ppm
  T09-py.b.ppm

Console logs:
  T09-c.tga.log
  T09-c.a.ppm.log
  T09-c.b.ppm.log
  T09-py.tga.log
  T09-py.a.ppm.log
  T09-py.b.ppm.log
Same as 8, but turn a bit first
Test group 'T10'
  Test 'T10-c.tga'
  Test 'T10-c.a.ppm'
  Test 'T10-c.b.ppm'
  Test 'T10-py.tga'
  Test 'T10-py.a.ppm'
  Test 'T10-py.b.ppm'
Image outputs:
  T10-c.tga
  T10-c.a.ppm
  T10-c.b.ppm
  T10-py.tga
  T10-py.a.ppm
  T10-py.b.ppm

Console logs:
  T10-c.tga.log
  T10-c.a.ppm.log
  T10-c.b.ppm.log
  T10-py.tga.log
  T10-py.a.ppm.log
  T10-py.b.ppm.log
Simple triangle with different colors on each edge (red/orange/yellow)
Test group 'T11'
  Test 'T11-c.tga'
  Test 'T11-c.a.ppm'
  Test 'T11-c.b.ppm'
  Test 'T11-py.tga'
  Test 'T11-py.a.ppm'
  Test 'T11-py.b.ppm'
Image outputs:
  T11-c.tga
  T11-c.a.ppm
  T11-c.b.ppm
  T11-py.tga
  T11-py.a.ppm
  T11-py.b.ppm

Console logs:
  T11-c.tga.log
  T11-c.a.ppm.log
  T11-c.b.ppm.log
  T11-py.tga.log
  T11-py.a.ppm.log
  T11-py.b.ppm.log
Draw a magenta box with a cyan X on it a bit off center
Test group 'T12'
  Test 'T12-c.tga'
  Test 'T12-c.a.ppm'
  Test 'T12-c.b.ppm'
  Test 'T12-py.tga'
  Test 'T12-py.a.ppm'
  Test 'T12-py.b.ppm'
Image outputs:
  T12-c.tga
  T12-c.a.ppm
  T12-c.b.ppm
  T12-py.tga
  T12-py.a.ppm
  T12-py.b.ppm

Console logs:
  T12-c.tga.log
  T12-c.a.ppm.log
  T12-c.b.ppm.log
  T12-py.tga.log
  T12-py.a.ppm.log
  T12-py.b.ppm.log
Draw a "zipper" type shape going from blue to cyan
Test group 'T13'
  Test 'T13-c.tga'
  Test 'T13-c.a.ppm'
  Test 'T13-c.b.ppm'
  Test 'T13-py.tga'
  Test 'T13-py.a.ppm'
  Test 'T13-py.b.ppm'
Image outputs:
  T13-c.tga
  T13-c.a.ppm
  T13-c.b.ppm
  T13-py.tga
  T13-py.a.ppm
  T13-py.b.ppm

Console logs:
  T13-c.tga.log
  T13-c.a.ppm.log
  T13-c.b.ppm.log
  T13-py.tga.log
  T13-py.a.ppm.log
  T13-py.b.ppm.log
Draw a dashed hexagon
Test group 'T14'
  Test 'T14-c.tga'
  Test 'T14-c.a.ppm'
  Test 'T14-c.b.ppm'
  Test 'T14-py.tga'
  Test 'T14-py.a.ppm'
  Test 'T14-py.b.ppm'
Image outputs:
  T14-c.tga
  T14-c.a.ppm
  T14-c.b.ppm
  T14-py.tga
  T14-py.a.ppm
  T14-py.b.ppm

Console logs:
  T14-c.tga.log
  T14-c.a.ppm.log
  T14-c.b.ppm.log
  T14-py.tga.log
  T14-py.a.ppm.log
  T14-py.b.ppm.log
Draw a totally sweet hilbert curve fractal
Test group 'T15'
  Test 'T15-c.tga'
  Test 'T15-c.a.ppm'
  Test 'T15-c.b.ppm'
  Test 'T15-py.tga'
  Test 'T15-py.a.ppm'
  Test 'T15-py.b.ppm'
Image outputs:
  T15-1-c.tga
  T15-1-c.a.ppm
  T15-1-c.b.ppm
  T15-1-py.tga
  T15-1-py.a.ppm
  T15-1-py.b.ppm
  T15-2-c.tga
  T15-2-c.a.ppm
  T15-2-c.b.ppm
  T15-2-py.tga
  T15-2-py.a.ppm
  T15-2-py.b.ppm

Console logs:
  T15-c.tga.log
  T15-c.a.ppm.log
  T15-c.b.ppm.log
  T15-py.tga.log
  T15-py.a.ppm.log
  T15-py.b.ppm.log
Make two images at once

Instructor tests

In this assignment, you will have limited access to the instructor tests. Jenkins will be configured to provide "workspace" access, but the only evidence of the instructor tests will be the outputs. Consider these to be submitted materials coming from your "users", along with the brief information below.


Test IDsOutputsDescriptionImage
Test group 'I01'
  Test 'I01-py.tga'
  Test 'I01-py.a.ppm'
  Test 'I01-py.b.ppm'
Image outputs:
  I01-py.tga
  I01-py.a.ppm
  I01-py.b.ppm

Console logs:
  I01-py.tga.log
  I01-py.a.ppm.log
  I01-py.b.ppm.log
Color map with do_pixel
Test group 'I02'
  Test 'I02-py.tga'
  Test 'I02-py.a.ppm'
  Test 'I02-py.b.ppm'
Image outputs:
  I02-1-py.tga
  I02-1-py.a.ppm
  I02-1-py.b.ppm
  I02-2-py.tga
  I02-2-py.a.ppm
  I02-2-py.b.ppm
  I02-3-py.tga
  I02-3-py.a.ppm
  I02-3-py.b.ppm

Console logs:
  I02-py.tga.log
  I02-py.a.ppm.log
  I02-py.b.ppm.log
3 images at once



Test group 'I03'
  Test 'I03-py.tga'
  Test 'I03-py.a.ppm'
  Test 'I03-py.b.ppm'
Image outputs:
  I03-1-py.tga
  I03-1-py.a.ppm
  I03-1-py.b.ppm
  I03-2-py.tga
  I03-2-py.a.ppm
  I03-2-py.b.ppm
  I03-3-py.tga
  I03-3-py.a.ppm
  I03-3-py.b.ppm

Console logs:
  I03-py.tga.log
  I03-py.a.ppm.log
  I03-py.b.ppm.log
Simple image test written 3 times

Enlarged 10x
Test group 'I04'
  Test 'I04-py.tga'
  Test 'I04-py.a.ppm'
  Test 'I04-py.b.ppm'
Image outputs:
  I04-py.tga
  I04-py.a.ppm
  I04-py.b.ppm

Console logs:
  I04-py.tga.log
  I04-py.a.ppm.log
  I04-py.b.ppm.log
A blue/cyan spiral with some pen up/down
Test group 'I05'
  Test 'I05-py.tga'
  Test 'I05-py.a.ppm'
  Test 'I05-py.b.ppm'
Image outputs:
  I05-py.tga
  I05-py.a.ppm
  I05-py.b.ppm

Console logs:
  I05-py.tga.log
  I05-py.a.ppm.log
  I05-py.b.ppm.log
A more complex spiral
Test group 'I06'
  Test 'I06-py.tga'
  Test 'I06-py.a.ppm'
  Test 'I06-py.b.ppm'
Image outputs:
  I06-py.tga
  I06-py.a.ppm
  I06-py.b.ppm

Console logs:
  I06-py.tga.log
  I06-py.a.ppm.log
  I06-py.b.ppm.log
Draw a multi-colored border exactly on the edge of the image using do_line
Test group 'I07'
  Test 'I07-py.tga'
  Test 'I07-py.a.ppm'
  Test 'I07-py.b.ppm'
Image outputs:
  I07-py.tga
  I07-py.a.ppm
  I07-py.b.ppm

Console logs:
  I07-py.tga.log
  I07-py.a.ppm.log
  I07-py.b.ppm.log
Draw a multi-colored border exactly on the edge of the image using turtle graphics
Test group 'I08'
  Test 'I08-py.tga'
  Test 'I08-py.a.ppm'
  Test 'I08-py.b.ppm'
Image outputs:
  I08-py.tga
  I08-py.a.ppm
  I08-py.b.ppm

Console logs:
  I08-py.tga.log
  I08-py.a.ppm.log
  I08-py.b.ppm.log
Go out of bounds with the turtle
Test group 'I09-bad1.a.ppm'
  Test 'I09-bad1.a.ppm-py.tga'
Image outputs:
  (none)
Console logs:
  I09-bad1.a.ppm-py.tga.log
Try to read an awfully bad ppm file n/a
Test group 'I10'
  Test 'I10-py.tga'
  Test 'I10-py.a.ppm'
  Test 'I10-py.b.ppm'
Image outputs:
  I10-py.tga
  I10-py.a.ppm
  I10-py.b.ppm

Console logs:
  I10-py.tga.log
  I10-py.a.ppm.log
  I10-py.b.ppm.log
Test of drawing pixels off each edge with do_pixel (only the brightest lines try to go off the edge)
Test group 'IVxx'
  Test 'IVxx-c.tga'
  Test 'IVxx-c.a.ppm'
  Test 'IVxx-c.b.ppm'
Image outputs:
  IVxx-c.tga
  IVxx-c.a.ppm
  IVxx-c.b.ppm

Console logs:
  IVxx-c.tga.log
  IVxx-c.a.ppm.log
  IVxx-c.b.ppm.log
Similar to the given Txx tests, but with a valgrind check n/a

Checking Jenkins Feedback

We have created a Jenkins build job for you. Jenkins is a continuous integration server that is used in industry to compile, build, and test applications under development. We will be using Jenkins to compile, build, and test your homework submissions to GitHub, which will provide early feedback on the completeness (does your implementation meet the requirements) and quality (does your implementation correctly implement the requirements).

Your Jenkins job is associated with your GitHub repository and will poll or query GitHub every two minutes for changes. After you have pushed code to GitHub, Jenkins will notice the change and automatically start a build process on your code. The following actions will occur:

Jenkins will record the results of each execution. To obtain your Jenkins feedback, do the following tasks:

NOTE: Jenkins will NOT execute test.sh without evidence that you have run the tests locally. We require that at least one file matching the regular expression test-out/actual-* is pushed to GitHub for automated testing to run.

Instructions for Submission

The following files MUST be pushed to your assigned CSC230 GitHub repository:

By submitting the actual results from the tests, you will prove that you have tested your program with the minimum set of the provided acceptance tests. Automated grading will not run on your program without at least one generated actual result file (it doesn't mean your tests have to pass, just that you've attempted the tests locally before pushing to your repo).

Additional Considerations

Follow the CSC 230 Style Guidelines.  Make sure your program compiles on the common platform and Jenkins cleanly (no errors or warnings), with the required compile options.

There are certain learning outcomes and basic software engineering skills that this assignment is assessing. See the rubric below for deductions may be applied to your submission as enforcement of good software engineering practices and assignment intentions.

Make sure that you push your code to the GitHub repository provided for you! Pushing your code to GitHub is your submission!

There is a 24 hour window for late submissions.  After the main deadline, continue to submit to GitHub. We will use the last commit to GitHub before the late deadline for grading and the timestamp of that commit will determine a deduction, if any.

Rubric

+20 for compiling on the common platform with gcc –Wall –std=c99 options

+60 for passing teaching staff test cases (all tests will be done using a diff of output files), which demonstrates a correct implementation.

+10 for artistic expression (all or nothing)

+20 for comments, style, and constants

-5 for meaningless or missing comments

-5 for inconsistent formatting

-5 for magic numbers

-5 for no name in comments

Total: 110 points

Global deductions FROM the score you earn above:

-15 points for any compilation warnings. Your program must compile cleanly! (if it doesn't compile, you will not receive any credit for test cases or compilation)

-60 points for using library functions outside of ANSI C99 or for using system() -- This means that you may have circumvented the intention of the assignment and receive no points for a correct implementation.

-10 points for files that are named incorrectly or missing.

-20 points for late work, even if you submit part of the assignment on time.

You can not receive less than a 0 for this assignment unless an academic integrity violation occurs.