Main Page | File List

Canvas.h

Go to the documentation of this file.
00001 /**
00002  *\file         Canvas.h
00003  *
00004  *\brief        The Canvas class is a discrete simulation of the real life canvas. 
00005  *
00006  *              The canvas is cutted into grid structure, with <b><var>vReso</var></b> grids along
00007  *              vertical (<b><var>Row</var></b>) direction and <b><var>hReso</var></b> horizontal grids along 
00008  *              horizontal(<b><var>Col</var></b>) direction. Each rectangular grid is called a pixel, or picture
00009  *              element, and is specified by its lower-left <b><var>(row, col)</var></b> position. 
00010  *              The coordinate system has the lower-left corner of the canvas as its origin, 
00011  *              with <b><var>Row</var></b> going up and <b><var>Col</var></b> going right. The lower-left corner 
00012  *              pixel has a coordinate of <b><var>(0,0)</var></b>, and the upper-right one of <b><var>
00013  *              (hReso - 1, vReso - 1)</var></b>. 
00014  *
00015  *              A <b><var>(x, y)</var></b> pair means <b><var>col = x+hReso/2, and row = y+vReso/2</var></b>.
00016  *
00017  *              To each piexel <b><var>(row, col)</var></b> on the Canvas, a RGB color value is associated,
00018  *              which can be accessed via index operator [<b><var>row</var></b>][<b><var>col</var></b>].
00019  *
00020  *              WritePPM() writes the Canvas to an image file of PPM format.
00021  *
00022  * <H4>11 Jul 2004:</H4> Point structure and perspectiveProjection(Point const&) added. Now the Canvas is actually a virutal camera
00023  *              in the simplest setting: the Canvas plane is centered at z=0 with x-size of hReso, and y-size of vReso, 
00024  *              Eye at eye_at (>0) and looking along positive z direction.  eye_at defaults to hReso / 2.
00025  *
00026  *              Now the triangle primitives are updated so the given points are in 3D space. The other primitives are
00027  *              not updated though, and actually can be deleted from Canvas class, since for 3D graphics, everything 
00028  *              is usually tesselated by triangles.
00029  *
00030  *\author       Xianming Chen
00031  *
00032  */
00033 
00034 
00035 #ifndef _CANVAS_H_
00036 #define _CANVAS_H_
00037 
00038 #include "RGB.h"
00039 #include "xmath.h"
00040 #include "Point.h"
00041 #include "color.h"
00042 
00043 #include <string>
00044 #include <cassert>
00045 #include <set>
00046 
00047 using namespace std;
00048 
00049 namespace columbia
00050 {
00051   
00052   struct ScannedResult
00053   {
00054       ScannedResult(int X, int Y, float Z, Point const* Tex, RGB const* Col) : x(X), y(Y), z(Z) { if(Tex) tex = *Tex; if(Col) col = *Col; }
00055 
00056       bool operator<(ScannedResult const& rhs) const { return (y<rhs.y) || ( (y==rhs.y) && (x<rhs.x) ); }
00057 
00058       int x, y;
00059       float z;
00060       Point tex;
00061       RGB col;
00062   };
00063 
00064   
00065   class Canvas
00066   { 
00067   public:
00068       explicit Canvas(int resoH = 512, int resoV = 512) : hReso(resoH), vReso(resoV), filename("tmp.ppm"), data(0), z_buffer(0), tex_image(0)  { init(); }
00069       ~Canvas() { delete [] data; delete [] z_buffer; }
00070 
00071 
00072       void SetTextureImage(Canvas* tex)         { tex_image = tex; }
00073 
00074       void Checkerboard(int sz=4, RGB col1 = White, RGB col2 = Black);
00075       
00076       void Clear(RGB const&);                    //! Clear the whole color buffer to given argument; the z-buffer is also cleared.
00077       
00078       RGB* operator[] (int r)                    { assert(r>=0 && r<vReso); return data + r*hReso; }
00079       RGB& Pixel(int row, int col);              //! Use this instead of the [] operator if both row and col number checking is needed.
00080       float& Z(int row, int col);    
00081 
00082       void Line(Point const& P1, RGB const& col1, Point const& P2, RGB const& col2);
00083 
00084       void WireTriangle(Point const& P1, RGB const& col1, Point const& P2, RGB const& col2, Point const& P3, RGB const& col3);
00085 
00086 
00087       void SolidTriangle(Point const& P1, RGB const& col1,  Point const& P2, RGB const& col2, Point const& P3, RGB const& col3);
00088 
00089       void SolidTriangle(Point const& P1, Point const& tex1,  Point const& P2, Point const& tex2, Point const& P3, Point const& tex3);
00090 
00091       void SolidTriangle(Point const& P1, Point const& T1, RGB const& col1,
00092                          Point const& P2, Point const& T2, RGB const& col2, 
00093                          Point const& P3, Point const& T3, RGB const& col3);
00094 
00095       void SolidTriangle(Point const& P1, Point const* T1, RGB const* col1,
00096                          Point const& P2, Point const* T2, RGB const* col2, 
00097                          Point const& P3, Point const* T3, RGB const* col3);
00098 
00099 
00100       void WritePPM(const char* = 0) const;
00101       void ReadPPM(const char* = 0);
00102 
00103       int hReso, vReso;                          //! Resolutions (pixel #)at horizontal and vertical directions.
00104       std::string filename;                      //! write as PPM image file name.
00105 
00106       RGB* data;
00107   private:
00108       float* z_buffer;                           // z-buffer value.
00109       Canvas* tex_image;
00110       
00111       bool swap_xy, flip_y;                      // swap x&y if abs(slope) > 1.0, flip y if y<0.
00112 
00113       void init();
00114 
00115       /**
00116        * Scan-convert a line segment given two end points. The scan conversion stop right before the second end point.
00117        * If output = 0, Draw a line segment between two end points, (x1,y1) and (x2,y2).  Each pixel color is interpolated from two end colors.
00118        * Else only return scanned result to output.
00119        *
00120        * z-buffer algorithm implemented: interpolate between given z-valus, and write to canvas only after passing z-buffer algorithm.
00121        * texture mapping implemented: if given texture value, color is from texture mapping.
00122        */
00123       void scanLineSegment(int x1, int y1, float z1, Point const* tex1, RGB const* col1, 
00124                            int x2, int y2, float z2, Point const* tex2, RGB const* col2, 
00125                            set<ScannedResult>* output=0);
00126 
00127       /* scan convert a line in its normal case. */
00128       void _scanLineSegment(int x1, int y1, float z1, Point const* tex1, RGB const* col1, 
00129                             int x2, int y2, float z2, Point const* tex2, RGB const* col2, 
00130                             set<ScannedResult>* output=0);
00131 
00132       RGB texture_color(Point const& tex) const { return tex_image->Pixel( round((tex_image->vReso-1) * tex.y), round((tex_image->hReso-1) * tex.x) ); }
00133       
00134       void writePixel(int X, int Y, float cur_z, Point const* tex, RGB const* col, set<ScannedResult>* output); 
00135 
00136       double eye_at;
00137       Point perspectiveProjection(Point const&); 
00138   };
00139   
00140   inline void Canvas :: writePixel(int X, int Y, float cur_z, Point const* tex, RGB const* col, set<ScannedResult>* output)
00141   {
00142     if(swap_xy) swap(X,Y);
00143     if(flip_y)  Y = -Y; 
00144 
00145     if(output)
00146     {
00147       output->insert( ScannedResult(X, Y, cur_z, tex, col) );
00148     }
00149     else if( (X >= -hReso/2 && X < hReso/2) &&
00150              (Y >= -vReso/2 && Y < vReso/2) )
00151     {
00152       if( cur_z > Z(Y + vReso/2, X + hReso/2) )
00153       {
00154         Z(Y + vReso/2, X + hReso/2) = cur_z;
00155         Pixel(Y + vReso/2, X + hReso/2) = tex && col ? texture_color(*tex) * *col : (tex ? texture_color(*tex) : *col);
00156       }
00157     }
00158   }
00159   
00160   inline RGB& Canvas :: Pixel(int row, int col)
00161   {
00162     if(row >= vReso) { cout << "row = " << row << endl; throw "row number overflow\n"; }    
00163     if(row < 0)      { cout << "1row = " << row << endl; throw "row number underflow\n"; }
00164     if(col >= hReso) { cout << "col = " << col << endl; throw "col number overrflow\n"; }
00165     if(col < 0)      { cout << "col = " << col << endl; throw "col number underflow\n"; }
00166     return *(data + row * hReso + col);
00167   }
00168   
00169   inline float& Canvas :: Z(int row, int col)
00170   {
00171     if(row >= vReso) { cout << "row = " << row << endl; throw "row number overflow\n"; }    
00172     if(row < 0)      { cout << "1row = " << row << endl; throw "row number underflow\n"; }
00173     if(col >= hReso) { cout << "col = " << col << endl; throw "col number overrflow\n"; }
00174     if(col < 0)      { cout << "col = " << col << endl; throw "col number underflow\n"; }
00175     return *(z_buffer + row * hReso + col);
00176   }
00177 
00178   inline Point Canvas :: perspectiveProjection(Point const& P)
00179   {
00180     double scale = eye_at / (eye_at - P.z);
00181     return Point(P.x * scale, P.y * scale, P.z);
00182   }
00183 
00184   inline void Canvas :: SolidTriangle(Point const& P1, RGB const& col1,  Point const& P2, RGB const& col2, Point const& P3, RGB const& col3)
00185   {
00186     SolidTriangle(P1, 0, &col1, P2, 0, &col2, P3, 0, &col3);
00187   }
00188   
00189   inline void Canvas :: SolidTriangle(Point const& P1, Point const& tex1,  Point const& P2, Point const& tex2, Point const& P3, Point const& tex3)
00190   {
00191     SolidTriangle(P1, &tex1, 0, P2, &tex2, 0, P3, &tex3, 0);
00192   }
00193   inline void Canvas :: SolidTriangle(Point const& P1, Point const& T1, RGB const& col1,
00194                                       Point const& P2, Point const& T2, RGB const& col2, 
00195                                       Point const& P3, Point const& T3, RGB const& col3)
00196   {
00197     SolidTriangle(P1, &T1, &col1, P2, &T2, &col2, P3, &T3, &col3);
00198   }
00199   
00200   inline void Canvas :: Line(Point const& P1, RGB const& col1, Point const& P2, RGB const& col2)
00201   {
00202     Point p1 = perspectiveProjection(P1);
00203     Point p2 = perspectiveProjection(P2);
00204 
00205     scanLineSegment(round(p1.x), round(p1.y), p1.z, 0, &col1, round(p2.x), round(p2.y), p2.z, 0, &col2);
00206   }
00207   
00208   inline void Canvas :: WireTriangle(Point const& P1, RGB const& col1, Point const& P2, RGB const& col2, Point const& P3, RGB const& col3)
00209   {
00210     Line(P1, col1, P2, col2);
00211     Line(P1, col1, P3, col3);
00212     Line(P2, col2, P3, col3);
00213   }
00214   
00215 
00216 
00217 }
00218 
00219 #endif

Generated on Sun Jul 18 20:32:35 2004 by doxygen 1.3.6