17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-05 22:59:10 +01:00

feat(editor): the hectometre posts go by the kilometrage of the line where the Kilometrage tool sets it, its jumps too, a post at a jump of the km on from it or of the km it is reached at, the km given at the point still there as a choice; the posts standing go with the place of their number nearest to them

This commit is contained in:
maj00r
2026-10-22 14:33:18 +02:00
parent 7cc8e5bb9a
commit 435b80eb55
6 changed files with 240 additions and 101 deletions

View File

@@ -163,51 +163,22 @@ void km_tool::refresh()
auto const high{axis->anchors.back().chainage + 1.0};
for (auto const chainage : axis->chainage)
entry.guessed.push_back(chainage < low || chainage > high ? 1 : 0);
std::vector<double> jumps;
for (auto const &anchor : axis->anchors)
{
if (std::abs(anchor.after - anchor.before) < 1e-6)
continue;
jumps.push_back(anchor.chainage);
auto const way{kilometrage::direction_on(*axis, anchor.chainage)};
entry.ticks.push_back({kilometrage::point_on(*axis, anchor.chainage), {-way.y, 0.0, way.x}, 0, true});
}
// a tick at each hectometre the km pass along the axis, none at a jump
auto const hectometres_along = [&](std::vector<glm::dvec3> const &Points, std::vector<double> const &Km, std::vector<double> const &Chainage, std::vector<tick> &Ticks) {
for (std::size_t i = 1; i < Points.size(); ++i)
if (std::abs(anchor.after - anchor.before) >= 1e-6)
{
auto const from{std::min(Chainage[i - 1], Chainage[i])};
auto const to{std::max(Chainage[i - 1], Chainage[i])};
if (std::any_of(jumps.begin(), jumps.end(), [&](double const Jump) { return Jump >= from - 1e-6 && Jump <= to + 1e-6; }))
continue;
auto const k0{Km[i - 1]};
auto const k1{Km[i]};
if (std::abs(k1 - k0) < 1e-9)
continue;
auto way{plan_of(Points[i] - Points[i - 1])};
way = glm::length(way) > 1e-9 ? glm::normalize(way) : glm::dvec2{0.0, 1.0};
auto const first{static_cast<int>(std::floor(std::min(k0, k1) * 10.0 + 1e-9)) + 1};
auto const last{static_cast<int>(std::floor(std::max(k0, k1) * 10.0 + 1e-9))};
for (auto hectometres = first; hectometres <= last && hectometres < first + 10; ++hectometres)
Ticks.push_back({glm::mix(Points[i - 1], Points[i], (hectometres / 10.0 - k0) / (k1 - k0)), {-way.y, 0.0, way.x}, hectometres, false});
auto const way{kilometrage::direction_on(*axis, anchor.chainage)};
entry.ticks.push_back({kilometrage::point_on(*axis, anchor.chainage), {-way.y, 0.0, way.x}, 0, true});
}
};
std::vector<double> km;
for (auto const chainage : axis->chainage)
km.push_back(kilometrage::km_on(*axis, chainage));
hectometres_along(axis->points, km, axis->chainage, entry.ticks);
for (auto const &hectometre : kilometrage::hectometres(*axis))
entry.ticks.push_back({hectometre.at, {-hectometre.rising.y, 0.0, hectometre.rising.x}, hectometre.number, false});
m_drawn.push_back(std::move(entry));
// the shorter track where the tracks run apart, with its irregular hectometre
// the shorter track where the tracks run apart
for (auto const &side : axis->sides)
{
drawn apart;
apart.line = line;
apart.points = side.points;
apart.guessed.assign(side.points.size(), 0);
km.clear();
for (auto const along : side.along)
km.push_back(kilometrage::km_on(*axis, side, along));
hectometres_along(side.points, km, side.chainage, apart.ticks);
m_drawn.push_back(std::move(apart));
}
}
@@ -303,18 +274,8 @@ void km_tool::pick(TTrack &Track, glm::dvec3 const &Point)
else
{
// the line the point lies on, and its km there
auto best{kReadoutReach};
for (auto const &line : kilometrage::lines())
if (auto const *axis{kilometrage::axis_of(line)}; axis != nullptr)
{
double gap{0.0};
kilometrage::chainage_at(*axis, entry.point, gap);
if (gap < best)
{
best = gap;
m_line = line;
}
}
if (auto const line{kilometrage::line_at(entry.point, kReadoutReach)}; false == line.empty())
m_line = line;
double km{0.0};
if (false == m_line.empty() && kilometrage::km_at(m_line, entry.point, km, kReadoutReach))
{

View File

@@ -16,6 +16,7 @@ http://mozilla.org/MPL/2.0/.
#include "editor/editorFormat.hpp"
#include "editor/editorGeometry.hpp"
#include "editor/editorIncludeInfo.hpp"
#include "editor/editorKilometrage.hpp"
#include "scene/scene.h"
#include "scene/scenelayers.h"
@@ -442,61 +443,97 @@ void hekto_tool::hekto_update()
continue;
tool.found.push_back(post);
}
if (false == tool.km_read)
tool.line_set = kilometrage::line_at(tool.point);
tool.line = tool.own_km ? std::string{} : tool.line_set;
if (tool.line.empty() && false == tool.km_read)
{
tool.km_read = true;
hekto_read_kilometrage();
}
auto const chainage_of = [&](int const Hectometres) { return tool.origin + tool.growth * (Hectometres / 10.0 - tool.km) * 1000.0; };
for (auto &post : tool.found)
{
post.expected = chainage_of(post.hectometres);
post.error = post.chainage - post.expected;
post.outside = true;
}
auto const km_at = [&](double const Chainage) { return tool.km + tool.growth * (Chainage - tool.origin) / 1000.0; };
auto const low{std::max(0, static_cast<int>(std::ceil(std::min(km_at(0.0), km_at(length)) * 10.0 - 1e-6)))};
auto const high{static_cast<int>(std::floor(std::max(km_at(0.0), km_at(length)) * 10.0 + 1e-6))};
for (int hectometres = low; hectometres <= high && tool.posts.size() < 5000; ++hectometres)
{
hekto_post post;
post.hectometres = hectometres;
post.chainage = chainage_of(hectometres);
if (post.chainage < 0.0 || post.chainage > length)
continue;
auto const axis{editor_track::sampled_position(tool.samples, post.chainage)};
auto const growing{sampled_direction(tool.samples, post.chainage) * static_cast<double>(tool.growth)};
auto const place = [&](hekto_post &Post) {
auto const axis{editor_track::sampled_position(tool.samples, Post.chainage)};
auto const growing{sampled_direction(tool.samples, Post.chainage) * static_cast<double>(Post.growth)};
glm::dvec2 const left{-growing.y, growing.x};
// alternately: the even hectometres on the right, the odd ones on the left of the growing kilometrage
auto const side{tool.side != 0 ? tool.side : (hectometres % 2 == 0 ? 1 : -1)};
auto const side{tool.side != 0 ? tool.side : (Post.hectometres % 2 == 0 ? 1 : -1)};
auto const planar{plan_of(axis) + left * (side * tool.offset)};
post.position = m_ground.placement_on_ground({planar.x, axis.y, planar.y});
post.yaw = std::fmod(heading_of(growing) + tool.turn, 360.0);
// of the posts with this number the one nearest to the place stands for it, the others are duplicates. it stands wrong
// when it's farther from the place than allowed: along the line, across it, or on the other side
auto best{std::numeric_limits<double>::max()};
for (int i = 0; i < static_cast<int>(tool.found.size()); ++i)
Post.position = m_ground.placement_on_ground({planar.x, axis.y, planar.y});
Post.yaw = std::fmod(heading_of(growing) + tool.turn, 360.0);
tool.posts.push_back(Post);
};
auto const *axis{tool.line.empty() ? nullptr : kilometrage::axis_of(tool.line)};
if (axis != nullptr)
{
// the hectometres of the kilometrage of the line, the jumps in it too
double km{0.0};
if (kilometrage::km_at(tool.line, tool.point, km))
tool.km = std::round(km * 1000.0) / 1000.0;
tool.growth = kilometrage::growth_at(tool.line, tool.point, sampled_direction(tool.samples, tool.origin)) < 0 ? -1 : 1;
for (auto const &hectometre : kilometrage::hectometres(*axis))
{
auto &found{tool.found[i]};
if (found.hectometres != hectometres)
hekto_post post;
post.hectometres = hectometre.number;
post.chainage = project(tool.samples, hectometre.at, distance);
if (distance > kLineCorridor || post.chainage <= 0.0 || post.chainage >= length || hectometre.number < 0 || tool.posts.size() >= 5000)
continue;
found.outside = false;
found.distance = plan_distance(found.post.location, post.position);
if (found.distance < best)
post.growth = glm::dot(hectometre.rising, sampled_direction(tool.samples, post.chainage)) < 0.0 ? -1 : 1;
place(post);
}
std::sort(tool.posts.begin(), tool.posts.end(), [](hekto_post const &A, hekto_post const &B) { return A.chainage < B.chainage; });
}
else
{
auto const km_at = [&](double const Chainage) { return tool.km + tool.growth * (Chainage - tool.origin) / 1000.0; };
auto const low{std::max(0, static_cast<int>(std::ceil(std::min(km_at(0.0), km_at(length)) * 10.0 - 1e-6)))};
auto const high{static_cast<int>(std::floor(std::max(km_at(0.0), km_at(length)) * 10.0 + 1e-6))};
for (int hectometres = low; hectometres <= high && tool.posts.size() < 5000; ++hectometres)
{
hekto_post post;
post.hectometres = hectometres;
post.chainage = tool.origin + tool.growth * (hectometres / 10.0 - tool.km) * 1000.0;
post.growth = tool.growth;
if (post.chainage < 0.0 || post.chainage > length)
continue;
place(post);
}
}
// each post standing goes with the place of its number nearest to it; of the posts going with a place the one nearest to it
// stands for it, the others are duplicates. it stands wrong when it's farther from the place than allowed: along the line,
// across it, or on the other side
for (auto &found : tool.found)
{
found.outside = true;
found.place = -1;
for (int i = 0; i < static_cast<int>(tool.posts.size()); ++i)
{
auto const &post{tool.posts[i]};
if (post.hectometres != found.hectometres)
continue;
auto const distance{plan_distance(found.post.location, post.position)};
if (found.place < 0 || distance < found.distance)
{
best = found.distance;
post.standing = i;
found.outside = false;
found.place = i;
found.distance = distance;
found.expected = post.chainage;
found.error = found.chainage - post.chainage;
}
}
for (int i = 0; i < static_cast<int>(tool.found.size()); ++i)
{
auto &found{tool.found[i]};
if (found.hectometres != hectometres)
continue;
found.duplicate = i != post.standing;
found.wrong = false == found.duplicate && found.distance > tool.tolerance;
}
tool.posts.push_back(post);
}
for (int i = 0; i < static_cast<int>(tool.found.size()); ++i)
{
auto const &found{tool.found[i]};
if (found.place < 0)
continue;
auto &post{tool.posts[found.place]};
if (post.standing < 0 || found.distance < tool.found[post.standing].distance)
post.standing = i;
}
for (int i = 0; i < static_cast<int>(tool.found.size()); ++i)
{
auto &found{tool.found[i]};
found.duplicate = found.place >= 0 && tool.posts[found.place].standing != i;
found.wrong = found.place >= 0 && false == found.duplicate && found.distance > tool.tolerance;
}
}
@@ -576,21 +613,40 @@ void hekto_tool::render_options()
if (Changed)
tool.dirty = true;
};
if (begin_fields("##hektokm", 0.45f))
if (false == tool.line.empty())
{
field_row("Kilometre at the point (km)");
changed(ImGui::InputDouble("##hektokilometre", &tool.km, 0.0, 0.0, "%.3f"));
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
if (ImGui::Button(STR_C("Read from the posts"), ImVec2(-FLT_MIN, 0.0f)))
ImGui::TextWrapped("%s", format(STR_C("The km of line %s, as the Kilometrage tool sets them, its jumps too: km %s at the point"), tool.line.c_str(), kilometrage::text(tool.km).c_str()).c_str());
if (ImGui::SmallButton(STR_C("Give the km here instead")))
{
tool.own_km = true;
tool.km_read = false;
tool.dirty = true;
}
ImGui::EndTable();
item_tooltip("The posts go by the km given at the point and the way it grows, without the jumps of the line");
}
else
{
if (false == tool.line_set.empty() && ImGui::SmallButton(format(STR_C("Follow the km of line %s"), tool.line_set.c_str()).c_str()))
{
tool.own_km = false;
tool.dirty = true;
}
if (begin_fields("##hektokm", 0.45f))
{
field_row("Kilometre at the point (km)");
changed(ImGui::InputDouble("##hektokilometre", &tool.km, 0.0, 0.0, "%.3f"));
ImGui::TableNextRow();
ImGui::TableSetColumnIndex(1);
if (ImGui::Button(STR_C("Read from the posts"), ImVec2(-FLT_MIN, 0.0f)))
{
tool.km_read = false;
tool.dirty = true;
}
ImGui::EndTable();
}
ImGui::TextDisabled("%s", STR_C("The kilometrage grows"));
changed(segmented("growth", tool.growth, {{1, STR_C("along the line")}, {-1, STR_C("against it")}}));
}
ImGui::TextDisabled("%s", STR_C("The kilometrage grows"));
changed(segmented("growth", tool.growth, {{1, STR_C("along the line")}, {-1, STR_C("against it")}}));
ImGui::TextDisabled("%s", STR_C("Side, looking where it grows"));
changed(segmented("side", tool.side, {{1, STR_C("on the right")}, {-1, STR_C("on the left")}, {0, STR_C("alternately"), "The even hectometres on the right, the odd ones on the left"}}));
if (ImGui::TreeNode(STR_C("More settings")))
@@ -1056,6 +1112,7 @@ editor_selftest_result hekto_tool::selftest(std::string const &Command, std::ist
auto &tool{m_hekto};
if (values.count("reach"))
tool.reach = std::stod(values["reach"]);
tool.own_km = values["own"] == "1";
tool.dirty = true;
hekto_update();
if (values.count("km"))
@@ -1067,7 +1124,10 @@ editor_selftest_result hekto_tool::selftest(std::string const &Command, std::ist
tool.dirty = true;
hekto_update();
auto const missing{std::count_if(tool.posts.begin(), tool.posts.end(), [](hekto_post const &Post) { return Post.standing < 0; })};
WriteLog(format("SELFTEST hekto: km %.3f growth %d, %zu posts along %.0f m, %d to place, %zu standing. %s", tool.km, tool.growth, tool.posts.size(), tool.samples.empty() ? 0.0 : tool.samples.back().chainage, static_cast<int>(missing), tool.found.size(), tool.status.c_str()));
auto const wrong{std::count_if(tool.found.begin(), tool.found.end(), [](hekto_found const &Post) { return Post.wrong; })};
auto const duplicates{std::count_if(tool.found.begin(), tool.found.end(), [](hekto_found const &Post) { return Post.duplicate; })};
WriteLog(format("SELFTEST hekto: line %s km %.3f growth %d, %zu posts along %.0f m, %d to place, %zu standing, %d wrong, %d duplicate. %s", tool.line.empty() ? "-" : tool.line.c_str(), tool.km, tool.growth, tool.posts.size(), tool.samples.empty() ? 0.0 : tool.samples.back().chainage, static_cast<int>(missing), tool.found.size(),
static_cast<int>(wrong), static_cast<int>(duplicates), tool.status.c_str()));
for (std::size_t i = 0; i < tool.posts.size() && i < 4; ++i)
{
auto const &post{tool.posts[i]};
@@ -1076,10 +1136,17 @@ editor_selftest_result hekto_tool::selftest(std::string const &Command, std::ist
auto const direction{editor_track::sampled_position(tool.samples, post.chainage + 1.0) - axis};
glm::dvec2 const offset{post.position.x - axis.x, post.position.z - axis.z};
lateral = glm::length(offset);
auto const right{(direction.x * offset.y - direction.z * offset.x) * tool.growth >= 0.0};
auto const right{(direction.x * offset.y - direction.z * offset.x) * post.growth >= 0.0};
WriteLog(format("SELFTEST hekto post %d.%d at %.1f m, %.2f m from the axis, %s of the growing kilometrage, at %.1f %.1f %.1f", post.hectometres / 10, post.hectometres % 10, post.chainage, lateral, right ? "right" : "left", post.position.x, post.position.y,
post.position.z));
}
if (values["list"] == "1")
{
std::string list;
for (auto const &post : tool.posts)
list += format(" %d@%.0f", post.hectometres, post.chainage);
WriteLog("SELFTEST hekto posts:" + list);
}
if (values["place"] == "1")
{
hekto_place();

View File

@@ -41,6 +41,7 @@ class hekto_tool final : public editor_tool
double chainage{0.0};
glm::dvec3 position{0.0};
double yaw{0.0};
int growth{1}; // 1: the kilometrage grows along the line there, -1: against
int standing{-1}; // post already there with this number
};
struct hekto_found
@@ -54,6 +55,7 @@ class hekto_tool final : public editor_tool
bool wrong{false};
bool duplicate{false}; // another post with the same number stands nearer to its place
bool outside{false}; // its number belongs past the ends of the line
int place{-1}; // of the posts, the one with its number nearest to it
};
struct hekto_state
{
@@ -62,6 +64,9 @@ class hekto_tool final : public editor_tool
double km{0.0};
bool km_read{false}; // the kilometrage was read from the posts already standing
int growth{1}; // 1: grows along the line, -1: against
std::string line; // of the kilometrage set for the line in the Kilometrage tool the posts follow, empty for the one given here
std::string line_set; // the line the Kilometrage tool sets the kilometrage of at the point, empty for none
bool own_km{false}; // the kilometrage given here, even where the line has one set
int side{1}; // 1: to the right of the growing kilometrage, -1: to the left, 0: alternately
double offset{3.0}; // m from the axis
int style{0}; // nowy, stary, zgnity, mixed