Skip to content
Snippets Groups Projects
GraphView.cpp 43.3 KiB
Newer Older
  for (IOIndex_t outputIdx = 0; outputIdx < deletedNode->getOrderedChildren().size(); ++outputIdx) {
Olivier BICHLER's avatar
Olivier BICHLER committed
    const auto val = std::make_pair(deletedNode, outputIdx);
    const auto iter = std::find(mOutputNodes.cbegin(), mOutputNodes.cend(), val);
Olivier BICHLER's avatar
Olivier BICHLER committed

    if (iter != mOutputNodes.cend()) {
      // The first old (removed) output becomes the insertion point for newNode GraphView outputs
      if (std::distance(newOutputsInsertionPoint, iter) <= 0) {
        newOutputsInsertionPoint = mOutputNodes.erase(iter);
      }
      else {
        mOutputNodes.erase(iter);
      }
Olivier BICHLER's avatar
Olivier BICHLER committed
    }
  }

  // Add parent node outputs that become GraphView output following the removal of the node
  // Outputs addition order follows deletedNode inputs order
  for (const std::shared_ptr<Node>& parent : deletedNode->getParents()) {
    if (mNodes.find(parent) != mNodes.end()) {
      IOIndex_t outputIdx = 0;
      for (auto orderedChilds : parent->getOrderedChildren()) {
        bool noInsideConnection = true;
        for (auto ch_ptr : orderedChilds) {
          if (mNodes.find(ch_ptr) != mNodes.end()) {
            noInsideConnection = false;
            break;
          }
        if (noInsideConnection) {
          const auto val = std::make_pair(parent, outputIdx);
          if (std::find(mOutputNodes.cbegin(), mOutputNodes.cend(), val) == mOutputNodes.cend()) {
            newOutputsInsertionPoint = mOutputNodes.insert(newOutputsInsertionPoint, val);
            newOutputsInsertionPoint = std::next(newOutputsInsertionPoint);
          }
        }
        ++outputIdx;
Cyril Moineau's avatar
Cyril Moineau committed

Olivier BICHLER's avatar
Olivier BICHLER committed
std::shared_ptr<Aidge::GraphView> Aidge::GraphView::cloneCallback(NodePtr(*cloneNode)(NodePtr)) const {
  std::shared_ptr<GraphView> newGraph = std::make_shared<GraphView>(mName);

  // Map for old node -> new node correspondance
  std::map<NodePtr, NodePtr> oldToNewNodes;

  for (const std::shared_ptr<Node> &node_ptr : mNodes) {
    auto clonedNode = cloneNode(node_ptr);
    if (clonedNode == nullptr) {
      AIDGE_ASSERT(node_ptr->getChildren().size() <= 1, "deleted nodes in GraphView::clone() cannot have multiple children");
Olivier BICHLER's avatar
Olivier BICHLER committed
      AIDGE_ASSERT(node_ptr->nbData() <= 1, "deleted nodes in GraphView::clone() cannot have multiple data input parents");
    }
    oldToNewNodes[node_ptr] = clonedNode;
  // For each node, convert old node -> new node connections
  for (auto &oldToNewNode : oldToNewNodes) {
    if (oldToNewNode.second == nullptr) {
      continue;  // deleted node

    // Connect parent nodes. Nodes that were removed with cloneNode() are set to nullptr
    size_t parentId = 0;
    for (auto parent : oldToNewNode.first->inputs()) {
      if (parent.first != nullptr) {
        while (oldToNewNodes[parent.first] == nullptr) {
          // Find next valid parent in line, going backward in the graph
          AIDGE_INTERNAL_ASSERT(parent.first->getChildren().size() == 1);
Olivier BICHLER's avatar
Olivier BICHLER committed
          AIDGE_INTERNAL_ASSERT(parent.first->nbData() <= 1);
          const auto& parents = parent.first->dataInputs();

          if (!parents.empty() && parents[0].first != nullptr // a valid parent exists
            && oldToNewNodes.find(parents[0].first) != oldToNewNodes.end()) // parent is in the GraphView
          {
            parent = parents[0];
          }
          else {
            break;
          }
        if (oldToNewNodes[parent.first]) {
          AIDGE_INTERNAL_ASSERT(oldToNewNodes[parent.first]->nbOutputs() == parent.first->nbOutputs());
          oldToNewNodes[parent.first]->addChild(oldToNewNode.second, parent.second, parentId);
        }
      ++parentId;
Olivier BICHLER's avatar
Olivier BICHLER committed
  // Once connected, add each new nodes to new GraphView
  // This has to be done in a second step to ensure that new GraphView inputs/outputs
  // are properly set (otherwise, some node's inputs/outputs may be wrongly registered as
  // GraphView inputs/outputs because not yet connected to other nodes)
  if (oldToNewNodes[mRootNode] != nullptr) {
    // Add root node first if is still exists!
    newGraph->add(oldToNewNodes[mRootNode], false);
  }

Olivier BICHLER's avatar
Olivier BICHLER committed
  for (auto &oldToNewNode : oldToNewNodes) {
    if (oldToNewNode.second == nullptr)
      continue;  // deleted node

    newGraph->add(oldToNewNode.second, false);
  }

  // Update cloned graph inputs/outputs order to match initial graph order
  auto newInputNodes = mInputNodes;
Olivier BICHLER's avatar
Olivier BICHLER committed
  for (auto it = newInputNodes.begin(); it != newInputNodes.end(); ) {
Olivier BICHLER's avatar
Olivier BICHLER committed
    // If input node was removed, find next valid input
    while (oldToNewNodes[it->first] == nullptr) {
      // Removed node should have only one connected output, otherwise cloning is invalid
      AIDGE_INTERNAL_ASSERT(it->first->getChildren().size() <= 1);
Olivier BICHLER's avatar
Olivier BICHLER committed
      bool found = false;

      if (it->first->getChildren().size() == 1) {
        auto child = *it->first->getChildren().begin();

        std::size_t inputIdx = 0;
        for (auto parent : child->getParents()) {
          if (parent == it->first) {
            it->first = child;
            it->second = inputIdx;
            found = true;
            break;
          }
          ++inputIdx;
Olivier BICHLER's avatar
Olivier BICHLER committed

    if (oldToNewNodes[it->first] == nullptr) {
      it = newInputNodes.erase(it);
    }
    else {
      it->first = oldToNewNodes[it->first];
Olivier BICHLER's avatar
Olivier BICHLER committed
      ++it;
Olivier BICHLER's avatar
Olivier BICHLER committed
    }
Olivier BICHLER's avatar
Olivier BICHLER committed
  }
  newGraph->setOrderedInputs(newInputNodes);

  auto newOutputNodes = mOutputNodes;
Olivier BICHLER's avatar
Olivier BICHLER committed
  for (auto it = newOutputNodes.begin(); it != newOutputNodes.end(); ) {
Olivier BICHLER's avatar
Olivier BICHLER committed
    // If output node was removed, find previous valid output
    while (oldToNewNodes[it->first] == nullptr) {
      // Removed node should have only one connected data input, otherwise cloning is invalid
Olivier BICHLER's avatar
Olivier BICHLER committed
      AIDGE_INTERNAL_ASSERT(it->first->nbData() <= 1);
Olivier BICHLER's avatar
Olivier BICHLER committed
      auto parents = it->first->dataInputs();

      if (!parents.empty() && parents[0].first != nullptr // a valid parent exists
        && oldToNewNodes.find(parents[0].first) != oldToNewNodes.end()) // parent is in the GraphView
      {
Olivier BICHLER's avatar
Olivier BICHLER committed
        *it = parents[0];
      }
      else {
        break;
      }
    }
Olivier BICHLER's avatar
Olivier BICHLER committed

    if (oldToNewNodes[it->first] == nullptr) {
      it = newOutputNodes.erase(it);
    }
    else {
      it->first = oldToNewNodes[it->first];
Olivier BICHLER's avatar
Olivier BICHLER committed
      ++it;
Olivier BICHLER's avatar
Olivier BICHLER committed
    }
Olivier BICHLER's avatar
Olivier BICHLER committed
  }
  newGraph->setOrderedOutputs(newOutputNodes);