スポンサーリンク

円を描くアルゴリズム(2)

前回の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);
}



struct Image {
  std::vector<unsigned char> img;
  int width;
  int height;
};
void resize(Image* img, int w, int h) {
  img->width = w;
  img->height = h;
  img->img.resize(w * h);
}

//! @brief 画像のピクセルを着色する
//! @param [in,out] img 画像
//! @param [in] x 座標
//! @param [in] y 座標
//! @return なし
void plot(Image& img, int x, int y) {
  img.img[y * img.width + x] = 255;
}


enum class CPOS {
  left,right,top,bottom
};

//! @brief 円の指定した1/4を描画する
//! @param [out] img 画像
//! @param [in] cx 中心座標
//! @param [in] cy 中心座標
//! @param [in] r  半径
//! @param [in] bx 円の四分割したエリアの指定
//! @param [in] by 円の四分割したエリアの指定
//! @return なし
void calc(Image& img, const int cx, const int cy, const int r, const CPOS bx, const CPOS by) {
  int x, y;//描画位置
  const int rr = r * r;//半径の二乗
  int dx, dy;
  int limx;
  int limy;

  if (bx==CPOS::right && by==CPOS::bottom) {
    // □□
    // □■
    //初期位置
    x = r;
    y = 0;
    dx = -1;
    dy = +1;
    limx = 0;
    limy = r;
  }
  else if (bx == CPOS::left && by == CPOS::bottom) {
    // □□
    // ■□
    x = 0;
    y = r;
    dx = -1;
    dy = -1;
    limx = -r;
    limy = 0;
  }
  else if (bx == CPOS::left && by == CPOS::top) {
    // ■□
    // □□
    x = -r;
    y = 0;
    dx = +1;
    dy = -1;
    limx = 0;
    limy = -r;
  }
  else if (bx == CPOS::right && by == CPOS::top) {
    // □■
    // □□
    x = 0;
    y = -r;
    dx = +1;
    dy = +1;
    limx = r;
    limy = 0;
  }

  //最初の一点
  //plot(img,x + cx, y + cy);

  while ((x != limx) || (y != limy)) {

    /////////////////////////
    // 次に描画するポイントを決定

    //次に描画するのは以下のいずれか。
    int xy1 = pow(x + dx, 2) + pow(y     , 2);
    int xy2 = pow(x + dx, 2) + pow(y + dy, 2);
    int xy3 = pow(x     , 2) + pow(y + dy, 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 + dx;
        y = y;
      }
      else {
        // xy2
        x = x + dx;
        y = y + dy;
      }
    }
    else {
      if (xy2 < xy3) {
        // xy2
        x = x + dx;
        y = y + dy;
      }
      else {
        // xy3
        x = x;
        y = y + dy;
      }
    }

    /////////////////////////
    // 描画
    if (
      (img, x + cx < 0) || 
      (img, x + cx >=img.width) ||
      (img, y + cy < 0) || 
      (img, y + cy >= img.height)
      ) {
      continue;
    }
    plot(img,x + cx, y + cy);


  }

}
      
      
      
//! @brief 円を描画する
//! @param [in] img 画像データ
//! @param [in] cx  中心座標
//! @param [in] cy  中心座標
//! @param [in] r   半径
//! @return なし
void circle(Image& img,const int cx, const int cy, const int r) {

  // □□
  // □■
  calc(img,cx, cy, r, CPOS::right, CPOS::bottom);

  // □□
  // ■□
  calc(img, cx, cy, r, CPOS::left, CPOS::bottom);

  // ■□
  // □□
  calc(img, cx, cy, r, CPOS::left, CPOS::top);

  // □■
  // □□
  calc(img, cx, cy, r, CPOS::right, CPOS::top);

}
int main()
{
  Image img;
  resize(&img, 500, 500);

  // 中心
  int cx = 250;
  int cy = 250;

  //半径
  int r = 50;

  //円を描画
  circle(img,cx, cy, r);


  pnmP3_Write("a.pgm", img.width, img.height, img.img.data());


}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: