Beispiel #1
0
void
KnobGui::onRemoveAnimationActionTriggered()
{
    QAction* action = qobject_cast<QAction*>( sender() );

    assert(action);
    int dim = action->data().toInt();
    KnobPtr knob = getKnob();
    std::map<boost::shared_ptr<CurveGui>, std::vector<KeyFrame > > toRemove;
    KnobGuiPtr thisShared = shared_from_this();
    for (int i = 0; i < knob->getDimension(); ++i) {
        if ( (dim == -1) || (dim == i) ) {
            std::list<boost::shared_ptr<CurveGui> > curves = getGui()->getCurveEditor()->findCurve(thisShared, i);
            for (std::list<boost::shared_ptr<CurveGui> >::iterator it = curves.begin(); it != curves.end(); ++it) {
                KeyFrameSet keys = (*it)->getInternalCurve()->getKeyFrames_mt_safe();
                std::vector<KeyFrame > vect;
                for (KeyFrameSet::const_iterator it2 = keys.begin(); it2 != keys.end(); ++it2) {
                    vect.push_back(*it2);
                }
                toRemove.insert( std::make_pair(*it, vect) );
            }
        }
    }
    pushUndoCommand( new RemoveKeysCommand(getGui()->getCurveEditor()->getCurveWidget(),
                                           toRemove) );
    //refresh the gui so it doesn't indicate the parameter is animated anymore
    updateGUI(dim);
}
Beispiel #2
0
void Curve::clone(const Curve& other)
{
    KeyFrameSet otherKeys = other.getKeyFrames_mt_safe();
    QWriteLocker l(&_imp->_lock);
    _imp->keyFrames.clear();
    std::transform(otherKeys.begin(), otherKeys.end(), std::inserter(_imp->keyFrames, _imp->keyFrames.begin()), KeyFrameCloner());
}
AnimItemDimViewKeyFrame
AnimationModuleViewPrivate::isNearbyKeyFrame(const ZoomContext& ctx, const QPoint & pt) const
{
    AnimItemDimViewKeyFrame ret;
    
    std::vector<CurveGuiPtr> curves = getVisibleCurves();

    for (std::vector<CurveGuiPtr>::const_iterator it = curves.begin(); it != curves.end(); ++it) {

        KeyFrameSet set = (*it)->getKeyFrames();

        for (KeyFrameSet::const_iterator it2 = set.begin(); it2 != set.end(); ++it2) {
            QPointF keyFramewidgetPos = ctx.toWidgetCoordinates( it2->getTime(), it2->getValue() );
            if ( (std::abs( pt.y() - keyFramewidgetPos.y() ) < TO_DPIX(CLICK_DISTANCE_TOLERANCE)) &&
                (std::abs( pt.x() - keyFramewidgetPos.x() ) < TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) {
                
                ret.key.key = *it2;
                StringAnimationManagerPtr stringAnim = (*it)->getItem()->getInternalAnimItem()->getStringAnimation();
                if (stringAnim) {
                    stringAnim->stringFromInterpolatedIndex(it2->getValue(), (*it)->getView(), &ret.key.string);
                }
                ret.id = (*it)->getCurveID();
                
                return ret;
            }
           
        }

    } // for all curves
    return ret;
} // CurveWidgetPrivate::isNearbyKeyFrame
Beispiel #4
0
void
TrackMarker::getCenterKeyframes(std::set<double>* keyframes) const
{
    CurvePtr curve = _imp->center.lock()->getAnimationCurve(ViewIdx(0), DimIdx(0));

    assert(curve);
    KeyFrameSet keys = curve->getKeyFrames_mt_safe();
    for (KeyFrameSet::iterator it = keys.begin(); it != keys.end(); ++it) {
        keyframes->insert( it->getTime() );
    }
}
Beispiel #5
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();
    }
}
std::pair<MoveTangentCommand::SelectedTangentEnum, AnimItemDimViewKeyFrame >
AnimationModuleViewPrivate::isNearbyTangent(const QPoint & pt) const
{
    
    std::pair<MoveTangentCommand::SelectedTangentEnum, AnimItemDimViewKeyFrame > ret;

    std::vector<CurveGuiPtr> curves = getVisibleCurves();

    for (std::vector<CurveGuiPtr>::const_iterator it = curves.begin(); it != curves.end(); ++it) {


        KeyFrameSet set = (*it)->getKeyFrames();
        
        for (KeyFrameSet::const_iterator it2 = set.begin(); it2 != set.end(); ++it2) {
            
            QPointF leftTanPos, rightTanPos;
            _publicInterface->getKeyTangentPoints(it2, set, &leftTanPos, &rightTanPos);
            QPointF leftTanPt = curveEditorZoomContext.toWidgetCoordinates(leftTanPos.x(), leftTanPos.y());
            QPointF rightTanPt = curveEditorZoomContext.toWidgetCoordinates( rightTanPos.x(), rightTanPos.y());
            if ( ( pt.x() >= (leftTanPt.x() - TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) &&
                ( pt.x() <= (leftTanPt.x() + TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) &&
                ( pt.y() <= (leftTanPt.y() + TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) &&
                ( pt.y() >= (leftTanPt.y() - TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) ) {
                
                ret.second.key.key = *it2;
                StringAnimationManagerPtr stringAnim = (*it)->getItem()->getInternalAnimItem()->getStringAnimation();
                if (stringAnim) {
                    stringAnim->stringFromInterpolatedIndex(it2->getValue(), (*it)->getView(), &ret.second.key.string);
                }
                ret.second.id = (*it)->getCurveID();
                ret.first = MoveTangentCommand::eSelectedTangentLeft;
                return ret;
            } else if ( ( pt.x() >= (rightTanPt.x() - TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) &&
                       ( pt.x() <= (rightTanPt.x() + TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) &&
                       ( pt.y() <= (rightTanPt.y() + TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) &&
                       ( pt.y() >= (rightTanPt.y() - TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) ) {
                ret.second.key.key = *it2;
                StringAnimationManagerPtr stringAnim = (*it)->getItem()->getInternalAnimItem()->getStringAnimation();
                if (stringAnim) {
                    stringAnim->stringFromInterpolatedIndex(it2->getValue(), (*it)->getView(), &ret.second.key.string);
                }
                ret.second.id = (*it)->getCurveID();
                ret.first = MoveTangentCommand::eSelectedTangentRight;
                return ret;
            }
        }
        
    } // for all curves
    return ret;
    
}
Beispiel #7
0
void
RestoreDefaultsCommand::undo()
{
    assert( _clones.size() == _knobs.size() );

    std::list<SequenceTime> times;
    KnobPtr first = _knobs.front().lock();
    AppInstance* app = first->getHolder()->getApp();
    assert(app);
    std::list<KnobWPtr >::const_iterator itClone = _clones.begin();
    for (std::list<KnobWPtr >::const_iterator it = _knobs.begin(); it != _knobs.end(); ++it, ++itClone) {
        KnobPtr itKnob = it->lock();
        if (!itKnob) {
            continue;
        }
        KnobPtr itCloneKnob = itClone->lock();
        if (!itCloneKnob) {
            continue;
        }
        itKnob->cloneAndUpdateGui( itCloneKnob.get() );

        if ( itKnob->getHolder()->getApp() ) {
            int dim = itKnob->getDimension();
            for (int i = 0; i < dim; ++i) {
                if ( (i == _targetDim) || (_targetDim == -1) ) {
                    boost::shared_ptr<Curve> c = itKnob->getCurve(ViewIdx(0), i);
                    if (c) {
                        KeyFrameSet kfs = c->getKeyFrames_mt_safe();
                        for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) {
                            times.push_back( std::floor(it->getTime() + 0.5) );
                        }
                    }
                }
            }
        }
    }
    app->addMultipleKeyframeIndicatorsAdded(times, true);

    first->getHolder()->incrHashAndEvaluate(true, true);
    if ( first->getHolder()->getApp() ) {
        first->getHolder()->getApp()->redrawAllViewers();
    }

    setText( tr("Restore default value(s)") );
}
void
AnimationModuleViewPrivate::keyFramesWithinRect(const RectD& canonicalRect, AnimItemDimViewKeyFramesMap* keys) const
{
    // always running in the main thread
    assert( qApp && qApp->thread() == QThread::currentThread() );

    if (canonicalRect.isNull()) {
        return;
    }
    
    std::vector<CurveGuiPtr> curves = getVisibleCurves();

    for (std::vector<CurveGuiPtr>::const_iterator it = curves.begin(); it != curves.end(); ++it) {

        KeyFrameSet set = (*it)->getKeyFrames();
        if ( set.empty() ) {
            continue;
        }

        AnimItemDimViewIndexID curveID = (*it)->getCurveID();
        StringAnimationManagerPtr stringAnim = curveID.item->getInternalAnimItem()->getStringAnimation();


        KeyFrameWithStringSet outKeys;
        
        for ( KeyFrameSet::const_iterator it2 = set.begin(); it2 != set.end(); ++it2) {
            double y = it2->getValue();
            double x = it2->getTime();
            if ( (x <= canonicalRect.x2) && (x >= canonicalRect.x1) && (y <= canonicalRect.y2) && (y >= canonicalRect.y1) ) {
                //KeyPtr newSelectedKey( new SelectedKey(*it, *it2, hasPrev, prevKey, hasNext, nextKey) );
                KeyFrameWithString k;
                k.key = *it2;
                if (stringAnim) {
                    stringAnim->stringFromInterpolatedIndex(it2->getValue(), curveID.view, &k.string);
                }
                outKeys.insert(k);
            }
        }
        if (!outKeys.empty()) {
            (*keys)[curveID] = outKeys;
        }
        
    }
} // CurveWidgetPrivate::keyFramesWithinRect
Beispiel #9
0
void Curve::clone(const Curve& other, SequenceTime offset, const RangeD* range)
{
    KeyFrameSet otherKeys = other.getKeyFrames_mt_safe();
    // The range=[0,0] case is obviously a bug in the spec of paramCopy() from the parameter suite:
    // it prevents copying the value of frame 0.
    bool copyRange = range != NULL /*&& (range->min != 0 || range->max != 0)*/;
    QWriteLocker l(&_imp->_lock);
    _imp->keyFrames.clear();
    for (KeyFrameSet::iterator it = otherKeys.begin(); it!=otherKeys.end(); ++it) {
        double time = it->getTime();
        if (copyRange && (time < range->min || time > range->max)) {
            continue;
        }
        KeyFrame k(*it);
        if (offset != 0) {
            k.setTime(time + offset);
        }
        _imp->keyFrames.insert(k);
    }
}
Beispiel #10
0
void
KnobGui::setAllKeyframeMarkersOnTimeline(int dimension)
{
    KnobPtr knob = getKnob();
    AppInstPtr app = knob->getHolder()->getApp();

    assert(app);
    std::list<SequenceTime> times;

    if (dimension == -1) {
        int dim = knob->getDimension();
        for (int i = 0; i < dim; ++i) {
            KeyFrameSet kfs = knob->getCurve(ViewIdx(0), i)->getKeyFrames_mt_safe();
            for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) {
                times.push_back( it->getTime() );
            }
        }
    } else {
        KeyFrameSet kfs = knob->getCurve(ViewIdx(0), dimension)->getKeyFrames_mt_safe();
        for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) {
            times.push_back( it->getTime() );
        }
    }
    app->addMultipleKeyframeIndicatorsAdded(times, true);
}
Beispiel #11
0
static void
deleteKnobAnimation(const std::set<double>& userKeyframes,
                    const KnobIPtr& knob,
                    DeleteKnobAnimationEnum type,
                    double currentTime)
{
    for (int i = 0; i < knob->getNDimensions(); ++i) {
        CurvePtr curve = knob->getAnimationCurve(ViewIdx(0), DimIdx(i));
        assert(curve);
        KeyFrameSet keys = curve->getKeyFrames_mt_safe();
        std::list<double> toRemove;
        switch (type) {
        case eDeleteKnobAnimationAll: {
            for (KeyFrameSet::iterator it = keys.begin(); it != keys.end(); ++it) {
                std::set<double>::iterator found = userKeyframes.find( it->getTime() );
                if ( found == userKeyframes.end() ) {
                    toRemove.push_back( it->getTime() );
                }
            }
            break;
        }
        case eDeleteKnobAnimationBeforeTime: {
            for (KeyFrameSet::iterator it = keys.begin(); it != keys.end(); ++it) {
                if (it->getTime() >= currentTime) {
                    break;
                }
                std::set<double>::iterator found = userKeyframes.find( it->getTime() );
                if ( found == userKeyframes.end() ) {
                    toRemove.push_back( it->getTime() );
                }
            }
            break;
        }
        case eDeleteKnobAnimationAfterTime: {
            for (KeyFrameSet::reverse_iterator it = keys.rbegin(); it != keys.rend(); ++it) {
                if (it->getTime() <= currentTime) {
                    break;
                }
                std::set<double>::iterator found = userKeyframes.find( it->getTime() );
                if ( found == userKeyframes.end() ) {
                    toRemove.push_back( it->getTime() );
                }
            }
            break;
        }
        }
        knob->deleteValuesAtTime(toRemove, ViewSetSpec::all(), DimIdx(i), eValueChangedReasonUserEdited);
    }
}
Beispiel #12
0
void
KnobGui::removeAllKeyframeMarkersOnTimeline(int dimension)
{
    KnobPtr knob = getKnob();

    if ( knob->getHolder() && knob->getHolder()->getApp() && !knob->getIsSecret() && ( knob->isDeclaredByPlugin() || knob->isUserKnob() ) ) {
        AppInstPtr app = knob->getHolder()->getApp();
        assert(app);
        std::list<SequenceTime> times;
        std::set<SequenceTime> tmpTimes;
        if (dimension == -1) {
            int dim = knob->getDimension();
            for (int i = 0; i < dim; ++i) {
                boost::shared_ptr<Curve> curve = knob->getCurve(ViewIdx(0), i);
                if (curve) {
                    KeyFrameSet kfs = curve->getKeyFrames_mt_safe();
                    for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) {
                        tmpTimes.insert( it->getTime() );
                    }
                }
            }
            for (std::set<SequenceTime>::iterator it = tmpTimes.begin(); it != tmpTimes.end(); ++it) {
                times.push_back(*it);
            }
        } else {
            boost::shared_ptr<Curve> curve = knob->getCurve(ViewIdx(0), dimension);
            if (curve) {
                KeyFrameSet kfs = curve->getKeyFrames_mt_safe();
                for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) {
                    times.push_back( it->getTime() );
                }
            }
        }
        if ( !times.empty() ) {
            app->removeMultipleKeyframeIndicator(times, true);
        }
    }
}
Beispiel #13
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
std::pair<MoveTangentCommand::SelectedTangentEnum, AnimItemDimViewKeyFrame >
AnimationModuleViewPrivate::isNearbySelectedTangentText(const QPoint & pt) const
{
    QFontMetrics fm( _publicInterface->font() );
    int yOffset = TO_DPIY(4);
    std::pair<MoveTangentCommand::SelectedTangentEnum, AnimItemDimViewKeyFrame > ret;
    
    AnimationModuleBasePtr model = _model.lock();
    const AnimItemDimViewKeyFramesMap& selectedKeys = model->getSelectionModel()->getCurrentKeyFramesSelection();
    if (selectedKeys.empty() || selectedKeys.size() > 1) {
        return ret;
    }

    AnimItemDimViewKeyFramesMap::const_iterator curveIT = selectedKeys.begin();
    const KeyFrameWithStringSet& keys = curveIT->second;
    if (keys.empty() || keys.size() > 1) {
        return ret;
    }

    const KeyFrameWithString& key = *keys.begin();
    CurvePtr curve = curveIT->first.item->getCurve(curveIT->first.dim, curveIT->first.view);
    if (!curve) {
        return ret;
    }
    KeyFrameSet curveKeys = curve->getKeyFrames_mt_safe();

    KeyFrameSet::iterator foundKey = Curve::findWithTime(curveKeys, curveKeys.begin(), key.key.getTime());
    assert(foundKey != curveKeys.end());
    if (foundKey == curveKeys.end()) {
        return ret;
    }
    for (KeyFrameWithStringSet::const_iterator it = keys.begin(); it != keys.end(); ++it) {


        QPointF leftTanPos, rightTanPos;
        _publicInterface->getKeyTangentPoints(foundKey, curveKeys, &leftTanPos, &rightTanPos);

        double rounding = std::pow(10., CURVEWIDGET_DERIVATIVE_ROUND_PRECISION);
        QPointF topLeft_LeftTanWidget = curveEditorZoomContext.toWidgetCoordinates( leftTanPos.x(), leftTanPos.y() );
        QPointF topLeft_RightTanWidget = curveEditorZoomContext.toWidgetCoordinates( rightTanPos.x(), rightTanPos.y() );
        topLeft_LeftTanWidget.ry() += yOffset;
        topLeft_RightTanWidget.ry() += yOffset;

        QString leftCoordStr =  QString( tr("l: %1") ).arg(std::floor( ( key.key.getLeftDerivative() * rounding ) + 0.5 ) / rounding);
        QString rightCoordStr =  QString( tr("r: %1") ).arg(std::floor( ( key.key.getRightDerivative() * rounding ) + 0.5 ) / rounding);
        QPointF btmRight_LeftTanWidget( topLeft_LeftTanWidget.x() + fm.width(leftCoordStr), topLeft_LeftTanWidget.y() + fm.height() );
        QPointF btmRight_RightTanWidget( topLeft_RightTanWidget.x() + fm.width(rightCoordStr), topLeft_RightTanWidget.y() + fm.height() );

        if ( (pt.x() >= topLeft_LeftTanWidget.x() - TO_DPIX(CLICK_DISTANCE_TOLERANCE)) && (pt.x() <= btmRight_LeftTanWidget.x() + TO_DPIX(CLICK_DISTANCE_TOLERANCE)) &&
            ( pt.y() >= topLeft_LeftTanWidget.y() - TO_DPIX(CLICK_DISTANCE_TOLERANCE)) && ( pt.y() <= btmRight_LeftTanWidget.y() + TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) {
            ret.second.key.key = key.key;
            StringAnimationManagerPtr stringAnim = curveIT->first.item->getInternalAnimItem()->getStringAnimation();
            if (stringAnim) {
                stringAnim->stringFromInterpolatedIndex(key.key.getValue(), curveIT->first.view, &ret.second.key.string);
            }
            ret.second.id = curveIT->first;
            ret.first = MoveTangentCommand::eSelectedTangentLeft;
        } else if ( (pt.x() >= topLeft_RightTanWidget.x() - TO_DPIX(CLICK_DISTANCE_TOLERANCE)) && (pt.x() <= btmRight_RightTanWidget.x() + TO_DPIX(CLICK_DISTANCE_TOLERANCE)) &&
                   ( pt.y() >= topLeft_RightTanWidget.y() - TO_DPIX(CLICK_DISTANCE_TOLERANCE)) && ( pt.y() <= btmRight_RightTanWidget.y() + TO_DPIX(CLICK_DISTANCE_TOLERANCE)) ) {
            ret.second.key.key = key.key;
            StringAnimationManagerPtr stringAnim = curveIT->first.item->getInternalAnimItem()->getStringAnimation();
            if (stringAnim) {
                stringAnim->stringFromInterpolatedIndex(key.key.getValue(), curveIT->first.view, &ret.second.key.string);
            }
            ret.second.id = curveIT->first;
            ret.first = MoveTangentCommand::eSelectedTangentRight;
        }



    } // for all curves
    return ret;

} // isNearbySelectedTangentText
Beispiel #15
0
void
CurveGui::drawCurve(int curveIndex,
                    int curvesCount)
{
    // always running in the main thread
    assert( qApp && qApp->thread() == QThread::currentThread() );

    AnimItemBasePtr item = _imp->item.lock();
    if (!item) {
        return;
    }

    std::vector<float> vertices, exprVertices;
    const double widgetWidth = _imp->curveWidget->width();
    KeyFrameSet keyframes;
    bool hasDrawnExpr = false;
    if (item->hasExpression(_imp->dimension, _imp->view)) {

        //we have no choice but to evaluate the expression at each time
        for (int i = 0; i < widgetWidth; ++i) {
            double x = _imp->curveWidget->toZoomCoordinates(i, 0).x();
            double y = evaluate(true /*useExpr*/, x);
            exprVertices.push_back(x);
            exprVertices.push_back(y);
        }
        hasDrawnExpr = true;

    }


    QPointF btmLeft = _imp->curveWidget->toZoomCoordinates(0, _imp->curveWidget->height() - 1);
    QPointF topRight = _imp->curveWidget->toZoomCoordinates(_imp->curveWidget->width() - 1, 0);

    


    bool isPeriodic = false;
    std::pair<double,double> parametricRange = std::make_pair(-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity());

    keyframes = getInternalCurve()->getKeyFrames_mt_safe();
    isPeriodic = getInternalCurve()->isCurvePeriodic();
    parametricRange = getInternalCurve()->getXRange();

    if ( keyframes.empty() ) {
        // Add a horizontal line for constant knobs, except string knobs.
        KnobIPtr isKnob = boost::dynamic_pointer_cast<KnobI>(item->getInternalAnimItem());

        if (isKnob) {
            KnobStringBasePtr isString = boost::dynamic_pointer_cast<KnobStringBase>(isKnob);
            if (!isString) {
                double value = evaluate(false, 0);
                vertices.push_back(btmLeft.x() + 1);
                vertices.push_back(value);
                vertices.push_back(topRight.x() - 1);
                vertices.push_back(value);
            }
        }
    } else {
        try {
            double x1 = 0;
            double x2;

            bool isX1AKey = false;
            KeyFrame x1Key;
            KeyFrameSet::const_iterator lastUpperIt = keyframes.end();

            while ( x1 < (widgetWidth - 1) ) {
                double x, y;
                if (!isX1AKey) {
                    x = _imp->curveWidget->toZoomCoordinates(x1, 0).x();
                    y = evaluate(false, x);
                } else {
                    x = x1Key.getTime();
                    y = x1Key.getValue();
                }

                vertices.push_back( (float)x );
                vertices.push_back( (float)y );
                nextPointForSegment(x, keyframes, isPeriodic, parametricRange.first, parametricRange.second,  &lastUpperIt, &x2, &x1Key, &isX1AKey);
                x1 = x2;
            }
            //also add the last point
            {
                double x = _imp->curveWidget->toZoomCoordinates(x1, 0).x();
                double y = evaluate(false, x);
                vertices.push_back( (float)x );
                vertices.push_back( (float)y );
            }
        } catch (...) {
        }
    }

    // No Expr curve or no vertices for the curve, don't draw anything else
    if (exprVertices.empty() && vertices.empty()) {
        return;
    }

    AnimationModuleSelectionModelPtr selectionModel = item->getModel()->getSelectionModel();
    assert(selectionModel);
    const AnimItemDimViewKeyFramesMap& selectedKeys = selectionModel->getCurrentKeyFramesSelection();


    const KeyFrameWithStringSet* foundThisCurveSelectedKeys = 0;
    {
        AnimItemDimViewIndexID k;
        k.item = item;
        k.dim = _imp->dimension;
        k.view = _imp->view;
        AnimItemDimViewKeyFramesMap::const_iterator foundDimView = selectedKeys.find(k);

        if (foundDimView != selectedKeys.end()) {
            foundThisCurveSelectedKeys = &foundDimView->second;
        }
    }


    {
        GLProtectAttrib<GL_GPU> a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT);

        // If this is the only curve selected, draw min/max
        if (foundThisCurveSelectedKeys && selectedKeys.size()) {

            // Draw y min/max axis so the user understands why the value is clamped
            Curve::YRange curveYRange = getCurveYRange();
            if (curveYRange.min != INT_MIN &&
                curveYRange.min != -std::numeric_limits<double>::infinity() &&
                curveYRange.max != INT_MAX &&
                curveYRange.max != std::numeric_limits<double>::infinity() ) {
                QColor minMaxColor;
                minMaxColor.setRgbF(0.398979, 0.398979, 0.398979);
                GL_GPU::Color4d(minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), 1.);
                GL_GPU::Begin(GL_LINES);
                GL_GPU::Vertex2d(btmLeft.x(), curveYRange.min);
                GL_GPU::Vertex2d(topRight.x(), curveYRange.min);
                GL_GPU::Vertex2d(btmLeft.x(), curveYRange.max);
                GL_GPU::Vertex2d(topRight.x(), curveYRange.max);
                GL_GPU::End();
                GL_GPU::Color4d(1., 1., 1., 1.);

                double xText = _imp->curveWidget->toZoomCoordinates(10, 0).x();

                _imp->curveWidget->renderText( xText, curveYRange.min, tr("min").toStdString(), minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), minMaxColor.alphaF());
                _imp->curveWidget->renderText( xText, curveYRange.max, tr("max").toStdString(), minMaxColor.redF(), minMaxColor.greenF(), minMaxColor.blueF(), minMaxColor.alphaF());
            }
        }


        GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]);
        GL_GPU::PointSize(_imp->lineWidth);
        GL_GPU::Enable(GL_BLEND);
        GL_GPU::BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        GL_GPU::Enable(GL_LINE_SMOOTH);
        GL_GPU::Hint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
        GL_GPU::LineWidth(1.5);
        glCheckError(GL_GPU);
        if (hasDrawnExpr) {
            drawLineStrip(exprVertices, btmLeft, topRight);
            GL_GPU::LineStipple(2, 0xAAAA);
            GL_GPU::Enable(GL_LINE_STIPPLE);
        }
        drawLineStrip(vertices, btmLeft, topRight);
        if (hasDrawnExpr) {
            GL_GPU::Disable(GL_LINE_STIPPLE);
        }

        glCheckErrorIgnoreOSXBug(GL_GPU);

        //render the name of the curve
        GL_GPU::Color4f(1.f, 1.f, 1.f, 1.f);


        double interval = ( topRight.x() - btmLeft.x() ) / (double)curvesCount;
        double textX = _imp->curveWidget->toZoomCoordinates(15, 0).x() + interval * (double)curveIndex;
        double textY;

        CurvePtr internalCurve = _imp->internalCurve.lock();
        QString curveName = getName();
        QColor thisColor;
        thisColor.setRgbF(Image::clamp(_imp->color[0], 0., 1.),
                          Image::clamp(_imp->color[1], 0., 1.),
                          Image::clamp(_imp->color[2], 0., 1.));

        try {
            // Use expression to place the text if the curve is not animated
            textY = evaluate(internalCurve && !internalCurve->isAnimated(), textX);
        } catch (...) {
            // if it fails attempt without expression, this will most likely return a constant value
            textY = evaluate(false /*useExpression*/, textX);
        }

        if ( ( textX >= btmLeft.x() ) && ( textX <= topRight.x() ) && ( textY >= btmLeft.y() ) && ( textY <= topRight.y() ) ) {
            _imp->curveWidget->renderText( textX, textY, curveName.toStdString(), thisColor.redF(), thisColor.greenF(), thisColor.blueF(), thisColor.alphaF());
        }
        GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]);

        //draw keyframes
        GL_GPU::PointSize(7.f);
        GL_GPU::Enable(GL_POINT_SMOOTH);


        KeyFrameWithStringSet::const_iterator foundSelectedKey;
        if (foundThisCurveSelectedKeys) {
            foundSelectedKey = foundThisCurveSelectedKeys->end();
        }
        for (KeyFrameSet::const_iterator k = keyframes.begin(); k != keyframes.end(); ++k) {
            const KeyFrame & key = (*k);

            // Do not draw keyframes out of range
            if ( ( key.getTime() < btmLeft.x() ) || ( key.getTime() > topRight.x() ) || ( key.getValue() < btmLeft.y() ) || ( key.getValue() > topRight.y() ) ) {
                continue;
            }

            GL_GPU::Color4f(_imp->color[0], _imp->color[1], _imp->color[2], _imp->color[3]);

            bool drawKeySelected = false;
            if (foundThisCurveSelectedKeys) {
                KeyFrameWithStringSet::const_iterator start = foundSelectedKey == foundThisCurveSelectedKeys->end() ? foundThisCurveSelectedKeys->begin() : foundSelectedKey;
                foundSelectedKey = std::find_if(start, foundThisCurveSelectedKeys->end(), KeyFrameWithStringTimePredicate(key.getTime()));
                drawKeySelected = foundSelectedKey != foundThisCurveSelectedKeys->end();
                if (!drawKeySelected) {
                    // Also draw the keyframe as selected if it is inside the selection rectangle (but not yet selected)
                    RectD selectionRect = _imp->curveWidget->getSelectionRectangle();
                    drawKeySelected |= _imp->curveWidget->_imp->eventTriggeredFromCurveEditor && (!selectionRect.isNull() && selectionRect.contains(key.getTime(), key.getValue()));
                }
            }
            // If the key is selected change its color
            if (drawKeySelected) {
                GL_GPU::Color4f(0.8f, 0.8f, 0.8f, 1.f);
            }


            RectD keyframeBbox = _imp->curveWidget->_imp->getKeyFrameBoundingRectCanonical(_imp->curveWidget->_imp->curveEditorZoomContext, key.getTime(), key.getValue());

            // draw texture of the keyframe
            {
                AnimationModuleViewPrivate::KeyframeTexture texType = AnimationModuleViewPrivate::kfTextureFromKeyframeType( key.getInterpolation(), drawKeySelected);
                if (texType != AnimationModuleViewPrivate::kfTextureNone) {
                    _imp->curveWidget->_imp->drawTexturedKeyframe(texType, keyframeBbox, false /*drawdimed*/);
                }
            }

            
            // Draw tangents if not constant
            bool drawTangents = drawKeySelected && internalCurve->isYComponentMovable() && (key.getInterpolation() != eKeyframeTypeConstant);
            if (drawTangents) {
                QFontMetrics m( _imp->curveWidget->font());


                // If interpolation is not free and not broken display with dashes the tangents lines
                if ( (key.getInterpolation() != eKeyframeTypeFree) && (key.getInterpolation() != eKeyframeTypeBroken) ) {
                    GL_GPU::LineStipple(2, 0xAAAA);
                    GL_GPU::Enable(GL_LINE_STIPPLE);
                }
                
                QPointF leftTanPos, rightTanPos;
                _imp->curveWidget->getKeyTangentPoints(k, keyframes, &leftTanPos, &rightTanPos);

                // Draw the derivatives lines
                GL_GPU::Begin(GL_LINES);
                GL_GPU::Color4f(1., 0.35, 0.35, 1.);
                GL_GPU::Vertex2f( leftTanPos.x(), leftTanPos.y() );
                GL_GPU::Vertex2f(key.getTime(), key.getValue());
                GL_GPU::Vertex2f(key.getTime(), key.getValue());
                GL_GPU::Vertex2f( rightTanPos.x(), rightTanPos.y());
                GL_GPU::End();
                if ( (key.getInterpolation() != eKeyframeTypeFree) && (key.getInterpolation() != eKeyframeTypeBroken) ) {
                    GL_GPU::Disable(GL_LINE_STIPPLE);
                }


                // Draw the tangents handles
                GL_GPU::Begin(GL_POINTS);
                GL_GPU::Vertex2f( leftTanPos.x(), leftTanPos.y() );
                GL_GPU::Vertex2f( rightTanPos.x(), rightTanPos.y());
                GL_GPU::End();

                // If only one keyframe is selected, also draw the coordinates
                if (selectedKeys.size() == 1 && foundThisCurveSelectedKeys && foundThisCurveSelectedKeys->size() == 1) {
                    double rounding = std::pow(10., CURVEWIDGET_DERIVATIVE_ROUND_PRECISION);
                    QString leftDerivStr = QString::fromUtf8("l: %1").arg(std::floor( (key.getLeftDerivative() * rounding) + 0.5 ) / rounding);
                    QString rightDerivStr = QString::fromUtf8("r: %1").arg(std::floor( (key.getRightDerivative() * rounding) + 0.5 ) / rounding);
                    double yLeftWidgetCoord = _imp->curveWidget->toWidgetCoordinates(0, leftTanPos.y()).y();
                    yLeftWidgetCoord += (m.height() + 4);

                    double yRightWidgetCoord = _imp->curveWidget->toWidgetCoordinates(0, rightTanPos.y()).y();
                    yRightWidgetCoord += (m.height() + 4);

                    GL_GPU::Color4f(1., 1., 1., 1.);
                    glCheckFramebufferError(GL_GPU);
                    _imp->curveWidget->renderText( leftTanPos.x(), _imp->curveWidget->toZoomCoordinates(0, yLeftWidgetCoord).y(),
                                              leftDerivStr.toStdString(), 0.9, 0.9, 0.9, 1.);
                    _imp->curveWidget->renderText( rightTanPos.x(), _imp->curveWidget->toZoomCoordinates(0, yRightWidgetCoord).y(),
                                                  rightDerivStr.toStdString(), 0.9, 0.9, 0.9, 1.);


                    QString coordStr = QString::fromUtf8("x: %1, y: %2");
                    coordStr = coordStr.arg(key.getTime()).arg(key.getValue());
                    double yWidgetCoord = _imp->curveWidget->toWidgetCoordinates( 0, key.getValue() ).y();
                    yWidgetCoord += (m.height() + 4);
                    GL_GPU::Color4f(1., 1., 1., 1.);
                    glCheckFramebufferError(GL_GPU);
                    _imp->curveWidget->renderText( key.getTime(), _imp->curveWidget->toZoomCoordinates(0, yWidgetCoord).y(),
                                                  coordStr.toStdString(), 0.9, 0.9, 0.9, 1.);

                }

            } // drawTangents

        } // for (KeyFrameSet::const_iterator k = keyframes.begin(); k != keyframes.end(); ++k) {
    } // GLProtectAttrib(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT);

    glCheckError(GL_GPU);
} // drawCurve
void
moveGroupNode(DopeSheetEditor* model,
              const NodePtr& node,
              double dt)
{
    NodeGroupPtr group = node->isEffectNodeGroup();

    assert(group);
    NodesList nodes;
    group->getNodes_recursive(nodes, true);

    for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) {
        NodeGuiPtr nodeGui = boost::dynamic_pointer_cast<NodeGui>( (*it)->getNodeGui() );
        assert(nodeGui);
        std::string pluginID = (*it)->getPluginID();
        NodeGroupPtr isChildGroup = (*it)->isEffectNodeGroup();

        // Move readers
#ifndef NATRON_ENABLE_IO_META_NODES
        if ( ReadNode::isBundledReader( pluginID, node->getApp()->wasProjectCreatedWithLowerCaseIDs() ) ) {
#else
        if (pluginID == PLUGINID_NATRON_READ) {
#endif
            moveReader(*it, dt);
        } else if (pluginID == PLUGINID_OFX_TIMEOFFSET) {
            moveTimeOffset(*it, dt);
        } else if (pluginID == PLUGINID_OFX_FRAMERANGE) {
            moveFrameRange(*it, dt);
        } else if (isChildGroup) {
            moveGroupNode(model, *it, dt);
        }

        // Move keyframes
        const KnobsVec &knobs = (*it)->getKnobs();

        for (KnobsVec::const_iterator knobIt = knobs.begin(); knobIt != knobs.end(); ++knobIt) {
            const KnobIPtr& knob = *knobIt;
            if ( !knob->hasAnimation() ) {
                continue;
            }

            for (int dim = 0; dim < knob->getDimension(); ++dim) {
                if ( !knob->isAnimated( dim, ViewIdx(0) ) ) {
                    continue;
                }
                KeyFrameSet keyframes = knob->getCurve(ViewIdx(0), dim)->getKeyFrames_mt_safe();

                for (KeyFrameSet::iterator kfIt = keyframes.begin(); kfIt != keyframes.end(); ++kfIt) {
                    KeyFrame kf = (*kfIt);
                    KeyFrame fake;

                    knob->moveValueAtTime(eCurveChangeReasonDopeSheet, kf.getTime(), ViewSpec::all(), dim, dt, 0, &fake);
                }
            }
        }
    }
} // moveGroupNode

