init
This commit is contained in:
commit
73aec484df
48
CMakeLists.txt
Normal file
48
CMakeLists.txt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
project(fourier LANGUAGES C CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
find_package(Wt REQUIRED
|
||||||
|
Wt
|
||||||
|
HTTP
|
||||||
|
)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
include_directories("src")
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME}
|
||||||
|
src/main.cpp
|
||||||
|
src/MyApplication.h
|
||||||
|
src/MyApplication.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
Wt::Wt
|
||||||
|
Wt::HTTP
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
run.sh.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.wt
|
||||||
|
)
|
||||||
|
|
||||||
|
install( TARGETS ${PROJECT_NAME}
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
install( FILES wt_config.xml DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/${PROJECT_NAME})
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.wt
|
||||||
|
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
PERMISSIONS
|
||||||
|
OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
||||||
|
GROUP_READ GROUP_EXECUTE
|
||||||
|
WORLD_READ WORLD_EXECUTE
|
||||||
|
)
|
||||||
|
|
25
run.sh.in
Normal file
25
run.sh.in
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
pid=''
|
||||||
|
run=true
|
||||||
|
|
||||||
|
start() {
|
||||||
|
@CMAKE_INSTALL_FULL_BINDIR@/@PROJECT_NAME@ \
|
||||||
|
--docroot=@CMAKE_INSTALL_FULL_DATADIR@/@PROJECT_NAME@/docroot \
|
||||||
|
--http-address 0.0.0.0 --http-port 8080 \
|
||||||
|
--config @CMAKE_INSTALL_FULL_SYSCONFDIR@/@PROJECT_NAME@/wt_config.xml \
|
||||||
|
--resources-dir /usr/share/Wt/resources &> >(logger --tag @PROJECT_NAME@) & pid="$!"
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdown() {
|
||||||
|
run=false
|
||||||
|
(( $pid )) && kill "$pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap shutdown EXIT
|
||||||
|
|
||||||
|
start
|
||||||
|
while $run
|
||||||
|
do
|
||||||
|
wait "$pid" || { logger --tag @PROJECT_NAME@ -p 3 "restarting server after error"; start; }
|
||||||
|
done
|
405
src/MyApplication.cpp
Normal file
405
src/MyApplication.cpp
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
#include "MyApplication.h"
|
||||||
|
|
||||||
|
#include <Wt/Chart/WAbstractChartModel.h>
|
||||||
|
#include <cfloat>
|
||||||
|
#include <ranges>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
class SinModel : public Chart::WAbstractChartModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SinModel(vector<Sin>& data, int rows)
|
||||||
|
: Chart::WAbstractChartModel{}
|
||||||
|
, sinuses{data}
|
||||||
|
, rows_{rows}
|
||||||
|
{ }
|
||||||
|
|
||||||
|
virtual double data(int row, int column) const override {
|
||||||
|
double x = -M_PI + row * 2*M_PI / (rowCount() - 1);
|
||||||
|
switch ( column ) {
|
||||||
|
case 0: return x;
|
||||||
|
case 1:
|
||||||
|
return ranges::fold_left_first(
|
||||||
|
sinuses | views::transform([x](Sin s){return s.get(x);}),
|
||||||
|
std::plus<double>()
|
||||||
|
).value_or(0);
|
||||||
|
default:
|
||||||
|
try { return sinuses.at(column-2).get(x); }
|
||||||
|
catch (out_of_range e){ return 0; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual int columnCount() const override { return 2 + sinuses.size(); };
|
||||||
|
virtual int rowCount() const override { return rows_; }
|
||||||
|
|
||||||
|
void setRows(int rows) {
|
||||||
|
rows_ = rows;
|
||||||
|
changed().emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int rows_;
|
||||||
|
vector<Sin>& sinuses;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class FourierModel : public Chart::WAbstractChartModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FourierModel(shared_ptr<SinModel> dm)
|
||||||
|
: Chart::WAbstractChartModel{}
|
||||||
|
, discreteModel{dm}
|
||||||
|
{
|
||||||
|
dm->changed().connect([this](){
|
||||||
|
data_.clear();
|
||||||
|
data_.reserve(rowCount());
|
||||||
|
|
||||||
|
vector<double> vals;
|
||||||
|
vals.reserve( discreteModel->rowCount());
|
||||||
|
|
||||||
|
double first_positive = DBL_MAX;
|
||||||
|
for(int i = 0; i < discreteModel->rowCount(); i++) {
|
||||||
|
auto v = discreteModel->data(i, 1);
|
||||||
|
vals.push_back(v);
|
||||||
|
|
||||||
|
if ( abs(vals[i]) > 0 && abs(vals[i]) < first_positive )
|
||||||
|
first_positive = abs(vals[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( int k = 0; k < rowCount(); k++ ) {
|
||||||
|
if ( first_positive > 0 ) {
|
||||||
|
double x = 0,
|
||||||
|
y = 0;
|
||||||
|
for ( int n = 0; n < discreteModel->rowCount(); n++ ) {
|
||||||
|
double e = 2*M_PI*k*n/discreteModel->rowCount();
|
||||||
|
double x = vals[n] / first_positive;
|
||||||
|
|
||||||
|
x+=x*cos(e);
|
||||||
|
y-=x*sin(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_[k] = {sqrt(x*x+y*y), atan2(y,x)};
|
||||||
|
} else data_[k] = {0,0};
|
||||||
|
}
|
||||||
|
|
||||||
|
changed().emit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual double data(int row, int column) const override {
|
||||||
|
//double x = row/(2*M_PI);
|
||||||
|
switch ( column ) {
|
||||||
|
case 0: return row;
|
||||||
|
case 1: return data_[row].first;
|
||||||
|
case 2: return data_[row].second;
|
||||||
|
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual int columnCount() const override { return 3; };
|
||||||
|
virtual int rowCount() const override { return discreteModel->rowCount()/2; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
shared_ptr<SinModel> discreteModel;
|
||||||
|
vector<pair<double, double>> data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#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>
|
||||||
|
|
||||||
|
|
||||||
|
MyApplication::MyApplication(const WEnvironment& env)
|
||||||
|
: WApplication{env}
|
||||||
|
{
|
||||||
|
setTheme(make_shared<WBootstrap5Theme>());
|
||||||
|
styleSheet().addRule("html, body", "height: 100%");
|
||||||
|
root()->addStyleClass("d-flex h-100");
|
||||||
|
|
||||||
|
auto left = root()->addNew<WContainerWidget>();
|
||||||
|
left->addStyleClass("d-flex flex-column h-100");
|
||||||
|
|
||||||
|
{ // график
|
||||||
|
auto chartContainer = left->addNew<WContainerWidget>();
|
||||||
|
chart = chartContainer->addNew<Chart::WCartesianChart>();
|
||||||
|
//chart->setBackground(WColor(220, 220, 220));
|
||||||
|
chart->setType(Chart::ChartType::Scatter);
|
||||||
|
|
||||||
|
|
||||||
|
roughModel = make_shared<SinModel>(sinuses, 100);
|
||||||
|
auto roughSeries = make_unique<Chart::WDataSeries>(1, Chart::SeriesType::Line);
|
||||||
|
auto roughSeries_ = roughSeries.get();
|
||||||
|
roughSeries_->setModel(roughModel);
|
||||||
|
roughSeries_->setXSeriesColumn(0);
|
||||||
|
roughSeries->setHidden(true);
|
||||||
|
chart->addSeries(std::move(roughSeries));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
detailedModel = make_shared<SinModel>(sinuses, 10000);
|
||||||
|
auto seriesPtr = make_unique<Chart::WDataSeries>(1, Chart::SeriesType::Line);
|
||||||
|
auto series = seriesPtr.get();
|
||||||
|
series->setModel(detailedModel);
|
||||||
|
series->setXSeriesColumn(0);
|
||||||
|
series->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
|
||||||
|
chart->addSeries(std::move(seriesPtr));
|
||||||
|
|
||||||
|
chart->setFollowCurve(series);
|
||||||
|
|
||||||
|
|
||||||
|
discreteModel = make_shared<SinModel>(sinuses, 0);
|
||||||
|
auto pointsDSPtr = make_unique<Chart::WDataSeries>(1, Chart::SeriesType::Point);
|
||||||
|
pointsDS = pointsDSPtr.get();
|
||||||
|
pointsDS->setModel(discreteModel);
|
||||||
|
pointsDS->setXSeriesColumn(0);
|
||||||
|
pointsDS->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
|
||||||
|
pointsDS->setHidden(true);
|
||||||
|
pointsDS->setLabelsEnabled(Chart::Axis::Y);
|
||||||
|
pointsDS->setLabelColor(StandardColor::Black);
|
||||||
|
chart->addSeries(std::move(pointsDSPtr));
|
||||||
|
|
||||||
|
|
||||||
|
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).setMinimum( -M_PI );
|
||||||
|
chart->axis(Chart::Axis::X).setMaximum( M_PI );
|
||||||
|
|
||||||
|
chart->axis(Chart::Axis::Y).setMinimumZoomRange(0.1);
|
||||||
|
|
||||||
|
chart->axis(Chart::Axis::X).setLocation(Chart::AxisValue::Zero);
|
||||||
|
chart->axis(Chart::Axis::Y).setLocation(Chart::AxisValue::Zero);
|
||||||
|
|
||||||
|
chart->axis(Chart::Axis::X).setGridLinesEnabled(true);
|
||||||
|
chart->axis(Chart::Axis::Y).setGridLinesEnabled(true);
|
||||||
|
|
||||||
|
|
||||||
|
chart->resize(800, 600);
|
||||||
|
|
||||||
|
chart->setPanEnabled(true);
|
||||||
|
chart->setZoomEnabled(true);
|
||||||
|
chart->setCrosshairEnabled(true);
|
||||||
|
chart->setOnDemandLoadingEnabled(true);
|
||||||
|
|
||||||
|
auto sliderWidget = chartContainer->addNew<Chart::WAxisSliderWidget>(roughSeries_);
|
||||||
|
sliderWidget->resize(800, 80);
|
||||||
|
sliderWidget->setSelectionAreaPadding(40, Side::Left | Side::Right);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto controlsContainer = left->addNew<WContainerWidget>();
|
||||||
|
controlsContainer->addStyleClass("h-100 d-flex flex-column");
|
||||||
|
controlsContainer->setOverflow(Overflow::Hidden);
|
||||||
|
|
||||||
|
{ // функции
|
||||||
|
auto form = controlsContainer->addNew<WContainerWidget>();
|
||||||
|
form->addStyleClass("d-flex flex-row align-items-center gap-1 p-3");
|
||||||
|
|
||||||
|
list = controlsContainer->addNew<WContainerWidget>();
|
||||||
|
list->addStyleClass("d-flex flex-column gap-1 p-2");
|
||||||
|
list->setOverflow(Overflow::Auto);
|
||||||
|
|
||||||
|
showAll = form->addNew<WCheckBox>();
|
||||||
|
showAll->setTristate();
|
||||||
|
showAll->clicked().connect([=,this](){
|
||||||
|
for ( auto s : sinuses ) {
|
||||||
|
((WCheckBox*)s.el->children().at(0))
|
||||||
|
->setChecked(showAll->isChecked());
|
||||||
|
s.ds->setHidden(!showAll->isChecked());
|
||||||
|
}
|
||||||
|
checkCount = showAll->isChecked() ? sinuses.size() : 0;
|
||||||
|
chart->update();
|
||||||
|
});
|
||||||
|
|
||||||
|
form->addNew<WText>("a=");
|
||||||
|
auto a = form->addNew<WDoubleSpinBox>();
|
||||||
|
form->addNew<WText>("w=");
|
||||||
|
auto w = form->addNew<WDoubleSpinBox>();
|
||||||
|
form->addNew<WText>("f=");
|
||||||
|
auto f = form->addNew<WDoubleSpinBox>();
|
||||||
|
|
||||||
|
for ( auto i : {a,w,f} ) {
|
||||||
|
i->setWidth(100);
|
||||||
|
i->setRange(-1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto addBtn = form->addNew<WPushButton>("+");
|
||||||
|
|
||||||
|
addBtn->clicked().connect([=,this](){
|
||||||
|
addSin({a->value(), w->value(), f->value()});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto right = root()->addNew<WContainerWidget>();
|
||||||
|
right->addStyleClass("d-flex flex-column h-100 gap-1");
|
||||||
|
right->setOverflow(Overflow::Hidden, Orientation::Horizontal);
|
||||||
|
{ // дискретизация
|
||||||
|
auto form = right->addNew<WContainerWidget>();
|
||||||
|
form->addStyleClass("d-flex flex-row gap-3");
|
||||||
|
|
||||||
|
auto show = form->addNew<WCheckBox>("дискретизация");
|
||||||
|
auto freq = form->addNew<WSpinBox>();
|
||||||
|
freq->setWidth(120);
|
||||||
|
freq->setRange(0, 1000);
|
||||||
|
|
||||||
|
show->clicked().connect([=,this](){
|
||||||
|
pointsDS->setHidden(!show->isChecked());
|
||||||
|
chart->update();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
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<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->setPlotAreaPadding(100);
|
||||||
|
fourierChart->setType(Chart::ChartType::Scatter);
|
||||||
|
fourierModel = make_shared<FourierModel>(discreteModel);
|
||||||
|
fourierChart->setModel(static_pointer_cast<Chart::WAbstractChartModel>(fourierModel));
|
||||||
|
|
||||||
|
auto seriesPtr = make_unique<Chart::WDataSeries>(2, Chart::SeriesType::Point);
|
||||||
|
auto phaseSeries = seriesPtr.get();
|
||||||
|
phaseSeries->setModel(fourierChart->model());
|
||||||
|
phaseSeries->setXSeriesColumn(0);
|
||||||
|
phaseSeries->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
|
||||||
|
fourierChart->addSeries(std::move(seriesPtr));
|
||||||
|
|
||||||
|
|
||||||
|
seriesPtr = make_unique<Chart::WDataSeries>(1, Chart::SeriesType::Line);
|
||||||
|
auto ampSeries = seriesPtr.get();
|
||||||
|
ampSeries->setModel(fourierChart->model());
|
||||||
|
ampSeries->setXSeriesColumn(0);
|
||||||
|
ampSeries->setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 3));
|
||||||
|
fourierChart->addSeries(std::move(seriesPtr));
|
||||||
|
auto id = fourierChart->addYAxis(make_unique<Chart::WAxis>());
|
||||||
|
ampSeries->bindToYAxis(id);
|
||||||
|
fourierChart->setFollowCurve(ampSeries);
|
||||||
|
|
||||||
|
fourierChart->yAxis(0).setTitle("φ");
|
||||||
|
fourierChart->yAxis(0).setTextPen(phaseSeries->pen());
|
||||||
|
fourierChart->yAxis(id).setTitle("α");
|
||||||
|
fourierChart->yAxis(id).setTextPen(ampSeries->pen());
|
||||||
|
|
||||||
|
fourierChart->axis(Chart::Axis::X).setLabelAngle(-90);
|
||||||
|
|
||||||
|
fourierChart->setPanEnabled(true);
|
||||||
|
fourierChart->setZoomEnabled(true);
|
||||||
|
fourierChart->setCrosshairEnabled(true);
|
||||||
|
|
||||||
|
fourierChart->resize(800, 600);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyApplication::addSin(Sin s) {
|
||||||
|
if ( find(sinuses.cbegin(), sinuses.cend(), s) != sinuses.cend() ) return;
|
||||||
|
|
||||||
|
sinuses.push_back(s);
|
||||||
|
int index = sinuses.size()-1;
|
||||||
|
roughModel->changed().emit();
|
||||||
|
detailedModel->changed().emit();
|
||||||
|
discreteModel->changed().emit();
|
||||||
|
|
||||||
|
auto seriesPtr = make_unique<Chart::WDataSeries>(index+2, Chart::SeriesType::Line);
|
||||||
|
auto series = seriesPtr.get();
|
||||||
|
series->setModel(detailedModel);
|
||||||
|
series->setXSeriesColumn(0);
|
||||||
|
series->setShadow({3, 3, WColor{0, 0, 0, 127}, 3});
|
||||||
|
series->setPen({StandardColor::Green});
|
||||||
|
chart->addSeries(std::move(seriesPtr));
|
||||||
|
sinuses[index].ds = series;
|
||||||
|
series->setHidden(true);
|
||||||
|
|
||||||
|
auto el = list->addNew<WContainerWidget>();
|
||||||
|
{
|
||||||
|
el->addStyleClass("border rounded-3 p-3 d-flex justify-content-between align-items-center gap-3");
|
||||||
|
|
||||||
|
auto show = el->addNew<WCheckBox>();
|
||||||
|
show->clicked().connect([=,this](){
|
||||||
|
sinuses.at(series->modelColumn()-2).ds->setHidden(!show->isChecked());
|
||||||
|
chart->update();
|
||||||
|
checkCount+= show->isChecked() ? 1 : -1;
|
||||||
|
showAll->setCheckState(
|
||||||
|
checkCount == 0 ? CheckState::Unchecked
|
||||||
|
: checkCount < sinuses.size() ? CheckState::PartiallyChecked
|
||||||
|
: CheckState::Checked
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto c = el->addNew<WColorPicker>(WColor{StandardColor::Black});
|
||||||
|
c->setColor(StandardColor::Green);
|
||||||
|
c->changed().connect([=,this](){
|
||||||
|
sinuses.at(series->modelColumn()-2).ds->setPen(WPen{c->color()});
|
||||||
|
chart->update();
|
||||||
|
});
|
||||||
|
c->setWidth(50);
|
||||||
|
|
||||||
|
el->addNew<WText>(WString{"{1} cos( {2}t + {3} )"}.arg(s.a).arg(s.w).arg(s.f));
|
||||||
|
auto del = el->addNew<WPushButton>("x");
|
||||||
|
del->setStyleClass("btn-danger");
|
||||||
|
|
||||||
|
del->clicked().connect([=,this](){
|
||||||
|
removeSin(series->modelColumn()-2);
|
||||||
|
el->removeFromParent();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sinuses[index].el = el;
|
||||||
|
|
||||||
|
int freq = 2*M_PI / ranges::max_element(sinuses, [](Sin a, Sin b){return abs(a.w) > abs(b.w);})->w * 4;
|
||||||
|
if ( detailedModel->rowCount() < freq ) detailedModel->setRows(freq);
|
||||||
|
else detailedModel->setRows(10000);
|
||||||
|
|
||||||
|
showAll->setCheckState(
|
||||||
|
checkCount == 0 ? CheckState::Unchecked
|
||||||
|
: checkCount < sinuses.size() ? CheckState::PartiallyChecked
|
||||||
|
: CheckState::Checked
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MyApplication::removeSin(int index) {
|
||||||
|
auto& s = sinuses.at(index);
|
||||||
|
|
||||||
|
chart->removeSeries(s.ds);
|
||||||
|
sinuses.erase(next(sinuses.begin(), index));
|
||||||
|
|
||||||
|
for ( ; index < sinuses.size(); index++ )
|
||||||
|
sinuses[index].ds->setModelColumn(index+2);
|
||||||
|
|
||||||
|
roughModel->changed().emit();
|
||||||
|
detailedModel->changed().emit();
|
||||||
|
discreteModel->changed().emit();
|
||||||
|
|
||||||
|
showAll->setCheckState(
|
||||||
|
checkCount == 0 ? CheckState::Unchecked
|
||||||
|
: checkCount < sinuses.size() ? CheckState::PartiallyChecked
|
||||||
|
: CheckState::Checked
|
||||||
|
);
|
||||||
|
}
|
60
src/MyApplication.h
Normal file
60
src/MyApplication.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Wt/WApplication.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
using namespace Wt;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
struct sin {
|
||||||
|
double a;
|
||||||
|
double w;
|
||||||
|
double f;
|
||||||
|
|
||||||
|
Chart::WDataSeries* ds;
|
||||||
|
WContainerWidget* el;
|
||||||
|
|
||||||
|
double get(double t) const
|
||||||
|
{ return a*cos(w*t+f); }
|
||||||
|
|
||||||
|
bool operator ==(const sin& other) const {
|
||||||
|
return a == other.a && w == other.w && f == other.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator <(const sin& other) const {
|
||||||
|
return (a+w+f) < (other.a + other.w + other.f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
typedef struct sin Sin;
|
||||||
|
|
||||||
|
class SinModel;
|
||||||
|
class FourierModel;
|
||||||
|
|
||||||
|
class MyApplication : public WApplication
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyApplication(const WEnvironment& env);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Chart::WCartesianChart* chart;
|
||||||
|
shared_ptr<SinModel> roughModel;
|
||||||
|
shared_ptr<SinModel> detailedModel;
|
||||||
|
shared_ptr<SinModel> discreteModel;
|
||||||
|
Chart::WDataSeries* pointsDS;
|
||||||
|
|
||||||
|
Chart::WCartesianChart* fourierChart;
|
||||||
|
shared_ptr<FourierModel> fourierModel;
|
||||||
|
|
||||||
|
|
||||||
|
WContainerWidget* list;
|
||||||
|
WCheckBox* showAll;
|
||||||
|
size_t checkCount = 0;
|
||||||
|
|
||||||
|
WContainerWidget* vals;
|
||||||
|
|
||||||
|
vector<Sin> sinuses;
|
||||||
|
|
||||||
|
void addSin(Sin s);
|
||||||
|
void removeSin(int index);
|
||||||
|
};
|
28
src/main.cpp
Normal file
28
src/main.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include <Wt/WServer.h>
|
||||||
|
|
||||||
|
#include "MyApplication.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace Wt;
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int code = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
WServer server(argc, argv, WTHTTP_CONFIGURATION);
|
||||||
|
|
||||||
|
using namespace placeholders;
|
||||||
|
|
||||||
|
server.addEntryPoint( EntryPointType::Application,
|
||||||
|
[](const WEnvironment& env){return make_unique<MyApplication>(env);},
|
||||||
|
"/"
|
||||||
|
);
|
||||||
|
|
||||||
|
server.run();
|
||||||
|
}
|
||||||
|
catch ( WServer::Exception& e ) { cerr << e.what() << endl; code = -1; }
|
||||||
|
catch ( std::exception& e ) { cerr << "exception: " << e.what() << endl; code = -1; }
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
55
wt_config.xml
Normal file
55
wt_config.xml
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!--
|
||||||
|
Wt Configuration File.
|
||||||
|
|
||||||
|
The Wt configuration file manages, for every Wt application, settings
|
||||||
|
for session management, debugging, directory for runtime information
|
||||||
|
such as session sockets, and some security settings.
|
||||||
|
|
||||||
|
Settings may be specified globally, or for a single application path.
|
||||||
|
|
||||||
|
The path should be as configured in the Wt build process, where it
|
||||||
|
defaults to /etc/wt/wt_config.xml. It can be overridden in the environment
|
||||||
|
variable WT_CONFIG_XML, or with the -c startup option of wthttp.
|
||||||
|
|
||||||
|
The values listed here are the default values, which are used when the
|
||||||
|
declaration is missing or no configuration file is used.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<server>
|
||||||
|
<application-settings location="*">
|
||||||
|
<web-sockets>true</web-sockets>
|
||||||
|
<progressive-bootstrap>false</progressive-bootstrap>
|
||||||
|
<head-matter user-agent=".*">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
|
</head-matter>
|
||||||
|
|
||||||
|
<behind-reverse-proxy>true</behind-reverse-proxy>
|
||||||
|
|
||||||
|
<trusted-proxy-config>
|
||||||
|
<original-ip-header>X-Forwarded-For</original-ip-header>
|
||||||
|
<trusted-proxies>
|
||||||
|
<!-- loopback -->
|
||||||
|
|
||||||
|
<proxy>127.0.0.1/8</proxy>
|
||||||
|
<proxy>::1/128</proxy>
|
||||||
|
|
||||||
|
<!-- link local -->
|
||||||
|
<!--
|
||||||
|
<proxy>169.254.0.0/16</proxy>
|
||||||
|
<proxy>fe80::/10</proxy>
|
||||||
|
-->
|
||||||
|
<!-- local -->
|
||||||
|
<!--
|
||||||
|
<proxy>10.0.0.0/8</proxy>
|
||||||
|
<proxy>172.16.0.0/12</proxy>
|
||||||
|
<proxy>192.168.0.0/16</proxy>
|
||||||
|
<proxy>fc00::/7</proxy>
|
||||||
|
-->
|
||||||
|
</trusted-proxies>
|
||||||
|
</trusted-proxy-config>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</application-settings>
|
||||||
|
</server>
|
Loading…
Reference in New Issue
Block a user