wxPlot 1.8
Real time plot library for wxWidgets
Loading...
Searching...
No Matches
Tutorial - Add your own plot

To make an own plot you need to first make sure that type of figure you want to use. Head over to the plotfigures.h file. In this case we can se that WXPLOT_FIGURE_2D is available at this moment. It's planned for 3D as well. We decided to use 2D in this tutorial.

#pragma once
typedef enum {
WXPLOT_FIGURE_2D
// Add more here
}WXPLOT_FIGURE;

The next step is to go to the file plottypes.h and see what it can offer. Let's say we want to add a bar plot to wxPlot.

#pragma once
typedef enum {
WXPLOT_TYPE_LINE,
WXPLOT_TYPE_SCATTER,
WXPLOT_TYPE_LINE_SCATTER,
WXPLOT_TYPE_SPLINE,
WXPLOT_TYPE_SPLINE_SCATTER
// Add more here
}WXPLOT_TYPE;

Just add in WXPLOT_TYPE_BAR

#pragma once
typedef enum {
WXPLOT_TYPE_LINE,
WXPLOT_TYPE_SCATTER,
WXPLOT_TYPE_LINE_SCATTER,
WXPLOT_TYPE_SPLINE,
WXPLOT_TYPE_SPLINE_SCATTER,
WXPLOT_TYPE_BAR
// Add more here
}WXPLOT_TYPE;

Further, go to the 2Dfigure.cpp and add new code to all draw-functions and get-functions, e.g this.

wxCoord _2D_Figure::getPlotEndWidth() const {
switch (wxPlotType) {
case WXPLOT_TYPE_LINE:
return proportional.getPlotEndWidth();
case WXPLOT_TYPE_SCATTER:
return proportional.getPlotEndWidth();
case WXPLOT_TYPE_LINE_SCATTER:
return proportional.getPlotEndWidth();
case WXPLOT_TYPE_SPLINE:
return proportional.getPlotEndWidth();
case WXPLOT_TYPE_SPLINE_SCATTER:
return proportional.getPlotEndWidth();
case WXPLOT_TYPE_BAR:
return proportional.getPlotEndWidth(); <--- NEW
default:
return 0;
break;
}
}
void _2D_Figure::drawFigure(wxDC& dc) {
switch (wxPlotType) {
case WXPLOT_TYPE_LINE:
proportional.drawFigure(dc);
break;
case WXPLOT_TYPE_SCATTER:
proportional.drawFigure(dc);
break;
case WXPLOT_TYPE_LINE_SCATTER:
proportional.drawFigure(dc);
break;
case WXPLOT_TYPE_SPLINE:
proportional.drawFigure(dc);
break;
case WXPLOT_TYPE_SPLINE_SCATTER:
proportional.drawFigure(dc);
break;
case WXPLOT_TYPE_BAR:
proportional.drawFigure(dc); <--- NEW
break;
default:
proportional.drawFigure(dc);
break;
}
}

The object proportional is from the class Proportional inside the class _2DFigure. That class writes the following:

  • Rectangle
  • Grid
  • Legend
  • Ticks
  • X, Y, title labels
  • Fonts

Consider the use of proportional when you want to have a proportional plot in both X-axis and Y-axis. If you want to use a logarithmic plot, then you need to use another object rather than proportional, that can draw the rectangle, grid, legend, ticks, labels and fonts. Most of the time, you can just inherit most of the code from the class Proportional. The rectangle, legend, labels, fonts would be the same for a logarithmic plot. Only the grid and the ticks would differ.

After you have done the configuration for the figure. Now it's time for add code for the actuall plot - drawing on the rectangle! In this case, we are focusing on 2D plots. Head over to the 2Dtype.h file and add new new object Bar bar; under private. Don't forget to add #include "bar/bar.h" as well!

