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); }
/// 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(); } }
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); } }
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); }
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
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()); }
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() ); } }
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; }
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); } } }
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
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); } }
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
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); }
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