#include "SelectionBounds.h" #include "Building.h" #include "DebrisComponent.h" #include "EntityAdmin.h" #include "FactoryQueries.h" #include "PositionComponent.h" #include "Simulation.h" #include "StationBodyComponent.h" #include "WorldCoordinates.h" #include "WorldPrimitives.h" namespace { // The widget rectangle of a footprint anchored at a tile, both kinds of body being // described the same way. QRectF getFootprintWidgetRect(const WorldCoordinates& coordinates, QPoint anchor, QSize footprint) { const QPointF topLeft = coordinates.tileToWidget(anchor); return QRectF(topLeft.x(), topLeft.y(), footprint.width() * static_cast(coordinates.getTilePx()), footprint.height() * static_cast(coordinates.getTilePx())); } } // namespace std::optional getBuildingWidgetRect(const FactoryState& state, const WorldCoordinates& coordinates, BuildingId id) { if (const Building* building = findBuilding(state, id)) { return getFootprintWidgetRect(coordinates, building->anchor, building->footprint); } if (const ConstructionSite* site = findSite(state, id)) { return getFootprintWidgetRect(coordinates, site->anchor, site->footprint); } return std::nullopt; } std::optional getActorWidgetRect(const EntityAdmin& admin, const WorldCoordinates& coordinates, entt::entity actor) { if (!admin.isValid(actor)) { return std::nullopt; } if (admin.hasAll(actor)) { const StationBodyComponent& body = admin.get(actor); return getFootprintWidgetRect(coordinates, body.anchor, body.footprint); } if (admin.hasAll(actor)) { // A ship is a triangle about its center; the square its longest extent fits in // is what the panel is placed beside. const QPointF center = coordinates.worldToWidget(admin.get(actor).value); const qreal extent = static_cast(getShipForwardExtentPx(coordinates)); return QRectF(center.x() - extent, center.y() - extent, 2.0 * extent, 2.0 * extent); } return std::nullopt; } std::optional getDebrisWidgetRect(const EntityAdmin& admin, const WorldCoordinates& coordinates, entt::entity debris) { if (!admin.isValid(debris) || !admin.hasAll(debris)) { return std::nullopt; } const QPointF center = coordinates.worldToWidget(admin.get(debris).value); const qreal radius = static_cast(getDebrisRadiusPx(coordinates)); return QRectF(center.x() - radius, center.y() - radius, 2.0 * radius, 2.0 * radius); } QRect getSelectionWidgetRect(const Simulation& sim, const WorldCoordinates& coordinates, const std::vector& buildings, const std::vector& actors, const std::vector& debris) { QRectF bounds; // A null rect unites to nothing of its own, so the first object found sets the box // and every later one grows it. auto add = [&bounds](const std::optional& rect) { if (rect.has_value()) { bounds = bounds.isNull() ? *rect : bounds.united(*rect); } }; for (BuildingId id : buildings) { add(getBuildingWidgetRect(sim.getFactoryState(), coordinates, id)); } for (entt::entity actor : actors) { add(getActorWidgetRect(sim.getAdmin(), coordinates, actor)); } for (entt::entity piece : debris) { add(getDebrisWidgetRect(sim.getAdmin(), coordinates, piece)); } return bounds.isNull() ? QRect() : bounds.toAlignedRect(); }