#pragma once
#include "line/line.h"
#include "scatter/scatter.h"
#include "spline/spline.h"
#include "bar/bar.h" <--- NEW
#include "../../plottools/plottools.h"
class _2DType {
private:
// Type select
WXPLOT_TYPE wxPlotType = WXPLOT_TYPE_LINE;
// The data
std::vector<std::vector<double>> data;
// Charts
Line line;
Scatter scatter;
Spline spline;
Bar bar; <--- NEW
public:
// Constructor
_2DType() { }
// Setters
void setWxPlotType(const WXPLOT_TYPE wxPlotType);
void setRadius(const wxCoord radius);
void fillCircles(const bool fillCircle);
void setData(const std::vector<std::vector<double>>& data);
void setPlotStartWidth(const wxCoord plotStartWidth);
void setPlotStartHeight(const wxCoord plotStartHeight);
void setPlotEndWidth(const wxCoord plotEndWidth);
void setPlotEndHeight(const wxCoord plotEndHeight);
// Functions
bool drawType(wxDC& dc);
};
Here all the 2D plot types.
Definition 2Dtype.hpp:13
void setPlotStartHeight(const wxCoord plotStartHeight)
Set the left upper corner in Y-axis. Inside this rectangle, the plot is drawn. NOTE: This value is re...
Definition 2Dtype.cpp:81
void fillCircles(const bool fillCircle)
Fill circles if radius is applied if circiles are going to be filled with the same colour as they are...
Definition 2Dtype.cpp:15
void setPlotStartWidth(const wxCoord plotStartWidth)
Set the left upper corner in X-axis. Inside this rectangle, the plot is drawn. NOTE: This value is re...
Definition 2Dtype.cpp:74
void setPlotEndHeight(const wxCoord plotEndHeight)
Set the right bottom corner in Y-axis. Inside this rectangle, the plot is drawn. NOTE: This value is ...
Definition 2Dtype.cpp:95
void setRadius(const wxCoord radius)
Set radius. Most used for circles e.g filling scatter plots.
Definition 2Dtype.cpp:7
void setWxPlotType(const WXPLOT_TYPE wxPlotType)
Set the plot type selection.
Definition 2Dtype.cpp:3
void setPlotEndWidth(const wxCoord plotEndWidth)
Set the right upper corner in X-axis. Inside this rectangle, the plot is drawn. NOTE: This value is r...
Definition 2Dtype.cpp:88
void drawType(wxDC &dc)
This function draw the plot e.g line, bar, histogram, spline, scatter etc. inside in this rectangle.
Definition 2Dtype.cpp:102
void setData(const std::vector< std::vector< double > > &data2D)
Set 2D data.
Definition 2Dtype.cpp:19
Bar class. Inheritance from class Line.
Definition bar.hpp:11
Line class. Does the most of the things in 2D.
Definition line.hpp:10
Scatter class. Inheritance from class Line.
Definition scatter.hpp:11
Spline class. Inheritance from class Line.
Definition spline.hpp:11

Create a folder called bar inside plots/plottypes/2D where line, scatter and spline are. Inside the bar folder. Add bar.h and bar.cpp

Inside bar.h, paste this. Here we are inherit most of the protected fields and public functions from the Line class. You don't need to do anything more here.

#pragma once
#include <vector>
#include <wx/wx.h>
#include "../line/line.h"
class Bar : public Line {
private:
public:
// Functions
bool draw(wxDC& dc, const std::vector<double>& xData, const std::vector<double>& yData, const int colourIndex) override;
};
void draw(wxDC &dc, const double value, const unsigned int dataSize, const unsigned int colourIndex, const size_t i)
Draw the bar plot inside the rectangle. This is called by iteration. For example, this function is us...
Definition bar.cpp:4

Head over to 2Dtype.cpp file and add code to all set-functions.

void _2DType::setData(const std::vector<std::vector<double>>& data) {
this->data = data;
// Find max and min for the complete data, or else, we will get very weird scaling inside the plot
double minX, maxX, minY, maxY;
findMaxMin2Ddata(data, minX, maxX, minY, maxY);
// Set the limits
line.setLimits(minX, maxX, minY, maxY);
scatter.setLimits(minX, maxX, minY, maxY);
spline.setLimits(minX, maxX, minY, maxY);
bar.setLimits(minX, maxX, minY, maxY); <--- NEW
}
void _2DType::setPlotStartWidth(const wxCoord plotStartWidth) {
line.setPlotStartWidth(plotStartWidth);
scatter.setPlotStartWidth(plotStartWidth);
spline.setPlotStartWidth(plotStartWidth);
bar.setPlotStartWidth(plotStartWidth); <--- NEW
}
void setLimits(const double minX, const double maxX, const double minY, const double maxY)
Set minimum and maximum limits of the data.
Definition line.cpp:5
void setPlotStartWidth(const wxCoord plotStartWidth)
Set the left upper corner in X-axis. Inside this rectangle, the plot is drawn. NOTE: This value is re...
Definition line.cpp:12

