18
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-14 01:57:58 +01:00

feat(editor): in the mode of the straights the line from the end of the diverging track of a switch begins with a straight right at P4, the curvature jumping as at the start of the switch, a bend clicked getting its own curve where it was clicked; only a curve traced from P4 goes on with the curve of the switch. Dragged from a free end the shape follows the mode of laying: a straight in the one of the straights, from P4 right from its end, an arc and a straight in the one of a curve (Tab), from P4 the curve of the switch going on; the four shapes to pick under More settings and with Shift+wheel are gone, which only muddled the two ways of laying. Shift+wheel picks the way of joining over another free end. The hints, the readout by the cursor and the table of the keys say so. Selftests of P4: a straight of 100 m and of 15 m laid right from P4, bends of 8 degrees either way after a straight, dragged straight; traced and joined from P4 the curve of the switch goes on

This commit is contained in:
maj00r
2026-10-26 19:50:28 +01:00
parent ed3d6551d8
commit eb3d3eb1e0
6 changed files with 64 additions and 82 deletions

View File

@@ -5530,9 +5530,9 @@ void editor_mode::render_shortcuts_window()
{"K", "Split the selected track at the cursor, a break of the grade at the height of the ground"},
{"L, LMB, LMB...", "Lay track: each click a bend of the straights, eased with a large curve; Enter lays them and goes on from the end"},
{"Tab while laying", "The points clicked next go along the middle of a curve seen on the photo, the last two on the straight after it; Tab again: back to the bends"},
{"Drag from a free end", "Lead a track out of it; onto another free end: join the two"},
{"Drag from a free end", "Lead a track out of it: a straight, an arc and a straight where a curve is traced (Tab); onto another free end: join the two"},
{"Ctrl+wheel", "Dragging from a free end: radius"},
{"Shift+wheel", "Dragging from a free end: arc then straight, straight then arc, arc, straight; over another free end: another way to join"},
{"Shift+wheel", "Dragging over another free end: another way to join"},
{"0-9 while laying", "Type the length of the next straight, Enter puts the point"},
{"Alt held while laying", "The direction in steps of 15 degrees, or 1:9, 1:6.6... to a straight track; the first click with Alt goes onto the axis of the track"},
{"N", "Parallel track: LMB on a track, then the side and the spacing"},

View File

@@ -1546,6 +1546,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri
lay_line_plan *Plan = nullptr) const;
// Adjoining: the curve next to a free end on a curve goes on from its curvature, where it turns its way; else from a straight there
bool lay_line(std::vector<glm::dvec3> const &Points, std::vector<lay_mode> const &Modes, editor_track::snap_target const &End, lay_line_plan &Plan, std::string &Error, bool const Adjoining = true) const;
// whether the points next to the start of the line, or to its end, AtEnd, a free end joined there, are clicked along a curve
static bool traced_from(std::vector<glm::dvec3> const &Points, std::vector<lay_mode> const &Modes, bool const AtEnd);
// the curves of a stretch traced, the ones fitted lately kept for the same points
// none while the stretch the cursor moves along is still being fitted; Meanwhile: the fit done last of the same points but the one
// at the cursor until then
@@ -1673,7 +1675,9 @@ class editor_mode : public application_mode, private editor_track::observer, pri
void extend_add(int const Side);
void render_extend_ui();
using track_layout = editor_track::layout;
track_layout m_track_layout{track_layout::arc_straight};
// the shape dragged from a free end, as the mode of laying has it: a straight in the one of the straights, an arc and a straight in
// the one of a curve
track_layout m_track_layout{track_layout::straight};
double m_join_radius{0.0};
int m_join_variant{0};
TTrack const *m_join_track{nullptr};
@@ -1696,7 +1700,6 @@ class editor_mode : public application_mode, private editor_track::observer, pri
wheel_state m_wheel;
int wheel_step(double const Offset, bool const Gesture);
std::string track_shaping(bool const Joined) const;
void render_layout_combo();
double design_speed(TTrack const *From, TTrack const *To) const;
// the track whose speed the design takes, none where it's the one set for the laying
TTrack const *speed_track(TTrack const *From, TTrack const *To) const;

