2017年5月6日 星期六

碎形幾何測試程式




以上影片說的是碎形幾何,若覺得手癢也想試試,以下的程式可幫上一點忙。

程式使用到 CxxlMan2 程式庫,可到 http://blog.cxxl3d.tk/2015/10/cxxlman2-class-lib.html 下載,不過只用到 cxxlcommon\include 內的引入檔,所以不用編譯這個程式庫,只要把 THREADMGR.HPP 所在的完整路徑加到 include path 就可以了。

程式在 Win10 用 vs2015 和 MinGW 6.3 編譯跑過沒有問題,程式只是一個架構,把程式碼整個複製貼上就能用了。


#include <windows.h>
#include <THREADMGR.HPP>

using namespace CxxlMan2;

HWND g_hwnd;
bool isQuit = false;
HDC g_memDC; // 輔助繪圖設備
RECT g_rcClient;//區域結構
const TCHAR *g_szClassName = TEXT("myWindowClass");

// 主要的功能宣告,邏輯運算放這裡,自由發揮
void Fractal(float X, float Y, ThreadLimit &Fractal_TL);

// Fractal() 算出來的結果交由這函數畫到畫布上
// 畫布左上是 (0.0, 0.0) 右下是 (1.0, 1.0)
void Show(float X, float Y)
{
  SetPixel(g_memDC, g_rcClient.right * X, g_rcClient.bottom * Y, 
           RGB(255, 0, 0));
  InvalidateRect(g_hwnd, nullptr, false);
}


void Draw()
{
  HDC hDC = GetDC(g_hwnd); //獲得系統繪圖設備
  g_memDC = CreateCompatibleDC(0); //創建輔助繪圖設備

  GetClientRect(g_hwnd, &g_rcClient);//獲得 client 區域

  //創建掩碼位圖(畫布)
  HBITMAP bmpBack = CreateCompatibleBitmap(hDC,
    g_rcClient.right - g_rcClient.left,
    g_rcClient.bottom - g_rcClient.top);

  SelectObject(g_memDC, bmpBack); //將畫布貼到繪圖設備上

  //擦除背景
  //獲得庫存物體,白色畫刷。
  HBRUSH brushTemp = (HBRUSH)GetStockObject(WHITE_BRUSH);
  FillRect(g_memDC, &g_rcClient, brushTemp);//填充客戶區域。

  // 加上區塊可以等待 Fractal_TL 結束才繼續
  {
    ThreadLimit Fractal_TL(std::thread::hardware_concurrency());
    Fractal_TL.Thread(Fractal, 0.5, 0.5, std::ref(Fractal_TL));
  }

  DeleteObject(bmpBack); //釋放位圖資源
  DeleteDC(g_memDC); //釋放輔助繪圖設備
  ReleaseDC(g_hwnd, hDC); //歸還繫統繪圖設備
  PostMessage(g_hwnd, WM_DESTROY, 0, 0);
}


// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg,
  WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
  case WM_KEYDOWN:
    if (wParam == 0x51) // Q key
      isQuit = true;
  case WM_PAINT:
  {
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);
    if (g_memDC)
      //複製到系統設備上顯示..
      BitBlt(hdc, g_rcClient.left, g_rcClient.top,
        g_rcClient.right, g_rcClient.bottom,
        g_memDC, 0, 0, SRCCOPY);

    EndPaint(hwnd, &ps);
  }
  break;

  case WM_CLOSE:
    DestroyWindow(hwnd);
    break;
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default:
    return DefWindowProc(hwnd, msg, wParam, lParam);
  }
  return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
  LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  MSG Msg;

  //Step 1: Registering the Window Class
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = 0;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = g_szClassName;
  wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

  if (!RegisterClassEx(&wc))
  {
    MessageBox(NULL, TEXT("Window Registration Failed!"), TEXT("Error!"),
      MB_ICONEXCLAMATION | MB_OK);
    return 0;
  }

  // Step 2: Creating the Window
  g_hwnd = CreateWindowEx(
    WS_EX_CLIENTEDGE,
    g_szClassName,
    TEXT("碎形測試"),
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, 300, 300,
    NULL, NULL, hInstance, NULL);

  if (g_hwnd == NULL)
  {
    MessageBox(NULL, TEXT("Window Creation Failed!"), TEXT("Error!"),
      MB_ICONEXCLAMATION | MB_OK);
    return 0;
  }

  ShowWindow(g_hwnd, nCmdShow);
  UpdateWindow(g_hwnd);

  ThreadLimit TL(1);
  TL.Thread(Draw);

  // Step 3: The Message Loop
  while (GetMessage(&Msg, NULL, 0, 0) > 0)
  {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
  }
  return Msg.wParam;
}

// 以下是實作的程式碼,可依自己的想法做修改
float Ax = 0.5, Ay = 0.1,
      Bx = 0.1, By = 0.9,
      Cx = 0.9, Cy = 0.9;

void Fractal(float X, float Y, ThreadLimit &Fractal_TL)
{
  if (isQuit == false)
  {
    Show(X, Y);
    Fractal_TL.Thread(Fractal, X + (Ax - X) / 2, Y + (Ay - Y) / 2,
                      std::ref(Fractal_TL));
    Fractal_TL.Thread(Fractal, X + (Bx - X) / 2, Y + (By - Y) / 2,
                      std::ref(Fractal_TL));
    Fractal_TL.Thread(Fractal, X + (Cx - X) / 2, Y + (Cy - Y) / 2,
                      std::ref(Fractal_TL));
  }
  else
    Fractal_TL.Clear();
}

執行時可按 Q 鍵結束程式,因程式使用 ThreadLimit 作為任務的處理工具,加上程式的任務沒有止境,若不按 Q 鍵結束程式也會因記憶體資源用完而自行結束程式。

其中 Fractal() 可以依自己的想法自己做修改,其他的部份沒必要就不用去動它,注意程式的畫布定義左上為 (0.0, 0.0) 右下為 (1.0, 1.0)。下圖是執行結果的截圖:
▲使用影片中講解的運算規則畫出的圖形

影片採用的是隨機的表現方法,而這程式採用量子演算法,意思是只要有可能就讓它發生,也就是走遍所有可能,也叫窮盡法或暴力法。影片採用隨機的表演方式是正確的,比較有衝擊性的戲劇效果,不然一板一眼的說明,可能沒人會繼續看下去。






沒有留言:

張貼留言