void TrackArgs::getRedrawAreasNeeded(TimeValue time, std::list<RectD>* canonicalRects) const { for (std::vector<TrackMarkerAndOptionsPtr >::const_iterator it = _imp->tracks.begin(); it != _imp->tracks.end(); ++it) { if ( !(*it)->natronMarker->isEnabled(time) ) { continue; } KnobDoublePtr searchBtmLeft = (*it)->natronMarker->getSearchWindowBottomLeftKnob(); KnobDoublePtr searchTopRight = (*it)->natronMarker->getSearchWindowTopRightKnob(); KnobDoublePtr centerKnob = (*it)->natronMarker->getCenterKnob(); KnobDoublePtr offsetKnob = (*it)->natronMarker->getOffsetKnob(); Point offset, center, btmLeft, topRight; offset.x = offsetKnob->getValueAtTime(time, DimIdx(0)); offset.y = offsetKnob->getValueAtTime(time, DimIdx(1)); center.x = centerKnob->getValueAtTime(time, DimIdx(0)); center.y = centerKnob->getValueAtTime(time, DimIdx(1)); btmLeft.x = searchBtmLeft->getValueAtTime(time, DimIdx(0)) + center.x + offset.x; btmLeft.y = searchBtmLeft->getValueAtTime(time, DimIdx(1)) + center.y + offset.y; topRight.x = searchTopRight->getValueAtTime(time, DimIdx(0)) + center.x + offset.x; topRight.y = searchTopRight->getValueAtTime(time, DimIdx(1)) + center.y + offset.y; RectD rect; rect.x1 = btmLeft.x; rect.y1 = btmLeft.y; rect.x2 = topRight.x; rect.y2 = topRight.y; canonicalRects->push_back(rect); } }
RectI TrackMarker::getMarkerImageRoI(int time) const { const unsigned int mipmapLevel = 0; Point center, offset; KnobDoublePtr centerKnob = getCenterKnob(); KnobDoublePtr offsetKnob = getOffsetKnob(); center.x = centerKnob->getValueAtTime(time, 0); center.y = centerKnob->getValueAtTime(time, 1); offset.x = offsetKnob->getValueAtTime(time, 0); offset.y = offsetKnob->getValueAtTime(time, 1); RectD roiCanonical; KnobDoublePtr swBl = getSearchWindowBottomLeftKnob(); KnobDoublePtr swTr = getSearchWindowTopRightKnob(); roiCanonical.x1 = swBl->getValueAtTime(time, 0) + center.x + offset.x; roiCanonical.y1 = swBl->getValueAtTime(time, 1) + center.y + offset.y; roiCanonical.x2 = swTr->getValueAtTime(time, 0) + center.x + offset.x; roiCanonical.y2 = swTr->getValueAtTime(time, 1) + center.y + offset.y; RectI roi; NodePtr node = getContext()->getNode(); NodePtr input = node->getInput(0); if (!input) { return RectI(); } roiCanonical.toPixelEnclosing(mipmapLevel, input ? input->getEffectInstance()->getAspectRatio(-1) : 1., &roi); return roi; }
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 TrackMarker::setKeyFrameOnCenterAndPatternAtTime(int time) { KnobDoublePtr center = _imp->center.lock(); for (int i = 0; i < center->getDimension(); ++i) { double v = center->getValueAtTime(time, i); center->setValueAtTime(time, v, ViewSpec::all(), i); } 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]; for (int i = 0; i < k->getDimension(); ++i) { double v = k->getValueAtTime(time, i); k->setValueAtTime(time, v, ViewSpec::all(), i); } } }
void getTo(TimeValue time, int index, double* tx, double* ty) const { KnobDoublePtr knob = to[index].lock(); assert(knob); *tx = knob->getValueAtTime(time, DimIdx(0)); *ty = knob->getValueAtTime(time, DimIdx(1)); }
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); } }
RectD TrackMarker::getMarkerImageRoI(TimeValue time) const { Point center, offset; KnobDoublePtr centerKnob = getCenterKnob(); KnobDoublePtr offsetKnob = getOffsetKnob(); center.x = centerKnob->getValueAtTime(time, DimIdx(0)); center.y = centerKnob->getValueAtTime(time, DimIdx(1)); offset.x = offsetKnob->getValueAtTime(time, DimIdx(0)); offset.y = offsetKnob->getValueAtTime(time, DimIdx(1)); RectD roiCanonical; KnobDoublePtr swBl = getSearchWindowBottomLeftKnob(); KnobDoublePtr swTr = getSearchWindowTopRightKnob(); roiCanonical.x1 = swBl->getValueAtTime(time, DimIdx(0)) + center.x + offset.x; roiCanonical.y1 = swBl->getValueAtTime(time, DimIdx(1)) + center.y + offset.y; roiCanonical.x2 = swTr->getValueAtTime(time, DimIdx(0)) + center.x + offset.x; roiCanonical.y2 = swTr->getValueAtTime(time, DimIdx(1)) + center.y + offset.y; return roiCanonical; }
TEST_F(BaseTest, SetValues) { NodePtr generator = createNode(_generatorPluginID); assert(generator); KnobIPtr knob = generator->getKnobByName("noiseZSlope"); KnobDoublePtr radius = boost::dynamic_pointer_cast<KnobDouble>(knob); EXPECT_TRUE(radius != 0); if (!radius) { return; } radius->setValue(0.5); EXPECT_TRUE(radius->getValue() == 0.5); //Check that linear interpolation is working as intended KeyFrame kf; radius->setInterpolationAtTime(eCurveChangeReasonInternal, ViewSpec::all(), 0, 0, eKeyframeTypeLinear, &kf); radius->setValueAtTime(0, 0., ViewSpec::all(), 0); radius->setValueAtTime(100, 1., ViewSpec::all(), 0); for (int i = 0; i <= 100; ++i) { double v = radius->getValueAtTime(i); EXPECT_TRUE(std::abs(v - i / 100.) < 1e-6); } }
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
bool TrackMarkerPM::trackMarker(bool forward, int refFrame, int frame) { KnobButtonPtr button; if (forward) { button = trackNextButton.lock(); } else { button = trackPrevButton.lock(); } KnobIntPtr refFrameK = refFrameKnob.lock(); refFrameK->setValue(refFrame); // Unslave the center knob since the trackerNode will update it, then update the marker center KnobDoublePtr center = centerKnob.lock(); center->unlink(DimSpec::all(), ViewSetSpec::all(), true); trackerNode->getEffectInstance()->onKnobValueChanged_public(button, eValueChangedReasonUserEdited, TimeValue(frame), ViewIdx(0)); KnobDoublePtr markerCenter = getCenterKnob(); // The TrackerPM plug-in has set a keyframe at the refFrame and frame, copy them bool ret = true; double centerPoint[2]; for (int i = 0; i < center->getNDimensions(); ++i) { { int index = center->getKeyFrameIndex(ViewIdx(0), DimIdx(i), TimeValue(frame)); if (index != -1) { centerPoint[i] = center->getValueAtTime(TimeValue(frame), DimIdx(i)); markerCenter->setValueAtTime(TimeValue(frame), centerPoint[i], ViewSetSpec::all(), DimIdx(i)); } else { // No keyframe at this time: tracking failed ret = false; break; } } { int index = center->getKeyFrameIndex(ViewIdx(0), DimIdx(i), TimeValue(refFrame)); if (index != -1) { double value = center->getValueAtTime(TimeValue(refFrame), DimIdx(i)); markerCenter->setValueAtTime(TimeValue(refFrame), value, ViewSetSpec::all(), DimIdx(i)); } } } // Convert the correlation score of the TrackerPM to the error if (ret) { KnobDoublePtr markerError = getErrorKnob(); KnobDoublePtr correlation = correlationScoreKnob.lock(); { int index = correlation->getKeyFrameIndex(ViewIdx(0), DimIdx(0), TimeValue(frame)); if (index != -1) { // The error is estimated as a percentage of the correlation across the number of pixels in the pattern window KnobDoublePtr pBtmLeft = patternBtmLeftKnob.lock(); KnobDoublePtr pTopRight = patternTopRightKnob.lock(); Point btmLeft, topRight; btmLeft.x = pBtmLeft->getValueAtTime(TimeValue(frame), DimIdx(0)); btmLeft.y = pBtmLeft->getValueAtTime(TimeValue(frame), DimIdx(1)); topRight.x = pTopRight->getValueAtTime(TimeValue(frame), DimIdx(0)); topRight.y = pTopRight->getValueAtTime(TimeValue(frame), DimIdx(1)); double areaPixels = (topRight.x - btmLeft.x) * (topRight.y - btmLeft.y); NodePtr trackerInput = trackerNode->getInput(0); if (trackerInput) { ImagePlaneDesc comps, paireComps; trackerInput->getEffectInstance()->getMetadataComponents(-1, &comps, &paireComps); areaPixels *= comps.getNumComponents(); } double value = correlation->getValueAtTime(TimeValue(frame), DimIdx(0)); // Convert to a percentage if (areaPixels > 0) { value /= areaPixels; } markerError->setValueAtTime(TimeValue(frame), value, ViewSetSpec::all(), DimIdx(0)); } } } if ( !center->linkTo(markerCenter) ) { throw std::runtime_error("Could not link center"); } return ret; } // TrackMarkerPM::trackMarker
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 RotoShapeRenderNodePrivate::renderStroke_generic(RenderStrokeDataPtr userData, PFNRenderStrokeBeginRender beginCallback, PFNRenderStrokeRenderDot renderDotCallback, PFNRenderStrokeEndRender endCallback, const std::list<std::list<std::pair<Point, double> > >& strokes, const double distToNextIn, const Point& lastCenterPointIn, const RotoDrawableItemPtr& stroke, bool doBuildup, double opacity, TimeValue time, ViewIdx /*view*/, const RenderScale& scale, double* distToNextOut, Point* lastCenterPoint) { assert(distToNextOut && lastCenterPoint); *distToNextOut = 0; double brushSize, brushSizePixelX, brushSizePixelY, brushSpacing, brushHardness, writeOnStart, writeOnEnd; bool pressureAffectsOpacity, pressureAffectsHardness, pressureAffectsSize; { KnobDoublePtr brushSizeKnob = stroke->getBrushSizeKnob(); brushSize = brushSizeKnob->getValueAtTime(time); KnobDoublePtr brushSpacingKnob = stroke->getBrushSpacingKnob(); brushSpacing = brushSpacingKnob->getValueAtTime(time); if (brushSpacing == 0.) { return false; } brushSpacing = std::max(brushSpacing, 0.05); KnobDoublePtr brushHardnessKnob = stroke->getBrushHardnessKnob(); brushHardness = brushHardnessKnob->getValueAtTime(time); KnobDoublePtr visiblePortionKnob = stroke->getBrushVisiblePortionKnob(); writeOnStart = visiblePortionKnob->getValueAtTime(time); writeOnEnd = visiblePortionKnob->getValueAtTime(time, DimIdx(1)); if ( (writeOnEnd - writeOnStart) <= 0. ) { return false; } // This function is also used for opened bezier which do not have pressure. RotoStrokeItemPtr isStroke = toRotoStrokeItem(stroke); if (!isStroke) { pressureAffectsOpacity = false; pressureAffectsSize = false; pressureAffectsHardness = false; } else { KnobBoolPtr pressureOpacityKnob = isStroke->getPressureOpacityKnob(); KnobBoolPtr pressureSizeKnob = isStroke->getPressureSizeKnob(); KnobBoolPtr pressureHardnessKnob = isStroke->getPressureHardnessKnob(); pressureAffectsOpacity = pressureOpacityKnob->getValueAtTime(time); pressureAffectsSize = pressureSizeKnob->getValueAtTime(time); pressureAffectsHardness = pressureHardnessKnob->getValueAtTime(time); } brushSizePixelX = brushSize; brushSizePixelY = brushSizePixelX; brushSizePixelX = std::max( 1., brushSizePixelX * scale.x); brushSizePixelY = std::max( 1., brushSizePixelY * scale.y); } double distToNext = distToNextIn; bool hasRenderedDot = false; beginCallback(userData, brushSizePixelX, brushSizePixelY, brushSpacing, brushHardness, pressureAffectsOpacity, pressureAffectsHardness, pressureAffectsSize, doBuildup, opacity); *lastCenterPoint = lastCenterPointIn; Point prevCenter = lastCenterPointIn; for (std::list<std::list<std::pair<Point, double> > >::const_iterator strokeIt = strokes.begin(); strokeIt != strokes.end(); ++strokeIt) { int firstPoint = (int)std::floor( (strokeIt->size() * writeOnStart) ); int endPoint = (int)std::ceil( (strokeIt->size() * writeOnEnd) ); assert( firstPoint >= 0 && firstPoint < (int)strokeIt->size() && endPoint > firstPoint && endPoint <= (int)strokeIt->size() ); ///The visible portion of the paint's stroke with points adjusted to pixel coordinates std::list<std::pair<Point, double> > visiblePortion; std::list<std::pair<Point, double> >::const_iterator startingIt = strokeIt->begin(); std::list<std::pair<Point, double> >::const_iterator endingIt = strokeIt->begin(); std::advance(startingIt, firstPoint); std::advance(endingIt, endPoint); for (std::list<std::pair<Point, double> >::const_iterator it = startingIt; it != endingIt; ++it) { visiblePortion.push_back(*it); } if ( visiblePortion.empty() ) { continue; } std::list<std::pair<Point, double> >::iterator it = visiblePortion.begin(); if (visiblePortion.size() == 1) { double spacing; *lastCenterPoint = it->first; renderDotCallback(userData, prevCenter, *lastCenterPoint, it->second, &spacing); distToNext += spacing; continue; } //prevCenter = it->first; std::list<std::pair<Point, double> >::iterator next = it; ++next; while ( next != visiblePortion.end() ) { //Render for each point a dot. Spacing is a percentage of brushSize: //Spacing at 1 means no dot is overlapping another (so the spacing is in fact brushSize) //Spacing at 0 we do not render the stroke double dx = next->first.x - it->first.x; double dy = next->first.y - it->first.y; // This is the distance between the current and next discretized points // Since the distance between each points may vary, we uniformly position a dot along the segments double dist = std::sqrt(dx * dx + dy * dy); // while the next point can be drawn on this segment, draw a point and advance while (distToNext <= dist) { double a = dist == 0. ? 0. : distToNext / dist; lastCenterPoint->x = it->first.x * (1 - a) + next->first.x * a; lastCenterPoint->y = it->first.y * (1 - a) + next->first.y * a; double pressure = it->second * (1 - a) + next->second * a; // draw the dot double spacing; bool rendered = renderDotCallback(userData, prevCenter, *lastCenterPoint, pressure, &spacing); hasRenderedDot |= rendered; prevCenter = *lastCenterPoint; distToNext += spacing; /*if (rendered) { } else { break; }*/ } // go to the next segment distToNext -= dist; ++next; ++it; } } endCallback(userData); *distToNextOut = distToNext; return hasRenderedDot; }
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
void PointOverlayInteract::drawOverlay(TimeValue time, const RenderScale & /*renderScale*/, ViewIdx /*view*/) { 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; } ColorRgba<double> color; if ( !getOverlayColor(color.r, color.g, color.b) ) { color.r = color.g = color.b = 0.8; } RenderScale pscale; getPixelScale(pscale.x, pscale.y); float pR = 1.f; float pG = 1.f; float pB = 1.f; switch (_imp->state) { case ePositionInteractStateInactive: pR = (float)color.r; pG = (float)color.g; pB = (float)color.b; break; case ePositionInteractStatePoised: pR = 0.f; pG = 1.0f; pB = 0.0f; break; case ePositionInteractStatePicked: pR = 0.f; pG = 1.0f; pB = 0.0f; break; } 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]); } //glPushAttrib(GL_ALL_ATTRIB_BITS); // caller is responsible for protecting attribs GL_GPU::PointSize( (GLfloat)_imp->pointSize() ); // Draw everything twice // l = 0: shadow // l = 1: drawing double w, h; getViewportSize(w, h); GLdouble projection[16]; GL_GPU::GetDoublev( GL_PROJECTION_MATRIX, projection); OfxPointD shadow; // how much to translate GL_PROJECTION to get exactly one pixel on screen shadow.x = 2. / (projection[0] * w); shadow.y = 2. / (projection[5] * h); int fmHeight = getLastCallingViewport()->getWidgetFontHeight(); for (int l = 0; l < 2; ++l) { // shadow (uses GL_PROJECTION) GL_GPU::MatrixMode(GL_PROJECTION); int direction = (l == 0) ? 1 : -1; // translate (1,-1) pixels GL_GPU::Translated(direction * shadow.x, -direction * shadow.y, 0); GL_GPU::MatrixMode(GL_MODELVIEW); // Modelview should be used on Nuke GL_GPU::Color3f(pR * l, pG * l, pB * l); GL_GPU::Begin(GL_POINTS); GL_GPU::Vertex2d( pos.x(), pos.y() ); GL_GPU::End(); getLastCallingViewport()->renderText(pos.x(), pos.y() - ( fmHeight + _imp->pointSize() ) * pscale.y, knob->getOriginalName(), pR*l, pG*l, pB*l, 1.); } } // drawOverlay
bool TrackMarkerPM::trackMarker(bool forward, int refFrame, int frame) { KnobButtonPtr button; if (forward) { button = trackNextButton.lock(); } else { button = trackPrevButton.lock(); } KnobIntPtr refFrameK = refFrameKnob.lock(); refFrameK->setValue(refFrame); // Unslave the center knob since the trackerNode will update it, then update the marker center KnobDoublePtr center = centerKnob.lock(); for (int i = 0; i < center->getDimension(); ++i) { center->unSlave(i, true); } trackerNode->getEffectInstance()->onKnobValueChanged_public(button, eValueChangedReasonNatronInternalEdited, frame, ViewIdx(0), true); KnobDoublePtr markerCenter = getCenterKnob(); // The TrackerPM plug-in has set a keyframe at the refFrame and frame, copy them bool ret = true; double centerPoint[2]; for (int i = 0; i < center->getDimension(); ++i) { { int index = center->getKeyFrameIndex(ViewSpec::current(), i, frame); if (index != -1) { centerPoint[i] = center->getValueAtTime(frame, i); markerCenter->setValueAtTime(frame, centerPoint[i], ViewSpec::current(), i); } else { // No keyframe at this time: tracking failed ret = false; break; } } { int index = center->getKeyFrameIndex(ViewSpec::current(), i, refFrame); if (index != -1) { double value = center->getValueAtTime(refFrame, i); markerCenter->setValueAtTime(refFrame, value, ViewSpec::current(), i); } } } // Convert the correlation score of the TrackerPM to the error if (ret) { KnobDoublePtr markerError = getErrorKnob(); KnobDoublePtr correlation = correlationScoreKnob.lock(); { int index = correlation->getKeyFrameIndex(ViewSpec::current(), 0, frame); if (index != -1) { // The error is estimated as a percentage of the correlation across the number of pixels in the pattern window KnobDoublePtr pBtmLeft = patternBtmLeftKnob.lock(); KnobDoublePtr pTopRight = patternTopRightKnob.lock(); Point btmLeft, topRight; btmLeft.x = pBtmLeft->getValueAtTime(frame, 0); btmLeft.y = pBtmLeft->getValueAtTime(frame, 1); topRight.x = pTopRight->getValueAtTime(frame, 0); topRight.y = pTopRight->getValueAtTime(frame, 1); double areaPixels = (topRight.x - btmLeft.x) * (topRight.y - btmLeft.y); NodePtr trackerInput = trackerNode->getInput(0); if (trackerInput) { ImageComponents comps = trackerInput->getEffectInstance()->getComponents(-1); areaPixels *= comps.getNumComponents(); } double value = correlation->getValueAtTime(frame, 0); // Convert to a percentage value /= areaPixels; markerError->setValueAtTime(frame, value, ViewSpec::current(), 0); } } } for (int i = 0; i < center->getDimension(); ++i) { center->slaveTo(i, markerCenter, i); } return ret; } // TrackMarkerPM::trackMarker