![]() |
Object-oriented Scientific Computing Library: Version 0.910
|
00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006-2012, 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 <cmath> 00027 #include <gsl/gsl_math.h> 00028 #include <o2scl/smart_interp.h> 00029 #include <o2scl/omatrix_tlate.h> 00030 00031 #ifndef DOXYGENP 00032 namespace o2scl { 00033 #endif 00034 00035 /** \brief A contour line 00036 00037 The contour lines generated by the \ref contour class are given 00038 as objects of this type. 00039 00040 \future Write HDF I/O for contour lines? 00041 \future Make this a subclass of \ref contour . 00042 */ 00043 class contour_line { 00044 public: 00045 /// The contour level 00046 double level; 00047 /// The line x coordinates 00048 ovector x; 00049 /// The line y coordinates 00050 ovector y; 00051 00052 /// Create an empty line 00053 contour_line() { 00054 } 00055 00056 /// Copy constructor for STL allocator 00057 contour_line(const contour_line &c) { 00058 level=c.level; 00059 x=c.x; 00060 y=c.y; 00061 } 00062 }; 00063 00064 /** \brief Edges for the contour class 00065 00066 The edge crossings generated by the \ref contour class are given 00067 as objects of this type. 00068 00069 The \ref status matrix contains one of four possible values 00070 - 0 - empty (no edge) 00071 - 1 - edge which has not yet been assigned to a contour 00072 - 2 - edge assigned to contour point 00073 - 3 - edge which has been designated as a contour endpoint 00074 00075 The matrices returned by \ref contour are not square, their 00076 size depends on whether or not they contain the "bottom edges" 00077 or the "right edges". 00078 00079 \future Write HDF I/O object for edge crossings 00080 \future Make this a subclass of \ref contour . 00081 */ 00082 class edge_crossings { 00083 00084 public: 00085 00086 /// Edge status 00087 omatrix_int status; 00088 /// Edge values 00089 omatrix values; 00090 }; 00091 00092 /** \brief Calculate contour lines from a two-dimensional data set 00093 00094 \b Basic \b Usage 00095 00096 - Specify the data as a two-dimensional square grid of "heights" 00097 with set_data(). 00098 - Specify the contour levels with set_levels(). 00099 - Compute the contours with calc_contours() 00100 00101 The contours are generated as a series of x- and y-coordinates, 00102 defining a line. If the contour is closed, then the first and 00103 the last set of coordinates will be equal. 00104 00105 The storage of the matrix to be specified in the function 00106 set_data() and this function is designed to follow the format: 00107 \f[ 00108 \begin{array}{cccc} 00109 & x_0 & x_1 & x_2 \\ 00110 y_0 & M_{00} & M_{01} & M_{02} \\ 00111 y_1 & M_{10} & M_{11} & M_{12} \\ 00112 y_2 & M_{20} & M_{21} & M_{22} 00113 \end{array} 00114 \f] 00115 thus the matrix should be \c M[i][j] where \c i is the y index 00116 and \c j is the row index. (See also the discussion in the 00117 User's guide in the section called \ref rowcol_subsect.) 00118 00119 The data is copied by set_data(), so changing the data will not 00120 change the contours unless set_data() is called again. The 00121 functions set_levels() and calc() can be called several times 00122 for the same data without calling set_data() again. 00123 00124 Note that in order to simplify the algorithm for computing 00125 contour lines, the calc_contours() function will adjust the 00126 user-specified contour levels slightly in order to ensure that 00127 no contour line passes exactly through any data point on the 00128 grid. The contours are adjusted by multiplying the original 00129 contour level by 1 plus a small number (\f$ 10^{-8} \f$ by 00130 default), which is specified in \ref lev_adjust. 00131 00132 Linear interpolation is used to decide whether or not a line 00133 segment and a contour cross. This choice is intentional, since 00134 (in addition to making the algorithm much simpler) it is the 00135 user (and not the class) which is likely best able to refine the 00136 data. In case a simple refinement scheme is desired, the method 00137 regrid_data() is provided which refines the data for any 00138 interpolation type. 00139 00140 Since linear interpolation is used, the contour calculation 00141 implicitly assumes that there is not more than one intersection 00142 of any contour level with any line segment. For contours which 00143 do not close inside the region of interest, the results will 00144 always end at either the minimum or maximum values of the x or y 00145 grid points (no extrapolation is ever done). Note also that the 00146 points defining the contour are not necessarily equally spaced, 00147 but two neighboring points will never be farther apart than the 00148 distance across opposite corners of one cell in the grid. 00149 00150 \b The \b Algorithm: 00151 00152 This works by viewing the data as defining a square 00153 two-dimensional grid. The function calc_contours() exhaustively 00154 enumerates every line segment in the grid which involves a level 00155 crossing and then organizes the points defined by the 00156 intersection of a line segment with a level curve into a full 00157 contour. 00158 00159 \future Rewrite the code which adjusts the contour levels 00160 to ensure contours don't go through the data to adjust the 00161 internal copy of the data instead. 00162 00163 \future It would be nice to have a function which creates a set 00164 of closed regions to fill which represent the data. However, 00165 this likely requires a completely new algorithm, because it's 00166 not easy to simply close the contours already generated by the 00167 calc_contours() function. There are, for example, several cases 00168 which are difficult to handle, such as filling a region in 00169 between several closed contours. 00170 00171 */ 00172 class contour { 00173 00174 public: 00175 00176 contour(); 00177 00178 ~contour(); 00179 00180 /// \name Basic usage 00181 //@{ 00182 00183 /** \brief Set the data 00184 00185 The types \c vec_t and \c mat_t can be any types which have \c 00186 operator[] and \c operator[][] for array and matrix indexing. 00187 00188 Note that this method copies all of the user-specified data to 00189 local storage so that changes in the data after calling this 00190 function will not be reflected in the contours that are 00191 generated. 00192 */ 00193 template<class vec_t, class mat_t> 00194 int set_data(size_t sizex, size_t sizey, const vec_t &x_fun, 00195 const vec_t &y_fun, const mat_t &udata) { 00196 00197 if (sizex<2 || sizey<2) { 00198 O2SCL_ERR_RET("Not enough data (must be at least 2x2) in set_data().", 00199 gsl_einval); 00200 } 00201 00202 nx=sizex; 00203 ny=sizey; 00204 xfun.allocate(nx); 00205 yfun.allocate(ny); 00206 data.allocate(ny,nx); 00207 for(int i=0;i<nx;i++) xfun[i]=x_fun[i]; 00208 for(int i=0;i<ny;i++) yfun[i]=y_fun[i]; 00209 for(int i=0;i<nx;i++) { 00210 for(int j=0;j<ny;j++) { 00211 data[j][i]=udata[j][i]; 00212 } 00213 } 00214 00215 return check_data(); 00216 } 00217 00218 /** \brief Set the contour levels 00219 00220 This is separate from the function calc_contours() so that 00221 the user can compute the contours for different data sets using 00222 the same levels. 00223 */ 00224 template<class vec_t> int set_levels(size_t nlevels, vec_t &ulevels) { 00225 nlev=nlevels; 00226 levels.allocate(nlevels); 00227 for(size_t i=0;i<nlevels;i++) { 00228 levels[i]=ulevels[i]; 00229 } 00230 levels_set=true; 00231 return 0; 00232 } 00233 00234 /** \brief Calculate the contours 00235 00236 The function calc_contours() returns the total number of 00237 contours found. Since there may be more than one disconnected 00238 contours for the same contour level, or no contours for a 00239 given level, the total number of contours may be less than or 00240 greater than the number of levels given by set_levels(). 00241 00242 If an error occurs, zero is returned. 00243 */ 00244 int calc_contours(std::vector<contour_line> &clines, 00245 bool debug=false); 00246 00247 //@} 00248 00249 /// \name Regrid function 00250 //@{ 00251 /** \brief Regrid the data 00252 00253 Use interpolation to refine the data set. This can be called 00254 before calc_contours() in order to make the contour levels 00255 smoother by providing a smaller grid size. If the original 00256 number of data points is \f$ (\mathrm{nx},\mathrm{ny}) \f$, 00257 then the new number of data points is 00258 \f[ 00259 (\mathrm{xfact}~(\mathrm{nx}-1)+1, 00260 \mathrm{yfact}~(\mathrm{ny}-1)+1) 00261 \f] 00262 The parameters \c xfact and \c yfact must both be larger than 00263 zero and they cannot both be 1. 00264 */ 00265 int regrid_data(size_t xfact, size_t yfact, 00266 base_interp_mgr<ovector_const_view> &bim1, 00267 base_interp_mgr<ovector_const_subvector> &bim2); 00268 //@} 00269 00270 /// \name Obtain internal data 00271 //@{ 00272 /** \brief Get the data 00273 00274 This is useful to see how the data has changed after 00275 a call to regrid_data(). 00276 00277 \future There is probably a better way than returning 00278 pointers to the internal data. 00279 */ 00280 int get_data(size_t &sizex, size_t &sizey, ovector *&x_fun, 00281 ovector *&y_fun, omatrix *&udata) { 00282 if (nx==0) { 00283 O2SCL_ERR_RET("Data not set in calc().",gsl_einval); 00284 } 00285 sizex=nx; 00286 sizey=ny; 00287 x_fun=&xfun; 00288 y_fun=&yfun; 00289 udata=&data; 00290 return 0; 00291 } 00292 00293 /// Return the edges 00294 int get_edges(std::vector<edge_crossings> &rt_edges, 00295 std::vector<edge_crossings> &bm_edges) { 00296 rt_edges=red; 00297 bm_edges=bed; 00298 return 0; 00299 } 00300 00301 /// Print out the edges to cout 00302 int print_edges(edge_crossings &right, 00303 edge_crossings &bottom); 00304 00305 //@} 00306 00307 /// Verbosity parameter 00308 int verbose; 00309 00310 /// (default \f$ 10^{-8} \f$) 00311 double lev_adjust; 00312 00313 #ifndef DOXYGEN_INTERNAL 00314 00315 protected: 00316 00317 /// \name Edge direction 00318 //@{ 00319 static const int dright=0; 00320 static const int dbottom=1; 00321 //@} 00322 00323 /// \name Edge status 00324 //@{ 00325 static const int empty=0; 00326 static const int edge=1; 00327 static const int contourp=2; 00328 static const int endpoint=3; 00329 //@} 00330 00331 /// \name Edge found or not found 00332 //@{ 00333 static const int efound=1; 00334 static const int enot_found=0; 00335 //@} 00336 00337 /// \name User-specified data 00338 //@{ 00339 int nx, ny; 00340 ovector xfun, yfun; 00341 omatrix data; 00342 //@} 00343 00344 /// \name User-specified contour levels 00345 //@{ 00346 int nlev; 00347 ovector levels; 00348 bool levels_set; 00349 //@} 00350 00351 /// Right edge list 00352 std::vector<edge_crossings> red; 00353 /// Bottom edge list 00354 std::vector<edge_crossings> bed; 00355 00356 /// Find next point starting from a point on a right edge 00357 int find_next_point_right(int j, int k, int &jnext, int &knext, 00358 int &dir_next, int nsw, 00359 edge_crossings &right, 00360 edge_crossings &bottom); 00361 00362 /// Find next point starting from a point on a bottom edge 00363 int find_next_point_bottom(int j, int k, int &jnext, int &knext, 00364 int &dir_next, int nsw, 00365 edge_crossings &right, 00366 edge_crossings &bottom); 00367 00368 /// Find all of the intersections of the edges with the contour level 00369 int find_intersections(size_t ilev, double &level, 00370 edge_crossings &right, edge_crossings &bottom); 00371 00372 /// Interpolate all right edge crossings 00373 int right_edges(double level, o2scl::sm_interp *si, 00374 edge_crossings &right); 00375 00376 /// Interpolate all bottom edge crossings 00377 int bottom_edges(double level, o2scl::sm_interp *si, 00378 edge_crossings &bottom); 00379 00380 /// Create a contour line from a starting edge 00381 int process_line(int j, int k, int dir, ovector &x, ovector &y, 00382 bool first, edge_crossings &right, 00383 edge_crossings &bottom); 00384 00385 /// Check to ensure the x- and y-arrays are monotonic 00386 int check_data(); 00387 00388 #endif 00389 00390 }; 00391 00392 #ifndef DOXYGENP 00393 } 00394 #endif 00395 00396 #endif 00397 00398
Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).