mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-26 23:59:48 +01:00
fix(editor): the band of the curvature edits each arc of a compound curve: its arcs, elements of an even curvature, were taken for nothing to drag, and any change went to the first radius; now the plateau of an arc changes its own radius, a ramp between two arcs the transition between them, the ends of a plateau the transitions either side of it, by a drag or the wheel, the tooltip and the value shown while dragging naming the arc. The tutorial shows the correcting of a compound curve in a clip recorded in the editor in place of the drawing: the plan of the curve opened, the plateau of R2 dragged from 138 to 174 m, the line applied to the scenery
This commit is contained in:
@@ -5046,85 +5046,6 @@ void tutorial_trainset_sketch(ImDrawList &List, ImVec2 const Low, ImVec2 const H
|
||||
List.AddText(ImVec2(cursor.x + height * 0.16f, cursor.y), IM_COL32(255, 220, 120, 255), "x2");
|
||||
}
|
||||
|
||||
// the plan of a curve over the band of its curvature: the plateau of the arc goes up and down under the mouse, the curve tightens
|
||||
// and opens with it over the plan kept faint as it was
|
||||
void tutorial_curve_sketch(ImDrawList &List, ImVec2 const Low, ImVec2 const High, double const Time)
|
||||
{
|
||||
auto const width{High.x - Low.x};
|
||||
auto const height{High.y - Low.y};
|
||||
auto const lift{static_cast<float>(0.5 + 0.5 * std::sin(Time * 1.3))};
|
||||
auto const curvature = [](float const Share, float const Peak) {
|
||||
if (Share < 0.15f || Share > 0.85f)
|
||||
return 0.0f;
|
||||
if (Share < 0.35f)
|
||||
return Peak * (Share - 0.15f) / 0.2f;
|
||||
if (Share > 0.65f)
|
||||
return Peak * (0.85f - Share) / 0.2f;
|
||||
return Peak;
|
||||
};
|
||||
auto const colour = [](float const Share) {
|
||||
if (Share < 0.15f || Share > 0.85f)
|
||||
return IM_COL32(255, 255, 255, 230);
|
||||
if (Share < 0.35f || Share > 0.65f)
|
||||
return IM_COL32(255, 140, 30, 255);
|
||||
return IM_COL32(60, 230, 90, 255);
|
||||
};
|
||||
int constexpr steps{90};
|
||||
auto const plan_height{height * 0.5f};
|
||||
float scale{1.0f};
|
||||
ImVec2 start{0.0f, 0.0f};
|
||||
auto const plan = [&](float const Peak) {
|
||||
std::vector<ImVec2> points;
|
||||
float heading{0.0f}, x{0.0f}, y{0.0f};
|
||||
for (int i = 0; i <= steps; ++i)
|
||||
{
|
||||
points.emplace_back(start.x + x * scale, start.y + y * scale);
|
||||
auto const share{(i + 0.5f) / steps};
|
||||
heading += curvature(share, Peak) / steps;
|
||||
x += std::cos(heading) / steps;
|
||||
y -= std::sin(heading) / steps;
|
||||
}
|
||||
return points;
|
||||
};
|
||||
// the scale fits the plan of the sharpest curve, the least sharp one is the widest
|
||||
auto const sharpest{plan(2.8f)};
|
||||
auto const widest{plan(1.6f)};
|
||||
scale = std::min(width * 0.85f / std::max(1e-3f, widest.back().x), plan_height / std::max(1e-3f, -sharpest.back().y));
|
||||
start = ImVec2(Low.x + (width - widest.back().x * scale) * 0.5f, Low.y + height * 0.06f + plan_height);
|
||||
auto const before{plan(1.6f)};
|
||||
List.AddPolyline(before.data(), static_cast<int>(before.size()), IM_COL32(170, 170, 170, 90), 0, 1.5f);
|
||||
auto const peak{1.6f + 1.2f * lift};
|
||||
auto const now{plan(peak)};
|
||||
for (int i = 0; i < steps; ++i)
|
||||
List.AddLine(now[i], now[i + 1], colour((i + 0.5f) / steps), 3.0f);
|
||||
// the band under the plan
|
||||
auto const band_top{Low.y + height * 0.66f};
|
||||
auto const band_bottom{High.y - height * 0.06f};
|
||||
auto const left{Low.x + width * 0.06f};
|
||||
auto const band_width{width * 0.88f};
|
||||
List.AddRectFilled(ImVec2(left - 6.0f, band_top - 6.0f), ImVec2(left + band_width + 6.0f, band_bottom + 4.0f), ImGui::GetColorU32(ImGuiCol_FrameBg), 4.0f);
|
||||
List.AddLine(ImVec2(left, band_bottom), ImVec2(left + band_width, band_bottom), ImGui::GetColorU32(ImGuiCol_TextDisabled));
|
||||
auto const k_scale{(band_bottom - band_top) * 0.9f / 2.8f};
|
||||
for (int i = 0; i < steps; ++i)
|
||||
{
|
||||
auto const a{static_cast<float>(i) / steps};
|
||||
auto const b{static_cast<float>(i + 1) / steps};
|
||||
ImVec2 const p{left + a * band_width, band_bottom - curvature(a, peak) * k_scale};
|
||||
ImVec2 const q{left + b * band_width, band_bottom - curvature(b, peak) * k_scale};
|
||||
auto const tint{colour((a + b) * 0.5f)};
|
||||
auto const flags{List.Flags};
|
||||
List.Flags &= ~ImDrawListFlags_AntiAliasedFill;
|
||||
List.AddQuadFilled(ImVec2(p.x, band_bottom), p, q, ImVec2(q.x, band_bottom), (tint & ~IM_COL32_A_MASK) | IM_COL32(0, 0, 0, 50));
|
||||
List.Flags = flags;
|
||||
List.AddLine(p, q, tint, 2.0f);
|
||||
}
|
||||
char text[32];
|
||||
std::snprintf(text, sizeof(text), "R %.0f", std::round(900.0f / peak / 5.0f) * 5.0f);
|
||||
ImVec2 const plateau{left + band_width * 0.5f, band_bottom - peak * k_scale};
|
||||
List.AddText(ImVec2(plateau.x - ImGui::CalcTextSize(text).x * 0.5f, plateau.y - ImGui::GetTextLineHeight() - 2.0f), IM_COL32(60, 230, 90, 255), text);
|
||||
gesture_mouse(List, ImVec2(plateau.x + band_width * 0.08f, plateau.y - height * 0.06f), height * 0.16f, "LMB");
|
||||
}
|
||||
|
||||
// the terrain model under the tracks: the track laid level sinks onto the ground along, the formation and the slopes under it
|
||||
void tutorial_terrain_sketch(ImDrawList &List, ImVec2 const Low, ImVec2 const High, double const Time)
|
||||
{
|
||||
@@ -5238,15 +5159,14 @@ void editor_mode::render_first_steps()
|
||||
{{first_step::bend, "Lay a track with a bend in it"}, {first_step::trace, "Switch to tracing a curve with Tab"}},
|
||||
trial::lay},
|
||||
{"Correcting a curve",
|
||||
nullptr,
|
||||
"plan",
|
||||
{{"G", "The plan tool: a click on a curve shows its design, the vertex and the radius to drag in the view"},
|
||||
{"LMB + move", "In the panel, the band of the curvature: drag the plateau of the arc up or down for its radius"},
|
||||
{"LMB + move", "In the panel, the band of the curvature: drag the plateau of an arc up or down for its radius, each arc of a compound curve its own"},
|
||||
{"LMB + move", "Drag a ramp sideways: the transition curve gets longer or shorter"},
|
||||
{"wheel", "Over the band: the radius by 5%, a transition by 5 m"},
|
||||
{"LMB", "Apply to the scenery: the tracks are laid by the design"}},
|
||||
{{first_step::plan, "Open the plan of a curve"}, {first_step::plan_band, "Change the curve in the band"}, {first_step::plan_apply, "Apply it to the scenery"}},
|
||||
trial::plan,
|
||||
tutorial_curve_sketch},
|
||||
trial::plan},
|
||||
{"Joining tracks",
|
||||
"join",
|
||||
{{"LMB + move", "Press on a free end (a magenta ring) and drag: a new track goes out of it"},
|
||||
|
||||
@@ -982,7 +982,8 @@ class editor_mode : public application_mode, private editor_track::observer, pri
|
||||
int hot{-1};
|
||||
double at{-1.0};
|
||||
int vertex{-1};
|
||||
int part{0}; // 1 the radius, 2 the transition in, 3 the transition out
|
||||
int part{0}; // 1 the radius, 2 the transition in, 3 the transition out, 4 the transition between two arcs of a compound curve
|
||||
int arc{0}; // the arc of a compound curve the radius is of, 0 the first; of a transition between arcs the one after it
|
||||
double value{0.0};
|
||||
ImVec2 press{0.0f, 0.0f};
|
||||
float metres{1.0f}; // pixels of a metre and of a unit of the curvature, kept as the drag goes
|
||||
|
||||
@@ -1694,7 +1694,46 @@ bool editor_mode::render_route_band()
|
||||
auto const mouse{ImGui::GetIO().MousePos};
|
||||
auto const count{static_cast<int>(result.elements.size())};
|
||||
|
||||
// the element under the mouse and what of it a drag changes: near the ends of a plateau its transitions, also the ones of no length yet
|
||||
// what of its vertex each element is: an arc, the arcs of a compound curve counted from the first one, which are elements of an even
|
||||
// curvature as an arc is, or a transition, in from the straight, out to it, or between two arcs, which goes on from the arc before it
|
||||
struct role
|
||||
{
|
||||
int part{0};
|
||||
int arc{0};
|
||||
};
|
||||
std::vector<role> roles(static_cast<std::size_t>(count));
|
||||
std::vector<int> arcs(design.vertices.size(), 0);
|
||||
{
|
||||
int vertex{-1};
|
||||
int arc{-1};
|
||||
double last{0.0};
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
auto const &element{result.elements[i]};
|
||||
if (element.vertex != vertex)
|
||||
{
|
||||
vertex = element.vertex;
|
||||
arc = -1;
|
||||
last = 0.0;
|
||||
}
|
||||
if (element.vertex < 0 || element.vertex >= static_cast<int>(design.vertices.size()) || element.kind == alignment::element_kind::straight)
|
||||
continue;
|
||||
auto const [from, to]{ends(element)};
|
||||
if (element.kind == alignment::element_kind::arc || (std::abs(from - to) <= 1e-6 * std::abs(from) && std::abs(from) > 1e-9))
|
||||
{
|
||||
// the next arc where the curvature isn't the one of the arc before, an arc cut in parts staying one
|
||||
if (arc < 0 || std::abs(std::abs(from) - last) > 1e-6 * std::abs(from))
|
||||
++arc;
|
||||
last = std::abs(from);
|
||||
roles[i] = {1, arc};
|
||||
arcs[element.vertex] = std::max(arcs[element.vertex], arc + 1);
|
||||
}
|
||||
else
|
||||
roles[i] = std::abs(from) <= 1e-9 ? role{2, 0} : std::abs(to) <= 1e-9 ? role{3, 0} : role{4, arc + 1};
|
||||
}
|
||||
}
|
||||
// the element under the mouse and what of it a drag changes: near the ends of a plateau the transitions either side of it, also the
|
||||
// ones of no length yet
|
||||
int under{-1};
|
||||
int part{0};
|
||||
if (hovered && false == dragging)
|
||||
@@ -1702,27 +1741,38 @@ bool editor_mode::render_route_band()
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
auto const &element{result.elements[i]};
|
||||
int arc{0};
|
||||
if (mouse.x < x_of(element.chainage) || mouse.x >= x_of(element.chainage + element.length))
|
||||
continue;
|
||||
under = i;
|
||||
auto const from{x_of(element.chainage)};
|
||||
auto const to{x_of(element.chainage + element.length)};
|
||||
auto const edge{std::min(std::round(font * 0.5f), (to - from) * 0.25f)};
|
||||
switch (element.kind)
|
||||
part = roles[i].part;
|
||||
arc = roles[i].arc;
|
||||
if (part == 1)
|
||||
{
|
||||
case alignment::element_kind::arc: part = mouse.x < from + edge ? 2 : mouse.x >= to - edge ? 3 : 1; break;
|
||||
case alignment::element_kind::transition_in: part = 2; break;
|
||||
case alignment::element_kind::transition_out: part = 3; break;
|
||||
default: break;
|
||||
auto const from{x_of(element.chainage)};
|
||||
auto const to{x_of(element.chainage + element.length)};
|
||||
auto const edge{std::min(std::round(font * 0.5f), (to - from) * 0.25f)};
|
||||
if (mouse.x < from + edge)
|
||||
part = arc == 0 ? 2 : 4;
|
||||
else if (mouse.x >= to - edge)
|
||||
{
|
||||
part = arc + 1 >= arcs[element.vertex] ? 3 : 4;
|
||||
arc += 1;
|
||||
}
|
||||
}
|
||||
if (element.vertex < 0 || element.vertex >= static_cast<int>(design.vertices.size()))
|
||||
part = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto const value_of = [&](int const Vertex, int const Part) -> double & {
|
||||
auto const value_of = [&](int const Vertex, int const Part, int const Arc) -> double & {
|
||||
auto &vertex{design.vertices[Vertex]};
|
||||
return Part == 1 ? vertex.radius : Part == 2 ? vertex.transition_in : vertex.transition_out;
|
||||
auto const later{Arc >= 1 && Arc <= static_cast<int>(vertex.arcs.size())};
|
||||
switch (Part)
|
||||
{
|
||||
case 1: return later ? vertex.arcs[Arc - 1].radius : vertex.radius;
|
||||
case 2: return vertex.transition_in;
|
||||
case 3: return vertex.transition_out;
|
||||
default: return later ? vertex.arcs[Arc - 1].transition : vertex.transition_in;
|
||||
}
|
||||
};
|
||||
auto const rounded_radius = [](double const Radius) {
|
||||
auto const step{Radius < 1000.0 ? 1.0 : 10.0};
|
||||
@@ -1733,14 +1783,15 @@ bool editor_mode::render_route_band()
|
||||
{
|
||||
band.vertex = result.elements[under].vertex;
|
||||
band.part = part;
|
||||
band.value = value_of(band.vertex, part);
|
||||
band.arc = arc;
|
||||
band.value = value_of(band.vertex, part, arc);
|
||||
band.press = mouse;
|
||||
band.metres = metres;
|
||||
band.curvature = curvature;
|
||||
}
|
||||
if (band.vertex >= 0 && band.vertex < static_cast<int>(design.vertices.size()) && active)
|
||||
{
|
||||
auto &value{value_of(band.vertex, band.part)};
|
||||
auto &value{value_of(band.vertex, band.part, band.arc)};
|
||||
double wanted{value};
|
||||
if (band.part == 1)
|
||||
{
|
||||
@@ -1759,6 +1810,7 @@ bool editor_mode::render_route_band()
|
||||
}
|
||||
if (wanted != value)
|
||||
{
|
||||
// the transition in grows to the left, from where it starts; the others to the right
|
||||
value = wanted;
|
||||
changed = true;
|
||||
}
|
||||
@@ -1773,7 +1825,7 @@ bool editor_mode::render_route_band()
|
||||
ImGui::SetMouseCursor(part == 1 ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW);
|
||||
if (auto const wheel{ImGui::GetIO().MouseWheel}; wheel != 0.0f)
|
||||
{
|
||||
auto &value{value_of(result.elements[under].vertex, part)};
|
||||
auto &value{value_of(result.elements[under].vertex, part, arc)};
|
||||
value = part == 1 ? rounded_radius(value * (wheel > 0.0f ? 0.95 : 1.0 / 0.95)) : std::max(0.0, std::round(value / 5.0) * 5.0 + (wheel > 0.0f ? 5.0 : -5.0));
|
||||
changed = true;
|
||||
}
|
||||
@@ -1862,8 +1914,8 @@ bool editor_mode::render_route_band()
|
||||
}
|
||||
if (dragging && band.vertex >= 0 && band.vertex < static_cast<int>(design.vertices.size()))
|
||||
{
|
||||
auto const &vertex{design.vertices[band.vertex]};
|
||||
auto const text{band.part == 1 ? format("R %.0f m", vertex.radius) : format("%.0f m", band.part == 2 ? vertex.transition_in : vertex.transition_out)};
|
||||
auto const value{value_of(band.vertex, band.part, band.arc)};
|
||||
auto const text{band.part == 1 ? format("R %.0f m", value) : format("%.0f m", value)};
|
||||
auto const size{ImGui::CalcTextSize(text.c_str())};
|
||||
ImVec2 const at{std::clamp(mouse.x + pad, top.x, bottom.x - size.x - pad), std::clamp(mouse.y - size.y - pad, top.y, bottom.y - size.y)};
|
||||
list->AddRectFilled(ImVec2(at.x - 3.0f, at.y - 1.0f), ImVec2(at.x + size.x + 3.0f, at.y + size.y + 1.0f), IM_COL32(0, 0, 0, 170), 3.0f);
|
||||
@@ -1875,12 +1927,18 @@ bool editor_mode::render_route_band()
|
||||
|
||||
if (part != 0 && false == dragging && ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
|
||||
{
|
||||
auto const &vertex{design.vertices[result.elements[under].vertex]};
|
||||
auto const index{result.elements[under].vertex};
|
||||
auto const &vertex{design.vertices[index]};
|
||||
auto const value{value_of(index, part, arc)};
|
||||
ImGui::BeginTooltip();
|
||||
if (part == 1)
|
||||
ImGui::Text(STR_C("Curve %d: R %.0f m, cant %.0f mm"), result.elements[under].vertex + 1, vertex.radius, vertex.cant);
|
||||
if (part == 1 && arcs[index] > 1)
|
||||
ImGui::Text(STR_C("Curve %d, arc R%d: %.0f m"), index + 1, arc + 1, value);
|
||||
else if (part == 1)
|
||||
ImGui::Text(STR_C("Curve %d: R %.0f m, cant %.0f mm"), index + 1, value, vertex.cant);
|
||||
else if (part == 4)
|
||||
ImGui::Text(STR_C("Curve %d, transition between R%d and R%d: %.0f m"), index + 1, arc, arc + 1, value);
|
||||
else
|
||||
ImGui::Text(part == 2 ? STR_C("Transition into curve %d: %.0f m") : STR_C("Transition out of curve %d: %.0f m"), result.elements[under].vertex + 1, part == 2 ? vertex.transition_in : vertex.transition_out);
|
||||
ImGui::Text(part == 2 ? STR_C("Transition into curve %d: %.0f m") : STR_C("Transition out of curve %d: %.0f m"), index + 1, value);
|
||||
gesture_rows({{"Drag", part == 1 ? "up or down: the radius" : "sideways: the length"}, {"Wheel", part == 1 ? "the radius by 5%" : "the length by 5 m"}});
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
@@ -7094,6 +7094,12 @@ msgstr "Łuk %d: R %.0f m, przechyłka %.0f mm"
|
||||
msgid "Transition into curve %d: %.0f m"
|
||||
msgstr "Przejściowa na wejściu w łuk %d: %.0f m"
|
||||
|
||||
msgid "Curve %d, arc R%d: %.0f m"
|
||||
msgstr "Łuk %d, promień R%d: %.0f m"
|
||||
|
||||
msgid "Curve %d, transition between R%d and R%d: %.0f m"
|
||||
msgstr "Łuk %d, przejściowa między R%d a R%d: %.0f m"
|
||||
|
||||
msgid "Transition out of curve %d: %.0f m"
|
||||
msgstr "Przejściowa na wyjściu z łuku %d: %.0f m"
|
||||
|
||||
@@ -7151,8 +7157,8 @@ msgstr "Poprawianie łuku"
|
||||
msgid "The plan tool: a click on a curve shows its design, the vertex and the radius to drag in the view"
|
||||
msgstr "Narzędzie Plan: klik na łuku pokazuje jego projekt, wierzchołek i promień do przeciągania w widoku"
|
||||
|
||||
msgid "In the panel, the band of the curvature: drag the plateau of the arc up or down for its radius"
|
||||
msgstr "W panelu pasek krzywizny: półka łuku przeciągnięta w górę albo w dół zmienia jego promień"
|
||||
msgid "In the panel, the band of the curvature: drag the plateau of an arc up or down for its radius, each arc of a compound curve its own"
|
||||
msgstr "W panelu pasek krzywizny: półka łuku przeciągnięta w górę albo w dół zmienia jego promień, każdy łuk koszowego osobno"
|
||||
|
||||
msgid "Drag a ramp sideways: the transition curve gets longer or shorter"
|
||||
msgstr "Rampa przeciągnięta w bok: krzywa przejściowa wydłuża się albo skraca"
|
||||
|
||||
BIN
lang/editor_tutorial/plan.gif
Normal file
BIN
lang/editor_tutorial/plan.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 461 KiB |
Reference in New Issue
Block a user