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