/**
© Florian Joncour, 2013-2018 florian@zetta-sys.com

Ce logiciel est un programme informatique faisant interface à la bibliothèque
Perspective3D, un outil de modélisation 3D à partir de vues orthographiques 2D.

Ce logiciel est régi par la licence CeCILL-C soumise au droit français et
respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions
de la licence CeCILL-C telle que diffusée par le CEA, le CNRS et l'INRIA
sur le site "http://www.cecill.info".

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme,  le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard  l'attention de l'utilisateur est attirée sur les risques
associés au chargement,  à l'utilisation,  à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant  des  connaissances  informatiques approfondies.  Les
utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
pris connaissance de la licence CeCILL-C, et que vous en avez accepté les
termes.
**/

#include <QApplication>
#include <QMessageBox>
#include <QBuffer>

#include "style.h"
#include "interface3d.h"
#include "API/Qt/Conversion.h"
#include "API/Qt/Utils.h"

#ifdef DEBUG
#include <QDebug>
//#define MODE_FILAIRE_AUTO /* Si activé, la vue 3D passera automatiquement en mode filaire suivant le contexte (utile pour le débogage, un peu chiant pour l'utilisateur final). */
#endif

Modele3DInst::Modele3DInst(Th_Perspective *th_p) : th_Perspective(th_p), inst_perspective(th_p->PerspectiveNonConst()), th_import_stl(nullptr), inst_scene3d(nullptr),
    mode_2d(false)
{
}

Modele3DInst::Modele3DInst(const Modele3DInst &c) : th_Perspective(c.th_Perspective), inst_perspective(c.inst_perspective), th_import_stl(c.th_import_stl), inst_scene3d(c.inst_scene3d),
    mode_2d(c.mode_2d)
{
}

Modele3DInst::Modele3DInst(Th_ImportSTL3D *th_imp) : th_Perspective(nullptr), inst_perspective(nullptr), th_import_stl(th_imp), inst_scene3d(th_imp->Scene3DNonConst()),
    mode_2d(false)
{
}

Modele3DInst::Modele3DInst(Perspective3D::PScene3D *s) :  th_Perspective(nullptr), inst_perspective(nullptr), th_import_stl(nullptr), inst_scene3d(s),
    mode_2d(false)
{
}

Modele3DInst::~Modele3DInst()
{
//    Destruct();
}

void Modele3DInst::Destruct()
{
    ArretGeneration();

    if (!mode_2d && inst_perspective) /* On supprime rien en mode 2D (ça n'est que l'affichage d'une partie d'une autre instance de Perspective3D). */
    {
        delete inst_perspective;
        inst_perspective = nullptr;
    }

    if (th_Perspective)
    {
        delete th_Perspective;
        th_Perspective = nullptr;
    }

    if (th_import_stl)
    {
        delete th_import_stl;
        th_import_stl = nullptr;
    }

    if (inst_scene3d)
    {
        delete inst_scene3d;
        inst_scene3d = nullptr;
    }
}

bool Modele3DInst::ArretGeneration()
{
    bool r = false;
    if (th_Perspective && th_Perspective->isRunning() && inst_perspective)
    {
        inst_perspective->Stop();
        th_Perspective->wait();
        r = true;
    }
    if (th_import_stl && th_import_stl->isRunning())
    {
        th_import_stl->Stop();
        th_import_stl->wait();
        r =  true;
    }

    return r;
}

void Modele3DInst::defMode2D(bool etat)
{
    mode_2d = etat;
}

bool Modele3DInst::FinGeneration() const
{
    if (inst_perspective)
    {
        return inst_perspective->FinGeneration();
    }
    if (th_import_stl)
    {
        return th_import_stl->FinImport();
    }
    return true;
}

bool Modele3DInst::ValideGeneration() const
{
    if (inst_perspective)
    {
        if (!inst_perspective->ModelePret())
        {
            return false;
        }
        return inst_perspective->ResultatGeneration() & Perspective3D::resultat_gen3d_t::PGEN_OK;
    }
    if (th_import_stl)
    {
        return th_import_stl->ValideImport();
    }
    return false;
}

bool Modele3DInst::ThreadEnCours() const
{
    if (th_Perspective)
    {
        return th_Perspective->isRunning();
    }
    if (th_import_stl)
    {
        return th_import_stl->isRunning();
    }
    return false;
}

bool Modele3DInst::AfficheAnimeLogo() const
{
    return (th_Perspective != nullptr) || (th_import_stl != nullptr);
}

puint Modele3DInst::Avancement() const
{
    if (inst_perspective)
    {
        return inst_perspective->Avancement();
    }
    if (th_import_stl)
    {
        return th_import_stl->Avancement();
    }
    return 0;
}

Perspective3D::Perspective *Modele3DInst::Perspective() const
{
    return inst_perspective;
}

const Perspective3D::Perspective *Modele3DInst::PerspectiveConst() const
{
    return inst_perspective;
}

Perspective3D::PScene3D *Modele3DInst::SceneP3D() const
{
    if (inst_perspective)
    {
        return inst_perspective->ScenePtr();
    }
    if (inst_scene3d)
    {
        return inst_scene3d;
    }
    return nullptr;
}

const Perspective3D::PScene3D *Modele3DInst::SceneP3DConst() const
{
    if (inst_perspective)
    {
        return inst_perspective->SceneConstPtr();
    }
    if (inst_scene3d)
    {
        return inst_scene3d;
    }
    return nullptr;
}

Interface3d::Interface3d(const AnimationQt &anime, const Modele3DInst &scene, unsigned int id_onglet, const PGL::Params_GL &params_gl, const PGL::Params_Perspective_GL &params_pgl, int id_style, unsigned int mode_affichage_3d, bool gen_barre_outils_3d, bool affichage_points_accroche, bool affiche_proprietes3d_init_, Perspective3D::vues2D_t vue_reference_affichage, QWidget *parent) :
    QWidget(parent), InstanceModele(scene), VueModele(nullptr), Notifications(nullptr), etat_barre_outils(false), affiche_proprietes3d_init(affiche_proprietes3d_init_),
    #ifdef ACTIVE_PROPRIETES3D_DOCK
        Proprietes(new Proprietes3D_dock(scene.Perspective(), scene.SceneP3D(), id_style, this)),
    #else
        Proprietes(new Proprietes3D_widget(scene.Perspective(), scene.SceneP3D(), id_style, this)),
//        Proprietes(nullptr),
    #endif // ACTIVE_PROPRIETES3D_DOCK
    animation(anime), IdOnglet(id_onglet)
{
    if (InstanceModele.AfficheAnimeLogo())
    {
        animelogo = new AnimeLogo(this);
    }
    else
    {
        animelogo = nullptr;
    }

    Construct(anime, params_gl, params_pgl, id_style, mode_affichage_3d, gen_barre_outils_3d, affichage_points_accroche, vue_reference_affichage);
}

Interface3d::~Interface3d()
{
    InstanceModele.Destruct();

    delete Notifications;
    delete VueModele;

    RetireBarreOutils3D();

    delete verticalLayout_3D;
    delete Proprietes;
}

