2
* plot.cpp - mathgl plot wrapper class
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/>.
24
#include "translation.h"
33
//Uncomment me if using MGL >=1_10 to
34
//enable plot cutting and colouring
37
//!Plot error bar estimation strings
38
const char *errModeStrings[] = {
43
const char *plotModeStrings[]= {
56
//Axis min/max bounding box is disallowed to be exactly zero on any given axis
57
// perform a little "push off" by this fudge factor
58
const float AXIS_MIN_TOLERANCE=10*sqrtf(std::numeric_limits<float>::epsilon());
60
int MGLColourFixer::maxCols=-1;
62
void MGLColourFixer::reset()
69
char MGLColourFixer::haveExactColour(float r, float g, float b) const
71
ASSERT(rs.size() == gs.size())
72
ASSERT(gs.size() == bs.size())
74
ASSERT(rs.size() <=getMaxColours());
76
for(unsigned int ui=0; ui<rs.size(); ui++)
78
if( fabs(r-rs[ui]) <std::numeric_limits<float>::epsilon()
79
&& fabs(g-gs[ui]) <std::numeric_limits<float>::epsilon()
80
&& fabs(b-bs[ui]) <std::numeric_limits<float>::epsilon())
81
return mglColorIds[ui+1].id; //Add one to offset to avoid the reserved "k"
87
unsigned int MGLColourFixer::getMaxColours() const
89
//Used cached value if available
93
//The array is statically defined in
94
//mgl/mgl_main.cpp, and must end with an id of zero.
96
//this is not documented at all.
98
while(mglColorIds[maxCols].id)
104
char MGLColourFixer::getNextBestColour(float r, float g, float b)
106
ASSERT(rs.size() == gs.size());
107
ASSERT(gs.size() == bs.size());
110
//As a special case, mgl has its own black
111
if(r == 0.0f && g == 0.0f && b == 0.0f)
112
return mglColorIds[0].id;
117
if(rs.size() == getMaxColours())
119
ASSERT(getMaxColours());
120
//Looks like we ran out of pallete colours.
121
//lets just give up and try to match this against our existing colours
123
//TODO: let this modify the existing pallette
124
// to find a better match.
125
float distanceSqr=std::numeric_limits<float>::max();
126
for(unsigned int ui=0; ui<rs.size(); ui++)
131
//3,4,2 weighted euclidean distance. Weights allow for closer human perception
132
distanceTmp= 3.0*(rs[ui] - r )*(rs[ui] - r ) +4.0*(gs[ui] - g )*(gs[ui] - g )
133
+ 2.0*(bs[ui] - b )*(bs[ui] - b );
137
//use alternate weighting for closer colour perception in "non-red" half of colour cube
138
distanceTmp= 2.0*(rs[ui] - r )*(rs[ui] - r ) +4.0*(gs[ui] - g )*(gs[ui] - g )
139
+ 3.0*(bs[ui] - b )*(bs[ui] - b );
142
if(distanceTmp < distanceSqr)
144
distanceSqr = (distanceTmp);
145
best=ui+1; //offset by 1 because mathgl colour 0 is special
153
//Check to see if we don't already have this
154
// no use wasting palette positions on existing
156
exactMatch=haveExactColour(r,g,b);
161
//Offset zero is special, for black things, like axes
163
graph->SetPalColor(best,r,g,b);
164
mglColorIds[best].col = mglColor(r,g,b);
171
ASSERT(mglColorIds[best].id != 'k');
172
return mglColorIds[best].id;
176
//Nasty string conversion functions.
177
std::wstring strToWStr(const std::string& s)
179
std::wstring temp(s.length(),L' ');
180
std::copy(s.begin(), s.end(), temp.begin());
184
std::string wstrToStr(const std::wstring& s)
186
std::string temp(s.length(), ' ');
187
std::copy(s.begin(), s.end(), temp.begin());
192
//TODO: Refactor these functions to use a common string map
194
string plotString(unsigned int traceType)
196
ASSERT(traceType< PLOT_TRACE_ENDOFENUM);
197
return TRANS(plotModeStrings[traceType]);
200
unsigned int plotID(const std::string &plotString)
202
for(unsigned int ui=0;ui<PLOT_TRACE_ENDOFENUM; ui++)
204
if(plotString==TRANS(plotModeStrings[ui]))
212
unsigned int plotErrmodeID(const std::string &s)
214
for(unsigned int ui=0;ui<PLOT_ERROR_ENDOFENUM; ui++)
216
if(s == errModeStrings[ui])
222
string plotErrmodeString(unsigned int plotID)
224
return errModeStrings[plotID];
228
void genErrBars(const std::vector<float> &x, const std::vector<float> &y,
229
std::vector<float> &errBars, const PLOT_ERROR &errMode)
233
case PLOT_ERROR_NONE:
235
case PLOT_ERROR_MOVING_AVERAGE:
237
ASSERT(errMode.movingAverageNum);
238
errBars.resize(y.size());
239
for(int ui=0;ui<y.size();ui++)
244
//Compute the local mean
245
for(int uj=0;uj<errMode.movingAverageNum;uj++)
248
idx= std::max((int)ui+(int)uj-(int)errMode.movingAverageNum/2,0);
249
idx=std::min(idx,(int)(y.size()-1));
250
ASSERT(idx<y.size());
254
mean/=(float)errMode.movingAverageNum;
256
//Compute the local stddev
259
for(int uj=0;uj<errMode.movingAverageNum;uj++)
262
idx= std::max((int)ui+(int)uj-(int)errMode.movingAverageNum/2,0);
263
idx=std::min(idx,(int)(y.size()-1));
264
stddev+=(y[idx]-mean)*(y[idx]-mean);
267
stddev=sqrtf(stddev/(float)errMode.movingAverageNum);
278
PlotWrapper::PlotWrapper()
280
COMPILE_ASSERT(ARRAYSIZE(plotModeStrings) == PLOT_TRACE_ENDOFENUM);
281
applyUserBounds=false;
286
unsigned int PlotWrapper::addPlot(PlotBase *p)
288
plottingData.push_back(p);
291
p=0; //zero out pointer, we are in command now.
294
//assign a unique identifier to this plot, by which it can be referenced
295
unsigned int uniqueID = plotIDHandler.genId(plottingData.size()-1);
300
void PlotWrapper::clear(bool preserveVisiblity)
303
//Do our best to preserve visiblity of
305
lastVisiblePlots.clear();
306
if(preserveVisiblity)
308
//Remember which plots were visible, who owned them, and their index
309
for(unsigned int ui=0;ui<plottingData.size(); ui++)
311
if(plottingData[ui]->visible && plottingData[ui]->parentObject)
313
lastVisiblePlots.push_back(std::make_pair(plottingData[ui]->parentObject,
314
plottingData[ui]->parentPlotIndex));
319
applyUserBounds=false;
322
//Free the plotting data pointers
323
for(size_t ui=0;ui<plottingData.size();ui++)
324
delete plottingData[ui];
326
plottingData.clear();
327
plotIDHandler.clear();
331
void PlotWrapper::setStrings(unsigned int plotID, const std::string &x,
332
const std::string &y, const std::string &t)
334
unsigned int plotPos=plotIDHandler.getPos(plotID);
335
plottingData[plotPos]->xLabel = strToWStr(x);
336
plottingData[plotPos]->yLabel = strToWStr(y);
338
plottingData[plotPos]->title = strToWStr(t);
342
void PlotWrapper::setParentData(unsigned int plotID, const void *parentObj, unsigned int idx)
344
unsigned int plotPos=plotIDHandler.getPos(plotID);
345
plottingData[plotPos]->parentObject= parentObj;
346
plottingData[plotPos]->parentPlotIndex=idx;
351
void PlotWrapper::setTraceStyle(unsigned int plotUniqueID,unsigned int mode)
354
ASSERT(mode<PLOT_TRACE_ENDOFENUM);
355
plottingData[plotIDHandler.getPos(plotUniqueID)]->traceType=mode;
359
void PlotWrapper::setColours(unsigned int plotUniqueID, float rN,float gN,float bN)
361
unsigned int plotPos=plotIDHandler.getPos(plotUniqueID);
362
plottingData[plotPos]->r=rN;
363
plottingData[plotPos]->g=gN;
364
plottingData[plotPos]->b=bN;
368
void PlotWrapper::setBounds(float xMin, float xMax,
369
float yMin,float yMax)
374
yUserMin=std::max(0.0f,yMin);
380
applyUserBounds=true;
384
void PlotWrapper::disableUserAxisBounds(bool xBound)
386
float xMin,xMax,yMin,yMax;
387
scanBounds(xMin,xMax,yMin,yMax);
396
yUserMin=std::max(0.0f,yMin);
400
//Check to see if we have zoomed all the bounds out anyway
401
if(fabs(xUserMin -xMin)<=std::numeric_limits<float>::epsilon() &&
402
fabs(yUserMin -yMin)<=std::numeric_limits<float>::epsilon())
404
applyUserBounds=false;
410
void PlotWrapper::getBounds(float &xMin, float &xMax,
411
float &yMin,float &yMax) const
421
scanBounds(xMin,xMax,yMin,yMax);
423
ASSERT(xMin <xMax && yMin <=yMax);
426
void PlotWrapper::scanBounds(float &xMin,float &xMax,float &yMin,float &yMax) const
428
//We are going to have to scan for max/min bounds
429
//from the shown plots
430
xMin=std::numeric_limits<float>::max();
431
xMax=-std::numeric_limits<float>::max();
432
yMin=std::numeric_limits<float>::max();
433
yMax=-std::numeric_limits<float>::max();
435
for(unsigned int uj=0;uj<plottingData.size(); uj++)
437
//only consider the bounding boxes from visible plots
438
if(!plottingData[uj]->visible)
441
//Expand our bounding box to encompass that of this visible plot
442
float tmpXMin,tmpXMax,tmpYMin,tmpYMax;
443
plottingData[uj]->getBounds(tmpXMin,tmpXMax,tmpYMin,tmpYMax);
445
xMin=std::min(xMin,tmpXMin);
446
xMax=std::max(xMax,tmpXMax);
447
yMin=std::min(yMin,tmpYMin);
448
yMax=std::max(yMax,tmpYMax);
452
ASSERT(xMin < xMax && yMin <=yMax);
455
void PlotWrapper::bestEffortRestoreVisibility()
457
//Try to match up the last visible plots to the
458
//new plots. Use index and owner as guiding data
460
for(unsigned int uj=0;uj<plottingData.size(); uj++)
461
plottingData[uj]->visible=false;
463
for(unsigned int ui=0; ui<lastVisiblePlots.size();ui++)
465
for(unsigned int uj=0;uj<plottingData.size(); uj++)
467
if(plottingData[uj]->parentObject == lastVisiblePlots[ui].first
468
&& plottingData[uj]->parentPlotIndex == lastVisiblePlots[ui].second)
470
plottingData[uj]->visible=true;
477
lastVisiblePlots.clear();
481
void PlotWrapper::getRawData(vector<vector<vector<float> > > &data,
482
std::vector<std::vector<std::wstring> > &labels) const
484
if(plottingData.empty())
487
//Determine if we have multiple types of plot.
488
//if so, we cannot really return the raw data for this
489
//in a meaningful fashion
490
switch(getVisibleType())
494
//Try to retrieve the raw data from the visible plots
495
for(unsigned int ui=0;ui<plottingData.size();ui++)
497
if(plottingData[ui]->visible)
499
vector<vector<float> > thisDat,dummy;
500
vector<std::wstring> thisLabel;
501
plottingData[ui]->getRawData(thisDat,thisLabel);
503
//Data title size should hopefully be the same
505
ASSERT(thisLabel.size() == thisDat.size());
509
data.push_back(dummy);
510
data.back().swap(thisDat);
512
labels.push_back(thisLabel);
520
//Try to retrieve the raw data from the visible plots
521
for(unsigned int ui=0;ui<plottingData.size();ui++)
523
if(plottingData[ui]->visible)
525
//FIXME: IMPLEMENT ME
531
case PLOT_TYPE_MIXED:
532
case PLOT_TYPE_ENUM_END:
539
unsigned int PlotWrapper::getVisibleType() const
541
unsigned int visibleType=PLOT_TYPE_ENUM_END;
542
for(unsigned int ui=0;ui<plottingData.size() ; ui++)
544
if(plottingData[ui]->visible &&
545
plottingData[ui]->plotType!= visibleType)
547
if(visibleType == PLOT_TYPE_ENUM_END)
549
visibleType=plottingData[ui]->plotType;
554
visibleType=PLOT_TYPE_MIXED;
563
void PlotWrapper::drawPlot(mglGraph *gr) const
565
unsigned int visType = getVisibleType();
566
if(visType == PLOT_TYPE_ENUM_END ||
567
visType == PLOT_TYPE_MIXED)
570
//Un-fudger for mathgl plots
571
MGLColourFixer colourFixer;
572
colourFixer.setGraph(gr);
574
bool haveMultiTitles=false;
575
float minX,maxX,minY,maxY;
576
minX=std::numeric_limits<float>::max();
577
maxX=-std::numeric_limits<float>::max();
578
minY=std::numeric_limits<float>::max();
579
maxY=-std::numeric_limits<float>::max();
581
//Compute the bounding box in data coordinates
582
std::wstring xLabel,yLabel,plotTitle;
583
for(unsigned int ui=0;ui<plottingData.size(); ui++)
585
if(plottingData[ui]->visible)
587
minX=std::min(minX,plottingData[ui]->minX);
588
maxX=std::max(maxX,plottingData[ui]->maxX);
589
minY=std::min(minY,plottingData[ui]->minY);
590
maxY=std::max(maxY,plottingData[ui]->maxY);
594
xLabel=plottingData[ui]->xLabel;
598
if(xLabel!=plottingData[ui]->xLabel)
599
xLabel=stlStrToStlWStr(TRANS("Multiple types"));
602
yLabel=plottingData[ui]->yLabel;
606
if(yLabel!=plottingData[ui]->yLabel)
607
yLabel=stlStrToStlWStr(TRANS("Multiple types"));
609
if(!haveMultiTitles && !plotTitle.size())
610
plotTitle=plottingData[ui]->title;
614
if(plotTitle!=plottingData[ui]->title)
616
plotTitle=L"";//L prefix means wide char
617
haveMultiTitles=true;
626
sX.assign(xLabel.begin(),xLabel.end()); //unicode conversion
627
sY.assign(yLabel.begin(),yLabel.end()); //unicode conversion
630
sT.assign(plotTitle.begin(), plotTitle.end()); //unicode conversion
631
gr->Title(sT.c_str());
635
type=plottingData[0]->plotType;
641
bool useLogPlot=false;
645
for(unsigned int ui=0;ui<plottingData.size(); ui++)
647
if(plottingData[ui]->visible)
649
if(((Plot1D*)plottingData[ui])->wantLogPlot())
656
//work out the bounding box for the plot,
657
//and where the axis should cross
662
ASSERT(yUserMax >=yUserMin);
663
ASSERT(xUserMax >=xUserMin);
677
//Retreive the bounds of the data that is in the plot
683
if(useLogPlot && maxY > 0.0f)
694
//tell mathgl about the bounding box
695
gr->Axis(min,max,axisCross);
697
WARN((fabs(min.x-max.x) > sqrt(std::numeric_limits<float>::epsilon())), "WARNING: Mgl limits (X) too Close! Due to limitiations in MGL, This may inf. loop!");
698
WARN((fabs(min.y-max.y) > sqrt(std::numeric_limits<float>::epsilon())), "WARNING: Mgl limits (Y) too Close! Due to limitiations in MGL, This may inf. loop!");
701
//"Push" bounds around to prevent min == max
711
gr->AdjustTicks("x");
712
gr->SetXTT("%g"); //Set the tick type
713
gr->Axis("xy"); //Build an X-Y crossing axis
715
//Draw regions as faces perp to z.
716
//this will colour in a region of the graph as a rectangle
717
for(unsigned int ui=0;ui<plottingData.size();ui++)
720
curPlot=(Plot1D*)plottingData[ui];
722
//If a plot is not visible, it cannot own a region
723
//nor have a legend in this run.
724
if(!curPlot->visible)
728
curPlot->drawRegions(gr,colourFixer,min,max);
729
curPlot->drawPlot(gr,colourFixer);
733
//Fake an mgl colour code
735
colourCode[0]=colourFixer.getNextBestColour(
736
curPlot->r,curPlot->g,curPlot->b);
738
gr->AddLegend(curPlot->title.c_str(),colourCode);
742
//Prevent mathgl from dropping lines that straddle the plot bound.
745
if(useLogPlot && !notLog)
746
sY = string("\\log_{10}(") + sY + ")";
747
else if (useLogPlot && notLog)
749
sY = string(TRANS("Mixed log/non-log:")) + sY ;
759
gr->Label('x',sX.c_str());
760
gr->Label('y',sY.c_str(),0);
765
gr->SetLegendBox(false);
767
//I have NO idea what this size value is about,
768
//I just kept changing the number until it looked nice.
769
//the library default is -0.85 (why negative??)
770
const float LEGEND_SIZE=-1.1f;
772
//Legend at top right (0x3), in default font "rL" at specified size
773
gr->Legend(0x3,"rL",LEGEND_SIZE);
778
void PlotWrapper::hideAll()
780
for(unsigned int ui=0;ui<plottingData.size(); ui++)
781
plottingData[ui]->visible=false;
785
void PlotWrapper::setVisible(unsigned int uniqueID, bool setVis)
787
unsigned int plotPos = plotIDHandler.getPos(uniqueID);
789
plottingData[plotPos]->visible=setVis;
793
bool PlotWrapper::getRegionIdAtPosition(float x, float y, unsigned int &pId, unsigned int &rId) const
795
for(size_t ui=0;ui<plottingData.size(); ui++)
797
//Regions can only be active for visible plots
798
if(!plottingData[ui]->visible)
801
if(plottingData[ui]->getRegionIdAtPosition(x,y,rId))
816
//Set the default plot properties
818
traceType=PLOT_TRACE_LINES;
819
plotType=PLOT_TYPE_ONED;
820
xLabel=(strToWStr(""));
821
yLabel=(strToWStr(""));
822
title=(strToWStr(""));
826
void Plot1D::setData(const vector<float> &vX, const vector<float> &vY,
827
const vector<float> &vErr)
830
ASSERT(vX.size() == vY.size());
831
ASSERT(vErr.size() == vY.size() || !vErr.size());
834
//Fill up vectors with data
835
xValues.resize(vX.size());
836
std::copy(vX.begin(),vX.end(),xValues.begin());
837
yValues.resize(vY.size());
838
std::copy(vY.begin(),vY.end(),yValues.begin());
840
errBars.resize(vErr.size());
841
std::copy(vErr.begin(),vErr.end(),errBars.begin());
843
//Compute minima and maxima of plot data, and keep a copy of it
844
float maxThis=-std::numeric_limits<float>::max();
845
float minThis=std::numeric_limits<float>::max();
846
for(unsigned int ui=0;ui<vX.size();ui++)
848
minThis=std::min(minThis,vX[ui]);
849
maxThis=std::max(maxThis,vX[ui]);
855
if(maxX - minX < AXIS_MIN_TOLERANCE)
857
minX-=AXIS_MIN_TOLERANCE;
858
maxX+=AXIS_MIN_TOLERANCE;
862
maxThis=-std::numeric_limits<float>::max();
863
minThis=std::numeric_limits<float>::max();
864
for(unsigned int ui=0;ui<vY.size();ui++)
866
minThis=std::min(minThis,vY[ui]);
867
maxThis=std::max(maxThis,vY[ui]);
872
if(maxY - minY < AXIS_MIN_TOLERANCE)
874
minY-=AXIS_MIN_TOLERANCE;
875
maxY+=AXIS_MIN_TOLERANCE;
879
void Plot1D::setData(const vector<std::pair<float,float> > &v)
881
vector<float> dummyVar;
886
void Plot1D::setData(const vector<std::pair<float,float> > &v,const vector<float> &vErr)
888
//Fill up vectors with data
889
xValues.resize(v.size());
890
yValues.resize(v.size());
891
for(unsigned int ui=0;ui<v.size();ui++)
893
xValues[ui]=v[ui].first;
894
yValues[ui]=v[ui].second;
897
errBars.resize(vErr.size());
898
std::copy(vErr.begin(),vErr.end(),errBars.begin());
902
//Compute minima and maxima of plot data, and keep a copy of it
903
float maxThis=-std::numeric_limits<float>::max();
904
float minThis=std::numeric_limits<float>::max();
905
for(unsigned int ui=0;ui<v.size();ui++)
907
minThis=std::min(minThis,v[ui].first);
908
maxThis=std::max(maxThis,v[ui].first);
913
if(maxX - minX < AXIS_MIN_TOLERANCE)
915
minX-=AXIS_MIN_TOLERANCE;
916
maxX+=AXIS_MIN_TOLERANCE;
919
maxThis=-std::numeric_limits<float>::max();
920
minThis=std::numeric_limits<float>::max();
923
ASSERT(vErr.size() == v.size());
924
for(unsigned int ui=0;ui<v.size();ui++)
926
minThis=std::min(minThis,v[ui].second-vErr[ui]);
927
maxThis=std::max(maxThis,v[ui].second+vErr[ui]);
932
for(unsigned int ui=0;ui<v.size();ui++)
934
minThis=std::min(minThis,v[ui].second);
935
maxThis=std::max(maxThis,v[ui].second);
940
maxY=1.10f*maxThis; //The 1.10 is because mathgl chops off data
942
if(maxY - minY < AXIS_MIN_TOLERANCE)
944
minY-=AXIS_MIN_TOLERANCE;
945
maxY+=AXIS_MIN_TOLERANCE;
951
void Plot1D::getBounds(float &xMin,float &xMax,float &yMin,float &yMax) const
953
//OK, we are going to have to scan for max/min
959
//If we are in log mode, then we need to set the
960
//log of that bound before emitting it.
961
if(logarithmic && yMax)
963
yMin=log10(std::max(yMin,1.0f));
968
void Plot1D::drawPlot(mglGraph *gr,MGLColourFixer &fixer) const
972
mglData xDat,yDat,eDat;
975
float *bufferX,*bufferY,*bufferErr;
976
bufferX = new float[xValues.size()];
977
bufferY = new float[yValues.size()];
979
showErrs=errBars.size();
981
bufferErr = new float[errBars.size()];
985
for(unsigned int uj=0;uj<xValues.size(); uj++)
987
bufferX[uj] = xValues[uj];
989
if(yValues[uj] > 0.0)
990
bufferY[uj] = log10(yValues[uj]);
998
for(unsigned int uj=0;uj<xValues.size(); uj++)
1000
bufferX[uj] = xValues[uj];
1001
bufferY[uj] = yValues[uj];
1006
for(unsigned int uj=0;uj<errBars.size(); uj++)
1007
bufferErr[uj] = errBars[uj];
1011
//Mathgl needs to know where to put the error bars.
1012
ASSERT(!showErrs || errBars.size() ==xValues.size());
1014
xDat.Set(bufferX,xValues.size());
1015
yDat.Set(bufferY,yValues.size());
1017
eDat.Set(bufferErr,errBars.size());
1020
colourCode[0]=fixer.getNextBestColour(r,g,b);
1024
//Plot the appropriate form
1027
case PLOT_TRACE_LINES:
1028
//Unfortunately, when using line plots, mathgl moves the data points to the plot boundary,
1029
//rather than linear interpolating them back along their paths. I have emailed the author.
1030
//for now, we shall have to put up with missing lines :( Absolute worst case, I may have to draw them myself.
1034
gr->Plot(xDat,yDat,colourCode);
1036
gr->Error(xDat,yDat,eDat,colourCode);
1039
gr->Plot(xDat,yDat);
1042
case PLOT_TRACE_BARS:
1043
#if !defined(__WIN32) && !defined(__WIN64)
1045
gr->Bars(xDat,yDat,colourCode);
1047
gr->Bars(xDat,yDat);
1052
case PLOT_TRACE_STEPS:
1053
//Same problem as for line plot.
1056
gr->Step(xDat,yDat,colourCode);
1059
gr->Step(xDat,yDat);
1062
case PLOT_TRACE_STEM:
1064
gr->Stem(xDat,yDat,colourCode);
1066
gr->Stem(xDat,yDat);
1070
case PLOT_TRACE_POINTS:
1074
//Mathgl uses strings to manipulate line styles
1076
//space means "no line"
1077
s+="x"; //x shaped point markers
1082
gr->Plot(xDat,yDat,s.c_str());
1084
gr->Error(xDat,yDat,eDat,s.c_str());
1087
gr->Plot(xDat,yDat," x");
1106
void Plot1D::addRegion(unsigned int parentPlot,unsigned int regionID,float start, float end,
1107
float rNew, float gNew, float bNew, Filter *parentFilter)
1110
ASSERT( rNew>=0.0 && rNew <= 1.0);
1111
ASSERT( gNew>=0.0 && gNew <= 1.0);
1112
ASSERT( bNew>=0.0 && bNew <= 1.0);
1115
//1D plots only have one bounding direction
1116
region.bounds.push_back(std::make_pair(start,end));
1117
region.boundAxis.push_back(0); // the bounding direction is along the X axis
1118
region.parentFilter = parentFilter;
1120
//Set the ID and create a unique ID (one that is invariant if regions are added or removed)
1122
region.id = regionID;
1123
region.uniqueID = regionIDHandler.genId(regions.size());
1129
regions.push_back(region);
1132
unsigned int PlotWrapper::getNumVisible() const
1135
for(unsigned int ui=0;ui<plottingData.size();ui++)
1137
if(plottingData[ui]->visible)
1145
bool PlotWrapper::isPlotVisible(unsigned int plotID) const
1147
return plottingData[plotIDHandler.getPos(plotID)]->visible;
1150
void PlotWrapper::getRegion(unsigned int plotId, unsigned int regionId, PlotRegion ®ion) const
1152
plottingData[plotIDHandler.getPos(plotId)]->getRegion(regionId,region);
1155
unsigned int PlotWrapper::plotType(unsigned int plotId) const
1157
return plottingData[plotIDHandler.getPos(plotId)]->plotType;
1161
void PlotWrapper::moveRegionLimit(unsigned int plotId, unsigned int regionId,
1162
unsigned int movementType, float &constrainX, float &constrainY) const
1164
plottingData[plotIDHandler.getPos(plotId)]->moveRegionLimit(
1165
regionId,movementType,constrainX,constrainY);
1169
void PlotWrapper::moveRegion(unsigned int plotID, unsigned int regionId, unsigned int movementType,
1170
float newX, float newY) const
1172
plottingData[plotIDHandler.getPos(plotID)]->moveRegion(regionId,movementType,newX,newY);
1176
void Plot1D::getRawData(std::vector<std::vector< float> > &rawData,
1177
std::vector<std::wstring> &labels) const
1180
vector<float> tmp,dummy;
1182
tmp.resize(xValues.size());
1183
std::copy(xValues.begin(),xValues.end(),tmp.begin());
1184
rawData.push_back(dummy);
1185
rawData.back().swap(tmp);
1187
tmp.resize(yValues.size());
1188
std::copy(yValues.begin(),yValues.end(),tmp.begin());
1189
rawData.push_back(dummy);
1190
rawData.back().swap(tmp);
1192
labels.push_back(xLabel);
1193
labels.push_back(title);
1198
tmp.resize(errBars.size());
1199
std::copy(errBars.begin(),errBars.end(),tmp.begin());
1201
rawData.push_back(dummy);
1202
rawData.back().swap(tmp);
1203
labels.push_back(stlStrToStlWStr(TRANS("error")));
1208
void Plot1D::moveRegionLimit(unsigned int regionID,
1209
unsigned int method, float &newPosX, float &newPosY) const
1212
unsigned int region=regionIDHandler.getPos(regionID);
1214
ASSERT(region<regions.size());
1216
//Check that moving this range will not cause any overlaps with
1219
mean=(regions[region].bounds[0].first + regions[region].bounds[0].second)/2.0f;
1221
//Who is the owner of the current plot -- we only want to interact with our colleagues
1222
float xMin,xMax,yMin,yMax;
1223
getBounds(xMin,xMax,yMin,yMax);
1228
case REGION_MOVE_EXTEND_XMINUS:
1229
//Check that the upper bound does not intersect any RHS of
1231
for(unsigned int ui=0; ui<regions.size(); ui++)
1233
if((regions[ui].bounds[0].second < mean && ui !=region) )
1234
newPosX=std::max(newPosX,regions[ui].bounds[0].second);
1236
//Dont allow past self right
1237
newPosX=std::min(newPosX,regions[region].bounds[0].second);
1238
//Dont extend outside plot
1239
newPosX=std::max(newPosX,xMin);
1242
case REGION_MOVE_TRANSLATE_X:
1243
//Check that the upper bound does not intersect any RHS or LHS of
1248
//Disallow hitting other bounds
1249
for(unsigned int ui=0; ui<regions.size(); ui++)
1251
if((regions[ui].bounds[0].first > mean && ui != region) )
1252
newPosX=std::min(newPosX,regions[ui].bounds[0].first);
1254
newPosX=std::max(newPosX,xMin);
1258
//Disallow hitting other bounds
1259
for(unsigned int ui=0; ui<regions.size(); ui++)
1261
if((regions[ui].bounds[0].second < mean && ui != region))
1262
newPosX=std::max(newPosX,regions[ui].bounds[0].second);
1264
//Dont extend outside plot
1265
newPosX=std::min(newPosX,xMax);
1269
case REGION_MOVE_EXTEND_XPLUS:
1270
//Disallow hitting other bounds
1272
for(unsigned int ui=0; ui<regions.size(); ui++)
1274
if((regions[ui].bounds[0].second > mean && ui != region))
1275
newPosX=std::min(newPosX,regions[ui].bounds[0].first);
1277
//Dont allow past self left
1278
newPosX=std::max(newPosX,regions[region].bounds[0].first);
1279
//Dont extend outside plot
1280
newPosX=std::min(newPosX,xMax);
1289
void Plot1D::moveRegion(unsigned int regionID, unsigned int method, float newPosX,float newPosY) const
1291
//Well, we should have called this externally to determine location
1292
//let us confirm that this is the case
1293
moveRegionLimit(regionID,method,newPosX,newPosY);
1296
unsigned int region=regionIDHandler.getPos(regionID);
1297
ASSERT(regions[region].parentFilter);
1299
//Pass the filter ID value stored in the region to the filter, along with the new
1300
//value to take for that filter ID
1301
regions[region].parentFilter->setPropFromRegion(method,regions[region].id,newPosX);
1306
void Plot1D::drawRegions(mglGraph *gr,MGLColourFixer &fixer,
1307
const mglPoint &min,const mglPoint &max) const
1309
//Mathgl pallette colour name
1313
for(unsigned int uj=0;uj<regions.size();uj++)
1315
//Compute region bounds, such that it will not exceed the axis
1316
float rMinX, rMaxX, rMinY,rMaxY;
1319
rMinX = std::max(min.x,regions[uj].bounds[0].first);
1320
rMaxX = std::min(max.x,regions[uj].bounds[0].second);
1322
//Prevent drawing inverted regions
1323
if(rMaxX > rMinX && rMaxY > rMinY)
1325
colourCode[0] = fixer.getNextBestColour(regions[uj].r,
1326
regions[uj].g,regions[uj].b);
1327
colourCode[1] = '\0';
1328
gr->FaceZ(rMinX,rMinY,-1,rMaxX-rMinX,rMaxY-rMinY,
1336
void Plot1D::clear( bool preserveVisiblity)
1339
regionIDHandler.clear();
1342
bool Plot1D::getRegionIdAtPosition(float x, float y, unsigned int &id) const
1344
for(unsigned int ui=0;ui<regions.size();ui++)
1346
ASSERT(regions[ui].boundAxis.size() == regions[ui].bounds.size() &&
1347
regions[ui].boundAxis.size()==1);
1348
ASSERT(regions[ui].boundAxis[0] == 0);
1351
if(regions[ui].bounds[0].first < x &&
1352
regions[ui].bounds[0].second > x )
1363
void Plot1D::getRegion(unsigned int id, PlotRegion &r) const
1365
r = regions[regionIDHandler.getPos(id)];
1372
//Draw the plot onto a given MGL graph
1373
void Plot2D::drawPlot(mglGraph *gr,MGLColourFixer &fixer) const
1376
mglData xDat,yDat,exDat,eyDat;
1379
ASSERT(xValues.size() == yValues.size());
1381
//Allocate buffers for XY data and error bars (as needed)
1382
float *bufferX,*bufferY,*bufferErrX,*bufferErrY;
1383
bufferX = new float[xValues.size()];
1384
bufferY = new float[yValues.size()];
1386
ASSERT(xErrBars.size() == yErrBars.size() || !xErrBars.size() || !yErrBars.size());
1388
bufferErrX = new float[xErrBars.size()];
1390
bufferErrY = new float[yErrBars.size()];
1392
//Copy data into buffers for mgl
1393
for(unsigned int uj=0;uj<xValues.size(); uj++)
1395
bufferX[uj] = xValues[uj];
1396
bufferY[uj] = yValues[uj];
1400
for(unsigned int uj=0;uj<xErrBars.size(); uj++)
1401
bufferErrX[uj] = xErrBars[uj];
1402
exDat.Set(bufferErrX,xErrBars.size());
1407
for(unsigned int uj=0;uj<yErrBars.size(); uj++)
1408
bufferErrY[uj] = yErrBars[uj];
1409
eyDat.Set(bufferErrY,yErrBars.size());
1412
xDat.Set(bufferX,xValues.size());
1413
yDat.Set(bufferY,yValues.size());
1415
//Mathgl palette colour name
1417
colourCode[0]= fixer.getNextBestColour(r,g,b);
1420
//Plot the appropriate form
1423
case PLOT_TRACE_LINES:
1424
//Unfortunately, when using line plots, mathgl moves the data points to the plot boundary,
1425
//rather than linear interpolating them back along their paths. I have emailed the author.
1426
//for now, we shall have to put up with missing lines :( Absolute worst case, I may have to draw them myself.
1430
gr->Plot(xDat,yDat,colourCode);
1431
if(xErrBars.size() && yErrBars.size())
1432
gr->Error(xDat,yDat,exDat,eyDat,colourCode);
1433
else if(xErrBars.size())
1435
gr->Error(xDat,yDat,exDat,colourCode);
1437
else if(yErrBars.size())
1439
//FIXME: Implement me?
1445
gr->Plot(xDat,yDat);
1453
//!Scan for the data bounds.
1454
void Plot2D::getBounds(float &xMin,float &xMax,
1455
float &yMin,float &yMax) const
1457
//OK, we are going to have to scan for max/min
1464
//Retrieve the raw data associated with this plot.
1465
void Plot2D::getRawData(vector<vector<float> > &rawData,
1466
std::vector<std::wstring> &labels) const
1469
vector<float> tmp,dummy;
1471
tmp.resize(xValues.size());
1472
std::copy(xValues.begin(),xValues.end(),tmp.begin());
1473
rawData.push_back(dummy);
1474
rawData.back().swap(tmp);
1476
tmp.resize(yValues.size());
1477
std::copy(yValues.begin(),yValues.end(),tmp.begin());
1478
rawData.push_back(dummy);
1479
rawData.back().swap(tmp);
1481
labels.push_back(xLabel);
1482
labels.push_back(title);
1485
ASSERT(xErrBars.size() == yErrBars.size() || !xErrBars.size() || !yErrBars.size());
1489
tmp.resize(xErrBars.size());
1490
std::copy(xErrBars.begin(),xErrBars.end(),tmp.begin());
1492
rawData.push_back(dummy);
1493
rawData.back().swap(tmp);
1494
labels.push_back(stlStrToStlWStr(TRANS("x error")));
1499
tmp.resize(yErrBars.size());
1500
std::copy(yErrBars.begin(),yErrBars.end(),tmp.begin());
1502
rawData.push_back(dummy);
1503
rawData.back().swap(tmp);
1504
labels.push_back(stlStrToStlWStr(TRANS("y error")));
1508
//!Retrieve the ID of the non-overlapping region in X-Y space
1509
bool Plot2D::getRegionIdAtPosition(float x, float y, unsigned int &id) const
1511
for(unsigned int ui=0;ui<regions.size();ui++)
1513
ASSERT(regions[ui].boundAxis.size() == regions[ui].bounds.size())
1515
bool containedInThis;
1516
containedInThis=true;
1518
for(unsigned int uj=0; uj<regions[ui].bounds.size();uj++)
1521
axis = regions[ui].boundAxis[uj];
1524
if(regions[ui].bounds[uj].first < x &&
1525
regions[ui].bounds[uj].second > x )
1527
containedInThis=false;
1533
if(regions[ui].bounds[uj].first < y &&
1534
regions[ui].bounds[uj].second > y )
1536
containedInThis=false;
1553
//!Retrieve a region using its unique ID
1554
void Plot2D::getRegion(unsigned int id, PlotRegion &r) const
1556
r = regions[regionIDHandler.getPos(id)];
1559
//!Pass the region movement information to the parent filter object
1560
void Plot2D::moveRegion(unsigned int regionId, unsigned int method,
1561
float newX, float newY) const
1566
//!Obtain limit of motion for a given region movement type
1567
void Plot2D::moveRegionLimit(unsigned int regionId,
1568
unsigned int movementType, float &maxX, float &maxY) const