Reveal port items in a thin margin at machine edges

This commit is contained in:
2026-07-14 20:32:27 +02:00
parent 486296feee
commit cd75492796

View File

@@ -20,6 +20,7 @@
#include <QPainter>
#include <QPen>
#include <QPolygonF>
#include <QRegion>
#include <QStringList>
#include <QTimer>
@@ -402,12 +403,12 @@ void GameWorldView::paintGL()
painter.setRenderHint(QPainter::Antialiasing, false);
drawTiles(painter);
// Port items are drawn before the buildings so the building body occludes the
// portion still inside the footprint: items appear to slide out of the output
// port (REQ-MAT-OUTPUT-EMERGE) and into the input port (REQ-MAT-INPUT-INTAKE)
// rather than popping in or out of existence.
drawPortItems(painter);
drawBuildings(painter);
// Port items are drawn over the buildings but clipped to a thin margin at each
// machine's edges (see drawPortItems), so items appear to emerge from / sink
// into the port and stay visible while crossing directly between two touching
// buildings (REQ-MAT-OUTPUT-EMERGE, REQ-MAT-INPUT-INTAKE, REQ-MAT-DIRECT-COUPLE).
drawPortItems(painter);
drawCopyConfigFeedback(painter);
drawStations(painter);
drawBeltItems(painter);
@@ -1162,9 +1163,39 @@ void GameWorldView::drawPortItems(QPainter& painter)
{
const float halfPx = tilePx() * 0.5f * 0.5f;
// Shared with belt items (REQ-GW-TILE-SIZE): a half-tile filled square with an
// outline, occluded by the building drawn afterwards so the item slides out of
// (REQ-MAT-OUTPUT-EMERGE) or into (REQ-MAT-INPUT-INTAKE) the port.
// Port items are drawn over the buildings (drawBuildings runs first) but clipped
// to a thin margin at each machine's edges: the clip region is the whole view
// minus every machine's interior (its footprint inset by kPortMarginTiles). So a
// transiting item shows only near the port edge — appearing to emerge from / sink
// into the machine (REQ-MAT-OUTPUT-EMERGE, REQ-MAT-INPUT-INTAKE) and staying
// visible in the ~2×margin band at a seam between two touching buildings
// (REQ-MAT-DIRECT-COUPLE). Transport tiles are not machines and never occlude, so
// items on belts stay fully visible.
constexpr double kPortMarginTiles = 0.2;
const double margin = kPortMarginTiles * static_cast<double>(tilePx());
QRegion clip(rect());
for (const Building& b : m_sim->buildings().allBuildings())
{
if (b.type == BuildingType::Belt || b.type == BuildingType::Splitter
|| b.type == BuildingType::TunnelEntry || b.type == BuildingType::TunnelExit)
{
continue;
}
const std::set<QPoint, QPointCompare> cells(b.bodyCells.begin(), b.bodyCells.end());
for (const QPoint& cell : b.bodyCells)
{
// Inset an edge only where the neighbouring cell is not part of the same
// building, so interior cell seams stay filled (handles L-shaped footprints).
const double l = cells.count(cell + QPoint(-1, 0)) ? 0.0 : margin;
const double t = cells.count(cell + QPoint( 0, -1)) ? 0.0 : margin;
const double r = cells.count(cell + QPoint( 1, 0)) ? 0.0 : margin;
const double d = cells.count(cell + QPoint( 0, 1)) ? 0.0 : margin;
clip = clip.subtracted(QRegion(tileRect(cell).adjusted(l, t, -r, -d).toRect()));
}
}
// Shared with belt items (REQ-GW-TILE-SIZE): a half-tile filled square + outline.
const std::function<void(const ItemType&, QPointF)> drawItem =
[&](const ItemType& type, QPointF worldPos)
{
@@ -1175,16 +1206,19 @@ void GameWorldView::drawPortItems(QPainter& painter)
const QPointF center = worldToWidget(
QVector2D(static_cast<float>(worldPos.x()),
static_cast<float>(worldPos.y())));
const QRectF rect(center.x() - halfPx, center.y() - halfPx,
halfPx * 2, halfPx * 2);
painter.fillRect(rect, it->second.fill);
const QRectF itemRect(center.x() - halfPx, center.y() - halfPx,
halfPx * 2, halfPx * 2);
painter.fillRect(itemRect, it->second.fill);
painter.setPen(QPen(it->second.outline, 1));
painter.setBrush(Qt::NoBrush);
painter.drawRect(rect);
painter.drawRect(itemRect);
};
painter.save();
painter.setClipRegion(clip);
m_sim->buildings().forEachEmergingItem(drawItem);
m_sim->buildings().forEachIncomingItem(drawItem);
painter.restore();
}
void GameWorldView::drawBeltItems(QPainter& painter)