void GraphicsItemEdge::calculateAndSetPath() { setControlPointLocations(); double edgeDistance = QLineF(m_startingLocation, m_endingLocation).length(); double extensionLength = g_settings->segmentLength; if (extensionLength > edgeDistance / 2.0) extensionLength = edgeDistance / 2.0; m_controlPoint1 = extendLine(m_beforeStartingLocation, m_startingLocation, extensionLength); m_controlPoint2 = extendLine(m_afterEndingLocation, m_endingLocation, extensionLength); //If this edge is connecting a node to itself, and that node //is made of only one line segment, then a special path is //required, otherwise the edge will be mostly hidden underneath //the node. DeBruijnNode * startingNode = m_deBruijnEdge->getStartingNode(); DeBruijnNode * endingNode = m_deBruijnEdge->getEndingNode(); if (startingNode == endingNode) { GraphicsItemNode * graphicsItemNode = startingNode->getGraphicsItemNode(); if (graphicsItemNode == 0) graphicsItemNode = startingNode->getReverseComplement()->getGraphicsItemNode(); if (graphicsItemNode != 0 && graphicsItemNode->m_linePoints.size() == 2) { makeSpecialPathConnectingNodeToSelf(); return; } } //If we are in single mode and the edge connects a node to its reverse //complement, then we need a special path to make it visible. if (startingNode == endingNode->getReverseComplement() && !g_settings->doubleMode) { makeSpecialPathConnectingNodeToReverseComplement(); return; } //Otherwise, the path is just a single cubic Bezier curve. QPainterPath path; path.moveTo(m_startingLocation); path.cubicTo(m_controlPoint1, m_controlPoint2, m_endingLocation); setPath(path); }
//This function recursively labels all nodes as drawn that are within a //certain distance of this node. Whichever node called this will //definitely be drawn, so that one is excluded from the recursive call. void DeBruijnNode::labelNeighbouringNodesAsDrawn(int nodeDistance, DeBruijnNode * callingNode) { if (m_highestDistanceInNeighbourSearch > nodeDistance) return; m_highestDistanceInNeighbourSearch = nodeDistance; if (nodeDistance == 0) return; DeBruijnNode * otherNode; for (size_t i = 0; i < m_edges.size(); ++i) { otherNode = m_edges[i]->getOtherNode(this); if (otherNode == callingNode) continue; if (g_settings->doubleMode) otherNode->m_drawn = true; else //single mode { if (otherNode->isPositiveNode()) otherNode->m_drawn = true; else otherNode->getReverseComplement()->m_drawn = true; } otherNode->labelNeighbouringNodesAsDrawn(nodeDistance-1, this); } }
void GraphicsItemEdge::setControlPointLocations() { DeBruijnNode * startingNode = m_deBruijnEdge->getStartingNode(); DeBruijnNode * endingNode = m_deBruijnEdge->getEndingNode(); if (startingNode->hasGraphicsItem()) { m_startingLocation = startingNode->getGraphicsItemNode()->getLast(); m_beforeStartingLocation = startingNode->getGraphicsItemNode()->getSecondLast(); } else if (startingNode->getReverseComplement()->hasGraphicsItem()) { m_startingLocation = startingNode->getReverseComplement()->getGraphicsItemNode()->getFirst(); m_beforeStartingLocation = startingNode->getReverseComplement()->getGraphicsItemNode()->getSecond(); } if (endingNode->hasGraphicsItem()) { m_endingLocation = endingNode->getGraphicsItemNode()->getFirst(); m_afterEndingLocation = endingNode->getGraphicsItemNode()->getSecond(); } else if (endingNode->getReverseComplement()->hasGraphicsItem()) { m_endingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getLast(); m_afterEndingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getSecondLast(); } }
//The startingNodes and nodeDistance parameters are only used if the graph scope //is not WHOLE_GRAPH. void AssemblyGraph::buildOgdfGraphFromNodesAndEdges(std::vector<DeBruijnNode *> startingNodes, int nodeDistance) { if (g_settings->graphScope == WHOLE_GRAPH) { QMapIterator<QString, DeBruijnNode*> i(m_deBruijnGraphNodes); while (i.hasNext()) { i.next(); //If double mode is off, only positive nodes are drawn. If it's //on, all nodes are drawn. if (i.value()->isPositiveNode() || g_settings->doubleMode) i.value()->setAsDrawn(); } } else //The scope is either around specified nodes or around nodes with BLAST hits { for (size_t i = 0; i < startingNodes.size(); ++i) { DeBruijnNode * node = startingNodes[i]; //If we are in single mode, make sure that each node is positive. if (!g_settings->doubleMode && node->isNegativeNode()) node = node->getReverseComplement(); node->setAsDrawn(); node->setAsSpecial(); node->labelNeighbouringNodesAsDrawn(nodeDistance, 0); } } //First loop through each node, adding it to OGDF if it is drawn. QMapIterator<QString, DeBruijnNode*> i(m_deBruijnGraphNodes); while (i.hasNext()) { i.next(); if (i.value()->isDrawn()) i.value()->addToOgdfGraph(m_ogdfGraph); } //Then loop through each determining its drawn status and adding it //to OGDF if it is drawn. for (size_t i = 0; i < m_deBruijnGraphEdges.size(); ++i) { m_deBruijnGraphEdges[i]->determineIfDrawn(); if (m_deBruijnGraphEdges[i]->isDrawn()) m_deBruijnGraphEdges[i]->addToOgdfGraph(m_ogdfGraph); } }
//This function differs from the above by including all reverse complement //nodes in the path search. std::vector<DeBruijnNode *> DeBruijnNode::getNodesCommonToAllPaths(std::vector< std::vector <DeBruijnNode *> > * paths, bool includeReverseComplements) const { std::vector<DeBruijnNode *> commonNodes; //If there are no paths, then return the empty vector. if (paths->size() == 0) return commonNodes; //If there is only one path in path, then they are all common nodes commonNodes = (*paths)[0]; if (paths->size() == 1) return commonNodes; //If there are two or more paths, it's necessary to find the intersection. for (size_t i = 1; i < paths->size(); ++i) { QApplication::processEvents(); std::vector <DeBruijnNode *> * path = &((*paths)[i]); //If we are including reverse complements in the search, //then it is necessary to build a new vector that includes //reverse complement nodes and then use that vector. std::vector <DeBruijnNode *> pathWithReverseComplements; if (includeReverseComplements) { for (size_t j = 0; j < path->size(); ++j) { DeBruijnNode * node = (*path)[j]; pathWithReverseComplements.push_back(node); pathWithReverseComplements.push_back(node->getReverseComplement()); } path = &pathWithReverseComplements; } //Combine the commonNodes vector with the path vector, //excluding any repeats. std::sort(commonNodes.begin(), commonNodes.end()); std::sort(path->begin(), path->end()); std::vector<DeBruijnNode *> newCommonNodes; std::set_intersection(commonNodes.begin(), commonNodes.end(), path->begin(), path->end(), std::back_inserter(newCommonNodes)); commonNodes = newCommonNodes; } return commonNodes; }
//This function determines the contiguity of nodes relative to this one. //It has two steps: // -First, for each edge leaving this node, all paths outward are found. // Any nodes in any path are MAYBE_CONTIGUOUS, and nodes in all of the // paths are CONTIGUOUS. // -Second, it is necessary to check in the opposite direction - for each // of the MAYBE_CONTIGUOUS nodes, do they have a path that unambiguously // leads to this node? If so, then they are CONTIGUOUS. void DeBruijnNode::determineContiguity() { upgradeContiguityStatus(STARTING); //A set is used to store all nodes found in the paths, as the nodes //that show up as MAYBE_CONTIGUOUS will have their paths checked //to this node. std::set<DeBruijnNode *> allCheckedNodes; //For each path leaving this node, find all possible paths //outward. Nodes in any of the paths for an edge are //MAYBE_CONTIGUOUS. Nodes in all of the paths for an edge //are CONTIGUOUS. for (size_t i = 0; i < m_edges.size(); ++i) { DeBruijnEdge * edge = m_edges[i]; bool outgoingEdge = (this == edge->getStartingNode()); std::vector< std::vector <DeBruijnNode *> > allPaths; edge->tracePaths(outgoingEdge, g_settings->contiguitySearchSteps, &allPaths, this); //Set all nodes in the paths as MAYBE_CONTIGUOUS for (size_t j = 0; j < allPaths.size(); ++j) { QApplication::processEvents(); for (size_t k = 0; k < allPaths[j].size(); ++k) { DeBruijnNode * node = allPaths[j][k]; node->upgradeContiguityStatus(MAYBE_CONTIGUOUS); allCheckedNodes.insert(node); } } //Set all common nodes as CONTIGUOUS_STRAND_SPECIFIC std::vector<DeBruijnNode *> commonNodesStrandSpecific = getNodesCommonToAllPaths(&allPaths, false); for (size_t j = 0; j < commonNodesStrandSpecific.size(); ++j) (commonNodesStrandSpecific[j])->upgradeContiguityStatus(CONTIGUOUS_STRAND_SPECIFIC); //Set all common nodes (when including reverse complement nodes) //as CONTIGUOUS_EITHER_STRAND std::vector<DeBruijnNode *> commonNodesEitherStrand = getNodesCommonToAllPaths(&allPaths, true); for (size_t j = 0; j < commonNodesEitherStrand.size(); ++j) { DeBruijnNode * node = commonNodesEitherStrand[j]; node->upgradeContiguityStatus(CONTIGUOUS_EITHER_STRAND); node->getReverseComplement()->upgradeContiguityStatus(CONTIGUOUS_EITHER_STRAND); } } //For each node that was checked, then we check to see if any //of its paths leads unambiuously back to the starting node (this node). for (std::set<DeBruijnNode *>::iterator i = allCheckedNodes.begin(); i != allCheckedNodes.end(); ++i) { QApplication::processEvents(); DeBruijnNode * node = *i; ContiguityStatus status = node->getContiguityStatus(); //First check without reverse complement target for //strand-specific contiguity. if (status != CONTIGUOUS_STRAND_SPECIFIC && node->doesPathLeadOnlyToNode(this, false)) node->upgradeContiguityStatus(CONTIGUOUS_STRAND_SPECIFIC); //Now check including the reverse complement target for //either strand contiguity. if (status != CONTIGUOUS_STRAND_SPECIFIC && status != CONTIGUOUS_EITHER_STRAND && node->doesPathLeadOnlyToNode(this, true)) { node->upgradeContiguityStatus(CONTIGUOUS_EITHER_STRAND); node->getReverseComplement()->upgradeContiguityStatus(CONTIGUOUS_EITHER_STRAND); } } }