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