]> git.sesse.net Git - vlc/blob - modules/gui/skins2/utils/bezier.cpp
* modules/gui/skins2/bezier.*: Compute the points coordinates only once
[vlc] / modules / gui / skins2 / utils / bezier.cpp
1 /*****************************************************************************
2  * bezier.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 VideoLAN
5  * $Id: bezier.cpp,v 1.3 2004/02/01 21:13:04 ipkiss 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     // We expect rAbscissas and rOrdinates to have the same size, of course
34     m_nbCtrlPt = rAbscissas.size();
35
36     // Copy the control points coordinates
37     m_ptx.assign( rAbscissas.begin(), rAbscissas.end() );
38     m_pty.assign( rOrdinates.begin(), rOrdinates.end() );
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     // Initialization
48     int cx, cy, oldx, oldy;
49     m_leftVect.reserve( MAX_BEZIER_POINT + 1 );
50     m_topVect.reserve( MAX_BEZIER_POINT + 1 );
51
52     // Calculate the first point
53     computePoint( 0, oldx, oldy );
54     m_leftVect[0] = oldx;
55     m_topVect[0]  = oldy;
56
57     // Calculate the other points
58     float percentage;
59     for( float j = 1; j <= MAX_BEZIER_POINT; j++ )
60     {
61         percentage = j / MAX_BEZIER_POINT;
62         computePoint( percentage, cx, cy );
63         if( ( flag == kCoordsBoth && ( cx != oldx || cy != oldy ) ) ||
64             ( flag == kCoordsX && cx != oldx ) ||
65             ( flag == kCoordsY && cy != oldy ) )
66         {
67             m_percVect.push_back( percentage );
68             m_leftVect.push_back( cx );
69             m_topVect.push_back( cy );
70             oldx = cx;
71             oldy = cy;
72         }
73     }
74     m_nbPoints = m_percVect.size();
75
76     // Small hack to ensure that the percentage of the last point is always 1
77     m_percVect[m_nbPoints - 1] = 1;
78 }
79
80
81 float Bezier::getNearestPercent( int x, int y ) const
82 {
83     int nearest = findNearestPoint( x, y );
84     return m_percVect[nearest];
85 }
86
87
88 float Bezier::getMinDist( int x, int y ) const
89 {
90     int nearest = findNearestPoint( x, y );
91     return sqrt( (m_leftVect[nearest] - x) * (m_leftVect[nearest] - x) +
92                  (m_topVect[nearest] - y) * (m_topVect[nearest] - y) );
93 }
94
95
96 void Bezier::getPoint( float t, int &x, int &y ) const
97 {
98     // Find the precalculated point whose percentage is nearest from t
99     int refPoint = 0;
100     float minDiff = fabs( m_percVect[0] - t );
101
102     // The percentages are stored in increasing order, so we can stop the loop
103     // as soon as 'diff' starts increasing
104     float diff;
105     while( refPoint < m_nbPoints &&
106            (diff = fabs( m_percVect[refPoint] - t )) <= minDiff )
107     {
108         refPoint++;
109         minDiff = diff;
110     }
111
112     // The searched point is then (refPoint - 1)
113     // We know that refPoint > 0 because we looped at least once
114     x = m_leftVect[refPoint - 1];
115     y = m_topVect[refPoint - 1];
116 }
117
118
119 int Bezier::getWidth() const
120 {
121     int width = 0;
122     for( int i = 0; i < m_nbPoints; i++ )
123     {
124         if( m_leftVect[i] > width )
125         {
126             width = m_leftVect[i];
127         }
128     }
129     return width;
130 }
131
132
133 int Bezier::getHeight() const
134 {
135     int height = 0;
136     for( int i = 0; i < m_nbPoints; i++ )
137     {
138         if( m_topVect[i] > height )
139         {
140             height = m_topVect[i];
141         }
142     }
143     return height;
144 }
145
146
147 int Bezier::findNearestPoint( int x, int y ) const
148 {
149     // The distance to the first point is taken as the reference
150     int refPoint = 0;
151     int minDist = (m_leftVect[0] - x) * (m_leftVect[0] - x) +
152                   (m_topVect[0] - y) * (m_topVect[0] - y);
153
154     int dist;
155     for( int i = 1; i < m_nbPoints; i++ )
156     {
157         dist = (m_leftVect[i] - x) * (m_leftVect[i] - x) +
158                (m_topVect[i] - y) * (m_topVect[i] - y);
159         if( dist < minDist )
160         {
161             minDist = dist;
162             refPoint = i;
163         }
164     }
165
166     return refPoint;
167 }
168
169
170 void Bezier::computePoint( float t, int &x, int &y ) const
171 {
172     // See http://astronomy.swin.edu.au/~pbourke/curves/bezier/ for a simple
173     // explanation of the algorithm
174     float xPos = 0;
175     float yPos = 0;
176     float coeff;
177     for( int i = 0; i < m_nbCtrlPt; i++ )
178     {
179         coeff = computeCoeff( i, m_nbCtrlPt - 1, t );
180         xPos += m_ptx[i] * coeff;
181         yPos += m_pty[i] * coeff;
182     }
183
184     // Float cast to avoid strange truncatures
185     // XXX: not very nice...
186     x = (int)(float)xPos;
187     y = (int)(float)yPos;
188 }
189
190
191 inline float Bezier::computeCoeff( int i, int n, float t ) const
192 {
193     return (power( t, i ) * power( 1 - t, (n - i) ) *
194         (m_ft[n] / m_ft[i] / m_ft[n - i]));
195 }
196
197
198 inline float Bezier::power( float x, int n ) const
199 {
200     if( n > 0 )
201         return x * power( x, n - 1);
202     else
203         return 1;
204 }
205