プロシージャルな樹木生成
学生時代に作ったプログラム.
がんばってリファクタリングしたけど,限界まで飽きたのでアップ.
VC++とWinAPI使用.
DrawTree.cpp
/******************************************************************* * * DESCRIPTION: 樹木曲線 * * AUTHOR: shom * * HISTORY: * * DATE: 2009/12/17 * *******************************************************************/ /** include files **/ #include "DrawTree.h" #include "windows.h" #include <math.h> /* sin(), cos() */ #include <stdlib.h> /* srand(), rand() */ #include <assert.h> #include <algorithm> /* min(), max() */ /** local definitions **/ #define FACT_RIGHT (0.700) #define FACT_LEFT (0.700) #define FACT_UP (0.800) #define DELTA_ANGLE_LEFT (0.4) #define DELTA_ANGLE_RIGHT (0.4) #define START_HIGH (4.0f/5.0f) #define START_LOW (3.0f/5.0f) /** * 関数:木の描画を行う再帰関数 * * @param energy 伸ばす枝の数の参考値 * @param length 幹の長さ * @param angle 幹の角度 * @param x 枝の始点のx座標値 * @param y 枝の始点のy座標値 * @param nest ネストの深さを保持 * @param hdc * @param flow */ void DrawTree( int energy, double length, double angle, double x, double y, double nest, HDC hdc ) { /* <幹の描画> */ // ペン生成 HPEN pen; if( nest < 3 ) { //* 幹 //色 BYTE r = min( 150 + static_cast<int>( ( nest / 3.0f ) * 50 ), 255 ); BYTE g = min( static_cast<int>( ( nest / 3.0f ) * 100 ), 255 ); BYTE b = min( static_cast<int>( 0 ), 255 ); COLORREF pen_color = RGB( r, g, b ); //太さ int pen_width = 15 - static_cast<int>( nest * 5 ); //ペン生成 pen = CreatePen( PS_SOLID, pen_width, pen_color ); } else if( ( nest < 4.9 ) && ( nest > 4.7 ) ) { static int flow = 0; flow++; if ( flow % 5 != 2 ) { //* 葉 //色 BYTE r = 0; BYTE g = min( static_cast<int>( ( nest / 6.0f ) * 255 ), 255 ); BYTE b = 0; COLORREF pen_color = RGB( r, g, b ); //太さ int pen_width = 2 + static_cast<int>( nest ); //ペン生成 pen = CreatePen( PS_SOLID, pen_width, pen_color ); } else { //* 花 //色 BYTE r = 230; BYTE g = 153; BYTE b = 153; COLORREF pen_color = RGB( r, g, b ); //太さ int pen_width = 5; //ペン生成 pen = CreatePen( PS_DASH, pen_width, pen_color ); } } else { //* 葉 //色 BYTE r = 0; BYTE g = min( static_cast<int>( ( nest / 6.0f ) * 255 ), 255 ); BYTE b = 0; COLORREF pen_color = RGB( r, g, b ); //太さ int pen_width = 2 + static_cast<int>( nest ); //ペン生成 pen = CreatePen( PS_SOLID, pen_width, pen_color ); } //デバイスハンドルにセット HGDIOBJ pen_post = SelectObject( hdc, pen ); //処分 DeleteObject( pen_post ); // ペンで枝を描く double delta_x = length * sin(angle); double delta_y = length * cos(angle); int start_x = static_cast<int>( x ); int start_y = static_cast<int>( y ); int end_x = static_cast<int>( x - delta_x ); int end_y = static_cast<int>( y - delta_y ); MoveToEx( hdc, start_x, start_y, NULL ); LineTo( hdc, end_x, end_y ); ///* ///<枝の描画> ///・DrawTreeを再帰に呼び出す. ///・枝は左, 右, 上の3方向に伸びる. ///・再帰の度にenergyを一定値減らす. ///* if( energy > 0 ) { // 左と右に枝を伸ばす { //length double lb_length = length * FACT_LEFT; double rb_length = length * FACT_RIGHT; //angle // "lb" is "Left Branch", "rb" is "Right Branch". double lb_angle = angle + ( static_cast<double>(rand() % 4) / 10 ) + DELTA_ANGLE_LEFT; double rb_angle = angle - ( static_cast<double>(rand() % 4) / 10 ) - DELTA_ANGLE_RIGHT; //start_x, start_y //左右どちらの枝を高くするかは、ランダムに決定する double lb_start_x, lb_start_y, rb_start_x, rb_start_y; if( rand() % 2 == 1 ) { lb_start_x = x - (delta_x * START_HIGH); lb_start_y = y - (delta_y * START_HIGH); rb_start_x = x - (delta_x * START_LOW); rb_start_y = y - (delta_y * START_LOW); } else { lb_start_x = x - (delta_x * START_LOW); lb_start_y = y - (delta_y * START_LOW); rb_start_x = x - (delta_x * START_HIGH); rb_start_y = y - (delta_y * START_HIGH); } //再帰 DrawTree( (energy-2), lb_length, lb_angle, lb_start_x, lb_start_y, (nest+1), hdc ); DrawTree( (energy-2), rb_length, rb_angle, rb_start_x, rb_start_y, (nest+1), hdc ); } // 上に枝を伸ばす { //length double ub_length = length * FACT_UP; //angle double ub_angle = angle; //start_x, start_y double ub_start_x = x - delta_x; double ub_start_y = y - delta_y; //再帰 DrawTree( (energy-1), ub_length, ub_angle, ub_start_x, ub_start_y, (nest+0.7), hdc ); } } }
DrawTree.h
#ifndef __DRAW_TREE__H__ #define __DRAW_TREE__H__ #include <windows.h> extern void DrawTree(int energy, double length, double angle, double x, double y, double nest, HDC hdc ); #endif //__DRAW_TREE__H__
WinMain.cpp
#include <windows.h> #include <math.h> /* sin(), cos() */ #include <stdlib.h> /* srand(), rand() */ #include "DrawTree.h" #define TM (1000.0f/10.0f) //タイマー PAINTSTRUCT ps; HDC hdc, hdc2; LRESULT CALLBACK WndProc(HWND hwnd, UINT msg , WPARAM wp , LPARAM lp) { static unsigned i=0; switch (msg) { case WM_CREATE: { SetTimer(hwnd, 1, static_cast<UINT>(TM), NULL); return 0; } //break; case WM_DESTROY: { KillTimer(hwnd,1); ReleaseDC(hwnd, hdc2); PostQuitMessage(0); return 0; } //break; case WM_PAINT: { /* // 描画処理 { treeingProcedural.DrawTree(); } return; */ /* //<描画処理> //・5つの木を、200ピクセル間隔で描画する。 //・1単位時間毎に、各木を25ピクセル右に動かす。 //・8単位時間毎に、 // (1)各木の位置を、元に戻す。 // (2)各木の乱数の種を、それぞれの右の木にずらす。 */ hdc = BeginPaint(hwnd, &ps); { srand(i/40 + 4); DrawTree( 8, 50.0f, 0, -100 + i%40 * 5, 300, 0, hdc ); srand(i/40 + 3); DrawTree( 8, 50.0f, 0, 100 + i%40 * 5, 300, 0, hdc ); srand(i/40 + 2); DrawTree( 8, 50.0f, 0, 300 + i%40 * 5, 300, 0, hdc ); srand(i/40 + 1); DrawTree( 8, 50.0f, 0, 500 + i%40 * 5, 300, 0, hdc ); srand(i/40); DrawTree( 8, 50.0f, 0, 700 + i%40 * 5, 300, 0, hdc ); } EndPaint(hwnd , &ps); return 0; } //break; case WM_TIMER: { // カウント i+=1; // WM_PAINT呼び出し InvalidateRect( hwnd, NULL, TRUE ); //表示を無効に return 0; } //break; } return DefWindowProc( hwnd, msg, wp, lp ); } int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow) { MSG msg; WNDCLASS winc; HWND hwnd; winc.style = CS_HREDRAW | CS_VREDRAW; winc.lpfnWndProc = WndProc; winc.cbClsExtra = 0; winc.cbWndExtra = 0; winc.hInstance = hInstance; winc.hIcon = LoadIcon(NULL , IDI_APPLICATION); winc.hCursor = LoadCursor(NULL , IDC_ARROW); winc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); winc.lpszMenuName = NULL; winc.lpszClassName = TEXT("CLASS"); if (!RegisterClass(&winc)) return -1; hwnd = CreateWindow( TEXT("CLASS") , TEXT("ProceduralTree") , WS_OVERLAPPEDWINDOW | WS_VISIBLE , CW_USEDEFAULT , CW_USEDEFAULT , 800, 500, NULL , NULL , hInstance , NULL ); if (hwnd == NULL) return -1; while( GetMessage(&msg , NULL , 0 , 0) ) { DispatchMessage(&msg); } return msg.wParam; }