diff --git a/application/editormodeselftest.cpp b/application/editormodeselftest.cpp index ca07b718..fa2f6b73 100644 --- a/application/editormodeselftest.cpp +++ b/application/editormodeselftest.cpp @@ -9,6 +9,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "application/editormode.h" +#include "editor/editorSettings.hpp" #include "application/application.h" #include "application/editoruilayer.h" #include "application/editorprojection.h" @@ -161,6 +162,8 @@ void editor_mode::selftest_step() tool.length_mode = length == "original" ? 0 : length == "all" ? 2 : 1; if (tool.length_mode == 1) tool.length = std::stod(length); + if (values.count("edge")) + EditorSettings.platform_edge(std::stod(values["edge"])); tool.measured.clear(); tool.open = true; sweep_curve(); @@ -193,7 +196,10 @@ void editor_mode::selftest_step() { // edge of the last laid model as it was made: its distance from the axis and the height of its top over the rail head std::string name; - words >> name; + double step{20.0}; + words >> name >> step; + if (name == "-") + name.clear(); auto *sweep{name.empty() ? m_sweep.edited : simulation::Sweeps.find(name)}; if (sweep == nullptr) { @@ -216,14 +222,14 @@ void editor_mode::selftest_step() if (lateral * sign <= 0.0) continue; auto const axis{sweep->frame_at(station)}; - auto &entry{bins[static_cast(station / 20.0)]}; + auto &entry{bins[static_cast(std::floor(station / step))]}; entry.edge = std::min(entry.edge, std::abs(lateral)); entry.points.emplace_back(std::abs(lateral), vertex.position.y - axis.position.y); } WriteLog(format("SELFTEST probe %s: %zu stretches with the model, %.1f - %.1f m of %.1f m", sweep->name().c_str(), bins.size(), sweep->start(), sweep->end(), sweep->length())); for (auto const &[index, entry] : bins) { - auto const station{index * 20.0 + 10.0}; + auto const station{(index + 0.5) * step}; if (station < sweep->start() || station > sweep->end()) continue; double top{-1000.0}; diff --git a/application/editormodesweep.cpp b/application/editormodesweep.cpp index c7f589e3..ed5e10dc 100644 --- a/application/editormodesweep.cpp +++ b/application/editormodesweep.cpp @@ -14,6 +14,7 @@ http://mozilla.org/MPL/2.0/. #include "editor/editorFormat.hpp" #include "editor/editorGeometry.hpp" #include "editor/editorIncludeInfo.hpp" +#include "editor/editorSettings.hpp" #include "rendering/renderer.h" #include "scene/scenelayers.h" @@ -87,7 +88,7 @@ bool platform_model(std::string const &File) // the edge of the platform 1.725 m from the axis on the side it stands, set back further by the widening of the gauge void platform_edge(sweep_node::state &State, std::string const &Model, double const Base) { - State.lateral = std::copysign(1.725, State.lateral == 0.0 ? 1.0 : State.lateral); + State.lateral = std::copysign(EditorSettings.platform_edge(), State.lateral == 0.0 ? 1.0 : State.lateral); State.side_anchor = 1; State.height_anchor = 3; if (auto const height{platform_height(Model)}; height > 0.0) @@ -96,6 +97,23 @@ void platform_edge(sweep_node::state &State, std::string const &Model, double co State.platform = true; } +bool platform_distance() +{ + double const distances[]{1.725, 1.675, 1.650}; + char const *labels[]{STR_C("1.725 m: lines of out-of-gauge loads, older platforms"), STR_C("1.675 m: nominal by the 0.55 and 0.76 m platforms"), STR_C("1.650 m: the least allowed in service")}; + auto const current{EditorSettings.platform_edge()}; + int chosen{0}; + for (int i = 0; i < IM_ARRAYSIZE(distances); ++i) + if (std::abs(current - distances[i]) < std::abs(current - distances[chosen])) + chosen = i; + ImGui::SetNextItemWidth(300.0f); + if (false == ImGui::Combo(STR_C("edge of the platform from the axis"), &chosen, labels, IM_ARRAYSIZE(labels))) + return false; + EditorSettings.platform_edge(distances[chosen]); + EditorSettings.save(); + return true; +} + segment_data reversed(segment_data const &Path) { segment_data result{Path}; @@ -864,7 +882,12 @@ void editor_mode::render_sweep_ui() changed = true; } if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C("The edge of the platform 1.725 m from the axis of the track, its top this high over the rail top")); + ImGui::SetTooltip(STR_C("The edge of the platform %.3f m from the axis of the track, its top this high over the rail top"), EditorSettings.platform_edge()); + } + if (platform_distance() && settings.platform) + { + settings.lateral = std::copysign(EditorSettings.platform_edge(), settings.lateral == 0.0 ? 1.0 : settings.lateral); + changed = true; } ImGui::TextDisabled(STR_C("%.2f m %s of the axis, %.2f m over the rail top"), std::abs(settings.lateral), settings.lateral >= 0.0 ? STR_C("right") : STR_C("left"), settings.height); if (false == tool.curve.empty() && tool.model_length > 0.0 && ImGui::Button(STR_C("Lay it along the selected path"))) @@ -921,7 +944,7 @@ void editor_mode::render_sweep_ui() changed = true; } if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C("Puts the edge facing the track 1.725 m from its axis, set back by the widening of the gauge in the curves.\n" + ImGui::SetTooltip("%s", STR_C("Puts the edge facing the track at the distance chosen from its axis, set back by the widening of the gauge in the curves.\n" "Stands on the plane of the rail heads carried on to its edge: lower on the inner side of a canted curve,\n" "higher on the outer one; on the inner side also set back by the cant times the height of the edge / 1.5")); ImGui::TextDisabled("%s", STR_C("Length")); @@ -1676,8 +1699,13 @@ void editor_mode::render_bend() } changed = true; } + if (state.platform && platform_distance()) + { + state.lateral = std::copysign(EditorSettings.platform_edge(), state.lateral); + changed = true; + } if (ImGui::IsItemHovered()) - ImGui::SetTooltip("%s", STR_C("Puts the edge facing the track 1.725 m from its axis, set back by the widening of the gauge in the curves.\n" + ImGui::SetTooltip("%s", STR_C("Puts the edge facing the track at the distance chosen from its axis, set back by the widening of the gauge in the curves.\n" "Stands on the plane of the rail heads carried on to its edge: lower on the inner side of a canted curve,\n" "higher on the outer one; on the inner side also set back by the cant times the height of the edge / 1.5")); if (changed) @@ -1717,9 +1745,11 @@ void editor_mode::render_bend() ImGui::SetNextItemWidth(170.0f); ImGui::Combo(STR_C("axis of the model along the track"), &tool.axis, axes, IM_ARRAYSIZE(axes)); ImGui::Checkbox(STR_C("Turned around"), &tool.turned); + if (platform_model(source.file)) + platform_distance(); bool anchored{false}; anchors(anchored); - auto const signature{format("%p %d %d %d %d %d %d %.4f %.4f %.4f", key, tool.track, tool.axis, tool.turned ? 1 : 0, tool.along_anchor, tool.side_anchor, tool.height_anchor, tool.point.x, tool.point.y, tool.point.z)}; + auto const signature{format("%p %d %d %d %d %d %d %.4f %.4f %.4f %.3f", key, tool.track, tool.axis, tool.turned ? 1 : 0, tool.along_anchor, tool.side_anchor, tool.height_anchor, tool.point.x, tool.point.y, tool.point.z, EditorSettings.platform_edge())}; if (signature != tool.signature) { tool.signature = signature; diff --git a/editor/editorSettings.cpp b/editor/editorSettings.cpp index f9725818..b4c5fb31 100644 --- a/editor/editorSettings.cpp +++ b/editor/editorSettings.cpp @@ -92,6 +92,8 @@ bool editorSettings::load() parse(value, m_orthophoto.lift); else if (key == "ortho_in_scene") m_orthophoto.in_scene = (value == "1"); + else if (key == "platform_edge") + parse(value, m_platform_edge); else if (key.rfind(origin_prefix, 0) == 0) { // northing;easting @@ -130,6 +132,7 @@ bool editorSettings::save() stream << "ortho_drape " << (m_orthophoto.drape ? 1 : 0) << "\n"; stream << "ortho_lift " << m_orthophoto.lift << "\n"; stream << "ortho_in_scene " << (m_orthophoto.in_scene ? 1 : 0) << "\n"; + stream << "platform_edge " << m_platform_edge << "\n"; stream << std::fixed << std::setprecision(3); for (auto const &origin : m_orthophoto_origins) stream << origin.first << " " << origin.second.first << ";" << origin.second.second << "\n"; diff --git a/editor/editorSettings.hpp b/editor/editorSettings.hpp index b43410fc..6e39cfa0 100644 --- a/editor/editorSettings.hpp +++ b/editor/editorSettings.hpp @@ -39,10 +39,14 @@ public: bool orthophoto_origin(std::string const &Scenery, double &North, double &East) const; void orthophoto_origin(std::string const &Scenery, double North, double East); + double platform_edge() const { return m_platform_edge; } + void platform_edge(double Distance) { m_platform_edge = Distance; } + private: movement_scheme m_movement{movement_scheme::wsad}; orthophoto_settings m_orthophoto; std::map> m_orthophoto_origins; + double m_platform_edge{1.725}; }; // global editor settings instance diff --git a/lang/editor_pl.po b/lang/editor_pl.po index 1fb3fa0b..cd6f4948 100644 --- a/lang/editor_pl.po +++ b/lang/editor_pl.po @@ -2599,8 +2599,8 @@ msgstr "wierzch" msgid "Platform edge:" msgstr "Krawędź peronu:" -msgid "The edge of the platform 1.725 m from the axis of the track, its top this high over the rail top" -msgstr "Krawędź peronu 1,725 m od osi toru, wierzch tyle nad główką szyny" +msgid "The edge of the platform %.3f m from the axis of the track, its top this high over the rail top" +msgstr "Krawędź peronu %.3f m od osi toru, wierzch tyle nad główką szyny" msgid "Parameters of the template" msgstr "Parametry szablonu" @@ -4389,5 +4389,17 @@ msgstr "LPM na torze: tam stanie pojazd" msgid "Platform" msgstr "Peron" -msgid "Puts the edge facing the track 1.725 m from its axis, set back by the widening of the gauge in the curves.\nStands on the plane of the rail heads carried on to its edge: lower on the inner side of a canted curve,\nhigher on the outer one; on the inner side also set back by the cant times the height of the edge / 1.5" -msgstr "Ustawia krawędź od strony toru 1,725 m od jego osi, odsuniętą o poszerzenie skrajni na łukach.\nStoi na płaszczyźnie główek szyn przedłużonej do krawędzi: niżej po wewnętrznej stronie łuku z przechyłką,\nwyżej po zewnętrznej; po wewnętrznej dodatkowo odsunięty o przechyłkę razy wysokość krawędzi / 1,5" +msgid "Puts the edge facing the track at the distance chosen from its axis, set back by the widening of the gauge in the curves.\nStands on the plane of the rail heads carried on to its edge: lower on the inner side of a canted curve,\nhigher on the outer one; on the inner side also set back by the cant times the height of the edge / 1.5" +msgstr "Ustawia krawędź od strony toru w wybranej odległości od jego osi, odsuniętą o poszerzenie skrajni na łukach.\nStoi na płaszczyźnie główek szyn przedłużonej do krawędzi: niżej po wewnętrznej stronie łuku z przechyłką,\nwyżej po zewnętrznej; po wewnętrznej dodatkowo odsunięty o przechyłkę razy wysokość krawędzi / 1,5" + +msgid "1.725 m: lines of out-of-gauge loads, older platforms" +msgstr "1,725 m: linie przesyłek z przekroczoną skrajnią, starsze perony" + +msgid "1.675 m: nominal by the 0.55 and 0.76 m platforms" +msgstr "1,675 m: nominalnie przy peronach 0,55 i 0,76 m" + +msgid "1.650 m: the least allowed in service" +msgstr "1,650 m: najmniej dopuszczalne w eksploatacji" + +msgid "edge of the platform from the axis" +msgstr "krawędź peronu od osi toru" diff --git a/world/Sweep.cpp b/world/Sweep.cpp index 422f03aa..8e4f76cb 100644 --- a/world/Sweep.cpp +++ b/world/Sweep.cpp @@ -129,12 +129,14 @@ std::vector paths_beyond(segment_data Last, std::vector 0.01 || - glm::dot(plan_heading(Last, 1.0), plan_heading(candidate, 0.0)) < 0.9) + if (glm::distance(candidate.points[segment_data::point::start], at) > 0.01 || glm::dot(plan_heading(Last, 1.0), plan_heading(candidate, 0.0)) < 0.9) continue; auto const known = [&](segment_data const &Piece) { return same_points(Piece, candidate) || same_points(turned_piece(Piece), candidate); }; if (std::any_of(Own.begin(), Own.end(), known) || std::any_of(result.begin(), result.end(), known)) continue; + auto const straight{glm::dot(plan_heading(candidate, 0.0), plan_heading(candidate, 1.0))}; + if (next.has_value() && straight <= glm::dot(plan_heading(*next, 0.0), plan_heading(*next, 1.0))) + continue; next = candidate; } } @@ -557,97 +559,108 @@ void sweep_node::gauge_along(std::vector &Samples) } } -void sweep_node::beside_switches(std::vector> const &Spans) +void sweep_node::beside_switches(std::vector &Samples) { + if (Samples.size() < 3) + return; auto const side{m_state.lateral >= 0.0 ? 1 : 0}; auto const sign{m_state.lateral >= 0.0 ? 1.0 : -1.0}; auto const edgeheight{std::max(0.0, m_state.height - gauge::rail_height)}; auto const total = [&](sample const &Sample) { return Sample.widening[side] + std::max(0.0, Sample.cant[side]) * edgeheight / 1.5; }; - auto const matches = [](segment_data const &A, segment_data const &B) { - for (int i = 0; i < 4; ++i) - if (glm::distance(A.points[i], B.points[i]) > 1e-3) - return false; - return true; + auto const plan = [](glm::dvec3 const &Vector) { + glm::dvec3 const flat{Vector.x, 0.0, Vector.z}; + return glm::length(flat) > 1e-9 ? glm::normalize(flat) : glm::dvec3{0.0, 0.0, 1.0}; }; - auto const turned = [](segment_data const &Path) { - segment_data result{Path}; - result.points[segment_data::point::start] = Path.points[segment_data::point::end]; - result.points[segment_data::point::end] = Path.points[segment_data::point::start]; - result.points[segment_data::point::control1] = Path.points[segment_data::point::control2]; - result.points[segment_data::point::control2] = Path.points[segment_data::point::control1]; - result.rolls = {-Path.rolls[1], -Path.rolls[0]}; - return result; - }; - for (std::size_t piece = 0; piece < m_state.pieces.size() && piece < Spans.size(); ++piece) + glm::dvec3 low{std::numeric_limits::max()}, high{-std::numeric_limits::max()}; + for (auto const &sample : Samples) { - auto const &own{m_state.pieces[piece]}; - for (auto *track : simulation::Paths.sequence()) - { - if (track == nullptr || track->eType != tt_Switch || track->m_paths.size() < 2 || - plan_distance(track->m_paths.front().points[segment_data::point::start], own.points[segment_data::point::start]) > 300.0) - continue; - for (std::size_t path = 0; path < track->m_paths.size(); ++path) + low = glm::min(low, sample.position); + high = glm::max(high, sample.position); + } + for (auto *track : simulation::Paths.sequence()) + { + if (track == nullptr || track->m_editorremoved || track->eType != tt_Switch || track->m_paths.size() < 2) + continue; + auto const points{track->m_paths.front().points[segment_data::point::start]}; + if (points.x < low.x - 1.0 || points.x > high.x + 1.0 || points.z < low.z - 1.0 || points.z > high.z + 1.0) + continue; + std::size_t at{0}; + auto best{std::numeric_limits::max()}; + for (std::size_t i = 0; i < Samples.size(); ++i) + if (auto const distance{plan_distance(Samples[i].position, points)}; distance < best) { - auto const forward{matches(own, track->m_paths[path])}; - if (false == forward && false == matches(own, turned(track->m_paths[path]))) + best = distance; + at = i; + } + if (best > sample_spacing) + continue; + auto const &near{Samples[at]}; + auto const along{plan(near.tangent)}; + glm::dvec3 const toward{points.x - near.position.x, 0.0, points.z - near.position.z}; + if (std::abs(glm::dot(toward, glm::dvec3{-along.z, 0.0, along.x})) > 0.05) + continue; + auto const s0{near.station + glm::dot(toward, along)}; + for (auto const &path : track->m_paths) + { + auto const heading{glm::dot(plan_heading(path, 0.0), along)}; + if (std::abs(heading) < 0.9) + continue; + auto const beyond{paths_beyond(path, {path})}; + std::vector chain; + double station{0.0}; + if (heading > 0.0) + { + for (auto const &sample : Samples) + if (sample.station >= s0 - outer_reach && sample.station < s0 - 1e-3) + { + station = sample.station; + chain.push_back(sample); + } + sample_piece(path, chain, station); + for (auto const &piece : beyond) + sample_piece(piece, chain, station); + } + else + { + for (auto piece = beyond.rbegin(); piece != beyond.rend(); ++piece) + sample_piece(turned_piece(*piece), chain, station); + sample_piece(turned_piece(path), chain, station); + auto const shift{station - s0}; + for (auto const &sample : Samples) + if (sample.station > s0 + 1e-3 && sample.station <= s0 + outer_reach) + { + chain.push_back(sample); + chain.back().station += shift; + } + } + gauge_along(chain); + for (auto &here : Samples) + { + if (plan_distance(here.position, points) > outer_reach + 2.0 * lead_reach + 100.0) continue; - for (std::size_t other = 0; other < track->m_paths.size(); ++other) + auto const forward{plan(here.tangent)}; + sample const *nearest{nullptr}; + auto closest{std::numeric_limits::max()}; + for (auto const &point : chain) { - if (other == path) - continue; - std::vector chain; - double station{0.0}; - if (forward) + auto const gap{std::abs(glm::dot(glm::dvec3{point.position.x - here.position.x, 0.0, point.position.z - here.position.z}, forward))}; + if (gap < closest) { - for (auto const &sample : m_samples) - if (sample.station >= Spans[piece].first - outer_reach && sample.station < Spans[piece].first) - { - station = sample.station; - chain.push_back(sample); - } - sample_piece(track->m_paths[other], chain, station); - } - else - { - sample_piece(turned(track->m_paths[other]), chain, station); - auto const shift{station - Spans[piece].second}; - for (auto const &sample : m_samples) - if (sample.station > Spans[piece].second && sample.station <= Spans[piece].second + outer_reach) - { - chain.push_back(sample); - chain.back().station += shift; - } - } - gauge_along(chain); - for (auto &here : m_samples) - { - if (here.station < Spans[piece].first - outer_reach || here.station > Spans[piece].second + outer_reach) - continue; - glm::dvec3 const left{-here.tangent.z, 0.0, here.tangent.x}; - sample const *nearest{nullptr}; - auto best{std::numeric_limits::max()}; - for (auto const &point : chain) - { - auto const along{std::abs(glm::dot(glm::dvec3{point.position.x - here.position.x, 0.0, point.position.z - here.position.z}, here.tangent))}; - if (along < best) - { - best = along; - nearest = &point; - } - } - if (nearest == nullptr || best > sample_spacing) - continue; - auto const offset{sign * glm::dot(glm::dvec3{nearest->position.x - here.position.x, 0.0, nearest->position.z - here.position.z}, glm::length(left) > 1e-9 ? glm::normalize(left) : left)}; - if (offset > 0.05) - continue; - auto const needed{offset + total(*nearest)}; - if (needed > total(here)) - { - here.widening[side] = needed; - here.cant[side] = 0.0; - } + closest = gap; + nearest = &point; } } + if (nearest == nullptr || closest > sample_spacing) + continue; + auto const offset{sign * glm::dot(glm::dvec3{nearest->position.x - here.position.x, 0.0, nearest->position.z - here.position.z}, glm::dvec3{-forward.z, 0.0, forward.x})}; + if (offset > 0.05) + continue; + auto const needed{offset + total(*nearest)}; + if (needed > total(here)) + { + here.widening[side] = needed; + here.cant[side] = 0.0; + } } } } @@ -759,7 +772,6 @@ void sweep_node::rebuild_samples() { m_boundsvalid = false; m_samples.clear(); - std::vector> spans; std::vector before, after; if ((m_state.widen || m_state.platform) && false == m_state.pieces.empty()) { @@ -772,24 +784,18 @@ void sweep_node::rebuild_samples() auto const head{station}; auto const first{m_samples.empty() ? std::size_t{0} : m_samples.size() - 1}; for (auto const &piece : m_state.pieces) - { - auto const begin{station}; sample_piece(piece, m_samples, station); - spans.emplace_back(begin - head, station - head); - } auto const last{m_samples.size()}; for (auto const &piece : after) sample_piece(piece, m_samples, station); gauge_along(m_samples); + if (m_state.widen || m_state.platform) + beside_switches(m_samples); // the paths beyond the ends only lead the gauge in, the copies stay on the pieces m_samples.erase(m_samples.begin() + last, m_samples.end()); m_samples.erase(m_samples.begin(), m_samples.begin() + first); for (auto &sample : m_samples) sample.station -= head; - if ((m_state.widen || m_state.platform) && m_samples.size() > 2) - { - beside_switches(spans); - } m_distances.clear(); glm::dvec3 previous{0.0}; for (auto const &sample : m_samples) diff --git a/world/Sweep.h b/world/Sweep.h index 2a8a11f6..c1a59581 100644 --- a/world/Sweep.h +++ b/world/Sweep.h @@ -161,7 +161,7 @@ class sweep_node : public scene::basic_node }; static void sample_piece(segment_data const &Piece, std::vector &Samples, double &Station); static void gauge_along(std::vector &Samples); - void beside_switches(std::vector> const &Spans); + void beside_switches(std::vector &Samples); state m_state; std::vector m_samples; std::vector m_distances;