UQ Students should read the Disclaimer & Warning
Note: This page dates from 2005, and is kept for historical purposes.
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>COMP2301 - Assignment 1 - C Programming (Stopwatch)</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<style type="text/css">
<!--
.wrong {
background: #FF9999;
}
body {
background: url(_img/DSC04989.jpg) fixed center;
font-family: "Arial Unicode MS", Arial, Helvetica, sans-serif;
}
th, td, textarea {
border: 1px solid #000000;
padding: 0 1ex;
background: transparent;
overflow: hidden;
}
table {
border: none;
}
-->
</style>
</head>
<body>
<h1>COMP2301 – Assignment One – C Programming (Stopwatch)</h1>
<p>This assignment is a pass/fail assignment. I achieved a pass. </p>
<p>The goal of this assignment is write C code to generate on screen graphics
representing a simple analogue / digital stopwatch . </p>
<p>The application must have the following features:</p>
<ul>
<li>Stopwatch face must have two hands. (Seconds, Minutes) </li>
<li>Stopwatch face must have a circular outline </li>
<li>Stopwatch face must contain a digital minutes : seconds display </li>
<li>Second hand must sweep behind the digital display </li>
<li>Code must be modular and make good use of C functions </li>
<li>All graphics must be generated by pixel manipulation only </li>
<li>Code comments must correctly reference the work of others </li>
</ul>
<p><a href=".//COMP2301-assignment-1-stopwatch.exe" title="Downloadable application - Stopwatch">Compiled
(WIN32) binary</a> (189 KB) <br />
<a href="#h">Assignment-1.h file</a><br />
<a href="#c">Assignment-1.c file</a></p>
<p id="h"> Assignment-1.h<br />
<textarea name="h" cols="82" rows="257" readonly="readonly" title="C Code - Copyright 2004 Ned Martin">/*
* Author: Ned Martin #40529927
*
* Creation Date: 13-MAR-2004
*
* Copyright: Ned Martin, Student s40529927, for COMP2301,
* University of Queensland
*
* Description: Assignment 1 for COMP2301
* Draws a clock face, second and minute hands, and digital
* display, and maintains a count of the minutes and
* seconds elapsed, updating its display to match this.
*
* Tab Stops: Eight spaces
*/
/*
* Defines constants used throughout
*/
const double
PI = 3.1415926535897932384626433832795; // pi used in circle math
/*
* Colour constants, defining colours used in drawing the clock
*/
const int
COLOUR_BG = 0xFFFFFF, // colour of background - this colour
// must match the background of the
// window canvas
COLOUR_SECONDS = 0xFF, // colour of second hand
COLOUR_MINUTES = 0x0, // colour of minute hand
COLOUR_TICKS = 0x0, // colour of the border and time marks
COLOUR_DIGIT = 0x2C3C35, // colour of the digits
COLOUR_DIGIT_UNLIT = 0xA7B7A8, // colour of the unlit digits
COLOUR_DIGIT_BG = 0xD0E0CF; // colour of the digital background
/*
* Position constants used to define positions and sizes of elements when
* drawing the clock
*/
const int
RADIUS = 100, // radius of the clock face
CENTRE_X = 200, // x centre of clock face
CENTRE_Y = 200, // y center of clock face
MINUTE_HAND_WIDTH = 2, // width of hands
SECOND_HAND_WIDTH = 1, // width of hands
TICK_WIDTH = 1, // width of ticks
DIGIT_WIDTH = 3, // width of digits for digital display
DIGIT_SCALE = 10, // scale factor for digital display
DIGIT_OFFSET_X = 150, // x offset for digital display
DIGIT_OFFSET_Y = 230, // y offset for digital display
DIGIT_SPACING = 10; // spacing between digits
// fix compiler name mangling
#ifdef __cplusplus
extern "C"
{
#endif
/*
* Function: incrementTime
*
* Purpose: Increments a time counter.
*
* Method: Called from a timer, increments a second and minute counter.
*
* Returns: None
*/
void incrementTime();
/*
* Function: changeTime
*
* Purpose: Changes the time displayed on the clock, redrawing the hands and
* digital display if necessary.
*
* Method: Accepts a handle to the window canvas and calls various
* procedures to update the minute and second hands and digital
* display.
*
* Returns: None
*/
void changeTime
(
HDC hdc // window canvas
);
/*
* Function: drawDigital
*
* Purpose: Draws a digital display representing time elapsed.
*
* Method: Accepts a handle to the window canvas and calls drawDigit with
* arguments to draw a four digit digital display and blanks out
* previous digits by calling drawDigit to draw an eight using the
* background colour.
*
* Returns: None
*/
void drawDigital
(
HDC hdc // window canvas
);
/*
* Function: drawHand
*
* Purpose: Draws second and minute hands.
*
* Method: Accepts a handle to the window canvas, a seconds value, length
* and colour and draws a line from a specified centre value with
* the given length, width and colour the given number of seconds
* around a circle.
*
* Returns: None
*/
void drawHand
(
HDC hdc, // window canvas
int seconds, // second to draw hand at
int length, // length to draw hand
int width, // width to draw hand
int colour // colour to draw hand
);
/*
* Function: drawClockFace
*
* Purpose: Draws a representation of an analogue clock face.
*
* Method: Accepts a handle to the window canvas and draws ticks in the
* seconds position around a circle, with ticks in the 15 and 5
* second positions being longer.
*
* Returns: None
*/
void drawClockFace
(
HDC hdc // window canvas
);
/*
* Function: drawCircle
*
* Purpose: Draws a circle.
*
* Method: Accepts a handle to the window canvas, centre coordinates,
* radius and a colour and draws a circle with the given
* dimensions.
*
* Returns: None
*/
void drawCircle
(
HDC hdc, // window canvas
int centreX, // x coordinate of centre of circle
int centreY, // y coordinate of centre of circle
int radius, // circle radius
int colour // circle colour
);
/*
* Function: drawLine
*
* Purpose: Draws a straight line.
*
* Method: Accepts a handle to the window canvas, start and end
* coordinates, a width and a colour and draws a straight line of
* that width and colour from the start to the end coordinates
* given.
*
* Returns: None
*
* Reference: Thanks to Matt Koczorowski for help getting my mind around the
* principles of drawing a straight line at the pixel level.
* (Koczorowski, Matt 2004, verbal communication, 11 March)
*/
void drawLine
(
HDC hdc, // window canvas
int startX, // start x coordinate
int startY, // start y coordinate
int endX, // end x coordinate
int endY, // end y coordinate
int lineWidth, // width of line
int colour // colour of line
);
/*
* Function: drawSegment
*
* Purpose: Draws a segment of a standard digital type display.
*
* Method: Accepts a handle to the window canvas, segment index, position
* offset and colour and draws a the correct segment at the
* position given and with the given colour, using a constant scale
* factor and offset to determine the segment size.
*
* Returns: None
*/
void drawSegment
(
HDC hdc, // window canvas
int segment, // segment to draw (1 to 7)
int position, // position to draw segment
int colour // colour to draw segment
);
/*
* Function: drawDigit
*
* Purpose: Draws a digital display type digit.
*
* Method: Accepts a handle to the window canvas, a digit, position and
* colour and calls drawSegment with the appropriate segments to
* draw the given digit with the given colour and in the given
* position.
*
* Returns: None
*/
void drawDigit
(
HDC hdc, // window canvas
int digit, // digit to draw
int position, // position to draw the digit
int colour // colour to draw the digit
);
/*
* Function: fillRectangle
*
* Purpose: Draws a filled rectangle with a border.
*
* Method: Accepts a handle to the window canvas, start and end
* coordinates, border colour and a fill colour and draws a
* rectangle from the top left start coordinates to the bottom
* right end coordinates filled with the given colour and with a
* border of the given border colour.
*
* Returns: None
*/
void fillRectangle
(
HDC hdc, // window canvas
int startX, // x coordinate to begin rectangle
int startY, // y coordinate to begin rectangle
int endX, // x coordinate to end rectangle
int endY, // y coordinate to end rectangle
int borderColour, // colour of the border
int colour // colour to draw and fill rectangle
);
#ifdef __cplusplus
}
#endif
</textarea>
</p>
<p id="c"> Assignment-1.c<br />
<textarea name="c" cols="82" rows="563" readonly="readonly" title="C Code - Copyright 2004 Ned Martin">/*
* Author: Ned Martin #40529927
*
* Creation Date: 13-MAR-2004
*
* Copyright: Ned Martin, Student s40529927, for COMP2301,
* University of Queensland
*
* Description: Assignment 1 for COMP2301
* Draws a clock face, second and minute hands, and digital
* display, and maintains a count of the minutes and
* seconds elapsed, updating its display to match this.
*
* Tab Stops: Eight spaces
*/
#include <math.h> // cos, sin, sqrt
#include "StdAfx.h" // for 'hdc'
#include "s40529927_1.h" // function prototypes, constants
int gElapsedSeconds = 0, // seconds elapsed since start
gElapsedMinutes = 0; // minutes elapsed since start
/*
* Function: incrementTime
*
* Purpose: Increments a time counter.
*
* Method: Called from a timer, increments a second and minute counter.
*
* Returns: None
*/
void incrementTime()
{
// increment the second counter but reset to 0 after 59
gElapsedSeconds++;
gElapsedSeconds = gElapsedSeconds % 60;
//every 60 seconds we need to increment minute counter
if (!gElapsedSeconds) // every 60 seconds when seconds == 0
{
// increment the minute counter but reset to 0 after 59
gElapsedMinutes++;
gElapsedMinutes = gElapsedMinutes % 60;
}
} // end incrementTime
/*
* Function: changeTime
*
* Purpose: Changes the time displayed on the clock, redrawing the hands and
* digital display if necessary.
*
* Method: Accepts a handle to the window canvas and calls various
* procedures to update the minute and second hands and digital
* display.
*
* Returns: None
*/
void changeTime
(
HDC hdc // window canvas
)
{
// clear previous second hand
drawHand(hdc, gElapsedSeconds - 1, RADIUS, SECOND_HAND_WIDTH,
COLOUR_BG);
// clear previous minute hand
drawHand(hdc, gElapsedMinutes - 1, RADIUS - 15, MINUTE_HAND_WIDTH,
COLOUR_BG);
// draw minute hand
drawHand(hdc, gElapsedMinutes, RADIUS - 15, MINUTE_HAND_WIDTH,
COLOUR_MINUTES);
// draw second hand
drawHand(hdc, gElapsedSeconds, RADIUS, SECOND_HAND_WIDTH,
COLOUR_SECONDS);
// draw rectangle for digital display
fillRectangle(hdc, DIGIT_OFFSET_X - 10, DIGIT_OFFSET_Y - 10,
DIGIT_OFFSET_X + 5 * (DIGIT_SCALE + DIGIT_SPACING) -
DIGIT_SPACING + 1 + 10,
DIGIT_OFFSET_Y + 2 * DIGIT_SCALE + 1 + 10,
COLOUR_TICKS, COLOUR_DIGIT_BG);
// draw digital display
drawDigital(hdc);
} // end changeTime
/*
* Function: drawDigital
*
* Purpose: Draws a digital display representing time elapsed.
*
* Method: Accepts a handle to the window canvas and calls drawDigit with
* arguments to draw a four digit digital display and blanks out
* previous digits by calling drawDigit to draw an eight using the
* background colour.
*
* Returns: None
*/
void drawDigital
(
HDC hdc // window canvas
)
{
// holds tens value of gElapsedSeconds
int secondTens = gElapsedSeconds / 10;
// holds ones value of gElapsedSeconds
int secondOnes = gElapsedSeconds - (10 * secondTens);
// holds tens value of gElapsedMinutes
int minuteTens = gElapsedMinutes / 10;
// holds ones value of gElapsedMinutes
int minuteOnes = gElapsedMinutes - (10 * minuteTens);
// draw tens minute digit, overwriting old digit first
drawDigit(hdc, 8, 0, COLOUR_DIGIT_UNLIT);
drawDigit(hdc, minuteTens, 0, COLOUR_DIGIT);
// draw ones minute digit, overwriting old digit first
drawDigit(hdc, 8, 1, COLOUR_DIGIT_UNLIT);
drawDigit(hdc, minuteOnes, 1, COLOUR_DIGIT);
// draw tens second digit, overwriting old digit first
drawDigit(hdc, 8, 3, COLOUR_DIGIT_UNLIT);
drawDigit(hdc, secondTens, 3, COLOUR_DIGIT);
// draw ones second digit, overwriting old digit first
drawDigit(hdc, 8, 4, COLOUR_DIGIT_UNLIT);
drawDigit(hdc, secondOnes, 4, COLOUR_DIGIT);
} // end drawDigital
/*
* Function: drawHand
*
* Purpose: Draws second and minute hands.
*
* Method: Accepts a handle to the window canvas, a seconds value, length
* and colour and draws a line from a specified centre value with
* the given length, width and colour the given number of seconds
* around a circle.
*
* Returns: None
*/
void drawHand
(
HDC hdc, // window canvas
int seconds, // second to draw hand at
int length, // length to draw hand
int width, // width to draw hand
int colour // colour to draw hand
)
{
// calculate coordinates for hand around a circle
int xx = (int) (length * cos(PI * (seconds - 15) / 30));
int yy = (int) (length * sin(PI * (seconds - 15) / 30));
// draw hand
drawLine(hdc, CENTRE_X, CENTRE_Y, xx + CENTRE_X, yy + CENTRE_Y,
width, colour);
} // end drawHand
/*
* Function: drawClockFace
*
* Purpose: Draws a representation of an analogue clock face.
*
* Method: Accepts a handle to the window canvas and draws ticks in the
* seconds position around a circle, with ticks in the 15 and 5
* second positions being longer.
*
* Returns: None
*/
void drawClockFace
(
HDC hdc // window canvas
)
{
// Draw the clock face
int startX, // start x coordinate
startY, // start y coordinate
endX, // end x coordinate
endY, // end y coordinate
tickSpace = 10, // space between end of hands and start
// of ticks
tickLength = 10, // length to draw clock face ticks
ii; // counter variable
// loop through 60 ii to draw clockface
for (ii = 0; ii < 60; ii++) // 60 seconds around the clockface
{
if (!(ii % 15)) // a 15 second division
{
tickLength = 20;
}
else if (!(ii % 5)) // a 5 second division
{
tickLength = 15;
}
// else set ticklength for small ticks
else tickLength = 10;
// set start positions for ticks
startX = (int) ((RADIUS + tickSpace) * sin(PI * ii/30));
startY = (int) ((RADIUS + tickSpace) * cos(PI * ii/30));
// set end positions for ticks
endX = (int) ((RADIUS + tickSpace + tickLength) *
sin(PI * ii/30));
endY = (int) ((RADIUS + tickSpace + tickLength) *
cos(PI * ii/30));
// draw the ticks
drawLine(hdc, startX + CENTRE_X, startY + CENTRE_Y,
endX + CENTRE_X, endY + CENTRE_Y, TICK_WIDTH,
COLOUR_TICKS);
}
// draw clock face circle
drawCircle(hdc, CENTRE_X, CENTRE_Y, RADIUS + tickSpace + 20,
COLOUR_TICKS);
} // end drawClockFace
/*
* Function: drawCircle
*
* Purpose: Draws a circle.
*
* Method: Accepts a handle to the window canvas, centre coordinates,
* radius and a colour and draws a circle with the given
* dimensions.
*
* Returns: None
*/
void drawCircle
(
HDC hdc, // window canvas
int centreX, // x coordinate of centre of circle
int centreY, // y coordinate of centre of circle
int radius, // circle radius
int colour // circle colour
)
{
int x = 1, // x coordinate
y, // y coordinate
r2 = radius * radius; // radius squared
// patch holes
SetPixel(hdc, centreX, centreY - radius, colour); // top
SetPixel(hdc, centreX - radius, centreY, colour); // left
SetPixel(hdc, centreX, centreY + radius, colour); // bottom
SetPixel(hdc, centreX + radius, centreY, colour); // right
y = (int)(sqrt(r2 - 1) + 0.5);
while (x < y) // draw individual octants
{
SetPixel(hdc, centreX + x, centreY - y, colour); // 1
SetPixel(hdc, centreX + y, centreY - x, colour); // 2
SetPixel(hdc, centreX + y, centreY + x, colour); // 3
SetPixel(hdc, centreX + x, centreY + y, colour); // 4
SetPixel(hdc, centreX - x, centreY + y, colour); // 5
SetPixel(hdc, centreX - y, centreY + x, colour); // 6
SetPixel(hdc, centreX - y, centreY - x, colour); // 7
SetPixel(hdc, centreX - x, centreY - y, colour); // 8
x += 1;
y = (int)(sqrt(r2 - x * x) + 0.5);
}
if (x == y) // patch holes in quadrants
{
SetPixel(hdc, centreX + x, centreY - y, colour); // 1
SetPixel(hdc, centreX + x, centreY + y, colour); // 2
SetPixel(hdc, centreX - x, centreY + y, colour); // 3
SetPixel(hdc, centreX - x, centreY - y, colour); // 4
}
} // end drawCircle
/*
* Function: drawLine
*
* Purpose: Draws a straight line.
*
* Method: Accepts a handle to the window canvas, start and end
* coordinates, a width and a colour and draws a straight line of
* that width and colour from the start to the end coordinates
* given.
*
* Returns: None
*
* Reference: Thanks to Matt Koczorowski for help getting my mind around the
* principles of drawing a straight line at the pixel level.
* (Koczorowski, Matt 2004, verbal communication, 11 March)
*/
void drawLine
(
HDC hdc, // window canvas
int startX, // start x coordinate
int startY, // start y coordinate
int endX, // end x coordinate
int endY, // end y coordinate
int lineWidth, // width of line
int colour // colour of line
)
{
double x = startX, // x coordinate
y = startY; // y coordinate
int ii, jj, kk, // loop variables
width = (endX - startX), // width of rect enclosing line
height = (endY - startY), // height of rect enclosing line
largest; // largest of height or width
// set largest to the larger of the absolute values of height or width
if(abs(width) > abs(height))
{
largest = abs(width);
}
else largest = abs(height);
for (ii = 0; ii < largest; ii++) // all pixels required in line
{
x += (((double)width)/((double)largest));
y += (((double)height)/((double)largest));
// increase the x and y position by the x and y step required
// (one of these will be 1, one of these will be < 1)
for (jj = 0; jj < lineWidth ; jj++) // width of line
{
//draw each pixel in the line, adjusting for size
// purposes
for (kk = 0; kk < lineWidth; kk++ ) // each pixel
{
SetPixel (hdc, (int)(x + jj), (int)(y + kk),
colour);
SetPixel (hdc, (int)(x - jj), (int)(y - kk),
colour);
}
}
}
} // end drawLine
/*
* Function: drawSegment
*
* Purpose: Draws a segment of a standard digital type display.
*
* Method: Accepts a handle to the window canvas, segment index, position
* offset and colour and draws a the correct segment at the
* position given and with the given colour, using a constant scale
* factor and offset to determine the segment size.
*
* Returns: None
*/
void drawSegment
(
HDC hdc, // window canvas
int segment, // segment to draw (1 to 7)
int position, // position to draw segment
int colour // colour to draw segment
)
{
char* coords; // array to hold coordinates
int x1, // start x coordinate
y1, // start y coordinate
x2, // end x coordinate
y2; // end y coordinate
/*
Make a character array to define start and end points of
segments, in format coords[start x, start y, end x, end y]
0 1
0 ------
| |
| |
1 ------
| |
| |
2 ------
*/
switch(segment)
{
case 1:
coords = "0001";
break;
case 2:
coords = "0010";
break;
case 3:
coords = "1011";
break;
case 4:
coords = "0111";
break;
case 5:
coords = "0102";
break;
case 6:
coords = "1112";
break;
case 7:
coords = "0212";
break;
}
// get the the x and y values from the array, subtracting 48 because
// they're ASCII
x1 = (int)(coords[0] - 48); // start x coordinate
y1 = (int)(coords[1] - 48); // start y coordinate
x2 = (int)(coords[2] - 48); // end x coordinate
y2 = (int)(coords[3] - 48); // end y coordinate
// position offset
position = position * (DIGIT_SCALE + DIGIT_SPACING);
// scale segments
x1 = x1 * DIGIT_SCALE + position;
y1 = y1 * DIGIT_SCALE;
x2 = x2 * DIGIT_SCALE + position;
y2 = y2 * DIGIT_SCALE;
// draw the line
drawLine(hdc, x1 + DIGIT_OFFSET_X, y1 + DIGIT_OFFSET_Y,
x2 + DIGIT_OFFSET_X, y2 + DIGIT_OFFSET_Y, DIGIT_WIDTH, colour);
} // end drawSegment
/*
* Function: drawDigit
*
* Purpose: Draws a digital display type digit.
*
* Method: Accepts a handle to the window canvas, a digit, position and
* colour and calls drawSegment with the appropriate segments to
* draw the given digit with the given colour and in the given
* position.
*
* Returns: None
*/
void drawDigit
(
HDC hdc, // window canvas
int digit, // digit to draw
int position, // position to draw the digit
int colour // colour to draw the digit
)
{
char* segments; // array to hold segment flags
int segment, // number of segment to draw
ii; // generic counter
/*
Make a character array to hold segments needed for specific
digits, where segments are numbered as below:
2
------
1 | | 3
| 4 |
------
5 | | 6
| |
------
7
*/
switch(digit)
{
case 1:
segments = "3600000";
break;
case 2:
segments = "2345700";
break;
case 3:
segments = "2346700";
break;
case 4:
segments = "1436000";
break;
case 5:
segments = "2146700";
break;
case 6:
segments = "2146750";
break;
case 7:
segments = "2360000";
break;
case 8:
segments = "1234567";
break;
case 9:
segments = "1234670";
break;
case 0:
segments = "1236750";
break;
default :
break;
}
// get the values of segments to draw from the array, subtracting 48
// because they're ASCII and then draw segments
for(ii = 0; ii < 7; ii++)
{
segment = (int)(segments[ii] - 48);
if(segment != 0)
{
drawSegment(hdc, segment, position, colour);
}
}
} // end drawDigit
/*
* Function: fillRectangle
*
* Purpose: Draws a filled rectangle with a border.
*
* Method: Accepts a handle to the window canvas, start and end
* coordinates, border colour and a fill colour and draws a
* rectangle from the top left start coordinates to the bottom
* right end coordinates filled with the given colour and with a
* border of the given border colour.
*
* Returns: None
*/
void fillRectangle
(
HDC hdc, // window canvas
int startX, // x coordinate to begin rectangle
int startY, // y coordinate to begin rectangle
int endX, // x coordinate to end rectangle
int endY, // y coordinate to end rectangle
int borderColour, // colour of the border
int colour // colour to draw and fill rectangle
)
{
int ii;
for(ii = startY + 1; ii < endY; ii++) // all y
{
drawLine(hdc, startX, ii, endX, ii, 1, colour);
}
// draw border
drawLine(hdc, startX, startY, endX, startY, 1, borderColour); // top
drawLine(hdc, endX, startY, endX, endY, 1, borderColour); // right
drawLine(hdc, startX, endY, endX, endY, 1, borderColour); // bottom
drawLine(hdc, startX, startY, startX, endY, 1, borderColour); // left
} // end fillRectangle
</textarea>
<br />
Code © Copyright 2004 Ned Martin</p>
<p>16-Mar-2004</p>
</body>
</html>