inlib
1.2.0
|
00001 // Copyright (C) 2010, Guy Barrand. All rights reserved. 00002 // See the file inlib.license for terms. 00003 00004 #ifndef inlib_CContour 00005 #define inlib_CContour 00006 00007 // G.Barrand : inline version of the one found in Lib of OSC 16.11. 00008 // This code is not mine and I keep it "as it is". 00009 00010 // Contour.h: interface for the CContour class. 00011 // 00012 // CContour implements Contour plot algorithm descrided in 00013 // IMPLEMENTATION OF 00014 // AN IMPROVED CONTOUR 00015 // PLOTTING ALGORITHM 00016 // BY 00017 // 00018 // MICHAEL JOSEPH ARAMINI 00019 // 00020 // B.S., Stevens Institute of Technology, 1980 00021 // See http://www.ultranet.com/~aramini/thesis.html 00022 // 00023 // Ported to C++ by Jonathan de Halleux. 00024 // 00025 // Using CContour : 00026 // 00027 // CContour is not directly usable. The user has to 00028 // 1. derive the function ExportLine that is 00029 // supposed to draw/store the segment of the contour 00030 // 2. Set the function draw contour of. (using SetFieldFn 00031 // The function must be declared as follows 00032 // double (*myF)(double x , double y); 00033 // 00034 // History: 00035 // 31-07-2002: 00036 // - A lot of contribution from Chenggang Zhou (better strip compressions, merging, area, weight), 00037 // - Got rid of ugly MFC lists for STL. 00039 00040 //G.Barrand : 00041 #include <vector> 00042 #include <cstdio> 00043 #include <cstdlib> 00044 #include <cmath> 00045 00046 #include "mnmx" 00047 00048 namespace inlib { 00049 00050 class CContour 00051 { 00052 protected: 00053 // plots a line from (x1,y1) to (x2,y2) 00054 virtual void ExportLine(int iPlane,int x1,int y1,int x2,int y2) = 0; 00055 00056 public: 00057 CContour(); 00058 virtual ~CContour(){CleanMemory();} 00059 protected: //G.Barrand 00060 inline CContour(const CContour&){} 00061 private: //G.Barrand 00062 inline CContour& operator=(const CContour&){return *this;} 00063 public: 00064 // Initialize memory. Called in Generate 00065 virtual void InitMemory(); 00066 // Clean work arrays 00067 virtual void CleanMemory(); 00068 // Generates contour 00069 // Before calling this functions you must 00070 // 1. derive the function ExportLine that is 00071 // supposed to draw/store the segment of the contour 00072 // 2. Set the function draw contour of. (using SetFieldFn 00073 // The function must be declared as follows 00074 // double (*myF)(double x , double y); 00075 virtual void Generate(); 00076 00077 // Set the dimension of the primary grid 00078 void SetFirstGrid(int iCol, int iRow); 00079 // Set the dimension of the base grid 00080 void SetSecondaryGrid(int iCol, int iRow); 00081 // Sets the region [left, right, bottom,top] to generate contour 00082 void SetLimits(double pLimits[4]); 00083 // Sets the isocurve values 00084 void SetPlanes(const std::vector<double>& vPlanes); 00085 // Sets the pointer to the F(x,y) funtion 00086 // G.Barrand : handle a user data pointer. 00087 void SetFieldFcn(double (*_pFieldFcn)(double, double,void*),void*); 00088 00089 // Retrieve dimension of grids, contouring region and isocurve 00090 inline int GetColFir() const { return m_iColFir;}; 00091 inline int GetRowFir() const { return m_iRowFir;}; 00092 inline int GetColSec() const { return m_iColSec;}; 00093 inline int GetRowSec() const { return m_iRowSec;}; 00094 void GetLimits(double pLimits[4]); 00095 inline unsigned int GetNPlanes() const { return m_vPlanes.size();}; 00096 inline const std::vector<double>& GetPlanes() const { return m_vPlanes;}; 00097 double GetPlane(unsigned int i) const; 00098 00099 // For an indexed point i on the sec. grid, returns x(i) 00100 inline double GetXi(int i) const { return m_pLimits[0] + i%(m_iColSec+1)*(m_pLimits[1]-m_pLimits[0])/(double)( m_iColSec );}; 00101 // For an indexed point i on the fir. grid, returns y(i) 00102 double GetYi(int i) const; 00103 00104 private: 00105 // A structure used internally by CContour 00106 struct CFnStr { 00107 double m_dFnVal; 00108 short m_sLeftLen; 00109 short m_sRightLen; 00110 short m_sTopLen; 00111 short m_sBotLen; 00112 }; 00113 00114 00115 protected: 00116 // Accesibles variables 00117 std::vector<double> m_vPlanes; // value of contour planes 00118 double m_pLimits[4]; // left, right, bottom, top 00119 int m_iColFir; // primary grid, number of columns 00120 int m_iRowFir; // primary grid, number of rows 00121 int m_iColSec; // secondary grid, number of columns 00122 int m_iRowSec; // secondary grid, number of rows 00123 void* m_pFieldFcnData; // G.Barrand : handle a user data pointer. 00124 double (*m_pFieldFcn)(double x, double y,void*); // pointer to F(x,y) function 00125 00126 // Protected function 00127 //virtual void ExportLine(int iPlane, int x1, int y1, int x2, int y2) = 0; // plots a line from (x1,y1) to (x2,y2) 00128 00129 // Work functions and variables 00130 double m_dDx; 00131 double m_dDy; 00132 CFnStr** m_ppFnData; // pointer to mesh parts 00133 inline CFnStr* FnctData(int i,int j) { return (m_ppFnData[i]+j);}; 00134 00135 double Field(int x, int y); /* evaluate funct if we must, */ 00136 void Cntr1(int x1, int x2, int y1, int y2); 00137 void Pass2(int x1, int x2, int y1, int y2); /* draws the contour lines */ 00138 00139 private: 00140 //G.Barrand : have the below in the class. 00141 // A simple test function 00142 inline static double ContourTestFunction(double x,double y,void*) { 00143 return 0.5*(::cos(x+3.14/4)+::sin(y+3.14/4)); 00144 } 00145 00146 protected: 00147 inline static void ASSERT(bool a_what,const char* a_where) { 00148 if(!a_what) { 00149 ::printf("debug : Contour : assert failure in %s\n",a_where); 00150 ::exit(0); 00151 } 00152 } 00153 00154 inline static void ASSERTP(void* a_what,const char* a_where) { 00155 if(!a_what) { 00156 ::printf("debug : Contour : assert failure in %s\n",a_where); 00157 ::exit(0); 00158 } 00159 } 00160 00161 }; 00162 00163 // implementation : 00164 // 00165 // Code get on the web at : 00166 // http://www.codeproject.com/cpp/contour.asp 00167 // 00168 // G.Barrand 00169 // 00170 00172 // Construction/Destruction 00174 00175 inline CContour::CContour() 00176 { 00177 m_iColFir=m_iRowFir=32; 00178 m_iColSec=m_iRowSec=256; 00179 m_dDx=m_dDy=0; 00180 m_pFieldFcnData = NULL; 00181 m_pFieldFcn=NULL; 00182 m_pLimits[0]=m_pLimits[2]=0; 00183 m_pLimits[1]=m_pLimits[3]=5.; 00184 m_ppFnData=NULL; 00185 00186 // temporary stuff 00187 m_pFieldFcn=ContourTestFunction; 00188 m_vPlanes.resize(20); 00189 for (unsigned int i=0;i<m_vPlanes.size();i++) 00190 { 00191 m_vPlanes[i]=(i-m_vPlanes.size()/2.0)*0.1; 00192 } 00193 } 00194 00195 //G.Barrand : inline 00196 00197 inline void CContour::InitMemory() 00198 { 00199 if (!m_ppFnData) 00200 { 00201 m_ppFnData=new CFnStr*[m_iColSec+1]; 00202 for (int i=0;i<m_iColSec+1;i++) 00203 { 00204 m_ppFnData[i]=NULL; 00205 } 00206 } 00207 } 00208 00209 inline void CContour::CleanMemory() 00210 { 00211 if (m_ppFnData) 00212 { 00213 int i; 00214 for (i=0;i<m_iColSec+1;i++) 00215 { 00216 if (m_ppFnData[i]) 00217 delete[] (m_ppFnData[i]); 00218 } 00219 delete[] m_ppFnData; 00220 m_ppFnData=NULL; 00221 } 00222 } 00223 00224 inline void CContour::Generate() 00225 { 00226 00227 int i, j; 00228 int x3, x4, y3, y4, x, y, oldx3, xlow; 00229 const int cols=m_iColSec+1; 00230 const int rows=m_iRowSec+1; 00231 double xoff,yoff; 00232 00233 // Initialize memroy if needed 00234 InitMemory(); 00235 00236 m_dDx = (m_pLimits[1]-m_pLimits[0])/(double)(m_iColSec); 00237 xoff = m_pLimits[0]; 00238 m_dDy = (m_pLimits[3]-m_pLimits[2])/(double)(m_iRowSec); 00239 yoff = m_pLimits[2]; 00240 00241 xlow = 0; 00242 oldx3 = 0; 00243 x3 = (cols-1)/m_iRowFir; 00244 x4 = ( 2*(cols-1) )/m_iRowFir; 00245 for (x = oldx3; x <= x4; x++) 00246 { /* allocate new columns needed 00247 */ 00248 if (x >= cols) 00249 break; 00250 if (m_ppFnData[x]==NULL) 00251 m_ppFnData[x] = new CFnStr[rows]; 00252 00253 for (y = 0; y < rows; y++) 00254 FnctData(x,y)->m_sTopLen = -1; 00255 } 00256 00257 y4 = 0; 00258 for (j = 0; j < m_iColFir; j++) 00259 { 00260 y3 = y4; 00261 y4 = ((j+1)*(rows-1))/m_iColFir; 00262 Cntr1(oldx3, x3, y3, y4); 00263 } 00264 00265 for (i = 1; i < m_iRowFir; i++) 00266 { 00267 y4 = 0; 00268 for (j = 0; j < m_iColFir; j++) 00269 { 00270 y3 = y4; 00271 y4 = ((j+1)*(rows-1))/m_iColFir; 00272 Cntr1(x3, x4, y3, y4); 00273 } 00274 00275 y4 = 0; 00276 for (j = 0; j < m_iColFir; j++) 00277 { 00278 y3 = y4; 00279 y4 = ((j+1)*(rows-1))/m_iColFir; 00280 Pass2(oldx3,x3,y3,y4); 00281 } 00282 00283 if (i < (m_iRowFir-1)) 00284 { /* re-use columns no longer needed */ 00285 oldx3 = x3; 00286 x3 = x4; 00287 x4 = ((i+2)*(cols-1))/m_iRowFir; 00288 for (x = x3+1; x <= x4; x++) 00289 { 00290 if (xlow < oldx3) 00291 { 00292 if (m_ppFnData[x]) 00293 delete[] m_ppFnData[x]; 00294 m_ppFnData[x] = m_ppFnData[xlow]; 00295 m_ppFnData[ xlow++ ] = NULL; 00296 } 00297 else 00298 if (m_ppFnData[x]==NULL) 00299 m_ppFnData[x] = new CFnStr[rows]; 00300 00301 for (y = 0; y < rows; y++) 00302 FnctData(x,y)->m_sTopLen = -1; 00303 } 00304 } 00305 } 00306 00307 y4 = 0; 00308 for (j = 0; j < m_iColFir; j++) 00309 { 00310 y3 = y4; 00311 y4 = ((j+1)*(rows-1))/m_iColFir; 00312 Pass2(x3,x4,y3,y4); 00313 } 00314 } 00315 00316 inline void CContour::Cntr1(int x1, int x2, int y1, int y2) 00317 { 00318 double f11, f12, f21, f22, f33; 00319 int x3, y3, i, j; 00320 00321 if ((x1 == x2) || (y1 == y2)) /* if not a real cell, punt */ 00322 return; 00323 f11 = Field(x1, y1); 00324 f12 = Field(x1, y2); 00325 f21 = Field(x2, y1); 00326 f22 = Field(x2, y2); 00327 if ((x2 > x1+1) || (y2 > y1+1)) { /* is cell divisible? */ 00328 x3 = (x1+x2)/2; 00329 y3 = (y1+y2)/2; 00330 f33 = Field(x3, y3); 00331 i = j = 0; 00332 if (f33 < f11) i++; else if (f33 > f11) j++; 00333 if (f33 < f12) i++; else if (f33 > f12) j++; 00334 if (f33 < f21) i++; else if (f33 > f21) j++; 00335 if (f33 < f22) i++; else if (f33 > f22) j++; 00336 if ((i > 2) || (j > 2)) /* should we divide cell? */ 00337 { 00338 /* subdivide cell */ 00339 Cntr1(x1, x3, y1, y3); 00340 Cntr1(x3, x2, y1, y3); 00341 Cntr1(x1, x3, y3, y2); 00342 Cntr1(x3, x2, y3, y2); 00343 return; 00344 } 00345 } 00346 /* install cell in array */ 00347 FnctData(x1,y2)->m_sBotLen = FnctData(x1,y1)->m_sTopLen = x2-x1; 00348 FnctData(x2,y1)->m_sLeftLen = FnctData(x1,y1)->m_sRightLen = y2-y1; 00349 } 00350 00351 inline void CContour::Pass2(int x1, int x2, int y1, int y2) 00352 { 00353 int left = 0, right = 0, top = 0, bot = 0,old, iNew, i, j, x3, y3; 00354 double yy0 = 0, yy1 = 0, xx0 = 0, xx1 = 0, xx3, yy3; 00355 double v, f11, f12, f21, f22, f33, fold, fnew, f; 00356 double xoff=m_pLimits[0]; 00357 double yoff=m_pLimits[2]; 00358 00359 if ((x1 == x2) || (y1 == y2)) /* if not a real cell, punt */ 00360 return; 00361 f11 = FnctData(x1,y1)->m_dFnVal; 00362 f12 = FnctData(x1,y2)->m_dFnVal; 00363 f21 = FnctData(x2,y1)->m_dFnVal; 00364 f22 = FnctData(x2,y2)->m_dFnVal; 00365 if ((x2 > x1+1) || (y2 > y1+1)) /* is cell divisible? */ 00366 { 00367 x3 = (x1+x2)/2; 00368 y3 = (y1+y2)/2; 00369 f33 = FnctData(x3, y3)->m_dFnVal; 00370 i = j = 0; 00371 if (f33 < f11) i++; else if (f33 > f11) j++; 00372 if (f33 < f12) i++; else if (f33 > f12) j++; 00373 if (f33 < f21) i++; else if (f33 > f21) j++; 00374 if (f33 < f22) i++; else if (f33 > f22) j++; 00375 if ((i > 2) || (j > 2)) /* should we divide cell? */ 00376 { 00377 /* subdivide cell */ 00378 Pass2(x1, x3, y1, y3); 00379 Pass2(x3, x2, y1, y3); 00380 Pass2(x1, x3, y3, y2); 00381 Pass2(x3, x2, y3, y2); 00382 return; 00383 } 00384 } 00385 00386 for (i = 0; i < (int)m_vPlanes.size(); i++) 00387 { 00388 v = m_vPlanes[i]; 00389 j = 0; 00390 if (f21 > v) j++; 00391 if (f11 > v) j |= 2; 00392 if (f22 > v) j |= 4; 00393 if (f12 > v) j |= 010; 00394 if ((f11 > v) ^ (f12 > v)) 00395 { 00396 if ((FnctData(x1,y1)->m_sLeftLen != 0) && 00397 (FnctData(x1,y1)->m_sLeftLen < FnctData(x1,y1)->m_sRightLen)) 00398 { 00399 old = y1; 00400 fold = f11; 00401 while (1) 00402 { 00403 iNew = old+FnctData(x1,old)->m_sLeftLen; 00404 fnew = FnctData(x1,iNew)->m_dFnVal; 00405 if ((fnew > v) ^ (fold > v)) 00406 break; 00407 old = iNew; 00408 fold = fnew; 00409 } 00410 yy0 = ((old-y1)+(iNew-old)*(v-fold)/(fnew-fold))/(y2-y1); 00411 } 00412 else 00413 yy0 = (v-f11)/(f12-f11); 00414 00415 left = (int)(y1+(y2-y1)*yy0+0.5); 00416 } 00417 if ((f21 > v) ^ (f22 > v)) 00418 { 00419 if ((FnctData(x2,y1)->m_sRightLen != 0) && 00420 (FnctData(x2,y1)->m_sRightLen < FnctData(x2,y1)->m_sLeftLen)) 00421 { 00422 old = y1; 00423 fold = f21; 00424 while (1) 00425 { 00426 iNew = old+FnctData(x2,old)->m_sRightLen; 00427 fnew = FnctData(x2,iNew)->m_dFnVal; 00428 if ((fnew > v) ^ (fold > v)) 00429 break; 00430 old = iNew; 00431 fold = fnew; 00432 } 00433 yy1 = ((old-y1)+(iNew-old)*(v-fold)/(fnew-fold))/(y2-y1); 00434 } 00435 else 00436 yy1 = (v-f21)/(f22-f21); 00437 00438 right = (int)(y1+(y2-y1)*yy1+0.5); 00439 } 00440 if ((f21 > v) ^ (f11 > v)) 00441 { 00442 if ((FnctData(x1,y1)->m_sBotLen != 0) && 00443 (FnctData(x1,y1)->m_sBotLen < FnctData(x1,y1)->m_sTopLen)) { 00444 old = x1; 00445 fold = f11; 00446 while (1) { 00447 iNew = old+FnctData(old,y1)->m_sBotLen; 00448 fnew = FnctData(iNew,y1)->m_dFnVal; 00449 if ((fnew > v) ^ (fold > v)) 00450 break; 00451 old = iNew; 00452 fold = fnew; 00453 } 00454 xx0 = ((old-x1)+(iNew-old)*(v-fold)/(fnew-fold))/(x2-x1); 00455 } 00456 else 00457 xx0 = (v-f11)/(f21-f11); 00458 00459 bot = (int)(x1+(x2-x1)*xx0+0.5); 00460 } 00461 if ((f22 > v) ^ (f12 > v)) 00462 { 00463 if ((FnctData(x1,y2)->m_sTopLen != 0) && 00464 (FnctData(x1,y2)->m_sTopLen < FnctData(x1,y2)->m_sBotLen)) { 00465 old = x1; 00466 fold = f12; 00467 while (1) { 00468 iNew = old+FnctData(old,y2)->m_sTopLen; 00469 fnew = FnctData(iNew,y2)->m_dFnVal; 00470 if ((fnew > v) ^ (fold > v)) 00471 break; 00472 old = iNew; 00473 fold = fnew; 00474 } 00475 xx1 = ((old-x1)+(iNew-old)*(v-fold)/(fnew-fold))/(x2-x1); 00476 } 00477 else 00478 xx1 = (v-f12)/(f22-f12); 00479 00480 top = (int)(x1+(x2-x1)*xx1+0.5); 00481 } 00482 00483 switch (j) 00484 { 00485 case 7: 00486 case 010: 00487 ExportLine(i,x1,left,top,y2); 00488 break; 00489 case 5: 00490 case 012: 00491 ExportLine(i,bot,y1,top,y2); 00492 break; 00493 case 2: 00494 case 015: 00495 ExportLine(i,x1,left,bot,y1); 00496 break; 00497 case 4: 00498 case 013: 00499 ExportLine(i,top,y2,x2,right); 00500 break; 00501 case 3: 00502 case 014: 00503 ExportLine(i,x1,left,x2,right); 00504 break; 00505 case 1: 00506 case 016: 00507 ExportLine(i,bot,y1,x2,right); 00508 break; 00509 case 0: 00510 case 017: 00511 break; 00512 case 6: 00513 case 011: 00514 yy3 = (xx0*(yy1-yy0)+yy0)/(1.0-(xx1-xx0)*(yy1-yy0)); 00515 xx3 = yy3*(xx1-xx0)+xx0; 00516 xx3 = x1+xx3*(x2-x1); 00517 yy3 = y1+yy3*(y2-y1); 00518 xx3 = xoff+xx3*m_dDx; 00519 yy3 = yoff+yy3*m_dDy; 00520 f = (*m_pFieldFcn)(xx3, yy3,m_pFieldFcnData); 00521 if (f == v) { 00522 ExportLine(i,bot,y1,top,y2); 00523 ExportLine(i,x1,left,x2,right); 00524 } else 00525 if (((f > v) && (f22 > v)) || ((f < v) && (f22 < v))) { 00526 ExportLine(i,x1,left,top,y2); 00527 ExportLine(i,bot,y1,x2,right); 00528 } else { 00529 ExportLine(i,x1,left,bot,y1); 00530 ExportLine(i,top,y2,x2,right); 00531 } 00532 } 00533 } 00534 } 00535 00536 inline double CContour::Field(int x, int y) /* evaluate funct if we must,*/ 00537 { 00538 double x1, y1; 00539 00540 if (FnctData(x,y)->m_sTopLen != -1) /* is it already in the array */ 00541 return(FnctData(x,y)->m_dFnVal); 00542 00543 /* not in the array, create new array element */ 00544 x1 = m_pLimits[0]+m_dDx*x; 00545 y1 = m_pLimits[2]+m_dDy*y; 00546 FnctData(x,y)->m_sTopLen = 0; 00547 FnctData(x,y)->m_sBotLen = 0; 00548 FnctData(x,y)->m_sRightLen = 0; 00549 FnctData(x,y)->m_sLeftLen = 0; 00550 return (FnctData(x,y)->m_dFnVal = (*m_pFieldFcn)(x1, y1,m_pFieldFcnData)); 00551 } 00552 00553 inline void CContour::SetPlanes(const std::vector<double>& vPlanes) 00554 { 00555 // cleaning memory 00556 CleanMemory(); 00557 00558 m_vPlanes = vPlanes; 00559 } 00560 00561 inline void CContour::SetFieldFcn(double (*_pFieldFcn)(double, double,void*),void* aData) 00562 { 00563 m_pFieldFcnData = aData; 00564 m_pFieldFcn=_pFieldFcn; 00565 } 00566 00567 inline void CContour::SetFirstGrid(int iCol, int iRow) 00568 { 00569 m_iColFir=mx<int>(iCol,2); 00570 m_iRowFir=mx<int>(iRow,2); 00571 } 00572 00573 inline void CContour::SetSecondaryGrid(int iCol, int iRow) 00574 { 00575 // cleaning work matrices if allocated 00576 CleanMemory(); 00577 00578 m_iColSec=mx<int>(iCol,2); 00579 m_iRowSec=mx<int>(iRow,2); 00580 } 00581 00582 inline void CContour::SetLimits(double pLimits[]) 00583 { 00584 ASSERT(pLimits[0]<pLimits[1],"CContour::SetLimits"); 00585 ASSERT(pLimits[2]<pLimits[3],"CContour::SetLimits"); 00586 for (int i=0;i<4;i++) 00587 { 00588 m_pLimits[i]=pLimits[i]; 00589 } 00590 } 00591 00592 inline void CContour::GetLimits(double pLimits[]) 00593 { 00594 for (int i=0;i<4;i++) 00595 { 00596 pLimits[i]=m_pLimits[i]; 00597 } 00598 } 00599 00600 //G.Barrand : from .h to .cxx to avoid ASSERT in .h 00601 inline double CContour::GetPlane(unsigned int i) const { 00602 /*ASSERT(i>=0);*/ 00603 ASSERT(i<m_vPlanes.size(),"CContour::GetPlane"); 00604 return m_vPlanes[i]; 00605 } 00606 00607 inline double CContour::GetYi(int i) const { 00608 if(i<0) ::printf("CContour::GetYi : %d\n",i); 00609 ASSERT(i>=0,"CContour::GetYi"); 00610 return m_pLimits[2] + i/(m_iColSec+1)*(m_pLimits[3]-m_pLimits[2])/(double)( m_iRowSec ); 00611 } 00612 00613 } 00614 00615 #endif