Mercurial > hg > easyhg
diff grapher.cpp @ 45:4286836bb3c9
* Some more work on graph layout; ensure LANG is set for parseable UTF8 output when running Hg
author | Chris Cannam <cannam@all-day-breakfast.com> |
---|---|
date | Wed, 10 Nov 2010 12:44:11 +0000 |
parents | bed7ab59f62e |
children | bd3accba9b3f |
line wrap: on
line diff
--- a/grapher.cpp Tue Nov 09 17:51:12 2010 +0000 +++ b/grapher.cpp Wed Nov 10 12:44:11 2010 +0000 @@ -1,87 +1,189 @@ #include "grapher.h" -#include <QSet> -#include <QMap> +#include <QGraphicsScene> #include <iostream> -typedef QSet<int> ColumnSet; -typedef QMap<int, ColumnSet> GridAlloc; -typedef QMap<QString, Changeset *> IdChangesetMap; -typedef QSet<Changeset *> ChangesetSet; +int +Grapher::findAvailableColumn(int row, int parent, bool preferParentCol) +{ + int col = parent; + if (preferParentCol) { + if (!m_alloc[row].contains(col)) { + return col; + } + } + while (col > 0) { + if (!m_alloc[row].contains(--col)) return col; + } + while (col < 0) { + if (!m_alloc[row].contains(++col)) return col; + } + col = parent; + int sign = (col < 0 ? -1 : 1); + while (1) { + col += sign; + if (!m_alloc[row].contains(col)) return col; + } +} -ChangesetItem * -layout(Changeset *cs, - IdChangesetMap idCsetMap, - ChangesetItemMap items, - GridAlloc &alloc, - ChangesetSet &handled) +void +Grapher::layoutRow(QString id) { - if (!cs) { - throw std::string("Null Changeset"); + if (m_handled.contains(id)) { + return; } - if (!items.contains(cs)) { - throw std::string("Changeset not in item map"); + if (!m_idCsetMap.contains(id)) { + throw LayoutException(QString("Changeset %1 not in ID map").arg(id)); } - ChangesetItem *item = items[cs]; - if (handled.contains(cs)) { - return item; + if (!m_items.contains(id)) { + throw LayoutException(QString("Changeset %1 not in item map").arg(id)); } + Changeset *cs = m_idCsetMap[id]; + ChangesetItem *item = m_items[id]; + std::cerr << "Looking at " << id.toStdString() << std::endl; + int row = 0; - int col = 0; - if (!cs->parents().empty()) { + int nparents = cs->parents().size(); + + if (nparents > 0) { bool haveRow = false; foreach (QString parentId, cs->parents()) { - if (parentId == "") continue; //!!! - std::cerr << "recursing to parent \"" << parentId.toStdString() << "\" of \"" << cs->id().toStdString() << "\"" << std::endl; - ChangesetItem *parentItem = - layout(idCsetMap[parentId], - idCsetMap, - items, - alloc, - handled); + + if (!m_idCsetMap.contains(parentId)) continue; + if (!m_items.contains(parentId)) continue; + + if (!m_handled.contains(parentId)) { + layoutRow(parentId); + } + + ChangesetItem *parentItem = m_items[parentId]; if (!haveRow || parentItem->row() < row) { row = parentItem->row(); haveRow = true; } - col += parentItem->column(); } - col /= cs->parents().size(); row = row - 1; - while (alloc[row].contains(col)) { - if (col > 0) col = -col; - else col = -col + 1; - } - alloc[row].insert(col); - } - item->setColumn(col); + } + + std::cerr << "putting " << cs->id().toStdString() << " at row " << row + << std::endl; + item->setRow(row); - item->setX(col * 100); item->setY(row * 100); - handled.insert(cs); - return item; + m_handled.insert(id); } void -Grapher::layout(Changesets csets, ChangesetItemMap items) +Grapher::layoutCol(QString id) { - IdChangesetMap idCsetMap; - foreach (Changeset *cs, csets) { - std::cerr << cs->id().toStdString() << std::endl; - if (cs->id() == "") { - throw std::string("Changeset has no ID"); + if (m_handled.contains(id)) { + std::cerr << "Already looked at " << id.toStdString() << std::endl; + return; + } + if (!m_idCsetMap.contains(id)) { + throw LayoutException(QString("Changeset %1 not in ID map").arg(id)); + } + if (!m_items.contains(id)) { + throw LayoutException(QString("Changeset %1 not in item map").arg(id)); + } + Changeset *cs = m_idCsetMap[id]; + ChangesetItem *item = m_items[id]; + std::cerr << "Looking at " << id.toStdString() << std::endl; + + int col = 0; + int nparents = cs->parents().size(); + + if (nparents > 0) { + bool preferParentCol = true; + foreach (QString parentId, cs->parents()) { + + if (!m_idCsetMap.contains(parentId)) continue; + if (!m_items.contains(parentId)) continue; + + if (nparents == 1) { + // when introducing a new branch, aim _not_ to + // position child on the same column as parent + Changeset *parent = m_idCsetMap[parentId]; + if (parent->branch() != cs->branch()) { + preferParentCol = false; + } + } + + if (!m_handled.contains(parentId)) { + layoutCol(parentId); + } + + ChangesetItem *parentItem = m_items[parentId]; + col += parentItem->column(); } - if (idCsetMap.contains(cs->id())) { - throw std::string("Changeset ID is already in map"); - } - idCsetMap[cs->id()] = cs; + + col /= cs->parents().size(); + col = findAvailableColumn(item->row(), col, preferParentCol); + m_alloc[item->row()].insert(col); } - GridAlloc alloc; - ChangesetSet handled; + std::cerr << "putting " << cs->id().toStdString() << " at col " << col << std::endl; + + item->setColumn(col); + item->setX(col * 100); + m_handled.insert(id); +} + +void +Grapher::layout(Changesets csets) +{ + m_idCsetMap.clear(); + m_items.clear(); + m_alloc.clear(); + foreach (Changeset *cs, csets) { - ::layout(cs, idCsetMap, items, alloc, handled); + + QString id = cs->id(); + std::cerr << id.toStdString() << std::endl; + + if (id == "") { + throw LayoutException("Changeset has no ID"); + } + if (m_idCsetMap.contains(id)) { + throw LayoutException(QString("Duplicate changeset ID %1").arg(id)); + } + + m_idCsetMap[id] = cs; + + ChangesetItem *item = new ChangesetItem(cs); + item->setX(0); + item->setY(0); + m_items[id] = item; + m_scene->addItem(item); + } + + // Layout in reverse order, i.e. forward chronological order. + // This ensures that parents will normally be laid out before + // their children -- though we can recurse from layout() if we + // find any weird exceptions + m_handled.clear(); + for (int i = csets.size() - 1; i >= 0; --i) { + layoutRow(csets[i]->id()); + } + m_handled.clear(); + for (int i = csets.size() - 1; i >= 0; --i) { + layoutCol(csets[i]->id()); + } + + foreach (Changeset *cs, csets) { + QString id = cs->id(); + if (!m_items.contains(id)) continue; + ChangesetItem *me = m_items[id]; + foreach (QString parentId, cs->parents()) { + if (!m_items.contains(parentId)) continue; + ChangesetItem *parent = m_items[parentId]; + QGraphicsLineItem *line = new QGraphicsLineItem; + line->setLine(me->x() + 25, me->y() + 50, + parent->x() + 25, parent->y()); + m_scene->addItem(line); + } } }