Some functions have only a unique functionality e.g scatter-plot. If you want to have a unique functionality to the bar-plot, then you need to write a specific set-function for that. Here is just one example how I can set the radius and fill circles for the scatter-plot. These won't effect the bar-plot.

void _2DType::setRadius(const wxCoord radius) {
scatter.setRadius(radius);
}
void _2DType::fillCircles(const bool fillCircle) {
scatter.fillCircles(fillCircle);
}
void fillCircles(const bool fillCircle)
Fill circles if radius is applied if circiles are going to be filled with the same colour as they are...
Definition scatter.cpp:8
void setRadius(const wxCoord radius)
Set radius. Most used for circles e.g filling scatter plots.
Definition scatter.cpp:5

The second last thing you need to do is to add code inside the bool _2DType::drawType(wxDC& dc) function. Same 2Dtype.cpp file. Add the new bar.draw(dc, xData, yData, colourIndex) as a new case.

// Draw type now
switch (wxPlotType) {
case WXPLOT_TYPE_LINE:
line.draw(dc, xData, yData, colourIndex);
break;
case WXPLOT_TYPE_SCATTER:
scatter.draw(dc, xData, yData, colourIndex);
break;
case WXPLOT_TYPE_LINE_SCATTER:
line.draw(dc, xData, yData, colourIndex);
scatter.draw(dc, xData, yData, colourIndex);
break;
case WXPLOT_TYPE_SPLINE:
spline.draw(dc, xData, yData, colourIndex);
break;
case WXPLOT_TYPE_SPLINE_SCATTER:
spline.draw(dc, xData, yData, colourIndex);
scatter.draw(dc, xData, yData, colourIndex);
break;
case WXPLOT_TYPE_BAR:
bar.draw(dc, xData, yData, colourIndex); <--- NEW
break;
default:
break;
}

The last thing you need to do is to add code to bar.cpp. The one you just created above, but did not wrote anyting in to it. Let's try out this code. It won't be Beautiful, but it can give us some suggestions about what should to fixed.

#include "bar.h"
#include "../../../plottools/plottools.h"
bool Bar::draw(wxDC& dc, const std::vector<double>& xData, const std::vector<double>& yData, const int colourIndex) {
// Set rectangle colour
setColourPen(dc, colourIndex);
setColourBrush(dc, colourIndex);
// Draw rectangles
const size_t dataLength = xData.size();
const wxCoord width = (plotEndWidth - plotStartHeight) / dataLength;
for (size_t j = 0; j < dataLength; j++) {
const wxCoord x = linearScalarXaxis(xData.at(j), minX, plotStartWidth, maxX, plotEndWidth);
const wxCoord y = linearScalarYaxis(yData.at(j), minY, plotStartHeight, maxY, plotEndHeight);
const wxCoord height = plotEndHeight - y;
dc.DrawRectangle(x, y, width, height);
}
// Nothing went wrong
return true;
}
wxCoord plotEndHeight
Location of right bottom corner in Y-axis.
Definition line.hpp:14
double minY
Minimum value of data in Y-axis.
Definition line.hpp:20
wxCoord plotStartHeight
Location of left upper corner in Y-axis.
Definition line.hpp:16
double maxX
Maximum value of data in X-axis.
Definition line.hpp:19
double maxY
Maximum value of data in Y-axis.
Definition line.hpp:21
wxCoord plotEndWidth
Location of right upper corner in X-axis.
Definition line.hpp:13
double minX
Minimum value of data in X-axis.
Definition line.hpp:18
wxCoord plotStartWidth
Location of left upper corner in X-axis.
Definition line.hpp:15

With this data:

// Create data for bar
std::vector<std::vector<double>> data = { {0}, {15},
{1}, {20},
{2}, {10},
{3}, {50},
{4}, {10},
{5}, {-10} };
plot->setData(data);

It would look something like this. Here we can see that the Y-ticks are placed after max and minimum of 2D vector. The minimum data of data 2D vector above, is -10 and maximum value is 50.

a

If we are using this data, that not contains any negative numbers, we can confirm that the Y-ticks are placed wrong.

// Create data for bar
std::vector<std::vector<double>> data = { {0}, {15},
{1}, {20},
{2}, {10},
{3}, {50},
{4}, {10},
{5}, {-10} };
plot->setData(data);

a

To solve this issue, we need to change the minY so the Y-ticks are displayed correctly. One good option is to head over to proportional.cpp and add this. So if minY is positive, turn it to 0, only for bar or other column charts.

/*
* This function draw the ticks.
* Call this function after you have drawn the plot type.
*/
void Proportional::drawTicks(wxDC& dc) {
if (ticks > 0) {
const wxCoord yStartRectangle = plotStartHeight;
const wxCoord xStartRectangle = plotStartWidth;
const wxCoord heightRectangle = plotEndHeight - plotStartHeight;
const wxCoord widthRectangle = plotEndWidth - plotStartWidth;
// Set color - Black
wxPen pen(plotColours.at(PLOT_COLOUR_BLACK));
dc.SetPen(pen);
// Compute the minimum and maximum value from the data for correct scaling
double minX, maxX, minY, maxY;
findMaxMin2Ddata(data, minX, maxX, minY, maxY);
// Only for column charts such as bar, histogram etc. <--- NEW
switch (wxPlotType) {
case WXPLOT_TYPE_BAR:
if (minY > 0) {
minY = 0;
}
break;
}
unsigned int ticks
The number of ticks at the figure in all axis.
Definition proportional.hpp:26
double minY
Minimum value of data in Y-axis.
Definition proportional.hpp:22
WXPLOT_TYPE wxPlotType
Plot type selection.
Definition proportional.hpp:34
wxCoord plotStartHeight
Location of left upper corner in Y-axis.
Definition proportional.hpp:18
wxCoord plotStartWidth
Location of left upper corner in X-axis.
Definition proportional.hpp:17
wxCoord plotEndHeight
Location of right bottom corner in Y-axis.
Definition proportional.hpp:16
double maxX
Maximum value of data in X-axis.
Definition proportional.hpp:21
wxCoord plotEndWidth
Location of right upper corner in X-axis.
Definition proportional.hpp:15
void drawTicks(wxDC &dc)
This function will draw the ticks. Call this function after you have drawn the plot type.
Definition proportional.cpp:161
double minX
Minimum value of data in X-axis.
Definition proportional.hpp:20
double maxY
Maximum value of data in Y-axis.
Definition proportional.hpp:23

After few hours of figure how to make a proper column setup as a bar plot. This results this code. As you can see, there was no use of X-data for displaying the X-axis.

#include "bar.h"
#include "../../../plottools/plottools.h"
bool Bar::draw(wxDC& dc, const double yData, const int colourIndex, const size_t dataSize, const size_t i) {
// Set rectangle colour
setColourPen(dc, colourIndex);
setColourBrush(dc, colourIndex);
// Some parameter
const wxCoord width = (plotEndWidth - plotStartWidth) / dataSize;
// Draw rectangle
const wxCoord x = plotStartWidth + width * i;
const wxCoord y = linearScalarYaxis(yData, minY, plotStartHeight, maxY, plotEndHeight);
const wxCoord y0 = linearScalarYaxis(0, minY, plotStartHeight, maxY, plotEndHeight);
const wxCoord yMin = linearScalarYaxis(minY, minY, plotStartHeight, maxY, plotEndHeight);
const wxCoord delta = yMin - y0;
const wxCoord height = plotEndHeight - y - delta;
dc.DrawRectangle(x, y, width, height);
// Nothing went wrong
return true;
}

a