NATRON_NAMESPACE_ANONYMOUS_EXIT


////////////////////////// DSMoveKeysCommand //////////////////////////

DSMoveKeysAndNodesCommand::DSMoveKeysAndNodesCommand(const DSKeyPtrList &keys,
                                                     const std::vector<DSNodePtr >& nodes,
                                                     double dt,
                                                     DopeSheetEditor *model,
                                                     QUndoCommand *parent)
    : QUndoCommand(parent),
    _keys(keys),
    _nodes(),
    _dt(dt),
    _model(model)
{
    setText( tr("Move selected keys") );
    std::set<NodePtr > nodesSet;
    for (std::vector<DSNodePtr >::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
        DopeSheetItemType type = (*it)->getItemType();
        if ( (type != eDopeSheetItemTypeReader) &&
             ( type != eDopeSheetItemTypeGroup) &&
             ( type != eDopeSheetItemTypeTimeOffset) &&
             ( type != eDopeSheetItemTypeFrameRange) ) {
            //Note that Retime nodes cannot be moved
            continue;
        }
        _nodes.push_back(*it);
        nodesSet.insert( (*it)->getInternalNode() );
        NodeGroupPtr isGroup = (*it)->getInternalNode()->isEffectNodeGroup();
        if (isGroup) {
            NodesList recurseNodes;
            isGroup->getNodes_recursive(recurseNodes, true);
            for (NodesList::iterator it = recurseNodes.begin(); it != recurseNodes.end(); ++it) {
                nodesSet.insert(*it);
            }
        }
    }

    for (DSKeyPtrList::iterator it = _keys.begin(); it != _keys.end(); ++it) {
        KnobHolderPtr holder = (*it)->getContext()->getInternalKnob()->getHolder();
        assert(holder);
        EffectInstancePtr isEffect = toEffectInstance(holder);
        if (isEffect) {
            nodesSet.insert( isEffect->getNode() );
        }
    }

    for (std::set<NodePtr >::iterator it = nodesSet.begin(); it != nodesSet.end(); ++it) {
        _allDifferentNodes.push_back(*it);
    }
}

