17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-06 00:02:26 +01:00

feat(editor): laid to the norms, a track laid on from an end gets its transition curves, into the arc, between the arcs of a compound curve and out to the straight

A track laid on from a free end in the PKP PLK laying, by a drag out of the end, by clicks from it or laid back onto it, starts from the curvature and the cant the end has. An arc turning the same way as the end is a compound curve: a transition of the norms ramps the curvature and the cant from the one arc into the other. From a straight the arc gets its entry transition, the arc then straight its exit one. Where the way is too short the transitions are cut and the cant lowered to what they ramp, and the readout says so; with no room at all the track is laid as before and the note says why. The curves traced on the orthophoto and the recommended values of a compound curve get the transitions between their arcs too. The free laying stays as it was.
This commit is contained in:
maj00r
2026-10-21 19:51:56 +02:00
parent 5764b328be
commit 415c7484ec
10 changed files with 661 additions and 26 deletions

View File

@@ -1482,6 +1482,10 @@ class editor_mode : public application_mode, private editor_track::observer, pri
TTrack const *m_join_track{nullptr};
editor_track::point_ref m_join_point;
mutable alignment::join_result m_join_last;
// the layout laid on from the end of From with the transition curves of the norms; none in the free laying, or where it doesn't fit
std::optional<alignment::lay_result> eased_layout(TTrack const *From, glm::dvec3 const &Point, glm::dvec2 const &Direction, glm::dvec3 const &Target, double const Radius) const;
mutable std::optional<alignment::lay_result> m_eased_last;
mutable std::optional<std::pair<alignment::lay_request, std::optional<alignment::lay_result>>> m_eased_cache;
bool track_wheel(double const Offset);
// a touchpad sends fractions of a notch: a choice moves a step for each whole notch gathered, and only one in a gesture when
// Gesture; a pause, another key or the other way starts over

View File

