diff --git a/SciQLopPlots/bindings/bindings.xml b/SciQLopPlots/bindings/bindings.xml
index 52a670c..729088b 100644
--- a/SciQLopPlots/bindings/bindings.xml
+++ b/SciQLopPlots/bindings/bindings.xml
@@ -87,6 +87,16 @@
+
+
+
+
+
+
+
+
+
+
@@ -362,6 +372,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/SciQLopPlots/bindings/helper_scripts/shiboken-gen.py b/SciQLopPlots/bindings/helper_scripts/shiboken-gen.py
index b3710e9..b893841 100644
--- a/SciQLopPlots/bindings/helper_scripts/shiboken-gen.py
+++ b/SciQLopPlots/bindings/helper_scripts/shiboken-gen.py
@@ -39,11 +39,19 @@ def cpp_flags(build_dir, ref_build_target):
'-std=c++17',
'--generator-set=shiboken']
-if os.path.exists('/opt/rh/gcc-toolset-11/root/usr/include/c++/11'):
- shiboken_constant_args += [
- '-I/opt/rh/gcc-toolset-11/root/usr/include/c++/11',
- '-I/opt/rh/gcc-toolset-11/root/usr/include/c++/11/x86_64-redhat-linux'
- ]
+if 'linux' in platform.system().lower():
+ if os.path.exists('/opt/rh/gcc-toolset-11/root/usr/include/c++/11'):
+ shiboken_constant_args += [
+ '-I/opt/rh/gcc-toolset-11/root/usr/include/c++/11',
+ '-I/opt/rh/gcc-toolset-11/root/usr/include/c++/11/x86_64-redhat-linux'
+ ]
+ elif os.path.exists('/usr/include/c++/14'):
+ shiboken_constant_args += [
+ '-I/usr/include/c++/14',
+ '-I/usr/include/c++/14/x86_64-redhat-linux'
+ ]
+
+
cmd = [args.shiboken, args.input_header, args.input_xml ] + shiboken_constant_args + cpp_flags(args.build_directory, args.ref_build_target) + [ f'--typesystem-paths={args.typesystem_paths}', f'--output-directory={args.output_directory}']
diff --git a/SciQLopPlots/meson.build b/SciQLopPlots/meson.build
index 8ca7691..87956bd 100644
--- a/SciQLopPlots/meson.build
+++ b/SciQLopPlots/meson.build
@@ -53,11 +53,13 @@ shiboken_dep = declare_dependency(compile_args: shiboken_build_flags, link_args:
moc_headers = [
'bindings/_QCustomPlot.hpp',
project_source_root + '/include/SciQLopPlots/SciQLopGraph.hpp',
+ project_source_root + '/include/SciQLopPlots/SciQLopCurve.hpp',
project_source_root + '/include/SciQLopPlots/SciQLopColorMap.hpp',
project_source_root + '/include/SciQLopPlots/SciQLopVerticalSpan.hpp',
project_source_root + '/include/SciQLopPlots/SciQLopPlotItem.hpp',
project_source_root + '/include/SciQLopPlots/SciQLopPlot.hpp',
project_source_root + '/include/SciQLopPlots/SciQLopGraphResampler.hpp',
+ project_source_root + '/include/SciQLopPlots/SciQLopCurveResampler.hpp',
project_source_root + '/include/SciQLopPlots/SciQLopColorMapResampler.hpp',
project_source_root + '/qcustomplot-source/qcustomplot.h'
]
@@ -82,6 +84,7 @@ sources = moc_files \
'../src/SciQLopPlotItem.cpp',
'../src/SciQLopVerticalSpan.cpp',
'../src/SciQLopGraph.cpp',
+ '../src/SciQLopCurve.cpp',
'../src/SciQLopColorMap.cpp',
'../src/SciQLopColorMapResampler.cpp',
'../src/BufferProtocol.cpp',
diff --git a/include/SciQLopPlots/SciQLopCurve.hpp b/include/SciQLopPlots/SciQLopCurve.hpp
new file mode 100644
index 0000000..ba679e9
--- /dev/null
+++ b/include/SciQLopPlots/SciQLopCurve.hpp
@@ -0,0 +1,79 @@
+/*------------------------------------------------------------------------------
+-- This file is a part of the SciQLop Software
+-- Copyright (C) 2024, Plasma Physics Laboratory - CNRS
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, write to the Free Software
+-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-------------------------------------------------------------------------------*/
+/*-- Author : Alexis Jeandet
+-- Mail : alexis.jeandet@member.fsf.org
+----------------------------------------------------------------------------*/
+#pragma once
+
+
+#include "BufferProtocol.hpp"
+#include "SciQLopGraph.hpp"
+#include
+class QThread;
+struct CurveResampler;
+
+class SciQLopCurve : public QObject
+{
+ CurveResampler* _resampler = nullptr;
+ QThread* _resampler_thread = nullptr;
+
+ QCPAxis* _keyAxis;
+ QCPAxis* _valueAxis;
+ QList _curves;
+
+ Q_OBJECT
+
+ inline QCustomPlot* _plot() const { return qobject_cast(this->parent()); }
+
+
+ void _range_changed(const QCPRange& newRange, const QCPRange& oldRange);
+
+ void _setCurveData(std::size_t index, QVector data);
+
+ Q_SIGNAL void _setCurveDataSig(std::size_t index, QVector data);
+
+ void clear_curves(bool curve_already_removed = false);
+ void clear_resampler();
+ void create_resampler(const QStringList& labels);
+ void curve_got_removed_from_plot(QCPCurve* curve);
+
+public:
+ Q_ENUMS(FractionStyle)
+ explicit SciQLopCurve(QCustomPlot* parent, QCPAxis* keyAxis, QCPAxis* valueAxis,
+ const QStringList& labels,
+ SciQLopGraph::DataOrder dataOrder = SciQLopGraph::DataOrder::xFirst);
+
+ explicit SciQLopCurve(QCustomPlot* parent, QCPAxis* keyAxis, QCPAxis* valueAxis,
+ SciQLopGraph::DataOrder dataOrder = SciQLopGraph::DataOrder::xFirst);
+
+ virtual ~SciQLopCurve() override;
+
+ void setData(Array_view&& x, Array_view&& y, bool ignoreCurrentRange = false);
+ inline QCPCurve* graphAt(std::size_t index) const { return _curves[index]; }
+ void create_graphs(const QStringList& labels);
+
+ inline std::size_t line_count() { return std::size(this->_curves); }
+
+#ifndef BINDINGS_H
+ Q_SIGNAL void range_changed(const QCPRange& newRange, bool missData);
+#endif
+
+private:
+ SciQLopGraph::DataOrder _dataOrder = SciQLopGraph::DataOrder::xFirst;
+};
diff --git a/include/SciQLopPlots/SciQLopCurveResampler.hpp b/include/SciQLopPlots/SciQLopCurveResampler.hpp
new file mode 100644
index 0000000..f34d44c
--- /dev/null
+++ b/include/SciQLopPlots/SciQLopCurveResampler.hpp
@@ -0,0 +1,130 @@
+/*------------------------------------------------------------------------------
+-- This file is a part of the SciQLop Software
+-- Copyright (C) 2024, Plasma Physics Laboratory - CNRS
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, write to the Free Software
+-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-------------------------------------------------------------------------------*/
+/*-- Author : Alexis Jeandet
+-- Mail : alexis.jeandet@member.fsf.org
+----------------------------------------------------------------------------*/
+#pragma once
+
+#include "BufferProtocol.hpp"
+#include "SciQLopCurve.hpp"
+#include "SciQLopGraph.hpp"
+#include
+#include
+
+
+static inline QVector copy_data(
+ const double* x, const double* y, std::size_t x_size, const int y_incr = 1)
+{
+ QVector data(x_size);
+ const double* current_y_it = y;
+ for (auto i = 0UL; i < x_size; i++)
+ {
+ data[i] = QCPCurveData { static_cast(i), x[i], *current_y_it };
+ current_y_it += y_incr;
+ }
+ return data;
+}
+
+
+struct CurveResampler : public QObject
+{
+ Q_OBJECT
+ QMutex _data_mutex;
+ QMutex _range_mutex;
+ Array_view _x;
+ Array_view _y;
+ SciQLopGraph::DataOrder _dataOrder;
+ QCPRange _data_x_range;
+ std::size_t _line_cnt;
+
+ Q_SIGNAL void _resample_sig(const QCPRange newRange);
+ inline void _resample_slot(const QCPRange newRange)
+ {
+ QMutexLocker locker(&_data_mutex);
+ if (_x.data() != nullptr && _x.flat_size() > 0)
+ {
+
+
+ const auto y_incr = (_dataOrder == SciQLopGraph::DataOrder::xFirst) ? 1UL : _line_cnt;
+ for (auto line_index = 0UL; line_index < _line_cnt; line_index++)
+ {
+ const auto count = std::size(_x);
+ const auto start_y = _y.data()
+ + (line_index
+ * ((_dataOrder == SciQLopGraph::DataOrder::xFirst) ? _x.flat_size() : 1));
+ emit this->setGraphData(line_index, copy_data(_x.data(), start_y, count, y_incr));
+ }
+ _x.release();
+ _y.release();
+ }
+ emit this->refreshPlot();
+ }
+
+public:
+#ifndef BINDINGS_H
+ Q_SIGNAL void setGraphData(std::size_t index, QVector data);
+ Q_SIGNAL void refreshPlot();
+#endif
+
+ CurveResampler(SciQLopGraph::DataOrder dataOrder, std::size_t line_cnt)
+ : _dataOrder { dataOrder }, _line_cnt { line_cnt }
+ {
+
+ connect(this, &CurveResampler::_resample_sig, this, &CurveResampler::_resample_slot,
+ Qt::QueuedConnection);
+ }
+
+ inline void resample(const QCPRange newRange) { emit this->_resample_sig(newRange); }
+
+ inline QCPRange x_range()
+ {
+ QMutexLocker locker(&_range_mutex);
+ auto rng = this->_data_x_range;
+ return rng;
+ }
+
+ inline void set_line_count(std::size_t line_cnt)
+ {
+ QMutexLocker locker(&_data_mutex);
+ this->_line_cnt = line_cnt;
+ }
+
+ inline void setData(Array_view&& x, Array_view&& y)
+ {
+ {
+ QMutexLocker locker(&_data_mutex);
+
+ _x = std::move(x);
+ _y = std::move(y);
+
+ const auto len = _x.flat_size();
+ if (len > 0)
+ {
+ _data_x_range.lower = _x.data()[0];
+ _data_x_range.upper = _x.data()[len - 1];
+ }
+ else
+ {
+ _data_x_range.lower = std::nan("");
+ _data_x_range.upper = std::nan("");
+ }
+ this->resample(_data_x_range);
+ }
+ }
+};
diff --git a/include/SciQLopPlots/SciQLopPlot.hpp b/include/SciQLopPlots/SciQLopPlot.hpp
index 7b79a62..52141a3 100644
--- a/include/SciQLopPlots/SciQLopPlot.hpp
+++ b/include/SciQLopPlots/SciQLopPlot.hpp
@@ -25,6 +25,7 @@
#include "constants.hpp"
#include
+#include
#include
#include
#include
@@ -67,6 +68,20 @@ class SciQLopPlot : public QCustomPlot
return sg;
}
+ inline SciQLopCurve* addSciQLopCurve(QCPAxis* x, QCPAxis* y, QStringList labels,
+ SciQLopGraph::DataOrder dataOrder = SciQLopGraph::DataOrder::xFirst)
+ {
+ auto sg = new SciQLopCurve(this, x, y, labels, dataOrder);
+ return sg;
+ }
+
+ inline SciQLopCurve* addSciQLopCurve(
+ QCPAxis* x, QCPAxis* y, SciQLopGraph::DataOrder dataOrder = SciQLopGraph::DataOrder::xFirst)
+ {
+ auto sg = new SciQLopCurve(this, x, y, dataOrder);
+ return sg;
+ }
+
inline SciQLopColorMap* addSciQLopColorMap(QCPAxis* x, QCPAxis* y, const QString& name,
SciQLopColorMap::DataOrder dataOrder = SciQLopColorMap::DataOrder::xFirst)
{
diff --git a/meson.build b/meson.build
index 8b8b577..b07dd52 100644
--- a/meson.build
+++ b/meson.build
@@ -10,6 +10,8 @@ extra_files=files(
'pyproject.toml',
'setup.cfg',
'.github/workflows/build_wheels.yml',
+ 'Docker/Dockerfile',
+ 'Docker/build.sh',
'README.md', 'COPYING'
)
diff --git a/src/SciQLopCurve.cpp b/src/SciQLopCurve.cpp
new file mode 100644
index 0000000..3d79954
--- /dev/null
+++ b/src/SciQLopCurve.cpp
@@ -0,0 +1,118 @@
+/*------------------------------------------------------------------------------
+-- This file is a part of the SciQLop Software
+-- Copyright (C) 2023, Plasma Physics Laboratory - CNRS
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 2 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program; if not, write to the Free Software
+-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-------------------------------------------------------------------------------*/
+/*-- Author : Alexis Jeandet
+-- Mail : alexis.jeandet@member.fsf.org
+----------------------------------------------------------------------------*/
+#include "SciQLopPlots/SciQLopCurve.hpp"
+#include "SciQLopPlots/SciQLopCurveResampler.hpp"
+
+void SciQLopCurve::_range_changed(const QCPRange& newRange, const QCPRange& oldRange)
+{
+ this->_resampler->resample(newRange);
+ emit this->range_changed(newRange, false);
+}
+
+void SciQLopCurve::_setCurveData(std::size_t index, QVector data)
+{
+ if (index < std::size(_curves))
+ _curves[index]->data()->set(std::move(data), true);
+}
+
+void SciQLopCurve::clear_curves(bool curve_already_removed)
+{
+ if (!curve_already_removed)
+ {
+ for (auto curve : _curves)
+ {
+ this->_plot()->removePlottable(curve);
+ }
+ }
+ this->_curves.clear();
+}
+
+void SciQLopCurve::clear_resampler()
+{
+ connect(this->_resampler_thread, &QThread::finished, this->_resampler, &QThread::deleteLater);
+ disconnect(this->_resampler, &CurveResampler::setGraphData, this, &SciQLopCurve::_setCurveData);
+ disconnect(this->_resampler, &CurveResampler::refreshPlot, nullptr, nullptr);
+ this->_resampler_thread->quit();
+ this->_resampler_thread->wait();
+ delete this->_resampler_thread;
+ this->_resampler = nullptr;
+ this->_resampler_thread = nullptr;
+}
+
+void SciQLopCurve::create_resampler(const QStringList& labels)
+{
+ this->_resampler = new CurveResampler(_dataOrder, std::size(labels));
+ this->_resampler_thread = new QThread();
+ this->_resampler->moveToThread(this->_resampler_thread);
+ this->_resampler_thread->start(QThread::LowPriority);
+ connect(this->_resampler, &CurveResampler::setGraphData, this, &SciQLopCurve::_setCurveData,
+ Qt::QueuedConnection);
+ connect(
+ this->_resampler, &CurveResampler::refreshPlot, this,
+ [this]() { this->_plot()->replot(QCustomPlot::rpQueuedReplot); }, Qt::QueuedConnection);
+}
+
+void SciQLopCurve::curve_got_removed_from_plot(QCPCurve* curve)
+{
+ this->_curves.removeOne(curve);
+}
+
+SciQLopCurve::SciQLopCurve(QCustomPlot* parent, QCPAxis* keyAxis, QCPAxis* valueAxis,
+ const QStringList& labels, SciQLopGraph::DataOrder dataOrder)
+ : QObject(parent), _keyAxis { keyAxis }, _valueAxis { valueAxis }, _dataOrder { dataOrder }
+{
+ create_resampler(labels);
+ this->create_graphs(labels);
+}
+
+SciQLopCurve::SciQLopCurve(
+ QCustomPlot* parent, QCPAxis* keyAxis, QCPAxis* valueAxis, SciQLopGraph::DataOrder dataOrder)
+ : QObject(parent), _keyAxis { keyAxis }, _valueAxis { valueAxis }, _dataOrder { dataOrder }
+{
+ create_resampler({});
+}
+
+SciQLopCurve::~SciQLopCurve()
+{
+ clear_curves();
+ clear_resampler();
+}
+
+void SciQLopCurve::setData(Array_view&& x, Array_view&& y, bool ignoreCurrentRange)
+{
+ this->_resampler->setData(std::move(x), std::move(y));
+}
+
+void SciQLopCurve::create_graphs(const QStringList& labels)
+{
+ if (std::size(_curves))
+ clear_curves();
+ for (const auto& label : labels)
+ {
+ const auto curve = new QCPCurve(_keyAxis, _valueAxis);
+ _curves.append(curve);
+ curve->setName(label);
+ connect(curve, &QCPCurve::destroyed, this,
+ [this, curve]() { this->curve_got_removed_from_plot(curve); });
+ }
+ _resampler->set_line_count(std::size(_curves));
+}