/**
© 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 <QRect>
#include <QLabel>
#include <QMimeData>
#include <QShortcut>
#include <QScrollBar>
#include <QFont>
#include <QLineF>
#include <QPointF>
#include <QString>
#include <QGraphicsTextItem>
#include <QGraphicsEllipseItem>
#include <QGraphicsRectItem>
#include <QPainter>
#include <QGraphicsScene>
#include <QPen>
#include <QGraphicsItemGroup>

//#define FORCE_OPENGL_SCENE2D

#ifdef FORCE_OPENGL_SCENE2D
#include <QOpenGLWidget>
#endif // FORCE_OPENGL_SCENE2D

#include "Conversion.h"

#include "vue2d.h"

#ifdef DEBUG
    #include <QDebug>
#endif // DEBUG

double DistancePoints2(double x1, double y1, double x2, double y2)
/* Renvoi la distance au carré entre deux points 2D */
{
    const double diffx = x2 - x1;
    const double diffy = y2 - y1;
    return (diffx*diffx + diffy*diffy);
}


double DistancePoints(double x1, double y1, double x2, double y2)
/* Renvoi la distance entre deux points 2D */
{
    return sqrt(DistancePoints2(x1, x2, y1, y2));
}

class QGraphicsGroupItem : public QGraphicsItemGroup
{
    public:
        QGraphicsGroupItem(const QPointF &origine, QGraphicsItem *parent = Q_NULLPTR) : QGraphicsItemGroup(parent), Origine(origine)
        {
//            removeFromIndex();
        }

        ~QGraphicsGroupItem()
        {
#ifdef DEBUG
            qDebug() << "      QGraphicsGroupItem :: Suppression du groupe de la scène " << scene();
#endif // DEBUG
        }

        QRectF boundingRect() const
        {
            return QGraphicsItemGroup::boundingRect();
        }

        QVariant itemChange(GraphicsItemChange change, const QVariant &value)
        {
#ifdef DEBUG
            if (change == ItemSceneChange)
            {
                qDebug() << "      QGraphicsGroupItem::itemChange() : Changement de scene d'un groupe :" << scene() << ":" << value.data_ptr().data.ptr;
            }
            else if (change == ItemSceneHasChanged)
            {
                qDebug() << "      QGraphicsGroupItem::itemChange() : Changement de scene effectué :" << scene() << ":" << value.data_ptr().data.ptr;
            }
            else if (change == ItemParentChange)
            {
                qDebug() << "      QGraphicsGroupItem::itemChange() : Changement de parent d'un groupe :" << parentItem() << ":" << value.data_ptr().data.ptr;
            }
            else if (change == ItemParentHasChanged)
            {
                qDebug() << "      QGraphicsGroupItem::itemChange() : Changement de parent effectué :" << parentItem() << ":" << value.data_ptr().data.ptr;
            }
            else if (change == ItemChildAddedChange)
            {
//                qDebug() << "      QGraphicsGroupItem::itemChange() : Ajout d'un enfant :" << value.data_ptr().data.ptr;
            }
            else if (change == ItemChildRemovedChange)
            {
//                qDebug() << "      QGraphicsGroupItem::itemChange() : Suppression d'un enfant :" << value.data_ptr().data.ptr;
            }

//            return value;
#endif // DEBUG
            return QGraphicsItemGroup::itemChange(change, value);
        }

        int type() const
        {
            return QGraphicsItemGroup::Type;
        }

        void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget=Q_NULLPTR)
        {
//            painter->setPen(QColor(230, 80, 80));
//            const qreal taille_croix = 10;
//            painter->drawLine(QPointF(Origine.x()-taille_croix, Origine.y()), QPointF(Origine.x()+taille_croix, Origine.y()));
//            painter->drawLine(QPointF(Origine.x(), Origine.y()-taille_croix), QPointF(Origine.x(), Origine.y()+taille_croix));

            QGraphicsItemGroup::paint(painter, option, widget);
        }

        int CompteurEntites() const
        /* Renvoi le nombre d'entités dans le groupe. */
        {
//            return childItems().size();

            int compteur = 0;
            const QList<QGraphicsItem *> &ch = childItems();

            for (QList<QGraphicsItem *>::const_iterator i = ch.constBegin(); i != ch.constEnd(); ++i)
            {
                const QGraphicsItem *ptr = *i;
                if (ptr->type() == QGraphicsItemGroup::Type)
                {
                    compteur += qgraphicsitem_cast<const QGraphicsGroupItem *>(ptr)->CompteurEntites();
                }
                else if (ptr->type() == QGraphicsPixmapItem::Type)
                {
                    ;
                }
                else
                {
                    ++compteur;
                }
            }
            return compteur;
        }

    protected:
        QPointF Origine;
};

class QGraphicsArcEllipseItem : public QGraphicsEllipseItem // Arc Elliptique sans line joignant le centre aux extrémitées (pas d'appel à drawPie()).
{
    public:
        QGraphicsArcEllipseItem(QGraphicsItem *parent) : QGraphicsEllipseItem(parent) { ; }

        QGraphicsArcEllipseItem (qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0 ) : QGraphicsEllipseItem(x, y, w, h, parent) { ; }

protected:
        QRectF boundingRect() const
        {
            return QGraphicsEllipseItem::boundingRect();
        }

        void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
        {
            painter->setPen(pen());
            painter->setBrush(brush());
            painter->drawArc(rect(), startAngle(), spanAngle());
//            painter->drawPie(rect(), startAngle(), spanAngle());

//            if (option->state & QStyle::State_Selected)
//                qt_graphicsItem_highlightSelected(this, painter, option);
        }
};

QRectF AdapteCadreAnimation(const QRectF &cadre)
/* Adapte un cadre quelconque pour l'adapter aux fonctions d'animation (il ne doit pas être négatif en largeur, ni en hauteur
    (en sachant que dans ce dernier cas on travaille en coordonnées inverse). */
{
    QRectF copie_cadre_vue = cadre;

    if (cadre.height() > 0)
    {
        copie_cadre_vue.setY(copie_cadre_vue.y()+cadre.height());
        copie_cadre_vue.setHeight(-cadre.height());
    }

    if (cadre.width() < 0)
    {
        copie_cadre_vue.setX(copie_cadre_vue.x()+cadre.width());
        copie_cadre_vue.setWidth(-cadre.width());
    }
    return copie_cadre_vue;
}

Vue2D::Vue2D(const AnimationQt &anim, Scene2D *scene, QWidget *parent) : QGraphicsView(parent), animation(anim), id_timer(-1), n_boucle_anim_zoom(0), val_zoom(0.)
{
    setScene(scene);
    setContentsMargins(0, 0, 0, 0);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setAutoFillBackground(true);
    setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing, true);
    setDragMode(QGraphicsView::ScrollHandDrag);
    setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
    setAcceptDrops(true);

    setStyleSheet("QGraphicsView { border-style: none; }");

#ifdef FORCE_OPENGL_SCENE2D /* Utilise OpenGL */
    QOpenGLWidget* vp = new QOpenGLWidget(this, Qt::Widget);
    QSurfaceFormat format_vp = vp->format();
    format_vp.setRenderableType(QSurfaceFormat::OpenGL);
    format_vp.setSwapBehavior(QSurfaceFormat::SingleBuffer);
    format_vp.setSamples(0);
    vp->setFormat(format_vp);
    setViewport(vp);
    setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
#endif // FORCE_OPENGL_SCENE2D

//    ChargeStyleWidget(*this, 4);

    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Up), this, SLOT(Zoom_p())));
    ListeRaccourcis.push_back(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Down), this, SLOT(Zoom_m())));
}

Vue2D::~Vue2D()
{
    for(int i=0; i<ListeRaccourcis.size(); ++i)
    {
        delete ListeRaccourcis[i];
    }
}

const Scene2D *Vue2D::Scene2DPtr() const
{
    return dynamic_cast<Scene2D*>(scene());
}

#ifdef SUPPORT_VISION2D
bool Vue2D::ModeEdition() const
/* Contrôle si la scène 2D est en mode édition. */
{
    const Scene2D *scene2d = Scene2DPtr();
    if (!scene2d)
    {
        return false;
    }
    return scene2d->ModeEdition();
}
#endif // SUPPORT_VISION2D

void Vue2D::Zoom_p()
{
    Zoom(120);
}

void Vue2D::Zoom_m()
{
    Zoom(-120);
}

void Vue2D::wheelEvent(QWheelEvent *event)
/* Quand on touche à la molette de la souris... */
{
    if (animation.Anime()) /* Ignore les évènements souris si une animation est en cours... */
    {
        event->accept();
        return;
    }

    QGraphicsView::wheelEvent(event);
#ifdef SUPPORT_VISION2D
    if (!ModeEdition())
    {
        Zoom(event->delta());
    }
#else
    Zoom(event->delta());
#endif // SUPPORT_VISION2D
    event->accept();
}

void Vue2D::timerEvent(QTimerEvent *ev)
{
    if (n_boucle_anim_zoom > 0)
    {
        scale(val_zoom, val_zoom);
        --n_boucle_anim_zoom;
    }
    else // Plus besoin d'animation
    {
        killTimer(ev->timerId());
        id_timer = -1;
    }
    ev->accept();
}

