Qwt User's Guide 6.3.0
Loading...
Searching...
No Matches
qwt_bezier.cpp
1/******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10#include "qwt_bezier.h"
11#include "qwt_math.h"
12
13#include <qpolygon.h>
14#include <qstack.h>
15
16namespace
17{
18 class BezierData
19 {
20 public:
21 inline BezierData()
22 {
23 // default constructor with uninitialized points
24 }
25
26 inline BezierData( const QPointF& p1, const QPointF& cp1,
27 const QPointF& cp2, const QPointF& p2 ):
28 m_x1( p1.x() ),
29 m_y1( p1.y() ),
30 m_cx1( cp1.x() ),
31 m_cy1( cp1.y() ),
32 m_cx2( cp2.x() ),
33 m_cy2( cp2.y() ),
34 m_x2( p2.x() ),
35 m_y2( p2.y() )
36 {
37 }
38
39 static inline double minFlatness( double tolerance )
40 {
41 // we can simplify the tolerance criterion check in
42 // the subdivision loop, by precalculating some
43 // flatness value.
44
45 return 16 * ( tolerance * tolerance );
46 }
47
48 inline double flatness() const
49 {
50 // algo by Roger Willcocks ( http://www.rops.org )
51
52 const double ux = 3.0 * m_cx1 - 2.0 * m_x1 - m_x2;
53 const double uy = 3.0 * m_cy1 - 2.0 * m_y1 - m_y2;
54 const double vx = 3.0 * m_cx2 - 2.0 * m_x2 - m_x1;
55 const double vy = 3.0 * m_cy2 - 2.0 * m_y2 - m_y1;
56
57 const double ux2 = ux * ux;
58 const double uy2 = uy * uy;
59
60 const double vx2 = vx * vx;
61 const double vy2 = vy * vy;
62
63 return qwtMaxF( ux2, vx2 ) + qwtMaxF( uy2, vy2 );
64 }
65
66 inline BezierData subdivided()
67 {
68 BezierData bz;
69
70 const double c1 = midValue( m_cx1, m_cx2 );
71
72 bz.m_cx1 = midValue( m_x1, m_cx1 );
73 m_cx2 = midValue( m_cx2, m_x2 );
74 bz.m_x1 = m_x1;
75 bz.m_cx2 = midValue( bz.m_cx1, c1 );
76 m_cx1 = midValue( c1, m_cx2 );
77 bz.m_x2 = m_x1 = midValue( bz.m_cx2, m_cx1 );
78
79 const double c2 = midValue( m_cy1, m_cy2 );
80
81 bz.m_cy1 = midValue( m_y1, m_cy1 );
82 m_cy2 = midValue( m_cy2, m_y2 );
83 bz.m_y1 = m_y1;
84 bz.m_cy2 = midValue( bz.m_cy1, c2 );
85 m_cy1 = midValue( m_cy2, c2 );
86 bz.m_y2 = m_y1 = midValue( bz.m_cy2, m_cy1 );
87
88 return bz;
89 }
90
91 inline QPointF p2() const
92 {
93 return QPointF( m_x2, m_y2 );
94 }
95
96 private:
97 inline double midValue( double v1, double v2 )
98 {
99 return 0.5 * ( v1 + v2 );
100 }
101
102 double m_x1, m_y1;
103 double m_cx1, m_cy1;
104 double m_cx2, m_cy2;
105 double m_x2, m_y2;
106 };
107}
108
116QwtBezier::QwtBezier( double tolerance )
117 : m_tolerance( qwtMaxF( tolerance, 0.0 ) )
118 , m_flatness( BezierData::minFlatness( m_tolerance ) )
119{
120}
121
126
141void QwtBezier::setTolerance( double tolerance )
142{
143 m_tolerance = qwtMaxF( tolerance, 0.0 );
144 m_flatness = BezierData::minFlatness( m_tolerance );
145}
146
157QPolygonF QwtBezier::toPolygon( const QPointF& p1,
158 const QPointF& cp1, const QPointF& cp2, const QPointF& p2 ) const
159{
160 QPolygonF polygon;
161
162 if ( m_flatness > 0.0 )
163 {
164 // a flatness of 0.0 is not achievable
165 appendToPolygon( p1, cp1, cp2, p2, polygon );
166 }
167
168 return polygon;
169}
170
186void QwtBezier::appendToPolygon( const QPointF& p1, const QPointF& cp1,
187 const QPointF& cp2, const QPointF& p2, QPolygonF& polygon ) const
188{
189 if ( m_flatness <= 0.0 )
190 {
191 // a flatness of 0.0 is not achievable
192 return;
193 }
194
195 if ( polygon.isEmpty() || polygon.last() != p1 )
196 polygon += p1;
197
198 // to avoid deep stacks we convert the recursive algo
199 // to something iterative, where the parameters of the
200 // recursive class are pushed to a stack instead
201
203 stack.push( BezierData( p1, cp1, cp2, p2 ) );
204
205 while( true )
206 {
207 BezierData& bz = stack.top();
208
209 if ( bz.flatness() < m_flatness )
210 {
211 if ( stack.size() == 1 )
212 {
213 polygon += p2;
214 return;
215 }
216
217 polygon += bz.p2();
218 stack.pop();
219 }
220 else
221 {
222 stack.push( bz.subdivided() );
223 }
224 }
225
226}
227
239QPointF QwtBezier::pointAt( const QPointF& p1,
240 const QPointF& cp1, const QPointF& cp2, const QPointF& p2, double t )
241{
242 const double d1 = 3.0 * t;
243 const double d2 = 3.0 * t * t;
244 const double d3 = t * t * t;
245 const double s = 1.0 - t;
246
247 const double x = ( ( s * p1.x() + d1 * cp1.x() ) * s + d2 * cp2.x() ) * s + d3 * p2.x();
248 const double y = ( ( s * p1.y() + d1 * cp1.y() ) * s + d2 * cp2.y() ) * s + d3 * p2.y();
249
250 return QPointF( x, y );
251}
252
QPolygonF toPolygon(const QPointF &p1, const QPointF &cp1, const QPointF &cp2, const QPointF &p2) const
Interpolate a Bézier curve by a polygon.
void appendToPolygon(const QPointF &p1, const QPointF &cp1, const QPointF &cp2, const QPointF &p2, QPolygonF &polygon) const
Interpolate a Bézier curve by a polygon.
double tolerance() const
Definition qwt_bezier.h:56
void setTolerance(double tolerance)
~QwtBezier()
Destructor.
QwtBezier(double tolerance=0.5)
Constructor.
static QPointF pointAt(const QPointF &p1, const QPointF &cp1, const QPointF &cp2, const QPointF &p2, double t)