File indexing completed on 2024-12-15 04:02:28
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 #ifndef CARTESIANCOORDINATETRANSFORMATION_H 0010 #define CARTESIANCOORDINATETRANSFORMATION_H 0011 0012 #include <QList> 0013 #include <QRectF> 0014 #include <QPointF> 0015 0016 #include "KChartZoomParameters.h" 0017 0018 #include <cmath> 0019 #include <limits> 0020 0021 namespace KChart { 0022 0023 // FIXME: if this struct is used more often, we need to make it a class 0024 // with proper accessor methods: 0025 0026 /** 0027 * \internal 0028 */ 0029 struct CoordinateTransformation { 0030 0031 CoordinateTransformation() 0032 : axesCalcModeY( CartesianCoordinatePlane::Linear ), 0033 axesCalcModeX( CartesianCoordinatePlane::Linear ), 0034 isPositiveX( true ), 0035 isPositiveY( true ) 0036 {} 0037 0038 CartesianCoordinatePlane::AxesCalcMode axesCalcModeY; 0039 CartesianCoordinatePlane::AxesCalcMode axesCalcModeX; 0040 0041 ZoomParameters zoom; 0042 0043 QTransform transform; 0044 QTransform backTransform; 0045 // a logarithmic scale cannot cross zero, so we have to know which side we are on. 0046 bool isPositiveX; 0047 bool isPositiveY; 0048 0049 qreal logTransform( qreal value, bool isPositiveRange ) const 0050 { 0051 if ( isPositiveRange ) { 0052 return log10( value ); 0053 } else { 0054 return -log10( -value ); 0055 } 0056 } 0057 0058 qreal logTransformBack( qreal value, bool wasPositive ) const 0059 { 0060 if ( wasPositive ) { 0061 return pow( 10.0, value ); 0062 } else { 0063 return -pow( 10.0, -value ); 0064 } 0065 } 0066 0067 void updateTransform( const QRectF& constDataRect, const QRectF& screenRect ) 0068 { 0069 QRectF dataRect = constDataRect; 0070 if ( axesCalcModeX == CartesianCoordinatePlane::Logarithmic ) { 0071 // the data will be scaled by logTransform() later, so scale its bounds as well 0072 isPositiveX = dataRect.left() >= 0.0; 0073 dataRect.setLeft( logTransform( dataRect.left(), isPositiveX ) ); 0074 dataRect.setRight( logTransform( dataRect.right(), isPositiveX ) ); 0075 } 0076 if ( axesCalcModeY == CartesianCoordinatePlane::Logarithmic ) { 0077 isPositiveY = dataRect.top() >= 0.0; 0078 dataRect.setTop( logTransform( dataRect.top(), isPositiveY ) ); 0079 dataRect.setBottom( logTransform( dataRect.bottom(), isPositiveY ) ); 0080 } 0081 0082 transform.reset(); 0083 // read the following transformation sequence from bottom to top(!) 0084 transform.translate( screenRect.left(), screenRect.bottom() ); 0085 transform.scale( screenRect.width(), screenRect.height() ); 0086 0087 // TODO: mirror in case of "reverse" axes? 0088 0089 // transform into screen space 0090 transform.translate( 0.5, -0.5 ); 0091 transform.scale( zoom.xFactor, zoom.yFactor ); 0092 transform.translate( -zoom.xCenter, 1.0 - zoom.yCenter ); 0093 // zoom 0094 transform.scale( 1.0 / dataRect.width(), 1.0 / dataRect.height() ); 0095 transform.translate( -dataRect.left(), -dataRect.bottom() ); 0096 // transform into the unit square 0097 0098 backTransform = transform.inverted(); 0099 } 0100 0101 // convert data space point to screen point 0102 inline QPointF translate( const QPointF& dataPoint ) const 0103 { 0104 QPointF data = dataPoint; 0105 if ( axesCalcModeX == CartesianCoordinatePlane::Logarithmic ) { 0106 data.setX( logTransform( data.x(), isPositiveX ) ); 0107 } 0108 if ( axesCalcModeY == CartesianCoordinatePlane::Logarithmic ) { 0109 data.setY( logTransform( data.y(), isPositiveY ) ); 0110 } 0111 0112 return transform.map( data ); 0113 } 0114 0115 // convert screen point to data space point 0116 inline const QPointF translateBack( const QPointF& screenPoint ) const 0117 { 0118 QPointF ret = backTransform.map( screenPoint ); 0119 if ( axesCalcModeX == CartesianCoordinatePlane::Logarithmic ) { 0120 ret.setX( logTransformBack( ret.x(), isPositiveX ) ); 0121 } 0122 if ( axesCalcModeY == CartesianCoordinatePlane::Logarithmic ) { 0123 ret.setY( logTransformBack( ret.y(), isPositiveY ) ); 0124 } 0125 return ret; 0126 } 0127 }; 0128 0129 typedef QList<CoordinateTransformation> CoordinateTransformationList; 0130 0131 } 0132 0133 #endif