Sketch C++ Project

view the project makefile.


Download Screen.h source file



// Screen.h - Interface for the Screen class.  Since class Point
// and class Screen are inter-dependent, and will be used together,
// it makes sense to provide a single interface (dot-h) file.
//
// Written by (c) Wayne Pollock, 2000.  All Rights Reserved.

#ifndef SCREEN_H
#define SCREEN_H

#include <iostream>

//using namespace std;  // not needed since I used std:: qualified names.

// A simple point class.  Although not necessary, this Point class
// is implemented as a "Concrete Data Type" for illustration purposes.
// Two useful overloaded operators are provided: equality testing ("==")
// and addition.  Although these short functions could be defined inline,
// they are implemented elsewhere, again just for illustration.

struct Point
{   int row;
    int col;

    Point () { row = 0; col = 0; }
    Point ( int r, int c ) { row = r; col = c; }
    Point ( const Point& p ) { row = p.row; col = p.col; }
    Point& operator= ( const Point& p )
	{ row = p.row; col = p.col; return *this; }
    ~Point () {}

    int operator== ( const Point& p ) const;
    Point operator+ ( const Point& ) const;
};


// A Screen is a two dimensional grid of chars.  The Screen keeps track of the
// current char to plot with ("ink"), whose value can be examined and changed
// with get_ink() and set_ink().  Additionally, when a point is plotted on the
// Screen (via "plot()"), an optional argument can be supplied to override the
// ink char used for that point only; zero means to use Screen::ink.
//
//    Note that an attempt to plot points which aren't on the screen is not
// an error; such points are ignored (note that "current_pt" is not updated.)
//
//    A Screen can return its size via "size()"; the value returned is a
// Point whose row member = the maximum row, and likewise for the column.
//
//    A Screen also maintains the coordinates of the last Point ploted. This
// Point is returned via "cur_pt()".  (This is useful for extending lines
// from the end of the previous line and for drawing concentric circles.)
// The current point can be set with via "move_to()".
//
//    Lastly, a Screen can be displayed to some output stream via "display()",
// and a Screen can be erased (cleared, blanked) via "clear()".

class Screen
{
public:
    // Default values for Screens:
    enum { NUM_ROWS = 20, NUM_COLS = 72, INK_DEFAULT = '*' };

    Screen ();
    Screen ( const Screen& );
    Screen& operator= ( const Screen& );
    ~Screen ()  {}

    void clear ( char erase_char = ' ' );
    void display ( std::ostream& = std::cout ) const;
    void draw_circle ( const Point& center, int radius, char ink = 0 );
    void draw_line ( const Point& start_pt, const Point& end_pt, char ink = 0 );
    char get_ink () const { return ink; }
    Point cur_pt () const { return current_pt; }
    void move_to ( const Point& );  // does noting if the Point is off-screen.
    void plot ( const Point&, char ink = 0 );
    void set_ink ( char new_ink ) { ink = new_ink; }
    Point size () const;

private:
    char ink;   // The character to draw with.
    char rep[NUM_ROWS][NUM_COLS];
	Point current_pt;	// The last plotted point.

	void circle_points ( const Point&, const Point&, char temp_ink = 0 );
};

#endif


Download Main.cpp source file


// main.cpp - main for sketch program.
// Written by (c) Wayne Pollock, 2000.  All Rights Reserved.

#include <iostream>
#include <sstream>  // Older compilers require <strstream> or <strstrea>.
#include <ctype>
#include <cstring>
#include "screen.h"

using namespace std;

void parse_circle_cmd ( istringstream& cmd );
void parse_line_cmd ( istringstream& cmd );
void display_help ();
void pause ();  // Wait for user to hit Enter.

Screen scrn;	// This project uses only one screen object.

int main ()
{
    char buf[256], cmd[256];
    scrn.clear( '.' );  // start with empty screen.

    for ( ;; )
    {	try
	{   scrn.display();
	    cerr << "\n\tEnter a command ('help' for brief help): " << flush;
	    if ( !cin.getline( buf, sizeof (buf) ) )
		break;
	    istringstream ibuf( buf );
	    if ( !(ibuf >> cmd) )
		continue;	//A blank line is skipped.

	    for ( unsigned int i=0; i < strlen(cmd); ++i ) // Capitalize command
		cmd[i] = (char) toupper( cmd[i] );

	    if ( strcoll( cmd, "CLEAR" ) == 0 ) {
		scrn.clear( '.' );
	    } else if ( strcoll( cmd, "EXIT" ) == 0 ) {
		return 0;
	    } else if ( strcoll( cmd, "HELP" ) == 0 || strcoll( cmd, "?" ) == 0 ) {
		display_help();
	    } else if ( strcoll( cmd, "LINE" ) == 0 ) {
		parse_line_cmd( ibuf );
	    } else if ( strcoll( cmd, "CIRCLE" ) == 0 ) {
		parse_circle_cmd( ibuf );
	    } else {
		char err[256] = "Unrecognized command: ";
		strcat( err, cmd );
		throw err;
	    }
	}
	catch ( char* msg )
	{   cerr << "*** Error: " << msg << "!\a" << endl;
	    pause();
	}
    } // end for

    return 0;	// If we get to here, an EOF was encountered!
}


