Home > Tags > C language

C language

OpenGL: ポリゴンで円の描画

円周上の座標(x, y)×n個を計算しその点を結ぶことによって描画します。nを分割数とすると、nに比例して円は滑らかになります。

実行結果

  • 分割数: 15(ちょっとカクカクしてる)

OpenGL: ポリゴンで円の描画(分割数:15)

  • 分割数: 100

OpenGL: ポリゴンで円の描画(分割数:100)

コード

// OpenGLで円の描画
#include <stdio.h>
#include <stdlib.h>
#include <gl/glut.h>
#include <gl/gl.h>
#include <math.h>

#define M_PI 3.14159265358979 // 円周率
#define PART 100 // 分割数

void display(void)
{
  int i, n = PART;
  float x, y, r = 0.5;
  double rate;

  glClear(GL_COLOR_BUFFER_BIT); // ウィンドウの背景をglClearColor()で指定された色で塗りつぶす
  glColor3f(1.0, 1.0, 1.0); // 描画物体に白色を設定
  glBegin(GL_POLYGON); // ポリゴンの描画
  // 円を描画
  for (i = 0; i < n; i++) {
    // 座標を計算
    rate = (double)i / n;
    x = r * cos(2.0 * M_PI * rate);
    y = r * sin(2.0 * M_PI * rate);
    glVertex3f(x, y, 0.0); // 頂点座標を指定
  }
  glEnd(); // ポリゴンの描画終了
  glFlush(); // OpenGLのコマンドを強制的に実行(描画終了)
}

void init(char *name)
{
  int width = 400, height = 400; // ウィンドウサイズ
  int w_window = glutGet(GLUT_SCREEN_WIDTH), h_window = glutGet(GLUT_SCREEN_HEIGHT); // デスクトップのサイズ
  int w_mid = (w_window-width)/2, h_mid = (h_window-height)/2; // デスクトップの中央座標

  glutInitWindowPosition(w_mid, h_mid);
  glutInitWindowSize(width, height);
  glutInitDisplayMode(GLUT_RGBA); // 色の指定にRGBAモードを使用
  glutCreateWindow(name);
  glClearColor(0.0, 0.0, 0.0, 1.0); // ウィンドウの背景色の指定
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); // 座標系を設定(平行投影)
}

int main(int argc, char **argv)
{
  glutInit(&argc, argv); // glutの初期化
  init(argv[0]);
  glutDisplayFunc(display); // ディスプレイコールバック関数の指定
  glutMainLoop(); // イベント待ちループ
  return 0;
}

そういえば、リアルタイム処理などではポリゴンの数を抑えることで処理速度を稼いでましたね。

参考サイト: GLUTによる「手抜き」OpenGL入門

3種類の括弧の対応をチェックするC言語プログラム

先日勉強会でこの辺のテーマを取り上げたので、字句解析や構文解析(の一部)とスタックの復習も兼ねて作成(required for 1h+)。

実装のポイント

  • 閉じ括弧の有無の判定は、ファイルの終端が読み終わった後。
  • 開き括弧の判定は、閉じ括弧を読み込んだ際に行い、スタックの最上位に対応する開き括弧があるか否かで。
  • 入れ子の(または再帰的な)構造で、どの深さにスレッドがいるか調べるにはスタックを用いる。

ソースコード(C言語) valid_pare.c

#include <stdio.h>
#include <stdlib.h>

const int MAXSIZE = 128; // スタックサイズ(静的)
// スタックするデータ構造(Cell)
typedef struct brackets Brackets;
struct brackets {
  int kind; // 括弧の種類
  int line; // 位置:行
  int pos; // 位置:列
};
Brackets stack[128];
int pnt;

Brackets pop(void)
{
  if (pnt < = 0) {
    printf("error:stack empty.\n");
    exit(1);
  }
  pnt--;
  return stack[pnt];
}
void push(Brackets b)
{
  if (pnt >= MAXSIZE) {
    printf("error:stack fullness.\n");
    exit(1);
  }
  stack[pnt] = b;
  pnt++;
}
// stackが空(1)か否(0)か
int empty(void)
{
  return (pnt == 0) ? 1 : 0;
}
// スタックの最上位の文字種を返す
int peek()
{
  return stack[pnt-1].kind;
}
// 括弧の判別
int kind(int ch)
{
  int code;
  switch (ch) {
  case '(':
    code = 1;
    break;
  case ')':
    code = 2;
    break;
  case '{':
    code = 3;
    break;
  case '}':
    code = 4;
    break;
  case '[':
    code = 5;
    break;
  case ']':
    code = 6;
    break;
  default:
    code = 0; // 括弧以外の文字
    break;
  }
  return code;
}

