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