From bcdc47bc3403e0f23ce6481e7c8298bc776fe772 Mon Sep 17 00:00:00 2001 From: Max Maisel Date: Fri, 14 Feb 2020 18:41:18 +0100 Subject: [PATCH] Add generic plot widget. This widget will be used in the new Compressor2 effect but it is designed for use in other effects as well. Signed-off-by: Max Maisel --- src/CMakeLists.txt | 2 + src/ShuttleGui.cpp | 28 +++++++++ src/ShuttleGui.h | 6 ++ src/widgets/Plot.cpp | 136 +++++++++++++++++++++++++++++++++++++++++++ src/widgets/Plot.h | 58 ++++++++++++++++++ 5 files changed, 230 insertions(+) create mode 100644 src/widgets/Plot.cpp create mode 100644 src/widgets/Plot.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ac44c420..cbf28101f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -949,6 +949,8 @@ list( APPEND SOURCES PRIVATE widgets/Overlay.h widgets/OverlayPanel.cpp widgets/OverlayPanel.h + widgets/Plot.cpp + widgets/Plot.h widgets/PopupMenuTable.cpp widgets/PopupMenuTable.h widgets/ProgressDialog.cpp diff --git a/src/ShuttleGui.cpp b/src/ShuttleGui.cpp index 3328d80b2..ead5aa0f2 100644 --- a/src/ShuttleGui.cpp +++ b/src/ShuttleGui.cpp @@ -121,6 +121,7 @@ for registering for changes. #include "widgets/wxTextCtrlWrapper.h" #include "AllThemeResources.h" +#include "widgets/Plot.h" #include "widgets/SliderTextCtrl.h" #if wxUSE_ACCESSIBILITY @@ -777,6 +778,33 @@ void ShuttleGuiBase::AddConstTextBox( UpdateSizers(); } +Plot* ShuttleGuiBase::AddPlot( const TranslatableString &Prompt, + double x_min, double x_max, double y_min, double y_max, + const TranslatableString& x_label, const TranslatableString& y_label, + int x_format, int y_format, int count) +{ + HandleOptionality( Prompt ); + AddPrompt( Prompt ); + UseUpId(); + if( mShuttleMode != eIsCreating ) + return wxDynamicCast(wxWindow::FindWindowById(miId, mpDlg), Plot); + Plot* pPlot; + mpWind = pPlot = safenew Plot(GetParent(), miId, + x_min, x_max, y_min, y_max, x_label, y_label, + x_format, y_format, count, + wxDefaultPosition, wxDefaultSize, + GetStyle( SliderTextCtrl::HORIZONTAL ) + ); +#if wxUSE_ACCESSIBILITY + // so that name can be set on a standard control + mpWind->SetAccessible(safenew WindowAccessible(mpWind)); +#endif + mpWind->SetName(wxStripMenuCodes(Prompt.Translation())); + miProp=1; + UpdateSizers(); + return pPlot; +} + wxListBox * ShuttleGuiBase::AddListBox(const wxArrayStringEx &choices) { UseUpId(); diff --git a/src/ShuttleGui.h b/src/ShuttleGui.h index da01fa8d6..04a97b28e 100644 --- a/src/ShuttleGui.h +++ b/src/ShuttleGui.h @@ -28,6 +28,7 @@ class ChoiceSetting; class wxArrayStringEx; +class Plot; class SliderTextCtrl; @@ -348,6 +349,11 @@ public: void AddConstTextBox( const TranslatableString &Caption, const TranslatableString & Value ); + Plot* AddPlot( const TranslatableString &Prompt, + double x_min, double x_max, double y_min, double y_max, + const TranslatableString& x_label, const TranslatableString& y_label, + int x_format = 1, int y_format = 1, int count = 1 ); + //-- Start and end functions. These are used for sizer, or other window containers // and create the appropriate widget. void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1); diff --git a/src/widgets/Plot.cpp b/src/widgets/Plot.cpp new file mode 100644 index 000000000..fb1c1bea0 --- /dev/null +++ b/src/widgets/Plot.cpp @@ -0,0 +1,136 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + Plot.cpp + + Max Maisel + +*******************************************************************//** + +\class Plot +\brief A customizable generic plot widget. + +*//*******************************************************************/ + + +#include "../Audacity.h" +#include "audacity/Types.h" +#include "Plot.h" +#include "Ruler.h" +#include "../AColor.h" +#include "../Theme.h" +#include "../AllThemeResources.h" + +#include +#include +#include + +Plot::Plot(wxWindow *parent, wxWindowID winid, + float x_min, float x_max, float y_min, float y_max, + const TranslatableString& xlabel, const TranslatableString& ylabel, + int xformat, int yformat, int count, + const wxPoint& pos, const wxSize& size, long style) + : + wxPanelWrapper(parent, winid, pos, size, style), + m_xmin(x_min), m_xmax(x_max), m_ymin(y_min), m_ymax(y_max), + m_plots(count) +{ + m_xruler = std::unique_ptr(safenew Ruler); + m_xruler->SetOrientation(wxHORIZONTAL); + m_xruler->SetFormat(static_cast(xformat)); + m_xruler->SetUnits(xlabel); + m_xruler->SetFlip(true); + + m_yruler = std::unique_ptr(safenew Ruler); + m_yruler->SetOrientation(wxVERTICAL); + m_yruler->SetFormat(static_cast(yformat)); + m_yruler->SetUnits(ylabel); +} + +void Plot::OnPaint(wxPaintEvent & evt) +{ + wxPaintDC dc(this); + + int width, height; + GetSize(&width, &height); + +#if defined(__WXMSW__) + dc.Clear(); +#endif + + // Ruler + int w = 0; + int h = 0; + + m_xruler->SetBounds(0, 0, width, height); + m_xruler->SetRange(m_xmin, m_xmax); + m_xruler->GetMaxSize(NULL, &h); + + m_yruler->SetBounds(0, 0, width, height); + m_yruler->SetRange(m_ymax, m_ymin); + m_yruler->GetMaxSize(&w, NULL); + + m_xruler->SetBounds(w, height - h, width, height); + m_yruler->SetBounds(0, 0, w, height - h); + + m_xruler->SetTickColour( theTheme.Colour( clrGraphLabels )); + m_yruler->SetTickColour( theTheme.Colour( clrGraphLabels )); + + wxRect border; + border.x = w; + border.y = 0; + border.width = width - w; + border.height = height - h + 1; + + dc.SetBrush(*wxWHITE_BRUSH); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.DrawRectangle(border); + + m_xruler->DrawGrid(dc, border.height, true, true, border.x, border.y); + m_yruler->DrawGrid(dc, border.width, true, true, border.x, border.y); + + for(const auto& plot : m_plots) + { + wxASSERT(plot.xdata.size() == plot.ydata.size()); + if(plot.xdata.size() == 0) + continue; + dc.SetPen(*plot.pen); + + size_t xsize = plot.xdata.size(); + for(size_t i = 1; i < xsize; ++i) + { + AColor::Line(dc, + XToScreen(plot.xdata[i-1], border), + YToScreen(plot.ydata[i-1], border), + XToScreen(plot.xdata[i], border), + YToScreen(plot.ydata[i], border)); + } + } + + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.SetPen(*wxBLACK_PEN); + dc.DrawRectangle(border); + m_xruler->Draw(dc); + m_yruler->Draw(dc); +} + +void Plot::OnSize(wxSizeEvent & evt) +{ + Refresh(false); +} + +int Plot::XToScreen(float x, wxRect& rect) +{ + return rect.x + lrint((x-m_xmin)*rect.width/(m_xmax-m_xmin)); +} + +int Plot::YToScreen(float y, wxRect& rect) +{ + return rect.y + rect.height - lrint((y-m_ymin)*rect.height/(m_ymax-m_ymin)); +} + +BEGIN_EVENT_TABLE(Plot, wxPanelWrapper) + EVT_PAINT(Plot::OnPaint) + EVT_SIZE(Plot::OnSize) +END_EVENT_TABLE() diff --git a/src/widgets/Plot.h b/src/widgets/Plot.h new file mode 100644 index 000000000..86253f956 --- /dev/null +++ b/src/widgets/Plot.h @@ -0,0 +1,58 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + Plot.h + + Max Maisel + + This class is a generic plot. + +**********************************************************************/ + +#ifndef __AUDACITY_PLOT__ +#define __AUDACITY_PLOT__ + +#include "wxPanelWrapper.h" // to inherit + +#include "MemoryX.h" + +class Ruler; + +struct PlotData +{ + std::unique_ptr pen; + std::vector xdata; + std::vector ydata; +}; + +class Plot : public wxPanelWrapper +{ + public: + Plot(wxWindow *parent, wxWindowID winid, + float x_min, float x_max, float y_min, float y_max, + const TranslatableString& xlabel, const TranslatableString& ylabel, + int xformat = 1, int yformat = 1, //Ruler::RealFormat + int count = 1, const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxTAB_TRAVERSAL | wxNO_BORDER); + + inline PlotData* GetPlotData(int id) + { return &m_plots[id]; } + + private: + void OnPaint(wxPaintEvent & evt); + void OnSize(wxSizeEvent & evt); + + float m_xmin, m_xmax; + float m_ymin, m_ymax; + std::vector m_plots; + std::unique_ptr m_xruler, m_yruler; + + int XToScreen(float x, wxRect& rect); + int YToScreen(float y, wxRect& rect); + + DECLARE_EVENT_TABLE() +}; + +#endif