From 85eba59f4ce2c831e7fec7061f11f34354224dd0 Mon Sep 17 00:00:00 2001 From: Shostina Date: Sun, 3 Oct 2021 10:13:20 +0300 Subject: [PATCH 01/11] add HiC edges --- HiCSettings.cpp | 43 ++++++ HiCSettings.h | 35 +++++ graph/assemblygraph.cpp | 292 ++++++++++++++++++++++++++++++++----- graph/assemblygraph.h | 12 +- graph/debruijnedge.cpp | 31 +++- graph/debruijnedge.h | 8 + graph/graphicsitemedge.cpp | 63 ++++++-- graph/graphicsitemnode.h | 18 +++ graph/ogdfnode.h | 9 ++ 9 files changed, 467 insertions(+), 44 deletions(-) create mode 100644 HiCSettings.cpp create mode 100644 HiCSettings.h diff --git a/HiCSettings.cpp b/HiCSettings.cpp new file mode 100644 index 00000000..d63df263 --- /dev/null +++ b/HiCSettings.cpp @@ -0,0 +1,43 @@ +#include "HiCSettings.h" +#include "../graph/debruijnnode.h" +#include "../graph/debruijnedge.h" + +HiCSettings::HiCSettings():numOfNodes(0), minWeight(0), minLength(1), minDist(1) {} +HiCSettings::~HiCSettings() {} + +bool HiCSettings::isDrawn(DeBruijnEdge* edge) { + /*bool drawEdge = (edge->getStartingNode()->isDrawn() || edge->getStartingNode()->getReverseComplement()->isDrawn()) + && (edge->getEndingNode()->isDrawn() || edge->getEndingNode()->getReverseComplement()->isDrawn());*/ + edge->determineIfDrawn(); + bool drawEdge = edge->isDrawn(); + if (!drawEdge) + return false; + return (getEdgeWeight(edge) >= minWeight && + edge -> getStartingNode() -> getLength() >= minLength && + edge->getEndingNode()->getLength() >= minLength); +} + +int HiCSettings::getEdgeWeight(DeBruijnEdge* edge) { + QString startingNodeName = edge->getStartingNode()->getName(); + QString endingNodeName = edge->getEndingNode()->getName(); + int startingNodeInd = -1; + int endingNodeInd = -1; + for (int i = 0; i < numOfNodes; i++) { + if (kontigNames.at(i) == startingNodeName) { + startingNodeInd = i; + } + if (kontigNames.at(i) == endingNodeName) { + endingNodeInd = i; + } + } + QFile logFile("C\:\\Users\\anastasia\\study\\maga\\Bandage\\my_test_data\\hic_1_1.log"); + logFile.open(QIODevice::WriteOnly); + logFile.write("getEdgeWeight: " + startingNodeName.toUtf8() + " " + endingNodeName.toUtf8() + "\n"); + logFile.write("getEdgeWeight: " + startingNodeInd + ' ' + endingNodeInd + '\n'); + logFile.close(); + if (startingNodeInd != -1 && endingNodeInd != -1) { + return weightMatrix.at(startingNodeInd).at(endingNodeInd); + } + return 0; +} + diff --git a/HiCSettings.h b/HiCSettings.h new file mode 100644 index 00000000..90f7f9ce --- /dev/null +++ b/HiCSettings.h @@ -0,0 +1,35 @@ +#ifndef HICSETTINGS_H +#define HICSETTINGS_H + +#include +#include + +#include "../ogdf/basic/Graph.h" +#include "../ogdf/basic/GraphAttributes.h" +#include +#include +#include "../program/globals.h" +#include "../ui/mygraphicsscene.h" +class HiCSettings : public QObject +{ + Q_OBJECT + +public: + HiCSettings(); + ~HiCSettings(); + + int numOfNodes = 0; + int minWeight = 0; + int minLength = 1; + int minDist = 1; + QVector kontigNames; + QVector > weightMatrix; + QVector > distanceMatrix; + bool isDrawn(DeBruijnEdge* edge); +private: + int getEdgeWeight(DeBruijnEdge* edge); + + +}; +#endif //HICSETTINGS_H + diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index a839d528..f92b953e 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -17,6 +17,7 @@ #include "assemblygraph.h" +#include "HiCSettings.h" #include #include "../program/globals.h" #include "../program/settings.h" @@ -44,6 +45,7 @@ #include #include "ogdfnode.h" #include "../command_line/commoncommandlinefunctions.h" +#include AssemblyGraph::AssemblyGraph() : m_kmer(0), m_contiguitySearchDone(false), @@ -51,6 +53,7 @@ AssemblyGraph::AssemblyGraph() : { m_ogdfGraph = new ogdf::Graph(); m_edgeArray = new ogdf::EdgeArray(*m_ogdfGraph); + m_hiCEdgeArray = new ogdf::EdgeArray(*m_ogdfGraph); m_graphAttributes = new ogdf::GraphAttributes(*m_ogdfGraph, ogdf::GraphAttributes::nodeGraphics | ogdf::GraphAttributes::edgeGraphics); clearGraphInfo(); @@ -60,6 +63,7 @@ AssemblyGraph::~AssemblyGraph() { delete m_graphAttributes; delete m_edgeArray; + delete m_hiCEdgeArray; delete m_ogdfGraph; } @@ -82,42 +86,46 @@ void AssemblyGraph::cleanUp() } m_deBruijnGraphEdges.clear(); + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + delete j_hiC.value(); + } + m_hiCDeBruijnGraphEdges.clear(); + m_contiguitySearchDone = false; clearGraphInfo(); } - - - - //This function makes a double edge: in one direction for the given nodes //and the opposite direction for their reverse complements. It adds the //new edges to the vector here and to the nodes themselves. void AssemblyGraph::createDeBruijnEdge(QString node1Name, QString node2Name, - int overlap, EdgeOverlapType overlapType) + int overlap, EdgeOverlapType overlapType, bool hiC, int weight) { QString node1Opposite = getOppositeNodeName(node1Name); QString node2Opposite = getOppositeNodeName(node2Name); //Quit if any of the nodes don't exist. if (!m_deBruijnGraphNodes.contains(node1Name) || - !m_deBruijnGraphNodes.contains(node2Name) || - !m_deBruijnGraphNodes.contains(node1Opposite) || - !m_deBruijnGraphNodes.contains(node2Opposite)) + !m_deBruijnGraphNodes.contains(node2Name) || + !m_deBruijnGraphNodes.contains(node1Opposite) || + !m_deBruijnGraphNodes.contains(node2Opposite)) return; - DeBruijnNode * node1 = m_deBruijnGraphNodes[node1Name]; - DeBruijnNode * node2 = m_deBruijnGraphNodes[node2Name]; - DeBruijnNode * negNode1 = m_deBruijnGraphNodes[node1Opposite]; - DeBruijnNode * negNode2 = m_deBruijnGraphNodes[node2Opposite]; + DeBruijnNode* node1 = m_deBruijnGraphNodes[node1Name]; + DeBruijnNode* node2 = m_deBruijnGraphNodes[node2Name]; + DeBruijnNode* negNode1 = m_deBruijnGraphNodes[node1Opposite]; + DeBruijnNode* negNode2 = m_deBruijnGraphNodes[node2Opposite]; //Quit if the edge already exists - const std::vector * edges = node1->getEdgesPointer(); + const std::vector* edges = node1->getEdgesPointer(); for (size_t i = 0; i < edges->size(); ++i) { if ((*edges)[i]->getStartingNode() == node1 && - (*edges)[i]->getEndingNode() == node2) + (*edges)[i]->getEndingNode() == node2) return; } @@ -125,8 +133,8 @@ void AssemblyGraph::createDeBruijnEdge(QString node1Name, QString node2Name, //for an edge to be its own pair. bool isOwnPair = (node1 == negNode2 && node2 == negNode1); - DeBruijnEdge * forwardEdge = new DeBruijnEdge(node1, node2); - DeBruijnEdge * backwardEdge; + DeBruijnEdge* forwardEdge = new DeBruijnEdge(node1, node2); + DeBruijnEdge* backwardEdge; if (isOwnPair) backwardEdge = forwardEdge; @@ -141,10 +149,18 @@ void AssemblyGraph::createDeBruijnEdge(QString node1Name, QString node2Name, forwardEdge->setOverlapType(overlapType); backwardEdge->setOverlapType(overlapType); - m_deBruijnGraphEdges.insert(QPair(forwardEdge->getStartingNode(), forwardEdge->getEndingNode()), forwardEdge); - if (!isOwnPair) - m_deBruijnGraphEdges.insert(QPair(backwardEdge->getStartingNode(), backwardEdge->getEndingNode()), backwardEdge); - + forwardEdge->setHiC(hiC, weight); + backwardEdge->setHiC(hiC, weight); + if (!hiC) { + m_deBruijnGraphEdges.insert(QPair(forwardEdge->getStartingNode(), forwardEdge->getEndingNode()), forwardEdge); + if (!isOwnPair) + m_deBruijnGraphEdges.insert(QPair(backwardEdge->getStartingNode(), backwardEdge->getEndingNode()), backwardEdge); + } + else { + m_hiCDeBruijnGraphEdges.insert(QPair(forwardEdge->getStartingNode(), forwardEdge->getEndingNode()), forwardEdge); + if (!isOwnPair) + m_hiCDeBruijnGraphEdges.insert(QPair(backwardEdge->getStartingNode(), backwardEdge->getEndingNode()), backwardEdge); + } node1->addEdge(forwardEdge); node2->addEdge(forwardEdge); negNode1->addEdge(backwardEdge); @@ -165,6 +181,7 @@ void AssemblyGraph::clearOgdfGraphAndResetNodes() m_ogdfGraph->clear(); m_edgeArray->init(*m_ogdfGraph); + m_hiCEdgeArray->init(*m_ogdfGraph); } @@ -224,8 +241,6 @@ QByteArray AssemblyGraph::getReverseComplement(QByteArray forwardSequence) } - - void AssemblyGraph::resetEdges() { QMapIterator, DeBruijnEdge*> i(m_deBruijnGraphEdges); @@ -234,6 +249,13 @@ void AssemblyGraph::resetEdges() i.next(); i.value()->reset(); } + + QMapIterator, DeBruijnEdge*> j(m_hiCDeBruijnGraphEdges); + while (j.hasNext()) + { + j.next(); + j.value()->reset(); + } } @@ -456,10 +478,6 @@ void AssemblyGraph::clearGraphInfo() } - - - - void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) { m_graphFileType = LAST_GRAPH; @@ -534,6 +552,8 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) } inputFile.close(); + loadHiC(); + setAllEdgesExactOverlap(0); } @@ -541,6 +561,112 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) throw "load error"; } +void AssemblyGraph::loadHiC() { + QFile logFile("C\:\\Users\\anastasia\\study\\maga\\Bandage\\my_test_data\\hic_1_1.log"); + logFile.open(QIODevice::WriteOnly); + QFile settingsFile("C\:\\Users\\anastasia\\study\\maga\\Bandage\\my_test_data\\settings_1.txt"); + settingsFile.open(QIODevice::ReadOnly); + QTextStream inSettings(&settingsFile); + QString filePath = inSettings.readLine(); + while (!inSettings.atEnd()) { + QStringList settings = inSettings.readLine().split(QRegExp(" ")); + if (settings.length() < 2) { + continue; + } + if (settings.at(0) == "weight") { + hiC.minWeight = settings.at(1).toInt(); + } + if (settings.at(0) == "length") { + hiC.minLength = settings.at(1).toInt(); + } + if (settings.at(0) == "dist") { + hiC.minDist = settings.at(1).toInt(); + } + + } + + QFile hiCMatrix("C\:\\Users\\anastasia\\study\\maga\\Bandage\\my_test_data\\hic_1_2.txt"); + if (hiCMatrix.open(QIODevice::ReadOnly)) + { + QTextStream in(&hiCMatrix); + QApplication::processEvents(); + QString line1 = in.readLine(); + hiC.numOfNodes = line1.toInt(); + logFile.write("Kontigs: \n"); + logFile.write(hiC.numOfNodes + "\n"); + for (int i = 0; i < hiC.numOfNodes; i++) { + hiC.kontigNames.push_back(convertNormalNumberStringToBandageNodeName(in.readLine())); + logFile.write(hiC.kontigNames.at(hiC.kontigNames.length() - 1).toUtf8() + "\n"); + } + logFile.write("Edges: \n"); + for (int i = 0; i < hiC.numOfNodes; i++) { + QStringList weights = in.readLine().split(QRegExp("\t")); + QVector tempWeights; + if (weights.length() == hiC.numOfNodes) { + for (int j = 0; j <= i; j++) { + int weight = weights.at(j).toInt(); + tempWeights.push_back(weight); + QString firstNodeName = hiC.kontigNames.at(i); + QString secondNodeName = hiC.kontigNames.at(j); + if (weight > 0 && firstNodeName != secondNodeName) { + createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true, weight); + logFile.write(firstNodeName.toUtf8() + " " + secondNodeName.toUtf8() + "\n"); + } + } + hiC.weightMatrix.push_back(tempWeights); + } + } + logFile.close(); + } +} + +//first List of nodes has more nodes than second List of nodes +/*void AssemblyGraph::addHiCEdges(QList biggestNodesList, QList smallestNodesList) { + if (biggestNodesList.length() >= smallestNodesList.length()) { + int bigSize = biggestNodesList.length(); + int smallSizeHalf = smallestNodesList.length() / 2; + int middlePart = biggestNodesList.length() - (smallestNodesList.length() / 2) * 2; + + + QList::iterator startBigListIter = biggestNodesList.begin(); + QList::iterator endBigListIter = biggestNodesList.end(); + endBigListIter--; + QList::iterator startSmallListIter = smallestNodesList.begin(); + QList::iterator endSmallListIter = smallestNodesList.end(); + endSmallListIter--; + for (int i = 0; i < smallSizeHalf; i++) { + QString firstNodeName = (*startBigListIter)->getName(); + QString secondNodeName = (*startSmallListIter)->getName(); + if (firstNodeName != secondNodeName) { + createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true); + createDeBruijnEdge(secondNodeName, firstNodeName, 0, UNKNOWN_OVERLAP, true); + } + + firstNodeName = (*endBigListIter)->getName(); + secondNodeName = (*endSmallListIter)->getName(); + if (firstNodeName != secondNodeName) { + createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true); + createDeBruijnEdge(secondNodeName, firstNodeName, 0, UNKNOWN_OVERLAP, true); + } + startBigListIter++; + startSmallListIter++; + endBigListIter--; + endSmallListIter--; + + } + + QString middleNodeName = smallestNodesList.at(smallestNodesList.length() / 2) -> getName(); + for (int i = 0; i < middlePart; i++) { + QString curNodeName = (*startBigListIter)->getName(); + if (middleNodeName != curNodeName) { + createDeBruijnEdge(middleNodeName, curNodeName, 0, UNKNOWN_OVERLAP, true); + createDeBruijnEdge(curNodeName, middleNodeName, 0, UNKNOWN_OVERLAP, true); + } + startBigListIter++; + } + } +} +*/ //This function takes a normal number string like "5" or "-6" and changes //it to "5+" or "6-" - the format of Bandage node names. @@ -866,6 +992,7 @@ void AssemblyGraph::buildDeBruijnGraphFromGfa(QString fullFileName, bool *unsupp } m_sequencesLoadedFromFasta = NOT_TRIED; + loadHiC(); } @@ -1103,8 +1230,6 @@ void AssemblyGraph::pointEachNodeToItsReverseComplement() } - - void AssemblyGraph::buildDeBruijnGraphFromTrinityFasta(QString fullFileName) { m_graphFileType = TRINITY; @@ -1913,6 +2038,16 @@ void AssemblyGraph::buildOgdfGraphFromNodesAndEdges(std::vector if (edge->isDrawn()) edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); } + + //Then loop through each edge determining its drawn status and adding it to OGDF if it is drawn. + + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + edge->setDrawn(hiC.isDrawn(edge)); + } } @@ -1962,6 +2097,26 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) } } + //std::ofstream vmdelet_out; + //vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); + //vmdelet_out << "before add hiC into GraphicsItemsToScene. m_hiCDeBruijnGraphEdges len = " << m_hiCDeBruijnGraphEdges.size() << '\n'; + //vmdelet_out.close(); + + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + + if (edge->isDrawn()) + { + GraphicsItemEdge* graphicsItemEdge = new GraphicsItemEdge(edge); + edge->setGraphicsItemEdge(graphicsItemEdge); + graphicsItemEdge->setFlag(QGraphicsItem::ItemIsSelectable); + scene->addItem(graphicsItemEdge); + } + } + //Now add the GraphicsItemNode objects to the scene so they are drawn //on top QMapIterator k(m_deBruijnGraphNodes); @@ -2261,13 +2416,20 @@ void AssemblyGraph::setAllEdgesExactOverlap(int overlap) i.next(); i.value()->setExactOverlap(overlap); } + + QMapIterator, DeBruijnEdge*> j(m_hiCDeBruijnGraphEdges); + while (j.hasNext()) + { + j.next(); + j.value()->setExactOverlap(overlap); + } } void AssemblyGraph::autoDetermineAllEdgesExactOverlap() { - int edgeCount = int(m_deBruijnGraphEdges.size()); + int edgeCount = int(m_deBruijnGraphEdges.size()) + int(m_hiCDeBruijnGraphEdges.size()); if (edgeCount == 0) return; @@ -2279,6 +2441,13 @@ void AssemblyGraph::autoDetermineAllEdgesExactOverlap() i.value()->autoDetermineExactOverlap(); } + QMapIterator, DeBruijnEdge*> i_hiC(m_hiCDeBruijnGraphEdges); + while (i_hiC.hasNext()) + { + i_hiC.next(); + i_hiC.value()->autoDetermineExactOverlap(); + } + //The expectation here is that most overlaps will be //the same or from a small subset of possible sizes. //Edges with an overlap that do not match the most common @@ -2334,6 +2503,23 @@ void AssemblyGraph::autoDetermineAllEdgesExactOverlap() } } } + + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + for (size_t k = 0; k < sortedOverlaps.size(); ++k) + { + if (edge->getOverlap() == sortedOverlaps[k]) + break; + else if (edge->testExactOverlap(sortedOverlaps[k])) + { + edge->setOverlap(sortedOverlaps[k]); + break; + } + } + } } @@ -2357,6 +2543,18 @@ std::vector AssemblyGraph::makeOverlapCountVector() ++overlapCounts[overlap]; } + QMapIterator, DeBruijnEdge*> i_hiC(m_hiCDeBruijnGraphEdges); + while (i_hiC.hasNext()) + { + i_hiC.next(); + int overlap = i_hiC.value()->getOverlap(); + + //Add the overlap to the count vector + if (int(overlapCounts.size()) < overlap + 1) + overlapCounts.resize(overlap + 1, 0); + ++overlapCounts[overlap]; + } + return overlapCounts; } @@ -2561,8 +2759,8 @@ void AssemblyGraph::deleteEdges(std::vector * edges) DeBruijnEdge * edge = edgesToDelete[i]; DeBruijnNode * startingNode = edge->getStartingNode(); DeBruijnNode * endingNode = edge->getEndingNode(); - m_deBruijnGraphEdges.remove(QPair(startingNode, endingNode)); + //m_hiCDeBruijnGraphEdges.remove(QPair(startingNode, endingNode)); startingNode->removeEdge(edge); endingNode->removeEdge(edge); @@ -3238,6 +3436,15 @@ bool AssemblyGraph::saveEntireGraphToGfa(QString filename) edgesToSave.push_back(edge); } + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + if (edge->isPositiveEdge()) + edgesToSave.push_back(edge); + } + std::sort(edgesToSave.begin(), edgesToSave.end(), DeBruijnEdge::compareEdgePointers); for (int i = 0; i < edgesToSave.size(); ++i) @@ -3276,6 +3483,17 @@ bool AssemblyGraph::saveVisibleGraphToGfa(QString filename) edgesToSave.push_back(edge); } + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + if (edge->getStartingNode()->thisNodeOrReverseComplementIsDrawn() && + edge->getEndingNode()->thisNodeOrReverseComplementIsDrawn() && + edge->isPositiveEdge()) + edgesToSave.push_back(edge); + } + std::sort(edgesToSave.begin(), edgesToSave.end(), DeBruijnEdge::compareEdgePointers); for (int i = 0; i < edgesToSave.size(); ++i) @@ -3665,6 +3883,16 @@ QPair AssemblyGraph::getOverlapRange() const if (overlap > largestOverlap) largestOverlap = overlap; } + QMapIterator, DeBruijnEdge*> i_hiC(m_hiCDeBruijnGraphEdges); + while (i_hiC.hasNext()) + { + i_hiC.next(); + int overlap = i_hiC.value()->getOverlap(); + if (overlap < smallestOverlap) + smallestOverlap = overlap; + if (overlap > largestOverlap) + largestOverlap = overlap; + } if (smallestOverlap == std::numeric_limits::max()) smallestOverlap = 0; return QPair(smallestOverlap, largestOverlap); @@ -3742,7 +3970,6 @@ bool AssemblyGraph::allNodesStartWith(QString start) const return true; } - QString AssemblyGraph::simplifyCanuNodeName(QString oldName) const { QString newName; @@ -3779,11 +4006,10 @@ long long AssemblyGraph::getTotalLengthOrphanedNodes() const { return total; } - bool AssemblyGraph::useLinearLayout() const { // If the graph has no edges, then we use a linear layout. Otherwise check the setting. if (m_edgeCount == 0) return true; else return g_settings->linearLayout; -} +} \ No newline at end of file diff --git a/graph/assemblygraph.h b/graph/assemblygraph.h index d1a63227..f5295ed5 100644 --- a/graph/assemblygraph.h +++ b/graph/assemblygraph.h @@ -24,6 +24,7 @@ #include "../ogdf/basic/Graph.h" #include "../ogdf/basic/GraphAttributes.h" +#include "HiCSettings.h" #include #include #include "../program/globals.h" @@ -49,9 +50,11 @@ class AssemblyGraph : public QObject //Edges are stored in a map with a key of the starting and ending node //pointers. QMap, DeBruijnEdge*> m_deBruijnGraphEdges; + QMap, DeBruijnEdge*> m_hiCDeBruijnGraphEdges; ogdf::Graph * m_ogdfGraph; ogdf::EdgeArray * m_edgeArray; + ogdf::EdgeArray * m_hiCEdgeArray; ogdf::GraphAttributes * m_graphAttributes; int m_kmer; @@ -69,11 +72,12 @@ class AssemblyGraph : public QObject QString m_filename; QString m_depthTag; SequencesLoadedFromFasta m_sequencesLoadedFromFasta; + HiCSettings hiC; void cleanUp(); void createDeBruijnEdge(QString node1Name, QString node2Name, int overlap = 0, - EdgeOverlapType overlapType = UNKNOWN_OVERLAP); + EdgeOverlapType overlapType = UNKNOWN_OVERLAP, bool hiC = false, int weight = 0); void clearOgdfGraphAndResetNodes(); static QByteArray getReverseComplement(QByteArray forwardSequence); void resetEdges(); @@ -206,6 +210,12 @@ class AssemblyGraph : public QObject double findDepthAtIndex(QList * nodeList, long long targetIndex) const; bool allNodesStartWith(QString start) const; QString simplifyCanuNodeName(QString oldName) const; + QList AssemblyGraph::findHiC(QByteArray hiC); + void AssemblyGraph::loadHiC(); + void AssemblyGraph::addHiCEdges(QList biggestNodesList, QList smallestNodesList); + QList AssemblyGraph::dfs(DeBruijnNode* curNode, QByteArray hiC); + QList AssemblyGraph::getNewStartIndexes(QByteArray wgs, QByteArray hiC); + bool AssemblyGraph::isBeginWith(QByteArray wgs, QByteArray hiC); signals: void setMergeTotalCount(int totalCount); diff --git a/graph/debruijnedge.cpp b/graph/debruijnedge.cpp index 8ce5c6dc..b6a40505 100644 --- a/graph/debruijnedge.cpp +++ b/graph/debruijnedge.cpp @@ -27,7 +27,7 @@ DeBruijnEdge::DeBruijnEdge(DeBruijnNode *startingNode, DeBruijnNode *endingNode) : m_startingNode(startingNode), m_endingNode(endingNode), m_graphicsItemEdge(0), - m_drawn(false), m_overlapType(UNKNOWN_OVERLAP), m_overlap(0) + m_drawn(false), m_overlapType(UNKNOWN_OVERLAP), m_overlap(0), m_HiC(false) { } @@ -129,7 +129,36 @@ void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray* edgeArray) const +{ + ogdf::node firstEdgeOgdfNode; + ogdf::node secondEdgeOgdfNode; + if (m_startingNode->inOgdf()) + firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getMiddle(); + else if (m_startingNode->getReverseComplement()->inOgdf()) + firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getMiddle(); + else + return; //Ending node or its reverse complement isn't in OGDF + + if (m_endingNode->inOgdf()) + secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getMiddle(); + else if (m_endingNode->getReverseComplement()->inOgdf()) + secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getMiddle(); + else + return; //Ending node or its reverse complement isn't in OGDF + //If this in an edge connected a single-segment node to itself, then we + //don't want to put it in the OGDF graph, because it would be redundant + //with the node segment (and created conflict with the node/edge length). + if (m_startingNode == m_endingNode) + { + if (m_startingNode->getNumberOfOgdfGraphEdges(m_startingNode->getDrawnNodeLength()) == 1) + return; + } + + ogdf::edge newEdge = ogdfGraph->newEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); + (*edgeArray)[newEdge] = 10; +}*/ diff --git a/graph/debruijnedge.h b/graph/debruijnedge.h index b007f941..7dbe7eaa 100644 --- a/graph/debruijnedge.h +++ b/graph/debruijnedge.h @@ -37,6 +37,8 @@ class DeBruijnEdge GraphicsItemEdge * getGraphicsItemEdge() const {return m_graphicsItemEdge;} DeBruijnEdge * getReverseComplement() const {return m_reverseComplement;} bool isDrawn() const {return m_drawn;} + bool isHiC() const {return m_HiC;} + int getWeight() const { return m_weight; } int getOverlap() const {return m_overlap;} EdgeOverlapType getOverlapType() const {return m_overlapType;} DeBruijnNode * getOtherNode(const DeBruijnNode * node) const; @@ -57,6 +59,7 @@ class DeBruijnEdge bool isOwnReverseComplement() const {return this == getReverseComplement();} static bool compareEdgePointers(DeBruijnEdge * a, DeBruijnEdge * b); + //MODIFERS void setGraphicsItemEdge(GraphicsItemEdge * gie) {m_graphicsItemEdge = gie;} void setReverseComplement(DeBruijnEdge * rc) {m_reverseComplement = rc;} @@ -64,9 +67,12 @@ class DeBruijnEdge void setOverlapType(EdgeOverlapType olt) {m_overlapType = olt;} void reset() {m_graphicsItemEdge = 0; m_drawn = false;} void determineIfDrawn() {m_drawn = edgeIsVisible();} + void setDrawn(bool isDrawn) { m_drawn = isDrawn; } void setExactOverlap(int overlap) {m_overlap = overlap; m_overlapType = EXACT_OVERLAP;} void autoDetermineExactOverlap(); void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray) const; + void addHiCToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray* edgeArray) const; + void setHiC(bool hiC, int weight) { m_HiC = hiC; m_weight = weight; } private: DeBruijnNode * m_startingNode; @@ -76,6 +82,8 @@ class DeBruijnEdge bool m_drawn; EdgeOverlapType m_overlapType; int m_overlap; + bool m_HiC; + int m_weight; bool edgeIsVisible() const; int timesNodeInPath(DeBruijnNode * node, std::vector * path) const; diff --git a/graph/graphicsitemedge.cpp b/graph/graphicsitemedge.cpp index ccabcdb0..40c2299e 100644 --- a/graph/graphicsitemedge.cpp +++ b/graph/graphicsitemedge.cpp @@ -54,7 +54,13 @@ void GraphicsItemEdge::paint(QPainter * painter, const QStyleOptionGraphicsItem penColour = g_settings->selectionColour; else penColour = g_settings->edgeColour; - QPen edgePen(QBrush(penColour), edgeWidth, Qt::SolidLine, Qt::RoundCap); + Qt::PenStyle s = Qt::SolidLine; + if (m_deBruijnEdge->isHiC()) { + //int dark = m_deBruijnEdge->getWeight(); + //penColour.setRgb(dark, dark, dark); + s = Qt::DotLine; + } + QPen edgePen(QBrush(penColour), edgeWidth, s, Qt::RoundCap); painter->setPen(edgePen); painter->drawPath(path()); } @@ -128,24 +134,63 @@ void GraphicsItemEdge::setControlPointLocations() if (startingNode->hasGraphicsItem()) { - m_startingLocation = startingNode->getGraphicsItemNode()->getLast(); - m_beforeStartingLocation = startingNode->getGraphicsItemNode()->getSecondLast(); + if (m_deBruijnEdge->isHiC() && startingNode->getGraphicsItemNode() -> isBig()) { + m_startingLocation = startingNode->getGraphicsItemNode()->getMiddle(); + m_beforeStartingLocation = startingNode->getGraphicsItemNode()->getBeforeMiddle(); + /*std::ofstream vmdelet_out; + vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); + vmdelet_out << "setControlPointLocations startingNode" << startingNode->getName().toUtf8().constData() << '\n'; + vmdelet_out.close();*/ + } else { + 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 (m_deBruijnEdge->isHiC() && startingNode->getReverseComplement()->getGraphicsItemNode() -> isBig()) { + m_startingLocation = startingNode->getReverseComplement() -> getGraphicsItemNode()->getMiddle(); + m_beforeStartingLocation = startingNode->getReverseComplement() -> getGraphicsItemNode()->getAfterMiddle(); + /*std::ofstream vmdelet_out; + vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); + vmdelet_out << "setControlPointLocations startingNode" << startingNode->getName().toUtf8().constData() << '\n'; + vmdelet_out.close();*/ + } + else { + 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(); + if (m_deBruijnEdge->isHiC() && endingNode->getGraphicsItemNode() -> isBig()) { + m_endingLocation = endingNode->getGraphicsItemNode()->getMiddle(); + m_afterEndingLocation = endingNode->getGraphicsItemNode()->getAfterMiddle(); + /*std::ofstream vmdelet_out; + vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); + vmdelet_out << "setControlPointLocations endingNode" << endingNode->getName().toUtf8().constData() << '\n'; + vmdelet_out.close();*/ + } + else { + 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(); + if (m_deBruijnEdge->isHiC() && endingNode->getReverseComplement()->getGraphicsItemNode() -> isBig()) { + m_endingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getMiddle(); + m_afterEndingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getBeforeMiddle(); + std::ofstream vmdelet_out; + vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); + vmdelet_out << "setControlPointLocations endingNode" << endingNode->getName().toUtf8().constData() << '\n'; + vmdelet_out.close(); + } + else { + m_endingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getLast(); + m_afterEndingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getSecondLast(); + } } } diff --git a/graph/graphicsitemnode.h b/graph/graphicsitemnode.h index 9cf9ac3d..95ba14f4 100644 --- a/graph/graphicsitemnode.h +++ b/graph/graphicsitemnode.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include class DeBruijnNode; class Path; @@ -66,6 +68,22 @@ class GraphicsItemNode : public QGraphicsItem QPointF getSecond() const {return m_linePoints[1];} QPointF getLast() const {return m_linePoints[m_linePoints.size()-1];} QPointF getSecondLast() const {return m_linePoints[m_linePoints.size()-2];} + bool isBig() const { return m_linePoints.size() >= 3; } + QPointF getMiddle() const { return m_linePoints[m_linePoints.size() / 2]; } + QPointF getBeforeMiddle() const + { + if (m_linePoints.size() >= 3) + return m_linePoints[(m_linePoints.size() / 2) - 1]; + else + return m_linePoints[0]; + } + QPointF getAfterMiddle() const + { + if (m_linePoints.size() >= 3) + return m_linePoints[(m_linePoints.size() / 2) + 1]; + else + return m_linePoints[1]; + } std::vector getCentres() const; QPointF getCentre(std::vector linePoints) const; void setNodeColour(); diff --git a/graph/ogdfnode.h b/graph/ogdfnode.h index e6b0e1c0..dbdf2988 100644 --- a/graph/ogdfnode.h +++ b/graph/ogdfnode.h @@ -37,6 +37,15 @@ class OgdfNode ogdf::node getSecond() {if (m_ogdfNodes.size() < 2) return 0; else return m_ogdfNodes[1];} ogdf::node getLast() {if (m_ogdfNodes.size() == 0) return 0; else return m_ogdfNodes[m_ogdfNodes.size()-1];} ogdf::node getSecondLast() {if (m_ogdfNodes.size() < 2) return 0; else return m_ogdfNodes[m_ogdfNodes.size()-2];} + ogdf::node getMiddle() { + if (m_ogdfNodes.size() == 0) return 0; else { + std::ofstream vmdelet_out; + vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\HiC_log.txt", std::ios::app); + vmdelet_out << "getMiddle "<< m_ogdfNodes.size()<<" "<< m_ogdfNodes.size() / 2<<"\n"; + vmdelet_out.close(); + return m_ogdfNodes[m_ogdfNodes.size() / 2]; + } + } }; #endif // OGDFNODE_H From 2e44490605d34a9b6c6f3fc3568c47c8b98d9466 Mon Sep 17 00:00:00 2001 From: Shostina Date: Tue, 5 Oct 2021 10:10:47 +0300 Subject: [PATCH 02/11] add simple ui for hic data --- graph/assemblygraph.cpp | 129 +--- graph/assemblygraph.h | 4 +- graph/ogdfnode.h | 4 - ui/mainwindow.cpp | 54 ++ ui/mainwindow.h | 4 +- ui/mainwindow.ui | 1543 +++++++++++++++++++++------------------ 6 files changed, 920 insertions(+), 818 deletions(-) diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index f92b953e..236e20f3 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -552,8 +552,6 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) } inputFile.close(); - loadHiC(); - setAllEdgesExactOverlap(0); } @@ -561,112 +559,42 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) throw "load error"; } -void AssemblyGraph::loadHiC() { - QFile logFile("C\:\\Users\\anastasia\\study\\maga\\Bandage\\my_test_data\\hic_1_1.log"); - logFile.open(QIODevice::WriteOnly); - QFile settingsFile("C\:\\Users\\anastasia\\study\\maga\\Bandage\\my_test_data\\settings_1.txt"); - settingsFile.open(QIODevice::ReadOnly); - QTextStream inSettings(&settingsFile); - QString filePath = inSettings.readLine(); - while (!inSettings.atEnd()) { - QStringList settings = inSettings.readLine().split(QRegExp(" ")); - if (settings.length() < 2) { - continue; - } - if (settings.at(0) == "weight") { - hiC.minWeight = settings.at(1).toInt(); - } - if (settings.at(0) == "length") { - hiC.minLength = settings.at(1).toInt(); - } - if (settings.at(0) == "dist") { - hiC.minDist = settings.at(1).toInt(); - } - - } +bool AssemblyGraph::loadHiC(QString filename, QString* errormsg) +{ - QFile hiCMatrix("C\:\\Users\\anastasia\\study\\maga\\Bandage\\my_test_data\\hic_1_2.txt"); - if (hiCMatrix.open(QIODevice::ReadOnly)) + QFile hiCMatrix(filename); + if (!hiCMatrix.open(QIODevice::ReadOnly)) { - QTextStream in(&hiCMatrix); - QApplication::processEvents(); - QString line1 = in.readLine(); - hiC.numOfNodes = line1.toInt(); - logFile.write("Kontigs: \n"); - logFile.write(hiC.numOfNodes + "\n"); - for (int i = 0; i < hiC.numOfNodes; i++) { - hiC.kontigNames.push_back(convertNormalNumberStringToBandageNodeName(in.readLine())); - logFile.write(hiC.kontigNames.at(hiC.kontigNames.length() - 1).toUtf8() + "\n"); - } - logFile.write("Edges: \n"); - for (int i = 0; i < hiC.numOfNodes; i++) { - QStringList weights = in.readLine().split(QRegExp("\t")); - QVector tempWeights; - if (weights.length() == hiC.numOfNodes) { - for (int j = 0; j <= i; j++) { - int weight = weights.at(j).toInt(); - tempWeights.push_back(weight); - QString firstNodeName = hiC.kontigNames.at(i); - QString secondNodeName = hiC.kontigNames.at(j); - if (weight > 0 && firstNodeName != secondNodeName) { - createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true, weight); - logFile.write(firstNodeName.toUtf8() + " " + secondNodeName.toUtf8() + "\n"); - } - } - hiC.weightMatrix.push_back(tempWeights); - } - } - logFile.close(); + *errormsg = "Unable to read from specified file."; + return false; } -} - -//first List of nodes has more nodes than second List of nodes -/*void AssemblyGraph::addHiCEdges(QList biggestNodesList, QList smallestNodesList) { - if (biggestNodesList.length() >= smallestNodesList.length()) { - int bigSize = biggestNodesList.length(); - int smallSizeHalf = smallestNodesList.length() / 2; - int middlePart = biggestNodesList.length() - (smallestNodesList.length() / 2) * 2; - - - QList::iterator startBigListIter = biggestNodesList.begin(); - QList::iterator endBigListIter = biggestNodesList.end(); - endBigListIter--; - QList::iterator startSmallListIter = smallestNodesList.begin(); - QList::iterator endSmallListIter = smallestNodesList.end(); - endSmallListIter--; - for (int i = 0; i < smallSizeHalf; i++) { - QString firstNodeName = (*startBigListIter)->getName(); - QString secondNodeName = (*startSmallListIter)->getName(); - if (firstNodeName != secondNodeName) { - createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true); - createDeBruijnEdge(secondNodeName, firstNodeName, 0, UNKNOWN_OVERLAP, true); - } - - firstNodeName = (*endBigListIter)->getName(); - secondNodeName = (*endSmallListIter)->getName(); - if (firstNodeName != secondNodeName) { - createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true); - createDeBruijnEdge(secondNodeName, firstNodeName, 0, UNKNOWN_OVERLAP, true); - } - startBigListIter++; - startSmallListIter++; - endBigListIter--; - endSmallListIter--; - - } - QString middleNodeName = smallestNodesList.at(smallestNodesList.length() / 2) -> getName(); - for (int i = 0; i < middlePart; i++) { - QString curNodeName = (*startBigListIter)->getName(); - if (middleNodeName != curNodeName) { - createDeBruijnEdge(middleNodeName, curNodeName, 0, UNKNOWN_OVERLAP, true); - createDeBruijnEdge(curNodeName, middleNodeName, 0, UNKNOWN_OVERLAP, true); + QTextStream in(&hiCMatrix); + QApplication::processEvents(); + QString line1 = in.readLine(); + hiC.numOfNodes = line1.toInt(); + for (int i = 0; i < hiC.numOfNodes; i++) { + hiC.kontigNames.push_back(convertNormalNumberStringToBandageNodeName(in.readLine())); + } + for (int i = 0; i < hiC.numOfNodes; i++) { + QStringList weights = in.readLine().split(QRegExp("\t")); + QVector tempWeights; + if (weights.length() == hiC.numOfNodes) { + for (int j = 0; j <= i; j++) { + int weight = weights.at(j).toInt(); + tempWeights.push_back(weight); + QString firstNodeName = hiC.kontigNames.at(i); + QString secondNodeName = hiC.kontigNames.at(j); + if (weight > 0 && firstNodeName != secondNodeName) { + createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true, weight); + } } - startBigListIter++; + hiC.weightMatrix.push_back(tempWeights); } } + return true; } -*/ + //This function takes a normal number string like "5" or "-6" and changes //it to "5+" or "6-" - the format of Bandage node names. @@ -992,7 +920,6 @@ void AssemblyGraph::buildDeBruijnGraphFromGfa(QString fullFileName, bool *unsupp } m_sequencesLoadedFromFasta = NOT_TRIED; - loadHiC(); } diff --git a/graph/assemblygraph.h b/graph/assemblygraph.h index f5295ed5..bc28ac14 100644 --- a/graph/assemblygraph.h +++ b/graph/assemblygraph.h @@ -171,6 +171,8 @@ class AssemblyGraph : public QObject bool attemptToLoadSequencesFromFasta(); long long getTotalLengthOrphanedNodes() const; bool useLinearLayout() const; + bool AssemblyGraph::loadHiC(QString filename, QString* errormsg); + void AssemblyGraph::loadHiCSettings(); private: @@ -210,8 +212,6 @@ class AssemblyGraph : public QObject double findDepthAtIndex(QList * nodeList, long long targetIndex) const; bool allNodesStartWith(QString start) const; QString simplifyCanuNodeName(QString oldName) const; - QList AssemblyGraph::findHiC(QByteArray hiC); - void AssemblyGraph::loadHiC(); void AssemblyGraph::addHiCEdges(QList biggestNodesList, QList smallestNodesList); QList AssemblyGraph::dfs(DeBruijnNode* curNode, QByteArray hiC); QList AssemblyGraph::getNewStartIndexes(QByteArray wgs, QByteArray hiC); diff --git a/graph/ogdfnode.h b/graph/ogdfnode.h index dbdf2988..36d7b810 100644 --- a/graph/ogdfnode.h +++ b/graph/ogdfnode.h @@ -39,10 +39,6 @@ class OgdfNode ogdf::node getSecondLast() {if (m_ogdfNodes.size() < 2) return 0; else return m_ogdfNodes[m_ogdfNodes.size()-2];} ogdf::node getMiddle() { if (m_ogdfNodes.size() == 0) return 0; else { - std::ofstream vmdelet_out; - vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\HiC_log.txt", std::ios::app); - vmdelet_out << "getMiddle "<< m_ogdfNodes.size()<<" "<< m_ogdfNodes.size() / 2<<"\n"; - vmdelet_out.close(); return m_ogdfNodes[m_ogdfNodes.size() / 2]; } } diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 9d1008c5..5a24144e 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -132,6 +132,7 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : connect(ui->drawGraphButton, SIGNAL(clicked()), this, SLOT(drawGraph())); connect(ui->actionLoad_graph, SIGNAL(triggered()), this, SLOT(loadGraph())); connect(ui->actionLoad_CSV, SIGNAL(triggered(bool)), this, SLOT(loadCSV())); + connect(ui->actionLoad_HiC_data, SIGNAL(triggered(bool)), this, SLOT(loadHiC())); connect(ui->actionExit, SIGNAL(triggered()), this, SLOT(close())); connect(ui->graphScopeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(graphScopeChanged())); connect(ui->zoomSpinBox, SIGNAL(valueChanged(double)), this, SLOT(zoomSpinBoxChanged())); @@ -264,6 +265,45 @@ void MainWindow::cleanUp() g_settings->displayNodeCsvDataCol = 0; } +void MainWindow::loadHiC(QString fullFileName) { + QString selectedFilter = "Comma separated value (*.txt)"; + if (fullFileName == "") + { + fullFileName = QFileDialog::getOpenFileName(this, "Load Hi-C", g_memory->rememberedPath, + "Comma separated value (*.txt)", + &selectedFilter); + } + + if (fullFileName == "") + return; // user clicked on cancel + QString errormsg; + QStringList columns; + + try + { + MyProgressDialog progress(this, "Loading Hi-C...", false); + progress.setWindowModality(Qt::WindowModal); + progress.show(); + + bool coloursLoaded = false; + bool success = g_assemblyGraph->loadHiC(fullFileName, &errormsg); + + if (success) + { + setHiCWidgetVisibility(true); + } + } + catch (...) + { + QString errorTitle = "Error loading HiC"; + QString errorMessage = "There was an error when attempting to load:\n" + + fullFileName + "\n\n" + "Please verify that this file has the correct format."; + QMessageBox::warning(this, errorTitle, errorMessage); + } + +} + void MainWindow::loadCSV(QString fullFileName) { QString selectedFilter = "Comma separated value (*.csv)"; @@ -717,6 +757,14 @@ void MainWindow::setDepthRangeWidgetVisibility(bool visible) ui->maxDepthSpinBox->setVisible(visible); } +void MainWindow::setHiCWidgetVisibility(bool visible) +{ + ui->hicSeqLenInfoText->setVisible(visible); + ui->hicSeqLenSpinBox->setVisible(visible); + ui->hicWeightInfoText->setVisible(visible); + ui->hicWeightSpinBox->setVisible(visible); +} + void MainWindow::drawGraph() { @@ -733,6 +781,9 @@ void MainWindow::drawGraph() return; } + g_assemblyGraph->hiC.minWeight = ui->hicWeightSpinBox->value(); + g_assemblyGraph->hiC.minLength = ui->hicSeqLenSpinBox->value(); + resetScene(); g_assemblyGraph->buildOgdfGraphFromNodesAndEdges(startingNodes, g_settings->nodeDistance); layoutGraph(); @@ -1772,6 +1823,7 @@ void MainWindow::setUiState(UiState uiState) ui->blastSearchWidget->setEnabled(false); ui->selectionScrollAreaWidgetContents->setEnabled(false); ui->actionLoad_CSV->setEnabled(false); + ui->actionLoad_HiC_data->setEnabled(false); break; case GRAPH_LOADED: ui->graphDetailsWidget->setEnabled(true); @@ -1781,6 +1833,7 @@ void MainWindow::setUiState(UiState uiState) ui->blastSearchWidget->setEnabled(true); ui->selectionScrollAreaWidgetContents->setEnabled(false); ui->actionLoad_CSV->setEnabled(true); + ui->actionLoad_HiC_data->setEnabled(true); break; case GRAPH_DRAWN: ui->graphDetailsWidget->setEnabled(true); @@ -1791,6 +1844,7 @@ void MainWindow::setUiState(UiState uiState) ui->selectionScrollAreaWidgetContents->setEnabled(true); ui->actionZoom_to_selection->setEnabled(true); ui->actionLoad_CSV->setEnabled(true); + ui->actionLoad_HiC_data->setEnabled(true); break; } } diff --git a/ui/mainwindow.h b/ui/mainwindow.h index fafc0864..08d88101 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -92,6 +92,7 @@ class MainWindow : public QMainWindow void setStartingNodesWidgetVisibility(bool visible); void setNodeDistanceWidgetVisibility(bool visible); void setDepthRangeWidgetVisibility(bool visible); + void MainWindow::setHiCWidgetVisibility(bool visible); static QByteArray makeStringUrlSafe(QByteArray s); void removeGraphicsItemNodes(const std::vector * nodes, bool reverseComplement); void removeGraphicsItemEdges(const std::vector * edges, bool reverseComplement); @@ -100,7 +101,8 @@ class MainWindow : public QMainWindow private slots: void loadGraph(QString fullFileName = ""); - void loadCSV(QString fullFileNAme = ""); + void loadCSV(QString fullFileName = ""); + void loadHiC(QString fullFileName = ""); void selectionChanged(); void graphScopeChanged(); void drawGraph(); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 7ac8182f..99183e46 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -59,8 +59,8 @@ 0 0 - 289 - 1058 + 251 + 976 @@ -259,443 +259,554 @@ - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Exact - - - true - - - - - - - - 0 - 0 - - - - Partial - - - - - - - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Single - - - true - - - - - - - - 0 - 0 - - - - Double - - - + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Exact + + + true + + + + + + + + 0 + 0 + + + + Partial + + + + + + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Single + + + true + + + + + + + + 0 + 0 + + + + Double + + + + + + + + + + Draw graph + + + + + + + + 0 + 0 + + + + Style: + + + + + + + true + + + + 0 + 0 + + + + Qt::AlignCenter + + + 10000 + + + + + + + true + + + + 0 + 0 + + + + Node(s): + + + + + + + true + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + Entire graph + + + + + Around nodes + + + + + Around BLAST hits + + + + + Depth range + + + + + + + + + 0 + 0 + + + + Scope: + + + + + + + true + + + Distance: + + + + + + + true + + + + 0 + 0 + + + + Match: + + + + + + + Qt::AlignCenter + + + 1 + + + 1000000.000000000000000 + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + HiC min weight: + + + Qt::AlignLeft + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.000000000000000 + + + 1000.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + HiC min sequence length: + + + Qt::AlignLeft + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.000000000000000 + + + 1.000000000000000 + + + 5.000000000000000 + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + Min: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + Max: + + + + + + + Qt::AlignCenter + + + 1 + + + 1000000.000000000000000 + + + - - - - - - Draw graph - - - - - - - - 0 - 0 - - - - Style: - - - - - - - true - - - - 0 - 0 - - - - Qt::AlignCenter - - - 10000 - - - - - - - true - - - - 0 - 0 - - - - Node(s): - - - - - - - true - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - Entire graph - - - - - Around nodes - - - - - Around BLAST hits - - - - - Depth range - - - - - - - - - 0 - 0 - - - - Scope: - - - - - - - true - - - Distance: - - - - - - - true - - - - 0 - 0 - - - - Match: - - - - - - - Qt::AlignCenter - - - 1 - - - 1000000.000000000000000 - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - Min: - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - Max: - - - - - - - Qt::AlignCenter - - - 1 - - - 1000000.000000000000000 - - - - @@ -757,209 +868,209 @@ - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - % - - - 1 - - - 5.000000000000000 - - - 500.000000000000000 - - - 5.000000000000000 - - - 100.000000000000000 - - - - - - - Determine contiguity - - - - - - - - Random colours - - - - - Uniform colour - - - - - Colour by depth - - - - - BLAST hits (solid) - - - - - BLAST hits (rainbow) - - - - - Colour by contiguity - - - - - Custom colours - - - - - - - - Zoom: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - Node width: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - - - - 1 - - - 0.500000000000000 - - - 1000.000000000000000 - - - 0.500000000000000 - - - 5.000000000000000 - - - - + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + % + + + 1 + + + 5.000000000000000 + + + 500.000000000000000 + + + 5.000000000000000 + + + 100.000000000000000 + + + + + + + Determine contiguity + + + + + + + + Random colours + + + + + Uniform colour + + + + + Colour by depth + + + + + BLAST hits (solid) + + + + + BLAST hits (rainbow) + + + + + Colour by contiguity + + + + + Custom colours + + + + + + + + Zoom: + + + Qt::AlignLeft + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Node width: + + + Qt::AlignLeft + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.500000000000000 + + + 1000.000000000000000 + + + 0.500000000000000 + + + 5.000000000000000 + + + + @@ -1079,72 +1190,72 @@ - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Custom - - - - - - - Name - - - - - - - Length - - - - - - - Depth - - - - - - - BLAST hits - - - - - - - false - - - - - - - CSV data: - - - - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Custom + + + + + + + Name + + + + + + + Length + + + + + + + Depth + + + + + + + BLAST hits + + + + + + + false + + + + + + + CSV data: + + + + @@ -1355,8 +1466,8 @@ 0 0 - 249 - 899 + 239 + 871 @@ -1775,7 +1886,7 @@ 0 0 1465 - 22 + 26 @@ -1789,6 +1900,8 @@ + + @@ -2226,6 +2339,15 @@ Change node depth + + + + :/icons/load-256.png:/icons/load-256.png + + + Load HiC data + + @@ -2254,6 +2376,7 @@ drawGraphButton zoomSpinBox nodeWidthSpinBox + hiCWeightSpinBox coloursComboBox contiguityButton nodeCustomLabelsCheckBox From 5d7999a8cc5abb14e9d7b783574a8d832c3114bd Mon Sep 17 00:00:00 2001 From: Shostina Date: Tue, 19 Oct 2021 10:26:38 +0300 Subject: [PATCH 03/11] impl grey feature --- graph/assemblygraph.cpp | 1 + graph/debruijnedge.cpp | 60 +++++++++++++++++------------------ graph/graphicsitemedge.cpp | 16 ++-------- program/graphlayoutworker.cpp | 1 + 4 files changed, 34 insertions(+), 44 deletions(-) diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index 236e20f3..0cdefacf 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -70,6 +70,7 @@ AssemblyGraph::~AssemblyGraph() void AssemblyGraph::cleanUp() { + QMapIterator i(m_deBruijnGraphNodes); while (i.hasNext()) { diff --git a/graph/debruijnedge.cpp b/graph/debruijnedge.cpp index b6a40505..a679ed9d 100644 --- a/graph/debruijnedge.cpp +++ b/graph/debruijnedge.cpp @@ -129,36 +129,36 @@ void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray* edgeArray) const -{ - ogdf::node firstEdgeOgdfNode; - ogdf::node secondEdgeOgdfNode; - if (m_startingNode->inOgdf()) - firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getMiddle(); - else if (m_startingNode->getReverseComplement()->inOgdf()) - firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getMiddle(); - else - return; //Ending node or its reverse complement isn't in OGDF - - if (m_endingNode->inOgdf()) - secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getMiddle(); - else if (m_endingNode->getReverseComplement()->inOgdf()) - secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getMiddle(); - else - return; //Ending node or its reverse complement isn't in OGDF - - //If this in an edge connected a single-segment node to itself, then we - //don't want to put it in the OGDF graph, because it would be redundant - //with the node segment (and created conflict with the node/edge length). - if (m_startingNode == m_endingNode) - { - if (m_startingNode->getNumberOfOgdfGraphEdges(m_startingNode->getDrawnNodeLength()) == 1) - return; - } - - ogdf::edge newEdge = ogdfGraph->newEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); - (*edgeArray)[newEdge] = 10; -}*/ +//void DeBruijnEdge::addHiCToOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::EdgeArray* edgeArray) const +//{ +// ogdf::node firstEdgeOgdfNode; +// ogdf::node secondEdgeOgdfNode; +// if (m_startingNode->inOgdf()) +// firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getMiddle(); +// else if (m_startingNode->getReverseComplement()->inOgdf()) +// firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getMiddle(); +// else +// return; //Ending node or its reverse complement isn't in OGDF +// +// if (m_endingNode->inOgdf()) +// secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getMiddle(); +// else if (m_endingNode->getReverseComplement()->inOgdf()) +// secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getMiddle(); +// else +// return; //Ending node or its reverse complement isn't in OGDF +// +// //If this in an edge connected a single-segment node to itself, then we +// //don't want to put it in the OGDF graph, because it would be redundant +// //with the node segment (and created conflict with the node/edge length). +// if (m_startingNode == m_endingNode) +// { +// if (m_startingNode->getNumberOfOgdfGraphEdges(m_startingNode->getDrawnNodeLength()) == 1) +// return; +// } +// +// ogdf::edge newEdge = ogdfGraph->newEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); +// (*edgeArray)[newEdge] = g_settings->edgeLength; +//} diff --git a/graph/graphicsitemedge.cpp b/graph/graphicsitemedge.cpp index 40c2299e..aeef5515 100644 --- a/graph/graphicsitemedge.cpp +++ b/graph/graphicsitemedge.cpp @@ -56,8 +56,8 @@ void GraphicsItemEdge::paint(QPainter * painter, const QStyleOptionGraphicsItem penColour = g_settings->edgeColour; Qt::PenStyle s = Qt::SolidLine; if (m_deBruijnEdge->isHiC()) { - //int dark = m_deBruijnEdge->getWeight(); - //penColour.setRgb(dark, dark, dark); + int dark = 255 - (m_deBruijnEdge->getWeight())*20; + penColour.setRgb(dark, dark, dark); s = Qt::DotLine; } QPen edgePen(QBrush(penColour), edgeWidth, s, Qt::RoundCap); @@ -137,10 +137,6 @@ void GraphicsItemEdge::setControlPointLocations() if (m_deBruijnEdge->isHiC() && startingNode->getGraphicsItemNode() -> isBig()) { m_startingLocation = startingNode->getGraphicsItemNode()->getMiddle(); m_beforeStartingLocation = startingNode->getGraphicsItemNode()->getBeforeMiddle(); - /*std::ofstream vmdelet_out; - vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); - vmdelet_out << "setControlPointLocations startingNode" << startingNode->getName().toUtf8().constData() << '\n'; - vmdelet_out.close();*/ } else { m_startingLocation = startingNode->getGraphicsItemNode()->getLast(); m_beforeStartingLocation = startingNode->getGraphicsItemNode()->getSecondLast(); @@ -151,10 +147,6 @@ void GraphicsItemEdge::setControlPointLocations() if (m_deBruijnEdge->isHiC() && startingNode->getReverseComplement()->getGraphicsItemNode() -> isBig()) { m_startingLocation = startingNode->getReverseComplement() -> getGraphicsItemNode()->getMiddle(); m_beforeStartingLocation = startingNode->getReverseComplement() -> getGraphicsItemNode()->getAfterMiddle(); - /*std::ofstream vmdelet_out; - vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); - vmdelet_out << "setControlPointLocations startingNode" << startingNode->getName().toUtf8().constData() << '\n'; - vmdelet_out.close();*/ } else { m_startingLocation = startingNode->getReverseComplement()->getGraphicsItemNode()->getFirst(); @@ -167,10 +159,6 @@ void GraphicsItemEdge::setControlPointLocations() if (m_deBruijnEdge->isHiC() && endingNode->getGraphicsItemNode() -> isBig()) { m_endingLocation = endingNode->getGraphicsItemNode()->getMiddle(); m_afterEndingLocation = endingNode->getGraphicsItemNode()->getAfterMiddle(); - /*std::ofstream vmdelet_out; - vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); - vmdelet_out << "setControlPointLocations endingNode" << endingNode->getName().toUtf8().constData() << '\n'; - vmdelet_out.close();*/ } else { m_endingLocation = endingNode->getGraphicsItemNode()->getFirst(); diff --git a/program/graphlayoutworker.cpp b/program/graphlayoutworker.cpp index b193aa4b..8f755148 100644 --- a/program/graphlayoutworker.cpp +++ b/program/graphlayoutworker.cpp @@ -77,6 +77,7 @@ void GraphLayoutWorker::layoutGraph() } m_fmmm->call(*m_graphAttributes, *m_edgeArray); + //m_fmmm->call(*m_graphAttributes); emit finishedLayout(); } From b6684070f2581226f90bcc6c109ead595a4ac445 Mon Sep 17 00:00:00 2001 From: Shostina Date: Sun, 20 Feb 2022 13:18:53 +0300 Subject: [PATCH 04/11] add filter by different component id --- HiCSettings.cpp | 59 ++++---- HiCSettings.h | 29 ++-- graph/assemblygraph.cpp | 259 ++++++++++++++++++++-------------- graph/assemblygraph.h | 8 +- graph/debruijnedge.cpp | 85 +++++------ graph/debruijnedge.h | 4 +- graph/debruijnnode.cpp | 48 +++++++ graph/debruijnnode.h | 4 + graph/graphicsitemedge.cpp | 13 +- graph/graphicsitemnode.cpp | 1 + graph/graphicsitemnode.h | 16 ++- program/graphlayoutworker.cpp | 19 ++- program/graphlayoutworker.h | 1 + ui/mainwindow.cpp | 39 +++-- ui/mainwindow.h | 5 + 15 files changed, 366 insertions(+), 224 deletions(-) diff --git a/HiCSettings.cpp b/HiCSettings.cpp index d63df263..40b7a0d9 100644 --- a/HiCSettings.cpp +++ b/HiCSettings.cpp @@ -1,43 +1,50 @@ #include "HiCSettings.h" #include "../graph/debruijnnode.h" -#include "../graph/debruijnedge.h" +#include -HiCSettings::HiCSettings():numOfNodes(0), minWeight(0), minLength(1), minDist(1) {} +HiCSettings::HiCSettings() : minWeight(0), minLength(1), minDist(1) {} HiCSettings::~HiCSettings() {} bool HiCSettings::isDrawn(DeBruijnEdge* edge) { - /*bool drawEdge = (edge->getStartingNode()->isDrawn() || edge->getStartingNode()->getReverseComplement()->isDrawn()) - && (edge->getEndingNode()->isDrawn() || edge->getEndingNode()->getReverseComplement()->isDrawn());*/ edge->determineIfDrawn(); bool drawEdge = edge->isDrawn(); if (!drawEdge) return false; - return (getEdgeWeight(edge) >= minWeight && - edge -> getStartingNode() -> getLength() >= minLength && - edge->getEndingNode()->getLength() >= minLength); + bool res(edge->getWeight() >= minWeight && + edge->getStartingNode()->getLength() >= minLength && + edge->getEndingNode()->getLength() >= minLength && + (filterHiC == 0 || + (filterHiC == 1 && edge->getStartingNode()->getComponentId() != edge->getEndingNode()->getComponentId()) || + (filterHiC == 2 && contains(edge))) + ); + return res; + } -int HiCSettings::getEdgeWeight(DeBruijnEdge* edge) { - QString startingNodeName = edge->getStartingNode()->getName(); - QString endingNodeName = edge->getEndingNode()->getName(); - int startingNodeInd = -1; - int endingNodeInd = -1; - for (int i = 0; i < numOfNodes; i++) { - if (kontigNames.at(i) == startingNodeName) { - startingNodeInd = i; +bool HiCSettings::addEdgeIfNeeded(DeBruijnEdge* edge) { + if (edge->getStartingNode()->getComponentId() != edge->getEndingNode()->getComponentId() && edge->isPositiveEdge()) { + QPair key = qMakePair(std::min(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId()), + std::max(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId())); + if (!componentEdgeMap.contains(key)) { + componentEdgeMap[key] = edge; + return true; } - if (kontigNames.at(i) == endingNodeName) { - endingNodeInd = i; + if (componentEdgeMap[key]->getStartingNode() == edge->getStartingNode()->getReverseComplement() && + componentEdgeMap[key]->getEndingNode() == edge->getEndingNode()->getReverseComplement() || + componentEdgeMap[key]->getStartingNode() == edge->getEndingNode()->getReverseComplement() && + componentEdgeMap[key]->getEndingNode() == edge->getStartingNode()->getReverseComplement()) { + QPair reverseKey = qMakePair(std::max(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId()), + std::min(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId())); + componentEdgeMap[reverseKey] = edge; } } - QFile logFile("C\:\\Users\\anastasia\\study\\maga\\Bandage\\my_test_data\\hic_1_1.log"); - logFile.open(QIODevice::WriteOnly); - logFile.write("getEdgeWeight: " + startingNodeName.toUtf8() + " " + endingNodeName.toUtf8() + "\n"); - logFile.write("getEdgeWeight: " + startingNodeInd + ' ' + endingNodeInd + '\n'); - logFile.close(); - if (startingNodeInd != -1 && endingNodeInd != -1) { - return weightMatrix.at(startingNodeInd).at(endingNodeInd); - } - return 0; + return false; +} + +bool HiCSettings::contains(DeBruijnEdge* edge) { + QPair key = qMakePair(std::min(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId()), + std::max(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId())); + return componentEdgeMap.contains(key) && + componentEdgeMap[key] == edge; } diff --git a/HiCSettings.h b/HiCSettings.h index 90f7f9ce..b712bb5a 100644 --- a/HiCSettings.h +++ b/HiCSettings.h @@ -4,12 +4,16 @@ #include #include -#include "../ogdf/basic/Graph.h" -#include "../ogdf/basic/GraphAttributes.h" -#include +#include +#include #include +#include + + #include "../program/globals.h" #include "../ui/mygraphicsscene.h" +#include "../graph/debruijnedge.h" + class HiCSettings : public QObject { Q_OBJECT @@ -17,19 +21,22 @@ class HiCSettings : public QObject public: HiCSettings(); ~HiCSettings(); - - int numOfNodes = 0; + + //int numOfNodes = 0; int minWeight = 0; int minLength = 1; int minDist = 1; - QVector kontigNames; - QVector > weightMatrix; - QVector > distanceMatrix; + int maxWeight = 0; + //0 - no filter + //1 - all edges between components + //2 - one edge between component + int filterHiC = 0; + int componentNum = 0; + QMap, DeBruijnEdge*> componentEdgeMap; bool isDrawn(DeBruijnEdge* edge); + bool HiCSettings::addEdgeIfNeeded(DeBruijnEdge* edge); + bool HiCSettings::contains(DeBruijnEdge* edge); private: - int getEdgeWeight(DeBruijnEdge* edge); - - }; #endif //HICSETTINGS_H diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index 0cdefacf..e86253d0 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -55,7 +55,8 @@ AssemblyGraph::AssemblyGraph() : m_edgeArray = new ogdf::EdgeArray(*m_ogdfGraph); m_hiCEdgeArray = new ogdf::EdgeArray(*m_ogdfGraph); m_graphAttributes = new ogdf::GraphAttributes(*m_ogdfGraph, ogdf::GraphAttributes::nodeGraphics | - ogdf::GraphAttributes::edgeGraphics); + ogdf::GraphAttributes::edgeGraphics); + m_hiC = new HiCSettings(); clearGraphInfo(); } @@ -65,6 +66,7 @@ AssemblyGraph::~AssemblyGraph() delete m_edgeArray; delete m_hiCEdgeArray; delete m_ogdfGraph; + delete m_hiC; } @@ -96,6 +98,7 @@ void AssemblyGraph::cleanUp() m_hiCDeBruijnGraphEdges.clear(); m_contiguitySearchDone = false; + m_hiC = new HiCSettings(); clearGraphInfo(); } @@ -104,7 +107,7 @@ void AssemblyGraph::cleanUp() //and the opposite direction for their reverse complements. It adds the //new edges to the vector here and to the nodes themselves. void AssemblyGraph::createDeBruijnEdge(QString node1Name, QString node2Name, - int overlap, EdgeOverlapType overlapType, bool hiC, int weight) + int overlap, EdgeOverlapType overlapType, bool isHiC, int weight) { QString node1Opposite = getOppositeNodeName(node1Name); QString node2Opposite = getOppositeNodeName(node2Name); @@ -150,9 +153,9 @@ void AssemblyGraph::createDeBruijnEdge(QString node1Name, QString node2Name, forwardEdge->setOverlapType(overlapType); backwardEdge->setOverlapType(overlapType); - forwardEdge->setHiC(hiC, weight); - backwardEdge->setHiC(hiC, weight); - if (!hiC) { + forwardEdge->setHiC(isHiC, weight); + backwardEdge->setHiC(isHiC, weight); + if (!isHiC) { m_deBruijnGraphEdges.insert(QPair(forwardEdge->getStartingNode(), forwardEdge->getEndingNode()), forwardEdge); if (!isOwnPair) m_deBruijnGraphEdges.insert(QPair(backwardEdge->getStartingNode(), backwardEdge->getEndingNode()), backwardEdge); @@ -434,7 +437,7 @@ void AssemblyGraph::determineGraphInfo() //target average node length. But if the graph is small, the value will be //increased (to avoid having an overly small and simple graph layout). double targetDrawnGraphLength = std::max(m_nodeCount * g_settings->meanNodeLength, - g_settings->minTotalGraphLength); + g_settings->minTotalGraphLength); double megabases = totalLength / 1000000.0; if (megabases > 0.0) g_settings->autoNodeLengthPerMegabase = targetDrawnGraphLength / megabases; @@ -499,7 +502,7 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) { QStringList firstLineParts = line.split(QRegExp("\\s+")); if (firstLineParts.size() > 2) - m_kmer = firstLineParts[2].toInt(); + m_kmer = firstLineParts[2].toInt(); firstLine = false; } @@ -562,6 +565,7 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) bool AssemblyGraph::loadHiC(QString filename, QString* errormsg) { + findComponents(); QFile hiCMatrix(filename); if (!hiCMatrix.open(QIODevice::ReadOnly)) @@ -572,27 +576,31 @@ bool AssemblyGraph::loadHiC(QString filename, QString* errormsg) QTextStream in(&hiCMatrix); QApplication::processEvents(); - QString line1 = in.readLine(); - hiC.numOfNodes = line1.toInt(); - for (int i = 0; i < hiC.numOfNodes; i++) { - hiC.kontigNames.push_back(convertNormalNumberStringToBandageNodeName(in.readLine())); - } - for (int i = 0; i < hiC.numOfNodes; i++) { - QStringList weights = in.readLine().split(QRegExp("\t")); - QVector tempWeights; - if (weights.length() == hiC.numOfNodes) { - for (int j = 0; j <= i; j++) { - int weight = weights.at(j).toInt(); - tempWeights.push_back(weight); - QString firstNodeName = hiC.kontigNames.at(i); - QString secondNodeName = hiC.kontigNames.at(j); - if (weight > 0 && firstNodeName != secondNodeName) { - createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true, weight); - } - } - hiC.weightMatrix.push_back(tempWeights); + QString line = in.readLine(); + int maxWeight = 0; + + while ((line = in.readLine()) != "") { + QStringList data = line.split(QRegExp("\t")); + QString firstNodeName = (convertNormalNumberStringToBandageNodeName(data.at(0).right(data.at(0).length() - 2))); + QString secondNodeName = (convertNormalNumberStringToBandageNodeName(data.at(1).right(data.at(1).length() - 2))); + int weight = data.at(2).toInt(); + + if (weight > 0 && firstNodeName != secondNodeName) { + createDeBruijnEdge(firstNodeName, secondNodeName, 0, UNKNOWN_OVERLAP, true, weight); + } + if (weight > maxWeight) { + maxWeight = weight; } } + m_hiC->maxWeight = maxWeight; + + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + m_hiC->addEdgeIfNeeded(edge); + } return true; } @@ -616,7 +624,7 @@ QString AssemblyGraph::convertNormalNumberStringToBandageNodeName(QString number //encountered an unsupported CIGAR string, whether the GFA has custom labels //and whether it has custom colours. void AssemblyGraph::buildDeBruijnGraphFromGfa(QString fullFileName, bool *unsupportedCigar, - bool *customLabels, bool *customColours, QString *bandageOptionsError) + bool *customLabels, bool *customColours, QString *bandageOptionsError) { m_graphFileType = GFA; m_filename = fullFileName; @@ -1130,8 +1138,8 @@ void AssemblyGraph::makeReverseComplementNodeIfNecessary(DeBruijnNode * node) else nodeSequence = node->getSequence(); DeBruijnNode * newNode = new DeBruijnNode(reverseComplementName, node->getDepth(), - getReverseComplement(nodeSequence), - node->getLength()); + getReverseComplement(nodeSequence), + node->getLength()); m_deBruijnGraphNodes.insert(reverseComplementName, newNode); } } @@ -1829,6 +1837,46 @@ bool AssemblyGraph::loadGraphFromFile(QString filename) return true; } +void AssemblyGraph::findComponents() { + QMapIterator i(m_deBruijnGraphNodes); + int componentId = 1; + while (i.hasNext()) + { + i.next(); + DeBruijnNode * node = i.value(); + if (node->getComponentId() == 0) { + bfs(node, componentId); + componentId++; + } + } + m_hiC->componentNum = componentId - 1; +} + +void AssemblyGraph::bfs(DeBruijnNode * node, int componentId) { + if (node->getComponentId() == 0) { + node->setComponentId(componentId); + node->getReverseComplement()->setComponentId(componentId); + for (DeBruijnEdge* edge : node->getLeavingEdges()) { + if (edge->getEndingNode()->getComponentId() == 0 && !edge->isHiC()) { + bfs(edge->getEndingNode(), componentId); + } + } + for (DeBruijnEdge* edge : node->getEnteringEdges()) { + if (edge->getStartingNode()->getComponentId() == 0 && !edge->isHiC()) { + bfs(edge->getStartingNode(), componentId); + } + } + } +} + +void AssemblyGraph::buildOgdfGraphFromNodesAndEdgesWithHiC(std::vector startingNodes, int nodeDistance) { + m_ogdfGraph = new ogdf::Graph(); + m_edgeArray = new ogdf::EdgeArray(*m_ogdfGraph); + m_graphAttributes = new ogdf::GraphAttributes(*m_ogdfGraph, ogdf::GraphAttributes::nodeGraphics | + ogdf::GraphAttributes::edgeGraphics); + buildOgdfGraphFromNodesAndEdges(startingNodes, nodeDistance); + addHiCEdges(startingNodes); +} //The startingNodes and nodeDistance parameters are only used if the graph scope @@ -1905,7 +1953,7 @@ void AssemblyGraph::buildOgdfGraphFromNodesAndEdges(std::vector i.next(); DeBruijnNode * node = i.value(); if (node->isDrawn()) - sortedDrawnNodes.push_back(node); + sortedDrawnNodes.push_back(node); } std::sort(sortedDrawnNodes.begin(), sortedDrawnNodes.end(), [](DeBruijnNode * a, DeBruijnNode * b) {return QString::localeAwareCompare(a->getNameWithoutSign().toUpper(), b->getNameWithoutSign().toUpper()) < 0;}); @@ -1963,18 +2011,22 @@ void AssemblyGraph::buildOgdfGraphFromNodesAndEdges(std::vector j.next(); DeBruijnEdge * edge = j.value(); edge->determineIfDrawn(); - if (edge->isDrawn()) - edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); + if (!edge->isHiC() && edge->isDrawn()) + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray, m_hiC->maxWeight); } +} - //Then loop through each edge determining its drawn status and adding it to OGDF if it is drawn. - +void AssemblyGraph::addHiCEdges(std::vector startingNodes) { + //Then loop through each hic edge determining its drawn status and adding it to OGDF if it is drawn. QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); while (j_hiC.hasNext()) { - j_hiC.next(); - DeBruijnEdge* edge = j_hiC.value(); - edge->setDrawn(hiC.isDrawn(edge)); + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + edge->setDrawn(m_hiC->isDrawn(edge)); + if (m_hiC->isDrawn(edge)) { + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray, m_hiC->maxWeight); + } } } @@ -2016,7 +2068,7 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) j.next(); DeBruijnEdge * edge = j.value(); - if (edge->isDrawn()) + if (edge->isDrawn() && !edge->isHiC()) { GraphicsItemEdge * graphicsItemEdge = new GraphicsItemEdge(edge); edge->setGraphicsItemEdge(graphicsItemEdge); @@ -2029,19 +2081,20 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) //vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); //vmdelet_out << "before add hiC into GraphicsItemsToScene. m_hiCDeBruijnGraphEdges len = " << m_hiCDeBruijnGraphEdges.size() << '\n'; //vmdelet_out.close(); - - QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); - while (j_hiC.hasNext()) - { - j_hiC.next(); - DeBruijnEdge* edge = j_hiC.value(); - - if (edge->isDrawn()) + if (!m_hiCDeBruijnGraphEdges.empty()) { + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) { - GraphicsItemEdge* graphicsItemEdge = new GraphicsItemEdge(edge); - edge->setGraphicsItemEdge(graphicsItemEdge); - graphicsItemEdge->setFlag(QGraphicsItem::ItemIsSelectable); - scene->addItem(graphicsItemEdge); + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + edge->setDrawn(m_hiC->isDrawn(edge)); + if (edge->isDrawn()) + { + GraphicsItemEdge* graphicsItemEdge = new GraphicsItemEdge(edge); + edge->setGraphicsItemEdge(graphicsItemEdge); + graphicsItemEdge->setFlag(QGraphicsItem::ItemIsSelectable); + scene->addItem(graphicsItemEdge); + } } } @@ -2061,7 +2114,7 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) std::vector AssemblyGraph::getStartingNodes(QString * errorTitle, QString * errorMessage, bool doubleMode, - QString nodesList, QString blastQueryName) + QString nodesList, QString blastQueryName) { std::vector startingNodes; @@ -2071,15 +2124,15 @@ std::vector AssemblyGraph::getStartingNodes(QString * errorTitle { *errorTitle = "No starting nodes"; *errorMessage = "Please enter at least one node when drawing the graph using the 'Around node(s)' scope. " - "Separate multiple nodes with commas."; + "Separate multiple nodes with commas."; return startingNodes; } //Make sure the nodes the user typed in are actually in the graph. std::vector nodesNotInGraph; std::vector nodesInGraph = getNodesFromString(nodesList, - g_settings->startingNodesExactMatch, - &nodesNotInGraph); + g_settings->startingNodesExactMatch, + &nodesNotInGraph); if (nodesNotInGraph.size() > 0) { *errorTitle = "Nodes not found"; @@ -2111,7 +2164,7 @@ std::vector AssemblyGraph::getStartingNodes(QString * errorTitle } std::vector startingNodes = getNodesInDepthRange(g_settings->minDepthRange, - g_settings->maxDepthRange); + g_settings->maxDepthRange); if (startingNodes.size() == 0) { @@ -2130,7 +2183,7 @@ std::vector AssemblyGraph::getStartingNodes(QString * errorTitle startingNodes = getNodesFromBlastHits(blastQueryName); else if (g_settings->graphScope == DEPTH_RANGE) startingNodes = getNodesInDepthRange(g_settings->minDepthRange, - g_settings->maxDepthRange); + g_settings->maxDepthRange); return startingNodes; } @@ -2181,7 +2234,7 @@ std::vector AssemblyGraph::getNodesFromString(QString nodeNamesS //those names exactly. The last +/- on the end of the node name is optional - if missing //both + and - nodes will be returned. std::vector AssemblyGraph::getNodesFromListExact(QStringList nodesList, - std::vector * nodesNotInGraph) + std::vector * nodesNotInGraph) { std::vector returnVector; @@ -2230,7 +2283,7 @@ std::vector AssemblyGraph::getNodesFromListExact(QStringList nod } std::vector AssemblyGraph::getNodesFromListPartial(QStringList nodesList, - std::vector * nodesNotInGraph) + std::vector * nodesNotInGraph) { std::vector returnVector; @@ -2293,7 +2346,7 @@ std::vector AssemblyGraph::getNodesFromBlastHits(QString queryNa } std::vector AssemblyGraph::getNodesInDepthRange(double min, - double max) + double max) { std::vector returnVector; @@ -2329,9 +2382,9 @@ void AssemblyGraph::layoutGraph() { ogdf::FMMMLayout fmmm; GraphLayoutWorker * graphLayoutWorker = new GraphLayoutWorker(&fmmm, m_graphAttributes, m_edgeArray, - g_settings->graphLayoutQuality, - useLinearLayout(), - g_settings->componentSeparation); + g_settings->graphLayoutQuality, + useLinearLayout(), + g_settings->componentSeparation); graphLayoutWorker->layoutGraph(); } @@ -2688,7 +2741,7 @@ void AssemblyGraph::deleteEdges(std::vector * edges) DeBruijnNode * startingNode = edge->getStartingNode(); DeBruijnNode * endingNode = edge->getEndingNode(); m_deBruijnGraphEdges.remove(QPair(startingNode, endingNode)); - //m_hiCDeBruijnGraphEdges.remove(QPair(startingNode, endingNode)); + m_hiCDeBruijnGraphEdges.remove(QPair(startingNode, endingNode)); startingNode->removeEdge(edge); endingNode->removeEdge(edge); @@ -2735,7 +2788,7 @@ void AssemblyGraph::duplicateNodePair(DeBruijnNode * node, MyGraphicsScene * sce DeBruijnEdge * edge = leavingEdges[i]; DeBruijnNode * downstreamNode = edge->getEndingNode(); createDeBruijnEdge(newPosNodeName, downstreamNode->getName(), - edge->getOverlap(), edge->getOverlapType()); + edge->getOverlap(), edge->getOverlapType()); } std::vector enteringEdges = originalPosNode->getEnteringEdges(); @@ -2744,7 +2797,7 @@ void AssemblyGraph::duplicateNodePair(DeBruijnNode * node, MyGraphicsScene * sce DeBruijnEdge * edge = enteringEdges[i]; DeBruijnNode * upstreamNode = edge->getStartingNode(); createDeBruijnEdge(upstreamNode->getName(), newPosNodeName, - edge->getOverlap(), edge->getOverlapType()); + edge->getOverlap(), edge->getOverlapType()); } originalPosNode->setDepth(newDepth); @@ -2824,7 +2877,7 @@ void AssemblyGraph::duplicateGraphicsNode(DeBruijnNode * originalNode, DeBruijnN //merged if they are in a simple, unbranching path with no extra edges. If the //merge is successful, it returns true, otherwise false. bool AssemblyGraph::mergeNodes(QList nodes, MyGraphicsScene * scene, - bool recalulateDepth) + bool recalulateDepth) { if (nodes.size() == 0) return true; @@ -2926,7 +2979,7 @@ bool AssemblyGraph::mergeNodes(QList nodes, MyGraphicsScene * sc { DeBruijnEdge * leavingEdge = leavingEdges[i]; createDeBruijnEdge(newPosNodeName, leavingEdge->getEndingNode()->getName(), leavingEdge->getOverlap(), - leavingEdge->getOverlapType()); + leavingEdge->getOverlapType()); } std::vector enteringEdges = orderedList.front()->getEnteringEdges(); @@ -2934,7 +2987,7 @@ bool AssemblyGraph::mergeNodes(QList nodes, MyGraphicsScene * sc { DeBruijnEdge * enteringEdge = enteringEdges[i]; createDeBruijnEdge(enteringEdge->getStartingNode()->getName(), newPosNodeName, enteringEdge->getOverlap(), - enteringEdge->getOverlapType()); + enteringEdge->getOverlapType()); } if (recalulateDepth) @@ -2967,28 +3020,28 @@ bool AssemblyGraph::mergeNodes(QList nodes, MyGraphicsScene * sc bool AssemblyGraph::canAddNodeToStartOfMergeList(QList * mergeList, - DeBruijnNode * potentialNode) + DeBruijnNode * potentialNode) { DeBruijnNode * firstNode = mergeList->front(); std::vector edgesEnteringFirstNode = firstNode->getEnteringEdges(); std::vector edgesLeavingPotentialNode = potentialNode->getLeavingEdges(); return (edgesEnteringFirstNode.size() == 1 && - edgesLeavingPotentialNode.size() == 1 && - edgesEnteringFirstNode[0]->getStartingNode() == potentialNode && - edgesLeavingPotentialNode[0]->getEndingNode() == firstNode); + edgesLeavingPotentialNode.size() == 1 && + edgesEnteringFirstNode[0]->getStartingNode() == potentialNode && + edgesLeavingPotentialNode[0]->getEndingNode() == firstNode); } bool AssemblyGraph::canAddNodeToEndOfMergeList(QList * mergeList, - DeBruijnNode * potentialNode) + DeBruijnNode * potentialNode) { DeBruijnNode * lastNode = mergeList->back(); std::vector edgesLeavingLastNode = lastNode->getLeavingEdges(); std::vector edgesEnteringPotentialNode = potentialNode->getEnteringEdges(); return (edgesLeavingLastNode.size() == 1 && - edgesEnteringPotentialNode.size() == 1 && - edgesLeavingLastNode[0]->getEndingNode() == potentialNode && - edgesEnteringPotentialNode[0]->getStartingNode() == lastNode); + edgesEnteringPotentialNode.size() == 1 && + edgesLeavingLastNode[0]->getEndingNode() == potentialNode && + edgesEnteringPotentialNode[0]->getStartingNode() == lastNode); } @@ -3013,9 +3066,9 @@ QString AssemblyGraph::getUniqueNodeName(QString baseName) void AssemblyGraph::mergeGraphicsNodes(QList * originalNodes, - QList * revCompOriginalNodes, - DeBruijnNode * newNode, - MyGraphicsScene * scene) + QList * revCompOriginalNodes, + DeBruijnNode * newNode, + MyGraphicsScene * scene) { bool success = mergeGraphicsNodes2(originalNodes, newNode, scene); if (success) @@ -3036,8 +3089,8 @@ void AssemblyGraph::mergeGraphicsNodes(QList * originalNodes, bool AssemblyGraph::mergeGraphicsNodes2(QList * originalNodes, - DeBruijnNode * newNode, - MyGraphicsScene * scene) + DeBruijnNode * newNode, + MyGraphicsScene * scene) { bool success = true; std::vector linePoints; @@ -3108,8 +3161,8 @@ bool AssemblyGraph::mergeGraphicsNodes2(QList * originalNodes, //If reverseComplement is true, this function will also remove the graphics items for reverse complements of the nodes. void AssemblyGraph::removeGraphicsItemNodes(const std::vector * nodes, - bool reverseComplement, - MyGraphicsScene * scene) + bool reverseComplement, + MyGraphicsScene * scene) { QSet graphicsItemNodesToDelete; for (size_t i = 0; i < nodes->size(); ++i) @@ -3151,15 +3204,15 @@ void AssemblyGraph::removeGraphicsItemNodes(const std::vector * void AssemblyGraph::removeAllGraphicsEdgesFromNode(DeBruijnNode * node, bool reverseComplement, - MyGraphicsScene * scene) + MyGraphicsScene * scene) { const std::vector * edges = node->getEdgesPointer(); removeGraphicsItemEdges(edges, reverseComplement, scene); } void AssemblyGraph::removeGraphicsItemEdges(const std::vector * edges, - bool reverseComplement, - MyGraphicsScene * scene) + bool reverseComplement, + MyGraphicsScene * scene) { QSet graphicsItemEdgesToDelete; for (size_t i = 0; i < edges->size(); ++i) @@ -3204,7 +3257,7 @@ void AssemblyGraph::removeGraphicsItemEdges(const std::vector * //It gets a pointer to the progress dialog as well so it can check to see if the //user has cancelled the merge. int AssemblyGraph::mergeAllPossible(MyGraphicsScene * scene, - MyProgressDialog * progressDialog) + MyProgressDialog * progressDialog) { //Create a set of all nodes. QSet uncheckedNodes; @@ -3246,9 +3299,9 @@ int AssemblyGraph::mergeAllPossible(MyGraphicsScene * scene, DeBruijnNode * potentialNode = potentialEdge->getEndingNode(); std::vector edgesEnteringPotentialNode = potentialNode->getEnteringEdges(); if (edgesEnteringPotentialNode.size() == 1 && - edgesEnteringPotentialNode[0] == potentialEdge && - !nodesToMerge.contains(potentialNode) && - uncheckedNodes.contains(potentialNode)) + edgesEnteringPotentialNode[0] == potentialEdge && + !nodesToMerge.contains(potentialNode) && + uncheckedNodes.contains(potentialNode)) { nodesToMerge.push_back(potentialNode); uncheckedNodes.remove(potentialNode); @@ -3270,9 +3323,9 @@ int AssemblyGraph::mergeAllPossible(MyGraphicsScene * scene, DeBruijnNode * potentialNode = potentialEdge->getStartingNode(); std::vector edgesLeavingPotentialNode = potentialNode->getLeavingEdges(); if (edgesLeavingPotentialNode.size() == 1 && - edgesLeavingPotentialNode[0] == potentialEdge && - !nodesToMerge.contains(potentialNode) && - uncheckedNodes.contains(potentialNode)) + edgesLeavingPotentialNode[0] == potentialEdge && + !nodesToMerge.contains(potentialNode) && + uncheckedNodes.contains(potentialNode)) { nodesToMerge.push_front(potentialNode); uncheckedNodes.remove(potentialNode); @@ -3406,8 +3459,8 @@ bool AssemblyGraph::saveVisibleGraphToGfa(QString filename) j.next(); DeBruijnEdge * edge = j.value(); if (edge->getStartingNode()->thisNodeOrReverseComplementIsDrawn() && - edge->getEndingNode()->thisNodeOrReverseComplementIsDrawn() && - edge->isPositiveEdge()) + edge->getEndingNode()->thisNodeOrReverseComplementIsDrawn() && + edge->isPositiveEdge()) edgesToSave.push_back(edge); } @@ -3491,7 +3544,7 @@ NodeNameStatus AssemblyGraph::checkNodeNameValidity(QString nodeName) void AssemblyGraph::changeNodeDepth(std::vector * nodes, - double newDepth) + double newDepth) { if (nodes->size() == 0) return; @@ -3515,7 +3568,7 @@ void AssemblyGraph::changeNodeDepth(std::vector * nodes, //uses. //The returned string always ends in a newline. QByteArray AssemblyGraph::addNewlinesToSequence(QByteArray sequence, - int interval) + int interval) { QByteArray output; @@ -3592,7 +3645,7 @@ void AssemblyGraph::getNodeStats(int * n50, int * shortestNode, int * firstQuart double halfTotalLength = m_totalLength / 2.0; long long totalSoFar = 0; - for (int i = int(nodeLengths.size()) - 1; i >= 0 ; --i) + for (int i = int(nodeLengths.size()) - 1; i >= 0; --i) { totalSoFar += nodeLengths[i]; if (totalSoFar >= halfTotalLength) @@ -3613,7 +3666,7 @@ void AssemblyGraph::getGraphComponentCountAndLargestComponentSize(int * componen QSet visitedNodes; QList< QList > connectedComponents; - + //Loop through all positive nodes. QMapIterator i(m_deBruijnGraphNodes); while (i.hasNext()) @@ -3622,12 +3675,12 @@ void AssemblyGraph::getGraphComponentCountAndLargestComponentSize(int * componen DeBruijnNode * v = i.value(); if (v->isNegativeNode()) continue; - + //If the node has not yet been visited, then it must be the start of a new connected component. if (!visitedNodes.contains(v)) { QList connectedComponent; - + QQueue q; q.enqueue(v); visitedNodes.insert(v); @@ -3650,9 +3703,9 @@ void AssemblyGraph::getGraphComponentCountAndLargestComponentSize(int * componen } connectedComponents.push_back(connectedComponent); - } + } } - + //Now that the list of connected components is built, we look for the //largest one (as measured by total node length). *componentCount = connectedComponents.size(); diff --git a/graph/assemblygraph.h b/graph/assemblygraph.h index bc28ac14..7a021511 100644 --- a/graph/assemblygraph.h +++ b/graph/assemblygraph.h @@ -72,7 +72,7 @@ class AssemblyGraph : public QObject QString m_filename; QString m_depthTag; SequencesLoadedFromFasta m_sequencesLoadedFromFasta; - HiCSettings hiC; + HiCSettings * m_hiC; void cleanUp(); void createDeBruijnEdge(QString node1Name, QString node2Name, @@ -172,7 +172,8 @@ class AssemblyGraph : public QObject long long getTotalLengthOrphanedNodes() const; bool useLinearLayout() const; bool AssemblyGraph::loadHiC(QString filename, QString* errormsg); - void AssemblyGraph::loadHiCSettings(); + void AssemblyGraph::buildOgdfGraphFromNodesAndEdgesWithHiC(std::vector startingNodes, int nodeDistance); + void setHiCFilter(int filterHiC) { m_hiC->filterHiC = filterHiC; } private: @@ -216,6 +217,9 @@ class AssemblyGraph : public QObject QList AssemblyGraph::dfs(DeBruijnNode* curNode, QByteArray hiC); QList AssemblyGraph::getNewStartIndexes(QByteArray wgs, QByteArray hiC); bool AssemblyGraph::isBeginWith(QByteArray wgs, QByteArray hiC); + void AssemblyGraph::bfs(DeBruijnNode* node, int componentId); + void AssemblyGraph::findComponents(); + void AssemblyGraph::addHiCEdges(std::vector startingNodes); signals: void setMergeTotalCount(int totalCount); diff --git a/graph/debruijnedge.cpp b/graph/debruijnedge.cpp index a679ed9d..937816f4 100644 --- a/graph/debruijnedge.cpp +++ b/graph/debruijnedge.cpp @@ -96,25 +96,41 @@ bool DeBruijnEdge::isPositiveEdge() const } -void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray) const +void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray, int maxHiCweight) const { ogdf::node firstEdgeOgdfNode; ogdf::node secondEdgeOgdfNode; - if (m_startingNode->inOgdf()) - firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getLast(); - else if (m_startingNode->getReverseComplement()->inOgdf()) - firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getFirst(); - else - return; //Ending node or its reverse complement isn't in OGDF + if (!isHiC()) { + if (m_startingNode->inOgdf()) + firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getLast(); + else if (m_startingNode->getReverseComplement()->inOgdf()) + firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getFirst(); + else + return; //Ending node or its reverse complement isn't in OGDF - if (m_endingNode->inOgdf()) - secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getFirst(); - else if (m_endingNode->getReverseComplement()->inOgdf()) - secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getLast(); - else - return; //Ending node or its reverse complement isn't in OGDF + if (m_endingNode->inOgdf()) + secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getFirst(); + else if (m_endingNode->getReverseComplement()->inOgdf()) + secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getLast(); + else + return; //Ending node or its reverse complement isn't in OGDF + } + else { + if (m_startingNode->inOgdf()) + firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getMiddle(); + else if (m_startingNode->getReverseComplement()->inOgdf()) + firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getMiddle(); + else + return; //Ending node or its reverse complement isn't in OGDF + if (m_endingNode->inOgdf()) + secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getMiddle(); + else if (m_endingNode->getReverseComplement()->inOgdf()) + secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getMiddle(); + else + return; //Ending node or its reverse complement isn't in OGDF + } //If this in an edge connected a single-segment node to itself, then we //don't want to put it in the OGDF graph, because it would be redundant //with the node segment (and created conflict with the node/edge length). @@ -125,45 +141,14 @@ void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArraynewEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); - (*edgeArray)[newEdge] = g_settings->edgeLength; + if (!isHiC()) { + (*edgeArray)[newEdge] = g_settings->edgeLength; + } + else { + (*edgeArray)[newEdge] = (g_settings->edgeLength) * 25; + } } - -//void DeBruijnEdge::addHiCToOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::EdgeArray* edgeArray) const -//{ -// ogdf::node firstEdgeOgdfNode; -// ogdf::node secondEdgeOgdfNode; -// if (m_startingNode->inOgdf()) -// firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getMiddle(); -// else if (m_startingNode->getReverseComplement()->inOgdf()) -// firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getMiddle(); -// else -// return; //Ending node or its reverse complement isn't in OGDF -// -// if (m_endingNode->inOgdf()) -// secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getMiddle(); -// else if (m_endingNode->getReverseComplement()->inOgdf()) -// secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getMiddle(); -// else -// return; //Ending node or its reverse complement isn't in OGDF -// -// //If this in an edge connected a single-segment node to itself, then we -// //don't want to put it in the OGDF graph, because it would be redundant -// //with the node segment (and created conflict with the node/edge length). -// if (m_startingNode == m_endingNode) -// { -// if (m_startingNode->getNumberOfOgdfGraphEdges(m_startingNode->getDrawnNodeLength()) == 1) -// return; -// } -// -// ogdf::edge newEdge = ogdfGraph->newEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); -// (*edgeArray)[newEdge] = g_settings->edgeLength; -//} - - - - - //This function traces all possible paths from this edge. //It proceeds a number of steps, as determined by a setting. //If forward is true, it looks in a forward direction (starting nodes to diff --git a/graph/debruijnedge.h b/graph/debruijnedge.h index 7dbe7eaa..71184263 100644 --- a/graph/debruijnedge.h +++ b/graph/debruijnedge.h @@ -59,7 +59,6 @@ class DeBruijnEdge bool isOwnReverseComplement() const {return this == getReverseComplement();} static bool compareEdgePointers(DeBruijnEdge * a, DeBruijnEdge * b); - //MODIFERS void setGraphicsItemEdge(GraphicsItemEdge * gie) {m_graphicsItemEdge = gie;} void setReverseComplement(DeBruijnEdge * rc) {m_reverseComplement = rc;} @@ -70,8 +69,7 @@ class DeBruijnEdge void setDrawn(bool isDrawn) { m_drawn = isDrawn; } void setExactOverlap(int overlap) {m_overlap = overlap; m_overlapType = EXACT_OVERLAP;} void autoDetermineExactOverlap(); - void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray) const; - void addHiCToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray* edgeArray) const; + void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray, int maxHiCweight) const; void setHiC(bool hiC, int weight) { m_HiC = hiC; m_weight = weight; } private: diff --git a/graph/debruijnnode.cpp b/graph/debruijnnode.cpp index 5da476ee..88224e0a 100644 --- a/graph/debruijnnode.cpp +++ b/graph/debruijnnode.cpp @@ -89,6 +89,54 @@ void DeBruijnNode::resetNode() m_highestDistanceInNeighbourSearch = 0; } +//void DeBruijnNode::addToSimpleOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::GraphAttributes* graphAttributes, double xPos, double yPos) { +// if (thisOrReverseComplementInOgdf()) +// return; +// m_ogdfNode = new OgdfNode(); +// ogdf::node newNode = ogdfGraph->newNode(); +// m_ogdfNode->addOgdfNode(newNode); +//} + +void DeBruijnNode::addToSimpleOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::GraphAttributes* graphAttributes, double xPos, double yPos) +{ + //If this node or its reverse complement is already in OGDF, then + //it's not necessary to make the node. + if (thisOrReverseComplementInOgdf()) + return; + + //Create the OgdfNode object + m_ogdfNode = new OgdfNode(); + + //Each node in the Velvet sense is made up of multiple nodes in the + //OGDF sense. This way, Velvet nodes appear as lines whose length + //corresponds to the sequence length. + double drawnNodeLength = g_settings->minimumNodeLength; + int numberOfGraphEdges = getNumberOfOgdfGraphEdges(drawnNodeLength); + int numberOfGraphNodes = numberOfGraphEdges + 1; + double drawnLengthPerEdge = drawnNodeLength / numberOfGraphEdges; + + ogdf::node newNode = 0; + ogdf::node previousNode = 0; + for (int i = 0; i < numberOfGraphNodes; ++i) + { + newNode = ogdfGraph->newNode(); + m_ogdfNode->addOgdfNode(newNode); + + if (g_assemblyGraph->useLinearLayout()) { + graphAttributes->x(newNode) = xPos; + graphAttributes->y(newNode) = yPos; + xPos += g_settings->nodeSegmentLength; + } + + if (i > 0) + { + ogdf::edge newEdge = ogdfGraph->newEdge(previousNode, newNode); + //(*edgeArray)[newEdge] = drawnLengthPerEdge; + } + + previousNode = newNode; + } +} void DeBruijnNode::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes * graphAttributes, ogdf::EdgeArray * edgeArray, double xPos, double yPos) diff --git a/graph/debruijnnode.h b/graph/debruijnnode.h index 7d3b7e13..768a3f1c 100644 --- a/graph/debruijnnode.h +++ b/graph/debruijnnode.h @@ -98,6 +98,7 @@ class DeBruijnNode int getDeadEndCount() const; int getNumberOfOgdfGraphEdges(double drawnNodeLength) const; double getDrawnNodeLength() const; + int getComponentId() {return m_componentId;}; //MODIFERS void setDepthRelativeToMeanDrawnDepth(double newVal) {m_depthRelativeToMeanDrawnDepth = newVal;} @@ -118,6 +119,7 @@ class DeBruijnNode void removeEdge(DeBruijnEdge * edge); void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes * graphAttributes, ogdf::EdgeArray * edgeArray, double xPos, double yPos); + void addToSimpleOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::GraphAttributes* graphAttributes, double xPos, double yPos); void determineContiguity(); void clearBlastHits() {m_blastHits.clear();} void addBlastHit(BlastHit * newHit) {m_blastHits.push_back(newHit);} @@ -126,6 +128,7 @@ class DeBruijnNode void clearCsvData() {m_csvData.clear();} void setDepth(double newDepth) {m_depth = newDepth;} void setName(QString newName) {m_name = newName;} + void setComponentId(int componentId) {m_componentId = componentId;} private: QString m_name; @@ -147,6 +150,7 @@ class DeBruijnNode QStringList m_csvData; QString getNodeNameForFasta(bool sign) const; QByteArray getUpstreamSequence(int upstreamSequenceLength) const; + int m_componentId = 0; double getNodeLengthPerMegabase() const; bool isOnlyPathInItsDirection(DeBruijnNode * connectedNode, diff --git a/graph/graphicsitemedge.cpp b/graph/graphicsitemedge.cpp index aeef5515..aa15d3a2 100644 --- a/graph/graphicsitemedge.cpp +++ b/graph/graphicsitemedge.cpp @@ -56,8 +56,8 @@ void GraphicsItemEdge::paint(QPainter * painter, const QStyleOptionGraphicsItem penColour = g_settings->edgeColour; Qt::PenStyle s = Qt::SolidLine; if (m_deBruijnEdge->isHiC()) { - int dark = 255 - (m_deBruijnEdge->getWeight())*20; - penColour.setRgb(dark, dark, dark); + //int dark = 255 - (m_deBruijnEdge->getWeight())*20; + penColour.setRgb(0, 0, 0); s = Qt::DotLine; } QPen edgePen(QBrush(penColour), edgeWidth, s, Qt::RoundCap); @@ -134,10 +134,11 @@ void GraphicsItemEdge::setControlPointLocations() if (startingNode->hasGraphicsItem()) { - if (m_deBruijnEdge->isHiC() && startingNode->getGraphicsItemNode() -> isBig()) { + if (m_deBruijnEdge->isHiC() && startingNode->getGraphicsItemNode()->isBig()) { m_startingLocation = startingNode->getGraphicsItemNode()->getMiddle(); m_beforeStartingLocation = startingNode->getGraphicsItemNode()->getBeforeMiddle(); - } else { + } + else { m_startingLocation = startingNode->getGraphicsItemNode()->getLast(); m_beforeStartingLocation = startingNode->getGraphicsItemNode()->getSecondLast(); } @@ -170,10 +171,6 @@ void GraphicsItemEdge::setControlPointLocations() if (m_deBruijnEdge->isHiC() && endingNode->getReverseComplement()->getGraphicsItemNode() -> isBig()) { m_endingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getMiddle(); m_afterEndingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getBeforeMiddle(); - std::ofstream vmdelet_out; - vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); - vmdelet_out << "setControlPointLocations endingNode" << endingNode->getName().toUtf8().constData() << '\n'; - vmdelet_out.close(); } else { m_endingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getLast(); diff --git a/graph/graphicsitemnode.cpp b/graph/graphicsitemnode.cpp index d3486bdd..400276ef 100644 --- a/graph/graphicsitemnode.cpp +++ b/graph/graphicsitemnode.cpp @@ -52,6 +52,7 @@ GraphicsItemNode::GraphicsItemNode(DeBruijnNode * deBruijnNode, { setWidth(); + //m_width = g_settings->averageNodeWidth; OgdfNode * pathOgdfNode = deBruijnNode->getOgdfNode(); if (pathOgdfNode != 0) diff --git a/graph/graphicsitemnode.h b/graph/graphicsitemnode.h index 95ba14f4..3d1720d4 100644 --- a/graph/graphicsitemnode.h +++ b/graph/graphicsitemnode.h @@ -65,10 +65,18 @@ class GraphicsItemNode : public QGraphicsItem double distance(QPointF p1, QPointF p2) const; bool usePositiveNodeColour(); QPointF getFirst() const {return m_linePoints[0];} - QPointF getSecond() const {return m_linePoints[1];} + QPointF getSecond() const { + if (m_linePoints.size() > 1) return m_linePoints[1]; + else return m_linePoints[0]; + } QPointF getLast() const {return m_linePoints[m_linePoints.size()-1];} - QPointF getSecondLast() const {return m_linePoints[m_linePoints.size()-2];} + QPointF getSecondLast() const + { + if (m_linePoints.size() > 1) return m_linePoints[m_linePoints.size() - 2]; + else return m_linePoints[0]; + } bool isBig() const { return m_linePoints.size() >= 3; } + bool isOne() const { return m_linePoints.size() == 1; } QPointF getMiddle() const { return m_linePoints[m_linePoints.size() / 2]; } QPointF getBeforeMiddle() const { @@ -81,8 +89,10 @@ class GraphicsItemNode : public QGraphicsItem { if (m_linePoints.size() >= 3) return m_linePoints[(m_linePoints.size() / 2) + 1]; - else + else if (m_linePoints.size() > 1) return m_linePoints[1]; + else + return m_linePoints[0]; } std::vector getCentres() const; QPointF getCentre(std::vector linePoints) const; diff --git a/program/graphlayoutworker.cpp b/program/graphlayoutworker.cpp index 8f755148..3d642201 100644 --- a/program/graphlayoutworker.cpp +++ b/program/graphlayoutworker.cpp @@ -21,6 +21,15 @@ #include "ogdf/basic/geometry.h" #include +//#include +//#include +//#include +//#include +//#include +//#include + +using namespace ogdf; + GraphLayoutWorker::GraphLayoutWorker(ogdf::FMMMLayout * fmmm, ogdf::GraphAttributes * graphAttributes, ogdf::EdgeArray * edgeArray, int graphLayoutQuality, bool linearLayout, double graphLayoutComponentSeparation, double aspectRatio) : @@ -30,13 +39,16 @@ GraphLayoutWorker::GraphLayoutWorker(ogdf::FMMMLayout * fmmm, ogdf::GraphAttribu { } - void GraphLayoutWorker::layoutGraph() { m_fmmm->randSeed(clock()); m_fmmm->useHighLevelOptions(false); m_fmmm->initialPlacementForces(ogdf::FMMMLayout::ipfRandomRandIterNr); - m_fmmm->unitEdgeLength(1.0); + if (m_edgeArray != NULL) + m_fmmm->unitEdgeLength(1.0); + else { + m_fmmm->unitEdgeLength(15.0); + } m_fmmm->allowedPositions(ogdf::FMMMLayout::apAll); m_fmmm->pageRatio(m_aspectRatio); m_fmmm->minDistCC(m_graphLayoutComponentSeparation); @@ -75,9 +87,8 @@ void GraphLayoutWorker::layoutGraph() m_fmmm->nmPrecision(8); break; } - + m_fmmm->call(*m_graphAttributes, *m_edgeArray); - //m_fmmm->call(*m_graphAttributes); emit finishedLayout(); } diff --git a/program/graphlayoutworker.h b/program/graphlayoutworker.h index cc808a80..19415c74 100644 --- a/program/graphlayoutworker.h +++ b/program/graphlayoutworker.h @@ -24,6 +24,7 @@ #include "../ogdf/basic/GraphAttributes.h" + class GraphLayoutWorker : public QObject { Q_OBJECT diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 5a24144e..348d1eba 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -768,12 +768,22 @@ void MainWindow::setHiCWidgetVisibility(bool visible) void MainWindow::drawGraph() { + /* + * 0 - HiC не влияет на укладку + * 1 - все HiC влияют на укладку, фиксированная длина ребер + */ + int type = 1; + float percent = 0.9; QString errorTitle; + //0 - no filter + //1 - all edges between components + //2 - one edge between component QString errorMessage; - std::vector startingNodes = g_assemblyGraph->getStartingNodes(&errorTitle, &errorMessage, - ui->doubleNodesRadioButton->isChecked(), - ui->startingNodesLineEdit->text(), - ui->blastQueryComboBox->currentText()); + int filterHiC = 0; + std::vector startingNodes = g_assemblyGraph->getStartingNodes(&errorTitle, &errorMessage, + ui->doubleNodesRadioButton->isChecked(), + ui->startingNodesLineEdit->text(), + ui->blastQueryComboBox->currentText()); if (errorMessage != "") { @@ -781,12 +791,19 @@ void MainWindow::drawGraph() return; } - g_assemblyGraph->hiC.minWeight = ui->hicWeightSpinBox->value(); - g_assemblyGraph->hiC.minLength = ui->hicSeqLenSpinBox->value(); + g_assemblyGraph->m_hiC->minWeight = ui->hicWeightSpinBox->value(); + g_assemblyGraph->m_hiC->minLength = ui->hicSeqLenSpinBox->value(); + g_assemblyGraph->setHiCFilter(filterHiC); resetScene(); - g_assemblyGraph->buildOgdfGraphFromNodesAndEdges(startingNodes, g_settings->nodeDistance); - layoutGraph(); + if (type == 0) { + g_assemblyGraph->buildOgdfGraphFromNodesAndEdges(startingNodes, g_settings->nodeDistance); + layoutGraph(); + } + if (type == 1) { + g_assemblyGraph->buildOgdfGraphFromNodesAndEdgesWithHiC(startingNodes, g_settings->nodeDistance); + layoutGraph(); + } } @@ -838,9 +855,6 @@ std::vector MainWindow::getNodesFromLineEdit(QLineEdit * lineEdi return g_assemblyGraph->getNodesFromString(lineEdit->text(), exactMatch, nodesNotInGraph); } - - - void MainWindow::layoutGraph() { //The actual layout is done in a different thread so the UI will stay responsive. @@ -879,9 +893,6 @@ void MainWindow::layoutGraph() m_layoutThread->start(); } - - - void MainWindow::zoomSpinBoxChanged() { double newValue = ui->zoomSpinBox->value(); diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 08d88101..145d8bc9 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -68,6 +68,11 @@ class MainWindow : public QMainWindow void clearGraphDetails(); void resetScene(); void layoutGraph(); + void layoutHiCGraph(); + void layoutPlanarGraph(); + void layoutGraphWithoutFinishing(); + void layoutGraphWithKeepPosition(); + void addGraphicsItemsToScene(); void zoomToFitRect(QRectF rect); void zoomToFitScene(); From b89c873f6ab5e481f3508a965539a31f8d84df6c Mon Sep 17 00:00:00 2001 From: Shostina Date: Tue, 19 Apr 2022 21:16:46 +0300 Subject: [PATCH 05/11] add new hi-c visualisation modes --- HiCSettings.cpp | 50 - HiCSettings.h | 42 - build_scripts/bandage_build_windows.bat | 22 +- graph/assemblygraph.cpp | 168 ++- graph/assemblygraph.h | 21 +- graph/debruijnedge.cpp | 53 +- graph/debruijnedge.h | 2 +- graph/debruijnnode.cpp | 8 +- graph/debruijnnode.h | 3 +- graph/graphicsitemedge.cpp | 12 +- graph/graphicsitemnode.cpp | 73 +- graph/graphicsitemnode.h | 6 +- graph/ogdfnode.h | 29 + program/HiCSettings.cpp | 123 ++ program/HiCSettings.h | 65 + program/globals.cpp | 1 + program/globals.h | 6 +- program/graphlayoutworker.cpp | 1 - program/main.cpp | 1 + program/settings.cpp | 3 + program/settings.h | 8 + ui/mainwindow.cpp | 54 +- ui/mainwindow.h | 9 +- ui/mainwindow.ui | 1645 ++++++++++++----------- ui/settingsdialog.cpp | 58 + ui/settingsdialog.ui | 205 ++- 26 files changed, 1651 insertions(+), 1017 deletions(-) delete mode 100644 HiCSettings.cpp delete mode 100644 HiCSettings.h create mode 100644 program/HiCSettings.cpp create mode 100644 program/HiCSettings.h diff --git a/HiCSettings.cpp b/HiCSettings.cpp deleted file mode 100644 index 40b7a0d9..00000000 --- a/HiCSettings.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "HiCSettings.h" -#include "../graph/debruijnnode.h" -#include - -HiCSettings::HiCSettings() : minWeight(0), minLength(1), minDist(1) {} -HiCSettings::~HiCSettings() {} - -bool HiCSettings::isDrawn(DeBruijnEdge* edge) { - edge->determineIfDrawn(); - bool drawEdge = edge->isDrawn(); - if (!drawEdge) - return false; - bool res(edge->getWeight() >= minWeight && - edge->getStartingNode()->getLength() >= minLength && - edge->getEndingNode()->getLength() >= minLength && - (filterHiC == 0 || - (filterHiC == 1 && edge->getStartingNode()->getComponentId() != edge->getEndingNode()->getComponentId()) || - (filterHiC == 2 && contains(edge))) - ); - return res; - -} - -bool HiCSettings::addEdgeIfNeeded(DeBruijnEdge* edge) { - if (edge->getStartingNode()->getComponentId() != edge->getEndingNode()->getComponentId() && edge->isPositiveEdge()) { - QPair key = qMakePair(std::min(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId()), - std::max(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId())); - if (!componentEdgeMap.contains(key)) { - componentEdgeMap[key] = edge; - return true; - } - if (componentEdgeMap[key]->getStartingNode() == edge->getStartingNode()->getReverseComplement() && - componentEdgeMap[key]->getEndingNode() == edge->getEndingNode()->getReverseComplement() || - componentEdgeMap[key]->getStartingNode() == edge->getEndingNode()->getReverseComplement() && - componentEdgeMap[key]->getEndingNode() == edge->getStartingNode()->getReverseComplement()) { - QPair reverseKey = qMakePair(std::max(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId()), - std::min(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId())); - componentEdgeMap[reverseKey] = edge; - } - } - return false; -} - -bool HiCSettings::contains(DeBruijnEdge* edge) { - QPair key = qMakePair(std::min(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId()), - std::max(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId())); - return componentEdgeMap.contains(key) && - componentEdgeMap[key] == edge; -} - diff --git a/HiCSettings.h b/HiCSettings.h deleted file mode 100644 index b712bb5a..00000000 --- a/HiCSettings.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef HICSETTINGS_H -#define HICSETTINGS_H - -#include -#include - -#include -#include -#include -#include - - -#include "../program/globals.h" -#include "../ui/mygraphicsscene.h" -#include "../graph/debruijnedge.h" - -class HiCSettings : public QObject -{ - Q_OBJECT - -public: - HiCSettings(); - ~HiCSettings(); - - //int numOfNodes = 0; - int minWeight = 0; - int minLength = 1; - int minDist = 1; - int maxWeight = 0; - //0 - no filter - //1 - all edges between components - //2 - one edge between component - int filterHiC = 0; - int componentNum = 0; - QMap, DeBruijnEdge*> componentEdgeMap; - bool isDrawn(DeBruijnEdge* edge); - bool HiCSettings::addEdgeIfNeeded(DeBruijnEdge* edge); - bool HiCSettings::contains(DeBruijnEdge* edge); -private: -}; -#endif //HICSETTINGS_H - diff --git a/build_scripts/bandage_build_windows.bat b/build_scripts/bandage_build_windows.bat index 631b2bc6..111e9812 100644 --- a/build_scripts/bandage_build_windows.bat +++ b/build_scripts/bandage_build_windows.bat @@ -5,16 +5,18 @@ rem Bandage. It produces a directory ready for deployment. It requires that samp rem the current directory. rem These variables must be set to the correct paths and values for your system. -set QT_PATH=C:\Qt\5.6 -set MSVC_PATH=C:\Program Files (x86)\Microsoft Visual Studio 14.0 -set MSVC_VERSION=msvc2015_64 -set MSVC_VERSION_NUM=140 -set GIT_PATH=C:\Program Files\Git\bin\git +set QT_PATH=C:\Qt3\5.12.4 +set MSVC_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community +set MSVC_VERSION=msvc2017_64 +set MSVC_VERSION_NUM=168 +set GIT_PATH=C:\Program Files\Git\bin\git.exe set ZIP_PATH=C:\Program Files\7-Zip\7z.exe rem Set up the MSVC compiler. call "%MSVC_PATH%\VC\vcvarsall.bat" x86_amd64 +pause + rem If a 'Bandage' directory already exists, then this script will use assume it is the Bandage rem source code and use it. If not, it will clone the master branch from GitHub. if not exist Bandage\ call "%GIT_PATH%" clone https://github.com/rrwick/Bandage.git @@ -23,21 +25,31 @@ call "%QT_PATH%\%MSVC_VERSION%\bin\qmake.exe" call "%QT_PATH%\..\Tools\QtCreator\bin\jom.exe" call cd .. +pause + rem Get the Bandage version number from the main.cpp file and replace its dots with underscores. for /f %%i in ('findstr setApplicationVersion Bandage\program\main.cpp') do set VERSION_LINE=%%i set VERSION_LINE=%VERSION_LINE:.=_% set VERSION_LINE=%VERSION_LINE:"=;% for /f "tokens=2 delims=;" %%G IN ("%VERSION_LINE%") DO set VERSION=%%G +pause + rem Delete all of the source and build files call move Bandage\release\Bandage.exe Bandage.exe call rmdir Bandage\ /S /Q call mkdir Bandage\ call move Bandage.exe Bandage\Bandage.exe +pause + rem Add the necessary libraries so Bandage can be deployed. call "%QT_PATH%\%MSVC_VERSION%\bin\windeployqt.exe" Bandage\Bandage.exe +pause + rem Zip Bandage with the sample graph and clean up. call "%ZIP_PATH%" a -tzip Bandage_Windows_v%VERSION%.zip Bandage\ sample_LastGraph installation.txt call rmdir Bandage\ /S /Q + +pause \ No newline at end of file diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index e86253d0..85e25797 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -17,7 +17,6 @@ #include "assemblygraph.h" -#include "HiCSettings.h" #include #include "../program/globals.h" #include "../program/settings.h" @@ -56,7 +55,6 @@ AssemblyGraph::AssemblyGraph() : m_hiCEdgeArray = new ogdf::EdgeArray(*m_ogdfGraph); m_graphAttributes = new ogdf::GraphAttributes(*m_ogdfGraph, ogdf::GraphAttributes::nodeGraphics | ogdf::GraphAttributes::edgeGraphics); - m_hiC = new HiCSettings(); clearGraphInfo(); } @@ -66,7 +64,7 @@ AssemblyGraph::~AssemblyGraph() delete m_edgeArray; delete m_hiCEdgeArray; delete m_ogdfGraph; - delete m_hiC; + g_hicSettings.reset(new HiCSettings()); } @@ -98,7 +96,7 @@ void AssemblyGraph::cleanUp() m_hiCDeBruijnGraphEdges.clear(); m_contiguitySearchDone = false; - m_hiC = new HiCSettings(); + g_hicSettings.reset(new HiCSettings()); clearGraphInfo(); } @@ -161,6 +159,7 @@ void AssemblyGraph::createDeBruijnEdge(QString node1Name, QString node2Name, m_deBruijnGraphEdges.insert(QPair(backwardEdge->getStartingNode(), backwardEdge->getEndingNode()), backwardEdge); } else { + //g_hicSettings->addToHicWeightBetweenComponentMap(forwardEdge); m_hiCDeBruijnGraphEdges.insert(QPair(forwardEdge->getStartingNode(), forwardEdge->getEndingNode()), forwardEdge); if (!isOwnPair) m_hiCDeBruijnGraphEdges.insert(QPair(backwardEdge->getStartingNode(), backwardEdge->getEndingNode()), backwardEdge); @@ -566,7 +565,6 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) bool AssemblyGraph::loadHiC(QString filename, QString* errormsg) { findComponents(); - QFile hiCMatrix(filename); if (!hiCMatrix.open(QIODevice::ReadOnly)) { @@ -592,14 +590,14 @@ bool AssemblyGraph::loadHiC(QString filename, QString* errormsg) maxWeight = weight; } } - m_hiC->maxWeight = maxWeight; + g_hicSettings->maxWeight = maxWeight; QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); while (j_hiC.hasNext()) { j_hiC.next(); DeBruijnEdge* edge = j_hiC.value(); - m_hiC->addEdgeIfNeeded(edge); + g_hicSettings->addEdgeIfNeeded(edge); } return true; } @@ -1845,28 +1843,42 @@ void AssemblyGraph::findComponents() { i.next(); DeBruijnNode * node = i.value(); if (node->getComponentId() == 0) { - bfs(node, componentId); + QPair res = dfs(node, componentId); + g_hicSettings->componentSize.append(res.second); + g_hicSettings->averageSize.append(res.second / (unsigned long)res.first); componentId++; } } - m_hiC->componentNum = componentId - 1; + g_hicSettings->componentNum = componentId - 1; } -void AssemblyGraph::bfs(DeBruijnNode * node, int componentId) { +QPair AssemblyGraph::dfs(DeBruijnNode * node, int componentId) { + unsigned long size = 0; + unsigned int contigCount = 0; if (node->getComponentId() == 0) { node->setComponentId(componentId); node->getReverseComplement()->setComponentId(componentId); + size += node->getLength(); + contigCount += 1; + if (node->getNameWithoutSign().endsWith("_start")) { + g_hicSettings->addTargetComponentIfNeeded(componentId); + } for (DeBruijnEdge* edge : node->getLeavingEdges()) { if (edge->getEndingNode()->getComponentId() == 0 && !edge->isHiC()) { - bfs(edge->getEndingNode(), componentId); + QPair res = dfs(edge->getEndingNode(), componentId); + size += res.second; + contigCount += res.first; } } for (DeBruijnEdge* edge : node->getEnteringEdges()) { if (edge->getStartingNode()->getComponentId() == 0 && !edge->isHiC()) { - bfs(edge->getStartingNode(), componentId); + QPair res = dfs(edge->getStartingNode(), componentId); + size += res.second; + contigCount += res.first; } } } + return qMakePair(contigCount, size); } void AssemblyGraph::buildOgdfGraphFromNodesAndEdgesWithHiC(std::vector startingNodes, int nodeDistance) { @@ -1892,8 +1904,9 @@ void AssemblyGraph::buildOgdfGraphFromNodesAndEdges(std::vector //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(); + if (i.value()->isPositiveNode() || g_settings->doubleMode) { + i.value()->setAsDrawn(); + } } } else //The scope is either around specified nodes, around nodes with BLAST hits or a depth range. @@ -2012,7 +2025,7 @@ void AssemblyGraph::buildOgdfGraphFromNodesAndEdges(std::vector DeBruijnEdge * edge = j.value(); edge->determineIfDrawn(); if (!edge->isHiC() && edge->isDrawn()) - edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray, m_hiC->maxWeight); + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); } } @@ -2023,14 +2036,111 @@ void AssemblyGraph::addHiCEdges(std::vector startingNodes) { { j_hiC.next(); DeBruijnEdge* edge = j_hiC.value(); - edge->setDrawn(m_hiC->isDrawn(edge)); - if (m_hiC->isDrawn(edge)) { - edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray, m_hiC->maxWeight); + edge->setDrawn(g_hicSettings->isDrawnWithNode(edge)); + if (g_hicSettings->isDrawnWithNode(edge)) { + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); } } } +void AssemblyGraph::addOneHiCBetweenComponent(std::vector startingNodes) { + //Then loop through each hic edge determining its drawn status and adding it to OGDF if it is drawn. + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + edge->setDrawn(g_hicSettings->isDrawnWithNode(edge)); + if (g_hicSettings->isDrawnWithNode(edge, ONE_BETWEEN_GRAPH_COMPONENT)) { + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); + } + } +} +void AssemblyGraph::setInclusionFilterAuto() { + int connectedwithTatgercomponentAmount = 0; + for (int componentId = 1; componentId <= g_hicSettings->componentNum; componentId++) { + if (g_hicSettings->isConnectedWithTargetComponent(componentId)) { + connectedwithTatgercomponentAmount += 1; + } + } + g_settings->hicEdgeWidth = 5; + if (connectedwithTatgercomponentAmount <= 5) { + g_hicSettings->inclusionFilter = ALL_BETWEEN_GRAPH_COMPONENTS; + } + if (connectedwithTatgercomponentAmount > 5 && connectedwithTatgercomponentAmount <= 10) { + g_hicSettings->inclusionFilter = ONE_BETWEEN_GRAPH_COMPONENT; + } + if (connectedwithTatgercomponentAmount > 10) { + g_hicSettings->inclusionFilter = ONE_FROM_TARGET_COMPONENT; + } +} + +void AssemblyGraph::buildOgdfGraphWithAutoParameters(std::vector startingNodes) +{ + + g_settings->hicDrawingType = ALL_EDGES; + g_settings->hicEdgeLength = 200; + g_settings->hicEdgeWidth = 7; + g_settings->averageNodeWidth = 12; + g_settings->nodeColourScheme = RANDOM_COMPONENT_COLOURS; + + setInclusionFilterAuto(); + + QMapIterator 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. + int nodesCount = 0; + if (i.value()->isPositiveNode() || g_settings->doubleMode) { + int componentId = i.value()->getComponentId(); + if (componentId != 0) { + if (!i.value()->isDrawn() && (g_hicSettings->isTargetComponent(i.value()->getComponentId()) || g_hicSettings->isConnectedWithTargetComponent(i.value() -> getComponentId()))) { + i.value()->setAsDrawn(); + } + } + else { + i.value()->setAsDrawn(); + } + if (i.value()->isDrawn() && i.value()->thisOrReverseComplementNotInOgdf()) { + i.value()->addToOgdfGraph(m_ogdfGraph, m_graphAttributes, m_edgeArray, 0.0, 0.0); + nodesCount += 1; + } + } + } + + int edgesCount = 0; + //Then loop through each edge determining its drawn status and adding it to OGDF if it is drawn. + QMapIterator, DeBruijnEdge*> j(m_deBruijnGraphEdges); + while (j.hasNext()) + { + j.next(); + DeBruijnEdge* edge = j.value(); + edge->determineIfDrawn(); + if (!edge->isHiC() && edge->isDrawn()) { + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); + edgesCount += 1; + } + } + + int hicEdgesCount = 0; + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + + + edge->setDrawn(g_hicSettings->isDrawnWithNode(edge)); + if (edge->isDrawn()) { + hicEdgesCount += 1; + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); + } + } +} void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) { @@ -2087,7 +2197,7 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) { j_hiC.next(); DeBruijnEdge* edge = j_hiC.value(); - edge->setDrawn(m_hiC->isDrawn(edge)); + edge->setDrawn(g_hicSettings->isDrawnWithNode(edge)); if (edge->isDrawn()) { GraphicsItemEdge* graphicsItemEdge = new GraphicsItemEdge(edge); @@ -2971,6 +3081,22 @@ bool AssemblyGraph::mergeNodes(QList nodes, MyGraphicsScene * sc newPosNode->setReverseComplement(newNegNode); newNegNode->setReverseComplement(newPosNode); + if (g_settings->isAutoParameters) { + bool isDrawn = true; + for (DeBruijnNode* node : orderedList) { + if (!node->isDrawn()) { + isDrawn = false; + } + } + if (g_settings->doubleMode && isDrawn) { + newPosNode->setAsDrawn(); + newNegNode->setAsDrawn(); + } + if (!g_settings->doubleMode && isDrawn) { + newPosNode->setAsDrawn(); + } + } + m_deBruijnGraphNodes.insert(newPosNodeName, newPosNode); m_deBruijnGraphNodes.insert(newNegNodeName, newNegNode); @@ -3007,8 +3133,8 @@ bool AssemblyGraph::mergeNodes(QList nodes, MyGraphicsScene * sc newPosNode->setDepthRelativeToMeanDrawnDepth(1.0); newNegNode->setDepthRelativeToMeanDrawnDepth(1.0); } - - mergeGraphicsNodes(&orderedList, &revCompOrderedList, newPosNode, scene); + if (scene != NULL) + mergeGraphicsNodes(&orderedList, &revCompOrderedList, newPosNode, scene); std::vector nodesToDelete; for (int i = 0; i < orderedList.size(); ++i) diff --git a/graph/assemblygraph.h b/graph/assemblygraph.h index 7a021511..4c0fcc40 100644 --- a/graph/assemblygraph.h +++ b/graph/assemblygraph.h @@ -24,10 +24,10 @@ #include "../ogdf/basic/Graph.h" #include "../ogdf/basic/GraphAttributes.h" -#include "HiCSettings.h" #include #include #include "../program/globals.h" +#include "../program/HiCSettings.h" #include "../ui/mygraphicsscene.h" #include "path.h" #include @@ -72,7 +72,6 @@ class AssemblyGraph : public QObject QString m_filename; QString m_depthTag; SequencesLoadedFromFasta m_sequencesLoadedFromFasta; - HiCSettings * m_hiC; void cleanUp(); void createDeBruijnEdge(QString node1Name, QString node2Name, @@ -111,6 +110,7 @@ class AssemblyGraph : public QObject bool loadGraphFromFile(QString filename); void buildOgdfGraphFromNodesAndEdges(std::vector startingNodes, int nodeDistance); + void buildOgdfGraphWithAutoParameters(std::vector startingNodes); void addGraphicsItemsToScene(MyGraphicsScene * scene); QStringList splitCsv(QString line, QString sep=","); @@ -171,9 +171,9 @@ class AssemblyGraph : public QObject bool attemptToLoadSequencesFromFasta(); long long getTotalLengthOrphanedNodes() const; bool useLinearLayout() const; - bool AssemblyGraph::loadHiC(QString filename, QString* errormsg); - void AssemblyGraph::buildOgdfGraphFromNodesAndEdgesWithHiC(std::vector startingNodes, int nodeDistance); - void setHiCFilter(int filterHiC) { m_hiC->filterHiC = filterHiC; } + bool loadHiC(QString filename, QString* errormsg); + void buildOgdfGraphFromNodesAndEdgesWithHiC(std::vector startingNodes, int nodeDistance); + void addOneHiCBetweenComponent(std::vector startingNodes); private: @@ -213,13 +213,10 @@ class AssemblyGraph : public QObject double findDepthAtIndex(QList * nodeList, long long targetIndex) const; bool allNodesStartWith(QString start) const; QString simplifyCanuNodeName(QString oldName) const; - void AssemblyGraph::addHiCEdges(QList biggestNodesList, QList smallestNodesList); - QList AssemblyGraph::dfs(DeBruijnNode* curNode, QByteArray hiC); - QList AssemblyGraph::getNewStartIndexes(QByteArray wgs, QByteArray hiC); - bool AssemblyGraph::isBeginWith(QByteArray wgs, QByteArray hiC); - void AssemblyGraph::bfs(DeBruijnNode* node, int componentId); - void AssemblyGraph::findComponents(); - void AssemblyGraph::addHiCEdges(std::vector startingNodes); + QPair dfs(DeBruijnNode* node, int componentId); + void findComponents(); + void addHiCEdges(std::vector startingNodes); + void AssemblyGraph::setInclusionFilterAuto(); signals: void setMergeTotalCount(int totalCount); diff --git a/graph/debruijnedge.cpp b/graph/debruijnedge.cpp index 937816f4..5f18d63f 100644 --- a/graph/debruijnedge.cpp +++ b/graph/debruijnedge.cpp @@ -96,10 +96,23 @@ bool DeBruijnEdge::isPositiveEdge() const } -void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray, int maxHiCweight) const +void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray) const { ogdf::node firstEdgeOgdfNode; ogdf::node secondEdgeOgdfNode; + ogdf::node firstEdgeOgdfNode_1; + ogdf::node secondEdgeOgdfNode_1; + ogdf::node firstEdgeOgdfNode_2; + ogdf::node secondEdgeOgdfNode_2; + + //If this in an edge connected a single-segment node to itself, then we + //don't want to put it in the OGDF graph, because it would be redundant + //with the node segment (and created conflict with the node/edge length). + if (m_startingNode == m_endingNode) + { + if (m_startingNode->getNumberOfOgdfGraphEdges(m_startingNode->getDrawnNodeLength()) == 1) + return; + } if (!isHiC()) { if (m_startingNode->inOgdf()) @@ -117,35 +130,35 @@ void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArrayinOgdf()) + if (m_startingNode->inOgdf()) { firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getMiddle(); - else if (m_startingNode->getReverseComplement()->inOgdf()) + firstEdgeOgdfNode_1 = m_startingNode->getOgdfNode()->getPrevMiddle(); + firstEdgeOgdfNode_2 = m_startingNode->getOgdfNode()->getAfterMiddle(); + } + else if (m_startingNode->getReverseComplement()->inOgdf()) { firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getMiddle(); + firstEdgeOgdfNode_1 = m_startingNode->getReverseComplement()->getOgdfNode()->getPrevMiddle(); + firstEdgeOgdfNode_2 = m_startingNode->getReverseComplement()->getOgdfNode()->getAfterMiddle(); + } else return; //Ending node or its reverse complement isn't in OGDF - if (m_endingNode->inOgdf()) + if (m_endingNode->inOgdf()) { secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getMiddle(); - else if (m_endingNode->getReverseComplement()->inOgdf()) + secondEdgeOgdfNode_1 = m_endingNode->getOgdfNode()->getPrevMiddle(); + secondEdgeOgdfNode_2 = m_endingNode->getOgdfNode()->getAfterMiddle(); + } + else if (m_endingNode->getReverseComplement()->inOgdf()) { secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getMiddle(); + secondEdgeOgdfNode_1 = m_endingNode->getReverseComplement()->getOgdfNode()->getPrevMiddle(); + secondEdgeOgdfNode_2 = m_endingNode->getReverseComplement()->getOgdfNode()->getAfterMiddle(); + } else return; //Ending node or its reverse complement isn't in OGDF } - //If this in an edge connected a single-segment node to itself, then we - //don't want to put it in the OGDF graph, because it would be redundant - //with the node segment (and created conflict with the node/edge length). - if (m_startingNode == m_endingNode) - { - if (m_startingNode->getNumberOfOgdfGraphEdges(m_startingNode->getDrawnNodeLength()) == 1) - return; - } - - ogdf::edge newEdge = ogdfGraph->newEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); - if (!isHiC()) { - (*edgeArray)[newEdge] = g_settings->edgeLength; - } - else { - (*edgeArray)[newEdge] = (g_settings->edgeLength) * 25; + if ((!isHiC()) || (m_startingNode->getComponentId() != m_endingNode->getComponentId())) { + ogdf::edge newEdge = ogdfGraph->newEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); + (*edgeArray)[newEdge] = isHiC() ? g_settings->hicEdgeLength : g_settings->edgeLength; } } diff --git a/graph/debruijnedge.h b/graph/debruijnedge.h index 71184263..19a2d672 100644 --- a/graph/debruijnedge.h +++ b/graph/debruijnedge.h @@ -69,7 +69,7 @@ class DeBruijnEdge void setDrawn(bool isDrawn) { m_drawn = isDrawn; } void setExactOverlap(int overlap) {m_overlap = overlap; m_overlapType = EXACT_OVERLAP;} void autoDetermineExactOverlap(); - void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray, int maxHiCweight) const; + void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray) const; void setHiC(bool hiC, int weight) { m_HiC = hiC; m_weight = weight; } private: diff --git a/graph/debruijnnode.cpp b/graph/debruijnnode.cpp index 88224e0a..aba1c983 100644 --- a/graph/debruijnnode.cpp +++ b/graph/debruijnnode.cpp @@ -27,6 +27,7 @@ #include #include #include +#include //The length parameter is optional. If it is set, then the node will use that @@ -592,11 +593,17 @@ void DeBruijnNode::labelNeighbouringNodesAsDrawn(int nodeDistance, DeBruijnNode DeBruijnNode * otherNode; for (size_t i = 0; i < m_edges.size(); ++i) { + if (m_edges[i]->isHiC() && !g_hicSettings->isDrawn(m_edges[i])) { + continue; + } otherNode = m_edges[i]->getOtherNode(this); if (otherNode == callingNode) continue; + if ((otherNode->getComponentId() != 0) && (!g_hicSettings->isBigComponent(otherNode->getComponentId()))) + continue; + if (g_settings->doubleMode) otherNode->m_drawn = true; else //single mode @@ -611,7 +618,6 @@ void DeBruijnNode::labelNeighbouringNodesAsDrawn(int nodeDistance, DeBruijnNode } - std::vector DeBruijnNode::getBlastHitPartsForThisNode(double scaledNodeLength) const { std::vector returnVector; diff --git a/graph/debruijnnode.h b/graph/debruijnnode.h index 768a3f1c..e4a795db 100644 --- a/graph/debruijnnode.h +++ b/graph/debruijnnode.h @@ -130,6 +130,7 @@ class DeBruijnNode void setName(QString newName) {m_name = newName;} void setComponentId(int componentId) {m_componentId = componentId;} + private: QString m_name; double m_depth; @@ -142,7 +143,7 @@ class DeBruijnNode GraphicsItemNode * m_graphicsItemNode; std::vector m_edges; bool m_specialNode; - bool m_drawn; + bool m_drawn = false; int m_highestDistanceInNeighbourSearch; QColor m_customColour; QString m_customLabel; diff --git a/graph/graphicsitemedge.cpp b/graph/graphicsitemedge.cpp index aa15d3a2..74a656fc 100644 --- a/graph/graphicsitemedge.cpp +++ b/graph/graphicsitemedge.cpp @@ -23,6 +23,7 @@ #include #include "../program/globals.h" #include "../program/settings.h" +#include "../program/HiCSettings.h" #include "debruijnnode.h" #include "ogdfnode.h" #include @@ -56,9 +57,10 @@ void GraphicsItemEdge::paint(QPainter * painter, const QStyleOptionGraphicsItem penColour = g_settings->edgeColour; Qt::PenStyle s = Qt::SolidLine; if (m_deBruijnEdge->isHiC()) { - //int dark = 255 - (m_deBruijnEdge->getWeight())*20; - penColour.setRgb(0, 0, 0); + int dark = 200 - 200 * (log(m_deBruijnEdge->getWeight()) / log(g_hicSettings->maxWeight)); + penColour.setRgb(dark, dark, dark); s = Qt::DotLine; + edgeWidth = g_settings->hicEdgeWidth; } QPen edgePen(QBrush(penColour), edgeWidth, s, Qt::RoundCap); painter->setPen(edgePen); @@ -145,7 +147,7 @@ void GraphicsItemEdge::setControlPointLocations() } else if (startingNode->getReverseComplement()->hasGraphicsItem()) { - if (m_deBruijnEdge->isHiC() && startingNode->getReverseComplement()->getGraphicsItemNode() -> isBig()) { + if (m_deBruijnEdge->isHiC() && startingNode->getReverseComplement() -> getGraphicsItemNode()->isBig()) { m_startingLocation = startingNode->getReverseComplement() -> getGraphicsItemNode()->getMiddle(); m_beforeStartingLocation = startingNode->getReverseComplement() -> getGraphicsItemNode()->getAfterMiddle(); } @@ -157,7 +159,7 @@ void GraphicsItemEdge::setControlPointLocations() if (endingNode->hasGraphicsItem()) { - if (m_deBruijnEdge->isHiC() && endingNode->getGraphicsItemNode() -> isBig()) { + if (m_deBruijnEdge->isHiC() && endingNode->getGraphicsItemNode()->isBig()) { m_endingLocation = endingNode->getGraphicsItemNode()->getMiddle(); m_afterEndingLocation = endingNode->getGraphicsItemNode()->getAfterMiddle(); } @@ -168,7 +170,7 @@ void GraphicsItemEdge::setControlPointLocations() } else if (endingNode->getReverseComplement()->hasGraphicsItem()) { - if (m_deBruijnEdge->isHiC() && endingNode->getReverseComplement()->getGraphicsItemNode() -> isBig()) { + if (m_deBruijnEdge->isHiC() && endingNode->getReverseComplement()->getGraphicsItemNode()->isBig()) { m_endingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getMiddle(); m_afterEndingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getBeforeMiddle(); } diff --git a/graph/graphicsitemnode.cpp b/graph/graphicsitemnode.cpp index 400276ef..ecec88f2 100644 --- a/graph/graphicsitemnode.cpp +++ b/graph/graphicsitemnode.cpp @@ -310,7 +310,65 @@ void GraphicsItemNode::setNodeColour() else m_colour = g_settings->uniformNegativeNodeColour; break; + case RANDOM_COMPONENT_COLOURS: + { + if (m_deBruijnNode->getNameWithoutSign().endsWith("_start")) { + QColor redColour; + redColour.setRgb(255, 0, 0); + m_colour = redColour; + } + else + { + if (g_hicSettings->isTargetComponent(m_deBruijnNode->getComponentId())) { + QColor orangeColour; + orangeColour.setRgb(255, 130, 5); + m_colour = orangeColour; + DeBruijnNode* revCompNode = m_deBruijnNode->getReverseComplement(); + if (revCompNode != 0) + { + GraphicsItemNode* revCompGraphNode = revCompNode->getGraphicsItemNode(); + if (revCompGraphNode != 0) + revCompGraphNode->m_colour = orangeColour; + } + break; + } + std::srand(m_deBruijnNode->getComponentId()); + int hue = rand() % 360; + QColor posColour; + posColour.setHsl(hue, + g_settings->randomColourPositiveSaturation, + g_settings->randomColourPositiveLightness); + posColour.setAlpha(g_settings->randomColourPositiveOpacity); + + QColor negColour; + negColour.setHsl(hue, + g_settings->randomColourNegativeSaturation, + g_settings->randomColourNegativeLightness); + negColour.setAlpha(g_settings->randomColourNegativeOpacity); + + QColor colour1, colour2; + if (m_deBruijnNode->isPositiveNode()) + { + colour1 = posColour; + colour2 = negColour; + } + else + { + colour1 = negColour; + colour2 = posColour; + } + m_colour = colour1; + DeBruijnNode* revCompNode = m_deBruijnNode->getReverseComplement(); + if (revCompNode != 0) + { + GraphicsItemNode* revCompGraphNode = revCompNode->getGraphicsItemNode(); + if (revCompGraphNode != 0) + revCompGraphNode->m_colour = colour2; + } + } + break; + } case RANDOM_COLOURS: { //Make a colour with a random hue. Assign a colour to both this node and @@ -591,8 +649,19 @@ void GraphicsItemNode::remakePath() QPainterPath path; path.moveTo(m_linePoints[0]); - for (size_t i = 1; i < m_linePoints.size(); ++i) - path.lineTo(m_linePoints[i]); + if (m_linePoints.size() <= 2) { + for (size_t i = 1; i < m_linePoints.size(); ++i) + path.lineTo(m_linePoints[i]); + } + else { + int middleInd = m_linePoints.size() / 2; + for (size_t i = 1; i < middleInd - 1; ++i) + path.lineTo(m_linePoints[i]); + path.quadTo(m_linePoints[middleInd], m_linePoints[middleInd + 1]); + + for (size_t i = middleInd + 1; i < m_linePoints.size(); ++i) + path.lineTo(m_linePoints[i]); + } m_path = path; } diff --git a/graph/graphicsitemnode.h b/graph/graphicsitemnode.h index 3d1720d4..30a65ee8 100644 --- a/graph/graphicsitemnode.h +++ b/graph/graphicsitemnode.h @@ -89,10 +89,8 @@ class GraphicsItemNode : public QGraphicsItem { if (m_linePoints.size() >= 3) return m_linePoints[(m_linePoints.size() / 2) + 1]; - else if (m_linePoints.size() > 1) - return m_linePoints[1]; else - return m_linePoints[0]; + return m_linePoints[m_linePoints.size() - 1]; } std::vector getCentres() const; QPointF getCentre(std::vector linePoints) const; @@ -120,6 +118,8 @@ class GraphicsItemNode : public QGraphicsItem void drawTextPathAtLocation(QPainter *painter, QPainterPath textPath, QPointF centre); void fixEdgePaths(std::vector * nodes = 0); + void GraphicsItemNode::shiftAroundMiddle(); + private: void exactPathHighlightNode(QPainter * painter); void queryPathHighlightNode(QPainter * painter); diff --git a/graph/ogdfnode.h b/graph/ogdfnode.h index 36d7b810..81014336 100644 --- a/graph/ogdfnode.h +++ b/graph/ogdfnode.h @@ -37,11 +37,40 @@ class OgdfNode ogdf::node getSecond() {if (m_ogdfNodes.size() < 2) return 0; else return m_ogdfNodes[1];} ogdf::node getLast() {if (m_ogdfNodes.size() == 0) return 0; else return m_ogdfNodes[m_ogdfNodes.size()-1];} ogdf::node getSecondLast() {if (m_ogdfNodes.size() < 2) return 0; else return m_ogdfNodes[m_ogdfNodes.size()-2];} + ogdf::node getMiddle() { if (m_ogdfNodes.size() == 0) return 0; else { return m_ogdfNodes[m_ogdfNodes.size() / 2]; } } + ogdf::node getPrevMiddle() { + if (m_ogdfNodes.size() == 0) return 0; + else + { + return (m_ogdfNodes.size() / 2 - 1) < 0 ? m_ogdfNodes[0] : m_ogdfNodes[m_ogdfNodes.size() / 2 - 1]; + } + /*if (m_ogdfNodes.size() == 0) return 0; + else if (m_ogdfNodes.size() <= 5) + { + return (m_ogdfNodes.size() / 2 - 1) < 0 ? m_ogdfNodes[0] : m_ogdfNodes[m_ogdfNodes.size() / 2 - 1]; + } + else { + return m_ogdfNodes[m_ogdfNodes.size() / 2 - 2]; + }*/ + } + ogdf::node getAfterMiddle() { + if (m_ogdfNodes.size() == 0) return 0; + else { + return (m_ogdfNodes.size() / 2 + 1) >= m_ogdfNodes.size() ? m_ogdfNodes[m_ogdfNodes.size() - 1] : m_ogdfNodes[m_ogdfNodes.size() / 2 + 1]; + } + /*if (m_ogdfNodes.size() == 0) return 0; + else if (m_ogdfNodes.size() <= 5) { + return (m_ogdfNodes.size() / 2 + 1) >= m_ogdfNodes.size() ? m_ogdfNodes[m_ogdfNodes.size() - 1] : m_ogdfNodes[m_ogdfNodes.size() / 2 + 1]; + } + else { + return m_ogdfNodes[m_ogdfNodes.size() / 2 + 2]; + }*/ + } }; #endif // OGDFNODE_H diff --git a/program/HiCSettings.cpp b/program/HiCSettings.cpp new file mode 100644 index 00000000..aefbd9c6 --- /dev/null +++ b/program/HiCSettings.cpp @@ -0,0 +1,123 @@ +#include "HiCSettings.h" +#include "../graph/debruijnnode.h" +#include + +HiCSettings::HiCSettings() : minWeight(0), minLength(1), minDist(1) {} +HiCSettings::~HiCSettings() {} + +bool HiCSettings::isDrawnWithNode(DeBruijnEdge* edge) { + return isDrawnWithNode(edge, this->inclusionFilter); + +} + +bool HiCSettings::isDrawnWithNode(DeBruijnEdge* edge, HiCInclusionFilter filterHiC) { + edge->determineIfDrawn(); + bool drawEdge = edge->isDrawn(); + if (!drawEdge) + return false; + bool res = (edge->getWeight() >= minWeight && + edge->getStartingNode()->getLength() >= minLength && + edge->getEndingNode()->getLength() >= minLength && + isValidContigLength(edge) && + (filterHiC == ALL || + filterHiC == ALL_BETWEEN_GRAPH_COMPONENTS && edge->getStartingNode()->getComponentId() != edge->getEndingNode()->getComponentId() || + filterHiC == ONE_BETWEEN_GRAPH_COMPONENT && contains(edge) || + filterHiC == ONE_FROM_TARGET_COMPONENT && contains(edge) && + (isTargetComponent(edge->getStartingNode()->getComponentId()) || isTargetComponent(edge->getEndingNode()->getComponentId())))); + return res; + +} + +bool HiCSettings::isDrawn(DeBruijnEdge* edge) { + return isDrawn(edge, inclusionFilter); + +} + +bool HiCSettings::isDrawn(DeBruijnEdge* edge, HiCInclusionFilter filterHiC) { + bool res(edge->getWeight() >= minWeight && + edge->getStartingNode()->getLength() >= minLength && + edge->getEndingNode()->getLength() >= minLength && + isValidContigLength(edge) && + (filterHiC == ALL || + filterHiC == ALL_BETWEEN_GRAPH_COMPONENTS && edge->getStartingNode()->getComponentId() != edge->getEndingNode()->getComponentId() || + filterHiC == ONE_BETWEEN_GRAPH_COMPONENT && contains(edge) || + filterHiC == ONE_FROM_TARGET_COMPONENT && contains(edge) && + (isTargetComponent(edge->getStartingNode()->getComponentId()) || isTargetComponent(edge->getEndingNode()->getComponentId())))); + return res; + +} + +bool HiCSettings::addEdgeIfNeeded(DeBruijnEdge* edge) { + if (!edge->isHiC()) { + return false; + } + QPair key = getComponentKey(edge); + int startingComponentId = edge->getStartingNode()->getComponentId(); + int endingComponentId = edge->getEndingNode()->getComponentId(); + if (startingComponentId != endingComponentId && + isBigComponent(startingComponentId) && + isBigComponent(endingComponentId) && + isValidContigLength(edge)) { + sumWeightBetweenComponent += edge->getWeight(); + countOfEdgesBetweenComponent += 1; + if (!componentEdgeMap.contains(key)) { + componentEdgeMap[key] = edge; + return true; + } + else if (componentEdgeMap[key]->getWeight() < edge -> getWeight()) { + componentEdgeMap[key] = edge; + return true; + } + } + return false; +} + +bool HiCSettings::isValidContigLength(DeBruijnEdge* edge) { + return !checkContigLength || max(1000, averageSize[edge->getStartingNode()->getComponentId() - 1]) <= edge->getStartingNode()->getLength() && + max(1000, averageSize[edge->getEndingNode()->getComponentId() - 1]) <= edge->getEndingNode()->getLength(); +} + +bool HiCSettings::contains(DeBruijnEdge* edge) { + if (!edge->isHiC()) + return false; + QPair key = getComponentKey(edge); + return componentEdgeMap.contains(key) && + componentEdgeMap[key] == edge; +} + +void HiCSettings::addTargetComponentIfNeeded(int id) { + if (!targetComponents.contains(id)) { + targetComponents.push_back(id); + } +} +bool HiCSettings::isTargetComponent(int componentId) { + return targetComponents.contains(componentId); +} + +bool HiCSettings::isConnectedWithTargetComponent(int componentId) { + if (isTargetComponent(componentId)) { + return true; + } + for (int targetComponentId : g_hicSettings->targetComponents) { + QPair key = qMakePair(componentId, targetComponentId); + QPair reverseKey = qMakePair(targetComponentId, componentId); + if ((g_hicSettings->componentEdgeMap.contains(key) /*&& g_hicSettings->componentEdgeMap[key]->getWeight() >= g_hicSettings->minWeight*/ ) || + (g_hicSettings->componentEdgeMap.contains(reverseKey) /*&& g_hicSettings->componentEdgeMap[reverseKey]->getWeight() >= g_hicSettings->minWeight*/)) { + return true; + } + } + return false; +} + +bool HiCSettings::isBigComponent(int componentId) { + return componentSize[componentId - 1] >= 500; +} + +QPair HiCSettings::getComponentKey(DeBruijnEdge* edge) { + if (edge->getStartingNode()->isPositiveNode() && edge->getEndingNode()->isPositiveNode()) + return qMakePair(std::min(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId()), + std::max(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId())); + else + return qMakePair(std::max(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId()), + std::min(edge->getStartingNode()->getComponentId(), edge->getEndingNode()->getComponentId())); +} diff --git a/program/HiCSettings.h b/program/HiCSettings.h new file mode 100644 index 00000000..7698b080 --- /dev/null +++ b/program/HiCSettings.h @@ -0,0 +1,65 @@ +#ifndef HICSETTINGS_H +#define HICSETTINGS_H + +#include +#include + +#include +#include +#include +#include + + +#include "../program/globals.h" +#include "../ui/mygraphicsscene.h" +#include "../graph/debruijnedge.h" + +class HiCSettings : public QObject +{ + Q_OBJECT + +public: + HiCSettings(); + ~HiCSettings(); + + int minWeight = 0; + int minLength = 1; + int minDist = 1; + int maxWeight = 0; + int maxComponentSize = 100; + bool checkContigLength = true; + unsigned long sumWeightBetweenComponent = 0; + unsigned long countOfEdgesBetweenComponent = 0; + + HiCInclusionFilter inclusionFilter = ALL; + int componentNum = 0; + QMap, DeBruijnEdge*> componentEdgeMap; + //QVector componentToContigMap; + //QMap, int> hicWeightBetweenComponent; + QVector targetComponents; + QVector componentSize; + QVector averageSize; + + bool isDrawnWithNode(DeBruijnEdge* edge); + bool isDrawnWithNode(DeBruijnEdge* edge, HiCInclusionFilter filterHiC); + bool isDrawn(DeBruijnEdge* edge); + bool isDrawn(DeBruijnEdge* edge, HiCInclusionFilter filterHiC); + bool addEdgeIfNeeded(DeBruijnEdge* edge); + bool contains(DeBruijnEdge* edge); + void addTargetComponentIfNeeded(int id); + bool isTargetComponent(int componentId); + bool isConnectedWithTargetComponent(int componentId); + bool isBigComponent(int componentId); + int getAverageWeightBetweenComponent() { + if (countOfEdgesBetweenComponent == 0) { + return 0; + } + return sumWeightBetweenComponent / countOfEdgesBetweenComponent; + } + //void addToHicWeightBetweenComponentMap(DeBruijnEdge* edge); +private: + QPair getComponentKey(DeBruijnEdge* edge); + bool isValidContigLength(DeBruijnEdge* edge); +}; +#endif //HICSETTINGS_H + diff --git a/program/globals.cpp b/program/globals.cpp index 42695874..9f1d9274 100644 --- a/program/globals.cpp +++ b/program/globals.cpp @@ -27,6 +27,7 @@ #include QSharedPointer g_settings; +QSharedPointer g_hicSettings; QSharedPointer g_memory; MyGraphicsView * g_graphicsView; double g_absoluteZoom; diff --git a/program/globals.h b/program/globals.h index 10b1bbbd..89dd96b6 100644 --- a/program/globals.h +++ b/program/globals.h @@ -30,11 +30,14 @@ class Memory; class MyGraphicsView; class BlastSearch; class AssemblyGraph; +class HiCSettings; enum NodeColourScheme {UNIFORM_COLOURS, RANDOM_COLOURS, DEPTH_COLOUR, BLAST_HITS_RAINBOW_COLOUR, BLAST_HITS_SOLID_COLOUR, - CONTIGUITY_COLOUR, CUSTOM_COLOURS}; + CONTIGUITY_COLOUR, CUSTOM_COLOURS, RANDOM_COMPONENT_COLOURS}; enum GraphScope {WHOLE_GRAPH, AROUND_NODE, AROUND_BLAST_HITS, DEPTH_RANGE}; +enum HiCDrawingType {ALL_EDGES, ONE_EDGE, NO_EDGE}; +enum HiCInclusionFilter {ALL, ALL_BETWEEN_GRAPH_COMPONENTS, ONE_BETWEEN_GRAPH_COMPONENT, ONE_FROM_TARGET_COMPONENT}; enum ContiguityStatus {STARTING, CONTIGUOUS_STRAND_SPECIFIC, CONTIGUOUS_EITHER_STRAND, MAYBE_CONTIGUOUS, NOT_CONTIGUOUS}; @@ -62,6 +65,7 @@ enum SequencesLoadedFromFasta {NOT_READY, NOT_TRIED, TRIED}; //Some of the program's common components are made global so they don't have //to be passed around as parameters. extern QSharedPointer g_settings; +extern QSharedPointer g_hicSettings; extern QSharedPointer g_memory; extern MyGraphicsView * g_graphicsView; extern double g_absoluteZoom; diff --git a/program/graphlayoutworker.cpp b/program/graphlayoutworker.cpp index 3d642201..0c8b39e0 100644 --- a/program/graphlayoutworker.cpp +++ b/program/graphlayoutworker.cpp @@ -87,7 +87,6 @@ void GraphLayoutWorker::layoutGraph() m_fmmm->nmPrecision(8); break; } - m_fmmm->call(*m_graphAttributes, *m_edgeArray); emit finishedLayout(); diff --git a/program/main.cpp b/program/main.cpp index 037d6e18..32ec333f 100644 --- a/program/main.cpp +++ b/program/main.cpp @@ -98,6 +98,7 @@ int main(int argc, char *argv[]) //Create the important global objects. g_settings.reset(new Settings()); + g_hicSettings.reset(new HiCSettings()); g_memory.reset(new Memory()); g_blastSearch.reset(new BlastSearch()); g_assemblyGraph.reset(new AssemblyGraph()); diff --git a/program/settings.cpp b/program/settings.cpp index e00de1eb..acc569bb 100644 --- a/program/settings.cpp +++ b/program/settings.cpp @@ -141,4 +141,7 @@ Settings::Settings() minDepthRange = FloatSetting(10.0, 0.0, 1000000.0); maxDepthRange = FloatSetting(100.0, 0.0, 1000000.0); + + hicEdgeLength = FloatSetting(200.0, 0.1, 1000.0); + hicEdgeWidth = FloatSetting(1.5, 0.1, 1000.0); } diff --git a/program/settings.h b/program/settings.h index 7ec64ed7..57515832 100644 --- a/program/settings.h +++ b/program/settings.h @@ -202,6 +202,14 @@ class Settings //These are used for the 'Depth range' graph scope. FloatSetting minDepthRange; FloatSetting maxDepthRange; + + FloatSetting hicEdgeLength; + FloatSetting hicEdgeWidth; + + HiCInclusionFilter hicInclusionFilter = ONE_FROM_TARGET_COMPONENT; + HiCDrawingType hicDrawingType = ALL_EDGES; + + bool isAutoParameters = true; }; #endif // SETTINGS_H diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 348d1eba..459c1172 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -67,6 +67,8 @@ #include "changenodedepthdialog.h" #include #include "graphinfodialog.h" +#include +#include MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : QMainWindow(0), @@ -430,6 +432,8 @@ void MainWindow::loadGraph2(GraphFileType graphFileType, QString fullFileName) progress.setWindowModality(Qt::WindowModal); progress.show(); + g_assemblyGraph->cleanUp(); + if (graphFileType == LAST_GRAPH) g_assemblyGraph->buildDeBruijnGraphFromLastGraph(fullFileName); else if (graphFileType == FASTG) @@ -768,18 +772,10 @@ void MainWindow::setHiCWidgetVisibility(bool visible) void MainWindow::drawGraph() { - /* - * 0 - HiC не влияет на укладку - * 1 - все HiC влияют на укладку, фиксированная длина ребер - */ - int type = 1; - float percent = 0.9; + HiCDrawingType type = g_settings->hicDrawingType; QString errorTitle; - //0 - no filter - //1 - all edges between components - //2 - one edge between component QString errorMessage; - int filterHiC = 0; + HiCInclusionFilter filterHiC = g_settings->hicInclusionFilter; std::vector startingNodes = g_assemblyGraph->getStartingNodes(&errorTitle, &errorMessage, ui->doubleNodesRadioButton->isChecked(), ui->startingNodesLineEdit->text(), @@ -791,22 +787,38 @@ void MainWindow::drawGraph() return; } - g_assemblyGraph->m_hiC->minWeight = ui->hicWeightSpinBox->value(); - g_assemblyGraph->m_hiC->minLength = ui->hicSeqLenSpinBox->value(); - g_assemblyGraph->setHiCFilter(filterHiC); + g_hicSettings->minWeight = ui->hicWeightSpinBox->value(); + g_hicSettings->minLength = ui->hicSeqLenSpinBox->value(); + g_hicSettings->inclusionFilter = filterHiC; resetScene(); - if (type == 0) { - g_assemblyGraph->buildOgdfGraphFromNodesAndEdges(startingNodes, g_settings->nodeDistance); + + if (g_settings->isAutoParameters) { + g_assemblyGraph->buildOgdfGraphWithAutoParameters(startingNodes); layoutGraph(); } - if (type == 1) { - g_assemblyGraph->buildOgdfGraphFromNodesAndEdgesWithHiC(startingNodes, g_settings->nodeDistance); - layoutGraph(); + else { + switch (type) + { + case ALL_EDGES: + g_assemblyGraph->buildOgdfGraphFromNodesAndEdgesWithHiC(startingNodes, g_settings->nodeDistance); + layoutGraph(); + break; + case ONE_EDGE: + g_assemblyGraph->buildOgdfGraphFromNodesAndEdges(startingNodes, g_settings->nodeDistance); + g_assemblyGraph->addOneHiCBetweenComponent(startingNodes); + layoutGraph(); + break; + case NO_EDGE: + g_assemblyGraph->buildOgdfGraphFromNodesAndEdges(startingNodes, g_settings->nodeDistance); + layoutGraph(); + break; + default: + break; + } } } - void MainWindow::graphLayoutFinished() { delete m_fmmm; @@ -1148,6 +1160,10 @@ void MainWindow::switchColourScheme() ui->contiguityButton->setVisible(false); ui->contiguityInfoText->setVisible(false); break; + case 7: + g_settings->nodeColourScheme = RANDOM_COMPONENT_COLOURS; + ui->contiguityButton->setVisible(false); + ui->contiguityInfoText->setVisible(false); } g_assemblyGraph->resetAllNodeColours(); diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 145d8bc9..6b814da6 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -68,19 +68,12 @@ class MainWindow : public QMainWindow void clearGraphDetails(); void resetScene(); void layoutGraph(); - void layoutHiCGraph(); - void layoutPlanarGraph(); - void layoutGraphWithoutFinishing(); - void layoutGraphWithKeepPosition(); - - void addGraphicsItemsToScene(); void zoomToFitRect(QRectF rect); void zoomToFitScene(); void setZoomSpinBoxStep(); void getSelectedNodeInfo(int & selectedNodeCount, QString & selectedNodeCountText, QString & selectedNodeListText, QString & selectedNodeLengthText, QString &selectedNodeDepthText); QString getSelectedEdgeListText(); std::vector getNodesFromLineEdit(QLineEdit * lineEdit, bool exactMatch, std::vector * nodesNotInGraph = 0); - void setSceneRectangle(); void loadGraph2(GraphFileType graphFileType, QString filename); void setInfoTexts(); void setUiState(UiState uiState); @@ -102,7 +95,7 @@ class MainWindow : public QMainWindow void removeGraphicsItemNodes(const std::vector * nodes, bool reverseComplement); void removeGraphicsItemEdges(const std::vector * edges, bool reverseComplement); void removeAllGraphicsEdgesFromNode(DeBruijnNode * node, bool reverseComplement); - std::vector addComplementaryNodes(std::vector nodes); + std::vector addComplementaryNodes(std::vector nodes); private slots: void loadGraph(QString fullFileName = ""); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 99183e46..33ec249d 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -59,8 +59,8 @@ 0 0 - 251 - 976 + 335 + 1038 @@ -259,554 +259,557 @@ - - - 0 - - - 0 - - - 0 - - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Exact - - - true - - - - - - - - 0 - 0 - - - - Partial - - - - - - - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Single - - - true - - - - - - - - 0 - 0 - - - - Double - - - - - - - - - - Draw graph - - - - - - - - 0 - 0 - - - - Style: - - - - - - - true - - - - 0 - 0 - - - - Qt::AlignCenter - - - 10000 - - - - - - - true - - - - 0 - 0 - - - - Node(s): - - - - - - - true - - - - 0 - 0 - - - - - - - - - 0 - 0 - - - - - Entire graph - - - - - Around nodes - - - - - Around BLAST hits - - - - - Depth range - - - - - - - - - 0 - 0 - - - - Scope: - - - - - - - true - - - Distance: - - - - - - - true - - - - 0 - 0 - - - - Match: - - - - - - - Qt::AlignCenter - - - 1 - - - 1000000.000000000000000 - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - HiC min weight: - - - Qt::AlignLeft - - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - - - - 1 - - - 0.000000000000000 - - - 1000.000000000000000 - - - 1.000000000000000 - - - 1.000000000000000 - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - HiC min sequence length: - - - Qt::AlignLeft - - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - - - - 1 - - - 0.000000000000000 - - - 1.000000000000000 - - - 5.000000000000000 - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - Min: - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - Max: - - - - - - - Qt::AlignCenter - - - 1 - - - 1000000.000000000000000 - - - + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Exact + + + true + + + + + + + + 0 + 0 + + + + Partial + + + + + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Single + + + true + + + + + + + + 0 + 0 + + + + Double + + + + + + + + + + Draw graph + + + + + + + + 0 + 0 + + + + Style: + + + + + + + true + + + + 0 + 0 + + + + Qt::AlignCenter + + + 10000 + + + + + + + true + + + + 0 + 0 + + + + Node(s): + + + + + + + true + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + Entire graph + + + + + Around nodes + + + + + Around BLAST hits + + + + + Depth range + + + + + + + + + 0 + 0 + + + + Scope: + + + + + + + true + + + Distance: + + + + + + + true + + + + 0 + 0 + + + + Match: + + + + + + + Qt::AlignCenter + + + 1 + + + 1000000.000000000000000 + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + HiC min weight: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.000000000000000 + + + 1000.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + HiC min sequence length: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.000000000000000 + + + 1000000.000000000000000 + + + 10.000000000000000 + + + 100.000000000000000 + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + Min: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + Max: + + + + + + + Qt::AlignCenter + + + 1 + + + 1000000.000000000000000 + + + + @@ -868,209 +871,214 @@ - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - % - - - 1 - - - 5.000000000000000 - - - 500.000000000000000 - - - 5.000000000000000 - - - 100.000000000000000 - - - - - - - Determine contiguity - - - - - - - - Random colours - - - - - Uniform colour - - - - - Colour by depth - - - - - BLAST hits (solid) - - - - - BLAST hits (rainbow) - - - - - Colour by contiguity - - - - - Custom colours - - - - - - - - Zoom: - - - Qt::AlignLeft - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - Node width: - - - Qt::AlignLeft - - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - - - - 1 - - - 0.500000000000000 - - - 1000.000000000000000 - - - 0.500000000000000 - - - 5.000000000000000 - - - - + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + % + + + 1 + + + 5.000000000000000 + + + 500.000000000000000 + + + 5.000000000000000 + + + 100.000000000000000 + + + + + + + Determine contiguity + + + + + + + + Random colours + + + + + Uniform colour + + + + + Colour by depth + + + + + BLAST hits (solid) + + + + + BLAST hits (rainbow) + + + + + Colour by contiguity + + + + + Custom colours + + + + + Random component colours + + + + + + + + Zoom: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Node width: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.500000000000000 + + + 1000.000000000000000 + + + 0.500000000000000 + + + 5.000000000000000 + + + + @@ -1190,72 +1198,72 @@ - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Custom - - - - - - - Name - - - - - - - Length - - - - - - - Depth - - - - - - - BLAST hits - - - - - - - false - - - - - - - CSV data: - - - - + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Custom + + + + + + + Name + + + + + + + Length + + + + + + + Depth + + + + + + + BLAST hits + + + + + + + false + + + + + + + CSV data: + + + + @@ -2376,7 +2384,6 @@ drawGraphButton zoomSpinBox nodeWidthSpinBox - hiCWeightSpinBox coloursComboBox contiguityButton nodeCustomLabelsCheckBox diff --git a/ui/settingsdialog.cpp b/ui/settingsdialog.cpp index cdbf12ff..29952bbf 100644 --- a/ui/settingsdialog.cpp +++ b/ui/settingsdialog.cpp @@ -223,6 +223,8 @@ void SettingsDialog::loadOrSaveSettingsToOrFromWidgets(bool setWidgets, Settings intFunctionPointer(&settings->minLengthBaseDiscrepancy, ui->minLengthBaseDiscrepancySpinBox); checkBoxFunctionPointer(&settings->maxLengthBaseDiscrepancy.on, ui->maxLengthBaseDiscrepancyCheckBox); intFunctionPointer(&settings->maxLengthBaseDiscrepancy, ui->maxLengthBaseDiscrepancySpinBox); + doubleFunctionPointer(&settings->hicEdgeLength, ui->hicEdgeLengthSpinBox, false); + doubleFunctionPointer(&settings->hicEdgeWidth, ui->hicEdgeWidthSpinBox, false); //A couple of settings are not in a spin box, check box or colour button, so //they have to be done manually, not with those function pointers. @@ -246,6 +248,34 @@ void SettingsDialog::loadOrSaveSettingsToOrFromWidgets(bool setWidgets, Settings ui->nodeLengthPerMegabaseManualRadioButton->setChecked(settings->nodeLengthMode != AUTO_NODE_LENGTH); ui->positionVisibleRadioButton->setChecked(!settings->positionTextNodeCentre); ui->positionCentreRadioButton->setChecked(settings->positionTextNodeCentre); + switch (settings->hicInclusionFilter) { + case ONE_BETWEEN_GRAPH_COMPONENT: + ui->hicInclusionFilterComboBox->setCurrentIndex(0); + break; + case ALL_BETWEEN_GRAPH_COMPONENTS: + ui->hicInclusionFilterComboBox->setCurrentIndex(1); + break; + case ALL: + ui->hicInclusionFilterComboBox->setCurrentIndex(2); + break; + case ONE_FROM_TARGET_COMPONENT: + ui->hicInclusionFilterComboBox->setCurrentIndex(3); + break; + } + + switch (settings->hicDrawingType) + { + case ALL_EDGES: + ui->hicDrawingTypeComboBox->setCurrentIndex(0); + break; + case ONE_EDGE: + ui->hicDrawingTypeComboBox->setCurrentIndex(1); + break; + case NO_EDGE: + ui->hicDrawingTypeComboBox->setCurrentIndex(2); + break; + } + } else { @@ -259,6 +289,34 @@ void SettingsDialog::loadOrSaveSettingsToOrFromWidgets(bool setWidgets, Settings else settings->nodeLengthMode = MANUAL_NODE_LENGTH; settings->positionTextNodeCentre = ui->positionCentreRadioButton->isChecked(); + switch (ui->hicInclusionFilterComboBox->currentIndex()) + { + case 0: + settings->hicInclusionFilter = ONE_BETWEEN_GRAPH_COMPONENT; + break; + case 1: + settings->hicInclusionFilter = ALL_BETWEEN_GRAPH_COMPONENTS; + break; + case 2: + settings->hicInclusionFilter = ALL; + break; + case 3: + settings->hicInclusionFilter = ONE_FROM_TARGET_COMPONENT; + break; + } + + switch (ui->hicDrawingTypeComboBox->currentIndex()) + { + case 0: + settings->hicDrawingType = ALL_EDGES; + break; + case 1: + settings->hicDrawingType = ONE_EDGE; + break; + case 2: + settings->hicDrawingType = NO_EDGE; + break; + } } } diff --git a/ui/settingsdialog.ui b/ui/settingsdialog.ui index 2c27a310..3ddafac5 100644 --- a/ui/settingsdialog.ui +++ b/ui/settingsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 378 - 673 + 487 + 649 @@ -38,9 +38,9 @@ 0 - -62 - 400 - 3466 + -2795 + 464 + 3389 @@ -1539,7 +1539,7 @@ single node style: - -1 + 7 0 @@ -4056,6 +4056,199 @@ discrepancy (bases): + + + + Hi-C Settings + + + + + + + Qt::Horizontal + + + + + + + These settings controle how Bandage visualize Hi-C metadatas. + + + + + + + + 100 + 150 + + + + + + + + 0 + 0 + + + + Hi-C edge length: + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + 1 + + + 0.100000000000000 + + + 1000.000000000000000 + + + 10.000000000000000 + + + 200.000000000000000 + + + + + + + + 0 + 0 + + + + Hi-C edge width: + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + 1 + + + 0.100000000000000 + + + 100.000000000000000 + + + 0.500000000000000 + + + + + + + + 0 + 0 + + + + Hi-C edge inclusion filter: + + + + + + + + 0 + 0 + + + + + One Hi-C edge between component + + + + + All Hi-C edges between component + + + + + All Hi-C edges + + + + + One Hi-C edge from target component + + + + + + + + + 0 + 0 + + + + Hi-C drawing type: + + + + + + + + 0 + 0 + + + + + Fixed size + + + + + Fixed and not fixed size + + + + + Not fixed size + + + + + + + From a2077d988653cb1b8f5823c24e5703a6525daa4a Mon Sep 17 00:00:00 2001 From: Shostina Date: Tue, 19 Apr 2022 21:20:26 +0300 Subject: [PATCH 06/11] revert bandage_build_windows.bat changes --- build_scripts/bandage_build_windows.bat | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/build_scripts/bandage_build_windows.bat b/build_scripts/bandage_build_windows.bat index 111e9812..cd580bed 100644 --- a/build_scripts/bandage_build_windows.bat +++ b/build_scripts/bandage_build_windows.bat @@ -5,18 +5,16 @@ rem Bandage. It produces a directory ready for deployment. It requires that samp rem the current directory. rem These variables must be set to the correct paths and values for your system. -set QT_PATH=C:\Qt3\5.12.4 -set MSVC_PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community -set MSVC_VERSION=msvc2017_64 -set MSVC_VERSION_NUM=168 -set GIT_PATH=C:\Program Files\Git\bin\git.exe +set QT_PATH=C:\Qt\5.6 +set MSVC_PATH=C:\Program Files (x86)\Microsoft Visual Studio 14.0 +set MSVC_VERSION=msvc2015_64 +set MSVC_VERSION_NUM=140 +set GIT_PATH=C:\Program Files\Git\bin\git set ZIP_PATH=C:\Program Files\7-Zip\7z.exe rem Set up the MSVC compiler. call "%MSVC_PATH%\VC\vcvarsall.bat" x86_amd64 -pause - rem If a 'Bandage' directory already exists, then this script will use assume it is the Bandage rem source code and use it. If not, it will clone the master branch from GitHub. if not exist Bandage\ call "%GIT_PATH%" clone https://github.com/rrwick/Bandage.git @@ -25,31 +23,21 @@ call "%QT_PATH%\%MSVC_VERSION%\bin\qmake.exe" call "%QT_PATH%\..\Tools\QtCreator\bin\jom.exe" call cd .. -pause - rem Get the Bandage version number from the main.cpp file and replace its dots with underscores. for /f %%i in ('findstr setApplicationVersion Bandage\program\main.cpp') do set VERSION_LINE=%%i set VERSION_LINE=%VERSION_LINE:.=_% set VERSION_LINE=%VERSION_LINE:"=;% for /f "tokens=2 delims=;" %%G IN ("%VERSION_LINE%") DO set VERSION=%%G -pause - rem Delete all of the source and build files call move Bandage\release\Bandage.exe Bandage.exe call rmdir Bandage\ /S /Q call mkdir Bandage\ call move Bandage.exe Bandage\Bandage.exe -pause - rem Add the necessary libraries so Bandage can be deployed. call "%QT_PATH%\%MSVC_VERSION%\bin\windeployqt.exe" Bandage\Bandage.exe -pause - rem Zip Bandage with the sample graph and clean up. call "%ZIP_PATH%" a -tzip Bandage_Windows_v%VERSION%.zip Bandage\ sample_LastGraph installation.txt -call rmdir Bandage\ /S /Q - -pause \ No newline at end of file +call rmdir Bandage\ /S /Q \ No newline at end of file From 0b2e84b6f10524b43a21b13807eab8f6b8bf4b08 Mon Sep 17 00:00:00 2001 From: Shostina Date: Tue, 19 Apr 2022 21:30:24 +0300 Subject: [PATCH 07/11] small refactoring --- graph/debruijnedge.cpp | 12 ----------- graph/debruijnnode.cpp | 49 ------------------------------------------ graph/debruijnnode.h | 1 - graph/ogdfnode.h | 15 ------------- program/HiCSettings.h | 4 +--- 5 files changed, 1 insertion(+), 80 deletions(-) diff --git a/graph/debruijnedge.cpp b/graph/debruijnedge.cpp index 5f18d63f..2fdc415e 100644 --- a/graph/debruijnedge.cpp +++ b/graph/debruijnedge.cpp @@ -100,10 +100,6 @@ void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArrayinOgdf()) { firstEdgeOgdfNode = m_startingNode->getOgdfNode()->getMiddle(); - firstEdgeOgdfNode_1 = m_startingNode->getOgdfNode()->getPrevMiddle(); - firstEdgeOgdfNode_2 = m_startingNode->getOgdfNode()->getAfterMiddle(); } else if (m_startingNode->getReverseComplement()->inOgdf()) { firstEdgeOgdfNode = m_startingNode->getReverseComplement()->getOgdfNode()->getMiddle(); - firstEdgeOgdfNode_1 = m_startingNode->getReverseComplement()->getOgdfNode()->getPrevMiddle(); - firstEdgeOgdfNode_2 = m_startingNode->getReverseComplement()->getOgdfNode()->getAfterMiddle(); } else return; //Ending node or its reverse complement isn't in OGDF if (m_endingNode->inOgdf()) { secondEdgeOgdfNode = m_endingNode->getOgdfNode()->getMiddle(); - secondEdgeOgdfNode_1 = m_endingNode->getOgdfNode()->getPrevMiddle(); - secondEdgeOgdfNode_2 = m_endingNode->getOgdfNode()->getAfterMiddle(); } else if (m_endingNode->getReverseComplement()->inOgdf()) { secondEdgeOgdfNode = m_endingNode->getReverseComplement()->getOgdfNode()->getMiddle(); - secondEdgeOgdfNode_1 = m_endingNode->getReverseComplement()->getOgdfNode()->getPrevMiddle(); - secondEdgeOgdfNode_2 = m_endingNode->getReverseComplement()->getOgdfNode()->getAfterMiddle(); } else return; //Ending node or its reverse complement isn't in OGDF diff --git a/graph/debruijnnode.cpp b/graph/debruijnnode.cpp index aba1c983..0b9a7b57 100644 --- a/graph/debruijnnode.cpp +++ b/graph/debruijnnode.cpp @@ -90,55 +90,6 @@ void DeBruijnNode::resetNode() m_highestDistanceInNeighbourSearch = 0; } -//void DeBruijnNode::addToSimpleOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::GraphAttributes* graphAttributes, double xPos, double yPos) { -// if (thisOrReverseComplementInOgdf()) -// return; -// m_ogdfNode = new OgdfNode(); -// ogdf::node newNode = ogdfGraph->newNode(); -// m_ogdfNode->addOgdfNode(newNode); -//} - -void DeBruijnNode::addToSimpleOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::GraphAttributes* graphAttributes, double xPos, double yPos) -{ - //If this node or its reverse complement is already in OGDF, then - //it's not necessary to make the node. - if (thisOrReverseComplementInOgdf()) - return; - - //Create the OgdfNode object - m_ogdfNode = new OgdfNode(); - - //Each node in the Velvet sense is made up of multiple nodes in the - //OGDF sense. This way, Velvet nodes appear as lines whose length - //corresponds to the sequence length. - double drawnNodeLength = g_settings->minimumNodeLength; - int numberOfGraphEdges = getNumberOfOgdfGraphEdges(drawnNodeLength); - int numberOfGraphNodes = numberOfGraphEdges + 1; - double drawnLengthPerEdge = drawnNodeLength / numberOfGraphEdges; - - ogdf::node newNode = 0; - ogdf::node previousNode = 0; - for (int i = 0; i < numberOfGraphNodes; ++i) - { - newNode = ogdfGraph->newNode(); - m_ogdfNode->addOgdfNode(newNode); - - if (g_assemblyGraph->useLinearLayout()) { - graphAttributes->x(newNode) = xPos; - graphAttributes->y(newNode) = yPos; - xPos += g_settings->nodeSegmentLength; - } - - if (i > 0) - { - ogdf::edge newEdge = ogdfGraph->newEdge(previousNode, newNode); - //(*edgeArray)[newEdge] = drawnLengthPerEdge; - } - - previousNode = newNode; - } -} - void DeBruijnNode::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes * graphAttributes, ogdf::EdgeArray * edgeArray, double xPos, double yPos) { diff --git a/graph/debruijnnode.h b/graph/debruijnnode.h index e4a795db..b70b596d 100644 --- a/graph/debruijnnode.h +++ b/graph/debruijnnode.h @@ -119,7 +119,6 @@ class DeBruijnNode void removeEdge(DeBruijnEdge * edge); void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes * graphAttributes, ogdf::EdgeArray * edgeArray, double xPos, double yPos); - void addToSimpleOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::GraphAttributes* graphAttributes, double xPos, double yPos); void determineContiguity(); void clearBlastHits() {m_blastHits.clear();} void addBlastHit(BlastHit * newHit) {m_blastHits.push_back(newHit);} diff --git a/graph/ogdfnode.h b/graph/ogdfnode.h index 81014336..c7fd6c96 100644 --- a/graph/ogdfnode.h +++ b/graph/ogdfnode.h @@ -49,27 +49,12 @@ class OgdfNode { return (m_ogdfNodes.size() / 2 - 1) < 0 ? m_ogdfNodes[0] : m_ogdfNodes[m_ogdfNodes.size() / 2 - 1]; } - /*if (m_ogdfNodes.size() == 0) return 0; - else if (m_ogdfNodes.size() <= 5) - { - return (m_ogdfNodes.size() / 2 - 1) < 0 ? m_ogdfNodes[0] : m_ogdfNodes[m_ogdfNodes.size() / 2 - 1]; - } - else { - return m_ogdfNodes[m_ogdfNodes.size() / 2 - 2]; - }*/ } ogdf::node getAfterMiddle() { if (m_ogdfNodes.size() == 0) return 0; else { return (m_ogdfNodes.size() / 2 + 1) >= m_ogdfNodes.size() ? m_ogdfNodes[m_ogdfNodes.size() - 1] : m_ogdfNodes[m_ogdfNodes.size() / 2 + 1]; } - /*if (m_ogdfNodes.size() == 0) return 0; - else if (m_ogdfNodes.size() <= 5) { - return (m_ogdfNodes.size() / 2 + 1) >= m_ogdfNodes.size() ? m_ogdfNodes[m_ogdfNodes.size() - 1] : m_ogdfNodes[m_ogdfNodes.size() / 2 + 1]; - } - else { - return m_ogdfNodes[m_ogdfNodes.size() / 2 + 2]; - }*/ } }; diff --git a/program/HiCSettings.h b/program/HiCSettings.h index 7698b080..d6220da5 100644 --- a/program/HiCSettings.h +++ b/program/HiCSettings.h @@ -34,8 +34,6 @@ class HiCSettings : public QObject HiCInclusionFilter inclusionFilter = ALL; int componentNum = 0; QMap, DeBruijnEdge*> componentEdgeMap; - //QVector componentToContigMap; - //QMap, int> hicWeightBetweenComponent; QVector targetComponents; QVector componentSize; QVector averageSize; @@ -56,7 +54,7 @@ class HiCSettings : public QObject } return sumWeightBetweenComponent / countOfEdgesBetweenComponent; } - //void addToHicWeightBetweenComponentMap(DeBruijnEdge* edge); + private: QPair getComponentKey(DeBruijnEdge* edge); bool isValidContigLength(DeBruijnEdge* edge); From 5c8caa7bbe7ce139d7f3d1a682b9d4c7a9d77e2e Mon Sep 17 00:00:00 2001 From: Shostina Date: Sun, 15 May 2022 22:55:56 +0300 Subject: [PATCH 08/11] add tax and zip graph --- graph/assemblygraph.cpp | 496 +++++++++++++++++++- graph/assemblygraph.h | 28 +- graph/debruijnedge.cpp | 3 + graph/debruijnedge.h | 5 +- graph/debruijnnode.cpp | 69 +++ graph/debruijnnode.h | 37 +- graph/graphicsitemedge.cpp | 9 + graph/graphicsitemnode.cpp | 126 ++++- graph/graphicsitemnode.h | 1 + graph/ogdfnode.h | 10 +- program/globals.h | 4 +- program/graphlayoutworker.cpp | 14 +- program/graphlayoutworker.h | 4 + program/settings.cpp | 7 + program/settings.h | 18 +- taxonomy/TaxData.cpp | 114 +++++ taxonomy/TaxData.h | 30 ++ taxonomy/tax.cpp | 54 +++ taxonomy/tax.h | 48 ++ ui/mainwindow.cpp | 267 ++++++++++- ui/mainwindow.h | 9 + ui/mainwindow.ui | 850 +++++++++++++++++++++------------- ui/taxinfodialog.cpp | 97 ++++ ui/taxinfodialog.h | 28 ++ ui/taxinfodialog.ui | 36 ++ 25 files changed, 1978 insertions(+), 386 deletions(-) create mode 100644 taxonomy/TaxData.cpp create mode 100644 taxonomy/TaxData.h create mode 100644 taxonomy/tax.cpp create mode 100644 taxonomy/tax.h create mode 100644 ui/taxinfodialog.cpp create mode 100644 ui/taxinfodialog.h create mode 100644 ui/taxinfodialog.ui diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index 85e25797..167ebc98 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -55,6 +55,8 @@ AssemblyGraph::AssemblyGraph() : m_hiCEdgeArray = new ogdf::EdgeArray(*m_ogdfGraph); m_graphAttributes = new ogdf::GraphAttributes(*m_ogdfGraph, ogdf::GraphAttributes::nodeGraphics | ogdf::GraphAttributes::edgeGraphics); + tax* root = new tax(0, "root", 0, NULL); + m_taxData = new TaxData(root); clearGraphInfo(); } @@ -64,6 +66,7 @@ AssemblyGraph::~AssemblyGraph() delete m_edgeArray; delete m_hiCEdgeArray; delete m_ogdfGraph; + delete m_taxData; g_hicSettings.reset(new HiCSettings()); } @@ -98,6 +101,9 @@ void AssemblyGraph::cleanUp() m_contiguitySearchDone = false; g_hicSettings.reset(new HiCSettings()); + tax* root = new tax(0, "root", 0, NULL); + m_taxData = new TaxData(root); + clearGraphInfo(); } @@ -361,7 +367,7 @@ void AssemblyGraph::resetAllNodeColours() while (i.hasNext()) { i.next(); - if (i.value()->getGraphicsItemNode() != 0) + if (i.value()->isDrawn() && i.value()->getGraphicsItemNode() != 0) i.value()->getGraphicsItemNode()->setNodeColour(); } } @@ -603,6 +609,53 @@ bool AssemblyGraph::loadHiC(QString filename, QString* errormsg) } +bool AssemblyGraph::loadTax(QString filename, QString* errormsg) +{ + QFile taxFile(filename); + if (!taxFile.open(QIODevice::ReadOnly)) + { + *errormsg = "Unable to read from specified file."; + return false; + } + + QTextStream in(&taxFile); + QApplication::processEvents(); + QString line; + int maxWeight = 0; + + while ((line = in.readLine()) != "") { + QStringList data = line.split(QRegExp("\\|")); + if (data.size() < 2) { + break; + } + QString nodeName = data[0]; + QString namePositive = convertNormalNumberStringToBandageNodeName(nodeName); + QString nameOpposite = getOppositeNodeName(namePositive); + + if (!m_deBruijnGraphNodes.contains(namePositive) || + !m_deBruijnGraphNodes.contains(nameOpposite)) + continue; + DeBruijnNode* node = m_deBruijnGraphNodes[namePositive]; + DeBruijnNode* negNode = m_deBruijnGraphNodes[nameOpposite]; + QVector> curTaxData; + for (int i = 1; i < data.size(); i++) { + QStringList data1 = data[i].split(QRegExp(",")); + if (data1.size() < 3) { + break; + } + unsigned int taxId = data1[0].toUInt(); + QString taxName = data1[2]; + QPair pair = qMakePair(taxName, taxId); + curTaxData.push_back(pair); + } + tax* curTax = m_taxData->addTax(&curTaxData); + m_taxData->addDeBruineNode(node, curTax); + node->setTax(curTax); + negNode->setTax(curTax); + } + m_taxData->calcStatistic(); +} + //This function takes a normal number string like "5" or "-6" and changes //it to "5+" or "6-" - the format of Bandage node names. QString AssemblyGraph::convertNormalNumberStringToBandageNodeName(QString number) @@ -1836,6 +1889,7 @@ bool AssemblyGraph::loadGraphFromFile(QString filename) } void AssemblyGraph::findComponents() { + g_settings->wasComponentsFound = true; QMapIterator i(m_deBruijnGraphNodes); int componentId = 1; while (i.hasNext()) @@ -1843,44 +1897,363 @@ void AssemblyGraph::findComponents() { i.next(); DeBruijnNode * node = i.value(); if (node->getComponentId() == 0) { - QPair res = dfs(node, componentId); - g_hicSettings->componentSize.append(res.second); - g_hicSettings->averageSize.append(res.second / (unsigned long)res.first); - componentId++; + std::vector mergedNode; + QPair res = dfsComponent(node, componentId, &mergedNode); + if (res.first != 0) { + g_hicSettings->componentSize.append(res.second); + g_hicSettings->averageSize.append(res.second / (unsigned long)res.first); + componentId++; + } } + } g_hicSettings->componentNum = componentId - 1; + } -QPair AssemblyGraph::dfs(DeBruijnNode * node, int componentId) { +unsigned int getMaxParentDepth(DeBruijnNode* node) { + unsigned int maxParentDepth = 0; + for (DeBruijnEdge* edge : node->getEnteringEdges()) { + DeBruijnNode* parent = edge->getStartingNode(); + if (parent->getDepth() > maxParentDepth) { + maxParentDepth = parent->getDepth(); + } + } + return maxParentDepth; +} + +unsigned int getMaxParentLength(DeBruijnNode* node) { + unsigned int maxParentLen = 0; + for (DeBruijnEdge* edge : node->getEnteringEdges()) { + DeBruijnNode* parent = edge->getStartingNode(); + if (parent->getLength() > maxParentLen) { + maxParentLen = parent->getLength(); + } + } + return maxParentLen; +} + +unsigned int getMaxChildDepth(DeBruijnNode* node) { + unsigned int maxChildDepth = 0; + for (DeBruijnEdge* edge : node->getLeavingEdges()) { + DeBruijnNode* child = edge->getEndingNode(); + if (child->getDepth() > maxChildDepth) { + maxChildDepth = child->getDepth(); + } + } + return maxChildDepth; +} + +unsigned int getMaxChildLength(DeBruijnNode* node) { + unsigned int maxChildLen = 0; + for (DeBruijnEdge* edge : node->getLeavingEdges()) { + DeBruijnNode* child = edge->getEndingNode(); + if (child->getLength() > maxChildLen) { + maxChildLen = child->getLength(); + } + } + return maxChildLen; +} + +bool AssemblyGraph::deleteLeafIfNeeded(DeBruijnNode* node) { + if (node->getLeavingEdges().size() == 0 && node->getEnteringEdges().size() != 0) { + unsigned int maxParentLen = getMaxParentLength(node); + unsigned int maxParentDepth = getMaxParentDepth(node); + if (node->getLength() < 1000/* && node->getLength() < maxParentLen*/ && + node->getDepth() <= maxParentDepth / 2) { + deleteNode(node); + return true; + } + } + if (node->getEnteringEdges().size() == 0 && node->getLeavingEdges().size() != 0) { + unsigned int maxChildLen = getMaxChildLength(node); + unsigned int maxChildDepth = getMaxChildDepth(node); + if (node->getLength() < 1000/* && node->getLength() < maxChildLen*/ && + node->getDepth() <= maxChildDepth / 2) { + deleteNode(node); + return true; + } + } + return false; +} + +QPair AssemblyGraph::dfsComponent(DeBruijnNode * node, int componentId, std::vector* mergedNode) { unsigned long size = 0; unsigned int contigCount = 0; if (node->getComponentId() == 0) { node->setComponentId(componentId); node->getReverseComplement()->setComponentId(componentId); - size += node->getLength(); - contigCount += 1; + if (node->getNameWithoutSign().endsWith("_start")) { g_hicSettings->addTargetComponentIfNeeded(componentId); } + else { + if (deleteLeafIfNeeded(node)) { + return qMakePair(contigCount, size); + } + if (markChainIfNeeded(node)) { + mergedNode->push_back(node); + return qMakePair(contigCount, size); + } + } for (DeBruijnEdge* edge : node->getLeavingEdges()) { if (edge->getEndingNode()->getComponentId() == 0 && !edge->isHiC()) { - QPair res = dfs(edge->getEndingNode(), componentId); + QPair res = dfsComponent(edge->getEndingNode(), componentId, mergedNode); size += res.second; contigCount += res.first; } } for (DeBruijnEdge* edge : node->getEnteringEdges()) { if (edge->getStartingNode()->getComponentId() == 0 && !edge->isHiC()) { - QPair res = dfs(edge->getStartingNode(), componentId); + QPair res = dfsComponent(edge->getStartingNode(), componentId, mergedNode); size += res.second; contigCount += res.first; } } - } + if (!(node->getNameWithoutSign().endsWith("_start"))) { + if (deleteLeafIfNeeded(node)) { + return qMakePair(contigCount, size); + } + } + size += node->getLength(); + contigCount += 1; + } return qMakePair(contigCount, size); } +void AssemblyGraph::dfsTax(DeBruijnNode* node, unsigned int taxId, int rank, int distance) { + tax* curTax = node->getTax(rank); + if ((!node->m_visited) && (curTax == NULL || curTax->getTaxId() == taxId)) { + node->m_visited = true; + node->getReverseComplement()->m_visited = true; + if (g_settings->taxDistance == -1 || distance <= g_settings->taxDistance) { + for (int i = 0; i < node->getEdgesPointer()->size(); i++) { + DeBruijnEdge* edge = (*node->getEdgesPointer())[i]; + if (!edge->isHiC()) { + dfsTax(edge->getEndingNode(), taxId, rank, distance + 1); + } + if (g_settings->displayAroundTaxWithHiC) { + if (edge->isHiC() && g_hicSettings->isDrawn(edge) && curTax != NULL && curTax->getTaxId() == g_settings->taxId) { + DeBruijnNode* otherNode = edge->getOtherNode(node); + tax* otherNodeTax = otherNode->getTax(rank); + if (!otherNode->m_visited && otherNodeTax != NULL && otherNodeTax->getTaxId() != taxId) { + dfsTax(otherNode, otherNodeTax->getTaxId(), rank, 0); + } + } + } + } + } + } +} + +void AssemblyGraph::makeZipped(int minSize) { + QMapIterator i(m_deBruijnGraphNodes); + int unionId = 0; + while (i.hasNext()) + { + i.next(); + DeBruijnNode* node = i.value(); + + if (node->isPositiveNode() && !((node->getName()).endsWith("_start")) && !node->isNodeUnion() && !node->isZipped() && node->getLength() >= minSize) { + QList zippedNodes1; + QList mainNodes1; + int boundLen = min(minSize, (i.value()->getLength()) / 4); + for (DeBruijnEdge* edge : node->getEnteringEdges()) { + DeBruijnNode* otherNode = edge->getOtherNode(node); + if (!otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { + dfsZipped(otherNode, boundLen, &mainNodes1, &zippedNodes1); + } + } + + if (mainNodes1.size() > 0 && zippedNodes1.size() > 1) { + createNodesUnion(mainNodes1, zippedNodes1, "union" + QString::number(unionId)); + unionId++; + } + + if (mainNodes1.size() > 0 && zippedNodes1.size() == 1) { + zippedNodes1[0]->setAsDrawn(); + zippedNodes1[0]->setZipped(false); + unionId++; + } + + QList zippedNodes2; + QList mainNodes2; + for (DeBruijnEdge* edge : node->getLeavingEdges()) { + DeBruijnNode* otherNode = edge->getOtherNode(node); + if (!otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { + dfsZipped(otherNode, boundLen, &mainNodes2, &zippedNodes2); + } + } + + if (mainNodes2.size() > 0 && zippedNodes2.size() > 1) { + createNodesUnion(mainNodes2, zippedNodes2, "union" + QString::number(unionId)); + unionId++; + } + + if (mainNodes2.size() > 0 && zippedNodes2.size() == 1) { + zippedNodes2[0]->setAsDrawn(); + zippedNodes2[0]->setZipped(false); + unionId++; + } + } + if ((node->isPositiveNode() || g_settings->doubleMode) && (node->getLength() >= minSize || (node->getName()).endsWith("_start"))) { + node->setAsDrawn(); + } + } +} + +void AssemblyGraph::createNodesUnion(QList mainNodes, QList zippedNodes, QString unionName) { + double nodeDepth = getMeanDepth(zippedNodes); + int length = 0; + for (DeBruijnNode* node : zippedNodes) { + length += (node->getLength()); + } + QString newPosNodeName = unionName + "+"; + QString newNegNodeName = unionName + "-"; + DeBruijnNode* posNode = new DeBruijnNode(newPosNodeName, nodeDepth, "", length); + DeBruijnNode* negNode = new DeBruijnNode(newNegNodeName, nodeDepth, "", length); + m_deBruijnGraphNodes.insert(newPosNodeName, posNode); + m_deBruijnGraphNodes.insert(newNegNodeName, negNode); + for (DeBruijnNode* node : mainNodes) { + for (DeBruijnEdge* edge : node->getEnteringEdges()) { + DeBruijnNode* otherNode = edge->getOtherNode(node); + if (otherNode->getPositiveNode()->isZipped() && zippedNodes.contains(otherNode->getPositiveNode())) { + createDeBruijnEdge(newPosNodeName, node->getName()); + break; + } + } + for (DeBruijnEdge* edge : node->getLeavingEdges()) { + DeBruijnNode* otherNode = edge->getOtherNode(node); + if (otherNode->getPositiveNode()->isZipped() && zippedNodes.contains(otherNode->getPositiveNode())) { + createDeBruijnEdge(node->getName(), newPosNodeName); + break; + } + } + } + posNode->setNodeUnion(true); + negNode->setNodeUnion(true); + posNode->setReverseComplement(negNode); + negNode->setReverseComplement(posNode); + posNode->setAsDrawn(); + if (g_settings->doubleMode) + negNode->setAsDrawn(); + posNode->setZippedNodes(zippedNodes); + negNode->setZippedNodes(zippedNodes); +} + +void AssemblyGraph::unzipSelectedNodes(DeBruijnNode* unionNode) { + QListzippedNodes = unionNode->getZippedNodes(); + + ogdf::node ogdfUnionNode = unionNode->getOgdfNode()->getFirst(); + double xPos = m_graphAttributes->x(ogdfUnionNode); + double yPos = m_graphAttributes->y(ogdfUnionNode); + + bool isLinearLayout = g_settings->linearLayout; + g_settings->linearLayout = true; + for (int i = 0; i < zippedNodes.size(); i++) { + DeBruijnNode* node = zippedNodes[i]; + node->setAsDrawn(); + //node->setZipped(false); + node->m_isNew = true; + node->addToOgdfGraph(m_ogdfGraph, m_graphAttributes, m_edgeArray, xPos, yPos); + } + for (int i = 0; i < zippedNodes.size(); i++) { + const std::vector* nodeEdges = zippedNodes[i]->getEdgesPointer(); + for (size_t i = 0; i < nodeEdges->size(); ++i) { + DeBruijnEdge* edge = (*nodeEdges)[i]; + edge->determineIfDrawn(); + if (!edge->isHiC() && edge->isDrawn()) + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); + } + } + + unionNode->setAsNotDrawn(); + + g_settings->linearLayout = isLinearLayout; +} + +void AssemblyGraph::dfsZipped(DeBruijnNode* curNode, int boundLen, QList* mainNodes, QList* zippedNodes) { + if (!curNode->isZipped()) { + curNode = curNode->getPositiveNode(); + if (!curNode->isDrawn() && curNode->getLength() < boundLen) { + curNode->setZipped(true); + curNode->getReverseComplement()->setZipped(true); + zippedNodes->append(curNode); + for (DeBruijnEdge* edge : curNode->getLeavingEdges()) { + if (!(edge->getEndingNode()->isZipped()) && !edge->isHiC()) { + dfsZipped(edge->getEndingNode(), boundLen, mainNodes, zippedNodes); + } + } + for (DeBruijnEdge* edge : curNode->getEnteringEdges()) { + if (!(edge->getStartingNode()->isZipped()) && !edge->isHiC()) { + dfsZipped(edge->getStartingNode(), boundLen, mainNodes, zippedNodes); + } + } + } + else if (curNode->getLength() >= boundLen && !(mainNodes -> contains(curNode))) { + mainNodes -> append(curNode); + curNode->setAsDrawn(); + if (g_settings->doubleMode) { + curNode->getReverseComplement()->setAsDrawn(); + } + } + } +} + +void AssemblyGraph::calcHiCLinkForTax() { + QMapIterator i(m_deBruijnGraphNodes); + while (i.hasNext()) + { + i.next(); + DeBruijnNode* node = i.value(); + tax* currentTax = node->getTax(); + if (currentTax != NULL) { + const std::vector* nodeEdges = node->getEdgesPointer(); + for (size_t i = 0; i < nodeEdges->size(); ++i) { + DeBruijnEdge* edge = (*nodeEdges)[i]; + if (edge->isHiC()) { + currentTax = node->getTax(); + DeBruijnNode* otherNode = edge->getOtherNode(node); + tax* otherTax = otherNode->getTax(); + while (currentTax!= NULL && otherTax != NULL) { + if (currentTax->getRank() == 0 || otherTax->getRank() == 0) { + break; + } + while (currentTax->getRank() > otherTax->getRank()) { + currentTax = currentTax->getPrevTax(); + } + while (otherTax != NULL && otherTax->getRank() > currentTax->getRank()) { + otherTax = otherTax->getPrevTax(); + } + if (currentTax != NULL && otherTax != NULL && currentTax->getRank() == otherTax->getRank()) { + if (currentTax->getTaxId() == otherTax->getTaxId()) { + (currentTax->hicLinksToThemself) += edge->getWeight(); + currentTax->hicLinksWeight += edge->getWeight(); + } + else { + currentTax->hicLinksWeight += edge->getWeight(); + otherTax->hicLinksWeight += edge->getWeight(); + QPair key = m_taxData->getHiCLinksWeightKey(currentTax->getTaxId(), otherTax->getTaxId()); + if (!(currentTax->addWeightInHicLinkedTaxes(otherTax, edge->getWeight()))) { + currentTax->m_hicLinkedTaxes.push_back(qMakePair(otherTax, edge->getWeight())); + otherTax->m_hicLinkedTaxes.push_back(qMakePair(currentTax, edge->getWeight())); + } + } + currentTax = currentTax->getPrevTax(); + otherTax = otherTax->getPrevTax(); + } + } + } + } + } + } +} + +std::vector> AssemblyGraph::getHiCConnectedTaxes(tax* currentTax) { + std::vector> res = currentTax->m_hicLinkedTaxes; + return res; +} + void AssemblyGraph::buildOgdfGraphFromNodesAndEdgesWithHiC(std::vector startingNodes, int nodeDistance) { m_ogdfGraph = new ogdf::Graph(); m_edgeArray = new ogdf::EdgeArray(*m_ogdfGraph); @@ -1905,7 +2278,19 @@ void AssemblyGraph::buildOgdfGraphFromNodesAndEdges(std::vector //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(); + i.value()->setAsDrawn(); + int componentId = i.value()->getComponentId(); + if (componentId != 0 && g_settings->aroundTargetNodes) { + if (!(g_hicSettings->isTargetComponent(i.value()->getComponentId()) || + g_hicSettings->isConnectedWithTargetComponent(i.value()->getComponentId()))) { + i.value()->setAsNotDrawn(); + } + } + if (componentId != 0 && g_settings->onlyBigComponent) { + if (!g_hicSettings->isBigComponent(componentId)) { + i.value()->setAsNotDrawn(); + } + } } } } @@ -2076,9 +2461,63 @@ void AssemblyGraph::setInclusionFilterAuto() { } } +void AssemblyGraph::buildOgdfGraphWithTaxFilter(unsigned int taxId, int distance) { + tax* curTax = m_taxData->m_taxMap[taxId]; + if (curTax == NULL) { + return; + } + int rank = curTax->getRank(); + QMapIterator i(m_deBruijnGraphNodes); + while (i.hasNext()) + { + i.next(); + if ((i.value()->isPositiveNode() || g_settings->doubleMode) && i.value()->getTax() != NULL && !(i.value()->isDrawn())) { + tax* tempTax = i.value()->getTax()->getTaxHierarchy(rank); + if (tempTax != NULL && tempTax->getTaxId() == taxId) { + dfsTax(i.value(), taxId, rank, 0); + } + } + } + + QMapIterator ii(m_deBruijnGraphNodes); + while (ii.hasNext()) + { + ii.next(); + DeBruijnNode* node = ii.value(); + if (node->m_visited) { + node->m_visited = false; + if ((node->isPositiveNode() || g_settings->doubleMode) && node->notInOgdf()) { + node->setAsDrawn(); + node->addToOgdfGraph(m_ogdfGraph, m_graphAttributes, m_edgeArray, 0.0, 0.0); + } + } + } + + QMapIterator, DeBruijnEdge*> j(m_deBruijnGraphEdges); + while (j.hasNext()) + { + j.next(); + DeBruijnEdge* edge = j.value(); + edge->determineIfDrawn(); + if (!edge->isHiC() && edge->isDrawn()) { + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); + } + } + + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + edge->setDrawn(g_hicSettings->isDrawnWithNode(edge)); + if (edge->isDrawn()) { + edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); + } + } +} + void AssemblyGraph::buildOgdfGraphWithAutoParameters(std::vector startingNodes) { - g_settings->hicDrawingType = ALL_EDGES; g_settings->hicEdgeLength = 200; g_settings->hicEdgeWidth = 7; @@ -2098,7 +2537,8 @@ void AssemblyGraph::buildOgdfGraphWithAutoParameters(std::vector if (i.value()->isPositiveNode() || g_settings->doubleMode) { int componentId = i.value()->getComponentId(); if (componentId != 0) { - if (!i.value()->isDrawn() && (g_hicSettings->isTargetComponent(i.value()->getComponentId()) || g_hicSettings->isConnectedWithTargetComponent(i.value() -> getComponentId()))) { + if (!i.value()->isDrawn() && (g_hicSettings->isTargetComponent(i.value()->getComponentId()) || + g_hicSettings->isConnectedWithTargetComponent(i.value() -> getComponentId()))) { i.value()->setAsDrawn(); } } @@ -2154,7 +2594,6 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) { i.next(); DeBruijnNode * node = i.value(); - if (node->isDrawn()) { if (meanDrawnDepth == 0) @@ -2177,7 +2616,7 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) { j.next(); DeBruijnEdge * edge = j.value(); - + edge->determineIfDrawn(); if (edge->isDrawn() && !edge->isHiC()) { GraphicsItemEdge * graphicsItemEdge = new GraphicsItemEdge(edge); @@ -2187,10 +2626,6 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) } } - //std::ofstream vmdelet_out; - //vmdelet_out.open("C\:\\Users\\anastasia\\study\\maga\\Bandage\\addGraphicsItemsToScene.txt", std::ios::app); - //vmdelet_out << "before add hiC into GraphicsItemsToScene. m_hiCDeBruijnGraphEdges len = " << m_hiCDeBruijnGraphEdges.size() << '\n'; - //vmdelet_out.close(); if (!m_hiCDeBruijnGraphEdges.empty()) { QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); while (j_hiC.hasNext()) @@ -2215,8 +2650,11 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) { k.next(); DeBruijnNode * node = k.value(); - if (node->hasGraphicsItem()) + if (node->isDrawn() && node->hasGraphicsItem()) scene->addItem(node->getGraphicsItemNode()); + if (!g_settings->addNewNodes && node->m_isNew) { + node->m_isNew = false; + } } } @@ -2774,6 +3212,13 @@ int AssemblyGraph::getDrawnNodeCount() const return nodeCount; } +void AssemblyGraph::deleteNode(DeBruijnNode* node) { + m_deBruijnGraphNodes.remove(node->getName()); + m_deBruijnGraphNodes.remove(node->getReverseComplement()->getName()); + deleteEdges(node->getEdgesPointer()); + deleteEdges(node->getReverseComplement()->getEdgesPointer()); + //delete node; +} void AssemblyGraph::deleteNodes(std::vector * nodes) { @@ -2829,7 +3274,7 @@ void AssemblyGraph::deleteNodes(std::vector * nodes) } } -void AssemblyGraph::deleteEdges(std::vector * edges) +void AssemblyGraph::deleteEdges(const std::vector * edges) { //Build a list of edges to delete. QList edgesToDelete; @@ -2982,7 +3427,6 @@ void AssemblyGraph::duplicateGraphicsNode(DeBruijnNode * originalNode, DeBruijnN } } - //This function will merge the given nodes, if possible. Nodes can only be //merged if they are in a simple, unbranching path with no extra edges. If the //merge is successful, it returns true, otherwise false. @@ -3077,7 +3521,8 @@ bool AssemblyGraph::mergeNodes(QList nodes, MyGraphicsScene * sc DeBruijnNode * newPosNode = new DeBruijnNode(newPosNodeName, mergedNodeDepth, mergedNodePosSequence); DeBruijnNode * newNegNode = new DeBruijnNode(newNegNodeName, mergedNodeDepth, mergedNodeNegSequence); - + newPosNode->setComponentId(orderedList[0]->getComponentId()); + newNegNode->setComponentId(orderedList[0]->getComponentId()); newPosNode->setReverseComplement(newNegNode); newNegNode->setReverseComplement(newPosNode); @@ -4119,4 +4564,5 @@ bool AssemblyGraph::useLinearLayout() const { return true; else return g_settings->linearLayout; -} \ No newline at end of file +} + diff --git a/graph/assemblygraph.h b/graph/assemblygraph.h index 4c0fcc40..fb0511e2 100644 --- a/graph/assemblygraph.h +++ b/graph/assemblygraph.h @@ -31,6 +31,8 @@ #include "../ui/mygraphicsscene.h" #include "path.h" #include +#include "../taxonomy/TaxData.h" +#include "../taxonomy/Tax.h" class DeBruijnNode; class DeBruijnEdge; @@ -56,6 +58,7 @@ class AssemblyGraph : public QObject ogdf::EdgeArray * m_edgeArray; ogdf::EdgeArray * m_hiCEdgeArray; ogdf::GraphAttributes * m_graphAttributes; + TaxData* m_taxData; int m_kmer; int m_nodeCount; @@ -137,7 +140,7 @@ class AssemblyGraph : public QObject int getDrawnNodeCount() const; void deleteNodes(std::vector * nodes); - void deleteEdges(std::vector * edges); + void deleteEdges(const std::vector * edges); void duplicateNodePair(DeBruijnNode * node, MyGraphicsScene * scene); bool mergeNodes(QList nodes, MyGraphicsScene * scene, bool recalulateDepth); @@ -172,9 +175,15 @@ class AssemblyGraph : public QObject long long getTotalLengthOrphanedNodes() const; bool useLinearLayout() const; bool loadHiC(QString filename, QString* errormsg); + bool loadTax(QString filename, QString* errormsg); void buildOgdfGraphFromNodesAndEdgesWithHiC(std::vector startingNodes, int nodeDistance); void addOneHiCBetweenComponent(std::vector startingNodes); - + void buildOgdfGraphWithTaxFilter(unsigned int taxId, int distance = -1); + void AssemblyGraph::makeZipped(int minSize); + void AssemblyGraph::calcHiCLinkForTax(); + std::vector> getHiCConnectedTaxes(tax* currentTax); + void findComponents(); + void unzipSelectedNodes(DeBruijnNode* unionNode); private: template double getValueUsingFractionalIndex(std::vector * v, double index) const; @@ -213,10 +222,19 @@ class AssemblyGraph : public QObject double findDepthAtIndex(QList * nodeList, long long targetIndex) const; bool allNodesStartWith(QString start) const; QString simplifyCanuNodeName(QString oldName) const; - QPair dfs(DeBruijnNode* node, int componentId); - void findComponents(); + QPair dfsComponent(DeBruijnNode* node, int componentId, std::vector* mergedNode); + void dfsTax(DeBruijnNode* node, unsigned int taxId, int rank, int distance); void addHiCEdges(std::vector startingNodes); - void AssemblyGraph::setInclusionFilterAuto(); + void setInclusionFilterAuto(); + void deleteNode(DeBruijnNode* node); + bool deleteLeafIfNeeded(DeBruijnNode* node); + bool markChainIfNeeded(DeBruijnNode* node); + bool mergeMiddleNodeIfNeeded(DeBruijnNode* startingNode, DeBruijnNode* endingNode, DeBruijnNode* deletedNode); + void fixChainIfNeeded(std::vector* mergedNode); + void bfsAroundNode(DeBruijnNode* startNode); + void dfsZipped(DeBruijnNode* curNode, int boundLen, QList* mainNodes, QList* zippedNodes); + void createNodesUnion(QList mainNodes, QList zippedNodes, QString unionName); + double AssemblyGraph::getHiCMinNormalizedWeightByTax(); signals: void setMergeTotalCount(int totalCount); diff --git a/graph/debruijnedge.cpp b/graph/debruijnedge.cpp index 2fdc415e..5f58f3bc 100644 --- a/graph/debruijnedge.cpp +++ b/graph/debruijnedge.cpp @@ -147,6 +147,9 @@ void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArraygetComponentId() != m_endingNode->getComponentId())) { ogdf::edge newEdge = ogdfGraph->newEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); (*edgeArray)[newEdge] = isHiC() ? g_settings->hicEdgeLength : g_settings->edgeLength; + if (m_startingNode->isNodeUnion() || m_endingNode->isNodeUnion()) { + (*edgeArray)[newEdge] += (g_settings->averageNodeWidth / 2.0); + } } } diff --git a/graph/debruijnedge.h b/graph/debruijnedge.h index 19a2d672..ba5b1f1d 100644 --- a/graph/debruijnedge.h +++ b/graph/debruijnedge.h @@ -71,6 +71,9 @@ class DeBruijnEdge void autoDetermineExactOverlap(); void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArray * edgeArray) const; void setHiC(bool hiC, int weight) { m_HiC = hiC; m_weight = weight; } + //void setOgdfEdge(ogdf::edge ogdfEdge) { m_ogdfEdge = ogdfEdge; }; + //ogdf::edge getOgdfEdge() { return m_ogdfEdge; }; + //ogdf::edge m_ogdfEdge; private: DeBruijnNode * m_startingNode; @@ -82,7 +85,7 @@ class DeBruijnEdge int m_overlap; bool m_HiC; int m_weight; - + bool edgeIsVisible() const; int timesNodeInPath(DeBruijnNode * node, std::vector * path) const; std::vector findNextEdgesInPath(DeBruijnNode * nextNode, diff --git a/graph/debruijnnode.cpp b/graph/debruijnnode.cpp index 0b9a7b57..d79ad454 100644 --- a/graph/debruijnnode.cpp +++ b/graph/debruijnnode.cpp @@ -101,6 +101,12 @@ void DeBruijnNode::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes //Create the OgdfNode object m_ogdfNode = new OgdfNode(); + if (m_isNodesUnion) { + ogdf::node newNode = ogdfGraph->newNode(); + m_ogdfNode->addOgdfNode(newNode); + return; + } + //Each node in the Velvet sense is made up of multiple nodes in the //OGDF sense. This way, Velvet nodes appear as lines whose length //corresponds to the sequence length. @@ -132,6 +138,47 @@ void DeBruijnNode::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes } } +void DeBruijnNode::addToOgdfGraph(ogdf::Graph* ogdfGraph, ogdf::GraphAttributes* graphAttributes, + ogdf::EdgeArray* edgeArray, ogdf::GraphAttributes* oldGraphAttribute) +{ + //If this node or its reverse complement is already in OGDF, then + //it's not necessary to make the node. + if (thisOrReverseComplementInOgdf() || m_isNodesUnion) + return; + + //Create the OgdfNode object + + //m_ogdfNode = new OgdfNode(); + //OgdfNode* oldOgdfNode = m_ogdfNode; + + double drawnNodeLength = getDrawnNodeLength(); + int numberOfGraphEdges = getNumberOfOgdfGraphEdges(drawnNodeLength); + int numberOfGraphNodes = numberOfGraphEdges + 1; + double drawnLengthPerEdge = drawnNodeLength / numberOfGraphEdges; + + ogdf::node newNode = 0; + ogdf::node previousNode = 0; + for (int i = 0; i < numberOfGraphNodes; ++i) + { + newNode = ogdfGraph->newNode(); + //m_ogdfNode->addOgdfNode(newNode); + + if (g_assemblyGraph->useLinearLayout()) { + graphAttributes->x(newNode) = oldGraphAttribute->x(m_ogdfNode->m_ogdfNodes[i]); + graphAttributes->y(newNode) = oldGraphAttribute->y(m_ogdfNode->m_ogdfNodes[i]); + } + + if (i > 0) + { + ogdf::edge newEdge = ogdfGraph->newEdge(previousNode, newNode); + (*edgeArray)[newEdge] = drawnLengthPerEdge; + } + + previousNode = newNode; + } +} + + double DeBruijnNode::getDrawnNodeLength() const @@ -843,3 +890,25 @@ QColor DeBruijnNode::getCustomColourForDisplay() const return m_reverseComplement->getCustomColour(); return g_settings->defaultCustomNodeColour; } + +bool DeBruijnNode::hasTax(unsigned int taxId, int rank) { + if (getTax() == NULL) { + return false; + } + tax* foundTax = getTax()->getTaxHierarchy(rank); + return foundTax != NULL && foundTax->getTaxId() == taxId; +} + +bool DeBruijnNode::hasTax(unsigned int taxId) { + if (getTax() == NULL) { + return false; + } + return getTax()->hasTax(taxId); +} + +tax* DeBruijnNode::getTax(int rank) { + if (getTax() == NULL) { + return NULL; + } + return getTax()->getTaxHierarchy(rank); +} diff --git a/graph/debruijnnode.h b/graph/debruijnnode.h index b70b596d..71e17492 100644 --- a/graph/debruijnnode.h +++ b/graph/debruijnnode.h @@ -27,6 +27,7 @@ #include #include "../blast/blasthitpart.h" #include "../program/settings.h" +#include "../taxonomy/tax.h" class OgdfNode; class DeBruijnEdge; @@ -67,7 +68,13 @@ class DeBruijnNode std::vector getUpstreamNodes() const; std::vector getAllConnectedPositiveNodes() const; bool isSpecialNode() const {return m_specialNode;} - bool isDrawn() const {return m_drawn;} + bool isDrawn() const {return m_drawn && !m_isNodesUnion && !(g_settings->makeZip) || + m_drawn && g_settings->makeZip && (!m_zipped || m_isNew);} + bool getDrawn() { return m_drawn; }; + bool isZipped() { return m_zipped; }; + bool isNodeUnion() { return m_isNodesUnion; }; + void setZipped(bool zipped) { m_zipped = zipped; }; + void setNodeUnion(bool nodeUnion) { m_isNodesUnion = nodeUnion; }; bool thisNodeOrReverseComplementIsDrawn() const {return isDrawn() || getReverseComplement()->isDrawn();} bool isNotDrawn() const {return !m_drawn;} QColor getCustomColour() const {return m_customColour;} @@ -77,6 +84,9 @@ class DeBruijnNode bool hasCustomColour() const {return m_customColour.isValid();} bool isPositiveNode() const; bool isNegativeNode() const; + DeBruijnNode* getPositiveNode() { + return (isPositiveNode() ? this : getReverseComplement()); + } bool inOgdf() const {return m_ogdfNode != 0;} bool notInOgdf() const {return m_ogdfNode == 0;} bool thisOrReverseComplementInOgdf() const {return (inOgdf() || getReverseComplement()->inOgdf());} @@ -119,6 +129,8 @@ class DeBruijnNode void removeEdge(DeBruijnEdge * edge); void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes * graphAttributes, ogdf::EdgeArray * edgeArray, double xPos, double yPos); + void addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes * graphAttributes, + ogdf::EdgeArray * edgeArray, ogdf::GraphAttributes* oldGraphAttribute); void determineContiguity(); void clearBlastHits() {m_blastHits.clear();} void addBlastHit(BlastHit * newHit) {m_blastHits.push_back(newHit);} @@ -128,7 +140,22 @@ class DeBruijnNode void setDepth(double newDepth) {m_depth = newDepth;} void setName(QString newName) {m_name = newName;} void setComponentId(int componentId) {m_componentId = componentId;} + void setDeleted(bool isDeleted) { m_deleted = isDeleted; } + bool isDeleted() { return m_deleted; } + void setTax(tax* taxInfo) { m_taxInfo = taxInfo; }; + tax* getTax() { return m_taxInfo; }; + bool hasTax(unsigned int taxId, int rank); + bool hasTax(unsigned int taxId); + tax* getTax(int rank); + QColor getTaxPropColor() { return m_taxPropagateColor; }; + void setTaxPropColor(QColor color) { m_taxPropagateColor = color; }; + + bool m_visited = false; + QList getZippedNodes() { return m_zippedNodes; }; + void setZippedNodes(QList zippedNodes) { m_zippedNodes = zippedNodes; }; + bool m_isNew = false; + QColor m_lastColor; private: QString m_name; @@ -143,6 +170,10 @@ class DeBruijnNode std::vector m_edges; bool m_specialNode; bool m_drawn = false; + + bool m_zipped = false; + bool m_isNodesUnion = false; + int m_highestDistanceInNeighbourSearch; QColor m_customColour; QString m_customLabel; @@ -151,6 +182,10 @@ class DeBruijnNode QString getNodeNameForFasta(bool sign) const; QByteArray getUpstreamSequence(int upstreamSequenceLength) const; int m_componentId = 0; + bool m_deleted = false; + tax* m_taxInfo = NULL; + QColor m_taxPropagateColor = NAN; + QList m_zippedNodes; double getNodeLengthPerMegabase() const; bool isOnlyPathInItsDirection(DeBruijnNode * connectedNode, diff --git a/graph/graphicsitemedge.cpp b/graph/graphicsitemedge.cpp index 74a656fc..b52c83cd 100644 --- a/graph/graphicsitemedge.cpp +++ b/graph/graphicsitemedge.cpp @@ -40,6 +40,9 @@ GraphicsItemEdge::GraphicsItemEdge(DeBruijnEdge * deBruijnEdge, QGraphicsItem * QPointF GraphicsItemEdge::extendLine(QPointF start, QPointF end, double extensionLength) { + if (QLineF(start, end).length() == 0) { + return end; + } double extensionRatio = extensionLength / QLineF(start, end).length(); QPointF difference = end - start; difference *= extensionRatio; @@ -125,6 +128,12 @@ void GraphicsItemEdge::calculateAndSetPath() QPainterPath path; path.moveTo(m_startingLocation); path.cubicTo(m_controlPoint1, m_controlPoint2, m_endingLocation); + /*if () { + path.lineTo(m_endingLocation); + } + else { + path.cubicTo(m_controlPoint1, m_controlPoint2, m_endingLocation); + }*/ setPath(path); } diff --git a/graph/graphicsitemnode.cpp b/graph/graphicsitemnode.cpp index ecec88f2..c14bbc57 100644 --- a/graph/graphicsitemnode.cpp +++ b/graph/graphicsitemnode.cpp @@ -121,10 +121,34 @@ void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem // painter->setPen(QPen(Qt::black, 1.0)); // painter->drawRect(boundingRect()); + if (m_deBruijnNode->isNodeUnion()) { + + int width = g_settings->averageNodeWidth; + int x = m_linePoints[0].x(); + int y = m_linePoints[0].y(); + //QPainter painter; + QColor fillColor = QColor(200, 200, 200); + QBrush brush; + //brush.setStyle(Qt::Sol); + //brush.setColor(fillColor); + painter->setBrush(fillColor); + painter->setPen(QPen(Qt::black, 1.0)); + QRect r(x, y, width, width); + r.moveCenter(m_linePoints[0].toPoint()); + painter->drawEllipse(r); + return; + } + QPainterPath outlinePath = shape(); //Fill the node's colour QBrush brush(m_colour); + if (g_settings->propagateTaxColour && m_deBruijnNode->getTax(g_settings->taxRank) == NULL && + m_colour != QColor(200, 200, 200) && g_settings->nodeColourScheme == COLOUR_BY_TAX) { + brush.setStyle(Qt::Dense2Pattern); + painter->setBrush(brush); + painter->setPen(QPen(Qt::black, 1.0)); + } painter->fillPath(outlinePath, brush); bool nodeHasBlastHits; @@ -300,6 +324,10 @@ void GraphicsItemNode::drawTextPathAtLocation(QPainter * painter, QPainterPath t void GraphicsItemNode::setNodeColour() { + if (g_settings->addNewNodes && !m_deBruijnNode->m_isNew) { + m_colour = m_deBruijnNode->m_lastColor; + return; + } switch (g_settings->nodeColourScheme) { case UNIFORM_COLOURS: @@ -369,6 +397,29 @@ void GraphicsItemNode::setNodeColour() } break; } + case COLOUR_BY_TAX: + { + QColor colour = QColor(200, 200, 200); + if (m_deBruijnNode->getTax() != NULL) { + int rank = g_settings->taxRank; + tax* curTax = m_deBruijnNode->getTax()->getTaxHierarchy(rank); + if (curTax != NULL) { + colour = curTax->getColor(); + } + } + else if (g_settings->propagateTaxColour) { + colour = propagateColour(); + } + m_colour = colour; + DeBruijnNode* revCompNode = m_deBruijnNode->getReverseComplement(); + if (revCompNode != 0) + { + GraphicsItemNode* revCompGraphNode = revCompNode->getGraphicsItemNode(); + if (revCompGraphNode != 0) + revCompGraphNode->m_colour = colour; + } + break; + } case RANDOM_COLOURS: { //Make a colour with a random hue. Assign a colour to both this node and @@ -465,8 +516,56 @@ void GraphicsItemNode::setNodeColour() } } } + m_deBruijnNode->m_lastColor = m_colour; } +QColor GraphicsItemNode::propagateColour() { + int rank = g_settings->taxRank; + QColor color = QColor(200, 200, 200); + if (m_deBruijnNode->getTax(rank) == NULL) { + tax* prevNodeTax = NULL; + bool flag = false; + for (DeBruijnEdge* edge : m_deBruijnNode->getEnteringEdges()) { + DeBruijnNode* prevNode = edge->getStartingNode(); + tax* prevTax = prevNode->getTax(rank); + if (prevTax != NULL) { + if (!flag) { + flag = true; + prevNodeTax = prevTax; + } + else { + if (prevNodeTax->getTaxId() != prevTax->getTaxId()) { + flag = false; + return color; + } + } + } + } + flag = false; + tax* nextNodeTax = NULL; + for (DeBruijnEdge* edge : m_deBruijnNode->getLeavingEdges()) { + DeBruijnNode* nextNode = edge->getEndingNode(); + tax* nextTax = nextNode->getTax(rank); + if (nextTax != NULL) { + if (!flag) { + flag = true; + nextNodeTax = nextTax; + } + else if (nextNodeTax->getTaxId() != nextTax->getTaxId()) { + flag = false; + return color; + } + } + } + if (flag && prevNodeTax!= NULL && nextNodeTax != NULL && prevNodeTax->getTaxId() == nextNodeTax->getTaxId()) { + return prevNodeTax->getColor(); + } + return color; + } + else { + return m_deBruijnNode->getTax(rank)->getColor(); + } +} QPainterPath GraphicsItemNode::shape() const { @@ -906,7 +1005,27 @@ QStringList GraphicsItemNode::getNodeText() nodeText << formatDepthForDisplay(m_deBruijnNode->getDepth()); if (g_settings->displayNodeCsvData && m_deBruijnNode->hasCsvData()) nodeText << m_deBruijnNode->getCsvLine(g_settings->displayNodeCsvDataCol); - + if (g_settings->displayTaxIdName && m_deBruijnNode->getTax() != NULL) { + tax* curTax = m_deBruijnNode->getTax(); + if (curTax != NULL) { + nodeText << curTax->getName() << QString::number(curTax->getTaxId()); + } + } + else { + if (g_settings->displayTaxNameRank && m_deBruijnNode->getTax() != NULL) { + tax* curTax = m_deBruijnNode->getTax(g_settings->taxRank); + if (curTax != NULL) { + nodeText << curTax->getName(); + } + } + if (g_settings->displayTaxIdRank && m_deBruijnNode->getTax() != NULL) { + tax* curTax = m_deBruijnNode->getTax(g_settings->taxRank); + if (curTax != NULL) { + nodeText << QString::number(curTax->getTaxId()); + } + } + } + return nodeText; } @@ -1214,5 +1333,8 @@ bool GraphicsItemNode::anyNodeDisplayText() g_settings->displayNodeNames || g_settings->displayNodeLengths || g_settings->displayNodeDepth || - g_settings->displayNodeCsvData; + g_settings->displayNodeCsvData || + g_settings->displayTaxIdName || + g_settings->displayTaxIdRank || + g_settings->displayTaxNameRank ; } diff --git a/graph/graphicsitemnode.h b/graph/graphicsitemnode.h index 30a65ee8..ea97d63d 100644 --- a/graph/graphicsitemnode.h +++ b/graph/graphicsitemnode.h @@ -95,6 +95,7 @@ class GraphicsItemNode : public QGraphicsItem std::vector getCentres() const; QPointF getCentre(std::vector linePoints) const; void setNodeColour(); + QColor propagateColour(); QStringList getNodeText(); QSize getNodeTextSize(QString text); QColor getDepthColour(); diff --git a/graph/ogdfnode.h b/graph/ogdfnode.h index c7fd6c96..3df4c16c 100644 --- a/graph/ogdfnode.h +++ b/graph/ogdfnode.h @@ -34,9 +34,15 @@ class OgdfNode void addOgdfNode(ogdf::node newNode) {m_ogdfNodes.push_back(newNode);} ogdf::node getFirst() {if (m_ogdfNodes.size() == 0) return 0; else return m_ogdfNodes[0];} - ogdf::node getSecond() {if (m_ogdfNodes.size() < 2) return 0; else return m_ogdfNodes[1];} + ogdf::node getSecond() { + if (m_ogdfNodes.size() == 0) return 0; + else if (m_ogdfNodes.size() < 2) return m_ogdfNodes[0]; + else return m_ogdfNodes[1];} ogdf::node getLast() {if (m_ogdfNodes.size() == 0) return 0; else return m_ogdfNodes[m_ogdfNodes.size()-1];} - ogdf::node getSecondLast() {if (m_ogdfNodes.size() < 2) return 0; else return m_ogdfNodes[m_ogdfNodes.size()-2];} + ogdf::node getSecondLast() { + if (m_ogdfNodes.size() == 0) return 0; + if (m_ogdfNodes.size() < 2) return m_ogdfNodes[0]; + else return m_ogdfNodes[m_ogdfNodes.size()-2];} ogdf::node getMiddle() { if (m_ogdfNodes.size() == 0) return 0; else { diff --git a/program/globals.h b/program/globals.h index 89dd96b6..4e9d04b6 100644 --- a/program/globals.h +++ b/program/globals.h @@ -34,8 +34,8 @@ class HiCSettings; enum NodeColourScheme {UNIFORM_COLOURS, RANDOM_COLOURS, DEPTH_COLOUR, BLAST_HITS_RAINBOW_COLOUR, BLAST_HITS_SOLID_COLOUR, - CONTIGUITY_COLOUR, CUSTOM_COLOURS, RANDOM_COMPONENT_COLOURS}; -enum GraphScope {WHOLE_GRAPH, AROUND_NODE, AROUND_BLAST_HITS, DEPTH_RANGE}; + CONTIGUITY_COLOUR, CUSTOM_COLOURS, RANDOM_COMPONENT_COLOURS, COLOUR_BY_TAX}; +enum GraphScope {WHOLE_GRAPH, AROUND_NODE, AROUND_BLAST_HITS, DEPTH_RANGE, AROUND_TAX}; enum HiCDrawingType {ALL_EDGES, ONE_EDGE, NO_EDGE}; enum HiCInclusionFilter {ALL, ALL_BETWEEN_GRAPH_COMPONENTS, ONE_BETWEEN_GRAPH_COMPONENT, ONE_FROM_TARGET_COMPONENT}; enum ContiguityStatus {STARTING, CONTIGUOUS_STRAND_SPECIFIC, diff --git a/program/graphlayoutworker.cpp b/program/graphlayoutworker.cpp index 0c8b39e0..90a484c9 100644 --- a/program/graphlayoutworker.cpp +++ b/program/graphlayoutworker.cpp @@ -35,13 +35,23 @@ GraphLayoutWorker::GraphLayoutWorker(ogdf::FMMMLayout * fmmm, ogdf::GraphAttribu double graphLayoutComponentSeparation, double aspectRatio) : m_fmmm(fmmm), m_graphAttributes(graphAttributes), m_edgeArray(edgeArray), m_graphLayoutQuality(graphLayoutQuality), m_linearLayout(linearLayout), m_graphLayoutComponentSeparation(graphLayoutComponentSeparation), - m_aspectRatio(aspectRatio) + m_randSeed(-1), m_aspectRatio(aspectRatio) +{ +} + +GraphLayoutWorker::GraphLayoutWorker(ogdf::FMMMLayout* fmmm, ogdf::GraphAttributes* graphAttributes, + ogdf::EdgeArray* edgeArray, int graphLayoutQuality, bool linearLayout, + double graphLayoutComponentSeparation, int randSeed, double aspectRatio) : + m_fmmm(fmmm), m_graphAttributes(graphAttributes), m_edgeArray(edgeArray), m_graphLayoutQuality(graphLayoutQuality), + m_linearLayout(linearLayout), m_graphLayoutComponentSeparation(graphLayoutComponentSeparation), + m_randSeed(randSeed), m_aspectRatio(aspectRatio) { } void GraphLayoutWorker::layoutGraph() { - m_fmmm->randSeed(clock()); + m_fmmm->randSeed(m_randSeed); + m_fmmm->useHighLevelOptions(false); m_fmmm->initialPlacementForces(ogdf::FMMMLayout::ipfRandomRandIterNr); if (m_edgeArray != NULL) diff --git a/program/graphlayoutworker.h b/program/graphlayoutworker.h index 19415c74..38e322f9 100644 --- a/program/graphlayoutworker.h +++ b/program/graphlayoutworker.h @@ -33,6 +33,9 @@ class GraphLayoutWorker : public QObject GraphLayoutWorker(ogdf::FMMMLayout * fmmm, ogdf::GraphAttributes * graphAttributes, ogdf::EdgeArray * edgeArray, int graphLayoutQuality, bool linearLayout, double graphLayoutComponentSeparation, double aspectRatio = 1.333333); + GraphLayoutWorker(ogdf::FMMMLayout* fmmm, ogdf::GraphAttributes* graphAttributes, + ogdf::EdgeArray* edgeArray, int graphLayoutQuality, bool linearLayout, + double graphLayoutComponentSeparation, int randSeed, double aspectRatio = 1.333333); ogdf::FMMMLayout * m_fmmm; ogdf::GraphAttributes * m_graphAttributes; @@ -41,6 +44,7 @@ class GraphLayoutWorker : public QObject bool m_linearLayout; double m_graphLayoutComponentSeparation; double m_aspectRatio; + int m_randSeed; public slots: void layoutGraph(); diff --git a/program/settings.cpp b/program/settings.cpp index acc569bb..5df10f44 100644 --- a/program/settings.cpp +++ b/program/settings.cpp @@ -76,6 +76,9 @@ Settings::Settings() textOutline = false; antialiasing = true; positionTextNodeCentre = false; + displayTaxIdName = false; + displayTaxIdRank = false; + displayTaxNameRank = false; nodeDragging = NEARBY_PIECES; @@ -144,4 +147,8 @@ Settings::Settings() hicEdgeLength = FloatSetting(200.0, 0.1, 1000.0); hicEdgeWidth = FloatSetting(1.5, 0.1, 1000.0); + + taxRank = IntSetting(1, 1, 8); + taxDistance = IntSetting(-1, 0, 1000); + displayAroundTaxWithHiC = false; } diff --git a/program/settings.h b/program/settings.h index 57515832..b9d8e29e 100644 --- a/program/settings.h +++ b/program/settings.h @@ -123,6 +123,9 @@ class Settings bool displayNodeCsvData; int displayNodeCsvDataCol; bool displayBlastHits; + bool displayTaxIdName; + bool displayTaxIdRank; + bool displayTaxNameRank; QFont labelFont; bool textOutline; bool antialiasing; @@ -209,7 +212,20 @@ class Settings HiCInclusionFilter hicInclusionFilter = ONE_FROM_TARGET_COMPONENT; HiCDrawingType hicDrawingType = ALL_EDGES; - bool isAutoParameters = true; + bool isAutoParameters = false; + int taxRank; + int taxId; + bool makeZip = false; + bool wasZipped = false; + bool propagateTaxColour = false; + int taxDistance; + int displayAroundTaxWithHiC; + bool wasCalcHiCLinkForTax = false; + bool aroundTargetNodes = false; + bool onlyBigComponent = false; + bool wasComponentsFound = false; + int m_clock = -1; + bool addNewNodes = false; }; #endif // SETTINGS_H diff --git a/taxonomy/TaxData.cpp b/taxonomy/TaxData.cpp new file mode 100644 index 00000000..4154ca9f --- /dev/null +++ b/taxonomy/TaxData.cpp @@ -0,0 +1,114 @@ +#include "TaxData.h" +#include +#include +#include +#include + +TaxData::TaxData(tax* root) : + m_treeRoot(root) +{} + +tax* TaxData::addTax(QVector>* taxonomy) { + tax* curTax = m_treeRoot; + for (int i = 0; i < taxonomy->size(); i++) { + QPair pair = taxonomy->at(i); + if (m_taxMap.contains(pair.second)) { + curTax = m_taxMap[pair.second]; + } + else { + tax* newTax = new tax(pair.second, pair.first, i + 1, curTax); + m_taxMap[pair.second] = newTax; + m_statistic[i + 1].push_back(newTax); + curTax = newTax; + } + } + return curTax; +} + +void TaxData::addDeBruineNode(DeBruijnNode* node, tax* curTax) { + curTax->addContigLen(node->getLength()); + curTax->incContigCount(); + while (curTax->getPrevTax() != NULL) + { + tax* prevTax = curTax->getPrevTax(); + prevTax->addContigLen(node->getLength()); + prevTax->incContigCount(); + curTax = prevTax; + } +} + +bool taxContigLenCmp(tax* a, tax* b) +{ + return a->getContigLen() > b->getContigLen(); +} + +void TaxData::calcStatistic() { + //QMap m_statistic; + for (int i = 1; i < m_statistic.size(); i++) { + std::vector* allRankTaxes = &m_statistic[i]; + std::sort(allRankTaxes->begin(), allRankTaxes->end(), taxContigLenCmp); + } + setColour(); +} + +void TaxData::setColour() { + for (std::vector allRankTaxes : m_statistic.values()) { + std::sort(allRankTaxes.begin(), allRankTaxes.end(), taxContigLenCmp); + bool random = false; + int h = 0; + int s = 255; + int l = 127; + for (tax* curTax : allRankTaxes) { + if (h > 340) { + h = 0; + s -= 50; + if (s < 100) { + random = true; + } + } + if (!random) { + QColor posColour; + + posColour.setHsl(h, s, l); + posColour.setAlpha(g_settings->randomColourPositiveOpacity); + h += 20; + curTax->setColor(posColour); + } + else { + int hue = rand() % 360; + QColor posColour; + posColour.setHsl(hue, + g_settings->randomColourPositiveSaturation, + g_settings->randomColourPositiveLightness); + posColour.setAlpha(g_settings->randomColourPositiveOpacity); + curTax->setColor(posColour); + } + } + } +} + +QString TaxData::getLevelByRank(int rank) { + switch (rank) + { + case 0: + return "Root"; + case 1: + return "Domain"; + case 2: + return "Kingdom"; + case 3: + return "Phylum"; + case 4: + return "Class"; + case 5: + return "Order"; + case 6: + return "Family"; + case 7: + return "Genus"; + case 8: + return "Species"; + default: + return "No rank"; + } +} \ No newline at end of file diff --git a/taxonomy/TaxData.h b/taxonomy/TaxData.h new file mode 100644 index 00000000..3147cc69 --- /dev/null +++ b/taxonomy/TaxData.h @@ -0,0 +1,30 @@ +#ifndef TAXDATA_H +#define TAXDATA_H +#include +#include +#include +#include "tax.h" +#include "../graph/debruijnnode.h" +class TaxData :QObject +{ + Q_OBJECT +public: + TaxData(tax* root); + tax* addTax(QVector>* taxonomy); + void addDeBruineNode(DeBruijnNode* node, tax* curTax); + void calcStatistic(); + QString getLevelByRank(int rank); + void setColour(); + + QMap m_taxMap; + QMap> m_statistic; + QMap, unsigned int> hiCLinksWeight; + QPair getHiCLinksWeightKey(unsigned int tax1, unsigned int tax2) { + return qMakePair(std::min(tax1, tax2), std::max(tax1, tax2)); + } + double m_hiCMinNormalizedWeightByTax = -1; +private: + tax* m_treeRoot; +}; +#endif //TAXDATA_H + diff --git a/taxonomy/tax.cpp b/taxonomy/tax.cpp new file mode 100644 index 00000000..bda82cac --- /dev/null +++ b/taxonomy/tax.cpp @@ -0,0 +1,54 @@ +#include "tax.h" + +tax::tax(int taxId, QString name, int rank, tax* prevTax) : + m_taxId(taxId), + m_name(name), + m_rank(rank), + m_prevTax(prevTax) +{} + +std::vector tax::getTaxHierarchy() { + std::vector res; + res.push_back(this); + tax* curTax = this; + while (curTax->getPrevTax() != NULL && curTax->getRank() != 0) { + curTax = curTax->getPrevTax(); + res.push_back(curTax); + } + return res; +} + +tax* tax::getTaxHierarchy(int rank) { + tax* res = NULL; + tax* curTax = this; + if (this->getRank() == rank) { + return this; + } + while (curTax->getPrevTax() != NULL && curTax->getRank() != 0) { + if (curTax->getRank() == rank) + return curTax; + curTax = curTax->getPrevTax(); + } + return res; +} + +bool tax::hasTax(unsigned int taxId) { + tax* curTax = this; + while (curTax->getPrevTax() != NULL && curTax->getRank() != 0) { + if (curTax->getTaxId() == taxId) { + return true; + } + curTax = curTax->getPrevTax(); + } + return false; +} + +bool tax::addWeightInHicLinkedTaxes(tax* curTax, int weight) { + for (int i = 0; i < m_hicLinkedTaxes.size(); i++) { + if (m_hicLinkedTaxes[i].first == curTax) { + m_hicLinkedTaxes[i].second += weight; + return true; + } + } + return false; +} diff --git a/taxonomy/tax.h b/taxonomy/tax.h new file mode 100644 index 00000000..daacdf3e --- /dev/null +++ b/taxonomy/tax.h @@ -0,0 +1,48 @@ +#ifndef TAX_H +#define TAX_H +#include +#include + +class tax : public QObject +{ + Q_OBJECT + +public: + tax(int taxId, QString name, int rank, tax* prevTax = NULL); + //~tax(); + + tax* getPrevTax() {return m_prevTax; }; + void setPrevTax(tax* prevTax) { m_prevTax = prevTax; }; + int getRank() { return m_rank; }; + void setRank(int rank) { m_rank = rank; }; + QString getName() { return m_name; }; + void setName(QString name) { m_name = name; }; + bool isRoot() { return m_prevTax == NULL; }; + void setContigCount(unsigned int contigCount) { m_contigCount = contigCount; }; + void incContigCount() { m_contigCount += 1; }; + unsigned int getContigCount() { return m_contigCount; }; + void setContigLen(unsigned int contigLen) { m_contigLen = contigLen; }; + void addContigLen(unsigned int contigLen) { m_contigLen += contigLen; }; + unsigned int getContigLen() { return m_contigLen; }; + void setColor(QColor color) { m_color = color; }; + QColor getColor() { return m_color; }; + std::vector getTaxHierarchy(); + tax* getTaxHierarchy(int rank); + unsigned int getTaxId() { return m_taxId; }; + bool hasTax(unsigned int taxId); + bool addWeightInHicLinkedTaxes(tax* curTax, int weight); + + int hicLinksWeight = 0; + int hicLinksToThemself = 0; + std::vector> m_hicLinkedTaxes; + +private: + QColor m_color = QColor(200, 200, 200); + tax* m_prevTax = NULL; + unsigned int m_taxId; + QString m_name; + int m_rank; + unsigned int m_contigLen = 0; + unsigned int m_contigCount = 0; +}; +#endif diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index 459c1172..f96f31a7 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -67,6 +67,7 @@ #include "changenodedepthdialog.h" #include #include "graphinfodialog.h" +#include "taxinfodialog.h" #include #include @@ -124,6 +125,7 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : graphScopeChanged(); switchColourScheme(); + switchTaxRank(); //If this is a Mac, change the 'Delete' shortcuts to 'Backspace' instead. #ifdef Q_OS_MAC @@ -132,9 +134,11 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : #endif connect(ui->drawGraphButton, SIGNAL(clicked()), this, SLOT(drawGraph())); + connect(ui->unzipNodesPushButton, SIGNAL(clicked()), this, SLOT(unzipSelectedNodes())); connect(ui->actionLoad_graph, SIGNAL(triggered()), this, SLOT(loadGraph())); connect(ui->actionLoad_CSV, SIGNAL(triggered(bool)), this, SLOT(loadCSV())); connect(ui->actionLoad_HiC_data, SIGNAL(triggered(bool)), this, SLOT(loadHiC())); + connect(ui->actionLoad_Taxonometry, SIGNAL(triggered(bool)), this, SLOT(loadTax())); connect(ui->actionExit, SIGNAL(triggered()), this, SLOT(close())); connect(ui->graphScopeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(graphScopeChanged())); connect(ui->zoomSpinBox, SIGNAL(valueChanged(double)), this, SLOT(zoomSpinBoxChanged())); @@ -144,6 +148,7 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : connect(ui->actionCopy_selected_node_path_to_clipboard, SIGNAL(triggered(bool)), this, SLOT(copySelectedPathToClipboard())); connect(ui->actionSave_selected_node_path_to_FASTA, SIGNAL(triggered(bool)), this, SLOT(saveSelectedPathToFile())); connect(ui->coloursComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(switchColourScheme())); + connect(ui->taxColourComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(switchTaxRank())); connect(ui->actionSave_image_current_view, SIGNAL(triggered()), this, SLOT(saveImageCurrentView())); connect(ui->actionSave_image_entire_scene, SIGNAL(triggered()), this, SLOT(saveImageEntireScene())); connect(ui->nodeCustomLabelsCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); @@ -154,6 +159,9 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : connect(ui->csvComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(setTextDisplaySettings())); connect(ui->blastHitsCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); connect(ui->textOutlineCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); + connect(ui->taxIdCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); + connect(ui->taxIdRankCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); + connect(ui->taxNameRankCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); connect(ui->fontButton, SIGNAL(clicked()), this, SLOT(fontButtonPressed())); connect(ui->setNodeCustomColourButton, SIGNAL(clicked()), this, SLOT(setNodeCustomColour())); connect(ui->setNodeCustomLabelButton, SIGNAL(clicked()), this, SLOT(setNodeCustomLabel())); @@ -197,6 +205,8 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : connect(ui->actionChange_node_name, SIGNAL(triggered(bool)), this, SLOT(changeNodeName())); connect(ui->actionChange_node_depth, SIGNAL(triggered(bool)), this, SLOT(changeNodeDepth())); connect(ui->moreInfoButton, SIGNAL(clicked(bool)), this, SLOT(openGraphInfoDialog())); + connect(ui->taxInfoButton, SIGNAL(clicked(bool)), this, SLOT(openTaxInfoDialog())); + connect(ui->taxInfoHiCButton, SIGNAL(clicked(bool)), this, SLOT(openTaxInfoHiCDialog())); connect(this, SIGNAL(windowLoaded()), this, SLOT(afterMainWindowShow()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection)); } @@ -306,6 +316,44 @@ void MainWindow::loadHiC(QString fullFileName) { } +void MainWindow::loadTax(QString fullFileName) { + QString selectedFilter = "Comma separated value (*.txt)"; + if (fullFileName == "") + { + fullFileName = QFileDialog::getOpenFileName(this, "Load Hi-C", g_memory->rememberedPath, + "Comma separated value (*.txt)", + &selectedFilter); + } + + if (fullFileName == "") + return; // user clicked on cancel + QString errormsg; + QStringList columns; + + try + { + MyProgressDialog progress(this, "Loading Tax...", false); + progress.setWindowModality(Qt::WindowModal); + progress.show(); + + bool coloursLoaded = false; + bool success = g_assemblyGraph->loadTax(fullFileName, &errormsg); + + if (success) + { + setTaxVisibility(true); + } + } + catch (...) + { + QString errorTitle = "Error loading Tax"; + QString errorMessage = "There was an error when attempting to load:\n" + + fullFileName + "\n\n" + "Please verify that this file has the correct format."; + QMessageBox::warning(this, errorTitle, errorMessage); + } +} + void MainWindow::loadCSV(QString fullFileName) { QString selectedFilter = "Comma separated value (*.csv)"; @@ -369,6 +417,11 @@ void MainWindow::loadGraph(QString fullFileName) if (fullFileName != "") //User did not hit cancel { + g_settings->wasCalcHiCLinkForTax = false; + g_settings->wasZipped = false; + g_settings->wasComponentsFound = false; + g_settings->m_clock = -1; + GraphFileType detectedFileType = g_assemblyGraph->getGraphFileTypeFromFile(fullFileName); GraphFileType selectedFileType = ANY_FILE_TYPE; @@ -522,6 +575,16 @@ void MainWindow::clearGraphDetails() ui->totalLengthLabel->setText("0"); } +void MainWindow::unzipSelectedNodes() { + std::vector selectedNodes = m_scene->getSelectedNodes(); + if (selectedNodes.size() == 1 && selectedNodes[0]->isNodeUnion()) { + resetScene(); + g_settings->addNewNodes = true; + DeBruijnNode* selectNode = selectedNodes[0]; + g_assemblyGraph->unzipSelectedNodes(selectNode); + layoutGraphUnzip(); + } +} void MainWindow::selectionChanged() { @@ -560,6 +623,9 @@ void MainWindow::selectionChanged() } ui->selectedNodesTextEdit->setPlainText(selectedNodeListText); + QString selectedNodeTaxListText; + getSelectedNodeTaxInfo(selectedNodeTaxListText); + ui->taxInfoTextEdit->setPlainText(selectedNodeTaxListText); } @@ -610,6 +676,34 @@ void MainWindow::getSelectedNodeInfo(int & selectedNodeCount, QString & selected selectedNodeDepthText = formatDepthForDisplay(g_assemblyGraph->getMeanDepth(selectedNodes)); } +void MainWindow::getSelectedNodeTaxInfo(QString& selectedNodeListText) { + std::vector selectedNodes = m_scene->getSelectedNodes(); + + int selectedNodeCount = int(selectedNodes.size()); + + for (int i = 0; i < selectedNodeCount; ++i) + { + QString nodeName = selectedNodes[i]->getName(); + if (!g_settings->doubleMode) + nodeName.chop(1); + if (selectedNodes[i]->getTax() != NULL) { + selectedNodeListText += nodeName; + selectedNodeListText += "\n"; + std::vector taxes = selectedNodes[i]->getTax()->getTaxHierarchy(); + for (std::vector::iterator it = taxes.begin(); it != taxes.end(); ++it) { + tax* curTax = *it; + selectedNodeListText += (g_assemblyGraph->m_taxData->getLevelByRank(curTax->getRank())); + selectedNodeListText += ": "; + selectedNodeListText += curTax->getName(); + selectedNodeListText += "\n"; + } + } + else { + selectedNodeListText += nodeName; + selectedNodeListText += ": unspecified tax"; + } + } +} @@ -648,6 +742,7 @@ void MainWindow::graphScopeChanged() setStartingNodesWidgetVisibility(false); setNodeDistanceWidgetVisibility(false); setDepthRangeWidgetVisibility(false); + setAroundTaxWidgetVisibility(false); ui->graphDrawingGridLayout->addWidget(ui->nodeStyleInfoText, 1, 0, 1, 1); ui->graphDrawingGridLayout->addWidget(ui->nodeStyleLabel, 1, 1, 1, 1); @@ -663,12 +758,13 @@ void MainWindow::graphScopeChanged() setStartingNodesWidgetVisibility(true); setNodeDistanceWidgetVisibility(true); setDepthRangeWidgetVisibility(false); + setAroundTaxWidgetVisibility(false); ui->nodeDistanceInfoText->setInfoText("Nodes will be drawn if they are specified in the above list or are " - "within this many steps of those nodes.

" - "A value of 0 will result in only the specified nodes being drawn. " - "A large value will result in large sections of the graph around " - "the specified nodes being drawn."); + "within this many steps of those nodes.

" + "A value of 0 will result in only the specified nodes being drawn. " + "A large value will result in large sections of the graph around " + "the specified nodes being drawn."); ui->graphDrawingGridLayout->addWidget(ui->startingNodesInfoText, 1, 0, 1, 1); ui->graphDrawingGridLayout->addWidget(ui->startingNodesLabel, 1, 1, 1, 1); @@ -695,10 +791,10 @@ void MainWindow::graphScopeChanged() setDepthRangeWidgetVisibility(false); ui->nodeDistanceInfoText->setInfoText("Nodes will be drawn if they contain a BLAST hit or are within this " - "many steps of nodes with a BLAST hit.

" - "A value of 0 will result in only nodes with BLAST hits being drawn. " - "A large value will result in large sections of the graph around " - "nodes with BLAST hits being drawn."); + "many steps of nodes with a BLAST hit.

" + "A value of 0 will result in only nodes with BLAST hits being drawn. " + "A large value will result in large sections of the graph around " + "nodes with BLAST hits being drawn."); ui->graphDrawingGridLayout->addWidget(ui->nodeDistanceInfoText, 1, 0, 1, 1); ui->graphDrawingGridLayout->addWidget(ui->nodeDistanceLabel, 1, 1, 1, 1); @@ -731,7 +827,21 @@ void MainWindow::graphScopeChanged() ui->graphDrawingGridLayout->addWidget(ui->drawGraphButton, 4, 1, 1, 2); break; + case 4: + g_settings->graphScope = AROUND_TAX; + + setStartingNodesWidgetVisibility(false); + setNodeDistanceWidgetVisibility(false); + setDepthRangeWidgetVisibility(false); + setAroundTaxWidgetVisibility(true); + + ui->graphDrawingGridLayout->addWidget(ui->taxFilterLabel, 6, 1, 1, 1); + ui->graphDrawingGridLayout->addWidget(ui->taxFilterLineEdit, 6, 2, 1, 1); + ui->graphDrawingGridLayout->addWidget(ui->aroundTaxHiCCheckBox, 7, 1, 1, 1); + ui->graphDrawingGridLayout->addWidget(ui->aroundTaxDistanceCheckBox, 8, 1, 1, 1); + ui->graphDrawingGridLayout->addWidget(ui->aroundTaxDistanceSpinBox, 8, 2, 1, 1); } + } @@ -760,6 +870,14 @@ void MainWindow::setDepthRangeWidgetVisibility(bool visible) ui->maxDepthLabel->setVisible(visible); ui->maxDepthSpinBox->setVisible(visible); } +void MainWindow::setAroundTaxWidgetVisibility(bool visible) +{ + ui->taxFilterLabel->setVisible(visible); + ui->taxFilterLineEdit->setVisible(visible); + ui->aroundTaxHiCCheckBox->setVisible(visible); + ui->aroundTaxDistanceCheckBox->setVisible(visible); + ui->aroundTaxDistanceSpinBox->setVisible(visible); +} void MainWindow::setHiCWidgetVisibility(bool visible) { @@ -769,6 +887,10 @@ void MainWindow::setHiCWidgetVisibility(bool visible) ui->hicWeightSpinBox->setVisible(visible); } +void MainWindow::setTaxVisibility(bool visible) +{ + +} void MainWindow::drawGraph() { @@ -786,14 +908,34 @@ void MainWindow::drawGraph() QMessageBox::information(this, errorTitle, errorMessage); return; } - + g_settings->addNewNodes = false; g_hicSettings->minWeight = ui->hicWeightSpinBox->value(); g_hicSettings->minLength = ui->hicSeqLenSpinBox->value(); g_hicSettings->inclusionFilter = filterHiC; - + g_settings->makeZip = ui->zipGraphCheckBox->isChecked(); + g_settings->aroundTargetNodes = ui->aroundTargetNodesCheckBox->isChecked(); + g_settings->onlyBigComponent = ui->onlyBigComponentCheckBox->isChecked(); resetScene(); - if (g_settings->isAutoParameters) { + if ((g_settings->aroundTargetNodes || g_settings->onlyBigComponent) && (!g_settings->wasComponentsFound)) { + g_assemblyGraph->findComponents(); + } + if (g_settings->makeZip && !g_settings->wasZipped) { + g_settings->wasZipped = true; + g_assemblyGraph->makeZipped(1000); + } + if (g_settings->graphScope == AROUND_TAX) { + unsigned int taxId = ui->taxFilterLineEdit->text().toUInt(); + g_settings->taxId = taxId; + g_settings->displayAroundTaxWithHiC = ui->aroundTaxHiCCheckBox->isChecked(); + if (ui->aroundTaxDistanceCheckBox->isChecked()) + g_settings->taxDistance = ui->aroundTaxDistanceSpinBox->value(); + else + g_settings->taxDistance = -1; + g_assemblyGraph->buildOgdfGraphWithTaxFilter(taxId); + layoutGraph(); + } + else if (g_settings->isAutoParameters) { g_assemblyGraph->buildOgdfGraphWithAutoParameters(startingNodes); layoutGraph(); } @@ -825,13 +967,15 @@ void MainWindow::graphLayoutFinished() m_layoutThread = 0; g_assemblyGraph->addGraphicsItemsToScene(m_scene); m_scene->setSceneRectangle(); - zoomToFitScene(); + if (!g_settings->addNewNodes) + zoomToFitScene(); selectionChanged(); setUiState(GRAPH_DRAWN); //Move the focus to the view so the user can use keyboard controls to navigate. g_graphicsView->setFocus(); + g_settings->addNewNodes = false; } @@ -888,11 +1032,53 @@ void MainWindow::layoutGraph() m_layoutThread = new QThread; double aspectRatio = double(g_graphicsView->width()) / g_graphicsView->height(); + int m_clock = clock(); + g_settings->m_clock = m_clock; GraphLayoutWorker * graphLayoutWorker = new GraphLayoutWorker(m_fmmm, g_assemblyGraph->m_graphAttributes, g_assemblyGraph->m_edgeArray, g_settings->graphLayoutQuality, g_assemblyGraph->useLinearLayout(), - g_settings->componentSeparation, aspectRatio); + g_settings->componentSeparation, + m_clock, aspectRatio); + graphLayoutWorker->moveToThread(m_layoutThread); + + connect(progress, SIGNAL(halt()), this, SLOT(graphLayoutCancelled())); + connect(m_layoutThread, SIGNAL(started()), graphLayoutWorker, SLOT(layoutGraph())); + connect(graphLayoutWorker, SIGNAL(finishedLayout()), m_layoutThread, SLOT(quit())); + connect(graphLayoutWorker, SIGNAL(finishedLayout()), graphLayoutWorker, SLOT(deleteLater())); + connect(graphLayoutWorker, SIGNAL(finishedLayout()), this, SLOT(graphLayoutFinished())); + connect(m_layoutThread, SIGNAL(finished()), m_layoutThread, SLOT(deleteLater())); + connect(m_layoutThread, SIGNAL(finished()), progress, SLOT(deleteLater())); + m_layoutThread->start(); +} + +void MainWindow::layoutGraphUnzip() +{ + //The actual layout is done in a different thread so the UI will stay responsive. + MyProgressDialog* progress = new MyProgressDialog(this, "Laying out graph...", true, "Cancel layout", "Cancelling layout...", + "Clicking this button will halt the graph layout and display " + "the graph in its current, incomplete state.

" + "Layout can take a long time for very large graphs. There are " + "three strategies to reduce the amount of time required:
    " + "
  • Change the scope of the graph from 'Entire graph' to either " + "'Around nodes' or 'Around BLAST hits'. This will reduce the " + "number of nodes that are drawn to the screen.
  • " + "
  • Increase the 'Base pairs per segment' setting. This will " + "result in shorter contigs which take less time to lay out.
  • " + "
  • Reduce the 'Graph layout iterations' setting.
"); + progress->setWindowModality(Qt::WindowModal); + progress->show(); + + m_fmmm = new ogdf::FMMMLayout(); + + m_layoutThread = new QThread; + double aspectRatio = double(g_graphicsView->width()) / g_graphicsView->height(); + int m_clock = g_settings->m_clock; + GraphLayoutWorker* graphLayoutWorker = new GraphLayoutWorker(m_fmmm, g_assemblyGraph->m_graphAttributes, + g_assemblyGraph->m_edgeArray, + 0, + true, + g_settings->componentSeparation, m_clock, aspectRatio); graphLayoutWorker->moveToThread(m_layoutThread); connect(progress, SIGNAL(halt()), this, SLOT(graphLayoutCancelled())); @@ -1118,8 +1304,11 @@ void MainWindow::saveSelectedPathToFile() } } - - +void MainWindow::switchTaxRank() { + g_settings->taxRank = ui->taxColourComboBox->currentIndex() + 1; + g_assemblyGraph->resetAllNodeColours(); + g_graphicsView->viewport()->update(); +} void MainWindow::switchColourScheme() { @@ -1164,14 +1353,19 @@ void MainWindow::switchColourScheme() g_settings->nodeColourScheme = RANDOM_COMPONENT_COLOURS; ui->contiguityButton->setVisible(false); ui->contiguityInfoText->setVisible(false); + break; + case 8: + g_settings->nodeColourScheme = COLOUR_BY_TAX; + ui->contiguityButton->setVisible(false); + ui->contiguityInfoText->setVisible(false); + ui->taxColourComboBox->setVisible(true); + break; } g_assemblyGraph->resetAllNodeColours(); g_graphicsView->viewport()->update(); } - - void MainWindow::determineContiguityFromSelectedNode() { g_assemblyGraph->resetNodeContiguityStatus(); @@ -1390,6 +1584,9 @@ void MainWindow::setTextDisplaySettings() g_settings->displayNodeCsvData = ui->csvCheckBox->isChecked(); g_settings->displayNodeCsvDataCol = ui->csvComboBox->currentIndex(); g_settings->textOutline = ui->textOutlineCheckBox->isChecked(); + g_settings->displayTaxIdName = ui->taxIdCheckBox->isChecked(); + g_settings->displayTaxIdRank = ui->taxIdRankCheckBox->isChecked(); + g_settings->displayTaxNameRank = ui->taxNameRankCheckBox->isChecked(); g_graphicsView->viewport()->update(); } @@ -1851,6 +2048,7 @@ void MainWindow::setUiState(UiState uiState) ui->selectionScrollAreaWidgetContents->setEnabled(false); ui->actionLoad_CSV->setEnabled(false); ui->actionLoad_HiC_data->setEnabled(false); + ui->actionLoad_Taxonometry->setEnabled(false); break; case GRAPH_LOADED: ui->graphDetailsWidget->setEnabled(true); @@ -1861,6 +2059,7 @@ void MainWindow::setUiState(UiState uiState) ui->selectionScrollAreaWidgetContents->setEnabled(false); ui->actionLoad_CSV->setEnabled(true); ui->actionLoad_HiC_data->setEnabled(true); + ui->actionLoad_Taxonometry->setEnabled(true); break; case GRAPH_DRAWN: ui->graphDetailsWidget->setEnabled(true); @@ -1872,6 +2071,7 @@ void MainWindow::setUiState(UiState uiState) ui->actionZoom_to_selection->setEnabled(true); ui->actionLoad_CSV->setEnabled(true); ui->actionLoad_HiC_data->setEnabled(true); + ui->actionLoad_Taxonometry->setEnabled(true); break; } } @@ -2138,6 +2338,9 @@ void MainWindow::setWidgetsFromSettings() ui->nodeDepthCheckBox->setChecked(g_settings->displayNodeDepth); ui->blastHitsCheckBox->setChecked(g_settings->displayBlastHits); ui->textOutlineCheckBox->setChecked(g_settings->textOutline); + ui->taxIdCheckBox->setChecked(g_settings->displayTaxIdName); + ui->taxIdCheckBox->setChecked(g_settings->displayTaxIdRank); + ui->taxIdCheckBox->setChecked(g_settings->displayTaxNameRank); ui->startingNodesExactMatchRadioButton->setChecked(g_settings->startingNodesExactMatch); ui->startingNodesPartialMatchRadioButton->setChecked(!g_settings->startingNodesExactMatch); @@ -2165,6 +2368,8 @@ void MainWindow::setNodeColourSchemeComboBox(NodeColourScheme nodeColourScheme) case BLAST_HITS_RAINBOW_COLOUR: ui->coloursComboBox->setCurrentIndex(4); break; case CONTIGUITY_COLOUR: ui->coloursComboBox->setCurrentIndex(5); break; case CUSTOM_COLOURS: ui->coloursComboBox->setCurrentIndex(6); break; + case RANDOM_COMPONENT_COLOURS: ui->coloursComboBox->setCurrentIndex(7); break; + case COLOUR_BY_TAX: ui->coloursComboBox->setCurrentIndex(8); break; } } @@ -2176,6 +2381,7 @@ void MainWindow::setGraphScopeComboBox(GraphScope graphScope) case AROUND_NODE: ui->graphScopeComboBox->setCurrentIndex(1); break; case AROUND_BLAST_HITS: ui->graphScopeComboBox->setCurrentIndex(2); break; case DEPTH_RANGE: ui->graphScopeComboBox->setCurrentIndex(3); break; + case AROUND_TAX: ui->graphScopeComboBox->setCurrentIndex(4); break; } } @@ -2243,6 +2449,10 @@ void MainWindow::setSelectedNodesWidgetsVisibility(bool visible) ui->selectedNodesLengthLabel->setVisible(visible); ui->selectedNodesDepthLabel->setVisible(visible); ui->selectedNodesSpacerWidget->setVisible(visible); + ui->taxInfoTextEdit->setVisible(visible); + ui->labelTaxInfo->setVisible(visible); + ui->taxLine_4->setVisible(visible); + ui->unzipNodesPushButton->setVisible(visible); } void MainWindow::setSelectedEdgesWidgetsVisibility(bool visible) @@ -2581,3 +2791,26 @@ void MainWindow::openGraphInfoDialog() GraphInfoDialog graphInfoDialog(this); graphInfoDialog.exec(); } +void MainWindow::openTaxInfoDialog() +{ + TaxInfoDialog taxInfoDialog(this); + taxInfoDialog.exec(); +} + +void MainWindow::openTaxInfoHiCDialog() +{ + if (!g_settings->wasCalcHiCLinkForTax) { + g_settings->wasCalcHiCLinkForTax = true; + g_assemblyGraph->calcHiCLinkForTax(); + } + if (ui->taxIdHiCStatLineEdit->text().size() > 0) { + int taxId = ui->taxIdHiCStatLineEdit->text().toInt(); + TaxInfoDialog taxInfoDialog(this, taxId); + taxInfoDialog.exec(); + } + else { + TaxInfoDialog taxInfoDialog(this, 0); + taxInfoDialog.exec(); + } + +} diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 6b814da6..48a0bcfa 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -72,6 +72,7 @@ class MainWindow : public QMainWindow void zoomToFitScene(); void setZoomSpinBoxStep(); void getSelectedNodeInfo(int & selectedNodeCount, QString & selectedNodeCountText, QString & selectedNodeListText, QString & selectedNodeLengthText, QString &selectedNodeDepthText); + void MainWindow::getSelectedNodeTaxInfo(QString& selectedNodeListText); QString getSelectedEdgeListText(); std::vector getNodesFromLineEdit(QLineEdit * lineEdit, bool exactMatch, std::vector * nodesNotInGraph = 0); void loadGraph2(GraphFileType graphFileType, QString filename); @@ -91,16 +92,19 @@ class MainWindow : public QMainWindow void setNodeDistanceWidgetVisibility(bool visible); void setDepthRangeWidgetVisibility(bool visible); void MainWindow::setHiCWidgetVisibility(bool visible); + void MainWindow::setTaxVisibility(bool visible); static QByteArray makeStringUrlSafe(QByteArray s); void removeGraphicsItemNodes(const std::vector * nodes, bool reverseComplement); void removeGraphicsItemEdges(const std::vector * edges, bool reverseComplement); void removeAllGraphicsEdgesFromNode(DeBruijnNode * node, bool reverseComplement); std::vector addComplementaryNodes(std::vector nodes); + void layoutGraphUnzip(); private slots: void loadGraph(QString fullFileName = ""); void loadCSV(QString fullFileName = ""); void loadHiC(QString fullFileName = ""); + void loadTax(QString fullFileName = ""); void selectionChanged(); void graphScopeChanged(); void drawGraph(); @@ -159,6 +163,11 @@ private slots: void changeNodeName(); void changeNodeDepth(); void openGraphInfoDialog(); + void switchTaxRank(); + void setAroundTaxWidgetVisibility(bool visible); + void openTaxInfoDialog(); + void openTaxInfoHiCDialog(); + void unzipSelectedNodes(); protected: void showEvent(QShowEvent *ev); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index 33ec249d..e73e19dc 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -6,7 +6,7 @@ 0 0 - 1465 + 1467 924
@@ -60,7 +60,7 @@ 0 0 335 - 1038 + 1392 @@ -85,6 +85,53 @@ 0 + + + + Tax info + + + + + + + Qt::Horizontal + + + + + + + Tax Id + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + + + More info + + + @@ -157,29 +204,6 @@ - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - @@ -193,10 +217,10 @@ - - + + - More info + Tax info (with Hi-C links) @@ -269,11 +293,22 @@ 0 - - - - true + + + + Draw graph + + + + + + + Tax id: + + + + 0 @@ -288,38 +323,47 @@ - - + + true - + 0 0 - - - 16 - 16 - - - - + + - + 0 0 - - - 16 - 16 - + + Qt::AlignCenter + + + + + + 1 + + + 0.000000000000000 + + + 1000.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 @@ -373,8 +417,8 @@ - - + + true @@ -392,7 +436,94 @@ - + + + + true + + + + 0 + 0 + + + + Qt::AlignCenter + + + 10000 + + + + + + + true + + + Distance: + + + + + + + + 0 + 0 + + + + Style: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + Min: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + @@ -408,7 +539,81 @@ - + + + + Zip graph + + + + + + + true + + + + 0 + 0 + + + + Match: + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.000000000000000 + + + 1000000.000000000000000 + + + 10.000000000000000 + + + 100.000000000000000 + + + + + + + + 0 + 0 + + + + Scope: + + + + + + + With Distance + + + + @@ -458,71 +663,32 @@ - - - - Draw graph - - - - - - - - 0 - 0 - - - - Style: - - + + - - + + true - + 0 0 - - Qt::AlignCenter - - - 10000 + + + 16 + 16 + - - - - true - - - - 0 - 0 - - + + - Node(s): - - - - - - - true - - - - 0 - 0 - + Around target nodes @@ -554,10 +720,15 @@ Depth range + + + Around tax + + - - + + 0 @@ -565,22 +736,25 @@ - Scope: + Max: - - - - true + + + + Qt::AlignCenter - - Distance: + + 1 + + + 1000000.000000000000000 - - + + true @@ -591,7 +765,7 @@ - Match: + Node(s): @@ -608,8 +782,18 @@ - - + + + + HiC min sequence length: + + + Qt::AlignLeading + + + + + 0 @@ -624,8 +808,8 @@ - - + + 0 @@ -640,49 +824,11 @@ - - - - HiC min weight: - - - Qt::AlignLeading - - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - - - - 1 - - - 0.000000000000000 - - - 1000.000000000000000 - - - 1.000000000000000 - - - 1.000000000000000 + + + + true - - - - 0 @@ -697,78 +843,35 @@ - - - - HiC min sequence length: - - - Qt::AlignLeading - - - - - - - 0 - 0 - - - - Qt::AlignCenter - - - - - - 1 - - - 0.000000000000000 - + - 1000000.000000000000000 - - - 10.000000000000000 + 1000 - 100.000000000000000 + 0 - - - - - 0 - 0 - + + + + HiC min weight: - - - 16 - 16 - + + Qt::AlignLeading - - - - - 0 - 0 - - + + - Min: + With Hi-C - - + + 0 @@ -779,33 +882,14 @@ 16 16 - - - - - - - - - 0 - 0 - - - - Max: + - - - - Qt::AlignCenter - - - 1 - - - 1000000.000000000000000 + + + + Only big component @@ -881,56 +965,83 @@ 0 - - + + - + 0 0 - - - 16 - 16 - + + Qt::AlignCenter - - - - - - - 0 - 0 - + + % - - - 16 - 16 - + + 1 - - - - - - - 0 - 0 - + + 5.000000000000000 - - - 16 - 16 - + + 500.000000000000000 + + + 5.000000000000000 + + + 100.000000000000000 - - + + + + + Domain + + + + + Kingdom + + + + + Phylum + + + + + Class + + + + + Order + + + + + Family + + + + + Genus + + + + + Species + + + + + + 0 @@ -941,32 +1052,68 @@ Qt::AlignCenter - % + 1 - 5.000000000000000 + 0.500000000000000 - 500.000000000000000 + 1000.000000000000000 - 5.000000000000000 + 0.500000000000000 - 100.000000000000000 + 5.000000000000000 - + + + + Node width: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + Determine contiguity + + + + Zoom: + + + Qt::AlignLeading + + + @@ -1009,16 +1156,11 @@ Random component colours - - - - - - Zoom: - - - Qt::AlignLeading - + + + Colour by tax + + @@ -1037,44 +1179,49 @@ - - + + - Node width: - - - Qt::AlignLeading + Tax Id (with rank) - - + + - + 0 0 - - Qt::AlignCenter - - - - - - 1 - - - 0.500000000000000 + + + 16 + 16 + - - 1000.000000000000000 + + + + + + + 0 + 0 + - - 0.500000000000000 + + + 16 + 16 + - - 5.000000000000000 + + + + + + Tax Name (with rank) @@ -1263,6 +1410,13 @@ + + + + Tax Name (id) + + + @@ -1473,9 +1627,9 @@ 0 - 0 - 239 - 871 + -149 + 278 + 999 @@ -1869,6 +2023,36 @@ + + + + + 75 + true + + + + Taxonometry for selected nodes + + + + + + + Qt::Horizontal + + + + + + + + + + Unzip selected nodes + + + @@ -1893,7 +2077,7 @@ 0 0 - 1465 + 1467 26 @@ -1910,6 +2094,7 @@ + @@ -2356,6 +2541,15 @@ Load HiC data + + + + :/icons/load-256.png:/icons/load-256.png + + + Load Taxonometry + + diff --git a/ui/taxinfodialog.cpp b/ui/taxinfodialog.cpp new file mode 100644 index 00000000..99c4d06a --- /dev/null +++ b/ui/taxinfodialog.cpp @@ -0,0 +1,97 @@ +#include "taxinfodialog.h" +#include "ui_taxinfodialog.h" + +#include "../program/globals.h" +#include "../graph/assemblygraph.h" +#include + +TaxInfoDialog::TaxInfoDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::TaxInfoDialog) +{ + ui->setupUi(this); + setInfoTexts(); +} + +TaxInfoDialog::TaxInfoDialog(QWidget* parent, int taxId) : + QDialog(parent), + ui(new Ui::TaxInfoDialog) +{ + ui->setupUi(this); + if (taxId != 0) + setSpecialTaxInfoTexts(taxId); + else + setErrorText(); +} + +TaxInfoDialog::~TaxInfoDialog() +{ + delete ui; +} + +void TaxInfoDialog::setInfoTexts() +{ + QMap>* stat = &(g_assemblyGraph->m_taxData->m_statistic); + QString text; + text += "There is statistic about taxonometry analyse. Ten most represented taxes in every of 8 ranks were displayed.\n"; + text += "Every record contains tax name, tax id, total length of contigs under this tax, number of taxes under this tax"; + for (int rank = 1; rank < 9; rank++) { + text += ""; + text += g_assemblyGraph->m_taxData->getLevelByRank(rank); + text += ""; + std::vector* tempRankStat = &((*stat)[rank]); + for (int i = 0; i < std::min(10, (int)tempRankStat->size()); i++) { + tax* curTax = tempRankStat->at(i); + text += ""; + text += (curTax->getName()); + text += " "; + text += QString::number(curTax->getTaxId()); + text += " "; + text += QString::number(curTax->getContigLen()); + text += ""; + text += QString::number(curTax->getContigCount()); + text += ""; + } + } + ui->taxPlainTextEdit->setHtml(text); +} + +bool taxPairCmp(QPair a, QPair b) +{ + return a.second > b.second; +} + +void TaxInfoDialog::setErrorText() { + QString text = "Tax Id was not filled."; + ui->taxPlainTextEdit->setHtml(text); +} + +void TaxInfoDialog::setErrorText(int taxId) { + QString text = "Tax Id (" +QString::number(taxId)+") was not found."; + ui->taxPlainTextEdit->setHtml(text); +} + +void TaxInfoDialog::setSpecialTaxInfoTexts(int taxId) { + tax* currentTax = g_assemblyGraph->m_taxData->m_taxMap[taxId]; + QString text; + if (currentTax != NULL) { + std::vector> res = g_assemblyGraph->getHiCConnectedTaxes(currentTax); + res.push_back(qMakePair(currentTax, currentTax->hicLinksToThemself)); + std::sort(res.begin(), res.end(), taxPairCmp); + text += ""; + text += (""); + for (int i = 0; i < res.size(); i++) { + QPair* pair = &res[i]; + text += (""); + text += (""); + text += (""); + text += (""); + text += (""); + } + text += "
|tax name\t|tax id\t|num of Hi-C links\t|contig len\t|% of all Hi-C links\t
|" + (pair->first->getName()) + "|" + QString::number(pair->first->getTaxId()) + "|" + QString::number(pair->second) + "|" + QString::number(pair->first->getContigLen()) + "|" + QString::number((double)((double)pair->second/(double)pair->first->hicLinksWeight)*100) + "%
"; + ui->taxPlainTextEdit->setHtml(text); + } + else { + setErrorText(taxId); + } +} \ No newline at end of file diff --git a/ui/taxinfodialog.h b/ui/taxinfodialog.h new file mode 100644 index 00000000..3e304b8f --- /dev/null +++ b/ui/taxinfodialog.h @@ -0,0 +1,28 @@ +#ifndef TAXINFODIALOG_H +#define TAXINFODIALOG_H + +#include + +namespace Ui { +class TaxInfoDialog; +} + +class TaxInfoDialog : public QDialog +{ + Q_OBJECT + +public: + explicit TaxInfoDialog(QWidget *parent = 0); + TaxInfoDialog(QWidget *parent, int taxId); + ~TaxInfoDialog(); + +private: + Ui::TaxInfoDialog *ui; + + void setInfoTexts(); + void setSpecialTaxInfoTexts(int taxId); + void setErrorText(); + void setErrorText(int taxId); +}; + +#endif // TAXINFODIALOG_H diff --git a/ui/taxinfodialog.ui b/ui/taxinfodialog.ui new file mode 100644 index 00000000..af402ed4 --- /dev/null +++ b/ui/taxinfodialog.ui @@ -0,0 +1,36 @@ + + + TaxInfoDialog + + + + 0 + 0 + 1187 + 868 + + + + Taxonometry information + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + From 113135d64762391fcc66c93b1b911bb971409181 Mon Sep 17 00:00:00 2001 From: Shostina Date: Sat, 28 May 2022 21:49:26 +0300 Subject: [PATCH 09/11] refactoring --- graph/assemblygraph.cpp | 4 ---- graph/graphicsitemnode.cpp | 2 +- ui/mainwindow.cpp | 15 +++++++++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index 167ebc98..076e3e1d 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -1991,10 +1991,6 @@ QPair AssemblyGraph::dfsComponent(DeBruijnNode * no if (deleteLeafIfNeeded(node)) { return qMakePair(contigCount, size); } - if (markChainIfNeeded(node)) { - mergedNode->push_back(node); - return qMakePair(contigCount, size); - } } for (DeBruijnEdge* edge : node->getLeavingEdges()) { if (edge->getEndingNode()->getComponentId() == 0 && !edge->isHiC()) { diff --git a/graph/graphicsitemnode.cpp b/graph/graphicsitemnode.cpp index c14bbc57..6e773559 100644 --- a/graph/graphicsitemnode.cpp +++ b/graph/graphicsitemnode.cpp @@ -237,6 +237,7 @@ void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem QStringList nodeText = getNodeText(); QPainterPath textPath; + g_settings->labelFont.setPixelSize(20); QFontMetrics metrics(g_settings->labelFont); double fontHeight = metrics.ascent(); @@ -989,7 +990,6 @@ QPointF GraphicsItemNode::getCentre(std::vector linePoints) const QStringList GraphicsItemNode::getNodeText() { QStringList nodeText; - if (g_settings->displayNodeCustomLabels) nodeText << m_deBruijnNode->getCustomLabelForDisplay(); if (g_settings->displayNodeNames) diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index f96f31a7..16d38ce6 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -967,8 +967,8 @@ void MainWindow::graphLayoutFinished() m_layoutThread = 0; g_assemblyGraph->addGraphicsItemsToScene(m_scene); m_scene->setSceneRectangle(); - if (!g_settings->addNewNodes) - zoomToFitScene(); + + zoomToFitScene(); selectionChanged(); setUiState(GRAPH_DRAWN); @@ -1032,8 +1032,15 @@ void MainWindow::layoutGraph() m_layoutThread = new QThread; double aspectRatio = double(g_graphicsView->width()) / g_graphicsView->height(); - int m_clock = clock(); - g_settings->m_clock = m_clock; + int m_clock; + if (g_settings->m_clock == -1) { + m_clock = clock(); + g_settings->m_clock = m_clock; + } + else { + m_clock = g_settings->m_clock; + } + GraphLayoutWorker * graphLayoutWorker = new GraphLayoutWorker(m_fmmm, g_assemblyGraph->m_graphAttributes, g_assemblyGraph->m_edgeArray, g_settings->graphLayoutQuality, From ae990d96bdf7a09b286a52f336d0518dc06e767b Mon Sep 17 00:00:00 2001 From: Shostina Date: Wed, 8 Jun 2022 20:10:37 +0300 Subject: [PATCH 10/11] refactoring, delete unused method --- graph/assemblygraph.cpp | 56 ++++++++--------------------------------- graph/assemblygraph.h | 7 ------ 2 files changed, 10 insertions(+), 53 deletions(-) diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index 3c6664ca..ea17e751 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -1978,28 +1978,6 @@ unsigned int getMaxChildLength(DeBruijnNode* node) { return maxChildLen; } -bool AssemblyGraph::deleteLeafIfNeeded(DeBruijnNode* node) { - if (node->getLeavingEdges().size() == 0 && node->getEnteringEdges().size() != 0) { - unsigned int maxParentLen = getMaxParentLength(node); - unsigned int maxParentDepth = getMaxParentDepth(node); - if (node->getLength() < 1000/* && node->getLength() < maxParentLen*/ && - node->getDepth() <= maxParentDepth / 2) { - deleteNode(node); - return true; - } - } - if (node->getEnteringEdges().size() == 0 && node->getLeavingEdges().size() != 0) { - unsigned int maxChildLen = getMaxChildLength(node); - unsigned int maxChildDepth = getMaxChildDepth(node); - if (node->getLength() < 1000/* && node->getLength() < maxChildLen*/ && - node->getDepth() <= maxChildDepth / 2) { - deleteNode(node); - return true; - } - } - return false; -} - QPair AssemblyGraph::dfsComponent(DeBruijnNode * node, int componentId, std::vector* mergedNode) { unsigned long size = 0; unsigned int contigCount = 0; @@ -2010,11 +1988,6 @@ QPair AssemblyGraph::dfsComponent(DeBruijnNode * no if (node->getNameWithoutSign().endsWith("_start")) { g_hicSettings->addTargetComponentIfNeeded(componentId); } - else { - if (deleteLeafIfNeeded(node)) { - return qMakePair(contigCount, size); - } - } for (DeBruijnEdge* edge : node->getLeavingEdges()) { if (edge->getEndingNode()->getComponentId() == 0 && !edge->isHiC()) { QPair res = dfsComponent(edge->getEndingNode(), componentId, mergedNode); @@ -2029,11 +2002,6 @@ QPair AssemblyGraph::dfsComponent(DeBruijnNode * no contigCount += res.first; } } - if (!(node->getNameWithoutSign().endsWith("_start"))) { - if (deleteLeafIfNeeded(node)) { - return qMakePair(contigCount, size); - } - } size += node->getLength(); contigCount += 1; } @@ -2078,9 +2046,11 @@ void AssemblyGraph::makeZipped(int minSize) { QList mainNodes1; int boundLen = min(minSize, (i.value()->getLength()) / 4); for (DeBruijnEdge* edge : node->getEnteringEdges()) { - DeBruijnNode* otherNode = edge->getOtherNode(node); - if (!otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { - dfsZipped(otherNode, boundLen, &mainNodes1, &zippedNodes1); + if (!edge->isHiC()) { + DeBruijnNode* otherNode = edge->getOtherNode(node); + if (!otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { + dfsZipped(otherNode, boundLen, &mainNodes1, &zippedNodes1); + } } } @@ -2098,9 +2068,11 @@ void AssemblyGraph::makeZipped(int minSize) { QList zippedNodes2; QList mainNodes2; for (DeBruijnEdge* edge : node->getLeavingEdges()) { - DeBruijnNode* otherNode = edge->getOtherNode(node); - if (!otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { - dfsZipped(otherNode, boundLen, &mainNodes2, &zippedNodes2); + if (!edge->isHiC()) { + DeBruijnNode* otherNode = edge->getOtherNode(node); + if (!otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { + dfsZipped(otherNode, boundLen, &mainNodes2, &zippedNodes2); + } } } @@ -3279,14 +3251,6 @@ int AssemblyGraph::getDrawnNodeCount() const return nodeCount; } -void AssemblyGraph::deleteNode(DeBruijnNode* node) { - m_deBruijnGraphNodes.remove(node->getName()); - m_deBruijnGraphNodes.remove(node->getReverseComplement()->getName()); - deleteEdges(node->getEdgesPointer()); - deleteEdges(node->getReverseComplement()->getEdgesPointer()); - //delete node; -} - void AssemblyGraph::deleteNodes(std::vector * nodes) { //Build a list of nodes to delete. diff --git a/graph/assemblygraph.h b/graph/assemblygraph.h index 761c5e55..7ec2cfb6 100644 --- a/graph/assemblygraph.h +++ b/graph/assemblygraph.h @@ -230,15 +230,8 @@ class AssemblyGraph : public QObject void dfsTax(DeBruijnNode* node, unsigned int taxId, int rank, int distance); void addHiCEdges(std::vector startingNodes); void setInclusionFilterAuto(); - void deleteNode(DeBruijnNode* node); - bool deleteLeafIfNeeded(DeBruijnNode* node); - bool markChainIfNeeded(DeBruijnNode* node); - bool mergeMiddleNodeIfNeeded(DeBruijnNode* startingNode, DeBruijnNode* endingNode, DeBruijnNode* deletedNode); - void fixChainIfNeeded(std::vector* mergedNode); - void bfsAroundNode(DeBruijnNode* startNode); void dfsZipped(DeBruijnNode* curNode, int boundLen, QList* mainNodes, QList* zippedNodes); void createNodesUnion(QList mainNodes, QList zippedNodes, QString unionName); - double AssemblyGraph::getHiCMinNormalizedWeightByTax(); signals: void setMergeTotalCount(int totalCount); From d5a26c274098c89af7893992bffa3f61e06aa6c3 Mon Sep 17 00:00:00 2001 From: Shostina Date: Sat, 3 Dec 2022 12:36:43 +0300 Subject: [PATCH 11/11] Add feature forest painting --- blast/BlastFeaturesNodesMatcher.cpp | 54 + blast/BlastFeaturesNodesMatcher.h | 18 + blast/blasthit.cpp | 8 +- blast/blastqueries.cpp | 22 + blast/blastqueries.h | 2 + blast/blastquery.h | 3 + graph/assemblygraph.cpp | 8 +- graph/graphicsitemnode.cpp | 596 +--- graph/graphicsitemnode.h | 71 +- ogdf/tree/TreeLayout.cpp | 1099 ++++++++ ogdf/tree/TreeLayout.h | 303 ++ painting/CommonGraphicsItemNode.cpp | 457 +++ painting/CommonGraphicsItemNode.h | 87 + painting/NodeMoving.cpp | 92 + painting/NodeMoving.h | 22 + program/TreeLayoutWorker.cpp | 44 + program/TreeLayoutWorker.h | 29 + program/globals.cpp | 2 + program/globals.h | 11 +- program/graphlayoutworker.cpp | 5 +- program/graphlayoutworker.h | 2 +- program/main.cpp | 5 + program/settings.cpp | 6 + program/settings.h | 10 + random_forest/GraphicsItemFeatureEdge.cpp | 37 + random_forest/GraphicsItemFeatureEdge.h | 24 + random_forest/GraphicsItemFeatureNode.cpp | 518 ++++ random_forest/GraphicsItemFeatureNode.h | 50 + random_forest/RandomForestEdge.cpp | 0 random_forest/RandomForestEdge.h | 17 + random_forest/RandomForestNode.cpp | 48 + random_forest/RandomForestNode.h | 89 + random_forest/assemblyforest.cpp | 251 ++ random_forest/assemblyforest.h | 76 + ui/mainwindow.cpp | 465 ++- ui/mainwindow.h | 36 +- ui/mainwindow.ui | 3136 +++++++++++---------- ui/mygraphicsscene.cpp | 60 +- ui/mygraphicsscene.h | 8 +- ui/mygraphicsview.cpp | 7 +- ui/randomforestmainwindow.cpp | 180 ++ ui/randomforestmainwindow.h | 130 + ui/randomforestmainwindow.ui | 1968 +++++++++++++ ui/taxinfodialog.cpp | 49 +- ui/taxinfodialog.h | 3 +- ui/taxinfodialog.ui | 7 + 46 files changed, 8080 insertions(+), 2035 deletions(-) create mode 100644 blast/BlastFeaturesNodesMatcher.cpp create mode 100644 blast/BlastFeaturesNodesMatcher.h create mode 100644 ogdf/tree/TreeLayout.cpp create mode 100644 ogdf/tree/TreeLayout.h create mode 100644 painting/CommonGraphicsItemNode.cpp create mode 100644 painting/CommonGraphicsItemNode.h create mode 100644 painting/NodeMoving.cpp create mode 100644 painting/NodeMoving.h create mode 100644 program/TreeLayoutWorker.cpp create mode 100644 program/TreeLayoutWorker.h create mode 100644 random_forest/GraphicsItemFeatureEdge.cpp create mode 100644 random_forest/GraphicsItemFeatureEdge.h create mode 100644 random_forest/GraphicsItemFeatureNode.cpp create mode 100644 random_forest/GraphicsItemFeatureNode.h create mode 100644 random_forest/RandomForestEdge.cpp create mode 100644 random_forest/RandomForestEdge.h create mode 100644 random_forest/RandomForestNode.cpp create mode 100644 random_forest/RandomForestNode.h create mode 100644 random_forest/assemblyforest.cpp create mode 100644 random_forest/assemblyforest.h create mode 100644 ui/randomforestmainwindow.cpp create mode 100644 ui/randomforestmainwindow.h create mode 100644 ui/randomforestmainwindow.ui diff --git a/blast/BlastFeaturesNodesMatcher.cpp b/blast/BlastFeaturesNodesMatcher.cpp new file mode 100644 index 00000000..b9a608bf --- /dev/null +++ b/blast/BlastFeaturesNodesMatcher.cpp @@ -0,0 +1,54 @@ +#include "BlastFeaturesNodesMatcher.h" +#include "BuildBlastDatabaseWorker.h" +#include "RunBlastSearchWorker.h" +#include "../blast/blastsearch.h" +#include "../blast/blasthit.h" +#include "../blast/blastquery.h" +#include "../program/globals.h" +#include "../program/settings.h" + +BlastFeaturesNodesMatcher::BlastFeaturesNodesMatcher() { + m_makeblastdbCommand = "makeblastdb"; + m_blastnCommand = "blastn"; + m_tblastnCommand = "tblastn"; +} + +void BlastFeaturesNodesMatcher::matchFeaturesNode(RandomForestNode* selectedNode) { + std::vector querySequences = selectedNode->getQuerySequences(); + if ((querySequences.size()!= 0) && (selectedNode->getBlastColourInd() == - 1)) { + //build blast db + if (!g_blastSearch->findProgram("makeblastdb", &m_makeblastdbCommand)) { + return; + } + + BuildBlastDatabaseWorker buildBlastDatabaseWorker(m_makeblastdbCommand); + buildBlastDatabaseWorker.buildBlastDatabase(); + + //add blast query + //g_blastSearch->cleanUp(); + QString featureNodeName = selectedNode->getName() + "_"; + int indexColour = g_blastSearch->m_blastQueries.m_queries.size(); + for (size_t i = 0; i < querySequences.size(); ++i) + { + QString queryName = featureNodeName + QString::number(i); + g_blastSearch->m_blastQueries.addQuery(new BlastQuery(queryName, querySequences[i]), indexColour, selectedNode->getClassInd()); + + } + //run blast search + + if (!g_blastSearch->findProgram("blastn", &m_blastnCommand)) + { + return; + } + if (!g_blastSearch->findProgram("tblastn", &m_tblastnCommand)) + { + return; + } + + g_blastSearch->clearBlastHits(); + + RunBlastSearchWorker runBlastSearchWorker(m_blastnCommand, m_tblastnCommand, ""); + runBlastSearchWorker.runBlastSearch(); + selectedNode->setBlastColourInd(indexColour); + } +} diff --git a/blast/BlastFeaturesNodesMatcher.h b/blast/BlastFeaturesNodesMatcher.h new file mode 100644 index 00000000..44236aca --- /dev/null +++ b/blast/BlastFeaturesNodesMatcher.h @@ -0,0 +1,18 @@ +#ifndef BLASTFEATURESNODESMATCHER_H +#define BLASTFEATURESNODESMATCHER_H + +#include +#include "../random_forest/RandomForestNode.h" + +class BlastFeaturesNodesMatcher +{ +public: + BlastFeaturesNodesMatcher(); + void matchFeaturesNode(RandomForestNode* selectedNode); +private: + QString m_makeblastdbCommand; + QString m_blastnCommand; + QString m_tblastnCommand; +}; + +#endif //BLASTFEATURESNODESMATCHER_H \ No newline at end of file diff --git a/blast/blasthit.cpp b/blast/blasthit.cpp index b2e8cd42..f6439d7e 100644 --- a/blast/blasthit.cpp +++ b/blast/blasthit.cpp @@ -86,7 +86,12 @@ std::vector BlastHit::getBlastHitParts(bool reverse, double scaled queryFraction += querySpacing; } } - + else if (g_settings->nodeColourScheme == BLAST_HITS_CLASS_COLOURS) { + if (reverse) + returnVector.push_back(BlastHitPart(m_query->getFeatureClassColour(), 1.0 - m_nodeStartFraction, 1.0 - m_nodeEndFraction)); + else + returnVector.push_back(BlastHitPart(m_query->getFeatureClassColour(), m_nodeStartFraction, m_nodeEndFraction)); + } //If the colour scheme is Blast solid, then this function generates only one //BlastHitPart with a colour dependent on the Blast query. else @@ -96,7 +101,6 @@ std::vector BlastHit::getBlastHitParts(bool reverse, double scaled else returnVector.push_back(BlastHitPart(m_query->getColour(), m_nodeStartFraction, m_nodeEndFraction)); } - return returnVector; } diff --git a/blast/blastqueries.cpp b/blast/blastqueries.cpp index b6cd46ec..a84ca68a 100644 --- a/blast/blastqueries.cpp +++ b/blast/blastqueries.cpp @@ -66,6 +66,28 @@ void BlastQueries::addQuery(BlastQuery * newQuery) updateTempFiles(); } +void BlastQueries::addQuery(BlastQuery* newQuery, int colourIndex) +{ + newQuery->setName(getUniqueName(newQuery->getName())); + + colourIndex %= m_presetColours.size(); + newQuery->setColour(m_presetColours[colourIndex]); + m_queries.push_back(newQuery); + updateTempFiles(); +} + +void BlastQueries::addQuery(BlastQuery* newQuery, int colourIndex, int featureClassColour) +{ + newQuery->setName(getUniqueName(newQuery->getName())); + + colourIndex %= m_presetColours.size(); + newQuery->setColour(m_presetColours[colourIndex]); + featureClassColour %= m_presetColours.size(); + newQuery->setFeatureClassColour(m_presetColours[featureClassColour]); + m_queries.push_back(newQuery); + updateTempFiles(); +} + //This function renames the query. It returns the name given, because that //might not be exactly the same as the name passed to the function if it diff --git a/blast/blastqueries.h b/blast/blastqueries.h index 5987fe18..12680609 100644 --- a/blast/blastqueries.h +++ b/blast/blastqueries.h @@ -55,6 +55,8 @@ class BlastQueries int getQueryCount(SequenceType sequenceType); bool isQueryPresent(BlastQuery * query); void findQueryPaths(); + void addQuery(BlastQuery* newQuery, int colourIndex); + void addQuery(BlastQuery* newQuery, int colourIndex, int featureClassColour); std::vector m_presetColours; diff --git a/blast/blastquery.h b/blast/blastquery.h index 612f991e..bc6e49cf 100644 --- a/blast/blastquery.h +++ b/blast/blastquery.h @@ -46,6 +46,7 @@ class BlastQuery : public QObject QList< QSharedPointer > getHits() const {return m_hits;} bool wasSearchedFor() const {return m_searchedFor;} QColor getColour() const {return m_colour;} + QColor getFeatureClassColour() const {return m_featureClassColour;} SequenceType getSequenceType() const {return m_sequenceType;} QList getPaths() const {return m_paths;} int getPathCount() const {return m_paths.size();} @@ -63,6 +64,7 @@ class BlastQuery : public QObject public slots: void setColour(QColor newColour) {m_colour = newColour;} + void setFeatureClassColour(QColor newColour) { m_featureClassColour = newColour; } void setShown(bool newShown) {m_shown = newShown;} private: @@ -71,6 +73,7 @@ public slots: QList< QSharedPointer > m_hits; bool m_searchedFor; QColor m_colour; + QColor m_featureClassColour; SequenceType m_sequenceType; QList m_paths; bool m_shown; diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index 167ebc98..20c176fd 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -1991,10 +1991,6 @@ QPair AssemblyGraph::dfsComponent(DeBruijnNode * no if (deleteLeafIfNeeded(node)) { return qMakePair(contigCount, size); } - if (markChainIfNeeded(node)) { - mergedNode->push_back(node); - return qMakePair(contigCount, size); - } } for (DeBruijnEdge* edge : node->getLeavingEdges()) { if (edge->getEndingNode()->getComponentId() == 0 && !edge->isHiC()) { @@ -2060,7 +2056,7 @@ void AssemblyGraph::makeZipped(int minSize) { int boundLen = min(minSize, (i.value()->getLength()) / 4); for (DeBruijnEdge* edge : node->getEnteringEdges()) { DeBruijnNode* otherNode = edge->getOtherNode(node); - if (!otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { + if (!edge->isHiC() && !otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { dfsZipped(otherNode, boundLen, &mainNodes1, &zippedNodes1); } } @@ -2080,7 +2076,7 @@ void AssemblyGraph::makeZipped(int minSize) { QList mainNodes2; for (DeBruijnEdge* edge : node->getLeavingEdges()) { DeBruijnNode* otherNode = edge->getOtherNode(node); - if (!otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { + if (!edge->isHiC() && !otherNode->isDrawn() && !(otherNode->isNodeUnion()) && !otherNode->isZipped() && otherNode->getLength() < boundLen) { dfsZipped(otherNode, boundLen, &mainNodes2, &zippedNodes2); } } diff --git a/graph/graphicsitemnode.cpp b/graph/graphicsitemnode.cpp index c14bbc57..860563f7 100644 --- a/graph/graphicsitemnode.cpp +++ b/graph/graphicsitemnode.cpp @@ -44,13 +44,14 @@ #include #include #include "../program/memory.h" +#include GraphicsItemNode::GraphicsItemNode(DeBruijnNode * deBruijnNode, - ogdf::GraphAttributes * graphAttributes, QGraphicsItem * parent) : - QGraphicsItem(parent), m_deBruijnNode(deBruijnNode), - m_hasArrow(g_settings->doubleMode || g_settings->arrowheadsInSingleMode) + ogdf::GraphAttributes * graphAttributes, CommonGraphicsItemNode * parent) : + CommonGraphicsItemNode(g_graphicsView, parent), m_deBruijnNode(deBruijnNode) { + m_hasArrow = g_settings->doubleMode || g_settings->arrowheadsInSingleMode; setWidth(); //m_width = g_settings->averageNodeWidth; @@ -87,13 +88,13 @@ GraphicsItemNode::GraphicsItemNode(DeBruijnNode * deBruijnNode, //This constructor makes a new GraphicsItemNode by copying the line points of //the given node. -GraphicsItemNode::GraphicsItemNode(DeBruijnNode * deBruijnNode, - GraphicsItemNode * toCopy, - QGraphicsItem * parent) : - QGraphicsItem(parent), m_deBruijnNode(deBruijnNode), - m_hasArrow(toCopy->m_hasArrow), - m_linePoints(toCopy->m_linePoints) +GraphicsItemNode::GraphicsItemNode(DeBruijnNode* deBruijnNode, + GraphicsItemNode* toCopy, + CommonGraphicsItemNode* parent) : + CommonGraphicsItemNode(g_graphicsView, parent), m_deBruijnNode(deBruijnNode) { + m_hasArrow = toCopy->m_hasArrow; + m_linePoints = toCopy->m_linePoints; setWidth(); remakePath(); } @@ -102,17 +103,15 @@ GraphicsItemNode::GraphicsItemNode(DeBruijnNode * deBruijnNode, //line points. GraphicsItemNode::GraphicsItemNode(DeBruijnNode * deBruijnNode, std::vector linePoints, - QGraphicsItem * parent) : - QGraphicsItem(parent), m_deBruijnNode(deBruijnNode), - m_hasArrow(g_settings->doubleMode), - m_linePoints(linePoints) + CommonGraphicsItemNode * parent) : + CommonGraphicsItemNode(g_graphicsView, parent), m_deBruijnNode(deBruijnNode) { + m_hasArrow = g_settings->doubleMode; + m_linePoints = linePoints; setWidth(); remakePath(); } - - void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem *, QWidget *) { //This code lets me see the node's bounding box. @@ -122,7 +121,7 @@ void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem // painter->drawRect(boundingRect()); if (m_deBruijnNode->isNodeUnion()) { - + QPainterPath outlinePath = shape(); int width = g_settings->averageNodeWidth; int x = m_linePoints[0].x(); int y = m_linePoints[0].y(); @@ -135,6 +134,7 @@ void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem painter->setPen(QPen(Qt::black, 1.0)); QRect r(x, y, width, width); r.moveCenter(m_linePoints[0].toPoint()); + painter->fillPath(outlinePath, brush); painter->drawEllipse(r); return; } @@ -159,7 +159,8 @@ void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem //If the node contains a BLAST hit, draw that on top. if (nodeHasBlastHits && (g_settings->nodeColourScheme == BLAST_HITS_RAINBOW_COLOUR || - g_settings->nodeColourScheme == BLAST_HITS_SOLID_COLOUR)) + g_settings->nodeColourScheme == BLAST_HITS_SOLID_COLOUR || + g_settings->nodeColourScheme == BLAST_HITS_CLASS_COLOURS)) { std::vector parts; @@ -235,27 +236,7 @@ void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem if (anyNodeDisplayText()) { QStringList nodeText = getNodeText(); - QPainterPath textPath; - - QFontMetrics metrics(g_settings->labelFont); - double fontHeight = metrics.ascent(); - - for (int i = 0; i < nodeText.size(); ++i) - { - QString text = nodeText.at(i); - int stepsUntilLast = nodeText.size() - 1 - i; - double shiftLeft = -metrics.width(text) / 2.0; - textPath.addText(shiftLeft, -stepsUntilLast * fontHeight, g_settings->labelFont, text); - } - - std::vector centres; - if (g_settings->positionTextNodeCentre) - centres.push_back(getCentre(m_linePoints)); - else - centres = getCentres(); - - for (size_t i = 0; i < centres.size(); ++i) - drawTextPathAtLocation(painter, textPath, centres[i]); + drawNodeText(painter, nodeText); } //Draw BLAST hit labels, if appropriate. @@ -284,47 +265,9 @@ void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem } } - -void GraphicsItemNode::drawTextPathAtLocation(QPainter * painter, QPainterPath textPath, QPointF centre) -{ - QRectF textBoundingRect = textPath.boundingRect(); - double textHeight = textBoundingRect.height(); - QPointF offset(0.0, textHeight / 2.0); - - double zoom = g_absoluteZoom; - if (zoom == 0.0) - zoom = 1.0; - - double zoomAdjustment = 1.0 / (1.0 + ((zoom - 1.0) * g_settings->textZoomScaleFactor)); - double inverseZoomAdjustment = 1.0 / zoomAdjustment; - - painter->translate(centre); - painter->rotate(-g_graphicsView->getRotation()); - painter->scale(zoomAdjustment, zoomAdjustment); - painter->translate(offset); - - if (g_settings->textOutline) - { - painter->setPen(QPen(g_settings->textOutlineColour, - g_settings->textOutlineThickness * 2.0, - Qt::SolidLine, - Qt::SquareCap, - Qt::RoundJoin)); - painter->drawPath(textPath); - } - - painter->fillPath(textPath, QBrush(g_settings->textColour)); - painter->translate(-offset); - painter->scale(inverseZoomAdjustment, inverseZoomAdjustment); - painter->rotate(g_graphicsView->getRotation()); - painter->translate(-centre); -} - - - void GraphicsItemNode::setNodeColour() { - if (g_settings->addNewNodes && !m_deBruijnNode->m_isNew) { + if (g_settings->nodeColourScheme == SAVE_COLOURS && m_deBruijnNode->m_lastColor.isValid()) { m_colour = m_deBruijnNode->m_lastColor; return; } @@ -420,7 +363,7 @@ void GraphicsItemNode::setNodeColour() } break; } - case RANDOM_COLOURS: + case (RANDOM_COLOURS || SAVE_COLOURS): { //Make a colour with a random hue. Assign a colour to both this node and //it complement so their hue matches. @@ -478,6 +421,11 @@ void GraphicsItemNode::setNodeColour() break; } + case BLAST_HITS_CLASS_COLOURS: + { + m_colour = g_settings->noBlastHitsColour; + break; + } case CUSTOM_COLOURS: { m_colour = m_deBruijnNode->getCustomColourForDisplay(); @@ -569,6 +517,12 @@ QColor GraphicsItemNode::propagateColour() { QPainterPath GraphicsItemNode::shape() const { + if (m_deBruijnNode->isNodeUnion()) { + int width = g_settings->averageNodeWidth; + QPainterPath mainNodePath; + mainNodePath.addEllipse(m_linePoints[0].toPoint(), width, width); + return mainNodePath; + } //If there is only one segment and it is shorter than half its //width, then the arrow head will not be made with 45 degree //angles, but rather whatever angle is made by going from the @@ -625,51 +579,68 @@ QPainterPath GraphicsItemNode::shape() const return mainNodePath.subtracted(subtractionPath); } - void GraphicsItemNode::mousePressEvent(QGraphicsSceneMouseEvent * event) { - m_grabIndex = 0; - QPointF grabPoint = event->pos(); - - double closestPointDistance = distance(grabPoint, m_linePoints[0]); - for (size_t i = 1; i < m_linePoints.size(); ++i) - { - double pointDistance = distance(grabPoint, m_linePoints[i]); - if (pointDistance < closestPointDistance) - { - closestPointDistance = pointDistance; - m_grabIndex = i; - } - } + updateGrabIndex(event); } +//When this node graphics item is moved, each of the connected edge +//graphics items will need to be adjusted accordingly. +void GraphicsItemNode::mouseRoundEvent(QGraphicsSceneMouseEvent* event) { + +} //When this node graphics item is moved, each of the connected edge //graphics items will need to be adjusted accordingly. void GraphicsItemNode::mouseMoveEvent(QGraphicsSceneMouseEvent * event) { - QPointF difference = event->pos() - event->lastPos(); + if (g_settings->roundMode) { + QPointF lastPos = event->lastPos(); //B + QPointF newPos = event->pos(); //C + QPointF centralPos; //A + if (m_grabIndex > m_linePoints.size() / 2) { + centralPos = m_linePoints[0]; + } + else { + centralPos = m_linePoints[m_linePoints.size() - 1]; + } - //If this node is selected, then move all of the other selected nodes too. - //If it is not selected, then only move this node. - std::vector nodesToMove; - MyGraphicsScene * graphicsScene = dynamic_cast(scene()); - if (isSelected()) - nodesToMove = graphicsScene->getSelectedGraphicsItemNodes(); - else + MyGraphicsScene* graphicsScene = dynamic_cast(scene()); + + std::vector nodesToMove; nodesToMove.push_back(this); + double alpha = angleBetweenTwoLines(centralPos, lastPos, centralPos, newPos); + roundPoints(centralPos, alpha); + remakePath(); + graphicsScene->possiblyExpandSceneRectangle(&nodesToMove); - for (size_t i = 0; i < nodesToMove.size(); ++i) - { - nodesToMove[i]->shiftPoints(difference); - nodesToMove[i]->remakePath(); + fixEdgePaths(&nodesToMove); } - graphicsScene->possiblyExpandSceneRectangle(&nodesToMove); + else { + QPointF difference = event->pos() - event->lastPos(); + + //If this node is selected, then move all of the other selected nodes too. + //If it is not selected, then only move this node. + std::vector nodesToMove; + MyGraphicsScene* graphicsScene = dynamic_cast(scene()); + if (isSelected()) + nodesToMove = graphicsScene->getSelectedGraphicsItemNodes(); + else + nodesToMove.push_back(this); - fixEdgePaths(&nodesToMove); + for (size_t i = 0; i < nodesToMove.size(); ++i) + { + nodesToMove[i]->shiftPoints(difference); + nodesToMove[i]->remakePath(); + } + graphicsScene->possiblyExpandSceneRectangle(&nodesToMove); + + fixEdgePaths(&nodesToMove); + } } + //This function remakes edge paths. If nodes is passed, it will remake the //edge paths for all of the nodes. If nodes isn't passed, then it will just //do it for this node. @@ -715,281 +686,14 @@ void GraphicsItemNode::fixEdgePaths(std::vector * nodes) } } - -void GraphicsItemNode::shiftPoints(QPointF difference) -{ - prepareGeometryChange(); - - if (g_settings->nodeDragging == NO_DRAGGING) - return; - - else if (isSelected()) //Move all pieces for selected nodes - { - for (size_t i = 0; i < m_linePoints.size(); ++i) - m_linePoints[i] += difference; - } - - else if (g_settings->nodeDragging == ONE_PIECE) - m_linePoints[m_grabIndex] += difference; - - else if (g_settings->nodeDragging == NEARBY_PIECES) - { - for (size_t i = 0; i < m_linePoints.size(); ++i) - { - int indexDistance = abs(int(i) - int(m_grabIndex)); - double dragStrength = pow(2.0, -1.0 * pow(double(indexDistance), 1.8) / g_settings->dragStrength); //constants chosen for dropoff of drag strength - m_linePoints[i] += difference * dragStrength; - } - } -} - -void GraphicsItemNode::remakePath() -{ - QPainterPath path; - - path.moveTo(m_linePoints[0]); - if (m_linePoints.size() <= 2) { - for (size_t i = 1; i < m_linePoints.size(); ++i) - path.lineTo(m_linePoints[i]); - } - else { - int middleInd = m_linePoints.size() / 2; - for (size_t i = 1; i < middleInd - 1; ++i) - path.lineTo(m_linePoints[i]); - path.quadTo(m_linePoints[middleInd], m_linePoints[middleInd + 1]); - - for (size_t i = middleInd + 1; i < m_linePoints.size(); ++i) - path.lineTo(m_linePoints[i]); - } - - m_path = path; -} - - -QPainterPath GraphicsItemNode::makePartialPath(double startFraction, double endFraction) -{ - if (endFraction < startFraction) - std::swap(startFraction, endFraction); - - double totalLength = getNodePathLength(); - - QPainterPath path; - bool pathStarted = false; - double lengthSoFar = 0.0; - for (size_t i = 0; i < m_linePoints.size() - 1; ++i) - { - QPointF point1 = m_linePoints[i]; - QPointF point2 = m_linePoints[i + 1]; - QLineF line(point1, point2); - - double point1Fraction = lengthSoFar / totalLength; - lengthSoFar += line.length(); - double point2Fraction = lengthSoFar / totalLength; - - //If the path hasn't yet begun and this segment is before - //the starting fraction, do nothing. - if (!pathStarted && point2Fraction < startFraction) - continue; - - //If the path hasn't yet begun but this segment covers the starting - //fraction, start the path now. - if (!pathStarted && point2Fraction >= startFraction) - { - pathStarted = true; - path.moveTo(findIntermediatePoint(point1, point2, point1Fraction, point2Fraction, startFraction)); - } - - //If the path is in progress and this segment hasn't yet reached the end, - //just continue the path. - if (pathStarted && point2Fraction < endFraction) - path.lineTo(point2); - - //If the path is in progress and this segment passes the end, finish the line. - if (pathStarted && point2Fraction >= endFraction) - { - path.lineTo(findIntermediatePoint(point1, point2, point1Fraction, point2Fraction, endFraction)); - return path; - } - } - - return path; -} - - -double GraphicsItemNode::getNodePathLength() -{ - double totalLength = 0.0; - for (size_t i = 0; i < m_linePoints.size() - 1; ++i) - { - QLineF line(m_linePoints[i], m_linePoints[i + 1]); - totalLength += line.length(); - } - return totalLength; -} - - -//This function will find the point that is a certain fraction of the way along the node's path. -QPointF GraphicsItemNode::findLocationOnPath(double fraction) -{ - double totalLength = getNodePathLength(); - - double lengthSoFar = 0.0; - for (size_t i = 0; i < m_linePoints.size() - 1; ++i) - { - QPointF point1 = m_linePoints[i]; - QPointF point2 = m_linePoints[i + 1]; - QLineF line(point1, point2); - - double point1Fraction = lengthSoFar / totalLength; - lengthSoFar += line.length(); - double point2Fraction = lengthSoFar / totalLength; - - //If point2 hasn't yet reached the target, do nothing. - if (point2Fraction < fraction) - continue; - - //If the path hasn't yet begun but this segment covers the starting - //fraction, start the path now. - if (point2Fraction >= fraction) - return findIntermediatePoint(point1, point2, point1Fraction, point2Fraction, fraction); - } - - //The code shouldn't get here, as the target point should have been found in the above loop. - return QPointF(); -} - -QPointF GraphicsItemNode::findIntermediatePoint(QPointF p1, QPointF p2, double p1Value, double p2Value, double targetValue) -{ - QPointF difference = p2 - p1; - double fraction = (targetValue - p1Value) / (p2Value - p1Value); - return difference * fraction + p1; -} - -double GraphicsItemNode::distance(QPointF p1, QPointF p2) const -{ - double xDiff = p1.x() - p2.x(); - double yDiff = p1.y() - p2.y(); - return sqrt(xDiff * xDiff + yDiff * yDiff); -} - - bool GraphicsItemNode::usePositiveNodeColour() { return !m_hasArrow || m_deBruijnNode->isPositiveNode(); } - - - -//This function returns the nodes' visible centres. If the entire node is visible, -//then there is just one visible centre. If none of the node is visible, then -//there are no visible centres. If multiple parts of the node are visible, then there -//are multiple visible centres. -std::vector GraphicsItemNode::getCentres() const -{ - std::vector centres; - std::vector currentRun; - - QPointF lastP; - bool lastPointVisible = false; - - for (size_t i = 0; i < m_linePoints.size(); ++i) - { - QPointF p = m_linePoints[i]; - bool pVisible = g_graphicsView->isPointVisible(p); - - //If this point is visible, but the last wasn't, a new run is started. - if (pVisible && !lastPointVisible) - { - //If this is not the first point, then we need to find the intermediate - //point that lies on the visible boundary and start the path with that. - if (i > 0) - currentRun.push_back(g_graphicsView->findIntersectionWithViewportBoundary(QLineF(p, lastP))); - currentRun.push_back(p); - } - - //If th last point is visible and this one is too, add it to the current run. - else if (pVisible && lastPointVisible) - currentRun.push_back(p); - - //If the last point is visible and this one isn't, then a run has ended. - else if (!pVisible && lastPointVisible) - { - //We need to find the intermediate point that is on the visible boundary. - currentRun.push_back(g_graphicsView->findIntersectionWithViewportBoundary(QLineF(p, lastP))); - - centres.push_back(getCentre(currentRun)); - currentRun.clear(); - } - - //If neither this point nor the last were visible, we still need to check whether - //the line segment between them is. If so, then then this may be a case where - //we are really zoomed in (and so line segments are large compared to the scene rect). - else if (i > 0 && !pVisible && !lastPointVisible) - { - bool success; - QLineF v = g_graphicsView->findVisiblePartOfLine(QLineF(lastP, p), &success); - if (success) - { - QPointF vCentre = QPointF((v.p1().x() + v.p2().x()) / 2.0, (v.p1().y() + v.p2().y()) / 2.0); - centres.push_back(vCentre); - } - } - - lastPointVisible = pVisible; - lastP = p; - } - - //If there is a current run, add its centre - if (currentRun.size() > 0) - centres.push_back(getCentre(currentRun)); - - return centres; -} - - - -//This function finds the centre point on the path defined by linePoints. -QPointF GraphicsItemNode::getCentre(std::vector linePoints) const -{ - if (linePoints.size() == 0) - return QPointF(); - if (linePoints.size() == 1) - return linePoints[0]; - - double pathLength = 0.0; - for (size_t i = 0; i < linePoints.size() - 1; ++i) - pathLength += distance(linePoints[i], linePoints[i+1]); - - double endToCentre = pathLength / 2.0; - - double lengthSoFar = 0.0; - for (size_t i = 0; i < linePoints.size() - 1; ++i) - { - QPointF a = linePoints[i]; - QPointF b = linePoints[i+1]; - double segmentLength = distance(a, b); - - //If this segment will push the distance over halfway, then it - //contains the centre point. - if (lengthSoFar + segmentLength >= endToCentre) - { - double additionalLengthNeeded = endToCentre - lengthSoFar; - double fractionOfCurrentSegment = additionalLengthNeeded / segmentLength; - return (b - a) * fractionOfCurrentSegment + a; - } - - lengthSoFar += segmentLength; - } - - //Code should never get here. - return QPointF(); -} - QStringList GraphicsItemNode::getNodeText() { QStringList nodeText; - if (g_settings->displayNodeCustomLabels) nodeText << m_deBruijnNode->getCustomLabelForDisplay(); if (g_settings->displayNodeNames) @@ -1029,14 +733,12 @@ QStringList GraphicsItemNode::getNodeText() return nodeText; } - QSize GraphicsItemNode::getNodeTextSize(QString text) { QFontMetrics fontMetrics(g_settings->labelFont); return fontMetrics.size(0, text); } - QColor GraphicsItemNode::getDepthColour() { double depth = m_deBruijnNode->getDepth(); @@ -1073,8 +775,6 @@ QColor GraphicsItemNode::getDepthColour() return QColor(red, green, blue, alpha); } - - void GraphicsItemNode::setWidth() { m_width = getNodeWidth(m_deBruijnNode->getDepthRelativeToMeanDrawnDepth(), g_settings->depthPower, @@ -1083,8 +783,6 @@ void GraphicsItemNode::setWidth() m_width = 0.0; } - - //The bounding rectangle of a node has to be a little bit bigger than //the node's path, because of the outline. The selection outline is //the largest outline we can expect, so use that to define the bounding @@ -1112,79 +810,6 @@ double GraphicsItemNode::getNodeWidth(double depthRelativeToMeanDrawnDepth, doub return averageNodeWidth * widthRelativeToAverage; } - - -//This function shifts all the node's points to the left (relative to its -//direction). This is used in double mode to prevent nodes from displaying -//directly on top of their complement nodes. -void GraphicsItemNode::shiftPointsLeft() -{ - shiftPointSideways(true); -} - -void GraphicsItemNode::shiftPointsRight() -{ - shiftPointSideways(false); -} - -void GraphicsItemNode::shiftPointSideways(bool left) -{ - prepareGeometryChange(); - - //The collection of line points should be at least - //two large. But just to be safe, quit now if it - //is not. - size_t linePointsSize = m_linePoints.size(); - if (linePointsSize < 2) - return; - - //Shift by a quarter of the segment length. This should make - //nodes one half segment length separated from their complements. - double shiftDistance = g_settings->doubleModeNodeSeparation; - - for (size_t i = 0; i < linePointsSize; ++i) - { - QPointF point = m_linePoints[i]; - QLineF nodeDirection; - - //If the point is on the end, then determine the node direction - //using this point and its adjacent point. - if (i == 0) - { - QPointF nextPoint = m_linePoints[i+1]; - nodeDirection = QLineF(point, nextPoint); - } - else if (i == linePointsSize - 1) - { - QPointF previousPoint = m_linePoints[i-1]; - nodeDirection = QLineF(previousPoint, point); - } - - // If the point is in the middle, then determine the node direction - //using both adjacent points. - else - { - QPointF previousPoint = m_linePoints[i-1]; - QPointF nextPoint = m_linePoints[i+1]; - nodeDirection = QLineF(previousPoint, nextPoint); - } - - QLineF shiftLine = nodeDirection.normalVector().unitVector(); - shiftLine.setLength(shiftDistance); - - QPointF shiftVector; - if (left) - shiftVector = shiftLine.p2() - shiftLine.p1(); - else - shiftVector = shiftLine.p1() - shiftLine.p2(); - QPointF newPoint = point + shiftVector; - m_linePoints[i] = newPoint; - } - - remakePath(); -} - - void GraphicsItemNode::getBlastHitsTextAndLocationThisNode(std::vector * blastHitText, std::vector * blastHitLocation) { @@ -1211,48 +836,53 @@ void GraphicsItemNode::getBlastHitsTextAndLocationThisNodeOrReverseComplement(st } } - - +bool GraphicsItemNode::anyNodeDisplayText() +{ + return g_settings->displayNodeCustomLabels || + g_settings->displayNodeNames || + g_settings->displayNodeLengths || + g_settings->displayNodeDepth || + g_settings->displayNodeCsvData || + g_settings->displayTaxIdName || + g_settings->displayTaxIdRank || + g_settings->displayTaxNameRank ; +} //This function outlines and shades the appropriate part of a node if it is //in the user-specified path. -void GraphicsItemNode::exactPathHighlightNode(QPainter * painter) +void GraphicsItemNode::exactPathHighlightNode(QPainter* painter) { if (g_memory->userSpecifiedPath.containsNode(m_deBruijnNode)) pathHighlightNode2(painter, m_deBruijnNode, false, &g_memory->userSpecifiedPath); if (!g_settings->doubleMode && - g_memory->userSpecifiedPath.containsNode(m_deBruijnNode->getReverseComplement())) + g_memory->userSpecifiedPath.containsNode(m_deBruijnNode->getReverseComplement())) pathHighlightNode2(painter, m_deBruijnNode->getReverseComplement(), true, &g_memory->userSpecifiedPath); } - - //This function outlines and shades the appropriate part of a node if it is //in the user-specified path. -void GraphicsItemNode::queryPathHighlightNode(QPainter * painter) +void GraphicsItemNode::queryPathHighlightNode(QPainter* painter) { if (g_memory->queryPaths.size() == 0) return; for (int i = 0; i < g_memory->queryPaths.size(); ++i) { - Path * path = &(g_memory->queryPaths[i]); + Path* path = &(g_memory->queryPaths[i]); if (path->containsNode(m_deBruijnNode)) pathHighlightNode2(painter, m_deBruijnNode, false, path); if (!g_settings->doubleMode && - path->containsNode(m_deBruijnNode->getReverseComplement())) + path->containsNode(m_deBruijnNode->getReverseComplement())) pathHighlightNode2(painter, m_deBruijnNode->getReverseComplement(), true, path); } } - - -void GraphicsItemNode::pathHighlightNode2(QPainter * painter, - DeBruijnNode * node, - bool reverse, - Path * path) +void GraphicsItemNode::pathHighlightNode2(QPainter* painter, + DeBruijnNode* node, + bool reverse, + Path* path) { int numberOfTimesInMiddle = path->numberOfOccurrencesInMiddleOfPath(node); for (int i = 0; i < numberOfTimesInMiddle; ++i) @@ -1275,26 +905,23 @@ void GraphicsItemNode::pathHighlightNode2(QPainter * painter, pathHighlightNode3(painter, buildPartialHighlightPath(0.0, path->getEndFraction(), reverse)); } - -void GraphicsItemNode::pathHighlightNode3(QPainter * painter, - QPainterPath highlightPath) +void GraphicsItemNode::pathHighlightNode3(QPainter* painter, + QPainterPath highlightPath) { QBrush shadingBrush(g_settings->pathHighlightShadingColour); painter->fillPath(highlightPath, shadingBrush); highlightPath = highlightPath.simplified(); QPen outlinePen(QBrush(g_settings->pathHighlightOutlineColour), - g_settings->selectionThickness, Qt::SolidLine, - Qt::SquareCap, Qt::RoundJoin); + g_settings->selectionThickness, Qt::SolidLine, + Qt::SquareCap, Qt::RoundJoin); painter->setPen(outlinePen); painter->drawPath(highlightPath); } - - QPainterPath GraphicsItemNode::buildPartialHighlightPath(double startFraction, - double endFraction, - bool reverse) + double endFraction, + bool reverse) { if (reverse) { @@ -1304,7 +931,7 @@ QPainterPath GraphicsItemNode::buildPartialHighlightPath(double startFraction, } QPainterPath partialPath = makePartialPath(startFraction, - endFraction); + endFraction); QPainterPathStroker stroker; @@ -1324,17 +951,4 @@ QPainterPath GraphicsItemNode::buildPartialHighlightPath(double startFraction, highlightPath = highlightPath.intersected(shape()); return highlightPath; -} - - -bool GraphicsItemNode::anyNodeDisplayText() -{ - return g_settings->displayNodeCustomLabels || - g_settings->displayNodeNames || - g_settings->displayNodeLengths || - g_settings->displayNodeDepth || - g_settings->displayNodeCsvData || - g_settings->displayTaxIdName || - g_settings->displayTaxIdRank || - g_settings->displayTaxNameRank ; -} +} \ No newline at end of file diff --git a/graph/graphicsitemnode.h b/graph/graphicsitemnode.h index ea97d63d..60d8e404 100644 --- a/graph/graphicsitemnode.h +++ b/graph/graphicsitemnode.h @@ -31,104 +31,57 @@ #include #include #include +#include "../painting/CommonGraphicsItemNode.h" class DeBruijnNode; -class Path; -class GraphicsItemNode : public QGraphicsItem +class GraphicsItemNode : public CommonGraphicsItemNode { public: GraphicsItemNode(DeBruijnNode * deBruijnNode, ogdf::GraphAttributes * graphAttributes, - QGraphicsItem * parent = 0); + CommonGraphicsItemNode* parent = 0); GraphicsItemNode(DeBruijnNode * deBruijnNode, GraphicsItemNode * toCopy, - QGraphicsItem * parent = 0); + CommonGraphicsItemNode* parent = 0); GraphicsItemNode(DeBruijnNode * deBruijnNode, std::vector linePoints, - QGraphicsItem * parent = 0); + CommonGraphicsItemNode* parent = 0); DeBruijnNode * m_deBruijnNode; - double m_width; - bool m_hasArrow; - std::vector m_linePoints; - size_t m_grabIndex; - QColor m_colour; - QPainterPath m_path; void mousePressEvent(QGraphicsSceneMouseEvent * event); void mouseMoveEvent(QGraphicsSceneMouseEvent * event); void paint(QPainter * painter, const QStyleOptionGraphicsItem *, QWidget *); QPainterPath shape() const; - void shiftPoints(QPointF difference); - void remakePath(); - double distance(QPointF p1, QPointF p2) const; + bool usePositiveNodeColour(); - QPointF getFirst() const {return m_linePoints[0];} - QPointF getSecond() const { - if (m_linePoints.size() > 1) return m_linePoints[1]; - else return m_linePoints[0]; - } - QPointF getLast() const {return m_linePoints[m_linePoints.size()-1];} - QPointF getSecondLast() const - { - if (m_linePoints.size() > 1) return m_linePoints[m_linePoints.size() - 2]; - else return m_linePoints[0]; - } - bool isBig() const { return m_linePoints.size() >= 3; } - bool isOne() const { return m_linePoints.size() == 1; } - QPointF getMiddle() const { return m_linePoints[m_linePoints.size() / 2]; } - QPointF getBeforeMiddle() const - { - if (m_linePoints.size() >= 3) - return m_linePoints[(m_linePoints.size() / 2) - 1]; - else - return m_linePoints[0]; - } - QPointF getAfterMiddle() const - { - if (m_linePoints.size() >= 3) - return m_linePoints[(m_linePoints.size() / 2) + 1]; - else - return m_linePoints[m_linePoints.size() - 1]; - } - std::vector getCentres() const; - QPointF getCentre(std::vector linePoints) const; void setNodeColour(); QColor propagateColour(); QStringList getNodeText(); QSize getNodeTextSize(QString text); QColor getDepthColour(); void setWidth(); - QPainterPath makePartialPath(double startFraction, double endFraction); - double getNodePathLength(); - QPointF findLocationOnPath(double fraction); - QPointF findIntermediatePoint(QPointF p1, QPointF p2, double p1Value, - double p2Value, double targetValue); QRectF boundingRect() const; static double getNodeWidth(double depthRelativeToMeanDrawnDepth, double depthPower, double depthEffectOnWidth, double averageNodeWidth); - void shiftPointsLeft(); - void shiftPointsRight(); void getBlastHitsTextAndLocationThisNode(std::vector * blastHitText, std::vector * blastHitLocation); void getBlastHitsTextAndLocationThisNodeOrReverseComplement(std::vector * blastHitText, std::vector * blastHitLocation); - void drawTextPathAtLocation(QPainter *painter, QPainterPath textPath, QPointF centre); void fixEdgePaths(std::vector * nodes = 0); - - void GraphicsItemNode::shiftAroundMiddle(); + void mouseRoundEvent(QGraphicsSceneMouseEvent* event); private: - void exactPathHighlightNode(QPainter * painter); - void queryPathHighlightNode(QPainter * painter); - void pathHighlightNode2(QPainter * painter, DeBruijnNode * node, bool reverse, Path * path); - void pathHighlightNode3(QPainter * painter, QPainterPath highlightPath); + void exactPathHighlightNode(QPainter* painter); + void queryPathHighlightNode(QPainter* painter); + void pathHighlightNode2(QPainter* painter, DeBruijnNode* node, bool reverse, Path* path); + void pathHighlightNode3(QPainter* painter, QPainterPath highlightPath); QPainterPath buildPartialHighlightPath(double startFraction, double endFraction, bool reverse); bool anyNodeDisplayText(); - void shiftPointSideways(bool left); + }; #endif // GRAPHICSITEMNODE_H diff --git a/ogdf/tree/TreeLayout.cpp b/ogdf/tree/TreeLayout.cpp new file mode 100644 index 00000000..1a2d386d --- /dev/null +++ b/ogdf/tree/TreeLayout.cpp @@ -0,0 +1,1099 @@ +/* + * $Revision: 3167 $ + * + * last checkin: + * $Author: beyer $ + * $Date: 2012-12-18 18:08:37 +0100 (Di, 18. Dez 2012) $ + ***************************************************************/ + + /** \file + * \brief Linear time layout algorithm for trees (TreeLayout) + * based on Walker's algorithm + * + * \author Christoph Buchheim + * + * \par License: + * This file is part of the Open Graph Drawing Framework (OGDF). + * + * \par + * Copyright (C)
+ * See README.txt in the root directory of the OGDF installation for details. + * + * \par + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 or 3 as published by the Free Software Foundation; + * see the file LICENSE.txt included in the packaging of this file + * for details. + * + * \par + * This program 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. + * + * \par + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * \see http://www.gnu.org/copyleft/gpl.html + ***************************************************************/ + + +#include "../ogdf/tree/TreeLayout.h" +#include "../ogdf/basic/List.h" +#include "../ogdf/basic/Math.h" + + +#include "../ogdf/basic/AdjEntryArray.h" +#include + + +#include "../ogdf/basic/simple_graph_alg.h" +#include "../ogdf/basic/Stack.h" + + +namespace ogdf { + + + TreeLayout::TreeLayout() + :m_siblingDistance(20), + m_subtreeDistance(20), + m_levelDistance(50), + m_treeDistance(50), + m_orthogonalLayout(false), + m_orientation(topToBottom), + m_selectRoot(rootIsSource), + m_pGraph(0) + { } + + + TreeLayout::TreeLayout(const TreeLayout& tl) + :m_siblingDistance(tl.m_siblingDistance), + m_subtreeDistance(tl.m_subtreeDistance), + m_levelDistance(tl.m_levelDistance), + m_treeDistance(tl.m_treeDistance), + m_orthogonalLayout(tl.m_orthogonalLayout), + m_orientation(tl.m_orientation), + m_selectRoot(tl.m_selectRoot) + { } + + + TreeLayout::~TreeLayout() + { } + + + TreeLayout& TreeLayout::operator=(const TreeLayout& tl) + { + m_siblingDistance = tl.m_siblingDistance; + m_subtreeDistance = tl.m_subtreeDistance; + m_levelDistance = tl.m_levelDistance; + m_treeDistance = tl.m_treeDistance; + m_orthogonalLayout = tl.m_orthogonalLayout; + m_orientation = tl.m_orientation; + m_selectRoot = tl.m_selectRoot; + return *this; + } + + + // comparer class used for sorting adjacency entries according to their angle + class TreeLayout::AdjComparer + { + public: + AdjComparer(const AdjEntryArray& angle) { + m_pAngle = ∠ + } + + int compare(const adjEntry& adjX, const adjEntry& adjY) const { + if ((*m_pAngle)[adjX] < (*m_pAngle)[adjY]) + return -1; + else + if ((*m_pAngle)[adjX] > (*m_pAngle)[adjY]) + return 1; + else + return 0; + } + OGDF_AUGMENT_COMPARER(adjEntry) + + private: + const AdjEntryArray* m_pAngle; + }; + + + + void TreeLayout::setRoot(GraphAttributes& AG, Graph& tree) + { + m_pGraph = &tree; + + NodeArray visited(tree, false); + StackPure S; + + node v; + forall_nodes(v, tree) + { + if (visited[v]) continue; + + // process a new connected component + node root = 0; + S.push(v); + + while (!S.empty()) + { + node x = S.pop(); + visited[x] = true; + + if (!root) { + if (m_selectRoot == rootIsSource) { + if (x->indeg() == 0) + root = x; + } + else if (m_selectRoot == rootIsSink) { + if (x->outdeg() == 0) + root = x; + } + else { // selectByCoordinate + root = x; + } + + } + else if (m_selectRoot == rootByCoord) { + switch (m_orientation) + { + case bottomToTop: + if (AG.y(x) < AG.y(root)) + root = x; + break; + case topToBottom: + if (AG.y(x) > AG.y(root)) + root = x; + break; + case leftToRight: + if (AG.x(x) < AG.x(root)) + root = x; + break; + case rightToLeft: + if (AG.x(x) > AG.x(root)) + root = x; + break; + } + } + + adjEntry adj; + forall_adj(adj, x) { + node w = adj->twinNode(); + if (!visited[w]) + S.push(w); + } + } + + if (root == 0) { + undoReverseEdges(AG); + OGDF_THROW_PARAM(PreconditionViolatedException, pvcForest); + } + + adjustEdgeDirections(tree, root, 0); + } + } + + + void TreeLayout::adjustEdgeDirections(Graph& G, node v, node parent) + { + adjEntry adj; + forall_adj(adj, v) { + node w = adj->twinNode(); + if (w == parent) continue; + edge e = adj->theEdge(); + if (w != e->target()) { + G.reverseEdge(e); + m_reversedEdges.pushBack(e); + } + adjustEdgeDirections(G, w, v); + } + } + + void TreeLayout::callSortByPositions(GraphAttributes& AG, Graph& tree) + { + OGDF_ASSERT(&tree == &(AG.constGraph())); + + if (!isFreeForest(tree)) + OGDF_THROW_PARAM(PreconditionViolatedException, pvcForest); + + setRoot(AG, tree); + + // stores angle of adjacency entry + AdjEntryArray angle(tree); + + AdjComparer cmp(angle); + + node v; + forall_nodes(v, tree) + { + // position of node v + double cx = AG.x(v); + double cy = AG.y(v); + + adjEntry adj; + forall_adj(adj, v) + { + // adjacent node + node w = adj->twinNode(); + + // relative position of w to v + double dx = AG.x(w) - cx; + double dy = AG.y(w) - cy; + + // if v and w lie on the same point ... + if (dx == 0 && dy == 0) { + angle[adj] = 0; + continue; + } + + if (m_orientation == leftToRight || m_orientation == rightToLeft) + swap(dx, dy); + if (m_orientation == topToBottom || m_orientation == rightToLeft) + dy = -dy; + + // compute angle of adj + double alpha = atan2(fabs(dx), fabs(dy)); + + if (dx < 0) { + if (dy < 0) + angle[adj] = alpha; + else + angle[adj] = Math::pi - alpha; + } + else { + if (dy > 0) + angle[adj] = Math::pi + alpha; + else + angle[adj] = 2 * Math::pi - alpha; + } + } + + // get list of all adjacency entries at v + SListPure entries; + tree.adjEntries(v, entries); + + // sort entries according to angle + entries.quicksort(cmp); + + // sort entries accordingly in tree + tree.sort(v, entries); + } + + // adjacency lists are now sorted, so we can apply the usual call + call(AG); + } + + + void TreeLayout::call(GraphAttributes& AG) + { + const Graph& tree = AG.constGraph(); + if (tree.numberOfNodes() == 0) return; + + if (!isForest(tree)) + OGDF_THROW_PARAM(PreconditionViolatedException, pvcForest); + + OGDF_ASSERT(m_siblingDistance > 0); + OGDF_ASSERT(m_subtreeDistance > 0); + OGDF_ASSERT(m_levelDistance > 0); + + // compute the tree structure + List roots; + initializeTreeStructure(tree, roots); + if (m_orientation == topToBottom || m_orientation == bottomToTop) + { + ListConstIterator it; + double minX = 0, maxX = 0; + for (it = roots.begin(); it.valid(); ++it) + { + node root = *it; + + // compute x-coordinates + firstWalk(root, AG, true); + secondWalkX(root, -m_preliminary[root], AG); + + // compute y-coordinates + computeYCoordinatesAndEdgeShapes(root, AG); + + if (it != roots.begin()) + { + findMinX(AG, root, minX); + + double shift = maxX + m_treeDistance - minX; + + shiftTreeX(AG, root, shift); + } + + findMaxX(AG, root, maxX); + } + + // The computed layout draws a tree downwards. If we want to draw the + // tree upwards, we simply invert all y-coordinates. + if (m_orientation == bottomToTop) + { + node v; + forall_nodes(v, tree) + AG.y(v) = -AG.y(v); + + edge e; + forall_edges(e, tree) { + ListIterator it; + for (it = AG.bends(e).begin(); it.valid(); ++it) + (*it).m_y = -(*it).m_y; + } + } + + } + else { + ListConstIterator it; + double minY = 0, maxY = 0; + for (it = roots.begin(); it.valid(); ++it) + { + node root = *it; + + // compute y-coordinates + firstWalk(root, AG, false); + secondWalkY(root, -m_preliminary[root], AG); + + // compute y-coordinates + computeXCoordinatesAndEdgeShapes(root, AG); + + if (it != roots.begin()) + { + findMinY(AG, root, minY); + + double shift = maxY + m_treeDistance - minY; + + shiftTreeY(AG, root, shift); + } + + findMaxY(AG, root, maxY); + } + + // The computed layout draws a tree upwards. If we want to draw the + // tree downwards, we simply invert all y-coordinates. + if (m_orientation == rightToLeft) + { + node v; + forall_nodes(v, tree) + AG.x(v) = -AG.x(v); + + edge e; + forall_edges(e, tree) { + ListIterator it; + for (it = AG.bends(e).begin(); it.valid(); ++it) + (*it).m_x = -(*it).m_x; + } + } + + } + + + // delete the tree structure + deleteTreeStructure(); + + // restore temporarily removed edges again + undoReverseEdges(AG); + } + + void TreeLayout::callMultiLine(GraphAttributes& AG, int numOfRows) + { + const Graph& tree = AG.constGraph(); + if (tree.numberOfNodes() == 0) return; + + if (!isForest(tree)) + OGDF_THROW_PARAM(PreconditionViolatedException, pvcForest); + + OGDF_ASSERT(m_siblingDistance > 0); + OGDF_ASSERT(m_subtreeDistance > 0); + OGDF_ASSERT(m_levelDistance > 0); + + // compute the tree structure + List roots; + initializeTreeStructure(tree, roots); + int numInRow; + if (numOfRows != 0) { + numInRow = max(roots.size() / numOfRows, 1); + } + else { + numInRow = sqrt(roots.size()); + } + if (m_orientation == topToBottom) + { + ListConstIterator it; + double minX = 0, minY = 0, maxX = 0, maxY = 0, yShift = 0; + int curNumInRow = 0; + for (it = roots.begin(); it.valid(); ++it) + { + node root = *it; + + // compute x-coordinates + firstWalk(root, AG, true); + secondWalkX(root, -m_preliminary[root], AG); + + // compute y-coordinates + computeYCoordinatesAndEdgeShapes(root, AG); + + bool firstInNewLine = false; + if (curNumInRow + 1 > numInRow) { + firstInNewLine = true; + curNumInRow = 0; + yShift = maxY; + minX = 0; + maxX = 0; + } + + if (it == roots.begin()) { + firstInNewLine = true; + } + + findMinY(AG, root, minY); + double shift = yShift + m_treeDistance - minY; + shiftTreeY(AG, root, shift); + + if (!firstInNewLine) + { + findMinX(AG, root, minX); + + double shift = maxX + m_treeDistance - minX; + + shiftTreeX(AG, root, shift); + } + + findMaxX(AG, root, maxX); + findMaxY(AG, root, maxY); + curNumInRow += 1; + } + } + + + // delete the tree structure + deleteTreeStructure(); + + // restore temporarily removed edges again + undoReverseEdges(AG); + } + + void TreeLayout::undoReverseEdges(GraphAttributes& AG) + { + if (m_pGraph) { + while (!m_reversedEdges.empty()) { + edge e = m_reversedEdges.popFrontRet(); + m_pGraph->reverseEdge(e); + AG.bends(e).reverse(); + } + + m_pGraph = 0; + } + } + + void TreeLayout::findMinX(GraphAttributes& AG, node root, double& minX) + { + Stack S; + S.push(root); + + while (!S.empty()) + { + node v = S.pop(); + + double left = AG.x(v) - AG.width(v) / 2; + if (left < minX) minX = left; + + edge e; + forall_adj_edges(e, v) { + node w = e->target(); + if (w != v) S.push(w); + } + } + } + + void TreeLayout::findMinY(GraphAttributes& AG, node root, double& minY) + { + Stack S; + S.push(root); + + while (!S.empty()) + { + node v = S.pop(); + + double left = AG.y(v) - AG.height(v) / 2; + if (left < minY) minY = left; + + edge e; + forall_adj_edges(e, v) { + node w = e->target(); + if (w != v) S.push(w); + } + } + } + + + void TreeLayout::shiftTreeX(GraphAttributes& AG, node root, double shift) + { + Stack S; + S.push(root); + while (!S.empty()) + { + node v = S.pop(); + + AG.x(v) += shift; + + edge e; + forall_adj_edges(e, v) { + node w = e->target(); + if (w != v) { + ListIterator itP; + for (itP = AG.bends(e).begin(); itP.valid(); ++itP) + (*itP).m_x += shift; + S.push(w); + } + } + } + } + + void TreeLayout::shiftTreeY(GraphAttributes& AG, node root, double shift) + { + Stack S; + S.push(root); + while (!S.empty()) + { + node v = S.pop(); + + AG.y(v) += shift; + + edge e; + forall_adj_edges(e, v) { + node w = e->target(); + if (w != v) { + ListIterator itP; + for (itP = AG.bends(e).begin(); itP.valid(); ++itP) + (*itP).m_y += shift; + S.push(w); + } + } + } + } + + + void TreeLayout::findMaxX(GraphAttributes& AG, node root, double& maxX) + { + Stack S; + S.push(root); + while (!S.empty()) + { + node v = S.pop(); + + double right = AG.x(v) + AG.width(v) / 2; + if (right > maxX) maxX = right; + + edge e; + forall_adj_edges(e, v) { + node w = e->target(); + if (w != v) S.push(w); + } + } + } + + void TreeLayout::findMaxY(GraphAttributes& AG, node root, double& maxY) + { + Stack S; + S.push(root); + while (!S.empty()) + { + node v = S.pop(); + + double right = AG.y(v) + AG.height(v) / 2; + if (right > maxY) maxY = right; + + edge e; + forall_adj_edges(e, v) { + node w = e->target(); + if (w != v) S.push(w); + } + } + } + + + //node TreeLayout::initializeTreeStructure(const Graph &tree) + void TreeLayout::initializeTreeStructure(const Graph& tree, List& roots) + { + node v; + + // initialize node arrays + m_number.init(tree, 0); + m_parent.init(tree, 0); + m_leftSibling.init(tree, 0); + m_firstChild.init(tree, 0); + m_lastChild.init(tree, 0); + m_thread.init(tree, 0); + m_ancestor.init(tree, 0); + m_preliminary.init(tree, 0); + m_modifier.init(tree, 0); + m_change.init(tree, 0); + m_shift.init(tree, 0); + + // compute the tree structure + + // find the roots + //node root = 0; + forall_nodes(v, tree) { + if (v->indeg() == 0) + roots.pushBack(v); + } + + int childCounter; + forall_nodes(v, tree) { + + // determine + // - the parent node of v + // - the leftmost and rightmost child of v + // - the numbers of the children of v + // - the left siblings of the children of v + // and initialize the actual ancestor of v + + m_ancestor[v] = v; + if (isLeaf(v)) { + if (v->indeg() == 0) { // is v a root + m_parent[v] = 0; + m_leftSibling[v] = 0; + } + else { + m_firstChild[v] = m_lastChild[v] = 0; + m_parent[v] = v->firstAdj()->theEdge()->source(); + } + } + else { + + // traverse the adjacency list of v + adjEntry first; // first leaving edge + adjEntry stop; // successor of last leaving edge + first = v->firstAdj(); + if (v->indeg() == 0) { // is v a root + stop = first; + m_parent[v] = 0; + m_leftSibling[v] = 0; + } + else { + + // search for first leaving edge + while (first->theEdge()->source() == v) + first = first->cyclicSucc(); + m_parent[v] = first->theEdge()->source(); + stop = first; + first = first->cyclicSucc(); + } + + // traverse the children of v + m_firstChild[v] = first->theEdge()->target(); + m_number[m_firstChild[v]] = childCounter = 0; + m_leftSibling[m_firstChild[v]] = 0; + adjEntry previous = first; + while (first->cyclicSucc() != stop) { + first = first->cyclicSucc(); + m_number[first->theEdge()->target()] = ++childCounter; + m_leftSibling[first->theEdge()->target()] + = previous->theEdge()->target(); + previous = first; + } + m_lastChild[v] = first->theEdge()->target(); + } + } + } + + + void TreeLayout::deleteTreeStructure() + { + m_number.init(); + m_parent.init(); + m_leftSibling.init(); + m_firstChild.init(); + m_lastChild.init(); + m_thread.init(); + m_ancestor.init(); + m_preliminary.init(); + m_modifier.init(); + m_change.init(); + m_shift.init(); + } + + + int TreeLayout::isLeaf(node v) const + { + OGDF_ASSERT(v != 0); + + // node v is a leaf if and only if no edge leaves v + return v->outdeg() == 0; + } + + + node TreeLayout::nextOnLeftContour(node v) const + { + OGDF_ASSERT(v != 0); + OGDF_ASSERT(v->graphOf() == m_firstChild.graphOf()); + OGDF_ASSERT(v->graphOf() == m_thread.graphOf()); + + // if v has children, the successor of v on the left contour + // is its leftmost child, + // otherwise, the successor is the thread of v (may be 0) + if (m_firstChild[v] != 0) + return m_firstChild[v]; + else + return m_thread[v]; + } + + + node TreeLayout::nextOnRightContour(node v) const + { + OGDF_ASSERT(v != 0); + OGDF_ASSERT(v->graphOf() == m_lastChild.graphOf()); + OGDF_ASSERT(v->graphOf() == m_thread.graphOf()); + + // if v has children, the successor of v on the right contour + // is its rightmost child, + // otherwise, the successor is the thread of v (may be 0) + if (m_lastChild[v] != 0) + return m_lastChild[v]; + else + return m_thread[v]; + } + + + void TreeLayout::firstWalk(node subtree, const GraphAttributes& AG, bool upDown) + { + OGDF_ASSERT(subtree != 0); + OGDF_ASSERT(subtree->graphOf() == m_leftSibling.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_preliminary.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_firstChild.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_lastChild.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_modifier.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_change.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_shift.graphOf()); + + // compute a preliminary x-coordinate for subtree + if (isLeaf(subtree)) { + + // place subtree close to the left sibling + node leftSibling = m_leftSibling[subtree]; + if (leftSibling != 0) { + if (upDown) { + m_preliminary[subtree] = m_preliminary[leftSibling] + + (AG.width(subtree) + AG.width(leftSibling)) / 2 + + m_siblingDistance; + } + else { + m_preliminary[subtree] = m_preliminary[leftSibling] + + (AG.height(subtree) + AG.height(leftSibling)) / 2 + + m_siblingDistance; + } + } + else m_preliminary[subtree] = 0; + } + else { + node defaultAncestor = m_firstChild[subtree]; + + // collect the children of subtree + List children; + node v = m_lastChild[subtree]; + do { + children.pushFront(v); + v = m_leftSibling[v]; + } while (v != 0); + + ListIterator it; + + // apply firstwalk and apportion to the children + for (it = children.begin(); it.valid(); it = it.succ()) { + firstWalk(*it, AG, upDown); + apportion(*it, defaultAncestor, AG, upDown); + } + + // shift the small subtrees + double shift = 0; + double change = 0; + children.reverse(); + for (it = children.begin(); it.valid(); it = it.succ()) { + m_preliminary[*it] += shift; + m_modifier[*it] += shift; + change += m_change[*it]; + shift += m_shift[*it] + change; + } + + // place the parent node + double midpoint = (m_preliminary[children.front()] + m_preliminary[children.back()]) / 2; + node leftSibling = m_leftSibling[subtree]; + if (leftSibling != 0) { + if (upDown) { + m_preliminary[subtree] = m_preliminary[leftSibling] + + (AG.width(subtree) + AG.width(leftSibling)) / 2 + + m_siblingDistance; + } + else { + m_preliminary[subtree] = m_preliminary[leftSibling] + + (AG.height(subtree) + AG.height(leftSibling)) / 2 + + m_siblingDistance; + } + m_modifier[subtree] = + m_preliminary[subtree] - midpoint; + } + else m_preliminary[subtree] = midpoint; + } + } + + void TreeLayout::apportion( + node subtree, + node& defaultAncestor, + const GraphAttributes& AG, + bool upDown) + { + OGDF_ASSERT(subtree != 0); + OGDF_ASSERT(subtree->graphOf() == defaultAncestor->graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_leftSibling.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_firstChild.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_modifier.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_ancestor.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_change.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_shift.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_thread.graphOf()); + + if (m_leftSibling[subtree] == 0) return; + + // check distance to the left of the subtree + // and traverse left/right inside/outside contour + + double leftModSumOut = 0; // sum of modifiers on left outside contour + double leftModSumIn = 0; // sum of modifiers on left inside contour + double rightModSumIn = 0; // sum of modifiers on right inside contour + double rightModSumOut = 0; // sum of modifiers on right outside contour + + double moveDistance; + int numberOfSubtrees; + node leftAncestor, rightAncestor; + + // start the traversal at the actual level + node leftContourOut = m_firstChild[m_parent[subtree]]; + node leftContourIn = m_leftSibling[subtree]; + node rightContourIn = subtree; + node rightContourOut = subtree; + bool stop = false; + do { + + // add modifiers + leftModSumOut += m_modifier[leftContourOut]; + leftModSumIn += m_modifier[leftContourIn]; + rightModSumIn += m_modifier[rightContourIn]; + rightModSumOut += m_modifier[rightContourOut]; + + // actualize ancestor for right contour + m_ancestor[rightContourOut] = subtree; + + if (nextOnLeftContour(leftContourOut) != 0 && nextOnRightContour(rightContourOut) != 0) + { + // continue traversal + leftContourOut = nextOnLeftContour(leftContourOut); + leftContourIn = nextOnRightContour(leftContourIn); + rightContourIn = nextOnLeftContour(rightContourIn); + rightContourOut = nextOnRightContour(rightContourOut); + + // check if subtree has to be moved + if (upDown) { + moveDistance = m_preliminary[leftContourIn] + leftModSumIn + + (AG.width(leftContourIn) + AG.width(rightContourIn)) / 2 + + m_subtreeDistance + - m_preliminary[rightContourIn] - rightModSumIn; + } + else { + moveDistance = m_preliminary[leftContourIn] + leftModSumIn + + (AG.height(leftContourIn) + AG.height(rightContourIn)) / 2 + + m_subtreeDistance + - m_preliminary[rightContourIn] - rightModSumIn; + } + if (moveDistance > 0) { + + // compute highest different ancestors of leftContourIn + // and rightContourIn + if (m_parent[m_ancestor[leftContourIn]] == m_parent[subtree]) + leftAncestor = m_ancestor[leftContourIn]; + else leftAncestor = defaultAncestor; + rightAncestor = subtree; + + // compute the number of small subtrees in between (plus 1) + numberOfSubtrees = + m_number[rightAncestor] - m_number[leftAncestor]; + + // compute the shifts and changes of shift + m_change[rightAncestor] -= moveDistance / numberOfSubtrees; + m_shift[rightAncestor] += moveDistance; + m_change[leftAncestor] += moveDistance / numberOfSubtrees; + + // move subtree to the right by moveDistance + m_preliminary[rightAncestor] += moveDistance; + m_modifier[rightAncestor] += moveDistance; + rightModSumIn += moveDistance; + rightModSumOut += moveDistance; + } + } + else stop = true; + } while (!stop); + + // adjust threads + if (nextOnRightContour(rightContourOut) == 0 && nextOnRightContour(leftContourIn) != 0) + { + // right subtree smaller than left subforest + m_thread[rightContourOut] = nextOnRightContour(leftContourIn); + m_modifier[rightContourOut] += leftModSumIn - rightModSumOut; + } + + if (nextOnLeftContour(leftContourOut) == 0 && nextOnLeftContour(rightContourIn) != 0) + { + // left subforest smaller than right subtree + m_thread[leftContourOut] = nextOnLeftContour(rightContourIn); + m_modifier[leftContourOut] += rightModSumIn - leftModSumOut; + defaultAncestor = subtree; + } + } + + + void TreeLayout::secondWalkX(node subtree, + double modifierSum, + GraphAttributes& AG) + { + OGDF_ASSERT(subtree != 0); + OGDF_ASSERT(subtree->graphOf() == m_preliminary.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_modifier.graphOf()); + + // compute final x-coordinates for the subtree + // by recursively aggregating modifiers + AG.x(subtree) = m_preliminary[subtree] + modifierSum; + modifierSum += m_modifier[subtree]; + edge e; + forall_adj_edges(e, subtree) if (e->target() != subtree) + secondWalkX(e->target(), modifierSum, AG); + } + + void TreeLayout::secondWalkY(node subtree, + double modifierSum, + GraphAttributes& AG) + { + OGDF_ASSERT(subtree != 0); + OGDF_ASSERT(subtree->graphOf() == m_preliminary.graphOf()); + OGDF_ASSERT(subtree->graphOf() == m_modifier.graphOf()); + + // compute final y-coordinates for the subtree + // by recursively aggregating modifiers + AG.y(subtree) = m_preliminary[subtree] + modifierSum; + modifierSum += m_modifier[subtree]; + edge e; + forall_adj_edges(e, subtree) if (e->target() != subtree) + secondWalkY(e->target(), modifierSum, AG); + } + + + void TreeLayout::computeYCoordinatesAndEdgeShapes(node root, GraphAttributes& AG) + { + OGDF_ASSERT(root != 0); + + // compute y-coordinates and edge shapes + node v, w; + edge e; + List oldLevel; // the nodes of the old level + List newLevel; // the nodes of the new level + ListIterator it; + double yCoordinate; // the y-coordinate for the new level + double edgeCoordinate; // the y-coordinate for edge bends + double oldHeight; // the maximal node height on the old level + double newHeight; // the maximal node height on the new level + + // traverse the tree level by level + newLevel.pushBack(root); + AG.y(root) = yCoordinate = 0; + newHeight = AG.height(root); + while (!newLevel.empty()) { + oldHeight = newHeight; + newHeight = 0; + oldLevel.conc(newLevel); + while (!oldLevel.empty()) { + v = oldLevel.popFrontRet(); + forall_adj_edges(e, v) if (e->target() != v) { + w = e->target(); + newLevel.pushBack(w); + + // compute the shape of edge e + DPolyline& edgeBends = AG.bends(e); + edgeBends.clear(); + if (m_orthogonalLayout) { + edgeCoordinate = + yCoordinate + (oldHeight + m_levelDistance) / 2; + edgeBends.pushBack(DPoint(AG.x(v), edgeCoordinate)); + edgeBends.pushBack(DPoint(AG.x(w), edgeCoordinate)); + } + + // compute the maximal node height on the new level + if (AG.height(e->target()) > newHeight) + newHeight = AG.height(e->target()); + } + } + + // assign y-coordinate to the nodes of the new level + yCoordinate += (oldHeight + newHeight) / 2 + m_levelDistance; + for (it = newLevel.begin(); it.valid(); it = it.succ()) + AG.y(*it) = yCoordinate; + } + } + + void TreeLayout::computeXCoordinatesAndEdgeShapes(node root, GraphAttributes& AG) + { + OGDF_ASSERT(root != 0); + + // compute y-coordinates and edge shapes + node v, w; + edge e; + List oldLevel; // the nodes of the old level + List newLevel; // the nodes of the new level + ListIterator it; + double xCoordinate; // the x-coordinate for the new level + double edgeCoordinate; // the x-coordinate for edge bends + double oldWidth; // the maximal node width on the old level + double newWidth; // the maximal node width on the new level + + // traverse the tree level by level + newLevel.pushBack(root); + AG.x(root) = xCoordinate = 0; + newWidth = AG.width(root); + while (!newLevel.empty()) { + oldWidth = newWidth; + newWidth = 0; + oldLevel.conc(newLevel); + while (!oldLevel.empty()) { + v = oldLevel.popFrontRet(); + forall_adj_edges(e, v) if (e->target() != v) { + w = e->target(); + newLevel.pushBack(w); + + // compute the shape of edge e + DPolyline& edgeBends = AG.bends(e); + edgeBends.clear(); + if (m_orthogonalLayout) { + edgeCoordinate = + xCoordinate + (oldWidth + m_levelDistance) / 2; + edgeBends.pushBack(DPoint(edgeCoordinate, AG.y(v))); + edgeBends.pushBack(DPoint(edgeCoordinate, AG.y(w))); + } + + // compute the maximal node width on the new level + if (AG.width(e->target()) > newWidth) + newWidth = AG.width(e->target()); + } + } + + // assign x-coordinate to the nodes of the new level + xCoordinate += (oldWidth + newWidth) / 2 + m_levelDistance; + for (it = newLevel.begin(); it.valid(); it = it.succ()) + AG.x(*it) = xCoordinate; + } + } + + +} // end namespace ogdf diff --git a/ogdf/tree/TreeLayout.h b/ogdf/tree/TreeLayout.h new file mode 100644 index 00000000..d26945f4 --- /dev/null +++ b/ogdf/tree/TreeLayout.h @@ -0,0 +1,303 @@ +/* + * $Revision: 2583 $ + * + * last checkin: + * $Author: gutwenger $ + * $Date: 2012-07-12 01:02:21 +0200 (Do, 12. Jul 2012) $ + ***************************************************************/ + + /** \file + * \brief Declaration of linear time layout algorithm for trees + * (TreeLayout) based on Walker's algorithm. + * + * \author Christoph Buchheim + * + * \par License: + * This file is part of the Open Graph Drawing Framework (OGDF). + * + * \par + * Copyright (C)
+ * See README.txt in the root directory of the OGDF installation for details. + * + * \par + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 or 3 as published by the Free Software Foundation; + * see the file LICENSE.txt included in the packaging of this file + * for details. + * + * \par + * This program 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. + * + * \par + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * \see http://www.gnu.org/copyleft/gpl.html + ***************************************************************/ + + +#ifdef _MSC_VER +#pragma once +#endif + +#ifndef OGDF_TREE_LAYOUT_H +#define OGDF_TREE_LAYOUT_H + +#include "../ogdf/module/LayoutModule.h" +#include "../ogdf/basic/SList.h" + +namespace ogdf { + + /** + * \brief The tree layout algorithm. + * + * The class TreeLayout represents the improved version of the tree layout + * algorithm by Walker presented in: + * + * Christoph Buchheim, Michael Jünger, Sebastian Leipert: Drawing + * rooted trees in linear time. Software: Practice and Experience 36(6), + * pp. 651-665, 2006. + * + * The algorithm also allows to lay out a forest, i.e., a collection of trees. + * + *

Optional parameters

+ * Tree layout provides various optional parameters. + * + * + * + * + * + * + * + * + * + * + * + *
OptionTypeDefaultDescription + *
siblingDistancedouble20.0 + * The horizontal spacing between adjacent sibling nodes. + *
subtreeDistancedouble20.0 + * The horizontal spacing between adjacent subtrees. + *
levelDistancedouble50.0 + * The vertical spacing between adjacent levels. + *
treeDistancedouble50.0 + * The horizontal spacing between adjacent trees in a forest. + *
orthogonalLayoutboolfalse + * Determines whether edges are routed in an orthogonal + * or straight-line fashion. + *
orientation #Orientation #topToBottom + * Determines if the tree is laid out in a top-to-bottom, + * bottom-to-top, left-to-right, or right-to-left fashion. + *
selectRoot #RootSelectionType #rootIsSource + * Determines how to select the root of the tree(s). Possible + * selection strategies are to take a (unique) source or sink in + * the graph, or to use the coordinates and to select the topmost + * node for top-to-bottom orientation, etc. + *
+ * + * The spacing between nodes is determined by the siblingDistance, + * subtreeDistance, levelDistance, and treeDistance. + * The layout style is determined by orthogonalLayout and + * orientation; the root of the tree is selected according to + * th eselection strategy given by selectRoot. + */ + class OGDF_EXPORT TreeLayout : public LayoutModule { + public: + //! Determines how to select the root of the tree. + enum RootSelectionType { + rootIsSource, //!< Select a source in the graph. + rootIsSink, //!< Select a sink in the graph. + rootByCoord //!< Use the coordinates, e.g., select the topmost node if orientation is topToBottom. + }; + + private: + double m_siblingDistance; //!< The minimal distance between siblings. + double m_subtreeDistance; //!< The minimal distance between subtrees. + double m_levelDistance; //!< The minimal distance between levels. + double m_treeDistance; //!< The minimal distance between trees. + + bool m_orthogonalLayout; //!< Option for orthogonal style (yes/no). + Orientation m_orientation; //!< Option for orientation of tree layout. + RootSelectionType m_selectRoot; //!< Option for how to determine the root. + + NodeArray m_number; //!< Consecutive numbers for children. + + NodeArray m_parent; //!< Parent node, 0 if root. + NodeArray m_leftSibling; //!< Left sibling, 0 if none. + NodeArray m_firstChild; //!< Leftmost child, 0 if leaf. + NodeArray m_lastChild; //!< Rightmost child, 0 if leaf. + NodeArray m_thread; //!< Thread, 0 if none. + NodeArray m_ancestor; //!< Actual highest ancestor. + + NodeArray m_preliminary; //!< Preliminary x-coordinates. + NodeArray m_modifier; //!< Modifier of x-coordinates. + NodeArray m_change; //!< Change of shift applied to subtrees. + NodeArray m_shift; //!< Shift applied to subtrees. + + SListPure m_reversedEdges; //!< List of temporarily removed edges. + Graph* m_pGraph; //!< The input graph. + + public: + //! Creates an instance of tree layout and sets options to default values. + TreeLayout(); + + //! Copy constructor. + TreeLayout(const TreeLayout& tl); + + // destructor + ~TreeLayout(); + + + /** + * @name Algorithm call + * @{ + */ + + /** + * \brief Calls tree layout for graph attributes \a GA. + * + * \pre The graph is a tree. + * + * The order of children is given by the adjacency lists; + * the successor of the unique in-edge of a non-root node + * leads to its leftmost child; the leftmost child of the root + * is given by its first adjacency entry. + * @param GA is the input graph and will also be assigned the layout information. + */ + void call(GraphAttributes& GA); + void callMultiLine(GraphAttributes& GA, int numOfRows = 0); + + /** + * \brief Calls tree layout for graph attributes \a GA. + * + * \pre The graph is a tree. + * + * Sorts the adjacency entries according to the positions of adjacent + * vertices in \a GA. + * @param GA is the input graph and will also be assigned the layout information. + * @param G is the graph associated with \a GA. + */ + void callSortByPositions(GraphAttributes& GA, Graph& G); + + + /** @} + * @name Optional parameters + * @{ + */ + + //! Returns the the minimal required horizontal distance between siblings. + double siblingDistance() const { return m_siblingDistance; } + + //! Sets the the minimal required horizontal distance between siblings to \a x. + void siblingDistance(double x) { m_siblingDistance = x; } + + //! Returns the minimal required horizontal distance between subtrees. + double subtreeDistance() const { return m_subtreeDistance; } + + //! Sets the minimal required horizontal distance between subtrees to \a x. + void subtreeDistance(double x) { m_subtreeDistance = x; } + + //! Returns the minimal required vertical distance between levels. + double levelDistance() const { return m_levelDistance; } + + //! Sets the minimal required vertical distance between levels to \a x. + void levelDistance(double x) { m_levelDistance = x; } + + //! Returns the minimal required horizontal distance between trees in the forest. + double treeDistance() const { return m_treeDistance; } + + //! Sets the minimal required horizontal distance between trees in the forest to \a x. + void treeDistance(double x) { m_treeDistance = x; } + + //! Returns whether orthogonal edge routing style is used. + bool orthogonalLayout() const { return m_orthogonalLayout; } + + //! Sets the option for orthogonal edge routing style to \a b. + void orthogonalLayout(bool b) { m_orthogonalLayout = b; } + + //! Returns the option that determines the orientation of the layout. + Orientation orientation() const { return m_orientation; } + + //! Sets the option that determines the orientation of the layout to \a orientation. + void orientation(Orientation orientation) { m_orientation = orientation; } + + //! Returns the option that determines how the root is selected. + RootSelectionType rootSelection() const { return m_selectRoot; } + + //! Sets the option that determines how the root is selected to \a rootSelection. + void rootSelection(RootSelectionType rootSelection) { m_selectRoot = rootSelection; } + + + /** @} + * @name Operators + * @{ + */ + + //! Assignment operator. + TreeLayout& operator=(const TreeLayout& tl); + + //! @} + + private: + class AdjComparer; + + void adjustEdgeDirections(Graph& G, node v, node parent); + void setRoot(GraphAttributes& AG, Graph& tree); + void undoReverseEdges(GraphAttributes& AG); + + // initialize all node arrays and + // compute the tree structure from the adjacency lists + // + // returns the root node + void initializeTreeStructure(const Graph& tree, List& roots); + + // delete all node arrays + void deleteTreeStructure(); + + // returns whether node v is a leaf + int isLeaf(node v) const; + + // returns the successor of node v on the left/right contour + // returns 0 if there is none + node nextOnLeftContour(node v) const; + node nextOnRightContour(node v) const; + + // recursive bottom up traversal of the tree for computing + // preliminary x-coordinates + void firstWalk(node subtree, const GraphAttributes& AG, bool upDown); + + // space out the small subtrees on the left hand side of subtree + // defaultAncestor is used for all nodes with obsolete m_ancestor + void apportion( + node subtree, + node& defaultAncestor, + const GraphAttributes& AG, + bool upDown); + + // recursive top down traversal of the tree for computing final + // x-coordinates + void secondWalkX(node subtree, double modifierSum, GraphAttributes& AG); + void secondWalkY(node subtree, double modifierSum, GraphAttributes& AG); + + // compute y-coordinates and edge shapes + void computeYCoordinatesAndEdgeShapes(node root, GraphAttributes& AG); + void computeXCoordinatesAndEdgeShapes(node root, GraphAttributes& AG); + + void findMinX(GraphAttributes& AG, node root, double& minX); + void findMinY(GraphAttributes& AG, node root, double& minY); + void findMaxX(GraphAttributes& AG, node root, double& maxX); + void findMaxY(GraphAttributes& AG, node root, double& maxY); + void shiftTreeX(GraphAttributes& AG, node root, double shift); + void shiftTreeY(GraphAttributes& AG, node root, double shift); + }; + +} // end namespace ogdf + +#endif + diff --git a/painting/CommonGraphicsItemNode.cpp b/painting/CommonGraphicsItemNode.cpp new file mode 100644 index 00000000..935b1a33 --- /dev/null +++ b/painting/CommonGraphicsItemNode.cpp @@ -0,0 +1,457 @@ +#include "CommonGraphicsItemNode.h" +#include "../program/settings.h" +#include "../program/globals.h" +#include +#include "../graph/ogdfnode.h" +#include "../program/settings.h" +#include +#include +#include +#include "../ogdf/basic/GraphAttributes.h" +#include +#include +#include +#include +#include +#include "../ui/mygraphicsscene.h" +#include +#include "../ui/mygraphicsview.h" +#include +#include "../blast/blasthit.h" +#include "../blast/blastquery.h" +#include "../blast/blasthitpart.h" +#include +#include +#include "../program/memory.h" + +CommonGraphicsItemNode::CommonGraphicsItemNode(MyGraphicsView* graphicsView, QGraphicsItem* parent) : + m_graphicsView(graphicsView), QGraphicsItem(parent) { } + +//This function returns the nodes' visible centres. If the entire node is visible, +//then there is just one visible centre. If none of the node is visible, then +//there are no visible centres. If multiple parts of the node are visible, then there +//are multiple visible centres. +std::vector CommonGraphicsItemNode::getCentres() const +{ + std::vector centres; + std::vector currentRun; + + QPointF lastP; + bool lastPointVisible = false; + + for (size_t i = 0; i < m_linePoints.size(); ++i) + { + QPointF p = m_linePoints[i]; + bool pVisible = m_graphicsView->isPointVisible(p); + + //If this point is visible, but the last wasn't, a new run is started. + if (pVisible && !lastPointVisible) + { + //If this is not the first point, then we need to find the intermediate + //point that lies on the visible boundary and start the path with that. + if (i > 0) + currentRun.push_back(m_graphicsView->findIntersectionWithViewportBoundary(QLineF(p, lastP))); + currentRun.push_back(p); + } + + //If th last point is visible and this one is too, add it to the current run. + else if (pVisible && lastPointVisible) + currentRun.push_back(p); + + //If the last point is visible and this one isn't, then a run has ended. + else if (!pVisible && lastPointVisible) + { + //We need to find the intermediate point that is on the visible boundary. + currentRun.push_back(m_graphicsView->findIntersectionWithViewportBoundary(QLineF(p, lastP))); + + centres.push_back(getCentre(currentRun)); + currentRun.clear(); + } + + //If neither this point nor the last were visible, we still need to check whether + //the line segment between them is. If so, then then this may be a case where + //we are really zoomed in (and so line segments are large compared to the scene rect). + else if (i > 0 && !pVisible && !lastPointVisible) + { + bool success; + QLineF v = m_graphicsView->findVisiblePartOfLine(QLineF(lastP, p), &success); + if (success) + { + QPointF vCentre = QPointF((v.p1().x() + v.p2().x()) / 2.0, (v.p1().y() + v.p2().y()) / 2.0); + centres.push_back(vCentre); + } + } + + lastPointVisible = pVisible; + lastP = p; + } + + //If there is a current run, add its centre + if (currentRun.size() > 0) + centres.push_back(getCentre(currentRun)); + + return centres; +} + +//This function finds the centre point on the path defined by linePoints. +QPointF CommonGraphicsItemNode::getCentre(std::vector linePoints) const +{ + if (linePoints.size() == 0) + return QPointF(); + if (linePoints.size() == 1) + return linePoints[0]; + + double pathLength = 0.0; + for (size_t i = 0; i < linePoints.size() - 1; ++i) + pathLength += distance(linePoints[i], linePoints[i + 1]); + + double endToCentre = pathLength / 2.0; + + double lengthSoFar = 0.0; + for (size_t i = 0; i < linePoints.size() - 1; ++i) + { + QPointF a = linePoints[i]; + QPointF b = linePoints[i + 1]; + double segmentLength = distance(a, b); + + //If this segment will push the distance over halfway, then it + //contains the centre point. + if (lengthSoFar + segmentLength >= endToCentre) + { + double additionalLengthNeeded = endToCentre - lengthSoFar; + double fractionOfCurrentSegment = additionalLengthNeeded / segmentLength; + return (b - a) * fractionOfCurrentSegment + a; + } + + lengthSoFar += segmentLength; + } + + //Code should never get here. + return QPointF(); +} + +void CommonGraphicsItemNode::updateGrabIndex(QGraphicsSceneMouseEvent* event) { + m_grabIndex = 0; + QPointF grabPoint = event->pos(); + + double closestPointDistance = distance(grabPoint, m_linePoints[0]); + for (size_t i = 1; i < m_linePoints.size(); ++i) + { + double pointDistance = distance(grabPoint, m_linePoints[i]); + if (pointDistance < closestPointDistance) + { + closestPointDistance = pointDistance; + m_grabIndex = i; + } + } +} + +void CommonGraphicsItemNode::shiftPoints(QPointF difference) +{ + prepareGeometryChange(); + + if (g_settings->nodeDragging == NO_DRAGGING) + return; + + else if (isSelected()) //Move all pieces for selected nodes + { + for (size_t i = 0; i < m_linePoints.size(); ++i) + m_linePoints[i] += difference; + } + + else if (g_settings->nodeDragging == ONE_PIECE) + m_linePoints[m_grabIndex] += difference; + + else if (g_settings->nodeDragging == NEARBY_PIECES) + { + for (size_t i = 0; i < m_linePoints.size(); ++i) + { + int indexDistance = abs(int(i) - int(m_grabIndex)); + double dragStrength = pow(2.0, -1.0 * pow(double(indexDistance), 1.8) / g_settings->dragStrength); //constants chosen for dropoff of drag strength + m_linePoints[i] += difference * dragStrength; + } + } +} + +void CommonGraphicsItemNode::remakePath() +{ + QPainterPath path; + + path.moveTo(m_linePoints[0]); + if (m_linePoints.size() <= 2) { + for (size_t i = 1; i < m_linePoints.size(); ++i) + path.lineTo(m_linePoints[i]); + } + else { + int middleInd = m_linePoints.size() / 2; + for (size_t i = 1; i < middleInd - 1; ++i) + path.lineTo(m_linePoints[i]); + path.quadTo(m_linePoints[middleInd], m_linePoints[middleInd + 1]); + + for (size_t i = middleInd + 1; i < m_linePoints.size(); ++i) + path.lineTo(m_linePoints[i]); + } + + m_path = path; +} + +QPainterPath CommonGraphicsItemNode::makePartialPath(double startFraction, double endFraction) +{ + if (endFraction < startFraction) + std::swap(startFraction, endFraction); + + double totalLength = getNodePathLength(); + + QPainterPath path; + bool pathStarted = false; + double lengthSoFar = 0.0; + for (size_t i = 0; i < m_linePoints.size() - 1; ++i) + { + QPointF point1 = m_linePoints[i]; + QPointF point2 = m_linePoints[i + 1]; + QLineF line(point1, point2); + + double point1Fraction = lengthSoFar / totalLength; + lengthSoFar += line.length(); + double point2Fraction = lengthSoFar / totalLength; + + //If the path hasn't yet begun and this segment is before + //the starting fraction, do nothing. + if (!pathStarted && point2Fraction < startFraction) + continue; + + //If the path hasn't yet begun but this segment covers the starting + //fraction, start the path now. + if (!pathStarted && point2Fraction >= startFraction) + { + pathStarted = true; + path.moveTo(findIntermediatePoint(point1, point2, point1Fraction, point2Fraction, startFraction)); + } + + //If the path is in progress and this segment hasn't yet reached the end, + //just continue the path. + if (pathStarted && point2Fraction < endFraction) + path.lineTo(point2); + + //If the path is in progress and this segment passes the end, finish the line. + if (pathStarted && point2Fraction >= endFraction) + { + path.lineTo(findIntermediatePoint(point1, point2, point1Fraction, point2Fraction, endFraction)); + return path; + } + } + + return path; +} + +double CommonGraphicsItemNode::getNodePathLength() +{ + double totalLength = 0.0; + for (size_t i = 0; i < m_linePoints.size() - 1; ++i) + { + QLineF line(m_linePoints[i], m_linePoints[i + 1]); + totalLength += line.length(); + } + return totalLength; +} + +//This function will find the point that is a certain fraction of the way along the node's path. +QPointF CommonGraphicsItemNode::findLocationOnPath(double fraction) +{ + double totalLength = getNodePathLength(); + + double lengthSoFar = 0.0; + for (size_t i = 0; i < m_linePoints.size() - 1; ++i) + { + QPointF point1 = m_linePoints[i]; + QPointF point2 = m_linePoints[i + 1]; + QLineF line(point1, point2); + + double point1Fraction = lengthSoFar / totalLength; + lengthSoFar += line.length(); + double point2Fraction = lengthSoFar / totalLength; + + //If point2 hasn't yet reached the target, do nothing. + if (point2Fraction < fraction) + continue; + + //If the path hasn't yet begun but this segment covers the starting + //fraction, start the path now. + if (point2Fraction >= fraction) + return findIntermediatePoint(point1, point2, point1Fraction, point2Fraction, fraction); + } + + //The code shouldn't get here, as the target point should have been found in the above loop. + return QPointF(); +} + +QPointF CommonGraphicsItemNode::findIntermediatePoint(QPointF p1, QPointF p2, double p1Value, double p2Value, double targetValue) +{ + QPointF difference = p2 - p1; + double fraction = (targetValue - p1Value) / (p2Value - p1Value); + return difference * fraction + p1; +} + +double CommonGraphicsItemNode::distance(QPointF p1, QPointF p2) const +{ + double xDiff = p1.x() - p2.x(); + double yDiff = p1.y() - p2.y(); + return sqrt(xDiff * xDiff + yDiff * yDiff); +} + +//This function shifts all the node's points to the left (relative to its +//direction). This is used in double mode to prevent nodes from displaying +//directly on top of their complement nodes. +void CommonGraphicsItemNode::shiftPointsLeft() +{ + shiftPointSideways(true); +} + +void CommonGraphicsItemNode::shiftPointsRight() +{ + shiftPointSideways(false); +} + +void CommonGraphicsItemNode::shiftPointSideways(bool left) +{ + prepareGeometryChange(); + + //The collection of line points should be at least + //two large. But just to be safe, quit now if it + //is not. + size_t linePointsSize = m_linePoints.size(); + if (linePointsSize < 2) + return; + + //Shift by a quarter of the segment length. This should make + //nodes one half segment length separated from their complements. + double shiftDistance = g_settings->doubleModeNodeSeparation; + + for (size_t i = 0; i < linePointsSize; ++i) + { + QPointF point = m_linePoints[i]; + QLineF nodeDirection; + + //If the point is on the end, then determine the node direction + //using this point and its adjacent point. + if (i == 0) + { + QPointF nextPoint = m_linePoints[i + 1]; + nodeDirection = QLineF(point, nextPoint); + } + else if (i == linePointsSize - 1) + { + QPointF previousPoint = m_linePoints[i - 1]; + nodeDirection = QLineF(previousPoint, point); + } + + // If the point is in the middle, then determine the node direction + //using both adjacent points. + else + { + QPointF previousPoint = m_linePoints[i - 1]; + QPointF nextPoint = m_linePoints[i + 1]; + nodeDirection = QLineF(previousPoint, nextPoint); + } + + QLineF shiftLine = nodeDirection.normalVector().unitVector(); + shiftLine.setLength(shiftDistance); + + QPointF shiftVector; + if (left) + shiftVector = shiftLine.p2() - shiftLine.p1(); + else + shiftVector = shiftLine.p1() - shiftLine.p2(); + QPointF newPoint = point + shiftVector; + m_linePoints[i] = newPoint; + } + + remakePath(); +} + +void CommonGraphicsItemNode::drawNodeText(QPainter* painter, QStringList nodeText) { + QPainterPath textPath; + + QFontMetrics metrics(g_settings->labelFont); + double fontHeight = metrics.ascent(); + + for (int i = 0; i < nodeText.size(); ++i) + { + QString text = nodeText.at(i); + int stepsUntilLast = nodeText.size() - 1 - i; + double shiftLeft = -metrics.width(text) / 2.0; + textPath.addText(shiftLeft, -stepsUntilLast * fontHeight, g_settings->labelFont, text); + } + + std::vector centres; + if (g_settings->positionTextNodeCentre) + centres.push_back(getCentre(m_linePoints)); + else + centres = getCentres(); + + for (size_t i = 0; i < centres.size(); ++i) + drawTextPathAtLocation(painter, textPath, centres[i]); +} + +void CommonGraphicsItemNode::drawTextPathAtLocation(QPainter* painter, QPainterPath textPath, QPointF centre) +{ + QRectF textBoundingRect = textPath.boundingRect(); + double textHeight = textBoundingRect.height(); + QPointF offset(0.0, textHeight / 2.0); + + double zoom = g_absoluteZoom; + if (zoom == 0.0) + zoom = 1.0; + + double zoomAdjustment = 1.0 / (1.0 + ((zoom - 1.0) * g_settings->textZoomScaleFactor)); + double inverseZoomAdjustment = 1.0 / zoomAdjustment; + + painter->translate(centre); + painter->rotate(-m_graphicsView->getRotation()); + painter->scale(zoomAdjustment, zoomAdjustment); + painter->translate(offset); + + if (g_settings->textOutline) + { + painter->setPen(QPen(g_settings->textOutlineColour, + g_settings->textOutlineThickness * 2.0, + Qt::SolidLine, + Qt::SquareCap, + Qt::RoundJoin)); + painter->drawPath(textPath); + } + + painter->fillPath(textPath, QBrush(g_settings->textColour)); + painter->translate(-offset); + painter->scale(inverseZoomAdjustment, inverseZoomAdjustment); + painter->rotate(m_graphicsView->getRotation()); + painter->translate(-centre); +} + +void CommonGraphicsItemNode::roundPoints(QPointF centralPoint, double alpha) { + double sinA = sin(alpha); + double cosA = cos(alpha); + double xA = centralPoint.x(); + double yA = centralPoint.y(); + for (size_t i = 0; i < m_linePoints.size(); ++i) { + QPointF point = m_linePoints[i]; + double x = -sinA * (point.y() - yA) + cosA * (point.x() - xA) + xA; + double y = cosA * (point.y() - yA) + sinA * (point.x() - xA) + yA; + point.setX(x); + point.setY(y); + m_linePoints[i] = point; + } +} + +double CommonGraphicsItemNode::angleBetweenTwoLines(QPointF line1Start, QPointF line1End, QPointF line2Start, QPointF line2End) +{ + double a = line1End.x() - line1Start.x(); + double b = line1End.y() - line1Start.y(); + double c = line2End.x() - line2Start.x(); + double d = line2End.y() - line2Start.y(); + + double atanA = atan2(a, b); + double atanB = atan2(c, d); + + return atanA - atanB; +} \ No newline at end of file diff --git a/painting/CommonGraphicsItemNode.h b/painting/CommonGraphicsItemNode.h new file mode 100644 index 00000000..d9458d36 --- /dev/null +++ b/painting/CommonGraphicsItemNode.h @@ -0,0 +1,87 @@ +#ifndef COMMONGRAPHICSITEMNODE_H +#define COMMONGRAPHICSITEMNODE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../ui/mygraphicsview.h" + +class Path; + +class CommonGraphicsItemNode : public QGraphicsItem +{ +public: + + CommonGraphicsItemNode(MyGraphicsView* graphicsView, QGraphicsItem* parent = 0); + + double m_width; + bool m_hasArrow; + std::vector m_linePoints; + size_t m_grabIndex; + QColor m_colour; + QPainterPath m_path; + MyGraphicsView* m_graphicsView; + + void shiftPoints(QPointF difference); + void remakePath(); + void setWidth(double width) { m_width = width; }; + void updateGrabIndex(QGraphicsSceneMouseEvent* event); + double distance(QPointF p1, QPointF p2) const; + + QPointF getFirst() const { return m_linePoints[0]; } + QPointF getSecond() const { + if (m_linePoints.size() > 1) return m_linePoints[1]; + else return m_linePoints[0]; + } + QPointF getLast() const { return m_linePoints[m_linePoints.size() - 1]; } + QPointF getSecondLast() const + { + if (m_linePoints.size() > 1) return m_linePoints[m_linePoints.size() - 2]; + else return m_linePoints[0]; + } + bool isBig() const { return m_linePoints.size() >= 3; } + bool isOne() const { return m_linePoints.size() == 1; } + QPointF getMiddle() const { return m_linePoints[m_linePoints.size() / 2]; } + QPointF getBeforeMiddle() const + { + if (m_linePoints.size() >= 3) + return m_linePoints[(m_linePoints.size() / 2) - 1]; + else + return m_linePoints[0]; + } + QPointF getAfterMiddle() const + { + if (m_linePoints.size() >= 3) + return m_linePoints[(m_linePoints.size() / 2) + 1]; + else + return m_linePoints[m_linePoints.size() - 1]; + } + std::vector getCentres() const; + QPointF getCentre(std::vector linePoints) const; + + QPainterPath makePartialPath(double startFraction, double endFraction); + double getNodePathLength(); + QPointF findLocationOnPath(double fraction); + QPointF findIntermediatePoint(QPointF p1, QPointF p2, double p1Value, + double p2Value, double targetValue); + void shiftPointsLeft(); + void shiftPointsRight(); + void drawNodeText(QPainter* painter, QStringList nodeText); + void drawTextPathAtLocation(QPainter* painter, QPainterPath textPath, QPointF centre); + void roundPoints(QPointF centralPoint, double alpha); + double angleBetweenTwoLines(QPointF line1Start, QPointF line1End, QPointF line2Start, QPointF line2End); + +private: + void shiftPointSideways(bool left); +}; + +#endif //COMMONGRAPHICSITEMNODE_H + diff --git a/painting/NodeMoving.cpp b/painting/NodeMoving.cpp new file mode 100644 index 00000000..001180d4 --- /dev/null +++ b/painting/NodeMoving.cpp @@ -0,0 +1,92 @@ +#include "NodeMoving.h" +#include "../program/settings.h" + +NodeMoving::NodeMoving(){} + +size_t NodeMoving::getGrapIndex(QGraphicsSceneMouseEvent* event, std::vector& linePoints) +{ + size_t grabIndex = 0; + QPointF grabPoint = event->pos(); + + double closestPointDistance = distance(grabPoint, linePoints[0]); + for (size_t i = 1; i < linePoints.size(); ++i) + { + double pointDistance = distance(grabPoint, linePoints[i]); + if (pointDistance < closestPointDistance) + { + closestPointDistance = pointDistance; + grabIndex = i; + } + } + return grabIndex; +} + +void NodeMoving::shiftPoints(QPointF difference, std::vector& linePoints, size_t grabIndex) +{ + prepareGeometryChange(); + + if (g_settings->nodeDragging == NO_DRAGGING) + return; + + else if (isSelected()) //Move all pieces for selected nodes + { + for (size_t i = 0; i < linePoints.size(); ++i) + linePoints[i] += difference; + } + + else if (g_settings->nodeDragging == ONE_PIECE) + linePoints[grabIndex] += difference; + + else if (g_settings->nodeDragging == NEARBY_PIECES) + { + for (size_t i = 0; i < linePoints.size(); ++i) + { + int indexDistance = abs(int(i) - int(grabIndex)); + double dragStrength = pow(2.0, -1.0 * pow(double(indexDistance), 1.8) / g_settings->dragStrength); //constants chosen for dropoff of drag strength + linePoints[i] += difference * dragStrength; + } + } +} + +void NodeMoving::roundPoints(QPointF centralPoint, double alpha, std::vector& linePoints) { + double sinA = sin(alpha); + double cosA = cos(alpha); + double xA = centralPoint.x(); + double yA = centralPoint.y(); + for (size_t i = 0; i < linePoints.size(); ++i) { + QPointF point = linePoints[i]; + double x = -sinA * (point.y() - yA) + cosA * (point.x() - xA) + xA; + double y = cosA * (point.y() - yA) + sinA * (point.x() - xA) + yA; + point.setX(x); + point.setY(y); + } +} + +QPainterPath NodeMoving::remakePath(std::vector & linePoints) +{ + QPainterPath path; + + path.moveTo(linePoints[0]); + if (linePoints.size() <= 2) { + for (size_t i = 1; i < linePoints.size(); ++i) + path.lineTo(linePoints[i]); + } + else { + int middleInd = linePoints.size() / 2; + for (size_t i = 1; i < middleInd - 1; ++i) + path.lineTo(linePoints[i]); + path.quadTo(linePoints[middleInd], linePoints[middleInd + 1]); + + for (size_t i = middleInd + 1; i < linePoints.size(); ++i) + path.lineTo(linePoints[i]); + } + + return path; +} + +double NodeMoving::distance(QPointF p1, QPointF p2) const +{ + double xDiff = p1.x() - p2.x(); + double yDiff = p1.y() - p2.y(); + return sqrt(xDiff * xDiff + yDiff * yDiff); +} diff --git a/painting/NodeMoving.h b/painting/NodeMoving.h new file mode 100644 index 00000000..a3ae196b --- /dev/null +++ b/painting/NodeMoving.h @@ -0,0 +1,22 @@ +#ifndef NODEMOVING_H +#define NODEMOVING_H + +#include +#include +#include + +class NodeMoving : public QGraphicsItem +{ +public: + NodeMoving(); + size_t getGrapIndex(QGraphicsSceneMouseEvent* event, std::vector& linePoints); + void shiftPoints(QPointF difference, std::vector& linePoints, size_t grabIndex); + void roundPoints(QPointF referencePont, double alpha, std::vector& linePoints); + + QPainterPath remakePath(std::vector& linePoints); + +private: + double distance(QPointF p1, QPointF p2) const; +}; +#endif; //NODEMOVING_H + diff --git a/program/TreeLayoutWorker.cpp b/program/TreeLayoutWorker.cpp new file mode 100644 index 00000000..fb66c8e7 --- /dev/null +++ b/program/TreeLayoutWorker.cpp @@ -0,0 +1,44 @@ +//Copyright 2017 Ryan Wick + +//This file is part of Bandage + +//Bandage 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 3 of the License, or +//(at your option) any later version. + +//Bandage 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 Bandage. If not, see . + + +#include "treelayoutworker.h" +#include +#include "ogdf/basic/geometry.h" +#include +#include "../program/settings.h" +#include "../program/globals.h" + +using namespace ogdf; + +TreeLayoutWorker::TreeLayoutWorker(ogdf::TreeLayout* treeLayout, ogdf::GraphAttributes* graphAttributes, + ogdf::EdgeArray* edgeArray) : + + m_treeLayout(treeLayout), m_graphAttributes(graphAttributes), m_edgeArray(edgeArray) +{ +} + +void TreeLayoutWorker::layoutGraph() +{ + m_treeLayout->orientation(ogdf::Orientation::topToBottom); + m_treeLayout->subtreeDistance(50.0); + m_treeLayout->siblingDistance(50.0); + m_treeLayout->callMultiLine(*m_graphAttributes); + + emit finishedLayout(); +} + diff --git a/program/TreeLayoutWorker.h b/program/TreeLayoutWorker.h new file mode 100644 index 00000000..cbc147ab --- /dev/null +++ b/program/TreeLayoutWorker.h @@ -0,0 +1,29 @@ +#ifndef TREELAYOUTWORKER_H +#define TREELAYOUTWORKER_H + +#include +#include "../ogdf/tree/TreeLayout.h" +#include "../ogdf/basic/GraphAttributes.h" + +using namespace ogdf; + +class TreeLayoutWorker : public QObject +{ + Q_OBJECT + +public: + TreeLayoutWorker(TreeLayout* fmmm, ogdf::GraphAttributes* graphAttributes, + ogdf::EdgeArray* edgeArray); + + ogdf::TreeLayout* m_treeLayout; + ogdf::GraphAttributes* m_graphAttributes; + ogdf::EdgeArray* m_edgeArray; + +public slots: + void layoutGraph(); + +signals: + void finishedLayout(); +}; + +#endif // TREELAYOUTWORKER_H diff --git a/program/globals.cpp b/program/globals.cpp index 9f1d9274..d654b91f 100644 --- a/program/globals.cpp +++ b/program/globals.cpp @@ -30,10 +30,12 @@ QSharedPointer g_settings; QSharedPointer g_hicSettings; QSharedPointer g_memory; MyGraphicsView * g_graphicsView; +MyGraphicsView * g_graphicsViewFeaturesForest; double g_absoluteZoom; QSharedPointer g_blastSearch; QString m_tempDirectory; QSharedPointer g_assemblyGraph; +QSharedPointer g_assemblyForest; QString formatIntForDisplay(int num) diff --git a/program/globals.h b/program/globals.h index 4e9d04b6..86f9188d 100644 --- a/program/globals.h +++ b/program/globals.h @@ -30,11 +30,15 @@ class Memory; class MyGraphicsView; class BlastSearch; class AssemblyGraph; +class AssemblyForest; class HiCSettings; +class RandomForestMainWindow; +class MainWindow; enum NodeColourScheme {UNIFORM_COLOURS, RANDOM_COLOURS, DEPTH_COLOUR, BLAST_HITS_RAINBOW_COLOUR, BLAST_HITS_SOLID_COLOUR, - CONTIGUITY_COLOUR, CUSTOM_COLOURS, RANDOM_COMPONENT_COLOURS, COLOUR_BY_TAX}; + CONTIGUITY_COLOUR, CUSTOM_COLOURS, RANDOM_COMPONENT_COLOURS, + COLOUR_BY_TAX, SAVE_COLOURS, CLASS_COLOURS, BLAST_HITS_CLASS_COLOURS}; enum GraphScope {WHOLE_GRAPH, AROUND_NODE, AROUND_BLAST_HITS, DEPTH_RANGE, AROUND_TAX}; enum HiCDrawingType {ALL_EDGES, ONE_EDGE, NO_EDGE}; enum HiCInclusionFilter {ALL, ALL_BETWEEN_GRAPH_COMPONENTS, ONE_BETWEEN_GRAPH_COMPONENT, ONE_FROM_TARGET_COMPONENT}; @@ -43,7 +47,7 @@ enum ContiguityStatus {STARTING, CONTIGUOUS_STRAND_SPECIFIC, NOT_CONTIGUOUS}; enum NodeDragging {ONE_PIECE, NEARBY_PIECES, ALL_PIECES, NO_DRAGGING}; enum ZoomSource {MOUSE_WHEEL, SPIN_BOX, KEYBOARD, GESTURE}; -enum UiState {NO_GRAPH_LOADED, GRAPH_LOADED, GRAPH_DRAWN}; +enum UiState {NO_GRAPH_LOADED, GRAPH_LOADED, GRAPH_DRAWN, NO_FEATURES_LOADED, FEATURES_LOADED, FEATURES_DRAWN}; enum NodeLengthMode {AUTO_NODE_LENGTH, MANUAL_NODE_LENGTH}; enum GraphFileType {LAST_GRAPH, FASTG, GFA, TRINITY, ASQG, PLAIN_FASTA, ANY_FILE_TYPE, UNKNOWN_FILE_TYPE}; @@ -61,16 +65,17 @@ enum NodeNameStatus {NODE_NAME_OKAY, NODE_NAME_TAKEN, NODE_NAME_CONTAINS_TAB, NODE_NAME_CONTAINS_SPACE}; enum SequencesLoadedFromFasta {NOT_READY, NOT_TRIED, TRIED}; - //Some of the program's common components are made global so they don't have //to be passed around as parameters. extern QSharedPointer g_settings; extern QSharedPointer g_hicSettings; extern QSharedPointer g_memory; extern MyGraphicsView * g_graphicsView; +extern MyGraphicsView * g_graphicsViewFeaturesForest; extern double g_absoluteZoom; extern QSharedPointer g_blastSearch; extern QSharedPointer g_assemblyGraph; +extern QSharedPointer g_assemblyForest; //Functions for formatting numbers are used in many places, and are made global. diff --git a/program/graphlayoutworker.cpp b/program/graphlayoutworker.cpp index 90a484c9..868a82b1 100644 --- a/program/graphlayoutworker.cpp +++ b/program/graphlayoutworker.cpp @@ -31,8 +31,9 @@ using namespace ogdf; GraphLayoutWorker::GraphLayoutWorker(ogdf::FMMMLayout * fmmm, ogdf::GraphAttributes * graphAttributes, - ogdf::EdgeArray * edgeArray, int graphLayoutQuality, bool linearLayout, - double graphLayoutComponentSeparation, double aspectRatio) : + ogdf::EdgeArray * edgeArray, int graphLayoutQuality, bool linearLayout, + double graphLayoutComponentSeparation, double aspectRatio) : + m_fmmm(fmmm), m_graphAttributes(graphAttributes), m_edgeArray(edgeArray), m_graphLayoutQuality(graphLayoutQuality), m_linearLayout(linearLayout), m_graphLayoutComponentSeparation(graphLayoutComponentSeparation), m_randSeed(-1), m_aspectRatio(aspectRatio) diff --git a/program/graphlayoutworker.h b/program/graphlayoutworker.h index 38e322f9..8ec3347d 100644 --- a/program/graphlayoutworker.h +++ b/program/graphlayoutworker.h @@ -31,7 +31,7 @@ class GraphLayoutWorker : public QObject public: GraphLayoutWorker(ogdf::FMMMLayout * fmmm, ogdf::GraphAttributes * graphAttributes, - ogdf::EdgeArray * edgeArray, int graphLayoutQuality, bool linearLayout, + ogdf::EdgeArray * edgeArray, int graphLayoutQuality, bool linearLayout, double graphLayoutComponentSeparation, double aspectRatio = 1.333333); GraphLayoutWorker(ogdf::FMMMLayout* fmmm, ogdf::GraphAttributes* graphAttributes, ogdf::EdgeArray* edgeArray, int graphLayoutQuality, bool linearLayout, diff --git a/program/main.cpp b/program/main.cpp index 32ec333f..00a03728 100644 --- a/program/main.cpp +++ b/program/main.cpp @@ -34,6 +34,8 @@ #include "../blast/blastsearch.h" #include "../graph/assemblygraph.h" #include "../ui/mygraphicsview.h" +#include "../random_forest/assemblyforest.h" +#include "../ui/randomforestmainwindow.h" #ifndef Q_OS_WIN32 #include @@ -102,7 +104,10 @@ int main(int argc, char *argv[]) g_memory.reset(new Memory()); g_blastSearch.reset(new BlastSearch()); g_assemblyGraph.reset(new AssemblyGraph()); + g_assemblyForest.reset(new AssemblyForest()); g_graphicsView = new MyGraphicsView(); + g_graphicsViewFeaturesForest = new MyGraphicsView(); + //g_randomForestMainWindow.reset(new RandomForestMainWindow()); //Save the terminal width (useful for displaying help text neatly). #ifndef Q_OS_WIN32 diff --git a/program/settings.cpp b/program/settings.cpp index 5df10f44..03a50581 100644 --- a/program/settings.cpp +++ b/program/settings.cpp @@ -37,6 +37,7 @@ Settings::Settings() componentSeparation = FloatSetting(50.0, 0, 1000.0); averageNodeWidth = FloatSetting(5.0, 0.5, 1000.0); + averageFeatureNodeWidth = FloatSetting(5.0, 0.5, 1000.0); depthEffectOnWidth = FloatSetting(0.5, 0.0, 1.0); depthPower = FloatSetting(0.5, 0.0, 1.0); @@ -80,6 +81,11 @@ Settings::Settings() displayTaxIdRank = false; displayTaxNameRank = false; + displayFeatureIdLabels = false; + displayFeatureClassLabels = false; + displayFeatureCustomLabels = false; + displayFeatureClassLikeFigure = false; + nodeDragging = NEARBY_PIECES; nodeColourScheme = RANDOM_COLOURS; diff --git a/program/settings.h b/program/settings.h index b9d8e29e..023c25d8 100644 --- a/program/settings.h +++ b/program/settings.h @@ -88,6 +88,7 @@ class Settings FloatSetting componentSeparation; FloatSetting averageNodeWidth; + FloatSetting averageFeatureNodeWidth; FloatSetting depthEffectOnWidth; FloatSetting depthPower; @@ -131,6 +132,11 @@ class Settings bool antialiasing; bool positionTextNodeCentre; + bool displayFeatureIdLabels; + bool displayFeatureClassLabels; + bool displayFeatureCustomLabels; + bool displayFeatureClassLikeFigure; + NodeDragging nodeDragging; QColor edgeColour; @@ -140,6 +146,7 @@ class Settings QColor textOutlineColour; NodeColourScheme nodeColourScheme; + NodeColourScheme featureColourScheme; QColor uniformPositiveNodeColour; QColor uniformNegativeNodeColour; QColor uniformNodeSpecialColour; @@ -226,6 +233,9 @@ class Settings bool wasComponentsFound = false; int m_clock = -1; bool addNewNodes = false; + bool roundMode = true; + + int featureForestEdgeLength = 10; }; #endif // SETTINGS_H diff --git a/random_forest/GraphicsItemFeatureEdge.cpp b/random_forest/GraphicsItemFeatureEdge.cpp new file mode 100644 index 00000000..b9eb3622 --- /dev/null +++ b/random_forest/GraphicsItemFeatureEdge.cpp @@ -0,0 +1,37 @@ + +#include "GraphicsItemFeatureEdge.h" +#include "../program/settings.h" +#include "../program/globals.h" +#include +#include +#include "RandomForestNode.h" +#include "GraphicsItemFeatureNode.h" + + +GraphicsItemFeatureEdge::GraphicsItemFeatureEdge(RandomForestNode* startingNode, RandomForestNode* endingNode, QGraphicsItem* parent) : + QGraphicsPathItem(parent), m_startingNode(startingNode), m_endingNode(endingNode) { + calculateAndSetPath(); +} + +void GraphicsItemFeatureEdge::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + double edgeWidth = g_settings->edgeWidth; + QColor penColour; + penColour = g_settings->edgeColour; + Qt::PenStyle s = Qt::SolidLine; + QPen edgePen(QBrush(penColour), edgeWidth, s, Qt::RoundCap); + painter->setPen(edgePen); + painter->drawPath(path()); +} + +void GraphicsItemFeatureEdge::calculateAndSetPath() { + { + m_startingLocation = m_startingNode->getGraphicsItemFeatureNode()->getFirst(); + m_endingLocation = m_endingNode->getGraphicsItemFeatureNode()->getFirst(); + + QPainterPath path; + path.moveTo(m_startingLocation); + path.lineTo(m_endingLocation); + setPath(path); + } +} \ No newline at end of file diff --git a/random_forest/GraphicsItemFeatureEdge.h b/random_forest/GraphicsItemFeatureEdge.h new file mode 100644 index 00000000..ee1ece7e --- /dev/null +++ b/random_forest/GraphicsItemFeatureEdge.h @@ -0,0 +1,24 @@ +#ifndef GRAPHICSITEMFUTUREEDGE_H +#define GRAPHICSITEMFUTUREEDGE_H + +#include "RandomForestNode.h" +#include + +class GraphicsItemFeatureNode; + +class GraphicsItemFeatureEdge : public QGraphicsPathItem +{ +public: + GraphicsItemFeatureEdge(RandomForestNode* startingNode, RandomForestNode* endingNode, QGraphicsItem* parent = 0); + + RandomForestNode* m_startingNode; + RandomForestNode* m_endingNode; + QPointF m_startingLocation; + QPointF m_endingLocation; + + void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*); + void calculateAndSetPath(); + +private: +}; +#endif //GRAPHICSITEMFUTUREEDGE_H diff --git a/random_forest/GraphicsItemFeatureNode.cpp b/random_forest/GraphicsItemFeatureNode.cpp new file mode 100644 index 00000000..d8219a14 --- /dev/null +++ b/random_forest/GraphicsItemFeatureNode.cpp @@ -0,0 +1,518 @@ +#include "GraphicsItemFeatureNode.h" +#include "../graph/ogdfnode.h" +#include +#include "RandomForestNode.h" +#include "GraphicsItemFeatureEdge.h" +#include +#include "../ui/mygraphicsview.h" +#include "../ui/mygraphicsscene.h" +#include +#include "RandomForestEdge.h" + +GraphicsItemFeatureNode::GraphicsItemFeatureNode(RandomForestNode* featureNode, + ogdf::GraphAttributes* graphAttributes, CommonGraphicsItemNode* parent) : + CommonGraphicsItemNode(g_graphicsViewFeaturesForest, parent), m_featureNode(featureNode) + +{ + m_width = g_settings->averageFeatureNodeWidth; + + OgdfNode* pathOgdfNode = featureNode->getOgdfNode(); + if (pathOgdfNode != 0) + { + for (size_t i = 0; i < pathOgdfNode->m_ogdfNodes.size(); ++i) + { + ogdf::node ogdfNode = pathOgdfNode->m_ogdfNodes[i]; + QPointF point(graphAttributes->x(ogdfNode), graphAttributes->y(ogdfNode)); + m_linePoints.push_back(point); + } + } + + remakePath(); +} + +QRectF GraphicsItemFeatureNode::boundingRect() const +{ + //double extraSize = g_settings->selectionThickness / 2.0; + QRectF bound = shape().boundingRect(); + + bound.setTop(bound.top() - 0.5); + bound.setBottom(bound.bottom() + 0.5); + bound.setLeft(bound.left() - 0.5); + bound.setRight(bound.right() + 0.5); + + return bound; +} + +QPainterPath GraphicsItemFeatureNode::shape() const +{ + int width = g_settings->averageFeatureNodeWidth; + QPainterPath mainNodePath; + mainNodePath.addEllipse(m_linePoints[0].toPoint(), width, width); + return mainNodePath; +} + +QPainterPath GraphicsItemFeatureNode::shapeRect() const +{ + int width = g_settings->averageFeatureNodeWidth; + QRect r(m_linePoints[0].toPoint().x(), m_linePoints[0].toPoint().y(), width, width); + r.moveCenter(m_linePoints[0].toPoint()); + QPainterPath mainNodePath; + mainNodePath.addRect(r); + return mainNodePath; +} + +void GraphicsItemFeatureNode::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) +{ + int width = g_settings->averageFeatureNodeWidth; + int x = m_linePoints[0].x(); + int y = m_linePoints[0].y(); + //QPainter painter; + setColour(); + QBrush brush; + QColor outlineColour = g_settings->outlineColour; + double outlineThickness = g_settings->outlineThickness; + if (isSelected()) { + outlineColour = g_settings->selectionColour; + outlineThickness = g_settings->selectionThickness; + } + if (outlineThickness > 0.0) + { + QPen outlinePen(QBrush(outlineColour), outlineThickness, Qt::SolidLine, + Qt::SquareCap, Qt::RoundJoin); + painter->setPen(outlinePen); + } + painter->setBrush(m_colour); + if (g_settings->displayFeatureClassLikeFigure) { + if (m_featureNode->getClassInd() == 0) { + QRect r(x, y, width, width); + r.moveCenter(m_linePoints[0].toPoint()); + + QPainterPath outlinePath; + outlinePath.addRect(r); + painter->fillPath(outlinePath, brush); + painter->drawRect(r); + } + else if (m_featureNode->getClassInd() == 1) { + QRect r(x, y, width, width); + r.moveCenter(m_linePoints[0].toPoint()); + + QPainterPath outlinePath; + outlinePath.moveTo(r.left() + (r.width() / 2), r.top()); + outlinePath.lineTo(r.bottomLeft()); + outlinePath.lineTo(r.bottomRight()); + outlinePath.lineTo(r.left() + (r.width() / 2), r.top()); + + painter->fillPath(outlinePath, brush); + painter->drawPath(outlinePath); + } + else { + QPainterPath outlinePath = shape(); + QRect r(x, y, width, width); + r.moveCenter(m_linePoints[0].toPoint()); + painter->fillPath(outlinePath, brush); + painter->drawEllipse(r); + } + } + else { + QPainterPath outlinePath = shape(); + QRect r(x, y, width, width); + r.moveCenter(m_linePoints[0].toPoint()); + painter->fillPath(outlinePath, brush); + painter->drawEllipse(r); + } + + if (g_settings->displayFeatureIdLabels || + g_settings->displayFeatureClassLabels || + g_settings->displayFeatureCustomLabels) + { + QStringList nodeText = getNodeText(); + drawNodeText(painter, nodeText); + } + + return; +} + +void GraphicsItemFeatureNode::drawNodeText(QPainter* painter, QStringList nodeText) { + QPainterPath textPath; + + QFontMetrics metrics(g_settings->labelFont); + double fontHeight = metrics.ascent(); + + for (int i = 0; i < nodeText.size(); ++i) + { + QString text = nodeText.at(i); + int stepsUntilLast = nodeText.size() - 1 - i; + double shiftLeft = -metrics.width(text) / 2.0; + textPath.addText(shiftLeft, -stepsUntilLast * fontHeight, g_settings->labelFont, text); + } + + drawTextPathAtLocation(painter, textPath); +} + +void GraphicsItemFeatureNode::drawTextPathAtLocation(QPainter* painter, QPainterPath textPath) +{ + QRectF textBoundingRect = textPath.boundingRect(); + double textHeight = textBoundingRect.height(); + QPointF offset(0.0, textHeight / 2.0); + QPointF centre(m_linePoints[0].x() - textBoundingRect.width() / 2.0 - m_width / 2.0 - 1.0, m_linePoints[0].y()); + + double zoom = g_absoluteZoom; + if (zoom == 0.0) + zoom = 1.0; + + double zoomAdjustment = 1.0 / (1.0 + ((zoom - 1.0) * g_settings->textZoomScaleFactor)); + double inverseZoomAdjustment = 1.0 / zoomAdjustment; + + painter->translate(centre); + painter->rotate(-m_graphicsView->getRotation()); + painter->scale(zoomAdjustment, zoomAdjustment); + painter->translate(offset); + + if (g_settings->textOutline) + { + painter->setPen(QPen(g_settings->textOutlineColour, + g_settings->textOutlineThickness * 2.0, + Qt::SolidLine, + Qt::SquareCap, + Qt::RoundJoin)); + painter->drawPath(textPath); + } + + painter->fillPath(textPath, QBrush(g_settings->textColour)); + painter->translate(-offset); + painter->scale(inverseZoomAdjustment, inverseZoomAdjustment); + painter->rotate(m_graphicsView->getRotation()); + painter->translate(-centre); +} + +void GraphicsItemFeatureNode::setColour() { + int blastColourIndex = m_featureNode->getBlastColourInd(); + int classInd = m_featureNode->getClassInd(); + switch (g_settings->featureColourScheme) + { + case UNIFORM_COLOURS: + m_colour.setRgb(255, 130, 5); + break; + case CLASS_COLOURS: + if (m_presetColours.empty()) { + m_presetColours = getPresetColours(); + } + classInd %= m_presetColours.size(); + m_colour = m_presetColours[classInd]; + break; + case BLAST_HITS_SOLID_COLOUR: + if (blastColourIndex == -1) { + m_colour = g_settings->noBlastHitsColour; + } else { + if (m_presetColours.empty()) { + m_presetColours = getPresetColours(); + } + m_presetColours = getPresetColours(); + + blastColourIndex %= m_presetColours.size(); + m_colour = m_presetColours[blastColourIndex]; + } + break; + case BLAST_HITS_CLASS_COLOURS: + if (blastColourIndex == -1) { + m_colour = g_settings->noBlastHitsColour; + } + else { + if (m_presetColours.empty()) { + m_presetColours = getPresetColours(); + } + classInd %= m_presetColours.size(); + m_colour = m_presetColours[classInd]; + } + break; + case CUSTOM_COLOURS: + m_colour = m_featureNode->getCustomColour(); + break; + } +} + +std::vector GraphicsItemFeatureNode::getPresetColours() +{ + std::vector presetColours; + + presetColours.push_back(QColor("#306FF8")); + presetColours.push_back(QColor("#86BB18")); + presetColours.push_back(QColor("#DF123A")); + presetColours.push_back(QColor("#181E2A")); + presetColours.push_back(QColor("#F91BBD")); + presetColours.push_back(QColor("#3CB2A4")); + presetColours.push_back(QColor("#D29AC1")); + presetColours.push_back(QColor("#E2922E")); + presetColours.push_back(QColor("#22501B")); + presetColours.push_back(QColor("#57297D")); + presetColours.push_back(QColor("#3FA0E6")); + presetColours.push_back(QColor("#770739")); + presetColours.push_back(QColor("#6A390C")); + presetColours.push_back(QColor("#25AB5D")); + presetColours.push_back(QColor("#ACAF61")); + presetColours.push_back(QColor("#F0826F")); + presetColours.push_back(QColor("#E94A80")); + presetColours.push_back(QColor("#C187F2")); + presetColours.push_back(QColor("#7E5764")); + presetColours.push_back(QColor("#037290")); + presetColours.push_back(QColor("#D65114")); + presetColours.push_back(QColor("#08396A")); + presetColours.push_back(QColor("#99ABBE")); + presetColours.push_back(QColor("#F270C0")); + presetColours.push_back(QColor("#F056F9")); + presetColours.push_back(QColor("#8E8D00")); + presetColours.push_back(QColor("#70010F")); + presetColours.push_back(QColor("#9C1E9A")); + presetColours.push_back(QColor("#471B1F")); + presetColours.push_back(QColor("#A00B6D")); + presetColours.push_back(QColor("#38C037")); + presetColours.push_back(QColor("#282C16")); + presetColours.push_back(QColor("#15604D")); + presetColours.push_back(QColor("#2E75D6")); + presetColours.push_back(QColor("#A09DEB")); + presetColours.push_back(QColor("#8454D7")); + presetColours.push_back(QColor("#301745")); + presetColours.push_back(QColor("#A45704")); + presetColours.push_back(QColor("#4D8C0E")); + presetColours.push_back(QColor("#C09860")); + presetColours.push_back(QColor("#009C73")); + presetColours.push_back(QColor("#FD6453")); + presetColours.push_back(QColor("#C11C4B")); + presetColours.push_back(QColor("#183B8B")); + presetColours.push_back(QColor("#5E6706")); + presetColours.push_back(QColor("#E42005")); + presetColours.push_back(QColor("#4873AF")); + presetColours.push_back(QColor("#6CA563")); + presetColours.push_back(QColor("#5E0F54")); + presetColours.push_back(QColor("#FE2065")); + presetColours.push_back(QColor("#5BB4D2")); + presetColours.push_back(QColor("#3F4204")); + presetColours.push_back(QColor("#521839")); + presetColours.push_back(QColor("#9A7706")); + presetColours.push_back(QColor("#77AB8C")); + presetColours.push_back(QColor("#105E04")); + presetColours.push_back(QColor("#98290F")); + presetColours.push_back(QColor("#B849D4")); + presetColours.push_back(QColor("#FC8426")); + presetColours.push_back(QColor("#341B03")); + presetColours.push_back(QColor("#E3278C")); + presetColours.push_back(QColor("#F28F93")); + presetColours.push_back(QColor("#D1A21F")); + presetColours.push_back(QColor("#277E46")); + presetColours.push_back(QColor("#285C60")); + presetColours.push_back(QColor("#76B945")); + presetColours.push_back(QColor("#E75D65")); + presetColours.push_back(QColor("#84ADDC")); + presetColours.push_back(QColor("#153C2B")); + presetColours.push_back(QColor("#FD10D9")); + presetColours.push_back(QColor("#C095D5")); + presetColours.push_back(QColor("#052B48")); + presetColours.push_back(QColor("#B365FC")); + presetColours.push_back(QColor("#97AA75")); + presetColours.push_back(QColor("#C78C9C")); + presetColours.push_back(QColor("#FD4838")); + presetColours.push_back(QColor("#F181E2")); + presetColours.push_back(QColor("#815A1A")); + presetColours.push_back(QColor("#BB2093")); + presetColours.push_back(QColor("#691822")); + presetColours.push_back(QColor("#C41A12")); + presetColours.push_back(QColor("#728A1F")); + presetColours.push_back(QColor("#375B73")); + presetColours.push_back(QColor("#97022C")); + presetColours.push_back(QColor("#95B44D")); + presetColours.push_back(QColor("#EB8DBB")); + presetColours.push_back(QColor("#83ACAB")); + presetColours.push_back(QColor("#E37D51")); + presetColours.push_back(QColor("#D78A68")); + presetColours.push_back(QColor("#4A41A2")); + presetColours.push_back(QColor("#8A0C79")); + presetColours.push_back(QColor("#133102")); + presetColours.push_back(QColor("#237A78")); + presetColours.push_back(QColor("#ADB03B")); + presetColours.push_back(QColor("#289E26")); + presetColours.push_back(QColor("#7683EC")); + presetColours.push_back(QColor("#4E1E04")); + presetColours.push_back(QColor("#BB17B2")); + presetColours.push_back(QColor("#EB6A81")); + presetColours.push_back(QColor("#47B4E8")); + presetColours.push_back(QColor("#0A6191")); + presetColours.push_back(QColor("#4EADB2")); + presetColours.push_back(QColor("#442965")); + presetColours.push_back(QColor("#FE784B")); + presetColours.push_back(QColor("#55BD8D")); + presetColours.push_back(QColor("#742B03")); + presetColours.push_back(QColor("#8C38AA")); + presetColours.push_back(QColor("#F758A6")); + presetColours.push_back(QColor("#A32526")); + presetColours.push_back(QColor("#442C2E")); + presetColours.push_back(QColor("#F06A97")); + presetColours.push_back(QColor("#3A1527")); + presetColours.push_back(QColor("#503509")); + presetColours.push_back(QColor("#2A67B4")); + presetColours.push_back(QColor("#243644")); + presetColours.push_back(QColor("#A74006")); + presetColours.push_back(QColor("#335900")); + presetColours.push_back(QColor("#A07484")); + presetColours.push_back(QColor("#490216")); + presetColours.push_back(QColor("#B19BCB")); + presetColours.push_back(QColor("#75B75A")); + presetColours.push_back(QColor("#BE71EB")); + presetColours.push_back(QColor("#024A2E")); + presetColours.push_back(QColor("#A097AB")); + presetColours.push_back(QColor("#7A287E")); + presetColours.push_back(QColor("#6A1444")); + presetColours.push_back(QColor("#212449")); + presetColours.push_back(QColor("#B07017")); + presetColours.push_back(QColor("#227D57")); + presetColours.push_back(QColor("#1B8CAF")); + presetColours.push_back(QColor("#016438")); + presetColours.push_back(QColor("#EA64CF")); + presetColours.push_back(QColor("#B5310E")); + presetColours.push_back(QColor("#B00765")); + presetColours.push_back(QColor("#5F42B3")); + presetColours.push_back(QColor("#EF9649")); + presetColours.push_back(QColor("#25717F")); + presetColours.push_back(QColor("#BCA309")); + presetColours.push_back(QColor("#FA35A6")); + presetColours.push_back(QColor("#F63D54")); + presetColours.push_back(QColor("#E83D6C")); + presetColours.push_back(QColor("#8362F2")); + presetColours.push_back(QColor("#33BC4A")); + presetColours.push_back(QColor("#194A85")); + presetColours.push_back(QColor("#E24215")); + presetColours.push_back(QColor("#6D71FE")); + presetColours.push_back(QColor("#3E52AF")); + presetColours.push_back(QColor("#1E9E89")); + presetColours.push_back(QColor("#740860")); + presetColours.push_back(QColor("#4B7BEE")); + presetColours.push_back(QColor("#8742C0")); + presetColours.push_back(QColor("#DD8EC6")); + presetColours.push_back(QColor("#CD202C")); + presetColours.push_back(QColor("#FD82C2")); + presetColours.push_back(QColor("#3C2874")); + presetColours.push_back(QColor("#F9742B")); + presetColours.push_back(QColor("#013B10")); + presetColours.push_back(QColor("#D12867")); + presetColours.push_back(QColor("#F743C3")); + presetColours.push_back(QColor("#B98EEC")); + presetColours.push_back(QColor("#D260EC")); + presetColours.push_back(QColor("#671C06")); + presetColours.push_back(QColor("#37A968")); + presetColours.push_back(QColor("#3B9529")); + presetColours.push_back(QColor("#2A0E33")); + presetColours.push_back(QColor("#51B237")); + presetColours.push_back(QColor("#95B61B")); + presetColours.push_back(QColor("#B195E2")); + presetColours.push_back(QColor("#68B49A")); + presetColours.push_back(QColor("#182339")); + presetColours.push_back(QColor("#FC4822")); + presetColours.push_back(QColor("#D79621")); + presetColours.push_back(QColor("#90761B")); + presetColours.push_back(QColor("#777315")); + presetColours.push_back(QColor("#E389E9")); + presetColours.push_back(QColor("#35BD64")); + presetColours.push_back(QColor("#C17910")); + presetColours.push_back(QColor("#3386ED")); + presetColours.push_back(QColor("#E82C2E")); + presetColours.push_back(QColor("#AC925F")); + presetColours.push_back(QColor("#F227C8")); + presetColours.push_back(QColor("#F43E67")); + presetColours.push_back(QColor("#55AEEB")); + presetColours.push_back(QColor("#F518E3")); + presetColours.push_back(QColor("#AB0643")); + presetColours.push_back(QColor("#8DA1F3")); + presetColours.push_back(QColor("#5C9C14")); + presetColours.push_back(QColor("#381F27")); + presetColours.push_back(QColor("#6BB7B5")); + presetColours.push_back(QColor("#9842BE")); + presetColours.push_back(QColor("#4897D6")); + presetColours.push_back(QColor("#8958E4")); + presetColours.push_back(QColor("#8F0065")); + presetColours.push_back(QColor("#A10A5E")); + presetColours.push_back(QColor("#076315")); + presetColours.push_back(QColor("#FA5EF9")); + presetColours.push_back(QColor("#A33402")); + presetColours.push_back(QColor("#A0ABC4")); + presetColours.push_back(QColor("#2B6EFE")); + presetColours.push_back(QColor("#9A9EE7")); + + return presetColours; +} + +QStringList GraphicsItemFeatureNode::getNodeText() +{ + QStringList nodeText; + if (g_settings->displayFeatureCustomLabels) + nodeText << m_featureNode->getCustomLabelForDisplay(); + if (g_settings->displayFeatureIdLabels) + { + QString id = m_featureNode->getName(); + nodeText << id; + } + if (g_settings->displayFeatureClassLabels) + nodeText << m_featureNode->getClass(); + + return nodeText; +} + +void GraphicsItemFeatureNode::mousePressEvent(QGraphicsSceneMouseEvent* event) +{ + updateGrabIndex(event); +} + +//When this node graphics item is moved, each of the connected edge +//graphics items will need to be adjusted accordingly. +void GraphicsItemFeatureNode::mouseMoveEvent(QGraphicsSceneMouseEvent* event) +{ + QPointF difference = event->pos() - event->lastPos(); + + //If this node is selected, then move all of the other selected nodes too. + //If it is not selected, then only move this node. + std::vector nodesToMove; + MyGraphicsScene* graphicsScene = dynamic_cast(scene()); + if (isSelected()) + nodesToMove = graphicsScene->getSelectedGraphicsItemFeatureNode(); + else + nodesToMove.push_back(this); + + for (size_t i = 0; i < nodesToMove.size(); ++i) + { + nodesToMove[i]->shiftPoints(difference); + nodesToMove[i]->remakePath(); + } + graphicsScene->possiblyExpandSceneRectangle(&nodesToMove); + + fixEdgePaths(&nodesToMove); +} + +void GraphicsItemFeatureNode::fixEdgePaths(std::vector* nodes) +{ + std::set edgesToFix; + + if (nodes == 0) + { + const std::vector* edges = m_featureNode->getEdges(); + for (size_t j = 0; j < edges->size(); ++j) + edgesToFix.insert((*edges)[j]); + } + else + { + for (size_t i = 0; i < nodes->size(); ++i) + { + RandomForestNode* node = (*nodes)[i]->m_featureNode; + const std::vector* edges = node->getEdges(); + for (size_t j = 0; j < edges->size(); ++j) + edgesToFix.insert((*edges)[j]); + } + } + + for (std::set::iterator i = edgesToFix.begin(); i != edgesToFix.end(); ++i) + { + RandomForestEdge* randomForestEdge = *i; + GraphicsItemFeatureEdge* graphicsItemEdge = randomForestEdge->getGraphicsItemFeatureEdge(); + + //If this edge has a graphics item, adjust it now. + if (graphicsItemEdge != 0) + graphicsItemEdge->calculateAndSetPath(); + } +} diff --git a/random_forest/GraphicsItemFeatureNode.h b/random_forest/GraphicsItemFeatureNode.h new file mode 100644 index 00000000..b5319947 --- /dev/null +++ b/random_forest/GraphicsItemFeatureNode.h @@ -0,0 +1,50 @@ +#ifndef GRAPHICSITEMFUTURENODE_H +#define GRAPHICSITEMFUTURENODE_H + +#include +#include "../ogdf/basic/GraphAttributes.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../program/settings.h" +#include "../program/globals.h" +#include "../painting/CommonGraphicsItemNode.h" + +class RandomForestNode; +class Path; + +class GraphicsItemFeatureNode : public CommonGraphicsItemNode +{ +public: + GraphicsItemFeatureNode(RandomForestNode* featureNode, + ogdf::GraphAttributes* graphAttributes, + CommonGraphicsItemNode* parent = 0); + + RandomForestNode* m_featureNode; + + QRectF boundingRect() const; + void paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*); + + QPainterPath shape() const; + QPainterPath shapeRect() const; + QPainterPath shapeTriangl() const; + void setColour(); + QStringList getNodeText(); + void mousePressEvent(QGraphicsSceneMouseEvent* event); + void mouseMoveEvent(QGraphicsSceneMouseEvent* event); +private: + void fixEdgePaths(std::vector* nodes); + std::vector m_presetColours; + std::vector getPresetColours(); + void drawNodeText(QPainter* painter, QStringList nodeText); + void drawTextPathAtLocation(QPainter* painter, QPainterPath textPath); + +}; +#endif // GRAPHICSITEMFUTURENODE_H + diff --git a/random_forest/RandomForestEdge.cpp b/random_forest/RandomForestEdge.cpp new file mode 100644 index 00000000..e69de29b diff --git a/random_forest/RandomForestEdge.h b/random_forest/RandomForestEdge.h new file mode 100644 index 00000000..5cc41986 --- /dev/null +++ b/random_forest/RandomForestEdge.h @@ -0,0 +1,17 @@ +#ifndef RANDOMFORESTEDGE_H +#define RANDOMFORESTEDGE_H + +#include "GraphicsItemFeatureEdge.h" + +class RandomForestEdge +{ +public: + GraphicsItemFeatureEdge* getGraphicsItemFeatureEdge() { + return m_graphicsEdge; + }; + void setGraphicsItemFeatureEdge(GraphicsItemFeatureEdge* edge) { m_graphicsEdge = edge; }; + +private: + GraphicsItemFeatureEdge* m_graphicsEdge; +}; +#endif; //RANDOMFORESTEDGE_H diff --git a/random_forest/RandomForestNode.cpp b/random_forest/RandomForestNode.cpp new file mode 100644 index 00000000..1c6c4ab2 --- /dev/null +++ b/random_forest/RandomForestNode.cpp @@ -0,0 +1,48 @@ +#include "RandomForestNode.h" +#include "../graph/ogdfnode.h" + +RandomForestNode::RandomForestNode(QString name) : + m_name(name) +{} + +RandomForestNode::~RandomForestNode() +{} + +void RandomForestNode::addChild(RandomForestNode * child) { + m_children.push_back(child); + child -> setDepth(m_depth + 1); + child->setParent(this); +} + +void RandomForestNode::addToOgdfGraph(ogdf::Graph* ogdfGraph) +{ + m_ogdfNode = new OgdfNode(); + ogdf::node newNode = ogdfGraph->newNode(); + m_ogdfNode->addOgdfNode(newNode); +} + +QColor RandomForestNode::getCustomColour() { + if (hasCustomColour()) { + return m_customColour; + } + else { + return g_settings->defaultCustomNodeColour; + } +} + +QStringList RandomForestNode::getCustomLabelForDisplay() const +{ + QStringList customLabelLines; + if (!getCustomLabel().isEmpty()) { + QStringList labelLines = getCustomLabel().split("\\n"); + for (int i = 0; i < labelLines.size(); ++i) + customLabelLines << labelLines[i]; + } + return customLabelLines; +} + +void RandomForestNode::setCustomLabel(QString newLabel) +{ + newLabel.replace("\t", " "); + m_customLabel = newLabel; +} \ No newline at end of file diff --git a/random_forest/RandomForestNode.h b/random_forest/RandomForestNode.h new file mode 100644 index 00000000..66d81bf1 --- /dev/null +++ b/random_forest/RandomForestNode.h @@ -0,0 +1,89 @@ +#ifndef RANDOMFORESTNODE_H +#define RANDOMFORESTNODE_H + +#include +#include +#include +#include "../ogdf/basic/Graph.h" +#include "../ogdf/basic/GraphAttributes.h" +#include "../program/globals.h" +#include "../program/settings.h" + +class OgdfNode; +class GraphicsItemFeatureNode; +class RandomForestEdge; + +class RandomForestNode +{ +public: + RandomForestNode(QString name); + ~RandomForestNode(); + + void addChild(RandomForestNode * child); + void setDepth(int depth) { m_depth = depth; } + + int getDepth() { return m_depth; } + QString getName() { return m_name; } + + RandomForestNode* getParent() { return m_parent; } + void setParent(RandomForestNode* parent) { m_parent = parent; } + + OgdfNode* getOgdfNode() const { return m_ogdfNode; } + bool inOgdf() const { return m_ogdfNode != NULL; } + + GraphicsItemFeatureNode * getGraphicsItemFeatureNode() { return m_graphicsItemFeatureNode; } + void setGraphicsItemFeatureNode(GraphicsItemFeatureNode* graphicsItemNode) { m_graphicsItemFeatureNode = graphicsItemNode; } + bool hasGraphicsItemFeature() { return m_graphicsItemFeatureNode != NULL; } + + void addToOgdfGraph(ogdf::Graph* ogdfGraph); + + bool isDrawn() { return m_isDrawn; } + void setAsDrawn(bool drawnStatus) { m_isDrawn = drawnStatus; } + + std::vector getChildren() { return m_children; } + + std::vector getQuerySequences() { return m_querySequences; } + + void addQuerySequence(QString seq) { m_querySequences.push_back(seq); } + + void setFeature(QString featureName, double threshold) { m_featureName = featureName; m_threshold = threshold; } + QString getFeatureName() { return m_featureName; } + double getThreshold() { return m_threshold; } + void setClass(int classInd, QString clazz) { m_classInd = classInd; m_class = clazz; } + QString getClass() { return m_class; } + int getClassInd() { return m_classInd; } + int getBlastColourInd() { return m_blastColourIndex; } + void setBlastColourInd(int blastColourInd) { m_blastColourIndex = blastColourInd; } + bool hasCustomColour() { return m_customColour.isValid(); } + QColor getCustomColour(); + void setCustomColour(QColor colour) { m_customColour = colour; } + QString getCustomLabel() const { return m_customLabel; } + QStringList getCustomLabelForDisplay() const; + void setCustomLabel(QString newLabel); + std::vector* getEdges() { return &m_edges; }; + void addEdge(RandomForestEdge* edge) { m_edges.push_back(edge); } + +private: + QString m_name; + int m_depth = 0; + std::vector m_children; + RandomForestNode * m_parent = NULL; + OgdfNode * m_ogdfNode = NULL; + bool m_isDrawn = false; + std::vector m_querySequences; + + GraphicsItemFeatureNode * m_graphicsItemFeatureNode = NULL; + QString m_featureName = NULL; + double m_threshold = 0; + QString m_class = NULL; + int m_classInd = -1; + int m_blastColourIndex = -1; + QColor m_customColour; + QString m_customLabel = NULL; + + std::vector m_edges; + +}; + +#endif // RANDOMFORESTNODE_H + diff --git a/random_forest/assemblyforest.cpp b/random_forest/assemblyforest.cpp new file mode 100644 index 00000000..8bf84020 --- /dev/null +++ b/random_forest/assemblyforest.cpp @@ -0,0 +1,251 @@ +#include "assemblyforest.h" +#include +#include +#include +#include "../graph/ogdfnode.h" +#include "../program/globals.h" +#include "GraphicsItemFeatureNode.h" +#include "GraphicsItemFeatureEdge.h" + +AssemblyForest::AssemblyForest() { + m_ogdfGraph = new ogdf::Graph(); + m_edgeArray = new ogdf::EdgeArray(*m_ogdfGraph); + m_graphAttributes = new ogdf::GraphAttributes(*m_ogdfGraph, ogdf::GraphAttributes::nodeGraphics | + ogdf::GraphAttributes::edgeGraphics); +} + +AssemblyForest::~AssemblyForest() {} + +bool AssemblyForest::loadRandomForestFromFile(QString filename, QString* errormsg) { + QFile featureForestFile(filename); + + if (!featureForestFile.open(QIODevice::ReadOnly)) + { + *errormsg = "Unable to read from specified file."; + return false; + } + + QTextStream in(&featureForestFile); + QApplication::processEvents(); + while (!in.atEnd()) + { + QString line = in.readLine(); + QStringList data = line.split(QRegExp("\t")); + if (data.size() <= 1) { + break; + } + if (data[0] == "N") { + QString nodeName = data[1]; + RandomForestNode* curnode = m_nodes[nodeName]; + if (curnode == nullptr) { + curnode = new RandomForestNode(nodeName); + m_nodes.insert(nodeName, curnode); + } + if (data.size() > 2 && data[2].size() > 0) { + processChild(curnode, data[2]); + } + if (data.size() > 3 && data[3].size() > 0) { + processChild(curnode, data[3]); + } + + } + if (data[0] == "S") { + QString nodeName = data[1]; + QString seq = data[2].simplified(); + m_nodes[nodeName]->addQuerySequence(seq); + } + if (data[0] == "F") { + QString nodeName = data[1]; + QString featureName = data[2]; + double threshold = data[3].toDouble(); + m_nodes[nodeName]->setFeature(featureName, threshold); + } + if (data[0] == "C") { + QString nodeName = data[1]; + QString clazz = data[2].simplified(); + addClass(clazz); + m_nodes[nodeName]->setClass(m_classes[clazz], clazz); + } + + } + featureForestFile.close(); + for (RandomForestNode* node : m_nodes) { + if (node->getParent() == NULL) { + m_roots[node->getName()] = node; + } + } + return true; +} + +void AssemblyForest::buildOgdfGraphFromNodesAndEdges() +{ + QMapIterator i(m_nodes); + + while (i.hasNext()) + { + i.next(); + + RandomForestNode* node = i.value(); + node->setAsDrawn(true); + node->addToOgdfGraph(m_ogdfGraph); + + } + + QMapIterator j(m_nodes); + while (j.hasNext()) + { + j.next(); + RandomForestNode* node = j.value(); + if (node->isDrawn()) { + for (RandomForestNode* child : node->getChildren()) { + if (child->isDrawn()) { + addEdgeToOgdfGraph(node, child); + } + } + } + } +} + +void AssemblyForest::recalculateAllNodeWidths() +{ + QMapIterator i(m_nodes); + while (i.hasNext()) + { + i.next(); + GraphicsItemFeatureNode* graphicsItemNode = i.value()->getGraphicsItemFeatureNode(); + if (graphicsItemNode != 0) + graphicsItemNode->m_width = g_settings->averageFeatureNodeWidth; + } +} + +void AssemblyForest::addEdgeToOgdfGraph(RandomForestNode* startingNode, RandomForestNode* endingNode) { + ogdf::node firstEdgeOgdfNode; + ogdf::node secondEdgeOgdfNode; + + if (startingNode->inOgdf()) + firstEdgeOgdfNode = startingNode->getOgdfNode()->getFirst(); + else + return; + + if (endingNode->inOgdf()) + secondEdgeOgdfNode = endingNode->getOgdfNode()->getFirst(); + else + return; + + + ogdf::edge newEdge = m_ogdfGraph->newEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); + (*m_edgeArray)[newEdge] = g_settings->featureForestEdgeLength; +} + +void AssemblyForest::cleanUp() {} + +void AssemblyForest::processChild(RandomForestNode* parent, QString childName) { + if (m_nodes.contains(childName)) { + RandomForestNode* child = m_nodes[childName]; + parent->addChild(child); + RandomForestEdge* edge = new RandomForestEdge(); + parent->addEdge(edge); + child->addEdge(edge); + m_edges.insert(QPair(parent, child), edge); + + } + else { + RandomForestNode* child = new RandomForestNode(childName); + m_nodes.insert(childName, child); + parent->addChild(child); + RandomForestEdge* edge = new RandomForestEdge(); + parent->addEdge(edge); + child->addEdge(edge); + m_edges.insert(QPair(parent, child), edge); + } + +} + +void AssemblyForest::addGraphicsItemsToScene(MyGraphicsScene* scene) +{ + scene->clear(); + + //First make the GraphicsItemNode objects + QMapIterator i(m_nodes); + while (i.hasNext()) + { + i.next(); + RandomForestNode* node = i.value(); + if (node->isDrawn()) + { + GraphicsItemFeatureNode* graphicsItemNode = new GraphicsItemFeatureNode(node, m_graphAttributes); + node->setGraphicsItemFeatureNode(graphicsItemNode); + graphicsItemNode->setFlag(QGraphicsItem::ItemIsSelectable); + graphicsItemNode->setFlag(QGraphicsItem::ItemIsMovable); + } + } + + resetAllNodeColours(); + + + QMapIterator j(m_nodes); + while (j.hasNext()) + { + j.next(); + RandomForestNode* node = j.value(); + if (node->isDrawn()) { + for (RandomForestNode* child : node->getChildren()) { + if (child->isDrawn()) { + GraphicsItemFeatureEdge* graphicsItemEdge = new GraphicsItemFeatureEdge(node, child); + graphicsItemEdge->setFlag(QGraphicsItem::ItemIsSelectable); + scene->addItem(graphicsItemEdge); + m_edges[QPair(node, child)]->setGraphicsItemFeatureEdge(graphicsItemEdge); + } + } + } + } + + //Now add the GraphicsItemNode objects to the scene so they are drawn + //on top + QMapIterator k(m_nodes); + while (k.hasNext()) + { + k.next(); + RandomForestNode* node = k.value(); + if (node->isDrawn() && node->hasGraphicsItemFeature()) + scene->addItem(node->getGraphicsItemFeatureNode()); + } +} + +void AssemblyForest::resetAllNodeColours() +{ + QMapIterator i(m_nodes); + while (i.hasNext()) + { + i.next(); + if (i.value()->isDrawn() && i.value()->getGraphicsItemFeatureNode() != 0) + i.value()->getGraphicsItemFeatureNode()->setColour(); + } +} + +void AssemblyForest::addClass(QString className) { + int ind = m_classes.size(); + if (!m_classes.contains(className)) { + m_classes[className] = ind; + } +} + +QString AssemblyForest::getClassFigureInfo() { + QString res = ""; + for (QString className : m_classes.keys()) { + res = res + className + ": "; + if (m_classes[className] == 0) { + res += QChar(0x2B1B); + res += " (cube)\n"; + } + else if (m_classes[className] == 1) { + res += QChar(0x25B2); + res += " (triangle)\n"; + } + else { + res += QChar(0x2B24); + res += " (circle)\n"; + } + } + return res; +} \ No newline at end of file diff --git a/random_forest/assemblyforest.h b/random_forest/assemblyforest.h new file mode 100644 index 00000000..56544eae --- /dev/null +++ b/random_forest/assemblyforest.h @@ -0,0 +1,76 @@ +//Copyright 2017 Ryan Wick + +//This file is part of Bandage + +//Bandage 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 3 of the License, or +//(at your option) any later version. + +//Bandage 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 Bandage. If not, see . + + +#ifndef ASSEMBLYFOREST_H +#define ASSEMBLYFOREST_H + +#include +#include + +#include "../ogdf/basic/Graph.h" +#include "../ogdf/basic/GraphAttributes.h" +#pragma once + +#include +#include +#include "../program/globals.h" +#include "../ui/mygraphicsscene.h" +#include +#include "RandomForestEdge.h" +#include "RandomForestNode.h" +#include "../program/globals.h" + +class RandomForestNode; +class RandomForestEdge; +class MyProgressDialog; +class OgdfNode; + +class AssemblyForest : public QObject +{ + Q_OBJECT + +public: + AssemblyForest(); + ~AssemblyForest(); + bool loadRandomForestFromFile(QString filename, QString* errormsg); + void AssemblyForest::buildOgdfGraphFromNodesAndEdges(); + + QMap m_roots; + QMap m_nodes; + QMap, RandomForestEdge*> m_edges; + QMap m_classes; + + ogdf::Graph* m_ogdfGraph; + ogdf::EdgeArray* m_edgeArray; + ogdf::GraphAttributes* m_graphAttributes; + void cleanUp(); + void addGraphicsItemsToScene(MyGraphicsScene* scene); + void recalculateAllNodeWidths(); + void addClass(QString className); + void resetAllNodeColours(); + QString getClassFigureInfo(); + +private: + + void processChild(RandomForestNode* parent, QString childName); + void addEdgeToOgdfGraph(RandomForestNode* startNode, RandomForestNode* lastNode); + + +}; + +#endif // ASSEMBLYFOREST_H diff --git a/ui/mainwindow.cpp b/ui/mainwindow.cpp index f96f31a7..9ce7a913 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -70,6 +70,9 @@ #include "taxinfodialog.h" #include #include +#include "../random_forest/assemblyforest.h" +#include "../random_forest/GraphicsItemFeatureNode.h" +#include MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : QMainWindow(0), @@ -78,9 +81,10 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : m_uiState(NO_GRAPH_LOADED), m_blastSearchDialog(0), m_alreadyShown(false) { ui->setupUi(this); - QApplication::setWindowIcon(QIcon(QPixmap(":/icons/icon.png"))); - ui->graphicsViewWidget->layout()->addWidget(g_graphicsView); + ui->featureClassInfoText->setFixedHeight(87); + ui->featureClassInfoText->setEnabled(false); + ui->allViewsWidget->layout()->addWidget(g_graphicsView); srand(time(NULL)); @@ -106,6 +110,7 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : ui->selectedEdgesTextEdit->setFixedHeight(ui->selectedEdgesTextEdit->sizeHint().height() / 2.5); setUiState(NO_GRAPH_LOADED); + setFeaturesUiState(NO_FEATURES_LOADED); m_graphicsViewZoom = new GraphicsViewZoom(g_graphicsView); g_graphicsView->m_zoom = m_graphicsViewZoom; @@ -113,6 +118,8 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : m_scene = new MyGraphicsScene(this); g_graphicsView->setScene(m_scene); + m_randomForestMainWindow = new RandomForestMainWindow(); + setInfoTexts(); //Nothing is selected yet, so this will hide the appropriate labels. @@ -125,6 +132,7 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : graphScopeChanged(); switchColourScheme(); + switchFeatureColourScheme(); switchTaxRank(); //If this is a Mac, change the 'Delete' shortcuts to 'Backspace' instead. @@ -134,11 +142,13 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : #endif connect(ui->drawGraphButton, SIGNAL(clicked()), this, SLOT(drawGraph())); + connect(ui->drawFeaturesButton, SIGNAL(clicked()), this, SLOT(drawFeaturesForest())); connect(ui->unzipNodesPushButton, SIGNAL(clicked()), this, SLOT(unzipSelectedNodes())); connect(ui->actionLoad_graph, SIGNAL(triggered()), this, SLOT(loadGraph())); connect(ui->actionLoad_CSV, SIGNAL(triggered(bool)), this, SLOT(loadCSV())); connect(ui->actionLoad_HiC_data, SIGNAL(triggered(bool)), this, SLOT(loadHiC())); connect(ui->actionLoad_Taxonometry, SIGNAL(triggered(bool)), this, SLOT(loadTax())); + connect(ui->actionLoad_features_forest, SIGNAL(triggered(bool)), this, SLOT(loadFeaturesForest())); connect(ui->actionExit, SIGNAL(triggered()), this, SLOT(close())); connect(ui->graphScopeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(graphScopeChanged())); connect(ui->zoomSpinBox, SIGNAL(valueChanged(double)), this, SLOT(zoomSpinBoxChanged())); @@ -148,9 +158,12 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : connect(ui->actionCopy_selected_node_path_to_clipboard, SIGNAL(triggered(bool)), this, SLOT(copySelectedPathToClipboard())); connect(ui->actionSave_selected_node_path_to_FASTA, SIGNAL(triggered(bool)), this, SLOT(saveSelectedPathToFile())); connect(ui->coloursComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(switchColourScheme())); + connect(ui->featuresColoursComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(switchFeatureColourScheme())); connect(ui->taxColourComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(switchTaxRank())); - connect(ui->actionSave_image_current_view, SIGNAL(triggered()), this, SLOT(saveImageCurrentView())); - connect(ui->actionSave_image_entire_scene, SIGNAL(triggered()), this, SLOT(saveImageEntireScene())); + connect(ui->actionSave_graph_image_current_view, SIGNAL(triggered()), this, SLOT(saveImageGraphCurrentView())); + connect(ui->actionSave_graph_image_entire_scene, SIGNAL(triggered()), this, SLOT(saveImageGraphEntireScene())); + connect(ui->actionSave_image_features_current_view, SIGNAL(triggered()), this, SLOT(saveImageFeaturesCurrentView())); + connect(ui->actionSave_image_features_entire_scene, SIGNAL(triggered()), this, SLOT(saveImageFeaturesEntireScene())); connect(ui->nodeCustomLabelsCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); connect(ui->nodeNamesCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); connect(ui->nodeLengthsCheckBox, SIGNAL(toggled(bool)), this, SLOT(setTextDisplaySettings())); @@ -190,6 +203,7 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : connect(ui->startingNodesExactMatchRadioButton, SIGNAL(toggled(bool)), this, SLOT(startingNodesExactMatchChanged())); connect(ui->actionSpecify_exact_path_for_copy_save, SIGNAL(triggered()), this, SLOT(openPathSpecifyDialog())); connect(ui->nodeWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(nodeWidthChanged())); + connect(ui->featureNodeWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(featureNodeWidthChanged())); connect(g_graphicsView, SIGNAL(copySelectedSequencesToClipboard()), this, SLOT(copySelectedSequencesToClipboard())); connect(g_graphicsView, SIGNAL(saveSelectedSequencesToFile()), this, SLOT(saveSelectedSequencesToFile())); connect(ui->actionSave_entire_graph_to_FASTA, SIGNAL(triggered(bool)), this, SLOT(saveEntireGraphToFasta())); @@ -207,6 +221,13 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : connect(ui->moreInfoButton, SIGNAL(clicked(bool)), this, SLOT(openGraphInfoDialog())); connect(ui->taxInfoButton, SIGNAL(clicked(bool)), this, SLOT(openTaxInfoDialog())); connect(ui->taxInfoHiCButton, SIGNAL(clicked(bool)), this, SLOT(openTaxInfoHiCDialog())); + connect(ui->actionSave_common_tax_information, SIGNAL(triggered()), this, SLOT(saveTaxInfo())); + connect(ui->actionSave_tax_info_with_Hi_C_links, SIGNAL(triggered()), this, SLOT(saveHiCTaxInfo())); + connect(ui->connectSelectedFeatureNodes, SIGNAL(clicked()), this, SLOT(matchSelectedFeatureNodes())); + connect(ui->featureIdCheckBox, SIGNAL(toggled(bool)), this, SLOT(setFeatureTextDisplaySettings())); + connect(ui->featureClassCheckBox, SIGNAL(toggled(bool)), this, SLOT(setFeatureTextDisplaySettings())); + connect(ui->featureCustomCheckBox, SIGNAL(toggled(bool)), this, SLOT(setFeatureTextDisplaySettings())); + connect(ui->featureClassLikeFigureCheckBox, SIGNAL(toggled(bool)), this, SLOT(setFeatureTextDisplaySettings())); connect(this, SIGNAL(windowLoaded()), this, SLOT(afterMainWindowShow()), Qt::ConnectionType(Qt::QueuedConnection | Qt::UniqueConnection)); } @@ -297,7 +318,6 @@ void MainWindow::loadHiC(QString fullFileName) { progress.setWindowModality(Qt::WindowModal); progress.show(); - bool coloursLoaded = false; bool success = g_assemblyGraph->loadHiC(fullFileName, &errormsg); if (success) @@ -336,7 +356,6 @@ void MainWindow::loadTax(QString fullFileName) { progress.setWindowModality(Qt::WindowModal); progress.show(); - bool coloursLoaded = false; bool success = g_assemblyGraph->loadTax(fullFileName, &errormsg); if (success) @@ -354,6 +373,40 @@ void MainWindow::loadTax(QString fullFileName) { } } +void MainWindow::loadFeaturesForest(QString fullFileName) { + QString selectedFilter = "Comma separated value (*.txt)"; + if (fullFileName == "") + { + fullFileName = QFileDialog::getOpenFileName(this, "Load Features forest", g_memory->rememberedPath, + "Comma separated value (*.txt)", + &selectedFilter); + } + + if (fullFileName == "") + return; // user clicked on cancel + QString errormsg; + QStringList columns; + + try + { + bool success = g_assemblyForest->loadRandomForestFromFile(fullFileName, &errormsg); + + if (success) + { + setFeaturesUiState(FEATURES_LOADED); + } + } + catch (...) + { + QString errorTitle = "Error loading features forest"; + QString errorMessage = "There was an error when attempting to load:\n" + + fullFileName + "\n\n" + "Please verify that this file has the correct format."; + QMessageBox::warning(this, errorTitle, errorMessage); + } + +} + void MainWindow::loadCSV(QString fullFileName) { QString selectedFilter = "Comma separated value (*.csv)"; @@ -586,6 +639,45 @@ void MainWindow::unzipSelectedNodes() { } } +void MainWindow::featureSelectionChanged() { + std::vector selectedNodes = m_randomForestMainWindow->m_scene->getSelectedFeatureNodes(); + + if (selectedNodes.size() == 0) + { + ui->selectedNodesTextEdit->setPlainText(""); + setSelectedNodesWidgetsVisibility(false); + } + + else //One or more nodes selected + { + setSelectedNodesWidgetsVisibility(true); + + int selectedNodeCount; + QString selectedFeatureNodeText; + + m_randomForestMainWindow->getSelectedNodeInfo(selectedNodeCount, selectedFeatureNodeText); + + if (selectedNodeCount == 1) + { + ui->selectedNodesTitleLabel->setText("Selected node"); + ui->selectedNodesLengthLabel->setText(""); + ui->selectedNodesDepthLabel->setText(""); + } + else + { + ui->selectedNodesTitleLabel->setText("Selected nodes (" + formatIntForDisplay(selectedNodeCount) + ")"); + ui->selectedNodesLengthLabel->setText(""); + ui->selectedNodesDepthLabel->setText(""); + } + + ui->selectedNodesTextEdit->setPlainText(selectedFeatureNodeText); + } + + ui->selectedEdgesTextEdit->setPlainText(""); + setSelectedEdgesWidgetsVisibility(false); + setTaxVisibility(false); +} + void MainWindow::selectionChanged() { std::vector selectedNodes = m_scene->getSelectedNodes(); @@ -889,6 +981,10 @@ void MainWindow::setHiCWidgetVisibility(bool visible) void MainWindow::setTaxVisibility(bool visible) { + ui->labelTaxInfo->setVisible(visible); + ui->taxLine_4->setVisible(visible); + ui->taxInfoTextEdit->setVisible(visible); + ui->labelTaxInfo->setVisible(visible); } @@ -961,14 +1057,33 @@ void MainWindow::drawGraph() } } +void MainWindow::drawFeaturesForest() { + //ui->allViewsWidget->layout()->addWidget(g_graphicsViewFeaturesForest); + QSplitter* allViewsSplitter = new QSplitter; + allViewsSplitter->setOrientation(Qt::Horizontal); + allViewsSplitter->addWidget(g_graphicsView); + allViewsSplitter->addWidget(g_graphicsViewFeaturesForest); + + ui->allViewsWidget->layout()->addWidget(allViewsSplitter); + //setUiState(GRAPH_LOADED); + m_randomForestMainWindow->drawGraph(); + setFeaturesUiState(FEATURES_DRAWN); + + zoomToFitFeatureScene(); + connect(m_randomForestMainWindow->m_scene, SIGNAL(selectionChanged()), this, SLOT(featureSelectionChanged())); + featureSelectionChanged(); + switchFeatureColourScheme(); + //zoomToFitScene(); +} + void MainWindow::graphLayoutFinished() { delete m_fmmm; m_layoutThread = 0; g_assemblyGraph->addGraphicsItemsToScene(m_scene); m_scene->setSceneRectangle(); - if (!g_settings->addNewNodes) - zoomToFitScene(); + + zoomToFitScene(); selectionChanged(); setUiState(GRAPH_DRAWN); @@ -995,7 +1110,9 @@ void MainWindow::resetScene() g_assemblyGraph->m_contiguitySearchDone = false; g_graphicsView->setScene(0); + delete m_scene; + m_scene = new MyGraphicsScene(this); g_graphicsView->setScene(m_scene); @@ -1032,8 +1149,15 @@ void MainWindow::layoutGraph() m_layoutThread = new QThread; double aspectRatio = double(g_graphicsView->width()) / g_graphicsView->height(); - int m_clock = clock(); - g_settings->m_clock = m_clock; + int m_clock; + if (g_settings->m_clock == -1) { + m_clock = clock(); + g_settings->m_clock = m_clock; + } + else { + m_clock = g_settings->m_clock; + } + GraphLayoutWorker * graphLayoutWorker = new GraphLayoutWorker(m_fmmm, g_assemblyGraph->m_graphAttributes, g_assemblyGraph->m_edgeArray, g_settings->graphLayoutQuality, @@ -1127,15 +1251,19 @@ void MainWindow::zoomedWithMouseWheel() void MainWindow::zoomToFitScene() { - zoomToFitRect(m_scene->sceneRect()); + zoomToFitRect(m_scene->sceneRect(), g_graphicsView); } +void MainWindow::zoomToFitFeatureScene() +{ + zoomToFitRect(m_randomForestMainWindow->m_scene->sceneRect(), g_graphicsViewFeaturesForest); +} -void MainWindow::zoomToFitRect(QRectF rect) +void MainWindow::zoomToFitRect(QRectF rect, MyGraphicsView* graphicsView) { - double startingZoom = g_graphicsView->transform().m11(); - g_graphicsView->fitInView(rect, Qt::KeepAspectRatio); - double endingZoom = g_graphicsView->transform().m11(); + double startingZoom = graphicsView->transform().m11(); + graphicsView->fitInView(rect, Qt::KeepAspectRatio); + double endingZoom = graphicsView->transform().m11(); double zoomFactor = endingZoom / startingZoom; g_absoluteZoom *= zoomFactor; double newSpinBoxValue = ui->zoomSpinBox->value() * zoomFactor; @@ -1241,6 +1369,53 @@ void MainWindow::saveSelectedSequencesToFile() } } +void MainWindow::saveTaxInfo() { + QString defaultFileNameAndPath = g_memory->rememberedPath + "/taxInfo.txt"; + QString fullFileName = QFileDialog::getSaveFileName(this, "Save all tax information", defaultFileNameAndPath, "TXT (*.txt)"); + + if (fullFileName != "") //User did not hit cancel + { + g_memory->rememberedPath = QFileInfo(fullFileName).absolutePath(); + QFile file(fullFileName); + file.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream taxInfoOut(&file); + + TaxInfoDialog taxInfoDialog(this); + + taxInfoOut << taxInfoDialog.getCommonTaxInfoInTxt(); + } +} + +void MainWindow::saveHiCTaxInfo() { + + if (ui->taxIdHiCStatLineEdit->text().size() > 0) { + int taxId = ui->taxIdHiCStatLineEdit->text().toInt(); + + QString defaultFileNameAndPath = g_memory->rememberedPath + "/" + QString::number(taxId) + "_taxInfo.txt"; + QString fullFileName = QFileDialog::getSaveFileName(this, "Save all tax information", defaultFileNameAndPath, "TXT (*.txt)"); + + if (fullFileName != "") //User did not hit cancel + { + g_memory->rememberedPath = QFileInfo(fullFileName).absolutePath(); + + TaxInfoDialog taxInfoDialog(this, taxId); + QString text = taxInfoDialog.getHiCTaxInfoInTxt(taxId); + if (text == NULL) { + QMessageBox::information(this, "Save tax information for selected tax", "Cannot find tax with id: " + taxId); + return; + } + + QFile file(fullFileName); + file.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream taxInfoOut(&file); + taxInfoOut << text; + } + } + else { + QMessageBox::information(this, "Save tax information for selected tax", "Tax id wasn't filled\n"); + } +} + void MainWindow::copySelectedPathToClipboard() { std::vector selectedNodes = m_scene->getSelectedNodes(); @@ -1310,6 +1485,39 @@ void MainWindow::switchTaxRank() { g_graphicsView->viewport()->update(); } +void MainWindow::switchFeatureColourScheme() +{ + switch (ui->featuresColoursComboBox->currentIndex()) + { + case 0: + g_settings->featureColourScheme = UNIFORM_COLOURS; + break; + case 1: + g_settings->featureColourScheme = CLASS_COLOURS; + break; + case 2: + g_settings->featureColourScheme = CUSTOM_COLOURS; + break; + case 3: + g_settings->featureColourScheme = BLAST_HITS_SOLID_COLOUR; + break; + case 4: + g_settings->featureColourScheme = BLAST_HITS_CLASS_COLOURS; + g_settings->nodeColourScheme = BLAST_HITS_CLASS_COLOURS; + break; + default: + g_settings->featureColourScheme = UNIFORM_COLOURS; + break; + } + g_assemblyForest->resetAllNodeColours(); + g_graphicsViewFeaturesForest->viewport()->update(); + if (g_settings->nodeColourScheme == BLAST_HITS_CLASS_COLOURS) { + g_assemblyGraph->resetAllNodeColours(); + g_graphicsView->viewport()->update(); + } +} + + void MainWindow::switchColourScheme() { switch (ui->coloursComboBox->currentIndex()) @@ -1360,6 +1568,11 @@ void MainWindow::switchColourScheme() ui->contiguityInfoText->setVisible(false); ui->taxColourComboBox->setVisible(true); break; + case 9: + g_settings->nodeColourScheme = SAVE_COLOURS; + ui->contiguityButton->setVisible(false); + ui->contiguityInfoText->setVisible(false); + break; } g_assemblyGraph->resetAllNodeColours(); @@ -1390,7 +1603,7 @@ void MainWindow::determineContiguityFromSelectedNode() } -QString MainWindow::getDefaultImageFileName() +QString MainWindow::getDefaultGraphImageFileName() { QString fileNameAndPath = g_memory->rememberedPath + "/graph"; @@ -1406,14 +1619,39 @@ QString MainWindow::getDefaultImageFileName() return fileNameAndPath; } - -void MainWindow::saveImageCurrentView() +QString MainWindow::getDefaultFeaturesImageFileName() { - if (!checkForImageSave()) + QString fileNameAndPath = g_memory->rememberedPath + "/features"; + + if (m_imageFilter == "PNG (*.png)") + fileNameAndPath += ".png"; + else if (m_imageFilter == "JPEG (*.jpg)") + fileNameAndPath += ".jpg"; + else if (m_imageFilter == "SVG (*.svg)") + fileNameAndPath += ".svg"; + else + fileNameAndPath += ".png"; + + return fileNameAndPath; +} + +void MainWindow::saveImageGraphCurrentView() { + if (!checkForGraphImageSave()) return; + QString defaultFileNameAndPath = getDefaultGraphImageFileName(); + saveImageCurrentView(defaultFileNameAndPath, g_graphicsView); +} - QString defaultFileNameAndPath = getDefaultImageFileName(); +void MainWindow::saveImageFeaturesCurrentView() { + if (!checkForFeaturesImageSave()) + return; + QString defaultFileNameAndPath = getDefaultFeaturesImageFileName(); + saveImageCurrentView(defaultFileNameAndPath, g_graphicsViewFeaturesForest); +} +void MainWindow::saveImageCurrentView(QString defaultFileNameAndPath, MyGraphicsView* graphicsView) +{ + QString selectedFilter = m_imageFilter; QString fullFileName = QFileDialog::getSaveFileName(this, "Save graph image (current view)", defaultFileNameAndPath, @@ -1433,12 +1671,12 @@ void MainWindow::saveImageCurrentView() QPainter painter; if (pixelImage) { - QImage image(g_graphicsView->viewport()->rect().size(), QImage::Format_ARGB32); + QImage image(graphicsView->viewport()->rect().size(), QImage::Format_ARGB32); image.fill(Qt::white); painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); - g_graphicsView->render(&painter); + graphicsView->render(&painter); image.save(fullFileName); g_memory->rememberedPath = QFileInfo(fullFileName).absolutePath(); painter.end(); @@ -1454,19 +1692,28 @@ void MainWindow::saveImageCurrentView() painter.fillRect(0, 0, size.width(), size.height(), Qt::white); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); - g_graphicsView->render(&painter); + graphicsView->render(&painter); painter.end(); } } } -void MainWindow::saveImageEntireScene() -{ - if (!checkForImageSave()) +void MainWindow::saveImageGraphEntireScene() { + if (!checkForGraphImageSave()) return; + QString defaultFileNameAndPath = getDefaultGraphImageFileName(); + saveImageEntireScene(defaultFileNameAndPath, g_graphicsView, m_scene); +} - QString defaultFileNameAndPath = getDefaultImageFileName(); +void MainWindow::saveImageFeaturesEntireScene() { + if (!checkForFeaturesImageSave()) + return; + QString defaultFileNameAndPath = getDefaultFeaturesImageFileName(); + saveImageEntireScene(defaultFileNameAndPath, g_graphicsViewFeaturesForest, m_randomForestMainWindow->m_scene); +} +void MainWindow::saveImageEntireScene(QString defaultFileNameAndPath, MyGraphicsView* graphicsView, MyGraphicsScene* scene) +{ QString selectedFilter = m_imageFilter; QString fullFileName = QFileDialog::getSaveFileName(this, "Save graph image (entire scene)", @@ -1488,15 +1735,15 @@ void MainWindow::saveImageEntireScene() g_settings->positionTextNodeCentre = true; //Temporarily undo any rotation so labels appear upright. - double rotationBefore = g_graphicsView->getRotation(); - g_graphicsView->undoRotation(); + double rotationBefore = graphicsView->getRotation(); + graphicsView->undoRotation(); m_imageFilter = selectedFilter; QPainter painter; if (pixelImage) { - QSize imageSize = g_absoluteZoom * m_scene->sceneRect().size().toSize(); + QSize imageSize = g_absoluteZoom * scene->sceneRect().size().toSize(); if (imageSize.width() > 32767 || imageSize.height() > 32767) { @@ -1526,8 +1773,8 @@ void MainWindow::saveImageEntireScene() painter.begin(&image); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); - m_scene->setSceneRectangle(); - m_scene->render(&painter); + scene->setSceneRectangle(); + scene->render(&painter); image.save(fullFileName); g_memory->rememberedPath = QFileInfo(fullFileName).absolutePath(); painter.end(); @@ -1536,29 +1783,27 @@ void MainWindow::saveImageEntireScene() { QSvgGenerator generator; generator.setFileName(fullFileName); - QSize size = g_absoluteZoom * m_scene->sceneRect().size().toSize(); + QSize size = g_absoluteZoom * scene->sceneRect().size().toSize(); generator.setSize(size); generator.setViewBox(QRect(0, 0, size.width(), size.height())); painter.begin(&generator); painter.fillRect(0, 0, size.width(), size.height(), Qt::white); painter.setRenderHint(QPainter::Antialiasing); painter.setRenderHint(QPainter::TextAntialiasing); - m_scene->setSceneRectangle(); - m_scene->render(&painter); + scene->setSceneRectangle(); + scene->render(&painter); painter.end(); } g_settings->positionTextNodeCentre = positionTextNodeCentreSettingBefore; - g_graphicsView->setRotation(rotationBefore); + graphicsView->setRotation(rotationBefore); } } - - //This function makes sure that a graph is loaded and drawn so that an image can be saved. //It returns true if everything is fine. If things aren't ready, it displays a message //to the user and returns false. -bool MainWindow::checkForImageSave() +bool MainWindow::checkForGraphImageSave() { if (m_uiState == NO_GRAPH_LOADED) { @@ -1573,6 +1818,20 @@ bool MainWindow::checkForImageSave() return true; } +bool MainWindow::checkForFeaturesImageSave() +{ + if (m_featuresUiState == NO_FEATURES_LOADED) + { + QMessageBox::information(this, "No image to save", "You must first load and then draw a features forest before you can save an image to file."); + return false; + } + if (m_featuresUiState == FEATURES_LOADED) + { + QMessageBox::information(this, "No image to save", "You must first draw the features forest before you can save an image to file."); + return false; + } + return true; +} void MainWindow::setTextDisplaySettings() { @@ -1591,6 +1850,22 @@ void MainWindow::setTextDisplaySettings() g_graphicsView->viewport()->update(); } +void MainWindow::setFeatureTextDisplaySettings() +{ + g_settings->displayFeatureIdLabels = ui->featureIdCheckBox->isChecked(); + g_settings->displayFeatureClassLabels = ui->featureClassCheckBox->isChecked(); + g_settings->displayFeatureCustomLabels = ui->featureCustomCheckBox->isChecked(); + g_settings->displayFeatureClassLikeFigure = ui->featureClassLikeFigureCheckBox->isChecked(); + if (g_settings->displayFeatureClassLikeFigure) { + QString classesInfo = g_assemblyForest->getClassFigureInfo(); + ui->featureClassInfoText->setPlainText(classesInfo); + ui->featureClassInfoText->setEnabled(true); + } + else { + ui->featureClassInfoText->setEnabled(false); + } + g_graphicsViewFeaturesForest->viewport()->update(); +} void MainWindow::fontButtonPressed() { @@ -1605,8 +1880,10 @@ void MainWindow::fontButtonPressed() void MainWindow::setNodeCustomColour() { std::vector selectedNodes = m_scene->getSelectedNodes(); - if (selectedNodes.size() == 0) + if (selectedNodes.size() == 0) { + setFeatureNodeCustomColour(); return; + } QString dialogTitle = "Select custom colour for selected node"; if (selectedNodes.size() > 1) @@ -1635,11 +1912,43 @@ void MainWindow::setNodeCustomColour() } } +void MainWindow::setFeatureNodeCustomColour() +{ + std::vector selectedNodes = m_randomForestMainWindow->m_scene->getSelectedFeatureNodes(); + if (selectedNodes.size() == 0) + return; + + QString dialogTitle = "Select custom colour for selected node"; + if (selectedNodes.size() > 1) + dialogTitle += "s"; + + QColor newColour = QColorDialog::getColor(selectedNodes[0]->getCustomColour(), this, dialogTitle); + if (newColour.isValid()) + { + + //If the colouring scheme is not currently custom, change it to custom now + if (g_settings->nodeColourScheme != CUSTOM_COLOURS) + setNodeColourSchemeComboBox(CUSTOM_COLOURS); + + for (size_t i = 0; i < selectedNodes.size(); ++i) + { + selectedNodes[i]->setCustomColour(newColour); + if (selectedNodes[i]->getGraphicsItemFeatureNode() != 0) { + selectedNodes[i]->getGraphicsItemFeatureNode()->setColour(); + } + + } + g_graphicsViewFeaturesForest->viewport()->update(); + } +} + void MainWindow::setNodeCustomLabel() { std::vector selectedNodes = m_scene->getSelectedNodes(); - if (selectedNodes.size() == 0) + if (selectedNodes.size() == 0) { + setFeatureNodeCustomLabel(); return; + } QString dialogMessage = "Type a custom label for selected node"; if (selectedNodes.size() > 1) @@ -1660,6 +1969,31 @@ void MainWindow::setNodeCustomLabel() } } +void MainWindow::setFeatureNodeCustomLabel() +{ + std::vector selectedNodes = m_randomForestMainWindow->m_scene->getSelectedFeatureNodes(); + if (selectedNodes.size() == 0) + return; + + QString dialogMessage = "Type a custom label for selected node"; + if (selectedNodes.size() > 1) + dialogMessage += "s"; + dialogMessage += ":"; + + bool ok; + QString newLabel = QInputDialog::getText(this, "Custom label", dialogMessage, QLineEdit::Normal, + selectedNodes[0]->getCustomLabel(), &ok); + + if (ok) + { + //If the custom label option isn't currently on, turn it on now. + ui->nodeCustomLabelsCheckBox->setChecked(true); + + for (size_t i = 0; i < selectedNodes.size(); ++i) + selectedNodes[i]->setCustomLabel(newLabel); + } +} + //Takes a vector of nodes and returns a vector of the same nodes, along with //their complements. Does not check for duplicates. @@ -2076,7 +2410,23 @@ void MainWindow::setUiState(UiState uiState) } } - +void MainWindow::setFeaturesUiState(UiState uiState) { + m_featuresUiState = uiState; + switch (uiState) + { + case NO_FEATURES_LOADED: + ui->featureForestWidget->setEnabled(false); + break; + case FEATURES_LOADED: + ui->featureForestWidget->setEnabled(true); + break; + case FEATURES_DRAWN: + ui->featureForestWidget->setEnabled(true); + ui->selectionScrollAreaWidgetContents->setEnabled(true); + ui->actionZoom_to_selection->setEnabled(true); + break; + } +} void MainWindow::showHidePanels() { ui->controlsScrollArea->setVisible(ui->actionControls_panel->isChecked()); @@ -2240,7 +2590,7 @@ void MainWindow::zoomToSelection() boundingBox = boundingBox | selectedItem->boundingRect(); } - zoomToFitRect(boundingBox); + zoomToFitRect(boundingBox, g_graphicsView); } @@ -2370,6 +2720,7 @@ void MainWindow::setNodeColourSchemeComboBox(NodeColourScheme nodeColourScheme) case CUSTOM_COLOURS: ui->coloursComboBox->setCurrentIndex(6); break; case RANDOM_COMPONENT_COLOURS: ui->coloursComboBox->setCurrentIndex(7); break; case COLOUR_BY_TAX: ui->coloursComboBox->setCurrentIndex(8); break; + case SAVE_COLOURS: ui->coloursComboBox->setCurrentIndex(9); break; } } @@ -2471,6 +2822,12 @@ void MainWindow::nodeWidthChanged() g_graphicsView->viewport()->update(); } +void MainWindow::featureNodeWidthChanged() +{ + g_settings->averageFeatureNodeWidth = ui->featureNodeWidthSpinBox->value(); + g_assemblyForest->recalculateAllNodeWidths(); + g_graphicsView->viewport()->update(); +} void MainWindow::saveEntireGraphToFasta() { @@ -2814,3 +3171,25 @@ void MainWindow::openTaxInfoHiCDialog() } } + +void MainWindow::matchSelectedFeatureNodes() { + std::vector selectedNodes = m_randomForestMainWindow->m_scene->getSelectedFeatureNodes(); + + if (selectedNodes.size() == 0) + { + return; + } + if (m_blastFeaturesNodesMatcher == NULL) { + m_blastFeaturesNodesMatcher = new BlastFeaturesNodesMatcher(); + } + for (RandomForestNode* node : selectedNodes) { + + m_blastFeaturesNodesMatcher->matchFeaturesNode(node); + } + ui->coloursComboBox->setCurrentIndex(3); + switchColourScheme(); + ui->featuresColoursComboBox->setCurrentIndex(3 + ); + switchFeatureColourScheme(); + blastChanged(); +} \ No newline at end of file diff --git a/ui/mainwindow.h b/ui/mainwindow.h index 48a0bcfa..f8ca95fa 100644 --- a/ui/mainwindow.h +++ b/ui/mainwindow.h @@ -29,6 +29,8 @@ #include "../program/globals.h" #include #include "../ogdf/energybased/FMMMLayout.h" +#include "../ui/randomforestmainwindow.h" +#include "../blast/BlastFeaturesNodesMatcher.h" class GraphicsViewZoom; class MyGraphicsScene; @@ -51,8 +53,10 @@ class MainWindow : public QMainWindow private: Ui::MainWindow *ui; MyGraphicsScene * m_scene; + MyGraphicsScene * m_featureForestScene; GraphicsViewZoom * m_graphicsViewZoom; + GraphicsViewZoom * m_featuresForestViewZoom; double m_previousZoomSpinBoxValue; QThread * m_layoutThread; ogdf::FMMMLayout * m_fmmm; @@ -60,16 +64,20 @@ class MainWindow : public QMainWindow QString m_fileToLoadOnStartup; bool m_drawGraphAfterLoad; UiState m_uiState; + UiState m_featuresUiState; BlastSearchDialog * m_blastSearchDialog; bool m_alreadyShown; + RandomForestMainWindow* m_randomForestMainWindow; + BlastFeaturesNodesMatcher* m_blastFeaturesNodesMatcher = NULL; void cleanUp(); void displayGraphDetails(); void clearGraphDetails(); void resetScene(); void layoutGraph(); - void zoomToFitRect(QRectF rect); + void zoomToFitRect(QRectF rect, MyGraphicsView* graphicsView); void zoomToFitScene(); + void zoomToFitFeatureScene(); void setZoomSpinBoxStep(); void getSelectedNodeInfo(int & selectedNodeCount, QString & selectedNodeCountText, QString & selectedNodeListText, QString & selectedNodeLengthText, QString &selectedNodeDepthText); void MainWindow::getSelectedNodeTaxInfo(QString& selectedNodeListText); @@ -78,13 +86,16 @@ class MainWindow : public QMainWindow void loadGraph2(GraphFileType graphFileType, QString filename); void setInfoTexts(); void setUiState(UiState uiState); + void setFeaturesUiState(UiState uiState); void selectBasedOnContiguity(ContiguityStatus contiguityStatus); void setWidgetsFromSettings(); - QString getDefaultImageFileName(); + QString getDefaultGraphImageFileName(); + QString getDefaultFeaturesImageFileName(); void setNodeColourSchemeComboBox(NodeColourScheme nodeColourScheme); void setGraphScopeComboBox(GraphScope graphScope); void setupBlastQueryComboBox(); - bool checkForImageSave(); + bool checkForGraphImageSave(); + bool checkForFeaturesImageSave(); QString convertGraphFileTypeToString(GraphFileType graphFileType); void setSelectedNodesWidgetsVisibility(bool visible); void setSelectedEdgesWidgetsVisibility(bool visible); @@ -105,6 +116,7 @@ private slots: void loadCSV(QString fullFileName = ""); void loadHiC(QString fullFileName = ""); void loadTax(QString fullFileName = ""); + void loadFeaturesForest(QString fullFileName = ""); void selectionChanged(); void graphScopeChanged(); void drawGraph(); @@ -118,8 +130,12 @@ private slots: void saveSelectedPathToFile(); void switchColourScheme(); void determineContiguityFromSelectedNode(); - void saveImageCurrentView(); - void saveImageEntireScene(); + void saveImageCurrentView(QString defaultFileNameAndPath, MyGraphicsView* graphicsView); + void saveImageGraphCurrentView(); + void saveImageFeaturesCurrentView(); + void saveImageEntireScene(QString defaultFileNameAndPath, MyGraphicsView* graphicsView, MyGraphicsScene* scene); + void saveImageGraphEntireScene(); + void saveImageFeaturesEntireScene(); void setTextDisplaySettings(); void fontButtonPressed(); void setNodeCustomColour(); @@ -150,6 +166,7 @@ private slots: void startingNodesExactMatchChanged(); void openPathSpecifyDialog(); void nodeWidthChanged(); + void featureNodeWidthChanged(); void saveEntireGraphToFasta(); void saveEntireGraphToFastaOnlyPositiveNodes(); void saveEntireGraphToGfa(); @@ -168,6 +185,15 @@ private slots: void openTaxInfoDialog(); void openTaxInfoHiCDialog(); void unzipSelectedNodes(); + void saveTaxInfo(); + void saveHiCTaxInfo(); + void drawFeaturesForest(); + void featureSelectionChanged(); + void matchSelectedFeatureNodes(); + void switchFeatureColourScheme(); + void setFeatureNodeCustomColour(); + void setFeatureNodeCustomLabel(); + void setFeatureTextDisplaySettings(); protected: void showEvent(QShowEvent *ev); diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index e73e19dc..2f13a009 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 1467 - 924 + 1465 + 843 @@ -18,10 +18,7 @@ :/icons/icon.png:/icons/icon.png - - - 0 - + 0 @@ -34,33 +31,24 @@ 0 - - + + 0 0 - - - 0 - 0 - - - - Qt::ScrollBarAsNeeded - true - + 0 0 - 335 - 1392 + 461 + 1034 @@ -69,13 +57,39 @@ 0 - + - + + + + 0 + 0 + + + + + 75 + true + + + + Find nodes + + + + + + + Qt::Horizontal + + + + + true - + 0 @@ -85,29 +99,56 @@ 0 - - - - Tax info + + 0 + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + - - - - Qt::Horizontal + + + + Exact + + + true - - + + + + true + + + + 0 + 0 + + - Tax Id + Node(s): - - + + 0 @@ -122,105 +163,33 @@ - - - - - + + + + true + - More info + Match: - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Edges: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 0 - - - - - - - Total length: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 0 - - - - - - - 0 - - - - - - - Nodes: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - 75 - true - - + + - Graph information + Partial - - - - Tax info (with Hi-C links) + + + + true + + + + 0 + 0 + @@ -228,27 +197,15 @@ - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - + + + Find node(s) - + - - - true - - + + 0 @@ -262,28 +219,401 @@ 0 - - - - 75 - true - + + + Qt::Vertical - - Graph drawing + + QSizePolicy::Fixed - + + + 20 + 60 + + +
- - + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Selected nodes + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + true + + + + + + + + 0 + 0 + + + + Total length: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + Mean depth: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Ctrl+L + + + Set label + + + + + + + Ctrl+O + + + Set colour + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 60 + + + + + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Selected edges + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + true + + + + + + + + 75 + true + + + + Taxonometry for selected nodes + + + + + + + Qt::Horizontal + + + + + + + + + + Unzip selected nodes + + + + + + + Connect fetaure node(s) with de bruijn nodes + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 87 + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::ScrollBarAsNeeded + + + true + + + + + 0 + -1032 + 461 + 1822 + + + + + 0 + 0 + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + + + Tax info + + + + + Qt::Horizontal - - - + + + + Tax Id + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + + + More info + + + + + + 0 @@ -293,222 +623,173 @@ 0 - - - - Draw graph - - - - - - - Tax id: - - - - - - - - 0 - 0 - + + 0 + + + 0 + + + + + Edges: - - - 16 - 16 - + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - true - - - - 0 - 0 - + + + + 0 - - - - - 0 - 0 - + + + + Total length: - Qt::AlignCenter - - - - - - 1 - - - 0.000000000000000 - - - 1000.000000000000000 - - - 1.000000000000000 - - - 1.000000000000000 + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Exact - - - true - - - - - - - - 0 - 0 - - - - Partial - - - - - - - - - - true - - - - 0 - 0 - - - - - 16 - 16 - + + + + 0 - - - - true - - - - 0 - 0 - - - - Qt::AlignCenter - - - 10000 + + + + 0 - - - - true - + + - Distance: + Nodes: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 0 - - - - Style: + + + + + + + + 75 + true + + + + Graph information + + + + + + + Tax info (with Hi-C links) + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Graph drawing + + + + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 0 + + + + + Qt::AlignCenter - - - - - - - 0 - 0 - + + 1 - - - 16 - 16 - + + 1000000.000000000000000 - - - - - 0 - 0 - - - - Min: + + + + true - - - - 0 @@ -523,154 +804,49 @@ - - - - - 0 - 0 - - - - - 16 - 16 - + + + + Around target nodes - - + + - Zip graph + Draw graph - - + + true - - 0 - 0 - - - - Match: - - - - - - - + 0 0 - - Qt::AlignCenter - - - - - - 1 - - - 0.000000000000000 - - - 1000000.000000000000000 - - - 10.000000000000000 - - - 100.000000000000000 - - - - - - 0 - 0 - - + + - Scope: + Only big component - - + + - With Distance + Zip graph - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - Single - - - true - - - - - - - - 0 - 0 - - - - Double - - - - - - - - - - - - - true - + + 0 @@ -685,13 +861,6 @@ - - - - Around target nodes - - - @@ -727,21 +896,34 @@ - - + + - + 0 0 + + + 16 + 16 + + + + + + - Max: + HiC min weight: + + + Qt::AlignLeading - - + + Qt::AlignCenter @@ -753,295 +935,225 @@ - - - - true - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Single + + + true + + + + + + + + 0 + 0 + + + + Double + + + + + + + + - + 0 0 - - Node(s): + + + 16 + 16 + - - - - Qt::AlignCenter - - - 1 - - - 1000000.000000000000000 - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Exact + + + true + + + + + + + + 0 + 0 + + + + Partial + + + + - - + + - HiC min sequence length: - - - Qt::AlignLeading + Tax id: - - + + + + true + - + 0 0 - - - 16 - 16 - + + Match: - - + + - + 0 0 - - - 16 - 16 - + + Scope: - - + + true - + 0 0 - - - 16 - 16 - + + Qt::AlignCenter - - - - - 1000 - - - 0 + 10000 - - + + - HiC min weight: - - - Qt::AlignLeading + With Distance - - - - With Hi-C + + + + true - - - - - + 0 0 - - - 16 - 16 - - - - - - - Only big component + Node(s): - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 229 - 15 - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 75 - true - - - - Graph display - - - - - - - Qt::Horizontal - - - - - - - - 0 - - - 0 - - - 0 - - - + + + - + 0 0 - - Qt::AlignCenter - - - % - - - 1 - - - 5.000000000000000 - - - 500.000000000000000 - - - 5.000000000000000 - - - 100.000000000000000 + + + 16 + 16 + - - - - - Domain - - - - - Kingdom - - - - - Phylum - - - - - Class - - - - - Order - - - - - Family - - - - - Genus - - - - - Species - - + + + + With Hi-C + - - + + 0 @@ -1058,31 +1170,37 @@ 1 - 0.500000000000000 + 0.000000000000000 1000.000000000000000 - 0.500000000000000 + 1.000000000000000 - 5.000000000000000 + 1.000000000000000 - - - - Node width: + + + + true - - Qt::AlignLeading + + Distance: + + + - + + + true + 0 @@ -1097,74 +1215,60 @@ - - - - Determine contiguity + + + + + 0 + 0 + + + + + 16 + 16 + - - - - Zoom: + + + + 1000 - - Qt::AlignLeading + + 0 - - - - - Random colours - - - - - Uniform colour - - - - - Colour by depth - - - - - BLAST hits (solid) - - - - - BLAST hits (rainbow) - - - - - Colour by contiguity - - - - - Custom colours - - - - - Random component colours - - - - - Colour by tax - - + + + + + 0 + 0 + + + + Min: + - - + + + + + 0 + 0 + + + + Style: + + + + + 0 @@ -1177,17 +1281,64 @@ 16 - - - - - - Tax Id (with rank) + + + + + + + 0 + 0 + + + + Max: + + + + + + + HiC min sequence length: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.000000000000000 + + + 1000000.000000000000000 + + + 10.000000000000000 + + + 100.000000000000000 - - + + 0 @@ -1202,8 +1353,11 @@ - - + + + + true + 0 @@ -1218,13 +1372,6 @@ - - - - Tax Name (with rank) - - - @@ -1232,7 +1379,7 @@ - + Qt::Vertical @@ -1241,18 +1388,18 @@ - 20 + 229 15 - + true - + 0 @@ -1266,7 +1413,7 @@ 0 - + 75 @@ -1274,20 +1421,20 @@ - Node labels + Graph display - + Qt::Horizontal - - + + 0 @@ -1297,203 +1444,107 @@ 0 - - - - - 0 - 0 - + + + + Tax Name (with rank) - - - 16 - 16 - + + + + + + Determine contiguity - - + + - + 0 0 - - - 16 - 16 - + + Qt::AlignCenter - - - - - - Font + + - - - - - - Text outline + + 1 - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Custom - - - - - - - Name - - - - - - - Length - - - - - - - Depth - - - - - - - BLAST hits - - - - - - - false - - - - - - - CSV data: - - - - - - - Tax Name (id) - - - - - - - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 229 - 15 - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 75 - true - - - - BLAST - - - - - - - Qt::Horizontal - - - - - - - - 0 - - - 0 - - - 0 - + + 0.500000000000000 + + + 1000.000000000000000 + + + 0.500000000000000 + + + 5.000000000000000 + + + + + + + + Random colours + + + + + Uniform colour + + + + + Colour by depth + + + + + BLAST hits (solid) + + + + + BLAST hits (rainbow) + + + + + Colour by contiguity + + + + + Custom colours + + + + + Random component colours + + + + + Colour by tax + + + + + Save colours + + + + - + 0 @@ -1509,17 +1560,24 @@ - + - Query: + Node width: - - true + + Qt::AlignLeading - - + + + + Tax Id (with rank) + + + + + 0 @@ -1534,143 +1592,151 @@ - - - - false - - - - 0 - 0 - - + + - none + Domain + + + + + Kingdom + + + + + Phylum + + + + + Class + + + + + Order + + + + + Family + + + + + Genus + + + + + Species - - - - Create/view BLAST search + + + + + 0 + 0 + + + + + 16 + 16 + - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - 0 - 0 - - - - true - - - - - 0 - -149 - 278 - 999 - - - - - 0 - 0 - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Find nodes - + + + + + 0 + 0 + + + + Qt::AlignCenter + + + % + + + 1 + + + 5.000000000000000 + + + 500.000000000000000 + + + 5.000000000000000 + + + 100.000000000000000 + + + + + + + Zoom: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + - + - Qt::Horizontal + Qt::Vertical - + + QSizePolicy::Fixed + + + + 20 + 15 + + + - + true - + 0 @@ -1683,110 +1749,189 @@ 0 - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - Exact - - - true - - - - - - - true - - - - 0 - 0 - - - - Node(s): - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - true + + + + + 75 + true + - Match: + Node labels - - - - Partial + + + + Qt::Horizontal - - - - true - - - - 0 - 0 - - + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Font + + + + + + + Text outline + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Custom + + + + + + + Name + + + + + + + Length + + + + + + + Depth + + + + + + + BLAST hits + + + + + + + false + + + + + + + CSV data: + + + + + + + Tax Name (id) + + + + + + + - - - Find node(s) + + + Qt::Vertical - + + QSizePolicy::Fixed + + + + 229 + 15 + + + - - + + + true + + 0 @@ -1800,119 +1945,209 @@ 0 - - - Qt::Vertical + + + + 75 + true + - - QSizePolicy::Fixed + + BLAST - - - 20 - 60 - + + + + + + Qt::Horizontal - + + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Query: + + + true + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + false + + + + 0 + 0 + + + + + none + + + + + + + + Create/view BLAST search + + + + + - - - - 0 - 0 - - - - - 75 - true - - - - Selected nodes - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - true - - - - - - - - 0 - 0 - - - - Total length: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - Mean depth: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - + + + + + + Zoom: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + % + + + 1 + + + 5.000000000000000 + + + 500.000000000000000 + + + 5.000000000000000 + + + 100.000000000000000 + + + + + + + + Uniform colour + + + + + Class colours + + + + + Custom colours + + + + + BLAST hits (solid) + + + + + BLAST hits (class colours) + + + + + + + + Node width: + + + Qt::AlignLeading + + + + + + + Qt::Horizontal + + + + + + + Custom + + + + + 0 @@ -1927,141 +2162,157 @@ - - - - Ctrl+L + + + + Draw features + + + + - Set label + Class like figure - - - - Ctrl+O + + + + Feature Id + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Class + + + + + + + + 75 + true + + + + 1 - Set colour + Features display + + + false - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical + + + + + 0 + 0 + - - QSizePolicy::Fixed + + Qt::AlignCenter - + + + + + 1 + + + 0.500000000000000 + + + 1000.000000000000000 + + + 0.500000000000000 + + + 5.000000000000000 + + + + + + + Feature labels + + + + + + + + 0 + 0 + + + - 20 - 60 + 16 + 16 - + + + + + + + 0 + 0 + + + + + 394 + 45 + + + + + 394 + 45 + + + - - - - 0 - 0 - - - - - 75 - true - - - - Selected edges - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - true - - - - - - - - 75 - true - - - - Taxonometry for selected nodes - - - - - - - Qt::Horizontal - - - - - - - - - - Unzip selected nodes - - - - - + Qt::Vertical 20 - 40 + 0 @@ -2077,7 +2328,7 @@ 0 0 - 1467 + 1465 26 @@ -2088,13 +2339,16 @@ - - + + + + + @@ -2160,6 +2414,9 @@ + + + @@ -2196,7 +2453,7 @@ Ctrl+O - + true @@ -2205,7 +2462,7 @@ :/icons/save-256.png:/icons/save-256.png - Save image (current view) + Save image (graph current view) @@ -2235,7 +2492,7 @@ About - + true @@ -2244,7 +2501,7 @@ :/icons/save-256.png:/icons/save-256.png - Save image (entire scene) + Save image (graph entire scene) @@ -2550,6 +2807,51 @@ Load Taxonometry + + + + :/icons/save-256.png:/icons/save-256.png + + + Save common tax information + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save tax info with Hi-C links for selected tax id + + + + + + :/icons/load-256.png:/icons/load-256.png + + + Load features forest + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save image (features current view) + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save image (features entire scene) + + diff --git a/ui/mygraphicsscene.cpp b/ui/mygraphicsscene.cpp index 1ba57544..6d824b15 100644 --- a/ui/mygraphicsscene.cpp +++ b/ui/mygraphicsscene.cpp @@ -22,6 +22,9 @@ #include "../graph/graphicsitemnode.h" #include "../graph/graphicsitemedge.h" #include "../graph/debruijnnode.h" +#include "../random_forest/RandomForestNode.h" +#include "../random_forest/GraphicsItemFeatureNode.h" +#include "../painting/CommonGraphicsItemNode.h" MyGraphicsScene::MyGraphicsScene(QObject *parent) : QGraphicsScene(parent) @@ -70,6 +73,25 @@ std::vector MyGraphicsScene::getSelectedNodes() return returnVector; } +//This function returns all of the selected nodes, sorted by their node number. +std::vector MyGraphicsScene::getSelectedFeatureNodes() +{ + std::vector returnVector; + + QList selection = selectedItems(); + for (int i = 0; i < selection.size(); ++i) + { + QGraphicsItem* selectedItem = selection[i]; + GraphicsItemFeatureNode* selectedNodeItem = dynamic_cast(selectedItem); + if (selectedNodeItem != 0) + returnVector.push_back(selectedNodeItem->m_featureNode); + } + + //std::sort(returnVector.begin(), returnVector.end(), compareNodePointers); + + return returnVector; +} + //This function works like getSelectedNodes, but only positive nodes are //returned. If a negative node is selected, its positive complement is in the @@ -123,6 +145,23 @@ std::vector MyGraphicsScene::getSelectedGraphicsItemNodes() } +//This function returns all of the selected nodes, sorted by their node number. +std::vector MyGraphicsScene::getSelectedGraphicsItemFeatureNode() +{ + std::vector returnVector; + + QList selection = selectedItems(); + for (int i = 0; i < selection.size(); ++i) + { + QGraphicsItem* selectedItem = selection[i]; + GraphicsItemFeatureNode* selectedNodeItem = dynamic_cast(selectedItem); + if (selectedNodeItem != 0) + returnVector.push_back(selectedNodeItem); + } + + return returnVector; +} + std::vector MyGraphicsScene::getSelectedEdges() { std::vector returnVector; @@ -235,14 +274,14 @@ void MyGraphicsScene::setSceneRectangle() //After the user drags nodes, it may be necessary to expand the scene rectangle //if the nodes were moved out of the existing rectangle. -void MyGraphicsScene::possiblyExpandSceneRectangle(std::vector * movedNodes) +void MyGraphicsScene::possiblyExpandSceneRectangle(std::vector * movedNodes) { QRectF currentSceneRect = sceneRect(); QRectF newSceneRect = currentSceneRect; for (size_t i = 0; i < movedNodes->size(); ++i) { - GraphicsItemNode * node = (*movedNodes)[i]; + GraphicsItemNode* node = (*movedNodes)[i]; QRectF nodeRect = node->boundingRect(); newSceneRect = newSceneRect.united(nodeRect); } @@ -251,3 +290,20 @@ void MyGraphicsScene::possiblyExpandSceneRectangle(std::vector* movedNodes) +{ + QRectF currentSceneRect = sceneRect(); + QRectF newSceneRect = currentSceneRect; + + for (size_t i = 0; i < movedNodes->size(); ++i) + { + GraphicsItemFeatureNode* node = (*movedNodes)[i]; + QRectF nodeRect = node->boundingRect(); + newSceneRect = newSceneRect.united(nodeRect); + } + + if (newSceneRect != currentSceneRect) + setSceneRect(newSceneRect); +} \ No newline at end of file diff --git a/ui/mygraphicsscene.h b/ui/mygraphicsscene.h index 81155961..ff26d482 100644 --- a/ui/mygraphicsscene.h +++ b/ui/mygraphicsscene.h @@ -23,8 +23,11 @@ #include class DeBruijnNode; +class RandomForestNode; class DeBruijnEdge; class GraphicsItemNode; +class GraphicsItemFeatureNode; +class CommonGraphicsItemNode; class MyGraphicsScene : public QGraphicsScene { @@ -32,6 +35,7 @@ class MyGraphicsScene : public QGraphicsScene public: explicit MyGraphicsScene(QObject *parent = 0); std::vector getSelectedNodes(); + std::vector getSelectedFeatureNodes(); std::vector getSelectedPositiveNodes(); std::vector getSelectedGraphicsItemNodes(); std::vector getSelectedEdges(); @@ -40,7 +44,9 @@ class MyGraphicsScene : public QGraphicsScene DeBruijnNode * getOnePositiveSelectedNode(); double getTopZValue(); void setSceneRectangle(); - void possiblyExpandSceneRectangle(std::vector * movedNodes); + void possiblyExpandSceneRectangle(std::vector * movedNodes); + void possiblyExpandSceneRectangle(std::vector * movedNodes); + std::vector getSelectedGraphicsItemFeatureNode(); }; diff --git a/ui/mygraphicsview.cpp b/ui/mygraphicsview.cpp index 78f033b6..9266a0bd 100644 --- a/ui/mygraphicsview.cpp +++ b/ui/mygraphicsview.cpp @@ -61,6 +61,7 @@ void MyGraphicsView::mouseMoveEvent(QMouseEvent * event) //If the user drags the right mouse button while holding control, //the view rotates. bool rightButtonDown = event->buttons() & Qt::RightButton; + g_settings->roundMode = false; if (event->modifiers() == Qt::CTRL && rightButtonDown) { QPointF viewCentre(width() / 2.0, height() / 2.0); @@ -74,8 +75,12 @@ void MyGraphicsView::mouseMoveEvent(QMouseEvent * event) g_settings->nodeDragging = NO_DRAGGING; } - else + else if (rightButtonDown) { + g_settings->roundMode = true; + QGraphicsView::mouseMoveEvent(event); + } + else { QGraphicsView::mouseMoveEvent(event); } } diff --git a/ui/randomforestmainwindow.cpp b/ui/randomforestmainwindow.cpp new file mode 100644 index 00000000..f2862130 --- /dev/null +++ b/ui/randomforestmainwindow.cpp @@ -0,0 +1,180 @@ +//Copyright 2017 Ryan Wick + +//This file is part of Bandage + +//Bandage 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 3 of the License, or +//(at your option) any later version. + +//Bandage 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 Bandage. If not, see . + + +#include "randomforestmainwindow.h" +#include "ui_randomforestmainwindow.h" +#include +#include +#include +#include +#include +#include +#include "../program/settings.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "settingsdialog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "aboutdialog.h" +#include +#include "blastsearchdialog.h" +#include "../graph/assemblygraph.h" +#include "mygraphicsview.h" +#include "graphicsviewzoom.h" +#include "mygraphicsscene.h" +#include "../blast/blastsearch.h" +#include "../graph/debruijnnode.h" +#include "../graph/debruijnedge.h" +#include "../graph/graphicsitemnode.h" +#include "../graph/graphicsitemedge.h" +#include "myprogressdialog.h" +#include +#include +#include +#include "../graph/path.h" +#include "pathspecifydialog.h" +#include "../program/memory.h" +#include "changenodenamedialog.h" +#include "changenodedepthdialog.h" +#include +#include "graphinfodialog.h" +#include "taxinfodialog.h" +#include +#include +#include "../random_forest/assemblyforest.h" +#include "../random_forest/RandomForestNode.h" +#include "../random_forest/GraphicsItemFeatureNode.h" +#include "../ogdf/tree/TreeLayout.h" +#include "../program/TreeLayoutWorker.h" + +RandomForestMainWindow::RandomForestMainWindow() +{ + m_graphicsViewZoom = new GraphicsViewZoom(g_graphicsViewFeaturesForest); + g_graphicsViewFeaturesForest->m_zoom = m_graphicsViewZoom; + + m_scene = new MyGraphicsScene(this); + g_graphicsViewFeaturesForest->setScene(m_scene); +} + +RandomForestMainWindow::~RandomForestMainWindow() +{ + delete m_graphicsViewZoom; +} + +void RandomForestMainWindow::getSelectedNodeInfo(int & selectedNodeCount, QString & selectedFeatureNodeText) +{ + std::vector selectedNodes = m_scene->getSelectedFeatureNodes(); + + selectedNodeCount = int(selectedNodes.size()); + + for (int i = 0; i < selectedNodeCount; ++i) + { + QString nodeName = selectedNodes[i]->getName(); + + selectedFeatureNodeText += nodeName; + selectedFeatureNodeText += '\n'; + if (selectedNodes[i]->getFeatureName() != NULL) { + QString threshold = QString::number(selectedNodes[i]->getThreshold(), 'g', 2); + selectedFeatureNodeText += "Feature_" + selectedNodes[i]->getFeatureName() + " <= " + threshold + "\n"; + } + if (selectedNodes[i]->getClass() != NULL) { + selectedFeatureNodeText += "Class: " + selectedNodes[i]->getClass() + "\n"; + } + for (QString seq : selectedNodes[i]->getQuerySequences()) { + selectedFeatureNodeText += "Seq: "; + selectedFeatureNodeText += seq; + selectedFeatureNodeText += "\n"; + + } + } +} + +void RandomForestMainWindow::drawGraph() +{ + resetScene(); + + g_assemblyForest->buildOgdfGraphFromNodesAndEdges(); + layoutGraph(); +} + +void RandomForestMainWindow::graphLayoutFinished() +{ + delete m_layout; + m_layoutThread = 0; + g_assemblyForest->addGraphicsItemsToScene(m_scene); + m_scene->setSceneRectangle(); + g_graphicsViewFeaturesForest->setFocus(); + g_settings->addNewNodes = false; +} + +void RandomForestMainWindow::graphLayoutCancelled() +{ +} + +void RandomForestMainWindow::resetScene() +{ + m_scene->blockSignals(true); + + g_graphicsViewFeaturesForest->setScene(0); + delete m_scene; + m_scene = new MyGraphicsScene(this); + + g_graphicsViewFeaturesForest->setScene(m_scene); + + g_graphicsViewFeaturesForest->undoRotation(); +} + +void RandomForestMainWindow::layoutGraph() +{ + m_layout = new ogdf::TreeLayout(); + + m_layoutThread = new QThread; + double aspectRatio = double(g_graphicsViewFeaturesForest->width()) / g_graphicsViewFeaturesForest->height(); + + int m_clock; + if (g_settings->m_clock == -1) { + m_clock = clock(); + g_settings->m_clock = m_clock; + } + else { + m_clock = g_settings->m_clock; + } + + TreeLayoutWorker* layoutWorker = new TreeLayoutWorker(m_layout, g_assemblyForest->m_graphAttributes, + g_assemblyForest->m_edgeArray); + layoutWorker->moveToThread(m_layoutThread); + + connect(m_layoutThread, SIGNAL(started()), layoutWorker, SLOT(layoutGraph())); + connect(layoutWorker, SIGNAL(finishedLayout()), m_layoutThread, SLOT(quit())); + connect(layoutWorker, SIGNAL(finishedLayout()), layoutWorker, SLOT(deleteLater())); + connect(layoutWorker, SIGNAL(finishedLayout()), this, SLOT(graphLayoutFinished())); + connect(m_layoutThread, SIGNAL(finished()), m_layoutThread, SLOT(deleteLater())); + m_layoutThread->start(); +} \ No newline at end of file diff --git a/ui/randomforestmainwindow.h b/ui/randomforestmainwindow.h new file mode 100644 index 00000000..4b183f42 --- /dev/null +++ b/ui/randomforestmainwindow.h @@ -0,0 +1,130 @@ +//Copyright 2017 Ryan Wick + +//This file is part of Bandage + +//Bandage 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 3 of the License, or +//(at your option) any later version. + +//Bandage 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 Bandage. If not, see . + + +#ifndef RANDOMFORESTMAINWINDOW_H +#define RANDOMFORESTMAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../program/globals.h" +#include +#include "../ogdf/tree/TreeLayout.h" +#include "mainwindow.h" + +class GraphicsViewZoom; +class MyGraphicsScene; +class RandomForestNode; +class DeBruijnNode; + +class RandomForestMainWindow : QObject +{ + Q_OBJECT + +public: + explicit RandomForestMainWindow(); + ~RandomForestMainWindow(); + + void drawGraph(); + + //Ui::MainWindow* m_mainWindowUi = NULL; + //Ui::MainWindow* ui; + MyGraphicsScene * m_scene; + + GraphicsViewZoom * m_graphicsViewZoom; + double m_previousZoomSpinBoxValue; + QThread * m_layoutThread; + ogdf::TreeLayout* m_layout; + QString m_imageFilter; + QString m_fileToLoadOnStartup; + bool m_drawGraphAfterLoad; + UiState m_uiState; + bool m_alreadyShown; + + //void cleanUp(); + // void displayGraphDetails(); + // void clearGraphDetails(); + void resetScene(); + void layoutGraph(); + // void zoomToFitRect(QRectF rect); + // void zoomToFitScene(); + // void setZoomSpinBoxStep(); + void getSelectedNodeInfo(int & selectedNodeCount, QString & selectedFeatureNodeText); + + //QString getSelectedEdgeListText(); + //std::vector getNodesFromLineEdit(QLineEdit * lineEdit, bool exactMatch, std::vector * nodesNotInGraph = 0); + // + //void setInfoTexts(); + //void setUiState(UiState uiState); + //void setWidgetsFromSettings(); + //QString getDefaultImageFileName(); + //void setNodeColourSchemeComboBox(NodeColourScheme nodeColourScheme); + //void setGraphScopeComboBox(GraphScope graphScope); + //bool checkForImageSave(); + + //void setSelectedNodesWidgetsVisibility(bool visible); + //void setSelectedEdgesWidgetsVisibility(bool visible); + //void setStartingNodesWidgetVisibility(bool visible); + //void setNodeDistanceWidgetVisibility(bool visible); + + //std::vector addComplementaryNodes(std::vector nodes); + +private slots: +// void selectionChanged(); +// void graphScopeChanged(); +// +// void zoomSpinBoxChanged(); +// void zoomedWithMouseWheel(); +// void switchColourScheme(); +// void saveImageCurrentView(); +// void saveImageEntireScene(); +// void setTextDisplaySettings(); +// void fontButtonPressed(); +// void setNodeCustomColour(); +// void setNodeCustomLabel(); +// void hideNodes(); +// void openSettingsDialog(); +// void openAboutDialog(); +// void selectUserSpecifiedNodes(); + void graphLayoutFinished(); +// void showHidePanels(); + void graphLayoutCancelled(); +// void bringSelectedNodesToFront(); +// void selectAll(); +// void selectNone(); +// void invertSelection(); +// void zoomToSelection(); +// void openBandageUrl(); +// void afterMainWindowShow(); +// void nodeWidthChanged(); +// void removeSelection(); +// void duplicateSelectedNodes(); +// void mergeSelectedNodes(); +// void changeNodeName(); + +signals: + void windowLoaded(); +}; + +#endif // RANDOMFORESTMAINWINDOW_H diff --git a/ui/randomforestmainwindow.ui b/ui/randomforestmainwindow.ui new file mode 100644 index 00000000..2646fda3 --- /dev/null +++ b/ui/randomforestmainwindow.ui @@ -0,0 +1,1968 @@ + + + RandomForestMainWindow + + + + 0 + 0 + 1069 + 684 + + + + Bandage + + + + :/icons/icon.png:/icons/icon.png + + + + + 0 + 0 + 791 + 681 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + Qt::ScrollBarAsNeeded + + + true + + + + + 0 + 0 + 368 + 731 + + + + + 0 + 0 + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Nodes: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 0 + + + + + + + Trees: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 0 + + + + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 75 + true + + + + Graph information + + + + + + + More info + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Graph drawing + + + + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Draw graph + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + true + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + Scope: + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + Entire graph + + + + + Around nodes + + + + + + + + true + + + + 0 + 0 + + + + Node(s): + + + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 229 + 15 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Graph display + + + + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 0 + + + + + + Random colours + + + + + Uniform colour + + + + + Colour by depth + + + + + Custom colours + + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + % + + + 1 + + + 5.000000000000000 + + + 500.000000000000000 + + + 5.000000000000000 + + + 100.000000000000000 + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 0.500000000000000 + + + 1000.000000000000000 + + + 0.500000000000000 + + + 5.000000000000000 + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Node width: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Zoom: + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Node labels + + + + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Font + + + + + + + Text outline + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Name + + + + + + + Depth + + + + + + + CSV data: + + + + + + + Custom + + + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 229 + 15 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + + + 0 + 0 + 1069 + 26 + + + + + File + + + + + + + + + + + + + + + + Tools + + + + + + View + + + + + + + Select + + + + Select nodes based on contiguity + + + + :/icons/contiguity.png:/icons/contiguity.png + + + + + + + + + + + + + + + + + + Help + + + + + + + Output + + + + + + + + + + + + + + + + + + + + + + Edit + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + 3 + 21 + + + + + + + 790 + 0 + 280 + 691 + + + + + 0 + 0 + + + + true + + + + + 0 + 0 + 257 + 837 + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Find nodes + + + + + + + Qt::Horizontal + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Exact + + + true + + + + + + + true + + + + 0 + 0 + + + + Node(s): + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + true + + + Match: + + + + + + + Partial + + + + + + + true + + + + 0 + 0 + + + + + + + + + + + Find node(s) + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 60 + + + + + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Selected nodes + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + true + + + + + + + + 0 + 0 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Ctrl+L + + + Set label + + + + + + + Ctrl+O + + + Set colour + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 60 + + + + + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Selected edges + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + :/icons/load-256.png:/icons/load-256.png + + + Load graph + + + Ctrl+O + + + + + true + + + + :/icons/save-256.png:/icons/save-256.png + + + Save image (current view) + + + + + + :/icons/exit-256.png:/icons/exit-256.png + + + Exit + + + + + + :/icons/settings-256.png:/icons/settings-256.png + + + Settings + + + + + + :/icons/information-256.png:/icons/information-256.png + + + About + + + + + true + + + + :/icons/save-256.png:/icons/save-256.png + + + Save image (entire scene) + + + + + true + + + true + + + Controls panel + + + + + true + + + true + + + Selection panel + + + + + + :/icons/bring-to-front.png:/icons/bring-to-front.png + + + Bring selected nodes to front + + + Bring selected nodes to front + + + + + + :/icons/BLAST.png:/icons/BLAST.png + + + Select nodes with BLAST hits + + + Select nodes with BLAST hits + + + + + + :/icons/select-all.png:/icons/select-all.png + + + Select all + + + + + + :/icons/invert-selection.png:/icons/invert-selection.png + + + Invert selection + + + + + + :/icons/copy.png:/icons/copy.png + + + Copy selected node sequences to clipboard + + + Copy selected node sequences to clipboard + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save selected node sequences to FASTA + + + Save selected node sequences to FASTA + + + + + + :/icons/select-none.png:/icons/select-none.png + + + Select none + + + + + + :/icons/zoom.png:/icons/zoom.png + + + Zoom to fit selection + + + + + + :/icons/contiguity-maybe_contiguous.png:/icons/contiguity-maybe_contiguous.png + + + Select possibly contiguous nodes + + + + + + :/icons/contiguity-contiguous.png:/icons/contiguity-contiguous.png + + + Select contiguous nodes + + + + + + :/icons/contiguity-not_contiguous.png:/icons/contiguity-not_contiguous.png + + + Select not contiguous nodes + + + + + + :/icons/icon.png:/icons/icon.png + + + Bandage online help + + + Bandage online help + + + + + + :/icons/copy.png:/icons/copy.png + + + Copy selected path sequence to clipboard + + + Copy selected path sequence to clipboard + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save selected path sequence to FASTA + + + Save selected path sequence to FASTA + + + + + + :/icons/specify-path.png:/icons/specify-path.png + + + Specify exact path for copy/save + + + + + + :/icons/load-256.png:/icons/load-256.png + + + Load CSV data + + + Load CSV label data + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save entire graph to FASTA (both positive and negative nodes) + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save entire graph to FASTA (only positive nodes) + + + + + + :/icons/ncbi-256.png:/icons/ncbi-256.png + + + Web BLAST selected nodes + + + + + Hide selected nodes + + + Del + + + + + Remove selection from graph + + + Shift+Del + + + + + Duplicate selected nodes + + + Ctrl+D + + + + + Merge selected nodes + + + Ctrl+M + + + + + Merge all possible nodes + + + Ctrl+Shift+M + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save entire graph to GFA + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save visible graph to GFA + + + + + Change node name + + + + + Change node depth + + + + + + :/icons/load-256.png:/icons/load-256.png + + + Load HiC data + + + + + + :/icons/load-256.png:/icons/load-256.png + + + Load Taxonometry + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save common tax information + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save tax info with Hi-C links for selected tax id + + + + + + :/icons/load-256.png:/icons/load-256.png + + + Load features forest + + + + + + + InfoTextWidget + QWidget +
infotextwidget.h
+ 1 +
+ + VerticalScrollArea + QScrollArea +
verticalscrollarea.h
+ 1 +
+
+ + controlsScrollArea + graphScopeComboBox + startingNodesLineEdit + drawGraphButton + zoomSpinBox + nodeWidthSpinBox + coloursComboBox + nodeCustomLabelsCheckBox + nodeNamesCheckBox + nodeDepthCheckBox + fontButton + textOutlineCheckBox + selectionSearchNodesLineEdit + selectionSearchNodesExactMatchRadioButton + selectionSearchNodesPartialMatchRadioButton + selectNodesButton + selectedNodesTextEdit + setNodeCustomColourButton + setNodeCustomLabelButton + selectedEdgesTextEdit + + + + + +
diff --git a/ui/taxinfodialog.cpp b/ui/taxinfodialog.cpp index 99c4d06a..401af4bb 100644 --- a/ui/taxinfodialog.cpp +++ b/ui/taxinfodialog.cpp @@ -4,6 +4,8 @@ #include "../program/globals.h" #include "../graph/assemblygraph.h" #include +#include +#include TaxInfoDialog::TaxInfoDialog(QWidget *parent) : QDialog(parent), @@ -94,4 +96,49 @@ void TaxInfoDialog::setSpecialTaxInfoTexts(int taxId) { else { setErrorText(taxId); } -} \ No newline at end of file +} + +QString TaxInfoDialog::getHiCTaxInfoInTxt(int taxId) { + tax* currentTax = g_assemblyGraph->m_taxData->m_taxMap[taxId]; + QString text = NULL; + if (currentTax != NULL) { + std::vector> res = g_assemblyGraph->getHiCConnectedTaxes(currentTax); + res.push_back(qMakePair(currentTax, currentTax->hicLinksToThemself)); + std::sort(res.begin(), res.end(), taxPairCmp); + text += ("tax name,\ttax id,\tnum of Hi-C links,\tcontig len,\t% of all Hi-C links;\n"); + for (int i = 0; i < res.size(); i++) { + QPair* pair = &res[i]; + text += (pair->first->getName() + ",\t"); + text += (QString::number(pair->first->getTaxId()) + ",\t"); + text += (QString::number(pair->second) + ",\t"); + text += (QString::number(pair->first->getContigLen()) + ",\t"); + text += (QString::number((double)((double)pair->second / (double)pair->first->hicLinksWeight) * 100) + "%;\n"); + } + } + return text; +} + +QString TaxInfoDialog::getCommonTaxInfoInTxt() { + QMap>* stat = &(g_assemblyGraph->m_taxData->m_statistic); + QString text; + text += "There is statistic about taxonometry analyse. Ten most represented taxes in every of 8 ranks were displayed.\n"; + text += "Every record contains tax name, tax id, total length of contigs under this tax, number of taxes under this tax"; + text += ";\n"; + for (int rank = 1; rank < 9; rank++) { + text += g_assemblyGraph->m_taxData->getLevelByRank(rank); + text += ";\n"; + std::vector* tempRankStat = &((*stat)[rank]); + for (int i = 0; i < tempRankStat->size(); i++) { + tax* curTax = tempRankStat->at(i); + text += (curTax->getName()); + text += ",\t"; + text += QString::number(curTax->getTaxId()); + text += ",\t"; + text += QString::number(curTax->getContigLen()); + text += ",\t"; + text += QString::number(curTax->getContigCount()); + text += ";\n"; + } + } + return text; +} diff --git a/ui/taxinfodialog.h b/ui/taxinfodialog.h index 3e304b8f..182a3bb4 100644 --- a/ui/taxinfodialog.h +++ b/ui/taxinfodialog.h @@ -15,10 +15,11 @@ class TaxInfoDialog : public QDialog explicit TaxInfoDialog(QWidget *parent = 0); TaxInfoDialog(QWidget *parent, int taxId); ~TaxInfoDialog(); + QString getHiCTaxInfoInTxt(int taxId); + QString getCommonTaxInfoInTxt(); private: Ui::TaxInfoDialog *ui; - void setInfoTexts(); void setSpecialTaxInfoTexts(int taxId); void setErrorText(); diff --git a/ui/taxinfodialog.ui b/ui/taxinfodialog.ui index af402ed4..8969da40 100644 --- a/ui/taxinfodialog.ui +++ b/ui/taxinfodialog.ui @@ -29,6 +29,13 @@ + + + + Save in file + + +