繪製正弦波形 (Sine Wave)

基本功能說明:

令函數 f(x) = sin(x) 是一個定義在 -π 到 π 之間的函式, 其數值則在 -1 到 1 之間, 例如: sin(0) 為 0, sin(π/2) 為 1,˙˙˙

我們可以表列出 -π 到 π 之間任意數值對應之函數值來解釋這個函數, 不過要讓別人瞭解這個 函數最好的方式還是透過繪圖, 本範例中分別運用控制台文字模式 ( Visual C++ / dev C++) 和 BGIm 函式庫的圖形模式 (Visual C++ / dev C++) 來繪製 sin() 函數:

範例執行程式:

  1. SineText.exe
  2. SineGraph.exe

繪圖概念

一個 sin() 函式圖形如下圖:

這是一個二維平面上的圖形, x 軸範圍在 -π 到 π 之間, y 軸範圍在 -1 到 1 之間, 因為這是一個連續函數, 所以不論 x 軸或是 y 軸, 我們都知道其上應該有無窮多個點。

但是在電腦螢幕上要顯示這樣子的圖形, 不可能也不需要使用無窮多個點,

不可能的原因是螢幕的解析度有限, 在文字 模式視窗只能顯示 80 個字元寬、24 個字元高, 每一個方格裡你只能顯示一個字元或是不顯示, 沒有太多的選擇。 在簡單的 BGIm 圖形模式視窗中允許你在指定的 寬度及高度 的視窗中作圖, 每一個點除了允許你畫點或是不畫點之外, 也容許你用 16 個顏色來畫點。

不需要使用無窮多點的原因是: 你的眼睛不夠好, 根本無法在螢幕上分辨距離太近的點。

下圖是在文字模式視窗中繪製的 sine 函數示意圖:

你可以想像畫面中分為 24 x 80 個不重疊的長方形小方格, 每一個小方格裡可以顯示一個字元, 例如 '-' 號或是 '*' 號或是 '.', 上圖表示之圖形雖然不夠細緻, 也還勉強可以感覺此曲線之走向。

下圖在 BGIm 繪圖模式視窗中繪製同樣的 sine 函數:

由於解析度大大提高, 在上面視窗中繪製的曲線, 你已經慢慢感覺不出來它是折線了。

座標轉換公式

我們要把前面的 sin(x) 函數圖形畫在螢幕上的話, 需要設計座標點的轉換公式, 下表中我們先列出代表性的幾個點的對應關係:
實數平面
寬:2π 高:2
文字模式
寬:80 高:24
繪圖模式視窗
寬:640 高:480
(x,y) (x1,y1) (x2,y2)
(0, 0) (40, 12) (320, 240)
(π, 0) (79, 12) (639, 240)
(-π, 0) (1, 12) (1, 240)
(0, 1) (40, 1) (320, 1)
(0, -1) (40, 23) (320, 479)

一般化的公式應該是

實數平面
寬:2π 高:2
文字模式
寬:80 高:24
繪圖模式視窗
寬:640 高:480
x
y
x1 = x / π * 39 + 40
y1 = -y * 11 + 12
x2 = x / π * 319 + 320
y2 = -y * 239 + 240

請注意

  1. 由於 x, y 是實數, 上列公式算出的 x1, y1, x2, y2 也是實數, 而我們知道不管在 文字模式視窗或是繪圖模式視窗下, 都只能將螢幕畫面切割為整數個點, 其座標也需要是整數, 所以我們計算出來的 x1, y1, x2, y2 要經過四捨五入來找到最接近的整數座標值。

  2. 如何將一個浮點變數內的浮點數四捨五入為一個整數變數內的整數資料呢? 則整數變數 i 內的數值為 3, j 內的數值為 2。

在 Visual C++ / dev C++ 控制台文字模式視窗中繪圖

如何在 80 x 24 個格子的區間中指定在某一位置上畫一個字元呢?

其中一種方法是使用 utilwin32.h utilwin32.cpp 中的 void gotoxy(int x, int y); 函式將游標移到第 x 行, 第 y 列, 再以 stdio.h 函式庫中的 int putchar(int c); 函式再該位置畫出字元, 例如:

會在視窗的左上角印出一個 * 字元。

由於在控制台視窗的文字模式下 x 軸最多就只能有 80 個點, 為了讓正負對稱, 我們只畫 79 個點, 因此我們最多只需要去求出這麼多 sin() 函數的數值即可, 也就是說在 -π 到 π 之間只要畫 79 個點, 其間隔為 2 π / 78, 這些點的位置為 π/39*i i=-39,-38.....38,39, 做這件事情的程式如下:

請注意

在繪製上面 sine 函數之前先繪出座標軸如下:

Visual C++ 6, 2008, 2010 中命令列編譯如下: cl SineText.cpp utilwin32.cpp

