メインページ | ネームスペース一覧 | クラス階層 | 構成 | Directories | ファイル一覧 | ネームスペースメンバ | 構成メンバ | ファイルメンバ | 関連ページ

CDQuickHull3DImp.h

00001 #ifndef CDQUICKHULL3DIMP_H
00002 #define CDQUICKHULL3DIMP_H
00003 
00004 #include "CDQuickHull3D.h"
00005 #include <Base/Affine.h>
00006 #include <Base/BaseDebug.h>
00007 #include <vector>
00008 #include <float.h>
00009 
00010 namespace Spr{;
00011 
00012 template <class TVtx>
00013 void CDQHPlane<TVtx>::Clear(){          ///<    メモリクリア.使う前に呼ぶ.
00014     deleted = false;
00015 }
00016 template <class TVtx>
00017 bool CDQHPlane<TVtx>::Visible(TVtx* p){
00018     //  面が有限の距離にある場合
00019     Vec3d GetPos = p->GetPos();
00020     return GetPos*normal > dist;
00021 }
00022 template <class TVtx>
00023 int CDQHPlane<TVtx>::GetVtxID(TVtx* v){
00024     int i;
00025     for(i=0; i<3; ++i) if(v == vtx[i]) break;
00026     return i;
00027 }
00028 template <class TVtx>
00029 void CDQHPlane<TVtx>::CalcNormal(){
00030     Vec3d pos0 = vtx[0]->GetPos();
00031     Vec3d pos1 = vtx[1]->GetPos();
00032     Vec3d pos2 = vtx[2]->GetPos();
00033     Vec3d a = pos1 - pos0;
00034     Vec3d b = pos2 - pos0;
00035     normal = a ^ b;
00036     assert(normal.norm());
00037     normal.unitize();
00038     dist = pos0 * normal;
00039 }
00040 template <class TVtx>
00041 void CDQHPlane<TVtx>::Reverse(){    
00042     normal = -normal;
00043     dist = -dist;
00044     std::swap(vtx[0], vtx[2]);
00045 }
00046 template <class TVtx>
00047 double CDQHPlane<TVtx>::CalcDist(TVtx* v){
00048     //  精度を考慮して距離を計算
00049     Vec3d vPos = v->GetPos();
00050     Vec3d diffMin = vPos - Vec3d(vtx[0]->GetPos());
00051 /*
00052     Vec3d diff = vPos - Vec3d(vtx[1]->GetPos());
00053     if (diff.square() < diffMin.square()) diffMin = diff;
00054     diff = vPos - Vec3d(vtx[2]->GetPos());
00055     if (diff.square() < diffMin.square()) diffMin = diff;
00056 */
00057     return diffMin * normal;
00058 }
00059 template <class TVtx>
00060 void CDQHPlane<TVtx>::Print(std::ostream& os) const {
00061     os << "Plane:";
00062     for(int i=0; i<3; ++i){
00063         os << " " << *vtx[i];
00064     }
00065     os << " d:" << dist << " n:" << normal;
00066     if (deleted) os << " del";
00067     os << std::endl;
00068 }
00069 
00070 template <class TVtx>
00071 std::ostream& operator << (std::ostream& os, const CDQHPlane<TVtx>& pl){
00072     pl.Print(os);
00073     return os;
00074 }
00075 
00076 template <class TVtx>
00077 unsigned CDQHPlanes<TVtx>::size(){
00078     return end - begin; 
00079 }
00080 template <class TVtx>
00081 CDQHPlanes<TVtx>::CDQHPlanes(int l):len(l){
00082     buffer = new CDQHPlane[len];
00083     Clear();
00084 }
00085 template <class TVtx>
00086 void CDQHPlanes<TVtx>::Clear(){
00087     begin = buffer;
00088     end = begin;
00089     nPlanes = 0;
00090 }
00091 template <class TVtx>
00092 CDQHPlanes<TVtx>::~CDQHPlanes(){
00093     delete [] buffer;
00094 }
00095 /** bからeまでの頂点から凸包を作る.使用した頂点はbからvtxBegin,
00096 使用しなかった頂点は,vtxBeginからeに移動する. 
00097 beginからendは頂点を3つ含む面になる.それらの面うち凸包に使われた面
00098 は CDQHPlane::deleted が false になっている.   */
00099 template <class TVtx>
00100 void CDQHPlanes<TVtx>::CreateConvexHull(TVtx** b, TVtx** e){
00101     vtxBeginInput = b;
00102     vtxEndInput = e;
00103     vtxBegin = b;
00104     vtxEnd = e;
00105     //  最初の面を作る
00106     CreateFirstConvex();
00107     HULL_DEBUG_EVAL(
00108         DSTR << "First:" << begin->vtx[0]->GetPos() << begin->vtx[1]->GetPos() << begin->vtx[2]->GetPos() << std::endl;
00109         if ((begin->vtx[1]->GetPos() - begin->vtx[0]->GetPos()).norm() < 0.001) {
00110             DSTR << "same error" << std::endl;
00111         }
00112         DSTR << begin->dist << begin->normal << std::endl;
00113     )
00114     for(CDQHPlane* cur = begin; cur != end; ++cur){
00115         if (cur->deleted) continue;
00116         TreatPlane(cur);
00117         assert(end < buffer+len);
00118     }
00119 }
00120     
00121 template <class TVtx>
00122 void CDQHPlanes<TVtx>::Print(std::ostream& os) const {
00123     int num=0;
00124     for(const CDQHPlane* it = begin; it != end; ++it){
00125         if (!it->deleted) num ++;
00126     }
00127     os << num << " planes." << std::endl;
00128     for(const CDQHPlane* it = begin; it != end; ++it){
00129         it->Print(os);
00130     }
00131 }
00132 
00133 template <class TVtx>
00134 void CDQHPlanes<TVtx>::CreateFirstConvex(){
00135     CDQHPlanes& planes = *this;
00136     double xMin, xMax;
00137     TVtxs::iterator it, xMinVtx, xMaxVtx;
00138     xMin = xMax = (*vtxBegin)->GetPos().X();
00139     xMinVtx = xMaxVtx = vtxBegin;
00140     //  最大と最小を見つける
00141     for(it = vtxBegin+1; it != vtxEnd; ++it){
00142         double x = (*it)->GetPos().X();
00143         if (x < xMin){
00144             xMin = x;
00145             xMinVtx = it;
00146         }
00147         if (x > xMax){
00148             xMax = x;
00149             xMaxVtx = it;
00150         }
00151     }
00152     //  最大を最初,最小を最初から2番目に置く
00153     std::swap(*xMaxVtx, vtxBegin[0]);       //  先頭と最大を入れ替え
00154     if (xMinVtx == vtxBegin){               //  先頭が最小だったら
00155         std::swap(*xMaxVtx, vtxBegin[1]);   //  最大だった場所=先頭が入っている場所が最小
00156     }else{
00157         std::swap(*xMinVtx, vtxBegin[1]);   //  最小を先頭から2番目と入れ替え
00158     }
00159     
00160     //  2つの頂点が作る辺から一番遠い点を見つける
00161     Vec3d dir = vtxBegin[1]->GetPos() - vtxBegin[0]->GetPos();
00162     dir.unitize();
00163     double maxDist = -1;
00164     TVtxs::iterator third;
00165     for(it = vtxBegin+2; it != vtxEnd; ++it){
00166         Vec3d v0 = Vec3d((*it)->GetPos()) - Vec3d(vtxBegin[0]->GetPos());
00167         Vec3d v1 = Vec3d((*it)->GetPos()) - Vec3d(vtxBegin[1]->GetPos());
00168         Vec3d v = v0.square() < v1.square() ? v0 : v1;
00169         double dist2 = v.square() - Square(v*dir);
00170         if (dist2 > maxDist){
00171             maxDist = dist2;
00172             third = it;
00173         }
00174     }
00175     std::swap(*third, vtxBegin[2]);
00176     planes.end->Clear();
00177     planes.end->vtx[0] = vtxBegin[2];
00178     planes.end->vtx[1] = vtxBegin[1];
00179     planes.end->vtx[2] = vtxBegin[0];
00180     planes.end->CalcNormal();
00181     planes.end++;
00182     
00183     //  裏表を作って最初の凸多面体にする.
00184     *planes.end = *planes.begin;
00185     planes.end->Reverse();
00186     planes.begin->neighbor[0] = planes.end;
00187     planes.begin->neighbor[1] = planes.end;
00188     planes.begin->neighbor[2] = planes.end;
00189     planes.end->neighbor[0] = planes.begin;
00190     planes.end->neighbor[1] = planes.begin;
00191     planes.end->neighbor[2] = planes.begin;
00192     planes.end++;
00193     //  使用した頂点を頂点リストからはずす.
00194     vtxBegin += 3;
00195     nPlanes = 2;
00196 }
00197 
00198 /** horizon を作る. cur が穴をあける面,vtx が新しい頂点.
00199 rv にhorizonを辺に持つ3角形を1つ返す.*/
00200 template <class TVtx>
00201 void CDQHPlanes<TVtx>::FindHorizon(TVtx*& rv, CDQHPlane* cur, TVtx* vtx){
00202     //  curの削除.隣の面からの参照も消す.
00203     for(int i=0; i<3; ++i){
00204         CDQHPlane* next = cur->neighbor[i];
00205         if (!next) continue;
00206         for(int j=0; j<3; ++j){
00207             if (next->neighbor[j] == cur){
00208                 next->neighbor[j] = NULL;
00209                 break;
00210             }   
00211         }
00212     }
00213     cur->deleted = true;
00214     nPlanes --;
00215     //  隣の面について,裏表を判定して処理
00216     bool bRecurse = false;
00217     TVtx* rvc;
00218     for(int i=0; i<3; ++i){
00219         CDQHPlane* next = cur->neighbor[i];
00220         if (!next) continue;
00221         if (next->Visible(vtx) && nPlanes>1){   //  見える面には穴をあける.
00222             FindHorizon(rv, next, vtx);
00223             bRecurse = true;
00224         }else{
00225             //  地平線を作る頂点にこの面を登録する.
00226             cur->vtx[i]->horizon = next;
00227             rvc = cur->vtx[i];
00228         }
00229     }
00230     if (!bRecurse){
00231         rv = rvc;
00232     }
00233 }
00234 /** 頂点とhorizonの間にコーンを作る.*/
00235 template <class TVtx>
00236 void CDQHPlanes<TVtx>::CreateCone(TVtx* firstVtx, TVtx* top){
00237     CDQHPlanes& planes = *this; 
00238     TVtx* curVtx = firstVtx;
00239     CDQHPlane* curHorizon = firstVtx->horizon;
00240     HULL_DEBUG_EVAL( std::cout << "Horizon:" << *curVtx; )
00241     //  最初の面を張る
00242     int curVtxID = curHorizon->GetVtxID(curVtx);
00243     int prevVtxID = (curVtxID+2)%3;
00244     //  面の作成
00245     planes.end->Clear();
00246     planes.end->vtx[0] = curHorizon->vtx[curVtxID];
00247     planes.end->vtx[1] = curHorizon->vtx[prevVtxID];
00248     planes.end->vtx[2] = top;
00249     planes.end->CalcNormal();
00250     //  curHorizonと接続
00251     planes.end->neighbor[0] = curHorizon;
00252     curHorizon->neighbor[prevVtxID] = planes.end;
00253     CDQHPlane* firstPlane = planes.end;
00254     //  面の作成完了
00255     planes.end ++;
00256     nPlanes ++;
00257     
00258     //  curHorizon の隣りの3角形を見つけて,curHorizonを更新
00259     curVtx = curHorizon->vtx[prevVtxID];
00260     curHorizon = curVtx->horizon;
00261 
00262     //  2番目以降を張る
00263     while(1){
00264         HULL_DEBUG_EVAL(std::cout << *curVtx;);
00265         int curVtxID = curHorizon->GetVtxID(curVtx);
00266         int prevVtxID = (curVtxID+2)%3;
00267         //  面の作成
00268         planes.end->Clear();
00269         planes.end->vtx[0] = curHorizon->vtx[curVtxID];
00270         planes.end->vtx[1] = curHorizon->vtx[prevVtxID];
00271         planes.end->vtx[2] = top;
00272         planes.end->CalcNormal();
00273         //  curHorizonと接続
00274         planes.end->neighbor[0] = curHorizon;
00275         curHorizon->neighbor[prevVtxID] = planes.end;
00276         //  1つ前に作った面と今作った面を接続
00277         planes.end[-1].neighbor[1] = planes.end;
00278         planes.end->neighbor[2] = planes.end-1;
00279         //  面の作成完了
00280         planes.end ++;
00281         if (planes.end - planes.begin > len-1){
00282             DSTR << "too many planes:" << std::endl;
00283             for(TVtx** p = vtxBeginInput; p != vtxEndInput; ++p){
00284                 DSTR << (*p)->GetPos() << std::endl;
00285             }
00286             DSTR << vtxEndInput - vtxBeginInput << " vertices." << std::endl;
00287 
00288             curHorizon = firstVtx->horizon;
00289             for(TVtx** p = vtxBeginInput; p != vtxEndInput; ++p){
00290                 if (firstVtx == *p){
00291                     DSTR << p - vtxBeginInput << " ";
00292                 }
00293             }
00294             for(int i=0; i<len; ++i){
00295                 curVtxID = curHorizon->GetVtxID(curVtx);
00296                 prevVtxID = (curVtxID+2)%3;
00297                 curVtx = curHorizon->vtx[prevVtxID];
00298                 curHorizon = curVtx->horizon;
00299                 for(TVtx** p = vtxBeginInput; p != vtxEndInput; ++p){
00300                     if (curVtx == *p){
00301                         DSTR << p - vtxBeginInput << " ";
00302                     }
00303                 }
00304             }
00305             DSTR << std::endl;
00306             exit(0);
00307         }
00308         nPlanes ++;
00309         
00310         //  curHorizon の隣りの3角形を見つけて,curHorizonを更新
00311         curVtx = curHorizon->vtx[prevVtxID];
00312         if (curVtx == firstVtx) break;
00313         curHorizon = curVtx->horizon;
00314     }
00315     HULL_DEBUG_EVAL( std::cout << std::endl;);
00316     //  最後の面とfirstPlaneを接続
00317     firstPlane->neighbor[2] = planes.end-1;
00318     planes.end[-1].neighbor[1] = firstPlane;
00319 }   
00320 /** 一番遠くの頂点を見つける.見つけたらそれを頂点リストからはずす  */
00321 template <class TVtx>
00322 bool CDQHPlanes<TVtx>::FindFarthest(CDQHPlane* plane){
00323     TVtx** maxVtx=NULL;
00324     double maxDist = HULL_EPSILON;
00325     for(TVtx** it=vtxBegin; it!= vtxEnd; ++it){
00326         double dist = plane->CalcDist(*it);
00327         if (dist > maxDist){
00328             maxDist = dist; 
00329             maxVtx = it;
00330         }
00331     }
00332     if (maxVtx){
00333         std::swap(*vtxBegin, *maxVtx);
00334         vtxBegin++;
00335 #ifdef _DEBUG
00336         if (    vtxBegin[-1]->GetPos() == plane->vtx[0]->GetPos()
00337             ||  vtxBegin[-1]->GetPos() == plane->vtx[1]->GetPos()
00338             ||  vtxBegin[-1]->GetPos() == plane->vtx[2]->GetPos()){
00339             DSTR << "Error: same position." << std::endl;
00340             for(int i=0; i<3; ++i){
00341                 DSTR << "V" << i << ": " << plane->vtx[i]->GetPos() << std::endl;
00342             }
00343             DSTR << "P : " << vtxBegin[-1]->GetPos() << std::endl;
00344             assert(0);
00345         }
00346 #endif
00347         return true;
00348     }
00349     return false;
00350 }
00351 /*  外側 内側 の順に並べる.
00352     外側の終わり=内側の始まりが inner  
00353     面の法線が内側を向いている.(逆向きの面)    */
00354 template <class TVtx>
00355 TVtx** CDQHPlanes<TVtx>::DivideByPlaneR(CDQHPlane* plane, TVtx** start, TVtx** end){
00356     double INNER_DISTANCE = HULL_EPSILON * plane->dist;
00357     while(start != end){
00358         double d = -plane->CalcDist(*start);
00359         if (d <= INNER_DISTANCE){   //  内側の場合は後ろに移動
00360             -- end;
00361             std::swap(*end, *start);
00362         }else{
00363             ++ start;
00364         }
00365     }
00366     return start;
00367 }
00368 /*  外側 内側 の順に並べる.
00369     外側の終わり=内側の始まりが inner.
00370     面の法線は外側を向いている.*/
00371 template <class TVtx>
00372 TVtx** CDQHPlanes<TVtx>::DivideByPlane(CDQHPlane* plane, TVtx** start, TVtx** end){
00373     double INNER_DISTANCE = HULL_EPSILON * plane->dist;
00374     while(start != end){
00375         double d = plane->CalcDist(*start);
00376         if (d <= INNER_DISTANCE){   //  内側の場合は後ろに移動
00377             -- end;
00378             std::swap(*end, *start);
00379         }else{
00380             ++ start;
00381         }
00382     }
00383     return start;
00384 }
00385 /** 一つの面に対する処理を行う.一番遠くの頂点を見つけ,
00386 地平線を調べ,コーンを作り,内部の頂点をはずす.*/
00387 template <class TVtx>
00388 void CDQHPlanes<TVtx>::TreatPlane(CDQHPlane* cur){
00389     if (!FindFarthest(cur)) return;
00390     HULL_DEBUG_EVAL(
00391         DSTR << "Farthest:" << vtxBegin[-1]->GetPos();
00392         DSTR << " cmp:" << vtxBegin[-1]->GetPos()*cur->normal << " > " << cur->dist << std::endl;
00393         if (cur->dist < 0){
00394             DSTR << "faceDist:" << cur->dist << std::endl;
00395         }
00396     )
00397     //  地平線の調査
00398     TVtx* hor=NULL;
00399     FindHorizon(hor, cur, vtxBegin[-1]);
00400     HULL_DEBUG_EVAL(
00401         if (!hor){
00402             DSTR << "No Horizon Error!!" << std::endl;
00403             assert(hor);
00404         }
00405     )
00406     //  コーンの作成
00407     CDQHPlane* coneBegin = end;
00408     CreateCone(hor, vtxBegin[-1]);
00409     CDQHPlane* coneEnd = end;
00410     //  コーンに閉じ込められる頂点をvtxEndの後ろに移動
00411     TVtx** inner = DivideByPlaneR(cur, vtxBegin, vtxEnd);
00412     for(CDQHPlane* it=coneBegin; it!=coneEnd; ++it){
00413         if (it->deleted) continue;
00414         HULL_DEBUG_EVAL(
00415             std::cout << "Inner:";
00416         for(TVtx** v = inner; v != vtxEnd; ++v){
00417             std::cout << **v;
00418         }
00419         std::cout << std::endl;
00420         );
00421         inner = DivideByPlane(it, inner, vtxEnd);
00422     }
00423     HULL_DEBUG_EVAL(
00424         std::cout << "InnerFinal:";
00425     for(TVtx** v = inner; v != vtxEnd; ++v){
00426         std::cout << **v;
00427     }
00428     std::cout << std::endl;
00429     );
00430     
00431     vtxEnd = inner;
00432 }
00433 
00434 template <class TVtx>
00435 std::ostream& operator << (std::ostream& os, const CDQHPlanes<TVtx>& pls){
00436     pls.Print(os);
00437     return os;
00438 }
00439 template <class TVtx>
00440 inline std::ostream& operator << (std::ostream& os, const TYPENAME CDQHPlanes<TVtx>::TVtxs& f){
00441     f.Print(os); return os;
00442 }
00443 
00444 inline Vec3f CDQHVtx3DSample::GetPos() const {
00445     return dir * dist;
00446 }
00447 inline void CDQHVtx3DSample::SetPos(Vec3f p){
00448     float d = p.norm();
00449     dir = p / d;
00450     dist = d;
00451 }
00452 inline void CDQHVtx3DSample::Print(std::ostream& os) const {
00453 //      os << Vtx();
00454         os << id_ << " ";
00455 }
00456 inline std::ostream& operator << (std::ostream& os, const CDQHVtx3DSample& f){
00457     f.Print(os); return os;
00458 }
00459 
00460 }
00461 
00462 #endif

Springheadに対してSun Apr 16 01:57:49 2006に生成されました。  doxygen 1.4.1