File indexing completed on 2024-05-12 04:20:38

0001 /*
0002  * SPDX-FileCopyrightText: 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #include "ReverseMapper.h"
0010 
0011 #include <math.h>
0012 
0013 #include <QRect>
0014 #include <QtDebug>
0015 #include <QPolygonF>
0016 #include <QPainterPath>
0017 #include <QGraphicsScene>
0018 
0019 #include "KChartAbstractDiagram.h"
0020 #include "ChartGraphicsItem.h"
0021 
0022 using namespace KChart;
0023 
0024 ReverseMapper::ReverseMapper()
0025     : m_diagram( nullptr )
0026     , m_scene( nullptr )
0027 {
0028 }
0029 
0030 ReverseMapper::ReverseMapper( AbstractDiagram* diagram )
0031     : m_diagram( diagram )
0032     , m_scene( nullptr )
0033 {
0034 }
0035 
0036 ReverseMapper::~ReverseMapper()
0037 {
0038     delete m_scene;
0039     m_scene = nullptr;
0040 }
0041 
0042 void ReverseMapper::setDiagram( AbstractDiagram* diagram )
0043 {
0044     m_diagram = diagram;
0045 }
0046 
0047 void ReverseMapper::clear()
0048 {
0049     m_polygons.clear();
0050     if (m_scene)
0051         m_scene->clear();
0052     m_sceneDirty = true;
0053 }
0054 
0055 QModelIndexList ReverseMapper::indexesIn( const QRect& rect ) const
0056 {
0057     populateScene();
0058 
0059     if ( m_scene->sceneRect().intersects( rect ) ) {
0060         const QList<QGraphicsItem *> items = m_scene->items( rect );
0061         QModelIndexList indexes;
0062         for ( QGraphicsItem* item : items ) {
0063             ChartGraphicsItem* i = qgraphicsitem_cast<ChartGraphicsItem*>( item );
0064             if ( i ) {
0065                 QModelIndex index ( m_diagram->model()->index( i->row(), i->column(), m_diagram->rootIndex() ) ); // checked
0066                 indexes << index;
0067             }
0068         }
0069         return indexes;
0070     } else {
0071         return QModelIndexList();
0072     }
0073 }
0074 
0075 QModelIndexList ReverseMapper::indexesAt( const QPointF& point ) const
0076 {
0077     populateScene();
0078 
0079     if ( m_scene->sceneRect().contains( point ) ) {
0080         const QList<QGraphicsItem *> items = m_scene->items( point );
0081         QModelIndexList indexes;
0082         for ( QGraphicsItem* item : items ) {
0083             ChartGraphicsItem* i = qgraphicsitem_cast<ChartGraphicsItem*>( item );
0084             if ( i ) {
0085                 QModelIndex index ( m_diagram->model()->index( i->row(), i->column(), m_diagram->rootIndex() ) ); // checked
0086                 if ( !indexes.contains(index) )
0087                     indexes << index;
0088             }
0089         }
0090         return indexes;
0091     } else {
0092         return QModelIndexList();
0093     }
0094 }
0095 
0096 QPolygonF ReverseMapper::polygon( int row, int column ) const
0097 {
0098     if ( !m_diagram->model()->hasIndex( row, column, m_diagram->rootIndex() ) )
0099         return QPolygon();
0100     const QModelIndex index = m_diagram->model()->index( row, column, m_diagram->rootIndex() ); // checked
0101     return m_polygons.value(index);
0102 }
0103 
0104 QRectF ReverseMapper::boundingRect( int row, int column ) const
0105 {
0106     return polygon(row, column).boundingRect();
0107 }
0108 
0109 void ReverseMapper::addRect( int row, int column, const QRectF& rect )
0110 {
0111     addPolygon( row, column, QPolygonF( rect ) );
0112 }
0113 
0114 void ReverseMapper::addPolygon( int row, int column, const QPolygonF& polygon )
0115 {
0116     const auto index = m_diagram->model()->index( row, column, m_diagram->rootIndex() );
0117     auto &value = m_polygons[index];
0118     if (value.isEmpty()) {
0119         value = polygon;
0120     } else {
0121         value = value.united(polygon);
0122     }
0123 
0124     m_sceneDirty = true;
0125 }
0126 
0127 void ReverseMapper::addCircle( int row, int column, const QPointF& location, const QSizeF& diameter )
0128 {
0129     QPainterPath path;
0130     QPointF ossfet( -0.5*diameter.width(), -0.5*diameter.height() );
0131     path.addEllipse( QRectF( location + ossfet, diameter ) );
0132     addPolygon( row, column, QPolygonF( path.toFillPolygon() ) );
0133 }
0134 
0135 void ReverseMapper::addLine( int row, int column, const QPointF& from, const QPointF& to )
0136 {
0137     // that's no line, dude... make a small circle around that point, instead
0138     if ( from == to )
0139     {
0140         addCircle( row, column, from, QSizeF( 1.5, 1.5 ) );
0141         return;
0142     }
0143     // lines do not make good polygons to click on. we calculate a 2
0144     // pixel wide rectangle, where the original line is exactly
0145     // centered in.
0146     // make a 3 pixel wide polygon from the line:
0147     QPointF left, right;
0148     if ( from.x() < to.x() ) {
0149         left = from;
0150         right = to;
0151     } else {
0152         right = from;
0153         left = to;
0154     }
0155     const QPointF lineVector( right - left );
0156     const qreal lineVectorLength = sqrt( lineVector.x() * lineVector.x() + lineVector.y() * lineVector.y() );
0157     const QPointF lineVectorUnit( lineVector / lineVectorLength );
0158     const QPointF normOfLineVectorUnit( -lineVectorUnit.y(), lineVectorUnit.x() );
0159     // now the four polygon end points:
0160     const QPointF one( left - lineVectorUnit + normOfLineVectorUnit );
0161     const QPointF two( left - lineVectorUnit - normOfLineVectorUnit );
0162     const QPointF three( right + lineVectorUnit - normOfLineVectorUnit );
0163     const QPointF four( right + lineVectorUnit + normOfLineVectorUnit );
0164     addPolygon( row, column, QPolygonF() << one << two << three << four );
0165 }
0166 
0167 void ReverseMapper::populateScene() const
0168 {
0169     // we populate the scene lazily...
0170 
0171     Q_ASSERT( m_diagram );
0172 
0173     if (!m_sceneDirty) {
0174         return;
0175     }
0176 
0177     if (!m_scene) {
0178         m_scene = new QGraphicsScene;
0179     }
0180 
0181     QRectF boundingRect;
0182     for (auto it = m_polygons.constBegin(), end = m_polygons.constEnd(); it != end; ++it) {
0183         auto* item = new ChartGraphicsItem( it.key().row(), it.key().column() );
0184         item->setPolygon( it.value() );
0185         m_scene->addItem( item );
0186         boundingRect |= it.value().boundingRect();
0187     }
0188 
0189     m_scene->setSceneRect(boundingRect);
0190     m_sceneDirty = false;
0191 }