bezier.cpp 6.08 KB
Newer Older
1 2 3
/*****************************************************************************
 * bezier.cpp
 *****************************************************************************
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 11 12 13 14 15 16 17 18 19 20 21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
22
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24
 *****************************************************************************/

25 26 27 28
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

29
#include <vlc_common.h>
30 31 32
#include "bezier.hpp"
#include <math.h>

33
// XXX should be in VLC core
34
#ifndef HAVE_LRINTF
35 36 37
#   ifdef HAVE_LRINT
#       define lrintf( x ) (int)rint( x )
#   elif defined WIN32
38 39
        __inline long int lrintf( float x )
        {
40 41
            int i;
            _asm fld x __asm fistp i
42 43
            return i;
        }
44
#   endif
45
#endif
46

Cyril Deguet's avatar
Cyril Deguet committed
47 48
Bezier::Bezier( intf_thread_t *p_intf, const vector<float> &rAbscissas,
                const vector<float> &rOrdinates, Flag_t flag )
49 50 51 52 53 54
    : SkinObject( p_intf )
{
    // Copy the control points coordinates
    m_ptx.assign( rAbscissas.begin(), rAbscissas.end() );
    m_pty.assign( rOrdinates.begin(), rOrdinates.end() );

55 56 57
    // We expect m_ptx and m_pty to have the same size, of course
    m_nbCtrlPt = m_ptx.size();

58 59 60 61 62 63 64 65
    // Precalculate the factoriels
    m_ft.push_back( 1 );
    for( int i = 1; i < m_nbCtrlPt; i++ )
    {
        m_ft.push_back( i * m_ft[i - 1] );
    }

    // Calculate the first point
66
    int oldx, oldy;
67
    computePoint( 0, oldx, oldy );
68 69 70
    m_leftVect.push_back( oldx );
    m_topVect.push_back( oldy );
    m_percVect.push_back( 0 );
71

72
    // Calculate the other points
Cyril Deguet's avatar
Cyril Deguet committed
73
    float percentage;
74
    int cx, cy;
Cyril Deguet's avatar
Cyril Deguet committed
75
    for( float j = 1; j <= MAX_BEZIER_POINT; j++ )
76 77
    {
        percentage = j / MAX_BEZIER_POINT;
78
        computePoint( percentage, cx, cy );
79 80 81 82
        if( ( flag == kCoordsBoth && ( cx != oldx || cy != oldy ) ) ||
            ( flag == kCoordsX && cx != oldx ) ||
            ( flag == kCoordsY && cy != oldy ) )
        {
83
            m_percVect.push_back( percentage );
84 85 86 87 88 89
            m_leftVect.push_back( cx );
            m_topVect.push_back( cy );
            oldx = cx;
            oldy = cy;
        }
    }
90 91 92
    m_nbPoints = m_leftVect.size();

    // If we have only one control point, we duplicate it
93
    // This allows simplifying the algorithms used in the class
94 95 96 97 98 99 100
    if( m_nbPoints == 1 )
    {
        m_leftVect.push_back( m_leftVect[0] );
        m_topVect.push_back( m_topVect[0] );
        m_percVect.push_back( 1 );
        m_nbPoints = 2;
   }
101

102
    // Ensure that the percentage of the last point is always 1
103
    m_percVect[m_nbPoints - 1] = 1;
104 105 106
}


Cyril Deguet's avatar
Cyril Deguet committed
107
float Bezier::getNearestPercent( int x, int y ) const
108 109
{
    int nearest = findNearestPoint( x, y );
110
    return m_percVect[nearest];
111 112 113
}


114
float Bezier::getMinDist( int x, int y, float xScale, float yScale ) const
115
{
116
    int nearest = findNearestPoint( x, y );
117 118 119
    double xDist = xScale * (m_leftVect[nearest] - x);
    double yDist = yScale * (m_topVect[nearest] - y);
    return sqrt( xDist * xDist + yDist * yDist );
120 121 122
}


Cyril Deguet's avatar
Cyril Deguet committed
123
void Bezier::getPoint( float t, int &x, int &y ) const
124
{
125 126 127 128 129 130 131 132 133
    // Find the precalculated point whose percentage is nearest from t
    int refPoint = 0;
    float minDiff = fabs( m_percVect[0] - t );

    // The percentages are stored in increasing order, so we can stop the loop
    // as soon as 'diff' starts increasing
    float diff;
    while( refPoint < m_nbPoints &&
           (diff = fabs( m_percVect[refPoint] - t )) <= minDiff )
134
    {
135 136
        refPoint++;
        minDiff = diff;
137 138
    }

139 140 141 142
    // The searched point is then (refPoint - 1)
    // We know that refPoint > 0 because we looped at least once
    x = m_leftVect[refPoint - 1];
    y = m_topVect[refPoint - 1];
143 144 145 146 147 148 149 150
}


int Bezier::getWidth() const
{
    int width = 0;
    for( int i = 0; i < m_nbPoints; i++ )
    {
151
        if( m_leftVect[i] >= width )
152
        {
153
            width = m_leftVect[i] + 1;
154 155 156 157 158 159 160 161 162 163 164
        }
    }
    return width;
}


int Bezier::getHeight() const
{
    int height = 0;
    for( int i = 0; i < m_nbPoints; i++ )
    {
165
        if( m_topVect[i] >= height )
166
        {
167
            height = m_topVect[i] + 1;
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
        }
    }
    return height;
}


int Bezier::findNearestPoint( int x, int y ) const
{
    // The distance to the first point is taken as the reference
    int refPoint = 0;
    int minDist = (m_leftVect[0] - x) * (m_leftVect[0] - x) +
                  (m_topVect[0] - y) * (m_topVect[0] - y);

    int dist;
    for( int i = 1; i < m_nbPoints; i++ )
    {
        dist = (m_leftVect[i] - x) * (m_leftVect[i] - x) +
               (m_topVect[i] - y) * (m_topVect[i] - y);
        if( dist < minDist )
        {
            minDist = dist;
            refPoint = i;
        }
    }

    return refPoint;
}


197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
inline float Bezier::power( float x, int n )
{
#if 0
    return n <= 0 ? 1 : x * power( x, n - 1 );
#else
    return powf( x, n );
#endif
}


inline float Bezier::computeCoeff( int i, int n, float t ) const
{
    return (power( t, i ) * power( 1 - t, (n - i) ) *
        (m_ft[n] / m_ft[i] / m_ft[n - i]));
}


214 215 216 217 218 219 220 221 222 223 224 225 226 227
void Bezier::computePoint( float t, int &x, int &y ) const
{
    // See http://astronomy.swin.edu.au/~pbourke/curves/bezier/ for a simple
    // explanation of the algorithm
    float xPos = 0;
    float yPos = 0;
    float coeff;
    for( int i = 0; i < m_nbCtrlPt; i++ )
    {
        coeff = computeCoeff( i, m_nbCtrlPt - 1, t );
        xPos += m_ptx[i] * coeff;
        yPos += m_pty[i] * coeff;
    }

228 229
    x = lrintf(xPos);
    y = lrintf(yPos);
230 231
}