void
DSMoveKeysAndNodesCommand::undo()
{
    moveSelection(-_dt);
}
Beispiel #17
0
void
RestoreDefaultsCommand::redo()
{
    std::list<SequenceTime> times;
    KnobPtr first = _knobs.front().lock();
    AppInstance* app = 0;
    KnobHolder* holder = first->getHolder();
    EffectInstance* isEffect = dynamic_cast<EffectInstance*>(holder);

    if (holder) {
        app = holder->getApp();
        holder->beginChanges();
    }


    /*
       First reset all knobs values, this will not call instanceChanged action
     */
    for (std::list<KnobWPtr >::iterator it = _knobs.begin(); it != _knobs.end(); ++it) {
        KnobPtr itKnob = it->lock();
        if (!itKnob) {
            continue;
        }
        if ( itKnob->getHolder() && itKnob->getHolder()->getApp() ) {
            int dim = itKnob->getDimension();
            for (int i = 0; i < dim; ++i) {
                if ( (i == _targetDim) || (_targetDim == -1) ) {
                    boost::shared_ptr<Curve> c = itKnob->getCurve(ViewIdx(0), i);
                    if (c) {
                        KeyFrameSet kfs = c->getKeyFrames_mt_safe();
                        for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) {
                            times.push_back( std::floor(it->getTime() + 0.5) );
                        }
                    }
                }
            }
        }

        if ( itKnob->getHolder() ) {
            itKnob->getHolder()->beginChanges();
        }
        itKnob->blockValueChanges();

        for (int d = 0; d < itKnob->getDimension(); ++d) {
            itKnob->resetToDefaultValue(d);
        }

        itKnob->unblockValueChanges();

        if ( itKnob->getHolder() ) {
            itKnob->getHolder()->endChanges(true);
        }
    }

    /*
       Block value changes and call instanceChange on all knobs  afterwards to put back the plug-in
       in a correct state
     */
    double time = 0;
    if (app) {
        time = app->getTimeLine()->currentFrame();
    }
    for (std::list<KnobWPtr >::iterator it = _knobs.begin(); it != _knobs.end(); ++it) {
        KnobPtr itKnob = it->lock();
        if (!itKnob) {
            continue;
        }
        if ( itKnob->getHolder() ) {
            itKnob->getHolder()->onKnobValueChanged_public(itKnob.get(), eValueChangedReasonRestoreDefault, time, ViewIdx(0), true);
        }
    }

    if (app) {
        app->removeMultipleKeyframeIndicator(times, true);
    }


    if ( holder && holder->getApp() ) {
        holder->endChanges();
    }

    if (_isNodeReset && isEffect) {
        isEffect->purgeCaches();
    }


    if ( first->getHolder() ) {
        first->getHolder()->incrHashAndEvaluate(true, true);
        if ( first->getHolder()->getApp() ) {
            first->getHolder()->getApp()->redrawAllViewers();
        }
    }
    setText( tr("Restore default value(s)") );
} // RestoreDefaultsCommand::redo