簡単に言うと、あるピクセルを描画した後、次に描画するピクセルはどれかを計算する。これを繰り返して円にする。
この時、例えば円を時計回りで描くなら、あるx,yを描画したら、次の候補は(x-1,y),(x-1,y+1),(x,y+1)のいずれかになる。
どれが正解かというと、中心からの距離が半径に最も近いものが答えとなる。
これが4パターンあり、x,yがそれぞれ前進する場合と後進する場合に分けられる。以下の図の■が現在描いているドット、□は次に描くドットの候補。
1/4だけ描画するコード
#pragma warning(disable:4996) #include <iostream> #include <vector> //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] vmax 全てのRGBの中の最大値 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP3_Write(const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k], p[k], p[k]); k++; } fprintf(fp, "\n"); } fclose(fp); } int width = 500; int height = 500; std::vector<unsigned char> img; void plot(int x, int y) { img[y * width + x] = 255; } int main() { img.resize(width * height); // 中心 int cx = 250; int cy = 250; //半径 int r = 50; const int rr = r * r;//半径の二乗 int x, y;//描画位置 //初期位置 x = r; y = 0; //最初の一点 plot(x + cx, y + cy); //反時計回りに描画していく(1/4) // 右下 // □□ // □■ while (x > 0) { ///////////////////////// // 次に描画するポイントを決定 //次に描画するのは以下のいずれか。 //1 { x - 1,y }; //2 { x - 1,y + 1 }; //3 { x ,y + 1 }; int xy1 = pow(x - 1, 2) + pow(y, 2); int xy2 = pow(x - 1, 2) + pow(y + 1, 2); int xy3 = pow(x, 2) + pow(y + 1, 2); // x*x + y*y == r*rなので // 上記各xyのうち最もr*rとの差が少ないx*x+y*yを選択 xy1 = abs(xy1 - rr); xy2 = abs(xy2 - rr); xy3 = abs(xy3 - rr); if (xy1 < xy2) { if (xy1 < xy3) { // xy1 x = x - 1; y = y; } else { // xy2 x = x - 1; y = y + 1; } } else { if (xy2 < xy3) { // xy2 x = x - 1; y = y + 1; } else { // xy3 x = x; y = y + 1; } } ///////////////////////// // 描画 plot(x + cx, y + cy); } pnmP3_Write("a.pgm", width, height, img.data()); }