Commit 0019fe08 authored by Yuya Nishihara's avatar Yuya Nishihara
Browse files

move adjacent tiles properly

parent 56bce74d
#include <QtDebug>
#include <algorithm>
#include <cmath>
#include <limits>
#include <numeric>
#include "tilelistmodel.h"
......@@ -9,6 +11,9 @@ enum Role : int {
TileIndexRole,
ContentRectRole,
};
// Maximum allowed absolute difference to take tiles as adjacent, in normalized coordinates.
constexpr qreal adjacentEpsilon = 0.00001;
}
TileListModel::TileListModel(QObject *parent)
......@@ -156,17 +161,86 @@ void TileListModel::moveHandle(int row, Qt::Orientation orientation, qreal posit
if (row < 0 || row >= static_cast<int>(m_tiles.size()))
return;
auto &tile = m_tiles.at(static_cast<size_t>(row));
const auto &tile = m_tiles.at(static_cast<size_t>(row));
if (tile.tileIndex < 0)
return;
// TODO: adjust adjacent tiles
if (orientation == Qt::Horizontal) {
tile.contentRect.setX(position);
} else {
tile.contentRect.setY(position);
// Collect left/right adjacent tiles of the border. Inserts sentinels for convenience.
Tile topSentinel {
{}, -1, { -std::numeric_limits<qreal>::max(), -std::numeric_limits<qreal>::max(), 0.0, 0.0 }
};
Tile bottomSentinel {
{}, -1, { +std::numeric_limits<qreal>::max(), +std::numeric_limits<qreal>::max(), 0.0, 0.0 }
};
std::vector<Tile *> leftTiles { &topSentinel, &bottomSentinel };
std::vector<Tile *> rightTiles { &topSentinel, &bottomSentinel };
for (auto &t : m_tiles) {
if (t.tileIndex < 0)
continue;
const qreal borderY = tile.leftIn(orientation);
if (std::abs(t.leftIn(orientation) - borderY) <= adjacentEpsilon) {
rightTiles.push_back(&t);
} else if (std::abs(t.rightIn(orientation) - borderY) <= adjacentEpsilon) {
leftTiles.push_back(&t);
}
}
std::sort(leftTiles.begin(), leftTiles.end(), [orientation](const auto *a, const auto *b) {
return a->topIn(orientation) < b->topIn(orientation);
});
std::sort(rightTiles.begin(), rightTiles.end(), [orientation](const auto *a, const auto *b) {
return a->topIn(orientation) < b->topIn(orientation);
});
Q_ASSERT(leftTiles.front() == &topSentinel && leftTiles.back() == &bottomSentinel);
Q_ASSERT(rightTiles.front() == &topSentinel && rightTiles.back() == &bottomSentinel);
auto leftTop =
std::find_if(leftTiles.begin(), leftTiles.end(), [&tile, orientation](const auto *t) {
return t->bottomIn(orientation) > tile.topIn(orientation);
});
auto rightTop = std::find(rightTiles.begin(), rightTiles.end(), &tile);
auto rightBottom = rightTop;
auto leftBottom = leftTop;
Q_ASSERT(leftTop != leftTiles.end() && leftBottom != leftTiles.end());
Q_ASSERT(rightTop != rightTiles.end() && rightBottom != rightTiles.end());
// Extend top until the horizontal borders meet.
for (;;) {
const qreal dy = (*leftTop)->topIn(orientation) - (*rightTop)->topIn(orientation);
if (std::abs(dy) <= adjacentEpsilon)
break;
if (dy > 0) {
--leftTop;
} else {
--rightTop;
}
}
// Extend bottom until the horizontal borders meet.
for (;;) {
const qreal dy =
(*leftBottom)->bottomIn(orientation) - (*rightBottom)->bottomIn(orientation);
if (std::abs(dy) <= adjacentEpsilon)
break;
if (dy < 0) {
++leftBottom;
} else {
++rightBottom;
}
}
Q_ASSERT(leftBottom != leftTiles.end() && rightBottom != rightTiles.end());
++leftBottom;
++rightBottom;
for (auto p = leftTop; p != leftBottom; ++p) {
(*p)->setRightIn(orientation, position);
}
for (auto p = rightTop; p != rightBottom; ++p) {
(*p)->setLeftIn(orientation, position);
}
emit dataChanged(index(row), index(row), { ContentRectRole });
emitAllDataChanged({ ContentRectRole });
}
QRectF TileListModel::defaultContentRectForTileAt(int tileIndex) const
......@@ -203,3 +277,41 @@ void TileListModel::emitAllDataChanged(const QVector<int> &roles)
const int last = static_cast<int>(m_tiles.size()) - 1;
emit dataChanged(index(0), index(last), roles);
}
inline qreal TileListModel::Tile::leftIn(Qt::Orientation orientation) const
{
return orientation == Qt::Horizontal ? contentRect.left() : contentRect.top();
}
inline qreal TileListModel::Tile::rightIn(Qt::Orientation orientation) const
{
return orientation == Qt::Horizontal ? contentRect.right() : contentRect.bottom();
}
inline qreal TileListModel::Tile::topIn(Qt::Orientation orientation) const
{
return orientation == Qt::Horizontal ? contentRect.top() : contentRect.left();
}
inline qreal TileListModel::Tile::bottomIn(Qt::Orientation orientation) const
{
return orientation == Qt::Horizontal ? contentRect.bottom() : contentRect.right();
}
inline void TileListModel::Tile::setLeftIn(Qt::Orientation orientation, qreal pos)
{
if (orientation == Qt::Horizontal) {
contentRect.setLeft(pos);
} else {
contentRect.setTop(pos);
}
}
inline void TileListModel::Tile::setRightIn(Qt::Orientation orientation, qreal pos)
{
if (orientation == Qt::Horizontal) {
contentRect.setRight(pos);
} else {
contentRect.setBottom(pos);
}
}
......@@ -40,6 +40,14 @@ private:
QString name;
int tileIndex;
QRectF contentRect;
// Helper functions to calculate tile layout horizontally for both axes.
qreal leftIn(Qt::Orientation orientation) const;
qreal rightIn(Qt::Orientation orientation) const;
qreal topIn(Qt::Orientation orientation) const;
qreal bottomIn(Qt::Orientation orientation) const;
void setLeftIn(Qt::Orientation orientation, qreal pos);
void setRightIn(Qt::Orientation orientation, qreal pos);
};
QRectF defaultContentRectForTileAt(int tileIndex) const;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment