/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * 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 * published by the Free Software Foundation; * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Original Author: Mathieu Lacage * Enhancements: Timo Bingmann */ #include "gnuplot.h" #include "ns3/assert.h" #include #include namespace ns3 { // --- 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) { } 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 Gnuplot2dDataset::Add (double x, double y) { 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; reinterpret_cast(m_data)->m_pointset.push_back (data); } void Gnuplot2dDataset::Add (double x, double y, double errorDelta) { 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; reinterpret_cast(m_data)->m_pointset.push_back (data); } void 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::SetExtra (const std::string& extra) { m_extra = extra; } void Gnuplot::AppendExtra (const std::string& extra) { m_extra += "\n"; m_extra += extra; } void Gnuplot::AddDataset (const GnuplotDataset& dataset) { m_datasets.push_back (dataset); } void Gnuplot::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; 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 ();) { 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++) { 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