mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-03 01:30:16 +01:00
feat(editor): the tracks are laid to the PKP PLK norms only: the choice of the free laying is gone, its cards in the panels of the laying and of the plan, the question as the laying starts and the setting; the transition curves and the cant of the norms always, the speed of the laying with its chips, the radius below the minimum for the speed a warning. The settings keep the radii the bends of the straights will be eased with instead
This commit is contained in:
@@ -426,7 +426,7 @@ 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();
|
||||
if (false == m_lay.transitions || From == nullptr || From->eType != tt_Normal)
|
||||
if (From == nullptr || From->eType != tt_Normal)
|
||||
return std::nullopt;
|
||||
auto const end{editor_track::bend_at(*From, Point)};
|
||||
if (false == end.has_value())
|
||||
@@ -571,13 +571,13 @@ std::vector<segment_data> editor_mode::join_pieces(TTrack const *From, editor_tr
|
||||
request.end.roll = End.track != nullptr ? editor_track::joint_roll(*End.track, End.point) : 0.0;
|
||||
request.radius = m_join_radius;
|
||||
request.variant = m_join_variant;
|
||||
request.transitions = m_lay.transitions;
|
||||
request.transitions = true;
|
||||
request.speed = design_speed(From, End.track);
|
||||
request.kink = m_joints.kink_angle() * 0.5;
|
||||
request.shape = m_route.design.shape;
|
||||
request.norms = laying_norms();
|
||||
auto result{alignment::join(request)};
|
||||
if (m_lay.transitions && result.kind != alignment::join_kind::straight && result.kind != alignment::join_kind::arc && glm::length(request.end.direction) > 1e-9)
|
||||
if (result.kind != alignment::join_kind::straight && result.kind != alignment::join_kind::arc && glm::length(request.end.direction) > 1e-9)
|
||||
{
|
||||
auto const grade{(End.position.y - Start.y) / std::max(1.0, plan_distance(Start, End.position))};
|
||||
auto const in{From != nullptr && From->eType == tt_Normal ? editor_track::ease_out_of(*From, Start, grade, request.speed, request.norms) : editor_track::easing{}};
|
||||
@@ -989,8 +989,8 @@ std::vector<segment_data> editor_mode::lay_pieces(std::vector<glm::dvec3> const
|
||||
result.pieces.clear();
|
||||
return result;
|
||||
};
|
||||
auto const in{tool.transitions ? eased(tool.start, 0.0, 1.0) : editor_track::easing{}};
|
||||
auto const out{tool.transitions ? eased(End, shape.length, -1.0) : editor_track::easing{}};
|
||||
auto const in{eased(tool.start, 0.0, 1.0)};
|
||||
auto const out{eased(End, shape.length, -1.0)};
|
||||
if ((false == in.pieces.empty() || false == out.pieces.empty()) && editor_track::eases_room(in, out, Points.front(), design.start_direction, Points.back(), design.end_direction))
|
||||
{
|
||||
auto points{Points};
|
||||
@@ -1063,7 +1063,7 @@ alignment::result editor_mode::lay_shape(std::vector<glm::dvec3> const &Points,
|
||||
design.vertices.back().offset = std::max(1.0, glm::dot(plan_of(Points.back() - Points[Points.size() - 2]), design.end_direction));
|
||||
}
|
||||
}
|
||||
return alignment::compute_recommended(design, tool.transitions);
|
||||
return alignment::compute_recommended(design, true);
|
||||
}
|
||||
|
||||
void editor_mode::lay_click()
|
||||
@@ -1360,202 +1360,26 @@ editor_mode::track_hover editor_mode::split_spot() const
|
||||
alignment::limits editor_mode::laying_norms() const
|
||||
{
|
||||
auto norms{m_route.design.norms};
|
||||
norms.checked = false == m_norms_free;
|
||||
norms.checked = true;
|
||||
return norms;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// a card of the geometry of the new track: its plan, and under it its curvature along the length, a point running along both;
|
||||
// a plain arc jumps to its curvature, a railway curve leads into it along a transition, the cant with it
|
||||
float geometry_card_height(char const *Title, char const *Text, float const Width)
|
||||
{
|
||||
auto const font{ImGui::GetFontSize()};
|
||||
auto const pad{std::round(font * 0.5f)};
|
||||
auto const wrap{Width - 2.0f * pad};
|
||||
return pad + ImGui::CalcTextSize(Title, nullptr, false, wrap).y + pad * 0.5f + std::round(font * 2.6f) + pad * 0.5f + ImGui::CalcTextSize(Text, nullptr, false, wrap).y + pad;
|
||||
}
|
||||
|
||||
bool geometry_card(char const *Id, char const *Title, char const *Text, bool const Railway, bool const Chosen, float const Width, float const Height)
|
||||
{
|
||||
auto const font{ImGui::GetFontSize()};
|
||||
auto const pad{std::round(font * 0.5f)};
|
||||
auto const sketch{std::round(font * 2.6f)};
|
||||
auto const wrap{Width - 2.0f * pad};
|
||||
auto const title{ImGui::CalcTextSize(Title, nullptr, false, wrap)};
|
||||
auto const height{Height};
|
||||
auto const top{ImGui::GetCursorScreenPos()};
|
||||
auto const clicked{ImGui::InvisibleButton(Id, ImVec2(Width, height))};
|
||||
auto const hovered{ImGui::IsItemHovered()};
|
||||
auto *list{ImGui::GetWindowDrawList()};
|
||||
auto const accent{family_colour(0)};
|
||||
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 y{top.y + pad};
|
||||
list->AddText(nullptr, 0.0f, ImVec2(top.x + pad, y), ImGui::GetColorU32(ImGuiCol_Text), Title, nullptr, wrap);
|
||||
y += title.y + pad * 0.5f;
|
||||
// the curvature along the length, as a share of the curvature of the arc
|
||||
auto const curvature = [Railway](double const Share) {
|
||||
if (Railway)
|
||||
return std::clamp((Share - 0.22) / 0.3, 0.0, 1.0);
|
||||
return Share < 0.37 ? 0.0 : 1.0;
|
||||
};
|
||||
int constexpr steps{80};
|
||||
std::vector<ImVec2> plan;
|
||||
plan.reserve(steps + 1);
|
||||
double heading{0.0}, px{0.0}, py{0.0};
|
||||
auto const turn{1.45 / (Railway ? 0.63 - 0.15 : 0.63)};
|
||||
for (int i = 0; i <= steps; ++i)
|
||||
{
|
||||
plan.emplace_back(static_cast<float>(px), static_cast<float>(py));
|
||||
auto const share{(i + 0.5) / steps};
|
||||
heading += curvature(share) * turn / steps;
|
||||
px += std::cos(heading) / steps;
|
||||
py -= std::sin(heading) / steps;
|
||||
}
|
||||
ImVec2 low{plan.front()}, high{plan.front()};
|
||||
for (auto const &point : plan)
|
||||
{
|
||||
low = ImVec2(std::min(low.x, point.x), std::min(low.y, point.y));
|
||||
high = ImVec2(std::max(high.x, point.x), std::max(high.y, point.y));
|
||||
}
|
||||
// the plan on the left, the curvature along the length on the right
|
||||
auto const half{std::floor((wrap - pad) * 0.5f)};
|
||||
auto const scale{std::min(half / std::max(1e-3f, high.x - low.x), sketch / std::max(1e-3f, high.y - low.y))};
|
||||
ImVec2 const origin{top.x + pad + (half - (high.x - low.x) * scale) * 0.5f, y + sketch - (high.y - low.y) * scale};
|
||||
for (auto &point : plan)
|
||||
point = ImVec2(origin.x + (point.x - low.x) * scale, origin.y + (point.y - low.y) * scale);
|
||||
auto const line{Chosen ? accent : ImGui::GetColorU32(ImGuiCol_Text, 0.75f)};
|
||||
list->AddPolyline(plan.data(), static_cast<int>(plan.size()), line, 0, 2.0f);
|
||||
auto const label{ImGui::CalcTextSize("1/R")};
|
||||
auto const graph_left{top.x + pad + half + pad * 0.5f + label.x + 2.0f};
|
||||
auto const graph_width{top.x + pad + wrap - graph_left};
|
||||
auto const graph_top{y};
|
||||
auto const graph{sketch};
|
||||
auto const axis{ImGui::GetColorU32(ImGuiCol_TextDisabled, 0.6f)};
|
||||
list->AddLine(ImVec2(graph_left, graph_top + graph), ImVec2(graph_left + graph_width, graph_top + graph), axis);
|
||||
list->AddLine(ImVec2(graph_left, graph_top), ImVec2(graph_left, graph_top + graph), axis);
|
||||
list->AddText(ImVec2(graph_left - label.x - 2.0f, graph_top + graph * 0.2f - label.y * 0.5f), axis, "1/R");
|
||||
std::vector<ImVec2> curve;
|
||||
for (int i = 0; i <= steps; ++i)
|
||||
{
|
||||
auto const share{static_cast<double>(i) / steps};
|
||||
curve.emplace_back(graph_left + static_cast<float>(share) * graph_width, graph_top + graph - static_cast<float>(curvature(share)) * graph * 0.8f);
|
||||
}
|
||||
list->AddPolyline(curve.data(), static_cast<int>(curve.size()), line, 0, 1.5f);
|
||||
if (Railway)
|
||||
for (int i = 0; i < steps; i += 4)
|
||||
{
|
||||
auto const share{static_cast<double>(i) / steps};
|
||||
auto const next{static_cast<double>(i + 2) / steps};
|
||||
list->AddLine(ImVec2(graph_left + static_cast<float>(share) * graph_width, graph_top + graph - static_cast<float>(curvature(share)) * graph * 0.45f),
|
||||
ImVec2(graph_left + static_cast<float>(next) * graph_width, graph_top + graph - static_cast<float>(curvature(next)) * graph * 0.45f), IM_COL32(240, 170, 60, 220), 1.0f);
|
||||
}
|
||||
auto const running{std::fmod(ImGui::GetTime() * 0.22, 1.0)};
|
||||
auto const index{std::clamp(static_cast<int>(running * steps), 0, steps)};
|
||||
list->AddCircleFilled(plan[index], 3.0f, IM_COL32(255, 255, 255, 230));
|
||||
list->AddLine(ImVec2(curve[index].x, graph_top), ImVec2(curve[index].x, graph_top + graph), IM_COL32(255, 255, 255, 70));
|
||||
list->AddCircleFilled(curve[index], 2.5f, IM_COL32(255, 255, 255, 230));
|
||||
y += sketch + pad * 0.5f;
|
||||
list->AddText(nullptr, 0.0f, ImVec2(top.x + pad, y), ImGui::GetColorU32(ImGuiCol_TextDisabled), Text, nullptr, wrap);
|
||||
return clicked;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool editor_mode::render_norms_choice()
|
||||
{
|
||||
auto picked{false};
|
||||
auto const gap{std::round(ImGui::GetFontSize() * 0.5f)};
|
||||
auto const width{std::floor((ImGui::GetContentRegionAvail().x - gap) * 0.5f)};
|
||||
char const *const titles[]{STR_C("Free laying"), STR_C("To the PKP PLK norms")};
|
||||
char const *const texts[]{STR_C("Plain arcs, no transition curves nor cant"), STR_C("Transition curves and cant for the speed, checked against the norms")};
|
||||
auto const height{std::max(geometry_card_height(titles[0], texts[0], width), geometry_card_height(titles[1], texts[1], width))};
|
||||
ImGui::PushID("geometry");
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (i > 0)
|
||||
ImGui::SameLine(0.0f, gap);
|
||||
auto const chosen{m_norms_free == (i == 0)};
|
||||
auto const clicked{geometry_card(i == 0 ? "##free" : "##norms", titles[i], texts[i], i == 1, chosen, width, height)};
|
||||
item_tooltip(i == 0 ? "Plain arcs with no transition curves and no cant, no warnings about the norms: the track goes where it's clicked"
|
||||
: "Transition curves and cant for the design speed, warnings when the radius, the cant or the transitions don't meet the norms");
|
||||
picked |= clicked;
|
||||
if (clicked && false == chosen)
|
||||
{
|
||||
set_norms_free(i == 0);
|
||||
// a kept choice follows the one made later
|
||||
if (EditorSettings.norms() != editorSettings::track_norms::ask)
|
||||
{
|
||||
EditorSettings.norms(m_norms_free ? editorSettings::track_norms::free_laying : editorSettings::track_norms::plk);
|
||||
EditorSettings.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
return picked;
|
||||
}
|
||||
|
||||
void editor_mode::render_norms_prompt()
|
||||
{
|
||||
if (false == m_norms_prompt)
|
||||
return;
|
||||
char const *const title{STR_C("How to lay the tracks###normsprompt")};
|
||||
if (false == ImGui::IsPopupOpen(title))
|
||||
ImGui::OpenPopup(title);
|
||||
ImGui::SetNextWindowSize(ImVec2(520.0f * Global.ui_scale, 0.0f));
|
||||
if (false == ImGui::BeginPopupModal(title, nullptr, ImGuiWindowFlags_NoResize))
|
||||
return;
|
||||
ImGui::TextWrapped("%s", STR_C("Choose how the new tracks are laid. It can be changed any time at the top of the panels of laying and of the plan."));
|
||||
ImGui::Spacing();
|
||||
if (render_norms_choice())
|
||||
{
|
||||
if (m_norms_remember)
|
||||
{
|
||||
EditorSettings.norms(m_norms_free ? editorSettings::track_norms::free_laying : editorSettings::track_norms::plk);
|
||||
EditorSettings.save();
|
||||
}
|
||||
m_norms_prompt = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::Checkbox(STR_C("Remember the choice, don't ask again"), &m_norms_remember);
|
||||
item_tooltip("Asking again can be switched on in the settings of the editor (General)");
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
void editor_mode::set_norms_free(bool const Free)
|
||||
{
|
||||
m_norms_free = Free;
|
||||
m_lay.transitions = false == m_norms_free;
|
||||
if (false == m_route.chain.tracks.empty())
|
||||
route_update();
|
||||
}
|
||||
|
||||
void editor_mode::render_lay_ui()
|
||||
{
|
||||
auto &tool{m_lay};
|
||||
render_norms_choice();
|
||||
ImGui::Separator();
|
||||
ImGui::PushItemWidth(120.0f);
|
||||
ImGui::DragScalar(STR_C("Curve radius (m)"), ImGuiDataType_Double, &tool.radius, 5.0f, nullptr, nullptr, "%.0f");
|
||||
item_tooltip("Drag to change, Ctrl+click to type the value. While laying: Ctrl+wheel");
|
||||
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::DragScalar(STR_C("Speed (km/h)"), ImGuiDataType_Double, &tool.style.velocity, 1.0f, nullptr, nullptr, "%.0f");
|
||||
item_tooltip(m_norms_free ? "Speed limit of the new paths; the speed of the track joined comes first"
|
||||
: "Speed limit of the new paths, and the design speed of the transition curves and of the cant; the speed of the track joined comes first, and none higher than the radius allows");
|
||||
if (false == m_norms_free)
|
||||
value_chips("speeds", tool.style.velocity, {40.0, 60.0, 80.0, 100.0, 120.0, 160.0}, "%.0f");
|
||||
item_tooltip("Speed limit of the new paths, and the design speed of the transition curves and of the cant; the speed of the track joined comes first, and none higher than the radius allows");
|
||||
value_chips("speeds", tool.style.velocity, {40.0, 60.0, 80.0, 100.0, 120.0, 160.0}, "%.0f");
|
||||
tool.style.velocity = std::clamp(tool.style.velocity, 0.0, 400.0);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Checkbox(STR_C("This speed also on from a track with its own"), &tool.own_speed);
|
||||
item_tooltip(m_norms_free ? "Off: a track laid on from another one, or joined to it, takes the speed of that track.\nOn: the new tracks get the speed above"
|
||||
: "Off: a track laid on from another one, or joined to it, is designed for the speed of that track and takes it.\nOn: it's designed for the speed above and the new tracks get it.\nWhile laying: Ctrl+Shift+wheel");
|
||||
if (auto const [from, to]{laying_ends()}; false == m_norms_free && (from != nullptr || to != nullptr))
|
||||
item_tooltip("Off: a track laid on from another one, or joined to it, is designed for the speed of that track and takes it.\nOn: it's designed for the speed above and the new tracks get it.\nWhile laying: Ctrl+Shift+wheel");
|
||||
if (auto const [from, to]{laying_ends()}; from != nullptr || to != nullptr)
|
||||
ImGui::TextDisabled("%s", design_speed_of(from, to).c_str());
|
||||
ImGui::TextDisabled("%s", STR_C("Height of the track on the ground"));
|
||||
if (int heights{static_cast<int>(EditorSettings.heights())};
|
||||
|
||||
Reference in New Issue
Block a user