qwt_bezier.cpp
Go to the documentation of this file.
00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
00008  *****************************************************************************/
00009 
00010 #include "qwt_bezier.h"
00011 #include <qstack.h>
00012 
00013 namespace 
00014 {
00015     class BezierData
00016     {
00017     public:
00018         inline BezierData()
00019         {
00020             // default constructor with unitialized points
00021         }
00022 
00023         inline BezierData( const QPointF &p1, const QPointF &cp1,
00024                 const QPointF &cp2, const QPointF &p2 ):
00025             d_x1( p1.x() ),
00026             d_y1( p1.y() ),
00027             d_cx1( cp1.x() ),
00028             d_cy1( cp1.y() ),
00029             d_cx2( cp2.x() ),
00030             d_cy2( cp2.y() ),
00031             d_x2( p2.x() ),
00032             d_y2( p2.y() )
00033         {
00034         }
00035 
00036         static inline double minFlatness( double tolerance )
00037         {
00038             // we can make simplify the tolerance criterion check in
00039             // the subdivison loop cheaper, by precalculating some
00040             // flatness value.
00041 
00042             return 16 * ( tolerance * tolerance );
00043         }
00044 
00045         inline double flatness() const
00046         {
00047             // algo by Roger Willcocks ( http://www.rops.org )
00048 
00049             const double ux = 3.0 * d_cx1 - 2.0 * d_x1 - d_x2;
00050             const double uy = 3.0 * d_cy1 - 2.0 * d_y1 - d_y2;
00051             const double vx = 3.0 * d_cx2 - 2.0 * d_x2 - d_x1;
00052             const double vy = 3.0 * d_cy2 - 2.0 * d_y2 - d_y1;
00053 
00054             const double ux2 = ux * ux;
00055             const double uy2 = uy * uy;
00056 
00057             const double vx2 = vx * vx;
00058             const double vy2 = vy * vy;
00059 
00060             return qMax( ux2, vx2 ) + qMax( uy2, vy2 );
00061         }
00062 
00063         inline BezierData subdivided()
00064         {
00065             BezierData bz;
00066 
00067             const double c1 = midValue( d_cx1, d_cx2 );
00068 
00069             bz.d_cx1 = midValue( d_x1, d_cx1 );
00070             d_cx2 = midValue( d_cx2, d_x2 );
00071             bz.d_x1 = d_x1;
00072             bz.d_cx2 = midValue( bz.d_cx1, c1 );
00073             d_cx1 = midValue( c1, d_cx2 );
00074             bz.d_x2 = d_x1 = midValue( bz.d_cx2, d_cx1 );
00075 
00076             const double c2 = midValue( d_cy1, d_cy2 );
00077 
00078             bz.d_cy1 = midValue( d_y1, d_cy1 );
00079             d_cy2 = midValue( d_cy2, d_y2 );
00080             bz.d_y1 = d_y1;
00081             bz.d_cy2 = midValue( bz.d_cy1, c2 );
00082             d_cy1 = midValue( d_cy2, c2 );
00083             bz.d_y2 = d_y1 = midValue( bz.d_cy2, d_cy1 );
00084 
00085             return bz;
00086         }
00087 
00088         inline QPointF p2() const
00089         {
00090             return QPointF( d_x2, d_y2 );
00091         }
00092 
00093     private:
00094         inline double midValue( double v1, double v2 )
00095         {
00096             return 0.5 * ( v1 + v2 );
00097         }
00098 
00099         double d_x1, d_y1;
00100         double d_cx1, d_cy1;
00101         double d_cx2, d_cy2;
00102         double d_x2, d_y2;
00103     };
00104 }
00105 
00113 QwtBezier::QwtBezier( double tolerance ):
00114     m_tolerance( qMax( tolerance, 0.0 ) ),
00115     m_flatness( BezierData::minFlatness( m_tolerance ) )
00116 {
00117 }
00118 
00120 QwtBezier::~QwtBezier()
00121 {
00122 }
00123 
00138 void QwtBezier::setTolerance( double tolerance )
00139 {
00140     m_tolerance = qMax( tolerance, 0.0 );
00141     m_flatness = BezierData::minFlatness( m_tolerance );
00142 }
00143 
00154 QPolygonF QwtBezier::toPolygon( const QPointF &p1,
00155     const QPointF &cp1, const QPointF &cp2, const QPointF &p2 ) const
00156 {
00157     QPolygonF polygon;
00158 
00159     if ( m_flatness > 0.0 )
00160     {
00161         // a flatness of 0.0 is not achievable
00162         appendToPolygon( p1, cp1, cp2, p2, polygon );
00163     }
00164 
00165     return polygon;
00166 }
00167 
00183 void QwtBezier::appendToPolygon( const QPointF &p1, const QPointF &cp1,
00184     const QPointF &cp2, const QPointF &p2, QPolygonF &polygon ) const
00185 {
00186     if ( m_flatness <= 0.0 )
00187     {
00188         // a flatness of 0.0 is not achievable
00189         return;
00190     }
00191 
00192     if ( polygon.isEmpty() || polygon.last() != p1 )
00193         polygon += p1;
00194 
00195     // to avoid deep stacks we convert the recursive algo
00196     // to something iterative, where the parameters of the
00197     // recursive class are pushed to a stack instead
00198 
00199     QStack<BezierData> stack;
00200     stack.push( BezierData( p1, cp1, cp2, p2 ) );
00201 
00202     while( true )
00203     {
00204         BezierData &bz = stack.top();
00205 
00206         if ( bz.flatness() < m_flatness )
00207         {
00208             if ( stack.size() == 1 )
00209             {
00210                 polygon += p2;
00211                 return;
00212             }
00213 
00214             polygon += bz.p2();
00215             stack.pop();
00216         }
00217         else
00218         {
00219             stack.push( bz.subdivided() );
00220         }
00221     }
00222 
00223 }


plotjuggler
Author(s): Davide Faconti
autogenerated on Fri Sep 1 2017 02:41:56