diff --git a/.gitignore b/.gitignore
index f5b8ab6..5a974b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,7 @@
# Build results
[Bb]uild/
.vs/
+.vscode/
out/
CMakeSettings.json
\ No newline at end of file
diff --git a/README.md b/README.md
index 8703257..342b5f9 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
# **PlotOpenCv C++ library**
-**v1.1.0**
+**v1.2.0**
@@ -16,18 +16,20 @@
- [Library files](#library-files)
- [plot class description](#plot-class-description)
- [Class declaration](#class-declaration)
+ - [getVersion method](#getversion-method)
+ - [Class constructor](#class-constructor)
- [addPlot (1D) method](#addplot-for-1d-dataset-method)
- [addPlot (2D) method](#addplot-for-2d-dataset-method)
- [clean method](#show-method)
- [show method](#clean-method)
-- [Example](#example)
- [Build and connect to your project](#build-and-connect-to-your-project)
+- [Example](#example)
# Overview
-**PlotOpenCv** C++ library provides the visualization of 2-dimensional line charts. This library is built upon the OpenCV, providing users with a convenient and efficient tool for visualizing data through line charts. With **PlotOpenCv**, users can create multiple line charts within a single window and tune various chart parameters, such as line width, color, and more. It utilizes C++17 standard. The library is licensed under the Apache 2.0 license.
+**PlotOpenCv** C++ library provides the visualization of 2-dimensional line charts. This library is built upon the OpenCV, providing users with a convenient and efficient tool for visualizing data through line charts. With **PlotOpenCv**, users can create multiple line charts within a single window and tune various chart parameters, such as line width, color, and more. It utilizes C++17 standard. The library is licensed under the **Apache 2.0** license.
@@ -42,6 +44,7 @@
| 1.0.2 | 16.04.2024 | - Antialiased line drawing implemented.
- Window size issue fixed.
- Documentation updated. |
| 1.0.3 | 17.05.2024 | - Documentation updated. |
| 1.1.0 | 19.07.2024 | - CMake structure updated.
- Files renamed. |
+| 1.2.0 | 23.07.2024 | - Files structure changed to hide opencv headers.
- Library interface changed. |
@@ -57,6 +60,9 @@ src ------------------------- Library source code folder.
PlotOpenCvVersion.h ----- Header file with library version.
PlotOpenCvVersion.h.in -- File for CMake to generate version header.
PlotOpenCv.cpp ---------- C++ implementation file.
+ Impl -------------------- Folder with plot implementation.
+ PlotOpenCvImpl.h ---- Plot implementation header file.
+ PlotOpenCvImpl.cpp -- C++ implementation file.
test ------------------------ Folder for test application.
CMakeLists.txt ---------- CMake file of test application.
main.cpp ---------------- Source code of test application.
@@ -77,7 +83,26 @@ namespace cr
{
namespace utils
{
-/// plot class.
+
+/**
+ * @brief Plot color.
+*/
+class PlotColor
+{
+public:
+ /// Class constructor
+ PlotColor(int b, int g, int r) : b(b), g(g), r(r) {}
+ /// Blue color 0-255.
+ int b{0};
+ /// Green color 0-255.
+ int g{0};
+ /// Red color 0-255.
+ int r{0};
+};
+
+/**
+ * @brief Plot class.
+ */
class Plot
{
public:
@@ -87,8 +112,8 @@ public:
/// Class constructor.
Plot(std::string name, int width = 1280, int height = 720,
- cv::Scalar backgroundColor = cv::Scalar(255, 255, 255),
- cv::Scalar scaleLineColor = cv::Scalar(0, 128, 128));
+ PlotColor backgroundColor = PlotColor(255, 255, 255),
+ PlotColor scaleLineColor = PlotColor(0, 128, 128));
/// Class destructor.
~Plot();
@@ -96,12 +121,13 @@ public:
/// Render plots on window.
template
void addPlot(std::vector& points, int id, int start = 0, int end = 0,
- cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1);
+ PlotColor color = PlotColor(255, 255, 255), int thickness = 1);
+
/// Method to render plots on window.
template
void addPlot(std::vector>& points, int id,
- int start = 0, int end = 0,
- cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1);
+ int start = 0, int end = 0,
+ PlotColor color = PlotColor(255, 255, 255), int thickness = 1);
/// Method to clean window.
void clean();
@@ -132,7 +158,32 @@ std::cout << "PlotOpenCv class version: " << PlotOpenCv::getVersion();
Console output:
```bash
-PlotOpenCv class version: 1.1.0
+PlotOpenCv class version: 1.2.0
+```
+
+
+## Class constructor
+
+The **Plot** class constructor requires basic parameters. Constructor declaration:
+
+```cpp
+Plot(std::string name, int width = 1280, int height = 720,
+ PlotColor backgroundColor = PlotColor(255, 255, 255),
+ PlotColor scaleLineColor = PlotColor(0, 128, 128));
+```
+
+| Parameter | Value |
+| --------------- | ------------------------------- |
+| name | OpenCV window name. |
+| width | Window width, pixels. |
+| height | Window height, pixels. |
+| backgroundColor | Background color, BGR. |
+| scaleLineColor | Scale line color, BGR. |
+
+Example of class initialization:
+
+```cpp
+Plot graph("Test graph", 1280, 640, PlotColor(50, 50, 50));
```
@@ -148,7 +199,7 @@ void addPlot(std::vector &points, int id, int start = 0, int end = 0,
| Parameter | Value |
| --------- | ------------------------------------------------------------ |
-| Points | One dimensional vector which includes vertical points.Vector format : {y1, y2, ... } |
+| Points | One dimensional vector which includes vertical points. Vector format : {y1, y2, ... } |
| id | Identifier for chart on a window. Provides user to update a chart or add new one. |
| start | Start index of plot from vector when user wants to plot a specific range from a dataset. Should be 0 for whole dataset.|
| end | End index of plot from vector when user wants to plot a specific range from a dataset. Should be 0 for whole dataset. |
@@ -162,8 +213,10 @@ void addPlot(std::vector &points, int id, int start = 0, int end = 0,
The **addPlot(...)** method serves the purpose of incorporating a new line chart into the existing window. It either introduces a new plot if the provided id is not yet present, or updates an existing plot associated with the given identifier. Method declaration:
```cpp
-void addPlot(std::vector> &points, int id, int start = 0, int end = 0,
- cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1);
+template
+void addPlot(std::vector>& points, int id,
+ int start = 0, int end = 0,
+ PlotColor color = PlotColor(255, 255, 255), int thickness = 1);
```
| Parameter | Value |
@@ -231,7 +284,7 @@ src
yourLib.cpp
```
-create folder **3rdparty** in your repository and copy **PlotOpenCv** repository folder there. New structure of your repository:
+Create folder **3rdparty** in your repository and copy **PlotOpenCv** repository folder there. New structure of your repository:
```bash
CMakeLists.txt
@@ -321,13 +374,13 @@ The example demonstrates how to use **PlotOpenCv** library.
int main()
{
- plot graph("Test graph", 1280, 720,cv::Scalar(0, 128, 128) cv::Scalar(50, 50, 50));
+ plot graph("Test graph", 1280, 720, PlotColor(0, 128, 128) PlotColor(50, 50, 50));
std::vector linePoints(9000);
std::vector> linePoints2(5000, std::vector(2));
- graph.addPlot(linePoints,0, 0, 0, cv::Scalar(255,0,0), 5);
- graph.addPlot(linePoints2,1, 0, 0, cv::Scalar(0,255,0), 2);
+ graph.addPlot(linePoints, 0, 0, 0, PlotColor(255,0,0), 5);
+ graph.addPlot(linePoints2, 1, 0, 0, PlotColor(0,255,0), 2);
graph.show();
cv::waitKey(0);
@@ -335,11 +388,7 @@ int main()
}
```
-
-
-# Example Charts
-
-Example charts shows what visual effects user should expect depending on input data.
+Example charts shows what visual effects user should expect depending on input data:
![plot_opencv_example_1](./static/plot_opencv_example_1.png)
![plot_opencv_example_2](./static/plot_opencv_example_2.png)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 60acff9..2a38cdc 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.13)
## LIBRARY-PROJECT
## name and version
###############################################################################
-project(PlotOpenCv VERSION 1.0.4 LANGUAGES CXX)
+project(PlotOpenCv VERSION 1.2.0 LANGUAGES CXX)
diff --git a/src/Impl/PlotOpenCvImpl.cpp b/src/Impl/PlotOpenCvImpl.cpp
new file mode 100644
index 0000000..2f2968e
--- /dev/null
+++ b/src/Impl/PlotOpenCvImpl.cpp
@@ -0,0 +1,362 @@
+#include "PlotOpenCvImpl.h"
+
+
+
+template
+T map(T x, T in_min, T in_max, T out_min, T out_max)
+{
+ return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
+}
+
+
+
+cr::utils::PlotImpl::PlotImpl(std::string name, int width, int height,
+ cv::Scalar backgroundColor, cv::Scalar scaleLineColor)
+{
+ // Create a window.
+ cv::namedWindow(name, cv::WINDOW_AUTOSIZE);
+
+ // Background image (white) for window.
+ m_image = new cv::Mat(height, width, CV_8UC3, backgroundColor);
+
+ // Remember window params.
+ m_width = width;
+ m_height = height;
+ m_backgroundColor = backgroundColor;
+ m_horizontalScaleLineColor = scaleLineColor;
+ m_name = name;
+
+ //Draw horizontal scale line.
+ cv::line(*m_image, cv::Point(0, height / 2),
+ cv::Point(width, height / 2), m_horizontalScaleLineColor, 5, cv::LINE_AA);
+
+ // Draw grid lines.
+ for (int i = 0; i < height; i += 50)
+ {
+ cv::line(*m_image, cv::Point(0, i),
+ cv::Point(width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA);
+ }
+ for (int i = 0; i < width; i += 50)
+ {
+ cv::line(*m_image, cv::Point(i, 0),
+ cv::Point(i, height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA);
+ }
+}
+
+
+
+cr::utils::PlotImpl::~PlotImpl()
+{
+ delete m_image;
+}
+
+
+
+void cr::utils::PlotImpl::renderPlot(Plot2D plot)
+{
+ // Ratio/offset for horizontal axis.
+ float dt = 0.0f;
+
+ // Fit signal into window.
+ dt = (float)m_width / (float)plot.m_length;
+
+ // Temporary points to draw a line.
+ cv::Point currentPoint;
+ cv::Point previousPoint;
+
+ // Update plot params with respect window.
+ plot.m_outMax = m_height / 2;
+
+ // Offset value for dataset that has negative values.
+ if (plot.m_inMin < 0)
+ {
+ plot.m_offsetY = static_cast(plot.m_outMax - (plot.m_outMax / 2));
+ }
+
+ // 1D vector drawing.
+ if (plot.m_type == 0)
+ {
+ // Points on horizontal axis.
+ float t = 0;
+ // Lines between points.
+ for (int i = 1; i < plot.m_length; ++i)
+ {
+ // line between two points
+ previousPoint.x = static_cast(t);
+ previousPoint.y = static_cast(::map(plot.m_points1D.at(i - 1), plot.m_inMin,
+ plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY);
+ t += dt;
+ currentPoint.x = static_cast(t);
+ currentPoint.y = static_cast(::map(plot.m_points1D.at(i), plot.m_inMin,
+ plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY);
+
+ cv::line(*m_image, previousPoint, currentPoint, plot.m_color,
+ plot.m_thickness, cv::LINE_AA);
+ }
+ }
+ // 2D vector drawing.
+ else
+ {
+ // Lines between points
+ for (int i = 1; i < plot.m_points2D.size(); ++i)
+ {
+ // line between two points
+ previousPoint.x = static_cast(((plot.m_points2D)[i - 1][0] - plot.m_offsetX) * dt);
+ previousPoint.y = static_cast(::map((plot.m_points2D)[i - 1][1], plot.m_inMin, plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY);
+ currentPoint.x = static_cast(((plot.m_points2D)[i][0] - plot.m_offsetX) * dt);
+ currentPoint.y = static_cast(::map((plot.m_points2D)[i][1], plot.m_inMin, plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY);
+ cv::line(*m_image, previousPoint, currentPoint, plot.m_color, plot.m_thickness, cv::LINE_AA);
+ }
+ }
+}
+
+
+
+template
+void cr::utils::PlotImpl::addPlot(std::vector& points, int id, int start, int end,
+ cv::Scalar color, int thickness)
+{
+ // Check if id is already contained.
+ for (const auto& pair : m_plots)
+ {
+ const int& i = pair.first;
+ if (i == id)
+ {
+ // Remove plot from list.
+ auto it = m_plots.find(i);
+ if (it != m_plots.end())
+ m_plots.erase(it);
+ }
+ }
+
+ // Add ploting object to list.
+ std::vector doubles;
+ for (int i =0;i(points[i]));
+ }
+
+ m_plots.insert(std::make_pair(id, Plot2D(doubles, id, start, end, color, thickness)));
+
+ }
+
+
+
+ template
+ void cr::utils::PlotImpl::addPlot(std::vector>& points, int id, int start, int end,
+ cv::Scalar color, int thickness)
+{
+ // Check if id is already contained.
+ for (const auto& pair : m_plots)
+ {
+ const int& i = pair.first;
+ if (i == id)
+ {
+ // Remove plot from list.
+ auto it = m_plots.find(i);
+ if (it != m_plots.end())
+ m_plots.erase(it);
+ }
+ }
+
+ // Convert points vector to double.
+ std::vector> doubles;
+ std::vector temp(2);
+ for (int i = 0; i < points.size(); i++)
+ {
+ temp[0] = static_cast(points[i][0]);
+ temp[1] = static_cast(points[i][1]);
+
+ doubles.push_back(temp);
+ }
+
+ // Add ploting object to list.
+ m_plots.insert(std::make_pair(id, Plot2D(doubles, id, start, end, color, thickness)));
+
+}
+
+
+
+void cr::utils::PlotImpl::clean()
+{
+ // Clean window.
+ m_image->setTo(m_backgroundColor);
+
+ // Draw horizontal scale line.
+ cv::line(*m_image, cv::Point(0, m_height / 2),
+ cv::Point(m_width, m_height / 2), m_horizontalScaleLineColor, 3, cv::LINE_AA);
+
+ // Draw grid lines.
+ for (int i = 0; i < m_height; i += 50)
+ cv::line(*m_image, cv::Point(0, i),
+ cv::Point(m_width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA);
+
+ for (int i = 0; i < m_width; i += 50)
+ cv::line(*m_image, cv::Point(i, 0),
+ cv::Point(i, m_height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA);
+
+ // Clear all instances from container.
+ m_plots.clear();
+}
+
+
+
+void cr::utils::PlotImpl::show()
+{
+ // Clean window.
+ m_image->setTo(m_backgroundColor);
+
+ // Draw horizontal scale line.
+ cv::line(*m_image, cv::Point(0, m_height / 2),
+ cv::Point(m_width, m_height / 2), m_horizontalScaleLineColor, 3, cv::LINE_AA);
+
+ // Draw grid lines.
+ for (int i = 0; i < m_height; i += 50)
+ {
+ cv::line(*m_image, cv::Point(0, i),
+ cv::Point(m_width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA);
+ }
+ for (int i = 0; i < m_width; i += 50)
+ {
+ cv::line(*m_image, cv::Point(i, 0),
+ cv::Point(i, m_height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA);
+ }
+
+ // Iterate over the values (Plot2D instances) in the map
+ for (const auto& pair : m_plots)
+ {
+ const Plot2D& plot = pair.second;
+ renderPlot(plot);
+ }
+
+ cv::imshow(m_name, *m_image);
+}
+
+
+
+cr::utils::PlotImpl::Plot2D::Plot2D(std::vector& points, int id,
+ int start, int end, cv::Scalar color, int thickness)
+{
+ m_length = end - start;
+
+ // Invalid range, use complete vector.
+ if (m_length <= 0)
+ {
+ m_length = static_cast(points.size());
+ std::copy(points.begin(), points.end(), std::back_inserter(m_points1D));
+ }
+ // Copy specified range of input vector.
+ else
+ {
+ // Define the range you want to copy.
+ std::vector::iterator startindex = points.begin() + start;
+ std::vector::iterator endindex = points.begin() + end;
+
+ // Copy the range from sourceVector to destinationVector.
+ std::copy(startindex, endindex, std::back_inserter(m_points1D));
+ }
+
+ // Max and min values are required for vertical scaling.
+ m_inMax = *max_element(points.begin(), points.end());
+ m_inMin = *min_element(points.begin(), points.end());
+
+ // Update params.
+ m_id = id;
+ m_color = color;
+ m_thickness = thickness;
+ m_type = 0;
+}
+
+
+
+cr::utils::PlotImpl::Plot2D::Plot2D(std::vector>& points, int id,
+ int start, int end, cv::Scalar color, int thickness)
+{
+ m_inMax = std::numeric_limits::lowest();
+ m_inMin = std::numeric_limits::max();
+ m_offsetX = std::numeric_limits::max();
+ m_length = end - start;
+
+ // Invalid range, use complete vector.
+ if (m_length <= 0)
+ {
+ m_length = static_cast(points.size());
+ std::copy(points.begin(), points.end(), std::back_inserter(m_points2D));
+ }
+ // Copy specified range of input vector.
+ else
+ {
+ // Range of copy.
+ std::vector>::iterator startindex = points.begin() + start;
+ std::vector>::iterator endindex = points.begin() + end;
+
+ // Copy the range from sourceVector to destinationVector.
+ std::copy(startindex, endindex, std::back_inserter(m_points2D));
+ };
+
+ // Iterate through the vector within the specified range.
+ for (int i = start; i < end && i < points.size(); ++i) {
+ const std::vector& row = points[i];
+
+ if (row.size() > 1) {
+ double valueY = row[1]; // y component
+ double valueX = row[0]; // x component
+
+ // Update maximum value using std::max
+ m_inMax = std::max(m_inMax, valueY);
+
+ // Update minimum value using std::min
+ m_inMin = std::min(m_inMin, valueY);
+
+ // Find the smallest value in X direction for horizontal offset.
+ m_offsetX = std::min(m_offsetX, valueX);
+ }
+ }
+
+ // Update params.
+ m_id = id;
+ m_thickness = thickness;
+ m_color = color;
+ m_type = 1;
+}
+
+cr::utils::PlotImpl::Plot2D::~Plot2D() {}
+
+template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+
+
+template void cr::utils::PlotImpl::addPlot(std::vector>& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points,int id,
+ int start, int end, cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points,
+ int id, int start, int end,
+ cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points,
+ int id, int start, int end,
+ cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, int id,
+ int start, int end,
+ cv::Scalar color, int thickness);
+template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points,
+ int id, int start, int end,
+ cv::Scalar color, int thickness);
\ No newline at end of file
diff --git a/src/Impl/PlotOpenCvImpl.h b/src/Impl/PlotOpenCvImpl.h
new file mode 100644
index 0000000..e1f8082
--- /dev/null
+++ b/src/Impl/PlotOpenCvImpl.h
@@ -0,0 +1,153 @@
+#pragma once
+#include
+#include
+#include