# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

edge_quad.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #include "stdpacs.h"
00027 
00028 #include "pacs/edge_quad.h"
00029 #include "pacs/global_retriever.h"
00030 
00031 using   namespace std;
00032 using   namespace NLMISC;
00033 
00034 
00035 namespace NLPACS
00036 {
00037 
00038 
00039 // ***************************************************************************
00040 const   float   CEdgeQuad::_QuadElementSize= 4; // = 4 meters.
00041 
00042 
00043 // ***************************************************************************
00044 CEdgeQuad::CEdgeQuad()
00045 {
00046         _QuadData= NULL;
00047         _QuadDataLen= 0;
00048 }
00049 // ***************************************************************************
00050 CEdgeQuad::~CEdgeQuad()
00051 {
00052         clear();
00053 }
00054 // ***************************************************************************
00055 CEdgeQuad::CEdgeQuad(const CEdgeQuad &o)
00056 {
00057         _QuadData= NULL;
00058         _QuadDataLen= 0;
00059         *this= o;
00060 }
00061 // ***************************************************************************
00062 CEdgeQuad       &CEdgeQuad::operator=(const CEdgeQuad &o)
00063 {
00064         // Alloc good quaddata.
00065         _QuadDataLen= o._QuadDataLen;
00066         delete [] _QuadData;
00067         if(_QuadDataLen>0)
00068         {
00069                 _QuadData= (uint8*)new uint8[_QuadDataLen];
00070                 // copy contents.
00071                 memcpy(_QuadData, o._QuadData, _QuadDataLen);
00072         }
00073         else
00074                 _QuadData= NULL;
00075 
00076         // copy infos.
00077         _Width= o._Width;
00078         _Height= o._Height;
00079         _X= o._X;
00080         _Y= o._Y;
00081         _EdgeEntries = o._EdgeEntries;
00082 
00083         // copy good pointers.
00084         _Quad.clear();
00085         _Quad.resize(o._Quad.size(), NULL);
00086         for(sint i=0; i<(sint)_Quad.size(); i++)
00087         {
00088                 if(o._Quad[i])
00089                 {
00090                         uint32  off= (uint32)(o._Quad[i]-o._QuadData);
00091                         _Quad[i]= _QuadData+off;
00092                 }
00093         }
00094 
00095 
00096         return *this;
00097 }
00098 
00099 // ***************************************************************************
00100 void    CEdgeQuad::clear()
00101 {
00102         delete [] _QuadData;
00103         _QuadData= NULL;
00104         _QuadDataLen= 0;
00105 
00106         _Quad.clear();
00107         _EdgeEntries.clear();
00108         _Width = 0;
00109         _Height = 0;
00110         _X = 0;
00111         _Y = 0;
00112 }
00113 
00114 // ***************************************************************************
00115 void                    CEdgeQuad::getGridBounds(sint32 &x0, sint32 &y0, sint32 &x1, sint32 &y1, const CVector &minP, const CVector &maxP) const
00116 {
00117         x0= (sint32)floor(minP.x / _QuadElementSize) - _X;
00118         y0= (sint32)floor(minP.y / _QuadElementSize) - _Y;
00119         x1= (sint32) ceil(maxP.x / _QuadElementSize) - _X;
00120         y1= (sint32) ceil(maxP.y / _QuadElementSize) - _Y;
00121         x0= max(x0, (sint32)0);
00122         y0= max(y0, (sint32)0);
00123         x1= min(x1, (sint32)_Width);
00124         y1= min(y1, (sint32)_Height);
00125 }
00126 
00127 
00128 
00129 // ***************************************************************************
00130 void                    CEdgeQuad::build(const CExteriorMesh &em,
00131                                                                  const CGlobalRetriever &global,
00132                                                                  CCollisionSurfaceTemp &cst,
00133                                                                  uint32 thisInstance)
00134 {
00135         const std::vector<CExteriorMesh::CEdge> &edges = em.getEdges();
00136 
00137         vector< list<uint16> >  tempQuad;
00138         sint                                    i, j;
00139 
00140         // first, clear any pr-build.
00141         contReset(_Quad);
00142         delete [] _QuadData;
00143         _QuadData= NULL;
00144         _QuadDataLen= 0;
00145 
00146         // don't care about the origin of the instance
00147         CVector origin = global.getInstance(thisInstance).getOrigin();
00148 
00149         // 0. Find BBox of the grid. Allocate grid.
00150         //=========================================
00151         bool            first=true;
00152         CAABBox         chainquadBBox;
00153         // run all chains.
00154         for (i=0; i<(sint)edges.size()-1; i++)
00155         {
00156                 // enlarge bbox.
00157                 if (first)
00158                         first= false, chainquadBBox.setCenter(edges[i].Start);
00159                 else
00160                         chainquadBBox.extend(edges[i].Start);
00161         }
00162 
00163         // compute X,Y,Width, Height.
00164         _X= (sint32)floor(chainquadBBox.getMin().x / _QuadElementSize);
00165         _Y= (sint32)floor(chainquadBBox.getMin().y / _QuadElementSize);
00166         _Width= (sint32)ceil(chainquadBBox.getMax().x / _QuadElementSize) - _X;
00167         _Height= (sint32)ceil(chainquadBBox.getMax().y / _QuadElementSize) - _Y;
00168 
00169         tempQuad.resize(_Width*_Height);
00170         _Quad.resize(_Width*_Height, NULL);
00171 
00172 
00173         // 1. For each edge, add them to the quadgrid.
00174         //=========================================
00175         // run all chains.
00176         for (i=0; i<(sint)edges.size()-1; i++)
00177         {
00178                 float           dnorm = (edges[i+1].Start-edges[i].Start).norm();
00179                 uint            numStep = (uint)(dnorm/0.1f)+1;
00180                 uint            step;
00181 
00182                 CVector         pbegin = edges[i].Start+origin,
00183                                         pend = edges[i+1].Start+origin;
00184 
00185                 CVector         opbegin = edges[i].Start,
00186                                         opend = edges[i+1].Start;
00187 
00188                 for (step=0; step<numStep; ++step)
00189                 {
00190                         float           lambda0 = (float)(step)/(float)(numStep);
00191                         float           lambda1 = (float)(step+1)/(float)(numStep);
00192                         CVector         p0 = pbegin*(1.0f-lambda0)+pend*(lambda0),
00193                                                 p1 = pbegin*(1.0f-lambda1)+pend*(lambda1);
00194                         CVector         op0 = opbegin*(1.0f-lambda0)+opend*(lambda0),
00195                                                 op1 = opbegin*(1.0f-lambda1)+opend*(lambda1);
00196                         CVector         s0, s1,
00197                                                 mins, maxs;
00198 
00199                         uint            prevEdge = (i-1)%(edges.size()-1);
00200                         bool            prio0 = (edges[i].Link!=-1) || (edges[prevEdge].Link!=-1);
00201 
00202                         UGlobalPosition gp0 = global.retrievePosition(p0);
00203                         global.updateHeight(gp0);
00204                         UGlobalPosition gp1 = global.retrievePosition(p1);
00205                         global.updateHeight(gp1);
00206 
00207                         if (!prio0)
00208                         {
00209                                 swap(p0, p1);
00210                                 swap(op0, op1);
00211                                 swap(gp0, gp1);
00212                         }
00213 
00214                         if (gp0.InstanceId == -1)
00215                         {
00216                                 swap(p0, p1);
00217                                 swap(op0, op1);
00218                                 swap(gp0, gp1);
00219                         }
00220                         
00221                         const TCollisionSurfaceDescVector       *pcd = global.testCylinderMove(gp0, p1-p0, 0.01f, cst);
00222 
00223                         if (pcd == NULL)
00224                         {
00225 //                              nlwarning("in CEdgeQuad::build(): testCylinderMove() returned NULL");
00226                                 continue;
00227                         }
00228 
00229                         TCollisionSurfaceDescVector     cd = (*pcd);
00230 
00231                         if (edges[i].Link != -1 && cd.size() > 0)
00232                         {
00233                                 nlwarning ("In NLPACS::CEdgeQuad::build()");
00234                                 nlwarning ("ERROR: exterior edge %d with interior link crosses some surfaces", i);
00235                                 cd.clear ();
00236                         }
00237 
00238                         // add start surface to the collision description
00239                         CCollisionSurfaceDesc   stcd;
00240                         stcd.ContactTime = 0.0f;
00241                         stcd.ContactSurface.RetrieverInstanceId = gp0.InstanceId;
00242                         stcd.ContactSurface.SurfaceId = gp0.LocalPosition.Surface;
00243                         cd.insert(cd.begin(), stcd);
00244 
00245                         // get the surface, chain ...
00246                         sint    edgeId = i;
00247                         uint16  chainId;
00248 
00249                         CSurfaceIdent   interior;
00250                         if (edges[i].Link == -1)
00251                         {
00252                                 interior.RetrieverInstanceId = -1;
00253                                 interior.SurfaceId = -1;
00254                                 chainId = 0xFFFF;
00255                         }
00256                         else
00257                         {
00258                                 interior.RetrieverInstanceId = thisInstance;
00259                                 interior.SurfaceId = em.getLink(edges[i].Link).SurfaceId;
00260                                 chainId = em.getLink(edges[i].Link).ChainId;
00261                         }
00262 
00263 
00264                         // add end point to the collision description
00265                         stcd = cd.back();
00266                         stcd.ContactTime = 1.0f;
00267                         cd.push_back(stcd);
00268 
00269                         for (j=0; j<(sint)cd.size()-1; ++j)
00270                         {
00271                                 s0 = op0*(float)(1.0-cd[j].ContactTime) + op1*(float)(cd[j].ContactTime);
00272                                 s1 = op0*(float)(1.0-cd[j+1].ContactTime) + op1*(float)(cd[j+1].ContactTime);
00273 
00274                                 mins.minof(s0, s1);
00275                                 maxs.maxof(s0, s1);
00276 
00277                                 // PrecisionPb: extend a little this edge. This is important for special case like borders on zones.
00278                                 if(mins.x-maxs.x==0)
00279                                         mins.x-=0.001f, maxs.x+=0.001f;
00280                                 if(mins.y-maxs.y==0)
00281                                         mins.y-=0.001f, maxs.y+=0.001f;
00282 
00283                                 // get bounding coordinate of this edge in the quadgrid.
00284                                 sint32  x0, y0, x1, y1;
00285                                 sint    x, y;
00286                                 getGridBounds(x0, y0, x1, y1, mins, maxs);
00287 
00288                                 CSurfaceIdent   exterior = cd[j].ContactSurface;
00289 
00290                                 uint    entry;
00291                                 for (entry=0; entry<_EdgeEntries.size(); ++entry)
00292                                 {
00293                                         if (_EdgeEntries[entry].EdgeId == edgeId &&
00294                                                 _EdgeEntries[entry].Exterior == exterior)
00295                                         {
00296                                                 if (_EdgeEntries[entry].ChainId != chainId ||
00297                                                         _EdgeEntries[entry].Interior != interior)
00298                                                 {
00299                                                         nlwarning("In NLPACS::CEdgeQuad::build()");
00300                                                         nlerror("exterior edge %d has different interior linkage", edgeId);
00301                                                 }
00302 
00303                                                 break;
00304                                         }
00305                                 }
00306 
00307                                 // if this entry didn't exist before create a new one...
00308                                 if (entry == _EdgeEntries.size())
00309                                 {
00310                                         _EdgeEntries.push_back(CExteriorEdgeEntry());
00311                                         _EdgeEntries.back().EdgeId = edgeId;
00312                                         _EdgeEntries.back().ChainId = chainId;
00313                                         _EdgeEntries.back().Interior = interior;
00314                                         _EdgeEntries.back().Exterior = exterior;
00315                                 }
00316 
00317                                 // add this edge to all the quadnode it touches.
00318                                 for(y=y0; y<y1; y++)
00319                                 {
00320                                         for(x=x0; x<x1; x++)
00321                                         {
00322                                                 // check we don't push this entry twice
00323                                                 list<uint16>::iterator  it;
00324                                                 for (it=tempQuad[y*_Width+x].begin(); it!=tempQuad[y*_Width+x].end(); ++it)
00325                                                         if (entry == *it)
00326                                                                 break;
00327                                                 if (it == tempQuad[y*_Width+x].end())
00328                                                         tempQuad[y*_Width+x].push_back(entry);
00329                                         }
00330                                 }
00331                         }
00332                 }
00333 
00334         }
00335 
00336         nlinfo("Built ExteriorEdgeQuad, linked following doors:");
00337         for (i=0; i<(sint)_EdgeEntries.size(); ++i)
00338         {
00339                 if (edges[_EdgeEntries[i].EdgeId].Link != -1 && 
00340                         (_EdgeEntries[i].Interior.RetrieverInstanceId == -1 || _EdgeEntries[i].Interior.SurfaceId == -1 ||
00341                          _EdgeEntries[i].Exterior.RetrieverInstanceId == -1 || _EdgeEntries[i].Exterior.SurfaceId == -1))
00342                 {
00343                         nlwarning("In NLPACS::CEdgeQuad::build(): exterior door %d has corrupted link", i);
00344                 }
00345                 else if (edges[_EdgeEntries[i].EdgeId].Link != -1)
00346                 {
00347                         nlinfo("Inst=%d ExtEdge=%d IntInst=%d IntSurf=%d IntChain=%d ExtInst=%d ExtSurf=%d", thisInstance, _EdgeEntries[i].EdgeId,
00348                                 _EdgeEntries[i].Interior.RetrieverInstanceId, _EdgeEntries[i].Interior.SurfaceId, _EdgeEntries[i].ChainId,
00349                                 _EdgeEntries[i].Exterior.RetrieverInstanceId, _EdgeEntries[i].Exterior.SurfaceId);
00350                 }
00351         }
00352 
00353         // 2. Mem optimisation: Use only 1 block for ALL quads of the grid.
00354         //=========================================
00355         sint    memSize= 0;
00356         // run all quads.
00357         for(i=0;i<(sint)tempQuad.size();i++)
00358         {
00359                 list<uint16>    &quadNode= tempQuad[i];
00360 
00361                 if(!quadNode.empty())
00362                 {
00363                         // add an entry for Len.
00364                         memSize+= sizeof(uint16);
00365                         // add N entry of CEdgeChainEntry.
00366                         memSize+= quadNode.size()*sizeof(uint16);
00367                 }
00368         }
00369 
00370         // allocate.
00371         _QuadData= (uint8*)new uint8[memSize];
00372         _QuadDataLen= memSize;
00373 
00374 
00375         // 3. Fill _QuadData with lists.
00376         //=========================================
00377         uint8   *ptr= _QuadData;
00378         for(i=0;i<(sint)tempQuad.size();i++)
00379         {
00380                 list<uint16>                    &srcQuadNode= tempQuad[i];
00381                 list<uint16>::iterator  it;
00382 
00383                 if(!srcQuadNode.empty())
00384                 {
00385                         _Quad[i]= ptr;
00386 
00387                         // write len.
00388                         uint16  len= srcQuadNode.size();
00389                         *((uint16*)ptr)= len;
00390                         ptr+= sizeof(uint16);
00391 
00392                         // add entries.
00393                         it= srcQuadNode.begin();
00394                         for(j=0; j<len; j++, it++)
00395                         {
00396                                 *((uint16 *)ptr)= *it;
00397                                 ptr+= sizeof(uint16);
00398                         }
00399                 }
00400         }
00401 
00402         // End.
00403 }
00404 
00405 
00406 // ***************************************************************************
00407 sint                    CEdgeQuad::selectEdges(const NLMISC::CAABBox &bbox, CCollisionSurfaceTemp &cst) const
00408 {
00409         sint    nRes=0;
00410         sint    i;
00411         uint16  *indexLUT = cst.OChainLUT;
00412 
00413         // start: no edge found.
00414         cst.ExteriorEdgeIndexes.clear();
00415 
00416         // get bounding coordinate of this bbox in the quadgrid.
00417         sint32  x0, y0, x1, y1;
00418         getGridBounds(x0, y0, x1, y1, bbox.getMin(), bbox.getMax());
00419 
00420 
00421         // run all intersected quads.
00422         for (sint y= y0; y<y1; y++)
00423         {
00424                 for (sint x= x0; x<x1; x++)
00425                 {
00426                         uint8   *quadNode= _Quad[y*_Width+x];
00427 
00428                         // no edgechain entry??
00429                         if(!quadNode)
00430                                 continue;
00431 
00432                         // get edgechain entries
00433                         sint    numExteriorEdgeIndexes= *((uint16*)quadNode);
00434                         quadNode+= sizeof(uint16);
00435                         uint16  *ptrExteriorEdgeIndex= (uint16*)quadNode;
00436 
00437                         // For each one, add it to the result list.
00438                         for (i=0;i<numExteriorEdgeIndexes;i++)
00439                         {
00440                                 uint16  index = ptrExteriorEdgeIndex[i];
00441 
00442                                 // if ochain not yet inserted.
00443                                 if (indexLUT[index]==0xFFFF)
00444                                 {
00445                                         // inc the list.
00446                                         indexLUT[index]= nRes;
00447                                         cst.ExteriorEdgeIndexes.push_back(index);
00448                                         nRes++;
00449                                 }
00450                         }
00451                 }
00452         }
00453 
00454 
00455         // reset LUT to 0xFFFF for all ochains selected.
00456         for(i=0;i<nRes;i++)
00457                 indexLUT[cst.ExteriorEdgeIndexes[i]]= 0xFFFF;
00458 
00459         return nRes;
00460 }
00461 
00462 sint            CEdgeQuad::selectEdges(CVector start, CVector end, CCollisionSurfaceTemp &cst) const
00463 {
00464         sint    nRes=0;
00465         sint    i;
00466         uint16  *indexLUT= cst.OChainLUT;
00467 
00468         // start: no edge found.
00469         cst.ExteriorEdgeIndexes.clear();
00470 
00471         if (end.x < start.x)
00472                 swap(start, end);
00473 
00474         float   minx = _X*_QuadElementSize,
00475                         miny = _Y*_QuadElementSize,
00476                         maxx = minx + _Width*_QuadElementSize,
00477                         maxy = miny + _Height*_QuadElementSize;
00478 
00479         if (start.x > maxx || end.x < minx || start.y > maxy || end.y < miny)
00480                 return nRes;
00481 
00482         if (start.x < minx)
00483         {
00484                 start.y = start.y+(end.y-start.y)*(minx-start.x)/(end.x-start.x);
00485                 start.x = minx;
00486         }
00487 
00488         if (start.y < miny)
00489         {
00490                 start.x = start.x+(end.x-start.x)*(miny-start.y)/(end.y-start.y);
00491                 start.y = miny;
00492         }
00493 
00494         if (end.x > maxx)
00495         {
00496                 end.y = start.y+(end.y-start.y)*(minx-start.x)/(end.x-start.x);
00497                 end.x = maxx;
00498         }
00499 
00500         if (end.y > maxy)
00501         {
00502                 end.x = start.x+(end.x-start.x)*(miny-start.y)/(end.y-start.y);
00503                 end.y = maxy;
00504         }
00505 
00506         sint32  x0, x1, ya, yb;
00507         sint    x, y;
00508         float   fx, fxa, fxb, fya, fyb;
00509 
00510         x0 = (sint32)floor(start.x / _QuadElementSize) - _X;
00511         x1 = (sint32)ceil(end.x / _QuadElementSize) - _X;
00512         fx = (x0+_X)*_QuadElementSize;
00513 
00514         for (x=x0; x<x1; ++x)
00515         {
00516                 fxa = (fx < start.x) ? start.x : fx;
00517                 fxb = (fx+_QuadElementSize > end.x) ? end.x : fx+_QuadElementSize;
00518 
00519                 fya = start.y+(end.y-start.y)*(fxa-start.x)/(end.x-start.x);
00520                 fyb = start.y+(end.y-start.y)*(fxb-start.x)/(end.x-start.x);
00521 
00522                 if (fya > fyb)
00523                         swap (fya, fyb);
00524 
00525                 ya = (sint32)floor(fya / _QuadElementSize) - _Y;
00526                 yb = (sint32)ceil(fyb / _QuadElementSize) - _Y;
00527 
00528                 fx += _QuadElementSize;
00529 
00530                 for (y=ya; y<yb; ++y)
00531                 {
00532                         uint8   *quadNode= _Quad[y*_Width+x];
00533 
00534                         // no edgechain entry??
00535                         if(!quadNode)
00536                                 continue;
00537 
00538                         // get edgechain entries
00539                         sint    numExteriorEdgeIndexes= *((uint16 *)quadNode);
00540                         quadNode+= sizeof(uint16);
00541                         uint16  *ptrExteriorEdgeIndex = (uint16 *)quadNode;
00542 
00543                         // For each one, add it to the result list.
00544                         for(i=0;i<numExteriorEdgeIndexes;i++)
00545                         {
00546                                 uint16  index = ptrExteriorEdgeIndex[i];
00547 
00548                                 // if ochain not yet inserted.
00549                                 if(indexLUT[index]==0xFFFF)
00550                                 {
00551                                         // inc the list.
00552                                         indexLUT[index]= nRes;
00553                                         cst.ExteriorEdgeIndexes.push_back(ptrExteriorEdgeIndex[i]);
00554                                         nRes++;
00555                                 }
00556                         }
00557                 }
00558         }
00559 
00560         // reset LUT to 0xFFFF for all ochains selected.
00561         for(i=0;i<nRes;i++)
00562                 indexLUT[cst.ExteriorEdgeIndexes[i]]= 0xFFFF;
00563 
00564         return nRes;
00565 }
00566 
00567 // ***************************************************************************
00568 void            CEdgeQuad::serial(NLMISC::IStream &f)
00569 {
00570         /*
00571         Version 0:
00572                 - base version.
00573         */
00574         (void)f.serialVersion(0);
00575         uint    i;
00576 
00577         // serial basics.
00578         f.serial(_X, _Y, _Width, _Height, _QuadDataLen);
00579         f.serialCont(_EdgeEntries);
00580 
00581         // serial _QuadData.
00582         if(f.isReading())
00583         {
00584                 delete [] _QuadData;
00585                 if(_QuadDataLen>0)
00586                         _QuadData= (uint8*)new uint8[_QuadDataLen];
00587                 else
00588                         _QuadData= NULL;
00589         }
00590         // Since we have only uint16 (see CEdgeChainEntry), serial them in a single block.
00591         uint16  *ptrQData= (uint16*)_QuadData;
00592         for(i=0;i<_QuadDataLen/2; i++, ptrQData++)
00593         {
00594                 f.serial(*ptrQData);
00595         }
00596 
00597 
00598         // serial _Quad.
00599         std::vector<uint32>             offsets;
00600         uint32          len;
00601         uint32          val;
00602         if(f.isReading())
00603         {
00604                 // len/resize.
00605                 f.serial(len);
00606                 offsets.resize(len);
00607                 contReset(_Quad);
00608                 _Quad.resize(len);
00609 
00610                 // read offsets -> ptrs.
00611                 for(i=0; i<len; i++)
00612                 {
00613                         f.serial(val);
00614                         if(val== 0xFFFFFFFF)
00615                                 _Quad[i]= NULL;
00616                         else
00617                                 _Quad[i]= _QuadData+val;
00618                 }
00619         }
00620         else
00621         {
00622                 // len/resize.
00623                 len= _Quad.size();
00624                 f.serial(len);
00625 
00626                 // write offsets.
00627                 for(i=0; i<len; i++)
00628                 {
00629                         uint8   *ptr= _Quad[i];
00630                         if(ptr==NULL)
00631                                 val= 0xFFFFFFFF;
00632                         else
00633                                 val= (uint32)(ptr-_QuadData);
00634                         f.serial(val);
00635                 }
00636         }
00637 
00638 }
00639 
00640 
00641 
00642 } // NLPACS