void Vue2D::Zoom(int delta_i)
{
//    qreal delta = pow(1.9, delta_i / 240.);
//    scale(delta, delta);

    // Nombre d'itérations pour effectuer un zoom (plus la valeur est grande, plus l'effet de zoom est adouci, tout en étant plus lent).
    static const unsigned int Nb_Iterations_Zoom = 2;
    const float facteur = 96. / Nb_Iterations_Zoom;

    val_zoom = pow(1.1, delta_i / (facteur)); // 160
    n_boucle_anim_zoom = Nb_Iterations_Zoom; // 160 pour 3 ou 5 itération ?
    if (id_timer == -1)
    {
        id_timer = startTimer(5);
    }
}

void Vue2D::AffichageGlobalVue()
{
    fitInView(scene()->itemsBoundingRect(), Qt::KeepAspectRatio);
}

void Vue2D::EchelleAnime(qreal e)
{
    if (!animation.Anime())
    {
        return;
    }
    setTransformationAnchor(QGraphicsView::AnchorViewCenter);
    qreal d = sqrt(sqrt(sqrt(e)));
    for(unsigned int i=0; i<8; ++i) // Nombre de boucles = 2**n récursions de sqrt sur le facteur d'echelle.
    {
        scale(d, d);
        if (!animation.Sommeil(animation.TempoReference()/4))
        {
            return;
        }
    }
    setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
}

//QPoint Vue2D::toLocalPos(const QPoint &pos)
//{
//    QTransform m = transform();
//    return m.map(pos);
//}

bool Vue2D::CentreSurAnime(const QPointF &pos)
/* Basé sur centerOn() */
{
    if (!animation.Anime())
        return false;

//    ReinitMatriceAffichage();
//    centerOn(QPointF(0.,0.));

    qreal width = viewport()->width();
    qreal height = viewport()->height();

    QTransform m = transform();
    QPointF viewPoint = m.map(pos);

    /* Coordonnées à atteindre */
    const qreal sc_w = int(viewPoint.x() - (width *.5));
    const qreal sc_h = int(viewPoint.y() - (height *.5));

    const unsigned int pas = 24;
    const int h = horizontalScrollBar()->value();
    const int v = verticalScrollBar()->value();
    const qreal inc_sc_w = ((sc_w-h) / (qreal) pas);
    const qreal inc_sc_h = ((sc_h-v) / (qreal) pas);

    qreal sh = h;
    qreal sv = v;

    for(unsigned int i=0; i<=pas; ++i)
    {
        horizontalScrollBar()->setValue(sh);
        verticalScrollBar()->setValue(sv);
        sh += (int) inc_sc_w;
        sv += (int) inc_sc_h;
        if (!animation.Sommeil(animation.TempoReference()/6))
            return false;
    }
    horizontalScrollBar()->setValue(sc_w);
    verticalScrollBar()->setValue(sc_h);
    return true;
}

void Vue2D::ReinitMatriceAffichage()
{
    setTransform(QTransform());
}

void Vue2D::ReinitEchelle()
{
    qreal ratio = 1.0 / EchelleActuelle();
    scale(ratio, ratio);
}

void Vue2D::ReinitEchelleAnime()
/* Restaure le facteur d'échelle (zoome) initial. Version animé. */
{
    qreal ratio = 1.0 / EchelleActuelle();
    EchelleAnime(ratio);
}

qreal Vue2D::EchelleActuelle()
{
    QTransform m = transform();
    return (m.m11()+m.m22()) * .5;
}

bool Vue2D::CadrageAnim(const QRectF &rect)
/* Basé sur fitInView() */
{
    if (rect.isNull())
    {
        return false;
    }

    QRectF copie_rect = AdapteCadreAnimation(rect);

    const int marge = 2;
    QRectF viewRect = viewport()->rect().adjusted(marge, marge, -marge, -marge);

    if (viewRect.isEmpty())
    {
        return false;
    }

    QRect viewport_rect(0, 0, viewport()->width(), viewport()->height());
    QRectF visible_scene_rect = mapToScene(viewport_rect).boundingRect();

    /* Curseur au centre de l'affichage pour que le zoom garde l'alignement */
    animation.CurseurPoint(viewport()->mapToGlobal(viewRect.center().toPoint()), EcranWidget(this));

    if (!visible_scene_rect.contains(copie_rect.center()))
    {
        EchelleAnime(0.5);
    }

    CentreSurAnime(copie_rect.center());

//    ReinitEchelleAnime();
//    ReinitMatriceAffichage();

    QTransform m = transform();
    QRectF sceneRect = m.mapRect(copie_rect);

//    if (sceneRect.isEmpty())
//    {
//        return false;
//    }

    qreal ratio = qMin(POSITIFD(viewRect.width()) / POSITIFD(sceneRect.width()), POSITIFD(viewRect.height()) / POSITIFD(sceneRect.height()));

    ratio *= .5; // .5;
    EchelleAnime(ratio);

    centerOn(copie_rect.center()); /* On assure le coup si le zoom a créé un décalage */
    return true;
}

void Vue2D::focusInEvent(QFocusEvent *event)
{
    QGraphicsView::focusInEvent(event);
    event->accept();
}

void Vue2D::focusOutEvent(QFocusEvent *event)
{
    QGraphicsView::focusOutEvent(event);
    event->accept();
}

void Vue2D::dragEnterEvent(QDragEnterEvent *event)
{
    const QMimeData *d = event->mimeData();
    if (d->hasUrls()) // && (d->hasFormat("text/z6s") || d->hasFormat("image/vnd.dxf"))
    {
        Fichier_Insert = d->urls().front().toLocalFile();
        event->acceptProposedAction();
    }
    else
    {
        Fichier_Insert.clear();
    }
//    event->accept();
}

//void Vue2D::dropEvent(QDropEvent *event)
//{
//    qDebug() << "dropEvent !";
////    if (!Fichier_Insert.isNull())
////    {
////        emit InsertFichier(Fichier_Insert);
////        Fichier_Insert.clear();
////    }
//    event->acceptProposedAction();
//}

void Vue2D::dragLeaveEvent(QDragLeaveEvent *event)
{
    if (!Fichier_Insert.isNull())
    {
        emit InsertFichier(Fichier_Insert);
        Fichier_Insert.clear();
    }
    event->accept();
}

void Vue2D::mouseDoubleClickEvent(QMouseEvent *event)
{
#if QT_VERSION < QT_VERSION_CHECK(5,4,0)
    if (event->button() & Qt::MidButton) /* Sera supprimé à partir de Qt6 */
#else
    if (event->button() & Qt::MiddleButton)
#endif // QT_VERSION
    {
        /* Double clic de la souris avec le bouton du milieu, centrage façon Autocad */
        AffichageGlobalVue();
    }
    else
    {
        QGraphicsView::mouseDoubleClickEvent(event);
    }
//    event->accept();
}

void Vue2D::mouseMoveEvent(QMouseEvent *event)
{
    QGraphicsView::mouseMoveEvent(event); /* On propage au parent pour gérer la navigation à la souris. */;
    event->accept();
}

void Vue2D::mousePressEvent(QMouseEvent *event)
{
    /* Permutte les boutons gauche/milieu (pour que la navigation latérale se fasse avec le clic du milieu). */
#if QT_VERSION < QT_VERSION_CHECK(5,4,0)
    if (event->button() == Qt::MidButton) /* Sera supprimé à partir de Qt6 */
#else
    if (event->button() == Qt::MiddleButton)
#endif // QT_VERSION
    {
        QMouseEvent ev_f(event->type(), event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers());
        QGraphicsView::mousePressEvent(&ev_f);
    }
    else if (event->button() == Qt::LeftButton)
    {
        QMouseEvent ev_f(event->type(), event->pos(), Qt::MiddleButton, Qt::MiddleButton, event->modifiers());
        QGraphicsView::mousePressEvent(&ev_f);
    }
    else
    {
        QGraphicsView::mousePressEvent(event);
    }

//    QGraphicsView::mousePressEvent(event);
    event->accept();
}

void Vue2D::resizeEvent(QResizeEvent *event)
{
    event->accept();
}

void Vue2D::mouseReleaseEvent(QMouseEvent *event)
{
    /* Permutte les boutons gauche/milieu (pour que la navigation latérale se fasse avec le clic du milieu). */
#if QT_VERSION < QT_VERSION_CHECK(5,4,0)
    if (event->button() == Qt::MidButton) /* Sera supprimé à partir de Qt6 */
#else
    if (event->button() == Qt::MiddleButton)
#endif // QT_VERSION
    {
       QMouseEvent ev_f(event->type(), event->pos(), Qt::LeftButton, Qt::LeftButton, event->modifiers());
       QGraphicsView::mouseReleaseEvent(&ev_f);
    }
    else if (event->button() == Qt::LeftButton)
    {
        QMouseEvent ev_f(event->type(), event->pos(), Qt::MiddleButton, Qt::MiddleButton, event->modifiers());
        QGraphicsView::mouseReleaseEvent(&ev_f);
    }
    else
    {
        QGraphicsView::mouseReleaseEvent(event);
    }
//    QGraphicsView::mouseReleaseEvent(event);
}

Scene2D::Scene2D(const AnimationQt &anim, QRgb couleur_fond, QWidget *parent) : QGraphicsScene(parent), animation(anim), MenuContextuel(false)
{
#ifdef SUPPORT_VISION2D
    mode_edition = ModeEdition2D::NUL;
    init_ligne = false;
    lignes_affichage_edition = 0;
    couleur_trace = QColor(255, 255, 255);
    taille_grille = 0.0;
#endif // SUPPORT_VISION2D

    idVue = Perspective3D::vues2D_t::VUENUL;
    etatRect = 0;
    etatOrigine = false;
    rectFace = rectCote = rectHaut = rectTripleVues = rectCourant = 0;
    EllipseorigineFace = EllipseorigineCote = EllipseorigineHaut = EllipseorigineCourante = 0;
    axeDessin = 0;
    AccrochageObjets = false;

//    pos_clic.setX(0.);
//    pos_clic.setY(0.);

    ChangeCouleurFond(couleur_fond); // Couleur de fond

//    setItemIndexMethod(QGraphicsScene::NoIndex);
    setItemIndexMethod(QGraphicsScene::BspTreeIndex);
}