void Interface3d::Construct(const AnimationQt &anime, const PGL::Params_GL &params_gl, const PGL::Params_Perspective_GL &params_pgl, int id_style, unsigned int mode_affichage_3d, bool gen_barre_outils_3d, bool affichage_points_accroche, Perspective3D::vues2D_t vue_reference_affichage)
{
    setAttribute(Qt::WA_DeleteOnClose);

    InstanceModele.defMode2D(params_pgl.mode_2d);
    copie_couleur_fond = QColor((params_gl.couleur_fond.R()*255), (params_gl.couleur_fond.V()*255), (params_gl.couleur_fond.B()*255));

//    ChargeStyleWidget(this, 3);

//    setStyleSheet("background-color: " COULEUR_FOND_GL_HEX ";");
    setContentsMargins(0, 0, 0, 0);

    Layout_principal = new QVBoxLayout(this);
    Layout_principal->setContentsMargins(3, 3, 3, 3);
    Layout_principal->setSpacing(0);
    Layout_principal->setObjectName("verticalLayout_5");

    VueModele = new VueModele3D(anime, params_gl, params_pgl, VectSuppr, true, affichage_points_accroche, vue_reference_affichage); /* Vue pour l'affichage 3D */

    VueModele->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
    if (id_style)
    {
        ChargeStyleWidget(VueModele, id_style);
    }

    verticalLayout_3D = new QVBoxLayout();
    verticalLayout_3D->setObjectName("verticalLayout_3D");
    verticalLayout_3D->setSizeConstraint(QLayout::SetDefaultConstraint);

//    verticalLayout_3D->setContentsMargins(0, 0, 0, 0);
    verticalLayout_3D->setSpacing(0); // 3
//    verticalLayout_3D->setSpacing(3);

    Layout_principal->addLayout(verticalLayout_3D);

//    defOpacite(params_pgl.opacite_solide);

    Notifications = new WNotifications;
//    Notifications->ChargeStyleNotifications(copie_couleur_fond);

//    if (gen_barre_outils_3d)
//    {
//        verticalLayout_3D->addWidget(Notifications); // Si l'on inclu inclu pas le widget dans l'interface, la notification sera placée librement sur l'écran (en prenant comme référence l'interface 3D courante).
//    }
    Notifications->MasqueNotification();

    verticalLayout_3D->addWidget(VueModele);

#ifndef ACTIVE_PROPRIETES3D_DOCK
    verticalLayout_3D->addWidget(Proprietes);
    Proprietes->close();
#endif //!ACTIVE_PROPRIETES3D_DOCK

    if (gen_barre_outils_3d)
    {
        GenBarreOutils3D();
        defTypeAffichage(mode_affichage_3d); // Affichage par défaut
    }
    else
    {
        layout_h_barre_outils3d = nullptr;
        layout_h_outils3d = nullptr;
        Outils3D = nullptr;
        cmb_TypeAffichage = nullptr;
        cmb_SelCouleur = nullptr;
        Separateur_Outils = nullptr;
        lbl_type_affichage = nullptr;
        horizontalSpacer1 = nullptr;
        horizontalSpacer2 = nullptr;
        horizontalSpacer3 = nullptr;
        lbl_RAL = nullptr;
        WOutils3D = nullptr;
        WBarreOutils3D = nullptr;

        SelectionAffichage(mode_affichage_3d);
    }

    if (!InstanceModele.Mode2D())
    {
        connect(VueModele, SIGNAL(SelectionePoint(int, bool, bool)), SLOT(SelectionPoint_sig(int, bool, bool)));
        connect(VueModele, SIGNAL(SelectioneSegment(int, bool, bool)), SLOT(SelectioneSegment_sig(int, bool, bool)));
        connect(VueModele, SIGNAL(SelectioneSurface(int, bool, bool, bool)), SLOT(SelectionSurface_sig(int, bool, bool, bool)));
//        connect(VueModele, SIGNAL(RegenModele(bool)), SLOT(RegenModele(bool)));
        connect(VueModele, SIGNAL(SupprimeSurface_S(int)), SLOT(SupprimeSurface(int)));
        connect(VueModele, SIGNAL(SupprimePoint_S(int)), SLOT(SupprimePoint(int)));
        connect(VueModele, SIGNAL(Restaure_S()), SLOT(Restaure()));
        connect(VueModele, SIGNAL(ExportSolide_S()), SIGNAL(ExportModele()));
        if (InstanceModele.PerspectiveConst())
        {
            connect(VueModele, SIGNAL(ExportScript_S()), SLOT(ExportScript_sig()));
        }
        connect(VueModele, SIGNAL(ExportImage_S()), SIGNAL(ExportImageModele()));
        connect(VueModele, SIGNAL(Imprimer_S()), SIGNAL(ImprimeModele()));
        connect(VueModele, SIGNAL(ConfigAffichage_S(int)), SIGNAL(ConfigAffichage(int)));
        connect(VueModele, SIGNAL(TypeAffichage_S(int)), SLOT(TypeAffichage_sig(int)));
        connect(VueModele, SIGNAL(ProprietesSolide_S()), SLOT(GereProprietes_sig()));

#ifdef ACTIVE_QPAINTER_POST_VUE3D
        connect(VueModele, SIGNAL(PaintGLPost()), SLOT(PaintPostGL()));
#endif // ACTIVE_QPAINTER_POST_VUE3D

#ifdef SUPPORT_GIF
        connect(VueModele, SIGNAL(ExportAnimation_S()), SIGNAL(EnregistreAnimationModele()));
#endif // SUPPORT_GIF
    }

    VueModele->setFocus(Qt::TabFocusReason);

    GereEtatBoutonsOutils3D();
    id_timer = -1;
    InitTimer();
}

void Interface3d::AttendInitialisationGL() const
/* Attend la fin de l'initialisation de la vue 3D. */
{
    if (VueModele)
    {
        while (!VueModele->TermineInitialisation())
        {
            SommeilTempoQt(1);
        }
    }
}

Perspective3D::Perspective *Interface3d::Perspective() const
{
    return InstanceModele.Perspective();
}

Perspective3D::PScene3D *Interface3d::SceneP3D() const
{
    return InstanceModele.SceneP3D();
}

const Perspective3D::PScene3D *Interface3d::SceneP3DConst() const
{
    return InstanceModele.SceneP3DConst();
}

bool Interface3d::AffichageDefinifPret() const
{
    return InstanceModele.FinGeneration();
}

VueModele3D *Interface3d::Vue_Modele() const
{
    return VueModele;
}

void Interface3d::ForceVue(Perspective3D::vues2D_t vue)
{
    VueModele->ForceVue(vue);
}

void Interface3d::VueFace() const { VueModele->VueFace(); }
void Interface3d::VueArriere() const { VueModele->VueArriere(); }
void Interface3d::VueCote() const { VueModele->VueCote(); }
void Interface3d::VueCoteGauche() const { VueModele->VueCoteGauche(); }
void Interface3d::VueHaut() const { VueModele->VueHaut(); }
void Interface3d::VueDessous() const { VueModele->VueDessous(); }
void Interface3d::VuePerspective() const { VueModele->VuePerspective(); }
void Interface3d::VuePerspectiveInv() const { VueModele->VuePerspectiveInv(); }
void Interface3d::AnimeRotationScene() const { VueModele->AnimeRotationScene(); }

void Interface3d::defModeAffichage(bool mode_ortho, bool force_redim, Perspective3D::vues2D_t vue)
{
    VueModele->defModeAffichage(mode_ortho, force_redim);
    ForceVue(vue);
}

void Interface3d::defLissageAffichage3D(bool etat)
{
    VueModele->defLissageAffichage3D(etat);
}

void Interface3d::ChangeStyleOutils3D(const QColor &couleur_fond)
{
    ChargeStyleWidget(WOutils3D, 3, couleur_fond.name().toStdString().data());
}

bool Interface3d::GenBarreOutils3D(QWidget *parent)
{
    if (!parent)
    {
        parent = this;
    }
    if (etat_barre_outils)
    {
        return false;
    }
    etat_barre_outils = true;

    const Perspective3D::PScene3D *scene_ptr = SceneP3DConst();
    if (!scene_ptr) { return false; }

    puint id_couleur_solide = Perspective3D::RAL::RechercheIndex(scene_ptr->CouleurRAL());
    Outils3D = new BarreOutils3D;

    WBarreOutils3D = new QWidget(parent);
    WBarreOutils3D->setContentsMargins(0,0,0,0);

    layout_h_barre_outils3d = new QHBoxLayout(WBarreOutils3D);
    layout_h_barre_outils3d->setSpacing(3);
    layout_h_barre_outils3d->setContentsMargins(0, 0, 0, 0);
    layout_h_barre_outils3d->setObjectName("layout_h_barre_outils3d");
    layout_h_barre_outils3d->setSizeConstraint(QLayout::SetMinimumSize);

    WOutils3D = new QWidget(parent);
    WOutils3D->setContentsMargins(0,0,0,0);
    WOutils3D->setObjectName("Outils3D");

    ChangeStyleOutils3D(copie_couleur_fond);

    layout_h_outils3d = new QHBoxLayout(WOutils3D);
    layout_h_outils3d->setSpacing(3);
    layout_h_outils3d->setContentsMargins(8, 3, 8, 0);
    layout_h_outils3d->setSizeConstraint(QLayout::SetMinimumSize);
    layout_h_outils3d->setObjectName("layout_h_outils3d");

    layout_h_barre_outils3d->addWidget(WOutils3D);

    Outils3D->setupUi(layout_h_outils3d, layout_h_barre_outils3d, InstanceModele.Mode2D(), parent);

    /* Ces outils peuvent être nuls si on est en mode 2D */
    if (Outils3D->vueface)
    {
        connect(Outils3D->vueface, SIGNAL(clicked()), this, SLOT(VueFace()));
    }
    if (Outils3D->vueface)
    {
        connect(Outils3D->vuearriere, SIGNAL(clicked()), this, SLOT(VueArriere()));
    }
    if (Outils3D->vuecote)
    {
        connect(Outils3D->vuecote, SIGNAL(clicked()), this, SLOT(VueCote()));
    }
    if (Outils3D->vuegauche)
    {
        connect(Outils3D->vuegauche, SIGNAL(clicked()), this, SLOT(VueCoteGauche()));
    }
    if (Outils3D->vuehaut)
    {
        connect(Outils3D->vuehaut, SIGNAL(clicked()), this, SLOT(VueHaut()));
    }
    if (Outils3D->vuedessous)
    {
        connect(Outils3D->vuedessous, SIGNAL(clicked()), this, SLOT(VueDessous()));
    }
    if (Outils3D->vueperspective)
    {
        connect(Outils3D->vueperspective, SIGNAL(clicked()), this, SLOT(VuePerspective()));
    }
    if (Outils3D->proprietes3d)
    {
        connect(Outils3D->proprietes3d, SIGNAL(clicked()), this, SLOT(GereProprietes_sig()));
    }
    if (Outils3D->gen_patron)
    {
        connect(Outils3D->gen_patron, SIGNAL(clicked()), this, SIGNAL(GenPatron()));
    }

    connect(Outils3D->export_fichier, SIGNAL(clicked()), this, SIGNAL(ExportModele()));
    connect(Outils3D->export_fichier_2d, SIGNAL(clicked()), this, SIGNAL(ExportModele_2D()));

    if (Perspective())
    {
        connect(Outils3D->export_script, SIGNAL(clicked()), this, SLOT(ExportScript_sig()));
    }
    else
    {
        Outils3D->export_script->setEnabled(false);
    }
    connect(Outils3D->imprime_fichier, SIGNAL(clicked()), this, SIGNAL(ImprimeModele()));
    connect(Outils3D->export_image_modele, SIGNAL(clicked()), this, SIGNAL(ExportImageModele()));
#ifdef SUPPORT_GIF
    connect(Outils3D->export_animation_modele, SIGNAL(clicked()), this, SIGNAL(EnregistreAnimationModele()));
#endif

    horizontalSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
    layout_h_barre_outils3d->addItem(horizontalSpacer1);

    lbl_RAL = new QLabel();
    lbl_RAL->setObjectName("lbl_RAL");
    lbl_RAL->setMaximumSize(QSize(150, 16777215));
    lbl_RAL->setText(tr("RAL"));
    layout_h_barre_outils3d->addWidget(lbl_RAL);

    cmb_SelCouleur = new QComboBox();
    cmb_SelCouleur->setObjectName("cmb_SelCouleur");
    cmb_SelCouleur->setMinimumSize(QSize(150, 0));
    layout_h_barre_outils3d->addWidget(cmb_SelCouleur);
    PeupleCmb_RAL(*(cmb_SelCouleur));
    cmb_SelCouleur->setCurrentIndex(id_couleur_solide);

    horizontalSpacer2 = new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
    layout_h_barre_outils3d->addItem(horizontalSpacer2);

    Separateur_Outils = new QFrame();
    Separateur_Outils->setFrameShape(QFrame::VLine);
    Separateur_Outils->setFrameShadow(QFrame::Sunken);
    Separateur_Outils->setObjectName("separateur");
    layout_h_barre_outils3d->addWidget(Separateur_Outils);

    horizontalSpacer3 = new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Fixed);
    layout_h_barre_outils3d->addItem(horizontalSpacer3);

    lbl_type_affichage = new QLabel();
    lbl_type_affichage->setObjectName("label");
    lbl_type_affichage->setMaximumSize(QSize(150, 16777215));
    layout_h_barre_outils3d->addWidget(lbl_type_affichage);

    cmb_TypeAffichage = new QComboBox();
    cmb_TypeAffichage->setObjectName("cmb_TypeAffichage");
    cmb_TypeAffichage->setMinimumSize(QSize(100, 0));
    layout_h_barre_outils3d->addWidget(cmb_TypeAffichage);

    lbl_type_affichage->setText(tr("Type d'affichage"));
    cmb_TypeAffichage->clear();

