File indexing completed on 2022-09-27 13:19:28

0001 /*
0002     SPDX-FileCopyrightText: 2006 Matthew Williams <matt@milliams.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "gameboardscene.h"
0008 
0009 #include "highlightanimation.h"
0010 
0011 #include <cmath>
0012 
0013 #include <QGraphicsSceneMouseEvent>
0014 #include <QGraphicsEllipseItem>
0015 
0016 #include <QDebug>
0017 
0018 GameBoardScene::GameBoardScene(int newWidth, int newHeight, QObject *parent) : QGraphicsScene(parent), width(newWidth), height(newHeight), acceptEvents(true)
0019 {
0020     ////qDebug() << "GameBoardScene::GameBoardScene()";
0021 
0022     for (int i = 0; i < (2 * width * height + width + height); i++) {
0023         lineList.append(false); //simply fill array with 'false's
0024     }
0025 
0026     spacing = 40;   //this hard coding doesn't matter since auto-resizing exists :)
0027     for (int iWidth = 0; iWidth <= width; iWidth++) {
0028         for (int iHeight = 0; iHeight <= height; iHeight++) {
0029             int x = iWidth * spacing;
0030             int y = iHeight * spacing;
0031             auto dot = new QGraphicsEllipseItem(QRectF(-2, -2, 4, 4));
0032             dot->moveBy(x, y);
0033             dot->setBrush(Qt::SolidPattern);
0034             dot->setZValue(20); // set the elevation, the dot's are on top
0035             addItem(dot);
0036         }
0037     }
0038     QPen feintPen(Qt::DotLine); //for the guidelines between dots
0039     feintPen.setWidth(1);
0040     feintPen.setColor(Qt::lightGray);
0041     for (int iWidth = 0; iWidth <= width; iWidth++) {
0042         addLine(QLineF(spacing * iWidth, 0, spacing * iWidth, spacing * height), feintPen);
0043     }
0044     for (int iHeight = 0; iHeight <= height; iHeight++) {
0045         addLine(QLineF(0, spacing * iHeight, spacing * width, spacing * iHeight), feintPen);
0046     }
0047 
0048     setBackgroundBrush(QBrush(Qt::white));
0049 
0050     indicatorLine = new QGraphicsLineItem(1, 1, 1, 1);
0051     indicatorLine->setZValue(10);
0052     indicatorLine->setPen(QPen(Qt::yellow, 2.5));
0053     indicatorLine->hide();
0054     addItem(indicatorLine);
0055 
0056     QGraphicsEllipseItem tempItem;
0057     QGraphicsEllipseItemType = tempItem.type();
0058 
0059     qreal border = 10;
0060     QRectF rect = sceneRect();
0061     rect.setLeft(rect.left() - border);
0062     rect.setRight(rect.right() + border);
0063     rect.setTop(rect.top() - border);
0064     rect.setBottom(rect.bottom() + border);
0065     setSceneRect(rect);
0066 }
0067 
0068 GameBoardScene::~GameBoardScene()
0069 {
0070     //qDebug() << "GameBoardScene::~GameBoardScene()";
0071     delete indicatorLine;
0072 }
0073 
0074 void GameBoardScene::drawLine(int index, const QColor &colour)
0075 {
0076   auto line = new QGraphicsLineItem(lineFromIndex(index));
0077   line->setZValue(10);
0078   line->setPen(QPen(QBrush(colour), 2.5));
0079   addItem(line);          // draw new line
0080   lineList[index] = true; // keep this table in sync
0081   indicatorLine->hide();
0082   update(line->boundingRect());
0083 }
0084 
0085 void GameBoardScene::highlightLine(int index)
0086 {
0087   auto anim = new HighlightAnimation(lineFromIndex(index));
0088   anim->setZValue(9);
0089   addItem(anim);
0090 }
0091 
0092 void GameBoardScene::drawSquare(int index, const QColor &colour)
0093 {
0094     QBrush brush(colour, Qt::SolidPattern);
0095 
0096     addRect(QRectF(qreal((index % width)*spacing), qreal((index / width)*spacing), qreal(spacing), qreal(spacing)), QPen(), brush)->setZValue(-1);
0097 }
0098 
0099 int GameBoardScene::indexFromPointPair(const QList<QGraphicsEllipseItem *> &pointPair) const
0100 {
0101 
0102     if (pointPair.size() != 2) {
0103         return -1;    // if it isn't a pair
0104     }
0105 
0106     qreal pointOneX = pointPair.at(0)->scenePos().x() / spacing;
0107     qreal pointOneY = pointPair.at(0)->scenePos().y() / spacing;
0108     qreal pointTwoX = pointPair.at(1)->scenePos().x() / spacing;
0109     qreal pointTwoY = pointPair.at(1)->scenePos().y() / spacing;
0110 
0111     //this int conversion could go bad but SHOULD be safe
0112     qreal refX; // these two will be the grid-coord of the
0113     qreal refY; // to and left most point of the two
0114 
0115     int index = -1;
0116     if (pointOneX == pointTwoX) {
0117         //dir = VERTICAL;
0118         refX = pointOneX;
0119         if (pointTwoY < pointOneY) { //want the topmost point as reference
0120             refY = pointTwoY;
0121         } else {
0122             refY = pointOneY;
0123         }
0124         index = static_cast<int>(refY * ((2 * width) + 1) + refX + width);
0125     }
0126 
0127     else if (pointOneY == pointTwoY) {
0128         //dir = HORIZONTAL;
0129         refY = pointOneY;
0130         if (pointOneX < pointTwoX) { //want the leftmost point as reference
0131             refX = pointOneX;
0132         } else {
0133             refX = pointTwoX;
0134         }
0135         index = static_cast<int>(refY * ((2 * width) + 1) + refX);
0136     }
0137     return index;
0138 }
0139 
0140 QLineF GameBoardScene::lineFromIndex(int index) const
0141 {
0142     int index2 = index % ((2 * width) + 1);
0143     enum {HORIZONTAL, VERTICAL} dir;
0144     if (index2 < width) {
0145         dir = HORIZONTAL;
0146     } else {
0147         dir = VERTICAL;
0148     }
0149 
0150     int yCoordStart = (index / ((2 * width) + 1)) * spacing;
0151     int xCoordStart = 0;
0152     int yCoordEnd = 0;
0153     int xCoordEnd = 0;
0154     switch (dir) {
0155     case HORIZONTAL:
0156         xCoordStart = index2 * spacing;
0157         yCoordEnd = yCoordStart;
0158         xCoordEnd = xCoordStart + spacing;
0159         break;
0160     case VERTICAL:
0161         xCoordStart = (index2 - width) * spacing;
0162         yCoordEnd = yCoordStart + spacing;
0163         xCoordEnd = xCoordStart;
0164         break;
0165     }
0166     return QLineF(xCoordStart, yCoordStart, xCoordEnd, yCoordEnd);
0167 }
0168 
0169 bool GameBoardScene::isLineAlready(const QList<QGraphicsEllipseItem *> &pointPair) const //TODO does this work?
0170 {
0171     int index = indexFromPointPair(pointPair);
0172     if (index == -1) {
0173         return true;
0174     }
0175 
0176     return lineList.at(index);
0177 }
0178 
0179 void GameBoardScene::addLineToIndex(const QList<QGraphicsEllipseItem *> &pointPair)
0180 {
0181     int index = indexFromPointPair(pointPair);
0182     if (index == -1) {  //not a valid line since no two unique ends
0183         return;
0184     }
0185 
0186     Q_EMIT lineDrawn(index);  //addLineToIndex(index);
0187 }
0188 
0189 QList<QGraphicsEllipseItem *> GameBoardScene::getTwoNearestPoints(const QPointF &pos) const
0190 {
0191     QList<QGraphicsItem *> itemList = items();
0192     QList<QGraphicsEllipseItem *> connectList;
0193     for (int i = 0; i < itemList.size(); ++i) {
0194         if (itemList.at(i)->type() == QGraphicsEllipseItemType) {
0195             //cout << "itemList.at(i)->scenePos():" << qgraphicsitem_cast<QGraphicsEllipseItem*>(itemList.at(i))->scenePos().x() << "," << qgraphicsitem_cast<QGraphicsEllipseItem*>(itemList.at(i))->scenePos().y() << endl;
0196             QPointF dist(pos - itemList.at(i)->scenePos());
0197             qreal distMod = sqrt(dist.x() * dist.x() + dist.y() * dist.y());
0198             //if (distMod < (spacing*0.7071))   //there will only ever be either 1 or 2 items that fulfil this [0.7071 ~ 2^(-0.5)]
0199             if (distMod < spacing - 5) {
0200                 connectList << qgraphicsitem_cast<QGraphicsEllipseItem *>(itemList.at(i));
0201             }
0202         }
0203     }
0204     return connectList;
0205 }
0206 
0207 const QSize GameBoardScene::minimumSizeHint() const
0208 {
0209     return QSize((width * spacing) + 10, (height * spacing) + 10); // the +10 is to provide padding and to avoid scrollbars
0210 }
0211 
0212 /*void GameBoardScene::mousePressEvent (QGraphicsSceneMouseEvent* mouseEvent)
0213 {
0214     if (!acceptEvents) return;
0215     QGraphicsScene::mousePressEvent(mouseEvent);
0216 }*/
0217 
0218 void GameBoardScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
0219 {
0220     if (!acceptEvents) {
0221         QGraphicsScene::mouseReleaseEvent(mouseEvent);
0222         return;
0223     }
0224 
0225     //cout << "GameBoardScene::mouseReleaseEvent" << endl;
0226     if (mouseEvent->button() == Qt::LeftButton) {
0227         QList<QGraphicsEllipseItem *> connectList = getTwoNearestPoints(mouseEvent->scenePos());
0228         if (connectList.size() == 2) {
0229             addLineToIndex(connectList);
0230         }
0231     }
0232 
0233     QGraphicsScene::mouseReleaseEvent(mouseEvent);
0234 }
0235 
0236 void GameBoardScene::acknowledgeMove(int x1, int y1, int x2, int y2)
0237 {
0238     QPoint calculatedpos(((x1 + x2) / 2.0) * spacing, ((y1 + y2) / 2.0) * spacing);
0239     QList<QGraphicsEllipseItem *> connectList = getTwoNearestPoints(calculatedpos);
0240     addLineToIndex(connectList);
0241 }
0242 
0243 void GameBoardScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
0244 {
0245     if (!acceptEvents) {
0246         return;
0247     }
0248     //indicatorLine = 0;
0249 
0250     ////qDebug() << "GameBoardScene::mouseMoveEvent";
0251     ////qDebug() << "mouseEvent->scenePos():" << mouseEvent->scenePos().x() << "," << mouseEvent->scenePos().y();
0252 
0253     QList<QGraphicsEllipseItem *> connectList = getTwoNearestPoints(mouseEvent->scenePos());
0254 
0255     if (connectList.size() == 2 && !isLineAlready(connectList)) {
0256         //if there are two nearest points and there isn't already a line
0257         indicatorLine->setLine(QLineF(connectList.at(0)->scenePos(), connectList.at(1)->scenePos()));   //where
0258         indicatorLine->show();
0259     } else {
0260         indicatorLine->hide();
0261     }
0262 
0263     QGraphicsScene::mouseMoveEvent(mouseEvent);
0264 }
0265