/*************************************************************************** * Copyright (C) 2025 by Piotr Wilczynski - delwing@gmail.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. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "CustomLineSession.h" #include "T2DMap.h" #include "TMap.h" #include "TRoom.h" #include "TRoomDB.h" #include CustomLineSession::CustomLineSession(T2DMap& mapWidget) : mMapWidget(mapWidget) { } bool CustomLineSession::isSnapToGridEnabled() const { return mSnapToGridEnabled; } void CustomLineSession::setSnapToGridEnabled(bool enabled) { if (mSnapToGridEnabled == enabled) { return; } if (enabled) { restoreOriginalLineIfNeeded(); const auto key = currentLineKey(); if (!key) { mSnapToGridEnabled = enabled; return; } TRoom* room = nullptr; QList* points = resolveLinePoints(*key, room); if (!points || !room) { mSnapToGridEnabled = enabled; return; } rememberOriginalLine(*key, *points); snapLineToGrid(*key, *points, *room); } else { restoreOriginalLineIfNeeded(); mSnapToGridEnabled = enabled; if (mMapWidget.mpMap) { mMapWidget.repaint(); } mOriginalLine.reset(); return; } mSnapToGridEnabled = enabled; } QPointF CustomLineSession::snapPointToGrid(const QPointF& point) const { constexpr qreal snapIncrement = 0.5; const qreal snappedX = std::round(point.x() / snapIncrement) * snapIncrement; const qreal snappedY = std::round(point.y() / snapIncrement) * snapIncrement; return QPointF(snappedX, snappedY); } bool CustomLineSession::canMoveSelectedCustomLineLastPointToTargetRoom() const { if (!mMapWidget.mpMap || !mMapWidget.mpMap->mpRoomDB) { return false; } if (mMapWidget.mCustomLineSelectedRoom <= 0 || mMapWidget.mCustomLineSelectedExit.isEmpty()) { return false; } TRoom* room = mMapWidget.mpMap->mpRoomDB->getRoom(mMapWidget.mCustomLineSelectedRoom); if (!room) { return false; } return canMoveCustomLineLastPointToTargetRoom(*room, mMapWidget.mCustomLineSelectedExit); } bool CustomLineSession::canMoveCustomLineLastPointToTargetRoom(const TRoom& room, const QString& exitKey) const { if (!mMapWidget.mpMap || !mMapWidget.mpMap->mpRoomDB) { return false; } const auto itLine = room.customLines.constFind(exitKey); if (itLine == room.customLines.constEnd()) { return false; } const QList& points = itLine.value(); if (points.isEmpty()) { return false; } const auto targetRoomId = resolveCustomLineTargetRoomId(room, exitKey); if (!targetRoomId || *targetRoomId <= 0) { return false; } return mMapWidget.mpMap->mpRoomDB->getRoom(*targetRoomId) != nullptr; } void CustomLineSession::moveCustomLineLastPointToTargetRoom() { if (!mMapWidget.mpMap || !mMapWidget.mpMap->mpRoomDB) { return; } const auto key = currentLineKey(); if (!key) { return; } TRoom* room = nullptr; QList* points = resolveLinePoints(*key, room); if (!points || !room || points->isEmpty()) { return; } const auto targetRoomId = resolveCustomLineTargetRoomId(*room, key->exitKey); if (!targetRoomId || *targetRoomId <= 0) { return; } TRoom* targetRoom = mMapWidget.mpMap->mpRoomDB->getRoom(*targetRoomId); if (!targetRoom) { return; } QPointF newPoint(targetRoom->x(), targetRoom->y()); if (mSnapToGridEnabled) { newPoint = snapPointToGrid(newPoint); } QPointF& lastPoint = points->last(); if (qFuzzyIsNull(lastPoint.x() - newPoint.x()) && qFuzzyIsNull(lastPoint.y() - newPoint.y())) { return; } lastPoint = newPoint; room->calcRoomDimensions(); mMapWidget.repaint(); if (mMapWidget.mpMap) { mMapWidget.mpMap->setUnsaved(__func__); } } void CustomLineSession::clearOriginalPoints() { mOriginalLine.reset(); } std::optional CustomLineSession::currentLineKey() const { if (mMapWidget.mCustomLineSelectedRoom > 0 && !mMapWidget.mCustomLineSelectedExit.isEmpty()) { return LineKey{mMapWidget.mCustomLineSelectedRoom, mMapWidget.mCustomLineSelectedExit}; } if (mMapWidget.mCustomLinesRoomFrom > 0 && !mMapWidget.mCustomLinesRoomExit.isEmpty()) { return LineKey{mMapWidget.mCustomLinesRoomFrom, mMapWidget.mCustomLinesRoomExit}; } return std::nullopt; } QList* CustomLineSession::resolveLinePoints(const LineKey& key, TRoom*& room) const { if (!key.isValid() || !mMapWidget.mpMap || !mMapWidget.mpMap->mpRoomDB) { return nullptr; } room = mMapWidget.mpMap->mpRoomDB->getRoom(key.roomId); if (!room) { return nullptr; } auto itLine = room->customLines.find(key.exitKey); if (itLine == room->customLines.end()) { return nullptr; } return &itLine.value(); } void CustomLineSession::rememberOriginalLine(const LineKey& key, const QList& points) { if (!key.isValid()) { mOriginalLine.reset(); return; } mOriginalLine = OriginalLine{key, points}; } void CustomLineSession::snapLineToGrid(LineKey key, QList& points, TRoom& room) { if (!key.isValid()) { return; } bool changed = false; for (QPointF& point : points) { const QPointF snapped = snapPointToGrid(point); if (!qFuzzyIsNull(point.x() - snapped.x()) || !qFuzzyIsNull(point.y() - snapped.y())) { point = snapped; changed = true; } } if (changed) { room.calcRoomDimensions(); mMapWidget.repaint(); if (mMapWidget.mpMap) { mMapWidget.mpMap->setUnsaved(__func__); } } } void CustomLineSession::restoreOriginalLineIfNeeded() { if (!mOriginalLine) { return; } const auto key = currentLineKey(); if (!key || !(mOriginalLine->key == *key)) { mOriginalLine.reset(); return; } TRoom* room = nullptr; QList* points = resolveLinePoints(mOriginalLine->key, room); if (!points || !room) { mOriginalLine.reset(); return; } if (*points == mOriginalLine->points) { mOriginalLine.reset(); return; } *points = mOriginalLine->points; room->calcRoomDimensions(); mMapWidget.repaint(); if (mMapWidget.mpMap) { mMapWidget.mpMap->setUnsaved(__func__); } mOriginalLine.reset(); } std::optional CustomLineSession::resolveCustomLineTargetRoomId(const TRoom& room, const QString& exitKey) const { if (exitKey == QLatin1String("nw")) { return room.getNorthwest(); } if (exitKey == QLatin1String("n")) { return room.getNorth(); } if (exitKey == QLatin1String("ne")) { return room.getNortheast(); } if (exitKey == QLatin1String("up")) { return room.getUp(); } if (exitKey == QLatin1String("w")) { return room.getWest(); } if (exitKey == QLatin1String("e")) { return room.getEast(); } if (exitKey == QLatin1String("down")) { return room.getDown(); } if (exitKey == QLatin1String("sw")) { return room.getSouthwest(); } if (exitKey == QLatin1String("s")) { return room.getSouth(); } if (exitKey == QLatin1String("se")) { return room.getSoutheast(); } if (exitKey == QLatin1String("in")) { return room.getIn(); } if (exitKey == QLatin1String("out")) { return room.getOut(); } const auto& specialExits = room.getSpecialExits(); const auto itSpecial = specialExits.constFind(exitKey); if (itSpecial != specialExits.constEnd()) { return itSpecial.value(); } return std::nullopt; }