1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2007 INRIA, 2008 Timo Bingmann
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  * Original Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
19  * Enhancements: Timo Bingmann <timo.bingmann@student.kit.edu>
20  */
21 #ifndef GNUPLOT_H
22 #define GNUPLOT_H
23 
24 #include <string>
25 #include <vector>
26 #include <utility>
27 
28 namespace ns3 {
29 
30 /**
31  * \ingroup gnuplot
32  *
33  * \brief Abstract class to store a plot line to be used by ns3::Gnuplot.
34  *
35  * This class contains a reference counted data object in m_data. The data
36  * object contains different structs derived from struct Data by subclasses.
37  */
38 class GnuplotDataset
39 {
40 public:
41 
42   /**
43    * Reference-counting copy constructor.
44    * \param original Original GnuPlotDataset
45    */
46   GnuplotDataset (const GnuplotDataset& original);
47 
48   /**
49    * Reference-counting destructor.
50    */
51   ~GnuplotDataset();
52 
53   /**
54    * Reference-counting assignment operator.
55    * \param original Right-hand side of assignment operator
56    * \return Copy of original GnuplotDataset
57    */
58   GnuplotDataset& operator= (const GnuplotDataset& original);
59 
60   /**
61    * \brief Change line title.
62    * \param title the new title string to use for this dataset.
63    *
64    * \note If you want your title to contain a newline character,
65    *       escape it like this:  "First line\\nSecond line" so that
66    *       it is converted to "First line\nSecond line" in the plot file.
67    */
68   void SetTitle (const std::string& title);
69 
70   /**
71    * \brief Change extra formatting style parameters for newly created objects.
72    * \param extra       extra formatting
73    */
74   static void SetDefaultExtra (const std::string& extra);
75 
76   /**
77    * \brief Add extra formatting parameters to this dataset.
78    * \param extra       extra formatting
79    */
80   void SetExtra (const std::string& extra);
81 
82 protected:
83 
84   /// Friend because it accesses m_data and it's virtual functions directly in
85   /// GenerateOutput().
86   friend class Gnuplot;
87 
88   /**
89    * \brief Extra gnuplot parameters set on every newly created dataset.
90    */
91   static std::string m_defaultExtra;
92 
93   /**
94    * \brief Derived classes subclass this struct and add their own data fields.
95    */
96   struct Data;
97 
98   /**
99    * Called by constructors of derived classes.
100    * \param data the reference counted data object representing this dataset.
101    */
102   GnuplotDataset (struct Data* data);
103 
104   /**
105    * Reference counted data object.
106    */
107   struct Data*  m_data;
108 };
109 
110 /**
111  * \ingroup gnuplot
112  *
113  * \class Gnuplot2dDataset
114  * \brief Class to represent a 2D points plot. Set the line or points style
115  * using SetStyle() and set points using Add().
116  */
117 class Gnuplot2dDataset : public GnuplotDataset
118 {
119 public:
120   /**
121    * The plotting style to use for this dataset.
122    */
123   enum Style {
124     LINES,
125     POINTS,
126     LINES_POINTS,
127     DOTS,
128     IMPULSES,
129     STEPS,
130     FSTEPS,
131     HISTEPS,
132   };
133 
134   /**
135    * Whether errorbars should be used for this dataset.
136    */
137   enum ErrorBars {
138     NONE,
139     X,
140     Y,
141     XY
142   };
143 
144   /**
145    * \param title the title to be associated to this dataset.
146    *
147    * Create an empty dataset. Usually, the dataset's title is
148    * displayed in the legend box.
149    */
150   Gnuplot2dDataset (const std::string& title = "Untitled");
151 
152   /**
153    * Change default style for all newly created objects.
154    * \param style the style of plotting to use for newly created datasets.
155    */
156   static void SetDefaultStyle (enum Style style);
157 
158   /**
159    * \param style the style of plotting to use for this dataset.
160    */
161   void SetStyle (enum Style style);
162 
163   /**
164    * Change default errorbars style for all newly created objects.
165    * \param errorBars the style of errorbars to use for newly created datasets.
166    */
167   static void SetDefaultErrorBars (enum ErrorBars errorBars);
168 
169   /**
170    * \param errorBars the style of errorbars to display.
171    *
172    * If you use any style other than none, you need
173    * to make sure you store the delta information in
174    * this dataset with the right GnuplotDataset::Add
175    * method.
176    */
177   void SetErrorBars (enum ErrorBars errorBars);
178 
179   /**
180    * \param x x coord to new data point
181    * \param y y coord to new data point
182    *
183    * Use this method with error bar style NONE.
184    */
185   void Add (double x, double y);
186 
187   /**
188    * \param x x coord to new data point
189    * \param y y coord to new data point
190    * \param errorDelta x and y data point uncertainty
191    *
192    * Use this method with error bar style X or Y.
193    */
194   void Add (double x, double y, double errorDelta);
195 
196   /**
197    * \param x x coord to new data point
198    * \param y y coord to new data point
199    * \param xErrorDelta x data point uncertainty
200    * \param yErrorDelta y data point uncertainty
201    *
202    * Use this method with error bar style XY.
203    */
204   void Add (double x, double y, double xErrorDelta, double yErrorDelta);
205 
206   /**
207    * Add an empty line in the data output sequence. Empty lines in the plot
208    * data break continuous lines and do other things in the output.
209    */
210   void AddEmptyLine ();
211 
212 private:
213 
214   /**
215    * A point in a 2D plot
216    */
217   struct Point {
218     bool empty; //!< the point is empty
219     double x; //!< X coordinate
220     double y; //!< Y coordinate
221     double dx; //!< X error delta
222     double dy; //!< Y error delta
223   };
224 
225   /// The set of points in the dataset
226   typedef std::vector<struct Point> PointSet;
227 
228   static enum Style m_defaultStyle; //!< default plot style
229   static enum ErrorBars m_defaultErrorBars;  //!< default error bars type
230 
231   /// Forward declaration of the internal data class.
232   struct Data2d;
233 };
234 
235 /**
236  * \ingroup gnuplot
237  *
238  * \brief Class to represent a 2D function expression plot.
239  *
240  * Since the function expression is not escaped, styles and extras could just
241  * as well be included in the expression string.
242  */
243 class Gnuplot2dFunction : public GnuplotDataset
244 {
245 public:
246   /**
247    * \param title the title to be associated to this dataset.
248    * \param function function to plot
249    *
250    * Create an function dataset. Usually, the dataset's title is displayed in
251    * the legend box.
252    */
253   Gnuplot2dFunction (const std::string& title = "Untitled", const std::string& function = "");
254 
255   /**
256    * \param function new function string to set
257    */
258   void SetFunction (const std::string& function);
259 
260 private:
261 
262   /// Forward declaration of the internal data class.
263   struct Function2d;
264 };
265 
266 /**
267  * \ingroup gnuplot
268  *
269  * \brief Class to represent a 3D points plot. Set the line or points style
270  * using SetStyle() and set points using Add().
271  */
272 class Gnuplot3dDataset : public GnuplotDataset
273 {
274 public:
275   /**
276    * \param title the title to be associated to this dataset.
277    *
278    * Create an empty dataset. Usually, the dataset's title is
279    * displayed in the legend box.
280    */
281   Gnuplot3dDataset (const std::string& title = "Untitled");
282 
283   /**
284    * Change default style for all newly created objects.
285    * \param style the style of plotting to use for newly created datasets.
286    */
287   static void SetDefaultStyle (const std::string& style);
288 
289   /**
290    * \param style the style of plotting to use for this dataset.
291    */
292   void SetStyle (const std::string& style);
293 
294   /**
295    * \param x x coord to new data point
296    * \param y y coord to new data point
297    * \param z z coord to new data point
298    *
299    * Use this method to add a new 3D point
300    */
301   void Add (double x, double y, double z);
302 
303   /**
304    * Add an empty line in the data output sequence. Empty lines in the plot
305    * data break continuous lines and do other things in the output.
306    */
307   void AddEmptyLine ();
308 
309 private:
310 
311   /**
312    * A point in a 3D plot
313    */
314   struct Point {
315     bool empty; //!< the point is empty
316     double x; //!< X coordinate
317     double y; //!< Y coordinate
318     double z; //!< Z coordinate
319   };
320 
321   /// The set of points in the dataset
322   typedef std::vector<struct Point> PointSet;
323 
324   static std::string m_defaultStyle; //!< default plot style
325 
326   /// Forward declaration of the internal data class.
327   struct Data3d;
328 };
329 
330 /**
331  * \ingroup gnuplot
332  *
333  * \brief Class to represent a 3D function expression plot.
334  *
335  * Since the function expression is not escaped, styles and extras could just as
336  * well be included in the expression string. The only difference to
337  * Gnuplot2dFunction is the splot command string.
338  */
339 class Gnuplot3dFunction : public GnuplotDataset
340 {
341 public:
342   /**
343    * \param title the title to be associated to this dataset.
344    * \param function function to plot
345    *
346    * Create an function dataset. Usually, the dataset's title is displayed in
347    * the legend box.
348    */
349   Gnuplot3dFunction (const std::string& title = "Untitled", const std::string& function = "");
350 
351   /**
352    * \param function new function string to set
353    */
354   void SetFunction (const std::string& function);
355 
356 private:
357 
358   /// Forward declaration of the internal data class.
359   struct Function3d;
360 };
361 
362 /**
363  * \ingroup gnuplot
364  *
365  * \brief a simple class to generate gnuplot-ready plotting commands
366  *        from a set of datasets.
367  *
368  * This class really represents a single graph on which multiple datasets
369  * can be plotted.
370  */
371 class Gnuplot
372 {
373 public:
374   /**
375    * \param outputFilename the name of the file where the rendering of the
376    *        graph will be generated if you feed the command stream output by
377    *        Gnuplot::GenerateOutput to the gnuplot program.
378    * \param title title line of the plot page
379    */
380   Gnuplot (const std::string& outputFilename="", const std::string& title = "");
381 
382   /**
383    * \param outputFilename the name of the file where the rendering of the
384    *        graph will be generated if you feed the command stream output by
385    *        Gnuplot::GenerateOutput to the gnuplot program.
386    */
387   void SetOutputFilename (const std::string& outputFilename);
388 
389   /**
390    * Crude attempt to auto-detect the correct terminal setting by inspecting
391    * the filename's extension.
392    * \param filename output filename
393    * \return File extension of the provided filename
394    */
395   static std::string DetectTerminal (const std::string& filename);
396 
397   /**
398    * \param terminal terminal setting string for output. The default terminal
399    * string is "png"
400    */
401   void SetTerminal (const std::string& terminal);
402 
403   /**
404    * \param title set new plot title string to use for this plot.
405    */
406   void SetTitle (const std::string& title);
407 
408   /**
409    * \param xLegend the legend for the x horizontal axis
410    * \param yLegend the legend for the y vertical axis
411    */
412   void SetLegend (const std::string& xLegend, const std::string& yLegend);
413 
414   /**
415    * \param extra set extra gnuplot directive for output.
416    */
417   void SetExtra (const std::string& extra);
418 
419   /**
420    * \param extra append extra gnuplot directive for output.
421    */
422   void AppendExtra (const std::string& extra);
423 
424   /**
425    * \param dataset add a dataset to the graph to be plotted.
426    */
427   void AddDataset (const GnuplotDataset& dataset);
428 
429   /**
430    * \param os the output stream on which the relevant gnuplot
431    * commands should be generated. Including output file and terminal
432    * headers.
433    *
434    * \brief Writes gnuplot commands and data values to a single
435    * output stream.
436    */
437   void GenerateOutput (std::ostream &os);
438 
439   /**
440    * \param osControl the output stream on which the relevant gnuplot
441    * control commands should be generated. Including output file and
442    * terminal headers.
443    * \param osData the output stream on which the relevant gnuplot
444    * data values should be generated.
445    * \param dataFileName the name for the data file that will be
446    * written.
447    *
448    * \brief Writes gnuplot commands and data values to two
449    * different outputs streams.
450    */
451   void GenerateOutput (std::ostream &osControl,
452                        std::ostream &osData,
453                        std::string dataFileName);
454 
455   /**
456    * \param index the index for the data stream in the data file.
457    *
458    * \brief Sets the current data stream index in the data file.
459    */
460   void SetDataFileDatasetIndex (unsigned int index);
461 
462 private:
463   /// Type for Datasets to be used in plots
464   typedef std::vector<GnuplotDataset> Datasets;
465 
466   std::string m_outputFilename; //!< Output file name
467   std::string m_terminal; //!< Gnuplot "terminal" to use
468 
469   Datasets m_datasets; //!< Data sets
470 
471   std::string m_title; //!< Plot title
472   std::string m_xLegend; //!< X axis legend
473   std::string m_yLegend; //!< Y axis legend
474   std::string m_extra; //!< extra parameters for the plot
475 
476   bool m_generateOneOutputFile; //!< true if only one plot will be generated
477 
478   unsigned int m_dataFileDatasetIndex; //!< Data set index to plot
479 };
480 
481 /**
482  * \ingroup gnuplot
483  *
484  * \brief a simple class to group together multiple gnuplots into one file,
485  * e.g. for PDF multi-page output terminals.
486  */
487 class GnuplotCollection
488 {
489 public:
490   /**
491    * \param outputFilename the name of the file where the rendering of the
492    *        graph will be generated if you feed the command stream output by
493    *        GnuplotCollection::GenerateOutput to the gnuplot program.
494    */
495   GnuplotCollection (const std::string& outputFilename);
496 
497   /**
498    * \param terminal terminal setting string for output. The default terminal
499    * string is guessed from the output filename's extension.
500    */
501   void SetTerminal (const std::string& terminal);
502 
503   /**
504    * \param plot add a plot to the collection to be plotted.
505    */
506   void AddPlot (const Gnuplot& plot);
507 
508   /**
509    * Return a pointer to one of the added plots.
510    * \param id  index of plot to return
511    * \return    reference to plot, throws std::range_error if it does not exist.
512    */
513   Gnuplot& GetPlot (unsigned int id);
514 
515   /**
516    * \param os the output stream on which the relevant gnuplot commands should
517    * be generated.
518    */
519   void GenerateOutput (std::ostream &os);
520 
521   /**
522    * \param osControl the output stream on which the relevant gnuplot
523    * control commands should be generated. Including output file and
524    * terminal headers.
525    * \param osData the output stream on which the relevant gnuplot
526    * data values should be generated.
527    * \param dataFileName the name for the data file that will be
528    * written.
529    */
530   void GenerateOutput (std::ostream &osControl,
531                        std::ostream &osData,
532                        std::string dataFileName);
533 
534 private:
535   /// Type of the Gnuplot collection
536   typedef std::vector<Gnuplot> Plots;
537 
538   std::string m_outputFilename; //!< Output file name
539   std::string m_terminal; //!< Gnuplot "terminal" to use
540 
541   Plots       m_plots; //!< Plots in the collection
542 };
543 
544 } // namespace ns3
545 
546 #endif /* GNUPLOT_H */
547