]> git.sesse.net Git - vlc/blob - modules/gui/skins2/utils/bezier.cpp
12c870448b113639d7b0d96ded696ef9bbdbd7c6
[vlc] / modules / gui / skins2 / utils / bezier.cpp
1 /*****************************************************************************
2  * bezier.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include <vlc/vlc.h>
26 #include "bezier.hpp"
27 #include <math.h>
28
29 // XXX should be in VLC core
30 #ifndef HAVE_LRINTF
31 #   ifdef HAVE_LRINT
32 #       define lrintf( x ) (int)rint( x )
33 #   elif defined WIN32
34             __inline long int lrintf( float x )
35             {
36             int i;
37             _asm fld x __asm fistp i
38                     return i;
39         }
40 #   endif
41 #endif
42
43 Bezier::Bezier( intf_thread_t *p_intf, const vector<float> &rAbscissas,
44                 const vector<float> &rOrdinates, Flag_t flag )
45     : SkinObject( p_intf )
46 {
47     // Copy the control points coordinates
48     m_ptx.assign( rAbscissas.begin(), rAbscissas.end() );
49     m_pty.assign( rOrdinates.begin(), rOrdinates.end() );
50
51     // We expect m_ptx and m_pty to have the same size, of course
52     m_nbCtrlPt = m_ptx.size();
53
54     // Precalculate the factoriels
55     m_ft.push_back( 1 );
56     for( int i = 1; i < m_nbCtrlPt; i++ )
57     {
58         m_ft.push_back( i * m_ft[i - 1] );
59     }
60
61     // Calculate the first point
62     int oldx, oldy;
63     computePoint( 0, oldx, oldy );
64     m_leftVect.push_back( oldx );
65     m_topVect.push_back( oldy );
66     m_percVect.push_back( 0 );
67
68     // Calculate the other points
69     float percentage;
70     int cx, cy;
71     for( float j = 1; j <= MAX_BEZIER_POINT; j++ )
72     {
73         percentage = j / MAX_BEZIER_POINT;
74         computePoint( percentage, cx, cy );
75         if( ( flag == kCoordsBoth && ( cx != oldx || cy != oldy ) ) ||
76             ( flag == kCoordsX && cx != oldx ) ||
77             ( flag == kCoordsY && cy != oldy ) )
78         {
79             m_percVect.push_back( percentage );
80             m_leftVect.push_back( cx );
81             m_topVect.push_back( cy );
82             oldx = cx;
83             oldy = cy;
84         }
85     }
86     m_nbPoints = m_leftVect.size();
87
88     // If we have only one control point, we duplicate it
89     // This allows to simplify the algorithms used in the class
90     if( m_nbPoints == 1 )
91     {
92         m_leftVect.push_back( m_leftVect[0] );
93         m_topVect.push_back( m_topVect[0] );
94         m_percVect.push_back( 1 );
95         m_nbPoints = 2;
96    }
97
98     // Ensure that the percentage of the last point is always 1
99     m_percVect[m_nbPoints - 1] = 1;
100 }
101
102
103 float Bezier::getNearestPercent( int x, int y ) const
104 {
105     int nearest = findNearestPoint( x, y );
106     return m_percVect[nearest];
107 }
108
109
110 float Bezier::getMinDist( int x, int y, float xScale, float yScale ) const
111 {
112     int nearest = findNearestPoint( x, y );
113     double xDist = xScale * (m_leftVect[nearest] - x);
114     double yDist = yScale * (m_topVect[nearest] - y);
115     return sqrt( xDist * xDist + yDist * yDist );
116 }
117
118
119 void Bezier::getPoint( float t, int &x, int &y ) const
120 {
121     // Find the precalculated point whose percentage is nearest from t
122     int refPoint = 0;
123     float minDiff = fabs( m_percVect[0] - t );
124
125     // The percentages are stored in increasing order, so we can stop the loop
126     // as soon as 'diff' starts increasing
127     float diff;
128     while( refPoint < m_nbPoints &&
129            (diff = fabs( m_percVect[refPoint] - t )) <= minDiff )
130     {
131         refPoint++;
132         minDiff = diff;
133     }
134
135     // The searched point is then (refPoint - 1)
136     // We know that refPoint > 0 because we looped at least once
137     x = m_leftVect[refPoint - 1];
138     y = m_topVect[refPoint - 1];
139 }
140
141
142 int Bezier::getWidth() const
143 {
144     int width = 0;
145     for( int i = 0; i < m_nbPoints; i++ )
146     {
147         if( m_leftVect[i] >= width )
148         {
149             width = m_leftVect[i] + 1;
150         }
151     }
152     return width;
153 }
154
155
156 int Bezier::getHeight() const
157 {
158     int height = 0;
159     for( int i = 0; i < m_nbPoints; i++ )
160     {
161         if( m_topVect[i] >= height )
162         {
163             height = m_topVect[i] + 1;
164         }
165     }
166     return height;
167 }
168
169
170 int Bezier::findNearestPoint( int x, int y ) const
171 {
172     // The distance to the first point is taken as the reference
173     int refPoint = 0;
174     int minDist = (m_leftVect[0] - x) * (m_leftVect[0] - x) +
175                   (m_topVect[0] - y) * (m_topVect[0] - y);
176
177     int dist;
178     for( int i = 1; i < m_nbPoints; i++ )
179     {
180         dist = (m_leftVect[i] - x) * (m_leftVect[i] - x) +
181                (m_topVect[i] - y) * (m_topVect[i] - y);
182         if( dist < minDist )
183         {
184             minDist = dist;
185             refPoint = i;
186         }
187     }
188
189     return refPoint;
190 }
191
192
193 void Bezier::computePoint( float t, int &x, int &y ) const
194 {
195     // See http://astronomy.swin.edu.au/~pbourke/curves/bezier/ for a simple
196     // explanation of the algorithm
197     float xPos = 0;
198     float yPos = 0;
199     float coeff;
200     for( int i = 0; i < m_nbCtrlPt; i++ )
201     {
202         coeff = computeCoeff( i, m_nbCtrlPt - 1, t );
203         xPos += m_ptx[i] * coeff;
204         yPos += m_pty[i] * coeff;
205     }
206
207     x = lrintf(xPos);
208     y = lrintf(yPos);
209 }
210
211
212 inline float Bezier::computeCoeff( int i, int n, float t ) const
213 {
214     return (power( t, i ) * power( 1 - t, (n - i) ) *
215         (m_ft[n] / m_ft[i] / m_ft[n - i]));
216 }
217
218
219 inline float Bezier::power( float x, int n ) const
220 {
221     if( n > 0 )
222         return x * power( x, n - 1);
223     else
224         return 1;
225 }