Restore feature: clicking collapsed nav target clears target

- clicking on collapsed group of in-space bodies containing the
  nav target clears that target (no popup displayed)
- PiGui.GetProjectedBodiesGrouped(): return additional info for
  each group; code cleanup / partial rewrite
master
cwyss 2019-12-27 19:23:27 +01:00
parent b1b64d0d6a
commit e88d8476c2
2 changed files with 102 additions and 102 deletions

View File

@ -145,16 +145,15 @@ local function displayOnScreenObjects()
local bodies_grouped = ui.getProjectedBodiesGrouped(collapse, IN_SPACE_INDICATOR_SHIP_MAX_DISTANCE)
for _,group in ipairs(bodies_grouped) do
local mainBody = group[2].body
local mainCoords = group[1].screenCoordinates
local count = #group - 1
local mainBody = group.mainBody
local mainCoords = group.screenCoordinates
ui.addIcon(mainCoords, getBodyIcon(mainBody), colors.frame, iconsize, ui.anchor.center, ui.anchor.center)
if should_show_label then
local label = mainBody:GetLabel()
if count > 1 then
label = label .. " (" .. count .. ")"
if group.multiple then
label = label .. " (" .. #group.bodies .. ")"
end
ui.addStyledText(mainCoords + Vector2(label_offset,0), ui.anchor.left, ui.anchor.center, label , colors.frame, pionillium.small)
end
@ -169,18 +168,17 @@ local function displayOnScreenObjects()
-- mouse release handler
if (mp - mainCoords):length() < click_radius then
if not ui.isAnyWindowHovered() and ui.isMouseReleased(0) then
if count == 1 then
if navTarget == mainBody then
-- if clicked and has nav target, unset nav target
player:SetNavTarget(nil)
navTarget = nil
elseif combatTarget == mainBody then
-- if clicked and has combat target, unset nav target
player:SetCombatTarget(nil)
combatTarget = nil
else
setTarget(mainBody)
end
if group.hasNavTarget then
-- if clicked and has nav target, unset nav target
player:SetNavTarget(nil)
navTarget = nil
elseif combatTarget == mainBody then
-- if clicked and has combat target, unset nav target
player:SetCombatTarget(nil)
combatTarget = nil
elseif not group.multiple then
-- clicked on single, just set navtarget/combatTarget
setTarget(mainBody)
else
-- clicked on group, show popup
ui.openPopup("navtarget" .. mainBody:GetLabel())
@ -190,35 +188,24 @@ local function displayOnScreenObjects()
-- popup content
ui.popup("navtarget" .. mainBody:GetLabel(), function()
local small_iconsize = Vector2(16,16)
ui.icon(getBodyIcon(mainBody), small_iconsize, colors.frame)
ui.sameLine()
if ui.selectable(mainBody:GetLabel(), mainBody == navTarget, {}) then
if mainBody:IsShip() then
player:SetCombatTarget(mainBody)
else
player:SetNavTarget(mainBody)
for _,b in pairs(group.bodies) do
ui.icon(getBodyIcon(b), small_iconsize, colors.frame)
ui.sameLine()
if ui.selectable(b:GetLabel(), b == navTarget, {}) then
if b:IsShip() then
player:SetCombatTarget(b)
else
player:SetNavTarget(b)
end
end
if ui.ctrlHeld() then
local target = mainBody
local target = b
if target == player:GetSetSpeedTarget() then
target = nil
end
player:SetSetSpeedTarget(target)
end
end
for _,v in pairs(group) do
if v.body and v.body~=mainBody then
ui.icon(getBodyIcon(v.body), small_iconsize, colors.frame)
ui.sameLine()
if ui.selectable(v.body:GetLabel(), v.body == navTarget, {}) then
if v.body:IsShip() then
player:SetCombatTarget(v.body)
else
player:SetNavTarget(v.body)
end
end
end
end
end)
end
end

View File

