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;
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";
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)
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)
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)
00109 {
00110 swap(x1, x2), swap(y1, y2);
00111 swap(c1, c2);
00112 }
00113 if( (flip_y = y1 > y2) )
00114 {
00115 y1 = -y1, y2 = -y2;
00116 }
00117 if( (swap_xy = y2-y1 > x2-x1) )
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;
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 );
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;
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;
00191 map<int, RGB> x_rgb;
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