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