116 lines
4.0 KiB
C++
116 lines
4.0 KiB
C++
#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<qreal>(coordinates.getTilePx()),
|
|
footprint.height() * static_cast<qreal>(coordinates.getTilePx()));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
std::optional<QRectF> 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<QRectF> getActorWidgetRect(const EntityAdmin& admin,
|
|
const WorldCoordinates& coordinates,
|
|
entt::entity actor)
|
|
{
|
|
if (!admin.isValid(actor))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
if (admin.hasAll<StationBodyComponent>(actor))
|
|
{
|
|
const StationBodyComponent& body = admin.get<StationBodyComponent>(actor);
|
|
return getFootprintWidgetRect(coordinates, body.anchor, body.footprint);
|
|
}
|
|
if (admin.hasAll<PositionComponent>(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<PositionComponent>(actor).value);
|
|
const qreal extent = static_cast<qreal>(getShipForwardExtentPx(coordinates));
|
|
return QRectF(center.x() - extent, center.y() - extent,
|
|
2.0 * extent, 2.0 * extent);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<QRectF> getDebrisWidgetRect(const EntityAdmin& admin,
|
|
const WorldCoordinates& coordinates,
|
|
entt::entity debris)
|
|
{
|
|
if (!admin.isValid(debris) || !admin.hasAll<PositionComponent, DebrisComponent>(debris))
|
|
{
|
|
return std::nullopt;
|
|
}
|
|
const QPointF center =
|
|
coordinates.worldToWidget(admin.get<PositionComponent>(debris).value);
|
|
const qreal radius = static_cast<qreal>(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<BuildingId>& buildings,
|
|
const std::vector<entt::entity>& actors,
|
|
const std::vector<entt::entity>& 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<QRectF>& 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();
|
|
}
|