int main()
{
  int ch;
  FILE *fp;
  char fname[64]; // ファイル名
  int k; // 文字の種類
  int line = 1, pos = 0;
  Brackets kakko, temp;

  pnt = 0; // スタックポインタ(GV)の初期化
  printf("Filename:");
  scanf("%62s", fname);
  if ((fp = fopen(fname, "r")) == NULL) {
    printf("\aCan't be opened.\n");
    exit(1);
  }
  //printf("%d行目\n", line);
  // ファイルから一文字ずつ読む
  while ((ch = fgetc(fp)) != EOF) {
    //putchar(ch);
    if (ch == '\n') {
      line++; pos=0;
      //printf("%d行目\n", line);
      continue;
    }
    pos++;
    //printf("%d", pos);
    k = kind(ch);
    if (k > 0) { // 文字が括弧の場合
      if (k % 2) { // 開き括弧の場合
        kakko.kind = k;
        kakko.line = line;
        kakko.pos  = pos;
        push(kakko);
      } else if (!empty() && (k == peek()+1)) {
        temp = pop(); // 対応する閉じ括弧があった
      } else {
        printf("対応する開き括弧がない");
        printf("(%d行目の%d文字目)。\n", line, pos);
      }
    }
  }
  puts("テキスト終端");
  fclose(fp);
  if (!empty()) {
    printf("対応する閉じ括弧がない。\n");
    while (!empty()) {
      temp = pop();
      printf("%d行目の%d文字目。\n", temp.line, temp.pos);
    }
  }
  return 0;
}

使用したText

