File indexing completed on 2024-04-28 07:54:26

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