diff --git a/CMakeLists.txt b/CMakeLists.txt index 405d1f7..9e1c5fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,93 +1,93 @@ cmake_minimum_required(VERSION 3.5) # KDE Application Version, managed by release script set(KDE_APPLICATIONS_VERSION_MAJOR "20") set(KDE_APPLICATIONS_VERSION_MINOR "03") set(KDE_APPLICATIONS_VERSION_MICRO "70") set(KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") project(step VERSION ${KDE_APPLICATIONS_VERSION}) find_package(ECM 1.7.0 REQUIRED NO_MODULE) find_package(KF5DocTools) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ${CMAKE_MODULE_PATH}) include(KDEInstallDirs) include(ECMAddTests) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(ECMInstallIcons) include(ECMOptionalAddSubdirectory) include(ECMSetupVersion) include(FeatureSummary) include(ECMPoQmTools) find_package(Qt5 5.2 REQUIRED NO_MODULE COMPONENTS Xml Svg OpenGL Test ) find_package(KF5 REQUIRED COMPONENTS Crash Config I18n IconThemes KIO NewStuff Plotting TextWidgets ) find_package(Eigen3 3.2.2 REQUIRED) find_package(GSL) find_package(Qalculate) set_package_properties(EIGEN3 PROPERTIES DESCRIPTION "Eigen3" - URL "http://eigen.tuxfamily.org" + URL "https://eigen.tuxfamily.org" TYPE OPTIONAL PURPOSE "Required to build step") set_package_properties(QALCULATE PROPERTIES # REQUIRED_VERSION "0.9.5" DESCRIPTION "Qalculate" - URL "http://qalculate.sourceforge.net/" + URL "https://qalculate.github.io/" TYPE OPTIONAL PURPOSE "Unit conversion support in Step") set_package_properties(GSL PROPERTIES # REQUIRED_VERSION "1.8" DESCRIPTION "GSL" - URL "http://eigen.tuxfamily.org" + URL "https://www.gnu.org/software/gsl/" TYPE OPTIONAL PURPOSE "Provides a GSL-powered solver for Step") set(STEPCORE_WITH_GSL ${GSL_FOUND} CACHE BOOL "Enable GSL-powered features for StepCore") set(STEPCORE_WITH_QT ON) # CACHE BOOL "Enable QT-powered features for StepCore") set(STEP_WITH_QALCULATE ${QALCULATE_FOUND} CACHE BOOL "Enable unit conversion support using libqalculate") # These settings are forced by Eigen2 creating many many warnings without them. # FIXME: How can we make Eigen behave and not need them? set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-local-typedefs") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter") include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${EIGEN3_INCLUDE_DIR}) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x060000) if(KF5DocTools_FOUND) ecm_optional_add_subdirectory(doc) endif() add_subdirectory(stepcore) add_subdirectory(step) add_subdirectory(autotests) install(FILES org.kde.step.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) find_package(SharedMimeInfo REQUIRED) install(FILES org.kde.step.xml DESTINATION ${XDG_MIME_INSTALL_DIR}) update_xdg_mimetypes(${XDG_MIME_INSTALL_DIR}) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/step/infobrowser.cc b/step/infobrowser.cc index 036da3c..e02becc 100644 --- a/step/infobrowser.cc +++ b/step/infobrowser.cc @@ -1,256 +1,256 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Step 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. Step 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 Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "infobrowser.h" #include "worldmodel.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include InfoBrowser::InfoBrowser(WorldModel* worldModel, QWidget* parent) : QDockWidget(i18n("Context info"), parent), _worldModel(worldModel), _selectionChanged(false) { QWidget* widget = new QWidget(this); setWidget(widget); QVBoxLayout* layout = new QVBoxLayout(widget); layout->setContentsMargins(0,0,0,0); layout->setSpacing(0); _toolBar = new KToolBar(widget); layout->addWidget(_toolBar); _toolBar->setMovable(false); _toolBar->setFloatable(false); _toolBar->setIconDimensions(16); _toolBar->setContextMenuPolicy(Qt::NoContextMenu); _toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); _backAction = _toolBar->addAction(QIcon::fromTheme(QStringLiteral("go-previous")), i18n("Back"), this, SLOT(back())); _backAction->setEnabled(false); _forwardAction = _toolBar->addAction(QIcon::fromTheme(QStringLiteral("go-next")), i18n("Forward"), this, SLOT(forward())); _forwardAction->setEnabled(false); _toolBar->addSeparator(); _syncAction = _toolBar->addAction(QIcon::fromTheme(QStringLiteral("goto-page")), i18n("Sync selection"), this, SLOT(syncSelection())); // XXX: icon _syncAction->setEnabled(false); _followAction = _toolBar->addAction(QIcon::fromTheme(QStringLiteral("note2")), i18n("Follow selection")/*, this, SLOT(syncSelection(bool))*/); // XXX: icon _followAction->setCheckable(true); _followAction->setChecked(true); _toolBar->addSeparator(); _execAction = _toolBar->addAction(QIcon::fromTheme(QStringLiteral("system-run")), i18n("Open in browser"), this, SLOT(openInBrowser())); _execAction->setEnabled(false); _htmlBrowser = new QTextBrowser(widget); _htmlBrowser->setOpenLinks(false); layout->addWidget(_htmlBrowser); connect(_htmlBrowser, &QTextBrowser::anchorClicked, this, [=](const QUrl &url){ openUrl(url); }); connect(_worldModel->selectionModel(), &QItemSelectionModel::currentChanged, this, &InfoBrowser::worldCurrentChanged); syncSelection(); } void InfoBrowser::showEvent(QShowEvent* event) { QDockWidget::showEvent(event); if(_selectionChanged) { _selectionChanged = false; QModelIndex current = _worldModel->selectionModel()->currentIndex(); worldCurrentChanged(current, QModelIndex()); } } void InfoBrowser::worldCurrentChanged(const QModelIndex& /*current*/, const QModelIndex& /*previous*/) { if(isVisible()) { if(_followAction->isChecked()) syncSelection(); else updateSyncSelection(); } else { _selectionChanged = true; } } void InfoBrowser::syncSelection(bool checked) { if(checked) { const QModelIndex current = _worldModel->selectionModel()->currentIndex(); const QUrl url(QStringLiteral("objinfo:").append(current.data(WorldModel::ClassNameRole).toString())); openUrl(url, true); } } void InfoBrowser::updateSyncSelection() { if(_htmlBrowser->source().scheme() == QLatin1String("objinfo")) { QModelIndex current = _worldModel->selectionModel()->currentIndex(); if(_htmlBrowser->source().path() == current.data(WorldModel::ClassNameRole).toString()) { _syncAction->setEnabled(false); return; } } _syncAction->setEnabled(true); } void InfoBrowser::openUrl(const QUrl& url, bool clearHistory, bool fromHistory) { if(url.scheme() == QLatin1String("objinfo")) { if(clearHistory) { _forwardHistory.clear(); _forwardAction->setEnabled(false); _backHistory.clear(); _backAction->setEnabled(false); fromHistory = true; } QString className = url.path(); if(className.isEmpty()) { setHtml("" "\n" "
\n" "
\n" "\n" + i18n( "Documentation" ) + "\n" "
\n" "
\n" "

