обратное преобразование
All checks were successful
CMake on a single platform / build (push) Successful in 21s

This commit is contained in:
parovoz 2024-09-18 20:48:40 +04:00 committed by Andrey Alekseev
parent 8fbbf0afcf
commit 42e7ac4d58
2 changed files with 146 additions and 49 deletions

View File

@ -5,6 +5,28 @@
#include <ranges> #include <ranges>
#include <algorithm> #include <algorithm>
#include <Wt/WBootstrap5Theme.h>
#include <Wt/Chart/WAbstractChartModel.h>
#include <Wt/Chart/WAxisSliderWidget.h>
#include <Wt/Chart/WCartesianChart.h>
#include <Wt/Chart/WDataSeries.h>
#include <Wt/WApplication.h>
#include <Wt/WContainerWidget.h>
#include <Wt/WObject.h>
#include <Wt/WShadow.h>
#include <Wt/WSpinBox.h>
#include <Wt/WDoubleSpinBox.h>
#include <Wt/WLabel.h>
#include <Wt/WPushButton.h>
#include <Wt/WColorPicker.h>
#include <Wt/WCheckBox.h>
#include <Wt/WStandardItemModel.h>
#include <Wt/WPanel.h>
class SinModel : public Chart::WAbstractChartModel class SinModel : public Chart::WAbstractChartModel
{ {
public: public:
@ -23,6 +45,7 @@ public:
sinuses | views::transform([x](Sin s){return s.get(x);}), sinuses | views::transform([x](Sin s){return s.get(x);}),
std::plus<double>() std::plus<double>()
).value_or(0); ).value_or(0);
default: default:
try { return sinuses.at(column-2).get(x); } try { return sinuses.at(column-2).get(x); }
catch (out_of_range e){ return 0; } catch (out_of_range e){ return 0; }
@ -45,13 +68,14 @@ private:
class FourierModel : public Chart::WAbstractChartModel class FourierModel : public Chart::WAbstractChartModel
{ {
public: public:
FourierModel(shared_ptr<SinModel> dm) FourierModel(shared_ptr<SinModel> dm, vector<pair<double, double>>& fd)
: Chart::WAbstractChartModel{} : Chart::WAbstractChartModel{}
, discreteModel{dm} , discreteModel{dm}
, fourier_data{fd}
{ {
dm->changed().connect([this](){ dm->changed().connect([this](){
data_.clear(); fourier_data.clear();
data_.reserve(rowCount()); fourier_data.reserve(rowCount());
for ( int k = 0; k < rowCount(); k++ ) { for ( int k = 0; k < rowCount(); k++ ) {
double x = 0, double x = 0,
@ -64,7 +88,7 @@ public:
y-=v*sin(e); y-=v*sin(e);
} }
data_[k] = {sqrt(x*x+y*y)/rowCount(), atan2(y,x)}; fourier_data.push_back({x, y});
} }
changed().emit(); changed().emit();
@ -74,38 +98,45 @@ public:
virtual double data(int row, int column) const override { virtual double data(int row, int column) const override {
switch ( column ) { switch ( column ) {
case 0: return row; case 0: return row;
case 1: return data_[row].first; case 1: return sqrt(pow(fourier_data[row].first,2)+pow(fourier_data[row].second,2))/rowCount();
case 2: return data_[row].second; case 2: return atan2(fourier_data[row].second, fourier_data[row].first);
default: return 0; default: return 0;
} }
} }
virtual int columnCount() const override { return 3; }; virtual int columnCount() const override { return 3; };
virtual int rowCount() const override { return discreteModel->rowCount()/2; } virtual int rowCount() const override { return discreteModel->rowCount(); }
private: private:
shared_ptr<SinModel> discreteModel; shared_ptr<SinModel> discreteModel;
vector<pair<double, double>> data_; vector<pair<double, double>>& fourier_data;
}; };
#include <Wt/WBootstrap5Theme.h> class RestoreModel : public Chart::WAbstractChartModel
{
public:
RestoreModel(vector<pair<double, double>>& fd)
: fourier_data{fd}
{}
#include <Wt/Chart/WAbstractChartModel.h> virtual double data(int row, int column) const override {
#include <Wt/Chart/WAxisSliderWidget.h> if ( column == 0 ) return -M_PI + row * 2*M_PI / (rowCount() - 1);;
#include <Wt/Chart/WCartesianChart.h> if (fourier_data.size()==0) return 0;
#include <Wt/Chart/WDataSeries.h> double y = 0;
#include <Wt/WApplication.h> for (int k = 0; k < fourier_data.size(); k++) {
#include <Wt/WContainerWidget.h> double theta = (2 * M_PI * k * (row+1)) / fourier_data.size();
#include <Wt/WObject.h> y += fourier_data.at(k).first * cos(theta) + fourier_data.at(k).second * sin(theta);
#include <Wt/WShadow.h> }
return y / fourier_data.size();
}
#include <Wt/WSpinBox.h> virtual int columnCount() const override { return 2; };
#include <Wt/WDoubleSpinBox.h> virtual int rowCount() const override { return fourier_data.size(); }
#include <Wt/WLabel.h>
#include <Wt/WPushButton.h> private:
#include <Wt/WColorPicker.h> int rows_ = 0;
#include <Wt/WCheckBox.h> vector<pair<double, double>>& fourier_data;
#include <Wt/WStandardItemModel.h> };
MyApplication::MyApplication(const WEnvironment& env) MyApplication::MyApplication(const WEnvironment& env)
@ -157,6 +188,26 @@ MyApplication::MyApplication(const WEnvironment& env)
pointsDS->setLabelColor(StandardColor::Black); pointsDS->setLabelColor(StandardColor::Black);
chart->addSeries(std::move(pointsDSPtr)); chart->addSeries(std::move(pointsDSPtr));
restoreModel = make_shared<RestoreModel>(fourier_data);
auto restoredSignal = make_unique<Chart::WDataSeries>(1, Chart::SeriesType::Curve);
restoredDS = restoredSignal.get();
restoredDS->setModel(restoreModel);
restoredDS->setXSeriesColumn(0);
restoredDS->setHidden(true);
restoredDS->setPen({StandardColor::Yellow});
restoredDS->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
chart->addSeries(std::move(restoredSignal));
auto restoredSignal_p = make_unique<Chart::WDataSeries>(1, Chart::SeriesType::Point);
restoredDS_points = restoredSignal_p.get();
restoredDS_points->setModel(restoreModel);
restoredDS_points->setXSeriesColumn(0);
restoredDS_points->setHidden(true);
restoredDS_points->setPen({StandardColor::Yellow});
restoredDS_points->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
restoredDS_points->setLabelsEnabled(Chart::Axis::Y);
restoredDS_points->setLabelColor(StandardColor::Black);
chart->addSeries(std::move(restoredSignal_p));
chart->axis(Chart::Axis::X).setMaximumZoomRange( 2*M_PI+1 ); chart->axis(Chart::Axis::X).setMaximumZoomRange( 2*M_PI+1 );
chart->axis(Chart::Axis::X).setMinimumZoomRange( M_PI / 16.0 ); chart->axis(Chart::Axis::X).setMinimumZoomRange( M_PI / 16.0 );
@ -172,7 +223,7 @@ MyApplication::MyApplication(const WEnvironment& env)
chart->axis(Chart::Axis::Y).setGridLinesEnabled(true); chart->axis(Chart::Axis::Y).setGridLinesEnabled(true);
chart->resize(800, 600); chart->resize(800, 500);
chart->setPanEnabled(true); chart->setPanEnabled(true);
chart->setZoomEnabled(true); chart->setZoomEnabled(true);
@ -208,11 +259,13 @@ MyApplication::MyApplication(const WEnvironment& env)
chart->update(); chart->update();
}); });
form->addNew<WText>("a="); form->addNew<WText>("α=");
auto a = form->addNew<WDoubleSpinBox>(); auto a = form->addNew<WDoubleSpinBox>();
form->addNew<WText>("w="); a->setValue(1);
form->addNew<WText>("ω=");
auto w = form->addNew<WDoubleSpinBox>(); auto w = form->addNew<WDoubleSpinBox>();
form->addNew<WText>("f="); w->setValue(1);
form->addNew<WText>("φ=");
auto f = form->addNew<WDoubleSpinBox>(); auto f = form->addNew<WDoubleSpinBox>();
for ( auto i : {a,w,f} ) { for ( auto i : {a,w,f} ) {
@ -233,12 +286,13 @@ MyApplication::MyApplication(const WEnvironment& env)
right->setOverflow(Overflow::Hidden, Orientation::Horizontal); right->setOverflow(Overflow::Hidden, Orientation::Horizontal);
{ // дискретизация { // дискретизация
auto form = right->addNew<WContainerWidget>(); auto form = right->addNew<WContainerWidget>();
form->addStyleClass("d-flex flex-row gap-3"); form->addStyleClass("d-flex flex-row gap-3 align-items-center my-1");
auto show = form->addNew<WCheckBox>("дискретизация"); form->addNew<WText>("частота дискретизации");
auto freq = form->addNew<WSpinBox>(); auto freq = form->addNew<WSpinBox>();
freq->setWidth(120); freq->setWidth(120);
freq->setRange(0, 1000); freq->setRange(0, 1000);
auto show = form->addNew<WCheckBox>("показать точки");
show->clicked().connect([=,this](){ show->clicked().connect([=,this](){
pointsDS->setHidden(!show->isChecked()); pointsDS->setHidden(!show->isChecked());
@ -248,27 +302,18 @@ MyApplication::MyApplication(const WEnvironment& env)
freq->changed().connect([=,this](){ freq->changed().connect([=,this](){
discreteModel->setRows(freq->value()); discreteModel->setRows(freq->value());
}); restoredDS->setHidden( freq->value()==0 || !lineCheck->isChecked() );
restoredDS_points->setHidden(freq->value()==0 || !pointsCheck->isChecked() );
discreteModel->changed().connect([this](){ chart->update();
vals->clear();
for ( int i = 0; i < discreteModel->rowCount(); i++ ) {
auto x = discreteModel->data(i,1);
vals->addNew<WText>(format("{:.2f}", x))
->addStyleClass("border rounded-3 p-1");
}
}); });
} }
vals = right->addNew<WContainerWidget>();
vals->setOverflow(Overflow::Auto, Orientation::Horizontal);
vals->addStyleClass("d-flex gap-1 p-1 text-nowrap");
fourierChart = right->addNew<Chart::WCartesianChart>(); fourierChart = right->addNew<Chart::WCartesianChart>();
fourierChart->setPlotAreaPadding(100); fourierChart->setPlotAreaPadding(100, Side::Right | Side::Left);
fourierChart->setPlotAreaPadding(50, Side::Top | Side::Bottom);
fourierChart->setType(Chart::ChartType::Scatter); fourierChart->setType(Chart::ChartType::Scatter);
fourierModel = make_shared<FourierModel>(discreteModel); fourierModel = make_shared<FourierModel>(discreteModel, fourier_data);
fourierModel->changed().connect([this](){restoreModel->changed().emit();});
fourierChart->setModel(static_pointer_cast<Chart::WAbstractChartModel>(fourierModel)); fourierChart->setModel(static_pointer_cast<Chart::WAbstractChartModel>(fourierModel));
auto seriesPtr = make_unique<Chart::WDataSeries>(2, Chart::SeriesType::Point); auto seriesPtr = make_unique<Chart::WDataSeries>(2, Chart::SeriesType::Point);
@ -300,7 +345,52 @@ MyApplication::MyApplication(const WEnvironment& env)
fourierChart->setZoomEnabled(true); fourierChart->setZoomEnabled(true);
fourierChart->setCrosshairEnabled(true); fourierChart->setCrosshairEnabled(true);
fourierChart->resize(800, 600); fourierChart->resize(800, 400);
fourierChart->axis(Chart::Axis::X).setMinimumZoomRange(0.1);
auto sliderWidget = right->addNew<Chart::WAxisSliderWidget>(ampSeries);
sliderWidget->resize(800, 80);
sliderWidget->setSelectionAreaPadding(40, Side::Left | Side::Right);
fourierChart->axis(Chart::Axis::X).setZoom(2);
fourierChart->axis(Chart::Axis::X).setPan(0);
auto restoreControls = right->addNew<WPanel>();
restoreControls->setTitle("Восстановленный сигнал");
restoreControls->setCentralWidget(make_unique<WContainerWidget>());
auto c = (WContainerWidget*)restoreControls->centralWidget();
{
auto line = c->addNew<WContainerWidget>();
line->addStyleClass("d-flex flex-row align-items-center gap-1 p-3");
lineCheck = line->addNew<WCheckBox>("показывать график");
lineCheck->clicked().connect([this](){
restoredDS->setHidden(!lineCheck->isChecked());
chart->update();
});
auto lineColor = line->addNew<WColorPicker>(WColor{StandardColor::Yellow});
lineColor->setWidth(50);
lineColor->changed().connect([lineColor,this](){
restoredDS->setPen(WPen{lineColor->color()});
chart->update();
});
}
{
auto points = c->addNew<WContainerWidget>();
points->addStyleClass("d-flex flex-row align-items-center gap-1 p-3");
pointsCheck = points->addNew<WCheckBox>("показывать точки");
pointsCheck->clicked().connect([this](){
restoredDS_points->setHidden(!pointsCheck->isChecked());
chart->update();
});
auto pointsColor = points->addNew<WColorPicker>(WColor{StandardColor::Yellow});
pointsColor->setWidth(50);
pointsColor->changed().connect([pointsColor,this](){
WPen p{pointsColor->color()};
restoredDS_points->setPen(p);
chart->update();
});
}
} }
void MyApplication::addSin(Sin s) { void MyApplication::addSin(Sin s) {

View File

@ -30,6 +30,7 @@ typedef struct sin Sin;
class SinModel; class SinModel;
class FourierModel; class FourierModel;
class RestoreModel;
class MyApplication : public WApplication class MyApplication : public WApplication
{ {
@ -43,6 +44,14 @@ private:
shared_ptr<SinModel> discreteModel; shared_ptr<SinModel> discreteModel;
Chart::WDataSeries* pointsDS; Chart::WDataSeries* pointsDS;
shared_ptr<RestoreModel> restoreModel;
vector<pair<double, double>> fourier_data;
Chart::WDataSeries* restoredDS;
Chart::WDataSeries* restoredDS_points;
WCheckBox* lineCheck;
WCheckBox* pointsCheck;
Chart::WCartesianChart* fourierChart; Chart::WCartesianChart* fourierChart;
shared_ptr<FourierModel> fourierModel; shared_ptr<FourierModel> fourierModel;
@ -51,8 +60,6 @@ private:
WCheckBox* showAll; WCheckBox* showAll;
size_t checkCount = 0; size_t checkCount = 0;
WContainerWidget* vals;
vector<Sin> sinuses; vector<Sin> sinuses;
void addSin(Sin s); void addSin(Sin s);