# HG changeset patch # User Timo Bingmann # Date 1231491249 -3600 # Node ID 359e0a1b5cf8fb9be5e6aa01034ad083b6f64be8 # Parent 200676be351f473924663ffc037b3a3f2996a8b7 extend gnuplot classes diff -r 200676be351f -r 359e0a1b5cf8 examples/wifi-adhoc.cc --- a/examples/wifi-adhoc.cc Fri Jan 09 09:39:54 2009 +0100 +++ b/examples/wifi-adhoc.cc Fri Jan 09 09:54:09 2009 +0100 @@ -36,7 +36,7 @@ public: Experiment (); Experiment (std::string name); - GnuplotDataset Run (const WifiHelper &wifi, const YansWifiPhyHelper &wifiPhy, const YansWifiChannelHelper &wifiChannel); + Gnuplot2dDataset Run (const WifiHelper &wifi, const YansWifiPhyHelper &wifiPhy, const YansWifiChannelHelper &wifiChannel); private: void ReceivePacket (Ptr socket); void SetPosition (Ptr node, Vector position); @@ -45,17 +45,16 @@ Ptr SetupPacketReceive (Ptr node); uint32_t m_bytesTotal; - GnuplotDataset m_output; + Gnuplot2dDataset m_output; }; Experiment::Experiment () - : m_output () {} Experiment::Experiment (std::string name) : m_output (name) { - m_output.SetStyle (GnuplotDataset::LINES); + m_output.SetStyle (Gnuplot2dDataset::LINES); } void @@ -109,7 +108,7 @@ return sink; } -GnuplotDataset +Gnuplot2dDataset Experiment::Run (const WifiHelper &wifi, const YansWifiPhyHelper &wifiPhy, const YansWifiChannelHelper &wifiChannel) { m_bytesTotal = 0; @@ -173,7 +172,7 @@ WifiHelper wifi = WifiHelper::Default (); YansWifiPhyHelper wifiPhy = YansWifiPhyHelper::Default (); YansWifiChannelHelper wifiChannel = YansWifiChannelHelper::Default (); - GnuplotDataset dataset; + Gnuplot2dDataset dataset; wifi.SetMac ("ns3::AdhocWifiMac"); diff -r 200676be351f -r 359e0a1b5cf8 src/contrib/gnuplot.cc --- a/src/contrib/gnuplot.cc Fri Jan 09 09:39:54 2009 +0100 +++ b/src/contrib/gnuplot.cc Fri Jan 09 09:54:09 2009 +0100 @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2007 INRIA + * Copyright (c) 2007 INRIA, 2008 Timo Bingmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,178 +15,719 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * Author: Mathieu Lacage + * Original Author: Mathieu Lacage + * Enhancements: Timo Bingmann */ #include "gnuplot.h" #include "ns3/assert.h" #include +#include namespace ns3 { -GnuplotDataset::GnuplotDataset () - : m_title ("untitled"), - m_style (LINES), - m_errorBars (NONE) -{} +// --- GnuplotDataset::Data ------------------------------------------------ // + +struct GnuplotDataset::Data +{ + // *** Data Variables *** + + unsigned int m_references; + + std::string m_title; + std::string m_extra; + + /** + * Initializes the reference counter to 1 and sets m_title and m_extra. + */ + Data(const std::string& title); + + /// Required. + virtual ~Data(); + + /** + * Returns "plot" or "splot". + */ + virtual std::string GetCommand() const = 0; + + /** + * Prints the plot description used as argument to (s)plot. Either the + * function expression or a datafile description. Should include m_title and + * m_extra in the output. + */ + virtual void PrintExpression(std::ostream &os) const = 0; + + /** + * Print the inline data file contents trailing the plot command. Empty for + * functions. + */ + virtual void PrintDatafile(std::ostream &os) const = 0; +}; + +GnuplotDataset::Data::Data(const std::string& title) + : m_references(1), + m_title(title), + m_extra(m_defaultExtra) +{ +} + +GnuplotDataset::Data::~Data() +{ +} + +// --- GnuplotDataset ------------------------------------------------------ // + +std::string GnuplotDataset::m_defaultExtra = ""; + +GnuplotDataset::GnuplotDataset (struct Data* data) + : m_data(data) +{ +} + +GnuplotDataset::GnuplotDataset (const GnuplotDataset& original) + : m_data(original.m_data) +{ + ++m_data->m_references; +} + +GnuplotDataset::~GnuplotDataset() +{ + if (--m_data->m_references == 0) + delete m_data; +} + +GnuplotDataset& GnuplotDataset::operator= (const GnuplotDataset& original) +{ + if (this != &original) + { + if (--m_data->m_references == 0) + delete m_data; + + m_data = original.m_data; + ++m_data->m_references; + } + return *this; +} + +void +GnuplotDataset::SetTitle (const std::string& title) +{ + m_data->m_title = title; +} + +void +GnuplotDataset::SetDefaultExtra (const std::string& extra) +{ + m_defaultExtra = extra; +} +void +GnuplotDataset::SetExtra (const std::string& extra) +{ + m_data->m_extra = extra; +} + +// --- Gnuplot2dDataset::Data2d -------------------------------------------- // + +struct Gnuplot2dDataset::Data2d : public GnuplotDataset::Data +{ + // *** Data Variables *** + + enum Style m_style; + enum ErrorBars m_errorBars; + + PointSet m_pointset; + + /** + * Initializes with the values from m_defaultStyle and m_defaultErrorBars. + */ + Data2d(const std::string& title); + + virtual std::string GetCommand() const; + virtual void PrintExpression(std::ostream &os) const; + virtual void PrintDatafile(std::ostream &os) const; +}; + +Gnuplot2dDataset::Data2d::Data2d(const std::string& title) + : Data(title), + m_style(m_defaultStyle), + m_errorBars(m_defaultErrorBars) +{ +} -GnuplotDataset::GnuplotDataset (std::string title) - : m_title (title), - m_style (LINES), - m_errorBars (NONE) -{} +std::string +Gnuplot2dDataset::Data2d::GetCommand() const +{ + return "plot"; +} + +void +Gnuplot2dDataset::Data2d::PrintExpression(std::ostream &os) const +{ + os << "'-' "; + + if (m_title.size()) + os << " title '" << m_title << "'"; + + switch (m_style) { + case LINES: + os << " with lines"; + break; + case POINTS: + switch (m_errorBars) + { + case NONE: + os << " with points"; + break; + case X: + os << " with xerrorbars"; + break; + case Y: + os << " with yerrorbars"; + break; + case XY: + os << " with xyerrorbars"; + break; + } + break; + case LINES_POINTS: + switch (m_errorBars) + { + case NONE: + os << " with linespoints"; + break; + case X: + os << " with errorlines"; + break; + case Y: + os << " with yerrorlines"; + break; + case XY: + os << " with xyerrorlines"; + break; + } + break; + case DOTS: + os << " with dots"; + break; + case IMPULSES: + os << " with impulses"; + break; + case STEPS: + os << " with steps"; + break; + case FSTEPS: + os << " with fsteps"; + break; + case HISTEPS: + os << " with histeps"; + break; + } + + if (m_extra.size()) + os << " " << m_extra; +} + +void +Gnuplot2dDataset::Data2d::PrintDatafile(std::ostream &os) const +{ + for (PointSet::const_iterator i = m_pointset.begin (); + i != m_pointset.end (); ++i) + { + if (i->empty) { + os << std::endl; + continue; + } + + switch (m_errorBars) { + case NONE: + os << i->x << " " << i->y << std::endl; + break; + case X: + os << i->x << " " << i->y << " " << i->dx << std::endl; + break; + case Y: + os << i->x << " " << i->y << " " << i->dy << std::endl; + break; + case XY: + os << i->x << " " << i->y << " " << i->dx << " " << i->dy << std::endl; + break; + } + } + os << "e" << std::endl; +} + +// --- Gnuplot2dDataset ---------------------------------------------------- // + +enum Gnuplot2dDataset::Style Gnuplot2dDataset::m_defaultStyle = LINES; +enum Gnuplot2dDataset::ErrorBars Gnuplot2dDataset::m_defaultErrorBars = NONE; + +Gnuplot2dDataset::Gnuplot2dDataset (const std::string& title) + : GnuplotDataset( new Data2d(title) ) +{ +} + +void +Gnuplot2dDataset::SetDefaultStyle (enum Style style) +{ + m_defaultStyle = style; +} +void +Gnuplot2dDataset::SetStyle (enum Style style) +{ + reinterpret_cast(m_data)->m_style = style; +} + +void +Gnuplot2dDataset::SetDefaultErrorBars (enum ErrorBars errorBars) +{ + m_defaultErrorBars = errorBars; +} +void +Gnuplot2dDataset::SetErrorBars (enum ErrorBars errorBars) +{ + reinterpret_cast(m_data)->m_errorBars = errorBars; +} + void -GnuplotDataset::SetStyle (enum Style style) -{ - m_style = style; -} -void -GnuplotDataset::SetErrorBars (enum ErrorBars errorBars) +Gnuplot2dDataset::Add (double x, double y) { - m_errorBars = errorBars; -} -void -GnuplotDataset::Add (double x, double y) -{ - NS_ASSERT (m_errorBars == NONE); - struct Data data; + NS_ASSERT (reinterpret_cast(m_data)->m_errorBars == NONE); + + struct Point data; + data.empty = false; data.x = x; data.y = y; data.dx = 0.0; data.dy = 0.0; - m_dataset.push_back (data); + reinterpret_cast(m_data)->m_pointset.push_back (data); } + void -GnuplotDataset::Add (double x, double y, double errorDelta) +Gnuplot2dDataset::Add (double x, double y, double errorDelta) { - NS_ASSERT (m_errorBars == X || m_errorBars == Y); - struct Data data; + NS_ASSERT ( reinterpret_cast(m_data)->m_errorBars == X || + reinterpret_cast(m_data)->m_errorBars == Y ); + + struct Point data; + data.empty = false; data.x = x; data.y = y; data.dx = errorDelta; data.dy = errorDelta; - m_dataset.push_back (data); -} - -Gnuplot::Gnuplot (std::string pngFilename) - : m_pngFilename (pngFilename) -{} - -Gnuplot::~Gnuplot () -{ - for (Datasets::const_iterator i = m_datasets.begin (); i != m_datasets.end (); i++) - { - delete *i; - } - m_datasets.clear (); + reinterpret_cast(m_data)->m_pointset.push_back (data); } void -Gnuplot::SetLegend (std::string xLegend, std::string yLegend) +Gnuplot2dDataset::Add (double x, double y, double minY, double maxY) +{ + NS_ASSERT ( reinterpret_cast(m_data)->m_errorBars == X || + reinterpret_cast(m_data)->m_errorBars == Y ); + + struct Point data; + data.empty = false; + data.x = x; + data.y = y; + data.dx = minY; + data.dy = maxY; + reinterpret_cast(m_data)->m_pointset.push_back (data); +} + +void +Gnuplot2dDataset::AddEmptyLine() +{ + struct Point data; + data.empty = true; + reinterpret_cast(m_data)->m_pointset.push_back (data); +} + +// --- Gnuplot2dFunction::Function2d --------------------------------------- // + +struct Gnuplot2dFunction::Function2d : public GnuplotDataset::Data +{ + // *** Data Variables *** + + std::string m_function; + + /** + * Initializes with the function and title. + */ + Function2d(const std::string& title, const std::string& function); + + virtual std::string GetCommand() const; + virtual void PrintExpression(std::ostream &os) const; + virtual void PrintDatafile(std::ostream &os) const; +}; + +Gnuplot2dFunction::Function2d::Function2d(const std::string& title, const std::string& function) + : Data(title), + m_function(function) +{ +} + +std::string +Gnuplot2dFunction::Function2d::GetCommand() const +{ + return "plot"; +} + +void +Gnuplot2dFunction::Function2d::PrintExpression(std::ostream &os) const +{ + os << m_function; + + if (m_title.size()) + os << " title '" << m_title << "'"; + + if (m_extra.size()) + os << " " << m_extra; +} + +void +Gnuplot2dFunction::Function2d::PrintDatafile(std::ostream &os) const +{ +} + +// --- Gnuplot2dFunction --------------------------------------------------- // + +Gnuplot2dFunction::Gnuplot2dFunction (const std::string& title, const std::string& function) + : GnuplotDataset( new Function2d(title, function) ) +{ +} + +void +Gnuplot2dFunction::SetFunction (const std::string& function) +{ + reinterpret_cast(m_data)->m_function = function; +} + +// --- Gnuplot3dDataset::Data3d -------------------------------------------- // + +struct Gnuplot3dDataset::Data3d : public GnuplotDataset::Data +{ + // *** Data Variables *** + + std::string m_style; + + PointSet m_pointset; + + /** + * Initializes with value from m_defaultStyle. + */ + Data3d(const std::string& title); + + virtual std::string GetCommand() const; + virtual void PrintExpression(std::ostream &os) const; + virtual void PrintDatafile(std::ostream &os) const; +}; + +Gnuplot3dDataset::Data3d::Data3d(const std::string& title) + : Data(title), + m_style(m_defaultStyle) +{ +} + +std::string +Gnuplot3dDataset::Data3d::GetCommand() const +{ + return "splot"; +} + +void +Gnuplot3dDataset::Data3d::PrintExpression(std::ostream &os) const +{ + os << "'-' "; + + if (m_style.size()) + os << " " << m_style; + + if (m_title.size()) + os << " title '" << m_title << "'"; + + if (m_extra.size()) + os << " " << m_extra; +} + +void +Gnuplot3dDataset::Data3d::PrintDatafile(std::ostream &os) const +{ + for (PointSet::const_iterator i = m_pointset.begin (); + i != m_pointset.end (); ++i) + { + if (i->empty) { + os << std::endl; + continue; + } + + os << i->x << " " << i->y << " " << i->z << std::endl; + } + os << "e" << std::endl; +} + +// --- Gnuplot3dDataset ---------------------------------------------------- // + +std::string Gnuplot3dDataset::m_defaultStyle = ""; + +Gnuplot3dDataset::Gnuplot3dDataset (const std::string& title) + : GnuplotDataset( new Data3d(title) ) +{ +} + +void +Gnuplot3dDataset::SetDefaultStyle (const std::string& style) +{ + m_defaultStyle = style; +} +void +Gnuplot3dDataset::SetStyle (const std::string& style) +{ + reinterpret_cast(m_data)->m_style = style; +} + +void +Gnuplot3dDataset::Add (double x, double y, double z) +{ + struct Point data; + data.empty = false; + data.x = x; + data.y = y; + data.z = z; + reinterpret_cast(m_data)->m_pointset.push_back (data); +} + +void +Gnuplot3dDataset::AddEmptyLine() +{ + struct Point data; + data.empty = true; + reinterpret_cast(m_data)->m_pointset.push_back (data); +} + +// --- Gnuplot3dFunction::Function3d --------------------------------------- // + +struct Gnuplot3dFunction::Function3d : public GnuplotDataset::Data +{ + // *** Data Variables *** + + std::string m_function; + + /** + * Initializes with the function and title. + */ + Function3d(const std::string& title, const std::string& function); + + virtual std::string GetCommand() const; + virtual void PrintExpression(std::ostream &os) const; + virtual void PrintDatafile(std::ostream &os) const; +}; + +Gnuplot3dFunction::Function3d::Function3d(const std::string& title, const std::string& function) + : Data(title), + m_function(function) +{ +} + +std::string +Gnuplot3dFunction::Function3d::GetCommand() const +{ + return "splot"; +} + +void +Gnuplot3dFunction::Function3d::PrintExpression(std::ostream &os) const +{ + os << m_function; + + if (m_title.size()) + os << " title '" << m_title << "'"; + + if (m_extra.size()) + os << " " << m_extra; +} + +void +Gnuplot3dFunction::Function3d::PrintDatafile(std::ostream &os) const +{ +} + +// --- Gnuplot3dFunction --------------------------------------------------- // + +Gnuplot3dFunction::Gnuplot3dFunction (const std::string& title, const std::string& function) + : GnuplotDataset( new Function3d(title, function) ) +{ +} + +void +Gnuplot3dFunction::SetFunction (const std::string& function) +{ + reinterpret_cast(m_data)->m_function = function; +} + +// ------------------------------------------------------------------------- // + +Gnuplot::Gnuplot (const std::string& outputFilename, const std::string& title) + : m_outputFilename(outputFilename), + m_terminal( DetectTerminal(outputFilename) ), + m_title(title) +{ +} + +std::string Gnuplot::DetectTerminal (const std::string& filename) +{ + std::string::size_type dotpos = filename.rfind('.'); + if (dotpos == std::string::npos) return ""; + + if (filename.substr(dotpos) == ".png") { + return "png"; + } + else if (filename.substr(dotpos) == ".pdf") { + return "pdf"; + } + + return ""; +} + +void +Gnuplot::SetTerminal (const std::string& terminal) +{ + m_terminal = terminal; +} + +void +Gnuplot::SetTitle (const std::string& title) +{ + m_title = title; +} + +void +Gnuplot::SetLegend (const std::string& xLegend, const std::string& yLegend) { m_xLegend = xLegend; m_yLegend = yLegend; } -void -Gnuplot::AddDataset (const GnuplotDataset &dataset) +void +Gnuplot::SetExtra (const std::string& extra) { - m_datasets.push_back (new GnuplotDataset (dataset)); + m_extra = extra; +} + +void +Gnuplot::AppendExtra (const std::string& extra) +{ + m_extra += "\n"; + m_extra += extra; } void -Gnuplot::GenerateOutput (std::ostream &os) +Gnuplot::AddDataset (const GnuplotDataset& dataset) +{ + m_datasets.push_back (dataset); +} + +void +Gnuplot::GenerateOutput (std::ostream &os) const { - os << "set terminal png" << std::endl; - os << "set output '" << m_pngFilename << "'" << std::endl; - os << "set xlabel '" << m_xLegend << "'" << std::endl; - os << "set ylabel '" << m_yLegend << "'" << std::endl; - os << "plot "; + if (m_terminal.size()) + os << "set terminal " << m_terminal << std::endl; + + if (m_outputFilename.size()) + os << "set output '" << m_outputFilename << "'" << std::endl; + + if (m_title.size()) + os << "set title '" << m_title << "'" << std::endl; + + if (m_xLegend.size()) + os << "set xlabel '" << m_xLegend << "'" << std::endl; + + if (m_yLegend.size()) + os << "set ylabel '" << m_yLegend << "'" << std::endl; + + if (m_extra.size()) + os << m_extra << std::endl; + + if (m_datasets.empty()) + return; + + // Determine the GetCommand() values of all datasets included. Check that all + // are equal and print the command. + + std::string command = m_datasets.begin()->m_data->GetCommand(); + + for (Datasets::const_iterator i = m_datasets.begin () + 1; + i != m_datasets.end (); ++i) + { + NS_ASSERT_MSG(command == i->m_data->GetCommand(), + "Cannot mix 'plot' and 'splot' GnuplotDatasets."); + } + + os << command << " "; + + // Print all dataset expressions + for (Datasets::const_iterator i = m_datasets.begin (); i != m_datasets.end ();) { - os << "'-' title '" << (*i)->m_title << "'"; - switch ((*i)->m_style) { - case GnuplotDataset::LINES: - os << " with lines"; - break; - case GnuplotDataset::POINTS: - switch ((*i)->m_errorBars) - { - case GnuplotDataset::NONE: - os << " with points"; - break; - case GnuplotDataset::X: - os << " with xerrorbars"; - break; - case GnuplotDataset::Y: - os << " with yerrorbars"; - break; - case GnuplotDataset::XY: - os << " with xyerrorbars"; - break; - } - break; - case GnuplotDataset::LINES_POINTS: - switch ((*i)->m_errorBars) - { - case GnuplotDataset::NONE: - os << " with linespoints"; - break; - case GnuplotDataset::X: - os << " with xerrorlines"; - break; - case GnuplotDataset::Y: - os << " with yerrorlines"; - break; - case GnuplotDataset::XY: - os << " with xyerrorlines"; - break; - } - break; - case GnuplotDataset::DOTS: - os << " with dots"; - break; - case GnuplotDataset::IMPULSES: - os << " with impulses"; - break; - case GnuplotDataset::STEPS: - os << " with steps"; - break; - case GnuplotDataset::FSTEPS: - os << " with fsteps"; - break; - case GnuplotDataset::HISTEPS: - os << " with histeps"; - break; - } + i->m_data->PrintExpression(os); + i++; + if (i != m_datasets.end ()) { os << ", "; } } os << std::endl; + + // followed by the inline datafile. + for (Datasets::const_iterator i = m_datasets.begin (); i != m_datasets.end (); i++) { - for (GnuplotDataset::Dataset::const_iterator j = (*i)->m_dataset.begin (); - j != (*i)->m_dataset.end (); j++) - { - switch ((*i)->m_errorBars) { - case GnuplotDataset::NONE: - os << j->x << " " << j->y << std::endl; - break; - case GnuplotDataset::X: - os << j->x << " " << j->y << " " << j->dx << std::endl; - break; - case GnuplotDataset::Y: - os << j->x << " " << j->y << " " << j->dy << std::endl; - break; - case GnuplotDataset::XY: - os << j->x << " " << j->y << " " << j->dx << " " << j->dy << std::endl; - break; - } - } - os << "e" << std::endl; + i->m_data->PrintDatafile(os); } } +// ------------------------------------------------------------------------- // + +GnuplotCollection::GnuplotCollection (const std::string& outputFilename) + : m_outputFilename(outputFilename), + m_terminal( Gnuplot::DetectTerminal(outputFilename) ) +{ +} + +void +GnuplotCollection::SetTerminal (const std::string& terminal) +{ + m_terminal = terminal; +} + +void +GnuplotCollection::AddPlot (const Gnuplot& plot) +{ + m_plots.push_back (plot); +} + +Gnuplot& +GnuplotCollection::GetPlot(unsigned int id) +{ + if (id >= m_plots.size()) + throw(std::range_error("Gnuplot id is out of range")); + else + return m_plots[id]; +} + +void +GnuplotCollection::GenerateOutput (std::ostream &os) const +{ + if (m_terminal.size()) + os << "set terminal " << m_terminal << std::endl; + + if (m_outputFilename.size()) + os << "set output '" << m_outputFilename << "'" << std::endl; + + for (Plots::const_iterator i = m_plots.begin (); i != m_plots.end (); ++i) + { + i->GenerateOutput (os); + } +} + +// ------------------------------------------------------------------------- // + } // namespace ns3 diff -r 200676be351f -r 359e0a1b5cf8 src/contrib/gnuplot.h --- a/src/contrib/gnuplot.h Fri Jan 09 09:39:54 2009 +0100 +++ b/src/contrib/gnuplot.h Fri Jan 09 09:54:09 2009 +0100 @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2007 INRIA + * Copyright (c) 2007 INRIA, 2008 Timo Bingmann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,7 +15,8 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * Author: Mathieu Lacage + * Original Author: Mathieu Lacage + * Enhancements: Timo Bingmann */ #ifndef GNUPLOT_H #define GNUPLOT_H @@ -27,11 +28,83 @@ namespace ns3 { /** - * \brief store a dataset to be used by ns3::Gnuplot + * \brief Abstract class to store a plot line to be used by ns3::Gnuplot. + * + * This class contains a reference counted data object in m_data. The data + * object contains different structs derived from struct Data by subclasses. */ class GnuplotDataset { public: + + /** + * Reference-counting copy constructor. + */ + GnuplotDataset (const GnuplotDataset& original); + + /** + * Reference-counting destructor. + */ + ~GnuplotDataset(); + + /** + * Reference-counting assignment operator. + */ + GnuplotDataset& operator= (const GnuplotDataset& original); + + /** + * \brief Change line title. + * \param title the new title string to use for this dataset. + */ + void SetTitle (const std::string& title); + + /** + * \brief Change extra formatting style parameters for newly created objects. + * \param extra extra formatting + */ + static void SetDefaultExtra (const std::string& extra); + + /** + * \brief Add extra formatting parameters to this dataset. + * \param extra extra formatting + */ + void SetExtra (const std::string& extra); + +protected: + + /// Friend because it accesses m_data and it's virtual functions directly in + /// GenerateOutput(). + friend class Gnuplot; + + /** + * \brief Extra gnuplot parameters set on every newly created dataset. + */ + static std::string m_defaultExtra; + + /** + * \brief Derived classes subclass this struct and add their own data fields. + */ + struct Data; + + /** + * Called by constructors of derived classes. + * \param data the reference counted data object representing this dataset. + */ + GnuplotDataset (struct Data* data); + + /** + * Reference counted data object. + */ + struct Data* m_data; +}; + +/** + * \brief Class to represent a 2D points plot. Set the line or points style + * using SetStyle() and set points using Add(). + */ +class Gnuplot2dDataset : public GnuplotDataset +{ +public: /** * The plotting style to use for this dataset. */ @@ -45,6 +118,7 @@ FSTEPS, HISTEPS, }; + /** * Whether errorbars should be used for this dataset. */ @@ -56,20 +130,30 @@ }; /** - * Create an empty dataset without any title. - */ - GnuplotDataset (); - /** * \param title the title to be associated to this dataset. * * Create an empty dataset. Usually, the dataset's title is * displayed in the legend box. */ - GnuplotDataset (std::string title); + Gnuplot2dDataset (const std::string& title = "Untitled"); + + /** + * Change default style for all newly created objects. + * \param style the style of plotting to use for newly created datasets. + */ + static void SetDefaultStyle (enum Style style); + /** * \param style the style of plotting to use for this dataset. */ void SetStyle (enum Style style); + + /** + * Change default errorbars style for all newly created objects. + * \param errorBars the style of errorbars to use for newly created datasets. + */ + static void SetDefaultErrorBars (enum ErrorBars errorBars); + /** * \param errorBars the style of errorbars to display. * @@ -79,6 +163,7 @@ * method. */ void SetErrorBars (enum ErrorBars errorBars); + /** * \param x x coord to new data point * \param y y coord to new data point @@ -86,27 +171,164 @@ * Use this method with error bar style NONE. */ void Add (double x, double y); + /** * \param x x coord to new data point * \param y y coord to new data point - * \param errorDelta + * \param errorDelta data point error range. * * Use this method with error bar style X or Y. */ void Add (double x, double y, double errorDelta); + + /** + * \param x x coord to new data point + * \param y y coord to new data point + * \param minY minimum error data point + * \param maxY maximum error data point + * + * Use this method with error bar style X or Y. + */ + void Add (double x, double y, double minY, double maxY); + + /** + * Add an empty line in the data output sequence. Empty lines in the plot + * data break continuous lines and do other things in the output. + */ + void AddEmptyLine(); + private: - friend class Gnuplot; - struct Data { + + struct Point { + bool empty; double x; double y; double dx; double dy; }; - typedef std::vector Dataset; - Dataset m_dataset; - std::string m_title; - enum Style m_style; - enum ErrorBars m_errorBars; + + typedef std::vector PointSet; + + static enum Style m_defaultStyle; + static enum ErrorBars m_defaultErrorBars; + + /// Forward declaration of the internal data class. + struct Data2d; +}; + +/** + * \brief Class to represent a 2D function expression plot. + * + * Since the function expression is not escaped, styles and extras could just + * as well be included in the expression string. + */ +class Gnuplot2dFunction : public GnuplotDataset +{ +public: + /** + * \param title the title to be associated to this dataset. + * \param function function to plot + * + * Create an function dataset. Usually, the dataset's title is displayed in + * the legend box. + */ + Gnuplot2dFunction (const std::string& title = "Untitled", const std::string& function = ""); + + /** + * \param function new function string to set + */ + void SetFunction (const std::string& function); + +private: + + /// Forward declaration of the internal data class. + struct Function2d; +}; + +/** + * \brief Class to represent a 3D points plot. Set the line or points style + * using SetStyle() and set points using Add(). + */ +class Gnuplot3dDataset : public GnuplotDataset +{ +public: + /** + * \param title the title to be associated to this dataset. + * + * Create an empty dataset. Usually, the dataset's title is + * displayed in the legend box. + */ + Gnuplot3dDataset (const std::string& title = "Untitled"); + + /** + * Change default style for all newly created objects. + * \param style the style of plotting to use for newly created datasets. + */ + static void SetDefaultStyle (const std::string& style); + + /** + * \param style the style of plotting to use for this dataset. + */ + void SetStyle (const std::string& style); + + /** + * \param x x coord to new data point + * \param y y coord to new data point + * \param z z coord to new data point + * + * Use this method to add a new 3D point + */ + void Add (double x, double y, double z); + + /** + * Add an empty line in the data output sequence. Empty lines in the plot + * data break continuous lines and do other things in the output. + */ + void AddEmptyLine(); + +private: + + struct Point { + bool empty; + double x, y, z; + }; + + typedef std::vector PointSet; + + static std::string m_defaultStyle; + + /// Forward declaration of the internal data class. + struct Data3d; +}; + +/** + * \brief Class to represent a 3D function expression plot. + * + * Since the function expression is not escaped, styles and extras could just as + * well be included in the expression string. The only difference to + * Gnuplot2dFunction is the splot command string. + */ +class Gnuplot3dFunction : public GnuplotDataset +{ +public: + /** + * \param title the title to be associated to this dataset. + * \param function function to plot + * + * Create an function dataset. Usually, the dataset's title is displayed in + * the legend box. + */ + Gnuplot3dFunction (const std::string& title = "Untitled", const std::string& function = ""); + + /** + * \param function new function string to set + */ + void SetFunction (const std::string& function); + +private: + + /// Forward declaration of the internal data class. + struct Function3d; }; /** @@ -120,35 +342,117 @@ { public: /** - * \param pngFilename the name of the file where the png rendering of the + * \param outputFilename the name of the file where the rendering of the * graph will be generated if you feed the command stream output by * Gnuplot::GenerateOutput to the gnuplot program. + * \param title title line of the plot page */ - Gnuplot (std::string pngFilename); - ~Gnuplot (); + Gnuplot (const std::string& outputFilename="", const std::string& title = ""); + + /** + * Crude attempt to auto-detect the correct terminal setting by inspecting + * the filename's extension. + * \param filename output file name + */ + static std::string DetectTerminal(const std::string& filename); + + /** + * \param terminal terminal setting string for output. The default terminal + * string is "png" + */ + void SetTerminal (const std::string& terminal); + + /** + * \param title set new plot title string to use for this plot. + */ + void SetTitle (const std::string& title); /** * \param xLegend the legend for the x horizontal axis * \param yLegend the legend for the y vertical axis */ - void SetLegend (std::string xLegend, std::string yLegend); + void SetLegend (const std::string& xLegend, const std::string& yLegend); + + /** + * \param extra set extra gnuplot directive for output. + */ + void SetExtra (const std::string& extra); + + /** + * \param extra append extra gnuplot directive for output. + */ + void AppendExtra (const std::string& extra); /** * \param dataset add a dataset to the graph to be plotted. */ - void AddDataset (const GnuplotDataset &dataset); + void AddDataset (const GnuplotDataset& dataset); /** - * \param os the output stream on which the relevant gnuplot - * commands should be generated. + * \param os the output stream on which the relevant gnuplot commands should + * be generated. Including output file and terminal headers. */ - void GenerateOutput (std::ostream &os); + void GenerateOutput (std::ostream &os) const; + private: - typedef std::vector Datasets; + typedef std::vector Datasets; + + std::string m_outputFilename; + std::string m_terminal; + Datasets m_datasets; + + std::string m_title; std::string m_xLegend; std::string m_yLegend; - std::string m_pngFilename; + std::string m_extra; +}; + +/** + * \brief a simple class to group together multiple gnuplots into one file, + * e.g. for PDF multi-page output terminals. + */ +class GnuplotCollection +{ +public: + /** + * \param outputFilename the name of the file where the rendering of the + * graph will be generated if you feed the command stream output by + * GnuplotCollection::GenerateOutput to the gnuplot program. + */ + GnuplotCollection (const std::string& outputFilename); + + /** + * \param terminal terminal setting string for output. The default terminal + * string is guessed from the output filename's extension. + */ + void SetTerminal (const std::string& terminal); + + /** + * \param plot add a plot to the collection to be plotted. + */ + void AddPlot (const Gnuplot& plot); + + /** + * Return a pointer to one of the added plots. + * \param id index of plot to return + * \return reference to plot, throws std::range_error if it does not exist. + */ + Gnuplot& GetPlot(unsigned int id); + + /** + * \param os the output stream on which the relevant gnuplot commands should + * be generated. + */ + void GenerateOutput (std::ostream &os) const; + +private: + typedef std::vector Plots; + + std::string m_outputFilename; + std::string m_terminal; + + Plots m_plots; }; } // namespace ns3