KChart

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

KDE's Doxygen guidelines are available online.