Skip to content
Permalink
Browse files

Another piece of random useless code in QGIS. Happy April 1st!

Type "bored" into the coordinates box (best results with a map loaded in canvas)

Click on tiles to move them to the empty space.
Click on the empty tile to toggle tile numbers.
  • Loading branch information
wonder-sk committed Apr 5, 2018
1 parent 7d65ee9 commit 63130d410ddb96fa32a94c5f3a1ea79d998a3b5b
@@ -57,6 +57,7 @@ SET(QGIS_APP_SRCS
qgsmapcanvasdockwidget.cpp
qgsmaplayerstyleguiutils.cpp
qgsmapsavedialog.cpp
qgspuzzlewidget.cpp
qgsversionmigration.cpp
qgsrulebasedlabelingwidget.cpp
qgssavestyletodbdialog.cpp
@@ -269,6 +270,7 @@ SET (QGIS_APP_MOC_HDRS
qgsmapcanvasdockwidget.h
qgsmaplayerstyleguiutils.h
qgsmapsavedialog.h
qgspuzzlewidget.h
qgsrulebasedlabelingwidget.h
qgssavestyletodbdialog.h
qgssnappinglayertreemodel.h
@@ -242,6 +242,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX();
#include "qgspluginmanager.h"
#include "qgspluginregistry.h"
#include "qgspointxy.h"
#include "qgspuzzlewidget.h"
#include "qgsruntimeprofiler.h"
#include "qgshandlebadlayers.h"
#include "qgsprintlayout.h"
@@ -1335,6 +1336,19 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
connect( QgsProject::instance(), &QgsProject::isDirtyChanged, mActionRevertProject, toggleRevert );
connect( QgsProject::instance(), &QgsProject::fileNameChanged, mActionRevertProject, toggleRevert );

// the most important part of the initialization: make sure that people can play puzzle if they need
QgsPuzzleWidget *puzzleWidget = new QgsPuzzleWidget( mMapCanvas );
mCentralContainer->insertWidget( 2, puzzleWidget );
connect( mCoordsEdit, &QgsStatusBarCoordinatesWidget::weAreBored, this, [ this, puzzleWidget ]
{
puzzleWidget->letsGetThePartyStarted();
mCentralContainer->setCurrentIndex( 2 );
} );
connect( puzzleWidget, &QgsPuzzleWidget::done, this, [ this ]
{
mCentralContainer->setCurrentIndex( 0 );
} );

} // QgisApp ctor

QgisApp::QgisApp()
@@ -0,0 +1,185 @@
/***************************************************************************
qgspuzzlewidget.cpp
--------------------------------------
Date : 1st of April 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgspuzzlewidget.h"

#include "qgsmapcanvas.h"

#include <QGraphicsPixmapItem>
#include <QMessageBox>


static bool testSolved( QVector<int> &positions )
{
for ( int i = 0; i < positions.count() - 1; ++i )
{
if ( positions[i] != i )
return false;
}
return true;
}

static int findEmpty( QVector<int> &positions )
{
for ( int i = 0; i < positions.count(); ++i )
{
if ( positions[i] == -1 )
return i;
}
Q_ASSERT( false );
return -1;
}

// half of the possible permutations lead to an unsolvable puzzle, so we initialize the puzzle by
// doing valid moves
// https://en.wikipedia.org/wiki/15_puzzle
static void shuffle( QVector<int> &positions, int count )
{
int size = sqrt( positions.count() );
int idxEmpty = findEmpty( positions );
int cEmpty = idxEmpty % size, rEmpty = idxEmpty / size;
int moveX[] = { 0, 0, 1, -1 };
int moveY[] = { 1, -1, 0, 0 };
int cOther, rOther;
for ( int i = 0; i < count; ++i )
{
do
{
int move = qrand() % 4;
cOther = cEmpty + moveX[move];
rOther = rEmpty + moveY[move];
}
while ( cOther < 0 || cOther >= size || rOther < 0 || rOther >= size );

int idxOther = rOther * size + cOther;
std::swap( positions[idxEmpty], positions[idxOther] );
idxEmpty = idxOther;
cEmpty = idxEmpty % size;
rEmpty = idxEmpty / size;
}
}

QgsPuzzleWidget::QgsPuzzleWidget( QgsMapCanvas *canvas )
: mCanvas( canvas )
{
setInteractive( false );
setBackgroundBrush( QBrush( Qt::lightGray ) );
}

void QgsPuzzleWidget::mousePressEvent( QMouseEvent *event )
{
int idxEmpty = findEmpty( mPositions );
int rEmpty = idxEmpty / mSize;
int cEmpty = idxEmpty % mSize;
int cMouse = event->pos().x() / mTileWidth;
int rMouse = event->pos().y() / mTileHeight;
int idxMouse = rMouse * mSize + cMouse;
int dx = cMouse - cEmpty;
int dy = rMouse - rEmpty;

if ( ( dx == 0 && qAbs( dy ) == 1 ) || ( dy == 0 && qAbs( dx ) == 1 ) )
{
std::swap( mPositions[idxEmpty], mPositions[idxMouse] );
updateTilePositions();
if ( testSolved( mPositions ) )
{
QMessageBox::information( nullptr, tr( "QGIS" ), tr( "Well done!\n\nNow let's get back to work, shall we?" ) );
emit done();
}
}
else if ( dx == 0 && dy == 0 )
{
// toggle text help when clicked on empty tile
for ( int i = 0; i < mTextItems.count(); ++i )
mTextItems[i]->setVisible( !mTextItems[i]->isVisible() );
}
}

void QgsPuzzleWidget::letsGetThePartyStarted()
{
mPositions.clear();
delete mScene;
mItems.clear();
mTextItems.clear();

mScene = new QGraphicsScene;

QTemporaryFile f;
f.open();
f.close();

QString filename( f.fileName() );
mCanvas->saveAsImage( filename );

QPixmap pixmap;
pixmap.load( filename );

int tileWidth = pixmap.width() / mSize;
int tileHeight = pixmap.height() / mSize;
mTileWidth = tileWidth;
mTileHeight = tileHeight;

for ( int row = 0; row < mSize; ++row )
for ( int col = 0; col < mSize; ++col )
{
QGraphicsPixmapItem *item = new QGraphicsPixmapItem;
item->setPixmap( pixmap.copy( col * tileWidth, row * tileHeight, tileWidth, tileHeight ) );
mScene->addItem( item ); // takes ownership
mItems.append( item );

QGraphicsSimpleTextItem *textItem = new QGraphicsSimpleTextItem( QString::number( row * mSize + col + 1 ), item );
QRectF textRect = textItem->boundingRect();
textItem->setPos( ( tileWidth - textRect.width() ) / 2, ( tileHeight - textRect.height() ) / 2 );
textItem->setVisible( false );
mTextItems.append( textItem );
}

// extra lines to show tile split points
for ( int i = 1; i < mSize; ++i )
{
QGraphicsLineItem *lineHorz = new QGraphicsLineItem( 0, i * tileHeight, tileWidth * mSize, i * tileHeight );
QGraphicsLineItem *lineVert = new QGraphicsLineItem( i * tileWidth, 0, i * tileWidth, tileHeight * mSize );
lineHorz->setZValue( 10 );
lineVert->setZValue( 10 );
mScene->addItem( lineHorz );
mScene->addItem( lineVert );
}

// initialize positions
for ( int i = 0; i < mSize * mSize; ++i )
mPositions << i;
mPositions[mSize * mSize - 1] = -1;
shuffle( mPositions, 1000 );

mItems[mSize * mSize - 1]->setVisible( false ); // hide item for the missing piece

updateTilePositions();

setScene( mScene );
}

void QgsPuzzleWidget::updateTilePositions()
{
for ( int i = 0; i < mSize * mSize; ++i )
{
int itemIndex = mPositions[i];
if ( itemIndex == -1 )
continue; // empty tile

int r = i / mSize, c = i % mSize;
int x = c * mTileWidth, y = r * mTileHeight;
mItems[itemIndex]->setPos( x, y );
}
}
@@ -0,0 +1,52 @@
/***************************************************************************
qgspuzzlewidget.h
--------------------------------------
Date : 1st of April 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program 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 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSPUZZLEWIDGET_H
#define QGSPUZZLEWIDGET_H

#include <QGraphicsView>

class QgsMapCanvas;


class QgsPuzzleWidget : public QGraphicsView
{
Q_OBJECT
public:
explicit QgsPuzzleWidget( QgsMapCanvas *canvas = nullptr );

protected:
void mousePressEvent( QMouseEvent *event ) override;

signals:
void done();

public slots:
void letsGetThePartyStarted();

private:
void updateTilePositions();

private:
QgsMapCanvas *mCanvas = nullptr;
QGraphicsScene *mScene = nullptr;
int mSize = 4; //!< Number of items in one row/column
int mTileWidth = 0, mTileHeight = 0;
QVector<QGraphicsItem *> mItems;
QVector<QGraphicsItem *> mTextItems;
QVector<int> mPositions; //!< Indices of items (-1 where the piece is missing)
};

#endif // QGSPUZZLEWIDGET_H
@@ -138,6 +138,11 @@ void QgsStatusBarCoordinatesWidget::validateCoordinates()
refreshMapCanvas();
return;
}
else if ( mLineEdit->text() == QStringLiteral( "bored" ) )
{
// it's friday afternoon and too late to start another piece of work...
emit weAreBored();
}

bool xOk = false;
bool yOk = false;
@@ -52,6 +52,7 @@ class APP_EXPORT QgsStatusBarCoordinatesWidget : public QWidget

signals:
void coordinatesChanged();
void weAreBored();

private slots:
void showMouseCoordinates( const QgsPointXY &p );

0 comments on commit 63130d4

Please sign in to comment.
You can’t perform that action at this time.