KD Chart API Documentation  3.1
KDChartTernaryGrid.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** This file is part of the KD Chart library.
4 **
5 ** SPDX-FileCopyrightText: 2001 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6 **
7 ** SPDX-License-Identifier: MIT
8 **
9 ****************************************************************************/
10 
11 #include "KDChartTernaryGrid.h"
12 
13 #include <cmath>
14 
15 #include <algorithm>
16 #include <limits>
17 
18 #include <QApplication>
19 #include <QtDebug>
20 
21 #include "KDChartPaintContext.h"
22 #include "KDChartPainterSaver_p.h"
25 #include "TernaryConstants.h"
26 #include "TernaryPoint.h"
27 
28 using namespace KDChart;
29 
30 TickInfo::TickInfo(qreal _percentage, int _depth)
31  : percentage(_percentage)
32  , depth(_depth)
33 {
34 }
35 
36 bool KDChart::operator==(const TickInfo &left, const TickInfo &right)
37 {
38  return fabs(left.percentage - right.percentage)
39  <= std::numeric_limits<qreal>::epsilon()
40  && left.depth == right.depth;
41 }
42 
44  : AbstractGrid()
45 {
46 }
47 
49 {
50 }
51 
53 {
54  static const int GridLineDistanceTreshold = 20; // <Threshold> pixels between each grid line
55 
56  QPainter &painter = *context->painter(); // recover from pointer madness
57  PainterSaver s(&painter); // can i have a reference based version of that?
58  auto *plane = dynamic_cast<TernaryCoordinatePlane *>(context->coordinatePlane());
59  Q_ASSERT_X(plane, "TernaryGrid::drawGrid",
60  "Bad function call: PaintContext::coodinatePlane() NOT a ternary plane.");
61 
62  // translate the points and see how many grid lines we can draw:
63  const int MaxDepth = 3;
64  qreal xPixels = plane->translate(TriangleBottomRight).x() - plane->translate(TriangleBottomLeft).x();
65  int granularity = 20;
66  if (xPixels > 10 * GridLineDistanceTreshold)
67  granularity = 10;
68  if (xPixels > 20 * GridLineDistanceTreshold)
69  granularity = 5;
70 
71  m_tickInfo.clear();
72  for (int i = granularity; i < 100; i += granularity) {
73  TickInfo tick((1.0 * i) / 100.0, 2);
74  if (i % 10 == 0)
75  tick.depth = 1;
76  if (i % 20 == 0)
77  tick.depth = 0;
78  m_tickInfo.append(tick);
79  }
80 
81  QVector<QLineF> lines[MaxDepth];
82  {
83  for (const TickInfo &tick : qAsConst(m_tickInfo)) {
84  const qreal &percent = tick.percentage;
85  { // draw parallels to B
86  TernaryPoint ternaryStart(percent, 1.0 - percent);
87  TernaryPoint ternaryEnd(0.0, 1.0 - percent);
88  QPointF start(translate(ternaryStart));
89  QPointF end(translate(ternaryEnd));
90  lines[tick.depth].append(QLineF(plane->translate(start),
91  plane->translate(end)));
92  }
93  { // draw parallels to C
94  TernaryPoint ternaryStart(percent, 0.0);
95  TernaryPoint ternaryEnd(0.0, percent);
96  QPointF start(translate(ternaryStart));
97  QPointF end(translate(ternaryEnd));
98  lines[tick.depth].append(QLineF(plane->translate(start),
99  plane->translate(end)));
100  }
101  { // draw parallels to A
102  TernaryPoint ternaryStart(percent, 1.0 - percent);
103  TernaryPoint ternaryEnd(percent, 0.0);
104  QPointF start(translate(ternaryStart));
105  QPointF end(translate(ternaryEnd));
106  lines[tick.depth].append(QLineF(plane->translate(start),
107  plane->translate(end)));
108  }
109  }
110  }
111 
112  // now draw the lines:
113  painter.setPen(PrintingParameters::scalePen(QPen(QColor("lightgray"), 1)));
114  painter.setBrush(QColor("lightgray"));
115  painter.drawLines(lines[2]);
116  painter.setPen(PrintingParameters::scalePen(QPen(QColor("gray"), 1)));
117  painter.setBrush(QColor("gray"));
118  painter.drawLines(lines[1]);
119  painter.setPen(PrintingParameters::scalePen(QPen(QColor("darkslategray"), 1)));
120  painter.setBrush(QColor("darkslategray"));
121  painter.drawLines(lines[0]);
122 
123  // now draw the triangle (this could be part of the axis, in fact):
124  painter.setPen(PrintingParameters::scalePen(QPen(Qt::black, 1)));
125  // make sure this does not fill, otherwise it wipes the contents
126  // of the triangle (doh!):
127  painter.setBrush(Qt::NoBrush);
128  QPolygonF points;
129  points << plane->translate(TriangleBottomLeft)
130  << plane->translate(TriangleBottomRight)
131  << plane->translate(TriangleTop);
132  painter.drawPolygon(points);
133 
134  // now draw the ticks:
135  painter.setPen(PrintingParameters::scalePen(QPen(Qt::black)));
136  painter.setBrush(Qt::black);
137 
138  QVector<QLineF> ticks;
139  // prepare list of percentages, then calculate lines:
140  QVector<TickInfo> percentages(m_tickInfo);
141  // I have commented those out, I think it looks ugly if they are
142  // enabled:
143  // percentages.prepend( 0.0 );
144  // percentages.append( 1.0 );
145 
146  // FIXME this may need a predicate that takes eplison into account
147  // (but it does not hurt, since it will not make the painter
148  // paint two lines):
149  percentages.erase(std::unique(percentages.begin(), percentages.end()),
150  percentages.end());
151 
152  {
153  for (const TickInfo &tick : percentages) {
154  const qreal &percent = tick.percentage;
155  { // BC axis markers:
156  const QPointF markerDistance(FullMarkerDistanceBC
157  / (tick.depth + 1));
158  QPointF start(percent, 0.0);
159  ticks.append(QLineF(plane->translate(start),
160  plane->translate(start - markerDistance)));
161  }
162  { // AC axis markers:
163  const QPointF markerDistance(FullMarkerDistanceAC
164  / (tick.depth + 1));
165  const QPointF start(TriangleBottomRight + percent * AxisVector_C_A);
166  const QPointF end(start + markerDistance);
167  ticks.append(QLineF(plane->translate(start),
168  plane->translate(end)));
169  }
170  {
171  // AB axis markers:
172  const QPointF markerDistance(FullMarkerDistanceBA
173  / (tick.depth + 1));
174  const QPointF start(percent * AxisVector_B_A);
175  const QPointF end(start + markerDistance);
176  ticks.append(QLineF(plane->translate(start),
177  plane->translate(end)));
178  }
179  }
180  }
181  painter.drawLines(ticks);
182 }
183 
185 {
186  return DataDimensionsList();
187 }
188 
189 QPair<QSizeF, QSizeF> TernaryGrid::requiredMargins() const
190 {
191  // qreal topMargin = ( FullMarkerDistanceBA * RelMarkerLength ).x();
192  qreal topMargin = 0.0; // no markers on tip of triangle
193  qreal leftMargin = fabs(FullMarkerDistanceBA.x());
194  qreal bottomMargin = fabs(FullMarkerDistanceBC.y());
195  // qDebug() << "TernaryGrid::requiredMargins: leftMargin:" << leftMargin
196  // << ", bottomMargin:" << bottomMargin
197  // << ", topMargin:" << topMargin
198  // << ", FullMarkerDistanceBC:" << FullMarkerDistanceBC
199  // << ", FullMarkerDistanceBA:" << FullMarkerDistanceBA
200  // << ", FullMarkerDistanceAC:" << FullMarkerDistanceAC
201  // << ", RelMarkerLength:" << RelMarkerLength;
202  return QPair<QSizeF, QSizeF>(QSizeF(leftMargin, topMargin),
203  QSizeF(leftMargin, bottomMargin));
204 }
205 
206 const QVector<TickInfo> &TernaryGrid::tickInfo() const
207 {
208  return m_tickInfo;
209 }
const QPointF FullMarkerDistanceBA
const QPointF TriangleTop
const QPointF FullMarkerDistanceAC
const QPointF FullMarkerDistanceBC
const QPointF TriangleBottomLeft
const QPointF AxisVector_B_A
const QPointF TriangleBottomRight
const QPointF AxisVector_C_A
QPointF translate(const TernaryPoint &point)
Stores information about painting diagrams.
AbstractCoordinatePlane * coordinatePlane() const
QPainter * painter() const
static QPen scalePen(const QPen &pen)
void drawGrid(PaintContext *context) override
QPair< QSizeF, QSizeF > requiredMargins() const
DataDimensionsList calculateGrid(const DataDimensionsList &rawDataDimensions) const override
Calculates the grid start/end/step width values.
const QVector< TickInfo > & tickInfo() const
TernaryPoint defines a point within a ternary coordinate plane.
Definition: TernaryPoint.h:22
bool operator==(const TickInfo &, const TickInfo &)
QList< DataDimension > DataDimensionsList
TickInfo(qreal percentage=0, int depth=0)

© 2001 Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-chart/
Generated by doxygen 1.9.1