]> git.sesse.net Git - vlc/blob - modules/gui/skins2/utils/bezier.cpp
* utils/bezier.cpp: use lrint() to avoid rounding issues
[vlc] / modules / gui / skins2 / utils / bezier.cpp
1 /*****************************************************************************
2  * bezier.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
5  * $Id: bezier.cpp,v 1.5 2004/03/03 22:57:15 asmax Exp $
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #include "bezier.hpp"
26 #include <math.h>
27
28
29 Bezier::Bezier( intf_thread_t *p_intf, const vector<float> &rAbscissas,
30                 const vector<float> &rOrdinates, Flag_t flag )
31     : SkinObject( p_intf )
32 {
33     // Copy the control points coordinates
34     m_ptx.assign( rAbscissas.begin(), rAbscissas.end() );
35     m_pty.assign( rOrdinates.begin(), rOrdinates.end() );
36
37     // We expect m_ptx and m_pty to have the same size, of course
38     m_nbCtrlPt = m_ptx.size();
39
40     // Precalculate the factoriels
41     m_ft.push_back( 1 );
42     for( int i = 1; i < m_nbCtrlPt; i++ )
43     {
44         m_ft.push_back( i * m_ft[i - 1] );
45     }
46
47     // Calculate the first point
48     int oldx, oldy;
49     computePoint( 0, oldx, oldy );
50     m_leftVect.push_back( oldx );
51     m_topVect.push_back( oldy );
52     m_percVect.push_back( 0 );
53
54     // Calculate the other points
55     float percentage;
56     int cx, cy;
57     for( float j = 1; j <= MAX_BEZIER_POINT; j++ )
58     {
59         percentage = j / MAX_BEZIER_POINT;
60         computePoint( percentage, cx, cy );
61         if( ( flag == kCoordsBoth && ( cx != oldx || cy != oldy ) ) ||
62             ( flag == kCoordsX && cx != oldx ) ||
63             ( flag == kCoordsY && cy != oldy ) )
64         {
65             m_percVect.push_back( percentage );
66             m_leftVect.push_back( cx );
67             m_topVect.push_back( cy );
68             oldx = cx;
69             oldy = cy;
70         }
71     }
72     m_nbPoints = m_leftVect.size();
73
74     // If we have only one control point, we duplicate it
75     // This allows to simplify the algorithms used in the class
76     if( m_nbPoints == 1 )
77     {
78         m_leftVect.push_back( m_leftVect[0] );
79         m_topVect.push_back( m_topVect[0] );
80         m_percVect.push_back( 1 );
81         m_nbPoints = 2;
82    }
83
84     // Ensure that the percentage of the last point is always 1
85     m_percVect[m_nbPoints - 1] = 1;
86 }
87
88
89 float Bezier::getNearestPercent( int x, int y ) const
90 {
91     int nearest = findNearestPoint( x, y );
92     return m_percVect[nearest];
93 }
94
95
96 float Bezier::getMinDist( int x, int y ) const
97 {
98     int nearest = findNearestPoint( x, y );
99     return sqrt( (m_leftVect[nearest] - x) * (m_leftVect[nearest] - x) +
100                  (m_topVect[nearest] - y) * (m_topVect[nearest] - y) );
101 }
102
103
104 void Bezier::getPoint( float t, int &x, int &y ) const
105 {
106     // Find the precalculated point whose percentage is nearest from t
107     int refPoint = 0;
108     float minDiff = fabs( m_percVect[0] - t );
109
110     // The percentages are stored in increasing order, so we can stop the loop
111     // as soon as 'diff' starts increasing
112     float diff;
113     while( refPoint < m_nbPoints &&
114            (diff = fabs( m_percVect[refPoint] - t )) <= minDiff )
115     {
116         refPoint++;
117         minDiff = diff;
118     }
119
120     // The searched point is then (refPoint - 1)
121     // We know that refPoint > 0 because we looped at least once
122     x = m_leftVect[refPoint - 1];
123     y = m_topVect[refPoint - 1];
124 }
125
126
127 int Bezier::getWidth() const
128 {
129     int width = 0;
130     for( int i = 0; i < m_nbPoints; i++ )
131     {
132         if( m_leftVect[i] > width )
133         {
134             width = m_leftVect[i];
135         }
136     }
137     return width;
138 }
139
140
141 int Bezier::getHeight() const
142 {
143     int height = 0;
144     for( int i = 0; i < m_nbPoints; i++ )
145     {
146         if( m_topVect[i] > height )
147         {
148             height = m_topVect[i];
149         }
150     }
151     return height;
152 }
153
154
155 int Bezier::findNearestPoint( int x, int y ) const
156 {
157     // The distance to the first point is taken as the reference
158     int refPoint = 0;
159     int minDist = (m_leftVect[0] - x) * (m_leftVect[0] - x) +
160                   (m_topVect[0] - y) * (m_topVect[0] - y);
161
162     int dist;
163     for( int i = 1; i < m_nbPoints; i++ )
164     {
165         dist = (m_leftVect[i] - x) * (m_leftVect[i] - x) +
166                (m_topVect[i] - y) * (m_topVect[i] - y);
167         if( dist < minDist )
168         {
169             minDist = dist;
170             refPoint = i;
171         }
172     }
173
174     return refPoint;
175 }
176
177
178 void Bezier::computePoint( float t, int &x, int &y ) const
179 {
180     // See http://astronomy.swin.edu.au/~pbourke/curves/bezier/ for a simple
181     // explanation of the algorithm
182     float xPos = 0;
183     float yPos = 0;
184     float coeff;
185     for( int i = 0; i < m_nbCtrlPt; i++ )
186     {
187         coeff = computeCoeff( i, m_nbCtrlPt - 1, t );
188         xPos += m_ptx[i] * coeff;
189         yPos += m_pty[i] * coeff;
190     }
191
192     x = lrintf(xPos);
193     y = lrintf(yPos);
194 }
195
196
197 inline float Bezier::computeCoeff( int i, int n, float t ) const
198 {
199     return (power( t, i ) * power( 1 - t, (n - i) ) *
200         (m_ft[n] / m_ft[i] / m_ft[n - i]));
201 }
202
203
204 inline float Bezier::power( float x, int n ) const
205 {
206     if( n > 0 )
207         return x * power( x, n - 1);
208     else
209         return 1;
210 }
211