View File

@@ -49,11 +49,19 @@ using namespace track_mode;
namespace
{
// the curve of the track of a free end the line goes on from or into: the one of a plain track always, the one of the diverging track
// of a switch where a curve is traced from it, Traced; the bends of straights begin there with a straight, the curvature jumping as at
// the start of the switch
bool goes_on_curved(editor_track::snap_target const &End, bool const Traced)
{
return End.track != nullptr && (End.track->eType == tt_Normal || (End.track->eType == tt_Switch && Traced));
}
// the curve of the track of a free end the line laid goes on from at its start, or into at its end: its radius, the side it turns to
// as the line runs and its cant leaning into the turn; nothing on a straight
void adjoining_curve(editor_track::snap_target const &End, bool const Atend, double const Gauge, alignment::design &Design)
void adjoining_curve(editor_track::snap_target const &End, bool const Atend, bool const Traced, double const Gauge, alignment::design &Design)
{
if (End.track == nullptr || (End.track->eType != tt_Normal && End.track->eType != tt_Switch))
if (false == goes_on_curved(End, Traced))
return;
auto const bend{editor_track::bend_at(*End.track, End.position)};
if (false == bend.has_value() || std::abs(bend->curvature) < 1e-4)
@@ -460,9 +468,10 @@ void editor_mode::point_drag_finish()
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();
// from the end of a curved track, the diverging track of a switch too, the line goes on from its curvature: a curve from it, a
// straight after a transition out of it, never with a jump of the curvature at the end
if (From == nullptr || (From->eType != tt_Normal && From->eType != tt_Switch))
// from the end of a curved track the line goes on from its curvature: a curve from it, a straight after a transition out of it, never
// with a jump of the curvature at the end; at the diverging track of a switch a curve from it, a straight right from its end, as the
// switch goes on with a straight
if (From == nullptr || (From->eType != tt_Normal && (From->eType != tt_Switch || m_track_layout == track_layout::straight)))
return std::nullopt;
auto const end{editor_track::bend_at(*From, Point)};
if (false == end.has_value())
@@ -784,66 +793,6 @@ void editor_mode::render_extend_ui()
extend_add(-1);
}
// the shapes laid from a free end as four tiles, each with a sketch: the end of the track, what's laid from it, the cursor
void editor_mode::render_layout_combo()
{
char const *const names[]{"Arc, then straight", "Straight, then arc", "Arc through the cursor", "Straight"};
auto const current{static_cast<int>(m_track_layout)};
ImGui::TextDisabled("%s", STR_C("Shape from a free end"));
auto const font{ImGui::GetFontSize()};
auto const gap{std::round(font * 0.3f)};
auto const width{std::floor((ImGui::GetContentRegionAvail().x - 3.0f * gap) * 0.25f)};
auto const height{std::round(font * 2.4f)};
auto *list{ImGui::GetWindowDrawList()};
auto const accent{family_colour(0)};
for (int i = 0; i < 4; ++i)
{
if (i > 0)
ImGui::SameLine(0.0f, gap);
ImGui::PushID(i);
auto const top{ImGui::GetCursorScreenPos()};
if (ImGui::InvisibleButton("##shape", ImVec2(width, height)))
m_track_layout = static_cast<track_layout>(i);
auto const hovered{ImGui::IsItemHovered()};
if (hovered)
ImGui::SetTooltip("%s\n%s", STR_C(names[i]), STR_C("While laying from a free end: Shift+wheel"));
ImGui::PopID();
auto const chosen{i == current};
ImVec2 const bottom{top.x + width, top.y + height};
auto const rounding{std::round(font * 0.35f)};
list->AddRectFilled(top, bottom, chosen ? (accent & ~IM_COL32_A_MASK) | IM_COL32(0, 0, 0, 46) : ImGui::GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), rounding);
list->AddRect(top, bottom, chosen ? accent : ImGui::GetColorU32(ImGuiCol_Border), rounding, 0, chosen ? 2.0f : 1.0f);
auto const pad{std::round(font * 0.45f)};
auto const at = [&](float const X, float const Y) { return ImVec2(top.x + pad + X * (width - 2.0f * pad), top.y + pad + Y * (height - 2.0f * pad)); };
auto const laid{chosen ? accent : ImGui::GetColorU32(ImGuiCol_Text, 0.7f)};
list->AddLine(at(0.0f, 0.85f), at(0.18f, 0.85f), ImGui::GetColorU32(ImGuiCol_TextDisabled), 3.0f);
switch (i)
{
case 0:
list->AddBezierQuadratic(at(0.18f, 0.85f), at(0.4f, 0.85f), at(0.61f, 0.6f), laid, 2.0f);
list->AddLine(at(0.61f, 0.6f), at(1.0f, 0.1f), laid, 2.0f);
break;
case 1:
list->AddLine(at(0.18f, 0.85f), at(0.45f, 0.85f), laid, 2.0f);
list->AddBezierQuadratic(at(0.45f, 0.85f), at(0.85f, 0.85f), at(1.0f, 0.1f), laid, 2.0f);
break;
case 2: list->AddBezierQuadratic(at(0.18f, 0.85f), at(0.7f, 0.85f), at(1.0f, 0.1f), laid, 2.0f); break;
default:
list->AddLine(at(0.18f, 0.85f), at(1.0f, 0.85f), laid, 2.0f);
for (float y{0.15f}; y < 0.8f; y += 0.2f)
list->AddLine(at(1.0f, y), at(1.0f, y + 0.1f), IM_COL32(255, 210, 60, 160), 1.0f);
break;
}
list->AddCircleFilled(at(0.18f, 0.85f), 2.5f, IM_COL32(230, 80, 230, 255));
auto const cursor{at(1.0f, 0.1f)};
list->AddLine(ImVec2(cursor.x - 4.0f, cursor.y), ImVec2(cursor.x + 4.0f, cursor.y), IM_COL32(255, 210, 60, 255), 1.5f);
list->AddLine(ImVec2(cursor.x, cursor.y - 4.0f), ImVec2(cursor.x, cursor.y + 4.0f), IM_COL32(255, 210, 60, 255), 1.5f);
}
ImGui::TextUnformatted(STR_C(names[current]));
ImGui::SameLine();
ImGui::TextDisabled("%s", STR_C("(Shift+wheel)"));
}
int editor_mode::wheel_step(double const Offset, bool const Gesture)
{
auto const &io{ImGui::GetIO()};
@@ -944,8 +893,6 @@ bool editor_mode::track_wheel(double const Offset)
}
if (joined)
m_join_variant += step;
else
m_track_layout = static_cast<track_layout>((static_cast<int>(m_track_layout) + 4 - step) % 4);
return true;
}
@@ -1002,10 +949,11 @@ std::vector<segment_data> editor_mode::lay_pieces(std::vector<glm::dvec3> const
};
std::vector<segment_data> pieces;
// the curvature of the track laid has to go on from the one of the track it joins, a jump gets a transition curve between them; at
// the end of the diverging track of a switch as well, the curve of the switch going on
// the end of the diverging track of a switch where a curve is traced from it, the curve of the switch going on, the bends of
// straights beginning there with a straight
auto const grade{(Points.back().y - Points.front().y) / std::max(1.0, plan_distance(Points.front(), Points.back()))};
auto const eased = [&](editor_track::snap_target const &Joint, double const Chainage, double const Sign) {
if (Joint.track == nullptr || (Joint.track->eType != tt_Normal && Joint.track->eType != tt_Switch))
if (false == goes_on_curved(Joint, traced_from(Points, Modes, Sign < 0.0)))
return editor_track::easing{};
auto result{editor_track::ease_out_of(*Joint.track, Joint.position, grade * Sign, design.speed, design.norms)};
auto const own{Sign * curvature_at(shape, Chainage)};
@@ -1095,6 +1043,14 @@ std::vector<segment_data> editor_mode::lay_pieces(std::vector<glm::dvec3> const
return pieces;
}
bool editor_mode::traced_from(std::vector<glm::dvec3> const &Points, std::vector<lay_mode> const &Modes, bool const AtEnd)
{
if (Points.size() < 3)
return false;
auto const index{AtEnd ? Points.size() - 2 : std::size_t{1}};
return index < Modes.size() && Modes[index] == lay_mode::curve;
}
bool editor_mode::lay_line(std::vector<glm::dvec3> const &Points, std::vector<lay_mode> const &Modes, editor_track::snap_target const &End, lay_line_plan &Plan, std::string &Error, bool const Adjoining) const
{
auto const &tool{m_lay};
@@ -1107,13 +1063,13 @@ bool editor_mode::lay_line(std::vector<glm::dvec3> const &Points, std::vector<la
auto const joined{End.track != nullptr && glm::length(plan_of(End.direction)) > 1e-6};
// from a free end on a curve, or to one, the curve laid next to it turning its way goes on from its curvature: it begins right at the
// end, not on a straight, its transition going from that curvature. Where no such curve fits, the line is laid from a straight there,
// which lay_pieces eases out of the curve
// which lay_pieces eases out of the curve. At a switch only a curve traced from it, the bends of straights from a straight there
if (Adjoining)
{
if (tool.start.track != nullptr && glm::length(plan_of(tool.start.direction)) > 1e-6)
adjoining_curve(tool.start, false, design.norms.gauge, design);
adjoining_curve(tool.start, false, traced_from(Points, Modes, false), design.norms.gauge, design);
if (joined)
adjoining_curve(End, true, design.norms.gauge, design);
adjoining_curve(End, true, traced_from(Points, Modes, true), design.norms.gauge, design);
}
auto const curved{design.start_curved || design.end_curved};
auto const from_straight = [&]() { return curved && lay_line(Points, Modes, End, Plan, Error, false); };
@@ -1505,6 +1461,7 @@ void editor_mode::lay_switch_mode()
{
auto &tool{m_lay};
tool.mode = tool.mode == lay_mode::straights ? lay_mode::curve : lay_mode::straights;
m_track_layout = tool.mode == lay_mode::curve ? track_layout::arc_straight : track_layout::straight;
if (tool.mode == lay_mode::curve)
first_step_done(first_step::trace);
tool.status = tool.mode == lay_mode::curve ? STR("Curve: click points along the middle of the track, the last two on the straight after the curve. Tab: back to the straights")
@@ -2010,7 +1967,6 @@ void editor_mode::render_lay_ui()
value_chips("radii", tool.radius, {190.0, 300.0, 500.0, 800.0, 1200.0, 2000.0}, "%.0f");
tool.radius = std::max(10.0, tool.radius);
ImGui::PopItemWidth();
render_layout_combo();
ImGui::Spacing();
ImGui::PushItemWidth(120.0f);
ImGui::DragScalar(STR_C("Longest path (m)"), ImGuiDataType_Double, &tool.piece_length, 1.0f, nullptr, nullptr, "%.0f");

View File

@@ -1690,8 +1690,10 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
else
{
hints = {{"LMB", curve ? "a point along the middle of the curve, the last two on the straight after it" : "a bend of the straights, eased with a large curve"}, {"LMB on a free end", "join and lay"}, {"Tab", tab}};
if (m_lay.mouse_snap.track != nullptr)
if (m_lay.mouse_snap.track != nullptr && m_lay.start.track != nullptr)
hints.insert(hints.end(), {{"Ctrl+wheel", "radius"}, {"Shift+wheel", "another shape"}});
else if (m_lay.mouse_snap.track != nullptr && curve)
hints.push_back({"Ctrl+wheel", "radius"});
hints.insert(hints.end(), {{"Alt", "the direction in steps of 15 degrees, or 1:9, 1:6.6... to a straight track"}, {"Enter", "stop laying"},
{"Backspace", m_lay.chain.empty() ? "take back the point" : "take back the last piece"}, {"Esc", "stop laying"}});
}
@@ -1703,7 +1705,14 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
hints = {{m_straights.tool == 2 ? "LMB" : "Shift+click", format(STR_C("point %zu of 4: %s"), step + 1, STR_C(steps[step]))}, {"Esc", "cancel"}};
}
else if (m_extend.active)
hints = {{"Drag", "lays the shape to the cursor, to a free end: joins it"}, {"Ctrl+wheel", "radius"}, {"Shift+wheel", "another shape"}, {"Release", "build"}, {"Esc", "cancel"}};
{
hints = {{"Drag", "lays the shape to the cursor, to a free end: joins it"}};
if (editor_track::snap_target target; extend_snap(target))
hints.insert(hints.end(), {{"Ctrl+wheel", "radius"}, {"Shift+wheel", "another shape"}});
else if (m_track_layout != track_layout::straight)
hints.push_back({"Ctrl+wheel", "radius"});
hints.insert(hints.end(), {{"Release", "build"}, {"Esc", "cancel"}});
}
else if (m_switch.placing)
hints = m_switch.connection >= 0 && false == m_switch.curved ?
std::vector<key_hint>{{"Drag onto the other track", "the connection there"}, {"Shift+wheel", "another kind"}, {"Ctrl+wheel", "R of the aligning curve"}, {"Alt+wheel", "where a double crossover meets"}, {"Esc", "cancel"}} :
@@ -2092,7 +2101,9 @@ std::string editor_mode::track_shaping(bool const Joined) const
auto text{std::string{STR_C(names[static_cast<int>(m_track_layout)])}};
if (m_track_layout == track_layout::arc_straight || m_track_layout == track_layout::straight_arc)
text += format(" R %.0f m", m_lay.radius);
return text + "\n" + STR("Ctrl+wheel: radius, Shift+wheel: arc / straight") + STR(", Ctrl+Shift+wheel: speed");
// the shape follows the mode of laying, Tab switching it where the laying goes on
return text + "\n" + (m_track_layout == track_layout::straight ? STR("Tab while laying: an arc, then a straight") : STR("Ctrl+wheel: radius, Tab while laying: a straight")) +
STR(", Ctrl+Shift+wheel: speed");
}
auto const &join{m_join_last};
char const *const kinds[]{"", "straight", "arc", "curve", "reverse curve", "free curve"};

View File

@@ -621,7 +621,7 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods)
intent.track = lay.mouse_snap.track;
intent.position = lay.mouse_snap.position;
auto const diverging{lay.mouse_snap.track->eType == tt_Switch && lay.mouse_snap.point == editor_track::point_ref{1, editor_track::point_kind::end}};
if (lay.points.empty() && diverging)
if (lay.points.empty() && diverging && m_track_layout != track_layout::straight)
return make(kind::lay_end, "Click: start from this free end, drag: the curve of the switch goes on");
return make(kind::lay_end, lay.points.empty() ? "Click: start from this free end, drag: lead a track out of it" : "Click: join this end and lay");
}
@@ -732,7 +732,7 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods)
intent.snap.track = selected;
intent.snap.point = hit;
intent.snap.position = intent.position;
auto const diverging{selected->eType == tt_Switch && hit == editor_track::point_ref{1, editor_track::point_kind::end}};
auto const diverging{selected->eType == tt_Switch && hit == editor_track::point_ref{1, editor_track::point_kind::end} && m_track_layout != track_layout::straight};
return make(kind::extend_end, diverging ? "Drag: the curve of the switch goes on; Ctrl+drag: move the end" : "Drag: a new track out of this end, as in laying; Ctrl+drag: move the end");
}
}

View File

@@ -12285,3 +12285,15 @@ msgstr "Wstrzymaj"
msgid "Terrain model: %d of %d squares in the cache, %d failed"
msgstr "Model terenu: %d z %d kwadratów w pamięci podręcznej, nieudanych %d"
msgid "Tab while laying: an arc, then a straight"
msgstr "Tab przy kładzeniu: łuk, potem prosta"
msgid "Ctrl+wheel: radius, Tab while laying: a straight"
msgstr "Ctrl+kółko: promień, Tab przy kładzeniu: prosta"
msgid "Lead a track out of it: a straight, an arc and a straight where a curve is traced (Tab); onto another free end: join the two"
msgstr "Wyprowadza z niego tor: prostą, a w trybie łuku (Tab) łuk i prostą; na inny wolny koniec: łączy oba"
msgid "Dragging over another free end: another way to join"
msgstr "Przeciąganie nad innym wolnym końcem: inny sposób połączenia"