File indexing completed on 2024-12-15 04:02:36
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 "KChartTernaryCoordinatePlane.h" 0010 #include "KChartTernaryCoordinatePlane_p.h" 0011 0012 #include <QtDebug> 0013 #include <QPainter> 0014 0015 #include "KChartPaintContext.h" 0016 #include "KChartPainterSaver_p.h" 0017 #include "KChartTernaryAxis.h" 0018 #include "KChartAbstractTernaryDiagram.h" 0019 0020 #include "TernaryConstants.h" 0021 0022 using namespace KChart; 0023 0024 #define d d_func() 0025 0026 TernaryCoordinatePlane::Private::Private() 0027 : AbstractCoordinatePlane::Private() 0028 { 0029 } 0030 0031 TernaryCoordinatePlane::TernaryCoordinatePlane( Chart* parent ) 0032 : AbstractCoordinatePlane( new Private(), parent ) 0033 { 0034 } 0035 0036 TernaryCoordinatePlane::~TernaryCoordinatePlane() 0037 { 0038 } 0039 0040 void TernaryCoordinatePlane::init() 0041 { 0042 } 0043 0044 void TernaryCoordinatePlane::addDiagram( AbstractDiagram* diagram ) 0045 { 0046 Q_ASSERT_X ( dynamic_cast<AbstractTernaryDiagram*>( diagram ), 0047 "TernaryCoordinatePlane::addDiagram", "Only ternary " 0048 "diagrams can be added to a ternary coordinate plane!" ); 0049 AbstractCoordinatePlane::addDiagram ( diagram ); 0050 } 0051 0052 void TernaryCoordinatePlane::layoutDiagrams() 0053 { // this is our "resize event": 0054 // all diagrams always take the same space, nothing to be done here 0055 // the "inner" margin (adjustments to diagram coordinates) 0056 QRectF diagramNativeRectangle ( QPointF( 0.0, 0.0 ), 0057 QSizeF( TriangleWidth, TriangleHeight ) ); 0058 QPair<QSizeF, QSizeF> margins = grid()->requiredMargins(); 0059 d->diagramRect = areaGeometry(); 0060 diagramNativeRectangle.adjust 0061 (-margins.first.width(), -margins.first.height(), 0062 margins.second.width(), margins.second.height() ); 0063 0064 // the "outer" margin (distance between diagram contents and area, 0065 // determined by axis label overlap 0066 { 0067 QSizeF topleft( 0.0, 0.0 ); 0068 QSizeF bottomRight( 0.0, 0.0 ); 0069 const auto ds = diagrams(); 0070 for ( AbstractDiagram* abstractDiagram : ds ) { 0071 AbstractTernaryDiagram* diagram = 0072 qobject_cast<AbstractTernaryDiagram*>( abstractDiagram ); 0073 Q_ASSERT( diagram ); 0074 const auto axes = diagram->axes(); 0075 for ( TernaryAxis* axis : axes ) { 0076 QPair<QSizeF, QSizeF> margin = axis->requiredMargins(); 0077 topleft = topleft.expandedTo( margin.first ); 0078 bottomRight = bottomRight.expandedTo( margin.second ); 0079 } 0080 } 0081 d->diagramRectContainer = 0082 d->diagramRect.adjusted( topleft.width(), 0083 topleft.height(), 0084 -bottomRight.width(), 0085 -bottomRight.height() ); 0086 } 0087 0088 // now calculate isometric projection, x and y widget coordinate 0089 // units, and location of (0.0, 0.0) in diagram coordinates 0090 QPointF zeroZeroPoint = d->diagramRectContainer.bottomLeft(); 0091 qreal w = d->diagramRectContainer.width(); 0092 qreal h = d->diagramRectContainer.height(); 0093 qreal usableWidth; 0094 qreal usableHeight; 0095 0096 if ( TriangleHeight * w > h ) { 0097 // shorten width: 0098 usableWidth = h / diagramNativeRectangle.height(); 0099 usableHeight = h; 0100 zeroZeroPoint.setX( zeroZeroPoint.x() + ( w - usableWidth ) / 2 ); 0101 } else { 0102 // reduce height: 0103 usableWidth = w; 0104 usableHeight = diagramNativeRectangle.height() * w; 0105 zeroZeroPoint.setY( zeroZeroPoint.y() - ( h - usableHeight ) / 2 ); 0106 } 0107 // the rectangle has 1 as it's width, and TriangleHeight as it's 0108 // height - so this is how we translate that to widget coordinates: 0109 d->xUnit = usableWidth / diagramNativeRectangle.width(); // only because we normalize the values to [0..1] 0110 d->yUnit = -usableHeight / diagramNativeRectangle.height(); 0111 0112 // now move zeroZeroPoint so that it does not include the tick marks 0113 { 0114 qreal descent = diagramNativeRectangle.height() - TriangleHeight; 0115 qreal rightShift = -diagramNativeRectangle.x(); 0116 zeroZeroPoint += QPointF( rightShift * d->xUnit, descent * d->yUnit ); 0117 } 0118 0119 d->diagramRect.setBottomLeft( zeroZeroPoint ); 0120 d->diagramRect.setTopRight( QPointF( usableWidth, -usableHeight ) + zeroZeroPoint ); 0121 } 0122 0123 const QPointF TernaryCoordinatePlane::translate( const QPointF& point ) const 0124 { 0125 return QPointF( d->diagramRect.bottomLeft().x() + point.x() * d->xUnit, 0126 d->diagramRect.bottomLeft().y() + point.y() * d->yUnit ); 0127 } 0128 0129 QSize TernaryCoordinatePlane::minimumSizeHint() const 0130 { 0131 // FIXME temp 0132 return QSize(); 0133 } 0134 0135 QSizePolicy TernaryCoordinatePlane::sizePolicy() const 0136 { 0137 return QSizePolicy( QSizePolicy::MinimumExpanding, 0138 QSizePolicy::MinimumExpanding ); 0139 } 0140 0141 void TernaryCoordinatePlane::paint( QPainter* painter ) 0142 { 0143 PainterSaver s( painter ); 0144 // FIXME: this is not a good location for that: 0145 painter->setRenderHint(QPainter::Antialiasing, true ); 0146 0147 AbstractDiagramList diags = diagrams(); 0148 if ( !diags.isEmpty() ) 0149 { 0150 PaintContext ctx; 0151 ctx.setPainter ( painter ); 0152 ctx.setCoordinatePlane ( this ); 0153 const QRectF drawArea( areaGeometry() ); 0154 ctx.setRectangle ( drawArea ); 0155 0156 // paint the coordinate system rulers: 0157 Q_ASSERT( d->grid != nullptr ); 0158 d->grid->drawGrid( &ctx ); 0159 0160 // paint the diagrams: 0161 for ( int i = 0; i < diags.size(); i++ ) 0162 { 0163 PainterSaver diagramPainterSaver( painter ); 0164 diags[i]->paint ( &ctx ); 0165 } 0166 } 0167 } 0168 0169 DataDimensionsList TernaryCoordinatePlane::getDataDimensionsList() const 0170 { // not needed 0171 return DataDimensionsList(); 0172 } 0173 0174 TernaryGrid* TernaryCoordinatePlane::grid() const 0175 { 0176 TernaryGrid* ternaryGrid = static_cast<TernaryGrid*>( d->grid ); 0177 Q_ASSERT( dynamic_cast<TernaryGrid*>( d->grid ) ); 0178 return ternaryGrid; 0179 } 0180 0181 #undef d