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