//    cmb_TypeAffichage->setStyleSheet("background-color: " COULEUR_FOND_GL_HEX ";");
    cmb_TypeAffichage->insertItems(0, QStringList() << tr("Scène") << tr("Solide") << tr("Arrêtes") << tr("Filaire"));
    cmb_TypeAffichage->setItemIcon(0, QIcon(":/icones/projections1.png"));
    cmb_TypeAffichage->setItemIcon(1, QIcon(":/icones/projections0.png"));
    cmb_TypeAffichage->setItemIcon(2, QIcon(":/icones/arretes.png"));
    cmb_TypeAffichage->setItemIcon(3, QIcon(":/icones/filaire.png"));

    Layout_principal->insertWidget(0, WBarreOutils3D);

    connect(cmb_TypeAffichage, SIGNAL(currentIndexChanged(int)), SLOT(SelectionAffichage(int)));
    connect(cmb_SelCouleur, SIGNAL(currentIndexChanged(int)), SLOT(SelectionCouleur(int)));

    return true;
}

bool Interface3d::RetireBarreOutils3D()
{
    if (!etat_barre_outils)
    {
        return false;
    }
    etat_barre_outils = false;

    if (Outils3D)
    {
        if (Outils3D->vueface)
        {
            disconnect(Outils3D->vueface, SIGNAL(clicked()), this, SLOT(VueFace()));
            layout_h_outils3d->removeWidget(Outils3D->vueface);
        }

        if (Outils3D->vuearriere)
        {
            layout_h_outils3d->removeWidget(Outils3D->vuearriere);
            disconnect(Outils3D->vuearriere, SIGNAL(clicked()), this, SLOT(VueArriere()));
        }

        if (Outils3D->vuecote)
        {
            layout_h_outils3d->removeWidget(Outils3D->vuecote);
            disconnect(Outils3D->vuecote, SIGNAL(clicked()), this, SLOT(VueCote()));
        }

        if (Outils3D->vuegauche)
        {
            layout_h_outils3d->removeWidget(Outils3D->vuegauche);
            disconnect(Outils3D->vuegauche, SIGNAL(clicked()), this, SLOT(VueCoteGauche()));
        }

        if (Outils3D->vuehaut)
        {
            layout_h_outils3d->removeWidget(Outils3D->vuehaut);
            disconnect(Outils3D->vuehaut, SIGNAL(clicked()), this, SLOT(VueHaut()));
        }

        if (Outils3D->vuedessous)
        {
            layout_h_outils3d->removeWidget(Outils3D->vuedessous);
            disconnect(Outils3D->vuedessous, SIGNAL(clicked()), this, SLOT(VueDessous()));
        }

        if (Outils3D->vueperspective)
        {
            layout_h_outils3d->removeWidget(Outils3D->vueperspective);
            disconnect(Outils3D->vueperspective, SIGNAL(clicked()), this, SLOT(VuePerspective()));
        }

        if (Outils3D->proprietes3d)
        {
            layout_h_barre_outils3d->removeWidget(Outils3D->proprietes3d);
            disconnect(Outils3D->proprietes3d, SIGNAL(clicked()), this, SLOT(GereProprietes_sig()));
        }

        if (Outils3D->gen_patron)
        {
            layout_h_barre_outils3d->removeWidget(Outils3D->gen_patron);
            disconnect(Outils3D->gen_patron, SIGNAL(clicked()), this, SIGNAL(GenPatron()));
        }

        if (Outils3D->projections)
        {
            layout_h_barre_outils3d->removeWidget(Outils3D->projections);
//            disconnect(Outils3D->projections, SIGNAL(clicked()), this, SIGNAL(AfficheProjections()));
        }

        if (Outils3D->export_fichier)
        {
            disconnect(Outils3D->export_fichier, SIGNAL(clicked()), this, SIGNAL(ExportModele()));
            layout_h_barre_outils3d->removeWidget(Outils3D->export_fichier);
        }

        if (Outils3D->export_fichier_2d)
        {
            disconnect(Outils3D->export_fichier_2d, SIGNAL(clicked()), this, SIGNAL(ExportModele()));
            layout_h_barre_outils3d->removeWidget(Outils3D->export_fichier_2d);
        }

        if (Outils3D->export_script && InstanceModele.PerspectiveConst())
        {
            disconnect(Outils3D->export_script, SIGNAL(clicked()), this, SLOT(ExportScript_sig()));
            layout_h_barre_outils3d->removeWidget(Outils3D->export_script);
        }

        if (Outils3D->imprime_fichier)
        {
            disconnect(Outils3D->imprime_fichier, SIGNAL(clicked()), this, SIGNAL(ImprimeModele()));
            layout_h_barre_outils3d->removeWidget(Outils3D->imprime_fichier);
        }

        if (Outils3D->export_image_modele)
        {
            disconnect(Outils3D->export_image_modele, SIGNAL(clicked()), this, SIGNAL(ExportImageModele()));
            layout_h_barre_outils3d->removeWidget(Outils3D->export_image_modele);
        }

#ifdef SUPPORT_GIF
        if (Outils3D->export_animation_modele)
        {
            disconnect(Outils3D->export_animation_modele, SIGNAL(clicked()), this, SIGNAL(EnregistreAnimationModele()));
            layout_h_barre_outils3d->removeWidget(Outils3D->export_animation_modele);
        }
#endif // SUPPORT_GIF

        delete Outils3D;
        Outils3D = nullptr;
    }

    if (Separateur_Outils)
    {
        layout_h_barre_outils3d->removeWidget(Separateur_Outils);
        delete Separateur_Outils;
        Separateur_Outils = nullptr;
    }

    if (lbl_type_affichage)
    {
        layout_h_barre_outils3d->removeWidget(lbl_type_affichage);
        delete lbl_type_affichage;
        lbl_type_affichage = nullptr;
    }

    if (lbl_RAL)
    {
        layout_h_barre_outils3d->removeWidget(lbl_RAL);
        delete lbl_RAL;
        lbl_RAL = nullptr;
    }

    if (cmb_TypeAffichage)
    {
        disconnect(cmb_TypeAffichage, SIGNAL(currentIndexChanged(int)), this, SLOT(SelectionAffichage(int)));
        layout_h_barre_outils3d->removeWidget(cmb_TypeAffichage);
        delete cmb_TypeAffichage;
        cmb_TypeAffichage = nullptr;
    }

    if (cmb_SelCouleur)
    {
        disconnect(cmb_SelCouleur, SIGNAL(currentIndexChanged(int)), this, SLOT(SelectionCouleur(int)));
        layout_h_barre_outils3d->removeWidget(cmb_TypeAffichage);
        delete cmb_SelCouleur;
        cmb_SelCouleur = nullptr;
    }

    if (horizontalSpacer1)
    {
        layout_h_barre_outils3d->removeItem(horizontalSpacer1);
//        delete horizontalSpacer1;
        horizontalSpacer1 = nullptr;
    }

    if (horizontalSpacer2)
    {
        layout_h_barre_outils3d->removeItem(horizontalSpacer2);
//        delete horizontalSpacer2;
        horizontalSpacer2 = nullptr;
    }

    if (horizontalSpacer3)
    {
        layout_h_barre_outils3d->removeItem(horizontalSpacer3);
//        delete horizontalSpacer3;
        horizontalSpacer3 = nullptr;
    }

    if (layout_h_outils3d)
    {
        layout_h_barre_outils3d->removeItem(layout_h_outils3d);
        delete layout_h_outils3d;
        layout_h_outils3d = nullptr;
    }

    if (layout_h_barre_outils3d)
    {
        delete layout_h_barre_outils3d;
        layout_h_barre_outils3d = nullptr;
    }

    if (WOutils3D)
    {
        delete WOutils3D;
        WOutils3D = nullptr;
    }

    if (WBarreOutils3D)
    {
        delete WBarreOutils3D;
        WBarreOutils3D = nullptr;
    }

    return true;
}

