mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 06:19:19 +02:00
feat(editor): self-test command guards logs each guard rail and what each of its ends and breaks is cut along: the rail of the other track, how far off its gauge line and how skewed; a fault when a cut is not along a rail, or a glued end of the frame of a crossing at a middle angle is not on the face of a guard rail of the other track
This commit is contained in:
@@ -21,6 +21,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#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"
|
||||
@@ -45,6 +46,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
// camera <x> <y> <z> <x> <y> <z> from the first point, looking at the second
|
||||
// select <track name>
|
||||
// frog [name] logs where the inner rails of each switch cross: distance on both paths, 1:n, throat, nose, check rails
|
||||
// guards [name] logs each guard rail and what each of its ends and breaks is cut along; a fault when a cut is not along a rail or a glued end not on the guard rail of the other track
|
||||
// crossings [name] logs where rails of tracks cross rails of other tracks, checks that each acute crossing has one partner and one nose
|
||||
// frogkey name [on|off] sets or clears the frogs key of a switch (one undo step); logs the key, whether it is written, the frog and the crossings
|
||||
// geomhash [name] logs the number of vertices and a hash of the drawn geometry of each rail track (rails, trackbed, cut faces), to compare builds
|
||||
@@ -239,6 +241,109 @@ void editor_mode::selftest_step()
|
||||
track->frog().valid ? 1 : 0, track->rail_crossings().size(), track->m_profile1.first.c_str()));
|
||||
}
|
||||
}
|
||||
else if (command == "guards")
|
||||
{
|
||||
std::string name;
|
||||
words >> name;
|
||||
int count{0};
|
||||
int faults{0};
|
||||
for (auto *track : simulation::Paths.sequence())
|
||||
{
|
||||
if (track == nullptr || track->m_editorremoved || (track->iCategoryFlag & 15) != 1)
|
||||
continue;
|
||||
if (false == name.empty() && track->name() != name)
|
||||
continue;
|
||||
auto const layout{track->plan_rails(true)};
|
||||
auto const size{track->rail_dimensions()};
|
||||
auto const guardoffset{-(track_rails::flangeway + size.head)};
|
||||
double const laterals[2]{size.low + guardoffset, track_rails::flangeway + size.head + size.low - 0.002 + guardoffset};
|
||||
for (int path = 0; path < 2; ++path)
|
||||
{
|
||||
for (int right = 0; right < 2; ++right)
|
||||
{
|
||||
auto const side{right ? 1 : -1};
|
||||
auto const length{track->eType == tt_Switch ? track->SwitchExtension->Segments[path]->GetLength() : track->Segment->GetLength()};
|
||||
auto const drawn = [&](glm::dvec2 const &Miter, double const Square) {
|
||||
auto const at{Miter.y == 0.0 ? Square : Miter.x + Miter.y * 0.5 * (laterals[0] + laterals[1])};
|
||||
return at > 0.0 && at < length;
|
||||
};
|
||||
for (auto const &guard : layout.guards[path][right])
|
||||
{
|
||||
if (false == drawn(guard.frommiter, guard.from) && false == drawn(guard.tomiter, guard.to) && (guard.to <= 0.0 || guard.from >= length))
|
||||
continue;
|
||||
++count;
|
||||
std::string text;
|
||||
auto const end = [&](glm::dvec2 const &Miter, bool const Flared, bool const Glued) {
|
||||
if (Miter.y == 0.0)
|
||||
return std::string{Flared ? "flared" : "square"};
|
||||
if (false == drawn(Miter, 0.0))
|
||||
return std::string{"past the end of the track"};
|
||||
glm::dvec3 points[2];
|
||||
for (int i = 0; i < 2; ++i)
|
||||
points[i] = track_rails::rail_point(*track, path, side, Miter.x + Miter.y * laterals[i], laterals[i]);
|
||||
auto best{1e9};
|
||||
double lateral{0.0}, along{0.0};
|
||||
TTrack *against{nullptr};
|
||||
int againstpath{0}, againstside{0};
|
||||
for (auto *other : layout.crossed)
|
||||
for (int otherpath = 0; otherpath < (other->eType == tt_Switch ? 2 : 1); ++otherpath)
|
||||
for (auto const otherside : {-1, 1})
|
||||
{
|
||||
double d[2], l[2];
|
||||
for (int i = 0; i < 2; ++i)
|
||||
track_rails::rail_offset(*other, otherpath, otherside, points[i], d[i], l[i]);
|
||||
if (std::abs(l[0]) > 0.5 || std::abs(l[1]) > 0.5 || std::abs(l[0] - l[1]) >= best)
|
||||
continue;
|
||||
best = std::abs(l[0] - l[1]);
|
||||
lateral = 0.5 * (l[0] + l[1]);
|
||||
along = 0.5 * (d[0] + d[1]);
|
||||
against = other;
|
||||
againstpath = otherpath;
|
||||
againstside = otherside;
|
||||
}
|
||||
if (against == nullptr)
|
||||
{
|
||||
++faults;
|
||||
return std::string{"cut along nothing FAULT"};
|
||||
}
|
||||
auto result{format("along %s/%d%+d at %.3f m, %.1f mm off its gauge line, skew %.1f mm", against->name().c_str(), againstpath, againstside, along, lateral * 1000.0, best * 1000.0)};
|
||||
auto fault{best > 0.002};
|
||||
if (Glued)
|
||||
{
|
||||
auto const theirsize{against->rail_dimensions()};
|
||||
auto const theiroffset{-(track_rails::flangeway + theirsize.head)};
|
||||
double const theirlaterals[2]{theirsize.low + theiroffset, track_rails::flangeway + theirsize.head + theirsize.low - 0.002 + theiroffset};
|
||||
auto const reach = [&](glm::dvec2 const &Miter, double const Square, bool const Latest) {
|
||||
if (Miter.y == 0.0)
|
||||
return Square;
|
||||
auto const a{Miter.x + Miter.y * theirlaterals[0]};
|
||||
auto const b{Miter.x + Miter.y * theirlaterals[1]};
|
||||
return Latest ? std::max(a, b) : std::min(a, b);
|
||||
};
|
||||
auto covered{false};
|
||||
for (auto const &theirs : against->plan_rails(true).guards[againstpath][againstside > 0])
|
||||
covered |= (along > reach(theirs.frommiter, theirs.from, false) - 0.002 && along < reach(theirs.tomiter, theirs.to, true) + 0.002);
|
||||
result += covered ? ", on its guard rail" : ", no guard rail there";
|
||||
fault |= (false == covered || std::abs(lateral + track_rails::flangeway) > 0.002);
|
||||
}
|
||||
if (fault)
|
||||
{
|
||||
++faults;
|
||||
result += " FAULT";
|
||||
}
|
||||
return result;
|
||||
};
|
||||
text += "from " + end(guard.frommiter, guard.flarefrom, false == guard.flarefrom) + "; to " + end(guard.tomiter, guard.flareto, false == guard.flareto);
|
||||
for (auto const &cut : guard.plan.cuts)
|
||||
if (cut.end > 0.0 && cut.start < length)
|
||||
text += format("; break %.3f-%.3f: ", cut.end, cut.start) + end(cut.endmiter, false, false) + " / " + end(cut.startmiter, false, false);
|
||||
WriteLog(format("SELFTEST guard %s path %d side %+d %.3f-%.3f m: %s", track->name().c_str(), path, side, guard.from, guard.to, text.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WriteLog(format("SELFTEST guards: %d guard rails, %d faults", count, faults));
|
||||
}
|
||||
else if (command == "crossings")
|
||||
{
|
||||
std::string name;
|
||||
|
||||
@@ -289,6 +289,36 @@ void cut_faces( gfx::vertex_array const &Strip, std::size_t const Ring, bool con
|
||||
}
|
||||
}
|
||||
|
||||
glm::dvec3 rail_point( TTrack const &Track, int const Path, int const Side, double const Distance, double const Lateral ) {
|
||||
|
||||
auto const line { make_gauge_line( path_segment( Track, Path ), Side * 0.5 * std::abs( Track.fTrackWidth ), 0.0 ) };
|
||||
glm::dvec3 point, tangent;
|
||||
line.at( Distance, point, tangent );
|
||||
return point - line.normal( Distance, Side ) * Lateral;
|
||||
}
|
||||
|
||||
void rail_offset( TTrack const &Track, int const Path, int const Side, glm::dvec3 const &Point, double &Distance, double &Lateral ) {
|
||||
|
||||
auto const line { make_gauge_line( path_segment( Track, Path ), Side * 0.5 * std::abs( Track.fTrackWidth ), 0.0 ) };
|
||||
auto const flat = []( glm::dvec3 const &V ) { return glm::dvec2{ V.x, V.z }; };
|
||||
Distance = line.distances.front();
|
||||
auto nearest { std::numeric_limits<double>::max() };
|
||||
for( std::size_t i = 0; i < line.points.size(); ++i ) {
|
||||
auto const gap { glm::length( flat( line.points[ i ] - Point ) ) };
|
||||
if( gap < nearest ) {
|
||||
nearest = gap;
|
||||
Distance = line.distances[ i ];
|
||||
}
|
||||
}
|
||||
glm::dvec3 point, tangent;
|
||||
for( int step = 0; step < 8; ++step ) {
|
||||
line.at( Distance, point, tangent );
|
||||
Distance += glm::dot( flat( Point - point ), glm::normalize( flat( tangent ) ) );
|
||||
}
|
||||
line.at( Distance, point, tangent );
|
||||
Lateral = -glm::dot( flat( Point - point ), flat( line.normal( Distance, Side ) ) );
|
||||
}
|
||||
|
||||
} // namespace track_rails
|
||||
|
||||
using namespace track_rails;
|
||||
|
||||
@@ -34,4 +34,7 @@ gfx::userdata_array worn_userdata( std::size_t const Count );
|
||||
// the ends of rails left open: at track ends with nothing joined, blade tips, the gap of a frog
|
||||
void cut_faces( gfx::vertex_array const &Strip, std::size_t const Ring, bool const Start, bool const End, gfx::geometrybank_handle const &Bank, TTrack::geometryhandle_sequence &Geometry );
|
||||
|
||||
glm::dvec3 rail_point( TTrack const &Track, int const Path, int const Side, double const Distance, double const Lateral );
|
||||
void rail_offset( TTrack const &Track, int const Path, int const Side, glm::dvec3 const &Point, double &Distance, double &Lateral );
|
||||
|
||||
} // namespace track_rails
|
||||
|
||||
Reference in New Issue
Block a user