From cd75492796dfb6be1738a00963b7555502f067db Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Tue, 14 Jul 2026 20:32:27 +0200 Subject: [PATCH] Reveal port items in a thin margin at machine edges --- src/ui/GameWorldView.cpp | 58 +++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/ui/GameWorldView.cpp b/src/ui/GameWorldView.cpp index 71cc639..9dd3008 100644 --- a/src/ui/GameWorldView.cpp +++ b/src/ui/GameWorldView.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -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(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 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 drawItem = [&](const ItemType& type, QPointF worldPos) { @@ -1175,16 +1206,19 @@ void GameWorldView::drawPortItems(QPainter& painter) const QPointF center = worldToWidget( QVector2D(static_cast(worldPos.x()), static_cast(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)