mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-02 20:56:27 +01:00
Structure gauge: G1/G2/GA/GB/GC installation gauges, road clearance, Toolset button
This commit is contained in:
@@ -321,7 +321,8 @@ class editor_mode : public application_mode, private editor_track::observer
|
|||||||
bool open{false}; // window
|
bool open{false}; // window
|
||||||
bool enabled{true}; // tunnel along the selected path
|
bool enabled{true}; // tunnel along the selected path
|
||||||
std::vector<gauge::profile> profiles;
|
std::vector<gauge::profile> profiles;
|
||||||
int profile{0};
|
int profile{0}; // railway
|
||||||
|
int road{-1};
|
||||||
TTrack const *track{nullptr}; // scanned
|
TTrack const *track{nullptr}; // scanned
|
||||||
std::size_t history{0};
|
std::size_t history{0};
|
||||||
bool pending{false}; // rescan once the edit ends
|
bool pending{false}; // rescan once the edit ends
|
||||||
@@ -349,6 +350,8 @@ class editor_mode : public application_mode, private editor_track::observer
|
|||||||
bool published{false}; // overlay up to date
|
bool published{false}; // overlay up to date
|
||||||
} m_gauge;
|
} m_gauge;
|
||||||
void gauge_load();
|
void gauge_load();
|
||||||
|
// the chosen railway and road outlines, null when there's none of the kind
|
||||||
|
std::pair<gauge::profile const *, gauge::profile const *> gauge_profiles() const;
|
||||||
std::vector<gauge_hit> const &gauge_hits() const;
|
std::vector<gauge_hit> const &gauge_hits() const;
|
||||||
void update_gauge();
|
void update_gauge();
|
||||||
void scan_gauge(TTrack &Track);
|
void scan_gauge(TTrack &Track);
|
||||||
@@ -358,7 +361,8 @@ class editor_mode : public application_mode, private editor_track::observer
|
|||||||
void gauge_spot(gauge_hit const &Hit);
|
void gauge_spot(gauge_hit const &Hit);
|
||||||
void render_gauge_window();
|
void render_gauge_window();
|
||||||
void render_gauge_hits();
|
void render_gauge_hits();
|
||||||
bool render_gauge_outline();
|
bool render_gauge_choice(char const *Label, int &Index, bool Road);
|
||||||
|
bool render_gauge_outline(int Index);
|
||||||
struct route_design
|
struct route_design
|
||||||
{
|
{
|
||||||
TTrack *from{nullptr};
|
TTrack *from{nullptr};
|
||||||
|
|||||||
@@ -45,37 +45,71 @@ double const gauge_spot_reach{60.0};
|
|||||||
// sampled line of the tracks with the widening of the gauge along it
|
// sampled line of the tracks with the widening of the gauge along it
|
||||||
struct gauge_line
|
struct gauge_line
|
||||||
{
|
{
|
||||||
|
gauge::profile const *profile{nullptr};
|
||||||
editor_track::route route;
|
editor_track::route route;
|
||||||
std::vector<editor_track::route_sample> samples;
|
std::vector<editor_track::route_sample> samples;
|
||||||
std::vector<gauge::section> sections;
|
std::vector<gauge::section> sections;
|
||||||
};
|
};
|
||||||
|
|
||||||
gauge_line make_line(editor_track::route &Route)
|
// the outline checked along the track: the railway one or the road one
|
||||||
|
struct gauge_choice
|
||||||
|
{
|
||||||
|
gauge::profile const *railway{nullptr};
|
||||||
|
gauge::profile const *road{nullptr};
|
||||||
|
|
||||||
|
gauge::profile const *of(TTrack const &Track) const
|
||||||
|
{
|
||||||
|
if ((Track.iCategoryFlag & 1) != 0)
|
||||||
|
return railway;
|
||||||
|
if ((Track.iCategoryFlag & 2) != 0)
|
||||||
|
return road;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// half of the carriageway of a road at the sample, it narrows linearly to the width at the end
|
||||||
|
double carriageway(editor_track::route_span const &Span, double const Chainage)
|
||||||
|
{
|
||||||
|
auto const &track{*Span.track};
|
||||||
|
auto const end{track.fTrackWidth2 > 0.f ? track.fTrackWidth2 : track.fTrackWidth};
|
||||||
|
auto t{Span.to > Span.from ? std::clamp((Chainage - Span.from) / (Span.to - Span.from), 0.0, 1.0) : 0.0};
|
||||||
|
if (false == Span.forward)
|
||||||
|
t = 1.0 - t;
|
||||||
|
return 0.5 * std::abs(track.fTrackWidth + (end - track.fTrackWidth) * t);
|
||||||
|
}
|
||||||
|
|
||||||
|
gauge_line make_line(editor_track::route &Route, gauge::profile const &Profile)
|
||||||
{
|
{
|
||||||
gauge_line line;
|
gauge_line line;
|
||||||
|
line.profile = &Profile;
|
||||||
line.samples = editor_track::sample_route(Route, 1.0);
|
line.samples = editor_track::sample_route(Route, 1.0);
|
||||||
line.route = Route;
|
line.route = Route;
|
||||||
|
bool const road{Profile.kind == gauge::kind::road};
|
||||||
std::vector<double> chainage, curvature, cant;
|
std::vector<double> chainage, curvature, cant;
|
||||||
for (auto &sample : line.samples)
|
for (auto &sample : line.samples)
|
||||||
{
|
{
|
||||||
sample.position.y += gauge::rail_height;
|
if (false == road)
|
||||||
|
sample.position.y += gauge::rail_height;
|
||||||
chainage.push_back(sample.chainage);
|
chainage.push_back(sample.chainage);
|
||||||
curvature.push_back(sample.curvature);
|
curvature.push_back(sample.curvature);
|
||||||
cant.push_back(sample.cant);
|
cant.push_back(sample.cant);
|
||||||
}
|
}
|
||||||
line.sections = gauge::sections(chainage, curvature, cant);
|
line.sections = gauge::sections(Profile.kind, chainage, curvature, cant);
|
||||||
|
if (road)
|
||||||
|
for (std::size_t i = 0; i < line.samples.size(); ++i)
|
||||||
|
line.sections[i].base = carriageway(line.route.spans[line.samples[i].span], line.samples[i].chainage);
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
// lines through all paths of the scenery, each path in one of them at least
|
// lines through all paths of the scenery, each path in one of them at least
|
||||||
std::vector<gauge_line> scenery_lines()
|
std::vector<gauge_line> scenery_lines(gauge_choice const &Choice)
|
||||||
{
|
{
|
||||||
std::vector<gauge_line> lines;
|
std::vector<gauge_line> lines;
|
||||||
std::unordered_set<TTrack const *> visited;
|
std::unordered_set<TTrack const *> visited;
|
||||||
auto const seen{[&](editor_track::route_span const &Span) { return visited.count(Span.track) > 0; }};
|
auto const seen{[&](editor_track::route_span const &Span) { return visited.count(Span.track) > 0; }};
|
||||||
for (auto *track : simulation::Paths.sequence())
|
for (auto *track : simulation::Paths.sequence())
|
||||||
{
|
{
|
||||||
if (track == nullptr || visited.count(track) > 0 || false == editor_track::is_supported(*track))
|
if (track == nullptr || visited.count(track) > 0 || false == editor_track::is_supported(*track) || Choice.of(*track) == nullptr)
|
||||||
continue;
|
continue;
|
||||||
auto route{editor_track::run_route(*track, 1e7)};
|
auto route{editor_track::run_route(*track, 1e7)};
|
||||||
// the line may run on over the paths already covered, one of them is enough for the widening ahead of a curve
|
// the line may run on over the paths already covered, one of them is enough for the widening ahead of a curve
|
||||||
@@ -86,15 +120,15 @@ std::vector<gauge_line> scenery_lines()
|
|||||||
spans.pop_back();
|
spans.pop_back();
|
||||||
for (auto const &span : spans)
|
for (auto const &span : spans)
|
||||||
visited.insert(span.track);
|
visited.insert(span.track);
|
||||||
lines.push_back(make_line(route));
|
lines.push_back(make_line(route, *Choice.of(*track)));
|
||||||
}
|
}
|
||||||
// diverging paths of the switches
|
// diverging paths of the switches
|
||||||
for (auto *track : simulation::Paths.sequence())
|
for (auto *track : simulation::Paths.sequence())
|
||||||
if (track != nullptr && track->eType == tt_Switch && editor_track::is_supported(*track))
|
if (track != nullptr && track->eType == tt_Switch && editor_track::is_supported(*track) && Choice.of(*track) != nullptr)
|
||||||
{
|
{
|
||||||
editor_track::route route;
|
editor_track::route route;
|
||||||
route.spans.push_back({track, 1, true});
|
route.spans.push_back({track, 1, true});
|
||||||
lines.push_back(make_line(route));
|
lines.push_back(make_line(route, *Choice.of(*track)));
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
@@ -103,11 +137,12 @@ std::vector<gauge_line> scenery_lines()
|
|||||||
class corridor
|
class corridor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
corridor(std::vector<gauge_line> const &Lines, double const Margin) : m_lines{Lines}, m_margin{Margin}
|
explicit corridor(std::vector<gauge_line> const &Lines) : m_lines{Lines}
|
||||||
{
|
{
|
||||||
for (std::uint32_t line = 0; line < Lines.size(); ++line)
|
for (std::uint32_t line = 0; line < Lines.size(); ++line)
|
||||||
for (std::uint32_t i = 0; i < Lines[line].samples.size(); ++i)
|
for (std::uint32_t i = 0; i < Lines[line].samples.size(); ++i)
|
||||||
{
|
{
|
||||||
|
m_margin = std::max(m_margin, reach(*Lines[line].profile) + Lines[line].sections[i].base);
|
||||||
auto const point{plan_of(Lines[line].samples[i].position)};
|
auto const point{plan_of(Lines[line].samples[i].position)};
|
||||||
m_cells[key(point, cell)].push_back({line, i});
|
m_cells[key(point, cell)].push_back({line, i});
|
||||||
m_regions.insert(key(point, region));
|
m_regions.insert(key(point, region));
|
||||||
@@ -139,7 +174,7 @@ public:
|
|||||||
editor_track::route_span const *span{nullptr}; // of the line the point enters
|
editor_track::route_span const *span{nullptr}; // of the line the point enters
|
||||||
};
|
};
|
||||||
// deepest intrusion of the point into the gauge of the lines nearby
|
// deepest intrusion of the point into the gauge of the lines nearby
|
||||||
hit intrusion(gauge::profile const &Profile, glm::dvec3 const &Point) const
|
hit intrusion(glm::dvec3 const &Point) const
|
||||||
{
|
{
|
||||||
auto const point{plan_of(Point)};
|
auto const point{plan_of(Point)};
|
||||||
auto const center{index(point, cell)};
|
auto const center{index(point, cell)};
|
||||||
@@ -173,7 +208,7 @@ public:
|
|||||||
if ((found.index == 0 && along < -0.6) || (found.index + 1 == line.samples.size() && along > 0.6))
|
if ((found.index == 0 && along < -0.6) || (found.index + 1 == line.samples.size() && along > 0.6))
|
||||||
continue;
|
continue;
|
||||||
auto const height{Point.y - (sample.position.y + sample.grade * along)};
|
auto const height{Point.y - (sample.position.y + sample.grade * along)};
|
||||||
auto const depth{gauge::intrusion(Profile, line.sections[found.index], glm::dot(offset, normal(sample)), height)};
|
auto const depth{gauge::intrusion(*line.profile, line.sections[found.index], glm::dot(offset, normal(sample)), height)};
|
||||||
if (depth > deepest.depth)
|
if (depth > deepest.depth)
|
||||||
deepest = {depth, &line.route.spans[sample.span]};
|
deepest = {depth, &line.route.spans[sample.span]};
|
||||||
}
|
}
|
||||||
@@ -208,8 +243,17 @@ private:
|
|||||||
return m_lines[Entry.line].samples[Entry.index];
|
return m_lines[Entry.line].samples[Entry.index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// half-width of the outline with the room for the widening in the sharpest curves on the largest cant
|
||||||
|
static double reach(gauge::profile const &Profile)
|
||||||
|
{
|
||||||
|
double result{0.0};
|
||||||
|
for (auto const &point : Profile.outline)
|
||||||
|
result = std::max(result, point.half_width);
|
||||||
|
return result + 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<gauge_line> const &m_lines;
|
std::vector<gauge_line> const &m_lines;
|
||||||
double m_margin;
|
double m_margin{0.0};
|
||||||
std::unordered_map<std::uint64_t, std::vector<entry>> m_cells;
|
std::unordered_map<std::uint64_t, std::vector<entry>> m_cells;
|
||||||
std::unordered_set<std::uint64_t> m_regions;
|
std::unordered_set<std::uint64_t> m_regions;
|
||||||
};
|
};
|
||||||
@@ -253,7 +297,7 @@ template <typename Visitor> void visit_triangles(TSubModel *Submodel, glm::dmat4
|
|||||||
}
|
}
|
||||||
|
|
||||||
// triangles of the models entering the gauge, and the deepest place of each model, the deepest models first
|
// triangles of the models entering the gauge, and the deepest place of each model, the deepest models first
|
||||||
void scan_models(corridor const &Space, gauge::profile const &Profile, std::vector<glm::dvec3> &Triangles, std::vector<editor_mode::gauge_hit> &Hits)
|
void scan_models(corridor const &Space, std::vector<glm::dvec3> &Triangles, std::vector<editor_mode::gauge_hit> &Hits)
|
||||||
{
|
{
|
||||||
for (auto *instance : simulation::Instances.sequence())
|
for (auto *instance : simulation::Instances.sequence())
|
||||||
{
|
{
|
||||||
@@ -272,7 +316,7 @@ void scan_models(corridor const &Space, gauge::profile const &Profile, std::vect
|
|||||||
for (int v = 0; u + v <= steps; ++v)
|
for (int v = 0; u + v <= steps; ++v)
|
||||||
{
|
{
|
||||||
auto const point{A + (B - A) * (static_cast<double>(u) / steps) + (C - A) * (static_cast<double>(v) / steps)};
|
auto const point{A + (B - A) * (static_cast<double>(u) / steps) + (C - A) * (static_cast<double>(v) / steps)};
|
||||||
auto const found{Space.intrusion(Profile, point)};
|
auto const found{Space.intrusion(point)};
|
||||||
if (found.depth <= 0.0)
|
if (found.depth <= 0.0)
|
||||||
continue;
|
continue;
|
||||||
Triangles.insert(Triangles.end(), {A, B, C});
|
Triangles.insert(Triangles.end(), {A, B, C});
|
||||||
@@ -293,8 +337,9 @@ void scan_models(corridor const &Space, gauge::profile const &Profile, std::vect
|
|||||||
}
|
}
|
||||||
|
|
||||||
// translucent tunnel of the gauge along the line, with the rings every 10 m and the lines along its corners
|
// translucent tunnel of the gauge along the line, with the rings every 10 m and the lines along its corners
|
||||||
void build_tunnel(gauge_line const &Line, gauge::profile const &Profile, std::vector<glm::dvec3> &Surface, std::vector<glm::dvec3> &Edges)
|
void build_tunnel(gauge_line const &Line, std::vector<glm::dvec3> &Surface, std::vector<glm::dvec3> &Edges)
|
||||||
{
|
{
|
||||||
|
auto const &profile{*Line.profile};
|
||||||
auto const lift{[](editor_track::route_sample const &Sample, double const Lateral, double const Height) {
|
auto const lift{[](editor_track::route_sample const &Sample, double const Lateral, double const Height) {
|
||||||
auto const normal{corridor::normal(Sample)};
|
auto const normal{corridor::normal(Sample)};
|
||||||
return Sample.position + glm::dvec3(normal.x * Lateral, Height, normal.y * Lateral);
|
return Sample.position + glm::dvec3(normal.x * Lateral, Height, normal.y * Lateral);
|
||||||
@@ -303,10 +348,10 @@ void build_tunnel(gauge_line const &Line, gauge::profile const &Profile, std::ve
|
|||||||
auto const ring{[&](std::size_t const Index) {
|
auto const ring{[&](std::size_t const Index) {
|
||||||
std::vector<glm::dvec3> points;
|
std::vector<glm::dvec3> points;
|
||||||
for (int side : {-1, 1})
|
for (int side : {-1, 1})
|
||||||
for (std::size_t j = 0; j < Profile.outline.size(); ++j)
|
for (std::size_t j = 0; j < profile.outline.size(); ++j)
|
||||||
{
|
{
|
||||||
auto const &point{Profile.outline[side < 0 ? j : Profile.outline.size() - 1 - j]};
|
auto const &point{profile.outline[side < 0 ? j : profile.outline.size() - 1 - j]};
|
||||||
auto const width{point.half_width + gauge::widening(Line.sections[Index], point.height, side)};
|
auto const width{Line.sections[Index].base + point.half_width + gauge::widening(Line.sections[Index], point.height, side)};
|
||||||
points.push_back(lift(Line.samples[Index], side * width, point.height));
|
points.push_back(lift(Line.samples[Index], side * width, point.height));
|
||||||
}
|
}
|
||||||
return points;
|
return points;
|
||||||
@@ -332,22 +377,30 @@ void build_tunnel(gauge_line const &Line, gauge::profile const &Profile, std::ve
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double widest(gauge::profile const &Profile)
|
|
||||||
{
|
|
||||||
double result{0.0};
|
|
||||||
for (auto const &point : Profile.outline)
|
|
||||||
result = std::max(result, point.half_width);
|
|
||||||
// with the room for the widening in the sharpest curves on the largest cant
|
|
||||||
return result + 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void editor_mode::gauge_load()
|
void editor_mode::gauge_load()
|
||||||
{
|
{
|
||||||
if (m_gauge.profiles.empty())
|
if (m_gauge.profiles.empty())
|
||||||
m_gauge.profiles = gauge::load_profiles(gauge_file);
|
m_gauge.profiles = gauge::load_profiles(gauge_file);
|
||||||
m_gauge.profile = std::clamp(m_gauge.profile, 0, static_cast<int>(m_gauge.profiles.size()) - 1);
|
// the chosen railway and road outlines, the first ones of the kind when there's none
|
||||||
|
auto const fits{[&](int const Index, bool const Road) {
|
||||||
|
return Index >= 0 && Index < static_cast<int>(m_gauge.profiles.size()) && (m_gauge.profiles[Index].kind == gauge::kind::road) == Road;
|
||||||
|
}};
|
||||||
|
for (auto [index, road] : {std::pair<int *, bool>{&m_gauge.profile, false}, std::pair<int *, bool>{&m_gauge.road, true}})
|
||||||
|
if (false == fits(*index, road))
|
||||||
|
{
|
||||||
|
*index = -1;
|
||||||
|
for (int i = 0; i < static_cast<int>(m_gauge.profiles.size()) && *index < 0; ++i)
|
||||||
|
if (fits(i, road))
|
||||||
|
*index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<gauge::profile const *, gauge::profile const *> editor_mode::gauge_profiles() const
|
||||||
|
{
|
||||||
|
auto const pick{[&](int const Index) { return Index >= 0 ? &m_gauge.profiles[Index] : nullptr; }};
|
||||||
|
return {pick(m_gauge.profile), pick(m_gauge.road)};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<editor_mode::gauge_hit> const &editor_mode::gauge_hits() const
|
std::vector<editor_mode::gauge_hit> const &editor_mode::gauge_hits() const
|
||||||
@@ -393,14 +446,17 @@ void editor_mode::scan_gauge(TTrack &Track)
|
|||||||
{
|
{
|
||||||
gauge_load();
|
gauge_load();
|
||||||
m_gauge.line = {};
|
m_gauge.line = {};
|
||||||
auto const &profile{m_gauge.profiles[m_gauge.profile]};
|
auto const [railway, road]{gauge_profiles()};
|
||||||
auto route{editor_track::run_route(Track, gauge_reach)};
|
auto const *profile{gauge_choice{railway, road}.of(Track)};
|
||||||
std::vector<gauge_line> const lines{make_line(route)};
|
if (profile == nullptr || profile->outline.size() < 2)
|
||||||
if (lines.front().samples.size() < 2 || profile.outline.size() < 2)
|
|
||||||
return;
|
return;
|
||||||
corridor const space{lines, widest(profile)};
|
auto route{editor_track::run_route(Track, gauge_reach)};
|
||||||
build_tunnel(lines.front(), profile, m_gauge.line.surface, m_gauge.line.edges);
|
std::vector<gauge_line> const lines{make_line(route, *profile)};
|
||||||
scan_models(space, profile, m_gauge.line.intruding, m_gauge.line.hits);
|
if (lines.front().samples.size() < 2)
|
||||||
|
return;
|
||||||
|
corridor const space{lines};
|
||||||
|
build_tunnel(lines.front(), m_gauge.line.surface, m_gauge.line.edges);
|
||||||
|
scan_models(space, m_gauge.line.intruding, m_gauge.line.hits);
|
||||||
if (false == m_gauge.map.scanned)
|
if (false == m_gauge.map.scanned)
|
||||||
m_gauge.current = -1;
|
m_gauge.current = -1;
|
||||||
}
|
}
|
||||||
@@ -410,16 +466,16 @@ void editor_mode::scan_gauge_map()
|
|||||||
gauge_load();
|
gauge_load();
|
||||||
auto const started{std::chrono::steady_clock::now()};
|
auto const started{std::chrono::steady_clock::now()};
|
||||||
m_gauge.map = {};
|
m_gauge.map = {};
|
||||||
auto const &profile{m_gauge.profiles[m_gauge.profile]};
|
auto const [railway, road]{gauge_profiles()};
|
||||||
auto const lines{scenery_lines()};
|
auto const lines{scenery_lines({railway, road})};
|
||||||
corridor const space{lines, widest(profile)};
|
corridor const space{lines};
|
||||||
scan_models(space, profile, m_gauge.map.intruding, m_gauge.map.hits);
|
scan_models(space, m_gauge.map.intruding, m_gauge.map.hits);
|
||||||
m_gauge.map.scanned = true;
|
m_gauge.map.scanned = true;
|
||||||
m_gauge.map.history = m_history.size();
|
m_gauge.map.history = m_history.size();
|
||||||
m_gauge.map.profile = profile.name;
|
m_gauge.map.profile = (railway ? railway->name : std::string{"-"}) + ", " + (road ? road->name : std::string{"-"});
|
||||||
m_gauge.current = -1;
|
m_gauge.current = -1;
|
||||||
m_gauge.published = false;
|
m_gauge.published = false;
|
||||||
WriteLog("Structure gauge (" + profile.name + "): " + std::to_string(m_gauge.map.hits.size()) + " models enter it along " + std::to_string(lines.size()) + " lines, scanned in " +
|
WriteLog("Structure gauge (" + m_gauge.map.profile + "): " + std::to_string(m_gauge.map.hits.size()) + " models enter it along " + std::to_string(lines.size()) + " lines, scanned in " +
|
||||||
std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - started).count()) + " ms");
|
std::to_string(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - started).count()) + " ms");
|
||||||
if (false == m_gauge.map.hits.empty())
|
if (false == m_gauge.map.hits.empty())
|
||||||
gauge_focus(0);
|
gauge_focus(0);
|
||||||
@@ -489,9 +545,13 @@ void editor_mode::gauge_spot(gauge_hit const &Hit)
|
|||||||
route = editor_track::run_route(*track, gauge_spot_reach);
|
route = editor_track::run_route(*track, gauge_spot_reach);
|
||||||
else
|
else
|
||||||
route.spans.push_back({track, Hit.path, true});
|
route.spans.push_back({track, Hit.path, true});
|
||||||
auto const line{make_line(route)};
|
auto const [railway, road]{gauge_profiles()};
|
||||||
|
auto const *profile{gauge_choice{railway, road}.of(*track)};
|
||||||
|
if (profile == nullptr)
|
||||||
|
return;
|
||||||
|
auto const line{make_line(route, *profile)};
|
||||||
if (line.samples.size() >= 2)
|
if (line.samples.size() >= 2)
|
||||||
build_tunnel(line, m_gauge.profiles[m_gauge.profile], m_gauge.spot.surface, m_gauge.spot.edges);
|
build_tunnel(line, m_gauge.spot.surface, m_gauge.spot.edges);
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_mode::render_gauge_window()
|
void editor_mode::render_gauge_window()
|
||||||
@@ -509,14 +569,12 @@ void editor_mode::render_gauge_window()
|
|||||||
if (ImGui::IsItemHovered())
|
if (ImGui::IsItemHovered())
|
||||||
ImGui::SetTooltip("Translucent tunnel of the gauge 500 m each way along the line of the selected path,\nwith the triangles of the models which enter it in red.\nWidened in the curves under 250 m and tilted on the cant, from 20 m (inner side)\nand 26 m (outer side) ahead of the curve, as in the PKP PLK standard, volume II.");
|
ImGui::SetTooltip("Translucent tunnel of the gauge 500 m each way along the line of the selected path,\nwith the triangles of the models which enter it in red.\nWidened in the curves under 250 m and tilted on the cant, from 20 m (inner side)\nand 26 m (outer side) ahead of the curve, as in the PKP PLK standard, volume II.");
|
||||||
gauge_load();
|
gauge_load();
|
||||||
auto const previous{m_gauge.profile};
|
bool changed{render_gauge_choice("Railway", m_gauge.profile, false)};
|
||||||
for (int i = 0; i < static_cast<int>(m_gauge.profiles.size()); ++i)
|
if (ImGui::IsItemHovered())
|
||||||
{
|
ImGui::SetTooltip("GPL: unified structure gauge, widened only in the curves under 250 m.\nG1, G2, GA, GB, GC: limit (GSZ) and nominal (NSZ) installation gauges, widened by 3750/R in all curves.");
|
||||||
if (i % 2 == 1)
|
changed |= render_gauge_choice("Road", m_gauge.road, true);
|
||||||
ImGui::SameLine(160.0f);
|
if (ImGui::IsItemHovered())
|
||||||
ImGui::RadioButton(m_gauge.profiles[i].name.c_str(), &m_gauge.profile, i);
|
ImGui::SetTooltip("Clearance over the roads, from 0.5 m beyond the edges of the carriageway");
|
||||||
}
|
|
||||||
bool changed{previous != m_gauge.profile};
|
|
||||||
|
|
||||||
if (ImGui::Button("Find the violations on the whole map"))
|
if (ImGui::Button("Find the violations on the whole map"))
|
||||||
scan_gauge_map();
|
scan_gauge_map();
|
||||||
@@ -535,7 +593,8 @@ void editor_mode::render_gauge_window()
|
|||||||
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.3f, 1.0f), "The scenery changed since the scan (%s)", m_gauge.map.profile.c_str());
|
ImGui::TextColored(ImVec4(1.0f, 0.75f, 0.3f, 1.0f), "The scenery changed since the scan (%s)", m_gauge.map.profile.c_str());
|
||||||
}
|
}
|
||||||
render_gauge_hits();
|
render_gauge_hits();
|
||||||
changed |= render_gauge_outline();
|
changed |= render_gauge_outline(m_gauge.profile);
|
||||||
|
changed |= render_gauge_outline(m_gauge.road);
|
||||||
if (changed)
|
if (changed)
|
||||||
m_gauge.pending = true;
|
m_gauge.pending = true;
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
@@ -572,12 +631,32 @@ void editor_mode::render_gauge_hits()
|
|||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool editor_mode::render_gauge_outline()
|
bool editor_mode::render_gauge_choice(char const *Label, int &Index, bool const Road)
|
||||||
{
|
{
|
||||||
if (false == ImGui::TreeNode("Outline (right half)"))
|
bool changed{false};
|
||||||
|
ImGui::SetNextItemWidth(-60.0f);
|
||||||
|
if (ImGui::BeginCombo(Label, Index >= 0 ? m_gauge.profiles[Index].name.c_str() : "none"))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < static_cast<int>(m_gauge.profiles.size()); ++i)
|
||||||
|
if ((m_gauge.profiles[i].kind == gauge::kind::road) == Road && ImGui::Selectable(m_gauge.profiles[i].name.c_str(), i == Index))
|
||||||
|
{
|
||||||
|
changed = i != Index;
|
||||||
|
Index = i;
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool editor_mode::render_gauge_outline(int const Index)
|
||||||
|
{
|
||||||
|
if (Index < 0)
|
||||||
return false;
|
return false;
|
||||||
ImGui::TextDisabled("PKP PLK standard, volume II; below 1170 mm the limit installation gauge.\nMetres from the track axis and above the rail top, bottom up.");
|
auto &profile{m_gauge.profiles[Index]};
|
||||||
auto &profile{m_gauge.profiles[m_gauge.profile]};
|
if (false == ImGui::TreeNode(&profile, "Outline of %s (right half)", profile.name.c_str()))
|
||||||
|
return false;
|
||||||
|
ImGui::TextDisabled(profile.kind == gauge::kind::road ? "Metres from the edge of the carriageway and above the road, bottom up."
|
||||||
|
: "PKP PLK standard, volume II; below 1170 mm the limit installation gauge.\nMetres from the track axis and above the rail top, bottom up.");
|
||||||
bool edited{false};
|
bool edited{false};
|
||||||
bool inserted{false};
|
bool inserted{false};
|
||||||
int remove{-1};
|
int remove{-1};
|
||||||
@@ -626,6 +705,7 @@ bool editor_mode::render_gauge_outline()
|
|||||||
if (ImGui::SmallButton("Restore the starting outlines"))
|
if (ImGui::SmallButton("Restore the starting outlines"))
|
||||||
{
|
{
|
||||||
m_gauge.profiles = gauge::default_profiles();
|
m_gauge.profiles = gauge::default_profiles();
|
||||||
|
gauge_load();
|
||||||
edited = true;
|
edited = true;
|
||||||
}
|
}
|
||||||
if (edited)
|
if (edited)
|
||||||
|
|||||||
@@ -96,6 +96,18 @@ void editor_ui::render_mode_options(nodebank_panel::edit_mode const Mode)
|
|||||||
|
|
||||||
void editor_ui::render_header_sections()
|
void editor_ui::render_header_sections()
|
||||||
{
|
{
|
||||||
|
if (m_gaugewindow != nullptr)
|
||||||
|
{
|
||||||
|
auto const open{*m_gaugewindow};
|
||||||
|
if (open)
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive));
|
||||||
|
if (ImGui::Button(STR_C("Structure gauge"), ImVec2(-1.0f, 0.0f)))
|
||||||
|
*m_gaugewindow = false == *m_gaugewindow;
|
||||||
|
if (open)
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
if (ImGui::IsItemHovered())
|
||||||
|
ImGui::SetTooltip("%s", STR_C("Checks which models enter the structure gauge of the tracks and the clearance over the roads (skrajnia budowli)"));
|
||||||
|
}
|
||||||
if (ImGui::CollapsingHeader("Gizmo", ImGuiTreeNodeFlags_DefaultOpen))
|
if (ImGui::CollapsingHeader("Gizmo", ImGuiTreeNodeFlags_DefaultOpen))
|
||||||
{
|
{
|
||||||
if (m_gizmooptions)
|
if (m_gizmooptions)
|
||||||
@@ -143,12 +155,6 @@ void editor_ui::render_menu_contents()
|
|||||||
ImGui::MenuItem(STR_C("Include database"), nullptr, &m_includespanel.is_open);
|
ImGui::MenuItem(STR_C("Include database"), nullptr, &m_includespanel.is_open);
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
if (m_gaugewindow != nullptr)
|
|
||||||
{
|
|
||||||
ImGui::MenuItem(STR_C("Structure gauge"), nullptr, m_gaugewindow);
|
|
||||||
if (ImGui::IsItemHovered())
|
|
||||||
ImGui::SetTooltip("%s", STR_C("Checks which models enter the structure gauge of the tracks (skrajnia budowli)"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void editor_ui::set_file_actions(std::function<void()> Save, std::function<void()> Export)
|
void editor_ui::set_file_actions(std::function<void()> Save, std::function<void()> Export)
|
||||||
|
|||||||
@@ -87,6 +87,18 @@ private:
|
|||||||
std::vector<double> m_integral;
|
std::vector<double> m_integral;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// names of the kinds in the file
|
||||||
|
char const *const kind_names[]{"unified", "installation", "road"};
|
||||||
|
|
||||||
|
// outline from the points listed top down, in millimetres, as in the tables of the standard, over the lower part
|
||||||
|
profile installation(std::string const &Name, std::vector<std::pair<int, int>> const &Points)
|
||||||
|
{
|
||||||
|
profile result{Name, kind::installation, {{1.211, 0.055}, {1.585, 0.380}, {1.675, 0.380}}};
|
||||||
|
for (auto point = Points.rbegin(); point != Points.rend(); ++point)
|
||||||
|
result.outline.push_back({point->second * 0.001, point->first * 0.001});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::vector<profile> default_profiles()
|
std::vector<profile> default_profiles()
|
||||||
@@ -97,16 +109,31 @@ std::vector<profile> default_profiles()
|
|||||||
std::vector<profile::point> const gpl2{{2.000, 3.625}, {1.920, 4.900}};
|
std::vector<profile::point> const gpl2{{2.000, 3.625}, {1.920, 4.900}};
|
||||||
std::vector<profile::point> const pantograph{{1.450, 6.600}, {1.150, 6.900}, {0.0, 6.900}};
|
std::vector<profile::point> const pantograph{{1.450, 6.600}, {1.150, 6.900}, {0.0, 6.900}};
|
||||||
auto const make{[&](std::string const &Name, std::vector<std::vector<profile::point>> const &Parts) {
|
auto const make{[&](std::string const &Name, std::vector<std::vector<profile::point>> const &Parts) {
|
||||||
profile result{Name, lower};
|
profile result{Name, kind::unified, lower};
|
||||||
for (auto const &part : Parts)
|
for (auto const &part : Parts)
|
||||||
result.outline.insert(result.outline.end(), part.begin(), part.end());
|
result.outline.insert(result.outline.end(), part.begin(), part.end());
|
||||||
return result;
|
return result;
|
||||||
}};
|
}};
|
||||||
return {make("GPL-1", {gpl1, {{1.260, 4.800}, {1.180, 4.850}, {0.0, 4.850}}}), make("GPL-1 + pantograf", {gpl1, pantograph}),
|
// tables 3, 5, 7, 9 of the type cards and the drawing of GC, above 380 mm
|
||||||
make("GPL-2", {gpl2, {{0.0, 4.900}}}), make("GPL-2 + pantograf", {gpl2, {{1.450, 4.900}}, pantograph})};
|
return {make("GPL-1", {gpl1, {{1.260, 4.800}, {1.180, 4.850}, {0.0, 4.850}}}),
|
||||||
|
make("GPL-1 + pantograf", {gpl1, pantograph}),
|
||||||
|
make("GPL-2", {gpl2, {{0.0, 4.900}}}),
|
||||||
|
make("GPL-2 + pantograf", {gpl2, {{1.450, 4.900}}, pantograph}),
|
||||||
|
installation("G1 graniczna (GSZ)", {{4375, 0}, {4375, 666}, {4010, 1255}, {3700, 1550}, {3250, 1755}, {1170, 1695}, {1170, 1675}}),
|
||||||
|
installation("G1 nominalna (NSZ)", {{4385, 0}, {4385, 785}, {4010, 1365}, {3700, 1655}, {3250, 1860}, {1170, 1860}, {1170, 1675}}),
|
||||||
|
installation("G2 graniczna (GSZ)", {{4765, 0}, {4765, 940}, {3835, 1600}, {3530, 1765}, {1170, 1695}, {1170, 1675}}),
|
||||||
|
installation("G2 nominalna (NSZ)", {{4770, 0}, {4770, 1060}, {3835, 1710}, {3530, 1870}, {1170, 1870}, {1170, 1675}}),
|
||||||
|
installation("GA graniczna (GSZ)", {{4415, 0}, {4415, 687}, {4080, 1225}, {3808, 1490}, {3250, 1755}, {1170, 1700}, {1170, 1675}}),
|
||||||
|
installation("GA nominalna (NSZ)", {{4425, 0}, {4425, 805}, {4080, 1340}, {3880, 1600}, {3250, 1860}, {1170, 1860}, {1170, 1675}}),
|
||||||
|
installation("GB graniczna (GSZ)", {{4415, 0}, {4415, 687}, {4080, 1495}, {3250, 1755}, {1170, 1700}, {1170, 1675}}),
|
||||||
|
installation("GB nominalna (NSZ)", {{4425, 0}, {4425, 805}, {4110, 1610}, {3250, 1860}, {1170, 1860}, {1170, 1675}}),
|
||||||
|
installation("GC graniczna (GSZ)", {{4800, 0}, {4800, 1693}, {3550, 1765}, {1170, 1765}, {1170, 1675}}),
|
||||||
|
installation("GC nominalna (NSZ)", {{4820, 0}, {4820, 1815}, {3550, 1870}, {1170, 1870}, {1170, 1675}}),
|
||||||
|
{"Droga 4,70 m", kind::road, {{0.5, 0.0}, {0.5, 4.70}}},
|
||||||
|
{"Droga 4,50 m", kind::road, {{0.5, 0.0}, {0.5, 4.50}}}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// one outline per line: the name in quotes, then pairs of half-width and height
|
// one outline per line: the name in quotes, the kind, then pairs of half-width and height
|
||||||
std::vector<profile> load_profiles(std::string const &File)
|
std::vector<profile> load_profiles(std::string const &File)
|
||||||
{
|
{
|
||||||
std::ifstream input{File};
|
std::ifstream input{File};
|
||||||
@@ -116,8 +143,13 @@ std::vector<profile> load_profiles(std::string const &File)
|
|||||||
{
|
{
|
||||||
std::istringstream stream{line};
|
std::istringstream stream{line};
|
||||||
profile profile;
|
profile profile;
|
||||||
if (false == static_cast<bool>(stream >> std::quoted(profile.name)))
|
std::string kind;
|
||||||
|
if (false == static_cast<bool>(stream >> std::quoted(profile.name) >> kind))
|
||||||
continue;
|
continue;
|
||||||
|
auto const known{std::find(std::begin(kind_names), std::end(kind_names), kind)};
|
||||||
|
if (known == std::end(kind_names))
|
||||||
|
continue;
|
||||||
|
profile.kind = static_cast<gauge::kind>(known - std::begin(kind_names));
|
||||||
profile::point point;
|
profile::point point;
|
||||||
while (stream >> point.half_width >> point.height)
|
while (stream >> point.half_width >> point.height)
|
||||||
profile.outline.push_back(point);
|
profile.outline.push_back(point);
|
||||||
@@ -132,18 +164,18 @@ void save_profiles(std::string const &File, std::vector<profile> const &Profiles
|
|||||||
std::ofstream output{File};
|
std::ofstream output{File};
|
||||||
for (auto const &profile : Profiles)
|
for (auto const &profile : Profiles)
|
||||||
{
|
{
|
||||||
output << std::quoted(profile.name);
|
output << std::quoted(profile.name) << ' ' << kind_names[static_cast<int>(profile.kind)];
|
||||||
for (auto const &point : profile.outline)
|
for (auto const &point : profile.outline)
|
||||||
output << ' ' << point.half_width << ' ' << point.height;
|
output << ' ' << point.half_width << ' ' << point.height;
|
||||||
output << '\n';
|
output << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<section> sections(std::vector<double> const &Chainage, std::vector<double> const &Curvature, std::vector<double> const &Cant)
|
std::vector<section> sections(kind const Kind, std::vector<double> const &Chainage, std::vector<double> const &Curvature, std::vector<double> const &Cant)
|
||||||
{
|
{
|
||||||
auto const count{Chainage.size()};
|
auto const count{Chainage.size()};
|
||||||
std::vector<section> result(count);
|
std::vector<section> result(count);
|
||||||
if (count == 0)
|
if (count == 0 || Kind == kind::road)
|
||||||
return result;
|
return result;
|
||||||
// per side, the parts when it's the inner side of the curve and when it's the outer one
|
// per side, the parts when it's the inner side of the curve and when it's the outer one
|
||||||
for (int side = 0; side < 2; ++side)
|
for (int side = 0; side < 2; ++side)
|
||||||
@@ -155,7 +187,7 @@ std::vector<section> sections(std::vector<double> const &Chainage, std::vector<d
|
|||||||
continue;
|
continue;
|
||||||
auto const radius{1.0 / std::abs(Curvature[i])};
|
auto const radius{1.0 / std::abs(Curvature[i])};
|
||||||
bool const inner{(Curvature[i] > 0.0) == (side == 1)};
|
bool const inner{(Curvature[i] > 0.0) == (side == 1)};
|
||||||
auto const upper{upper_widening(radius)};
|
auto const upper{Kind == kind::unified ? upper_widening(radius) : lower_widening(radius)};
|
||||||
auto const lower{lower_widening(radius)};
|
auto const lower{lower_widening(radius)};
|
||||||
(inner ? upperinner : upperouter)[i] = inner ? upper.inner : upper.outer;
|
(inner ? upperinner : upperouter)[i] = inner ? upper.inner : upper.outer;
|
||||||
(inner ? lowerinner : lowerouter)[i] = inner ? lower.inner : lower.outer;
|
(inner ? lowerinner : lowerouter)[i] = inner ? lower.inner : lower.outer;
|
||||||
@@ -195,7 +227,7 @@ double half_width(profile const &Profile, section const &Section, double const H
|
|||||||
}
|
}
|
||||||
if (width < 0.0)
|
if (width < 0.0)
|
||||||
return width;
|
return width;
|
||||||
return width + widening(Section, Height, Side);
|
return Section.base + width + widening(Section, Height, Side);
|
||||||
}
|
}
|
||||||
|
|
||||||
double intrusion(profile const &Profile, section const &Section, double const Lateral, double const Height)
|
double intrusion(profile const &Profile, section const &Section, double const Lateral, double const Height)
|
||||||
|
|||||||
@@ -18,11 +18,21 @@ http://mozilla.org/MPL/2.0/.
|
|||||||
namespace gauge
|
namespace gauge
|
||||||
{
|
{
|
||||||
|
|
||||||
// right half of the outline on a straight track, in metres: half-width from the axis and height above the rail top,
|
// how the outline widens along the track
|
||||||
// listed from the bottom up; the left half is the mirror
|
enum class kind
|
||||||
|
{
|
||||||
|
unified, // skrajnia budowli ujednolicona GPL: the curves of R >= 250 m are included above the lower part
|
||||||
|
installation, // graniczna or nominalna skrajnia zabudowy: widened by 3750/R in all curves
|
||||||
|
road // skrajnia drogi: measured from the edge of the carriageway, no widening
|
||||||
|
};
|
||||||
|
|
||||||
|
// right half of the outline on a straight track, in metres: half-width from the axis (for the roads from the edge
|
||||||
|
// of the carriageway) and height above the rail top or the road surface, listed from the bottom up; the left half
|
||||||
|
// is the mirror
|
||||||
struct profile
|
struct profile
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
|
gauge::kind kind{kind::unified};
|
||||||
struct point
|
struct point
|
||||||
{
|
{
|
||||||
double half_width{0.0};
|
double half_width{0.0};
|
||||||
@@ -31,7 +41,8 @@ struct profile
|
|||||||
std::vector<point> outline;
|
std::vector<point> outline;
|
||||||
};
|
};
|
||||||
|
|
||||||
// GPL-1 and GPL-2 over the limit installation gauge below 1170 mm, with and without the pantograph gauge
|
// GPL-1 and GPL-2 over the limit installation gauge below 1170 mm, with and without the pantograph gauge, the limit
|
||||||
|
// and nominal installation gauges G1, G2, GA, GB, GC of the PKP PLK technical standards, and the road gauges
|
||||||
std::vector<profile> default_profiles();
|
std::vector<profile> default_profiles();
|
||||||
// outlines kept in the file, the defaults when there's none
|
// outlines kept in the file, the defaults when there's none
|
||||||
std::vector<profile> load_profiles(std::string const &File);
|
std::vector<profile> load_profiles(std::string const &File);
|
||||||
@@ -46,13 +57,14 @@ double constexpr lower_part{1.17};
|
|||||||
// widening of the sides of the gauge at a place of the track, metres; index 0 is the right side, 1 the left one
|
// widening of the sides of the gauge at a place of the track, metres; index 0 is the right side, 1 the left one
|
||||||
struct section
|
struct section
|
||||||
{
|
{
|
||||||
|
double base{0.0}; // added to the half-width of the outline: half of the carriageway of a road
|
||||||
std::array<double, 2> upper{}; // from the radius, above the lower part
|
std::array<double, 2> upper{}; // from the radius, above the lower part
|
||||||
std::array<double, 2> lower{}; // from the radius, in the lower part
|
std::array<double, 2> lower{}; // from the radius, in the lower part
|
||||||
std::array<double, 2> cant{}; // cant in metres, positive on the inner side of the curve and negative on the outer one
|
std::array<double, 2> cant{}; // cant in metres, positive on the inner side of the curve and negative on the outer one
|
||||||
};
|
};
|
||||||
// sections at the points of a route, from their chainage, curvature (1/m, positive turning left) and cant (m).
|
// sections at the points of a route, from their chainage, curvature (1/m, positive turning left) and cant (m).
|
||||||
// The widening begins and ends ahead of the changes of the curvature and cant, as the vehicles enter a curve
|
// The widening begins and ends ahead of the changes of the curvature and cant, as the vehicles enter a curve
|
||||||
std::vector<section> sections(std::vector<double> const &Chainage, std::vector<double> const &Curvature, std::vector<double> const &Cant);
|
std::vector<section> sections(kind Kind, std::vector<double> const &Chainage, std::vector<double> const &Curvature, std::vector<double> const &Cant);
|
||||||
|
|
||||||
// widening of the side (+1 left, -1 right) at the height
|
// widening of the side (+1 left, -1 right) at the height
|
||||||
double widening(section const &Section, double Height, int Side);
|
double widening(section const &Section, double Height, int Side);
|
||||||
|
|||||||
Reference in New Issue
Block a user