bool Interface3d::ChangeEtatBarreOutils(bool etat)
{
    if (etat == etat_barre_outils)
    {
        return false;
    }

    if (etat)
    {
        return GenBarreOutils3D();
    }
    return RetireBarreOutils3D();
}

void Interface3d::GereEtatBoutonsOutils3D()
{
    if (!Outils3D)
    {
        return;
    }

    bool etat = false;
    if (Perspective() && VueModele->TermineInitialisation())
    {
        if (InstanceModele.FinGeneration() && !InstanceModele.ThreadEnCours())
        {
            etat = true; /* Génération terminée... */
        }
    }
    else if (SceneP3DConst() && VueModele->TermineInitialisation())
    {
        if (InstanceModele.FinGeneration() && !InstanceModele.ThreadEnCours()) /* Génération terminée... */
        {
            etat = true;
        }
    }

    Outils3D->ChangeEtatOutilsExport(etat, Ptest_ptr(Perspective()));
}

void Interface3d::defCouleurFond(QRgb c, bool animation)
{
    VueModele->defCouleurFond(c, animation);
    copie_couleur_fond = QColor(c);
    ChangeStyleOutils3D(copie_couleur_fond);

//    if (Notifications != nullptr)
//    {
//        Notifications->ChargeStyleNotifications(copie_couleur_fond);
//    }
}

void Interface3d::defCouleursScene(QRgb couleur_sommets, QRgb couleur_segments, QRgb couleur_imprimante, QRgb couleur_reperes_sommets, QRgb couleur_reperes_segments, QRgb couleur_reperes_faces)
{
    VueModele->defCouleursScene(couleur_sommets, couleur_segments, couleur_imprimante, couleur_reperes_sommets, couleur_reperes_segments, couleur_reperes_faces);
}

void Interface3d::ForceModeAffichage(int index, bool sauvegarde_mode_affichage)
{
    if (sauvegarde_mode_affichage)
    {
        proprietes_plan.type_affichage = index;
    }

    if (cmb_TypeAffichage)
    {
        if (cmb_TypeAffichage->currentIndex() != index)
        {
            cmb_TypeAffichage->setCurrentIndex(index);
        }
        else
        {
//            return;
        }
    }
    VueModele->defAffichage(index, true);
}

void Interface3d::ForceSelectionCouleur(int index, bool sauvegarde_couleur)
{
    if (sauvegarde_couleur)
    {
        proprietes_plan.id_ral = Perspective3D::RAL::Index(index).id;
    }

    if (cmb_SelCouleur)
    {
        if (cmb_SelCouleur->currentIndex() != index)
        {
            cmb_SelCouleur->setCurrentIndex(index);
        }
        else
        {
//            return;
        }
    }
    VueModele->defIdCouleurScene(index, true);
}

void Interface3d::defImprimante3D(bool active, float x, float y, float z)
/* Active ou désactive la zone d'impression 3D (si on active, les valeurs x et y seront les dimensions du plateau, z étant l'extrusion) */
{
    VueModele->defImprimante3D(active, x, y, z);
}

bool Interface3d::ArretGeneration()
{
    return InstanceModele.ArretGeneration();
}

void Interface3d::GereProprietes_sig()
{
    GereProprietes(false);
}

void Interface3d::TypeAffichage_sig(int id)
{
    defTypeAffichage(id);
}

void Interface3d::ExportScript_sig()
{
    if (Perspective())
    {
        if (!proprietes_plan.ignore)
        {
            emit(ExportScript(proprietes_plan, *Perspective()));
        }
    }
}

void Interface3d::GereProprietes(bool force_fermeture)
{
    if (Proprietes->isVisible() || force_fermeture)
    {
        Proprietes->close();
    }
    else
    {
        Proprietes->show();
    }

    if (Outils3D)
    {
        Outils3D->proprietes3d->setChecked(Proprietes->isVisible());
    }
}

ProprietesPlan_t &Interface3d::ProprietesPlanNonConst()
{
    /* Change l'id de du RAL, si l'utilisateur l'a changé dans l'interface il diffère de la configuration. */
    if (cmb_SelCouleur)
    {
        proprietes_plan.id_ral = cmb_SelCouleur->currentData().toUInt();
    }
    return proprietes_plan;
}

const ProprietesPlan_t &Interface3d::ProprietesPlan()
{
    return ProprietesPlanNonConst();
}

void Interface3d::SelectionAffichage(int index)
{
    ForceModeAffichage(index);
}

void Interface3d::SelectionCouleur(int index)
{
    ForceSelectionCouleur(index);
}

void Interface3d::defIdCouleurSolide(int index)
{
    if (cmb_SelCouleur)
    {
        cmb_SelCouleur->setCurrentIndex(index);
    }
    else
    {
        SelectionCouleur(index);
    }
}

void Interface3d::defTypeAffichage(int type)
{
    if (cmb_TypeAffichage)
    {
        cmb_TypeAffichage->setCurrentIndex(type);
    }
    else
    {
        SelectionAffichage(type);
    }
}

void Interface3d::defOpacite(int val)
{
    VueModele->defOpacite(val);
}

void Interface3d::DefEclairageDynamique(bool etat)
{
    VueModele->defEclairageDynamique(etat);
}

void Interface3d::defAffichageAccroche(bool etat)
{
    VueModele->defAffichageAccroche(etat);
}

bool Interface3d::InitAnimationSolide()
{
    return VueModele->InitAnimationSolide();
}

void Interface3d::SurligneCouleur(PGL::PCouleur_GL &c, unsigned int n) const
{
    for(unsigned int i=0; i<n; ++i)
    {
        c = c.Div();
    }
}

void Interface3d::RestaureCouleur(PGL::PCouleur_GL &c, unsigned int n) const
{
    for(unsigned int i=0; i<n; ++i)
    {
        c = c.Div2();
    }
}

int Interface3d::NormaliseId(int id, const Perspective3D::PStdVectInts &vect) const
/* Normalise un id de vecteur d'après les ids contenus dans un second vecteur qui contient les ids à ignorer.
    Les ids qui suivent un id à ignorer sont décrémentés. */
{
    if (id < 0) { return 0; }

    unsigned int decalage = 0;
    const unsigned int n = vect.size();
    for(unsigned int i=0; i<n; ++i)
    {
        if (vect[i] < id)
        {
            ++decalage;
        }
    }
    return id-decalage;
}

unsigned int Interface3d::NSommetsCorbeillePoints(int index) const
/* Renvoi le total des sommets dans les points placées en corbeille après l'index donné en argument. */
{
    if (index < 0) { return 0; }
    unsigned int decalage = 0;
    const unsigned int ns = Corbeille_Points.size();
    for(unsigned int i=0; i<ns; ++i)
    {
        if (index > Corbeille_Points[i])
        {
            ++decalage;
        }
    }
    return decalage;
}

unsigned int Interface3d::NSurfacesCorbeilleSolides(int index) const
/* Renvoi le total des surfaces dans les solides placés en corbeille après l'index donné en argument. */
{
    if (index < 0) { return 0; }
    unsigned int decalage = 0;
    const unsigned int ns = Corbeille_Solides.size();
    for(unsigned int i=0; i<ns; ++i)
    {
        if (index > Corbeille_Solides[i])
        {
            ++decalage;
        }
    }
    return decalage;
}

unsigned int Interface3d::NSegmentsCorbeilleLigne(int index) const
{
    if (index<0) { return 0; }
    unsigned int decalage = 0;
    const unsigned int ns = Corbeille_Lignes.size();
    for(unsigned int i=0; i<ns; ++i)
    {
        if (index >= Corbeille_Lignes[i])
        {
            ++decalage;
        }
    }
    return decalage;
}

unsigned int Interface3d::NTrianglesCorbeilleSurfaces(int index) const
/* Renvoi le total des triangles dans les surfaces placées en corbeille pour les surfaces après l'index donné en argument. */
{
//    if (index<0 || (!Corbeille_Surfaces.size())) { return 0; }
    unsigned int decalage = 0;

    const Perspective3D::PScene3D *scene_ptr = SceneP3DConst();
    if (!scene_ptr) { return 0; }

    const Perspective3D::PStdVectSurfaces3D &surfaces = scene_ptr->Maillage().Surfaces();
    const unsigned int ns = Corbeille_Surfaces.size();

    for(unsigned int i=0; i<ns; ++i)
    {
        if (index >= Corbeille_Surfaces[i])
        {
            decalage += (surfaces[Corbeille_Surfaces[i]].TrianglesConst().size());
//            decalage += (surfaces[Corbeille_Surfaces[i]].TrianglesConst().size()) * 3;
        }
    }
    return decalage;
}

void Interface3d::SelectionPoint_sig(int index, bool active_proprietes, bool centre_point)
{
    SelectionPoint(index, active_proprietes, centre_point);
}

void Interface3d::SelectioneSegment_sig(int index, bool active_proprietes, bool centre_segment)
{
    SelectionSegment(index, active_proprietes, centre_segment);
}

void Interface3d::SupprimeRestaurePoint(int index, bool regen)
{
    if (!SceneP3DConst())
    {
        return;
    }
    const int id = index-1;

    if (!SceneP3DConst())
    {
        return;
    }

    if (id < 0 || size_t(id) >= SceneP3D()->Maillage().Sommets().size())
    {
        return;
    }

    Perspective3D::PSommet3D &t = SceneP3D()->Sommet(id);

    if (t.Actif()) /* Suppression */
    {
        SupprimePoint(id, regen);
    }
    else /* Restauration */
    {
        Restaure();
    }
}

void Interface3d::SupprimeRestaureLigne(int index, bool regen)
{
    if (!SceneP3DConst())
    {
        return;
    }
    const int id = index-1;

    if (id < 0 || size_t(id) >= SceneP3D()->Maillage().Filaire().size())
    {
        return;
    }

    Perspective3D::PSegment3D &t = SceneP3D()->Segment(id);

    if (t.Actif()) /* Suppression */
    {
        SupprimeLigne(id, regen);
    }
    else /* Restauration */
    {
        Restaure();
    }
}

