/* STPlot v2.6 C++ API for plotting with gnuplot. Created by Stilianos Louca on 2008.11.25 Last modified on 2014.03.06 Tested using GCC 4.2.1 and gnuplot 4.6.5 on Mac OS 10.6.8. Namespace ST */ #ifndef STPLOT #define STPLOT #define STPLOT_VERSION 2.6 #include #include #include #include #include #include #include #include #include #include #include #include "STPipe.h" #include "STColor.h" #include "STPlotDefaults.h" using namespace std; namespace ST{ extern string STPLOT_GNUPLOT_PATH; // modify this to set the gnuplot program path globally, or leave empty for automatic detection #pragma mark - #pragma mark Typedefs #pragma mark - typedef enum _PlotType{ PlotTypeNone, //No plot ('NULL') PlotType1DPoints, //1 data column: x1 PlotType1DColoredPoints, //4 data columns: x1, r,g,b. Plots points and sets color by rgb-value \in [0,1]^3. PlotType1DVectors, //2 data columns: x1, v1 PlotType1DColoredVectors, //5 data columns: x1, v1, r,g,b. Plots vectors and sets color by rgb-value \in [0,1]^3. PlotType2DPoints, //2 data columns: x1,x2 PlotType2DColoredPoints, //5 data columns: x1,x2,r,g,b. Plots points and sets color by rgb-value \in [0,1]^3. PlotType2DVectors, //4 data columns: x1,x2,v1,v2 PlotType2DColoredVectors, //7 data columns: x1,x2,v1,v2,r,g,b. Plots vectors and sets color by rgb-value \in [0,1]^3. PlotType2DVariableSizedSpheres, //3 data columns: x1,x2,radius. Plots spheres with variable radius (given in x-axis coordinates). PlotType2DColoredVariableSizedSpheres, //6 data columns: x1,x2,radius,r,g,b. Plots (r,g,b)-colored spheres with variable radius (in x-axis coordinates). PlotType2DCurve, //2 data columns: x1,x2 PlotType2DCurvePoints, //2 data columns: x1,x2 PlotType2DGraphLinear, //2 data columns: x,f(x) PlotType2DGraphLinearPoints, //2 data columns: x,f(x) PlotType2DGraphSplines, //2 data columns: x,f(x). x-values need not be ordered. PlotType2DGraphSplinesPoints, //2 data columns: x,f(x). x-values need not be ordered. PlotType2DFilledCurve, //3 data columnns: x,y PlotType2DFilledCurves, //3 data columnns: x,y1,y2. PlotType2DAnalytic, //No data needed. Plots function f(x) given analytically PlotType3DPoints, //3 data columns: x1,x2,x3 PlotType3DColoredPoints, //6 data columns: x1,x2,x3,r,g,b. Plots points and sets color by rgb-value \in [0,1]^3. PlotType3DVectors, //6 data columns: x1,x2,x3,v1,v2,v3 PlotType3DColoredVectors, //9 data columns: x1,x2,x3,v1,v2,v3,r,g,b. Plots vectors and sets color by rgb-value \in [0,1]^3. PlotType3DCurve, //3 data columns: x1,x2,x3 PlotType3DCurvePoints, //3 data columns: x1,x2,x3 PlotType1DHistogram, //2 data columns: interval-names, value PlotType1DEmpiricalDensity, //2 data columns: interval start, value PlotType2DScatteredValueMap, //3 data columns: x1,x2,value (scattered data). Value -> color map is determined using a color palette. PlotType2DGridValueMap, //data matrix: matrix entry corresponds to value on grid node (not grid box!). Value -> color map is determined using a color palette. PlotType2DScannedValueMap, //3 data columns: x1,x2,value. Data needs to be in form of equally-sized row "chuncs", separated by one empty line. Each such chunk corresponds to one "sampling-line" of the surface. Value -> color map is determined using a color palette. PlotType2DColorImage, //data matrix: matrix entry corresponds to color of box. Color should be given as integer (colorNumber) within [0, 256^3 - 1]. See the color conversion functions RGB2ColorNumber() & colorNumber2RGB() for more details. PlotType2DValueImage, //data matrix: matrix entry corresponds to value of box. Value -> color map is determined using a color palette. PlotType3DScatteredSurface, //3 data columns: x1,x2,f(x1,x2) (scattered data) PlotType3DGridSurface, //data matrix: matrix entry corresponds to surface height at that position PlotType3DScannedSurface, //3 data columns: x1,x2,f(x1,x2). Data needs to be in form of equally-sized row "chuncs", separated by one empty line. Each such chunk corresponds to one "sampling-line" of the surface. PlotType3DValuedScannedSurface //4 data columns: x1,x2,f(x1,x2),value. Data needs to be in form of equally-sized row "chuncs", separated by one empty line. Each such chunk corresponds to one "sampling-line" of the surface. Value -> color map is determined using a color palette. } PlotType; typedef enum _PlotError{ PlotErrorNone, PlotErrorInvalidSyntax, PlotErrorException, PlotErrorFileReadError, PlotErrorFileWriteError, PlotErrorFileNotFoundError, PlotErrorPermissionError, PlotErrorEOF, PlotErrorPlotterNotFound, PlotErrorUnknownError, PlotErrorNothingToBeDone, PlotErrorParameterError, PlotErrorIncompatiblePlots } PlotError; typedef enum _GridColoringType{ GridColoringTypeMean, //Box value(color or height) is mean of adjacent corners GridColoringTypeMax, //Box value(color or height) is max of adjacent corners GridColoringTypeMin, //Box value(color or height) is min of adjacent corners GridColoringTypeLL, //Box value(color or height) is lower-left adjacent corner GridColoringTypeUR //Box value(color or height) is upper-right adjacent corner } GridColoringType; typedef enum _GridSmoothingType{ GridSmoothingTypeEuclidean, GridSmoothingTypeGauss, GridSmoothingTypeBox } GridSmoothingType; typedef enum _PlotOutputType{ PlotOutputTypeTerminal, PlotOutputTypeFilePDF, PlotOutputTypeFileEPS, PlotOutputTypeFileSVG } PlotOutputType; typedef enum _VectorHeadStyle{ VectorHeadStyleFilledTriangle, VectorHeadStyleFilledArrow, VectorHeadStyleEmptyTriangle, VectorHeadStyleEmptyArrow, VectorHeadStyleNone // don't draw any arrow head. Can be used to draw line segments. } VectorHeadStyle; typedef enum _FilledCurveStyle{ FilledCurveStyleAbove, FilledCurveStyleBelow, FilledCurveStyleAll } FilledCurveStyle; typedef enum _IntegrationMethod{ IntegrationMethodForwardEuler, IntegrationMethodClassicalRungeKutta } IntegrationMethod; typedef enum _ContourPlacement{ ContourPlacementBottom, ContourPlacementSurface, ContourPlacementBottomSurface } ContourPlacement; typedef enum _HClusterMetric{ HClusterMetricEuclidean, HClusterMetricManhattan, HClusterMetricMaximum } HClusterMetric; typedef enum _HClusterOrdering{ HClusterOrderingArbitrary, HClusterOrderingCentroid, HClusterOrderingAscending, HClusterOrderingByDistance } HClusterOrdering; typedef enum _HClusterLinkage{ HClusterLinkageSingle, //minimum linkage HClusterLinkageComplete, //maximum linkage HClusterLinkageUPGMA //average linkage } HClusterLinkage; typedef enum _HClusterWhat{ HClusterNone, HClusterRows, HClusterColumns, HClusterRowsAndColumns, HClusterRowsAndLockColumns2Rows, HClusterColumnsAndLockRows2Columns } HClusterWhat; typedef enum _LabelAlignment{ LabelAlignmentLeft, LabelAlignmentCenter, LabelAlignmentRight } LabelAlignment; typedef enum { SortNone, SortAscending, SortDescending } SortHow; typedef enum { PlotColorTypeAuto, PlotColorTypeAsColor, PlotColorTypeAsValue } PlotColorType; #pragma mark - #pragma mark Preliminary class definitions #pragma mark - //Public classes class Contours; class Range; class MultiRange; class MultiLabel; // for axis labeling class PlotSource; class Plot; class MultiPlot; class Label; // for custom labels within the plot //Private Classes class GnuplotPipe; #pragma mark - #pragma mark Class definitions #pragma mark - class GnuplotPipe : private STPipe{ private: string gnuplotPath; FILE *scriptStream; string scriptPath; bool _foundGnuplot; bool tryToFindGnuplot(const string &firstGuess=""); public: //constructor GnuplotPipe() : STPipe(), scriptStream(NULL) {}; //copy constructor: this will actually initiate a new pipe to gnuplot but with similar settings GnuplotPipe(const GnuplotPipe &original); //assignment operator: this will actually initiate a new pipe to gnuplot but with similar settings GnuplotPipe &operator=(const GnuplotPipe &original); void open(); void open(string gnuplotPath); using STPipe::pipeIsReady; using STPipe::setLogStream; virtual bool write(const char *format, ...); virtual bool write(const string &message); void transformToScript(); void transformToPipe(); bool executeScript(); bool isInScriptMode() const; bool foundGnuplot() const{ return _foundGnuplot; } string currentGnuplotPath() const{ return gnuplotPath; } string currentGnuplotVersion() const; }; //Class for defining a 1-dimensional plot-range //Unsetting a bound (min or max) sets it to 'auto' class Range{ friend class MultiRange; friend class Plot; private: bool rangeMinAuto, rangeMaxAuto; double rangeMin, rangeMax; string gnuplotExpression() const; void mergeWithRange(Range range); public: //constructors Range(); Range(double rangeMin, double rangeMax); void unsetRangeMin(){ rangeMinAuto = true; } void unsetRangeMax(){ rangeMaxAuto = true; } void setRangeMin(double value){ rangeMin = value; rangeMinAuto = false; } void setRangeMax(double value){ rangeMax = value; rangeMaxAuto = false; } // get suggestions for a 'nice' plot range depending on the data static Range getNicePlotRange(const vector &values, long start, long end, bool zeroIsReference, bool logarithmic, double epsilon); static Range getNicePlotRange(double minValue, double maxValue, bool zeroIsReference, bool logarithmic, double epsilon); void scaleRangeByFactor(double factor); }; //Class for defining a multidimensional (2D or 3D) plot-range. //Dimension is determined via the constructor used. class MultiRange{ friend class Plot; private: unsigned int dim; void mergeWithRange(MultiRange range); string gnuplotExpression() const; string gnuplotCommand() const; public: Range xRange, yRange, zRange; //constructors MultiRange(); //dim = 2, ranges set to auto MultiRange(unsigned int dim); //dim \in {2,3}, ranges set to auto MultiRange(Range xRange, Range yRange); //dim = 2 MultiRange(Range xRange, Range yRange, Range zRange); //dim = 3 MultiRange(double xRangeMin, double xRangeMax, double yRangeMin, double yRangeMax); //dim = 2 MultiRange(double xRangeMin, double xRangeMax, double yRangeMin, double yRangeMax, double zRangeMin, double zRangeMax); //dim = 3 void scaleRangeByFactor(double factor); }; //Class for defining a multidimensional (2D or 3D) axis-label. //Dimension is determined via the constructor used. class MultiLabel{ friend class Plot; private: string _xLabel, _yLabel, _zLabel; unsigned int _dim; string gnuplotCommand(bool interpretSymbols, bool escapeUnderscores) const; public: //constructors MultiLabel(); //dim = 2. labels set to "x" and "y" MultiLabel(string xLabel, string yLabel); //dim = 2 MultiLabel(string xLabel, string yLabel, string zLabel); //dim = 3 }; //Single class for defining contouring behaviour of plots class Contours{ friend class Plot; private: bool drawContours; bool incremental; bool fixedCount; bool automatic; double start, step, end; unsigned int count; vector levels; static string contourPlacement2string(ContourPlacement placement); string gnuplotCommand() const; public: //constructors Contours(bool enable = false); Contours(const vector &levels); Contours(double singleLevel); Contours(unsigned int count); Contours(double start, double step, unsigned int count); Contours(double start, double step, double end); ContourPlacement placement; bool individualContours; }; //Class for defining tics on a single axis class Tics{ friend class Plot; private: vector customTicLabels; vector customTicValues; vector customTicWithCustomLabel; bool automatic; double start, step, end; bool startSet, stepSet, endSet; double rotation; void add(unsigned int ticCount, va_list values); string gnuplotCommand(string ticsName, bool interpretSymbols, bool escapeUnderscores) const; string gnuplotCommand(string ticsName, bool interpretSymbols, bool escapeUnderscores, double rescale) const; public: Tics(); //sets tics to automatic with no custom tics. Tics(unsigned int ticCount,...); //following ticCount arguments should be of type double, corresponding to values of custom tics. void add(const string &label, double value); void add(double value); void add(unsigned int count, ...); void clearCustomTics(); void enableAutomaticTics(); void enableAutomaticTics(double step); void enableAutomaticTics(double start, double step); void enableAutomaticTics(double start, double step, double end); void disableAutomaticTics(); void setToRegularIntegerValues(double rangeMin, double rangeMax); // will generate a regular tics grid on integer values within the given range. Guaranteed to include ceil(rangeMin). void setRotation(double angle); }; class Label{ friend class Plot; private: string gnuplotCommand(long tag, bool interpretSymbols, int dim, const string &font, bool escapeUnderscores) const; public: string text; bool wrtCoordinates; double x,y,z; LabelAlignment alignment; double fontSize; // set to negative for default Label(); Label(const string &text, bool wrtCoordinates, double x, double y, LabelAlignment alignment); Label(const string &text, bool wrtCoordinates, double x, double y, double z, LabelAlignment alignment); }; //Class for defining a single plot source: file-path, data-index, title, dimension of data (number of columns), column-indices //Used in subsequent plotting functions class PlotSource{ friend class Plot; friend class MultiPlot; private: string filePath; unsigned long index; vector columns; //columns to be considered from data. Note that the left-most column has index 1 (gnuplot convention) PlotType plotType; PlotColorType colorType; Color color; double colorValue; bool lineWidthAuto; double lineWidth; //width in multiples of the default width. bool lineTypeAuto; int lineType; //actual line form depends on terminal used. Typically type -1 is a solid line. bool pointSizeAuto; double pointSize; bool pointTypeAuto; int pointType; //actual point form depends on terminal used static unsigned int columnCountForPlotType(PlotType type); string gnuplotLineWidthExpression() const; string gnuplotColorExpression() const; // string gnuplotColorColumnExpression() const; string gnuplotLineTypeExpression() const; string gnuplotPointSizeExpression() const; string gnuplotPointTypeExpression() const; string gnuplotFilledCurveStyleExpression() const; bool isPlotWithEPSBlurProblems() const; bool isPlotWhichShowsColorbox() const; public: string title; //specialized parameters needed depending on plotType double xStart, yStart; double xStep, yStep; unsigned int xCount, yCount; double minX, maxX, minY, maxY; string xTransformationFunction; //function given as gnuplot math expression of variable '#' string yTransformationFunction; //function given as gnuplot math expression of variable '#' string zTransformationFunction; //function given as gnuplot math expression of variable '#' string wTransformationFunction; //function given as gnuplot math expression of variable '#' VectorHeadStyle vectorHeadStyle; double vectorHeadSize; FilledCurveStyle filledCurveStyle; double fillReference; //reference y-value against which to fill a single curve void setColor(const Color &c); void setColorAsValue(double value); void setColorAuto(); void setLineWidth(double width); void setLineWidthAuto(); void setLineType(int type); void setLineTypeAuto(); void setPointSize(double size); void setPointSizeAuto(); void setPointType(int type); void setPointTypeAuto(); void setColorLineTypeLineWidth(const Color &c, int type, double width); //returns values 0, 1, 2 or 3 depending on plot type unsigned int plotDimension() const; //constructor //missing arguments should be unsigned ints, corresponding to data-columns used. Their number is determined by plotType. //filePath should be analytic function in x if plotType == PlotType2DAnalytic. In that case f(x) should have gnuplot function syntax. PlotSource(PlotType plotType, string title, string filePath, unsigned int index, ...); //empty source of PlotTypeNone PlotSource(); //destructor ~PlotSource(); //copy constructor // PlotSource(const PlotSource &original); //assignment operator // PlotSource &operator=(const PlotSource &original); bool usesSourceFile() const; }; //Single class for defining a plot with one or multiple graphs/data sets //Note: PlotTypes must be compatible with each other, otherwise only the first compatible sources will be considered class Plot{ friend class MultiPlot; friend PlotSource vectorField2PlotSource( bool (*vectorField)(double, double, double&, double&), string filePath, string title, unsigned int resolutionX, unsigned int resolutionY, double minX, double maxX, double minY, double maxY, VectorColoring coloring, double meanVectorLength, PlotError &error); friend PlotSource vectorField2PlotSource( bool (*vectorField)(double, double, double, double&, double&, double&), string filePath, string title, unsigned int resolutionX, unsigned int resolutionY, unsigned int resolutionZ, double minX, double maxX, double minY, double maxY, double minZ, double maxZ, VectorColoring coloring, double meanVectorLength, PlotError &error); private: //Takes a mathematical expression of parameter '$' and returns it with $ replaced by $column. Used for transforming plot data. //An empty trafo string is interpreted as the identity. static string evaluateFunctionForColumn(string trafo, unsigned long column); static unsigned int compatibilityGroup(PlotType type); static bool plotTypesCompatible(PlotType type1, PlotType type2); static string vectorHeadStyle2string(VectorHeadStyle style, double headSize); string gnuplotDataSeparatorExpression() const; PlotError writeCommandToPipe( GnuplotPipe &pipe, double originX, double originY, double availableWidth, double availableHeight, double resizeNonPlotAreaWidthByFactor, double resizeNonPlotAreaHeightByFactor, double resizeTicsByFactor, const string &windowFont, bool interpretSymbols, unsigned long startSource, const string &plotTag, bool escapeUnderscoresInTitle, bool escapeUnderscoresInTicsAndLabels, string commandJustAfterFirstPlot, long &nextAvailLabelTag) const; bool hasPlotWithEPSBlurProblems() const; bool hasPlotWhichShowsColorbox() const; public: bool showColorBox; bool showColorMap; //if set to false, only contours are drawn (if enabled). ColorPalette colorPalette; bool showKeys; string title; MultiLabel axisLabels; string CBLabel; MultiRange range; vector sources; GridColoringType gridColoringType; GridSmoothingType gridSmoothingType; Contours contours; double xDistanceScale; double yDistanceScale; unsigned int xGridSize; unsigned int yGridSize; Range CBRange; Tics CBTics, xTics, yTics, zTics; bool xLogarithmic, yLogarithmic, zLogarithmic; Color backgroundColor; vector