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