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/build_scripts/bandage_build_windows.bat b/build_scripts/bandage_build_windows.bat index 631b2bc6..cd580bed 100644 --- a/build_scripts/bandage_build_windows.bat +++ b/build_scripts/bandage_build_windows.bat @@ -40,4 +40,4 @@ call "%QT_PATH%\%MSVC_VERSION%\bin\windeployqt.exe" Bandage\Bandage.exe 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 +call rmdir Bandage\ /S /Q \ No newline at end of file diff --git a/graph/assemblygraph.cpp b/graph/assemblygraph.cpp index 07ae9b1d..ea17e751 100644 --- a/graph/assemblygraph.cpp +++ b/graph/assemblygraph.cpp @@ -45,6 +45,7 @@ #include #include "ogdfnode.h" #include "../command_line/commoncommandlinefunctions.h" +#include AssemblyGraph::AssemblyGraph() : m_kmer(0), m_contiguitySearchDone(false), @@ -52,8 +53,11 @@ 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); + ogdf::GraphAttributes::edgeGraphics); + tax* root = new tax(0, "root", 0, NULL); + m_taxData = new TaxData(root); clearGraphInfo(); } @@ -61,12 +65,16 @@ AssemblyGraph::~AssemblyGraph() { delete m_graphAttributes; delete m_edgeArray; + delete m_hiCEdgeArray; delete m_ogdfGraph; + delete m_taxData; + g_hicSettings.reset(new HiCSettings()); } void AssemblyGraph::cleanUp() { + QMapIterator i(m_deBruijnGraphNodes); while (i.hasNext()) { @@ -83,42 +91,50 @@ 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; + g_hicSettings.reset(new HiCSettings()); + + tax* root = new tax(0, "root", 0, NULL); + m_taxData = new TaxData(root); 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 isHiC, 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; } @@ -126,8 +142,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; @@ -142,10 +158,19 @@ 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(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); + } + 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); + } node1->addEdge(forwardEdge); node2->addEdge(forwardEdge); negNode1->addEdge(backwardEdge); @@ -166,6 +191,7 @@ void AssemblyGraph::clearOgdfGraphAndResetNodes() m_ogdfGraph->clear(); m_edgeArray->init(*m_ogdfGraph); + m_hiCEdgeArray->init(*m_ogdfGraph); } @@ -225,8 +251,6 @@ QByteArray AssemblyGraph::getReverseComplement(QByteArray forwardSequence) } - - void AssemblyGraph::resetEdges() { QMapIterator, DeBruijnEdge*> i(m_deBruijnGraphEdges); @@ -235,6 +259,13 @@ void AssemblyGraph::resetEdges() i.next(); i.value()->reset(); } + + QMapIterator, DeBruijnEdge*> j(m_hiCDeBruijnGraphEdges); + while (j.hasNext()) + { + j.next(); + j.value()->reset(); + } } @@ -337,7 +368,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(); } } @@ -412,7 +443,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; @@ -457,10 +488,6 @@ void AssemblyGraph::clearGraphInfo() } - - - - void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) { m_graphFileType = LAST_GRAPH; @@ -481,7 +508,7 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) { QStringList firstLineParts = line.split(QRegularExpression("\\s+")); if (firstLineParts.size() > 2) - m_kmer = firstLineParts[2].toInt(); + m_kmer = firstLineParts[2].toInt(); firstLine = false; } @@ -542,6 +569,93 @@ void AssemblyGraph::buildDeBruijnGraphFromLastGraph(QString fullFileName) throw "load error"; } +bool AssemblyGraph::loadHiC(QString filename, QString* errormsg) +{ + findComponents(); + QFile hiCMatrix(filename); + if (!hiCMatrix.open(QIODevice::ReadOnly)) + { + *errormsg = "Unable to read from specified file."; + return false; + } + + QTextStream in(&hiCMatrix); + QApplication::processEvents(); + 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; + } + } + g_hicSettings->maxWeight = maxWeight; + + QMapIterator, DeBruijnEdge*> j_hiC(m_hiCDeBruijnGraphEdges); + while (j_hiC.hasNext()) + { + j_hiC.next(); + DeBruijnEdge* edge = j_hiC.value(); + g_hicSettings->addEdgeIfNeeded(edge); + } + return true; +} + + +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. @@ -562,7 +676,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; @@ -1077,8 +1191,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); } } @@ -1105,8 +1219,6 @@ void AssemblyGraph::pointEachNodeToItsReverseComplement() } - - void AssemblyGraph::buildDeBruijnGraphFromTrinityFasta(QString fullFileName) { m_graphFileType = TRINITY; @@ -1799,6 +1911,348 @@ bool AssemblyGraph::loadGraphFromFile(QString filename) return true; } +void AssemblyGraph::findComponents() { + g_settings->wasComponentsFound = true; + QMapIterator i(m_deBruijnGraphNodes); + int componentId = 1; + while (i.hasNext()) + { + i.next(); + DeBruijnNode * node = i.value(); + if (node->getComponentId() == 0) { + 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; + +} + +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; +} + +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); + + if (node->getNameWithoutSign().endsWith("_start")) { + g_hicSettings->addTargetComponentIfNeeded(componentId); + } + for (DeBruijnEdge* edge : node->getLeavingEdges()) { + if (edge->getEndingNode()->getComponentId() == 0 && !edge->isHiC()) { + 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 = dfsComponent(edge->getStartingNode(), componentId, mergedNode); + size += res.second; + contigCount += res.first; + } + } + 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()) { + if (!edge->isHiC()) { + 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()) { + if (!edge->isHiC()) { + 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); + 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 @@ -1814,8 +2268,21 @@ 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) + if (i.value()->isPositiveNode() || g_settings->doubleMode) { 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(); + } + } + } } } else //The scope is either around specified nodes, around nodes with BLAST hits or a depth range. @@ -1875,7 +2342,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;}); @@ -1933,12 +2400,178 @@ void AssemblyGraph::buildOgdfGraphFromNodesAndEdges(std::vector j.next(); DeBruijnEdge * edge = j.value(); edge->determineIfDrawn(); - if (edge->isDrawn()) + if (!edge->isHiC() && edge->isDrawn()) edge->addToOgdfGraph(m_ogdfGraph, m_edgeArray); } } +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(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::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; + 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) { @@ -1952,7 +2585,6 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) { i.next(); DeBruijnNode * node = i.value(); - if (node->isDrawn()) { if (meanDrawnDepth == 0) @@ -1975,8 +2607,8 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) { j.next(); DeBruijnEdge * edge = j.value(); - - if (edge->isDrawn()) + edge->determineIfDrawn(); + if (edge->isDrawn() && !edge->isHiC()) { GraphicsItemEdge * graphicsItemEdge = new GraphicsItemEdge(edge); edge->setGraphicsItemEdge(graphicsItemEdge); @@ -1985,6 +2617,23 @@ void AssemblyGraph::addGraphicsItemsToScene(MyGraphicsScene * scene) } } + if (!m_hiCDeBruijnGraphEdges.empty()) { + 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()) + { + 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); @@ -1992,8 +2641,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; + } } } @@ -2001,7 +2653,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; @@ -2011,15 +2663,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"; @@ -2051,7 +2703,7 @@ std::vector AssemblyGraph::getStartingNodes(QString * errorTitle } std::vector startingNodes = getNodesInDepthRange(g_settings->minDepthRange, - g_settings->maxDepthRange); + g_settings->maxDepthRange); if (startingNodes.size() == 0) { @@ -2070,7 +2722,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; } @@ -2121,7 +2773,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; @@ -2170,7 +2822,7 @@ std::vector AssemblyGraph::getNodesFromListExact(QStringList nod } std::vector AssemblyGraph::getNodesFromListPartial(QStringList nodesList, - std::vector * nodesNotInGraph) + std::vector * nodesNotInGraph) { std::vector returnVector; @@ -2233,7 +2885,7 @@ std::vector AssemblyGraph::getNodesFromBlastHits(QString queryNa } std::vector AssemblyGraph::getNodesInDepthRange(double min, - double max) + double max) { std::vector returnVector; @@ -2269,9 +2921,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(); } @@ -2284,13 +2936,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; @@ -2302,6 +2961,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 @@ -2357,6 +3023,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; + } + } + } } @@ -2380,6 +3063,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; } @@ -2556,7 +3251,6 @@ int AssemblyGraph::getDrawnNodeCount() const return nodeCount; } - void AssemblyGraph::deleteNodes(std::vector * nodes) { //Build a list of nodes to delete. @@ -2611,7 +3305,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; @@ -2632,8 +3326,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); @@ -2680,7 +3374,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(); @@ -2689,7 +3383,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); @@ -2764,12 +3458,11 @@ 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. bool AssemblyGraph::mergeNodes(QList nodes, MyGraphicsScene * scene, - bool recalulateDepth) + bool recalulateDepth) { if (nodes.size() == 0) return true; @@ -2859,10 +3552,27 @@ 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); + 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); @@ -2871,7 +3581,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(); @@ -2879,7 +3589,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) @@ -2899,8 +3609,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) @@ -2912,28 +3622,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); } @@ -2958,9 +3668,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) @@ -2981,8 +3691,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; @@ -3053,8 +3763,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) @@ -3096,15 +3806,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) @@ -3149,7 +3859,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; @@ -3191,9 +3901,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); @@ -3215,9 +3925,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); @@ -3309,6 +4019,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) @@ -3342,8 +4061,19 @@ 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); + } + + 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); } @@ -3416,7 +4146,7 @@ NodeNameStatus AssemblyGraph::checkNodeNameValidity(QString nodeName) void AssemblyGraph::changeNodeDepth(std::vector * nodes, - double newDepth) + double newDepth) { if (nodes->size() == 0) return; @@ -3440,7 +4170,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; @@ -3517,7 +4247,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) @@ -3538,7 +4268,7 @@ void AssemblyGraph::getGraphComponentCountAndLargestComponentSize(int * componen QSet visitedNodes; QList< QList > connectedComponents; - + //Loop through all positive nodes. QMapIterator i(m_deBruijnGraphNodes); while (i.hasNext()) @@ -3547,12 +4277,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); @@ -3575,9 +4305,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(); @@ -3736,6 +4466,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); @@ -3813,7 +4553,6 @@ bool AssemblyGraph::allNodesStartWith(QString start) const return true; } - QString AssemblyGraph::simplifyCanuNodeName(QString oldName) const { QString newName; @@ -3850,7 +4589,6 @@ 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) @@ -3858,3 +4596,4 @@ bool AssemblyGraph::useLinearLayout() const { else return g_settings->linearLayout; } + diff --git a/graph/assemblygraph.h b/graph/assemblygraph.h index 426e7532..7ec2cfb6 100644 --- a/graph/assemblygraph.h +++ b/graph/assemblygraph.h @@ -27,9 +27,12 @@ #include #include #include "../program/globals.h" +#include "../program/HiCSettings.h" #include "../ui/mygraphicsscene.h" #include "path.h" #include +#include "../taxonomy/TaxData.h" +#include "../taxonomy/Tax.h" class DeBruijnNode; class DeBruijnEdge; @@ -49,10 +52,13 @@ 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; + TaxData* m_taxData; int m_kmer; int m_nodeCount; @@ -73,7 +79,7 @@ class AssemblyGraph : public QObject 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(); @@ -107,6 +113,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=","); @@ -137,7 +144,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); @@ -171,7 +178,16 @@ class AssemblyGraph : public QObject bool attemptToLoadSequencesFromFasta(); 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; @@ -210,6 +226,12 @@ class AssemblyGraph : public QObject double findDepthAtIndex(QList * nodeList, long long targetIndex) const; bool allNodesStartWith(QString start) const; QString simplifyCanuNodeName(QString oldName) const; + 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 setInclusionFilterAuto(); + void dfsZipped(DeBruijnNode* curNode, int boundLen, QList* mainNodes, QList* zippedNodes); + void createNodesUnion(QList mainNodes, QList zippedNodes, QString unionName); signals: void setMergeTotalCount(int totalCount); diff --git a/graph/debruijnedge.cpp b/graph/debruijnedge.cpp index 9d8899c4..4299bc94 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) { } @@ -101,20 +101,6 @@ void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArrayinOgdf()) - 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 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). @@ -124,16 +110,48 @@ void DeBruijnEdge::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::EdgeArraynewEdge(firstEdgeOgdfNode, secondEdgeOgdfNode); - (*edgeArray)[newEdge] = g_settings->edgeLength; -} - - - - - + 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 + } + 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 ((!isHiC()) || (m_startingNode->getComponentId() != 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); + } + } +} //This function traces all possible paths from this edge. //It proceeds a number of steps, as determined by a setting. diff --git a/graph/debruijnedge.h b/graph/debruijnedge.h index b007f941..ba5b1f1d 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; @@ -64,9 +66,14 @@ 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 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; @@ -76,7 +83,9 @@ 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; std::vector findNextEdgesInPath(DeBruijnNode * nextNode, diff --git a/graph/debruijnnode.cpp b/graph/debruijnnode.cpp index 42eaa2c3..d68b0a36 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 @@ -89,7 +90,6 @@ void DeBruijnNode::resetNode() m_highestDistanceInNeighbourSearch = 0; } - void DeBruijnNode::addToOgdfGraph(ogdf::Graph * ogdfGraph, ogdf::GraphAttributes * graphAttributes, ogdf::EdgeArray * edgeArray, double xPos, double yPos) { @@ -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 @@ -544,11 +591,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 @@ -563,7 +616,6 @@ void DeBruijnNode::labelNeighbouringNodesAsDrawn(int nodeDistance, DeBruijnNode } - std::vector DeBruijnNode::getBlastHitPartsForThisNode(double scaledNodeLength) const { std::vector returnVector; @@ -838,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 7d3b7e13..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());} @@ -98,6 +108,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 +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);} @@ -126,6 +139,23 @@ 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;} + 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; @@ -139,7 +169,11 @@ class DeBruijnNode GraphicsItemNode * m_graphicsItemNode; std::vector m_edges; bool m_specialNode; - bool m_drawn; + bool m_drawn = false; + + bool m_zipped = false; + bool m_isNodesUnion = false; + int m_highestDistanceInNeighbourSearch; QColor m_customColour; QString m_customLabel; @@ -147,6 +181,11 @@ class DeBruijnNode QStringList m_csvData; 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 ccabcdb0..b52c83cd 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 @@ -39,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; @@ -54,7 +58,14 @@ 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 = 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); painter->drawPath(path()); } @@ -117,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); } @@ -128,24 +145,48 @@ 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(); + } + 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(); + } + 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(); + } + 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(); + } + else { + m_endingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getLast(); + m_afterEndingLocation = endingNode->getReverseComplement()->getGraphicsItemNode()->getSecondLast(); + } } } diff --git a/graph/graphicsitemnode.cpp b/graph/graphicsitemnode.cpp index e04ea6a1..b4992de5 100644 --- a/graph/graphicsitemnode.cpp +++ b/graph/graphicsitemnode.cpp @@ -44,14 +44,16 @@ #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; OgdfNode * pathOgdfNode = deBruijnNode->getOgdfNode(); if (pathOgdfNode != 0) @@ -86,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(); } @@ -101,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. @@ -120,10 +120,35 @@ void GraphicsItemNode::paint(QPainter * painter, const QStyleOptionGraphicsItem // painter->setPen(QPen(Qt::black, 1.0)); // 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(); + //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->fillPath(outlinePath, brush); + 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; @@ -134,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; @@ -210,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.boundingRect(text).width() / 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. @@ -259,46 +265,12 @@ 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->nodeColourScheme == SAVE_COLOURS && m_deBruijnNode->m_lastColor.isValid()) { + m_colour = m_deBruijnNode->m_lastColor; + return; + } switch (g_settings->nodeColourScheme) { case UNIFORM_COLOURS: @@ -309,8 +281,89 @@ 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; + } - case RANDOM_COLOURS: + m_colour = colour1; + DeBruijnNode* revCompNode = m_deBruijnNode->getReverseComplement(); + if (revCompNode != 0) + { + GraphicsItemNode* revCompGraphNode = revCompNode->getGraphicsItemNode(); + if (revCompGraphNode != 0) + revCompGraphNode->m_colour = colour2; + } + } + 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 || SAVE_COLOURS): { //Make a colour with a random hue. Assign a colour to both this node and //it complement so their hue matches. @@ -368,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(); @@ -406,11 +464,65 @@ 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 { + 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 @@ -467,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); + + fixEdgePaths(&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); - for (size_t i = 0; i < nodesToMove.size(); ++i) - { - nodesToMove[i]->shiftPoints(difference); - nodesToMove[i]->remakePath(); - } - graphicsScene->possiblyExpandSceneRectangle(&nodesToMove); + for (size_t i = 0; i < nodesToMove.size(); ++i) + { + nodesToMove[i]->shiftPoints(difference); + nodesToMove[i]->remakePath(); + } + graphicsScene->possiblyExpandSceneRectangle(&nodesToMove); - fixEdgePaths(&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. @@ -557,270 +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]); - for (size_t i = 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) @@ -836,18 +709,36 @@ 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; } - QSize GraphicsItemNode::getNodeTextSize(QString text) { QFontMetrics fontMetrics(g_settings->labelFont); return fontMetrics.size(0, text); } - QColor GraphicsItemNode::getDepthColour() { double depth = m_deBruijnNode->getDepth(); @@ -884,8 +775,6 @@ QColor GraphicsItemNode::getDepthColour() return QColor(red, green, blue, alpha); } - - void GraphicsItemNode::setWidth() { m_width = getNodeWidth(m_deBruijnNode->getDepthRelativeToMeanDrawnDepth(), g_settings->depthPower, @@ -894,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 @@ -923,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) { @@ -1022,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) @@ -1086,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) { @@ -1115,7 +931,7 @@ QPainterPath GraphicsItemNode::buildPartialHighlightPath(double startFraction, } QPainterPath partialPath = makePartialPath(startFraction, - endFraction); + endFraction); QPainterPathStroker stroker; @@ -1135,14 +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; -} +} \ No newline at end of file diff --git a/graph/graphicsitemnode.h b/graph/graphicsitemnode.h index 9cf9ac3d..60d8e404 100644 --- a/graph/graphicsitemnode.h +++ b/graph/graphicsitemnode.h @@ -29,77 +29,59 @@ #include #include #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 {return m_linePoints[1];} - QPointF getLast() const {return m_linePoints[m_linePoints.size()-1];} - QPointF getSecondLast() const {return m_linePoints[m_linePoints.size()-2];} - 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 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/graph/ogdfnode.h b/graph/ogdfnode.h index e6b0e1c0..3df4c16c 100644 --- a/graph/ogdfnode.h +++ b/graph/ogdfnode.h @@ -34,9 +34,34 @@ 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 { + 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]; + } + } + 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]; + } + } }; #endif // OGDFNODE_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/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..d6220da5 --- /dev/null +++ b/program/HiCSettings.h @@ -0,0 +1,63 @@ +#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 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; + } + +private: + QPair getComponentKey(DeBruijnEdge* edge); + bool isValidContigLength(DeBruijnEdge* edge); +}; +#endif //HICSETTINGS_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 42695874..d654b91f 100644 --- a/program/globals.cpp +++ b/program/globals.cpp @@ -27,12 +27,15 @@ #include 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 10b1bbbd..86f9188d 100644 --- a/program/globals.h +++ b/program/globals.h @@ -30,17 +30,24 @@ 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}; -enum GraphScope {WHOLE_GRAPH, AROUND_NODE, AROUND_BLAST_HITS, DEPTH_RANGE}; + 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}; enum ContiguityStatus {STARTING, CONTIGUOUS_STRAND_SPECIFIC, CONTIGUOUS_EITHER_STRAND, MAYBE_CONTIGUOUS, 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}; @@ -58,15 +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 b193aa4b..868a82b1 100644 --- a/program/graphlayoutworker.cpp +++ b/program/graphlayoutworker.cpp @@ -21,22 +21,45 @@ #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) : + 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_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); - 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,7 +98,6 @@ void GraphLayoutWorker::layoutGraph() m_fmmm->nmPrecision(8); break; } - m_fmmm->call(*m_graphAttributes, *m_edgeArray); emit finishedLayout(); diff --git a/program/graphlayoutworker.h b/program/graphlayoutworker.h index cc808a80..8ec3347d 100644 --- a/program/graphlayoutworker.h +++ b/program/graphlayoutworker.h @@ -24,14 +24,18 @@ #include "../ogdf/basic/GraphAttributes.h" + class GraphLayoutWorker : public QObject { Q_OBJECT 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, + double graphLayoutComponentSeparation, int randSeed, double aspectRatio = 1.333333); ogdf::FMMMLayout * m_fmmm; ogdf::GraphAttributes * m_graphAttributes; @@ -40,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/main.cpp b/program/main.cpp index 6a81a8d4..db167a60 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 @@ -98,10 +100,14 @@ 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()); + 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 e00de1eb..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); @@ -76,6 +77,14 @@ Settings::Settings() textOutline = false; antialiasing = true; positionTextNodeCentre = false; + displayTaxIdName = false; + displayTaxIdRank = false; + displayTaxNameRank = false; + + displayFeatureIdLabels = false; + displayFeatureClassLabels = false; + displayFeatureCustomLabels = false; + displayFeatureClassLikeFigure = false; nodeDragging = NEARBY_PIECES; @@ -141,4 +150,11 @@ 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); + + taxRank = IntSetting(1, 1, 8); + taxDistance = IntSetting(-1, 0, 1000); + displayAroundTaxWithHiC = false; } diff --git a/program/settings.h b/program/settings.h index 7ec64ed7..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; @@ -123,11 +124,19 @@ class Settings bool displayNodeCsvData; int displayNodeCsvDataCol; bool displayBlastHits; + bool displayTaxIdName; + bool displayTaxIdRank; + bool displayTaxNameRank; QFont labelFont; bool textOutline; bool antialiasing; bool positionTextNodeCentre; + bool displayFeatureIdLabels; + bool displayFeatureClassLabels; + bool displayFeatureCustomLabels; + bool displayFeatureClassLikeFigure; + NodeDragging nodeDragging; QColor edgeColour; @@ -137,6 +146,7 @@ class Settings QColor textOutlineColour; NodeColourScheme nodeColourScheme; + NodeColourScheme featureColourScheme; QColor uniformPositiveNodeColour; QColor uniformNegativeNodeColour; QColor uniformNodeSpecialColour; @@ -202,6 +212,30 @@ 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 = 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; + 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/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 b8645e30..c0c0e627 100644 --- a/ui/mainwindow.cpp +++ b/ui/mainwindow.cpp @@ -65,6 +65,12 @@ #include "changenodedepthdialog.h" #include #include "graphinfodialog.h" +#include "taxinfodialog.h" +#include +#include +#include "../random_forest/assemblyforest.h" +#include "../random_forest/GraphicsItemFeatureNode.h" +#include MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : QMainWindow(0), @@ -73,9 +79,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)); @@ -101,6 +108,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; @@ -108,6 +116,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. @@ -120,6 +130,8 @@ MainWindow::MainWindow(QString fileToLoadOnStartup, bool drawGraphAfterLoad) : graphScopeChanged(); switchColourScheme(); + switchFeatureColourScheme(); + switchTaxRank(); //If this is a Mac, change the 'Delete' shortcuts to 'Backspace' instead. #ifdef Q_OS_MAC @@ -128,8 +140,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())); @@ -139,8 +156,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->actionSave_image_current_view, SIGNAL(triggered()), this, SLOT(saveImageCurrentView())); - connect(ui->actionSave_image_entire_scene, SIGNAL(triggered()), this, SLOT(saveImageEntireScene())); + connect(ui->featuresColoursComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(switchFeatureColourScheme())); + connect(ui->taxColourComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(switchTaxRank())); + 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())); @@ -149,6 +170,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())); @@ -178,6 +202,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())); @@ -193,6 +218,15 @@ 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(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)); } @@ -263,6 +297,115 @@ 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 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::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 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::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)"; @@ -326,6 +469,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; @@ -389,6 +537,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) @@ -477,6 +627,55 @@ 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::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() { @@ -515,6 +714,9 @@ void MainWindow::selectionChanged() } ui->selectedNodesTextEdit->setPlainText(selectedNodeListText); + QString selectedNodeTaxListText; + getSelectedNodeTaxInfo(selectedNodeTaxListText); + ui->taxInfoTextEdit->setPlainText(selectedNodeTaxListText); } @@ -565,6 +767,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"; + } + } +} @@ -603,6 +833,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); @@ -618,12 +849,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); @@ -650,10 +882,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); @@ -686,7 +918,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); } + } @@ -715,28 +961,119 @@ 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) +{ + ui->hicSeqLenInfoText->setVisible(visible); + ui->hicSeqLenSpinBox->setVisible(visible); + ui->hicWeightInfoText->setVisible(visible); + ui->hicWeightSpinBox->setVisible(visible); +} +void MainWindow::setTaxVisibility(bool visible) +{ + ui->labelTaxInfo->setVisible(visible); + ui->taxLine_4->setVisible(visible); + ui->taxInfoTextEdit->setVisible(visible); + ui->labelTaxInfo->setVisible(visible); + +} void MainWindow::drawGraph() { + HiCDrawingType type = g_settings->hicDrawingType; QString errorTitle; QString errorMessage; - std::vector startingNodes = g_assemblyGraph->getStartingNodes(&errorTitle, &errorMessage, - ui->doubleNodesRadioButton->isChecked(), - ui->startingNodesLineEdit->text(), - ui->blastQueryComboBox->currentText()); + HiCInclusionFilter filterHiC = g_settings->hicInclusionFilter; + std::vector startingNodes = g_assemblyGraph->getStartingNodes(&errorTitle, &errorMessage, + ui->doubleNodesRadioButton->isChecked(), + ui->startingNodesLineEdit->text(), + ui->blastQueryComboBox->currentText()); if (errorMessage != "") { 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(); - g_assemblyGraph->buildOgdfGraphFromNodesAndEdges(startingNodes, g_settings->nodeDistance); - layoutGraph(); + + 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(); + } + 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::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() { @@ -744,6 +1081,7 @@ void MainWindow::graphLayoutFinished() m_layoutThread = 0; g_assemblyGraph->addGraphicsItemsToScene(m_scene); m_scene->setSceneRectangle(); + zoomToFitScene(); selectionChanged(); @@ -751,6 +1089,7 @@ void MainWindow::graphLayoutFinished() //Move the focus to the view so the user can use keyboard controls to navigate. g_graphicsView->setFocus(); + g_settings->addNewNodes = false; } @@ -770,7 +1109,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); @@ -786,9 +1127,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. @@ -810,11 +1148,21 @@ void MainWindow::layoutGraph() m_layoutThread = new QThread; double aspectRatio = double(g_graphicsView->width()) / g_graphicsView->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; + } + 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())); @@ -827,8 +1175,44 @@ void MainWindow::layoutGraph() 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())); + 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::zoomSpinBoxChanged() { @@ -866,15 +1250,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; @@ -980,6 +1368,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(); @@ -1043,7 +1478,43 @@ void MainWindow::saveSelectedPathToFile() } } +void MainWindow::switchTaxRank() { + g_settings->taxRank = ui->taxColourComboBox->currentIndex() + 1; + g_assemblyGraph->resetAllNodeColours(); + 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() @@ -1085,14 +1556,28 @@ 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); + break; + case 8: + g_settings->nodeColourScheme = COLOUR_BY_TAX; + ui->contiguityButton->setVisible(false); + 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(); g_graphicsView->viewport()->update(); } - - void MainWindow::determineContiguityFromSelectedNode() { g_assemblyGraph->resetNodeContiguityStatus(); @@ -1117,7 +1602,7 @@ void MainWindow::determineContiguityFromSelectedNode() } -QString MainWindow::getDefaultImageFileName() +QString MainWindow::getDefaultGraphImageFileName() { QString fileNameAndPath = g_memory->rememberedPath + "/graph"; @@ -1133,14 +1618,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, @@ -1160,12 +1670,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(); @@ -1181,19 +1691,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)", @@ -1215,15 +1734,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) { @@ -1253,8 +1772,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(); @@ -1263,29 +1782,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) { @@ -1300,6 +1817,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() { @@ -1311,10 +1842,29 @@ 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(); } +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() { @@ -1329,8 +1879,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) @@ -1359,11 +1911,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) @@ -1384,6 +1968,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. @@ -1771,6 +2380,8 @@ void MainWindow::setUiState(UiState uiState) ui->blastSearchWidget->setEnabled(false); 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); @@ -1780,6 +2391,8 @@ void MainWindow::setUiState(UiState uiState) ui->blastSearchWidget->setEnabled(true); 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); @@ -1790,11 +2403,29 @@ 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); + ui->actionLoad_Taxonometry->setEnabled(true); break; } } - +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()); @@ -2006,7 +2637,7 @@ void MainWindow::zoomToSelection() boundingBox = boundingBox | selectedItem->boundingRect(); } - zoomToFitRect(boundingBox); + zoomToFitRect(boundingBox, g_graphicsView); } @@ -2104,6 +2735,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); @@ -2131,6 +2765,9 @@ 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; + case SAVE_COLOURS: ui->coloursComboBox->setCurrentIndex(9); break; } } @@ -2142,6 +2779,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; } } @@ -2209,6 +2847,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) @@ -2227,6 +2869,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() { @@ -2547,3 +3195,48 @@ 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(); + } + +} + +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 803fb91b..9b6f91a7 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,47 +64,59 @@ 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 addGraphicsItemsToScene(); - 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); 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); + 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); void setStartingNodesWidgetVisibility(bool visible); 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); + std::vector addComplementaryNodes(std::vector nodes); + void layoutGraphUnzip(); private slots: void loadGraph(QString fullFileName = ""); - void loadCSV(QString fullFileNAme = ""); + void loadCSV(QString fullFileName = ""); + void loadHiC(QString fullFileName = ""); + void loadTax(QString fullFileName = ""); + void loadFeaturesForest(QString fullFileName = ""); void selectionChanged(); void graphScopeChanged(); void drawGraph(); @@ -114,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(); @@ -147,6 +167,7 @@ private slots: void startingNodesExactMatchChanged(); void openPathSpecifyDialog(); void nodeWidthChanged(); + void featureNodeWidthChanged(); void saveEntireGraphToFasta(); void saveEntireGraphToFastaOnlyPositiveNodes(); void saveEntireGraphToGfa(); @@ -160,6 +181,20 @@ private slots: void changeNodeName(); void changeNodeDepth(); void openGraphInfoDialog(); + void switchTaxRank(); + void setAroundTaxWidgetVisibility(bool visible); + 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 b20e0cf7..4f005d20 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -7,7 +7,7 @@ 0 0 1465 - 924 + 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 - -194 - 289 - 1058 + 0 + 461 + 1034 @@ -69,13 +57,39 @@ 0 - + - + + + + 0 + 0 + + + + + 75 + true + + + + Find nodes + + + + + + + Qt::Horizontal + + + + + true - + 0 @@ -85,87 +99,56 @@ 0 - - - - - 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 - - - - + + 0 + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + - - - - Qt::Horizontal + + + + Exact + + + true - - + + + + true + + + + 0 + 0 + + + + Node(s): + + + + + 0 @@ -180,26 +163,33 @@ - - - - - 75 - true - + + + + true - Graph information + Match: - - - - Qt::StrongFocus - + + - More info + Partial + + + + + + + true + + + + 0 + 0 + @@ -207,27 +197,15 @@ - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - + + + Find node(s) - + - - - true - - + + 0 @@ -241,29 +219,551 @@ 0 - - - - 75 - true - + + + Qt::Vertical - - Graph drawing + + QSizePolicy::Fixed - - - - - - Qt::Horizontal + + + 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 + + + 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 + + + + + + + Tax info (with Hi-C links) + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + true + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 75 + true + + + + Graph drawing + + + + + + + Qt::Horizontal + + + + + + + 0 @@ -272,11 +772,163 @@ 0 - - + + + + Qt::AlignCenter + + + 1 + + + 1000000.000000000000000 + + + + + + + true + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Around target nodes + + + + + + + Qt::StrongFocus + + + Draw graph + + + + + true + + + 0 + 0 + + + + + + + + Only big component + + + + + + + Zip graph + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + Qt::StrongFocus + + + Entire graph + + + + + Around nodes + + + + + Around BLAST hits + + + + + Depth range + + + + + Around tax + + + + + Entire graph + + + + + Around nodes + + + + + Around BLAST hits + + + + + Depth range + + + + + Around tax + + + + + + 0 @@ -291,44 +943,32 @@ - - - - true - - - - 0 - 0 - + + + + HiC min weight: - - - 16 - 16 - + + Qt::AlignLeading - - - - - 0 - 0 - + + + + Qt::AlignCenter - - - 16 - 16 - + + 1 + + + 1000000.000000000000000 - - - + + + 0 @@ -345,7 +985,7 @@ 0 - + 0 @@ -356,7 +996,7 @@ Qt::StrongFocus - Exact + Single true @@ -364,7 +1004,7 @@ - + 0 @@ -375,34 +1015,15 @@ Qt::StrongFocus - Partial + Double - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - + + 0 @@ -417,9 +1038,9 @@ - - - + + + 0 @@ -436,18 +1057,15 @@ 0 - + 0 0 - - Qt::StrongFocus - - Single + Exact true @@ -455,71 +1073,30 @@ - + 0 0 - - Qt::StrongFocus - - Double + Partial - - - - Qt::StrongFocus - - - Draw graph - - - - - - - 0 - 0 - - + - Style: - - - - - - - true - - - - 0 - 0 - - - - Qt::StrongFocus - - - Qt::AlignCenter - - - 10000 + Tax id: - - + + true @@ -530,57 +1107,8 @@ - Node(s): - - - - - - - true - - - - 0 - 0 - - - - Qt::StrongFocus - - - - - - - - 0 - 0 - - - - Qt::StrongFocus + Match: - - - Entire graph - - - - - Around nodes - - - - - Around BLAST hits - - - - - Depth range - - @@ -596,82 +1124,37 @@ - - - - true - - - Distance: - - - - - + + true - + 0 0 - - Match: - - - - - - - Qt::StrongFocus - Qt::AlignCenter - - 1 - - 1000000.000000000000000 - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - - 0 - 0 - + 10000 - - - 16 - 16 - + + + + + + With Distance - - + + + + true + 0 @@ -679,12 +1162,12 @@ - Min: + Node(s): - - + + 0 @@ -699,125 +1182,65 @@ - - + + + + With Hi-C + + + + + - + 0 0 - - Max: - - - - - Qt::StrongFocus Qt::AlignCenter + + + 1 + + 0.000000000000000 + - 1000000.000000000000000 + 1000.000000000000000 + + + 1.000000000000000 + + + 1.000000000000000 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 229 - 15 - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 75 - true - - - - Graph display - - - - - - - Qt::Horizontal - - - - - - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - + + + + true - - - 16 - 16 - + + Distance: - - + + + + + + + true + 0 @@ -832,8 +1255,8 @@ - - + + 0 @@ -848,104 +1271,47 @@ - - - - - 0 - 0 - - - - Qt::StrongFocus - - - Qt::AlignCenter - - - % - - - 1 - - - 5.000000000000000 - + + - 500.000000000000000 - - - 5.000000000000000 + 1000 - 100.000000000000000 + 0 - - + + + + + 0 + 0 + + Qt::StrongFocus - Determine contiguity + Min: - - - - Qt::StrongFocus + + + + + 0 + 0 + - - - 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 + Style: - - + + 0 @@ -960,27 +1326,37 @@ - + + + + + 0 + 0 + + + + Max: + + + + - Node width: + HiC min sequence length: - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignLeading - - + + 0 0 - - Qt::StrongFocus - Qt::AlignCenter @@ -991,93 +1367,21 @@ 1 - 0.500000000000000 + 0.000000000000000 - 1000.000000000000000 + 1000000.000000000000000 - 0.500000000000000 + 10.000000000000000 - 5.000000000000000 + 100.000000000000000 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - - - - - - - - true - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 75 - true - - - - Node labels - - - - - - - Qt::Horizontal - - - - - - - - 0 - - - 0 - - - 0 - - - + + 0 @@ -1093,7 +1397,10 @@ - + + + true + 0 @@ -1108,117 +1415,6 @@ - - - - Qt::StrongFocus - - - Font - - - - - - - Qt::StrongFocus - - - Text outline - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::StrongFocus - - - Custom - - - - - - - Qt::StrongFocus - - - Name - - - - - - - Qt::StrongFocus - - - Length - - - - - - - Qt::StrongFocus - - - Depth - - - - - - - Qt::StrongFocus - - - BLAST hits - - - - - - - false - - - Qt::StrongFocus - - - - - - - Qt::StrongFocus - - - CSV data: - - - - - - @@ -1226,7 +1422,7 @@ - + Qt::Vertical @@ -1242,11 +1438,11 @@ - + true - + 0 @@ -1260,7 +1456,7 @@ 0 - + 75 @@ -1268,20 +1464,20 @@ - BLAST + Graph display - + Qt::Horizontal - - + + 0 @@ -1291,8 +1487,107 @@ 0 + + + + Tax Name (with rank) + + + + + + + Determine contiguity + + + + + + + + 0 + 0 + + + + Qt::AlignCenter + + + + + + 1 + + + 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 @@ -1308,17 +1603,24 @@ - + - Query: + Node width: - - true + + Qt::AlignLeading - - + + + + Tax Id (with rank) + + + + + 0 @@ -1333,149 +1635,151 @@ - - - - false + + + + + Domain + + + + + Kingdom + + + + + Phylum + + + + + Class + + + + + Order + + + + + Family + + + + + Genus + + + + + Species + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + 0 0 - - Qt::StrongFocus + + Qt::AlignCenter + + + % + + + 1 + + + 5.000000000000000 + + + 500.000000000000000 + + + 5.000000000000000 + + + 100.000000000000000 - - - none - - - - - - Qt::StrongFocus - + + - Create/view BLAST search + Zoom: + + + Qt::AlignLeading - - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - - - - - - - - - 0 - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - 0 - 0 - - - - true - - - - - 0 - 0 - 249 - 899 - - - - - 0 - 0 - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Find nodes - + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + - + - Qt::Horizontal + Qt::Vertical - + + QSizePolicy::Fixed + + + + 20 + 15 + + + - + true - + 0 @@ -1488,122 +1792,210 @@ 0 - - - - true - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - Qt::StrongFocus - - - Exact - - - true - - - - - - - true - - - - 0 - 0 - - - - Node(s): - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - - true + + + + + 75 + true + - Match: + Node labels - - - - Qt::StrongFocus - - - Partial + + + + Qt::Horizontal - - - - true - - - - 0 - 0 - - - - Qt::StrongFocus - + + + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Font + + + + + + + Text outline + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::StrongFocus + + + Custom + + + + + + + Qt::StrongFocus + + + Name + + + + + + + Qt::StrongFocus + + + Length + + + + + + + Qt::StrongFocus + + + Depth + + + + + + + Qt::StrongFocus + + + BLAST hits + + + + + + + false + + + Qt::StrongFocus + + + + + + + Qt::StrongFocus + + + CSV data: + + + + + + + Tax Name (id) + + + + + + + - - - Qt::StrongFocus + + + Qt::Vertical - - Find node(s) + + QSizePolicy::Fixed - + + + 229 + 15 + + + - - + + + true + + 0 @@ -1617,119 +2009,215 @@ 0 - - - Qt::Vertical + + + + 75 + true + - - QSizePolicy::Fixed + + BLAST - - - 20 - 60 - + + + + + + Qt::Horizontal - + - - - - - - - - 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 + + + 0 + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + Query: + + + true + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + + false + + + + 0 + 0 + + + + Qt::StrongFocus + + + + none + + + + + + + + Qt::StrongFocus + + + Create/view BLAST search + + + + + + + + + + + + + + + + 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 @@ -1744,117 +2232,157 @@ - - - - Qt::StrongFocus + + + + Draw features - - Ctrl+L + + + + + + Class like figure + + + + - Set label + Feature Id - - - - Qt::StrongFocus + + + + + 0 + 0 + - - Ctrl+O + + + 16 + 16 + + + + + - Set colour + Class - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical + + + + + 75 + true + - - QSizePolicy::Fixed + + 1 - + + Features display + + + false + + + + + + + + 0 + 0 + + + + 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 - - - - - + Qt::Vertical 20 - 40 + 0 @@ -1871,7 +2399,7 @@ 0 0 1465 - 22 + 26 @@ -1881,10 +2409,16 @@ - - + + + + + + + + @@ -1924,7 +2458,6 @@ - @@ -1951,6 +2484,9 @@ + + + @@ -1987,7 +2523,7 @@ Ctrl+O - + true @@ -1996,7 +2532,7 @@ :/icons/save-256.png:/icons/save-256.png - Save image (current view) + Save image (graph current view) @@ -2026,7 +2562,7 @@ About - + true @@ -2035,7 +2571,7 @@ :/icons/save-256.png:/icons/save-256.png - Save image (entire scene) + Save image (graph entire scene) @@ -2323,9 +2859,67 @@ 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 + + + + + + :/icons/save-256.png:/icons/save-256.png + + + Save image (features current view) + + + + + + :/icons/save-256.png:/icons/save-256.png + - Select nodes with dead ends + Save image (features entire scene) @@ -2346,14 +2940,11 @@ controlsScrollArea - moreInfoButton graphScopeComboBox startingNodesLineEdit startingNodesExactMatchRadioButton startingNodesPartialMatchRadioButton nodeDistanceSpinBox - minDepthSpinBox - maxDepthSpinBox singleNodesRadioButton doubleNodesRadioButton drawGraphButton @@ -2366,9 +2957,6 @@ nodeLengthsCheckBox nodeDepthCheckBox blastHitsCheckBox - setNodeCustomLabelButton - csvCheckBox - csvComboBox fontButton textOutlineCheckBox blastSearchButton @@ -2378,12 +2966,10 @@ selectionSearchNodesExactMatchRadioButton selectionSearchNodesPartialMatchRadioButton selectNodesButton - kmerSizeInput - drawDotplotButton - dotplotGraphicsView selectedNodesTextEdit - selectedEdgesTextEdit setNodeCustomColourButton + setNodeCustomLabelButton + selectedEdgesTextEdit 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 66e6c02d..91fa59b7 100644 --- a/ui/mygraphicsview.cpp +++ b/ui/mygraphicsview.cpp @@ -62,6 +62,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); @@ -75,8 +76,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/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 b3b6f2c4..58c917db 100644 --- a/ui/settingsdialog.ui +++ b/ui/settingsdialog.ui @@ -6,8 +6,8 @@ 0 0 - 378 - 673 + 487 + 649 @@ -1611,7 +1611,7 @@ single node style: - -1 + 7 0 @@ -4278,6 +4278,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 + + + + + + + diff --git a/ui/taxinfodialog.cpp b/ui/taxinfodialog.cpp new file mode 100644 index 00000000..401af4bb --- /dev/null +++ b/ui/taxinfodialog.cpp @@ -0,0 +1,144 @@ +#include "taxinfodialog.h" +#include "ui_taxinfodialog.h" + +#include "../program/globals.h" +#include "../graph/assemblygraph.h" +#include +#include +#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); + } +} + +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 new file mode 100644 index 00000000..182a3bb4 --- /dev/null +++ b/ui/taxinfodialog.h @@ -0,0 +1,29 @@ +#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(); + QString getHiCTaxInfoInTxt(int taxId); + QString getCommonTaxInfoInTxt(); + +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..8969da40 --- /dev/null +++ b/ui/taxinfodialog.ui @@ -0,0 +1,43 @@ + + + TaxInfoDialog + + + + 0 + 0 + 1187 + 868 + + + + Taxonometry information + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Save in file + + + + + + + +