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 <QPainter>
#include <QPen> #include <QPen>
#include <QPolygonF> #include <QPolygonF>
#include <QRegion>
#include <QStringList> #include <QStringList>
#include <QTimer> #include <QTimer>
@@ -402,12 +403,12 @@ void GameWorldView::paintGL()
painter.setRenderHint(QPainter::Antialiasing, false); painter.setRenderHint(QPainter::Antialiasing, false);
drawTiles(painter); 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); 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); drawCopyConfigFeedback(painter);
drawStations(painter); drawStations(painter);
drawBeltItems(painter); drawBeltItems(painter);
@@ -1162,9 +1163,39 @@ void GameWorldView::drawPortItems(QPainter& painter)
{ {
const float halfPx = tilePx() * 0.5f * 0.5f; const float halfPx = tilePx() * 0.5f * 0.5f;
// Shared with belt items (REQ-GW-TILE-SIZE): a half-tile filled square with an // Port items are drawn over the buildings (drawBuildings runs first) but clipped
// outline, occluded by the building drawn afterwards so the item slides out of // to a thin margin at each machine's edges: the clip region is the whole view
// (REQ-MAT-OUTPUT-EMERGE) or into (REQ-MAT-INPUT-INTAKE) the port. // 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 std::function<void(const ItemType&, QPointF)> drawItem =
[&](const ItemType& type, QPointF worldPos) [&](const ItemType& type, QPointF worldPos)
{ {
@@ -1175,16 +1206,19 @@ void GameWorldView::drawPortItems(QPainter& painter)
const QPointF center = worldToWidget( const QPointF center = worldToWidget(
QVector2D(static_cast<float>(worldPos.x()), QVector2D(static_cast<float>(worldPos.x()),
static_cast<float>(worldPos.y()))); static_cast<float>(worldPos.y())));
const QRectF rect(center.x() - halfPx, center.y() - halfPx, const QRectF itemRect(center.x() - halfPx, center.y() - halfPx,
halfPx * 2, halfPx * 2); halfPx * 2, halfPx * 2);
painter.fillRect(rect, it->second.fill); painter.fillRect(itemRect, it->second.fill);
painter.setPen(QPen(it->second.outline, 1)); painter.setPen(QPen(it->second.outline, 1));
painter.setBrush(Qt::NoBrush); painter.setBrush(Qt::NoBrush);
painter.drawRect(rect); painter.drawRect(itemRect);
}; };
painter.save();
painter.setClipRegion(clip);
m_sim->buildings().forEachEmergingItem(drawItem); m_sim->buildings().forEachEmergingItem(drawItem);
m_sim->buildings().forEachIncomingItem(drawItem); m_sim->buildings().forEachIncomingItem(drawItem);
painter.restore();
} }
void GameWorldView::drawBeltItems(QPainter& painter) void GameWorldView::drawBeltItems(QPainter& painter)