17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 18:14:55 +01:00

fix(editor): a point taken out of a grade line leaves its neighbours with the radius of the limits, and paths meeting at a sharp break keep their own grades

The points of a grade line read from the tracks keep the radius measured there; once a point between them was taken out, the
breaks of its neighbours changed while their radii stayed, so a break without a curve read as 0.4 per mille became one of
9 per mille with no curve, and the tracks took it as a kink. The neighbours of the point taken out follow the limits now,
with the button, Del and the self-test alike (profile_remove_point).

Applying a line with a break without a curve gave the path which ends at the break the grade of the element after it, so
its Hermite heights bowed 0.12 m off the line over 100 m before the break; each path takes the grade on its own side now.

Self-test commands vprofilerun, vprofiledel and vprofileput.
This commit is contained in:
maj00r
2026-10-21 17:25:17 +02:00
parent d2d06817bc
commit e86a536a9d
4 changed files with 78 additions and 10 deletions

View File

@@ -2179,6 +2179,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri
void profile_forget();
void profile_open_stored(std::size_t const Index);
void profile_edited();
// the radii of its neighbours were for the breaks of the grade it made, they follow the limits after it
void profile_remove_point(int const Index);
void render_profile_body();
void render_profile_strip();
void render_profile_header();

View File

@@ -630,6 +630,20 @@ void editor_mode::profile_edited()
first_step_done(first_step::profile_point);
}
void editor_mode::profile_remove_point(int const Index)
{
auto &state{m_profile};
auto &points{state.line.points};
if (Index <= 0 || Index + 1 >= static_cast<int>(points.size()))
return;
points.erase(points.begin() + Index);
for (auto const neighbour : {Index - 1, Index})
if (neighbour > 0 && neighbour + 1 < static_cast<int>(points.size()) && false == points[neighbour].joint)
points[neighbour].automatic = true;
state.selected = -1;
profile_edited();
}
void editor_mode::profile_open(TTrack *From, TTrack *To)
{
auto &state{m_profile};
@@ -2086,9 +2100,8 @@ void editor_mode::render_profile_point()
ImGui::SameLine();
if (ImGui::Button(STR_C("Remove Del")))
{
points.erase(points.begin() + index);
state.selected = -1;
changed = true;
profile_remove_point(index);
return;
}
}
}
@@ -2513,11 +2526,7 @@ bool editor_mode::profile_canvas_edit(profile_view const &View, bool const Hover
else
state.curve_grip = -1;
if ((Hovered || ImGui::IsWindowFocused()) && state.selected > 0 && state.selected + 1 < static_cast<int>(points.size()) && false == points[state.selected].joint && ImGui::IsKeyPressed(ImGuiKey_Delete))
{
points.erase(points.begin() + state.selected);
state.selected = -1;
changed = true;
}
profile_remove_point(state.selected);
return changed;
}

View File

@@ -396,6 +396,61 @@ editor_mode::selftest_result editor_mode::selftest_track(std::string const &comm
WriteLog(format("SELFTEST vprofile zone %.3f %.3f %s", zone.from, zone.to, zone.name.c_str()));
return selftest_result::handled;
}
if (command == "vprofilerun" || command == "vprofiledel" || command == "vprofileput")
{
// the grade line of the line through a track, a point of it taken out as with Del, the line applied
auto &state{m_profile};
if (command == "vprofilerun")
{
std::string name;
double length{std::numeric_limits<double>::infinity()};
words >> name >> length;
auto *track{simulation::Paths.find(name)};
if (track == nullptr)
{
WriteLog("SELFTEST vprofilerun: no track " + name);
return selftest_result::handled;
}
state.run_length = length;
profile_open_run(*track);
}
else if (command == "vprofiledel")
{
int index{-1};
words >> index;
auto &points{state.line.points};
if (index <= 0 || index + 1 >= static_cast<int>(points.size()) || points[index].joint)
{
WriteLog(format("SELFTEST vprofiledel: point %d can't be taken out", index));
return selftest_result::handled;
}
profile_remove_point(index);
}
else
{
std::string option;
words >> option;
profile_apply(option == "terrain");
WriteLog("SELFTEST vprofileput: " + state.status + " " + state.error);
}
std::string points;
for (auto const &point : state.line.points)
points += format(" (%.1f %.3f R%.0f%s%s)", point.chainage, point.elevation, point.radius, point.automatic ? "" : " fixed", point.joint ? " joint" : "");
std::string issues;
for (auto const &issue : state.issues)
issues += std::string{issue.error ? " | error: " : " | "} + issue.text;
double low{0.0}, high{0.0}, at_low{0.0}, at_high{0.0};
for (auto const &sample : state.samples)
{
auto const off{sample.position.y - profile::elevation(state.line, sample.chainage)};
if (off < low)
low = off, at_low = sample.chainage;
if (off > high)
high = off, at_high = sample.chainage;
}
WriteLog(format("SELFTEST %s: route %.1f m, %zu samples, track against the line %.3f at %.1f .. %+.3f at %.1f; points%s%s", command.c_str(), state.route.length, state.samples.size(), low, at_low, high, at_high, points.c_str(), issues.c_str()));
return selftest_result::handled;
}
if (command == "vprofileapply")
{
// a plain grade line from the height of the start of the open route, applied to it

View File

@@ -2559,7 +2559,9 @@ std::vector<editor_track::height_gap> editor_track::apply_profile(route &Route,
auto const endchainage{span.forward ? span.to : span.from};
auto const sign{span.forward ? 1.0 : -1.0};
auto &path{span.track->m_paths[span.path]};
set_heights(path, Elevation(startchainage), Elevation(endchainage), Grade(startchainage) * sign, Grade(endchainage) * sign);
// at a break of the grade without a curve each path takes the grade on its own side of it
auto const inward = [&](double const Chainage, double const Other) { return Grade(Chainage + std::min(1e-3, std::abs(Other - Chainage) * 0.5) * (Other > Chainage ? 1.0 : -1.0)); };
set_heights(path, Elevation(startchainage), Elevation(endchainage), inward(startchainage, endchainage) * sign, inward(endchainage, startchainage) * sign);
// the pieces of a path made of several curves follow the grade line along them, else the path would part from them and lose them
std::vector<double> lengths;
double total{0.0};
@@ -2576,7 +2578,7 @@ std::vector<editor_track::height_gap> editor_track::apply_profile(route &Route,
{
auto const first{at(along)};
auto const last{at(along + lengths[k])};
set_heights(path.pieces[k], Elevation(first), Elevation(last), Grade(first) * sign, Grade(last) * sign);
set_heights(path.pieces[k], Elevation(first), Elevation(last), inward(first, last) * sign, inward(last, first) * sign);
along += lengths[k];
}
}