bool CornerPinOverlayInteract::onOverlayPenUp(TimeValue time, const RenderScale & /*renderScale*/, ViewIdx view, const QPointF & /*viewportPos*/, const QPointF & /*pos*/, double /*pressure*/, TimeValue /*timestamp*/) { // do not show interact if knob is secret or not enabled // see https://github.com/MrKepzie/Natron/issues/932 KnobDoublePtr from1Knob = _imp->from[0].lock(); if ( !from1Knob || !from1Knob->shouldDrawOverlayInteract() ) { return false; } bool didSomething = _imp->dragging != -1; if ( !_imp->interactiveDrag && (_imp->dragging != -1) ) { // no need to redraw overlay since it is slave to the paramaters if (_imp->useFromDrag) { KnobDoublePtr knob = _imp->from[_imp->dragging].lock(); assert(knob); std::vector<double> val(2); val[0] = _imp->fromDrag[_imp->dragging].x; val[1] = _imp->fromDrag[_imp->dragging].y; knob->setValueAcrossDimensions(val, DimIdx(0), view, eValueChangedReasonUserEdited); } else { KnobDoublePtr knob = _imp->to[_imp->dragging].lock(); assert(knob); std::vector<double> val(2); val[0] = _imp->toDrag[_imp->dragging].x; val[1] = _imp->toDrag[_imp->dragging].y; if (_imp->toPointsAutoKeyingEnabled) { knob->setValueAtTimeAcrossDimensions(time, val, DimIdx(0), view, eValueChangedReasonUserEdited); // Also set a keyframe on other points for (int i = 0; i < 4; ++i) { if (i == _imp->dragging) { continue; } std::vector<double> values(2); KnobDoublePtr toPoint = _imp->to[i].lock(); values[0] = toPoint->getValueAtTime(time, DimIdx(0)); values[1] = toPoint->getValueAtTime(time, DimIdx(1)); toPoint->setValueAtTimeAcrossDimensions(time, values, DimIdx(0), view, eValueChangedReasonUserEdited); } } else { knob->setValueAcrossDimensions(val, DimIdx(0), view, eValueChangedReasonUserEdited); } } } _imp->dragging = -1; return didSomething; } // onOverlayPenUp
void ViewerNode::setUserRoI(const RectD& rect) { KnobDoublePtr btmLeft = _imp->userRoIBtmLeftKnob.lock(); KnobDoublePtr size = _imp->userRoISizeKnob.lock(); std::vector<double> values(2); values[0] = rect.x1; values[1] = rect.y1; btmLeft->setValueAcrossDimensions(values, DimIdx(0), ViewSetSpec::all(), eValueChangedReasonUserEdited); values[0] = rect.x2 - rect.x1; values[1] = rect.y2 - rect.y1; size->setValueAcrossDimensions(values, DimIdx(0), ViewSetSpec::all(), eValueChangedReasonUserEdited); }
void TrackMarker::resetTrack() { Point curCenter; KnobDoublePtr centerKnob = getCenterKnob(); curCenter.x = centerKnob->getValue(); curCenter.y = centerKnob->getValue(DimIdx(1)); const KnobsVec& knobs = getKnobs(); for (KnobsVec::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { if (*it != centerKnob) { (*it)->resetToDefaultValue(DimSpec::all(), ViewSetSpec::all()); } else { (*it)->removeAnimation(ViewSetSpec::all(), DimSpec::all(), eValueChangedReasonUserEdited); std::vector<double> values(2); values[0] = curCenter.x; values[1] = curCenter.y; centerKnob->setValueAcrossDimensions(values); } } removeAnimation(ViewSetSpec::all(), DimSpec::all(), eValueChangedReasonUserEdited); }
bool PointOverlayInteract::onOverlayPenUp(TimeValue time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & penPos, double pressure, TimeValue timestamp) { KnobDoublePtr knob = _imp->param.lock(); // do not show interact if knob is secret or not enabled // see https://github.com/MrKepzie/Natron/issues/932 if ( !knob || !knob->shouldDrawOverlayInteract() ) { return false; } RenderScale pscale; getPixelScale(pscale.x, pscale.y); bool didSomething = false; if (_imp->state == ePositionInteractStatePicked) { if (!_imp->interactiveDrag) { std::vector<double> p(2); p[0] = fround(_imp->lastPenPos.x(), pscale.x); p[1] = fround(_imp->lastPenPos.y(), pscale.y); for (int i = 0; i < 2; ++i) { if (knob->getValueIsNormalized(DimIdx(i)) != eValueIsNormalizedNone) { p[i] = knob->normalize(DimIdx(i), time, p[i]); } } knob->setValueAcrossDimensions(p, DimIdx(0), view, eValueChangedReasonUserEdited); } _imp->state = ePositionInteractStateInactive; bool motion = onOverlayPenMotion(time, renderScale, view, viewportPos, penPos, pressure, timestamp); Q_UNUSED(motion); didSomething = true; } return didSomething; } // onOverlayPenUp
void TrackMarker::setKeyFrameOnCenterAndPatternAtTime(TimeValue time) { KnobDoublePtr center = _imp->center.lock(); { std::vector<double> values(2); values[0] = center->getValueAtTime(time); values[1] = center->getValueAtTime(time, DimIdx(1)); center->setValueAtTimeAcrossDimensions(time, values); } KnobDoublePtr patternCorners[4] = {_imp->patternBtmLeft.lock(), _imp->patternTopLeft.lock(), _imp->patternTopRight.lock(), _imp->patternBtmRight.lock()}; for (int c = 0; c < 4; ++c) { KnobDoublePtr k = patternCorners[c]; std::vector<double> values(2); values[0] = k->getValueAtTime(time, DimIdx(0)); values[1] = k->getValueAtTime(time, DimIdx(1)); k->setValueAcrossDimensions(values); } }
void TrackerHelper::trackMarkers(const std::list<TrackMarkerPtr >& markers, TimeValue start, TimeValue end, TimeValue frameStep, const ViewerNodePtr& viewer) { if ( markers.empty() ) { Q_EMIT trackingFinished(); return; } TrackerParamsProviderPtr provider = _imp->provider.lock(); if (!provider) { Q_EMIT trackingFinished(); return; } NodePtr trackerNode = provider->getTrackerNode(); if (!trackerNode) { Q_EMIT trackingFinished(); return; } if (trackerNode->hasMandatoryInputDisconnected()) { Q_EMIT trackingFinished(); return; } // The channels we are going to use for tracking bool enabledChannels[3]; provider->getTrackChannels(&enabledChannels[0], &enabledChannels[1], &enabledChannels[2]); double formatWidth, formatHeight; Format f; trackerNode->getApp()->getProject()->getProjectDefaultFormat(&f); formatWidth = f.width(); formatHeight = f.height(); bool autoKeyingOnEnabledParamEnabled = provider->canDisableMarkersAutomatically(); /// The accessor and its cache is local to a track operation, it is wiped once the whole sequence track is finished. boost::shared_ptr<TrackerFrameAccessor> accessor( new TrackerFrameAccessor(trackerNode, enabledChannels, formatHeight) ); boost::shared_ptr<mv::AutoTrack> trackContext( new mv::AutoTrack( accessor.get() ) ); std::vector<TrackMarkerAndOptionsPtr > trackAndOptions; mv::TrackRegionOptions mvOptions; /* Get the global parameters for the LivMV track: pre-blur sigma, No iterations, normalized intensities, etc... */ _imp->beginLibMVOptionsForTrack(&mvOptions); /* For the given markers, do the following: - Get the "User" keyframes that have been set and create a LibMV marker for each keyframe as well as for the "start" time - Set the "per-track" parameters that were given by the user, that is the mv::TrackRegionOptions - t->mvMarker will contain the marker that evolves throughout the tracking */ int trackIndex = 0; for (std::list<TrackMarkerPtr >::const_iterator it = markers.begin(); it != markers.end(); ++it, ++trackIndex) { if (autoKeyingOnEnabledParamEnabled) { (*it)->setEnabledAtTime(start, true); } TrackMarkerAndOptionsPtr t(new TrackMarkerAndOptions); t->natronMarker = *it; int mode_i = (*it)->getMotionModelKnob()->getValue(); mvOptions.mode = motionModelIndexToLivMVMode(mode_i); // Set a keyframe on the marker to initialize its position { KnobDoublePtr centerKnob = (*it)->getCenterKnob(); std::vector<double> values(2); values[0] = centerKnob->getValueAtTime(start, DimIdx(0)); values[1] = centerKnob->getValueAtTime(start, DimIdx(1)); centerKnob->setValueAtTimeAcrossDimensions(start, values); } // For a translation warp, we do not need to add an animation curve for the pattern which stays constant. if (mvOptions.mode != libmv::TrackRegionOptions::TRANSLATION) { KnobDoublePtr patternCorners[4]; patternCorners[0] = (*it)->getPatternBtmLeftKnob(); patternCorners[1] = (*it)->getPatternBtmRightKnob(); patternCorners[2] = (*it)->getPatternTopRightKnob(); patternCorners[3] = (*it)->getPatternTopLeftKnob(); for (int c = 0; c < 4; ++c) { KnobDoublePtr k = patternCorners[c]; std::vector<double> values(2); values[0] = k->getValueAtTime(start, DimIdx(0)); values[1] = k->getValueAtTime(start, DimIdx(1)); k->setValueAcrossDimensions(values); } } std::set<double> userKeys; t->natronMarker->getMasterKeyFrameTimes(ViewIdx(0), &userKeys); if ( userKeys.empty() ) { // Set a user keyframe on tracking start if the marker does not have any user keys t->natronMarker->setKeyFrame(start, ViewSetSpec(0), 0); } PreviouslyTrackedFrameSet previousFramesOrdered; // Make sure to create a marker at the start time userKeys.insert(start); // Add a libmv marker for all keyframes for (std::set<double>::iterator it2 = userKeys.begin(); it2 != userKeys.end(); ++it2) { // Add the marker to the markers ordered only if it can contribute to predicting its next position if ( ( (frameStep > 0) && (*it2 <= start) ) || ( (frameStep < 0) && (*it2 >= start) ) ) { previousFramesOrdered.insert( PreviouslyComputedTrackFrame(TimeValue(*it2), true) ); } } //For all already tracked frames which are not keyframes, add them to the AutoTrack too std::set<double> centerKeys; t->natronMarker->getCenterKeyframes(¢erKeys); for (std::set<double>::iterator it2 = centerKeys.begin(); it2 != centerKeys.end(); ++it2) { if ( userKeys.find(*it2) != userKeys.end() ) { continue; } // Add the marker to the markers ordered only if it can contribute to predicting its next position if ( ( ( (frameStep > 0) && (*it2 < start) ) || ( (frameStep < 0) && (*it2 > start) ) ) ) { previousFramesOrdered.insert( PreviouslyComputedTrackFrame(TimeValue(*it2), false) ); } } // Taken from libmv, only initialize the filter to this amount of frames (max) const int max_frames_to_predict_from = 20; std::list<mv::Marker> previouslyComputedMarkersOrdered; // Find the first keyframe that's not considered to go before start or end PreviouslyTrackedFrameSet::iterator prevFramesIt = previousFramesOrdered.lower_bound(PreviouslyComputedTrackFrame(start, false)); if (frameStep < 0) { if (prevFramesIt != previousFramesOrdered.end()) { while (prevFramesIt != previousFramesOrdered.end() && (int)previouslyComputedMarkersOrdered.size() != max_frames_to_predict_from) { mv::Marker mvMarker; TrackerHelperPrivate::natronTrackerToLibMVTracker(true, enabledChannels, *t->natronMarker, trackIndex, TimeValue(prevFramesIt->frame), frameStep, formatHeight, &mvMarker); trackContext->AddMarker(mvMarker); // insert in the front of the list so that the order is reversed previouslyComputedMarkersOrdered.push_front(mvMarker); ++prevFramesIt; } } // previouslyComputedMarkersOrdered is now ordererd by decreasing order } else { if (prevFramesIt != previousFramesOrdered.end()) { while (prevFramesIt != previousFramesOrdered.begin() && (int)previouslyComputedMarkersOrdered.size() != max_frames_to_predict_from) { mv::Marker mvMarker; TrackerHelperPrivate::natronTrackerToLibMVTracker(true, enabledChannels, *t->natronMarker, trackIndex, TimeValue(prevFramesIt->frame), frameStep, formatHeight, &mvMarker); trackContext->AddMarker(mvMarker); // insert in the front of the list so that the order is reversed previouslyComputedMarkersOrdered.push_front(mvMarker); --prevFramesIt; } if (prevFramesIt == previousFramesOrdered.begin() && (int)previouslyComputedMarkersOrdered.size() != max_frames_to_predict_from) { mv::Marker mvMarker; TrackerHelperPrivate::natronTrackerToLibMVTracker(true, enabledChannels, *t->natronMarker, trackIndex, TimeValue(prevFramesIt->frame), frameStep, formatHeight, &mvMarker); trackContext->AddMarker(mvMarker); // insert in the front of the list so that the order is reversed previouslyComputedMarkersOrdered.push_front(mvMarker); } } // previouslyComputedMarkersOrdered is now ordererd by increasing order } // There must be at least 1 marker at the start time assert( !previouslyComputedMarkersOrdered.empty() ); // Initialise the kalman state with the marker at the position std::list<mv::Marker>::iterator mIt = previouslyComputedMarkersOrdered.begin(); t->mvState.Init(*mIt, frameStep); ++mIt; for (; mIt != previouslyComputedMarkersOrdered.end(); ++mIt) { mv::Marker predictedMarker; if ( !t->mvState.PredictForward(mIt->frame, &predictedMarker) ) { break; } else { t->mvState.Update(*mIt); } } t->mvOptions = mvOptions; trackAndOptions.push_back(t); } /* Launch tracking in the scheduler thread. */ boost::shared_ptr<TrackArgs> args( new TrackArgs(start, end, frameStep, trackerNode->getApp()->getTimeLine(), viewer, trackContext, accessor, trackAndOptions, formatWidth, formatHeight, autoKeyingOnEnabledParamEnabled) ); _imp->scheduler->track(args); } // TrackerHelper::trackMarkers
void TrackerNodePrivate::solveTransformParams() { setTransformOutOfDate(false); std::vector<TrackMarkerPtr> markers; knobsTable->getAllMarkers(&markers); if ( markers.empty() ) { return; } resetTransformParamsAnimation(); KnobChoicePtr motionTypeKnob = motionType.lock(); int motionType_i = motionTypeKnob->getValue(); TrackerMotionTypeEnum type = (TrackerMotionTypeEnum)motionType_i; TimeValue refTime(referenceFrame.lock()->getValue()); int jitterPer = 0; bool jitterAdd = false; switch (type) { case eTrackerMotionTypeNone: return; case eTrackerMotionTypeMatchMove: case eTrackerMotionTypeStabilize: break; case eTrackerMotionTypeAddJitter: case eTrackerMotionTypeRemoveJitter: { jitterPer = jitterPeriod.lock()->getValue(); jitterAdd = type == eTrackerMotionTypeAddJitter; break; } } setSolverParamsEnabled(false); std::set<TimeValue> keyframes; { for (std::size_t i = 0; i < markers.size(); ++i) { std::set<double> keys; markers[i]->getCenterKeyframes(&keys); for (std::set<double>::iterator it = keys.begin(); it != keys.end(); ++it) { keyframes.insert(TimeValue(*it)); } } } KnobChoicePtr transformTypeKnob = transformType.lock(); assert(transformTypeKnob); int transformType_i = transformTypeKnob->getValue(); TrackerTransformNodeEnum transformType = (TrackerTransformNodeEnum)transformType_i; NodePtr node = publicInterface->getNode(); invertTransform.lock()->setValue(type == eTrackerMotionTypeStabilize); KnobDoublePtr centerKnob = center.lock(); // Set the center at the reference frame Point centerValue = {0, 0}; int nSamplesAtRefTime = 0; for (std::size_t i = 0; i < markers.size(); ++i) { if ( !markers[i]->isEnabled(refTime) ) { continue; } KnobDoublePtr markerCenterKnob = markers[i]->getCenterKnob(); centerValue.x += markerCenterKnob->getValueAtTime(refTime); centerValue.y += markerCenterKnob->getValueAtTime(refTime, DimIdx(1)); ++nSamplesAtRefTime; } if (nSamplesAtRefTime) { centerValue.x /= nSamplesAtRefTime; centerValue.y /= nSamplesAtRefTime; { std::vector<double> values(2); values[0] = centerValue.x; values[1] = centerValue.y; centerKnob->setValueAcrossDimensions(values); } } bool robust; robust = robustModel.lock()->getValue(); KnobDoublePtr maxFittingErrorKnob = fittingErrorWarnIfAbove.lock(); const double maxFittingError = maxFittingErrorKnob->getValue(); node->getApp()->progressStart( node, tr("Solving for transform parameters...").toStdString(), std::string() ); lastSolveRequest.refTime = refTime; lastSolveRequest.jitterPeriod = jitterPer; lastSolveRequest.jitterAdd = jitterAdd; lastSolveRequest.allMarkers = markers; lastSolveRequest.keyframes = keyframes; lastSolveRequest.robustModel = robust; lastSolveRequest.maxFittingError = maxFittingError; switch (transformType) { case eTrackerTransformNodeTransform: computeTransformParamsFromTracks(); break; case eTrackerTransformNodeCornerPin: computeCornerParamsFromTracks(); break; } } // TrackerNodePrivate::solveTransformParams
bool CornerPinOverlayInteract::onOverlayPenMotion(TimeValue time, const RenderScale & /*renderScale*/, ViewIdx view, const QPointF & /*viewportPos*/, const QPointF & penPos, double /*pressure*/, TimeValue /*timestamp*/) { // do not show interact if knob is secret or not enabled // see https://github.com/MrKepzie/Natron/issues/932 KnobDoublePtr from1Knob = _imp->from[0].lock(); if ( !from1Knob || !from1Knob->shouldDrawOverlayInteract() ) { return false; } RenderScale pscale; getPixelScale(pscale.x, pscale.y); OfxPointD to[4]; OfxPointD from[4]; bool enable[4]; bool useFrom; if (_imp->dragging == -1) { for (int i = 0; i < 4; ++i) { _imp->getFrom(time, i, &from[i].x, &from[i].y); _imp->getTo(time, i, &to[i].x, &to[i].y); enable[i] = _imp->getEnabled(time, i); } useFrom = _imp->getUseFromPoints(time); } else { for (int i = 0; i < 4; ++i) { to[i] = _imp->toDrag[i]; from[i] = _imp->fromDrag[i]; enable[i] = _imp->enableDrag[i]; } useFrom = _imp->useFromDrag; } if (!useFrom && !_imp->areToPointsAnimated()) { return false; } OfxPointD p[4]; OfxPointD q[4]; int enableBegin = 4; int enableEnd = 0; for (int i = 0; i < 4; ++i) { if (enable[i]) { if (useFrom) { p[i] = from[i]; q[i] = to[i]; } else { q[i] = from[i]; p[i] = to[i]; } if (i < enableBegin) { enableBegin = i; } if (i + 1 > enableEnd) { enableEnd = i + 1; } } } bool didSomething = false; bool valuesChanged = false; OfxPointD delta; delta.x = penPos.x() - _imp->lastPenPos.x(); delta.y = penPos.y() - _imp->lastPenPos.y(); _imp->hovering = -1; for (int i = enableBegin; i < enableEnd; ++i) { if (enable[i]) { if (_imp->dragging == i) { if (useFrom) { from[i].x += delta.x; from[i].y += delta.y; _imp->fromDrag[i] = from[i]; } else { to[i].x += delta.x; to[i].y += delta.y; _imp->toDrag[i] = to[i]; } valuesChanged = true; } else if ( CornerPinOverlayInteractPrivate::isNearby(penPos, p[i].x, p[i].y, CornerPinOverlayInteractPrivate::pointTolerance(), pscale) ) { _imp->hovering = i; didSomething = true; } } } if ( (_imp->dragging != -1) && _imp->interactiveDrag && valuesChanged ) { // no need to redraw overlay since it is slave to the paramaters if (_imp->useFromDrag) { KnobDoublePtr knob = _imp->from[_imp->dragging].lock(); assert(knob); std::vector<double> val(2); val[0] = from[_imp->dragging].x; val[1] = from[_imp->dragging].y; knob->setValueAcrossDimensions(val, DimIdx(0), view, eValueChangedReasonUserEdited); } else { KnobDoublePtr knob = _imp->to[_imp->dragging].lock(); assert(knob); std::vector<double> val(2); val[0] = to[_imp->dragging].x; val[1] = to[_imp->dragging].y; if (_imp->toPointsAutoKeyingEnabled) { knob->setValueAtTimeAcrossDimensions(time, val, DimIdx(0), view, eValueChangedReasonUserEdited); // Also set a keyframe on other points for (int i = 0; i < 4; ++i) { if (i == _imp->dragging) { continue; } std::vector<double> values(2); KnobDoublePtr toPoint = _imp->to[i].lock(); values[0] = toPoint->getValueAtTime(time, DimIdx(0)); values[1] = toPoint->getValueAtTime(time, DimIdx(1)); toPoint->setValueAtTimeAcrossDimensions(time, values, DimIdx(0), view, eValueChangedReasonUserEdited); } } else { knob->setValueAcrossDimensions(val, DimIdx(0), view, eValueChangedReasonUserEdited); } } } _imp->lastPenPos = penPos; return didSomething || valuesChanged; } // onOverlayPenMotion
bool PointOverlayInteract::onOverlayPenMotion(TimeValue time, const RenderScale & /*renderScale*/, ViewIdx view, const QPointF & /*viewportPos*/, const QPointF & penPos, double /*pressure*/, TimeValue /*timestamp*/) { KnobDoublePtr knob = _imp->param.lock(); // do not show interact if knob is secret or not enabled // see https://github.com/MrKepzie/Natron/issues/932 if ( !knob || !knob->shouldDrawOverlayInteract() ) { return false; } RenderScale pscale; getPixelScale(pscale.x, pscale.y); QPointF pos; if (_imp->state == ePositionInteractStatePicked) { pos = _imp->lastPenPos; } else { double p[2]; for (int i = 0; i < 2; ++i) { p[i] = knob->getValueAtTime(time, DimIdx(i)); if (knob->getValueIsNormalized(DimIdx(i)) != eValueIsNormalizedNone) { p[i] = knob->denormalize(DimIdx(i), time, p[i]); } } pos.setX(p[0]); pos.setY(p[1]); } bool didSomething = false; bool valuesChanged = false; switch (_imp->state) { case ePositionInteractStateInactive: case ePositionInteractStatePoised: { // are we in the box, become 'poised' PositionInteractState newState; if ( ( std::fabs( penPos.x() - pos.x() ) <= _imp->pointTolerance() * pscale.x) && ( std::fabs( penPos.y() - pos.y() ) <= _imp->pointTolerance() * pscale.y) ) { newState = ePositionInteractStatePoised; } else { newState = ePositionInteractStateInactive; } if (_imp->state != newState) { // state changed, must redraw redraw(); } _imp->state = newState; //} break; } case ePositionInteractStatePicked: { valuesChanged = true; break; } } didSomething = (_imp->state == ePositionInteractStatePoised) || (_imp->state == ePositionInteractStatePicked); if ( (_imp->state != ePositionInteractStateInactive) && _imp->interactiveDrag && valuesChanged ) { std::vector<double> p(2); p[0] = fround(_imp->lastPenPos.x(), pscale.x); p[1] = fround(_imp->lastPenPos.y(), pscale.y); for (int i = 0; i < 2; ++i) { if (knob->getValueIsNormalized(DimIdx(i)) != eValueIsNormalizedNone) { p[i] = knob->normalize(DimIdx(i), time, p[i]); } } knob->setValueAcrossDimensions(p, DimIdx(0), ViewSetSpec(view), eValueChangedReasonUserEdited); } _imp->lastPenPos = penPos; return (didSomething || valuesChanged); } // onOverlayPenMotion