diff --git a/App/App.pro b/App/App.pro index 6ba9256134..9c08cc90f2 100644 --- a/App/App.pro +++ b/App/App.pro @@ -26,7 +26,7 @@ win32 { CONFIG += app } CONFIG += moc -CONFIG += boost glew opengl qt cairo python shiboken pyside +CONFIG += boost opengl qt cairo python shiboken pyside glfw CONFIG += static-gui static-engine static-host-support static-breakpadclient static-libmv static-openmvg static-ceres static-qhttpserver QT += gui core opengl network diff --git a/Engine/AppInstance.cpp b/Engine/AppInstance.cpp index 5bec6a83f3..0e49c0a77e 100644 --- a/Engine/AppInstance.cpp +++ b/Engine/AppInstance.cpp @@ -536,6 +536,13 @@ AppInstance::load(const CLArgs& cl, return; } + if (getAppID() == 0) { + QString missingOpenGLError; + if (!appPTR->hasPlatformNecessaryOpenGLRequirements(&missingOpenGLError)) { + throw std::runtime_error(missingOpenGLError.toStdString()); + } + } + ///if the app is a background project autorun and the project name is empty just throw an exception. if ( ( (appPTR->getAppType() == AppManager::eAppTypeBackgroundAutoRun) || ( appPTR->getAppType() == AppManager::eAppTypeBackgroundAutoRunLaunchedFromGui) ) ) { diff --git a/Engine/AppManager.cpp b/Engine/AppManager.cpp index 0a62cab5fc..b0f03f278b 100644 --- a/Engine/AppManager.cpp +++ b/Engine/AppManager.cpp @@ -50,6 +50,10 @@ #endif #endif + +#include +#include + #include #include #include @@ -63,7 +67,7 @@ #include "Global/ProcInfo.h" - +#include "Global/GLIncludes.h" #include "Engine/AppInstance.h" #include "Engine/Backdrop.h" @@ -97,6 +101,11 @@ #include "sbkversion.h" // shiboken/pyside version +#define GLFW_VERSION_STRING NATRON_VERSION_STRINGIZE( \ +GLFW_VERSION_MAJOR, \ +GLFW_VERSION_MINOR, \ +GLFW_VERSION_REVISION) + #if QT_VERSION < 0x050000 Q_DECLARE_METATYPE(QAbstractSocket::SocketState) #endif @@ -375,6 +384,8 @@ AppManager::~AppManager() _imp->_viewerCache.reset(); _imp->_diskCache.reset(); + // free OpenGL resources of glfw + _imp->teardownGlfw(); tearDownPython(); _instance = 0; @@ -545,6 +556,9 @@ AppManager::loadInternal(const CLArgs& cl) # endif + // Initialize OpenGL + _imp->initGlfw(); + _imp->_settings.reset( new Settings() ); _imp->_settings->initializeKnobsPublic(); ///Call restore after initializing knobs @@ -1129,8 +1143,17 @@ AppManager::registerBuiltInPlugin(const QString& iconPath, for (std::list::iterator it = grouping.begin(); it != grouping.end(); ++it) { qgrouping.push_back( QString::fromUtf8( it->c_str() ) ); } - Plugin* p = registerPlugin(qgrouping, QString::fromUtf8( node->getPluginID().c_str() ), QString::fromUtf8( node->getPluginLabel().c_str() ), + + // Empty since statically bundled + QString resourcesPath = QString(); + + Plugin* p = registerPlugin(resourcesPath,qgrouping, QString::fromUtf8( node->getPluginID().c_str() ), QString::fromUtf8( node->getPluginLabel().c_str() ), iconPath, QStringList(), node->isReader(), node->isWriter(), binary, node->renderThreadSafety() == eRenderSafetyUnsafe, node->getMajorVersion(), node->getMinorVersion(), isDeprecated); + + std::list shortcuts; + node->getPluginShortcuts(&shortcuts); + p->setShorcuts(shortcuts); + if (internalUseOnly) { p->setForInternalUseOnly(true); } @@ -1528,7 +1551,7 @@ AppManager::loadPythonGroups() if (gotInfos) { qDebug() << "Loading " << moduleName; QStringList grouping = QString::fromUtf8( pluginGrouping.c_str() ).split( QChar::fromLatin1('/') ); - Plugin* p = registerPlugin(grouping, QString::fromUtf8( pluginID.c_str() ), QString::fromUtf8( pluginLabel.c_str() ), QString::fromUtf8( iconFilePath.c_str() ), QStringList(), false, false, 0, false, version, 0, false); + Plugin* p = registerPlugin(modulePath, grouping, QString::fromUtf8( pluginID.c_str() ), QString::fromUtf8( pluginLabel.c_str() ), QString::fromUtf8( iconFilePath.c_str() ), QStringList(), false, false, 0, false, version, 0, false); p->setPythonModule(modulePath + moduleName); p->setToolsetScript(isToolset); @@ -1537,7 +1560,8 @@ AppManager::loadPythonGroups() } // AppManager::loadPythonGroups Plugin* -AppManager::registerPlugin(const QStringList & groups, +AppManager::registerPlugin(const QString& resourcesPath, + const QStringList & groups, const QString & pluginID, const QString & pluginLabel, const QString & pluginIconPath, @@ -1555,7 +1579,7 @@ AppManager::registerPlugin(const QStringList & groups, if (mustCreateMutex) { pluginMutex = new QMutex(QMutex::Recursive); } - Plugin* plugin = new Plugin(binary, pluginID, pluginLabel, pluginIconPath, groupIconPath, groups, pluginMutex, major, minor, + Plugin* plugin = new Plugin(binary, resourcesPath, pluginID, pluginLabel, pluginIconPath, groupIconPath, groups, pluginMutex, major, minor, isReader, isWriter, isDeprecated); std::string stdID = pluginID.toStdString(); @@ -2985,6 +3009,55 @@ AppManager::hasThreadsRendering() const return false; } + + +QString +AppManager::getGlfwVersion() const +{ + return QString::fromUtf8(GLFW_VERSION_STRING) + QString::fromUtf8(" / ") + QString::fromUtf8( glfwGetVersionString() ); +} + +bool +AppManager::hasPlatformNecessaryOpenGLRequirements(QString* missingOpenGLError) const +{ + if (missingOpenGLError) { + *missingOpenGLError = _imp->missingOpenglError; + } + return _imp->hasRequiredOpenGLVersionAndExtensions; +} + +QString +AppManager::getOpenGLVersion() const +{ + QString glslVer = QString::fromUtf8((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION)); + QString openglVer = QString::fromUtf8((const char*)glGetString(GL_VERSION)); + return openglVer + QString::fromUtf8(", GLSL ") + glslVer; +} + +QString +AppManager::getBoostVersion() const +{ + return QString::fromUtf8(BOOST_LIB_VERSION); +} + +QString +AppManager::getQtVersion() const +{ + return QString::fromUtf8(QT_VERSION_STR) + QString::fromUtf8(" / ") + QString::fromUtf8( qVersion() ); +} + +QString +AppManager::getCairoVersion() const +{ + return QString::fromUtf8(CAIRO_VERSION_STRING) + QString::fromUtf8(" / ") + QString::fromUtf8( cairo_version_string() ); +} + +QString +AppManager::getPySideVersion() const +{ + return QString::fromUtf8(SHIBOKEN_VERSION); +} + const NATRON_NAMESPACE::OfxHost* AppManager::getOFXHost() const { diff --git a/Engine/AppManager.h b/Engine/AppManager.h index 9ca164e59d..692e83cf39 100644 --- a/Engine/AppManager.h +++ b/Engine/AppManager.h @@ -338,7 +338,8 @@ class AppManager { } - Plugin* registerPlugin(const QStringList & groups, + Plugin* registerPlugin(const QString& resourcesPath, + const QStringList & groups, const QString & pluginID, const QString & pluginLabel, const QString & pluginIconPath, @@ -512,6 +513,20 @@ class AppManager void setPluginsUseInputImageCopyToRender(bool b); bool isCopyInputImageForPluginRenderEnabled() const; + bool hasPlatformNecessaryOpenGLRequirements(QString* missingOpenGLError = 0) const; + + QString getGlfwVersion() const; + + QString getOpenGLVersion() const; + + QString getBoostVersion() const; + + QString getQtVersion() const; + + QString getCairoVersion() const; + + QString getPySideVersion() const; + public Q_SLOTS: void exitAppWithSaveWarning() diff --git a/Engine/AppManagerPrivate.cpp b/Engine/AppManagerPrivate.cpp index 94a8c2d86a..0d79b0593c 100644 --- a/Engine/AppManagerPrivate.cpp +++ b/Engine/AppManagerPrivate.cpp @@ -49,6 +49,7 @@ GCC_DIAG_ON(unused-parameter) #include "Global/QtCompat.h" // for removeRecursively #include "Global/GlobalDefines.h" +#include "Global/GLIncludes.h" #include "Engine/FStreamsSupport.h" #include "Engine/CacheSerialization.h" @@ -111,6 +112,7 @@ AppManagerPrivate::AppManagerPrivate() #endif , natronPythonGIL(QMutex::Recursive) , pluginsUseInputImageCopyToRender(false) + , hasRequiredOpenGLVersionAndExtensions(true) { setMaxCacheFiles(); @@ -516,4 +518,71 @@ AppManagerPrivate::setMaxCacheFiles() maxCacheFiles = hardMax * 0.9; } + +#ifdef DEBUG +static void glfw_error_callback(int error, const char* description) +{ + qDebug() << "GLFW ERROR: " << __FILE__ << __LINE__ << error << description; +} +#endif + +void +AppManagerPrivate::initGlfw() +{ + + + assert(QThread::currentThread() == qApp->thread()); + +#ifdef DEBUG + // May be called before glfwInit() + glfwSetErrorCallback(glfw_error_callback); +#endif + + hasRequiredOpenGLVersionAndExtensions = true; + + if (glfwInit() != GL_TRUE) { + hasRequiredOpenGLVersionAndExtensions = false; + missingOpenglError = QObject::tr("Failed to initialize OpenGL requirements of the GLFW library on your platform."); + return; + } + + +#ifdef GL_TRACE_CALLS + glad_set_pre_callback(pre_gl_call); + glad_set_post_callback(post_gl_call); +#endif + + // gladLoadGLLoader needs a window, so create a hidden one + + glfwWindowHint(GLFW_VISIBLE, GL_FALSE); + GLFWwindow* offscreen_window = glfwCreateWindow(200, 200 , "", NULL, NULL); + if (!offscreen_window) { + hasRequiredOpenGLVersionAndExtensions = false; + missingOpenglError = QObject::tr("Failed to create GLFW window."); + return; + } + + glfwMakeContextCurrent(offscreen_window); + + + if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) { + missingOpenglError = QObject::tr("Failed to load required OpenGL functions. " NATRON_APPLICATION_NAME " requires at least OpenGL 2.0 with the following extensions: "); + missingOpenglError += QString::fromUtf8("GL_ARB_vertex_buffer_object,GL_ARB_pixel_buffer_object,GL_ARB_vertex_array_object,GL_ARB_framebuffer_object,GL_ARB_texture_float"); + missingOpenglError += QLatin1String("\n"); + missingOpenglError += QObject::tr("Your OpenGL version "); + missingOpenglError += appPTR->getOpenGLVersion(); + hasRequiredOpenGLVersionAndExtensions = false; + return; + } + + // OpenGL is now read to be used! just include "Global/GLIncludes.h" +} + +void +AppManagerPrivate::teardownGlfw() +{ + // Terminates GLFW, clearing any resources allocated by GLFW. + glfwTerminate(); +} + NATRON_NAMESPACE_EXIT; diff --git a/Engine/AppManagerPrivate.h b/Engine/AppManagerPrivate.h index 25e515af3e..837d392bbd 100644 --- a/Engine/AppManagerPrivate.h +++ b/Engine/AppManagerPrivate.h @@ -137,6 +137,10 @@ struct AppManagerPrivate // Copy of the setting knob for faster access from OfxImage constructor bool pluginsUseInputImageCopyToRender; + // True if we can use OpenGL + bool hasRequiredOpenGLVersionAndExtensions; + QString missingOpenglError; + AppManagerPrivate(); ~AppManagerPrivate(); @@ -167,6 +171,10 @@ struct AppManagerPrivate void createBreakpadHandler(const QString& breakpadPipePath, int breakpad_client_fd); #endif + + void initGlfw(); + + void teardownGlfw(); }; NATRON_NAMESPACE_EXIT; diff --git a/Engine/EffectInstance.cpp b/Engine/EffectInstance.cpp index c4e3669441..c775d45fa1 100644 --- a/Engine/EffectInstance.cpp +++ b/Engine/EffectInstance.cpp @@ -81,6 +81,7 @@ GCC_DIAG_UNUSED_LOCAL_TYPEDEFS_ON #include "Engine/Settings.h" #include "Engine/Timer.h" #include "Engine/Transform.h" +#include "Engine/UndoCommand.h" #include "Engine/ViewIdx.h" #include "Engine/ViewerInstance.h" @@ -2908,10 +2909,19 @@ EffectInstance::onKnobSlaved(const KnobPtr& slave, void EffectInstance::setCurrentViewportForOverlays_public(OverlaySupport* viewport) { + assert(QThread::currentThread() == qApp->thread()); getNode()->setCurrentViewportForHostOverlays(viewport); + _imp->overlaysViewport = viewport; setCurrentViewportForOverlays(viewport); } +OverlaySupport* +EffectInstance::getCurrentViewportForOverlays() const +{ + assert(QThread::currentThread() == qApp->thread()); + return _imp->overlaysViewport; +} + void EffectInstance::setDoingInteractAction(bool doing) { @@ -2950,7 +2960,9 @@ EffectInstance::onOverlayPenDown_public(double time, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, - double pressure) + double pressure, + double timestamp, + PenType pen) { ///cannot be run in another thread assert( QThread::currentThread() == qApp->thread() ); @@ -2969,7 +2981,7 @@ EffectInstance::onOverlayPenDown_public(double time, { NON_RECURSIVE_ACTION(); _imp->setDuringInteractAction(true); - ret = onOverlayPenDown(time, actualScale, view, viewportPos, pos, pressure); + ret = onOverlayPenDown(time, actualScale, view, viewportPos, pos, pressure, timestamp, pen); if (!ret) { ret |= getNode()->onOverlayPenDownDefault(time, actualScale, view, viewportPos, pos, pressure); } @@ -2980,13 +2992,45 @@ EffectInstance::onOverlayPenDown_public(double time, return ret; } +bool +EffectInstance::onOverlayPenDoubleClicked_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos) +{ + ///cannot be run in another thread + assert( QThread::currentThread() == qApp->thread() ); + if ( !hasOverlay() && !getNode()->hasHostOverlay() ) { + return false; + } + + RenderScale actualScale; + if ( !canHandleRenderScaleForOverlays() ) { + actualScale.x = actualScale.y = 1.; + } else { + actualScale = renderScale; + } + + bool ret; + { + NON_RECURSIVE_ACTION(); + _imp->setDuringInteractAction(true); + ret = onOverlayPenDoubleClicked(time, actualScale, view, viewportPos, pos); + if (!ret) { + ret |= getNode()->onOverlayPenDoubleClickedDefault(time, actualScale, view, viewportPos, pos); + } + _imp->setDuringInteractAction(false); + } + checkIfRenderNeeded(); + + return ret; +} + bool EffectInstance::onOverlayPenMotion_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, - double pressure) + double pressure, + double timestamp) { ///cannot be run in another thread assert( QThread::currentThread() == qApp->thread() ); @@ -3004,7 +3048,7 @@ EffectInstance::onOverlayPenMotion_public(double time, NON_RECURSIVE_ACTION(); _imp->setDuringInteractAction(true); - bool ret = onOverlayPenMotion(time, actualScale, view, viewportPos, pos, pressure); + bool ret = onOverlayPenMotion(time, actualScale, view, viewportPos, pos, pressure, timestamp); if (!ret) { ret |= getNode()->onOverlayPenMotionDefault(time, actualScale, view, viewportPos, pos, pressure); } @@ -3021,7 +3065,8 @@ EffectInstance::onOverlayPenUp_public(double time, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, - double pressure) + double pressure, + double timestamp) { ///cannot be run in another thread assert( QThread::currentThread() == qApp->thread() ); @@ -3040,7 +3085,7 @@ EffectInstance::onOverlayPenUp_public(double time, { NON_RECURSIVE_ACTION(); _imp->setDuringInteractAction(true); - ret = onOverlayPenUp(time, actualScale, view, viewportPos, pos, pressure); + ret = onOverlayPenUp(time, actualScale, view, viewportPos, pos, pressure, timestamp); if (!ret) { ret |= getNode()->onOverlayPenUpDefault(time, actualScale, view, viewportPos, pos, pressure); } @@ -4053,11 +4098,52 @@ EffectInstance::redrawOverlayInteract() RenderScale EffectInstance::getOverlayInteractRenderScale() const { + RenderScale renderScale(1.); + + if (isDoingInteractAction() && _imp->overlaysViewport) { + unsigned int mmLevel = _imp->overlaysViewport->getCurrentRenderScale(); + renderScale.x = renderScale.y = 1 << mmLevel; + } + + return renderScale; + RenderScale r(1.); return r; } +void +EffectInstance::pushUndoCommand(UndoCommand* command) +{ + UndoCommandPtr ptr(command); + getNode()->pushUndoCommand(ptr); +} + +void +EffectInstance::pushUndoCommand(const UndoCommandPtr& command) +{ + getNode()->pushUndoCommand(command); +} + +bool +EffectInstance::setCurrentCursor(CursorEnum defaultCursor) +{ + if (!isDoingInteractAction()) { + return false; + } + getNode()->setCurrentCursor(defaultCursor); + return true; +} + +bool +EffectInstance::setCurrentCursor(const QString& customCursorFilePath) +{ + if (!isDoingInteractAction()) { + return false; + } + return getNode()->setCurrentCursor(customCursorFilePath); +} + void EffectInstance::addOverlaySlaveParam(const boost::shared_ptr& knob) { diff --git a/Engine/EffectInstance.h b/Engine/EffectInstance.h index 7458e063ba..9910fd0429 100644 --- a/Engine/EffectInstance.h +++ b/Engine/EffectInstance.h @@ -46,6 +46,7 @@ #include "Engine/RenderStats.h" #include "Engine/EngineFwd.h" #include "Engine/ParallelRenderArgs.h" +#include "Engine/PluginActionShortcut.h" #include "Engine/ViewIdx.h" // Various useful plugin IDs, @see EffectInstance::getPluginID() @@ -488,6 +489,14 @@ class EffectInstance **/ virtual std::string getPluginDescription() const WARN_UNUSED_RETURN = 0; + /** + * @brief Must returns the shortcuts that are going to be used for this plug-in. Each shortcut + * will be added to the shortcut editor and must have an ID and a description label. + * Make sure that within the same plug-in there are no conflicting shortcuts. + * Each shortcut ID can then be set to KnobButton used to indicate they have a shortcut. + **/ + virtual void getPluginShortcuts(std::list* /*shortcuts*/) {} + /** * @bried Returns the effect render order preferences: @@ -775,7 +784,7 @@ class EffectInstance return false; } - virtual RenderScale getOverlayInteractRenderScale() const; + RenderScale getOverlayInteractRenderScale() const; SequenceTime getFrameRenderArgsCurrentTime() const; @@ -1043,6 +1052,8 @@ class EffectInstance void setCurrentViewportForOverlays_public(OverlaySupport* viewport); + OverlaySupport* getCurrentViewportForOverlays() const; + protected: virtual void setCurrentViewportForOverlays(OverlaySupport* /*viewport*/) @@ -1434,6 +1445,25 @@ class EffectInstance public: + /** + * @brief Push a new undo command to the undo/redo stack associated to this node. + * The stack takes ownership of the shared pointer, so you should not hold a strong reference to the passed pointer. + * If no undo/redo stack is present, the command will just be redone once then destroyed. + **/ + void pushUndoCommand(const UndoCommandPtr& command); + + // Same as the version above, do NOT derefence command after this call as it will be destroyed already, usually this call is + // made as such: pushUndoCommand(new MyCommand(...)) + void pushUndoCommand(UndoCommand* command); + + /** + * @brief Set the cursor to be one of the default cursor. + * @returns True if it successfully set the cursor, false otherwise. + * Note: this can only be called during an overlay interact action. + **/ + bool setCurrentCursor(CursorEnum defaultCursor); + bool setCurrentCursor(const QString& customCursorFilePath); + ///Doesn't do anything, instead we overriden onKnobValueChanged_public virtual void onKnobValueChanged(KnobI* k, ValueChangedReasonEnum reason, @@ -1456,11 +1486,13 @@ class EffectInstance void drawOverlay_public(double time, const RenderScale & renderScale, ViewIdx view); - bool onOverlayPenDown_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) WARN_UNUSED_RETURN; + bool onOverlayPenDown_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, PenType pen) WARN_UNUSED_RETURN; - bool onOverlayPenMotion_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) WARN_UNUSED_RETURN; + bool onOverlayPenMotion_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) WARN_UNUSED_RETURN; - bool onOverlayPenUp_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) WARN_UNUSED_RETURN; + bool onOverlayPenDoubleClicked_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos) WARN_UNUSED_RETURN; + + bool onOverlayPenUp_public(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) WARN_UNUSED_RETURN; bool onOverlayKeyDown_public(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) WARN_UNUSED_RETURN; @@ -1474,6 +1506,10 @@ class EffectInstance virtual bool isDoingInteractAction() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual void onInteractViewportSelectionCleared() {} + + virtual void onInteractViewportSelectionUpdated(const RectD& /*rectangle*/, bool /*onRelease*/) {} + void setDoingInteractAction(bool doing); /* @brief Overlay support: @@ -1763,7 +1799,18 @@ class EffectInstance ViewIdx /*view*/, const QPointF & /*viewportPos*/, const QPointF & /*pos*/, - double /*pressure*/) WARN_UNUSED_RETURN + double /*pressure*/, + double /*timestamp*/, + PenType /*pen*/) WARN_UNUSED_RETURN + { + return false; + } + + virtual bool onOverlayPenDoubleClicked(double /*time*/, + const RenderScale & /*renderScale*/, + ViewIdx /*view*/, + const QPointF & /*viewportPos*/, + const QPointF & /*pos*/) WARN_UNUSED_RETURN { return false; } @@ -1773,7 +1820,7 @@ class EffectInstance ViewIdx /*view*/, const QPointF & /*viewportPos*/, const QPointF & /*pos*/, - double /*pressure*/) WARN_UNUSED_RETURN + double /*pressure*/, double /*timestamp*/) WARN_UNUSED_RETURN { return false; } @@ -1783,7 +1830,7 @@ class EffectInstance ViewIdx /*view*/, const QPointF & /*viewportPos*/, const QPointF & /*pos*/, - double /*pressure*/) WARN_UNUSED_RETURN + double /*pressure*/, double /*timestamp*/) WARN_UNUSED_RETURN { return false; } diff --git a/Engine/EffectInstancePrivate.cpp b/Engine/EffectInstancePrivate.cpp index 13be6b4774..c1f1aa13bc 100644 --- a/Engine/EffectInstancePrivate.cpp +++ b/Engine/EffectInstancePrivate.cpp @@ -340,6 +340,7 @@ EffectInstance::Implementation::Implementation(EffectInstance* publicInterface) , metadatasMutex() , metadatas() , runningClipPreferences(false) + , overlaysViewport(0) { } diff --git a/Engine/EffectInstancePrivate.h b/Engine/EffectInstancePrivate.h index 0e3d6e2fdd..0f6c75404f 100644 --- a/Engine/EffectInstancePrivate.h +++ b/Engine/EffectInstancePrivate.h @@ -194,6 +194,8 @@ struct EffectInstance::Implementation NodeMetadata metadatas; bool runningClipPreferences; //only used on main thread + // set during interact actions on main-thread + OverlaySupport* overlaysViewport; void runChangedParamCallback(KnobI* k, bool userEdited, const std::string & callback); diff --git a/Engine/Engine.pro b/Engine/Engine.pro index 046b8d5ca8..dac4f54ed0 100644 --- a/Engine/Engine.pro +++ b/Engine/Engine.pro @@ -20,7 +20,7 @@ TARGET = Engine TEMPLATE = lib CONFIG += staticlib CONFIG += moc -CONFIG += boost qt cairo python shiboken pyside +CONFIG += boost qt cairo python shiboken pyside glfw QT += core network greaterThan(QT_MAJOR_VERSION, 4): QT += concurrent @@ -29,7 +29,7 @@ greaterThan(QT_MAJOR_VERSION, 4): QT += concurrent # Do not uncomment the following: pyside requires QtGui, because PySide/QtCore/pyside_qtcore_python.h includes qtextdocument.h #QT -= gui -CONFIG += libmv-flags openmvg-flags +CONFIG += libmv-flags openmvg-flags glad-flags include(../global.pri) @@ -156,12 +156,15 @@ SOURCES += \ RotoItem.cpp \ RotoLayer.cpp \ RotoPaint.cpp \ + RotoPaintInteract.cpp \ RotoSmear.cpp \ RotoStrokeItem.cpp \ + RotoUndoCommand.cpp \ ScriptObject.cpp \ Settings.cpp \ StandardPaths.cpp \ StringAnimationManager.cpp \ + Texture.cpp \ TextureRect.cpp \ ThreadPool.cpp \ TimeLine.cpp \ @@ -171,10 +174,12 @@ SOURCES += \ TrackerFrameAccessor.cpp \ TrackMarker.cpp \ TrackerNode.cpp \ + TrackerNodeInteract.cpp \ TLSHolder.cpp \ Transform.cpp \ ViewerInstance.cpp \ WriteNode.cpp \ + ../Global/glad_source.c \ ../Global/ProcInfo.cpp \ ../libs/SequenceParsing/SequenceParsing.cpp \ NatronEngine/natronengine_module_wrapper.cpp \ @@ -315,6 +320,7 @@ HEADERS += \ OverlaySupport.h \ ParallelRenderArgs.h \ Plugin.h \ + PluginActionShortcut.h \ PluginMemory.h \ PrecompNode.h \ ProcessHandler.h \ @@ -345,15 +351,18 @@ HEADERS += \ RotoItem.h \ RotoItemSerialization.h \ RotoPaint.h \ + RotoPaintInteract.h \ RotoPoint.h \ RotoSmear.h \ RotoStrokeItem.h \ RotoStrokeItemSerialization.h \ + RotoUndoCommand.h \ ScriptObject.h \ Settings.h \ Singleton.h \ StandardPaths.h \ StringAnimationManager.h \ + Texture.h \ TextureRect.h \ TextureRectSerialization.h \ ThreadStorage.h \ @@ -365,12 +374,14 @@ HEADERS += \ TrackerContextPrivate.h \ TrackerFrameAccessor.h \ TrackerNode.h \ + TrackerNodeInteract.h \ TrackMarker.h \ TrackerSerialization.h \ TLSHolder.h \ TLSHolderImpl.h \ Transform.h \ UpdateViewerParams.h \ + UndoCommand.h \ Variant.h \ VariantSerialization.h \ ViewerInstance.h \ @@ -379,6 +390,7 @@ HEADERS += \ WriteNode.h \ ../Global/Enums.h \ ../Global/GitVersion.h \ + ../Global/glad_include.h \ ../Global/GLIncludes.h \ ../Global/GlobalDefines.h \ ../Global/KeySymbols.h \ diff --git a/Engine/EngineFwd.h b/Engine/EngineFwd.h index a20d25974a..23767b6e1b 100644 --- a/Engine/EngineFwd.h +++ b/Engine/EngineFwd.h @@ -186,6 +186,7 @@ class RenderStats; class RenderingFlagSetter; class RequestedFrame; class RotoContext; +class RotoPaint; class RotoDrawableItem; class RotoItem; class RotoItemSerialization; @@ -195,6 +196,7 @@ class RotoStrokeItem; class Settings; class StringAnimationManager; class TLSHolderBase; +class Texture; class TextureRect; class TimeLine; class TrackArgs; @@ -204,6 +206,7 @@ class TrackMarker; class TrackerContext; class TrackSerialization; class TrackerContextSerialization; +class UndoCommand; class ViewerInstance; class ViewIdx; namespace Color { @@ -248,6 +251,7 @@ class UserParamHolder; NATRON_PYTHON_NAMESPACE_EXIT; +typedef boost::shared_ptr UndoCommandPtr; typedef boost::shared_ptr AbortableRenderInfoPtr; typedef boost::shared_ptr TrackMarkerPtr; typedef boost::shared_ptr NodePtr; @@ -262,6 +266,7 @@ typedef boost::weak_ptr EffectInstWPtr; typedef boost::weak_ptr ImageWPtr; typedef boost::shared_ptr ImagePtr; typedef std::list ImageList; +typedef boost::shared_ptr GLTexturePtr; typedef boost::shared_ptr FrameEntryPtr; NATRON_NAMESPACE_EXIT; diff --git a/Engine/Knob.cpp b/Engine/Knob.cpp index 3df8de7b5e..20cb2516dc 100644 --- a/Engine/Knob.cpp +++ b/Engine/Knob.cpp @@ -249,10 +249,12 @@ struct KnobHelperPrivate int itemSpacing; // If this knob is supposed to be visible in the Viewer UI, this is the index at which it should be positioned - int inViewerContextIndex; int inViewerContextAddSeparator; int inViewerContextItemSpacing; int inViewerContextAddNewLine; + std::string inViewerContextLabel; + bool inViewerContextHasShortcut; + boost::weak_ptr parentKnob; mutable QMutex stateMutex; // protects IsSecret defaultIsSecret enabled @@ -335,10 +337,11 @@ struct KnobHelperPrivate , newLine(true) , addSeparator(false) , itemSpacing(0) - , inViewerContextIndex(-1) , inViewerContextAddSeparator(false) , inViewerContextItemSpacing(1) , inViewerContextAddNewLine(false) + , inViewerContextLabel() + , inViewerContextHasShortcut(false) , parentKnob() , stateMutex() , IsSecret(false) @@ -1724,16 +1727,31 @@ KnobHelper::setSpacingBetweenItems(int spacing) _imp->itemSpacing = spacing; } + +std::string +KnobHelper::getInViewerContextLabel() const +{ + QMutexLocker k(&_imp->labelMutex); + return _imp->inViewerContextLabel; +} + void -KnobHelper::setInViewerContextIndex(int index) +KnobHelper::setInViewerContextLabel(const std::string& label) { - _imp->inViewerContextIndex = index; + QMutexLocker k(&_imp->labelMutex); + _imp->inViewerContextLabel = label; } -int -KnobHelper::getInViewerContextIndex() const +void +KnobHelper::setInViewerContextCanHaveShortcut(bool haveShortcut) +{ + _imp->inViewerContextHasShortcut = haveShortcut; +} + +bool +KnobHelper::getInViewerContextHasShortcut() const { - return _imp->inViewerContextIndex; + return _imp->inViewerContextHasShortcut; } void @@ -3158,6 +3176,32 @@ KnobHelper::getBackgroundColour(double &r, } } +RectD +KnobHelper::getViewportRect() const +{ + boost::shared_ptr hasGui = getKnobGuiPointer(); + + if (hasGui) { + return hasGui->getViewportRect(); + } else { + return RectD(); + } +} + +void +KnobHelper::getCursorPosition(double& x, double& y) const +{ + boost::shared_ptr hasGui = getKnobGuiPointer(); + + if (hasGui) { + return hasGui->getCursorPosition(x, y); + } else { + x = 0; + y = 0; + } + +} + void KnobHelper::saveOpenGLContext() { @@ -4383,7 +4427,7 @@ struct KnobHolder::KnobHolderPrivate bool knobsInitialized; bool isInitializingKnobs; bool isSlave; - + std::vector knobsWithViewerUI; ///Count how many times an overlay needs to be redrawn for the instanceChanged/penMotion/penDown etc... actions ///to just redraw it once when the recursion level is back to 0 @@ -4459,17 +4503,25 @@ KnobHolder::~KnobHolder() } } -bool -KnobHolder::hasKnobWithViewerInContextUI() const +void +KnobHolder::addKnobToViewerUI(const KnobPtr& knob) { - QMutexLocker k(&_imp->knobsMutex); - for (KnobsVec::const_iterator it = _imp->knobs.begin(); it != _imp->knobs.end(); ++it) { - int index = (*it)->getInViewerContextIndex(); - if (index != -1) { - return true; + assert(QThread::currentThread() == qApp->thread()); + _imp->knobsWithViewerUI.push_back(knob); +} + +KnobsVec +KnobHolder::getViewerUIKnobs() const +{ + assert(QThread::currentThread() == qApp->thread()); + KnobsVec ret; + for (std::vector::const_iterator it = _imp->knobsWithViewerUI.begin(); it!=_imp->knobsWithViewerUI.end(); ++it) { + KnobPtr k = it->lock(); + if (k) { + ret.push_back(k); } } - return false; + return ret; } void @@ -5689,12 +5741,10 @@ KnobHolder::restoreDefaultValues() for (U32 i = 0; i < _imp->knobs.size(); ++i) { KnobButton* isBtn = dynamic_cast( _imp->knobs[i].get() ); - KnobPage* isPage = dynamic_cast( _imp->knobs[i].get() ); - KnobGroup* isGroup = dynamic_cast( _imp->knobs[i].get() ); KnobSeparator* isSeparator = dynamic_cast( _imp->knobs[i].get() ); ///Don't restore buttons and the node label - if ( !isBtn && !isPage && !isGroup && !isSeparator && (_imp->knobs[i]->getName() != kUserLabelKnobName) ) { + if ( (!isBtn || isBtn->getIsCheckable()) && !isSeparator && (_imp->knobs[i]->getName() != kUserLabelKnobName) ) { for (int d = 0; d < _imp->knobs[i]->getDimension(); ++d) { _imp->knobs[i]->resetToDefaultValue(d); } diff --git a/Engine/Knob.h b/Engine/Knob.h index 74725bf8b8..6dcb3c1135 100644 --- a/Engine/Knob.h +++ b/Engine/Knob.h @@ -828,10 +828,18 @@ class KnobI virtual void setSpacingBetweenItems(int spacing) = 0; /** - * @brief Set whether the knob should have a GUI on the viewer, if so set at which index + * @brief Set the label of the knob on the viewer **/ - virtual void setInViewerContextIndex(int index) = 0; - virtual int getInViewerContextIndex() const = 0; + virtual std::string getInViewerContextLabel() const = 0; + virtual void setInViewerContextLabel(const std::string& label) = 0; + + /** + * @brief Determines whether this knob can be assigned a shortcut or not via the shortcut editor. + * If true, Natron will look for a shortcut in the shortcuts database with an ID matching the name of this + * parameter. To set default values for shortcuts, implement EffectInstance::getPluginShortcuts(...) + **/ + virtual void setInViewerContextCanHaveShortcut(bool haveShortcut) = 0; + virtual bool getInViewerContextHasShortcut() const = 0; /** * @brief Returns whether this type of knob can be instantiated in the viewer UI @@ -1022,6 +1030,9 @@ class KnobI virtual void getBackgroundColour(double &r, double &g, double &b) const OVERRIDE = 0; virtual unsigned int getCurrentRenderScale() const OVERRIDE FINAL { return 0; } + virtual RectD getViewportRect() const OVERRIDE = 0; + virtual void getCursorPosition(double& x, double& y) const OVERRIDE = 0; + virtual void saveOpenGLContext() OVERRIDE = 0; virtual void restoreOpenGLContext() OVERRIDE = 0; @@ -1397,8 +1408,10 @@ class KnobHelper virtual bool isNewLineActivated() const OVERRIDE FINAL WARN_UNUSED_RETURN; virtual bool isSeparatorActivated() const OVERRIDE FINAL WARN_UNUSED_RETURN; virtual void setSpacingBetweenItems(int spacing) OVERRIDE FINAL; - virtual void setInViewerContextIndex(int index) OVERRIDE FINAL; - virtual int getInViewerContextIndex() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual std::string getInViewerContextLabel() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual void setInViewerContextLabel(const std::string& label) OVERRIDE FINAL; + virtual void setInViewerContextCanHaveShortcut(bool haveShortcut) OVERRIDE FINAL; + virtual bool getInViewerContextHasShortcut() const OVERRIDE FINAL WARN_UNUSED_RETURN; virtual void setInViewerContextItemSpacing(int spacing) OVERRIDE FINAL; virtual int getInViewerContextItemSpacing() const OVERRIDE FINAL WARN_UNUSED_RETURN; virtual void setInViewerContextAddSeparator(bool addSeparator) OVERRIDE FINAL; @@ -1444,6 +1457,8 @@ class KnobHelper virtual void getViewportSize(double &width, double &height) const OVERRIDE FINAL; virtual void getPixelScale(double & xScale, double & yScale) const OVERRIDE FINAL; virtual void getBackgroundColour(double &r, double &g, double &b) const OVERRIDE FINAL; + virtual RectD getViewportRect() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual void getCursorPosition(double& x, double& y) const OVERRIDE FINAL; virtual void saveOpenGLContext() OVERRIDE FINAL; virtual void restoreOpenGLContext() OVERRIDE FINAL; virtual void setOfxParamHandle(void* ofxParamHandle) OVERRIDE FINAL; @@ -2244,7 +2259,9 @@ class KnobHolder void setIsInitializingKnobs(bool b); bool isInitializingKnobs() const; - bool hasKnobWithViewerInContextUI() const; + void addKnobToViewerUI(const KnobPtr& knob); + KnobsVec getViewerUIKnobs() const; + protected: diff --git a/Engine/KnobGuiI.h b/Engine/KnobGuiI.h index 27cfd9c52e..f20df2d305 100644 --- a/Engine/KnobGuiI.h +++ b/Engine/KnobGuiI.h @@ -62,6 +62,10 @@ class KnobGuiI virtual boost::shared_ptr getCurve(ViewSpec view, int dimension) const = 0; virtual bool getAllDimensionsVisible() const = 0; + virtual RectD getViewportRect() const = 0; + virtual void getCursorPosition(double& x, double& y) const = 0; + + protected: ///Should set to the underlying knob the gui ptr diff --git a/Engine/KnobTypes.cpp b/Engine/KnobTypes.cpp index 93d7e6a53b..9ae82ad4d7 100644 --- a/Engine/KnobTypes.cpp +++ b/Engine/KnobTypes.cpp @@ -537,6 +537,7 @@ KnobButton::KnobButton(KnobHolder* holder, : Knob(holder, label, dimension, declaredByPlugin) , _renderButton(false) , _checkable(false) + , _isToolButtonAction(false) { //setIsPersistant(false); } @@ -566,6 +567,8 @@ KnobButton::trigger() evaluateValueChange(0, getCurrentTime(), ViewIdx(0), eValueChangedReasonUserEdited); } + + /******************************KnobChoice**************************************/ #define KNOBCHOICE_MAX_ENTRIES_HELP 40 // don't show help in the tootlip if there are more entries that this @@ -1381,6 +1384,7 @@ KnobGroup::KnobGroup(KnobHolder* holder, bool declaredByPlugin) : Knob(holder, label, dimension, declaredByPlugin) , _isTab(false) + , _isToolButton(false) { } @@ -1396,6 +1400,18 @@ KnobGroup::isTab() const return _isTab; } +void +KnobGroup::setAsToolButton(bool b) +{ + _isToolButton = b; +} + +bool +KnobGroup::getIsToolButton() const +{ + return _isToolButton; +} + bool KnobGroup::canAnimate() const { @@ -1522,6 +1538,7 @@ KnobPage::KnobPage(KnobHolder* holder, int dimension, bool declaredByPlugin) : Knob(holder, label, dimension, declaredByPlugin) + , _isToolBar(false) { setIsPersistant(false); } diff --git a/Engine/KnobTypes.h b/Engine/KnobTypes.h index 76d6e30cd1..270456309d 100644 --- a/Engine/KnobTypes.h +++ b/Engine/KnobTypes.h @@ -433,6 +433,17 @@ class KnobButton { return _checkable; } + + void setAsToolButtonAction(bool b) + { + _isToolButtonAction = b; + } + + bool getIsToolButtonAction() const + { + return _isToolButtonAction; + } + private: @@ -443,6 +454,7 @@ class KnobButton static const std::string _typeNameStr; bool _renderButton; bool _checkable; + bool _isToolButtonAction; }; /******************************KnobChoice**************************************/ @@ -840,6 +852,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON std::vector< boost::weak_ptr > _children; bool _isTab; + bool _isToolButton; public: @@ -870,6 +883,9 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON bool isTab() const; + void setAsToolButton(bool b); + bool getIsToolButton() const; + static const std::string & typeNameStatic(); private: @@ -878,6 +894,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON virtual const std::string & typeName() const OVERRIDE FINAL; private: + static const std::string _typeNameStr; }; @@ -908,6 +925,14 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON void addKnob(const KnobPtr& k); + void setAsToolBar(bool b) + { + _isToolBar = b; + } + bool getIsToolBar() const + { + return _isToolBar; + } bool moveOneStepUp(KnobI* k); bool moveOneStepDown(KnobI* k); @@ -925,6 +950,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON private: + bool _isToolBar; std::vector< boost::weak_ptr > _children; static const std::string _typeNameStr; }; diff --git a/Engine/Node.cpp b/Engine/Node.cpp index b9d943866a..8e57303acb 100644 --- a/Engine/Node.cpp +++ b/Engine/Node.cpp @@ -87,6 +87,7 @@ GCC_DIAG_UNUSED_LOCAL_TYPEDEFS_ON #include "Engine/TrackMarker.h" #include "Engine/TrackerContext.h" #include "Engine/TLSHolder.h" +#include "Engine/UndoCommand.h" #include "Engine/ViewIdx.h" #include "Engine/ViewerInstance.h" #include "Engine/WriteNode.h" @@ -3077,6 +3078,22 @@ Node::findPluginFormatKnobs() findPluginFormatKnobs(getKnobs(), true); } +void +Node::findRightClickMenuKnob(const KnobsVec& knobs) +{ + + for (std::size_t i = 0; i < knobs.size(); ++i) { + if (knobs[i]->getName() == kNatronOfxParamRightClickMenu) { + KnobPtr rightClickKnob = knobs[i]; + KnobChoice* isChoice = dynamic_cast(rightClickKnob.get()); + if (isChoice) { + QObject::connect(isChoice, SIGNAL(populated()), this, SLOT(rightClickMenuKnobPopulated())); + } + break; + } + } +} + void Node::findPluginFormatKnobs(const KnobsVec & knobs, bool loadingSerialization) @@ -3633,7 +3650,7 @@ Node::initializeDefaultKnobs(int renderScaleSupportPref, const KnobsVec & knobs = _imp->effect->getKnobs(); findPluginFormatKnobs(knobs, loadingSerialization); - + findRightClickMenuKnob(knobs); // Scan all inputs to find masks and get inputs labels //Pair hasMaskChannelSelector, isMask @@ -6245,6 +6262,21 @@ Node::getPluginLabel() const return _imp->effect->getPluginLabel(); } +std::string +Node::getPluginResourcesPath() const +{ + { + QMutexLocker k(&_imp->pluginPythonModuleMutex); + if ( !_imp->pluginPythonModule.empty() ) { + std::size_t foundSlash = _imp->pluginPythonModule.find_last_of("/"); + if (foundSlash != std::string::npos) { + return _imp->pluginPythonModule.substr(0, foundSlash); + } + } + } + return _imp->plugin->getResourcesPath().toStdString(); +} + std::string Node::getPluginDescription() const { @@ -7407,6 +7439,21 @@ Node::onOverlayPenDownDefault(double time, return false; } +bool +Node::onOverlayPenDoubleClickedDefault(double time, + const RenderScale& renderScale, + ViewIdx view, const QPointF & viewportPos, const QPointF & pos) +{ + boost::shared_ptr nodeGui = getNodeGui(); + + if (nodeGui) { + return nodeGui->onOverlayPenDoubleClickedDefault(time, renderScale, view, viewportPos, pos); + } + + return false; +} + + bool Node::onOverlayPenMotionDefault(double time, const RenderScale& renderScale, @@ -7761,6 +7808,41 @@ Node::hasHostOverlay() const return false; } +void +Node::pushUndoCommand(const UndoCommandPtr& command) +{ + boost::shared_ptr nodeGui = getNodeGui(); + + if (nodeGui) { + nodeGui->pushUndoCommand(command); + } else { + command->redo(); + } +} + + +void +Node::setCurrentCursor(CursorEnum defaultCursor) +{ + boost::shared_ptr nodeGui = getNodeGui(); + + if (nodeGui) { + nodeGui->setCurrentCursor(defaultCursor); + } +} + +bool +Node::setCurrentCursor(const QString& customCursorFilePath) +{ + boost::shared_ptr nodeGui = getNodeGui(); + + if (nodeGui) { + return nodeGui->setCurrentCursor(customCursorFilePath); + } + return false; +} + + void Node::setCurrentViewportForHostOverlays(OverlaySupport* viewPort) { diff --git a/Engine/Node.h b/Engine/Node.h index bc4b586729..33dcd0862a 100644 --- a/Engine/Node.h +++ b/Engine/Node.h @@ -51,7 +51,6 @@ CLANG_DIAG_ON(deprecated) #include "Engine/ViewIdx.h" #include "Engine/EngineFwd.h" - #define NATRON_PARAMETER_PAGE_NAME_EXTRA "Node" #define NATRON_PARAMETER_PAGE_NAME_INFO "Info" @@ -634,6 +633,11 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON **/ std::string getPluginLabel() const; + /** + * @brief Returns the path to where the resources are stored for this plug-in + **/ + std::string getPluginResourcesPath() const; + void getPluginGrouping(std::list* grouping) const; /** @@ -954,6 +958,8 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON void findPluginFormatKnobs(const KnobsVec & knobs, bool loadingSerialization); + void findRightClickMenuKnob(const KnobsVec& knobs); + void createNodePage(const boost::shared_ptr& settingsPage, int renderScaleSupportPref); void createInfoPage(); @@ -1078,6 +1084,22 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON bool canHandleRenderScaleForOverlays() const; + /** + * @brief Push a new undo command to the undo/redo stack associated to this node. + * The stack takes ownership of the shared pointer, so you should not hold a strong reference to the passed pointer. + * If no undo/redo stack is present, the command will just be redone once then destroyed. + **/ + void pushUndoCommand(const UndoCommandPtr& command); + + + /** + * @brief Set the cursor to be one of the default cursor. + * @returns True if it successfully set the cursor, false otherwise. + * Note: this can only be called during an overlay interact action. + **/ + void setCurrentCursor(CursorEnum defaultCursor); + bool setCurrentCursor(const QString& customCursorFilePath); + bool shouldDrawOverlay() const; @@ -1089,6 +1111,11 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON const RenderScale& renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) WARN_UNUSED_RETURN; + bool onOverlayPenDoubleClickedDefault(double time, + const RenderScale& renderScale, + ViewIdx view, const QPointF & viewportPos, const QPointF & pos) WARN_UNUSED_RETURN; + + bool onOverlayPenMotionDefault(double time, const RenderScale& renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) WARN_UNUSED_RETURN; @@ -1282,6 +1309,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON public Q_SLOTS: + void onRefreshIdentityStateRequestReceived(); void setKnobsAge(U64 newAge); @@ -1321,6 +1349,8 @@ public Q_SLOTS: Q_SIGNALS: + void rightClickMenuKnobPopulated(); + void s_refreshPreviewsAfterProjectLoadRequested(); void hideInputsKnobChanged(bool hidden); diff --git a/Engine/NodeGuiI.h b/Engine/NodeGuiI.h index f17451f55f..84061fbd27 100644 --- a/Engine/NodeGuiI.h +++ b/Engine/NodeGuiI.h @@ -30,6 +30,7 @@ #include "Global/Enums.h" #include "Engine/HostOverlaySupport.h" #include "Engine/EngineFwd.h" +#include "Engine/UndoCommand.h" NATRON_NAMESPACE_ENTER; @@ -110,6 +111,10 @@ class NodeGuiI virtual bool onOverlayPenDownDefault(double time, const RenderScale& renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) = 0; + virtual bool onOverlayPenDoubleClickedDefault(double time, + const RenderScale& renderScale, + ViewIdx view, const QPointF & viewportPos, const QPointF & pos) = 0; + virtual bool onOverlayPenMotionDefault(double time, const RenderScale& renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) = 0; @@ -141,6 +146,21 @@ class NodeGuiI virtual bool isUserSelected() const = 0; virtual void restoreStateAfterCreation() = 0; virtual void onIdentityStateChanged(int inputNb) = 0; + + /** + * @brief Push a new undo command to the undo/redo stack associated to this node. + * The stack takes ownership of the shared pointer, so you should not hold a strong reference to the passed pointer. + * If no undo/redo stack is present, the command will just be redone once then destroyed. + **/ + virtual void pushUndoCommand(const UndoCommandPtr& command) = 0; + + /** + * @brief Set the cursor to be one of the default cursor. + * @returns True if it successfully set the cursor, false otherwise. + * Note: this can only be called during an overlay interact action. + **/ + virtual void setCurrentCursor(CursorEnum defaultCursor) = 0; + virtual bool setCurrentCursor(const QString& customCursorFilePath) = 0; }; NATRON_NAMESPACE_EXIT; diff --git a/Engine/OfxEffectInstance.cpp b/Engine/OfxEffectInstance.cpp index a91a7a09ae..9f12f0c545 100644 --- a/Engine/OfxEffectInstance.cpp +++ b/Engine/OfxEffectInstance.cpp @@ -1967,7 +1967,9 @@ OfxEffectInstance::onOverlayPenDown(double time, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, - double pressure) + double pressure, + double /*timestamp*/, + PenType /*pen*/) { if (!_imp->initialized) { return false; @@ -2007,7 +2009,8 @@ OfxEffectInstance::onOverlayPenMotion(double time, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, - double pressure) + double pressure, + double /*timestamp*/) { if (!_imp->initialized) { return false; @@ -2043,7 +2046,8 @@ OfxEffectInstance::onOverlayPenUp(double time, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, - double pressure) + double pressure, + double /*timestamp*/) { if (!_imp->initialized) { return false; @@ -2215,20 +2219,6 @@ OfxEffectInstance::redrawOverlayInteract() Q_UNUSED(st); } -RenderScale -OfxEffectInstance::getOverlayInteractRenderScale() const -{ - RenderScale renderScale(1.); - - if (isDoingInteractAction() && _imp->overlayInteract) { - OverlaySupport* lastInteract = _imp->overlayInteract->getLastCallingViewport(); - assert(lastInteract); - unsigned int mmLevel = lastInteract->getCurrentRenderScale(); - renderScale.x = renderScale.y = 1 << mmLevel; - } - - return renderScale; -} std::string OfxEffectInstance::natronValueChangedReasonToOfxValueChangedReason(ValueChangedReasonEnum reason) diff --git a/Engine/OfxEffectInstance.h b/Engine/OfxEffectInstance.h index ad0b122f90..193fd0730e 100644 --- a/Engine/OfxEffectInstance.h +++ b/Engine/OfxEffectInstance.h @@ -171,12 +171,11 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON virtual void initializeOverlayInteract() OVERRIDE FINAL; virtual bool hasOverlay() const OVERRIDE FINAL; virtual void redrawOverlayInteract() OVERRIDE FINAL; - virtual RenderScale getOverlayInteractRenderScale() const OVERRIDE FINAL; virtual void drawOverlay(double time, const RenderScale & renderScale, ViewIdx view) OVERRIDE FINAL; - virtual bool onOverlayPenDown(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenDown(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, PenType pen) OVERRIDE FINAL WARN_UNUSED_RETURN; virtual bool onOverlayPenMotion(double time, const RenderScale & renderScale, ViewIdx view, - const QPointF & viewportPos, const QPointF & pos, double pressure) OVERRIDE FINAL WARN_UNUSED_RETURN; - virtual bool onOverlayPenUp(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) OVERRIDE FINAL WARN_UNUSED_RETURN; + const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenUp(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) OVERRIDE FINAL WARN_UNUSED_RETURN; virtual bool onOverlayKeyDown(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; virtual bool onOverlayKeyUp(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; virtual bool onOverlayKeyRepeat(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; diff --git a/Engine/OfxHost.cpp b/Engine/OfxHost.cpp index a8d25ff48a..12589942bd 100644 --- a/Engine/OfxHost.cpp +++ b/Engine/OfxHost.cpp @@ -312,6 +312,27 @@ OfxHost::setProperties() _properties.setStringProperty(kNatronOfxImageEffectPropChannelSelector, kOfxImageComponentRGBA); _properties.setIntProperty(kNatronOfxImageEffectPropHostMasking, 1); _properties.setIntProperty(kNatronOfxImageEffectPropHostMixing, 1); + + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxArrowCursor, 0); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxUpArrowCursor, 1); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxCrossCursor, 2); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxIBeamCursor, 3); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxWaitCursor, 4); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxBusyCursor, 5); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxForbiddenCursor, 6); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxPointingHandCursor, 7); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxWhatsThisCursor, 8); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxSizeVerCursor, 9); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxSizeHorCursor, 10); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxSizeBDiagCursor, 11); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxSizeFDiagCursor, 12); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxSizeAllCursor, 13); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxSplitVCursor, 14); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxSplitHCursor, 15); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxOpenHandCursor, 16); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxClosedHandCursor, 17); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxBlankCursor, 18); + _properties.setStringProperty(kNatronOfxImageEffectPropDefaultCursors, kNatronOfxDefaultCursor, 19); #endif } // OfxHost::setProperties @@ -816,7 +837,7 @@ OfxHost::loadOFXPlugins(std::mapgetBinary() ); - QString iconPath = QString::fromUtf8( bundlePath.c_str() ) + QString::fromUtf8("/Contents/Resources/"); + QString resourcesPath = QString::fromUtf8( bundlePath.c_str() ) + QString::fromUtf8("/Contents/Resources/"); QString iconFileName; std::string pngIcon; try { @@ -831,11 +852,11 @@ OfxHost::loadOFXPlugins(std::map 0) { - groupIconFilename = iconPath; + groupIconFilename = resourcesPath; // the plugin grouping has no descriptor, just try the default filename. groupIconFilename.append(groups[0]); groupIconFilename.append( QString::fromUtf8(".png") ); @@ -846,7 +867,7 @@ OfxHost::loadOFXPlugins(std::map::const_iterator foundReader = contexts.find(kOfxImageEffectContextReader); std::set::const_iterator foundWriter = contexts.find(kOfxImageEffectContextWriter); const bool isDeprecated = p->getDescriptor().isDeprecated(); - Plugin* natronPlugin = appPTR->registerPlugin( groups, + Plugin* natronPlugin = appPTR->registerPlugin( resourcesPath, + groups, QString::fromUtf8( openfxId.c_str() ), QString::fromUtf8( pluginLabel.c_str() ), iconFileName, diff --git a/Engine/OfxImageEffectInstance.cpp b/Engine/OfxImageEffectInstance.cpp index 62785e5a93..53abe26a31 100644 --- a/Engine/OfxImageEffectInstance.cpp +++ b/Engine/OfxImageEffectInstance.cpp @@ -1249,4 +1249,55 @@ OfxImageEffectInstance::paramChangedByPlugin(OFX::Host::Param::Instance */*param */ } + +bool +OfxImageEffectInstance::ofxCursorToNatronCursor(const std::string &ofxCursor, CursorEnum* cursor) +{ + bool ret = true; + if (ofxCursor == kNatronOfxDefaultCursor) { + *cursor = eCursorDefault; + } else if (ofxCursor == kNatronOfxBlankCursor) { + *cursor = eCursorBlank; + } else if (ofxCursor == kNatronOfxArrowCursor) { + *cursor = eCursorArrow; + } else if (ofxCursor == kNatronOfxUpArrowCursor) { + *cursor = eCursorUpArrow; + } else if (ofxCursor == kNatronOfxCrossCursor) { + *cursor = eCursorCross; + } else if (ofxCursor == kNatronOfxIBeamCursor) { + *cursor = eCursorIBeam; + } else if (ofxCursor == kNatronOfxWaitCursor) { + *cursor = eCursorWait; + } else if (ofxCursor == kNatronOfxBusyCursor) { + *cursor = eCursorBusy; + } else if (ofxCursor == kNatronOfxForbiddenCursor) { + *cursor = eCursorForbidden; + } else if (ofxCursor == kNatronOfxPointingHandCursor) { + *cursor = eCursorPointingHand; + } else if (ofxCursor == kNatronOfxWhatsThisCursor) { + *cursor = eCursorWhatsThis; + } else if (ofxCursor == kNatronOfxSizeVerCursor) { + *cursor = eCursorSizeVer; + } else if (ofxCursor == kNatronOfxSizeHorCursor) { + *cursor = eCursorSizeHor; + } else if (ofxCursor == kNatronOfxSizeBDiagCursor) { + *cursor = eCursorBDiag; + } else if (ofxCursor == kNatronOfxSizeFDiagCursor) { + *cursor = eCursorFDiag; + } else if (ofxCursor == kNatronOfxSizeAllCursor) { + *cursor = eCursorSizeAll; + } else if (ofxCursor == kNatronOfxSplitVCursor) { + *cursor = eCursorSplitV; + } else if (ofxCursor == kNatronOfxSplitHCursor) { + *cursor = eCursorSplitH; + } else if (ofxCursor == kNatronOfxOpenHandCursor) { + *cursor = eCursorOpenHand; + } else if (ofxCursor == kNatronOfxClosedHandCursor) { + *cursor = eCursorClosedHand; + } else { + ret = false; + } + return ret; +} + NATRON_NAMESPACE_EXIT; diff --git a/Engine/OfxImageEffectInstance.h b/Engine/OfxImageEffectInstance.h index 0c5c073e88..6c80ce27ad 100644 --- a/Engine/OfxImageEffectInstance.h +++ b/Engine/OfxImageEffectInstance.h @@ -235,6 +235,8 @@ class OfxImageEffectInstance const std::map& getClips() const; + static bool ofxCursorToNatronCursor(const std::string& ofxCursor, CursorEnum* cursor); + private: boost::weak_ptr _ofxEffectInstance; /* FIXME: OfxImageEffectInstance should be able to work without the node_ // Not easy since every Knob need a valid pointer to a node when diff --git a/Engine/OpenGLViewerI.h b/Engine/OpenGLViewerI.h index b018f48ad2..ace08eeaed 100644 --- a/Engine/OpenGLViewerI.h +++ b/Engine/OpenGLViewerI.h @@ -33,35 +33,11 @@ #include "Engine/OverlaySupport.h" #include "Engine/ViewIdx.h" #include "Engine/EngineFwd.h" -#include "Engine/TextureRect.h" +#include "Engine/Texture.h" NATRON_NAMESPACE_ENTER; -class OpenGLTextureI -{ -public: - - - OpenGLTextureI() - : _texID(0) - { - } - - U32 getTexID() const - { - return _texID; - } - - virtual ~OpenGLTextureI() - { - } - -protected: - - - U32 _texID; -}; class OpenGLViewerI : public OverlaySupport @@ -137,9 +113,9 @@ class OpenGLViewerI int textureIndex, bool isPartialRect, bool isFirstTile, - boost::shared_ptr* texture) = 0; + boost::shared_ptr* texture) = 0; virtual void endTransferBufferFromRAMToGPU(int textureIndex, - const boost::shared_ptr& texture, + const boost::shared_ptr& texture, const ImagePtr& image, int time, const RectD& rod, @@ -178,11 +154,6 @@ class OpenGLViewerI **/ virtual void makeOpenGLcontextCurrent() = 0; - /** - * @brief Must return true if the current platform supports GLSL. - **/ - virtual bool supportsGLSL() const = 0; - /** * @brief Called when the live instance of the viewer node is killed. (i.e: when the node is deleted). @@ -239,6 +210,7 @@ class OpenGLViewerI *@brief To be called if redraw needs to be called now without waiting the end of the event loop **/ virtual void redrawNow() = 0; + }; NATRON_NAMESPACE_EXIT; diff --git a/Engine/OverlaySupport.h b/Engine/OverlaySupport.h index 3fe3eb5488..34e99a0660 100644 --- a/Engine/OverlaySupport.h +++ b/Engine/OverlaySupport.h @@ -26,6 +26,7 @@ // ***** END PYTHON BLOCK ***** #include "Engine/EngineFwd.h" +#include "Engine/RectD.h" NATRON_NAMESPACE_ENTER; @@ -85,6 +86,26 @@ class OverlaySupport * @brief Get the current mipmapLevel applied by the viewer **/ virtual unsigned int getCurrentRenderScale() const = 0; + + /** + * @brief Returns whether the given rectangle in canonical coords is visible in the viewport + **/ + bool isVisibleInViewport(const RectD& rect) const + { + RectD visiblePortion = getViewportRect(); + return visiblePortion.intersects(rect); + } + + /** + * @brief Returns the viewport visible portion in canonical coordinates + **/ + virtual RectD getViewportRect() const = 0; + + /** + * @brief Returns the cursor position in canonical coordinates + **/ + virtual void getCursorPosition(double& x, double& y) const = 0; + }; NATRON_NAMESPACE_EXIT; diff --git a/Engine/Plugin.h b/Engine/Plugin.h index 347a598fb4..7a196a7a12 100644 --- a/Engine/Plugin.h +++ b/Engine/Plugin.h @@ -39,6 +39,7 @@ #include "Global/Enums.h" #include "Engine/EngineFwd.h" +#include "Engine/PluginActionShortcut.h" NATRON_NAMESPACE_ENTER; @@ -151,22 +152,11 @@ class PluginGroupNode } }; -class PluginActionShortcut -{ - std::string actionID; - std::string actionLabel; - - struct ShortCut { - std::list modifiers; - int symbol; - }; - - ShortCut shortcut; -}; class Plugin { LibraryBinary* _binary; + QString _resourcesPath; // Path to the resources (images etc..) of the plug-in, or empty if statically linked (epxected to be found in resources) QString _id; QString _label; QString _iconFilePath; @@ -199,10 +189,13 @@ class Plugin */ std::list _shortcuts; + + public: Plugin() : _binary(NULL) + , _resourcesPath() , _id() , _label() , _iconFilePath() @@ -228,6 +221,7 @@ class Plugin } Plugin(LibraryBinary* binary, + const QString& resourcesPath, const QString & id, const QString & label, const QString & iconFilePath, @@ -240,6 +234,7 @@ class Plugin bool isWriter, bool isDeprecated) : _binary(binary) + , _resourcesPath(resourcesPath) , _id(id) , _label(label) , _iconFilePath(iconFilePath) @@ -262,6 +257,9 @@ class Plugin , _activatedSet(false) , _activated(true) { + if (_resourcesPath.isEmpty()) { + _resourcesPath = QLatin1String(":/Resources/"); + } } ~Plugin(); @@ -275,6 +273,11 @@ class Plugin return _shortcuts; } + const QString& getResourcesPath() const + { + return _resourcesPath; + } + QString getPluginShortcutGroup() const; bool getIsForInternalUseOnly() const { return _isInternalOnly; } diff --git a/Engine/PluginActionShortcut.h b/Engine/PluginActionShortcut.h new file mode 100644 index 0000000000..c354781837 --- /dev/null +++ b/Engine/PluginActionShortcut.h @@ -0,0 +1,60 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * This file is part of Natron , + * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat + * + * Natron is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Natron is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Natron. If not, see + * ***** END LICENSE BLOCK ***** */ + + +#ifndef PLUGINACTIONSHORTCUT_H +#define PLUGINACTIONSHORTCUT_H + +// ***** BEGIN PYTHON BLOCK ***** +// from : +// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." +#include +// ***** END PYTHON BLOCK ***** + +#include "Global/GlobalDefines.h" +#include "Global/KeySymbols.h" +NATRON_NAMESPACE_ENTER; + +struct PluginActionShortcut +{ + std::string actionID; + std::string actionLabel; + + KeyboardModifiers modifiers; + Key key; + + PluginActionShortcut() + : actionID() + , actionLabel() + , modifiers(eKeyboardModifierNone) + , key(Key_Unknown) + { + + } + + PluginActionShortcut(const std::string& id, const std::string& label, Key symbol = Key_Unknown, const KeyboardModifiers& mods = KeyboardModifiers(eKeyboardModifierNone)) + : actionID(id) + , actionLabel(label) + , modifiers(mods) + , key(symbol) + {} +}; + +NATRON_NAMESPACE_EXIT; + +#endif // PLUGINACTIONSHORTCUT_H diff --git a/Engine/RotoPaint.cpp b/Engine/RotoPaint.cpp index ed085aa9ae..241c10114b 100644 --- a/Engine/RotoPaint.cpp +++ b/Engine/RotoPaint.cpp @@ -33,29 +33,34 @@ #include "Engine/Node.h" #include "Engine/NodeGroup.h" #include "Engine/NodeMetadata.h" +#include "Engine/MergingEnum.h" #include "Engine/RotoContext.h" +#include "Engine/Bezier.h" +#include "Engine/BezierCP.h" +#include "Engine/KnobTypes.h" +#include "Engine/RotoStrokeItem.h" #include "Engine/KnobTypes.h" #include "Engine/RotoDrawableItem.h" +#include "Engine/RotoPoint.h" +#include "Engine/RotoUndoCommand.h" +#include "Engine/RotoPaintInteract.h" #include "Engine/TimeLine.h" +#include "Engine/Transform.h" #include "Engine/ViewIdx.h" + +#include "Global/GLIncludes.h" +#include "Global/GlobalDefines.h" + + + #define ROTOPAINT_MASK_INPUT_INDEX 10 NATRON_NAMESPACE_ENTER; -struct RotoPaintPrivate -{ - bool isPaintByDefault; - boost::weak_ptr premultKnob; - boost::weak_ptr enabledKnobs[4]; - RotoPaintPrivate(bool isPaintByDefault) - : isPaintByDefault(isPaintByDefault) - , premultKnob() - , enabledKnobs() - { - } -}; + + std::string RotoPaint::getPluginDescription() const @@ -66,7 +71,7 @@ RotoPaint::getPluginDescription() const RotoPaint::RotoPaint(NodePtr node, bool isPaintByDefault) : EffectInstance(node) - , _imp( new RotoPaintPrivate(isPaintByDefault) ) + , _imp( new RotoPaintPrivate(this, isPaintByDefault) ) { setSupportsRenderScaleMaybe(eSupportsYes); } @@ -220,6 +225,833 @@ RotoPaint::initializeKnobs() premultKnob->setIsMetadataSlave(true); _imp->premultKnob = premultKnob; generalPage->addKnob(premultKnob); + + boost::shared_ptr context = getNode()->getRotoContext(); + assert(context); + QObject::connect( context.get(), SIGNAL(refreshViewerOverlays()), this, SLOT(onRefreshAsked()) ); + QObject::connect( context.get(), SIGNAL(selectionChanged(int)), this, SLOT(onSelectionChanged(int)) ); + QObject::connect( context.get(), SIGNAL(itemLockedChanged(int)), this, SLOT(onCurveLockedChanged(int)) ); + QObject::connect( context.get(), SIGNAL(breakMultiStroke()), this, SLOT(onBreakMultiStrokeTriggered()) ); + + + /// Initializing the viewer interface + boost::shared_ptr autoKeyingEnabled = AppManager::createKnob(this, kRotoUIParamAutoKeyingEnabledLabel); + autoKeyingEnabled->setName(kRotoUIParamAutoKeyingEnabled); + autoKeyingEnabled->setHintToolTip(kRotoUIParamAutoKeyingEnabledHint); + autoKeyingEnabled->setEvaluateOnChange(false); + autoKeyingEnabled->setCheckable(true); + autoKeyingEnabled->setDefaultValue(true); + autoKeyingEnabled->setSecretByDefault(true); + autoKeyingEnabled->setInViewerContextCanHaveShortcut(true); + autoKeyingEnabled->setIconLabel(NATRON_IMAGES_PATH "autoKeyingEnabled.png", true); + autoKeyingEnabled->setIconLabel(NATRON_IMAGES_PATH "autoKeyingDisabled.png", false); + generalPage->addKnob(autoKeyingEnabled); + _imp->ui->autoKeyingEnabledButton = autoKeyingEnabled; + + boost::shared_ptr featherLinkEnabled = AppManager::createKnob(this, kRotoUIParamFeatherLinkEnabledLabel); + featherLinkEnabled->setName(kRotoUIParamFeatherLinkEnabled); + featherLinkEnabled->setCheckable(true); + featherLinkEnabled->setEvaluateOnChange(false); + featherLinkEnabled->setDefaultValue(true); + featherLinkEnabled->setSecretByDefault(true); + featherLinkEnabled->setInViewerContextCanHaveShortcut(true); + featherLinkEnabled->setHintToolTip(kRotoUIParamFeatherLinkEnabledHint); + featherLinkEnabled->setIconLabel(NATRON_IMAGES_PATH "featherLinkEnabled.png", true); + featherLinkEnabled->setIconLabel(NATRON_IMAGES_PATH "featherLinkDisabled.png", false); + generalPage->addKnob(featherLinkEnabled); + _imp->ui->featherLinkEnabledButton = featherLinkEnabled; + + boost::shared_ptr displayFeatherEnabled = AppManager::createKnob(this, kRotoUIParamDisplayFeatherLabel); + displayFeatherEnabled->setName(kRotoUIParamDisplayFeather); + displayFeatherEnabled->setHintToolTip(kRotoUIParamDisplayFeatherHint); + displayFeatherEnabled->setEvaluateOnChange(false); + displayFeatherEnabled->setCheckable(true); + displayFeatherEnabled->setDefaultValue(true); + displayFeatherEnabled->setSecretByDefault(true); + displayFeatherEnabled->setInViewerContextCanHaveShortcut(true); + addOverlaySlaveParam(displayFeatherEnabled); + displayFeatherEnabled->setIconLabel(NATRON_IMAGES_PATH "featherEnabled.png", true); + displayFeatherEnabled->setIconLabel(NATRON_IMAGES_PATH "featherDisabled.png", false); + generalPage->addKnob(displayFeatherEnabled); + _imp->ui->displayFeatherEnabledButton = displayFeatherEnabled; + + boost::shared_ptr stickySelection = AppManager::createKnob(this, kRotoUIParamStickySelectionEnabledLabel); + stickySelection->setName(kRotoUIParamStickySelectionEnabled); + stickySelection->setHintToolTip(kRotoUIParamStickySelectionEnabledHint); + stickySelection->setEvaluateOnChange(false); + stickySelection->setCheckable(true); + stickySelection->setDefaultValue(false); + stickySelection->setSecretByDefault(true); + stickySelection->setInViewerContextCanHaveShortcut(true); + stickySelection->setIconLabel(NATRON_IMAGES_PATH "stickySelectionEnabled.png", true); + stickySelection->setIconLabel(NATRON_IMAGES_PATH "stickySelectionDisabled.png", false); + generalPage->addKnob(stickySelection); + _imp->ui->stickySelectionEnabledButton = stickySelection; + + boost::shared_ptr bboxClickAnywhere = AppManager::createKnob(this, kRotoUIParamStickyBboxLabel); + bboxClickAnywhere->setName(kRotoUIParamStickyBbox); + bboxClickAnywhere->setHintToolTip(kRotoUIParamStickyBboxHint); + bboxClickAnywhere->setEvaluateOnChange(false); + bboxClickAnywhere->setCheckable(true); + bboxClickAnywhere->setDefaultValue(false); + bboxClickAnywhere->setSecretByDefault(true); + bboxClickAnywhere->setInViewerContextCanHaveShortcut(true); + bboxClickAnywhere->setIconLabel(NATRON_IMAGES_PATH "viewer_roiEnabled.png", true); + bboxClickAnywhere->setIconLabel(NATRON_IMAGES_PATH "viewer_roiDisabled.png", false); + generalPage->addKnob(bboxClickAnywhere); + _imp->ui->bboxClickAnywhereButton = bboxClickAnywhere; + + boost::shared_ptr rippleEditEnabled = AppManager::createKnob(this, kRotoUIParamRippleEditLabel); + rippleEditEnabled->setName(kRotoUIParamRippleEdit); + rippleEditEnabled->setHintToolTip(kRotoUIParamRippleEditLabelHint); + rippleEditEnabled->setEvaluateOnChange(false); + rippleEditEnabled->setCheckable(true); + rippleEditEnabled->setDefaultValue(false); + rippleEditEnabled->setSecretByDefault(true); + rippleEditEnabled->setInViewerContextCanHaveShortcut(true); + rippleEditEnabled->setIconLabel(NATRON_IMAGES_PATH "rippleEditEnabled.png", true); + rippleEditEnabled->setIconLabel(NATRON_IMAGES_PATH "rippleEditDisabled.png", false); + generalPage->addKnob(rippleEditEnabled); + _imp->ui->rippleEditEnabledButton = rippleEditEnabled; + + boost::shared_ptr addKeyframe = AppManager::createKnob(this, kRotoUIParamAddKeyFrameLabel); + addKeyframe->setName(kRotoUIParamAddKeyFrame); + addKeyframe->setEvaluateOnChange(false); + addKeyframe->setHintToolTip(kRotoUIParamAddKeyFrameHint); + addKeyframe->setSecretByDefault(true); + addKeyframe->setInViewerContextCanHaveShortcut(true); + addKeyframe->setIconLabel(NATRON_IMAGES_PATH "addKF.png"); + generalPage->addKnob(addKeyframe); + _imp->ui->addKeyframeButton = bboxClickAnywhere; + + boost::shared_ptr removeKeyframe = AppManager::createKnob(this, kRotoUIParamRemoveKeyframeLabel); + removeKeyframe->setName(kRotoUIParamRemoveKeyframe); + removeKeyframe->setHintToolTip(kRotoUIParamRemoveKeyframeHint); + removeKeyframe->setEvaluateOnChange(false); + removeKeyframe->setSecretByDefault(true); + removeKeyframe->setInViewerContextCanHaveShortcut(true); + removeKeyframe->setInViewerContextNewLineActivated(true); + removeKeyframe->setIconLabel(NATRON_IMAGES_PATH "removeKF.png"); + generalPage->addKnob(removeKeyframe); + _imp->ui->removeKeyframeButton = removeKeyframe; + + // RotoPaint + + boost::shared_ptr multiStroke = AppManager::createKnob(this, kRotoUIParamMultiStrokeEnabledLabel); + multiStroke->setName(kRotoUIParamMultiStrokeEnabled); + multiStroke->setHintToolTip(kRotoUIParamMultiStrokeEnabledHint); + multiStroke->setEvaluateOnChange(false); + multiStroke->setDefaultValue(true); + multiStroke->setSecretByDefault(true); + generalPage->addKnob(multiStroke); + _imp->ui->multiStrokeEnabled = multiStroke; + + boost::shared_ptr colorWheel = AppManager::createKnob(this, kRotoUIParamColorWheelLabel, 3); + colorWheel->setName(kRotoUIParamColorWheel); + colorWheel->setHintToolTip(kRotoUIParamColorWheelHint); + colorWheel->setEvaluateOnChange(false); + colorWheel->setDefaultValue(1.,0); + colorWheel->setDefaultValue(1.,1); + colorWheel->setDefaultValue(1.,2); + colorWheel->setSecretByDefault(true); + generalPage->addKnob(colorWheel); + _imp->ui->colorWheelButton = colorWheel; + + boost::shared_ptr blendingModes = AppManager::createKnob(this, kRotoUIParamBlendingOpLabel); + blendingModes->setName(kRotoUIParamBlendingOp); + blendingModes->setHintToolTip(kRotoUIParamBlendingOpHint); + blendingModes->setEvaluateOnChange(false); + blendingModes->setSecretByDefault(true); + { + std::vector choices,helps; + Merge::getOperatorStrings(&choices, &helps); + blendingModes->populateChoices(choices, helps); + } + blendingModes->setDefaultValue((int)eMergeOver); + generalPage->addKnob(blendingModes); + _imp->ui->compositingOperatorChoice = blendingModes; + + + boost::shared_ptr opacityKnob = AppManager::createKnob(this, kRotoUIParamOpacityLabel); + opacityKnob->setName(kRotoUIParamOpacity); + opacityKnob->setHintToolTip(kRotoUIParamOpacityHint); + opacityKnob->setEvaluateOnChange(false); + opacityKnob->setSecretByDefault(true); + opacityKnob->setDefaultValue(1.); + opacityKnob->setMinimum(0.); + opacityKnob->setMaximum(1.); + opacityKnob->disableSlider(); + generalPage->addKnob(opacityKnob); + _imp->ui->opacitySpinbox = opacityKnob; + + boost::shared_ptr pressureOpacity = AppManager::createKnob(this, kRotoUIParamPressureOpacityLabel); + pressureOpacity->setName(kRotoUIParamPressureOpacity); + pressureOpacity->setHintToolTip(kRotoUIParamPressureOpacityHint); + pressureOpacity->setEvaluateOnChange(false); + pressureOpacity->setCheckable(true); + pressureOpacity->setDefaultValue(true); + pressureOpacity->setSecretByDefault(true); + pressureOpacity->setInViewerContextCanHaveShortcut(true); + pressureOpacity->setIconLabel(NATRON_IMAGES_PATH "rotopaint_pressure_on.png", true); + pressureOpacity->setIconLabel(NATRON_IMAGES_PATH "rotopaint_pressure_off.png", false); + generalPage->addKnob(rippleEditEnabled); + _imp->ui->pressureOpacityButton = rippleEditEnabled; + + boost::shared_ptr sizeKnob = AppManager::createKnob(this, kRotoUIParamSizeLabel); + sizeKnob->setName(kRotoUIParamSize); + sizeKnob->setHintToolTip(kRotoUIParamSizeHint); + sizeKnob->setEvaluateOnChange(false); + sizeKnob->setSecretByDefault(true); + sizeKnob->setDefaultValue(25.); + sizeKnob->setMinimum(0.); + sizeKnob->setMaximum(1000.); + sizeKnob->disableSlider(); + generalPage->addKnob(sizeKnob); + _imp->ui->sizeSpinbox = sizeKnob; + + boost::shared_ptr pressureSize = AppManager::createKnob(this, kRotoUIParamPressureSizeLabel); + pressureSize->setName(kRotoUIParamPressureSize); + pressureSize->setHintToolTip(kRotoUIParamPressureSizeHint); + pressureSize->setEvaluateOnChange(false); + pressureSize->setCheckable(true); + pressureSize->setDefaultValue(false); + pressureSize->setSecretByDefault(true); + pressureSize->setInViewerContextCanHaveShortcut(true); + pressureSize->setIconLabel(NATRON_IMAGES_PATH "rotopaint_pressure_on.png", true); + pressureSize->setIconLabel(NATRON_IMAGES_PATH "rotopaint_pressure_off.png", false); + generalPage->addKnob(pressureSize); + _imp->ui->pressureSizeButton = pressureSize; + + boost::shared_ptr hardnessKnob = AppManager::createKnob(this, kRotoUIParamHardnessLabel); + hardnessKnob->setName(kRotoUIParamHardness); + hardnessKnob->setHintToolTip(kRotoUIParamHardnessHint); + hardnessKnob->setEvaluateOnChange(false); + hardnessKnob->setSecretByDefault(true); + hardnessKnob->setDefaultValue(.2); + hardnessKnob->setMinimum(0.); + hardnessKnob->setMaximum(1.); + hardnessKnob->disableSlider(); + generalPage->addKnob(hardnessKnob); + _imp->ui->hardnessSpinbox = hardnessKnob; + + boost::shared_ptr pressureHardness = AppManager::createKnob(this, kRotoUIParamPressureHardnessLabel); + pressureHardness->setName(kRotoUIParamPressureHardness); + pressureHardness->setHintToolTip(kRotoUIParamPressureHardnessHint); + pressureHardness->setEvaluateOnChange(false); + pressureHardness->setCheckable(true); + pressureHardness->setDefaultValue(false); + pressureHardness->setSecretByDefault(true); + pressureHardness->setInViewerContextCanHaveShortcut(true); + pressureHardness->setIconLabel(NATRON_IMAGES_PATH "rotopaint_pressure_on.png", true); + pressureHardness->setIconLabel(NATRON_IMAGES_PATH "rotopaint_pressure_off.png", false); + generalPage->addKnob(pressureHardness); + _imp->ui->pressureHardnessButton = pressureHardness; + + boost::shared_ptr buildUp = AppManager::createKnob(this, kRotoUIParamBuildUpLabel); + buildUp->setName(kRotoUIParamBuildUp); + buildUp->setHintToolTip(kRotoUIParamBuildUpHint); + buildUp->setEvaluateOnChange(false); + buildUp->setCheckable(true); + buildUp->setDefaultValue(true); + buildUp->setSecretByDefault(true); + buildUp->setInViewerContextCanHaveShortcut(true); + buildUp->setIconLabel(NATRON_IMAGES_PATH "rotopaint_buildup_on.png", true); + buildUp->setIconLabel(NATRON_IMAGES_PATH "rotopaint_buildup_off.png", false); + generalPage->addKnob(buildUp); + _imp->ui->buildUpButton = buildUp; + + boost::shared_ptr effectStrength = AppManager::createKnob(this, kRotoUIParamEffectLabel); + effectStrength->setName(kRotoUIParamEffect); + effectStrength->setHintToolTip(kRotoUIParamEffectHint); + effectStrength->setEvaluateOnChange(false); + effectStrength->setSecretByDefault(true); + effectStrength->setDefaultValue(15); + effectStrength->setMinimum(0.); + effectStrength->setMaximum(100.); + effectStrength->disableSlider(); + generalPage->addKnob(effectStrength); + _imp->ui->effectSpinBox = effectStrength; + + boost::shared_ptr timeOffsetSb = AppManager::createKnob(this, kRotoUIParamTimeOffsetLabel); + timeOffsetSb->setName(kRotoUIParamTimeOffset); + timeOffsetSb->setHintToolTip(kRotoUIParamTimeOffsetHint); + timeOffsetSb->setEvaluateOnChange(false); + timeOffsetSb->setSecretByDefault(true); + timeOffsetSb->setDefaultValue(0); + timeOffsetSb->disableSlider(); + generalPage->addKnob(timeOffsetSb); + _imp->ui->timeOffsetSpinBox = timeOffsetSb; + + boost::shared_ptr timeOffsetMode = AppManager::createKnob(this, kRotoUIParamTimeOffsetLabel); + timeOffsetMode->setName(kRotoUIParamTimeOffset); + timeOffsetMode->setHintToolTip(kRotoUIParamTimeOffsetHint); + timeOffsetMode->setEvaluateOnChange(false); + timeOffsetMode->setSecretByDefault(true); + { + std::vector choices,helps; + choices.push_back("Relative"); + helps.push_back("The time offset is a frame number in the source"); + choices.push_back("Absolute"); + helps.push_back("The time offset is a relative amount of frames relative to the current frame"); + timeOffsetMode->populateChoices(choices, helps); + } + timeOffsetMode->setDefaultValue(0); + generalPage->addKnob(timeOffsetMode); + _imp->ui->timeOffsetModeChoice = timeOffsetMode; + + boost::shared_ptr sourceType = AppManager::createKnob(this, kRotoUIParamSourceTypeLabel); + sourceType->setName(kRotoUIParamSourceType); + sourceType->setHintToolTip(kRotoUIParamSourceTypeHint); + sourceType->setEvaluateOnChange(false); + sourceType->setSecretByDefault(true); + { + std::vector choices,helps; + choices.push_back("foreground"); + choices.push_back("background"); + for (int i = 1; i < 10; ++i) { + QString str = tr("background") + QString::number(i + 1); + choices.push_back(str.toStdString()); + } + sourceType->populateChoices(choices); + } + sourceType->setDefaultValue(1); + generalPage->addKnob(sourceType); + _imp->ui->sourceTypeChoice = sourceType; + + + boost::shared_ptr resetCloneOffset = AppManager::createKnob(this, kRotoUIParamResetCloneOffsetLabel); + resetCloneOffset->setName(kRotoUIParamResetCloneOffset); + resetCloneOffset->setHintToolTip(kRotoUIParamResetCloneOffsetHint); + resetCloneOffset->setEvaluateOnChange(false); + resetCloneOffset->setSecretByDefault(true); + resetCloneOffset->setInViewerContextCanHaveShortcut(true); + resetCloneOffset->setInViewerContextNewLineActivated(true); + generalPage->addKnob(resetCloneOffset); + _imp->ui->resetCloneOffsetButton = resetCloneOffset; + + + // Roto + addKnobToViewerUI(autoKeyingEnabled); + addKnobToViewerUI(featherLinkEnabled); + addKnobToViewerUI(displayFeatherEnabled); + addKnobToViewerUI(stickySelection); + addKnobToViewerUI(bboxClickAnywhere); + addKnobToViewerUI(rippleEditEnabled); + addKnobToViewerUI(addKeyframe); + addKnobToViewerUI(removeKeyframe); + + // RotoPaint + addKnobToViewerUI(multiStroke); + multiStroke->setInViewerContextItemSpacing(10); + addKnobToViewerUI(colorWheel); + colorWheel->setInViewerContextItemSpacing(5); + addKnobToViewerUI(blendingModes); + blendingModes->setInViewerContextItemSpacing(5); + addKnobToViewerUI(opacityKnob); + addKnobToViewerUI(pressureOpacity); + pressureOpacity->setInViewerContextItemSpacing(5); + addKnobToViewerUI(sizeKnob); + addKnobToViewerUI(pressureSize); + pressureSize->setInViewerContextItemSpacing(5); + addKnobToViewerUI(hardnessKnob); + addKnobToViewerUI(pressureHardness); + pressureHardness->setInViewerContextItemSpacing(5); + addKnobToViewerUI(buildUp); + buildUp->setInViewerContextItemSpacing(5); + addKnobToViewerUI(effectStrength); + effectStrength->setInViewerContextItemSpacing(5); + addKnobToViewerUI(timeOffsetSb); + addKnobToViewerUI(timeOffsetMode); + addKnobToViewerUI(sourceType); + addKnobToViewerUI(resetCloneOffset); + + + boost::shared_ptr toolbar = AppManager::createKnob(this, kRotoUIParamToolbar); + toolbar->setAsToolBar(true); + toolbar->setEvaluateOnChange(false); + toolbar->setSecretByDefault(true); + _imp->ui->toolbarPage = toolbar; + boost::shared_ptr selectionToolButton = AppManager::createKnob(this, kRotoUIParamSelectionToolButton); + selectionToolButton->setAsToolButton(true); + selectionToolButton->setDefaultValue(true); + selectionToolButton->setEvaluateOnChange(false); + selectionToolButton->setSecretByDefault(true); + selectionToolButton->setInViewerContextCanHaveShortcut(true); + toolbar->addKnob(selectionToolButton); + _imp->ui->selectToolGroup = selectionToolButton; + boost::shared_ptr editPointsToolButton = AppManager::createKnob(this, kRotoUIParamEditPointsToolButton); + editPointsToolButton->setAsToolButton(true); + editPointsToolButton->setEvaluateOnChange(false); + editPointsToolButton->setSecretByDefault(true); + editPointsToolButton->setInViewerContextCanHaveShortcut(true); + toolbar->addKnob(editPointsToolButton); + _imp->ui->pointsEditionToolGroup = editPointsToolButton; + boost::shared_ptr editBezierToolButton = AppManager::createKnob(this, kRotoUIParamBezierEditionToolButton); + editBezierToolButton->setAsToolButton(true); + editBezierToolButton->setEvaluateOnChange(false); + editBezierToolButton->setSecretByDefault(true); + editBezierToolButton->setInViewerContextCanHaveShortcut(true); + toolbar->addKnob(editBezierToolButton); + _imp->ui->bezierEditionToolGroup = editBezierToolButton; + boost::shared_ptr paintToolButton = AppManager::createKnob(this, kRotoUIParamPaintBrushToolButton); + paintToolButton->setAsToolButton(true); + paintToolButton->setEvaluateOnChange(false); + paintToolButton->setSecretByDefault(true); + paintToolButton->setInViewerContextCanHaveShortcut(true); + toolbar->addKnob(paintToolButton); + _imp->ui->paintBrushToolGroup = paintToolButton; + + boost::shared_ptr cloneToolButton, effectToolButton, mergeToolButton; + if (_imp->isPaintByDefault) { + cloneToolButton = AppManager::createKnob(this, kRotoUIParamCloneBrushToolButton); + cloneToolButton->setAsToolButton(true); + cloneToolButton->setEvaluateOnChange(false); + cloneToolButton->setSecretByDefault(true); + cloneToolButton->setInViewerContextCanHaveShortcut(true); + toolbar->addKnob(cloneToolButton); + _imp->ui->cloneBrushToolGroup = cloneToolButton; + effectToolButton = AppManager::createKnob(this, kRotoUIParamEffectBrushToolButton); + effectToolButton->setAsToolButton(true); + effectToolButton->setEvaluateOnChange(false); + effectToolButton->setSecretByDefault(true); + effectToolButton->setInViewerContextCanHaveShortcut(true); + toolbar->addKnob(effectToolButton); + _imp->ui->effectBrushToolGroup = effectToolButton; + mergeToolButton = AppManager::createKnob(this, kRotoUIParamMergeBrushToolButton); + mergeToolButton->setAsToolButton(true); + mergeToolButton->setEvaluateOnChange(false); + mergeToolButton->setSecretByDefault(true); + mergeToolButton->setInViewerContextCanHaveShortcut(true); + toolbar->addKnob(mergeToolButton); + _imp->ui->mergeBrushToolGroup = mergeToolButton; + } + + + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamSelectAllToolButtonAction); + tool->setHintToolTip(kRotoUIParamSelectAllToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(true); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "cursor.png"); + selectionToolButton->addKnob(tool); + _imp->ui->selectAllAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamSelectPointsToolButtonAction); + tool->setHintToolTip(kRotoUIParamSelectPointsToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "selectPoints.png"); + selectionToolButton->addKnob(tool); + _imp->ui->selectPointsAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamSelectShapesToolButtonAction); + tool->setHintToolTip(kRotoUIParamSelectShapesToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "selectCurves.png"); + selectionToolButton->addKnob(tool); + _imp->ui->selectCurvesAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamSelectFeatherPointsToolButtonAction); + tool->setHintToolTip(kRotoUIParamSelectFeatherPointsToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "selectFeather.png"); + selectionToolButton->addKnob(tool); + _imp->ui->selectFeatherPointsAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamAddPointsToolButtonAction); + tool->setHintToolTip(kRotoUIParamAddPointsToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(true); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "addPoints.png"); + editPointsToolButton->addKnob(tool); + _imp->ui->addPointsAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamRemovePointsToolButtonAction); + tool->setHintToolTip(kRotoUIParamRemovePointsToolButtonAction); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "removePoints.png"); + editPointsToolButton->addKnob(tool); + _imp->ui->removePointsAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamCuspPointsToolButtonAction); + tool->setHintToolTip(kRotoUIParamCuspPointsToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "cuspPoints.png"); + editPointsToolButton->addKnob(tool); + _imp->ui->cuspPointsAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamSmoothPointsToolButtonAction); + tool->setHintToolTip(kRotoUIParamSmoothPointsToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "smoothPoints.png"); + editPointsToolButton->addKnob(tool); + _imp->ui->smoothPointsAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamOpenCloseCurveToolButtonAction); + tool->setHintToolTip(kRotoUIParamOpenCloseCurveToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "openCloseCurve.png"); + editPointsToolButton->addKnob(tool); + _imp->ui->openCloseCurveAction = tool; + } + + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamRemoveFeatherToolButtonAction); + tool->setHintToolTip(kRotoUIParamRemoveFeatherToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "removeFeather.png"); + editPointsToolButton->addKnob(tool); + _imp->ui->removeFeatherAction = tool; + } + + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamDrawBezierToolButtonAction); + tool->setHintToolTip(kRotoUIParamDrawBezierToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(true); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "bezier32.png"); + editBezierToolButton->addKnob(tool); + _imp->ui->drawBezierAction = tool; + } + + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamDrawEllipseToolButtonAction); + tool->setHintToolTip(kRotoUIParamDrawEllipseToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "ellipse.png"); + editBezierToolButton->addKnob(tool); + _imp->ui->drawEllipseAction = tool; + } + + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamDrawRectangleToolButtonAction); + tool->setHintToolTip(kRotoUIParamDrawRectangleToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rectangle.png"); + editBezierToolButton->addKnob(tool); + _imp->ui->drawRectangleAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamDrawBrushToolButtonAction); + tool->setHintToolTip(kRotoUIParamDrawBrushToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(true); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotopaint_solid.png"); + paintToolButton->addKnob(tool); + _imp->ui->brushAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamPencilToolButtonAction); + tool->setHintToolTip(kRotoUIParamPencilToolButtonAction); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotoToolPencil.png"); + paintToolButton->addKnob(tool); + _imp->ui->pencilAction = tool; + } + + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamEraserToolButtonAction); + tool->setHintToolTip(kRotoUIParamEraserToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotopaint_eraser.png"); + paintToolButton->addKnob(tool); + _imp->ui->eraserAction = tool; + } + if (_imp->isPaintByDefault) { + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamCloneToolButtonAction); + tool->setHintToolTip(kRotoUIParamCloneToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(true); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotopaint_clone.png"); + cloneToolButton->addKnob(tool); + _imp->ui->cloneAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamRevealToolButtonAction); + tool->setHintToolTip(kRotoUIParamRevealToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotopaint_reveal.png"); + cloneToolButton->addKnob(tool); + _imp->ui->revealAction = tool; + } + + + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamBlurToolButtonAction); + tool->setHintToolTip(kRotoUIParamBlurToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(true); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotopaint_blur.png"); + effectToolButton->addKnob(tool); + _imp->ui->blurAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamSmearToolButtonAction); + tool->setHintToolTip(kRotoUIParamSmearToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotopaint_smear.png"); + effectToolButton->addKnob(tool); + _imp->ui->smearAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamDodgeToolButtonAction); + tool->setHintToolTip(kRotoUIParamDodgeToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(true); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotopaint_dodge.png"); + mergeToolButton->addKnob(tool); + _imp->ui->dodgeAction = tool; + } + { + boost::shared_ptr tool = AppManager::createKnob(this, kRotoUIParamBurnToolButtonAction); + tool->setHintToolTip(kRotoUIParamBurnToolButtonActionHint); + tool->setCheckable(true); + tool->setDefaultValue(false); + tool->setSecretByDefault(true); + tool->setEvaluateOnChange(false); + tool->setIconLabel(NATRON_IMAGES_PATH "rotopaint_burn.png"); + mergeToolButton->addKnob(tool); + _imp->ui->burnAction = tool; + } + } // if (_imp->isPaintByDefault) { + + // Right click menu + boost::shared_ptr rightClickMenu = AppManager::createKnob(this, kRotoUIParamRightClickMenu); + rightClickMenu->setSecretByDefault(true); + rightClickMenu->setEvaluateOnChange(false); + generalPage->addKnob(rightClickMenu); + _imp->ui->rightClickMenuKnob = rightClickMenu; + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionRemoveItemsLabel); + action->setName(kRotoUIParamRightClickMenuActionRemoveItems); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->removeItemsMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionCuspItemsLabel); + action->setName(kRotoUIParamRightClickMenuActionCuspItems); + action->setEvaluateOnChange(false); + action->setSecretByDefault(true); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->cuspItemMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionSmoothItemsLabel); + action->setName(kRotoUIParamRightClickMenuActionSmoothItems); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->smoothItemMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionRemoveItemsFeatherLabel); + action->setName(kRotoUIParamRightClickMenuActionRemoveItemsFeather); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->removeItemFeatherMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionLinkItemsToTrackLabel); + action->setName(kRotoUIParamRightClickMenuActionLinkItemsToTrack); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->linkPointMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionUnlinkItemsFromTrackLabel); + action->setName(kRotoUIParamRightClickMenuActionUnlinkItemsFromTrack); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->unlinkPointMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionNudgeBottomLabel); + action->setName(kRotoUIParamRightClickMenuActionNudgeBottom); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->nudgeBottomMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionNudgeLeftLabel); + action->setName(kRotoUIParamRightClickMenuActionNudgeLeft); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->nudgeLeftMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionNudgeTopLabel); + action->setName(kRotoUIParamRightClickMenuActionNudgeTop); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->nudgeTopMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionNudgeRightLabel); + action->setName(kRotoUIParamRightClickMenuActionNudgeRight); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->nudgeRightMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionSelectAllLabel); + action->setName(kRotoUIParamRightClickMenuActionSelectAll); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + generalPage->addKnob(action); + _imp->ui->selectAllMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionOpenCloseLabel); + action->setName(kRotoUIParamRightClickMenuActionOpenClose); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->openCloseMenuAction = action; + } + { + boost::shared_ptr action = AppManager::createKnob(this, kRotoUIParamRightClickMenuActionLockShapesLabel); + action->setName(kRotoUIParamRightClickMenuActionLockShapes); + action->setSecretByDefault(true); + action->setEvaluateOnChange(false); + addOverlaySlaveParam(action); + generalPage->addKnob(action); + _imp->ui->lockShapeMenuAction = action; + } + + _imp->ui->setCurrentTool(_imp->ui->selectAllAction.lock()); +} + +void +RotoPaint::getPluginShortcuts(std::list* shortcuts) +{ + // Viewer buttons + shortcuts->push_back(PluginActionShortcut(kRotoUIParamAutoKeyingEnabled, kRotoUIParamAutoKeyingEnabledLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamFeatherLinkEnabled, kRotoUIParamFeatherLinkEnabledLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamDisplayFeather, kRotoUIParamDisplayFeatherLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamStickySelectionEnabled, kRotoUIParamStickySelectionEnabledLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamStickyBbox, kRotoUIParamStickyBboxLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRippleEdit, kRotoUIParamRippleEditLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamAddKeyFrame, kRotoUIParamAddKeyFrameLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRemoveKeyframe, kRotoUIParamRemoveKeyframeLabel)); + + shortcuts->push_back(PluginActionShortcut(kRotoUIParamPressureOpacity, kRotoUIParamPressureOpacityLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamPressureSize, kRotoUIParamPressureSizeLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamPressureHardness, kRotoUIParamPressureHardnessLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamBuildUp, kRotoUIParamBuildUpLabel)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamResetCloneOffset, kRotoUIParamResetCloneOffsetLabel)); + + // Toolbuttons + shortcuts->push_back(PluginActionShortcut(kRotoUIParamSelectionToolButton, kRotoUIParamSelectionToolButtonLabel, Key_Q)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamEditPointsToolButton, kRotoUIParamEditPointsToolButtonLabel, Key_D)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamBezierEditionToolButton, kRotoUIParamBezierEditionToolButtonLabel, Key_V)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamPaintBrushToolButton, kRotoUIParamPaintBrushToolButtonLabel, Key_N)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamCloneBrushToolButton, kRotoUIParamCloneBrushToolButtonLabel, Key_C)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamEffectBrushToolButton, kRotoUIParamEffectBrushToolButtonLabel, Key_X)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamMergeBrushToolButton, kRotoUIParamMergeBrushToolButtonLabel, Key_E)); + + // Right click actions + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionRemoveItems, kRotoUIParamRightClickMenuActionRemoveItemsLabel, Key_BackSpace)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionCuspItems, kRotoUIParamRightClickMenuActionCuspItemsLabel, Key_Z, eKeyboardModifierShift)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionSmoothItems, kRotoUIParamRightClickMenuActionSmoothItemsLabel, Key_Z)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionRemoveItemsFeather, kRotoUIParamRightClickMenuActionRemoveItemsFeatherLabel, Key_E, eKeyboardModifierShift)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionNudgeLeft, kRotoUIParamRightClickMenuActionNudgeLeftLabel, Key_Left)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionNudgeBottom, kRotoUIParamRightClickMenuActionNudgeBottomLabel, Key_Down)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionNudgeTop, kRotoUIParamRightClickMenuActionNudgeTopLabel, Key_Up)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionNudgeRight, kRotoUIParamRightClickMenuActionNudgeRightLabel, Key_Right)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionSelectAll, kRotoUIParamRightClickMenuActionSelectAllLabel, Key_A, eKeyboardModifierControl)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionOpenClose, kRotoUIParamRightClickMenuActionOpenCloseLabel, Key_Return)); + shortcuts->push_back(PluginActionShortcut(kRotoUIParamRightClickMenuActionLockShapes, kRotoUIParamRightClickMenuActionLockShapesLabel, Key_L, eKeyboardModifierShift)); + +} + +void +RotoPaint::onKnobsLoaded() +{ + _imp->ui->selectedItems = getNode()->getRotoContext()->getSelectedCurves(); } void @@ -229,14 +1061,152 @@ RotoPaint::knobChanged(KnobI* k, double time, bool originatedFromMainThread) { + + if (!k) { + return; + } + boost::shared_ptr ctx = getNode()->getRotoContext(); if (!ctx) { return; } + KnobPtr kShared = k->shared_from_this(); + boost::shared_ptr isBtn = boost::dynamic_pointer_cast(kShared); + boost::shared_ptr isGrp = boost::dynamic_pointer_cast(kShared); + if (isBtn && _imp->ui->onToolChangedInternal(isBtn)) { + return; + } else if (isGrp && _imp->ui->onRoleChangedInternal(isGrp)) { + return; + } else if (k == _imp->ui->featherLinkEnabledButton.lock().get()) { + ctx->onFeatherLinkChanged(_imp->ui->featherLinkEnabledButton.lock()->getValue()); + } else if (k == _imp->ui->autoKeyingEnabledButton.lock().get()) { + ctx->onFeatherLinkChanged(_imp->ui->autoKeyingEnabledButton.lock()->getValue()); + } else if (k == _imp->ui->rippleEditEnabledButton.lock().get()) { + ctx->onFeatherLinkChanged(_imp->ui->rippleEditEnabledButton.lock()->getValue()); + } else if (k == _imp->ui->colorWheelButton.lock().get()) { + _imp->ui->onBreakMultiStrokeTriggered(); + } else if (k == _imp->ui->pressureOpacityButton.lock().get()) { + _imp->ui->onBreakMultiStrokeTriggered(); + } else if (k == _imp->ui->pressureSizeButton.lock().get()) { + _imp->ui->onBreakMultiStrokeTriggered(); + } else if (k == _imp->ui->hardnessSpinbox.lock().get()) { + _imp->ui->onBreakMultiStrokeTriggered(); + } else if (k == _imp->ui->buildUpButton.lock().get()) { + _imp->ui->onBreakMultiStrokeTriggered(); + } else if (k == _imp->ui->resetCloneOffsetButton.lock().get()) { + _imp->ui->onBreakMultiStrokeTriggered(); + } else if (k == _imp->ui->addKeyframeButton.lock().get()) { + for (SelectedItems::iterator it = _imp->ui->selectedItems.begin(); it != _imp->ui->selectedItems.end(); ++it) { + Bezier* isBezier = dynamic_cast( it->get() ); + if (!isBezier) { + continue; + } + isBezier->setKeyframe(time); + } + getNode()->getRotoContext()->evaluateChange(); + } else if (k == _imp->ui->removeKeyframeButton.lock().get()) { + + for (SelectedItems::iterator it = _imp->ui->selectedItems.begin(); it != _imp->ui->selectedItems.end(); ++it) { + Bezier* isBezier = dynamic_cast( it->get() ); + if (!isBezier) { + continue; + } + isBezier->removeKeyframe(time); + } + getNode()->getRotoContext()->evaluateChange(); + } else if (k == _imp->ui->removeItemsMenuAction.lock().get()) { + ///if control points are selected, delete them, otherwise delete the selected beziers + if ( !_imp->ui->selectedCps.empty() ) { + pushUndoCommand( new RemovePointUndoCommand(_imp->ui, _imp->ui->selectedCps) ); + } else if ( !_imp->ui->selectedItems.empty() ) { + pushUndoCommand( new RemoveCurveUndoCommand(_imp->ui, _imp->ui->selectedItems) ); + } + } else if (k == _imp->ui->smoothItemMenuAction.lock().get()) { + _imp->ui->smoothSelectedCurve(); + } else if (k == _imp->ui->cuspItemMenuAction.lock().get()) { + _imp->ui->cuspSelectedCurve(); + } else if (k == _imp->ui->removeItemFeatherMenuAction.lock().get()) { + + _imp->ui->removeFeatherForSelectedCurve(); + + } else if (k == _imp->ui->linkPointMenuAction.lock().get()) { + + } else if (k == _imp->ui->unlinkPointMenuAction.lock().get()) { + + } else if (k == _imp->ui->nudgeLeftMenuAction.lock().get()) { + _imp->ui->moveSelectedCpsWithKeyArrows(-1, 0); + } else if (k == _imp->ui->nudgeRightMenuAction.lock().get()) { + _imp->ui->moveSelectedCpsWithKeyArrows(1, 0); + } else if (k == _imp->ui->nudgeBottomMenuAction.lock().get()) { + _imp->ui->moveSelectedCpsWithKeyArrows(0, -1); + } else if (k == _imp->ui->nudgeTopMenuAction.lock().get()) { + _imp->ui->moveSelectedCpsWithKeyArrows(0, 1); + } else if (k == _imp->ui->selectAllMenuAction.lock().get()) { + _imp->ui->iSelectingwithCtrlA = true; + ///if no bezier are selected, select all beziers + if ( _imp->ui->selectedItems.empty() ) { + std::list > bez = ctx->getCurvesByRenderOrder(); + for (std::list >::const_iterator it = bez.begin(); it != bez.end(); ++it) { + ctx->select(*it, RotoItem::eSelectionReasonOverlayInteract); + _imp->ui->selectedItems.push_back(*it); + } + } else { + ///select all the control points of all selected beziers + _imp->ui->selectedCps.clear(); + for (SelectedItems::iterator it = _imp->ui->selectedItems.begin(); it != _imp->ui->selectedItems.end(); ++it) { + Bezier* isBezier = dynamic_cast( it->get() ); + if (!isBezier) { + continue; + } + const std::list > & cps = isBezier->getControlPoints(); + const std::list > & fps = isBezier->getFeatherPoints(); + assert( cps.size() == fps.size() ); + + std::list >::const_iterator cpIT = cps.begin(); + for (std::list >::const_iterator fpIT = fps.begin(); fpIT != fps.end(); ++fpIT, ++cpIT) { + _imp->ui->selectedCps.push_back( std::make_pair(*cpIT, *fpIT) ); + } + } + _imp->ui->computeSelectedCpsBBOX(); + } + + } else if (k == _imp->ui->openCloseCurveAction.lock().get()) { + if ( ( (_imp->ui->selectedTool == eRotoToolDrawBezier) || (_imp->ui->selectedTool == eRotoToolOpenBezier) ) && _imp->ui->builtBezier && !_imp->ui->builtBezier->isCurveFinished() ) { + pushUndoCommand( new OpenCloseUndoCommand(_imp->ui, _imp->ui->builtBezier) ); + + _imp->ui->builtBezier.reset(); + _imp->ui->selectedCps.clear(); + _imp->ui->setCurrentTool(_imp->ui->selectAllAction.lock()); + _imp->ui->getContext()->evaluateChange(); + } + } else if (k == _imp->ui->lockShapeMenuAction.lock().get()) { + _imp->ui->lockSelectedCurves(); + } + + ctx->knobChanged(k, reason, view, time, originatedFromMainThread); } +void +RotoPaint::refreshExtraStateAfterTimeChanged(double time) +{ + EffectInstance::refreshExtraStateAfterTimeChanged(time); + if ( (_imp->ui->selectedTool == eRotoToolBlur) || + ( _imp->ui->selectedTool == eRotoToolBurn) || + ( _imp->ui->selectedTool == eRotoToolDodge) || + ( _imp->ui->selectedTool == eRotoToolClone) || + ( _imp->ui->selectedTool == eRotoToolEraserBrush) || + ( _imp->ui->selectedTool == eRotoToolSolidBrush) || + ( _imp->ui->selectedTool == eRotoToolReveal) || + ( _imp->ui->selectedTool == eRotoToolSmear) || + ( _imp->ui->selectedTool == eRotoToolSharpen) ) { + _imp->ui->onBreakMultiStrokeTriggered(); + } + _imp->ui->computeSelectedCpsBBOX(); + +} + StatusEnum RotoPaint::getPreferredMetaDatas(NodeMetadata& metadata) { @@ -565,4 +1535,1669 @@ RotoPaint::clearLastRenderedImage() } } + + +void +RotoPaint::drawOverlay(double time, const RenderScale & /*renderScale*/, ViewIdx /*view*/) +{ + std::list< boost::shared_ptr > drawables = getNode()->getRotoContext()->getCurvesByRenderOrder(); + std::pair pixelScale; + std::pair viewportSize; + + getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + getCurrentViewportForOverlays()->getViewportSize(viewportSize.first, viewportSize.second); + + bool featherVisible = _imp->ui->isFeatherVisible(); + + { + GLProtectAttrib a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + glLineWidth(1.5); + glEnable(GL_POINT_SMOOTH); + glPointSize(7.); + for (std::list< boost::shared_ptr >::const_iterator it = drawables.begin(); it != drawables.end(); ++it) { + if ( !(*it)->isGloballyActivated() ) { + continue; + } + + + Bezier* isBezier = dynamic_cast( it->get() ); + RotoStrokeItem* isStroke = dynamic_cast( it->get() ); + if (isStroke) { + if (_imp->ui->selectedTool != eRotoToolSelectAll) { + continue; + } + + bool selected = false; + for (SelectedItems::const_iterator it2 = _imp->ui->selectedItems.begin(); it2 != _imp->ui->selectedItems.end(); ++it2) { + if (it2->get() == isStroke) { + selected = true; + break; + } + } + if (!selected) { + continue; + } + + std::list > > strokes; + isStroke->evaluateStroke(0, time, &strokes); + bool locked = (*it)->isLockedRecursive(); + double curveColor[4]; + if (!locked) { + (*it)->getOverlayColor(curveColor); + } else { + curveColor[0] = 0.8; curveColor[1] = 0.8; curveColor[2] = 0.8; curveColor[3] = 1.; + } + glColor4dv(curveColor); + + for (std::list > >::iterator itStroke = strokes.begin(); itStroke != strokes.end(); ++itStroke) { + glBegin(GL_LINE_STRIP); + for (std::list >::const_iterator it2 = itStroke->begin(); it2 != itStroke->end(); ++it2) { + glVertex2f(it2->first.x, it2->first.y); + } + glEnd(); + } + } else if (isBezier) { + ///draw the bezier + // check if the bbox is visible + // if the bbox is visible, compute the polygon and draw it. + + + RectD bbox = isBezier->getBoundingBox(time); + if ( !getCurrentViewportForOverlays()->isVisibleInViewport(bbox) ) { + continue; + } + + std::list< Point > points; + isBezier->evaluateAtTime_DeCasteljau(true, time, 0, 100, &points, NULL); + + bool locked = (*it)->isLockedRecursive(); + double curveColor[4]; + if (!locked) { + (*it)->getOverlayColor(curveColor); + } else { + curveColor[0] = 0.8; curveColor[1] = 0.8; curveColor[2] = 0.8; curveColor[3] = 1.; + } + glColor4dv(curveColor); + + glBegin(GL_LINE_STRIP); + for (std::list::const_iterator it2 = points.begin(); it2 != points.end(); ++it2) { + glVertex2f(it2->x, it2->y); + } + glEnd(); + + ///draw the feather points + std::list< Point > featherPoints; + RectD featherBBox( std::numeric_limits::infinity(), + std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), + -std::numeric_limits::infinity() ); + bool clockWise = isBezier->isFeatherPolygonClockwiseOriented(true, time); + + + if ( featherVisible ) { + ///Draw feather only if visible (button is toggled in the user interface) + isBezier->evaluateFeatherPointsAtTime_DeCasteljau(true, time, 0, 100, true, &featherPoints, &featherBBox); + + if ( !featherPoints.empty() ) { + glLineStipple(2, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + glBegin(GL_LINE_STRIP); + for (std::list::const_iterator it2 = featherPoints.begin(); it2 != featherPoints.end(); ++it2) { + glVertex2f(it2->x, it2->y); + } + glEnd(); + glDisable(GL_LINE_STIPPLE); + } + } + + ///draw the control points if the bezier is selected + bool selected = false; + for (SelectedItems::const_iterator it2 = _imp->ui->selectedItems.begin(); it2 != _imp->ui->selectedItems.end(); ++it2) { + if (it2->get() == isBezier) { + selected = true; + break; + } + } + + + if (selected && !locked) { + Transform::Matrix3x3 transform; + isBezier->getTransformAtTime(time, &transform); + + const std::list< boost::shared_ptr > & cps = isBezier->getControlPoints(); + const std::list< boost::shared_ptr > & featherPts = isBezier->getFeatherPoints(); + assert( cps.size() == featherPts.size() ); + + if ( cps.empty() ) { + continue; + } + + double cpHalfWidth = kControlPointMidSize * pixelScale.first; + double cpHalfHeight = kControlPointMidSize * pixelScale.second; + + glColor3d(0.85, 0.67, 0.); + + std::list< boost::shared_ptr >::const_iterator itF = featherPts.begin(); + int index = 0; + std::list< boost::shared_ptr >::const_iterator prevCp = cps.end(); + if ( prevCp != cps.begin() ) { + --prevCp; + } + std::list< boost::shared_ptr >::const_iterator nextCp = cps.begin(); + if ( nextCp != cps.end() ) { + ++nextCp; + } + for (std::list< boost::shared_ptr >::const_iterator it2 = cps.begin(); it2 != cps.end(); + ++it2) { + if ( nextCp == cps.end() ) { + nextCp = cps.begin(); + } + if ( prevCp == cps.end() ) { + prevCp = cps.begin(); + } + assert( itF != featherPts.end() ); // because cps.size() == featherPts.size() + if ( itF == featherPts.end() ) { + break; + } + double x, y; + Transform::Point3D p, pF; + (*it2)->getPositionAtTime(true, time, ViewIdx(0), &p.x, &p.y); + p.z = 1.; + + double xF, yF; + (*itF)->getPositionAtTime(true, time, ViewIdx(0), &pF.x, &pF.y); + pF.z = 1.; + + p = Transform::matApply(transform, p); + pF = Transform::matApply(transform, pF); + + x = p.x; + y = p.y; + xF = pF.x; + yF = pF.y; + + ///draw the feather point only if it is distinct from the associated point + bool drawFeather = featherVisible; + if (featherVisible) { + drawFeather = !(*it2)->equalsAtTime(true, time, ViewIdx(0), **itF); + } + + + ///if the control point is the only control point being dragged, color it to identify it to the user + bool colorChanged = false; + SelectedCPs::const_iterator firstSelectedCP = _imp->ui->selectedCps.begin(); + if ( ( firstSelectedCP != _imp->ui->selectedCps.end() ) && + ( ( firstSelectedCP->first == *it2) || ( firstSelectedCP->second == *it2) ) && + ( _imp->ui->selectedCps.size() == 1) && + ( ( _imp->ui->state == eEventStateDraggingSelectedControlPoints) || ( _imp->ui->state == eEventStateDraggingControlPoint) ) ) { + glColor3f(0., 1., 1.); + colorChanged = true; + } + + for (SelectedCPs::const_iterator cpIt = _imp->ui->selectedCps.begin(); + cpIt != _imp->ui->selectedCps.end(); + ++cpIt) { + ///if the control point is selected, draw its tangent handles + if (cpIt->first == *it2) { + _imp->ui->drawSelectedCp(time, cpIt->first, x, y, transform); + if (drawFeather) { + _imp->ui->drawSelectedCp(time, cpIt->second, xF, yF, transform); + } + glColor3f(0.2, 1., 0.); + colorChanged = true; + break; + } else if (cpIt->second == *it2) { + _imp->ui->drawSelectedCp(time, cpIt->second, x, y, transform); + if (drawFeather) { + _imp->ui->drawSelectedCp(time, cpIt->first, xF, yF, transform); + } + glColor3f(0.2, 1., 0.); + colorChanged = true; + break; + } + } // for(cpIt) + + glBegin(GL_POLYGON); + glVertex2f(x - cpHalfWidth, y - cpHalfHeight); + glVertex2f(x + cpHalfWidth, y - cpHalfHeight); + glVertex2f(x + cpHalfWidth, y + cpHalfHeight); + glVertex2f(x - cpHalfWidth, y + cpHalfHeight); + glEnd(); + + if (colorChanged) { + glColor3d(0.85, 0.67, 0.); + } + + if ( (firstSelectedCP->first == *itF) + && ( _imp->ui->selectedCps.size() == 1) && + ( ( _imp->ui->state == eEventStateDraggingSelectedControlPoints) || ( _imp->ui->state == eEventStateDraggingControlPoint) ) + && !colorChanged ) { + glColor3f(0.2, 1., 0.); + colorChanged = true; + } + + + double distFeatherX = 20. * pixelScale.first; + double distFeatherY = 20. * pixelScale.second; + bool isHovered = false; + if (_imp->ui->featherBarBeingHovered.first) { + assert(_imp->ui->featherBarBeingHovered.second); + if ( _imp->ui->featherBarBeingHovered.first->isFeatherPoint() ) { + isHovered = _imp->ui->featherBarBeingHovered.first == *itF; + } else if ( _imp->ui->featherBarBeingHovered.second->isFeatherPoint() ) { + isHovered = _imp->ui->featherBarBeingHovered.second == *itF; + } + } + + if (drawFeather) { + glBegin(GL_POLYGON); + glVertex2f(xF - cpHalfWidth, yF - cpHalfHeight); + glVertex2f(xF + cpHalfWidth, yF - cpHalfHeight); + glVertex2f(xF + cpHalfWidth, yF + cpHalfHeight); + glVertex2f(xF - cpHalfWidth, yF + cpHalfHeight); + glEnd(); + + + if ( ( (_imp->ui->state == eEventStateDraggingFeatherBar) && + ( ( *itF == _imp->ui->featherBarBeingDragged.first) || ( *itF == _imp->ui->featherBarBeingDragged.second) ) ) || + isHovered ) { + glColor3f(0.2, 1., 0.); + colorChanged = true; + } else { + glColor4dv(curveColor); + } + + double beyondX, beyondY; + double dx = (xF - x); + double dy = (yF - y); + double dist = sqrt(dx * dx + dy * dy); + beyondX = ( dx * (dist + distFeatherX) ) / dist + x; + beyondY = ( dy * (dist + distFeatherY) ) / dist + y; + + ///draw a link between the feather point and the control point. + ///Also extend that link of 20 pixels beyond the feather point. + + glBegin(GL_LINE_STRIP); + glVertex2f(x, y); + glVertex2f(xF, yF); + glVertex2f(beyondX, beyondY); + glEnd(); + + glColor3d(0.85, 0.67, 0.); + } else if ( featherVisible ) { + ///if the feather point is identical to the control point + ///draw a small hint line that the user can drag to move the feather point + if ( !isBezier->isOpenBezier() && ( (_imp->ui->selectedTool == eRotoToolSelectAll) || (_imp->ui->selectedTool == eRotoToolSelectFeatherPoints) ) ) { + int cpCount = (*it2)->getBezier()->getControlPointsCount(); + if (cpCount > 1) { + Point controlPoint; + controlPoint.x = x; + controlPoint.y = y; + Point featherPoint; + featherPoint.x = xF; + featherPoint.y = yF; + + + Bezier::expandToFeatherDistance(true, controlPoint, &featherPoint, distFeatherX, time, clockWise, transform, prevCp, it2, nextCp); + + if ( ( (_imp->ui->state == eEventStateDraggingFeatherBar) && + ( ( *itF == _imp->ui->featherBarBeingDragged.first) || + ( *itF == _imp->ui->featherBarBeingDragged.second) ) ) || isHovered ) { + glColor3f(0.2, 1., 0.); + colorChanged = true; + } else { + glColor4dv(curveColor); + } + + glBegin(GL_LINES); + glVertex2f(x, y); + glVertex2f(featherPoint.x, featherPoint.y); + glEnd(); + + glColor3d(0.85, 0.67, 0.); + } + } + } // isFeatherVisible() + + + if (colorChanged) { + glColor3d(0.85, 0.67, 0.); + } + + // increment for next iteration + if ( itF != featherPts.end() ) { + ++itF; + } + if ( nextCp != cps.end() ) { + ++nextCp; + } + if ( prevCp != cps.end() ) { + ++prevCp; + } + ++index; + } // for(it2) + } // if ( ( selected != _imp->ui->selectedBeziers.end() ) && !locked ) { + } // if (isBezier) + glCheckError(); + } // for (std::list< boost::shared_ptr >::const_iterator it = drawables.begin(); it != drawables.end(); ++it) { + + if ( _imp->isPaintByDefault && + ( ( _imp->ui->selectedRole == eRotoRoleMergeBrush) || + ( _imp->ui->selectedRole == eRotoRolePaintBrush) || + ( _imp->ui->selectedRole == eRotoRoleEffectBrush) || + ( _imp->ui->selectedRole == eRotoRoleCloneBrush) ) && + ( _imp->ui->selectedTool != eRotoToolOpenBezier) ) { + + Point cursorPos; + getCurrentViewportForOverlays()->getCursorPosition(cursorPos.x, cursorPos.y); + RectD viewportRect = getCurrentViewportForOverlays()->getViewportRect(); + + if ( viewportRect.contains(cursorPos.x, cursorPos.y) ) { + //Draw a circle around the cursor + double brushSize = _imp->ui->sizeSpinbox.lock()->getValue(); + GLdouble projection[16]; + glGetDoublev( GL_PROJECTION_MATRIX, projection); + Point shadow; // how much to translate GL_PROJECTION to get exactly one pixel on screen + shadow.x = 2. / (projection[0] * viewportSize.first); + shadow.y = 2. / (projection[5] * viewportSize.second); + + double halfBrush = brushSize / 2.; + QPointF ellipsePos; + if ( (_imp->ui->state == eEventStateDraggingBrushSize) || (_imp->ui->state == eEventStateDraggingBrushOpacity) ) { + ellipsePos = _imp->ui->mouseCenterOnSizeChange; + } else { + ellipsePos = _imp->ui->lastMousePos; + } + double opacity = _imp->ui->opacitySpinbox.lock()->getValue(); + + for (int l = 0; l < 2; ++l) { + glMatrixMode(GL_PROJECTION); + int direction = (l == 0) ? 1 : -1; + // translate (1,-1) pixels + glTranslated(direction * shadow.x, -direction * shadow.y, 0); + glMatrixMode(GL_MODELVIEW); + _imp->ui->drawEllipse(ellipsePos.x(), ellipsePos.y(), halfBrush, halfBrush, l, 1.f, 1.f, 1.f, opacity); + + glColor3f(.5f * l * opacity, .5f * l * opacity, .5f * l * opacity); + + + if ( ( (_imp->ui->selectedTool == eRotoToolClone) || (_imp->ui->selectedTool == eRotoToolReveal) ) && + ( ( _imp->ui->cloneOffset.first != 0) || ( _imp->ui->cloneOffset.second != 0) ) ) { + glBegin(GL_LINES); + + if (_imp->ui->state == eEventStateDraggingCloneOffset) { + //draw a line between the center of the 2 ellipses + glVertex2d( ellipsePos.x(), ellipsePos.y() ); + glVertex2d(ellipsePos.x() + _imp->ui->cloneOffset.first, ellipsePos.y() + _imp->ui->cloneOffset.second); + } + //draw a cross in the center of the source ellipse + glVertex2d(ellipsePos.x() + _imp->ui->cloneOffset.first, ellipsePos.y() + _imp->ui->cloneOffset.second - halfBrush); + glVertex2d(ellipsePos.x() + _imp->ui->cloneOffset.first, ellipsePos.y() + _imp->ui->cloneOffset.second + halfBrush); + glVertex2d(ellipsePos.x() + _imp->ui->cloneOffset.first - halfBrush, ellipsePos.y() + _imp->ui->cloneOffset.second); + glVertex2d(ellipsePos.x() + _imp->ui->cloneOffset.first + halfBrush, ellipsePos.y() + _imp->ui->cloneOffset.second); + glEnd(); + + + //draw the source ellipse + _imp->ui->drawEllipse(ellipsePos.x() + _imp->ui->cloneOffset.first, ellipsePos.y() + _imp->ui->cloneOffset.second, halfBrush, halfBrush, l, 1.f, 1.f, 1.f, opacity / 2.); + } + } + } + } + } // GLProtectAttrib a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT); + + if (_imp->ui->showCpsBbox ) { + _imp->ui->drawSelectedCpsBBOX(); + } + glCheckError(); + +} // drawOverlay + +void +RotoPaint::onInteractViewportSelectionCleared() +{ + if (!_imp->ui->isStickySelectionEnabled() && !_imp->ui->shiftDown) { + _imp->ui->clearSelection(); + } + + if ((_imp->ui->selectedTool == eRotoToolDrawBezier) || (_imp->ui->selectedTool == eRotoToolOpenBezier)) { + if ( ( (_imp->ui->selectedTool == eRotoToolDrawBezier) || (_imp->ui->selectedTool == eRotoToolOpenBezier) ) && _imp->ui->builtBezier && !_imp->ui->builtBezier->isCurveFinished() ) { + pushUndoCommand( new OpenCloseUndoCommand(_imp->ui, _imp->ui->builtBezier) ); + + _imp->ui->builtBezier.reset(); + _imp->ui->selectedCps.clear(); + _imp->ui->setCurrentTool(_imp->ui->selectAllAction.lock()); + _imp->ui->getContext()->evaluateChange(); + } + } +} + +void +RotoPaint::onInteractViewportSelectionUpdated(const RectD& rectangle, bool onRelease) +{ + if ( !onRelease || !getNode()->isSettingsPanelVisible() ) { + return; + } + + bool stickySel = _imp->ui->isStickySelectionEnabled(); + if (!stickySel && !_imp->ui->shiftDown) { + _imp->ui->clearCPSSelection(); + _imp->ui->selectedItems.clear(); + } + + int selectionMode = -1; + if (_imp->ui->selectedTool == eRotoToolSelectAll) { + selectionMode = 0; + } else if (_imp->ui->selectedTool == eRotoToolSelectPoints) { + selectionMode = 1; + } else if ( (_imp->ui->selectedTool == eRotoToolSelectFeatherPoints) || (_imp->ui->selectedTool == eRotoToolSelectCurves) ) { + selectionMode = 2; + } + + + bool featherVisible = _imp->ui->isFeatherVisible(); + + boost::shared_ptr ctx = getNode()->getRotoContext(); + assert(ctx); + std::list > curves = ctx->getCurvesByRenderOrder(); + for (std::list >::const_iterator it = curves.begin(); it != curves.end(); ++it) { + boost::shared_ptr isBezier = boost::dynamic_pointer_cast(*it); + if ( (*it)->isLockedRecursive() ) { + continue; + } + + if (isBezier) { + SelectedCPs points = isBezier->controlPointsWithinRect(rectangle.x1, rectangle.x2, rectangle.y1, rectangle.y2, 0, selectionMode); + if (_imp->ui->selectedTool != eRotoToolSelectCurves) { + for (SelectedCPs::iterator ptIt = points.begin(); ptIt != points.end(); ++ptIt) { + if ( !featherVisible && ptIt->first->isFeatherPoint() ) { + continue; + } + SelectedCPs::iterator foundCP = std::find(_imp->ui->selectedCps.begin(), _imp->ui->selectedCps.end(), *ptIt); + if ( foundCP == _imp->ui->selectedCps.end() ) { + if (!_imp->ui->shiftDown || !_imp->ui->ctrlDown) { + _imp->ui->selectedCps.push_back(*ptIt); + } + } else { + if (_imp->ui->shiftDown && _imp->ui->ctrlDown) { + _imp->ui->selectedCps.erase(foundCP); + } + } + } + } + if ( !points.empty() ) { + _imp->ui->selectedItems.push_back(isBezier); + } + } + } + + if ( !_imp->ui->selectedItems.empty() ) { + ctx->select(_imp->ui->selectedItems, RotoItem::eSelectionReasonOverlayInteract); + } else if (!stickySel && !_imp->ui->shiftDown) { + ctx->clearSelection(RotoItem::eSelectionReasonOverlayInteract); + } + + _imp->ui->computeSelectedCpsBBOX(); +} + +bool +RotoPaint::onOverlayPenDoubleClicked(double /*time*/, + const RenderScale & /*renderScale*/, + ViewIdx /*view*/, + const QPointF & /*viewportPos*/, + const QPointF & pos) +{ + bool didSomething = false; + std::pair pixelScale; + + getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + + if (_imp->ui->selectedTool == eRotoToolSelectAll) { + double bezierSelectionTolerance = kBezierSelectionTolerance * pixelScale.first; + double nearbyBezierT; + int nearbyBezierCPIndex; + bool isFeather; + boost::shared_ptr nearbyBezier = + getNode()->getRotoContext()->isNearbyBezier(pos.x(), pos.y(), bezierSelectionTolerance, &nearbyBezierCPIndex, &nearbyBezierT, &isFeather); + + if (nearbyBezier) { + ///If the bezier is already selected and we re-click on it, change the transform mode + _imp->ui->handleBezierSelection(nearbyBezier); + _imp->ui->clearCPSSelection(); + const std::list > & cps = nearbyBezier->getControlPoints(); + const std::list > & fps = nearbyBezier->getFeatherPoints(); + assert( cps.size() == fps.size() ); + std::list >::const_iterator itCp = cps.begin(); + std::list >::const_iterator itFp = fps.begin(); + for (; itCp != cps.end(); ++itCp, ++itFp) { + _imp->ui->selectedCps.push_back( std::make_pair(*itCp, *itFp) ); + } + if (_imp->ui->selectedCps.size() > 1) { + _imp->ui->computeSelectedCpsBBOX(); + } + didSomething = true; + } + } + + return didSomething; +} // onOverlayPenDoubleClicked + + +bool +RotoPaint::onOverlayPenDown(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, PenType pen) +{ + NodePtr node = getNode(); + if ( node->onOverlayPenDownDefault(time, renderScale, view, viewportPos, pos, pressure) ) { + return true; + } + + std::pair pixelScale; + getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + + bool didSomething = false; + double tangentSelectionTol = kTangentHandleSelectionTolerance * pixelScale.first; + double cpSelectionTolerance = kControlPointSelectionTolerance * pixelScale.first; + + _imp->ui->lastTabletDownTriggeredEraser = false; + if (_imp->isPaintByDefault && (pen == ePenTypeEraser || pen == ePenTypePen || pen == ePenTypeCursor)) { + if ( (pen == ePenTypeEraser) && (_imp->ui->selectedTool != eRotoToolEraserBrush) ) { + _imp->ui->setCurrentTool(_imp->ui->eraserAction.lock()); + _imp->ui->lastTabletDownTriggeredEraser = true; + } + } + + boost::shared_ptr context = node->getRotoContext(); + assert(context); + + const bool featherVisible = _imp->ui->isFeatherVisible(); + + //////////////////BEZIER SELECTION + /////Check if the point is nearby a bezier + ///tolerance for bezier selection + double bezierSelectionTolerance = kBezierSelectionTolerance * pixelScale.first; + double nearbyBezierT; + int nearbyBezierCPIndex; + bool isFeather; + boost::shared_ptr nearbyBezier = + context->isNearbyBezier(pos.x(), pos.y(), bezierSelectionTolerance, &nearbyBezierCPIndex, &nearbyBezierT, &isFeather); + std::pair, boost::shared_ptr > nearbyCP; + int nearbyCpIndex = -1; + if (nearbyBezier) { + /////////////////CONTROL POINT SELECTION + //////Check if the point is nearby a control point of a selected bezier + ///Find out if the user selected a control point + + Bezier::ControlPointSelectionPrefEnum pref = Bezier::eControlPointSelectionPrefWhateverFirst; + if ( (_imp->ui->selectedTool == eRotoToolSelectFeatherPoints) && featherVisible ) { + pref = Bezier::eControlPointSelectionPrefFeatherFirst; + } + + nearbyCP = nearbyBezier->isNearbyControlPoint(pos.x(), pos.y(), cpSelectionTolerance, pref, &nearbyCpIndex); + } + + ////////////////// TANGENT SELECTION + ///in all cases except cusp/smooth if a control point is selected, check if the user clicked on a tangent handle + ///in which case we go into eEventStateDraggingTangent mode + if ( !nearbyCP.first && + ( _imp->ui->selectedTool != eRotoToolCuspPoints) && + ( _imp->ui->selectedTool != eRotoToolSmoothPoints) && + ( _imp->ui->selectedTool != eRotoToolSelectCurves) ) { + for (SelectedCPs::iterator it = _imp->ui->selectedCps.begin(); it != _imp->ui->selectedCps.end(); ++it) { + if ( (_imp->ui->selectedTool == eRotoToolSelectAll) || + ( _imp->ui->selectedTool == eRotoToolDrawBezier) ) { + int ret = it->first->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), tangentSelectionTol); + if (ret >= 0) { + _imp->ui->tangentBeingDragged = it->first; + _imp->ui->state = ret == 0 ? eEventStateDraggingLeftTangent : eEventStateDraggingRightTangent; + didSomething = true; + } else { + ///try with the counter part point + if (it->second) { + ret = it->second->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), tangentSelectionTol); + } + if (ret >= 0) { + _imp->ui->tangentBeingDragged = it->second; + _imp->ui->state = ret == 0 ? eEventStateDraggingLeftTangent : eEventStateDraggingRightTangent; + didSomething = true; + } + } + } else if (_imp->ui->selectedTool == eRotoToolSelectFeatherPoints) { + const boost::shared_ptr & fp = it->first->isFeatherPoint() ? it->first : it->second; + int ret = fp->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), tangentSelectionTol); + if (ret >= 0) { + _imp->ui->tangentBeingDragged = fp; + _imp->ui->state = ret == 0 ? eEventStateDraggingLeftTangent : eEventStateDraggingRightTangent; + didSomething = true; + } + } else if (_imp->ui->selectedTool == eRotoToolSelectPoints) { + const boost::shared_ptr & cp = it->first->isFeatherPoint() ? it->second : it->first; + int ret = cp->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), tangentSelectionTol); + if (ret >= 0) { + _imp->ui->tangentBeingDragged = cp; + _imp->ui->state = ret == 0 ? eEventStateDraggingLeftTangent : eEventStateDraggingRightTangent; + didSomething = true; + } + } + + ///check in case this is a feather tangent + if ( _imp->ui->tangentBeingDragged && _imp->ui->tangentBeingDragged->isFeatherPoint() && !featherVisible ) { + _imp->ui->tangentBeingDragged.reset(); + _imp->ui->state = eEventStateNone; + didSomething = false; + } + + if (didSomething) { + return didSomething; + } + } + } + + + switch (_imp->ui->selectedTool) { + case eRotoToolSelectAll: + case eRotoToolSelectPoints: + case eRotoToolSelectFeatherPoints: { + if ( ( _imp->ui->selectedTool == eRotoToolSelectFeatherPoints) && !featherVisible ) { + ///nothing to do + break; + } + std::pair, boost::shared_ptr > featherBarSel; + if ( ( ( _imp->ui->selectedTool == eRotoToolSelectAll) || ( _imp->ui->selectedTool == eRotoToolSelectFeatherPoints) ) ) { + featherBarSel = _imp->ui->isNearbyFeatherBar(time, pixelScale, pos); + if ( featherBarSel.first && !featherVisible ) { + featherBarSel.first.reset(); + featherBarSel.second.reset(); + } + } + + + if (nearbyBezier) { + ///check if the user clicked nearby the cross hair of the selection rectangle in which case + ///we drag all the control points selected + if (nearbyCP.first) { + _imp->ui->handleControlPointSelection(nearbyCP); + _imp->ui->handleBezierSelection(nearbyBezier); + if ( pen == ePenTypeRMB ) { + _imp->ui->state = eEventStateNone; + _imp->ui->showMenuForControlPoint(nearbyCP.first); + } + didSomething = true; + } else if (featherBarSel.first) { + _imp->ui->clearCPSSelection(); + _imp->ui->featherBarBeingDragged = featherBarSel; + + ///Also select the point only if the curve is the same! + if (featherBarSel.first->getBezier() == nearbyBezier) { + _imp->ui->handleControlPointSelection(_imp->ui->featherBarBeingDragged); + _imp->ui->handleBezierSelection(nearbyBezier); + } + _imp->ui->state = eEventStateDraggingFeatherBar; + didSomething = true; + } else { + bool found = false; + for (SelectedItems::iterator it = _imp->ui->selectedItems.begin(); it != _imp->ui->selectedItems.end(); ++it) { + if ( it->get() == nearbyBezier.get() ) { + found = true; + break; + } + } + if (!found) { + _imp->ui->handleBezierSelection(nearbyBezier); + } + if (pen == ePenTypeLMB) { + if ( _imp->ui->ctrlDown && _imp->ui->altDown && !_imp->ui->shiftDown ) { + pushUndoCommand( new AddPointUndoCommand(_imp->ui, nearbyBezier, nearbyBezierCPIndex, nearbyBezierT) ); + _imp->ui->evaluateOnPenUp = true; + } else { + _imp->ui->state = eEventStateDraggingSelectedControlPoints; + _imp->ui->bezierBeingDragged = nearbyBezier; + } + } else if ( pen == ePenTypeRMB ) { + _imp->ui->showMenuForCurve(nearbyBezier); + } + didSomething = true; + } + } else { + if (featherBarSel.first) { + _imp->ui->clearCPSSelection(); + _imp->ui->featherBarBeingDragged = featherBarSel; + _imp->ui->handleControlPointSelection(_imp->ui->featherBarBeingDragged); + _imp->ui->state = eEventStateDraggingFeatherBar; + didSomething = true; + } + if (_imp->ui->state == eEventStateNone) { + _imp->ui->state = _imp->ui->isMouseInteractingWithCPSBbox(pos, cpSelectionTolerance, pixelScale); + if (_imp->ui->state != eEventStateNone) { + didSomething = true; + } + } + } + break; + } + case eRotoToolSelectCurves: + + if (nearbyBezier) { + ///If the bezier is already selected and we re-click on it, change the transform mode + bool found = false; + for (SelectedItems::iterator it = _imp->ui->selectedItems.begin(); it != _imp->ui->selectedItems.end(); ++it) { + if ( it->get() == nearbyBezier.get() ) { + found = true; + break; + } + } + if (!found) { + _imp->ui->handleBezierSelection(nearbyBezier); + } + if ( pen == ePenTypeRMB ) { + _imp->ui->showMenuForCurve(nearbyBezier); + } else { + if ( _imp->ui->ctrlDown && _imp->ui->altDown && !_imp->ui->shiftDown ) { + pushUndoCommand( new AddPointUndoCommand(_imp->ui, nearbyBezier, nearbyBezierCPIndex, nearbyBezierT) ); + _imp->ui->evaluateOnPenUp = true; + } + } + didSomething = true; + } else { + if (_imp->ui->state == eEventStateNone) { + _imp->ui->state = _imp->ui->isMouseInteractingWithCPSBbox(pos, cpSelectionTolerance, pixelScale); + if (_imp->ui->state != eEventStateNone) { + didSomething = true; + } + } + } + break; + case eRotoToolAddPoints: + ///If the user clicked on a bezier and this bezier is selected add a control point by + ///splitting up the targeted segment + if (nearbyBezier) { + bool found = false; + for (SelectedItems::iterator it = _imp->ui->selectedItems.begin(); it != _imp->ui->selectedItems.end(); ++it) { + if ( it->get() == nearbyBezier.get() ) { + found = true; + break; + } + } + if (found) { + ///check that the point is not too close to an existing point + if (nearbyCP.first) { + _imp->ui->handleControlPointSelection(nearbyCP); + } else { + pushUndoCommand( new AddPointUndoCommand(_imp->ui, nearbyBezier, nearbyBezierCPIndex, nearbyBezierT) ); + _imp->ui->evaluateOnPenUp = true; + } + didSomething = true; + } + } + break; + case eRotoToolRemovePoints: + if (nearbyCP.first) { + assert( nearbyBezier && nearbyBezier == nearbyCP.first->getBezier() ); + if ( nearbyCP.first->isFeatherPoint() ) { + pushUndoCommand( new RemovePointUndoCommand(_imp->ui, nearbyBezier, nearbyCP.second) ); + } else { + pushUndoCommand( new RemovePointUndoCommand(_imp->ui, nearbyBezier, nearbyCP.first) ); + } + didSomething = true; + } + break; + case eRotoToolRemoveFeatherPoints: + if (nearbyCP.first) { + assert(nearbyBezier); + std::list datas; + RemoveFeatherUndoCommand::RemoveFeatherData data; + data.curve = nearbyBezier; + data.newPoints.push_back(nearbyCP.first->isFeatherPoint() ? nearbyCP.first : nearbyCP.second); + datas.push_back(data); + pushUndoCommand( new RemoveFeatherUndoCommand(_imp->ui, datas) ); + didSomething = true; + } + break; + case eRotoToolOpenCloseCurve: + if (nearbyBezier) { + pushUndoCommand( new OpenCloseUndoCommand(_imp->ui, nearbyBezier) ); + didSomething = true; + } + break; + case eRotoToolSmoothPoints: + + if (nearbyCP.first) { + std::list datas; + SmoothCuspUndoCommand::SmoothCuspCurveData data; + data.curve = nearbyBezier; + data.newPoints.push_back(nearbyCP); + datas.push_back(data); + pushUndoCommand( new SmoothCuspUndoCommand(_imp->ui, datas, time, false, pixelScale) ); + didSomething = true; + } + break; + case eRotoToolCuspPoints: + if ( nearbyCP.first && getNode()->getRotoContext()->isAutoKeyingEnabled() ) { + std::list datas; + SmoothCuspUndoCommand::SmoothCuspCurveData data; + data.curve = nearbyBezier; + data.newPoints.push_back(nearbyCP); + datas.push_back(data); + pushUndoCommand( new SmoothCuspUndoCommand(_imp->ui, datas, time, true, pixelScale) ); + didSomething = true; + } + break; + case eRotoToolDrawBezier: + case eRotoToolOpenBezier: { + if ( _imp->ui->builtBezier && _imp->ui->builtBezier->isCurveFinished() ) { + _imp->ui->builtBezier.reset(); + _imp->ui->clearSelection(); + _imp->ui->setCurrentTool(_imp->ui->selectAllAction.lock()); + return true; + } + if (_imp->ui->builtBezier) { + ///if the user clicked on a control point of the bezier, select the point instead. + ///if that point is the starting point of the curve, close the curve + const std::list > & cps = _imp->ui->builtBezier->getControlPoints(); + int i = 0; + for (std::list >::const_iterator it = cps.begin(); it != cps.end(); ++it, ++i) { + double x, y; + (*it)->getPositionAtTime(true, time, ViewIdx(0), &x, &y); + if ( ( x >= (pos.x() - cpSelectionTolerance) ) && ( x <= (pos.x() + cpSelectionTolerance) ) && + ( y >= (pos.y() - cpSelectionTolerance) ) && ( y <= (pos.y() + cpSelectionTolerance) ) ) { + if ( it == cps.begin() ) { + pushUndoCommand( new OpenCloseUndoCommand(_imp->ui, _imp->ui->builtBezier) ); + + _imp->ui->builtBezier.reset(); + + _imp->ui->selectedCps.clear(); + _imp->ui->setCurrentTool(_imp->ui->selectAllAction.lock()); + } else { + boost::shared_ptr fp = _imp->ui->builtBezier->getFeatherPointAtIndex(i); + assert(fp); + _imp->ui->handleControlPointSelection(std::make_pair(*it, fp)); + } + + return true; + } + } + } + + bool isOpenBezier = _imp->ui->selectedTool == eRotoToolOpenBezier; + MakeBezierUndoCommand* cmd = new MakeBezierUndoCommand(_imp->ui, _imp->ui->builtBezier, isOpenBezier, true, pos.x(), pos.y(), time); + pushUndoCommand(cmd); + _imp->ui->builtBezier = cmd->getCurve(); + assert(_imp->ui->builtBezier); + _imp->ui->state = eEventStateBuildingBezierControlPointTangent; + didSomething = true; + break; + } + case eRotoToolDrawBSpline: + + break; + case eRotoToolDrawEllipse: { + _imp->ui->click = pos; + pushUndoCommand( new MakeEllipseUndoCommand(_imp->ui, true, false, false, pos.x(), pos.y(), pos.x(), pos.y(), time) ); + _imp->ui->state = eEventStateBuildingEllipse; + didSomething = true; + break; + } + case eRotoToolDrawRectangle: { + _imp->ui->click = pos; + pushUndoCommand( new MakeRectangleUndoCommand(_imp->ui, true, false, false, pos.x(), pos.y(), pos.x(), pos.y(), time) ); + _imp->ui->evaluateOnPenUp = true; + _imp->ui->state = eEventStateBuildingRectangle; + didSomething = true; + break; + } + case eRotoToolSolidBrush: + case eRotoToolEraserBrush: + case eRotoToolClone: + case eRotoToolReveal: + case eRotoToolBlur: + case eRotoToolSharpen: + case eRotoToolSmear: + case eRotoToolDodge: + case eRotoToolBurn: { + if ( ( ( _imp->ui->selectedTool == eRotoToolClone) || ( _imp->ui->selectedTool == eRotoToolReveal) ) && _imp->ui->ctrlDown && + !_imp->ui->shiftDown && !_imp->ui->altDown) { + _imp->ui->state = eEventStateDraggingCloneOffset; + } else if ( _imp->ui->shiftDown && !_imp->ui->ctrlDown && !_imp->ui->altDown) { + _imp->ui->state = eEventStateDraggingBrushSize; + _imp->ui->mouseCenterOnSizeChange = pos; + } else if ( _imp->ui->ctrlDown && _imp->ui->shiftDown ) { + _imp->ui->state = eEventStateDraggingBrushOpacity; + _imp->ui->mouseCenterOnSizeChange = pos; + } else { + /* + Check that all viewers downstream are connected directly to the RotoPaint to avoid glitches and bugs + */ + _imp->ui->checkViewersAreDirectlyConnected(); + bool multiStrokeEnabled = _imp->ui->isMultiStrokeEnabled(); + if ( _imp->ui->strokeBeingPaint && + _imp->ui->strokeBeingPaint->getParentLayer() && + multiStrokeEnabled ) { + boost::shared_ptr layer = _imp->ui->strokeBeingPaint->getParentLayer(); + if (!layer) { + layer = context->findDeepestSelectedLayer(); + if (!layer) { + layer = context->getOrCreateBaseLayer(); + } + assert(layer); + context->addItem(layer, 0, _imp->ui->strokeBeingPaint, RotoItem::eSelectionReasonOther); + } + + context->getNode()->getApp()->setUserIsPainting(context->getNode(), _imp->ui->strokeBeingPaint, true); + + boost::shared_ptr lifeTimeFrameKnob = _imp->ui->strokeBeingPaint->getLifeTimeFrameKnob(); + lifeTimeFrameKnob->setValue( context->getTimelineCurrentTime() ); + + _imp->ui->strokeBeingPaint->appendPoint( true, RotoPoint(pos.x(), pos.y(), pressure, timestamp) ); + } else { + if ( _imp->ui->strokeBeingPaint && + !_imp->ui->strokeBeingPaint->getParentLayer() && + multiStrokeEnabled ) { + _imp->ui->strokeBeingPaint.reset(); + } + _imp->ui->makeStroke( false, RotoPoint(pos.x(), pos.y(), pressure, timestamp) ); + } + context->evaluateChange(); + _imp->ui->state = eEventStateBuildingStroke; + setCurrentCursor(eCursorBlank); + } + didSomething = true; + break; + } + default: + assert(false); + break; + } // switch + + _imp->ui->lastClickPos = pos; + _imp->ui->lastMousePos = pos; + + return didSomething; +} // penDown + +bool +RotoPaint::onOverlayPenMotion(double time, const RenderScale & /*renderScale*/, ViewIdx /*view*/, + const QPointF & /*viewportPos*/, const QPointF & pos, double pressure, double timestamp) +{ + std::pair pixelScale; + getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + + boost::shared_ptr context = getNode()->getRotoContext(); + assert(context); + if (!context) { + return false; + } + bool didSomething = false; + HoverStateEnum lastHoverState = _imp->ui->hoverState; + ///Set the cursor to the appropriate case + bool cursorSet = false; + double cpTol = kControlPointSelectionTolerance * pixelScale.first; + + if ( context->isRotoPaint() && + ( ( _imp->ui->selectedRole == eRotoRoleMergeBrush) || + ( _imp->ui->selectedRole == eRotoRoleCloneBrush) || + ( _imp->ui->selectedRole == eRotoRolePaintBrush) || + ( _imp->ui->selectedRole == eRotoRoleEffectBrush) ) ) { + if (_imp->ui->state != eEventStateBuildingStroke) { + setCurrentCursor(eCursorCross); + } else { + setCurrentCursor(eCursorBlank); + } + didSomething = true; + cursorSet = true; + } + + if ( !cursorSet && _imp->ui->showCpsBbox && (_imp->ui->state != eEventStateDraggingControlPoint) && (_imp->ui->state != eEventStateDraggingSelectedControlPoints) + && ( _imp->ui->state != eEventStateDraggingLeftTangent) && + ( _imp->ui->state != eEventStateDraggingRightTangent) ) { + double bboxTol = cpTol; + if ( _imp->ui->isNearbyBBoxBtmLeft(pos, bboxTol, pixelScale) ) { + _imp->ui->hoverState = eHoverStateBboxBtmLeft; + didSomething = true; + } else if ( _imp->ui->isNearbyBBoxBtmRight(pos, bboxTol, pixelScale) ) { + _imp->ui->hoverState = eHoverStateBboxBtmRight; + didSomething = true; + } else if ( _imp->ui->isNearbyBBoxTopRight(pos, bboxTol, pixelScale) ) { + _imp->ui->hoverState = eHoverStateBboxTopRight; + didSomething = true; + } else if ( _imp->ui->isNearbyBBoxTopLeft(pos, bboxTol, pixelScale) ) { + _imp->ui->hoverState = eHoverStateBboxTopLeft; + didSomething = true; + } else if ( _imp->ui->isNearbyBBoxMidTop(pos, bboxTol, pixelScale) ) { + _imp->ui->hoverState = eHoverStateBboxMidTop; + didSomething = true; + } else if ( _imp->ui->isNearbyBBoxMidRight(pos, bboxTol, pixelScale) ) { + _imp->ui->hoverState = eHoverStateBboxMidRight; + didSomething = true; + } else if ( _imp->ui->isNearbyBBoxMidBtm(pos, bboxTol, pixelScale) ) { + _imp->ui->hoverState = eHoverStateBboxMidBtm; + didSomething = true; + } else if ( _imp->ui->isNearbyBBoxMidLeft(pos, bboxTol, pixelScale) ) { + _imp->ui->hoverState = eHoverStateBboxMidLeft; + didSomething = true; + } else { + _imp->ui->hoverState = eHoverStateNothing; + didSomething = true; + } + } + const bool featherVisible = _imp->ui->isFeatherVisible(); + + if ( (_imp->ui->state == eEventStateNone) && (_imp->ui->hoverState == eHoverStateNothing) ) { + if ( (_imp->ui->state != eEventStateDraggingControlPoint) && (_imp->ui->state != eEventStateDraggingSelectedControlPoints) ) { + + for (SelectedItems::const_iterator it = _imp->ui->selectedItems.begin(); it != _imp->ui->selectedItems.end(); ++it) { + int index = -1; + Bezier* isBezier = dynamic_cast( it->get() ); + if (isBezier) { + std::pair, boost::shared_ptr > nb = + isBezier->isNearbyControlPoint(pos.x(), pos.y(), cpTol, Bezier::eControlPointSelectionPrefWhateverFirst, &index); + if ( (index != -1) && ( ( !nb.first->isFeatherPoint() && !featherVisible ) || featherVisible ) ) { + setCurrentCursor(eCursorCross); + cursorSet = true; + break; + } + } + } + } + if ( !cursorSet && (_imp->ui->state != eEventStateDraggingLeftTangent) && (_imp->ui->state != eEventStateDraggingRightTangent) ) { + ///find a nearby tangent + for (SelectedCPs::const_iterator it = _imp->ui->selectedCps.begin(); it != _imp->ui->selectedCps.end(); ++it) { + if (it->first->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), cpTol) != -1) { + setCurrentCursor(eCursorCross); + cursorSet = true; + break; + } + } + } + if ( !cursorSet && (_imp->ui->state != eEventStateDraggingControlPoint) && (_imp->ui->state != eEventStateDraggingSelectedControlPoints) && (_imp->ui->state != eEventStateDraggingLeftTangent) && + ( _imp->ui->state != eEventStateDraggingRightTangent) ) { + double bezierSelectionTolerance = kBezierSelectionTolerance * pixelScale.first; + double nearbyBezierT; + int nearbyBezierCPIndex; + bool isFeather; + boost::shared_ptr nearbyBezier = + context->isNearbyBezier(pos.x(), pos.y(), bezierSelectionTolerance, &nearbyBezierCPIndex, &nearbyBezierT, &isFeather); + if ( isFeather && !featherVisible ) { + nearbyBezier.reset(); + } + if (nearbyBezier) { + setCurrentCursor(eCursorPointingHand); + cursorSet = true; + } + } + + bool clickAnywhere = _imp->ui->isBboxClickAnywhereEnabled(); + + if ( !cursorSet && (_imp->ui->selectedCps.size() > 1) ) { + if ( ( clickAnywhere && _imp->ui->isWithinSelectedCpsBBox(pos) ) || + ( !clickAnywhere && _imp->ui->isNearbySelectedCpsCrossHair(pos) ) ) { + setCurrentCursor(eCursorSizeAll); + cursorSet = true; + } + } + + SelectedCP nearbyFeatherBar; + if ( !cursorSet && featherVisible ) { + nearbyFeatherBar = _imp->ui->isNearbyFeatherBar(time, pixelScale, pos); + if (nearbyFeatherBar.first && nearbyFeatherBar.second) { + _imp->ui->featherBarBeingHovered = nearbyFeatherBar; + } + } + if (!nearbyFeatherBar.first || !nearbyFeatherBar.second) { + _imp->ui->featherBarBeingHovered.first.reset(); + _imp->ui->featherBarBeingHovered.second.reset(); + } + + if ( (_imp->ui->state != eEventStateNone) || _imp->ui->featherBarBeingHovered.first || cursorSet || (lastHoverState != eHoverStateNothing) ) { + didSomething = true; + } + } + + + if (!cursorSet) { + setCurrentCursor(eCursorDefault); + } + + + double dx = pos.x() - _imp->ui->lastMousePos.x(); + double dy = pos.y() - _imp->ui->lastMousePos.y(); + switch (_imp->ui->state) { + case eEventStateDraggingSelectedControlPoints: { + if (_imp->ui->bezierBeingDragged) { + SelectedCPs cps; + const std::list >& c = _imp->ui->bezierBeingDragged->getControlPoints(); + const std::list >& f = _imp->ui->bezierBeingDragged->getFeatherPoints(); + assert( c.size() == f.size() || !_imp->ui->bezierBeingDragged->useFeatherPoints() ); + bool useFeather = _imp->ui->bezierBeingDragged->useFeatherPoints(); + std::list >::const_iterator itFp = f.begin(); + for (std::list >::const_iterator itCp = c.begin(); itCp != c.end(); ++itCp) { + if (useFeather) { + cps.push_back( std::make_pair(*itCp, *itFp) ); + ++itFp; + } else { + cps.push_back( std::make_pair( *itCp, boost::shared_ptr() ) ); + } + } + pushUndoCommand( new MoveControlPointsUndoCommand(_imp->ui, cps, dx, dy, time) ); + } else { + pushUndoCommand( new MoveControlPointsUndoCommand(_imp->ui, _imp->ui->selectedCps, dx, dy, time) ); + } + _imp->ui->evaluateOnPenUp = true; + _imp->ui->computeSelectedCpsBBOX(); + didSomething = true; + break; + } + case eEventStateDraggingControlPoint: { + assert(_imp->ui->cpBeingDragged.first); + std::list toDrag; + toDrag.push_back(_imp->ui->cpBeingDragged); + pushUndoCommand( new MoveControlPointsUndoCommand(_imp->ui, toDrag, dx, dy, time) ); + _imp->ui->evaluateOnPenUp = true; + _imp->ui->computeSelectedCpsBBOX(); + didSomething = true; + }; break; + case eEventStateBuildingBezierControlPointTangent: { + assert(_imp->ui->builtBezier); + bool isOpenBezier = _imp->ui->selectedTool == eRotoToolOpenBezier; + pushUndoCommand( new MakeBezierUndoCommand(_imp->ui, _imp->ui->builtBezier, isOpenBezier, false, dx, dy, time) ); + break; + } + case eEventStateBuildingEllipse: { + bool fromCenter = _imp->ui->ctrlDown > 0; + bool constrained = _imp->ui->shiftDown > 0; + pushUndoCommand( new MakeEllipseUndoCommand(_imp->ui, false, fromCenter, constrained, _imp->ui->click.x(), _imp->ui->click.y(), pos.x(), pos.y(), time) ); + + didSomething = true; + _imp->ui->evaluateOnPenUp = true; + break; + } + case eEventStateBuildingRectangle: { + bool fromCenter = _imp->ui->ctrlDown > 0; + bool constrained = _imp->ui->shiftDown > 0; + pushUndoCommand( new MakeRectangleUndoCommand(_imp->ui, false, fromCenter, constrained, _imp->ui->click.x(), _imp->ui->click.y(), pos.x(), pos.y(), time) ); + didSomething = true; + _imp->ui->evaluateOnPenUp = true; + break; + } + case eEventStateDraggingLeftTangent: { + assert(_imp->ui->tangentBeingDragged); + pushUndoCommand( new MoveTangentUndoCommand( _imp->ui, dx, dy, time, _imp->ui->tangentBeingDragged, true, + _imp->ui->ctrlDown && !_imp->ui->shiftDown && !_imp->ui->altDown ) ); + _imp->ui->evaluateOnPenUp = true; + didSomething = true; + break; + } + case eEventStateDraggingRightTangent: { + assert(_imp->ui->tangentBeingDragged); + pushUndoCommand( new MoveTangentUndoCommand( _imp->ui, dx, dy, time, _imp->ui->tangentBeingDragged, false, + _imp->ui->ctrlDown && !_imp->ui->shiftDown && !_imp->ui->altDown ) ); + _imp->ui->evaluateOnPenUp = true; + didSomething = true; + break; + } + case eEventStateDraggingFeatherBar: { + pushUndoCommand( new MoveFeatherBarUndoCommand(_imp->ui, dx, dy, _imp->ui->featherBarBeingDragged, time) ); + _imp->ui->evaluateOnPenUp = true; + didSomething = true; + break; + } + case eEventStateDraggingBBoxTopLeft: + case eEventStateDraggingBBoxTopRight: + case eEventStateDraggingBBoxBtmRight: + case eEventStateDraggingBBoxBtmLeft: { + QPointF center = _imp->ui->getSelectedCpsBBOXCenter(); + double rot = 0; + double sx = 1., sy = 1.; + + if (_imp->ui->transformMode == eSelectedCpsTransformModeRotateAndSkew) { + double angle = std::atan2( pos.y() - center.y(), pos.x() - center.x() ); + double prevAngle = std::atan2( _imp->ui->lastMousePos.y() - center.y(), _imp->ui->lastMousePos.x() - center.x() ); + rot = angle - prevAngle; + } else { + // the scale ratio is the ratio of distances to the center + double prevDist = ( _imp->ui->lastMousePos.x() - center.x() ) * ( _imp->ui->lastMousePos.x() - center.x() ) + + ( _imp->ui->lastMousePos.y() - center.y() ) * ( _imp->ui->lastMousePos.y() - center.y() ); + if (prevDist != 0) { + double dist = ( pos.x() - center.x() ) * ( pos.x() - center.x() ) + ( pos.y() - center.y() ) * ( pos.y() - center.y() ); + double ratio = std::sqrt(dist / prevDist); + sx *= ratio; + sy *= ratio; + } + } + + double tx = 0., ty = 0.; + double skewX = 0., skewY = 0.; + pushUndoCommand( new TransformUndoCommand(_imp->ui, center.x(), center.y(), rot, skewX, skewY, tx, ty, sx, sy, time) ); + _imp->ui->evaluateOnPenUp = true; + didSomething = true; + break; + } + case eEventStateDraggingBBoxMidTop: + case eEventStateDraggingBBoxMidBtm: + case eEventStateDraggingBBoxMidLeft: + case eEventStateDraggingBBoxMidRight: { + QPointF center = _imp->ui->getSelectedCpsBBOXCenter(); + double rot = 0; + double sx = 1., sy = 1.; + double skewX = 0., skewY = 0.; + double tx = 0., ty = 0.; + TransformUndoCommand::TransformPointsSelectionEnum type = TransformUndoCommand::eTransformAllPoints; + if ( !_imp->ui->shiftDown ) { + type = TransformUndoCommand::eTransformAllPoints; + } else { + if (_imp->ui->state == eEventStateDraggingBBoxMidTop) { + type = TransformUndoCommand::eTransformMidTop; + } else if (_imp->ui->state == eEventStateDraggingBBoxMidBtm) { + type = TransformUndoCommand::eTransformMidBottom; + } else if (_imp->ui->state == eEventStateDraggingBBoxMidLeft) { + type = TransformUndoCommand::eTransformMidLeft; + } else if (_imp->ui->state == eEventStateDraggingBBoxMidRight) { + type = TransformUndoCommand::eTransformMidRight; + } + } + + const QRectF& bbox = _imp->ui->selectedCpsBbox; + + switch (type) { + case TransformUndoCommand::eTransformMidBottom: + center.rx() = bbox.center().x(); + center.ry() = bbox.top(); + break; + case TransformUndoCommand::eTransformMidTop: + center.rx() = bbox.center().x(); + center.ry() = bbox.bottom(); + break; + case TransformUndoCommand::eTransformMidRight: + center.rx() = bbox.left(); + center.ry() = bbox.center().y(); + break; + case TransformUndoCommand::eTransformMidLeft: + center.rx() = bbox.right(); + center.ry() = bbox.center().y(); + break; + default: + break; + } + + bool processX = _imp->ui->state == eEventStateDraggingBBoxMidRight || _imp->ui->state == eEventStateDraggingBBoxMidLeft; + + if (_imp->ui->transformMode == eSelectedCpsTransformModeRotateAndSkew) { + if (!processX) { + const double addSkew = ( pos.x() - _imp->ui->lastMousePos.x() ) / ( pos.y() - center.y() ); + skewX += addSkew; + } else { + const double addSkew = ( pos.y() - _imp->ui->lastMousePos.y() ) / ( pos.x() - center.x() ); + skewY += addSkew; + } + } else { + // the scale ratio is the ratio of distances to the center + double prevDist = ( _imp->ui->lastMousePos.x() - center.x() ) * ( _imp->ui->lastMousePos.x() - center.x() ) + + ( _imp->ui->lastMousePos.y() - center.y() ) * ( _imp->ui->lastMousePos.y() - center.y() ); + if (prevDist != 0) { + double dist = ( pos.x() - center.x() ) * ( pos.x() - center.x() ) + ( pos.y() - center.y() ) * ( pos.y() - center.y() ); + double ratio = std::sqrt(dist / prevDist); + if (processX) { + sx *= ratio; + } else { + sy *= ratio; + } + } + } + + + pushUndoCommand( new TransformUndoCommand(_imp->ui, center.x(), center.y(), rot, skewX, skewY, tx, ty, sx, sy, time) ); + _imp->ui->evaluateOnPenUp = true; + didSomething = true; + break; + } + case eEventStateBuildingStroke: { + if (_imp->ui->strokeBeingPaint) { + RotoPoint p(pos.x(), pos.y(), pressure, timestamp); + if ( _imp->ui->strokeBeingPaint->appendPoint(false, p) ) { + _imp->ui->lastMousePos = pos; + context->evaluateChange_noIncrement(); + + return true; + } + } + break; + } + case eEventStateDraggingCloneOffset: { + _imp->ui->cloneOffset.first -= dx; + _imp->ui->cloneOffset.second -= dy; + _imp->ui->onBreakMultiStrokeTriggered(); + break; + } + case eEventStateDraggingBrushSize: { + boost::shared_ptr sizeSb = _imp->ui->sizeSpinbox.lock(); + double size = 0; + if (sizeSb) { + size = sizeSb->getValue(); + } + size += ( (dx + dy) / 2. ); + if (sizeSb) { + sizeSb->setValue( std::max(1., size) ); + } + _imp->ui->onBreakMultiStrokeTriggered(); + didSomething = true; + break; + } + case eEventStateDraggingBrushOpacity: { + boost::shared_ptr opaSb = _imp->ui->sizeSpinbox.lock(); + double opa = 0; + if (opaSb) { + opa = opaSb->getValue(); + } + double newOpa = opa + ( (dx + dy) / 2. ); + if (opa != 0) { + newOpa = std::max( 0., std::min(1., newOpa / opa) ); + newOpa = newOpa > 0 ? std::min(1., opa + 0.05) : std::max(0., opa - 0.05); + } else { + newOpa = newOpa < 0 ? .0 : 0.05; + } + if (opaSb) { + opaSb->setValue(newOpa); + } + _imp->ui->onBreakMultiStrokeTriggered(); + didSomething = true; + break; + } + case eEventStateNone: + default: + break; + } // switch + _imp->ui->lastMousePos = pos; + + return didSomething; +} // onOverlayPenMotion + +bool +RotoPaint::onOverlayPenUp(double /*time*/, const RenderScale & /*renderScale*/, ViewIdx /*view*/, const QPointF & /*viewportPos*/, const QPointF & /*pos*/, double /*pressure*/, double /*timestamp*/) +{ + boost::shared_ptr context = getNode()->getRotoContext(); + assert(context); + if (!context) { + return false; + } + + if (_imp->ui->evaluateOnPenUp) { + context->evaluateChange(); + getApp()->triggerAutoSave(); + + //sync other viewers linked to this roto + redrawOverlayInteract(); + _imp->ui->evaluateOnPenUp = false; + } + _imp->ui->tangentBeingDragged.reset(); + _imp->ui->bezierBeingDragged.reset(); + _imp->ui->cpBeingDragged.first.reset(); + _imp->ui->cpBeingDragged.second.reset(); + _imp->ui->featherBarBeingDragged.first.reset(); + _imp->ui->featherBarBeingDragged.second.reset(); + + if ( (_imp->ui->state == eEventStateDraggingBBoxMidLeft) || + ( _imp->ui->state == eEventStateDraggingBBoxMidLeft) || + ( _imp->ui->state == eEventStateDraggingBBoxMidTop) || + ( _imp->ui->state == eEventStateDraggingBBoxMidBtm) || + ( _imp->ui->state == eEventStateDraggingBBoxTopLeft) || + ( _imp->ui->state == eEventStateDraggingBBoxTopRight) || + ( _imp->ui->state == eEventStateDraggingBBoxBtmRight) || + ( _imp->ui->state == eEventStateDraggingBBoxBtmLeft) ) { + _imp->ui->computeSelectedCpsBBOX(); + } + + if (_imp->ui->state == eEventStateBuildingStroke) { + assert(_imp->ui->strokeBeingPaint); + context->getNode()->getApp()->setUserIsPainting(context->getNode(), _imp->ui->strokeBeingPaint, false); + assert( _imp->ui->strokeBeingPaint->getParentLayer() ); + + bool multiStrokeEnabled = _imp->ui->isMultiStrokeEnabled(); + if ( !multiStrokeEnabled ) { + pushUndoCommand( new AddStrokeUndoCommand(_imp->ui, _imp->ui->strokeBeingPaint) ); + _imp->ui->makeStroke( true, RotoPoint() ); + } else { + pushUndoCommand( new AddMultiStrokeUndoCommand(_imp->ui, _imp->ui->strokeBeingPaint) ); + } + + /** + * Do a neat render for the stroke (better interpolation). This call is blocking otherwise the user might + * attempt to make a new stroke while the previous stroke is not finished... this would yield artifacts. + **/ + setCurrentCursor(eCursorBusy); + context->evaluateNeatStrokeRender(); + setCurrentCursor(eCursorDefault); + _imp->ui->strokeBeingPaint->setStrokeFinished(); + + } + + _imp->ui->state = eEventStateNone; + + if ( (_imp->ui->selectedTool == eRotoToolDrawEllipse) || (_imp->ui->selectedTool == eRotoToolDrawRectangle) ) { + _imp->ui->selectedCps.clear(); + _imp->ui->setCurrentTool(_imp->ui->selectAllAction.lock()); + } + + if (_imp->ui->lastTabletDownTriggeredEraser) { + _imp->ui->setCurrentTool(_imp->ui->lastPaintToolAction.lock()); + } + + return true; +} // onOverlayPenUp + +bool +RotoPaint::onOverlayKeyDown(double /*time*/, const RenderScale & /*renderScale*/, ViewIdx /*view*/, Key key, KeyboardModifiers /*modifiers*/) +{ + + + bool didSomething = false; + + if (key == Key_Shift_L || key == Key_Shift_R) { + ++_imp->ui->shiftDown; + } else if (key == Key_Control_L || key == Key_Control_R) { + ++_imp->ui->ctrlDown; + } else if (key == Key_Alt_L || key == Key_Alt_R) { + ++_imp->ui->altDown; + } + + if ( _imp->ui->ctrlDown && !_imp->ui->shiftDown && !_imp->ui->altDown ) { + if ( !_imp->ui->iSelectingwithCtrlA && _imp->ui->showCpsBbox && (key == Key_Control_L || key == Key_Control_R) ) { + _imp->ui->transformMode = _imp->ui->transformMode == eSelectedCpsTransformModeTranslateAndScale ? + eSelectedCpsTransformModeRotateAndSkew : eSelectedCpsTransformModeTranslateAndScale; + didSomething = true; + } + } + + /*if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoDelete, modifiers, key) ) { + ///if control points are selected, delete them, otherwise delete the selected beziers + if ( !_imp->rotoData->selectedCps.empty() ) { + pushUndoCommand( new RemovePointUndoCommand(this, _imp->rotoData->selectedCps) ); + didSomething = true; + } else if ( !_imp->rotoData->selectedItems.empty() ) { + pushUndoCommand( new RemoveCurveUndoCommand(this, _imp->rotoData->selectedItems) ); + didSomething = true; + } + } else if ( ( (key == Qt::Key_Escape) && ( (_imp->selectedTool == eRotoToolDrawBezier) || (_imp->selectedTool == eRotoToolOpenBezier) ) ) || isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoCloseBezier, modifiers, key) ) { + if ( ( (_imp->selectedTool == eRotoToolDrawBezier) || (_imp->selectedTool == eRotoToolOpenBezier) ) && _imp->rotoData->builtBezier && !_imp->rotoData->builtBezier->isCurveFinished() ) { + pushUndoCommand( new OpenCloseUndoCommand(this, _imp->rotoData->builtBezier) ); + + _imp->rotoData->builtBezier.reset(); + _imp->rotoData->selectedCps.clear(); + onToolActionTriggered(_imp->selectAllAction); + _imp->context->evaluateChange(); + didSomething = true; + } + } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoSelectAll, modifiers, key) ) { + _imp->iSelectingwithCtrlA = true; + ///if no bezier are selected, select all beziers + if ( _imp->rotoData->selectedItems.empty() ) { + std::list > bez = _imp->context->getCurvesByRenderOrder(); + for (std::list >::const_iterator it = bez.begin(); it != bez.end(); ++it) { + _imp->context->select(*it, RotoItem::eSelectionReasonOverlayInteract); + _imp->rotoData->selectedItems.push_back(*it); + } + } else { + ///select all the control points of all selected beziers + _imp->rotoData->selectedCps.clear(); + for (SelectedItems::iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { + Bezier* isBezier = dynamic_cast( it->get() ); + if (!isBezier) { + continue; + } + const std::list > & cps = isBezier->getControlPoints(); + const std::list > & fps = isBezier->getFeatherPoints(); + assert( cps.size() == fps.size() ); + + std::list >::const_iterator cpIT = cps.begin(); + for (std::list >::const_iterator fpIT = fps.begin(); fpIT != fps.end(); ++fpIT, ++cpIT) { + _imp->rotoData->selectedCps.push_back( std::make_pair(*cpIT, *fpIT) ); + } + } + _imp->computeSelectedCpsBBOX(); + } + didSomething = true; + } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoSelectionTool, modifiers, key) ) { + _imp->selectTool->handleSelection(); + didSomething = true; + } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoEditTool, modifiers, key) ) { + if (_imp->bezierEditionTool) { + _imp->bezierEditionTool->handleSelection(); + didSomething = true; + } + } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoAddTool, modifiers, key) ) { + if (_imp->pointsEditionTool) { + _imp->pointsEditionTool->handleSelection(); + didSomething = true; + } + } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoBrushTool, modifiers, key) ) { + if (_imp->paintBrushTool) { + _imp->paintBrushTool->handleSelection(); + didSomething = true; + } + } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoCloneTool, modifiers, key) ) { + if (_imp->cloneBrushTool) { + _imp->cloneBrushTool->handleSelection(); + didSomething = true; + } + } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoEffectTool, modifiers, key) ) { + if (_imp->effectBrushTool) { + _imp->effectBrushTool->handleSelection(); + didSomething = true; + } + } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoColorTool, modifiers, key) ) { + if (_imp->mergeBrushTool) { + _imp->mergeBrushTool->handleSelection(); + didSomething = true; + } + }*/ + + return didSomething; +} //onOverlayKeyDown + +bool +RotoPaint::onOverlayKeyUp(double /*time*/, const RenderScale & /*renderScale*/, ViewIdx /*view*/, Key key, KeyboardModifiers /*modifiers*/) +{ + bool didSomething = false; + + if (key == Key_Shift_L || key == Key_Shift_R) { + --_imp->ui->shiftDown; + } else if (key == Key_Control_L || key == Key_Control_R) { + --_imp->ui->ctrlDown; + } else if (key == Key_Alt_L || key == Key_Alt_R) { + --_imp->ui->altDown; + } + + + + if ( !_imp->ui->ctrlDown ) { + if ( !_imp->ui->iSelectingwithCtrlA && _imp->ui->showCpsBbox && (key == Key_Control_L || key == Key_Control_R) ) { + _imp->ui->transformMode = (_imp->ui->transformMode == eSelectedCpsTransformModeTranslateAndScale ? + eSelectedCpsTransformModeRotateAndSkew : eSelectedCpsTransformModeTranslateAndScale); + didSomething = true; + } + } + + if ( (key == Key_Control_L || key == Key_Control_R) && _imp->ui->iSelectingwithCtrlA ) { + _imp->ui->iSelectingwithCtrlA = false; + } + + if (_imp->ui->evaluateOnKeyUp) { + getNode()->getRotoContext()->evaluateChange(); + getNode()->getApp()->triggerAutoSave(); + redrawOverlayInteract(); + _imp->ui->evaluateOnKeyUp = false; + } + + return didSomething; +} // onOverlayKeyUp + +bool +RotoPaint::onOverlayKeyRepeat(double /*time*/, const RenderScale & /*renderScale*/, ViewIdx /*view*/, Key /*key*/, KeyboardModifiers /*modifiers*/) +{ + + return false; +} // onOverlayKeyRepeat + +bool +RotoPaint::onOverlayFocusGained(double /*time*/, const RenderScale & /*renderScale*/, ViewIdx /*view*/) +{ + return false; +} // onOverlayFocusGained + +bool +RotoPaint::onOverlayFocusLost(double /*time*/, const RenderScale & /*renderScale*/, ViewIdx /*view*/) +{ + _imp->ui->shiftDown = 0; + _imp->ui->ctrlDown = 0; + _imp->ui->altDown = 0; + return true; +} // onOverlayFocusLost + + + +void +RotoPaint::onRefreshAsked() +{ + redrawOverlayInteract(); +} + + +void +RotoPaint::onCurveLockedChanged(int reason) +{ + boost::shared_ptr item = getNode()->getRotoContext()->getLastItemLocked(); + + if ( item && ( (RotoItem::SelectionReasonEnum)reason != RotoItem::eSelectionReasonOverlayInteract ) ) { + assert(item); + bool changed = false; + if (item) { + _imp->ui->onCurveLockedChangedRecursive(item, &changed); + } + + if (changed) { + redrawOverlayInteract(); + } + } +} + +void +RotoPaint::onBreakMultiStrokeTriggered() +{ + _imp->ui->onBreakMultiStrokeTriggered(); +} + + +void +RotoPaint::onSelectionChanged(int reason) +{ + if ( (RotoItem::SelectionReasonEnum)reason != RotoItem::eSelectionReasonOverlayInteract ) { + _imp->ui->selectedItems = getNode()->getRotoContext()->getSelectedCurves(); + redrawOverlayInteract(); + } +} + NATRON_NAMESPACE_EXIT; +NATRON_NAMESPACE_USING; +#include "moc_Rotopaint.cpp" diff --git a/Engine/RotoPaint.h b/Engine/RotoPaint.h index 2525cc8741..3d1f8fe3fe 100644 --- a/Engine/RotoPaint.h +++ b/Engine/RotoPaint.h @@ -41,6 +41,11 @@ struct RotoPaintPrivate; class RotoPaint : public EffectInstance { + + GCC_DIAG_SUGGEST_OVERRIDE_OFF + Q_OBJECT + GCC_DIAG_SUGGEST_OVERRIDE_ON + public: static EffectInstance* BuildEffect(NodePtr n) @@ -124,8 +129,46 @@ class RotoPaint virtual bool isHostChannelSelectorSupported(bool* defaultR, bool* defaultG, bool* defaultB, bool* defaultA) const OVERRIDE WARN_UNUSED_RETURN; + virtual void onKnobsLoaded() OVERRIDE FINAL; + + +public Q_SLOTS: + + + void onRefreshAsked(); + + void onCurveLockedChanged(int); + + void onSelectionChanged(int reason); + + void onBreakMultiStrokeTriggered(); + private: + virtual void getPluginShortcuts(std::list* shortcuts) OVERRIDE FINAL; + + virtual void drawOverlay(double time, const RenderScale & renderScale, ViewIdx view) OVERRIDE FINAL; + virtual bool onOverlayPenDown(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, PenType pen) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenMotion(double time, const RenderScale & renderScale, ViewIdx view, + const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenUp(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenDoubleClicked(double time, + const RenderScale & renderScale, + ViewIdx view, + const QPointF & viewportPos, + const QPointF & pos) OVERRIDE FINAL WARN_UNUSED_RETURN; + + + virtual bool onOverlayKeyDown(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; + virtual bool onOverlayKeyUp(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; + virtual bool onOverlayKeyRepeat(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; + virtual bool onOverlayFocusGained(double time, const RenderScale & renderScale, ViewIdx view) OVERRIDE FINAL; + virtual bool onOverlayFocusLost(double time, const RenderScale & renderScale, ViewIdx view) OVERRIDE FINAL; + + virtual void onInteractViewportSelectionCleared() OVERRIDE FINAL; + + virtual void onInteractViewportSelectionUpdated(const RectD& rectangle, bool onRelease) OVERRIDE FINAL; + virtual void knobChanged(KnobI* k, ValueChangedReasonEnum reason, ViewSpec view, @@ -147,6 +190,9 @@ class RotoPaint ViewIdx* inputView, int* inputNb) OVERRIDE FINAL WARN_UNUSED_RETURN; virtual StatusEnum render(const RenderActionArgs& args) OVERRIDE WARN_UNUSED_RETURN; + + virtual void refreshExtraStateAfterTimeChanged(double time) OVERRIDE FINAL; + boost::scoped_ptr _imp; }; diff --git a/Engine/RotoPaintInteract.cpp b/Engine/RotoPaintInteract.cpp new file mode 100644 index 0000000000..429fa173f0 --- /dev/null +++ b/Engine/RotoPaintInteract.cpp @@ -0,0 +1,1843 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * This file is part of Natron , + * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat + * + * Natron is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Natron is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Natron. If not, see + * ***** END LICENSE BLOCK ***** */ + +// ***** BEGIN PYTHON BLOCK ***** +// from : +// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." +#include +// ***** END PYTHON BLOCK ***** + + +#include "RotoPaintInteract.h" + +#include "Global/GLIncludes.h" +#include "Engine/KnobTypes.h" +#include "Engine/RotoPaint.h" +#include "Engine/MergingEnum.h" +#include "Engine/RotoPoint.h" +#include "Engine/RotoContext.h" +#include "Engine/RotoStrokeItem.h" +#include "Engine/RotoUndoCommand.h" +#include "Engine/RotoLayer.h" +#include "Engine/Bezier.h" +#include "Engine/Transform.h" +#include "Engine/AppInstance.h" +#include "Engine/ViewerInstance.h" + +NATRON_NAMESPACE_ENTER; + +RotoPaintPrivate::RotoPaintPrivate(RotoPaint* publicInterface,bool isPaintByDefault) +: publicInterface(publicInterface) +, isPaintByDefault(isPaintByDefault) +, premultKnob() +, enabledKnobs() +, ui(new RotoPaintInteract(this)) +{ +} + +RotoPaintInteract::RotoPaintInteract(RotoPaintPrivate* p) +: p(p) +, selectedItems() +, selectedCps() +, selectedCpsBbox() +, showCpsBbox(false) +, transformMode() +, builtBezier() +, bezierBeingDragged() +, cpBeingDragged() +, tangentBeingDragged() +, featherBarBeingDragged() +, featherBarBeingHovered() +, strokeBeingPaint() +, cloneOffset() +, click() +, selectedTool(eRotoToolSelectAll) +, selectedRole(eRotoRoleSelection) +, state(eEventStateNone) +, hoverState(eHoverStateNothing) +, lastClickPos() +, lastMousePos() +, evaluateOnPenUp(false) +, evaluateOnKeyUp(false) +, iSelectingwithCtrlA(false) +, shiftDown(0) +, ctrlDown(0) +, altDown(0) +, lastTabletDownTriggeredEraser(false) +, mouseCenterOnSizeChange() +{ + cloneOffset.first = cloneOffset.second = 0.; + +} + +void +RotoPaintInteract::evaluate(bool redraw) +{ + if (redraw) { + p->publicInterface->redrawOverlayInteract(); + } + p->publicInterface->getNode()->getRotoContext()->evaluateChange(); + p->publicInterface->getApp()->triggerAutoSave(); +} + +void +RotoPaintInteract::autoSaveAndRedraw() +{ + p->publicInterface->redrawOverlayInteract(); + p->publicInterface->getApp()->triggerAutoSave(); +} + +void +RotoPaintInteract::redrawOverlays() +{ + p->publicInterface->redrawOverlayInteract(); +} + +boost::shared_ptr +RotoPaintInteract::getContext() +{ + return p->publicInterface->getNode()->getRotoContext(); +} + +bool +RotoPaintInteract::isFeatherVisible() const +{ + boost::shared_ptr b = displayFeatherEnabledButton.lock(); + if (b) { + return b->getValue(); + } else { + return true; + } +} + +bool +RotoPaintInteract::isStickySelectionEnabled() const +{ + boost::shared_ptr b = stickySelectionEnabledButton.lock(); + if (b) { + return b->getValue(); + } else { + return false; + } +} + +bool +RotoPaintInteract::isMultiStrokeEnabled() const +{ + boost::shared_ptr b = multiStrokeEnabled.lock(); + if (b) { + return b->getValue(); + } else { + return false; + } +} + +bool +RotoPaintInteract::isBboxClickAnywhereEnabled() const +{ + boost::shared_ptr b = bboxClickAnywhereButton.lock(); + return b ? b->getValue() : false; +} + + +void +RotoPaintInteract::drawSelectedCp(double time, + const boost::shared_ptr & cp, + double x, + double y, + const Transform::Matrix3x3& transform) +{ + ///if the tangent is being dragged, color it + bool colorLeftTangent = false; + bool colorRightTangent = false; + + if ( (cp == tangentBeingDragged) && + ( ( state == eEventStateDraggingLeftTangent) || ( state == eEventStateDraggingRightTangent) ) ) { + colorLeftTangent = state == eEventStateDraggingLeftTangent ? true : false; + colorRightTangent = !colorLeftTangent; + } + + + Transform::Point3D leftDeriv, rightDeriv; + leftDeriv.z = rightDeriv.z = 1.; + cp->getLeftBezierPointAtTime(true, time, ViewIdx(0), &leftDeriv.x, &leftDeriv.y); + cp->getRightBezierPointAtTime(true, time, ViewIdx(0), &rightDeriv.x, &rightDeriv.y); + leftDeriv = Transform::matApply(transform, leftDeriv); + rightDeriv = Transform::matApply(transform, rightDeriv); + + bool drawLeftHandle = leftDeriv.x != x || leftDeriv.y != y; + bool drawRightHandle = rightDeriv.y != x || rightDeriv.y != y; + glBegin(GL_POINTS); + if (drawLeftHandle) { + if (colorLeftTangent) { + glColor3f(0.2, 1., 0.); + } + glVertex2d(leftDeriv.x, leftDeriv.y); + if (colorLeftTangent) { + glColor3d(0.85, 0.67, 0.); + } + } + if (drawRightHandle) { + if (colorRightTangent) { + glColor3f(0.2, 1., 0.); + } + glVertex2d(rightDeriv.x, rightDeriv.y); + if (colorRightTangent) { + glColor3d(0.85, 0.67, 0.); + } + } + glEnd(); + + glBegin(GL_LINE_STRIP); + if (drawLeftHandle) { + glVertex2d(leftDeriv.x, leftDeriv.y); + } + glVertex2d(x, y); + if (drawRightHandle) { + glVertex2d(rightDeriv.x, rightDeriv.y); + } + glEnd(); +} // drawSelectedCp + +void +RotoPaintInteract::drawEllipse(double x, + double y, + double radiusX, + double radiusY, + int l, + double r, + double g, + double b, + double a) +{ + glColor3f(r * l * a, g * l * a, b * l * a); + + glPushMatrix(); + // center the oval at x_center, y_center + glTranslatef( (float)x, (float)y, 0.f ); + // draw the oval using line segments + glBegin(GL_LINE_LOOP); + // we don't need to be pixel-perfect here, it's just an interact! + // 40 segments is enough. + double m = 2 * 3.14159265358979323846264338327950288419717 / 40.; + for (int i = 0; i < 40; ++i) { + double theta = i * m; + glVertex2d( radiusX * std::cos(theta), radiusY * std::sin(theta) ); + } + glEnd(); + + glPopMatrix(); +} + +void +RotoPaintInteract::drawArrow(double centerX, + double centerY, + double rotate, + bool hovered, + const std::pair & pixelScale) +{ + GLProtectMatrix p(GL_MODELVIEW); + + if (hovered) { + glColor3f(0., 1., 0.); + } else { + glColor3f(1., 1., 1.); + } + + double arrowLenght = kTransformArrowLenght * pixelScale.second; + double arrowWidth = kTransformArrowWidth * pixelScale.second; + double arrowHeadHeight = 4 * pixelScale.second; + + glTranslatef(centerX, centerY, 0.); + glRotatef(rotate, 0., 0., 1.); + QPointF bottom(0., -arrowLenght); + QPointF top(0, arrowLenght); + ///the arrow head is 4 pixels long and kTransformArrowWidth * 2 large + glBegin(GL_LINES); + glVertex2f( top.x(), top.y() ); + glVertex2f( bottom.x(), bottom.y() ); + glEnd(); + + glBegin(GL_POLYGON); + glVertex2f( bottom.x(), bottom.y() ); + glVertex2f(bottom.x() + arrowWidth, bottom.y() + arrowHeadHeight); + glVertex2f(bottom.x() - arrowWidth, bottom.y() + arrowHeadHeight); + glEnd(); + + glBegin(GL_POLYGON); + glVertex2f( top.x(), top.y() ); + glVertex2f(top.x() - arrowWidth, top.y() - arrowHeadHeight); + glVertex2f(top.x() + arrowWidth, top.y() - arrowHeadHeight); + glEnd(); +} + +void +RotoPaintInteract::drawBendedArrow(double centerX, + double centerY, + double rotate, + bool hovered, + const std::pair & pixelScale) +{ + GLProtectMatrix p(GL_MODELVIEW); + + if (hovered) { + glColor3f(0., 1., 0.); + } else { + glColor3f(1., 1., 1.); + } + + double arrowLenght = kTransformArrowLenght * pixelScale.second; + double arrowWidth = kTransformArrowWidth * pixelScale.second; + double arrowHeadHeight = 4 * pixelScale.second; + + glTranslatef(centerX, centerY, 0.); + glRotatef(rotate, 0., 0., 1.); + + /// by default we draw the top left + QPointF bottom(0., -arrowLenght / 2.); + QPointF right(arrowLenght / 2., 0.); + glBegin (GL_LINE_STRIP); + glVertex2f ( bottom.x(), bottom.y() ); + glVertex2f (0., 0.); + glVertex2f ( right.x(), right.y() ); + glEnd (); + + glBegin(GL_POLYGON); + glVertex2f(bottom.x(), bottom.y() - arrowHeadHeight); + glVertex2f( bottom.x() - arrowWidth, bottom.y() ); + glVertex2f( bottom.x() + arrowWidth, bottom.y() ); + glEnd(); + + glBegin(GL_POLYGON); + glVertex2f( right.x() + arrowHeadHeight, right.y() ); + glVertex2f(right.x(), right.y() - arrowWidth); + glVertex2f(right.x(), right.y() + arrowWidth); + glEnd(); +} + +void +RotoPaintInteract::drawSelectedCpsBBOX() +{ + std::pair pixelScale; + + p->publicInterface->getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + + { + GLProtectAttrib a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_TRANSFORM_BIT); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + + + QPointF topLeft = selectedCpsBbox.topLeft(); + QPointF btmRight = selectedCpsBbox.bottomRight(); + + glLineWidth(1.5); + + if (hoverState == eHoverStateBbox) { + glColor4f(0.9, 0.5, 0, 1.); + } else { + glColor4f(0.8, 0.8, 0.8, 1.); + } + glBegin(GL_LINE_LOOP); + glVertex2f( topLeft.x(), btmRight.y() ); + glVertex2f( topLeft.x(), topLeft.y() ); + glVertex2f( btmRight.x(), topLeft.y() ); + glVertex2f( btmRight.x(), btmRight.y() ); + glEnd(); + + double midX = ( topLeft.x() + btmRight.x() ) / 2.; + double midY = ( btmRight.y() + topLeft.y() ) / 2.; + double xHairMidSizeX = kXHairSelectedCpsBox * pixelScale.first; + double xHairMidSizeY = kXHairSelectedCpsBox * pixelScale.second; + QLineF selectedCpsCrossHorizLine; + selectedCpsCrossHorizLine.setLine(midX - xHairMidSizeX, midY, midX + xHairMidSizeX, midY); + QLineF selectedCpsCrossVertLine; + selectedCpsCrossVertLine.setLine(midX, midY - xHairMidSizeY, midX, midY + xHairMidSizeY); + + glBegin(GL_LINES); + glVertex2f( std::max( selectedCpsCrossHorizLine.p1().x(), topLeft.x() ), selectedCpsCrossHorizLine.p1().y() ); + glVertex2f( std::min( selectedCpsCrossHorizLine.p2().x(), btmRight.x() ), selectedCpsCrossHorizLine.p2().y() ); + glVertex2f( selectedCpsCrossVertLine.p1().x(), std::max( selectedCpsCrossVertLine.p1().y(), btmRight.y() ) ); + glVertex2f( selectedCpsCrossVertLine.p2().x(), std::min( selectedCpsCrossVertLine.p2().y(), topLeft.y() ) ); + glEnd(); + + glCheckError(); + + + QPointF midTop( ( topLeft.x() + btmRight.x() ) / 2., topLeft.y() ); + QPointF midRight(btmRight.x(), ( topLeft.y() + btmRight.y() ) / 2.); + QPointF midBtm( ( topLeft.x() + btmRight.x() ) / 2., btmRight.y() ); + QPointF midLeft(topLeft.x(), ( topLeft.y() + btmRight.y() ) / 2.); + + ///draw the 4 corners points and the 4 mid points + glPointSize(5.f); + glBegin(GL_POINTS); + glVertex2f( topLeft.x(), topLeft.y() ); + glVertex2f( btmRight.x(), topLeft.y() ); + glVertex2f( btmRight.x(), btmRight.y() ); + glVertex2f( topLeft.x(), btmRight.y() ); + + glVertex2f( midTop.x(), midTop.y() ); + glVertex2f( midRight.x(), midRight.y() ); + glVertex2f( midBtm.x(), midBtm.y() ); + glVertex2f( midLeft.x(), midLeft.y() ); + glEnd(); + + ///now draw the handles to indicate the user he/she can transform the selection rectangle + ///draw it only if it is not dragged + bool drawHandles = state != eEventStateDraggingBBoxBtmLeft && state != eEventStateDraggingBBoxBtmRight && + state != eEventStateDraggingBBoxTopLeft && state != eEventStateDraggingBBoxTopRight && state != eEventStateDraggingBBoxMidTop + && state != eEventStateDraggingBBoxMidRight && state != eEventStateDraggingBBoxMidLeft && state != eEventStateDraggingBBoxMidBtm; + + + if (drawHandles) { + double offset = kTransformArrowOffsetFromPoint * pixelScale.first; + double halfOffset = offset / 2.; + if (transformMode == eSelectedCpsTransformModeTranslateAndScale) { + ///draw mid top arrow vertical + drawArrow(midTop.x(), midTop.y() + offset, 0., hoverState == eHoverStateBboxMidTop, pixelScale); + ///draw mid right arrow horizontal + drawArrow(midRight.x() + offset, midRight.y(), 90., hoverState == eHoverStateBboxMidRight, pixelScale); + ///draw mid btm arrow vertical + drawArrow(midBtm.x(), midBtm.y() - offset, 0., hoverState == eHoverStateBboxMidBtm, pixelScale); + ///draw mid left arrow horizontal + drawArrow(midLeft.x() - offset, midLeft.y(), 90., hoverState == eHoverStateBboxMidLeft, pixelScale); + ///draw top left arrow rotated + drawArrow(topLeft.x() - offset, topLeft.y() + offset, 45., hoverState == eHoverStateBboxTopLeft, pixelScale); + ///draw top right arrow rotated + drawArrow(btmRight.x() + offset, topLeft.y() + offset, -45., hoverState == eHoverStateBboxTopRight, pixelScale); + ///draw btm right arrow rotated + drawArrow(btmRight.x() + offset, btmRight.y() - offset, 45., hoverState == eHoverStateBboxBtmRight, pixelScale); + ///draw btm left arrow rotated + drawArrow(topLeft.x() - offset, btmRight.y() - offset, -45., hoverState == eHoverStateBboxBtmLeft, pixelScale); + } else { + ///draw mid top arrow horizontal + drawArrow(midTop.x(), midTop.y() + offset, 90., hoverState == eHoverStateBboxMidTop, pixelScale); + ///draw mid right arrow vertical + drawArrow(midRight.x() + offset, midRight.y(), 0., hoverState == eHoverStateBboxMidRight, pixelScale); + ///draw mid btm arrow horizontal + drawArrow(midBtm.x(), midBtm.y() - offset, 90., hoverState == eHoverStateBboxMidBtm, pixelScale); + ///draw mid left arrow vertical + drawArrow(midLeft.x() - offset, midLeft.y(), 0., hoverState == eHoverStateBboxMidLeft, pixelScale); + ///draw the top left bended + drawBendedArrow(topLeft.x() - halfOffset, topLeft.y() + halfOffset, 0., hoverState == eHoverStateBboxTopLeft, pixelScale); + ///draw the top right bended + drawBendedArrow(btmRight.x() + halfOffset, topLeft.y() + halfOffset, -90, hoverState == eHoverStateBboxTopRight, pixelScale); + ///draw the btm right bended + drawBendedArrow(btmRight.x() + halfOffset, btmRight.y() - halfOffset, -180, hoverState == eHoverStateBboxBtmRight, pixelScale); + ///draw the btm left bended + drawBendedArrow(topLeft.x() - halfOffset, btmRight.y() - halfOffset, 90, hoverState == eHoverStateBboxBtmLeft, pixelScale); + } + } + } // GLProtectAttrib a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT); +} // drawSelectedCpsBBOX + + + +void +RotoPaintInteract::clearSelection() +{ + clearBeziersSelection(); + clearCPSSelection(); +} + +bool +RotoPaintInteract::hasSelection() const +{ + return !selectedItems.empty() || !selectedCps.empty(); +} + +void +RotoPaintInteract::clearCPSSelection() +{ + selectedCps.clear(); + showCpsBbox = false; + transformMode = eSelectedCpsTransformModeTranslateAndScale; + selectedCpsBbox.setTopLeft( QPointF(0, 0) ); + selectedCpsBbox.setTopRight( QPointF(0, 0) ); +} + +void +RotoPaintInteract::clearBeziersSelection() +{ + boost::shared_ptr ctx = p->publicInterface->getNode()->getRotoContext(); + assert(ctx); + ctx->clearSelection(RotoItem::eSelectionReasonOverlayInteract); + selectedItems.clear(); +} + +bool +RotoPaintInteract::removeItemFromSelection(const boost::shared_ptr& b) +{ + boost::shared_ptr ctx = p->publicInterface->getNode()->getRotoContext(); + assert(ctx); + for (SelectedItems::iterator fb = selectedItems.begin(); fb != selectedItems.end(); ++fb) { + if ( fb->get() == b.get() ) { + ctx->deselect(*fb, RotoItem::eSelectionReasonOverlayInteract); + selectedItems.erase(fb); + + return true; + } + } + + return false; +} + + +static void +handleControlPointMaximum(double time, + const BezierCP & p, + double* l, + double *b, + double *r, + double *t) +{ + double x, y, xLeft, yLeft, xRight, yRight; + + p.getPositionAtTime(true, time, ViewIdx(0), &x, &y); + p.getLeftBezierPointAtTime(true, time, ViewIdx(0), &xLeft, &yLeft); + p.getRightBezierPointAtTime(true, time, ViewIdx(0), &xRight, &yRight); + + *r = std::max(x, *r); + *l = std::min(x, *l); + + *r = std::max(xLeft, *r); + *l = std::min(xLeft, *l); + + *r = std::max(xRight, *r); + *l = std::min(xRight, *l); + + *t = std::max(y, *t); + *b = std::min(y, *b); + + *t = std::max(yLeft, *t); + *b = std::min(yLeft, *b); + + + *t = std::max(yRight, *t); + *b = std::min(yRight, *b); +} + +bool +RotoPaintInteract::getRoleForGroup(const boost::shared_ptr& k, RotoRoleEnum* role) const +{ + bool ret = true; + if (k == selectToolGroup.lock()) { + *role = eRotoRoleSelection; + } else if (k == pointsEditionToolGroup.lock()) { + *role = eRotoRolePointsEdition; + } else if (k == bezierEditionToolGroup.lock()) { + *role = eRotoRoleBezierEdition; + } else if (k == paintBrushToolGroup.lock()) { + *role = eRotoRolePaintBrush; + } else if (k == cloneBrushToolGroup.lock()) { + *role = eRotoRoleCloneBrush; + } else if (k == effectBrushToolGroup.lock()) { + *role = eRotoRoleEffectBrush; + } else if (k == mergeBrushToolGroup.lock()) { + *role = eRotoRoleMergeBrush; + } else { + ret = false; + } + return ret; +} + +bool +RotoPaintInteract::getToolForAction(const boost::shared_ptr& k,RotoToolEnum* tool) const +{ + bool ret = true; + if (k == selectAllAction.lock()) { + *tool = eRotoToolSelectAll; + } else if (k == selectPointsAction.lock()) { + *tool = eRotoToolSelectPoints; + } else if (k == selectCurvesAction.lock()) { + *tool = eRotoToolSelectCurves; + } else if (k == selectFeatherPointsAction.lock()) { + *tool = eRotoToolSelectFeatherPoints; + } else if (k == addPointsAction.lock()) { + *tool = eRotoToolAddPoints; + } else if (k == removePointsAction.lock()) { + *tool = eRotoToolRemovePoints; + } else if (k == cuspPointsAction.lock()) { + *tool = eRotoToolCuspPoints; + } else if (k == smoothPointsAction.lock()) { + *tool = eRotoToolSmoothPoints; + } else if (k == openCloseCurveAction.lock()) { + *tool = eRotoToolOpenCloseCurve; + } else if (k == removeFeatherAction.lock()) { + *tool = eRotoToolRemoveFeatherPoints; + } else if (k == drawBezierAction.lock()) { + *tool = eRotoToolDrawBezier; + } else if (k == drawEllipseAction.lock()) { + *tool = eRotoToolDrawEllipse; + } else if (k == drawRectangleAction.lock()) { + *tool = eRotoToolDrawRectangle; + } else if (k == brushAction.lock()) { + *tool = eRotoToolSolidBrush; + } else if (k == pencilAction.lock()) { + *tool = eRotoToolOpenBezier; + } else if (k == eraserAction.lock()) { + *tool = eRotoToolEraserBrush; + } else if (k == cloneAction.lock()) { + *tool = eRotoToolClone; + } else if (k == revealAction.lock()) { + *tool = eRotoToolReveal; + } else if (k == blurAction.lock()) { + *tool = eRotoToolBlur; + } else if (k == smearAction.lock()) { + *tool = eRotoToolSmear; + } else if (k == dodgeAction.lock()) { + *tool = eRotoToolDodge; + } else if (k == burnAction.lock()) { + *tool = eRotoToolBurn; + } else { + ret = false; + } + return ret; +} + +bool +RotoPaintInteract::onRoleChangedInternal(const boost::shared_ptr& roleGroup) +{ + RotoRoleEnum role; + if (!getRoleForGroup(roleGroup, &role)) { + return false; + } + + // The gui just deactivated this action + if (!roleGroup->getValue()) { + return true; + } + + bool isPaintRole = (role == eRotoRolePaintBrush) || (role == eRotoRoleCloneBrush) || (role == eRotoRoleMergeBrush) || + (role == eRotoRoleEffectBrush); + + + // Reset the selected control points + selectedCps.clear(); + showCpsBbox = false; + transformMode = eSelectedCpsTransformModeTranslateAndScale; + selectedCpsBbox.setTopLeft( QPointF(0, 0) ); + selectedCpsBbox.setTopRight( QPointF(0, 0) ); + + + // Roto action bar + autoKeyingEnabledButton.lock()->setInViewerContextSecret(isPaintRole); + featherLinkEnabledButton.lock()->setInViewerContextSecret(isPaintRole); + displayFeatherEnabledButton.lock()->setInViewerContextSecret(isPaintRole); + stickySelectionEnabledButton.lock()->setInViewerContextSecret(isPaintRole); + bboxClickAnywhereButton.lock()->setInViewerContextSecret(isPaintRole); + rippleEditEnabledButton.lock()->setInViewerContextSecret(isPaintRole); + addKeyframeButton.lock()->setInViewerContextSecret(isPaintRole); + removeKeyframeButton.lock()->setInViewerContextSecret(isPaintRole); + + // RotoPaint action bar + colorWheelButton.lock()->setInViewerContextSecret(!isPaintRole); + compositingOperatorChoice.lock()->setInViewerContextSecret(!isPaintRole); + opacitySpinbox.lock()->setInViewerContextSecret(!isPaintRole); + pressureOpacityButton.lock()->setInViewerContextSecret(!isPaintRole); + sizeSpinbox.lock()->setInViewerContextSecret(!isPaintRole); + pressureSizeButton.lock()->setInViewerContextSecret(!isPaintRole); + hardnessSpinbox.lock()->setInViewerContextSecret(!isPaintRole); + pressureHardnessButton.lock()->setInViewerContextSecret(!isPaintRole); + buildUpButton.lock()->setInViewerContextSecret(!isPaintRole); + effectSpinBox.lock()->setInViewerContextSecret(!isPaintRole); + timeOffsetSpinBox.lock()->setInViewerContextSecret(!isPaintRole); + timeOffsetModeChoice.lock()->setInViewerContextSecret(!isPaintRole); + sourceTypeChoice.lock()->setInViewerContextSecret(!isPaintRole); + resetCloneOffsetButton.lock()->setInViewerContextSecret(!isPaintRole); + multiStrokeEnabled.lock()->setInViewerContextSecret(!isPaintRole); + + selectedRole = role; + + return true; +} + +bool +RotoPaintInteract::onToolChangedInternal(const boost::shared_ptr& actionButton) +{ + + RotoToolEnum tool; + if (!getToolForAction(actionButton, &tool)) { + return false; + } + + // The gui just deactivated this action + if (!actionButton->getValue()) { + return true; + } + + bool isPaintRole = (selectedRole == eRotoRolePaintBrush) || (selectedRole == eRotoRoleCloneBrush) || (selectedRole == eRotoRoleMergeBrush) || + (selectedRole == eRotoRoleEffectBrush); + if (isPaintRole) { + effectSpinBox.lock()->setInViewerContextSecret(tool != eRotoToolBlur); + timeOffsetModeChoice.lock()->setInViewerContextSecret(selectedRole != eRotoRoleCloneBrush); + timeOffsetSpinBox.lock()->setInViewerContextSecret(selectedRole != eRotoRoleCloneBrush); + sourceTypeChoice.lock()->setInViewerContextSecret(selectedRole != eRotoRoleCloneBrush); + resetCloneOffsetButton.lock()->setInViewerContextSecret(selectedRole != eRotoRoleCloneBrush); + if (tool == eRotoToolClone) { + sourceTypeChoice.lock()->setValue(1); + } else if (tool == eRotoToolReveal) { + sourceTypeChoice.lock()->setValue(2); + } + + if ( (tool == eRotoToolSolidBrush ) || ( tool == eRotoToolOpenBezier ) ) { + compositingOperatorChoice.lock()->setValue( (int)eMergeOver ); + } else if ( tool == eRotoToolBurn ) { + compositingOperatorChoice.lock()->setValue( (int)eMergeColorBurn ); + } else if ( tool == eRotoToolDodge ) { + compositingOperatorChoice.lock()->setValue( (int)eMergeColorDodge ); + } else { + compositingOperatorChoice.lock()->setValue( (int)eMergeCopy ); + } + } + + // Clear all selection if we were building a new bezier + if ( (selectedRole == eRotoRoleBezierEdition) && + ( ( selectedTool == eRotoToolDrawBezier) || ( selectedTool == eRotoToolOpenBezier) ) && + builtBezier && + ( tool != selectedTool ) ) { + builtBezier->setCurveFinished(true); + clearSelection(); + } + + selectedTool = tool; + if ( tool != eRotoToolEraserBrush && isPaintRole) { + lastPaintToolAction = actionButton; + } + + + if ( (selectedTool == eRotoToolBlur) || + ( selectedTool == eRotoToolBurn) || + ( selectedTool == eRotoToolDodge) || + ( selectedTool == eRotoToolClone) || + ( selectedTool == eRotoToolEraserBrush) || + ( selectedTool == eRotoToolSolidBrush) || + ( selectedTool == eRotoToolReveal) || + ( selectedTool == eRotoToolSmear) || + ( selectedTool == eRotoToolSharpen) ) { + makeStroke( true, RotoPoint() ); + } + + return true; +} + + +void +RotoPaintInteract::setCurrentTool(const boost::shared_ptr& tool) +{ + if (!tool) { + return; + } + KnobPtr parentKnob = tool->getParentKnob(); + boost::shared_ptr parentGroup = boost::dynamic_pointer_cast(parentKnob); + assert(parentGroup); + if (!parentGroup) { + return; + } + + + boost::shared_ptr curGroup = selectedToolRole.lock(); + boost::shared_ptr curTool = selectedToolAction.lock(); + if (curGroup) { + curGroup->setValue(false); + } + if (curTool) { + curTool->setValue(false); + } + parentGroup->setValue(true); + tool->setValue(true); +} + + +void +RotoPaintInteract::computeSelectedCpsBBOX() +{ + NodePtr n = p->publicInterface->getNode(); + + if (!n || !n->isActivated()) { + return; + } + + double time = p->publicInterface->getCurrentTime(); + std::pair pixelScale; + + p->publicInterface->getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + + + double l = INT_MAX, r = INT_MIN, b = INT_MAX, t = INT_MIN; + for (SelectedCPs::iterator it = selectedCps.begin(); it != selectedCps.end(); ++it) { + handleControlPointMaximum(time, *(it->first), &l, &b, &r, &t); + if (it->second) { + handleControlPointMaximum(time, *(it->second), &l, &b, &r, &t); + } + } + selectedCpsBbox.setCoords(l, t, r, b); + if (selectedCps.size() > 1) { + showCpsBbox = true; + } else { + showCpsBbox = false; + } +} + +QPointF +RotoPaintInteract::getSelectedCpsBBOXCenter() +{ + return selectedCpsBbox.center(); +} + +void +RotoPaintInteract::handleBezierSelection(const boost::shared_ptr & curve) +{ + ///find out if the bezier is already selected. + bool found = false; + + for (SelectedItems::iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { + if ( it->get() == curve.get() ) { + found = true; + break; + } + } + + if (!found) { + ///clear previous selection if the SHIFT modifier isn't held + if ( !shiftDown ) { + clearBeziersSelection(); + } + selectedItems.push_back(curve); + + boost::shared_ptr ctx = p->publicInterface->getNode()->getRotoContext(); + assert(ctx); + ctx->select(curve, RotoItem::eSelectionReasonOverlayInteract); + } +} + +void +RotoPaintInteract::handleControlPointSelection(const std::pair, + boost::shared_ptr > & p) +{ + ///find out if the cp is already selected. + SelectedCPs::iterator foundCP = selectedCps.end(); + + for (SelectedCPs::iterator it = selectedCps.begin(); it != selectedCps.end(); ++it) { + if (p.first == it->first) { + foundCP = it; + break; + } + } + + if ( foundCP == selectedCps.end() ) { + ///clear previous selection if the SHIFT modifier isn't held + if ( !shiftDown ) { + selectedCps.clear(); + } + selectedCps.push_back(p); + computeSelectedCpsBBOX(); + } else { + ///Erase the point from the selection to allow the user to toggle the selection + if ( shiftDown ) { + selectedCps.erase(foundCP); + computeSelectedCpsBBOX(); + } + } + + cpBeingDragged = p; + state = eEventStateDraggingControlPoint; +} + + +void +RotoPaintInteract::showMenuForControlPoint(const boost::shared_ptr& cp) +{ + + boost::shared_ptr menu = rightClickMenuKnob.lock(); + if (!menu) { + return; + } + std::vector choices; + + choices.push_back(removeItemsMenuAction.lock()->getName()); + choices.push_back(smoothItemMenuAction.lock()->getName()); + choices.push_back(cuspItemMenuAction.lock()->getName()); + choices.push_back(removeItemFeatherMenuAction.lock()->getName()); + + boost::shared_ptr isSlaved = cp->isSlaved(); + if (!isSlaved) { + choices.push_back(linkPointMenuAction.lock()->getName()); + } else { + choices.push_back(unlinkPointMenuAction.lock()->getName()); + } + choices.push_back(nudgeLeftMenuAction.lock()->getName()); + choices.push_back(nudgeBottomMenuAction.lock()->getName()); + choices.push_back(nudgeRightMenuAction.lock()->getName()); + choices.push_back(nudgeTopMenuAction.lock()->getName()); + menu->populateChoices(choices); + +} // showMenuForControlPoint + +void +RotoPaintInteract::showMenuForCurve(const boost::shared_ptr & curve) +{ + boost::shared_ptr menu = rightClickMenuKnob.lock(); + if (!menu) { + return; + } + std::vector choices; + + choices.push_back(selectAllMenuAction.lock()->getName()); + choices.push_back(removeItemsMenuAction.lock()->getName()); + if (!curve->isOpenBezier()) { + choices.push_back(openCloseCurveAction.lock()->getName()); + } + + choices.push_back(smoothItemMenuAction.lock()->getName()); + choices.push_back(cuspItemMenuAction.lock()->getName()); + if (!curve->isOpenBezier()) { + choices.push_back(removeItemFeatherMenuAction.lock()->getName()); + } + choices.push_back(lockShapeMenuAction.lock()->getName()); + menu->populateChoices(choices); +} // showMenuForCurve + +void +RotoPaintInteract::onBreakMultiStrokeTriggered() +{ + makeStroke( true, RotoPoint() ); +} + + + + +static bool +isBranchConnectedToRotoNodeRecursive(Node* node, + const Node* rotoNode, + int* recursion, + std::list& markedNodes) +{ + assert(recursion); + if (!node) { + return false; + } + if (rotoNode == node) { + return true; + } + markedNodes.push_back(node); + int maxInputs = node->getMaxInputCount(); + *recursion = *recursion + 1; + for (int i = 0; i < maxInputs; ++i) { + NodePtr inp = node->getInput(i); + if (inp) { + if ( isBranchConnectedToRotoNodeRecursive(inp.get(), rotoNode, recursion, markedNodes) ) { + return true; + } + } + } + + return false; +} + +void +RotoPaintInteract::checkViewersAreDirectlyConnected() +{ + NodePtr rotoNode = p->publicInterface->getNode(); + std::list viewers; + + rotoNode->hasViewersConnected(&viewers); + for (std::list::iterator it = viewers.begin(); it != viewers.end(); ++it) { + NodePtr viewerNode = (*it)->getNode(); + int maxInputs = viewerNode->getMaxInputCount(); + int hasBranchConnectedToRoto = -1; + for (int i = 0; i < maxInputs; ++i) { + NodePtr input = viewerNode->getInput(i); + if (input) { + std::list markedNodes; + int recursion = 0; + if ( isBranchConnectedToRotoNodeRecursive(input.get(), rotoNode.get(), &recursion, markedNodes) ) { + if (recursion == 0) { + //This viewer is already connected to the Roto node directly. + break; + } + viewerNode->disconnectInput(i); + if (hasBranchConnectedToRoto == -1) { + viewerNode->connectInput(rotoNode, i); + hasBranchConnectedToRoto = i; + } + } + } + } + } +} + +void +RotoPaintInteract::makeStroke(bool prepareForLater, + const RotoPoint& point) +{ + RotoStrokeType strokeType; + std::string itemName; + + switch (selectedTool) { + case eRotoToolSolidBrush: + strokeType = eRotoStrokeTypeSolid; + itemName = kRotoPaintBrushBaseName; + break; + case eRotoToolEraserBrush: + strokeType = eRotoStrokeTypeEraser; + itemName = kRotoPaintEraserBaseName; + break; + case eRotoToolClone: + strokeType = eRotoStrokeTypeClone; + itemName = kRotoPaintCloneBaseName; + break; + case eRotoToolReveal: + strokeType = eRotoStrokeTypeReveal; + itemName = kRotoPaintRevealBaseName; + break; + case eRotoToolBlur: + strokeType = eRotoStrokeTypeBlur; + itemName = kRotoPaintBlurBaseName; + break; + case eRotoToolSharpen: + strokeType = eRotoStrokeTypeSharpen; + itemName = kRotoPaintSharpenBaseName; + break; + case eRotoToolSmear: + strokeType = eRotoStrokeTypeSmear; + itemName = kRotoPaintSmearBaseName; + break; + case eRotoToolDodge: + strokeType = eRotoStrokeTypeDodge; + itemName = kRotoPaintDodgeBaseName; + break; + case eRotoToolBurn: + strokeType = eRotoStrokeTypeBurn; + itemName = kRotoPaintBurnBaseName; + break; + default: + + return; + } + + boost::shared_ptr context = p->publicInterface->getNode()->getRotoContext(); + + + if (prepareForLater || !strokeBeingPaint) { + if ( strokeBeingPaint && + ( strokeBeingPaint->getBrushType() == strokeType) && + strokeBeingPaint->isEmpty() ) { + ///We already have a fresh stroke prepared for that type + return; + } + std::string name = context->generateUniqueName(itemName); + strokeBeingPaint.reset( new RotoStrokeItem( strokeType, context, name, boost::shared_ptr() ) ); + strokeBeingPaint->createNodes(false); + } + + + assert(strokeBeingPaint); + boost::shared_ptr colorKnob = strokeBeingPaint->getColorKnob(); + boost::shared_ptr operatorKnob = strokeBeingPaint->getOperatorKnob(); + boost::shared_ptr opacityKnob = strokeBeingPaint->getOpacityKnob(); + boost::shared_ptr sizeKnob = strokeBeingPaint->getBrushSizeKnob(); + boost::shared_ptr hardnessKnob = strokeBeingPaint->getBrushHardnessKnob(); + boost::shared_ptr pressureOpaKnob = strokeBeingPaint->getPressureOpacityKnob(); + boost::shared_ptr pressureSizeKnob = strokeBeingPaint->getPressureSizeKnob(); + boost::shared_ptr pressureHardnessKnob = strokeBeingPaint->getPressureHardnessKnob(); + boost::shared_ptr buildUpKnob = strokeBeingPaint->getBuildupKnob(); + boost::shared_ptr timeOffsetModeKnob = strokeBeingPaint->getTimeOffsetModeKnob(); + boost::shared_ptr sourceTypeKnob = strokeBeingPaint->getBrushSourceTypeKnob(); + boost::shared_ptr timeOffsetKnob = strokeBeingPaint->getTimeOffsetKnob(); + boost::shared_ptr translateKnob = strokeBeingPaint->getBrushCloneTranslateKnob(); + boost::shared_ptr effectKnob = strokeBeingPaint->getBrushEffectKnob(); + + boost::shared_ptr colorWheel = colorWheelButton.lock(); + double color[4]; + for (int i = 0; i < 3; ++i) { + color[i] = colorWheel->getValue(i); + } + + MergingFunctionEnum compOp = (MergingFunctionEnum)compositingOperatorChoice.lock()->getValue(); + double opacity = opacitySpinbox.lock()->getValue(); + double size = sizeSpinbox.lock()->getValue(); + double hardness = hardnessSpinbox.lock()->getValue(); + bool pressOpa = pressureOpacityButton.lock()->getValue(); + bool pressSize = pressureSizeButton.lock()->getValue(); + bool pressHarness = pressureHardnessButton.lock()->getValue(); + bool buildUp = buildUpButton.lock()->getValue(); + double timeOffset = timeOffsetSpinBox.lock()->getValue(); + double timeOffsetMode_i = timeOffsetModeChoice.lock()->getValue(); + int sourceType_i = sourceTypeChoice.lock()->getValue(); + double effectValue = effectSpinBox.lock()->getValue(); + + + colorKnob->setValues(color[0], color[1], color[2], ViewSpec::all(), eValueChangedReasonNatronGuiEdited); + operatorKnob->setValueFromLabel(Merge::getOperatorString(compOp), 0); + opacityKnob->setValue(opacity); + sizeKnob->setValue(size); + hardnessKnob->setValue(hardness); + pressureOpaKnob->setValue(pressOpa); + pressureSizeKnob->setValue(pressSize); + pressureHardnessKnob->setValue(pressHarness); + buildUpKnob->setValue(buildUp); + effectKnob->setValue(effectValue); + if (!prepareForLater) { + boost::shared_ptr lifeTimeFrameKnob = strokeBeingPaint->getLifeTimeFrameKnob(); + lifeTimeFrameKnob->setValue( context->getTimelineCurrentTime() ); + } + if ( (strokeType == eRotoStrokeTypeClone) || (strokeType == eRotoStrokeTypeReveal) ) { + timeOffsetKnob->setValue(timeOffset); + timeOffsetModeKnob->setValue(timeOffsetMode_i); + sourceTypeKnob->setValue(sourceType_i); + translateKnob->setValues(-cloneOffset.first, -cloneOffset.second, ViewSpec::all(), eValueChangedReasonNatronGuiEdited); + } + if (!prepareForLater) { + boost::shared_ptr layer = context->findDeepestSelectedLayer(); + if (!layer) { + layer = context->getOrCreateBaseLayer(); + } + assert(layer); + context->addItem(layer, 0, strokeBeingPaint, RotoItem::eSelectionReasonOther); + context->getNode()->getApp()->setUserIsPainting(context->getNode(), strokeBeingPaint, true); + strokeBeingPaint->appendPoint(true, point); + } + +} // RotoGui::RotoGuiPrivate::makeStroke + + + +bool +RotoPaintInteract::isNearbySelectedCpsCrossHair(const QPointF & pos) const +{ + std::pair pixelScale; + + p->publicInterface->getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + + double xHairMidSizeX = kXHairSelectedCpsBox * pixelScale.first; + double xHairMidSizeY = kXHairSelectedCpsBox * pixelScale.second; + double l = selectedCpsBbox.topLeft().x(); + double r = selectedCpsBbox.bottomRight().x(); + double b = selectedCpsBbox.bottomRight().y(); + double t = selectedCpsBbox.topLeft().y(); + double toleranceX = kXHairSelectedCpsTolerance * pixelScale.first; + double toleranceY = kXHairSelectedCpsTolerance * pixelScale.second; + double midX = (l + r) / 2.; + double midY = (b + t) / 2.; + double lCross = midX - xHairMidSizeX; + double rCross = midX + xHairMidSizeX; + double bCross = midY - xHairMidSizeY; + double tCross = midY + xHairMidSizeY; + + if ( ( pos.x() >= (lCross - toleranceX) ) && + ( pos.x() <= (rCross + toleranceX) ) && + ( pos.y() <= (tCross + toleranceY) ) && + ( pos.y() >= (bCross - toleranceY) ) ) { + return true; + } else { + return false; + } +} + +bool +RotoPaintInteract::isWithinSelectedCpsBBox(const QPointF& pos) const +{ + // std::pair pixelScale; + // viewer->getPixelScale(pixelScale.first,pixelScale.second); + + double l = selectedCpsBbox.topLeft().x(); + double r = selectedCpsBbox.bottomRight().x(); + double b = selectedCpsBbox.bottomRight().y(); + double t = selectedCpsBbox.topLeft().y(); + double toleranceX = 0;//kXHairSelectedCpsTolerance * pixelScale.first; + double toleranceY = 0;//kXHairSelectedCpsTolerance * pixelScale.second; + + return pos.x() > (l - toleranceX) && pos.x() < (r + toleranceX) && + pos.y() > (b - toleranceY) && pos.y() < (t + toleranceY); +} + +bool +RotoPaintInteract::isNearbyBBoxTopLeft(const QPointF & p, + double tolerance, + const std::pair & pixelScale) const +{ + QPointF corner = selectedCpsBbox.topLeft(); + + if ( ( p.x() >= (corner.x() - tolerance) ) && ( p.x() <= (corner.x() + tolerance) ) && + ( p.y() >= (corner.y() - tolerance) ) && ( p.y() <= (corner.y() + tolerance) ) ) { + return true; + } else { + double halfOffset = kTransformArrowOffsetFromPoint * pixelScale.first / 2.; + double length = kTransformArrowLenght * pixelScale.first; + double halfLength = length / 2.;; + ///test if pos is within the arrow bounding box + QPointF center(corner.x() - halfOffset, corner.y() + halfOffset); + RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); + + return arrowBbox.contains( p.x(), p.y() ); + } +} + +bool +RotoPaintInteract::isNearbyBBoxTopRight(const QPointF & p, + double tolerance, + const std::pair & pixelScale) const +{ + QPointF topLeft = selectedCpsBbox.topLeft(); + QPointF btmRight = selectedCpsBbox.bottomRight(); + QPointF corner( btmRight.x(), topLeft.y() ); + + if ( ( p.x() >= (corner.x() - tolerance) ) && ( p.x() <= (corner.x() + tolerance) ) && + ( p.y() >= (corner.y() - tolerance) ) && ( p.y() <= (corner.y() + tolerance) ) ) { + return true; + } else { + double halfOffset = kTransformArrowOffsetFromPoint * pixelScale.first / 2.; + double length = kTransformArrowLenght * pixelScale.first; + double halfLength = length / 2.;; + ///test if pos is within the arrow bounding box + QPointF center(corner.x() + halfOffset, corner.y() + halfOffset); + RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); + + return arrowBbox.contains( p.x(), p.y() ); + } +} + +bool +RotoPaintInteract::isNearbyBBoxBtmLeft(const QPointF & p, + double tolerance, + const std::pair & pixelScale) const +{ + QPointF topLeft = selectedCpsBbox.topLeft(); + QPointF btmRight = selectedCpsBbox.bottomRight(); + QPointF corner( topLeft.x(), btmRight.y() ); + + if ( ( p.x() >= (corner.x() - tolerance) ) && ( p.x() <= (corner.x() + tolerance) ) && + ( p.y() >= (corner.y() - tolerance) ) && ( p.y() <= (corner.y() + tolerance) ) ) { + return true; + } else { + double halfOffset = kTransformArrowOffsetFromPoint * pixelScale.first / 2.; + double length = kTransformArrowLenght * pixelScale.first; + double halfLength = length / 2.;; + ///test if pos is within the arrow bounding box + QPointF center(corner.x() - halfOffset, corner.y() - halfOffset); + RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); + + return arrowBbox.contains( p.x(), p.y() ); + } +} + +bool +RotoPaintInteract::isNearbyBBoxBtmRight(const QPointF & p, + double tolerance, + const std::pair & pixelScale) const +{ + QPointF corner = selectedCpsBbox.bottomRight(); + + if ( ( p.x() >= (corner.x() - tolerance) ) && ( p.x() <= (corner.x() + tolerance) ) && + ( p.y() >= (corner.y() - tolerance) ) && ( p.y() <= (corner.y() + tolerance) ) ) { + return true; + } else { + double halfOffset = kTransformArrowOffsetFromPoint * pixelScale.first / 2.; + double length = kTransformArrowLenght * pixelScale.first; + double halfLength = length / 2.;; + ///test if pos is within the arrow bounding box + QPointF center(corner.x() + halfOffset, corner.y() - halfOffset); + RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); + + return arrowBbox.contains( p.x(), p.y() ); + } +} + +bool +RotoPaintInteract::isNearbyBBoxMidTop(const QPointF & p, + double tolerance, + const std::pair & pixelScale) const +{ + QPointF topLeft = selectedCpsBbox.topLeft(); + QPointF btmRight = selectedCpsBbox.bottomRight(); + QPointF topRight( btmRight.x(), topLeft.y() ); + QPointF mid = (topLeft + topRight) / 2.; + + if ( ( p.x() >= (mid.x() - tolerance) ) && ( p.x() <= (mid.x() + tolerance) ) && + ( p.y() >= (mid.y() - tolerance) ) && ( p.y() <= (mid.y() + tolerance) ) ) { + return true; + } else { + double offset = kTransformArrowOffsetFromPoint * pixelScale.first; + double length = kTransformArrowLenght * pixelScale.first; + double halfLength = length / 2.; + ///test if pos is within the arrow bounding box + QPointF center(mid.x(), mid.y() + offset); + RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); + + return arrowBbox.contains( p.x(), p.y() ); + } +} + +bool +RotoPaintInteract::isNearbyBBoxMidRight(const QPointF & p, + double tolerance, + const std::pair & pixelScale) const +{ + QPointF topLeft = selectedCpsBbox.topLeft(); + QPointF btmRight = selectedCpsBbox.bottomRight(); + QPointF topRight( btmRight.x(), topLeft.y() ); + QPointF mid = (btmRight + topRight) / 2.; + + if ( ( p.x() >= (mid.x() - tolerance) ) && ( p.x() <= (mid.x() + tolerance) ) && + ( p.y() >= (mid.y() - tolerance) ) && ( p.y() <= (mid.y() + tolerance) ) ) { + return true; + } else { + double offset = kTransformArrowOffsetFromPoint * pixelScale.first; + double length = kTransformArrowLenght * pixelScale.first; + double halfLength = length / 2.;; + ///test if pos is within the arrow bounding box + QPointF center( mid.x() + offset, mid.y() ); + RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); + + return arrowBbox.contains( p.x(), p.y() ); + } +} + +bool +RotoPaintInteract::isNearbyBBoxMidBtm(const QPointF & p, + double tolerance, + const std::pair & pixelScale) const +{ + QPointF topLeft = selectedCpsBbox.topLeft(); + QPointF btmRight = selectedCpsBbox.bottomRight(); + QPointF btmLeft( topLeft.x(), btmRight.y() ); + QPointF mid = (btmRight + btmLeft) / 2.; + + if ( ( p.x() >= (mid.x() - tolerance) ) && ( p.x() <= (mid.x() + tolerance) ) && + ( p.y() >= (mid.y() - tolerance) ) && ( p.y() <= (mid.y() + tolerance) ) ) { + return true; + } else { + double offset = kTransformArrowOffsetFromPoint * pixelScale.first; + double length = kTransformArrowLenght * pixelScale.first; + double halfLength = length / 2.;; + ///test if pos is within the arrow bounding box + QPointF center(mid.x(), mid.y() - offset); + RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); + + return arrowBbox.contains( p.x(), p.y() ); + } +} + +EventStateEnum +RotoPaintInteract::isMouseInteractingWithCPSBbox(const QPointF& pos, + double cpSelectionTolerance, + const std::pair& pixelScale) const +{ + bool clickAnywhere = isBboxClickAnywhereEnabled(); + EventStateEnum state = eEventStateNone; + + if ( showCpsBbox && isNearbyBBoxTopLeft(pos, cpSelectionTolerance, pixelScale) ) { + state = eEventStateDraggingBBoxTopLeft; + } else if ( showCpsBbox && isNearbyBBoxTopRight(pos, cpSelectionTolerance, pixelScale) ) { + state = eEventStateDraggingBBoxTopRight; + } else if ( showCpsBbox && isNearbyBBoxBtmLeft(pos, cpSelectionTolerance, pixelScale) ) { + state = eEventStateDraggingBBoxBtmLeft; + } else if ( showCpsBbox && isNearbyBBoxBtmRight(pos, cpSelectionTolerance, pixelScale) ) { + state = eEventStateDraggingBBoxBtmRight; + } else if ( showCpsBbox && isNearbyBBoxMidTop(pos, cpSelectionTolerance, pixelScale) ) { + state = eEventStateDraggingBBoxMidTop; + } else if ( showCpsBbox && isNearbyBBoxMidRight(pos, cpSelectionTolerance, pixelScale) ) { + state = eEventStateDraggingBBoxMidRight; + } else if ( showCpsBbox && isNearbyBBoxMidBtm(pos, cpSelectionTolerance, pixelScale) ) { + state = eEventStateDraggingBBoxMidBtm; + } else if ( showCpsBbox && isNearbyBBoxMidLeft(pos, cpSelectionTolerance, pixelScale) ) { + state = eEventStateDraggingBBoxMidLeft; + } else if ( clickAnywhere && showCpsBbox && isWithinSelectedCpsBBox(pos) ) { + state = eEventStateDraggingSelectedControlPoints; + } else if ( !clickAnywhere && showCpsBbox && isNearbySelectedCpsCrossHair(pos) ) { + state = eEventStateDraggingSelectedControlPoints; + } + + return state; +} + +bool +RotoPaintInteract::isNearbyBBoxMidLeft(const QPointF & p, + double tolerance, + const std::pair & pixelScale) const +{ + QPointF topLeft = selectedCpsBbox.topLeft(); + QPointF btmRight = selectedCpsBbox.bottomRight(); + QPointF btmLeft( topLeft.x(), btmRight.y() ); + QPointF mid = (topLeft + btmLeft) / 2.; + + if ( ( p.x() >= (mid.x() - tolerance) ) && ( p.x() <= (mid.x() + tolerance) ) && + ( p.y() >= (mid.y() - tolerance) ) && ( p.y() <= (mid.y() + tolerance) ) ) { + return true; + } else { + double offset = kTransformArrowOffsetFromPoint * pixelScale.first; + double length = kTransformArrowLenght * pixelScale.first; + double halfLength = length / 2.;; + ///test if pos is within the arrow bounding box + QPointF center( mid.x() - offset, mid.y() ); + RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); + + return arrowBbox.contains( p.x(), p.y() ); + } +} + +bool +RotoPaintInteract::isNearbySelectedCpsBoundingBox(const QPointF & pos, + double tolerance) const +{ + QPointF topLeft = selectedCpsBbox.topLeft(); + QPointF btmRight = selectedCpsBbox.bottomRight(); + QPointF btmLeft( topLeft.x(), btmRight.y() ); + QPointF topRight( btmRight.x(), topLeft.y() ); + + ///check if it is nearby top edge + if ( ( pos.x() >= (topLeft.x() - tolerance) ) && ( pos.x() <= (topRight.x() + tolerance) ) && + ( pos.y() >= (topLeft.y() - tolerance) ) && ( pos.y() <= (topLeft.y() + tolerance) ) ) { + return true; + } + + ///right edge + if ( ( pos.x() >= (topRight.x() - tolerance) ) && ( pos.x() <= (topRight.x() + tolerance) ) && + ( pos.y() >= (btmRight.y() - tolerance) ) && ( pos.y() <= (topRight.y() + tolerance) ) ) { + return true; + } + + ///btm edge + if ( ( pos.x() >= (btmLeft.x() - tolerance) ) && ( pos.x() <= (btmRight.x() + tolerance) ) && + ( pos.y() >= (btmLeft.y() - tolerance) ) && ( pos.y() <= (btmLeft.y() + tolerance) ) ) { + return true; + } + + ///left edge + if ( ( pos.x() >= (btmLeft.x() - tolerance) ) && ( pos.x() <= (btmLeft.x() + tolerance) ) && + ( pos.y() >= (btmLeft.y() - tolerance) ) && ( pos.y() <= (topLeft.y() + tolerance) ) ) { + return true; + } + + return false; +} + +std::pair, boost::shared_ptr > +RotoPaintInteract::isNearbyFeatherBar(double time, + const std::pair & pixelScale, + const QPointF & pos) const +{ + double distFeatherX = 20. * pixelScale.first; + double acceptance = 10 * pixelScale.second; + + for (SelectedItems::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { + Bezier* isBezier = dynamic_cast( it->get() ); + RotoStrokeItem* isStroke = dynamic_cast( it->get() ); + assert(isStroke || isBezier); + if ( isStroke || !isBezier || ( isBezier && isBezier->isOpenBezier() ) ) { + continue; + } + + /* + For each selected bezier, we compute the extent of the feather bars and check if the mouse would be nearby one of these bars. + The feather bar of a control point is only displayed is the feather point is equal to the bezier control point. + In order to give it the correc direction we use the derivative of the bezier curve at the control point and then use + the pointInPolygon function to make sure the feather bar is always oriented on the outter part of the polygon. + The pointInPolygon function needs the polygon of the bezier to test whether the point is inside or outside the polygon + hence in this loop we compute the polygon for each bezier. + */ + + Transform::Matrix3x3 transform; + isBezier->getTransformAtTime(time, &transform); + + const std::list > & fps = isBezier->getFeatherPoints(); + const std::list > & cps = isBezier->getControlPoints(); + assert( cps.size() == fps.size() ); + + int cpCount = (int)cps.size(); + if (cpCount <= 1) { + continue; + } + // std::list polygon; + // RectD polygonBBox( std::numeric_limits::infinity(), + // std::numeric_limits::infinity(), + // -std::numeric_limits::infinity(), + // -std::numeric_limits::infinity() ); + // (*it)->evaluateFeatherPointsAtTime_DeCasteljau(time, 0, 50, true, &polygon, &polygonBBox); + + std::list >::const_iterator itF = fps.begin(); + std::list >::const_iterator nextF = itF; + if ( nextF != fps.end() ) { + ++nextF; + } + std::list >::const_iterator prevF = fps.end(); + if ( prevF != fps.begin() ) { + --prevF; + } + bool isClockWiseOriented = isBezier->isFeatherPolygonClockwiseOriented(true, time); + + for (std::list >::const_iterator itCp = cps.begin(); + itCp != cps.end(); + ++itCp) { + if ( prevF == fps.end() ) { + prevF = fps.begin(); + } + if ( nextF == fps.end() ) { + nextF = fps.begin(); + } + assert( itF != fps.end() ); // because cps.size() == fps.size() + if ( itF == fps.end() ) { + itF = fps.begin(); + } + + Transform::Point3D controlPoint, featherPoint; + controlPoint.z = featherPoint.z = 1; + (*itCp)->getPositionAtTime(true, time, ViewIdx(0), &controlPoint.x, &controlPoint.y); + (*itF)->getPositionAtTime(true, time, ViewIdx(0), &featherPoint.x, &featherPoint.y); + + controlPoint = Transform::matApply(transform, controlPoint); + featherPoint = Transform::matApply(transform, featherPoint); + { + Point cp, fp; + cp.x = controlPoint.x; + cp.y = controlPoint.y; + fp.x = featherPoint.x; + fp.y = featherPoint.y; + Bezier::expandToFeatherDistance(true, cp, &fp, distFeatherX, time, isClockWiseOriented, transform, prevF, itF, nextF); + featherPoint.x = fp.x; + featherPoint.y = fp.y; + } + assert(featherPoint.x != controlPoint.x || featherPoint.y != controlPoint.y); + + ///Now test if the user mouse click is on the line using bounding box and cross product. + if ( ( ( ( pos.y() >= (controlPoint.y - acceptance) ) && ( pos.y() <= (featherPoint.y + acceptance) ) ) || + ( ( pos.y() >= (featherPoint.y - acceptance) ) && ( pos.y() <= (controlPoint.y + acceptance) ) ) ) && + ( ( ( pos.x() >= (controlPoint.x - acceptance) ) && ( pos.x() <= (featherPoint.x + acceptance) ) ) || + ( ( pos.x() >= (featherPoint.x - acceptance) ) && ( pos.x() <= (controlPoint.x + acceptance) ) ) ) ) { + Point a; + a.x = (featherPoint.x - controlPoint.x); + a.y = (featherPoint.y - controlPoint.y); + double norm = sqrt(a.x * a.x + a.y * a.y); + + ///The point is in the bounding box of the segment, if it is vertical it must be on the segment anyway + if (norm == 0) { + return std::make_pair(*itCp, *itF); + } + + a.x /= norm; + a.y /= norm; + Point b; + b.x = (pos.x() - controlPoint.x); + b.y = (pos.y() - controlPoint.y); + norm = sqrt(b.x * b.x + b.y * b.y); + + ///This vector is not vertical + if (norm != 0) { + b.x /= norm; + b.y /= norm; + + double crossProduct = b.y * a.x - b.x * a.y; + if (std::abs(crossProduct) < 0.3) { + return std::make_pair(*itCp, *itF); + } + } + } + + // increment for next iteration + // ++itF, ++nextF, ++prevF + if ( itF != fps.end() ) { + ++itF; + } + if ( nextF != fps.end() ) { + ++nextF; + } + if ( prevF != fps.end() ) { + ++prevF; + } + } // for(itCp) + } + + return std::make_pair( boost::shared_ptr(), boost::shared_ptr() ); +} // isNearbyFeatherBar + + + +void +RotoPaintInteract::setSelection(const std::list > & drawables, + const std::list, boost::shared_ptr > > & points) +{ + selectedItems.clear(); + for (std::list >::const_iterator it = drawables.begin(); it != drawables.end(); ++it) { + if (*it) { + selectedItems.push_back(*it); + } + } + selectedCps.clear(); + for (SelectedCPs::const_iterator it = points.begin(); it != points.end(); ++it) { + if (it->first && it->second) { + selectedCps.push_back(*it); + } + } + p->publicInterface->getNode()->getRotoContext()->select(selectedItems, RotoItem::eSelectionReasonOverlayInteract); + computeSelectedCpsBBOX(); +} + +void +RotoPaintInteract::setSelection(const boost::shared_ptr & curve, + const std::pair, boost::shared_ptr > & point) +{ + selectedItems.clear(); + if (curve) { + selectedItems.push_back(curve); + } + selectedCps.clear(); + if (point.first && point.second) { + selectedCps.push_back(point); + } + if (curve) { + p->publicInterface->getNode()->getRotoContext()->select(curve, RotoItem::eSelectionReasonOverlayInteract); + } + computeSelectedCpsBBOX(); +} + +void +RotoPaintInteract::getSelection(std::list >* beziers, + std::list, boost::shared_ptr > >* points) +{ + *beziers = selectedItems; + *points = selectedCps; +} + +void +RotoPaintInteract::setBuiltBezier(const boost::shared_ptr & curve) +{ + assert(curve); + builtBezier = curve; +} + +boost::shared_ptr RotoPaintInteract::getBezierBeingBuild() const +{ + return builtBezier; +} + + + + +void +RotoPaintInteract::smoothSelectedCurve() +{ + std::pair pixelScale; + + p->publicInterface->getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + boost::shared_ptr context = p->publicInterface->getNode()->getRotoContext(); + + double time = context->getTimelineCurrentTime(); + std::list datas; + + if ( !selectedCps.empty() ) { + for (SelectedCPs::const_iterator it = selectedCps.begin(); it != selectedCps.end(); ++it) { + SmoothCuspUndoCommand::SmoothCuspCurveData data; + data.curve = it->first->getBezier(); + data.newPoints.push_back(*it); + datas.push_back(data); + } + } else { + for (SelectedItems::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { + boost::shared_ptr bezier = boost::dynamic_pointer_cast(*it); + if (bezier) { + SmoothCuspUndoCommand::SmoothCuspCurveData data; + data.curve = bezier; + const std::list > & cps = bezier->getControlPoints(); + const std::list > & fps = bezier->getFeatherPoints(); + std::list >::const_iterator itFp = fps.begin(); + for (std::list >::const_iterator it = cps.begin(); it != cps.end(); ++it, ++itFp) { + data.newPoints.push_back( std::make_pair(*it, *itFp) ); + } + datas.push_back(data); + } + } + } + if ( !datas.empty() ) { + p->publicInterface->pushUndoCommand( new SmoothCuspUndoCommand(shared_from_this(), datas, time, false, pixelScale) ); + } +} + +void +RotoPaintInteract::cuspSelectedCurve() +{ + std::pair pixelScale; + + p->publicInterface->getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + boost::shared_ptr context = p->publicInterface->getNode()->getRotoContext(); + double time = context->getTimelineCurrentTime(); + std::list datas; + + if ( !selectedCps.empty() ) { + for (SelectedCPs::const_iterator it = selectedCps.begin(); it != selectedCps.end(); ++it) { + SmoothCuspUndoCommand::SmoothCuspCurveData data; + data.curve = it->first->getBezier(); + data.newPoints.push_back(*it); + datas.push_back(data); + } + } else { + for (SelectedItems::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { + boost::shared_ptr bezier = boost::dynamic_pointer_cast(*it); + if (bezier) { + SmoothCuspUndoCommand::SmoothCuspCurveData data; + data.curve = bezier; + const std::list > & cps = bezier->getControlPoints(); + const std::list > & fps = bezier->getFeatherPoints(); + std::list >::const_iterator itFp = fps.begin(); + for (std::list >::const_iterator it = cps.begin(); it != cps.end(); ++it, ++itFp) { + data.newPoints.push_back( std::make_pair(*it, *itFp) ); + } + datas.push_back(data); + } + } + } + if ( !datas.empty() ) { + p->publicInterface->pushUndoCommand( new SmoothCuspUndoCommand(shared_from_this(), datas, time, true, pixelScale) ); + } +} + +void +RotoPaintInteract::removeFeatherForSelectedCurve() +{ + std::list datas; + + if ( !selectedCps.empty() ) { + for (SelectedCPs::const_iterator it = selectedCps.begin(); it != selectedCps.end(); ++it) { + RemoveFeatherUndoCommand::RemoveFeatherData data; + data.curve = it->first->getBezier(); + data.newPoints = data.curve->getFeatherPoints(); + datas.push_back(data); + } + } else { + for (SelectedItems::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { + boost::shared_ptr bezier = boost::dynamic_pointer_cast(*it); + if (bezier) { + RemoveFeatherUndoCommand::RemoveFeatherData data; + data.curve = bezier; + data.newPoints = bezier->getFeatherPoints(); + datas.push_back(data); + } + } + } + if ( !datas.empty() ) { + p->publicInterface->pushUndoCommand( new RemoveFeatherUndoCommand(shared_from_this(), datas) ); + } +} + +void +RotoPaintInteract::lockSelectedCurves() +{ + ///Make a copy because setLocked will change the selection internally and invalidate the iterator + SelectedItems selection = selectedItems; + + for (SelectedItems::const_iterator it = selection.begin(); it != selection.end(); ++it) { + (*it)->setLocked(true, false, RotoItem::eSelectionReasonOverlayInteract); + } + clearSelection(); +} + +void +RotoPaintInteract::moveSelectedCpsWithKeyArrows(int x, + int y) +{ + + std::list< std::pair, boost::shared_ptr > > points; + if ( !selectedCps.empty() ) { + points = selectedCps; + } else { + for (SelectedItems::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { + boost::shared_ptr bezier = boost::dynamic_pointer_cast(*it); + if (bezier) { + const std::list< boost::shared_ptr > & cps = bezier->getControlPoints(); + const std::list< boost::shared_ptr > & fps = bezier->getFeatherPoints(); + + std::list< boost::shared_ptr >::const_iterator fpIt = fps.begin(); + for (std::list< boost::shared_ptr >::const_iterator it = cps.begin(); it!=fps.end(); ++it) { + + points.push_back(std::make_pair(*it, *fpIt)); + if (!fps.empty()) { + ++fpIt; + } + } + + } + } + } + + if (!points.empty()) { + std::pair pixelScale; + p->publicInterface->getCurrentViewportForOverlays()->getPixelScale(pixelScale.first, pixelScale.second); + double time = p->publicInterface->getCurrentTime(); + + p->publicInterface->pushUndoCommand( new MoveControlPointsUndoCommand(shared_from_this(), points, (double)x * pixelScale.first, + (double)y * pixelScale.second, time) ); + computeSelectedCpsBBOX(); + p->publicInterface->getNode()->getRotoContext()->evaluateChange(); + } +} + + +void +RotoPaintInteract::onCurveLockedChangedRecursive(const boost::shared_ptr & item, + bool* ret) +{ + boost::shared_ptr b = boost::dynamic_pointer_cast(item); + boost::shared_ptr layer = boost::dynamic_pointer_cast(item); + + if (b) { + if ( item->isLockedRecursive() ) { + for (SelectedItems::iterator fb = selectedItems.begin(); fb != selectedItems.end(); ++fb) { + if ( fb->get() == b.get() ) { + ///if the curve was selected, wipe the selection CP bbox + clearCPSSelection(); + selectedItems.erase(fb); + *ret = true; + break; + } + } + } else { + ///Explanation: This change has been made in result to a user click on the settings panel. + ///We have to reselect the bezier overlay hence put a reason different of eSelectionReasonOverlayInteract + SelectedItems::iterator found = std::find(selectedItems.begin(), selectedItems.end(), b); + if ( found == selectedItems.end() ) { + selectedItems.push_back(b); + p->publicInterface->getNode()->getRotoContext()->select(b, RotoItem::eSelectionReasonSettingsPanel); + *ret = true; + } + } + } else if (layer) { + const std::list > & items = layer->getItems(); + for (std::list >::const_iterator it = items.begin(); it != items.end(); ++it) { + onCurveLockedChangedRecursive(*it, ret); + } + } +} + + +void +RotoPaintInteract::removeCurve(const boost::shared_ptr& curve) +{ + if (curve == builtBezier) { + builtBezier.reset(); + } else if (curve == strokeBeingPaint) { + strokeBeingPaint.reset(); + } + getContext()->removeItem(curve); +} + + +NATRON_NAMESPACE_EXIT; \ No newline at end of file diff --git a/Engine/RotoPaintInteract.h b/Engine/RotoPaintInteract.h new file mode 100644 index 0000000000..407c9bdcde --- /dev/null +++ b/Engine/RotoPaintInteract.h @@ -0,0 +1,779 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * This file is part of Natron , + * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat + * + * Natron is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Natron is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Natron. If not, see + * ***** END LICENSE BLOCK ***** */ + +#ifndef ROTOPAINTINTERACT_H +#define ROTOPAINTINTERACT_H + +// ***** BEGIN PYTHON BLOCK ***** +// from : +// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." +#include +// ***** END PYTHON BLOCK ***** + +#include +#include + +#if !defined(Q_MOC_RUN) && !defined(SBK_RUN) +#include +#endif + +#include + +#include "Engine/EngineFwd.h" +#include "Engine/BezierCP.h" +#include "Engine/Bezier.h" + +NATRON_NAMESPACE_ENTER; + + +#define kControlPointMidSize 3 +#define kBezierSelectionTolerance 8 +#define kControlPointSelectionTolerance 8 +#define kXHairSelectedCpsTolerance 8 +#define kXHairSelectedCpsBox 8 +#define kTangentHandleSelectionTolerance 8 +#define kTransformArrowLenght 10 +#define kTransformArrowWidth 3 +#define kTransformArrowOffsetFromPoint 15 + + +// parameters + + +// The toolbar +#define kRotoUIParamToolbar "Toolbar" + +#define kRotoUIParamSelectionToolButton "SelectionToolButton" +#define kRotoUIParamSelectionToolButtonLabel "Selection Tool" + +#define kRotoUIParamSelectAllToolButtonAction "SelectAllTool" +#define kRotoUIParamSelectAllToolButtonActionLabel "Select All Tool" +#define kRotoUIParamSelectAllToolButtonActionHint "Everything can be selected and moved" + +#define kRotoUIParamSelectPointsToolButtonAction "SelectPointsTool" +#define kRotoUIParamSelectPointsToolButtonActionLabel "Select Points Tool" +#define kRotoUIParamSelectPointsToolButtonActionHint "Works only for the points of the inner shape," \ +" feather points will not be taken into account." + +#define kRotoUIParamSelectShapesToolButtonAction "SelectShapesTool" +#define kRotoUIParamSelectShapesToolButtonActionLabel "Select Shapes Tool" +#define kRotoUIParamSelectShapesToolButtonActionHint "Only Shapes may be selected" + +#define kRotoUIParamSelectFeatherPointsToolButtonAction "SelectFeatherTool" +#define kRotoUIParamSelectFeatherPointsToolButtonActionLabel "Select Feather Points Tool" +#define kRotoUIParamSelectFeatherPointsToolButtonActionHint "Only Feather points may be selected" + +#define kRotoUIParamEditPointsToolButton "EditPointsToolButton" +#define kRotoUIParamEditPointsToolButtonLabel "Points Edition Tool" + +#define kRotoUIParamAddPointsToolButtonAction "AddPointsTool" +#define kRotoUIParamAddPointsToolButtonActionLabel "Add Points Tool" +#define kRotoUIParamAddPointsToolButtonActionHint "Add a control point to the shape" + +#define kRotoUIParamRemovePointsToolButtonAction "RemovePointsTool" +#define kRotoUIParamRemovePointsToolButtonActionLabel "Remove Points Tool" +#define kRotoUIParamRemovePointsToolButtonActionHint "Remove a control point from the shape" + +#define kRotoUIParamCuspPointsToolButtonAction "CuspPointsTool" +#define kRotoUIParamCuspPointsToolButtonActionLabel "Cusp Points Tool" +#define kRotoUIParamCuspPointsToolButtonActionHint "Cusp points on the shape" + +#define kRotoUIParamSmoothPointsToolButtonAction "SmoothPointsTool" +#define kRotoUIParamSmoothPointsToolButtonActionLabel "Smooth Points Tool" +#define kRotoUIParamSmoothPointsToolButtonActionHint "Smooth points on the shape" + +#define kRotoUIParamOpenCloseCurveToolButtonAction "OpenCloseShapeTool" +#define kRotoUIParamOpenCloseCurveToolButtonActionLabel "Open/Close Shape Tool" +#define kRotoUIParamOpenCloseCurveToolButtonActionHint "Open or Close shapes" + +#define kRotoUIParamRemoveFeatherToolButtonAction "RemoveFeatherPointTool" +#define kRotoUIParamRemoveFeatherToolButtonActionLabel "Remove Feather Tool" +#define kRotoUIParamRemoveFeatherToolButtonActionHint "Remove feather on points" + +#define kRotoUIParamBezierEditionToolButton "EditBezierToolButton" +#define kRotoUIParamBezierEditionToolButtonLabel "Shapes Drawing Tool" + +#define kRotoUIParamDrawBezierToolButtonAction "DrawBezierTool" +#define kRotoUIParamDrawBezierToolButtonActionLabel "Bezier Tool" +#define kRotoUIParamDrawBezierToolButtonActionHint "Draw Bezier" + +#define kRotoUIParamDrawEllipseToolButtonAction "DrawEllipseTool" +#define kRotoUIParamDrawEllipseToolButtonActionLabel "Ellipse Tool" +#define kRotoUIParamDrawEllipseToolButtonActionHint "Draw Ellipse" + +#define kRotoUIParamDrawRectangleToolButtonAction "DrawRectangleTool" +#define kRotoUIParamDrawRectangleToolButtonActionLabel "Rectangle Tol" +#define kRotoUIParamDrawRectangleToolButtonActionHint "Draw Rectangle" + + +#define kRotoUIParamPaintBrushToolButton "PaintBrushToolButton" +#define kRotoUIParamPaintBrushToolButtonLabel "Paint Brush Tool" + +#define kRotoUIParamDrawBrushToolButtonAction "PaintSolidTool" +#define kRotoUIParamDrawBrushToolButtonActionLabel "Solid Paint Brush Tool" +#define kRotoUIParamDrawBrushToolButtonActionHint "Draw using the pen" + +#define kRotoUIParamPencilToolButtonAction "PencilTool" +#define kRotoUIParamPencilToolButtonActionLabel "Pencil Tool" +#define kRotoUIParamPencilToolButtonActionHint "Draw open bezier" + +#define kRotoUIParamEraserToolButtonAction "EraserTool" +#define kRotoUIParamEraserToolButtonActionLabel "Eraser Tool" +#define kRotoUIParamEraserToolButtonActionHint "Use the Eraser" + +#define kRotoUIParamCloneBrushToolButton "CloneToolButton" +#define kRotoUIParamCloneBrushToolButtonLabel "Clone Tool" + +#define kRotoUIParamCloneToolButtonAction "CloneTool" +#define kRotoUIParamCloneToolButtonActionLabel "Clone Tool" +#define kRotoUIParamCloneToolButtonActionHint "Draw with the pen to clone areas of the image. Hold CTRL to move the clone offset." + +#define kRotoUIParamRevealToolButtonAction "RevealTool" +#define kRotoUIParamRevealToolButtonActionLabel "Reveal Tool" +#define kRotoUIParamRevealToolButtonActionHint "Draw with the pen to apply the effect from the selected source. Hold CTRL to move the offset" + +#define kRotoUIParamEffectBrushToolButton "EffectToolButton" +#define kRotoUIParamEffectBrushToolButtonLabel "Effect Tool" + +#define kRotoUIParamBlurToolButtonAction "BlurTool" +#define kRotoUIParamBlurToolButtonActionLabel "Blur Tool" +#define kRotoUIParamBlurToolButtonActionHint "Draw with the pen to apply a blur" + +#define kRotoUIParamSmearToolButtonAction "SmearTool" +#define kRotoUIParamSmearToolButtonActionLabel "Smear Tool" +#define kRotoUIParamSmearToolButtonActionHint "Draw with the pen to blur and displace a portion of the source image along the direction of the pen" + +#define kRotoUIParamMergeBrushToolButton "MergeToolButton" +#define kRotoUIParamMergeBrushToolButtonLabel "Merging Tool" + +#define kRotoUIParamDodgeToolButtonAction "DodgeTool" +#define kRotoUIParamDodgeToolButtonActionLabel "Dodge Tool" +#define kRotoUIParamDodgeToolButtonActionHint "Make the source image brighter" + +#define kRotoUIParamBurnToolButtonAction "BurnTool" +#define kRotoUIParamBurnToolButtonActionLabel "Burn Tool" +#define kRotoUIParamBurnToolButtonActionHint "Make the source image darker" + +// The right click menu +#define kRotoUIParamRightClickMenu kNatronOfxParamRightClickMenu + +//For all items +#define kRotoUIParamRightClickMenuActionRemoveItems "removeItemsAction" +#define kRotoUIParamRightClickMenuActionRemoveItemsLabel "Remove Selected Item(s)" + +#define kRotoUIParamRightClickMenuActionCuspItems "cuspItemsAction" +#define kRotoUIParamRightClickMenuActionCuspItemsLabel "Cusp Selected Item(s)" + +#define kRotoUIParamRightClickMenuActionSmoothItems "SmoothItemsAction" +#define kRotoUIParamRightClickMenuActionSmoothItemsLabel "Smooth Selected Item(s)" + +#define kRotoUIParamRightClickMenuActionRemoveItemsFeather "removeItemsFeatherAction" +#define kRotoUIParamRightClickMenuActionRemoveItemsFeatherLabel "Remove Selected item(s) Feather" + +#define kRotoUIParamRightClickMenuActionLinkItemsToTrack "linkItemsAction" +#define kRotoUIParamRightClickMenuActionLinkItemsToTrackLabel "Link To Track..." + +#define kRotoUIParamRightClickMenuActionUnlinkItemsFromTrack "unlinkItemsAction" +#define kRotoUIParamRightClickMenuActionUnlinkItemsFromTrackLabel "Unlink From Track..." + +#define kRotoUIParamRightClickMenuActionNudgeLeft "nudgeLeftAction" +#define kRotoUIParamRightClickMenuActionNudgeLeftLabel "Nudge Left" + +#define kRotoUIParamRightClickMenuActionNudgeRight "nudgeRightAction" +#define kRotoUIParamRightClickMenuActionNudgeRightLabel "Nudge Right" + +#define kRotoUIParamRightClickMenuActionNudgeBottom "nudgeBottomAction" +#define kRotoUIParamRightClickMenuActionNudgeBottomLabel "Nudge Bottom" + +#define kRotoUIParamRightClickMenuActionNudgeTop "nudgeTopAction" +#define kRotoUIParamRightClickMenuActionNudgeTopLabel "Nudge Top" + +// just for shapes +#define kRotoUIParamRightClickMenuActionSelectAll "selectAllAction" +#define kRotoUIParamRightClickMenuActionSelectAllLabel "Select All" + +#define kRotoUIParamRightClickMenuActionOpenClose "openCloseAction" +#define kRotoUIParamRightClickMenuActionOpenCloseLabel "Open/Close Shape" + +#define kRotoUIParamRightClickMenuActionLockShapes "lockShapesAction" +#define kRotoUIParamRightClickMenuActionLockShapesLabel "Lock Shape(s)" + +// Viewer UI buttons + +// Roto +#define kRotoUIParamAutoKeyingEnabled "autoKeyingEnabledButton" +#define kRotoUIParamAutoKeyingEnabledLabel "Enable Auto-Keying" +#define kRotoUIParamAutoKeyingEnabledHint "When activated any change made to a control point will set a keyframe at the current time" + +#define kRotoUIParamFeatherLinkEnabled "featherLinkEnabledButton" +#define kRotoUIParamFeatherLinkEnabledLabel "Enable Feather-Link" +#define kRotoUIParamFeatherLinkEnabledHint "Feather-link: When activated the feather points will follow the same" \ +" movement as their counter-part does" + +#define kRotoUIParamDisplayFeather "displayFeatherButton" +#define kRotoUIParamDisplayFeatherLabel "Display Feather" +#define kRotoUIParamDisplayFeatherHint "When checked, the feather curve applied to the shape(s) will be visible and editable" + +#define kRotoUIParamStickySelectionEnabled "stickySelectionEnabledButton" +#define kRotoUIParamStickySelectionEnabledLabel "Enable Sticky Selection" +#define kRotoUIParamStickySelectionEnabledHint "Sticky-selection: When activated, " \ +" clicking outside of any shape will not clear the current selection" + +#define kRotoUIParamStickyBbox "stickyBboxButton" +#define kRotoUIParamStickyBboxLabel "Enable Sticky Bounding Box" +#define kRotoUIParamStickyBboxHint "Easy bounding box manipulation: When activated, " \ +" clicking inside of the bounding box of selected points will move the points." \ +"When deactivated, only clicking on the cross will move the points" + +#define kRotoUIParamRippleEdit "rippleEditButton" +#define kRotoUIParamRippleEditLabel "Enable Ripple Edit" +#define kRotoUIParamRippleEditLabelHint "Ripple-edit: When activated, moving a control point" \ +" will move it by the same amount for all the keyframes " \ +"it has" + +#define kRotoUIParamAddKeyFrame "addKeyframeButton" +#define kRotoUIParamAddKeyFrameLabel "Add Keyframe" +#define kRotoUIParamAddKeyFrameHint "Set a keyframe at the current time for the selected shape(s), if any" + +#define kRotoUIParamRemoveKeyframe "removeKeyframeButton" +#define kRotoUIParamRemoveKeyframeLabel "Remove Keyframe" +#define kRotoUIParamRemoveKeyframeHint "Remove a keyframe at the current time for the selected shape(s), if any" + + +// RotoPaint +#define kRotoUIParamColorWheel "strokeColorButton" +#define kRotoUIParamColorWheelLabel "Stroke Color" +#define kRotoUIParamColorWheelHint "The color of the next paint brush stroke to be painted" + +#define kRotoUIParamBlendingOp "blendingModeButton" +#define kRotoUIParamBlendingOpLabel "Blending Mode" +#define kRotoUIParamBlendingOpHint "The blending mode of the next brush stroke" + +#define kRotoUIParamOpacity "opacitySpinbox" +#define kRotoUIParamOpacityLabel "Opacity" +#define kRotoUIParamOpacityHint "The opacity of the next brush stroke to be painted. Use CTRL + SHIFT + drag " \ +"with the mouse to change the opacity." + +#define kRotoUIParamPressureOpacity "pressureOpacityButton" +#define kRotoUIParamPressureOpacityLabel "Pressure Affects Opacity" +#define kRotoUIParamPressureOpacityHint "If checked, the pressure of the pen will dynamically alter the opacity of the next " \ +"brush stroke." + +#define kRotoUIParamSize "sizeSpinbox" +#define kRotoUIParamSizeLabel "Brush Size" +#define kRotoUIParamSizeHint "The size of the next brush stroke to be painted. Use SHIFT + drag with the mouse " \ +"to change the size." + +#define kRotoUIParamPressureSize "pressureSizeButton" +#define kRotoUIParamPressureSizeLabel "Pressure Affects Size" +#define kRotoUIParamPressureSizeHint "If checked, the pressure of the pen will dynamically alter the size of the next " \ +"brush stroke." + +#define kRotoUIParamHardness "hardnessSpinbox" +#define kRotoUIParamHardnessLabel "Brush Hardness" +#define kRotoUIParamHardnessHint "The hardness of the next brush stroke to be painted" + +#define kRotoUIParamPressureHardness "pressureHardnessButton" +#define kRotoUIParamPressureHardnessLabel "Pressure Affects Hardness" +#define kRotoUIParamPressureHardnessHint "If checked, the pressure of the pen will dynamically alter the hardness of the next " \ +"brush stroke" + +#define kRotoUIParamBuildUp "buildUpButton" +#define kRotoUIParamBuildUpLabel "Build-Up" +#define kRotoUIParamBuildUpHint "When build-up is enabled, the next brush stroke will build up " \ +"when painted over itself" + +#define kRotoUIParamEffect "effectSpinbox" +#define kRotoUIParamEffectLabel "Effect Strength" +#define kRotoUIParamEffectHint "The strength of the next paint brush effect" + +#define kRotoUIParamTimeOffset "timeOffsetSpinbox" +#define kRotoUIParamTimeOffsetLabel "Time Offset" +#define kRotoUIParamTimeOffsetHint "When the Clone tool is used, this determines depending on the time offset " \ +"mode the source frame to clone. When in absolute mode, this is the frame " \ +"number of the source, when in relative mode, this is an offset relative " \ +"to the current frame" + +#define kRotoUIParamTimeOffsetMode "timeOffsetModeChoice" +#define kRotoUIParamTimeOffsetModeLabel "Time Offset Mode" +#define kRotoUIParamTimeOffsetModeHint "When in absolute mode, this is the frame number of the source, " \ +"when in relative mode, this is an offset relative to " \ +"the current frame" \ + +#define kRotoUIParamSourceType "sourceTypeChoice" +#define kRotoUIParamSourceTypeLabel "Source" +#define kRotoUIParamSourceTypeHint "Source color used for painting the stroke when the Reveal/Clone tools are used:\n" \ +"- foreground: the painted result at this point in the hierarchy,\n" \ +"- background: the original image unpainted connected to bg,\n" \ +"- backgroundN: the original image unpainted connected to bgN" + +#define kRotoUIParamResetCloneOffset "resetCloneOffsetButton" +#define kRotoUIParamResetCloneOffsetLabel "Reset Transform" +#define kRotoUIParamResetCloneOffsetHint "Reset the transform applied before cloning to identity" + +#define kRotoUIParamMultiStrokeEnabled "multiStrokeEnabledButton" +#define kRotoUIParamMultiStrokeEnabledLabel "Multi-Stroke" +#define kRotoUIParamMultiStrokeEnabledHint "When checked, strokes will be appended to the same item " \ +"in the hierarchy as long as the same tool is selected.\n" \ +"Select another tool to make a new item." + + +// Shortcuts + +#define kShortcutIDActionRotoDelete "delete" +#define kShortcutDescActionRotoDelete "Delete Element" + +#define kShortcutIDActionRotoCloseBezier "closeBezier" +#define kShortcutDescActionRotoCloseBezier "Close Bezier" + +#define kShortcutIDActionRotoSelectAll "selectAll" +#define kShortcutDescActionRotoSelectAll "Select All" + +#define kShortcutIDActionRotoSelectionTool "selectionTool" +#define kShortcutDescActionRotoSelectionTool "Switch to Selection Mode" + +#define kShortcutIDActionRotoAddTool "addTool" +#define kShortcutDescActionRotoAddTool "Switch to Add Mode" + +#define kShortcutIDActionRotoEditTool "editTool" +#define kShortcutDescActionRotoEditTool "Switch to Edition Mode" + +#define kShortcutIDActionRotoBrushTool "brushTool" +#define kShortcutDescActionRotoBrushTool "Switch to Brush Mode" + +#define kShortcutIDActionRotoCloneTool "cloneTool" +#define kShortcutDescActionRotoCloneTool "Switch to Clone Mode" + +#define kShortcutIDActionRotoEffectTool "EffectTool" +#define kShortcutDescActionRotoEffectTool "Switch to Effect Mode" + +#define kShortcutIDActionRotoColorTool "colorTool" +#define kShortcutDescActionRotoColorTool "Switch to Color Mode" + +#define kShortcutIDActionRotoNudgeLeft "nudgeLeft" +#define kShortcutDescActionRotoNudgeLeft "Move Bezier to the Left" + +#define kShortcutIDActionRotoNudgeRight "nudgeRight" +#define kShortcutDescActionRotoNudgeRight "Move Bezier to the Right" + +#define kShortcutIDActionRotoNudgeBottom "nudgeBottom" +#define kShortcutDescActionRotoNudgeBottom "Move Bezier to the Bottom" + +#define kShortcutIDActionRotoNudgeTop "nudgeTop" +#define kShortcutDescActionRotoNudgeTop "Move Bezier to the Top" + +#define kShortcutIDActionRotoSmooth "smooth" +#define kShortcutDescActionRotoSmooth "Smooth Bezier" + +#define kShortcutIDActionRotoCuspBezier "cusp" +#define kShortcutDescActionRotoCuspBezier "Cusp Bezier" + +#define kShortcutIDActionRotoRemoveFeather "rmvFeather" +#define kShortcutDescActionRotoRemoveFeather "Remove Feather" + +#define kShortcutIDActionRotoLinkToTrack "linkToTrack" +#define kShortcutDescActionRotoLinkToTrack "Link to Track" + +#define kShortcutIDActionRotoUnlinkToTrack "unlinkFromTrack" +#define kShortcutDescActionRotoUnlinkToTrack "Unlink from Track" + +#define kShortcutIDActionRotoLockCurve "lock" +#define kShortcutDescActionRotoLockCurve "Lock Shape" + +struct RotoPaintInteract; + +struct RotoPaintPrivate +{ + RotoPaint* publicInterface; + bool isPaintByDefault; + boost::weak_ptr premultKnob; + boost::weak_ptr enabledKnobs[4]; + + boost::shared_ptr ui; + + RotoPaintPrivate(RotoPaint* publicInterface,bool isPaintByDefault); +}; + +///A list of points and their counter-part, that is: either a control point and its feather point, or +///the feather point and its associated control point +typedef std::pair, boost::shared_ptr > SelectedCP; +typedef std::list< SelectedCP > SelectedCPs; +typedef std::list< boost::shared_ptr > SelectedItems; + +enum EventStateEnum +{ + eEventStateNone = 0, + eEventStateDraggingControlPoint, + eEventStateDraggingSelectedControlPoints, + eEventStateBuildingBezierControlPointTangent, + eEventStateBuildingEllipse, + eEventStateBuildingRectangle, + eEventStateDraggingLeftTangent, + eEventStateDraggingRightTangent, + eEventStateDraggingFeatherBar, + eEventStateDraggingBBoxTopLeft, + eEventStateDraggingBBoxTopRight, + eEventStateDraggingBBoxBtmRight, + eEventStateDraggingBBoxBtmLeft, + eEventStateDraggingBBoxMidTop, + eEventStateDraggingBBoxMidRight, + eEventStateDraggingBBoxMidBtm, + eEventStateDraggingBBoxMidLeft, + eEventStateBuildingStroke, + eEventStateDraggingCloneOffset, + eEventStateDraggingBrushSize, + eEventStateDraggingBrushOpacity, +}; + +enum HoverStateEnum +{ + eHoverStateNothing = 0, + eHoverStateBboxTopLeft, + eHoverStateBboxTopRight, + eHoverStateBboxBtmRight, + eHoverStateBboxBtmLeft, + eHoverStateBboxMidTop, + eHoverStateBboxMidRight, + eHoverStateBboxMidBtm, + eHoverStateBboxMidLeft, + eHoverStateBbox +}; + +enum SelectedCpsTransformModeEnum +{ + eSelectedCpsTransformModeTranslateAndScale = 0, + eSelectedCpsTransformModeRotateAndSkew = 1 +}; + +enum RotoRoleEnum +{ + eRotoRoleSelection = 0, + eRotoRolePointsEdition, + eRotoRoleBezierEdition, + eRotoRolePaintBrush, + eRotoRoleCloneBrush, + eRotoRoleEffectBrush, + eRotoRoleMergeBrush +}; + +enum RotoToolEnum +{ + eRotoToolSelectAll = 0, + eRotoToolSelectPoints, + eRotoToolSelectCurves, + eRotoToolSelectFeatherPoints, + + eRotoToolAddPoints, + eRotoToolRemovePoints, + eRotoToolRemoveFeatherPoints, + eRotoToolOpenCloseCurve, + eRotoToolSmoothPoints, + eRotoToolCuspPoints, + + eRotoToolDrawBezier, + eRotoToolDrawBSpline, + eRotoToolDrawEllipse, + eRotoToolDrawRectangle, + + eRotoToolSolidBrush, + eRotoToolOpenBezier, + eRotoToolEraserBrush, + + eRotoToolClone, + eRotoToolReveal, + + eRotoToolBlur, + eRotoToolSharpen, + eRotoToolSmear, + + eRotoToolDodge, + eRotoToolBurn +}; + +struct RotoPaintInteract : public boost::enable_shared_from_this +{ + RotoPaintPrivate* p; + + SelectedItems selectedItems; + SelectedCPs selectedCps; + QRectF selectedCpsBbox; + bool showCpsBbox; + + ////This is by default eSelectedCpsTransformModeTranslateAndScale. When clicking the cross-hair in the center this will toggle the transform mode + ////like it does in inkscape. + SelectedCpsTransformModeEnum transformMode; + boost::shared_ptr builtBezier; //< the bezier currently being built + boost::shared_ptr bezierBeingDragged; + SelectedCP cpBeingDragged; //< the cp being dragged + boost::shared_ptr tangentBeingDragged; //< the control point whose tangent is being dragged. + //only relevant when the state is DRAGGING_X_TANGENT + SelectedCP featherBarBeingDragged, featherBarBeingHovered; + boost::shared_ptr strokeBeingPaint; + std::pair cloneOffset; + QPointF click; // used for drawing ellipses and rectangles, to handle center/constrain. May also be used for the selection bbox. + + RotoToolEnum selectedTool; + RotoRoleEnum selectedRole; + boost::weak_ptr lastPaintToolAction; + EventStateEnum state; + HoverStateEnum hoverState; + QPointF lastClickPos; + QPointF lastMousePos; + bool evaluateOnPenUp; //< if true the next pen up will call context->evaluateChange() + bool evaluateOnKeyUp; //< if true the next key up will call context->evaluateChange() + bool iSelectingwithCtrlA; + int shiftDown; + int ctrlDown; + int altDown; + bool lastTabletDownTriggeredEraser; + QPointF mouseCenterOnSizeChange; + + + //////// Toolbar + boost::weak_ptr toolbarPage; + + boost::weak_ptr selectedToolRole; + boost::weak_ptr selectedToolAction; + + boost::weak_ptr selectToolGroup; + boost::weak_ptr selectAllAction; + boost::weak_ptr selectPointsAction; + boost::weak_ptr selectCurvesAction; + boost::weak_ptr selectFeatherPointsAction; + + boost::weak_ptr pointsEditionToolGroup; + boost::weak_ptr addPointsAction; + boost::weak_ptr removePointsAction; + boost::weak_ptr cuspPointsAction; + boost::weak_ptr smoothPointsAction; + boost::weak_ptr openCloseCurveAction; + boost::weak_ptr removeFeatherAction; + + boost::weak_ptr bezierEditionToolGroup; + boost::weak_ptr drawBezierAction; + boost::weak_ptr drawEllipseAction; + boost::weak_ptr drawRectangleAction; + + boost::weak_ptr paintBrushToolGroup; + boost::weak_ptr brushAction; + boost::weak_ptr pencilAction; + boost::weak_ptr eraserAction; + + boost::weak_ptr cloneBrushToolGroup; + boost::weak_ptr cloneAction; + boost::weak_ptr revealAction; + + boost::weak_ptr effectBrushToolGroup; + boost::weak_ptr blurAction; + boost::weak_ptr smearAction; + + boost::weak_ptr mergeBrushToolGroup; + boost::weak_ptr dodgeAction; + boost::weak_ptr burnAction; + + //////Right click menu + boost::weak_ptr rightClickMenuKnob; + + //Right click on point + boost::weak_ptr removeItemsMenuAction; + boost::weak_ptr cuspItemMenuAction; + boost::weak_ptr smoothItemMenuAction; + boost::weak_ptr removeItemFeatherMenuAction; + boost::weak_ptr linkPointMenuAction; + boost::weak_ptr unlinkPointMenuAction; + boost::weak_ptr nudgeLeftMenuAction, nudgeRightMenuAction, nudgeBottomMenuAction, nudgeTopMenuAction; + + // Right click on curve + boost::weak_ptr selectAllMenuAction; + boost::weak_ptr openCloseMenuAction; + boost::weak_ptr lockShapeMenuAction; + + // Roto buttons + boost::weak_ptr autoKeyingEnabledButton; + boost::weak_ptr featherLinkEnabledButton; + boost::weak_ptr displayFeatherEnabledButton; + boost::weak_ptr stickySelectionEnabledButton; + boost::weak_ptr bboxClickAnywhereButton; + boost::weak_ptr rippleEditEnabledButton; + boost::weak_ptr addKeyframeButton; + boost::weak_ptr removeKeyframeButton; + + // RotoPaint buttons + boost::weak_ptr colorWheelButton; + boost::weak_ptr compositingOperatorChoice; + boost::weak_ptr opacitySpinbox; + boost::weak_ptr pressureOpacityButton; + boost::weak_ptr sizeSpinbox; + boost::weak_ptr pressureSizeButton; + boost::weak_ptr hardnessSpinbox; + boost::weak_ptr pressureHardnessButton; + boost::weak_ptr buildUpButton; + boost::weak_ptr effectSpinBox; + boost::weak_ptr timeOffsetSpinBox; + boost::weak_ptr timeOffsetModeChoice; + boost::weak_ptr sourceTypeChoice; + boost::weak_ptr resetCloneOffsetButton; + boost::weak_ptr multiStrokeEnabled; + + + RotoPaintInteract(RotoPaintPrivate* p); + + bool isFeatherVisible() const; + + boost::shared_ptr getContext(); + + RotoToolEnum getSelectedTool() const + { + return selectedTool; + } + + bool isStickySelectionEnabled() const; + + bool isMultiStrokeEnabled() const; + + bool getRoleForGroup(const boost::shared_ptr& group, RotoRoleEnum* role) const; + bool getToolForAction(const boost::shared_ptr& action, RotoToolEnum* tool) const; + + bool onRoleChangedInternal(const boost::shared_ptr& roleGroup); + + bool onToolChangedInternal(const boost::shared_ptr& actionButton); + + void clearSelection(); + + void clearCPSSelection(); + + void clearBeziersSelection(); + + bool hasSelection() const; + + void onCurveLockedChangedRecursive(const boost::shared_ptr & item, bool* ret); + + bool removeItemFromSelection(const boost::shared_ptr& b); + + void computeSelectedCpsBBOX(); + + QPointF getSelectedCpsBBOXCenter(); + + void drawSelectedCpsBBOX(); + + void drawEllipse(double x, + double y, + double radiusX, + double radiusY, + int l, + double r, + double g, + double b, + double a); + + ///by default draws a vertical arrow, which can be rotated by rotate amount. + void drawArrow(double centerX, double centerY, double rotate, bool hovered, const std::pair & pixelScale); + + ///same as drawArrow but the two ends will make an angle of 90 degrees + void drawBendedArrow(double centerX, double centerY, double rotate, bool hovered, const std::pair & pixelScale); + + void handleBezierSelection(const boost::shared_ptr & curve); + + void handleControlPointSelection(const std::pair, boost::shared_ptr > & p); + + void drawSelectedCp(double time, + const boost::shared_ptr & cp, + double x, double y, + const Transform::Matrix3x3& transform); + + std::pair, boost::shared_ptr >isNearbyFeatherBar(double time, const std::pair & pixelScale, const QPointF & pos) const; + + bool isNearbySelectedCpsCrossHair(const QPointF & pos) const; + + bool isWithinSelectedCpsBBox(const QPointF& pos) const; + + bool isNearbyBBoxTopLeft(const QPointF & p, double tolerance, const std::pair & pixelScale) const; + bool isNearbyBBoxTopRight(const QPointF & p, double tolerance, const std::pair & pixelScale) const; + bool isNearbyBBoxBtmLeft(const QPointF & p, double tolerance, const std::pair & pixelScale) const; + bool isNearbyBBoxBtmRight(const QPointF & p, double tolerance, const std::pair & pixelScale) const; + + bool isNearbyBBoxMidTop(const QPointF & p, double tolerance, const std::pair & pixelScale) const; + bool isNearbyBBoxMidRight(const QPointF & p, double tolerance, const std::pair & pixelScale) const; + bool isNearbyBBoxMidBtm(const QPointF & p, double tolerance, const std::pair & pixelScale) const; + bool isNearbyBBoxMidLeft(const QPointF & p, double tolerance, const std::pair & pixelScale) const; + + bool isNearbySelectedCpsBoundingBox(const QPointF & pos, double tolerance) const; + + EventStateEnum isMouseInteractingWithCPSBbox(const QPointF& pos, double tolerance, const std::pair& pixelScale) const; + + bool isBboxClickAnywhereEnabled() const; + + void makeStroke(bool prepareForLater, const RotoPoint& p); + + void checkViewersAreDirectlyConnected(); + + void showMenuForControlPoint(const boost::shared_ptr& cp); + + void showMenuForCurve(const boost::shared_ptr & curve); + + void setCurrentTool(const boost::shared_ptr& tool); + + void onBreakMultiStrokeTriggered(); + + + /** + * @brief Set the selection to be the given beziers and the given control points. + * This can only be called on the main-thread. + **/ + void setSelection(const std::list > & selectedBeziers, + const std::list, boost::shared_ptr > > & selectedCps); + void setSelection(const boost::shared_ptr & curve, + const std::pair, boost::shared_ptr > & point); + + void getSelection(std::list >* selectedBeziers, + std::list, boost::shared_ptr > >* selectedCps); + + void setBuiltBezier(const boost::shared_ptr & curve); + + boost::shared_ptr getBezierBeingBuild() const; + + void smoothSelectedCurve(); + void cuspSelectedCurve(); + void removeFeatherForSelectedCurve(); + void lockSelectedCurves(); + + + + /** + *@brief Moves of the given pixel the selected control points. + * This takes into account the zoom factor. + **/ + void moveSelectedCpsWithKeyArrows(int x, int y); + + + void evaluate(bool redraw); + void autoSaveAndRedraw(); + + void redrawOverlays(); + + + /** + * @brief Calls RotoContext::removeItem but also clears some pointers if they point to + * this curve. For undo/redo purpose. + **/ + void removeCurve(const boost::shared_ptr& curve); +}; + +NATRON_NAMESPACE_EXIT; + +#endif // ROTOPAINTINTERACT_H diff --git a/Gui/RotoUndoCommand.cpp b/Engine/RotoUndoCommand.cpp similarity index 60% rename from Gui/RotoUndoCommand.cpp rename to Engine/RotoUndoCommand.cpp index 0ff6d51a0d..7964e8e104 100644 --- a/Gui/RotoUndoCommand.cpp +++ b/Engine/RotoUndoCommand.cpp @@ -44,12 +44,11 @@ CLANG_DIAG_ON(uninitialized) #include "Engine/RotoContext.h" #include "Engine/RotoLayer.h" #include "Engine/RotoStrokeItem.h" +#include "Engine/RotoPaintInteract.h" +#include "Engine/RotoPaint.h" #include "Engine/Transform.h" #include "Engine/ViewIdx.h" -#include "Gui/GuiAppInstance.h" -#include "Gui/RotoGui.h" -#include "Gui/RotoPanel.h" NATRON_NAMESPACE_ENTER; @@ -60,23 +59,26 @@ typedef std::list SelectedCpList; typedef boost::shared_ptr BezierPtr; typedef std::list BezierList; -MoveControlPointsUndoCommand::MoveControlPointsUndoCommand(RotoGui* roto, +MoveControlPointsUndoCommand::MoveControlPointsUndoCommand(const boost::shared_ptr& roto, const std::list< std::pair, boost::shared_ptr > > & toDrag , double dx, double dy, double time) - : QUndoCommand() + : UndoCommand() , _firstRedoCalled(false) , _roto(roto) , _dx(dx) , _dy(dy) , _featherLinkEnabled( roto->getContext()->isFeatherLinkEnabled() ) , _rippleEditEnabled( roto->getContext()->isRippleEditEnabled() ) - , _selectedTool( (int)roto->getSelectedTool() ) + , _selectedTool(roto->selectedToolAction ) , _time(time) , _pointsToDrag(toDrag) { + + setText( QObject::tr("Move control points").toStdString() ); + assert(roto); roto->getSelection(&_selectedCurves, &_selectedPoints); @@ -136,10 +138,11 @@ MoveControlPointsUndoCommand::undo() (*it)->incrementNodesAge(); } - _roto->evaluate(true); - _roto->setCurrentTool( (RotoGui::RotoToolEnum)_selectedTool, true ); - _roto->setSelection(_selectedCurves, _selectedPoints); - setText( QObject::tr("Move points of %1").arg( _roto->getNodeName() ) ); + boost::shared_ptr roto = _roto.lock(); + roto->evaluate(true); + roto->setCurrentTool(_selectedTool.lock()); + roto->setSelection(_selectedCurves, _selectedPoints); + } void @@ -149,18 +152,25 @@ MoveControlPointsUndoCommand::redo() assert( _pointsToDrag.size() == _indexesToMove.size() ); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + RotoToolEnum selectedTool; + bool ok = roto->getToolForAction(_selectedTool.lock(), &selectedTool); + try { for (std::list::iterator it = _indexesToMove.begin(); it != _indexesToMove.end(); ++it, ++itPoints) { if ( itPoints->first->isFeatherPoint() ) { - if ( ( (RotoGui::RotoToolEnum)_selectedTool == RotoGui::eRotoToolSelectFeatherPoints ) || - ( (RotoGui::RotoToolEnum)_selectedTool == RotoGui::eRotoToolSelectAll ) || - ( (RotoGui::RotoToolEnum)_selectedTool == RotoGui::eRotoToolDrawBezier ) ) { + if ( ok && (( selectedTool == eRotoToolSelectFeatherPoints ) || + ( selectedTool == eRotoToolSelectAll ) || + ( selectedTool == eRotoToolDrawBezier )) ) { itPoints->first->getBezier()->moveFeatherByIndex(*it, _time, _dx, _dy); } } else { - if ( ( (RotoGui::RotoToolEnum)_selectedTool == RotoGui::eRotoToolSelectPoints ) || - ( (RotoGui::RotoToolEnum)_selectedTool == RotoGui::eRotoToolSelectAll ) || - ( (RotoGui::RotoToolEnum)_selectedTool == RotoGui::eRotoToolDrawBezier ) ) { + if ( ok && (( selectedTool == eRotoToolSelectPoints ) || + ( selectedTool == eRotoToolSelectAll ) || + ( selectedTool == eRotoToolDrawBezier )) ) { itPoints->first->getBezier()->movePointByIndex(*it, _time, _dx, _dy); } } @@ -170,30 +180,23 @@ MoveControlPointsUndoCommand::redo() } if (_firstRedoCalled) { - _roto->setSelection(_selectedCurves, _selectedPoints); - _roto->evaluate(true); + roto->setSelection(_selectedCurves, _selectedPoints); + roto->evaluate(true); } _firstRedoCalled = true; - setText( QObject::tr("Move points of %1").arg( _roto->getNodeName() ) ); -} - -int -MoveControlPointsUndoCommand::id() const -{ - return kRotoMoveControlPointsCompressionID; } bool -MoveControlPointsUndoCommand::mergeWith(const QUndoCommand *other) +MoveControlPointsUndoCommand::mergeWith(const UndoCommandPtr& other) { - const MoveControlPointsUndoCommand* mvCmd = dynamic_cast(other); + const MoveControlPointsUndoCommand* mvCmd = dynamic_cast(other.get()); if (!mvCmd) { return false; } - if ( ( mvCmd->_pointsToDrag.size() != _pointsToDrag.size() ) || (mvCmd->_time != _time) || (mvCmd->_selectedTool != _selectedTool) + if ( ( mvCmd->_pointsToDrag.size() != _pointsToDrag.size() ) || (mvCmd->_time != _time) || (mvCmd->_selectedTool.lock() != _selectedTool.lock()) || ( mvCmd->_rippleEditEnabled != _rippleEditEnabled) || ( mvCmd->_featherLinkEnabled != _featherLinkEnabled) ) { return false; } @@ -214,7 +217,7 @@ MoveControlPointsUndoCommand::mergeWith(const QUndoCommand *other) //////////////////////// -TransformUndoCommand::TransformUndoCommand(RotoGui* roto, +TransformUndoCommand::TransformUndoCommand(const boost::shared_ptr& roto, double centerX, double centerY, double rot, @@ -225,11 +228,11 @@ TransformUndoCommand::TransformUndoCommand(RotoGui* roto, double sx, double sy, double time) - : QUndoCommand() + : UndoCommand() , _firstRedoCalled(false) , _roto(roto) , _rippleEditEnabled( roto->getContext()->isRippleEditEnabled() ) - , _selectedTool( (int)roto->getSelectedTool() ) + , _selectedTool(roto->selectedToolAction) , _matrix(new Transform::Matrix3x3) , _time(time) , _selectedCurves() @@ -253,6 +256,8 @@ TransformUndoCommand::TransformUndoCommand(RotoGui* roto, } _originalPoints.push_back( std::make_pair(first, second) ); } + + setText( QObject::tr("Transform control points").toStdString()); } TransformUndoCommand::~TransformUndoCommand() @@ -281,10 +286,15 @@ TransformUndoCommand::undo() } } - _roto->evaluate(true); - _roto->setCurrentTool( (RotoGui::RotoToolEnum)_selectedTool, true ); - _roto->setSelection(_selectedCurves, _selectedPoints); - setText( QObject::tr("Transform points of %1").arg( _roto->getNodeName() ) ); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + + roto->evaluate(true); + roto->setCurrentTool(_selectedTool.lock()); + roto->setSelection(_selectedCurves, _selectedPoints); + } void @@ -303,34 +313,32 @@ TransformUndoCommand::redo() } } + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } if (_firstRedoCalled) { - _roto->setSelection(_selectedCurves, _selectedPoints); - _roto->evaluate(true); + roto->setSelection(_selectedCurves, _selectedPoints); + roto->evaluate(true); } else { - _roto->refreshSelectionBBox(); - _roto->onRefreshAsked(); + roto->computeSelectedCpsBBOX(); + roto->redrawOverlays(); } _firstRedoCalled = true; - setText( QObject::tr("Transform points of %1").arg( _roto->getNodeName() ) ); } -int -TransformUndoCommand::id() const -{ - return kRotoTransformCompressionID; -} bool -TransformUndoCommand::mergeWith(const QUndoCommand *other) +TransformUndoCommand::mergeWith(const UndoCommandPtr& other) { - const TransformUndoCommand* cmd = dynamic_cast(other); + const TransformUndoCommand* cmd = dynamic_cast(other.get()); if (!cmd) { return false; } - if ( ( cmd->_selectedPoints.size() != _selectedPoints.size() ) || (cmd->_time != _time) || (cmd->_selectedTool != _selectedTool) + if ( ( cmd->_selectedPoints.size() != _selectedPoints.size() ) || (cmd->_time != _time) || (cmd->_selectedTool.lock() != _selectedTool.lock()) || ( cmd->_rippleEditEnabled != _rippleEditEnabled) ) { return false; } @@ -351,17 +359,18 @@ TransformUndoCommand::mergeWith(const QUndoCommand *other) //////////////////////// -AddPointUndoCommand::AddPointUndoCommand(RotoGui* roto, +AddPointUndoCommand::AddPointUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr & curve, int index, double t) - : QUndoCommand() + : UndoCommand() , _firstRedoCalled(false) , _roto(roto) , _curve(curve) , _index(index) , _t(t) { + setText( QObject::tr("Add control point").toStdString()); } AddPointUndoCommand::~AddPointUndoCommand() @@ -371,10 +380,14 @@ AddPointUndoCommand::~AddPointUndoCommand() void AddPointUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } _curve->removeControlPointByIndex(_index + 1); - _roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); - _roto->evaluate(true); - setText( QObject::tr("Add point to %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); + roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); + roto->evaluate(true); + } void @@ -383,21 +396,25 @@ AddPointUndoCommand::redo() boost::shared_ptr cp = _curve->addControlPointAfterIndex(_index, _t); boost::shared_ptr newFp = _curve->getFeatherPointAtIndex(_index + 1); - _roto->setSelection( _curve, std::make_pair(cp, newFp) ); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + roto->setSelection( _curve, std::make_pair(cp, newFp) ); if (_firstRedoCalled) { - _roto->evaluate(true); + roto->evaluate(true); } _firstRedoCalled = true; - setText( QObject::tr("Add point to %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); + } //////////////////////// -RemovePointUndoCommand::RemovePointUndoCommand(RotoGui* roto, +RemovePointUndoCommand::RemovePointUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr & curve, const boost::shared_ptr & cp) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedoCalled(false) , _curves() @@ -408,7 +425,7 @@ RemovePointUndoCommand::RemovePointUndoCommand(RotoGui* roto, int indexToRemove = curve->getControlPointIndex(cp); desc.curveRemoved = false; //set in the redo() desc.parentLayer = - boost::dynamic_pointer_cast( _roto->getContext()->getItemByName( curve->getParentLayer()->getScriptName() ) ); + boost::dynamic_pointer_cast( roto->getContext()->getItemByName( curve->getParentLayer()->getScriptName() ) ); assert(desc.parentLayer); desc.curve = curve; desc.points.push_back(indexToRemove); @@ -417,9 +434,9 @@ RemovePointUndoCommand::RemovePointUndoCommand(RotoGui* roto, _curves.push_back(desc); } -RemovePointUndoCommand::RemovePointUndoCommand(RotoGui* roto, +RemovePointUndoCommand::RemovePointUndoCommand(const boost::shared_ptr& roto, const SelectedCpList & points) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedoCalled(false) , _curves() @@ -431,8 +448,8 @@ RemovePointUndoCommand::RemovePointUndoCommand(RotoGui* roto, } else { cp = it->first; } - assert( cp && cp->getBezier() && _roto && _roto->getContext() ); - BezierPtr curve = boost::dynamic_pointer_cast( _roto->getContext()->getItemByName( cp->getBezier()->getScriptName() ) ); + assert( cp && cp->getBezier() && roto && roto->getContext() ); + BezierPtr curve = boost::dynamic_pointer_cast( roto->getContext()->getItemByName( cp->getBezier()->getScriptName() ) ); assert(curve); RotoStrokeItem* isStroke = dynamic_cast( curve.get() ); std::list< CurveDesc >::iterator foundCurve = _curves.end(); @@ -452,7 +469,7 @@ RemovePointUndoCommand::RemovePointUndoCommand(RotoGui* roto, CurveDesc curveDesc; curveDesc.curveRemoved = false; //set in the redo() curveDesc.parentLayer = - boost::dynamic_pointer_cast( _roto->getContext()->getItemByName( cp->getBezier()->getParentLayer()->getScriptName() ) ); + boost::dynamic_pointer_cast( roto->getContext()->getItemByName( cp->getBezier()->getParentLayer()->getScriptName() ) ); assert(curveDesc.parentLayer); curveDesc.points.push_back(indexToRemove); curveDesc.curve = curve; @@ -470,6 +487,9 @@ RemovePointUndoCommand::RemovePointUndoCommand(RotoGui* roto, for (std::list::iterator it = _curves.begin(); it != _curves.end(); ++it) { it->points.sort(); } + + setText( QObject::tr("Remove control points").toStdString()); + } RemovePointUndoCommand::~RemovePointUndoCommand() @@ -479,6 +499,11 @@ RemovePointUndoCommand::~RemovePointUndoCommand() void RemovePointUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + std::list > selection; SelectedCpList cpSelection; @@ -486,20 +511,25 @@ RemovePointUndoCommand::undo() ///clone the curve it->curve->clone( it->oldCurve.get() ); if (it->curveRemoved) { - _roto->getContext()->addItem(it->parentLayer, it->indexInLayer, it->curve, RotoItem::eSelectionReasonOverlayInteract); + roto->getContext()->addItem(it->parentLayer, it->indexInLayer, it->curve, RotoItem::eSelectionReasonOverlayInteract); } selection.push_back(it->curve); } - _roto->setSelection(selection, cpSelection); - _roto->evaluate(true); + roto->setSelection(selection, cpSelection); + roto->evaluate(true); - setText( QObject::tr("Remove points to %1").arg( _roto->getNodeName() ) ); } void RemovePointUndoCommand::redo() { + + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + ///clone the curve for (std::list< CurveDesc >::iterator it = _curves.begin(); it != _curves.end(); ++it) { it->oldCurve->clone( it->curve.get() ); @@ -531,22 +561,21 @@ RemovePointUndoCommand::redo() } for (std::list >::iterator it = toRemove.begin(); it != toRemove.end(); ++it) { - _roto->removeCurve(*it); + roto->removeCurve(*it); } - _roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); - _roto->evaluate(_firstRedoCalled); + roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); + roto->evaluate(_firstRedoCalled); _firstRedoCalled = true; - setText( QObject::tr("Remove points to %1").arg( _roto->getNodeName() ) ); } ////////////////////////// -RemoveCurveUndoCommand::RemoveCurveUndoCommand(RotoGui* roto, +RemoveCurveUndoCommand::RemoveCurveUndoCommand(const boost::shared_ptr& roto, const std::list > & curves) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedoCalled(false) , _curves() @@ -554,12 +583,14 @@ RemoveCurveUndoCommand::RemoveCurveUndoCommand(RotoGui* roto, for (std::list >::const_iterator it = curves.begin(); it != curves.end(); ++it) { RemovedCurve r; r.curve = *it; - r.layer = boost::dynamic_pointer_cast( _roto->getContext()->getItemByName( (*it)->getParentLayer()->getScriptName() ) ); + r.layer = boost::dynamic_pointer_cast( roto->getContext()->getItemByName( (*it)->getParentLayer()->getScriptName() ) ); assert(r.layer); r.indexInLayer = r.layer->getChildIndex(*it); assert(r.indexInLayer != -1); _curves.push_back(r); } + setText( QObject::tr("Remove shape(s)").toStdString()); + } RemoveCurveUndoCommand::~RemoveCurveUndoCommand() @@ -569,10 +600,14 @@ RemoveCurveUndoCommand::~RemoveCurveUndoCommand() void RemoveCurveUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } std::list > selection; for (std::list::iterator it = _curves.begin(); it != _curves.end(); ++it) { - _roto->getContext()->addItem(it->layer, it->indexInLayer, it->curve, RotoItem::eSelectionReasonOverlayInteract); + roto->getContext()->addItem(it->layer, it->indexInLayer, it->curve, RotoItem::eSelectionReasonOverlayInteract); boost::shared_ptr isBezier = boost::dynamic_pointer_cast(it->curve); if (isBezier) { selection.push_back(isBezier); @@ -581,31 +616,34 @@ RemoveCurveUndoCommand::undo() SelectedCpList cpList; if ( !selection.empty() ) { - _roto->setSelection(selection, cpList); + roto->setSelection(selection, cpList); } - _roto->evaluate(true); + roto->evaluate(true); - setText( QObject::tr("Remove curves to %1").arg( _roto->getNodeName() ) ); } void RemoveCurveUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + for (std::list::iterator it = _curves.begin(); it != _curves.end(); ++it) { - _roto->removeCurve(it->curve); + roto->removeCurve(it->curve); } - _roto->evaluate(_firstRedoCalled); - _roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); + roto->evaluate(_firstRedoCalled); + roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); _firstRedoCalled = true; - setText( QObject::tr("Remove curves to %1").arg( _roto->getNodeName() ) ); } //////////////////////////////// -AddStrokeUndoCommand::AddStrokeUndoCommand(RotoGui* roto, +AddStrokeUndoCommand::AddStrokeUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr& item) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedoCalled(false) , _item(item) @@ -613,7 +651,7 @@ AddStrokeUndoCommand::AddStrokeUndoCommand(RotoGui* roto, , _indexInLayer(_layer ? _layer->getChildIndex(_item) : -1) { assert(_indexInLayer != -1); - setText( QObject::tr("Paint Stroke") ); + setText( QObject::tr("Paint Stroke").toStdString() ); } AddStrokeUndoCommand::~AddStrokeUndoCommand() @@ -623,25 +661,35 @@ AddStrokeUndoCommand::~AddStrokeUndoCommand() void AddStrokeUndoCommand::undo() { - _roto->removeCurve(_item); - _roto->evaluate(true); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + + roto->removeCurve(_item); + roto->evaluate(true); } void AddStrokeUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + if (_firstRedoCalled) { - _roto->getContext()->addItem(_layer, _indexInLayer, _item, RotoItem::eSelectionReasonOverlayInteract); + roto->getContext()->addItem(_layer, _indexInLayer, _item, RotoItem::eSelectionReasonOverlayInteract); } if (_firstRedoCalled) { - _roto->evaluate(true); + roto->evaluate(true); } _firstRedoCalled = true; } -AddMultiStrokeUndoCommand::AddMultiStrokeUndoCommand(RotoGui* roto, +AddMultiStrokeUndoCommand::AddMultiStrokeUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr& item) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedoCalled(false) , _item(item) @@ -650,7 +698,7 @@ AddMultiStrokeUndoCommand::AddMultiStrokeUndoCommand(RotoGui* roto, , isRemoved(false) { assert(_indexInLayer != -1); - setText( QObject::tr("Paint Stroke") ); + setText( QObject::tr("Paint Stroke").toStdString() ); } AddMultiStrokeUndoCommand::~AddMultiStrokeUndoCommand() @@ -660,38 +708,48 @@ AddMultiStrokeUndoCommand::~AddMultiStrokeUndoCommand() void AddMultiStrokeUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + if ( _item->removeLastStroke(&_xCurve, &_yCurve, &_pCurve) ) { - _roto->removeCurve(_item); + roto->removeCurve(_item); isRemoved = true; } - _roto->evaluate(true); + roto->evaluate(true); } void AddMultiStrokeUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + if (_firstRedoCalled) { if (_xCurve) { _item->addStroke(_xCurve, _yCurve, _pCurve); } if (isRemoved) { - _roto->getContext()->addItem(_layer, _indexInLayer, _item, RotoItem::eSelectionReasonOverlayInteract); + roto->getContext()->addItem(_layer, _indexInLayer, _item, RotoItem::eSelectionReasonOverlayInteract); } - _roto->evaluate(true); + roto->evaluate(true); } _firstRedoCalled = true; } -MoveTangentUndoCommand::MoveTangentUndoCommand(RotoGui* roto, +MoveTangentUndoCommand::MoveTangentUndoCommand(const boost::shared_ptr& roto, double dx, double dy, double time, const boost::shared_ptr & cp, bool left, bool breakTangents) - : QUndoCommand() + : UndoCommand() , _firstRedoCalled(false) , _roto(roto) , _dx(dx) @@ -718,6 +776,8 @@ MoveTangentUndoCommand::MoveTangentUndoCommand(RotoGui* roto, _oldFp.reset( new BezierCP(*counterPart) ); } } + + setText( QObject::tr("Move control point tangent").toStdString()); } MoveTangentUndoCommand::~MoveTangentUndoCommand() @@ -802,6 +862,11 @@ NATRON_NAMESPACE_ANONYMOUS_EXIT void MoveTangentUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + boost::shared_ptr counterPart; if ( _tangentBeingDragged->isFeatherPoint() ) { @@ -819,17 +884,22 @@ MoveTangentUndoCommand::undo() } _tangentBeingDragged->getBezier()->incrementNodesAge(); if (_firstRedoCalled) { - _roto->setSelection(_selectedCurves, _selectedPoints); + roto->setSelection(_selectedCurves, _selectedPoints); } - _roto->evaluate(true); + roto->evaluate(true); - setText( QObject::tr("Move tangent of %1 of %2").arg( QString::fromUtf8( _tangentBeingDragged->getBezier()->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); } void MoveTangentUndoCommand::redo() { + + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + boost::shared_ptr cp, fp; if ( _tangentBeingDragged->isFeatherPoint() ) { @@ -851,35 +921,29 @@ MoveTangentUndoCommand::redo() Transform::Matrix3x3 transform; _tangentBeingDragged->getBezier()->getTransformAtTime(_time, &transform); - bool autoKeying = _roto->getContext()->isAutoKeyingEnabled(); + bool autoKeying = roto->getContext()->isAutoKeyingEnabled(); dragTangent( _time, *cp, *fp, transform, _dx, _dy, _left, autoKeying, _breakTangents, _tangentBeingDragged->isFeatherPoint() ); if (_firstRedoCalled) { - _roto->setSelection(_selectedCurves, _selectedPoints); - _roto->evaluate(true); + roto->setSelection(_selectedCurves, _selectedPoints); + roto->evaluate(true); } else { - _roto->refreshSelectionBBox(); + roto->computeSelectedCpsBBOX(); } _firstRedoCalled = true; - setText( QObject::tr("Move tangent of %1 of %2").arg( QString::fromUtf8( _tangentBeingDragged->getBezier()->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); } -int -MoveTangentUndoCommand::id() const -{ - return kRotoMoveTangentCompressionID; -} bool -MoveTangentUndoCommand::mergeWith(const QUndoCommand *other) +MoveTangentUndoCommand::mergeWith(const UndoCommandPtr &other) { - const MoveTangentUndoCommand* mvCmd = dynamic_cast(other); + const MoveTangentUndoCommand* mvCmd = dynamic_cast(other.get()); if (!mvCmd) { return false; @@ -897,12 +961,12 @@ MoveTangentUndoCommand::mergeWith(const QUndoCommand *other) ////////////////////////// -MoveFeatherBarUndoCommand::MoveFeatherBarUndoCommand(RotoGui* roto, +MoveFeatherBarUndoCommand::MoveFeatherBarUndoCommand(const boost::shared_ptr& roto, double dx, double dy, const std::pair, boost::shared_ptr > & point, double time) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedoCalled(false) , _dx(dx) @@ -913,10 +977,12 @@ MoveFeatherBarUndoCommand::MoveFeatherBarUndoCommand(RotoGui* roto, , _oldPoint() , _newPoint(point) { - _curve = boost::dynamic_pointer_cast( _roto->getContext()->getItemByName( point.first->getBezier()->getScriptName() ) ); + _curve = boost::dynamic_pointer_cast( roto->getContext()->getItemByName( point.first->getBezier()->getScriptName() ) ); assert(_curve); _oldPoint.first.reset( new BezierCP(*_newPoint.first) ); _oldPoint.second.reset( new BezierCP(*_newPoint.second) ); + setText( QObject::tr("Move control point feather bar").toStdString()); + } MoveFeatherBarUndoCommand::~MoveFeatherBarUndoCommand() @@ -926,17 +992,24 @@ MoveFeatherBarUndoCommand::~MoveFeatherBarUndoCommand() void MoveFeatherBarUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } _newPoint.first->clone(*_oldPoint.first); _newPoint.second->clone(*_oldPoint.second); _newPoint.first->getBezier()->incrementNodesAge(); - _roto->evaluate(true); - _roto->setSelection(_curve, _newPoint); - setText( QObject::tr("Move feather bar of %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); + roto->evaluate(true); + roto->setSelection(_curve, _newPoint); } void MoveFeatherBarUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } boost::shared_ptr p = _newPoint.first->isFeatherPoint() ? _newPoint.second : _newPoint.first; boost::shared_ptr fp = _newPoint.first->isFeatherPoint() ? @@ -1014,31 +1087,25 @@ MoveFeatherBarUndoCommand::redo() delta.y = delta.y * dotProduct; } - if (_roto->getContext()->isAutoKeyingEnabled() || isOnKeyframe) { + if (roto->getContext()->isAutoKeyingEnabled() || isOnKeyframe) { int index = fp->getBezier()->getFeatherPointIndex(fp); fp->getBezier()->moveFeatherByIndex(index, _time, delta.x, delta.y); } if (_firstRedoCalled) { - _roto->evaluate(true); + roto->evaluate(true); } - _roto->setSelection(_curve, _newPoint); + roto->setSelection(_curve, _newPoint); _firstRedoCalled = true; - setText( QObject::tr("Move feather bar of %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); } // redo -int -MoveFeatherBarUndoCommand::id() const -{ - return kRotoMoveFeatherBarCompressionID; -} bool -MoveFeatherBarUndoCommand::mergeWith(const QUndoCommand *other) +MoveFeatherBarUndoCommand::mergeWith(const UndoCommandPtr& other) { - const MoveFeatherBarUndoCommand* mvCmd = dynamic_cast(other); + const MoveFeatherBarUndoCommand* mvCmd = dynamic_cast(other.get()); if (!mvCmd) { return false; @@ -1056,9 +1123,9 @@ MoveFeatherBarUndoCommand::mergeWith(const QUndoCommand *other) ///////////////////////// -RemoveFeatherUndoCommand::RemoveFeatherUndoCommand(RotoGui* roto, +RemoveFeatherUndoCommand::RemoveFeatherUndoCommand(const boost::shared_ptr& roto, const std::list & datas) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedocalled(false) , _datas(datas) @@ -1068,6 +1135,7 @@ RemoveFeatherUndoCommand::RemoveFeatherUndoCommand(RotoGui* roto, it->oldPoints.push_back( boost::shared_ptr( new BezierCP(**it2) ) ); } } + setText( QObject::tr("Remove feather").toStdString()); } RemoveFeatherUndoCommand::~RemoveFeatherUndoCommand() @@ -1085,9 +1153,13 @@ RemoveFeatherUndoCommand::undo() } it->curve->incrementNodesAge(); } - _roto->evaluate(true); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + roto->evaluate(true); + - setText( QObject::tr("Remove feather of %1").arg( _roto->getNodeName() ) ); } void @@ -1106,27 +1178,30 @@ RemoveFeatherUndoCommand::redo() } } } + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } - - _roto->evaluate(_firstRedocalled); + roto->evaluate(_firstRedocalled); _firstRedocalled = true; - setText( QObject::tr("Remove feather of %1").arg( _roto->getNodeName() ) ); } //////////////////////////// -OpenCloseUndoCommand::OpenCloseUndoCommand(RotoGui* roto, +OpenCloseUndoCommand::OpenCloseUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr & curve) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedoCalled(false) - , _selectedTool( (int)roto->getSelectedTool() ) + , _selectedTool(roto->selectedToolAction ) , _curve(curve) { + setText( QObject::tr("Open/Close bezier").toStdString()); } OpenCloseUndoCommand::~OpenCloseUndoCommand() @@ -1136,39 +1211,46 @@ OpenCloseUndoCommand::~OpenCloseUndoCommand() void OpenCloseUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } if (_firstRedoCalled) { - _roto->setCurrentTool( (RotoGui::RotoToolEnum)_selectedTool, true ); - if ( (RotoGui::RotoToolEnum)_selectedTool == RotoGui::eRotoToolDrawBezier ) { - _roto->setBuiltBezier(_curve); + roto->setCurrentTool( _selectedTool.lock() ); + if ( _selectedTool.lock() == roto->drawBezierAction.lock() ) { + roto->setBuiltBezier(_curve); } } _curve->setCurveFinished( !_curve->isCurveFinished() ); - _roto->evaluate(true); - _roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); - setText( QObject::tr("Open/Close %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); + roto->evaluate(true); + roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); + } void OpenCloseUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } if (_firstRedoCalled) { - _roto->setCurrentTool( (RotoGui::RotoToolEnum)_selectedTool, true ); + roto->setCurrentTool(_selectedTool.lock() ); } _curve->setCurveFinished( !_curve->isCurveFinished() ); - _roto->evaluate(_firstRedoCalled); - _roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); + roto->evaluate(_firstRedoCalled); + roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); _firstRedoCalled = true; - setText( QObject::tr("Open/Close %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); } //////////////////////////// -SmoothCuspUndoCommand::SmoothCuspUndoCommand(RotoGui* roto, +SmoothCuspUndoCommand::SmoothCuspUndoCommand(const boost::shared_ptr& roto, const std::list & data, double time, bool cusp, const std::pair& pixelScale) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _firstRedoCalled(false) , _time(time) @@ -1184,6 +1266,11 @@ SmoothCuspUndoCommand::SmoothCuspUndoCommand(RotoGui* roto, it->oldPoints.push_back( std::make_pair(firstCpy, secondCpy) ); } } + if (_cusp) { + setText( QObject::tr("Cusp points").toStdString()); + } else { + setText( QObject::tr("Smooth points").toStdString()); + } } SmoothCuspUndoCommand::~SmoothCuspUndoCommand() @@ -1193,6 +1280,10 @@ SmoothCuspUndoCommand::~SmoothCuspUndoCommand() void SmoothCuspUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } for (std::list::iterator it = curves.begin(); it != curves.end(); ++it) { SelectedPointList::const_iterator itOld = it->oldPoints.begin(); for (SelectedPointList::const_iterator itNew = it->newPoints.begin(); @@ -1203,17 +1294,17 @@ SmoothCuspUndoCommand::undo() } } - _roto->evaluate(true); - if (_cusp) { - setText( QObject::tr("Cusp points of %1").arg( _roto->getNodeName() ) ); - } else { - setText( QObject::tr("Smooth points of %1").arg( _roto->getNodeName() ) ); - } + roto->evaluate(true); + } void SmoothCuspUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } for (std::list::iterator it = curves.begin(); it != curves.end(); ++it) { SelectedPointList::const_iterator itOld = it->oldPoints.begin(); for (SelectedPointList::const_iterator itNew = it->newPoints.begin(); @@ -1234,26 +1325,18 @@ SmoothCuspUndoCommand::redo() } } - _roto->evaluate(_firstRedoCalled); + roto->evaluate(_firstRedoCalled); _firstRedoCalled = true; - if (_cusp) { - setText( QObject::tr("Cusp points of %1").arg( _roto->getNodeName() ) ); - } else { - setText( QObject::tr("Smooth points of %1").arg( _roto->getNodeName() ) ); - } -} -int -SmoothCuspUndoCommand::id() const -{ - return kRotoCuspSmoothCompressionID; } + + bool -SmoothCuspUndoCommand::mergeWith(const QUndoCommand *other) +SmoothCuspUndoCommand::mergeWith(const UndoCommandPtr& other) { - const SmoothCuspUndoCommand* sCmd = dynamic_cast(other); + const SmoothCuspUndoCommand* sCmd = dynamic_cast(other.get()); if (!sCmd) { return false; @@ -1284,14 +1367,14 @@ SmoothCuspUndoCommand::mergeWith(const QUndoCommand *other) ///////////////////////// -MakeBezierUndoCommand::MakeBezierUndoCommand(RotoGui* roto, +MakeBezierUndoCommand::MakeBezierUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr & curve, bool isOpenBezier, bool createPoint, double dx, double dy, double time) - : QUndoCommand() + : UndoCommand() , _firstRedoCalled(false) , _roto(roto) , _parentLayer() @@ -1314,6 +1397,8 @@ MakeBezierUndoCommand::MakeBezierUndoCommand(RotoGui* roto, _oldCurve.reset( new Bezier(_newCurve->getContext(), _newCurve->getScriptName(), _newCurve->getParentLayer(), false) ); _oldCurve->clone( _newCurve.get() ); } + setText( QObject::tr("Draw Bezier").toStdString()); + } MakeBezierUndoCommand::~MakeBezierUndoCommand() @@ -1323,38 +1408,46 @@ MakeBezierUndoCommand::~MakeBezierUndoCommand() void MakeBezierUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + assert(_createdPoint); - _roto->setCurrentTool(RotoGui::eRotoToolDrawBezier, true); + roto->setCurrentTool(roto->drawBezierAction.lock()); assert(_lastPointAdded != -1); _oldCurve->clone( _newCurve.get() ); if (_newCurve->getControlPointsCount() == 1) { _curveNonExistant = true; - _roto->removeCurve(_newCurve); + roto->removeCurve(_newCurve); } _newCurve->removeControlPointByIndex(_lastPointAdded); if (!_curveNonExistant) { - _roto->setSelection( _newCurve, std::make_pair( CpPtr(), CpPtr() ) ); - _roto->setBuiltBezier(_newCurve); + roto->setSelection( _newCurve, std::make_pair( CpPtr(), CpPtr() ) ); + roto->setBuiltBezier(_newCurve); } else { - _roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); + roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); } - _roto->evaluate(true); - setText( QObject::tr("Build bezier %1 of %2").arg( QString::fromUtf8( _newCurve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); + roto->evaluate(true); } void MakeBezierUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } if (_firstRedoCalled) { - _roto->setCurrentTool(RotoGui::eRotoToolDrawBezier, true); + roto->setCurrentTool(roto->drawBezierAction.lock()); } if (!_firstRedoCalled) { if (_createdPoint) { if (!_newCurve) { - _newCurve = _roto->getContext()->makeBezier(_x, _y, _isOpenBezier ? kRotoOpenBezierBaseName : kRotoBezierBaseName, _time, _isOpenBezier); + _newCurve = roto->getContext()->makeBezier(_x, _y, _isOpenBezier ? kRotoOpenBezierBaseName : kRotoBezierBaseName, _time, _isOpenBezier); assert(_newCurve); _oldCurve.reset( new Bezier(_newCurve->getContext(), _newCurve->getScriptName(), _newCurve->getParentLayer(), false) ); _oldCurve->clone( _newCurve.get() ); @@ -1379,7 +1472,7 @@ MakeBezierUndoCommand::redo() boost::shared_ptr parentItem; if ( _newCurve->getParentLayer() ) { - parentItem = _roto->getContext()->getItemByName( _newCurve->getParentLayer()->getScriptName() ); + parentItem = roto->getContext()->getItemByName( _newCurve->getParentLayer()->getScriptName() ); } if (parentItem) { _parentLayer = boost::dynamic_pointer_cast(parentItem); @@ -1388,31 +1481,25 @@ MakeBezierUndoCommand::redo() } else { _newCurve->clone( _oldCurve.get() ); if (_curveNonExistant) { - _roto->getContext()->addItem(_parentLayer, _indexInLayer, _newCurve, RotoItem::eSelectionReasonOverlayInteract); + roto->getContext()->addItem(_parentLayer, _indexInLayer, _newCurve, RotoItem::eSelectionReasonOverlayInteract); } } boost::shared_ptr cp = _newCurve->getControlPointAtIndex(_lastPointAdded); boost::shared_ptr fp = _newCurve->getFeatherPointAtIndex(_lastPointAdded); - _roto->setSelection( _newCurve, std::make_pair(cp, fp) ); - _roto->autoSaveAndRedraw(); + roto->setSelection( _newCurve, std::make_pair(cp, fp) ); + roto->autoSaveAndRedraw(); _firstRedoCalled = true; - setText( QObject::tr("Build bezier %1 of %2").arg( QString::fromUtf8( _newCurve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); } // redo -int -MakeBezierUndoCommand::id() const -{ - return kRotoMakeBezierCompressionID; -} bool -MakeBezierUndoCommand::mergeWith(const QUndoCommand *other) +MakeBezierUndoCommand::mergeWith(const UndoCommandPtr& other) { - const MakeBezierUndoCommand* sCmd = dynamic_cast(other); + const MakeBezierUndoCommand* sCmd = dynamic_cast(other.get()); if (!sCmd) { return false; @@ -1433,7 +1520,7 @@ MakeBezierUndoCommand::mergeWith(const QUndoCommand *other) ////////////////////////////// -MakeEllipseUndoCommand::MakeEllipseUndoCommand(RotoGui* roto, +MakeEllipseUndoCommand::MakeEllipseUndoCommand(const boost::shared_ptr& roto, bool create, bool fromCenter, bool constrained, @@ -1442,7 +1529,7 @@ MakeEllipseUndoCommand::MakeEllipseUndoCommand(RotoGui* roto, double tox, double toy, double time) - : QUndoCommand() + : UndoCommand() , _firstRedoCalled(false) , _roto(roto) , _indexInLayer(-1) @@ -1457,8 +1544,10 @@ MakeEllipseUndoCommand::MakeEllipseUndoCommand(RotoGui* roto, , _time(time) { if (!_create) { - _curve = _roto->getBezierBeingBuild(); + _curve = roto->getBezierBeingBuild(); } + setText( QObject::tr("Draw Ellipse").toStdString()); + } MakeEllipseUndoCommand::~MakeEllipseUndoCommand() @@ -1468,18 +1557,25 @@ MakeEllipseUndoCommand::~MakeEllipseUndoCommand() void MakeEllipseUndoCommand::undo() { - _roto->removeCurve(_curve); - _roto->evaluate(true); - _roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); - setText( QObject::tr("Build Ellipse %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + roto->removeCurve(_curve); + roto->evaluate(true); + roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); } void MakeEllipseUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } if (_firstRedoCalled) { - _roto->getContext()->addItem(_parentLayer, _indexInLayer, _curve, RotoItem::eSelectionReasonOverlayInteract); - _roto->evaluate(true); + roto->getContext()->addItem(_parentLayer, _indexInLayer, _curve, RotoItem::eSelectionReasonOverlayInteract); + roto->evaluate(true); } else { double ytop, xright, ybottom, xleft; xright = _tox; @@ -1506,7 +1602,7 @@ MakeEllipseUndoCommand::redo() double xmid = (xleft + xright) / 2.; double ymid = (ytop + ybottom) / 2.; if (_create) { - _curve = _roto->getContext()->makeBezier(xmid, ytop, kRotoEllipseBaseName, _time, false); //top + _curve = roto->getContext()->makeBezier(xmid, ytop, kRotoEllipseBaseName, _time, false); //top assert(_curve); _curve->addControlPoint(xright, ymid, _time); // right _curve->addControlPoint(xmid, ybottom, _time); // bottom @@ -1540,28 +1636,23 @@ MakeEllipseUndoCommand::redo() _curve->setLeftBezierPoint(3, _time, leftX, (btmY + leftY) / 2.); _curve->setRightBezierPoint(3, _time, leftX, (topY + leftY) / 2.); } - boost::shared_ptr parentItem = _roto->getContext()->getItemByName( _curve->getParentLayer()->getScriptName() ); + boost::shared_ptr parentItem = roto->getContext()->getItemByName( _curve->getParentLayer()->getScriptName() ); if (parentItem) { _parentLayer = boost::dynamic_pointer_cast(parentItem); _indexInLayer = _parentLayer->getChildIndex(_curve); } } - _roto->setBuiltBezier(_curve); + roto->setBuiltBezier(_curve); _firstRedoCalled = true; - _roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); - setText( QObject::tr("Build Ellipse %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); + roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); + } // redo -int -MakeEllipseUndoCommand::id() const -{ - return kRotoMakeEllipseCompressionID; -} bool -MakeEllipseUndoCommand::mergeWith(const QUndoCommand *other) +MakeEllipseUndoCommand::mergeWith(const UndoCommandPtr &other) { - const MakeEllipseUndoCommand* sCmd = dynamic_cast(other); + const MakeEllipseUndoCommand* sCmd = dynamic_cast(other.get()); if (!sCmd) { return false; @@ -1585,7 +1676,7 @@ MakeEllipseUndoCommand::mergeWith(const QUndoCommand *other) //////////////////////////////////// -MakeRectangleUndoCommand::MakeRectangleUndoCommand(RotoGui* roto, +MakeRectangleUndoCommand::MakeRectangleUndoCommand(const boost::shared_ptr& roto, bool create, bool fromCenter, bool constrained, @@ -1594,7 +1685,7 @@ MakeRectangleUndoCommand::MakeRectangleUndoCommand(RotoGui* roto, double tox, double toy, double time) - : QUndoCommand() + : UndoCommand() , _firstRedoCalled(false) , _roto(roto) , _parentLayer() @@ -1610,8 +1701,9 @@ MakeRectangleUndoCommand::MakeRectangleUndoCommand(RotoGui* roto, , _time(time) { if (!_create) { - _curve = _roto->getBezierBeingBuild(); + _curve = roto->getBezierBeingBuild(); } + setText( QObject::tr("Draw Rectangle").toStdString()); } MakeRectangleUndoCommand::~MakeRectangleUndoCommand() @@ -1621,18 +1713,26 @@ MakeRectangleUndoCommand::~MakeRectangleUndoCommand() void MakeRectangleUndoCommand::undo() { - _roto->removeCurve(_curve); - _roto->evaluate(true); - _roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); - setText( QObject::tr("Build Ellipse %1 of %2").arg( QString::fromUtf8( _curve->getLabel().c_str() ) ).arg( _roto->getNodeName() ) ); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + roto->removeCurve(_curve); + roto->evaluate(true); + roto->setSelection( BezierPtr(), std::make_pair( CpPtr(), CpPtr() ) ); + } void MakeRectangleUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } if (_firstRedoCalled) { - _roto->getContext()->addItem(_parentLayer, _indexInLayer, _curve, RotoItem::eSelectionReasonOverlayInteract); - _roto->evaluate(true); + roto->getContext()->addItem(_parentLayer, _indexInLayer, _curve, RotoItem::eSelectionReasonOverlayInteract); + roto->evaluate(true); } else { double ytop, xright, ybottom, xleft; xright = _tox; @@ -1657,7 +1757,7 @@ MakeRectangleUndoCommand::redo() ybottom -= 1.; } if (_create) { - _curve = _roto->getContext()->makeBezier(xleft, ytop, kRotoRectangleBaseName, _time, false); //topleft + _curve = roto->getContext()->makeBezier(xleft, ytop, kRotoRectangleBaseName, _time, false); //topleft assert(_curve); _curve->addControlPoint(xright, ytop, _time); // topright _curve->addControlPoint(xright, ybottom, _time); // bottomright @@ -1669,28 +1769,23 @@ MakeRectangleUndoCommand::redo() _curve->setPointByIndex(2, _time, xright, ybottom); // bottomright _curve->setPointByIndex(3, _time, xleft, ybottom); // bottomleft } - boost::shared_ptr parentItem = _roto->getContext()->getItemByName( _curve->getParentLayer()->getScriptName() ); + boost::shared_ptr parentItem = roto->getContext()->getItemByName( _curve->getParentLayer()->getScriptName() ); if (parentItem) { _parentLayer = boost::dynamic_pointer_cast(parentItem); _indexInLayer = _parentLayer->getChildIndex(_curve); } } - _roto->setBuiltBezier(_curve); + roto->setBuiltBezier(_curve); _firstRedoCalled = true; - _roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); - setText( QObject::tr("Build Rectangle %1 of %2").arg( QString::fromUtf8( _curve->getScriptName().c_str() ) ).arg( _roto->getNodeName() ) ); + roto->setSelection( _curve, std::make_pair( CpPtr(), CpPtr() ) ); + } // MakeRectangleUndoCommand::redo -int -MakeRectangleUndoCommand::id() const -{ - return kRotoMakeRectangleCompressionID; -} bool -MakeRectangleUndoCommand::mergeWith(const QUndoCommand *other) +MakeRectangleUndoCommand::mergeWith(const UndoCommandPtr &other) { - const MakeRectangleUndoCommand* sCmd = dynamic_cast(other); + const MakeRectangleUndoCommand* sCmd = dynamic_cast(other.get()); if (!sCmd) { return false; @@ -1711,481 +1806,17 @@ MakeRectangleUndoCommand::mergeWith(const QUndoCommand *other) return true; } -////////////////////////////// - - -RemoveItemsUndoCommand::RemoveItemsUndoCommand(RotoPanel* roto, - const QList & items) - : QUndoCommand() - , _roto(roto) - , _items() -{ - for (QList::const_iterator it = items.begin(); it != items.end(); ++it) { - QTreeWidgetItem* parentItem = (*it)->parent(); - bool foundParent = false; - for (QList::const_iterator it2 = items.begin(); it2 != items.end(); ++it2) { - if ( (*it2) == parentItem ) { - foundParent = true; - break; - } - } - if (foundParent) { - //Not necessary to add this item to the list since the parent is going to remove it anyway - continue; - } - RemovedItem r; - r.treeItem = *it; - r.parentTreeItem = parentItem; - r.item = _roto->getRotoItemForTreeItem(r.treeItem); - assert(r.item); - if (r.parentTreeItem) { - r.parentLayer = boost::dynamic_pointer_cast( _roto->getRotoItemForTreeItem(r.parentTreeItem) ); - assert(r.parentLayer); - r.indexInLayer = r.parentLayer->getChildIndex(r.item); - } - _items.push_back(r); - } -} - -RemoveItemsUndoCommand::~RemoveItemsUndoCommand() -{ -} - -void -RemoveItemsUndoCommand::undo() -{ - for (std::list::iterator it = _items.begin(); it != _items.end(); ++it) { - if (it->parentTreeItem) { - it->parentTreeItem->addChild(it->treeItem); - } - _roto->getContext()->addItem(it->parentLayer, it->indexInLayer, it->item, RotoItem::eSelectionReasonSettingsPanel); - - it->treeItem->setHidden(false); - } - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Remove items of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -void -RemoveItemsUndoCommand::redo() -{ - if ( _items.empty() ) { - return; - } - _roto->clearAndSelectPreviousItem(_items.back().item); - for (std::list::iterator it = _items.begin(); it != _items.end(); ++it) { - _roto->getContext()->removeItem(it->item, RotoItem::eSelectionReasonSettingsPanel); - it->treeItem->setHidden(true); - if ( it->treeItem->isSelected() ) { - it->treeItem->setSelected(false); - } - if (it->parentTreeItem) { - it->parentTreeItem->removeChild(it->treeItem); - } - } - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Remove items of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -///////////////////////////// - - -AddLayerUndoCommand::AddLayerUndoCommand(RotoPanel* roto) - : QUndoCommand() - , _roto(roto) - , _firstRedoCalled(false) - , _parentLayer() - , _parentTreeItem(0) - , _treeItem(0) - , _layer() - , _indexInParentLayer(-1) -{ -} - -AddLayerUndoCommand::~AddLayerUndoCommand() -{ -} - -void -AddLayerUndoCommand::undo() -{ - _treeItem->setHidden(true); - if (_parentTreeItem) { - _parentTreeItem->removeChild(_treeItem); - } - _roto->getContext()->removeItem(_layer, RotoItem::eSelectionReasonSettingsPanel); - _roto->clearSelection(); - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Add layer to %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -void -AddLayerUndoCommand::redo() -{ - if (!_firstRedoCalled) { - _layer = _roto->getContext()->addLayer(); - _parentLayer = _layer->getParentLayer(); - _treeItem = _roto->getTreeItemForRotoItem(_layer); - _parentTreeItem = _treeItem->parent(); - if (_parentLayer) { - _indexInParentLayer = _parentLayer->getChildIndex(_layer); - } - } else { - _roto->getContext()->addLayer(_layer); - _treeItem->setHidden(false); - if (_parentLayer) { - _roto->getContext()->addItem(_parentLayer, _indexInParentLayer, _layer, RotoItem::eSelectionReasonSettingsPanel); - _parentTreeItem->addChild(_treeItem); - } - } - _roto->clearSelection(); - _roto->getContext()->select(_layer, RotoItem::eSelectionReasonOther); - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Add layer to %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); - _firstRedoCalled = true; -} - -///////////////////////////////// - -DragItemsUndoCommand::DragItemsUndoCommand(RotoPanel* roto, - const std::list< boost::shared_ptr > & items) - : QUndoCommand() - , _roto(roto) - , _items() -{ - for (std::list< boost::shared_ptr >::const_iterator it = items.begin(); it != items.end(); ++it) { - assert( (*it)->newParentLayer && (*it)->newParentItem && (*it)->insertIndex != -1 ); - Item i; - i.dropped = *it; - i.oldParentItem = (*it)->dropped->parent(); - if (!i.oldParentItem) { - continue; - } - i.oldParentLayer = (*it)->droppedRotoItem->getParentLayer(); - if (i.oldParentLayer) { - i.indexInOldLayer = i.oldParentLayer->getChildIndex( (*it)->droppedRotoItem ); - } else { - i.indexInOldLayer = -1; - } - _items.push_back(i); - } -} - -DragItemsUndoCommand::~DragItemsUndoCommand() -{ -} - -static void -createCustomWidgetRecursively(RotoPanel* panel, - const boost::shared_ptr& item) -{ - const boost::shared_ptr isDrawable = boost::dynamic_pointer_cast(item); - - if (isDrawable) { - panel->makeCustomWidgetsForItem(isDrawable); - } - const boost::shared_ptr isLayer = boost::dynamic_pointer_cast(item); - if (isLayer) { - const std::list > & children = isLayer->getItems(); - for (std::list >::const_iterator it = children.begin(); it != children.end(); ++it) { - createCustomWidgetRecursively( panel, *it ); - } - } -} - -void -DragItemsUndoCommand::undo() -{ - for (std::list::iterator it = _items.begin(); it != _items.end(); ++it) { - assert(it->dropped->newParentItem); - it->dropped->newParentItem->removeChild(it->dropped->dropped); - it->dropped->newParentLayer->removeItem(it->dropped->droppedRotoItem); - if (it->oldParentItem) { - it->oldParentItem->insertChild(it->indexInOldLayer, it->dropped->dropped); - - createCustomWidgetRecursively( _roto, it->dropped->droppedRotoItem ); - - assert(it->oldParentLayer); - it->dropped->droppedRotoItem->setParentLayer(it->oldParentLayer); - _roto->getContext()->addItem(it->oldParentLayer, it->indexInOldLayer, it->dropped->droppedRotoItem, RotoItem::eSelectionReasonSettingsPanel); - } else { - it->dropped->droppedRotoItem->setParentLayer( boost::shared_ptr() ); - } - } - _roto->getContext()->refreshRotoPaintTree(); - _roto->getContext()->evaluateChange(); - - setText( QObject::tr("Re-organize items of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -void -DragItemsUndoCommand::redo() -{ - for (std::list::iterator it = _items.begin(); it != _items.end(); ++it) { - it->oldParentItem->removeChild(it->dropped->dropped); - if (it->oldParentLayer) { - it->oldParentLayer->removeItem(it->dropped->droppedRotoItem); - } - assert(it->dropped->newParentItem); - it->dropped->newParentItem->insertChild(it->dropped->insertIndex, it->dropped->dropped); - - createCustomWidgetRecursively( _roto, it->dropped->droppedRotoItem ); - - it->dropped->newParentItem->setExpanded(true); - it->dropped->newParentLayer->insertItem(it->dropped->droppedRotoItem, it->dropped->insertIndex); - } - _roto->getContext()->refreshRotoPaintTree(); - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Re-organize items of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -////////////////////// - -static std::string -getItemCopyName(RotoPanel* roto, - const boost::shared_ptr& originalItem) -{ - int i = 1; - std::string name = originalItem->getScriptName() + "_copy"; - boost::shared_ptr foundItemWithName = roto->getContext()->getItemByName(name); - - while (foundItemWithName && foundItemWithName != originalItem) { - std::stringstream ss; - ss << originalItem->getScriptName() << "_copy " << i; - name = ss.str(); - foundItemWithName = roto->getContext()->getItemByName(name); - ++i; - } - return name; -} - -static -void -setItemCopyNameRecursive(RotoPanel* panel, - const boost::shared_ptr& item) -{ - item->setScriptName( getItemCopyName(panel, item) ); - boost::shared_ptr isLayer = boost::dynamic_pointer_cast(item); - - if (isLayer) { - for (std::list >::const_iterator it = isLayer->getItems().begin(); it != isLayer->getItems().end(); ++it) { - setItemCopyNameRecursive( panel, *it ); - } - } -} - -PasteItemUndoCommand::PasteItemUndoCommand(RotoPanel* roto, - QTreeWidgetItem* target, - QList source) - : QUndoCommand() - , _roto(roto) - , _mode() - , _targetTreeItem(target) - , _targetItem() - , _pastedItems() -{ - _targetItem = roto->getRotoItemForTreeItem(target); - assert(_targetItem); - - for (int i = 0; i < source.size(); ++i) { - PastedItem item; - item.treeItem = source[i]; - item.rotoItem = roto->getRotoItemForTreeItem(item.treeItem); - assert(item.rotoItem); - _pastedItems.push_back(item); - } - - boost::shared_ptr isDrawable = boost::dynamic_pointer_cast(_targetItem); - - if (isDrawable) { - _mode = ePasteModeCopyToItem; - assert(source.size() == 1 && _pastedItems.size() == 1); - assert( dynamic_cast( _pastedItems.front().rotoItem.get() ) ); - } else { - _mode = ePasteModeCopyToLayer; - for (std::list::iterator it = _pastedItems.begin(); it != _pastedItems.end(); ++it) { - boost::shared_ptr srcBezier = boost::dynamic_pointer_cast(it->rotoItem); - boost::shared_ptr srcLayer = boost::dynamic_pointer_cast(it->rotoItem); - boost::shared_ptr srcStroke = boost::dynamic_pointer_cast(it->rotoItem); - if (srcBezier) { - std::string name = getItemCopyName(roto, it->rotoItem); - boost::shared_ptr copy( new Bezier(srcBezier->getContext(), name, - srcBezier->getParentLayer(), false) ); - copy->clone( srcBezier.get() ); - copy->createNodes(); - //clone overwrittes the script name, don't forget to set it back - copy->setScriptName(name); - copy->setLabel(name); - it->itemCopy = copy; - } else if (srcStroke) { - std::string name = getItemCopyName(roto, it->rotoItem); - boost::shared_ptr copy( new RotoStrokeItem( srcStroke->getBrushType(), - srcStroke->getContext(), - name, - boost::shared_ptr() ) ); - copy->createNodes(); - if ( srcStroke->getParentLayer() ) { - srcStroke->getParentLayer()->insertItem(copy, 0); - } - copy->clone( srcStroke.get() ); - copy->createNodes(); - //clone overwrittes the script name, don't forget to set it back - copy->setScriptName(name); - copy->setLabel(name); - it->itemCopy = copy; - } else { - assert(srcLayer); - boost::shared_ptr copy( new RotoLayer( srcLayer->getContext(), - "", - boost::shared_ptr() ) ); - copy->clone( srcLayer.get() ); - setItemCopyNameRecursive( roto, copy ); - it->itemCopy = copy; - } - } - } -} - -PasteItemUndoCommand::~PasteItemUndoCommand() -{ -} - -void -PasteItemUndoCommand::undo() -{ - if (_mode == ePasteModeCopyToItem) { - assert(_oldTargetItem); - _roto->getContext()->deselect(_targetItem, RotoItem::eSelectionReasonOther); - _targetItem->clone( _oldTargetItem.get() ); - _roto->updateItemGui(_targetTreeItem); - _roto->getContext()->select(_targetItem, RotoItem::eSelectionReasonOther); - } else { - // check that it is a RotoLayer - assert( dynamic_cast( _targetItem.get() ) ); - for (std::list::iterator it = _pastedItems.begin(); it != _pastedItems.end(); ++it) { - _roto->getContext()->removeItem(it->itemCopy, RotoItem::eSelectionReasonOther); - } - } - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Paste item(s) of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -void -PasteItemUndoCommand::redo() -{ - if (_mode == ePasteModeCopyToItem) { - Bezier* isBezier = dynamic_cast( _targetItem.get() ); - RotoStrokeItem* isStroke = dynamic_cast( _targetItem.get() ); - if (isBezier) { - boost::shared_ptr oldBezier( new Bezier(isBezier->getContext(), isBezier->getScriptName(), isBezier->getParentLayer(), false) ); - oldBezier->createNodes(); - _oldTargetItem = oldBezier; - } else if (isStroke) { - boost::shared_ptr oldStroke( new RotoStrokeItem( isStroke->getBrushType(), isStroke->getContext(), isStroke->getScriptName(), boost::shared_ptr() ) ); - oldStroke->createNodes(); - _oldTargetItem = oldStroke; - if ( isStroke->getParentLayer() ) { - //isStroke->getParentLayer()->insertItem(_oldTargetItem, 0); - } - } - _oldTargetItem->clone( _targetItem.get() ); - assert(_pastedItems.size() == 1); - PastedItem & front = _pastedItems.front(); - ///If we don't deselct the updateItemGUI call will not function correctly because the knobs GUI - ///have not been refreshed and the selected item is linked to those dirty knobs - _roto->getContext()->deselect(_targetItem, RotoItem::eSelectionReasonOther); - _targetItem->clone( front.rotoItem.get() ); - _targetItem->setScriptName( _oldTargetItem->getScriptName() ); - _roto->updateItemGui(_targetTreeItem); - _roto->getContext()->select(_targetItem, RotoItem::eSelectionReasonOther); - } else { - boost::shared_ptr isLayer = boost::dynamic_pointer_cast(_targetItem); - assert(isLayer); - for (std::list::iterator it = _pastedItems.begin(); it != _pastedItems.end(); ++it) { - assert(it->itemCopy); - //it->itemCopy->setParentLayer(isLayer); - _roto->getContext()->addItem(isLayer, 0, it->itemCopy, RotoItem::eSelectionReasonOther); - } - } - - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Paste item(s) of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -////////////////// - - -DuplicateItemUndoCommand::DuplicateItemUndoCommand(RotoPanel* roto, - QTreeWidgetItem* items) - : QUndoCommand() - , _roto(roto) - , _item() -{ - _item.treeItem = items; - _item.item = _roto->getRotoItemForTreeItem(_item.treeItem); - assert( _item.item->getParentLayer() ); - boost::shared_ptr isBezier = boost::dynamic_pointer_cast( _item.item ); - boost::shared_ptr isStroke = boost::dynamic_pointer_cast( _item.item); - boost::shared_ptr isLayer = boost::dynamic_pointer_cast( _item.item); - if (isBezier) { - std::string name = getItemCopyName(roto, isBezier); - boost::shared_ptr bezierCopy( new Bezier(isBezier->getContext(), name, isBezier->getParentLayer(), false) ); - bezierCopy->createNodes(); - _item.duplicatedItem = bezierCopy; - _item.duplicatedItem->clone( isBezier.get() ); - //clone has overwritten the name - _item.duplicatedItem->setScriptName(name); - _item.duplicatedItem->setLabel(name); - } else if (isStroke) { - std::string name = getItemCopyName(roto, isStroke); - boost::shared_ptr strokeCopy( new RotoStrokeItem( isStroke->getBrushType(), isStroke->getContext(), name, boost::shared_ptr() ) ); - strokeCopy->createNodes(); - _item.duplicatedItem = strokeCopy; - if ( isStroke->getParentLayer() ) { - isStroke->getParentLayer()->insertItem(_item.duplicatedItem, 0); - } - _item.duplicatedItem->clone( isStroke.get() ); - //clone has overwritten the name - _item.duplicatedItem->setScriptName(name); - _item.duplicatedItem->setLabel(name); - } else { - assert(isLayer); - _item.duplicatedItem.reset( new RotoLayer(*isLayer) ); - setItemCopyNameRecursive( roto, _item.duplicatedItem ); - } -} - -DuplicateItemUndoCommand::~DuplicateItemUndoCommand() -{ -} - -void -DuplicateItemUndoCommand::undo() -{ - _roto->getContext()->removeItem(_item.duplicatedItem, RotoItem::eSelectionReasonOther); - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Duplicate item(s) of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -void -DuplicateItemUndoCommand::redo() -{ - _roto->getContext()->addItem(_item.item->getParentLayer(), - 0, _item.duplicatedItem, RotoItem::eSelectionReasonOther); - - _roto->getContext()->evaluateChange(); - setText( QObject::tr("Duplicate item(s) of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); -} - -LinkToTrackUndoCommand::LinkToTrackUndoCommand(RotoGui* roto, +LinkToTrackUndoCommand::LinkToTrackUndoCommand(const boost::shared_ptr& roto, const SelectedCpList & points, const boost::shared_ptr & track) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _points(points) , _track(track) { + setText( QObject::tr("Link to track").toStdString() ); + } LinkToTrackUndoCommand::~LinkToTrackUndoCommand() @@ -2195,6 +1826,10 @@ LinkToTrackUndoCommand::~LinkToTrackUndoCommand() void LinkToTrackUndoCommand::undo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } for (SelectedCpList::iterator it = _points.begin(); it != _points.end(); ++it) { it->first->unslave(); _track->removeSlavedTrack(it->first); @@ -2203,14 +1838,17 @@ LinkToTrackUndoCommand::undo() _track->removeSlavedTrack(it->second); } } - setText( QObject::tr("Link to track") ); - _roto->evaluate(true); + roto->evaluate(true); } void LinkToTrackUndoCommand::redo() { - SequenceTime time = _roto->getContext()->getTimelineCurrentTime(); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + SequenceTime time = roto->getContext()->getTimelineCurrentTime(); //bool featherLinkEnabled = _roto->getContext()->isFeatherLinkEnabled(); @@ -2223,13 +1861,12 @@ LinkToTrackUndoCommand::redo() _track->addSlavedTrack(it->second); // } } - setText( QObject::tr("Link to track") ); - _roto->evaluate(true); + roto->evaluate(true); } -UnLinkFromTrackUndoCommand::UnLinkFromTrackUndoCommand(RotoGui* roto, +UnLinkFromTrackUndoCommand::UnLinkFromTrackUndoCommand(const boost::shared_ptr& roto, const std::list, boost::shared_ptr > > & points) - : QUndoCommand() + : UndoCommand() , _roto(roto) , _points() { @@ -2240,6 +1877,7 @@ UnLinkFromTrackUndoCommand::UnLinkFromTrackUndoCommand(RotoGui* roto, p.track = p.cp->isSlaved(); _points.push_back(p); } + setText( QObject::tr("Unlink from track").toStdString() ); } UnLinkFromTrackUndoCommand::~UnLinkFromTrackUndoCommand() @@ -2249,8 +1887,12 @@ UnLinkFromTrackUndoCommand::~UnLinkFromTrackUndoCommand() void UnLinkFromTrackUndoCommand::undo() { - SequenceTime time = _roto->getContext()->getTimelineCurrentTime(); - bool featherLinkEnabled = _roto->getContext()->isFeatherLinkEnabled(); + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } + SequenceTime time = roto->getContext()->getTimelineCurrentTime(); + bool featherLinkEnabled = roto->getContext()->isFeatherLinkEnabled(); for (std::list::iterator it = _points.begin(); it != _points.end(); ++it) { it->cp->slaveTo(time, it->track); @@ -2261,14 +1903,18 @@ UnLinkFromTrackUndoCommand::undo() it->track->addSlavedTrack(it->fp); } } - setText( QObject::tr("Unlink from track") ); - _roto->evaluate(true); + + roto->evaluate(true); } void UnLinkFromTrackUndoCommand::redo() { + boost::shared_ptr roto = _roto.lock(); + if (!roto) { + return; + } for (std::list::iterator it = _points.begin(); it != _points.end(); ++it) { it->cp->unslave(); it->track->removeSlavedTrack(it->cp); @@ -2277,8 +1923,7 @@ UnLinkFromTrackUndoCommand::redo() it->track->removeSlavedTrack(it->fp); } } - _roto->evaluate(true); - setText( QObject::tr("Unlink from track") ); + roto->evaluate(true); } NATRON_NAMESPACE_EXIT; diff --git a/Gui/RotoUndoCommand.h b/Engine/RotoUndoCommand.h similarity index 65% rename from Gui/RotoUndoCommand.h rename to Engine/RotoUndoCommand.h index c60a05746f..0e56e9e629 100644 --- a/Gui/RotoUndoCommand.h +++ b/Engine/RotoUndoCommand.h @@ -30,30 +30,21 @@ #include #include -#if !defined(Q_MOC_RUN) && !defined(SBK_RUN) -#include -#include -#endif - -CLANG_DIAG_OFF(deprecated) -CLANG_DIAG_OFF(uninitialized) -#include -#include -CLANG_DIAG_ON(deprecated) -CLANG_DIAG_ON(uninitialized) - #include "Engine/EngineFwd.h" +#include "Engine/UndoCommand.h" -#include "Gui/GuiFwd.h" NATRON_NAMESPACE_ENTER; +struct RotoPaintInteract; + + class MoveControlPointsUndoCommand - : public QUndoCommand + : public UndoCommand { public: - MoveControlPointsUndoCommand(RotoGui* roto, + MoveControlPointsUndoCommand(const boost::shared_ptr& roto, const std::list< std::pair, boost::shared_ptr > > & toDrag , double dx, @@ -64,17 +55,16 @@ class MoveControlPointsUndoCommand virtual void undo() OVERRIDE FINAL; virtual void redo() OVERRIDE FINAL; - virtual int id() const OVERRIDE FINAL; - virtual bool mergeWith(const QUndoCommand *other) OVERRIDE FINAL; + virtual bool mergeWith(const UndoCommandPtr& other) OVERRIDE FINAL; private: bool _firstRedoCalled; //< false by default - RotoGui* _roto; + boost::weak_ptr _roto; double _dx, _dy; bool _featherLinkEnabled; bool _rippleEditEnabled; - int _selectedTool; //< corresponds to the RotoGui::RotoToolEnum enum + boost::weak_ptr _selectedTool; //< corresponds to the RotoGui::RotoToolEnum enum double _time; //< the time at which the change was made std::list > _selectedCurves; std::list _indexesToMove; //< indexes of the control points @@ -83,7 +73,7 @@ class MoveControlPointsUndoCommand class TransformUndoCommand - : public QUndoCommand + : public UndoCommand { public: @@ -96,7 +86,7 @@ class TransformUndoCommand eTransformMidTop }; - TransformUndoCommand(RotoGui* roto, + TransformUndoCommand(const boost::shared_ptr& roto, double centerX, double centerY, double rot, @@ -112,17 +102,16 @@ class TransformUndoCommand virtual void undo() OVERRIDE FINAL; virtual void redo() OVERRIDE FINAL; - virtual int id() const OVERRIDE FINAL; - virtual bool mergeWith(const QUndoCommand *other) OVERRIDE FINAL; + virtual bool mergeWith(const UndoCommandPtr& other) OVERRIDE FINAL; private: void transformPoint(const boost::shared_ptr & point); bool _firstRedoCalled; //< false by default - RotoGui* _roto; + boost::weak_ptr _roto; bool _rippleEditEnabled; - int _selectedTool; //< corresponds to the RotoGui::RotoToolEnum enum + boost::weak_ptr _selectedTool; //< corresponds to the RotoGui::RotoToolEnum enum boost::shared_ptr _matrix; double _time; //< the time at which the change was made std::list > _selectedCurves; @@ -130,11 +119,11 @@ class TransformUndoCommand }; class AddPointUndoCommand - : public QUndoCommand + : public UndoCommand { public: - AddPointUndoCommand(RotoGui* roto, + AddPointUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr & curve, int index, double t); @@ -147,7 +136,7 @@ class AddPointUndoCommand private: bool _firstRedoCalled; //< false by default - RotoGui* _roto; + boost::weak_ptr _roto; boost::shared_ptr _curve; int _index; double _t; @@ -155,7 +144,7 @@ class AddPointUndoCommand class RemovePointUndoCommand - : public QUndoCommand + : public UndoCommand { struct CurveDesc { @@ -168,11 +157,11 @@ class RemovePointUndoCommand public: - RemovePointUndoCommand(RotoGui* roto, + RemovePointUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr & curve, const boost::shared_ptr & cp); - RemovePointUndoCommand(RotoGui* roto, + RemovePointUndoCommand(const boost::shared_ptr& roto, const std::list< std::pair < boost::shared_ptr, boost::shared_ptr > > & points); virtual ~RemovePointUndoCommand(); @@ -181,7 +170,7 @@ class RemovePointUndoCommand virtual void redo() OVERRIDE FINAL; private: - RotoGui* _roto; + boost::weak_ptr _roto; struct CurveOrdering { bool operator() (const CurveDesc & lhs, @@ -197,7 +186,7 @@ class RemovePointUndoCommand class RemoveCurveUndoCommand - : public QUndoCommand + : public UndoCommand { struct RemovedCurve { @@ -209,7 +198,7 @@ class RemoveCurveUndoCommand public: - RemoveCurveUndoCommand(RotoGui* roto, + RemoveCurveUndoCommand(const boost::shared_ptr& roto, const std::list > & curves); virtual ~RemoveCurveUndoCommand(); @@ -218,17 +207,17 @@ class RemoveCurveUndoCommand virtual void redo() OVERRIDE FINAL; private: - RotoGui* _roto; + boost::weak_ptr _roto; bool _firstRedoCalled; std::list _curves; }; class AddStrokeUndoCommand - : public QUndoCommand + : public UndoCommand { public: - AddStrokeUndoCommand(RotoGui* roto, const boost::shared_ptr& item); + AddStrokeUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr& item); virtual ~AddStrokeUndoCommand(); virtual void undo() OVERRIDE FINAL; @@ -236,7 +225,7 @@ class AddStrokeUndoCommand private: - RotoGui* _roto; + boost::weak_ptr _roto; bool _firstRedoCalled; boost::shared_ptr _item; boost::shared_ptr _layer; @@ -244,11 +233,11 @@ class AddStrokeUndoCommand }; class AddMultiStrokeUndoCommand - : public QUndoCommand + : public UndoCommand { public: - AddMultiStrokeUndoCommand(RotoGui* roto, const boost::shared_ptr& item); + AddMultiStrokeUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr& item); virtual ~AddMultiStrokeUndoCommand(); virtual void undo() OVERRIDE FINAL; @@ -256,7 +245,7 @@ class AddMultiStrokeUndoCommand private: - RotoGui* _roto; + boost::weak_ptr _roto; bool _firstRedoCalled; boost::shared_ptr _item; boost::shared_ptr _layer; @@ -267,11 +256,11 @@ class AddMultiStrokeUndoCommand class MoveTangentUndoCommand - : public QUndoCommand + : public UndoCommand { public: - MoveTangentUndoCommand(RotoGui* roto, + MoveTangentUndoCommand(const boost::shared_ptr& roto, double dx, double dy, double time, @@ -283,13 +272,12 @@ class MoveTangentUndoCommand virtual void undo() OVERRIDE FINAL; virtual void redo() OVERRIDE FINAL; - virtual int id() const OVERRIDE FINAL; - virtual bool mergeWith(const QUndoCommand *other) OVERRIDE FINAL; + virtual bool mergeWith(const UndoCommandPtr& other) OVERRIDE FINAL; private: bool _firstRedoCalled; //< false by default - RotoGui* _roto; + boost::weak_ptr _roto; double _dx, _dy; bool _featherLinkEnabled; bool _rippleEditEnabled; @@ -303,11 +291,11 @@ class MoveTangentUndoCommand class MoveFeatherBarUndoCommand - : public QUndoCommand + : public UndoCommand { public: - MoveFeatherBarUndoCommand(RotoGui* roto, + MoveFeatherBarUndoCommand(const boost::shared_ptr& roto, double dx, double dy, const std::pair, boost::shared_ptr > & point, @@ -317,12 +305,11 @@ class MoveFeatherBarUndoCommand virtual void undo() OVERRIDE FINAL; virtual void redo() OVERRIDE FINAL; - virtual int id() const OVERRIDE FINAL; - virtual bool mergeWith(const QUndoCommand *other) OVERRIDE FINAL; + virtual bool mergeWith(const UndoCommandPtr& other) OVERRIDE FINAL; private: - RotoGui* _roto; + boost::weak_ptr _roto; bool _firstRedoCalled; double _dx, _dy; bool _rippleEditEnabled; @@ -333,7 +320,7 @@ class MoveFeatherBarUndoCommand class RemoveFeatherUndoCommand - : public QUndoCommand + : public UndoCommand { public: @@ -344,7 +331,7 @@ class RemoveFeatherUndoCommand }; - RemoveFeatherUndoCommand(RotoGui* roto, + RemoveFeatherUndoCommand(const boost::shared_ptr& roto, const std::list & datas); virtual ~RemoveFeatherUndoCommand(); @@ -353,18 +340,18 @@ class RemoveFeatherUndoCommand virtual void redo() OVERRIDE FINAL; private: - RotoGui* _roto; + boost::weak_ptr _roto; bool _firstRedocalled; std::list _datas; }; class OpenCloseUndoCommand - : public QUndoCommand + : public UndoCommand { public: - OpenCloseUndoCommand(RotoGui* roto, + OpenCloseUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr & curve); virtual ~OpenCloseUndoCommand(); @@ -374,15 +361,15 @@ class OpenCloseUndoCommand private: - RotoGui* _roto; + boost::weak_ptr _roto; bool _firstRedoCalled; - int _selectedTool; + boost::weak_ptr _selectedTool; boost::shared_ptr _curve; }; class SmoothCuspUndoCommand - : public QUndoCommand + : public UndoCommand { public: @@ -393,7 +380,7 @@ class SmoothCuspUndoCommand SelectedPointList newPoints, oldPoints; }; - SmoothCuspUndoCommand(RotoGui* roto, + SmoothCuspUndoCommand(const boost::shared_ptr& roto, const std::list & data, double time, bool cusp, @@ -403,11 +390,10 @@ class SmoothCuspUndoCommand virtual void undo() OVERRIDE FINAL; virtual void redo() OVERRIDE FINAL; - virtual int id() const OVERRIDE FINAL; - virtual bool mergeWith(const QUndoCommand *other) OVERRIDE FINAL; + virtual bool mergeWith(const UndoCommandPtr& other) OVERRIDE FINAL; private: - RotoGui* _roto; + boost::weak_ptr _roto; bool _firstRedoCalled; double _time; int _count; @@ -418,11 +404,11 @@ class SmoothCuspUndoCommand class MakeBezierUndoCommand - : public QUndoCommand + : public UndoCommand { public: - MakeBezierUndoCommand(RotoGui* roto, + MakeBezierUndoCommand(const boost::shared_ptr& roto, const boost::shared_ptr & curve, bool isOpenBezier, bool createPoint, @@ -434,8 +420,7 @@ class MakeBezierUndoCommand virtual void undo() OVERRIDE FINAL; virtual void redo() OVERRIDE FINAL; - virtual int id() const OVERRIDE FINAL; - virtual bool mergeWith(const QUndoCommand *other) OVERRIDE FINAL; + virtual bool mergeWith(const UndoCommandPtr& other) OVERRIDE FINAL; boost::shared_ptr getCurve() const { return _newCurve; @@ -443,7 +428,7 @@ class MakeBezierUndoCommand private: bool _firstRedoCalled; - RotoGui* _roto; + boost::weak_ptr _roto; boost::shared_ptr _parentLayer; int _indexInLayer; boost::shared_ptr _oldCurve, _newCurve; @@ -458,11 +443,11 @@ class MakeBezierUndoCommand class MakeEllipseUndoCommand - : public QUndoCommand + : public UndoCommand { public: - MakeEllipseUndoCommand(RotoGui* roto, + MakeEllipseUndoCommand(const boost::shared_ptr& roto, bool create, bool fromCenter, bool constrained, @@ -476,12 +461,11 @@ class MakeEllipseUndoCommand virtual void undo() OVERRIDE FINAL; virtual void redo() OVERRIDE FINAL; - virtual int id() const OVERRIDE FINAL; - virtual bool mergeWith(const QUndoCommand *other) OVERRIDE FINAL; + virtual bool mergeWith(const UndoCommandPtr& other) OVERRIDE FINAL; private: bool _firstRedoCalled; - RotoGui* _roto; + boost::weak_ptr _roto; boost::shared_ptr _parentLayer; int _indexInLayer; boost::shared_ptr _curve; @@ -495,11 +479,11 @@ class MakeEllipseUndoCommand class MakeRectangleUndoCommand - : public QUndoCommand + : public UndoCommand { public: - MakeRectangleUndoCommand(RotoGui* roto, + MakeRectangleUndoCommand(const boost::shared_ptr& roto, bool create, bool fromCenter, bool constrained, @@ -513,12 +497,11 @@ class MakeRectangleUndoCommand virtual void undo() OVERRIDE FINAL; virtual void redo() OVERRIDE FINAL; - virtual int id() const OVERRIDE FINAL; - virtual bool mergeWith(const QUndoCommand *other) OVERRIDE FINAL; + virtual bool mergeWith(const UndoCommandPtr& other) OVERRIDE FINAL; private: bool _firstRedoCalled; - RotoGui* _roto; + boost::weak_ptr _roto; boost::shared_ptr _parentLayer; int _indexInLayer; boost::shared_ptr _curve; @@ -531,180 +514,14 @@ class MakeRectangleUndoCommand }; -class RemoveItemsUndoCommand - : public QUndoCommand -{ - struct RemovedItem - { - QTreeWidgetItem* treeItem; - QTreeWidgetItem* parentTreeItem; - boost::shared_ptr parentLayer; - int indexInLayer; - boost::shared_ptr item; - - RemovedItem() - : treeItem(0) - , parentTreeItem(0) - , parentLayer() - , indexInLayer(-1) - , item() - { - } - }; - -public: - - - RemoveItemsUndoCommand(RotoPanel* roto, - const QList & items); - - virtual ~RemoveItemsUndoCommand(); - - virtual void undo() OVERRIDE FINAL; - virtual void redo() OVERRIDE FINAL; - -private: - - RotoPanel* _roto; - std::list _items; -}; - -class AddLayerUndoCommand - : public QUndoCommand -{ -public: - - - AddLayerUndoCommand(RotoPanel* roto); - - virtual ~AddLayerUndoCommand(); - - virtual void undo() OVERRIDE FINAL; - virtual void redo() OVERRIDE FINAL; - -private: - - RotoPanel* _roto; - bool _firstRedoCalled; - boost::shared_ptr _parentLayer; - QTreeWidgetItem* _parentTreeItem; - QTreeWidgetItem* _treeItem; - boost::shared_ptr _layer; - int _indexInParentLayer; -}; - - -class DragItemsUndoCommand - : public QUndoCommand -{ -public: - - struct Item - { - boost::shared_ptr dropped; - boost::shared_ptr oldParentLayer; - int indexInOldLayer; - QTreeWidgetItem* oldParentItem; - }; - - - DragItemsUndoCommand(RotoPanel* roto, - const std::list< boost::shared_ptr > & items); - - virtual ~DragItemsUndoCommand(); - - virtual void undo() OVERRIDE FINAL; - virtual void redo() OVERRIDE FINAL; - -private: - - RotoPanel* _roto; - std::list < Item > _items; -}; - - -/** - * @class This class supports 2 behaviours: - * 1) The user pastes one item upon another. The target's shape and attributes are copied and the - * name is the source's name plus "- copy" at the end. - * 2) The user pastes several items upon a layer in which case the items are copied into that layer and - * the new items name is the same than the original appeneded with "- copy". - * - * Anything else will not do anything and you should not issue a command which will yield an unsupported behaviour - * otherwise you'll create an empty action in the undo/redo stack. - **/ -class PasteItemUndoCommand - : public QUndoCommand -{ -public: - - enum PasteModeEnum - { - ePasteModeCopyToLayer = 0, - ePasteModeCopyToItem - }; - - struct PastedItem - { - QTreeWidgetItem* treeItem; - boost::shared_ptr rotoItem; - boost::shared_ptr itemCopy; - }; - - - PasteItemUndoCommand(RotoPanel* roto, - QTreeWidgetItem* target, - QList source); - - virtual ~PasteItemUndoCommand(); - - virtual void undo() OVERRIDE FINAL; - virtual void redo() OVERRIDE FINAL; - -private: - - RotoPanel* _roto; - PasteModeEnum _mode; - QTreeWidgetItem* _targetTreeItem; - boost::shared_ptr _targetItem; - boost::shared_ptr _oldTargetItem; - std::list < PastedItem > _pastedItems; -}; - - -class DuplicateItemUndoCommand - : public QUndoCommand -{ -public: - - struct DuplicatedItem - { - QTreeWidgetItem* treeItem; - boost::shared_ptr item; - boost::shared_ptr duplicatedItem; - }; - - DuplicateItemUndoCommand(RotoPanel* roto, - QTreeWidgetItem* items); - - virtual ~DuplicateItemUndoCommand(); - - virtual void undo() OVERRIDE FINAL; - virtual void redo() OVERRIDE FINAL; - -private: - - RotoPanel* _roto; - DuplicatedItem _item; -}; class LinkToTrackUndoCommand - : public QUndoCommand + : public UndoCommand { public: - LinkToTrackUndoCommand(RotoGui* roto, + LinkToTrackUndoCommand(const boost::shared_ptr& roto, const std::list, boost::shared_ptr > > & points, const boost::shared_ptr & track); @@ -715,13 +532,13 @@ class LinkToTrackUndoCommand private: - RotoGui* _roto; + boost::weak_ptr _roto; std::list, boost::shared_ptr > > _points; boost::shared_ptr _track; }; class UnLinkFromTrackUndoCommand - : public QUndoCommand + : public UndoCommand { struct PointToUnlink { @@ -733,7 +550,7 @@ class UnLinkFromTrackUndoCommand public: - UnLinkFromTrackUndoCommand(RotoGui* roto, + UnLinkFromTrackUndoCommand(const boost::shared_ptr& roto, const std::list, boost::shared_ptr > > & points); virtual ~UnLinkFromTrackUndoCommand(); @@ -743,7 +560,7 @@ class UnLinkFromTrackUndoCommand private: - RotoGui* _roto; + boost::weak_ptr _roto; std::list _points; }; diff --git a/Engine/Settings.cpp b/Engine/Settings.cpp index 89391848c3..444f7266e4 100644 --- a/Engine/Settings.cpp +++ b/Engine/Settings.cpp @@ -1085,6 +1085,15 @@ Settings::initializeKnobsViewers() autoProxyChoices.push_back("32"); _autoProxyLevel->populateChoices(autoProxyChoices); _viewersTab->addKnob(_autoProxyLevel); + + + _maximumNodeViewerUIOpened = AppManager::createKnob(this, "Max. opened node viewer interface"); + _maximumNodeViewerUIOpened->setName("maxNodeUiOpened"); + _maximumNodeViewerUIOpened->setMinimum(1); + _maximumNodeViewerUIOpened->setAnimationEnabled(false); + _maximumNodeViewerUIOpened->disableSlider(); + _maximumNodeViewerUIOpened->setHintToolTip("Controls the maximum amount of nodes that can have their interface showing up at the same time in the viewer"); + _viewersTab->addKnob(_maximumNodeViewerUIOpened); } // Settings::initializeKnobsViewers void @@ -1493,6 +1502,7 @@ Settings::setDefaultValues() _autoWipe->setDefaultValue(true); _autoProxyWhenScrubbingTimeline->setDefaultValue(true); _autoProxyLevel->setDefaultValue(1); + _maximumNodeViewerUIOpened->setDefaultValue(2); _warnOcioConfigKnobChanged->setDefaultValue(true); _ocioStartupCheck->setDefaultValue(true); @@ -1740,21 +1750,10 @@ Settings::warnChangedKnobs(const std::vector& knobs) } } else if ( knobs[i] == _texturesMode.get() ) { std::map apps = appPTR->getAppInstances(); - bool isFirstViewer = true; for (std::map::iterator it = apps.begin(); it != apps.end(); ++it) { std::list allViewers; it->second.app->getProject()->getViewers(&allViewers); for (std::list::iterator it = allViewers.begin(); it != allViewers.end(); ++it) { - if (isFirstViewer) { - if ( !(*it)->supportsGLSL() && (_texturesMode->getValue() != 0) ) { - Dialogs::errorDialog( QObject::tr("Viewer").toStdString(), QObject::tr("You need OpenGL GLSL in order to use 32 bit fp textures.\n" - "Reverting to 8bits textures.").toStdString() ); - _texturesMode->setValue(0); - saveSetting( _texturesMode.get() ); - - return; - } - } (*it)->renderCurrentFrame(true); } } @@ -3746,6 +3745,12 @@ Settings::getDopeSheetEditorNodeSeparationWith() const return 4; } +int +Settings::getMaxOpenedNodesViewerContext() const +{ + return _maximumNodeViewerUIOpened->getValue(); +} + bool Settings::isAutoProxyEnabled() const { diff --git a/Engine/Settings.h b/Engine/Settings.h index c5b0828f65..7f06e74567 100644 --- a/Engine/Settings.h +++ b/Engine/Settings.h @@ -353,6 +353,8 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON bool isAutoProxyEnabled() const; unsigned int getAutoProxyMipMapLevel() const; + int getMaxOpenedNodesViewerContext() const; + bool isNaNHandlingEnabled() const; bool isCopyInputImageForPluginRenderEnabled() const; @@ -477,6 +479,8 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON boost::shared_ptr _autoWipe; boost::shared_ptr _autoProxyWhenScrubbingTimeline; boost::shared_ptr _autoProxyLevel; + boost::shared_ptr _maximumNodeViewerUIOpened; + boost::shared_ptr _nodegraphTab; boost::shared_ptr _autoTurbo; boost::shared_ptr _useNodeGraphHints; diff --git a/Gui/Texture.cpp b/Engine/Texture.cpp similarity index 94% rename from Gui/Texture.cpp rename to Engine/Texture.cpp index 4f6c379094..f01c8b7d54 100644 --- a/Gui/Texture.cpp +++ b/Engine/Texture.cpp @@ -26,20 +26,13 @@ #include -#include "Global/GLIncludes.h" -#include "Gui/ViewerGL.h" - -// warning: 'gluErrorString' is deprecated: first deprecated in OS X 10.9 [-Wdeprecated-declarations] -CLANG_DIAG_OFF(deprecated-declarations) -GCC_DIAG_OFF(deprecated-declarations) - NATRON_NAMESPACE_ENTER; Texture::Texture(U32 target, int minFilter, int magFilter, int clamp) - : OpenGLTextureI() + : _texID(0) , _target(target) , _minFilter(minFilter) , _magFilter(magFilter) diff --git a/Gui/Texture.h b/Engine/Texture.h similarity index 74% rename from Gui/Texture.h rename to Engine/Texture.h index 9243cd1aa0..be3fd1dd52 100644 --- a/Gui/Texture.h +++ b/Engine/Texture.h @@ -31,12 +31,11 @@ #include "Engine/TextureRect.h" #include "Engine/EngineFwd.h" -#include "Engine/OpenGLViewerI.h" +#include "Global/GLIncludes.h" NATRON_NAMESPACE_ENTER; class Texture - : public OpenGLTextureI { public: @@ -54,6 +53,11 @@ class Texture int magFilter, int clamp); + U32 getTexID() const + { + return _texID; + } + int w() const { return _textureRect.width(); @@ -64,6 +68,9 @@ class Texture return _textureRect.height(); } + /** + * @brief The bitdepth of the texture + **/ DataTypeEnum type() const { return _type; @@ -72,12 +79,23 @@ class Texture /* * @brief Ensures that the texture is of size texRect and of the given type * @returns True if something changed, false otherwise + * Note: Internally this function calls glTexImage2D to reallocate the texture buffer */ bool ensureTextureHasSize(const TextureRect& texRect, DataTypeEnum type); - /*allocates the texture*/ + /** + * @brief Update the texture with the currently bound PBO across the given rectangle. + * @param texRect The bounds of the texture, if the texture does not match these bounds, it will be reallocated + * using ensureTextureHasSize(texRect,type)/ + * @param type The bitdepth of the texture + * @param roi if updateOnlyRoi is true, this will be the portion of the texture to update with glTextSubImage2D + * @param updateOnlyRoI if updateOnlyRoi is true, only the portion defined by roi will be updated on the texture + **/ void fillOrAllocateTexture(const TextureRect & texRect, DataTypeEnum type, const RectI& roi, bool updateOnlyRoi); + /** + * @brief The bounds of the texture + **/ const TextureRect & getTextureRect() const { return _textureRect; @@ -87,6 +105,7 @@ class Texture private: + U32 _texID; U32 _target; int _minFilter, _magFilter, _clamp; TextureRect _textureRect; diff --git a/Engine/TrackerContext.cpp b/Engine/TrackerContext.cpp index b77c27dc04..c7de7677c3 100644 --- a/Engine/TrackerContext.cpp +++ b/Engine/TrackerContext.cpp @@ -1196,6 +1196,7 @@ TrackerContext::onOverlayPenDownInternalNodes(double time, const QPointF & viewportPos, const QPointF & pos, double pressure, + double timestamp, PenType pen, OverlaySupport* viewer) { if ( _imp->transformPageKnob.lock()->getIsSecret() ) { @@ -1205,7 +1206,7 @@ TrackerContext::onOverlayPenDownInternalNodes(double time, if (node) { NodePtr thisNode = getNode(); thisNode->getEffectInstance()->setCurrentViewportForOverlays_public(viewer); - if ( thisNode->getEffectInstance()->onOverlayPenDown_public(time, renderScale, view, viewportPos, pos, pressure) ) { + if ( thisNode->getEffectInstance()->onOverlayPenDown_public(time, renderScale, view, viewportPos, pos, pressure, timestamp, pen) ) { return true; } } @@ -1220,6 +1221,7 @@ TrackerContext::onOverlayPenMotionInternalNodes(double time, const QPointF & viewportPos, const QPointF & pos, double pressure, + double timestamp, OverlaySupport* viewer) { if ( _imp->transformPageKnob.lock()->getIsSecret() ) { @@ -1229,7 +1231,7 @@ TrackerContext::onOverlayPenMotionInternalNodes(double time, if (node) { NodePtr thisNode = getNode(); thisNode->getEffectInstance()->setCurrentViewportForOverlays_public(viewer); - if ( thisNode->getEffectInstance()->onOverlayPenMotion_public(time, renderScale, view, viewportPos, pos, pressure) ) { + if ( thisNode->getEffectInstance()->onOverlayPenMotion_public(time, renderScale, view, viewportPos, pos, pressure, timestamp) ) { return true; } } @@ -1244,6 +1246,7 @@ TrackerContext::onOverlayPenUpInternalNodes(double time, const QPointF & viewportPos, const QPointF & pos, double pressure, + double timestamp, OverlaySupport* viewer) { if ( _imp->transformPageKnob.lock()->getIsSecret() ) { @@ -1253,7 +1256,7 @@ TrackerContext::onOverlayPenUpInternalNodes(double time, if (node) { NodePtr thisNode = getNode(); thisNode->getEffectInstance()->setCurrentViewportForOverlays_public(viewer); - if ( thisNode->getEffectInstance()->onOverlayPenUp_public(time, renderScale, view, viewportPos, pos, pressure) ) { + if ( thisNode->getEffectInstance()->onOverlayPenUp_public(time, renderScale, view, viewportPos, pos, pressure, timestamp) ) { return true; } } diff --git a/Engine/TrackerContext.h b/Engine/TrackerContext.h index 44eca4874d..040f181749 100644 --- a/Engine/TrackerContext.h +++ b/Engine/TrackerContext.h @@ -213,15 +213,15 @@ class TrackerContext bool onOverlayPenDownInternalNodes(double time, const RenderScale & renderScale, - ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, OverlaySupport* viewer) WARN_UNUSED_RETURN; + ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, PenType pen, OverlaySupport* viewer) WARN_UNUSED_RETURN; bool onOverlayPenMotionInternalNodes(double time, const RenderScale & renderScale, - ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, OverlaySupport* viewer) WARN_UNUSED_RETURN; + ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, OverlaySupport* viewer) WARN_UNUSED_RETURN; bool onOverlayPenUpInternalNodes(double time, const RenderScale & renderScale, - ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, OverlaySupport* viewer) WARN_UNUSED_RETURN; + ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, OverlaySupport* viewer) WARN_UNUSED_RETURN; bool onOverlayKeyDownInternalNodes(double time, const RenderScale & renderScale, diff --git a/Engine/TrackerNode.cpp b/Engine/TrackerNode.cpp index 4746577699..b097bf44da 100644 --- a/Engine/TrackerNode.cpp +++ b/Engine/TrackerNode.cpp @@ -26,20 +26,16 @@ #include "TrackerNode.h" #include "Engine/Node.h" #include "Engine/TrackerContext.h" +#include "Engine/TrackerNodeInteract.h" NATRON_NAMESPACE_ENTER; -struct TrackerNodePrivate -{ - TrackerNodePrivate() - { - } -}; TrackerNode::TrackerNode(boost::shared_ptr node) : NodeGroup(node) - , _imp( new TrackerNodePrivate() ) + , _imp( new TrackerNodePrivate(this) ) { + } TrackerNode::~TrackerNode() @@ -86,9 +82,262 @@ TrackerNode::getPluginDescription() const "

You may export the tracking data either to a CornerPin node or to a Transform node. The CornerPin node performs a warp that may be more stable than a Transform node when using 4 or more tracks: it retains more information than the Transform node.

"; } +void +TrackerNode::getPluginShortcuts(std::list* shortcuts) +{ + +} + void TrackerNode::initializeKnobs() { + boost::shared_ptr trackingPage; + { + KnobPtr page = getKnobByName("Tracking"); + assert(page); + trackingPage = boost::dynamic_pointer_cast(page); + } + + boost::shared_ptr addMarker = AppManager::createKnob(this, kTrackerUIParamAddTrackLabel); + addMarker->setName(kTrackerUIParamAddTrack); + addMarker->setHintToolTip(kTrackerUIParamAddTrackHint); + addMarker->setEvaluateOnChange(false); + addMarker->setCheckable(true); + addMarker->setDefaultValue(false); + addMarker->setSecretByDefault(true); + addMarker->setInViewerContextCanHaveShortcut(true); + addMarker->setIconLabel(NATRON_IMAGES_PATH "addTrack.png"); + trackingPage->addKnob(addMarker); + _imp->ui->addTrackButton = addMarker; + + boost::shared_ptr trackBw = AppManager::createKnob(this, kTrackerUIParamTrackBWLabel); + trackBw->setName(kTrackerUIParamTrackBW); + trackBw->setHintToolTip(kTrackerUIParamTrackBWHint); + trackBw->setEvaluateOnChange(false); + trackBw->setCheckable(true); + trackBw->setDefaultValue(false); + trackBw->setSecretByDefault(true); + trackBw->setInViewerContextCanHaveShortcut(true); + trackBw->setIconLabel(NATRON_IMAGES_PATH "trackBackwardOn.png", true); + trackBw->setIconLabel(NATRON_IMAGES_PATH "trackBackwardOff.png", false); + trackingPage->addKnob(trackBw); + _imp->ui->trackBwButton = trackBw; + + boost::shared_ptr trackPrev = AppManager::createKnob(this, kTrackerUIParamTrackPreviousLabel); + trackPrev->setName(kTrackerUIParamTrackPrevious); + trackPrev->setHintToolTip(kTrackerUIParamTrackPreviousHint); + trackPrev->setEvaluateOnChange(false); + trackPrev->setSecretByDefault(true); + trackPrev->setInViewerContextCanHaveShortcut(true); + trackPrev->setIconLabel(NATRON_IMAGES_PATH "trackPrev.png"); + trackingPage->addKnob(trackPrev); + _imp->ui->trackPrevButton = trackPrev; + + boost::shared_ptr trackNext = AppManager::createKnob(this, kTrackerUIParamTrackNextLabel); + trackNext->setName(kTrackerUIParamTrackNext); + trackNext->setHintToolTip(kTrackerUIParamTrackNextHint); + trackNext->setEvaluateOnChange(false); + trackNext->setSecretByDefault(true); + trackNext->setInViewerContextCanHaveShortcut(true); + trackNext->setIconLabel(NATRON_IMAGES_PATH "trackNext.png"); + trackingPage->addKnob(trackNext); + _imp->ui->trackNextButton = trackNext; + + boost::shared_ptr trackFw = AppManager::createKnob(this, kTrackerUIParamTrackFWLabel); + trackFw->setName(kTrackerUIParamTrackFW); + trackFw->setHintToolTip(kTrackerUIParamTrackFWHint); + trackFw->setEvaluateOnChange(false); + trackFw->setCheckable(true); + trackFw->setDefaultValue(false); + trackFw->setSecretByDefault(true); + trackFw->setInViewerContextCanHaveShortcut(true); + trackFw->setIconLabel(NATRON_IMAGES_PATH "trackForwardOn.png", true); + trackFw->setIconLabel(NATRON_IMAGES_PATH "trackForwardOff.png", false); + trackingPage->addKnob(trackFw); + _imp->ui->trackFwButton = trackFw; + + boost::shared_ptr trackRange = AppManager::createKnob(this, kTrackerUIParamTrackRangeLabel); + trackRange->setName(kTrackerUIParamTrackRange); + trackRange->setHintToolTip(kTrackerUIParamTrackRangeHint); + trackRange->setEvaluateOnChange(false); + trackRange->setSecretByDefault(true); + trackRange->setInViewerContextCanHaveShortcut(true); + trackRange->setIconLabel(NATRON_IMAGES_PATH "trackRange.png"); + trackingPage->addKnob(trackRange); + _imp->ui->trackRangeButton = trackRange; + + boost::shared_ptr trackAllKeys = AppManager::createKnob(this, kTrackerUIParamTrackAllKeyframesLabel); + trackAllKeys->setName(kTrackerUIParamTrackAllKeyframes); + trackAllKeys->setHintToolTip(kTrackerUIParamTrackAllKeyframesHint); + trackAllKeys->setEvaluateOnChange(false); + trackAllKeys->setSecretByDefault(true); + trackAllKeys->setInViewerContextCanHaveShortcut(true); + trackAllKeys->setIconLabel(NATRON_IMAGES_PATH "trackAllKeyframes.png"); + trackingPage->addKnob(trackAllKeys); + _imp->ui->trackAllKeyframesButton = trackAllKeys; + + + boost::shared_ptr trackCurKey = AppManager::createKnob(this, kTrackerUIParamTrackCurrentKeyframeLabel); + trackCurKey->setName(kTrackerUIParamTrackCurrentKeyframe); + trackCurKey->setHintToolTip(kTrackerUIParamTrackCurrentKeyframeHint); + trackCurKey->setEvaluateOnChange(false); + trackCurKey->setSecretByDefault(true); + trackCurKey->setInViewerContextCanHaveShortcut(true); + trackCurKey->setIconLabel(NATRON_IMAGES_PATH "trackCurrentKeyframe.png"); + trackingPage->addKnob(trackCurKey); + _imp->ui->trackCurrentKeyframeButton = trackCurKey; + + boost::shared_ptr clearAllAnimation = AppManager::createKnob(this, kTrackerUIParamClearAllAnimationLabel); + clearAllAnimation->setName(kTrackerUIParamClearAllAnimation); + clearAllAnimation->setHintToolTip(kTrackerUIParamClearAllAnimationHint); + clearAllAnimation->setEvaluateOnChange(false); + clearAllAnimation->setSecretByDefault(true); + clearAllAnimation->setInViewerContextCanHaveShortcut(true); + clearAllAnimation->setIconLabel(NATRON_IMAGES_PATH "clearAnimation.png"); + trackingPage->addKnob(clearAllAnimation); + _imp->ui->clearAllAnimationButton = clearAllAnimation; + + boost::shared_ptr clearBackwardAnim = AppManager::createKnob(this, kTrackerUIParamClearAnimationBwLabel); + clearBackwardAnim->setName(kTrackerUIParamClearAnimationBw); + clearBackwardAnim->setHintToolTip(kTrackerUIParamClearAnimationBwHint); + clearBackwardAnim->setEvaluateOnChange(false); + clearBackwardAnim->setSecretByDefault(true); + clearBackwardAnim->setInViewerContextCanHaveShortcut(true); + clearBackwardAnim->setIconLabel(NATRON_IMAGES_PATH "clearAnimationBw.png"); + trackingPage->addKnob(clearBackwardAnim); + _imp->ui->clearBwAnimationButton = clearBackwardAnim; + + boost::shared_ptr clearForwardAnim = AppManager::createKnob(this, kTrackerUIParamClearAnimationFwLabel); + clearForwardAnim->setName(kTrackerUIParamClearAnimationFw); + clearForwardAnim->setHintToolTip(kTrackerUIParamClearAnimationFwHint); + clearForwardAnim->setEvaluateOnChange(false); + clearForwardAnim->setSecretByDefault(true); + clearForwardAnim->setInViewerContextCanHaveShortcut(true); + clearForwardAnim->setIconLabel(NATRON_IMAGES_PATH "clearAnimationFw.png"); + trackingPage->addKnob(clearForwardAnim); + _imp->ui->clearFwAnimationButton = clearForwardAnim; + + boost::shared_ptr updateViewer = AppManager::createKnob(this, kTrackerUIParamRefreshViewerLabel); + updateViewer->setName(kTrackerUIParamRefreshViewer); + updateViewer->setHintToolTip(kTrackerUIParamRefreshViewerHint); + updateViewer->setEvaluateOnChange(false); + updateViewer->setCheckable(true); + updateViewer->setDefaultValue(true); + updateViewer->setSecretByDefault(true); + updateViewer->setInViewerContextCanHaveShortcut(true); + updateViewer->setIconLabel(NATRON_IMAGES_PATH "updateViewerEnabled.png", true); + updateViewer->setIconLabel(NATRON_IMAGES_PATH "updateViewerDisabled.png", false); + trackingPage->addKnob(updateViewer); + _imp->ui->updateViewerButton = updateViewer; + + boost::shared_ptr centerViewer = AppManager::createKnob(this, kTrackerUIParamCenterViewerLabel); + centerViewer->setName(kTrackerUIParamCenterViewer); + centerViewer->setHintToolTip(kTrackerUIParamCenterViewerHint); + centerViewer->setEvaluateOnChange(false); + centerViewer->setCheckable(true); + centerViewer->setDefaultValue(false); + centerViewer->setSecretByDefault(true); + centerViewer->setInViewerContextCanHaveShortcut(true); + centerViewer->setIconLabel(NATRON_IMAGES_PATH "centerOnTrack.png"); + trackingPage->addKnob(centerViewer); + _imp->ui->centerViewerButton = centerViewer; + + boost::shared_ptr createKeyOnMove = AppManager::createKnob(this, kTrackerUIParamCreateKeyOnMoveLabel); + createKeyOnMove->setName(kTrackerUIParamCreateKeyOnMove); + createKeyOnMove->setHintToolTip(kTrackerUIParamCreateKeyOnMoveHint); + createKeyOnMove->setEvaluateOnChange(false); + createKeyOnMove->setCheckable(true); + createKeyOnMove->setDefaultValue(true); + createKeyOnMove->setSecretByDefault(true); + createKeyOnMove->setInViewerContextCanHaveShortcut(true); + createKeyOnMove->setIconLabel(NATRON_IMAGES_PATH "createKeyOnMoveOn.png", true); + createKeyOnMove->setIconLabel(NATRON_IMAGES_PATH "createKeyOnMoveOff.png", false); + trackingPage->addKnob(createKeyOnMove); + _imp->ui->createKeyOnMoveButton = createKeyOnMove; + + boost::shared_ptr showError = AppManager::createKnob(this, kTrackerUIParamShowErrorLabel); + showError->setName(kTrackerUIParamShowError); + showError->setHintToolTip(kTrackerUIParamShowErrorHint); + showError->setEvaluateOnChange(false); + showError->setCheckable(true); + showError->setDefaultValue(false); + showError->setSecretByDefault(true); + showError->setInViewerContextCanHaveShortcut(true); + showError->setIconLabel(NATRON_IMAGES_PATH "showTrackError.png", true); + showError->setIconLabel(NATRON_IMAGES_PATH "hideTrackError.png", false); + trackingPage->addKnob(showError); + _imp->ui->showCorrelationButton = showError; + + + boost::shared_ptr addKeyframe = AppManager::createKnob(this, kTrackerUIParamSetPatternKeyFrameLabel); + addKeyframe->setName(kTrackerUIParamSetPatternKeyFrame); + addKeyframe->setHintToolTip(kTrackerUIParamSetPatternKeyFrameHint); + addKeyframe->setEvaluateOnChange(false); + addKeyframe->setSecretByDefault(true); + addKeyframe->setInViewerContextCanHaveShortcut(true); + addKeyframe->setIconLabel(NATRON_IMAGES_PATH "addUserKey.png"); + trackingPage->addKnob(addKeyframe); + _imp->ui->setKeyFrameButton = addKeyframe; + + boost::shared_ptr removeKeyframe = AppManager::createKnob(this, kTrackerUIParamRemovePatternKeyFrameLabel); + removeKeyframe->setName(kTrackerUIParamRemovePatternKeyFrame); + removeKeyframe->setHintToolTip(kTrackerUIParamRemovePatternKeyFrameHint); + removeKeyframe->setEvaluateOnChange(false); + removeKeyframe->setSecretByDefault(true); + removeKeyframe->setInViewerContextCanHaveShortcut(true); + removeKeyframe->setIconLabel(NATRON_IMAGES_PATH "removeUserKey.png"); + trackingPage->addKnob(removeKeyframe); + _imp->ui->removeKeyFrameButton = removeKeyframe; + + boost::shared_ptr resetOffset = AppManager::createKnob(this, kTrackerUIParamResetOffsetLabel); + resetOffset->setName(kTrackerUIParamResetOffset); + resetOffset->setHintToolTip(kTrackerUIParamResetOffsetHint); + resetOffset->setEvaluateOnChange(false); + resetOffset->setSecretByDefault(true); + resetOffset->setInViewerContextCanHaveShortcut(true); + resetOffset->setIconLabel(NATRON_IMAGES_PATH "resetTrackOffset.png"); + trackingPage->addKnob(resetOffset); + _imp->ui->resetOffsetButton = resetOffset; + + boost::shared_ptr resetTrack = AppManager::createKnob(this, kTrackerUIParamResetTrackLabel); + resetTrack->setName(kTrackerUIParamResetTrack); + resetTrack->setHintToolTip(kTrackerUIParamResetTrackHint); + resetTrack->setEvaluateOnChange(false); + resetTrack->setSecretByDefault(true); + resetTrack->setInViewerContextCanHaveShortcut(true); + resetTrack->setIconLabel(NATRON_IMAGES_PATH "restoreDefaultEnabled.png"); + trackingPage->addKnob(resetTrack); + _imp->ui->resetTrackButton = resetTrack; + + + addKnobToViewerUI(addMarker); + addMarker->setInViewerContextItemSpacing(5); + addKnobToViewerUI(trackBw); + addKnobToViewerUI(trackPrev); + addKnobToViewerUI(trackNext); + addKnobToViewerUI(trackFw); + trackFw->setInViewerContextItemSpacing(5); + addKnobToViewerUI(trackRange); + trackRange->setInViewerContextItemSpacing(5); + addKnobToViewerUI(trackAllKeys); + addKnobToViewerUI(trackCurKey); + trackCurKey->setInViewerContextItemSpacing(5); + addKnobToViewerUI(clearAllAnimation); + addKnobToViewerUI(clearBackwardAnim); + addKnobToViewerUI(clearForwardAnim); + clearForwardAnim->setInViewerContextItemSpacing(5); + addKnobToViewerUI(updateViewer); + addKnobToViewerUI(centerViewer); + centerViewer->setInViewerContextItemSpacing(5); + addKnobToViewerUI(createKeyOnMove); + addKnobToViewerUI(showError); + showError->setInViewerContextItemSpacing(5); + addKnobToViewerUI(addKeyframe); + addKnobToViewerUI(removeKeyframe); + removeKeyframe->setInViewerContextItemSpacing(5); + addKnobToViewerUI(resetOffset); + addKnobToViewerUI(resetTrack); + } void @@ -115,6 +364,9 @@ TrackerNode::onKnobsLoaded() return; } ctx->onKnobsLoaded(); + + ctx->setUpdateViewer( _imp->ui->updateViewerButton.lock()->getValue() ); + ctx->setCenterOnTrack( _imp->ui->centerViewerButton.lock()->getValue() ); } void @@ -123,6 +375,1830 @@ TrackerNode::onInputChanged(int inputNb) boost::shared_ptr ctx = getNode()->getTrackerContext(); ctx->inputChanged(inputNb); + + _imp->ui->refreshSelectedMarkerTexture(); +} + + +void +TrackerNode::drawOverlay(double time, const RenderScale & renderScale, ViewIdx view) +{ +#if 0 + double pixelScaleX, pixelScaleY; + ViewerGL* viewer = _imp->viewer->getViewer(); + + viewer->getPixelScale(pixelScaleX, pixelScaleY); + double viewportSize[2]; + viewer->getViewportSize(viewportSize[0], viewportSize[1]); + + { + GLProtectAttrib a(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_ENABLE_BIT | GL_HINT_BIT | GL_TRANSFORM_BIT); + + if (_imp->panelv1) { + ///For each instance: + const std::list > & instances = _imp->panelv1->getInstances(); + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + boost::shared_ptr instance = it->first.lock(); + + if ( instance->isNodeDisabled() ) { + continue; + } + if (it->second) { + ///The track is selected, use the plug-ins interact + EffectInstPtr effect = instance->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); + effect->drawOverlay_public(time, renderScale, view); + } else { + ///Draw a custom interact, indicating the track isn't selected + boost::shared_ptr newInstanceKnob = instance->getKnobByName("center"); + assert(newInstanceKnob); //< if it crashes here that means the parameter's name changed in the OpenFX plug-in. + KnobDouble* dblKnob = dynamic_cast( newInstanceKnob.get() ); + assert(dblKnob); + + //GLProtectMatrix p(GL_PROJECTION); // useless (we do two glTranslate in opposite directions) + for (int l = 0; l < 2; ++l) { + // shadow (uses GL_PROJECTION) + glMatrixMode(GL_PROJECTION); + int direction = (l == 0) ? 1 : -1; + // translate (1,-1) pixels + glTranslated(direction * pixelScaleX / 256, -direction * pixelScaleY / 256, 0); + glMatrixMode(GL_MODELVIEW); + + if (l == 0) { + glColor4d(0., 0., 0., 1.); + } else { + glColor4f(1., 1., 1., 1.); + } + + double x = dblKnob->getValue(0); + double y = dblKnob->getValue(1); + glPointSize(POINT_SIZE); + glBegin(GL_POINTS); + glVertex2d(x, y); + glEnd(); + + glBegin(GL_LINES); + glVertex2d(x - CROSS_SIZE * pixelScaleX, y); + glVertex2d(x + CROSS_SIZE * pixelScaleX, y); + + glVertex2d(x, y - CROSS_SIZE * pixelScaleY); + glVertex2d(x, y + CROSS_SIZE * pixelScaleY); + glEnd(); + } + glPointSize(1.); + } + } + } // if (_imp->panelv1) { + else { + assert(_imp->panel); + double markerColor[3]; + if ( !_imp->panel->getNode()->getOverlayColor(&markerColor[0], &markerColor[1], &markerColor[2]) ) { + markerColor[0] = markerColor[1] = markerColor[2] = 0.8; + } + + std::vector allMarkers; + std::list selectedMarkers; + boost::shared_ptr context = _imp->panel->getContext(); + context->getSelectedMarkers(&selectedMarkers); + context->getAllMarkers(&allMarkers); + + bool showErrorColor = _imp->showCorrelationButton->isChecked(); + TrackMarkerPtr selectedMarker = _imp->selectedMarker.lock(); + Point selectedCenter, selectedPtnTopLeft, selectedPtnTopRight, selectedPtnBtmRight, selectedPtnBtmLeft, selectedOffset, selectedSearchBtmLeft, selectedSearchTopRight; + + for (std::vector::iterator it = allMarkers.begin(); it != allMarkers.end(); ++it) { + if ( !(*it)->isEnabled( (*it)->getCurrentTime() ) ) { + continue; + } + + bool isHoverMarker = *it == _imp->hoverMarker; + bool isDraggedMarker = *it == _imp->interactMarker; + bool isHoverOrDraggedMarker = isHoverMarker || isDraggedMarker; + std::list::iterator foundSelected = std::find(selectedMarkers.begin(), selectedMarkers.end(), *it); + bool isSelected = foundSelected != selectedMarkers.end(); + boost::shared_ptr centerKnob = (*it)->getCenterKnob(); + boost::shared_ptr offsetKnob = (*it)->getOffsetKnob(); + boost::shared_ptr errorKnob = (*it)->getErrorKnob(); + boost::shared_ptr ptnTopLeft = (*it)->getPatternTopLeftKnob(); + boost::shared_ptr ptnTopRight = (*it)->getPatternTopRightKnob(); + boost::shared_ptr ptnBtmRight = (*it)->getPatternBtmRightKnob(); + boost::shared_ptr ptnBtmLeft = (*it)->getPatternBtmLeftKnob(); + boost::shared_ptr searchWndBtmLeft = (*it)->getSearchWindowBottomLeftKnob(); + boost::shared_ptr searchWndTopRight = (*it)->getSearchWindowTopRightKnob(); + + if (!isSelected) { + ///Draw a custom interact, indicating the track isn't selected + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + glLineWidth(1.5f); + + for (int l = 0; l < 2; ++l) { + // shadow (uses GL_PROJECTION) + glMatrixMode(GL_PROJECTION); + int direction = (l == 0) ? 1 : -1; + // translate (1,-1) pixels + glTranslated(direction * pixelScaleX / 256, -direction * pixelScaleY / 256, 0); + glMatrixMode(GL_MODELVIEW); + + if (l == 0) { + glColor4d(0., 0., 0., 1.); + } else { + glColor4f(markerColor[0], markerColor[1], markerColor[2], 1.); + } + + + double x = centerKnob->getValueAtTime(time, 0); + double y = centerKnob->getValueAtTime(time, 1); + + glPointSize(POINT_SIZE); + glBegin(GL_POINTS); + glVertex2d(x, y); + glEnd(); + + glBegin(GL_LINES); + glVertex2d(x - CROSS_SIZE * pixelScaleX, y); + glVertex2d(x + CROSS_SIZE * pixelScaleX, y); + + + glVertex2d(x, y - CROSS_SIZE * pixelScaleY); + glVertex2d(x, y + CROSS_SIZE * pixelScaleY); + glEnd(); + } + glPointSize(1.); + } else { // if (isSelected) { + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + GLdouble projection[16]; + glGetDoublev( GL_PROJECTION_MATRIX, projection); + OfxPointD shadow; // how much to translate GL_PROJECTION to get exactly one pixel on screen + shadow.x = 2. / (projection[0] * viewportSize[0]); + shadow.y = 2. / (projection[5] * viewportSize[1]); + + const QPointF center( centerKnob->getValueAtTime(time, 0), + centerKnob->getValueAtTime(time, 1) ); + const QPointF offset( offsetKnob->getValueAtTime(time, 0), + offsetKnob->getValueAtTime(time, 1) ); + const QPointF topLeft( ptnTopLeft->getValueAtTime(time, 0) + offset.x() + center.x(), + ptnTopLeft->getValueAtTime(time, 1) + offset.y() + center.y() ); + const QPointF topRight( ptnTopRight->getValueAtTime(time, 0) + offset.x() + center.x(), + ptnTopRight->getValueAtTime(time, 1) + offset.y() + center.y() ); + const QPointF btmRight( ptnBtmRight->getValueAtTime(time, 0) + offset.x() + center.x(), + ptnBtmRight->getValueAtTime(time, 1) + offset.y() + center.y() ); + const QPointF btmLeft( ptnBtmLeft->getValueAtTime(time, 0) + offset.x() + center.x(), + ptnBtmLeft->getValueAtTime(time, 1) + offset.y() + center.y() ); + const double searchLeft = searchWndBtmLeft->getValueAtTime(time, 0) + offset.x() + center.x(); + const double searchBottom = searchWndBtmLeft->getValueAtTime(time, 1) + offset.y() + center.y(); + const double searchRight = searchWndTopRight->getValueAtTime(time, 0) + offset.x() + center.x(); + const double searchTop = searchWndTopRight->getValueAtTime(time, 1) + offset.y() + center.y(); + const double searchMidX = (searchLeft + searchRight) / 2; + const double searchMidY = (searchTop + searchBottom) / 2; + + if (selectedMarker == *it) { + selectedCenter.x = center.x(); + selectedCenter.y = center.y(); + selectedOffset.x = offset.x(); + selectedOffset.y = offset.y(); + selectedPtnBtmLeft.x = btmLeft.x(); + selectedPtnBtmLeft.y = btmLeft.y(); + selectedPtnBtmRight.x = btmRight.x(); + selectedPtnBtmRight.y = btmRight.y(); + selectedPtnTopRight.x = topRight.x(); + selectedPtnTopRight.y = topRight.y(); + selectedPtnTopLeft.x = topLeft.x(); + selectedPtnTopLeft.y = topLeft.y(); + selectedSearchBtmLeft.x = searchLeft; + selectedSearchBtmLeft.y = searchBottom; + + selectedSearchTopRight.x = searchRight; + selectedSearchTopRight.y = searchTop; + } + + const QPointF innerMidLeft( (btmLeft + topLeft) / 2 ); + const QPointF innerMidTop( (topLeft + topRight) / 2 ); + const QPointF innerMidRight( (btmRight + topRight) / 2 ); + const QPointF innerMidBtm( (btmLeft + btmRight) / 2 ); + const QPointF outterMidLeft(searchLeft, searchMidY); + const QPointF outterMidTop(searchMidX, searchTop); + const QPointF outterMidRight(searchRight, searchMidY); + const QPointF outterMidBtm(searchMidX, searchBottom); + const QPointF handleSize( HANDLE_SIZE * pixelScaleX, handleSize.x() ); + const QPointF innerMidLeftExt = computeMidPointExtent(topLeft, btmLeft, innerMidLeft, handleSize); + const QPointF innerMidRightExt = computeMidPointExtent(btmRight, topRight, innerMidRight, handleSize); + const QPointF innerMidTopExt = computeMidPointExtent(topRight, topLeft, innerMidTop, handleSize); + const QPointF innerMidBtmExt = computeMidPointExtent(btmLeft, btmRight, innerMidBtm, handleSize); + const QPointF searchTopLeft(searchLeft, searchTop); + const QPointF searchTopRight(searchRight, searchTop); + const QPointF searchBtmRight(searchRight, searchBottom); + const QPointF searchBtmLeft(searchLeft, searchBottom); + const QPointF searchTopMid(searchMidX, searchTop); + const QPointF searchRightMid(searchRight, searchMidY); + const QPointF searchLeftMid(searchLeft, searchMidY); + const QPointF searchBtmMid(searchMidX, searchBottom); + const QPointF outterMidLeftExt = computeMidPointExtent(searchTopLeft, searchBtmLeft, outterMidLeft, handleSize); + const QPointF outterMidRightExt = computeMidPointExtent(searchBtmRight, searchTopRight, outterMidRight, handleSize); + const QPointF outterMidTopExt = computeMidPointExtent(searchTopRight, searchTopLeft, outterMidTop, handleSize); + const QPointF outterMidBtmExt = computeMidPointExtent(searchBtmLeft, searchBtmRight, outterMidBtm, handleSize); + std::string name = (*it)->getLabel(); + std::map > centerPoints; + int floorTime = std::floor(time + 0.5); + boost::shared_ptr xCurve = centerKnob->getCurve(ViewSpec::current(), 0); + boost::shared_ptr yCurve = centerKnob->getCurve(ViewSpec::current(), 1); + boost::shared_ptr errorCurve = errorKnob->getCurve(ViewSpec::current(), 0); + + for (int i = 0; i < MAX_CENTER_POINTS_DISPLAYED / 2; ++i) { + KeyFrame k; + int keyTimes[2] = {floorTime + i, floorTime - i}; + + for (int j = 0; j < 2; ++j) { + if ( xCurve->getKeyFrameWithTime(keyTimes[j], &k) ) { + std::pair& p = centerPoints[k.getTime()]; + p.first.x = k.getValue(); + p.first.y = INT_MIN; + + if ( yCurve->getKeyFrameWithTime(keyTimes[j], &k) ) { + p.first.y = k.getValue(); + } + if ( showErrorColor && errorCurve->getKeyFrameWithTime(keyTimes[j], &k) ) { + p.second = k.getValue(); + } + } + } + } + + + for (int l = 0; l < 2; ++l) { + // shadow (uses GL_PROJECTION) + glMatrixMode(GL_PROJECTION); + int direction = (l == 0) ? 1 : -1; + // translate (1,-1) pixels + glTranslated(direction * shadow.x, -direction * shadow.y, 0); + glMatrixMode(GL_MODELVIEW); + + ///Draw center position + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + glBegin(GL_LINE_STRIP); + glColor3f(0.5 * l, 0.5 * l, 0.5 * l); + for (std::map >::iterator it = centerPoints.begin(); it != centerPoints.end(); ++it) { + glVertex2d(it->second.first.x, it->second.first.y); + } + glEnd(); + glDisable(GL_LINE_SMOOTH); + + glEnable(GL_POINT_SMOOTH); + glBegin(GL_POINTS); + if (!showErrorColor) { + glColor3f(0.5 * l, 0.5 * l, 0.5 * l); + } + + for (std::map >::iterator it2 = centerPoints.begin(); it2 != centerPoints.end(); ++it2) { + if (showErrorColor) { + if (l != 0) { + /* + Clamp the correlation to [CORRELATION_ERROR_MIN, 1] then + Map the correlation to [0, 0.33] since 0 is Red for HSV and 0.33 is Green. + Also clamp to the interval if the correlation is higher, and reverse. + */ + + double error = std::min(std::max(it2->second.second, 0.), CORRELATION_ERROR_MAX_DISPLAY); + double mappedError = 0.33 - 0.33 * error / CORRELATION_ERROR_MAX_DISPLAY; + QColor c; + c.setHsvF(mappedError, 1., 1.); + glColor3f( c.redF(), c.greenF(), c.blueF() ); + } else { + glColor3f(0., 0., 0.); + } + } + glVertex2d(it2->second.first.x, it2->second.first.y); + } + glEnd(); + glDisable(GL_POINT_SMOOTH); + + + + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + glBegin(GL_LINE_LOOP); + glVertex2d( topLeft.x(), topLeft.y() ); + glVertex2d( topRight.x(), topRight.y() ); + glVertex2d( btmRight.x(), btmRight.y() ); + glVertex2d( btmLeft.x(), btmLeft.y() ); + glEnd(); + + glBegin(GL_LINE_LOOP); + glVertex2d( searchTopLeft.x(), searchTopLeft.y() ); + glVertex2d( searchTopRight.x(), searchTopRight.y() ); + glVertex2d( searchBtmRight.x(), searchBtmRight.y() ); + glVertex2d( searchBtmLeft.x(), searchBtmRight.y() ); + glEnd(); + + glPointSize(POINT_SIZE); + glBegin(GL_POINTS); + + ///draw center + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringCenter) || (_imp->eventState == eMouseStateDraggingCenter) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( center.x(), center.y() ); + + if ( (offset.x() != 0) || (offset.y() != 0) ) { + glVertex2d( center.x() + offset.x(), center.y() + offset.y() ); + } + + //////DRAWING INNER POINTS + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerBtmLeft) || (_imp->eventState == eMouseStateDraggingInnerBtmLeft) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( btmLeft.x(), btmLeft.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerBtmMid) || (_imp->eventState == eMouseStateDraggingInnerBtmMid) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( innerMidBtm.x(), innerMidBtm.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerBtmRight) || (_imp->eventState == eMouseStateDraggingInnerBtmRight) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( btmRight.x(), btmRight.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerMidLeft) || (_imp->eventState == eMouseStateDraggingInnerMidLeft) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( innerMidLeft.x(), innerMidLeft.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerMidRight) || (_imp->eventState == eMouseStateDraggingInnerMidRight) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( innerMidRight.x(), innerMidRight.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerTopLeft) || (_imp->eventState == eMouseStateDraggingInnerTopLeft) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( topLeft.x(), topLeft.y() ); + } + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerTopMid) || (_imp->eventState == eMouseStateDraggingInnerTopMid) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( innerMidTop.x(), innerMidTop.y() ); + } + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerTopRight) || (_imp->eventState == eMouseStateDraggingInnerTopRight) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( topRight.x(), topRight.y() ); + } + + + //////DRAWING OUTTER POINTS + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterBtmLeft) || (_imp->eventState == eMouseStateDraggingOuterBtmLeft) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( searchBtmLeft.x(), searchBtmLeft.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterBtmMid) || (_imp->eventState == eMouseStateDraggingOuterBtmMid) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( outterMidBtm.x(), outterMidBtm.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterBtmRight) || (_imp->eventState == eMouseStateDraggingOuterBtmRight) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( searchBtmRight.x(), searchBtmRight.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterMidLeft) || (_imp->eventState == eMouseStateDraggingOuterMidLeft) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( outterMidLeft.x(), outterMidLeft.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterMidRight) || (_imp->eventState == eMouseStateDraggingOuterMidRight) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( outterMidRight.x(), outterMidRight.y() ); + } + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterTopLeft) || (_imp->eventState == eMouseStateDraggingOuterTopLeft) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( searchTopLeft.x(), searchTopLeft.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterTopMid) || (_imp->eventState == eMouseStateDraggingOuterTopMid) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( outterMidTop.x(), outterMidTop.y() ); + } + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterTopRight) || (_imp->eventState == eMouseStateDraggingOuterTopRight) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + glVertex2d( searchTopRight.x(), searchTopRight.y() ); + } + + glEnd(); + + if ( (offset.x() != 0) || (offset.y() != 0) ) { + glBegin(GL_LINES); + glColor3f( (float)markerColor[0] * l * 0.5, (float)markerColor[1] * l * 0.5, (float)markerColor[2] * l * 0.5 ); + glVertex2d( center.x(), center.y() ); + glVertex2d( center.x() + offset.x(), center.y() + offset.y() ); + glEnd(); + } + + ///now show small lines at handle positions + glBegin(GL_LINES); + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerMidLeft) || (_imp->eventState == eMouseStateDraggingInnerMidLeft) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( innerMidLeft.x(), innerMidLeft.y() ); + glVertex2d( innerMidLeftExt.x(), innerMidLeftExt.y() ); + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerTopMid) || (_imp->eventState == eMouseStateDraggingInnerTopMid) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( innerMidTop.x(), innerMidTop.y() ); + glVertex2d( innerMidTopExt.x(), innerMidTopExt.y() ); + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerMidRight) || (_imp->eventState == eMouseStateDraggingInnerMidRight) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( innerMidRight.x(), innerMidRight.y() ); + glVertex2d( innerMidRightExt.x(), innerMidRightExt.y() ); + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerBtmMid) || (_imp->eventState == eMouseStateDraggingInnerBtmMid) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( innerMidBtm.x(), innerMidBtm.y() ); + glVertex2d( innerMidBtmExt.x(), innerMidBtmExt.y() ); + + //////DRAWING OUTTER HANDLES + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterMidLeft) || (_imp->eventState == eMouseStateDraggingOuterMidLeft) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( outterMidLeft.x(), outterMidLeft.y() ); + glVertex2d( outterMidLeftExt.x(), outterMidLeftExt.y() ); + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterTopMid) || (_imp->eventState == eMouseStateDraggingOuterTopMid) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( outterMidTop.x(), outterMidTop.y() ); + glVertex2d( outterMidTopExt.x(), outterMidTopExt.y() ); + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterMidRight) || (_imp->eventState == eMouseStateDraggingOuterMidRight) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( outterMidRight.x(), outterMidRight.y() ); + glVertex2d( outterMidRightExt.x(), outterMidRightExt.y() ); + + if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterBtmMid) || (_imp->eventState == eMouseStateDraggingOuterBtmMid) ) ) { + glColor3f(0.f * l, 1.f * l, 0.f * l); + } else { + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + } + glVertex2d( outterMidBtm.x(), outterMidBtm.y() ); + glVertex2d( outterMidBtmExt.x(), outterMidBtmExt.y() ); + glEnd(); + + glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); + + QColor c; + c.setRgbF(markerColor[0], markerColor[1], markerColor[2]); + viewer->renderText( center.x(), center.y(), QString::fromUtf8( name.c_str() ), c, viewer->font() ); + } // for (int l = 0; l < 2; ++l) { + } // if (!isSelected) { + } // for (std::vector::iterator it = allMarkers.begin(); it!=allMarkers.end(); ++it) { + + if (_imp->showMarkerTexture) { + _imp->drawSelectedMarkerTexture(std::make_pair(pixelScaleX, pixelScaleY), _imp->selectedMarkerTextureTime, selectedCenter, selectedOffset, selectedPtnTopLeft, selectedPtnTopRight, selectedPtnBtmRight, selectedPtnBtmLeft, selectedSearchBtmLeft, selectedSearchTopRight); + } + _imp->panel->getContext()->drawInternalNodesOverlay( time, renderScale, view, _imp->viewer->getViewer() ); + } // // if (_imp->panelv1) { + + + if (_imp->clickToAddTrackEnabled) { + ///draw a square of 20px around the mouse cursor + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + glLineWidth(1.5); + //GLProtectMatrix p(GL_PROJECTION); // useless (we do two glTranslate in opposite directions) + + const double addTrackSize = TO_DPIX(ADDTRACK_SIZE); + + for (int l = 0; l < 2; ++l) { + // shadow (uses GL_PROJECTION) + glMatrixMode(GL_PROJECTION); + int direction = (l == 0) ? 1 : -1; + // translate (1,-1) pixels + glTranslated(direction * pixelScaleX / 256, -direction * pixelScaleY / 256, 0); + glMatrixMode(GL_MODELVIEW); + + if (l == 0) { + glColor4d(0., 0., 0., 0.8); + } else { + glColor4d(0., 1., 0., 0.8); + } + + glBegin(GL_LINE_LOOP); + glVertex2d(_imp->lastMousePos.x() - addTrackSize * 2 * pixelScaleX, _imp->lastMousePos.y() - addTrackSize * 2 * pixelScaleY); + glVertex2d(_imp->lastMousePos.x() - addTrackSize * 2 * pixelScaleX, _imp->lastMousePos.y() + addTrackSize * 2 * pixelScaleY); + glVertex2d(_imp->lastMousePos.x() + addTrackSize * 2 * pixelScaleX, _imp->lastMousePos.y() + addTrackSize * 2 * pixelScaleY); + glVertex2d(_imp->lastMousePos.x() + addTrackSize * 2 * pixelScaleX, _imp->lastMousePos.y() - addTrackSize * 2 * pixelScaleY); + glEnd(); + + ///draw a cross at the cursor position + glBegin(GL_LINES); + glVertex2d( _imp->lastMousePos.x() - addTrackSize * pixelScaleX, _imp->lastMousePos.y() ); + glVertex2d( _imp->lastMousePos.x() + addTrackSize * pixelScaleX, _imp->lastMousePos.y() ); + glVertex2d(_imp->lastMousePos.x(), _imp->lastMousePos.y() - addTrackSize * pixelScaleY); + glVertex2d(_imp->lastMousePos.x(), _imp->lastMousePos.y() + addTrackSize * pixelScaleY); + glEnd(); + } + } + } // GLProtectAttrib a(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_ENABLE_BIT | GL_HINT_BIT); +#endif +} // drawOverlay + +bool +TrackerNode::onOverlayPenDown(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, PenType pen) +{ +#if 0 + std::pair pixelScale; + ViewerGL* viewer = _imp->viewer->getViewer(); + + viewer->getPixelScale(pixelScale.first, pixelScale.second); + bool didSomething = false; + + if (_imp->panel) { + if ( _imp->panel->getContext()->onOverlayPenDownInternalNodes( time, renderScale, view, viewportPos, pos, pressure, _imp->viewer->getViewer() ) ) { + return true; + } + } + + + if (_imp->panelv1) { + const std::list > & instances = _imp->panelv1->getInstances(); + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + NodePtr instance = it->first.lock(); + if ( it->second && !instance->isNodeDisabled() ) { + EffectInstPtr effect = instance->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); + didSomething = effect->onOverlayPenDown_public(time, renderScale, view, viewportPos, pos, pressure); + } + } + + double selectionTol = pixelScale.first * 10.; + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + NodePtr instance = it->first.lock(); + boost::shared_ptr newInstanceKnob = instance->getKnobByName("center"); + assert(newInstanceKnob); //< if it crashes here that means the parameter's name changed in the OpenFX plug-in. + KnobDouble* dblKnob = dynamic_cast( newInstanceKnob.get() ); + assert(dblKnob); + double x, y; + x = dblKnob->getValueAtTime(time, 0); + y = dblKnob->getValueAtTime(time, 1); + + if ( ( pos.x() >= (x - selectionTol) ) && ( pos.x() <= (x + selectionTol) ) && + ( pos.y() >= (y - selectionTol) ) && ( pos.y() <= (y + selectionTol) ) ) { + if (!it->second) { + _imp->panelv1->selectNode( instance, modCASIsShift(e) ); + } + didSomething = true; + } + } + + if (_imp->clickToAddTrackEnabled && !didSomething) { + NodePtr newInstance = _imp->panelv1->createNewInstance(true); + boost::shared_ptr newInstanceKnob = newInstance->getKnobByName("center"); + assert(newInstanceKnob); //< if it crashes here that means the parameter's name changed in the OpenFX plug-in. + KnobDouble* dblKnob = dynamic_cast( newInstanceKnob.get() ); + assert(dblKnob); + dblKnob->beginChanges(); + dblKnob->blockValueChanges(); + dblKnob->setValueAtTime(time, pos.x(), view, 0); + dblKnob->setValueAtTime(time, pos.y(), view, 1); + dblKnob->unblockValueChanges(); + dblKnob->endChanges(); + didSomething = true; + } + + if ( !didSomething && !modCASIsShift(e) ) { + _imp->panelv1->clearSelection(); + } + } else { // if (_imp->panelv1) { + boost::shared_ptr context = _imp->panel->getContext(); + std::vector allMarkers; + context->getAllMarkers(&allMarkers); + for (std::vector::iterator it = allMarkers.begin(); it != allMarkers.end(); ++it) { + if ( !(*it)->isEnabled(time) ) { + continue; + } + + bool isSelected = context->isMarkerSelected( (*it) ); + boost::shared_ptr centerKnob = (*it)->getCenterKnob(); + boost::shared_ptr offsetKnob = (*it)->getOffsetKnob(); + boost::shared_ptr ptnTopLeft = (*it)->getPatternTopLeftKnob(); + boost::shared_ptr ptnTopRight = (*it)->getPatternTopRightKnob(); + boost::shared_ptr ptnBtmRight = (*it)->getPatternBtmRightKnob(); + boost::shared_ptr ptnBtmLeft = (*it)->getPatternBtmLeftKnob(); + boost::shared_ptr searchWndTopRight = (*it)->getSearchWindowTopRightKnob(); + boost::shared_ptr searchWndBtmLeft = (*it)->getSearchWindowBottomLeftKnob(); + QPointF centerPoint; + centerPoint.rx() = centerKnob->getValueAtTime(time, 0); + centerPoint.ry() = centerKnob->getValueAtTime(time, 1); + + QPointF offset; + offset.rx() = offsetKnob->getValueAtTime(time, 0); + offset.ry() = offsetKnob->getValueAtTime(time, 1); + + if ( isNearbyPoint(centerKnob, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE, time) ) { + if (_imp->controlDown > 0) { + _imp->eventState = eMouseStateDraggingOffset; + } else { + _imp->eventState = eMouseStateDraggingCenter; + } + _imp->interactMarker = *it; + didSomething = true; + } else if ( ( (offset.x() != 0) || (offset.y() != 0) ) && isNearbyPoint(QPointF( centerPoint.x() + offset.x(), centerPoint.y() + offset.y() ), viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOffset; + _imp->interactMarker = *it; + didSomething = true; + } + + if (!didSomething && isSelected) { + QPointF topLeft, topRight, btmRight, btmLeft; + topLeft.rx() = ptnTopLeft->getValueAtTime(time, 0) + offset.x() + centerPoint.x(); + topLeft.ry() = ptnTopLeft->getValueAtTime(time, 1) + offset.y() + centerPoint.y(); + + topRight.rx() = ptnTopRight->getValueAtTime(time, 0) + offset.x() + centerPoint.x(); + topRight.ry() = ptnTopRight->getValueAtTime(time, 1) + offset.y() + centerPoint.y(); + + btmRight.rx() = ptnBtmRight->getValueAtTime(time, 0) + offset.x() + centerPoint.x(); + btmRight.ry() = ptnBtmRight->getValueAtTime(time, 1) + offset.y() + centerPoint.y(); + + btmLeft.rx() = ptnBtmLeft->getValueAtTime(time, 0) + offset.x() + centerPoint.x(); + btmLeft.ry() = ptnBtmLeft->getValueAtTime(time, 1) + offset.y() + centerPoint.y(); + + QPointF midTop, midRight, midBtm, midLeft; + midTop.rx() = ( topLeft.x() + topRight.x() ) / 2.; + midTop.ry() = ( topLeft.y() + topRight.y() ) / 2.; + + midRight.rx() = ( btmRight.x() + topRight.x() ) / 2.; + midRight.ry() = ( btmRight.y() + topRight.y() ) / 2.; + + midBtm.rx() = ( btmRight.x() + btmLeft.x() ) / 2.; + midBtm.ry() = ( btmRight.y() + btmLeft.y() ) / 2.; + + midLeft.rx() = ( topLeft.x() + btmLeft.x() ) / 2.; + midLeft.ry() = ( topLeft.y() + btmLeft.y() ) / 2.; + + if ( isSelected && isNearbyPoint(topLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingInnerTopLeft; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isSelected && isNearbyPoint(topRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingInnerTopRight; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isSelected && isNearbyPoint(btmRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingInnerBtmRight; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isSelected && isNearbyPoint(btmLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingInnerBtmLeft; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isSelected && isNearbyPoint(midTop, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingInnerTopMid; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isSelected && isNearbyPoint(midRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingInnerMidRight; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isSelected && isNearbyPoint(midLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingInnerMidLeft; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isSelected && isNearbyPoint(midBtm, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingInnerBtmMid; + _imp->interactMarker = *it; + didSomething = true; + } + } + + if (!didSomething && isSelected) { + ///Test search window + const double searchLeft = searchWndBtmLeft->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); + const double searchRight = searchWndTopRight->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); + const double searchTop = searchWndTopRight->getValueAtTime(time, 1) + centerPoint.y() + offset.y(); + const double searchBottom = searchWndBtmLeft->getValueAtTime(time, 1) + +centerPoint.y() + offset.y(); + const double searchMidX = (searchLeft + searchRight) / 2.; + const double searchMidY = (searchTop + searchBottom) / 2.; + const QPointF searchTopLeft(searchLeft, searchTop); + const QPointF searchTopRight(searchRight, searchTop); + const QPointF searchBtmRight(searchRight, searchBottom); + const QPointF searchBtmLeft(searchLeft, searchBottom); + const QPointF searchTopMid(searchMidX, searchTop); + const QPointF searchRightMid(searchRight, searchMidY); + const QPointF searchLeftMid(searchLeft, searchMidY); + const QPointF searchBtmMid(searchMidX, searchBottom); + + if ( isNearbyPoint(searchTopLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOuterTopLeft; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isNearbyPoint(searchTopRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOuterTopRight; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isNearbyPoint(searchBtmRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOuterBtmRight; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isNearbyPoint(searchBtmLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOuterBtmLeft; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isNearbyPoint(searchTopMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOuterTopMid; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isNearbyPoint(searchBtmMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOuterBtmMid; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isNearbyPoint(searchLeftMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOuterMidLeft; + _imp->interactMarker = *it; + didSomething = true; + } else if ( isNearbyPoint(searchRightMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->eventState = eMouseStateDraggingOuterMidRight; + _imp->interactMarker = *it; + didSomething = true; + } + } + + //If we hit the interact, make sure it is selected + if (_imp->interactMarker) { + if (!isSelected) { + context->beginEditSelection(TrackerContext::eTrackSelectionViewer); + if ( !modCASIsShift(e) ) { + context->clearSelection(TrackerContext::eTrackSelectionViewer); + } + context->addTrackToSelection(_imp->interactMarker, TrackerContext::eTrackSelectionViewer); + context->endEditSelection(TrackerContext::eTrackSelectionViewer); + } + } + + if (didSomething) { + break; + } + } // for (std::vector::iterator it = allMarkers.begin(); it!=allMarkers.end(); ++it) { + + if (_imp->clickToAddTrackEnabled && !didSomething) { + TrackMarkerPtr marker = context->createMarker(); + boost::shared_ptr centerKnob = marker->getCenterKnob(); + centerKnob->setValuesAtTime(time, pos.x(), pos.y(), view, eValueChangedReasonNatronInternalEdited); + if ( _imp->createKeyOnMoveButton->isChecked() ) { + marker->setUserKeyframe(time); + } + _imp->panel->pushUndoCommand( new AddTrackCommand(marker, context) ); + updateSelectedMarkerTexture(); + didSomething = true; + } + + if ( !didSomething && _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->isNearbySelectedMarkerTextureResizeAnchor(pos) ) { + _imp->eventState = eMouseStateDraggingSelectedMarkerResizeAnchor; + didSomething = true; + } + + if ( !didSomething && _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->isInsideSelectedMarkerTextureResizeAnchor(pos) ) { + if (_imp->shiftDown) { + _imp->eventState = eMouseStateScalingSelectedMarker; + } else { + _imp->eventState = eMouseStateDraggingSelectedMarker; + } + _imp->interactMarker = _imp->selectedMarker.lock(); + didSomething = true; + } + + if (!didSomething) { + int keyTime = _imp->isInsideKeyFrameTexture(time, pos, viewportPos); + if (keyTime != INT_MAX) { + _imp->viewer->seek(keyTime); + didSomething = true; + } + } + if (!didSomething) { + std::list selectedMarkers; + context->getSelectedMarkers(&selectedMarkers); + if ( !selectedMarkers.empty() ) { + context->clearSelection(TrackerContext::eTrackSelectionViewer); + + didSomething = true; + } + } + } + _imp->lastMousePos = pos; + + return didSomething; +#endif +} // penDown + +bool +TrackerNode::onOverlayPenMotion(double time, const RenderScale & renderScale, ViewIdx view, + const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) +{ +#if 0 + std::pair pixelScale; + ViewerGL* viewer = _imp->viewer->getViewer(); + + viewer->getPixelScale(pixelScale.first, pixelScale.second); + bool didSomething = false; + + if (_imp->panel) { + if ( _imp->panel->getContext()->onOverlayPenMotionInternalNodes( time, renderScale, view, viewportPos, pos, pressure, _imp->viewer->getViewer() ) ) { + return true; + } + } + + Point delta; + delta.x = pos.x() - _imp->lastMousePos.x(); + delta.y = pos.y() - _imp->lastMousePos.y(); + + if (_imp->panelv1) { + const std::list > & instances = _imp->panelv1->getInstances(); + + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + NodePtr instance = it->first.lock(); + if ( it->second && !instance->isNodeDisabled() ) { + EffectInstPtr effect = instance->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); + if ( effect->onOverlayPenMotion_public(time, renderScale, view, viewportPos, pos, pressure) ) { + didSomething = true; + } + } + } + } else { + if (_imp->hoverState != eDrawStateInactive) { + _imp->hoverState = eDrawStateInactive; + _imp->hoverMarker.reset(); + didSomething = true; + } + + boost::shared_ptr context = _imp->panel->getContext(); + std::vector allMarkers; + context->getAllMarkers(&allMarkers); + + bool hoverProcess = false; + for (std::vector::iterator it = allMarkers.begin(); it != allMarkers.end(); ++it) { + if ( !(*it)->isEnabled(time) ) { + continue; + } + + bool isSelected = context->isMarkerSelected( (*it) ); + boost::shared_ptr centerKnob = (*it)->getCenterKnob(); + boost::shared_ptr offsetKnob = (*it)->getOffsetKnob(); + boost::shared_ptr ptnTopLeft = (*it)->getPatternTopLeftKnob(); + boost::shared_ptr ptnTopRight = (*it)->getPatternTopRightKnob(); + boost::shared_ptr ptnBtmRight = (*it)->getPatternBtmRightKnob(); + boost::shared_ptr ptnBtmLeft = (*it)->getPatternBtmLeftKnob(); + boost::shared_ptr searchWndTopRight = (*it)->getSearchWindowTopRightKnob(); + boost::shared_ptr searchWndBtmLeft = (*it)->getSearchWindowBottomLeftKnob(); + QPointF center; + center.rx() = centerKnob->getValueAtTime(time, 0); + center.ry() = centerKnob->getValueAtTime(time, 1); + + QPointF offset; + offset.rx() = offsetKnob->getValueAtTime(time, 0); + offset.ry() = offsetKnob->getValueAtTime(time, 1); + if ( isNearbyPoint(centerKnob, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE, time) ) { + _imp->hoverState = eDrawStateHoveringCenter; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( ( (offset.x() != 0) || (offset.y() != 0) ) && isNearbyPoint(QPointF( center.x() + offset.x(), center.y() + offset.y() ), viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringCenter; + _imp->hoverMarker = *it; + didSomething = true; + } + + + if (!hoverProcess) { + QPointF topLeft, topRight, btmRight, btmLeft; + topLeft.rx() = ptnTopLeft->getValueAtTime(time, 0) + offset.x() + center.x(); + topLeft.ry() = ptnTopLeft->getValueAtTime(time, 1) + offset.y() + center.y(); + + topRight.rx() = ptnTopRight->getValueAtTime(time, 0) + offset.x() + center.x(); + topRight.ry() = ptnTopRight->getValueAtTime(time, 1) + offset.y() + center.y(); + + btmRight.rx() = ptnBtmRight->getValueAtTime(time, 0) + offset.x() + center.x(); + btmRight.ry() = ptnBtmRight->getValueAtTime(time, 1) + offset.y() + center.y(); + + btmLeft.rx() = ptnBtmLeft->getValueAtTime(time, 0) + offset.x() + center.x(); + btmLeft.ry() = ptnBtmLeft->getValueAtTime(time, 1) + offset.y() + center.y(); + + QPointF midTop, midRight, midBtm, midLeft; + midTop.rx() = ( topLeft.x() + topRight.x() ) / 2.; + midTop.ry() = ( topLeft.y() + topRight.y() ) / 2.; + + midRight.rx() = ( btmRight.x() + topRight.x() ) / 2.; + midRight.ry() = ( btmRight.y() + topRight.y() ) / 2.; + + midBtm.rx() = ( btmRight.x() + btmLeft.x() ) / 2.; + midBtm.ry() = ( btmRight.y() + btmLeft.y() ) / 2.; + + midLeft.rx() = ( topLeft.x() + btmLeft.x() ) / 2.; + midLeft.ry() = ( topLeft.y() + btmLeft.y() ) / 2.; + + + if ( isSelected && isNearbyPoint(topLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringInnerTopLeft; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isSelected && isNearbyPoint(topRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringInnerTopRight; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isSelected && isNearbyPoint(btmRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringInnerBtmRight; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isSelected && isNearbyPoint(btmLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringInnerBtmLeft; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isSelected && isNearbyPoint(midTop, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringInnerTopMid; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isSelected && isNearbyPoint(midRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringInnerMidRight; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isSelected && isNearbyPoint(midLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringInnerMidLeft; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isSelected && isNearbyPoint(midBtm, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringInnerBtmMid; + _imp->hoverMarker = *it; + hoverProcess = true; + } + } + + if (!hoverProcess && isSelected) { + ///Test search window + const double searchLeft = searchWndBtmLeft->getValueAtTime(time, 0) + offset.x() + center.x(); + const double searchBottom = searchWndBtmLeft->getValueAtTime(time, 1) + offset.y() + center.y(); + const double searchRight = searchWndTopRight->getValueAtTime(time, 0) + offset.x() + center.x(); + const double searchTop = searchWndTopRight->getValueAtTime(time, 1) + offset.y() + center.y(); + const double searchMidX = (searchLeft + searchRight) / 2; + const double searchMidY = (searchTop + searchBottom) / 2; + const QPointF searchTopLeft(searchLeft, searchTop); + const QPointF searchTopRight(searchRight, searchTop); + const QPointF searchBtmRight(searchRight, searchBottom); + const QPointF searchBtmLeft(searchLeft, searchBottom); + const QPointF searchTopMid(searchMidX, searchTop); + const QPointF searchRightMid(searchRight, searchMidY); + const QPointF searchLeftMid(searchLeft, searchMidY); + const QPointF searchBtmMid(searchMidX, searchBottom); + + if ( isNearbyPoint(searchTopLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringOuterTopLeft; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isNearbyPoint(searchTopRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringOuterTopRight; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isNearbyPoint(searchBtmRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringOuterBtmRight; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isNearbyPoint(searchBtmLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringOuterBtmLeft; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isNearbyPoint(searchTopMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringOuterTopMid; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isNearbyPoint(searchBtmMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringOuterBtmMid; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isNearbyPoint(searchLeftMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringOuterMidLeft; + _imp->hoverMarker = *it; + hoverProcess = true; + } else if ( isNearbyPoint(searchRightMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { + _imp->hoverState = eDrawStateHoveringOuterMidRight; + _imp->hoverMarker = *it; + hoverProcess = true; + } + } + + if (hoverProcess) { + break; + } + } // for (std::vector::iterator it = allMarkers.begin(); it!=allMarkers.end(); ++it) { + + if ( _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->isNearbySelectedMarkerTextureResizeAnchor(pos) ) { + _imp->viewer->getViewer()->setCursor(Qt::SizeFDiagCursor); + hoverProcess = true; + } else if ( _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->isInsideSelectedMarkerTextureResizeAnchor(pos) ) { + _imp->viewer->getViewer()->setCursor(Qt::SizeAllCursor); + hoverProcess = true; + } else if ( _imp->showMarkerTexture && (_imp->isInsideKeyFrameTexture(time, pos, viewportPos) != INT_MAX) ) { + _imp->viewer->getViewer()->setCursor(Qt::PointingHandCursor); + hoverProcess = true; + } else { + _imp->viewer->getViewer()->unsetCursor(); + } + + if ( _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->shiftDown && _imp->isInsideSelectedMarkerTextureResizeAnchor(pos) ) { + _imp->hoverState = eDrawStateShowScalingHint; + hoverProcess = true; + } + + if (hoverProcess) { + didSomething = true; + } + + boost::shared_ptr centerKnob, offsetKnob, searchWndTopRight, searchWndBtmLeft; + boost::shared_ptr patternCorners[4]; + if (_imp->interactMarker) { + centerKnob = _imp->interactMarker->getCenterKnob(); + offsetKnob = _imp->interactMarker->getOffsetKnob(); + + /* + + TopLeft(0) ------------- Top right(3) + | | + | | + | | + Btm left (1) ------------ Btm right (2) + + */ + patternCorners[0] = _imp->interactMarker->getPatternTopLeftKnob(); + patternCorners[1] = _imp->interactMarker->getPatternBtmLeftKnob(); + patternCorners[2] = _imp->interactMarker->getPatternBtmRightKnob(); + patternCorners[3] = _imp->interactMarker->getPatternTopRightKnob(); + searchWndTopRight = _imp->interactMarker->getSearchWindowTopRightKnob(); + searchWndBtmLeft = _imp->interactMarker->getSearchWindowBottomLeftKnob(); + } + + + switch (_imp->eventState) { + case eMouseStateDraggingCenter: + case eMouseStateDraggingOffset: { + assert(_imp->interactMarker); + if (_imp->eventState == eMouseStateDraggingOffset) { + offsetKnob->setValues(offsetKnob->getValueAtTime(time, 0) + delta.x, + offsetKnob->getValueAtTime(time, 1) + delta.y, + view, + eValueChangedReasonPluginEdited); + } else { + centerKnob->setValuesAtTime(time, centerKnob->getValueAtTime(time, 0) + delta.x, + centerKnob->getValueAtTime(time, 1) + delta.y, + view, + eValueChangedReasonPluginEdited); + for (int i = 0; i < 4; ++i) { + for (int d = 0; d < patternCorners[i]->getDimension(); ++d) { + patternCorners[i]->setValueAtTime(time, patternCorners[i]->getValueAtTime(time, d), view, d); + } + } + } + updateSelectedMarkerTexture(); + if ( _imp->createKeyOnMoveButton->isChecked() ) { + _imp->interactMarker->setUserKeyframe(time); + } + didSomething = true; + break; + } + case eMouseStateDraggingInnerBtmLeft: + case eMouseStateDraggingInnerTopRight: + case eMouseStateDraggingInnerTopLeft: + case eMouseStateDraggingInnerBtmRight: { + if (_imp->controlDown == 0) { + _imp->transformPattern(time, _imp->eventState, delta); + didSomething = true; + break; + } + + int index; + if (_imp->eventState == eMouseStateDraggingInnerBtmLeft) { + index = 1; + } else if (_imp->eventState == eMouseStateDraggingInnerBtmRight) { + index = 2; + } else if (_imp->eventState == eMouseStateDraggingInnerTopRight) { + index = 3; + } else if (_imp->eventState == eMouseStateDraggingInnerTopLeft) { + index = 0; + } + + int nextIndex = (index + 1) % 4; + int prevIndex = (index + 3) % 4; + int diagIndex = (index + 2) % 4; + Point center; + center.x = centerKnob->getValueAtTime(time, 0); + center.y = centerKnob->getValueAtTime(time, 1); + Point offset; + offset.x = offsetKnob->getValueAtTime(time, 0); + offset.y = offsetKnob->getValueAtTime(time, 1); + + Point cur, prev, next, diag; + cur.x = patternCorners[index]->getValueAtTime(time, 0) + delta.x + center.x + offset.x;; + cur.y = patternCorners[index]->getValueAtTime(time, 1) + delta.y + center.y + offset.y; + + prev.x = patternCorners[prevIndex]->getValueAtTime(time, 0) + center.x + offset.x;; + prev.y = patternCorners[prevIndex]->getValueAtTime(time, 1) + center.y + offset.y; + + next.x = patternCorners[nextIndex]->getValueAtTime(time, 0) + center.x + offset.x;; + next.y = patternCorners[nextIndex]->getValueAtTime(time, 1) + center.y + offset.y; + + diag.x = patternCorners[diagIndex]->getValueAtTime(time, 0) + center.x + offset.x;; + diag.y = patternCorners[diagIndex]->getValueAtTime(time, 1) + center.y + offset.y; + + Point nextVec; + nextVec.x = next.x - cur.x; + nextVec.y = next.y - cur.y; + + Point prevVec; + prevVec.x = cur.x - prev.x; + prevVec.y = cur.y - prev.y; + + Point nextDiagVec, prevDiagVec; + prevDiagVec.x = diag.x - next.x; + prevDiagVec.y = diag.y - next.y; + + nextDiagVec.x = prev.x - diag.x; + nextDiagVec.y = prev.y - diag.y; + + //Clamp so the 4 points remaing the same in the homography + if (prevVec.x * nextVec.y - prevVec.y * nextVec.x < 0.) { // cross-product + findLineIntersection(cur, prev, next, &cur); + } + if (nextDiagVec.x * prevVec.y - nextDiagVec.y * prevVec.x < 0.) { // cross-product + findLineIntersection(cur, prev, diag, &cur); + } + if (nextVec.x * prevDiagVec.y - nextVec.y * prevDiagVec.x < 0.) { // cross-product + findLineIntersection(cur, next, diag, &cur); + } + + + Point searchWindowCorners[2]; + searchWindowCorners[0].x = searchWndBtmLeft->getValueAtTime(time, 0) + center.x + offset.x; + searchWindowCorners[0].y = searchWndBtmLeft->getValueAtTime(time, 1) + center.y + offset.y; + + searchWindowCorners[1].x = searchWndTopRight->getValueAtTime(time, 0) + center.x + offset.x; + searchWindowCorners[1].y = searchWndTopRight->getValueAtTime(time, 1) + center.y + offset.y; + + cur.x = std::max(std::min(cur.x, searchWindowCorners[1].x), searchWindowCorners[0].x); + cur.y = std::max(std::min(cur.y, searchWindowCorners[1].y), searchWindowCorners[0].y); + + cur.x -= (center.x + offset.x); + cur.y -= (center.y + offset.y); + + patternCorners[index]->setValuesAtTime(time, cur.x, cur.y, view, eValueChangedReasonNatronInternalEdited); + + if ( _imp->createKeyOnMoveButton->isChecked() ) { + _imp->interactMarker->setUserKeyframe(time); + } + didSomething = true; + break; + } + case eMouseStateDraggingOuterBtmLeft: { + if (_imp->controlDown == 0) { + _imp->transformPattern(time, _imp->eventState, delta); + didSomething = true; + break; + } + Point center; + center.x = centerKnob->getValueAtTime(time, 0); + center.y = centerKnob->getValueAtTime(time, 1); + Point offset; + offset.x = offsetKnob->getValueAtTime(time, 0); + offset.y = offsetKnob->getValueAtTime(time, 1); + + Point p; + p.x = searchWndBtmLeft->getValueAtTime(time, 0) + center.x + offset.x + delta.x; + p.y = searchWndBtmLeft->getValueAtTime(time, 1) + center.y + offset.y + delta.y; + + Point topLeft; + topLeft.x = patternCorners[0]->getValueAtTime(time, 0) + center.x + offset.x; + topLeft.y = patternCorners[0]->getValueAtTime(time, 1) + center.y + offset.y; + Point btmLeft; + btmLeft.x = patternCorners[1]->getValueAtTime(time, 0) + center.x + offset.x; + btmLeft.y = patternCorners[1]->getValueAtTime(time, 1) + center.y + offset.y; + Point btmRight; + btmRight.y = patternCorners[2]->getValueAtTime(time, 0) + center.x + offset.x; + btmRight.y = patternCorners[2]->getValueAtTime(time, 1) + center.y + offset.y; + Point topRight; + topRight.x = patternCorners[3]->getValueAtTime(time, 0) + center.x + offset.x; + topRight.y = patternCorners[3]->getValueAtTime(time, 1) + center.y + offset.y; + + // test every point: even topRight pattern corner may be on the left of topLeft + p.x = std::min(p.x, topLeft.x); + p.x = std::min(p.x, btmLeft.x); + p.x = std::min(p.x, btmRight.x); + p.x = std::min(p.x, topRight.x); + + p.y = std::min(p.y, topLeft.y); + p.y = std::min(p.y, btmLeft.y); + p.y = std::min(p.y, btmRight.y); + p.y = std::min(p.y, topRight.y); + + p.x -= (center.x + offset.x); + p.y -= (center.y + offset.y); + if ( searchWndBtmLeft->hasAnimation() ) { + searchWndBtmLeft->setValuesAtTime(time, p.x, p.y, view, eValueChangedReasonNatronInternalEdited); + } else { + searchWndBtmLeft->setValues(p.x, p.y, view, eValueChangedReasonNatronInternalEdited); + } + + updateSelectedMarkerTexture(); + didSomething = true; + break; + } + case eMouseStateDraggingOuterBtmRight: { + if (_imp->controlDown == 0) { + _imp->transformPattern(time, _imp->eventState, delta); + didSomething = true; + break; + } + Point center; + center.x = centerKnob->getValueAtTime(time, 0); + center.y = centerKnob->getValueAtTime(time, 1); + Point offset; + offset.x = offsetKnob->getValueAtTime(time, 0); + offset.y = offsetKnob->getValueAtTime(time, 1); + + Point p; + p.x = searchWndTopRight->getValueAtTime(time, 0) + center.x + offset.x + delta.x; + p.y = searchWndBtmLeft->getValueAtTime(time, 1) + center.y + offset.y + delta.y; + + Point topLeft; + topLeft.x = patternCorners[0]->getValueAtTime(time, 0) + center.x + offset.x; + topLeft.y = patternCorners[0]->getValueAtTime(time, 1) + center.y + offset.y; + Point btmLeft; + btmLeft.x = patternCorners[1]->getValueAtTime(time, 0) + center.x + offset.x; + btmLeft.y = patternCorners[1]->getValueAtTime(time, 1) + center.y + offset.y; + Point btmRight; + btmRight.y = patternCorners[2]->getValueAtTime(time, 0) + center.x + offset.x; + btmRight.y = patternCorners[2]->getValueAtTime(time, 1) + center.y + offset.y; + Point topRight; + topRight.x = patternCorners[3]->getValueAtTime(time, 0) + center.x + offset.x; + topRight.y = patternCorners[3]->getValueAtTime(time, 1) + center.y + offset.y; + + // test every point: even topRight pattern corner may be on the left of topLeft + p.x = std::max(p.x, topLeft.x); + p.x = std::max(p.x, btmLeft.x); + p.x = std::max(p.x, btmRight.x); + p.x = std::max(p.x, topRight.x); + + p.y = std::min(p.y, topLeft.y); + p.y = std::min(p.y, btmLeft.y); + p.y = std::min(p.y, btmRight.y); + p.y = std::min(p.y, topRight.y); + + p.x -= (center.x + offset.x); + p.y -= (center.y + offset.y); + if ( searchWndBtmLeft->hasAnimation() ) { + searchWndBtmLeft->setValueAtTime(time, p.y, view, 1); + } else { + searchWndBtmLeft->setValue(p.y, view, 1); + } + if ( searchWndTopRight->hasAnimation() ) { + searchWndTopRight->setValueAtTime(time, p.x, view, 0); + } else { + searchWndTopRight->setValue(p.x, view, 0); + } + + updateSelectedMarkerTexture(); + didSomething = true; + break; + } + case eMouseStateDraggingOuterTopRight: { + if (_imp->controlDown == 0) { + _imp->transformPattern(time, _imp->eventState, delta); + didSomething = true; + break; + } + Point center; + center.x = centerKnob->getValueAtTime(time, 0); + center.y = centerKnob->getValueAtTime(time, 1); + Point offset; + offset.x = offsetKnob->getValueAtTime(time, 0); + offset.y = offsetKnob->getValueAtTime(time, 1); + + Point p; + p.x = searchWndTopRight->getValueAtTime(time, 0) + center.x + offset.x + delta.x; + p.y = searchWndTopRight->getValueAtTime(time, 1) + center.y + offset.y + delta.y; + + Point topLeft; + topLeft.x = patternCorners[0]->getValueAtTime(time, 0) + center.x + offset.x; + topLeft.y = patternCorners[0]->getValueAtTime(time, 1) + center.y + offset.y; + Point btmLeft; + btmLeft.x = patternCorners[1]->getValueAtTime(time, 0) + center.x + offset.x; + btmLeft.y = patternCorners[1]->getValueAtTime(time, 1) + center.y + offset.y; + Point btmRight; + btmRight.y = patternCorners[2]->getValueAtTime(time, 0) + center.x + offset.x; + btmRight.y = patternCorners[2]->getValueAtTime(time, 1) + center.y + offset.y; + Point topRight; + topRight.x = patternCorners[3]->getValueAtTime(time, 0) + center.x + offset.x; + topRight.y = patternCorners[3]->getValueAtTime(time, 1) + center.y + offset.y; + + // test every point: even topRight pattern corner may be on the left of topLeft + p.x = std::max(p.x, topLeft.x); + p.x = std::max(p.x, btmLeft.x); + p.x = std::max(p.x, btmRight.x); + p.x = std::max(p.x, topRight.x); + + p.y = std::max(p.y, topLeft.y); + p.y = std::max(p.y, btmLeft.y); + p.y = std::max(p.y, btmRight.y); + p.y = std::max(p.y, topRight.y); + + p.x -= (center.x + offset.x); + p.y -= (center.y + offset.y); + if ( searchWndTopRight->hasAnimation() ) { + searchWndTopRight->setValuesAtTime(time, p.x, p.y, view, eValueChangedReasonNatronInternalEdited); + } else { + searchWndTopRight->setValues(p.x, p.y, view, eValueChangedReasonNatronInternalEdited); + } + + updateSelectedMarkerTexture(); + didSomething = true; + break; + } + case eMouseStateDraggingOuterTopLeft: { + if (_imp->controlDown == 0) { + _imp->transformPattern(time, _imp->eventState, delta); + didSomething = true; + break; + } + Point center; + center.x = centerKnob->getValueAtTime(time, 0); + center.y = centerKnob->getValueAtTime(time, 1); + Point offset; + offset.x = offsetKnob->getValueAtTime(time, 0); + offset.y = offsetKnob->getValueAtTime(time, 1); + + Point p; + p.x = searchWndBtmLeft->getValueAtTime(time, 0) + center.x + offset.x + delta.x; + p.y = searchWndTopRight->getValueAtTime(time, 1) + center.y + offset.y + delta.y; + + Point topLeft; + topLeft.x = patternCorners[0]->getValueAtTime(time, 0) + center.x + offset.x; + topLeft.y = patternCorners[0]->getValueAtTime(time, 1) + center.y + offset.y; + Point btmLeft; + btmLeft.x = patternCorners[1]->getValueAtTime(time, 0) + center.x + offset.x; + btmLeft.y = patternCorners[1]->getValueAtTime(time, 1) + center.y + offset.y; + Point btmRight; + btmRight.y = patternCorners[2]->getValueAtTime(time, 0) + center.x + offset.x; + btmRight.y = patternCorners[2]->getValueAtTime(time, 1) + center.y + offset.y; + Point topRight; + topRight.x = patternCorners[3]->getValueAtTime(time, 0) + center.x + offset.x; + topRight.y = patternCorners[3]->getValueAtTime(time, 1) + center.y + offset.y; + + // test every point: even topRight pattern corner may be on the left of topLeft + p.x = std::min(p.x, topLeft.x); + p.x = std::min(p.x, btmLeft.x); + p.x = std::min(p.x, btmRight.x); + p.x = std::min(p.x, topRight.x); + + p.y = std::max(p.y, topLeft.y); + p.y = std::max(p.y, btmLeft.y); + p.y = std::max(p.y, btmRight.y); + p.y = std::max(p.y, topRight.y); + + p.x -= (center.x + offset.x); + p.y -= (center.y + offset.y); + if ( searchWndBtmLeft->hasAnimation() ) { + searchWndBtmLeft->setValueAtTime(time, p.x, view, 0); + } else { + searchWndBtmLeft->setValue(p.x, view, 0); + } + if ( searchWndTopRight->hasAnimation() ) { + searchWndTopRight->setValueAtTime(time, p.y, view, 1); + } else { + searchWndTopRight->setValue(p.y, view, 1); + } + + updateSelectedMarkerTexture(); + didSomething = true; + break; + } + case eMouseStateDraggingInnerBtmMid: + case eMouseStateDraggingInnerTopMid: + case eMouseStateDraggingInnerMidLeft: + case eMouseStateDraggingInnerMidRight: + case eMouseStateDraggingOuterBtmMid: + case eMouseStateDraggingOuterTopMid: + case eMouseStateDraggingOuterMidLeft: + case eMouseStateDraggingOuterMidRight: { + _imp->transformPattern(time, _imp->eventState, delta); + didSomething = true; + break; + } + case eMouseStateDraggingSelectedMarkerResizeAnchor: { + QPointF lastPosWidget = viewer->toWidgetCoordinates(_imp->lastMousePos); + double dx = viewportPos.x() - lastPosWidget.x(); + _imp->selectedMarkerWidth += dx; + _imp->selectedMarkerWidth = std::max(_imp->selectedMarkerWidth, 10); + didSomething = true; + break; + } + case eMouseStateScalingSelectedMarker: { + TrackMarkerPtr marker = _imp->selectedMarker.lock(); + assert(marker); + RectD markerMagRect; + _imp->computeSelectedMarkerCanonicalRect(&markerMagRect); + boost::shared_ptr centerKnob = marker->getCenterKnob(); + boost::shared_ptr offsetKnob = marker->getOffsetKnob(); + boost::shared_ptr searchBtmLeft = marker->getSearchWindowBottomLeftKnob(); + boost::shared_ptr searchTopRight = marker->getSearchWindowTopRightKnob(); + Point center, offset, btmLeft, topRight; + center.x = centerKnob->getValueAtTime(time, 0); + center.y = centerKnob->getValueAtTime(time, 1); + offset.x = offsetKnob->getValueAtTime(time, 0); + offset.y = offsetKnob->getValueAtTime(time, 1); + btmLeft.x = searchBtmLeft->getValueAtTime(time, 0) + center.x + offset.x; + btmLeft.y = searchBtmLeft->getValueAtTime(time, 1) + center.y + offset.y; + topRight.x = searchTopRight->getValueAtTime(time, 0) + center.x + offset.x; + topRight.y = searchTopRight->getValueAtTime(time, 1) + center.y + offset.y; + + //Remove any offset to the center to see the marker in the magnification window + double xCenterPercent = (center.x - btmLeft.x + offset.x) / (topRight.x - btmLeft.x); + double yCenterPercent = (center.y - btmLeft.y + offset.y) / (topRight.y - btmLeft.y); + Point centerPoint; + centerPoint.x = markerMagRect.x1 + xCenterPercent * (markerMagRect.x2 - markerMagRect.x1); + centerPoint.y = markerMagRect.y1 + yCenterPercent * (markerMagRect.y2 - markerMagRect.y1); + + double prevDist = std::sqrt( (_imp->lastMousePos.x() - centerPoint.x ) * ( _imp->lastMousePos.x() - centerPoint.x) + ( _imp->lastMousePos.y() - centerPoint.y) * ( _imp->lastMousePos.y() - centerPoint.y) ); + if (prevDist != 0) { + double dist = std::sqrt( ( pos.x() - centerPoint.x) * ( pos.x() - centerPoint.x) + ( pos.y() - centerPoint.y) * ( pos.y() - centerPoint.y) ); + double ratio = dist / prevDist; + _imp->selectedMarkerScale.x *= ratio; + _imp->selectedMarkerScale.x = std::max( 0.05, std::min(1., _imp->selectedMarkerScale.x) ); + _imp->selectedMarkerScale.y = _imp->selectedMarkerScale.x; + didSomething = true; + } + break; + } + case eMouseStateDraggingSelectedMarker: { + double x = centerKnob->getValueAtTime(time, 0); + double y = centerKnob->getValueAtTime(time, 1); + double dx = delta.x * _imp->selectedMarkerScale.x; + double dy = delta.y * _imp->selectedMarkerScale.y; + x -= dx; + y -= dy; + centerKnob->setValuesAtTime(time, x, y, view, eValueChangedReasonPluginEdited); + for (int i = 0; i < 4; ++i) { + for (int d = 0; d < patternCorners[i]->getDimension(); ++d) { + patternCorners[i]->setValueAtTime(time, patternCorners[i]->getValueAtTime(time, d), view, d); + } + } + if ( _imp->createKeyOnMoveButton->isChecked() ) { + _imp->interactMarker->setUserKeyframe(time); + } + updateSelectedMarkerTexture(); + didSomething = true; + break; + } + default: + break; + } // switch + } + if (_imp->clickToAddTrackEnabled) { + ///Refresh the overlay + didSomething = true; + } + _imp->lastMousePos = pos; + + return didSomething; +#endif +} //penMotion + +bool +TrackerNode::onOverlayPenDoubleClicked(double /*time*/, + const RenderScale & /*renderScale*/, + ViewIdx /*view*/, + const QPointF & /*viewportPos*/, + const QPointF & /*pos*/) +{ + return false; +} + +bool +TrackerNode::onOverlayPenUp(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) +{ +#if 0 + bool didSomething = false; + + if (_imp->panel) { + if ( _imp->panel->getContext()->onOverlayPenUpInternalNodes( time, renderScale, view, viewportPos, pos, pressure, _imp->viewer->getViewer() ) ) { + return true; + } + } + + + TrackerMouseStateEnum state = _imp->eventState; + _imp->eventState = eMouseStateIdle; + if (_imp->panelv1) { + const std::list > & instances = _imp->panelv1->getInstances(); + + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + NodePtr instance = it->first.lock(); + if ( it->second && !instance->isNodeDisabled() ) { + EffectInstPtr effect = instance->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); + didSomething = effect->onOverlayPenUp_public(time, renderScale, view, viewportPos, pos, pressure); + if (didSomething) { + return true; + } + } + } + } else { // if (_imp->panelv1) { + _imp->interactMarker.reset(); + (void)state; + } // if (_imp->panelv1) { + + return didSomething; +#endif + +} // penUp + +bool +TrackerNode::onOverlayKeyDown(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) +{ +#if 0 + bool didSomething = false; + Qt::KeyboardModifiers modifiers = e->modifiers(); + Qt::Key key = (Qt::Key)e->key(); + Key natronKey = QtEnumConvert::fromQtKey(key); + KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers(modifiers); + + if (_imp->panel) { + if ( _imp->panel->getContext()->onOverlayKeyDownInternalNodes( time, renderScale, view, natronKey, natronMod, _imp->viewer->getViewer() ) ) { + return true; + } + } + + if (e->key() == Qt::Key_Control) { + ++_imp->controlDown; + } else if (e->key() == Qt::Key_Shift) { + ++_imp->shiftDown; + } + + + if (_imp->panelv1) { + const std::list > & instances = _imp->panelv1->getInstances(); + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + NodePtr instance = it->first.lock(); + if ( it->second && !instance->isNodeDisabled() ) { + EffectInstPtr effect = instance->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); + didSomething = effect->onOverlayKeyDown_public(time, renderScale, view, natronKey, natronMod); + if (didSomething) { + return true; + } + } + } + } + + if ( modCASIsControlAlt(e) && ( (e->key() == Qt::Key_Control) || (e->key() == Qt::Key_Alt) ) ) { + _imp->clickToAddTrackEnabled = true; + _imp->addTrackButton->setDown(true); + _imp->addTrackButton->setChecked(true); + didSomething = true; + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingSelectAll, modifiers, key) ) { + if (_imp->panelv1) { + _imp->panelv1->onSelectAllButtonClicked(); + std::list selectedInstances; + _imp->panelv1->getSelectedInstances(&selectedInstances); + didSomething = !selectedInstances.empty(); + } else { + _imp->panel->getContext()->selectAll(TrackerContext::eTrackSelectionInternal); + didSomething = false; //viewer is refreshed already + } + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingDelete, modifiers, key) ) { + if (_imp->panelv1) { + _imp->panelv1->onDeleteKeyPressed(); + std::list selectedInstances; + _imp->panelv1->getSelectedInstances(&selectedInstances); + didSomething = !selectedInstances.empty(); + } else { + _imp->panel->onRemoveButtonClicked(); + didSomething = true; + } + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingBackward, modifiers, key) ) { + onTrackBwClicked(); + didSomething = true; + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingPrevious, modifiers, key) ) { + onTrackPrevClicked(); + didSomething = true; + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingNext, modifiers, key) ) { + onTrackNextClicked(); + didSomething = true; + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingForward, modifiers, key) ) { + onTrackFwClicked(); + didSomething = true; + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingStop, modifiers, key) ) { + onStopButtonClicked(); + didSomething = true; + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingRange, modifiers, key) ) { + onTrackRangeClicked(); + didSomething = true; + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingAllKeyframes, modifiers, key) ) { + onTrackAllKeyframesClicked(); + didSomething = true; + } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingCurrentKeyframes, modifiers, key) ) { + onTrackCurrentKeyframeClicked(); + didSomething = true; + } + + + return didSomething; +#endif +} // keydown + +bool +TrackerNode::onOverlayKeyUp(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) +{ +#if 0 + Key natronKey = QtEnumConvert::fromQtKey( (Qt::Key)e->key() ); + KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers( e->modifiers() ); + + if (_imp->panel) { + if ( _imp->panel->getContext()->onOverlayKeyUpInternalNodes( time, renderScale, view, natronKey, natronMod, _imp->viewer->getViewer() ) ) { + return true; + } + } + + bool didSomething = false; + + if (e->key() == Qt::Key_Control) { + if (_imp->controlDown > 0) { + --_imp->controlDown; + } + } else if (e->key() == Qt::Key_Shift) { + if (_imp->shiftDown > 0) { + --_imp->shiftDown; + } + if (_imp->eventState == eMouseStateScalingSelectedMarker) { + _imp->eventState = eMouseStateIdle; + didSomething = true; + } + } + + if (_imp->panelv1) { + const std::list > & instances = _imp->panelv1->getInstances(); + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + NodePtr instance = it->first.lock(); + if ( it->second && !instance->isNodeDisabled() ) { + EffectInstPtr effect = instance->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); + didSomething = effect->onOverlayKeyUp_public(time, renderScale, view, natronKey, natronMod); + if (didSomething) { + return true; + } + } + } + } + if ( _imp->clickToAddTrackEnabled && ( (e->key() == Qt::Key_Control) || (e->key() == Qt::Key_Alt) ) ) { + _imp->clickToAddTrackEnabled = false; + _imp->addTrackButton->setDown(false); + _imp->addTrackButton->setChecked(false); + didSomething = true; + } + + return didSomething; +#endif +} // KeyUp + +bool +TrackerNode::onOverlayKeyRepeat(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) +{ + +} // keyrepeat + +bool +TrackerNode::onOverlayFocusGained(double time, const RenderScale & renderScale, ViewIdx view) +{ + +} // gainFocus + +bool +TrackerNode::onOverlayFocusLost(double time, const RenderScale & renderScale, ViewIdx view) +{ +#if 0 + _imp->controlDown = 0; + _imp->shiftDown = 0; + + if (_imp->panelv1) { + const std::list > & instances = _imp->panelv1->getInstances(); + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + NodePtr instance = it->first.lock(); + if ( it->second && !instance->isNodeDisabled() ) { + EffectInstPtr effect = instance->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); + didSomething |= effect->onOverlayFocusLost_public(time, renderScale, view); + } + } + } + + return didSomething; +#endif +} // loseFocus + +void +TrackerNode::onInteractViewportSelectionCleared() +{ +#if 0 + if (_imp->panelv1) { + _imp->panelv1->clearSelection(); + } else { + _imp->panel->getContext()->clearSelection(TrackerContext::eTrackSelectionViewer); + } +#endif +} + +void +TrackerNode::onInteractViewportSelectionUpdated(const RectD& rectangle, bool onRelease) +{ +#if 0 + if (!onRelease) { + return; + } + + + double l, r, b, t; + _imp->viewer->getViewer()->getSelectionRectangle(l, r, b, t); + + if (_imp->panelv1) { + std::list currentSelection; + const std::list > & instances = _imp->panelv1->getInstances(); + for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { + NodePtr instance = it->first.lock(); + boost::shared_ptr newInstanceKnob = instance->getKnobByName("center"); + assert(newInstanceKnob); //< if it crashes here that means the parameter's name changed in the OpenFX plug-in. + KnobDouble* dblKnob = dynamic_cast( newInstanceKnob.get() ); + assert(dblKnob); + double x, y; + x = dblKnob->getValue(0); + y = dblKnob->getValue(1); + if ( (x >= l) && (x <= r) && (y >= b) && (y <= t) ) { + ///assert that the node is really not part of the selection + assert( std::find( currentSelection.begin(), currentSelection.end(), instance.get() ) == currentSelection.end() ); + currentSelection.push_back( instance.get() ); + } + } + _imp->panelv1->selectNodes( currentSelection, (_imp->controlDown > 0) ); + } else { + std::vector allMarkers; + std::list selectedMarkers; + boost::shared_ptr context = _imp->panel->getContext(); + context->getAllMarkers(&allMarkers); + for (std::size_t i = 0; i < allMarkers.size(); ++i) { + if ( !allMarkers[i]->isEnabled( allMarkers[i]->getCurrentTime() ) ) { + continue; + } + boost::shared_ptr center = allMarkers[i]->getCenterKnob(); + double x, y; + x = center->getValue(0); + y = center->getValue(1); + if ( (x >= l) && (x <= r) && (y >= b) && (y <= t) ) { + selectedMarkers.push_back(allMarkers[i]); + } + } + + context->beginEditSelection(TrackerContext::eTrackSelectionInternal); + context->clearSelection(TrackerContext::eTrackSelectionInternal); + context->addTracksToSelection(selectedMarkers, TrackerContext::eTrackSelectionInternal); + context->endEditSelection(TrackerContext::eTrackSelectionInternal); + } +#endif +} + +void +TrackerNode::refreshExtraStateAfterTimeChanged(double time) +{ +#if 0 + if ( _imp->showMarkerTexture && (reason != eTimelineChangeReasonPlaybackSeek) ) { + _imp->refreshSelectedMarkerTexture(); + } +#endif } NATRON_NAMESPACE_EXIT; diff --git a/Engine/TrackerNode.h b/Engine/TrackerNode.h index bfdfa38a2a..9e7c3c2221 100644 --- a/Engine/TrackerNode.h +++ b/Engine/TrackerNode.h @@ -27,12 +27,16 @@ #include "Engine/NodeGroup.h" + + + NATRON_NAMESPACE_ENTER; struct TrackerNodePrivate; class TrackerNode : public NodeGroup { + public: static EffectInstance* BuildEffect(boost::shared_ptr n) @@ -121,8 +125,33 @@ class TrackerNode virtual void onKnobsLoaded() OVERRIDE FINAL; + private: + virtual void getPluginShortcuts(std::list* shortcuts) OVERRIDE FINAL; + + virtual void drawOverlay(double time, const RenderScale & renderScale, ViewIdx view) OVERRIDE FINAL; + virtual bool onOverlayPenDown(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, PenType pen) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenMotion(double time, const RenderScale & renderScale, ViewIdx view, + const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenUp(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenDoubleClicked(double time, + const RenderScale & renderScale, + ViewIdx view, + const QPointF & viewportPos, + const QPointF & pos) OVERRIDE FINAL WARN_UNUSED_RETURN; + + + virtual bool onOverlayKeyDown(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; + virtual bool onOverlayKeyUp(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; + virtual bool onOverlayKeyRepeat(double time, const RenderScale & renderScale, ViewIdx view, Key key, KeyboardModifiers modifiers) OVERRIDE FINAL; + virtual bool onOverlayFocusGained(double time, const RenderScale & renderScale, ViewIdx view) OVERRIDE FINAL; + virtual bool onOverlayFocusLost(double time, const RenderScale & renderScale, ViewIdx view) OVERRIDE FINAL; + + virtual void onInteractViewportSelectionCleared() OVERRIDE FINAL; + + virtual void onInteractViewportSelectionUpdated(const RectD& rectangle, bool onRelease) OVERRIDE FINAL; + virtual void knobChanged(KnobI* k, ValueChangedReasonEnum reason, @@ -130,6 +159,8 @@ class TrackerNode double time, bool originatedFromMainThread) OVERRIDE FINAL; + virtual void refreshExtraStateAfterTimeChanged(double time) OVERRIDE FINAL; + private: boost::scoped_ptr _imp; diff --git a/Engine/TrackerNodeInteract.cpp b/Engine/TrackerNodeInteract.cpp new file mode 100644 index 0000000000..7a9a6d756d --- /dev/null +++ b/Engine/TrackerNodeInteract.cpp @@ -0,0 +1,1798 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * This file is part of Natron , + * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat + * + * Natron is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Natron is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Natron. If not, see + * ***** END LICENSE BLOCK ***** */ + +// ***** BEGIN PYTHON BLOCK ***** +// from : +// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." +#include +// ***** END PYTHON BLOCK ***** + + +#include "TrackerNodeInteract.h" + +#include "Engine/Image.h" +#include "Engine/Lut.h" +#include "Engine/TrackerNode.h" +#include "Engine/TrackerContext.h" +#include "Engine/TrackMarker.h" + +NATRON_NAMESPACE_ENTER; + +TrackerNodePrivate::TrackerNodePrivate(TrackerNode* publicInterface) +: publicInterface(publicInterface) +, ui(new TrackerNodeInteract(this)) +{ + +} + +TrackerNodePrivate::~TrackerNodePrivate() +{ + +} + +TrackerNodeInteract::TrackerNodeInteract(TrackerNodePrivate* p) +: _p(p) +, addTrackButton() +, trackRangeButton() +, trackBwButton() +, trackPrevButton() +, trackNextButton() +, trackFwButton() +, trackAllKeyframesButton() +, trackCurrentKeyframeButton() +, clearAllAnimationButton() +, clearBwAnimationButton() +, clearFwAnimationButton() +, updateViewerButton() +, centerViewerButton() +, createKeyOnMoveButton() +, setKeyFrameButton() +, removeKeyFrameButton() +, resetOffsetButton() +, resetTrackButton() +, showCorrelationButton() +, clickToAddTrackEnabled(false) +, lastMousePos() +, selectionRectangle() +, controlDown(0) +, shiftDown(0) +, eventState(eMouseStateIdle) +, hoverState(eDrawStateInactive) +, interactMarker() +, trackTextures() +, trackRequestsMap() +, selectedMarkerTexture() +, selectedMarkerTextureTime(0) +, selectedMarkerTextureRoI() +, selectedMarker() +, pboID(0) +, selectedMarkerWidth(SELECTED_MARKER_WINDOW_BASE_WIDTH_SCREEN_PX) +, imageGetterWatcher() +, showMarkerTexture(false) +, selectedMarkerScale() +, selectedMarkerImg() +, isTracking(false) +, lastTrackRangeFirstFrame(INT_MIN) +, lastTrackRangeLastFrame(INT_MIN) +, lastTrackRangeStep(INT_MIN) +{ + glGenBuffers(1, &pboID); + selectedMarkerScale.x = selectedMarkerScale.y = 1.; + QObject::connect( p->publicInterface->getNode().get(), SIGNAL(s_refreshPreviewsAfterProjectLoadRequested()), this, SLOT(rebuildMarkerTextures()) ); +} + + +TrackerNodeInteract::~TrackerNodeInteract() +{ + glDeleteBuffers(1, &pboID); +} + +void +TrackerNodeInteract::onTrackRangeClicked() +{ +#if 0 + SequenceTime timelineFirst, timelineLast; + + _imp->viewer->getTimelineBounds(&timelineFirst, &timelineLast); + + NATRON_PYTHON_NAMESPACE::PyModalDialog dialog( _imp->viewer->getGui() ); + boost::shared_ptr firstFrame( dialog.createIntParam( QString::fromUtf8("firstFrame"), QString::fromUtf8("First frame") ) ); + firstFrame->set(_imp->lastTrackRangeFirstFrame != INT_MIN ? _imp->lastTrackRangeFirstFrame : timelineFirst); + firstFrame->setAnimationEnabled(false); + boost::shared_ptr lastFrame( dialog.createIntParam( QString::fromUtf8("lastFrame"), QString::fromUtf8("Last frame") ) ); + lastFrame->set(_imp->lastTrackRangeLastFrame != INT_MIN ? _imp->lastTrackRangeLastFrame : timelineLast); + lastFrame->setAnimationEnabled(false); + boost::shared_ptr stepFrame( dialog.createIntParam( QString::fromUtf8("step"), QString::fromUtf8("Step") ) ); + stepFrame->setAnimationEnabled(false); + stepFrame->set(_imp->lastTrackRangeStep != INT_MIN ? _imp->lastTrackRangeStep : 1); + dialog.refreshUserParamsGUI(); + if ( dialog.exec() ) { + int first = firstFrame->getValue(); + int last = lastFrame->getValue(); + int step = stepFrame->getValue(); + boost::shared_ptr ctx = _imp->panel->getContext(); + if ( ctx->isCurrentlyTracking() ) { + ctx->abortTracking(); + } + + if (step == 0) { + Dialogs::errorDialog( tr("Track Range").toStdString(), tr("The Step cannot be 0").toStdString() ); + + return; + } + + int startFrame = step > 0 ? first : last; + int lastFrame = step > 0 ? last + 1 : first - 1; + + if ( ( (step > 0) && (startFrame >= lastFrame) ) || ( (step < 0) && (startFrame <= lastFrame) ) ) { + return; + } + + _imp->lastTrackRangeStep = step; + _imp->lastTrackRangeFirstFrame = first; + _imp->lastTrackRangeLastFrame = last; + + ctx->trackSelectedMarkers( startFrame, lastFrame, step, _imp->viewer->getInternalNode() ); + } +#endif +} + +void +TrackerNodeInteract::onTrackAllKeyframesClicked() +{ +#if 0 + boost::shared_ptr ctx = _imp->panel->getContext(); + std::list selectedMarkers; + + ctx->getSelectedMarkers(&selectedMarkers); + + std::set userKeys; + + for (std::list::iterator it = selectedMarkers.begin(); it != selectedMarkers.end(); ++it) { + std::set trackUserKeys; + (*it)->getUserKeyframes(&trackUserKeys); + userKeys.insert( trackUserKeys.begin(), trackUserKeys.end() ); + } + if ( userKeys.empty() ) { + return; + } + + int first = *userKeys.begin(); + int last = *userKeys.rbegin() + 1; + ctx->trackSelectedMarkers( first, last, 1, _imp->viewer->getInternalNode() ); +#endif +} + +void +TrackerNodeInteract::onTrackCurrentKeyframeClicked() +{ +#if 0 + boost::shared_ptr ctx = _imp->panel->getContext(); + SequenceTime currentFrame = _imp->viewer->getTimeLine()->currentFrame(); + std::list selectedMarkers; + + ctx->getSelectedMarkers(&selectedMarkers); + + std::set userKeys; + + for (std::list::iterator it = selectedMarkers.begin(); it != selectedMarkers.end(); ++it) { + std::set trackUserKeys; + (*it)->getUserKeyframes(&trackUserKeys); + userKeys.insert( trackUserKeys.begin(), trackUserKeys.end() ); + } + if ( userKeys.empty() ) { + return; + } + + std::set::iterator it = userKeys.lower_bound(currentFrame); + if ( it == userKeys.end() ) { + return; + } + + int last = *it + 1; + int first; + if ( it == userKeys.begin() ) { + first = *it; + } else { + --it; + first = *it; + } + + ctx->trackSelectedMarkers( first, last, 1, _imp->viewer->getInternalNode() ); +#endif +} + +void +TrackerNodeInteract::onTrackBwClicked() +{ +#if 0 + _imp->trackBwButton->setDown(false); + _imp->trackBwButton->setChecked(false); + + if (_imp->panelv1) { + if ( _imp->panelv1->isTracking() ) { + _imp->panelv1->stopTracking(); + + return; + } + if ( !_imp->panelv1->trackBackward( _imp->viewer->getInternalNode() ) ) { + _imp->panelv1->stopTracking(); + } + } else { + boost::shared_ptr timeline = _imp->viewer->getGui()->getApp()->getTimeLine(); + int startFrame = timeline->currentFrame(); + SequenceTime first, last; + _imp->viewer->getTimelineBounds(&first, &last); + boost::shared_ptr ctx = _imp->panel->getContext(); + if ( ctx->isCurrentlyTracking() ) { + ctx->abortTracking(); + } else { + ctx->trackSelectedMarkers( startFrame, first - 1, false, _imp->viewer->getInternalNode() ); + } + } +#endif +} + +void +TrackerNodeInteract::onTrackPrevClicked() +{ +#if 0 + if (_imp->panelv1) { + _imp->panelv1->trackPrevious( _imp->viewer->getInternalNode() ); + } else { + boost::shared_ptr timeline = _imp->viewer->getGui()->getApp()->getTimeLine(); + int startFrame = timeline->currentFrame(); + boost::shared_ptr ctx = _imp->panel->getContext(); + ctx->trackSelectedMarkers( startFrame, startFrame - 2, false, _imp->viewer->getInternalNode() ); + } +#endif +} + +void +TrackerNodeInteract::onStopButtonClicked() +{ +#if 0 + _imp->trackBwButton->setDown(false); + _imp->trackFwButton->setDown(false); + if (_imp->panelv1) { + _imp->panelv1->stopTracking(); + } else { + _imp->panel->getContext()->abortTracking(); + } +#endif +} + +void +TrackerNodeInteract::onTrackNextClicked() +{ +#if 0 + if (_imp->panelv1) { + _imp->panelv1->trackNext( _imp->viewer->getInternalNode() ); + } else { + int startFrame = _imp->viewer->getGui()->getApp()->getTimeLine()->currentFrame(); + boost::shared_ptr ctx = _imp->panel->getContext(); + ctx->trackSelectedMarkers( startFrame, startFrame + 2, true, _imp->viewer->getInternalNode() ); + } +#endif +} + +void +TrackerNodeInteract::onTrackFwClicked() +{ +#if 0 + _imp->trackFwButton->setDown(false); + _imp->trackFwButton->setChecked(false); + if (_imp->panelv1) { + if ( _imp->panelv1->isTracking() ) { + _imp->panelv1->stopTracking(); + + return; + } + + if ( !_imp->panelv1->trackForward( _imp->viewer->getInternalNode() ) ) { + _imp->panelv1->stopTracking(); + } + } else { + boost::shared_ptr timeline = _imp->viewer->getGui()->getApp()->getTimeLine(); + int startFrame = timeline->currentFrame(); + SequenceTime first, last; + _imp->viewer->getTimelineBounds(&first, &last); + boost::shared_ptr ctx = _imp->panel->getContext(); + if ( ctx->isCurrentlyTracking() ) { + ctx->abortTracking(); + } else { + ctx->trackSelectedMarkers( startFrame, last + 1, true, _imp->viewer->getInternalNode() ); + } + } +#endif +} + +void +TrackerNodeInteract::onUpdateViewerClicked(bool clicked) +{ +#if 0 + _imp->updateViewerButton->setDown(clicked); + _imp->updateViewerButton->setChecked(clicked); + if (_imp->panelv1) { + _imp->panelv1->setUpdateViewer(clicked); + } else { + _imp->panel->getContext()->setUpdateViewer(clicked); + } +#endif +} + + +void +TrackerNodeInteract::onAddTrackClicked(bool clicked) +{ +#if 0 + _imp->clickToAddTrackEnabled = !_imp->clickToAddTrackEnabled; + _imp->addTrackButton->setDown(clicked); + _imp->addTrackButton->setChecked(clicked); + _imp->viewer->getViewer()->redraw(); +#endif +} + + +void +TrackerNodeInteract::onClearAllAnimationClicked() +{ + #if 0 + if (_imp->panelv1) { + _imp->panelv1->clearAllAnimationForSelection(); + } else { + std::list markers; + _imp->panel->getContext()->getSelectedMarkers(&markers); + for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { + (*it)->clearAnimation(); + } + } + #endif +} + +void +TrackerNodeInteract::onClearBwAnimationClicked() +{ + #if 0 + if (_imp->panelv1) { + _imp->panelv1->clearBackwardAnimationForSelection(); + } else { + int time = _imp->panel->getContext()->getNode()->getApp()->getTimeLine()->currentFrame(); + std::list markers; + _imp->panel->getContext()->getSelectedMarkers(&markers); + for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { + (*it)->clearAnimationBeforeTime(time); + } + } + #endif +} + +void +TrackerNodeInteract::onClearFwAnimationClicked() +{ + #if 0 + if (_imp->panelv1) { + _imp->panelv1->clearForwardAnimationForSelection(); + } else { + int time = _imp->panel->getContext()->getNode()->getApp()->getTimeLine()->currentFrame(); + std::list markers; + _imp->panel->getContext()->getSelectedMarkers(&markers); + for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { + (*it)->clearAnimationAfterTime(time); + } + } + #endif +} + +void +TrackerNodeInteract::onCreateKeyOnMoveButtonClicked(bool clicked) +{ + #if 0 + _imp->createKeyOnMoveButton->setDown(clicked); + _imp->createKeyOnMoveButton->setChecked(clicked); + #endif +} + +void +TrackerNodeInteract::onShowCorrelationButtonClicked(bool clicked) +{ + #if 0 + _imp->showCorrelationButton->setDown(clicked); + _imp->viewer->getViewer()->redraw(); + #endif +} + +void +TrackerNodeInteract::onCenterViewerButtonClicked(bool clicked) +{ + #if 0 + _imp->centerViewerButton->setDown(clicked); + if (_imp->panelv1) { + _imp->panelv1->setCenterOnTrack(clicked); + } else { + _imp->panel->getContext()->setCenterOnTrack(clicked); + } + #endif +} + +void +TrackerNodeInteract::onSetKeyframeButtonClicked() +{ + #if 0 + int time = _imp->panel->getNode()->getNode()->getApp()->getTimeLine()->currentFrame(); + std::list markers; + + _imp->panel->getContext()->getSelectedMarkers(&markers); + for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { + (*it)->setUserKeyframe(time); + } + #endif +} + +void +TrackerNodeInteract::onRemoveKeyframeButtonClicked() +{ + #if 0 + int time = _imp->panel->getNode()->getNode()->getApp()->getTimeLine()->currentFrame(); + std::list markers; + + _imp->panel->getContext()->getSelectedMarkers(&markers); + for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { + (*it)->removeUserKeyframe(time); + } + #endif +} + +void +TrackerNodeInteract::onResetOffsetButtonClicked() +{ + #if 0 + std::list markers; + + _imp->panel->getContext()->getSelectedMarkers(&markers); + for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { + boost::shared_ptr offsetKnob = (*it)->getOffsetKnob(); + assert(offsetKnob); + for (int i = 0; i < offsetKnob->getDimension(); ++i) { + offsetKnob->resetToDefaultValue(i); + } + } + _imp->viewer->getGui()->redrawAllViewers(); + #endif +} + +void +TrackerNodeInteract::onResetTrackButtonClicked() +{ + #if 0 + _imp->panel->onResetButtonClicked(); +#endif +} + + + +QPointF +TrackerNodeInteract::computeMidPointExtent(const QPointF& prev, + const QPointF& next, + const QPointF& point, + const QPointF& handleSize) +{ +#if 0 + Point leftDeriv, rightDeriv; + + leftDeriv.x = prev.x() - point.x(); + leftDeriv.y = prev.y() - point.y(); + + rightDeriv.x = next.x() - point.x(); + rightDeriv.y = next.y() - point.y(); + double derivNorm = std::sqrt( (rightDeriv.x - leftDeriv.x) * (rightDeriv.x - leftDeriv.x) + (rightDeriv.y - leftDeriv.y) * (rightDeriv.y - leftDeriv.y) ); + QPointF ret; + if (derivNorm == 0) { + double norm = std::sqrt( ( leftDeriv.x - point.x() ) * ( leftDeriv.x - point.x() ) + ( leftDeriv.y - point.y() ) * ( leftDeriv.y - point.y() ) ); + if (norm != 0) { + ret.rx() = point.x() + ( ( leftDeriv.y - point.y() ) / norm ) * handleSize.x(); + ret.ry() = point.y() - ( ( leftDeriv.x - point.x() ) / norm ) * handleSize.y(); + + return ret; + } else { + return QPointF(0, 0); + } + } else { + ret.rx() = point.x() + ( (rightDeriv.y - leftDeriv.y) / derivNorm ) * handleSize.x(); + ret.ry() = point.y() - ( (rightDeriv.x - leftDeriv.x) / derivNorm ) * handleSize.y(); + } + + return ret; +#endif +} + + +int +TrackerNodeInteract::isInsideKeyFrameTexture(double currentTime, + const QPointF& pos, + const QPointF& viewportPos) const +{ + #if 0 + if (!showMarkerTexture) { + return INT_MAX; + } + + + RectD textureRectCanonical; + if (selectedMarkerTexture) { + computeSelectedMarkerCanonicalRect(&textureRectCanonical); + } + + if ( (pos.y() < textureRectCanonical.y1) || (pos.y() > textureRectCanonical.y2) ) { + return INT_MAX; + } + if (pos.x() < textureRectCanonical.x2) { + return INT_MAX; + } + + TrackMarkerPtr marker = selectedMarker.lock(); + if (!marker) { + return INT_MAX; + } + + //Find out which keyframe it is by counting keyframe portions + int xRightMainTexture = viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x2, 0) ).x(); + const double keyWidthpx = TO_DPIX(SELECTED_MARKER_KEYFRAME_WIDTH_SCREEN_PX); + double indexF = (viewportPos.x() - xRightMainTexture) / keyWidthpx; + int texIndex = (int)std::floor(indexF); + + for (TrackKeysMap::const_iterator it = trackTextures.begin(); it != trackTextures.end(); ++it) { + if (it->first.lock() == marker) { + if ( it->second.empty() ) { + break; + } + ///Render at most MAX_TRACK_KEYS_TO_DISPLAY keyframes + KeyFrameTexIDs keysToRender = getKeysToRenderForMarker(currentTime, it->second); + if ( (texIndex < 0) || ( texIndex >= (int)keysToRender.size() ) ) { + return INT_MAX; + } + KeyFrameTexIDs::iterator found = keysToRender.begin(); + std::advance(found, texIndex); + RectD texCanonicalRect; + computeTextureCanonicalRect(*found->second, indexF * keyWidthpx + xRightMainTexture, + keyWidthpx, &texCanonicalRect); + + if ( (pos.y() >= texCanonicalRect.y1) && (pos.y() < texCanonicalRect.y2) ) { + return found->first; + } + break; + } + } + + return INT_MAX; +#endif +} // isInsideKeyFrameTexture + +bool +TrackerNodeInteract::isNearbySelectedMarkerTextureResizeAnchor(const QPointF& pos) const +{ +#if 0 + RectD textureRectCanonical; + + computeSelectedMarkerCanonicalRect(&textureRectCanonical); + + QPointF clickWidget = viewer->getViewer()->toWidgetCoordinates(pos); + QPointF btmRightWidget = viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x2, textureRectCanonical.y1) ); + double tolerance = TO_DPIX(POINT_TOLERANCE); + if ( ( clickWidget.x() >= (btmRightWidget.x() - tolerance) ) && ( clickWidget.x() <= (btmRightWidget.x() + tolerance) ) && + ( clickWidget.y() >= (btmRightWidget.y() - tolerance) ) && ( clickWidget.y() <= (btmRightWidget.y() + tolerance) ) ) { + return true; + } + + return false; +#endif +} + +bool +TrackerNodeInteract::isInsideSelectedMarkerTextureResizeAnchor(const QPointF& pos) const +{ + #if 0 + RectD textureRectCanonical; + + computeSelectedMarkerCanonicalRect(&textureRectCanonical); + + QPointF clickWidget = viewer->getViewer()->toWidgetCoordinates(pos); + QPointF btmRightWidget = viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x2, textureRectCanonical.y1) ); + QPointF topLeftWidget = viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x1, textureRectCanonical.y2) ); + RectD rect; + rect.x1 = topLeftWidget.x(); + rect.y1 = topLeftWidget.y(); + rect.x2 = btmRightWidget.x(); + rect.y2 = btmRightWidget.y(); + + return rect.contains( clickWidget.x(), clickWidget.y() ); +#endif +} + +void +TrackerNodeInteract::computeTextureCanonicalRect(const Texture& tex, + int xOffset, + int texWidthPx, + RectD* rect) const +{ + #if 0 + ///Preserve width + double par = tex.w() / (double)tex.h(); + + rect->x2 = viewer->getViewer()->toZoomCoordinates( QPointF(xOffset + texWidthPx, 0.) ).x(); + QPointF topLeft = viewer->getViewer()->toZoomCoordinates( QPointF(xOffset, 0.) ); + rect->x1 = topLeft.x(); + rect->y2 = topLeft.y(); + double height = rect->width() / par; + rect->y1 = rect->y2 - height; +#endif +} + +void +TrackerNodeInteract::computeSelectedMarkerCanonicalRect(RectD* rect) const +{ + #if 0 + assert(selectedMarkerTexture); + computeTextureCanonicalRect(*selectedMarkerTexture, 0, selectedMarkerWidth, rect); +#endif +} + +Point +TrackerNodeInteract::toMagWindowPoint(const Point& ptnPoint, + const RectD& canonicalSearchWindow, + const RectD& textureRectCanonical) +{ + #if 0 + Point ret; + double xCenterPercent = (ptnPoint.x - canonicalSearchWindow.x1) / (canonicalSearchWindow.x2 - canonicalSearchWindow.x1); + double yCenterPercent = (ptnPoint.y - canonicalSearchWindow.y1) / (canonicalSearchWindow.y2 - canonicalSearchWindow.y1); + + ret.y = textureRectCanonical.y1 + yCenterPercent * (textureRectCanonical.y2 - textureRectCanonical.y1); + ret.x = textureRectCanonical.x1 + xCenterPercent * (textureRectCanonical.x2 - textureRectCanonical.x1); + + return ret; +#endif +} + +void +TrackerNodeInteract::drawEllipse(double x, + double y, + double radiusX, + double radiusY, + int l, + double r, + double g, + double b, + double a) +{ + glColor3f(r * l * a, g * l * a, b * l * a); + + glPushMatrix(); + // center the oval at x_center, y_center + glTranslatef( (float)x, (float)y, 0.f ); + // draw the oval using line segments + glBegin(GL_LINE_LOOP); + // we don't need to be pixel-perfect here, it's just an interact! + // 40 segments is enough. + double m = 2 * 3.14159265358979323846264338327950288419717 / 40.; + for (int i = 0; i < 40; ++i) { + double theta = i * m; + glVertex2d( radiusX * std::cos(theta), radiusY * std::sin(theta) ); + } + glEnd(); + + glPopMatrix(); +} + +TrackerNodeInteract::KeyFrameTexIDs +TrackerNodeInteract::getKeysToRenderForMarker(double currentTime, + const KeyFrameTexIDs& allKeys) +{ + #if 0 + KeyFrameTexIDs keysToRender; + ///Find the first key equivalent to currentTime or after + KeyFrameTexIDs::const_iterator lower = allKeys.lower_bound(currentTime); + KeyFrameTexIDs::const_iterator prev = lower; + + if ( lower != allKeys.begin() ) { + --prev; + } else { + prev = allKeys.end(); + } + + for (int i = 0; i < MAX_TRACK_KEYS_TO_DISPLAY; ++i) { + if ( lower != allKeys.end() ) { + keysToRender.insert(*lower); + ++lower; + } + if (i == MAX_TRACK_KEYS_TO_DISPLAY) { + break; + } + if ( prev != allKeys.end() ) { + keysToRender.insert(*prev); + if ( prev != allKeys.begin() ) { + --prev; + } else { + prev = allKeys.end(); + } + } else { + if ( lower == allKeys.end() ) { + ///No more keyframes + break; + } + } + } + + return keysToRender; +#endif +} + +void +TrackerNodeInteract::drawSelectedMarkerKeyframes(const std::pair& pixelScale, + int currentTime) +{ +#if 0 + TrackMarkerPtr marker = selectedMarker.lock(); + + assert(marker); + if (!marker) { + return; + } + if ( !marker->isEnabled(currentTime) ) { + return; + } + boost::shared_ptr centerKnob = marker->getCenterKnob(); + boost::shared_ptr offsetKnob = marker->getOffsetKnob(); + boost::shared_ptr errorKnob = marker->getErrorKnob(); + boost::shared_ptr ptnTopLeft = marker->getPatternTopLeftKnob(); + boost::shared_ptr ptnTopRight = marker->getPatternTopRightKnob(); + boost::shared_ptr ptnBtmRight = marker->getPatternBtmRightKnob(); + boost::shared_ptr ptnBtmLeft = marker->getPatternBtmLeftKnob(); + boost::shared_ptr searchWndBtmLeft = marker->getSearchWindowBottomLeftKnob(); + boost::shared_ptr searchWndTopRight = marker->getSearchWindowTopRightKnob(); + const QFont& font = viewer->font(); + QFontMetrics fm(font); + int fontHeight = fm.height(); + double xOffsetPixels = selectedMarkerWidth; + QPointF viewerTopLeftCanonical = viewer->getViewer()->toZoomCoordinates( QPointF(0, 0.) ); + + + for (TrackKeysMap::iterator it = trackTextures.begin(); it != trackTextures.end(); ++it) { + if (it->first.lock() == marker) { + if ( it->second.empty() ) { + break; + } + ///Render at most MAX_TRACK_KEYS_TO_DISPLAY keyframes + KeyFrameTexIDs keysToRender = getKeysToRenderForMarker(currentTime, it->second); + + for (KeyFrameTexIDs::const_iterator it2 = keysToRender.begin(); it2 != keysToRender.end(); ++it2) { + double time = (double)it2->first; + Point offset, center, topLeft, topRight, btmRight, btmLeft; + + center.x = centerKnob->getValueAtTime(time, 0); + center.y = centerKnob->getValueAtTime(time, 1); + offset.x = offsetKnob->getValueAtTime(time, 0); + offset.y = offsetKnob->getValueAtTime(time, 1); + + topLeft.x = ptnTopLeft->getValueAtTime(time, 0) + offset.x + center.x; + topLeft.y = ptnTopLeft->getValueAtTime(time, 1) + offset.y + center.y; + + topRight.x = ptnTopRight->getValueAtTime(time, 0) + offset.x + center.x; + topRight.y = ptnTopRight->getValueAtTime(time, 1) + offset.y + center.y; + + btmRight.x = ptnBtmRight->getValueAtTime(time, 0) + offset.x + center.x; + btmRight.y = ptnBtmRight->getValueAtTime(time, 1) + offset.y + center.y; + + btmLeft.x = ptnBtmLeft->getValueAtTime(time, 0) + offset.x + center.x; + btmLeft.y = ptnBtmLeft->getValueAtTime(time, 1) + offset.y + center.y; + + //const double searchLeft = searchWndBtmLeft->getValueAtTime(time, 0) + offset.x + center.x; + //const double searchRight = searchWndTopRight->getValueAtTime(time, 0) + offset.x + center.x; + //const double searchBottom = searchWndBtmLeft->getValueAtTime(time, 1) + offset.y + center.y; + //const double searchTop = searchWndTopRight->getValueAtTime(time, 1) + offset.y + center.y; + + const TextureRect& texRect = it2->second->getTextureRect(); + if (texRect.height() <= 0) { + continue; + } + double par = texRect.width() / (double)texRect.height(); + RectD textureRectCanonical; + + textureRectCanonical.x2 = viewer->getViewer()->toZoomCoordinates( QPointF(TO_DPIX(SELECTED_MARKER_KEYFRAME_WIDTH_SCREEN_PX) + xOffsetPixels, 0.) ).x(); + textureRectCanonical.x1 = viewer->getViewer()->toZoomCoordinates( QPointF(xOffsetPixels, 0.) ).x(); + textureRectCanonical.y2 = viewerTopLeftCanonical.y(); + double height = textureRectCanonical.width() / par; + textureRectCanonical.y1 = textureRectCanonical.y2 - height; + + + RectD canonicalSearchWindow; + texRect.toCanonical_noClipping(0, texRect.par, &canonicalSearchWindow); + + //Remove any offset to the center to see the marker in the magnification window + double xCenterPercent = (center.x - canonicalSearchWindow.x1 + offset.x) / (canonicalSearchWindow.x2 - canonicalSearchWindow.x1); + double yCenterPercent = (center.y - canonicalSearchWindow.y1 + offset.y) / (canonicalSearchWindow.y2 - canonicalSearchWindow.y1); + Point centerPointCanonical; + centerPointCanonical.y = textureRectCanonical.y1 + yCenterPercent * (textureRectCanonical.y2 - textureRectCanonical.y1); + centerPointCanonical.x = textureRectCanonical.x1 + xCenterPercent * (textureRectCanonical.x2 - textureRectCanonical.x1); + + + Point innerTopLeft = toMagWindowPoint(topLeft, canonicalSearchWindow, textureRectCanonical); + Point innerTopRight = toMagWindowPoint(topRight, canonicalSearchWindow, textureRectCanonical); + Point innerBtmLeft = toMagWindowPoint(btmLeft, canonicalSearchWindow, textureRectCanonical); + Point innerBtmRight = toMagWindowPoint(btmRight, canonicalSearchWindow, textureRectCanonical); + + //Map texture + glColor4f(1., 1., 1., 1.); + glEnable(GL_TEXTURE_2D); + glBindTexture( GL_TEXTURE_2D, it2->second->getTexID() ); + glBegin(GL_POLYGON); + glTexCoord2d(0, 0); glVertex2d(textureRectCanonical.x1, textureRectCanonical.y1); + glTexCoord2d(0, 1); glVertex2d(textureRectCanonical.x1, textureRectCanonical.y2); + glTexCoord2d(1, 1); glVertex2d(textureRectCanonical.x2, textureRectCanonical.y2); + glTexCoord2d(1, 0); glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + + QPointF textPos = viewer->getViewer()->toZoomCoordinates( QPointF(xOffsetPixels + 5, fontHeight + 5 ) ); + viewer->getViewer()->renderText(textPos.x(), textPos.y(), QString::fromUtf8( marker->getLabel().c_str() ), QColor(200, 200, 200), font); + + QPointF framePos = viewer->getViewer()->toZoomCoordinates( QPointF( xOffsetPixels + 5, viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x1, textureRectCanonical.y1) ).y() ) ); + QString frameText = _publicInterface->tr("Frame"); + frameText.append( QString::fromUtf8(" ") + QString::number(it2->first) ); + viewer->getViewer()->renderText(framePos.x(), framePos.y(), frameText, QColor(200, 200, 200), font); + + //Draw contour + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + if (time == currentTime) { + glColor4f(0.93, 0.54, 0, 1); + } else { + KeyFrameTexIDs::const_iterator next = it2; + ++next; + KeyFrameTexIDs::const_iterator prev = it2; + if ( prev != keysToRender.begin() ) { + --prev; + } else { + prev = keysToRender.end(); + } + if ( ( next == keysToRender.end() ) && (time < currentTime) ) { + //Beyond the last keyframe + glColor4f(0.93, 0.54, 0, 1); + } else if ( ( prev == keysToRender.end() ) && (time > currentTime) ) { + //Before the first keyframe + glColor4f(0.93, 0.54, 0, 1); + } else { + if (time < currentTime) { + assert( next != keysToRender.end() ); + if (next->first > currentTime) { + glColor4f(1, 0.75, 0.47, 1); + } else { + glColor4f(1., 1., 1., 0.5); + } + } else { + //time > currentTime + assert( prev != keysToRender.end() ); + if (prev->first < currentTime) { + glColor4f(1, 0.75, 0.47, 1); + } else { + glColor4f(1., 1., 1., 0.5); + } + } + } + } + + glLineWidth(1.5); + glCheckError(); + glBegin(GL_LINE_LOOP); + glVertex2d(textureRectCanonical.x1, textureRectCanonical.y1); + glVertex2d(textureRectCanonical.x1, textureRectCanonical.y2); + glVertex2d(textureRectCanonical.x2, textureRectCanonical.y2); + glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); + glEnd(); + + glCheckError(); + + + //Draw internal marker + for (int l = 0; l < 2; ++l) { + // shadow (uses GL_PROJECTION) + glMatrixMode(GL_PROJECTION); + int direction = (l == 0) ? 1 : -1; + // translate (1,-1) pixels + glTranslated(direction * pixelScale.first / 256, -direction * pixelScale.second / 256, 0); + glMatrixMode(GL_MODELVIEW); + + glColor4f(0.8 * l, 0.8 * l, 0.8 * l, 1); + + glBegin(GL_LINE_LOOP); + glVertex2d(innerTopLeft.x, innerTopLeft.y); + glVertex2d(innerTopRight.x, innerTopRight.y); + glVertex2d(innerBtmRight.x, innerBtmRight.y); + glVertex2d(innerBtmLeft.x, innerBtmLeft.y); + glEnd(); + + glBegin(GL_POINTS); + glVertex2d(centerPointCanonical.x, centerPointCanonical.y); + glEnd(); + } + + xOffsetPixels += TO_DPIX(SELECTED_MARKER_KEYFRAME_WIDTH_SCREEN_PX); + } + break; + } + } +#endif +} // TrackerNodeInteract::drawSelectedMarkerKeyframes + +void +TrackerNodeInteract::drawSelectedMarkerTexture(const std::pair& pixelScale, + int currentTime, + const Point& ptnCenter, + const Point& offset, + const Point& ptnTopLeft, + const Point& ptnTopRight, + const Point& ptnBtmRight, + const Point& ptnBtmLeft, + const Point& /*selectedSearchWndBtmLeft*/, + const Point& /*selectedSearchWndTopRight*/) +{ +#if 0 + TrackMarkerPtr marker = selectedMarker.lock(); + + if ( isTracking || !selectedMarkerTexture || !marker || !marker->isEnabled(currentTime) || viewer->getInternalNode()->getRenderEngine()->isDoingSequentialRender() ) { + return; + } + + RectD textureRectCanonical; + computeSelectedMarkerCanonicalRect(&textureRectCanonical); + + + const TextureRect& texRect = selectedMarkerTexture->getTextureRect(); + RectD texCoords; + /*texCoords.x1 = (texRect.x1 - selectedMarkerTextureRoI.x1) / (double)selectedMarkerTextureRoI.width(); + texCoords.y1 = (texRect.y1 - selectedMarkerTextureRoI.y1) / (double)selectedMarkerTextureRoI.height(); + if (texRect.x2 <= selectedMarkerTextureRoI.x2) { + texCoords.x2 = (texRect.x2 - selectedMarkerTextureRoI.x1) / (double)selectedMarkerTextureRoI.width(); + } else { + texCoords.x2 = 1.; + } + if (texRect.y2 <= selectedMarkerTextureRoI.y2) { + texCoords.y2 = (texRect.y2 - selectedMarkerTextureRoI.y1) / (double)selectedMarkerTextureRoI.height(); + } else { + texCoords.y2 = 1.; + }*/ + texCoords.x1 = texCoords.y1 = 0.; + texCoords.x2 = texCoords.y2 = 1.; + + RectD canonicalSearchWindow; + texRect.toCanonical_noClipping(0, texRect.par, &canonicalSearchWindow); + + Point centerPoint, innerTopLeft, innerTopRight, innerBtmLeft, innerBtmRight; + + //Remove any offset to the center to see the marker in the magnification window + double xCenterPercent = (ptnCenter.x - canonicalSearchWindow.x1 + offset.x) / (canonicalSearchWindow.x2 - canonicalSearchWindow.x1); + double yCenterPercent = (ptnCenter.y - canonicalSearchWindow.y1 + offset.y) / (canonicalSearchWindow.y2 - canonicalSearchWindow.y1); + centerPoint.y = textureRectCanonical.y1 + yCenterPercent * (textureRectCanonical.y2 - textureRectCanonical.y1); + centerPoint.x = textureRectCanonical.x1 + xCenterPercent * (textureRectCanonical.x2 - textureRectCanonical.x1); + + + innerTopLeft = toMagWindowPoint(ptnTopLeft, canonicalSearchWindow, textureRectCanonical); + innerTopRight = toMagWindowPoint(ptnTopRight, canonicalSearchWindow, textureRectCanonical); + innerBtmLeft = toMagWindowPoint(ptnBtmLeft, canonicalSearchWindow, textureRectCanonical); + innerBtmRight = toMagWindowPoint(ptnBtmRight, canonicalSearchWindow, textureRectCanonical); + + Transform::Point3D btmLeftTex, topLeftTex, topRightTex, btmRightTex; + btmLeftTex.z = topLeftTex.z = topRightTex.z = btmRightTex.z = 1.; + btmLeftTex.x = texCoords.x1; btmLeftTex.y = texCoords.y1; + topLeftTex.x = texCoords.x1; topLeftTex.y = texCoords.y2; + topRightTex.x = texCoords.x2; topRightTex.y = texCoords.y2; + btmRightTex.x = texCoords.x2; btmRightTex.y = texCoords.y1; + Transform::Matrix3x3 m = Transform::matTransformCanonical(0, 0, selectedMarkerScale.x, selectedMarkerScale.y, 0, 0, false, 0, xCenterPercent, yCenterPercent); + btmLeftTex = Transform::matApply(m, btmLeftTex); + topLeftTex = Transform::matApply(m, topLeftTex); + btmRightTex = Transform::matApply(m, btmRightTex); + topRightTex = Transform::matApply(m, topRightTex); + + //Map texture + glColor4f(1., 1., 1., 1.); + glEnable(GL_TEXTURE_2D); + glBindTexture( GL_TEXTURE_2D, selectedMarkerTexture->getTexID() ); + glBegin(GL_POLYGON); + glTexCoord2d(btmLeftTex.x, btmRightTex.y); glVertex2d(textureRectCanonical.x1, textureRectCanonical.y1); + glTexCoord2d(topLeftTex.x, topLeftTex.y); glVertex2d(textureRectCanonical.x1, textureRectCanonical.y2); + glTexCoord2d(topRightTex.x, topRightTex.y); glVertex2d(textureRectCanonical.x2, textureRectCanonical.y2); + glTexCoord2d(btmRightTex.x, btmRightTex.y); glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, 0); + + //Draw contour + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor4f(1., 1., 1., 0.5); + glLineWidth(1.5); + glCheckError(); + glBegin(GL_LINE_LOOP); + glVertex2d(textureRectCanonical.x1, textureRectCanonical.y1); + glVertex2d(textureRectCanonical.x1, textureRectCanonical.y2); + glVertex2d(textureRectCanonical.x2, textureRectCanonical.y2); + glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); + glEnd(); + + glColor4f(0.8, 0.8, 0.8, 1.); + glPointSize(POINT_SIZE); + glBegin(GL_POINTS); + glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); + glEnd(); + glCheckError(); + + const QFont& font = viewer->font(); + QFontMetrics fm(font); + QPointF textPos = viewer->getViewer()->toZoomCoordinates( QPointF(5, fm.height() + 5) ); + viewer->getViewer()->renderText(textPos.x(), textPos.y(), QString::fromUtf8( marker->getLabel().c_str() ), QColor(200, 200, 200), font); + + //Draw internal marker + + for (int l = 0; l < 2; ++l) { + // shadow (uses GL_PROJECTION) + glMatrixMode(GL_PROJECTION); + int direction = (l == 0) ? 1 : -1; + // translate (1,-1) pixels + glTranslated(direction * pixelScale.first / 256, -direction * pixelScale.second / 256, 0); + glMatrixMode(GL_MODELVIEW); + + glColor4f(0.8 * l, 0.8 * l, 0.8 * l, 1); + + glBegin(GL_LINE_LOOP); + glVertex2d(innerTopLeft.x, innerTopLeft.y); + glVertex2d(innerTopRight.x, innerTopRight.y); + glVertex2d(innerBtmRight.x, innerBtmRight.y); + glVertex2d(innerBtmLeft.x, innerBtmLeft.y); + glEnd(); + + glBegin(GL_POINTS); + glVertex2d(centerPoint.x, centerPoint.y); + glEnd(); + + ///Draw ellipse if scaling + if ( (eventState == eMouseStateScalingSelectedMarker) || (hoverState == eDrawStateShowScalingHint) ) { + double ellipseColor[3]; + if (eventState == eMouseStateScalingSelectedMarker) { + ellipseColor[0] = 0.8; + ellipseColor[1] = 0.8; + ellipseColor[2] = 0.; + } else { + ellipseColor[0] = 0.8; + ellipseColor[1] = 0.8; + ellipseColor[2] = 0.8; + } + double rx = std::sqrt( (lastMousePos.x() - centerPoint.x) * (lastMousePos.x() - centerPoint.x) + (lastMousePos.y() - centerPoint.y) * (lastMousePos.y() - centerPoint.y) ); + double ry = rx; + drawEllipse(centerPoint.x, centerPoint.y, rx, ry, l, ellipseColor[0], ellipseColor[1], ellipseColor[2], 1.); + } + } + + ///Now draw keyframes + drawSelectedMarkerKeyframes(pixelScale, currentTime); +#endif +} // TrackerNodeInteract::drawSelectedMarkerTexture + +bool +TrackerNodeInteract::isNearbyPoint(const boost::shared_ptr& knob, + const std::pair& pixelScale, + double xWidget, + double yWidget, + double toleranceWidget, + double time) +{ +#if 0 + QPointF p; + + p.rx() = knob->getValueAtTime(time, 0); + p.ry() = knob->getValueAtTime(time, 1); + p = viewer->toWidgetCoordinates(p); + if ( ( p.x() <= (xWidget + toleranceWidget) ) && ( p.x() >= (xWidget - toleranceWidget) ) && + ( p.y() <= (yWidget + toleranceWidget) ) && ( p.y() >= (yWidget - toleranceWidget) ) ) { + return true; + } + + return false; +#endif +} + +bool +TrackerNodeInteract::isNearbyPoint(const QPointF& p, + const std::pair& pixelScale, + double xWidget, + double yWidget, + double toleranceWidget) +{ +#if 0 + QPointF pw = viewer->toWidgetCoordinates(p); + + if ( ( pw.x() <= (xWidget + toleranceWidget) ) && ( pw.x() >= (xWidget - toleranceWidget) ) && + ( pw.y() <= (yWidget + toleranceWidget) ) && ( pw.y() >= (yWidget - toleranceWidget) ) ) { + return true; + } + + return false; +#endif +} + +void +TrackerNodeInteract::findLineIntersection(const Point& p, + const Point& l1, + const Point& l2, + Point* inter) +{ + Point h, u; + double a; + + h.x = p.x - l1.x; + h.y = p.y - l1.y; + + u.x = l2.x - l1.x; + u.y = l2.y - l1.y; + + a = (u.x * h.x + u.y * h.y) / (u.x * u.x + u.y * u.y); + inter->x = l1.x + u.x * a; + inter->y = l1.y + u.y * a; +} + + +void +TrackerNodeInteract::refreshSelectedMarkerTexture() +{ +#if 0 + assert( QThread::currentThread() == qApp->thread() ); + if (isTracking) { + return; + } + TrackMarkerPtr marker = selectedMarker.lock(); + if (!marker) { + return; + } + + int time = panel->getNode()->getNode()->getApp()->getTimeLine()->currentFrame(); + RectI roi = marker->getMarkerImageRoI(time); + if ( roi.isNull() ) { + return; + } + ImagePtr existingMarkerImg = selectedMarkerImg.lock(); + if ( existingMarkerImg && (existingMarkerImg->getTime() == time) && (roi == selectedMarkerTextureRoI) ) { + return; + } + + selectedMarkerImg.reset(); + + imageGetterWatcher.reset( new TrackWatcher() ); + QObject::connect( imageGetterWatcher.get(), SIGNAL(finished()), _publicInterface, SLOT(onTrackImageRenderingFinished()) ); + imageGetterWatcher->setFuture( QtConcurrent::run(marker.get(), &TrackMarker::getMarkerImage, time, roi) ); +#endif +} + +void +TrackerNodeInteract::makeMarkerKeyTexture(int time, + const TrackMarkerPtr& track) +{ +#if 0 + assert( QThread::currentThread() == qApp->thread() ); + TrackRequestKey k; + k.time = time; + k.track = track; + k.roi = track->getMarkerImageRoI(time); + + TrackKeysMap::iterator foundTrack = trackTextures.find(track); + if ( foundTrack != trackTextures.end() ) { + KeyFrameTexIDs::iterator foundKey = foundTrack->second.find(k.time); + if ( foundKey != foundTrack->second.end() ) { + const TextureRect& texRect = foundKey->second->getTextureRect(); + if ( (k.roi.x1 == texRect.x1) && + ( k.roi.x2 == texRect.x2) && + ( k.roi.y1 == texRect.y1) && + ( k.roi.y2 == texRect.y2) ) { + return; + } + } + } + + if ( !k.roi.isNull() ) { + TrackWatcherPtr watcher( new TrackWatcher() ); + QObject::connect( watcher.get(), SIGNAL(finished()), _publicInterface, SLOT(onKeyFrameImageRenderingFinished()) ); + trackRequestsMap[k] = watcher; + watcher->setFuture( QtConcurrent::run(track.get(), &TrackMarker::getMarkerImage, time, k.roi) ); + } +#endif +} + + +static unsigned int toBGRA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) WARN_UNUSED_RETURN; +unsigned int +toBGRA(unsigned char r, + unsigned char g, + unsigned char b, + unsigned char a) +{ + return (a << 24) | (r << 16) | (g << 8) | b; +} + + +void +TrackerNodeInteract::convertImageTosRGBOpenGLTexture(const boost::shared_ptr& image, + const boost::shared_ptr& tex, + const RectI& renderWindow) +{ + + RectI bounds; + RectI roi; + + if (image) { + bounds = image->getBounds(); + renderWindow.intersect(bounds, &roi); + } else { + bounds = renderWindow; + roi = bounds; + } + if ( roi.isNull() ) { + return; + } + + + std::size_t bytesCount = 4 * sizeof(unsigned char) * roi.area(); + TextureRect region; + region.x1 = roi.x1; + region.x2 = roi.x2; + region.y1 = roi.y1; + region.y2 = roi.y2; + + GLint currentBoundPBO = 0; + glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING_ARB, ¤tBoundPBO); + + glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, pboID ); + glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, bytesCount, NULL, GL_DYNAMIC_DRAW_ARB); + GLvoid *buf = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB); + glCheckError(); + assert(buf); + + if (!image) { + int pixelsCount = roi.area(); + unsigned int* dstPixels = (unsigned int*)buf; + for (int i = 0; i < pixelsCount; ++i, ++dstPixels) { + *dstPixels = toBGRA(0, 0, 0, 255); + } + } else { + int srcNComps = (int)image->getComponentsCount(); + assert(srcNComps >= 3); + Image::ReadAccess acc( image.get() ); + const float* srcPixels = (const float*)acc.pixelAt(roi.x1, roi.y1); + unsigned int* dstPixels = (unsigned int*)buf; + assert(srcPixels); + + int w = roi.width(); + int srcRowElements = bounds.width() * srcNComps; + const Color::Lut* lut = Color::LutManager::sRGBLut(); + lut->validate(); + assert(lut); + + unsigned char alpha = 255; + + for (int y = roi.y1; y < roi.y2; ++y, dstPixels += w, srcPixels += srcRowElements) { + int start = (int)( rand() % (roi.x2 - roi.x1) ); + + for (int backward = 0; backward < 2; ++backward) { + int index = backward ? start - 1 : start; + assert( backward == 1 || ( index >= 0 && index < (roi.x2 - roi.x1) ) ); + unsigned error_r = 0x80; + unsigned error_g = 0x80; + unsigned error_b = 0x80; + + while (index < w && index >= 0) { + float r = srcPixels[index * srcNComps]; + float g = srcPixels[index * srcNComps + 1]; + float b = srcPixels[index * srcNComps + 2]; + + error_r = (error_r & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(r); + error_g = (error_g & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(g); + error_b = (error_b & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(b); + assert(error_r < 0x10000 && error_g < 0x10000 && error_b < 0x10000); + + dstPixels[index] = toBGRA( (U8)(error_r >> 8), + (U8)(error_g >> 8), + (U8)(error_b >> 8), + alpha ); + + + if (backward) { + --index; + } else { + ++index; + } + } + } + } + } + + glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); + glCheckError(); + tex->fillOrAllocateTexture(region, Texture::eDataTypeByte, RectI(), false); + + glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, currentBoundPBO); + + glCheckError(); +} // TrackerNodeInteract::convertImageTosRGBOpenGLTexture + + + +void +TrackerNodeInteract::onTrackingStarted(int step) +{ +#if 0 + _imp->isTracking = true; + if (step > 0) { + _imp->trackFwButton->setChecked(true); + _imp->trackFwButton->setDown(true); + } else { + _imp->trackBwButton->setChecked(true); + _imp->trackBwButton->setDown(true); + } +} + +void +TrackerNodeInteract::onTrackingEnded() +{ + _imp->trackBwButton->setChecked(false); + _imp->trackFwButton->setChecked(false); + _imp->trackBwButton->setDown(false); + _imp->trackFwButton->setDown(false); + _imp->isTracking = false; + _imp->viewer->getViewer()->redraw(); +#endif +} + +void +TrackerNodeInteract::onContextSelectionChanged(int reason) +{ +#if 0 + std::list selection; + + _imp->panel->getContext()->getSelectedMarkers(&selection); + if ( selection.empty() || (selection.size() > 1) ) { + _imp->showMarkerTexture = false; + } else { + assert(selection.size() == 1); + + const TrackMarkerPtr& selectionFront = selection.front(); + TrackMarkerPtr oldMarker = _imp->selectedMarker.lock(); + if (oldMarker != selectionFront) { + _imp->selectedMarker = selectionFront; + _imp->refreshSelectedMarkerTexture(); + + + std::set keys; + selectionFront->getUserKeyframes(&keys); + for (std::set::iterator it2 = keys.begin(); it2 != keys.end(); ++it2) { + _imp->makeMarkerKeyTexture(*it2, selectionFront); + } + } else { + if (selectionFront) { + _imp->showMarkerTexture = true; + } + } + } + if ( (TrackerContext::TrackSelectionReason)reason == TrackerContext::eTrackSelectionViewer ) { + return; + } + + _imp->viewer->getViewer()->redraw(); +#endif +} + + +void +TrackerNodeInteract::onTrackImageRenderingFinished() +{ +#if 0 + assert( QThread::currentThread() == qApp->thread() ); + QFutureWatcher, RectI> >* future = dynamic_cast, RectI> >*>( sender() ); + assert(future); + std::pair, RectI> ret = future->result(); + + + _imp->viewer->getViewer()->makeOpenGLcontextCurrent(); + _imp->showMarkerTexture = true; + if (!_imp->selectedMarkerTexture) { + _imp->selectedMarkerTexture.reset( new Texture(GL_TEXTURE_2D, GL_LINEAR, GL_NEAREST, GL_CLAMP_TO_EDGE) ); + } + _imp->selectedMarkerTextureTime = (int)ret.first->getTime(); + _imp->selectedMarkerTextureRoI = ret.second; + + _imp->convertImageTosRGBOpenGLTexture(ret.first, _imp->selectedMarkerTexture, ret.second); + + + _imp->viewer->getViewer()->redraw(); +#endif +} + +void +TrackerNodeInteract::onKeyFrameImageRenderingFinished() +{ +#if 0 + assert( QThread::currentThread() == qApp->thread() ); + TrackWatcher* future = dynamic_cast( sender() ); + assert(future); + std::pair, RectI> ret = future->result(); + if ( !ret.first || ret.second.isNull() ) { + return; + } + + _imp->viewer->getViewer()->makeOpenGLcontextCurrent(); + + for (TrackKeyframeRequests::iterator it = _imp->trackRequestsMap.begin(); it != _imp->trackRequestsMap.end(); ++it) { + if (it->second.get() == future) { + TrackMarkerPtr track = it->first.track.lock(); + if (!track) { + return; + } + TrackerGuiPrivate::KeyFrameTexIDs& keyTextures = _imp->trackTextures[track]; + GLTexturePtr tex( new Texture(GL_TEXTURE_2D, GL_LINEAR, GL_NEAREST, GL_CLAMP_TO_EDGE) ); + keyTextures[it->first.time] = tex; + _imp->convertImageTosRGBOpenGLTexture(ret.first, tex, ret.second); + + _imp->trackRequestsMap.erase(it); + + _imp->viewer->getViewer()->redraw(); + + return; + } + } + assert(false); +#endif +} + +void +TrackerNodeInteract::rebuildMarkerTextures() +{ +#if 0 + ///Refreh textures for all markers + std::list markers; + + _imp->panel->getContext()->getSelectedMarkers(&markers); + for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { + std::set keys; + (*it)->getUserKeyframes(&keys); + for (std::set::iterator it2 = keys.begin(); it2 != keys.end(); ++it2) { + _imp->makeMarkerKeyTexture(*it2, *it); + } + } + onContextSelectionChanged(TrackerContext::eTrackSelectionInternal); +#endif +} + +void +TrackerNodeInteract::transformPattern(double time, + TrackerMouseStateEnum state, + const Point& delta) +{ +#if 0 + boost::shared_ptr searchWndTopRight, searchWndBtmLeft; + boost::shared_ptr patternCorners[4]; + boost::shared_ptr context = panel->getContext(); + boost::shared_ptr centerKnob = interactMarker->getCenterKnob(); + boost::shared_ptr offsetKnob = interactMarker->getOffsetKnob(); + bool transformPatternCorners = state != eMouseStateDraggingOuterBtmLeft && + state != eMouseStateDraggingOuterBtmRight && + state != eMouseStateDraggingOuterTopLeft && + state != eMouseStateDraggingOuterTopRight && + state != eMouseStateDraggingOuterMidLeft && + state != eMouseStateDraggingOuterMidRight && + state != eMouseStateDraggingOuterTopMid && + state != eMouseStateDraggingOuterBtmMid; + + if (transformPatternCorners) { + patternCorners[0] = interactMarker->getPatternTopLeftKnob(); + patternCorners[1] = interactMarker->getPatternBtmLeftKnob(); + patternCorners[2] = interactMarker->getPatternBtmRightKnob(); + patternCorners[3] = interactMarker->getPatternTopRightKnob(); + } + searchWndTopRight = interactMarker->getSearchWindowTopRightKnob(); + searchWndBtmLeft = interactMarker->getSearchWindowBottomLeftKnob(); + + QPointF centerPoint; + centerPoint.rx() = centerKnob->getValueAtTime(time, 0); + centerPoint.ry() = centerKnob->getValueAtTime(time, 1); + + QPointF offset; + offset.rx() = offsetKnob->getValueAtTime(time, 0); + offset.ry() = offsetKnob->getValueAtTime(time, 1); + + QPointF patternPoints[4]; + QPointF searchPoints[4]; + if (transformPatternCorners) { + for (int i = 0; i < 4; ++i) { + patternPoints[i].rx() = patternCorners[i]->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); + patternPoints[i].ry() = patternCorners[i]->getValueAtTime(time, 1) + centerPoint.y() + offset.y(); + } + } + searchPoints[1].rx() = searchWndBtmLeft->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); + searchPoints[1].ry() = searchWndBtmLeft->getValueAtTime(time, 1) + centerPoint.y() + offset.y(); + + searchPoints[3].rx() = searchWndTopRight->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); + searchPoints[3].ry() = searchWndTopRight->getValueAtTime(time, 1) + centerPoint.y() + offset.y(); + + searchPoints[0].rx() = searchPoints[1].x(); + searchPoints[0].ry() = searchPoints[3].y(); + + searchPoints[2].rx() = searchPoints[3].x(); + searchPoints[2].ry() = searchPoints[1].y(); + + if ( (state == eMouseStateDraggingInnerBtmLeft) || + ( state == eMouseStateDraggingOuterBtmLeft) ) { + if (transformPatternCorners) { + patternPoints[1].rx() += delta.x; + patternPoints[1].ry() += delta.y; + + patternPoints[0].rx() += delta.x; + patternPoints[0].ry() -= delta.y; + + patternPoints[2].rx() -= delta.x; + patternPoints[2].ry() += delta.y; + + patternPoints[3].rx() -= delta.x; + patternPoints[3].ry() -= delta.y; + } + + searchPoints[1].rx() += delta.x; + searchPoints[1].ry() += delta.y; + + searchPoints[0].rx() += delta.x; + searchPoints[0].ry() -= delta.y; + + searchPoints[2].rx() -= delta.x; + searchPoints[2].ry() += delta.y; + + searchPoints[3].rx() -= delta.x; + searchPoints[3].ry() -= delta.y; + } else if ( (state == eMouseStateDraggingInnerBtmRight) || + ( state == eMouseStateDraggingOuterBtmRight) ) { + if (transformPatternCorners) { + patternPoints[1].rx() -= delta.x; + patternPoints[1].ry() += delta.y; + + patternPoints[0].rx() -= delta.x; + patternPoints[0].ry() -= delta.y; + + patternPoints[2].rx() += delta.x; + patternPoints[2].ry() += delta.y; + + patternPoints[3].rx() += delta.x; + patternPoints[3].ry() -= delta.y; + } + + searchPoints[1].rx() -= delta.x; + searchPoints[1].ry() += delta.y; + + searchPoints[0].rx() -= delta.x; + searchPoints[0].ry() -= delta.y; + + searchPoints[2].rx() += delta.x; + searchPoints[2].ry() += delta.y; + + searchPoints[3].rx() += delta.x; + searchPoints[3].ry() -= delta.y; + } else if ( (state == eMouseStateDraggingInnerTopRight) || + ( state == eMouseStateDraggingOuterTopRight) ) { + if (transformPatternCorners) { + patternPoints[1].rx() -= delta.x; + patternPoints[1].ry() -= delta.y; + + patternPoints[0].rx() -= delta.x; + patternPoints[0].ry() += delta.y; + + patternPoints[2].rx() += delta.x; + patternPoints[2].ry() -= delta.y; + + patternPoints[3].rx() += delta.x; + patternPoints[3].ry() += delta.y; + } + + searchPoints[1].rx() -= delta.x; + searchPoints[1].ry() -= delta.y; + + searchPoints[0].rx() -= delta.x; + searchPoints[0].ry() += delta.y; + + searchPoints[2].rx() += delta.x; + searchPoints[2].ry() -= delta.y; + + searchPoints[3].rx() += delta.x; + searchPoints[3].ry() += delta.y; + } else if ( (state == eMouseStateDraggingInnerTopLeft) || + ( state == eMouseStateDraggingOuterTopLeft) ) { + if (transformPatternCorners) { + patternPoints[1].rx() += delta.x; + patternPoints[1].ry() -= delta.y; + + patternPoints[0].rx() += delta.x; + patternPoints[0].ry() += delta.y; + + patternPoints[2].rx() -= delta.x; + patternPoints[2].ry() -= delta.y; + + patternPoints[3].rx() -= delta.x; + patternPoints[3].ry() += delta.y; + } + + searchPoints[1].rx() += delta.x; + searchPoints[1].ry() -= delta.y; + + searchPoints[0].rx() += delta.x; + searchPoints[0].ry() += delta.y; + + searchPoints[2].rx() -= delta.x; + searchPoints[2].ry() -= delta.y; + + searchPoints[3].rx() -= delta.x; + searchPoints[3].ry() += delta.y; + } else if ( (state == eMouseStateDraggingInnerBtmMid) || + ( state == eMouseStateDraggingOuterBtmMid) ) { + if (transformPatternCorners) { + patternPoints[1].ry() += delta.y; + patternPoints[2].ry() += delta.y; + patternPoints[0].ry() -= delta.y; + patternPoints[3].ry() -= delta.y; + } + searchPoints[1].ry() += delta.y; + searchPoints[2].ry() += delta.y; + searchPoints[0].ry() -= delta.y; + searchPoints[3].ry() -= delta.y; + } else if ( (state == eMouseStateDraggingInnerTopMid) || + ( state == eMouseStateDraggingOuterTopMid) ) { + if (transformPatternCorners) { + patternPoints[1].ry() -= delta.y; + patternPoints[2].ry() -= delta.y; + patternPoints[0].ry() += delta.y; + patternPoints[3].ry() += delta.y; + } + searchPoints[1].ry() -= delta.y; + searchPoints[2].ry() -= delta.y; + searchPoints[0].ry() += delta.y; + searchPoints[3].ry() += delta.y; + } else if ( (state == eMouseStateDraggingInnerMidLeft) || + ( state == eMouseStateDraggingOuterMidLeft) ) { + if (transformPatternCorners) { + patternPoints[1].rx() += delta.x; + patternPoints[2].rx() -= delta.x; + patternPoints[0].rx() += delta.x; + patternPoints[3].rx() -= delta.x; + } + searchPoints[1].rx() += delta.x; + searchPoints[2].rx() -= delta.x; + searchPoints[0].rx() += delta.x; + searchPoints[3].rx() -= delta.x; + } else if ( (state == eMouseStateDraggingInnerMidRight) || + ( state == eMouseStateDraggingOuterMidRight) ) { + if (transformPatternCorners) { + patternPoints[1].rx() -= delta.x; + patternPoints[2].rx() += delta.x; + patternPoints[0].rx() -= delta.x; + patternPoints[3].rx() += delta.x; + } + searchPoints[1].rx() -= delta.x; + searchPoints[2].rx() += delta.x; + searchPoints[0].rx() -= delta.x; + searchPoints[3].rx() += delta.x; + } + + EffectInstPtr effect = context->getNode()->getEffectInstance(); + effect->beginChanges(); + + if (transformPatternCorners) { + for (int i = 0; i < 4; ++i) { + patternPoints[i].rx() -= ( centerPoint.x() + offset.x() ); + patternPoints[i].ry() -= ( centerPoint.y() + offset.y() ); + + + if ( patternCorners[i]->hasAnimation() ) { + patternCorners[i]->setValuesAtTime(time, patternPoints[i].x(), patternPoints[i].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); + } else { + patternCorners[i]->setValues(patternPoints[i].x(), patternPoints[i].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); + } + } + } + searchPoints[1].rx() -= ( centerPoint.x() + offset.x() ); + searchPoints[1].ry() -= ( centerPoint.y() + offset.y() ); + + searchPoints[3].rx() -= ( centerPoint.x() + offset.x() ); + searchPoints[3].ry() -= ( centerPoint.y() + offset.y() ); + + if ( searchWndBtmLeft->hasAnimation() ) { + searchWndBtmLeft->setValuesAtTime(time, searchPoints[1].x(), searchPoints[1].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); + } else { + searchWndBtmLeft->setValues(searchPoints[1].x(), searchPoints[1].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); + } + + if ( searchWndTopRight->hasAnimation() ) { + searchWndTopRight->setValuesAtTime(time, searchPoints[3].x(), searchPoints[3].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); + } else { + searchWndTopRight->setValues(searchPoints[3].x(), searchPoints[3].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); + } + effect->endChanges(); + + refreshSelectedMarkerTexture(); + + if ( createKeyOnMoveButton->isChecked() ) { + interactMarker->setUserKeyframe(time); + } +#endif +} // TrackerNodeInteract::transformPattern + + + + + + +void +TrackerNodeInteract::onKeyframeSetOnTrack(const TrackMarkerPtr& marker, + int key) +{ +#if 0 + _imp->makeMarkerKeyTexture(key, marker); +#endif +} + +void +TrackerNodeInteract::onKeyframeRemovedOnTrack(const TrackMarkerPtr& marker, + int key) +{ +#if 0 + for (TrackerGuiPrivate::TrackKeysMap::iterator it = _imp->trackTextures.begin(); it != _imp->trackTextures.end(); ++it) { + if (it->first.lock() == marker) { + std::map >::iterator found = it->second.find(key); + if ( found != it->second.end() ) { + it->second.erase(found); + } + break; + } + } + _imp->viewer->getViewer()->redraw(); +#endif +} + +void +TrackerNodeInteract::onAllKeyframesRemovedOnTrack(const TrackMarkerPtr& marker) +{ +#if 0 + for (TrackerGuiPrivate::TrackKeysMap::iterator it = _imp->trackTextures.begin(); it != _imp->trackTextures.end(); ++it) { + if (it->first.lock() == marker) { + it->second.clear(); + break; + } + } + _imp->viewer->getViewer()->redraw(); +#endif +} + +NATRON_NAMESPACE_EXIT; +NATRON_NAMESPACE_USING; +#include "moc_TrackerNodeInteract.cpp" \ No newline at end of file diff --git a/Engine/TrackerNodeInteract.h b/Engine/TrackerNodeInteract.h new file mode 100644 index 0000000000..d4daac764e --- /dev/null +++ b/Engine/TrackerNodeInteract.h @@ -0,0 +1,442 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * This file is part of Natron , + * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat + * + * Natron is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Natron is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Natron. If not, see + * ***** END LICENSE BLOCK ***** */ + + +#ifndef TRACKERNODEINTERACT_H +#define TRACKERNODEINTERACT_H + + +// ***** BEGIN PYTHON BLOCK ***** +// from : +// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." +#include +// ***** END PYTHON BLOCK ***** + +#include +#include + +#include "Engine/EngineFwd.h" +#include "Engine/KnobTypes.h" +#include "Engine/RectI.h" +#include "Engine/Texture.h" + + +#define POINT_SIZE 5 +#define CROSS_SIZE 6 +#define POINT_TOLERANCE 6 +#define ADDTRACK_SIZE 5 +#define HANDLE_SIZE 6 + +//Controls how many center keyframes should be displayed before and after the time displayed +#define MAX_CENTER_POINTS_DISPLAYED 50 + +#define SELECTED_MARKER_WINDOW_BASE_WIDTH_SCREEN_PX 200 + +#define SELECTED_MARKER_KEYFRAME_WIDTH_SCREEN_PX 75 +#define MAX_TRACK_KEYS_TO_DISPLAY 10 + +#define CORRELATION_ERROR_MAX_DISPLAY 0.2 + +#define kTrackerUIParamAddTrack "addTrack" +#define kTrackerUIParamAddTrackLabel "Add Track" +#define kTrackerUIParamAddTrackHint "When enabled you can add new tracks " \ +"by clicking on the Viewer. " \ +"Holding the Control + Alt keys is the " \ +"same as pressing this button." + +#define kTrackerUIParamTrackBW "trackBW" +#define kTrackerUIParamTrackBWLabel "Track Backward" +#define kTrackerUIParamTrackBWHint "Track selected tracks backward until left bound of the timeline" + +#define kTrackerUIParamTrackPrevious "trackPrevious" +#define kTrackerUIParamTrackPreviousLabel "Track Previous" +#define kTrackerUIParamTrackPreviousHint "Track selected tracks on the previous frame" + +#define kTrackerUIParamTrackFW "trackFW" +#define kTrackerUIParamTrackFWLabel "Track Forward" +#define kTrackerUIParamTrackFWHint "Track selected tracks forward until right bound of the timeline" + +#define kTrackerUIParamTrackNext "trackNext" +#define kTrackerUIParamTrackNextLabel "Track Next" +#define kTrackerUIParamTrackNextHint "Track selected tracks on the next frame" + +#define kTrackerUIParamTrackRange "trackRange" +#define kTrackerUIParamTrackRangeLabel "Track Range" +#define kTrackerUIParamTrackRangeHint "Track selected tracks over the range and with the step input by a dialog" + +#define kTrackerUIParamTrackAllKeyframes "trackAllKeys" +#define kTrackerUIParamTrackAllKeyframesLabel "Track All Keyframes" +#define kTrackerUIParamTrackAllKeyframesHint "Track selected tracks across all pattern keyframes" + +#define kTrackerUIParamTrackCurrentKeyframe "trackCurrentKey" +#define kTrackerUIParamTrackCurrentKeyframeLabel "Track Current Keyframe" +#define kTrackerUIParamTrackCurrentKeyframeHint "Track selected tracks over only the pattern keyframes related to the current track" + +#define kTrackerUIParamClearAllAnimation "clearAnimation" +#define kTrackerUIParamClearAllAnimationLabel "Clear Animation" +#define kTrackerUIParamClearAllAnimationHint "Reset animation on the selected tracks" + +#define kTrackerUIParamClearAnimationBw "clearAnimationBw" +#define kTrackerUIParamClearAnimationBwLabel "Clear Animation Backward" +#define kTrackerUIParamClearAnimationBwHint "Reset animation on the selected tracks backward from the current frame" + +#define kTrackerUIParamClearAnimationFw "clearAnimationFw" +#define kTrackerUIParamClearAnimationFwLabel "Clear Animation Forward" +#define kTrackerUIParamClearAnimationFwHint "Reset animation on the selected tracks forward from the current frame" + +#define kTrackerUIParamRefreshViewer "refreshViewer" +#define kTrackerUIParamRefreshViewerLabel "Refresh Viewer" +#define kTrackerUIParamRefreshViewerHint "Update viewer during tracking for each frame instead of just the tracks" + +#define kTrackerUIParamCenterViewer "centerViewer" +#define kTrackerUIParamCenterViewerLabel "Center Viewer" +#define kTrackerUIParamCenterViewerHint "Center the viewer on selected tracks during tracking" + +#define kTrackerUIParamCreateKeyOnMove "createKeyOnMove" +#define kTrackerUIParamCreateKeyOnMoveLabel "Create Keyframe On Move" +#define kTrackerUIParamCreateKeyOnMoveHint "When enabled, adjusting a track on the viewer will create a new keyframe" + +#define kTrackerUIParamShowError "showError" +#define kTrackerUIParamShowErrorLabel "Show Error" +#define kTrackerUIParamShowErrorHint "When enabled, the error of the track for each frame will be displayed on " \ +"the viewer, with good tracks close to green and bad tracks close to red" + +#define kTrackerUIParamSetPatternKeyFrame "setPatternKey" +#define kTrackerUIParamSetPatternKeyFrameLabel "Set Keyframe On Pattern" +#define kTrackerUIParamSetPatternKeyFrameHint "Set a keyframe for the pattern for the selected tracks" + +#define kTrackerUIParamRemovePatternKeyFrame "removePatternKey" +#define kTrackerUIParamRemovePatternKeyFrameLabel "Remove Keyframe On Pattern" +#define kTrackerUIParamRemovePatternKeyFrameHint "Remove a keyframe for the pattern for the selected tracks at the current timeline's time, if any" + +#define kTrackerUIParamResetOffset "resetTrackOffset" +#define kTrackerUIParamResetOffsetLabel "Reset Offset" +#define kTrackerUIParamResetOffsetHint "Resets the offset for the selected tracks" + +#define kTrackerUIParamResetTrack "resetTrack" +#define kTrackerUIParamResetTrackLabel "Reset Track" +#define kTrackerUIParamResetTrackHint "Reset pattern, search window and track animation for the selected tracks" + +NATRON_NAMESPACE_ENTER; + + + +enum TrackerMouseStateEnum +{ + eMouseStateIdle = 0, + eMouseStateDraggingCenter, + eMouseStateDraggingOffset, + + eMouseStateDraggingInnerTopLeft, + eMouseStateDraggingInnerTopRight, + eMouseStateDraggingInnerBtmLeft, + eMouseStateDraggingInnerBtmRight, + eMouseStateDraggingInnerTopMid, + eMouseStateDraggingInnerMidRight, + eMouseStateDraggingInnerBtmMid, + eMouseStateDraggingInnerMidLeft, + + eMouseStateDraggingOuterTopLeft, + eMouseStateDraggingOuterTopRight, + eMouseStateDraggingOuterBtmLeft, + eMouseStateDraggingOuterBtmRight, + eMouseStateDraggingOuterTopMid, + eMouseStateDraggingOuterMidRight, + eMouseStateDraggingOuterBtmMid, + eMouseStateDraggingOuterMidLeft, + + eMouseStateDraggingSelectedMarkerResizeAnchor, + eMouseStateScalingSelectedMarker, + eMouseStateDraggingSelectedMarker, +}; + +enum TrackerDrawStateEnum +{ + eDrawStateInactive = 0, + eDrawStateHoveringCenter, + + eDrawStateHoveringInnerTopLeft, + eDrawStateHoveringInnerTopRight, + eDrawStateHoveringInnerBtmLeft, + eDrawStateHoveringInnerBtmRight, + eDrawStateHoveringInnerTopMid, + eDrawStateHoveringInnerMidRight, + eDrawStateHoveringInnerBtmMid, + eDrawStateHoveringInnerMidLeft, + + eDrawStateHoveringOuterTopLeft, + eDrawStateHoveringOuterTopRight, + eDrawStateHoveringOuterBtmLeft, + eDrawStateHoveringOuterBtmRight, + eDrawStateHoveringOuterTopMid, + eDrawStateHoveringOuterMidRight, + eDrawStateHoveringOuterBtmMid, + eDrawStateHoveringOuterMidLeft, + + eDrawStateShowScalingHint, +}; + +typedef QFutureWatcher, RectI> > TrackWatcher; +typedef boost::shared_ptr TrackWatcherPtr; + +struct TrackRequestKey +{ + int time; + boost::weak_ptr track; + RectI roi; +}; + +struct TrackRequestKey_compareLess +{ + bool operator()(const TrackRequestKey& lhs, + const TrackRequestKey& rhs) const + { + if (lhs.time < rhs.time) { + return true; + } else if (lhs.time > rhs.time) { + return false; + } else { + TrackMarkerPtr lptr = lhs.track.lock(); + TrackMarkerPtr rptr = rhs.track.lock(); + if ( lptr.get() < rptr.get() ) { + return true; + } else if ( lptr.get() > rptr.get() ) { + return false; + } else { + double la = lhs.roi.area(); + double ra = rhs.roi.area(); + if (la < ra) { + return true; + } else if (la > ra) { + return false; + } else { + return false; + } + } + } + } +}; + +typedef std::map TrackKeyframeRequests; + +class TrackerNode; +class TrackerNodeInteract; +struct TrackerNodePrivate +{ + TrackerNode* publicInterface; + boost::shared_ptr ui; + + TrackerNodePrivate(TrackerNode* publicInterface); + + ~TrackerNodePrivate(); +}; + +class TrackerNodeInteract : public QObject +{ + GCC_DIAG_SUGGEST_OVERRIDE_OFF + Q_OBJECT + GCC_DIAG_SUGGEST_OVERRIDE_ON + +public: + + TrackerNodePrivate* _p; + boost::weak_ptr addTrackButton; + boost::weak_ptr trackRangeButton; + boost::weak_ptr trackBwButton; + boost::weak_ptr trackPrevButton; + boost::weak_ptr trackNextButton; + boost::weak_ptr trackFwButton; + boost::weak_ptr trackAllKeyframesButton; + boost::weak_ptr trackCurrentKeyframeButton; + boost::weak_ptr clearAllAnimationButton; + boost::weak_ptr clearBwAnimationButton; + boost::weak_ptr clearFwAnimationButton; + boost::weak_ptr updateViewerButton; + boost::weak_ptr centerViewerButton; + boost::weak_ptr createKeyOnMoveButton; + boost::weak_ptr setKeyFrameButton; + boost::weak_ptr removeKeyFrameButton; + boost::weak_ptr resetOffsetButton; + boost::weak_ptr resetTrackButton; + boost::weak_ptr showCorrelationButton; + bool clickToAddTrackEnabled; + QPointF lastMousePos; + QRectF selectionRectangle; + int controlDown; + int shiftDown; + TrackerMouseStateEnum eventState; + TrackerDrawStateEnum hoverState; + TrackMarkerPtr interactMarker, hoverMarker; + + typedef std::map KeyFrameTexIDs; + typedef std::map, KeyFrameTexIDs> TrackKeysMap; + TrackKeysMap trackTextures; + TrackKeyframeRequests trackRequestsMap; + GLTexturePtr selectedMarkerTexture; + int selectedMarkerTextureTime; + RectI selectedMarkerTextureRoI; + //If theres a single selection, this points to it + boost::weak_ptr selectedMarker; + GLuint pboID; + int selectedMarkerWidth; + TrackWatcherPtr imageGetterWatcher; + bool showMarkerTexture; + RenderScale selectedMarkerScale; + boost::weak_ptr selectedMarkerImg; + bool isTracking; + int lastTrackRangeFirstFrame, lastTrackRangeLastFrame, lastTrackRangeStep; + + + TrackerNodeInteract(TrackerNodePrivate* p); + + ~TrackerNodeInteract(); + + + void onAddTrackClicked(bool clicked); + + void onTrackRangeClicked(); + + void onTrackAllKeyframesClicked(); + + void onTrackCurrentKeyframeClicked(); + + void onTrackBwClicked(); + + void onTrackPrevClicked(); + + void onStopButtonClicked(); + + void onTrackNextClicked(); + + void onTrackFwClicked(); + + void onUpdateViewerClicked(bool clicked); + + void onClearAllAnimationClicked(); + + void onClearBwAnimationClicked(); + + void onClearFwAnimationClicked(); + + void onCreateKeyOnMoveButtonClicked(bool clicked); + + void onShowCorrelationButtonClicked(bool clicked); + + void onCenterViewerButtonClicked(bool clicked); + + void onSetKeyframeButtonClicked(); + + void onRemoveKeyframeButtonClicked(); + + void onResetOffsetButtonClicked(); + + void onResetTrackButtonClicked(); + + void transformPattern(double time, TrackerMouseStateEnum state, const Point& delta); + + void refreshSelectedMarkerTexture(); + + void convertImageTosRGBOpenGLTexture(const boost::shared_ptr& image, const boost::shared_ptr& tex, const RectI& renderWindow); + + void makeMarkerKeyTexture(int time, const TrackMarkerPtr& track); + + void drawSelectedMarkerTexture(const std::pair& pixelScale, + int currentTime, + const Point& selectedCenter, + const Point& offset, + const Point& selectedPtnTopLeft, + const Point& selectedPtnTopRight, + const Point& selectedPtnBtmRight, + const Point& selectedPtnBtmLeft, + const Point& selectedSearchWndBtmLeft, + const Point& selectedSearchWndTopRight); + + void drawSelectedMarkerKeyframes(const std::pair& pixelScale, int currentTime); + + ///Filter allkeys so only that only the MAX_TRACK_KEYS_TO_DISPLAY surrounding are displayed + static KeyFrameTexIDs getKeysToRenderForMarker(double currentTime, const KeyFrameTexIDs& allKeys); + + void computeTextureCanonicalRect(const Texture& tex, int xOffset, int texWidthPx, RectD* rect) const; + void computeSelectedMarkerCanonicalRect(RectD* rect) const; + bool isNearbySelectedMarkerTextureResizeAnchor(const QPointF& pos) const; + bool isInsideSelectedMarkerTextureResizeAnchor(const QPointF& pos) const; + + ///Returns the keyframe time if the mouse is inside a keyframe texture + int isInsideKeyFrameTexture(double currentTime, const QPointF& pos, const QPointF& viewportPos) const; + + static Point toMagWindowPoint(const Point& ptnPoint, + const RectD& canonicalSearchWindow, + const RectD& textureRectCanonical); + + + static QPointF computeMidPointExtent(const QPointF& prev, + const QPointF& next, + const QPointF& point, + const QPointF& handleSize); + + static bool isNearbyPoint(const boost::shared_ptr& knob, + const std::pair& pixelScale, + double xWidget, + double yWidget, + double toleranceWidget, + double time); + + static bool isNearbyPoint(const QPointF& p, + const std::pair& pixelScale, + double xWidget, + double yWidget, + double toleranceWidget); + + static void drawEllipse(double x, + double y, + double radiusX, + double radiusY, + int l, + double r, + double g, + double b, + double a); + + static void findLineIntersection(const Point& p, + const Point& l1, + const Point& l2, + Point* inter); + + +public Q_SLOTS: + + void onTrackingStarted(int step); + + void onTrackingEnded(); + + + void onContextSelectionChanged(int reason); + void onKeyframeSetOnTrack(const TrackMarkerPtr &marker, int key); + void onKeyframeRemovedOnTrack(const TrackMarkerPtr &marker, int key); + void onAllKeyframesRemovedOnTrack(const TrackMarkerPtr& marker); + + void rebuildMarkerTextures(); + void onTrackImageRenderingFinished(); + void onKeyFrameImageRenderingFinished(); + + +}; + +NATRON_NAMESPACE_EXIT; + +#endif // TRACKERNODEINTERACT_H diff --git a/Engine/UndoCommand.h b/Engine/UndoCommand.h new file mode 100644 index 0000000000..8882e2ae20 --- /dev/null +++ b/Engine/UndoCommand.h @@ -0,0 +1,78 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * This file is part of Natron , + * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat + * + * Natron is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Natron is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Natron. If not, see + * ***** END LICENSE BLOCK ***** */ + +#ifndef UNDOCOMMAND_H +#define UNDOCOMMAND_H + +// ***** BEGIN PYTHON BLOCK ***** +// from : +// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." +#include +// ***** END PYTHON BLOCK ***** + + +#include +#include "Engine/EngineFwd.h" + + +NATRON_NAMESPACE_ENTER; + +class UndoCommand { + + std::string _text; +public: + + UndoCommand(const std::string& text = std::string()) + : _text(text) + {} + + virtual ~UndoCommand() {} + + std::string getText() const + { + return _text; + } + + // Must be called before pushUndoCommand() otherwise the text does not get updated + void setText(const std::string& text) + { + _text = text; + } + + /** + * @brief Called to redo the action + **/ + virtual void redo() = 0; + + /** + * @brief Called to undo the action + **/ + virtual void undo() = 0; + + /** + * @brief Called to merge the other action in this action (other is the newest action) + **/ + virtual bool mergeWith(const UndoCommandPtr& /*other*/) + { + return false; + } +}; + +NATRON_NAMESPACE_EXIT; + +#endif // UNDOCOMMAND_H diff --git a/Engine/ViewerInstance.cpp b/Engine/ViewerInstance.cpp index 9a56be85fb..2329cdf984 100644 --- a/Engine/ViewerInstance.cpp +++ b/Engine/ViewerInstance.cpp @@ -1011,7 +1011,7 @@ ViewerInstance::getViewerRoIAndTexture(const RectD& rod, inputToRenderName, outArgs->params->layer, outArgs->params->alphaLayer.getLayerName() + outArgs->params->alphaChannelName, - outArgs->params->depth == eImageBitDepthFloat && supportsGLSL(), + outArgs->params->depth == eImageBitDepthFloat, isDraftMode); std::list entries; bool hasTextureCached = AppManager::getTextureFromCache(key, &entries); @@ -1685,7 +1685,7 @@ ViewerInstance::renderViewer_internal(ViewIdx view, inputToRenderName, inArgs.params->layer, inArgs.params->alphaLayer.getLayerName() + inArgs.params->alphaChannelName, - inArgs.params->depth == eImageBitDepthFloat && supportsGLSL(), + inArgs.params->depth == eImageBitDepthFloat, inArgs.draftModeEnabled); boost::shared_ptr cachedFrameParams = FrameEntry::makeParams( bounds, key.getBitDepth(), it->rect.width(), it->rect.height(), ImagePtr() ); bool cached = AppManager::getTextureFromCacheOrCreate(key, cachedFrameParams, &it->cachedData); @@ -2828,7 +2828,7 @@ ViewerInstance::ViewerInstancePrivate::updateViewer(boost::shared_ptrisPartialRect && params->tiles.size() == 1) || !params->isPartialRect ); - boost::shared_ptr texture; + boost::shared_ptr texture; bool isFirstTile = true; for (std::list::iterator it = params->tiles.begin(); it != params->tiles.end(); ++it) { assert(it->ramBuffer); @@ -2908,7 +2908,7 @@ ViewerInstance::onGammaChanged(double value) } assert(_imp->uiContext); if (changed) { - if ( ( (_imp->uiContext->getBitDepth() == eImageBitDepthByte) || !_imp->uiContext->supportsGLSL() ) + if (_imp->uiContext->getBitDepth() == eImageBitDepthByte && !getApp()->getProject()->isLoadingProject() ) { renderCurrentFrame(true); } else { @@ -2941,7 +2941,7 @@ ViewerInstance::onGainChanged(double exp) } if (changed) { assert(_imp->uiContext); - if ( ( (_imp->uiContext->getBitDepth() == eImageBitDepthByte) || !_imp->uiContext->supportsGLSL() ) + if ( ( (_imp->uiContext->getBitDepth() == eImageBitDepthByte) ) && !getApp()->getProject()->isLoadingProject() ) { renderCurrentFrame(true); } else { @@ -3014,7 +3014,7 @@ ViewerInstance::onColorSpaceChanged(ViewerColorSpaceEnum colorspace) _imp->viewerParamsLut = colorspace; } assert(_imp->uiContext); - if ( ( (_imp->uiContext->getBitDepth() == eImageBitDepthByte) || !_imp->uiContext->supportsGLSL() ) + if ( ( (_imp->uiContext->getBitDepth() == eImageBitDepthByte) ) && !getApp()->getProject()->isLoadingProject() ) { renderCurrentFrame(true); } else { @@ -3113,16 +3113,7 @@ ViewerInstance::disconnectTexture(int index) } } -bool -ViewerInstance::supportsGLSL() const -{ - ///This is a short-cut, this is primarily used when the user switch the - /// texture mode in the preferences menu. If the hardware doesn't support GLSL - /// it returns false, true otherwise. @see Settings::onKnobValueChanged - assert(_imp->uiContext); - return _imp->uiContext->supportsGLSL(); -} void ViewerInstance::redrawViewer() diff --git a/Engine/ViewerInstance.h b/Engine/ViewerInstance.h index 1d2c5be0b0..635a196e99 100644 --- a/Engine/ViewerInstance.h +++ b/Engine/ViewerInstance.h @@ -240,12 +240,6 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON DisplayChannelsEnum getChannels(int texIndex) const WARN_UNUSED_RETURN; - /** - * @brief This is a short-cut, this is primarily used when the user switch the - * texture mode in the preferences menu. If the hardware doesn't support GLSL - * it returns false, true otherwise. @see Settings::onKnobValueChanged - **/ - bool supportsGLSL() const WARN_UNUSED_RETURN; virtual bool supportsMultipleClipsPAR() const OVERRIDE FINAL WARN_UNUSED_RETURN { diff --git a/Global/Enums.h b/Global/Enums.h index ba991fff0c..45fe8bee96 100644 --- a/Global/Enums.h +++ b/Global/Enums.h @@ -527,6 +527,9 @@ enum RenderSafetyEnum enum PenType { + ePenTypeLMB, + ePenTypeMMB, + ePenTypeRMB, ePenTypePen, ePenTypeCursor, ePenTypeEraser @@ -591,6 +594,31 @@ enum ValueIsNormalizedEnum eValueIsNormalizedY ///< indicating that the dimension holds a value normalized against the Y dimension of the project format }; +enum CursorEnum +{ + eCursorDefault, + eCursorBlank, + eCursorArrow, + eCursorUpArrow, + eCursorCross, + eCursorIBeam, + eCursorWait, + eCursorBusy, + eCursorForbidden, + eCursorPointingHand, + eCursorWhatsThis, + eCursorSizeVer, + eCursorSizeHor, + eCursorBDiag, + eCursorFDiag, + eCursorSizeAll, + eCursorSplitV, + eCursorSplitH, + eCursorOpenHand, + eCursorClosedHand +}; + + //typedef QFlags StandardButtons; Q_DECLARE_FLAGS(StandardButtons, StandardButtonEnum) diff --git a/Global/GLIncludes.h b/Global/GLIncludes.h index a34ccdace9..cc55cdf6c6 100644 --- a/Global/GLIncludes.h +++ b/Global/GLIncludes.h @@ -19,7 +19,18 @@ #ifndef NATRON_GLOBAL_GLINCLUDES_H #define NATRON_GLOBAL_GLINCLUDES_H -#include +#include "Global/glad_include.h" + +// On OSX GLFW should be compiled with -DGLFW_USE_CHDIR=off -DGLFW_USE_MENUBAR=off -DGLFW_USE_RETINA=on +#include + +/* this is where we can safely include GLU */ +# if defined(__APPLE__) && defined(__MACH__) +# include +# else +# include +# endif + #define QT_NO_OPENGL_ES_2 #include "Global/Macros.h" @@ -27,10 +38,28 @@ #ifdef DEBUG #include -// put a breakpoint in glError to halt the debugger + +// put a breakpoint in glError to halt the debugger on opengl errors inline void glError() {} +#ifdef GL_TRACE_CALLS +// logs every gl call to the console +void pre_gl_call(const char *name, void *funcptr, int len_args, ...) { + printf("Calling: %s (%d arguments)\n", name, len_args); +} +// logs every gl call to the console +void post_gl_call(const char */*name*/, void */*funcptr*/, int /*len_args*/, ...) { + + GLenum _glerror_ = glGetError(); + if (_glerror_ != GL_NO_ERROR) { + std::cout << "GL_ERROR :" << __FILE__ << " " << __LINE__ << " " << gluErrorString(_glerror_) << std::endl; + glError(); + } + +} +#endif + #define glCheckError() \ { \ GLenum _glerror_ = glGetError(); \ @@ -96,10 +125,6 @@ glError() {} std::cout << "Framebuffer incomplete multisample" << std::endl; \ glError(); \ } \ - else if (error == GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT) { \ - std::cout << "Framebuffer incomplete layer targets" << std::endl; \ - glError(); \ - } \ else if (error == 0) { \ std::cout << "an error occured determining the status of the framebuffer" << std::endl; \ glError(); \ diff --git a/Global/GlobalDefines.h b/Global/GlobalDefines.h index 091abea091..8890305822 100644 --- a/Global/GlobalDefines.h +++ b/Global/GlobalDefines.h @@ -144,6 +144,7 @@ typedef OfxRangeD RangeD; #define kDopeSheetEditorRightTrimCommandCompressionID 20 #define kDopeSheetEditorTransformKeysCommandCompressionID 21 #define kDopeSheetEditorSlipReaderCommandCompressionID 23 +#define kNodeUndoChangeCommandCompressionID 24 #define PY_VERSION_STRINGIZE_(major, minor) \ # major "." # minor diff --git a/Global/glad_include.h b/Global/glad_include.h new file mode 100644 index 0000000000..2a930d0dcd --- /dev/null +++ b/Global/glad_include.h @@ -0,0 +1,10 @@ +#ifndef NATRON_GLOBAL_GLAD_H +#define NATRON_GLOBAL_GLAD_H + +#ifdef DEBUG +#include "Global/glad_debug/include/glad/glad.h" +#else +#include "Global/glad_release/include/glad/glad.h" +#endif + +#endif // NATRON_GLOBAL_GLAD_H \ No newline at end of file diff --git a/Global/glad_source.c b/Global/glad_source.c new file mode 100644 index 0000000000..f4003b88a5 --- /dev/null +++ b/Global/glad_source.c @@ -0,0 +1,5 @@ +#ifdef DEBUG +#include "Global/glad_debug/src/glad.c" +#else +#include "Global/glad_release/src/glad.c" +#endif \ No newline at end of file diff --git a/Gui/AboutWindow.cpp b/Gui/AboutWindow.cpp index ba3f53e0dd..784d897ca3 100644 --- a/Gui/AboutWindow.cpp +++ b/Gui/AboutWindow.cpp @@ -40,6 +40,8 @@ CLANG_DIAG_OFF(deprecated) #include CLANG_DIAG_ON(deprecated) +#include "Engine/AppManager.h" + #include "Global/GlobalDefines.h" #include "Global/GitVersion.h" #include "Gui/Button.h" @@ -52,10 +54,8 @@ CLANG_DIAG_ON(deprecated) NATRON_NAMESPACE_ENTER; -AboutWindow::AboutWindow(Gui* gui, - QWidget* parent) +AboutWindow::AboutWindow(QWidget* parent) : QDialog(parent) - , _gui(gui) { setWindowTitle( QObject::tr("About ") + QString::fromUtf8(NATRON_APPLICATION_NAME) ); _mainLayout = new QVBoxLayout(this); @@ -342,15 +342,15 @@ AboutWindow::updateLibrariesVersions() QString libsText = QString::fromUtf8("

Python %1

" "

Qt %2

" "

Boost %3

" - "

Glew %4

" + "

GLFW %4

" "

OpenGL %5

" "

Cairo %6

") .arg( QString::fromUtf8(PY_VERSION) ) - .arg( _gui->getQtVersion() ) - .arg( _gui->getBoostVersion() ) - .arg( _gui->getGlewVersion() ) - .arg( _gui->getOpenGLVersion() ) - .arg( _gui->getCairoVersion() ); + .arg( appPTR->getQtVersion() ) + .arg( appPTR->getBoostVersion() ) + .arg( appPTR->getGlfwVersion() ) + .arg( appPTR->getOpenGLVersion() ) + .arg( appPTR->getCairoVersion() ); _libsText->setText(libsText); } diff --git a/Gui/AboutWindow.h b/Gui/AboutWindow.h index 96dca6aca2..626f9bec48 100644 --- a/Gui/AboutWindow.h +++ b/Gui/AboutWindow.h @@ -54,14 +54,12 @@ class AboutWindow QWidget* _buttonContainer; QHBoxLayout* _buttonLayout; Button* _closeButton; - Gui* _gui; TableModel* _model; TableView* _view; public: - AboutWindow(Gui* gui, - QWidget* parent = 0); + AboutWindow(QWidget* parent = 0); void updateLibrariesVersions(); diff --git a/Gui/ActionShortcuts.h b/Gui/ActionShortcuts.h index fd07cdd5b8..6e5eabb6aa 100644 --- a/Gui/ActionShortcuts.h +++ b/Gui/ActionShortcuts.h @@ -44,6 +44,7 @@ CLANG_DIAG_OFF(uninitialized) CLANG_DIAG_ON(deprecated) CLANG_DIAG_ON(uninitialized) +#include "Engine/PluginActionShortcut.h" #include "Gui/GuiFwd.h" #define kShortcutGroupGlobal "Global" @@ -364,67 +365,6 @@ CLANG_DIAG_ON(uninitialized) #define kShortcutIDActionPlayerPlaybackOut "pbOut" #define kShortcutDescActionPlayerPlaybackOut "Set Playback Out Point" -///////////ROTO SHORTCUTS -#define kShortcutIDActionRotoDelete "delete" -#define kShortcutDescActionRotoDelete "Delete Element" - -#define kShortcutIDActionRotoCloseBezier "closeBezier" -#define kShortcutDescActionRotoCloseBezier "Close Bezier" - -#define kShortcutIDActionRotoSelectAll "selectAll" -#define kShortcutDescActionRotoSelectAll "Select All" - -#define kShortcutIDActionRotoSelectionTool "selectionTool" -#define kShortcutDescActionRotoSelectionTool "Switch to Selection Mode" - -#define kShortcutIDActionRotoAddTool "addTool" -#define kShortcutDescActionRotoAddTool "Switch to Add Mode" - -#define kShortcutIDActionRotoEditTool "editTool" -#define kShortcutDescActionRotoEditTool "Switch to Edition Mode" - -#define kShortcutIDActionRotoBrushTool "brushTool" -#define kShortcutDescActionRotoBrushTool "Switch to Brush Mode" - -#define kShortcutIDActionRotoCloneTool "cloneTool" -#define kShortcutDescActionRotoCloneTool "Switch to Clone Mode" - -#define kShortcutIDActionRotoEffectTool "EffectTool" -#define kShortcutDescActionRotoEffectTool "Switch to Effect Mode" - -#define kShortcutIDActionRotoColorTool "colorTool" -#define kShortcutDescActionRotoColorTool "Switch to Color Mode" - -#define kShortcutIDActionRotoNudgeLeft "nudgeLeft" -#define kShortcutDescActionRotoNudgeLeft "Move Bezier to the Left" - -#define kShortcutIDActionRotoNudgeRight "nudgeRight" -#define kShortcutDescActionRotoNudgeRight "Move Bezier to the Right" - -#define kShortcutIDActionRotoNudgeBottom "nudgeBottom" -#define kShortcutDescActionRotoNudgeBottom "Move Bezier to the Bottom" - -#define kShortcutIDActionRotoNudgeTop "nudgeTop" -#define kShortcutDescActionRotoNudgeTop "Move Bezier to the Top" - -#define kShortcutIDActionRotoSmooth "smooth" -#define kShortcutDescActionRotoSmooth "Smooth Bezier" - -#define kShortcutIDActionRotoCuspBezier "cusp" -#define kShortcutDescActionRotoCuspBezier "Cusp Bezier" - -#define kShortcutIDActionRotoRemoveFeather "rmvFeather" -#define kShortcutDescActionRotoRemoveFeather "Remove Feather" - -#define kShortcutIDActionRotoLinkToTrack "linkToTrack" -#define kShortcutDescActionRotoLinkToTrack "Link to Track" - -#define kShortcutIDActionRotoUnlinkToTrack "unlinkFromTrack" -#define kShortcutDescActionRotoUnlinkToTrack "Unlink from Track" - -#define kShortcutIDActionRotoLockCurve "lock" -#define kShortcutDescActionRotoLockCurve "Lock Shape" - ///////////TRACKING SHORTCUTS #define kShortcutIDActionTrackingSelectAll "selectAll" #define kShortcutDescActionTrackingSelectAll "Select All Tracks" @@ -755,6 +695,11 @@ class ActionWithShortcut bool setShortcutOnAction = true); + const std::vector >& getShortcuts() const + { + return _shortcuts; + } + virtual ~ActionWithShortcut(); virtual void setShortcutWrapper(const QString& actionID, const QKeySequence& shortcut); diff --git a/Gui/CurveWidget.cpp b/Gui/CurveWidget.cpp index a227664e97..c9e167b94b 100644 --- a/Gui/CurveWidget.cpp +++ b/Gui/CurveWidget.cpp @@ -154,10 +154,6 @@ CurveWidget::initializeGL() // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); - if ( !glewIsSupported("GL_ARB_vertex_array_object " // BindVertexArray, DeleteVertexArrays, GenVertexArrays, IsVertexArray (VAO), core since 3.0 - ) ) { - _imp->_hasOpenGLVAOSupport = false; - } } void @@ -401,6 +397,29 @@ CurveWidget::getBackgroundColour(double &r, appPTR->getCurrentSettings()->getCurveEditorBGColor(&r, &g, &b); } +RectD +CurveWidget::getViewportRect() const +{ + RectD bbox; + { + bbox.x1 = _imp->zoomCtx.left(); + bbox.y1 = _imp->zoomCtx.bottom(); + bbox.x2 = _imp->zoomCtx.right(); + bbox.y2 = _imp->zoomCtx.top(); + } + return bbox; +} + +void +CurveWidget::getCursorPosition(double& x, double& y) const +{ + QPoint p = QCursor::pos(); + p = mapFromGlobal(p); + QPointF mappedPos = toZoomCoordinates(p.x(), p.y()); + x = mappedPos.x(); + y = mappedPos.y(); +} + void CurveWidget::saveOpenGLContext() { @@ -1873,15 +1892,6 @@ CurveWidget::getSelectedKeyFrames() const return _imp->_selectedKeyFrames; } -bool -CurveWidget::isSupportingOpenGLVAO() const -{ - // always running in the main thread - assert( qApp && qApp->thread() == QThread::currentThread() ); - - return _imp->_hasOpenGLVAOSupport; -} - const QFont & CurveWidget::getTextFont() const { diff --git a/Gui/CurveWidget.h b/Gui/CurveWidget.h index d8fd36ab0f..c412829863 100644 --- a/Gui/CurveWidget.h +++ b/Gui/CurveWidget.h @@ -131,6 +131,10 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON virtual void getBackgroundColour(double &r, double &g, double &b) const OVERRIDE FINAL; virtual unsigned int getCurrentRenderScale() const OVERRIDE FINAL { return 0; } + virtual RectD getViewportRect() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual void getCursorPosition(double& x, double& y) const OVERRIDE FINAL; + + virtual void saveOpenGLContext() OVERRIDE FINAL; virtual void restoreOpenGLContext() OVERRIDE FINAL; @@ -221,8 +225,6 @@ public Q_SLOTS: const QFont & getFont() const; const SelectedKeys & getSelectedKeyFrames() const; - bool isSupportingOpenGLVAO() const; - private: diff --git a/Gui/CurveWidgetPrivate.cpp b/Gui/CurveWidgetPrivate.cpp index d4c8df98b5..25849e36ab 100644 --- a/Gui/CurveWidgetPrivate.cpp +++ b/Gui/CurveWidgetPrivate.cpp @@ -82,7 +82,6 @@ CurveWidgetPrivate::CurveWidgetPrivate(Gui* gui, , _font( new QFont(appFont, appFontSize) ) , _curves() , _selectedKeyFrames() - , _hasOpenGLVAOSupport(true) , _mustSetDragOrientation(false) , _mouseDragOrientation() , _keyFramesClipBoard() diff --git a/Gui/CurveWidgetPrivate.h b/Gui/CurveWidgetPrivate.h index 5a51d0a8b8..69c585bdf3 100644 --- a/Gui/CurveWidgetPrivate.h +++ b/Gui/CurveWidgetPrivate.h @@ -162,7 +162,6 @@ class CurveWidgetPrivate QFont* _font; Curves _curves; SelectedKeys _selectedKeyFrames; - bool _hasOpenGLVAOSupport; bool _mustSetDragOrientation; QPoint _mouseDragOrientation; ///used to drag a key frame in only 1 direction (horizontal or vertical) ///the value is either (1,0) or (0,1) diff --git a/Gui/CustomParamInteract.cpp b/Gui/CustomParamInteract.cpp index 626397bfd0..7eb74bea29 100644 --- a/Gui/CustomParamInteract.cpp +++ b/Gui/CustomParamInteract.cpp @@ -227,6 +227,29 @@ CustomParamInteract::restoreOpenGLContext() glPopAttrib(); } +RectD +CustomParamInteract::getViewportRect() const +{ + RectD bbox; + { + bbox.x1 = -0.5; + bbox.y1 = -0.5; + bbox.x2 = width() + 0.5; + bbox.y2 = height() + 0.5; + } + return bbox; +} + +void +CustomParamInteract::getCursorPosition(double& x, double& y) const +{ + QPoint p = QCursor::pos(); + p = mapFromGlobal(p); + x = p.x(); + y = p.y(); +} + + void CustomParamInteract::mousePressEvent(QMouseEvent* e) { diff --git a/Gui/CustomParamInteract.h b/Gui/CustomParamInteract.h index 45a028cdd6..6a44dd4666 100644 --- a/Gui/CustomParamInteract.h +++ b/Gui/CustomParamInteract.h @@ -88,6 +88,10 @@ class CustomParamInteract virtual void restoreOpenGLContext() OVERRIDE FINAL; virtual unsigned int getCurrentRenderScale() const OVERRIDE FINAL { return 0; } + virtual RectD getViewportRect() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual void getCursorPosition(double& x, double& y) const OVERRIDE FINAL; + + private: virtual void paintGL() OVERRIDE FINAL; diff --git a/Gui/DopeSheetView.cpp b/Gui/DopeSheetView.cpp index e814a76867..1628ba9396 100644 --- a/Gui/DopeSheetView.cpp +++ b/Gui/DopeSheetView.cpp @@ -308,8 +308,6 @@ class DopeSheetViewPrivate // for clip (Reader, Time nodes) user interaction boost::shared_ptr currentEditedReader; - // others - bool hasOpenGLVAOSupport; // UI Menu *contextMenu; @@ -336,7 +334,6 @@ DopeSheetViewPrivate::DopeSheetViewPrivate(DopeSheetView *qq) : keyDragLastMovement(), eventState(DopeSheetView::esNoEditingState), currentEditedReader(), - hasOpenGLVAOSupport(true), contextMenu( new Menu(q_ptr) ) { } @@ -2654,6 +2651,29 @@ DopeSheetView::getBackgroundColour(double &r, appPTR->getCurrentSettings()->getCurveEditorBGColor(&r, &g, &b); } +RectD +DopeSheetView::getViewportRect() const +{ + RectD bbox; + { + bbox.x1 = _imp->zoomContext.left(); + bbox.y1 = _imp->zoomContext.bottom(); + bbox.x2 = _imp->zoomContext.right(); + bbox.y2 = _imp->zoomContext.top(); + } + return bbox; +} + +void +DopeSheetView::getCursorPosition(double& x, double& y) const +{ + QPoint p = QCursor::pos(); + p = mapFromGlobal(p); + QPointF mappedPos = _imp->zoomContext.toZoomCoordinates(p.x(), p.y()); + x = mappedPos.x(); + y = mappedPos.y(); +} + /** * @brief DopeSheetView::saveOpenGLContext * @@ -3066,11 +3086,6 @@ void DopeSheetView::initializeGL() { running_in_main_thread(); - - if ( !glewIsSupported("GL_ARB_vertex_array_object ") ) { - _imp->hasOpenGLVAOSupport = false; - } - _imp->generateKeyframeTextures(); } diff --git a/Gui/DopeSheetView.h b/Gui/DopeSheetView.h index 0993dd7395..f86c5f8ade 100644 --- a/Gui/DopeSheetView.h +++ b/Gui/DopeSheetView.h @@ -144,6 +144,10 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON void restoreOpenGLContext() OVERRIDE FINAL; unsigned int getCurrentRenderScale() const OVERRIDE FINAL; + virtual RectD getViewportRect() const OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual void getCursorPosition(double& x, double& y) const OVERRIDE FINAL; + + void refreshSelectionBboxAndRedraw(); public Q_SLOTS: diff --git a/Gui/Gui.h b/Gui/Gui.h index 7ceb18f837..edc9a0bb73 100644 --- a/Gui/Gui.h +++ b/Gui/Gui.h @@ -369,50 +369,22 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON void setUndoRedoStackLimit(int limit); - void setGlewVersion(const QString & version); - - void setOpenGLVersion(const QString & version); - - QString getGlewVersion() const; - - QString getOpenGLVersion() const; - - QString getBoostVersion() const; - - QString getQtVersion() const; - - QString getCairoVersion() const; - /** - * @brief Make a new rotoscoping/painting interface for the given node. + * @brief Make a new viewer interface for the given node. * This will create new widgets and enrich the interface of the viewer tab. **/ - void createNewRotoInterface(NodeGui* n); + void createNodeViewerInterface(const NodeGuiPtr& n); /** - * @brief Set the RotoGui for the node n to be active on all viewers. + * @brief Set the viewer interface for a plug-in to be the one of the node n to be active on all viewers. **/ - void setRotoInterface(NodeGui* n); + void setNodeViewerInterface(const NodeGuiPtr& n); /** - * @brief Called by Gui::deactivateRotoInterface and by NodeGraph::deleteNodepluginsly + * @brief Removes the interface for this node of any viewer + * @bool permanantly If true, the interface will be destroyed instead of hidden **/ - void removeRotoInterface(NodeGui* n, bool pluginsly); - - void onViewerRotoEvaluated(ViewerTab* viewer); - - /** - * @brief Make a new tracker interface for the given node. - * This will create new widgets and enrich the interface of the viewer tab. - **/ - void createNewTrackerInterface(NodeGui* n); - - /** - * @brief Set the TrackerGui for the node n to be active on all viewers. - **/ - void setTrackerInterface(NodeGui* n); - - void removeTrackerInterface(NodeGui* n, bool pluginsly); + void removeNodeViewerInterface(const NodeGuiPtr& n, bool permanantly); void progressStart(const NodePtr& node, const std::string &message, const std::string &messageid, bool canCancel = true); @@ -679,8 +651,6 @@ public Q_SLOTS: void onEnableRenderStatsActionTriggered(); - void onRotoSelectedToolChanged(int tool); - void onMaxVisibleDockablePanelChanged(int maxPanels); void clearAllVisiblePanels(); diff --git a/Gui/Gui.pro b/Gui/Gui.pro index b6714d21d2..91711e82ed 100644 --- a/Gui/Gui.pro +++ b/Gui/Gui.pro @@ -20,7 +20,7 @@ TARGET = Gui TEMPLATE = lib CONFIG += staticlib CONFIG += moc rcc -CONFIG += boost glew opengl qt cairo python shiboken pyside +CONFIG += boost opengl qt cairo python shiboken pyside glfw QT += gui core opengl network greaterThan(QT_MAJOR_VERSION, 4): QT += concurrent @@ -59,6 +59,8 @@ INCLUDEPATH += $$PWD INCLUDEPATH += $$PWD/NatronGui DEPENDPATH += $$PWD/NatronGui +CONFIG += glad-flags + win32-msvc* { CONFIG(64bit) { QMAKE_LFLAGS += /MACHINE:X64 @@ -185,9 +187,7 @@ SOURCES += \ RenderStatsDialog.cpp \ ResizableMessageBox.cpp \ RightClickableWidget.cpp \ - RotoGui.cpp \ RotoPanel.cpp \ - RotoUndoCommand.cpp \ ScaleSliderQWidget.cpp \ ScriptEditor.cpp \ ScriptTextEdit.cpp \ @@ -203,12 +203,10 @@ SOURCES += \ TableModelView.cpp \ TabWidget.cpp \ TextRenderer.cpp \ - Texture.cpp \ ticks.cpp \ ToolButton.cpp \ TimeLineGui.cpp \ TrackerPanel.cpp \ - TrackerGui.cpp \ TrackerUndoCommand.cpp \ Utils.cpp \ VerticalColorBar.cpp \ @@ -334,9 +332,7 @@ HEADERS += \ RenderStatsDialog.h \ ResizableMessageBox.h \ RightClickableWidget.h \ - RotoGui.h \ RotoPanel.h \ - RotoUndoCommand.h \ ScaleSliderQWidget.h \ ScriptEditor.h \ ScriptTextEdit.h \ @@ -352,12 +348,10 @@ HEADERS += \ TableModelView.h \ TabWidget.h \ TextRenderer.h \ - Texture.h \ ticks.h \ TimeLineGui.h \ ToolButton.h \ TrackerPanel.h \ - TrackerGui.h \ TrackerUndoCommand.h \ Utils.h \ VerticalColorBar.h \ diff --git a/Gui/Gui05.cpp b/Gui/Gui05.cpp index f48fcf10ec..46b510d098 100644 --- a/Gui/Gui05.cpp +++ b/Gui/Gui05.cpp @@ -132,7 +132,7 @@ Gui::setupUi() setVisibleProjectSettingsPanel(); - _imp->_aboutWindow = new AboutWindow(this, this); + _imp->_aboutWindow = new AboutWindow(this); _imp->_aboutWindow->hide(); _imp->shortcutEditor = new ShortCutEditor(this); diff --git a/Gui/Gui20.cpp b/Gui/Gui20.cpp index 566eb3a937..36de727efd 100644 --- a/Gui/Gui20.cpp +++ b/Gui/Gui20.cpp @@ -336,48 +336,44 @@ ViewerTab* Gui::addNewViewerTab(ViewerInstance* viewer, TabWidget* where) { - std::map rotoNodes; - std::list rotoNodesList; - std::pair currentRoto; - std::map trackerNodes; - std::list trackerNodesList; - std::pair currentTracker; if (!viewer) { return 0; } + NodesGuiList activeNodeViewerUi, nodeViewerUi; + //Don't create tracker & roto interface for file dialog preview viewer - if (viewer->getNode()->getScriptName() != NATRON_FILE_DIALOG_PREVIEW_VIEWER_NAME) { + boost::shared_ptr group = viewer->getNode()->getGroup(); + if (group) { if ( !_imp->_viewerTabs.empty() ) { - ( *_imp->_viewerTabs.begin() )->getRotoContext(&rotoNodes, ¤tRoto); - ( *_imp->_viewerTabs.begin() )->getTrackerContext(&trackerNodes, ¤tTracker); + ( *_imp->_viewerTabs.begin() )->getNodesViewerInterface(&nodeViewerUi, &activeNodeViewerUi); } else { - const NodesGuiList & allNodes = _imp->_nodeGraphArea->getAllActiveNodes(); - for (NodesGuiList::const_iterator it = allNodes.begin(); it != allNodes.end(); ++it) { - if ( (*it)->getNode()->getRotoContext() ) { - rotoNodesList.push_back( it->get() ); - if (!currentRoto.first) { - currentRoto.first = it->get(); - } - } else if ( (*it)->getNode()->isPointTrackerNode() ) { - trackerNodesList.push_back( it->get() ); - if (!currentTracker.first) { - currentTracker.first = it->get(); + NodeGraph* graph = dynamic_cast(group->getNodeGraph()); + if (!graph) { + graph = _imp->_nodeGraphArea; + } + + if (graph) { + const NodesGuiList & allNodes = graph->getAllActiveNodes(); + + std::set activeNodesPluginID; + + for (NodesGuiList::const_iterator it = allNodes.begin(); it != allNodes.end(); ++it) { + nodeViewerUi.push_back( *it ); + std::string pluginID = (*it)->getNode()->getPluginID(); + std::set::iterator found = activeNodesPluginID.find(pluginID); + if (found == activeNodesPluginID.end()) { + activeNodesPluginID.insert(pluginID); + activeNodeViewerUi.push_back(*it); + } } - } + } } } - for (std::map::iterator it = rotoNodes.begin(); it != rotoNodes.end(); ++it) { - rotoNodesList.push_back(it->first); - } - - for (std::map::iterator it = trackerNodes.begin(); it != trackerNodes.end(); ++it) { - trackerNodesList.push_back(it->first); - } - ViewerTab* tab = new ViewerTab(rotoNodesList, currentRoto.first, trackerNodesList, currentTracker.first, this, viewer, where); + ViewerTab* tab = new ViewerTab(nodeViewerUi,activeNodeViewerUi, this, viewer, where); QObject::connect( tab->getViewer(), SIGNAL(imageChanged(int,bool)), this, SLOT(onViewerImageChanged(int,bool)) ); { QMutexLocker l(&_imp->_viewerTabsMutex); diff --git a/Gui/Gui40.cpp b/Gui/Gui40.cpp index 9a081474c1..db0ced3895 100644 --- a/Gui/Gui40.cpp +++ b/Gui/Gui40.cpp @@ -44,9 +44,6 @@ #include #include // qApp -#include - -#include #include "Engine/CLArgs.h" #include "Engine/Image.h" @@ -736,49 +733,6 @@ Gui::ensureProgressPanelVisible() } } -void -Gui::setGlewVersion(const QString & version) -{ - _imp->_glewVersion = version; - _imp->_aboutWindow->updateLibrariesVersions(); -} - -void -Gui::setOpenGLVersion(const QString & version) -{ - _imp->_openGLVersion = version; - _imp->_aboutWindow->updateLibrariesVersions(); -} - -QString -Gui::getGlewVersion() const -{ - return _imp->_glewVersion; -} - -QString -Gui::getOpenGLVersion() const -{ - return _imp->_openGLVersion; -} - -QString -Gui::getBoostVersion() const -{ - return QString::fromUtf8(BOOST_LIB_VERSION); -} - -QString -Gui::getQtVersion() const -{ - return QString::fromUtf8(QT_VERSION_STR) + QString::fromUtf8(" / ") + QString::fromUtf8( qVersion() ); -} - -QString -Gui::getCairoVersion() const -{ - return QString::fromUtf8(CAIRO_VERSION_STRING) + QString::fromUtf8(" / ") + QString::fromUtf8( cairo_version_string() ); -} void Gui::onNodeNameChanged(const QString & /*name*/) diff --git a/Gui/Gui50.cpp b/Gui/Gui50.cpp index 91fd318311..88fc1dabf8 100644 --- a/Gui/Gui50.cpp +++ b/Gui/Gui50.cpp @@ -94,7 +94,6 @@ GCC_DIAG_UNUSED_PRIVATE_FIELD_ON #include "Gui/ProgressPanel.h" #include "Gui/RightClickableWidget.h" #include "Gui/RenderingProgressDialog.h" -#include "Gui/RotoGui.h" #include "Gui/ScriptEditor.h" #include "Gui/ShortCutEditor.h" #include "Gui/SpinBox.h" @@ -124,100 +123,35 @@ Gui::showErrorLog() ignore_result( lw.exec() ); } -void -Gui::createNewTrackerInterface(NodeGui* n) -{ - std::list viewers; - { - QMutexLocker l(&_imp->_viewerTabsMutex); - viewers = _imp->_viewerTabs; - } - - for (std::list::iterator it = viewers.begin(); it != viewers.end(); ++it) { - (*it)->createTrackerInterface(n); - } -} - -void -Gui::setTrackerInterface(NodeGui* n) -{ - QMutexLocker l(&_imp->_viewerTabsMutex); - - for (std::list::iterator it = _imp->_viewerTabs.begin(); it != _imp->_viewerTabs.end(); ++it) { - (*it)->setTrackerInterface(n); - } -} - -void -Gui::removeTrackerInterface(NodeGui* n, - bool permanently) -{ - QMutexLocker l(&_imp->_viewerTabsMutex); - - for (std::list::iterator it = _imp->_viewerTabs.begin(); it != _imp->_viewerTabs.end(); ++it) { - (*it)->removeTrackerInterface(n, permanently, false); - } -} - -void -Gui::onRotoSelectedToolChanged(int tool) -{ - RotoGui* roto = qobject_cast( sender() ); - - if (!roto) { - return; - } - - std::list viewers; - { - QMutexLocker l(&_imp->_viewerTabsMutex); - viewers = _imp->_viewerTabs; - } - for (std::list::iterator it = viewers.begin(); it != viewers.end(); ++it) { - (*it)->updateRotoSelectedTool(tool, roto); - } -} void -Gui::createNewRotoInterface(NodeGui* n) +Gui::createNodeViewerInterface(const NodeGuiPtr& n) { QMutexLocker l(&_imp->_viewerTabsMutex); for (std::list::iterator it = _imp->_viewerTabs.begin(); it != _imp->_viewerTabs.end(); ++it) { - (*it)->createRotoInterface(n); + (*it)->createNodeViewerInterface(n); } } void -Gui::removeRotoInterface(NodeGui* n, +Gui::removeNodeViewerInterface(const NodeGuiPtr& n, bool permanently) { QMutexLocker l(&_imp->_viewerTabsMutex); for (std::list::iterator it = _imp->_viewerTabs.begin(); it != _imp->_viewerTabs.end(); ++it) { - (*it)->removeRotoInterface(n, permanently, false); + (*it)->removeNodeViewerInterface(n, permanently, false /*setNewInterface*/); } } void -Gui::setRotoInterface(NodeGui* n) +Gui::setNodeViewerInterface(const NodeGuiPtr& n) { QMutexLocker l(&_imp->_viewerTabsMutex); for (std::list::iterator it = _imp->_viewerTabs.begin(); it != _imp->_viewerTabs.end(); ++it) { - (*it)->setRotoInterface(n); - } -} - -void -Gui::onViewerRotoEvaluated(ViewerTab* viewer) -{ - QMutexLocker l(&_imp->_viewerTabsMutex); - - for (std::list::iterator it = _imp->_viewerTabs.begin(); it != _imp->_viewerTabs.end(); ++it) { - if (*it != viewer) { - (*it)->getViewer()->redraw(); - } + (*it)->setPluginViewerInterface(n); } } diff --git a/Gui/GuiAppInstance.cpp b/Gui/GuiAppInstance.cpp index 42921203af..5d54c70862 100644 --- a/Gui/GuiAppInstance.cpp +++ b/Gui/GuiAppInstance.cpp @@ -251,7 +251,7 @@ GuiAppInstance::load(const CLArgs& cl, try { declareCurrentAppVariable_Python(); } catch (const std::exception& e) { - std::cerr << e.what() << std::endl; + throw std::runtime_error(e.what()); } _imp->_gui = new Gui(this); @@ -306,6 +306,12 @@ GuiAppInstance::load(const CLArgs& cl, if (getAppID() == 0) { + + QString missingOpenGLError; + if (!appPTR->hasPlatformNecessaryOpenGLRequirements(&missingOpenGLError)) { + throw std::runtime_error(missingOpenGLError.toStdString()); + } + appPTR->getCurrentSettings()->doOCIOStartupCheckIfNeeded(); if ( !appPTR->isShorcutVersionUpToDate() ) { @@ -460,8 +466,8 @@ GuiAppInstance::createNodeGui(const NodePtr &node, NodesGuiList selectedNodes = graph->getSelectedNodes(); NodeGuiPtr nodegui = _imp->_gui->createNodeGUI(node, args); - assert(nodegui); + if (parentMultiInstance && nodegui) { nodegui->hideGui(); @@ -471,20 +477,14 @@ GuiAppInstance::createNodeGui(const NodePtr &node, nodegui->setParentMultiInstance( boost::dynamic_pointer_cast(parentNodeGui_i) ); } - ///It needs to be here because we rely on the _nodeMapping member bool isViewer = node->isEffectViewer() != 0; if (isViewer) { _imp->_gui->createViewerGui(node); } - ///must be done after the viewer gui has been created - if ( node->isRotoPaintingNode() ) { - _imp->_gui->createNewRotoInterface( nodegui.get() ); - } + // Must be done after the viewer gui has been created + _imp->_gui->createNodeViewerInterface(nodegui); - if ( node->getEffectInstance()->isBuiltinTrackerNode() ) { - _imp->_gui->createNewTrackerInterface( nodegui.get() ); - } NodeGroup* isGroup = node->isEffectGroup(); if ( isGroup && isGroup->isSubGraphUserVisible() ) { diff --git a/Gui/GuiApplicationManager.cpp b/Gui/GuiApplicationManager.cpp index a93e8eab41..7b59f381b1 100644 --- a/Gui/GuiApplicationManager.cpp +++ b/Gui/GuiApplicationManager.cpp @@ -42,7 +42,8 @@ CLANG_DIAG_ON(uninitialized) #include "Engine/Settings.h" #include "Engine/EffectInstance.h" // PLUGINID_OFX_* - +#include "Engine/PluginActionShortcut.h" +#include "Gui/QtEnumConvert.h" #include "Gui/Gui.h" #include "Gui/GuiDefines.h" #include "Gui/KnobGuiFactory.h" @@ -1024,6 +1025,12 @@ GuiApplicationManager::onPluginLoaded(Plugin* plugin) if ( plugin->getIsUserCreatable() ) { _imp->addKeybind(shortcutGrouping.toStdString(), pluginID.toStdString(), pluginLabel.toStdString(), modifiers, symbol); } + + const std::list& shortcuts = plugin->getShortcuts(); + std::string pluginShortcutGroup = plugin->getPluginShortcutGroup().toStdString(); + for (std::list::const_iterator it = shortcuts.begin(); it != shortcuts.end(); ++it) { + _imp->addKeybind(pluginShortcutGroup, it->actionID, it->actionLabel, QtEnumConvert::toQtModifiers(it->modifiers), QtEnumConvert::toQtKey(it->key)); + } } // GuiApplicationManager::onPluginLoaded void diff --git a/Gui/GuiApplicationManager10.cpp b/Gui/GuiApplicationManager10.cpp index 43e61fe418..13ea3db99a 100644 --- a/Gui/GuiApplicationManager10.cpp +++ b/Gui/GuiApplicationManager10.cpp @@ -151,7 +151,7 @@ GuiApplicationManager::loadBuiltinNodePlugins(std::mapgetPluginID().c_str(), reader->getPluginLabel().c_str(), "", QStringList(), false, false, readerPlugin, false, reader->getMajorVersion(), reader->getMinorVersion(), false); + registerPlugin(QString(), pgrp, reader->getPluginID().c_str(), reader->getPluginLabel().c_str(), "", QStringList(), false, false, readerPlugin, false, reader->getMajorVersion(), reader->getMinorVersion(), false); std::vector extensions = reader->supportedFileFormats(); for (U32 k = 0; k < extensions.size(); ++k) { @@ -178,7 +178,7 @@ GuiApplicationManager::loadBuiltinNodePlugins(std::mapgetPluginID().c_str(), writer->getPluginLabel().c_str(), "", QStringList(), false, false, writerPlugin, false, writer->getMajorVersion(), writer->getMinorVersion(), false); + registerPlugin(QString() , pgrp, writer->getPluginID().c_str(), writer->getPluginLabel().c_str(), "", QStringList(), false, false, writerPlugin, false, writer->getMajorVersion(), writer->getMinorVersion(), false); std::vector extensions = writer->supportedFileFormats(); @@ -896,31 +896,6 @@ GuiApplicationManager::populateShortcuts() registerKeybind(kShortcutGroupPlayer, kShortcutIDActionPlayerPlaybackIn, kShortcutDescActionPlayerPlaybackIn, Qt::AltModifier, Qt::Key_I); registerKeybind(kShortcutGroupPlayer, kShortcutIDActionPlayerPlaybackOut, kShortcutDescActionPlayerPlaybackOut, Qt::AltModifier, Qt::Key_O); - ///Roto - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoDelete, kShortcutDescActionRotoDelete, Qt::NoModifier, Qt::Key_Backspace); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoCloseBezier, kShortcutDescActionRotoCloseBezier, Qt::NoModifier, Qt::Key_Enter); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoSelectAll, kShortcutDescActionRotoSelectAll, Qt::ControlModifier, Qt::Key_A); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoSelectionTool, kShortcutDescActionRotoSelectionTool, Qt::NoModifier, Qt::Key_Q); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoAddTool, kShortcutDescActionRotoAddTool, Qt::NoModifier, Qt::Key_D); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoEditTool, kShortcutDescActionRotoEditTool, Qt::NoModifier, Qt::Key_V); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoBrushTool, kShortcutDescActionRotoBrushTool, Qt::NoModifier, Qt::Key_N); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoCloneTool, kShortcutDescActionRotoCloneTool, Qt::NoModifier, Qt::Key_C); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoEffectTool, kShortcutDescActionRotoEffectTool, Qt::NoModifier, Qt::Key_X); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoColorTool, kShortcutDescActionRotoColorTool, Qt::NoModifier, Qt::Key_E); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoNudgeLeft, kShortcutDescActionRotoNudgeLeft, Qt::AltModifier, Qt::Key_Left); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoNudgeBottom, kShortcutDescActionRotoNudgeBottom, Qt::AltModifier, Qt::Key_Down); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoNudgeRight, kShortcutDescActionRotoNudgeRight, Qt::AltModifier, Qt::Key_Right); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoNudgeTop, kShortcutDescActionRotoNudgeTop, Qt::AltModifier, Qt::Key_Up); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoSmooth, kShortcutDescActionRotoSmooth, Qt::NoModifier, Qt::Key_Z); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoCuspBezier, kShortcutDescActionRotoCuspBezier, Qt::ShiftModifier, Qt::Key_Z); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoRemoveFeather, kShortcutDescActionRotoRemoveFeather, Qt::ShiftModifier, Qt::Key_E); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoLinkToTrack, kShortcutDescActionRotoLinkToTrack, Qt::NoModifier, (Qt::Key)0); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoUnlinkToTrack, kShortcutDescActionRotoUnlinkToTrack, - Qt::NoModifier, (Qt::Key)0); - registerKeybind(kShortcutGroupRoto, kShortcutIDActionRotoLockCurve, kShortcutDescActionRotoLockCurve, - Qt::ShiftModifier, Qt::Key_L); - - ///Tracking registerKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingSelectAll, kShortcutDescActionTrackingSelectAll, Qt::ControlModifier, Qt::Key_A); registerKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingDelete, kShortcutDescActionTrackingDelete, Qt::NoModifier, Qt::Key_Backspace); diff --git a/Gui/GuiFwd.h b/Gui/GuiFwd.h index e3c9c465f5..1c3c402eeb 100644 --- a/Gui/GuiFwd.h +++ b/Gui/GuiFwd.h @@ -182,7 +182,6 @@ class TabWidget; class TableItem; class TableModel; class TableView; -class Texture; class TimeLineGui; class ToolButton; class TrackerGui; @@ -208,10 +207,11 @@ class PyPanel; class PyTabWidget; NATRON_PYTHON_NAMESPACE_EXIT; +typedef boost::shared_ptr NodeViewerContextPtr; +typedef boost::weak_ptr NodeViewerContextWPtr; typedef boost::weak_ptr NodeGuiWPtr; typedef boost::shared_ptr NodeGuiPtr; typedef std::list NodesGuiList; -typedef boost::shared_ptr GLTexturePtr; typedef boost::shared_ptr ProgressTaskInfoPtr; typedef boost::shared_ptr KnobGuiPtr; typedef boost::weak_ptr KnobGuiWPtr; diff --git a/Gui/GuiPrivate.cpp b/Gui/GuiPrivate.cpp index 913e2e25b7..fd17d9e247 100644 --- a/Gui/GuiPrivate.cpp +++ b/Gui/GuiPrivate.cpp @@ -117,7 +117,6 @@ GCC_DIAG_ON(unused-parameter) #include "Gui/PythonPanels.h" #include "Gui/RenderingProgressDialog.h" #include "Gui/RightClickableWidget.h" -#include "Gui/RotoGui.h" #include "Gui/ScriptEditor.h" #include "Gui/SequenceFileDialog.h" #include "Gui/ShortCutEditor.h" @@ -249,8 +248,6 @@ GuiPrivate::GuiPrivate(GuiAppInstance* app, , _aboutWindow(0) , openedPanelsMutex() , openedPanels() - , _openGLVersion() - , _glewVersion() , _toolButtonMenuOpened(NULL) , aboutToCloseMutex() , _aboutToClose(false) diff --git a/Gui/GuiPrivate.h b/Gui/GuiPrivate.h index 6e8c7c33c4..3c8a4eff10 100644 --- a/Gui/GuiPrivate.h +++ b/Gui/GuiPrivate.h @@ -227,8 +227,6 @@ struct GuiPrivate ///list of the currently opened property panels mutable QMutex openedPanelsMutex; std::list openedPanels; - QString _openGLVersion; - QString _glewVersion; QToolButton* _toolButtonMenuOpened; QMutex aboutToCloseMutex; bool _aboutToClose; diff --git a/Gui/Histogram.cpp b/Gui/Histogram.cpp index 98d17928d3..88ef6a84cc 100644 --- a/Gui/Histogram.cpp +++ b/Gui/Histogram.cpp @@ -45,6 +45,7 @@ GCC_DIAG_UNUSED_PRIVATE_FIELD_ON #include "Engine/HistogramCPU.h" #include "Engine/Image.h" #include "Engine/Node.h" +#include "Engine/Texture.h" #include "Engine/ViewerInstance.h" #include "Gui/ActionShortcuts.h" @@ -59,7 +60,6 @@ GCC_DIAG_UNUSED_PRIVATE_FIELD_ON #include "Gui/NodeGraph.h" #include "Gui/Shaders.h" #include "Gui/TextRenderer.h" -#include "Gui/Texture.h" #include "Gui/ViewerGL.h" #include "Gui/ViewerTab.h" #include "Gui/ZoomContext.h" @@ -102,8 +102,6 @@ struct HistogramPrivate , mode(Histogram::eDisplayModeRGB) , oldClick() , zoomCtx() - , supportsGLSL(true) - , hasOpenGLVAOSupport(true) , state(eEventStateNone) , hasBeenModifiedSinceResize(false) , _baseAxisColor(118, 215, 90, 255) @@ -190,8 +188,6 @@ struct HistogramPrivate Histogram::DisplayModeEnum mode; QPoint oldClick; /// the last click pressed, in widget coordinates [ (0,0) == top left corner ] ZoomContext zoomCtx; - bool supportsGLSL; - bool hasOpenGLVAOSupport; EventStateEnum state; bool hasBeenModifiedSinceResize; //< true if the user panned or zoomed since the last resize QColor _baseAxisColor; @@ -679,55 +675,37 @@ Histogram::initializeGL() assert( qApp && qApp->thread() == QThread::currentThread() ); assert( QGLContext::currentContext() == context() ); - GLenum err = glewInit(); - if (GLEW_OK != err) { - /* Problem: glewInit failed, something is seriously wrong. */ - Dialogs::errorDialog( tr("OpenGL/GLEW error").toStdString(), - (const char*)glewGetErrorString(err) ); - } - if ( !QGLShaderProgram::hasOpenGLShaderPrograms( context() ) ) { - _imp->supportsGLSL = false; - } else { #ifdef NATRON_HISTOGRAM_USING_OPENGL - _imp->histogramComputingShader.reset( new QGLShaderProgram( context() ) ); - if ( !_imp->histogramComputingShader->addShaderFromSourceCode(QGLShader::Vertex, histogramComputationVertex_vert) ) { - qDebug() << _imp->histogramComputingShader->log(); - } - if ( !_imp->histogramComputingShader->addShaderFromSourceCode(QGLShader::Fragment, histogramComputation_frag) ) { - qDebug() << _imp->histogramComputingShader->log(); - } - if ( !_imp->histogramComputingShader->link() ) { - qDebug() << _imp->histogramComputingShader->log(); - } - - _imp->histogramMaximumShader.reset( new QGLShaderProgram( context() ) ); - if ( !_imp->histogramMaximumShader->addShaderFromSourceCode(QGLShader::Fragment, histogramMaximum_frag) ) { - qDebug() << _imp->histogramMaximumShader->log(); - } - if ( !_imp->histogramMaximumShader->link() ) { - qDebug() << _imp->histogramMaximumShader->log(); - } - - _imp->histogramRenderingShader.reset( new QGLShaderProgram( context() ) ); - if ( !_imp->histogramRenderingShader->addShaderFromSourceCode(QGLShader::Vertex, histogramRenderingVertex_vert) ) { - qDebug() << _imp->histogramRenderingShader->log(); - } - if ( !_imp->histogramRenderingShader->addShaderFromSourceCode(QGLShader::Fragment, histogramRendering_frag) ) { - qDebug() << _imp->histogramRenderingShader->log(); - } - if ( !_imp->histogramRenderingShader->link() ) { - qDebug() << _imp->histogramRenderingShader->log(); - } -#endif + _imp->histogramComputingShader.reset( new QGLShaderProgram( context() ) ); + if ( !_imp->histogramComputingShader->addShaderFromSourceCode(QGLShader::Vertex, histogramComputationVertex_vert) ) { + qDebug() << _imp->histogramComputingShader->log(); + } + if ( !_imp->histogramComputingShader->addShaderFromSourceCode(QGLShader::Fragment, histogramComputation_frag) ) { + qDebug() << _imp->histogramComputingShader->log(); + } + if ( !_imp->histogramComputingShader->link() ) { + qDebug() << _imp->histogramComputingShader->log(); } - if ( !glewIsSupported("GL_ARB_vertex_array_object " // BindVertexArray, DeleteVertexArrays, GenVertexArrays, IsVertexArray (VAO), core since 3.0 - ) ) { - _imp->hasOpenGLVAOSupport = false; + _imp->histogramMaximumShader.reset( new QGLShaderProgram( context() ) ); + if ( !_imp->histogramMaximumShader->addShaderFromSourceCode(QGLShader::Fragment, histogramMaximum_frag) ) { + qDebug() << _imp->histogramMaximumShader->log(); + } + if ( !_imp->histogramMaximumShader->link() ) { + qDebug() << _imp->histogramMaximumShader->log(); } -#ifdef NATRON_HISTOGRAM_USING_OPENGL + _imp->histogramRenderingShader.reset( new QGLShaderProgram( context() ) ); + if ( !_imp->histogramRenderingShader->addShaderFromSourceCode(QGLShader::Vertex, histogramRenderingVertex_vert) ) { + qDebug() << _imp->histogramRenderingShader->log(); + } + if ( !_imp->histogramRenderingShader->addShaderFromSourceCode(QGLShader::Fragment, histogramRendering_frag) ) { + qDebug() << _imp->histogramRenderingShader->log(); + } + if ( !_imp->histogramRenderingShader->link() ) { + qDebug() << _imp->histogramRenderingShader->log(); + } // enable globally : no glPushAttrib() glEnable(GL_TEXTURE_RECTANGLE_ARB); @@ -1550,12 +1528,6 @@ HistogramPrivate::drawScale() assert( qApp && qApp->thread() == QThread::currentThread() ); assert( QGLContext::currentContext() == widget->context() ); - { - GLenum _glerror_ = glGetError(); - if (_glerror_ != GL_NO_ERROR) { - std::cout << "GL_ERROR :" << __FILE__ << " " << __LINE__ << " " << gluErrorString(_glerror_) << std::endl; - } - } glCheckError(); QPointF btmLeft = zoomCtx.toZoomCoordinates(0, widget->height() - 1); diff --git a/Gui/HostOverlay.cpp b/Gui/HostOverlay.cpp index 697996680e..3b8ada7947 100644 --- a/Gui/HostOverlay.cpp +++ b/Gui/HostOverlay.cpp @@ -151,6 +151,19 @@ DefaultInteractI::penDown(double /*time*/, return false; } + +bool +DefaultInteractI::penDoubleClicked(double /*time*/, + const RenderScale& /*renderScale*/, + ViewIdx /*view*/, + const OfxPointD& /*pscale*/, + const QPointF& /*lastPenPos*/, + const QPointF & /*penPos*/, + const QPoint & /*penPosViewport*/) +{ + return false; +} + bool DefaultInteractI::keyDown(double /*time*/, const RenderScale& /*renderScale*/, @@ -2704,6 +2717,29 @@ HostOverlay::penDown(double time, return false; } +bool +HostOverlay::penDoubleClicked(double time, + const RenderScale& renderScale, + ViewIdx view, + const QPointF &penPos, + const QPoint &penPosViewport) +{ + OfxPointD pscale; + + n_getPixelScale(pscale.x, pscale.y); + for (InteractList::iterator it = _imp->interacts.begin(); it != _imp->interacts.end(); ++it) { + if ( (*it)->penDoubleClicked(time, renderScale, view, pscale, _imp->lastPenPos, penPos, penPosViewport) ) { + _imp->lastPenPos = penPos; + + return true; + } + } + + _imp->lastPenPos = penPos; + + return false; +} + bool TransformInteract::keyDown(double /*time*/, const RenderScale& /*renderScale*/, diff --git a/Gui/HostOverlay.h b/Gui/HostOverlay.h index 779d9f9a80..3aab632cb6 100644 --- a/Gui/HostOverlay.h +++ b/Gui/HostOverlay.h @@ -90,6 +90,13 @@ class DefaultInteractI const QPointF &penPos, const QPoint &penPosViewport, double pressure); + virtual bool penDoubleClicked(double time, + const RenderScale& renderScale, + ViewIdx view, + const OfxPointD& pscale, + const QPointF& lastPenPos, + const QPointF &penPos, + const QPoint &penPosViewport); virtual bool keyDown(double time, const RenderScale& renderScale, ViewIdx view, @@ -164,6 +171,12 @@ class HostOverlay const QPoint &penPosViewport, double pressure); + bool penDoubleClicked(double time, + const RenderScale& renderScale, + ViewIdx view, + const QPointF &penPos, + const QPoint &penPosViewport); + bool keyDown(double time, const RenderScale& renderScale, diff --git a/Gui/KnobGui.h b/Gui/KnobGui.h index e4bac2d1c8..2e22026410 100644 --- a/Gui/KnobGui.h +++ b/Gui/KnobGui.h @@ -243,6 +243,8 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON virtual void getBackgroundColour(double &r, double &g, double &b) const OVERRIDE FINAL; virtual void saveOpenGLContext() OVERRIDE FINAL; virtual void restoreOpenGLContext() OVERRIDE FINAL; + virtual RectD getViewportRect() const OVERRIDE FINAL; + virtual void getCursorPosition(double& x, double& y) const OVERRIDE FINAL; ///Should set to the underlying knob the gui ptr virtual void setKnobGuiPointer() OVERRIDE FINAL; diff --git a/Gui/KnobGui20.cpp b/Gui/KnobGui20.cpp index a12cde691b..9d3392d870 100644 --- a/Gui/KnobGui20.cpp +++ b/Gui/KnobGui20.cpp @@ -541,6 +541,23 @@ KnobGui::restoreOpenGLContext() } } +RectD +KnobGui::getViewportRect() const +{ + if (_imp->customInteract) { + return _imp->customInteract->getViewportRect(); + } + return RectD(); +} + +void +KnobGui::getCursorPosition(double& x, double& y) const +{ + if (_imp->customInteract) { + _imp->customInteract->getCursorPosition(x, y); + } +} + ///Should set to the underlying knob the gui ptr void KnobGui::setKnobGuiPointer() diff --git a/Gui/KnobGuiButton.cpp b/Gui/KnobGuiButton.cpp index a3f5926a64..2504efefd3 100644 --- a/Gui/KnobGuiButton.cpp +++ b/Gui/KnobGuiButton.cpp @@ -103,28 +103,29 @@ KnobGuiButton::createWidget(QHBoxLayout* layout) if ( !onIconFilePath.isEmpty() && !QFile::exists(onIconFilePath) ) { - ///Search all natron paths for a file - - QStringList paths = appPTR->getAllNonOFXPluginsPaths(); - for (int i = 0; i < paths.size(); ++i) { - QString tmp = paths[i] + QLatin1Char('/') + onIconFilePath; - if ( QFile::exists(tmp) ) { - onIconFilePath = tmp; - break; + + EffectInstance* isEffect = dynamic_cast(knob->getHolder()); + if (isEffect) { + //Prepend the resources path + QString resourcesPath = QString::fromUtf8(isEffect->getNode()->getPluginResourcesPath().c_str()); + if (!resourcesPath.endsWith(QLatin1Char('/'))) { + resourcesPath += QLatin1Char('/'); } + onIconFilePath.prepend(resourcesPath); } + } if ( !offIconFilePath.isEmpty() && !QFile::exists(offIconFilePath) ) { - ///Search all natron paths for a file - - QStringList paths = appPTR->getAllNonOFXPluginsPaths(); - for (int i = 0; i < paths.size(); ++i) { - QString tmp = paths[i] + QLatin1Char('/') + offIconFilePath; - if ( QFile::exists(tmp) ) { - offIconFilePath = tmp; - break; + EffectInstance* isEffect = dynamic_cast(knob->getHolder()); + if (isEffect) { + //Prepend the resources path + QString resourcesPath = QString::fromUtf8(isEffect->getNode()->getPluginResourcesPath().c_str()); + if (!resourcesPath.endsWith(QLatin1Char('/'))) { + resourcesPath += QLatin1Char('/'); } + offIconFilePath.prepend(resourcesPath); } + } bool checkable = knob->getIsCheckable(); diff --git a/Gui/NodeGraphUndoRedo.cpp b/Gui/NodeGraphUndoRedo.cpp index 668edc3ce6..5001c19248 100644 --- a/Gui/NodeGraphUndoRedo.cpp +++ b/Gui/NodeGraphUndoRedo.cpp @@ -1145,15 +1145,7 @@ LoadNodePresetsCommand::getListAsShared(const std::list< NodeWPtr >& original, LoadNodePresetsCommand::~LoadNodePresetsCommand() { -// if (_isUndone) { -// for (NodesList::iterator it = _newChildren.begin(); it != _newChildren.end(); ++it) { -// (*it)->getDagGui()->deleteNodepluginsly(*it); -// } -// } else { -// for (NodesList::iterator it = _oldChildren.begin(); it != _oldChildren.end(); ++it) { -// (*it)->getDagGui()->deleteNodepluginsly(*it); -// } -// } + } void diff --git a/Gui/NodeGui.cpp b/Gui/NodeGui.cpp index d0f8439991..90c860b270 100644 --- a/Gui/NodeGui.cpp +++ b/Gui/NodeGui.cpp @@ -64,6 +64,7 @@ CLANG_DIAG_ON(uninitialized) #include "Engine/Settings.h" #include "Engine/ViewerInstance.h" +#include "Gui/ActionShortcuts.h" #include "Gui/BackdropGui.h" #include "Gui/Button.h" #include "Gui/CurveEditor.h" @@ -77,9 +78,11 @@ CLANG_DIAG_ON(uninitialized) #include "Gui/GuiDefines.h" #include "Gui/KnobGui.h" #include "Gui/KnobGuiString.h" +#include "Gui/QtEnumConvert.h" #include "Gui/Label.h" #include "Gui/LineEdit.h" #include "Gui/MultiInstancePanel.h" +#include "Gui/Menu.h" #include "Gui/NodeClipBoard.h" #include "Gui/NodeGraph.h" #include "Gui/NodeGraphUndoRedo.h" @@ -237,6 +240,7 @@ NodeGui::initialize(NodeGraph* dag, QObject::connect( internalNode.get(), SIGNAL(outputLayerChanged()), this, SLOT(onOutputLayerChanged()) ); QObject::connect( internalNode.get(), SIGNAL(hideInputsKnobChanged(bool)), this, SLOT(onHideInputsKnobValueChanged(bool)) ); QObject::connect( internalNode.get(), SIGNAL(availableViewsChanged()), this, SLOT(onAvailableViewsChanged()) ); + QObject::connect( internalNode.get(), SIGNAL(rightClickMenuKnobPopulated()), this, SLOT(onRightClickMenuKnobPopulated())); QObject::connect( this, SIGNAL(previewImageComputed()), this, SLOT(onPreviewImageComputed()) ); setCacheMode(DeviceCoordinateCache); @@ -409,12 +413,8 @@ NodeGui::ensurePanelCreated() QObject::connect( _settingsPanel, SIGNAL(nameChanged(QString)), this, SLOT(setName(QString)) ); QObject::connect( _settingsPanel, SIGNAL(closeChanged(bool)), this, SLOT(onSettingsPanelClosed(bool)) ); QObject::connect( _settingsPanel, SIGNAL(colorChanged(QColor)), this, SLOT(onSettingsPanelColorChanged(QColor)) ); - if ( getNode()->isRotoPaintingNode() ) { - _graph->getGui()->setRotoInterface(this); - } - if ( ( getNode()->isTrackerNodePlugin() && !getNode()->getParentMultiInstance() ) || getNode()->getEffectInstance()->isBuiltinTrackerNode() ) { - _graph->getGui()->setTrackerInterface(this); - } + + _graph->getGui()->setNodeViewerInterface(thisShared); } gui->addNodeGuiToCurveEditor(thisShared); @@ -1658,12 +1658,8 @@ NodeGui::setUserSelected(bool b) _settingsPanel->setSelected(b); _settingsPanel->update(); if ( b && isSettingsPanelVisible() ) { - if ( getNode()->isRotoPaintingNode() ) { - _graph->getGui()->setRotoInterface(this); - } - if ( ( getNode()->isTrackerNodePlugin() && !getNode()->getParentMultiInstance() ) || getNode()->getEffectInstance()->isBuiltinTrackerNode() ) { - _graph->getGui()->setTrackerInterface(this); - } + NodeGuiPtr thisShared = shared_from_this(); + _graph->getGui()->setNodeViewerInterface(thisShared); } } @@ -1843,12 +1839,9 @@ NodeGui::showGui() if (_panelOpenedBeforeDeactivate) { setVisibleSettingsPanel(true); } - if ( node->isRotoPaintingNode() ) { - _graph->getGui()->setRotoInterface(this); - } - if ( ( node->isTrackerNodePlugin() && !node->getParentMultiInstance() ) || node->getEffectInstance()->isBuiltinTrackerNode() ) { - _graph->getGui()->setTrackerInterface(this); - } + NodeGuiPtr thisShared = shared_from_this(); + _graph->getGui()->setNodeViewerInterface(thisShared); + OfxEffectInstance* ofxNode = dynamic_cast( node->getEffectInstance().get() ); if (ofxNode) { ofxNode->effectInstance()->beginInstanceEditAction(); @@ -1937,12 +1930,8 @@ NodeGui::hideGui() setVisibleSettingsPanel(false); } - if ( node->isRotoPaintingNode() ) { - _graph->getGui()->removeRotoInterface(this, false); - } - if ( node->isPointTrackerNode() && node->getParentMultiInstanceName().empty() ) { - _graph->getGui()->removeTrackerInterface(this, false); - } + NodeGuiPtr thisShared = shared_from_this(); + _graph->getGui()->removeNodeViewerInterface(thisShared, false); NodeGroup* isGrp = node->isEffectGroup(); if ( isGrp && isGrp->isSubGraphUserVisible() ) { @@ -2526,9 +2515,10 @@ NodeGui::destroyGui() removeUndoStack(); + NodeGuiPtr thisShared = shared_from_this(); + { ///Remove from the nodegraph containers - NodeGuiPtr thisShared = shared_from_this(); _graph->deleteNodePermanantly(thisShared); } @@ -2536,15 +2526,8 @@ NodeGui::destroyGui() NodePtr internalNode = _internalNode.lock(); assert(internalNode); - //Remove roto - if ( internalNode->isRotoPaintingNode() ) { - guiObj->removeRotoInterface(this, true); - } - - //Remove tracker - if ( internalNode->isTrackerNodePlugin() || internalNode->getEffectInstance()->isBuiltinTrackerNode() ) { - guiObj->removeTrackerInterface(this, true); - } + //Remove viewer UI + guiObj->removeNodeViewerInterface(thisShared, true); //Remove from curve editor @@ -3380,6 +3363,18 @@ NodeGui::onOverlayPenDownDefault(double time, return false; } +bool +NodeGui::onOverlayPenDoubleClickedDefault(double time, + const RenderScale& renderScale, + ViewIdx view, const QPointF & viewportPos, const QPointF & pos) +{ + if (_hostOverlay) { + return _hostOverlay->penDoubleClicked(time, renderScale, view, pos, viewportPos.toPoint()); + } + + return false; +} + bool NodeGui::onOverlayPenMotionDefault(double time, const RenderScale& renderScale, @@ -3671,6 +3666,219 @@ NodeGui::onHideInputsKnobValueChanged(bool /*hidden*/) refreshEdgesVisility(); } +class NodeUndoRedoCommand : public QUndoCommand +{ + + boost::shared_ptr _command; +public: + + NodeUndoRedoCommand(const UndoCommandPtr& command) + : QUndoCommand() + , _command(command) + { + setText(QString::fromUtf8(command->getText().c_str())); + } + + virtual ~NodeUndoRedoCommand() + { + + } + + + virtual void redo() OVERRIDE FINAL + { + _command->redo(); + } + + virtual void undo() OVERRIDE FINAL + { + _command->undo(); + } + + virtual int id() const OVERRIDE FINAL WARN_UNUSED_RETURN + { + return kNodeUndoChangeCommandCompressionID; + } + + virtual bool mergeWith(const QUndoCommand* other) OVERRIDE FINAL WARN_UNUSED_RETURN + { + const NodeUndoRedoCommand* o = dynamic_cast(other); + if (!o) { + return false; + } + return _command->mergeWith(o->_command); + } +}; + +void +NodeGui::pushUndoCommand(const UndoCommandPtr& command) +{ + NodeSettingsPanel* panel = getSettingPanel(); + if (!panel) { + command->redo(); + } else { + panel->pushUndoCommand(new NodeUndoRedoCommand(command)); + } +} + +void +NodeGui::setCurrentCursor(CursorEnum defaultCursor) +{ + NodePtr node = getNode(); + if (!node) { + return; + } + OverlaySupport* overlayInteract= node->getEffectInstance()->getCurrentViewportForOverlays(); + if (!overlayInteract) { + return; + } + ViewerGL* isViewer = dynamic_cast(overlayInteract); + if (!isViewer) { + return; + } + + if (defaultCursor == eCursorDefault) { + isViewer->unsetCursor(); + } + Qt::CursorShape qtCursor; + bool ok = QtEnumConvert::toQtCursor(defaultCursor, &qtCursor); + if (!ok) { + return; + } + isViewer->setCursor(qtCursor); +} + +bool +NodeGui::setCurrentCursor(const QString& customCursorFilePath) +{ + NodePtr node = getNode(); + if (!node) { + return false; + } + OverlaySupport* overlayInteract= node->getEffectInstance()->getCurrentViewportForOverlays(); + if (!overlayInteract) { + return false; + } + ViewerGL* isViewer = dynamic_cast(overlayInteract); + if (!isViewer) { + return false; + } + if (customCursorFilePath.isEmpty()) { + return false; + } + + QString cursorFilePath = customCursorFilePath; + + if (!QFile::exists(customCursorFilePath)) { + QString resourcesPath = QString::fromUtf8(getNode()->getPluginResourcesPath().c_str()); + if (!resourcesPath.endsWith(QLatin1Char('/'))) { + resourcesPath += QLatin1Char('/'); + } + cursorFilePath.prepend(resourcesPath); + } + + if (!QFile::exists(cursorFilePath)) { + return false; + } + + QPixmap pix(cursorFilePath); + if (pix.isNull()) { + return false; + } + QCursor c(pix); + isViewer->setCursor(c); + return true; + +} + +void +NodeGui::onRightClickMenuKnobPopulated() +{ + NodePtr node = getNode(); + if (!node) { + return; + } + OverlaySupport* overlayInteract= node->getEffectInstance()->getCurrentViewportForOverlays(); + if (!overlayInteract) { + return; + } + ViewerGL* isViewer = dynamic_cast(overlayInteract); + if (!isViewer) { + return; + } + + KnobSignalSlotHandler* handler = qobject_cast(sender()); + if (!handler) { + return; + } + KnobPtr rightClickKnob = handler->getKnob(); + if (!rightClickKnob) { + return; + } + KnobChoice* isChoice = dynamic_cast(rightClickKnob.get()); + if (!isChoice) { + return; + } + std::vector entries = isChoice->getEntries_mt_safe(); + if (entries.empty()) { + return; + } + + Menu m(isViewer); + for (std::vector::iterator it = entries.begin(); it != entries.end(); ++it) { + KnobPtr knob = node->getKnobByName(*it); + if(!knob) { + // Plug-in specified invalid knob name in the menu + continue; + } + KnobButton* button = dynamic_cast(knob.get()); + if (!button) { + // Plug-in must only use buttons inside menu + continue; + } + bool checkable = button->getIsCheckable(); + ActionWithShortcut* action = new ActionWithShortcut(node->getPlugin()->getPluginShortcutGroup().toStdString(), + button->getName(), + button->getLabel(), + &m); + if (checkable) { + action->setCheckable(true); + action->setChecked(button->getValue()); + } + QObject::connect(action, SIGNAL(triggered()), this, SLOT(onRightClickActionTriggered())); + m.addAction(action); + } + + m.exec(QCursor::pos()); +} + +void +NodeGui::onRightClickActionTriggered() +{ + ActionWithShortcut* action = dynamic_cast(sender()); + if (!action) { + return; + } + const std::vector >& shortcuts = action->getShortcuts(); + assert(!shortcuts.empty()); + std::string knobName = shortcuts.front().first.toStdString(); + KnobPtr knob = getNode()->getKnobByName(knobName); + if (!knob) { + // Plug-in specified invalid knob name in the menu + return; + } + KnobButton* button = dynamic_cast(knob.get()); + if (!button) { + // Plug-in must only use buttons inside menu + return; + } + if (button->getIsCheckable()) { + button->setValue(!button->getValue()); + } else { + button->trigger(); + } +} + NATRON_NAMESPACE_EXIT; NATRON_NAMESPACE_USING; diff --git a/Gui/NodeGui.h b/Gui/NodeGui.h index 7b42aa1b9e..47534abbae 100644 --- a/Gui/NodeGui.h +++ b/Gui/NodeGui.h @@ -355,6 +355,9 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON virtual bool onOverlayPenDownDefault(double time, const RenderScale& renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) OVERRIDE FINAL WARN_UNUSED_RETURN; + virtual bool onOverlayPenDoubleClickedDefault(double time, + const RenderScale& renderScale, + ViewIdx view, const QPointF & viewportPos, const QPointF & pos) OVERRIDE FINAL WARN_UNUSED_RETURN; virtual bool onOverlayPenMotionDefault(double time, const RenderScale& renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure) OVERRIDE FINAL WARN_UNUSED_RETURN; @@ -389,6 +392,17 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON void onKnobExpressionChanged(const KnobGui* knob); + virtual void pushUndoCommand(const UndoCommandPtr& command) OVERRIDE FINAL; + + /** + * @brief Set the cursor to be one of the default cursor. + * @returns True if it successfully set the cursor, false otherwise. + * Note: this can only be called during an overlay interact action. + **/ + virtual void setCurrentCursor(CursorEnum defaultCursor) OVERRIDE FINAL; + + virtual bool setCurrentCursor(const QString& customCursorFilePath) OVERRIDE FINAL; + protected: virtual int getBaseDepth() const { return 20; } @@ -410,6 +424,10 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON public Q_SLOTS: + void onRightClickActionTriggered(); + + void onRightClickMenuKnobPopulated(); + void setColorFromGrouping(); void onHideInputsKnobValueChanged(bool hidden); diff --git a/Gui/NodeViewerContext.cpp b/Gui/NodeViewerContext.cpp index f8b0a41c10..10c2ef75b2 100644 --- a/Gui/NodeViewerContext.cpp +++ b/Gui/NodeViewerContext.cpp @@ -38,6 +38,7 @@ #include "Engine/KnobTypes.h" #include "Engine/Node.h" +#include "Engine/EffectInstance.h" #include "Engine/Plugin.h" #include "Gui/ActionShortcuts.h" @@ -88,7 +89,7 @@ struct NodeViewerContextPrivate } - void createKnobs(); + void createKnobs(const KnobsVec& knobsUi); NodeGuiPtr getNode() const { @@ -133,12 +134,8 @@ NodeViewerContext::NodeViewerContext(const NodeGuiPtr& node, ViewerTab* viewer) , KnobGuiContainerI() , _imp(new NodeViewerContextPrivate(this, node, viewer)) { - _imp->mainContainer = new QWidget(viewer); - _imp->mainContainerLayout = new QVBoxLayout(_imp->mainContainer); - _imp->mainContainerLayout->setContentsMargins(0, 0, 0, 0); - _imp->mainContainerLayout->setSpacing(0); - setContainerWidget(_imp->mainContainer); + } @@ -148,6 +145,61 @@ NodeViewerContext::~NodeViewerContext() } +void +NodeViewerContext::createGui() +{ + + QObject::connect( _imp->viewer, SIGNAL(selectionRectangleChanged(bool)), this, SLOT(updateSelectionFromViewerSelectionRectangle(bool)), Qt::UniqueConnection ); + QObject::connect( _imp->viewer, SIGNAL(selectionCleared()), this, SLOT(onViewerSelectionCleared()), Qt::UniqueConnection ); + + NodeGuiPtr node = _imp->getNode(); + QObject::connect( node.get(), SIGNAL(settingsPanelClosed(bool)), this, SLOT(onNodeSettingsPanelClosed(bool)), Qt::UniqueConnection ); + + + KnobsVec knobsOrdered = node->getNode()->getEffectInstance()->getViewerUIKnobs(); + + + if (!knobsOrdered.empty()) { + _imp->createKnobs(knobsOrdered); + _imp->mainContainer = new QWidget(_imp->viewer); + _imp->mainContainerLayout = new QVBoxLayout(_imp->mainContainer); + _imp->mainContainerLayout->setContentsMargins(0, 0, 0, 0); + _imp->mainContainerLayout->setSpacing(0); + setContainerWidget(_imp->mainContainer); + } + + const KnobsVec& allKnobs = node->getNode()->getKnobs(); + KnobPage* toolbarPage = 0; + for (KnobsVec::const_iterator it = allKnobs.begin(); it != allKnobs.end(); ++it) { + KnobPage* isPage = dynamic_cast(it->get()); + if (isPage && isPage->getIsToolBar()) { + toolbarPage = isPage; + break; + } + } + if (toolbarPage) { + std::vector pageChildren = toolbarPage->getChildren(); + if (!pageChildren.empty()) { + _imp->toolbar = new QToolBar(_imp->viewer); + _imp->toolbar->setOrientation(Qt::Vertical); + + for (std::size_t i = 0; i < pageChildren.size(); ++i) { + KnobGroup* isGroup = dynamic_cast(pageChildren[i].get()); + if (isGroup) { + std::vector toolButtonChildren = isGroup->getChildren(); + for (std::size_t j = 0; j < toolButtonChildren.size(); ++j) { + KnobButton* isButton = dynamic_cast(toolButtonChildren[j].get()); + if (isButton) { + const std::string& roleShortcutID = isGroup->getName(); + _imp->addToolBarTool(isButton->getName(), isGroup->getName(), roleShortcutID, isButton->getLabel(), isButton->getHintToolTip(), isButton->getIconLabel()); + } + } + } + } + } + } +} + static void addSpacer(QBoxLayout* layout) { @@ -163,25 +215,27 @@ addSpacer(QBoxLayout* layout) } void -NodeViewerContextPrivate::createKnobs() +NodeViewerContext::onNodeSettingsPanelClosed(bool closed) { - NodeGuiPtr thisNode = getNode(); - - const KnobsVec& knobs = thisNode->getNode()->getKnobs(); - std::map knobsOrdered; - for (KnobsVec::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { - int index = (*it)->getInViewerContextIndex(); - if (index != -1) { - knobsOrdered[index] = *it; - } + if (!_imp->viewerTab) { + return; } + NodeGuiPtr node = _imp->node.lock(); + if (closed) { + _imp->viewerTab->removeNodeViewerInterface(node, false /*permanantly*/, true /*setAnother*/); + } else { + // Set the viewer interface for this plug-in to be the one of this node + _imp->viewerTab->setPluginViewerInterface(node); + } + +} - // hasKnobWithViewerInContextUI() should have been checked before +void +NodeViewerContextPrivate::createKnobs(const KnobsVec& knobsOrdered) +{ + NodeGuiPtr thisNode = getNode(); assert(!knobsOrdered.empty()); - if (knobsOrdered.empty()) { - return; - } knobsMapping.clear(); @@ -195,19 +249,19 @@ NodeViewerContextPrivate::createKnobs() KnobsVec knobsOnSameLine; - std::map::iterator next = knobsOrdered.begin(); + KnobsVec::const_iterator next = knobsOrdered.begin(); ++next; - for (std::map::iterator it = knobsOrdered.begin(); it != knobsOrdered.end(); ++it) { - KnobGuiPtr ret( appPTR->createGuiForKnob(it->second, publicInterface) ); + for (KnobsVec::const_iterator it = knobsOrdered.begin(); it != knobsOrdered.end(); ++it) { + KnobGuiPtr ret( appPTR->createGuiForKnob(*it, publicInterface) ); if (!ret) { assert(false); continue; } ret->initialize(); - knobsMapping.insert(std::make_pair(it->second, ret)); + knobsMapping.insert(std::make_pair(*it, ret)); - bool makeNewLine = it->second->getInViewerContextNewLineActivated(); + bool makeNewLine = (*it)->getInViewerContextNewLineActivated(); QWidget* labelContainer = 0; KnobClickableLabel* label = 0; @@ -223,12 +277,12 @@ NodeViewerContextPrivate::createKnobs() mainContainerLayout->addWidget(lastRowContainer); } else { - knobsOnSameLine.push_back(it->second); + knobsOnSameLine.push_back(*it); if (next != knobsOrdered.end()) { - if (it->second->getInViewerContextAddSeparator()) { + if ((*it)->getInViewerContextAddSeparator()) { addSpacer(lastRowLayout); } else { - int spacing = it->second->getInViewerContextItemSpacing(); + int spacing = (*it)->getInViewerContextItemSpacing(); lastRowLayout->addSpacing(TO_DPIX(spacing)); } } @@ -278,14 +332,9 @@ NodeViewerContextPrivate::addToolBarTool(const std::string& toolID, const std::s } } - QAction* action; - if (!hintToolTip.empty()) { - action = new TooltipActionShortcut(shortcutGroup.toStdString(), toolID, label, toolButton); - action->setText(QString::fromUtf8(label.c_str())); - action->setIcon(icon); - } else { - action = new QAction(icon, QString::fromUtf8(label.c_str()), toolButton); - } + QString labelTouse = icon.isNull() ? QString::fromUtf8(label.c_str()) : QString(); + QAction* action = new QAction(icon, labelTouse, toolButton); + QStringList data; data.push_back(qRoleId); @@ -311,13 +360,6 @@ NodeViewerContextPrivate::addToolBarTool(const std::string& toolID, const std::s toolButton->addAction(action); } -QWidget* -NodeViewerContext::getButtonsContainer() const -{ - return _imp->mainContainer; -} - - QToolBar* NodeViewerContext::getToolBar() const { @@ -339,7 +381,7 @@ NodeViewerContext::getCurrentTool() const Gui* NodeViewerContext::getGui() const { - return _imp->viewerTab->getGui(); + return _imp->viewerTab ? _imp->viewerTab->getGui() : 0; } const QUndoCommand* @@ -406,6 +448,94 @@ NodeViewerContext::setCurrentTool(const QString& toolID, bool notifyNode) return; } } +} + +void +NodeViewerContext::onToolGroupValueChanged(ViewSpec /*view*/, + int /*dimension*/, + int reason) +{ + KnobSignalSlotHandler* caller = dynamic_cast(sender()); + if (!caller) { + return; + } + KnobPtr knob = caller->getKnob(); + if (!knob) { + return; + } + + if (reason == eValueChangedReasonNatronGuiEdited || + reason == eValueChangedReasonUserEdited) { + return; + } + + QString newRoleID = QString::fromUtf8(knob->getName().c_str()); + + std::map::iterator foundOldTool = _imp->toolButtons.find(newRoleID); + assert(foundOldTool != _imp->toolButtons.end()); + if (foundOldTool == _imp->toolButtons.end()) { + return; + } + + ViewerToolButton* newToolButton = foundOldTool->second; + assert(newToolButton); + _imp->toggleToolsSelection(newToolButton); + newToolButton->setDown(true); + + _imp->currentRole = newRoleID; + +} + +void +NodeViewerContext::onToolActionValueChanged(ViewSpec /*view*/, + int /*dimension*/, + int reason) +{ + KnobSignalSlotHandler* caller = dynamic_cast(sender()); + if (!caller) { + return; + } + KnobPtr knob = caller->getKnob(); + if (!knob) { + return; + } + + if (reason == eValueChangedReasonNatronGuiEdited || + reason == eValueChangedReasonUserEdited) { + return; + } + + QString newToolID = QString::fromUtf8(knob->getName().c_str()); + + std::map::iterator foundOldTool = _imp->toolButtons.find(_imp->currentRole); + assert(foundOldTool != _imp->toolButtons.end()); + if (foundOldTool == _imp->toolButtons.end()) { + return; + } + + ViewerToolButton* newToolButton = foundOldTool->second; + assert(newToolButton); + QList actions = newToolButton->actions(); + for (QList::iterator it = actions.begin(); it != actions.end(); ++it) { + QStringList actionData = (*it)->data().toStringList(); + + if (actionData.size() != 2) { + continue; + } + const QString& actionRoleID = actionData[0]; + const QString& actionTool = actionData[1]; + assert(actionRoleID == _imp->currentRole); + if (actionRoleID == _imp->currentRole && actionTool == newToolID) { + newToolButton->setDefaultAction(*it); + _imp->currentTool = newToolID; + return; + } + + } + + + + } void @@ -444,9 +574,82 @@ NodeViewerContextPrivate::onToolActionTriggeredInternal(QAction* action, bool no currentTool = newToolID; if (notifyNode) { -#pragma message WARN("To implement when a toolbutton changes") + + // Refresh other viewers toolbars + NodeGuiPtr n = node.lock(); + const std::list viewers = publicInterface->getGui()->getViewersList(); + for (std::list::const_iterator it = viewers.begin(); it != viewers.end(); ++it) { + if (*it != viewerTab) { + (*it)->updateSelectedToolForNode(newToolID, n); + } + } + + KnobPtr oldGroupKnob = n->getNode()->getKnobByName(oldRole.toStdString()); + KnobPtr newGroupKnob = n->getNode()->getKnobByName(newRoleID.toStdString()); + + KnobPtr oldToolKnob = n->getNode()->getKnobByName(oldTool.toStdString()); + KnobPtr newToolKnob = n->getNode()->getKnobByName(newToolID.toStdString()); + assert(oldToolKnob && newToolKnob && oldGroupKnob && newGroupKnob); + if (oldToolKnob && newToolKnob && oldGroupKnob && newGroupKnob) { + KnobButton* oldIsButton = dynamic_cast(oldToolKnob.get()); + assert(oldIsButton); + KnobButton* newIsButton = dynamic_cast(newToolKnob.get()); + assert(newIsButton); + + KnobGroup* oldIsGroup = dynamic_cast(oldGroupKnob.get()); + assert(oldIsGroup); + KnobGroup* newIsGroup = dynamic_cast(newGroupKnob.get()); + assert(newIsGroup); + if (oldIsButton && newIsButton && oldIsGroup && newIsGroup) { + + oldIsGroup->onValueChanged(false, ViewSpec::all(), 0, eValueChangedReasonUserEdited, 0); + newIsGroup->onValueChanged(true, ViewSpec::all(), 0, eValueChangedReasonUserEdited, 0); + + + oldIsButton->onValueChanged(false, ViewSpec::all(), 0, eValueChangedReasonUserEdited, 0); + newIsButton->onValueChanged(true, ViewSpec::all(), 0, eValueChangedReasonUserEdited, 0); + } + } + + } + +} + +void +NodeViewerContext::updateSelectionFromViewerSelectionRectangle(bool onRelease) +{ + NodeGuiPtr n = _imp->getNode(); + if (n) { + return; + } + NodePtr node = n->getNode(); + if (!node) { + return; } + RectD rect; + _imp->viewer->getSelectionRectangle(rect.x1, rect.x2, rect.y1, rect.y2); + node->getEffectInstance()->onInteractViewportSelectionUpdated(rect, onRelease); +} +void +NodeViewerContext::onViewerSelectionCleared() +{ + NodeGuiPtr n = _imp->getNode(); + if (n) { + return; + } + NodePtr node = n->getNode(); + if (!node) { + return; + } + node->getEffectInstance()->onInteractViewportSelectionCleared(); +} + +void +NodeViewerContext::notifyGuiClosing() +{ + _imp->viewer = 0; + _imp->viewerTab = 0; } NATRON_NAMESPACE_EXIT; diff --git a/Gui/NodeViewerContext.h b/Gui/NodeViewerContext.h index f1c32527f5..fcad198399 100644 --- a/Gui/NodeViewerContext.h +++ b/Gui/NodeViewerContext.h @@ -27,6 +27,10 @@ #include +#if !defined(Q_MOC_RUN) && !defined(SBK_RUN) +#include +#endif + #include "Gui/GuiFwd.h" #include "Gui/KnobGuiContainerI.h" @@ -37,6 +41,7 @@ struct NodeViewerContextPrivate; class NodeViewerContext : public QObject , public KnobGuiContainerI +, public boost::enable_shared_from_this { GCC_DIAG_SUGGEST_OVERRIDE_OFF @@ -47,13 +52,10 @@ class NodeViewerContext NodeViewerContext(const NodeGuiPtr& node, ViewerTab* viewer); - virtual ~NodeViewerContext(); - - /** - * @brief Return the container widget for the controls of the node on the viewer - **/ - QWidget* getButtonsContainer() const; + void createGui(); + virtual ~NodeViewerContext(); + /** * @brief If this node's viewer context has a toolbar, this will return it **/ @@ -79,19 +81,41 @@ class NodeViewerContext void setCurrentTool(const QString& toolID, bool notifyNode); + void notifyGuiClosing(); + Q_SIGNALS: /** * @brief Emitted when the selected role changes **/ void roleChanged(int previousRole, int newRole); - + public Q_SLOTS: + /** + * @brief Received when the selection rectangle has changed on the viewer. + * @param onRelease When true, this signal is emitted on the mouse release event + * which means this is the last selection desired by the user. + * Receivers can either update the selection always or just on mouse release. + **/ + void updateSelectionFromViewerSelectionRectangle(bool onRelease); + + void onViewerSelectionCleared(); + void onToolActionTriggered(); void onToolActionTriggered(QAction* act); + void onToolGroupValueChanged(ViewSpec view, + int dimension, + int reason); + + void onToolActionValueChanged(ViewSpec view, + int dimension, + int reason); + + void onNodeSettingsPanelClosed(bool closed); + private: boost::scoped_ptr _imp; diff --git a/Gui/QtEnumConvert.cpp b/Gui/QtEnumConvert.cpp index a673e5969c..9ae0ce1cd3 100644 --- a/Gui/QtEnumConvert.cpp +++ b/Gui/QtEnumConvert.cpp @@ -1819,5 +1819,57 @@ QtEnumConvert::toQtStandarButtons(StandardButtons buttons) return ret; } // toQtStandarButtons + +bool +QtEnumConvert::toQtCursor(CursorEnum c, Qt::CursorShape* ret) +{ + bool b = true; + switch (c) { + case eCursorArrow: + *ret = Qt::ArrowCursor; + case eCursorBDiag: + *ret = Qt::SizeBDiagCursor; + case eCursorFDiag: + *ret = Qt::SizeFDiagCursor; + case eCursorBlank: + *ret = Qt::BlankCursor; + case eCursorBusy: + *ret = Qt::BusyCursor; + case eCursorClosedHand: + *ret = Qt::ClosedHandCursor; + case eCursorCross: + *ret = Qt::CrossCursor; + case eCursorForbidden: + *ret = Qt::ForbiddenCursor; + case eCursorIBeam: + *ret = Qt::IBeamCursor; + case eCursorOpenHand: + *ret = Qt::OpenHandCursor; + case eCursorPointingHand: + *ret = Qt::PointingHandCursor; + case eCursorSizeAll: + *ret = Qt::SizeAllCursor; + case eCursorSizeHor: + *ret = Qt::SizeHorCursor; + case eCursorSizeVer: + *ret = Qt::SizeVerCursor; + case eCursorSplitH: + *ret = Qt::SplitHCursor; + case eCursorSplitV: + *ret = Qt::SplitVCursor; + case eCursorUpArrow: + *ret = Qt::UpArrowCursor; + case eCursorWait: + *ret = Qt::WaitCursor; + case eCursorWhatsThis: + *ret = Qt::WhatsThisCursor; + case eCursorDefault: + default: + b = false; + break; + } + return b; +} // toQtCursor + NATRON_NAMESPACE_EXIT; diff --git a/Gui/QtEnumConvert.h b/Gui/QtEnumConvert.h index 7324030349..c6badae4a8 100644 --- a/Gui/QtEnumConvert.h +++ b/Gui/QtEnumConvert.h @@ -60,6 +60,8 @@ class QtEnumConvert static StandardButtonEnum fromQtStandardButton(QMessageBox::StandardButton b); static QMessageBox::StandardButton toQtStandardButton(StandardButtonEnum b); static QMessageBox::StandardButtons toQtStandarButtons(StandardButtons buttons); + + static bool toQtCursor(CursorEnum c, Qt::CursorShape* ret); }; NATRON_NAMESPACE_EXIT; diff --git a/Gui/RotoGui.cpp b/Gui/RotoGui.cpp deleted file mode 100644 index 993fa09483..0000000000 --- a/Gui/RotoGui.cpp +++ /dev/null @@ -1,4992 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * This file is part of Natron , - * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat - * - * Natron is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Natron is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Natron. If not, see - * ***** END LICENSE BLOCK ***** */ - -// ***** BEGIN PYTHON BLOCK ***** -// from : -// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." -#include -// ***** END PYTHON BLOCK ***** - -#include "RotoGui.h" - -#include // min, max -#include - -#include "Global/GLIncludes.h" - -CLANG_DIAG_OFF(deprecated) -CLANG_DIAG_OFF(uninitialized) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -CLANG_DIAG_ON(deprecated) -CLANG_DIAG_ON(uninitialized) - -#include - -#include "Engine/Bezier.h" -#include "Engine/KnobTypes.h" -#include "Engine/Lut.h" -#include "Engine/Node.h" -#include "Engine/NodeGroup.h" -#include "Engine/RotoContext.h" -#include "Engine/RotoContextPrivate.h" -#include "Engine/RotoLayer.h" -#include "Engine/RotoPaint.h" -#include "Engine/RotoPoint.h" -#include "Engine/RotoStrokeItem.h" -#include "Engine/TimeLine.h" -#include "Engine/Transform.h" -#include "Engine/ViewIdx.h" -#include "Engine/ViewerInstance.h" - -#include "Gui/ActionShortcuts.h" -#include "Gui/Button.h" -#include "Gui/ComboBox.h" -#include "Gui/DockablePanel.h" -#include "Gui/QtEnumConvert.h" -#include "Gui/Gui.h" -#include "Gui/GuiAppInstance.h" -#include "Gui/GuiApplicationManager.h" -#include "Gui/GuiDefines.h" -#include "Gui/GuiMacros.h" -#include "Gui/KnobGuiColor.h" // ColorPickerLabel -#include "Gui/Menu.h" -#include "Gui/NodeGraph.h" -#include "Gui/NodeGui.h" -#include "Gui/NodeSettingsPanel.h" -#include "Gui/RotoUndoCommand.h" -#include "Gui/SpinBox.h" -#include "Gui/Utils.h" -#include "Gui/ViewerGL.h" -#include "Gui/ViewerTab.h" - - -#define kControlPointMidSize 3 -#define kBezierSelectionTolerance 8 -#define kControlPointSelectionTolerance 8 -#define kXHairSelectedCpsTolerance 8 -#define kXHairSelectedCpsBox 8 -#define kTangentHandleSelectionTolerance 8 -#define kTransformArrowLenght 10 -#define kTransformArrowWidth 3 -#define kTransformArrowOffsetFromPoint 15 - - -NATRON_NAMESPACE_ENTER; - - -NATRON_NAMESPACE_ANONYMOUS_ENTER - -///A list of points and their counter-part, that is: either a control point and its feather point, or -///the feather point and its associated control point -typedef std::pair, boost::shared_ptr > SelectedCP; -typedef std::list< SelectedCP > SelectedCPs; -typedef std::list< boost::shared_ptr > SelectedItems; - -enum EventStateEnum -{ - eEventStateNone = 0, - eEventStateDraggingControlPoint, - eEventStateDraggingSelectedControlPoints, - eEventStateBuildingBezierControlPointTangent, - eEventStateBuildingEllipse, - eEventStateBuildingRectangle, - eEventStateDraggingLeftTangent, - eEventStateDraggingRightTangent, - eEventStateDraggingFeatherBar, - eEventStateDraggingBBoxTopLeft, - eEventStateDraggingBBoxTopRight, - eEventStateDraggingBBoxBtmRight, - eEventStateDraggingBBoxBtmLeft, - eEventStateDraggingBBoxMidTop, - eEventStateDraggingBBoxMidRight, - eEventStateDraggingBBoxMidBtm, - eEventStateDraggingBBoxMidLeft, - eEventStateBuildingStroke, - eEventStateDraggingCloneOffset, - eEventStateDraggingBrushSize, - eEventStateDraggingBrushOpacity, -}; - -enum HoverStateEnum -{ - eHoverStateNothing = 0, - eHoverStateBboxTopLeft, - eHoverStateBboxTopRight, - eHoverStateBboxBtmRight, - eHoverStateBboxBtmLeft, - eHoverStateBboxMidTop, - eHoverStateBboxMidRight, - eHoverStateBboxMidBtm, - eHoverStateBboxMidLeft, - eHoverStateBbox -}; - -enum SelectedCpsTransformModeEnum -{ - eSelectedCpsTransformModeTranslateAndScale = 0, - eSelectedCpsTransformModeRotateAndSkew = 1 -}; - -NATRON_NAMESPACE_ANONYMOUS_EXIT - - -///A small structure of all the data shared by all the viewers watching the same Roto -class RotoGuiSharedData -{ -public: - SelectedItems selectedItems; - SelectedCPs selectedCps; - QRectF selectedCpsBbox; - bool showCpsBbox; - - ////This is by default eSelectedCpsTransformModeTranslateAndScale. When clicking the cross-hair in the center this will toggle the transform mode - ////like it does in inkscape. - SelectedCpsTransformModeEnum transformMode; - boost::shared_ptr builtBezier; //< the bezier currently being built - boost::shared_ptr bezierBeingDragged; - SelectedCP cpBeingDragged; //< the cp being dragged - boost::shared_ptr tangentBeingDragged; //< the control point whose tangent is being dragged. - //only relevant when the state is DRAGGING_X_TANGENT - SelectedCP featherBarBeingDragged, featherBarBeingHovered; - bool displayFeather; - boost::shared_ptr strokeBeingPaint; - std::pair cloneOffset; - QPointF click; // used for drawing ellipses and rectangles, to handle center/constrain. May also be used for the selection bbox. - - RotoGuiSharedData() - : selectedItems() - , selectedCps() - , selectedCpsBbox() - , showCpsBbox(false) - , transformMode() - , builtBezier() - , bezierBeingDragged() - , cpBeingDragged() - , tangentBeingDragged() - , featherBarBeingDragged() - , featherBarBeingHovered() - , displayFeather(true) - , strokeBeingPaint() - , cloneOffset() - , click() - { - cloneOffset.first = cloneOffset.second = 0.; - } -}; - -struct RotoGui::RotoGuiPrivate -{ - RotoGui* publicInterface; - NodeGui* node; - ViewerGL* viewer; - ViewerTab* viewerTab; - boost::shared_ptr context; - RotoTypeEnum type; - QToolBar* toolbar; - QWidget* selectionButtonsBar; - QHBoxLayout* selectionButtonsBarLayout; - Button* autoKeyingEnabled; - Button* featherLinkEnabled; - Button* displayFeatherEnabled; - Button* stickySelectionEnabled; - Button* bboxClickAnywhere; - Button* rippleEditEnabled; - Button* addKeyframeButton; - Button* removeKeyframeButton; - QWidget* brushButtonsBar; - QHBoxLayout* brushButtonsBarLayout; - ColorPickerLabel* colorPickerLabel; - Button* colorWheelButton; - ComboBox* compositingOperatorButton; - QLabel* opacityLabel; - SpinBox* opacitySpinbox; - Button* pressureOpacityButton; - QLabel* sizeLabel; - SpinBox* sizeSpinbox; - Button* pressureSizeButton; - QLabel* hardnessLabel; - SpinBox* hardnessSpinBox; - Button* pressureHardnessButton; - QLabel* buildUpLabel; - Button* buildUpButton; - QLabel* effectLabel; - SpinBox* effectSpinBox; - QLabel* timeOffsetLabel; - SpinBox* timeOffsetSpinbox; - ComboBox* timeOffsetMode; - ComboBox* sourceTypeCombobox; - Button* resetCloneOffset; - Label* multiStrokeEnabledLabel; - QCheckBox* multiStrokeEnabled; - RotoToolButton* selectTool; - RotoToolButton* pointsEditionTool; - RotoToolButton* bezierEditionTool; - RotoToolButton* paintBrushTool; - RotoToolButton* cloneBrushTool; - RotoToolButton* effectBrushTool; - RotoToolButton* mergeBrushTool; - std::list allTools; - QAction* selectAllAction; - QAction* lastPaintToolAction; - QAction* eraserAction; - RotoToolEnum selectedTool; - QToolButton* selectedRole; - EventStateEnum state; - HoverStateEnum hoverState; - QPointF lastClickPos; - QPointF lastMousePos; - boost::shared_ptr< RotoGuiSharedData > rotoData; - bool evaluateOnPenUp; //< if true the next pen up will call context->evaluateChange() - bool evaluateOnKeyUp; //< if true the next key up will call context->evaluateChange() - bool iSelectingwithCtrlA; - int shiftDown; - int ctrlDown; - bool lastTabletDownTriggeredEraser; - QPointF mouseCenterOnSizeChange; - - RotoGuiPrivate(RotoGui* pub, - NodeGui* n, - ViewerTab* tab, - const boost::shared_ptr & sharedData) - : publicInterface(pub) - , node(n) - , viewer( tab->getViewer() ) - , viewerTab(tab) - , context() - , type(eRotoTypeRotoscoping) - , toolbar(0) - , selectionButtonsBar(0) - , selectionButtonsBarLayout(0) - , autoKeyingEnabled(0) - , featherLinkEnabled(0) - , displayFeatherEnabled(0) - , stickySelectionEnabled(0) - , bboxClickAnywhere(0) - , rippleEditEnabled(0) - , addKeyframeButton(0) - , removeKeyframeButton(0) - , brushButtonsBar(0) - , brushButtonsBarLayout(0) - , colorPickerLabel(0) - , colorWheelButton(0) - , compositingOperatorButton(0) - , opacityLabel(0) - , opacitySpinbox(0) - , pressureOpacityButton(0) - , sizeLabel(0) - , sizeSpinbox(0) - , pressureSizeButton(0) - , hardnessLabel(0) - , hardnessSpinBox(0) - , pressureHardnessButton(0) - , buildUpLabel(0) - , buildUpButton(0) - , effectLabel(0) - , effectSpinBox(0) - , timeOffsetLabel(0) - , timeOffsetSpinbox(0) - , timeOffsetMode(0) - , sourceTypeCombobox(0) - , resetCloneOffset(0) - , multiStrokeEnabledLabel(0) - , multiStrokeEnabled(0) - , selectTool(0) - , pointsEditionTool(0) - , bezierEditionTool(0) - , paintBrushTool(0) - , cloneBrushTool(0) - , effectBrushTool(0) - , mergeBrushTool(0) - , allTools() - , selectAllAction(0) - , lastPaintToolAction(0) - , eraserAction(0) - , selectedTool(eRotoToolSelectAll) - , selectedRole(0) - , state(eEventStateNone) - , hoverState(eHoverStateNothing) - , lastClickPos() - , lastMousePos() - , rotoData(sharedData) - , evaluateOnPenUp(false) - , evaluateOnKeyUp(false) - , iSelectingwithCtrlA(false) - , shiftDown(0) - , ctrlDown(0) - , lastTabletDownTriggeredEraser(false) - , mouseCenterOnSizeChange() - { - if ( n->getNode()->isRotoPaintingNode() ) { - type = eRotoTypeRotopainting; - } - context = node->getNode()->getRotoContext(); - assert(context); - if (!rotoData) { - rotoData.reset(new RotoGuiSharedData); - } - } - - void clearSelection(); - - void clearCPSSelection(); - - void clearBeziersSelection(); - - bool hasSelection() const; - - void onCurveLockedChangedRecursive(const boost::shared_ptr & item, bool* ret); - - bool removeItemFromSelection(const boost::shared_ptr& b); - - void computeSelectedCpsBBOX(); - - QPointF getSelectedCpsBBOXCenter(); - - void drawSelectedCpsBBOX(); - - ///by default draws a vertical arrow, which can be rotated by rotate amount. - void drawArrow(double centerX, double centerY, double rotate, bool hovered, const std::pair & pixelScale); - - ///same as drawArrow but the two ends will make an angle of 90 degrees - void drawBendedArrow(double centerX, double centerY, double rotate, bool hovered, const std::pair & pixelScale); - - void handleBezierSelection(const boost::shared_ptr & curve, QMouseEvent* e); - - void handleControlPointSelection(const std::pair, boost::shared_ptr > & p, QMouseEvent* e); - - void drawSelectedCp(double time, - const boost::shared_ptr & cp, - double x, double y, - const Transform::Matrix3x3& transform); - - std::pair, boost::shared_ptr >isNearbyFeatherBar(double time, const std::pair & pixelScale, const QPointF & pos) const; - - bool isNearbySelectedCpsCrossHair(const QPointF & pos) const; - - bool isWithinSelectedCpsBBox(const QPointF& pos) const; - - bool isNearbyBBoxTopLeft(const QPointF & p, double tolerance, const std::pair & pixelScale) const; - bool isNearbyBBoxTopRight(const QPointF & p, double tolerance, const std::pair & pixelScale) const; - bool isNearbyBBoxBtmLeft(const QPointF & p, double tolerance, const std::pair & pixelScale) const; - bool isNearbyBBoxBtmRight(const QPointF & p, double tolerance, const std::pair & pixelScale) const; - - bool isNearbyBBoxMidTop(const QPointF & p, double tolerance, const std::pair & pixelScale) const; - bool isNearbyBBoxMidRight(const QPointF & p, double tolerance, const std::pair & pixelScale) const; - bool isNearbyBBoxMidBtm(const QPointF & p, double tolerance, const std::pair & pixelScale) const; - bool isNearbyBBoxMidLeft(const QPointF & p, double tolerance, const std::pair & pixelScale) const; - - bool isNearbySelectedCpsBoundingBox(const QPointF & pos, double tolerance) const; - - EventStateEnum isMouseInteractingWithCPSBbox(const QPointF& pos, double tolerance, const std::pair& pixelScale) const; - - bool isBboxClickAnywhereEnabled() const - { - return bboxClickAnywhere ? bboxClickAnywhere->isDown() : false; - } - - void toggleToolsSelection(QToolButton* selected); - - void makeStroke(bool prepareForLater, const RotoPoint& p); - - void checkViewersAreDirectlyConnected(); -}; - -RotoToolButton::RotoToolButton(QWidget* parent) - : QToolButton(parent) - , isSelected(false) - , wasMouseReleased(false) -{ - setFocusPolicy(Qt::ClickFocus); -} - -RotoToolButton::~RotoToolButton() -{ -} - -void -RotoToolButton::mousePressEvent(QMouseEvent* /*e*/) -{ - setFocus(); - wasMouseReleased = false; - QTimer::singleShot( 300, this, SLOT(handleLongPress()) ); -} - -void -RotoToolButton::handleLongPress() -{ - if (!wasMouseReleased) { - showMenu(); - } -} - -void -RotoToolButton::mouseReleaseEvent(QMouseEvent* e) -{ - wasMouseReleased = true; - if ( triggerButtonIsRight(e) ) { - showMenu(); - } else if ( triggerButtonIsLeft(e) ) { - handleSelection(); - } else { - QToolButton::mousePressEvent(e); - } -} - -bool -RotoToolButton::getIsSelected() const -{ - return isSelected; -} - -void -RotoToolButton::setIsSelected(bool s) -{ - if (s != isSelected) { - isSelected = s; - style()->unpolish(this); - style()->polish(this); - update(); - } -} - -void -RotoToolButton::handleSelection() -{ - QAction* curAction = defaultAction(); - - if ( !isDown() ) { - setDown(true); - Q_EMIT triggered(curAction); - } else { - QList allAction = actions(); - for (int i = 0; i < allAction.size(); ++i) { - if (allAction[i] == curAction) { - int next = ( i == (allAction.size() - 1) ) ? 0 : i + 1; - setDefaultAction(allAction[next]); - Q_EMIT triggered(allAction[next]); - break; - } - } - } -} - -QAction* -RotoGui::createToolAction(QToolButton* toolGroup, - const QIcon & icon, - const QString & text, - const QString & tooltip, - const QKeySequence & shortcut, - RotoGui::RotoToolEnum tool) -{ - QAction *action = new QAction(icon, text, toolGroup); - - - action->setToolTip( QString::fromUtf8("

") + text + QString::fromUtf8(": ") + tooltip + QString::fromUtf8("

") + tr("Keyboard shortcut:") + QString::fromUtf8(" ") + shortcut.toString(QKeySequence::NativeText) + QString::fromUtf8("

") ); - - QPoint data; - data.setX( (int)tool ); - if (toolGroup == _imp->selectTool) { - data.setY( (int)eRotoRoleSelection ); - } else if (toolGroup == _imp->pointsEditionTool) { - data.setY( (int)eRotoRolePointsEdition ); - } else if (toolGroup == _imp->bezierEditionTool) { - data.setY(eRotoRoleBezierEdition); - } else if (toolGroup == _imp->paintBrushTool) { - data.setY(eRotoRolePaintBrush); - } else if (toolGroup == _imp->cloneBrushTool) { - data.setY(eRotoRoleCloneBrush); - } else if (toolGroup == _imp->effectBrushTool) { - data.setY(eRotoRoleEffectBrush); - } else if (toolGroup == _imp->mergeBrushTool) { - data.setY(eRotoRoleMergeBrush); - } - action->setData( QVariant(data) ); - QObject::connect( action, SIGNAL(triggered()), this, SLOT(onToolActionTriggered()) ); - toolGroup->addAction(action); - - return action; -} - -RotoGui::RotoGui(NodeGui* node, - ViewerTab* parent, - const boost::shared_ptr & sharedData) - : _imp( new RotoGuiPrivate(this, node, parent, sharedData) ) -{ - assert(parent); - assert(_imp->context); - - bool hasShapes = _imp->context->getNCurves(); - bool effectIsPaint = _imp->context->isRotoPaint(); - QObject::connect( parent->getViewer(), SIGNAL(selectionRectangleChanged(bool)), this, SLOT(updateSelectionFromSelectionRectangle(bool)) ); - QObject::connect( parent->getViewer(), SIGNAL(selectionCleared()), this, SLOT(onSelectionCleared()) ); - QPixmap pixBezier, pixEllipse, pixRectangle, pixAddPts, pixRemovePts, pixCuspPts, pixSmoothPts, pixOpenCloseCurve, pixRemoveFeather; - QPixmap pixSelectAll, pixSelectPoints, pixSelectFeather, pixSelectCurves, pixAutoKeyingEnabled, pixAutoKeyingDisabled; - QPixmap pixStickySelEnabled, pixStickySelDisabled, pixFeatherLinkEnabled, pixFeatherLinkDisabled, pixAddKey, pixRemoveKey; - QPixmap pixRippleEnabled, pixRippleDisabled; - QPixmap pixFeatherEnabled, pixFeatherDisabled; - QPixmap pixBboxClickEnabled, pixBboxClickDisabled; - QPixmap pixPaintBrush, pixEraser, pixBlur, pixSmear, pixSharpen, pixDodge, pixBurn, pixClone, pixReveal, pixPencil; - int largeIconSize = TO_DPIY(NATRON_LARGE_BUTTON_ICON_SIZE); - - appPTR->getIcon(NATRON_PIXMAP_BEZIER_32, largeIconSize, &pixBezier); - appPTR->getIcon(NATRON_PIXMAP_ELLIPSE, largeIconSize, &pixEllipse); - appPTR->getIcon(NATRON_PIXMAP_RECTANGLE, largeIconSize, &pixRectangle); - appPTR->getIcon(NATRON_PIXMAP_ADD_POINTS, largeIconSize, &pixAddPts); - appPTR->getIcon(NATRON_PIXMAP_REMOVE_POINTS, largeIconSize, &pixRemovePts); - appPTR->getIcon(NATRON_PIXMAP_CUSP_POINTS, largeIconSize, &pixCuspPts); - appPTR->getIcon(NATRON_PIXMAP_SMOOTH_POINTS, largeIconSize, &pixSmoothPts); - appPTR->getIcon(NATRON_PIXMAP_OPEN_CLOSE_CURVE, largeIconSize, &pixOpenCloseCurve); - appPTR->getIcon(NATRON_PIXMAP_REMOVE_FEATHER, largeIconSize, &pixRemoveFeather); - appPTR->getIcon(NATRON_PIXMAP_SELECT_ALL, largeIconSize, &pixSelectAll); - appPTR->getIcon(NATRON_PIXMAP_SELECT_POINTS, largeIconSize, &pixSelectPoints); - appPTR->getIcon(NATRON_PIXMAP_SELECT_FEATHER, largeIconSize, &pixSelectFeather); - appPTR->getIcon(NATRON_PIXMAP_SELECT_CURVES, largeIconSize, &pixSelectCurves); - appPTR->getIcon(NATRON_PIXMAP_AUTO_KEYING_ENABLED, largeIconSize, &pixAutoKeyingEnabled); - appPTR->getIcon(NATRON_PIXMAP_AUTO_KEYING_DISABLED, largeIconSize, &pixAutoKeyingDisabled); - appPTR->getIcon(NATRON_PIXMAP_STICKY_SELECTION_ENABLED, largeIconSize, &pixStickySelEnabled); - appPTR->getIcon(NATRON_PIXMAP_STICKY_SELECTION_DISABLED, largeIconSize, &pixStickySelDisabled); - appPTR->getIcon(NATRON_PIXMAP_FEATHER_LINK_ENABLED, largeIconSize, &pixFeatherLinkEnabled); - appPTR->getIcon(NATRON_PIXMAP_FEATHER_LINK_DISABLED, largeIconSize, &pixFeatherLinkDisabled); - appPTR->getIcon(NATRON_PIXMAP_ADD_KEYFRAME, largeIconSize, &pixAddKey); - appPTR->getIcon(NATRON_PIXMAP_REMOVE_KEYFRAME, largeIconSize, &pixRemoveKey); - appPTR->getIcon(NATRON_PIXMAP_RIPPLE_EDIT_ENABLED, largeIconSize, &pixRippleEnabled); - appPTR->getIcon(NATRON_PIXMAP_RIPPLE_EDIT_DISABLED, largeIconSize, &pixRippleDisabled); - appPTR->getIcon(NATRON_PIXMAP_FEATHER_VISIBLE, largeIconSize, &pixFeatherEnabled); - appPTR->getIcon(NATRON_PIXMAP_FEATHER_UNVISIBLE, largeIconSize, &pixFeatherDisabled); - appPTR->getIcon(NATRON_PIXMAP_VIEWER_ROI_ENABLED, largeIconSize, &pixBboxClickEnabled); - appPTR->getIcon(NATRON_PIXMAP_VIEWER_ROI_DISABLED, largeIconSize, &pixBboxClickDisabled); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_SOLID, largeIconSize, &pixPaintBrush); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_ERASER, largeIconSize, &pixEraser); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_BLUR, largeIconSize, &pixBlur); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_SMEAR, largeIconSize, &pixSmear); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_SHARPEN, largeIconSize, &pixSharpen); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_DODGE, largeIconSize, &pixDodge); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_BURN, largeIconSize, &pixBurn); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_CLONE, largeIconSize, &pixClone); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_REVEAL, largeIconSize, &pixReveal); - appPTR->getIcon(NATRON_PIXMAP_PENCIL, largeIconSize, &pixPencil); - - _imp->toolbar = new QToolBar(parent); - _imp->toolbar->setOrientation(Qt::Vertical); - _imp->selectionButtonsBar = new QWidget(parent); - _imp->selectionButtonsBarLayout = new QHBoxLayout(_imp->selectionButtonsBar); - _imp->selectionButtonsBarLayout->setContentsMargins(3, 2, 0, 0); - - QIcon autoKeyIc; - autoKeyIc.addPixmap(pixAutoKeyingEnabled, QIcon::Normal, QIcon::On); - autoKeyIc.addPixmap(pixAutoKeyingDisabled, QIcon::Normal, QIcon::Off); - - QSize medButtonSize( TO_DPIX(NATRON_MEDIUM_BUTTON_SIZE), TO_DPIY(NATRON_MEDIUM_BUTTON_SIZE) ); - QSize medButtonIconSize( TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), TO_DPIY(NATRON_MEDIUM_BUTTON_ICON_SIZE) ); - - _imp->autoKeyingEnabled = new Button(autoKeyIc, QString(), _imp->selectionButtonsBar); - _imp->autoKeyingEnabled->setFixedSize(medButtonSize); - _imp->autoKeyingEnabled->setIconSize(medButtonIconSize); - _imp->autoKeyingEnabled->setCheckable(true); - _imp->autoKeyingEnabled->setChecked( _imp->context->isAutoKeyingEnabled() ); - _imp->autoKeyingEnabled->setDown( _imp->context->isAutoKeyingEnabled() ); - _imp->autoKeyingEnabled->setToolTip( GuiUtils::convertFromPlainText(tr("Auto-keying: When activated any movement to a control point will set a keyframe at the current time."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->autoKeyingEnabled, SIGNAL(clicked(bool)), this, SLOT(onAutoKeyingButtonClicked(bool)) ); - _imp->selectionButtonsBarLayout->addWidget(_imp->autoKeyingEnabled); - - QIcon featherLinkIc; - featherLinkIc.addPixmap(pixFeatherLinkEnabled, QIcon::Normal, QIcon::On); - featherLinkIc.addPixmap(pixFeatherLinkDisabled, QIcon::Normal, QIcon::Off); - _imp->featherLinkEnabled = new Button(featherLinkIc, QString(), _imp->selectionButtonsBar); - _imp->featherLinkEnabled->setFixedSize(medButtonSize); - _imp->featherLinkEnabled->setIconSize(medButtonIconSize); - _imp->featherLinkEnabled->setCheckable(true); - _imp->featherLinkEnabled->setChecked( _imp->context->isFeatherLinkEnabled() ); - _imp->featherLinkEnabled->setDown( _imp->context->isFeatherLinkEnabled() ); - _imp->featherLinkEnabled->setToolTip( GuiUtils::convertFromPlainText(tr("Feather-link: When activated the feather points will follow the same" - " movement as their counter-part does."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->featherLinkEnabled, SIGNAL(clicked(bool)), this, SLOT(onFeatherLinkButtonClicked(bool)) ); - _imp->selectionButtonsBarLayout->addWidget(_imp->featherLinkEnabled); - - QIcon enableFeatherIC; - enableFeatherIC.addPixmap(pixFeatherEnabled, QIcon::Normal, QIcon::On); - enableFeatherIC.addPixmap(pixFeatherDisabled, QIcon::Normal, QIcon::Off); - _imp->displayFeatherEnabled = new Button(enableFeatherIC, QString(), _imp->selectionButtonsBar); - _imp->displayFeatherEnabled->setFixedSize(medButtonSize); - _imp->displayFeatherEnabled->setIconSize(medButtonIconSize); - _imp->displayFeatherEnabled->setCheckable(true); - _imp->displayFeatherEnabled->setChecked(true); - _imp->displayFeatherEnabled->setDown(true); - _imp->displayFeatherEnabled->setToolTip( GuiUtils::convertFromPlainText(tr("When checked, the feather curve applied to the shape(s) will be visible and editable."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->displayFeatherEnabled, SIGNAL(clicked(bool)), this, SLOT(onDisplayFeatherButtonClicked(bool)) ); - _imp->selectionButtonsBarLayout->addWidget(_imp->displayFeatherEnabled); - - QIcon stickSelIc; - stickSelIc.addPixmap(pixStickySelEnabled, QIcon::Normal, QIcon::On); - stickSelIc.addPixmap(pixStickySelDisabled, QIcon::Normal, QIcon::Off); - _imp->stickySelectionEnabled = new Button(stickSelIc, QString(), _imp->selectionButtonsBar); - _imp->stickySelectionEnabled->setFixedSize(medButtonSize); - _imp->stickySelectionEnabled->setIconSize(medButtonIconSize); - _imp->stickySelectionEnabled->setCheckable(true); - _imp->stickySelectionEnabled->setChecked(false); - _imp->stickySelectionEnabled->setDown(false); - _imp->stickySelectionEnabled->setToolTip( GuiUtils::convertFromPlainText(tr("Sticky-selection: When activated, " - " clicking outside of any shape will not clear the current selection."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->stickySelectionEnabled, SIGNAL(clicked(bool)), this, SLOT(onStickySelectionButtonClicked(bool)) ); - _imp->selectionButtonsBarLayout->addWidget(_imp->stickySelectionEnabled); - - QIcon bboxClickIc; - bboxClickIc.addPixmap(pixBboxClickEnabled, QIcon::Normal, QIcon::On); - bboxClickIc.addPixmap(pixBboxClickDisabled, QIcon::Normal, QIcon::Off); - _imp->bboxClickAnywhere = new Button(bboxClickIc, QString(), _imp->selectionButtonsBar); - _imp->bboxClickAnywhere->setFixedSize(medButtonSize); - _imp->bboxClickAnywhere->setIconSize(medButtonIconSize); - _imp->bboxClickAnywhere->setCheckable(true); - _imp->bboxClickAnywhere->setChecked(true); - _imp->bboxClickAnywhere->setDown(true); - _imp->bboxClickAnywhere->setToolTip( GuiUtils::convertFromPlainText(tr("Easy bounding box manipulation: When activated, " - " clicking inside of the bounding box of selected points will move the points." - "When deactivated, only clicking on the cross will move the points."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->bboxClickAnywhere, SIGNAL(clicked(bool)), this, SLOT(onBboxClickButtonClicked(bool)) ); - _imp->selectionButtonsBarLayout->addWidget(_imp->bboxClickAnywhere); - - - QIcon rippleEditIc; - rippleEditIc.addPixmap(pixRippleEnabled, QIcon::Normal, QIcon::On); - rippleEditIc.addPixmap(pixRippleDisabled, QIcon::Normal, QIcon::Off); - _imp->rippleEditEnabled = new Button(rippleEditIc, QString(), _imp->selectionButtonsBar); - _imp->rippleEditEnabled->setFixedSize(medButtonSize); - _imp->rippleEditEnabled->setIconSize(medButtonIconSize); - _imp->rippleEditEnabled->setCheckable(true); - _imp->rippleEditEnabled->setChecked( _imp->context->isRippleEditEnabled() ); - _imp->rippleEditEnabled->setDown( _imp->context->isRippleEditEnabled() ); - _imp->rippleEditEnabled->setToolTip( GuiUtils::convertFromPlainText(tr("Ripple-edit: When activated, moving a control point" - " will move it by the same amount for all the keyframes " - "it has."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->rippleEditEnabled, SIGNAL(clicked(bool)), this, SLOT(onRippleEditButtonClicked(bool)) ); - _imp->selectionButtonsBarLayout->addWidget(_imp->rippleEditEnabled); - - _imp->addKeyframeButton = new Button(QIcon(pixAddKey), QString(), _imp->selectionButtonsBar); - _imp->addKeyframeButton->setFixedSize(medButtonSize); - _imp->addKeyframeButton->setIconSize(medButtonIconSize); - QObject::connect( _imp->addKeyframeButton, SIGNAL(clicked(bool)), this, SLOT(onAddKeyFrameClicked()) ); - _imp->addKeyframeButton->setToolTip( GuiUtils::convertFromPlainText(tr("Set a keyframe at the current time for the selected shape(s), if any."), Qt::WhiteSpaceNormal) ); - _imp->selectionButtonsBarLayout->addWidget(_imp->addKeyframeButton); - - _imp->removeKeyframeButton = new Button(QIcon(pixRemoveKey), QString(), _imp->selectionButtonsBar); - _imp->removeKeyframeButton->setFixedSize(medButtonSize); - _imp->removeKeyframeButton->setIconSize(medButtonIconSize); - QObject::connect( _imp->removeKeyframeButton, SIGNAL(clicked(bool)), this, SLOT(onRemoveKeyFrameClicked()) ); - _imp->removeKeyframeButton->setToolTip( GuiUtils::convertFromPlainText(tr("Remove a keyframe at the current time for the selected shape(s), if any."), Qt::WhiteSpaceNormal) ); - _imp->selectionButtonsBarLayout->addWidget(_imp->removeKeyframeButton); - _imp->selectionButtonsBarLayout->addStretch(); - _imp->selectionButtonsBar->setVisible(false); - ////// - - _imp->brushButtonsBar = new QWidget(parent); - _imp->brushButtonsBarLayout = new QHBoxLayout(_imp->brushButtonsBar); - _imp->brushButtonsBarLayout->setContentsMargins(3, 2, 0, 0); - _imp->brushButtonsBarLayout->setSpacing(1); - - - QString multiTt = GuiUtils::convertFromPlainText(tr("When checked, strokes will be appended to the same item " - "in the hierarchy as long as the same tool is selected.\n" - "Select another tool to make a new item."), Qt::WhiteSpaceNormal); - _imp->multiStrokeEnabledLabel = new Label(QObject::tr("Multi-stroke:"), _imp->brushButtonsBar); - _imp->multiStrokeEnabledLabel->setToolTip(multiTt); - _imp->brushButtonsBarLayout->addWidget(_imp->multiStrokeEnabledLabel); - - _imp->brushButtonsBarLayout->addSpacing(3); - - _imp->multiStrokeEnabled = new QCheckBox(_imp->brushButtonsBar); - _imp->multiStrokeEnabled->setToolTip(multiTt); - _imp->multiStrokeEnabled->setChecked(true); - _imp->brushButtonsBarLayout->addWidget(_imp->multiStrokeEnabled); - - _imp->brushButtonsBarLayout->addSpacing(10); - - _imp->colorPickerLabel = new ColorPickerLabel(0, _imp->brushButtonsBar); - _imp->colorPickerLabel->setColor(Qt::white); - _imp->colorPickerLabel->setFixedSize(medButtonSize); - _imp->colorPickerLabel->setToolTip( GuiUtils::convertFromPlainText(tr("The color of the next paint brush stroke to be painted."), Qt::WhiteSpaceNormal) ); - _imp->brushButtonsBarLayout->addWidget(_imp->colorPickerLabel); - QPixmap colorWheelPix; - appPTR->getIcon(NATRON_PIXMAP_COLORWHEEL, TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), &colorWheelPix); - _imp->colorWheelButton = new Button(QIcon(colorWheelPix), QString(), _imp->brushButtonsBar); - _imp->colorWheelButton->setToolTip( GuiUtils::convertFromPlainText(tr("Open the color dialog."), Qt::WhiteSpaceNormal) ); - _imp->colorWheelButton->setFixedSize(medButtonSize); - _imp->colorWheelButton->setIconSize(medButtonIconSize); - QObject::connect( _imp->colorWheelButton, SIGNAL(clicked(bool)), this, SLOT(onColorWheelButtonClicked()) ); - _imp->brushButtonsBarLayout->addWidget(_imp->colorWheelButton); - - _imp->brushButtonsBarLayout->addSpacing(5); - - _imp->compositingOperatorButton = new ComboBox(_imp->brushButtonsBar); - { - std::vector operators, tooltips; - Merge::getOperatorStrings(&operators, &tooltips); - assert( operators.size() == tooltips.size() ); - for (std::size_t i = 0; i < operators.size(); ++i) { - _imp->compositingOperatorButton->addItem( QString::fromUtf8( operators[i].c_str() ), QIcon(), QKeySequence(), QString::fromUtf8( tooltips[i].c_str() ) ); - } - } - _imp->compositingOperatorButton->setCurrentIndex_no_emit( (int)eMergeCopy ); - _imp->compositingOperatorButton->setToolTip( GuiUtils::convertFromPlainText(tr("The blending mode of the next brush stroke."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->compositingOperatorButton, SIGNAL(currentIndexChanged(int)), this, SLOT(onBreakMultiStrokeTriggered()) ); - _imp->brushButtonsBarLayout->addWidget(_imp->compositingOperatorButton); - - - _imp->brushButtonsBarLayout->addSpacing(5); - - QString opacitytt = GuiUtils::convertFromPlainText(tr("The opacity of the next brush stroke to be painted. Use CTRL + SHIFT + drag " - "with the mouse to change the opacity."), Qt::WhiteSpaceNormal); - _imp->opacityLabel = new Label(tr("Opacity:"), _imp->brushButtonsBar); - _imp->opacityLabel->setToolTip(opacitytt); - _imp->brushButtonsBarLayout->addWidget(_imp->opacityLabel); - - _imp->opacitySpinbox = new SpinBox(_imp->brushButtonsBar, SpinBox::eSpinBoxTypeDouble); - QObject::connect( _imp->opacitySpinbox, SIGNAL(valueChanged(double)), this, SLOT(onBreakMultiStrokeTriggered()) ); - _imp->opacitySpinbox->setToolTip(opacitytt); - _imp->opacitySpinbox->setMinimum(0); - _imp->opacitySpinbox->setMaximum(1); - _imp->opacitySpinbox->setValue(1.); - _imp->brushButtonsBarLayout->addWidget(_imp->opacitySpinbox); - - QPixmap pressureOnPix, pressureOffPix, buildupOnPix, buildupOffPix; - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_PRESSURE_ENABLED, TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), &pressureOnPix); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_PRESSURE_DISABLED, TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), &pressureOffPix); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_BUILDUP_ENABLED, TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), &buildupOnPix); - appPTR->getIcon(NATRON_PIXMAP_ROTOPAINT_BUILDUP_DISABLED, TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), &buildupOffPix); - - QIcon pressureIc; - pressureIc.addPixmap(pressureOnPix, QIcon::Normal, QIcon::On); - pressureIc.addPixmap(pressureOffPix, QIcon::Normal, QIcon::Off); - QString pressOpatt = GuiUtils::convertFromPlainText(tr("If checked, the pressure of the pen will dynamically alter the opacity of the next " - "brush stroke."), Qt::WhiteSpaceNormal); - - _imp->pressureOpacityButton = new Button(pressureIc, QString(), _imp->brushButtonsBar); - QObject::connect( _imp->pressureOpacityButton, SIGNAL(clicked(bool)), this, SLOT(onPressureOpacityClicked(bool)) ); - _imp->pressureOpacityButton->setToolTip(pressOpatt); - _imp->pressureOpacityButton->setFixedSize(medButtonSize); - _imp->pressureOpacityButton->setIconSize(medButtonIconSize); - _imp->pressureOpacityButton->setCheckable(true); - _imp->pressureOpacityButton->setChecked(true); - _imp->pressureOpacityButton->setDown(true); - _imp->brushButtonsBarLayout->addWidget(_imp->pressureOpacityButton); - - _imp->brushButtonsBarLayout->addSpacing(5); - - QString sizett = GuiUtils::convertFromPlainText(tr("The size of the next brush stroke to be painted. Use SHIFT + drag with the mouse " - "to change the size."), Qt::WhiteSpaceNormal); - _imp->sizeLabel = new Label(tr("Size:"), _imp->brushButtonsBar); - _imp->sizeLabel->setToolTip(sizett); - _imp->brushButtonsBarLayout->addWidget(_imp->sizeLabel); - - _imp->sizeSpinbox = new SpinBox(_imp->brushButtonsBar, SpinBox::eSpinBoxTypeDouble); - QObject::connect( _imp->sizeSpinbox, SIGNAL(valueChanged(double)), this, SLOT(onBreakMultiStrokeTriggered()) ); - _imp->sizeSpinbox->setMinimum(0); - _imp->sizeSpinbox->setMaximum(1000); - _imp->sizeSpinbox->setValue(25.); - _imp->sizeSpinbox->setToolTip(sizett); - _imp->brushButtonsBarLayout->addWidget(_imp->sizeSpinbox); - - QString pressSizett = GuiUtils::convertFromPlainText(tr("If checked, the pressure of the pen will dynamically alter the size of the next " - "brush stroke."), Qt::WhiteSpaceNormal); - _imp->pressureSizeButton = new Button(pressureIc, QString(), _imp->brushButtonsBar); - QObject::connect( _imp->pressureSizeButton, SIGNAL(clicked(bool)), this, SLOT(onPressureSizeClicked(bool)) ); - _imp->pressureSizeButton->setToolTip(pressSizett); - _imp->pressureSizeButton->setFixedSize(medButtonSize); - _imp->pressureSizeButton->setIconSize(medButtonIconSize); - _imp->pressureSizeButton->setCheckable(true); - _imp->pressureSizeButton->setChecked(false); - _imp->pressureSizeButton->setDown(false); - _imp->brushButtonsBarLayout->addWidget(_imp->pressureSizeButton); - - _imp->brushButtonsBarLayout->addSpacing(5); - - QString hardnesstt = GuiUtils::convertFromPlainText(tr("The hardness of the next brush stroke to be painted."), Qt::WhiteSpaceNormal); - _imp->hardnessLabel = new Label(tr("Hardness:"), _imp->brushButtonsBar); - _imp->hardnessLabel->setToolTip(hardnesstt); - _imp->brushButtonsBarLayout->addWidget(_imp->hardnessLabel); - - _imp->hardnessSpinBox = new SpinBox(_imp->brushButtonsBar, SpinBox::eSpinBoxTypeDouble); - QObject::connect( _imp->hardnessSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBreakMultiStrokeTriggered()) ); - _imp->hardnessSpinBox->setMinimum(0); - _imp->hardnessSpinBox->setMaximum(1); - _imp->hardnessSpinBox->setValue(0.2); - _imp->hardnessSpinBox->setToolTip(hardnesstt); - _imp->brushButtonsBarLayout->addWidget(_imp->hardnessSpinBox); - - QString pressHardnesstt = GuiUtils::convertFromPlainText(tr("If checked, the pressure of the pen will dynamically alter the hardness of the next " - "brush stroke."), Qt::WhiteSpaceNormal); - _imp->pressureHardnessButton = new Button(pressureIc, QString(), _imp->brushButtonsBar); - QObject::connect( _imp->pressureHardnessButton, SIGNAL(clicked(bool)), this, SLOT(onPressureHardnessClicked(bool)) ); - _imp->pressureHardnessButton->setToolTip(pressHardnesstt); - _imp->pressureHardnessButton->setFixedSize(medButtonSize); - _imp->pressureHardnessButton->setIconSize(medButtonIconSize); - _imp->pressureHardnessButton->setCheckable(true); - _imp->pressureHardnessButton->setChecked(false); - _imp->pressureHardnessButton->setDown(false); - _imp->brushButtonsBarLayout->addWidget(_imp->pressureHardnessButton); - - _imp->brushButtonsBarLayout->addSpacing(5); - - QString builduptt = GuiUtils::convertFromPlainText(tr("When build-up is enabled, the next brush stroke will build up " - "when painted over itself."), Qt::WhiteSpaceNormal); - - _imp->buildUpLabel = new Label(tr("Build-up:"), _imp->brushButtonsBar); - _imp->buildUpLabel->setToolTip(builduptt); - _imp->brushButtonsBarLayout->addWidget(_imp->buildUpLabel); - - QIcon buildupIc; - buildupIc.addPixmap(buildupOnPix, QIcon::Normal, QIcon::On); - buildupIc.addPixmap(buildupOffPix, QIcon::Normal, QIcon::Off); - _imp->buildUpButton = new Button(buildupIc, QString(), _imp->brushButtonsBar); - QObject::connect( _imp->buildUpButton, SIGNAL(clicked(bool)), this, SLOT(onBuildupClicked(bool)) ); - _imp->buildUpButton->setToolTip(builduptt); - _imp->buildUpButton->setFixedSize(medButtonSize); - _imp->buildUpButton->setIconSize(medButtonIconSize); - _imp->buildUpButton->setCheckable(true); - _imp->buildUpButton->setChecked(true); - _imp->buildUpButton->setDown(true); - _imp->brushButtonsBarLayout->addWidget(_imp->buildUpButton); - - _imp->brushButtonsBarLayout->addSpacing(5); - - QString effecttt = GuiUtils::convertFromPlainText(tr("The strength of the next paint brush effect."), Qt::WhiteSpaceNormal); - _imp->effectLabel = new Label(tr("Effect:"), _imp->brushButtonsBar); - _imp->effectLabel->setToolTip(effecttt); - _imp->effectLabel->setVisible(false); - _imp->brushButtonsBarLayout->addWidget(_imp->effectLabel); - - _imp->effectSpinBox = new SpinBox(_imp->brushButtonsBar, SpinBox::eSpinBoxTypeDouble); - QObject::connect( _imp->effectSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onBreakMultiStrokeTriggered()) ); - _imp->effectSpinBox->setMinimum(0); - _imp->effectSpinBox->setMaximum(100); - _imp->effectSpinBox->setValue(15); - _imp->effectSpinBox->setVisible(false); - _imp->effectSpinBox->setToolTip(effecttt); - _imp->brushButtonsBarLayout->addWidget(_imp->effectSpinBox); - - _imp->brushButtonsBarLayout->addSpacing(5); - - QString timeOfftt = GuiUtils::convertFromPlainText(tr("When the Clone tool is used, this determines depending on the time offset " - "mode the source frame to clone. When in absolute mode, this is the frame " - "number of the source, when in relative mode, this is an offset relative " - "to the current frame."), Qt::WhiteSpaceNormal); - - _imp->timeOffsetLabel = new Label(tr("Time Offset:"), _imp->brushButtonsBar); - _imp->timeOffsetLabel->setVisible(false); - _imp->timeOffsetLabel->setToolTip(timeOfftt); - _imp->brushButtonsBarLayout->addWidget(_imp->timeOffsetLabel); - - _imp->timeOffsetSpinbox = new SpinBox(_imp->brushButtonsBar, SpinBox::eSpinBoxTypeInt); - QObject::connect( _imp->timeOffsetSpinbox, SIGNAL(valueChanged(double)), this, SLOT(onBreakMultiStrokeTriggered()) ); - _imp->timeOffsetSpinbox->setValue(0); - _imp->timeOffsetSpinbox->setVisible(false); - _imp->timeOffsetSpinbox->setToolTip(timeOfftt); - _imp->brushButtonsBarLayout->addWidget(_imp->timeOffsetSpinbox); - - _imp->timeOffsetMode = new ComboBox(_imp->brushButtonsBar); - _imp->timeOffsetMode->setToolTip( GuiUtils::convertFromPlainText(tr("When in absolute mode, this is the frame number of the source, " - "when in relative mode, this is an offset relative to " - "the current frame."), Qt::WhiteSpaceNormal) ); - _imp->timeOffsetMode->addItem( tr("Relative") ); - _imp->timeOffsetMode->addItem( tr("Absolute") ); - _imp->timeOffsetMode->setCurrentIndex_no_emit(0); - _imp->timeOffsetMode->setVisible(false); - _imp->brushButtonsBarLayout->addWidget(_imp->timeOffsetMode); - - _imp->sourceTypeCombobox = new ComboBox(_imp->brushButtonsBar); - _imp->sourceTypeCombobox->setToolTip( GuiUtils::convertFromPlainText(tr( - "Source color used for painting the stroke when the Reveal/Clone tools are used:\n" - "- foreground: the painted result at this point in the hierarchy,\n" - "- background: the original image unpainted connected to bg,\n" - "- backgroundN: the original image unpainted connected to bgN."), Qt::WhiteSpaceNormal) ); - { - _imp->sourceTypeCombobox->addItem( tr("foreground") ); - _imp->sourceTypeCombobox->addItem( tr("background") ); - for (int i = 1; i < 10; ++i) { - QString str = tr("background") + QString::number(i + 1); - _imp->sourceTypeCombobox->addItem(str); - } - } - _imp->sourceTypeCombobox->setCurrentIndex_no_emit(1); - _imp->sourceTypeCombobox->setVisible(false); - _imp->brushButtonsBarLayout->addWidget(_imp->sourceTypeCombobox); - - _imp->resetCloneOffset = new Button(QIcon(), tr("Reset Transform"), _imp->brushButtonsBar); - _imp->resetCloneOffset->setToolTip( GuiUtils::convertFromPlainText(tr("Reset the transform applied before cloning to identity."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->resetCloneOffset, SIGNAL(clicked(bool)), this, SLOT(onResetCloneTransformClicked()) ); - _imp->brushButtonsBarLayout->addWidget(_imp->resetCloneOffset); - _imp->resetCloneOffset->setVisible(false); - - _imp->brushButtonsBarLayout->addStretch(); - _imp->brushButtonsBar->setVisible(false); - - ////////////////////////////////////// CREATING VIEWER LEFT TOOLBAR ////////////////////////////////////// - - QSize rotoToolSize( TO_DPIX(NATRON_LARGE_BUTTON_SIZE), TO_DPIY(NATRON_LARGE_BUTTON_SIZE) ); - - _imp->selectTool = new RotoToolButton(_imp->toolbar); - _imp->selectTool->setFixedSize(rotoToolSize); - _imp->selectTool->setIconSize(rotoToolSize); - _imp->selectTool->setPopupMode(QToolButton::InstantPopup); - QObject::connect( _imp->selectTool, SIGNAL(triggered(QAction*)), this, SLOT(onToolActionTriggered(QAction*)) ); - QKeySequence selectShortCut(Qt::Key_Q); - _imp->selectAllAction = createToolAction(_imp->selectTool, QIcon(pixSelectAll), tr("Select all"), - tr("everything can be selected and moved."), - selectShortCut, eRotoToolSelectAll); - createToolAction(_imp->selectTool, QIcon(pixSelectPoints), tr("Select points"), - tr("works only for the points of the inner shape," - " feather points will not be taken into account."), - selectShortCut, eRotoToolSelectPoints); - createToolAction(_imp->selectTool, QIcon(pixSelectCurves), tr("Select curves"), - tr("only the curves can be selected.") - , selectShortCut, eRotoToolSelectCurves); - createToolAction(_imp->selectTool, QIcon(pixSelectFeather), tr("Select feather points"), tr("only the feather points can be selected."), selectShortCut, eRotoToolSelectFeatherPoints); - - _imp->selectTool->setDown(hasShapes); - _imp->selectTool->setDefaultAction(_imp->selectAllAction); - _imp->allTools.push_back(_imp->selectTool); - _imp->toolbar->addWidget(_imp->selectTool); - - QAction* defaultAction = _imp->selectAllAction; - - _imp->pointsEditionTool = new RotoToolButton(_imp->toolbar); - _imp->pointsEditionTool->setFixedSize(rotoToolSize); - _imp->pointsEditionTool->setIconSize(rotoToolSize); - _imp->pointsEditionTool->setPopupMode(QToolButton::InstantPopup); - QObject::connect( _imp->pointsEditionTool, SIGNAL(triggered(QAction*)), this, SLOT(onToolActionTriggered(QAction*)) ); - _imp->pointsEditionTool->setText( tr("Add points") ); - QKeySequence pointsEditionShortcut(Qt::Key_D); - QAction* addPtsAct = createToolAction(_imp->pointsEditionTool, QIcon(pixAddPts), tr("Add points"), tr("add a new control point to the shape") - , pointsEditionShortcut, eRotoToolAddPoints); - createToolAction(_imp->pointsEditionTool, QIcon(pixRemovePts), tr("Remove points"), QString(), pointsEditionShortcut, eRotoToolRemovePoints); - createToolAction(_imp->pointsEditionTool, QIcon(pixCuspPts), tr("Cusp points"), QString(), pointsEditionShortcut, eRotoToolCuspPoints); - createToolAction(_imp->pointsEditionTool, QIcon(pixSmoothPts), tr("Smooth points"), QString(), pointsEditionShortcut, eRotoToolSmoothPoints); - createToolAction(_imp->pointsEditionTool, QIcon(pixOpenCloseCurve), tr("Open/Close curve"), QString(), pointsEditionShortcut, eRotoToolOpenCloseCurve); - createToolAction(_imp->pointsEditionTool, QIcon(pixRemoveFeather), tr("Remove feather"), tr("set the feather point to be equal to the control point"), pointsEditionShortcut, eRotoToolRemoveFeatherPoints); - _imp->pointsEditionTool->setDown(false); - _imp->pointsEditionTool->setDefaultAction(addPtsAct); - _imp->allTools.push_back(_imp->pointsEditionTool); - _imp->toolbar->addWidget(_imp->pointsEditionTool); - - _imp->bezierEditionTool = new RotoToolButton(_imp->toolbar); - _imp->bezierEditionTool->setFixedSize(rotoToolSize); - _imp->bezierEditionTool->setIconSize(rotoToolSize); - _imp->bezierEditionTool->setPopupMode(QToolButton::InstantPopup); - QObject::connect( _imp->bezierEditionTool, SIGNAL(triggered(QAction*)), this, SLOT(onToolActionTriggered(QAction*)) ); - _imp->bezierEditionTool->setText( QString::fromUtf8("Bezier") ); - _imp->bezierEditionTool->setDown(!hasShapes && !effectIsPaint); - - - QKeySequence editBezierShortcut(Qt::Key_V); - QAction* drawBezierAct = createToolAction(_imp->bezierEditionTool, QIcon(pixBezier), tr("Bezier"), - tr("Edit bezier paths. Click and drag the mouse to adjust tangents. Press enter to close the shape. ") - , editBezierShortcut, eRotoToolDrawBezier); - - ////B-splines are not implemented yet - //createToolAction(_imp->bezierEditionTool, QIcon(), "B-Spline", eRotoToolDrawBSpline); - - createToolAction(_imp->bezierEditionTool, QIcon(pixEllipse), tr("Ellipse"), tr("Hold control to draw the ellipse from its center"), editBezierShortcut, eRotoToolDrawEllipse); - createToolAction(_imp->bezierEditionTool, QIcon(pixRectangle), tr("Rectangle"), QString(), editBezierShortcut, eRotoToolDrawRectangle); - _imp->bezierEditionTool->setDefaultAction(drawBezierAct); - _imp->allTools.push_back(_imp->bezierEditionTool); - _imp->toolbar->addWidget(_imp->bezierEditionTool); - - if (!hasShapes && !effectIsPaint) { - defaultAction = drawBezierAct; - } - - _imp->paintBrushTool = new RotoToolButton(_imp->toolbar); - _imp->paintBrushTool->setFixedSize(rotoToolSize); - _imp->paintBrushTool->setIconSize(rotoToolSize); - _imp->paintBrushTool->setPopupMode(QToolButton::InstantPopup); - QObject::connect( _imp->paintBrushTool, SIGNAL(triggered(QAction*)), this, SLOT(onToolActionTriggered(QAction*)) ); - _imp->paintBrushTool->setText( QString::fromUtf8("Brush") ); - _imp->paintBrushTool->setDown(!hasShapes && effectIsPaint); - QKeySequence brushPaintShortcut(Qt::Key_N); - QAction* brushPaintAct = createToolAction(_imp->paintBrushTool, QIcon(pixPaintBrush), tr("Brush"), tr("Freehand painting"), brushPaintShortcut, eRotoToolSolidBrush); - createToolAction(_imp->paintBrushTool, QIcon(pixPencil), tr("Pencil"), tr("Freehand painting based on bezier curves"), brushPaintShortcut, eRotoToolOpenBezier); - if (effectIsPaint) { - _imp->eraserAction = createToolAction(_imp->paintBrushTool, QIcon(pixEraser), tr("Eraser"), tr("Erase previous paintings"), brushPaintShortcut, eRotoToolEraserBrush); - } - _imp->paintBrushTool->setDefaultAction(brushPaintAct); - _imp->allTools.push_back(_imp->paintBrushTool); - _imp->toolbar->addWidget(_imp->paintBrushTool); - - if (effectIsPaint) { - _imp->cloneBrushTool = new RotoToolButton(_imp->toolbar); - _imp->cloneBrushTool->setFixedSize(rotoToolSize); - _imp->cloneBrushTool->setIconSize(rotoToolSize); - _imp->cloneBrushTool->setPopupMode(QToolButton::InstantPopup); - QObject::connect( _imp->cloneBrushTool, SIGNAL(triggered(QAction*)), this, SLOT(onToolActionTriggered(QAction*)) ); - _imp->cloneBrushTool->setText( QString::fromUtf8("Clone") ); - _imp->cloneBrushTool->setDown(false); - QKeySequence cloneBrushShortcut(Qt::Key_C); - QAction* cloneBrushAct = createToolAction(_imp->cloneBrushTool, QIcon(pixClone), tr("Clone"), tr("Clone a portion of the source image"), cloneBrushShortcut, eRotoToolClone); - createToolAction(_imp->cloneBrushTool, QIcon(pixReveal), tr("Reveal"), tr("Reveal a portion of the source image"), cloneBrushShortcut, eRotoToolReveal); - _imp->cloneBrushTool->setDefaultAction(cloneBrushAct); - _imp->allTools.push_back(_imp->cloneBrushTool); - _imp->toolbar->addWidget(_imp->cloneBrushTool); - - _imp->effectBrushTool = new RotoToolButton(_imp->toolbar); - _imp->effectBrushTool->setFixedSize(rotoToolSize); - _imp->effectBrushTool->setIconSize(rotoToolSize); - _imp->effectBrushTool->setPopupMode(QToolButton::InstantPopup); - QObject::connect( _imp->effectBrushTool, SIGNAL(triggered(QAction*)), this, SLOT(onToolActionTriggered(QAction*)) ); - _imp->effectBrushTool->setText( QString::fromUtf8("Blur") ); - _imp->effectBrushTool->setDown(false); - QKeySequence blurShortcut(Qt::Key_X); - QAction* blurBrushAct = createToolAction(_imp->effectBrushTool, QIcon(pixBlur), tr("Blur"), tr("Blur a portion of the source image"), blurShortcut, eRotoToolBlur); - //createToolAction(_imp->effectBrushTool, QIcon(pixSharpen), tr("Sharpen"), tr("Sharpen a portion of the source image"), blurShortcut, eRotoToolSharpen); - createToolAction(_imp->effectBrushTool, QIcon(pixSmear), tr("Smear"), tr("Blur and displace a portion of the source image along the direction of the pen"), blurShortcut, eRotoToolSmear); - _imp->effectBrushTool->setDefaultAction(blurBrushAct); - _imp->allTools.push_back(_imp->effectBrushTool); - _imp->toolbar->addWidget(_imp->effectBrushTool); - - _imp->mergeBrushTool = new RotoToolButton(_imp->toolbar); - _imp->mergeBrushTool->setFixedSize(rotoToolSize); - _imp->mergeBrushTool->setIconSize(rotoToolSize); - _imp->mergeBrushTool->setPopupMode(QToolButton::InstantPopup); - QObject::connect( _imp->mergeBrushTool, SIGNAL(triggered(QAction*)), this, SLOT(onToolActionTriggered(QAction*)) ); - _imp->mergeBrushTool->setText( QString::fromUtf8("Dodge") ); - _imp->mergeBrushTool->setDown(false); - QKeySequence dodgeBrushShortcut(Qt::Key_E); - QAction* dodgeBrushAct = createToolAction(_imp->mergeBrushTool, QIcon(pixDodge), tr("Dodge"), tr("Make the source image brighter"), dodgeBrushShortcut, eRotoToolDodge); - createToolAction(_imp->mergeBrushTool, QIcon(pixBurn), tr("Burn"), tr("Make the source image darker"), dodgeBrushShortcut, eRotoToolBurn); - _imp->mergeBrushTool->setDefaultAction(dodgeBrushAct); - _imp->allTools.push_back(_imp->mergeBrushTool); - _imp->toolbar->addWidget(_imp->mergeBrushTool); - } - - if (!hasShapes && effectIsPaint) { - defaultAction = brushPaintAct; - } - - ////////////Default action is to make a new bezier - _imp->selectedRole = _imp->selectTool; - onToolActionTriggered(defaultAction); - - QObject::connect( _imp->node->getNode()->getApp()->getTimeLine().get(), SIGNAL(frameChanged(SequenceTime,int)), - this, SLOT(onCurrentFrameChanged(SequenceTime,int)) ); - QObject::connect( _imp->context.get(), SIGNAL(refreshViewerOverlays()), this, SLOT(onRefreshAsked()) ); - QObject::connect( _imp->context.get(), SIGNAL(selectionChanged(int)), this, SLOT(onSelectionChanged(int)) ); - QObject::connect( _imp->context.get(), SIGNAL(itemLockedChanged(int)), this, SLOT(onCurveLockedChanged(int)) ); - QObject::connect( _imp->context.get(), SIGNAL(breakMultiStroke()), this, SLOT(onBreakMultiStrokeTriggered()) ); - QObject::connect ( _imp->viewerTab->getGui()->getApp()->getTimeLine().get(), SIGNAL(frameChanged(SequenceTime,int)), this, - SLOT(onTimelineTimeChanged()) );; - restoreSelectionFromContext(); -} - -RotoGui::~RotoGui() -{ -} - -boost::shared_ptr -RotoGui::getRotoGuiSharedData() const -{ - return _imp->rotoData; -} - -QWidget* -RotoGui::getButtonsBar(RotoGui::RotoRoleEnum role) const -{ - switch (role) { - case eRotoRoleSelection: - - return _imp->selectionButtonsBar; - case eRotoRolePointsEdition: - - return _imp->selectionButtonsBar; - case eRotoRoleBezierEdition: - - return _imp->selectionButtonsBar; - case eRotoRolePaintBrush: - - return _imp->brushButtonsBar; - case eRotoRoleEffectBrush: - - return _imp->brushButtonsBar; - case eRotoRoleCloneBrush: - - return _imp->brushButtonsBar; - case eRotoRoleMergeBrush: - - return _imp->brushButtonsBar; - } - assert(false); - - return NULL; -} - -GuiAppInstance* -RotoGui::getApp() const -{ - return _imp->node->getDagGui()->getGui()->getApp(); -} - -QWidget* -RotoGui::getCurrentButtonsBar() const -{ - return getButtonsBar( getCurrentRole() ); -} - -RotoGui::RotoToolEnum -RotoGui::getSelectedTool() const -{ - return _imp->selectedTool; -} - -void -RotoGui::setCurrentTool(RotoGui::RotoToolEnum tool, - bool emitSignal) -{ - QList actions = _imp->selectTool->actions(); - if (_imp->pointsEditionTool) { - actions.append( _imp->pointsEditionTool->actions() ); - } - if (_imp->bezierEditionTool) { - actions.append( _imp->bezierEditionTool->actions() ); - } - if (_imp->paintBrushTool) { - actions.append( _imp->paintBrushTool->actions() ); - } - if (_imp->cloneBrushTool) { - actions.append( _imp->cloneBrushTool->actions() ); - } - if (_imp->effectBrushTool) { - actions.append( _imp->effectBrushTool->actions() ); - } - if (_imp->mergeBrushTool) { - actions.append( _imp->mergeBrushTool->actions() ); - } - for (int i = 0; i < actions.size(); ++i) { - QPoint data = actions[i]->data().toPoint(); - if ( (RotoGui::RotoToolEnum)data.x() == tool ) { - onToolActionTriggeredInternal(actions[i], emitSignal); - - return; - } - } - assert(false); -} - -QToolBar* -RotoGui::getToolBar() const -{ - return _imp->toolbar; -} - -void -RotoGui::onToolActionTriggered() -{ - QAction* act = qobject_cast( sender() ); - - if (act) { - onToolActionTriggered(act); - } -} - -void -RotoGui::RotoGuiPrivate::toggleToolsSelection(QToolButton* selected) -{ - for (std::list::iterator it = allTools.begin(); it != allTools.end(); ++it) { - if (*it == selected) { - (*it)->setIsSelected(true); - } else { - (*it)->setIsSelected(false); - } - } -} - -void -RotoGui::onToolActionTriggeredInternal(QAction* action, - bool emitSignal) -{ - QPoint data = action->data().toPoint(); - - if ( _imp->selectedTool == (RotoToolEnum)data.x() ) { - return; - } - - RotoRoleEnum actionRole = (RotoRoleEnum)data.y(); - QToolButton* toolButton = 0; - RotoRoleEnum previousRole = getCurrentRole(); - - switch (actionRole) { - case eRotoRoleSelection: - toolButton = _imp->selectTool; - break; - case eRotoRolePointsEdition: - toolButton = _imp->pointsEditionTool; - break; - case eRotoRoleBezierEdition: - toolButton = _imp->bezierEditionTool; - break; - case eRotoRoleEffectBrush: - toolButton = _imp->effectBrushTool; - break; - case eRotoRoleMergeBrush: - toolButton = _imp->mergeBrushTool; - break; - case eRotoRoleCloneBrush: - toolButton = _imp->cloneBrushTool; - break; - case eRotoRolePaintBrush: - toolButton = _imp->paintBrushTool; - break; - default: - assert(false); - break; - } - - if ( (RotoToolEnum)data.x() != eRotoToolBlur ) { - if (_imp->effectLabel) { - _imp->effectLabel->setVisible(false); - } - if (_imp->effectSpinBox) { - _imp->effectSpinBox->setVisible(false); - } - } else { - if (_imp->effectLabel) { - _imp->effectLabel->setVisible(true); - } - if (_imp->effectSpinBox) { - _imp->effectSpinBox->setVisible(true); - } - } - if (actionRole == eRotoRoleCloneBrush) { - _imp->timeOffsetLabel->setVisible(true); - _imp->timeOffsetMode->setVisible(true); - _imp->timeOffsetSpinbox->setVisible(true); - _imp->sourceTypeCombobox->setVisible(true); - _imp->resetCloneOffset->setVisible(true); - if ( (RotoToolEnum)data.x() == eRotoToolClone ) { - _imp->sourceTypeCombobox->setCurrentIndex_no_emit(1); - } else if ( (RotoToolEnum)data.x() == eRotoToolReveal ) { - _imp->sourceTypeCombobox->setCurrentIndex_no_emit(2); - } - } else { - if (_imp->timeOffsetLabel) { - _imp->timeOffsetLabel->setVisible(false); - } - if (_imp->timeOffsetMode) { - _imp->timeOffsetMode->setVisible(false); - } - if (_imp->timeOffsetSpinbox) { - _imp->timeOffsetSpinbox->setVisible(false); - } - if (_imp->sourceTypeCombobox) { - _imp->sourceTypeCombobox->setVisible(false); - } - if (_imp->resetCloneOffset) { - _imp->resetCloneOffset->setVisible(false); - } - } - if ( (actionRole == eRotoRolePaintBrush) || (actionRole == eRotoRoleCloneBrush) || (actionRole == eRotoRoleMergeBrush) || - ( actionRole == eRotoRoleEffectBrush) ) { - if ( ( (RotoToolEnum)data.x() == eRotoToolSolidBrush ) || ( (RotoToolEnum)data.x() == eRotoToolOpenBezier ) ) { - _imp->compositingOperatorButton->setCurrentIndex_no_emit( (int)eMergeOver ); - } else if ( (RotoToolEnum)data.x() == eRotoToolBurn ) { - _imp->compositingOperatorButton->setCurrentIndex_no_emit( (int)eMergeColorBurn ); - } else if ( (RotoToolEnum)data.x() == eRotoToolDodge ) { - _imp->compositingOperatorButton->setCurrentIndex_no_emit( (int)eMergeColorDodge ); - } else { - _imp->compositingOperatorButton->setCurrentIndex_no_emit( (int)eMergeCopy ); - } - } - - if ( (RotoToolEnum)data.x() != eRotoToolEraserBrush ) { - _imp->lastPaintToolAction = action; - } - - _imp->toggleToolsSelection(toolButton); - Q_EMIT roleChanged( (int)previousRole, (int)actionRole ); - - assert(_imp->selectedRole); - if (_imp->selectedRole != toolButton) { - _imp->selectedRole->setDown(false); - } - - ///reset the selected control points - _imp->rotoData->selectedCps.clear(); - _imp->rotoData->showCpsBbox = false; - _imp->rotoData->transformMode = eSelectedCpsTransformModeTranslateAndScale; - _imp->rotoData->selectedCpsBbox.setTopLeft( QPointF(0, 0) ); - _imp->rotoData->selectedCpsBbox.setTopRight( QPointF(0, 0) ); - - ///clear all selection if we were building a new bezier - if ( (previousRole == eRotoRoleBezierEdition) && - ( ( _imp->selectedTool == eRotoToolDrawBezier) || ( _imp->selectedTool == eRotoToolOpenBezier) ) && - _imp->rotoData->builtBezier && - ( (RotoToolEnum)data.x() != _imp->selectedTool ) ) { - _imp->rotoData->builtBezier->setCurveFinished(true); - - _imp->clearSelection(); - } - - - assert(toolButton); - toolButton->setDown(true); - toolButton->setDefaultAction(action); - _imp->selectedRole = toolButton; - _imp->selectedTool = (RotoToolEnum)data.x(); - if (emitSignal) { - Q_EMIT selectedToolChanged( (int)_imp->selectedTool ); - } - - if ( (_imp->selectedTool == eRotoToolBlur) || - ( _imp->selectedTool == eRotoToolBurn) || - ( _imp->selectedTool == eRotoToolDodge) || - ( _imp->selectedTool == eRotoToolClone) || - ( _imp->selectedTool == eRotoToolEraserBrush) || - ( _imp->selectedTool == eRotoToolSolidBrush) || - ( _imp->selectedTool == eRotoToolReveal) || - ( _imp->selectedTool == eRotoToolSmear) || - ( _imp->selectedTool == eRotoToolSharpen) ) { - _imp->makeStroke( true, RotoPoint() ); - } -} // onToolActionTriggeredInternal - -void -RotoGui::onToolActionTriggered(QAction* act) -{ - onToolActionTriggeredInternal(act, true); -} - -RotoGui::RotoRoleEnum -RotoGui::getCurrentRole() const -{ - if (_imp->selectedRole == _imp->selectTool) { - return eRotoRoleSelection; - } else if (_imp->selectedRole == _imp->pointsEditionTool) { - return eRotoRolePointsEdition; - } else if (_imp->selectedRole == _imp->bezierEditionTool) { - return eRotoRoleBezierEdition; - } else if (_imp->selectedRole == _imp->paintBrushTool) { - return eRotoRolePaintBrush; - } else if (_imp->selectedRole == _imp->effectBrushTool) { - return eRotoRoleEffectBrush; - } else if (_imp->selectedRole == _imp->cloneBrushTool) { - return eRotoRoleCloneBrush; - } else if (_imp->selectedRole == _imp->mergeBrushTool) { - return eRotoRoleMergeBrush; - } - assert(false); - - return eRotoRoleSelection; -} - -void -RotoGui::RotoGuiPrivate::drawSelectedCp(double time, - const boost::shared_ptr & cp, - double x, - double y, - const Transform::Matrix3x3& transform) -{ - ///if the tangent is being dragged, color it - bool colorLeftTangent = false; - bool colorRightTangent = false; - - if ( (cp == rotoData->tangentBeingDragged) && - ( ( state == eEventStateDraggingLeftTangent) || ( state == eEventStateDraggingRightTangent) ) ) { - colorLeftTangent = state == eEventStateDraggingLeftTangent ? true : false; - colorRightTangent = !colorLeftTangent; - } - - - Transform::Point3D leftDeriv, rightDeriv; - leftDeriv.z = rightDeriv.z = 1.; - cp->getLeftBezierPointAtTime(true, time, ViewIdx(0), &leftDeriv.x, &leftDeriv.y); - cp->getRightBezierPointAtTime(true, time, ViewIdx(0), &rightDeriv.x, &rightDeriv.y); - leftDeriv = Transform::matApply(transform, leftDeriv); - rightDeriv = Transform::matApply(transform, rightDeriv); - - bool drawLeftHandle = leftDeriv.x != x || leftDeriv.y != y; - bool drawRightHandle = rightDeriv.y != x || rightDeriv.y != y; - glBegin(GL_POINTS); - if (drawLeftHandle) { - if (colorLeftTangent) { - glColor3f(0.2, 1., 0.); - } - glVertex2d(leftDeriv.x, leftDeriv.y); - if (colorLeftTangent) { - glColor3d(0.85, 0.67, 0.); - } - } - if (drawRightHandle) { - if (colorRightTangent) { - glColor3f(0.2, 1., 0.); - } - glVertex2d(rightDeriv.x, rightDeriv.y); - if (colorRightTangent) { - glColor3d(0.85, 0.67, 0.); - } - } - glEnd(); - - glBegin(GL_LINE_STRIP); - if (drawLeftHandle) { - glVertex2d(leftDeriv.x, leftDeriv.y); - } - glVertex2d(x, y); - if (drawRightHandle) { - glVertex2d(rightDeriv.x, rightDeriv.y); - } - glEnd(); -} // drawSelectedCp - -static void -drawEllipse(double x, - double y, - double radiusX, - double radiusY, - int l, - double r, - double g, - double b, - double a) -{ - glColor3f(r * l * a, g * l * a, b * l * a); - - glPushMatrix(); - // center the oval at x_center, y_center - glTranslatef( (float)x, (float)y, 0.f ); - // draw the oval using line segments - glBegin(GL_LINE_LOOP); - // we don't need to be pixel-perfect here, it's just an interact! - // 40 segments is enough. - double m = 2 * 3.14159265358979323846264338327950288419717 / 40.; - for (int i = 0; i < 40; ++i) { - double theta = i * m; - glVertex2d( radiusX * std::cos(theta), radiusY * std::sin(theta) ); - } - glEnd(); - - glPopMatrix(); -} - -void -RotoGui::drawOverlays(double time, - const RenderScale & renderScale, - ViewIdx view) const -{ - std::list< boost::shared_ptr > drawables = _imp->context->getCurvesByRenderOrder(); - std::pair pixelScale; - std::pair viewportSize; - - _imp->viewer->getPixelScale(pixelScale.first, pixelScale.second); - _imp->viewer->getViewportSize(viewportSize.first, viewportSize.second); - - { - GLProtectAttrib a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glLineWidth(1.5); - glEnable(GL_POINT_SMOOTH); - glPointSize(7.); - for (std::list< boost::shared_ptr >::const_iterator it = drawables.begin(); it != drawables.end(); ++it) { - if ( !(*it)->isGloballyActivated() ) { - continue; - } - - - Bezier* isBezier = dynamic_cast( it->get() ); - RotoStrokeItem* isStroke = dynamic_cast( it->get() ); - if (isStroke) { - if (_imp->selectedTool != eRotoToolSelectAll) { - continue; - } - - bool selected = false; - for (SelectedItems::const_iterator it2 = _imp->rotoData->selectedItems.begin(); it2 != _imp->rotoData->selectedItems.end(); ++it2) { - if (it2->get() == isStroke) { - selected = true; - break; - } - } - if (!selected) { - continue; - } - - std::list > > strokes; - isStroke->evaluateStroke(0, time, &strokes); - bool locked = (*it)->isLockedRecursive(); - double curveColor[4]; - if (!locked) { - (*it)->getOverlayColor(curveColor); - } else { - curveColor[0] = 0.8; curveColor[1] = 0.8; curveColor[2] = 0.8; curveColor[3] = 1.; - } - glColor4dv(curveColor); - - for (std::list > >::iterator itStroke = strokes.begin(); itStroke != strokes.end(); ++itStroke) { - glBegin(GL_LINE_STRIP); - for (std::list >::const_iterator it2 = itStroke->begin(); it2 != itStroke->end(); ++it2) { - glVertex2f(it2->first.x, it2->first.y); - } - glEnd(); - } - } else if (isBezier) { - ///draw the bezier - // check if the bbox is visible - // if the bbox is visible, compute the polygon and draw it. - - - RectD bbox = isBezier->getBoundingBox(time); - if ( !_imp->viewer->isVisibleInViewport(bbox) ) { - continue; - } - - std::list< Point > points; - isBezier->evaluateAtTime_DeCasteljau(true, time, 0, 100, &points, NULL); - - bool locked = (*it)->isLockedRecursive(); - double curveColor[4]; - if (!locked) { - (*it)->getOverlayColor(curveColor); - } else { - curveColor[0] = 0.8; curveColor[1] = 0.8; curveColor[2] = 0.8; curveColor[3] = 1.; - } - glColor4dv(curveColor); - - glBegin(GL_LINE_STRIP); - for (std::list::const_iterator it2 = points.begin(); it2 != points.end(); ++it2) { - glVertex2f(it2->x, it2->y); - } - glEnd(); - - ///draw the feather points - std::list< Point > featherPoints; - RectD featherBBox( std::numeric_limits::infinity(), - std::numeric_limits::infinity(), - -std::numeric_limits::infinity(), - -std::numeric_limits::infinity() ); - bool clockWise = isBezier->isFeatherPolygonClockwiseOriented(true, time); - - - if ( isFeatherVisible() ) { - ///Draw feather only if visible (button is toggled in the user interface) - isBezier->evaluateFeatherPointsAtTime_DeCasteljau(true, time, 0, 100, true, &featherPoints, &featherBBox); - - if ( !featherPoints.empty() ) { - glLineStipple(2, 0xAAAA); - glEnable(GL_LINE_STIPPLE); - glBegin(GL_LINE_STRIP); - for (std::list::const_iterator it2 = featherPoints.begin(); it2 != featherPoints.end(); ++it2) { - glVertex2f(it2->x, it2->y); - } - glEnd(); - glDisable(GL_LINE_STIPPLE); - } - } - - ///draw the control points if the bezier is selected - bool selected = false; - for (SelectedItems::const_iterator it2 = _imp->rotoData->selectedItems.begin(); it2 != _imp->rotoData->selectedItems.end(); ++it2) { - if (it2->get() == isBezier) { - selected = true; - break; - } - } - - - if (selected && !locked) { - Transform::Matrix3x3 transform; - isBezier->getTransformAtTime(time, &transform); - - const std::list< boost::shared_ptr > & cps = isBezier->getControlPoints(); - const std::list< boost::shared_ptr > & featherPts = isBezier->getFeatherPoints(); - assert( cps.size() == featherPts.size() ); - - if ( cps.empty() ) { - continue; - } - - double cpHalfWidth = kControlPointMidSize * pixelScale.first; - double cpHalfHeight = kControlPointMidSize * pixelScale.second; - - glColor3d(0.85, 0.67, 0.); - - std::list< boost::shared_ptr >::const_iterator itF = featherPts.begin(); - int index = 0; - std::list< boost::shared_ptr >::const_iterator prevCp = cps.end(); - if ( prevCp != cps.begin() ) { - --prevCp; - } - std::list< boost::shared_ptr >::const_iterator nextCp = cps.begin(); - if ( nextCp != cps.end() ) { - ++nextCp; - } - for (std::list< boost::shared_ptr >::const_iterator it2 = cps.begin(); it2 != cps.end(); - ++it2) { - if ( nextCp == cps.end() ) { - nextCp = cps.begin(); - } - if ( prevCp == cps.end() ) { - prevCp = cps.begin(); - } - assert( itF != featherPts.end() ); // because cps.size() == featherPts.size() - if ( itF == featherPts.end() ) { - break; - } - double x, y; - Transform::Point3D p, pF; - (*it2)->getPositionAtTime(true, time, ViewIdx(0), &p.x, &p.y); - p.z = 1.; - - double xF, yF; - (*itF)->getPositionAtTime(true, time, ViewIdx(0), &pF.x, &pF.y); - pF.z = 1.; - - p = Transform::matApply(transform, p); - pF = Transform::matApply(transform, pF); - - x = p.x; - y = p.y; - xF = pF.x; - yF = pF.y; - - ///draw the feather point only if it is distinct from the associated point - bool drawFeather = isFeatherVisible(); - if (drawFeather) { - drawFeather = !(*it2)->equalsAtTime(true, time, ViewIdx(0), **itF); - } - - - ///if the control point is the only control point being dragged, color it to identify it to the user - bool colorChanged = false; - SelectedCPs::const_iterator firstSelectedCP = _imp->rotoData->selectedCps.begin(); - if ( ( firstSelectedCP != _imp->rotoData->selectedCps.end() ) && - ( ( firstSelectedCP->first == *it2) || ( firstSelectedCP->second == *it2) ) && - ( _imp->rotoData->selectedCps.size() == 1) && - ( ( _imp->state == eEventStateDraggingSelectedControlPoints) || ( _imp->state == eEventStateDraggingControlPoint) ) ) { - glColor3f(0., 1., 1.); - colorChanged = true; - } - - for (SelectedCPs::const_iterator cpIt = _imp->rotoData->selectedCps.begin(); - cpIt != _imp->rotoData->selectedCps.end(); - ++cpIt) { - ///if the control point is selected, draw its tangent handles - if (cpIt->first == *it2) { - _imp->drawSelectedCp(time, cpIt->first, x, y, transform); - if (drawFeather) { - _imp->drawSelectedCp(time, cpIt->second, xF, yF, transform); - } - glColor3f(0.2, 1., 0.); - colorChanged = true; - break; - } else if (cpIt->second == *it2) { - _imp->drawSelectedCp(time, cpIt->second, x, y, transform); - if (drawFeather) { - _imp->drawSelectedCp(time, cpIt->first, xF, yF, transform); - } - glColor3f(0.2, 1., 0.); - colorChanged = true; - break; - } - } // for(cpIt) - - glBegin(GL_POLYGON); - glVertex2f(x - cpHalfWidth, y - cpHalfHeight); - glVertex2f(x + cpHalfWidth, y - cpHalfHeight); - glVertex2f(x + cpHalfWidth, y + cpHalfHeight); - glVertex2f(x - cpHalfWidth, y + cpHalfHeight); - glEnd(); - - if (colorChanged) { - glColor3d(0.85, 0.67, 0.); - } - - if ( (firstSelectedCP->first == *itF) - && ( _imp->rotoData->selectedCps.size() == 1) && - ( ( _imp->state == eEventStateDraggingSelectedControlPoints) || ( _imp->state == eEventStateDraggingControlPoint) ) - && !colorChanged ) { - glColor3f(0.2, 1., 0.); - colorChanged = true; - } - - - double distFeatherX = 20. * pixelScale.first; - double distFeatherY = 20. * pixelScale.second; - bool isHovered = false; - if (_imp->rotoData->featherBarBeingHovered.first) { - assert(_imp->rotoData->featherBarBeingHovered.second); - if ( _imp->rotoData->featherBarBeingHovered.first->isFeatherPoint() ) { - isHovered = _imp->rotoData->featherBarBeingHovered.first == *itF; - } else if ( _imp->rotoData->featherBarBeingHovered.second->isFeatherPoint() ) { - isHovered = _imp->rotoData->featherBarBeingHovered.second == *itF; - } - } - - if (drawFeather) { - glBegin(GL_POLYGON); - glVertex2f(xF - cpHalfWidth, yF - cpHalfHeight); - glVertex2f(xF + cpHalfWidth, yF - cpHalfHeight); - glVertex2f(xF + cpHalfWidth, yF + cpHalfHeight); - glVertex2f(xF - cpHalfWidth, yF + cpHalfHeight); - glEnd(); - - - if ( ( (_imp->state == eEventStateDraggingFeatherBar) && - ( ( *itF == _imp->rotoData->featherBarBeingDragged.first) || ( *itF == _imp->rotoData->featherBarBeingDragged.second) ) ) || - isHovered ) { - glColor3f(0.2, 1., 0.); - colorChanged = true; - } else { - glColor4dv(curveColor); - } - - double beyondX, beyondY; - double dx = (xF - x); - double dy = (yF - y); - double dist = sqrt(dx * dx + dy * dy); - beyondX = ( dx * (dist + distFeatherX) ) / dist + x; - beyondY = ( dy * (dist + distFeatherY) ) / dist + y; - - ///draw a link between the feather point and the control point. - ///Also extend that link of 20 pixels beyond the feather point. - - glBegin(GL_LINE_STRIP); - glVertex2f(x, y); - glVertex2f(xF, yF); - glVertex2f(beyondX, beyondY); - glEnd(); - - glColor3d(0.85, 0.67, 0.); - } else if ( isFeatherVisible() ) { - ///if the feather point is identical to the control point - ///draw a small hint line that the user can drag to move the feather point - if ( !isBezier->isOpenBezier() && ( (_imp->selectedTool == eRotoToolSelectAll) || (_imp->selectedTool == eRotoToolSelectFeatherPoints) ) ) { - int cpCount = (*it2)->getBezier()->getControlPointsCount(); - if (cpCount > 1) { - Point controlPoint; - controlPoint.x = x; - controlPoint.y = y; - Point featherPoint; - featherPoint.x = xF; - featherPoint.y = yF; - - - Bezier::expandToFeatherDistance(true, controlPoint, &featherPoint, distFeatherX, time, clockWise, transform, prevCp, it2, nextCp); - - if ( ( (_imp->state == eEventStateDraggingFeatherBar) && - ( ( *itF == _imp->rotoData->featherBarBeingDragged.first) || - ( *itF == _imp->rotoData->featherBarBeingDragged.second) ) ) || isHovered ) { - glColor3f(0.2, 1., 0.); - colorChanged = true; - } else { - glColor4dv(curveColor); - } - - glBegin(GL_LINES); - glVertex2f(x, y); - glVertex2f(featherPoint.x, featherPoint.y); - glEnd(); - - glColor3d(0.85, 0.67, 0.); - } - } - } // isFeatherVisible() - - - if (colorChanged) { - glColor3d(0.85, 0.67, 0.); - } - - // increment for next iteration - if ( itF != featherPts.end() ) { - ++itF; - } - if ( nextCp != cps.end() ) { - ++nextCp; - } - if ( prevCp != cps.end() ) { - ++prevCp; - } - ++index; - } // for(it2) - } // if ( ( selected != _imp->rotoData->selectedBeziers.end() ) && !locked ) { - } // if (isBezier) - glCheckError(); - } // for (std::list< boost::shared_ptr >::const_iterator it = drawables.begin(); it != drawables.end(); ++it) { - - if ( _imp->context->isRotoPaint() && - ( ( _imp->selectedRole == _imp->mergeBrushTool) || - ( _imp->selectedRole == _imp->effectBrushTool) || - ( _imp->selectedRole == _imp->paintBrushTool) || - ( _imp->selectedRole == _imp->cloneBrushTool) ) && - ( _imp->selectedTool != eRotoToolOpenBezier) ) { - QPoint widgetPos = _imp->viewer->mapToGlobal( _imp->viewer->mapFromParent( _imp->viewer->pos() ) ); - QRect r( widgetPos.x(), widgetPos.y(), _imp->viewer->width(), _imp->viewer->height() ); - - if ( r.contains( QCursor::pos() ) ) { - //Draw a circle around the cursor - double brushSize = _imp->sizeSpinbox->value(); - GLdouble projection[16]; - glGetDoublev( GL_PROJECTION_MATRIX, projection); - OfxPointD shadow; // how much to translate GL_PROJECTION to get exactly one pixel on screen - shadow.x = 2. / (projection[0] * viewportSize.first); - shadow.y = 2. / (projection[5] * viewportSize.second); - - double halfBrush = brushSize / 2.; - QPointF ellipsePos; - if ( (_imp->state == eEventStateDraggingBrushSize) || (_imp->state == eEventStateDraggingBrushOpacity) ) { - ellipsePos = _imp->mouseCenterOnSizeChange; - } else { - ellipsePos = _imp->lastMousePos; - } - double opacity = _imp->opacitySpinbox->value(); - - for (int l = 0; l < 2; ++l) { - glMatrixMode(GL_PROJECTION); - int direction = (l == 0) ? 1 : -1; - // translate (1,-1) pixels - glTranslated(direction * shadow.x, -direction * shadow.y, 0); - glMatrixMode(GL_MODELVIEW); - drawEllipse(ellipsePos.x(), ellipsePos.y(), halfBrush, halfBrush, l, 1.f, 1.f, 1.f, opacity); - - glColor3f(.5f * l * opacity, .5f * l * opacity, .5f * l * opacity); - - - if ( ( (_imp->selectedTool == eRotoToolClone) || (_imp->selectedTool == eRotoToolReveal) ) && - ( ( _imp->rotoData->cloneOffset.first != 0) || ( _imp->rotoData->cloneOffset.second != 0) ) ) { - glBegin(GL_LINES); - - if (_imp->state == eEventStateDraggingCloneOffset) { - //draw a line between the center of the 2 ellipses - glVertex2d( ellipsePos.x(), ellipsePos.y() ); - glVertex2d(ellipsePos.x() + _imp->rotoData->cloneOffset.first, ellipsePos.y() + _imp->rotoData->cloneOffset.second); - } - //draw a cross in the center of the source ellipse - glVertex2d(ellipsePos.x() + _imp->rotoData->cloneOffset.first, ellipsePos.y() + _imp->rotoData->cloneOffset.second - halfBrush); - glVertex2d(ellipsePos.x() + _imp->rotoData->cloneOffset.first, ellipsePos.y() + _imp->rotoData->cloneOffset.second + halfBrush); - glVertex2d(ellipsePos.x() + _imp->rotoData->cloneOffset.first - halfBrush, ellipsePos.y() + _imp->rotoData->cloneOffset.second); - glVertex2d(ellipsePos.x() + _imp->rotoData->cloneOffset.first + halfBrush, ellipsePos.y() + _imp->rotoData->cloneOffset.second); - glEnd(); - - - //draw the source ellipse - drawEllipse(ellipsePos.x() + _imp->rotoData->cloneOffset.first, ellipsePos.y() + _imp->rotoData->cloneOffset.second, halfBrush, halfBrush, l, 1.f, 1.f, 1.f, opacity / 2.); - } - } - } - } - } // GLProtectAttrib a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_CURRENT_BIT); - - if ( _imp->rotoData->showCpsBbox && _imp->node->isSettingsPanelVisible() ) { - _imp->drawSelectedCpsBBOX(); - } - glCheckError(); - - NodePtr node = _imp->node->getNode(); - node->getEffectInstance()->setCurrentViewportForOverlays_public(_imp->viewer); - node->drawHostOverlay(time, renderScale, view); -} // drawOverlays - -void -RotoGui::RotoGuiPrivate::drawArrow(double centerX, - double centerY, - double rotate, - bool hovered, - const std::pair & pixelScale) -{ - GLProtectMatrix p(GL_MODELVIEW); - - if (hovered) { - glColor3f(0., 1., 0.); - } else { - glColor3f(1., 1., 1.); - } - - double arrowLenght = kTransformArrowLenght * pixelScale.second; - double arrowWidth = kTransformArrowWidth * pixelScale.second; - double arrowHeadHeight = 4 * pixelScale.second; - - glTranslatef(centerX, centerY, 0.); - glRotatef(rotate, 0., 0., 1.); - QPointF bottom(0., -arrowLenght); - QPointF top(0, arrowLenght); - ///the arrow head is 4 pixels long and kTransformArrowWidth * 2 large - glBegin(GL_LINES); - glVertex2f( top.x(), top.y() ); - glVertex2f( bottom.x(), bottom.y() ); - glEnd(); - - glBegin(GL_POLYGON); - glVertex2f( bottom.x(), bottom.y() ); - glVertex2f(bottom.x() + arrowWidth, bottom.y() + arrowHeadHeight); - glVertex2f(bottom.x() - arrowWidth, bottom.y() + arrowHeadHeight); - glEnd(); - - glBegin(GL_POLYGON); - glVertex2f( top.x(), top.y() ); - glVertex2f(top.x() - arrowWidth, top.y() - arrowHeadHeight); - glVertex2f(top.x() + arrowWidth, top.y() - arrowHeadHeight); - glEnd(); -} - -void -RotoGui::RotoGuiPrivate::drawBendedArrow(double centerX, - double centerY, - double rotate, - bool hovered, - const std::pair & pixelScale) -{ - GLProtectMatrix p(GL_MODELVIEW); - - if (hovered) { - glColor3f(0., 1., 0.); - } else { - glColor3f(1., 1., 1.); - } - - double arrowLenght = kTransformArrowLenght * pixelScale.second; - double arrowWidth = kTransformArrowWidth * pixelScale.second; - double arrowHeadHeight = 4 * pixelScale.second; - - glTranslatef(centerX, centerY, 0.); - glRotatef(rotate, 0., 0., 1.); - - /// by default we draw the top left - QPointF bottom(0., -arrowLenght / 2.); - QPointF right(arrowLenght / 2., 0.); - glBegin (GL_LINE_STRIP); - glVertex2f ( bottom.x(), bottom.y() ); - glVertex2f (0., 0.); - glVertex2f ( right.x(), right.y() ); - glEnd (); - - glBegin(GL_POLYGON); - glVertex2f(bottom.x(), bottom.y() - arrowHeadHeight); - glVertex2f( bottom.x() - arrowWidth, bottom.y() ); - glVertex2f( bottom.x() + arrowWidth, bottom.y() ); - glEnd(); - - glBegin(GL_POLYGON); - glVertex2f( right.x() + arrowHeadHeight, right.y() ); - glVertex2f(right.x(), right.y() - arrowWidth); - glVertex2f(right.x(), right.y() + arrowWidth); - glEnd(); -} - -void -RotoGui::RotoGuiPrivate::drawSelectedCpsBBOX() -{ - std::pair pixelScale; - - viewer->getPixelScale(pixelScale.first, pixelScale.second); - - { - GLProtectAttrib a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT | GL_TRANSFORM_BIT); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - - - QPointF topLeft = rotoData->selectedCpsBbox.topLeft(); - QPointF btmRight = rotoData->selectedCpsBbox.bottomRight(); - - glLineWidth(1.5); - - if (hoverState == eHoverStateBbox) { - glColor4f(0.9, 0.5, 0, 1.); - } else { - glColor4f(0.8, 0.8, 0.8, 1.); - } - glBegin(GL_LINE_LOOP); - glVertex2f( topLeft.x(), btmRight.y() ); - glVertex2f( topLeft.x(), topLeft.y() ); - glVertex2f( btmRight.x(), topLeft.y() ); - glVertex2f( btmRight.x(), btmRight.y() ); - glEnd(); - - double midX = ( topLeft.x() + btmRight.x() ) / 2.; - double midY = ( btmRight.y() + topLeft.y() ) / 2.; - double xHairMidSizeX = kXHairSelectedCpsBox * pixelScale.first; - double xHairMidSizeY = kXHairSelectedCpsBox * pixelScale.second; - QLineF selectedCpsCrossHorizLine; - selectedCpsCrossHorizLine.setLine(midX - xHairMidSizeX, midY, midX + xHairMidSizeX, midY); - QLineF selectedCpsCrossVertLine; - selectedCpsCrossVertLine.setLine(midX, midY - xHairMidSizeY, midX, midY + xHairMidSizeY); - - glBegin(GL_LINES); - glVertex2f( std::max( selectedCpsCrossHorizLine.p1().x(), topLeft.x() ), selectedCpsCrossHorizLine.p1().y() ); - glVertex2f( std::min( selectedCpsCrossHorizLine.p2().x(), btmRight.x() ), selectedCpsCrossHorizLine.p2().y() ); - glVertex2f( selectedCpsCrossVertLine.p1().x(), std::max( selectedCpsCrossVertLine.p1().y(), btmRight.y() ) ); - glVertex2f( selectedCpsCrossVertLine.p2().x(), std::min( selectedCpsCrossVertLine.p2().y(), topLeft.y() ) ); - glEnd(); - - glCheckError(); - - - QPointF midTop( ( topLeft.x() + btmRight.x() ) / 2., topLeft.y() ); - QPointF midRight(btmRight.x(), ( topLeft.y() + btmRight.y() ) / 2.); - QPointF midBtm( ( topLeft.x() + btmRight.x() ) / 2., btmRight.y() ); - QPointF midLeft(topLeft.x(), ( topLeft.y() + btmRight.y() ) / 2.); - - ///draw the 4 corners points and the 4 mid points - glPointSize(5.f); - glBegin(GL_POINTS); - glVertex2f( topLeft.x(), topLeft.y() ); - glVertex2f( btmRight.x(), topLeft.y() ); - glVertex2f( btmRight.x(), btmRight.y() ); - glVertex2f( topLeft.x(), btmRight.y() ); - - glVertex2f( midTop.x(), midTop.y() ); - glVertex2f( midRight.x(), midRight.y() ); - glVertex2f( midBtm.x(), midBtm.y() ); - glVertex2f( midLeft.x(), midLeft.y() ); - glEnd(); - - ///now draw the handles to indicate the user he/she can transform the selection rectangle - ///draw it only if it is not dragged - bool drawHandles = state != eEventStateDraggingBBoxBtmLeft && state != eEventStateDraggingBBoxBtmRight && - state != eEventStateDraggingBBoxTopLeft && state != eEventStateDraggingBBoxTopRight && state != eEventStateDraggingBBoxMidTop - && state != eEventStateDraggingBBoxMidRight && state != eEventStateDraggingBBoxMidLeft && state != eEventStateDraggingBBoxMidBtm; - - - if (drawHandles) { - double offset = kTransformArrowOffsetFromPoint * pixelScale.first; - double halfOffset = offset / 2.; - if (rotoData->transformMode == eSelectedCpsTransformModeTranslateAndScale) { - ///draw mid top arrow vertical - drawArrow(midTop.x(), midTop.y() + offset, 0., hoverState == eHoverStateBboxMidTop, pixelScale); - ///draw mid right arrow horizontal - drawArrow(midRight.x() + offset, midRight.y(), 90., hoverState == eHoverStateBboxMidRight, pixelScale); - ///draw mid btm arrow vertical - drawArrow(midBtm.x(), midBtm.y() - offset, 0., hoverState == eHoverStateBboxMidBtm, pixelScale); - ///draw mid left arrow horizontal - drawArrow(midLeft.x() - offset, midLeft.y(), 90., hoverState == eHoverStateBboxMidLeft, pixelScale); - ///draw top left arrow rotated - drawArrow(topLeft.x() - offset, topLeft.y() + offset, 45., hoverState == eHoverStateBboxTopLeft, pixelScale); - ///draw top right arrow rotated - drawArrow(btmRight.x() + offset, topLeft.y() + offset, -45., hoverState == eHoverStateBboxTopRight, pixelScale); - ///draw btm right arrow rotated - drawArrow(btmRight.x() + offset, btmRight.y() - offset, 45., hoverState == eHoverStateBboxBtmRight, pixelScale); - ///draw btm left arrow rotated - drawArrow(topLeft.x() - offset, btmRight.y() - offset, -45., hoverState == eHoverStateBboxBtmLeft, pixelScale); - } else { - ///draw mid top arrow horizontal - drawArrow(midTop.x(), midTop.y() + offset, 90., hoverState == eHoverStateBboxMidTop, pixelScale); - ///draw mid right arrow vertical - drawArrow(midRight.x() + offset, midRight.y(), 0., hoverState == eHoverStateBboxMidRight, pixelScale); - ///draw mid btm arrow horizontal - drawArrow(midBtm.x(), midBtm.y() - offset, 90., hoverState == eHoverStateBboxMidBtm, pixelScale); - ///draw mid left arrow vertical - drawArrow(midLeft.x() - offset, midLeft.y(), 0., hoverState == eHoverStateBboxMidLeft, pixelScale); - ///draw the top left bended - drawBendedArrow(topLeft.x() - halfOffset, topLeft.y() + halfOffset, 0., hoverState == eHoverStateBboxTopLeft, pixelScale); - ///draw the top right bended - drawBendedArrow(btmRight.x() + halfOffset, topLeft.y() + halfOffset, -90, hoverState == eHoverStateBboxTopRight, pixelScale); - ///draw the btm right bended - drawBendedArrow(btmRight.x() + halfOffset, btmRight.y() - halfOffset, -180, hoverState == eHoverStateBboxBtmRight, pixelScale); - ///draw the btm left bended - drawBendedArrow(topLeft.x() - halfOffset, btmRight.y() - halfOffset, 90, hoverState == eHoverStateBboxBtmLeft, pixelScale); - } - } - } // GLProtectAttrib a(GL_HINT_BIT | GL_ENABLE_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT); -} // drawSelectedCpsBBOX - -void -RotoGui::onSelectionCleared() -{ - if (!isStickySelectionEnabled() && !_imp->shiftDown) { - _imp->clearSelection(); - } -} - -void -RotoGui::updateSelectionFromSelectionRectangle(bool onRelease) -{ - if ( !onRelease || !_imp->node->isSettingsPanelVisible() ) { - return; - } - - bool stickySel = isStickySelectionEnabled(); - if (!stickySel && !_imp->shiftDown) { - _imp->clearCPSSelection(); - _imp->rotoData->selectedItems.clear(); - } - - int selectionMode = -1; - if (_imp->selectedTool == eRotoToolSelectAll) { - selectionMode = 0; - } else if (_imp->selectedTool == eRotoToolSelectPoints) { - selectionMode = 1; - } else if ( (_imp->selectedTool == eRotoToolSelectFeatherPoints) || (_imp->selectedTool == eRotoToolSelectCurves) ) { - selectionMode = 2; - } - - - double l, r, b, t; - _imp->viewer->getSelectionRectangle(l, r, b, t); - std::list > curves = _imp->context->getCurvesByRenderOrder(); - for (std::list >::const_iterator it = curves.begin(); it != curves.end(); ++it) { - boost::shared_ptr isBezier = boost::dynamic_pointer_cast(*it); - if ( (*it)->isLockedRecursive() ) { - continue; - } - - if (isBezier) { - SelectedCPs points = isBezier->controlPointsWithinRect(l, r, b, t, 0, selectionMode); - if (_imp->selectedTool != eRotoToolSelectCurves) { - for (SelectedCPs::iterator ptIt = points.begin(); ptIt != points.end(); ++ptIt) { - if ( !isFeatherVisible() && ptIt->first->isFeatherPoint() ) { - continue; - } - SelectedCPs::iterator foundCP = std::find(_imp->rotoData->selectedCps.begin(), _imp->rotoData->selectedCps.end(), *ptIt); - if ( foundCP == _imp->rotoData->selectedCps.end() ) { - if (!_imp->shiftDown || !_imp->ctrlDown) { - _imp->rotoData->selectedCps.push_back(*ptIt); - } - } else { - if (_imp->shiftDown && _imp->ctrlDown) { - _imp->rotoData->selectedCps.erase(foundCP); - } - } - } - } - if ( !points.empty() ) { - _imp->rotoData->selectedItems.push_back(isBezier); - } - } - } - - if ( !_imp->rotoData->selectedItems.empty() ) { - _imp->context->select(_imp->rotoData->selectedItems, RotoItem::eSelectionReasonOverlayInteract); - } else if (!stickySel && !_imp->shiftDown) { - _imp->context->clearSelection(RotoItem::eSelectionReasonOverlayInteract); - } - - - _imp->computeSelectedCpsBBOX(); -} // RotoGui::updateSelectionFromSelectionRectangle - -void -RotoGui::RotoGuiPrivate::clearSelection() -{ - clearBeziersSelection(); - clearCPSSelection(); -} - -bool -RotoGui::RotoGuiPrivate::hasSelection() const -{ - return !rotoData->selectedItems.empty() || !rotoData->selectedCps.empty(); -} - -void -RotoGui::RotoGuiPrivate::clearCPSSelection() -{ - rotoData->selectedCps.clear(); - rotoData->showCpsBbox = false; - rotoData->transformMode = eSelectedCpsTransformModeTranslateAndScale; - rotoData->selectedCpsBbox.setTopLeft( QPointF(0, 0) ); - rotoData->selectedCpsBbox.setTopRight( QPointF(0, 0) ); -} - -void -RotoGui::RotoGuiPrivate::clearBeziersSelection() -{ - context->clearSelection(RotoItem::eSelectionReasonOverlayInteract); - rotoData->selectedItems.clear(); -} - -bool -RotoGui::RotoGuiPrivate::removeItemFromSelection(const boost::shared_ptr& b) -{ - for (SelectedItems::iterator fb = rotoData->selectedItems.begin(); fb != rotoData->selectedItems.end(); ++fb) { - if ( fb->get() == b.get() ) { - context->deselect(*fb, RotoItem::eSelectionReasonOverlayInteract); - rotoData->selectedItems.erase(fb); - - return true; - } - } - - return false; -} - -static void -handleControlPointMaximum(double time, - const BezierCP & p, - double* l, - double *b, - double *r, - double *t) -{ - double x, y, xLeft, yLeft, xRight, yRight; - - p.getPositionAtTime(true, time, ViewIdx(0), &x, &y); - p.getLeftBezierPointAtTime(true, time, ViewIdx(0), &xLeft, &yLeft); - p.getRightBezierPointAtTime(true, time, ViewIdx(0), &xRight, &yRight); - - *r = std::max(x, *r); - *l = std::min(x, *l); - - *r = std::max(xLeft, *r); - *l = std::min(xLeft, *l); - - *r = std::max(xRight, *r); - *l = std::min(xRight, *l); - - *t = std::max(y, *t); - *b = std::min(y, *b); - - *t = std::max(yLeft, *t); - *b = std::min(yLeft, *b); - - - *t = std::max(yRight, *t); - *b = std::min(yRight, *b); -} - -void -RotoGui::RotoGuiPrivate::computeSelectedCpsBBOX() -{ - NodePtr n = node->getNode(); - - if ( ( n && !n->isActivated() ) || !viewer ) { - return; - } - double time = context->getTimelineCurrentTime(); - std::pair pixelScale; - - viewer->getPixelScale(pixelScale.first, pixelScale.second); - - - double l = INT_MAX, r = INT_MIN, b = INT_MAX, t = INT_MIN; - for (SelectedCPs::iterator it = rotoData->selectedCps.begin(); it != rotoData->selectedCps.end(); ++it) { - handleControlPointMaximum(time, *(it->first), &l, &b, &r, &t); - if (it->second) { - handleControlPointMaximum(time, *(it->second), &l, &b, &r, &t); - } - } - rotoData->selectedCpsBbox.setCoords(l, t, r, b); - if (rotoData->selectedCps.size() > 1) { - rotoData->showCpsBbox = true; - } else { - rotoData->showCpsBbox = false; - } -} - -QPointF -RotoGui::RotoGuiPrivate::getSelectedCpsBBOXCenter() -{ - return rotoData->selectedCpsBbox.center(); -} - -void -RotoGui::RotoGuiPrivate::handleBezierSelection(const boost::shared_ptr & curve, - QMouseEvent* e) -{ - ///find out if the bezier is already selected. - bool found = false; - - for (SelectedItems::iterator it = rotoData->selectedItems.begin(); it != rotoData->selectedItems.end(); ++it) { - if ( it->get() == curve.get() ) { - found = true; - break; - } - } - - if (!found) { - ///clear previous selection if the SHIFT modifier isn't held - if ( !modCASIsShift(e) ) { - clearBeziersSelection(); - } - rotoData->selectedItems.push_back(curve); - context->select(curve, RotoItem::eSelectionReasonOverlayInteract); - } -} - -void -RotoGui::RotoGuiPrivate::handleControlPointSelection(const std::pair, - boost::shared_ptr > & p, - QMouseEvent* e) -{ - ///find out if the cp is already selected. - SelectedCPs::iterator foundCP = rotoData->selectedCps.end(); - - for (SelectedCPs::iterator it = rotoData->selectedCps.begin(); it != rotoData->selectedCps.end(); ++it) { - if (p.first == it->first) { - foundCP = it; - break; - } - } - - if ( foundCP == rotoData->selectedCps.end() ) { - ///clear previous selection if the SHIFT modifier isn't held - if ( !modCASIsShift(e) ) { - rotoData->selectedCps.clear(); - } - rotoData->selectedCps.push_back(p); - computeSelectedCpsBBOX(); - } else { - ///Erase the point from the selection to allow the user to toggle the selection - if ( modCASIsShift(e) ) { - rotoData->selectedCps.erase(foundCP); - computeSelectedCpsBBOX(); - } - } - - rotoData->cpBeingDragged = p; - state = eEventStateDraggingControlPoint; -} - -bool -RotoGui::penDown(double time, - const RenderScale & renderScale, - ViewIdx view, - PenType pen, - bool isTabletEvent, - const QPointF & viewportPos, - const QPointF & pos, - double pressure, - double timestamp, - QMouseEvent* e) -{ - NodePtr node = _imp->node->getNode(); - - node->getEffectInstance()->setCurrentViewportForOverlays_public(_imp->viewer); - if ( node->onOverlayPenDownDefault(time, renderScale, view, viewportPos, pos, pressure) ) { - return true; - } - - std::pair pixelScale; - - _imp->viewer->getPixelScale(pixelScale.first, pixelScale.second); - - bool didSomething = false; - double tangentSelectionTol = kTangentHandleSelectionTolerance * pixelScale.first; - double cpSelectionTolerance = kControlPointSelectionTolerance * pixelScale.first; - - _imp->lastTabletDownTriggeredEraser = false; - if (_imp->context->isRotoPaint() && isTabletEvent) { - if ( (pen == ePenTypeEraser) && (_imp->selectedTool != eRotoToolEraserBrush) ) { - onToolActionTriggered(_imp->eraserAction); - _imp->lastTabletDownTriggeredEraser = true; - } - } - - - //////////////////BEZIER SELECTION - /////Check if the point is nearby a bezier - ///tolerance for bezier selection - double bezierSelectionTolerance = kBezierSelectionTolerance * pixelScale.first; - double nearbyBezierT; - int nearbyBezierCPIndex; - bool isFeather; - boost::shared_ptr nearbyBezier = - _imp->context->isNearbyBezier(pos.x(), pos.y(), bezierSelectionTolerance, &nearbyBezierCPIndex, &nearbyBezierT, &isFeather); - std::pair, boost::shared_ptr > nearbyCP; - int nearbyCpIndex = -1; - if (nearbyBezier) { - /////////////////CONTROL POINT SELECTION - //////Check if the point is nearby a control point of a selected bezier - ///Find out if the user selected a control point - - Bezier::ControlPointSelectionPrefEnum pref = Bezier::eControlPointSelectionPrefWhateverFirst; - if ( (_imp->selectedTool == eRotoToolSelectFeatherPoints) && isFeatherVisible() ) { - pref = Bezier::eControlPointSelectionPrefFeatherFirst; - } - - nearbyCP = nearbyBezier->isNearbyControlPoint(pos.x(), pos.y(), cpSelectionTolerance, pref, &nearbyCpIndex); - } - - ////////////////// TANGENT SELECTION - ///in all cases except cusp/smooth if a control point is selected, check if the user clicked on a tangent handle - ///in which case we go into eEventStateDraggingTangent mode - if ( !nearbyCP.first && - ( _imp->selectedTool != eRotoToolCuspPoints) && - ( _imp->selectedTool != eRotoToolSmoothPoints) && - ( _imp->selectedTool != eRotoToolSelectCurves) ) { - for (SelectedCPs::iterator it = _imp->rotoData->selectedCps.begin(); it != _imp->rotoData->selectedCps.end(); ++it) { - if ( (_imp->selectedTool == eRotoToolSelectAll) || - ( _imp->selectedTool == eRotoToolDrawBezier) ) { - int ret = it->first->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), tangentSelectionTol); - if (ret >= 0) { - _imp->rotoData->tangentBeingDragged = it->first; - _imp->state = ret == 0 ? eEventStateDraggingLeftTangent : eEventStateDraggingRightTangent; - didSomething = true; - } else { - ///try with the counter part point - if (it->second) { - ret = it->second->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), tangentSelectionTol); - } - if (ret >= 0) { - _imp->rotoData->tangentBeingDragged = it->second; - _imp->state = ret == 0 ? eEventStateDraggingLeftTangent : eEventStateDraggingRightTangent; - didSomething = true; - } - } - } else if (_imp->selectedTool == eRotoToolSelectFeatherPoints) { - const boost::shared_ptr & fp = it->first->isFeatherPoint() ? it->first : it->second; - int ret = fp->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), tangentSelectionTol); - if (ret >= 0) { - _imp->rotoData->tangentBeingDragged = fp; - _imp->state = ret == 0 ? eEventStateDraggingLeftTangent : eEventStateDraggingRightTangent; - didSomething = true; - } - } else if (_imp->selectedTool == eRotoToolSelectPoints) { - const boost::shared_ptr & cp = it->first->isFeatherPoint() ? it->second : it->first; - int ret = cp->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), tangentSelectionTol); - if (ret >= 0) { - _imp->rotoData->tangentBeingDragged = cp; - _imp->state = ret == 0 ? eEventStateDraggingLeftTangent : eEventStateDraggingRightTangent; - didSomething = true; - } - } - - ///check in case this is a feather tangent - if ( _imp->rotoData->tangentBeingDragged && _imp->rotoData->tangentBeingDragged->isFeatherPoint() && !isFeatherVisible() ) { - _imp->rotoData->tangentBeingDragged.reset(); - _imp->state = eEventStateNone; - didSomething = false; - } - - if (didSomething) { - return didSomething; - } - } - } - - - switch (_imp->selectedTool) { - case eRotoToolSelectAll: - case eRotoToolSelectPoints: - case eRotoToolSelectFeatherPoints: { - if ( ( _imp->selectedTool == eRotoToolSelectFeatherPoints) && !isFeatherVisible() ) { - ///nothing to do - break; - } - std::pair, boost::shared_ptr > featherBarSel; - if ( ( ( _imp->selectedTool == eRotoToolSelectAll) || ( _imp->selectedTool == eRotoToolSelectFeatherPoints) ) ) { - featherBarSel = _imp->isNearbyFeatherBar(time, pixelScale, pos); - if ( featherBarSel.first && !isFeatherVisible() ) { - featherBarSel.first.reset(); - featherBarSel.second.reset(); - } - } - - - if (nearbyBezier) { - ///check if the user clicked nearby the cross hair of the selection rectangle in which case - ///we drag all the control points selected - if (nearbyCP.first) { - _imp->handleControlPointSelection(nearbyCP, e); - _imp->handleBezierSelection(nearbyBezier, e); - if ( buttonDownIsRight(e) ) { - _imp->state = eEventStateNone; - showMenuForControlPoint(nearbyBezier, nearbyCP); - } - didSomething = true; - } else if (featherBarSel.first) { - _imp->clearCPSSelection(); - _imp->rotoData->featherBarBeingDragged = featherBarSel; - - ///Also select the point only if the curve is the same! - if (featherBarSel.first->getBezier() == nearbyBezier) { - _imp->handleControlPointSelection(_imp->rotoData->featherBarBeingDragged, e); - _imp->handleBezierSelection(nearbyBezier, e); - } - _imp->state = eEventStateDraggingFeatherBar; - didSomething = true; - } else { - bool found = false; - for (SelectedItems::iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - if ( it->get() == nearbyBezier.get() ) { - found = true; - break; - } - } - if (!found) { - _imp->handleBezierSelection(nearbyBezier, e); - } - if (e->button() == Qt::LeftButton) { - if ( modCASIsControlAlt(e) ) { - pushUndoCommand( new AddPointUndoCommand(this, nearbyBezier, nearbyBezierCPIndex, nearbyBezierT) ); - _imp->evaluateOnPenUp = true; - } else { - _imp->state = eEventStateDraggingSelectedControlPoints; - _imp->rotoData->bezierBeingDragged = nearbyBezier; - } - } else if ( buttonDownIsRight(e) ) { - showMenuForCurve(nearbyBezier); - } - didSomething = true; - } - } else { - if (featherBarSel.first) { - _imp->clearCPSSelection(); - _imp->rotoData->featherBarBeingDragged = featherBarSel; - _imp->handleControlPointSelection(_imp->rotoData->featherBarBeingDragged, e); - _imp->state = eEventStateDraggingFeatherBar; - didSomething = true; - } - if (_imp->state == eEventStateNone) { - _imp->state = _imp->isMouseInteractingWithCPSBbox(pos, cpSelectionTolerance, pixelScale); - if (_imp->state != eEventStateNone) { - didSomething = true; - } - } - } - break; - } - case eRotoToolSelectCurves: - - if (nearbyBezier) { - ///If the bezier is already selected and we re-click on it, change the transform mode - bool found = false; - for (SelectedItems::iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - if ( it->get() == nearbyBezier.get() ) { - found = true; - break; - } - } - if (!found) { - _imp->handleBezierSelection(nearbyBezier, e); - } - if ( buttonDownIsRight(e) ) { - showMenuForCurve(nearbyBezier); - } else { - if ( modCASIsControlAlt(e) ) { - pushUndoCommand( new AddPointUndoCommand(this, nearbyBezier, nearbyBezierCPIndex, nearbyBezierT) ); - _imp->evaluateOnPenUp = true; - } - } - didSomething = true; - } else { - if (_imp->state == eEventStateNone) { - _imp->state = _imp->isMouseInteractingWithCPSBbox(pos, cpSelectionTolerance, pixelScale); - if (_imp->state != eEventStateNone) { - didSomething = true; - } - } - } - break; - case eRotoToolAddPoints: - ///If the user clicked on a bezier and this bezier is selected add a control point by - ///splitting up the targeted segment - if (nearbyBezier) { - bool found = false; - for (SelectedItems::iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - if ( it->get() == nearbyBezier.get() ) { - found = true; - break; - } - } - if (found) { - ///check that the point is not too close to an existing point - if (nearbyCP.first) { - _imp->handleControlPointSelection(nearbyCP, e); - } else { - pushUndoCommand( new AddPointUndoCommand(this, nearbyBezier, nearbyBezierCPIndex, nearbyBezierT) ); - _imp->evaluateOnPenUp = true; - } - didSomething = true; - } - } - break; - case eRotoToolRemovePoints: - if (nearbyCP.first) { - assert( nearbyBezier && nearbyBezier == nearbyCP.first->getBezier() ); - if ( nearbyCP.first->isFeatherPoint() ) { - pushUndoCommand( new RemovePointUndoCommand(this, nearbyBezier, nearbyCP.second) ); - } else { - pushUndoCommand( new RemovePointUndoCommand(this, nearbyBezier, nearbyCP.first) ); - } - didSomething = true; - } - break; - case eRotoToolRemoveFeatherPoints: - if (nearbyCP.first) { - assert(nearbyBezier); - std::list datas; - RemoveFeatherUndoCommand::RemoveFeatherData data; - data.curve = nearbyBezier; - data.newPoints.push_back(nearbyCP.first->isFeatherPoint() ? nearbyCP.first : nearbyCP.second); - datas.push_back(data); - pushUndoCommand( new RemoveFeatherUndoCommand(this, datas) ); - didSomething = true; - } - break; - case eRotoToolOpenCloseCurve: - if (nearbyBezier) { - pushUndoCommand( new OpenCloseUndoCommand(this, nearbyBezier) ); - didSomething = true; - } - break; - case eRotoToolSmoothPoints: - - if (nearbyCP.first) { - std::list datas; - SmoothCuspUndoCommand::SmoothCuspCurveData data; - data.curve = nearbyBezier; - data.newPoints.push_back(nearbyCP); - datas.push_back(data); - pushUndoCommand( new SmoothCuspUndoCommand(this, datas, time, false, pixelScale) ); - didSomething = true; - } - break; - case eRotoToolCuspPoints: - if ( nearbyCP.first && _imp->context->isAutoKeyingEnabled() ) { - std::list datas; - SmoothCuspUndoCommand::SmoothCuspCurveData data; - data.curve = nearbyBezier; - data.newPoints.push_back(nearbyCP); - datas.push_back(data); - pushUndoCommand( new SmoothCuspUndoCommand(this, datas, time, true, pixelScale) ); - didSomething = true; - } - break; - case eRotoToolDrawBezier: - case eRotoToolOpenBezier: { - if ( _imp->rotoData->builtBezier && _imp->rotoData->builtBezier->isCurveFinished() ) { - _imp->rotoData->builtBezier.reset(); - _imp->clearSelection(); - onToolActionTriggered(_imp->selectAllAction); - - return true; - } - if (_imp->rotoData->builtBezier) { - ///if the user clicked on a control point of the bezier, select the point instead. - ///if that point is the starting point of the curve, close the curve - const std::list > & cps = _imp->rotoData->builtBezier->getControlPoints(); - int i = 0; - for (std::list >::const_iterator it = cps.begin(); it != cps.end(); ++it, ++i) { - double x, y; - (*it)->getPositionAtTime(true, time, ViewIdx(0), &x, &y); - if ( ( x >= (pos.x() - cpSelectionTolerance) ) && ( x <= (pos.x() + cpSelectionTolerance) ) && - ( y >= (pos.y() - cpSelectionTolerance) ) && ( y <= (pos.y() + cpSelectionTolerance) ) ) { - if ( it == cps.begin() ) { - pushUndoCommand( new OpenCloseUndoCommand(this, _imp->rotoData->builtBezier) ); - - _imp->rotoData->builtBezier.reset(); - - _imp->rotoData->selectedCps.clear(); - onToolActionTriggered(_imp->selectAllAction); - } else { - boost::shared_ptr fp = _imp->rotoData->builtBezier->getFeatherPointAtIndex(i); - assert(fp); - _imp->handleControlPointSelection(std::make_pair(*it, fp), e); - } - - return true; - } - } - } - - bool isOpenBezier = _imp->selectedTool == eRotoToolOpenBezier; - MakeBezierUndoCommand* cmd = new MakeBezierUndoCommand(this, _imp->rotoData->builtBezier, isOpenBezier, true, pos.x(), pos.y(), time); - pushUndoCommand(cmd); - _imp->rotoData->builtBezier = cmd->getCurve(); - assert(_imp->rotoData->builtBezier); - _imp->state = eEventStateBuildingBezierControlPointTangent; - didSomething = true; - break; - } - case eRotoToolDrawBSpline: - - break; - case eRotoToolDrawEllipse: { - _imp->rotoData->click = pos; - pushUndoCommand( new MakeEllipseUndoCommand(this, true, false, false, pos.x(), pos.y(), pos.x(), pos.y(), time) ); - _imp->state = eEventStateBuildingEllipse; - didSomething = true; - break; - } - case eRotoToolDrawRectangle: { - _imp->rotoData->click = pos; - pushUndoCommand( new MakeRectangleUndoCommand(this, true, false, false, pos.x(), pos.y(), pos.x(), pos.y(), time) ); - _imp->evaluateOnPenUp = true; - _imp->state = eEventStateBuildingRectangle; - didSomething = true; - break; - } - case eRotoToolSolidBrush: - case eRotoToolEraserBrush: - case eRotoToolClone: - case eRotoToolReveal: - case eRotoToolBlur: - case eRotoToolSharpen: - case eRotoToolSmear: - case eRotoToolDodge: - case eRotoToolBurn: { - if ( ( ( _imp->selectedTool == eRotoToolClone) || ( _imp->selectedTool == eRotoToolReveal) ) && modCASIsControl(e) ) { - _imp->state = eEventStateDraggingCloneOffset; - } else if ( modCASIsShift(e) ) { - _imp->state = eEventStateDraggingBrushSize; - _imp->mouseCenterOnSizeChange = pos; - } else if ( modCASIsControlShift(e) ) { - _imp->state = eEventStateDraggingBrushOpacity; - _imp->mouseCenterOnSizeChange = pos; - } else { - /* - Check that all viewers downstream are connected directly to the RotoPaint to avoid glitches and bugs - */ - _imp->checkViewersAreDirectlyConnected(); - - if ( _imp->rotoData->strokeBeingPaint && - _imp->rotoData->strokeBeingPaint->getParentLayer() && - _imp->multiStrokeEnabled->isChecked() ) { - boost::shared_ptr layer = _imp->rotoData->strokeBeingPaint->getParentLayer(); - if (!layer) { - layer = _imp->context->findDeepestSelectedLayer(); - if (!layer) { - layer = _imp->context->getOrCreateBaseLayer(); - } - assert(layer); - _imp->context->addItem(layer, 0, _imp->rotoData->strokeBeingPaint, RotoItem::eSelectionReasonOther); - } - - _imp->context->getNode()->getApp()->setUserIsPainting(_imp->context->getNode(), _imp->rotoData->strokeBeingPaint, true); - - boost::shared_ptr lifeTimeFrameKnob = _imp->rotoData->strokeBeingPaint->getLifeTimeFrameKnob(); - lifeTimeFrameKnob->setValue( _imp->context->getTimelineCurrentTime() ); - - _imp->rotoData->strokeBeingPaint->appendPoint( true, RotoPoint(pos.x(), pos.y(), pressure, timestamp) ); - } else { - if ( _imp->rotoData->strokeBeingPaint && - !_imp->rotoData->strokeBeingPaint->getParentLayer() && - _imp->multiStrokeEnabled->isChecked() ) { - _imp->rotoData->strokeBeingPaint.reset(); - } - _imp->makeStroke( false, RotoPoint(pos.x(), pos.y(), pressure, timestamp) ); - } - _imp->context->evaluateChange(); - _imp->state = eEventStateBuildingStroke; - _imp->viewer->setCursor(Qt::BlankCursor); - } - didSomething = true; - break; - } - default: - assert(false); - break; - } // switch - - _imp->lastClickPos = pos; - _imp->lastMousePos = pos; - - return didSomething; -} // penDown - -bool -RotoGui::penDoubleClicked(double /*time*/, - const RenderScale & /*renderScale*/, - ViewIdx /*view*/, - const QPointF & /*viewportPos*/, - const QPointF & pos, - QMouseEvent* e) -{ - bool didSomething = false; - std::pair pixelScale; - - _imp->viewer->getPixelScale(pixelScale.first, pixelScale.second); - - if (_imp->selectedTool == eRotoToolSelectAll) { - double bezierSelectionTolerance = kBezierSelectionTolerance * pixelScale.first; - double nearbyBezierT; - int nearbyBezierCPIndex; - bool isFeather; - boost::shared_ptr nearbyBezier = - _imp->context->isNearbyBezier(pos.x(), pos.y(), bezierSelectionTolerance, &nearbyBezierCPIndex, &nearbyBezierT, &isFeather); - - if (nearbyBezier) { - ///If the bezier is already selected and we re-click on it, change the transform mode - _imp->handleBezierSelection(nearbyBezier, e); - _imp->clearCPSSelection(); - const std::list > & cps = nearbyBezier->getControlPoints(); - const std::list > & fps = nearbyBezier->getFeatherPoints(); - assert( cps.size() == fps.size() ); - std::list >::const_iterator itCp = cps.begin(); - std::list >::const_iterator itFp = fps.begin(); - for (; itCp != cps.end(); ++itCp, ++itFp) { - _imp->rotoData->selectedCps.push_back( std::make_pair(*itCp, *itFp) ); - } - if (_imp->rotoData->selectedCps.size() > 1) { - _imp->computeSelectedCpsBBOX(); - } - didSomething = true; - } - } - - return didSomething; -} - -bool -RotoGui::penMotion(double time, - const RenderScale & renderScale, - ViewIdx view, - const QPointF & viewportPos, - const QPointF & pos, - double pressure, - double timestamp, - QInputEvent* e) -{ - NodePtr node = _imp->node->getNode(); - - node->getEffectInstance()->setCurrentViewportForOverlays_public(_imp->viewer); - if ( node->onOverlayPenMotionDefault(time, renderScale, view, viewportPos, pos, pressure) ) { - return true; - } - - std::pair pixelScale; - - _imp->viewer->getPixelScale(pixelScale.first, pixelScale.second); - - bool didSomething = false; - HoverStateEnum lastHoverState = _imp->hoverState; - ///Set the cursor to the appropriate case - bool cursorSet = false; - double cpTol = kControlPointSelectionTolerance * pixelScale.first; - - if ( _imp->context->isRotoPaint() && - ( ( _imp->selectedRole == _imp->mergeBrushTool) || - ( _imp->selectedRole == _imp->cloneBrushTool) || - ( _imp->selectedRole == _imp->effectBrushTool) || - ( _imp->selectedRole == _imp->paintBrushTool) ) ) { - if (_imp->state != eEventStateBuildingStroke) { - _imp->viewer->setCursor(Qt::CrossCursor); - } else { - _imp->viewer->setCursor(Qt::BlankCursor); - } - didSomething = true; - cursorSet = true; - } - - if ( !cursorSet && _imp->rotoData->showCpsBbox && (_imp->state != eEventStateDraggingControlPoint) && (_imp->state != eEventStateDraggingSelectedControlPoints) - && ( _imp->state != eEventStateDraggingLeftTangent) && - ( _imp->state != eEventStateDraggingRightTangent) ) { - double bboxTol = cpTol; - if ( _imp->isNearbyBBoxBtmLeft(pos, bboxTol, pixelScale) ) { - _imp->hoverState = eHoverStateBboxBtmLeft; - didSomething = true; - } else if ( _imp->isNearbyBBoxBtmRight(pos, bboxTol, pixelScale) ) { - _imp->hoverState = eHoverStateBboxBtmRight; - didSomething = true; - } else if ( _imp->isNearbyBBoxTopRight(pos, bboxTol, pixelScale) ) { - _imp->hoverState = eHoverStateBboxTopRight; - didSomething = true; - } else if ( _imp->isNearbyBBoxTopLeft(pos, bboxTol, pixelScale) ) { - _imp->hoverState = eHoverStateBboxTopLeft; - didSomething = true; - } else if ( _imp->isNearbyBBoxMidTop(pos, bboxTol, pixelScale) ) { - _imp->hoverState = eHoverStateBboxMidTop; - didSomething = true; - } else if ( _imp->isNearbyBBoxMidRight(pos, bboxTol, pixelScale) ) { - _imp->hoverState = eHoverStateBboxMidRight; - didSomething = true; - } else if ( _imp->isNearbyBBoxMidBtm(pos, bboxTol, pixelScale) ) { - _imp->hoverState = eHoverStateBboxMidBtm; - didSomething = true; - } else if ( _imp->isNearbyBBoxMidLeft(pos, bboxTol, pixelScale) ) { - _imp->hoverState = eHoverStateBboxMidLeft; - didSomething = true; - } else { - _imp->hoverState = eHoverStateNothing; - didSomething = true; - } - } - if ( (_imp->state == eEventStateNone) && (_imp->hoverState == eHoverStateNothing) ) { - if ( (_imp->state != eEventStateDraggingControlPoint) && (_imp->state != eEventStateDraggingSelectedControlPoints) ) { - for (SelectedItems::const_iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - int index = -1; - Bezier* isBezier = dynamic_cast( it->get() ); - if (isBezier) { - std::pair, boost::shared_ptr > nb = - isBezier->isNearbyControlPoint(pos.x(), pos.y(), cpTol, Bezier::eControlPointSelectionPrefWhateverFirst, &index); - if ( (index != -1) && ( ( !nb.first->isFeatherPoint() && !isFeatherVisible() ) || isFeatherVisible() ) ) { - _imp->viewer->setCursor( QCursor(Qt::CrossCursor) ); - cursorSet = true; - break; - } - } - } - } - if ( !cursorSet && (_imp->state != eEventStateDraggingLeftTangent) && (_imp->state != eEventStateDraggingRightTangent) ) { - ///find a nearby tangent - for (SelectedCPs::const_iterator it = _imp->rotoData->selectedCps.begin(); it != _imp->rotoData->selectedCps.end(); ++it) { - if (it->first->isNearbyTangent(true, time, ViewIdx(0), pos.x(), pos.y(), cpTol) != -1) { - _imp->viewer->setCursor( QCursor(Qt::CrossCursor) ); - cursorSet = true; - break; - } - } - } - if ( !cursorSet && (_imp->state != eEventStateDraggingControlPoint) && (_imp->state != eEventStateDraggingSelectedControlPoints) && (_imp->state != eEventStateDraggingLeftTangent) && - ( _imp->state != eEventStateDraggingRightTangent) ) { - double bezierSelectionTolerance = kBezierSelectionTolerance * pixelScale.first; - double nearbyBezierT; - int nearbyBezierCPIndex; - bool isFeather; - boost::shared_ptr nearbyBezier = - _imp->context->isNearbyBezier(pos.x(), pos.y(), bezierSelectionTolerance, &nearbyBezierCPIndex, &nearbyBezierT, &isFeather); - if ( isFeather && !isFeatherVisible() ) { - nearbyBezier.reset(); - } - if (nearbyBezier) { - _imp->viewer->setCursor( QCursor(Qt::PointingHandCursor) ); - cursorSet = true; - } - } - - bool clickAnywhere = _imp->isBboxClickAnywhereEnabled(); - - if ( !cursorSet && (_imp->rotoData->selectedCps.size() > 1) ) { - if ( ( clickAnywhere && _imp->isWithinSelectedCpsBBox(pos) ) || - ( !clickAnywhere && _imp->isNearbySelectedCpsCrossHair(pos) ) ) { - _imp->viewer->setCursor( QCursor(Qt::SizeAllCursor) ); - cursorSet = true; - } - } - - SelectedCP nearbyFeatherBar; - if ( !cursorSet && isFeatherVisible() ) { - nearbyFeatherBar = _imp->isNearbyFeatherBar(time, pixelScale, pos); - if (nearbyFeatherBar.first && nearbyFeatherBar.second) { - _imp->rotoData->featherBarBeingHovered = nearbyFeatherBar; - } - } - if (!nearbyFeatherBar.first || !nearbyFeatherBar.second) { - _imp->rotoData->featherBarBeingHovered.first.reset(); - _imp->rotoData->featherBarBeingHovered.second.reset(); - } - - if ( (_imp->state != eEventStateNone) || _imp->rotoData->featherBarBeingHovered.first || cursorSet || (lastHoverState != eHoverStateNothing) ) { - didSomething = true; - } - } - - - if (!cursorSet) { - _imp->viewer->unsetCursor(); - } - - - double dx = pos.x() - _imp->lastMousePos.x(); - double dy = pos.y() - _imp->lastMousePos.y(); - switch (_imp->state) { - case eEventStateDraggingSelectedControlPoints: { - if (_imp->rotoData->bezierBeingDragged) { - SelectedCPs cps; - const std::list >& c = _imp->rotoData->bezierBeingDragged->getControlPoints(); - const std::list >& f = _imp->rotoData->bezierBeingDragged->getFeatherPoints(); - assert( c.size() == f.size() || !_imp->rotoData->bezierBeingDragged->useFeatherPoints() ); - bool useFeather = _imp->rotoData->bezierBeingDragged->useFeatherPoints(); - std::list >::const_iterator itFp = f.begin(); - for (std::list >::const_iterator itCp = c.begin(); itCp != c.end(); ++itCp) { - if (useFeather) { - cps.push_back( std::make_pair(*itCp, *itFp) ); - ++itFp; - } else { - cps.push_back( std::make_pair( *itCp, boost::shared_ptr() ) ); - } - } - pushUndoCommand( new MoveControlPointsUndoCommand(this, cps, dx, dy, time) ); - } else { - pushUndoCommand( new MoveControlPointsUndoCommand(this, _imp->rotoData->selectedCps, dx, dy, time) ); - } - _imp->evaluateOnPenUp = true; - _imp->computeSelectedCpsBBOX(); - didSomething = true; - break; - } - case eEventStateDraggingControlPoint: { - assert(_imp->rotoData->cpBeingDragged.first); - std::list toDrag; - toDrag.push_back(_imp->rotoData->cpBeingDragged); - pushUndoCommand( new MoveControlPointsUndoCommand(this, toDrag, dx, dy, time) ); - _imp->evaluateOnPenUp = true; - _imp->computeSelectedCpsBBOX(); - didSomething = true; - }; break; - case eEventStateBuildingBezierControlPointTangent: { - assert(_imp->rotoData->builtBezier); - bool isOpenBezier = _imp->selectedTool == eRotoToolOpenBezier; - pushUndoCommand( new MakeBezierUndoCommand(this, _imp->rotoData->builtBezier, isOpenBezier, false, dx, dy, time) ); - break; - } - case eEventStateBuildingEllipse: { - bool fromCenter = modifierHasControl(e); - bool constrained = modifierHasShift(e); - pushUndoCommand( new MakeEllipseUndoCommand(this, false, fromCenter, constrained, _imp->rotoData->click.x(), _imp->rotoData->click.y(), pos.x(), pos.y(), time) ); - - didSomething = true; - _imp->evaluateOnPenUp = true; - break; - } - case eEventStateBuildingRectangle: { - bool fromCenter = modifierHasControl(e); - bool constrained = modifierHasShift(e); - pushUndoCommand( new MakeRectangleUndoCommand(this, false, fromCenter, constrained, _imp->rotoData->click.x(), _imp->rotoData->click.y(), pos.x(), pos.y(), time) ); - didSomething = true; - _imp->evaluateOnPenUp = true; - break; - } - case eEventStateDraggingLeftTangent: { - assert(_imp->rotoData->tangentBeingDragged); - pushUndoCommand( new MoveTangentUndoCommand( this, dx, dy, time, _imp->rotoData->tangentBeingDragged, true, - modCASIsControl(e) ) ); - _imp->evaluateOnPenUp = true; - didSomething = true; - break; - } - case eEventStateDraggingRightTangent: { - assert(_imp->rotoData->tangentBeingDragged); - pushUndoCommand( new MoveTangentUndoCommand( this, dx, dy, time, _imp->rotoData->tangentBeingDragged, false, - modCASIsControl(e) ) ); - _imp->evaluateOnPenUp = true; - didSomething = true; - break; - } - case eEventStateDraggingFeatherBar: { - pushUndoCommand( new MoveFeatherBarUndoCommand(this, dx, dy, _imp->rotoData->featherBarBeingDragged, time) ); - _imp->evaluateOnPenUp = true; - didSomething = true; - break; - } - case eEventStateDraggingBBoxTopLeft: - case eEventStateDraggingBBoxTopRight: - case eEventStateDraggingBBoxBtmRight: - case eEventStateDraggingBBoxBtmLeft: { - QPointF center = _imp->getSelectedCpsBBOXCenter(); - double rot = 0; - double sx = 1., sy = 1.; - - if (_imp->rotoData->transformMode == eSelectedCpsTransformModeRotateAndSkew) { - double angle = std::atan2( pos.y() - center.y(), pos.x() - center.x() ); - double prevAngle = std::atan2( _imp->lastMousePos.y() - center.y(), _imp->lastMousePos.x() - center.x() ); - rot = angle - prevAngle; - } else { - // the scale ratio is the ratio of distances to the center - double prevDist = ( _imp->lastMousePos.x() - center.x() ) * ( _imp->lastMousePos.x() - center.x() ) + - ( _imp->lastMousePos.y() - center.y() ) * ( _imp->lastMousePos.y() - center.y() ); - if (prevDist != 0) { - double dist = ( pos.x() - center.x() ) * ( pos.x() - center.x() ) + ( pos.y() - center.y() ) * ( pos.y() - center.y() ); - double ratio = std::sqrt(dist / prevDist); - sx *= ratio; - sy *= ratio; - } - } - - double tx = 0., ty = 0.; - double skewX = 0., skewY = 0.; - pushUndoCommand( new TransformUndoCommand(this, center.x(), center.y(), rot, skewX, skewY, tx, ty, sx, sy, time) ); - _imp->evaluateOnPenUp = true; - didSomething = true; - break; - } - case eEventStateDraggingBBoxMidTop: - case eEventStateDraggingBBoxMidBtm: - case eEventStateDraggingBBoxMidLeft: - case eEventStateDraggingBBoxMidRight: { - QPointF center = _imp->getSelectedCpsBBOXCenter(); - double rot = 0; - double sx = 1., sy = 1.; - double skewX = 0., skewY = 0.; - double tx = 0., ty = 0.; - TransformUndoCommand::TransformPointsSelectionEnum type = TransformUndoCommand::eTransformAllPoints; - if ( !modCASIsShift(e) ) { - type = TransformUndoCommand::eTransformAllPoints; - } else { - if (_imp->state == eEventStateDraggingBBoxMidTop) { - type = TransformUndoCommand::eTransformMidTop; - } else if (_imp->state == eEventStateDraggingBBoxMidBtm) { - type = TransformUndoCommand::eTransformMidBottom; - } else if (_imp->state == eEventStateDraggingBBoxMidLeft) { - type = TransformUndoCommand::eTransformMidLeft; - } else if (_imp->state == eEventStateDraggingBBoxMidRight) { - type = TransformUndoCommand::eTransformMidRight; - } - } - - const QRectF& bbox = _imp->rotoData->selectedCpsBbox; - - switch (type) { - case TransformUndoCommand::eTransformMidBottom: - center.rx() = bbox.center().x(); - center.ry() = bbox.top(); - break; - case TransformUndoCommand::eTransformMidTop: - center.rx() = bbox.center().x(); - center.ry() = bbox.bottom(); - break; - case TransformUndoCommand::eTransformMidRight: - center.rx() = bbox.left(); - center.ry() = bbox.center().y(); - break; - case TransformUndoCommand::eTransformMidLeft: - center.rx() = bbox.right(); - center.ry() = bbox.center().y(); - break; - default: - break; - } - - bool processX = _imp->state == eEventStateDraggingBBoxMidRight || _imp->state == eEventStateDraggingBBoxMidLeft; - - if (_imp->rotoData->transformMode == eSelectedCpsTransformModeRotateAndSkew) { - if (!processX) { - const double addSkew = ( pos.x() - _imp->lastMousePos.x() ) / ( pos.y() - center.y() ); - skewX += addSkew; - } else { - const double addSkew = ( pos.y() - _imp->lastMousePos.y() ) / ( pos.x() - center.x() ); - skewY += addSkew; - } - } else { - // the scale ratio is the ratio of distances to the center - double prevDist = ( _imp->lastMousePos.x() - center.x() ) * ( _imp->lastMousePos.x() - center.x() ) + - ( _imp->lastMousePos.y() - center.y() ) * ( _imp->lastMousePos.y() - center.y() ); - if (prevDist != 0) { - double dist = ( pos.x() - center.x() ) * ( pos.x() - center.x() ) + ( pos.y() - center.y() ) * ( pos.y() - center.y() ); - double ratio = std::sqrt(dist / prevDist); - if (processX) { - sx *= ratio; - } else { - sy *= ratio; - } - } - } - - - pushUndoCommand( new TransformUndoCommand(this, center.x(), center.y(), rot, skewX, skewY, tx, ty, sx, sy, time) ); - _imp->evaluateOnPenUp = true; - didSomething = true; - break; - } - case eEventStateBuildingStroke: { - if (_imp->rotoData->strokeBeingPaint) { - RotoPoint p(pos.x(), pos.y(), pressure, timestamp); - if ( _imp->rotoData->strokeBeingPaint->appendPoint(false, p) ) { - _imp->lastMousePos = pos; - _imp->context->evaluateChange_noIncrement(); - - return true; - } - } - break; - } - case eEventStateDraggingCloneOffset: { - _imp->rotoData->cloneOffset.first -= dx; - _imp->rotoData->cloneOffset.second -= dy; - onBreakMultiStrokeTriggered(); - break; - } - case eEventStateDraggingBrushSize: { - double size = _imp->sizeSpinbox->value(); - size += ( (dx + dy) / 2. ); - _imp->sizeSpinbox->setValue( std::max(1., size) ); - onBreakMultiStrokeTriggered(); - didSomething = true; - break; - } - case eEventStateDraggingBrushOpacity: { - double opa = _imp->opacitySpinbox->value(); - double newOpa = opa + ( (dx + dy) / 2. ); - if (opa != 0) { - newOpa = std::max( 0., std::min(1., newOpa / opa) ); - newOpa = newOpa > 0 ? std::min(1., opa + 0.05) : std::max(0., opa - 0.05); - } else { - newOpa = newOpa < 0 ? .0 : 0.05; - } - _imp->opacitySpinbox->setValue(newOpa); - onBreakMultiStrokeTriggered(); - didSomething = true; - break; - } - case eEventStateNone: - default: - break; - } // switch - _imp->lastMousePos = pos; - - return didSomething; -} // penMotion - -void -RotoGui::moveSelectedCpsWithKeyArrows(int x, - int y) -{ - if ( !_imp->rotoData->selectedCps.empty() ) { - std::pair pixelScale; - _imp->viewer->getPixelScale(pixelScale.first, pixelScale.second); - double time = _imp->context->getTimelineCurrentTime(); - pushUndoCommand( new MoveControlPointsUndoCommand(this, _imp->rotoData->selectedCps, (double)x * pixelScale.first, - (double)y * pixelScale.second, time) ); - _imp->computeSelectedCpsBBOX(); - _imp->context->evaluateChange(); - _imp->node->getNode()->getApp()->triggerAutoSave(); - _imp->viewerTab->onRotoEvaluatedForThisViewer(); - } -} - -void -RotoGui::evaluate(bool redraw) -{ - if (redraw) { - _imp->viewer->redraw(); - } - _imp->context->evaluateChange(); - _imp->node->getNode()->getApp()->triggerAutoSave(); - _imp->viewerTab->onRotoEvaluatedForThisViewer(); -} - -void -RotoGui::autoSaveAndRedraw() -{ - _imp->viewer->redraw(); - _imp->node->getNode()->getApp()->triggerAutoSave(); -} - -bool -RotoGui::penUp(double time, - const RenderScale & renderScale, - ViewIdx view, - const QPointF & viewportPos, - const QPointF & pos, - double pressure, - double /*timestamp*/, - QMouseEvent* /*e*/) -{ - NodePtr node = _imp->node->getNode(); - - node->getEffectInstance()->setCurrentViewportForOverlays_public(_imp->viewer); - if ( node->onOverlayPenUpDefault(time, renderScale, view, viewportPos, pos, pressure) ) { - return true; - } - - if (_imp->evaluateOnPenUp) { - _imp->context->evaluateChange(); - node->getApp()->triggerAutoSave(); - - //sync other viewers linked to this roto - _imp->viewerTab->onRotoEvaluatedForThisViewer(); - _imp->evaluateOnPenUp = false; - } - _imp->rotoData->tangentBeingDragged.reset(); - _imp->rotoData->bezierBeingDragged.reset(); - _imp->rotoData->cpBeingDragged.first.reset(); - _imp->rotoData->cpBeingDragged.second.reset(); - _imp->rotoData->featherBarBeingDragged.first.reset(); - _imp->rotoData->featherBarBeingDragged.second.reset(); - - if ( (_imp->state == eEventStateDraggingBBoxMidLeft) || - ( _imp->state == eEventStateDraggingBBoxMidLeft) || - ( _imp->state == eEventStateDraggingBBoxMidTop) || - ( _imp->state == eEventStateDraggingBBoxMidBtm) || - ( _imp->state == eEventStateDraggingBBoxTopLeft) || - ( _imp->state == eEventStateDraggingBBoxTopRight) || - ( _imp->state == eEventStateDraggingBBoxBtmRight) || - ( _imp->state == eEventStateDraggingBBoxBtmLeft) ) { - refreshSelectionBBox(); - } - - if (_imp->state == eEventStateBuildingStroke) { - assert(_imp->rotoData->strokeBeingPaint); - _imp->context->getNode()->getApp()->setUserIsPainting(_imp->context->getNode(), _imp->rotoData->strokeBeingPaint, false); - assert( _imp->rotoData->strokeBeingPaint->getParentLayer() ); - if ( !_imp->multiStrokeEnabled->isChecked() ) { - pushUndoCommand( new AddStrokeUndoCommand(this, _imp->rotoData->strokeBeingPaint) ); - _imp->makeStroke( true, RotoPoint() ); - } else { - pushUndoCommand( new AddMultiStrokeUndoCommand(this, _imp->rotoData->strokeBeingPaint) ); - } - - /** - * Do a neat render for the stroke (better interpolation). This call is blocking otherwise the user might - * attempt to make a new stroke while the previous stroke is not finished... this would yield artifacts. - **/ - _imp->viewer->setCursor( QCursor(Qt::BusyCursor) ); - _imp->context->evaluateNeatStrokeRender(); - _imp->viewer->unsetCursor(); - _imp->rotoData->strokeBeingPaint->setStrokeFinished(); - - } - - _imp->state = eEventStateNone; - - if ( (_imp->selectedTool == eRotoToolDrawEllipse) || (_imp->selectedTool == eRotoToolDrawRectangle) ) { - _imp->rotoData->selectedCps.clear(); - onToolActionTriggered(_imp->selectAllAction); - } - - if (_imp->lastTabletDownTriggeredEraser) { - onToolActionTriggered(_imp->lastPaintToolAction); - } - - return true; -} // RotoGui::penUp - -void -RotoGui::onTimelineTimeChanged() -{ - if ( (_imp->selectedTool == eRotoToolBlur) || - ( _imp->selectedTool == eRotoToolBurn) || - ( _imp->selectedTool == eRotoToolDodge) || - ( _imp->selectedTool == eRotoToolClone) || - ( _imp->selectedTool == eRotoToolEraserBrush) || - ( _imp->selectedTool == eRotoToolSolidBrush) || - ( _imp->selectedTool == eRotoToolReveal) || - ( _imp->selectedTool == eRotoToolSmear) || - ( _imp->selectedTool == eRotoToolSharpen) ) { - _imp->makeStroke( true, RotoPoint() ); - } -} - -void -RotoGui::onBreakMultiStrokeTriggered() -{ - _imp->makeStroke( true, RotoPoint() ); -} - -static bool -isBranchConnectedToRotoNodeRecursive(Node* node, - const Node* rotoNode, - int* recursion, - std::list& markedNodes) -{ - assert(recursion); - if (!node) { - return false; - } - if (rotoNode == node) { - return true; - } - markedNodes.push_back(node); - int maxInputs = node->getMaxInputCount(); - *recursion = *recursion + 1; - for (int i = 0; i < maxInputs; ++i) { - NodePtr inp = node->getInput(i); - if (inp) { - if ( isBranchConnectedToRotoNodeRecursive(inp.get(), rotoNode, recursion, markedNodes) ) { - return true; - } - } - } - - return false; -} - -void -RotoGui::RotoGuiPrivate::checkViewersAreDirectlyConnected() -{ - NodePtr rotoNode = context->getNode(); - std::list viewers; - - rotoNode->hasViewersConnected(&viewers); - for (std::list::iterator it = viewers.begin(); it != viewers.end(); ++it) { - NodePtr viewerNode = (*it)->getNode(); - int maxInputs = viewerNode->getMaxInputCount(); - int hasBranchConnectedToRoto = -1; - for (int i = 0; i < maxInputs; ++i) { - NodePtr input = viewerNode->getInput(i); - if (input) { - std::list markedNodes; - int recursion = 0; - if ( isBranchConnectedToRotoNodeRecursive(input.get(), rotoNode.get(), &recursion, markedNodes) ) { - if (recursion == 0) { - //This viewer is already connected to the Roto node directly. - break; - } - viewerNode->disconnectInput(i); - if (hasBranchConnectedToRoto == -1) { - viewerNode->connectInput(rotoNode, i); - hasBranchConnectedToRoto = i; - } - } - } - } - } -} - -void -RotoGui::RotoGuiPrivate::makeStroke(bool prepareForLater, - const RotoPoint& p) -{ - RotoStrokeType strokeType; - std::string itemName; - - switch (selectedTool) { - case eRotoToolSolidBrush: - strokeType = eRotoStrokeTypeSolid; - itemName = kRotoPaintBrushBaseName; - break; - case eRotoToolEraserBrush: - strokeType = eRotoStrokeTypeEraser; - itemName = kRotoPaintEraserBaseName; - break; - case eRotoToolClone: - strokeType = eRotoStrokeTypeClone; - itemName = kRotoPaintCloneBaseName; - break; - case eRotoToolReveal: - strokeType = eRotoStrokeTypeReveal; - itemName = kRotoPaintRevealBaseName; - break; - case eRotoToolBlur: - strokeType = eRotoStrokeTypeBlur; - itemName = kRotoPaintBlurBaseName; - break; - case eRotoToolSharpen: - strokeType = eRotoStrokeTypeSharpen; - itemName = kRotoPaintSharpenBaseName; - break; - case eRotoToolSmear: - strokeType = eRotoStrokeTypeSmear; - itemName = kRotoPaintSmearBaseName; - break; - case eRotoToolDodge: - strokeType = eRotoStrokeTypeDodge; - itemName = kRotoPaintDodgeBaseName; - break; - case eRotoToolBurn: - strokeType = eRotoStrokeTypeBurn; - itemName = kRotoPaintBurnBaseName; - break; - default: - - return; - } - - if (prepareForLater || !rotoData->strokeBeingPaint) { - if ( rotoData->strokeBeingPaint && - ( rotoData->strokeBeingPaint->getBrushType() == strokeType) && - rotoData->strokeBeingPaint->isEmpty() ) { - ///We already have a fresh stroke prepared for that type - return; - } - - std::string name = context->generateUniqueName(itemName); - rotoData->strokeBeingPaint.reset( new RotoStrokeItem( strokeType, context, name, boost::shared_ptr() ) ); - rotoData->strokeBeingPaint->createNodes(false); - } - - - assert(rotoData->strokeBeingPaint); - boost::shared_ptr colorKnob = rotoData->strokeBeingPaint->getColorKnob(); - boost::shared_ptr operatorKnob = rotoData->strokeBeingPaint->getOperatorKnob(); - boost::shared_ptr opacityKnob = rotoData->strokeBeingPaint->getOpacityKnob(); - boost::shared_ptr sizeKnob = rotoData->strokeBeingPaint->getBrushSizeKnob(); - boost::shared_ptr hardnessKnob = rotoData->strokeBeingPaint->getBrushHardnessKnob(); - boost::shared_ptr pressureOpaKnob = rotoData->strokeBeingPaint->getPressureOpacityKnob(); - boost::shared_ptr pressureSizeKnob = rotoData->strokeBeingPaint->getPressureSizeKnob(); - boost::shared_ptr pressureHardnessKnob = rotoData->strokeBeingPaint->getPressureHardnessKnob(); - boost::shared_ptr buildUpKnob = rotoData->strokeBeingPaint->getBuildupKnob(); - boost::shared_ptr timeOffsetModeKnob = rotoData->strokeBeingPaint->getTimeOffsetModeKnob(); - boost::shared_ptr sourceTypeKnob = rotoData->strokeBeingPaint->getBrushSourceTypeKnob(); - boost::shared_ptr timeOffsetKnob = rotoData->strokeBeingPaint->getTimeOffsetKnob(); - boost::shared_ptr translateKnob = rotoData->strokeBeingPaint->getBrushCloneTranslateKnob(); - boost::shared_ptr effectKnob = rotoData->strokeBeingPaint->getBrushEffectKnob(); - const QColor& color = colorPickerLabel->getCurrentColor(); - MergingFunctionEnum compOp = (MergingFunctionEnum)compositingOperatorButton->activeIndex(); - double opacity = opacitySpinbox->value(); - double size = sizeSpinbox->value(); - double hardness = hardnessSpinBox->value(); - bool pressOpa = pressureOpacityButton->isDown(); - bool pressSize = pressureSizeButton->isDown(); - bool pressHarness = pressureHardnessButton->isDown(); - bool buildUp = buildUpButton->isDown(); - double timeOffset = timeOffsetSpinbox->value(); - double timeOffsetMode_i = timeOffsetMode->activeIndex(); - int sourceType_i = sourceTypeCombobox->activeIndex(); - double effectValue = effectSpinBox->value(); - double r = Color::from_func_srgb( color.redF() ); - double g = Color::from_func_srgb( color.greenF() ); - double b = Color::from_func_srgb( color.blueF() ); - - colorKnob->setValues(r, g, b, ViewSpec::all(), eValueChangedReasonNatronGuiEdited); - operatorKnob->setValueFromLabel(Merge::getOperatorString(compOp), 0); - opacityKnob->setValue(opacity); - sizeKnob->setValue(size); - hardnessKnob->setValue(hardness); - pressureOpaKnob->setValue(pressOpa); - pressureSizeKnob->setValue(pressSize); - pressureHardnessKnob->setValue(pressHarness); - buildUpKnob->setValue(buildUp); - effectKnob->setValue(effectValue); - if (!prepareForLater) { - boost::shared_ptr lifeTimeFrameKnob = rotoData->strokeBeingPaint->getLifeTimeFrameKnob(); - lifeTimeFrameKnob->setValue( context->getTimelineCurrentTime() ); - } - if ( (strokeType == eRotoStrokeTypeClone) || (strokeType == eRotoStrokeTypeReveal) ) { - timeOffsetKnob->setValue(timeOffset); - timeOffsetModeKnob->setValue(timeOffsetMode_i); - sourceTypeKnob->setValue(sourceType_i); - translateKnob->setValues(-rotoData->cloneOffset.first, -rotoData->cloneOffset.second, ViewSpec::all(), eValueChangedReasonNatronGuiEdited); - } - if (!prepareForLater) { - boost::shared_ptr layer = context->findDeepestSelectedLayer(); - if (!layer) { - layer = context->getOrCreateBaseLayer(); - } - assert(layer); - context->addItem(layer, 0, rotoData->strokeBeingPaint, RotoItem::eSelectionReasonOther); - context->getNode()->getApp()->setUserIsPainting(context->getNode(), rotoData->strokeBeingPaint, true); - rotoData->strokeBeingPaint->appendPoint(true, p); - } - - //context->clearSelection(RotoItem::eSelectionReasonOther); - //context->select(rotoData->strokeBeingPaint, RotoItem::eSelectionReasonOther); -} // RotoGui::RotoGuiPrivate::makeStroke - -void -RotoGui::removeCurve(const boost::shared_ptr& curve) -{ - if (curve == _imp->rotoData->builtBezier) { - _imp->rotoData->builtBezier.reset(); - } else if (curve == _imp->rotoData->strokeBeingPaint) { - _imp->rotoData->strokeBeingPaint.reset(); - } - _imp->context->removeItem(curve); -} - -bool -RotoGui::keyDown(double time, - const RenderScale & renderScale, - ViewIdx view, - QKeyEvent* e) -{ - Key natronKey = QtEnumConvert::fromQtKey( (Qt::Key)e->key() ); - KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers( e->modifiers() ); - NodePtr node = _imp->node->getNode(); - - node->getEffectInstance()->setCurrentViewportForOverlays_public(_imp->viewer); - if ( node->onOverlayKeyDownDefault(time, renderScale, view, natronKey, natronMod) ) { - return true; - } - - bool didSomething = false; - Qt::KeyboardModifiers modifiers = e->modifiers(); - Qt::Key key = (Qt::Key)e->key(); - - if (key == Qt::Key_Shift) { - ++_imp->shiftDown; - } else if (key == Qt::Key_Control) { - ++_imp->ctrlDown; - } - - if ( modCASIsControl(e) ) { - if ( !_imp->iSelectingwithCtrlA && _imp->rotoData->showCpsBbox && (e->key() == Qt::Key_Control) ) { - _imp->rotoData->transformMode = _imp->rotoData->transformMode == eSelectedCpsTransformModeTranslateAndScale ? - eSelectedCpsTransformModeRotateAndSkew : eSelectedCpsTransformModeTranslateAndScale; - didSomething = true; - } - } - - if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoDelete, modifiers, key) ) { - ///if control points are selected, delete them, otherwise delete the selected beziers - if ( !_imp->rotoData->selectedCps.empty() ) { - pushUndoCommand( new RemovePointUndoCommand(this, _imp->rotoData->selectedCps) ); - didSomething = true; - } else if ( !_imp->rotoData->selectedItems.empty() ) { - pushUndoCommand( new RemoveCurveUndoCommand(this, _imp->rotoData->selectedItems) ); - didSomething = true; - } - } else if ( ( (key == Qt::Key_Escape) && ( (_imp->selectedTool == eRotoToolDrawBezier) || (_imp->selectedTool == eRotoToolOpenBezier) ) ) || isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoCloseBezier, modifiers, key) ) { - if ( ( (_imp->selectedTool == eRotoToolDrawBezier) || (_imp->selectedTool == eRotoToolOpenBezier) ) && _imp->rotoData->builtBezier && !_imp->rotoData->builtBezier->isCurveFinished() ) { - pushUndoCommand( new OpenCloseUndoCommand(this, _imp->rotoData->builtBezier) ); - - _imp->rotoData->builtBezier.reset(); - _imp->rotoData->selectedCps.clear(); - onToolActionTriggered(_imp->selectAllAction); - _imp->context->evaluateChange(); - didSomething = true; - } - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoSelectAll, modifiers, key) ) { - _imp->iSelectingwithCtrlA = true; - ///if no bezier are selected, select all beziers - if ( _imp->rotoData->selectedItems.empty() ) { - std::list > bez = _imp->context->getCurvesByRenderOrder(); - for (std::list >::const_iterator it = bez.begin(); it != bez.end(); ++it) { - _imp->context->select(*it, RotoItem::eSelectionReasonOverlayInteract); - _imp->rotoData->selectedItems.push_back(*it); - } - } else { - ///select all the control points of all selected beziers - _imp->rotoData->selectedCps.clear(); - for (SelectedItems::iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - Bezier* isBezier = dynamic_cast( it->get() ); - if (!isBezier) { - continue; - } - const std::list > & cps = isBezier->getControlPoints(); - const std::list > & fps = isBezier->getFeatherPoints(); - assert( cps.size() == fps.size() ); - - std::list >::const_iterator cpIT = cps.begin(); - for (std::list >::const_iterator fpIT = fps.begin(); fpIT != fps.end(); ++fpIT, ++cpIT) { - _imp->rotoData->selectedCps.push_back( std::make_pair(*cpIT, *fpIT) ); - } - } - _imp->computeSelectedCpsBBOX(); - } - didSomething = true; - } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoSelectionTool, modifiers, key) ) { - _imp->selectTool->handleSelection(); - didSomething = true; - } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoEditTool, modifiers, key) ) { - if (_imp->bezierEditionTool) { - _imp->bezierEditionTool->handleSelection(); - didSomething = true; - } - } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoAddTool, modifiers, key) ) { - if (_imp->pointsEditionTool) { - _imp->pointsEditionTool->handleSelection(); - didSomething = true; - } - } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoBrushTool, modifiers, key) ) { - if (_imp->paintBrushTool) { - _imp->paintBrushTool->handleSelection(); - didSomething = true; - } - } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoCloneTool, modifiers, key) ) { - if (_imp->cloneBrushTool) { - _imp->cloneBrushTool->handleSelection(); - didSomething = true; - } - } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoEffectTool, modifiers, key) ) { - if (_imp->effectBrushTool) { - _imp->effectBrushTool->handleSelection(); - didSomething = true; - } - } else if ( (_imp->state != eEventStateBuildingStroke) && isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoColorTool, modifiers, key) ) { - if (_imp->mergeBrushTool) { - _imp->mergeBrushTool->handleSelection(); - didSomething = true; - } - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoNudgeRight, modifiers, key) ) { - moveSelectedCpsWithKeyArrows(1, 0); - didSomething = true; - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoNudgeLeft, modifiers, key) ) { - moveSelectedCpsWithKeyArrows(-1, 0); - didSomething = true; - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoNudgeTop, modifiers, key) ) { - moveSelectedCpsWithKeyArrows(0, 1); - didSomething = true; - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoNudgeBottom, modifiers, key) ) { - moveSelectedCpsWithKeyArrows(0, -1); - didSomething = true; - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoSmooth, modifiers, key) ) { - smoothSelectedCurve(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoCuspBezier, modifiers, key) ) { - cuspSelectedCurve(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoRemoveFeather, modifiers, key) ) { - removeFeatherForSelectedCurve(); - didSomething = true; - } else if (key == Qt::Key_Escape) { - _imp->clearSelection(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupRoto, kShortcutIDActionRotoLockCurve, modifiers, key) ) { - lockSelectedCurves(); - didSomething = true; - } - - return didSomething; -} // keyDown - -bool -RotoGui::keyRepeat(double time, - const RenderScale & renderScale, - ViewIdx view, - QKeyEvent* e) -{ - NodePtr node = _imp->node->getNode(); - - node->getEffectInstance()->setCurrentViewportForOverlays_public(_imp->viewer); - Key natronKey = QtEnumConvert::fromQtKey( (Qt::Key)e->key() ); - KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers( e->modifiers() ); - if ( node->onOverlayKeyRepeatDefault(time, renderScale, view, natronKey, natronMod) ) { - return true; - } - - bool didSomething = false; - - if ( (e->key() == Qt::Key_Right) && modCASIsAlt(e) ) { - moveSelectedCpsWithKeyArrows(1, 0); - didSomething = true; - } else if ( (e->key() == Qt::Key_Left) && modCASIsAlt(e) ) { - moveSelectedCpsWithKeyArrows(-1, 0); - didSomething = true; - } else if ( (e->key() == Qt::Key_Up) && modCASIsAlt(e) ) { - moveSelectedCpsWithKeyArrows(0, 1); - didSomething = true; - } else if ( (e->key() == Qt::Key_Down) && modCASIsAlt(e) ) { - moveSelectedCpsWithKeyArrows(0, -1); - didSomething = true; - } - - return didSomething; -} - -void -RotoGui::focusOut(double /*time*/, - ViewIdx /*view*/) -{ - _imp->shiftDown = 0; - _imp->ctrlDown = 0; -} - -bool -RotoGui::keyUp(double time, - const RenderScale & renderScale, - ViewIdx view, - QKeyEvent* e) -{ - Key natronKey = QtEnumConvert::fromQtKey( (Qt::Key)e->key() ); - KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers( e->modifiers() ); - NodePtr node = _imp->node->getNode(); - - node->getEffectInstance()->setCurrentViewportForOverlays_public(_imp->viewer); - if ( node->onOverlayKeyUpDefault(time, renderScale, view, natronKey, natronMod) ) { - return true; - } - - bool didSomething = false; - - if (e->key() == Qt::Key_Shift) { - --_imp->shiftDown; - } else if (e->key() == Qt::Key_Control) { - --_imp->ctrlDown; - } - - - if ( !modCASIsControl(e) ) { - if ( !_imp->iSelectingwithCtrlA && _imp->rotoData->showCpsBbox && (e->key() == Qt::Key_Control) ) { - _imp->rotoData->transformMode = (_imp->rotoData->transformMode == eSelectedCpsTransformModeTranslateAndScale ? - eSelectedCpsTransformModeRotateAndSkew : eSelectedCpsTransformModeTranslateAndScale); - didSomething = true; - } - } - - if ( (e->key() == Qt::Key_Control) && _imp->iSelectingwithCtrlA ) { - _imp->iSelectingwithCtrlA = false; - } - - if (_imp->evaluateOnKeyUp) { - _imp->context->evaluateChange(); - _imp->node->getNode()->getApp()->triggerAutoSave(); - _imp->viewerTab->onRotoEvaluatedForThisViewer(); - _imp->evaluateOnKeyUp = false; - } - - return didSomething; -} - -bool -RotoGui::RotoGuiPrivate::isNearbySelectedCpsCrossHair(const QPointF & pos) const -{ - std::pair pixelScale; - - viewer->getPixelScale(pixelScale.first, pixelScale.second); - - double xHairMidSizeX = kXHairSelectedCpsBox * pixelScale.first; - double xHairMidSizeY = kXHairSelectedCpsBox * pixelScale.second; - double l = rotoData->selectedCpsBbox.topLeft().x(); - double r = rotoData->selectedCpsBbox.bottomRight().x(); - double b = rotoData->selectedCpsBbox.bottomRight().y(); - double t = rotoData->selectedCpsBbox.topLeft().y(); - double toleranceX = kXHairSelectedCpsTolerance * pixelScale.first; - double toleranceY = kXHairSelectedCpsTolerance * pixelScale.second; - double midX = (l + r) / 2.; - double midY = (b + t) / 2.; - double lCross = midX - xHairMidSizeX; - double rCross = midX + xHairMidSizeX; - double bCross = midY - xHairMidSizeY; - double tCross = midY + xHairMidSizeY; - - if ( ( pos.x() >= (lCross - toleranceX) ) && - ( pos.x() <= (rCross + toleranceX) ) && - ( pos.y() <= (tCross + toleranceY) ) && - ( pos.y() >= (bCross - toleranceY) ) ) { - return true; - } else { - return false; - } -} - -bool -RotoGui::RotoGuiPrivate::isWithinSelectedCpsBBox(const QPointF& pos) const -{ - // std::pair pixelScale; -// viewer->getPixelScale(pixelScale.first,pixelScale.second); - - double l = rotoData->selectedCpsBbox.topLeft().x(); - double r = rotoData->selectedCpsBbox.bottomRight().x(); - double b = rotoData->selectedCpsBbox.bottomRight().y(); - double t = rotoData->selectedCpsBbox.topLeft().y(); - double toleranceX = 0;//kXHairSelectedCpsTolerance * pixelScale.first; - double toleranceY = 0;//kXHairSelectedCpsTolerance * pixelScale.second; - - return pos.x() > (l - toleranceX) && pos.x() < (r + toleranceX) && - pos.y() > (b - toleranceY) && pos.y() < (t + toleranceY); -} - -bool -RotoGui::RotoGuiPrivate::isNearbyBBoxTopLeft(const QPointF & p, - double tolerance, - const std::pair & pixelScale) const -{ - QPointF corner = rotoData->selectedCpsBbox.topLeft(); - - if ( ( p.x() >= (corner.x() - tolerance) ) && ( p.x() <= (corner.x() + tolerance) ) && - ( p.y() >= (corner.y() - tolerance) ) && ( p.y() <= (corner.y() + tolerance) ) ) { - return true; - } else { - double halfOffset = kTransformArrowOffsetFromPoint * pixelScale.first / 2.; - double length = kTransformArrowLenght * pixelScale.first; - double halfLength = length / 2.;; - ///test if pos is within the arrow bounding box - QPointF center(corner.x() - halfOffset, corner.y() + halfOffset); - RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); - - return arrowBbox.contains( p.x(), p.y() ); - } -} - -bool -RotoGui::RotoGuiPrivate::isNearbyBBoxTopRight(const QPointF & p, - double tolerance, - const std::pair & pixelScale) const -{ - QPointF topLeft = rotoData->selectedCpsBbox.topLeft(); - QPointF btmRight = rotoData->selectedCpsBbox.bottomRight(); - QPointF corner( btmRight.x(), topLeft.y() ); - - if ( ( p.x() >= (corner.x() - tolerance) ) && ( p.x() <= (corner.x() + tolerance) ) && - ( p.y() >= (corner.y() - tolerance) ) && ( p.y() <= (corner.y() + tolerance) ) ) { - return true; - } else { - double halfOffset = kTransformArrowOffsetFromPoint * pixelScale.first / 2.; - double length = kTransformArrowLenght * pixelScale.first; - double halfLength = length / 2.;; - ///test if pos is within the arrow bounding box - QPointF center(corner.x() + halfOffset, corner.y() + halfOffset); - RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); - - return arrowBbox.contains( p.x(), p.y() ); - } -} - -bool -RotoGui::RotoGuiPrivate::isNearbyBBoxBtmLeft(const QPointF & p, - double tolerance, - const std::pair & pixelScale) const -{ - QPointF topLeft = rotoData->selectedCpsBbox.topLeft(); - QPointF btmRight = rotoData->selectedCpsBbox.bottomRight(); - QPointF corner( topLeft.x(), btmRight.y() ); - - if ( ( p.x() >= (corner.x() - tolerance) ) && ( p.x() <= (corner.x() + tolerance) ) && - ( p.y() >= (corner.y() - tolerance) ) && ( p.y() <= (corner.y() + tolerance) ) ) { - return true; - } else { - double halfOffset = kTransformArrowOffsetFromPoint * pixelScale.first / 2.; - double length = kTransformArrowLenght * pixelScale.first; - double halfLength = length / 2.;; - ///test if pos is within the arrow bounding box - QPointF center(corner.x() - halfOffset, corner.y() - halfOffset); - RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); - - return arrowBbox.contains( p.x(), p.y() ); - } -} - -bool -RotoGui::RotoGuiPrivate::isNearbyBBoxBtmRight(const QPointF & p, - double tolerance, - const std::pair & pixelScale) const -{ - QPointF corner = rotoData->selectedCpsBbox.bottomRight(); - - if ( ( p.x() >= (corner.x() - tolerance) ) && ( p.x() <= (corner.x() + tolerance) ) && - ( p.y() >= (corner.y() - tolerance) ) && ( p.y() <= (corner.y() + tolerance) ) ) { - return true; - } else { - double halfOffset = kTransformArrowOffsetFromPoint * pixelScale.first / 2.; - double length = kTransformArrowLenght * pixelScale.first; - double halfLength = length / 2.;; - ///test if pos is within the arrow bounding box - QPointF center(corner.x() + halfOffset, corner.y() - halfOffset); - RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); - - return arrowBbox.contains( p.x(), p.y() ); - } -} - -bool -RotoGui::RotoGuiPrivate::isNearbyBBoxMidTop(const QPointF & p, - double tolerance, - const std::pair & pixelScale) const -{ - QPointF topLeft = rotoData->selectedCpsBbox.topLeft(); - QPointF btmRight = rotoData->selectedCpsBbox.bottomRight(); - QPointF topRight( btmRight.x(), topLeft.y() ); - QPointF mid = (topLeft + topRight) / 2.; - - if ( ( p.x() >= (mid.x() - tolerance) ) && ( p.x() <= (mid.x() + tolerance) ) && - ( p.y() >= (mid.y() - tolerance) ) && ( p.y() <= (mid.y() + tolerance) ) ) { - return true; - } else { - double offset = kTransformArrowOffsetFromPoint * pixelScale.first; - double length = kTransformArrowLenght * pixelScale.first; - double halfLength = length / 2.; - ///test if pos is within the arrow bounding box - QPointF center(mid.x(), mid.y() + offset); - RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); - - return arrowBbox.contains( p.x(), p.y() ); - } -} - -bool -RotoGui::RotoGuiPrivate::isNearbyBBoxMidRight(const QPointF & p, - double tolerance, - const std::pair & pixelScale) const -{ - QPointF topLeft = rotoData->selectedCpsBbox.topLeft(); - QPointF btmRight = rotoData->selectedCpsBbox.bottomRight(); - QPointF topRight( btmRight.x(), topLeft.y() ); - QPointF mid = (btmRight + topRight) / 2.; - - if ( ( p.x() >= (mid.x() - tolerance) ) && ( p.x() <= (mid.x() + tolerance) ) && - ( p.y() >= (mid.y() - tolerance) ) && ( p.y() <= (mid.y() + tolerance) ) ) { - return true; - } else { - double offset = kTransformArrowOffsetFromPoint * pixelScale.first; - double length = kTransformArrowLenght * pixelScale.first; - double halfLength = length / 2.;; - ///test if pos is within the arrow bounding box - QPointF center( mid.x() + offset, mid.y() ); - RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); - - return arrowBbox.contains( p.x(), p.y() ); - } -} - -bool -RotoGui::RotoGuiPrivate::isNearbyBBoxMidBtm(const QPointF & p, - double tolerance, - const std::pair & pixelScale) const -{ - QPointF topLeft = rotoData->selectedCpsBbox.topLeft(); - QPointF btmRight = rotoData->selectedCpsBbox.bottomRight(); - QPointF btmLeft( topLeft.x(), btmRight.y() ); - QPointF mid = (btmRight + btmLeft) / 2.; - - if ( ( p.x() >= (mid.x() - tolerance) ) && ( p.x() <= (mid.x() + tolerance) ) && - ( p.y() >= (mid.y() - tolerance) ) && ( p.y() <= (mid.y() + tolerance) ) ) { - return true; - } else { - double offset = kTransformArrowOffsetFromPoint * pixelScale.first; - double length = kTransformArrowLenght * pixelScale.first; - double halfLength = length / 2.;; - ///test if pos is within the arrow bounding box - QPointF center(mid.x(), mid.y() - offset); - RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); - - return arrowBbox.contains( p.x(), p.y() ); - } -} - -EventStateEnum -RotoGui::RotoGuiPrivate::isMouseInteractingWithCPSBbox(const QPointF& pos, - double cpSelectionTolerance, - const std::pair& pixelScale) const -{ - bool clickAnywhere = isBboxClickAnywhereEnabled(); - EventStateEnum state = eEventStateNone; - - if ( rotoData->showCpsBbox && isNearbyBBoxTopLeft(pos, cpSelectionTolerance, pixelScale) ) { - state = eEventStateDraggingBBoxTopLeft; - } else if ( rotoData->showCpsBbox && isNearbyBBoxTopRight(pos, cpSelectionTolerance, pixelScale) ) { - state = eEventStateDraggingBBoxTopRight; - } else if ( rotoData->showCpsBbox && isNearbyBBoxBtmLeft(pos, cpSelectionTolerance, pixelScale) ) { - state = eEventStateDraggingBBoxBtmLeft; - } else if ( rotoData->showCpsBbox && isNearbyBBoxBtmRight(pos, cpSelectionTolerance, pixelScale) ) { - state = eEventStateDraggingBBoxBtmRight; - } else if ( rotoData->showCpsBbox && isNearbyBBoxMidTop(pos, cpSelectionTolerance, pixelScale) ) { - state = eEventStateDraggingBBoxMidTop; - } else if ( rotoData->showCpsBbox && isNearbyBBoxMidRight(pos, cpSelectionTolerance, pixelScale) ) { - state = eEventStateDraggingBBoxMidRight; - } else if ( rotoData->showCpsBbox && isNearbyBBoxMidBtm(pos, cpSelectionTolerance, pixelScale) ) { - state = eEventStateDraggingBBoxMidBtm; - } else if ( rotoData->showCpsBbox && isNearbyBBoxMidLeft(pos, cpSelectionTolerance, pixelScale) ) { - state = eEventStateDraggingBBoxMidLeft; - } else if ( clickAnywhere && rotoData->showCpsBbox && isWithinSelectedCpsBBox(pos) ) { - state = eEventStateDraggingSelectedControlPoints; - } else if ( !clickAnywhere && rotoData->showCpsBbox && isNearbySelectedCpsCrossHair(pos) ) { - state = eEventStateDraggingSelectedControlPoints; - } - - return state; -} - -bool -RotoGui::RotoGuiPrivate::isNearbyBBoxMidLeft(const QPointF & p, - double tolerance, - const std::pair & pixelScale) const -{ - QPointF topLeft = rotoData->selectedCpsBbox.topLeft(); - QPointF btmRight = rotoData->selectedCpsBbox.bottomRight(); - QPointF btmLeft( topLeft.x(), btmRight.y() ); - QPointF mid = (topLeft + btmLeft) / 2.; - - if ( ( p.x() >= (mid.x() - tolerance) ) && ( p.x() <= (mid.x() + tolerance) ) && - ( p.y() >= (mid.y() - tolerance) ) && ( p.y() <= (mid.y() + tolerance) ) ) { - return true; - } else { - double offset = kTransformArrowOffsetFromPoint * pixelScale.first; - double length = kTransformArrowLenght * pixelScale.first; - double halfLength = length / 2.;; - ///test if pos is within the arrow bounding box - QPointF center( mid.x() - offset, mid.y() ); - RectD arrowBbox(center.x() - halfLength, center.y() - halfLength, center.x() + halfLength, center.y() + halfLength); - - return arrowBbox.contains( p.x(), p.y() ); - } -} - -bool -RotoGui::RotoGuiPrivate::isNearbySelectedCpsBoundingBox(const QPointF & pos, - double tolerance) const -{ - QPointF topLeft = rotoData->selectedCpsBbox.topLeft(); - QPointF btmRight = rotoData->selectedCpsBbox.bottomRight(); - QPointF btmLeft( topLeft.x(), btmRight.y() ); - QPointF topRight( btmRight.x(), topLeft.y() ); - - ///check if it is nearby top edge - if ( ( pos.x() >= (topLeft.x() - tolerance) ) && ( pos.x() <= (topRight.x() + tolerance) ) && - ( pos.y() >= (topLeft.y() - tolerance) ) && ( pos.y() <= (topLeft.y() + tolerance) ) ) { - return true; - } - - ///right edge - if ( ( pos.x() >= (topRight.x() - tolerance) ) && ( pos.x() <= (topRight.x() + tolerance) ) && - ( pos.y() >= (btmRight.y() - tolerance) ) && ( pos.y() <= (topRight.y() + tolerance) ) ) { - return true; - } - - ///btm edge - if ( ( pos.x() >= (btmLeft.x() - tolerance) ) && ( pos.x() <= (btmRight.x() + tolerance) ) && - ( pos.y() >= (btmLeft.y() - tolerance) ) && ( pos.y() <= (btmLeft.y() + tolerance) ) ) { - return true; - } - - ///left edge - if ( ( pos.x() >= (btmLeft.x() - tolerance) ) && ( pos.x() <= (btmLeft.x() + tolerance) ) && - ( pos.y() >= (btmLeft.y() - tolerance) ) && ( pos.y() <= (topLeft.y() + tolerance) ) ) { - return true; - } - - return false; -} - -std::pair, boost::shared_ptr > -RotoGui::RotoGuiPrivate::isNearbyFeatherBar(double time, - const std::pair & pixelScale, - const QPointF & pos) const -{ - double distFeatherX = 20. * pixelScale.first; - double acceptance = 10 * pixelScale.second; - - for (SelectedItems::const_iterator it = rotoData->selectedItems.begin(); it != rotoData->selectedItems.end(); ++it) { - Bezier* isBezier = dynamic_cast( it->get() ); - RotoStrokeItem* isStroke = dynamic_cast( it->get() ); - assert(isStroke || isBezier); - if ( isStroke || !isBezier || ( isBezier && isBezier->isOpenBezier() ) ) { - continue; - } - - /* - For each selected bezier, we compute the extent of the feather bars and check if the mouse would be nearby one of these bars. - The feather bar of a control point is only displayed is the feather point is equal to the bezier control point. - In order to give it the correc direction we use the derivative of the bezier curve at the control point and then use - the pointInPolygon function to make sure the feather bar is always oriented on the outter part of the polygon. - The pointInPolygon function needs the polygon of the bezier to test whether the point is inside or outside the polygon - hence in this loop we compute the polygon for each bezier. - */ - - Transform::Matrix3x3 transform; - isBezier->getTransformAtTime(time, &transform); - - const std::list > & fps = isBezier->getFeatherPoints(); - const std::list > & cps = isBezier->getControlPoints(); - assert( cps.size() == fps.size() ); - - int cpCount = (int)cps.size(); - if (cpCount <= 1) { - continue; - } -// std::list polygon; -// RectD polygonBBox( std::numeric_limits::infinity(), -// std::numeric_limits::infinity(), -// -std::numeric_limits::infinity(), -// -std::numeric_limits::infinity() ); -// (*it)->evaluateFeatherPointsAtTime_DeCasteljau(time, 0, 50, true, &polygon, &polygonBBox); - - std::list >::const_iterator itF = fps.begin(); - std::list >::const_iterator nextF = itF; - if ( nextF != fps.end() ) { - ++nextF; - } - std::list >::const_iterator prevF = fps.end(); - if ( prevF != fps.begin() ) { - --prevF; - } - bool isClockWiseOriented = isBezier->isFeatherPolygonClockwiseOriented(true, time); - - for (std::list >::const_iterator itCp = cps.begin(); - itCp != cps.end(); - ++itCp) { - if ( prevF == fps.end() ) { - prevF = fps.begin(); - } - if ( nextF == fps.end() ) { - nextF = fps.begin(); - } - assert( itF != fps.end() ); // because cps.size() == fps.size() - if ( itF == fps.end() ) { - itF = fps.begin(); - } - - Transform::Point3D controlPoint, featherPoint; - controlPoint.z = featherPoint.z = 1; - (*itCp)->getPositionAtTime(true, time, ViewIdx(0), &controlPoint.x, &controlPoint.y); - (*itF)->getPositionAtTime(true, time, ViewIdx(0), &featherPoint.x, &featherPoint.y); - - controlPoint = Transform::matApply(transform, controlPoint); - featherPoint = Transform::matApply(transform, featherPoint); - { - Point cp, fp; - cp.x = controlPoint.x; - cp.y = controlPoint.y; - fp.x = featherPoint.x; - fp.y = featherPoint.y; - Bezier::expandToFeatherDistance(true, cp, &fp, distFeatherX, time, isClockWiseOriented, transform, prevF, itF, nextF); - featherPoint.x = fp.x; - featherPoint.y = fp.y; - } - assert(featherPoint.x != controlPoint.x || featherPoint.y != controlPoint.y); - - ///Now test if the user mouse click is on the line using bounding box and cross product. - if ( ( ( ( pos.y() >= (controlPoint.y - acceptance) ) && ( pos.y() <= (featherPoint.y + acceptance) ) ) || - ( ( pos.y() >= (featherPoint.y - acceptance) ) && ( pos.y() <= (controlPoint.y + acceptance) ) ) ) && - ( ( ( pos.x() >= (controlPoint.x - acceptance) ) && ( pos.x() <= (featherPoint.x + acceptance) ) ) || - ( ( pos.x() >= (featherPoint.x - acceptance) ) && ( pos.x() <= (controlPoint.x + acceptance) ) ) ) ) { - Point a; - a.x = (featherPoint.x - controlPoint.x); - a.y = (featherPoint.y - controlPoint.y); - double norm = sqrt(a.x * a.x + a.y * a.y); - - ///The point is in the bounding box of the segment, if it is vertical it must be on the segment anyway - if (norm == 0) { - return std::make_pair(*itCp, *itF); - } - - a.x /= norm; - a.y /= norm; - Point b; - b.x = (pos.x() - controlPoint.x); - b.y = (pos.y() - controlPoint.y); - norm = sqrt(b.x * b.x + b.y * b.y); - - ///This vector is not vertical - if (norm != 0) { - b.x /= norm; - b.y /= norm; - - double crossProduct = b.y * a.x - b.x * a.y; - if (std::abs(crossProduct) < 0.3) { - return std::make_pair(*itCp, *itF); - } - } - } - - // increment for next iteration - // ++itF, ++nextF, ++prevF - if ( itF != fps.end() ) { - ++itF; - } - if ( nextF != fps.end() ) { - ++nextF; - } - if ( prevF != fps.end() ) { - ++prevF; - } - } // for(itCp) - } - - return std::make_pair( boost::shared_ptr(), boost::shared_ptr() ); -} // isNearbyFeatherBar - -void -RotoGui::onAutoKeyingButtonClicked(bool e) -{ - _imp->autoKeyingEnabled->setDown(e); - _imp->context->onAutoKeyingChanged(e); -} - -void -RotoGui::onFeatherLinkButtonClicked(bool e) -{ - _imp->featherLinkEnabled->setDown(e); - _imp->context->onFeatherLinkChanged(e); -} - -void -RotoGui::onRippleEditButtonClicked(bool e) -{ - _imp->rippleEditEnabled->setDown(e); - _imp->context->onRippleEditChanged(e); -} - -void -RotoGui::onStickySelectionButtonClicked(bool e) -{ - _imp->stickySelectionEnabled->setDown(e); -} - -void -RotoGui::onBboxClickButtonClicked(bool e) -{ - _imp->bboxClickAnywhere->setDown(e); -} - -bool -RotoGui::isStickySelectionEnabled() const -{ - return _imp->stickySelectionEnabled ? _imp->stickySelectionEnabled->isChecked() : false; -} - -void -RotoGui::onAddKeyFrameClicked() -{ - double time = _imp->context->getTimelineCurrentTime(); - - for (SelectedItems::iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - Bezier* isBezier = dynamic_cast( it->get() ); - if (!isBezier) { - continue; - } - isBezier->setKeyframe(time); - } - _imp->context->evaluateChange(); -} - -void -RotoGui::onRemoveKeyFrameClicked() -{ - double time = _imp->context->getTimelineCurrentTime(); - - for (SelectedItems::iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - Bezier* isBezier = dynamic_cast( it->get() ); - if (!isBezier) { - continue; - } - isBezier->removeKeyframe(time); - } - _imp->context->evaluateChange(); -} - -void -RotoGui::onCurrentFrameChanged(SequenceTime /*time*/, - int) -{ - _imp->computeSelectedCpsBBOX(); -} - -void -RotoGui::restoreSelectionFromContext() -{ - _imp->rotoData->selectedItems = _imp->context->getSelectedCurves(); -} - -void -RotoGui::onRefreshAsked() -{ - _imp->viewer->redraw(); -} - -void -RotoGui::RotoGuiPrivate::onCurveLockedChangedRecursive(const boost::shared_ptr & item, - bool* ret) -{ - boost::shared_ptr b = boost::dynamic_pointer_cast(item); - boost::shared_ptr layer = boost::dynamic_pointer_cast(item); - - if (b) { - if ( item->isLockedRecursive() ) { - for (SelectedItems::iterator fb = rotoData->selectedItems.begin(); fb != rotoData->selectedItems.end(); ++fb) { - if ( fb->get() == b.get() ) { - ///if the curve was selected, wipe the selection CP bbox - clearCPSSelection(); - rotoData->selectedItems.erase(fb); - *ret = true; - break; - } - } - } else { - ///Explanation: This change has been made in result to a user click on the settings panel. - ///We have to reselect the bezier overlay hence put a reason different of eSelectionReasonOverlayInteract - SelectedItems::iterator found = std::find(rotoData->selectedItems.begin(), rotoData->selectedItems.end(), b); - if ( found == rotoData->selectedItems.end() ) { - rotoData->selectedItems.push_back(b); - context->select(b, RotoItem::eSelectionReasonSettingsPanel); - *ret = true; - } - } - } else if (layer) { - const std::list > & items = layer->getItems(); - for (std::list >::const_iterator it = items.begin(); it != items.end(); ++it) { - onCurveLockedChangedRecursive(*it, ret); - } - } -} - -void -RotoGui::onCurveLockedChanged(int reason) -{ - boost::shared_ptr item = _imp->context->getLastItemLocked(); - - if ( item && ( (RotoItem::SelectionReasonEnum)reason != RotoItem::eSelectionReasonOverlayInteract ) ) { - assert(item); - bool changed = false; - if (item) { - _imp->onCurveLockedChangedRecursive(item, &changed); - } - - if (changed) { - _imp->viewer->redraw(); - } - } -} - -void -RotoGui::onSelectionChanged(int reason) -{ - if ( (RotoItem::SelectionReasonEnum)reason != RotoItem::eSelectionReasonOverlayInteract ) { - _imp->rotoData->selectedItems = _imp->context->getSelectedCurves(); - _imp->viewer->redraw(); - } -} - -void -RotoGui::setSelection(const std::list > & selectedItems, - const std::list, boost::shared_ptr > > & selectedCps) -{ - _imp->rotoData->selectedItems.clear(); - for (std::list >::const_iterator it = selectedItems.begin(); it != selectedItems.end(); ++it) { - if (*it) { - _imp->rotoData->selectedItems.push_back(*it); - } - } - _imp->rotoData->selectedCps.clear(); - for (SelectedCPs::const_iterator it = selectedCps.begin(); it != selectedCps.end(); ++it) { - if (it->first && it->second) { - _imp->rotoData->selectedCps.push_back(*it); - } - } - _imp->context->select(_imp->rotoData->selectedItems, RotoItem::eSelectionReasonOverlayInteract); - _imp->computeSelectedCpsBBOX(); -} - -void -RotoGui::setSelection(const boost::shared_ptr & curve, - const std::pair, boost::shared_ptr > & point) -{ - _imp->rotoData->selectedItems.clear(); - if (curve) { - _imp->rotoData->selectedItems.push_back(curve); - } - _imp->rotoData->selectedCps.clear(); - if (point.first && point.second) { - _imp->rotoData->selectedCps.push_back(point); - } - if (curve) { - _imp->context->select(curve, RotoItem::eSelectionReasonOverlayInteract); - } - _imp->computeSelectedCpsBBOX(); -} - -void -RotoGui::getSelection(std::list >* selectedBeziers, - std::list, boost::shared_ptr > >* selectedCps) -{ - *selectedBeziers = _imp->rotoData->selectedItems; - *selectedCps = _imp->rotoData->selectedCps; -} - -void -RotoGui::refreshSelectionBBox() -{ - _imp->computeSelectedCpsBBOX(); -} - -void -RotoGui::setBuiltBezier(const boost::shared_ptr & curve) -{ - assert(curve); - _imp->rotoData->builtBezier = curve; -} - -boost::shared_ptr RotoGui::getBezierBeingBuild() const -{ - return _imp->rotoData->builtBezier; -} - -void -RotoGui::pushUndoCommand(QUndoCommand* cmd) -{ - NodeSettingsPanel* panel = _imp->node->getSettingPanel(); - - assert(panel); - panel->pushUndoCommand(cmd); -} - -QString -RotoGui::getNodeName() const -{ - return QString::fromUtf8( _imp->node->getNode()->getScriptName().c_str() ); -} - -RotoContext* -RotoGui::getContext() -{ - return _imp->context.get(); -} - -void -RotoGui::onDisplayFeatherButtonClicked(bool toggled) -{ - _imp->displayFeatherEnabled->setDown(toggled); - _imp->rotoData->displayFeather = toggled; - _imp->viewer->redraw(); -} - -bool -RotoGui::isFeatherVisible() const -{ - return _imp->rotoData->displayFeather; -} - -void -RotoGui::showMenuForCurve(const boost::shared_ptr & curve) -{ - QPoint pos = QCursor::pos(); - Menu menu(_imp->viewer); - - - //menu.setFont( QFont(appFont,appFontSize) ); - ActionWithShortcut* selectAllAction = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoSelectAll, - kShortcutDescActionRotoSelectAll, &menu); - - menu.addAction(selectAllAction); - - ActionWithShortcut* deleteCurve = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoDelete, - kShortcutDescActionRotoDelete, &menu); - menu.addAction(deleteCurve); - - ActionWithShortcut* openCloseCurve = 0; - if ( !curve->isOpenBezier() ) { - openCloseCurve = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoCloseBezier, - kShortcutDescActionRotoCloseBezier - , &menu); - menu.addAction(openCloseCurve); - } - - ActionWithShortcut* smoothAction = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoSmooth, - kShortcutDescActionRotoSmooth - , &menu); - menu.addAction(smoothAction); - - ActionWithShortcut* cuspAction = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoCuspBezier, - kShortcutDescActionRotoCuspBezier - , &menu); - menu.addAction(cuspAction); - - ActionWithShortcut* removeFeather = 0; - if ( !curve->isOpenBezier() ) { - removeFeather = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoRemoveFeather, - kShortcutDescActionRotoRemoveFeather - , &menu); - menu.addAction(removeFeather); - } - - ActionWithShortcut* lockShape = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoLockCurve, - kShortcutDescActionRotoLockCurve - , &menu); - menu.addAction(lockShape); - - - ActionWithShortcut* linkTo = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoLinkToTrack, - kShortcutDescActionRotoLinkToTrack - , &menu); - menu.addAction(linkTo); - ActionWithShortcut* unLinkFrom = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoUnlinkToTrack, - kShortcutDescActionRotoUnlinkToTrack - , &menu); - menu.addAction(unLinkFrom); - - - QAction* ret = menu.exec(pos); - - - if (ret == selectAllAction) { - const std::list > & cps = curve->getControlPoints(); - const std::list > & fps = curve->getFeatherPoints(); - assert( cps.size() == fps.size() ); - - std::list >::const_iterator cpIT = cps.begin(); - for (std::list >::const_iterator fpIT = fps.begin(); fpIT != fps.end(); ++fpIT, ++cpIT) { - _imp->rotoData->selectedCps.push_back( std::make_pair(*cpIT, *fpIT) ); - } - _imp->computeSelectedCpsBBOX(); - _imp->viewer->redraw(); - } else if (ret == deleteCurve) { - std::list > beziers; - beziers.push_back(curve); - pushUndoCommand( new RemoveCurveUndoCommand(this, beziers) ); - _imp->viewer->redraw(); - } else if ( openCloseCurve && (ret == openCloseCurve) ) { - pushUndoCommand( new OpenCloseUndoCommand(this, curve) ); - _imp->viewer->redraw(); - } else if (ret == smoothAction) { - smoothSelectedCurve(); - } else if (ret == cuspAction) { - cuspSelectedCurve(); - } else if ( removeFeather && (ret == removeFeather) ) { - removeFeatherForSelectedCurve(); - } else if (ret == linkTo) { - SelectedCPs points; - const std::list > & cps = curve->getControlPoints(); - const std::list > & fps = curve->getFeatherPoints(); - assert( cps.size() == fps.size() ); - - std::list >::const_iterator cpIT = cps.begin(); - for (std::list >::const_iterator fpIT = fps.begin(); fpIT != fps.end(); ++fpIT, ++cpIT) { - if ( !(*cpIT)->isSlaved() && !(*fpIT)->isSlaved() ) { - points.push_back( std::make_pair(*cpIT, *fpIT) ); - } - } - - linkPointTo(points); - } else if (ret == unLinkFrom) { - SelectedCPs points; - const std::list > & cps = curve->getControlPoints(); - const std::list > & fps = curve->getFeatherPoints(); - assert( cps.size() == fps.size() ); - - std::list >::const_iterator cpIT = cps.begin(); - for (std::list >::const_iterator fpIT = fps.begin(); fpIT != fps.end(); ++fpIT, ++cpIT) { - if ( (*cpIT)->isSlaved() && (*fpIT)->isSlaved() ) { - points.push_back( std::make_pair(*cpIT, *fpIT) ); - } - } - - pushUndoCommand( new UnLinkFromTrackUndoCommand(this, points) ); - } else if (ret == lockShape) { - lockSelectedCurves(); - } -} // showMenuForCurve - -void -RotoGui::smoothSelectedCurve() -{ - std::pair pixelScale; - - _imp->viewer->getPixelScale(pixelScale.first, pixelScale.second); - double time = _imp->context->getTimelineCurrentTime(); - std::list datas; - - if ( !_imp->rotoData->selectedCps.empty() ) { - for (SelectedCPs::const_iterator it = _imp->rotoData->selectedCps.begin(); it != _imp->rotoData->selectedCps.end(); ++it) { - SmoothCuspUndoCommand::SmoothCuspCurveData data; - data.curve = it->first->getBezier(); - data.newPoints.push_back(*it); - datas.push_back(data); - } - } else { - for (SelectedItems::const_iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - boost::shared_ptr bezier = boost::dynamic_pointer_cast(*it); - if (bezier) { - SmoothCuspUndoCommand::SmoothCuspCurveData data; - data.curve = bezier; - const std::list > & cps = bezier->getControlPoints(); - const std::list > & fps = bezier->getFeatherPoints(); - std::list >::const_iterator itFp = fps.begin(); - for (std::list >::const_iterator it = cps.begin(); it != cps.end(); ++it, ++itFp) { - data.newPoints.push_back( std::make_pair(*it, *itFp) ); - } - datas.push_back(data); - } - } - } - if ( !datas.empty() ) { - pushUndoCommand( new SmoothCuspUndoCommand(this, datas, time, false, pixelScale) ); - _imp->viewer->redraw(); - } -} - -void -RotoGui::cuspSelectedCurve() -{ - std::pair pixelScale; - - _imp->viewer->getPixelScale(pixelScale.first, pixelScale.second); - double time = _imp->context->getTimelineCurrentTime(); - std::list datas; - - if ( !_imp->rotoData->selectedCps.empty() ) { - for (SelectedCPs::const_iterator it = _imp->rotoData->selectedCps.begin(); it != _imp->rotoData->selectedCps.end(); ++it) { - SmoothCuspUndoCommand::SmoothCuspCurveData data; - data.curve = it->first->getBezier(); - data.newPoints.push_back(*it); - datas.push_back(data); - } - } else { - for (SelectedItems::const_iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - boost::shared_ptr bezier = boost::dynamic_pointer_cast(*it); - if (bezier) { - SmoothCuspUndoCommand::SmoothCuspCurveData data; - data.curve = bezier; - const std::list > & cps = bezier->getControlPoints(); - const std::list > & fps = bezier->getFeatherPoints(); - std::list >::const_iterator itFp = fps.begin(); - for (std::list >::const_iterator it = cps.begin(); it != cps.end(); ++it, ++itFp) { - data.newPoints.push_back( std::make_pair(*it, *itFp) ); - } - datas.push_back(data); - } - } - } - if ( !datas.empty() ) { - pushUndoCommand( new SmoothCuspUndoCommand(this, datas, time, true, pixelScale) ); - } - _imp->viewer->redraw(); -} - -void -RotoGui::removeFeatherForSelectedCurve() -{ - std::list datas; - - for (SelectedItems::const_iterator it = _imp->rotoData->selectedItems.begin(); it != _imp->rotoData->selectedItems.end(); ++it) { - boost::shared_ptr bezier = boost::dynamic_pointer_cast(*it); - if (bezier) { - RemoveFeatherUndoCommand::RemoveFeatherData data; - data.curve = bezier; - data.newPoints = bezier->getFeatherPoints(); - datas.push_back(data); - } - } - if ( !datas.empty() ) { - pushUndoCommand( new RemoveFeatherUndoCommand(this, datas) ); - _imp->viewer->redraw(); - } -} - -void -RotoGui::lockSelectedCurves() -{ - ///Make a copy because setLocked will change the selection internally and invalidate the iterator - SelectedItems selection = _imp->rotoData->selectedItems; - - for (SelectedItems::const_iterator it = selection.begin(); it != selection.end(); ++it) { - (*it)->setLocked(true, false, RotoItem::eSelectionReasonOverlayInteract); - } - _imp->clearSelection(); - _imp->viewer->redraw(); -} - -void -RotoGui::showMenuForControlPoint(const boost::shared_ptr & curve, - const std::pair, boost::shared_ptr > & cp) -{ - std::pair pixelScale; - - _imp->viewer->getPixelScale(pixelScale.first, pixelScale.second); - - QPoint pos = QCursor::pos(); - Menu menu(_imp->viewer); - - //menu.setFont( QFont(appFont,appFontSize) ); - ActionWithShortcut* deleteCp = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoDelete, - kShortcutDescActionRotoDelete, &menu); - menu.addAction(deleteCp); - - ActionWithShortcut* smoothAction = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoSmooth, - kShortcutDescActionRotoSmooth - , &menu); - - menu.addAction(smoothAction); - - ActionWithShortcut* cuspAction = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoCuspBezier, - kShortcutDescActionRotoCuspBezier - , &menu); - menu.addAction(cuspAction); - - ActionWithShortcut* removeFeather = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoRemoveFeather, - kShortcutDescActionRotoRemoveFeather - , &menu); - - menu.addAction(removeFeather); - - menu.addSeparator(); - - boost::shared_ptr isSlaved = cp.first->isSlaved(); - ActionWithShortcut* linkTo = 0, *unLinkFrom = 0; - if (!isSlaved) { - linkTo = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoLinkToTrack, - kShortcutDescActionRotoLinkToTrack - , &menu); - - menu.addAction(linkTo); - } else { - unLinkFrom = new ActionWithShortcut(kShortcutGroupRoto, - kShortcutIDActionRotoUnlinkToTrack, - kShortcutDescActionRotoUnlinkToTrack - , &menu); - menu.addAction(unLinkFrom); - } - - QAction* ret = menu.exec(pos); - double time = _imp->context->getTimelineCurrentTime(); - if (ret == deleteCp) { - pushUndoCommand( new RemovePointUndoCommand(this, curve, !cp.first->isFeatherPoint() ? cp.first : cp.second) ); - _imp->viewer->redraw(); - } else if (ret == smoothAction) { - std::list datas; - SmoothCuspUndoCommand::SmoothCuspCurveData data; - data.curve = curve; - data.newPoints.push_back(cp); - datas.push_back(data); - pushUndoCommand( new SmoothCuspUndoCommand(this, datas, time, false, pixelScale) ); - _imp->viewer->redraw(); - } else if (ret == cuspAction) { - std::list datas; - SmoothCuspUndoCommand::SmoothCuspCurveData data; - data.curve = curve; - data.newPoints.push_back(cp); - datas.push_back(data); - pushUndoCommand( new SmoothCuspUndoCommand(this, datas, time, true, pixelScale) ); - _imp->viewer->redraw(); - } else if (ret == removeFeather) { - std::list datas; - RemoveFeatherUndoCommand::RemoveFeatherData data; - data.curve = curve; - data.newPoints.push_back(cp.first->isFeatherPoint() ? cp.first : cp.second); - datas.push_back(data); - pushUndoCommand( new RemoveFeatherUndoCommand(this, datas) ); - _imp->viewer->redraw(); - } else if ( (ret == linkTo) && (ret != NULL) ) { - SelectedCPs points; - points.push_back(cp); - linkPointTo(points); - } else if ( (ret == unLinkFrom) && (ret != NULL) ) { - SelectedCPs points; - points.push_back(cp); - pushUndoCommand( new UnLinkFromTrackUndoCommand(this, points) ); - } -} // showMenuForControlPoint - -class LinkToTrackDialog - : public QDialog -{ - ComboBox* _choice; - -public: - - LinkToTrackDialog(const std::vector< std::pair > > & knobs, - QWidget* parent) - : QDialog(parent) - { - QVBoxLayout* mainLayout = new QVBoxLayout(this); - - _choice = new ComboBox(this); - - for (std::vector< std::pair > >::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { - _choice->addItem( QString::fromUtf8( it->first.c_str() ) ); - } - - mainLayout->addWidget(_choice); - - QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this); - connect( buttons, SIGNAL(accepted()), this, SLOT(accept()) ); - connect( buttons, SIGNAL(rejected()), this, SLOT(reject()) ); - mainLayout->addWidget(buttons); - setWindowTitle( QObject::tr("Link to track") ); - } - - int getSelectedKnob() const - { - return _choice->activeIndex(); - } - - virtual ~LinkToTrackDialog() - { - } -}; - -void -RotoGui::linkPointTo(const std::list, boost::shared_ptr > > & points) -{ - std::vector< std::pair > > knobs; - NodesList activeNodes; - - _imp->node->getNode()->getGroup()->getActiveNodes(&activeNodes); - for (NodesList::iterator it = activeNodes.begin(); it != activeNodes.end(); ++it) { - if ( (*it)->isPointTrackerNode() && (*it)->getParentMultiInstance() ) { - KnobPtr k = (*it)->getKnobByName("center"); - KnobPtr name = (*it)->getKnobByName(kNatronOfxParamStringSublabelName); - if (k && name) { - boost::shared_ptr dk = boost::dynamic_pointer_cast(k); - KnobString* nameKnob = dynamic_cast( name.get() ); - if (dk && nameKnob) { - std::string trackName = nameKnob->getValue(); - trackName += "/"; - trackName += dk->getLabel(); - knobs.push_back( std::make_pair(trackName, dk) ); - } - } - } - } - if ( knobs.empty() ) { - Dialogs::warningDialog( "", tr("No tracker found in the project.").toStdString() ); - - return; - } - LinkToTrackDialog dialog(knobs, _imp->viewerTab); - if ( dialog.exec() ) { - int index = dialog.getSelectedKnob(); - if ( (index >= 0) && ( index < (int)knobs.size() ) ) { - const boost::shared_ptr & knob = knobs[index].second; - if ( knob && (knob->getDimension() == 2) ) { - pushUndoCommand( new LinkToTrackUndoCommand(this, points, knob) ); - } - } - } -} - -void -RotoGui::onColorWheelButtonClicked() -{ - QColorDialog dialog(_imp->viewerTab); - QColor previousColor = _imp->colorPickerLabel->getCurrentColor(); - - dialog.setCurrentColor(previousColor); - QObject::connect( &dialog, SIGNAL(currentColorChanged(QColor)), this, SLOT(onDialogCurrentColorChanged(QColor)) ); - if ( !dialog.exec() ) { - _imp->colorPickerLabel->setColor(previousColor); - } else { - _imp->colorPickerLabel->setColor( dialog.currentColor() ); - onBreakMultiStrokeTriggered(); - } -} - -void -RotoGui::onDialogCurrentColorChanged(const QColor& color) -{ - assert(_imp->colorPickerLabel); - _imp->colorPickerLabel->setColor(color); -} - -void -RotoGui::onPressureOpacityClicked(bool isDown) -{ - _imp->pressureOpacityButton->setDown(isDown); - _imp->pressureOpacityButton->setChecked(isDown); - onBreakMultiStrokeTriggered(); -} - -void -RotoGui::onPressureSizeClicked(bool isDown) -{ - _imp->pressureSizeButton->setDown(isDown); - _imp->pressureSizeButton->setChecked(isDown); - onBreakMultiStrokeTriggered(); -} - -void -RotoGui::onPressureHardnessClicked(bool isDown) -{ - _imp->pressureHardnessButton->setDown(isDown); - _imp->pressureHardnessButton->setChecked(isDown); - onBreakMultiStrokeTriggered(); -} - -void -RotoGui::onBuildupClicked(bool isDown) -{ - _imp->buildUpButton->setDown(isDown); - onBreakMultiStrokeTriggered(); -} - -void -RotoGui::notifyGuiClosing() -{ - _imp->viewerTab = 0; - _imp->viewer = 0; - _imp->rotoData->strokeBeingPaint.reset(); -} - -void -RotoGui::onResetCloneTransformClicked() -{ - _imp->rotoData->cloneOffset.first = _imp->rotoData->cloneOffset.second = 0; - onBreakMultiStrokeTriggered(); -} - -NATRON_NAMESPACE_EXIT; - -NATRON_NAMESPACE_USING; -#include "moc_RotoGui.cpp" diff --git a/Gui/RotoGui.h b/Gui/RotoGui.h deleted file mode 100644 index 576a904055..0000000000 --- a/Gui/RotoGui.h +++ /dev/null @@ -1,336 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * This file is part of Natron , - * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat - * - * Natron is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Natron is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Natron. If not, see - * ***** END LICENSE BLOCK ***** */ - -#ifndef ROTOGUI_H -#define ROTOGUI_H - -// ***** BEGIN PYTHON BLOCK ***** -// from : -// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." -#include -// ***** END PYTHON BLOCK ***** - -#include "Global/Macros.h" - -#if !defined(Q_MOC_RUN) && !defined(SBK_RUN) -#include -#include -#endif - -CLANG_DIAG_OFF(deprecated) -CLANG_DIAG_OFF(uninitialized) -#include -#include -CLANG_DIAG_ON(deprecated) -CLANG_DIAG_ON(uninitialized) - -#include "Global/GlobalDefines.h" - -#include "Engine/EngineFwd.h" - -#include "Gui/GuiFwd.h" - -NATRON_NAMESPACE_ENTER; - -class RotoToolButton - : public QToolButton -{ -GCC_DIAG_SUGGEST_OVERRIDE_OFF - Q_OBJECT -GCC_DIAG_SUGGEST_OVERRIDE_ON Q_PROPERTY(bool isSelected READ getIsSelected WRITE setIsSelected) - -public: - - RotoToolButton(QWidget* parent); - - virtual ~RotoToolButton(); - - - void handleSelection(); - - bool getIsSelected() const; - void setIsSelected(bool s); - -public Q_SLOTS: - - void handleLongPress(); - -private: - - virtual void mousePressEvent(QMouseEvent* e) OVERRIDE FINAL; - virtual void mouseReleaseEvent(QMouseEvent* e) OVERRIDE FINAL; - bool isSelected; - bool wasMouseReleased; -}; - -class RotoGui - : public QObject -{ - Q_OBJECT - -public: - - enum RotoTypeEnum - { - eRotoTypeRotoscoping = 0, - eRotoTypeRotopainting - }; - - enum RotoRoleEnum - { - eRotoRoleSelection = 0, - eRotoRolePointsEdition, - eRotoRoleBezierEdition, - eRotoRolePaintBrush, - eRotoRoleCloneBrush, - eRotoRoleEffectBrush, - eRotoRoleMergeBrush - }; - - enum RotoToolEnum - { - eRotoToolSelectAll = 0, - eRotoToolSelectPoints, - eRotoToolSelectCurves, - eRotoToolSelectFeatherPoints, - - eRotoToolAddPoints, - eRotoToolRemovePoints, - eRotoToolRemoveFeatherPoints, - eRotoToolOpenCloseCurve, - eRotoToolSmoothPoints, - eRotoToolCuspPoints, - - eRotoToolDrawBezier, - eRotoToolDrawBSpline, - eRotoToolDrawEllipse, - eRotoToolDrawRectangle, - - eRotoToolSolidBrush, - eRotoToolOpenBezier, - eRotoToolEraserBrush, - - eRotoToolClone, - eRotoToolReveal, - - eRotoToolBlur, - eRotoToolSharpen, - eRotoToolSmear, - - eRotoToolDodge, - eRotoToolBurn - }; - - RotoGui(NodeGui* node, - ViewerTab* parent, - const boost::shared_ptr & sharedData); - - ~RotoGui(); - - boost::shared_ptr getRotoGuiSharedData() const; - GuiAppInstance* getApp() const; - - /** - * @brief Return the horizontal buttons bar for the given role - **/ - QWidget* getButtonsBar(RotoGui::RotoRoleEnum role) const; - - /** - * @brief Same as getButtonsBar(getCurrentRole()) - **/ - QWidget* getCurrentButtonsBar() const; - - /** - * @brief The currently used tool - **/ - RotoGui::RotoToolEnum getSelectedTool() const; - - void setCurrentTool(RotoGui::RotoToolEnum tool, bool emitSignal); - - QToolBar* getToolBar() const; - - /** - * @brief The selected role (selection,draw,add points, etc...) - **/ - RotoGui::RotoRoleEnum getCurrentRole() const; - - void drawOverlays(double time, const RenderScale & renderScale, ViewIdx view) const; - - bool penDown(double time, const RenderScale & renderScale, ViewIdx view, PenType pen, bool isTabletEvent, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, QMouseEvent* e); - - bool penDoubleClicked(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, QMouseEvent* e); - - bool penMotion(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, QInputEvent* e); - - bool penUp(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, QMouseEvent* e); - - bool keyDown(double time, const RenderScale & renderScale, ViewIdx view, QKeyEvent* e); - - bool keyUp(double time, const RenderScale & renderScale, ViewIdx view, QKeyEvent* e); - - bool keyRepeat(double time, const RenderScale & renderScale, ViewIdx view, QKeyEvent* e); - - void focusOut(double time, ViewIdx view); - - bool isStickySelectionEnabled() const; - - /** - * @brief Set the selection to be the given beziers and the given control points. - * This can only be called on the main-thread. - **/ - void setSelection(const std::list > & selectedBeziers, - const std::list, boost::shared_ptr > > & selectedCps); - void setSelection(const boost::shared_ptr & curve, - const std::pair, boost::shared_ptr > & point); - - void getSelection(std::list >* selectedBeziers, - std::list, boost::shared_ptr > >* selectedCps); - - void refreshSelectionBBox(); - - void setBuiltBezier(const boost::shared_ptr & curve); - - boost::shared_ptr getBezierBeingBuild() const; - - /** - * @brief For undo/redo purpose, calling this will do 3 things: - * Refresh overlays - * Trigger a new render - * Trigger an auto-save - * Never call this upon the *first* redo() call, we do this already in the user event methods. - **/ - void evaluate(bool redraw); - - void autoSaveAndRedraw(); - - void pushUndoCommand(QUndoCommand* cmd); - - QString getNodeName() const; - - /** - * @brief This pointer is not meant to be stored away - **/ - RotoContext* getContext(); - - /** - * @brief Calls RotoContext::removeItem but also clears some pointers if they point to - * this curve. For undo/redo purpose. - **/ - void removeCurve(const boost::shared_ptr& curve); - - bool isFeatherVisible() const; - - void linkPointTo(const std::list, boost::shared_ptr > > & cp); - - void notifyGuiClosing(); - -Q_SIGNALS: - - /** - * @brief Emitted when the selected role changes - **/ - void roleChanged(int previousRole, int newRole); - - void selectedToolChanged(int); - -public Q_SLOTS: - - void updateSelectionFromSelectionRectangle(bool onRelease); - - void onSelectionCleared(); - - void onToolActionTriggered(); - - void onToolActionTriggered(QAction* act); - - void onAutoKeyingButtonClicked(bool); - - void onFeatherLinkButtonClicked(bool); - - void onRippleEditButtonClicked(bool); - - void onStickySelectionButtonClicked(bool); - - void onBboxClickButtonClicked(bool); - - void onAddKeyFrameClicked(); - - void onRemoveKeyFrameClicked(); - - void onCurrentFrameChanged(SequenceTime, int); - - void restoreSelectionFromContext(); - - void onRefreshAsked(); - - void onCurveLockedChanged(int); - - void onSelectionChanged(int reason); - - void onDisplayFeatherButtonClicked(bool toggled); - - void onTimelineTimeChanged(); - - void onBreakMultiStrokeTriggered(); - - void smoothSelectedCurve(); - void cuspSelectedCurve(); - void removeFeatherForSelectedCurve(); - void lockSelectedCurves(); - - void onColorWheelButtonClicked(); - void onDialogCurrentColorChanged(const QColor& color); - - void onPressureOpacityClicked(bool isDown); - void onPressureSizeClicked(bool isDown); - void onPressureHardnessClicked(bool isDown); - void onBuildupClicked(bool isDown); - - void onResetCloneTransformClicked(); - -private: - - - void showMenuForCurve(const boost::shared_ptr & curve); - - void showMenuForControlPoint(const boost::shared_ptr & curve, - const std::pair, boost::shared_ptr > & cp); - - - /** - *@brief Moves of the given pixel the selected control points. - * This takes into account the zoom factor. - **/ - void moveSelectedCpsWithKeyArrows(int x, int y); - - void onToolActionTriggeredInternal(QAction* action, bool emitSignal); - - - QAction* createToolAction(QToolButton* toolGroup, - const QIcon & icon, - const QString & text, - const QString & tooltip, - const QKeySequence & shortcut, - RotoGui::RotoToolEnum tool); - struct RotoGuiPrivate; - boost::scoped_ptr _imp; -}; - -NATRON_NAMESPACE_EXIT; - -#endif // ROTOGUI_H diff --git a/Gui/RotoPanel.cpp b/Gui/RotoPanel.cpp index af790f1f81..ef53274ffd 100644 --- a/Gui/RotoPanel.cpp +++ b/Gui/RotoPanel.cpp @@ -44,6 +44,7 @@ CLANG_DIAG_OFF(uninitialized) #include #include #include +#include #include CLANG_DIAG_ON(deprecated) CLANG_DIAG_ON(uninitialized) @@ -70,7 +71,6 @@ CLANG_DIAG_ON(uninitialized) #include "Gui/Menu.h" #include "Gui/NodeGui.h" #include "Gui/NodeSettingsPanel.h" -#include "Gui/RotoUndoCommand.h" #include "Gui/SpinBox.h" #include "Gui/Utils.h" @@ -92,6 +92,175 @@ CLANG_DIAG_ON(uninitialized) NATRON_NAMESPACE_ENTER; + +class RemoveItemsUndoCommand +: public QUndoCommand +{ + struct RemovedItem + { + QTreeWidgetItem* treeItem; + QTreeWidgetItem* parentTreeItem; + boost::shared_ptr parentLayer; + int indexInLayer; + boost::shared_ptr item; + + RemovedItem() + : treeItem(0) + , parentTreeItem(0) + , parentLayer() + , indexInLayer(-1) + , item() + { + } + }; + +public: + + + RemoveItemsUndoCommand(RotoPanel* roto, + const QList & items); + + virtual ~RemoveItemsUndoCommand(); + + virtual void undo() OVERRIDE FINAL; + virtual void redo() OVERRIDE FINAL; + +private: + + RotoPanel* _roto; + std::list _items; +}; + +class AddLayerUndoCommand +: public QUndoCommand +{ +public: + + + AddLayerUndoCommand(RotoPanel* roto); + + virtual ~AddLayerUndoCommand(); + + virtual void undo() OVERRIDE FINAL; + virtual void redo() OVERRIDE FINAL; + +private: + + RotoPanel* _roto; + bool _firstRedoCalled; + boost::shared_ptr _parentLayer; + QTreeWidgetItem* _parentTreeItem; + QTreeWidgetItem* _treeItem; + boost::shared_ptr _layer; + int _indexInParentLayer; +}; + + +class DragItemsUndoCommand +: public QUndoCommand +{ +public: + + struct Item + { + boost::shared_ptr dropped; + boost::shared_ptr oldParentLayer; + int indexInOldLayer; + QTreeWidgetItem* oldParentItem; + }; + + + DragItemsUndoCommand(RotoPanel* roto, + const std::list< boost::shared_ptr > & items); + + virtual ~DragItemsUndoCommand(); + + virtual void undo() OVERRIDE FINAL; + virtual void redo() OVERRIDE FINAL; + +private: + + RotoPanel* _roto; + std::list < Item > _items; +}; + + +/** + * @class This class supports 2 behaviours: + * 1) The user pastes one item upon another. The target's shape and attributes are copied and the + * name is the source's name plus "- copy" at the end. + * 2) The user pastes several items upon a layer in which case the items are copied into that layer and + * the new items name is the same than the original appeneded with "- copy". + * + * Anything else will not do anything and you should not issue a command which will yield an unsupported behaviour + * otherwise you'll create an empty action in the undo/redo stack. + **/ +class PasteItemUndoCommand +: public QUndoCommand +{ +public: + + enum PasteModeEnum + { + ePasteModeCopyToLayer = 0, + ePasteModeCopyToItem + }; + + struct PastedItem + { + QTreeWidgetItem* treeItem; + boost::shared_ptr rotoItem; + boost::shared_ptr itemCopy; + }; + + + PasteItemUndoCommand(RotoPanel* roto, + QTreeWidgetItem* target, + QList source); + + virtual ~PasteItemUndoCommand(); + + virtual void undo() OVERRIDE FINAL; + virtual void redo() OVERRIDE FINAL; + +private: + + RotoPanel* _roto; + PasteModeEnum _mode; + QTreeWidgetItem* _targetTreeItem; + boost::shared_ptr _targetItem; + boost::shared_ptr _oldTargetItem; + std::list < PastedItem > _pastedItems; +}; + + +class DuplicateItemUndoCommand +: public QUndoCommand +{ +public: + + struct DuplicatedItem + { + QTreeWidgetItem* treeItem; + boost::shared_ptr item; + boost::shared_ptr duplicatedItem; + }; + + DuplicateItemUndoCommand(RotoPanel* roto, + QTreeWidgetItem* items); + + virtual ~DuplicateItemUndoCommand(); + + virtual void undo() OVERRIDE FINAL; + virtual void redo() OVERRIDE FINAL; + +private: + + RotoPanel* _roto; + DuplicatedItem _item; +}; + + class TreeWidget : public QTreeWidget { @@ -2339,6 +2508,476 @@ RotoPanel::onOperatorColMinimumSizeChanged(const QSize& size) #endif } + + +////////////////////////////// + + +RemoveItemsUndoCommand::RemoveItemsUndoCommand(RotoPanel* roto, + const QList & items) +: QUndoCommand() +, _roto(roto) +, _items() +{ + for (QList::const_iterator it = items.begin(); it != items.end(); ++it) { + QTreeWidgetItem* parentItem = (*it)->parent(); + bool foundParent = false; + for (QList::const_iterator it2 = items.begin(); it2 != items.end(); ++it2) { + if ( (*it2) == parentItem ) { + foundParent = true; + break; + } + } + if (foundParent) { + //Not necessary to add this item to the list since the parent is going to remove it anyway + continue; + } + RemovedItem r; + r.treeItem = *it; + r.parentTreeItem = parentItem; + r.item = _roto->getRotoItemForTreeItem(r.treeItem); + assert(r.item); + if (r.parentTreeItem) { + r.parentLayer = boost::dynamic_pointer_cast( _roto->getRotoItemForTreeItem(r.parentTreeItem) ); + assert(r.parentLayer); + r.indexInLayer = r.parentLayer->getChildIndex(r.item); + } + _items.push_back(r); + } +} + +RemoveItemsUndoCommand::~RemoveItemsUndoCommand() +{ +} + +void +RemoveItemsUndoCommand::undo() +{ + for (std::list::iterator it = _items.begin(); it != _items.end(); ++it) { + if (it->parentTreeItem) { + it->parentTreeItem->addChild(it->treeItem); + } + _roto->getContext()->addItem(it->parentLayer, it->indexInLayer, it->item, RotoItem::eSelectionReasonSettingsPanel); + + it->treeItem->setHidden(false); + } + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Remove items of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + +void +RemoveItemsUndoCommand::redo() +{ + if ( _items.empty() ) { + return; + } + _roto->clearAndSelectPreviousItem(_items.back().item); + for (std::list::iterator it = _items.begin(); it != _items.end(); ++it) { + _roto->getContext()->removeItem(it->item, RotoItem::eSelectionReasonSettingsPanel); + it->treeItem->setHidden(true); + if ( it->treeItem->isSelected() ) { + it->treeItem->setSelected(false); + } + if (it->parentTreeItem) { + it->parentTreeItem->removeChild(it->treeItem); + } + } + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Remove items of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + +///////////////////////////// + + +AddLayerUndoCommand::AddLayerUndoCommand(RotoPanel* roto) +: QUndoCommand() +, _roto(roto) +, _firstRedoCalled(false) +, _parentLayer() +, _parentTreeItem(0) +, _treeItem(0) +, _layer() +, _indexInParentLayer(-1) +{ +} + +AddLayerUndoCommand::~AddLayerUndoCommand() +{ +} + +void +AddLayerUndoCommand::undo() +{ + _treeItem->setHidden(true); + if (_parentTreeItem) { + _parentTreeItem->removeChild(_treeItem); + } + _roto->getContext()->removeItem(_layer, RotoItem::eSelectionReasonSettingsPanel); + _roto->clearSelection(); + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Add layer to %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + +void +AddLayerUndoCommand::redo() +{ + if (!_firstRedoCalled) { + _layer = _roto->getContext()->addLayer(); + _parentLayer = _layer->getParentLayer(); + _treeItem = _roto->getTreeItemForRotoItem(_layer); + _parentTreeItem = _treeItem->parent(); + if (_parentLayer) { + _indexInParentLayer = _parentLayer->getChildIndex(_layer); + } + } else { + _roto->getContext()->addLayer(_layer); + _treeItem->setHidden(false); + if (_parentLayer) { + _roto->getContext()->addItem(_parentLayer, _indexInParentLayer, _layer, RotoItem::eSelectionReasonSettingsPanel); + _parentTreeItem->addChild(_treeItem); + } + } + _roto->clearSelection(); + _roto->getContext()->select(_layer, RotoItem::eSelectionReasonOther); + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Add layer to %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); + _firstRedoCalled = true; +} + +///////////////////////////////// + +DragItemsUndoCommand::DragItemsUndoCommand(RotoPanel* roto, + const std::list< boost::shared_ptr > & items) +: QUndoCommand() +, _roto(roto) +, _items() +{ + for (std::list< boost::shared_ptr >::const_iterator it = items.begin(); it != items.end(); ++it) { + assert( (*it)->newParentLayer && (*it)->newParentItem && (*it)->insertIndex != -1 ); + Item i; + i.dropped = *it; + i.oldParentItem = (*it)->dropped->parent(); + if (!i.oldParentItem) { + continue; + } + i.oldParentLayer = (*it)->droppedRotoItem->getParentLayer(); + if (i.oldParentLayer) { + i.indexInOldLayer = i.oldParentLayer->getChildIndex( (*it)->droppedRotoItem ); + } else { + i.indexInOldLayer = -1; + } + _items.push_back(i); + } +} + +DragItemsUndoCommand::~DragItemsUndoCommand() +{ +} + +static void +createCustomWidgetRecursively(RotoPanel* panel, + const boost::shared_ptr& item) +{ + const boost::shared_ptr isDrawable = boost::dynamic_pointer_cast(item); + + if (isDrawable) { + panel->makeCustomWidgetsForItem(isDrawable); + } + const boost::shared_ptr isLayer = boost::dynamic_pointer_cast(item); + if (isLayer) { + const std::list > & children = isLayer->getItems(); + for (std::list >::const_iterator it = children.begin(); it != children.end(); ++it) { + createCustomWidgetRecursively( panel, *it ); + } + } +} + +void +DragItemsUndoCommand::undo() +{ + for (std::list::iterator it = _items.begin(); it != _items.end(); ++it) { + assert(it->dropped->newParentItem); + it->dropped->newParentItem->removeChild(it->dropped->dropped); + it->dropped->newParentLayer->removeItem(it->dropped->droppedRotoItem); + if (it->oldParentItem) { + it->oldParentItem->insertChild(it->indexInOldLayer, it->dropped->dropped); + + createCustomWidgetRecursively( _roto, it->dropped->droppedRotoItem ); + + assert(it->oldParentLayer); + it->dropped->droppedRotoItem->setParentLayer(it->oldParentLayer); + _roto->getContext()->addItem(it->oldParentLayer, it->indexInOldLayer, it->dropped->droppedRotoItem, RotoItem::eSelectionReasonSettingsPanel); + } else { + it->dropped->droppedRotoItem->setParentLayer( boost::shared_ptr() ); + } + } + _roto->getContext()->refreshRotoPaintTree(); + _roto->getContext()->evaluateChange(); + + setText( QObject::tr("Re-organize items of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + +void +DragItemsUndoCommand::redo() +{ + for (std::list::iterator it = _items.begin(); it != _items.end(); ++it) { + it->oldParentItem->removeChild(it->dropped->dropped); + if (it->oldParentLayer) { + it->oldParentLayer->removeItem(it->dropped->droppedRotoItem); + } + assert(it->dropped->newParentItem); + it->dropped->newParentItem->insertChild(it->dropped->insertIndex, it->dropped->dropped); + + createCustomWidgetRecursively( _roto, it->dropped->droppedRotoItem ); + + it->dropped->newParentItem->setExpanded(true); + it->dropped->newParentLayer->insertItem(it->dropped->droppedRotoItem, it->dropped->insertIndex); + } + _roto->getContext()->refreshRotoPaintTree(); + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Re-organize items of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + +////////////////////// + +static std::string +getItemCopyName(RotoPanel* roto, + const boost::shared_ptr& originalItem) +{ + int i = 1; + std::string name = originalItem->getScriptName() + "_copy"; + boost::shared_ptr foundItemWithName = roto->getContext()->getItemByName(name); + + while (foundItemWithName && foundItemWithName != originalItem) { + std::stringstream ss; + ss << originalItem->getScriptName() << "_copy " << i; + name = ss.str(); + foundItemWithName = roto->getContext()->getItemByName(name); + ++i; + } + + return name; +} + +static +void +setItemCopyNameRecursive(RotoPanel* panel, + const boost::shared_ptr& item) +{ + item->setScriptName( getItemCopyName(panel, item) ); + boost::shared_ptr isLayer = boost::dynamic_pointer_cast(item); + + if (isLayer) { + for (std::list >::const_iterator it = isLayer->getItems().begin(); it != isLayer->getItems().end(); ++it) { + setItemCopyNameRecursive( panel, *it ); + } + } +} + +PasteItemUndoCommand::PasteItemUndoCommand(RotoPanel* roto, + QTreeWidgetItem* target, + QList source) +: QUndoCommand() +, _roto(roto) +, _mode() +, _targetTreeItem(target) +, _targetItem() +, _pastedItems() +{ + _targetItem = roto->getRotoItemForTreeItem(target); + assert(_targetItem); + + for (int i = 0; i < source.size(); ++i) { + PastedItem item; + item.treeItem = source[i]; + item.rotoItem = roto->getRotoItemForTreeItem(item.treeItem); + assert(item.rotoItem); + _pastedItems.push_back(item); + } + + boost::shared_ptr isDrawable = boost::dynamic_pointer_cast(_targetItem); + + if (isDrawable) { + _mode = ePasteModeCopyToItem; + assert(source.size() == 1 && _pastedItems.size() == 1); + assert( dynamic_cast( _pastedItems.front().rotoItem.get() ) ); + } else { + _mode = ePasteModeCopyToLayer; + for (std::list::iterator it = _pastedItems.begin(); it != _pastedItems.end(); ++it) { + boost::shared_ptr srcBezier = boost::dynamic_pointer_cast(it->rotoItem); + boost::shared_ptr srcLayer = boost::dynamic_pointer_cast(it->rotoItem); + boost::shared_ptr srcStroke = boost::dynamic_pointer_cast(it->rotoItem); + if (srcBezier) { + std::string name = getItemCopyName(roto, it->rotoItem); + boost::shared_ptr copy( new Bezier(srcBezier->getContext(), name, + srcBezier->getParentLayer(), false) ); + copy->clone( srcBezier.get() ); + copy->createNodes(); + //clone overwrittes the script name, don't forget to set it back + copy->setScriptName(name); + copy->setLabel(name); + it->itemCopy = copy; + } else if (srcStroke) { + std::string name = getItemCopyName(roto, it->rotoItem); + boost::shared_ptr copy( new RotoStrokeItem( srcStroke->getBrushType(), + srcStroke->getContext(), + name, + boost::shared_ptr() ) ); + copy->createNodes(); + if ( srcStroke->getParentLayer() ) { + srcStroke->getParentLayer()->insertItem(copy, 0); + } + copy->clone( srcStroke.get() ); + copy->createNodes(); + //clone overwrittes the script name, don't forget to set it back + copy->setScriptName(name); + copy->setLabel(name); + it->itemCopy = copy; + } else { + assert(srcLayer); + boost::shared_ptr copy( new RotoLayer( srcLayer->getContext(), + "", + boost::shared_ptr() ) ); + copy->clone( srcLayer.get() ); + setItemCopyNameRecursive( roto, copy ); + it->itemCopy = copy; + } + } + } +} + +PasteItemUndoCommand::~PasteItemUndoCommand() +{ +} + +void +PasteItemUndoCommand::undo() +{ + if (_mode == ePasteModeCopyToItem) { + assert(_oldTargetItem); + _roto->getContext()->deselect(_targetItem, RotoItem::eSelectionReasonOther); + _targetItem->clone( _oldTargetItem.get() ); + _roto->updateItemGui(_targetTreeItem); + _roto->getContext()->select(_targetItem, RotoItem::eSelectionReasonOther); + } else { + // check that it is a RotoLayer + assert( dynamic_cast( _targetItem.get() ) ); + for (std::list::iterator it = _pastedItems.begin(); it != _pastedItems.end(); ++it) { + _roto->getContext()->removeItem(it->itemCopy, RotoItem::eSelectionReasonOther); + } + } + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Paste item(s) of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + +void +PasteItemUndoCommand::redo() +{ + if (_mode == ePasteModeCopyToItem) { + Bezier* isBezier = dynamic_cast( _targetItem.get() ); + RotoStrokeItem* isStroke = dynamic_cast( _targetItem.get() ); + if (isBezier) { + boost::shared_ptr oldBezier( new Bezier(isBezier->getContext(), isBezier->getScriptName(), isBezier->getParentLayer(), false) ); + oldBezier->createNodes(); + _oldTargetItem = oldBezier; + } else if (isStroke) { + boost::shared_ptr oldStroke( new RotoStrokeItem( isStroke->getBrushType(), isStroke->getContext(), isStroke->getScriptName(), boost::shared_ptr() ) ); + oldStroke->createNodes(); + _oldTargetItem = oldStroke; + if ( isStroke->getParentLayer() ) { + //isStroke->getParentLayer()->insertItem(_oldTargetItem, 0); + } + } + _oldTargetItem->clone( _targetItem.get() ); + assert(_pastedItems.size() == 1); + PastedItem & front = _pastedItems.front(); + ///If we don't deselct the updateItemGUI call will not function correctly because the knobs GUI + ///have not been refreshed and the selected item is linked to those dirty knobs + _roto->getContext()->deselect(_targetItem, RotoItem::eSelectionReasonOther); + _targetItem->clone( front.rotoItem.get() ); + _targetItem->setScriptName( _oldTargetItem->getScriptName() ); + _roto->updateItemGui(_targetTreeItem); + _roto->getContext()->select(_targetItem, RotoItem::eSelectionReasonOther); + } else { + boost::shared_ptr isLayer = boost::dynamic_pointer_cast(_targetItem); + assert(isLayer); + for (std::list::iterator it = _pastedItems.begin(); it != _pastedItems.end(); ++it) { + assert(it->itemCopy); + //it->itemCopy->setParentLayer(isLayer); + _roto->getContext()->addItem(isLayer, 0, it->itemCopy, RotoItem::eSelectionReasonOther); + } + } + + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Paste item(s) of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + +////////////////// + + +DuplicateItemUndoCommand::DuplicateItemUndoCommand(RotoPanel* roto, + QTreeWidgetItem* items) +: QUndoCommand() +, _roto(roto) +, _item() +{ + _item.treeItem = items; + _item.item = _roto->getRotoItemForTreeItem(_item.treeItem); + assert( _item.item->getParentLayer() ); + boost::shared_ptr isBezier = boost::dynamic_pointer_cast( _item.item ); + boost::shared_ptr isStroke = boost::dynamic_pointer_cast( _item.item); + boost::shared_ptr isLayer = boost::dynamic_pointer_cast( _item.item); + if (isBezier) { + std::string name = getItemCopyName(roto, isBezier); + boost::shared_ptr bezierCopy( new Bezier(isBezier->getContext(), name, isBezier->getParentLayer(), false) ); + bezierCopy->createNodes(); + _item.duplicatedItem = bezierCopy; + _item.duplicatedItem->clone( isBezier.get() ); + //clone has overwritten the name + _item.duplicatedItem->setScriptName(name); + _item.duplicatedItem->setLabel(name); + } else if (isStroke) { + std::string name = getItemCopyName(roto, isStroke); + boost::shared_ptr strokeCopy( new RotoStrokeItem( isStroke->getBrushType(), isStroke->getContext(), name, boost::shared_ptr() ) ); + strokeCopy->createNodes(); + _item.duplicatedItem = strokeCopy; + if ( isStroke->getParentLayer() ) { + isStroke->getParentLayer()->insertItem(_item.duplicatedItem, 0); + } + _item.duplicatedItem->clone( isStroke.get() ); + //clone has overwritten the name + _item.duplicatedItem->setScriptName(name); + _item.duplicatedItem->setLabel(name); + } else { + assert(isLayer); + _item.duplicatedItem.reset( new RotoLayer(*isLayer) ); + setItemCopyNameRecursive( roto, _item.duplicatedItem ); + } +} + +DuplicateItemUndoCommand::~DuplicateItemUndoCommand() +{ +} + +void +DuplicateItemUndoCommand::undo() +{ + _roto->getContext()->removeItem(_item.duplicatedItem, RotoItem::eSelectionReasonOther); + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Duplicate item(s) of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + +void +DuplicateItemUndoCommand::redo() +{ + _roto->getContext()->addItem(_item.item->getParentLayer(), + 0, _item.duplicatedItem, RotoItem::eSelectionReasonOther); + + _roto->getContext()->evaluateChange(); + setText( QObject::tr("Duplicate item(s) of %2").arg( QString::fromUtf8( _roto->getNodeName().c_str() ) ) ); +} + + NATRON_NAMESPACE_EXIT; NATRON_NAMESPACE_USING; diff --git a/Gui/TrackerGui.cpp b/Gui/TrackerGui.cpp deleted file mode 100644 index 248b64faea..0000000000 --- a/Gui/TrackerGui.cpp +++ /dev/null @@ -1,4174 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * This file is part of Natron , - * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat - * - * Natron is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Natron is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Natron. If not, see - * ***** END LICENSE BLOCK ***** */ - -// ***** BEGIN PYTHON BLOCK ***** -// from : -// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." -#include -// ***** END PYTHON BLOCK ***** - -#include "TrackerGui.h" - -#include - -CLANG_DIAG_OFF(deprecated) -CLANG_DIAG_OFF(uninitialized) -#include -#include -#include -#include -#include -#include -#include -#include -#include -CLANG_DIAG_ON(deprecated) -CLANG_DIAG_ON(uninitialized) - -#include "Engine/AppInstance.h" -#include "Engine/Curve.h" -#include "Engine/EffectInstance.h" -#include "Engine/KnobTypes.h" -#include "Engine/PyParameter.h" -#include "Engine/Node.h" -#include "Engine/Project.h" -#include "Engine/Image.h" -#include "Engine/OutputSchedulerThread.h" -#include "Engine/Lut.h" -#include "Engine/TimeLine.h" -#include "Engine/Transform.h" -#include "Engine/TrackMarker.h" -#include "Engine/TrackerContext.h" -#include "Engine/ViewerInstance.h" - -#include "Engine/ViewIdx.h" - -#include "Gui/ActionShortcuts.h" -#include "Gui/Button.h" -#include "Gui/GuiApplicationManager.h" -#include "Gui/GuiDefines.h" -#include "Gui/GuiMacros.h" -#include "Gui/Gui.h" -#include "Gui/TrackerUndoCommand.h" -#include "Gui/Texture.h" -#include "Gui/GuiAppInstance.h" -#include "Gui/NodeGui.h" -#include "Gui/PythonPanels.h" -#include "Gui/TrackerPanel.h" -#include "Gui/MultiInstancePanel.h" -#include "Gui/QtEnumConvert.h" -#include "Gui/Utils.h" -#include "Gui/ViewerGL.h" -#include "Gui/ViewerTab.h" - -#define POINT_SIZE 5 -#define CROSS_SIZE 6 -#define POINT_TOLERANCE 6 -#define ADDTRACK_SIZE 5 -#define HANDLE_SIZE 6 - -//Controls how many center keyframes should be displayed before and after the time displayed -#define MAX_CENTER_POINTS_DISPLAYED 50 - -#define SELECTED_MARKER_WINDOW_BASE_WIDTH_SCREEN_PX 200 - -#define SELECTED_MARKER_KEYFRAME_WIDTH_SCREEN_PX 75 -#define MAX_TRACK_KEYS_TO_DISPLAY 10 - -#define CORRELATION_ERROR_MAX_DISPLAY 0.2 - -NATRON_NAMESPACE_ENTER; - - -enum TrackerMouseStateEnum -{ - eMouseStateIdle = 0, - eMouseStateDraggingCenter, - eMouseStateDraggingOffset, - - eMouseStateDraggingInnerTopLeft, - eMouseStateDraggingInnerTopRight, - eMouseStateDraggingInnerBtmLeft, - eMouseStateDraggingInnerBtmRight, - eMouseStateDraggingInnerTopMid, - eMouseStateDraggingInnerMidRight, - eMouseStateDraggingInnerBtmMid, - eMouseStateDraggingInnerMidLeft, - - eMouseStateDraggingOuterTopLeft, - eMouseStateDraggingOuterTopRight, - eMouseStateDraggingOuterBtmLeft, - eMouseStateDraggingOuterBtmRight, - eMouseStateDraggingOuterTopMid, - eMouseStateDraggingOuterMidRight, - eMouseStateDraggingOuterBtmMid, - eMouseStateDraggingOuterMidLeft, - - eMouseStateDraggingSelectedMarkerResizeAnchor, - eMouseStateScalingSelectedMarker, - eMouseStateDraggingSelectedMarker, -}; - -enum TrackerDrawStateEnum -{ - eDrawStateInactive = 0, - eDrawStateHoveringCenter, - - eDrawStateHoveringInnerTopLeft, - eDrawStateHoveringInnerTopRight, - eDrawStateHoveringInnerBtmLeft, - eDrawStateHoveringInnerBtmRight, - eDrawStateHoveringInnerTopMid, - eDrawStateHoveringInnerMidRight, - eDrawStateHoveringInnerBtmMid, - eDrawStateHoveringInnerMidLeft, - - eDrawStateHoveringOuterTopLeft, - eDrawStateHoveringOuterTopRight, - eDrawStateHoveringOuterBtmLeft, - eDrawStateHoveringOuterBtmRight, - eDrawStateHoveringOuterTopMid, - eDrawStateHoveringOuterMidRight, - eDrawStateHoveringOuterBtmMid, - eDrawStateHoveringOuterMidLeft, - - eDrawStateShowScalingHint, -}; - -typedef QFutureWatcher, RectI> > TrackWatcher; -typedef boost::shared_ptr TrackWatcherPtr; - -struct TrackRequestKey -{ - int time; - boost::weak_ptr track; - RectI roi; -}; - -struct TrackRequestKey_compareLess -{ - bool operator()(const TrackRequestKey& lhs, - const TrackRequestKey& rhs) const - { - if (lhs.time < rhs.time) { - return true; - } else if (lhs.time > rhs.time) { - return false; - } else { - TrackMarkerPtr lptr = lhs.track.lock(); - TrackMarkerPtr rptr = rhs.track.lock(); - if ( lptr.get() < rptr.get() ) { - return true; - } else if ( lptr.get() > rptr.get() ) { - return false; - } else { - double la = lhs.roi.area(); - double ra = rhs.roi.area(); - if (la < ra) { - return true; - } else if (la > ra) { - return false; - } else { - return false; - } - } - } - } -}; - -typedef std::map TrackKeyframeRequests; - - -struct TrackerGuiPrivate -{ - TrackerGui* _publicInterface; - boost::shared_ptr panelv1; - TrackerPanel* panel; - ViewerTab* viewer; - QWidget* buttonsBar; - QHBoxLayout* buttonsLayout; - Button* addTrackButton; - Button* trackRangeButton; - Button* trackBwButton; - Button* trackPrevButton; - Button* trackNextButton; - Button* trackFwButton; - Button* trackAllKeyframesButton; - Button* trackCurrentKeyframeButton; - Button* clearAllAnimationButton; - Button* clearBwAnimationButton; - Button* clearFwAnimationButton; - Button* updateViewerButton; - Button* centerViewerButton; - Button* createKeyOnMoveButton; - Button* setKeyFrameButton; - Button* removeKeyFrameButton; - Button* resetOffsetButton; - Button* resetTrackButton; - Button* showCorrelationButton; - bool clickToAddTrackEnabled; - QPointF lastMousePos; - QRectF selectionRectangle; - int controlDown; - int shiftDown; - TrackerMouseStateEnum eventState; - TrackerDrawStateEnum hoverState; - TrackMarkerPtr interactMarker, hoverMarker; - - typedef std::map KeyFrameTexIDs; - typedef std::map, KeyFrameTexIDs> TrackKeysMap; - TrackKeysMap trackTextures; - TrackKeyframeRequests trackRequestsMap; - GLTexturePtr selectedMarkerTexture; - int selectedMarkerTextureTime; - RectI selectedMarkerTextureRoI; - //If theres a single selection, this points to it - boost::weak_ptr selectedMarker; - GLuint pboID; - int selectedMarkerWidth; - TrackWatcherPtr imageGetterWatcher; - bool showMarkerTexture; - RenderScale selectedMarkerScale; - boost::weak_ptr selectedMarkerImg; - bool isTracking; - int lastTrackRangeFirstFrame, lastTrackRangeLastFrame, lastTrackRangeStep; - - TrackerGuiPrivate(TrackerGui* publicInterface, - const boost::shared_ptr & panelv1, - TrackerPanel* panel, - ViewerTab* parent) - : _publicInterface(publicInterface) - , panelv1(panelv1) - , panel(panel) - , viewer(parent) - , buttonsBar(NULL) - , buttonsLayout(NULL) - , addTrackButton(NULL) - , trackRangeButton(0) - , trackBwButton(NULL) - , trackPrevButton(NULL) - , trackNextButton(NULL) - , trackFwButton(NULL) - , trackAllKeyframesButton(0) - , trackCurrentKeyframeButton(0) - , clearAllAnimationButton(NULL) - , clearBwAnimationButton(NULL) - , clearFwAnimationButton(NULL) - , updateViewerButton(NULL) - , centerViewerButton(NULL) - , createKeyOnMoveButton(0) - , setKeyFrameButton(0) - , removeKeyFrameButton(0) - , resetOffsetButton(0) - , resetTrackButton(0) - , showCorrelationButton(0) - , clickToAddTrackEnabled(false) - , lastMousePos() - , selectionRectangle() - , controlDown(0) - , shiftDown(0) - , eventState(eMouseStateIdle) - , hoverState(eDrawStateInactive) - , interactMarker() - , trackTextures() - , trackRequestsMap() - , selectedMarkerTexture() - , selectedMarkerTextureTime(0) - , selectedMarkerTextureRoI() - , selectedMarker() - , pboID(0) - , selectedMarkerWidth(SELECTED_MARKER_WINDOW_BASE_WIDTH_SCREEN_PX) - , imageGetterWatcher() - , showMarkerTexture(false) - , selectedMarkerScale() - , selectedMarkerImg() - , isTracking(false) - , lastTrackRangeFirstFrame(INT_MIN) - , lastTrackRangeLastFrame(INT_MIN) - , lastTrackRangeStep(INT_MIN) - { - glGenBuffers(1, &pboID); - selectedMarkerScale.x = selectedMarkerScale.y = 1.; - } - - ~TrackerGuiPrivate() - { - glDeleteBuffers(1, &pboID); - } - - void transformPattern(double time, TrackerMouseStateEnum state, const Point& delta); - - void refreshSelectedMarkerTexture(); - - void convertImageTosRGBOpenGLTexture(const boost::shared_ptr& image, const boost::shared_ptr& tex, const RectI& renderWindow); - - void makeMarkerKeyTexture(int time, const TrackMarkerPtr& track); - - void drawSelectedMarkerTexture(const std::pair& pixelScale, - int currentTime, - const Point& selectedCenter, - const Point& offset, - const Point& selectedPtnTopLeft, - const Point& selectedPtnTopRight, - const Point& selectedPtnBtmRight, - const Point& selectedPtnBtmLeft, - const Point& selectedSearchWndBtmLeft, - const Point& selectedSearchWndTopRight); - - void drawSelectedMarkerKeyframes(const std::pair& pixelScale, int currentTime); - - ///Filter allkeys so only that only the MAX_TRACK_KEYS_TO_DISPLAY surrounding are displayed - static KeyFrameTexIDs getKeysToRenderForMarker(double currentTime, const KeyFrameTexIDs& allKeys); - - void computeTextureCanonicalRect(const Texture& tex, int xOffset, int texWidthPx, RectD* rect) const; - void computeSelectedMarkerCanonicalRect(RectD* rect) const; - bool isNearbySelectedMarkerTextureResizeAnchor(const QPointF& pos) const; - bool isInsideSelectedMarkerTextureResizeAnchor(const QPointF& pos) const; - - ///Returns the keyframe time if the mouse is inside a keyframe texture - int isInsideKeyFrameTexture(double currentTime, const QPointF& pos, const QPointF& viewportPos) const; -}; - -class DuringOverlayFlag_RAII -{ - EffectInstPtr e; - -public: - - DuringOverlayFlag_RAII(TrackerGuiPrivate* p) - : e() - { - if (p->panel) { - e = p->panel->getNode()->getNode()->getEffectInstance(); - } - if (e) { - e->setDoingInteractAction(true); - } - } - - ~DuringOverlayFlag_RAII() - { - if (e) { - e->setDoingInteractAction(false); - } - } -}; - -#define FLAG_DURING_INTERACT DuringOverlayFlag_RAII __flag_setter__( _imp.get() ); - -TrackerGui::TrackerGui(TrackerPanel* panel, - ViewerTab* parent) - : QObject() - , _imp( new TrackerGuiPrivate(this, boost::shared_ptr(), panel, parent) ) - -{ - createGui(); -} - -TrackerGui::TrackerGui(const boost::shared_ptr & panel, - ViewerTab* parent) - : QObject() - , _imp( new TrackerGuiPrivate(this, panel, 0, parent) ) -{ - createGui(); -} - -void -TrackerGui::createGui() -{ - assert(_imp->viewer); - - QObject::connect( _imp->viewer->getViewer(), SIGNAL(selectionRectangleChanged(bool)), this, SLOT(updateSelectionFromSelectionRectangle(bool)) ); - QObject::connect( _imp->viewer->getViewer(), SIGNAL(selectionCleared()), this, SLOT(onSelectionCleared()) ); - - if (_imp->panelv1) { - QObject::connect( _imp->panelv1.get(), SIGNAL(trackingEnded()), this, SLOT(onTrackingEnded()) ); - } - - _imp->buttonsBar = new QWidget(_imp->viewer); - _imp->buttonsLayout = new QHBoxLayout(_imp->buttonsBar); - _imp->buttonsLayout->setContentsMargins(3, 2, 0, 0); - - - const double iconSizeX = TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE); - QPixmap pixAdd; - - appPTR->getIcon(NATRON_PIXMAP_ADD_TRACK, iconSizeX, &pixAdd); - - const QSize medButtonSize( TO_DPIX(NATRON_MEDIUM_BUTTON_SIZE), TO_DPIY(NATRON_MEDIUM_BUTTON_SIZE) ); - const QSize medButtonIconSize( TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), TO_DPIY(NATRON_MEDIUM_BUTTON_ICON_SIZE) ); - - _imp->addTrackButton = new Button(QIcon(pixAdd), QString(), _imp->buttonsBar); - _imp->addTrackButton->setCheckable(true); - _imp->addTrackButton->setChecked(false); - _imp->addTrackButton->setFixedSize(medButtonSize); - _imp->addTrackButton->setIconSize(medButtonIconSize); - _imp->addTrackButton->setToolTip( GuiUtils::convertFromPlainText(tr("When enabled you can add new tracks " - "by clicking on the Viewer. " - "Holding the Control + Alt keys is the " - "same as pressing this button."), - Qt::WhiteSpaceNormal) ); - - _imp->buttonsLayout->addWidget(_imp->addTrackButton); - QObject::connect( _imp->addTrackButton, SIGNAL(clicked(bool)), this, SLOT(onAddTrackClicked(bool)) ); - QPixmap pixPrev, pixNext, pixClearAll, pixClearBw, pixClearFw, pixUpdateViewerEnabled, pixUpdateViewerDisabled, pixStop; - QPixmap bwEnabled, bwDisabled, fwEnabled, fwDisabled; - QPixmap pixTrackRange, pixTrackKeyframes, pixTrackCurrentKey; - - appPTR->getIcon(NATRON_PIXMAP_TRACK_BACKWARD_OFF, iconSizeX, &bwDisabled); - appPTR->getIcon(NATRON_PIXMAP_TRACK_BACKWARD_ON, iconSizeX, &bwEnabled); - appPTR->getIcon(NATRON_PIXMAP_TRACK_PREVIOUS, iconSizeX, &pixPrev); - appPTR->getIcon(NATRON_PIXMAP_TRACK_NEXT, iconSizeX, &pixNext); - appPTR->getIcon(NATRON_PIXMAP_TRACK_FORWARD_OFF, iconSizeX, &fwDisabled); - appPTR->getIcon(NATRON_PIXMAP_TRACK_FORWARD_ON, iconSizeX, &fwEnabled); - appPTR->getIcon(NATRON_PIXMAP_CLEAR_ALL_ANIMATION, iconSizeX, &pixClearAll); - appPTR->getIcon(NATRON_PIXMAP_CLEAR_BACKWARD_ANIMATION, iconSizeX, &pixClearBw); - appPTR->getIcon(NATRON_PIXMAP_CLEAR_FORWARD_ANIMATION, iconSizeX, &pixClearFw); - appPTR->getIcon(NATRON_PIXMAP_VIEWER_REFRESH_ACTIVE, iconSizeX, &pixUpdateViewerEnabled); - appPTR->getIcon(NATRON_PIXMAP_VIEWER_REFRESH, iconSizeX, &pixUpdateViewerDisabled); - appPTR->getIcon(NATRON_PIXMAP_PLAYER_STOP_ENABLED, iconSizeX, &pixStop); - appPTR->getIcon(NATRON_PIXMAP_TRACK_RANGE, iconSizeX, &pixTrackRange); - appPTR->getIcon(NATRON_PIXMAP_TRACK_ALL_KEYS, iconSizeX, &pixTrackKeyframes); - appPTR->getIcon(NATRON_PIXMAP_TRACK_CURRENT_KEY, iconSizeX, &pixTrackCurrentKey); - - - QIcon bwIcon; - bwIcon.addPixmap(pixStop, QIcon::Normal, QIcon::On); - bwIcon.addPixmap(bwDisabled, QIcon::Normal, QIcon::Off); - - QWidget* trackPlayer = new QWidget(_imp->buttonsBar); - QHBoxLayout* trackPlayerLayout = new QHBoxLayout(trackPlayer); - trackPlayerLayout->setContentsMargins(0, 0, 0, 0); - trackPlayerLayout->setSpacing(0); - - - _imp->trackBwButton = new Button(bwIcon, QString(), _imp->buttonsBar); - _imp->trackBwButton->setFixedSize(medButtonSize); - _imp->trackBwButton->setIconSize(medButtonIconSize); - setTooltipWithShortcut(kShortcutGroupTracking, kShortcutIDActionTrackingBackward, "

" + tr("Track selected tracks backward until left bound of the timeline").toStdString() + "

" + - "

" + tr("Keyboard shortcut").toStdString() + ": %1

", _imp->trackBwButton); - _imp->trackBwButton->setCheckable(true); - _imp->trackBwButton->setChecked(false); - QObject::connect( _imp->trackBwButton, SIGNAL(clicked(bool)), this, SLOT(onTrackBwClicked()) ); - trackPlayerLayout->addWidget(_imp->trackBwButton); - - _imp->trackPrevButton = new Button(QIcon(pixPrev), QString(), _imp->buttonsBar); - _imp->trackPrevButton->setFixedSize(medButtonSize); - _imp->trackPrevButton->setIconSize(medButtonIconSize); - setTooltipWithShortcut(kShortcutGroupTracking, kShortcutIDActionTrackingPrevious, "

" + tr("Track selected tracks on the previous frame").toStdString() + "

" + - "

" + tr("Keyboard shortcut").toStdString() + ": %1

", _imp->trackPrevButton); - QObject::connect( _imp->trackPrevButton, SIGNAL(clicked(bool)), this, SLOT(onTrackPrevClicked()) ); - trackPlayerLayout->addWidget(_imp->trackPrevButton); - - - _imp->trackNextButton = new Button(QIcon(pixNext), QString(), _imp->buttonsBar); - _imp->trackNextButton->setFixedSize(medButtonSize); - _imp->trackNextButton->setIconSize(medButtonIconSize); - setTooltipWithShortcut(kShortcutGroupTracking, kShortcutIDActionTrackingNext, "

" + tr("Track selected tracks on the next frame").toStdString() + "

" + - "

" + tr("Keyboard shortcut").toStdString() + ": %1

", _imp->trackNextButton); - QObject::connect( _imp->trackNextButton, SIGNAL(clicked(bool)), this, SLOT(onTrackNextClicked()) ); - trackPlayerLayout->addWidget(_imp->trackNextButton); - - - QIcon fwIcon; - fwIcon.addPixmap(pixStop, QIcon::Normal, QIcon::On); - fwIcon.addPixmap(fwDisabled, QIcon::Normal, QIcon::Off); - _imp->trackFwButton = new Button(fwIcon, QString(), _imp->buttonsBar); - _imp->trackFwButton->setFixedSize(medButtonSize); - _imp->trackFwButton->setIconSize(medButtonIconSize); - setTooltipWithShortcut(kShortcutGroupTracking, kShortcutIDActionTrackingForward, "

" + tr("Track selected tracks forward until right bound of the timeline").toStdString() + "

" + - "

" + tr("Keyboard shortcut").toStdString() + ": %1

", _imp->trackFwButton); - _imp->trackFwButton->setCheckable(true); - _imp->trackFwButton->setChecked(false); - QObject::connect( _imp->trackFwButton, SIGNAL(clicked(bool)), this, SLOT(onTrackFwClicked()) ); - trackPlayerLayout->addWidget(_imp->trackFwButton); - - trackPlayerLayout->addSpacing( TO_DPIX(5) ); - - _imp->trackRangeButton = new Button(QIcon(pixTrackRange), QString(), _imp->buttonsBar); - _imp->trackRangeButton->setFixedSize(medButtonSize); - _imp->trackRangeButton->setIconSize(medButtonIconSize); - setTooltipWithShortcut(kShortcutGroupTracking, kShortcutIDActionTrackingRange, "

" + tr("Track selected tracks over the range and with the step input by a dialog").toStdString() + "

" + - "

" + tr("Keyboard shortcut").toStdString() + ": %1

", _imp->trackRangeButton); - QObject::connect( _imp->trackRangeButton, SIGNAL(clicked(bool)), this, SLOT(onTrackRangeClicked()) ); - trackPlayerLayout->addWidget(_imp->trackRangeButton); - - trackPlayerLayout->addSpacing( TO_DPIX(5) ); - - _imp->trackAllKeyframesButton = new Button(QIcon(pixTrackKeyframes), QString(), _imp->buttonsBar); - _imp->trackAllKeyframesButton->setFixedSize(medButtonSize); - _imp->trackAllKeyframesButton->setIconSize(medButtonIconSize); - setTooltipWithShortcut(kShortcutGroupTracking, kShortcutIDActionTrackingAllKeyframes, "

" + tr("Track selected tracks across all pattern keyframes").toStdString() + "

" + - "

" + tr("Keyboard shortcut").toStdString() + ": %1

", _imp->trackAllKeyframesButton); - QObject::connect( _imp->trackAllKeyframesButton, SIGNAL(clicked(bool)), this, SLOT(onTrackAllKeyframesClicked()) ); - trackPlayerLayout->addWidget(_imp->trackAllKeyframesButton); - - _imp->buttonsLayout->addWidget(trackPlayer); - - _imp->trackCurrentKeyframeButton = new Button(QIcon(pixTrackCurrentKey), QString(), _imp->buttonsBar); - _imp->trackCurrentKeyframeButton->setFixedSize(medButtonSize); - _imp->trackCurrentKeyframeButton->setIconSize(medButtonIconSize); - setTooltipWithShortcut(kShortcutGroupTracking, kShortcutIDActionTrackingCurrentKeyframes, "

" + tr("Track selected tracks over only the pattern keyframes related to the current track").toStdString() + "

" + - "

" + tr("Keyboard shortcut").toStdString() + ": %1

", _imp->trackCurrentKeyframeButton); - QObject::connect( _imp->trackCurrentKeyframeButton, SIGNAL(clicked(bool)), this, SLOT(onTrackCurrentKeyframeClicked()) ); - trackPlayerLayout->addWidget(_imp->trackCurrentKeyframeButton); - - _imp->buttonsLayout->addWidget(trackPlayer); - - - QWidget* clearAnimationContainer = new QWidget(_imp->buttonsBar); - QHBoxLayout* clearAnimationLayout = new QHBoxLayout(clearAnimationContainer); - clearAnimationLayout->setContentsMargins(0, 0, 0, 0); - clearAnimationLayout->setSpacing(0); - - _imp->clearAllAnimationButton = new Button(QIcon(pixClearAll), QString(), _imp->buttonsBar); - _imp->clearAllAnimationButton->setFixedSize(medButtonSize); - _imp->clearAllAnimationButton->setIconSize(medButtonIconSize); - _imp->clearAllAnimationButton->setToolTip( GuiUtils::convertFromPlainText(tr("Reset animation on the selected tracks."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->clearAllAnimationButton, SIGNAL(clicked(bool)), this, SLOT(onClearAllAnimationClicked()) ); - clearAnimationLayout->addWidget(_imp->clearAllAnimationButton); - - _imp->clearBwAnimationButton = new Button(QIcon(pixClearBw), QString(), _imp->buttonsBar); - _imp->clearBwAnimationButton->setFixedSize(medButtonSize); - _imp->clearBwAnimationButton->setIconSize(medButtonIconSize); - _imp->clearBwAnimationButton->setToolTip( GuiUtils::convertFromPlainText(tr("Reset animation on the selected tracks backward from the current frame."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->clearBwAnimationButton, SIGNAL(clicked(bool)), this, SLOT(onClearBwAnimationClicked()) ); - clearAnimationLayout->addWidget(_imp->clearBwAnimationButton); - - _imp->clearFwAnimationButton = new Button(QIcon(pixClearFw), QString(), _imp->buttonsBar); - _imp->clearFwAnimationButton->setFixedSize(medButtonSize); - _imp->clearFwAnimationButton->setIconSize(medButtonIconSize); - _imp->clearFwAnimationButton->setToolTip( GuiUtils::convertFromPlainText(tr("Reset animation on the semected tracks forward from the current frame."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->clearFwAnimationButton, SIGNAL(clicked(bool)), this, SLOT(onClearFwAnimationClicked()) ); - clearAnimationLayout->addWidget(_imp->clearFwAnimationButton); - - _imp->buttonsLayout->addWidget(clearAnimationContainer); - - - QIcon updateViewerIC; - updateViewerIC.addPixmap(pixUpdateViewerEnabled, QIcon::Normal, QIcon::On); - updateViewerIC.addPixmap(pixUpdateViewerDisabled, QIcon::Normal, QIcon::Off); - _imp->updateViewerButton = new Button(updateViewerIC, QString(), _imp->buttonsBar); - _imp->updateViewerButton->setFixedSize(medButtonSize); - _imp->updateViewerButton->setIconSize(medButtonIconSize); - - - _imp->updateViewerButton->setCheckable(true); - _imp->updateViewerButton->setChecked(true); - _imp->updateViewerButton->setDown(true); - _imp->updateViewerButton->setToolTip( GuiUtils::convertFromPlainText(tr("Update viewer during tracking for each frame instead of just the tracks."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->updateViewerButton, SIGNAL(clicked(bool)), this, SLOT(onUpdateViewerClicked(bool)) ); - _imp->buttonsLayout->addWidget(_imp->updateViewerButton); - - QPixmap centerViewerPix; - appPTR->getIcon(NATRON_PIXMAP_CENTER_VIEWER_ON_TRACK, ¢erViewerPix); - - _imp->centerViewerButton = new Button(QIcon(centerViewerPix), QString(), _imp->buttonsBar); - _imp->centerViewerButton->setFixedSize(medButtonSize); - _imp->centerViewerButton->setIconSize(medButtonIconSize); - _imp->centerViewerButton->setCheckable(true); - _imp->centerViewerButton->setChecked(false); - _imp->centerViewerButton->setDown(false); - _imp->centerViewerButton->setToolTip( GuiUtils::convertFromPlainText(tr("Center the viewer on selected tracks during tracking."), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->centerViewerButton, SIGNAL(clicked(bool)), this, SLOT(onCenterViewerButtonClicked(bool)) ); - _imp->buttonsLayout->addWidget(_imp->centerViewerButton); - - - if (_imp->panel) { - /// This is for v2 only - - boost::shared_ptr context = _imp->panel->getContext(); - boost::shared_ptr timeline = _imp->panel->getNode()->getNode()->getApp()->getTimeLine(); - QObject::connect( timeline.get(), SIGNAL(frameChanged(SequenceTime,int)), this, SLOT(onTimelineTimeChanged(SequenceTime,int)) ); - - assert(context); - QObject::connect( context.get(), SIGNAL(selectionChanged(int)), this, SLOT(onContextSelectionChanged(int)) ); - QObject::connect( context.get(), SIGNAL(keyframeSetOnTrack(TrackMarkerPtr,int)), this, SLOT(onKeyframeSetOnTrack(TrackMarkerPtr,int)) ); - QObject::connect( context.get(), SIGNAL(keyframeRemovedOnTrack(TrackMarkerPtr,int)), this, SLOT(onKeyframeRemovedOnTrack(TrackMarkerPtr,int)) ); - QObject::connect( context.get(), SIGNAL(allKeyframesRemovedOnTrack(TrackMarkerPtr)), this, SLOT(onAllKeyframesRemovedOnTrack(TrackMarkerPtr)) ); - QObject::connect( context.get(), SIGNAL(onNodeInputChanged(int)), this, SLOT(onTrackerInputChanged(int)) ); - QObject::connect( context.get(), SIGNAL(trackingFinished()), this, SLOT(onTrackingEnded()) ); - QObject::connect( context.get(), SIGNAL(trackingStarted(int)), this, SLOT(onTrackingStarted(int)) ); - QPixmap addKeyOnPix, addKeyOffPix; - QIcon addKeyIc; - appPTR->getIcon(NATRON_PIXMAP_CREATE_USER_KEY_ON_MOVE_ON, &addKeyOnPix); - appPTR->getIcon(NATRON_PIXMAP_CREATE_USER_KEY_ON_MOVE_OFF, &addKeyOffPix); - addKeyIc.addPixmap(addKeyOnPix, QIcon::Normal, QIcon::On); - addKeyIc.addPixmap(addKeyOffPix, QIcon::Normal, QIcon::Off); - - _imp->createKeyOnMoveButton = new Button(addKeyIc, QString(), _imp->buttonsBar); - _imp->createKeyOnMoveButton->setFixedSize(medButtonSize); - _imp->createKeyOnMoveButton->setIconSize(medButtonIconSize); - _imp->createKeyOnMoveButton->setToolTip( GuiUtils::convertFromPlainText(tr("When enabled, adjusting a track on the viewer will create a new keyframe"), Qt::WhiteSpaceNormal) ); - _imp->createKeyOnMoveButton->setCheckable(true); - _imp->createKeyOnMoveButton->setChecked(true); - _imp->createKeyOnMoveButton->setDown(true); - QObject::connect( _imp->createKeyOnMoveButton, SIGNAL(clicked(bool)), this, SLOT(onCreateKeyOnMoveButtonClicked(bool)) ); - _imp->buttonsLayout->addWidget(_imp->createKeyOnMoveButton); - - QPixmap showCorrPix, hideCorrPix; - appPTR->getIcon(NATRON_PIXMAP_SHOW_TRACK_ERROR, &showCorrPix); - appPTR->getIcon(NATRON_PIXMAP_HIDE_TRACK_ERROR, &hideCorrPix); - QIcon corrIc; - corrIc.addPixmap(showCorrPix, QIcon::Normal, QIcon::On); - corrIc.addPixmap(hideCorrPix, QIcon::Normal, QIcon::Off); - _imp->showCorrelationButton = new Button(corrIc, QString(), _imp->buttonsBar); - _imp->showCorrelationButton->setFixedSize(medButtonSize); - _imp->showCorrelationButton->setIconSize(medButtonIconSize); - _imp->showCorrelationButton->setToolTip( GuiUtils::convertFromPlainText(tr("When enabled, the correlation score of each tracked frame will be displayed on " - "the viewer, with lower correlations close to green and greater correlations " - "close to red."), Qt::WhiteSpaceNormal) ); - _imp->showCorrelationButton->setCheckable(true); - _imp->showCorrelationButton->setChecked(false); - _imp->showCorrelationButton->setDown(false); - QObject::connect( _imp->showCorrelationButton, SIGNAL(clicked(bool)), this, SLOT(onShowCorrelationButtonClicked(bool)) ); - _imp->buttonsLayout->addWidget(_imp->showCorrelationButton); - - QWidget* keyframeContainer = new QWidget(_imp->buttonsBar); - QHBoxLayout* keyframeLayout = new QHBoxLayout(keyframeContainer); - keyframeLayout->setContentsMargins(0, 0, 0, 0); - keyframeLayout->setSpacing(0); - - QPixmap addKeyPix, removeKeyPix, resetOffsetPix, removeAllUserKeysPix; - appPTR->getIcon(NATRON_PIXMAP_ADD_USER_KEY, &addKeyPix); - appPTR->getIcon(NATRON_PIXMAP_REMOVE_USER_KEY, &removeKeyPix); - appPTR->getIcon(NATRON_PIXMAP_RESET_TRACK_OFFSET, &resetOffsetPix); - appPTR->getIcon(NATRON_PIXMAP_RESET_USER_KEYS, &removeAllUserKeysPix); - - - _imp->setKeyFrameButton = new Button(QIcon(addKeyPix), QString(), keyframeContainer); - _imp->setKeyFrameButton->setFixedSize(medButtonSize); - _imp->setKeyFrameButton->setIconSize(medButtonIconSize); - _imp->setKeyFrameButton->setToolTip( GuiUtils::convertFromPlainText(tr("Set a keyframe for the pattern for the selected tracks"), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->setKeyFrameButton, SIGNAL(clicked(bool)), this, SLOT(onSetKeyframeButtonClicked()) ); - keyframeLayout->addWidget(_imp->setKeyFrameButton); - - _imp->removeKeyFrameButton = new Button(QIcon(removeKeyPix), QString(), keyframeContainer); - _imp->removeKeyFrameButton->setFixedSize(medButtonSize); - _imp->removeKeyFrameButton->setIconSize(medButtonIconSize); - _imp->removeKeyFrameButton->setToolTip( GuiUtils::convertFromPlainText(tr("Remove a keyframe for the pattern for the selected tracks"), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->removeKeyFrameButton, SIGNAL(clicked(bool)), this, SLOT(onRemoveKeyframeButtonClicked()) ); - keyframeLayout->addWidget(_imp->removeKeyFrameButton); - - - _imp->buttonsLayout->addWidget(keyframeContainer); - - QPixmap resetPix; - appPTR->getIcon(NATRON_PIXMAP_RESTORE_DEFAULTS_ENABLED, &resetPix); - _imp->resetOffsetButton = new Button(QIcon(resetOffsetPix), QString(), _imp->buttonsBar); - _imp->resetOffsetButton->setFixedSize(medButtonSize); - _imp->resetOffsetButton->setIconSize(medButtonIconSize); - _imp->resetOffsetButton->setToolTip( GuiUtils::convertFromPlainText(tr("Resets the offset for the selected tracks"), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->resetOffsetButton, SIGNAL(clicked(bool)), this, SLOT(onResetOffsetButtonClicked()) ); - _imp->buttonsLayout->addWidget(_imp->resetOffsetButton); - - _imp->resetTrackButton = new Button(QIcon(resetPix), QString(), _imp->buttonsBar); - _imp->resetTrackButton->setFixedSize(medButtonSize); - _imp->resetTrackButton->setIconSize(medButtonIconSize); - _imp->resetTrackButton->setToolTip( GuiUtils::convertFromPlainText(tr("Reset pattern search window and track animation for the selected tracks"), Qt::WhiteSpaceNormal) ); - QObject::connect( _imp->resetTrackButton, SIGNAL(clicked(bool)), this, SLOT(onResetTrackButtonClicked()) ); - _imp->buttonsLayout->addWidget(_imp->resetTrackButton); - } - - _imp->buttonsLayout->addStretch(); - - if (_imp->panel) { - QObject::connect( _imp->panel->getNode()->getNode().get(), SIGNAL(s_refreshPreviewsAfterProjectLoadRequested()), this, SLOT(rebuildMarkerTextures()) ); - } - - - ///Refresh track parameters according to buttons state - if (_imp->panelv1) { - _imp->panelv1->setUpdateViewer( _imp->updateViewerButton->isDown() ); - _imp->panelv1->setCenterOnTrack( _imp->centerViewerButton->isDown() ); - } else if (_imp->panel) { - _imp->panel->getContext()->setUpdateViewer( _imp->updateViewerButton->isDown() ); - _imp->panel->getContext()->setCenterOnTrack( _imp->centerViewerButton->isDown() ); - } -} // TrackerGui::createGui - -TrackerGui::~TrackerGui() -{ -} - -void -TrackerGui::rebuildMarkerTextures() -{ - ///Refreh textures for all markers - std::list markers; - - _imp->panel->getContext()->getSelectedMarkers(&markers); - for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { - std::set keys; - (*it)->getUserKeyframes(&keys); - for (std::set::iterator it2 = keys.begin(); it2 != keys.end(); ++it2) { - _imp->makeMarkerKeyTexture(*it2, *it); - } - } - onContextSelectionChanged(TrackerContext::eTrackSelectionInternal); -} - -QWidget* -TrackerGui::getButtonsBar() const -{ - return _imp->buttonsBar; -} - -void -TrackerGui::onAddTrackClicked(bool clicked) -{ - _imp->clickToAddTrackEnabled = !_imp->clickToAddTrackEnabled; - _imp->addTrackButton->setDown(clicked); - _imp->addTrackButton->setChecked(clicked); - _imp->viewer->getViewer()->redraw(); -} - -static QPointF -computeMidPointExtent(const QPointF& prev, - const QPointF& next, - const QPointF& point, - const QPointF& handleSize) -{ - Point leftDeriv, rightDeriv; - - leftDeriv.x = prev.x() - point.x(); - leftDeriv.y = prev.y() - point.y(); - - rightDeriv.x = next.x() - point.x(); - rightDeriv.y = next.y() - point.y(); - double derivNorm = std::sqrt( (rightDeriv.x - leftDeriv.x) * (rightDeriv.x - leftDeriv.x) + (rightDeriv.y - leftDeriv.y) * (rightDeriv.y - leftDeriv.y) ); - QPointF ret; - if (derivNorm == 0) { - double norm = std::sqrt( ( leftDeriv.x - point.x() ) * ( leftDeriv.x - point.x() ) + ( leftDeriv.y - point.y() ) * ( leftDeriv.y - point.y() ) ); - if (norm != 0) { - ret.rx() = point.x() + ( ( leftDeriv.y - point.y() ) / norm ) * handleSize.x(); - ret.ry() = point.y() - ( ( leftDeriv.x - point.x() ) / norm ) * handleSize.y(); - - return ret; - } else { - return QPointF(0, 0); - } - } else { - ret.rx() = point.x() + ( (rightDeriv.y - leftDeriv.y) / derivNorm ) * handleSize.x(); - ret.ry() = point.y() - ( (rightDeriv.x - leftDeriv.x) / derivNorm ) * handleSize.y(); - } - - return ret; -} - -void -TrackerGui::drawOverlays(double time, - const RenderScale & renderScale, - ViewIdx view) const -{ - FLAG_DURING_INTERACT - - double pixelScaleX, pixelScaleY; - ViewerGL* viewer = _imp->viewer->getViewer(); - - viewer->getPixelScale(pixelScaleX, pixelScaleY); - double viewportSize[2]; - viewer->getViewportSize(viewportSize[0], viewportSize[1]); - - { - GLProtectAttrib a(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_POINT_BIT | GL_ENABLE_BIT | GL_HINT_BIT | GL_TRANSFORM_BIT); - - if (_imp->panelv1) { - ///For each instance: - const std::list > & instances = _imp->panelv1->getInstances(); - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - boost::shared_ptr instance = it->first.lock(); - - if ( instance->isNodeDisabled() ) { - continue; - } - if (it->second) { - ///The track is selected, use the plug-ins interact - EffectInstPtr effect = instance->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); - effect->drawOverlay_public(time, renderScale, view); - } else { - ///Draw a custom interact, indicating the track isn't selected - boost::shared_ptr newInstanceKnob = instance->getKnobByName("center"); - assert(newInstanceKnob); //< if it crashes here that means the parameter's name changed in the OpenFX plug-in. - KnobDouble* dblKnob = dynamic_cast( newInstanceKnob.get() ); - assert(dblKnob); - - //GLProtectMatrix p(GL_PROJECTION); // useless (we do two glTranslate in opposite directions) - for (int l = 0; l < 2; ++l) { - // shadow (uses GL_PROJECTION) - glMatrixMode(GL_PROJECTION); - int direction = (l == 0) ? 1 : -1; - // translate (1,-1) pixels - glTranslated(direction * pixelScaleX / 256, -direction * pixelScaleY / 256, 0); - glMatrixMode(GL_MODELVIEW); - - if (l == 0) { - glColor4d(0., 0., 0., 1.); - } else { - glColor4f(1., 1., 1., 1.); - } - - double x = dblKnob->getValue(0); - double y = dblKnob->getValue(1); - glPointSize(POINT_SIZE); - glBegin(GL_POINTS); - glVertex2d(x, y); - glEnd(); - - glBegin(GL_LINES); - glVertex2d(x - CROSS_SIZE * pixelScaleX, y); - glVertex2d(x + CROSS_SIZE * pixelScaleX, y); - - glVertex2d(x, y - CROSS_SIZE * pixelScaleY); - glVertex2d(x, y + CROSS_SIZE * pixelScaleY); - glEnd(); - } - glPointSize(1.); - } - } - } // if (_imp->panelv1) { - else { - assert(_imp->panel); - double markerColor[3]; - if ( !_imp->panel->getNode()->getOverlayColor(&markerColor[0], &markerColor[1], &markerColor[2]) ) { - markerColor[0] = markerColor[1] = markerColor[2] = 0.8; - } - - std::vector allMarkers; - std::list selectedMarkers; - boost::shared_ptr context = _imp->panel->getContext(); - context->getSelectedMarkers(&selectedMarkers); - context->getAllMarkers(&allMarkers); - - bool showErrorColor = _imp->showCorrelationButton->isChecked(); - TrackMarkerPtr selectedMarker = _imp->selectedMarker.lock(); - Point selectedCenter, selectedPtnTopLeft, selectedPtnTopRight, selectedPtnBtmRight, selectedPtnBtmLeft, selectedOffset, selectedSearchBtmLeft, selectedSearchTopRight; - - for (std::vector::iterator it = allMarkers.begin(); it != allMarkers.end(); ++it) { - if ( !(*it)->isEnabled( (*it)->getCurrentTime() ) ) { - continue; - } - - bool isHoverMarker = *it == _imp->hoverMarker; - bool isDraggedMarker = *it == _imp->interactMarker; - bool isHoverOrDraggedMarker = isHoverMarker || isDraggedMarker; - std::list::iterator foundSelected = std::find(selectedMarkers.begin(), selectedMarkers.end(), *it); - bool isSelected = foundSelected != selectedMarkers.end(); - boost::shared_ptr centerKnob = (*it)->getCenterKnob(); - boost::shared_ptr offsetKnob = (*it)->getOffsetKnob(); - boost::shared_ptr errorKnob = (*it)->getErrorKnob(); - boost::shared_ptr ptnTopLeft = (*it)->getPatternTopLeftKnob(); - boost::shared_ptr ptnTopRight = (*it)->getPatternTopRightKnob(); - boost::shared_ptr ptnBtmRight = (*it)->getPatternBtmRightKnob(); - boost::shared_ptr ptnBtmLeft = (*it)->getPatternBtmLeftKnob(); - boost::shared_ptr searchWndBtmLeft = (*it)->getSearchWindowBottomLeftKnob(); - boost::shared_ptr searchWndTopRight = (*it)->getSearchWindowTopRightKnob(); - - if (!isSelected) { - ///Draw a custom interact, indicating the track isn't selected - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glLineWidth(1.5f); - - for (int l = 0; l < 2; ++l) { - // shadow (uses GL_PROJECTION) - glMatrixMode(GL_PROJECTION); - int direction = (l == 0) ? 1 : -1; - // translate (1,-1) pixels - glTranslated(direction * pixelScaleX / 256, -direction * pixelScaleY / 256, 0); - glMatrixMode(GL_MODELVIEW); - - if (l == 0) { - glColor4d(0., 0., 0., 1.); - } else { - glColor4f(markerColor[0], markerColor[1], markerColor[2], 1.); - } - - - double x = centerKnob->getValueAtTime(time, 0); - double y = centerKnob->getValueAtTime(time, 1); - - glPointSize(POINT_SIZE); - glBegin(GL_POINTS); - glVertex2d(x, y); - glEnd(); - - glBegin(GL_LINES); - glVertex2d(x - CROSS_SIZE * pixelScaleX, y); - glVertex2d(x + CROSS_SIZE * pixelScaleX, y); - - - glVertex2d(x, y - CROSS_SIZE * pixelScaleY); - glVertex2d(x, y + CROSS_SIZE * pixelScaleY); - glEnd(); - } - glPointSize(1.); - } else { // if (isSelected) { - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - GLdouble projection[16]; - glGetDoublev( GL_PROJECTION_MATRIX, projection); - OfxPointD shadow; // how much to translate GL_PROJECTION to get exactly one pixel on screen - shadow.x = 2. / (projection[0] * viewportSize[0]); - shadow.y = 2. / (projection[5] * viewportSize[1]); - - const QPointF center( centerKnob->getValueAtTime(time, 0), - centerKnob->getValueAtTime(time, 1) ); - const QPointF offset( offsetKnob->getValueAtTime(time, 0), - offsetKnob->getValueAtTime(time, 1) ); - const QPointF topLeft( ptnTopLeft->getValueAtTime(time, 0) + offset.x() + center.x(), - ptnTopLeft->getValueAtTime(time, 1) + offset.y() + center.y() ); - const QPointF topRight( ptnTopRight->getValueAtTime(time, 0) + offset.x() + center.x(), - ptnTopRight->getValueAtTime(time, 1) + offset.y() + center.y() ); - const QPointF btmRight( ptnBtmRight->getValueAtTime(time, 0) + offset.x() + center.x(), - ptnBtmRight->getValueAtTime(time, 1) + offset.y() + center.y() ); - const QPointF btmLeft( ptnBtmLeft->getValueAtTime(time, 0) + offset.x() + center.x(), - ptnBtmLeft->getValueAtTime(time, 1) + offset.y() + center.y() ); - const double searchLeft = searchWndBtmLeft->getValueAtTime(time, 0) + offset.x() + center.x(); - const double searchBottom = searchWndBtmLeft->getValueAtTime(time, 1) + offset.y() + center.y(); - const double searchRight = searchWndTopRight->getValueAtTime(time, 0) + offset.x() + center.x(); - const double searchTop = searchWndTopRight->getValueAtTime(time, 1) + offset.y() + center.y(); - const double searchMidX = (searchLeft + searchRight) / 2; - const double searchMidY = (searchTop + searchBottom) / 2; - - if (selectedMarker == *it) { - selectedCenter.x = center.x(); - selectedCenter.y = center.y(); - selectedOffset.x = offset.x(); - selectedOffset.y = offset.y(); - selectedPtnBtmLeft.x = btmLeft.x(); - selectedPtnBtmLeft.y = btmLeft.y(); - selectedPtnBtmRight.x = btmRight.x(); - selectedPtnBtmRight.y = btmRight.y(); - selectedPtnTopRight.x = topRight.x(); - selectedPtnTopRight.y = topRight.y(); - selectedPtnTopLeft.x = topLeft.x(); - selectedPtnTopLeft.y = topLeft.y(); - selectedSearchBtmLeft.x = searchLeft; - selectedSearchBtmLeft.y = searchBottom; - - selectedSearchTopRight.x = searchRight; - selectedSearchTopRight.y = searchTop; - } - - const QPointF innerMidLeft( (btmLeft + topLeft) / 2 ); - const QPointF innerMidTop( (topLeft + topRight) / 2 ); - const QPointF innerMidRight( (btmRight + topRight) / 2 ); - const QPointF innerMidBtm( (btmLeft + btmRight) / 2 ); - const QPointF outterMidLeft(searchLeft, searchMidY); - const QPointF outterMidTop(searchMidX, searchTop); - const QPointF outterMidRight(searchRight, searchMidY); - const QPointF outterMidBtm(searchMidX, searchBottom); - const QPointF handleSize( HANDLE_SIZE * pixelScaleX, handleSize.x() ); - const QPointF innerMidLeftExt = computeMidPointExtent(topLeft, btmLeft, innerMidLeft, handleSize); - const QPointF innerMidRightExt = computeMidPointExtent(btmRight, topRight, innerMidRight, handleSize); - const QPointF innerMidTopExt = computeMidPointExtent(topRight, topLeft, innerMidTop, handleSize); - const QPointF innerMidBtmExt = computeMidPointExtent(btmLeft, btmRight, innerMidBtm, handleSize); - const QPointF searchTopLeft(searchLeft, searchTop); - const QPointF searchTopRight(searchRight, searchTop); - const QPointF searchBtmRight(searchRight, searchBottom); - const QPointF searchBtmLeft(searchLeft, searchBottom); - const QPointF searchTopMid(searchMidX, searchTop); - const QPointF searchRightMid(searchRight, searchMidY); - const QPointF searchLeftMid(searchLeft, searchMidY); - const QPointF searchBtmMid(searchMidX, searchBottom); - const QPointF outterMidLeftExt = computeMidPointExtent(searchTopLeft, searchBtmLeft, outterMidLeft, handleSize); - const QPointF outterMidRightExt = computeMidPointExtent(searchBtmRight, searchTopRight, outterMidRight, handleSize); - const QPointF outterMidTopExt = computeMidPointExtent(searchTopRight, searchTopLeft, outterMidTop, handleSize); - const QPointF outterMidBtmExt = computeMidPointExtent(searchBtmLeft, searchBtmRight, outterMidBtm, handleSize); - std::string name = (*it)->getLabel(); - std::map > centerPoints; - int floorTime = std::floor(time + 0.5); - boost::shared_ptr xCurve = centerKnob->getCurve(ViewSpec::current(), 0); - boost::shared_ptr yCurve = centerKnob->getCurve(ViewSpec::current(), 1); - boost::shared_ptr errorCurve = errorKnob->getCurve(ViewSpec::current(), 0); - - for (int i = 0; i < MAX_CENTER_POINTS_DISPLAYED / 2; ++i) { - KeyFrame k; - int keyTimes[2] = {floorTime + i, floorTime - i}; - - for (int j = 0; j < 2; ++j) { - if ( xCurve->getKeyFrameWithTime(keyTimes[j], &k) ) { - std::pair& p = centerPoints[k.getTime()]; - p.first.x = k.getValue(); - p.first.y = INT_MIN; - - if ( yCurve->getKeyFrameWithTime(keyTimes[j], &k) ) { - p.first.y = k.getValue(); - } - if ( showErrorColor && errorCurve->getKeyFrameWithTime(keyTimes[j], &k) ) { - p.second = k.getValue(); - } - } - } - } - - - for (int l = 0; l < 2; ++l) { - // shadow (uses GL_PROJECTION) - glMatrixMode(GL_PROJECTION); - int direction = (l == 0) ? 1 : -1; - // translate (1,-1) pixels - glTranslated(direction * shadow.x, -direction * shadow.y, 0); - glMatrixMode(GL_MODELVIEW); - - ///Draw center position - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glBegin(GL_LINE_STRIP); - glColor3f(0.5 * l, 0.5 * l, 0.5 * l); - for (std::map >::iterator it = centerPoints.begin(); it != centerPoints.end(); ++it) { - glVertex2d(it->second.first.x, it->second.first.y); - } - glEnd(); - glDisable(GL_LINE_SMOOTH); - - glEnable(GL_POINT_SMOOTH); - glBegin(GL_POINTS); - if (!showErrorColor) { - glColor3f(0.5 * l, 0.5 * l, 0.5 * l); - } - - for (std::map >::iterator it2 = centerPoints.begin(); it2 != centerPoints.end(); ++it2) { - if (showErrorColor) { - if (l != 0) { - /* - Clamp the correlation to [CORRELATION_ERROR_MIN, 1] then - Map the correlation to [0, 0.33] since 0 is Red for HSV and 0.33 is Green. - Also clamp to the interval if the correlation is higher, and reverse. - */ - - double error = std::min(std::max(it2->second.second, 0.), CORRELATION_ERROR_MAX_DISPLAY); - double mappedError = 0.33 - 0.33 * error / CORRELATION_ERROR_MAX_DISPLAY; - QColor c; - c.setHsvF(mappedError, 1., 1.); - glColor3f( c.redF(), c.greenF(), c.blueF() ); - } else { - glColor3f(0., 0., 0.); - } - } - glVertex2d(it2->second.first.x, it2->second.first.y); - } - glEnd(); - glDisable(GL_POINT_SMOOTH); - - - - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - glBegin(GL_LINE_LOOP); - glVertex2d( topLeft.x(), topLeft.y() ); - glVertex2d( topRight.x(), topRight.y() ); - glVertex2d( btmRight.x(), btmRight.y() ); - glVertex2d( btmLeft.x(), btmLeft.y() ); - glEnd(); - - glBegin(GL_LINE_LOOP); - glVertex2d( searchTopLeft.x(), searchTopLeft.y() ); - glVertex2d( searchTopRight.x(), searchTopRight.y() ); - glVertex2d( searchBtmRight.x(), searchBtmRight.y() ); - glVertex2d( searchBtmLeft.x(), searchBtmRight.y() ); - glEnd(); - - glPointSize(POINT_SIZE); - glBegin(GL_POINTS); - - ///draw center - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringCenter) || (_imp->eventState == eMouseStateDraggingCenter) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( center.x(), center.y() ); - - if ( (offset.x() != 0) || (offset.y() != 0) ) { - glVertex2d( center.x() + offset.x(), center.y() + offset.y() ); - } - - //////DRAWING INNER POINTS - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerBtmLeft) || (_imp->eventState == eMouseStateDraggingInnerBtmLeft) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( btmLeft.x(), btmLeft.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerBtmMid) || (_imp->eventState == eMouseStateDraggingInnerBtmMid) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( innerMidBtm.x(), innerMidBtm.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerBtmRight) || (_imp->eventState == eMouseStateDraggingInnerBtmRight) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( btmRight.x(), btmRight.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerMidLeft) || (_imp->eventState == eMouseStateDraggingInnerMidLeft) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( innerMidLeft.x(), innerMidLeft.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerMidRight) || (_imp->eventState == eMouseStateDraggingInnerMidRight) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( innerMidRight.x(), innerMidRight.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerTopLeft) || (_imp->eventState == eMouseStateDraggingInnerTopLeft) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( topLeft.x(), topLeft.y() ); - } - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerTopMid) || (_imp->eventState == eMouseStateDraggingInnerTopMid) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( innerMidTop.x(), innerMidTop.y() ); - } - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerTopRight) || (_imp->eventState == eMouseStateDraggingInnerTopRight) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( topRight.x(), topRight.y() ); - } - - - //////DRAWING OUTTER POINTS - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterBtmLeft) || (_imp->eventState == eMouseStateDraggingOuterBtmLeft) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( searchBtmLeft.x(), searchBtmLeft.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterBtmMid) || (_imp->eventState == eMouseStateDraggingOuterBtmMid) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( outterMidBtm.x(), outterMidBtm.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterBtmRight) || (_imp->eventState == eMouseStateDraggingOuterBtmRight) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( searchBtmRight.x(), searchBtmRight.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterMidLeft) || (_imp->eventState == eMouseStateDraggingOuterMidLeft) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( outterMidLeft.x(), outterMidLeft.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterMidRight) || (_imp->eventState == eMouseStateDraggingOuterMidRight) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( outterMidRight.x(), outterMidRight.y() ); - } - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterTopLeft) || (_imp->eventState == eMouseStateDraggingOuterTopLeft) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( searchTopLeft.x(), searchTopLeft.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterTopMid) || (_imp->eventState == eMouseStateDraggingOuterTopMid) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( outterMidTop.x(), outterMidTop.y() ); - } - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterTopRight) || (_imp->eventState == eMouseStateDraggingOuterTopRight) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - glVertex2d( searchTopRight.x(), searchTopRight.y() ); - } - - glEnd(); - - if ( (offset.x() != 0) || (offset.y() != 0) ) { - glBegin(GL_LINES); - glColor3f( (float)markerColor[0] * l * 0.5, (float)markerColor[1] * l * 0.5, (float)markerColor[2] * l * 0.5 ); - glVertex2d( center.x(), center.y() ); - glVertex2d( center.x() + offset.x(), center.y() + offset.y() ); - glEnd(); - } - - ///now show small lines at handle positions - glBegin(GL_LINES); - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerMidLeft) || (_imp->eventState == eMouseStateDraggingInnerMidLeft) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( innerMidLeft.x(), innerMidLeft.y() ); - glVertex2d( innerMidLeftExt.x(), innerMidLeftExt.y() ); - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerTopMid) || (_imp->eventState == eMouseStateDraggingInnerTopMid) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( innerMidTop.x(), innerMidTop.y() ); - glVertex2d( innerMidTopExt.x(), innerMidTopExt.y() ); - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerMidRight) || (_imp->eventState == eMouseStateDraggingInnerMidRight) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( innerMidRight.x(), innerMidRight.y() ); - glVertex2d( innerMidRightExt.x(), innerMidRightExt.y() ); - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringInnerBtmMid) || (_imp->eventState == eMouseStateDraggingInnerBtmMid) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( innerMidBtm.x(), innerMidBtm.y() ); - glVertex2d( innerMidBtmExt.x(), innerMidBtmExt.y() ); - - //////DRAWING OUTTER HANDLES - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterMidLeft) || (_imp->eventState == eMouseStateDraggingOuterMidLeft) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( outterMidLeft.x(), outterMidLeft.y() ); - glVertex2d( outterMidLeftExt.x(), outterMidLeftExt.y() ); - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterTopMid) || (_imp->eventState == eMouseStateDraggingOuterTopMid) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( outterMidTop.x(), outterMidTop.y() ); - glVertex2d( outterMidTopExt.x(), outterMidTopExt.y() ); - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterMidRight) || (_imp->eventState == eMouseStateDraggingOuterMidRight) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( outterMidRight.x(), outterMidRight.y() ); - glVertex2d( outterMidRightExt.x(), outterMidRightExt.y() ); - - if ( isHoverOrDraggedMarker && ( (_imp->hoverState == eDrawStateHoveringOuterBtmMid) || (_imp->eventState == eMouseStateDraggingOuterBtmMid) ) ) { - glColor3f(0.f * l, 1.f * l, 0.f * l); - } else { - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - } - glVertex2d( outterMidBtm.x(), outterMidBtm.y() ); - glVertex2d( outterMidBtmExt.x(), outterMidBtmExt.y() ); - glEnd(); - - glColor3f( (float)markerColor[0] * l, (float)markerColor[1] * l, (float)markerColor[2] * l ); - - QColor c; - c.setRgbF(markerColor[0], markerColor[1], markerColor[2]); - viewer->renderText( center.x(), center.y(), QString::fromUtf8( name.c_str() ), c, viewer->font() ); - } // for (int l = 0; l < 2; ++l) { - } // if (!isSelected) { - } // for (std::vector::iterator it = allMarkers.begin(); it!=allMarkers.end(); ++it) { - - if (_imp->showMarkerTexture) { - _imp->drawSelectedMarkerTexture(std::make_pair(pixelScaleX, pixelScaleY), _imp->selectedMarkerTextureTime, selectedCenter, selectedOffset, selectedPtnTopLeft, selectedPtnTopRight, selectedPtnBtmRight, selectedPtnBtmLeft, selectedSearchBtmLeft, selectedSearchTopRight); - } - _imp->panel->getContext()->drawInternalNodesOverlay( time, renderScale, view, _imp->viewer->getViewer() ); - } // // if (_imp->panelv1) { - - - if (_imp->clickToAddTrackEnabled) { - ///draw a square of 20px around the mouse cursor - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glLineWidth(1.5); - //GLProtectMatrix p(GL_PROJECTION); // useless (we do two glTranslate in opposite directions) - - const double addTrackSize = TO_DPIX(ADDTRACK_SIZE); - - for (int l = 0; l < 2; ++l) { - // shadow (uses GL_PROJECTION) - glMatrixMode(GL_PROJECTION); - int direction = (l == 0) ? 1 : -1; - // translate (1,-1) pixels - glTranslated(direction * pixelScaleX / 256, -direction * pixelScaleY / 256, 0); - glMatrixMode(GL_MODELVIEW); - - if (l == 0) { - glColor4d(0., 0., 0., 0.8); - } else { - glColor4d(0., 1., 0., 0.8); - } - - glBegin(GL_LINE_LOOP); - glVertex2d(_imp->lastMousePos.x() - addTrackSize * 2 * pixelScaleX, _imp->lastMousePos.y() - addTrackSize * 2 * pixelScaleY); - glVertex2d(_imp->lastMousePos.x() - addTrackSize * 2 * pixelScaleX, _imp->lastMousePos.y() + addTrackSize * 2 * pixelScaleY); - glVertex2d(_imp->lastMousePos.x() + addTrackSize * 2 * pixelScaleX, _imp->lastMousePos.y() + addTrackSize * 2 * pixelScaleY); - glVertex2d(_imp->lastMousePos.x() + addTrackSize * 2 * pixelScaleX, _imp->lastMousePos.y() - addTrackSize * 2 * pixelScaleY); - glEnd(); - - ///draw a cross at the cursor position - glBegin(GL_LINES); - glVertex2d( _imp->lastMousePos.x() - addTrackSize * pixelScaleX, _imp->lastMousePos.y() ); - glVertex2d( _imp->lastMousePos.x() + addTrackSize * pixelScaleX, _imp->lastMousePos.y() ); - glVertex2d(_imp->lastMousePos.x(), _imp->lastMousePos.y() - addTrackSize * pixelScaleY); - glVertex2d(_imp->lastMousePos.x(), _imp->lastMousePos.y() + addTrackSize * pixelScaleY); - glEnd(); - } - } - } // GLProtectAttrib a(GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT | GL_LINE_BIT | GL_ENABLE_BIT | GL_HINT_BIT); -} // drawOverlays - -int -TrackerGuiPrivate::isInsideKeyFrameTexture(double currentTime, - const QPointF& pos, - const QPointF& viewportPos) const -{ - if (!showMarkerTexture) { - return INT_MAX; - } - - - RectD textureRectCanonical; - if (selectedMarkerTexture) { - computeSelectedMarkerCanonicalRect(&textureRectCanonical); - } - - if ( (pos.y() < textureRectCanonical.y1) || (pos.y() > textureRectCanonical.y2) ) { - return INT_MAX; - } - if (pos.x() < textureRectCanonical.x2) { - return INT_MAX; - } - - TrackMarkerPtr marker = selectedMarker.lock(); - if (!marker) { - return INT_MAX; - } - - //Find out which keyframe it is by counting keyframe portions - int xRightMainTexture = viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x2, 0) ).x(); - const double keyWidthpx = TO_DPIX(SELECTED_MARKER_KEYFRAME_WIDTH_SCREEN_PX); - double indexF = (viewportPos.x() - xRightMainTexture) / keyWidthpx; - int texIndex = (int)std::floor(indexF); - - for (TrackKeysMap::const_iterator it = trackTextures.begin(); it != trackTextures.end(); ++it) { - if (it->first.lock() == marker) { - if ( it->second.empty() ) { - break; - } - ///Render at most MAX_TRACK_KEYS_TO_DISPLAY keyframes - KeyFrameTexIDs keysToRender = getKeysToRenderForMarker(currentTime, it->second); - if ( (texIndex < 0) || ( texIndex >= (int)keysToRender.size() ) ) { - return INT_MAX; - } - KeyFrameTexIDs::iterator found = keysToRender.begin(); - std::advance(found, texIndex); - RectD texCanonicalRect; - computeTextureCanonicalRect(*found->second, indexF * keyWidthpx + xRightMainTexture, - keyWidthpx, &texCanonicalRect); - - if ( (pos.y() >= texCanonicalRect.y1) && (pos.y() < texCanonicalRect.y2) ) { - return found->first; - } - break; - } - } - - return INT_MAX; -} // TrackerGuiPrivate::isInsideKeyFrameTexture - -bool -TrackerGuiPrivate::isNearbySelectedMarkerTextureResizeAnchor(const QPointF& pos) const -{ - RectD textureRectCanonical; - - computeSelectedMarkerCanonicalRect(&textureRectCanonical); - - QPointF clickWidget = viewer->getViewer()->toWidgetCoordinates(pos); - QPointF btmRightWidget = viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x2, textureRectCanonical.y1) ); - double tolerance = TO_DPIX(POINT_TOLERANCE); - if ( ( clickWidget.x() >= (btmRightWidget.x() - tolerance) ) && ( clickWidget.x() <= (btmRightWidget.x() + tolerance) ) && - ( clickWidget.y() >= (btmRightWidget.y() - tolerance) ) && ( clickWidget.y() <= (btmRightWidget.y() + tolerance) ) ) { - return true; - } - - return false; -} - -bool -TrackerGuiPrivate::isInsideSelectedMarkerTextureResizeAnchor(const QPointF& pos) const -{ - RectD textureRectCanonical; - - computeSelectedMarkerCanonicalRect(&textureRectCanonical); - - QPointF clickWidget = viewer->getViewer()->toWidgetCoordinates(pos); - QPointF btmRightWidget = viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x2, textureRectCanonical.y1) ); - QPointF topLeftWidget = viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x1, textureRectCanonical.y2) ); - RectD rect; - rect.x1 = topLeftWidget.x(); - rect.y1 = topLeftWidget.y(); - rect.x2 = btmRightWidget.x(); - rect.y2 = btmRightWidget.y(); - - return rect.contains( clickWidget.x(), clickWidget.y() ); -} - -void -TrackerGuiPrivate::computeTextureCanonicalRect(const Texture& tex, - int xOffset, - int texWidthPx, - RectD* rect) const -{ - ///Preserve width - double par = tex.w() / (double)tex.h(); - - rect->x2 = viewer->getViewer()->toZoomCoordinates( QPointF(xOffset + texWidthPx, 0.) ).x(); - QPointF topLeft = viewer->getViewer()->toZoomCoordinates( QPointF(xOffset, 0.) ); - rect->x1 = topLeft.x(); - rect->y2 = topLeft.y(); - double height = rect->width() / par; - rect->y1 = rect->y2 - height; -} - -void -TrackerGuiPrivate::computeSelectedMarkerCanonicalRect(RectD* rect) const -{ - assert(selectedMarkerTexture); - computeTextureCanonicalRect(*selectedMarkerTexture, 0, selectedMarkerWidth, rect); -} - -static Point -toMagWindowPoint(const Point& ptnPoint, - const RectD& canonicalSearchWindow, - const RectD& textureRectCanonical) -{ - Point ret; - double xCenterPercent = (ptnPoint.x - canonicalSearchWindow.x1) / (canonicalSearchWindow.x2 - canonicalSearchWindow.x1); - double yCenterPercent = (ptnPoint.y - canonicalSearchWindow.y1) / (canonicalSearchWindow.y2 - canonicalSearchWindow.y1); - - ret.y = textureRectCanonical.y1 + yCenterPercent * (textureRectCanonical.y2 - textureRectCanonical.y1); - ret.x = textureRectCanonical.x1 + xCenterPercent * (textureRectCanonical.x2 - textureRectCanonical.x1); - - return ret; -} - -static void -drawEllipse(double x, - double y, - double radiusX, - double radiusY, - int l, - double r, - double g, - double b, - double a) -{ - glColor3f(r * l * a, g * l * a, b * l * a); - - glPushMatrix(); - // center the oval at x_center, y_center - glTranslatef( (float)x, (float)y, 0.f ); - // draw the oval using line segments - glBegin(GL_LINE_LOOP); - // we don't need to be pixel-perfect here, it's just an interact! - // 40 segments is enough. - double m = 2 * 3.14159265358979323846264338327950288419717 / 40.; - for (int i = 0; i < 40; ++i) { - double theta = i * m; - glVertex2d( radiusX * std::cos(theta), radiusY * std::sin(theta) ); - } - glEnd(); - - glPopMatrix(); -} - -TrackerGuiPrivate::KeyFrameTexIDs -TrackerGuiPrivate::getKeysToRenderForMarker(double currentTime, - const KeyFrameTexIDs& allKeys) -{ - KeyFrameTexIDs keysToRender; - ///Find the first key equivalent to currentTime or after - KeyFrameTexIDs::const_iterator lower = allKeys.lower_bound(currentTime); - KeyFrameTexIDs::const_iterator prev = lower; - - if ( lower != allKeys.begin() ) { - --prev; - } else { - prev = allKeys.end(); - } - - for (int i = 0; i < MAX_TRACK_KEYS_TO_DISPLAY; ++i) { - if ( lower != allKeys.end() ) { - keysToRender.insert(*lower); - ++lower; - } - if (i == MAX_TRACK_KEYS_TO_DISPLAY) { - break; - } - if ( prev != allKeys.end() ) { - keysToRender.insert(*prev); - if ( prev != allKeys.begin() ) { - --prev; - } else { - prev = allKeys.end(); - } - } else { - if ( lower == allKeys.end() ) { - ///No more keyframes - break; - } - } - } - - return keysToRender; -} - -void -TrackerGuiPrivate::drawSelectedMarkerKeyframes(const std::pair& pixelScale, - int currentTime) -{ - TrackMarkerPtr marker = selectedMarker.lock(); - - assert(marker); - if (!marker) { - return; - } - if ( !marker->isEnabled(currentTime) ) { - return; - } - boost::shared_ptr centerKnob = marker->getCenterKnob(); - boost::shared_ptr offsetKnob = marker->getOffsetKnob(); - boost::shared_ptr errorKnob = marker->getErrorKnob(); - boost::shared_ptr ptnTopLeft = marker->getPatternTopLeftKnob(); - boost::shared_ptr ptnTopRight = marker->getPatternTopRightKnob(); - boost::shared_ptr ptnBtmRight = marker->getPatternBtmRightKnob(); - boost::shared_ptr ptnBtmLeft = marker->getPatternBtmLeftKnob(); - boost::shared_ptr searchWndBtmLeft = marker->getSearchWindowBottomLeftKnob(); - boost::shared_ptr searchWndTopRight = marker->getSearchWindowTopRightKnob(); - const QFont& font = viewer->font(); - QFontMetrics fm(font); - int fontHeight = fm.height(); - double xOffsetPixels = selectedMarkerWidth; - QPointF viewerTopLeftCanonical = viewer->getViewer()->toZoomCoordinates( QPointF(0, 0.) ); - - - for (TrackKeysMap::iterator it = trackTextures.begin(); it != trackTextures.end(); ++it) { - if (it->first.lock() == marker) { - if ( it->second.empty() ) { - break; - } - ///Render at most MAX_TRACK_KEYS_TO_DISPLAY keyframes - KeyFrameTexIDs keysToRender = getKeysToRenderForMarker(currentTime, it->second); - - for (KeyFrameTexIDs::const_iterator it2 = keysToRender.begin(); it2 != keysToRender.end(); ++it2) { - double time = (double)it2->first; - Point offset, center, topLeft, topRight, btmRight, btmLeft; - - center.x = centerKnob->getValueAtTime(time, 0); - center.y = centerKnob->getValueAtTime(time, 1); - offset.x = offsetKnob->getValueAtTime(time, 0); - offset.y = offsetKnob->getValueAtTime(time, 1); - - topLeft.x = ptnTopLeft->getValueAtTime(time, 0) + offset.x + center.x; - topLeft.y = ptnTopLeft->getValueAtTime(time, 1) + offset.y + center.y; - - topRight.x = ptnTopRight->getValueAtTime(time, 0) + offset.x + center.x; - topRight.y = ptnTopRight->getValueAtTime(time, 1) + offset.y + center.y; - - btmRight.x = ptnBtmRight->getValueAtTime(time, 0) + offset.x + center.x; - btmRight.y = ptnBtmRight->getValueAtTime(time, 1) + offset.y + center.y; - - btmLeft.x = ptnBtmLeft->getValueAtTime(time, 0) + offset.x + center.x; - btmLeft.y = ptnBtmLeft->getValueAtTime(time, 1) + offset.y + center.y; - - //const double searchLeft = searchWndBtmLeft->getValueAtTime(time, 0) + offset.x + center.x; - //const double searchRight = searchWndTopRight->getValueAtTime(time, 0) + offset.x + center.x; - //const double searchBottom = searchWndBtmLeft->getValueAtTime(time, 1) + offset.y + center.y; - //const double searchTop = searchWndTopRight->getValueAtTime(time, 1) + offset.y + center.y; - - const TextureRect& texRect = it2->second->getTextureRect(); - if (texRect.height() <= 0) { - continue; - } - double par = texRect.width() / (double)texRect.height(); - RectD textureRectCanonical; - - textureRectCanonical.x2 = viewer->getViewer()->toZoomCoordinates( QPointF(TO_DPIX(SELECTED_MARKER_KEYFRAME_WIDTH_SCREEN_PX) + xOffsetPixels, 0.) ).x(); - textureRectCanonical.x1 = viewer->getViewer()->toZoomCoordinates( QPointF(xOffsetPixels, 0.) ).x(); - textureRectCanonical.y2 = viewerTopLeftCanonical.y(); - double height = textureRectCanonical.width() / par; - textureRectCanonical.y1 = textureRectCanonical.y2 - height; - - - RectD canonicalSearchWindow; - texRect.toCanonical_noClipping(0, texRect.par, &canonicalSearchWindow); - - //Remove any offset to the center to see the marker in the magnification window - double xCenterPercent = (center.x - canonicalSearchWindow.x1 + offset.x) / (canonicalSearchWindow.x2 - canonicalSearchWindow.x1); - double yCenterPercent = (center.y - canonicalSearchWindow.y1 + offset.y) / (canonicalSearchWindow.y2 - canonicalSearchWindow.y1); - Point centerPointCanonical; - centerPointCanonical.y = textureRectCanonical.y1 + yCenterPercent * (textureRectCanonical.y2 - textureRectCanonical.y1); - centerPointCanonical.x = textureRectCanonical.x1 + xCenterPercent * (textureRectCanonical.x2 - textureRectCanonical.x1); - - - Point innerTopLeft = toMagWindowPoint(topLeft, canonicalSearchWindow, textureRectCanonical); - Point innerTopRight = toMagWindowPoint(topRight, canonicalSearchWindow, textureRectCanonical); - Point innerBtmLeft = toMagWindowPoint(btmLeft, canonicalSearchWindow, textureRectCanonical); - Point innerBtmRight = toMagWindowPoint(btmRight, canonicalSearchWindow, textureRectCanonical); - - //Map texture - glColor4f(1., 1., 1., 1.); - glEnable(GL_TEXTURE_2D); - glBindTexture( GL_TEXTURE_2D, it2->second->getTexID() ); - glBegin(GL_POLYGON); - glTexCoord2d(0, 0); glVertex2d(textureRectCanonical.x1, textureRectCanonical.y1); - glTexCoord2d(0, 1); glVertex2d(textureRectCanonical.x1, textureRectCanonical.y2); - glTexCoord2d(1, 1); glVertex2d(textureRectCanonical.x2, textureRectCanonical.y2); - glTexCoord2d(1, 0); glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); - glEnd(); - - glBindTexture(GL_TEXTURE_2D, 0); - - QPointF textPos = viewer->getViewer()->toZoomCoordinates( QPointF(xOffsetPixels + 5, fontHeight + 5 ) ); - viewer->getViewer()->renderText(textPos.x(), textPos.y(), QString::fromUtf8( marker->getLabel().c_str() ), QColor(200, 200, 200), font); - - QPointF framePos = viewer->getViewer()->toZoomCoordinates( QPointF( xOffsetPixels + 5, viewer->getViewer()->toWidgetCoordinates( QPointF(textureRectCanonical.x1, textureRectCanonical.y1) ).y() ) ); - QString frameText = _publicInterface->tr("Frame"); - frameText.append( QString::fromUtf8(" ") + QString::number(it2->first) ); - viewer->getViewer()->renderText(framePos.x(), framePos.y(), frameText, QColor(200, 200, 200), font); - - //Draw contour - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - if (time == currentTime) { - glColor4f(0.93, 0.54, 0, 1); - } else { - KeyFrameTexIDs::const_iterator next = it2; - ++next; - KeyFrameTexIDs::const_iterator prev = it2; - if ( prev != keysToRender.begin() ) { - --prev; - } else { - prev = keysToRender.end(); - } - if ( ( next == keysToRender.end() ) && (time < currentTime) ) { - //Beyond the last keyframe - glColor4f(0.93, 0.54, 0, 1); - } else if ( ( prev == keysToRender.end() ) && (time > currentTime) ) { - //Before the first keyframe - glColor4f(0.93, 0.54, 0, 1); - } else { - if (time < currentTime) { - assert( next != keysToRender.end() ); - if (next->first > currentTime) { - glColor4f(1, 0.75, 0.47, 1); - } else { - glColor4f(1., 1., 1., 0.5); - } - } else { - //time > currentTime - assert( prev != keysToRender.end() ); - if (prev->first < currentTime) { - glColor4f(1, 0.75, 0.47, 1); - } else { - glColor4f(1., 1., 1., 0.5); - } - } - } - } - - glLineWidth(1.5); - glCheckError(); - glBegin(GL_LINE_LOOP); - glVertex2d(textureRectCanonical.x1, textureRectCanonical.y1); - glVertex2d(textureRectCanonical.x1, textureRectCanonical.y2); - glVertex2d(textureRectCanonical.x2, textureRectCanonical.y2); - glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); - glEnd(); - - glCheckError(); - - - //Draw internal marker - for (int l = 0; l < 2; ++l) { - // shadow (uses GL_PROJECTION) - glMatrixMode(GL_PROJECTION); - int direction = (l == 0) ? 1 : -1; - // translate (1,-1) pixels - glTranslated(direction * pixelScale.first / 256, -direction * pixelScale.second / 256, 0); - glMatrixMode(GL_MODELVIEW); - - glColor4f(0.8 * l, 0.8 * l, 0.8 * l, 1); - - glBegin(GL_LINE_LOOP); - glVertex2d(innerTopLeft.x, innerTopLeft.y); - glVertex2d(innerTopRight.x, innerTopRight.y); - glVertex2d(innerBtmRight.x, innerBtmRight.y); - glVertex2d(innerBtmLeft.x, innerBtmLeft.y); - glEnd(); - - glBegin(GL_POINTS); - glVertex2d(centerPointCanonical.x, centerPointCanonical.y); - glEnd(); - } - - xOffsetPixels += TO_DPIX(SELECTED_MARKER_KEYFRAME_WIDTH_SCREEN_PX); - } - break; - } - } -} // TrackerGuiPrivate::drawSelectedMarkerKeyframes - -void -TrackerGuiPrivate::drawSelectedMarkerTexture(const std::pair& pixelScale, - int currentTime, - const Point& ptnCenter, - const Point& offset, - const Point& ptnTopLeft, - const Point& ptnTopRight, - const Point& ptnBtmRight, - const Point& ptnBtmLeft, - const Point& /*selectedSearchWndBtmLeft*/, - const Point& /*selectedSearchWndTopRight*/) -{ - TrackMarkerPtr marker = selectedMarker.lock(); - - if ( isTracking || !selectedMarkerTexture || !marker || !marker->isEnabled(currentTime) || viewer->getInternalNode()->getRenderEngine()->isDoingSequentialRender() ) { - return; - } - - RectD textureRectCanonical; - computeSelectedMarkerCanonicalRect(&textureRectCanonical); - - - const TextureRect& texRect = selectedMarkerTexture->getTextureRect(); - RectD texCoords; - /*texCoords.x1 = (texRect.x1 - selectedMarkerTextureRoI.x1) / (double)selectedMarkerTextureRoI.width(); - texCoords.y1 = (texRect.y1 - selectedMarkerTextureRoI.y1) / (double)selectedMarkerTextureRoI.height(); - if (texRect.x2 <= selectedMarkerTextureRoI.x2) { - texCoords.x2 = (texRect.x2 - selectedMarkerTextureRoI.x1) / (double)selectedMarkerTextureRoI.width(); - } else { - texCoords.x2 = 1.; - } - if (texRect.y2 <= selectedMarkerTextureRoI.y2) { - texCoords.y2 = (texRect.y2 - selectedMarkerTextureRoI.y1) / (double)selectedMarkerTextureRoI.height(); - } else { - texCoords.y2 = 1.; - }*/ - texCoords.x1 = texCoords.y1 = 0.; - texCoords.x2 = texCoords.y2 = 1.; - - RectD canonicalSearchWindow; - texRect.toCanonical_noClipping(0, texRect.par, &canonicalSearchWindow); - - Point centerPoint, innerTopLeft, innerTopRight, innerBtmLeft, innerBtmRight; - - //Remove any offset to the center to see the marker in the magnification window - double xCenterPercent = (ptnCenter.x - canonicalSearchWindow.x1 + offset.x) / (canonicalSearchWindow.x2 - canonicalSearchWindow.x1); - double yCenterPercent = (ptnCenter.y - canonicalSearchWindow.y1 + offset.y) / (canonicalSearchWindow.y2 - canonicalSearchWindow.y1); - centerPoint.y = textureRectCanonical.y1 + yCenterPercent * (textureRectCanonical.y2 - textureRectCanonical.y1); - centerPoint.x = textureRectCanonical.x1 + xCenterPercent * (textureRectCanonical.x2 - textureRectCanonical.x1); - - - innerTopLeft = toMagWindowPoint(ptnTopLeft, canonicalSearchWindow, textureRectCanonical); - innerTopRight = toMagWindowPoint(ptnTopRight, canonicalSearchWindow, textureRectCanonical); - innerBtmLeft = toMagWindowPoint(ptnBtmLeft, canonicalSearchWindow, textureRectCanonical); - innerBtmRight = toMagWindowPoint(ptnBtmRight, canonicalSearchWindow, textureRectCanonical); - - Transform::Point3D btmLeftTex, topLeftTex, topRightTex, btmRightTex; - btmLeftTex.z = topLeftTex.z = topRightTex.z = btmRightTex.z = 1.; - btmLeftTex.x = texCoords.x1; btmLeftTex.y = texCoords.y1; - topLeftTex.x = texCoords.x1; topLeftTex.y = texCoords.y2; - topRightTex.x = texCoords.x2; topRightTex.y = texCoords.y2; - btmRightTex.x = texCoords.x2; btmRightTex.y = texCoords.y1; - Transform::Matrix3x3 m = Transform::matTransformCanonical(0, 0, selectedMarkerScale.x, selectedMarkerScale.y, 0, 0, false, 0, xCenterPercent, yCenterPercent); - btmLeftTex = Transform::matApply(m, btmLeftTex); - topLeftTex = Transform::matApply(m, topLeftTex); - btmRightTex = Transform::matApply(m, btmRightTex); - topRightTex = Transform::matApply(m, topRightTex); - - //Map texture - glColor4f(1., 1., 1., 1.); - glEnable(GL_TEXTURE_2D); - glBindTexture( GL_TEXTURE_2D, selectedMarkerTexture->getTexID() ); - glBegin(GL_POLYGON); - glTexCoord2d(btmLeftTex.x, btmRightTex.y); glVertex2d(textureRectCanonical.x1, textureRectCanonical.y1); - glTexCoord2d(topLeftTex.x, topLeftTex.y); glVertex2d(textureRectCanonical.x1, textureRectCanonical.y2); - glTexCoord2d(topRightTex.x, topRightTex.y); glVertex2d(textureRectCanonical.x2, textureRectCanonical.y2); - glTexCoord2d(btmRightTex.x, btmRightTex.y); glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); - glEnd(); - - glBindTexture(GL_TEXTURE_2D, 0); - - //Draw contour - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(1., 1., 1., 0.5); - glLineWidth(1.5); - glCheckError(); - glBegin(GL_LINE_LOOP); - glVertex2d(textureRectCanonical.x1, textureRectCanonical.y1); - glVertex2d(textureRectCanonical.x1, textureRectCanonical.y2); - glVertex2d(textureRectCanonical.x2, textureRectCanonical.y2); - glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); - glEnd(); - - glColor4f(0.8, 0.8, 0.8, 1.); - glPointSize(POINT_SIZE); - glBegin(GL_POINTS); - glVertex2d(textureRectCanonical.x2, textureRectCanonical.y1); - glEnd(); - glCheckError(); - - const QFont& font = viewer->font(); - QFontMetrics fm(font); - QPointF textPos = viewer->getViewer()->toZoomCoordinates( QPointF(5, fm.height() + 5) ); - viewer->getViewer()->renderText(textPos.x(), textPos.y(), QString::fromUtf8( marker->getLabel().c_str() ), QColor(200, 200, 200), font); - - //Draw internal marker - - for (int l = 0; l < 2; ++l) { - // shadow (uses GL_PROJECTION) - glMatrixMode(GL_PROJECTION); - int direction = (l == 0) ? 1 : -1; - // translate (1,-1) pixels - glTranslated(direction * pixelScale.first / 256, -direction * pixelScale.second / 256, 0); - glMatrixMode(GL_MODELVIEW); - - glColor4f(0.8 * l, 0.8 * l, 0.8 * l, 1); - - glBegin(GL_LINE_LOOP); - glVertex2d(innerTopLeft.x, innerTopLeft.y); - glVertex2d(innerTopRight.x, innerTopRight.y); - glVertex2d(innerBtmRight.x, innerBtmRight.y); - glVertex2d(innerBtmLeft.x, innerBtmLeft.y); - glEnd(); - - glBegin(GL_POINTS); - glVertex2d(centerPoint.x, centerPoint.y); - glEnd(); - - ///Draw ellipse if scaling - if ( (eventState == eMouseStateScalingSelectedMarker) || (hoverState == eDrawStateShowScalingHint) ) { - double ellipseColor[3]; - if (eventState == eMouseStateScalingSelectedMarker) { - ellipseColor[0] = 0.8; - ellipseColor[1] = 0.8; - ellipseColor[2] = 0.; - } else { - ellipseColor[0] = 0.8; - ellipseColor[1] = 0.8; - ellipseColor[2] = 0.8; - } - double rx = std::sqrt( (lastMousePos.x() - centerPoint.x) * (lastMousePos.x() - centerPoint.x) + (lastMousePos.y() - centerPoint.y) * (lastMousePos.y() - centerPoint.y) ); - double ry = rx; - drawEllipse(centerPoint.x, centerPoint.y, rx, ry, l, ellipseColor[0], ellipseColor[1], ellipseColor[2], 1.); - } - } - - ///Now draw keyframes - drawSelectedMarkerKeyframes(pixelScale, currentTime); -} // TrackerGuiPrivate::drawSelectedMarkerTexture - -static bool -isNearbyPoint(const boost::shared_ptr& knob, - ViewerGL* viewer, - double xWidget, - double yWidget, - double toleranceWidget, - double time) -{ - QPointF p; - - p.rx() = knob->getValueAtTime(time, 0); - p.ry() = knob->getValueAtTime(time, 1); - p = viewer->toWidgetCoordinates(p); - if ( ( p.x() <= (xWidget + toleranceWidget) ) && ( p.x() >= (xWidget - toleranceWidget) ) && - ( p.y() <= (yWidget + toleranceWidget) ) && ( p.y() >= (yWidget - toleranceWidget) ) ) { - return true; - } - - return false; -} - -static bool -isNearbyPoint(const QPointF& p, - ViewerGL* viewer, - double xWidget, - double yWidget, - double toleranceWidget) -{ - QPointF pw = viewer->toWidgetCoordinates(p); - - if ( ( pw.x() <= (xWidget + toleranceWidget) ) && ( pw.x() >= (xWidget - toleranceWidget) ) && - ( pw.y() <= (yWidget + toleranceWidget) ) && ( pw.y() >= (yWidget - toleranceWidget) ) ) { - return true; - } - - return false; -} - -static void -findLineIntersection(const Point& p, - const Point& l1, - const Point& l2, - Point* inter) -{ - Point h, u; - double a; - - h.x = p.x - l1.x; - h.y = p.y - l1.y; - - u.x = l2.x - l1.x; - u.y = l2.y - l1.y; - - a = (u.x * h.x + u.y * h.y) / (u.x * u.x + u.y * u.y); - inter->x = l1.x + u.x * a; - inter->y = l1.y + u.y * a; -} - -bool -TrackerGui::penDown(double time, - const RenderScale& renderScale, - ViewIdx view, - const QPointF & viewportPos, - const QPointF & pos, - double pressure, - QMouseEvent* e) -{ - FLAG_DURING_INTERACT - - std::pair pixelScale; - ViewerGL* viewer = _imp->viewer->getViewer(); - - viewer->getPixelScale(pixelScale.first, pixelScale.second); - bool didSomething = false; - - if (_imp->panel) { - if ( _imp->panel->getContext()->onOverlayPenDownInternalNodes( time, renderScale, view, viewportPos, pos, pressure, _imp->viewer->getViewer() ) ) { - return true; - } - } - - - if (_imp->panelv1) { - const std::list > & instances = _imp->panelv1->getInstances(); - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - NodePtr instance = it->first.lock(); - if ( it->second && !instance->isNodeDisabled() ) { - EffectInstPtr effect = instance->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); - didSomething = effect->onOverlayPenDown_public(time, renderScale, view, viewportPos, pos, pressure); - } - } - - double selectionTol = pixelScale.first * 10.; - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - NodePtr instance = it->first.lock(); - boost::shared_ptr newInstanceKnob = instance->getKnobByName("center"); - assert(newInstanceKnob); //< if it crashes here that means the parameter's name changed in the OpenFX plug-in. - KnobDouble* dblKnob = dynamic_cast( newInstanceKnob.get() ); - assert(dblKnob); - double x, y; - x = dblKnob->getValueAtTime(time, 0); - y = dblKnob->getValueAtTime(time, 1); - - if ( ( pos.x() >= (x - selectionTol) ) && ( pos.x() <= (x + selectionTol) ) && - ( pos.y() >= (y - selectionTol) ) && ( pos.y() <= (y + selectionTol) ) ) { - if (!it->second) { - _imp->panelv1->selectNode( instance, modCASIsShift(e) ); - } - didSomething = true; - } - } - - if (_imp->clickToAddTrackEnabled && !didSomething) { - NodePtr newInstance = _imp->panelv1->createNewInstance(true); - boost::shared_ptr newInstanceKnob = newInstance->getKnobByName("center"); - assert(newInstanceKnob); //< if it crashes here that means the parameter's name changed in the OpenFX plug-in. - KnobDouble* dblKnob = dynamic_cast( newInstanceKnob.get() ); - assert(dblKnob); - dblKnob->beginChanges(); - dblKnob->blockValueChanges(); - dblKnob->setValueAtTime(time, pos.x(), view, 0); - dblKnob->setValueAtTime(time, pos.y(), view, 1); - dblKnob->unblockValueChanges(); - dblKnob->endChanges(); - didSomething = true; - } - - if ( !didSomething && !modCASIsShift(e) ) { - _imp->panelv1->clearSelection(); - } - } else { // if (_imp->panelv1) { - boost::shared_ptr context = _imp->panel->getContext(); - std::vector allMarkers; - context->getAllMarkers(&allMarkers); - for (std::vector::iterator it = allMarkers.begin(); it != allMarkers.end(); ++it) { - if ( !(*it)->isEnabled(time) ) { - continue; - } - - bool isSelected = context->isMarkerSelected( (*it) ); - boost::shared_ptr centerKnob = (*it)->getCenterKnob(); - boost::shared_ptr offsetKnob = (*it)->getOffsetKnob(); - boost::shared_ptr ptnTopLeft = (*it)->getPatternTopLeftKnob(); - boost::shared_ptr ptnTopRight = (*it)->getPatternTopRightKnob(); - boost::shared_ptr ptnBtmRight = (*it)->getPatternBtmRightKnob(); - boost::shared_ptr ptnBtmLeft = (*it)->getPatternBtmLeftKnob(); - boost::shared_ptr searchWndTopRight = (*it)->getSearchWindowTopRightKnob(); - boost::shared_ptr searchWndBtmLeft = (*it)->getSearchWindowBottomLeftKnob(); - QPointF centerPoint; - centerPoint.rx() = centerKnob->getValueAtTime(time, 0); - centerPoint.ry() = centerKnob->getValueAtTime(time, 1); - - QPointF offset; - offset.rx() = offsetKnob->getValueAtTime(time, 0); - offset.ry() = offsetKnob->getValueAtTime(time, 1); - - if ( isNearbyPoint(centerKnob, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE, time) ) { - if (_imp->controlDown > 0) { - _imp->eventState = eMouseStateDraggingOffset; - } else { - _imp->eventState = eMouseStateDraggingCenter; - } - _imp->interactMarker = *it; - didSomething = true; - } else if ( ( (offset.x() != 0) || (offset.y() != 0) ) && isNearbyPoint(QPointF( centerPoint.x() + offset.x(), centerPoint.y() + offset.y() ), viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOffset; - _imp->interactMarker = *it; - didSomething = true; - } - - if (!didSomething && isSelected) { - QPointF topLeft, topRight, btmRight, btmLeft; - topLeft.rx() = ptnTopLeft->getValueAtTime(time, 0) + offset.x() + centerPoint.x(); - topLeft.ry() = ptnTopLeft->getValueAtTime(time, 1) + offset.y() + centerPoint.y(); - - topRight.rx() = ptnTopRight->getValueAtTime(time, 0) + offset.x() + centerPoint.x(); - topRight.ry() = ptnTopRight->getValueAtTime(time, 1) + offset.y() + centerPoint.y(); - - btmRight.rx() = ptnBtmRight->getValueAtTime(time, 0) + offset.x() + centerPoint.x(); - btmRight.ry() = ptnBtmRight->getValueAtTime(time, 1) + offset.y() + centerPoint.y(); - - btmLeft.rx() = ptnBtmLeft->getValueAtTime(time, 0) + offset.x() + centerPoint.x(); - btmLeft.ry() = ptnBtmLeft->getValueAtTime(time, 1) + offset.y() + centerPoint.y(); - - QPointF midTop, midRight, midBtm, midLeft; - midTop.rx() = ( topLeft.x() + topRight.x() ) / 2.; - midTop.ry() = ( topLeft.y() + topRight.y() ) / 2.; - - midRight.rx() = ( btmRight.x() + topRight.x() ) / 2.; - midRight.ry() = ( btmRight.y() + topRight.y() ) / 2.; - - midBtm.rx() = ( btmRight.x() + btmLeft.x() ) / 2.; - midBtm.ry() = ( btmRight.y() + btmLeft.y() ) / 2.; - - midLeft.rx() = ( topLeft.x() + btmLeft.x() ) / 2.; - midLeft.ry() = ( topLeft.y() + btmLeft.y() ) / 2.; - - if ( isSelected && isNearbyPoint(topLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingInnerTopLeft; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isSelected && isNearbyPoint(topRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingInnerTopRight; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isSelected && isNearbyPoint(btmRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingInnerBtmRight; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isSelected && isNearbyPoint(btmLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingInnerBtmLeft; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isSelected && isNearbyPoint(midTop, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingInnerTopMid; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isSelected && isNearbyPoint(midRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingInnerMidRight; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isSelected && isNearbyPoint(midLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingInnerMidLeft; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isSelected && isNearbyPoint(midBtm, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingInnerBtmMid; - _imp->interactMarker = *it; - didSomething = true; - } - } - - if (!didSomething && isSelected) { - ///Test search window - const double searchLeft = searchWndBtmLeft->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); - const double searchRight = searchWndTopRight->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); - const double searchTop = searchWndTopRight->getValueAtTime(time, 1) + centerPoint.y() + offset.y(); - const double searchBottom = searchWndBtmLeft->getValueAtTime(time, 1) + +centerPoint.y() + offset.y(); - const double searchMidX = (searchLeft + searchRight) / 2.; - const double searchMidY = (searchTop + searchBottom) / 2.; - const QPointF searchTopLeft(searchLeft, searchTop); - const QPointF searchTopRight(searchRight, searchTop); - const QPointF searchBtmRight(searchRight, searchBottom); - const QPointF searchBtmLeft(searchLeft, searchBottom); - const QPointF searchTopMid(searchMidX, searchTop); - const QPointF searchRightMid(searchRight, searchMidY); - const QPointF searchLeftMid(searchLeft, searchMidY); - const QPointF searchBtmMid(searchMidX, searchBottom); - - if ( isNearbyPoint(searchTopLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOuterTopLeft; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isNearbyPoint(searchTopRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOuterTopRight; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isNearbyPoint(searchBtmRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOuterBtmRight; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isNearbyPoint(searchBtmLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOuterBtmLeft; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isNearbyPoint(searchTopMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOuterTopMid; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isNearbyPoint(searchBtmMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOuterBtmMid; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isNearbyPoint(searchLeftMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOuterMidLeft; - _imp->interactMarker = *it; - didSomething = true; - } else if ( isNearbyPoint(searchRightMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->eventState = eMouseStateDraggingOuterMidRight; - _imp->interactMarker = *it; - didSomething = true; - } - } - - //If we hit the interact, make sure it is selected - if (_imp->interactMarker) { - if (!isSelected) { - context->beginEditSelection(TrackerContext::eTrackSelectionViewer); - if ( !modCASIsShift(e) ) { - context->clearSelection(TrackerContext::eTrackSelectionViewer); - } - context->addTrackToSelection(_imp->interactMarker, TrackerContext::eTrackSelectionViewer); - context->endEditSelection(TrackerContext::eTrackSelectionViewer); - } - } - - if (didSomething) { - break; - } - } // for (std::vector::iterator it = allMarkers.begin(); it!=allMarkers.end(); ++it) { - - if (_imp->clickToAddTrackEnabled && !didSomething) { - TrackMarkerPtr marker = context->createMarker(); - boost::shared_ptr centerKnob = marker->getCenterKnob(); - centerKnob->setValuesAtTime(time, pos.x(), pos.y(), view, eValueChangedReasonNatronInternalEdited); - if ( _imp->createKeyOnMoveButton->isChecked() ) { - marker->setUserKeyframe(time); - } - _imp->panel->pushUndoCommand( new AddTrackCommand(marker, context) ); - updateSelectedMarkerTexture(); - didSomething = true; - } - - if ( !didSomething && _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->isNearbySelectedMarkerTextureResizeAnchor(pos) ) { - _imp->eventState = eMouseStateDraggingSelectedMarkerResizeAnchor; - didSomething = true; - } - - if ( !didSomething && _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->isInsideSelectedMarkerTextureResizeAnchor(pos) ) { - if (_imp->shiftDown) { - _imp->eventState = eMouseStateScalingSelectedMarker; - } else { - _imp->eventState = eMouseStateDraggingSelectedMarker; - } - _imp->interactMarker = _imp->selectedMarker.lock(); - didSomething = true; - } - - if (!didSomething) { - int keyTime = _imp->isInsideKeyFrameTexture(time, pos, viewportPos); - if (keyTime != INT_MAX) { - _imp->viewer->seek(keyTime); - didSomething = true; - } - } - if (!didSomething) { - std::list selectedMarkers; - context->getSelectedMarkers(&selectedMarkers); - if ( !selectedMarkers.empty() ) { - context->clearSelection(TrackerContext::eTrackSelectionViewer); - - didSomething = true; - } - } - } - _imp->lastMousePos = pos; - - return didSomething; -} // penDown - -bool -TrackerGui::penDoubleClicked(double /*time*/, - const RenderScale& /*renderScale*/, - ViewIdx /*view*/, - const QPointF & /*viewportPos*/, - const QPointF & /*pos*/, - QMouseEvent* /*e*/) -{ - bool didSomething = false; - - return didSomething; -} - -void -TrackerGuiPrivate::transformPattern(double time, - TrackerMouseStateEnum state, - const Point& delta) -{ - boost::shared_ptr searchWndTopRight, searchWndBtmLeft; - boost::shared_ptr patternCorners[4]; - boost::shared_ptr context = panel->getContext(); - boost::shared_ptr centerKnob = interactMarker->getCenterKnob(); - boost::shared_ptr offsetKnob = interactMarker->getOffsetKnob(); - bool transformPatternCorners = state != eMouseStateDraggingOuterBtmLeft && - state != eMouseStateDraggingOuterBtmRight && - state != eMouseStateDraggingOuterTopLeft && - state != eMouseStateDraggingOuterTopRight && - state != eMouseStateDraggingOuterMidLeft && - state != eMouseStateDraggingOuterMidRight && - state != eMouseStateDraggingOuterTopMid && - state != eMouseStateDraggingOuterBtmMid; - - if (transformPatternCorners) { - patternCorners[0] = interactMarker->getPatternTopLeftKnob(); - patternCorners[1] = interactMarker->getPatternBtmLeftKnob(); - patternCorners[2] = interactMarker->getPatternBtmRightKnob(); - patternCorners[3] = interactMarker->getPatternTopRightKnob(); - } - searchWndTopRight = interactMarker->getSearchWindowTopRightKnob(); - searchWndBtmLeft = interactMarker->getSearchWindowBottomLeftKnob(); - - QPointF centerPoint; - centerPoint.rx() = centerKnob->getValueAtTime(time, 0); - centerPoint.ry() = centerKnob->getValueAtTime(time, 1); - - QPointF offset; - offset.rx() = offsetKnob->getValueAtTime(time, 0); - offset.ry() = offsetKnob->getValueAtTime(time, 1); - - QPointF patternPoints[4]; - QPointF searchPoints[4]; - if (transformPatternCorners) { - for (int i = 0; i < 4; ++i) { - patternPoints[i].rx() = patternCorners[i]->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); - patternPoints[i].ry() = patternCorners[i]->getValueAtTime(time, 1) + centerPoint.y() + offset.y(); - } - } - searchPoints[1].rx() = searchWndBtmLeft->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); - searchPoints[1].ry() = searchWndBtmLeft->getValueAtTime(time, 1) + centerPoint.y() + offset.y(); - - searchPoints[3].rx() = searchWndTopRight->getValueAtTime(time, 0) + centerPoint.x() + offset.x(); - searchPoints[3].ry() = searchWndTopRight->getValueAtTime(time, 1) + centerPoint.y() + offset.y(); - - searchPoints[0].rx() = searchPoints[1].x(); - searchPoints[0].ry() = searchPoints[3].y(); - - searchPoints[2].rx() = searchPoints[3].x(); - searchPoints[2].ry() = searchPoints[1].y(); - - if ( (state == eMouseStateDraggingInnerBtmLeft) || - ( state == eMouseStateDraggingOuterBtmLeft) ) { - if (transformPatternCorners) { - patternPoints[1].rx() += delta.x; - patternPoints[1].ry() += delta.y; - - patternPoints[0].rx() += delta.x; - patternPoints[0].ry() -= delta.y; - - patternPoints[2].rx() -= delta.x; - patternPoints[2].ry() += delta.y; - - patternPoints[3].rx() -= delta.x; - patternPoints[3].ry() -= delta.y; - } - - searchPoints[1].rx() += delta.x; - searchPoints[1].ry() += delta.y; - - searchPoints[0].rx() += delta.x; - searchPoints[0].ry() -= delta.y; - - searchPoints[2].rx() -= delta.x; - searchPoints[2].ry() += delta.y; - - searchPoints[3].rx() -= delta.x; - searchPoints[3].ry() -= delta.y; - } else if ( (state == eMouseStateDraggingInnerBtmRight) || - ( state == eMouseStateDraggingOuterBtmRight) ) { - if (transformPatternCorners) { - patternPoints[1].rx() -= delta.x; - patternPoints[1].ry() += delta.y; - - patternPoints[0].rx() -= delta.x; - patternPoints[0].ry() -= delta.y; - - patternPoints[2].rx() += delta.x; - patternPoints[2].ry() += delta.y; - - patternPoints[3].rx() += delta.x; - patternPoints[3].ry() -= delta.y; - } - - searchPoints[1].rx() -= delta.x; - searchPoints[1].ry() += delta.y; - - searchPoints[0].rx() -= delta.x; - searchPoints[0].ry() -= delta.y; - - searchPoints[2].rx() += delta.x; - searchPoints[2].ry() += delta.y; - - searchPoints[3].rx() += delta.x; - searchPoints[3].ry() -= delta.y; - } else if ( (state == eMouseStateDraggingInnerTopRight) || - ( state == eMouseStateDraggingOuterTopRight) ) { - if (transformPatternCorners) { - patternPoints[1].rx() -= delta.x; - patternPoints[1].ry() -= delta.y; - - patternPoints[0].rx() -= delta.x; - patternPoints[0].ry() += delta.y; - - patternPoints[2].rx() += delta.x; - patternPoints[2].ry() -= delta.y; - - patternPoints[3].rx() += delta.x; - patternPoints[3].ry() += delta.y; - } - - searchPoints[1].rx() -= delta.x; - searchPoints[1].ry() -= delta.y; - - searchPoints[0].rx() -= delta.x; - searchPoints[0].ry() += delta.y; - - searchPoints[2].rx() += delta.x; - searchPoints[2].ry() -= delta.y; - - searchPoints[3].rx() += delta.x; - searchPoints[3].ry() += delta.y; - } else if ( (state == eMouseStateDraggingInnerTopLeft) || - ( state == eMouseStateDraggingOuterTopLeft) ) { - if (transformPatternCorners) { - patternPoints[1].rx() += delta.x; - patternPoints[1].ry() -= delta.y; - - patternPoints[0].rx() += delta.x; - patternPoints[0].ry() += delta.y; - - patternPoints[2].rx() -= delta.x; - patternPoints[2].ry() -= delta.y; - - patternPoints[3].rx() -= delta.x; - patternPoints[3].ry() += delta.y; - } - - searchPoints[1].rx() += delta.x; - searchPoints[1].ry() -= delta.y; - - searchPoints[0].rx() += delta.x; - searchPoints[0].ry() += delta.y; - - searchPoints[2].rx() -= delta.x; - searchPoints[2].ry() -= delta.y; - - searchPoints[3].rx() -= delta.x; - searchPoints[3].ry() += delta.y; - } else if ( (state == eMouseStateDraggingInnerBtmMid) || - ( state == eMouseStateDraggingOuterBtmMid) ) { - if (transformPatternCorners) { - patternPoints[1].ry() += delta.y; - patternPoints[2].ry() += delta.y; - patternPoints[0].ry() -= delta.y; - patternPoints[3].ry() -= delta.y; - } - searchPoints[1].ry() += delta.y; - searchPoints[2].ry() += delta.y; - searchPoints[0].ry() -= delta.y; - searchPoints[3].ry() -= delta.y; - } else if ( (state == eMouseStateDraggingInnerTopMid) || - ( state == eMouseStateDraggingOuterTopMid) ) { - if (transformPatternCorners) { - patternPoints[1].ry() -= delta.y; - patternPoints[2].ry() -= delta.y; - patternPoints[0].ry() += delta.y; - patternPoints[3].ry() += delta.y; - } - searchPoints[1].ry() -= delta.y; - searchPoints[2].ry() -= delta.y; - searchPoints[0].ry() += delta.y; - searchPoints[3].ry() += delta.y; - } else if ( (state == eMouseStateDraggingInnerMidLeft) || - ( state == eMouseStateDraggingOuterMidLeft) ) { - if (transformPatternCorners) { - patternPoints[1].rx() += delta.x; - patternPoints[2].rx() -= delta.x; - patternPoints[0].rx() += delta.x; - patternPoints[3].rx() -= delta.x; - } - searchPoints[1].rx() += delta.x; - searchPoints[2].rx() -= delta.x; - searchPoints[0].rx() += delta.x; - searchPoints[3].rx() -= delta.x; - } else if ( (state == eMouseStateDraggingInnerMidRight) || - ( state == eMouseStateDraggingOuterMidRight) ) { - if (transformPatternCorners) { - patternPoints[1].rx() -= delta.x; - patternPoints[2].rx() += delta.x; - patternPoints[0].rx() -= delta.x; - patternPoints[3].rx() += delta.x; - } - searchPoints[1].rx() -= delta.x; - searchPoints[2].rx() += delta.x; - searchPoints[0].rx() -= delta.x; - searchPoints[3].rx() += delta.x; - } - - EffectInstPtr effect = context->getNode()->getEffectInstance(); - effect->beginChanges(); - - if (transformPatternCorners) { - for (int i = 0; i < 4; ++i) { - patternPoints[i].rx() -= ( centerPoint.x() + offset.x() ); - patternPoints[i].ry() -= ( centerPoint.y() + offset.y() ); - - - if ( patternCorners[i]->hasAnimation() ) { - patternCorners[i]->setValuesAtTime(time, patternPoints[i].x(), patternPoints[i].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); - } else { - patternCorners[i]->setValues(patternPoints[i].x(), patternPoints[i].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); - } - } - } - searchPoints[1].rx() -= ( centerPoint.x() + offset.x() ); - searchPoints[1].ry() -= ( centerPoint.y() + offset.y() ); - - searchPoints[3].rx() -= ( centerPoint.x() + offset.x() ); - searchPoints[3].ry() -= ( centerPoint.y() + offset.y() ); - - if ( searchWndBtmLeft->hasAnimation() ) { - searchWndBtmLeft->setValuesAtTime(time, searchPoints[1].x(), searchPoints[1].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); - } else { - searchWndBtmLeft->setValues(searchPoints[1].x(), searchPoints[1].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); - } - - if ( searchWndTopRight->hasAnimation() ) { - searchWndTopRight->setValuesAtTime(time, searchPoints[3].x(), searchPoints[3].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); - } else { - searchWndTopRight->setValues(searchPoints[3].x(), searchPoints[3].y(), ViewSpec::current(), eValueChangedReasonNatronInternalEdited); - } - effect->endChanges(); - - refreshSelectedMarkerTexture(); - - if ( createKeyOnMoveButton->isChecked() ) { - interactMarker->setUserKeyframe(time); - } -} // TrackerGuiPrivate::transformPattern - -bool -TrackerGui::penMotion(double time, - const RenderScale & renderScale, - ViewIdx view, - const QPointF & viewportPos, - const QPointF & pos, - double pressure, - QInputEvent* /*e*/) -{ - FLAG_DURING_INTERACT - - std::pair pixelScale; - ViewerGL* viewer = _imp->viewer->getViewer(); - - viewer->getPixelScale(pixelScale.first, pixelScale.second); - bool didSomething = false; - - if (_imp->panel) { - if ( _imp->panel->getContext()->onOverlayPenMotionInternalNodes( time, renderScale, view, viewportPos, pos, pressure, _imp->viewer->getViewer() ) ) { - return true; - } - } - - Point delta; - delta.x = pos.x() - _imp->lastMousePos.x(); - delta.y = pos.y() - _imp->lastMousePos.y(); - - if (_imp->panelv1) { - const std::list > & instances = _imp->panelv1->getInstances(); - - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - NodePtr instance = it->first.lock(); - if ( it->second && !instance->isNodeDisabled() ) { - EffectInstPtr effect = instance->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); - if ( effect->onOverlayPenMotion_public(time, renderScale, view, viewportPos, pos, pressure) ) { - didSomething = true; - } - } - } - } else { - if (_imp->hoverState != eDrawStateInactive) { - _imp->hoverState = eDrawStateInactive; - _imp->hoverMarker.reset(); - didSomething = true; - } - - boost::shared_ptr context = _imp->panel->getContext(); - std::vector allMarkers; - context->getAllMarkers(&allMarkers); - - bool hoverProcess = false; - for (std::vector::iterator it = allMarkers.begin(); it != allMarkers.end(); ++it) { - if ( !(*it)->isEnabled(time) ) { - continue; - } - - bool isSelected = context->isMarkerSelected( (*it) ); - boost::shared_ptr centerKnob = (*it)->getCenterKnob(); - boost::shared_ptr offsetKnob = (*it)->getOffsetKnob(); - boost::shared_ptr ptnTopLeft = (*it)->getPatternTopLeftKnob(); - boost::shared_ptr ptnTopRight = (*it)->getPatternTopRightKnob(); - boost::shared_ptr ptnBtmRight = (*it)->getPatternBtmRightKnob(); - boost::shared_ptr ptnBtmLeft = (*it)->getPatternBtmLeftKnob(); - boost::shared_ptr searchWndTopRight = (*it)->getSearchWindowTopRightKnob(); - boost::shared_ptr searchWndBtmLeft = (*it)->getSearchWindowBottomLeftKnob(); - QPointF center; - center.rx() = centerKnob->getValueAtTime(time, 0); - center.ry() = centerKnob->getValueAtTime(time, 1); - - QPointF offset; - offset.rx() = offsetKnob->getValueAtTime(time, 0); - offset.ry() = offsetKnob->getValueAtTime(time, 1); - if ( isNearbyPoint(centerKnob, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE, time) ) { - _imp->hoverState = eDrawStateHoveringCenter; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( ( (offset.x() != 0) || (offset.y() != 0) ) && isNearbyPoint(QPointF( center.x() + offset.x(), center.y() + offset.y() ), viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringCenter; - _imp->hoverMarker = *it; - didSomething = true; - } - - - if (!hoverProcess) { - QPointF topLeft, topRight, btmRight, btmLeft; - topLeft.rx() = ptnTopLeft->getValueAtTime(time, 0) + offset.x() + center.x(); - topLeft.ry() = ptnTopLeft->getValueAtTime(time, 1) + offset.y() + center.y(); - - topRight.rx() = ptnTopRight->getValueAtTime(time, 0) + offset.x() + center.x(); - topRight.ry() = ptnTopRight->getValueAtTime(time, 1) + offset.y() + center.y(); - - btmRight.rx() = ptnBtmRight->getValueAtTime(time, 0) + offset.x() + center.x(); - btmRight.ry() = ptnBtmRight->getValueAtTime(time, 1) + offset.y() + center.y(); - - btmLeft.rx() = ptnBtmLeft->getValueAtTime(time, 0) + offset.x() + center.x(); - btmLeft.ry() = ptnBtmLeft->getValueAtTime(time, 1) + offset.y() + center.y(); - - QPointF midTop, midRight, midBtm, midLeft; - midTop.rx() = ( topLeft.x() + topRight.x() ) / 2.; - midTop.ry() = ( topLeft.y() + topRight.y() ) / 2.; - - midRight.rx() = ( btmRight.x() + topRight.x() ) / 2.; - midRight.ry() = ( btmRight.y() + topRight.y() ) / 2.; - - midBtm.rx() = ( btmRight.x() + btmLeft.x() ) / 2.; - midBtm.ry() = ( btmRight.y() + btmLeft.y() ) / 2.; - - midLeft.rx() = ( topLeft.x() + btmLeft.x() ) / 2.; - midLeft.ry() = ( topLeft.y() + btmLeft.y() ) / 2.; - - - if ( isSelected && isNearbyPoint(topLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringInnerTopLeft; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isSelected && isNearbyPoint(topRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringInnerTopRight; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isSelected && isNearbyPoint(btmRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringInnerBtmRight; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isSelected && isNearbyPoint(btmLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringInnerBtmLeft; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isSelected && isNearbyPoint(midTop, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringInnerTopMid; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isSelected && isNearbyPoint(midRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringInnerMidRight; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isSelected && isNearbyPoint(midLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringInnerMidLeft; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isSelected && isNearbyPoint(midBtm, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringInnerBtmMid; - _imp->hoverMarker = *it; - hoverProcess = true; - } - } - - if (!hoverProcess && isSelected) { - ///Test search window - const double searchLeft = searchWndBtmLeft->getValueAtTime(time, 0) + offset.x() + center.x(); - const double searchBottom = searchWndBtmLeft->getValueAtTime(time, 1) + offset.y() + center.y(); - const double searchRight = searchWndTopRight->getValueAtTime(time, 0) + offset.x() + center.x(); - const double searchTop = searchWndTopRight->getValueAtTime(time, 1) + offset.y() + center.y(); - const double searchMidX = (searchLeft + searchRight) / 2; - const double searchMidY = (searchTop + searchBottom) / 2; - const QPointF searchTopLeft(searchLeft, searchTop); - const QPointF searchTopRight(searchRight, searchTop); - const QPointF searchBtmRight(searchRight, searchBottom); - const QPointF searchBtmLeft(searchLeft, searchBottom); - const QPointF searchTopMid(searchMidX, searchTop); - const QPointF searchRightMid(searchRight, searchMidY); - const QPointF searchLeftMid(searchLeft, searchMidY); - const QPointF searchBtmMid(searchMidX, searchBottom); - - if ( isNearbyPoint(searchTopLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringOuterTopLeft; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isNearbyPoint(searchTopRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringOuterTopRight; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isNearbyPoint(searchBtmRight, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringOuterBtmRight; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isNearbyPoint(searchBtmLeft, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringOuterBtmLeft; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isNearbyPoint(searchTopMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringOuterTopMid; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isNearbyPoint(searchBtmMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringOuterBtmMid; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isNearbyPoint(searchLeftMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringOuterMidLeft; - _imp->hoverMarker = *it; - hoverProcess = true; - } else if ( isNearbyPoint(searchRightMid, viewer, viewportPos.x(), viewportPos.y(), POINT_TOLERANCE) ) { - _imp->hoverState = eDrawStateHoveringOuterMidRight; - _imp->hoverMarker = *it; - hoverProcess = true; - } - } - - if (hoverProcess) { - break; - } - } // for (std::vector::iterator it = allMarkers.begin(); it!=allMarkers.end(); ++it) { - - if ( _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->isNearbySelectedMarkerTextureResizeAnchor(pos) ) { - _imp->viewer->getViewer()->setCursor(Qt::SizeFDiagCursor); - hoverProcess = true; - } else if ( _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->isInsideSelectedMarkerTextureResizeAnchor(pos) ) { - _imp->viewer->getViewer()->setCursor(Qt::SizeAllCursor); - hoverProcess = true; - } else if ( _imp->showMarkerTexture && (_imp->isInsideKeyFrameTexture(time, pos, viewportPos) != INT_MAX) ) { - _imp->viewer->getViewer()->setCursor(Qt::PointingHandCursor); - hoverProcess = true; - } else { - _imp->viewer->getViewer()->unsetCursor(); - } - - if ( _imp->showMarkerTexture && _imp->selectedMarkerTexture && _imp->shiftDown && _imp->isInsideSelectedMarkerTextureResizeAnchor(pos) ) { - _imp->hoverState = eDrawStateShowScalingHint; - hoverProcess = true; - } - - if (hoverProcess) { - didSomething = true; - } - - boost::shared_ptr centerKnob, offsetKnob, searchWndTopRight, searchWndBtmLeft; - boost::shared_ptr patternCorners[4]; - if (_imp->interactMarker) { - centerKnob = _imp->interactMarker->getCenterKnob(); - offsetKnob = _imp->interactMarker->getOffsetKnob(); - - /* - - TopLeft(0) ------------- Top right(3) - | | - | | - | | - Btm left (1) ------------ Btm right (2) - - */ - patternCorners[0] = _imp->interactMarker->getPatternTopLeftKnob(); - patternCorners[1] = _imp->interactMarker->getPatternBtmLeftKnob(); - patternCorners[2] = _imp->interactMarker->getPatternBtmRightKnob(); - patternCorners[3] = _imp->interactMarker->getPatternTopRightKnob(); - searchWndTopRight = _imp->interactMarker->getSearchWindowTopRightKnob(); - searchWndBtmLeft = _imp->interactMarker->getSearchWindowBottomLeftKnob(); - } - - - switch (_imp->eventState) { - case eMouseStateDraggingCenter: - case eMouseStateDraggingOffset: { - assert(_imp->interactMarker); - if (_imp->eventState == eMouseStateDraggingOffset) { - offsetKnob->setValues(offsetKnob->getValueAtTime(time, 0) + delta.x, - offsetKnob->getValueAtTime(time, 1) + delta.y, - view, - eValueChangedReasonPluginEdited); - } else { - centerKnob->setValuesAtTime(time, centerKnob->getValueAtTime(time, 0) + delta.x, - centerKnob->getValueAtTime(time, 1) + delta.y, - view, - eValueChangedReasonPluginEdited); - for (int i = 0; i < 4; ++i) { - for (int d = 0; d < patternCorners[i]->getDimension(); ++d) { - patternCorners[i]->setValueAtTime(time, patternCorners[i]->getValueAtTime(time, d), view, d); - } - } - } - updateSelectedMarkerTexture(); - if ( _imp->createKeyOnMoveButton->isChecked() ) { - _imp->interactMarker->setUserKeyframe(time); - } - didSomething = true; - break; - } - case eMouseStateDraggingInnerBtmLeft: - case eMouseStateDraggingInnerTopRight: - case eMouseStateDraggingInnerTopLeft: - case eMouseStateDraggingInnerBtmRight: { - if (_imp->controlDown == 0) { - _imp->transformPattern(time, _imp->eventState, delta); - didSomething = true; - break; - } - - int index; - if (_imp->eventState == eMouseStateDraggingInnerBtmLeft) { - index = 1; - } else if (_imp->eventState == eMouseStateDraggingInnerBtmRight) { - index = 2; - } else if (_imp->eventState == eMouseStateDraggingInnerTopRight) { - index = 3; - } else if (_imp->eventState == eMouseStateDraggingInnerTopLeft) { - index = 0; - } - - int nextIndex = (index + 1) % 4; - int prevIndex = (index + 3) % 4; - int diagIndex = (index + 2) % 4; - Point center; - center.x = centerKnob->getValueAtTime(time, 0); - center.y = centerKnob->getValueAtTime(time, 1); - Point offset; - offset.x = offsetKnob->getValueAtTime(time, 0); - offset.y = offsetKnob->getValueAtTime(time, 1); - - Point cur, prev, next, diag; - cur.x = patternCorners[index]->getValueAtTime(time, 0) + delta.x + center.x + offset.x;; - cur.y = patternCorners[index]->getValueAtTime(time, 1) + delta.y + center.y + offset.y; - - prev.x = patternCorners[prevIndex]->getValueAtTime(time, 0) + center.x + offset.x;; - prev.y = patternCorners[prevIndex]->getValueAtTime(time, 1) + center.y + offset.y; - - next.x = patternCorners[nextIndex]->getValueAtTime(time, 0) + center.x + offset.x;; - next.y = patternCorners[nextIndex]->getValueAtTime(time, 1) + center.y + offset.y; - - diag.x = patternCorners[diagIndex]->getValueAtTime(time, 0) + center.x + offset.x;; - diag.y = patternCorners[diagIndex]->getValueAtTime(time, 1) + center.y + offset.y; - - Point nextVec; - nextVec.x = next.x - cur.x; - nextVec.y = next.y - cur.y; - - Point prevVec; - prevVec.x = cur.x - prev.x; - prevVec.y = cur.y - prev.y; - - Point nextDiagVec, prevDiagVec; - prevDiagVec.x = diag.x - next.x; - prevDiagVec.y = diag.y - next.y; - - nextDiagVec.x = prev.x - diag.x; - nextDiagVec.y = prev.y - diag.y; - - //Clamp so the 4 points remaing the same in the homography - if (prevVec.x * nextVec.y - prevVec.y * nextVec.x < 0.) { // cross-product - findLineIntersection(cur, prev, next, &cur); - } - if (nextDiagVec.x * prevVec.y - nextDiagVec.y * prevVec.x < 0.) { // cross-product - findLineIntersection(cur, prev, diag, &cur); - } - if (nextVec.x * prevDiagVec.y - nextVec.y * prevDiagVec.x < 0.) { // cross-product - findLineIntersection(cur, next, diag, &cur); - } - - - Point searchWindowCorners[2]; - searchWindowCorners[0].x = searchWndBtmLeft->getValueAtTime(time, 0) + center.x + offset.x; - searchWindowCorners[0].y = searchWndBtmLeft->getValueAtTime(time, 1) + center.y + offset.y; - - searchWindowCorners[1].x = searchWndTopRight->getValueAtTime(time, 0) + center.x + offset.x; - searchWindowCorners[1].y = searchWndTopRight->getValueAtTime(time, 1) + center.y + offset.y; - - cur.x = std::max(std::min(cur.x, searchWindowCorners[1].x), searchWindowCorners[0].x); - cur.y = std::max(std::min(cur.y, searchWindowCorners[1].y), searchWindowCorners[0].y); - - cur.x -= (center.x + offset.x); - cur.y -= (center.y + offset.y); - - patternCorners[index]->setValuesAtTime(time, cur.x, cur.y, view, eValueChangedReasonNatronInternalEdited); - - if ( _imp->createKeyOnMoveButton->isChecked() ) { - _imp->interactMarker->setUserKeyframe(time); - } - didSomething = true; - break; - } - case eMouseStateDraggingOuterBtmLeft: { - if (_imp->controlDown == 0) { - _imp->transformPattern(time, _imp->eventState, delta); - didSomething = true; - break; - } - Point center; - center.x = centerKnob->getValueAtTime(time, 0); - center.y = centerKnob->getValueAtTime(time, 1); - Point offset; - offset.x = offsetKnob->getValueAtTime(time, 0); - offset.y = offsetKnob->getValueAtTime(time, 1); - - Point p; - p.x = searchWndBtmLeft->getValueAtTime(time, 0) + center.x + offset.x + delta.x; - p.y = searchWndBtmLeft->getValueAtTime(time, 1) + center.y + offset.y + delta.y; - - Point topLeft; - topLeft.x = patternCorners[0]->getValueAtTime(time, 0) + center.x + offset.x; - topLeft.y = patternCorners[0]->getValueAtTime(time, 1) + center.y + offset.y; - Point btmLeft; - btmLeft.x = patternCorners[1]->getValueAtTime(time, 0) + center.x + offset.x; - btmLeft.y = patternCorners[1]->getValueAtTime(time, 1) + center.y + offset.y; - Point btmRight; - btmRight.y = patternCorners[2]->getValueAtTime(time, 0) + center.x + offset.x; - btmRight.y = patternCorners[2]->getValueAtTime(time, 1) + center.y + offset.y; - Point topRight; - topRight.x = patternCorners[3]->getValueAtTime(time, 0) + center.x + offset.x; - topRight.y = patternCorners[3]->getValueAtTime(time, 1) + center.y + offset.y; - - // test every point: even topRight pattern corner may be on the left of topLeft - p.x = std::min(p.x, topLeft.x); - p.x = std::min(p.x, btmLeft.x); - p.x = std::min(p.x, btmRight.x); - p.x = std::min(p.x, topRight.x); - - p.y = std::min(p.y, topLeft.y); - p.y = std::min(p.y, btmLeft.y); - p.y = std::min(p.y, btmRight.y); - p.y = std::min(p.y, topRight.y); - - p.x -= (center.x + offset.x); - p.y -= (center.y + offset.y); - if ( searchWndBtmLeft->hasAnimation() ) { - searchWndBtmLeft->setValuesAtTime(time, p.x, p.y, view, eValueChangedReasonNatronInternalEdited); - } else { - searchWndBtmLeft->setValues(p.x, p.y, view, eValueChangedReasonNatronInternalEdited); - } - - updateSelectedMarkerTexture(); - didSomething = true; - break; - } - case eMouseStateDraggingOuterBtmRight: { - if (_imp->controlDown == 0) { - _imp->transformPattern(time, _imp->eventState, delta); - didSomething = true; - break; - } - Point center; - center.x = centerKnob->getValueAtTime(time, 0); - center.y = centerKnob->getValueAtTime(time, 1); - Point offset; - offset.x = offsetKnob->getValueAtTime(time, 0); - offset.y = offsetKnob->getValueAtTime(time, 1); - - Point p; - p.x = searchWndTopRight->getValueAtTime(time, 0) + center.x + offset.x + delta.x; - p.y = searchWndBtmLeft->getValueAtTime(time, 1) + center.y + offset.y + delta.y; - - Point topLeft; - topLeft.x = patternCorners[0]->getValueAtTime(time, 0) + center.x + offset.x; - topLeft.y = patternCorners[0]->getValueAtTime(time, 1) + center.y + offset.y; - Point btmLeft; - btmLeft.x = patternCorners[1]->getValueAtTime(time, 0) + center.x + offset.x; - btmLeft.y = patternCorners[1]->getValueAtTime(time, 1) + center.y + offset.y; - Point btmRight; - btmRight.y = patternCorners[2]->getValueAtTime(time, 0) + center.x + offset.x; - btmRight.y = patternCorners[2]->getValueAtTime(time, 1) + center.y + offset.y; - Point topRight; - topRight.x = patternCorners[3]->getValueAtTime(time, 0) + center.x + offset.x; - topRight.y = patternCorners[3]->getValueAtTime(time, 1) + center.y + offset.y; - - // test every point: even topRight pattern corner may be on the left of topLeft - p.x = std::max(p.x, topLeft.x); - p.x = std::max(p.x, btmLeft.x); - p.x = std::max(p.x, btmRight.x); - p.x = std::max(p.x, topRight.x); - - p.y = std::min(p.y, topLeft.y); - p.y = std::min(p.y, btmLeft.y); - p.y = std::min(p.y, btmRight.y); - p.y = std::min(p.y, topRight.y); - - p.x -= (center.x + offset.x); - p.y -= (center.y + offset.y); - if ( searchWndBtmLeft->hasAnimation() ) { - searchWndBtmLeft->setValueAtTime(time, p.y, view, 1); - } else { - searchWndBtmLeft->setValue(p.y, view, 1); - } - if ( searchWndTopRight->hasAnimation() ) { - searchWndTopRight->setValueAtTime(time, p.x, view, 0); - } else { - searchWndTopRight->setValue(p.x, view, 0); - } - - updateSelectedMarkerTexture(); - didSomething = true; - break; - } - case eMouseStateDraggingOuterTopRight: { - if (_imp->controlDown == 0) { - _imp->transformPattern(time, _imp->eventState, delta); - didSomething = true; - break; - } - Point center; - center.x = centerKnob->getValueAtTime(time, 0); - center.y = centerKnob->getValueAtTime(time, 1); - Point offset; - offset.x = offsetKnob->getValueAtTime(time, 0); - offset.y = offsetKnob->getValueAtTime(time, 1); - - Point p; - p.x = searchWndTopRight->getValueAtTime(time, 0) + center.x + offset.x + delta.x; - p.y = searchWndTopRight->getValueAtTime(time, 1) + center.y + offset.y + delta.y; - - Point topLeft; - topLeft.x = patternCorners[0]->getValueAtTime(time, 0) + center.x + offset.x; - topLeft.y = patternCorners[0]->getValueAtTime(time, 1) + center.y + offset.y; - Point btmLeft; - btmLeft.x = patternCorners[1]->getValueAtTime(time, 0) + center.x + offset.x; - btmLeft.y = patternCorners[1]->getValueAtTime(time, 1) + center.y + offset.y; - Point btmRight; - btmRight.y = patternCorners[2]->getValueAtTime(time, 0) + center.x + offset.x; - btmRight.y = patternCorners[2]->getValueAtTime(time, 1) + center.y + offset.y; - Point topRight; - topRight.x = patternCorners[3]->getValueAtTime(time, 0) + center.x + offset.x; - topRight.y = patternCorners[3]->getValueAtTime(time, 1) + center.y + offset.y; - - // test every point: even topRight pattern corner may be on the left of topLeft - p.x = std::max(p.x, topLeft.x); - p.x = std::max(p.x, btmLeft.x); - p.x = std::max(p.x, btmRight.x); - p.x = std::max(p.x, topRight.x); - - p.y = std::max(p.y, topLeft.y); - p.y = std::max(p.y, btmLeft.y); - p.y = std::max(p.y, btmRight.y); - p.y = std::max(p.y, topRight.y); - - p.x -= (center.x + offset.x); - p.y -= (center.y + offset.y); - if ( searchWndTopRight->hasAnimation() ) { - searchWndTopRight->setValuesAtTime(time, p.x, p.y, view, eValueChangedReasonNatronInternalEdited); - } else { - searchWndTopRight->setValues(p.x, p.y, view, eValueChangedReasonNatronInternalEdited); - } - - updateSelectedMarkerTexture(); - didSomething = true; - break; - } - case eMouseStateDraggingOuterTopLeft: { - if (_imp->controlDown == 0) { - _imp->transformPattern(time, _imp->eventState, delta); - didSomething = true; - break; - } - Point center; - center.x = centerKnob->getValueAtTime(time, 0); - center.y = centerKnob->getValueAtTime(time, 1); - Point offset; - offset.x = offsetKnob->getValueAtTime(time, 0); - offset.y = offsetKnob->getValueAtTime(time, 1); - - Point p; - p.x = searchWndBtmLeft->getValueAtTime(time, 0) + center.x + offset.x + delta.x; - p.y = searchWndTopRight->getValueAtTime(time, 1) + center.y + offset.y + delta.y; - - Point topLeft; - topLeft.x = patternCorners[0]->getValueAtTime(time, 0) + center.x + offset.x; - topLeft.y = patternCorners[0]->getValueAtTime(time, 1) + center.y + offset.y; - Point btmLeft; - btmLeft.x = patternCorners[1]->getValueAtTime(time, 0) + center.x + offset.x; - btmLeft.y = patternCorners[1]->getValueAtTime(time, 1) + center.y + offset.y; - Point btmRight; - btmRight.y = patternCorners[2]->getValueAtTime(time, 0) + center.x + offset.x; - btmRight.y = patternCorners[2]->getValueAtTime(time, 1) + center.y + offset.y; - Point topRight; - topRight.x = patternCorners[3]->getValueAtTime(time, 0) + center.x + offset.x; - topRight.y = patternCorners[3]->getValueAtTime(time, 1) + center.y + offset.y; - - // test every point: even topRight pattern corner may be on the left of topLeft - p.x = std::min(p.x, topLeft.x); - p.x = std::min(p.x, btmLeft.x); - p.x = std::min(p.x, btmRight.x); - p.x = std::min(p.x, topRight.x); - - p.y = std::max(p.y, topLeft.y); - p.y = std::max(p.y, btmLeft.y); - p.y = std::max(p.y, btmRight.y); - p.y = std::max(p.y, topRight.y); - - p.x -= (center.x + offset.x); - p.y -= (center.y + offset.y); - if ( searchWndBtmLeft->hasAnimation() ) { - searchWndBtmLeft->setValueAtTime(time, p.x, view, 0); - } else { - searchWndBtmLeft->setValue(p.x, view, 0); - } - if ( searchWndTopRight->hasAnimation() ) { - searchWndTopRight->setValueAtTime(time, p.y, view, 1); - } else { - searchWndTopRight->setValue(p.y, view, 1); - } - - updateSelectedMarkerTexture(); - didSomething = true; - break; - } - case eMouseStateDraggingInnerBtmMid: - case eMouseStateDraggingInnerTopMid: - case eMouseStateDraggingInnerMidLeft: - case eMouseStateDraggingInnerMidRight: - case eMouseStateDraggingOuterBtmMid: - case eMouseStateDraggingOuterTopMid: - case eMouseStateDraggingOuterMidLeft: - case eMouseStateDraggingOuterMidRight: { - _imp->transformPattern(time, _imp->eventState, delta); - didSomething = true; - break; - } - case eMouseStateDraggingSelectedMarkerResizeAnchor: { - QPointF lastPosWidget = viewer->toWidgetCoordinates(_imp->lastMousePos); - double dx = viewportPos.x() - lastPosWidget.x(); - _imp->selectedMarkerWidth += dx; - _imp->selectedMarkerWidth = std::max(_imp->selectedMarkerWidth, 10); - didSomething = true; - break; - } - case eMouseStateScalingSelectedMarker: { - TrackMarkerPtr marker = _imp->selectedMarker.lock(); - assert(marker); - RectD markerMagRect; - _imp->computeSelectedMarkerCanonicalRect(&markerMagRect); - boost::shared_ptr centerKnob = marker->getCenterKnob(); - boost::shared_ptr offsetKnob = marker->getOffsetKnob(); - boost::shared_ptr searchBtmLeft = marker->getSearchWindowBottomLeftKnob(); - boost::shared_ptr searchTopRight = marker->getSearchWindowTopRightKnob(); - Point center, offset, btmLeft, topRight; - center.x = centerKnob->getValueAtTime(time, 0); - center.y = centerKnob->getValueAtTime(time, 1); - offset.x = offsetKnob->getValueAtTime(time, 0); - offset.y = offsetKnob->getValueAtTime(time, 1); - btmLeft.x = searchBtmLeft->getValueAtTime(time, 0) + center.x + offset.x; - btmLeft.y = searchBtmLeft->getValueAtTime(time, 1) + center.y + offset.y; - topRight.x = searchTopRight->getValueAtTime(time, 0) + center.x + offset.x; - topRight.y = searchTopRight->getValueAtTime(time, 1) + center.y + offset.y; - - //Remove any offset to the center to see the marker in the magnification window - double xCenterPercent = (center.x - btmLeft.x + offset.x) / (topRight.x - btmLeft.x); - double yCenterPercent = (center.y - btmLeft.y + offset.y) / (topRight.y - btmLeft.y); - Point centerPoint; - centerPoint.x = markerMagRect.x1 + xCenterPercent * (markerMagRect.x2 - markerMagRect.x1); - centerPoint.y = markerMagRect.y1 + yCenterPercent * (markerMagRect.y2 - markerMagRect.y1); - - double prevDist = std::sqrt( (_imp->lastMousePos.x() - centerPoint.x ) * ( _imp->lastMousePos.x() - centerPoint.x) + ( _imp->lastMousePos.y() - centerPoint.y) * ( _imp->lastMousePos.y() - centerPoint.y) ); - if (prevDist != 0) { - double dist = std::sqrt( ( pos.x() - centerPoint.x) * ( pos.x() - centerPoint.x) + ( pos.y() - centerPoint.y) * ( pos.y() - centerPoint.y) ); - double ratio = dist / prevDist; - _imp->selectedMarkerScale.x *= ratio; - _imp->selectedMarkerScale.x = std::max( 0.05, std::min(1., _imp->selectedMarkerScale.x) ); - _imp->selectedMarkerScale.y = _imp->selectedMarkerScale.x; - didSomething = true; - } - break; - } - case eMouseStateDraggingSelectedMarker: { - double x = centerKnob->getValueAtTime(time, 0); - double y = centerKnob->getValueAtTime(time, 1); - double dx = delta.x * _imp->selectedMarkerScale.x; - double dy = delta.y * _imp->selectedMarkerScale.y; - x -= dx; - y -= dy; - centerKnob->setValuesAtTime(time, x, y, view, eValueChangedReasonPluginEdited); - for (int i = 0; i < 4; ++i) { - for (int d = 0; d < patternCorners[i]->getDimension(); ++d) { - patternCorners[i]->setValueAtTime(time, patternCorners[i]->getValueAtTime(time, d), view, d); - } - } - if ( _imp->createKeyOnMoveButton->isChecked() ) { - _imp->interactMarker->setUserKeyframe(time); - } - updateSelectedMarkerTexture(); - didSomething = true; - break; - } - default: - break; - } // switch - } - if (_imp->clickToAddTrackEnabled) { - ///Refresh the overlay - didSomething = true; - } - _imp->lastMousePos = pos; - - return didSomething; -} //penMotion - -bool -TrackerGui::penUp(double time, - const RenderScale & renderScale, - ViewIdx view, - const QPointF & viewportPos, - const QPointF & pos, - double pressure, - QMouseEvent* /*e*/) -{ - FLAG_DURING_INTERACT - - bool didSomething = false; - - if (_imp->panel) { - if ( _imp->panel->getContext()->onOverlayPenUpInternalNodes( time, renderScale, view, viewportPos, pos, pressure, _imp->viewer->getViewer() ) ) { - return true; - } - } - - - TrackerMouseStateEnum state = _imp->eventState; - _imp->eventState = eMouseStateIdle; - if (_imp->panelv1) { - const std::list > & instances = _imp->panelv1->getInstances(); - - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - NodePtr instance = it->first.lock(); - if ( it->second && !instance->isNodeDisabled() ) { - EffectInstPtr effect = instance->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); - didSomething = effect->onOverlayPenUp_public(time, renderScale, view, viewportPos, pos, pressure); - if (didSomething) { - return true; - } - } - } - } else { // if (_imp->panelv1) { - _imp->interactMarker.reset(); - (void)state; - } // if (_imp->panelv1) { - - return didSomething; -} - -bool -TrackerGui::keyDown(double time, - const RenderScale & renderScale, - ViewIdx view, - QKeyEvent* e) -{ - FLAG_DURING_INTERACT - - bool didSomething = false; - Qt::KeyboardModifiers modifiers = e->modifiers(); - Qt::Key key = (Qt::Key)e->key(); - Key natronKey = QtEnumConvert::fromQtKey(key); - KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers(modifiers); - - if (_imp->panel) { - if ( _imp->panel->getContext()->onOverlayKeyDownInternalNodes( time, renderScale, view, natronKey, natronMod, _imp->viewer->getViewer() ) ) { - return true; - } - } - - if (e->key() == Qt::Key_Control) { - ++_imp->controlDown; - } else if (e->key() == Qt::Key_Shift) { - ++_imp->shiftDown; - } - - - if (_imp->panelv1) { - const std::list > & instances = _imp->panelv1->getInstances(); - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - NodePtr instance = it->first.lock(); - if ( it->second && !instance->isNodeDisabled() ) { - EffectInstPtr effect = instance->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); - didSomething = effect->onOverlayKeyDown_public(time, renderScale, view, natronKey, natronMod); - if (didSomething) { - return true; - } - } - } - } - - if ( modCASIsControlAlt(e) && ( (e->key() == Qt::Key_Control) || (e->key() == Qt::Key_Alt) ) ) { - _imp->clickToAddTrackEnabled = true; - _imp->addTrackButton->setDown(true); - _imp->addTrackButton->setChecked(true); - didSomething = true; - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingSelectAll, modifiers, key) ) { - if (_imp->panelv1) { - _imp->panelv1->onSelectAllButtonClicked(); - std::list selectedInstances; - _imp->panelv1->getSelectedInstances(&selectedInstances); - didSomething = !selectedInstances.empty(); - } else { - _imp->panel->getContext()->selectAll(TrackerContext::eTrackSelectionInternal); - didSomething = false; //viewer is refreshed already - } - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingDelete, modifiers, key) ) { - if (_imp->panelv1) { - _imp->panelv1->onDeleteKeyPressed(); - std::list selectedInstances; - _imp->panelv1->getSelectedInstances(&selectedInstances); - didSomething = !selectedInstances.empty(); - } else { - _imp->panel->onRemoveButtonClicked(); - didSomething = true; - } - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingBackward, modifiers, key) ) { - onTrackBwClicked(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingPrevious, modifiers, key) ) { - onTrackPrevClicked(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingNext, modifiers, key) ) { - onTrackNextClicked(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingForward, modifiers, key) ) { - onTrackFwClicked(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingStop, modifiers, key) ) { - onStopButtonClicked(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingRange, modifiers, key) ) { - onTrackRangeClicked(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingAllKeyframes, modifiers, key) ) { - onTrackAllKeyframesClicked(); - didSomething = true; - } else if ( isKeybind(kShortcutGroupTracking, kShortcutIDActionTrackingCurrentKeyframes, modifiers, key) ) { - onTrackCurrentKeyframeClicked(); - didSomething = true; - } - - - return didSomething; -} // keyDown - -bool -TrackerGui::keyUp(double time, - const RenderScale & renderScale, - ViewIdx view, - QKeyEvent* e) -{ - FLAG_DURING_INTERACT - - Key natronKey = QtEnumConvert::fromQtKey( (Qt::Key)e->key() ); - KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers( e->modifiers() ); - - if (_imp->panel) { - if ( _imp->panel->getContext()->onOverlayKeyUpInternalNodes( time, renderScale, view, natronKey, natronMod, _imp->viewer->getViewer() ) ) { - return true; - } - } - - bool didSomething = false; - - if (e->key() == Qt::Key_Control) { - if (_imp->controlDown > 0) { - --_imp->controlDown; - } - } else if (e->key() == Qt::Key_Shift) { - if (_imp->shiftDown > 0) { - --_imp->shiftDown; - } - if (_imp->eventState == eMouseStateScalingSelectedMarker) { - _imp->eventState = eMouseStateIdle; - didSomething = true; - } - } - - if (_imp->panelv1) { - const std::list > & instances = _imp->panelv1->getInstances(); - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - NodePtr instance = it->first.lock(); - if ( it->second && !instance->isNodeDisabled() ) { - EffectInstPtr effect = instance->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); - didSomething = effect->onOverlayKeyUp_public(time, renderScale, view, natronKey, natronMod); - if (didSomething) { - return true; - } - } - } - } - if ( _imp->clickToAddTrackEnabled && ( (e->key() == Qt::Key_Control) || (e->key() == Qt::Key_Alt) ) ) { - _imp->clickToAddTrackEnabled = false; - _imp->addTrackButton->setDown(false); - _imp->addTrackButton->setChecked(false); - didSomething = true; - } - - return didSomething; -} // TrackerGui::keyUp - -bool -TrackerGui::gainFocus(double time, - const RenderScale & renderScale, - ViewIdx view) -{ - bool didSomething = false; - - if (_imp->panel) { - if ( _imp->panel->getContext()->onOverlayFocusGainedInternalNodes( time, renderScale, view, _imp->viewer->getViewer() ) ) { - didSomething = true; - } - } - - return didSomething; -} - -bool -TrackerGui::loseFocus(double time, - const RenderScale & renderScale, - ViewIdx view) -{ - bool didSomething = false; - - if (_imp->panel) { - if ( _imp->panel->getContext()->onOverlayFocusLostInternalNodes( time, renderScale, view, _imp->viewer->getViewer() ) ) { - didSomething = true; - } - } - - - _imp->controlDown = 0; - _imp->shiftDown = 0; - - if (_imp->panelv1) { - const std::list > & instances = _imp->panelv1->getInstances(); - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - NodePtr instance = it->first.lock(); - if ( it->second && !instance->isNodeDisabled() ) { - EffectInstPtr effect = instance->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public( _imp->viewer->getViewer() ); - didSomething |= effect->onOverlayFocusLost_public(time, renderScale, view); - } - } - } - - return didSomething; -} - -void -TrackerGui::updateSelectionFromSelectionRectangle(bool onRelease) -{ - if (!onRelease) { - return; - } - - - double l, r, b, t; - _imp->viewer->getViewer()->getSelectionRectangle(l, r, b, t); - - if (_imp->panelv1) { - std::list currentSelection; - const std::list > & instances = _imp->panelv1->getInstances(); - for (std::list >::const_iterator it = instances.begin(); it != instances.end(); ++it) { - NodePtr instance = it->first.lock(); - boost::shared_ptr newInstanceKnob = instance->getKnobByName("center"); - assert(newInstanceKnob); //< if it crashes here that means the parameter's name changed in the OpenFX plug-in. - KnobDouble* dblKnob = dynamic_cast( newInstanceKnob.get() ); - assert(dblKnob); - double x, y; - x = dblKnob->getValue(0); - y = dblKnob->getValue(1); - if ( (x >= l) && (x <= r) && (y >= b) && (y <= t) ) { - ///assert that the node is really not part of the selection - assert( std::find( currentSelection.begin(), currentSelection.end(), instance.get() ) == currentSelection.end() ); - currentSelection.push_back( instance.get() ); - } - } - _imp->panelv1->selectNodes( currentSelection, (_imp->controlDown > 0) ); - } else { - std::vector allMarkers; - std::list selectedMarkers; - boost::shared_ptr context = _imp->panel->getContext(); - context->getAllMarkers(&allMarkers); - for (std::size_t i = 0; i < allMarkers.size(); ++i) { - if ( !allMarkers[i]->isEnabled( allMarkers[i]->getCurrentTime() ) ) { - continue; - } - boost::shared_ptr center = allMarkers[i]->getCenterKnob(); - double x, y; - x = center->getValue(0); - y = center->getValue(1); - if ( (x >= l) && (x <= r) && (y >= b) && (y <= t) ) { - selectedMarkers.push_back(allMarkers[i]); - } - } - - context->beginEditSelection(TrackerContext::eTrackSelectionInternal); - context->clearSelection(TrackerContext::eTrackSelectionInternal); - context->addTracksToSelection(selectedMarkers, TrackerContext::eTrackSelectionInternal); - context->endEditSelection(TrackerContext::eTrackSelectionInternal); - } -} // TrackerGui::updateSelectionFromSelectionRectangle - -void -TrackerGui::onSelectionCleared() -{ - if (_imp->panelv1) { - _imp->panelv1->clearSelection(); - } else { - _imp->panel->getContext()->clearSelection(TrackerContext::eTrackSelectionViewer); - } -} - -void -TrackerGui::onTrackRangeClicked() -{ - SequenceTime timelineFirst, timelineLast; - - _imp->viewer->getTimelineBounds(&timelineFirst, &timelineLast); - - NATRON_PYTHON_NAMESPACE::PyModalDialog dialog( _imp->viewer->getGui() ); - boost::shared_ptr firstFrame( dialog.createIntParam( QString::fromUtf8("firstFrame"), QString::fromUtf8("First frame") ) ); - firstFrame->set(_imp->lastTrackRangeFirstFrame != INT_MIN ? _imp->lastTrackRangeFirstFrame : timelineFirst); - firstFrame->setAnimationEnabled(false); - boost::shared_ptr lastFrame( dialog.createIntParam( QString::fromUtf8("lastFrame"), QString::fromUtf8("Last frame") ) ); - lastFrame->set(_imp->lastTrackRangeLastFrame != INT_MIN ? _imp->lastTrackRangeLastFrame : timelineLast); - lastFrame->setAnimationEnabled(false); - boost::shared_ptr stepFrame( dialog.createIntParam( QString::fromUtf8("step"), QString::fromUtf8("Step") ) ); - stepFrame->setAnimationEnabled(false); - stepFrame->set(_imp->lastTrackRangeStep != INT_MIN ? _imp->lastTrackRangeStep : 1); - dialog.refreshUserParamsGUI(); - if ( dialog.exec() ) { - int first = firstFrame->getValue(); - int last = lastFrame->getValue(); - int step = stepFrame->getValue(); - boost::shared_ptr ctx = _imp->panel->getContext(); - if ( ctx->isCurrentlyTracking() ) { - ctx->abortTracking(); - } - - if (step == 0) { - Dialogs::errorDialog( tr("Track Range").toStdString(), tr("The Step cannot be 0").toStdString() ); - - return; - } - - int startFrame = step > 0 ? first : last; - int lastFrame = step > 0 ? last + 1 : first - 1; - - if ( ( (step > 0) && (startFrame >= lastFrame) ) || ( (step < 0) && (startFrame <= lastFrame) ) ) { - return; - } - - _imp->lastTrackRangeStep = step; - _imp->lastTrackRangeFirstFrame = first; - _imp->lastTrackRangeLastFrame = last; - - ctx->trackSelectedMarkers( startFrame, lastFrame, step, _imp->viewer->getInternalNode() ); - } -} - -void -TrackerGui::onTrackAllKeyframesClicked() -{ - boost::shared_ptr ctx = _imp->panel->getContext(); - std::list selectedMarkers; - - ctx->getSelectedMarkers(&selectedMarkers); - - std::set userKeys; - - for (std::list::iterator it = selectedMarkers.begin(); it != selectedMarkers.end(); ++it) { - std::set trackUserKeys; - (*it)->getUserKeyframes(&trackUserKeys); - userKeys.insert( trackUserKeys.begin(), trackUserKeys.end() ); - } - if ( userKeys.empty() ) { - return; - } - - int first = *userKeys.begin(); - int last = *userKeys.rbegin() + 1; - ctx->trackSelectedMarkers( first, last, 1, _imp->viewer->getInternalNode() ); -} - -void -TrackerGui::onTrackCurrentKeyframeClicked() -{ - boost::shared_ptr ctx = _imp->panel->getContext(); - SequenceTime currentFrame = _imp->viewer->getTimeLine()->currentFrame(); - std::list selectedMarkers; - - ctx->getSelectedMarkers(&selectedMarkers); - - std::set userKeys; - - for (std::list::iterator it = selectedMarkers.begin(); it != selectedMarkers.end(); ++it) { - std::set trackUserKeys; - (*it)->getUserKeyframes(&trackUserKeys); - userKeys.insert( trackUserKeys.begin(), trackUserKeys.end() ); - } - if ( userKeys.empty() ) { - return; - } - - std::set::iterator it = userKeys.lower_bound(currentFrame); - if ( it == userKeys.end() ) { - return; - } - - int last = *it + 1; - int first; - if ( it == userKeys.begin() ) { - first = *it; - } else { - --it; - first = *it; - } - - ctx->trackSelectedMarkers( first, last, 1, _imp->viewer->getInternalNode() ); -} - -void -TrackerGui::onTrackBwClicked() -{ - _imp->trackBwButton->setDown(false); - _imp->trackBwButton->setChecked(false); - - if (_imp->panelv1) { - if ( _imp->panelv1->isTracking() ) { - _imp->panelv1->stopTracking(); - - return; - } - if ( !_imp->panelv1->trackBackward( _imp->viewer->getInternalNode() ) ) { - _imp->panelv1->stopTracking(); - } - } else { - boost::shared_ptr timeline = _imp->viewer->getGui()->getApp()->getTimeLine(); - int startFrame = timeline->currentFrame(); - SequenceTime first, last; - _imp->viewer->getTimelineBounds(&first, &last); - boost::shared_ptr ctx = _imp->panel->getContext(); - if ( ctx->isCurrentlyTracking() ) { - ctx->abortTracking(); - } else { - ctx->trackSelectedMarkers( startFrame, first - 1, false, _imp->viewer->getInternalNode() ); - } - } -} - -void -TrackerGui::onTrackPrevClicked() -{ - if (_imp->panelv1) { - _imp->panelv1->trackPrevious( _imp->viewer->getInternalNode() ); - } else { - boost::shared_ptr timeline = _imp->viewer->getGui()->getApp()->getTimeLine(); - int startFrame = timeline->currentFrame(); - boost::shared_ptr ctx = _imp->panel->getContext(); - ctx->trackSelectedMarkers( startFrame, startFrame - 2, false, _imp->viewer->getInternalNode() ); - } -} - -void -TrackerGui::onStopButtonClicked() -{ - _imp->trackBwButton->setDown(false); - _imp->trackFwButton->setDown(false); - if (_imp->panelv1) { - _imp->panelv1->stopTracking(); - } else { - _imp->panel->getContext()->abortTracking(); - } -} - -void -TrackerGui::onTrackNextClicked() -{ - if (_imp->panelv1) { - _imp->panelv1->trackNext( _imp->viewer->getInternalNode() ); - } else { - int startFrame = _imp->viewer->getGui()->getApp()->getTimeLine()->currentFrame(); - boost::shared_ptr ctx = _imp->panel->getContext(); - ctx->trackSelectedMarkers( startFrame, startFrame + 2, true, _imp->viewer->getInternalNode() ); - } -} - -void -TrackerGui::onTrackFwClicked() -{ - _imp->trackFwButton->setDown(false); - _imp->trackFwButton->setChecked(false); - if (_imp->panelv1) { - if ( _imp->panelv1->isTracking() ) { - _imp->panelv1->stopTracking(); - - return; - } - - if ( !_imp->panelv1->trackForward( _imp->viewer->getInternalNode() ) ) { - _imp->panelv1->stopTracking(); - } - } else { - boost::shared_ptr timeline = _imp->viewer->getGui()->getApp()->getTimeLine(); - int startFrame = timeline->currentFrame(); - SequenceTime first, last; - _imp->viewer->getTimelineBounds(&first, &last); - boost::shared_ptr ctx = _imp->panel->getContext(); - if ( ctx->isCurrentlyTracking() ) { - ctx->abortTracking(); - } else { - ctx->trackSelectedMarkers( startFrame, last + 1, true, _imp->viewer->getInternalNode() ); - } - } -} - -void -TrackerGui::onUpdateViewerClicked(bool clicked) -{ - _imp->updateViewerButton->setDown(clicked); - _imp->updateViewerButton->setChecked(clicked); - if (_imp->panelv1) { - _imp->panelv1->setUpdateViewer(clicked); - } else { - _imp->panel->getContext()->setUpdateViewer(clicked); - } -} - -void -TrackerGui::onTrackingStarted(int step) -{ - _imp->isTracking = true; - if (step > 0) { - _imp->trackFwButton->setChecked(true); - _imp->trackFwButton->setDown(true); - } else { - _imp->trackBwButton->setChecked(true); - _imp->trackBwButton->setDown(true); - } -} - -void -TrackerGui::onTrackingEnded() -{ - _imp->trackBwButton->setChecked(false); - _imp->trackFwButton->setChecked(false); - _imp->trackBwButton->setDown(false); - _imp->trackFwButton->setDown(false); - _imp->isTracking = false; - _imp->viewer->getViewer()->redraw(); -} - -void -TrackerGui::onClearAllAnimationClicked() -{ - if (_imp->panelv1) { - _imp->panelv1->clearAllAnimationForSelection(); - } else { - std::list markers; - _imp->panel->getContext()->getSelectedMarkers(&markers); - for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { - (*it)->clearAnimation(); - } - } -} - -void -TrackerGui::onClearBwAnimationClicked() -{ - if (_imp->panelv1) { - _imp->panelv1->clearBackwardAnimationForSelection(); - } else { - int time = _imp->panel->getContext()->getNode()->getApp()->getTimeLine()->currentFrame(); - std::list markers; - _imp->panel->getContext()->getSelectedMarkers(&markers); - for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { - (*it)->clearAnimationBeforeTime(time); - } - } -} - -void -TrackerGui::onClearFwAnimationClicked() -{ - if (_imp->panelv1) { - _imp->panelv1->clearForwardAnimationForSelection(); - } else { - int time = _imp->panel->getContext()->getNode()->getApp()->getTimeLine()->currentFrame(); - std::list markers; - _imp->panel->getContext()->getSelectedMarkers(&markers); - for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { - (*it)->clearAnimationAfterTime(time); - } - } -} - -void -TrackerGui::onCreateKeyOnMoveButtonClicked(bool clicked) -{ - _imp->createKeyOnMoveButton->setDown(clicked); - _imp->createKeyOnMoveButton->setChecked(clicked); -} - -void -TrackerGui::onShowCorrelationButtonClicked(bool clicked) -{ - _imp->showCorrelationButton->setDown(clicked); - _imp->viewer->getViewer()->redraw(); -} - -void -TrackerGui::onCenterViewerButtonClicked(bool clicked) -{ - _imp->centerViewerButton->setDown(clicked); - if (_imp->panelv1) { - _imp->panelv1->setCenterOnTrack(clicked); - } else { - _imp->panel->getContext()->setCenterOnTrack(clicked); - } -} - -void -TrackerGui::onSetKeyframeButtonClicked() -{ - int time = _imp->panel->getNode()->getNode()->getApp()->getTimeLine()->currentFrame(); - std::list markers; - - _imp->panel->getContext()->getSelectedMarkers(&markers); - for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { - (*it)->setUserKeyframe(time); - } -} - -void -TrackerGui::onRemoveKeyframeButtonClicked() -{ - int time = _imp->panel->getNode()->getNode()->getApp()->getTimeLine()->currentFrame(); - std::list markers; - - _imp->panel->getContext()->getSelectedMarkers(&markers); - for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { - (*it)->removeUserKeyframe(time); - } -} - -void -TrackerGui::onResetOffsetButtonClicked() -{ - std::list markers; - - _imp->panel->getContext()->getSelectedMarkers(&markers); - for (std::list::iterator it = markers.begin(); it != markers.end(); ++it) { - boost::shared_ptr offsetKnob = (*it)->getOffsetKnob(); - assert(offsetKnob); - for (int i = 0; i < offsetKnob->getDimension(); ++i) { - offsetKnob->resetToDefaultValue(i); - } - } - _imp->viewer->getGui()->redrawAllViewers(); -} - -void -TrackerGui::onResetTrackButtonClicked() -{ - _imp->panel->onResetButtonClicked(); -} - -void -TrackerGui::onContextSelectionChanged(int reason) -{ - std::list selection; - - _imp->panel->getContext()->getSelectedMarkers(&selection); - if ( selection.empty() || (selection.size() > 1) ) { - _imp->showMarkerTexture = false; - } else { - assert(selection.size() == 1); - - const TrackMarkerPtr& selectionFront = selection.front(); - TrackMarkerPtr oldMarker = _imp->selectedMarker.lock(); - if (oldMarker != selectionFront) { - _imp->selectedMarker = selectionFront; - _imp->refreshSelectedMarkerTexture(); - - - std::set keys; - selectionFront->getUserKeyframes(&keys); - for (std::set::iterator it2 = keys.begin(); it2 != keys.end(); ++it2) { - _imp->makeMarkerKeyTexture(*it2, selectionFront); - } - } else { - if (selectionFront) { - _imp->showMarkerTexture = true; - } - } - } - if ( (TrackerContext::TrackSelectionReason)reason == TrackerContext::eTrackSelectionViewer ) { - return; - } - - _imp->viewer->getViewer()->redraw(); -} - -void -TrackerGui::onTimelineTimeChanged(SequenceTime /*time*/, - int reason) -{ - if ( _imp->showMarkerTexture && (reason != eTimelineChangeReasonPlaybackSeek) ) { - _imp->refreshSelectedMarkerTexture(); - } -} - -static unsigned int toBGRA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) WARN_UNUSED_RETURN; -unsigned int -toBGRA(unsigned char r, - unsigned char g, - unsigned char b, - unsigned char a) -{ - return (a << 24) | (r << 16) | (g << 8) | b; -} - -void -TrackerGui::onTrackImageRenderingFinished() -{ - assert( QThread::currentThread() == qApp->thread() ); - QFutureWatcher, RectI> >* future = dynamic_cast, RectI> >*>( sender() ); - assert(future); - std::pair, RectI> ret = future->result(); - - - _imp->viewer->getViewer()->makeOpenGLcontextCurrent(); - _imp->showMarkerTexture = true; - if (!_imp->selectedMarkerTexture) { - _imp->selectedMarkerTexture.reset( new Texture(GL_TEXTURE_2D, GL_LINEAR, GL_NEAREST, GL_CLAMP_TO_EDGE) ); - } - _imp->selectedMarkerTextureTime = (int)ret.first->getTime(); - _imp->selectedMarkerTextureRoI = ret.second; - - _imp->convertImageTosRGBOpenGLTexture(ret.first, _imp->selectedMarkerTexture, ret.second); - - - _imp->viewer->getViewer()->redraw(); -} - -void -TrackerGui::onKeyFrameImageRenderingFinished() -{ - assert( QThread::currentThread() == qApp->thread() ); - TrackWatcher* future = dynamic_cast( sender() ); - assert(future); - std::pair, RectI> ret = future->result(); - if ( !ret.first || ret.second.isNull() ) { - return; - } - - _imp->viewer->getViewer()->makeOpenGLcontextCurrent(); - - for (TrackKeyframeRequests::iterator it = _imp->trackRequestsMap.begin(); it != _imp->trackRequestsMap.end(); ++it) { - if (it->second.get() == future) { - TrackMarkerPtr track = it->first.track.lock(); - if (!track) { - return; - } - TrackerGuiPrivate::KeyFrameTexIDs& keyTextures = _imp->trackTextures[track]; - GLTexturePtr tex( new Texture(GL_TEXTURE_2D, GL_LINEAR, GL_NEAREST, GL_CLAMP_TO_EDGE) ); - keyTextures[it->first.time] = tex; - _imp->convertImageTosRGBOpenGLTexture(ret.first, tex, ret.second); - - _imp->trackRequestsMap.erase(it); - - _imp->viewer->getViewer()->redraw(); - - return; - } - } - assert(false); -} - -void -TrackerGuiPrivate::convertImageTosRGBOpenGLTexture(const boost::shared_ptr& image, - const boost::shared_ptr& tex, - const RectI& renderWindow) -{ - RectI bounds; - RectI roi; - - if (image) { - bounds = image->getBounds(); - renderWindow.intersect(bounds, &roi); - } else { - bounds = renderWindow; - roi = bounds; - } - if ( roi.isNull() ) { - return; - } - - - std::size_t bytesCount = 4 * sizeof(unsigned char) * roi.area(); - TextureRect region; - region.x1 = roi.x1; - region.x2 = roi.x2; - region.y1 = roi.y1; - region.y2 = roi.y2; - - GLint currentBoundPBO = 0; - glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, ¤tBoundPBO); - - glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, pboID ); - glBufferDataARB(GL_PIXEL_UNPACK_BUFFER_ARB, bytesCount, NULL, GL_DYNAMIC_DRAW_ARB); - GLvoid *buf = glMapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY_ARB); - glCheckError(); - assert(buf); - - if (!image) { - int pixelsCount = roi.area(); - unsigned int* dstPixels = (unsigned int*)buf; - for (int i = 0; i < pixelsCount; ++i, ++dstPixels) { - *dstPixels = toBGRA(0, 0, 0, 255); - } - } else { - int srcNComps = (int)image->getComponentsCount(); - assert(srcNComps >= 3); - Image::ReadAccess acc( image.get() ); - const float* srcPixels = (const float*)acc.pixelAt(roi.x1, roi.y1); - unsigned int* dstPixels = (unsigned int*)buf; - assert(srcPixels); - - int w = roi.width(); - int srcRowElements = bounds.width() * srcNComps; - const Color::Lut* lut = Color::LutManager::sRGBLut(); - lut->validate(); - assert(lut); - - unsigned char alpha = 255; - - for (int y = roi.y1; y < roi.y2; ++y, dstPixels += w, srcPixels += srcRowElements) { - int start = (int)( rand() % (roi.x2 - roi.x1) ); - - for (int backward = 0; backward < 2; ++backward) { - int index = backward ? start - 1 : start; - assert( backward == 1 || ( index >= 0 && index < (roi.x2 - roi.x1) ) ); - unsigned error_r = 0x80; - unsigned error_g = 0x80; - unsigned error_b = 0x80; - - while (index < w && index >= 0) { - float r = srcPixels[index * srcNComps]; - float g = srcPixels[index * srcNComps + 1]; - float b = srcPixels[index * srcNComps + 2]; - - error_r = (error_r & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(r); - error_g = (error_g & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(g); - error_b = (error_b & 0xff) + lut->toColorSpaceUint8xxFromLinearFloatFast(b); - assert(error_r < 0x10000 && error_g < 0x10000 && error_b < 0x10000); - - dstPixels[index] = toBGRA( (U8)(error_r >> 8), - (U8)(error_g >> 8), - (U8)(error_b >> 8), - alpha ); - - - if (backward) { - --index; - } else { - ++index; - } - } - } - } - } - - glUnmapBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB); - glCheckError(); - tex->fillOrAllocateTexture(region, Texture::eDataTypeByte, RectI(), false); - - glBindBufferARB(GL_PIXEL_UNPACK_BUFFER_ARB, currentBoundPBO); - - glCheckError(); -} // TrackerGuiPrivate::convertImageTosRGBOpenGLTexture - -void -TrackerGui::updateSelectedMarkerTexture() -{ - _imp->refreshSelectedMarkerTexture(); -} - -void -TrackerGui::onTrackerInputChanged(int /*inputNb*/) -{ - _imp->refreshSelectedMarkerTexture(); -} - -void -TrackerGuiPrivate::refreshSelectedMarkerTexture() -{ - assert( QThread::currentThread() == qApp->thread() ); - if (isTracking) { - return; - } - TrackMarkerPtr marker = selectedMarker.lock(); - if (!marker) { - return; - } - - int time = panel->getNode()->getNode()->getApp()->getTimeLine()->currentFrame(); - RectI roi = marker->getMarkerImageRoI(time); - if ( roi.isNull() ) { - return; - } - ImagePtr existingMarkerImg = selectedMarkerImg.lock(); - if ( existingMarkerImg && (existingMarkerImg->getTime() == time) && (roi == selectedMarkerTextureRoI) ) { - return; - } - - selectedMarkerImg.reset(); - - imageGetterWatcher.reset( new TrackWatcher() ); - QObject::connect( imageGetterWatcher.get(), SIGNAL(finished()), _publicInterface, SLOT(onTrackImageRenderingFinished()) ); - imageGetterWatcher->setFuture( QtConcurrent::run(marker.get(), &TrackMarker::getMarkerImage, time, roi) ); -} - -void -TrackerGuiPrivate::makeMarkerKeyTexture(int time, - const TrackMarkerPtr& track) -{ - assert( QThread::currentThread() == qApp->thread() ); - TrackRequestKey k; - k.time = time; - k.track = track; - k.roi = track->getMarkerImageRoI(time); - - TrackKeysMap::iterator foundTrack = trackTextures.find(track); - if ( foundTrack != trackTextures.end() ) { - KeyFrameTexIDs::iterator foundKey = foundTrack->second.find(k.time); - if ( foundKey != foundTrack->second.end() ) { - const TextureRect& texRect = foundKey->second->getTextureRect(); - if ( (k.roi.x1 == texRect.x1) && - ( k.roi.x2 == texRect.x2) && - ( k.roi.y1 == texRect.y1) && - ( k.roi.y2 == texRect.y2) ) { - return; - } - } - } - - if ( !k.roi.isNull() ) { - TrackWatcherPtr watcher( new TrackWatcher() ); - QObject::connect( watcher.get(), SIGNAL(finished()), _publicInterface, SLOT(onKeyFrameImageRenderingFinished()) ); - trackRequestsMap[k] = watcher; - watcher->setFuture( QtConcurrent::run(track.get(), &TrackMarker::getMarkerImage, time, k.roi) ); - } -} - -void -TrackerGui::onKeyframeSetOnTrack(const TrackMarkerPtr& marker, - int key) -{ - _imp->makeMarkerKeyTexture(key, marker); -} - -void -TrackerGui::onKeyframeRemovedOnTrack(const TrackMarkerPtr& marker, - int key) -{ - for (TrackerGuiPrivate::TrackKeysMap::iterator it = _imp->trackTextures.begin(); it != _imp->trackTextures.end(); ++it) { - if (it->first.lock() == marker) { - std::map >::iterator found = it->second.find(key); - if ( found != it->second.end() ) { - it->second.erase(found); - } - break; - } - } - _imp->viewer->getViewer()->redraw(); -} - -void -TrackerGui::onAllKeyframesRemovedOnTrack(const TrackMarkerPtr& marker) -{ - for (TrackerGuiPrivate::TrackKeysMap::iterator it = _imp->trackTextures.begin(); it != _imp->trackTextures.end(); ++it) { - if (it->first.lock() == marker) { - it->second.clear(); - break; - } - } - _imp->viewer->getViewer()->redraw(); -} - -NATRON_NAMESPACE_EXIT; - -NATRON_NAMESPACE_USING; -#include "moc_TrackerGui.cpp" diff --git a/Gui/TrackerGui.h b/Gui/TrackerGui.h deleted file mode 100644 index e03fca860e..0000000000 --- a/Gui/TrackerGui.h +++ /dev/null @@ -1,166 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * This file is part of Natron , - * Copyright (C) 2016 INRIA and Alexandre Gauthier-Foichat - * - * Natron is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * Natron is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Natron. If not, see - * ***** END LICENSE BLOCK ***** */ - -#ifndef TRACKERGUI_H -#define TRACKERGUI_H - -// ***** BEGIN PYTHON BLOCK ***** -// from : -// "Since Python may define some pre-processor definitions which affect the standard headers on some systems, you must include Python.h before any standard headers are included." -#include -// ***** END PYTHON BLOCK ***** - -#include "Global/GlobalDefines.h" - -#if !defined(Q_MOC_RUN) && !defined(SBK_RUN) -#include -#include -#endif -#include "Global/GlobalDefines.h" - -CLANG_DIAG_OFF(deprecated) -CLANG_DIAG_OFF(uninitialized) -#include -CLANG_DIAG_ON(deprecated) -CLANG_DIAG_ON(uninitialized) - -#include "Engine/ViewIdx.h" - -#include "Gui/GuiFwd.h" - -#include "Engine/ViewIdx.h" - - -NATRON_NAMESPACE_ENTER; - -struct TrackerGuiPrivate; - -class TrackerGui - : public QObject -{ - Q_OBJECT - -public: - - TrackerGui(const boost::shared_ptr & panel, - ViewerTab* parent); - - TrackerGui(TrackerPanel* panel, - ViewerTab* parent); - - virtual ~TrackerGui(); - - /** - * @brief Return the horizontal buttons bar. - **/ - QWidget* getButtonsBar() const; - - - void drawOverlays(double time, const RenderScale & renderScale, ViewIdx view) const; - - bool penDown(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, QMouseEvent* e); - - bool penDoubleClicked(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, QMouseEvent* e); - - bool penMotion(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, QInputEvent* e); - - bool penUp(double time, const RenderScale & renderScale, ViewIdx view, const QPointF & viewportPos, const QPointF & pos, double pressure, QMouseEvent* e); - - bool keyDown(double time, const RenderScale & renderScale, ViewIdx view, QKeyEvent* e); - - bool keyUp(double time, const RenderScale & renderScale, ViewIdx view, QKeyEvent* e); - - bool loseFocus(double time, const RenderScale & renderScale, ViewIdx view); - - bool gainFocus(double time, const RenderScale & renderScale, ViewIdx view); - -public Q_SLOTS: - - void onTimelineTimeChanged(SequenceTime time, int reason); - - void onAddTrackClicked(bool clicked); - - void onTrackRangeClicked(); - - void onTrackAllKeyframesClicked(); - - void onTrackCurrentKeyframeClicked(); - - void onTrackBwClicked(); - - void onTrackPrevClicked(); - - void onStopButtonClicked(); - - void onTrackNextClicked(); - - void onTrackFwClicked(); - - void onUpdateViewerClicked(bool clicked); - - void onClearAllAnimationClicked(); - - void onClearBwAnimationClicked(); - - void onClearFwAnimationClicked(); - - void updateSelectionFromSelectionRectangle(bool onRelease); - - void onSelectionCleared(); - - void onTrackingStarted(int step); - - void onTrackingEnded(); - - void onCreateKeyOnMoveButtonClicked(bool clicked); - - void onShowCorrelationButtonClicked(bool clicked); - - void onCenterViewerButtonClicked(bool clicked); - - void onSetKeyframeButtonClicked(); - void onRemoveKeyframeButtonClicked(); - void onResetOffsetButtonClicked(); - void onResetTrackButtonClicked(); - - void onContextSelectionChanged(int reason); - void onKeyframeSetOnTrack(const TrackMarkerPtr &marker, int key); - void onKeyframeRemovedOnTrack(const TrackMarkerPtr &marker, int key); - void onAllKeyframesRemovedOnTrack(const TrackMarkerPtr& marker); - - void updateSelectedMarkerTexture(); - -private Q_SLOTS: - - void rebuildMarkerTextures(); - - void onTrackerInputChanged(int inputNb); - void onTrackImageRenderingFinished(); - void onKeyFrameImageRenderingFinished(); - -private: - - - void createGui(); - - boost::scoped_ptr _imp; -}; - -NATRON_NAMESPACE_EXIT; - -#endif // TRACKERGUI_H diff --git a/Gui/ViewerGL.cpp b/Gui/ViewerGL.cpp index ffb73c5679..4e9ee22936 100644 --- a/Gui/ViewerGL.cpp +++ b/Gui/ViewerGL.cpp @@ -58,6 +58,7 @@ GCC_DIAG_UNUSED_PRIVATE_FIELD_ON #include "Engine/Project.h" #include "Engine/Settings.h" #include "Engine/Timer.h" // for gettimeofday +#include "Engine/Texture.h" #include "Engine/ViewIdx.h" #include "Engine/ViewerInstance.h" @@ -76,7 +77,6 @@ GCC_DIAG_UNUSED_PRIVATE_FIELD_ON #include "Gui/NodeSettingsPanel.h" #include "Gui/Shaders.h" #include "Gui/TabWidget.h" -#include "Gui/Texture.h" #include "Gui/ViewerTab.h" @@ -187,9 +187,7 @@ void ViewerGL::resizeGL(int w, int h) { - if (!_imp->supportsOpenGL) { - return; - } + // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); if ( (h == 0) || (w == 0) ) { // prevent division by 0 @@ -269,9 +267,7 @@ class BlendSetter void ViewerGL::paintGL() { - if (!_imp->supportsOpenGL) { - return; - } + // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); @@ -1358,7 +1354,7 @@ ViewerGL::initShaderGLSL() assert( qApp && qApp->thread() == QThread::currentThread() ); assert( QGLContext::currentContext() == context() ); - if (!_imp->shaderLoaded && _imp->supportsGLSL) { + if (!_imp->shaderLoaded) { _imp->shaderBlack.reset( new QGLShaderProgram( context() ) ); if ( !_imp->shaderBlack->addShaderFromSourceCode(QGLShader::Vertex, vertRGB) ) { qDebug() << qPrintable( _imp->shaderBlack->log() ); @@ -1411,7 +1407,7 @@ ViewerGL::isViewerUIVisible() const void ViewerGL::endTransferBufferFromRAMToGPU(int textureIndex, - const boost::shared_ptr& texture, + const boost::shared_ptr& texture, const ImagePtr& image, int time, const RectD& rod, @@ -1507,7 +1503,7 @@ ViewerGL::transferBufferFromRAMtoGPU(const unsigned char* ramBuffer, int textureIndex, bool isPartialRect, bool isFirstTile, - boost::shared_ptr* texture) + boost::shared_ptr* texture) { // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); @@ -1516,7 +1512,7 @@ ViewerGL::transferBufferFromRAMtoGPU(const unsigned char* ramBuffer, Q_UNUSED(e); GLint currentBoundPBO = 0; - glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, ¤tBoundPBO); + glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING_ARB, ¤tBoundPBO); GLenum err = glGetError(); if ( (err != GL_NO_ERROR) || (currentBoundPBO != 0) ) { qDebug() << "(ViewerGL::allocateAndMapPBO): Another PBO is currently mapped, glMap failed."; @@ -1631,14 +1627,6 @@ ViewerGL::setLut(int lut) _imp->displayingImageLut = (ViewerColorSpaceEnum)lut; } -/** - *@returns Returns true if the graphic card supports GLSL. - **/ -bool -ViewerGL::supportsGLSL() const -{ - return _imp->supportsGLSL; -} #if QT_VERSION < 0x050000 #define QMouseEventLocalPos(e) ( e->posF() ) @@ -1659,6 +1647,14 @@ ViewerGL::mousePressEvent(QMouseEvent* e) _imp->hasMovedSincePress = false; _imp->pressureOnRelease = 1.; + if (buttonDownIsLeft(e)) { + _imp->pointerTypeOnPress = ePenTypeLMB; + } else if (buttonDownIsRight(e)) { + _imp->pointerTypeOnPress = ePenTypeRMB; + } else if (buttonDownIsMiddle(e)) { + _imp->pointerTypeOnPress = ePenTypeMMB; + } + ///Set focus on user click setFocus(); @@ -1772,7 +1768,7 @@ ViewerGL::mousePressEvent(QMouseEvent* e) _imp->overlay) { unsigned int mipMapLevel = getCurrentRenderScale(); double scale = 1. / (1 << mipMapLevel); - overlaysCaught = _imp->viewerTab->notifyOverlaysPenDown(RenderScale(scale), _imp->pointerTypeOnPress, _imp->subsequentMousePressIsTablet, QMouseEventLocalPos(e), zoomPos, _imp->pressureOnPress, currentTimeForEvent(e), e); + overlaysCaught = _imp->viewerTab->notifyOverlaysPenDown(RenderScale(scale), _imp->pointerTypeOnPress, QMouseEventLocalPos(e), zoomPos, _imp->pressureOnPress, currentTimeForEvent(e)); if (overlaysCaught) { mustRedraw = true; } @@ -1924,7 +1920,7 @@ ViewerGL::mouseReleaseEvent(QMouseEvent* e) } _imp->pressureOnPress = 1; - _imp->subsequentMousePressIsTablet = false; + _imp->pointerTypeOnPress = ePenTypeLMB; bool mustRedraw = false; if (_imp->ms == eMouseStateBuildingPickerRectangle) { @@ -1954,7 +1950,7 @@ ViewerGL::mouseReleaseEvent(QMouseEvent* e) } unsigned int mipMapLevel = getCurrentRenderScale(); double scale = 1. / (1 << mipMapLevel); - if ( _imp->viewerTab->notifyOverlaysPenUp(RenderScale(scale), QMouseEventLocalPos(e), zoomPos, currentTimeForEvent(e), _imp->pressureOnRelease, e) ) { + if ( _imp->viewerTab->notifyOverlaysPenUp(RenderScale(scale), QMouseEventLocalPos(e), zoomPos, currentTimeForEvent(e), _imp->pressureOnRelease) ) { mustRedraw = true; } if (mustRedraw) { @@ -1993,7 +1989,6 @@ ViewerGL::tabletEvent(QTabletEvent* e) break; } _imp->pressureOnPress = e->pressure(); - _imp->subsequentMousePressIsTablet = true; QGLWidget::tabletEvent(e); break; } @@ -2376,7 +2371,7 @@ ViewerGL::penMotionInternal(int x, unsigned int mipMapLevel = getCurrentRenderScale(); double scale = 1. / (1 << mipMapLevel); if ( _imp->overlay && - _imp->viewerTab->notifyOverlaysPenMotion(RenderScale(scale), localPos, zoomPos, pressure, timestamp, e) ) { + _imp->viewerTab->notifyOverlaysPenMotion(RenderScale(scale), localPos, zoomPos, pressure, timestamp) ) { mustRedraw = true; overlaysCaughtByPlugin = true; } @@ -2410,7 +2405,7 @@ ViewerGL::mouseDoubleClickEvent(QMouseEvent* e) } double scale = 1. / (1 << mipMapLevel); - if ( _imp->viewerTab->notifyOverlaysPenDoubleClick(RenderScale(scale), QMouseEventLocalPos(e), pos_opengl, e) ) { + if ( _imp->viewerTab->notifyOverlaysPenDoubleClick(RenderScale(scale), QMouseEventLocalPos(e), pos_opengl) ) { update(); } QGLWidget::mouseDoubleClickEvent(e); @@ -3051,13 +3046,7 @@ ViewerGL::resizeEvent(QResizeEvent* e) ImageBitDepthEnum ViewerGL::getBitDepth() const { - // MT-SAFE - ///supportsGLSL is set on the main thread only once on startup, it doesn't need to be protected. - if (!_imp->supportsGLSL) { - return eImageBitDepthByte; - } else { - return appPTR->getCurrentSettings()->getViewersBitDepth(); - } + return appPTR->getCurrentSettings()->getViewersBitDepth(); } void @@ -3275,8 +3264,8 @@ ViewerGL::setProjection(double zoomLeft, Q_EMIT zoomChanged(100 * zoomFactor); } -bool -ViewerGL::isVisibleInViewport(const RectD& rectangle) const +RectD +ViewerGL::getViewportRect() const { RectD bbox; { @@ -3286,8 +3275,17 @@ ViewerGL::isVisibleInViewport(const RectD& rectangle) const bbox.x2 = _imp->zoomCtx.right(); bbox.y2 = _imp->zoomCtx.top(); } + return bbox; +} - return bbox.intersects(rectangle); +void +ViewerGL::getCursorPosition(double &x, double &y) const +{ + QPoint p = QCursor::pos(); + p = mapFromGlobal(p); + QPointF mappedPos = toZoomCoordinates(p); + x = mappedPos.x(); + y = mappedPos.y(); } void @@ -3821,7 +3819,7 @@ ViewerGL::getTextureColorAt(int x, pos = _imp->zoomCtx.toWidgetCoordinates(x, y); } - if ( (type == Texture::eDataTypeByte) || !_imp->supportsGLSL ) { + if (type == Texture::eDataTypeByte) { U32 pixel; glReadBuffer(GL_FRONT); glReadPixels(pos.x(), height() - pos.y(), 1, 1, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &pixel); @@ -3835,7 +3833,7 @@ ViewerGL::getTextureColorAt(int x, *b = (double)blue / 255.; *a = (double)alpha / 255.; glCheckError(); - } else if ( (type == Texture::eDataTypeFloat) && _imp->supportsGLSL ) { + } else if (type == Texture::eDataTypeFloat) { GLfloat pixel[4]; glReadPixels(pos.x(), height() - pos.y(), 1, 1, GL_RGBA, GL_FLOAT, pixel); *r = (double)pixel[0]; @@ -4236,7 +4234,7 @@ ViewerGL::getColorAtRect(const RectD &rect, // rectangle in canonical coordinate return false; } - if ( (type == Texture::eDataTypeByte) || !_imp->supportsGLSL ) { + if ( (type == Texture::eDataTypeByte) ) { std::vector pixels(rectPixel.width() * rectPixel.height()); glReadBuffer(GL_FRONT); glReadPixels(rectPixel.left(), rectPixel.right(), rectPixel.width(), rectPixel.height(), @@ -4269,7 +4267,7 @@ ViewerGL::getColorAtRect(const RectD &rect, // rectangle in canonical coordinate } glCheckError(); - } else if ( (type == Texture::eDataTypeFloat) && _imp->supportsGLSL ) { + } else if ( (type == Texture::eDataTypeFloat)) { std::vector pixels(rectPixel.width() * rectPixel.height() * 4); glReadPixels(rectPixel.left(), rectPixel.right(), rectPixel.width(), rectPixel.height(), GL_RGBA, GL_FLOAT, &pixels.front()); diff --git a/Gui/ViewerGL.h b/Gui/ViewerGL.h index ab187d6109..25faaa04ca 100644 --- a/Gui/ViewerGL.h +++ b/Gui/ViewerGL.h @@ -176,9 +176,9 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON int textureIndex, bool isPartialRect, bool isFirstTile, - boost::shared_ptr* texture) OVERRIDE FINAL; + boost::shared_ptr* texture) OVERRIDE FINAL; virtual void endTransferBufferFromRAMToGPU(int textureIndex, - const boost::shared_ptr& texture, + const boost::shared_ptr& texture, const ImagePtr& image, int time, const RectD& rod, @@ -196,10 +196,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON bool isPartialRect) OVERRIDE FINAL; virtual void clearLastRenderedImage() OVERRIDE FINAL; virtual void disconnectInputTexture(int textureIndex) OVERRIDE FINAL; - /** - *@returns Returns true if the graphic card supports GLSL. - **/ - virtual bool supportsGLSL() const OVERRIDE FINAL; + /** *@brief Disconnects the viewer. @@ -275,11 +272,6 @@ public Q_SLOTS: void setProjection(double zoomLeft, double zoomBottom, double zoomFactor, double zoomAspectRatio); - /** - * @brief Returns whether the given rectangle is visible in the viewport, in zoom (OpenGL) coordinates. - **/ - bool isVisibleInViewport(const RectD& rectangle) const; - void setUserRoIEnabled(bool b); void setBuildNewUserRoI(bool b); @@ -322,6 +314,17 @@ public Q_SLOTS: ViewerInstance* getInternalNode() const; ViewerTab* getViewerTab() const; + /** + * @brief Returns the viewport visible portion in canonical coordinates + **/ + virtual RectD getViewportRect() const OVERRIDE FINAL WARN_UNUSED_RETURN; + + /** + * @brief Returns the cursor position in canonical coordinates + **/ + virtual void getCursorPosition(double& x, double& y) const OVERRIDE FINAL; + + /** * @brief can only be called on the main-thread **/ @@ -392,6 +395,10 @@ public Q_SLOTS: void checkIfViewPortRoIValidOrRender(); + void s_selectionCleared() + { + Q_EMIT selectionCleared(); + } Q_SIGNALS: diff --git a/Gui/ViewerGLPrivate.cpp b/Gui/ViewerGLPrivate.cpp index 247969631f..a61509f383 100644 --- a/Gui/ViewerGLPrivate.cpp +++ b/Gui/ViewerGLPrivate.cpp @@ -36,11 +36,11 @@ #include "Engine/Lut.h" // Color #include "Engine/Settings.h" +#include "Engine/Texture.h" #include "Gui/Gui.h" #include "Gui/GuiApplicationManager.h" // appFont #include "Gui/Menu.h" -#include "Gui/Texture.h" #include "Gui/ViewerTab.h" // warning: 'gluErrorString' is deprecated: first deprecated in OS X 10.9 [-Wdeprecated-declarations] @@ -89,8 +89,6 @@ ViewerGL::Implementation::Implementation(ViewerGL* this_, , rodOverlayColor(100, 100, 100, 255) , textFont( new QFont(appFont, appFontSize) ) , overlay(true) - , supportsOpenGL(true) - , supportsGLSL(true) , updatingTexture(false) , clearColor(0, 0, 0, 255) , menu( new Menu(_this) ) @@ -125,8 +123,7 @@ ViewerGL::Implementation::Implementation(ViewerGL* this_, , prevBoundTexture(0) , lastRenderedImageMutex() , sizeH() - , pointerTypeOnPress(ePenTypePen) - , subsequentMousePressIsTablet(false) + , pointerTypeOnPress(ePenTypeLMB) , pressureOnPress(1.) , pressureOnRelease(1.) , wheelDeltaSeekFrame(0) @@ -246,7 +243,7 @@ ViewerGL::Implementation::drawRenderingVAO(unsigned int mipMapLevel, assert( qApp && qApp->thread() == QThread::currentThread() ); assert( QGLContext::currentContext() == _this->context() ); - bool useShader = _this->getBitDepth() != eImageBitDepthByte && this->supportsGLSL; + bool useShader = _this->getBitDepth() != eImageBitDepthByte; ///the texture rectangle in image coordinates. The values in it are multiples of tile size. @@ -431,10 +428,8 @@ ViewerGL::Implementation::initializeGL() // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); _this->makeCurrent(); - supportsOpenGL = initAndCheckGlExtensions(); - if (!supportsOpenGL) { - return; - } + initAndCheckGlExtensions(); + displayTextures[0].texture.reset( new Texture(GL_TEXTURE_2D, GL_LINEAR, GL_NEAREST, GL_CLAMP_TO_EDGE) ); displayTextures[1].texture.reset( new Texture(GL_TEXTURE_2D, GL_LINEAR, GL_NEAREST, GL_CLAMP_TO_EDGE) ); @@ -459,41 +454,12 @@ ViewerGL::Implementation::initializeGL() initializeCheckerboardTexture(true); - if (this->supportsGLSL) { - _this->initShaderGLSL(); - glCheckError(); - } + _this->initShaderGLSL(); glCheckError(); } -static -QString -getOpenGLVersionString() -{ - const char* str = (const char*)glGetString(GL_VERSION); - QString ret; - - if (str) { - ret.append( QString::fromUtf8(str) ); - } - - return ret; -} - -static -QString -getGlewVersionString() -{ - const char* str = reinterpret_cast( glewGetString(GLEW_VERSION) ); - QString ret; - - if (str) { - ret.append( QString::fromUtf8(str) ); - } - return ret; -} bool ViewerGL::Implementation::initAndCheckGlExtensions() @@ -501,45 +467,8 @@ ViewerGL::Implementation::initAndCheckGlExtensions() // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); assert( QGLContext::currentContext() == _this->context() ); - const QGLContext* context = _this->context(); - GLenum err = glewInit(); - if (GLEW_OK != err) { - /* Problem: glewInit failed, something is seriously wrong. */ - Dialogs::errorDialog( tr("OpenGL/GLEW error").toStdString(), - (const char*)glewGetErrorString(err) ); - - return false; - } - //fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); - - // is GL_VERSION_2_0 necessary? note that GL_VERSION_2_0 includes GLSL - if ( !glewIsSupported("GL_VERSION_1_5 " - "GL_ARB_texture_non_power_of_two " // or GL_IMG_texture_npot, or GL_OES_texture_npot, core since 2.0 - "GL_ARB_shader_objects " // GLSL, Uniform*, core since 2.0 - "GL_ARB_vertex_buffer_object " // BindBuffer, MapBuffer, etc. - "GL_ARB_pixel_buffer_object " // BindBuffer(PIXEL_UNPACK_BUFFER,... - //"GL_ARB_vertex_array_object " // BindVertexArray, DeleteVertexArrays, GenVertexArrays, IsVertexArray (VAO), core since 3.0 - //"GL_ARB_framebuffer_object " // or GL_EXT_framebuffer_object GenFramebuffers, core since version 3.0 - ) ) { - Dialogs::errorDialog( tr("Missing OpenGL requirements").toStdString(), - tr("The viewer may not be fully functional. " - "This software needs at least OpenGL 1.5 with NPOT textures, GLSL, VBO, PBO, vertex arrays. ").toStdString() ); - - return false; - } - - this->viewerTab->getGui()->setOpenGLVersion( getOpenGLVersionString() ); - this->viewerTab->getGui()->setGlewVersion( getGlewVersionString() ); - - if ( !context || !QGLShaderProgram::hasOpenGLShaderPrograms(context) ) { - // no need to pull out a dialog, it was already presented after the GLEW check above - - //Dialogs::errorDialog("Viewer error","The viewer is unabgile to work without a proper version of GLSL."); - //cout << "Warning : GLSL not present on this hardware, no material acceleration possible." << endl; - this->supportsGLSL = false; - } - + assert(QGLShaderProgram::hasOpenGLShaderPrograms(context)); return true; } @@ -981,8 +910,6 @@ ViewerGL::Implementation::activateShaderRGB(int texIndex) // - 8-bits textures are stored non-linear and must be displayer as is // - floating-point textures are linear and must be decompressed according to the given lut - assert(supportsGLSL); - if ( !shaderRGB->bind() ) { qDebug() << "Error when binding shader" << qPrintable( shaderRGB->log() ); } diff --git a/Gui/ViewerGLPrivate.h b/Gui/ViewerGLPrivate.h index f1e54eb225..068ea88067 100644 --- a/Gui/ViewerGLPrivate.h +++ b/Gui/ViewerGLPrivate.h @@ -144,9 +144,6 @@ struct ViewerGL::Implementation QFont* textFont; bool overlay; /*!< True if the user enabled overlay dispay*/ - // supportsGLSL is accessed from several threads, but is set only once at startup - bool supportsOpenGL; - bool supportsGLSL; /*!< True if the user has a GLSL version supporting everything requested.*/ bool updatingTexture; QColor clearColor; QMenu* menu; @@ -196,7 +193,6 @@ struct ViewerGL::Implementation mutable QMutex lastRenderedImageMutex; //protects lastRenderedImage & memoryHeldByLastRenderedImages QSize sizeH; PenType pointerTypeOnPress; - bool subsequentMousePressIsTablet; double pressureOnPress, pressureOnRelease; int wheelDeltaSeekFrame; // accumulated wheel delta for frame seeking (crtl+wheel) bool isUpdatingTexture; diff --git a/Gui/ViewerTab.cpp b/Gui/ViewerTab.cpp index 122274c4d3..8f52325c34 100644 --- a/Gui/ViewerTab.cpp +++ b/Gui/ViewerTab.cpp @@ -96,10 +96,8 @@ addSpacer(QBoxLayout* layout) layout->addSpacing(5); } -ViewerTab::ViewerTab(const std::list & existingRotoNodes, - NodeGui* currentRoto, - const std::list & existingTrackerNodes, - NodeGui* currentTracker, +ViewerTab::ViewerTab(const std::list & existingNodesContext, + const std::list& activePluginsContext, Gui* gui, ViewerInstance* node, QWidget* parent) @@ -1014,18 +1012,11 @@ ViewerTab::ViewerTab(const std::list & existingRotoNodes, connectToViewerCache(); - for (std::list::const_iterator it = existingRotoNodes.begin(); it != existingRotoNodes.end(); ++it) { - createRotoInterface(*it); + for (std::list::const_iterator it = existingNodesContext.begin(); it != existingNodesContext.end(); ++it) { + createNodeViewerInterface(*it); } - if ( currentRoto && currentRoto->isSettingsPanelVisible() ) { - setRotoInterface(currentRoto); - } - - for (std::list::const_iterator it = existingTrackerNodes.begin(); it != existingTrackerNodes.end(); ++it) { - createTrackerInterface(*it); - } - if ( currentTracker && currentTracker->isSettingsPanelVisible() ) { - setRotoInterface(currentTracker); + for (std::list::const_iterator it = activePluginsContext.begin(); it != activePluginsContext.end(); ++it) { + setPluginViewerInterface(*it); } setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); diff --git a/Gui/ViewerTab.h b/Gui/ViewerTab.h index 090aad6a4e..a6019e0148 100644 --- a/Gui/ViewerTab.h +++ b/Gui/ViewerTab.h @@ -55,10 +55,11 @@ GCC_DIAG_SUGGEST_OVERRIDE_OFF GCC_DIAG_SUGGEST_OVERRIDE_ON public: - explicit ViewerTab(const std::list & existingRotoNodes, - NodeGui* currentRoto, - const std::list & existingTrackerNodes, - NodeGui* currentTracker, + + + + explicit ViewerTab(const std::list & existingNodesContext, + const std::list& activePluginsContext, Gui* gui, ViewerInstance* node, QWidget* parent = 0); @@ -86,13 +87,13 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON /*All the overlay methods are forwarding calls to the default node instance*/ void drawOverlays(double time, const RenderScale & renderScale) const; - bool notifyOverlaysPenDown(const RenderScale & renderScale, PenType pen, bool isTabletEvent, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, QMouseEvent* e); + bool notifyOverlaysPenDown(const RenderScale & renderScale, PenType pen, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp); - bool notifyOverlaysPenDoubleClick(const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, QMouseEvent* e); + bool notifyOverlaysPenDoubleClick(const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos); - bool notifyOverlaysPenMotion(const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, QInputEvent* e); + bool notifyOverlaysPenMotion(const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp); - bool notifyOverlaysPenUp(const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, QMouseEvent* e); + bool notifyOverlaysPenUp(const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp); bool notifyOverlaysKeyDown(const RenderScale & renderScale, QKeyEvent* e); @@ -106,13 +107,13 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON private: - bool notifyOverlaysPenDown_internal(const NodePtr& node, const RenderScale & renderScale, PenType pen, bool isTabletEvent, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, QMouseEvent* e); + bool notifyOverlaysPenDown_internal(const NodePtr& node, const RenderScale & renderScale, PenType pen, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp); - bool notifyOverlaysPenMotion_internal(const NodePtr& node, const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp, QInputEvent* e); - bool notifyOverlaysKeyDown_internal(const NodePtr& node, const RenderScale & renderScale, QKeyEvent* e, Key k, - KeyboardModifiers km); - bool notifyOverlaysKeyRepeat_internal(const NodePtr& node, const RenderScale & renderScale, QKeyEvent* e, Key k, - KeyboardModifiers km); + bool notifyOverlaysPenMotion_internal(const NodePtr& node, const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, double pressure, double timestamp); + bool notifyOverlaysKeyDown_internal(const NodePtr& node, const RenderScale & renderScale, Key k, + KeyboardModifiers km, Qt::Key qKey, const Qt::KeyboardModifiers& mods); + bool notifyOverlaysKeyRepeat_internal(const NodePtr& node, const RenderScale & renderScale, Key k, + KeyboardModifiers km, Qt::Key qKey, const Qt::KeyboardModifiers& mods); public: @@ -171,32 +172,35 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON void setInfoBarResolution(const Format & f); - void createRotoInterface(NodeGui* n); - /** - * @brief Set the current roto interface + * @brief Creates a new viewer interface context for this node. This is not shared among viewers. **/ - void setRotoInterface(NodeGui* n); - - void removeRotoInterface(NodeGui* n, bool permanently, bool removeAndDontSetAnother); - - void getRotoContext(std::map* rotoNodes, std::pair* currentRoto) const; - - void updateRotoSelectedTool(int tool, RotoGui* sender); + void createNodeViewerInterface(const NodeGuiPtr& n); - boost::shared_ptr getRotoGuiSharedData(NodeGui* node) const; - - void onRotoEvaluatedForThisViewer(); - - - void createTrackerInterface(NodeGui* n); - - void setTrackerInterface(NodeGui* n); + /** + * @brief Set the current viewer interface for a given plug-in to be the one of the given node + **/ + void setPluginViewerInterface(const NodeGuiPtr& n); - void removeTrackerInterface(NodeGui* n, bool permanently, bool removeAndDontSetAnother); + /** + * @brief Removes the interface associated to the given node. + * @param permanently The interface is destroyed instead of being hidden + * @param setAnotherFromSamePlugin If true, if another node of the same plug-in is a candidate for a viewer interface, it will replace the existing + * viewer interface for this plug-in + **/ + void removeNodeViewerInterface(const NodeGuiPtr& n, bool permanently, bool setAnotherFromSamePlugin); - void getTrackerContext(std::map* trackerNodes, std::pair* currentTracker) const; + /** + * @brief Get the list of all nodes that have a user interface created on this viewer (but not necessarily displayed) + * and a list for each plug-in of the active node. + **/ + void getNodesViewerInterface(std::list* nodesWithUI, + std::list* perPluginActiveUI) const; + /** + * @brief Called to refresh the current selected tool on the toolbar + **/ + void updateSelectedToolForNode(const QString& toolID, const NodeGuiPtr& node); ViewerCompositingOperatorEnum getCompositingOperator() const; @@ -338,12 +342,6 @@ public Q_SLOTS: void onRenderScaleButtonClicked(bool checked); - void onRotoRoleChanged(int previousRole, int newRole); - - void onRotoNodeGuiSettingsPanelClosed(bool closed); - - void onTrackerNodeGuiSettingsPanelClosed(bool closed); - void onColorSpaceComboBoxChanged(int v); void onCompositingOperatorIndexChanged(int index); diff --git a/Gui/ViewerTab10.cpp b/Gui/ViewerTab10.cpp index 0ec1041388..eb580b1e12 100644 --- a/Gui/ViewerTab10.cpp +++ b/Gui/ViewerTab10.cpp @@ -52,11 +52,10 @@ GCC_DIAG_UNUSED_PRIVATE_FIELD_ON #include "Gui/GuiApplicationManager.h" // appPTR #include "Gui/NodeGraph.h" #include "Gui/NodeGui.h" +#include "Gui/NodeViewerContext.h" #include "Gui/RenderStatsDialog.h" -#include "Gui/RotoGui.h" #include "Gui/SpinBox.h" #include "Gui/TimeLineGui.h" -#include "Gui/TrackerGui.h" #include "Gui/TabWidget.h" #include "Gui/ViewerGL.h" @@ -490,12 +489,8 @@ ViewerTab::~ViewerTab() graph->setLastSelectedViewer(0); } } - for (std::map::iterator it = _imp->rotoNodes.begin(); it != _imp->rotoNodes.end(); ++it) { - delete it->second; - } - for (std::map::iterator it = _imp->trackerNodes.begin(); it != _imp->trackerNodes.end(); ++it) { - delete it->second; - } + _imp->nodesContext.clear(); + } void @@ -775,6 +770,9 @@ ViewerTab::keyPressEvent(QKeyEvent* e) update(); } else if ( notifyOverlaysKeyDown(RenderScale(scale), e) ) { update(); + } else if (key == Qt::Key_Escape) { + _imp->viewer->s_selectionCleared(); + update(); } else if ( isKeybind(kShortcutGroupViewer, kShortcutIDSwitchInputAAndB, modifiers, key) ) { ///Put it after notifyOverlaysKeyDown() because Roto may intercept Enter if ( getViewer()->hasFocus() ) { diff --git a/Gui/ViewerTab20.cpp b/Gui/ViewerTab20.cpp index a5ecb3a56d..ecfb7ecf0e 100644 --- a/Gui/ViewerTab20.cpp +++ b/Gui/ViewerTab20.cpp @@ -49,10 +49,8 @@ GCC_DIAG_UNUSED_PRIVATE_FIELD_ON #include "Gui/GuiAppInstance.h" #include "Gui/NodeGui.h" #include "Gui/QtEnumConvert.h" -#include "Gui/RotoGui.h" #include "Gui/SpinBox.h" #include "Gui/TimeLineGui.h" -#include "Gui/TrackerGui.h" #include "Gui/ViewerGL.h" @@ -107,10 +105,10 @@ ViewerTab::drawOverlays(double time, ///Draw overlays in reverse order of appearance so that the first (top) panel is drawn on top of everything else for (NodesList::reverse_iterator it = nodes.rbegin(); it != nodes.rend(); ++it) { + NodeGuiPtr nodeUi = boost::dynamic_pointer_cast( (*it)->getNodeGui() ); #ifdef NATRON_TRANSFORM_AFFECTS_OVERLAYS double transformedTime; bool ok = _imp->getTimeTransform(time, view, *it, getInternalNode(), &transformedTime); - NodeGuiPtr nodeUi = boost::dynamic_pointer_cast( (*it)->getNodeGui() ); if (!nodeUi) { continue; } @@ -139,21 +137,14 @@ ViewerTab::drawOverlays(double time, } #endif - - if ( _imp->currentRoto.first && ( (*it) == _imp->currentRoto.first->getNode() ) ) { - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - _imp->currentRoto.second->drawOverlays(time, renderScale, view); - } - } else if ( _imp->currentTracker.first && ( (*it) == _imp->currentTracker.first->getNode() ) ) { - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - _imp->currentTracker.second->drawOverlays(time, renderScale, view); - } - } else { + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(*it); + if (!isInActiveViewerUI) { EffectInstPtr effect = (*it)->getEffectInstance(); assert(effect); effect->setCurrentViewportForOverlays_public(_imp->viewer); effect->drawOverlay_public(time, renderScale, view); } + #ifdef NATRON_TRANSFORM_AFFECTS_OVERLAYS if (ok) { glMatrixMode(GL_MODELVIEW); @@ -167,12 +158,10 @@ bool ViewerTab::notifyOverlaysPenDown_internal(const NodePtr& node, const RenderScale & renderScale, PenType pen, - bool isTabletEvent, const QPointF & viewportPos, const QPointF & pos, double pressure, - double timestamp, - QMouseEvent* e) + double timestamp) { QPointF transformViewportPos; QPointF transformPos; @@ -230,27 +219,12 @@ ViewerTab::notifyOverlaysPenDown_internal(const NodePtr& node, transformPos = pos; #endif - if ( _imp->currentRoto.first && ( node == _imp->currentRoto.first->getNode() ) ) { - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - if ( _imp->currentRoto.second->penDown(time, renderScale, view, pen, isTabletEvent, transformViewportPos, transformPos, pressure, timestamp, e) ) { - _imp->lastOverlayNode = node; - - return true; - } - } - } else if ( _imp->currentTracker.first && ( node == _imp->currentTracker.first->getNode() ) ) { - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - if ( _imp->currentTracker.second->penDown(time, renderScale, view, transformViewportPos, transformPos, pressure, e) ) { - _imp->lastOverlayNode = node; - - return true; - } - } - } else { + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(node); + if (!isInActiveViewerUI) { EffectInstPtr effect = node->getEffectInstance(); assert(effect); effect->setCurrentViewportForOverlays_public(_imp->viewer); - bool didSmthing = effect->onOverlayPenDown_public(time, renderScale, view, transformViewportPos, transformPos, pressure); + bool didSmthing = effect->onOverlayPenDown_public(time, renderScale, view, transformViewportPos, transformPos, pressure, timestamp, pen); if (didSmthing) { //http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html // if the instance returns kOfxStatOK, the host should not pass the pen motion @@ -262,18 +236,17 @@ ViewerTab::notifyOverlaysPenDown_internal(const NodePtr& node, } } + return false; } // ViewerTab::notifyOverlaysPenDown_internal bool ViewerTab::notifyOverlaysPenDown(const RenderScale & renderScale, PenType pen, - bool isTabletEvent, const QPointF & viewportPos, const QPointF & pos, double pressure, - double timestamp, - QMouseEvent* e) + double timestamp) { if ( !getGui()->getApp() || getGui()->getApp()->isClosing() ) { return false; @@ -301,7 +274,7 @@ ViewerTab::notifyOverlaysPenDown(const RenderScale & renderScale, if (lastOverlay) { for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { if (*it == lastOverlay) { - if ( notifyOverlaysPenDown_internal(*it, renderScale, pen, isTabletEvent, viewportPos, pos, pressure, timestamp, e) ) { + if ( notifyOverlaysPenDown_internal(*it, renderScale, pen, viewportPos, pos, pressure, timestamp) ) { return true; } else { nodes.erase(it); @@ -312,7 +285,7 @@ ViewerTab::notifyOverlaysPenDown(const RenderScale & renderScale, } for (NodesList::reverse_iterator it = nodes.rbegin(); it != nodes.rend(); ++it) { - if ( notifyOverlaysPenDown_internal(*it, renderScale, pen, isTabletEvent, viewportPos, pos, pressure, timestamp, e) ) { + if ( notifyOverlaysPenDown_internal(*it, renderScale, pen, viewportPos, pos, pressure, timestamp) ) { return true; } } @@ -328,8 +301,7 @@ ViewerTab::notifyOverlaysPenDown(const RenderScale & renderScale, bool ViewerTab::notifyOverlaysPenDoubleClick(const RenderScale & renderScale, const QPointF & viewportPos, - const QPointF & pos, - QMouseEvent* e) + const QPointF & pos) { if ( !getGui()->getApp() || getGui()->getApp()->isClosing() ) { return false; @@ -384,18 +356,20 @@ ViewerTab::notifyOverlaysPenDoubleClick(const RenderScale & renderScale, transformPos = pos; #endif - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - if ( _imp->currentRoto.second->penDoubleClicked(time, renderScale, view, transformViewportPos, transformPos, e) ) { - _imp->lastOverlayNode = _imp->currentRoto.first->getNode(); - - return true; - } - } + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(*it); + if (!isInActiveViewerUI) { + EffectInstPtr effect = (*it)->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public(_imp->viewer); - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - if ( _imp->currentTracker.second->penDoubleClicked(time, renderScale, view, transformViewportPos, transformPos, e) ) { - _imp->lastOverlayNode = _imp->currentRoto.first->getNode(); + bool didSmthing = effect->onOverlayPenDoubleClicked_public(time, renderScale, view, transformViewportPos, transformPos); + if (didSmthing) { + //http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html + // if the instance returns kOfxStatOK, the host should not pass the pen motion + // to any other interactive object it may own that shares the same view. + _imp->lastOverlayNode = *it; + return true; } } @@ -410,8 +384,7 @@ ViewerTab::notifyOverlaysPenMotion_internal(const NodePtr& node, const QPointF & viewportPos, const QPointF & pos, double pressure, - double timestamp, - QInputEvent* e) + double timestamp) { QPointF transformViewportPos; QPointF transformPos; @@ -467,23 +440,8 @@ ViewerTab::notifyOverlaysPenMotion_internal(const NodePtr& node, transformPos = pos; #endif - if ( _imp->currentRoto.first && ( node == _imp->currentRoto.first->getNode() ) ) { - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - if ( _imp->currentRoto.second->penMotion(time, renderScale, view, transformViewportPos, transformPos, pressure, timestamp, e) ) { - _imp->lastOverlayNode = node; - - return true; - } - } - } else if ( _imp->currentTracker.first && ( node == _imp->currentTracker.first->getNode() ) ) { - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - if ( _imp->currentTracker.second->penMotion(time, renderScale, view, transformViewportPos, transformPos, pressure, e) ) { - _imp->lastOverlayNode = node; - - return true; - } - } - } else { + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(node); + if (!isInActiveViewerUI) { ///If we are dragging with mouse, set draft mode (not for roto though) if ( _imp->hasPenDown && !getGui()->isDraftRenderEnabled() ) { getGui()->setDraftRenderEnabled(true); @@ -492,7 +450,7 @@ ViewerTab::notifyOverlaysPenMotion_internal(const NodePtr& node, EffectInstPtr effect = node->getEffectInstance(); assert(effect); effect->setCurrentViewportForOverlays_public(_imp->viewer); - bool didSmthing = effect->onOverlayPenMotion_public(time, renderScale, view, transformViewportPos, transformPos, pressure); + bool didSmthing = effect->onOverlayPenMotion_public(time, renderScale, view, transformViewportPos, transformPos, pressure, timestamp); if (didSmthing) { if (_imp->hasPenDown) { _imp->hasCaughtPenMotionWhileDragging = true; @@ -508,6 +466,7 @@ ViewerTab::notifyOverlaysPenMotion_internal(const NodePtr& node, } } + return false; } // ViewerTab::notifyOverlaysPenMotion_internal @@ -516,8 +475,7 @@ ViewerTab::notifyOverlaysPenMotion(const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, double pressure, - double timestamp, - QInputEvent* e) + double timestamp) { bool didSomething = false; @@ -544,7 +502,7 @@ ViewerTab::notifyOverlaysPenMotion(const RenderScale & renderScale, if (lastOverlay) { for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { if (*it == lastOverlay) { - if ( notifyOverlaysPenMotion_internal(*it, renderScale, viewportPos, pos, pressure, timestamp, e) ) { + if ( notifyOverlaysPenMotion_internal(*it, renderScale, viewportPos, pos, pressure, timestamp) ) { return true; } else { nodes.erase(it); @@ -556,7 +514,7 @@ ViewerTab::notifyOverlaysPenMotion(const RenderScale & renderScale, for (NodesList::reverse_iterator it = nodes.rbegin(); it != nodes.rend(); ++it) { - if ( notifyOverlaysPenMotion_internal(*it, renderScale, viewportPos, pos, pressure, timestamp, e) ) { + if ( notifyOverlaysPenMotion_internal(*it, renderScale, viewportPos, pos, pressure, timestamp) ) { return true; } } @@ -576,8 +534,7 @@ ViewerTab::notifyOverlaysPenUp(const RenderScale & renderScale, const QPointF & viewportPos, const QPointF & pos, double pressure, - double timestamp, - QMouseEvent* e) + double timestamp) { bool didSomething = false; @@ -668,22 +625,15 @@ ViewerTab::notifyOverlaysPenUp(const RenderScale & renderScale, transformPos = pos; #endif - - if ( _imp->currentRoto.first && ( (*it) == _imp->currentRoto.first->getNode() ) ) { - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - didSomething |= _imp->currentRoto.second->penUp(time, renderScale, view, transformViewportPos, transformPos, pressure, timestamp, e); - } - } - if ( _imp->currentTracker.first && ( (*it) == _imp->currentTracker.first->getNode() ) ) { - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - didSomething |= _imp->currentTracker.second->penUp(time, renderScale, view, transformViewportPos, transformPos, pressure, e); - } + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(*it); + if (!isInActiveViewerUI) { + EffectInstPtr effect = (*it)->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public(_imp->viewer); + didSomething |= effect->onOverlayPenUp_public(time, renderScale, view, transformViewportPos, transformPos, pressure, timestamp); } - EffectInstPtr effect = (*it)->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public(_imp->viewer); - didSomething |= effect->onOverlayPenUp_public(time, renderScale, view, transformViewportPos, transformPos, pressure); + } @@ -704,9 +654,10 @@ ViewerTab::notifyOverlaysPenUp(const RenderScale & renderScale, bool ViewerTab::notifyOverlaysKeyDown_internal(const NodePtr& node, const RenderScale & renderScale, - QKeyEvent* e, Key k, - KeyboardModifiers km) + KeyboardModifiers km, + Qt::Key qKey, + const Qt::KeyboardModifiers& mods) { double time = getGui()->getApp()->getTimeLine()->currentFrame(); ViewIdx view = getCurrentView(); @@ -728,26 +679,27 @@ ViewerTab::notifyOverlaysKeyDown_internal(const NodePtr& node, #endif - if ( _imp->currentRoto.first && ( node == _imp->currentRoto.first->getNode() ) ) { - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - if ( _imp->currentRoto.second->keyDown(time, renderScale, view, e) ) { - _imp->lastOverlayNode = node; + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(node); + if (!isInActiveViewerUI) { + EffectInstPtr effect = node->getEffectInstance(); + assert(effect); + effect->setCurrentViewportForOverlays_public(_imp->viewer); - return true; + // Intercept plug-in defined shortcuts + const KnobsVec& knobs = effect->getKnobs(); + std::string pluginShortcutGroup; + for (KnobsVec::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { + if ((*it)->getInViewerContextHasShortcut() && !(*it)->getInViewerContextSecret()) { + if (pluginShortcutGroup.empty()) { + pluginShortcutGroup = effect->getNode()->getPlugin()->getPluginShortcutGroup().toStdString(); + } + if (isKeybind(pluginShortcutGroup, (*it)->getName(), mods, qKey)) { + effect->onKnobValueChanged_public(it->get(), eValueChangedReasonUserEdited, time, ViewSpec(0), true); + return true; + } } } - } else if ( _imp->currentTracker.first && ( node == _imp->currentTracker.first->getNode() ) ) { - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - if ( _imp->currentTracker.second->keyDown(time, renderScale, view, e) ) { - _imp->lastOverlayNode = node; - return true; - } - } - } else { - EffectInstPtr effect = node->getEffectInstance(); - assert(effect); - effect->setCurrentViewportForOverlays_public(_imp->viewer); bool didSmthing = effect->onOverlayKeyDown_public(time, renderScale, view, k, km); if (didSmthing) { //http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html @@ -760,6 +712,7 @@ ViewerTab::notifyOverlaysKeyDown_internal(const NodePtr& node, } } + return false; } // ViewerTab::notifyOverlaysKeyDown_internal @@ -781,8 +734,10 @@ ViewerTab::notifyOverlaysKeyDown(const RenderScale & renderScale, */ bool isModifier = e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Alt || e->key() == Qt::Key_Meta; - Key natronKey = QtEnumConvert::fromQtKey( (Qt::Key)e->key() ); - KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers( e->modifiers() ); + Qt::Key qKey = (Qt::Key)e->key(); + Qt::KeyboardModifiers qMods = e->modifiers(); + Key natronKey = QtEnumConvert::fromQtKey(qKey ); + KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers(qMods); NodesList nodes; getGui()->getNodesEntitledForOverlays(nodes); @@ -790,7 +745,7 @@ ViewerTab::notifyOverlaysKeyDown(const RenderScale & renderScale, if (lastOverlay) { for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { if (*it == lastOverlay) { - if ( notifyOverlaysKeyDown_internal(*it, renderScale, e, natronKey, natronMod) ) { + if ( notifyOverlaysKeyDown_internal(*it, renderScale, natronKey, natronMod, qKey, qMods) ) { if (isModifier) { nodes.erase(it); break; @@ -809,7 +764,7 @@ ViewerTab::notifyOverlaysKeyDown(const RenderScale & renderScale, for (NodesList::reverse_iterator it = nodes.rbegin(); it != nodes.rend(); ++it) { - if ( notifyOverlaysKeyDown_internal(*it, renderScale, e, natronKey, natronMod) ) { + if ( notifyOverlaysKeyDown_internal(*it, renderScale, natronKey, natronMod, qKey, qMods) ) { if (isModifier) { continue; } @@ -868,20 +823,15 @@ ViewerTab::notifyOverlaysKeyUp(const RenderScale & renderScale, } #endif - if ( _imp->currentRoto.first && ( (*it) == _imp->currentRoto.first->getNode() ) ) { - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - didSomething |= _imp->currentRoto.second->keyUp(time, renderScale, view, e); - } - } - if ( _imp->currentTracker.first && ( (*it) == _imp->currentTracker.first->getNode() ) ) { - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - didSomething |= _imp->currentTracker.second->keyUp(time, renderScale, view, e); - } + + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(*it); + if (!isInActiveViewerUI) { + effect->setCurrentViewportForOverlays_public(_imp->viewer); + didSomething |= effect->onOverlayKeyUp_public( time, renderScale, view, + QtEnumConvert::fromQtKey( (Qt::Key)e->key() ), QtEnumConvert::fromQtModifiers( e->modifiers() ) ); } - effect->setCurrentViewportForOverlays_public(_imp->viewer); - didSomething |= effect->onOverlayKeyUp_public( time, renderScale, view, - QtEnumConvert::fromQtKey( (Qt::Key)e->key() ), QtEnumConvert::fromQtModifiers( e->modifiers() ) ); + } /* @@ -908,9 +858,9 @@ ViewerTab::notifyOverlaysKeyUp(const RenderScale & renderScale, bool ViewerTab::notifyOverlaysKeyRepeat_internal(const NodePtr& node, const RenderScale & renderScale, - QKeyEvent* e, Key k, - KeyboardModifiers km) + KeyboardModifiers km, + Qt::Key qKey, const Qt::KeyboardModifiers& mods) { ViewIdx view = getCurrentView(); double time = getGui()->getApp()->getTimeLine()->currentFrame(); @@ -931,23 +881,28 @@ ViewerTab::notifyOverlaysKeyRepeat_internal(const NodePtr& node, } #endif - if ( _imp->currentRoto.first && ( node == _imp->currentRoto.first->getNode() ) ) { - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - if ( _imp->currentRoto.second->keyRepeat(time, renderScale, view, e) ) { - _imp->lastOverlayNode = node; - - return true; - } - } - } else { - //if (_imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible()) { - // if (_imp->currentTracker.second->loseFocus(scaleX, scaleY,e)) { - // return true; - // } - //} + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(node); + if (!isInActiveViewerUI) { EffectInstPtr effect = node->getEffectInstance(); assert(effect); effect->setCurrentViewportForOverlays_public(_imp->viewer); + + + // Intercept plug-in defined shortcuts + const KnobsVec& knobs = effect->getKnobs(); + std::string pluginShortcutGroup; + for (KnobsVec::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { + if ((*it)->getInViewerContextHasShortcut() && !(*it)->getInViewerContextSecret()) { + if (pluginShortcutGroup.empty()) { + pluginShortcutGroup = effect->getNode()->getPlugin()->getPluginShortcutGroup().toStdString(); + } + if (isKeybind(pluginShortcutGroup, (*it)->getName(), mods, qKey)) { + effect->onKnobValueChanged_public(it->get(), eValueChangedReasonUserEdited, time, ViewSpec(0), true); + return true; + } + } + } + bool didSmthing = effect->onOverlayKeyRepeat_public(time, renderScale, view, k, km); if (didSmthing) { //http://openfx.sourceforge.net/Documentation/1.3/ofxProgrammingReference.html @@ -958,8 +913,10 @@ ViewerTab::notifyOverlaysKeyRepeat_internal(const NodePtr& node, return true; } + } + return false; } @@ -971,16 +928,18 @@ ViewerTab::notifyOverlaysKeyRepeat(const RenderScale & renderScale, return false; } - Key natronKey = QtEnumConvert::fromQtKey( (Qt::Key)e->key() ); - KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers( e->modifiers() ); + Qt::Key qKey = (Qt::Key)e->key(); + Qt::KeyboardModifiers qMods = e->modifiers(); + Key natronKey = QtEnumConvert::fromQtKey( qKey); + KeyboardModifiers natronMod = QtEnumConvert::fromQtModifiers( qMods ); NodesList nodes; - getGui()->getNodesEntitledForOverlays(nodes); + NodePtr lastOverlay = _imp->lastOverlayNode.lock(); if (lastOverlay) { for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { if (*it == lastOverlay) { - if ( notifyOverlaysKeyRepeat_internal(*it, renderScale, e, natronKey, natronMod) ) { + if ( notifyOverlaysKeyRepeat_internal(*it, renderScale, natronKey, natronMod, qKey, qMods) ) { return true; } else { nodes.erase(it); @@ -992,7 +951,7 @@ ViewerTab::notifyOverlaysKeyRepeat(const RenderScale & renderScale, for (NodesList::reverse_iterator it = nodes.rbegin(); it != nodes.rend(); ++it) { - if ( notifyOverlaysKeyRepeat_internal(*it, renderScale, e, natronKey, natronMod) ) { + if ( notifyOverlaysKeyRepeat_internal(*it, renderScale, natronKey, natronMod, qKey, qMods) ) { return true; } } @@ -1041,19 +1000,14 @@ ViewerTab::notifyOverlaysFocusGained(const RenderScale & renderScale) } #endif - if ( _imp->currentTracker.first && ( (*it) == _imp->currentTracker.first->getNode() ) ) { - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - if ( _imp->currentTracker.second->gainFocus(time, renderScale, view) ) { - ret = true; - } + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(*it); + if (!isInActiveViewerUI) { + effect->setCurrentViewportForOverlays_public(_imp->viewer); + bool didSmthing = effect->onOverlayFocusGained_public(time, renderScale, view); + if (didSmthing) { + ret = true; } } - - effect->setCurrentViewportForOverlays_public(_imp->viewer); - bool didSmthing = effect->onOverlayFocusGained_public(time, renderScale, view); - if (didSmthing) { - ret = true; - } } if ( !ret && (getGui()->getApp()->getOverlayRedrawRequestsCount() > 0) ) { @@ -1095,27 +1049,18 @@ ViewerTab::notifyOverlaysFocusLost(const RenderScale & renderScale) time = transformedTime; } #endif + bool isInActiveViewerUI = _imp->hasInactiveNodeViewerContext(*it); + if (!isInActiveViewerUI) { + EffectInstPtr effect = (*it)->getEffectInstance(); + assert(effect); - if ( _imp->currentRoto.first && ( (*it) == _imp->currentRoto.first->getNode() ) ) { - if ( _imp->currentRoto.second && _imp->currentRoto.first->isSettingsPanelVisible() ) { - _imp->currentRoto.second->focusOut(time, view); - } - } else if ( _imp->currentTracker.first && ( (*it) == _imp->currentTracker.first->getNode() ) ) { - if ( _imp->currentTracker.second && _imp->currentTracker.first->isSettingsPanelVisible() ) { - if ( _imp->currentTracker.second->loseFocus(time, renderScale, view) ) { - ret = true; - } + effect->setCurrentViewportForOverlays_public(_imp->viewer); + bool didSmthing = effect->onOverlayFocusLost_public(time, renderScale, view); + if (didSmthing) { + ret = true; } } - EffectInstPtr effect = (*it)->getEffectInstance(); - assert(effect); - - effect->setCurrentViewportForOverlays_public(_imp->viewer); - bool didSmthing = effect->onOverlayFocusLost_public(time, renderScale, view); - if (didSmthing) { - ret = true; - } } diff --git a/Gui/ViewerTab30.cpp b/Gui/ViewerTab30.cpp index 9e18185b70..8818fda8c0 100644 --- a/Gui/ViewerTab30.cpp +++ b/Gui/ViewerTab30.cpp @@ -47,11 +47,10 @@ #include "Gui/InfoViewerWidget.h" #include "Gui/MultiInstancePanel.h" #include "Gui/NodeGui.h" -#include "Gui/RotoGui.h" +#include "Gui/NodeViewerContext.h" #include "Gui/ScaleSliderQWidget.h" #include "Gui/SpinBox.h" #include "Gui/TimeLineGui.h" -#include "Gui/TrackerGui.h" #include "Gui/ViewerGL.h" @@ -340,11 +339,11 @@ ViewerTab::setInfoBarResolution(const Format & f) _imp->infoWidget[0]->setResolution(f); _imp->infoWidget[1]->setResolution(f); } - +#if 0 void -ViewerTab::createTrackerInterface(NodeGui* n) +ViewerTab::createTrackerInterface(const NodeGuiPtr& n) { - std::map::iterator found = _imp->trackerNodes.find(n); + std::map::iterator found = _imp->trackerNodes.find(n); if ( found != _imp->trackerNodes.end() ) { return; @@ -375,7 +374,7 @@ ViewerTab::createTrackerInterface(NodeGui* n) return; } - QObject::connect( n, SIGNAL(settingsPanelClosed(bool)), this, SLOT(onTrackerNodeGuiSettingsPanelClosed(bool)) ); + QObject::connect( n.get(), SIGNAL(settingsPanelClosed(bool)), this, SLOT(onTrackerNodeGuiSettingsPanelClosed(bool)) ); if ( n->isSettingsPanelVisible() ) { setTrackerInterface(n); } else if (tracker) { @@ -388,18 +387,18 @@ ViewerTab::createTrackerInterface(NodeGui* n) } void -ViewerTab::setTrackerInterface(NodeGui* n) +ViewerTab::setTrackerInterface(const NodeGuiPtr& n) { assert(n); - std::map::iterator it = _imp->trackerNodes.find(n); + std::map::iterator it = _imp->trackerNodes.find(n); if ( it != _imp->trackerNodes.end() ) { - if (_imp->currentTracker.first == n) { + if (_imp->currentTracker.first.lock() == n) { return; } ///remove any existing tracker gui - if (_imp->currentTracker.first != NULL) { - removeTrackerInterface(_imp->currentTracker.first, false, true); + if (_imp->currentTracker.first.lock()) { + removeTrackerInterface(_imp->currentTracker.first.lock(), false, true); } ///Add the widgets @@ -433,11 +432,11 @@ ViewerTab::setTrackerInterface(NodeGui* n) } void -ViewerTab::removeTrackerInterface(NodeGui* n, +ViewerTab::removeTrackerInterface(const NodeGuiPtr& n, bool permanently, bool removeAndDontSetAnother) { - std::map::iterator it = _imp->trackerNodes.find(n); + std::map::iterator it = _imp->trackerNodes.find(n); if ( it != _imp->trackerNodes.end() ) { if ( !getGui() ) { @@ -448,7 +447,7 @@ ViewerTab::removeTrackerInterface(NodeGui* n, return; } - if (_imp->currentTracker.first == n) { + if (_imp->currentTracker.first .lock()== n) { ///Remove the widgets of the current tracker node int buttonsBarIndex = _imp->mainLayout->indexOf( _imp->currentTracker.second->getButtonsBar() ); @@ -463,19 +462,19 @@ ViewerTab::removeTrackerInterface(NodeGui* n, } if (!removeAndDontSetAnother) { ///If theres another tracker node, set it as the current tracker interface - std::map::iterator newTracker = _imp->trackerNodes.end(); - for (std::map::iterator it2 = _imp->trackerNodes.begin(); it2 != _imp->trackerNodes.end(); ++it2) { - if ( (it2->second != it->second) && it2->first->isSettingsPanelVisible() ) { + std::map::iterator newTracker = _imp->trackerNodes.end(); + for (std::map::iterator it2 = _imp->trackerNodes.begin(); it2 != _imp->trackerNodes.end(); ++it2) { + if ( (it2->second != it->second) && it2->first.lock()->isSettingsPanelVisible() ) { newTracker = it2; break; } } - _imp->currentTracker.first = 0; + _imp->currentTracker.first.reset(); _imp->currentTracker.second = 0; if ( newTracker != _imp->trackerNodes.end() ) { - setTrackerInterface(newTracker->first); + setTrackerInterface(newTracker->first.lock()); } } } @@ -487,253 +486,262 @@ ViewerTab::removeTrackerInterface(NodeGui* n, } } // ViewerTab::removeTrackerInterface -void -ViewerTab::createRotoInterface(NodeGui* n) -{ - RotoGui* roto = new RotoGui( n, this, getRotoGuiSharedData(n) ); - QObject::connect( roto, SIGNAL(selectedToolChanged(int)), getGui(), SLOT(onRotoSelectedToolChanged(int)) ); - std::pair::iterator, bool> ret = _imp->rotoNodes.insert( std::make_pair(n, roto) ); - assert(ret.second); - if (!ret.second) { - qDebug() << "ViewerTab::createRotoInterface() failed"; - delete roto; +#endif +/** + * @brief Creates a new viewer interface context for this node. This is not shared among viewers. + **/ +void +ViewerTab::createNodeViewerInterface(const NodeGuiPtr& n) +{ + if (!n) { + return; + } + std::map::iterator found = _imp->nodesContext.find(n); + if (found != _imp->nodesContext.end()) { + // Already exists return; } - QObject::connect( n, SIGNAL(settingsPanelClosed(bool)), this, SLOT(onRotoNodeGuiSettingsPanelClosed(bool)) ); + + boost::shared_ptr nodeContext(new NodeViewerContext(n, this)); + nodeContext->createGui(); + _imp->nodesContext.insert(std::make_pair(n, nodeContext)); + if ( n->isSettingsPanelVisible() ) { - setRotoInterface(n); + setPluginViewerInterface(n); } else { - roto->getToolBar()->hide(); - roto->getCurrentButtonsBar()->hide(); + QToolBar *toolbar = nodeContext->getToolBar(); + if (toolbar) { + toolbar->hide(); + } + QWidget* w = nodeContext->getContainerWidget(); + if (w) { + w->hide(); + } } + } +/** + * @brief Set the current viewer interface for a given plug-in to be the one of the given node + **/ void -ViewerTab::setRotoInterface(NodeGui* n) +ViewerTab::setPluginViewerInterface(const NodeGuiPtr& n) { - assert(n); - std::map::iterator it = _imp->rotoNodes.find(n); - if ( it != _imp->rotoNodes.end() ) { - if (_imp->currentRoto.first == n) { - return; - } + if (!n) { + return; + } + std::map::iterator it = _imp->nodesContext.find(n); + if ( it == _imp->nodesContext.end() ) { + return; + } - ///remove any existing roto gui - if (_imp->currentRoto.first != NULL) { - removeRotoInterface(_imp->currentRoto.first, false, true); - } + std::string pluginID = n->getNode()->getPluginID(); + std::list::iterator foundActive = _imp->findActiveNodeContextForPlugin(pluginID); + NodeGuiPtr activeNodeForPlugin; + if (foundActive != _imp->currentNodeContext.end()) { + activeNodeForPlugin = foundActive->currentNode.lock(); + } - ///Add the widgets - QToolBar* toolBar = it->second->getToolBar(); - _imp->viewerLayout->insertWidget(0, toolBar); + // If already active, return + if (activeNodeForPlugin == n) { + return; + } + + QToolBar* newToolbar = it->second->getToolBar(); + QWidget* newContainer = it->second->getContainerWidget(); + + // Plugin has no viewer interface + if (!newToolbar && !newContainer) { + return; + } + + ///remove any existing roto gui + if (activeNodeForPlugin) { + removeNodeViewerInterface(activeNodeForPlugin, false /*permanantly*/, /*setAnother*/ false); + } + + // Add the widgets + + if (newToolbar) { + _imp->viewerLayout->insertWidget(0, newToolbar); { QMutexLocker l(&_imp->visibleToolbarsMutex); if (_imp->leftToolbarVisible) { - toolBar->show(); + newToolbar->show(); } } - - ///If there's a tracker add it right after the tracker - int index; - if (_imp->currentTracker.second) { - index = _imp->mainLayout->indexOf( _imp->currentTracker.second->getButtonsBar() ); - assert(index != -1); - if (index >= 0) { - ++index; - } - } else { - index = _imp->mainLayout->indexOf(_imp->viewerContainer); + } + + // If there are other interface active, add it after them + int index; + if (_imp->currentNodeContext.empty()) { + // insert before the viewer + index = _imp->mainLayout->indexOf(_imp->viewerContainer); + } else { + // Remove the oldest opened interface if we reached the maximum + int maxNodeContextOpened = appPTR->getCurrentSettings()->getMaxOpenedNodesViewerContext(); + if ((int)_imp->currentNodeContext.size() == maxNodeContextOpened) { + const ViewerTabPrivate::PluginViewerContext& oldestNodeViewerInterface = _imp->currentNodeContext.front(); + removeNodeViewerInterface(oldestNodeViewerInterface.currentNode.lock(), false /*permanantly*/, false /*setAnother*/); } - assert(index >= 0); + + QWidget* container = _imp->currentNodeContext.back().currentContext->getContainerWidget(); + index = _imp->mainLayout->indexOf(container); + assert(index != -1); if (index >= 0) { - QWidget* buttonsBar = it->second->getCurrentButtonsBar(); - assert(buttonsBar); - if (buttonsBar) { - _imp->mainLayout->insertWidget(index, buttonsBar); - { - QMutexLocker l(&_imp->visibleToolbarsMutex); - if (_imp->topToolbarVisible) { - buttonsBar->show(); - } - } - } + ++index; } - QObject::connect( it->second, SIGNAL(roleChanged(int,int)), this, SLOT(onRotoRoleChanged(int,int)) ); - _imp->currentRoto.first = n; - _imp->currentRoto.second = it->second; - _imp->viewer->redraw(); } -} // ViewerTab::setRotoInterface - -void -ViewerTab::removeRotoInterface(NodeGui* n, - bool permanently, - bool removeAndDontSetAnother) -{ - std::map::iterator it = _imp->rotoNodes.find(n); - - if ( it != _imp->rotoNodes.end() ) { - if (_imp->currentRoto.first == n) { - QObject::disconnect( _imp->currentRoto.second, SIGNAL(roleChanged(int,int)), this, SLOT(onRotoRoleChanged(int,int)) ); - ///Remove the widgets of the current roto node - assert(_imp->viewerLayout->count() > 1); - QLayoutItem* currentToolBarItem = _imp->viewerLayout->itemAt(0); - QToolBar* currentToolBar = qobject_cast( currentToolBarItem->widget() ); - currentToolBar->hide(); - assert( currentToolBar == _imp->currentRoto.second->getToolBar() ); - _imp->viewerLayout->removeItem(currentToolBarItem); - int buttonsBarIndex = _imp->mainLayout->indexOf( _imp->currentRoto.second->getCurrentButtonsBar() ); - assert(buttonsBarIndex >= 0); - QLayoutItem* buttonsBar = _imp->mainLayout->itemAt(buttonsBarIndex); - assert(buttonsBar); - _imp->mainLayout->removeItem(buttonsBar); - buttonsBar->widget()->hide(); - - if (!removeAndDontSetAnother) { - ///If theres another roto node, set it as the current roto interface - std::map::iterator newRoto = _imp->rotoNodes.end(); - for (std::map::iterator it2 = _imp->rotoNodes.begin(); it2 != _imp->rotoNodes.end(); ++it2) { - if ( (it2->second != it->second) && it2->first->isSettingsPanelVisible() ) { - newRoto = it2; - break; - } - } - - _imp->currentRoto.first = 0; - _imp->currentRoto.second = 0; - - if ( newRoto != _imp->rotoNodes.end() ) { - setRotoInterface(newRoto->first); + assert(index >= 0); + if (index >= 0) { + assert(newContainer); + if (newContainer) { + _imp->mainLayout->insertWidget(index, newContainer); + { + QMutexLocker l(&_imp->visibleToolbarsMutex); + if (_imp->topToolbarVisible) { + newContainer->show(); } } } - - if (permanently) { - delete it->second; - _imp->rotoNodes.erase(it); - } } -} + ViewerTabPrivate::PluginViewerContext p; + p.pluginID = pluginID; + p.currentNode = n; + p.currentContext = it->second; + _imp->currentNodeContext.push_back(p); -void -ViewerTab::getRotoContext(std::map* rotoNodes, - std::pair* currentRoto) const -{ - *rotoNodes = _imp->rotoNodes; - *currentRoto = _imp->currentRoto; -} + _imp->viewer->redraw(); -void -ViewerTab::getTrackerContext(std::map* trackerNodes, - std::pair* currentTracker) const -{ - *trackerNodes = _imp->trackerNodes; - *currentTracker = _imp->currentTracker; } +/** + * @brief Removes the interface associated to the given node. + * @param permanently The interface is destroyed instead of being hidden + * @param setAnotherFromSamePlugin If true, if another node of the same plug-in is a candidate for a viewer interface, it will replace the existing + * viewer interface for this plug-in + **/ void -ViewerTab::onRotoRoleChanged(int previousRole, - int newRole) +ViewerTab::removeNodeViewerInterface(const NodeGuiPtr& n, bool permanently, bool setAnotherFromSamePlugin) { - RotoGui* roto = qobject_cast( sender() ); + std::map::iterator found = _imp->nodesContext.find(n); + if ( found == _imp->nodesContext.end() ) { + return; + } - if (roto) { - assert(roto == _imp->currentRoto.second); + std::string pluginID = n->getNode()->getPluginID(); + NodeGuiPtr activeNodeForPlugin; + QToolBar* activeItemToolBar = 0; + QWidget* activeItemContainer = 0; - ///Remove the previous buttons bar - QWidget* previousBar = _imp->currentRoto.second->getButtonsBar( (RotoGui::RotoRoleEnum)previousRole ); - assert(previousBar); - if (previousBar) { - int buttonsBarIndex = _imp->mainLayout->indexOf(previousBar); - assert(buttonsBarIndex >= 0); - if (buttonsBarIndex >= 0) { - _imp->mainLayout->removeItem( _imp->mainLayout->itemAt(buttonsBarIndex) ); - } - previousBar->hide(); + { + // Keep the iterator under this scope since we erase it + std::list::iterator foundActive = _imp->findActiveNodeContextForPlugin(pluginID); + if (foundActive != _imp->currentNodeContext.end()) { + activeNodeForPlugin = foundActive->currentNode.lock(); + activeItemToolBar = foundActive->currentContext->getToolBar(); + activeItemContainer = foundActive->currentContext->getContainerWidget(); + _imp->currentNodeContext.erase(foundActive); } + } - ///Set the new buttons bar - int viewerIndex = _imp->mainLayout->indexOf(_imp->viewerContainer); - assert(viewerIndex >= 0); - if (viewerIndex >= 0) { - QWidget* currentBar = _imp->currentRoto.second->getButtonsBar( (RotoGui::RotoRoleEnum)newRole ); - assert(currentBar); - if (currentBar) { - _imp->mainLayout->insertWidget( viewerIndex, currentBar); - currentBar->show(); - assert(_imp->mainLayout->itemAt(viewerIndex)->widget() == currentBar); + // Remove the widgets of the current node + if (activeItemToolBar) { + int nLayoutItems = _imp->viewerLayout->count(); + for (int i = 0; i < nLayoutItems; ++i) { + QLayoutItem* item = _imp->viewerLayout->itemAt(i); + if (item->widget() == activeItemToolBar) { + activeItemToolBar->hide(); + _imp->viewerLayout->removeItem(item); + break; } } } -} + if (activeItemContainer) { + int buttonsBarIndex = _imp->mainLayout->indexOf(activeItemContainer); + assert(buttonsBarIndex >= 0); + if (buttonsBarIndex >= 0) { + _imp->mainLayout->removeWidget(activeItemContainer); + } + activeItemContainer->hide(); + } -void -ViewerTab::updateRotoSelectedTool(int tool, - RotoGui* sender) -{ - if ( _imp->currentRoto.second && (_imp->currentRoto.second != sender) ) { - _imp->currentRoto.second->setCurrentTool( (RotoGui::RotoToolEnum)tool, false ); + if (setAnotherFromSamePlugin) { + ///If theres another roto node, set it as the current roto interface + std::map::iterator newInterface = _imp->nodesContext.end(); + for (std::map::iterator it2 = _imp->nodesContext.begin(); it2 != _imp->nodesContext.end(); ++it2) { + NodeGuiPtr otherNode = it2->first.lock(); + if (!otherNode) { + continue; + } + if (otherNode == n) { + continue; + } + if (otherNode->getNode()->getPluginID() != pluginID) { + continue; + } + if ( (it2->second != found->second) && it2->first.lock()->isSettingsPanelVisible() ) { + newInterface = it2; + break; + } + } + + if ( newInterface != _imp->nodesContext.end() ) { + setPluginViewerInterface(newInterface->first.lock()); + } } -} -boost::shared_ptr -ViewerTab::getRotoGuiSharedData(NodeGui* node) const -{ - std::map::const_iterator found = _imp->rotoNodes.find(node); - if ( found == _imp->rotoNodes.end() ) { - return boost::shared_ptr(); - } else { - return found->second->getRotoGuiSharedData(); + if (permanently) { + found->second.reset(); + _imp->nodesContext.erase(found); } -} -void -ViewerTab::onRotoEvaluatedForThisViewer() -{ - getGui()->onViewerRotoEvaluated(this); } +/** + * @brief Get the list of all nodes that have a user interface created on this viewer (but not necessarily displayed) + * and a list for each plug-in of the active node. + **/ void -ViewerTab::onRotoNodeGuiSettingsPanelClosed(bool closed) +ViewerTab::getNodesViewerInterface(std::list* nodesWithUI, + std::list* perPluginActiveUI) const { - NodeGui* n = qobject_cast( sender() ); - - if (n) { - if (closed) { - removeRotoInterface(n, false, false); - } else { - if (n != _imp->currentRoto.first) { - setRotoInterface(n); - } + for (std::map::const_iterator it = _imp->nodesContext.begin(); it != _imp->nodesContext.end(); ++it) { + NodeGuiPtr n = it->first.lock(); + if (n) { + nodesWithUI->push_back(n); + } + } + for (std::list::const_iterator it = _imp->currentNodeContext.begin() ; it != _imp->currentNodeContext.end(); ++it) { + NodeGuiPtr n = it->currentNode.lock(); + if (n) { + perPluginActiveUI->push_back(n); } } } + void -ViewerTab::onTrackerNodeGuiSettingsPanelClosed(bool closed) +ViewerTab::updateSelectedToolForNode(const QString& toolID, const NodeGuiPtr& node) { - NodeGui* n = qobject_cast( sender() ); - - if (n) { - if (closed) { - removeTrackerInterface(n, false, false); - } else { - if (n != _imp->currentTracker.first) { - setTrackerInterface(n); - } - } + std::map::iterator found = _imp->nodesContext.find(node); + if (found == _imp->nodesContext.end()) { + // Already exists + return; } + found->second->setCurrentTool(toolID, false /*notifyNode*/); } + void ViewerTab::notifyGuiClosing() { _imp->timeLineGui->discardGuiPointer(); - for (std::map::iterator it = _imp->rotoNodes.begin(); it != _imp->rotoNodes.end(); ++it) { + for (std::map::iterator it = _imp->nodesContext.begin(); it != _imp->nodesContext.end(); ++it) { it->second->notifyGuiClosing(); } } diff --git a/Gui/ViewerTab40.cpp b/Gui/ViewerTab40.cpp index 821355c783..087c47c5b7 100644 --- a/Gui/ViewerTab40.cpp +++ b/Gui/ViewerTab40.cpp @@ -55,12 +55,10 @@ #include "Gui/GuiAppInstance.h" #include "Gui/NodeGraph.h" #include "Gui/RenderStatsDialog.h" -#include "Gui/RotoGui.h" #include "Gui/ScaleSliderQWidget.h" #include "Gui/SpinBox.h" #include "Gui/TabWidget.h" #include "Gui/TimeLineGui.h" -#include "Gui/TrackerGui.h" #include "Gui/ViewerGL.h" @@ -422,9 +420,13 @@ ViewerTab::setLeftToolbarVisible(bool visible) QMutexLocker l(&_imp->visibleToolbarsMutex); _imp->leftToolbarVisible = visible; - if (_imp->currentRoto.second) { - _imp->currentRoto.second->getToolBar()->setVisible(_imp->leftToolbarVisible); + for (std::list::iterator it = _imp->currentNodeContext.begin(); it != _imp->currentNodeContext.end(); ++it) { + QToolBar* bar = it->currentContext->getToolBar(); + if (bar) { + bar->setVisible(_imp->leftToolbarVisible); + } } + } void @@ -443,15 +445,8 @@ ViewerTab::setTopToolbarVisible(bool visible) _imp->topToolbarVisible = visible; _imp->firstSettingsRow->setVisible(_imp->topToolbarVisible); _imp->secondSettingsRow->setVisible(_imp->topToolbarVisible); - if (_imp->currentRoto.second) { - _imp->currentRoto.second->getCurrentButtonsBar()->setVisible(_imp->topToolbarVisible); - } - if (_imp->currentTracker.second) { - QWidget* buttonsBar = _imp->currentTracker.second->getButtonsBar(); - assert(buttonsBar); - if (buttonsBar) { - buttonsBar->setVisible(_imp->topToolbarVisible); - } + for (std::list::iterator it = _imp->currentNodeContext.begin(); it != _imp->currentNodeContext.end(); ++it) { + it->currentContext->getContainerWidget()->setVisible(_imp->topToolbarVisible); } } diff --git a/Gui/ViewerTabPrivate.cpp b/Gui/ViewerTabPrivate.cpp index 57f04bb6d4..6f839e7b28 100644 --- a/Gui/ViewerTabPrivate.cpp +++ b/Gui/ViewerTabPrivate.cpp @@ -40,6 +40,7 @@ #include "Gui/ChannelsComboBox.h" #include "Gui/ClickableLabel.h" #include "Gui/Gui.h" +#include "Gui/NodeGui.h" #include "Gui/GuiAppInstance.h" #include "Gui/ViewerTab.h" @@ -128,10 +129,8 @@ ViewerTabPrivate::ViewerTabPrivate(ViewerTab* publicInterface, , userFps(24) , turboButton(NULL) , timeLineGui(NULL) - , rotoNodes() - , currentRoto() - , trackerNodes() - , currentTracker() + , nodesContext() + , currentNodeContext() , inputNamesMap() , compOperatorMutex() , compOperator(eViewerCompositingOperatorNone) @@ -153,8 +152,6 @@ ViewerTabPrivate::ViewerTabPrivate(ViewerTab* publicInterface, , hasCaughtPenMotionWhileDragging(false) { infoWidget[0] = infoWidget[1] = NULL; - currentRoto.first = NULL; - currentRoto.second = NULL; } #ifdef NATRON_TRANSFORM_AFFECTS_OVERLAYS @@ -356,4 +353,29 @@ ViewerTabPrivate::getComponentsAvailabel(std::set* comps) const } } +std::list::iterator +ViewerTabPrivate::findActiveNodeContextForPlugin(const std::string& pluginID) +{ + for (std::list::iterator it = currentNodeContext.begin(); it!=currentNodeContext.end(); ++it) { + if (it->pluginID == pluginID) { + return it; + } + } + return currentNodeContext.end(); +} + +bool +ViewerTabPrivate::hasInactiveNodeViewerContext(const NodePtr& node) { + std::list::iterator found = findActiveNodeContextForPlugin(node->getPluginID()); + if (found == currentNodeContext.end()) { + return false; + } + NodeGuiPtr n = found->currentNode.lock(); + if (!n) { + return false; + } + return n->getNode() != node; +} + + NATRON_NAMESPACE_EXIT; diff --git a/Gui/ViewerTabPrivate.h b/Gui/ViewerTabPrivate.h index 2bd4cb5430..d87edb4f61 100644 --- a/Gui/ViewerTabPrivate.h +++ b/Gui/ViewerTabPrivate.h @@ -43,6 +43,7 @@ CLANG_DIAG_ON(uninitialized) #include "Gui/ComboBox.h" #include "Gui/GuiFwd.h" +#include "Gui/NodeViewerContext.h" #define NATRON_TRANSFORM_AFFECTS_OVERLAYS @@ -152,10 +153,24 @@ struct ViewerTabPrivate /*frame seeker*/ TimeLineGui* timeLineGui; - std::map rotoNodes; - std::pair currentRoto; - std::map trackerNodes; - std::pair currentTracker; + + // This is all nodes that have a viewer context + std::map nodesContext; + + /* + Tells for a given plug-in ID, which nodes is currently activated in the viewer interface + */ + struct PluginViewerContext { + std::string pluginID; + NodeGuiWPtr currentNode; + NodeViewerContextPtr currentContext; + }; + + + // This is the current active context for each plug-in + // We don't use a map because we need to retain the insertion order for each plug-in + std::list currentNodeContext; + InputNamesMap inputNamesMap; mutable QMutex compOperatorMutex; ViewerCompositingOperatorEnum compOperator; @@ -197,6 +212,11 @@ struct ViewerTabPrivate #endif void getComponentsAvailabel(std::set* comps) const; + + std::list::iterator findActiveNodeContextForPlugin(const std::string& pluginID); + + // Returns true if this node has a viewer context but it is not active + bool hasInactiveNodeViewerContext(const NodePtr& node); }; NATRON_NAMESPACE_EXIT; diff --git a/INSTALL_FREEBSD.md b/INSTALL_FREEBSD.md index 39a449cb44..73a69c3740 100644 --- a/INSTALL_FREEBSD.md +++ b/INSTALL_FREEBSD.md @@ -9,7 +9,7 @@ Natron on FreeBSD. In order to have Natron compiling, first you need to install the required libraries. ``` -pkg install qt4 boost-all pyside-py27 glew expat cairo pkgconf +pkg install qt4 boost-all pyside-py27 expat cairo pkgconf ``` ### Submodules diff --git a/INSTALL_LINUX.md b/INSTALL_LINUX.md index 61c596cf02..2259db5e06 100644 --- a/INSTALL_LINUX.md +++ b/INSTALL_LINUX.md @@ -8,7 +8,6 @@ Natron on GNU/Linux. - [Qt4](#qt-486) - [Boost](#boost) - [Expat](#expat) - - [Glew](#glew) - [Cairo](#cairo) - [Pyside](#pyside) - [Shiboken](#shiboken) @@ -55,11 +54,6 @@ Alternatively you can install boost from [boost download](http://www.boost.org/u You can download it with your package manager. The package depends on your distribution. -### GLEW - -You can download it with your package manager. -The package depends on your distribution. - ### Cairo Natron links statically to cairo. Sometimes you can find a static version in development packages. @@ -174,7 +168,7 @@ global.pri file. To enable an option just add `CONFIG+=