Lab 9-2: CComplex 的應用與 Mandelbrot 圖形界面

 
實習目標 使用實習 3-1, 4-2 中的CComplex 類別
做一個 Mandelbrot 碎形動畫
練習與視窗圖形界面的合作
練習視窗界面程式的 debug
 
步驟一 這個實習接續 9-1, 我們稍微修改一下 Mandelbrot 類別的界面, 讓它能夠支援一個 Mandelbrot 動畫的程式, 範例執行程式 Mandelbrot03a, 這個程式會慢慢畫出每計算一次 z = z * z + c 之後的圖形, 在狀態列會顯示目前已經作了多少次了, 以及每一次有多少個點發散掉了, 程式一直執行到 256 次為止

在任何時候, 你可以用滑鼠左鍵點兩下來暫停動畫, 也可以用選單上的暫停模擬來暫停動畫, 然後你可以用滑鼠左鍵在視窗中指定一個區域來放大顯示, 重新點兩下可以繼續執行, 另外使用右鍵點選可以更換一組顏色。

步驟二 請下載圖形界面程式碼 Mandelbrot03b.rar, 解壓縮出來, 請編譯並且執行, 你應該可以看到視窗中是全部空白的, 選單中有 檔案/暫停動畫 的選項, 如同上一個實習, 我們將在這個 project 中應用你先前寫的 CComplex 類別來撰寫程式
步驟三 首先在類別檢視視窗中你可以找到 Mandelbrot 類別, 打開這個類別你可以看到下列類別的定義以及空的成員函式:
    class Mandelbrot  
    {
    public:
    	int updateData(int ** const &data, int currentIteration);
    	void generateData(int ** const &data, int numIterations);
    	Mandelbrot(double centerX, double centerY, 
    	           double range, int windowSize);
    	virtual ~Mandelbrot();
    
    private:
    ...
    };
這個類別和前一個實習裡的 Mandelbrot 大同小異, 主要的界面修改
    int updateData(int ** const &data, int currentIteration);

是為了支援動畫的繪製, 為了繪製動畫, Mandelbrot 物件不能呼叫 generateData() 函式一次把整個圖形都計算完, 而需要另外一個 updateData() 的介面來慢慢計算圖形中每一點是否發散, 你可以想像視窗界面函式做的動作變成

    while (每一秒鐘到時)
        updateData();

每次當 updateData() 被呼叫到的時候, 在 updateData() 裡把還沒有發散的座標點都計算一次 z = z * z + c, 判斷是否發散, 修改對應的 data[i][j] 的數值

首先建構元函式會傳進來繪圖中心點的複數平面的座標, 例如 (-0.5, 0), 以及繪圖區域的寬與高 (目前所畫的是一個方形區域), 例如 2.3, 以及視窗顯示區域的點數, 例如 500, 請在 Mandelbrot 類別中設計常數 (const) 資料成員記錄下來, 例如:

    const double m_range;
    const double m_centerY;
    const double m_centerX;
    const int m_windowSize;
除此之外, 類別內也需要有一個 m_windowSize x m_windowSize 的二維CComplex陣列 m_z, 主要目的是記錄每一回合計算後 z 的數值, 這個陣列我們為了節省記憶體, 還是用下圖動態的方式來配置:
建構元中必須負責配置這個二維陣列, 解構元中必須負責釋放這個二維陣列
步驟四 這個類別也需要用到複數的類別, 請由前一個實習中拷貝進來 , 在 Complex.cpp 的第一列加入 #include "stdafx.h"
步驟五
    void generateData(int ** const &data, int numIterations);
這個函式和前一個實習中實作的 generateData() 概念相同, 每次被呼叫到時, 代表繪圖界面程式需要知道在 numIterations 回合的限制下有多少點發散掉了; 但是這一次的 generateData() 負責的事情較少, 它不需要去配置 data 陣列的記憶體也不允許修改 data 指標變數了, 它仍然需要計算 numIterations 個回合, 算出每一個點經過多少回合才會發散, 並且將結果記錄在 data 陣列中,

對於所有沒有發散的點, data[i][j] 都必須設定為 0

對於所有沒有發散的點, 目前所計算出來的 z 值必須記錄在 m_z 陣列中, 以便後續在 updateData() 函式被呼叫到時, 繼續計算 z = z * z + c 得到下一回合每個點對應的 z 值

步驟六
    int updateData(int ** const &data, int currentIteration);

每次 updateData() 被呼叫到時, 代表界面程式需要下一個時間點的動畫資料, 請根據先前所記錄在 m_z 陣列中的 z 值, 繼續運用 z = z * z + c 計算下一個回合的 data[i][j] 數值並且存放在 data[i][j] 陣列中, z 的資料一樣需要記錄下來。

另外在函式裡只需要對於 data[i][j] 為 0 的點來計算就可以了, data[i][j] 不等於 0 的點在前面的運算中都已經發散了, 不需要再繼續計算下去, 如果在計算的過程中發現某一個點發散掉了, 請將 data[i][j] 設為 currentIteration+1, 這個傳進來的 currentIteration 數值代表目前進行到第幾個回合了 (第一回合 currentIteration == 0)。

updateData() 函式回傳的數值是在這個回合中發散的點數, 圖形界面程式用來在視窗下緣的狀態列中顯示本回合中發散掉的點數。

步驟七 請助教檢查後, 將所完成的 project (去掉 debug/ 資料匣下的所有內容) 壓縮起來, 選擇 Lab9-2 上傳, 後面的實習課程可能需要使用這裡所完成的程式

C++ 物件導向程式設計課程 首頁

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