17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 19:24:54 +01:00
Files
maszyna/application/editormodeselftestsignal.cpp
maj00r 7da0432dca fix(editor): a signal goes to the other side of the track and turns around
Dragging a standing signal over the track takes it to the other side;
the side used to come from where the signal stood, so it never crossed.
The selected signal gets "To the other side" and "Turn around": the
first mirrors it over the axis facing the same trains, the second turns
it to the trains of the other direction and moves its event to the
other event list of the path. Either one swaps a head leaning to the
track for its counterpart, and an include keeps the template its
directive names, so undo brings the old file back too.
2026-10-21 10:44:58 +02:00

207 lines
8.7 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
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"
#include "simulation/simulation.h"
#include "simulation/simulationtime.h"
#include "scene/scenelayers.h"
#include "utilities/Globals.h"
#include "utilities/Logs.h"
#include "world/Track.h"
#include "world/TrackRails.h"
#include "world/MemCell.h"
#include "world/Event.h"
#include "vehicle/DynObj.h"
#include "model/AnimModel.h"
#include "model/Model3d.h"
#include "rendering/renderer.h"
#include "world/Sweep.h"
#include "input/keyboardinput.h"
#include "launcher/textures_scanner.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <algorithm>
#include "editor/editorFormat.hpp"
#include "editor/editorAlignmentStore.hpp"
#include "editor/editorSwitchStore.hpp"
#include "scene/sceneterrain.h"
#include "editor/editorSpeed.hpp"
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <fstream>
#include <map>
#include <sstream>
#include "application/editormodeselftest.h"
editor_mode::selftest_result editor_mode::selftest_signal(std::string const &command, std::istringstream &words)
{
if (command == "signal" || command == "signalmove" || command == "signaldelete" || command == "signalrename" || command == "signalprobe" || command == "signalflip" || command == "signalturn")
{
auto values{selftest_values(words)};
show_track_tab(track_tab::signals);
signal_scan();
signal_refresh(true);
auto &tool{m_signal};
auto const standing_named = [&](std::string const &Name) -> signal_standing const * {
auto const found{std::find_if(tool.standing.begin(), tool.standing.end(), [&](signal_standing const &Standing) { return Standing.name == Name; })};
return found != tool.standing.end() ? &*found : nullptr;
};
auto const spot_on = [&](signal_template const &Template, int const Facing, int const Side, signal_spot &Spot) {
auto *track{simulation::Paths.find(values["track"])};
if (track == nullptr)
return false;
auto const path{values.count("path") ? std::stoi(values["path"]) : 0};
auto const at{values.count("at") ? std::stod(values["at"]) : 0.5};
geometry::bezier const curve{track->m_paths[std::clamp(path, 0, static_cast<int>(track->m_paths.size()) - 1)]};
return signal_spot_at(*track, path, curve.point(at), curve.point(at), Facing, Side, Template, Spot);
};
if (command == "signal")
{
auto const *item{signal_template_of(values["file"])};
if (item == nullptr)
{
WriteLog("SELFTEST signal: no signal template " + values["file"]);
return selftest_result::handled;
}
for (int k = 0; k < static_cast<int>(tool.templates.size()); ++k)
if (&tool.templates[k] == item)
{
static char const *const kinds[] = {"main", "block", "warning", "shunt", "stop", "substitute", "repeater"};
for (int i = 0; i < 7; ++i)
if (item->kind == kinds[i])
{
tool.kind = i;
tool.chosen[i] = k;
}
}
if (values.count("name"))
std::snprintf(tool.name, sizeof(tool.name), "%s", values["name"].c_str());
if (values.count("plate"))
std::snprintf(tool.plate, sizeof(tool.plate), "%s", values["plate"].c_str());
auto const side{values["side"] == "left" ? -1 : 1};
auto const facing{values["facing"] == "along" ? 1 : values["facing"] == "against" ? -1 : 0};
signal_spot spot;
if (false == spot_on(*signal_armed(), facing, side, spot))
{
WriteLog("SELFTEST signal: no such track " + values["track"]);
return selftest_result::handled;
}
auto const placed{signal_place(spot, *signal_armed())};
WriteLog(format("SELFTEST signal %s: %s", placed ? "placed" : "failed", tool.status.c_str()));
}
else if (command == "signalmove")
{
auto const *standing{standing_named(values["name"])};
auto *track{simulation::Paths.find(values["track"])};
if (standing == nullptr || track == nullptr || track->m_paths.empty())
{
WriteLog("SELFTEST signalmove: no signal " + values["name"] + " or no track " + values["track"]);
return selftest_result::handled;
}
auto const path{std::clamp(values.count("path") ? std::stoi(values["path"]) : 0, 0, static_cast<int>(track->m_paths.size()) - 1)};
auto const at{values.count("at") ? std::stod(values["at"]) : 0.5};
geometry::bezier const curve{track->m_paths[path]};
auto point{curve.point(at)};
auto const sided{values.count("side") != 0};
auto side{values["side"] == "left" ? -1 : 1};
if (values.count("off"))
{
auto const tangent{glm::normalize(glm::dvec2{curve.first(at).x, curve.first(at).z})};
auto const off{std::stod(values["off"])};
point += glm::dvec3{-tangent.y * off, 0.0, tangent.x * off};
side = 0;
}
else if (signal_spot home; false == sided && signal_standing_spot(*standing, home))
side = home.side * (home.list == 2 ? 1 : -1);
signal_select(standing->include.instance);
tool.moving = true;
tool.moved_valid = signal_drop_at(*standing, *track, path, point, side, tool.moved);
tool.pressed = {-1000.f, -1000.f};
signal_release();
WriteLog("SELFTEST signalmove: " + tool.status);
}
else if (command == "signalflip" || command == "signalturn")
{
auto const *standing{standing_named(values["name"])};
auto const done{standing != nullptr && (command == "signalflip" ? signal_flip(standing->include.instance) : signal_turn(standing->include.instance))};
WriteLog("SELFTEST " + command + " " + values["name"] + (done ? ": " : ": failed, ") + tool.status);
}
else if (command == "signaldelete")
{
auto const *standing{standing_named(values["name"])};
auto const deleted{standing != nullptr && signal_delete(standing->include.instance)};
WriteLog(std::string{"SELFTEST signaldelete "} + values["name"] + (deleted ? ": removed with its event" : ": not removed by the signal tool"));
}
else if (command == "signalrename")
{
auto const *standing{standing_named(values["name"])};
if (standing == nullptr)
{
WriteLog("SELFTEST signalrename: no signal " + values["name"]);
return selftest_result::handled;
}
select_include(standing->include.instance);
m_include.values[0] = values["to"];
apply_include();
m_include_gesture = false;
WriteLog("SELFTEST signalrename " + values["name"] + " -> " + values["to"]);
}
else
{
signal_refresh(true);
WriteLog(format("SELFTEST signalprobe: %zu signals", tool.standing.size()));
for (auto const &standing : tool.standing)
{
std::string where{"not read"};
if (standing.track != nullptr)
{
auto const &curve{standing.track->m_paths.front()};
geometry::bezier const bez{curve};
auto best{std::numeric_limits<double>::max()};
double parameter{0.0};
for (int k = 0; k <= 1024; ++k)
{
auto const d{geometry::plan_distance(bez.point(k / 1024.0), standing.include.location)};
if (d < best)
{
best = d;
parameter = k / 1024.0;
}
}
auto const axis{bez.point(parameter)};
auto tangent{bez.first(parameter)};
glm::dvec2 travel{tangent.x, tangent.z};
travel = glm::normalize(travel) * (standing.list == 1 ? -1.0 : 1.0);
glm::dvec2 const offset{standing.include.location.x - axis.x, standing.include.location.z - axis.z};
auto const cross{travel.x * offset.y - travel.y * offset.x};
auto heading{glm::degrees(std::atan2(travel.x, travel.y))};
auto turn{std::fmod(standing.include.yaw - heading + 720.0, 360.0)};
auto const &list{editor_track::events(*standing.track, standing.list)};
auto const bound{std::any_of(list.begin(), list.end(), [&](auto const &Event) { return Event.second != nullptr && ToLower(Event.first) == ToLower(standing.read); })};
where = std::string{bound ? "bound, " : "unbound, "} + format("%s of %s, %.3f m from the axis, %s of the trains, %.3f m over the axis, model turned %.1f deg from the travel", editor_track::event_keyword(standing.list), standing.track->name().c_str(), glm::length(offset), cross > 0.0 ? "right" : "left",
standing.include.location.y - axis.y, turn);
}
WriteLog(format("SELFTEST signalprobe %s %s at %.3f %.3f %.3f yaw %.2f read %s: %s", standing.name.c_str(), standing.include.file.c_str(), standing.include.location.x, standing.include.location.y, standing.include.location.z, standing.include.yaw,
standing.read.empty() ? "-" : standing.read.c_str(), where.c_str()));
}
}
return selftest_result::handled;
}
return selftest_result::unhandled;
}