Beispiel #1
0
NodePtr
AnimationModulePrivate::getNearestTimeNodeFromOutputs_recursive(const NodePtr& node,
                                                                const NodeCollectionPtr& collection,
                                                                std::list<NodePtr>& markedNodes) const
{
    const NodesWList &outputs = node->getOutputs();

    if ( std::find(markedNodes.begin(), markedNodes.end(), node) != markedNodes.end() ) {
        return NodePtr();
    }

    // If we go outside of the initial node group scope, return
    if (node->getGroup() != collection) {
        return NodePtr();
    }
    markedNodes.push_back(node);
    for (NodesWList::const_iterator it = outputs.begin(); it != outputs.end(); ++it) {
        NodePtr output = it->lock();
        std::string pluginID = output->getPluginID();

        if ( (pluginID == PLUGINID_OFX_RETIME) || ( pluginID == PLUGINID_OFX_TIMEOFFSET) || ( pluginID == PLUGINID_OFX_FRAMERANGE) ) {
            return output;
        } else {
            NodePtr ret =  getNearestTimeNodeFromOutputs_recursive(output, collection, markedNodes);
            if (ret) {
                return ret;
            }
        }
    }

    return NodePtr();
}
Beispiel #2
0
void
NodeGraph::moveNodesForIdealPosition(const NodeGuiPtr &node,
                                     const NodeGuiPtr &selected,
                                     bool autoConnect)
{
    BackdropGuiPtr isBd = toBackdropGui(node);

    if (isBd) {
        return;
    }

    QRectF viewPos = visibleSceneRect();

    ///3 possible values:
    /// 0 = default , i.e: we pop the node in the middle of the graph's current view
    /// 1 = pop the node above the selected node and move the inputs of the selected node a little
    /// 2 = pop the node below the selected node and move the outputs of the selected node a little
    int behavior = 0;

    if (!selected || !autoConnect) {
        behavior = 0;
    } else {
        ///this function is redundant with Project::autoConnect, depending on the node selected
        ///and this node we make some assumptions on to where we could put the node.

        //        1) selected is output
        //          a) created is output --> fail
        //          b) created is input --> connect input
        //          c) created is regular --> connect input
        //        2) selected is input
        //          a) created is output --> connect output
        //          b) created is input --> fail
        //          c) created is regular --> connect output
        //        3) selected is regular
        //          a) created is output--> connect output
        //          b) created is input --> connect input
        //          c) created is regular --> connect output

        ///1)
        if ( selected->getNode()->isOutputNode() ) {
            ///case 1-a) just do default we don't know what else to do
            if ( node->getNode()->isOutputNode() ) {
                behavior = 0;
            } else {
                ///for either cases 1-b) or 1-c) we just connect the created node as input of the selected node.
                behavior = 1;
            }
        }
        ///2) and 3) are similar except for case b)
        else {
            ///case 2 or 3- a): connect the created node as output of the selected node.
            if ( node->getNode()->isOutputNode() ) {
                behavior = 2;
            }
            ///case b)
            else if ( node->getNode()->isInputNode() ) {
                if ( selected->getNode()->getEffectInstance()->isReader() ) {
                    ///case 2-b) just do default we don't know what else to do
                    behavior = 0;
                } else {
                    ///case 3-b): connect the created node as input of the selected node
                    behavior = 1;
                }
            }
            ///case c) connect created as output of the selected node
            else {
                behavior = 2;
            }
        }
    }

    NodePtr createdNodeInternal = node->getNode();
    NodePtr selectedNodeInternal;
    if (selected) {
        selectedNodeInternal = selected->getNode();
    }


    ///if behaviour is 1 , just check that we can effectively connect the node to avoid moving them for nothing
    ///otherwise fallback on behaviour 0
    if (behavior == 1) {
        const std::vector<NodeWPtr > & inputs = selected->getNode()->getGuiInputs();
        bool oneInputEmpty = false;
        for (std::size_t i = 0; i < inputs.size(); ++i) {
            if ( !inputs[i].lock() ) {
                oneInputEmpty = true;
                break;
            }
        }
        if (!oneInputEmpty) {
            behavior = 0;
        }
    }


    ProjectPtr proj = getGui()->getApp()->getProject();


    ///default
    QPointF position;
    if (behavior == 0) {
        position.setX( ( viewPos.bottomRight().x() + viewPos.topLeft().x() ) / 2. );
        position.setY( ( viewPos.topLeft().y() + viewPos.bottomRight().y() ) / 2. );
    } else if (behavior == 1) {
        ///pop it above the selected node

        ///If this is the first connected input, insert it in a "linear" way so the tree remains vertical
        int nbConnectedInput = 0;
        const std::vector<Edge*> & selectedNodeInputs = selected->getInputsArrows();
        for (std::vector<Edge*>::const_iterator it = selectedNodeInputs.begin(); it != selectedNodeInputs.end(); ++it) {
            NodeGuiPtr input;
            if (*it) {
                input = (*it)->getSource();
            }
            if (input) {
                ++nbConnectedInput;
            }
        }

        ///connect it to the first input
        QSize selectedNodeSize = selected->getSize();
        QSize createdNodeSize = node->getSize();


        if (nbConnectedInput == 0) {
            QPointF selectedNodeMiddlePos = selected->scenePos() +
                                            QPointF(selectedNodeSize.width() / 2, selectedNodeSize.height() / 2);


            position.setX(selectedNodeMiddlePos.x() - createdNodeSize.width() / 2);
            position.setY( selectedNodeMiddlePos.y() - selectedNodeSize.height() / 2 - NodeGui::DEFAULT_OFFSET_BETWEEN_NODES
                           - createdNodeSize.height() );

            QRectF createdNodeRect( position.x(), position.y(), createdNodeSize.width(), createdNodeSize.height() );

            ///now that we have the position of the node, move the inputs of the selected node to make some space for this node

            for (std::vector<Edge*>::const_iterator it = selectedNodeInputs.begin(); it != selectedNodeInputs.end(); ++it) {
                if ( (*it)->hasSource() ) {
                    (*it)->getSource()->moveAbovePositionRecursively(createdNodeRect);
                }
            }

            int selectedInput = selectedNodeInternal->getPreferredInputForConnection();
            if (selectedInput != -1) {
                bool ok = proj->connectNodes(selectedInput, createdNodeInternal, selectedNodeInternal, true);
                Q_UNUSED(ok);
            }
        } else {
            //ViewerInstancePtr isSelectedViewer = selectedNodeInternal->isEffectViewerInstance();
            //Don't pop a dot, it will most likely annoy the user, just fallback on behavior 0
            /*    position.setX( ( viewPos.bottomRight().x() + viewPos.topLeft().x() ) / 2. );
                position.setY( ( viewPos.topLeft().y() + viewPos.bottomRight().y() ) / 2. );
               }
               else {
             */
            QRectF selectedBbox = selected->mapToScene( selected->boundingRect() ).boundingRect();
            QPointF selectedCenter = selectedBbox.center();
            double y = selectedCenter.y() - selectedNodeSize.height() / 2.
                       - NodeGui::DEFAULT_OFFSET_BETWEEN_NODES - createdNodeSize.height();
            double x = selectedCenter.x() + nbConnectedInput * 150;

            position.setX(x  - createdNodeSize.width() / 2.);
            position.setY(y);

            int index = selectedNodeInternal->getPreferredInputForConnection();
            if (index != -1) {
                bool ok = proj->connectNodes(index, createdNodeInternal, selectedNodeInternal, true);
                Q_UNUSED(ok);
            }
            //} // if (isSelectedViewer) {*/
        } // if (nbConnectedInput == 0) {
    } else {
        ///pop it below the selected node

        const NodesWList& outputs = selectedNodeInternal->getGuiOutputs();
        if ( !createdNodeInternal->isOutputNode() || outputs.empty() ) {
            QSize selectedNodeSize = selected->getSize();
            QSize createdNodeSize = node->getSize();
            QPointF selectedNodeMiddlePos = selected->scenePos() +
                                            QPointF(selectedNodeSize.width() / 2, selectedNodeSize.height() / 2);

            ///actually move the created node where the selected node is
            position.setX(selectedNodeMiddlePos.x() - createdNodeSize.width() / 2);
            position.setY(selectedNodeMiddlePos.y() + (selectedNodeSize.height() / 2) + NodeGui::DEFAULT_OFFSET_BETWEEN_NODES);

            QRectF createdNodeRect( position.x(), position.y(), createdNodeSize.width(), createdNodeSize.height() );

            ///and move the selected node below recusively
            for (NodesWList::const_iterator it = outputs.begin(); it != outputs.end(); ++it) {
                NodePtr output = it->lock();
                if (!output) {
                    continue;
                }
                NodeGuiIPtr output_i = output->getNodeGui();
                if (!output_i) {
                    continue;
                }
                NodeGuiPtr outputGui = toNodeGui( output_i );
                assert(outputGui);
                if (outputGui) {
                    outputGui->moveBelowPositionRecursively(createdNodeRect);
                }
            }

            ///Connect the created node to the selected node
            ///finally we connect the created node to the selected node
            int createdInput = createdNodeInternal->getPreferredInputForConnection();
            if (createdInput != -1) {
                ignore_result( createdNodeInternal->connectInput(selectedNodeInternal, createdInput) );
            }

            if ( !createdNodeInternal->isOutputNode() ) {
                ///we find all the nodes that were previously connected to the selected node,
                ///and connect them to the created node instead.
                std::map<NodePtr, int> outputsConnectedToSelectedNode;
                selectedNodeInternal->getOutputsConnectedToThisNode(&outputsConnectedToSelectedNode);

                for (std::map<NodePtr, int>::iterator it = outputsConnectedToSelectedNode.begin();
                     it != outputsConnectedToSelectedNode.end(); ++it) {
                    if ( it->first->getParentMultiInstanceName().empty() && (it->first != createdNodeInternal) ) {
                        /*
                           Internal rotopaint nodes are connecting to the Rotopaint itself... make sure not to connect
                           internal nodes of the tree
                         */
                        RotoDrawableItemPtr stroke = it->first->getAttachedRotoItem();
                        if ( stroke && (stroke->getContext()->getNode() == selectedNodeInternal) ) {
                            continue;
                        }

                        ignore_result( it->first->replaceInput(createdNodeInternal, it->second) );
//                        bool ok = proj->disconnectNodes(selectedNodeInternal.get(), it->first);
//                        if (ok) {
//                            ignore_result(proj->connectNodes(it->second, createdNodeInternal, it->first));
//                        }
                        //assert(ok); Might not be ok if the disconnectNodes() action above was queued
                    }
                }
            }
        } else {
            ///the created node is an output node and the selected node already has several outputs, create it aside
            QSize createdNodeSize = node->getSize();
            QRectF selectedBbox = selected->mapToScene( selected->boundingRect() ).boundingRect();
            QPointF selectedCenter = selectedBbox.center();
            double y = selectedCenter.y() + selectedBbox.height() / 2.
                       + NodeGui::DEFAULT_OFFSET_BETWEEN_NODES;
            double x = selectedCenter.x() + (int)outputs.size() * 150;

            position.setX(x  - createdNodeSize.width() / 2.);
            position.setY(y);


            //Don't pop a dot, it will most likely annoy the user, just fallback on behavior 0

            int index = createdNodeInternal->getPreferredInputForConnection();
            bool ok = proj->connectNodes(index, selectedNodeInternal, createdNodeInternal, true);
            Q_UNUSED(ok);
        }
    }
    position = node->mapFromScene(position);
    position = node->mapToParent(position);
    node->setPosition( position.x(), position.y() );
} // moveNodesForIdealPosition
void
DSMoveKeysAndNodesCommand::moveSelection(double dt)
{
    /*
       The view is going to get MANY update() calls since every knob change will trigger a computeNodeRange() in DopeSheetView
       We explicitly disable update on the dopesheet view and re-enable it afterwards.
     */
    DopeSheetView* view = _model->getDopesheetView();

    view->setUpdatesEnabled(false);

    for (NodesWList::iterator khIt = _allDifferentNodes.begin(); khIt != _allDifferentNodes.end(); ++khIt) {
        NodePtr node = khIt->lock();
        if (!node) {
            continue;
        }
        node->getEffectInstance()->beginChanges();
    }


    //////////Handle selected keyframes
    for (DSKeyPtrList::iterator it = _keys.begin(); it != _keys.end(); ++it) {
        const DSKeyPtr& selectedKey = (*it);
        DSKnobPtr knobContext = selectedKey->context.lock();
        if (!knobContext) {
            continue;
        }

        KnobIPtr knob = knobContext->getKnobGui()->getKnob();

        knob->moveValueAtTime(eCurveChangeReasonDopeSheet, selectedKey->key.getTime(), ViewIdx(0),
                              knobContext->getDimension(),
                              dt, 0, &selectedKey->key);
    }
    ////////////Handle selected nodes
    for (std::vector<DSNodePtr >::iterator it = _nodes.begin(); it != _nodes.end(); ++it) {
        DopeSheetItemType type = (*it)->getItemType();
        if (type == eDopeSheetItemTypeReader) {
            moveReader( (*it)->getInternalNode(), dt );
        } else if (type == eDopeSheetItemTypeFrameRange) {
            moveFrameRange( (*it)->getInternalNode(), dt );
        } else if (type == eDopeSheetItemTypeTimeOffset) {
            moveTimeOffset( (*it)->getInternalNode(), dt );
        } else if (type == eDopeSheetItemTypeGroup) {
            moveGroupNode(_model, (*it)->getInternalNode(), dt);
        }
    }


    for (NodesWList::const_iterator khIt = _allDifferentNodes.begin(); khIt != _allDifferentNodes.end(); ++khIt) {
        NodePtr node = khIt->lock();
        if (!node) {
            continue;
        }
        node->getEffectInstance()->endChanges();
    }

    view->setUpdatesEnabled(true);


    _model->refreshSelectionBboxAndRedrawView();
} // DSMoveKeysAndNodesCommand::moveSelection
Beispiel #4
0
void
BaseTest::disconnectNodes(const NodePtr& input,
                          const NodePtr& output,
                          bool expectedReturnvalue)
{
    if (expectedReturnvalue) {
        ///check that the connections are internally all set as "expected"

        ///the input must have in its output the node 'output'
        EXPECT_TRUE( input->hasOutputConnected() );
        const NodesWList & outputs = input->getGuiOutputs();
        bool foundOutput = false;
        for (NodesWList::const_iterator it = outputs.begin(); it != outputs.end(); ++it) {
            if (it->lock() == output) {
                foundOutput = true;
                break;
            }
        }

        ///the output must have in its inputs the node 'input'
        const std::vector<NodeWPtr> & inputs = output->getGuiInputs();
        int inputIndex = 0;
        bool foundInput = false;
        for (U32 i = 0; i < inputs.size(); ++i) {
            if (inputs[i].lock() == input) {
                foundInput = true;
                break;
            }
            ++inputIndex;
        }

        EXPECT_TRUE(foundInput);
        EXPECT_TRUE(foundOutput);
        EXPECT_EQ(output->getInput(inputIndex), input);
        EXPECT_TRUE( output->isInputConnected(inputIndex) );
    }

    ///call disconnect
    bool ret = getApp()->getProject()->disconnectNodes(input, output);
    EXPECT_EQ(expectedReturnvalue, ret);

    if (expectedReturnvalue) {
        ///check that the disconnection went OK

        const NodesWList & outputs = input->getGuiOutputs();
        bool foundOutput = false;
        for (NodesWList::const_iterator it = outputs.begin(); it != outputs.end(); ++it) {
            if (it->lock() == output) {
                foundOutput = true;
                break;
            }
        }

        ///the output must have in its inputs the node 'input'
        const std::vector<NodeWPtr> & inputs = output->getGuiInputs();
        int inputIndex = 0;
        bool foundInput = false;
        for (U32 i = 0; i < inputs.size(); ++i) {
            if (inputs[i].lock() == input) {
                foundInput = true;
                break;
            }
            ++inputIndex;
        }

        EXPECT_FALSE(foundOutput);
        EXPECT_FALSE(foundInput);
        EXPECT_EQ( (Node*)NULL, output->getInput(inputIndex).get() );
        EXPECT_FALSE( output->isInputConnected(inputIndex) );
    }
} // disconnectNodes