void Interface3d::SupprimeRestaureSurface(int index, bool regen)
{
    if (!SceneP3DConst())
    {
        return;
    }
    const int id = index-1;

    if (id < 0 || size_t(id) >= SceneP3D()->Maillage().Surfaces().size())
    {
        return;
    }

    Perspective3D::PSurface3D &t = SceneP3D()->Surface(id);

    if (t.Actif()) /* Suppression */
    {
        SupprimeSurface(id, regen);
    }
    else /* Restauration */
    {
        Restaure();
    }
}

void Interface3d::SupprimeRestaureSolide(int index, bool regen)
{
    if (!SceneP3DConst())
    {
        return;
    }
    const int id = index-1;

    if (id < 0 || size_t(id) >= SceneP3D()->Solides().size())
    {
        return;
    }

    Perspective3D::PSolide3D &t = SceneP3D()->Solide(id);

    if (t.Actif()) /* Suppression */
    {
        SupprimeSolide(id, regen);
    }
    else /* Restauration */
    {
        Restaure();
    }
}

void Interface3d::SelectionPoint(int index, bool active_proprietes, bool centre_point)
{
    if (!AffichageDefinifPret() || (index < 0))
    {
        return;
    }

    const Perspective3D::PScene3D *scene_ptr = SceneP3DConst();
    if (!scene_ptr) { return; }

    const int id = index-1; /* Si id vaut -1, touts les points seront réinitialisés. */
    const Perspective3D::PStdVectSommets3D &points = scene_ptr->Maillage().Sommets();
    Perspective3D::PSommet3D const *point_selection = nullptr;
    PGL::PVectCouleur_GL &couleurssommets = VueModele->CouleursSommets();

    if (!couleurssommets.size())
    {
        return;
    }
//    unsigned int decalage = NSommetsCorbeillePoints(id);

#ifdef MODE_FILAIRE_AUTO
    DefTypeAffichage(PGL_MODE_AFFICHAGE_DEFAUT);
#endif // MODE_FILAIRE_AUTO

    const PGL::PCouleur_GL &couleur_ref_sommets = VueModele->CouleurReferenceSommets();

    for (unsigned i=0; i<points.size(); ++i)
    {
        unsigned int decalage = NSommetsCorbeillePoints(i);
        const int id_dec = i-decalage;
        if (id_dec < 0)
        {
            continue;
        }
        const Perspective3D::PSommet3D &t = points[id_dec];
        PGL::PCouleur_GL &couleur = couleurssommets[id_dec];
        if (t.Id() == id)
        {
            point_selection = &t;
//            centre = t.v1;
            SurligneCouleur(couleur);
        }
        else
        {
            if (t.CouleurConst().Actif())
            {
                couleur.defCouleur(couleur_ref_sommets);
            }
            else
            {
//                couleur.defCouleur(VueModele->CouleurReferenceSommets());
                RestaureCouleur(couleur);
            }
        }
    }

    if (point_selection)
    {
        if (centre_point)
        {
            VueModele->CentreSur(point_selection->V1(), false, false);
        }
        if (active_proprietes)
        {
#ifdef MODE_FILAIRE_AUTO
            DefTypeAffichage(MODE_FILAIRE);
#endif // MODE_FILAIRE_AUTO
            Proprietes->widget().AfficheInfosPoint(SceneP3DConst()->Maillage().Sommets()[point_selection->Id()]);
        }
    }
    VueModele->MiseaJourModele(true, false, false);
}

void Interface3d::SelectionSegment(int index, bool active_proprietes, bool centre_segment)
{
    if (!AffichageDefinifPret() || (index < 0))
    {
        return;
    }

    const Perspective3D::PScene3D *scene_ptr = SceneP3DConst();
    if (!scene_ptr) { return; }

    const bool surligne_deux_points = true;

    const int id = index-1; /* Si id vaut -1, toutes les lignes seront réinitialisés. */
//    Ppoint3D centre = InstanceModele.PerspectiveConst()->Modele()->centre;
    const Perspective3D::PStdVectSegments3D &lignes = scene_ptr->Maillage().Filaire();
    const unsigned int n_lignes = lignes.size();
    Perspective3D::PSegment3D const *ligne_selection = nullptr;
#ifdef MODE_FILAIRE_AUTO
    DefTypeAffichage(PGL_MODE_AFFICHAGE_DEFAUT);
#endif // MODE_FILAIRE_AUTO
    PGL::PVectCouleur_GL &couleursfilaire = VueModele->CouleursLignes();

    if (!couleursfilaire.size())
    {
        return;
    }

//    unsigned int decalage = NSegmentsCorbeilleLigne(id);

    const PGL::PCouleur_GL &couleur_ref_segments = VueModele->CouleurReferenceSegments();

    for (unsigned int i=0; i<n_lignes; ++i)
    {
        unsigned int decalage = NSegmentsCorbeilleLigne(i);

        const int id_dec = i-decalage;
        if (id_dec < 0)
        {
            continue;
        }

        const Perspective3D::PSegment3D &t = lignes[id_dec];
        PGL::PCouleur_GL &couleur1 = couleursfilaire[id_dec*2]; // Deux éléments de couleur pour chaque ligne.
        PGL::PCouleur_GL &couleur2 = couleursfilaire[(id_dec*2)+1];

        if (t.Id() == id)
        {
            ligne_selection = &t;
            SurligneCouleur(couleur1);
            if (surligne_deux_points)
            {
                SurligneCouleur(couleur2);
            }
        }
        else
        {
            if (t.CouleurConst().Actif())
            {
                couleur1.defCouleur(couleur_ref_segments);
                if (surligne_deux_points)
                {
                    couleur2.defCouleur(couleur_ref_segments);
                }
            }
            else
            {
//                couleur.defCouleur(VueModele->CouleurReferenceSommets());
                RestaureCouleur(couleur1);
                if (surligne_deux_points)
                {
                    RestaureCouleur(couleur2);
                }
            }
        }
    }

    if (ligne_selection)
    {
        if (centre_segment)
        {
            Perspective3D::Pvec3 centre(ligne_selection->V1().X() + ((ligne_selection->V2().X() - ligne_selection->V1().X()) * .5),
                                        ligne_selection->V1().Y() + ((ligne_selection->V2().Y() - ligne_selection->V1().Y()) * .5),
                                        ligne_selection->V1().Z() + ((ligne_selection->V2().Z() - ligne_selection->V1().Z()) * .5) );
            VueModele->CentreSur(centre, false, false);
        }
        if (active_proprietes)
        {
#ifdef MODE_FILAIRE_AUTO
            DefTypeAffichage(MODE_FILAIRE);
#endif // MODE_FILAIRE_AUTO
            Proprietes->widget().AfficheInfosLigne(SceneP3DConst()->Maillage().Filaire()[ligne_selection->Id()]);
        }
    }
    VueModele->MiseaJourModele(false, true, false);
}

void Interface3d::SelectionSurface_sig(int index, bool active_proprietes, bool anim_surface, bool reinit)
{
    SelectionSurface(index, active_proprietes, anim_surface, reinit);
}

