diff --git a/application/editormode.h b/application/editormode.h index 4bef2f71..8114831f 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -321,7 +321,8 @@ class editor_mode : public application_mode, private editor_track::observer bool open{false}; // window bool enabled{true}; // tunnel along the selected path std::vector profiles; - int profile{0}; + int profile{0}; // railway + int road{-1}; TTrack const *track{nullptr}; // scanned std::size_t history{0}; 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 } m_gauge; void gauge_load(); + // the chosen railway and road outlines, null when there's none of the kind + std::pair gauge_profiles() const; std::vector const &gauge_hits() const; void update_gauge(); 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 render_gauge_window(); 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 { TTrack *from{nullptr}; diff --git a/application/editormodegauge.cpp b/application/editormodegauge.cpp index 2a07a950..d08b91ba 100644 --- a/application/editormodegauge.cpp +++ b/application/editormodegauge.cpp @@ -45,37 +45,71 @@ double const gauge_spot_reach{60.0}; // sampled line of the tracks with the widening of the gauge along it struct gauge_line { + gauge::profile const *profile{nullptr}; editor_track::route route; std::vector samples; std::vector 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; + line.profile = &Profile; line.samples = editor_track::sample_route(Route, 1.0); line.route = Route; + bool const road{Profile.kind == gauge::kind::road}; std::vector chainage, curvature, cant; 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); curvature.push_back(sample.curvature); 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; } // lines through all paths of the scenery, each path in one of them at least -std::vector scenery_lines() +std::vector scenery_lines(gauge_choice const &Choice) { std::vector lines; std::unordered_set visited; auto const seen{[&](editor_track::route_span const &Span) { return visited.count(Span.track) > 0; }}; 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; 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 @@ -86,15 +120,15 @@ std::vector scenery_lines() spans.pop_back(); for (auto const &span : spans) visited.insert(span.track); - lines.push_back(make_line(route)); + lines.push_back(make_line(route, *Choice.of(*track))); } // diverging paths of the switches 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; route.spans.push_back({track, 1, true}); - lines.push_back(make_line(route)); + lines.push_back(make_line(route, *Choice.of(*track))); } return lines; } @@ -103,11 +137,12 @@ std::vector scenery_lines() class corridor { public: - corridor(std::vector const &Lines, double const Margin) : m_lines{Lines}, m_margin{Margin} + explicit corridor(std::vector const &Lines) : m_lines{Lines} { for (std::uint32_t line = 0; line < Lines.size(); ++line) 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)}; m_cells[key(point, cell)].push_back({line, i}); m_regions.insert(key(point, region)); @@ -139,7 +174,7 @@ public: 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 - 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 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)) continue; 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) deepest = {depth, &line.route.spans[sample.span]}; } @@ -208,8 +243,17 @@ private: 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 const &m_lines; - double m_margin; + double m_margin{0.0}; std::unordered_map> m_cells; std::unordered_set m_regions; }; @@ -253,7 +297,7 @@ template 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 -void scan_models(corridor const &Space, gauge::profile const &Profile, std::vector &Triangles, std::vector &Hits) +void scan_models(corridor const &Space, std::vector &Triangles, std::vector &Hits) { 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) { auto const point{A + (B - A) * (static_cast(u) / steps) + (C - A) * (static_cast(v) / steps)}; - auto const found{Space.intrusion(Profile, point)}; + auto const found{Space.intrusion(point)}; if (found.depth <= 0.0) continue; 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 -void build_tunnel(gauge_line const &Line, gauge::profile const &Profile, std::vector &Surface, std::vector &Edges) +void build_tunnel(gauge_line const &Line, std::vector &Surface, std::vector &Edges) { + auto const &profile{*Line.profile}; auto const lift{[](editor_track::route_sample const &Sample, double const Lateral, double const Height) { auto const normal{corridor::normal(Sample)}; 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) { std::vector points; 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 width{point.half_width + gauge::widening(Line.sections[Index], point.height, side)}; + auto const &point{profile.outline[side < 0 ? j : profile.outline.size() - 1 - j]}; + 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)); } 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 void editor_mode::gauge_load() { if (m_gauge.profiles.empty()) m_gauge.profiles = gauge::load_profiles(gauge_file); - m_gauge.profile = std::clamp(m_gauge.profile, 0, static_cast(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(m_gauge.profiles.size()) && (m_gauge.profiles[Index].kind == gauge::kind::road) == Road; + }}; + for (auto [index, road] : {std::pair{&m_gauge.profile, false}, std::pair{&m_gauge.road, true}}) + if (false == fits(*index, road)) + { + *index = -1; + for (int i = 0; i < static_cast(m_gauge.profiles.size()) && *index < 0; ++i) + if (fits(i, road)) + *index = i; + } +} + +std::pair 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 const &editor_mode::gauge_hits() const @@ -393,14 +446,17 @@ void editor_mode::scan_gauge(TTrack &Track) { gauge_load(); m_gauge.line = {}; - auto const &profile{m_gauge.profiles[m_gauge.profile]}; - auto route{editor_track::run_route(Track, gauge_reach)}; - std::vector const lines{make_line(route)}; - if (lines.front().samples.size() < 2 || profile.outline.size() < 2) + auto const [railway, road]{gauge_profiles()}; + auto const *profile{gauge_choice{railway, road}.of(Track)}; + if (profile == nullptr || profile->outline.size() < 2) return; - corridor const space{lines, widest(profile)}; - build_tunnel(lines.front(), profile, m_gauge.line.surface, m_gauge.line.edges); - scan_models(space, profile, m_gauge.line.intruding, m_gauge.line.hits); + auto route{editor_track::run_route(Track, gauge_reach)}; + std::vector const lines{make_line(route, *profile)}; + 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) m_gauge.current = -1; } @@ -410,16 +466,16 @@ void editor_mode::scan_gauge_map() gauge_load(); auto const started{std::chrono::steady_clock::now()}; m_gauge.map = {}; - auto const &profile{m_gauge.profiles[m_gauge.profile]}; - auto const lines{scenery_lines()}; - corridor const space{lines, widest(profile)}; - scan_models(space, profile, m_gauge.map.intruding, m_gauge.map.hits); + auto const [railway, road]{gauge_profiles()}; + auto const lines{scenery_lines({railway, road})}; + corridor const space{lines}; + scan_models(space, m_gauge.map.intruding, m_gauge.map.hits); m_gauge.map.scanned = true; 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.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::steady_clock::now() - started).count()) + " ms"); if (false == m_gauge.map.hits.empty()) 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); else 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) - 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() @@ -509,14 +569,12 @@ void editor_mode::render_gauge_window() 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."); gauge_load(); - auto const previous{m_gauge.profile}; - for (int i = 0; i < static_cast(m_gauge.profiles.size()); ++i) - { - if (i % 2 == 1) - ImGui::SameLine(160.0f); - ImGui::RadioButton(m_gauge.profiles[i].name.c_str(), &m_gauge.profile, i); - } - bool changed{previous != m_gauge.profile}; + bool changed{render_gauge_choice("Railway", m_gauge.profile, false)}; + 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."); + changed |= render_gauge_choice("Road", m_gauge.road, true); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Clearance over the roads, from 0.5 m beyond the edges of the carriageway"); if (ImGui::Button("Find the violations on the whole 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()); } render_gauge_hits(); - changed |= render_gauge_outline(); + changed |= render_gauge_outline(m_gauge.profile); + changed |= render_gauge_outline(m_gauge.road); if (changed) m_gauge.pending = true; ImGui::End(); @@ -572,12 +631,32 @@ void editor_mode::render_gauge_hits() 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(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; - 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[m_gauge.profile]}; + auto &profile{m_gauge.profiles[Index]}; + 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 inserted{false}; int remove{-1}; @@ -626,6 +705,7 @@ bool editor_mode::render_gauge_outline() if (ImGui::SmallButton("Restore the starting outlines")) { m_gauge.profiles = gauge::default_profiles(); + gauge_load(); edited = true; } if (edited) diff --git a/application/editoruilayer.cpp b/application/editoruilayer.cpp index a4610491..a4c10e01 100644 --- a/application/editoruilayer.cpp +++ b/application/editoruilayer.cpp @@ -96,6 +96,18 @@ void editor_ui::render_mode_options(nodebank_panel::edit_mode const Mode) 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 (m_gizmooptions) @@ -143,12 +155,6 @@ void editor_ui::render_menu_contents() ImGui::MenuItem(STR_C("Include database"), nullptr, &m_includespanel.is_open); 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 Save, std::function Export) diff --git a/editor/editorGauge.cpp b/editor/editorGauge.cpp index 59acae31..9ba12e9b 100644 --- a/editor/editorGauge.cpp +++ b/editor/editorGauge.cpp @@ -87,6 +87,18 @@ private: std::vector 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> 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 std::vector default_profiles() @@ -97,16 +109,31 @@ std::vector default_profiles() std::vector const gpl2{{2.000, 3.625}, {1.920, 4.900}}; std::vector const pantograph{{1.450, 6.600}, {1.150, 6.900}, {0.0, 6.900}}; auto const make{[&](std::string const &Name, std::vector> const &Parts) { - profile result{Name, lower}; + profile result{Name, kind::unified, lower}; for (auto const &part : Parts) result.outline.insert(result.outline.end(), part.begin(), part.end()); 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}), - make("GPL-2", {gpl2, {{0.0, 4.900}}}), make("GPL-2 + pantograf", {gpl2, {{1.450, 4.900}}, pantograph})}; + // tables 3, 5, 7, 9 of the type cards and the drawing of GC, above 380 mm + 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 load_profiles(std::string const &File) { std::ifstream input{File}; @@ -116,8 +143,13 @@ std::vector load_profiles(std::string const &File) { std::istringstream stream{line}; profile profile; - if (false == static_cast(stream >> std::quoted(profile.name))) + std::string kind; + if (false == static_cast(stream >> std::quoted(profile.name) >> kind)) 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(known - std::begin(kind_names)); profile::point point; while (stream >> point.half_width >> point.height) profile.outline.push_back(point); @@ -132,18 +164,18 @@ void save_profiles(std::string const &File, std::vector const &Profiles std::ofstream output{File}; for (auto const &profile : Profiles) { - output << std::quoted(profile.name); + output << std::quoted(profile.name) << ' ' << kind_names[static_cast(profile.kind)]; for (auto const &point : profile.outline) output << ' ' << point.half_width << ' ' << point.height; output << '\n'; } } -std::vector
sections(std::vector const &Chainage, std::vector const &Curvature, std::vector const &Cant) +std::vector
sections(kind const Kind, std::vector const &Chainage, std::vector const &Curvature, std::vector const &Cant) { auto const count{Chainage.size()}; std::vector
result(count); - if (count == 0) + if (count == 0 || Kind == kind::road) return result; // 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) @@ -155,7 +187,7 @@ std::vector
sections(std::vector const &Chainage, std::vector 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)}; (inner ? upperinner : upperouter)[i] = inner ? upper.inner : upper.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) 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) diff --git a/editor/editorGauge.hpp b/editor/editorGauge.hpp index ee9b6be8..d7e94cf9 100644 --- a/editor/editorGauge.hpp +++ b/editor/editorGauge.hpp @@ -18,11 +18,21 @@ http://mozilla.org/MPL/2.0/. namespace gauge { -// right half of the outline on a straight track, in metres: half-width from the axis and height above the rail top, -// listed from the bottom up; the left half is the mirror +// how the outline widens along the track +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 { std::string name; + gauge::kind kind{kind::unified}; struct point { double half_width{0.0}; @@ -31,7 +41,8 @@ struct profile std::vector 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 default_profiles(); // outlines kept in the file, the defaults when there's none std::vector 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 struct section { + double base{0.0}; // added to the half-width of the outline: half of the carriageway of a road std::array upper{}; // from the radius, above the lower part std::array lower{}; // from the radius, in the lower part std::array 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). // The widening begins and ends ahead of the changes of the curvature and cant, as the vehicles enter a curve -std::vector
sections(std::vector const &Chainage, std::vector const &Curvature, std::vector const &Cant); +std::vector
sections(kind Kind, std::vector const &Chainage, std::vector const &Curvature, std::vector const &Cant); // widening of the side (+1 left, -1 right) at the height double widening(section const &Section, double Height, int Side);