WHY數學表達式的3D可視化


WHY數學表達式的3D可視化

 

    很早之前我就有這種想法,將數學表達式的圖形顯示出來.最近終於實現了這套較為完善的版本,將其代碼公布,也為開源做點貢獻.首先系統中定義一套腳本語言格式,用於描述數學表達式.使用時先要將數學表達式寫成該腳本的形式,解析腳本代碼以生成相應的圖形.該系統能夠生成三維曲線圖形和曲面圖形.

開發環境:VS2008,圖形渲染用的是D3D9,用於解析數學表達式的核心代碼MathExpression只使用了C的基本庫函數,可以跨平台使用.

軟件下載地址:

http://files.cnblogs.com/WhyEngine/MathGraph_2_0.zip

代碼下載地址:

http://pan.baidu.com/s/1pJkD4Bh

早期版本地址:

http://www.cnblogs.com/WhyEngine/p/3535903.html

補丁下載地址:

http://files.cnblogs.com/WhyEngine/MathExpression.zip修改了幾個表達式解析錯誤的BUG,支持科學計數格式.

[一]軟件介紹

(1)主界面

 

 

    將數學腳本文件拖入到主界面中,可以顯示其圖形.亦可以通過菜單項打開腳本文件.

 

(2)數學腳本面板

    用於打開腳本文件,編輯腳本,保存文件,解析腳本,輸出編譯信息.

 

(3)可視化屬性面板

 

    用於對圖形顯示的設置,如果設置顏色,紋理等.有兩種類型的圖形,MESH和曲線.將紋理文件拖入軟件界面中后,會自動將其設置為MESH紋理.

 

(4)包圍盒面板

 

圖形包圍盒的顯示.

 

(5)系統配置面板

設置場景相關的屬性,並能夠保存配置文件.

 

(6)快捷方式

F5:         數學腳本編輯界面

F6:         可視化屬性面板

F7:         包圍盒面板

F9:         系統配置面板

F11:        全屏切換

ESC:       退出全屏

‘X’:        恢復為默認視角

'L':          開關燈光

'B':          包圍盒的可見屬性切換

'G':          地平面網格的可見屬性切換

'M':         坐標系軸的可見屬性切換

'N':          地面的可見屬性切換

'1':          MESH圖形中,面片可見,線框不可見

'2':          MESH圖形中,面片不可見,線框可見

'3':          MESH圖形中,面片可見,線框可見

'4':          MESH圖形中,使用紋理色

'5':          MESH圖形中,使用頂點色

'6':          MESH圖形中,使用默認色

'0':          重新編譯腳本(如果腳本中使用了隨機數函數,則重新編譯后,生成的圖形會不一樣)

 

[二]腳本語法

(1)常量

系統中默認有兩個常量值

PI    3.1415927

E     2.7182818

常量名通常為大寫字母,如 (PI + E) 或 sin(PI*0.5)

 

(2)數值解析

對整數的解析支持2,8,10,16四種進制

0X開頭為16進制, XABCDEF大小寫都可以, 如0xffff

0Y開頭為2進制, Y大小寫都可以, 如0y10101010000

0開頭為8進制, 如07523, 注意出現了"08"之類的會解析失敗

默認為10進制

對浮點數的解析只支持形如”0.12”的格式,不能省略前面的0,目前尚不支持科學表達.

 

(3)頂點數目

    所謂頂點數目是指表達式運算時所需要的輸入數據.數據分為兩類:一維數據用於生成曲線圖形,其定義如下:

vertices = 3600 // 設置頂點數目

二維數據用於生成生成MESH圖形數據,其定義如下:

vertices = dimension1:80 dimension2:160 或

vertices = D1:80 D2:160

表示第一個維度的輸入為80,第二個維度的輸入為160,整體輸入的頂點數目為80*160.

 

(4)變量及其賦值

   系統中使用a-z的26個英文小寫字母表示變量,變量能夠存儲單個實數,或一個實數數組.如果為實數數據,則其大小為之前設置的頂點數目(見3).

設置為單個實數

a = 3.1415                   // 將a賦值為3.1415

一維數組的設置

a = from 0 to 100       // a為一個實數數組,數組大小為vertices的設置,數值為線性插值求得.

