• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            SoRoMan

            人若無名,便可專心練劍.

              C++博客 :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
              12 隨筆 :: 1 文章 :: 41 評論 :: 0 Trackbacks

            2006年9月20日 #

            http://www.cnblogs.com/soroman
            posted @ 2006-09-20 14:55 SoRoMan 閱讀(280) | 評論 (0)編輯 收藏

            2006年9月19日 #

            3D空間中的旋轉可用旋轉矩陣、歐拉角或四元數等形式來表示,他們不過都是數學工具,其中在繞任意向量的旋轉方面,旋轉矩陣和四元數兩種工具用的較多,歐拉角由于存在萬向節死鎖等問題,使用存在限制。

            (本文假設坐標系為左手坐標系中,旋轉方向為順時針。)

            所求問題:

            給定任意單位軸q(向量),求向量p(x,y,z)(或點p)饒q旋轉theta角度的變換后的新向量p'(或點p'):

            1.用四元數工具:
            -------------------------------------------------------------------------
            結論:構造四元數變換p'= q*p*q-1,(p,q是由向量p,q擴展成的四元數)。那么,p'轉換至對應的向量(或點)就是變換后的新向量p'(或點p')。

            其中,p',q,p,q-1均為四元數。q由向量q擴展,為q=(cos(theta/2),sin(theta/2)*q),p由向量p擴展,為p=(0,x,y,z),q-1為q的逆,因為q為單位四元數,所以q-1=q*=(cos(theta/2),-sin(theta/2)*q)。
            -------------------------------------------------------------------------

            (這個結論的證明過程可以在網上找到。這里略去。)
            下面看其時間復雜度:

            首先有個三角函數的計算時間,這個可以預先計算好,花費時間不計??紤]n個四元數相乘需進行4*4*(n-1)=16*(n-1)次乘法,15*(n-1)次加法,因為加法化費時間較少,這里僅考慮乘法。這里涉及到三個四元數的乘法,設一次乘法的時間為T,故花費16*2=32T

            2.旋轉矩陣工具:
            -------------------------------------------------------------------------
            結論:構造旋轉矩陣變換Trot,則變換后的新向量p'(或點p')為p'= p*Trot

            其中,p'(x',y',z',1),p(x,y,z,1)為向量p',p的4D齊次坐標表示,Trot =

            |t*x*x + c,???????t*x*y + s*z,??????t*x*z - s*y,???? 0|
            |t*x*y - s*z,????t*y*y + c,??????????t*y*z + s*x,????0|
            |t*x*z + s*y,?? t*y*z - s*x,???????t*z*z + c,????????0|
            |0,????????????????????0,???????????????????????0,????????????????????1|???

            c=cos(theta), s=sin(theta),t=1-c.
            -------------------------------------------------------------------------
            (這個結論的證明過程可以在網上找到。這里略去。)

            下面看其時間復雜度:

            三角函數的計算時間不計。矩陣本身的元素乘法主要是計算t*x和s*x之類,需進行12+3=15次乘法。兩個矩陣相乘的需進行n*n*n次乘法,這里n=4,所以花費4*4*4=64次乘法時間,但這里有個注意的地方就是旋轉矩陣的第4維無須考慮,即可簡化為3X3的矩陣。故花費3*3*3=27次乘法時間,總共的時間為15+27=42次乘法時間。cost=42T.

            比較來看,還是使用四元數的效率要高出一些,這在要變換的點的數目越大時,體現的越明顯。實際上,有很多3D引擎為了利用四元數運算的高效率性,一般先將矩陣轉換成四元數后進行運算再轉回為矩陣后,再傳給DirectX或OpenGL庫函數。

            關于四元數和矩陣在向量(方向)之間的進行插值的效率比較,見下篇:
            探討:物體繞任意向量的旋轉-四元數法VS.旋轉矩陣法的性能比較

            posted @ 2006-09-19 17:11 SoRoMan 閱讀(22071) | 評論 (0)編輯 收藏

            2006年9月17日 #

            感覺很多書上都沒講清楚透視投影變換的推導過程,自己推導了下,以前一直含糊的關于方形/非方形的視平面和屏幕的寬高比的問題也有了答案.本文組織如下:

            1.相機空間到視平面的變換
            2.視平面到屏幕的變換
            3.綜合
            4.一般情形

            1.相機空間到視平面的變換


            ?????????????????????? * p (xc,0, zc)
            ???????????????????? ?/ |
            ??????????????????? ?/? |
            ?????????????????? ?/?? |
            ?????????? X??? |/???? |
            ?????????? ^???? *p' |(xp,0,zp)
            ?????????? |?? / |????? |
            ?????????? |? /? |???? ?|
            ?????????? | /?? |???? ?|
            C(cam)?|/??? |???? ?|
            --------*----|----*------------->Z
            ?????????? 0??? dx?? zc
            ???? (X-Z平面的投影示圖)

            a.透視投影一般的視景體為棱臺,相機空間的物體會投影到視平面z=d,這里考慮左手坐標系,矩陣使用行優先方式。如圖所示,由相似三角形知識可知相機空間中的物體投影到視平面上的坐標為:

            xp = xc*(dx/zc)
            yp = yc*(dy/zc)

            其中,xc,yc,zc為相機空間坐標,xp,yp,zp為視平面坐標,dx,dy為x,y軸向的視距view distance,視平面到camera的距離,
            故相機空間投影到視平面上的矩陣Tcp為:

            |dx 0? 0 0? |

            |0? dy 0 0? |

            |0? 0?? 1 1? |

            |0? 0?? 0 0? |

            (驗證:Tcp右乘點p(xc,yc,zc,1)得點p'(xc*dx, yc*dy, zc, zc),轉換為3D坐標為(xc*dx/zc, yc*dy/zc, 1),正確。)

            ********************************************************************
            注:因為轉換過程中點使用的是4D齊次坐標,所以最后需轉換為3D坐標。4D齊次坐標(x,y,z,w)轉換為3D坐標的方法為除以w分量,即對應3D坐標為(x/w,y/w,z/w)。
            ********************************************************************


            考慮dx/zc和dy/zc項,如果dx != dy,則投影后x,y的比例會發生變化(原因:投影前坐標比例為xc/yc,投影后為xp/yp = xc*(dx/zc)/yc*(dy/zc) = xc*dx/yc*dy),從而投影后的圖像的x,y比例會發生變形。

            ---------------------------------------------
            結論1:所以,一般都會令d=dx=dy,即x,y向的視距相同。否則,圖像失真。
            ---------------------------------------------

            考慮視角(view angle,或視野filed of view)的問題,視角的大小不會影響到物體投影后的坐標,只會影響可視的范圍。

            在視距一樣的情況下,x,y軸的視角可以不一樣。如果一樣,那么視平面就是一個正方形的。于是有:

            tan(theta_x/2) = width_p/d
            tan(theta_y/2) = height_p/d?

            其中,theta_x,theta_y為x,y軸向的視角,width_p,height_p為視平面z=d的寬度(x軸)和高度(y軸)。
            ----------------------------------------------------------------
            結論2:視平面的寬高比rp=width_p/height_p = tan(theta_x/2)/tan(theta_y/2)。
            ----------------------------------------------------------------

            2.視平面到屏幕的變換

            下面就是視平面到屏幕的變換了,這是一個2D到2D的變換(視平面的坐標需伸縮以填滿整個屏幕空間,即在視平面中出現的所有的點要變換到屏幕上去,同時x,y軸向的比例還要維持和變換前一樣,以免比例失真,同時視平面的坐標原點和屏幕中心點(x0=(width_s)/2, y0=(height_s)/2)對應),其實,就是一個坐標縮放加平移的過程:

            xs = xp*kx + x0
            ys = -yp*ky + y0
            ?
            矩陣Tps為:

            |kx???? 0??????0 0? |

            |0????? -ky????0 0? |

            |0????? 0?????? 1 0? |

            |x0?? y0??????0 1? |

            (驗證:Tps右乘點p(xp,yp,zp,1)得點p'(xp*kx + x0, -yp*ky + y0, zp, 1),轉換為3D坐標為(xp*kx + x0, -yp*ky + y0, zp),正確。)

            其中,kx,ky(kx>0,ky>0)為x,y軸向的縮放因子(kx=(width_s)/(width_p), ky = (height_s)/(height_p),和視距相同,kx,ky的值必須一樣,否則圖像的x,y比例又會發生變形。這里-yp*ky是因為一般屏幕的y軸是向下的,跟相機空間和視平面坐標系中的y軸方向相反。
            ------------------------------------------------------------------------------------------------
            結論3: 一般令k=kx=ky,即x,y向的縮放因子相同。否則,圖像失真。
            于是有,width_s/width_p = height_s/height_p,變化等式得,rp = width_p/height_p = width_s/height_s = rs
            所以,在x,y軸視距一樣的情況下,要想最后變換到屏幕上的圖像x,y比例不失真,必須rp=rs,即視平面的寬高比和屏幕的寬高比一樣。

            -----------------------------------------------------------------------------------------------

            ********************************************************************
            注:若屏幕寬高為W,H,當W != H,即屏幕不為方形的時候,要確保投影到屏幕上的圖像x,y比例不失真,則x,y軸視角(或視野FOV)肯定不能相等。
            原因: 由結論2,3知,rp=width_p/height_p = tan(theta_x/2)/tan(theta_y/2)=width_s/height_s=rs=W/H。 故由W/H != 1 => theta_x != theta_Y.
            ********************************************************************

            3.綜合:

            相機空間點p轉換到屏幕空間點p',變換公式為:

            xs = xc*(dx/zc)*kx + x0 = xc*(d/zc)*k + x0
            ys = -yc*(dy/zc)*ky + y0 = -yc*(d/zc)*k + y0

            綜合變換矩陣(相機空間到屏幕空間)Tcs為:
            ?
            Tcs = Tcp*Tps =

            |d*k??? 0?????? 0 0? |

            |0????? -d*k??? 0 0? |

            |x0???? y0????? 1 1? |

            |0????? 0???????? 0? 0|

            ?其中,d為視距,k為屏幕寬高比或視平面寬高比,x0,y0為屏幕中心,注:最后需轉換為3D坐標。

            (驗證:Tcs右乘點p(xc,yc,zc,1)得點p'(xc*d*k + x0*zc, -yc*d*k + y0*zc, zc, zc),轉換為3D坐標為(xc*(d/zc)*k + x0, -yc*(d/zc)*k + y0, 1),正確。)

            4.一般情形:
            ?************************************
            視距為1,x軸視角為90度,屏幕寬高為W,H.
            ************************************
            ?
            代入d=1,theta_x = PI/2,x0= W/2,y0=H/2,則視平面寬高為width_p = 2。
            要確保屏幕上的圖像x,y比例不失真,即rs=rp,有
            height_p = 2/rp=2/rs=2H/W,
            k=kx=ky=width_s/width_p = W/2.

            于是,矩陣為:

            Tcs1 =?

            |W/2??? 0?????? 0 0? |

            |0????? -W/2??? 0 0? |

            |W/2??? H/2?? 1 1? |

            |0????? 0???????? 0 0? |



            ?? |W/2??? 0????????????????? 0 0? |

            |0????? -H/2*(W/H)??? 0 0? |

            |W/2??? H/2????????????? 1 1? |

            |0????? 0?????????????????? ?0 0? |

            (可以看到,y軸的縮放因子中乘上了寬高比(aspect ratio))
            ?這個矩陣較常用。

            ---------------------
            有什么問題,歡迎探討.
            ?

            posted @ 2006-09-17 00:34 SoRoMan 閱讀(8373) | 評論 (4)編輯 收藏

            2006年8月9日 #

            今天看到一個網友在談論一個關于The Tower of Babylon的, 見http://www.shnenglu.com/sicheng/archive/2006/08/09/11024.html,感覺其實就是個按關鍵字面積插入排序的問題。于是用插入排序算法實現如下。

            簡單測試了下,應該沒問題。
            We have 4 kinds of blocks, they are:
            (1,1,1)
            (1,2,3)
            (2,3,4)
            (2,3,2)
            Building the tower of babylon...
            The max height of tower is: 18.
            Totally 7 blocks are used, they are:
            (1) block (1,1,1), area = 1
            (2) block (1,2,3), area = 2
            (3) block (1,2,3), area = 3
            (4) block (2,3,2), area = 4
            (5) block (2,3,4), area = 6
            (6) block (2,3,4), area = 8
            (7) block (2,3,4), area = 12

            ------------------------------------
            TowerOfBabylon.cpp:
            ------------------------------------
            //
            // File: [TowerOfBabylon.cpp]
            // Description:[This program illustrates how to get the Tower Of Babylon. the Tower Of Babylon: Given N kinds of blocks,
            // you need to buid the tower under the condition that every upper block's area should less than the lower one.]
            // Author:[SoRoMan]
            // Date:[2006-08-08]
            // Version:[2.0]
            // History: [1.0: initial draft.
            // 2.0: add a new type TOWER
            // 3.0: chnage the sequence data structure to list structure to avoid memory access violation.]
            //

            // INCLUDES //////////////////////////////////////////////////////////////////////////
            #include "stdio.h"
            #include "malloc.h"

            // DEFINES //////////////////////////////////////////////////////////////////////////
            #define N 4

            // TYPES //////////////////////////////////////////////////////////////////////////
            typedef struct BLOCK_TYP
            {
            ?int x,y,z;
            ?int area;
            ?int height;
            ?
            ?BLOCK_TYP *pNext;
            } BLOCK, *BLOCK_PTR;

            typedef struct TOWER_TYP
            {
            ?int height;
            ?int num_block;

            ?BLOCK_PTR pTowerTop;
            } TOWER, *TOWER_PTR;

            // PROTOTYPES //////////////////////////////////////////////////////////////////////////
            TOWER_PTR BuildTower(BLOCK_PTR pBlock, int n);

            // FUNCTIONS //////////////////////////////////////////////////////////////////////////
            int main(int argc, wchar_t* argv[])
            {
            ?BLOCK blocks[N] = {{1,1,1},{2,2,2},{3,3,3},{4,4,4}};

            ?printf("We have %d kinds of blocks, they are:\n", N);
            ?int i;
            ?for(i = 0; i < N; i++)
            ?{
            ? printf("(%d,%d,%d)\n", blocks[i].x, blocks[i].y, blocks[i].z);
            ?}
            ?printf("Building the tower of babylon...\n");

            ?TOWER_PTR pT = BuildTower(blocks, N);
            ?printf("The max height of tower is: %d.\nTotally %d blocks are used, they are:\n", pT->height, pT->num_block );

            ?BLOCK_PTR pBlock = pT->pTowerTop;
            ?for(i = 0; i < pT->num_block; i++)
            ?{
            ??????? printf("(%d) block (%d,%d,%d), area = %d, height = %d\n", i+1, pBlock->x, pBlock->y, pBlock->z, pBlock->area, pBlock->height);?
            ??pBlock = pBlock->pNext;
            ?}

            ?getchar();

            ?return 0;
            }

            ////////////////////////////////////////////////////////////////////////////////
            // Algorithm of building the Tower Of Babylon.
            // Input Parameters: pBlock: pointer variable that identifies blocks sequence.
            // n: int variable that identifies how many kinds of blocks.
            // Output Parameters: None.
            // Return: pointer variable that identifies the built tower.
            ////////////////////////////////////////////////////////////////////////////////
            TOWER_PTR BuildTower(BLOCK_PTR pBlock, int n)
            {
            ?int index_block = 0;
            ?TOWER_PTR pTower = new TOWER();
            ?BLOCK_PTR pTop = new BLOCK();?
            ?pTower->pTowerTop = pTop;

            ?// initialize tower
            ?pTower->pTowerTop->x = pBlock->x;
            ?pTower->pTowerTop->y = pBlock->y;
            ?pTower->pTowerTop->z = pBlock->z;

            ?pTower->pTowerTop->area = (pBlock->x)*(pBlock->y);
            ?pTower->pTowerTop->height = pBlock->z;
            ?pTower->height = pTower->pTowerTop->height;
            ?pTower->num_block = 1;
            ???
            ?for(int i = 1; i < 3*n; i++)
            ?{
            ? index_block = i/3;
            ? if (i%3 == 0) // condition 1, area = x*y, height = z.
            ? {
            ?? (pBlock+index_block)->area = ((pBlock+index_block)->x)*((pBlock+index_block)->y);
            ?? (pBlock+index_block)->height = (pBlock+index_block)->z;
            ? }
            ? else if (i%3 == 1) // condition 2, area = x*z, height = y.
            ? {
            ?? (pBlock+index_block)->area = ((pBlock+index_block)->x)*((pBlock+index_block)->z);
            ?? (pBlock+index_block)->height = (pBlock+index_block)->y;
            ? }
            ? else // condition 3, area = y*z, height = z.
            ? {
            ?? (pBlock+index_block)->area = ((pBlock+index_block)->y)*((pBlock+index_block)->z);
            ?? (pBlock+index_block)->height = (pBlock+index_block)->x;
            ? }
            ?
            ? bool bNeedToBeAdded = true;?

            ? BLOCK_PTR pB = pTower->pTowerTop;
            ? BLOCK_PTR pPrev = pTower->pTowerTop;
            ? while(pB != NULL)
            ? {
            ?? if ((pBlock+index_block)->area < (pB->area))
            ?? {
            ?// insert new block
            ?BLOCK_PTR pNewBlock = new BLOCK();?
            ??? *pNewBlock = *(pBlock+index_block);

            ?if(pB == pPrev)
            ?{
            ??pNewBlock->pNext = pB;
            ??pTower->pTowerTop = pNewBlock;
            ?}
            ?else
            ?{
            ??pNewBlock->pNext = pPrev->pNext;
            ??pPrev->pNext = pNewBlock;
            ?}
            ?
            ??? // increase number of blocks
            ??? pTower->num_block++;
            ??? // increase height of tower
            ??? pTower->height += (pBlock+index_block)->height;
            ???
            ??? bNeedToBeAdded = false;
            ??? break;
            ?? }
            ?? else if ((pBlock+index_block)->area == (pB->area))
            ?? {
            ??? if (pB->height < ((pBlock+index_block)->height))
            ??? {
            ???? // increase height of tower
            ???? pTower->height += (pBlock+index_block)->height - pB->height;
            ???? // replace blocks
            ? BLOCK_PTR pNewBlock = new BLOCK();?
            ? *pNewBlock = *(pBlock+index_block);

            ? if (pB == pPrev)
            ? {
            ??pNewBlock->pNext = pB->pNext;
            ??pTower->pTowerTop = pNewBlock;
            ? }
            ? else
            ? {
            ?? pNewBlock->pNext = pB->pNext;
            ?? pPrev->pNext = pNewBlock;
            ? }
            ??? }

            ??? bNeedToBeAdded = false;
            ??? break;
            ?? }

            ?? pPrev = pB;
            ?? pB = pB->pNext;
            ? }
            ?
            ? if(bNeedToBeAdded)
            ? {
            ?? // add new block at the end
            ?? BLOCK_PTR pNewBlock = new BLOCK();?
            ?? *pNewBlock = *(pBlock+index_block);
            ??
            ?? pPrev->pNext = pNewBlock;
            ?? pNewBlock->pNext = NULL;

            ?? // increase number of blocks
            ?? pTower->num_block++;
            ?? // increase height of tower
            ?? pTower->height += (pBlock+index_block)->height;
            ? }
            ?}

            ?return pTower;
            }

            ?

            ?

            ?

            ?

            posted @ 2006-08-09 17:32 SoRoMan 閱讀(2595) | 評論 (2)編輯 收藏

            2006年8月8日 #

            本文只是讀者的一些關于左右坐標系的探討,有待繼續挖掘。
            參考資料:Real-Time Rendering second edtion

            一般左右坐標系的定義是這樣說的:
            在左手坐標系中,坐標軸的定義符合左手法則:左手四個手指的旋轉方向從X軸到Y軸,大拇指的指向就是Z軸。但是沒有給出數學上的定義。參考一些資料,關于左右坐標系的定義,涉及到向量叉積與左右坐標系的關系。

            從基說起:
            基是n維的歐式空間中的一組線性無關的向量V0,V1,...,Vn-1。關于線性無關向量組的定義可以去參考其他資料。

            基與坐標系的關系:
            如果一個基的行列式為正,那么它就可以形成一個右手坐標系。如果為負則形成左手坐標系。

            向量叉積與三維左右坐標系的關系:
            那么向量叉積與左右坐標系的關系有什么關系呢?由基與坐標系的關系可以推知,比如在三維歐式空間中,設有一個基(向量組)(Vx,Vy,Vz)存在,那么我們知道行列式的表達式|Vx,Vy,Vz|=(Vx*Vy).Vz,可以得知,當向量Vz的方向與向量Vx*Vy(Vx與Vy的叉積)的夾角在0-PI/2之間的時候,就一定會有(Vx*Vy).Vz>0,那么由基(向量組)Vx,Vy,Vz組成的坐標系就為右手坐標系。其實,說白了,就是向量叉積的方向規定了左右坐標系的定義。

            三維x,y,z左右手坐標系特例:
            令三維歐式空間空存在三向量Vx=(x,0,0)(x>0),Vy=(0,y,0)(y>0),Vz=(0,0,z)(注:這里規定z軸的正方向和向量Vx*Vy的方向一致。)此時,向量Vx*Vy的方向是符合右手坐標系的,因為向量Vx*Vy的方向與其自身的夾角為0,由(Vx*Vy).Vz = xyz可知,當z>0時,該向量組((x,0,0),(0,y,0),(0,0,z))構成右手坐標系。反之,若z<0,那么由(Vx*Vy).Vz = xyz<0可知,該向量組((x,0,0),(0,y,0),(0,0,z))構成左手坐標系。特例:(1,0,0),(0,1,0),(0,0,1)構成右手坐標系,(1,0,0),(0,1,0),(0,0,-1)構成左手坐標系。

            posted @ 2006-08-08 12:53 SoRoMan 閱讀(4791) | 評論 (0)編輯 收藏

            2006年7月31日 #

            //
            // File: [TestSort.cpp]
            // Description:[This file illustrate the sort methods. Make sure the length of the sequence consistent with its contents!]?
            // Author:[SoRoMan]
            // Date:[07/31/2006]
            // Version:[1.0]
            // History: []
            //?

            // INCLUDES ///////////////////////////////////////////////
            #include "stdio.h"

            // DEFINES ////////////////////////////////////////////////
            #define N 11

            // PROTOTYPES /////////////////////////////////////////////
            void QuickSort(int *seq, int lenght);
            void InsertSort(int *seq, int lenght);
            void InsertSort2(int *seq, int lenght);
            void ShellSort(int *seq, int lenght);
            void IncrementInsertSort(int *seq, int length, int increment);
            void HeapSort(int *seq, int length);
            void SelectionSort(int *seq, int length);
            void BubbleSort(int *seq, int length);
            void BiBubbleSort(int *seq, int length);
            void AdjustHeap(int *seq, int startIndex, int endIndex);

            // GLOBALS ////////////////////////////////////////////////
            int nAssignmentOperation = 0; // assignment counter
            int nComparsionOperation = 0; // comparsion counter

            // FUNCTIONS //////////////////////////////////////////////

            int main(int argc, char* argv[])
            {
            ?printf("0: Original Sequence\n");
            ?printf("1: Quick Sort\n");
            ?printf("2: Insert Sort\n");
            ?printf("3: Shell Sort\n");
            ?printf("4: Insert Sort2\n");
            ?printf("5: Heap Sort\n");
            ?printf("6: Selection Sort\n");
            ?printf("7: Bubble Sort\n");
            ?printf("8: Bi-Bubble Sort\n");
            ?printf("-1: Exit\n");?

            ?int cat = 0;?
            ?while (cat != -1)
            ?{
            ??// clear counter
            ??nAssignmentOperation = 0;
            ??nComparsionOperation = 0;

            ??//int array[N] = {600, 64,6,78,246,445,345,445,7,4885,1,600, 64,6,78,246,445,345,445,7,4885,1,600, 64,6,78,246,445,345,445,7,4885,1,600, 64,6,78,246,445,345,445,7,4885,1,600, 64,6,78,246,445,345,445,7,4885,1,
            ??//600, 64,6,78,246,445,345,445,7,4885,1,600, 64,6,78,246,445,345,445,7,4885,1,600, 64,6,78,246,445,345,445,7,4885,1,600, 64,6,78,246,445,345,445,7,4885,1,600, 64,6,78,246,445,345,445,7,4885,1};
            ??int array[N] = {600, 64,6,78,246,445,345,445,7745,4885,1};

            ??printf("\nPlease select sort method:");
            ??scanf("%d", &cat);
            ??printf("-------------------------------------------------\n");

            ??switch(cat)
            ??{
            ???case 0:printf("No Sort. The Original Sequence is\n");
            ????break;
            ???case 1:printf("Quick Sort\n"); QuickSort(array, N);
            ????break;
            ???case 2:printf("Insert Sort\n"); InsertSort(array, N);
            ????break;
            ???case 3:printf("Shell Sort\n"); ShellSort(array, N);
            ????break;
            ???case 4:printf("Insert Sort2\n"); InsertSort2(array, N);
            ????break;
            ???case 5:printf("Heap Sort\n"); HeapSort(array, N);
            ????break;
            ???case 6:printf("Selection Sort\n"); SelectionSort(array, N);
            ????break;
            ???case 7:printf("Bubble Sort\n"); BubbleSort(array, N);
            ????break;
            ???case 8:printf("Bi-Bubble Sort\n"); BiBubbleSort(array, N);
            ????break;
            ???default:printf("Exit...\n");
            ????return 0;
            ??}

            ??for(int i = 0; i < N; i++)
            ??{
            ???printf("int = %d\n", array[i]);
            ??}

            ??printf("Count of comparsion operation? = %d\n", nComparsionOperation);
            ??printf("Count of assignment operation? = %d\n", nAssignmentOperation);
            ??printf("-------------------------------------------------\n");
            ?}

            ?return 0;
            }

            inline void Swap(int *pCurrPost, int *pCurrKey)
            {
            ?nAssignmentOperation += 3;
            ?int temp;

            ?//swap value
            ?temp = *pCurrPost;
            ?*pCurrPost = *pCurrKey;
            ?*pCurrKey = temp;
            }

            ////////////////////////////////////////////////////////////////////////////////
            // Quick Sort algorithm.? (By SoRoMan, 2006.7.31)
            // 快速排序的基本思想是基于分治策略的。對于輸入的子序列L[p..r],如果規模足夠小則直接進行排序(比如用前述的冒泡、選擇、插入排序均可),否則分三步處理:
            // 1.分解(Divide):將待排序列L[p..r]劃分為兩個非空子序列L[p..q]和L[q+1..r],使L[p..q]中任一元素的值不大于L[q+1..r]中任一元素的值。具體可通過這樣的途徑實現:在序列L[p..r]中選擇數據元素L[q],經比較和移動后,L[q]將處于L[p..r]中間的適當位置,使得數據元素L[q]的值小于L[q+1..r]中任一元素的值。
            // 2.遞歸求解(Conquer):通過遞歸調用快速排序算法,分別對L[p..q]和L[q+1..r]進行排序。
            // 3.合并(Merge):由于對分解出的兩個子序列的排序是就地進行的,所以在L[p..q]和L[q+1..r]都排好序后不需要執行任何計算L[p..r]就已排好序,即自然合并。
            // 這個解決流程是符合分治法的基本步驟的。因此,快速排序法是分治法的經典應用實例之一。
            // Input Parameters: seq: pointer variable that identifies which sequence will be sorted.
            // length: int that identifies the length of the sequence.
            // Output Parameters: None.
            // Return: None.
            ////////////////////////////////////////////////////////////////////////////////
            void QuickSort(int *seq, int length)
            {
            ?if(length <= 1)
            ?{
            ??return;
            ?}
            ?else
            ?{
            ??int post = *seq; // set post
            ??int *pCurrPost = seq; // set current position of post
            ??int *pCurrKey; // current position of key

            ??for(int j = length - 1, i = 1; i <= j;)
            ??{
            ???// handle the right sub-sequence
            ???while(i <= j)
            ???{
            ????pCurrKey = seq + j;

            ????if(*pCurrKey < post)
            ????{
            ?????Swap(pCurrPost, pCurrKey);
            ?????pCurrPost = pCurrKey;

            ?????break;
            ????}
            ????else
            ????{
            ?????j--;
            ????}

            ????nComparsionOperation ++;
            ???}

            ???// handle the left sub-sequence
            ???while(i <= j)
            ???{
            ????pCurrKey = seq + i;

            ????if(*pCurrKey > post)
            ????{
            ?????Swap(pCurrPost, pCurrKey);
            ?????pCurrPost = pCurrKey;

            ?????break;
            ????}
            ????else
            ????{
            ?????i++;
            ????}

            ????nComparsionOperation ++;
            ???}
            ??}
            ??// handle two sub sequences recursively
            ??int lleng = pCurrPost - seq; // left sub sequence
            ??int rleng = length - lleng - 1; // right sub sequence
            ??QuickSort(seq, lleng);
            ??QuickSort(seq + lleng + 1, rleng);
            ?}
            }


            ////////////////////////////////////////////////////////////////////////////////
            // Insert Sort algorithm (A special increment insert sort, increment = 1).? (By SoRoMan, 2006.7.31)
            //插入排序的基本思想是,經過i-1遍處理后,L[1..i-1]己排好序。第i遍處理僅將L[i]插入L[1..i-1]的適當位置,使得L[1..i]又是排好序的序列。要達到這個目的,我們可以用順序比較的方法。首先比較L[i]和L[i-1],如果L[i-1]≤ L[i],則L[1..i]已排好序,第i遍處理就結束了;否則交換L[i]與L[i-1]的位置,繼續比較L[i-1]和L[i-2],直到找到某一個位置j(1≤j≤i-1),使得L[j] ≤L[j+1]時為止。
            //簡言之,插入排序就是每一步都將一個待排數據按其大小插入到已經排序的數據中的適當位置,直到全部插入完畢。插入排序方法分直接插入排序和折半插入排序兩種,這里只介紹直接插入排序,折半插入排序留到“查找”內容中進行
            // Input Parameters: seq: pointer variable that identifies which sequence will be sorted.
            // length: int that identifies the length of the sequence.
            // Output Parameters: None.
            // Return: None.
            ////////////////////////////////////////////////////////////////////////////////
            void InsertSort(int *seq, int length)
            {
            ?IncrementInsertSort(seq, length, 1);
            }

            void InsertSort2(int *seq, int length)
            {
            ?for(int i = 1; i < length; i++)
            ?{
            ??for (int j = 0; j < i; j++)
            ??{
            ???if(*(seq + i) < *(seq + j))
            ???{?
            ????int temp = *(seq + i);
            ????nAssignmentOperation ++;

            ????// insert, move (i-j) items
            ????for(int k = i; k > j; k-- )
            ????{
            ?????*(seq + k) = *(seq + k - 1);
            ?????nAssignmentOperation ++;
            ????}

            ????*(seq + k) = temp;
            ????nAssignmentOperation ++;

            ????nComparsionOperation ++;

            ????break;
            ???}
            ??}
            ?}
            }

            void IncrementInsertSort(int *seq, int length, int increment)
            {
            ?for(int k = 0; k < increment; k++)
            ?{
            ??for (int i = increment + k; i < length; i+=increment + k)
            ??{
            ???int temp = *(seq + i);
            ???for (int j = i; j > increment - 1;)
            ???{
            ????nComparsionOperation ++;

            ????if(temp > *(seq + j - increment))
            ????{????
            ?????*(seq + j) = *(seq + j - increment);
            ?????nAssignmentOperation ++;
            ?????j-=increment;
            ????}
            ????else
            ????{
            ?????break;
            ????}
            ???}

            ???*(seq + j) = temp;
            ???nAssignmentOperation ++;????
            ??}
            ?}?
            }

            ////////////////////////////////////////////////////////////////////////////////
            // Shell Sort algorithm (Increment insert sort, increment = increment/3 + 1.).? (By SoRoMan, 2006.7.31)
            // Input Parameters: seq: pointer variable that identifies which sequence will be sorted.
            // length: int that identifies the length of the sequence.
            // Output Parameters: None.
            // Return: None.
            ////////////////////////////////////////////////////////////////////////////////
            void ShellSort(int *seq, int length)
            {
            ?for(int increment = length; increment > 1;)
            ?{
            ??increment = increment/3 + 1;
            ??IncrementInsertSort(seq, length, increment);
            ?}
            }


            ////////////////////////////////////////////////////////////////////////////////
            // Heap Sort algorithm.? (SoRoMan, 2006.7.31)
            //
            //1.堆的概念:
            //堆是一個關鍵字序列{k1,k2,…,kn},它具有如下特性:
            //ki ≤k2i, ki ≤ k2i+1
            //或ki ≥ k2i,ki ≥ k2i+1(i=1,2,…, └n/2┘)
            //堆的特性在完全二叉樹中解釋為:完全二叉樹中任一結點的關鍵字都小于等于(或大于等于)它的左右孩子的關鍵字。
            //如下列關鍵字序列均是堆: {5,23,16,68,94,72,71,73} (小頂堆) {96,83,27,38,11,9} (大頂堆) 由堆的定義可知:若關鍵字序列{k1,k2,…,kn}是堆,則堆頂元素是n個元素序列中的最?。ɑ蜃畲螅┰亍?/p>

            //2.基本思想:
            //首先將初始待排序記錄序列建堆,則堆頂元素必為含最大關鍵字或最小關鍵字的記錄,輸出該記錄,然后將剩余記錄再調整為堆,如此反復,直至排序結束。
            //在堆排序主要需解決兩個問題:
            //① 如何建堆? 在完全二叉樹中,所有序號i>└n/2┘的結點都是葉子,因此,以這些結點為根的子樹均已是堆,這樣只需將以序號為└n/2┘, └n/2┘-1, └n/2┘-2,…,1的結點為根、的子樹都調整為堆即可。在按此次序調整每個結點時,其左、右子樹均已是堆。
            //② 若ki的左、右子樹已經是堆,如何將以ki為根的完全二叉樹也調整為堆? 因ki的左、右子樹已經是堆,所以必須在ki 和它的左、右孩子中選出最?。ɑ蜃畲螅┑慕Y點放到ki的位置上,不妨設k2I關鍵字最小,將ki與k2I交換位置,而交換之后有可能導致以k2I為根的子樹不再為堆,于是可重復上述過程,將以k2I為根的子樹調整為堆,……,如此逐層下去,最多可能一直調整到樹葉,此方法稱為"篩選法"。

            //3.性能分析:
            //堆排序的時間主要由建立初始堆和不斷重復建堆這兩部分的時間開銷構成。其中:建立初始堆時,總共需進行的關鍵字比較次數 ≤ 4n =O(n) ;排序過程中進行n-1次重建堆所進行的總比較次數不超過下式: 2*(└ log2(n-1) ┘+└ log2(n-2) ┘+…+ └ log22 ┘) < 2n └ log2n ┘=O(n log2n)因此堆排序總的時間復雜度是 O(n+ n log2n)= O(n log2n)。
            //堆排序在最壞情況下的時間復雜度也是O(nlog2n),相對于快速排序來說,這是堆排序的最大優點。
            //另外,堆排序僅需一個記錄大小供交換用的輔助空間。由于建初始堆所需的比較次數較多,所以堆排序不適宜于記錄較少的文件,但對n較大的文件還是很有效的。
            // Input Parameters: seq: pointer variable that identifies which sequence will be sorted.
            // length: int that identifies the length of the sequence.
            // Output Parameters: None.
            // Return: None.
            ////////////////////////////////////////////////////////////////////////////////
            void HeapSort(int *seq, int length)
            {
            ?for(int n = length; n > 0; n--)
            ?{
            ??for(int j = n/2; j > 0; j--)
            ??{
            ???AdjustHeap(seq, j, n);
            ??}

            ??Swap(seq, seq+n-1);??
            ?}?
            }

            void AdjustHeap(int *seq, int startIndex, int endIndex)
            {
            ?while(2*startIndex <= endIndex)
            ?{
            ??int i =? startIndex - 1;
            ??startIndex = startIndex*2;

            ??nComparsionOperation ++;
            ??if (startIndex < endIndex && *(seq + startIndex -1) < *(seq + startIndex))
            ??{
            ???startIndex++;
            ??}

            ??nComparsionOperation ++;
            ??if(*(seq + startIndex -1) > *(seq + i))
            ??{
            ???Swap(seq + startIndex - 1, seq + i);
            ??}??
            ??else
            ??{
            ???break;
            ??}
            ?}
            }

            ////////////////////////////////////////////////////////////////////////////////
            // Selection Sort algorithm.? (SoRoMan, 2006.7.31)
            // Input Parameters: seq: pointer variable that identifies which sequence will be sorted.
            // length: int that identifies the length of the sequence.
            // Output Parameters: None.
            // Return: None.
            ////////////////////////////////////////////////////////////////////////////////
            void SelectionSort(int *seq, int length)
            {
            ?for(int i = 0; i < length; i++)
            ?{
            ??int k = i;
            ??for(int j = i+1; j < length; j++)
            ??{
            ???nComparsionOperation ++;
            ???if (*(seq + j) < *(seq + k))
            ???{
            ????k = j;
            ???}
            ??}

            ??Swap(seq + k, seq + i);
            ?}
            }

            ////////////////////////////////////////////////////////////////////////////////
            // Bubble Sort algorithm.? (SoRoMan, 2006.7.31)
            // Input Parameters: seq: pointer variable that identifies which sequence will be sorted.
            // length: int that identifies the length of the sequence.
            // Output Parameters: None.
            // Return: None.
            ////////////////////////////////////////////////////////////////////////////////
            void BubbleSort(int *seq, int length)
            {
            ?for(int i = 0; i < length; i++)
            ?{
            ??for(int j = length-1; j > i; j--)
            ??{
            ???nComparsionOperation ++;
            ???if (*(seq + j) < *(seq + j - 1))
            ???{
            ????Swap(seq + j, seq + j - 1);
            ???}
            ??}
            ?}
            }

            ////////////////////////////////////////////////////////////////////////////////
            // Bi-Directinal Bubble Sort algorithm.? (SoRoMan, 2006.7.31)
            //思想:
            //基于在一趟排序中,位于最后一次交換關鍵字的的后面的關鍵字序列已經是有序的,所以不用再排序。
            //相對于一般的冒泡排序,可以減少關鍵字比較次數,但交換次數不變。
            // Input Parameters: seq: pointer variable that identifies which sequence will be sorted.
            // length: int that identifies the length of the sequence.
            // Output Parameters: None.
            // Return: None.
            ////////////////////////////////////////////////////////////////////////////////
            void BiBubbleSort(int *seq, int length)
            {
            ?int k,low,up;
            ?low = 0;
            ?up = length-1;
            ?while (up > low)
            ?{
            ??k = low;??

            ??for(int i = low; i < up; i++)
            ??{
            ???nComparsionOperation ++;
            ???if (*(seq + i) > *(seq + i + 1))
            ???{
            ????Swap(seq + i, seq + i + 1);

            ????k = i;
            ???}
            ??}

            ??up = k;

            ??for(int j = up; j > low; j--)
            ??{
            ???nComparsionOperation ++;
            ???if (*(seq + j) < *(seq + j - 1))
            ???{
            ????Swap(seq + j, seq + j - 1);

            ????k = j;
            ???}
            ??}?

            ??low = k;
            ?}

            }

            ?

            ?


            ?

            posted @ 2006-07-31 15:14 SoRoMan 閱讀(1240) | 評論 (0)編輯 收藏

            2006年7月27日 #

            以前看到Bresenham畫線算法,直接拿來用,沒有去推導它,近日,參考一些資料,特整理其算法推導過程如下。各位大蝦如果知道其細節,趕緊閃過,不用浪費時間了。

            基本上Bresenham畫線算法的思路如下:

            // 假設該線段位于第一象限內且斜率大于0小于1,設起點為(x1,y1),終點為(x2,y2).
            // 根據對稱性,可推導至全象限內的線段.
            1.畫起點(x1,y1).
            2.準備畫下個點。x坐標增1,判斷如果達到終點,則完成。否則,由圖中可知,下個要畫的點要么為當前點的右鄰接點,要么是當前點的右上鄰接點.
            2.1.如果線段ax+by+c=0與x=x1+1的交點的y坐標大于M點的y坐標的話,下個點為U(x1+1,y1+1)
            2.2.否則,下個點為B(x1+1,y1+1)
            3.畫點(U或者B).
            4.跳回第2步.
            5.結束.


            這里需要細化的是怎么判斷下個要畫的點為當前點的右鄰接點還是當前點的右上鄰接點.

            設線段方程:ax+by+c=0(x1<x<x2,y1<y<y2)
            令dx=x2-x1,dy=y2-y1
            則:斜率-a/b = dy/dx.

            從第一個點開始,我們有F(x,1,y1) = a*x1+b*y1+c=0
            下面求線段ax+by+c=0與x=x1+1的交點:
            由a*(x1+1)+b*y+c = 0, 求出交點坐標y=(-c-a(x1+1))/b
            所以交點與M的y坐標差值Sub1 = (-c-a(x1+1))/b - (y1+0.5) = -a/b-0.5,即Sub1的處始值為-a/b-0.5。

            則可得條件當 Sub1 = -a/b-0.5>0時候,即下個點為U.
            反之,下個點為B.
            代入a/b,則Sub1 = dy/dx-0.5.

            因為是個循環中都要判斷Sub,所以得求出循環下的Sub表達式,我們可以求出Sub的差值的表達式.下面求x=x1+2時的Sub,即Sub2
            1.如果下下個點是下個點的右上鄰接點,則
            Sub2 = (-c-a(x1+2))/b - (y1+1.5) = -2a/b - 1.5
            故Sub差值Dsub = Sub2 - Sub1 = -2a/b - 1.5 - (-a/b-0.5) = -a/b - 1.代入a/b得Dsub = dy/dx -1;
            2.如果下下個點是下個點的右鄰接點,
            Sub2 = (-c-a(x1+2))/b - (y1+0.5) = -2a/b - 0.5
            故Sub差值Dsub = Sub2 - Sub1 = -2a/b - 0.5 - (-a/b-0.5) = -a/b. 代入a/b得Dsub = dy/dx;

            于是,我們有了Sub的處始值Sub1 = -a/b-0.5 = dy/dx-0.5,又有了Sub的差值的表達式Dsub = dy/dx -1 (當Sub1 > 0)或 dy/dx(當Sub1 < 0).細化工作完成。

            于是pcode可以細化如下:

            // Pcode for Bresenham Line
            // By SoRoMan
            x=x1;
            y=y1;
            dx = x2-x1;
            dy = y2-y1;
            Sub = dy/dx-0.5; // 賦初值,下個要畫的點與中點的差值

            DrawPixel(x, y); // 畫起點
            while(x<x2)
            {
            ?x++;
            ?if(Sub > 0) // 下個要畫的點為當前點的右上鄰接點
            ?{
            ? Sub += dy/dx - 1; //下下個要畫的點與中點的差值
            ? y++; // 右上鄰接點y需增1
            ?}
            ?else// 下個要畫的點為當前點的右鄰接點
            ?{
            ? Sub += dy/dx;?
            ?}
            // 畫下個點
            DrawPixel(x,y);
            }

            PS:一般優化:
            為避免小數轉整數以及除法運算,由于Sub只是用來進行正負判斷,所以可以令Sub = 2*dx*Sub = 2dy-dx,則
            相應的DSub = 2dy - 2dx或2dy.

            思考1:如果Sub = 0時,會產生取兩個點都可以的問題。這個問題還沒深入。

            posted @ 2006-07-27 11:07 SoRoMan 閱讀(24399) | 評論 (4)編輯 收藏

            2006年7月26日 #

            思考:計算機圖形學中的左上像素填充約定(top-left filling convention for filling geometry)

            我們知道目前顯示器顯示的原理,光柵化顯示最小的單元是pixel(或者說精度為piexl),一個pixel所占的區域一般是個正方形,顯示器上的畫面就由一個或多個這樣的小方格組成。如果以每個小方格為一個單元來制定屏幕坐標的話,問題就來了,因為方格本身有大小,不是一個嚴格意義上的"點"。

            問題來源:屏幕坐標點的選取以及像素方格本身的大小
            如果以方格中心點為基準制定坐標點(即對于任意一整數坐標點p(x,y),p必為某個像素方格的中心),以一個方格為跨度來制定屏幕坐標系的話。如圖,屏幕上3X3的方格組成一個正方形的畫面,如果按照方格中心點為基準制定坐標點,其區域屏幕坐標為(0,0,2,2)(注意:0,1,2都位于方格中心,其中,左上角坐標為(0,0),右下角坐標為(2,2)),而按照數學運算,其上下跨距就是2-0 = 2 pixels,左右跨距2-0 = 2 pixels,即總共包含2X2=4個方格.而實際畫面中的跨距是3X3,即共包含9個方格(從最左上端到最右下端),問題來了,前后矛盾了。左上像素填充約定可以解決這個問題。

            問題解決:左上像素填充約定
            還是舉剛才的例子,如果給定左上角坐標為(0,0),右下角坐標為(2,2)的矩形區域,叫顯示設備去繪制的話,按照左上像素填充約定,在屏幕上填充的像素點將是:(0,0),(0,1),(1,0),(1,0)這個2X2區域,而非圖中所示的3X3區域。反過來說圖中所示的3X3區域要用坐標表示的話應該是(0,0,3,3)。簡單來說,在左上像素填充約定下,要填充(x1,y1,x2,y2)(x1,y1,x2,y2為整數)的矩形,實際填充的區域會是(x1-0.5,y1-0.5,x2-0.5,y2-0.5),這個區域的左上角像素坐標為(x1,y1),右下角像素坐標為(x2-1,y2-1),即實際的填充區域會向左和向上各偏移0.5個像素。故稱左上像素填充約定。

            ? 0 1?2
            ?? 0 口口口
            ?? 1 口口口
            ?? 2 口口口

            D3D, GDI, OpenGL等圖形庫使用左上填充約定的,如果接觸過這些圖形庫,你可能會知道這個約定。還記得畫一個全屏幕的矩形吧:DrawRectangle(0, 0, Screen_Width-1, Screen_Height-1)。

            想法一:想到別的填充約定,比如右上,左下,右下等,這些不知道有哪些系統使用。

            想法二:如果以像素方格的左上角為基準制定坐標點(即對于任意一整數坐標點p(x,y),p必為某個像素方格的左上角),情況怎么樣?這時,給定(0,0,2,2),則填充時可以還會遇到一樣的問題,填充(0,0),(0,1),(1,0),(1,0),(2,0),(2,1),(0,2),(1,2),(2,2),最后還是包含了3X3= 9個像素。

            想法三:
            to be continued...

            posted @ 2006-07-26 16:55 SoRoMan 閱讀(2129) | 評論 (7)編輯 收藏

            2006年7月24日 #


            Self Training-Rendering
            Tool
            ???Maya Basic
            ???Maya Rendering Basic
            ???Maya API/SDK
            Library
            ???OpenGL Basic
            Theory
            ???Computering Geometry
            ???Real-Time Rendering
            ??????Graphic Algorithm
            ?????????Draw
            ????????????Draw Line
            ????????????Draw Circle
            ?????????Clip
            ????????????Object Space
            ????????????Image Space
            ???Linear Math

            To be continued...
            posted @ 2006-07-24 17:33 SoRoMan 閱讀(385) | 評論 (1)編輯 收藏

            2006年7月16日 #

                 摘要: <轉貼-To Me>概述 Singleton模式? 五種實現 ...  閱讀全文
            posted @ 2006-07-16 23:12 SoRoMan 閱讀(1551) | 評論 (3)編輯 收藏

            僅列出標題  下一頁
            久久久久久综合网天天| 国产精品一区二区久久精品无码| 国产精品欧美亚洲韩国日本久久 | 久久99精品国产99久久6男男| 精品精品国产自在久久高清| 久久精品亚洲乱码伦伦中文| 久久久久久久精品成人热色戒| 国产精品99久久99久久久| 久久久久无码精品国产app| 东方aⅴ免费观看久久av| 一本一道久久精品综合| 一本一本久久aa综合精品| 久久国产精品视频| 99久久国产综合精品麻豆| 一级做a爰片久久毛片毛片| 91久久精品电影| 久久国产色AV免费看| 久久午夜免费视频| 国产香蕉97碰碰久久人人| 国产精品99久久99久久久| 久久免费视频1| 一本久久免费视频| 国产精品成人无码久久久久久 | 久久一日本道色综合久久| 精品国产婷婷久久久| 狠狠狠色丁香婷婷综合久久五月| 久久久久久久97| 免费无码国产欧美久久18| 亚洲精品乱码久久久久久蜜桃| 久久91精品综合国产首页| 国产精品成人久久久久久久| 一本大道加勒比久久综合| 亚洲国产成人久久综合碰碰动漫3d| 久久亚洲AV成人无码电影| 亚洲精品无码久久久久去q| 久久国产免费直播| 亚洲午夜无码久久久久| 久久精品国产亚洲精品2020| 97热久久免费频精品99| 久久精品国产半推半就| 久久久久97国产精华液好用吗|