From b103a561e0ead7dc238dad6032bb058decd3e77c Mon Sep 17 00:00:00 2001 From: maj00r Date: Sun, 11 Oct 2026 19:23:59 +0200 Subject: [PATCH] fix(editor): the drive of a switch follows the switch: moved, turned, its grade or cant changed with the gizmo or any other change of the switch, the include gets the new position, direction and rot.x/rot.z of the start of the switch, in the same undo step; self-test drives prints the parameters of the drive --- application/editormode.h | 4 + application/editormodeinfra.cpp | 2 + application/editormodeselftest.cpp | 4 + application/editormodetrack.cpp | 119 ++++++++++++++++++++++++----- 4 files changed, 109 insertions(+), 20 deletions(-) diff --git a/application/editormode.h b/application/editormode.h index 385b10c5..380922c2 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -1407,6 +1407,9 @@ class editor_mode : public application_mode, private editor_track::observer std::vector standing_templates(std::function const &Accept) const; bool find_switch_drive(TTrack const &Switch, standing_template &Drive, int &Family, int &Index); bool replace_switch_drive(TTrack &Switch, int Family, bool Flip); + bool switch_drive_spot(TTrack const &Switch, glm::dvec3 &Location, double &Yaw, glm::dvec2 &Tilt, bool &Rightswitch) const; + void switch_drive_captured(TTrack const &Track); + void switch_drives_committed(std::vector const &Tracks); // places them in the active layer, as one step of the undo. returns: how many were placed std::size_t place_templates(std::vector const &Items, std::string &Error); // moves them, as one step of the undo @@ -1940,6 +1943,7 @@ class editor_mode : public application_mode, private editor_track::observer std::vector> m_bindings_unresolved; // lines kept as they are bool m_bindings_loaded{false}; std::vector m_infra_buffer; // states of the objects before the change in progress + std::vector> m_drive_buffer; bool m_infra_suspended{false}; void infra_load(); void infra_store(); diff --git a/application/editormodeinfra.cpp b/application/editormodeinfra.cpp index e805c545..a1d055f5 100644 --- a/application/editormodeinfra.cpp +++ b/application/editormodeinfra.cpp @@ -1128,6 +1128,7 @@ void editor_mode::infra_buffer(infra::binding const &Binding, bool const Refresh void editor_mode::track_captured(TTrack const &Track) { sweeps_captured(Track); + switch_drive_captured(Track); if (m_infra_suspended || false == m_infra.follow) return; infra_load(); @@ -1166,6 +1167,7 @@ void editor_mode::track_captured(TTrack const &Track) void editor_mode::tracks_committed(std::vector const &Tracks) { sweeps_committed(Tracks); + switch_drives_committed(Tracks); if (m_infra_suspended || false == m_infra.follow) return; infra_load(); diff --git a/application/editormodeselftest.cpp b/application/editormodeselftest.cpp index bb2f2d21..eecd34a8 100644 --- a/application/editormodeselftest.cpp +++ b/application/editormodeselftest.cpp @@ -413,7 +413,11 @@ void editor_mode::selftest_step() std::string files; for (auto const &standing : standing_templates([](std::string const &File) { return File.find("napedy/") != std::string::npos; })) if (false == standing.values.empty() && standing.values[0] == name) + { files += " " + standing.file; + for (std::size_t i = 1; i < standing.values.size(); ++i) + files += (i == 1 ? " (" : " ") + standing.values[i] + (i + 1 == standing.values.size() ? ")" : ""); + } for (scene::instance_handle handle = 1; handle <= scene::Layers.instance_count(); ++handle) if (scene::Layers.tracked(handle) && scene::Layers.instance(handle).file != nullptr && scene::Layers.instance(handle).file->find("napedy") != std::string::npos) files += format(" [%d %s removed %d dead %d]", static_cast(handle), scene::Layers.instance(handle).file->c_str(), scene::Layers.instance(handle).removed ? 1 : 0, scene::Layers.instance(handle).dead ? 1 : 0); diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 3d67dd99..7df8358c 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -1239,6 +1239,8 @@ void editor_mode::push_track_snapshot(std::vector 0.0; + Location = start; + auto const yaw{glm::degrees(std::atan2(heading.x, heading.y))}; + Yaw = yaw < 0.0 ? yaw + 360.0 : yaw; + auto const run{glm::length(plan_of(along))}; + auto const roll{static_cast(main.rolls[0])}; + Tilt = {-glm::degrees(std::atan(along.y / run)), roll}; + if (Global.bRollFix) + Location.y += 0.75 * std::abs(std::sin(glm::radians(roll))); + return true; +} + +void editor_mode::switch_drive_captured(TTrack const &Track) +{ + if (Track.eType != tt_Switch || m_infra_suspended) + return; + standing_template drive; + int family{-1}; + int variant{0}; + if (false == find_switch_drive(Track, drive, family, variant)) + return; + if (std::any_of(m_drive_buffer.begin(), m_drive_buffer.end(), [&](auto const &Entry) { return Entry.first == drive.instance; })) + return; + m_drive_buffer.emplace_back(drive.instance, scene::Layers.directive(drive.instance)); +} + +void editor_mode::switch_drives_committed(std::vector const &Tracks) +{ + if (m_infra_suspended) + return; + for (auto *track : Tracks) + { + if (track == nullptr || track->m_editorremoved || track->eType != tt_Switch) + continue; + standing_template drive; + int family{-1}; + int variant{0}; + glm::dvec3 location; + double yaw; + glm::dvec2 tilt; + bool rightswitch; + if (false == find_switch_drive(*track, drive, family, variant) || false == switch_drive_spot(*track, location, yaw, tilt, rightswitch)) + continue; + auto const &included{scene::Layers.instance(drive.instance)}; + include_info info; + std::string error; + if (included.file == nullptr || false == editor_includes::load(*included.file, info, error)) + continue; + auto const local{location - included.context.offset}; + auto values{drive.values}; + for (auto const ¶meter : info.parameters) + { + if (parameter.id < 1 || parameter.id > static_cast(values.size())) + continue; + auto &value{values[parameter.id - 1]}; + if (parameter.role == "pos.x") + value = editor_includes::number(local.x); + else if (parameter.role == "pos.y") + value = editor_includes::number(local.y); + else if (parameter.role == "pos.z") + value = editor_includes::number(local.z); + else if (parameter.role == "rot.y") + value = editor_includes::number(yaw); + else if (parameter.role == "rot.x") + value = editor_includes::number(tilt.x); + else if (parameter.role == "rot.z") + value = editor_includes::number(tilt.y); + } + auto const directive{editor_includes::compose_directive(*included.file, values)}; + if (directive != scene::Layers.directive(drive.instance)) + set_include_directive(drive.instance, directive); + } +} + bool editor_mode::place_switch_drive(TTrack &Switch) { auto &tool{m_switch}; scan_switch_drives(); if (tool.drive < 0 || tool.drive >= static_cast(tool.drives.size()) || Switch.eType != tt_Switch || Switch.m_paths.size() < 2) return false; - auto const &main{Switch.m_paths[0]}; - auto const &diverging{Switch.m_paths[1]}; - auto const start{main.points[segment_data::point::start]}; - auto const &control{main.points[segment_data::point::control1]}; - auto heading{plan_of(control != glm::dvec3{} ? control : main.points[segment_data::point::end] - start)}; - if (glm::length(heading) < 1e-9) + glm::dvec3 location; + double yaw; + glm::dvec2 tilt; + bool rightswitch; + if (false == switch_drive_spot(Switch, location, yaw, tilt, rightswitch)) return false; - heading = glm::normalize(heading); - glm::dvec2 const right{-heading.y, heading.x}; - bool const rightswitch{glm::dot(plan_of(diverging.points[segment_data::point::end] - start), right) > 0.0}; bool const driveright{(tool.drive_side == 0) == rightswitch}; auto const &family{tool.drives[tool.drive]}; template_item item; @@ -6674,19 +6761,11 @@ bool editor_mode::place_switch_drive(TTrack &Switch) tool.status = format(STR_C("The drive %s has no variant for a %s switch with the drive on the %s"), family.name.c_str(), rightswitch ? STR_C("right") : STR_C("left"), driveright ? STR_C("right") : STR_C("left")); return false; } - item.location = start; - auto yaw{glm::degrees(std::atan2(heading.x, heading.y))}; - item.yaw = yaw < 0.0 ? yaw + 360.0 : yaw; + item.location = location; + item.yaw = yaw; item.described = true; item.track = Switch.name(); - { - auto const along{control != glm::dvec3{} ? control : main.points[segment_data::point::end] - start}; - auto const run{glm::length(plan_of(along))}; - auto const roll{static_cast(main.rolls[0])}; - item.tilt = {run > 1e-9 ? -glm::degrees(std::atan(along.y / run)) : 0.0, roll}; - if (Global.bRollFix) - item.location.y += 0.75 * std::abs(std::sin(glm::radians(roll))); - } + item.tilt = tilt; standing_template standing; int present{-1}; int variant{0};