// This code allows either two or four values:
void parse_line_cmd ( istringstream& cmd )
{
    int start_row, start_col, end_row, end_col;
    Point start_pt, end_pt;
    char junk[256];

    if ( !(cmd >> start_row) ) {
	throw "Bad first argument to Line";
    } else if ( !(cmd >> start_col) ) {
	throw "Bad second argument to Line";
    } else if ( !(cmd >> end_row) ) {	// Assume a relative line!
	end_pt = scrn.cur_pt() + Point( start_row, start_col );
	start_pt = scrn.cur_pt();
    } else if ( !(cmd >> end_col) ) {
	throw "Bad fourth argument to Line";
    } else if ( cmd >> junk ) {
	throw "Extra arguments to Line";
    } else {
	start_pt = Point( start_row, start_col );
	end_pt = Point( end_row, end_col );
    }
    scrn.draw_line( start_pt, end_pt );
    return;
}


// This code is tricky, since you can supply radius only, or row, col, radius:
void parse_circle_cmd ( istringstream& cmd )
{
    int arg1, arg2, arg3, radius;
    char junk[256];
    Point center;

    if ( !(cmd >> arg1) ) {
	throw "Bad first argument to Circle";
    } else if ( !(cmd >> arg2) ) {
	if ( cmd >> junk )
	    throw "Bad second argument to Circle";
	center = scrn.cur_pt();  // Else assume a relative circle.
	radius = arg1;
    } else if ( !(cmd >> arg3) ) {
	throw "Bad third argument to Circle";
    } else if ( cmd >> junk ) {
	throw "Extra arguments to Circle";
    } else {
	center = Point( arg1, arg2 );
	radius = arg3;
    }
    if ( radius <= 0 )
	throw "The circle's radius must be greater than zero";
   scrn.draw_circle( center, radius );
   return;
}


void display_help ()
{
    cerr  << "\n\n\n\n\n\nSummary of Legal Commands:\n" << endl;
    cerr  << "\tClear  -  erase the screen.\n" << endl;
    cerr  << "\tCircle <center_row> <center_col> <radius>\n"
	  << "\tCircle <radius>  - draws a circle centered on "
	     "the \"current point\".\n" << endl;
    cerr  << "\tExit   -  quit the program.\n" << endl;
    cerr  << "\tHelp (or \"?\") - display this message.\n" << endl;
    cerr  << "\tLine <start_row> <start_col> <end_row> <end_col>\n"
	  << "\tLine <row> <col>  - draws a line from "
	     "the \"current point\" to here.\n" << endl;
    cerr  << "\n\n\n\n" << endl;
    pause();
}


void pause ()
{
    char line[256];
    cerr << "\nHit Enter to continue..." << flush;
    if ( !cin.getline( line, sizeof( line ) ) )
	cin.clear();  // reset the EOF bit.
    return;
}


Download Screen.cpp source file


// screen.cpp - implementation for Screen and Point classes.
// Written by (c) Wayne Pollock, 2000.  All Rights Reserved.

#include <iostream>
#include <iomanip>
#include <cstdlib>   // For older compilers use: <stdlib.h>
#include "screen.h"

using namespace std;

//=========================================================================
//                      Point Class implementation:
//=========================================================================

Point Point::operator+ ( const Point& p ) const
{   Point t( row, col );
    t.row += p.row;
    t.col += p.col;
    return t;
}

int Point::operator== ( const Point& p ) const
{   return ( p.row == row && p.col == col );
}

//=========================================================================
//                      Screen class implementation:
//=========================================================================

// In this implementation, the grid of characters is represented as a
// two dimentional array.
// Any value can be used to draw (or erase) the screen, even ' ' (blank).
// You can draw with ' ' to selectively erase parts of the image.
// Note the origin is in the upper left-hand corner.

