diff --git a/application/editormode.h b/application/editormode.h index ee0fade6..7bc24ab0 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -926,6 +926,7 @@ class editor_mode : public application_mode, private editor_track::observer bool switch_reach(glm::dvec3 const &Ground, glm::dvec3 &Point); glm::dvec3 cursor_ground() const; glm::dvec3 cursor_level(double const Height) const; + std::optional m_cursor_override; bool sweep_grabs(glm::dvec3 const &Point); // menu of the path a click of the right mouse button lands on struct track_context diff --git a/application/editormodeselftest.cpp b/application/editormodeselftest.cpp index fa2f6b73..c25d6726 100644 --- a/application/editormodeselftest.cpp +++ b/application/editormodeselftest.cpp @@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/. #include "stdafx.h" #include "application/editormode.h" #include "editor/editorSettings.hpp" +#include "editor/editorGeometry.hpp" #include "application/application.h" #include "application/editoruilayer.h" #include "application/editorprojection.h" @@ -273,6 +274,67 @@ void editor_mode::selftest_step() text += format(" [%.1f %.2f..%.2f]", index * 0.1, column.first, column.second); WriteLog(format("SELFTEST profile %s at %.0f m:", name.c_str(), station) + text); } + else if (command == "curvedswitch") + { + std::string name, label; + glm::dvec3 at{0.0}, toward{0.0}; + words >> name >> at.x >> at.z >> toward.x >> toward.z >> label; + m_node = simulation::Paths.find(name); + ui()->set_node(m_node); + show_track_tab(track_tab::turnout); + arm_switch(); + auto &tool{m_switch}; + for (int i = 0; i < static_cast(tool.templates.size()); ++i) + if (false == label.empty() && tool.templates[i].label.find(label) != std::string::npos) + { + tool.armed = i; + break; + } + m_cursor_override = at; + auto const started{start_switch_placement()}; + m_cursor_override.reset(); + if (false == started) + { + WriteLog("SELFTEST curvedswitch: not started, " + tool.status); + continue; + } + tool.mouse = glm::dvec3{toward.x, tool.point.y, toward.z}; + finish_switch_placement(); + WriteLog("SELFTEST curvedswitch " + (tool.armed >= 0 ? tool.templates[tool.armed].label : std::string{}) + ": " + tool.status); + } + else if (command == "conecheck") + { + std::string name; + words >> name; + auto *track{simulation::Paths.find(name)}; + if (track == nullptr || track->m_paths.size() < 2) + { + WriteLog("SELFTEST conecheck: no switch " + name); + continue; + } + geometry::bezier const main{track->m_paths[0]}; + geometry::bezier const other{track->m_paths[1]}; + std::string text; + for (double t = 0.125; t < 1.01; t += 0.125) + { + auto const point{other.point(t)}; + double best{std::numeric_limits::max()}, along{0.0}; + for (int k = 0; k <= 2000; ++k) + if (auto const distance{geometry::plan_distance(main.point(k / 2000.0), point)}; distance < best) + { + best = distance; + along = k / 2000.0; + } + auto const base{main.point(along)}; + auto const direction{glm::normalize(geometry::plan_of(main.first(along)))}; + auto const lateral{(point.x - base.x) * -direction.y + (point.z - base.z) * direction.x}; + auto const &path{track->m_paths[0]}; + auto const roll{glm::radians(path.rolls[0] + (path.rolls[1] - path.rolls[0]) * along)}; + auto const cone{base.y - lateral * std::tan(roll)}; + text += format(" [t %.3f off %.3f dy %.4f]", t, lateral, point.y - cone); + } + WriteLog("SELFTEST conecheck " + name + ":" + text); + } else if (command == "fouling") { std::map values; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index fe4db564..d0fb0593 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -3815,6 +3815,8 @@ glm::dvec3 editor_mode::cursor_ground() const glm::dvec3 editor_mode::cursor_level(double const Height) const { + if (m_cursor_override.has_value()) + return glm::dvec3{m_cursor_override->x, Height, m_cursor_override->z}; glm::dvec3 const ground{cursor_ground()}; screen_projection const projection; glm::dvec3 level; @@ -4565,11 +4567,17 @@ bool editor_mode::start_switch_placement() tool.status = STR_C("The selected track is neither a recognized straight nor a curve"); return false; } - if (false == editor_track::find_chain(curve.from, curve.to, chain, error)) + if (false == editor_track::find_chain(curve.from, curve.to, chain, error, true)) { tool.status = error.empty() ? std::string{STR_C("The curve can't be followed")} : error; return false; } + for (auto const &part : editor_track::chain_parts(chain)) + if (std::find(part.tracks.begin(), part.tracks.end(), track) != part.tracks.end()) + { + chain = part; + break; + } tool.frame = sample_chain(chain); tool.frame_tracks = chain.tracks; if (tool.frame.size() < 2) @@ -5825,6 +5833,20 @@ std::vector editor_mode::curved_switch_paths(editor_track::switch_ auto const p0{map(start, startdirection, localstart)}; auto const p3{map(end, enddirection, localend)}; auto path{hermite(p0, startdirection, p3, enddirection)}; + { + auto const cone = [&](double const T) { + auto const point{bezier{path}.point(T)}; + auto const sample{sample_at(Frame, nearest_station(Frame, point))}; + auto const lateral{glm::dot(plan_of(point) - plan_of(sample.position), glm::dvec2{-sample.tangent.y, sample.tangent.x})}; + return sample.position.y - lateral * std::tan(glm::radians(static_cast(sample.roll))); + }; + auto const h1{cone(1.0 / 3.0)}; + auto const h2{cone(2.0 / 3.0)}; + auto const y1{3.0 * h1 - 1.5 * h2 - 5.0 / 6.0 * p0.y + p3.y / 3.0}; + auto const y2{3.0 * h2 - 1.5 * h1 + p0.y / 3.0 - 5.0 / 6.0 * p3.y}; + path.points[segment_data::point::control1].y = y1 - p0.y; + path.points[segment_data::point::control2].y = y2 - p3.y; + } auto const rollstart{sample_at(Frame, Station + Direction * start.z).roll * Direction}; auto const rollend{sample_at(Frame, Station + Direction * end.z).roll * Direction}; path.rolls = {static_cast(rollstart), static_cast(rollend)}; diff --git a/editor/editorTrack.cpp b/editor/editorTrack.cpp index 11a6a1cf..f661e518 100644 --- a/editor/editorTrack.cpp +++ b/editor/editorTrack.cpp @@ -1368,12 +1368,32 @@ bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance return false; std::deque> run{{&Track, true}}; + auto const known = [&](TTrack const *Other) { return std::any_of(run.begin(), run.end(), [&](auto const &Member) { return Member.first == Other; }); }; for (auto const throughend : {true, false}) { TTrack *previous{&Track}; TTrack *current{throughend ? Track.trNext : Track.trPrev}; - while (curved(current) && std::none_of(run.begin(), run.end(), [&](auto const &Member) { return Member.first == current; })) + while (current != nullptr && false == known(current)) { + if (current->eType == tt_Switch && has_switch_paths(*current) && is_supported(*current) && false == current->m_editorremoved) + { + auto const &extension{*current->SwitchExtension}; + bool const atstart{extension.pPrevs[0] == previous}; + if (atstart == (extension.pNexts[0] == previous)) + break; + auto *beyond{atstart ? extension.pNexts[0] : extension.pPrevs[0]}; + if (false == curved(beyond) || known(beyond)) + break; + if (throughend) + run.emplace_back(current, atstart); + else + run.emplace_front(current, false == atstart); + previous = current; + current = beyond; + continue; + } + if (false == curved(current)) + break; auto const entersatstart{current->trPrev == previous}; if (throughend) run.emplace_back(current, entersatstart); @@ -1433,7 +1453,7 @@ bool editor_track::analyse_curve(std::vector> const &R for (std::size_t i = 0; i < run.size(); ++i) { auto const &path{run[i].first->m_paths.front()}; - auto const length{run[i].first->Length()}; + auto const length{run[i].first->eType == tt_Switch ? bezier{path}.plan_length() : run[i].first->Length()}; auto const internal{signed_angle(incoming(i), outgoing(i))}; turns.push_back(internal + kinks[i] * 0.5 + kinks[i + 1] * 0.5); lengths.push_back(length); diff --git a/world/Sweep.cpp b/world/Sweep.cpp index 8e4f76cb..42dd07c0 100644 --- a/world/Sweep.cpp +++ b/world/Sweep.cpp @@ -657,10 +657,7 @@ void sweep_node::beside_switches(std::vector &Samples) continue; auto const needed{offset + total(*nearest)}; if (needed > total(here)) - { - here.widening[side] = needed; - here.cant[side] = 0.0; - } + here.widening[side] = needed - std::max(0.0, here.cant[side]) * edgeheight / 1.5; } } }