Main Page | File List

Canvas.cpp

00001 #include "Canvas.h"
00002 #include "color.h"
00003 #include "xmath.h"
00004 #include <iostream>
00005 #include <fstream>
00006 #include <vector>
00007 
00008 
00009 using namespace std;
00010 
00011 namespace columbia
00012 {
00013 
00014   void Canvas :: init() 
00015   { 
00016     int N = hReso*vReso;
00017     
00018     delete [] data; 
00019     data = new RGB [N]; 
00020     memset(data, 0, sizeof(RGB) * N);
00021 
00022     eye_at = hReso / 2;     // default horizontal field of view = 90.
00023   }
00024 
00025   void Canvas :: Clear(RGB const& color)
00026   {
00027     for (int row = 0; row < vReso; row++) 
00028       for (int col = 0; col < hReso; col++) 
00029       {
00030         (*this)[row][col] = color;
00031       }
00032   }
00033   
00034 
00035   void Canvas :: WritePPM(const char *_filename) const
00036   {
00037     Canvas* This =  (Canvas*)this;
00038     
00039     if(!_filename) 
00040       _filename = filename.c_str();
00041 
00042     ofstream file;
00043     file.open (_filename, ios::out | ios::binary);
00044     
00045     if(file) 
00046     {
00047       file << "P6\n";   // color rawbits format
00048       file << hReso << " " << vReso << endl << 100 << endl;
00049   
00050       unsigned char r, g, b;
00051       for (int row = 0; row < vReso; row++) 
00052         for (int col = 0; col < hReso; col++) 
00053         {
00054           r = (unsigned char) (100.0*(*This)[vReso-row-1][col][0]);  
00055           g = (unsigned char) (100.0*(*This)[vReso-row-1][col][1]);  
00056           b = (unsigned char) (100.0*(*This)[vReso-row-1][col][2]);  
00057 
00058           if(r>100.0 || g>100.0 || b>100.0)  throw "RGB color overflow\n"; 
00059 
00060           file << r << g << b;
00061         }
00062 
00063       file << endl;
00064       file.close();
00065     }
00066     else 
00067       cerr << "Can't open output file " << _filename << endl;
00068   }
00069 
00070 
00071 
00072   void Canvas :: scanLineSegment(int x1, int y1, RGB const& col1, int x2, int y2, RGB const& col2, multimap<int, pair<int, RGB> >* output)
00073   {
00074     assert( x2-x1 >= 1 && x2-x1 >= y2-y1 && y2-y1 >= 0);
00075 
00076     if(y1 == y2)                                     // this is horizontal line, or vertical line if swap_xy set.
00077     {
00078       for(int x = x1; x < x2; ++x)                   
00079       {
00080         writePixel(x, y1, interpolate(( double(x-x1) ) / (x2 - x1), col1, col2), output);
00081       }
00082     }
00083     else if(y2-y1 == x2-x1)                         // diagonal lines.
00084     {
00085       int y = y1;
00086       for(int x = x1; x < x2; ++x, ++y)
00087       {
00088         writePixel(x, y, interpolate(( double(x-x1) ) / (x2 - x1), col1, col2), output);
00089       }
00090     }
00091     else     
00092     {
00093       for(int x = x1; x < x2; ++x)
00094       {
00095         double t = ( double(x-x1) ) / (x2 - x1);
00096         writePixel(x, round( interpolate(t, double(y1), double(y2)) ), interpolate(t, col1, col2), output);
00097       }
00098     }
00099   }
00100   
00101 
00102   void Canvas :: ScanLineSegment(int x1, int y1, RGB const& col1, int x2, int y2, RGB const& col2, multimap<int, pair<int, RGB> >* output)
00103   {
00104     if(x1==x2 && y1==y2) return;
00105 
00106     RGB c1(col1), c2(col2);
00107     
00108     if(x1 > x2)                                       // always scan convert from left to right.
00109     {
00110       swap(x1, x2), swap(y1, y2); 
00111       swap(c1,  c2);
00112     }
00113     if( (flip_y = y1 > y2) )                          // always scan convert from down to up.
00114     {
00115       y1 = -y1, y2 = -y2;
00116     }
00117     if( (swap_xy = y2-y1 > x2-x1) )                   // and always scan convert a line with <= 45 deg to x-axis.
00118     {
00119       swap(x1, y1), swap(x2, y2);
00120     }
00121     scanLineSegment(x1, y1, c1, x2, y2, c2, output);
00122   }
00123 
00124 
00125   void Canvas :: WireCircle(int center_x, int center_y, int radius, int slices, RGB const& col)
00126   {
00127     double angle_step = 2 * pi / slices;
00128 
00129     double angle = 0;
00130     int x1 = center_x + radius, y1 = center_y;
00131     int x2, y2;
00132     
00133     for(int i=0; i<slices; i++)
00134     {
00135       angle += angle_step;
00136       x2 = round(center_x + radius * cos(angle));
00137       y2 = round(center_y + radius * sin(angle));
00138       (*this)[y2 + vReso/2][x2 + hReso/2] = col;   // make sure consecutive segment is connected.  
00139       ScanLineSegment(x1, y1, col, x2, y2, col);
00140       x1 = x2;
00141       y1 = y2;
00142     }
00143   }
00144 
00145 
00146   void Canvas :: SolidCircle(int center_x, int center_y, int radius, RGB const& col)
00147   {
00148     for(int y = 0; y <= radius; y++)
00149     {
00150       int x = round( sqrt(double(radius * radius - y * y)) );
00151       if(x == 0) 
00152         x = round( sqrt(2. * radius - 1) / 2 );     // half value of the previous x. this way the circle looks better.
00153       
00154       ScanLineSegment(-x + center_x,  y + center_y, col,   x + center_x,  y + center_y, col);
00155       if(y != 0)
00156         ScanLineSegment(-x + center_x,  -y + center_y, col,   x + center_x,  -y + center_y, col);
00157     }
00158   }
00159 
00160   
00161   void Canvas :: SolidRectangle(int minx, int miny, int maxx, int maxy, RGB const& col)
00162   {
00163     for(int y = miny; y < maxy; y++)
00164       ScanLineSegment(minx,  y, col,   maxx, y, col);
00165   }
00166   
00167 
00168   void Canvas :: WireTriangle(Point const& P1, RGB const& col1, Point const& P2, RGB const& col2, Point const& P3, RGB const& col3)
00169   {
00170     Point p1 = perspectiveProjection(P1);
00171     Point p2 = perspectiveProjection(P2);
00172     Point p3 = perspectiveProjection(P3);
00173     ScanLineSegment(round(p1.x), round(p1.y), col1, round(p2.x), round(p2.y), col2);
00174     ScanLineSegment(round(p1.x), round(p1.y), col1, round(p3.x), round(p3.y), col3);
00175     ScanLineSegment(round(p3.x), round(p3.y), col3, round(p2.x), round(p2.y), col2);
00176   }
00177   
00178   void Canvas :: SolidTriangle(Point const& P1, RGB const& col1, Point const& P2, RGB const& col2, Point const& P3, RGB const& col3)
00179   {
00180     Point p1 = perspectiveProjection(P1);
00181     Point p2 = perspectiveProjection(P2);
00182     Point p3 = perspectiveProjection(P3);
00183 
00184     multimap<int, pair<int, RGB> > scanned_pnts;     // ele is <y, <x, col> >, sorted by y val.
00185 
00186     ScanLineSegment(round(p1.x), round(p1.y), col1, round(p2.x), round(p2.y), col2, &scanned_pnts);
00187     ScanLineSegment(round(p1.x), round(p1.y), col1, round(p3.x), round(p3.y), col3, &scanned_pnts);
00188     ScanLineSegment(round(p3.x), round(p3.y), col3, round(p2.x), round(p2.y), col2, &scanned_pnts);
00189     
00190     int cur_yval = vReso / 2 + 1;                    // initialize to an invalid value.
00191     map<int, RGB> x_rgb;                             // put x_vals and related cols of all points with the same y value here.
00192     
00193     for (multimap<int, pair<int, RGB> >::iterator it = scanned_pnts.begin(); it != scanned_pnts.end(); ++it)
00194     {
00195       int y = it->first;
00196       if(y != cur_yval) 
00197       {
00198         if(x_rgb.size())
00199         {
00200           ScanLineSegment(x_rgb.begin()->first, cur_yval, x_rgb.begin()->second, 
00201                    (--x_rgb.end())->first+1, cur_yval, (--x_rgb.end())->second);
00202           x_rgb.clear();
00203         }
00204         cur_yval = y;
00205       }
00206       x_rgb.insert(it->second);
00207     }
00208   }
00209 }
00210 

Generated on Wed Jul 14 19:41:01 2004 by doxygen 1.3.6