Dev C++ 5.11 產生多檔案專案, 加入 SineText.cpp 及 utilwin32.cpp, 編譯並執行

gcc 4.9.2 中命令列編譯如下: g++ -static SineText.cpp utilwin32.cpp

以 BGIm 函式庫繪圖 (Visual C++ 或是 dev C++/MinGW)

這個圖形界面原先是在 Turbo C 和 Borland C 中一個簡單的圖形界面, 拿來練習還蠻不錯的, 不需要一開始就學習使用 Win32 裡比較複雜的圖形 API

1. 引入檔: (請以滑鼠右鍵另存連結/目標)下載 graphics.h (請注意不需要再使用 utilwin32.h 了, 這兩個裡面的 delay() 函式是重複的)

 Visual C++ 6 拷貝至 C:\Program Files\Microsoft Visual Studio\VC98\Include
 Visual C++ 2008 拷貝至 C:\Program Files\Microsoft Visual Studio 9\VC\include
 Visual C++ 2010 拷貝至 C:\Program Files\Microsoft Visual Studio 10.0\VC\include

 devC++ 4.9.9.2 (g++ 3.4.2) 拷貝至 C:\Dev-Cpp\include (請注意 BGIm 需要用 sstream, 需要用 g++ 編譯器, 你自己的程式也要使用 .cpp 作為副檔名)

 MinGW g++ 4.8.1-4 (32bit) 拷貝至 C:\MinGW\include (請注意 BGIm 需要用 sstream, 需要用 g++ 編譯器, 你自己的程式也要使用 .cpp 作為副檔名)

 devC++ 5.7.1 (g++ 4.8.1, 64bit) 拷貝至 C:\Program Files (x86)\Dev-Cpp\MinGW64\include (請注意 BGIm 需要用 sstream, 需要用 g++ 編譯器, 你自己的程式也要使用 .cpp 作為副檔名)

 devC++ 5.11 (g++ 4.9.2, 64bit) 拷貝至 C:\Program Files (x86)\Dev-Cpp\MinGW64\include (請注意 BGIm 需要用 sstream, 需要用 g++ 編譯器, 你自己的程式也要使用 .cpp 作為副檔名)

 MinGW (g++ 4.8.1, 64bit) 拷貝至 C:\MinGW\include (請注意 BGIm 需要用 sstream, 需要用 g++ 編譯器, 你自己的程式也要使用 .cpp 作為副檔名)

2. 函式庫 (請以滑鼠右鍵點選適當版本的 libbgi.lib 另存連結/目標)

 Visual C++ 6 下載 libbgi.lib, 拷貝至 C:\Program Files\Microsoft Visual Studio\VC98\Lib
 Visual C++ 2008 下載 libbgi.lib, 拷貝至 C:\Program Files\Microsoft Visual Studio 9\VC\Lib
 Visual C++ 2010 下載 libbgi.lib, 拷貝至 C:\Program Files\Microsoft Visual Studio 10.0\VC\Lib

 dev C++ 4.9.9.2 (g++ 3.4.2, 32bit) 下載 libbgi.a, 拷貝至 C:\Dev-Cpp\lib
 MinGW (g++ 4.8.1-4, 32bit) 下載 libbgi.a, 拷貝至 C:\MingGW\lib
 dev C++ 5.7.1 (g++ 4.8.1, 64bit) 下載 libbgi.a, 拷貝至 C:\Program Files (x86)\Dev-Cpp\MinGW64\lib
 dev C++ 5.11 (g++ 4.9.2, 64bit) 下載 libbgi.a, 拷貝至 C:\Program Files (x86)\Dev-Cpp\MinGW64\lib (工具/編譯器選項/一般/在連結器命令列加入以下的命令)
 MinGW (g++ 4.8.1, 64bit) 下載 libbgi.a, 拷貝至 C:\MinGW\lib

3. 測試程式 testBGIm.cpp (下載完上面兩個檔案以後, 請以滑鼠右鍵另存連結/目標 下載此測試程式,編譯並且測試簡單的繪圖功能)

4. 編譯器/連結器參數 (將下面的 xxx.cpp 改為你自己的應用程式名稱)

 Visual C++ 6: cl -GX xxx.cpp libbgi.lib gdi32.lib comdlg32.lib uuid.lib oleaut32.lib ole32.lib user32.lib
 Visual C++ 2008: cl -EHsc xxx.cpp libbgi.lib gdi32.lib comdlg32.lib uuid.lib oleaut32.lib ole32.lib user32.lib
 Visual C++ 2010: cl -EHsc xxx.cpp libbgi.lib gdi32.lib comdlg32.lib uuid.lib oleaut32.lib ole32.lib user32.lib

 dev C++ 4.9.9.2: -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32
 dev C++ 5.7.1, dev C++ 5.11: -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32 -Wno-write-strings (工具/編譯器選項/一般/在連結器命令列加入以下的命令)

 MinGW (g++ 3.4.2, 32bit): g++ testBGIm.cpp -o testBGIm.exe -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32
 MinGW (g++ 4.8.1, 64bit): g++ testBGIm.cpp -o testBGIm.exe -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32 -Wno-write-strings