Scene2D::~Scene2D()
{
    ;
}

void Scene2D::ChangeCouleurFond(QRgb couleur_fond)
{
    if (backgroundBrush().color().rgba() != couleur_fond)
    {
        setBackgroundBrush(QBrush(QColor::fromRgb(couleur_fond), Qt::SolidPattern));
    }
}

QGraphicsGroupItem *Scene2D::NouveauGroupeEnts()
{
    QGraphicsGroupItem *groupe_q = new QGraphicsGroupItem(QPointF(0,0));
    groupe_q->setData(Qt::UserRole, QVariant(-1));
    addItem(groupe_q);
    GroupesEntites.push_back(groupe_q);

//    qDebug() << "Nouveau groupe de scène : " << GroupesEntites.size();

    return groupe_q;
}

QGraphicsGroupItem *Scene2D::DernierGroupeEnts()
{
    if (GroupesEntites.size())
    {
        return GroupesEntites.back();
    }
    return NouveauGroupeEnts();
}

void Scene2D::AjoutEntLigne(const Perspective3D::Pligne2D &ligne, int id_entite, int id_groupe)
{
    QGraphicsGroupItem *groupe_q = DernierGroupeEnts();

    QGraphicsLineItem *ent = new QGraphicsLineItem(groupe_q);
    ent->setLine(ligne.P1Const().X(), -(ligne.P1Const().Y()), ligne.P2Const().X(), -(ligne.P2Const().Y()));
    ent->setPen(Stylo_trait(ligne.Cache(), ligne.CouleurConst()));
    ent->setFlag(QGraphicsItem::ItemIsSelectable, true);
    ent->setEnabled(true);
    qulonglong id_user_role = (qulonglong(id_groupe)<<32) | (qulonglong(id_entite) & 0xFFFFFFFF);
    ent->setData(Qt::UserRole, QVariant(id_user_role)); /* On assigne l'id de la ligne (sera utilisé pour faire le lien entre les élements de la QGraphicsScene et Perspective2D. */

    ent->setOpacity(1.0);
    ent->setVisible(true);
}

void Scene2D::AjoutEntEllipse(const Perspective3D::Pellipse2D &ellipse, int id_entite, int id_groupe)
{
    QGraphicsGroupItem *groupe_q = DernierGroupeEnts();

    pfloat span = ellipse.Angle2()-ellipse.Angle1();
    if (span < 0.)
    {
        span += 360.;
    }

    pfloat x = ellipse.CentreConst().X();
    pfloat y = -(ellipse.CentreConst().Y());

    QGraphicsArcEllipseItem *ent = new QGraphicsArcEllipseItem(groupe_q);
    ent->setRect(x-(ellipse.Lg()*.5), ((y)-(ellipse.Ht()*.5)), ellipse.Lg(), (ellipse.Ht()));
    ent->setPen(Stylo_trait(ellipse.Cache(), ellipse.CouleurConst()));
    ent->setTransformOriginPoint(x, y);
    ent->setRotation(ellipse.Rotation());
    ent->setStartAngle(ellipse.Angle1()*16);
    ent->setSpanAngle(span*16);
    ent->setFlag(QGraphicsItem::ItemIsSelectable, false);

    qulonglong id_user_role = (qulonglong(id_groupe)<<32) | (qulonglong(id_entite) & 0xFFFFFFFF);
    ent->setData(Qt::UserRole, QVariant(id_user_role)); /* On assigne l'id de l'ellipse (sera utilisé pour faire le lien entre les élements de la QGraphicsScene et Perspective2D. */
}

void Scene2D::AjoutEntTexte(const Perspective3D::PTexte2D &texte, int id_entite, int id_groupe)
{
    QGraphicsGroupItem *groupe_q = DernierGroupeEnts();

    QFont font;
    font.setPixelSize(texte.TailleTexte());

    QGraphicsTextItem *ent = new QGraphicsTextItem(groupe_q);
    ent->setPlainText(QString(texte.Texte()));
    ent->setFont(font);
//    ent->setDefaultTextColor(Qt::blue);
    ent->setDefaultTextColor(qColor(texte.CouleurConst()));
    ent->setPos(texte.Position().X(), texte.Position().Y());
    ent->setRotation(texte.Rotation());

    qulonglong id_user_role = (qulonglong(id_groupe)<<32) | (qulonglong(id_entite) & 0xFFFFFFFF);
    ent->setData(Qt::UserRole, QVariant(id_user_role));
}

void Scene2D::AjoutEntPoint(const Perspective3D::Ppoint2D &point, int id_entite, int id_groupe, const QColor &couleur)
{
    QGraphicsGroupItem *groupe_q = DernierGroupeEnts();

    const pfloat taille_rect = 1;
    QGraphicsRectItem *ent = new QGraphicsRectItem(groupe_q);
    ent->setRect(QRectF(point.X()-(taille_rect*.5), -(point.Y()+(taille_rect*.5)), taille_rect, taille_rect));
    QPen stylo(couleur, 4);
    stylo.setCosmetic(true);

    ent->setPen(stylo);
    ent->setFlag(QGraphicsItem::ItemIsSelectable, true);
    ent->setEnabled(true);

    qulonglong id_user_role = (qulonglong(id_groupe)<<32) | (qulonglong(id_entite) & 0xFFFFFFFF);
    ent->setData(Qt::UserRole, QVariant(id_user_role)); /* On assigne l'id de la ligne (sera utilisé pour faire le lien entre les élements de la QGraphicsScene et Perspective2D. */
    ent->setOpacity(1.0);
    ent->setVisible(true);
}

void Scene2D::AjoutImage(const Perspective3D::PImage &image, const Perspective3D::Ppoint2D &position)
{
    QGraphicsGroupItem *groupe_q = DernierGroupeEnts();
//    QGraphicsGroupItem *groupe_q = NouveauGroupeEnts();
    QPixmap q_image_matrice_tmp = ConversionPImageQPixmap(image);

    QGraphicsPixmapItem *pix = new QGraphicsPixmapItem(q_image_matrice_tmp, groupe_q);
    pix->setEnabled(true);
    pix->setScale(1.0);
    pix->setVisible(true);
    pix->setOpacity(1.0);
    pix->setPos(position.X(), position.Y());
    pix->setZValue(-1);

#ifdef DEBUG
    qDebug() << "Scene2D::AjoutImage (" << q_image_matrice_tmp.size() << "). Position : " << pix->pos()<< ". Visible : " << pix->isVisible() << ". Activée : " << pix->isEnabled();
#endif // DEBUG
}

void Scene2D::ModifOpaciteRect(QGraphicsRectItem *rect, float val)
{
    QRectF r = rect->rect();
    if (r.width() < 0 )
    {
        r.setRect(r.x()+r.width(), r.y(), -r.width(), r.height());
    }
    if (r.height() < 0)
    {
        r.setRect(r.x(), r.y()+r.height(), r.width(), -r.height());
    }

//    QList<QGraphicsItem *> elements = items(r, Qt::ContainsItemShape);
    QList<QGraphicsItem *> elements = items(r, Qt::IntersectsItemBoundingRect);

    for (QList<QGraphicsItem *>::iterator i = elements.begin(); i != elements.end(); ++i)
    {
        QGraphicsItem *entite = *i;
        if ((entite->type() == QGraphicsLineItem::Type) || (entite->type() == QGraphicsEllipseItem::Type))
        {
            entite->setOpacity(val);
        }
    }
}

void Scene2D::ModifOpacite(Perspective3D::vues2D_t vue, float val)
{
    if (vue & Perspective3D::vues2D_t::VUEFACE && ValideElement(rectFace)) { ModifOpaciteRect(rectFace, val); }
    if (vue & Perspective3D::vues2D_t::VUECOTE && ValideElement(rectCote)) { ModifOpaciteRect(rectCote, val); }
    if (vue & Perspective3D::vues2D_t::VUEHAUT && ValideElement(rectHaut)) { ModifOpaciteRect(rectHaut, val); }
    if (vue & Perspective3D::vues2D_t::VUEMULT && rectTripleVues)          { ModifOpaciteRect(rectTripleVues, val); }
    if (rectCourant)                                                       { ModifOpaciteRect(rectCourant, val); }
}

void Scene2D::ReinitRect(Perspective3D::vues2D_t vue)
/* Supprime un rectangle de sélection (d'après son id). */
{
    ModifOpacite(vue, 1.0);
    if (vue & Perspective3D::vues2D_t::VUEFACE)
    {
        if (ValideElement(rectFace)) { SupprimeElement(rectFace); }
        rectFace = 0;
    }
    if (vue & Perspective3D::vues2D_t::VUECOTE)
    {
        if (ValideElement(rectCote)) { SupprimeElement(rectCote); }
        rectCote = 0;
    }
    if (vue & Perspective3D::vues2D_t::VUEHAUT)
    {
        if (ValideElement(rectHaut)) { SupprimeElement(rectHaut); }
        rectHaut = 0;
    }
    if (vue & Perspective3D::vues2D_t::VUENUL)
    {
        if (ValideElement(rectCourant)) { SupprimeElement(rectCourant); }
        rectCourant = 0;
    }
    if (vue & Perspective3D::vues2D_t::VUEMULT)
    {
        if (ValideElement(rectTripleVues)) { SupprimeElement(rectTripleVues); }
        rectCourant = 0;
    }
//    ReinitOrigine(vue);
}

