スポンサーリンク

走査変換で塗りつぶされた三角形を描画

三点を指定し、中が塗りつぶされた三角形を描く。以下、プログラムでは表示にブレゼンハムを使ってはいるが領域の塗りつぶしではないのでただの確認のために使っている。

scanConversion.hpp

#pragma once

#include "NByteData.hpp"
#include "Bresenham.hpp"
#include "LineCross.hpp"

template<typename PixelType>
struct imagedata {
  int width;
  int height;
  PixelType* img;
};

//! @brief 水平な線分ともう一方の線分との交差判定を行う
//! @param [out] cross 交差していた場合の座標(ピクセル単位)
//! @param [in] scanL 水平な線分
//! @param [in] L 線分
bool isPixelHorizonalCross(
  Pointi* cross,
  const LineSegmenti& scanL,
  const LineSegmenti& L) {

  //三角形塗りつぶしようなので、scanLは必ずLの始点・終点よりも広い範囲をとる
  //そのため始点・終点のY座標が一致していたら交差しているのでtrueを返す
  if (scanL.s.y == L.s.y) {
    *cross = L.s;
    return true;
  }
  if (scanL.s.y == L.e.y) {
    *cross = L.e;
    return true;
  }

  return isCrossPixel(cross, scanL, L);

}

//! @brief 画素に色を設定
//! @param [in,out] img 画像データ
//! @param [in] P 書き込み先の座標
template<typename PixelType>
void Plot(
  imagedata<PixelType> *img,
  Pointi P) {

  PixelType Color{ 255,0,0 };
  img->img[P.y * img->width + P.x] = Color;

}

//! @brief 走査変換で頂点ABCからなる三角形を描画
//! @param [in,out] img 画像データ
//! @param [in] A 頂点1の座標
//! @param [in] B 頂点2の座標
//! @param [in] C 頂点3の座標
template<typename PixelType>
void scanConversion(
  imagedata<PixelType> *img,
  const Pointi A,
  const Pointi B,
  const Pointi C
) {

  //入力された頂点をY座標が小さい順にソート
  Pointi m[3]{ A,B,C };
  std::sort(
    std::begin(m), std::end(m),
    [](const auto& s, const auto& t) {
    return s.y < t.y;
  });


  //X座標の最大・最小より少し広い範囲
  const int xmin = std::min_element(
    std::begin(m), std::end(m),
    [](const auto& s, const auto& t) {return s.x < t.x; }
  )->x - 1;
  const int xmax = std::max_element(
    std::begin(m), std::end(m),
    [](const auto& s, const auto& t) {return s.x < t.x; }
  )->x + 1;

  /*
           0
         / |
       /   |
     /     |
    1      |
     \     |
      \    |
       \   |
        \  |
         \ |
           2

  ソートにより三角形の配列mはy座標順に並んでいる


  */

  // 走査線と 01,02
  for (int y = m[0].y; y < m[1].y; y++) {
    //上半分について処理

    LineSegmenti scan(
      Pointi(xmin, y),
      Pointi(xmax, y)
    );

    Pointi cross01;
    Pointi cross02;

    LineSegmenti L01(m[0], m[1]);
    LineSegmenti L02(m[0], m[2]);

    bool ab = isPixelHorizonalCross(&cross01, scan, L01);
    bool ac = isPixelHorizonalCross(&cross02, scan, L02);

    if (ab == true && ac == true) {
      int cxmin = (std::min)(cross01.x, cross02.x);
      int cxmax = (std::max)(cross01.x, cross02.x);
      for (int x = cxmin; x <= cxmax; x++) {
        Pointi P(x, y);
        Plot(img, P);
      }
    }


  }
  // 走査線と 12 , 01
  for (int y = m[1].y; y <= m[2].y; y++) {
    LineSegmenti scan(
      Pointi(xmin, y),
      Pointi(xmax, y)
    );

    Pointi cross12;
    Pointi cross02;

    LineSegmenti L12(m[1], m[2]);
    LineSegmenti L02(m[0], m[2]);

    bool bc = isPixelHorizonalCross(&cross12, scan, L12);
    bool ac = isPixelHorizonalCross(&cross02, scan, L02);

    if (bc == true && ac == true) {
      int cxmin = (std::min)(cross12.x, cross02.x);
      int cxmax = (std::max)(cross12.x, cross02.x);
      for (int x = cxmin; x <= cxmax; x++) {
        Pointi P(x, y);
        Plot(img, P);
      }

    }
  }

}

テストプログラム

#pragma warning(disable:4996)

#include <iostream>
#include <vector>

#include "scanConversion.hpp"

void ppmP3_write(
  const char* const fname,
  const int width,
  const int height,
  const unsigned char* const p,
  const int vmax
);

using ucrgb = NByteData<3>;
ucrgb Black = ucrgb{ 0,0,0 };
ucrgb White = ucrgb{ 255,255,255 };
ucrgb Red = ucrgb{ 255,0,0 };

void test1(imagedata<ucrgb>& img,bool fill);
void test2(imagedata<ucrgb>& img,bool fill);
void test3(imagedata<ucrgb>& img,bool fill);
void test4(imagedata<ucrgb>& img,bool fill);
void test5(imagedata<ucrgb>& img,bool fill);
void test6(imagedata<ucrgb>& img, bool fill);