5. Visual C++ 6 命令列編譯: 下載 bgi.exe, 拷貝至 C:\Program Files\Microsoft Visual Studio\VC98\bin, 以 bgi xxx.cpp 編譯程式

6. 說明文件 bgi.chm (Windows 中如果看不到內容的話, 請以滑鼠右鍵點選檔案 bgi.chm /內容/取消封鎖) (線上版本)

注意:如果你沒有權限拷貝檔案到系統裡, 把 graphics.h 和 libbgi.a (libbgi.lib) 一起和你的程式 xxx.cpp 放在同一個資料匣, 程式裡改成

 #include "graphics.h"

 Visual C++ 6: cl -GX xxx.cpp libbgi.lib gdi32.lib comdlg32.lib uuid.lib oleaut32.lib ole32.lib user32.lib

 Visual C++ 2008/2010: cl -EHsc xxx.cpp libbgi.lib gdi32.lib comdlg32.lib uuid.lib oleaut32.lib ole32.lib user32.lib

 dev C++ 4.9.9.2 (g++ 3.4.2): -L. -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32

 dev C++ 5.7.1 (g++ 4.8.1, 32bit,64bit), dev C++ 5.11 (g++ 4.9.2): -L. -lbgi -lgdi32 -lcomdlg32 -luuid -loleaut32 -lole32 -Wno-write-strings

 

程式中如何 進入 及 結束 繪圖模式?

程式如何知道螢幕寬度及高度各有幾點呢?

程式在執行的時候可以藉由 graphics.h 中定義的函式 getmaxx() 及 getmaxy() 來得到螢幕的寬度及高度,例如: 螢幕上可以在 x 軸(正向朝右) 0 到 maxX 以及 y 軸(正向朝下) 0 到 maxY 的範圍中作畫。

如何在螢幕上畫線呢?

請運用 graphics.h 函式庫中
  1. void line(int x1, int y1, int x2, int y2);
  2. void lineto(int x2, int y2);
  3. void moveto(int x2, int y2);
等三個函式在螢幕上畫線, line() 這個函式可以指定起始點座標 (x1, y1) 及終點座標 (x2, y2), 在這之間畫一條直線。 moveto() 及 lineto() 這一組函式配合目前的游標位置 CP 也可以用來畫直線,例如:

的效果完全一樣, moveto(200, 20) 將游標位置 CP 移動到 (200, 20) 的地方, lineto(200, 80) 則由 CP 的位置畫直線到 (200, 80), 並設定 CP 為 (200, 80)。

使用 lineto() 函式畫連續線段比起 line() 函式最大的好處就是程式中不需要記錄上一點的座標, 而由繪圖系統中的 CP 來記住。

注意

如何畫 sine 函式

參考前面文字模式的畫圖程式, 座標軸繪製如下:

畫點的部份都以畫線來取代, 程式如下:

練習:

  1. 請在 BGIm 繪圖模式下, 同時繪出 sin() 函式及 cos() 函式由 -π 到 π 的函式變化 (可以練習一下函式指標), 請繪製座標軸, 請使用兩種顏色, 並利用 graphics.h 中的 outtextxy(int x, int y, char *str); 函式來標明哪一個顏色是 sin() 函式, 哪一個顏色是 cos() 函式。
    請注意: 要配合 outtextxy 函式列印某一 double 型態變數裡面的資料的話, 你可以使用 stdio.h 裡面的 sprintf() 函式來將 double 型態的變數轉換成 字元陣列 (sprintf 在使用的時候基本上和 printf 或是 fprintf 一樣, 只是輸出到字元陣列裡面而已), 範例如下:

    #include <stdio.h>
    ...
    char buf[10];
    double x = 0.75;
    sprintf(buf, "%4.2f", x);
    outtextxy(315, 100, buf); // 請注意 座標值 (315,100) 指定了印出文字的左上角

  2. 接上題, 請在 x 座標軸上每隔 0.1 畫一刻度, 並在 -3, -2, -1, 0, 1, 2, 3, 等處標明座標值,請在 y 座標軸上每隔 0.05 畫一刻度, 並在 -1.00, -0.75, -0.5, -0.25, 0.00, 0.25, 0.5, 0.75, 1.00, 等處標明座標值。

其它 BGIm 範例:

程式設計課程 首頁

製作日期: 101/10/22 by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw TEL: 02 24622192x6615
海洋大學 電機資訊學院 資訊工程學系 Lagoon