void Scene2D::ReinitOrigine(Perspective3D::vues2D_t vue)
{
    AccrochageObjets = false;
    if (vue == Perspective3D::vues2D_t::VUENUL)
    {
        if (!origineCourante.isNull())
        {
            if (EllipseorigineCourante)
            {
                SupprimeElement(EllipseorigineCourante);
                EllipseorigineCourante = 0;
            }
            origineCourante.setX(0);
            origineCourante.setY(0);
        }
    }
    else
    {
        if (vue & Perspective3D::vues2D_t::VUEFACE)
        {
            if (!origineFace.isNull())
            {
                if (EllipseorigineFace)
                {
                    SupprimeElement(EllipseorigineFace);
                    EllipseorigineFace = 0;
                }
                origineFace.setX(0);
                origineFace.setY(0);
            }
        }
        if (vue & Perspective3D::vues2D_t::VUECOTE)
        {
            if (!origineCote.isNull())
            {
                if (EllipseorigineCote)
                {
                    SupprimeElement(EllipseorigineCote);
                    EllipseorigineCote = 0;
                }
                origineCote.setX(0);
                origineCote.setY(0);
            }
        }
        if (vue & Perspective3D::vues2D_t::VUEHAUT)
        {
            if (!origineHaut.isNull())
            {
                if (EllipseorigineHaut)
                {
                    SupprimeElement(EllipseorigineHaut);
                    EllipseorigineHaut = 0;
                }
                origineHaut.setX(0);
                origineHaut.setY(0);
            }
        }
    }
}

void Scene2D::NettoyageCadresOrigines()
/* Nettoyage de la scène hors plan. */
{
    ReinitRect(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUEMULT);
    ReinitOrigine(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);
    ReinitOrigine(Perspective3D::vues2D_t::VUENUL);
    FinAxe();
}

int Scene2D::CompteurEntites() const
/* Renvoi le nombre d'entités sur la scène. */
{
    int compteur = 0;
    const int n_groupes = GroupesEntites.size();
    for(int i=0; i<n_groupes; ++i)
    {
        const QGraphicsGroupItem *gr = GroupesEntites[i];
        compteur += gr->CompteurEntites();
    }
    return compteur;
}