void Interface3d::SelectionSurface(int index, bool active_proprietes, bool anim_surface, bool reinit)
{
    if (!AffichageDefinifPret() || (index < 0))
    {
        return;
    }
    const Perspective3D::PScene3D *scene_ptr = SceneP3DConst();
    if (!scene_ptr) { return; }

#ifdef DEBUG
    const bool selection_contour = false;
#else
    const bool selection_contour = false;
#endif // DEBUG

    const int id = index-1; /* Si id vaut -1, toutes les surfaces seront réinitialisés. */

    PGL::PVectCouleur_GL &couleurs_triangles = VueModele->CouleursTriangles();
    if (!couleurs_triangles.size())
    {
        return;
    }
#ifdef MODE_FILAIRE_AUTO
    DefTypeAffichage(PGL_MODE_AFFICHAGE_DEFAUT);
#endif // MODE_FILAIRE_AUTO

    const Perspective3D::PStdVectSurfaces3D &surfaces = scene_ptr->Maillage().Surfaces();
    const unsigned int nsurfaces = surfaces.size();
    if (!nsurfaces)
    {
        return;
    }

    PGL::PVectCouleur_GL &couleurs_lignes = VueModele->CouleursLignes();
    if (!couleurs_lignes.size())
    {
        return;
    }
    int pos_tri_id = -1;

    if (reinit) /* Réinitialisation des couleurs: */
    {
        for(unsigned int it=0, cpt_tri=0; it<nsurfaces; ++it)
        {
            const Perspective3D::PSurface3D &surface = surfaces[it];
            const Perspective3D::PStdVectTriangles3D &triangles = surface.TrianglesConst();

            if (surface.Actif() && surface.Id() == id)
            {
                pos_tri_id = cpt_tri;
            }

            for(unsigned int i=0; i<triangles.size(); ++i)
            {
                const unsigned int id_couleur = cpt_tri*3;

                if (id_couleur >= couleurs_triangles.size())
                {
                    break;
                }
                PGL::PCouleur_GL &c1 = couleurs_triangles[id_couleur];
                PGL::PCouleur_GL &c2 = couleurs_triangles[id_couleur+1];
                PGL::PCouleur_GL &c3 = couleurs_triangles[id_couleur+2];

//                std::cout << ": (avant)" << surface.CouleurConst().Actif() << " :: " << c1 << " : " << int(surface.CouleurConst().R()) << ":" << int(surface.CouleurConst().V()) << ":" << int(surface.CouleurConst().B()) << std::endl;

                if (surface.CouleurConst().Actif())
                {
                    c1.defCouleur(surface.CouleurConst());
                    c2.defCouleur(surface.CouleurConst());
                    c3.defCouleur(surface.CouleurConst());
                }
                else
                {
                    VueModele->SelectionCouleur(c1, surface.Norm());
                    VueModele->SelectionCouleur(c2, surface.Norm());
                    VueModele->SelectionCouleur(c3, surface.Norm());
                }

//                std::cout << ":   (après)" << surface.CouleurConst().Actif() << " :: " << couleurs_triangles[id_couleur] << std::endl;

                ++cpt_tri;
            }

            if (selection_contour)
            {
                // Restaure les couleurs par défaut des lignes:
                const Perspective3D::PStdVectSegments3D &contour_surface = surface.ContourConst();
                for(unsigned int i=0; i<contour_surface.size(); ++i)
                {
                    const Perspective3D::PSegment3D &l = contour_surface[i];
                    if (!l.IdNul())
                    {
                        int id_ligne = NormaliseId(l.Id(), Corbeille_Lignes) * 2;
                        PGL::PCouleur_GL &couleur_1 = couleurs_lignes[id_ligne];
                        PGL::PCouleur_GL &couleur_2 = couleurs_lignes[id_ligne+1];
                        couleur_1.defCouleur(VueModele->CouleurReferenceSegments());
                        couleur_2.defCouleur(VueModele->CouleurReferenceSegments());
                    }
                }
            }
        }
        if (pos_tri_id == -1)
        {
            VueModele->MiseaJourModele(false, false, true);
            return;
        }
    }
    else
    {
        for(unsigned int it=0, cpt_tri=0; it<nsurfaces; ++it)
        {
            const Perspective3D::PSurface3D &surface = surfaces[it];
            if(surface.Actif() && surface.Id() == id)
            {
                pos_tri_id = cpt_tri;
            }
            cpt_tri += surface.TrianglesConst().size();
        }
        if (pos_tri_id == -1)
        {
            VueModele->MiseaJourModele(false, false, true);
            return;
        }
    }

    /* Assigne la couleur sur la surface qui va bien: */
    if (id != -1)
    {
        const Perspective3D::PSurface3D &surface = SceneP3DConst()->Maillage().Surfaces()[id]; // Sélection de la surface.

        if (active_proprietes)
        {
            Proprietes->widget().AfficheInfosSurface(surface);
        }

//        if (centre_surface)
//        {
//            pfloat anglex, angley, anglez;
//            Perspective3D::AnglesVecteur3D(surface.Norm(), anglex, angley, anglez);
//            VueModele->CentreSur(surface.Centre(), true, true, anglex, angley, anglez);
//        }

        pos_tri_id -= NTrianglesCorbeilleSurfaces(id);

        // Surligne la couleur des triangles de la surface:
        const Perspective3D::PStdVectTriangles3D &triangles = surface.TrianglesConst();
        for(unsigned int i=0, cpt_tri=pos_tri_id; i<triangles.size(); ++i, ++cpt_tri)
        {
            const unsigned int id_couleur_tri = cpt_tri * 3; /* Trois couleurs, une pour chaque sommet. */
            if ((id_couleur_tri) >= couleurs_triangles.size())
            {
                break;
            }
//            qDebug() << "Surligne couleur surface : " << id_couleur_tri;
            SurligneCouleur(couleurs_triangles[id_couleur_tri]);
            SurligneCouleur(couleurs_triangles[id_couleur_tri+1]);
            SurligneCouleur(couleurs_triangles[id_couleur_tri+2]);
        }

        if (selection_contour)
        {
            const Perspective3D::PStdVectSegments3D &contour_surface = surface.ContourConst();

            for(unsigned int i=0; i<contour_surface.size(); ++i)
            {
                const Perspective3D::PSegment3D &l = contour_surface[i];
                if (!l.IdNul())
                {
                    int id_ligne = NormaliseId(l.Id(), Corbeille_Lignes) * 2;
                    SurligneCouleur(couleurs_lignes[id_ligne], 2);
                    SurligneCouleur(couleurs_lignes[id_ligne+1], 2);
                }
            }
        }

        if (anim_surface)
        {
#ifdef MODE_FILAIRE_AUTO
            DefTypeAffichage(MODE_FILAIRE);
#endif
            VueModele->AnimationContour(surface.ContourConst()); // Met en évidence le trajet du contour.
        }
    }
    VueModele->MiseaJourModele(false, true, true);
}

void Interface3d::SelectionSolide(int index)
{
    if (!AffichageDefinifPret() || (index < 0))
    {
        return;
    }
    if (!SceneP3DConst())
    {
        return;
    }
    const int id = index-1;

    if (id == -1)
    {
        SelectionSurface(0, false, false, true);
        return;
    }

//    PVectCouleur_GL &couleurs_triangles = VueModele->CouleursTriangles();

    const Perspective3D::PSolide3D &solide = SceneP3D()->Solides()[id];
//    solide.sort(CompareSortInt);

    SelectionSurface(0, false, false, true); // Réinitialise la sélection des surfaces:

    const int ns = solide.NombreSurfaces();
    for(int i=0; i<ns; ++i)
    {
        SelectionSurface(solide.Surfaces()[i]+1, false, false, false);
    }

    Proprietes->widget().AfficheInfosSolide(solide, id);
    VueModele->MiseaJourModele(false, false, true);
}

void Interface3d::ReinitAffichage()
{
    if (Perspective())
    {
        VueModele->ReinitPerspGL(Perspective());
    }
    else if (SceneP3DConst())
    {
        VueModele->ReinitPerspGL(SceneP3DConst());
    }
}

void Interface3d::TransfertModele()
/* Demande le transfert du modèle Perspective3D -> Perspective_GL. */
{
    if (VueModele->TermineInitialisation())
    {
        if (Perspective())
        {
            VueModele->TransfertModele(Perspective());
        }
        else if (SceneP3D())
        {
            VueModele->TransfertModele(SceneP3D());
        }
    }
}

void Interface3d::RegenProjections(Perspective3D::vues2D_t projs, Perspective3D::infos3d_t infos, int tailletxt_pct, bool regen)
{
    if (Perspective())
    {
        Perspective()->RegenProjections(projs, infos, tailletxt_pct);
        if (regen)
        {
            TransfertModele();
        }
    }
}

void Interface3d::RegenModele(bool regen_projections)
/* Re-genère l'ensemble du modèle: */
{
    if (VueModele->TermineInitialisation())
    {
        if (Perspective())
        {
            if (InstanceModele.PerspectiveConst()->FinInit())
            {
                if (regen_projections)
                {
                    InstanceModele.Perspective()->RegenProjections();
                }
                if (InstanceModele.PerspectiveConst()->FinGeneration())
                {
                    Proprietes->widget().AfficheProprietesScene();
                }
                TransfertModele();
            }
        }
        else if (SceneP3DConst())
        {
            TransfertModele();
        }
    }
}

bool Interface3d::SupprimeSurface(int id, bool regen, bool gere_vectsuppr)
/* Supprime la surface dont l'id est donné en argument. */
{
    if (id < 0)
    {
        return false;
    }
#ifdef DEBUG
    qDebug() << "SupprimeSurface : " << id;
#endif

    Perspective3D::PSurface3D &s = SceneP3D()->Surface(id);

    if (!s.Actif())
    {
        return false;
    }

    Corbeille_Surfaces.push_back(id);

    s.defActif(false);
    s.defTrianglesActif(false);

    if (gere_vectsuppr)
    {
        VectSuppr.push_back(ElementsSuppr_t(0, 0, 1, 0));
    }

    if (regen)
    {
        RegenModele();
    }
    return true;
}

void Interface3d::RestaureSurface(unsigned int n, bool regen)
/* Restaure les n dernières surfaces dans la corbeille */
{
//    qDebug() << "Restaure la surface: (anciennement, avant le nouvel index) " << Corbeille_Surfaces.back().Id();

    if (!Corbeille_Surfaces.size())
    {
        return;
    }
    if (!SceneP3DConst())
    {
        return;
    }

    for(unsigned int i=0; i<n; ++i)
    {
        const unsigned int id_restaure = Corbeille_Surfaces.back();

//        //if (i >= Corbeille_Surfaces.size())
//            //break;
        Perspective3D::PSurface3D &s = SceneP3D()->Surface(id_restaure);
#ifdef DEBUG
        qDebug() << "Restaure la surface: " << id_restaure;
#endif
        s.defActif(true);
        s.defTrianglesActif(true);
        Corbeille_Surfaces.pop_back();
    }

    if (regen)
    {
        RegenModele();
    }
}

