///High level test: render 1 frame of dot generator TEST_F(BaseTest, GenerateDot) { ///create the generator NodePtr generator = createNode(_generatorPluginID); ///create the writer and set its output filename NodePtr writer = createNode(_writeOIIOPluginID); ASSERT_TRUE(generator && writer); KnobIPtr frameRange = generator->getApp()->getProject()->getKnobByName("frameRange"); ASSERT_TRUE(frameRange); KnobIntPtr knob = toKnobInt(frameRange); ASSERT_TRUE(knob); knob->setValue(1, ViewSpec::all(), 0); knob->setValue(1, ViewSpec::all(), 1); Format f(0, 0, 200, 200, "toto", 1.); generator->getApp()->getProject()->setOrAddProjectFormat(f); const QString& binPath = appPTR->getApplicationBinaryPath(); QString filePath = binPath + QString::fromUtf8("/test_dot_generator.jpg"); writer->setOutputFilesForWriter( filePath.toStdString() ); ///attempt to connect the 2 nodes together connectNodes(generator, writer, 0, true); ///and start rendering. This call is blocking. std::list<AppInstance::RenderWork> works; AppInstance::RenderWork w; w.writer = toOutputEffectInstance( writer->getEffectInstance() ); assert(w.writer); w.firstFrame = INT_MIN; w.lastFrame = INT_MAX; w.frameStep = INT_MIN; w.useRenderStats = false; works.push_back(w); getApp()->startWritersRendering(false, works); EXPECT_TRUE( QFile::exists(filePath) ); QFile::remove(filePath); }
void OfxOverlayInteract::onViewportSelectionUpdated(const RectD& rectangle, bool onRelease) { OfxEffectInstancePtr effect = toOfxEffectInstance(getEffect()); if (!effect) { return; } KnobIPtr foundSelKnob = effect->getKnobByName(kNatronOfxImageEffectSelectionRectangle); if (!foundSelKnob) { return; } KnobIntPtr isIntKnob = toKnobInt(foundSelKnob); if (!isIntKnob) { return; } double propV[4] = {rectangle.x1, rectangle.y1, rectangle.x2, rectangle.y2}; effect->effectInstance()->getProps().setDoublePropertyN(kNatronOfxImageEffectSelectionRectangle, propV, 4); isIntKnob->setValue(onRelease ? 2 : 1); }
void OfxOverlayInteract::onViewportSelectionCleared() { OfxEffectInstancePtr effect = toOfxEffectInstance(getEffect()); if (!effect) { return; } KnobIPtr foundSelKnob = effect->getKnobByName(kNatronOfxImageEffectSelectionRectangle); if (!foundSelKnob) { return; } KnobIntPtr isIntKnob = toKnobInt(foundSelKnob); if (!isIntKnob) { return; } double propV[4] = {0, 0, 0, 0}; effect->effectInstance()->getProps().setDoublePropertyN(kNatronOfxImageEffectSelectionRectangle, propV, 4); isIntKnob->setValue(0); }
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::computeTransformParamsFromTracksEnd(double refTime, double maxFittingError, const QList<TransformData>& results) { QList<TransformData> validResults; for (QList<TransformData>::const_iterator it = results.begin(); it != results.end(); ++it) { if (it->valid) { validResults.push_back(*it); } } KnobIntPtr smoothKnob = smoothTransform.lock(); int smoothTJitter, smoothRJitter, smoothSJitter; smoothTJitter = smoothKnob->getValue(DimIdx(0)); smoothRJitter = smoothKnob->getValue(DimIdx(1)); smoothSJitter = smoothKnob->getValue(DimIdx(2)); KnobDoublePtr translationKnob = translate.lock(); KnobDoublePtr scaleKnob = scale.lock(); KnobDoublePtr rotationKnob = rotate.lock(); KnobDoublePtr fittingErrorKnob = fittingError.lock(); KnobStringPtr fittingWarningKnob = fittingErrorWarning.lock(); int halfTJitter = smoothTJitter / 2; int halfRJitter = smoothRJitter / 2; int halfSJitter = smoothSJitter / 2; translationKnob->blockValueChanges(); scaleKnob->blockValueChanges(); rotationKnob->blockValueChanges(); fittingErrorKnob->blockValueChanges(); std::list<KnobIPtr> animatedKnobsChanged; animatedKnobsChanged.push_back(translationKnob); animatedKnobsChanged.push_back(scaleKnob); animatedKnobsChanged.push_back(rotationKnob); animatedKnobsChanged.push_back(fittingErrorKnob); Curve tmpTXCurve, tmpTYCurve, tmpRotateCurve, tmpScaleCurve, tmpFittingErrorCurve; bool mustShowFittingWarn = false; for (QList<TransformData>::const_iterator itResults = validResults.begin(); itResults != validResults.end(); ++itResults) { const TransformData& dataAtTime = *itResults; { KeyFrame kf(dataAtTime.time, dataAtTime.rms); if (dataAtTime.rms >= maxFittingError) { mustShowFittingWarn = true; } tmpFittingErrorCurve.setOrAddKeyframe(kf); } if (!dataAtTime.hasRotationAndScale) { // no rotation or scale: simply extract the translation Point translation; if (smoothTJitter > 1) { TranslateData avgT; averageDataFunctor<QList<TransformData>::const_iterator, void, TranslateData>(validResults.begin(), validResults.end(), itResults, halfTJitter, 0, &avgT, 0); translation.x = avgT.p.x; translation.y = avgT.p.y; } else { translation.x = dataAtTime.translation.x; translation.y = dataAtTime.translation.y; } { KeyFrame kx(dataAtTime.time, translation.x); KeyFrame ky(dataAtTime.time, translation.y); tmpTXCurve.setOrAddKeyframe(kx); tmpTYCurve.setOrAddKeyframe(ky); } } else { double rot = 0; if (smoothRJitter > 1) { RotateData avgR; averageDataFunctor<QList<TransformData>::const_iterator, void, RotateData>(validResults.begin(), validResults.end(), itResults, halfRJitter, 0, &avgR, 0); rot = avgR.r; } else { rot = dataAtTime.rotation; } { KeyFrame k(dataAtTime.time, rot); tmpRotateCurve.setOrAddKeyframe(k); } double scale; if (smoothSJitter > 1) { ScaleData avgR; averageDataFunctor<QList<TransformData>::const_iterator, void, ScaleData>(validResults.begin(), validResults.end(), itResults, halfSJitter, 0, &avgR, 0); scale = avgR.s; } else { scale = dataAtTime.scale; } { KeyFrame k(dataAtTime.time, scale); tmpScaleCurve.setOrAddKeyframe(k); } Point translation; if (smoothTJitter > 1) { TranslateData avgT; averageDataFunctor<QList<TransformData>::const_iterator, void, TranslateData>(validResults.begin(), validResults.end(), itResults, halfTJitter, 0, &avgT, 0); translation.x = avgT.p.x; translation.y = avgT.p.y; } else { translation.x = dataAtTime.translation.x; translation.y = dataAtTime.translation.y; } { KeyFrame kx(dataAtTime.time, translation.x); KeyFrame ky(dataAtTime.time, translation.y); tmpTXCurve.setOrAddKeyframe(kx); tmpTYCurve.setOrAddKeyframe(ky); } } } // for all samples fittingWarningKnob->setSecret(!mustShowFittingWarn); fittingErrorKnob->cloneCurve(ViewIdx(0), DimIdx(0), tmpFittingErrorCurve, 0 /*offset*/, 0 /*range*/); translationKnob->cloneCurve(ViewIdx(0), DimIdx(0), tmpTXCurve, 0 /*offset*/, 0 /*range*/); translationKnob->cloneCurve(ViewIdx(0), DimIdx(1), tmpTYCurve, 0 /*offset*/, 0 /*range*/); rotationKnob->cloneCurve(ViewIdx(0), DimIdx(0), tmpRotateCurve, 0 /*offset*/, 0 /*range*/); scaleKnob->cloneCurve(ViewIdx(0), DimIdx(0), tmpScaleCurve, 0 /*offset*/, 0 /*range*/); scaleKnob->cloneCurve(ViewIdx(0), DimIdx(1), tmpScaleCurve, 0 /*offset*/, 0 /*range*/); for (std::list<KnobIPtr>::iterator it = animatedKnobsChanged.begin(); it != animatedKnobsChanged.end(); ++it) { (*it)->unblockValueChanges(); (*it)->evaluateValueChange(DimSpec::all(), TimeValue(refTime), ViewSetSpec::all(), eValueChangedReasonUserEdited); } endSolve(); } // TrackerNodePrivate::computeTransformParamsFromTracksEnd
void TrackerNodePrivate::computeCornerParamsFromTracksEnd(double refTime, double maxFittingError, const QList<CornerPinData>& results) { // Make sure we get only valid results QList<CornerPinData> validResults; for (QList<CornerPinData>::const_iterator it = results.begin(); it != results.end(); ++it) { if (it->valid) { validResults.push_back(*it); } } // Get all knobs that we are going to write to and block any value changes on them KnobIntPtr smoothCornerPinKnob = smoothCornerPin.lock(); int smoothJitter = smoothCornerPinKnob->getValue(); int halfJitter = smoothJitter / 2; KnobDoublePtr fittingErrorKnob = fittingError.lock(); KnobDoublePtr fromPointsKnob[4]; KnobDoublePtr toPointsKnob[4]; KnobBoolPtr enabledPointsKnob[4]; KnobStringPtr fittingWarningKnob = fittingErrorWarning.lock(); for (int i = 0; i < 4; ++i) { fromPointsKnob[i] = fromPoints[i].lock(); toPointsKnob[i] = toPoints[i].lock(); enabledPointsKnob[i] = enableToPoint[i].lock(); } std::list<KnobIPtr> animatedKnobsChanged; fittingErrorKnob->blockValueChanges(); animatedKnobsChanged.push_back(fittingErrorKnob); for (int i = 0; i < 4; ++i) { toPointsKnob[i]->blockValueChanges(); animatedKnobsChanged.push_back(toPointsKnob[i]); } // Get reference corner pin CornerPinPoints refFrom; for (int c = 0; c < 4; ++c) { refFrom.pts[c].x = fromPointsKnob[c]->getValueAtTime(TimeValue(refTime)); refFrom.pts[c].y = fromPointsKnob[c]->getValueAtTime(TimeValue(refTime), DimIdx(1)); } // Create temporary curves and clone the toPoint internal curves at once because setValueAtTime will be slow since it emits // signals to create keyframes in keyframeSet Curve tmpToPointsCurveX[4], tmpToPointsCurveY[4]; Curve tmpFittingErrorCurve; bool mustShowFittingWarn = false; for (QList<CornerPinData>::const_iterator itResults = validResults.begin(); itResults != validResults.end(); ++itResults) { const CornerPinData& dataAtTime = *itResults; // Add the error to the curve and check if we need to turn on the RMS warning { KeyFrame kf(dataAtTime.time, dataAtTime.rms); if (dataAtTime.rms >= maxFittingError) { mustShowFittingWarn = true; } tmpFittingErrorCurve.setOrAddKeyframe(kf); } if (smoothJitter <= 1) { for (int c = 0; c < 4; ++c) { Point toPoint; toPoint = TrackerHelper::applyHomography(refFrom.pts[c], dataAtTime.h); KeyFrame kx(dataAtTime.time, toPoint.x); KeyFrame ky(dataAtTime.time, toPoint.y); tmpToPointsCurveX[c].setOrAddKeyframe(kx); tmpToPointsCurveY[c].setOrAddKeyframe(ky); //toPoints[c]->setValuesAtTime(dataAtTime[i].time, toPoint.x, toPoint.y, ViewSpec::all(), eValueChangedReasonUserEdited); } } else { // Average to points before and after if using jitter CornerPinPoints avgTos; averageDataFunctor<QList<CornerPinData>::const_iterator, CornerPinPoints, CornerPinPoints>(validResults.begin(), validResults.end(), itResults, halfJitter, &refFrom, &avgTos, 0); for (int c = 0; c < 4; ++c) { KeyFrame kx(dataAtTime.time, avgTos.pts[c].x); KeyFrame ky(dataAtTime.time, avgTos.pts[c].y); tmpToPointsCurveX[c].setOrAddKeyframe(kx); tmpToPointsCurveY[c].setOrAddKeyframe(ky); } } // use jitter } // for each result // If user wants a post-smooth, apply it if (smoothJitter > 1) { int halfSmoothJitter = smoothJitter / 2; KeyFrameSet xSet[4], ySet[4]; KeyFrameSet newXSet[4], newYSet[4]; for (int c = 0; c < 4; ++c) { xSet[c] = tmpToPointsCurveX[c].getKeyFrames_mt_safe(); ySet[c] = tmpToPointsCurveY[c].getKeyFrames_mt_safe(); } for (int c = 0; c < 4; ++c) { for (KeyFrameSet::const_iterator it = xSet[c].begin(); it != xSet[c].end(); ++it) { double avg; averageDataFunctor<KeyFrameSet::const_iterator, void, double>(xSet[c].begin(), xSet[c].end(), it, halfSmoothJitter, 0, &avg, 0); KeyFrame k(*it); k.setValue(avg); newXSet[c].insert(k); } for (KeyFrameSet::const_iterator it = ySet[c].begin(); it != ySet[c].end(); ++it) { double avg; averageDataFunctor<KeyFrameSet::const_iterator, void, double>(ySet[c].begin(), ySet[c].end(), it, halfSmoothJitter, 0, &avg, 0); KeyFrame k(*it); k.setValue(avg); newYSet[c].insert(k); } } for (int c = 0; c < 4; ++c) { tmpToPointsCurveX[c].setKeyframes(newXSet[c], true); tmpToPointsCurveY[c].setKeyframes(newYSet[c], true); } } fittingWarningKnob->setSecret(!mustShowFittingWarn); fittingErrorKnob->cloneCurve(ViewIdx(0), DimIdx(0), tmpFittingErrorCurve, 0 /*offset*/, 0 /*range*/); for (int c = 0; c < 4; ++c) { toPointsKnob[c]->cloneCurve(ViewIdx(0), DimIdx(0), tmpToPointsCurveX[c], 0 /*offset*/, 0 /*range*/); toPointsKnob[c]->cloneCurve(ViewIdx(0), DimIdx(1), tmpToPointsCurveY[c], 0 /*offset*/, 0 /*range*/); } for (std::list<KnobIPtr>::iterator it = animatedKnobsChanged.begin(); it != animatedKnobsChanged.end(); ++it) { (*it)->unblockValueChanges(); (*it)->evaluateValueChange(DimSpec::all(), TimeValue(refTime), ViewSetSpec::all(), eValueChangedReasonUserEdited); } endSolve(); } // TrackerNodePrivate::computeCornerParamsFromTracksEnd
NATRON_NAMESPACE_ENTER // those parameters should be ignored (they are always secret in Natron) #define kOCIOParamInputSpace "ocioInputSpace" #define kOCIOParamOutputSpace "ocioOutputSpace" // those parameters should not have their options in the help file if generating markdown, // because the options are dinamlically constructed at run-time from the OCIO config. #define kOCIOParamInputSpaceChoice "ocioInputSpaceIndex" #define kOCIOParamOutputSpaceChoice "ocioOutputSpaceIndex" #define kOCIODisplayPluginIdentifier "fr.inria.openfx.OCIODisplay" #define kOCIODisplayParamDisplay "display" #define kOCIODisplayParamDisplayChoice "displayIndex" #define kOCIODisplayParamView "view" #define kOCIODisplayParamViewChoice "viewIndex" // not yet implemented (see OCIOCDLTransform.cpp) //#define kOCIOCDLTransformPluginIdentifier "fr.inria.openfx.OCIOCDLTransform" //#define kOCIOCDLTransformParamCCCID "cccId" //#define kOCIOCDLTransformParamCCCIDChoice "cccIdIndex" #define kOCIOFileTransformPluginIdentifier "fr.inria.openfx.OCIOFileTransform" #define kOCIOFileTransformParamCCCID "cccId" #define kOCIOFileTransformParamCCCIDChoice "cccIdIndex" // genHTML: true for live HTML output for the internal web-server, false for markdown output QString Node::makeDocumentation(bool genHTML) const { QString ret; QString markdown; QTextStream ts(&ret); QTextStream ms(&markdown); QString pluginID, pluginLabel, pluginDescription, pluginIcon; int majorVersion = getMajorVersion(); int minorVersion = getMinorVersion(); std::vector<std::string> pluginGroup; bool pluginDescriptionIsMarkdown = false; QVector<QStringList> inputs; QVector<QStringList> items; { PluginPtr plugin = getPlugin(); assert(plugin); pluginID = QString::fromUtf8(plugin->getPluginID().c_str()); pluginLabel = QString::fromUtf8( Plugin::makeLabelWithoutSuffix( plugin->getPluginLabel() ).c_str()); pluginDescription = QString::fromUtf8( plugin->getPropertyUnsafe<std::string>(kNatronPluginPropDescription).c_str() ); pluginIcon = QString::fromUtf8(plugin->getPropertyUnsafe<std::string>(kNatronPluginPropIconFilePath).c_str()); pluginGroup = plugin->getPropertyNUnsafe<std::string>(kNatronPluginPropGrouping); pluginDescriptionIsMarkdown = plugin->getPropertyUnsafe<bool>(kNatronPluginPropDescriptionIsMarkdown); for (int i = 0; i < _imp->effect->getNInputs(); ++i) { QStringList input; input << convertFromPlainTextToMarkdown( QString::fromStdString( getInputLabel(i) ), genHTML, true ); input << convertFromPlainTextToMarkdown( QString::fromStdString( getInputHint(i) ), genHTML, true ); input << ( isInputOptional(i) ? tr("Yes") : tr("No") ); inputs.push_back(input); // Don't show more than doc for 4 inputs otherwise it will just clutter the page if (i == 3) { break; } } } // check for plugin icon QString pluginIconUrl; if ( !pluginIcon.isEmpty() ) { QFile iconFile(pluginIcon); if ( iconFile.exists() ) { if (genHTML) { pluginIconUrl.append( QString::fromUtf8("/LOCAL_FILE/") ); pluginIconUrl.append(pluginIcon); pluginIconUrl.replace( QString::fromUtf8("\\"), QString::fromUtf8("/") ); } else { pluginIconUrl.append(pluginID); pluginIconUrl.append(QString::fromUtf8(".png")); } } } // check for extra markdown file QString extraMarkdown; QString pluginMD = pluginIcon; pluginMD.replace( QString::fromUtf8(".png"), QString::fromUtf8(".md") ); QFile pluginMarkdownFile(pluginMD); if ( pluginMarkdownFile.exists() ) { if ( pluginMarkdownFile.open(QIODevice::ReadOnly | QIODevice::Text) ) { extraMarkdown = QString::fromUtf8( pluginMarkdownFile.readAll() ); pluginMarkdownFile.close(); } } // generate knobs info KnobsVec knobs = getEffectInstance()->getKnobs_mt_safe(); for (KnobsVec::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { #pragma message WARN("TODO: restore getDefaultIsSecret from RB-2.2") //if ( (*it)->getDefaultIsSecret() ) { if ( (*it)->getIsSecret() ) { continue; } if ((*it)->getKnobDeclarationType() != KnobI::eKnobDeclarationTypePlugin) { continue; } // do not escape characters in the scriptName, since it will be put between backquotes QString knobScriptName = /*NATRON_NAMESPACE::convertFromPlainTextToMarkdown(*/ QString::fromUtf8( (*it)->getName().c_str() )/*, genHTML, true)*/; QString knobLabel = NATRON_NAMESPACE::convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getLabel().c_str() ), genHTML, true); QString knobHint = NATRON_NAMESPACE::convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getHintToolTip().c_str() ), genHTML, true); // totally ignore the documentation for these parameters (which are always secret in Natron) if ( knobScriptName.startsWith( QString::fromUtf8("NatronOfxParam") ) || knobScriptName == QString::fromUtf8("exportAsPyPlug") || knobScriptName == QString::fromUtf8(kOCIOParamInputSpace) || knobScriptName == QString::fromUtf8(kOCIOParamOutputSpace) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamDisplay) ) ) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamView) ) ) || //( ( pluginID == QString::fromUtf8(kOCIOCDLTransformPluginIdentifier) ) && // ( knobScriptName == QString::fromUtf8(kOCIOCDLTransformParamCCCID) ) ) || ( ( pluginID == QString::fromUtf8(kOCIOFileTransformPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIOFileTransformParamCCCID) ) ) || false) { continue; } QString defValuesStr, knobType; std::vector<std::pair<QString, QString> > dimsDefaultValueStr; KnobIntPtr isInt = toKnobInt(*it); KnobChoicePtr isChoice = toKnobChoice(*it); KnobBoolPtr isBool = toKnobBool(*it); KnobDoublePtr isDbl = toKnobDouble(*it); KnobStringPtr isString = toKnobString(*it); bool isLabel = isString && isString->isLabel(); KnobSeparatorPtr isSep = toKnobSeparator(*it); KnobButtonPtr isBtn = toKnobButton(*it); KnobParametricPtr isParametric = toKnobParametric(*it); KnobGroupPtr isGroup = toKnobGroup(*it); KnobPagePtr isPage = toKnobPage(*it); KnobColorPtr isColor = toKnobColor(*it); if (isInt) { knobType = tr("Integer"); } else if (isChoice) { knobType = tr("Choice"); } else if (isBool) { knobType = tr("Boolean"); } else if (isDbl) { knobType = tr("Double"); } else if (isString) { if (isLabel) { knobType = tr("Label"); } else { knobType = tr("String"); } } else if (isSep) { knobType = tr("Separator"); } else if (isBtn) { knobType = tr("Button"); } else if (isParametric) { knobType = tr("Parametric"); } else if (isGroup) { knobType = tr("Group"); } else if (isPage) { knobType = tr("Page"); } else if (isColor) { knobType = tr("Color"); } else { knobType = tr("N/A"); } if (!isGroup && !isPage) { for (int i = 0; i < (*it)->getNDimensions(); ++i) { QString valueStr; if (!isBtn && !isSep && !isParametric) { // If this is a ChoiceParam and we are not generating live HTML doc, // only add the list of entries and their halp if this node should not be // ignored (eg. OCIO colorspace knobs). if ( isChoice && (genHTML || !( knobScriptName == QString::fromUtf8(kOCIOParamInputSpaceChoice) || knobScriptName == QString::fromUtf8(kOCIOParamOutputSpaceChoice) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamDisplayChoice) ) ) || ( ( pluginID == QString::fromUtf8(kOCIODisplayPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIODisplayParamViewChoice) ) ) || //( ( pluginID == QString::fromUtf8(kOCIOCDLTransformPluginIdentifier) ) && // ( knobScriptName == QString::fromUtf8(kOCIOCDLTransformParamCCCIDChoice) ) ) || ( ( pluginID == QString::fromUtf8(kOCIOFileTransformPluginIdentifier) ) && ( knobScriptName == QString::fromUtf8(kOCIOFileTransformParamCCCIDChoice) ) ) || ( ( pluginID == QString::fromUtf8("net.fxarena.openfx.Text") ) && ( knobScriptName == QString::fromUtf8("name") ) ) || // font family from Text plugin ( ( pluginID == QString::fromUtf8("net.fxarena.openfx.Polaroid") ) && ( knobScriptName == QString::fromUtf8("font") ) ) || // font family from Polaroid plugin ( ( pluginID == QString::fromUtf8(PLUGINID_NATRON_PRECOMP) ) && ( knobScriptName == QString::fromUtf8("writeNode") ) ) || ( ( pluginID == QString::fromUtf8(PLUGINID_NATRON_ONEVIEW) ) && ( knobScriptName == QString::fromUtf8("view") ) ) ) ) ) { // see also KnobChoice::getHintToolTipFull() int index = isChoice->getDefaultValue(DimIdx(i)); std::vector<ChoiceOption> entries = isChoice->getEntries(); if ( (index >= 0) && ( index < (int)entries.size() ) ) { valueStr = QString::fromUtf8( entries[index].id.c_str() ); } bool first = true; for (size_t i = 0; i < entries.size(); i++) { QString entryHelp = QString::fromUtf8( entries[i].tooltip.c_str() ); QString entry; if (entries[i].id != entries[i].label) { entry = QString::fromUtf8( "%1 (%2)" ).arg(QString::fromUtf8( entries[i].label.c_str() )).arg(QString::fromUtf8( entries[i].id.c_str() )); } else { entry = QString::fromUtf8( entries[i].label.c_str() ); } if (!entry.isEmpty()) { if (first) { // empty line before the option descriptions if (genHTML) { if ( !knobHint.isEmpty() ) { knobHint.append( QString::fromUtf8("<br />") ); } knobHint.append( tr("Possible values:") + QString::fromUtf8("<br />") ); } else { // we do a hack for multiline elements, because the markdown->rst conversion by pandoc doesn't use the line block syntax. // what we do here is put a supplementary dot at the beginning of each line, which is then converted to a pipe '|' in the // genStaticDocs.sh script by a simple sed command after converting to RsT if ( !knobHint.isEmpty() ) { if (!knobHint.startsWith( QString::fromUtf8(". ") )) { knobHint.prepend( QString::fromUtf8(". ") ); } knobHint.append( QString::fromUtf8("\\\n") ); } knobHint.append( QString::fromUtf8(". ") + tr("Possible values:") + QString::fromUtf8("\\\n") ); } first = false; } if (genHTML) { knobHint.append( QString::fromUtf8("<br />") ); } else { knobHint.append( QString::fromUtf8("\\\n") ); // we do a hack for multiline elements, because the markdown->rst conversion by pandoc doesn't use the line block syntax. // what we do here is put a supplementary dot at the beginning of each line, which is then converted to a pipe '|' in the // genStaticDocs.sh script by a simple sed command after converting to RsT knobHint.append( QString::fromUtf8(". ") ); } if (entryHelp.isEmpty()) { knobHint.append( QString::fromUtf8("**%1**").arg( convertFromPlainTextToMarkdown(entry, genHTML, true) ) ); } else { knobHint.append( QString::fromUtf8("**%1**: %2").arg( convertFromPlainTextToMarkdown(entry, genHTML, true) ).arg( convertFromPlainTextToMarkdown(entryHelp, genHTML, true) ) ); } } } } else if (isInt) { valueStr = QString::number( isInt->getDefaultValue( DimIdx(i) ) ); } else if (isDbl) { valueStr = QString::number( isDbl->getDefaultValue( DimIdx(i) ) ); } else if (isBool) { valueStr = isBool->getDefaultValue( DimIdx(i) ) ? tr("On") : tr("Off"); } else if (isString) { valueStr = QString::fromUtf8( isString->getDefaultValue( DimIdx(i) ).c_str() ); } else if (isColor) { valueStr = QString::number( isColor->getDefaultValue( DimIdx(i) ) ); } } dimsDefaultValueStr.push_back( std::make_pair(convertFromPlainTextToMarkdown( QString::fromUtf8( (*it)->getDimensionName( DimIdx(i) ).c_str() ), genHTML, true ), convertFromPlainTextToMarkdown(valueStr, genHTML, true)) ); } for (std::size_t i = 0; i < dimsDefaultValueStr.size(); ++i) { if ( !dimsDefaultValueStr[i].second.isEmpty() ) { if (dimsDefaultValueStr.size() > 1) { defValuesStr.append(dimsDefaultValueStr[i].first); defValuesStr.append( QString::fromUtf8(": ") ); } defValuesStr.append(dimsDefaultValueStr[i].second); if (i < dimsDefaultValueStr.size() - 1) { defValuesStr.append( QString::fromUtf8(" ") ); } } } if ( defValuesStr.isEmpty() ) { defValuesStr = tr("N/A"); } } if (!isPage && !isSep && !isGroup && !isLabel) { QStringList row; row << knobLabel << knobScriptName << knobType << defValuesStr << knobHint; items.append(row); } } // for (KnobsVec::const_iterator it = knobs.begin(); it!=knobs.end(); ++it) { // generate plugin info ms << tr("%1 node").arg(pluginLabel) << "\n==========\n\n"; // a hack to avoid repeating the documentation for the various merge plugins if ( pluginID.startsWith( QString::fromUtf8("net.sf.openfx.Merge") ) ) { std::string id = pluginID.toStdString(); std::string op; if (id == PLUGINID_OFX_MERGE) { // do nothing } else if (id == "net.sf.openfx.MergeDifference") { op = "difference (a.k.a. absminus)"; } else if (id == "net.sf.openfx.MergeIn") { op = "in"; } else if (id == "net.sf.openfx.MergeMatte") { op = "matte"; } else if (id == "net.sf.openfx.MergeMax") { op = "max"; } else if (id == "net.sf.openfx.MergeMin") { op = "min"; } else if (id == "net.sf.openfx.MergeMultiply") { op = "multiply"; } else if (id == "net.sf.openfx.MergeOut") { op = "out"; } else if (id == "net.sf.openfx.MergePlus") { op = "plus"; } else if (id == "net.sf.openfx.MergeScreen") { op = "screen"; } if ( !op.empty() ) { // we should use the custom link "[Merge node](|http::/plugins/" PLUGINID_OFX_MERGE ".html||rst::net.sf.openfx.MergePlugin|)" // but pandoc borks it ms << tr("The *%1* node is a convenience node identical to the %2, except that the operator is set to *%3* by default.") .arg(pluginLabel) .arg(genHTML ? QString::fromUtf8("<a href=\"" PLUGINID_OFX_MERGE ".html\">Merge node</a>") : QString::fromUtf8(":ref:`" PLUGINID_OFX_MERGE "`") //QString::fromUtf8("[Merge node](http::/plugins/" PLUGINID_OFX_MERGE ".html)") ) .arg( QString::fromUtf8( op.c_str() ) ); goto OUTPUT; } } if (!pluginIconUrl.isEmpty()) { // add a nonbreaking space so that pandoc doesn't use the alt-text as a caption // http://pandoc.org/MANUAL.html#images ms << "![pluginIcon](" << pluginIconUrl << ")"; if (!genHTML) { // specify image width for pandoc-generated printed doc // (for hoedown-generated HTML, this handled by the CSS using the alt=pluginIcon attribute) // see http://pandoc.org/MANUAL.html#images // note that only % units are understood both by pandox and sphinx ms << "{ width=10% }"; } ms << " \n\n"; // required so that there is no legend when converted to rst by pandoc } ms << tr("*This documentation is for version %2.%3 of %1.*").arg(pluginLabel).arg(majorVersion).arg(minorVersion) << "\n\n"; ms << "\n" << tr("Description") << "\n--------------------------------------------------------------------------------\n\n"; if (!pluginDescriptionIsMarkdown) { if (genHTML) { pluginDescription = NATRON_NAMESPACE::convertFromPlainText(pluginDescription, NATRON_NAMESPACE::WhiteSpaceNormal); // replace URLs with links QRegExp re( QString::fromUtf8("((http|ftp|https)://([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?)") ); pluginDescription.replace( re, QString::fromUtf8("<a href=\"\\1\">\\1</a>") ); } else { pluginDescription = convertFromPlainTextToMarkdown(pluginDescription, genHTML, false); } } ms << pluginDescription << "\n"; // create markdown table ms << "\n" << tr("Inputs") << "\n--------------------------------------------------------------------------------\n\n"; ms << tr("Input") << " | " << tr("Description") << " | " << tr("Optional") << "\n"; ms << "--- | --- | ---\n"; if (inputs.size() > 0) { Q_FOREACH(const QStringList &input, inputs) { QString inputName = input.at(0); QString inputDesc = input.at(1); QString inputOpt = input.at(2); ms << inputName << " | " << inputDesc << " | " << inputOpt << "\n"; }
void DiskCacheNode::initializeKnobs() { KnobPagePtr page = AppManager::createKnob<KnobPage>( shared_from_this(), tr("Controls") ); KnobChoicePtr frameRange = AppManager::createKnob<KnobChoice>( shared_from_this(), tr("Frame range") ); frameRange->setName("frameRange"); frameRange->setAnimationEnabled(false); std::vector<std::string> choices; choices.push_back("Input frame range"); choices.push_back("Project frame range"); choices.push_back("Manual"); frameRange->populateChoices(choices); frameRange->setEvaluateOnChange(false); frameRange->setDefaultValue(0); page->addKnob(frameRange); _imp->frameRange = frameRange; KnobIntPtr firstFrame = AppManager::createKnob<KnobInt>( shared_from_this(), tr("First Frame") ); firstFrame->setAnimationEnabled(false); firstFrame->setName("firstFrame"); firstFrame->disableSlider(); firstFrame->setEvaluateOnChange(false); firstFrame->setAddNewLine(false); firstFrame->setDefaultValue(1); firstFrame->setSecret(true); page->addKnob(firstFrame); _imp->firstFrame = firstFrame; KnobIntPtr lastFrame = AppManager::createKnob<KnobInt>( shared_from_this(), tr("Last Frame") ); lastFrame->setAnimationEnabled(false); lastFrame->setName("LastFrame"); lastFrame->disableSlider(); lastFrame->setEvaluateOnChange(false); lastFrame->setDefaultValue(100); lastFrame->setSecret(true); page->addKnob(lastFrame); _imp->lastFrame = lastFrame; KnobButtonPtr preRender = AppManager::createKnob<KnobButton>( shared_from_this(), tr("Pre-cache") ); preRender->setName("preRender"); preRender->setEvaluateOnChange(false); preRender->setHintToolTip( tr("Cache the frame range specified by rendering images at zoom-level 100% only.") ); page->addKnob(preRender); _imp->preRender = preRender; }
void DiskCacheNode::initializeKnobs() { KnobPagePtr page = createKnob<KnobPage>("controlsPage"); page->setLabel(tr("Controls") ); KnobChoicePtr frameRange = createKnob<KnobChoice>(kDiskCacheNodeFrameRange); frameRange->setLabel(tr(kDiskCacheNodeFrameRangeLabel) ); frameRange->setHintToolTip(tr(kDiskCacheNodeFrameRangeHint)); frameRange->setAnimationEnabled(false); { std::vector<ChoiceOption> choices; choices.push_back(ChoiceOption("Input frame range", "", "")); choices.push_back(ChoiceOption("Project frame range", "", "")); choices.push_back(ChoiceOption("Manual","", "")); frameRange->populateChoices(choices); } frameRange->setEvaluateOnChange(false); frameRange->setDefaultValue(0); page->addKnob(frameRange); _imp->frameRange = frameRange; KnobIntPtr firstFrame = createKnob<KnobInt>(kDiskCacheNodeFirstFrame); firstFrame->setLabel(tr(kDiskCacheNodeFirstFrameLabel) ); firstFrame->setHintToolTip(tr(kDiskCacheNodeFirstFrameHint)); firstFrame->setAnimationEnabled(false); firstFrame->disableSlider(); firstFrame->setEvaluateOnChange(false); firstFrame->setAddNewLine(false); firstFrame->setDefaultValue(1); firstFrame->setSecret(true); page->addKnob(firstFrame); _imp->firstFrame = firstFrame; KnobIntPtr lastFrame = createKnob<KnobInt>(kDiskCacheNodeLastFrame); lastFrame->setAnimationEnabled(false); lastFrame->setLabel(tr(kDiskCacheNodeLastFrameLabel)); lastFrame->setHintToolTip(tr(kDiskCacheNodeLastFrameHint)); lastFrame->disableSlider(); lastFrame->setEvaluateOnChange(false); lastFrame->setDefaultValue(100); lastFrame->setSecret(true); page->addKnob(lastFrame); _imp->lastFrame = lastFrame; KnobButtonPtr preRender = createKnob<KnobButton>("preRender"); preRender->setLabel(tr("Pre-cache")); preRender->setEvaluateOnChange(false); preRender->setHintToolTip( tr("Cache the frame range specified by rendering images at zoom-level 100% only.") ); page->addKnob(preRender); _imp->preRender = preRender; }
ActionRetCodeEnum RotoShapeRenderNode::getRegionOfDefinition(TimeValue time, const RenderScale& scale, ViewIdx view, RectD* rod) { RotoDrawableItemPtr item = getAttachedRotoItem(); assert(item); assert((isRenderClone() && item->isRenderClone()) || (!isRenderClone() && !item->isRenderClone())); const bool isPainting = isDuringPaintStrokeCreation(); RectD shapeRoD; getRoDFromItem(item, time, view, isPainting, &shapeRoD); bool clipToFormat = _imp->clipToFormatKnob.lock()->getValue(); RotoShapeRenderTypeEnum type = (RotoShapeRenderTypeEnum)_imp->renderType.lock()->getValue(); switch (type) { case eRotoShapeRenderTypeSmear: { RectD defaultRod; ActionRetCodeEnum stat = EffectInstance::getRegionOfDefinition(time, scale, view, &defaultRod); if (isFailureRetCode(stat)) { return stat; } if (!defaultRod.isNull()) { *rod = shapeRoD; rod->merge(defaultRod); } } break; case eRotoShapeRenderTypeSolid: { RotoPaintOutputRoDTypeEnum rodType = (RotoPaintOutputRoDTypeEnum)_imp->outputRoDTypeKnob.lock()->getValue(); switch (rodType) { case eRotoPaintOutputRoDTypeDefault: { *rod = shapeRoD; // No format is set, use the format from the input if (clipToFormat) { EffectInstancePtr inputEffect = getInputRenderEffectAtAnyTimeView(0); if (inputEffect) { RectI outputFormat = inputEffect->getOutputFormat(); RectD outputFormatCanonical; outputFormat.toCanonical_noClipping(scale, inputEffect->getAspectRatio(-1), &outputFormatCanonical); rod->intersect(outputFormatCanonical, rod); } } } break; case eRotoPaintOutputRoDTypeFormat: { KnobIntPtr sizeKnob = _imp->outputFormatSizeKnob.lock(); int w = sizeKnob->getValue(DimIdx(0)); int h = _imp->outputFormatSizeKnob.lock()->getValue(DimIdx(1)); double par = _imp->outputFormatParKnob.lock()->getValue(); RectI pixelFormat; pixelFormat.x1 = pixelFormat.y1 = 0; pixelFormat.x2 = w; pixelFormat.y2 = h; RenderScale renderScale(1.); pixelFormat.toCanonical_noClipping(renderScale, par, rod); if (!clipToFormat) { rod->merge(shapeRoD); } } break; case eRotoPaintOutputRoDTypeProject: { Format f; getApp()->getProject()->getProjectDefaultFormat(&f); f.toCanonical_noClipping(RenderScale(1.), f.getPixelAspectRatio(), rod); if (!clipToFormat) { rod->merge(shapeRoD); } } break; } } break; } return eActionStatusOK; }
ActionRetCodeEnum RotoShapeRenderNode::getTimeInvariantMetadata(NodeMetadata& metadata) { assert(_imp->renderType.lock()); RotoShapeRenderTypeEnum type = (RotoShapeRenderTypeEnum)_imp->renderType.lock()->getValue(); int nComps; if (type == eRotoShapeRenderTypeSolid) { // If there's an input to the RotoShapeRender node, pass-through the meta-data number of components so that downstream nodes // have a good default color plane. // E.g: if we have Constant--> RotoShapeRender --> Merge, we want the Merge to have the number of components of the Constant EffectInstancePtr inputEffect = getInputRenderEffectAtAnyTimeView(0); if (inputEffect) { ImagePlaneDesc inputPlane, paireInputPlane; inputEffect->getMetadataComponents(-1, &inputPlane, &paireInputPlane); nComps = inputPlane.getNumComponents(); } else { nComps = 1; } } else { nComps = 4; } metadata.setColorPlaneNComps(-1, nComps); metadata.setColorPlaneNComps(0, nComps); // The roto can be sampled at any non integer time metadata.setIsContinuous(true); RotoPaintOutputRoDTypeEnum rodType = (RotoPaintOutputRoDTypeEnum)_imp->outputRoDTypeKnob.lock()->getValue(); switch (rodType) { case eRotoPaintOutputRoDTypeDefault: // No format is set break; case eRotoPaintOutputRoDTypeFormat: { KnobIntPtr sizeKnob = _imp->outputFormatSizeKnob.lock(); int w = sizeKnob->getValue(DimIdx(0)); int h = _imp->outputFormatSizeKnob.lock()->getValue(DimIdx(1)); double par = _imp->outputFormatParKnob.lock()->getValue(); RectI pixelFormat; pixelFormat.x1 = pixelFormat.y1 = 0; pixelFormat.x2 = w; pixelFormat.y2 = h; metadata.setPixelAspectRatio(-1, par); metadata.setOutputFormat(pixelFormat); } break; case eRotoPaintOutputRoDTypeProject: { Format f; getApp()->getProject()->getProjectDefaultFormat(&f); metadata.setPixelAspectRatio(-1, f.getPixelAspectRatio()); metadata.setOutputFormat(f); } break; } return eActionStatusOK; }
void RotoShapeRenderNode::initializeKnobs() { assert(!isRenderClone()); KnobPagePtr page = createKnob<KnobPage>("controlsPage"); page->setLabel(tr("Controls")); { KnobChoicePtr param = createKnob<KnobChoice>(kRotoShapeRenderNodeParamType); param->setLabel(tr(kRotoShapeRenderNodeParamTypeLabel)); { std::vector<ChoiceOption> options; options.push_back(ChoiceOption(kRotoShapeRenderNodeParamTypeSolid, "", "")); options.push_back(ChoiceOption(kRotoShapeRenderNodeParamTypeSmear, "", "")); param->populateChoices(options); } param->setIsMetadataSlave(true); page->addKnob(param); _imp->renderType = param; } { KnobChoicePtr param = createKnob<KnobChoice>(kRotoOutputRodType); param->setLabel(tr(kRotoOutputRodTypeLabel)); param->setHintToolTip(tr(kRotoOutputRodTypeHint)); std::vector<ChoiceOption> options; options.push_back(ChoiceOption(kRotoOutputRodTypeDefaultID, kRotoOutputRodTypeDefaultLabel, tr(kRotoOutputRodTypeDefaultHint).toStdString())); options.push_back(ChoiceOption(kRotoOutputRodTypeFormatID, kRotoOutputRodTypeFormatLabel, tr(kRotoOutputRodTypeFormatHint).toStdString())); options.push_back(ChoiceOption(kRotoOutputRodTypeProjectID, kRotoOutputRodTypeProjectLabel, tr(kRotoOutputRodTypeProjectHint).toStdString())); param->populateChoices(options); param->setAddNewLine(false); param->setIsPersistent(false); page->addKnob(param); _imp->outputRoDTypeKnob = param; } { KnobChoicePtr param = createKnob<KnobChoice>(kRotoFormatParam); param->setLabel(tr(kRotoFormatParamLabel)); param->setHintToolTip(tr(kRotoFormatParamHint)); page->addKnob(param); param->setIsPersistent(false); _imp->outputFormatKnob = param; } { KnobIntPtr param = createKnob<KnobInt>(kRotoFormatSize, 2); param->setSecret(true); page->addKnob(param); param->setIsPersistent(false); _imp->outputFormatSizeKnob = param; } { KnobDoublePtr param = createKnob<KnobDouble>(kRotoFormatPar); param->setSecret(true); page->addKnob(param); param->setIsPersistent(false); _imp->outputFormatParKnob = param; } { KnobBoolPtr param = createKnob<KnobBool>(kRotoClipToFormatParam); param->setLabel(tr(kRotoClipToFormatParamLabel)); param->setHintToolTip(tr(kRotoClipToFormatParamHint)); param->setDefaultValue(false); page->addKnob(param); _imp->clipToFormatKnob = param; } }
void NodeAnim::initialize(AnimatedItemTypeEnum nodeType) { _imp->nodeType = nodeType; NodePtr internalNode = getNodeGui()->getNode(); AnimationModuleBasePtr model = getModel(); NodeAnimPtr thisShared = shared_from_this(); _imp->nameItem = new QTreeWidgetItem; _imp->nameItem->setData(0, QT_ROLE_CONTEXT_ITEM_POINTER, qVariantFromValue((void*)thisShared.get())); _imp->nameItem->setText( 0, QString::fromUtf8( internalNode->getLabel().c_str() ) ); _imp->nameItem->setData(0, QT_ROLE_CONTEXT_TYPE, nodeType); _imp->nameItem->setData(0, QT_ROLE_CONTEXT_IS_ANIMATED, true); _imp->nameItem->setExpanded(true); int nCols = getModel()->getTreeColumnsCount(); if (nCols > ANIMATION_MODULE_TREE_VIEW_COL_VISIBLE) { _imp->nameItem->setData(ANIMATION_MODULE_TREE_VIEW_COL_VISIBLE, QT_ROLE_CONTEXT_ITEM_VISIBLE, QVariant(true)); _imp->nameItem->setIcon(ANIMATION_MODULE_TREE_VIEW_COL_VISIBLE, model->getItemVisibilityIcon(true)); } if (nCols > ANIMATION_MODULE_TREE_VIEW_COL_PLUGIN_ICON) { QString iconFilePath = QString::fromUtf8(internalNode->getPluginIconFilePath().c_str()); AnimationModuleTreeView::setItemIcon(iconFilePath, _imp->nameItem); } connect( internalNode.get(), SIGNAL(labelChanged(QString, QString)), this, SLOT(onNodeLabelChanged(QString, QString)) ); initializeKnobsAnim(); initializeTableItems(); // Connect signals/slots to knobs to compute the frame range AnimationModulePtr animModel = toAnimationModule(model); assert(animModel); if (nodeType == eAnimatedItemTypeCommon) { // Also connect the lifetime knob KnobIntPtr lifeTimeKnob = internalNode->getLifeTimeKnob(); if (lifeTimeKnob) { connect( lifeTimeKnob->getSignalSlotHandler().get(), SIGNAL(mustRefreshKnobGui(ViewSetSpec,DimSpec,ValueChangedReasonEnum)), this, SLOT(onFrameRangeKnobChanged()) ); } } else if (nodeType == eAnimatedItemTypeReader) { // The dopesheet view must refresh if the user set some values in the settings panel // so we connect some signals/slots KnobIPtr lastFrameKnob = internalNode->getKnobByName(kReaderParamNameLastFrame); if (!lastFrameKnob) { return; } boost::shared_ptr<KnobSignalSlotHandler> lastFrameKnobHandler = lastFrameKnob->getSignalSlotHandler(); assert(lastFrameKnob); boost::shared_ptr<KnobSignalSlotHandler> startingTimeKnob = internalNode->getKnobByName(kReaderParamNameStartingTime)->getSignalSlotHandler(); assert(startingTimeKnob); connect( lastFrameKnobHandler.get(), SIGNAL(mustRefreshKnobGui(ViewSetSpec,DimSpec,ValueChangedReasonEnum)), this, SLOT(onFrameRangeKnobChanged()) ); connect( startingTimeKnob.get(), SIGNAL(mustRefreshKnobGui(ViewSetSpec,DimSpec,ValueChangedReasonEnum)), this, SLOT(onFrameRangeKnobChanged()) ); // We don't make the connection for the first frame knob, because the // starting time is updated when it's modified. Thus we avoid two // refreshes of the view. } else if (nodeType == eAnimatedItemTypeRetime) { boost::shared_ptr<KnobSignalSlotHandler> speedKnob = internalNode->getKnobByName(kRetimeParamNameSpeed)->getSignalSlotHandler(); assert(speedKnob); connect( speedKnob.get(), SIGNAL(mustRefreshKnobGui(ViewSetSpec,DimSpec,ValueChangedReasonEnum)), this, SLOT(onFrameRangeKnobChanged()) ); } else if (nodeType == eAnimatedItemTypeTimeOffset) { boost::shared_ptr<KnobSignalSlotHandler> timeOffsetKnob = internalNode->getKnobByName(kReaderParamNameTimeOffset)->getSignalSlotHandler(); assert(timeOffsetKnob); connect( timeOffsetKnob.get(), SIGNAL(mustRefreshKnobGui(ViewSetSpec,DimSpec,ValueChangedReasonEnum)), this, SLOT(onFrameRangeKnobChanged()) ); } else if (nodeType == eAnimatedItemTypeFrameRange) { boost::shared_ptr<KnobSignalSlotHandler> frameRangeKnob = internalNode->getKnobByName(kFrameRangeParamNameFrameRange)->getSignalSlotHandler(); assert(frameRangeKnob); connect( frameRangeKnob.get(), SIGNAL(mustRefreshKnobGui(ViewSetSpec,DimSpec,ValueChangedReasonEnum)), this, SLOT(onFrameRangeKnobChanged()) ); } refreshFrameRange(); } // initialize
void TrackMarker::initializeKnobs() { TrackerContextPtr context = _imp->context.lock(); KnobIntPtr defPatternSizeKnob = context->getDefaultMarkerPatternWinSizeKnob(); KnobIntPtr defSearchSizeKnob = context->getDefaultMarkerSearchWinSizeKnob(); double patternHalfSize = defPatternSizeKnob->getValue() / 2.; double searchHalfSize = defSearchSizeKnob->getValue() / 2.; KnobDoublePtr swbbtmLeft = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamSearchWndBtmLeftLabel), 2, false); swbbtmLeft->setName(kTrackerParamSearchWndBtmLeft); swbbtmLeft->setDefaultValue(-searchHalfSize, 0); swbbtmLeft->setDefaultValue(-searchHalfSize, 1); swbbtmLeft->setHintToolTip( tr(kTrackerParamSearchWndBtmLeftHint) ); _imp->searchWindowBtmLeft = swbbtmLeft; KnobDoublePtr swbtRight = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamSearchWndTopRightLabel), 2, false); swbtRight->setName(kTrackerParamSearchWndTopRight); swbtRight->setDefaultValue(searchHalfSize, 0); swbtRight->setDefaultValue(searchHalfSize, 1); swbtRight->setHintToolTip( tr(kTrackerParamSearchWndTopRightHint) ); _imp->searchWindowTopRight = swbtRight; KnobDoublePtr ptLeft = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamPatternTopLeftLabel), 2, false); ptLeft->setName(kTrackerParamPatternTopLeft); ptLeft->setDefaultValue(-patternHalfSize, 0); ptLeft->setDefaultValue(patternHalfSize, 1); ptLeft->setHintToolTip( tr(kTrackerParamPatternTopLeftHint) ); _imp->patternTopLeft = ptLeft; KnobDoublePtr ptRight = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamPatternTopRightLabel), 2, false); ptRight->setName(kTrackerParamPatternTopRight); ptRight->setDefaultValue(patternHalfSize, 0); ptRight->setDefaultValue(patternHalfSize, 1); ptRight->setHintToolTip( tr(kTrackerParamPatternTopRightHint) ); _imp->patternTopRight = ptRight; KnobDoublePtr pBRight = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamPatternBtmRightLabel), 2, false); pBRight->setName(kTrackerParamPatternBtmRight); pBRight->setDefaultValue(patternHalfSize, 0); pBRight->setDefaultValue(-patternHalfSize, 1); pBRight->setHintToolTip( tr(kTrackerParamPatternBtmRightHint) ); _imp->patternBtmRight = pBRight; KnobDoublePtr pBLeft = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamPatternBtmLeftLabel), 2, false); pBLeft->setName(kTrackerParamPatternBtmLeft); pBLeft->setDefaultValue(-patternHalfSize, 0); pBLeft->setDefaultValue(-patternHalfSize, 1); pBLeft->setHintToolTip( tr(kTrackerParamPatternBtmLeftHint) ); _imp->patternBtmLeft = pBLeft; KnobDoublePtr centerKnob = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamCenterLabel), 2, false); centerKnob->setName(kTrackerParamCenter); centerKnob->setHintToolTip( tr(kTrackerParamCenterHint) ); _imp->center = centerKnob; KnobDoublePtr offsetKnob = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamOffsetLabel), 2, false); offsetKnob->setName(kTrackerParamOffset); offsetKnob->setHintToolTip( tr(kTrackerParamOffsetHint) ); _imp->offset = offsetKnob; #ifdef NATRON_TRACK_MARKER_USE_WEIGHT KnobDoublePtr weightKnob = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamTrackWeightLabel), 1, false); weightKnob->setName(kTrackerParamTrackWeight); weightKnob->setHintToolTip( tr(kTrackerParamTrackWeightHint) ); weightKnob->setDefaultValue(1.); weightKnob->setAnimationEnabled(false); weightKnob->setMinimum(0.); weightKnob->setMaximum(1.); _imp->weight = weightKnob; #endif KnobChoicePtr mmodelKnob = AppManager::createKnob<KnobChoice>(shared_from_this(), tr(kTrackerParamMotionModelLabel), 1, false); mmodelKnob->setHintToolTip( tr(kTrackerParamMotionModelHint) ); mmodelKnob->setName(kTrackerParamMotionModel); { std::vector<std::string> choices, helps; TrackerContext::getMotionModelsAndHelps(true, &choices, &helps); mmodelKnob->populateChoices(choices, helps); } mmodelKnob->setDefaultValue(0); _imp->motionModel = mmodelKnob; KnobDoublePtr errKnob = AppManager::createKnob<KnobDouble>(shared_from_this(), tr(kTrackerParamErrorLabel), 1, false); errKnob->setName(kTrackerParamError); _imp->error = errKnob; KnobBoolPtr enableKnob = AppManager::createKnob<KnobBool>(shared_from_this(), tr(kTrackerParamEnabledLabel), 1, false); enableKnob->setName(kTrackerParamEnabled); enableKnob->setHintToolTip( tr(kTrackerParamEnabledHint) ); enableKnob->setAnimationEnabled(true); enableKnob->setDefaultValue(true); _imp->enabled = enableKnob; QObject::connect( this, SIGNAL(enabledChanged(int)), _imp->context.lock().get(), SLOT(onMarkerEnabledChanged(int)) ); boost::shared_ptr<KnobSignalSlotHandler> handler = _imp->center.lock()->getSignalSlotHandler(); QObject::connect( handler.get(), SIGNAL(keyFrameSet(double,ViewSpec,int,int,bool)), this, SLOT(onCenterKeyframeSet(double,ViewSpec,int,int,bool)) ); QObject::connect( handler.get(), SIGNAL(keyFrameRemoved(double,ViewSpec,int,int)), this, SLOT(onCenterKeyframeRemoved(double,ViewSpec,int,int)) ); QObject::connect( handler.get(), SIGNAL(keyFrameMoved(ViewSpec,int,double,double)), this, SLOT(onCenterKeyframeMoved(ViewSpec,int,double,double)) ); QObject::connect( handler.get(), SIGNAL(multipleKeyFramesSet(std::list<double>,ViewSpec,int,int)), this, SLOT(onCenterKeyframesSet(std::list<double>,ViewSpec,int,int)) ); QObject::connect( handler.get(), SIGNAL(multipleKeyFramesRemoved(std::list<double>,ViewSpec,int,int)), this, SLOT(onCenterMultipleKeysRemoved(std::list<double>,ViewSpec,int,int)) ); QObject::connect( handler.get(), SIGNAL(animationRemoved(ViewSpec,int)), this, SLOT(onCenterAnimationRemoved(ViewSpec,int)) ); QObject::connect( handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onCenterKnobValueChanged(ViewSpec,int,int)) ); handler = _imp->offset.lock()->getSignalSlotHandler(); QObject::connect( handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onOffsetKnobValueChanged(ViewSpec,int,int)) ); handler = _imp->error.lock()->getSignalSlotHandler(); QObject::connect( handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onErrorKnobValueChanged(ViewSpec,int,int)) ); #ifdef NATRON_TRACK_MARKER_USE_WEIGHT handler = _imp->weight.lock()->getSignalSlotHandler(); QObject::connect( handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onWeightKnobValueChanged(ViewSpec,int,int)) ); #endif handler = _imp->motionModel.lock()->getSignalSlotHandler(); QObject::connect( handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onMotionModelKnobValueChanged(ViewSpec,int,int)) ); /*handler = _imp->patternBtmLeft->getSignalSlotHandler(); QObject::connect(handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onPatternBtmLeftKnobValueChanged(int,int))); handler = _imp->patternTopLeft->getSignalSlotHandler(); QObject::connect(handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onPatternTopLeftKnobValueChanged(int,int))); handler = _imp->patternTopRight->getSignalSlotHandler(); QObject::connect(handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onPatternTopRightKnobValueChanged(int,int))); handler = _imp->patternBtmRight->getSignalSlotHandler(); QObject::connect(handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onPatternBtmRightKnobValueChanged(int,int))); */ handler = _imp->searchWindowBtmLeft.lock()->getSignalSlotHandler(); QObject::connect( handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onSearchBtmLeftKnobValueChanged(ViewSpec,int,int)) ); handler = _imp->searchWindowTopRight.lock()->getSignalSlotHandler(); QObject::connect( handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onSearchTopRightKnobValueChanged(ViewSpec,int,int)) ); handler = _imp->enabled.lock()->getSignalSlotHandler(); QObject::connect( handler.get(), SIGNAL(valueChanged(ViewSpec,int,int)), this, SLOT(onEnabledValueChanged(ViewSpec,int,int)) ); } // TrackMarker::initializeKnobs
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