二維數據的設置

a = from 0 to (2*PI) dimension1

b = from (-PI*0.5) to (PI*0.5) dimension2

或者

a = from 0 to (2*PI) D1

b = from (-PI*0.5) to (PI*0.5) D2

變量中x,y,z將組成3D頂點位置坐標

r,g,b將組成頂點顏色.其值范圍在0-1.0之間.如果沒有設置r,g,b,將使用默認方式生成頂點色.

u,v為頂點的紋理坐標,如果沒有設置u,v,將使用x,z生成頂點的紋理坐標.

 

(5)運算符

a.標准單目運算符

+,-

如:a = -b

b.標准雙目運算符

+,-,*,/,%,^

如:c = a * b 如果a,b都為單個實數則運算結果c也是單個實數,否則c為實數數組

c.函數單目運算符 形如sin(a)

positive,negative,abs,floor,ceil,sign,sqrt,exp,log,log2,log10,sin,cos,tan,asin,acos,atan,rand 

d.函數雙目運算符 形如pow(a, b)       

add,sub,multiply,divide,max,min,mod,pow,atan2,rand2,       

e.函數三目運算符 形如lerp(a, b, r)            

lerp,clamp,gray,add3,min3,max3,average3

f.函數四目運算符 形如average4(a, b, c, d)

add4,min4,max4,average4

g.函數數組運算符(輸入實數數組,輸出一個浮點數,如求最大值,最小值,數組加和等)

add_array,min_array,max_array,ave_array

h.函數數組運算符(輸入實數數組,輸出也是實數數組,如求數組左移,數組右移,前向累加等)

array_move_right,array_move_left,array_cumulate

 

(6)運算符嵌套

支持{}, [], ()這三類括號標志符,括號必需成對出現.支持最大32級括號的嵌套.如:

(2+(-2+(5.0*(9 + ((1+2)*3))/3) + 2))

-{exp[5*sqrt(1 -abs[sin{rand(100)}]) + 6]}

 

(7)示例

Sin曲線

vertices = 1200

x = from (-4*PI) to (4*PI)

y = sin(x)

 

vertices = 360

w = from 0 to (2*PI)

r = 10.0

x = r*sin(w)

y = r*cos(w)

 

曲線球

vertices = 3600

w = from 0 to 32

a = mod(w, 1) * 2 * PI

b = from 0 to PI

r = 10.0

x = r*sin(a)*sin(b)

y = r*cos(a)*sin(b)

z = r*cos(b)

 

線圈

vertices = 36000

a = rand2(8, 64)

b = rand2(4, 64)

c = a + b

s = c / b

o = rand2(4, b)

i = from 0 to (360*2*PI)

j = mod(i, 2*PI)

k = mod(s*i, 2*PI)

m = a*sin(j)

n = a*cos(j)

x = m + o*sin(k)

y = n + o*cos(k)

 

地形面

vertices = dimension1:320 dimension2:320

x = from (-4) to (4) dimension1

z = from (-4) to (4) dimension2

r = x^2 + z^2

y = sin(x^2 + z^2*3)/(0.05 + r) + (x^2 + z^2*5)*exp(1 -r)/2

 

拋物線曲面

vertices = dimension1:101 dimension2:101

x = from (-100) to (100) dimension1

z = from (-100) to (100) dimension2

y = (20000 - x^2 - z^2)*0.005

 

三維球體

vertices = dimension1:36 dimension2:72

a = from 0 to (2*PI) dimension1

b = from (-PI*0.5) to (PI*0.5) dimension2

r = 10.0

x = r*cos(b)*sin(a)

y = r*sin(b)

z = r*cos(b)*cos(a)

 

圓錐體

vertices = D1:72 D2:72

u = from 0 to (2) D2

v = from 0 to (2*PI) D1

a = 1.0

b = 0.5

c = sin(v);d = cos(v);

e = sin(b);f = cos(b);

g = sin(a);h = cos(a);

x = f*h*d - f*g*c + e*3

y = g*d + h*c

z = -e*h*d + e*g*c + f*3

x = x*u

y = y*u

z = z*u

 

心形

vertices = dimension1:80 dimension2:160

a = from 0 to (2*PI) dimension1

