From 7a2d001231356d6903d068e28fb914a9af0071c0 Mon Sep 17 00:00:00 2001 From: David Mayerich Date: Tue, 5 Aug 2014 10:25:00 -0500 Subject: [PATCH] mirst1d updates --- math/complexfield.cuh | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- math/field.cuh | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++-- math/realfield.cuh | 4 ++-- optics/material.h | 6 +++--- optics/mirst-1d.cuh | 420 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/arguments.h | 416 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- tools/progressbar.h | 33 --------------------------------- ui/arguments.h | 416 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ui/progressbar.h | 33 +++++++++++++++++++++++++++++++++ 9 files changed, 973 insertions(+), 458 deletions(-) create mode 100644 optics/mirst-1d.cuh delete mode 100644 tools/arguments.h delete mode 100644 tools/progressbar.h create mode 100644 ui/arguments.h create mode 100644 ui/progressbar.h diff --git a/math/complexfield.cuh b/math/complexfield.cuh index 25c1b3e..8a52bed 100644 --- a/math/complexfield.cuh +++ b/math/complexfield.cuh @@ -6,16 +6,36 @@ #include "../math/field.cuh" #include "../math/complex.h" +#include "../math/realfield.cuh" namespace rts{ +template +__global__ void gpu_complexfield_mag(T* dest, complex* source, unsigned int r0, unsigned int r1){ + + int iu = blockIdx.x * blockDim.x + threadIdx.x; + int iv = blockIdx.y * blockDim.y + threadIdx.y; + + //make sure that the thread indices are in-bounds + if(iu >= r0 || iv >= r1) return; + + //compute the index into the field + int i = iv*r0 + iu; + + //calculate and store the result + dest[i] = source[i].abs(); +} + /*This class stores functions for saving images of complex fields */ -template +template class complexfield : public field< rts::complex, D >{ using field< rts::complex, D >::R; using field< rts::complex, D >::X; using field< rts::complex, D >::shape; + using field< rts::complex, D >::cuda_params; + + public: @@ -56,12 +76,20 @@ public: public: + enum attribute {magnitude, real, imaginary}; + //constructor (no parameters) complexfield() : field, D>(){}; //constructor (resolution specified) complexfield(unsigned int r0, unsigned int r1) : field, D>(r0, r1){}; + //assignment from a field of complex values + complexfield & operator=(const field< rts::complex, D > rhs){ + field< complex, D >::operator=(rhs); + return *this; + } + //assignment operator (scalar value) complexfield & operator= (const complex rhs){ @@ -76,7 +104,26 @@ public: return *this; } - void toImage(std::string filename, unsigned int n){ + //cropping + complexfield crop(unsigned int width, unsigned int height){ + + complexfield result; + result = field< complex, D>::crop(width, height); + return result; + } + + void toImage(std::string filename, attribute type = magnitude, unsigned int n=0){ + + realfield rf(R[0], R[1]); + + //get cuda parameters + dim3 blocks, grids; + cuda_params(grids, blocks); + + if(type == magnitude){ + gpu_complexfield_mag <<>> (rf.ptr(), X[n], R[0], R[1]); + rf.toImages(filename, 0); + } } diff --git a/math/field.cuh b/math/field.cuh index ab60701..98186b3 100644 --- a/math/field.cuh +++ b/math/field.cuh @@ -47,7 +47,27 @@ __global__ void gpu_field_assign(T* ptr, T val, unsigned int r0, unsigned int r1 ptr[i] = val; } -template +//crop the field to the new dimensions (width x height) +template +__global__ void gpu_field_crop(T* dest, T* source, + unsigned int r0, unsigned int r1, + unsigned int width, unsigned int height){ + + int iu = blockIdx.x * blockDim.x + threadIdx.x; + int iv = blockIdx.y * blockDim.y + threadIdx.y; + + //make sure that the thread indices are in-bounds + if(iu >= width || iv >= height) return; + + //compute the index into the field + int is = iv*r0 + iu; + int id = iv*width + iu; + + //calculate and store the result + dest[id] = source[is]; +} + +template class field{ protected: @@ -56,6 +76,16 @@ protected: unsigned int R[2]; //field resolution rts::rect shape; //position and shape of the field slice + //calculates the optimal block and grid sizes using information from the GPU + void cuda_params(dim3& grids, dim3& blocks){ + int maxThreads = rts::maxThreadsPerBlock(); //compute the optimal block size + int SQRT_BLOCK = (int)std::sqrt((float)maxThreads); + + //create one thread for each detector pixel + blocks = dim3(SQRT_BLOCK, SQRT_BLOCK); + grids = dim3((R[0] + SQRT_BLOCK -1)/SQRT_BLOCK, (R[1] + SQRT_BLOCK - 1)/SQRT_BLOCK); + } + public: //returns a list of file names given an input string with wild cards @@ -101,7 +131,6 @@ public: HANDLE_ERROR(cudaFree(X[n])); } - public: //field constructor field(){ @@ -241,6 +270,25 @@ public: HANDLE_ERROR(cudaMemset(X[n], 0, sizeof(T) * R[0] * R[1])); } + //crop the field + field crop(unsigned int width, unsigned int height){ + int maxThreads = rts::maxThreadsPerBlock(); //compute the optimal block size + int SQRT_BLOCK = (int)std::sqrt((float)maxThreads); + + //create one thread for each detector pixel + dim3 dimBlock(SQRT_BLOCK, SQRT_BLOCK); + dim3 dimGrid((width + SQRT_BLOCK -1)/SQRT_BLOCK, (height + SQRT_BLOCK - 1)/SQRT_BLOCK); + + //create a scalar field to store the result + field result(width, height); + + for(int n=0; n>> (result.X[n], X[n], R[0], R[1], width, height); + + return result; + + } + }; } //end namespace rts diff --git a/math/realfield.cuh b/math/realfield.cuh index a0b0a18..6a2f2af 100644 --- a/math/realfield.cuh +++ b/math/realfield.cuh @@ -37,7 +37,7 @@ namespace rts{ //multiply R = X * Y template -__global__ void gpu_field_multiply(T* R, T* X, T* Y, unsigned int r0, unsigned int r1){ +__global__ void gpu_realfield_multiply(T* R, T* X, T* Y, unsigned int r0, unsigned int r1){ int iu = blockIdx.x * blockDim.x + threadIdx.x; int iv = blockIdx.y * blockDim.y + threadIdx.y; @@ -244,7 +244,7 @@ public: realfield result(R[0], R[1]); for(int n=0; n>> (result.X[n], X[n], rhs.X[n], R[0], R[1]); + rts::gpu_realfield_multiply <<>> (result.X[n], X[n], rhs.X[n], R[0], R[1]); return result; } diff --git a/optics/material.h b/optics/material.h index 9921702..d94ed10 100644 --- a/optics/material.h +++ b/optics/material.h @@ -403,9 +403,9 @@ namespace rts{ #endif } - material(T lambda = 1.0, T n = 1.4, T k = 0.0) + material(T lambda = 1, T n = 1, T k = 0) { - dispersion.insert(lambda, complex(0.0, k)); + dispersion.insert(lambda, complex(n, k)); /*//create a default refractive index refIndex def; def.lambda = lambda; @@ -414,7 +414,7 @@ namespace rts{ add(def); */ //set n0 - n0 = n; + n0 = 0; } material(std::string filename, std::string format = "", T scaleA = 1.0) diff --git a/optics/mirst-1d.cuh b/optics/mirst-1d.cuh new file mode 100644 index 0000000..ae27af2 --- /dev/null +++ b/optics/mirst-1d.cuh @@ -0,0 +1,420 @@ +#include "../optics/material.h" +#include "../math/complexfield.cuh" + +#include "cufft.h" + +#include +#include + +namespace rts{ + +//this function writes a sinc function to "dest" such that an iFFT produces a slab +template +__global__ void gpu_mirst1d_layer_fft(complex* dest, complex* ri, + T* src, T* zf, + T w, unsigned int zR, unsigned int nuR){ + //dest = complex field representing the sample + //ri = refractive indices for each wavelength + //src = intensity of the light source for each wavelength + //zf = z position of the slab interface for each wavelength (accounting for optical path length) + //w = width of the slab (in pixels) + //zR = number of z-axis samples + //nuR = number of wavelengths + + //get the current coordinate in the plane slice + int ifz = blockIdx.x * blockDim.x + threadIdx.x; + int inu = blockIdx.y * blockDim.y + threadIdx.y; + + //make sure that the thread indices are in-bounds + if(inu >= nuR || ifz >= zR) return; + + int i = inu * zR + ifz; + + T fz; + if(ifz < zR/2) + fz = ifz / (T)zR; + else + fz = -(zR - ifz) / (T)zR; + + //if the slab starts outside of the simulation domain, just return + if(zf[inu] >= zR) return; + + //fill the array along z with a sinc function representing the Fourier transform of the layer + + T opl = w * ri[inu].real(); //optical path length + + //handle the case where the slab goes outside the simulation domain + if(zf[inu] + opl >= zR) + opl = zR - zf[inu]; + + if(opl == 0) return; + + //T l = w * ri[inu].real(); + //complex e(0.0, -2 * PI * fz * (zf[inu] + zR/2 - l/2.0)); + complex e(0, -2 * PI * fz * (zf[inu] + opl/2)); + + complex eta = ri[inu] * ri[inu] - 1; + + //dest[i] = fz;//exp(e) * m[inu] * src[inu] * sin(PI * fz * l) / (PI * fz); + if(ifz == 0) + dest[i] += opl * exp(e) * eta * src[inu]; + else + dest[i] += opl * exp(e) * eta * src[inu] * sin(PI * fz * opl) / (PI * fz * opl); +} + +template +__global__ void gpu_mirst1d_increment_z(T* zf, complex* ri, T w, unsigned int S){ + //zf = current z depth (optical path length) in pixels + //ri = refractive index of the material + //w = actual width of the layer (in pixels) + + + //compute the index for this thread + int i = blockIdx.x * blockDim.x + threadIdx.x; + if(i >= S) return; + + if(ri == NULL) + zf[i] += w; + else + zf[i] += ri[i].real() * w; +} + +//apply the 1D MIRST filter to an existing sample (overwriting the sample) +template +__global__ void gpu_mirst1d_apply_filter(complex* sampleFFT, T* lambda, + T dFz, + T inNA, T outNA, + unsigned int lambdaR, unsigned int zR, + T sigma = 0){ + //sampleFFT = the sample in the Fourier domain (will be overwritten) + //lambda = list of wavelengths + //dFz = delta along the Fz axis in the frequency domain + //inNA = NA of the internal obscuration + //outNA = NA of the objective + //zR = number of pixels along the Fz axis (same as the z-axis) + //lambdaR = number of wavelengths + //sigma = width of the Gaussian source + int ifz = blockIdx.x * blockDim.x + threadIdx.x; + int inu = blockIdx.y * blockDim.y + threadIdx.y; + + if(inu >= lambdaR || ifz >= zR) return; + + //calculate the index into the sample FT + int i = inu * zR + ifz; + + //compute the frequency (and set all negative spatial frequencies to zero) + T fz; + if(ifz < zR / 2) + fz = ifz * dFz; + //if the spatial frequency is negative, set it to zero and exit + else{ + sampleFFT[i] = 0; + return; + } + + //compute the frequency in inverse microns + T nu = 1/lambda[inu]; + + //determine the radius of the integration circle + T nu_sq = nu * nu; + T fz_sq = (fz * fz) / 4; + + //cut off frequencies above the diffraction limit + T r; + if(fz_sq < nu_sq) + r = sqrt(nu_sq - fz_sq); + else + r = 0; + + //account for the optics + T Q = 0; + if(r > nu * inNA && r < nu * outNA) + Q = 1; + + //account for the source + //T sigma = 30.0; + T s = exp( - (r*r * sigma*sigma) / 2 ); + //T s=1; + + //compute the final filter + T mirst = 0; + if(fz != 0) + mirst = 2 * PI * r * s * Q * (1/fz); + + sampleFFT[i] *= mirst; + +} + +/*This object performs a 1-dimensional (layered) MIRST simulation +*/ +template +class mirst1d{ + +private: + unsigned int Z; //z-axis resolution + unsigned int pad; //pixel padding on either side of the sample + + std::vector< material > matlist; //list of materials + std::vector< T > layers; //list of layer thicknesses + + std::vector< T > lambdas; //list of wavelengths that are being simulated + unsigned int S; //number of wavelengths (size of "lambdas") + + T NA[2]; //numerical aperature (central obscuration and outer diameter) + + complexfield scratch; //scratch GPU memory used to build samples, transforms, etc. + + void fft(int direction = CUFFT_FORWARD){ + + unsigned padZ = Z + pad; + + //create cuFFT handles + cufftHandle plan; + cufftResult result; + + if(sizeof(T) == 4) + result = cufftPlan1d(&plan, padZ, CUFFT_C2C, lambdas.size()); //single precision + else + result = cufftPlan1d(&plan, padZ, CUFFT_Z2Z, lambdas.size()); //double precision + + //check for Plan 1D errors + if(result != CUFFT_SUCCESS){ + std::cout<<"Error creating CUFFT plan for computing the FFT:"<(Z + pad , lambdas.size()); + scratch = 0; + } + + //get the list of scattering efficiency (eta) values for a specified layer + std::vector< complex > layer_etas(unsigned int l){ + + std::vector< complex > etas; + + //fill the list of etas + for(unsigned int i=0; i* gpuRi; + HANDLE_ERROR(cudaMalloc( (void**)&gpuRi, sizeof(complex) * S)); + + //allocate memory for the source profile + T* gpuSrc; + HANDLE_ERROR(cudaMalloc( (void**)&gpuSrc, sizeof(T) * S)); + + complex ri; + T source; + //store the refractive index and source profile in a CPU array + for(int inu=0; inu), cudaMemcpyHostToDevice )); + + //save the source profile to the GPU + source = 1; + HANDLE_ERROR(cudaMemcpy( gpuSrc + inu, &source, sizeof(T), cudaMemcpyHostToDevice )); + + } + + //create one thread for each pixel of the field slice + dim3 gridDim, blockDim; + cuda_params(gridDim, blockDim); + rts::gpu_mirst1d_layer_fft<<>>(scratch.ptr(), gpuRi, gpuSrc, zf, wpx, paddedZ, S); + + int linBlock = rts::maxThreadsPerBlock(); //compute the optimal block size + int linGrid = S / linBlock + 1; + rts::gpu_mirst1d_increment_z <<>>(zf, gpuRi, wpx, S); + + //free memory + HANDLE_ERROR(cudaFree(gpuRi)); + HANDLE_ERROR(cudaFree(gpuSrc)); + } + + void build_sample(){ + init_scratch(); //initialize the GPU scratch space + //build_layer(1); + + T* zf; + HANDLE_ERROR(cudaMalloc(&zf, sizeof(T) * S)); + HANDLE_ERROR(cudaMemset(zf, 0, sizeof(T) * S)); + + //render each layer of the sample + for(unsigned int l=0; l>>(scratch.ptr(), gpuLambdas, + dFz, + NA[0], NA[1], + S, Zpad); + } + + //crop the image to the sample thickness - keep in mind that sample thickness != optical path length + void crop(){ + + scratch = scratch.crop(Z, S); + } + + +public: + + //constructor + mirst1d(unsigned int rZ = 100, + unsigned int padding = 0){ + Z = rZ; + pad = padding; + NA[0] = 0; + NA[1] = 0.8; + } + + //add a layer, thickness = microns + void add_layer(material mat, T thickness){ + matlist.push_back(mat); + layers.push_back(thickness); + } + + void add_layer(std::string filename, T thickness){ + add_layer(material(filename), thickness); + } + + //adds a block of wavenumbers (cm^-1) to the simulation parameters + void add_wavenumbers(unsigned int start, unsigned int stop, unsigned int step){ + unsigned int nu = start; + while(nu <= stop){ + lambdas.push_back((T)10000 / nu); + nu += step; + } + S = lambdas.size(); //increment the number of wavelengths (shorthand for later) + } + + T thickness(){ + T t = 0; + for(unsigned int l=0; l::magnitude); + } + + void save_mirst(std::string filename){ + //apply the MIRST filter to a sample and save the image + + //build the sample in the Fourier domain + build_sample(); + + //apply the MIRST filter + apply_filter(); + + //apply an inverse FFT to bring the results back into the spatial domain + fft(CUFFT_INVERSE); + + crop(); + + //save the image + scratch.toImage(filename, rts::complexfield::magnitude); + } + + + + + std::string str(){ + + stringstream ss; + + ss<<"z-axis resolution: "< -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 -#include -#endif - -namespace rts{ - - class argument - { - private: - bool ansi; - - //argument name - std::string name; - - //description of the argument - std::vector desc; - - //argument values - std::vector vals; - - //range or example - std::string range; - - //flag is true when the argument is user-specified - bool flag; - - void parse_val(const std::string &s){ - - vals.clear(); - - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, ' ')) { - vals.push_back(item); - } - } - - void parse_desc(const std::string &s){ - - desc.clear(); - - std::stringstream ss(s); - std::string item; - while (std::getline(ss, item, '\n')) { - desc.push_back(item); - } - } - - public: - void set_ansi(bool b){ ansi = b; } - //create an argument with a given name, description, and default value - argument(std::string _name, std::string _desc, std::string _default = "", std::string _range = "") - { - name = _name; - parse_desc(_desc); - parse_val(_default); - - //if a default value is provided, set the flag - if(_default != "") - flag = true; - else flag = false; - - range = _range; - - - } - - int nargs() - { - return vals.size(); - } - - //return the value of a text argument - std::string as_text(int n = 0) - { - if(!flag) - { - std::cout<<"ERROR - Argument requested without being set: "< n) - return vals[n]; - - else return ""; - } - - //return the value of a floating point argument - float as_float(int n = 0) - { - if(!flag) - { - std::cout<<"ERROR - Argument requested without being set: "< n) - { - float r; - if ( ! (istringstream(vals[n]) >> r) ) r = 0; - return r; - } - - else return 0; - } - - //return the value of an integer argument - int as_int(int n = 0) - { - if(!flag) - { - std::cout<<"ERROR - Argument requested without being set: "< n) - { - int r; - if ( ! (istringstream(vals[n]) >> r) ) r = 0; - return r; - } - - else return 0; - } - - //get the width of the left column - int col_width() - { - int n = 3; - //add the length of the argument name - n += name.size(); - - //if there are any default parameters - if(vals.size() > 0) - { - //padding (parenthesis, =, etc.) - n += 6; - - //for each default argument value - for(int v=0; v args; - - //column width of the longest argument - int col_width; - - //list of sections - std::vector sections; - - public: - - arglist(){ col_width = 0; } - - void set_ansi(bool b) - { - ansi = b; - for(int i=0; i(col_width, arg.col_width()); - } - - void section(std::string _name) - { - argsection s; - s.name = _name; - s.index = args.size(); - sections.push_back(s); - } - - //output the arguments (generally in response to --help) - std::string toStr() - { - std::stringstream ss; - - int si = -1; - - if(sections.size() > 0) - si = 0; - - //for each argument - for(int a=0; a= args.size()) - i = -1; - - return i; - } - - void set(std::string _name, std::string _value) - { - int i = index(_name); - - if(i != -1) - { - args[i].set(_value); - //adjust the column width if necessary - col_width = max(col_width, args[i].col_width()); - } - else - std::cout<<"ERROR - Argument not recognized: "<<_name< -#include -using namespace std; - -static void rtsProgressBar(int percent) -{ - stringstream bar; - static int x = 0; - string slash[4]; - slash[0] = "\\"; - slash[1] = "-"; - slash[2] = "/"; - slash[3] = "|"; - bar<<"["; - for(int i=0; i<40; i++) - { - if(percent > (float)i/(float)40 *100) - bar << "*"; - else - bar << " "; - } - bar<<"]"; - cout << "\r"; // carriage return back to beginning of line - cout << bar.str() << " " << slash[x] << " " << percent << " %"; // print the bars and percentage - x++; // increment to make the slash appear to rotate - if(x == 4) - x = 0; // reset slash animation -} - -#endif diff --git a/ui/arguments.h b/ui/arguments.h new file mode 100644 index 0000000..2231187 --- /dev/null +++ b/ui/arguments.h @@ -0,0 +1,416 @@ +#ifndef RTS_ARGUMENTS +#define RTS_ARGUMENTS + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +namespace rts{ + + class argument + { + private: + bool ansi; + + //argument name + std::string name; + + //description of the argument + std::vector desc; + + //argument values + std::vector vals; + + //range or example + std::string range; + + //flag is true when the argument is user-specified + bool flag; + + void parse_val(const std::string &s){ + + vals.clear(); + + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, ' ')) { + vals.push_back(item); + } + } + + void parse_desc(const std::string &s){ + + desc.clear(); + + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, '\n')) { + desc.push_back(item); + } + } + + public: + void set_ansi(bool b){ ansi = b; } + //create an argument with a given name, description, and default value + argument(std::string _name, std::string _desc, std::string _default = "", std::string _range = "") + { + name = _name; + parse_desc(_desc); + parse_val(_default); + + //if a default value is provided, set the flag + if(_default != "") + flag = true; + else flag = false; + + range = _range; + + + } + + int nargs() + { + return vals.size(); + } + + //return the value of a text argument + std::string as_text(int n = 0) + { + if(!flag) + { + std::cout<<"ERROR - Argument requested without being set: "< n) + return vals[n]; + + else return ""; + } + + //return the value of a floating point argument + float as_float(int n = 0) + { + if(!flag) + { + std::cout<<"ERROR - Argument requested without being set: "< n) + { + float r; + if ( ! (istringstream(vals[n]) >> r) ) r = 0; + return r; + } + + else return 0; + } + + //return the value of an integer argument + int as_int(int n = 0) + { + if(!flag) + { + std::cout<<"ERROR - Argument requested without being set: "< n) + { + int r; + if ( ! (istringstream(vals[n]) >> r) ) r = 0; + return r; + } + + else return 0; + } + + //get the width of the left column + int col_width() + { + int n = 3; + //add the length of the argument name + n += name.size(); + + //if there are any default parameters + if(vals.size() > 0) + { + //padding (parenthesis, =, etc.) + n += 6; + + //for each default argument value + for(int v=0; v args; + + //column width of the longest argument + int col_width; + + //list of sections + std::vector sections; + + public: + + arglist(){ col_width = 0; } + + void set_ansi(bool b) + { + ansi = b; + for(int i=0; i(col_width, arg.col_width()); + } + + void section(std::string _name) + { + argsection s; + s.name = _name; + s.index = args.size(); + sections.push_back(s); + } + + //output the arguments (generally in response to --help) + std::string str() + { + std::stringstream ss; + + int si = -1; + + if(sections.size() > 0) + si = 0; + + //for each argument + for(int a=0; a= args.size()) + i = -1; + + return i; + } + + void set(std::string _name, std::string _value) + { + int i = index(_name); + + if(i != -1) + { + args[i].set(_value); + //adjust the column width if necessary + col_width = max(col_width, args[i].col_width()); + } + else + std::cout<<"ERROR - Argument not recognized: "<<_name< +#include +using namespace std; + +static void rtsProgressBar(int percent) +{ + stringstream bar; + static int x = 0; + string slash[4]; + slash[0] = "\\"; + slash[1] = "-"; + slash[2] = "/"; + slash[3] = "|"; + bar<<"["; + for(int i=0; i<40; i++) + { + if(percent > (float)i/(float)40 *100) + bar << "*"; + else + bar << " "; + } + bar<<"]"; + cout << "\r"; // carriage return back to beginning of line + cout << bar.str() << " " << slash[x] << " " << percent << " %"; // print the bars and percentage + x++; // increment to make the slash appear to rotate + if(x == 4) + x = 0; // reset slash animation +} + +#endif -- libgit2 0.21.4