#ifdef DEBUG
bool Scene2D::ControleIdsEntites(const Perspective3D::PScene2D *scene_p3d) const
{
    if (!scene_p3d)
    {
        qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Scène nulle...";
        return false;
    }

    unsigned int compteur_erreurs = 0;
    const QList<QGraphicsItem *> elements = items(Qt::AscendingOrder);
    const Perspective3D::PStdVectGroupeEnts2D &groupes_ents_perspective = scene_p3d->Groupes();

    for (QList<QGraphicsItem *>::const_iterator i = elements.constBegin(); i != elements.constEnd(); ++i)
    {
        const QGraphicsItem* entite = *i;
        if (entite->type() == QGraphicsItemGroup::Type) /* On ignore si il s'agit d'une entité de groupe */
        {
            continue;
        }
        else if (entite->type() == QGraphicsPixmapItem::Type)
        {
            continue;
        }

        const qulonglong user_role_ent = entite->data(Qt::UserRole).toULongLong();
        const qint32 id_entite = qint32(user_role_ent & 0xFFFFFFFF); /* Récupère l'id de l'élément */
        const qint32 id_groupe_entite = qint32(user_role_ent >> 32); /* Récupère le groupe de l'élément */

        if (id_groupe_entite < 0)
        {
            qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, identifiant de groupe de l'entité " << ((void*) entite) << " nul... (Type=" << entite->type()  << ").";
//            ++compteur_erreurs;
            continue;
        }
        else if (id_groupe_entite >= qint32(groupes_ents_perspective.size()))
        {
            qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, identifiant de groupe de l'entité " << ((void*) entite) << " en dehors des bornes...";
            ++compteur_erreurs;
            continue;
        }

        const Perspective3D::PGroupeEnts2D *groupe = groupes_ents_perspective[id_groupe_entite];

        if (groupe->CompteurEntites() == 0)
        {
            qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, groupe vide...";
            ++compteur_erreurs;
            continue;
        }

        if (entite->type() == QGraphicsRectItem::Type)
        {
            if (id_entite < 0)
            {
//                qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, rectangle avec id nul.";
                continue; /* On ignore si il s'agit d'un rectangle de sélection */
            }

            const Perspective3D::PStdVectPoints2D &listepoints = groupe->ListePoints();
            if (static_cast<size_t>(id_entite) >= listepoints.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
            {
                qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, point avec id sortant du groupe : " << id_entite << "/" << listepoints.size() << ".";
                ++compteur_erreurs;
            }
        }
        else if (entite->type() == QGraphicsLineItem::Type) /* On a affaire à une ligne. */
        {
            const Perspective3D::PStdVectLignes2D &listelignes = groupe->ListeLignes();
            if (id_entite < 0)
            {
                qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, ligne avec id nul.";
                continue;
            }
            if (static_cast<size_t>(id_entite) >= listelignes.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
            {
                qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, ligne avec id sortant du groupe : " << id_entite << "/" << listelignes.size() << ".";
                ++compteur_erreurs;
            }
        }
        else if (entite->type() == QGraphicsEllipseItem::Type) /* On a affaire à une ellipse. */
        {
            const Perspective3D::PStdVectEllipses2D &listeellipses = groupe->ListeEllipses();
            if (id_entite < 0)
            {
                qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, ellipse avec id nul.";
                continue;
            }
            if (static_cast<size_t>(id_entite) >= listeellipses.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
            {
                qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, ellipse avec id sortant du groupe : " << id_entite << "/" << listeellipses.size() << ".";
                ++compteur_erreurs;
            }
        }
        else if (entite->type() == QGraphicsTextItem::Type)
        {
            const Perspective3D::PStdVectTextes2D &listetextes = groupe->ListeTextes();
            if (id_entite < 0)
            {
                qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, texte avec id nul.";
                continue;
            }
            if (static_cast<size_t>(id_entite) >= listetextes.size()) // Erreur d'alignement entre le groupe et la scène 2D ?!
            {
                qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, texte avec id sortant du groupe : " << id_entite << "/" << listetextes.size() << ".";
                ++compteur_erreurs;
            }
        }
        else
        {
            QRectF rect = entite->boundingRect().normalized();
            qDebug() << "Scene2D::ControleIdsEntites(" << ((void*)scene_p3d) << ") :: Attention, entitée graphique 2D non gérée à la position " << (rect.x()+(rect.width()*.5)) << " : " << (-(rect.y() + (rect.height()*.5)));
        }
    }
    return compteur_erreurs == 0;
}
#endif // DEBUG

void Scene2D::Nettoyage()
/* Nettoyage de la scène. */
{
    NettoyageCadresOrigines();

#ifdef SUPPORT_VISION2D
    /* Supprime les lignes du tracé. */
    if (lignes_affichage_edition)
    {
        SupprimeElement(lignes_affichage_edition);
        lignes_affichage_edition = nullptr;
    }
#endif // SUPPORT_VISION2D

    clear();
    GroupesEntites.clear();
    emit NouvelleScene();
}

bool Scene2D::SupprimeDernierGroupe()
{
    if (GroupesEntites.size())
    {
        SupprimeElement(GroupesEntites.back());
        GroupesEntites.back() = nullptr;
        GroupesEntites.pop_back();

#ifdef DEBUG
        qDebug() << "Supprime le dernier groupe de la scène : " << GroupesEntites.size();
#endif // DEBUG

        emit NouvelleScene();
        return true;
    }
    return false;
}

void Scene2D::SupprimeElement(QGraphicsItem *item)
{
#ifdef DEBUG
    if (!item)
    {
        qDebug() << "Scene2D :: Attention, tentative de suppression d'un élément nul !";
    }
    else if (item->scene() != this)
    {
        QGraphicsScene *p = item->scene();
//        qDebug() << "Scene2D :: Attention, tentative de suppression d'un élément en dehors de la scène : " << (void*)p << "!=" << (this) << "(" << item << ")";
        qDebug() << "Scene2D :: Attention, tentative de suppression d'un élément en dehors de la scène : " << (void*)p << "!=" << (this);
    }
    else
    {
        removeItem(item);
    }
#else
    removeItem(item);
#endif // DEBUG
    item = nullptr;
}

#ifdef SUPPORT_VISION2D
void Scene2D::PermutationModeEdition()
{
    ForceModeEdition(mode_edition != ModeEdition2D::NUL);
}
// SUPPORT_VISION2D
void Scene2D::ForceModeEdition(bool etat)
{
#ifdef DEBUG
//    qDebug() << "Scene2D::ForceModeEdition (avant) :: (" << etat << ")" << " :: " << ModeEdition() << " : " << ModeEdition2() << " : " << RejeteModeEdition2();
#endif // DEBUG

    if (ModeEdition())
    {
        if (etat)
        {
            ;
        }
        else
        {
            mode_edition ^= ModeEdition2D::EDITION1;
        }
    }
    else
    {
        if (etat)
        {
            mode_edition |= ModeEdition2D::EDITION1;
            update();
        }
        else
        {
            mode_edition = ModeEdition2D::NUL;
        }
    }

#ifdef DEBUG
//    qDebug() << "Scene2D::ForceModeEdition (après) :: (" << etat << ")" << " :: " << ModeEdition() << " : " << ModeEdition2() << " : " << RejeteModeEdition2();
#endif // DEBUG

    if (!ModeEdition() && ValideElement(lignes_affichage_edition))
    {
        SupprimeElement(lignes_affichage_edition);
        lignes_affichage_edition = nullptr;
    }
}
// SUPPORT_VISION2D
bool Scene2D::ModeEdition() const
{
    return mode_edition & ModeEdition2D::EDITION1;
}
// SUPPORT_VISION2D
bool Scene2D::ModeEdition2() const
{
    return mode_edition & ModeEdition2D::EDITION2;
}
// SUPPORT_VISION2D
bool Scene2D::RejeteModeEdition2() const
{
    return mode_edition & ModeEdition2D::REJETE_EDITION2;
}
// SUPPORT_VISION2D
void Scene2D::drawBackground(QPainter *painter, const QRectF &rect)
{
    const unsigned int interval_sous_grille = 10;

    QGraphicsScene::drawBackground(painter, rect);

    if ((ModeEdition()) && (!CompareE(taille_grille, 0))) // Affichage de la grille...
    {
        const QPointF decalage(-taille_grille, -taille_grille);
        const QPointF origine = QPointF(long(rect.left()/taille_grille) * taille_grille, long(rect.top()/taille_grille) * taille_grille) + decalage; // Origine alignée sur la grille.
        const QPointF max = rect.bottomRight();

#ifdef DEBUG
//        qDebug() << "Scene2D :: Affichage grille (" << taille_grille << ") : " << rect << ". Origine = " << origine;
#endif // DEBUG

        const QColor couleur_fond = backgroundBrush().color();
        const Perspective3D::PCouleur c_div = qRgbP(couleur_fond.rgba()).Div();
        const QColor couleur_grille(c_div.R(), c_div.V(), c_div.B(), 15);

        if (!interval_sous_grille)
        {
            painter->setPen(couleur_grille);

            float w = origine.x();
            while (w <= max.x())
            {
                painter->drawLine(w, origine.y(), w, max.y());
                w += taille_grille;
            }

            float h = origine.y();
            while (h <= max.y())
            {
                painter->drawLine(origine.x(), h, max.x(), h);
                h += taille_grille;
            }
        }
        else
        {
            const QColor couleur_grille_2(c_div.R(), c_div.V(), c_div.B(), 50);

            float w = origine.x();
            while (w <= max.x())
            {
                if ((int(fabs(w)/taille_grille) % interval_sous_grille) == 0)
                {
                    painter->setPen(couleur_grille_2);

                }
                else
                {
                    painter->setPen(couleur_grille);
                }

                painter->drawLine(w, origine.y(), w, max.y());
                w += taille_grille;
            }

            float h = origine.y();
            while (h <= max.y())
            {
                if ((int(fabs(h)/taille_grille) % interval_sous_grille) == 0)
                {
                    painter->setPen(couleur_grille_2);

                }
                else
                {
                    painter->setPen(couleur_grille);
                }

                painter->drawLine(origine.x(), h, max.x(), h);
                h += taille_grille;
            }
        }
    }
    else
    {
#ifdef DEBUG
//        qDebug() << "Scene2D::drawBackground() ? " << ModeEdition() << ":" << ModeEdition2() << " :: " << taille_grille;
#endif // DEBUG
    }
}
#endif // SUPPORT_VISION2D

bool Scene2D::ImportGroupeEntitesPerspective(const Perspective3D::PGroupeEnts2D *groupe_p, int id_groupe)
/* Import d'un groupe d'entités Perspective dans la scène Qt. */
{
    if (!groupe_p || !groupe_p->Valide())
    {
        return false;
    }
    if (groupe_p->CompteurEntites() == 0)
    {
        return false;
    }

    NouveauGroupeEnts();

    const Perspective3D::PStdVectLignes2D &ls = groupe_p->ListeLignes();
    const unsigned int tls = ls.size();
    for(unsigned int i=0; i<tls; ++i)
    {
        const Perspective3D::Pligne2D &l = ls[i];

//        if (l.Nulle())
//        {
//            continue;
//        }

        AjoutEntLigne(l, i, id_groupe);
    }

    const Perspective3D::PStdVectEllipses2D &le = groupe_p->ListeEllipses();
    const unsigned int tle = le.size();
    for(unsigned int i=0; i<tle; ++i)
    {
        const Perspective3D::Pellipse2D &e = le[i];
        AjoutEntEllipse(e, i, id_groupe);
    }

    const Perspective3D::PStdVectTextes2D &lt = groupe_p->ListeTextes();
    const unsigned int tlt = lt.size();
    for(unsigned int i=0; i<tlt; ++i)
    {
        const Perspective3D::PTexte2D &t = lt[i];
        AjoutEntTexte(t, i, id_groupe);
    }

    const Perspective3D::PStdVectPoints2D &lp = groupe_p->ListePoints();
    const unsigned int tlp = lp.size();
    for(unsigned int i=0; i<tlp; ++i)
    {
        const Perspective3D::Ppoint2D &p = lp[i];
        AjoutEntPoint(p, i, id_groupe, QColor(Qt::cyan));
    }
    emit NouvelleScene();
    return true;
}

bool Scene2D::ImportScenePerspective(const Perspective3D::PScene2D *scene2d_perspective, bool nettoyage)
/* Import du scene 2D Perspective dans une scène Qt. */
{
    if (nettoyage)
    {
        Nettoyage();
    }

    if (!scene2d_perspective || !scene2d_perspective->ValideScene())
    {
        return false;
    }

    const Perspective3D::PStdVectGroupeEnts2D &groupes_perspective = scene2d_perspective->Groupes();
    const int n_groupes_perspective = groupes_perspective.size();

    if (!n_groupes_perspective)
    {
        return false;
    }

    bool valide = false;

    for(int ig=0; ig<n_groupes_perspective; ++ig)
    {
        if (!groupes_perspective[ig])
        {
            continue;
//            break;
        }

        if (ImportGroupeEntitesPerspective(groupes_perspective[ig], ig))
        {
            valide = true;
        }
    }

#ifdef DEBUG
    ControleIdsEntites(scene2d_perspective);
#endif // DEBUG

    return valide;
}

const QPen Scene2D::Stylo_trait(bool cache, const QColor &couleur) const
{
    QPen stylo(couleur);

    if (cache) // Trait caché
    {
//        stylo.setColor(Qt::magenta);
        stylo.setStyle(Qt::DotLine);
        QVector<qreal> trait_cache;
        const pfloat taille_trait = 5.;
        const pfloat taille_espace = 6.;
//        const pfloat taille_point = 1.;
        trait_cache << taille_trait << taille_espace;
        stylo.setDashPattern(trait_cache);
        stylo.setWidth(1);
        stylo.setCosmetic(true);
//        stylo.setDashOffset(6.);
    }
    else
    {
        stylo.setStyle(Qt::SolidLine);
//        stylo.setWidth(5);
        stylo.setWidth(1);
        stylo.setCosmetic(true);
    }
    return stylo;
}

void Scene2D::ValideRectSelection()
{
    if (!rectCourant)
    {
        return;
    }
    QRectF r = rectCourant->rect();
    if (r.width() < 0 )
    {
        rectCourant->setRect(r.x()+r.width(), r.y(), -r.width(), r.height());
    }
    if (r.height() < 0)
    {
        rectCourant->setRect(r.x(), r.y()+r.height(), r.width(), -r.height());
    }
    QPen p = rectCourant->pen();
    p.setWidth(3);
    rectCourant->setPen(p);
    ModifOpacite(Perspective3D::vues2D_t::VUENUL, 0.20f);
}

QPoint Scene2D::ConvCoordsSceneEcran(const QGraphicsRectItem *rect, bool p1) const
/* Converti les coordonnées de l'entitée en coordonnées globales (celles de l'écran). L'entitée doit être visible. */
{
    QPointF px;
    if (p1) { px = rect->boundingRect().bottomLeft(); }
    else { px = rect->boundingRect().topRight(); }

    const QGraphicsView *v = GraphicView();
    QPointF p_scene = rect->mapToScene(px);
    QPoint p_vue = v->mapFromScene(p_scene);
    return (v->viewport()->mapToGlobal(p_vue));
}

QPoint Scene2D::ConvCoordsSceneEcran(const QGraphicsEllipseItem *el) const
/* Converti les coordonnées de l'entitée en coordonnées globales (celles de l'écran). L'entitée doit être visible. */
{
    const QGraphicsView *v = GraphicView();
    QPointF p_scene = el->mapToScene(el->rect().center());
    QPoint p_vue = v->mapFromScene(p_scene);
    return (v->viewport()->mapToGlobal(p_vue));
}

QGraphicsRectItem *Scene2D::RectVue(Perspective3D::vues2D_t vue) const
{
    if (vue == Perspective3D::vues2D_t::VUEFACE) { return rectFace; }
    if (vue == Perspective3D::vues2D_t::VUECOTE) { return rectCote; }
    if (vue == Perspective3D::vues2D_t::VUEHAUT) { return rectHaut; }
    if (vue == Perspective3D::vues2D_t::VUEMULT) { return rectTripleVues; }
    return nullptr;
}

const QGraphicsRectItem *Scene2D::RectVueConst(Perspective3D::vues2D_t vue) const
{
    return RectVue(vue);
}

int Scene2D::NbVuesDefinies() const
{
    int score = 0;
    if (VUE_EXTRUSION == Perspective3D::vues2D_t::VUEHAUT)
    {
        if (rectHaut)
        {
            ++score;
            if (rectFace) { ++score; }
        }
    }
    else
    {
        if (rectFace)
        {
            ++score;
            if (rectHaut) { ++score; }
        }
    }
    if (rectCote) { ++score; }
    return score;
}

bool Scene2D::RectVueDefini(Perspective3D::vues2D_t vue) const
{
    if (vue == Perspective3D::vues2D_t::VUEFACE) { return ValideElement(rectFace); }
    if (vue == Perspective3D::vues2D_t::VUECOTE) { return ValideElement(rectCote); }
    if (vue == Perspective3D::vues2D_t::VUEHAUT) { return ValideElement(rectHaut); }
    if (vue == Perspective3D::vues2D_t::VUEMULT) { return ValideElement(rectTripleVues); }
//    return (rectCourant != 0);
    return false;
}

QGraphicsEllipseItem *Scene2D::EllipseVue(Perspective3D::vues2D_t vue) const
{
    if (vue == Perspective3D::vues2D_t::VUEFACE) { return (EllipseorigineFace); }
    if (vue == Perspective3D::vues2D_t::VUECOTE) { return (EllipseorigineCote); }
    if (vue == Perspective3D::vues2D_t::VUEHAUT) { return (EllipseorigineHaut); }
    return nullptr;
}

bool Scene2D::OrigineDefinie(Perspective3D::vues2D_t vue) const
{
    if (vue == Perspective3D::vues2D_t::VUEFACE) { return (!origineFace.isNull()); }
    if (vue == Perspective3D::vues2D_t::VUECOTE) { return (!origineCote.isNull()); }
    if (vue == Perspective3D::vues2D_t::VUEHAUT) { return (!origineHaut.isNull()); }
    return false;
}

qreal Scene2D::TailleOrigineVue(Perspective3D::vues2D_t vue)
{
    QGraphicsRectItem *rectvue;
    if (vue == Perspective3D::vues2D_t::VUEFACE) { rectvue = rectFace; }
    else if (vue == Perspective3D::vues2D_t::VUECOTE) { rectvue = rectCote; }
    else if (vue == Perspective3D::vues2D_t::VUEHAUT) { rectvue = rectHaut; }
    else if (vue == Perspective3D::vues2D_t::VUEMULT) { rectvue = rectTripleVues; }
    else { return 15.; }

    if (!rectvue) { return 15; }
    QRectF rect = rectvue->rect();
    return (rect.width() > rect.height() ? rect.width() : rect.height()) * .1f;
}

void Scene2D::DessineOrigine(double x, double y, Perspective3D::vues2D_t vue)
/* Trace l'ellipse d'une origine */
{
    QPen stylo(CouleurVue(vue));
    stylo.setWidth(3);
    stylo.setCosmetic(true);

    qreal tailleorigine = TailleOrigineVue(vue);

    if (vue == Perspective3D::vues2D_t::VUEFACE)
    {
        if (!EllipseorigineFace)
        {
            EllipseorigineFace = addEllipse(0., 0., tailleorigine, tailleorigine, stylo);
            EllipseorigineFace->setData(Qt::UserRole, -1);
        }
        else
        {
            EllipseorigineFace->setRect(0., 0., tailleorigine, tailleorigine);
        }
        EllipseorigineFace->setPos(x-(tailleorigine*.5), y-(tailleorigine*.5));
    }
    else if (vue == Perspective3D::vues2D_t::VUECOTE)
    {
        if (!EllipseorigineCote)
        {
            EllipseorigineCote = addEllipse(0., 0., tailleorigine, tailleorigine, stylo);
            EllipseorigineCote->setData(Qt::UserRole, -1);
        }
        else
        {
            EllipseorigineCote->setRect(0., 0., tailleorigine, tailleorigine);
        }
        EllipseorigineCote->setPos(x-(tailleorigine*.5), y-(tailleorigine*.5));
    }
    else if (vue == Perspective3D::vues2D_t::VUEHAUT)
    {
        if (!EllipseorigineHaut)
        {
            EllipseorigineHaut = addEllipse(0., 0., tailleorigine, tailleorigine, stylo);
            EllipseorigineHaut->setData(Qt::UserRole, -1);
        }
        else
        {
            EllipseorigineHaut->setRect(0., 0., tailleorigine, tailleorigine);
        }
        EllipseorigineHaut->setPos(x-(tailleorigine*.5), y-(tailleorigine*.5));
    }
    else if (vue == Perspective3D::vues2D_t::VUENUL)
    {
        if (!EllipseorigineCourante)
        {
            EllipseorigineCourante = addEllipse(0., 0., tailleorigine, tailleorigine, stylo);
            EllipseorigineCourante->setData(Qt::UserRole, -1);
            EllipseorigineCourante->setFlag(QGraphicsItem::ItemIgnoresTransformations, true);
        }
        EllipseorigineCourante->setPos(x-(tailleorigine*.5), y-(tailleorigine*.5));
    }
}

void Scene2D::AnimOrigine(const QPointF &point, Perspective3D::vues2D_t vue)
/* Ajoute une origine avec une animation. */
{
    DessineOrigine(point.x(), point.y(), vue);
    QGraphicsEllipseItem *el_vue = EllipseVue(vue);
    el_vue->hide();

    if ( animation.CurseurPoint(ConvCoordsSceneEcran(el_vue), EcranWidget(GraphicView())) )
    {
        el_vue->show();
        animation.ClicPoint();
    }

//    SupprimeElement(el_vue);
//    el_vue = 0;

    ReinitOrigine(vue);
}

void Scene2D::AjoutOrigine(const QPointF &point, Perspective3D::vues2D_t vue)
/* Ajoute une origine pour la vue donnée en argument. */
{
    DessineOrigine(point.x(), point.y(), vue);

    if (vue == Perspective3D::vues2D_t::VUEFACE)
    {
        origineFace = point;
    }
    else if (vue == Perspective3D::vues2D_t::VUECOTE)
    {
        origineCote = point;
    }
    else if (vue == Perspective3D::vues2D_t::VUEHAUT)
    {
        origineHaut = point;
    }
    else if (vue == Perspective3D::vues2D_t::VUENUL)
    {
        origineCourante = point;
        etatOrigine = true;
    }
}

void Scene2D::DefOrigine(Perspective3D::vues2D_t vue)
/* On demande l'assignation d'une nouvelle origine (définie à la main par l'utilisateur). */
{
//    ReinitOrigine(vue);
    AjoutOrigine(QPointF(0.,0.),vue);
    idVue = vue;
    etatOrigine = true;
    AccrochageObjets = true;
    DebutAxe(0., 0.);
}

QPointF Scene2D::MilieuLigne(const QLineF &l) const
/* Renvoi le point au milieu d'une ligne */
{
    QPointF ret;
    ret.setX( l.p1().x() + ((l.p2().x()-l.p1().x())*.5) );
    ret.setY( l.p1().y() + ((l.p2().y()-l.p1().y())*.5) );
    return ret;
}

QPointF Scene2D::PerpendiculairePL(double x, double y, const QLineF &l) const
/* Renvoi le point perpendiculaire à la ligne donnée en argument depuis le point. */
{
    QPointF intersection;

    double dist2 = DistancePoints2(l.p2().x(), l.p2().y(), l.p1().x(), l.p1().y());

    double U = ( ((x - l.p1().x()) * (l.p2().x() - l.p1().x())) +
                 ((y - l.p1().y() ) * (l.p2().y() - l.p1().y())) ) / (dist2);

    if( U < 0.0f || U > 1.0f )
    {
        return intersection;   // Le point est en dehors de la ligne.
    }
    intersection.setX(l.p1().x() + U * (l.p2().x() - l.p1().x()));
    intersection.setY(l.p1().y() + U * (l.p2().y() - l.p1().y()));
    return intersection;
}

QPointF Scene2D::PointAccrochage(const QPointF &pos) const
/* Renvoi un point d'accorchage sur une entitée au plus proche de la position donnée en argument */
{
    QPointF ret(0., 0.);
    qreal diff_prec2 = 25.; /* Différence mini (distance au carré) pour valider un point */
    QRectF rect;

    rect.setX(pos.x()-(diff_prec2*.5));
    rect.setY(pos.y()-(diff_prec2*.5));
    rect.setHeight(diff_prec2);
    rect.setWidth(diff_prec2);

    bool trouve = false;
    QList<QGraphicsItem *> elements = items(rect, Qt::IntersectsItemShape);

    if (elements.size())
    {
        for (QList<QGraphicsItem *>::iterator i = elements.begin(); i != elements.end(); ++i)
        {
            QGraphicsItem *entite = *i;
            if (entite->type() == QGraphicsLineItem::Type)
            {
                if (QGraphicsLineItem *l = qgraphicsitem_cast<QGraphicsLineItem *>(entite))
                {
                    QLineF ligne = l->line();
                    QPointF centre = MilieuLigne(ligne);

                    if (!origineFace.isNull()) /* Si l'origine de la vue de face est définie, on accroche perpendiculairement à ce point. */
                    {
                        const QPointF perpendiculaire = PerpendiculairePL(origineFace.x(), origineFace.y(), ligne);
                        if (DistancePoints2(perpendiculaire.x(), perpendiculaire.y(), pos.x(), pos.y()) < diff_prec2) /* Recherche perpendiculaire */
                        {
                            diff_prec2 = perpendiculaire.x() - pos.x();
                            ret = perpendiculaire;
                            trouve = true;
                        }
                    }

                    if (DistancePoints2(ligne.p1().x(), ligne.p1().y(), pos.x(), pos.y()) < diff_prec2) /* Compare l'extrémité de ligne 1s */
                    {
                        diff_prec2 = ligne.p1().x() - pos.x();
                        ret = ligne.p1();
                        trouve = true;
                    }
                    if (DistancePoints2(ligne.p2().x(), ligne.p2().y(), pos.x(), pos.y()) < diff_prec2) /* Compare l'extrémité de ligne 2 */
                    {
                        diff_prec2 = ligne.p2().x() - pos.x();
                        ret = ligne.p2();
                        trouve = true;
                    }
                    if (DistancePoints2(centre.x(), centre.y(), pos.x(), pos.y()) < diff_prec2) /* Compare le point au centre de la ligne. */
                    {
                        diff_prec2 = centre.x() - pos.x();
                        ret = centre;
                        trouve = true;
                    }
                }
            }
            else if (entite->type() == QGraphicsEllipseItem::Type)
            {
                if (QGraphicsEllipseItem *e = qgraphicsitem_cast<QGraphicsEllipseItem *>(entite))
                {
                    QPointF centre = e->rect().center();
                    if (DistancePoints2(centre.x(), centre.y(), pos.x(), pos.y()) < diff_prec2)
                    {
                        diff_prec2 = centre.x() - pos.x();
                        ret = centre;
                        trouve = true;
                    }
                }
            }
            else if (entite->type() == QGraphicsRectItem::Type) // Utilisé pour les points
            {
                if (QGraphicsRectItem *e = qgraphicsitem_cast<QGraphicsRectItem *>(entite))
                {
                    QPointF centre = e->rect().center();
                    if (DistancePoints2(centre.x(), centre.y(), pos.x(), pos.y()) < diff_prec2)
                    {
                        diff_prec2 = centre.x() - pos.x();
                        ret = centre;
                        trouve = true;
                    }
                }
            }
        }
    }
    if (trouve)
    {
        return ret;
    }
    return pos;
}

bool Scene2D::DebutAxe(double x, double y)
/* Axe diagonal./ */
{
    if (!axeDessin)
    {
        QPen stylo(Qt::yellow);
        stylo.setWidth(1);
        stylo.setStyle(Qt::DashLine);
        stylo.setCosmetic(true);
        axeDessin = addLine(x, y, x, y, stylo);
        axeDessin->setData(Qt::UserRole, QVariant(-1));
        return true;
    }
    return false;
}

bool Scene2D::P2Axe(const QPointF &p)
{
    if (axeDessin)
    {
        QLineF l = axeDessin->line();
        l.setP2(p);
        axeDessin->setLine(l);
        return true;
    }
    return false;
}

bool Scene2D::P1Axe(const QPointF &p)
{
    if (axeDessin)
    {
        QLineF l = axeDessin->line();
        l.setP1(p);
        axeDessin->setLine(l);
        return true;
    }
    return false;
}

bool Scene2D::FinAxe()
{
    if (axeDessin)
    {
        SupprimeElement(axeDessin);
        axeDessin = 0;
        return true;
    }
    return false;
}

void Scene2D::wheelEvent(QGraphicsSceneWheelEvent *event)
{
    event->accept();
}

void Scene2D::focusInEvent(QFocusEvent *event)
{
    event->accept();
}

void Scene2D::focusOutEvent(QFocusEvent *event)
{
    event->accept();
}

void Scene2D::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if (animation.Anime()) /* Ignore les évènements souris si une animation est en cours... */
    {
        event->accept();
        return;
    }

    const QPointF pos = event->scenePos();

#ifdef SUPPORT_VISION2D
    if (ModeEdition() && init_ligne)
    {
        if (ValideElement(lignes_affichage_edition))
        {
            QGraphicsLineItem *nouvelle_ligne = new QGraphicsLineItem(lignes_affichage_edition);

            nouvelle_ligne->setLine(QLineF(point_precedent_edition, pos));
            nouvelle_ligne->setPen(Stylo_trait(false, couleur_trace));
            nouvelle_ligne->setFlag(QGraphicsItem::ItemIsSelectable, true);
            nouvelle_ligne->setEnabled(true);
            nouvelle_ligne->setData(Qt::UserRole, QVariant(-1));
            nouvelle_ligne->setOpacity(1.0);
            nouvelle_ligne->setVisible(true);

            point_precedent_edition = pos;
            points_edition.push_back(event->screenPos());
        }
    }
    else
#endif // SUPPORT_VISION2D
    if (etatRect && rectCourant && rectCourant->scene()) /* On dessine le rectangle d'une vue... */
    {
        rectCourant->setRect(posRect_orig.x(), posRect_orig.y(), pos.x() - posRect_orig.x(), pos.y() - posRect_orig.y());
        P2Axe(pos);
        const QRectF rect = rectCourant->rect();
        emit TexteCoords(QString::number(rect.x(), 'f', 2) + " : " + QString::number(-(rect.y()), 'f', 2) + " [" + QString::number(rect.width(), 'f', 2) + " : " + QString::number(-rect.height(), 'f', 2) + "]");
    }
    else
    {
        if (AccrochageObjets) /* Accrochage aux objets activé. */
        {
            QPointF accroche = PointAccrochage(pos);
            if ( (accroche.isNull()) || (accroche == pos) )
            {
                if (EllipseorigineCourante)
                {
                    EllipseorigineCourante->hide();
                }
            }
            else
            {
                if (EllipseorigineCourante)
                {
                    EllipseorigineCourante->show();
                }
                if (!origineFace.isNull())
                {
                    P1Axe(origineFace);
                    P2Axe(accroche);
                }
            }
            origineCourante.setX(accroche.x());
            origineCourante.setY(accroche.y());
            DessineOrigine(accroche.x(), accroche.y(), idVue);
        }
        emit TexteCoords(QString::number(pos.x(), 'f', 2) + " : " + QString::number(-(pos.y()), 'f', 2));
    }
    event->accept();
}

Qt::GlobalColor Scene2D::CouleurVue(Perspective3D::vues2D_t vue) const
/* Renvoi la couleur correspondant à la vue donnée en argument */
{
    return (vue == Perspective3D::vues2D_t::VUEFACE ? Qt::blue : (vue == Perspective3D::vues2D_t::VUECOTE ? Qt::red : (vue == Perspective3D::vues2D_t::VUEHAUT ? Qt::green : (vue == Perspective3D::vues2D_t::VUEMULT ? Qt::magenta : Qt::yellow))));
}

void Scene2D::AnimRectSelection(const QRectF &cadre_vue, Perspective3D::vues2D_t vue)
/* Dessine un rectangle automatique de façon animée (on doit auparavent être sûr qu'il est visuellement présent sur la vue 2D).
    Le rectangle en question est éphémère, il sera supprimé dès la fin de la fonction. */
{
//    if (!animation.Anime())
//    {
//        return;
//    }
    QPen stylo(CouleurVue(vue));
    stylo.setWidth(3);
    stylo.setCosmetic(true);

    QRectF copie_cadre_vue = AdapteCadreAnimation(cadre_vue);

    const unsigned int n_divis = 20;
    float inc_x = copie_cadre_vue.width() / (n_divis);
    float inc_y = copie_cadre_vue.height() / (n_divis);
    float v_x = 0;
    float v_y = 0;

    QRectF rect_l(copie_cadre_vue.x(), copie_cadre_vue.y(), 0, 0);

    QGraphicsRectItem *rect = addRect(rect_l, stylo);

    QScreen *ecran = EcranWidget(GraphicView());

//    rect->ensureVisible();
    QPoint px = ConvCoordsSceneEcran(rect, true);
    if (animation.CurseurPoint(px, ecran))
    {
        animation.ClicPoint();
    }

    bool ignore_suppression = false;

    for(unsigned int i=0; i<=n_divis; ++i)
    {
        if (!animation.Anime())
        {
            ignore_suppression = true;
            break;
        }
        rect_l.setTopRight(QPointF(copie_cadre_vue.x()+v_x, copie_cadre_vue.y()+v_y));
        rect->setRect(rect_l);

        px = ConvCoordsSceneEcran(rect, false);
        animation.CurseurPoint(px, ecran, animation.TempoDeplacementSouris()*2);

//        rect->update();

        v_x += inc_x;
        v_y += inc_y;
//        if (!animation.Sommeil(animation.TempoReference()/6))
//            return;
    }

    if (animation.Anime())
    {
        px = ConvCoordsSceneEcran(rect, false);
        if (animation.CurseurPoint(px, ecran))
        {
            animation.ClicPoint();
        }
    }

    if (!ignore_suppression)
    {
        SupprimeElement(rect);
    }
    rect = nullptr;
}

bool Scene2D::AnnuleDessinRectCourant()
{
    if (rectCourant)
    {
        SupprimeElement(rectCourant);
        rectCourant = 0;
    }
    etatRect = 0;
    return FinAxe();
}

void Scene2D::Ajout_RectSelection(const QRectF &rect, Perspective3D::vues2D_t vue, bool courant)
/* Ajoute un rectangle de sélection pour les vues. */
{
    QPen stylo(CouleurVue(vue));
    stylo.setWidth(3);
    stylo.setCosmetic(true);

#ifdef debug
    qDebug() << "Ajout_RectSelection :: " << rect << " : " << (int)vue << ", courant=" << courant;
#endif // DEBUG

    if (courant)
    {
        ReinitRect(vue);
        rectCourant = addRect(rect.x(), rect.y(), rect.width(), rect.height(), stylo);
    }
    else
    {
        ReinitRect(vue);
        if (vue == Perspective3D::vues2D_t::VUEFACE)
        {
            rectFace = addRect(rect.x(), rect.y(), rect.width(), rect.height(), stylo);
        }
        else if (vue == Perspective3D::vues2D_t::VUECOTE)
        {
            rectCote = addRect(rect.x(), rect.y(), rect.width(), rect.height(), stylo);
        }
        else if (vue == Perspective3D::vues2D_t::VUEHAUT)
        {
            rectHaut = addRect(rect.x(), rect.y(), rect.width(), rect.height(), stylo);
        }
        else if (vue == Perspective3D::vues2D_t::VUEMULT)
        {
            rectTripleVues = addRect(rect.x(), rect.y(), rect.width(), rect.height(), stylo);
        }
        else
        {
            return;
        }
        ModifOpacite(vue, 0.20f);
    }
}

//void Scene2D::changed(const QList<QRectF> &region)
//{
////    qDebug() << "Scène changée : " << region;
//    ;
//}

#ifdef SUPPORT_VISION2D
void Scene2D::DepartTrace(const QPointF &pos_scene, const QPoint &pos_ecran)
{
    point_precedent_edition = pos_scene;
    points_edition.clear();
    points_edition.push_back(pos_ecran);
    lignes_affichage_edition = new QGraphicsGroupItem(point_precedent_edition);
    addItem(lignes_affichage_edition);
    init_ligne = true;
}
#endif // SUPPORT_VISION2D

void Scene2D::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
#ifdef SUPPORT_VISION2D
    if (event->button() == Qt::LeftButton)
    {
//        qDebug() << "mouseDoubleClickEvent :: " << ModeEdition() << " & " << ModeEdition2() << " & " << RejeteModeEdition2();
        if (!ModeEdition())
        {
            if (!ModeEdition2())
            {
                mode_edition |= ModeEdition2D::EDITION2;
                mode_edition |= ModeEdition2D::REJETE_EDITION2;
                emit DoubleClicScene(true);
            }
            else
            {
//                qDebug() << "Désactivation de l'édition (2) !";
                mode_edition ^= ModeEdition2D::REJETE_EDITION2;
//                points_edition.clear();
//                mode_edition = ModeEdition2D::NUL;
                emit DoubleClicScene(true);
            }
        }
        else
        {
//            qDebug() << "Désactivation de l'édition (2:2) !";
            mode_edition = ModeEdition2D::NUL;
            emit DoubleClicScene(false);
        }
    }
#endif // SUPPORT_VISION2D
    event->accept();
}

void Scene2D::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (animation.Anime()) /* Ignore les évènements souris si une animation est en cours... */
    {
        event->accept();
        return;
    }

//    qDebug() << "Scene2D::mousePressEvent :: " << ModeEdition() << " & " << ModeEdition2() << " & " << RejeteModeEdition2();

    if (event->button() == Qt::RightButton)
    {
        if (MenuContextuel)
        {
            emit AfficheMenu(event->screenPos());
            event->ignore();
            return;
        }
        else
        {
            emit GenModele(false);
        }
    }
    else if (event->button() == Qt::MiddleButton)
    {
#ifdef SUPPORT_VISION2D
        if (ModeEdition())
        {
            DepartTrace(event->scenePos(), event->screenPos());
            event->ignore();
            return;
        }
        else
#endif // SUPPORT_VISION2D
        {
            if (!etatOrigine && !etatRect)
            {
                if (NbVuesDefinies() == 3)
                {
                    NettoyageCadresOrigines();
//                    emit GenModele(MenuContextuel); /* Les vues ne seront supprimées après génération que si l'on utilise le menu contextuel */
                }
//                else
                {
                    ReinitOrigine(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUENUL);
                    emit SupprimeOrigine(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT | Perspective3D::vues2D_t::VUENUL);
                    etatOrigine = false;
                    DessineRect(Perspective3D::vues2D_t::VUEMULT, false);
                }
            }
        }
    }
    else if (event->button() == Qt::LeftButton) /* En réalité bouton du milieu ! */
    {
#ifdef SUPPORT_VISION2D
//        if (ModeEdition())
//        {
//            ForceModeEdition(false);
//        }
#endif // SUPPORT_VISION2D
    }

    if (!GereNouveauRect(event->scenePos()))
    {
        event->ignore();
        return;
    }
    event->ignore();
}

