2
* basics.h - Basic functionality header
3
* Copyright (C) 2011, D Haley
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
22
//!Basic objects header file
24
#include "assertion.h"
25
#include "mathfuncs.h"
40
std::string boolStrEnc(bool b);
42
bool dummyCallback(bool);
44
extern const char *DTD_NAME;
45
extern const char *PROGRAM_NAME;
46
extern const char *PROGRAM_VERSION;
47
extern const char *FONT_FILE;
49
#define ARRAYSIZE(f) (sizeof (f) / sizeof(*f))
53
//C file peek function
54
inline int fpeek(FILE *stream)
66
//!Text file loader errors
72
ERR_FILE_ENUM_END // not an error, just end of enum
75
extern const char *TEXT_LOAD_ERR_STRINGS[];
78
template<class T1, class T2>
79
bool hasFirstInPairVec(const std::vector<std::pair<T1,T2> > &v, const std::pair<T1,T2> &r)
81
for(size_t ui=0;ui<v.size();ui++)
83
if(v[ui].first == r.first)
89
//!Convert a path format into a native path from unix format
90
std::string convertFileStringToNative(const std::string &s);
92
//!Convert a path format into a unix path from native format
93
std::string convertFileStringToCanonical(const std::string &s);
95
//!Convert a normal string to a latex one, using charachter replacement
96
void tickSpacingsFromInterspace(float start, float end,
97
float interSpacing, std::vector<float> &spacings);
99
void tickSpacingsFromFixedNum(float start, float end,
100
unsigned int nTicks, std::vector<float> &spacings);
102
//!Get a "human-like" version of the time elapsed between new and original time period
103
std::string veryFuzzyTimeSince( time_t origTime, time_t newTime);
106
//!A routine for loading numeric data from a text file
107
unsigned int loadTextData(const char *cpFilename,
108
std::vector<std::vector<float> > &dataVec,
109
std::vector<std::string> &header,const char *delim);
114
bool writeTextFile(const char *cpFilename,
115
const std::vector<std::pair<T, T> > &dataVec, const char delim='\t')
117
std::ofstream f(cpFilename);
122
for(unsigned int ui=0;ui<dataVec.size();ui++)
123
f << dataVec[ui].first << delim << dataVec[ui].second << std::endl;
128
//!Return the default font file to use. Must precede (first) call to getDefaultFontFile
129
void setDefaultFontFile(const std::string &font);
131
//!Return the default font file to use.
132
//Not valid until you have set it with setDefaultFontFile
133
std::string getDefaultFontFile();
135
//!Generate string that can be parsed by wxPropertyGrid for combo control
136
//String format is CHOICEID:string 1, string 2, string 3,..... string_N
137
std::string choiceString(std::vector<std::pair<unsigned int, std::string> > comboString,
138
unsigned int curChoice);
141
//!Generate a string with leading digits up to maxDigit (eg, if maxDigit is 424, and thisDigit is 1
142
//leading digit will generate the string 001
143
std::string digitString(unsigned int thisDigit, unsigned int maxDigit);
145
//!Returns Choice from string (see choiceString(...) for string format)
146
std::string getActiveChoice(std::string choiceString);
148
//!Convert a choiceString() into something that a wxGridCellChoiceEditor will accept
149
std::string wxChoiceParamString(std::string choiceString);
152
inline std::string stlWStrToStlStr(const std::wstring& s)
154
std::string temp(s.length(),' ');
155
std::copy(s.begin(), s.end(), temp.begin());
159
inline std::wstring stlStrToStlWStr(const std::string& s)
161
std::wstring temp(s.length(),L' ');
162
std::copy(s.begin(), s.end(), temp.begin());
166
//!Template function to cast and object to another by the stringstream
168
template<class T1, class T2> bool stream_cast(T1 &result, const T2 &obj)
170
std::stringstream ss;
176
template<class T1, class T2> bool stream_cast_noskip(T1 &result, const T2 &obj)
178
std::stringstream ss;
185
//!Replace first instance of marker with null terminator
186
void nullifyMarker(char *buffer, char marker);
188
//retrieve the active bit in a power of two sequence
189
inline unsigned int getBitNum(unsigned int u)
203
inline bool isVersionNumberString(const std::string &s)
205
for(unsigned int ui=0;ui<s.size();ui++)
210
if(s[ui] !='.' || ui ==0 || ui ==s.size())
218
//Strip whitespace from a string
219
std::string stripWhite(const std::string &str);
221
//!Return a lowercas version for a given string
222
std::string lowercase(std::string s);
224
void stripZeroEntries(std::vector<std::string> &s);
226
//Convert a point string from its "C" language respresentation to a point vlaue
227
bool parsePointStr(const std::string &str,Point3D &pt);
229
bool parseColString(const std::string &str,
230
unsigned char &r, unsigned char &g, unsigned char &b, unsigned char &a);
232
void genColString(unsigned char r, unsigned char g,
233
unsigned char b, unsigned char a, std::string &s);
234
void genColString(unsigned char r, unsigned char g,
235
unsigned char b, std::string &s);
237
//!Retrieve the maximum version string from a list of version strings
238
std::string getMaxVerStr(const std::vector<std::string> &verStrings);
241
std::string stripWhite(const std::string &str);
243
//!Split string references using a single delimeter.
244
void splitStrsRef(const char *cpStr, const char delim,std::vector<std::string> &v );
246
//!Split string references using any of a given string of delimters
247
void splitStrsRef(const char *cpStr, const char *delim,std::vector<std::string> &v );
249
//!A class to manage "tear-off" ID values, to allow for indexing without knowing position.
250
//You simply ask for a new unique ID. and it maintains the position->ID mapping
251
//TODO: Extend to any unique type, rather than just int (think iterators..., pointers)
252
class UniqueIDHandler
255
//!position-ID pairings
256
std::list<std::pair<unsigned int, unsigned int > > idList;
259
//!Generate a unique ID value, storing the position ID pair
260
unsigned int genId(unsigned int position);
261
//!Remove a uniqueID using its position
262
void killByPos(unsigned int position);
263
//!Get the position from its unique ID
264
unsigned int getPos(unsigned int id) const;
265
//!Get the uniqueID from the position
266
unsigned int getId(unsigned int pos) const;
268
//!Get all unique IDs
269
void getIds(std::vector<unsigned int> &idvec) const;
272
//!Get the number of elements stored
273
unsigned int size() const {return idList.size();};
276
//!Get total filesize in bytes
277
bool getFilesize(const char *fname, size_t &size);
279
//!get total ram in MB
282
//!Get available ram in MB
283
size_t getAvailRAM();
287
bool isValidXML(const char *filename);
290
inline std::string tabs(unsigned int nTabs)
294
std::fill(s.begin(),s.end(),'\t');
298
class ComparePairFirst
301
template<class T1, class T2>
302
bool operator()(const std::pair< T1, T2 > &p1, const std::pair<T1,T2> &p2)
304
return p1.first< p2.first;
308
class ComparePairSecond
311
template<class T1, class T2>
312
bool operator()(const std::pair< T1, T2 > &p1, const std::pair<T1,T2> &p2)
314
return p1.second< p2.second;
319
class ComparePairFirstReverse
322
template<class T1, class T2>
323
bool operator()(const std::pair< T1, T2 > &p1, const std::pair<T1,T2> &p2)
325
return p1.first> p2.first;
329
//! A helper class to define a bounding cube
332
//!bounding values (x,y,z) (lower,upper)
342
void setBounds(float xMin,float yMin,float zMin,
343
float xMax,float yMax,float zMax) {
344
bounds[0][0]=xMin; bounds[1][0]=yMin; bounds[2][0]=zMin;
345
bounds[0][1]=xMax; bounds[1][1]=yMax; bounds[2][1]=zMax;
346
valid[0][0]=true; valid[1][0]=true; valid[2][0]=true;
347
valid[0][1]=true; valid[1][1]=true; valid[2][1]=true;
350
void setBounds(const BoundCube &b)
352
for(unsigned int ui=0;ui<3;ui++)
354
bounds[ui][0] = b.bounds[ui][0];
355
valid[ui][0] = b.valid[ui][0];
356
bounds[ui][1] = b.bounds[ui][1];
357
valid[ui][1] = b.valid[ui][1];
362
valid[0][0]=false; valid[1][0]=false; valid[2][0]=false;
363
valid[0][1]=false; valid[1][1]=false; valid[2][1]=false;
366
//Set the cube to be "inside out" at the limits of numeric results;
367
void setInverseLimits();
369
void setBound(unsigned int bound, unsigned int minMax, float value) {
370
ASSERT(bound <3 && minMax < 2);
371
bounds[bound][minMax]=value;
372
valid[bound][minMax]=true;
375
float getBound(unsigned int bound, unsigned int minMax) const {
376
ASSERT(bound <3 && minMax < 2);
377
ASSERT(valid[bound][minMax]==true);
378
return bounds[bound][minMax];
380
//!Return the centroid
381
Point3D getCentroid() const;
384
void getBounds(Point3D &low, Point3D &high) const ;
387
float getSize(unsigned int dim) const;
389
//! Returns true if all bounds are valid
390
bool isValid() const;
392
//! Returns true if any bound is of null thickness
395
//!Returns true if any bound of datacube is considered to be "large" in magnitude compared to
396
// floating pt data type.
397
bool isNumericallyBig() const;
399
//!Obtain bounds from an array of Point3Ds
400
void setBounds( const Point3D *ptArray, unsigned int nPoints);
401
//!Use two points to set bounds -- does not need to be high,low. this is worked out/
402
void setBounds( const Point3D &p, const Point3D &q);
403
//!Obtain bounds from an array of Point3Ds
404
void setBounds(const std::vector<Point3D> &ptArray);
405
//!Checks if a point intersects a sphere of centre Pt, radius^2 sqrRad
406
bool intersects(const Point3D &pt, float sqrRad);
407
//Check to see if the point is contained in, or part of the walls
409
bool containsPt(const Point3D &pt) const;
411
//!Is this bounding cube completely contained within a sphere centred on pt of sqr size sqrRad?
412
bool containedInSphere(const Point3D &pt, float sqrRad) const;
414
//!Returns maximum distnace to box corners (which is an upper bound on max box distance).
415
//Bounding box must be valid.
416
float getMaxDistanceToBox(const Point3D &pt) const;
418
//Get the largest dimension of the bound cube
419
float getLargestDim() const;
421
//Return the rectilinear volume represented by this prism.
422
float volume() const { return (bounds[0][1] - bounds[0][0])*
423
(bounds[1][1] - bounds[1][0])*(bounds[2][1] - bounds[2][0]);}
425
const BoundCube &operator=(const BoundCube &);
426
//!Expand (as needed) volume such that the argument bounding cube is enclosed by this one
427
void expand(const BoundCube &b);
428
//!Expand such that point is contained in this volume. Existing volume must be valid
429
void expand(const Point3D &p);
430
//!Expand by a specified thickness
431
void expand(float v);
434
friend std::ostream &operator<<(std::ostream &stream, const BoundCube& b);
437
friend class K3DTree;
438
friend class K3DTreeMk2;
443
//!Return only the filename component
444
std::string onlyFilename( const std::string& path );
445
//!Return only the directory name component of the full path
446
std::string onlyDir( const std::string& path );
448
//OK, this is a bit tricky. We override the operators to call
449
//a callback, so the UI updates keep happening, even inside the STL function
452
class GreaterWithCallback
455
bool (*callback)(bool);
456
//!Reduction frequency (use callback every k its)
458
//!Current reduction counter
459
unsigned int reduction;
460
//!pointer to progress value
461
unsigned int *prgPtr;
463
//!Second argument is a "reduction" value to set the number of calls
464
//to the random functor before initiating a callback
465
GreaterWithCallback( bool (*ptr)(bool),unsigned int red)
466
{ callback=ptr; reduction=redMax=red;};
468
bool operator()(const T &a, const T &b)
483
class EqualWithCallback
486
bool (*callback)(bool);
487
//!Reduction frequency (use callback every k its)
489
//!Current reduction counter
490
unsigned int reduction;
491
//!pointer to progress value
492
unsigned int *prgPtr;
494
//!Second argument is a "reduction" value to set the number of calls
495
//to the random functor before initiating a callback
496
EqualWithCallback( bool (*ptr)(bool),unsigned int red)
497
{ callback=ptr; reduction=redMax=red;};
499
bool operator()(const T &a, const T &b)
514
//Randomly select subset. Subset will be (somewhat) sorted on output
515
template<class T> size_t randomSelect(std::vector<T> &result, const std::vector<T> &source,
516
RandNumGen &rng, size_t num,unsigned int &progress,bool (*callback)(bool), bool strongRandom=false)
518
const unsigned int NUM_CALLBACK=50000;
519
//If there are not enough points, just copy it across in whole
520
if(source.size() <= num)
523
result.resize(source.size());
524
for(size_t ui=0; ui<num; ui++)
525
result[ui] = source[ui];
532
if(strongRandom || source.size() < 4)
535
size_t numTicksNeeded;
536
//If the number of items is larger than half the source size,
537
//switch to tracking vacancies, rather than data
538
if(num < source.size()/2)
541
numTicksNeeded=source.size()-num;
543
//Randomly selected items
545
std::vector<size_t> ticks;
546
ticks.resize(numTicksNeeded);
548
//Create an array of numTicksNeededbers and fill
549
for(size_t ui=0; ui<numTicksNeeded; ui++)
550
ticks[ui]=(size_t)(rng.genUniformDev()*(source.size()-1));
552
//Remove duplicates. Intersperse some callbacks to be nice
553
GreaterWithCallback<size_t> gFunctor(callback,50000);
554
std::sort(ticks.begin(),ticks.end(),gFunctor);
555
EqualWithCallback<size_t> eqFunctor(callback,50000);
556
std::vector<size_t>::iterator newLast;
557
newLast=std::unique(ticks.begin(),ticks.end());
558
ticks.erase(newLast,ticks.end());
560
std::vector<size_t> moreTicks;
561
//Top up with unique entries
562
while(ticks.size() +moreTicks.size() < numTicksNeeded)
566
//This is actually not too bad. the collision probability is at most 50%
567
//due the switching behaviour above, for any large number of items
568
//So this is at worst case nlog(n) (I think)
569
index =(size_t)(rng.genUniformDev()*(float)(source.size()-1));
570
if(!binary_search(ticks.begin(),ticks.end(),index) &&
571
std::find(moreTicks.begin(),moreTicks.end(),index) ==moreTicks.end())
572
moreTicks.push_back(index);
576
ticks.reserve(numTicksNeeded);
577
for(size_t ui=0;ui<moreTicks.size();ui++)
578
ticks.push_back(moreTicks[ui]);
582
ASSERT(ticks.size() == numTicksNeeded);
586
//Transfer the output
587
unsigned int curProg=NUM_CALLBACK;
589
if(num < source.size()/2)
591
for(std::vector<size_t>::iterator it=ticks.begin();it!=ticks.end();it++)
594
result[pos]=source[*it];
598
progress= (unsigned int)((float)(pos)/((float)num)*100.0f);
600
curProg=NUM_CALLBACK;
606
//Sort the ticks properly (mostly sorted anyway..)
607
std::sort(ticks.begin(),ticks.end(),gFunctor);
609
unsigned int curTick=0;
610
for(size_t ui=0;ui<source.size(); ui++)
612
//Don't copy if this is marked
613
if(ui == ticks[curTick])
617
ASSERT(result.size() > (ui-curTick));
618
result[ui-curTick]=source[ui];
623
progress= (unsigned int)(((float)(ui)/(float)source.size())*100.0f);
625
curProg=NUM_CALLBACK;
634
//Use a weak randomisation
635
LinearFeedbackShiftReg l;
637
//work out the mask level we need to use
640
while(i < (source.size()<<1))
646
//linear shift table starts at 3.
653
//start at a random position in the linear state
654
start =(size_t)(rng.genUniformDev()*i);
659
unsigned int curProg=NUM_CALLBACK;
660
//generate unique weak random numbers.
666
//use the source if it lies within range.
667
//drop it silently if it is out of range
668
if(res< source.size())
670
result[ui] =source[res];
675
progress= (unsigned int)((float)(ui)/((float)source.size())*100.0f);
677
curProg=NUM_CALLBACK;
686
//Randomly select subset. Subset will be (somewhat) sorted on output
687
template<class T> size_t randomDigitSelection(std::vector<T> &result, const size_t max,
688
RandNumGen &rng, size_t num,unsigned int &progress,bool (*callback)(bool),
689
bool strongRandom=false)
691
//If there are not enough points, just copy it across in whole
696
for(size_t ui=0; ui<num; ui++)
704
//If we have strong randomisation, or we have too few items to use the LFSR,
705
//use proper random generation
706
if(strongRandom || max < 3 )
709
size_t numTicksNeeded;
710
//If the number of items is larger than half the source size,
711
//switch to tracking vacancies, rather than data
715
numTicksNeeded=max-num;
717
//Randomly selected items
719
std::vector<size_t> ticks;
720
ticks.resize(numTicksNeeded);
722
//Create an array of numTicksNeededbers and fill
723
for(size_t ui=0; ui<numTicksNeeded; ui++)
724
ticks[ui]=(size_t)(rng.genUniformDev()*(max-1));
726
//Remove duplicates. Intersperse some callbacks to be nice
727
GreaterWithCallback<size_t> gFunctor(callback,50000);
728
std::sort(ticks.begin(),ticks.end(),gFunctor);
729
EqualWithCallback<size_t> eqFunctor(callback,50000);
731
std::vector<size_t>::iterator itLast;
732
itLast=std::unique(ticks.begin(),ticks.end(),eqFunctor);
733
ticks.erase(itLast,ticks.end());
734
std::vector<size_t> moreTicks;
735
//Top up with unique entries
736
while(ticks.size() +moreTicks.size() < numTicksNeeded)
740
//This is actually not too bad. the collision probability is at most 50%
741
//due the switching behaviour above, for any large number of items
742
//So this is at worst case nlog(n) (I think)
743
index =(size_t)(rng.genUniformDev()*(float)(max-1));
744
if(!binary_search(ticks.begin(),ticks.end(),index) &&
745
std::find(moreTicks.begin(),moreTicks.end(),index) ==moreTicks.end())
746
moreTicks.push_back(index);
750
ticks.reserve(numTicksNeeded);
751
for(size_t ui=0;ui<moreTicks.size();ui++)
752
ticks.push_back(moreTicks[ui]);
756
ASSERT(ticks.size() == numTicksNeeded);
760
//Transfer the output
761
unsigned int curProg=70000;
765
for(std::vector<size_t>::iterator it=ticks.begin();it!=ticks.end();it++)
772
progress= (unsigned int)((float)(curProg)/((float)num)*100.0f);
780
//Sort the ticks properly (mostly sorted anyway..)
781
std::sort(ticks.begin(),ticks.end(),gFunctor);
783
unsigned int curTick=0;
784
for(size_t ui=0;ui<numTicksNeeded; ui++)
786
//Don't copy if this is marked
787
if(ui == ticks[curTick])
790
result[ui-curTick]=ui;
794
progress= (unsigned int)((float)(curProg)/((float)num)*100.0f);
805
//Use a weak randomisation
806
LinearFeedbackShiftReg l;
808
//work out the mask level we need to use
818
//start at a random position in the linear state
819
start =(size_t)(rng.genUniformDev()*i);
824
//generate unique weak random numbers.
830
//use the source if it lies within range.
831
//drop it silently if it is out of range