b = from (-PI*0.5) to (PI*0.5) dimension2

r = 10.0

c = sqrt(abs(a - PI))*1.5

x = r*cos(b)*sin(a)*c

y = -r*cos(b)*cos(a)*c

z = r*sin(b)*0.5

 

海螺

vertices = dimension1:160 dimension2:160

u = from 0 to (6*PI) dimension1

v = from 0 to (2*PI) dimension2

k = 1.2

a = 1.5

w = (k^u) * (1+cos(v))

x = w*cos(u)

y = w*sin(u)

z = (k^u)*sin(v) - (k^u)*a

 

vertices = D1:20 D2:500

u = from 0 to (2*PI) D1

v = from 0 to (2*PI) D2

a = sin(u)

b = cos(u)

c = sin(v)

d = cos(v)

r = (12 + c + b) *(1+d)

v = 10 * v

x = r*sin(v)

y = a + 32*d

z = r*cos(v)

 

 

[三]代碼與用戶自定義擴展

代碼中MathExpression模塊中為數學表達式及腳本解析的代碼邏輯.

 

(1)主要接口

文件”math_expression_parser.h”提供了對表達式的解析函數

// 設置參數值

void SetParameterValue(char c, float value);

// 清空參數值

void ClearParameterValues();

// 表達式解析

bool ParseExpression(const char* szExpression, float& rst);

// 括號內的子表達式解析

bool ParseBracketExpression(const char* szExpression,

    unsigned int& pos, float& rst,

unsigned int bracketIndex, bool endComma);

 

可以針求獨立表達式的結果,如:

bool sus = ParseExpression(“sin(PI*3) + 10*E*log(100)”, rst);

 

文件”math_script_parser.h”提供了一個對象MathScriptParser,用於對數學腳本的解析

class MathScriptParser

{

public:

    ...

    // 加載腳本文件

    bool LoadFile(const char* szFile);

    // 處理內存數據

    bool ProcessMemory(const char* bufPtr, unsigned int bufSize);

    ...

};

 

(2)添加或設置常量值

開發者可以通過”const_value.h”文件中的如下函數對常量進行添加或修改

// 添加或設置常量值

bool SetConstValue(const char* szName, float value);

 

(3)添加或設置函數運算符

程序中的運算符操作是以函數指針的方式使用,開發者可以將自己實現的函數設置到系統中.

有6種運算符函數類型:

 

// 單目運算函數

typedef float (*SINGLE_OPERATOR_FUNC)(float);

 

// 雙目運算函數

typedef float (*TWIN_OPERATOR_FUNC)(float, float);

 

// 三目運算函數

typedef float (*THREE_OPERATOR_FUNC)(float, float, float);

 

// 四目運算函數

typedef float (*FOUR_OPERATOR_FUNC)(float, float, float, float);

 

// 對ValueNode的運算函數(輸入ValueNode,輸出一個浮點數)

typedef float (*VALUENODE_TO_FLOAT_OPERATOR_FUNC)(const ValueNode*);

 

// 對ValueNode的運算函數(輸入ValueNode,輸出ValueNode)

typedef void  (*VALUENODE_CONVERT_OPERATOR_FUNC)(const ValueNode*, ValueNode*);

 

開發者可自定義運算函數后,使用如下接口,將其添加到系統中.

 

// 添加或設置運算函數

bool SetSingleOperator(const char* szName, SINGLE_OPERATOR_FUNC func);

bool SetTwinOperator(const char* szName, TWIN_OPERATOR_FUNC func);

bool SetThreeOperator(const char* szName, THREE_OPERATOR_FUNC func);

bool SetFourOperator(const char* szName, FOUR_OPERATOR_FUNC func);

bool SetValueNodeToFloatOperator(const char* szName, VALUENODE_TO_FLOAT_OPERATOR_FUNC func);

bool SetValueNodeConvertOperator2(const char* szName, VALUENODE_CONVERT_OPERATOR_FUNC func);

需要注意這兩個宏:

#define OPERATOR_NAME_LENGTH        24                // 操作符名的最大長度

#define MAX_FUNCTIONS_COUNT         256               // 運算函數的最大數目

如果不滿足要求,請自己修改這兩個宏值,不要出現內存越界.

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM