You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

240 lines
7.1 KiB

#include "graphwidget.hpp"
#include "edge.hpp"
#include "node.hpp"
#include <graph/graph.hpp>
#include "floats.hpp"
#include <QtGui/QKeyEvent>
#include <QtGui/QWheelEvent>
#include <QtGui/QGraphicsScene>
#include <QtGui/QApplication>
#include <QtCore/QDebug>
namespace std {
template <>
struct hash<float2>
{
std::size_t operator()(const float2& f2) const
{
std::size_t h1 = std::hash<float>()(f2.x);
std::size_t h2 = std::hash<float>()(f2.y);
return h1 ^ (h2 << 1);
}
};
}
namespace {
// for the map
// bool operator< (const float2& v1, const float2& v2)
// {
// return length(v1) < length(v2);
// }
float2 inline float2FromQPointF(const QPointF& p)
{
return float2(p.x(), p.y());
}
QPointF inline QPointFFromfloat2(const float2& f)
{
return QPointF(f.x, f.y);
}
} // anonym namespace
GraphWidget::GraphWidget(Graph<float2>* graph, QWidget *p)
: QGraphicsView(p)
, m_graph(graph)
{
QGraphicsScene *s = new QGraphicsScene(this);
s->setItemIndexMethod(QGraphicsScene::NoIndex);
s->setSceneRect(-200, -200, 400, 400);
setScene(s);
setCacheMode(CacheBackground);
setViewportUpdateMode(BoundingRectViewportUpdate);
setRenderHint(QPainter::Antialiasing);
setTransformationAnchor(AnchorUnderMouse);
scale(qreal(0.8), qreal(0.8));
setMinimumSize(400, 400);
setWindowTitle(tr("Elastic Nodes"));
}
void GraphWidget::itemMoved(const QPointF oldPos, const QPointF newPos)
{
float2 old_v = float2FromQPointF(oldPos);
float2 new_v = float2FromQPointF(newPos);
// if old_pos not found, returns silently
m_graph->modifyVertex(old_v, new_v);
}
void GraphWidget::updateFromGraph()
{
for (Graph<float2>::iterator cit = m_graph->begin(); cit != m_graph->end(); ++cit) {
Node *node = new Node(this);
scene()->addItem(node);
node->setPos(QPointFFromfloat2(*cit));
}
for (Graph<float2>::iterator cit = m_graph->begin(); cit != m_graph->end(); ++cit) {
for (const auto cit2 : m_graph->neighboursOf(*cit)) {
Node* node1 = dynamic_cast<Node*>(scene()->itemAt(QPointFFromfloat2(*cit)));
Node* node2 = dynamic_cast<Node*>(scene()->itemAt(QPointFFromfloat2(cit2)));
scene()->addItem(new Edge(node1, node2));
}
}
}
void GraphWidget::keyPressEvent(QKeyEvent *e)
{
switch (e->key()) {
case Qt::Key_Plus:
zoomIn();
break;
case Qt::Key_Minus:
zoomOut();
break;
case Qt::Key_Delete: {
QList <QGraphicsItem* > selectedItems = scene()->selectedItems();
if (selectedItems.isEmpty())
break;
QGraphicsItem* selectedItem = selectedItems.first();
Node* selectedNode = dynamic_cast<Node*>(selectedItem);
const QPoint global_p = QCursor::pos();
const QPoint widget_p = mapFromGlobal(global_p);
const QPointF scene_p = mapToScene(widget_p);
/// @bug no hit deteced (hover works) on bottom right corner
QGraphicsItem* item_under_mouse = scene()->itemAt(scene_p);
Node* node_under_mouse = dynamic_cast<Node*>(item_under_mouse);
if (node_under_mouse != 0 && node_under_mouse != selectedNode) { // remove edge
const float2 source_pos = float2FromQPointF(selectedItem->pos());
const float2 destination_pos = float2FromQPointF(node_under_mouse->pos());
if (!m_graph->connected(source_pos, destination_pos))
return;
m_graph->removeEdge(source_pos, destination_pos);
QList<Edge *> edges_of_selected = selectedNode->edges();
for (Edge* edge : edges_of_selected) {
if (edge->sourceNode() == node_under_mouse || edge->destNode() == node_under_mouse) {
selectedNode->removeEdge(edge);
node_under_mouse->removeEdge(edge);
scene()->removeItem(edge);
delete edge;
return;
}
}
} else { // remove node
m_graph->removeVertex(float2FromQPointF(selectedItem->pos()));
QList<Edge *> edges_of_selected = selectedNode->edges();
for (Edge* edge : edges_of_selected) {
edge->sourceNode()->removeEdge(edge);
edge->destNode()->removeEdge(edge);
scene()->removeItem(edge);
delete edge;
}
scene()->removeItem(selectedItem);
}
}
case Qt::Key_Insert: {
QList <QGraphicsItem* > selectedItems = scene()->selectedItems();
if (selectedItems.isEmpty())
break;
QGraphicsItem* selectedItem = selectedItems.first();
Node* selectedNode = dynamic_cast<Node*>(selectedItem);
const QPoint global_p = QCursor::pos();
const QPoint widget_p = mapFromGlobal(global_p);
const QPointF scene_p = mapToScene(widget_p);
/// @bug no hit deteced (hover works) on bottom right corner
QGraphicsItem* item_under_mouse = scene()->itemAt(scene_p);
Node* node_under_mouse = dynamic_cast<Node*>(item_under_mouse);
if (node_under_mouse != 0 && node_under_mouse != selectedNode) { // insert Edge
const float2 source_pos = float2FromQPointF(selectedItem->pos());
const float2 destination_pos = float2FromQPointF(node_under_mouse->pos());
if (m_graph->connected(source_pos, destination_pos))
return;
scene()->addItem(new Edge(selectedNode, node_under_mouse));
m_graph->addEdge(source_pos, destination_pos);
} else { // insert new node
Node *node = new Node(this);
scene()->addItem(node);
node->setPos(scene_p.x(), scene_p.y());
scene()->addItem(new Edge(selectedNode, node));
const float2 source_pos = float2FromQPointF(selectedItem->pos());
const float2 destination_pos = float2FromQPointF(scene_p);
m_graph->addEdge(source_pos, destination_pos);
selectedItem->setSelected(false);
node->setSelected(true);
}
}
default:
QGraphicsView::keyPressEvent(e);
}
}
void GraphWidget::wheelEvent(QWheelEvent *e)
{
scaleView(pow((double)2, -e->delta() / 240.0));
}
void GraphWidget::drawBackground(QPainter *painter, const QRectF &r)
{
// Shadow
QRectF scene_rect = this->sceneRect();
QRectF rightShadow(scene_rect.right(), scene_rect.top() + 5, 5, scene_rect.height());
QRectF bottomShadow(scene_rect.left() + 5, scene_rect.bottom(), scene_rect.width(), 5);
if (rightShadow.intersects(r) || rightShadow.contains(r))
painter->fillRect(rightShadow, Qt::darkGray);
if (bottomShadow.intersects(r) || bottomShadow.contains(r))
painter->fillRect(bottomShadow, Qt::darkGray);
// Fill
QLinearGradient gradient(scene_rect.topLeft(), scene_rect.bottomRight());
gradient.setColorAt(0, Qt::white);
gradient.setColorAt(1, Qt::lightGray);
painter->fillRect(r.intersect(scene_rect), gradient);
painter->setBrush(Qt::NoBrush);
painter->drawRect(scene_rect);
}
void GraphWidget::scaleView(qreal scaleFactor)
{
qreal factor = transform().scale(scaleFactor, scaleFactor).mapRect(QRectF(0, 0, 1, 1)).width();
if (factor < 0.07 || factor > 100)
return;
scale(scaleFactor, scaleFactor);
}
void GraphWidget::zoomIn()
{
scaleView(qreal(1.2));
}
void GraphWidget::zoomOut()
{
scaleView(1 / qreal(1.2));
}
void GraphWidget::quit()
{
QApplication::quit();
}