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);
};
_2DType
Here all the 2D plot types.
Definition 2Dtype.hpp:13
_2DType::setPlotStartHeight
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
_2DType::fillCircles
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
_2DType::setPlotStartWidth
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
_2DType::setPlotEndHeight
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
_2DType::setRadius
void setRadius(const wxCoord radius)
Set radius. Most used for circles e.g filling scatter plots.
Definition 2Dtype.cpp:7
_2DType::setWxPlotType
void setWxPlotType(const WXPLOT_TYPE wxPlotType)
Set the plot type selection.
Definition 2Dtype.cpp:3
_2DType::setPlotEndWidth
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
_2DType::drawType
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
_2DType::setData
void setData(const std::vector< std::vector< double > > &data2D)
Set 2D data.
Definition 2Dtype.cpp:19
Bar
Bar class. Inheritance from class Line.
Definition bar.hpp:11
Line
Line class. Does the most of the things in 2D.
Definition line.hpp:10
Scatter
Scatter class. Inheritance from class Line.
Definition scatter.hpp:11
Spline
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;
};
Bar::draw
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
}
Line::setLimits
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
Line::setPlotStartWidth
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);
}
Scatter::fillCircles
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
Scatter::setRadius
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;
}
Line::plotEndHeight
wxCoord plotEndHeight
Location of right bottom corner in Y-axis.
Definition line.hpp:14
Line::minY
double minY
Minimum value of data in Y-axis.
Definition line.hpp:20
Line::plotStartHeight
wxCoord plotStartHeight
Location of left upper corner in Y-axis.
Definition line.hpp:16
Line::maxX
double maxX
Maximum value of data in X-axis.
Definition line.hpp:19
Line::maxY
double maxY
Maximum value of data in Y-axis.
Definition line.hpp:21
Line::plotEndWidth
wxCoord plotEndWidth
Location of right upper corner in X-axis.
Definition line.hpp:13
Line::minX
double minX
Minimum value of data in X-axis.
Definition line.hpp:18
Line::plotStartWidth
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`.

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);

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;
}
Proportional::ticks
unsigned int ticks
The number of ticks at the figure in all axis.
Definition proportional.hpp:26
Proportional::minY
double minY
Minimum value of data in Y-axis.
Definition proportional.hpp:22
Proportional::wxPlotType
WXPLOT_TYPE wxPlotType
Plot type selection.
Definition proportional.hpp:34
Proportional::plotStartHeight
wxCoord plotStartHeight
Location of left upper corner in Y-axis.
Definition proportional.hpp:18
Proportional::plotStartWidth
wxCoord plotStartWidth
Location of left upper corner in X-axis.
Definition proportional.hpp:17
Proportional::plotEndHeight
wxCoord plotEndHeight
Location of right bottom corner in Y-axis.
Definition proportional.hpp:16
Proportional::maxX
double maxX
Maximum value of data in X-axis.
Definition proportional.hpp:21
Proportional::plotEndWidth
wxCoord plotEndWidth
Location of right upper corner in X-axis.
Definition proportional.hpp:15
Proportional::drawTicks
void drawTicks(wxDC &dc)
This function will draw the ticks. Call this function after you have drawn the plot type.
Definition proportional.cpp:161
Proportional::minX
double minX
Minimum value of data in X-axis.
Definition proportional.hpp:20
Proportional::maxY
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;
}