KChart

KChartTernaryGrid.cpp
1 /*
2  * Copyright (C) 2001-2015 Klaralvdalens Datakonsult AB. All rights reserved.
3  *
4  * This file is part of the KD Chart library.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
20 #include "KChartTernaryGrid.h"
21 
22 #include <cmath>
23 
24 #include <algorithm>
25 #include <limits>
26 
27 #include <QtDebug>
28 #include <QApplication>
29 
30 #include "TernaryPoint.h"
31 #include "TernaryConstants.h"
32 #include "KChartPaintContext.h"
33 #include "KChartPainterSaver_p.h"
34 #include "KChartTernaryCoordinatePlane.h"
35 #include "KChartPrintingParameters.h"
36 
37 using namespace KChart;
38 
39 TickInfo::TickInfo( qreal _percentage, int _depth )
40  : percentage ( _percentage )
41  , depth( _depth )
42 {
43 }
44 
45 bool KChart::operator==(const TickInfo& left, const TickInfo& right)
46 {
47  return fabs( left.percentage - right.percentage )
48  <= std::numeric_limits<qreal>::epsilon()
49  && left.depth == right.depth;
50 }
51 
52 TernaryGrid::TernaryGrid()
53  : AbstractGrid()
54 {
55 }
56 
57 TernaryGrid::~TernaryGrid()
58 {
59 }
60 
61 void TernaryGrid::drawGrid( PaintContext* context )
62 {
63  static const int GridLineDistanceTreshold = 20; // <Treshold> pixels between each grid line
64 
65  QPainter& painter = *context->painter(); // recover from pointer madness
66  PainterSaver s( &painter ); // can i have a reference based version of that?
67  TernaryCoordinatePlane* plane = dynamic_cast<TernaryCoordinatePlane*>(context->coordinatePlane());
68  Q_ASSERT_X ( plane, "TernaryGrid::drawGrid",
69  "Bad function call: PaintContext::coodinatePlane() NOT a ternary plane." );
70 
71  // translate the points and see how many grid lines we can draw:
72  const int MaxDepth = 3;
73  qreal xPixels = plane->translate( TriangleBottomRight ).x() -
74  plane->translate( TriangleBottomLeft ).x();
75  int granularity = 20;
76  if ( xPixels > 10 * GridLineDistanceTreshold ) granularity = 10;
77  if ( xPixels > 20 * GridLineDistanceTreshold ) granularity = 5;
78 
79  m_tickInfo.clear();
80  for ( int i = granularity; i < 100; i+=granularity )
81  {
82  TickInfo tick( ( 1.0 * i ) / 100.0, 2 );
83  if ( i % 10 == 0 ) tick.depth = 1;
84  if ( i % 20 == 0 ) tick.depth = 0;
85  m_tickInfo.append( tick );
86  }
87 
88  QVector<QLineF> lines[MaxDepth];
89  { for ( const TickInfo& tick : qAsConst(m_tickInfo) ) {
90  const qreal& percent = tick.percentage;
91  { // draw parallels to B
92  TernaryPoint ternaryStart( percent, 1.0 - percent );
93  TernaryPoint ternaryEnd( 0.0, 1.0 - percent );
94  QPointF start( translate( ternaryStart ) );
95  QPointF end( translate( ternaryEnd ) );
96  lines[tick.depth].append( QLineF( plane->translate( start ),
97  plane->translate( end ) ) );
98  }
99  { // draw parallels to C
100  TernaryPoint ternaryStart( percent, 0.0 );
101  TernaryPoint ternaryEnd( 0.0, percent );
102  QPointF start( translate( ternaryStart ) );
103  QPointF end( translate( ternaryEnd ) );
104  lines[tick.depth].append( QLineF( plane->translate( start ),
105  plane->translate( end ) ) );
106  }
107  { // draw parallels to A
108  TernaryPoint ternaryStart( percent, 1.0 - percent );
109  TernaryPoint ternaryEnd( percent, 0.0 );
110  QPointF start( translate( ternaryStart ) );
111  QPointF end( translate( ternaryEnd ) );
112  lines[tick.depth].append( QLineF( plane->translate( start ),
113  plane->translate( end ) ) );
114  }
115  }}
116 
117  // now draw the lines:
118  painter.setPen( PrintingParameters::scalePen( QPen( QColor( "lightgray" ), 1 ) ) );
119  painter.setBrush( QColor( "lightgray" ) );
120  painter.drawLines( lines[2] );
121  painter.setPen( PrintingParameters::scalePen( QPen( QColor( "gray" ), 1 ) ) );
122  painter.setBrush( QColor( "gray" ) );
123  painter.drawLines( lines[1] );
124  painter.setPen( PrintingParameters::scalePen( QPen( QColor( "darkslategray" ), 1 ) ) );
125  painter.setBrush( QColor( "darkslategray" ) );
126  painter.drawLines( lines[0] );
127 
128  // now draw the triangle (this could be part of the axis, in fact):
129  painter.setPen( PrintingParameters::scalePen( QPen( Qt::black, 1 ) ) );
130  // make sure this does not fill, otherwise it wipes the contents
131  // of the triangle (doh!):
132  painter.setBrush( Qt::NoBrush );
133  QPolygonF points;
134  points << plane->translate( TriangleBottomLeft )
135  << plane->translate( TriangleBottomRight )
136  << plane->translate( TriangleTop );
137  painter.drawPolygon( points );
138 
139  // now draw the ticks:
140  painter.setPen( PrintingParameters::scalePen( QPen( Qt::black ) ) );
141  painter.setBrush( Qt::black );
142 
143  QVector<QLineF> ticks;
144  // prepare list of percentages, then calculate lines:
145  QVector<TickInfo> percentages( m_tickInfo );
146  // I have commented those out, I think it looks ugly if they are
147  // enabled:
148  // percentages.prepend( 0.0 );
149  // percentages.append( 1.0 );
150 
151  // FIXME this may need a predicate that takes eplison into account
152  // (but it does not hurt, since it will not make the painter
153  // paint two lines):
154  percentages.erase( std::unique( percentages.begin(), percentages.end() ),
155  percentages.end() );
156 
157  {for ( const TickInfo& tick : qAsConst(percentages) ) {
158  const qreal& percent = tick.percentage;
159  { // BC axis markers:
160  const QPointF markerDistance( FullMarkerDistanceBC
161  / ( tick.depth + 1 ) );
162  QPointF start( percent, 0.0 );
163  ticks.append( QLineF( plane->translate( start ),
164  plane->translate( start - markerDistance ) ) );
165  }
166  { // AC axis markers:
167  const QPointF markerDistance( FullMarkerDistanceAC
168  / ( tick.depth + 1 ) );
169  const QPointF start( TriangleBottomRight + percent * AxisVector_C_A );
170  const QPointF end( start + markerDistance );
171  ticks.append( QLineF( plane->translate( start ),
172  plane->translate( end ) ) );
173  }
174  {
175  // AB axis markers:
176  const QPointF markerDistance( FullMarkerDistanceBA
177  / ( tick.depth +1 ) );
178  const QPointF start( percent * AxisVector_B_A );
179  const QPointF end( start + markerDistance );
180  ticks.append( QLineF( plane->translate( start ),
181  plane->translate( end ) ) );
182  }
183  }}
184  painter.drawLines( ticks );
185 }
186 
187 DataDimensionsList TernaryGrid::calculateGrid( const DataDimensionsList& ) const
188 {
189  return DataDimensionsList();
190 }
191 
192 QPair<QSizeF, QSizeF> TernaryGrid::requiredMargins() const
193 {
194 // qreal topMargin = ( FullMarkerDistanceBA * RelMarkerLength ).x();
195  qreal topMargin = 0.0; // no markers on tip of triangle
196  qreal leftMargin = fabs( FullMarkerDistanceBA.x() );
197  qreal bottomMargin = fabs( FullMarkerDistanceBC.y() );
198 // qDebug() << "TernaryGrid::requiredMargins: leftMargin:" << leftMargin
199 // << ", bottomMargin:" << bottomMargin
200 // << ", topMargin:" << topMargin
201 // << ", FullMarkerDistanceBC:" << FullMarkerDistanceBC
202 // << ", FullMarkerDistanceBA:" << FullMarkerDistanceBA
203 // << ", FullMarkerDistanceAC:" << FullMarkerDistanceAC
204 // << ", RelMarkerLength:" << RelMarkerLength;
205  return QPair<QSizeF, QSizeF>
206  ( QSizeF( leftMargin, topMargin ),
207  QSizeF( leftMargin, bottomMargin ) );
208 }
209 
210 const QVector<TickInfo>& TernaryGrid::tickInfo() const
211 {
212  return m_tickInfo;
213 }
void append(const T &value)
void drawLines(const QLineF *lines, int lineCount)
void drawPolygon(const QPointF *points, int pointCount, Qt::FillRule fillRule)
const QPointF translate(const QPointF &diagramPoint) const override
Translate the given point in value space coordinates to a position in pixel space.
qreal x() const const
qreal y() const const
Stores information about painting diagrams.
void setPen(const QColor &color)
void setBrush(const QBrush &brush)
TernaryPoint defines a point within a ternary coordinate plane.
Definition: TernaryPoint.h:30
const QList< QKeySequence > & end()
Global namespace.
Abstract base class for grid classes: cartesian, polar, ...
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Sep 24 2020 22:36:58 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.