實習目標 |
使用實習 3-1, 4-2 中的CComplex 類別
做一個 Mandelbrot 碎形圖形 練習與基本圖形界面的合作 練習視窗界面程式的 debug |
---|---|
步驟一 |
請執行 Mandelbrot02a 程式
這個程式會顯示 Mandelbrot 碎形圖形如下圖: int i, j; Complex c; double step = range / (windowSize-1); for (i=0; i<windowSize; i++) for (j=0; j<windowSize; j++) c = Complex(centerX-range/2.0+i*step, centerY+range/2.0-j*step);上面迴圈中每一次算出來的複數 c 就是每個座標點 (i, j) 所對應的複數值。 接下來我們需要測試繪圖區域內每一個點, 看看在下列的測試公式下, 每一個 c 值發散的狀況: 令複數 z 的初始值為 0+0i 令每一個點的複數座標為 c 重複計算 z = z * z + c 每次運算後, 如果 z 的實部或是虛部絕對值超過 2 的話, 就表示已經發散掉了, 此時請記錄由開始到 發散需要計算上式幾次, 通常在前 50 次計算中, 會發散的點就已經有百分之九十九都發散掉了, 剩下的也幾乎不會發散了上圖中同一顏色的那些點代表它們都在算到相同次數時發散掉的 (超過 2/-2 的) 上面是一個很漂亮的圖形, 而且如果放大來看的話, 它會不斷重複類似的圖案, 請執行 Mandelbrot03a, 在自然界中這種圖形很多, 這種圖形也常常可以用簡單的數學公式描述出來 如果發散的條件改成 "當 z 的實部和虛部的絕對值都超過 2" 的話, 還是一個碎形圖形, 但是看起來感覺不太一樣: |
步驟二 | 請下載圖形界面程式碼 Mandelbrot02b.rar, 解壓縮出來, 請編譯並且執行, 你應該可以看到視窗中是全部空白的, 選單中有 檔案/MaxIteration 設定的選項, 我們將在這個 project 中應用你先前寫的 CComplex 類別來撰寫程式 |
步驟三 |
首先在 ClassView 中你可以找到 Mandelbrot 類別,
打開這個類別你可以看到下列類別的定義以及空的成員函式:
class Mandelbrot { public: Mandelbrot(double centerX, double centerY, double range); void generateData(int ** &data, int windowSize, int maxIterations); void deleteData(int ** &data, int windowSize); private: ... };首先建構元函式會傳進來繪圖中心點的複數平面的座標, 例如 (-0.5, 0), 以及繪圖區域的寬與高 (目前所畫的是一個方形區域), 例如 2.3, 請在 Mandelbrot 類別中用資料成員記錄下來 |
步驟四 |
void generateData(int ** &data, int windowSize, int maxIterations);
這個成員函式應該要檢查傳入的指標, 如果是 0 的話, 動態配置一塊二維的整數 (int) 陣列, 其架構如下圖 |
步驟五 |
這個 data 陣列的每一點代表繪圖區域中一個像素 (pixel),
依照一般的慣例,
data[0][0] 代表左上角, data[0][windowSize-1] 代表右上角,
data[windowSize-1][0] 代表左下角, data[windowSize-1][windowSize-1] 代表右下角,
你需要計算那個點的複數座標,
依照步驟一中的公式算算看在做到第幾次時 z 的實部或是虛部的絕對值超過 2,
然後記錄在 data[i][j] 中,
請注意傳入的 maxIterations 數值是你在測試時的最大次數
在你真正去算 z 的運算式並且算出 data[i][j] 之前, 你可以先把 data[i][j] 設定成一些簡單的圖樣來測試一下, 例如 (直紋): for (i=0; i<windowSize; i++) for (j=0; j<windowSize; j++) data[i][j] = i%256;或是 (矩形)int i, j, min_i, min_j; for (i=0; i<windowSize; i++) for (j=0; j<windowSize; j++) { min_i = i<(windowSize-i) ? i : windowSize-i; min_j = j<(windowSize-j) ? j : windowSize-j; data[i][j] = min_i<min_j ? min_i : min_j; }請注意 data[i][j] 的數值要維持在 [0, 255] 間 |
步驟六 |
上面步驟中你需要用到一個 CComplex 的複數類別,
同時類別中要有
CComplex add(CComplex &rhs); CComplex multiply(CComplex &rhs);兩個成員函式, 這兩個成員函式我們在前面的實習中定義過, 不過那時定義的是 add(CComplex &rhs) 實際上是 addEqual(CComplex &rhs), 也就是把 rhs 的數字和自己這個物件加起來, 並且修改自己這個物件的內容, 你可以直接用上次的成員函式來實作 z = z * z + c, 例如 z.multiplyEqual(z); z.addEqual(c)或者你也可以修改你的函式 addEqual() 為 add(), 讓它在把兩個數字加起來以後, 不要修改自己那個物件, 而是以傳值 (call by value) 的方式把一個 CComplex 的物件傳回, 如此你在實作 z = z * z + c 時需要用類似下列敘述: z = c.add(z.multiply(z));或是 tmp = z.multiply(z); z = tmp.add(c);請注意如果你直接將前面實習的 Complex.cpp 和 Complex.h 拷貝進來的話, 請在你的 Complex.cpp 檔案的第一列加入 #include "stdafx.h" 如此 compiler 才不會跟你抱怨說它找不到 pch 檔案 |
步驟七 |
void deleteData(int ** &data, int windowSize);
函式中需要實作將步驟四中配置的二維陣列刪除的程式碼 此時你可以編譯並且執行, 畫面上應該可以看到 Mandelbrot 的顯示了, |
步驟八 |
如果程式不幸出錯或是沒有顯示的話...就只好開始 debug 了,
這個程式是視窗的界面, 所以你沒有辦法用 cout, cin
來偵錯, 當然你可以用 VC 的 source debugger 一步一步執行來找錯誤,
不過這是最慢的方法, 同時在過程中你也會看到一大堆根本不是你寫的程式...
很困擾呢...
在使用 Microsoft Foundation Class (MFC) 製作視窗界面時, 我們常用一個 TRACE 巨集來取代 cout, TRACE 的用法和 printf 很像, 相信你還沒有忘記怎樣用 printf, 例如: TRACE("m_data=%d\n", m_data); TRACE("m_doubleData=%f\n", m_doubleData);這些敘述的輸出會到哪裡去呢? 如果你用 debug 模式執行的話, 會跑到 output 視窗中, 如果你是正常執行的話, 可以開啟 dbwin32 程式來顯示偵錯的結果 |
步驟九 |
請測試一下選單 檔案/MaxIterations 設定,
當使用者更改過這個設定值 (10-120) 後,
你提供的 generateData 會重新被呼叫到,
同時 maxIterations 參數會有新的數值傳入
由於你在這個程式中有動態配置記憶體, 請特別在 dbwin32 視窗中確定一下程式結束時有沒有 memory leakage |
步驟十 | 請將所完成的 project (去掉 debug/ 資料匣下的所有內容) 壓縮起來儲存在 cyber 上你的帳號內, 後面的實習課程可能需要使用這裡所完成的程式 |
回
C++ 物件導向程式設計課程
首頁
製作日期: 04/23/2004
by 丁培毅 (Pei-yih Ting)
E-mail: pyting@mail.ntou.edu.tw
TEL: 02 24622192x6615
海洋大學
電機資訊學院
資訊工程系
Lagoon