Skip to content

Commit

Permalink
widget: added implot real-time line plot widget
Browse files Browse the repository at this point in the history
  • Loading branch information
rxdu committed Oct 26, 2024
1 parent 8ebdd4a commit 7170da8
Show file tree
Hide file tree
Showing 14 changed files with 397 additions and 108 deletions.
3 changes: 2 additions & 1 deletion src/imview/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ add_library(imview
src/box.cpp
src/layer.cpp
# src/popup.cpp
# src/data_buffer.cpp
src/buffer/scrolling_plot_buffer.cpp
# utils
src/utils/image_utils.cpp
# widgets
Expand All @@ -28,6 +28,7 @@ add_library(imview
src/widget/cairo_widget.cpp
src/widget/cairo/cairo_context.cpp
src/widget/cairo/cairo_draw.cpp
src/widget/rt_line_plot_widget.cpp
# data buffer
src/buffer/buffer_registry.cpp
# event handling
Expand Down
4 changes: 4 additions & 0 deletions src/imview/include/imview/buffer/buffer_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ class BufferBase {
template <typename T>
class BufferInterface : public BufferBase {
public:
/// \brief Get the number of bytes that can be read from the buffer
/// \return number of bytes that can be read
virtual std::size_t GetOccupiedSize() const = 0;

/// \brief Read data from the buffer
/// \param data
/// \return 0 if failed, otherwise the number of bytes read (1)
Expand Down
2 changes: 2 additions & 0 deletions src/imview/include/imview/buffer/double_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class DoubleBuffer : public BufferInterface<T> {
public:
DoubleBuffer() : write_index_(0), ready_(false) {}

std::size_t GetOccupiedSize() const override { return ready_.load() ? 1 : 0; }

std::size_t Write(const T& data) {
{
std::lock_guard<std::mutex> lock(mutex_);
Expand Down
2 changes: 1 addition & 1 deletion src/imview/include/imview/buffer/ring_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class RingBuffer : public BufferInterface<T> {
return N - 1 - ((write_index_ - read_index_) & size_mask_);
}

std::size_t GetOccupiedSize() const {
std::size_t GetOccupiedSize() const override {
std::lock_guard<std::mutex> lock(buffer_mutex_);
return (write_index_ - read_index_) & size_mask_;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
/*
* data_buffer.hpp
* scrolling_plot_buffer.hpp
*
* Created on: Mar 25, 2021 14:29
* Description: adapted from class ScrollingBuffer from implot_demo.cpp
*
* Copyright (c) 2021 Ruixiang Du (rdu)
*/

#ifndef PLOT_BUFFER_HPP
#define PLOT_BUFFER_HPP
#ifndef SCROLLING_PLOT_BUFFER_HPP
#define SCROLLING_PLOT_BUFFER_HPP

#include <cstdint>
#include <mutex>
Expand All @@ -17,10 +17,9 @@
#include "imgui.h"

namespace quickviz {
namespace swviz {
class DataBuffer {
class ScrollingPlotBuffer {
public:
DataBuffer(uint32_t size = 2048);
ScrollingPlotBuffer(uint32_t size = 2048);

void Resize(uint32_t size);
std::size_t GetSize() const;
Expand All @@ -36,7 +35,6 @@ class DataBuffer {
uint32_t buffer_size_ = 0;
uint32_t offset_ = 0;
};
} // namespace swviz
} // namespace xmotion
} // namespace quickviz

#endif /* PLOT_BUFFER_HPP */
#endif /* SCROLLING_PLOT_BUFFER_HPP */
63 changes: 63 additions & 0 deletions src/imview/include/imview/widget/rt_line_plot_widget.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* @file rt_line_plot_widget.hpp
* @date 10/26/24
* @brief this LinePlotWidget serves as an example of how to create a custom
* widget that plots data in real-time
*
* @copyright Copyright (c) 2024 Ruixiang Du (rdu)
*/

#ifndef QUICKVIZ_RT_LINE_PLOT_WIDGET_HPP
#define QUICKVIZ_RT_LINE_PLOT_WIDGET_HPP

#include <unordered_map>

#include "imview/panel.hpp"
#include "imview/buffer/buffer_registry.hpp"
#include "imview/buffer/scrolling_plot_buffer.hpp"

namespace quickviz {
class RtLinePlotWidget : public Panel {
public:
struct DataPoint {
float x; // time
float y; // value
};

public:
RtLinePlotWidget(const std::string& widget_name);
~RtLinePlotWidget() = default;

// public methods
void SetPlotTransparency(float alpha);
void SetAxisLabels(const std::string& x_label, const std::string& y_label);
void SetAxisUnits(const std::string& x_unit, const std::string& y_unit);
void SetFixedHistory(float history);
void SetYAxisRange(float min, float max);
void AddLine(const std::string& line_name, const std::string& buffer_name);

// for internal use
void Draw() override;

private:
struct PlotSpecs {
std::string name;
ScrollingPlotBuffer internal_buffer;
std::shared_ptr<BufferInterface<DataPoint>> input_buffer;
};

float alpha_ = 1.0f;
std::string x_label_;
std::string y_label_;
std::string x_unit_;
std::string y_unit_;
float t_ = 0;
bool fixed_history_ = true;
float history_ = 10.0f;
float y_min_ = 0;
float y_max_ = 1;
std::unordered_map<std::string, PlotSpecs> line_specs_;
};
} // namespace quickviz

#endif // QUICKVIZ_RT_LINE_PLOT_WIDGET_HPP
50 changes: 0 additions & 50 deletions src/imview/src/buffer/data_buffer.cpp

This file was deleted.

48 changes: 48 additions & 0 deletions src/imview/src/buffer/scrolling_plot_buffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* scrolling_plot_buffer.cpp
*
* Created on: Mar 25, 2021 17:39
* Description:
*
* Copyright (c) 2021 Ruixiang Du (rdu)
*/

#include "imview/buffer/scrolling_plot_buffer.hpp"

#include <cmath>

namespace quickviz {
ScrollingPlotBuffer::ScrollingPlotBuffer(uint32_t size) : buffer_size_(size) {
data_.reserve(size);
}

void ScrollingPlotBuffer::Resize(uint32_t size) {
data_.reserve(size);
buffer_size_ = size;
}

std::size_t ScrollingPlotBuffer::GetSize() const { return data_.size(); }

std::size_t ScrollingPlotBuffer::GetOffset() const { return offset_; }

void ScrollingPlotBuffer::Erase() {
if (data_.size() > 0) {
data_.shrink(0);
offset_ = 0;
}
}

void ScrollingPlotBuffer::AddPoint(float x, float y) {
if (data_.size() < buffer_size_)
data_.push_back(ImVec2(x, y));
else {
data_[offset_] = ImVec2(x, y);
offset_ = (offset_ + 1) % buffer_size_;
}
}

ImVec2 &ScrollingPlotBuffer::operator[](std::size_t index) {
assert(index < data_.size());
return data_[index];
}
} // namespace quickviz
126 changes: 126 additions & 0 deletions src/imview/src/widget/rt_line_plot_widget.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* @file line_plot_widget.cpp
* @date 10/26/24
* @brief
*
* @copyright Copyright (c) 2024 Ruixiang Du (rdu)
*/

#include "imview/widget/rt_line_plot_widget.hpp"

#include <cmath>
#include <iostream>

#include "implot/implot.h"

namespace quickviz {
RtLinePlotWidget::RtLinePlotWidget(const std::string& widget_name)
: Panel(widget_name) {
this->SetAutoLayout(false);
this->SetWindowNoMenuButton();
this->SetNoBackground(true);
}

void RtLinePlotWidget::SetPlotTransparency(float alpha) { alpha_ = alpha; }

void RtLinePlotWidget::SetAxisLabels(const std::string& x_label,
const std::string& y_label) {
x_label_ = x_label;
y_label_ = y_label;
}

void RtLinePlotWidget::SetAxisUnits(const std::string& x_unit,
const std::string& y_unit) {
x_unit_ = x_unit;
y_unit_ = y_unit;
}

void RtLinePlotWidget::SetFixedHistory(float history) {
fixed_history_ = true;
history_ = history;
}

void RtLinePlotWidget::SetYAxisRange(float min, float max) {
y_min_ = min;
y_max_ = max;
}

void RtLinePlotWidget::AddLine(const std::string& line_name,
const std::string& buffer_name) {
line_specs_[line_name].name = line_name;
line_specs_[line_name].internal_buffer = ScrollingPlotBuffer();
auto& buffer_registry = BufferRegistry::GetInstance();
line_specs_[line_name].input_buffer =
buffer_registry.GetBuffer<DataPoint>(buffer_name);
}

void RtLinePlotWidget::Draw() {
Begin();
{
ImVec2 contentSize = ImGui::GetContentRegionAvail();
float width = contentSize.x;
float height = contentSize.y;

// fetch data from buffer and find the latest time
for (auto& line : line_specs_) {
auto& spec = line.second;
auto size = spec.input_buffer->GetOccupiedSize();
for (int i = 0; i < size; i++) {
DataPoint pt;
if (spec.input_buffer->Read(pt) != 0) {
if (pt.x > t_) t_ = pt.x;
spec.internal_buffer.AddPoint(pt.x, pt.y);
}
}
}

static ImPlotAxisFlags flags =
ImPlotAxisFlags_None; // ImPlotAxisFlags_NoTickLabels;

auto frame_bg = ImGui::GetStyleColorVec4(ImGuiCol_FrameBg);
auto plot_bg = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
auto popup_bg = ImGui::GetStyleColorVec4(ImGuiCol_PopupBg);
frame_bg.w = alpha_;
plot_bg.w = alpha_;
popup_bg.w = alpha_;
ImPlot::PushStyleColor(ImPlotCol_FrameBg, frame_bg);
ImPlot::PushStyleColor(ImPlotCol_PlotBg, plot_bg);
ImPlot::PushStyleColor(ImPlotCol_LegendBg, popup_bg);
{
float plot_height = height - ImGui::GetFrameHeight() * 1.4;
if (fixed_history_) plot_height = height;
if (ImPlot::BeginPlot(("##" + GetName()).c_str(),
ImVec2(-1, plot_height))) {
char* x_label = nullptr;
char* y_label = nullptr;
if (!x_label_.empty()) x_label = &x_label_[0];
if (!y_label_.empty()) y_label = &y_label_[0];
ImPlot::SetupAxes(x_label, y_label, flags, flags);
if (!x_unit_.empty())
ImPlot::SetupAxisFormat(ImAxis_X1, ("%g " + x_unit_).c_str());
if (!y_unit_.empty())
ImPlot::SetupAxisFormat(ImAxis_Y1, ("%g " + y_unit_).c_str());
ImPlot::SetupAxisLimits(ImAxis_X1, t_ - history_, t_, ImGuiCond_Always);
ImPlot::SetupAxisLimits(ImAxis_Y1, y_min_, y_max_);

for (auto& line : line_specs_) {
auto& spec = line.second;
if (spec.internal_buffer.GetSize() == 0) continue;
ImPlot::PlotLine(spec.name.c_str(), &spec.internal_buffer[0].x,
&spec.internal_buffer[0].y,
spec.internal_buffer.GetSize(), 0,
spec.internal_buffer.GetOffset(), 2 * sizeof(float));
}
ImPlot::EndPlot();
}

if (!fixed_history_) {
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x - 60);
ImGui::SliderFloat("History", &history_, 1, 30, "%.1f s");
}
}
ImPlot::PopStyleColor(3);
}
End();
}
} // namespace quickviz
3 changes: 3 additions & 0 deletions src/imview/test/feature/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,6 @@ target_link_libraries(test_buffered_cv_image_widget PRIVATE imview)

add_executable(test_cairo_widget test_cairo_widget.cpp)
target_link_libraries(test_cairo_widget PRIVATE imview)

add_executable(test_implot_widget test_implot_widget.cpp)
target_link_libraries(test_implot_widget PRIVATE imview)
Loading

0 comments on commit 7170da8

Please sign in to comment.