Skip to content
Snippets Groups Projects
qwt_raster_data.cpp 11.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • pixhawk's avatar
    pixhawk committed
    /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
     * Qwt Widget Library
     * Copyright (C) 1997   Josef Wilgen
     * Copyright (C) 2002   Uwe Rathmann
     *
     * This library is free software; you can redistribute it and/or
     * modify it under the terms of the Qwt License, Version 1.0
     *****************************************************************************/
    
    #include "qwt_raster_data.h"
    
    Bryant's avatar
    Bryant committed
    #include "qwt_point_3d.h"
    #include <qnumeric.h>
    
    pixhawk's avatar
    pixhawk committed
    
    class QwtRasterData::ContourPlane
    {
    public:
    
    Bryant's avatar
    Bryant committed
        inline ContourPlane( double z ):
            d_z( z )
        {
    
    pixhawk's avatar
    pixhawk committed
        }
    
    
    Bryant's avatar
    Bryant committed
        inline bool intersect( const QwtPoint3D vertex[3],
            QPointF line[2], bool ignoreOnPlane ) const;
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        inline double z() const { return d_z; }
    
    pixhawk's avatar
    pixhawk committed
    
    private:
    
    Bryant's avatar
    Bryant committed
        inline int compare( double z ) const;
        inline QPointF intersection(
            const QwtPoint3D& p1, const QwtPoint3D &p2 ) const;
    
    pixhawk's avatar
    pixhawk committed
    
        double d_z;
    };
    
    inline bool QwtRasterData::ContourPlane::intersect(
    
    Bryant's avatar
    Bryant committed
        const QwtPoint3D vertex[3], QPointF line[2],
        bool ignoreOnPlane ) const
    
    pixhawk's avatar
    pixhawk committed
    {
        bool found = true;
    
        // Are the vertices below (-1), on (0) or above (1) the plan ?
    
    Bryant's avatar
    Bryant committed
        const int eq1 = compare( vertex[0].z() );
        const int eq2 = compare( vertex[1].z() );
        const int eq3 = compare( vertex[2].z() );
    
    pixhawk's avatar
    pixhawk committed
    
        /*
            (a) All the vertices lie below the contour level.
            (b) Two vertices lie below and one on the contour level.
            (c) Two vertices lie below and one above the contour level.
            (d) One vertex lies below and two on the contour level.
            (e) One vertex lies below, one on and one above the contour level.
            (f) One vertex lies below and two above the contour level.
            (g) Three vertices lie on the contour level.
            (h) Two vertices lie on and one above the contour level.
            (i) One vertex lies on and two above the contour level.
            (j) All the vertices lie above the contour level.
         */
    
    
    Bryant's avatar
    Bryant committed
        static const int tab[3][3][3] =
        {
    
    pixhawk's avatar
    pixhawk committed
            // jump table to avoid nested case statements
            { { 0, 0, 8 }, { 0, 2, 5 }, { 7, 6, 9 } },
            { { 0, 3, 4 }, { 1, 10, 1 }, { 4, 3, 0 } },
            { { 9, 6, 7 }, { 5, 2, 0 }, { 8, 0, 0 } }
        };
    
        const int edgeType = tab[eq1+1][eq2+1][eq3+1];
    
    Bryant's avatar
    Bryant committed
        switch ( edgeType )
        {
            case 1:
                // d(0,0,-1), h(0,0,1)
                line[0] = vertex[0].toPoint();
                line[1] = vertex[1].toPoint();
                break;
            case 2:
                // d(-1,0,0), h(1,0,0)
                line[0] = vertex[1].toPoint();
                line[1] = vertex[2].toPoint();
                break;
            case 3:
                // d(0,-1,0), h(0,1,0)
                line[0] = vertex[2].toPoint();
                line[1] = vertex[0].toPoint();
                break;
            case 4:
                // e(0,-1,1), e(0,1,-1)
                line[0] = vertex[0].toPoint();
                line[1] = intersection( vertex[1], vertex[2] );
                break;
            case 5:
                // e(-1,0,1), e(1,0,-1)
                line[0] = vertex[1].toPoint();
                line[1] = intersection( vertex[2], vertex[0] );
                break;
            case 6:
                // e(-1,1,0), e(1,0,-1)
                line[0] = vertex[2].toPoint();
                line[1] = intersection( vertex[0], vertex[1] );
                break;
            case 7:
                // c(-1,1,-1), f(1,1,-1)
                line[0] = intersection( vertex[0], vertex[1] );
                line[1] = intersection( vertex[1], vertex[2] );
                break;
            case 8:
                // c(-1,-1,1), f(1,1,-1)
                line[0] = intersection( vertex[1], vertex[2] );
                line[1] = intersection( vertex[2], vertex[0] );
                break;
            case 9:
                // f(-1,1,1), c(1,-1,-1)
                line[0] = intersection( vertex[2], vertex[0] );
                line[1] = intersection( vertex[0], vertex[1] );
                break;
            case 10:
                // g(0,0,0)
                // The CONREC algorithm has no satisfying solution for
                // what to do, when all vertices are on the plane.
    
                if ( ignoreOnPlane )
                    found = false;
                else
                {
                    line[0] = vertex[2].toPoint();
                    line[1] = vertex[0].toPoint();
                }
                break;
            default:
    
                found = false;
    
    pixhawk's avatar
    pixhawk committed
        }
    
        return found;
    }
    
    
    Bryant's avatar
    Bryant committed
    inline int QwtRasterData::ContourPlane::compare( double z ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        if ( z > d_z )
    
    pixhawk's avatar
    pixhawk committed
            return 1;
    
    
    Bryant's avatar
    Bryant committed
        if ( z < d_z )
    
    pixhawk's avatar
    pixhawk committed
            return -1;
    
        return 0;
    }
    
    
    Bryant's avatar
    Bryant committed
    inline QPointF QwtRasterData::ContourPlane::intersection(
        const QwtPoint3D& p1, const QwtPoint3D &p2 ) const
    
    pixhawk's avatar
    pixhawk committed
    {
        const double h1 = p1.z() - d_z;
        const double h2 = p2.z() - d_z;
    
    
    Bryant's avatar
    Bryant committed
        const double x = ( h2 * p1.x() - h1 * p2.x() ) / ( h2 - h1 );
        const double y = ( h2 * p1.y() - h1 * p2.y() ) / ( h2 - h1 );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        return QPointF( x, y );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    
    Bryant's avatar
    Bryant committed
    //! Constructor
    
    pixhawk's avatar
    pixhawk committed
    QwtRasterData::QwtRasterData()
    {
    }
    
    
    Bryant's avatar
    Bryant committed
    //! Destructor
    
    pixhawk's avatar
    pixhawk committed
    QwtRasterData::~QwtRasterData()
    {
    }
    
    
    Bryant's avatar
    Bryant committed
    /*!
       Set the bounding interval for the x, y or z coordinates.
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
       \param axis Axis
       \param interval Bounding interval
    
       \sa interval()
    */
    void QwtRasterData::setInterval( Qt::Axis axis, const QwtInterval &interval )
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        d_intervals[axis] = interval;
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
      \brief Initialize a raster
    
    
    Bryant's avatar
    Bryant committed
      Before the composition of an image QwtPlotSpectrogram calls initRaster(),
    
    pixhawk's avatar
    pixhawk committed
      announcing the area and its resolution that will be requested.
    
    pixhawk's avatar
    pixhawk committed
      The default implementation does nothing, but for data sets that
    
    Bryant's avatar
    Bryant committed
      are stored in files, it might be good idea to reimplement initRaster(),
    
    pixhawk's avatar
    pixhawk committed
      where the data is resampled and loaded into memory.
    
    Bryant's avatar
    Bryant committed
      \param area Area of the raster
    
    pixhawk's avatar
    pixhawk committed
      \param raster Number of horizontal and vertical pixels
    
      \sa initRaster(), value()
    */
    
    Bryant's avatar
    Bryant committed
    void QwtRasterData::initRaster( const QRectF &area, const QSize &raster )
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        Q_UNUSED( area );
        Q_UNUSED( raster );
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
      \brief Discard a raster
    
      After the composition of an image QwtPlotSpectrogram calls discardRaster().
    
    pixhawk's avatar
    pixhawk committed
      The default implementation does nothing, but if data has been loaded
      in initRaster(), it could deleted now.
    
      \sa initRaster(), value()
    */
    void QwtRasterData::discardRaster()
    {
    }
    
    /*!
    
    Bryant's avatar
    Bryant committed
       \brief Pixel hint
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
       pixelHint() returns the geometry of a pixel, that can be used 
       to calculate the resolution and alignment of the plot item, that is
       representing the data. 
       
       Width and height of the hint need to be the horizontal  
       and vertical distances between 2 neighbored points. 
       The center of the hint has to be the position of any point 
       ( it doesn't matter which one ).
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
       An empty hint indicates, that there are values for any detail level.
    
    Bryant's avatar
    Bryant committed
       Limiting the resolution of the image might significantly improve
       the performance and heavily reduce the amount of memory when rendering
       a QImage from the raster data. 
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
       The default implementation returns an empty rectangle recommending
       to render in target device ( f.e. screen ) resolution.
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
       \param area In most implementations the resolution of the data doesn't
                   depend on the requested area.
    
       \return Bounding rectangle of a pixel 
    
    pixhawk's avatar
    pixhawk committed
    */
    
    Bryant's avatar
    Bryant committed
    QRectF QwtRasterData::pixelHint( const QRectF &area ) const
    
    pixhawk's avatar
    pixhawk committed
    {
    
    Bryant's avatar
    Bryant committed
        Q_UNUSED( area );
        return QRectF(); 
    
    pixhawk's avatar
    pixhawk committed
    }
    
    /*!
       Calculate contour lines
    
    Bryant's avatar
    Bryant committed
       \param rect Bounding rectangle for the contour lines
       \param raster Number of data pixels of the raster data
       \param levels List of limits, where to insert contour lines
       \param flags Flags to customize the contouring algorithm
    
       \return Calculated contour lines
    
    
    pixhawk's avatar
    pixhawk committed
       An adaption of CONREC, a simple contouring algorithm.
       http://local.wasp.uwa.edu.au/~pbourke/papers/conrec/
    
    pixhawk's avatar
    pixhawk committed
    QwtRasterData::ContourLines QwtRasterData::contourLines(
    
    Bryant's avatar
    Bryant committed
        const QRectF &rect, const QSize &raster,
        const QList<double> &levels, ConrecFlags flags ) const
    
    pixhawk's avatar
    pixhawk committed
        ContourLines contourLines;
    
    pixhawk's avatar
    pixhawk committed
        if ( levels.size() == 0 || !rect.isValid() || !raster.isValid() )
            return contourLines;
    
        const double dx = rect.width() / raster.width();
        const double dy = rect.height() / raster.height();
    
        const bool ignoreOnPlane =
            flags & QwtRasterData::IgnoreAllVerticesOnLevel;
    
    
    Bryant's avatar
    Bryant committed
        const QwtInterval range = interval( Qt::ZAxis );
    
    pixhawk's avatar
    pixhawk committed
        bool ignoreOutOfRange = false;
        if ( range.isValid() )
            ignoreOutOfRange = flags & IgnoreOutOfRange;
    
    
    Bryant's avatar
    Bryant committed
        QwtRasterData *that = const_cast<QwtRasterData *>( this );
        that->initRaster( rect, raster );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
        for ( int y = 0; y < raster.height() - 1; y++ )
        {
            enum Position
            {
    
    pixhawk's avatar
    pixhawk committed
                Center,
    
                TopLeft,
                TopRight,
                BottomRight,
                BottomLeft,
    
                NumPositions
            };
    
    
    Bryant's avatar
    Bryant committed
            QwtPoint3D xy[NumPositions];
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
            for ( int x = 0; x < raster.width() - 1; x++ )
            {
                const QPointF pos( rect.x() + x * dx, rect.y() + y * dy );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
                if ( x == 0 )
                {
                    xy[TopRight].setX( pos.x() );
                    xy[TopRight].setY( pos.y() );
    
    pixhawk's avatar
    pixhawk committed
                    xy[TopRight].setZ(
    
    Bryant's avatar
    Bryant committed
                        value( xy[TopRight].x(), xy[TopRight].y() )
    
    pixhawk's avatar
    pixhawk committed
                    );
    
    
    Bryant's avatar
    Bryant committed
                    xy[BottomRight].setX( pos.x() );
                    xy[BottomRight].setY( pos.y() + dy );
    
    pixhawk's avatar
    pixhawk committed
                    xy[BottomRight].setZ(
    
    Bryant's avatar
    Bryant committed
                        value( xy[BottomRight].x(), xy[BottomRight].y() )
    
    pixhawk's avatar
    pixhawk committed
                    );
                }
    
                xy[TopLeft] = xy[TopRight];
                xy[BottomLeft] = xy[BottomRight];
    
    
    Bryant's avatar
    Bryant committed
                xy[TopRight].setX( pos.x() + dx );
                xy[TopRight].setY( pos.y() );
                xy[BottomRight].setX( pos.x() + dx );
                xy[BottomRight].setY( pos.y() + dy );
    
    pixhawk's avatar
    pixhawk committed
    
                xy[TopRight].setZ(
    
    Bryant's avatar
    Bryant committed
                    value( xy[TopRight].x(), xy[TopRight].y() )
    
    pixhawk's avatar
    pixhawk committed
                );
                xy[BottomRight].setZ(
    
    Bryant's avatar
    Bryant committed
                    value( xy[BottomRight].x(), xy[BottomRight].y() )
    
    pixhawk's avatar
    pixhawk committed
                );
    
                double zMin = xy[TopLeft].z();
                double zMax = zMin;
                double zSum = zMin;
    
    
    Bryant's avatar
    Bryant committed
                for ( int i = TopRight; i <= BottomLeft; i++ )
                {
    
    pixhawk's avatar
    pixhawk committed
                    const double z = xy[i].z();
    
                    zSum += z;
                    if ( z < zMin )
                        zMin = z;
                    if ( z > zMax )
                        zMax = z;
                }
    
    
    Bryant's avatar
    Bryant committed
                if ( qIsNaN( zSum ) )
                {
                    // one of the points is NaN
                    continue;
                }
    
                if ( ignoreOutOfRange )
                {
                    if ( !range.contains( zMin ) || !range.contains( zMax ) )
    
    pixhawk's avatar
    pixhawk committed
                        continue;
                }
    
                if ( zMax < levels[0] ||
    
    Bryant's avatar
    Bryant committed
                    zMin > levels[levels.size() - 1] )
                {
    
    pixhawk's avatar
    pixhawk committed
                    continue;
                }
    
    
    Bryant's avatar
    Bryant committed
                xy[Center].setX( pos.x() + 0.5 * dx );
                xy[Center].setY( pos.y() + 0.5 * dy );
                xy[Center].setZ( 0.25 * zSum );
    
                const int numLevels = levels.size();
                for ( int l = 0; l < numLevels; l++ )
                {
    
    pixhawk's avatar
    pixhawk committed
                    const double level = levels[l];
                    if ( level < zMin || level > zMax )
                        continue;
                    QPolygonF &lines = contourLines[level];
    
    Bryant's avatar
    Bryant committed
                    const ContourPlane plane( level );
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
                    QPointF line[2];
                    QwtPoint3D vertex[3];
    
    pixhawk's avatar
    pixhawk committed
    
    
    Bryant's avatar
    Bryant committed
                    for ( int m = TopLeft; m < NumPositions; m++ )
                    {
    
    pixhawk's avatar
    pixhawk committed
                        vertex[0] = xy[m];
                        vertex[1] = xy[0];
                        vertex[2] = xy[m != BottomLeft ? m + 1 : TopLeft];
    
                        const bool intersects =
    
    Bryant's avatar
    Bryant committed
                            plane.intersect( vertex, line, ignoreOnPlane );
                        if ( intersects )
                        {
    
    pixhawk's avatar
    pixhawk committed
                            lines += line[0];
                            lines += line[1];
                        }
                    }
                }
            }
        }
    
    
    Bryant's avatar
    Bryant committed
        that->discardRaster();
    
    pixhawk's avatar
    pixhawk committed
    
        return contourLines;
    }