@@ -408,8 +408,40 @@ void editor_mode::point_drag_finish()
m_track_joints.clear();
}
std::optional<alignment::lay_result> editor_mode::eased_layout(TTrack const *From, glm::dvec3 const &Point, glm::dvec2 const &Direction, glm::dvec3 const &Target, double const Radius) const
{
m_eased_last.reset();
if (false == m_lay.transitions || From == nullptr || From->eType != tt_Normal)
return std::nullopt;
auto const end{editor_track::bend_at(*From, Point)};
if (false == end.has_value())
return std::nullopt;
alignment::lay_request request;
request.start.position = Point;
request.start.direction = Direction;
request.start.roll = end->roll;
request.curvature = end->curvature;
request.target = Target;
switch (m_track_layout)
{
case track_layout::straight: request.kind = alignment::lay_kind::straight; break;
case track_layout::arc: request.kind = alignment::lay_kind::arc; break;
case track_layout::straight_arc: request.kind = alignment::lay_kind::straight_arc; break;
default: request.kind = alignment::lay_kind::arc_straight; break;
}
request.radius = Radius;
request.speed = design_speed(From, nullptr);
request.piece_length = m_lay.piece_length;
request.norms = laying_norms();
if (false == m_eased_cache.has_value() || m_eased_cache->first != request)
m_eased_cache.emplace(request, alignment::lay_on(request));
m_eased_last = m_eased_cache->second;
return m_eased_last;
}
std::vector<segment_data> editor_mode::extend_pieces() const
{
m_eased_last.reset();
std::vector<segment_data> result;
auto const &tool{m_extend};
editor_track::snap_target target;
@@ -452,7 +484,8 @@ std::vector<segment_data> editor_mode::extend_pieces() const
auto radius{m_lay.radius};
if (auto const values{typed_values()}; false == values.empty() && (m_track_layout == track_layout::arc_straight || m_track_layout == track_layout::straight_arc) && std::abs(values[0]) >= 1.0)
radius = std::abs(values[0]);
auto pieces{editor_track::layout_pieces(m_track_layout, tool.point, tool.direction, tool.grade, tool.mouse, radius)};
auto const eased{eased_layout(tool.track, tool.point, tool.direction, tool.mouse, radius)};
auto pieces{eased.has_value() && false == eased->pieces.empty() ? eased->pieces : editor_track::layout_pieces(m_track_layout, tool.point, tool.direction, tool.grade, tool.mouse, radius)};
follow_ground(pieces, tool.point.y, tool.grade, tool.mouse.y, std::numeric_limits<double>::quiet_NaN(), tool.track);
return pieces;
}
@@ -807,6 +840,7 @@ std::vector<segment_data> editor_mode::lay_pieces(std::vector<glm::dvec3> const
{
auto const &tool{m_lay};
Length = 0.0;
m_eased_last.reset();
if (Points.size() < 2)
return {};
if (Points.size() == 2)
@@ -824,8 +858,10 @@ std::vector<segment_data> editor_mode::lay_pieces(std::vector<glm::dvec3> const
if (tool.start.track != nullptr)
{
auto const grade{-grade_of(tool.start.direction)};
pieces = editor_track::layout_pieces(m_track_layout, Points[0], glm::normalize(leaving), grade, Points[1], tool.radius);
Error = narrowed(pieces, m_track_layout, tool.radius);
auto const eased{eased_layout(tool.start.track, Points[0], glm::normalize(leaving), Points[1], tool.radius)};
auto const laid{eased.has_value() && false == eased->pieces.empty()};
pieces = laid ? eased->pieces : editor_track::layout_pieces(m_track_layout, Points[0], glm::normalize(leaving), grade, Points[1], tool.radius);
Error = laid ? narrowed(m_track_layout, tool.radius, eased->radius) : narrowed(pieces, m_track_layout, tool.radius);
follow_ground(pieces, Points[0].y, grade, Points[1].y, std::numeric_limits<double>::quiet_NaN(), tool.start.track);
}
else if (End.track == nullptr)
@@ -836,10 +872,12 @@ std::vector<segment_data> editor_mode::lay_pieces(std::vector<glm::dvec3> const
}
else if (glm::length(plan_of(End.direction)) > 1e-6)
{
auto const back{editor_track::layout_pieces(m_track_layout, End.position, glm::normalize(-plan_of(End.direction)), -grade_of(End.direction), Points[0], tool.radius)};
auto const eased{eased_layout(End.track, End.position, glm::normalize(-plan_of(End.direction)), Points[0], tool.radius)};
auto const laid{eased.has_value() && false == eased->pieces.empty()};
auto const back{laid ? eased->pieces : editor_track::layout_pieces(m_track_layout, End.position, glm::normalize(-plan_of(End.direction)), -grade_of(End.direction), Points[0], tool.radius)};
for (auto piece{back.rbegin()}; piece != back.rend(); ++piece)
pieces.push_back(reversed(*piece));
Error = narrowed(pieces, m_track_layout, tool.radius);
Error = laid ? narrowed(m_track_layout, tool.radius, eased->radius) : narrowed(pieces, m_track_layout, tool.radius);
follow_ground(pieces, Points[0].y, std::numeric_limits<double>::quiet_NaN(), End.position.y, grade_of(End.direction), End.track);
}
for (auto const &piece : pieces)
@@ -1020,6 +1058,7 @@ void editor_mode::lay_finish(editor_track::snap_target const &End)
double length;
std::string error;
auto const pieces{lay_pieces(tool.points, End, length, error)};
auto const eased{m_eased_last};
tool.status_time = ImGui::GetTime();
if (pieces.empty())
{
@@ -1043,6 +1082,10 @@ void editor_mode::lay_finish(editor_track::snap_target const &End)
tool.status = format(STR_P("Laid %zu track, %.1f m, Ctrl+Z takes it back", "Laid %zu tracks, %.1f m, Ctrl+Z takes them back", static_cast<long long>(created.size())), created.size(), length);
if (false == error.empty())
tool.status += "\n" + error;
if (eased.has_value() && eased->compound)
tool.status += "\n" + format(STR_C("Compound curve: R %.0f m goes on into R %.0f m through a transition curve of %.0f m"), eased->from_radius, eased->radius, eased->entry);
if (eased.has_value() && false == eased->note.empty())
tool.status += "\n" + eased->note;
{
std::vector<std::pair<TTrack *, bool>> run;
if (tool.start.track != nullptr && tool.start.track->eType == tt_Normal)

View File

@@ -52,10 +52,15 @@ bool editor_mode::route_active() const
void editor_mode::route_recommend(alignment::vertex &Vertex) const
{
auto const recommended{alignment::recommend(m_route.design.speed, Vertex.radius, laying_norms())};
auto sharpest{Vertex.radius};
if (Vertex.compound)
for (auto const &arc : Vertex.arcs)
sharpest = std::min(sharpest, arc.radius);
auto const recommended{alignment::recommend(m_route.design.speed, sharpest, laying_norms())};
Vertex.cant = recommended.cant;
Vertex.transition_in = recommended.transition;
Vertex.transition_out = recommended.transition;
alignment::recommend_between(Vertex, m_route.design.speed, laying_norms());
}
void editor_mode::route_bind_ends()

View File

@@ -1782,6 +1782,8 @@ std::string editor_mode::track_readout() const
if (piece.radius != 0.0f && (radius == 0.0 || std::abs(piece.radius) < radius))
radius = std::abs(piece.radius);
}
if (m_eased_last.has_value() && false == m_eased_last->pieces.empty())
radius = m_eased_last->radius;
editor_track::snap_target target;
auto const snapped{extend_snap(target) && target.track != nullptr};
editor_track::branch_link link;
@@ -1791,6 +1793,8 @@ std::string editor_mode::track_readout() const
if (snapped && pieces.empty())
return format(STR_C("no track fits to the end of %s"), name(target.track).c_str());
auto text{radius > 0.0 ? format("L %.2f m R %.0f m", length, radius) + speed_of(radius, laying_norms()) : format("L %.2f m %s", length, STR_C("straight"))};
if (auto const eased{eased_of(m_eased_last)}; false == eased.empty())
text += "\n" + eased;
if (auto const vertical{vertical_of(pieces)}; false == vertical.empty())
text += "\n" + vertical;
if (snapped)
@@ -1817,10 +1821,14 @@ std::string editor_mode::track_readout() const
else if (radius == 0.0 || r < radius)
radius = r;
}
if (m_eased_last.has_value() && false == m_eased_last->pieces.empty())
radius = m_eased_last->radius;
if (radius > 0.0)
text = (straight ? format("L %.1f m %s + R %.0f m", lay.preview_length, STR_C("straight"), radius) : format("L %.1f m R %.0f m", lay.preview_length, radius)) + speed_of(radius, laying_norms());
else
text = format("L %.1f m %s", lay.preview_length, STR_C("straight"));
if (auto const eased{eased_of(m_eased_last)}; false == eased.empty())
text += "\n" + eased;
if (auto const vertical{vertical_of(lay.preview)}; false == vertical.empty())
text += "\n" + vertical;
}

View File

@@ -70,6 +70,21 @@ inline std::string speed_of(double const Radius, alignment::limits const &Limits
return speed > 0.0 ? format(STR_C(" (up to %.0f km/h)"), speed) : std::string{};
}
// the transition curves and the cant the norms put in a layout, a compound curve where it goes on from an arc turning the same way
inline std::string eased_of(std::optional<alignment::lay_result> const &Eased)
{
if (false == Eased.has_value())
return {};
if (Eased->pieces.empty() || (Eased->entry <= 0.0 && Eased->exit <= 0.0))
return Eased->note;
auto text{Eased->compound ? format(STR_C("compound curve on from R %.0f m, a transition of %.0f m between the arcs"), Eased->from_radius, Eased->entry) : format(STR_C("transition curves %.0f / %.0f m"), Eased->entry, Eased->exit)};
if (Eased->cant > 0.0)
text += format(STR_C(" cant %.0f mm"), Eased->cant);
if (false == Eased->note.empty())
text += "\n" + Eased->note;
return text;
}
// the rise of the track from its start to its end, and the grades along it
inline std::string vertical_of(std::vector<segment_data> const &Pieces)
{
@@ -106,17 +121,20 @@ inline std::string lines_of(std::vector<std::string> const &Texts)
return text;
}
inline std::string narrowed(editor_track::layout const Layout, double const Radius, double const Arc)
{
if ((Layout != editor_track::layout::arc_straight && Layout != editor_track::layout::straight_arc) || Arc == 0.0 || std::abs(Arc - Radius) < Radius * 0.01)
return {};
return format(STR_C("R %.0f m doesn't reach the cursor, the arc goes through it with R %.0f m"), Radius, Arc);
}
inline std::string narrowed(std::vector<segment_data> const &Pieces, editor_track::layout const Layout, double const Radius)
{
if (Layout != editor_track::layout::arc_straight && Layout != editor_track::layout::straight_arc)
return {};
double sharpest{0.0};
for (auto const &piece : Pieces)
if (auto const radius{piece_plan_radius(piece)}; radius >= 0.5 && (sharpest == 0.0 || radius < sharpest))
sharpest = radius;
if (sharpest == 0.0 || sharpest >= Radius * 0.99)
return {};
return format(STR_C("R %.0f m doesn't reach the cursor, the arc goes through it with R %.0f m"), Radius, sharpest);
return narrowed(Layout, Radius, sharpest > Radius ? 0.0 : sharpest);
}
inline std::string describe(editor_track::straight const &Line)