bool Interface3d::SupprimePoint(int id, bool regen, bool gere_vectsuppr)
/* Supprime un point dont l'id est donné en argument. */
{
    if (id < 0)
    {
        return false;
    }

    const Perspective3D::PScene3D *scene_ptr = SceneP3DConst();
    if (!scene_ptr)
    {
        return false;
    }

    id+=NSommetsCorbeillePoints(id);
    if (id != IDNUL)
    {
        Perspective3D::PSommet3D &tr_pt = SceneP3D()->Sommet(id);
        if (!tr_pt.Actif())
        {
            return false;
        }

        const Perspective3D::PStdVectSegments3D &lignes = scene_ptr->Maillage().Filaire();
        const Perspective3D::PStdVectSurfaces3D &surfaces = scene_ptr->Maillage().Surfaces();

        Perspective3D::PStdVectInts vect_suppr_lignes;
        Perspective3D::PStdVectInts vect_suppr_surfaces;

        const unsigned int nl = lignes.size();
        for(unsigned int i=0; i<nl; ++i)
        {
            const Perspective3D::PSegment3D &l = lignes[i];
            if (l.Actif())
            {
                if (l.V1().Id() == id || l.V2().Id() == id)
                {
#ifdef DEBUG
                    qDebug() << "SupprimePoint :: Ligne à supprimer: " << l.V1().Id() << ":" << l.V2().Id() << " ( connexion au point" << id << ")";
#endif
                    vect_suppr_lignes.push_back(i);
                }
            }
        }

        for(unsigned int i=0; i<surfaces.size(); ++i)
        {
            const Perspective3D::PSurface3D &s = surfaces[i];
            if (s.Actif())
            {
                const unsigned int ns = s.ContourConst().size();
                for(unsigned int i2=0; i2<ns; ++i2)
                {
                    if (s.ContourConst()[i2].V1().Id() == id)
                    {
#ifdef DEBUG
                        qDebug() << "SupprimePoint :: Surface à supprimer: " << s.Id() << " ( connexion au point" << id << ")";
#endif
                        vect_suppr_surfaces.push_back(i);
                        break;
                    }
                }
            }
        }

//#ifdef DEBUG
//        qDebug() << "SupprimePoint(" << id << "): Point à supprimer :: " << id;
//        qDebug() << "SupprimePoint(" << id << "): Lignes à supprimer :: " << vect_suppr_lignes;
//        qDebug() << "SupprimePoint(" << id << "): Surfaces à supprimer :: " << vect_suppr_surfaces;
//#endif

        if (gere_vectsuppr)
        {
            VectSuppr.push_back(ElementsSuppr_t(1, vect_suppr_lignes.size(), vect_suppr_surfaces.size(), 0));
        }

        Corbeille_Points.push_back(id);

        tr_pt.defActif(false);

        TriStdVect(vect_suppr_lignes, CompareSortIntR);

        for(unsigned int i=0; i<vect_suppr_lignes.size(); ++i)
        {
            SupprimeLigne(vect_suppr_lignes[i], false, false);
        }

        TriStdVect(vect_suppr_surfaces, CompareSortIntR);

        for(unsigned int i=0; i<vect_suppr_surfaces.size(); ++i)
        {
            SupprimeSurface(vect_suppr_surfaces[i], false, false);
        }
        if (regen)
        {
            RegenModele();
        }
        return true;
    }
    return false;
}

void Interface3d::RestaurePoint(unsigned int n, bool regen)
/* Restaure les n derniers points supprimés. */
{
    if (!Corbeille_Points.size())
    {
        return;
    }
    if (!SceneP3DConst())
    {
        return;
    }
    for(unsigned int i=0; i<n; ++i)
    {
//        if (i >= Corbeille_Points.size())
//            { break; }
        const int id = Corbeille_Points.back();
#ifdef DEBUG
        qDebug() << "Restauration du point : " << SceneP3DConst()->Sommet(id).Id();
#endif

        Perspective3D::PSommet3D &t = SceneP3D()->Sommet(id);
        t.defActif(true);
        Corbeille_Points.pop_back();
    }
    if (regen)
    {
        RegenModele();
    }
}

bool Interface3d::SupprimeLigne(int id, bool regen, bool gere_vectsuppr)
/* Supprime une ligne dont l'id est donné en argument. */
{
    if (id < 0)
    {
        return false;
    }
    if (!SceneP3DConst())
    {
        return false;
    }
    Corbeille_Lignes.push_back(id);
    if (id != IDNUL)
    {
        Perspective3D::PSegment3D &t = SceneP3D()->Segment(id);

        if (!t.Actif())
        {
            return false;
        }

        t.defActif(false);
#ifdef DEBUG
        qDebug() << "SupprimeLigne() :: Ligne à supprimer: " << id;
#endif
        if (gere_vectsuppr)
            VectSuppr.push_back(ElementsSuppr_t(0, 1, 0, 0));

        if (regen)
        {
            RegenModele();
        }
        return false;
    }
    return false;
}

void Interface3d::RestaureLigne(unsigned int n, bool regen)
/* Restaure les n dernières lignes supprimés */
{
    if (!Corbeille_Lignes.size())
    {
        return;
    }
    if (!SceneP3DConst())
    {
        return;
    }

    for(unsigned int i=0; i<n; ++i)
    {
//        if (i >= Corbeille_Lignes.size())
//            { break; }
        const int id = Corbeille_Lignes.back();
#ifdef DEBUG
        qDebug() << "Restauration de la ligne: " << SceneP3DConst()->Segment(id).Id();
#endif

        Perspective3D::PSegment3D &t = SceneP3D()->Segment(id);
        t.defActif(true);
        Corbeille_Lignes.pop_back();
    }
    if (regen)
    {
        RegenModele();
    }
}

bool Interface3d::SupprimeSolide(int id, bool regen, bool gere_vectsuppr)
{
    if (id < 0)
    {
        return false;
    }
    if (!SceneP3DConst())
    {
        return false;
    }

    const Perspective3D::PScene3D *scene_ptr = SceneP3DConst();
    if (!scene_ptr)
    {
        return false;
    }

//    id+=NSurfacesCorbeilleSolides(id);


    if (id != IDNUL)
    {
        if (size_t(id) >= SceneP3D()->Solides().size())
        {
            return false;
        }

        Perspective3D::PSolide3D &tr_s = SceneP3D()->Solide(id);
        if (!tr_s.Actif())
        {
            return false;
        }

        const Perspective3D::PStdVectSurfaces3D &surfaces = scene_ptr->Maillage().Surfaces();
        Perspective3D::PStdVectInts vect_suppr_surfaces;

        const Perspective3D::PStdSolide3D &ids_surfaces = tr_s.Surfaces();

        for(unsigned int i=0; i<ids_surfaces.size(); ++i)
        {
            const Perspective3D::PSurface3D &s = surfaces[ids_surfaces[i]];
            if (s.Actif())
            {
#ifdef DEBUG
                qDebug() << "SupprimeSolide (" << id <<  ") :: Surface à supprimer: " << s.Id();
#endif
                vect_suppr_surfaces.push_back(ids_surfaces[i]);
            }
        }

        if (gere_vectsuppr)
        {
            VectSuppr.push_back(ElementsSuppr_t(0, 0, vect_suppr_surfaces.size(), 1));
        }
        Corbeille_Solides.push_back(id);
        tr_s.defActif(false);

        TriStdVect(vect_suppr_surfaces, CompareSortIntR);

        for(unsigned int i=0; i<vect_suppr_surfaces.size(); ++i)
        {
            SupprimeSurface(vect_suppr_surfaces[i], false, false);
        }
        if (regen)
        {
            RegenModele();
        }
        return true;
    }
    return false;
}

void Interface3d::RestaureSolide(unsigned int n, bool regen)
{
    if (!Corbeille_Solides.size())
    {
        return;
    }

    if (!SceneP3DConst())
    {
        return;
    }

    for(unsigned int i=0; i<n; ++i)
    {
//        if (i >= Corbeille_Points.size())
//            { break; }
        const int id = Corbeille_Solides.back();
#ifdef DEBUG
        qDebug() << "Restauration du solide: " << id;
#endif

        Perspective3D::PSolide3D &t = SceneP3D()->Solide(id);
        t.defActif(true);
        Corbeille_Solides.pop_back();
    }
    if (regen)
    {
        RegenModele();
    }
}

void Interface3d::Restaure()
/* Annule la suppression des derniers éléments. */
{
    if (VectSuppr.size() > 0)
    {
        ElementsSuppr_t &el = VectSuppr.back();
        if (el.nSommets) { RestaurePoint(el.nSommets, true); }
        if (el.nSegments) { RestaureLigne(el.nSegments, true); }
        if (el.nSurfaces) { RestaureSurface(el.nSurfaces, true); }
        if (el.nSolides) { RestaureSolide(el.nSolides, true); }
        VectSuppr.pop_back();
    }
}

void Interface3d::showEvent(QShowEvent *event)
{
    VueModele->setFocus();
    event->accept();
}

void Interface3d::closeEvent(QCloseEvent *ev)
{
    GereProprietes(true);

    while(true) /* Attend la fin d'une éventuelle animation en cours avant de fermer l'onglet. */
    {
        if (animation.Anime())
        {
            SommeilTempoQt(25);
        }
        else
        {
            break;
        }
    }

    ev->accept();
}

void Interface3d::InitTimer()
{
    if (!InstanceModele.PerspectiveConst() && !SceneP3DConst())
    {
        return;
    }

//    VueModele->xSuppr();
    if (animelogo)
    {
        animelogo->show();
    }
    Proprietes->widget().Nettoyage();

    if (id_timer != -1)
    {
        killTimer(id_timer);
        id_timer = -1;
    }
//    id_timer = startTimer(40);
    id_timer = startTimer(100); // Une cadence rapide est plus fluide mais diminue la vitesse du génération du fait des mutexes
}