Screen::Screen ()
{
    ink = INK_DEFAULT;
    current_pt.row = 0;
    current_pt.col = 0;
}

Screen::Screen ( const Screen& s )   
{
    ink = s.ink;
    current_pt = s.current_pt;
    for ( int r=0; r<NUM_ROWS; ++r )
	for ( int c=0; c<NUM_COLS; ++c )
	    rep[r][c] = s.rep[r][c];
}

Screen& Screen::operator= ( const Screen& s )
{   ink = s.ink;
    current_pt = s.current_pt;
    for ( int r=0; r<NUM_ROWS; ++r )
	for ( int c=0; c<NUM_COLS; ++c )
	    rep[r][c] = s.rep[r][c];
    return *this;
}

// Note if "ink" is zero (the default), plot uses the Screen::ink character.
void Screen::plot ( const Point& p, char new_ink )	// new_ink has default value.
{
    if ( p.row < NUM_ROWS && p.col < NUM_COLS && p.row >= 0 && p.col >= 0 )
    {	if ( ! new_ink )
	    new_ink = ink;
	rep[p.row][p.col] = new_ink;
	current_pt.row = p.row;  // Update the current point.
	current_pt.col = p.col;
    }
    return;
}

void Screen::move_to ( const Point& p )  // never move the point off-screen!
{
    if ( p.row < NUM_ROWS && p.col < NUM_COLS && p.row >= 0 && p.col >= 0 )
	current_pt = p;
    return;
}

void Screen::clear ( char erase_char )	// erase_char has a default value.
{
    for ( int r=0; r<NUM_ROWS; ++r )
	for ( int c=0; c<NUM_COLS; ++c )
	    rep[r][c] = erase_char;
    return;	// Note that old Current Point is not reset here.
}

void Screen::display ( ostream& out ) const	// out has a default value.
{
    out << "\n\n\n" << endl;
    out << "   ";  // offset the column numbers by the width of row numbers.
    for ( int c=0; c<NUM_COLS; ++c )
	out << ( c % 10 );             // print column numbers.
    out << endl;
    for ( int r=0; r<NUM_ROWS; ++r )
    {	out << setw( 2 ) << r << " ";  // print row numbers.
	out.write( rep[r], NUM_COLS ); // print out one whole row.
	out << endl;
    }
    return;
}

Point Screen::size () const
{
    Point p;
    p.row = NUM_ROWS;
    p.col = NUM_COLS;
    return p;
}

void Screen::draw_line ( const Point& start_pt, const Point& end_pt, char ink )
{
    if (start_pt == end_pt)      //  i.e., a one point line.
    {	plot( start_pt, ink );
	return;
    }
    int len_r = end_pt.row - start_pt.row;
    int len_c = end_pt.col - start_pt.col;
    int len = abs( len_c );
    if ( abs( len_r ) > abs( len_c ) )
	len = abs( len_r );
    double r_inc = len_r / double( len );
    double c_inc = len_c / double( len );
    double r = start_pt.row + 0.5;
    double c = start_pt.col + 0.5;
    for ( int i=0; i<=len; ++i )
    {	plot( Point( int( r ), int( c ) ), ink );
	r += r_inc;
	c += c_inc;
    }
    return;
}


void Screen::draw_circle ( const Point& center, int radius, char ink )
{
    int r = radius;
    int c = 0;
    int decision = 1 - r;
    circle_points( Point( r, c ), center, ink );
    while ( r > c )
    {	if ( decision < 0 )
	{   decision += 2 * c + 3;
	    ++c;
	} else {
	    decision += 2 * ( c - r ) + 5;
	    ++c;   --r;
	}
	circle_points( Point( r, c ), center, ink );
    }
    move_to( center );	// Reset the current point to the center.
    return;
}


void Screen::circle_points ( const Point& p, const Point& c, char ink )
{
    plot( Point(  p.row+c.row,  p.col+c.col ), ink );
    plot( Point(  p.row+c.row, -p.col+c.col ), ink );
    plot( Point( -p.row+c.row,  p.col+c.col ), ink );
    plot( Point( -p.row+c.row, -p.col+c.col ), ink );
    plot( Point(  p.col+c.row,  p.row+c.col ), ink );
    plot( Point(  p.col+c.row, -p.row+c.col ), ink );
    plot( Point( -p.col+c.row,  p.row+c.col ), ink );
    plot( Point( -p.col+c.row, -p.row+c.col ), ink );
    return;
}


Download Sketch.exe