/* STPlot v2.2 API for plotting with gnuplot. Created by Stilianos Louca on 2008.11.23 Last modified on 2012.06.07 Tested using GCC 4.2.1 and gnuplot 4.4.2. License agreement: You are free to use and modify STPlot for personal or educational purposes as you like. You may not redistribute it, modified or not, without giving credits to the original author. The author would furthermore be grateful if he is cited in any scientific works making use of STPlot. See the 'User Guide' that came with this source for a thorough documentation of STPlot. */ #ifndef STPLOT_DEF #define STPLOT_DEF #include "STPlot.h" namespace ST{ string STPLOT_GNUPLOT_PATH; #pragma mark - #pragma mark GnuplotPipe #pragma mark - //copy constructor GnuplotPipe::GnuplotPipe(const GnuplotPipe &original): STPipe(original) // copy all pipe-related parts { // adopt the original's settings gnuplotPath = original.gnuplotPath; scriptPath = original.scriptPath; _foundGnuplot = original._foundGnuplot; if(original.scriptStream!=NULL){ scriptStream = fopen(scriptPath.c_str(), "a"); }else{ scriptStream = NULL; } } //assignment operator GnuplotPipe &GnuplotPipe::operator=(const GnuplotPipe &original){ STPipe::operator=(original); // assign all pipe-related parts // close any existing output streams if(scriptStream!=NULL){ fclose(scriptStream); remove(scriptPath.c_str()); scriptStream = NULL; } // adopt the original's settings gnuplotPath = original.gnuplotPath; _foundGnuplot = original._foundGnuplot; scriptPath = original.scriptPath; if(original.scriptStream!=NULL){ scriptStream = fopen(scriptPath.c_str(), "a"); } return *this; } void GnuplotPipe::open(){ setRequirePipe(false); tryToFindGnuplot(); STPipe::open(ST_DEFAULT_PERSIST_PLOT ? gnuplotPath + " -persist" : gnuplotPath); } void GnuplotPipe::open(string _gnuplotPath){ setRequirePipe(false); tryToFindGnuplot(_gnuplotPath); STPipe::open(ST_DEFAULT_PERSIST_PLOT ? gnuplotPath + " -persist" : gnuplotPath); } bool GnuplotPipe::tryToFindGnuplot(const string &firstGuess){ gnuplotPath = firstGuess; if((gnuplotPath=="") || (!STPlot_aux_fileExists(gnuplotPath))){ gnuplotPath = STPLOT_GNUPLOT_PATH; }else{ return (_foundGnuplot=true); } if((gnuplotPath=="") || (!STPlot_aux_fileExists(gnuplotPath))){ if(!getProgramPath(string("gnuplot")+ST_GNUPLOT_IDEAL_VERSION, gnuplotPath)){ gnuplotPath = ""; } }else{ return (_foundGnuplot=true); } if((gnuplotPath=="") || (!STPlot_aux_fileExists(gnuplotPath))){ if(!getProgramPath("gnuplot", gnuplotPath)){ gnuplotPath = ""; } }else{ return (_foundGnuplot=true); } if((gnuplotPath=="") || (!STPlot_aux_fileExists(gnuplotPath))){ gnuplotPath = ST_DEFAULT_GNUPLOT_PATH; }else{ return (_foundGnuplot=true); } if((gnuplotPath=="") || (!STPlot_aux_fileExists(gnuplotPath))){ gnuplotPath = "gnuplot"; }else{ return (_foundGnuplot=true); } if((gnuplotPath=="") || (!STPlot_aux_fileExists(gnuplotPath))){ return (_foundGnuplot=false); }else{ return (_foundGnuplot=true); } } bool GnuplotPipe::write(const char *format, ...){ va_list args, logArgumentList; bool result; va_start(args, format); if(isInScriptMode()){ va_start(logArgumentList, format); result=(vfprintf(scriptStream, format, args) >= 0); if(logStream != NULL){ if(logStream->good()){ vstreamprintf(*logStream, format, logArgumentList); } } va_end(logArgumentList); }else{ result = STPipe::write(format, args); } va_end(args); return result; } bool GnuplotPipe::write(const string &message){ if(isInScriptMode()){ bool result = (fprintf(scriptStream, message.c_str()) >= 0); if((logStream != NULL) && (logStream->good())) (*logStream) << message; return result; }else{ return STPipe::write(message); } } void GnuplotPipe::transformToScript(){ if(system(NULL) == 0) return; //no shell access, so script can not be executed. scriptPath = STPlot_aux_getUniqueTempFilePath(); //tmpnam(NULL); // deprecated scriptStream = fopen(scriptPath.c_str(), "w"); } void GnuplotPipe::transformToPipe(){ if(scriptStream != NULL){ fclose(scriptStream); remove(scriptPath.c_str()); scriptStream = NULL; } } bool GnuplotPipe::executeScript(){ if(!isInScriptMode()) return false; if(!foundGnuplot()) return false; if(fflush(scriptStream) == EOF) return false; system((gnuplotPath+" '" + scriptPath + "'" + (SUPPRESS_GNUPLOT_ERRORS ? " 2>/dev/null" : "")).c_str()); return true; } bool GnuplotPipe::isInScriptMode() const{ return (scriptStream != NULL); } string GnuplotPipe::currentGnuplotVersion() const{ string version; if(!ST::getCommandOutput(gnuplotPath + " --version", true, version)){ return "unknown"; } int p0 = strlen("gnuplot "); int p1 = version.find(" patchlevel"); int p2 = p1 + strlen(" patchlevel"); return version.substr(p0, p1-p0); } #pragma mark - #pragma mark Escaping & Greek symbols #pragma mark //Used internally for replacing greek symbols with appropriate gnuplot symbols //For more details see: http://t16web.lanl.gov/Kawano/gnuplot/label-e.html const char* Symbols[] = { "$Alpha$", "$Beta$", "$Gamma$", "$Delta$", "$Epsilon$", "$Zeta$", "$Eta$", "$Theta$", "$Iota$", "$Kappa$", "$Lambda$", "$Mu$", "$Nu$", "$Xi$", "$Omicron$", "$Pi$", "$Rho$", "$Sigma$", "$Tau$", "$Upsilon$", "$Phi$", "$Chi$", "$Psi$", "$Omega$", "$alpha$", "$beta$", "$gamma$", "$delta$", "$epsilon$", "$zeta$", "$eta$", "$theta$", "$iota$", "$kappa$", "$lambda$", "$mu$", "$nu$", "$xi$", "$omicron$", "$pi$", "$rho$", "$sigma$", "$tau$", "$upsilon$", "$phi$", "$chi$", "$psi$", "$omega$", "$vartheta$", "$varphi$", "$overline$", "$underline$", "$partial$", "$Sum$", "$Prod$", "$infty$", "$times$"}; const char* GnuplotSymbols[] = { "{/Symbol A}", "{/Symbol B}", "{/Symbol G}", "{/Symbol D}", "{/Symbol E}", "{/Symbol Z}", "{/Symbol H}", "{/Symbol Q}", "{/Symbol I}", "{/Symbol K}", "{/Symbol L}", "{/Symbol M}", "{/Symbol N}", "{/Symbol X}", "{/Symbol O}", "{/Symbol P}", "{/Symbol R}", "{/Symbol S}", "{/Symbol T}", "{/Symbol U}", "{/Symbol F}", "{/Symbol C}", "{/Symbol Y}", "{/Symbol W}", "{/Symbol a}", "{/Symbol b}", "{/Symbol g}", "{/Symbol d}", "{/Symbol e}", "{/Symbol z}", "{/Symbol h}", "{/Symbol q}", "{/Symbol i}", "{/Symbol k}", "{/Symbol l}", "{/Symbol m}", "{/Symbol n}", "{/Symbol x}", "{/Symbol o}", "{/Symbol p}", "{/Symbol r}", "{/Symbol s}", "{/Symbol t}", "{/Symbol u}", "{/Symbol f}", "{/Symbol c}", "{/Symbol y}", "{/Symbol w}", "{/Symbol \\112}", "{/Symbol \\152}", "{/Symbol \\140}", "{/Symbol \\137}", "{/Symbol \\266}", "{/Symbol \\345}", "{/Symbol \\325}", "{/Symbol \\245}", "{/Symbol \\264}"}; const unsigned int symbolCount = 57; string replaceSymbols(string s){ for(unsigned int i=0; i &values, long start, long end, bool zeroIsReference, bool logarithmic, double epsilon){ double minValue, maxValue; if(end0)){ Range range; range.setRangeMax(maxValue*1.2); return range; }else{ const double median = (maxValue + minValue)/2; epsilon = abs(epsilon); if(maxValue-minValue<=epsilon){ if(epsilon==0){ return (minValue==0 ? Range(-1,1) : Range(0.9*minValue, 1.1*minValue)); } else{ return (abs(maxValue)0 ? 1.2*maxValue : 0)); }else if(minValue<0 && maxValue>0){ return Range(1.2*minValue, 1.2*maxValue); }else{ return Range((minValue<0 ? median-(median-minValue)*1.4 : max(0.0,median-(median-minValue)*1.4)), (maxValue>0 ? median+(maxValue-median)*1.4 : min(0.0,median+(maxValue-median)*1.4))); } } } #pragma mark - #pragma mark MultiRange #pragma mark - MultiRange::MultiRange(){ dim = 2; } MultiRange::MultiRange(unsigned int _dim){ dim = min(3u, max(2u,_dim)); } MultiRange::MultiRange(Range _xRange, Range _yRange){ dim = 2; xRange = _xRange; yRange = _yRange; } MultiRange::MultiRange(Range _xRange, Range _yRange, Range _zRange){ dim = 3; xRange = _xRange; yRange = _yRange; zRange = _zRange; } MultiRange::MultiRange(double _xRangeMin, double _xRangeMax, double _yRangeMin, double _yRangeMax){ dim = 2; xRange = Range(_xRangeMin, _xRangeMax); yRange = Range(_yRangeMin, _yRangeMax); } MultiRange::MultiRange(double _xRangeMin, double _xRangeMax, double _yRangeMin, double _yRangeMax, double _zRangeMin, double _zRangeMax){ dim = 3; xRange = Range(_xRangeMin, _xRangeMax); yRange = Range(_yRangeMin, _yRangeMax); zRange = Range(_zRangeMin, _zRangeMax); } string MultiRange::gnuplotExpression() const{ switch(dim){ case 1: return xRange.gnuplotExpression(); case 2: return xRange.gnuplotExpression() + " " + yRange.gnuplotExpression(); case 3: return xRange.gnuplotExpression() + " " + yRange.gnuplotExpression() + " " + zRange.gnuplotExpression(); default:return ""; } } string MultiRange::gnuplotCommand() const{ switch(dim){ case 1: return "set xrange " + xRange.gnuplotExpression() + ";"; case 2: return "set xrange " + xRange.gnuplotExpression() + ";\nset yrange " + yRange.gnuplotExpression() + ";"; case 3: return "set xrange " + xRange.gnuplotExpression() + ";\nset yrange " + yRange.gnuplotExpression() + ";\nset zrange " + zRange.gnuplotExpression() + ";"; default:return ""; } } void MultiRange::scaleRangeByFactor(double factor){ xRange.scaleRangeByFactor(factor); yRange.scaleRangeByFactor(factor); zRange.scaleRangeByFactor(factor); } void MultiRange::mergeWithRange(MultiRange range){ xRange.mergeWithRange(range.xRange); yRange.mergeWithRange(range.yRange); zRange.mergeWithRange(range.zRange); } #pragma mark - #pragma mark MultiLabel #pragma mark - MultiLabel::MultiLabel(){ _dim = 2; _xLabel = "x"; _yLabel = "y"; } MultiLabel::MultiLabel(string xLabel, string yLabel){ _dim = 2; _xLabel = xLabel; _yLabel = yLabel; } MultiLabel::MultiLabel(string xLabel, string yLabel, string zLabel){ _dim = 3; _xLabel = xLabel; _yLabel = yLabel; _zLabel = zLabel; } string MultiLabel::gnuplotCommand(bool interpretSymbols, bool escapeUnderscores) const{ string formattedXLabel = STPlot_newLineEscapedVersion((interpretSymbols ? replaceSymbols(_xLabel) : _xLabel)); string formattedYLabel = STPlot_newLineEscapedVersion((interpretSymbols ? replaceSymbols(_yLabel) : _yLabel)); string formattedZLabel = STPlot_newLineEscapedVersion((interpretSymbols ? replaceSymbols(_zLabel) : _zLabel)); if(escapeUnderscores){ STPlot_aux_replaceAll(formattedXLabel, "_", "\\\\_"); STPlot_aux_replaceAll(formattedYLabel, "_", "\\\\_"); STPlot_aux_replaceAll(formattedZLabel, "_", "\\\\_"); } switch(_dim){ case 2: return "set xlabel \"" + formattedXLabel + "\";\nset ylabel \"" + formattedYLabel + "\"" + (_yLabel.size() > ST_DEFAULT_ZLABEL_MAX_SIZE_BEFORE_ROTATION ? " rotate by 90;" : ";"); default: return "set xlabel \"" + formattedXLabel + "\";\nset ylabel \"" + formattedYLabel + "\";\nset zlabel \"" + formattedZLabel + "\"" + (_zLabel.size() > ST_DEFAULT_ZLABEL_MAX_SIZE_BEFORE_ROTATION ? " rotate by 90;" : ";"); } } #pragma mark - #pragma mark Contours #pragma mark - Contours::Contours(bool enable){ drawContours = enable; automatic = true; fixedCount = false; incremental = false; placement = ST_DEFAULT_CONTOUR_PLACEMENT; individualContours = ST_DEFAULT_INDIVIDUAL_CONTOURS; } Contours::Contours(const vector &_levels){ drawContours = (! _levels.empty()); automatic = false; incremental = false; levels = _levels; placement = ST_DEFAULT_CONTOUR_PLACEMENT; individualContours = ST_DEFAULT_INDIVIDUAL_CONTOURS; } Contours::Contours(double singleLevel){ drawContours = true; automatic = false; incremental = false; levels.push_back(singleLevel); placement = ST_DEFAULT_CONTOUR_PLACEMENT; individualContours = ST_DEFAULT_INDIVIDUAL_CONTOURS; } Contours::Contours(unsigned int _count){ drawContours = true; automatic = true; incremental = false; fixedCount = true; count = _count; placement = ST_DEFAULT_CONTOUR_PLACEMENT; individualContours = ST_DEFAULT_INDIVIDUAL_CONTOURS; } Contours::Contours(double _start, double _step, unsigned int _count){ drawContours = true; automatic = false; incremental = true; fixedCount = true; start = _start; step = _step; count = _count; placement = ST_DEFAULT_CONTOUR_PLACEMENT; individualContours = ST_DEFAULT_INDIVIDUAL_CONTOURS; } Contours::Contours(double _start, double _step, double _end){ drawContours = true; automatic = false; incremental = true; fixedCount = false; start = _start; step = _step; end = _end; placement = ST_DEFAULT_CONTOUR_PLACEMENT; individualContours = ST_DEFAULT_INDIVIDUAL_CONTOURS; } string Contours::contourPlacement2string(ContourPlacement placement){ switch(placement){ case ContourPlacementSurface: return "surface"; case ContourPlacementBottom: return "base"; case ContourPlacementBottomSurface: return "both"; default: return ""; } } string Contours::gnuplotCommand() const{ ostringstream contours; if(! drawContours){ return "unset contour;"; }else{ if(automatic && fixedCount){ contours << "set contour " << contourPlacement2string(placement) << "; set cntrparam levels " << count << ";"; }else if(automatic && (!fixedCount)){ contours << "set contour " << contourPlacement2string(placement) << "; set cntrparam levels auto;"; }else if((!automatic) && incremental && fixedCount){ contours << "set contour " + contourPlacement2string(placement) + "; set cntrparam levels " << count << "; set cntrparam levels incremental " << start << ", " << step << ";"; }else if((!automatic) && incremental && (!fixedCount)){ contours << "set contour " + contourPlacement2string(placement) + "; set cntrparam levels incremental " << start << ", " << step << ", " << end << ";"; }else if((!automatic) && (!incremental)){ contours << "set contour " + contourPlacement2string(placement) + "; set cntrparam levels discrete " << levels[0]; for(unsigned int i=1; i grid; STPlot_getRegularIntegerGrid(ceil(rangeMin), floor(rangeMax), grid); for(int j=0; j=0 ? "right" : "left"); }else if(ticsName=="x2tics"){ expression << (rotationM>=0 ? "left" : "right"); }else if(ticsName=="ytics"){ expression << (rotationM>=-90 && rotationM<=90 ? "right" : "left"); }else if(ticsName=="y2tics" || ticsName=="cbtics"){ expression << (rotationM>=-90 && rotationM<=90 ? "left" : "right"); } } expression << ";"; return expression.str(); } void Tics::setRotation(double angle){ rotation = angle; } #pragma mark - #pragma mark Label #pragma mark string Label::gnuplotCommand(long tag, bool interpretSymbols, int dim, const string &plotFont, bool escapeUnderscores) const{ ostringstream command; string formattedText = STPlot_newLineEscapedVersion((interpretSymbols ? replaceSymbols(text) : text)); if(escapeUnderscores) STPlot_aux_replaceAll(formattedText, "_", "\\\\_"); command << "set label " << tag << " \"" << formattedText << "\" at" << (wrtCoordinates ? " first" : " graph") << " " << x; if(dim>1) command << ", " << y; if(dim>2) command << ", " << z; switch(alignment){ case LabelAlignmentLeft: command << " left"; break; case LabelAlignmentRight: command << " right"; break; case LabelAlignmentCenter: command << " center"; break; } command << " front"; if(fontSize>=0){ command << " font '" << (plotFont=="" ? "*" : plotFont) << "," << fontSize << "'"; } return command.str(); } Label::Label(){ x=y=z=0; wrtCoordinates=false; alignment = LabelAlignmentLeft; fontSize = -1; } Label::Label(const string &_text, bool _wrtCoordinates, double _x, double _y, LabelAlignment _alignment){ text = _text; wrtCoordinates = _wrtCoordinates; x = _x; y = _y; alignment = _alignment; fontSize = -1; } Label::Label(const string &_text, bool _wrtCoordinates, double _x, double _y, double _z, LabelAlignment _alignment){ text = _text; wrtCoordinates = _wrtCoordinates; x = _x; y = _y; z = _z; alignment = _alignment; fontSize = -1; } #pragma mark - #pragma mark Plot Source #pragma mark - PlotSource::PlotSource(){ plotType = PlotTypeNone; colorType = PlotColorTypeAuto; } PlotSource::PlotSource(PlotType _plotType, string _title, string _filePath, unsigned int _index, ...){ plotType = _plotType; filePath = _filePath; index = _index; title = _title; columns.resize(columnCountForPlotType(plotType)); va_list argumentList; va_start(argumentList, _index); for(unsigned long i=0; i &_Sources, const MultiLabel &_axisLabels, const MultiRange &_Range, const string &_Title) { sources = _Sources; axisLabels = _axisLabels; range = _Range; title = _Title; showKeys = true; showColorBox = false; gridColoringType = ST_DEFAULT_GRID_COLORING_TYPE; gridSmoothingType = ST_DEFAULT_GRID_SMOOTHING_TYPE; showColorMap = true; xDistanceScale = 1; yDistanceScale = 1; xGridSize = ST_DEFAULT_ST_GNUPLOT_GRIDSIZE; yGridSize = ST_DEFAULT_ST_GNUPLOT_GRIDSIZE; colorPalette = ColorPaletteGrey; xLogarithmic = yLogarithmic = zLogarithmic = false; backgroundColor = Color::white(); } Plot::Plot( const PlotSource &_Source, const MultiLabel &_axisLabels, const MultiRange &_Range, const string &_Title) { sources.push_back(_Source); axisLabels = _axisLabels; range = _Range; title = _Title; showKeys = false; showColorBox = false; gridColoringType = ST_DEFAULT_GRID_COLORING_TYPE; gridSmoothingType = ST_DEFAULT_GRID_SMOOTHING_TYPE; showColorMap = true; xDistanceScale = 1; yDistanceScale = 1; xGridSize = ST_DEFAULT_ST_GNUPLOT_GRIDSIZE; yGridSize = ST_DEFAULT_ST_GNUPLOT_GRIDSIZE; colorPalette = ColorPaletteGrey; xLogarithmic = yLogarithmic = zLogarithmic = false; backgroundColor = Color::white(); } unsigned int Plot::compatibilityGroup(PlotType type){ switch(type){ case PlotTypeNone: return 0; case PlotType1DPoints: case PlotType1DColoredPoints: case PlotType1DVectors: case PlotType1DColoredVectors: case PlotType2DPoints: case PlotType2DColoredPoints: case PlotType2DVectors: case PlotType2DColoredVectors: case PlotType2DCurve: case PlotType2DCurvePoints: case PlotType2DGraphLinear: case PlotType2DGraphLinearPoints: case PlotType2DGraphSplines: case PlotType2DGraphSplinesPoints: case PlotType2DVariableSizedSpheres: case PlotType2DColoredVariableSizedSpheres: case PlotType2DFilledCurve: case PlotType2DFilledCurves: case PlotType2DAnalytic: return 1; case PlotType3DPoints: case PlotType3DColoredPoints: case PlotType3DVectors: case PlotType3DColoredVectors: case PlotType3DCurve: case PlotType3DCurvePoints: return 2; case PlotType1DHistogram: return 3; case PlotType1DEmpiricalDensity: return 4; case PlotType2DScatteredValueMap: return 5; case PlotType2DGridValueMap: return 6; case PlotType2DScannedValueMap: return 7; case PlotType2DColorImage: case PlotType2DValueImage: return 8; case PlotType3DScatteredSurface: return 9; case PlotType3DGridSurface: case PlotType3DScannedSurface: case PlotType3DValuedScannedSurface: return 10; default: return 11; } } bool Plot::plotTypesCompatible(PlotType type1, PlotType type2){ return (compatibilityGroup(type1) == compatibilityGroup(type2)); } string Plot::vectorHeadStyle2string(VectorHeadStyle style, double headSize){ switch(style){ case VectorHeadStyleFilledTriangle: return "head filled front size " + STPlot_makeString(headSize) + "*0.6,20"; case VectorHeadStyleFilledArrow: return "head filled front size " + STPlot_makeString(headSize) + "*0.6,20,30"; case VectorHeadStyleEmptyTriangle: return "head empty front size " + STPlot_makeString(headSize) + "*0.6,20"; case VectorHeadStyleEmptyArrow: return "head empty front size " + STPlot_makeString(headSize) + "*0.6,20,30"; case VectorHeadStyleNone: return "nohead"; default: return "default"; } } string Plot::evaluateFunctionForColumn(string trafo, unsigned long column){ string s = "$" + STPlot_makeString(column); if(trafo == ""){ return s; }else{ STPlot_aux_replaceAll(trafo, "#", s.c_str()); return trafo; } } string Plot::plotErrorDescription(PlotError error){ switch(error){ case PlotErrorNone: return "None"; case PlotErrorInvalidSyntax: return "Invalid syntax"; case PlotErrorException: return "Exception"; case PlotErrorFileReadError: return "File read error"; case PlotErrorFileWriteError: return "File write error"; case PlotErrorFileNotFoundError:return "File not found"; case PlotErrorPermissionError: return "No permission"; case PlotErrorEOF: return "EOF"; case PlotErrorPlotterNotFound: return "Gnuplot not found"; case PlotErrorUnknownError: return "Unknown error"; case PlotErrorNothingToBeDone: return "Nothing to be done"; case PlotErrorParameterError: return "Parameter error"; case PlotErrorIncompatiblePlots:return "Incompatiple plots"; default: return ""; } } string Plot::integrationMethod2string(IntegrationMethod method){ switch(method){ case IntegrationMethodForwardEuler: return "Forward Euler"; case IntegrationMethodClassicalRungeKutta: return "Runge-Kutta"; default: return "Unknown"; } } unsigned int Plot::highestPlotDimension() const{ unsigned int hpd = 0; for(unsigned int n=0; n colors; generateDiscreteColors(sources.size(), true, colors, ColorBlindnessNone); for(long s=0; s= sources.size()){ //nothing to be plotted. Display empty box with title lmargin = originX + availableWidth * (ST_DEFAULT_RELATIVE_LMARGIN); rmargin = originX + availableWidth * (1.0 - ST_DEFAULT_RELATIVE_RMARGIN); bmargin = originY + availableHeight * (ST_DEFAULT_RELATIVE_BMARGIN); tmargin = originY + availableHeight * (1.0 - ST_DEFAULT_RELATIVE_TMARGIN); pipe.write("set lmargin at screen %e\nset rmargin at screen %e\n", lmargin, rmargin); pipe.write("set bmargin at screen %e\nset tmargin at screen %e\n", bmargin, tmargin); pipe.write("unset xtics;\nunset xlabel;\nunset ytics;\nunset ylabel;\nunset border;\nset xrange [1:2];\nset yrange [1:2];\n"); pipe.write("set title \"%s%s\" offset 0, 0;\nunset key;\nunset colorbox;\nunset pm3d;\n", (tag=="" ? "" : ("("+tag+") ").c_str()), formattedTitle.c_str()); pipe.write("plot 1/0 notitle\n"); goto CLOSING; //go to end of this function to finish with some technical closing commands. return PlotErrorNone; } pipe.write("\n"); if(startSource == 0){ //color palette int redCode, greenCode, blueCode; colorPalette2PaletteCodes(colorPalette, redCode, greenCode, blueCode); pipe.write("set palette rgbformulae %i,%i,%i;\n", redCode, greenCode, blueCode); //color conversion formula needed for a bunch of PlotTypes. //(r,g,b) to int \in [0, 256^3 - 1], whereas (r,g,b) \in [0,1]^3 pipe.write("%s(r,g,b) = int(r * 255) * 65536 + int(g * 255) * 256 + int(b * 255) * 1; %smapping (r,g,b) triplets to ints, as encoded by gnuplot\n", ST_GNUPLOT_RGB_FUNCTION_NAME, ST_GNUPLOT_COMMENT_PREFIX); } //plot border pipe.write("set border\n"); //data separator pipe.write(gnuplotDataSeparatorExpression()+"\n"); //PLOT-TYPE SPECIFIC HEADER switch(sources[startSource].plotType){ case PlotTypeNone: rotationInducedScaleX = rotationInducedScaleY = 1.0; break; case PlotType1DPoints: case PlotType1DColoredPoints: case PlotType1DVectors: case PlotType1DColoredVectors: rotationInducedScaleX = rotationInducedScaleY = 1.0; //adjust labels, tics & title offsets pipe.write("set title offset 0,0\nset xlabel offset 0, 0\nset ylabel offset 0, 0\nset xtic offset 0, 0\nset ytic offset 0, 0\n"); pipe.write("unset pm3d;\n"); break; case PlotType2DPoints: case PlotType2DColoredPoints: case PlotType2DCurve: case PlotType2DCurvePoints: case PlotType2DGraphLinear: case PlotType2DGraphLinearPoints: case PlotType2DGraphSplines: case PlotType2DGraphSplinesPoints: case PlotType2DVectors: case PlotType2DColoredVectors: case PlotType2DVariableSizedSpheres: case PlotType2DColoredVariableSizedSpheres: case PlotType2DFilledCurve: case PlotType2DFilledCurves: case PlotType2DAnalytic: rotationInducedScaleX = rotationInducedScaleY = 1.0; //adjust labels, tics & title offsets pipe.write("set title offset 0,0\nset xlabel offset 0, 0\nset ylabel offset 0, 0\nset xtic offset 0, 0\nset ytic offset 0, 0\n"); pipe.write("unset pm3d;\nset autoscale;\n"); break; case PlotType3DPoints: case PlotType3DColoredPoints: case PlotType3DCurve: case PlotType3DCurvePoints: case PlotType3DVectors: case PlotType3DColoredVectors: rotationInducedScaleX = 0.73; rotationInducedScaleY = 0.7; //adjust labels, tics & title offsets pipe.write("set title offset 0, 2.6\nset xlabel offset 0, 0\nset ylabel offset 0, 0\nset xtic offset 0, 0\nset ytic offset 0, 0\n"); pipe.write("unset pm3d;\nset surface;\nunset dgrid3d;\nunset contour;\n"); pipe.write("set view 60, 30, 1, 1;\nset autoscale;\n"); break; case PlotType1DHistogram: rotationInducedScaleX = rotationInducedScaleY = 1.0; //adjust labels, tics & title offsets pipe.write("set title offset 0,0\nset xlabel offset 0, 0\nset ylabel offset 0, 0\nset xtic offset 0, 0\nset ytic offset 0, 0\n"); pipe.write("unset pm3d;\nset style data histogram;\nset style histogram clustered gap %e;\nset style fill solid border -1;\n", ST_DEFAULT_ST_GNUPLOT_HISTOGRAM_GAP); break; case PlotType1DEmpiricalDensity: rotationInducedScaleX = rotationInducedScaleY = 1.0; //adjust labels, tics & title offsets pipe.write("set title offset 0,0\nset xlabel offset 0, 0\nset ylabel offset 0, 0\nset xtic offset 0, 0\n"); pipe.write("unset pm3d;\n"); break; case PlotType2DScatteredValueMap: rotationInducedScaleX = rotationInducedScaleY = 1.0; //adjust labels, tics & title offsets pipe.write("set title offset 0,0\nset xlabel offset 0, 0.3\nset ylabel offset -1.5, 0\nset xtic offset 0, 0.3\nset ytic offset 0, 0\n"); pipe.write("%s\n",contours.gnuplotCommand().c_str()); pipe.write((showColorMap ? "set surface;\n" : "unset surface;\n")); switch(gridColoringType){ case GridColoringTypeMean: pipe.write("set pm3d map corners2color mean;\n"); break; case GridColoringTypeMax: pipe.write("set pm3d map corners2color max;\n"); break; case GridColoringTypeMin: pipe.write("set pm3d map corners2color min;\n"); break; case GridColoringTypeLL: pipe.write("set pm3d map corners2color c1;\n"); break; case GridColoringTypeUR: pipe.write("set pm3d map corners2color c4;\n"); break; } //grid interpolating (smoothing) switch(gridSmoothingType){ case GridSmoothingTypeEuclidean: pipe.write( "set dgrid3d %i,%i qnorm %e\n", xGridSize, yGridSize, ST_DEFAULT_ST_GNUPLOT_GRIDNORM); break; case GridSmoothingTypeGauss: pipe.write( "set dgrid3d %i,%i gauss %e, %e\n", xGridSize, yGridSize, xDistanceScale, yDistanceScale); break; case GridSmoothingTypeBox: pipe.write( "set dgrid3d %i,%i box %e, %e\n", xGridSize, yGridSize, xDistanceScale, yDistanceScale); break; } break; case PlotType2DGridValueMap: rotationInducedScaleX = rotationInducedScaleY = 1.0; //pipe.write("set size %e, %e\n", availableWidth * (showColorBox ? 1.1 : 1.23), availableHeight * 1.16); //pipe.write("set origin %e, %e\n", originX - 0.11 * availableWidth, originY - 0.073 * availableHeight); pipe.write("set title offset 0,0\nset xlabel offset 0, 0.3\nset ylabel offset -1.5, 0\nset xtic offset 0, 0.3\nset ytic offset 0, 0\n"); pipe.write("unset dgrid3d;\n"); eRange.scaleRangeByFactor(1.0 + ST_DEFAULT_PLOT_RANGE_TOLLERANCE); pipe.write("%s\n", contours.gnuplotCommand().c_str()); pipe.write((showColorMap ? "set surface;\n" : "unset surface;\n")); switch(gridColoringType){ case GridColoringTypeMean: pipe.write("set pm3d map corners2color mean;\n"); break; case GridColoringTypeMax: pipe.write("set pm3d map corners2color max;\n"); break; case GridColoringTypeMin: pipe.write("set pm3d map corners2color min;\n"); break; case GridColoringTypeLL: pipe.write("set pm3d map corners2color c1;\n"); break; case GridColoringTypeUR: pipe.write("set pm3d map corners2color c4;\n"); break; } break; case PlotType2DScannedValueMap: rotationInducedScaleX = rotationInducedScaleY = 1.0; //pipe.write("set size %e, %e\n", availableWidth * (showColorBox ? 1.1 : 1.23), availableHeight * 1.16); //pipe.write("set origin %e, %e\n", originX - 0.11 * availableWidth, originY - 0.073 * availableHeight); pipe.write("set title offset 0,0\nset xlabel offset 0, 0.3\nset ylabel offset -1.5, 0\nset xtic offset 0, 0.3\nset ytic offset 0, 0\n"); pipe.write("unset dgrid3d;\n"); pipe.write("%s\n",contours.gnuplotCommand().c_str()); pipe.write((showColorMap ? "set surface;\n" : "unset surface;\n")); switch(gridColoringType){ case GridColoringTypeMean: pipe.write("set pm3d map corners2color mean;\n"); break; case GridColoringTypeMax: pipe.write("set pm3d map corners2color max;\n"); break; case GridColoringTypeMin: pipe.write("set pm3d map corners2color min;\n"); break; case GridColoringTypeLL: pipe.write("set pm3d map corners2color c1;\n"); break; case GridColoringTypeUR: pipe.write("set pm3d map corners2color c4;\n"); break; } break; case PlotType2DColorImage: case PlotType2DValueImage: rotationInducedScaleX = rotationInducedScaleY = 1.0; //color conversion formulas pipe.write("div(x,y) = floor(x/y); mod(x,y) = x - floor(x/y) * y;\n"); //int to (r,g,b) \in [0,1]^3 pipe.write("%s(v) = div(v, 65536)/255.0; %sred component of inverse of %s()\n", ST_GNUPLOT_INVERSE_RGB_FUNCTION_NAME_RED,ST_GNUPLOT_COMMENT_PREFIX,ST_GNUPLOT_RGB_FUNCTION_NAME); pipe.write("%s(v) = div(mod(v, 65536), 256)/255.0; %sgreen component of inverse of %s()\n", ST_GNUPLOT_INVERSE_RGB_FUNCTION_NAME_GREEN,ST_GNUPLOT_COMMENT_PREFIX,ST_GNUPLOT_RGB_FUNCTION_NAME); pipe.write("%s(v) = mod(v, 256)/255.0; %sblue component of inverse of %s()\n", ST_GNUPLOT_INVERSE_RGB_FUNCTION_NAME_BLUE,ST_GNUPLOT_COMMENT_PREFIX,ST_GNUPLOT_RGB_FUNCTION_NAME); //adjust labels, tics & title offsets pipe.write("set title offset 0,0\nset xlabel offset 0, 0\nset ylabel offset 0, 0\nset xtic offset 0, 0\nset ytic offset 0, 0\n"); //steps and ranges xStep = (sources[startSource].xCount<=1 ? 0 : (sources[startSource].maxX - sources[startSource].minX)/(sources[startSource].xCount - 1.0)); yStep = (sources[startSource].yCount<=1 ? 0 : (sources[startSource].maxY - sources[startSource].minY)/(sources[startSource].yCount - 1.0)); eRange.mergeWithRange(MultiRange( sources[startSource].minX - 0.5*xStep, sources[startSource].maxX + 0.5*xStep, sources[startSource].minY - 0.5*yStep, sources[startSource].maxY + 0.5*yStep)); break; case PlotType3DScatteredSurface: rotationInducedScaleX = 0.73; rotationInducedScaleY = 0.7; //adjust labels, tics & title offsets pipe.write("set title offset 0,2.6\nset xlabel offset 0, 0\nset ylabel offset 0, 0\nset xtic offset 0, 0\nset ytic offset 0, 0\n"); pipe.write("unset hidden3d;\nset surface;\nset view 60, 30, 1, 1;\n"); pipe.write("%s\n",contours.gnuplotCommand().c_str()); switch(gridColoringType){ case GridColoringTypeMean: pipe.write("set pm3d at s corners2color mean;\n"); break; case GridColoringTypeMax: pipe.write("set pm3d at s corners2color max;\n"); break; case GridColoringTypeMin: pipe.write("set pm3d at s corners2color min;\n"); break; case GridColoringTypeLL: pipe.write("set pm3d at s corners2color c1;\n"); break; case GridColoringTypeUR: pipe.write("set pm3d at s corners2color c4;\n"); break; } //grid interpolating (smoothing) switch(gridSmoothingType){ case GridSmoothingTypeEuclidean: pipe.write( "set dgrid3d %i,%i qnorm %e\n", xGridSize, yGridSize, ST_DEFAULT_ST_GNUPLOT_GRIDNORM); break; case GridSmoothingTypeGauss: pipe.write( "set dgrid3d %i,%i gauss %e, %e\n", xGridSize, yGridSize, xDistanceScale, yDistanceScale); break; case GridSmoothingTypeBox: pipe.write( "set dgrid3d %i,%i box %e, %e\n", xGridSize, yGridSize, xDistanceScale, yDistanceScale); break; } break; case PlotType3DGridSurface: case PlotType3DScannedSurface: case PlotType3DValuedScannedSurface: rotationInducedScaleX = 0.73; rotationInducedScaleY = 0.7; //adjust labels, tics & title offsets pipe.write("set title offset 0,2.6\nset xlabel offset 0, 0\nset ylabel offset 0, 0\nset xtic offset 0, 0\nset ytic offset 0, 0\n"); pipe.write("unset dgrid3d; unset hidden3d;\nset surface;\nset view 60, 30, 1, 1;\n"); pipe.write("%s\n",contours.gnuplotCommand().c_str()); switch(gridColoringType){ case GridColoringTypeMean: pipe.write("set pm3d at s corners2color mean;\n"); break; case GridColoringTypeMax: pipe.write("set pm3d at s corners2color max;\n"); break; case GridColoringTypeMin: pipe.write("set pm3d at s corners2color min;\n"); break; case GridColoringTypeLL: pipe.write("set pm3d at s corners2color c1;\n"); break; case GridColoringTypeUR: pipe.write("set pm3d at s corners2color c4;\n"); break; } break; default: rotationInducedScaleX = rotationInducedScaleY = 1.0; } //set origin and size of plot by means of margins //only write this for the first source if(startSource == 0){ double colorBoxInducedMarginX = (showColorBox ? ST_DEFAULT_RELATIVE_COLORBOX_WIDTH + ST_DEFAULT_RELATIVE_COLORBOX_DIST + ST_DEFAULT_RELATIVE_COLORBOX_RMARGIN : 0.0); double rotationInducedMarginX = (1.0 - ST_DEFAULT_RELATIVE_LMARGIN - ST_DEFAULT_RELATIVE_RMARGIN) * (1.0 - rotationInducedScaleX); double rotationInducedMarginY = (1.0 - ST_DEFAULT_RELATIVE_TMARGIN - ST_DEFAULT_RELATIVE_BMARGIN) * (1.0 - rotationInducedScaleY); lmargin = (originX + resizeNonPlotAreaWidthByFactor*availableWidth * (ST_DEFAULT_RELATIVE_LMARGIN + rotationInducedMarginX/2.0)); rmargin = (originX + availableWidth * (1.0 - resizeNonPlotAreaWidthByFactor*(ST_DEFAULT_RELATIVE_RMARGIN + rotationInducedMarginX/2.0 + colorBoxInducedMarginX))); bmargin = (originY + resizeNonPlotAreaHeightByFactor*availableHeight * (ST_DEFAULT_RELATIVE_BMARGIN + rotationInducedMarginY/2.0)); tmargin = (originY + availableHeight * (1.0 - resizeNonPlotAreaHeightByFactor*(ST_DEFAULT_RELATIVE_TMARGIN + rotationInducedMarginY/2.0))); pipe.write("set lmargin at screen %e\nset rmargin at screen %e\n", lmargin, rmargin); pipe.write("set bmargin at screen %e\nset tmargin at screen %e\n", bmargin, tmargin); //color box if(showColorBox){ pipe.write("set colorbox vertical user origin %e, %e size %e, %e;\n", originX + availableWidth * (1.0 - resizeNonPlotAreaWidthByFactor*(ST_DEFAULT_RELATIVE_RMARGIN + ST_DEFAULT_RELATIVE_COLORBOX_WIDTH + ST_DEFAULT_RELATIVE_COLORBOX_RMARGIN)), bmargin, resizeNonPlotAreaWidthByFactor * availableWidth * ST_DEFAULT_RELATIVE_COLORBOX_WIDTH, tmargin - bmargin); }else{ pipe.write("unset colorbox;\n"); } } //Only to be set for the first plot round to avoid duplicate (and thus fuzzy) printings if(startSource == 0){ pipe.write("set title \"%s%s\";\n", (tag=="" ? "" : ("("+tag+") ").c_str()), formattedTitle.c_str()); pipe.write("%s\n", axisLabels.gnuplotCommand(interpretSymbols, escapeUnderscoresInTicsAndLabels).c_str()); //axis labels string formattedCBLabel = STPlot_newLineEscapedVersion((interpretSymbols ? replaceSymbols(CBLabel) : CBLabel)); if(escapeUnderscoresInTicsAndLabels) STPlot_aux_replaceAll(formattedCBLabel, "_", "\\\\_"); pipe.write("set cblabel \"%s\";\n", formattedCBLabel.c_str()); //ColorBox label pipe.write((showKeys ? "set key;\n" : "unset key;\n")); //keys pipe.write("%s\n", CBTics.gnuplotCommand("cbtics", interpretSymbols, escapeUnderscoresInTicsAndLabels, resizeTicsByFactor).c_str()); //custom labels for(long l=0; l= startSource + 1){ if(!plotTypesCompatible(sources[s].plotType, sources[s-1].plotType)){ pipe.write("%sChanging plot style within same plot\n", ST_GNUPLOT_COMMENT_PREFIX); if(commandJustAfterFirstPlot != ""){ pipe.write("%s%s\n%s\n", ST_GNUPLOT_COMMENT_PREFIX, "Calling some closing commands after first plot", commandJustAfterFirstPlot.c_str()); } writeCommandToPipe( pipe, originX, originY, availableWidth, availableHeight, resizeNonPlotAreaWidthByFactor, resizeNonPlotAreaHeightByFactor, resizeTicsByFactor, windowFont, interpretSymbols, s, tag, escapeUnderscoresInTitle, escapeUnderscoresInTicsAndLabels, "", nextAvailLabelTag); break; }else{ pipe.write(ST_DEFAULT_WRITE_COMPOSITE_PLOTS_MULTILINE ? ",\\\n\t\t" : ", "); } } if(sources[s].title == ""){ sourceTitle = "notitle"; }else{ sourceTitle = STPlot_newLineEscapedVersion(interpretSymbols ? replaceSymbols(sources[s].title) : sources[s].title); if(escapeUnderscoresInTicsAndLabels){ STPlot_aux_replaceAll(sourceTitle,"_","\\\\_"); } sourceTitle = "title \"" + sourceTitle + "\""; } switch(sources[s].plotType){ case PlotTypeNone: //nothing to plot here break; case PlotType1DPoints: pipe.write( "'%s' index %i using (%s):(0) with points %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType1DColoredPoints: pipe.write( "'%s' index %i using (%s):(0):(%s($%i, $%i, $%i)) with points %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), ST_GNUPLOT_RGB_FUNCTION_NAME, sources[s].columns[1], sources[s].columns[2], sources[s].columns[3], sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType1DVectors: pipe.write( "'%s' index %i using (%s):(0):%i:(0) with vectors %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), sources[s].columns[1], vectorHeadStyle2string(sources[s].vectorHeadStyle, sources[s].vectorHeadSize).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType1DColoredVectors: pipe.write( "'%s' index %i using (%s):(0):%i:(0):(%s($%i, $%i, $%i)) with vectors %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), sources[s].columns[1], ST_GNUPLOT_RGB_FUNCTION_NAME, sources[s].columns[2], sources[s].columns[3], sources[s].columns[4], vectorHeadStyle2string(sources[s].vectorHeadStyle, sources[s].vectorHeadSize).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DPoints: pipe.write( "'%s' index %i using (%s):(%s) with points %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DColoredPoints: pipe.write( "'%s' index %i using (%s):(%s):(%s($%i, $%i, $%i)) with points %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), ST_GNUPLOT_RGB_FUNCTION_NAME, sources[s].columns[2], sources[s].columns[3], sources[s].columns[4], sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DVectors: pipe.write( "'%s' index %i using (%s):(%s):%i:%i with vectors %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].columns[2], sources[s].columns[3], vectorHeadStyle2string(sources[s].vectorHeadStyle, sources[s].vectorHeadSize).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DColoredVectors: pipe.write( "'%s' index %i using (%s):(%s):%i:%i:(%s($%i, $%i, $%i)) with vectors %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].columns[2], sources[s].columns[3], ST_GNUPLOT_RGB_FUNCTION_NAME, sources[s].columns[4], sources[s].columns[5], sources[s].columns[6], vectorHeadStyle2string(sources[s].vectorHeadStyle, sources[s].vectorHeadSize).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DVariableSizedSpheres: pipe.write( "'%s' index %i using (%s):(%s):%i with circles fillstyle solid %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].columns[2], sources[s].gnuplotColorExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DColoredVariableSizedSpheres: pipe.write( "'%s' index %i using (%s):(%s):%i:(rgb($%i,$%i,$%i)) with circles fillstyle solid %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].columns[2], sources[s].columns[3], sources[s].columns[4], sources[s].columns[5], sources[s].gnuplotColorExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DFilledCurve: pipe.write( "'%s' index %i using (%s):(%s):(%g) with filledcurves %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].fillReference, sources[s].gnuplotFilledCurveStyleExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DFilledCurves: pipe.write( "'%s' index %i using (%s):(%s):(%s) with filledcurves %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sources[s].gnuplotFilledCurveStyleExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DGraphLinear: pipe.write( "'%s' index %i using (%s):(%s) with lines %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DGraphLinearPoints: pipe.write( "'%s' index %i using (%s):(%s) with linespoints %s %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DCurve: pipe.write( "'%s' index %i using (%s):(%s) with lines %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DCurvePoints: pipe.write( "'%s' index %i using (%s):(%s) with linespoints %s %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DGraphSplines: pipe.write( "'%s' index %i using (%s):(%s) smooth csplines %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DGraphSplinesPoints: pipe.write( "'%s' index %i using (%s):(%s) smooth csplines %s %s %s notitle, '' index %i using (%s):(%s) with points %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DAnalytic: pipe.write( "%s %s %s %s %s", sources[s].filePath.c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType3DPoints: pipe.write( "'%s' index %i using (%s):(%s):(%s) with points %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType3DColoredPoints: pipe.write( "'%s' index %i using (%s):(%s):(%s):(%s($%i, $%i, $%i)) with points %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), ST_GNUPLOT_RGB_FUNCTION_NAME, sources[s].columns[3], sources[s].columns[4], sources[s].columns[5], sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType3DVectors: pipe.write( "'%s' index %i using (%s):(%s):(%s):%i:i:i with vectors %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sources[s].columns[3], sources[s].columns[4], sources[s].columns[5], vectorHeadStyle2string(sources[s].vectorHeadStyle, sources[s].vectorHeadSize).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType3DColoredVectors: pipe.write( "'%s' index %i using (%s):(%s):(%s):%i:%i:%i:(%s($%i, $%i, $%i)) with vectors %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sources[s].columns[3], sources[s].columns[4], sources[s].columns[5], ST_GNUPLOT_RGB_FUNCTION_NAME, sources[s].columns[6], sources[s].columns[7], sources[s].columns[8], vectorHeadStyle2string(sources[s].vectorHeadStyle, sources[s].vectorHeadSize).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType3DCurve: pipe.write( "'%s' index %i using (%s):(%s):(%s) with lines %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sourceTitle.c_str()); break; case PlotType3DCurvePoints: pipe.write( "'%s' index %i using (%s):(%s):(%s) with linespoints %s %s %s %s %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sources[s].gnuplotLineWidthExpression().c_str(), sources[s].gnuplotColorExpression().c_str(), sources[s].gnuplotLineTypeExpression().c_str(), sources[s].gnuplotPointTypeExpression().c_str(), sources[s].gnuplotPointSizeExpression().c_str(), sourceTitle.c_str()); break; case PlotType1DHistogram: pipe.write( "'%s' index %i using (%s):xticlabels(%i) %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].columns[0], sources[s].gnuplotColorExpression().c_str(), sourceTitle.c_str()); break; case PlotType1DEmpiricalDensity: pipe.write( "'%s' index %i using (%s):(%s) smooth unique with boxes fillstyle solid %s %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), sources[s].gnuplotColorExpression().c_str(), sourceTitle.c_str()); break; case PlotType2DScatteredValueMap: pipe.write( "'%s' index %i using (%s):(%s):(%s) %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sourceTitle.c_str()); break; case PlotType2DGridValueMap: switch(gridColoringType){ case GridColoringTypeMean: pipe.write( "'%s' index %i using ($1*%e+%e):($2*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case GridColoringTypeMax: pipe.write( "'%s' index %i using ($1*%e+%e):($2*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case GridColoringTypeMin: pipe.write( "'%s' index %i using ($1*%e+%e):($2*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case GridColoringTypeLL: pipe.write( "'%s' index %i using (($1-0.5)*%e+%e):(($2-0.5)*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case GridColoringTypeUR: pipe.write( "'%s' index %i using (($1+0.5)*%e+%e):(($2+0.5)*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; } break; case PlotType2DScannedValueMap: pipe.write( "'%s' index %i using (%s):(%s):(%s) %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sourceTitle.c_str()); break; case PlotType2DColorImage: if((sources[s].xCount <= 1) || (sources[s].yCount <= 1)){ error = PlotErrorNothingToBeDone; break; } xStep = (sources[s].maxX - sources[s].minX)/(sources[s].xCount - 1.0); yStep = (sources[s].maxY - sources[s].minY)/(sources[s].yCount - 1.0); pipe.write( "'%s' index %i using (%e+%e*$1):(%e+%e*$2):(255 * %s($3)):(255 * %s($3)):(255 * %s($3)) matrix with rgbimage %s\n", sources[s].filePath.c_str(), sources[s].index, sources[s].minX, xStep, sources[s].minY, yStep, ST_GNUPLOT_INVERSE_RGB_FUNCTION_NAME_RED, ST_GNUPLOT_INVERSE_RGB_FUNCTION_NAME_GREEN, ST_GNUPLOT_INVERSE_RGB_FUNCTION_NAME_BLUE, sourceTitle.c_str()); break; case PlotType2DValueImage: if((sources[s].xCount <= 1) || (sources[s].yCount <= 1)){ error = PlotErrorNothingToBeDone; break; } xStep = (sources[s].maxX - sources[s].minX)/(sources[s].xCount - 1.0); yStep = (sources[s].maxY - sources[s].minY)/(sources[s].yCount - 1.0); pipe.write( "'%s' index %i using (%e+%e*$1):(%e+%e*$2):(%s) matrix with image %s\n", sources[s].filePath.c_str(), sources[s].index, sources[s].minX, xStep, sources[s].minY, yStep, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case PlotType3DScatteredSurface: pipe.write( "'%s' index %i using (%s):(%s):(%s) with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].columns[0], sources[s].columns[1], evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sourceTitle.c_str()); break; case PlotType3DGridSurface: switch(gridColoringType){ case GridColoringTypeMean: pipe.write( "'%s' index %i using ($1*%e+%e):($2*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case GridColoringTypeMax: pipe.write( "'%s' index %i using ($1*%e+%e):($2*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case GridColoringTypeMin: pipe.write( "'%s' index %i using ($1*%e+%e):($2*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case GridColoringTypeLL: pipe.write( "'%s' index %i using (($1-0.5)*%e+%e):(($2-0.5)*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; case GridColoringTypeUR: pipe.write( "'%s' index %i using (($1+0.5)*%e+%e):(($2+0.5)*%e+%e):(%s) matrix with pm3d %s", sources[s].filePath.c_str(), sources[s].index, sources[s].xStep, sources[s].xStart, sources[s].yStep, sources[s].yStart, evaluateFunctionForColumn(sources[s].zTransformationFunction, 3).c_str(), sourceTitle.c_str()); break; } break; case PlotType3DScannedSurface: pipe.write( "'%s' index %i using (%s):(%s):(%s) with pm3d %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), sourceTitle.c_str()); break; case PlotType3DValuedScannedSurface: pipe.write( "'%s' index %i using (%s):(%s):(%s):(%s) with pm3d %s", sources[s].filePath.c_str(), sources[s].index, evaluateFunctionForColumn(sources[s].xTransformationFunction, sources[s].columns[0]).c_str(), evaluateFunctionForColumn(sources[s].yTransformationFunction, sources[s].columns[1]).c_str(), evaluateFunctionForColumn(sources[s].zTransformationFunction, sources[s].columns[2]).c_str(), evaluateFunctionForColumn(sources[s].wTransformationFunction, sources[s].columns[3]).c_str(), sourceTitle.c_str()); break; } } // force appearance of color box if(showColorBox && (!hasPlotWhichShowsColorbox())){ pipe.write(ST_DEFAULT_WRITE_COMPOSITE_PLOTS_MULTILINE ? ",\\\n\t\t" : ", "); pipe.write("1/0 lc palette notitle %s Needed to make color box appear", ST_GNUPLOT_COMMENT_PREFIX); } CLOSING: pipe.write("\n"); if(commandJustAfterFirstPlot != ""){ pipe.write("\n%s%s\n%s\n\n", ST_GNUPLOT_COMMENT_PREFIX, "Calling some closing commands after first plot", commandJustAfterFirstPlot.c_str()); } if(!backgroundColor.isWhite()){ //If background object is not removed, it will be drawn multiple times with any subsequent plots pipe.write("unset object %i;\n",ST_DEFAULT_BACKGROUND_TAG); } if(error == PlotErrorIncompatiblePlots){ pipe.write("%sWarning: Incompatible plots requested. Plot incomplete.\n", ST_GNUPLOT_COMMENT_PREFIX); } pipe.write("\n"); return error; } #pragma mark - #pragma mark MultiPlot #pragma mark - //constructors MultiPlot::MultiPlot(){ setDefaults(); setOutputToTerminal(ST_DEFAULT_TERM,ST_DEFAULT_TERMINAL_WIDTH,ST_DEFAULT_TERMINAL_HEIGHT,""); pipe.open(); } MultiPlot::MultiPlot(const string &gnuplotPath){ setDefaults(); setOutputToTerminal(ST_DEFAULT_TERM,ST_DEFAULT_TERMINAL_WIDTH,ST_DEFAULT_TERMINAL_HEIGHT,""); if(gnuplotPath==""){ pipe.open(); } else{ pipe.open(gnuplotPath); } } MultiPlot::MultiPlot(const Plot &_plot, const string &_title){ setDefaults(); setOutputToTerminal(ST_DEFAULT_TERM,ST_DEFAULT_TERMINAL_WIDTH,ST_DEFAULT_TERMINAL_HEIGHT, _title); plots.push_back(_plot); pipe.open(); } MultiPlot::MultiPlot(const Plot &_plot, const string &_title, const string &gnuplotPath){ setDefaults(); setOutputToTerminal(ST_DEFAULT_TERM, ST_DEFAULT_TERMINAL_WIDTH,ST_DEFAULT_TERMINAL_HEIGHT, _title); plots.push_back(_plot); if(gnuplotPath==""){ pipe.open(); } else{ pipe.open(gnuplotPath); } } MultiPlot::MultiPlot(const vector &_plots, string _title){ setDefaults(); setOutputToTerminal(ST_DEFAULT_TERM,ST_DEFAULT_TERMINAL_WIDTH,ST_DEFAULT_TERMINAL_HEIGHT,_title); plots = _plots; pipe.open(); } MultiPlot::MultiPlot(const vector &_plots, string _title, string gnuplotPath){ setDefaults(); setOutputToTerminal(ST_DEFAULT_TERM,ST_DEFAULT_TERMINAL_WIDTH,ST_DEFAULT_TERMINAL_HEIGHT,_title); plots = _plots; pipe.open(gnuplotPath); } void MultiPlot::setDefaults(){ setAspectRatio(ST_DEFAULT_MULTIPLOT_RATIO, ST_DEFAULT_MIN_MULTIPLOT_RATIO, ST_DEFAULT_MAX_MULTIPLOT_RATIO); interpretSymbols = true; tagPlots = false; onlyResizePlotAreaInResizedPlots = false; escapeUnderscoresInFootnote = escapeUnderscoresInTitle = false; plotHSpacing = ST_DEFAULT_PLOT_H_SPACING; plotVSpacing = ST_DEFAULT_PLOT_V_SPACING; } void MultiPlot::setOutputToTerminal(const string &_terminalName, double width, double height, const string &_title){ terminalName = _terminalName; filePath = ""; plotWidth = width; plotHeight = height; defaultPlotWidth = ST_DEFAULT_TERMINAL_WIDTH; defaultPlotHeight = ST_DEFAULT_TERMINAL_HEIGHT; terminalTitle = _title; outputType = PlotOutputTypeTerminal; } void MultiPlot::setOutputToPDF(const string &_filePath, double width, double height){ filePath = _filePath; terminalName = ""; plotWidth = width; plotHeight = height; defaultPlotWidth = ST_DEFAULT_PDF_WIDTH; defaultPlotHeight = ST_DEFAULT_PDF_HEIGHT; terminalTitle = ""; outputType = PlotOutputTypeFilePDF; } void MultiPlot::setOutputToEPS(const string &_filePath, double width, double height){ filePath = _filePath; terminalName = ""; plotWidth = width; plotHeight = height; defaultPlotWidth = ST_DEFAULT_EPS_WIDTH; defaultPlotHeight = ST_DEFAULT_EPS_HEIGHT; terminalTitle = ""; outputType = PlotOutputTypeFileEPS; } void MultiPlot::setOutputToSVG(const string &_filePath, double width, double height){ filePath = _filePath; terminalName = ""; plotWidth = width; plotHeight = height; defaultPlotWidth = ST_DEFAULT_SVG_WIDTH; defaultPlotHeight = ST_DEFAULT_SVG_HEIGHT; terminalTitle = ""; outputType = PlotOutputTypeFileSVG; } void MultiPlot::setPlotSpacing(double Hspacing, double Vspacing){ plotHSpacing = Hspacing; plotVSpacing = Vspacing; } bool MultiPlot::program_exists(const string &name){ string path; if(!getProgramPath(name,path)) return false; return STPlot_aux_fileExists(path); } void MultiPlot::getOutputCommands( double suggestedWidth, double suggestedHeight, long sessionNumber, string &outputCommand, string &closingShellCommand, string &terminalCommand, string &terminalTitleCommand, double &resizeTicsByFactor) const{ const bool epstopdf = program_exists("epstopdf"); const bool eps2eps = program_exists("eps2eps"); const bool ps2pdf = program_exists("ps2pdf"); const bool pstopdf = program_exists("pstopdf"); ostringstream terminalCommandSS; double lineDownscaling, fontDownscaling, width, height; // make sure total plot dimensions do not exceed certain bounds (rendering problems appear otherwise) // if we need to cut down the size, we should also downscale fonts, lines, dashes and tics if(outputType==PlotOutputTypeFilePDF || outputType==PlotOutputTypeFileEPS){ width = min(suggestedWidth, ST_DEFAULT_MAX_EPS_WIDTH); height = min(suggestedHeight, ST_DEFAULT_MAX_EPS_HEIGHT); // fix aspect ratios if(width/suggestedWidth < height/suggestedHeight){ height = suggestedHeight * (width/suggestedWidth); }else{ width = suggestedWidth * (height/suggestedHeight); } lineDownscaling = fontDownscaling = min(width/suggestedWidth, height/suggestedHeight); resizeTicsByFactor = lineDownscaling; }else{ width = suggestedWidth; height = suggestedHeight; lineDownscaling = fontDownscaling = resizeTicsByFactor = 1; } if(outputType==PlotOutputTypeTerminal){ outputCommand = "unset output"; if(terminalName=="aqua") outputCommand += " fontsize " + STPlot_makeString(ST_DEFAULT_AQUATERM_FONTSIZE); closingShellCommand = ""; terminalCommandSS << terminalName << " " << sessionNumber << " size " << width << ", " << height; terminalTitleCommand = "title '" + terminalTitle + "'"; }else if(outputType==PlotOutputTypeFilePDF && (epstopdf || pstopdf || ps2pdf)){ terminalCommandSS << "postscript eps enhanced color dashed size " << width << "in, " << height << "in fontscale " << fontDownscaling << " lw " << lineDownscaling << " dashlength " << lineDownscaling; terminalTitleCommand = ""; if(hasPlotWithEPSBlurProblems() && ST_CORRECT_EPS_BLURS && eps2eps){ string temp_eps1 = filePath + "_temp1.eps"; string temp_eps2 = filePath + "_temp2.eps"; closingShellCommand = string(ST_GNUPLOT_COMMENT_PREFIX) + " This command is needed to correct EPS rendering errors with some viewers\n" + "!eps2eps -dNOCACHE '"+temp_eps1+"' '" + temp_eps2 + "'\n" ST_GNUPLOT_COMMENT_PREFIX + " Transform EPS to PDF\n" + (epstopdf ? "!epstopdf --filter < '" + temp_eps2 + "' > " : (ps2pdf ? "!ps2pdf -dEPSCrop '"+temp_eps2+"' " : "!pstopdf '" + temp_eps2 + "' -o ")) + "'" + filePath + "'\n" ST_GNUPLOT_COMMENT_PREFIX + " Remove temporary EPS files\n" + "!rm '" + temp_eps1 + "' '" + temp_eps2 + "'\n"; outputCommand = "set output '" + temp_eps1 + "'"; }else{ outputCommand = string("set output '| ") + (epstopdf ? "epstopdf --filter > " : (ps2pdf ? "ps2pdf -dEPSCrop - " : "pstopdf -i -o ")) +"\"" + filePath + "\"'"; closingShellCommand = ""; } }else if((outputType==PlotOutputTypeFileEPS) || (outputType==PlotOutputTypeFilePDF && (!epstopdf) && (!pstopdf) && (!ps2pdf))){ string correct_filePath = filePath; if(outputType==PlotOutputTypeFilePDF){ // requested to create PDF, but cannot do it, so change file extension to ps STPlot_aux_correctFileExtensionFromTo(correct_filePath,"pdf","ps"); } terminalCommandSS <<"postscript eps enhanced color dashed size " << width << "in, " << height << "in fontscale " << fontDownscaling << " lw " << lineDownscaling << " dashlength " << lineDownscaling; terminalTitleCommand = ""; if(hasPlotWithEPSBlurProblems() && ST_CORRECT_EPS_BLURS && eps2eps){ string temp_eps = correct_filePath + "_temp.eps"; closingShellCommand = string(ST_GNUPLOT_COMMENT_PREFIX) + " This command is needed to correct EPS rendering errors with some viewers\n" + "!eps2eps -dNOCACHE '" + temp_eps + "' '" + correct_filePath + "'\n" ST_GNUPLOT_COMMENT_PREFIX + " Remove temporary EPS file\n" + "!rm '" + temp_eps + "'\n"; outputCommand = "set output '" + temp_eps + "'"; }else{ outputCommand = "set output '" + correct_filePath + "'"; closingShellCommand = ""; } }else if(outputType==PlotOutputTypeFileSVG){ terminalCommandSS << "svg size " << width << ", " << height << " enhanced dashed fsize " + STPlot_makeString(ST_DEFAULT_SVG_FONTSIZE); terminalTitleCommand = ""; closingShellCommand = "unset output"; outputCommand = "set output '" + filePath + "'"; } terminalCommand = terminalCommandSS.str(); return; } bool MultiPlot::outputIsToFile() const{ return (outputType==PlotOutputTypeFilePDF) || (outputType==PlotOutputTypeFileEPS) || (outputType==PlotOutputTypeFileSVG); } bool MultiPlot::outputIsPS() const{ return (outputType==PlotOutputTypeFilePDF) || (outputType==PlotOutputTypeFileEPS); } template void explodeString(SCONTAINER &parts, const string &haystack, const string &separator, int maxPartsCount){ parts.clear(); if(haystack=="") return; int previousPos = 0, nextPos, separatorLen = separator.length(); while(((nextPos = haystack.find(separator, previousPos)) != string::npos) && ((maxPartsCount>1) || (maxPartsCount<0))){ parts.push_back(haystack.substr(previousPos, nextPos - previousPos)); previousPos = nextPos + separatorLen; -- maxPartsCount; } parts.push_back(haystack.substr(previousPos)); } void MultiPlot::renderFootnote( string &openingCommand, string &closingCommand, double &footnoteScreenHeight, double plotHeightWithoutFootnote, double &plotHeightWithFootnote, long &nextAvailLabelTag){ const string formattedFootnote = (escapeUnderscoresInFootnote ? STPlot_aux_get_replaceAll(footnote,"_","\\\\_") : footnote); if(formattedFootnote == ""){ footnoteScreenHeight = 0; openingCommand == ""; closingCommand == ""; plotHeightWithFootnote = plotHeightWithoutFootnote; return; } vector footnoteLines; explodeString(footnoteLines, formattedFootnote, "\n", -1); double footnoteHeight = (formattedFootnote == "" ? 0 : getLabelHeight(formattedFootnote)); footnoteScreenHeight = footnoteHeight / (plotHeightWithoutFootnote + footnoteHeight); plotHeightWithFootnote = plotHeightWithoutFootnote + footnoteHeight; double screenHeightPerLine = footnoteScreenHeight/footnoteLines.size(); ostringstream openingCommandStream, closingCommandStream; for(unsigned int i=0; i 1) || outputIsPS()){ //some problems occur with margins in EPS files in non-multiplot mode getFactorization(plots.size(), optimalRatio, minRatio, maxRatio, rows, columns); const double totalWidth = columns*plotWidth + (columns+1)*plotHSpacing*plotWidth; renderFootnote( footnoteOpeningCommand, footnoteClosingCommand, footnoteScreenHeight, plotHeight * rows + (rows+1)*plotVSpacing*plotHeight, totalPlotHeightWithFootnote, nextAvailLabelTag); getOutputCommands(totalWidth, totalPlotHeightWithFootnote, sessionNumber, outputCommand, closingShellCommand, terminalCommand, terminalTitleCommand, resizeTicsByFactor); //prepare plot window and footnote pipe.write( "set term %s %s %s;\nset size 1,1;\n%s;\n", terminalCommand.c_str(), terminalTitleCommand.c_str(), (font == "" ? "" : (" fname '" + font + "'").c_str()), outputCommand.c_str()); pipe.write("set multiplot\n%s\n",(outputType==PlotOutputTypeFileSVG ? "" : "clear\n")); // for EPS this needs to come after multiplot, but for SVGs that generates an error //pipe.write("set lmargin %i; set rmargin %i\n\n", ST_DEFAULT_MULTIPLOT_LMARGIN, ST_DEFAULT_MULTIPLOT_RMARGIN); if(footnote != ""){ pipe.write("%s%s\n%s\n\n", ST_GNUPLOT_COMMENT_PREFIX, "Footnote", footnoteOpeningCommand.c_str()); commandJustAfterFirstPlot = footnoteClosingCommand; }else{ footnoteScreenHeight = 0; } const double relativePlotHSpacing = plotHSpacing*plotWidth/totalWidth; const double relativePlotVSpacing = plotVSpacing*plotHeight/totalPlotHeightWithFootnote; const double relativePlotWidth = plotWidth/totalWidth; const double relativePlotHeight = plotHeight/totalPlotHeightWithFootnote; for(unsigned int i=0; i1) ? generatePlotTag(i) : ""), escapeUnderscoresInTitle, escapeUnderscoresInTicsAndLabels, (i == 0 ? commandJustAfterFirstPlot : ""), nextAvailLabelTag); if(error != PlotErrorNone){ pipe.transformToPipe(); return error; } } pipe.write("\nunset multiplot;\n"); pipe.write("reset;\n\n\n"); }else{ //Only a single plot. Do not use multiplot modus, since it needlessly prevents terminal-user interaction const double totalWidth = plotWidth + 2*plotHSpacing*plotWidth; renderFootnote( footnoteOpeningCommand, footnoteClosingCommand, footnoteScreenHeight, plotHeight+2*plotVSpacing*plotHeight, totalPlotHeightWithFootnote, nextAvailLabelTag); getOutputCommands(totalWidth, totalPlotHeightWithFootnote, sessionNumber, outputCommand, closingShellCommand, terminalCommand, terminalTitleCommand, resizeTicsByFactor); pipe.write( "set term %s %s %s;\n%s;\n%s\n", terminalCommand.c_str(), terminalTitleCommand.c_str(), (font == "" ? "" : (" fname '" + font + "'").c_str()), outputCommand.c_str(), (outputType==PlotOutputTypeFileSVG ? "" : "clear\n")); //footnote if(footnote != ""){ pipe.write("%s%s\n%s\n\n", ST_GNUPLOT_COMMENT_PREFIX, "Footnote", footnoteOpeningCommand.c_str()); commandJustAfterFirstPlot = footnoteClosingCommand; }else{ footnoteScreenHeight = 0; } error = plots[0].writeCommandToPipe(pipe, plotHSpacing*plotWidth/totalWidth, footnoteScreenHeight + plotVSpacing*plotHeight/totalPlotHeightWithFootnote, plotWidth/totalWidth, plotHeight/totalPlotHeightWithFootnote, (onlyResizePlotAreaInResizedPlots ? defaultPlotWidth/plotWidth : 1.0), (onlyResizePlotAreaInResizedPlots ? defaultPlotHeight/plotHeight : 1.0), resizeTicsByFactor, font, interpretSymbols, 0, "", escapeUnderscoresInTitle, escapeUnderscoresInTicsAndLabels, commandJustAfterFirstPlot, nextAvailLabelTag); pipe.write("\nreset;\n\n\n"); if(error != PlotErrorNone){ pipe.transformToPipe(); return error; } } if(closingShellCommand!=""){ pipe.write("\n%s Closing shell commands\n%s\n", ST_GNUPLOT_COMMENT_PREFIX, closingShellCommand.c_str()); } if(outputIsToFile()){ bool er = !pipe.executeScript(); pipe.transformToPipe(); if(er){ return (foundGnuplot() ? PlotErrorUnknownError : PlotErrorPlotterNotFound); } } return PlotErrorNone; } PlotError MultiPlot::plot(const Plot &plot, const string &title){ MultiPlot mp(plot, title); return mp.plot(); } PlotError MultiPlot::plot(const Plot &plot, const string &title, const string &gnuplotPath){ MultiPlot mp(plot, title, gnuplotPath); return mp.plot(); } void MultiPlot::setLogStream(ostream *logStream){ pipe.setLogStream(logStream); } void MultiPlot::setAspectRatio(double _optimalRatio){ optimalRatio = _optimalRatio; minRatio = 0; maxRatio = numeric_limits::infinity(); } void MultiPlot::setAspectRatio(double _optimalRatio, double _minRatio, double _maxRatio){ optimalRatio = _optimalRatio; minRatio = min(_optimalRatio * (1.0 - ST_DEFAULT_ASPECT_RATIO_RELATIVE_TOLLERANCE), _minRatio); maxRatio = max(_optimalRatio * (1.0 + ST_DEFAULT_ASPECT_RATIO_RELATIVE_TOLLERANCE), _maxRatio); } void MultiPlot::getFactorization(unsigned int value, double ratio, unsigned int &r, unsigned int &c){ if(value == 0){ r = c = 0; return; } double r0 = sqrt(value/ratio); double c0 = sqrt(value * ratio); unsigned int rt=1, ct=value; for(int factor = (int)max(1.0,floor(r0)); factor >= 1; --factor){ if((value % factor) == 0){ r = factor; c = value / factor; break; } } double dist = fabs(r - r0) + fabs(c - c0); for(int factor = (int)max(1.0,floor(c)); factor >= 1; --factor){ if((value % factor) == 0){ rt = factor; ct = value / factor; break; } } if((fabs(rt - r0) + fabs(ct - c0)) < dist){ r = rt; c = ct; } } void MultiPlot::getFactorization(unsigned int value, double optimalRatio, double minRatio, double maxRatio, unsigned int &r, unsigned int &c){ do{ getFactorization(value, optimalRatio, r, c); ++ value; }while((c > r * maxRatio) || (c < r * minRatio)); } unsigned int MultiPlot::countNeedle(const string &haystack, const string &needle){ int pos = 0, count = 0; int needleLen = needle.length(); while((pos = haystack.find(needle, pos))!= string::npos){ pos += needleLen; ++count; } return count; } double MultiPlot::getLabelHeight(const string &label){ return (1 + countNeedle(label, "\n")) * (outputIsPS() ? ST_DEFAULT_LABEL_HEIGHT_EPS : (outputType==PlotOutputTypeFileSVG ? ST_DEFAULT_LABEL_HEIGHT_SVG : ST_DEFAULT_LABEL_HEIGHT_TERM)); } /* void MultiPlot::copyTo(MultiPlot &MP) const{ MP.plots = plots; MP.footnote = footnote; MP.interpretSymbols= interpretSymbols; MP.optimalRatio = optimalRatio; MP.minRatio = minRatio; MP.maxRatio = maxRatio; MP.filePath = filePath; MP.terminalName = terminalName; MP.outputType = outputType; MP.terminalTitle = terminalTitle; MP.plotWidth = plotWidth; MP.plotHeight = plotHeight; MP.plotHSpacing = plotHSpacing; MP.plotVSpacing = plotVSpacing; } */ unsigned int MultiPlot::highestPlotDimension() const{ unsigned int hpd = 0; for(unsigned int n=0; n MultiPlot::sourceFiles() const{ set files; long p,s; for(p=0; p0){ tag = string(1,'a' + number % period) + tag; number = (number/period) -1; } if(number==0) tag = "a"+tag; return tag; } bool MultiPlot::hasPlotWithEPSBlurProblems() const{ for(long i=0; i maxX) || (point[0] < minX) || (point[1] > maxY) || (point[1] < minY)) break; } //calculate new point if(!vectorField(point[0], point[1], V[0], V[1])) break; for(i=0; i<2; ++i) point[i] = point[i] + timeStep * V[i]; } break; case IntegrationMethodClassicalRungeKutta: for(t = 0.0; t <= flowTime; t += timeStep){ //write point to file fout << point[0] << " " << point[1] << " " << point[2] << "\n"; //check if point is within bounds if(!followFlowsOutsideBounds){ if((point[0] > maxX) || (point[0] < minX) || (point[1] > maxY) || (point[1] < minY)) break; } //calculate new point if(!vectorField(point[0], point[1], V[0], V[1])) break; for(i=0; i<2; ++i) tempPointA[i] = point[i] + (timeStep/2.0) * V[i]; if(!vectorField(tempPointA[0], tempPointA[1], tempVA[0], tempVA[1])) break; for(i=0; i<2; ++i) tempPointB[i] = point[i] + (timeStep/2.0) * tempVA[i]; if(!vectorField(tempPointB[0], tempPointB[1], tempVB[0], tempVB[1])) break; for(i=0; i<2; ++i) tempPointC[i] = point[i] + timeStep * tempVB[i]; if(!vectorField(tempPointC[0], tempPointC[1], tempVC[0], tempVC[1])) break; for(i=0; i<2; ++i) point[i] = point[i] + (timeStep/6.0) * (V[i] + 2 * (tempVA[i] + tempVB[i]) + tempVC[i]); } break; } fout << endl; } } fout << endl; fout.close(); return PlotSource(PlotType2DCurve, title, filePath, 0, 1, 2); } PlotSource vectorFieldFlowLines2PlotSource( bool (*vectorField)(double, double, double, double&, double&, double&), string filePath, string title, unsigned int resolutionX, unsigned int resolutionY, unsigned int resolutionZ, double timeStep, double flowTime, IntegrationMethod integrationMethod, bool followFlowsOutsideBounds, double minX, double maxX, double minY, double maxY, double minZ, double maxZ, PlotError &error) { ofstream fout; unsigned int i; double x,y,z,xStep,yStep,zStep; double t, point[3], V[3], tempPointA[3], tempPointB[3], tempPointC[3], tempVA[3], tempVB[3], tempVC[3]; fout.open(filePath.c_str(), ios::out); if(!fout.good()){ error = PlotErrorFileWriteError; return PlotSource(PlotTypeNone, "", "", 0); } fout << ST_GNUPLOT_COMMENT_PREFIX << "Vector field flow-lines (trajectories) integrated up to time " << flowTime << ", starting on a " << resolutionX << " x " << resolutionY << " x " << resolutionZ << " grid, within the domain [" << minX << ", " << maxX << "] x [" << minY << ", " << maxY << "] x [" << minZ << ", " << maxZ << "]\n"; fout << ST_GNUPLOT_COMMENT_PREFIX << "Integration method was " << Plot::integrationMethod2string(integrationMethod) << " using a time-step of " << timeStep << "\n"; fout << ST_GNUPLOT_COMMENT_PREFIX << "Data columns are: x y z\n"; fout << ST_GNUPLOT_COMMENT_PREFIX << "Different flow-lines are separated by one empty line" << endl; error = PlotErrorNone; xStep = (maxX - minX) / resolutionX; yStep = (maxY - minY) / resolutionY; zStep = (maxZ - minZ) / resolutionZ; for(x = (resolutionX == 1 ? (maxX + minX)/2.0 : minX); x <= maxX; x += xStep){ for(y = (resolutionY == 1 ? (maxY + minY)/2.0 : minY); y <= maxY; y += yStep){ for(z = (resolutionZ == 1 ? (maxZ + minZ)/2.0 : minZ); z <= maxZ; z += zStep){ //follow flow line starting at point (x,y,z) up to time flowTime point[0] = x; point[1] = y; point[2] = z; switch(integrationMethod){ case IntegrationMethodForwardEuler: for(t = 0.0; t <= flowTime; t += timeStep){ //write point to file fout << point[0] << " " << point[1] << " " << point[2] << "\n"; //check if point is within bounds if(!followFlowsOutsideBounds){ if((point[0] > maxX) || (point[0] < minX) || (point[1] > maxY) || (point[1] < minY) || (point[2] > maxZ) || (point[2] < minZ)) break; } //calculate new point if(!vectorField(point[0], point[1], point[2], V[0], V[1], V[2])) break; for(i=0; i<3; ++i) point[i] = point[i] + timeStep * V[i]; } break; case IntegrationMethodClassicalRungeKutta: for(t = 0.0; t <= flowTime; t += timeStep){ //write point to file fout << point[0] << " " << point[1] << " " << point[2] << "\n"; //check if point is within bounds if(!followFlowsOutsideBounds){ if((point[0] > maxX) || (point[0] < minX) || (point[1] > maxY) || (point[1] < minY) || (point[2] > maxZ) || (point[2] < minZ)) break; } //calculate new point if(!vectorField(point[0], point[1], point[2], V[0], V[1], V[2])) break; for(i=0; i<3; ++i) tempPointA[i] = point[i] + (timeStep/2.0) * V[i]; if(!vectorField(tempPointA[0], tempPointA[1], tempPointA[2], tempVA[0], tempVA[1], tempVA[2])) break; for(i=0; i<3; ++i) tempPointB[i] = point[i] + (timeStep/2.0) * tempVA[i]; if(!vectorField(tempPointB[0], tempPointB[1], tempPointB[2], tempVB[0], tempVB[1], tempVB[2])) break; for(i=0; i<3; ++i) tempPointC[i] = point[i] + timeStep * tempVB[i]; if(!vectorField(tempPointC[0], tempPointC[1], tempPointC[2], tempVC[0], tempVC[1], tempVC[2])) break; for(i=0; i<3; ++i) point[i] = point[i] + (timeStep/6.0) * (V[i] + 2 * (tempVA[i] + tempVB[i]) + tempVC[i]); } break; } fout << endl; } } } fout << endl; fout.close(); return PlotSource(PlotType3DCurve, title, filePath, 0, 1, 2, 3); } PlotSource complexMapping2PlotSource( void (*mapping)(double, double, double&, double&), string filePath, string title, unsigned int resolutionR, unsigned int resolutionI, double minR, double maxR, double minI, double maxI, double zeroRadius, double inftyRadius, PlotError &error) { ofstream fout; double r,g,b; double xr,xi,yr,yi,rStep,iStep; fout.open(filePath.c_str(), ios::out); if(!fout.good()){ error = PlotErrorFileWriteError; return PlotSource(PlotTypeNone, "", "", 0); } fout << ST_GNUPLOT_COMMENT_PREFIX << "Complex mapping C -> C sampled on a " << resolutionR << " x " << resolutionI << " grid, within the domain [" << minR << ", " << maxR << "] x [" << minI << ", " << maxI << "]\n" << ST_GNUPLOT_COMMENT_PREFIX << "Sampling grid given as matrix, whose entries are color numbers, corresponding to the complex value of the function at that grid point.\n" << ST_GNUPLOT_COMMENT_PREFIX << "Complex to color transformation is done using the HSV-cylinder color model. 0 is white and INFTY is black.\n" << ST_GNUPLOT_COMMENT_PREFIX << "Upper left matrix entry is point (" << minR << ", " << minI << ")\n"; error = PlotErrorNone; rStep = (maxR - minR) / resolutionR; iStep = (maxI - minI) / resolutionI; for(xi = minI; xi <= maxI; xi += iStep){ for(xr = minR; xr <= maxR; xr += rStep){ mapping(xr,xi,yr,yi); complex2RGB(yr,yi,zeroRadius,inftyRadius,r,g,b); fout << RGB2ColorNumber(r,g,b) << " "; } fout << "\n"; } fout << endl; fout.close(); //create plot source PlotSource source = PlotSource(PlotType2DColorImage, title, filePath, 0); source.minX = minR; source.maxX = maxR; source.minY = minI; source.maxY = maxI; source.xCount = resolutionR; source.yCount = resolutionI; return source; } Plot HeatMapHClustered2Plot( const vector &data, // (INPUT) data matrix in row-major format long Nrows, long Ncolumns, const string &Rlegend, const string &Clegend, const string &ValueLegend, const vector &Rnames, const vector &Cnames, const string &filePath, ofstream &fout, long ¤tDataIndex, const string &title, const string &fileComment, double columnNamesRotation, HClusterWhat clusterWhat, HClusterOrdering Rordering, HClusterOrdering Cordering, HClusterMetric metric, HClusterLinkage linkage, bool uniformize, double &recommendedPlotWidthCorrection, double &recommendedPlotHeightCorrection, double &recommendedRelativePlotHSpacing, double &recommendedRelativePlotVSpacing, PlotError &error){ long r,r2,c,c2,n; bool foutWasOpen = fout.is_open(); if(!foutWasOpen){ fout.open(filePath.c_str(), ios::app); } if(!fout.good()){ error = PlotErrorFileWriteError; return Plot(); } error = PlotErrorNone; string nnFileComment = STPlot_aux_get_replaceAll(fileComment, "\n", (string(ST_GNUPLOT_COMMENT_PREFIX) + "\n").c_str()); const bool clusterRows = (clusterWhat==HClusterRows || clusterWhat==HClusterRowsAndColumns || clusterWhat==HClusterRowsAndLockColumns2Rows); const bool clusterColumns = (clusterWhat==HClusterColumns || clusterWhat==HClusterRowsAndColumns || clusterWhat==HClusterColumnsAndLockRows2Columns); long Nmax = max((clusterRows ? Nrows : 0), (clusterColumns ? Ncolumns : 0)); long *orderR = new long[Nrows]; long *orderC = new long[Ncolumns]; long *merge1 = new long[Nmax]; long *merge2 = new long[Nmax]; double *distances = new double[Nmax*Nmax]; bool needDistancesBackup = (((Rordering == HClusterOrderingByDistance) && clusterRows) || ((Cordering == HClusterOrderingByDistance) && clusterColumns)); double *distances2 = NULL; if(needDistancesBackup){ distances2 = new double[Nmax*Nmax]; } //cluster rows if needed if(clusterRows){ //calculate distance matrix (upper-triangular) for(r=0; r0 ? "\n\n" : "") << ST_GNUPLOT_COMMENT_PREFIX << " Data index: " << currentDataIndex << "\n" << ST_GNUPLOT_COMMENT_PREFIX << " " << nnFileComment << "\n" << ST_GNUPLOT_COMMENT_PREFIX << (clusterWhat==HClusterNone ? " Heatmap" : " Hierarchically clustered heatmap") << " (" << Nrows << " rows x " << Ncolumns << " columns)\n" << ST_GNUPLOT_COMMENT_PREFIX << " Rows" << (Rlegend=="" ? "" : " ("+Rlegend+")") << " are " << (clusterRows ? "" : " not") << " clustered\n"; if(clusterRows){ fout << ST_GNUPLOT_COMMENT_PREFIX << " Plot_row original_row\n"; for(r=0; r=0; --r){ for(c=0; c transformedData; if(uniformize){ ++currentDataIndex; STPlot_transformDataToUniformDistribution( data, 0, Nrows*Ncolumns-1, transformedData, adjustedTics); //write transformed data as matrix, in similar way as original data fout << "\n\n" << ST_GNUPLOT_COMMENT_PREFIX << " Data index: " << currentDataIndex << "\n" << ST_GNUPLOT_COMMENT_PREFIX << " Transformed data for plotting purposes\n" << ST_GNUPLOT_COMMENT_PREFIX << " The order of the values is preserved, but values have been non-linearly transformed to become uniformly distributed on the interval [0,1]\n" << ST_GNUPLOT_COMMENT_PREFIX << " Data: top-left entry corresponds to bottom-left plot pixel\n"; if(Nrows==1){ fout << ST_GNUPLOT_COMMENT_PREFIX << " Note: 2nd row is a dummy (duplicate) row for technical purposes and should be ignored\n"; } if(Ncolumns==1){ fout << ST_GNUPLOT_COMMENT_PREFIX << " Note: 2nd column is a dummy (duplicate) column for technical purposes and should be ignored\n"; } for(r=max(2l,Nrows)-1; r>=0; --r){ for(c=0; c &names, const vector &values, const string &nameLegend, const string &valueLegend, const string &filePath, long &dataIndex, const string &title, const string &fileComment, const string &separator, double namesRotation, SortHow sortHow, bool escapeUnderscoresInNames, double &recommendedPlotWidthCorrection, double &recommendedPlotHeightCorrection, double &recommendedRelativePlotHSpacing, double &recommendedRelativePlotVSpacing, long &histogramIndex, PlotError &error){ const long N=names.size(); if(N==0){ error = PlotErrorNothingToBeDone; return Plot(); } ofstream fout; fout.open(filePath.c_str(), (dataIndex<0 ? ios::out : ios::app)); if(!fout.good()){ error = PlotErrorFileWriteError; return Plot(); } if(dataIndex>=0) fout << "\n\n" << ST_GNUPLOT_COMMENT_PREFIX << " Data index: " << dataIndex << "\n"; error = PlotErrorNone; string nnFileComment = STPlot_aux_get_replaceAll(fileComment, "\n", (string(ST_GNUPLOT_COMMENT_PREFIX) + "\n").c_str()); double maxValue=values[0], minValue=values[0]; // figure out category order vector order(N); if(sortHow!=SortNone){ STPlot_qsortIndices(values, order, (sortHow==SortAscending ? true : false)); }else{ for(long i=0; i=0){ plot.range.yRange = Range(0,maxValue*1.2); } else if(maxValue<=0){ plot.range.yRange = Range(minValue*1.2,0); } else{ plot.range.yRange = Range(1.2*minValue, 1.2*maxValue); } plot.dataSeparator = separator; plot.xTics.setRotation(namesRotation); plot.sources.back().setColor(Color::getNicePlotColor(0)); histogramIndex = plot.sources.size()-1; // if bars are both negative & positive, include a horizontal line at zero // this must come after the histogram plot so that xtics are correct if((minValue<0) && (maxValue>0)){ plot.sources.push_back(PlotSource(PlotType2DAnalytic, "", "0", 0)); plot.sources.back().setColor(Color(0.6,0.6,0.6)); } ++dataIndex; return plot; } Plot StackedFilledCurves2Plot( const double data[], unsigned long xcount, unsigned long ycount, const vector &colors, const vector &names, const string &filePath, long dataIndex, const string &title, const string &fileComment, const string &xlabel, const string &ylabel, bool relative, ColorBlindness correctForColorBlindness, PlotError &error){ long i,j,n; double cumulative, rescaleAtZero; Color color; if(ycount==0){ error = PlotErrorNothingToBeDone; return Plot(); } //prepare output file ofstream fout; fout.open(filePath.c_str(), (dataIndex<0 ? ios::out : ios::app)); if(!fout.good()){ error = PlotErrorFileWriteError; return Plot(); } if(dataIndex>=0) fout << "\n\n" << ST_GNUPLOT_COMMENT_PREFIX << " Data index: " << dataIndex << "\n"; error = PlotErrorNone; string nnFileComment = fileComment; STPlot_aux_replaceAll(nnFileComment, "\n", (string(ST_GNUPLOT_COMMENT_PREFIX) + "\n").c_str()); fout << ST_GNUPLOT_COMMENT_PREFIX << " " << nnFileComment << "\n" << ST_GNUPLOT_COMMENT_PREFIX << " Data columns: 1:x" << (xlabel=="" ? "" : "("+xlabel+")"); for(j=0; jj ? "("+names[j]+")" : ""); } fout << "\n"; for(i=0; i extraColors; generateDiscreteColors(max(0l,(long)ycount - (long)colors.size()), true, extraColors, ColorBlindnessNone); for(j=0; j ×, // (INPUT) common times for all sample time series, strictly increasing const vector ¢iles, // (INPUT) centiles (e.g. 0.5, 0.95) at which to show contours and change coloring const vector > ¢ileMinValues, // (INPUT) values[t][c] is min value of c-th centile at time times[t] const vector > ¢ileMaxValues, // (INPUT) values[t][c] is max value of c-th centile at time times[t] . That is, at time t, time series will be within [centileMinValues[t][c], centileMaxValues[t][c]] with probablity centiles[c] const vector &highlightValues, // (INPUT) values[t] is value of median at time times[t]. Can also be empty (omit). const string &highlightName, const vector &meanValues, double lineWidth, ofstream &fout, const string &filePath, long ¤tDataIndex, const string &title, const string &fileComment, const string &xlabel, const string &ylabel, const string &dataName, double epsilon, ColorPalette colorPalette, bool useKeysInsteadOfColorBar, // if true, centile color scale is shown as plot keys instead of a color bar PlotError &error){ const long NC = centiles.size(); const long NT = times.size(); if(NT<1){ error = PlotErrorNothingToBeDone; return Plot(); } if((NC==0) && highlightValues.empty()){ error = PlotErrorNothingToBeDone; return Plot(); } const bool foutWasOpen = fout.is_open(); if(!foutWasOpen) fout.open(filePath.c_str()); if(!fout.is_open()){ error = PlotErrorFileWriteError; return Plot(); } fout << (currentDataIndex==0 ? "" : "\n\n") << ST_GNUPLOT_COMMENT_PREFIX << " Data index: " << currentDataIndex << "\n"; error = PlotErrorNone; string nnFileComment = STPlot_aux_get_replaceAll(fileComment, "\n", (string(ST_GNUPLOT_COMMENT_PREFIX) + "\n").c_str()); // sort centiles just in case vector sortedCentileIndices; STPlot_qsortIndices(centiles, sortedCentileIndices, true); //write file header fout << ST_GNUPLOT_COMMENT_PREFIX << " " << nnFileComment << "\n" << ST_GNUPLOT_COMMENT_PREFIX << " Distribution of " << dataName << " time series (resolved at " << NC << " centiles)\n" << ST_GNUPLOT_COMMENT_PREFIX << " 1:(" << xlabel << ")"; // write raw centile data long columns=1; for(long c=0; c=0; --c){ plot.sources.push_back(PlotSource(PlotType2DFilledCurves,(useKeysInsteadOfColorBar ? STPlot_makeString(100*centiles[sortedCentileIndices[c]])+"%" : ""),filePath,currentDataIndex,1,2+2*c,3+2*c)); plot.sources.back().setColorAsValue(centiles[sortedCentileIndices[c]]); plot.sources.back().filledCurveStyle = FilledCurveStyleAll; } columns = 1+2*NC; if(!highlightValues.empty()){ // show highlighted time series as 'double curve' (i.e. two alternating colors) plot.sources.push_back(PlotSource(PlotType2DCurve,"",filePath,currentDataIndex,1,++columns)); plot.sources.back().setColorLineTypeLineWidth(Color::white(),1,lineWidth); plot.sources.push_back(plot.sources.back()); plot.sources.back().setColorLineTypeLineWidth(Color::black(),2,lineWidth); plot.sources.back().title = highlightName; } if(!meanValues.empty()){ // show mean time series as 'double curve' (i.e. two alternating colors) plot.sources.push_back(PlotSource(PlotType2DCurve,"",filePath,currentDataIndex,1,++columns)); plot.sources.back().setColorLineTypeLineWidth(Color::white(),1,lineWidth); plot.sources.push_back(plot.sources.back()); plot.sources.back().setColorLineTypeLineWidth(Color::black(),3,lineWidth); plot.sources.back().title = "mean"; } ++currentDataIndex; // add color bar or keys if((!highlightValues.empty()) || (!meanValues.empty()) || useKeysInsteadOfColorBar){ plot.showKeys = true; } if(!useKeysInsteadOfColorBar){ plot.showColorBox = true; plot.CBRange = Range(0,centiles[sortedCentileIndices.back()]); plot.colorPalette = colorPalette; plot.CBTics.disableAutomaticTics(); plot.CBLabel = "centile"; for(long c=0; c &gridValues, // (INPUT) data grid values as matrix in row-major format long NR, // (INPUT) number of grid rows long NC, // (INPUT) number of grid columns const vector &rowValues, // (INPUT) coordinates at row centres, in ascenting order. Of size at least NR const vector &columnValues, // (INPUT) coordinates at column centres, in ascending order. Of size at least NC ofstream &fout, const string &filePath, long ¤tDataIndex, const string &title, const string &fileComment, const string &xlabel, const string &ylabel, const string &dataName, bool alsoSaveOriginalData, PlotError &error){ if(NR*NC==0){ error = PlotErrorNothingToBeDone; return Plot(); } //prepare output file if(!fout.is_open()){ error = PlotErrorFileWriteError; return Plot(); } long r,c; error = PlotErrorNone; string nnFileComment = fileComment; STPlot_aux_replaceAll(nnFileComment, "\n", (string(ST_GNUPLOT_COMMENT_PREFIX) + "\n").c_str()); // write original data if(alsoSaveOriginalData){ fout << ST_GNUPLOT_COMMENT_PREFIX << " Data index: " << currentDataIndex << "\n" << ST_GNUPLOT_COMMENT_PREFIX << " " << nnFileComment << "\n" << ST_GNUPLOT_COMMENT_PREFIX << " Original data\n" << ST_GNUPLOT_COMMENT_PREFIX << " Data columns: 1:(" << xlabel << ")\t2:(" << ylabel << ")\t3:(" << dataName << ")\n"; for(r=0; r &sizeParam, // (INPUT) object size >0 (~ node area) parameter (only relative values matter). Should either contain N entries or be empty. const vector &colorParam, // (INPUT) object color parameter (only relative values matter). Should either contain N entries or be empty. const string &filePath, const string &title, const string &fileComment, ColorPalette nodeColorPalette, // (INPUT) color palette from which to choose object colors const Color &colorNeg, const Color &colorPos, double thresholdNeg, // (INPUT) negative correlation threshold (negative adjacencies greater than this are not plotted) double thresholdPos, // (INPUT) positive correlation threshold (positive adjacencies smaller than this are not plotted) bool cluster, PlotError &error){ long i,j,n; double temp,r,g,b,x1,y1,x2,y2; ofstream fout; fout.open(filePath.c_str()); if(!fout.good()){ error = PlotErrorFileWriteError; return Plot(); } error = PlotErrorNone; bool varSizes = (N<=sizeParam.size()); bool varColors = (N<=colorParam.size()); string nnFileComment = fileComment; STPlot_aux_replaceAll(nnFileComment, "\n", (string(ST_GNUPLOT_COMMENT_PREFIX) + "\n").c_str()); //prepare geometry const double circleGap = 0.2*PI; const double domega = -(2.0*PI-circleGap)/N; //move clockwise const double omegao = PI/2 - circleGap/2; double omega, sizeScale, maxSize, colorScale, minColorParam, maxColorParam; const double defaultNodeRadius = sqrt(ST_DEFAULT_NETWORK_NODE_COVERAGE/(PI*N)); if(varSizes){ for(i=0, temp=0, maxSize=sizeParam[0]; ithresholdNeg) && (adjacencies[n]1){ //find cluster pair with least distance //min-distance pair will be clusters clmin1 & clmin2 dmin = distances[STPlot_aux_array_rowColumn2Index(clusters[clmin1=0],clusters[clmin2=1],N)]; for(r=0; r0 ? Pcentroid[(c1-1)*dim+n] : data[STPlot_aux_array_rowColumn2Index(Ncolumns, transpose, -c1, n)])*mass1 + (c2>0 ? Pcentroid[(c2-1)*dim+n] : data[STPlot_aux_array_rowColumn2Index(Ncolumns, transpose, -c2, n)])*mass2)/Pmass[p]; } } // order nodes according to the proximity of their centroids to the centroid of their parent’s sibling long *PRangeL = new long[Nmerges]; long *PRangeU = new long[Nmerges]; // PRangeL[i] - PRangeU[i] is the range within which the leafs descending from node i are to be placed long *leafs = new long[N]; // leafs[i] is the new position of the i-th object // root leaf range is entire index space (1,..,N) // root children order does not matter PRangeL[Nmerges-1] = 0; PRangeU[Nmerges-1] = N-1; aux_HCsplitRange( Nmerges-1, 0, N-1, merge1[Nmerges-1], merge2[Nmerges-1], leafs, PRangeL, PRangeU, merge1, merge2, Pmass); for(p=Nmerges-1; p>=0; --p){ for(ci=0; ci<=1; ++ci){ // reorder children of each child ci (i.e. all grandchildren of p) child = (ci==0 ? merge1[p] : merge2[p]); if(child<=0) continue; // child is a leaf, so has no grandchildren child -= 1; gchild1 = merge1[child]; gchild2 = merge2[child]; // get distance of grandchildren to sibling's centroid si = 1-ci; sibling = (si==0 ? merge1[p] : merge2[p]); d1 = STPlot_aux_distanceBetweenDataRecords((sibling<=0 ? data : Pcentroid), (sibling<=0 ? Ncolumns : dim), (sibling<=0 ? -sibling : sibling-1), (sibling<=0 ? !transpose: true), (gchild1<=0 ? data : Pcentroid), (gchild1<=0 ? Ncolumns : dim), (gchild1<=0 ? -gchild1 : gchild1-1), (gchild1<=0 ? !transpose: true), dim, metric); d2 = STPlot_aux_distanceBetweenDataRecords((sibling<=0 ? data : Pcentroid), (sibling<=0 ? Ncolumns : dim), (sibling<=0 ? -sibling : sibling-1), (sibling<=0 ? !transpose: true), (gchild2<=0 ? data : Pcentroid), (gchild2<=0 ? Ncolumns : dim), (gchild2<=0 ? -gchild2 : gchild2-1), (gchild2<=0 ? !transpose: true), dim, metric); // determine appropriate order of grandchildren keepOrder = ((d1d2)&(si>ci)); // set appropriate order and leaf ranges for grandchildren aux_HCsplitRange( child, PRangeL[child], PRangeU[child], (keepOrder ? gchild1 : gchild2), (keepOrder ? gchild2 : gchild1), leafs, PRangeL, PRangeU, merge1, merge2, Pmass); } } //order[] is inverse permutation of leafs[] for(p=0; p0 if composite cluster long cl2, // <=0 if leaf, >0 if composite cluster double clDistances[]){ //Distance matrix between all clusters, in row-major format. Only strictly upper-triangular part needed. Already weighted by Pmass[]. Negative if distance has not been calculated yet for a given cluster pair. long i,j,n,m; if(cl2>0){ //cl2 is a composite cluster long child1 = merge1[cl2-1], child2 = merge2[cl2-1]; i = N-1+cl1; j = N-1+child1; n = (i0){ //cl1 is a composite cluster but cl2 is a leaf long child1 = merge1[cl1-1], child2 = merge2[cl1-1]; i = N-1+cl2; j = N-1+child1; n = (i=0; --p){ for(ci=0; ci<=1; ++ci){ // reorder children of each child ci (i.e. all grandchildren of p) child = (ci==0 ? merge1[p] : merge2[p]); if(child<=0) continue; // child is a leaf, so has no grandchildren child -= 1; gchild1 = merge1[child]; gchild2 = merge2[child]; // get distance of grandchildren to sibling's centroid si = 1-ci; sibling = (si==0 ? merge1[p] : merge2[p]); d1 = aux_distanceBetweenHClusters( N, distances, merge1, merge2, Pmass, sibling, gchild1, clDistances); d2 = aux_distanceBetweenHClusters( N, distances, merge1, merge2, Pmass, sibling, gchild2, clDistances); // determine appropriate order of grandchildren keepOrder = ((d1d2)&(si>ci)); // set appropriate order and leaf ranges for grandchildren aux_HCsplitRange( child, PRangeL[child], PRangeU[child], (keepOrder ? gchild1 : gchild2), (keepOrder ? gchild2 : gchild1), leafs, PRangeL, PRangeU, merge1, merge2, Pmass); } } //order[] is inverse permutation of leafs[] for(p=0; p=0; --p){ c1 = merge1[p]; c2 = merge2[p]; ID1 = (c1<=0 ? (-c1) : PID[c1-1]); ID2 = (c2<=0 ? (-c2) : PID[c2-1]); // set appropriate order and leaf ranges for children aux_HCsplitRange( p, PRangeL[p], PRangeU[p], (ID1 sources = MP.sourceFiles(); shellFout << "\n# Plot" << (plotName=="" ? "" : " " + plotName) << ":\n"; if(plotTerm){ shellFout << "# Will plot to the " << ST_DEFAULT_TERM << " terminal\n"; } else if(plotPDF){ shellFout << "# Will generate the PDF plot: " << plotPDFFile << "\n"; } else if(plotSVG){ shellFout << "# Will generate the SVG plot: " << plotSVGFile << "\n"; } if(!sources.empty()){ shellFout << "# Required source files:\n"; } for(set::const_iterator i=sources.begin(); i!=sources.end(); ++i){ shellFout << "# " << *i << "\n"; } if(logOK){ shellFout << "# Required gnuplot script file: " << logFile << "\n"; shellFout << (MP.foundGnuplot() ? MP.gnuplotPath() : "gnuplot") << " '" << logFile << "'\n"; }else{ shellFout << "# Error: Failed to write to file '" << logFile << "'\n"; } } } //some feedback if(streamOut != NULL){ if(PDFOK) plotOutput = "PDF"; if(SVGOK) plotOutput = plotOutput + (plotOutput=="" ? "" : " & ") + "SVG"; if(termOK) plotOutput = plotOutput + (plotOutput=="" ? "" : " & ") + "terminal"; (*streamOut) << "Successfully plotted " << plotName << " to " << plotOutput << endl; } return ((PDFOK || (!plotPDF)) && (SVGOK || (!plotSVG)) && (termOK || (!plotTerm))); } #pragma mark - #pragma mark Auxiliary functions #pragma mark - void STPlot_transformDataToUniformDistribution( const vector &original, long start, long end, vector &transformed, Tics &adjustedTics){ const long N = end-start+1; transformed.resize(N); vector indices; adjustedTics.disableAutomaticTics(); if(N<=0) return; // map data to ascending order values (equivalent to transforming using empirical CDF) indices.resize(N); for(long i=0; i orderedDifferentValues(Ndifferent); for(long i=0; i gridI; STPlot_getRegularIntegerGrid(0,Ndifferent-1,gridI); vector ticLabels(gridI.size()); for(long i=0; i &grid){ grid.clear(); if(maxI=0) && (denominator >= 0)){ return numerator - denominator * floor(numerator/denominator); }else if((numerator < 0) && (denominator < 0)){ return numerator - denominator * floor(numerator/denominator); }else if((numerator < 0) && (denominator > 0)){ return numerator + denominator * ceil(abs(numerator/denominator)); }else if((numerator >0) && (denominator < 0)){ return numerator + denominator * ceil(numerator/abs(denominator)); } return 0; //should not have arrived here } bool STPlot_aux_fileExists(const string &path){ struct stat fileInfo; if(stat(path.c_str(),&fileInfo) == 0){ return true; }else{ return false; } } void STPlot_qsortIndices(const vector &values, vector &sortedIndices, bool ascending){ sortedIndices.resize(values.size()); for(long n=0; n &values, vector &indices, long start, long end, bool ascending){ const long pi = start+(end-start)/2; //middle const double &pv = values[indices[pi]]; long temp; // swap temp = indices[pi]; indices[pi] = indices[end]; indices[end] = temp; long si = start; for(long i=start; i pv){ // swap temp = indices[i]; indices[i] = indices[si]; indices[si] = temp; ++si; } } // swap temp = indices[si]; indices[si] = indices[end]; indices[end] = temp; return si; } void STPlot_aux_qsortIndices(const vector &values, vector &indices, long start, long end, bool ascending){ if(start &values){ double minDifference = -1; for(long i=1; i