void Scene2D::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if (animation.Anime()) /* Ignore les évènements souris si une animation est en cours... */
    {
        event->accept();
        return;
    }

#ifdef SUPPORT_VISION2D
    if (event->button() == Qt::MiddleButton)
    {
        if (ModeEdition())
        {
            if (!RejeteModeEdition2())
            {
//                qDebug() << "mouseReleaseEvent :: " << ModeEdition() << " & " << ModeEdition2() << " & " << RejeteModeEdition2();

                init_ligne = false;
                points_edition.push_back(event->screenPos());

                /* Supprime les lignes du tracé. */
                SupprimeElement(lignes_affichage_edition);
                lignes_affichage_edition = nullptr;

                emit NouveauTraceVision2D(points_edition, event->scenePos());
            }
            else
            {
                mode_edition ^= ModeEdition2D::REJETE_EDITION2;
            }
        }
    }
    else if (event->button() == Qt::LeftButton) /* En réalité bouton du milieu ! */
    {
    }
#endif // SUPPORT_VISION2D
    event->accept();
}

bool Scene2D::GereNouveauRect(const QPointF &pos)
{
    if (etatOrigine && !origineCourante.isNull()) /* On demande de définir une origine... */
    {
        if ((idVue == Perspective3D::vues2D_t::VUERREUR) ||(idVue == Perspective3D::vues2D_t::VUENUL))
        {
            return false;
        }

        AjoutOrigine(origineCourante, idVue);
        ReinitOrigine(Perspective3D::vues2D_t::VUENUL);
        etatOrigine = false;
        FinAxe();
        emit NouvelleOrigine(idVue);
    }

    if (etatRect == 1 && !rectCourant) /* On dessine un nouveau rectangle */
    {
        if ((idVue == Perspective3D::vues2D_t::VUERREUR) ||(idVue == Perspective3D::vues2D_t::VUENUL))
        {
            return false;
        }
        etatRect = 2; /* Au prochain clic, on validera la sélection. */
        posRect_orig = pos;
        if (DebutAxe(posRect_orig.x(), posRect_orig.y()))
        {
            Ajout_RectSelection(QRectF(posRect_orig.x(), posRect_orig.y(), 0, 0), idVue, true);
        }
    }
    else if (etatRect == 2) /* On valide la sélection */
    {
        ValideRectSelection();

        if (idVue == Perspective3D::vues2D_t::VUEFACE) { rectFace = rectCourant; }
        else if (idVue == Perspective3D::vues2D_t::VUECOTE) { rectCote = rectCourant; }
        else if (idVue == Perspective3D::vues2D_t::VUEHAUT) { rectHaut = rectCourant; }
        else if (idVue == Perspective3D::vues2D_t::VUEMULT) { rectTripleVues = rectCourant; }

        etatRect = 0;
        emit NouveauRect(idVue);
        rectCourant = 0;
        FinAxe();
    }
    else
    {
    }
    return true;
}

