173 lines
4.5 KiB
C++
173 lines
4.5 KiB
C++
#include "SurfaceMask.h"
|
|
|
|
#include <algorithm>
|
|
#include <climits>
|
|
|
|
namespace
|
|
{
|
|
|
|
// Rotate direction character 90° clockwise.
|
|
char rotateDirCharCW(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '>': return 'v';
|
|
case 'v': return '<';
|
|
case '<': return '^';
|
|
case '^': return '>';
|
|
default: return c;
|
|
}
|
|
}
|
|
|
|
// Rotate the character grid 90° clockwise.
|
|
// Input grid[row][col]. Returns a new grid with swapped dimensions.
|
|
std::vector<std::string> rotateCW(const std::vector<std::string>& grid)
|
|
{
|
|
if (grid.empty())
|
|
{
|
|
return {};
|
|
}
|
|
|
|
const int srcH = static_cast<int>(grid.size());
|
|
// Pad all rows to the same width.
|
|
int srcW = 0;
|
|
for (const std::string& row : grid)
|
|
{
|
|
const int w = static_cast<int>(row.size());
|
|
if (w > srcW)
|
|
{
|
|
srcW = w;
|
|
}
|
|
}
|
|
|
|
// After 90° CW: new width = srcH, new height = srcW.
|
|
const int dstW = srcH;
|
|
const int dstH = srcW;
|
|
std::vector<std::string> dst(dstH, std::string(dstW, ' '));
|
|
|
|
for (int row = 0; row < srcH; ++row)
|
|
{
|
|
for (int col = 0; col < srcW; ++col)
|
|
{
|
|
const char ch = (col < static_cast<int>(grid[row].size()))
|
|
? grid[row][col]
|
|
: ' ';
|
|
// 90° CW mapping: (col, row) -> (dstCol = srcH-1-row, dstRow = col)
|
|
const int dstCol = srcH - 1 - row;
|
|
const int dstRow = col;
|
|
dst[dstRow][dstCol] = rotateDirCharCW(ch);
|
|
}
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ParsedSurfaceMask parseSurfaceMask(const std::vector<std::string>& rows,
|
|
Rotation rotation)
|
|
{
|
|
// Number of 90° CW steps to apply.
|
|
int cwSteps = 0;
|
|
switch (rotation)
|
|
{
|
|
case Rotation::East: cwSteps = 0; break;
|
|
case Rotation::South: cwSteps = 1; break;
|
|
case Rotation::West: cwSteps = 2; break;
|
|
case Rotation::North: cwSteps = 3; break;
|
|
}
|
|
|
|
// Apply rotations.
|
|
std::vector<std::string> grid = rows;
|
|
for (int i = 0; i < cwSteps; ++i)
|
|
{
|
|
grid = rotateCW(grid);
|
|
}
|
|
|
|
// Scan grid: collect body cells, ship dock cells, and output port indicators.
|
|
std::vector<QPoint> rawBodyCells;
|
|
std::vector<QPoint> rawShipDockCells;
|
|
struct RawPort
|
|
{
|
|
QPoint tile;
|
|
Rotation direction;
|
|
};
|
|
std::vector<RawPort> rawPorts;
|
|
|
|
for (int row = 0; row < static_cast<int>(grid.size()); ++row)
|
|
{
|
|
for (int col = 0; col < static_cast<int>(grid[row].size()); ++col)
|
|
{
|
|
const char ch = grid[row][col];
|
|
if (ch == 'A')
|
|
{
|
|
rawBodyCells.push_back(QPoint(col, row));
|
|
}
|
|
else if (ch == 'S')
|
|
{
|
|
rawBodyCells.push_back(QPoint(col, row));
|
|
rawShipDockCells.push_back(QPoint(col, row));
|
|
}
|
|
else if (ch == '>')
|
|
{
|
|
rawPorts.push_back({QPoint(col, row), Rotation::East});
|
|
}
|
|
else if (ch == '<')
|
|
{
|
|
rawPorts.push_back({QPoint(col, row), Rotation::West});
|
|
}
|
|
else if (ch == '^')
|
|
{
|
|
rawPorts.push_back({QPoint(col, row), Rotation::North});
|
|
}
|
|
else if (ch == 'v')
|
|
{
|
|
rawPorts.push_back({QPoint(col, row), Rotation::South});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compute bounding box of body cells and normalize to (0,0).
|
|
int minCol = INT_MAX;
|
|
int minRow = INT_MAX;
|
|
int maxCol = INT_MIN;
|
|
int maxRow = INT_MIN;
|
|
|
|
for (const QPoint& pt : rawBodyCells)
|
|
{
|
|
if (pt.x() < minCol) { minCol = pt.x(); }
|
|
if (pt.x() > maxCol) { maxCol = pt.x(); }
|
|
if (pt.y() < minRow) { minRow = pt.y(); }
|
|
if (pt.y() > maxRow) { maxRow = pt.y(); }
|
|
}
|
|
|
|
// If there are no body cells, return an empty mask.
|
|
if (rawBodyCells.empty())
|
|
{
|
|
return ParsedSurfaceMask{};
|
|
}
|
|
|
|
const QPoint offset(-minCol, -minRow);
|
|
|
|
ParsedSurfaceMask result;
|
|
result.footprint = QSize(maxCol - minCol + 1, maxRow - minRow + 1);
|
|
|
|
for (const QPoint& pt : rawBodyCells)
|
|
{
|
|
result.bodyCells.push_back(pt + offset);
|
|
}
|
|
for (const QPoint& pt : rawShipDockCells)
|
|
{
|
|
result.shipDockCells.push_back(pt + offset);
|
|
}
|
|
for (const RawPort& rp : rawPorts)
|
|
{
|
|
Port port;
|
|
port.tile = rp.tile + offset;
|
|
port.direction = rp.direction;
|
|
result.outputPorts.push_back(port);
|
|
}
|
|
|
|
return result;
|
|
}
|