Commit accff21e authored by Yann Boucher's avatar Yann Boucher
Browse files

Implemented the basis for dynamic configuration of transition and neighborhood rules

parent 4ea831f3
Pipeline #78347 passed with stages
in 17 seconds
...@@ -283,6 +283,19 @@ pattern recorded :</string> ...@@ -283,6 +283,19 @@ pattern recorded :</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="QSpinBox" name="widthSpinBox">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="heightLabel"> <widget class="QLabel" name="heightLabel">
<property name="text"> <property name="text">
...@@ -293,16 +306,6 @@ pattern recorded :</string> ...@@ -293,16 +306,6 @@ pattern recorded :</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="1">
<widget class="QPushButton" name="validateGridDim">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>OK</string>
</property>
</widget>
</item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QSpinBox" name="heightSpinBox"> <widget class="QSpinBox" name="heightSpinBox">
<property name="minimum"> <property name="minimum">
...@@ -316,16 +319,13 @@ pattern recorded :</string> ...@@ -316,16 +319,13 @@ pattern recorded :</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="2" column="1">
<widget class="QSpinBox" name="widthSpinBox"> <widget class="QPushButton" name="validateGridDim">
<property name="minimum"> <property name="enabled">
<number>1</number> <bool>false</bool>
</property>
<property name="maximum">
<number>9999</number>
</property> </property>
<property name="value"> <property name="text">
<number>10</number> <string>OK</string>
</property> </property>
</widget> </widget>
</item> </item>
...@@ -411,30 +411,15 @@ pattern recorded :</string> ...@@ -411,30 +411,15 @@ pattern recorded :</string>
<string>Neighborhood :</string> <string>Neighborhood :</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>neighborhoodComboBox</cstring> <cstring>neighborhood_list</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QComboBox" name="neighborhoodComboBox"> <widget class="QComboBox" name="neighborhood_list">
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>-1</number>
</property> </property>
<item>
<property name="text">
<string>Von Neumann</string>
</property>
</item>
<item>
<property name="text">
<string>Moore</string>
</property>
</item>
<item>
<property name="text">
<string>Custom</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item row="0" column="2"> <item row="0" column="2">
...@@ -523,17 +508,25 @@ pattern recorded :</string> ...@@ -523,17 +508,25 @@ pattern recorded :</string>
</layout> </layout>
</item> </item>
<item> <item>
<widget class="QLabel" name="transitionFunctionLabel"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="text"> <item>
<string>Transition function :</string> <widget class="QLabel" name="transitionFunctionLabel">
</property> <property name="text">
<property name="buddy"> <string>Transition function :</string>
<cstring>transitionFunctionTextEdit</cstring> </property>
</property> </widget>
</widget> </item>
<item>
<widget class="QComboBox" name="transition_list"/>
</item>
</layout>
</item> </item>
<item> <item>
<widget class="QPlainTextEdit" name="transitionFunctionTextEdit"/> <widget class="QGroupBox" name="rule_settings_area">
<property name="title">
<string>Transition rule settings</string>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="informationLabel"> <widget class="QLabel" name="informationLabel">
......
...@@ -20,7 +20,7 @@ Fichier contenant la class singleton template Factory, à qui on peut associer d ...@@ -20,7 +20,7 @@ Fichier contenant la class singleton template Factory, à qui on peut associer d
namespace detail namespace detail
{ {
template <typename _Base, typename Child> template <typename _Base, typename Child>
bool register_to_factory(); bool register_to_factory(const char* name);
} }
/** /**
...@@ -33,7 +33,7 @@ template <typename Base> ...@@ -33,7 +33,7 @@ template <typename Base>
class Factory class Factory
{ {
template <typename _Base, typename Child> template <typename _Base, typename Child>
friend bool detail::register_to_factory(); friend bool detail::register_to_factory(const char* name);
public: public:
//! \brief Retourne un objet créé à partir de choice //! \brief Retourne un objet créé à partir de choice
...@@ -70,9 +70,9 @@ private: ...@@ -70,9 +70,9 @@ private:
} }
template <typename Child> template <typename Child>
bool register_child() bool register_child(const char* name)
{ {
child_register[Child::name()] = []() -> std::unique_ptr<Base>{ return std::make_unique<Child>(); }; child_register[name] = []() -> std::unique_ptr<Base>{ return std::make_unique<Child>(); };
return true; return true;
} }
...@@ -86,21 +86,21 @@ private: ...@@ -86,21 +86,21 @@ private:
//! \param child Le type de classe dérivée de base que la fabrique devra pouvoir construire. //! \param child Le type de classe dérivée de base que la fabrique devra pouvoir construire.
//! Ajoute la classe child à la liste des objets fabriquables par Factory<base>. //! Ajoute la classe child à la liste des objets fabriquables par Factory<base>.
//! Le nom de la classe tel que considéré par factory est obtenu par un appel à Child::name(), il est donc nécéssaire que l'enfant possède une méthode statique de signature 'static std::string name()'. //! Le nom de la classe tel que considéré par factory est obtenu par un appel à Child::name(), il est donc nécéssaire que l'enfant possède une méthode statique de signature 'static std::string name()'.
#define REGISTER_FACTORY_ENTRY(base, child) \ #define REGISTER_FACTORY_ENTRY(base, child, name) \
namespace detail { \ namespace detail { \
/* nécéssaire pour que la transition soit enregistrée parmis la liste de classes de transitions*/ \ /* nécéssaire pour que la transition soit enregistrée parmis la liste de classes de transitions*/ \
/* variable globale static permettant seulement de forcer l'enregistrement d'une telle classe dans la factory, 'static' permettant l'instantiation de cette variable dans des fichiers .cpp différents lors de l'inclusion*/ \ /* variable globale static permettant seulement de forcer l'enregistrement d'une telle classe dans la factory, 'static' permettant l'instantiation de cette variable dans des fichiers .cpp différents lors de l'inclusion*/ \
static bool const fact_##base##_##child##_registered = register_to_factory<base, child>(); \ static bool const fact_##base##_##child##_registered = register_to_factory<base, child>(name); \
} }
//! \internal namespace contenant les détails d'implémentation de Factory //! \internal namespace contenant les détails d'implémentation de Factory
namespace detail namespace detail
{ {
template <typename Base, typename Child> template <typename Base, typename Child>
inline bool register_to_factory() inline bool register_to_factory(const char* name)
{ {
static_assert (std::is_base_of<Base, Child>::value, "Le type Child doit être dérivé de Base"); static_assert (std::is_base_of<Base, Child>::value, "Le type Child doit être dérivé de Base");
return Factory<Base>::get().template register_child<Child>(); return Factory<Base>::get().template register_child<Child>(name);
} }
} }
......
...@@ -37,7 +37,7 @@ private slots: ...@@ -37,7 +37,7 @@ private slots:
void on_openRuleButton_clicked(); void on_openRuleButton_clicked();
//! \brief Envoie un signal lors du changement de voisinage. Lorsque Custom est sélectionné, le bouton "Custom Neighborhood" est actif //! \brief Envoie un signal lors du changement de voisinage. Lorsque Custom est sélectionné, le bouton "Custom Neighborhood" est actif
void on_neighborhoodComboBox_currentTextChanged(const QString &arg1); void on_neighborhood_list_currentTextChanged(const QString &arg1);
//! \brief Envoie le signal que la dimension de la grille a été modifié //! \brief Envoie le signal que la dimension de la grille a été modifié
void on_validateGridDim_clicked(); void on_validateGridDim_clicked();
...@@ -64,6 +64,13 @@ private slots: ...@@ -64,6 +64,13 @@ private slots:
//! \brief Copie la structure choisie dans la bibliothèque dans le presse-papier du GridView //! \brief Copie la structure choisie dans la bibliothèque dans le presse-papier du GridView
void copy_structure_clicked(const Structure& s); void copy_structure_clicked(const Structure& s);
private:
//! \brief Initialiser la liste des transitions et voisinages disponibles
void init_transition_neighborhood_list();
//! \brief Mets à jour le widget contenant les paramètres pour la règle de transition choisie
void update_transition_settings();
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
}; };
......
...@@ -14,16 +14,16 @@ Représente un voisinage arbitraire, défini par l'utilisateur. ...@@ -14,16 +14,16 @@ Représente un voisinage arbitraire, défini par l'utilisateur.
#include "neighborhoodrule.hpp" #include "neighborhoodrule.hpp"
#include "property.hpp" #include "property.hpp"
class ArbitraryNeighborhoodRule : public HasUserProperties class ArbitraryNeighborhoodRule : public NeighborhoodRule, public HasUserProperties
{ {
public: public:
virtual Neighborhood getNeighborhood(const Grid& grid, Coord pos) const; Neighborhood getNeighborhood(const Grid& grid, Coord pos) const override;
virtual std::vector<NeighborhoodFormat> getFormats() const; std::vector<NeighborhoodFormat> getFormats() const override;
public: public:
DEFINE_CONFIGURABLE_DYNLIST (CoordinateProperty, neighbors, "neighbors", ()); DEFINE_CONFIGURABLE_DYNLIST (CoordinateProperty, neighbors, "neighbors", ());
}; };
REGISTER_FACTORY_ENTRY(NeighborhoodRule, ArbitraryNeighborhoodRule, "Arbitrary");
#endif // ARBITRARYNEIGHBORHOODRULE_HPP #endif // ARBITRARYNEIGHBORHOODRULE_HPP
...@@ -29,5 +29,6 @@ public: ...@@ -29,5 +29,6 @@ public:
//! \return Retourne les formats de voisinage possible dans un std::vector. //! \return Retourne les formats de voisinage possible dans un std::vector.
std::vector<NeighborhoodFormat> getFormats() const; std::vector<NeighborhoodFormat> getFormats() const;
}; };
REGISTER_FACTORY_ENTRY(NeighborhoodRule, mooreNeighborhoodRule, "Moore");
#endif // MOORENEIGHBOURHOODRULE_HPP #endif // MOORENEIGHBOURHOODRULE_HPP
...@@ -30,5 +30,6 @@ public: ...@@ -30,5 +30,6 @@ public:
//! \return Retourne les formats de voisinage possible dans un std::vector. //! \return Retourne les formats de voisinage possible dans un std::vector.
std::vector<NeighborhoodFormat> getFormats() const; std::vector<NeighborhoodFormat> getFormats() const;
}; };
REGISTER_FACTORY_ENTRY(NeighborhoodRule, vonNeumannNeighborhoodRule, "Von Neumann");
#endif // VONNEUMANNNEIGHBORHOODRULE_HPP #endif // VONNEUMANNNEIGHBORHOODRULE_HPP
...@@ -18,6 +18,7 @@ Cette classe peut aussi fournir l'ensemble des positions de voisins qu'elle peut ...@@ -18,6 +18,7 @@ Cette classe peut aussi fournir l'ensemble des positions de voisins qu'elle peut
#include "coord.hpp" #include "coord.hpp"
#include "grid.h" #include "grid.h"
#include "neighborhood.hpp" #include "neighborhood.hpp"
#include "factory.hpp"
/** /**
\struct NeighborhoodFormat \struct NeighborhoodFormat
......
...@@ -3,28 +3,19 @@ ...@@ -3,28 +3,19 @@
#include "transitionrule.hpp" #include "transitionrule.hpp"
#include "property.hpp"
// http://joatdev.fr/CyclicAutomaton // http://joatdev.fr/CyclicAutomaton
class CircularTransition : public TransitionRule, public HasUserProperties class CircularTransition : public TransitionRule
{ {
public: public:
bool acceptFormat(const std::vector<NeighborhoodFormat>&) const override bool acceptFormat(const std::vector<NeighborhoodFormat>&) const override
{ return true; } { return true; }
unsigned getState(unsigned cell, const Neighborhood& neighborhood) const override unsigned getState(unsigned cell, const Neighborhood& neighborhood) const override;
{
unsigned next_state = (cell+1) % states.val;
unsigned next_state_neighbors = neighborhood.getNb(next_state);
if ((int)next_state_neighbors >= thresold.val)
return next_state;
else
return cell;
}
private: private:
DEFINE_CONFIGURABLE_PROPERTY(IntegerProperty, states, "Nombre d'états", 1); DEFINE_CONFIGURABLE_PROPERTY(IntegerProperty, states, "Nombre d'états", 1);
DEFINE_CONFIGURABLE_PROPERTY(IntegerProperty, thresold, "Seuil"); DEFINE_CONFIGURABLE_PROPERTY(IntegerProperty, thresold, "Seuil");
}; };
REGISTER_FACTORY_ENTRY(TransitionRule, CircularTransition, "Circular");
#endif // CIRCULARTRANSITION_HPP #endif // CIRCULARTRANSITION_HPP
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
#include "transitionrule.hpp" #include "transitionrule.hpp"
class LifeGameTransition : TransitionRule class LifeGameTransition : public TransitionRule
{ {
public: public:
LifeGameTransition(); LifeGameTransition();
bool acceptFormat(const std::vector<NeighborhoodFormat>&) const; bool acceptFormat(const std::vector<NeighborhoodFormat>&) const;
unsigned int getState(unsigned int, const Neighborhood &) const; unsigned int getState(unsigned int, const Neighborhood &) const;
}; };
REGISTER_FACTORY_ENTRY(TransitionRule, LifeGameTransition, "Game of Life");
#endif // LIFEGAMETRANSITION_H #endif // LIFEGAMETRANSITION_H
...@@ -12,10 +12,12 @@ Cette classe représente une règle de transition. ...@@ -12,10 +12,12 @@ Cette classe représente une règle de transition.
#include <vector> #include <vector>
#include "coord.hpp" #include "coord.hpp"
#include "neighborhood.hpp" #include "neighborhood.hpp"
#include "factory.hpp"
#include "property.hpp"
struct NeighborhoodFormat; struct NeighborhoodFormat;
class TransitionRule { class TransitionRule : public HasUserProperties {
private: private:
......
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
#include "ui_interface.h" #include "ui_interface.h"
#include "structuresavingdialog.hpp" #include "structuresavingdialog.hpp"
#include "structurewriter.hpp" #include "structurewriter.hpp"
#include "factory.hpp"
#include "transitionrule.hpp"
#include "neighborhoodrule.hpp"
#include "propertyvisitors.hpp"
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
...@@ -11,9 +14,14 @@ MainWindow::MainWindow(QWidget *parent) ...@@ -11,9 +14,14 @@ MainWindow::MainWindow(QWidget *parent)
{ {
ui->setupUi(this); ui->setupUi(this);
init_transition_neighborhood_list();
update_transition_settings();
connect(ui->action_save_struct, &QAction::triggered, this, &MainWindow::afficher_interface_sauvegarde_structure); connect(ui->action_save_struct, &QAction::triggered, this, &MainWindow::afficher_interface_sauvegarde_structure);
connect(ui->struct_library, &StructureLibraryView::structure_copied, this, &MainWindow::copy_structure_clicked); connect(ui->struct_library, &StructureLibraryView::structure_copied, this, &MainWindow::copy_structure_clicked);
connect(ui->grid_view, &GridView::zoom_changed, ui->struct_library, &StructureLibraryView::update_cell_pixel_size); connect(ui->grid_view, &GridView::zoom_changed, ui->struct_library, &StructureLibraryView::update_cell_pixel_size);
connect(ui->transition_list, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int)
{ update_transition_settings(); });
ui->struct_library->update_cell_pixel_size(ui->grid_view->cell_pixel_size()); ui->struct_library->update_cell_pixel_size(ui->grid_view->cell_pixel_size());
} }
...@@ -53,19 +61,15 @@ void MainWindow::on_openRuleButton_clicked() ...@@ -53,19 +61,15 @@ void MainWindow::on_openRuleButton_clicked()
// Indiquer qu'une nouvelle règle vient d'être ouverte. La règle est sauvegardé, donc on peut lancer la simulation // Indiquer qu'une nouvelle règle vient d'être ouverte. La règle est sauvegardé, donc on peut lancer la simulation
} }
void MainWindow::on_neighborhoodComboBox_currentTextChanged(const QString &arg1) void MainWindow::on_neighborhood_list_currentTextChanged(const QString &arg1)
{ {
if(arg1 == "Von Neumann"){
ui->cusumNeighborhoodButton->setEnabled(false);
// Utiliser la règle de voisinage VonNeumann
}
if(arg1 == "Moore"){
ui->cusumNeighborhoodButton->setEnabled(false);
// Utiliser la règle de voisinage Moore
}
if(arg1 == "Custom"){ if(arg1 == "Custom"){
ui->cusumNeighborhoodButton->setEnabled(true); ui->cusumNeighborhoodButton->setEnabled(true);
} }
else
{
ui->cusumNeighborhoodButton->setEnabled(false);
}
} }
void MainWindow::on_widthSpinBox_valueChanged(int) void MainWindow::on_widthSpinBox_valueChanged(int)
...@@ -228,3 +232,35 @@ void MainWindow::copy_structure_clicked(const Structure &s) ...@@ -228,3 +232,35 @@ void MainWindow::copy_structure_clicked(const Structure &s)
ui->grid_view->set_clipboard(s); ui->grid_view->set_clipboard(s);
statusBar()->showMessage("Structure copied.", 5000); statusBar()->showMessage("Structure copied.", 5000);
} }
void MainWindow::init_transition_neighborhood_list()
{
for (const auto& transition : Factory<TransitionRule>::list_choices())
{
ui->transition_list->addItem(QString::fromStdString(transition));
}
for (const auto& rule : Factory<NeighborhoodRule>::list_choices())
{
ui->neighborhood_list->addItem(QString::fromStdString(rule));
}
}
void MainWindow::update_transition_settings()
{
std::string selected = ui->transition_list->currentText().toStdString();
auto rule = Factory<TransitionRule>::make(selected);
if (!rule)
return;
if (rule->get_properties().size() == 0)
ui->rule_settings_area->hide();
else
{
ui->rule_settings_area->show();
UIBuilderVisitor visit(ui->rule_settings_area);
for (auto& prop : rule->get_properties())
prop->accept(visit);
}
}
...@@ -16,6 +16,7 @@ SOURCES += \ ...@@ -16,6 +16,7 @@ SOURCES += \
neighborhood_rules/arbitraryneighborhoodrule.cpp \ neighborhood_rules/arbitraryneighborhoodrule.cpp \
automaton.cpp \ automaton.cpp \
gridview.cpp \ gridview.cpp \
transition_rules/circulartransition.cpp \
transition_rules/lifegametransition.cpp \ transition_rules/lifegametransition.cpp \
main.cpp \ main.cpp \
neighborhood_rules/mooreNeighborhoodRule.cpp \ neighborhood_rules/mooreNeighborhoodRule.cpp \
......
#include "circulartransition.hpp"
unsigned CircularTransition::getState(unsigned cell, const Neighborhood &neighborhood) const
{
unsigned next_state = (cell+1) % states.val;
unsigned next_state_neighbors = neighborhood.getNb(next_state);
if ((int)next_state_neighbors >= thresold.val)
return next_state;
else
return cell;
}
...@@ -11,24 +11,20 @@ public: ...@@ -11,24 +11,20 @@ public:
class LifeLikeTransition : public Transition class LifeLikeTransition : public Transition
{ {
public: public:
static std::string name()
{ return "life-like"; }
virtual std::string say_hi() virtual std::string say_hi()
{ return "Life hi!\n"; } { return "Life hi!\n"; }
}; };
REGISTER_FACTORY_ENTRY(Transition, LifeLikeTransition); REGISTER_FACTORY_ENTRY(Transition, LifeLikeTransition, "life-like");
class GenerationsTransition : public Transition class GenerationsTransition : public Transition
{ {
public: public:
static std::string name()
{ return "generations"; }
virtual std::string say_hi() virtual std::string say_hi()
{ return "Generations hi!\n"; } { return "Generations hi!\n"; }
}; };
REGISTER_FACTORY_ENTRY(Transition, GenerationsTransition); REGISTER_FACTORY_ENTRY(Transition, GenerationsTransition, "generations");
void CellulutTests::test_factory() void CellulutTests::test_factory()
......
...@@ -23,6 +23,7 @@ SOURCES += \ ...@@ -23,6 +23,7 @@ SOURCES += \
../src/grid.cpp \ ../src/grid.cpp \