File indexing completed on 2024-05-19 15:27:29

0001 /*
0002  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB.  All rights reserved.
0003  *
0004  * This file is part of the KD Chart library.
0005  *
0006  * This program is free software; you can redistribute it and/or
0007  * modify it under the terms of the GNU General Public License as
0008  * published by the Free Software Foundation; either version 2 of
0009  * the License, or (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
0018  */
0019 
0020 #include "ReverseMapper.h"
0021 
0022 #include <math.h>
0023 
0024 #include <QRect>
0025 #include <QtDebug>
0026 #include <QPolygonF>
0027 #include <QPainterPath>
0028 #include <QGraphicsScene>
0029 
0030 #include "KChartAbstractDiagram.h"
0031 #include "ChartGraphicsItem.h"
0032 
0033 using namespace KChart;
0034 
0035 ReverseMapper::ReverseMapper()
0036     : m_scene( nullptr )
0037     , m_diagram( nullptr )
0038 {
0039 }
0040 
0041 ReverseMapper::ReverseMapper( AbstractDiagram* diagram )
0042     : m_scene( nullptr )
0043     , m_diagram( diagram )
0044 {
0045 }
0046 
0047 ReverseMapper::~ReverseMapper()
0048 {
0049     delete m_scene; m_scene = nullptr;
0050 }
0051 
0052 void ReverseMapper::setDiagram( AbstractDiagram* diagram )
0053 {
0054 
0055     m_diagram = diagram;
0056 }
0057 
0058 void ReverseMapper::clear()
0059 {
0060     m_itemMap.clear();
0061     delete m_scene;
0062     m_scene = new QGraphicsScene();
0063 }
0064 
0065 QModelIndexList ReverseMapper::indexesIn( const QRect& rect ) const
0066 {
0067     Q_ASSERT( m_diagram );
0068     if ( m_scene && m_scene->sceneRect().intersects( rect ) ) {
0069         QList<QGraphicsItem *> items = m_scene->items( rect );
0070         QModelIndexList indexes;
0071         Q_FOREACH( QGraphicsItem* item, items ) {
0072             ChartGraphicsItem* i = qgraphicsitem_cast<ChartGraphicsItem*>( item );
0073             if ( i ) {
0074                 QModelIndex index ( m_diagram->model()->index( i->row(), i->column(), m_diagram->rootIndex() ) ); // checked
0075                 indexes << index;
0076             }
0077         }
0078         return indexes;
0079     } else {
0080         return QModelIndexList();
0081     }
0082 }
0083 
0084 QModelIndexList ReverseMapper::indexesAt( const QPointF& point ) const
0085 {
0086     Q_ASSERT( m_diagram );
0087     if ( m_scene && m_scene->sceneRect().contains( point ) ) {
0088         QList<QGraphicsItem *> items = m_scene->items( point );
0089         QModelIndexList indexes;
0090         Q_FOREACH( QGraphicsItem* item, items ) {
0091             ChartGraphicsItem* i = qgraphicsitem_cast<ChartGraphicsItem*>( item );
0092             if ( i ) {
0093                 QModelIndex index ( m_diagram->model()->index( i->row(), i->column(), m_diagram->rootIndex() ) ); // checked
0094                 if ( !indexes.contains(index) )
0095                     indexes << index;
0096             }
0097         }
0098         return indexes;
0099     } else {
0100         return QModelIndexList();
0101     }
0102 }
0103 
0104 QPolygonF ReverseMapper::polygon( int row, int column ) const
0105 {
0106     if ( !m_diagram->model()->hasIndex( row, column, m_diagram->rootIndex() ) )
0107         return QPolygon();
0108     const QModelIndex index = m_diagram->model()->index( row, column, m_diagram->rootIndex() ); // checked
0109     return m_itemMap.contains( index ) ? m_itemMap[ index ]->polygon() : QPolygon();
0110 }
0111 
0112 QRectF ReverseMapper::boundingRect( int row, int column ) const
0113 {
0114     if ( !m_diagram->model()->hasIndex( row, column, m_diagram->rootIndex() ) )
0115         return QRectF();
0116     const QModelIndex index = m_diagram->model()->index( row, column, m_diagram->rootIndex() ); // checked
0117     return m_itemMap.contains( index ) ? m_itemMap[ index ]->polygon().boundingRect() : QRectF();
0118 }
0119 
0120 void ReverseMapper::addItem( ChartGraphicsItem* item )
0121 {
0122     Q_ASSERT( m_scene );
0123     m_scene->addItem( item );
0124     m_itemMap.insert( m_diagram->model()->index( item->row(), item->column(), m_diagram->rootIndex() ), item ); // checked
0125 }
0126 
0127 void ReverseMapper::addRect( int row, int column, const QRectF& rect )
0128 {
0129     addPolygon( row, column, QPolygonF( rect ) );
0130 }
0131 
0132 void ReverseMapper::addPolygon( int row, int column, const QPolygonF& polygon )
0133 {
0134     ChartGraphicsItem* item = new ChartGraphicsItem( row, column );
0135     item->setPolygon( polygon );
0136     addItem( item );
0137 }
0138 
0139 void ReverseMapper::addCircle( int row, int column, const QPointF& location, const QSizeF& diameter )
0140 {
0141     QPainterPath path;
0142     QPointF ossfet( -0.5*diameter.width(), -0.5*diameter.height() );
0143     path.addEllipse( QRectF( location + ossfet, diameter ) );
0144     addPolygon( row, column, QPolygonF( path.toFillPolygon() ) );
0145 }
0146 
0147 void ReverseMapper::addLine( int row, int column, const QPointF& from, const QPointF& to )
0148 {
0149     // that's no line, dude... make a small circle around that point, instead
0150     if ( from == to )
0151     {
0152         addCircle( row, column, from, QSizeF( 1.5, 1.5 ) );
0153         return;
0154     }
0155     // lines do not make good polygons to click on. we calculate a 2
0156     // pixel wide rectangle, where the original line is excatly
0157     // centered in.
0158     // make a 3 pixel wide polygon from the line:
0159     QPointF left, right;
0160     if ( from.x() < to.x() ) {
0161         left = from;
0162         right = to;
0163     } else {
0164         right = from;
0165         left = to;
0166     }
0167     const QPointF lineVector( right - left );
0168     const qreal lineVectorLength = sqrt( lineVector.x() * lineVector.x() + lineVector.y() * lineVector.y() );
0169     const QPointF lineVectorUnit( lineVector / lineVectorLength );
0170     const QPointF normOfLineVectorUnit( -lineVectorUnit.y(), lineVectorUnit.x() );
0171     // now the four polygon end points:
0172     const QPointF one( left - lineVectorUnit + normOfLineVectorUnit );
0173     const QPointF two( left - lineVectorUnit - normOfLineVectorUnit );
0174     const QPointF three( right + lineVectorUnit - normOfLineVectorUnit );
0175     const QPointF four( right + lineVectorUnit + normOfLineVectorUnit );
0176     addPolygon( row, column, QPolygonF() << one << two << three << four );
0177 }