00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006, 2007, 2008, Andrew W. Steiner 00005 00006 This file is part of O2scl. 00007 00008 O2scl is free software; you can redistribute it and/or modify 00009 it under the terms of the GNU General Public License as published by 00010 the Free Software Foundation; either version 3 of the License, or 00011 (at your option) any later version. 00012 00013 O2scl is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 GNU General Public License for more details. 00017 00018 You should have received a copy of the GNU General Public License 00019 along with O2scl. If not, see <http://www.gnu.org/licenses/>. 00020 00021 ------------------------------------------------------------------- 00022 */ 00023 #ifndef O2SCL_CONTOUR_H 00024 #define O2SCL_CONTOUR_H 00025 00026 #include <gsl/gsl_math.h> 00027 #include <o2scl/smart_interp.h> 00028 #include <o2scl/omatrix_tlate.h> 00029 #include <o2scl/pinside.h> 00030 00031 #ifndef DOXYGENP 00032 namespace o2scl { 00033 #endif 00034 00035 /** 00036 \brief Calculate contour lines from a two-dimensional data set 00037 00038 \b Basic \b Usage 00039 00040 - Specify the data as a two-dimensional square grid of "heights" 00041 with set_data(). 00042 - Specify the contour levels with set_levels(). 00043 - Compute the contours with calc_contours() which returns 00044 the number of contours (which is often larger than the number 00045 of contour levels, since one level can result in multiple contours). 00046 - Retrieve the contours individually using calls to 00047 get_contour(). 00048 00049 The data should be stored so that the y-index is first, i.e. 00050 data[iy][ix]. One can always switch \c x_fun and \c y_fun if 00051 this is not the case. The data is copied by set_data(), so 00052 changing the data will not change the contours unless set_data() 00053 is called again. The functions set_levels() and calc() can be 00054 called several times for the same data without calling 00055 set_data() again. 00056 00057 Linear interpolation is used to decide whether or not a line 00058 segment and a contour cross. This choice is intentional, since 00059 (in addition to making the algorithm much simpler) 00060 it is the user (and not the class) which is likely best able to 00061 refine the data. In case a simple refinement scheme is desired, 00062 the method regrid_data() is provided which uses cubic spline 00063 interpolation to refine the data and thus make the curves more 00064 continuous. 00065 00066 Since linear interpolation is used, the contour calculation 00067 implicitly assumes that there is not more than one intersection 00068 of any contour level with any line segment, so if this is the 00069 case, then either a more refined data set should be specified or 00070 regrid_data() should be used. For contours which do not close 00071 inside the region of interest, the results will always end at 00072 either the minimum or maximum values of \c x_fun or \c y_fun (no 00073 extrapolation is ever done). 00074 00075 As an example, for the function 00076 \f[ 00077 15 e^{-(x-20)^2/400-(y-5)^2/25}+ 00078 40 e^{-(x-70)^2/4900-(y-2)^2/4} 00079 \f] 00080 a 10x10 grid gives the contours: 00081 \image html contourg.png "contourg.png" 00082 \image latex contourg.eps "contourg.eps" width=9cm 00083 00084 While after a call to regrid_data(3,3), the contours 00085 are a little smoother: 00086 \image html contourg2.png "contourg2.png" 00087 \image latex contourg2.eps "contourg2.eps" width=9cm 00088 00089 Mathematica gives a similar result: 00090 \image html contourg3.png "contourg3.png" 00091 \image latex contourg3.eps "contourg3.eps" width=9cm 00092 00093 \b The \b Algorithm: 00094 00095 This works by viewing the data as defining a square 00096 two-dimensional grid. The function calc() exhaustively 00097 enumerates every line segment in the grid which involves a level 00098 crossing and then organizing the points defined by the 00099 intersection of a line segment with a level curve into a full 00100 contour. 00101 00102 \b Representing \b Contours \b by \b Fill \b Regions 00103 00104 If the user wants to "shade" the contours to provide a 00105 shaded or colored contour plot, then it is useful 00106 to provide a set of closed contours to be shaded instead 00107 of the (possibly) open contours returned by calc_contours(). 00108 After a call to calc_contours(), the function regions() can 00109 be used to organize the closed contours into regions to 00110 be shaded. Open contours are closed by adding points defining 00111 lines along the edges of the data and closed contours are 00112 inverted (if necessary) by adding an external line emanating 00113 from the closed contour and properly including the boundary 00114 edges. 00115 00116 \todo 00117 - Some contours which should be closed are not properly 00118 closed yet. See the tests for examples which fail. 00119 - Use twod_intp for regrid_data 00120 - Include the functionality to provide regions to be shaded 00121 instead of just lines. This can be done by providing a method, 00122 i.e. regions() which converts the curves into regions. 00123 - Rework documentation to refer to rows and columns, not x and y 00124 00125 \future 00126 - Work on how memory is allocated 00127 - Create a new separate struct for contour curves 00128 00129 */ 00130 class contour { 00131 00132 public: 00133 00134 contour(); 00135 00136 ~contour(); 00137 00138 /// \name Basic usage 00139 //@{ 00140 00141 /** 00142 \brief Set the data 00143 00144 The types \c vec_t and \c mat_t can be any types which have \c 00145 operator[] and \c operator[][] for array and matrix indexing. 00146 00147 Note that this method copies all of the user-specified data to 00148 local storage so that changes in the data after calling this function 00149 will not be reflected in the contours that are generated. 00150 */ 00151 template<class vec_t, class mat_t> 00152 int set_data(size_t sizex, size_t sizey, const vec_t &x_fun, 00153 const vec_t &y_fun, const mat_t &udata) { 00154 int i; 00155 00156 if (sizex<2 || sizey<2) { 00157 set_err_ret("Not enough data (must be at least 2x2) in set_data().", 00158 gsl_einval); 00159 } 00160 00161 free_memory(); 00162 00163 nx=sizex; 00164 ny=sizey; 00165 xfun=new ovector(nx); 00166 yfun=new ovector(ny); 00167 for(int is=0;is<nx;is++) (*xfun)[is]=x_fun[is]; 00168 for(int is=0;is<ny;is++) (*yfun)[is]=y_fun[is]; 00169 00170 data=new ovector *[ny]; 00171 for(i=0;i<ny;i++) { 00172 data[i]=new ovector(nx); 00173 for(int j=0;j<nx;j++) { 00174 (*(data[i]))[j]=udata[i][j]; 00175 } 00176 } 00177 00178 return 0; 00179 } 00180 00181 /** 00182 \brief Set the contour levels 00183 00184 This is separate from the function calc_contours() so that 00185 the user can compute the contours for different data sets using 00186 the same levels 00187 */ 00188 template<class vec_t> int set_levels(size_t nlevels, vec_t &ulevels) { 00189 nlev=nlevels; 00190 levels.allocate(nlevels); 00191 for(size_t i=0;i<nlevels;i++) { 00192 levels[i]=ulevels[i]; 00193 } 00194 levels_set=true; 00195 return 0; 00196 } 00197 00198 /** 00199 \brief Calculate the contours 00200 00201 The function calc_contours() returns the total number of 00202 contours found. Since there may be more than one disconnected 00203 contours for the same contour level, or no contours for a 00204 given level, the total number of contours may be less than or 00205 greater than the number of levels given by set_levels(). 00206 00207 If an error occurs, zero is returned. 00208 */ 00209 template<class vec_t> 00210 int calc_contours(vec_t &new_levels, bool debug=false) { 00211 int i,j,k,ileft,iright,m; 00212 00213 for(i=0;i<nlev;i++) new_levels[i]=levels[i]; 00214 00215 if (nx==0) { 00216 set_err("Data not set in calc_contours().",gsl_einval); 00217 return 0; 00218 } 00219 if (levels_set==false) { 00220 set_err("Contour levels not set in calc_contours().",gsl_einval); 00221 return 0; 00222 } 00223 00224 // Free previously stored contours 00225 if (ncurves!=0) { 00226 csizes.clear(); 00227 vals.clear(); 00228 xc.clear(); 00229 yc.clear(); 00230 ncurves=0; 00231 } 00232 00233 // Free space for previously computed edges 00234 for(j=0;j<((int)(re.size()));j++) { 00235 delete re[j]; 00236 delete be[j]; 00237 delete redges[j]; 00238 delete bedges[j]; 00239 } 00240 re.clear(); 00241 be.clear(); 00242 redges.clear(); 00243 bedges.clear(); 00244 00245 // Temporary storage for the interpolation arrays 00246 int nint; 00247 double *xi, *yi; 00248 00249 // Some useful variables for looping over the lines 00250 bool foundline, linedone; 00251 int fp; 00252 00253 // Temporary storage for the curves 00254 std::vector<double> x, y; 00255 00256 // The interpolation object 00257 00258 linear_interp<ovector_view> it1; 00259 linear_interp<ovector_const_reverse> it2; 00260 linear_interp<ovector_const_subvector> it3; 00261 linear_interp<ovector_const_subvector_reverse> it4; 00262 sm_interp *si=new sm_interp(it1,it2,it3,it4); 00263 00264 // Start with zero curves 00265 ncurves=0; 00266 00267 // For each level 00268 for(i=0;i<nlev;i++) { 00269 00270 // Make space for the edges and add to the list 00271 rep=new omatrix_int(ny-1,nx); 00272 bep=new omatrix_int(ny,nx-1); 00273 redgesp=new omatrix(ny-1,nx); 00274 bedgesp=new omatrix(ny,nx-1); 00275 re.push_back(rep); 00276 be.push_back(bep); 00277 redges.push_back(redgesp); 00278 bedges.push_back(bedgesp); 00279 00280 if (verbose>0) { 00281 std::cout << "\nLooking for edges for level: " 00282 << new_levels[i] << std::endl; 00283 } 00284 00285 // Examine the each of the rows for an intersection 00286 find_intersections(i,new_levels[i]); 00287 00288 if (verbose>0) { 00289 std::cout << "\nInterpolating edge intersections for level: " 00290 << new_levels[i] << std::endl; 00291 } 00292 00293 // Process the right edges 00294 right_edges(new_levels[i],si); 00295 00296 // Process the bottom edges 00297 bottom_edges(new_levels[i],si); 00298 00299 if (verbose>0) { 00300 std::cout << "\nPiecing together contour lines for level: " 00301 << new_levels[i] << std::endl; 00302 } 00303 00304 // Now go through and one side of the line 00305 foundline=true; 00306 while(foundline==true) { 00307 for(j=0;j<nx;j++) { 00308 for(k=0;k<ny;k++) { 00309 foundline=false; 00310 00311 // A line beginning with a right edge 00312 if (k<ny-1 && (*rep)[k][j]==edge) { 00313 if (verbose>0) { 00314 std::cout << "Starting contour line: " << std::endl; 00315 std::cout << "(" << (*xfun)[j] << ", " << (*redgesp)[k][j] 00316 << ")" << std::endl; 00317 } 00318 x.push_back((*xfun)[j]); 00319 y.push_back((*redgesp)[k][j]); 00320 (*rep)[k][j]++; 00321 00322 // Go through both sides 00323 process_line(j,k,dright,x,y,true); 00324 if (verbose>0) { 00325 std::cout << "Computing other side of line." << std::endl; 00326 } 00327 process_line(j,k,dright,x,y,false); 00328 foundline=true; 00329 } 00330 00331 // A line beginning with a bottom edge 00332 if (j<nx-1 && foundline==false && (*bep)[k][j]==edge) { 00333 if (verbose>0) { 00334 std::cout << "Starting contour line: " << std::endl; 00335 std::cout << "(" << (*bedgesp)[k][j] << ", " << (*yfun)[k] 00336 << ")" << std::endl; 00337 } 00338 x.push_back((*bedgesp)[k][j]); 00339 y.push_back((*yfun)[k]); 00340 (*bep)[k][j]++; 00341 00342 // Go through both sides 00343 process_line(j,k,dbottom,x,y,true); 00344 if (verbose>0) { 00345 std::cout << "Computing other side of line." << std::endl; 00346 } 00347 process_line(j,k,dbottom,x,y,false); 00348 foundline=true; 00349 } 00350 00351 // Now repackage the line information in x and y 00352 // to xc and yc and update csizes, vals and ncurves: 00353 if (foundline==true) { 00354 csizes.push_back(x.size()); 00355 vals.push_back(new_levels[i]); 00356 ncurves++; 00357 std::vector<double> xv, yv; 00358 for(m=0;m<((int)x.size());m++) { 00359 xv.push_back(x[m]); 00360 yv.push_back(y[m]); 00361 } 00362 xc.push_back(xv); 00363 yc.push_back(yv); 00364 x.clear(); 00365 y.clear(); 00366 } 00367 } 00368 } 00369 } 00370 } 00371 00372 return ncurves; 00373 } 00374 00375 /// Return the number of points in the specified contour 00376 int get_contour_size(int index); 00377 00378 /** 00379 \brief Get a contour 00380 00381 Given the \c index, which is between 0 and the number of contours 00382 returned by calc_contours() minus 1 (inclusive), this 00383 returns the level for this contour with the x and y-values 00384 in \c x and \c y which are both of length \c csize. 00385 The vectors \c x and \c y must have been previously allocated. 00386 The user can obtain the necessary size for \c x and \c y by 00387 calling get_contour_size(). 00388 */ 00389 template<class vec_t> int get_contour(int index, double &val, int &csize, 00390 vec_t &x, vec_t &y) { 00391 int i; 00392 if (index>=ncurves) { 00393 set_err_ret("No curve with specified index in get_contour().", 00394 gsl_efailed); 00395 } 00396 00397 csize=csizes[index]; 00398 val=vals[index]; 00399 for(i=0;i<csize;i++) { 00400 x[i]=xc[index][i]; 00401 y[i]=yc[index][i]; 00402 } 00403 return 0; 00404 } 00405 00406 /** 00407 \brief Compute closed contour regions (unfinished) 00408 */ 00409 int regions(bool larger); 00410 00411 //@} 00412 00413 /// \name Regrid function 00414 //@{ 00415 /** 00416 \brief Regrid the data 00417 00418 The uses cubic spline interpolation to refine the data set, 00419 ideally used before attempting to calculate the contour 00420 levels. If the original number of data points is 00421 \f$(\mathrm{nx},\mathrm{ny})\f$, then the new number of data 00422 points is 00423 \f[ 00424 (\mathrm{xfact}~(\mathrm{nx}-1)+1, 00425 \mathrm{yfact}~(\mathrm{ny}-1)+1) 00426 \f] 00427 */ 00428 int regrid_data(size_t xfact, size_t yfact); 00429 //@} 00430 00431 /// \name Obtain internal data 00432 //@{ 00433 /** 00434 \brief Get the data 00435 00436 This is useful to see how the data has changed after 00437 a call to regrid_data(). 00438 */ 00439 int get_data(size_t &sizex, size_t &sizey, const ovector *&x_fun, 00440 const ovector *&y_fun, const ovector **&udata) { 00441 if (nx==0) { 00442 set_err_ret("Data not set in calc().",gsl_einval); 00443 } 00444 sizex=nx; 00445 sizey=ny; 00446 x_fun=xfun; 00447 y_fun=yfun; 00448 udata=(const ovector **)data; 00449 return 0; 00450 } 00451 00452 /** \brief Return the edges used to compute the contours 00453 */ 00454 int get_edges(const std::vector<omatrix_int *> *rints, 00455 const std::vector<omatrix_int *> *bints, 00456 const std::vector<omatrix *> *rpoints, 00457 const std::vector<omatrix *> *bpoints); 00458 00459 /** 00460 \brief Return the edges used to compute the contours 00461 00462 This function allocates memory for \c rints, \c bints, 00463 \c rpoints, and \c bpoints and fills them with a copy of 00464 the data that the class is using. As such, there is no 00465 need for them to be const. 00466 */ 00467 int get_edges_for_level(size_t nl, omatrix_int &rints, omatrix_int &bints, 00468 omatrix &rpoints, omatrix &bpoints); 00469 00470 //@} 00471 00472 /// Verbosity parameter 00473 int verbose; 00474 00475 /// (default \f$ 10^{-8} \f$) 00476 double lev_adjust; 00477 00478 #ifndef DOXYGEN_INTERNAL 00479 00480 protected: 00481 00482 /// \name Edge direction 00483 //@{ 00484 static const int dright=0; 00485 static const int dbottom=1; 00486 //@} 00487 00488 /// \name Edge status 00489 //@{ 00490 static const int empty=0; 00491 static const int edge=1; 00492 static const int contourp=2; 00493 static const int endpoint=3; 00494 //@} 00495 00496 /// \name Edge found or not found 00497 //@{ 00498 static const int efound=1; 00499 static const int enot_found=0; 00500 //@} 00501 00502 /// \name User-specified data 00503 //@{ 00504 int nx, ny; 00505 ovector *xfun, *yfun, **data; 00506 //@} 00507 00508 int new_debug; 00509 00510 /// \name User-specified contour levels 00511 //@{ 00512 int nlev; 00513 ovector levels; 00514 bool levels_set; 00515 //@} 00516 00517 /// \name Generated curves 00518 //@{ 00519 int ncurves; 00520 std::vector<int> csizes; 00521 std::vector<double> vals; 00522 std::vector< std::vector<double> > xc, yc; 00523 //@} 00524 00525 /// \name Storage for edges 00526 //@{ 00527 std::vector<omatrix *> redges, bedges; 00528 std::vector<omatrix_int *> re, be; 00529 omatrix_int *rep; 00530 omatrix_int *bep; 00531 omatrix *redgesp; 00532 omatrix *bedgesp; 00533 //@} 00534 00535 /// Object to find if a point is inside a polygon 00536 pinside pi; 00537 00538 /// Find next point starting from a point on a right edge 00539 int find_next_point_right(int j, int k, int &jnext, int &knext, 00540 int &dir_next, int nsw=1); 00541 00542 /// Find next point starting from a point on a bottom edge 00543 int find_next_point_bottom(int j, int k, int &jnext, int &knext, 00544 int &dir_next, int nsw=1); 00545 00546 /// Find all of the intersections of the edges with the contour level 00547 int find_intersections(size_t ilev, double &level); 00548 00549 /// Interpolate all right edge crossings 00550 int right_edges(double level, o2scl::sm_interp *si); 00551 00552 /// Interpolate all bottom edge crossings 00553 int bottom_edges(double level, o2scl::sm_interp *si); 00554 00555 /// Create a contour line from a starting edge 00556 int process_line(int j, int k, int dir, std::vector<double> &x, 00557 std::vector<double> &y, bool first=true); 00558 00559 /** 00560 \brief Test if a point is inside a closed contour (unfinished) 00561 00562 This returns true if the point (x1,y1) is "inside" the contour 00563 (i.e. a collection of line segments) given in \c x and \c 00564 y. The arrays \c x and \c y must be "ordered" so that adjacent 00565 points are placed at adjacent entries. The result is 00566 undefined if this is not the case or if the contours are not 00567 properly closed. The first and last points in \c x and \c y 00568 should be the same to indicate a closed contour. The values 00569 xscale and yscale should be an approximate scale for the 00570 contours \c x and \c y. 00571 00572 \note This function is deprecated and has been replaced by 00573 \ref pinside 00574 */ 00575 bool is_point_inside_old(double x1, double y1, const ovector_view &x, 00576 const ovector_view &y, double xscale=0.01, 00577 double yscale=0.01); 00578 00579 /// Free memory 00580 int free_memory(); 00581 00582 /** 00583 \brief Check if lines cross 00584 00585 Returns true if the line segment defined by (x1,y1) and (x2,y2) 00586 and the line segment defined by (x3,y3) and (x4,y4) have 00587 an intersection point that is between the endpoints of both 00588 segments. The function handles vertical and horizontal lines 00589 appropriately. This function will fail if x1==y1 and x2==y2 00590 or if x3==y3 and x4==y4, i.e. if the points do not really 00591 define a line. If the function fails, it returns false. 00592 00593 \note This function is deprecated and has been replaced by 00594 \ref pinside 00595 */ 00596 bool lines_cross_old(double x1, double y1, double x2, double y2, 00597 double x3, double y3, double x4, double y4); 00598 00599 /// Check to ensure the x- and y-arrays are monotonic 00600 int check_data(); 00601 00602 /** \brief Smooth the contours by adding internal points using cubic 00603 interpolation (this doesn't work) 00604 00605 This makes the contours smoother by adding internal points 00606 between each contour line segment determined by cubic spline 00607 interpolation. 00608 00609 For more accurate contours, it is better to provide the 00610 original data on a finer grid, or use regrid_data(). 00611 */ 00612 int smooth_contours(size_t nfact); 00613 00614 #endif 00615 00616 }; 00617 00618 #ifndef DOXYGENP 00619 } 00620 #endif 00621 00622 #endif 00623 00624
Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.
Project hosting provided by
,
O2scl Sourceforge Project Page