void Interface3d::ControleErreursPerspective()
/* Contrôle le résultat de la génération et avertir l'utilisateur si il y a un truc qui s'est mal passé
    (doit en principe être appelé après la génération de la scène dabns Perspective). */
{
    if (!InstanceModele.PerspectiveConst())
    {
        return;
    }

    const Perspective3D::resultat_gen3d_t errs = InstanceModele.PerspectiveConst()->ResultatGeneration();

    unsigned int cpt_errs = 0;

    if (errs == Perspective3D::resultat_gen3d_t::PGEN_NUL)
    {
        Notifications->AfficheNotification(tr("Rien n'a été généré..."), IconeNotifications::ICONE_NOTIFICATION_ATTENTION, false, this);
    }
    else if (errs & Perspective3D::resultat_gen3d_t::PGEN_OK) /* Rien à signaler mis à part que tout est ok... */
    {
        if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERREUR_LICENCE)
        {
            Notifications->AfficheNotification(tr("La génération 3D est incomplète car aucune clé de licence valide n'a été fournie."), IconeNotifications::ICONE_NOTIFICATION_ATTENTION, true, this);
        }
        else if (errs & Perspective3D::resultat_gen3d_t::PGEN_VERROU_LICENCE)
        {
            Notifications->AfficheNotification(tr("Le modèle a été généré normalement, même si aucune clé de licence valide n'a été fournie."), IconeNotifications::ICONE_NOTIFICATION_ATTENTION, true, this);
        }
        else
        {
            Notifications->AfficheNotification(tr("Modèle généré avec succès"), IconeNotifications::ICONE_NOTIFICATION_VALIDE, true, this);
        }
        return;
    }

    QString mesg;

    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_ALIGNEMENT_VUES)
    {
        ++cpt_errs;
        if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_ALIGNEMENT_VUES_FACE_COTE)
        {
            mesg += QString("- ") + tr("Les vues de face et de côté ne sont pas alignées (vous pouvez définir manuellement les origines pour y remédier).") + "\n\n";
        }
        else if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_ALIGNEMENT_VUES_FACE_HAUT)
        {
            mesg += QString("- ") + tr("Les vues de face et de dessus ne sont pas alignées (vous pouvez définir manuellement les origines pour y remédier).") + "\n\n";
        }
        else if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_ALIGNEMENT_VUES_COTE_HAUT)
        {
            mesg += QString("- ") + tr("Les vues de côté et de dessus ne sont pas alignées (vous pouvez définir manuellement les origines pour y remédier).") + "\n\n";
        }
        else
        {
            mesg += QString("- ") + tr("Les vues ne sont pas alignées (vous pouvez définir manuellement les origines pour y remédier).") + "\n\n";
        }
    }
    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_CORRESPONDANCE_VUES)
    {
        ++cpt_errs;
        if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_CORRESPONDANCE_VUES_FACE_COTE)
        {
            mesg += QString("- ") + tr("La dimension des vues de face et de côté ne correspondent pas (Y).") + "\n\n";
        }
        else if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_CORRESPONDANCE_VUES_FACE_HAUT)
        {
            mesg += QString("- ") + tr("La dimension des vues de face et de dessus ne correspondent pas (X).") + "\n\n";
        }
        else if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_CORRESPONDANCE_VUES_COTE_HAUT)
        {
            mesg += QString("- ") + tr("La dimension des vues de côté et de dessus ne correspondent pas (Y/X).") + "\n\n";
        }
        else
        {
            mesg += QString("- ") + tr("La dimension des vues ne correspondent pas (par exemple entre la hauteur de la vue de dessus et la largeur de la vue de côté).") + "\n\n";
        }
    }

    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_VUEFACE_NULLE)
    {
        ++cpt_errs;
        mesg += QString("- ") + tr("La vue de face est vide.") + "\n\n";
    }
    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_VUECOTE_NULLE)
    {
        ++cpt_errs;
        mesg += QString("- ") + tr("La vue de côté est vide.") + "\n\n";
    }
    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_VUEHAUT_NULLE)
    {
        ++cpt_errs;
        mesg += QString("- ") + tr("La vue de dessus est vide.") + "\n\n";
    }
    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_VUEEXTRUSION_NULLE)
    {
        ++cpt_errs;
        mesg += QString("- ") + tr("La vue de face (pour l'extrusion) est vide.") + "\n\n";
    }
    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_VUEREVOLUTION_NULLE)
    {
        ++cpt_errs;
        mesg += QString("- ") + tr("La vue de profil (pour la révolution) est vide.") + "\n\n";
    }

    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_CONVERSION3D)
    {
        ++cpt_errs;
        mesg += QString("- ") + tr("La conversion 2D -> 3D a échoué.") + "\n\n";
    }

    if (errs & Perspective3D::resultat_gen3d_t::PGEN_ERR_GENERATION3D)
    {
        ++cpt_errs;
        mesg += QString("- ") + tr("La génération 3D a échoué (par exemple la génération des surfaces).") + "\n\n";
    }

    if (cpt_errs > 0)
    {
        if (cpt_errs > 1)
        {
            mesg = tr("Il semble que la génération de la scène ait rencontré %1 problèmes:").arg(cpt_errs) + "\n\n" + mesg;
        }
        else
        {
            mesg = tr("Il semble que la génération de la scène ait rencontré un problème:") + "\n\n" + mesg;
        }
        mesg += "\n" + tr("Vous pouvez consulter le journal de génération pour plus d'informations et afficher les projections des vues pour contrôler si elles correspondent.");

//        QMessageBox mesgbox(QMessageBox::Information, tr("Résultat de génération"), mesg, QMessageBox::Ok, this);
//        mesgbox.exec();
        Notifications->AfficheNotification(mesg, IconeNotifications::ICONE_NOTIFICATION_ERREUR, false, this);
    }
    else // Impossible en principe.
    {
        mesg = tr("Le modèle semble avoir rencontré un problème inconnu lors de la génération.");
        Notifications->AfficheNotification(mesg, IconeNotifications::ICONE_NOTIFICATION_ATTENTION, false, this);
    }
}

void Interface3d::ControleErreursImport()
/* Contrôle la validité de la génération de la scène 3D (via import). */
{
    if (!InstanceModele.SceneP3DConst())
    {
        return;
    }
    if (InstanceModele.ValideGeneration())
    {
        return;
    }

    QMessageBox mesgbox(QMessageBox::Information, tr("Erreur"), tr("Une erreur est survenue lors de l'import de la scène."), QMessageBox::Ok, this);
    mesgbox.exec();
}

#ifdef ACTIVE_QPAINTER_POST_VUE3D
void Interface3d::PaintPostGL()
/* Appelé (via connexion à la vue 3D) juste après l'affichage de la scène, ça permet d'ajouter des éléments par dessus l'affichage 3D. */
{
//    QPainter &painter = VueModele->PainterWGL(); // Récupère le painter de l'affichage 3D.
}
#endif // ACTIVE_QPAINTER_POST_VUE3D

void Interface3d::timerEvent(QTimerEvent *ev)
{
    /* Si un nouveau message est apparu dans le journal de Perspective3D, on l'affiche */
    if (InstanceModele.PerspectiveConst() && !InstanceModele.PerspectiveConst()->JournalConst().StrConst().empty())
    {
        Proprietes->widget().defTexteConsole(QString::fromStdString(InstanceModele.PerspectiveConst()->JournalConst().StrConst()));
//        InstanceModele.PerspectiveConst()->Journal().Suppr();
    }

    if (InstanceModele.PerspectiveConst() && VueModele->TermineInitialisation())
    {
        if (InstanceModele.FinGeneration() && !InstanceModele.ThreadEnCours()) /* Génération terminée... */
        {
            animelogo->AffichePasAPas(100, tr("Terminé"));
            if (InstanceModele.PerspectiveConst()->ModelePret())
            {
                emit FinGeneration(IdOnglet);

                Proprietes->widget().AfficheProprietesScene();

                if (affiche_proprietes3d_init)
                {
                    OuvreProprietes(); // Affiche effectivement les propriétés.
                }

                TransfertModele(); /* Demande l'affichage du modèle. */
                GereEtatBoutonsOutils3D();

                InstanceModele.Perspective()->Scene().GenereArbresRecherche(); /* Genère les arbres de recherche. Permettra d'optimiser la vitesse de sélection des objets dans l'affichage 3D. */

                killTimer(ev->timerId());
                id_timer = -1;
            }
            else // Erreur lors de la génération...
            {
                if (!InstanceModele.PerspectiveConst()->ContinueGeneration())
                {
                    Proprietes->widget().AjoutConsole(tr("Arret"));
                }
                else
                {
                    Proprietes->widget().AjoutConsole(tr("Erreur: le fil d'exécution de Perspective 3D a retourné une erreur..."));
                }

                TransfertModele(); /* Demande l'affichage de ce qui a été généré. */
                GereEtatBoutonsOutils3D();

                animelogo->AffichePasAPas(100, tr("Erreur"));
                killTimer(ev->timerId());
                id_timer = -1;
            }
            ControleErreursPerspective();
        }
        else if (InstanceModele.ThreadEnCours()) /* Génération en cours... */
        {
            animelogo->AffichePasAPas(InstanceModele.Avancement(), QString::fromStdString(InstanceModele.PerspectiveConst()->JournalConst().DerniereEntreeConst()));
            RegenModele(false);
        }
    }
    else if (InstanceModele.SceneP3D() && VueModele->TermineInitialisation())
    {
        if (InstanceModele.FinGeneration() && !InstanceModele.ThreadEnCours()) /* Génération terminée... */
        {
            animelogo->AffichePasAPas(100, tr("Terminé"));

            Proprietes->widget().defTexteConsole(QString(""));
            emit FinGeneration(IdOnglet);
            Proprietes->widget().AfficheProprietesScene();

            if (affiche_proprietes3d_init)
            {
                OuvreProprietes(); // Affiche effectivement les propriétés.
            }

            TransfertModele(); /* Demande l'affichage du modèle. */
            GereEtatBoutonsOutils3D();

            InstanceModele.SceneP3D()->GenereArbresRecherche(); /* Genère les arbres de recherche. Permettra d'optimiser la vitesse de sélection des objets dans l'affichage 3D. */
            killTimer(ev->timerId());
            id_timer = -1;
            ControleErreursImport();
        }
        else
        {
            animelogo->AffichePasAPas(InstanceModele.Avancement(), tr("Import..."));
            RegenModele(false);
        }
    }
    ev->accept();
}
