Example #1
0
/// compute interpolation parameters from keyframes and an iterator
/// to the next keyframe (the first with time > t)
static void
interParams(const KeyFrameSet &keyFrames,
            double t,
            const KeyFrameSet::const_iterator &itup,
            double *tcur,
            double *vcur,
            double *vcurDerivRight,
            Natron::KeyframeType *interp,
            double *tnext,
            double *vnext,
            double *vnextDerivLeft,
            Natron::KeyframeType *interpNext)
{
    assert(itup == keyFrames.end() || t < itup->getTime());
    if (itup == keyFrames.begin()) {
        //if all keys have a greater time
        // get the first keyframe
        *tnext = itup->getTime();
        *vnext = itup->getValue();
        *vnextDerivLeft = itup->getLeftDerivative();
        *interpNext = itup->getInterpolation();
        *tcur = *tnext - 1.;
        *vcur = *vnext;
        *vcurDerivRight = 0.;
        *interp = Natron::KEYFRAME_NONE;
    } else if (itup == keyFrames.end()) {
        //if we found no key that has a greater time
        // get the last keyframe
        KeyFrameSet::const_reverse_iterator itlast = keyFrames.rbegin();
        *tcur = itlast->getTime();
        *vcur = itlast->getValue();
        *vcurDerivRight = itlast->getRightDerivative();
        *interp = itlast->getInterpolation();
        *tnext = *tcur + 1.;
        *vnext = *vcur;
        *vnextDerivLeft = 0.;
        *interpNext = Natron::KEYFRAME_NONE;
    } else {
        // between two keyframes
        // get the last keyframe with time <= t
        KeyFrameSet::const_iterator itcur = itup;
        --itcur;
        assert(itcur->getTime() <= t);
        *tcur = itcur->getTime();
        *vcur = itcur->getValue();
        *vcurDerivRight = itcur->getRightDerivative();
        *interp = itcur->getInterpolation();
        *tnext = itup->getTime();
        *vnext = itup->getValue();
        *vnextDerivLeft = itup->getLeftDerivative();
        *interpNext = itup->getInterpolation();
    }
}
Example #2
0
int Curve::keyFrameIndex(double time) const
{
    QReadLocker l(&_imp->_lock);
    int i = 0;
    double paramEps;
    if (_imp->xMax != INT_MAX && _imp->xMin != INT_MIN) {
        paramEps = NATRON_CURVE_X_SPACING_EPSILON * std::abs(_imp->xMax - _imp->xMin);
    } else {
        paramEps = NATRON_CURVE_X_SPACING_EPSILON;
    }
    for (KeyFrameSet::const_iterator it = _imp->keyFrames.begin();
         it!=_imp->keyFrames.end() && (it->getTime() < time+paramEps);
         ++it, ++i) {
        if (std::abs(it->getTime() - time) < paramEps) {
            return i;
        }
    }
    return -1;

}
Example #3
0
bool Curve::getPreviousKeyframeTime(double time,KeyFrame* k) const
{
    assert(k);
    QReadLocker l(&_imp->_lock);
    if (_imp->keyFrames.empty()) {
        return false;
    }
    KeyFrameSet::const_iterator upper = _imp->keyFrames.end();
    for (KeyFrameSet::const_iterator it = _imp->keyFrames.begin(); it!=_imp->keyFrames.end(); ++it) {
        if (it->getTime() > time) {
            upper = it;
            break;
        } else if (it->getTime() == time) {
            if (it == _imp->keyFrames.begin()) {
                return false;
            } else {
                --it;
                *k = *it;
                return true;
            }
        }
    }
    if (upper == _imp->keyFrames.end()) {
        *k = *_imp->keyFrames.rbegin();
        return true;
    } else if (upper == _imp->keyFrames.begin()) {
        return false;
    } else {
        ///If we reach here the previous keyframe is exactly the previous to upper because we already checked
        ///in the for loop that the previous key wasn't equal to the given time
        --upper;
        assert(upper->getTime() < time);
        *k = *upper;
        return true;
    }

}
Example #4
0
bool Curve::getNextKeyframeTime(double time,KeyFrame* k) const
{
    assert(k);
    QReadLocker l(&_imp->_lock);
    if (_imp->keyFrames.empty()) {
        return false;
    }
    KeyFrameSet::const_iterator upper = _imp->keyFrames.end();
    for (KeyFrameSet::const_iterator it = _imp->keyFrames.begin(); it!=_imp->keyFrames.end(); ++it) {
        if (it->getTime() > time) {
            upper = it;
            break;
        }
    }
    if (upper == _imp->keyFrames.end()) {
        return false;
    } else {
        *k = *upper;
        return true;
    }
}
Example #5
0
bool Curve::getNearestKeyFrameWithTime(double time,KeyFrame* k) const
{
    assert(k);
    QReadLocker l(&_imp->_lock);
    if (_imp->keyFrames.empty()) {
        return false;
    }
    KeyFrameSet::const_iterator upper = _imp->keyFrames.end();
    for (KeyFrameSet::const_iterator it = _imp->keyFrames.begin(); it!=_imp->keyFrames.end(); ++it) {
        if (it->getTime() > time) {
            upper = it;
            break;
        } else if (it->getTime() == time) {
            *k = *it;
            return true;
        }
    }
    
    if (upper == _imp->keyFrames.begin()) {
        *k = *upper;
        return true;
    }
    
    KeyFrameSet::const_iterator lower = upper;
    --lower;
    if (upper == _imp->keyFrames.end()) {
        *k = *lower;
        return true;
    }
    
    assert(time - lower->getTime() > 0);
    assert(upper->getTime() - time > 0);
    
    if ((upper->getTime() - time) < (time - lower->getTime())) {
        *k = *upper;
    } else {
        *k = *lower;
    }
    return true;
}
Example #6
0
double Curve::getIntegrateFromTo(double t1, double t2) const
{
    QReadLocker l(&_imp->_lock);
    bool opposite = false;

    // the following assumes that t2 > t1. If it's not the case, swap them and return the opposite.
    if (t1 > t2) {
        opposite = true;
        std::swap(t1,t2);
    }

    if (_imp->keyFrames.empty()) {
        throw std::runtime_error("Curve has no control points!");
    }
    assert(_imp->type == CurvePrivate::DOUBLE_CURVE); // only real-valued curves can be derived

    // even when there is only one keyframe, there may be tangents!
    //if (_imp->keyFrames.size() == 1) {
    //    //if there's only 1 keyframe, don't bother interpolating
    //    return (*_imp->keyFrames.begin()).getValue();
    //}
    double tcur,tnext;
    double vcurDerivRight ,vnextDerivLeft ,vcur ,vnext ;
    Natron::KeyframeType interp ,interpNext;

    KeyFrame k(t1,0.);
    // find the first keyframe with time strictly greater than t1
    KeyFrameSet::const_iterator itup;
    itup = _imp->keyFrames.upper_bound(k);
    interParams(_imp->keyFrames,
                t1,
                itup,
                &tcur,
                &vcur,
                &vcurDerivRight,
                &interp,
                &tnext,
                &vnext,
                &vnextDerivLeft,
                &interpNext);

    double sum = 0.;

    // while there are still keyframes after the current time, add to the total sum and advance
    while (itup != _imp->keyFrames.end() && itup->getTime() < t2) {
        // add integral from t1 to itup->getTime() to sum
        if (mustClamp()) {
            std::pair<double,double> minmax = getCurveYRange();
            sum += Natron::integrate_clamp(tcur,vcur,
                                           vcurDerivRight,
                                           vnextDerivLeft,
                                           tnext,vnext,
                                           t1, itup->getTime(),
                                           minmax.first, minmax.second,
                                           interp,
                                           interpNext);
        } else {
            sum += Natron::integrate(tcur,vcur,
                                     vcurDerivRight,
                                     vnextDerivLeft,
                                     tnext,vnext,
                                     t1, itup->getTime(),
                                     interp,
                                     interpNext);
        }
        // advance
        t1 = itup->getTime();
        ++itup;
        interParams(_imp->keyFrames,
                    t1,
                    itup,
                    &tcur,
                    &vcur,
                    &vcurDerivRight,
                    &interp,
                    &tnext,
                    &vnext,
                    &vnextDerivLeft,
                    &interpNext);
    }

    assert(itup == _imp->keyFrames.end() || t2 <= itup->getTime());
    // add integral from t1 to t2 to sum
    if (mustClamp()) {
        std::pair<double,double> minmax = getCurveYRange();
        sum += Natron::integrate_clamp(tcur,vcur,
                                       vcurDerivRight,
                                       vnextDerivLeft,
                                       tnext,vnext,
                                       t1, t2,
                                       minmax.first, minmax.second,
                                       interp,
                                       interpNext);
    } else {
        sum += Natron::integrate(tcur,vcur,
                                 vcurDerivRight,
                                 vnextDerivLeft,
                                 tnext,vnext,
                                 t1, t2,
                                 interp,
                                 interpNext);
    }

    return opposite ? -sum : sum;
}
Example #7
0
KeyFrameSet::iterator Curve::refreshDerivatives(Curve::CurveChangedReason reason, KeyFrameSet::iterator key)
{
    // PRIVATE - should not lock
    double tcur = key->getTime();
    double vcur = key->getValue();
    
    double tprev, vprev, tnext, vnext, vprevDerivRight, vnextDerivLeft;
    Natron::KeyframeType prevType, nextType;
    if (key == _imp->keyFrames.begin()) {
        tprev = tcur;
        vprev = vcur;
        vprevDerivRight = 0.;
        prevType = Natron::KEYFRAME_NONE;
    } else {
        KeyFrameSet::const_iterator prev = key;
        --prev;
        tprev = prev->getTime();
        vprev = prev->getValue();
        vprevDerivRight = prev->getRightDerivative();
        prevType = prev->getInterpolation();
        
        //if prev is the first keyframe, and not edited by the user then interpolate linearly
        if (prev == _imp->keyFrames.begin() && prevType != Natron::KEYFRAME_FREE &&
            prevType != Natron::KEYFRAME_BROKEN) {
            prevType = Natron::KEYFRAME_LINEAR;
        }
    }
    
    KeyFrameSet::const_iterator next = key;
    ++next;
    if (next == _imp->keyFrames.end()) {
        tnext = tcur;
        vnext = vcur;
        vnextDerivLeft = 0.;
        nextType = Natron::KEYFRAME_NONE;
    } else {
        tnext = next->getTime();
        vnext = next->getValue();
        vnextDerivLeft = next->getLeftDerivative();
        nextType = next->getInterpolation();
        
        KeyFrameSet::const_iterator nextnext = next;
        ++nextnext;
        //if next is thelast keyframe, and not edited by the user then interpolate linearly
        if (nextnext == _imp->keyFrames.end() && nextType != Natron::KEYFRAME_FREE &&
            nextType != Natron::KEYFRAME_BROKEN) {
            nextType = Natron::KEYFRAME_LINEAR;
        }
    }
    
    double vcurDerivLeft,vcurDerivRight;

    assert(key->getInterpolation() != Natron::KEYFRAME_NONE &&
            key->getInterpolation() != Natron::KEYFRAME_BROKEN &&
            key->getInterpolation() != Natron::KEYFRAME_FREE);
    Natron::autoComputeDerivatives(prevType,
                                   key->getInterpolation(),
                                   nextType,
                                   tprev, vprev,
                                   tcur, vcur,
                                   tnext, vnext,
                                   vprevDerivRight,
                                   vnextDerivLeft,
                                   &vcurDerivLeft, &vcurDerivRight);


    KeyFrame newKey(*key);
    newKey.setLeftDerivative(vcurDerivLeft);
    newKey.setRightDerivative(vcurDerivRight);

    std::pair<KeyFrameSet::iterator,bool> newKeyIt = _imp->keyFrames.insert(newKey);

    // keyframe at this time exists, erase and insert again
    if (!newKeyIt.second) {
        _imp->keyFrames.erase(newKeyIt.first);
        newKeyIt = _imp->keyFrames.insert(newKey);
        assert(newKeyIt.second);
    }
    key = newKeyIt.first;
    
    if (reason != DERIVATIVES_CHANGED) {
        key = evaluateCurveChanged(DERIVATIVES_CHANGED,key);
    }
    return key;
}
Example #8
0
void
CurveGui::nextPointForSegment(const double x, // < in curve coordinates
                              const KeyFrameSet & keys,
                              const bool isPeriodic,
                              const double parametricXMin,
                              const double parametricXMax,
                              KeyFrameSet::const_iterator* lastUpperIt,
                              double* x2WidgetCoords,
                              KeyFrame* x1Key,
                              bool* isx1Key)
{
    // always running in the main thread
    assert( qApp && qApp->thread() == QThread::currentThread() );
    assert( !keys.empty() );

    *isx1Key = false;

    // If non periodic and out of curve range, draw straight lines from widget border to
    // the keyframe on the side
    if (!isPeriodic && x < keys.begin()->getTime()) {
        *x2WidgetCoords = keys.begin()->getTime();
        double y;
        _imp->curveWidget->toWidgetCoordinates(x2WidgetCoords, &y);
        *x1Key = *keys.begin();
        *isx1Key = true;
        return;
    } else if (!isPeriodic && x >= keys.rbegin()->getTime()) {
        *x2WidgetCoords = _imp->curveWidget->width() - 1;
        return;
    }



    // We're between 2 keyframes or the curve is periodic, get the upper and lower keyframes widget coordinates

    // Points to the first keyframe with a greater time (in widget coords) than x1
    KeyFrameSet::const_iterator upperIt = keys.end();

    // If periodic, bring back x in the period range (in widget coordinates)
    double xClamped = x;
    double period = parametricXMax - parametricXMin;

    {
        //KeyFrameSet::const_iterator start = keys.begin();
        const double xMin = parametricXMin;// + start->getTime();
        const double xMax = parametricXMax;// + start->getTime();
        if ((x < xMin || x > xMax) && isPeriodic) {
            xClamped = std::fmod(x - xMin, period) + parametricXMin;
            if (xClamped < xMin) {
                xClamped += period;
            }
            assert(xClamped >= xMin && xClamped <= xMax);
        }
    }
    {

        KeyFrameSet::const_iterator itKeys = keys.begin();

        if ( *lastUpperIt != keys.end() ) {
            // If we already have called this function before, start from the previously
            // computed iterator to avoid n square complexity
            itKeys = *lastUpperIt;
        } else {
            // Otherwise start from the begining
            itKeys = keys.begin();
        }
        *lastUpperIt = keys.end();
        for (; itKeys != keys.end(); ++itKeys) {
            if (itKeys->getTime() > xClamped) {
                upperIt = itKeys;
                *lastUpperIt = upperIt;
                break;
            }
        }
    }

    double tprev, vprev, vprevDerivRight, tnext, vnext, vnextDerivLeft;
    if ( upperIt == keys.end() ) {
        // We are in a periodic curve: we are in-between the last keyframe and the parametric xMax
        // If the curve is non periodic, it should have been handled in the 2 cases above: we only draw a straightline
        // from the widget border to the first/last keyframe
        assert(isPeriodic);
        KeyFrameSet::const_iterator start = keys.begin();
        KeyFrameSet::const_reverse_iterator last = keys.rbegin();
        tprev = last->getTime();
        vprev = last->getValue();
        vprevDerivRight = last->getRightDerivative();
        tnext = std::fmod(last->getTime() - start->getTime(), period) + tprev;
        //xClamped += period;
        vnext = start->getValue();
        vnextDerivLeft = start->getLeftDerivative();

    } else if ( upperIt == keys.begin() ) {
        // We are in a periodic curve: we are in-between the parametric xMin and the first keyframe
        // If the curve is non periodic, it should have been handled in the 2 cases above: we only draw a straightline
        // from the widget border to the first/last keyframe
        assert(isPeriodic);
        KeyFrameSet::const_reverse_iterator last = keys.rbegin();
        tprev = last->getTime();
        //xClamped -= period;
        vprev = last->getValue();
        vprevDerivRight = last->getRightDerivative();
        tnext = std::fmod(last->getTime() - upperIt->getTime(), period) + tprev;
        vnext = upperIt->getValue();
        vnextDerivLeft = upperIt->getLeftDerivative();

    } else {
        // in-between 2 keyframes
        KeyFrameSet::const_iterator prev = upperIt;
        --prev;
        tprev = prev->getTime();
        vprev = prev->getValue();
        vprevDerivRight = prev->getRightDerivative();
        tnext = upperIt->getTime();
        vnext = upperIt->getValue();
        vnextDerivLeft = upperIt->getLeftDerivative();
    }
    double normalizeTimeRange = tnext - tprev;
    if (normalizeTimeRange == 0) {
        // Only 1 keyframe, draw a horizontal line
        *x2WidgetCoords = _imp->curveWidget->width() - 1;
        return;
    }
    assert(normalizeTimeRange > 0.);

    double t = ( xClamped - tprev ) / normalizeTimeRange;
    double P3 = vnext;
    double P0 = vprev;
    // Hermite coefficients P0' and P3' are for t normalized in [0,1]
    double P3pl = vnextDerivLeft / normalizeTimeRange; // normalize for t \in [0,1]
    double P0pr = vprevDerivRight / normalizeTimeRange; // normalize for t \in [0,1]
    double secondDer = 6. * (1. - t) * (P3 - P3pl / 3. - P0 - 2. * P0pr / 3.) +
    6. * t * (P0 - P3 + 2 * P3pl / 3. + P0pr / 3. );

    double secondDerWidgetCoord = _imp->curveWidget->toWidgetCoordinates(0, secondDer).y();
    double normalizedSecondDerWidgetCoord = std::abs(secondDerWidgetCoord / normalizeTimeRange);

    // compute delta_x so that the y difference between the derivative and the curve is at most
    // 1 pixel (use the second order Taylor expansion of the function)
    double delta_x = std::max(2. / std::max(std::sqrt(normalizedSecondDerWidgetCoord), 0.1), 1.);

    // The x widget coordinate of the next keyframe
    double tNextWidgetCoords = _imp->curveWidget->toWidgetCoordinates(tnext, 0).x();

    // The widget coordinate of the x passed in parameter but clamped to the curve period
    double xClampedWidgetCoords = _imp->curveWidget->toWidgetCoordinates(xClamped, 0).x();

    // The real x passed in parameter in widget coordinates
    double xWidgetCoords = _imp->curveWidget->toWidgetCoordinates(x, 0).x();

    double x2ClampedWidgetCoords = xClampedWidgetCoords + delta_x;

    double deltaXtoNext = (tNextWidgetCoords - xClampedWidgetCoords);
    // If nearby next key, clamp to it
    if (x2ClampedWidgetCoords > tNextWidgetCoords && deltaXtoNext > 1e-6) {
        // x2 is the position of the next keyframe with the period removed
        *x2WidgetCoords = xWidgetCoords + deltaXtoNext;
        x1Key->setValue(vnext);
        x1Key->setTime(x + (tnext - xClamped));
        *isx1Key = true;
    } else {
        // just add the delta to the x widget coord
        *x2WidgetCoords = xWidgetCoords + delta_x;
    }

} // nextPointForSegment