17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-30 23:40:29 +01:00

fix(editor): a curve with a switch in it read as one curve through the main track of the switch, the switch placed into a curve on the part around the click; the diverging path of a switch placed into a canted curve lies on the conical surface of the cant along its whole length, not only at its ends; platforms by a switch keep the cant of the track for their height, the gauge of the other path only sets them back

This commit is contained in:
maj00r
2026-10-08 01:39:35 +02:00
parent ee3833fdba
commit a5ade3258f
5 changed files with 109 additions and 7 deletions

View File

@@ -926,6 +926,7 @@ class editor_mode : public application_mode, private editor_track::observer
bool switch_reach(glm::dvec3 const &Ground, glm::dvec3 &Point); bool switch_reach(glm::dvec3 const &Ground, glm::dvec3 &Point);
glm::dvec3 cursor_ground() const; glm::dvec3 cursor_ground() const;
glm::dvec3 cursor_level(double const Height) const; glm::dvec3 cursor_level(double const Height) const;
std::optional<glm::dvec3> m_cursor_override;
bool sweep_grabs(glm::dvec3 const &Point); bool sweep_grabs(glm::dvec3 const &Point);
// menu of the path a click of the right mouse button lands on // menu of the path a click of the right mouse button lands on
struct track_context struct track_context

View File

@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h" #include "stdafx.h"
#include "application/editormode.h" #include "application/editormode.h"
#include "editor/editorSettings.hpp" #include "editor/editorSettings.hpp"
#include "editor/editorGeometry.hpp"
#include "application/application.h" #include "application/application.h"
#include "application/editoruilayer.h" #include "application/editoruilayer.h"
#include "application/editorprojection.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); 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); 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<int>(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<double>::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") else if (command == "fouling")
{ {
std::map<std::string, std::string> values; std::map<std::string, std::string> values;

View File

@@ -3815,6 +3815,8 @@ glm::dvec3 editor_mode::cursor_ground() const
glm::dvec3 editor_mode::cursor_level(double const Height) 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()}; glm::dvec3 const ground{cursor_ground()};
screen_projection const projection; screen_projection const projection;
glm::dvec3 level; 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"); tool.status = STR_C("The selected track is neither a recognized straight nor a curve");
return false; 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; tool.status = error.empty() ? std::string{STR_C("The curve can't be followed")} : error;
return false; 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 = sample_chain(chain);
tool.frame_tracks = chain.tracks; tool.frame_tracks = chain.tracks;
if (tool.frame.size() < 2) if (tool.frame.size() < 2)
@@ -5825,6 +5833,20 @@ std::vector<segment_data> editor_mode::curved_switch_paths(editor_track::switch_
auto const p0{map(start, startdirection, localstart)}; auto const p0{map(start, startdirection, localstart)};
auto const p3{map(end, enddirection, localend)}; auto const p3{map(end, enddirection, localend)};
auto path{hermite(p0, startdirection, p3, enddirection)}; 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<double>(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 rollstart{sample_at(Frame, Station + Direction * start.z).roll * Direction};
auto const rollend{sample_at(Frame, Station + Direction * end.z).roll * Direction}; auto const rollend{sample_at(Frame, Station + Direction * end.z).roll * Direction};
path.rolls = {static_cast<float>(rollstart), static_cast<float>(rollend)}; path.rolls = {static_cast<float>(rollstart), static_cast<float>(rollend)};

View File

@@ -1368,12 +1368,32 @@ bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance
return false; return false;
std::deque<std::pair<TTrack *, bool>> run{{&Track, true}}; std::deque<std::pair<TTrack *, bool>> 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}) for (auto const throughend : {true, false})
{ {
TTrack *previous{&Track}; TTrack *previous{&Track};
TTrack *current{throughend ? Track.trNext : Track.trPrev}; 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}; auto const entersatstart{current->trPrev == previous};
if (throughend) if (throughend)
run.emplace_back(current, entersatstart); run.emplace_back(current, entersatstart);
@@ -1433,7 +1453,7 @@ bool editor_track::analyse_curve(std::vector<std::pair<TTrack *, bool>> const &R
for (std::size_t i = 0; i < run.size(); ++i) for (std::size_t i = 0; i < run.size(); ++i)
{ {
auto const &path{run[i].first->m_paths.front()}; 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))}; auto const internal{signed_angle(incoming(i), outgoing(i))};
turns.push_back(internal + kinks[i] * 0.5 + kinks[i + 1] * 0.5); turns.push_back(internal + kinks[i] * 0.5 + kinks[i + 1] * 0.5);
lengths.push_back(length); lengths.push_back(length);

View File

@@ -657,10 +657,7 @@ void sweep_node::beside_switches(std::vector<sample> &Samples)
continue; continue;
auto const needed{offset + total(*nearest)}; auto const needed{offset + total(*nearest)};
if (needed > total(here)) if (needed > total(here))
{ here.widening[side] = needed - std::max(0.0, here.cant[side]) * edgeheight / 1.5;
here.widening[side] = needed;
here.cant[side] = 0.0;
}
} }
} }
} }