void Scene2D::DessineRect(Perspective3D::vues2D_t vue, bool reinit)
/* Demande le dessin d'un rectangle de sélection d'une vue. */
{
    if (!rectCourant)
    {
        if (reinit)
        {
            ReinitRect(vue);
        }
        idVue = vue;
        etatRect = 1;
    }
}

bool Scene2D::ConvVueMult()
/* Converti le rectangle de vue multiple en une vue spécifique en fonction du contexte. Le rectangle de vue multiple sera supprimé.
    Renvoi true si les trois vues sont définies, sinon false. */
{
    if (!rectTripleVues)
    {
        return false;
    }
    QRectF rect_mult = rectTripleVues->rect();
    ReinitRect(Perspective3D::vues2D_t::VUEMULT); // Supprime le rectangle des vues multiples.

    if (ValideElement(rectFace) && ValideElement(rectCote) && ValideElement(rectHaut))
    {
        ReinitRect(Perspective3D::vues2D_t::VUEFACE | Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);
    }

    if (!ValideElement(rectFace)) // Pas de vue de face définie, on la trace
    {
        ReinitRect(Perspective3D::vues2D_t::VUECOTE | Perspective3D::vues2D_t::VUEHAUT);
        ReinitOrigine(Perspective3D::vues2D_t::VUEFACE);
        Ajout_RectSelection(rect_mult, Perspective3D::vues2D_t::VUEFACE, false);
    }
    else if (!ValideElement(rectCote)) // Vue de face définie, mais la celle de côté...
    {
        ReinitRect(Perspective3D::vues2D_t::VUEHAUT);
        ReinitOrigine(Perspective3D::vues2D_t::VUECOTE);
        Ajout_RectSelection(rect_mult, Perspective3D::vues2D_t::VUECOTE, false);
    }
    else if(!ValideElement(rectHaut)) // Vues de face et de côté définies, il ne manque que celle de dessus...
    {
        ReinitOrigine(Perspective3D::vues2D_t::VUEHAUT);
        Ajout_RectSelection(rect_mult, Perspective3D::vues2D_t::VUEHAUT, false);
        return true;
    }
    return false;
}