@ -1467,9 +1467,9 @@ bool first_body_is_more_important_than(Body *body, Body *other)
* Function: GetProjectedBodiesGrouped
*
* Returns all bodies visible on screen, grouped into clusters of bodies
* that are close together on screen.
* which are close together on screen.
*
* > GetProjectedBodiesGrouped(collapse, ship_max_distance)
* > groups = Engine.pigui.GetProjectedBodiesGrouped(collapse, ship_max_distance)
*
* Parameters:
*
@ -1477,6 +1477,18 @@ bool first_body_is_more_important_than(Body *body, Body *other)
*
* ship_max_distance - ships farther away than this are not included in the result
*
* Returns:
*
* groups - array of info records describing each group
*
* Fields in info record:
*
* screenCoordinates - coordinates of the geometric centre of the group
* mainBody - the main <Body> of the group
* hasNavTarget - true if group contains the player's current navigation target
* multiple - true if group consists of more than one body
* bodies - array of all <Body> objects in the group, sorted by importance
*
* Availability:
*
* 2019-12
@ -1505,78 +1517,79 @@ static int l_pigui_get_projected_bodies_grouped(lua_State *l)
filtered.back()._body = body;
}
std::vector<TSS_vector> groups;
groups.reserve(filtered.size());
struct GroupInfo {
Body *m_mainBody;
vector2d m_centreCoords; // screen space centre of group
vector3d m_worldCoordSum; // coord sum of all bodies in group
std::vector<Body *> m_bodies;
bool m_hasNavTarget;
// Perform half-matrix double for
for (TSS_vector::iterator it = filtered.begin(); it != filtered.end(); ++it) {
TSS_vector group;
group.reserve(filtered.end() - it + 1);
// First element should always be the "media";
// so push twice that element:
//printf(" Body 1 = %s (%f, %f)\n", (*it)._body->GetLabel().c_str(), (*it)._screenPosition.x, (*it)._screenPosition.y);
group.push_back((*it));
// Zeroed body so you could distinguish between a "media" element and a "real" element
group.back()._body = nullptr;
group.push_back((*it));
// Find near displayed bodies in remaining list of bodies
for (TSS_vector::iterator it2 = --filtered.end(); it2 != it;) {
//printf(" Body 2 = %s (%f, %f)\n", (*it2)._body->GetLabel().c_str(), (*it2)._screenPosition.x, (*it2)._screenPosition.y);
if ((std::abs((*group.begin())._screenPosition.x - (*it2)._screenPosition.x) > gap.x) ||
(std::abs((*group.begin())._screenPosition.y - (*it2)._screenPosition.y) > gap.y)) {
it2--;
continue;
}
//printf(" %s and %s are near!\n", (*it)._body->GetLabel().c_str(), (*it2)._body->GetLabel().c_str());
// There's a "nearest": push it on group, remove from filtered and recalc group center
group.push_back(*it2);
// nearly-swap&pop: copy last element over *it2 and...
(*it2) = filtered.back();
// ensure it2 never point past-the-end (thus to rbegin)
it2--;
// ..."pop"
filtered.pop_back();
// recalc group (starting with second element because first is the center itself)
vector3d media = std::accumulate(group.begin() + 1, group.end(), vector3d(0.0), [](const vector3d &a, const TScreenSpace &ss) {
//printf(" Third level with '%s'\n", ss._body->GetLabel().c_str());
return a + ss._body->GetPositionRelTo(Pi::player);
});
media /= double(group.size() - 1);
group.front() = lua_world_space_to_screen_space(media);
group.front()._body = nullptr; // <- just in case...
GroupInfo(Body *b, const vector2d &coords, bool isNavTarget) :
m_mainBody(b),
m_centreCoords(coords),
m_worldCoordSum(b->GetPositionRelTo(Pi::player)),
m_hasNavTarget(isNavTarget)
{
m_bodies.push_back(b);
}
};
std::vector<GroupInfo> groups;
groups.reserve(filtered.size());
const Body *nav_target = Pi::game->GetPlayer()->GetNavTarget();
for (TScreenSpace &obj : filtered) {
bool inserted = false;
for (GroupInfo &group : groups) {
if ((std::abs(group.m_centreCoords.x - obj._screenPosition.x) <= gap.x) &&
(std::abs(group.m_centreCoords.y - obj._screenPosition.y) <= gap.y)) {
// body inside group boundaries: insert into group
group.m_bodies.push_back(obj._body);
if (obj._body == nav_target)
group.m_hasNavTarget = true;
// recalc centre
group.m_worldCoordSum += obj._body->GetPositionRelTo(Pi::player);
vector3d centre = group.m_worldCoordSum / static_cast<double>(group.m_bodies.size());
group.m_centreCoords = lua_world_space_to_screen_space(centre)._screenPosition;
inserted = true;
break;
}
}
if (!inserted) {
// create new group
GroupInfo newgroup(obj._body, obj._screenPosition,
obj._body == nav_target ? true : false);
groups.push_back(std::move(newgroup));
}
groups.push_back(std::move(group));
}
// Sort each groups member according to a given function (skipping first element)
std::for_each(begin(groups), end(groups), [](TSS_vector &group) {
std::sort(begin(group) + 1, end(group), [](TScreenSpace &a, TScreenSpace &b) {
return first_body_is_more_important_than(a._body, b._body);
});
});
// Sort each groups member according to a given function
for (GroupInfo &group : groups) {
std::sort(begin(group.m_bodies), end(group.m_bodies),
[](Body *a, Body *b) {
return first_body_is_more_important_than(a, b);
});
// make most important body the main body
group.m_mainBody = group.m_bodies.front();
}
LuaTable result(l, groups.size(), 0);
int index = 1;
std::for_each(begin(groups), end(groups), [&l, &result, &index](TSS_vector &group) {
int index2 = 1;
LuaTable table_group(l, group.size(), 0);
for (GroupInfo &group : groups) {
LuaTable info_table(l, 0, 5);
LuaTable bodies_table(l, group.m_bodies.size(), 0);
std::for_each(begin(group), end(group), [&l, &table_group, &index2](TScreenSpace &on_screen_object) {
LuaTable object(l, 0, 3);
object.Set("onscreen", on_screen_object._onScreen);
object.Set("screenCoordinates", on_screen_object._screenPosition);
if (on_screen_object._body != nullptr)
object.Set("body", on_screen_object._body);
table_group.Set(index2++, object);
lua_pop(l, 1);
});
result.Set(index++, table_group);
info_table.Set("screenCoordinates", group.m_centreCoords);
info_table.Set("mainBody", group.m_mainBody);
bodies_table.LoadVector(group.m_bodies.begin(), group.m_bodies.end());
info_table.Set("bodies", bodies_table);
lua_pop(l, 1);
});
info_table.Set("multiple", group.m_bodies.size() > 1 ? true : false);
info_table.Set("hasNavTarget", group.m_hasNavTarget);
result.Set(index++, info_table);
lua_pop(l, 1);
}
LuaPush(l, result);
return 1;
}