(1+2)
ac)c
((3+6)ge))
(([y)
ij])(k
{5/3}

実行結果

D:\parentheses>gcc valid_pare.c
D:\parentheses>a.exe
Filename:pare.txt
対応する開き括弧がない(2行目の3文字目)。
対応する開き括弧がない(3行目の10文字目)。
対応する開き括弧がない(4行目の5文字目)。
テキスト終端
対応する閉じ括弧がない。
5行目の5文字目。
4行目の1文字目。
D:\parentheses>

改良するなら

  • 関数を他のファイルに分けて、main側でインクルードする。
  • スタック領域を動的に割り当てる。(Brackets *)emalloc(sizeof(Brackets))かな。
  • スタック配列を伸縮性のある構造・操作関数で実装する。
  • 結果の表示をコンパイラのエラー出力っぽくする。該当箇所に「^」をマーク。
  • 他の言語で実装してみる(OOPを用いて等等)。
  • 字句・構文解析のオープンソースを参考にする。

こういった小さなものを作りつつ、OOPのパターンに基づいて拡張性を考慮する今日この頃。

10進数を2進数に変換表示するC言語プログラム

ソースコード

// filename: dtob.c
// convert decimal to binary
#include <stdio.h>

const int BitSize = sizeof(int) * 8; // 整数型のビットサイズを算出
void dtob(int x) {
  int bit = 1, i;
  char c[BitSize];

  for (i = 0; i < BitSize; i++) {
    if (x & bit)
      c[i] = '1';
    else
      c[i] = '0';
    bit <<= 1;
  }
  // 計算結果の表示
  printf("2進数: ");
  for ( i = BitSize - 1; i >= 0; i-- ) {
      putchar(c[i]);
  }
  printf("\n");
}

int main()
{
  int x = 0;
  do {
  printf("10進数を2進数に変換します(0で終了)\n");
  printf("xの値: ");
  scanf("%d", &x);
  dtob(x);
  } while (x != 0);
  return 0;
}

実行結果

[yu@localhost cpp]# gcc dtob.c
[yu@localhost cpp]# ./a.out
10進数を2進数に変換します(0で終了)
xの値: 5
2進数: 00000000000000000000000000000101
10進数を2進数に変換します(0で終了)
xの値: 8
2進数: 00000000000000000000000000001000
10進数を2進数に変換します(0で終了)
xの値: 100
2進数: 00000000000000000000000001100100
10進数を2進数に変換します(0で終了)
xの値: 256
2進数: 00000000000000000000000100000000
10進数を2進数に変換します(0で終了)
xの値: 1984949894
2進数: 01110110010011111110111010000110
10進数を2進数に変換します(0で終了)
xの値: 0
2進数: 00000000000000000000000000000000
[yu@localhost cpp]# 

ビットが0か1の判断するループ順序を逆にして、配列末尾に'\0'を代入すれば、計算結果の表示は配列の文字列表示ですみます。そうすると「計算結果の表示」の際にforループを使う必要はないなぁ、と書き終わった今思いました(ぇー)。

作成の経緯

以前、拡張ハッシュ法の削除関数を実装している際に、キーをバケットに振り分ける際のアドレス算出の処理部分にデバッグプリントが欲しくて作ったものです。

Eclipse+CDTを用いてプロジェクトを作成する際の注意点

以前、K-na TechNotes | Homeのページを参考にWindowsでEclipse3.3とCDTをインストールしました。分かりやすく書かれており、とても参考になりました(謝々)。
たまたまK-na TechNotes | CDT のトラブル対策ページ下部にある

<実行>を押しても、デバッグしても、必ず「アプリケーション・エラー 起動に失敗(バイナリ・ファイルがありません)」のメッセージが出ます。

という件を見て、この症状の理由はProject typesの設定ミスではないかと推測しました。

解決方法は、まず上述のページを参照しながら、

  • Cygwin か MinGWをインストール及びパスの設定をする
  • Eclipse(CDT)をインストールする
  • Eclipse上のバイナリパーサの設定等を行う(コンパイルで生成されるバイナリファイルをEclipseで認識させるため)

ここまで出来れば使えるはず。
早速プロジェクトを作成しましょう。

以下の画像はEclipse3.3ですが、Linuxパージョンである。だが、表示項目はWindowsのそれとほとんど変わりません。

[ファイル]>[新規]>[Cプロジェクト]とクリック

Eclipse の C++ プロジェクト作成ウィザード画面

以下の画面で適当なプロジェクト名を打ち込んだ後、Project typesを選択します。ここで[実行可能]>[Empty Project]を選択します。Toochainの項目はLinux系なら[Linux GCC]であるし、上述の設定を行った場合のWindowsのToochainは[Cygwin GCC]を選択します。
これで終了を押せば、問題なくコーディングでき、ビルド→実行となります。

プロジェクトタイプ(Project types)の設定に注意

ここで、[Makefile project]を選択した場合は、Makefileを作成しなければエラーになります。[static ライブラリー]を選択した場合はビルドしてもバイナリファイルは生成されないので注意です。

雑感

最近はC, C++, C#やJavaにしても統合開発環境があればコーディングやリーディングがしやすいです(ソースの規模にもよりますが)。これを使うことでの弊害も無いとは言えないですが、それはまた別の話で。
RubyやRails, PHPの開発ではどうなのかな。

X Window System 上での描画色の変更

先日X11/Xlib.h、X11/Xutil.hを用いてフラクタルを描画するプログラムを作成していた折、描画する図形を構成する線分の色を変えようと試みました。X Window Systemではあらかじめ定義されている色名があります。
参考サイト:X Window System X Window System rgb.txt sample
しかし、今回は多くの色を扱うためRGB指定での描画色の変更を行います。まず、以下のような整数型のピクセル値を返す関数MyColorを作成します。

#include <x11/Xlib.h>
#include <x11/Xutil.h>

unsigned long MyColor(display, color)
     Display *display;
     char *color;
{
  Colormap cmap;
  XColor c0, c1;
  cmap = DefaultColormap(display, 0);
  XAllocNamedColor(display, cmap, color, &c1, &c0);
  return (c1.pixel);
}

引数colorに以下に示す書式に則ったRGB色を表す文字列を代入しピクセル値を得ます。

"rgb:00/00/FF" (指定した色が青の場合)

ここで得られるピクセル値をXSetForeground関数に代入することでRGB値の指定による描画色の変更を行うことが出来ます。

RGB値指定が可能になることで表現できる色は飛躍的に増えますが、ソースプログラム中にいちいち値を指定した文字列を予め作成しておくのでは、少々非効率な面もあります。
特にフラクタルのような再起関数を用いる場合、繰り返し回数は等比級数的に増す場合もあり、カラフルな図形の表現を行うには別の手法を試みる必要があります。
そこで、上記のRGB値を指定する書式を自動生成する関数のプログラムを以下に示します。

#include <stdio.h>
#include <string.h> // for strdup()

// グローバル変数
char *comap[0x1000000]; // RGB形式のカラーマップ
int coindex = 0; // カラーマップ用パラメータ
int colimit = 0; // カラーマップの個数

// RGBカラーマップを生成
// iの可算度合は色のバラツキ度
void cremap()
{
  char str[16];
  int i, j;
  for(i=0, j=0; i< =0xFFFFFF; i+=0x2888, j++) {
    sprintf(str, "rgb:%02X/%02X/%02X",
      // i / 0xFFFF & 0xFF, i / 0xFF & 0xFF, i & 0xFF); // チョイ非効率
      (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF); // bit演算
    comap[j] = strdup(str);
    //puts(comap[i]);
    //printf("%d\n", i);
  }
  printf("%d\n", j);
  colimit = j; // 生成した色の数
  //exit(0);
}

sprintf関数を用いて書式を指定した文字列を生成することが出来ます。for ループのiの増分をi++に変更すれば0xFFFFFF色分のカラーマップを生成できます。ただし、このカラーマップの順序は少々とっぴで、パラメータj順に色を表示すると所所でカラージャンプがあるため、滑らかなグラデーションを生成したい場合はsprintfの引数の指定方法を変える必要があります。

最終的にはソースコード中で以下のように色を変更する一文を加えます。

XSetForeground(d,gc, MyColor(d, comap[lim(++coindex)]));

これによって生成されたフラクタル画像を下図に示します。左がイニシエータ、右がジェネレータ、中央が生成したフラクタル図形です。

からふるフラクタル

Home > Tags > C language

バックナンバー
最近のコメント
最近のトラックバック
メタ情報

Return to page top