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 }