int main()
{

  int width = 200;
  int height = 200;
  std::vector<ucrgb> image;


  image.resize(width*height, White);

  imagedata<ucrgb> imginfo;
  imginfo.width = width;
  imginfo.height = height;
  imginfo.img = &image[0];

  std::fill(image.begin(), image.end(), White);
  test1(imginfo,true);

  std::fill(image.begin(), image.end(), White);
  test2(imginfo, true);

  std::fill(image.begin(), image.end(), White);
  test3(imginfo, true);

  std::fill(image.begin(), image.end(), White);
  test4(imginfo, true);

  std::fill(image.begin(), image.end(), White);
  test5(imginfo, true);

  std::fill(image.begin(), image.end(), White);
  test6(imginfo, true);

}
//! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む
//! @param [in] fname ファイル名
//! @param [in] width 画像の幅
//! @param [in] height 画像の高さ
//! @param [in] p 画像のメモリへのアドレス
//! @param [in] vmax 全てのRGBの中の最大値。普通の画像なら255
//! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む
void ppmP3_write(
  const char* const fname,
  const int width,
  const int height,
  const unsigned char* const p,
  const int vmax
) {

  FILE* fp = fopen(fname, "wb");
  fprintf(fp, "P3\n%d %d\n%d\n", width, height, vmax);

  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 * 3 + 0],
        p[k * 3 + 1],
        p[k * 3 + 2]
      );
      k++;
    }
    fprintf(fp, "\n");
  }

  fclose(fp);
}



void test1(imagedata<ucrgb>& imginfo, bool fill) {
  Pointi A{ 20,20 };
  Pointi B{ 20,120 };
  Pointi C{ 120,70 };

  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, A.x, A.y, B.x, B.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, B.x, B.y, C.x, C.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, C.x, C.y, A.x, A.y, Black);

  if (fill) {
    scanConversion<ucrgb>(&imginfo, A, B, C);
  }


  ppmP3_write(R"(C:\test\t1.ppm)", imginfo.width, imginfo.height, imginfo.img->data(), 255);

}

void test2(imagedata<ucrgb>& imginfo, bool fill) {
  Pointi A{ 20,20 };
  Pointi B{ 120,20 };
  Pointi C{ 100,100 };

  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, A.x, A.y, B.x, B.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, B.x, B.y, C.x, C.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, C.x, C.y, A.x, A.y, Black);

  if (fill) {
    scanConversion<ucrgb>(&imginfo, A, B, C);
  }


  ppmP3_write(R"(C:\test\t2.ppm)", imginfo.width, imginfo.height, imginfo.img->data(), 255);

}

void test3(imagedata<ucrgb>& imginfo, bool fill) {
  Pointi A{ 100,20 };
  Pointi B{ 20,50 };
  Pointi C{ 130,100 };

  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, A.x, A.y, B.x, B.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, B.x, B.y, C.x, C.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, C.x, C.y, A.x, A.y, Black);

  if (fill) {
    scanConversion<ucrgb>(&imginfo, A, B, C);
  }


  ppmP3_write(R"(C:\test\t3.ppm)", imginfo.width, imginfo.height, imginfo.img->data(), 255);

}

void test4(imagedata<ucrgb>& imginfo, bool fill) {
  Pointi A{ 20,20 };
  Pointi B{ 20,20 };
  Pointi C{ 20,20 };

  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, A.x, A.y, B.x, B.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, B.x, B.y, C.x, C.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, C.x, C.y, A.x, A.y, Black);

  if (fill) {
    scanConversion<ucrgb>(&imginfo, A, B, C);
  }


  ppmP3_write(R"(C:\test\t4.ppm)", imginfo.width, imginfo.height, imginfo.img->data(), 255);

}
void test5(imagedata<ucrgb>& imginfo, bool fill) {
  Pointi A{ 20,20 };
  Pointi B{ 100,20 };
  Pointi C{ 50,20 };

  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, A.x, A.y, B.x, B.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, B.x, B.y, C.x, C.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, C.x, C.y, A.x, A.y, Black);

  if (fill) {
    scanConversion<ucrgb>(&imginfo, A, B, C);
  }

  ppmP3_write(R"(C:\test\t5.ppm)", imginfo.width, imginfo.height, imginfo.img->data(), 255);

}

void test6(imagedata<ucrgb>& imginfo, bool fill) {
  Pointi A{ 20,20 };
  Pointi B{ 20,50 };
  Pointi C{ 20,100 };

  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, A.x, A.y, B.x, B.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, B.x, B.y, C.x, C.y, Black);
  Bresenham(&imginfo.img[0], imginfo.width, imginfo.height, C.x, C.y, A.x, A.y, Black);

  if (fill) {
    scanConversion<ucrgb>(&imginfo, A, B, C);
  }

  ppmP3_write(R"(C:\test\t6.ppm)", imginfo.width, imginfo.height, imginfo.img->data(), 255);

}

コメントを残す

メールアドレスが公開されることはありません。

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


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