\n" + i18n("No current object.") + "

\n" "
\n" "
\n" "", fromHistory, url ); return; } QString fileName = QStringLiteral(":/objectinfo/%1.html").arg(className.toLower()); if(!fileName.isEmpty()) { QFile file(fileName); if(file.open(QIODevice::ReadOnly | QIODevice::Text)) { setHtml(QString::fromUtf8(file.readAll()), fromHistory, url); return; } else { qWarning() << "Could not open help file at location:" << fileName; } } setHtml("" "\n" "
\n" "
\n" "\n" + i18n( "Documentation error" ) + "\n" "
\n" "
\n" "

\n" + i18n("Documentation for %1 not available. ", QCoreApplication::translate("ObjectClass", className.toUtf8().constData())) - + i18n("You can help Step by writing it!") + + + i18n("You can help Step by writing it!") + "

\n" "
\n" "
\n" "", fromHistory, url ); show(); } else if(url.scheme() == QLatin1String("https") || url.scheme() == QLatin1String("http")) { // do not clear history when open external URL QDesktopServices::openUrl(url); } else { qWarning() << "Unknown URL scheme detected, skipping:" << url; } } void InfoBrowser::setHtml(const QString& data, bool fromHistory, const QUrl& url) { if(!fromHistory) { _forwardAction->setEnabled(false); _forwardHistory.clear(); QString oldUrl = _htmlBrowser->source().url(); if(!oldUrl.isEmpty()) { _backHistory << oldUrl; _backAction->setEnabled(true); } } if(url.scheme() == QLatin1String("http")) { _execAction->setEnabled(true); } else { _execAction->setEnabled(false); } _htmlBrowser->setSource(url); _htmlBrowser->setHtml(data); updateSyncSelection(); } void InfoBrowser::back() { Q_ASSERT(!_backHistory.isEmpty()); QString url(_backHistory.takeLast()); if(_backHistory.isEmpty()) _backAction->setEnabled(false); QString curUrl = _htmlBrowser->source().url(); if(!curUrl.isEmpty()) { _forwardHistory << curUrl; _forwardAction->setEnabled(true); } openUrl(QUrl(url), false, true); } void InfoBrowser::forward() { Q_ASSERT(!_forwardHistory.isEmpty()); QString url(_forwardHistory.takeLast()); if(_forwardHistory.isEmpty()) _forwardAction->setEnabled(false); QString curUrl = _htmlBrowser->source().url(); if(!curUrl.isEmpty()) { _backHistory << curUrl; _backAction->setEnabled(true); } openUrl(QUrl(url), false, true); } void InfoBrowser::openInBrowser() { if(_htmlBrowser->source().scheme() == QLatin1String("https")) { QDesktopServices::openUrl(_htmlBrowser->source()); } } diff --git a/step/main.cc b/step/main.cc index 4d7b4cb..0ed5dd6 100644 --- a/step/main.cc +++ b/step/main.cc @@ -1,100 +1,100 @@ /* This file is part of Step. Copyright (C) 2007 Vladimir Kuznetsov Step 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. Step 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 Step; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "step_version.h" static const char description[] = I18N_NOOP("Interactive physical simulator"); int main(int argc, char* argv[]) { KLocalizedString::setApplicationDomain("step"); QApplication app(argc, argv); QApplication::setApplicationName(QStringLiteral("step")); QApplication::setApplicationVersion(STEP_VERSION_STRING); QApplication::setOrganizationDomain(QStringLiteral("kde.org")); QApplication::setApplicationDisplayName(i18n("Step")); QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral("step"))); app.setAttribute(Qt::AA_UseHighDpiPixmaps, true); KCrash::initialize(); KAboutData aboutData(QStringLiteral("step"), i18n("Step"), STEP_VERSION_STRING, i18n(description), KAboutLicense::GPL, i18n("(C) 2007 Vladimir Kuznetsov"), - i18n("http://edu.kde.org/step") + i18n("https://edu.kde.org/step") ); aboutData.addAuthor( QStringLiteral("Vladimir Kuznetsov"), i18n("Original author"), i18n("ks.vladimir@gmail.com") ); aboutData.addAuthor( QStringLiteral("Carsten Niehaus"), i18n("Code contributions"), i18n("cniehaus@kde.org") ); KAboutData::setApplicationData(aboutData); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption option(QStringList() << QStringLiteral("+[url]") << i18n( "Document to open" )); parser.addOption(option); parser.process(app); MainWindow* mainWindow = new MainWindow(); mainWindow->show(); const QStringList args = parser.positionalArguments(); if(args.count() > 0) { //qDebug() << args[0] << endl; // open the step files passed as arguments as relative or absolute paths mainWindow->openFile( (QUrl(args[0], QUrl::TolerantMode).isRelative() && !QDir::isAbsolutePath(args[0])) ? QUrl::fromLocalFile(QDir::current().absoluteFilePath(args[0])) : QUrl::fromUserInput(args[0]) ); } return app.exec(); } diff --git a/stepcore/constants.h b/stepcore/constants.h index 23e4fc9..313d71e 100644 --- a/stepcore/constants.h +++ b/stepcore/constants.h @@ -1,74 +1,74 @@ /* This file is part of StepCore library. Copyright (C) 2007 Vladimir Kuznetsov StepCore library 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. StepCore library 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 StepCore; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef STEPCORE_CONSTANTS_H #define STEPCORE_CONSTANTS_H /** \file constants.h * \brief Constants class */ namespace StepCore { /** \ingroup constants * \brief Common physical constants * - * Values taken from http://en.wikipedia.org/wiki/Physical_constants + * Values taken from https://en.wikipedia.org/wiki/Physical_constants */ class Constants { public: /** Pi constant */ static const double Pi; /** Speed of light in vacuum [m/s] */ static const double SpeedOfLight; /** Electric constant (permittivity of free space) [F/m] */ static const double Electric; /** Magnetic constant (permeability of free space) [N/A^2] */ static const double Magnetic; /** Coulomb's constant [N*m^2/C^2] */ static const double Coulomb; /** Error of Coulomb's constant */ static const double CoulombError; /** Newtonian constant of gravitation [N*m^2/kg^2] */ static const double Gravitational; /** Error of newtonian constant of gravitation */ static const double GravitationalError; /** Planck's constant [J*s] */ static const double Planck; /** Error of Planck's constant */ static const double PlanckError; /** Boltzmann's constant [J/K] */ static const double Boltzmann; /** Error of Boltzmann's constant */ static const double BoltzmannError; /** Standard acceleration of gravity (free fall on Earth) [m/s^2] */ static const double WeightAccel; /** Error of standard acceleration of gravity (free fall on Earth) */ static const double WeightAccelError; }; } // namespace StepCore #endif diff --git a/stepcore/eulersolver.h b/stepcore/eulersolver.h index 3751f18..29aecde 100644 --- a/stepcore/eulersolver.h +++ b/stepcore/eulersolver.h @@ -1,107 +1,107 @@ /* This file is part of StepCore library. Copyright (C) 2007 Vladimir Kuznetsov StepCore library 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. StepCore library 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 StepCore; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** \file eulersolver.h * \brief GenericEulerSolver, EulerSolver and AdaptiveEulerSolver classes */ #ifndef STEPCORE_EULERSOLVER_H #define STEPCORE_EULERSOLVER_H #include "solver.h" #include "object.h" namespace StepCore { /** \ingroup solvers * \brief Adaptive and non-adaptive Euler solver with error estimation * - * See http://en.wikipedia.org/wiki/Numerical_ordinary_differential_equations#The_Euler_method - * and http://en.wikipedia.org/wiki/Adaptive_step_size + * See https://en.wikipedia.org/wiki/Numerical_ordinary_differential_equations#The_Euler_method + * and https://en.wikipedia.org/wiki/Adaptive_step_size * * \todo tests */ class GenericEulerSolver: public Solver { STEPCORE_OBJECT(GenericEulerSolver) public: /** Constructs GenericEulerSolver */ GenericEulerSolver(double stepSize, bool adaptive) : Solver(stepSize), _adaptive(adaptive) { init(); } /** Constructs GenericEulerSolver */ GenericEulerSolver(int dimension, Function function, void* params, double stepSize, bool adaptive) : Solver(dimension, function, params, stepSize), _adaptive(adaptive) { init(); } /** Copy constructor */ GenericEulerSolver(const GenericEulerSolver& eulerSolver) : Solver(eulerSolver), _adaptive(eulerSolver._adaptive) { init(); } ~GenericEulerSolver() { fini(); } void setDimension(int dimension) Q_DECL_OVERRIDE { fini(); _dimension = dimension; init(); } int doCalcFn(double* t, const VectorXd* y, const VectorXd* yvar = 0, VectorXd* f = 0, VectorXd* fvar = 0) Q_DECL_OVERRIDE; int doEvolve(double* t, double t1, VectorXd* y, VectorXd* yvar) Q_DECL_OVERRIDE; protected: int doStep(double t, double stepSize, VectorXd* y, VectorXd* yvar); void init(); void fini(); bool _adaptive; VectorXd _yerr; VectorXd _ytemp; VectorXd _ydiff; VectorXd _ytempvar; VectorXd _ydiffvar; }; /** \ingroup solvers * \brief Non-adaptive Euler solver */ class EulerSolver: public GenericEulerSolver { STEPCORE_OBJECT(EulerSolver) public: EulerSolver(double stepSize = 0.01): GenericEulerSolver(stepSize, false) {} EulerSolver(int dimension, Function function, void* params, double stepSize) : GenericEulerSolver(dimension, function, params, stepSize, false) {} EulerSolver(const EulerSolver& eulerSolver): GenericEulerSolver(eulerSolver) {} }; /** \ingroup solvers * \brief Adaptive Euler solver */ class AdaptiveEulerSolver: public GenericEulerSolver { STEPCORE_OBJECT(AdaptiveEulerSolver) public: AdaptiveEulerSolver(): GenericEulerSolver(1, true) {} AdaptiveEulerSolver(int dimension, Function function, void* params) : GenericEulerSolver(dimension, function, params, 1, true) {} AdaptiveEulerSolver(const AdaptiveEulerSolver& eulerSolver) : GenericEulerSolver(eulerSolver) {} }; } // namespace StepCore #endif diff --git a/stepcore/gslsolver.h b/stepcore/gslsolver.h index bd82678..0b2d048 100644 --- a/stepcore/gslsolver.h +++ b/stepcore/gslsolver.h @@ -1,233 +1,233 @@ /* This file is part of StepCore library. Copyright (C) 2007 Vladimir Kuznetsov StepCore library 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. StepCore library 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 StepCore; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /** \file gslsolver.h * \brief GslGenericSolver, GslSolver and GslAdaptiveSolver classes */ #ifndef STEPCORE_GSLSOLVER_H #define STEPCORE_GSLSOLVER_H // HACK: CMake does not passes definitions to moc #if defined(STEPCORE_WITH_GSL) || defined(Q_MOC_RUN) #include "solver.h" #include "object.h" #include namespace StepCore { /** \ingroup solvers * \brief Adaptive and non-adaptive solvers from GSL library * - * See http://www.gnu.org/software/gsl/manual/html_node/Ordinary-Differential-Equations.html#Ordinary-Differential-Equations - * and http://en.wikipedia.org/wiki/Numerical_ordinary_differential_equations + * See https://www.gnu.org/software/gsl/manual/html_node/Ordinary-Differential-Equations.html#Ordinary-Differential-Equations + * and https://en.wikipedia.org/wiki/Numerical_ordinary_differential_equations * */ class GslGenericSolver: public Solver { STEPCORE_OBJECT(GslGenericSolver) public: /** Constructs GslSolver */ GslGenericSolver(double stepSize, bool adaptive, const gsl_odeiv_step_type* gslStepType) : Solver(stepSize), _adaptive(adaptive), _gslStepType(gslStepType) { init(); } /** Constructs GslSolver */ GslGenericSolver(int dimension, Function function, void* params, double stepSize, bool adaptive, const gsl_odeiv_step_type* gslStepType) : Solver(dimension, function, params, stepSize), _adaptive(adaptive), _gslStepType(gslStepType) { init(); } /** Copy constructor */ GslGenericSolver(const GslGenericSolver& gslSolver) : Solver(gslSolver), _adaptive(gslSolver._adaptive), _gslStepType(gslSolver._gslStepType) { init(); } ~GslGenericSolver() { fini(); } void setDimension(int dimension) { fini(); _dimension = dimension; init(); } void setToleranceAbs(double toleranceAbs) { fini(); _toleranceAbs = toleranceAbs; init(); } void setToleranceRel(double toleranceRel) { fini(); _toleranceRel = toleranceRel; init(); } int doCalcFn(double* t, const VectorXd* y, const VectorXd* yvar, VectorXd* f = 0, VectorXd* fvar = 0); int doEvolve(double* t, double t1, VectorXd* y, VectorXd* yvar); protected: static int gslFunction(double t, const double* y, double* f, void* params); void init(); void fini(); bool _adaptive; //gsl_odeiv_control* _gslControl; //gsl_odeiv_evolve* _gslEvolve; VectorXd _yerr; VectorXd _ytemp; VectorXd _ydiff; VectorXd _dydt_in; VectorXd _dydt_out; const gsl_odeiv_step_type* _gslStepType; gsl_odeiv_system _gslSystem; gsl_odeiv_step* _gslStep; gsl_odeiv_control* _gslControl; gsl_odeiv_evolve* _gslEvolve; }; /** \ingroup solvers * \brief Non-adaptive solvers from GSL library */ class GslSolver: public GslGenericSolver { STEPCORE_OBJECT(GslSolver) public: GslSolver(double stepSize, const gsl_odeiv_step_type* gslStepType): GslGenericSolver(stepSize, false, gslStepType) {} GslSolver(int dimension, Function function, void* params, double stepSize, const gsl_odeiv_step_type* gslStepType) : GslGenericSolver(dimension, function, params, stepSize, false, gslStepType) {} GslSolver(const GslSolver& gslSolver): GslGenericSolver(gslSolver) {} }; /** \ingroup solvers * \brief Adaptive solvers from GSL library */ class GslAdaptiveSolver: public GslGenericSolver { STEPCORE_OBJECT(GslAdaptiveSolver) public: explicit GslAdaptiveSolver(const gsl_odeiv_step_type* gslStepType): GslGenericSolver(1, true, gslStepType) {} GslAdaptiveSolver(int dimension, Function function, void* params, const gsl_odeiv_step_type* gslStepType) : GslGenericSolver(dimension, function, params, 1, true, gslStepType) {} GslAdaptiveSolver(const GslAdaptiveSolver& gslSolver): GslGenericSolver(gslSolver) {} }; #define STEPCORE_DECLARE_GSLSOLVER(Class, type) \ class Gsl##Class##Solver: public GslSolver { \ STEPCORE_OBJECT(Gsl##Class##Solver) \ public: \ Gsl##Class##Solver(double stepSize = 0.01): GslSolver(stepSize, gsl_odeiv_step_##type) {} \ Gsl##Class##Solver(int dimension, Function function, void* params, double stepSize) \ : GslSolver(dimension, function, params, stepSize, gsl_odeiv_step_##type) {} \ Gsl##Class##Solver(const Gsl##Class##Solver& gslSolver): GslSolver(gslSolver) {} \ }; #define STEPCORE_DECLARE_GSLASOLVER(Class, type) \ class GslAdaptive##Class##Solver: public GslAdaptiveSolver { \ STEPCORE_OBJECT(GslAdaptive##Class##Solver) \ public: \ GslAdaptive##Class##Solver(): GslAdaptiveSolver(gsl_odeiv_step_##type) {} \ GslAdaptive##Class##Solver(int dimension, Function function, void* params) \ : GslAdaptiveSolver(dimension, function, params, gsl_odeiv_step_##type) {} \ GslAdaptive##Class##Solver(const GslAdaptive##Class##Solver& gslSolver): GslAdaptiveSolver(gslSolver) {} \ }; /** \ingroup solvers * \class GslRK2Solver * \brief Runge-Kutta second-order solver from GSL library */ STEPCORE_DECLARE_GSLSOLVER(RK2, rk2) /** \ingroup solvers * \class GslAdaptiveRK2Solver * \brief Adaptive Runge-Kutta second-order solver from GSL library */ STEPCORE_DECLARE_GSLASOLVER(RK2, rk2) /** \ingroup solvers * \class GslRK4Solver * \brief Runge-Kutta classical fourth-order solver from GSL library */ STEPCORE_DECLARE_GSLSOLVER(RK4, rk4) /** \ingroup solvers * \class GslAdaptiveRK4Solver * \brief Adaptive Runge-Kutta classical fourth-order solver from GSL library */ STEPCORE_DECLARE_GSLASOLVER(RK4, rk4) /** \ingroup solvers * \class GslRKF45Solver * \brief Runge-Kutta-Fehlberg (4,5) solver from GSL library */ STEPCORE_DECLARE_GSLSOLVER(RKF45, rkf45) /** \ingroup solvers * \class AdaptiveGslRKF45Solver * \brief Adaptive Runge-Kutta-Fehlberg (4,5) solver from GSL library */ STEPCORE_DECLARE_GSLASOLVER(RKF45, rkf45) /** \ingroup solvers * \class GslRKCKSolver * \brief Runge-Kutta Cash-Karp (4,5) solver from GSL library */ STEPCORE_DECLARE_GSLSOLVER(RKCK, rkck) /** \ingroup solvers * \class AdaptiveGslRKCKSolver * \brief Adaptive Runge-Kutta Cash-Karp (4,5) solver from GSL library */ STEPCORE_DECLARE_GSLASOLVER(RKCK, rkck) /** \ingroup solvers * \class GslRK8PDSolver * \brief Runge-Kutta Prince-Dormand (8,9) solver from GSL library */ STEPCORE_DECLARE_GSLSOLVER(RK8PD, rk8pd) /** \ingroup solvers * \class GslAdaptiveRK8PDSolver * \brief Adaptive Runge-Kutta Prince-Dormand (8,9) solver from GSL library */ STEPCORE_DECLARE_GSLASOLVER(RK8PD, rk8pd) /** \ingroup solvers * \class GslRK2IMPSolver * \brief Runge-Kutta implicit second-order solver from GSL library */ STEPCORE_DECLARE_GSLSOLVER(RK2IMP, rk2imp) /** \ingroup solvers * \class GslAdaptiveRK2IMPSolver * \brief Adaptive Runge-Kutta Prince-Dormand (8,9) solver from GSL library */ STEPCORE_DECLARE_GSLASOLVER(RK2IMP, rk2imp) /** \ingroup solvers * \class GslRK4IMPSolver * \brief Runge-Kutta implicit fourth-order solver from GSL library */ STEPCORE_DECLARE_GSLSOLVER(RK4IMP, rk4imp) /** \ingroup solvers * \class GslAdaptiveRK4IMPSolver * \brief Runge-Kutta implicit fourth-order solver from GSL library */ STEPCORE_DECLARE_GSLASOLVER(RK4IMP, rk4imp) } // namespace StepCore #endif // defined(STEPCORE_WITH_GSL) || defined(Q_MOC_RUN) #endif // STEPCORE_GSLSOLVER_H diff --git a/stepcore/xmlfile.cc b/stepcore/xmlfile.cc index 7d1c89b..5343b9b 100644 --- a/stepcore/xmlfile.cc +++ b/stepcore/xmlfile.cc @@ -1,405 +1,405 @@ /* This file is part of StepCore library. Copyright (C) 2007 Vladimir Kuznetsov StepCore library 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. StepCore library 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 StepCore; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "xmlfile.h" #ifdef STEPCORE_WITH_QT #include "world.h" #include "solver.h" #include "collisionsolver.h" #include "constraintsolver.h" #include "factory.h" #include #include namespace StepCore { const char* XmlFile::DOCTYPE = ""; -const char* XmlFile::NAMESPACE_URI = "http://edu.kde.org/step/StepCoreXML"; +const char* XmlFile::NAMESPACE_URI = "https://edu.kde.org/step/StepCoreXML"; const char* XmlFile::VERSION = "1.0"; namespace { class StepStreamWriter { public: StepStreamWriter(QIODevice* device); bool writeWorld(const World* world); protected: void saveProperties(const Object* obj, int first); void saveObject(const QString& tag, const Object* obj); QXmlStreamWriter _writer; QIODevice* _device; QHash _ids; static const int INDENT = 4; }; StepStreamWriter::StepStreamWriter(QIODevice* device) : _device(device) { _writer.setAutoFormatting(true); _writer.setAutoFormattingIndent(INDENT); } void StepStreamWriter::saveProperties(const Object* obj, int first) { const MetaObject* metaObject = obj->metaObject(); for(int i = first; i < metaObject->propertyCount(); ++i) { const MetaProperty* p = metaObject->property(i); if(p->isStored()) { if(p->userTypeId() == qMetaTypeId()) { int id = _ids.value(p->readVariant(obj).value(), -1); _writer.writeTextElement(p->name(), QString::number(id)); } else { _writer.writeTextElement(p->name(), p->readString(obj)); } } } } void StepStreamWriter::saveObject(const QString& tag, const Object* obj) { Q_ASSERT(obj != NULL); _writer.writeStartElement(tag); _writer.writeAttribute(QStringLiteral("class"), obj->metaObject()->className()); _writer.writeAttribute(QStringLiteral("id"), QString::number(_ids.value(obj, -1))); saveProperties(obj, 0); if(obj->metaObject()->inherits()) { const ObjectErrors* objErrors = static_cast(obj)->tryGetObjectErrors(); if(objErrors) saveProperties(objErrors, 1); } if(obj->metaObject()->inherits()) { const ItemGroup* group = static_cast(obj); ItemList::const_iterator end = group->items().end(); for(ItemList::const_iterator it = group->items().begin(); it != end; ++it) { saveObject(QStringLiteral("item"), *it); } } _writer.writeEndElement(); } bool StepStreamWriter::writeWorld(const World* world) { Q_ASSERT(_device->isOpen() && _device->isWritable()); _writer.setDevice(_device); int maxid = -1; _ids.insert(NULL, ++maxid); _ids.insert(world, ++maxid); ItemList items = world->allItems(); const ItemList::const_iterator end0 = items.end(); for(ItemList::const_iterator it = items.begin(); it != end0; ++it) _ids.insert(*it, ++maxid); if(world->solver()) _ids.insert(world->solver(), ++maxid); if(world->collisionSolver()) _ids.insert(world->collisionSolver(), ++maxid); if(world->constraintSolver()) _ids.insert(world->constraintSolver(), ++maxid); _writer.writeStartDocument(); _writer.writeDTD(XmlFile::DOCTYPE); _writer.writeStartElement(QStringLiteral("world")); _writer.writeAttribute(QStringLiteral("xmlns"), XmlFile::NAMESPACE_URI); _writer.writeAttribute(QStringLiteral("version"), XmlFile::VERSION); _writer.writeAttribute(QStringLiteral("id"), QStringLiteral("1")); saveProperties(world, 0); ItemList::const_iterator end = world->items().end(); for(ItemList::const_iterator it = world->items().begin(); it != end; ++it) { saveObject(QStringLiteral("item"), *it); } if(world->solver()) { saveObject(QStringLiteral("solver"), world->solver()); } if(world->collisionSolver()) { saveObject(QStringLiteral("collisionSolver"), world->collisionSolver()); } if(world->constraintSolver()) { saveObject(QStringLiteral("constraintSolver"), world->constraintSolver()); } _writer.writeEndElement(); _writer.writeEndDocument(); return true; } class StepDomDocument { public: StepDomDocument(World* world, const Factory* factory); bool parse(QIODevice* device); const QString& errorMsg() const { return _errorMsg; } private: typedef QPair, int> Link; Item* createItem(const QDomElement& element); Solver* createSolver(const QDomElement& element); CollisionSolver* createCollisionSolver(const QDomElement& element); ConstraintSolver* createConstraintSolver(const QDomElement& element); bool parseWorld(const QDomElement& element); bool parseItems(ItemGroup* parent, const QDomElement& element); bool parseObject(Object* object, const QDomElement& element); bool parseProperties(Object* object, const QDomElement& parent); bool connectLinks(); World* _world; const Factory* _factory; QDomDocument _document; QString _errorMsg; int _errorLine; int _errorCount; QString _version; QHash _ids; QList _links; }; StepDomDocument::StepDomDocument(World* world, const StepCore::Factory* factory) : _world(world), _factory(factory), _errorLine(0), _errorCount(0) { } bool StepDomDocument::parse(QIODevice* device) { if (!_document.setContent(device, &_errorMsg, &_errorLine, &_errorCount)) { return false; } QDomElement worldElement = _document.firstChildElement(QStringLiteral("world")); if (worldElement.isNull()) { _errorMsg = QObject::tr("The file is not a StepCoreXML file."); return false; } return parseWorld(worldElement); } bool StepDomDocument::parseWorld(const QDomElement& world) { _version = world.attribute(QStringLiteral("version"), QStringLiteral("1.0")); if (!parseObject(_world, world)) return false; if (!parseItems(_world, world)) return false; QDomElement solverElement = world.firstChildElement(QStringLiteral("solver")); if (!solverElement.isNull()) { Solver *solver = createSolver(solverElement); if (!solver) return false; _world->setSolver(solver); } QDomElement collisionSolverElement = world.firstChildElement(QStringLiteral("collisionSolver")); if (!collisionSolverElement.isNull()) { CollisionSolver *solver = createCollisionSolver(collisionSolverElement); if (!solver) return false; _world->setCollisionSolver(solver); } QDomElement constraintSolverElement = world.firstChildElement(QStringLiteral("constraintSolver")); if (!constraintSolverElement.isNull()) { ConstraintSolver *solver = createConstraintSolver(constraintSolverElement); if (!solver) return false; _world->setConstraintSolver(solver); } return connectLinks(); } Item* StepDomDocument::createItem(const QDomElement& element) { QString className = element.attribute(QStringLiteral("class")); QScopedPointer item(_factory->newItem(className)); if (!item) { _errorMsg = QObject::tr("Unknown item type \"%1\"").arg(className); return 0; } if (!parseObject(item.data(), element)) return 0; ObjectErrors *objErrors = item->objectErrors(); if (objErrors && !parseProperties(objErrors, element)) return 0; if (item->metaObject()->inherits("ItemGroup")) { ItemGroup *group = static_cast(item.data()); if (!parseItems(group, element)) return 0; } return item.take(); } Solver* StepDomDocument::createSolver(const QDomElement& element) { QString className = element.attribute(QStringLiteral("class")); QScopedPointer solver(_factory->newSolver(className)); if (!solver) { _errorMsg = QObject::tr("Unknown solver type \"%1\"").arg(className); return 0; } if (!parseObject(solver.data(), element)) return 0; return solver.take(); } CollisionSolver* StepDomDocument::createCollisionSolver(const QDomElement& element) { QString className = element.attribute(QStringLiteral("class")); QScopedPointer solver(_factory->newCollisionSolver(className)); if (!solver) { _errorMsg = QObject::tr("Unknown collisionSolver type \"%1\"").arg(className); return 0; } if (!parseObject(solver.data(), element)) return 0; return solver.take(); } ConstraintSolver* StepDomDocument::createConstraintSolver(const QDomElement& element) { QString className = element.attribute(QStringLiteral("class")); QScopedPointer solver(_factory->newConstraintSolver(className)); if (!solver) { _errorMsg = QObject::tr("Unknown constraint solver type \"%1\"").arg(className); return 0; } if (!parseObject(solver.data(), element)) return 0; return solver.take(); } bool StepDomDocument::parseItems(ItemGroup* parent, const QDomElement& element) { QDomElement itemElement = element.firstChildElement(QStringLiteral("item")); while (!itemElement.isNull()) { Item *item = createItem(itemElement); if (!item) return false; parent->addItem(item); itemElement = itemElement.nextSiblingElement(QStringLiteral("item")); } return true; } bool StepDomDocument::parseObject(Object* object, const QDomElement& element) { int n = element.attribute(QStringLiteral("id")).trimmed().toInt(); if (!n) { _errorMsg = QObject::tr("Wrong ID attribute value for %1") .arg(object->metaObject()->className()); return false; } if (_ids.contains(n)) { _errorMsg = QObject::tr("Non-unique ID attribute value for %1") .arg(object->metaObject()->className()); return false; } _ids.insert(n, object); return parseProperties(object, element); } bool StepDomDocument::parseProperties(Object* object, const QDomElement& parent) { int properties = object->metaObject()->propertyCount(); for (int n = 0; n < properties; ++n) { const MetaProperty* property = object->metaObject()->property(n); if (!property->isStored()) continue; QString name = property->name(); QDomElement propertyElement = parent.firstChildElement(name); if (propertyElement.isNull()) continue; QString text = propertyElement.text(); if (property->userTypeId() == qMetaTypeId()) { int n = text.trimmed().toInt(); _links.push_back(qMakePair(qMakePair(object, property), n)); } else if (!property->writeString(object, text)) { _errorMsg = QObject::tr("Property \"%1\" of \"%2\" has illegal value") .arg(name, object->metaObject()->className()); return false; } } return true; } bool StepDomDocument::connectLinks() { foreach (const Link& link, _links) { QVariant target = QVariant::fromValue(_ids.value(link.second, 0)); if (!link.first.second->writeVariant(link.first.first, target)) { _errorMsg = QObject::tr("Property \"%1\" of \"%2\" has illegal value") .arg(link.first.second->name(), link.first.first->metaObject()->className()); return false; } } return true; } } // namespace bool XmlFile::save(const World* world) { if(!_device->isOpen() || !_device-> isWritable()) { _errorString = QObject::tr("File is not writable."); return false; } StepStreamWriter writer(_device); return writer.writeWorld(world); } bool XmlFile::load(World* world, const Factory* factory) { StepDomDocument document(world, factory); if (!document.parse(_device)) { _errorString = document.errorMsg(); return false; } return true; } } // namespace StepCore #endif //STEPCORE_WITH_QT