17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 18:37:29 +01:00

Merge remote-tracking branch 'jano211/siec-radio' into feat/renderers-cleanup

This commit is contained in:
2026-10-26 16:36:36 +01:00
57 changed files with 23957 additions and 22 deletions

View File

@@ -20,6 +20,7 @@ http://mozilla.org/MPL/2.0/.
#include "utilities/Timer.h"
#include "world/Event.h"
#include "world/TractionPower.h"
#include "world/catenary_span.h"
#include "vehicle/Camera.h"
#include "world/mtable.h"
#include "vehicle/Train.h"
@@ -1568,6 +1569,10 @@ debug_panel::render_section_settings() const {
if( ImGui::SliderFloat( ( to_string( std::abs( Global.gfx_shadow_angle_min ), 2 ) + "###shadowanglecutoff" ).c_str(), &Global.gfx_shadow_angle_min, -1.0, -0.2f, "Shadow angle cutoff" ) ) {
Global.gfx_shadow_angle_min = quantize( Global.gfx_shadow_angle_min, 0.05f );
}
// sieć trakcyjna: zasięg rysowania przewodów, lin i uchwytów jako brył; 0 wyłącza bryły
if( ImGui::SliderFloat( ( to_string( CatenarySettings.solid_range, 0 ) + " m###catenarysolidrange" ).c_str(), &CatenarySettings.solid_range, 0.0f, 500.0f, "3D overhead wires range" ) ) {
CatenarySettings.solid_range = quantize( CatenarySettings.solid_range, 10.0f );
}
if( DebugModeFlag ) {
// sky sliders
{

View File

@@ -34,6 +34,7 @@ extern char **environ;
#include "editor/editorFormat.hpp"
#include "editor/editorAlignmentStore.hpp"
#include "editor/editorSwitchStore.hpp"
#include "world/catenary_scene.h"
#include "editor/editorGeometry.hpp"
#include "editor/editorIcons.hpp"
#include "utilities/translation.h"
@@ -1276,6 +1277,7 @@ void editor_mode::redo_one()
bool editor_mode::update()
{
// the catenary doesn't need the far tracks drawn (50 km of them cost a lot in a big scenery)
Global.editor_tracks = (current_work_area() == work_area::tracks);
selftest_step();
if (m_vehicle.leave)
@@ -1443,6 +1445,11 @@ bool editor_mode::update()
signal_move_update();
draw_signal_overlay();
}
if (m_track_tab == track_tab::catenary)
{
catenary_drag_update();
draw_catenary_overlay();
}
draw_parallel_preview();
if (m_track_tab == track_tab::lineside)
{
@@ -2986,6 +2993,23 @@ bool editor_mode::save()
switch_store::tidy();
switch_store::settle();
infra_store();
// the catenary (cantilevers on the poles) lives in files of its own; a file made in the editor needs the directive
std::vector<std::string> catenarynew;
if (catenary_scene_changed())
{
std::string message;
if (false == catenary_scene_save(message))
{
ui()->set_status(format(STR_C("The catenary couldn't be saved: %s"), message.c_str()), true);
return false;
}
WriteLog("Editor: catenary written: " + message, logtype::generic);
}
for (auto const &file : catenary_scene_new_files())
{
rootstatements.emplace_back("catenary " + file + " endcatenary");
catenarynew.push_back(file);
}
if (false == m_saved_camera.has_value() || glm::distance(m_saved_camera->first, glm::dvec3{Camera.Pos}) > 0.5 || glm::distance(m_saved_camera->second, glm::vec3{Camera.Angle}) > 0.01f)
{
m_saved_camera = std::make_pair(glm::dvec3{Camera.Pos}, glm::vec3{Camera.Angle});
@@ -3034,6 +3058,9 @@ bool editor_mode::save()
{
scene::Layers.terrain_directive(true);
}
if (result.success)
for (auto const &file : catenarynew)
catenary_scene_file_included(file);
ui()->set_status(result.message, false == result.success);
WriteLog("Editor: " + result.message, logtype::generic);
return result.success;
@@ -3815,6 +3842,7 @@ void editor_mode::render_environment_window()
{
Global.AirTemperature = std::clamp(temperature, -35.0f, 40.0f);
simulation::Environment.compute_weather();
catenary_scene_temperature(); // ciężary kotwień idą za temperaturą
}
// the layers of clouds, the fog and a weather report, as the weather section of a scenery describes them
if (ImGui::CollapsingHeader(STR_C("Clouds, fog and weather report")))
@@ -4668,7 +4696,7 @@ editor_mode::work_area editor_mode::current_work_area() const
if (m_terrain_open)
return work_area::terrain;
if (m_track_window_open)
return work_area::tracks;
return m_track_tab == track_tab::catenary ? work_area::catenary : work_area::tracks;
if (m_roadtool.window)
return work_area::roads;
return work_area::surroundings;
@@ -4684,7 +4712,11 @@ void editor_mode::choose_work_area(work_area const Area)
break;
case work_area::tracks:
terrain_workspace(false);
show_track_tab(m_track_tab);
show_track_tab(m_track_tab == track_tab::catenary ? track_tab::path : m_track_tab);
break;
case work_area::catenary:
terrain_workspace(false);
show_track_tab(track_tab::catenary);
break;
case work_area::roads:
m_track_window_open = false;
@@ -4713,8 +4745,9 @@ void editor_mode::render_workspaces()
{STR_C("Tracks"), work_area::tracks, editor_icons::icon::work_tracks, STR_C("Tracks and switches, signals, objects along the track, checks of the line")},
{STR_C("Roads"), work_area::roads, editor_icons::icon::work_roads, STR_C("Roads and junctions, level crossings, the points where the traffic comes and goes")},
{STR_C("Terrain"), work_area::terrain, editor_icons::icon::work_terrain, STR_C("Heightmap terrain: chunks, sculpting, painting, water, orthophoto")},
{STR_C("Catenary"), work_area::catenary, editor_icons::icon::work_catenary, STR_C("The overhead catenary: poles with cantilevers, gantries, the wires of the chains")},
};
char const *const keys[] = {"F2", "F3", "F4", "F5"};
char const *const keys[] = {"F2", "F3", "F4", "F5", "F6"};
auto const current{current_work_area()};
auto const &style{ImGui::GetStyle()};
auto *list{ImGui::GetWindowDrawList()};
@@ -5610,7 +5643,7 @@ void editor_mode::render_shortcuts_window()
{"Ctrl+Shift+F11", "Export scenery dump"},
{"F11", "Leave the editor"},
{"F10", "Quit the simulator"}}},
{"Fields of work", {{"F2", "Surroundings"}, {"F3", "Tracks"}, {"F4", "Roads"}, {"F5", "Terrain"}}},
{"Fields of work", {{"F2", "Surroundings"}, {"F3", "Tracks"}, {"F4", "Roads"}, {"F5", "Terrain"}, {"F6", "Catenary"}}},
{"Camera",
{{"RMB held, mouse", "Look around"},
{"RMB held, W S A D", "Fly forwards, back, left, right (arrows in the legacy scheme)"},
@@ -6362,6 +6395,8 @@ void editor_mode::delete_selected()
// what's selected: a road piece or point, a signal, an include of a scenery template, a model, tracks
if (road_delete())
return;
if (m_track_tab == track_tab::catenary && ui()->mode() == nodebank_panel::TRACK && catenary_delete_selected())
return;
if (m_instance != 0 && signal_delete(m_instance))
return;
if (m_instance != 0)
@@ -6429,6 +6464,10 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in
if (ui()->mode() == nodebank_panel::TRACK && is_press(Action) && (Mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)) == 0 && typed_key(Key))
return;
// the catenary tool may have keys of its own
if (ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::catenary && catenary_on_key(Key, Action, Mods))
return;
// gizmo transform shortcuts (Q/W/E/R) — only when the camera isn't being flown (RMB up).
// handled before the camera keyboard step because Q/W/E are also the fly-mode movement keys,
// which would otherwise consume them.
@@ -6452,6 +6491,7 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in
case GLFW_KEY_F3: choose_work_area(work_area::tracks); break;
case GLFW_KEY_F4: choose_work_area(work_area::roads); break;
case GLFW_KEY_F5: choose_work_area(work_area::terrain); break;
case GLFW_KEY_F6: choose_work_area(work_area::catenary); break;
// the edit modes in the order of the toolbar; in the track mode the digits typed as a value were taken already
case GLFW_KEY_1: handled = ui()->mode() != nodebank_panel::TRACK; if (handled) choose_edit_mode(nodebank_panel::MODIFY); break;
case GLFW_KEY_2: handled = ui()->mode() != nodebank_panel::TRACK; if (handled) choose_edit_mode(nodebank_panel::ADD); break;
@@ -6544,6 +6584,9 @@ void editor_mode::on_key(int const Key, int const Scancode, int const Action, in
return;
if (Global.ctrlState && Key == GLFW_KEY_Z && is_press(Action))
{
// in the catenary tool its own changes are taken back first
if (ui()->mode() == nodebank_panel::TRACK && m_track_tab == track_tab::catenary && catenary_undo())
return;
undo_last();
return;
}
@@ -6886,6 +6929,8 @@ void editor_mode::on_mouse_button(int const Button, int const Action, int const
signal_release();
if (m_switch.slide.active)
switch_slide_finish();
if (catenary_dragging())
catenary_release();
}
m_dragging = false;

View File

@@ -58,6 +58,8 @@ struct vehicle_desc;
struct skin_set;
}
struct catenary_editor; // application/editormodecatenary.h
class editor_mode : public application_mode, private editor_track::observer
{
@@ -768,7 +770,8 @@ class editor_mode : public application_mode, private editor_track::observer
surroundings,
tracks,
roads,
terrain
terrain,
catenary // the catenary tool of the tracks, as a field of work of its own
};
work_area current_work_area() const;
void choose_work_area(work_area const Area);
@@ -1540,7 +1543,7 @@ class editor_mode : public application_mode, private editor_track::observer
void update_build_tools();
void draw_build_overlay() const;
// modes of the track editor, one at a time: path is the plain selection
enum class track_tab { straights, route, path, turnout, profile, speed, joints, infra, lay, lineside, turntable, signals, parallel };
enum class track_tab { straights, route, path, turnout, profile, speed, joints, infra, lay, lineside, turntable, signals, parallel, catenary };
struct track_mode
{
char const *label;
@@ -1550,7 +1553,7 @@ class editor_mode : public application_mode, private editor_track::observer
editor_icons::icon picture;
int family; // 0 builds the track, 1 equips it, 2 checks it
};
static std::array<track_mode, 11> const &track_modes();
static std::array<track_mode, 12> const &track_modes();
bool track_plan_tab() const { return m_track_tab == track_tab::straights || m_track_tab == track_tab::route; }
track_tab plan_tab_for(TTrack const *Track) const;
// the objects along the track: a tab for each tool
@@ -1636,7 +1639,7 @@ class editor_mode : public application_mode, private editor_track::observer
void draw_track_hover() const;
struct track_intent
{
enum class kind { none, gizmo, select, point, box, straight_set, straight_break, detour, straight_tool, straight_handle, route_vertex, route_grip, switch_at, lay, lay_end, sweep, turntable_place, turntable_exit, turntable_fit, spread, extend_end, signal_place, signal_move, switch_slide, pick_style, fouling };
enum class kind { none, gizmo, select, point, box, straight_set, straight_break, detour, straight_tool, straight_handle, route_vertex, route_grip, switch_at, lay, lay_end, sweep, turntable_place, turntable_exit, turntable_fit, spread, extend_end, signal_place, signal_move, switch_slide, pick_style, fouling, catenary };
kind what{kind::none};
TTrack *track{nullptr};
int path{0};
@@ -1645,6 +1648,7 @@ class editor_mode : public application_mode, private editor_track::observer
glm::dvec3 position{0.0};
editor_track::snap_target snap;
std::string action;
int detail{0}; // what the tool of the intent means by it (catenary: what was hit)
};
track_intent m_intent;
track_intent track_intent_at(int const Mods);
@@ -2176,6 +2180,28 @@ class editor_mode : public application_mode, private editor_track::observer
std::string signal_intent_label(signal_template const &Template, signal_spot const &Spot) const;
std::string signal_variant(signal_template const &Template, signal_spot const &Spot) const;
// catenary (sieć trakcyjna): the whole tool, its state and its workings are in application/editormodecatenary.h/.cpp;
// here only the fixed hooks the rest of the editor calls, so changes of the tool rebuild only its own file
friend struct catenary_editor;
std::shared_ptr<catenary_editor> m_catenary_editor;
catenary_editor &catenary_tool();
void catenary_scan();
bool catenary_intent(track_intent &Intent); // in the catenary tab: what a click would do; false: nothing
void catenary_press(track_intent const &Intent);
void catenary_release();
bool catenary_dragging() const;
void catenary_drag_update();
void draw_catenary_overlay() const;
void render_catenary_ui();
bool catenary_delete_selected();
bool catenary_undo(); // false: nothing of its own to take back
bool catenary_on_key(int const Key, int const Action, int const Mods); // true: the key was used by the tool
std::string catenary_mode_name() const;
void catenary_key_hints(std::vector<key_hint> &Hints) const;
// the page of the catenary field of work: 0 poles, 1 gantries, 2 wires
int catenary_page();
void catenary_choose_page(int const Page);
// a model laid along the track: bent to follow it, or repeated along it
struct sweep_tool
{

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,260 @@
/*
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/.
*/
// The catenary tool of the editor (sieć trakcyjna): its state and workings. The editor (editormode.h) holds it behind a
// pointer and calls it through a few fixed hooks, so a change here rebuilds only editormodecatenary.cpp, not the files
// which include editormode.h. The tool is a friend of editor_mode and reaches the editor through `e`.
#pragma once
#include "application/editormode.h"
#include "world/catenary_suspension.h"
#include "world/catenary_cantilever.h"
#include <map>
#include <optional>
#include <string>
#include <vector>
struct catenary_editor
{
explicit catenary_editor(editor_mode &Editor) : e{Editor} {}
editor_mode &e;
using track_intent = editor_mode::track_intent;
using key_hint = editor_mode::key_hint;
// what a catenary intent (track_intent::kind::catenary) is about, in its detail
enum class intent_detail { place, select, chain, gantry, mast, anchor };
static bool is_intent(track_intent const &Intent, intent_detail const Detail)
{
return Intent.what == track_intent::kind::catenary && Intent.detail == static_cast<int>(Detail);
}
// catenary: poles with cantilevers by the track, put where the user sees them (on the orthophoto), the cantilever card
// chosen from the catalogue by the distance from the face of the pole; spans checked against the catalogue tables
struct catenary_spot
{
TTrack *track{nullptr};
int path{0};
double parameter{0.0};
glm::dvec3 axis{0.0}; // the point of the path, under the rail heads
glm::dvec2 forward{0.0, 1.0};
int side{1}; // the pole on the right (+1) or on the left (-1) of the direction of the path
double radius{0.0}; // of the track there, 0 on a straight
int inward{0}; // the centre of the bend: +1 on the right of the direction of the path, -1 on the left, 0 straight
bool curve{false};
bool toward{true}; // the wires are offset towards the pole
double reach{0.0}; // A: from the wires to the face of the pole
int card{0};
std::string problem; // why there's no card, empty when there is one
std::string family; // the family of the card in the described catalogue; empty without it
bool execution_changed{false}; // the execution chosen in the panel has no card for this A, another one was taken
bool switch_error{false}; // a pole of a group that doesn't stand by switches (concrete), standing by one
catenary::suspension data;
};
struct catenary_change
{
int group{0}; // changes of one action are taken back together
bool chain{false}; // a change of a chain, otherwise of a suspension
std::size_t index{0};
std::optional<catenary::suspension> before; // none: the suspension was added
std::optional<catenary::chain> chain_before; // none: the chain was added
bool removed{false}; // the suspension was removed
bool gantry{false}; // a change of a gantry
std::optional<catenary::gantry> gantry_before; // none: the gantry was added
bool anchor{false}; // a change of an anchor
std::optional<catenary::anchor> anchor_before; // none: the anchor was added
};
struct catenary_tool
{
std::vector<std::string> poles;
std::vector<std::string> networks;
int pole{0};
int network{0};
int zone{1};
int execution{1};
bool swinging{true};
bool toward{true};
bool alternate{true}; // on a straight the next one goes the other way
int bend{0}; // 0: from the track, 1: straight, 2: curve
float stagger{0.30f};
float clearance{2.50f}; // from the axis of the track to the face of the pole
float height{5.60f}; // of the contact wires over the rails
float system_height{0.0f}; // hk: of the messenger over the contact wires; 0 = the card's
bool insulators25{false};
char file[128]{};
int selected{-1};
// chains: the poles placed one after another are joined by spans
bool chaining{true};
int continue_pending{-1}; // the chain goes on from this pole, from the next frame of the panel (the button is in the middle of it)
bool insert_spans{true}; // a pole placed in a span of a chain goes into the chain between the two poles of the span
int chain{-1}; // the chain the next pole is added to
int last{-1}; // the pole the next span starts at
bool append{true}; // the next pole goes after the last one of the chain, otherwise before the first
int chain_selected{-1}; // the chain shown in the panel
int connect_from{-1}; // hanging the wires on from this suspension: the next poles clicked get the spans
int umbrella_from{-1}; // an umbrella from this suspension: the next LMB by a track puts the second cantilever on its pole (or drop)
int anchor_pick{-1}; // anchoring this chain to an existing pole: the next pole clicked carries the tensioning device
bool anchor_pick_start{false}; // ... at the start of the chain, otherwise at its end
bool anchor_pick_contact{false}; // ... the contact wires only (anchored apart from the messenger)
int anchor_selected{-1}; // the anchor picked in the view: its chain is shown, the panel marks it, Del removes it
bool anchor_scroll{false}; // the panel scrolls to the anchor picked in the view, once
char power[64]{"none"};
float voltage{3000.0f};
int material{0}; // 0 cu, 1 al
int state{0}; // 0 new, 1 worn, 2 tarnished (state 0, 2, 1 in the entry)
std::map<std::size_t, catenary_spot> spots; // where the suspensions are by the track, by their numbers
std::vector<catenary_change> undo;
int group{0};
catenary::cantilever_result probe;
bool probed{false};
bool probe_good{false};
std::string status;
bool scanned{false};
// gantries: two masts and the beam between them, drops with cantilevers on the beam
std::vector<std::string> masts;
int mast_first{0};
int mast_second{0};
bool mast_second_own{false}; // the second mast was picked by the user: it no longer follows the first
bool gantry_high{false};
int gantry_side{1};
bool gantry_placing{false}; // the next two clicks put the masts
bool gantry_single{false}; // what's placed is a single beam element on one through mast: the second click sets its direction
std::optional<glm::dvec3> gantry_first; // the first mast, waiting for the second
std::optional<glm::dvec2> gantry_track; // the direction of the track by the first mast, for the beam square to it
bool gantry_square{true}; // the beam goes square to the track
bool gantry_snap{true}; // the first mast goes at gantry_face from the axis of the track under the cursor
int shift_group{0}; // the undo group of the distance of a mast being dragged; 0: the next change opens one
float gantry_face{2.50f}; // from the axis of the track to the face of the mast (towards the track)
char gantry_skin[128]{}; // the replaceable texture of the masts, the beam and the drops
int dragging{0}; // 1: the selected pole along the track, 2: the selected gantry along the track
glm::vec2 pressed{0.f};
glm::dvec3 drag_start{0.0}; // the point of the ground the drag started at
std::vector<std::pair<std::size_t, catenary::gantry>> drag_group; // the spans of the dragged gantry as they were when the drag started
int drag_undo{0};
glm::dvec3 drag_anchor{0.0}; // the first mast of the dragged gantry where the drag started
double drag_axis{0.0}; // its axis from the axis of the track then: the drag keeps it there ("first mast from the track") // the undo group of the drag, opened at its first move
std::vector<std::pair<std::size_t, catenary::suspension>> drag_drops; // the drops of the gantry, as they were
int gantry_selected{-1}; // the gantry the drops go to
std::string drop_choice; // the drop for the next cantilevers on a gantry; empty: the shortest one which fits
// the described catalogue (data/traction_catalog.txt): the group the user picks, the family and the card in it
// (empty / 0: chosen by the track, the stagger and the distance A)
std::string catalog_kind;
std::string catalog_group;
std::string catalog_family;
int catalog_card{0};
int page{0}; // the page of the catenary field of work: 0 poles, 1 gantries, 2 wires
int gantry_hang{0}; // the next cantilevers on the selected gantry: 0 on drops of the beam, 1 straight on its masts
};
catenary_tool m_catenary;
void catenary_scan();
bool catenary_spot_at(TTrack &Track, int const Path, glm::dvec3 const &Point, glm::dvec3 const &Cursor, int const Side, catenary_spot &Spot) const;
// a pole of this type can't stand there: its group doesn't stand by switches and a switch is by it
bool catenary_switch_error(std::string const &Pole, glm::dvec3 const &Foot, TTrack const *Track) const;
bool catenary_switch_error(std::size_t const Index) const; // of a placed pole, remembered until it moves or changes type
struct switch_check
{
glm::dvec3 foot{0.0};
std::string pole;
bool error{false};
};
mutable std::vector<std::pair<TTrack const *, int>> m_switch_paths; // the paths of switches, gathered again when the number of tracks changes
mutable std::size_t m_switch_tracks{static_cast<std::size_t>(-1)};
mutable std::map<std::size_t, switch_check> m_switch_checks;
// superelevation of the track at the spot (radians, unsigned) and the horizontal shift of the axis of a leaning
// pantograph at Height above the rail heads, towards the inside of the bend (table 23-0007, curve with superelevation)
static double catenary_cant(catenary_spot const &Spot);
static glm::dvec3 catenary_cant_shift(catenary_spot const &Spot, double Height);
void catenary_compose(catenary_spot &Spot, double const Clearance, double const Stagger, bool const Toward, std::string const &Pole = {}) const;
bool catenary_on_mast(catenary::gantry const &Gantry, double const Stagger, catenary_spot &Spot) const;
catenary_spot const *catenary_spot_of(std::size_t const Index);
int catenary_hit() const;
int catenary_span_hit() const;
void catenary_press(track_intent const &Intent);
void catenary_select(int const Index);
void catenary_apply(catenary_spot const &Spot);
void catenary_copy(std::vector<std::size_t> const &Targets);
bool catenary_propose_toward(bool &Toward);
void catenary_place(catenary_spot const &Spot);
// the second cantilever of an umbrella on the pole (or drop) of tool.umbrella_from, towards this track; false when it can't
// be put there (Spot.problem says why when Spot.card is 0)
bool catenary_umbrella_spot(TTrack &Track, int Path, catenary_spot &Spot);
void catenary_umbrella_place(catenary_spot const &Spot);
// the two cantilevers of the umbrella of this suspension become separate ones
void catenary_umbrella_break(std::size_t Index);
// a cantilever of an umbrella or of spread cantilevers: composed with its pole (and its console mount) where they are
void catenary_keep_pole(catenary_spot &Spot, catenary::suspension const &Current, double Stagger, bool Toward) const;
// spread cantilevers on the console of the pole: this one onto the first mount, a second one onto the other, same track
void catenary_spread_create(std::size_t Index);
// the pole of an umbrella or of spread cantilevers moved to Foot (rail heads): both cantilevers go with it
// (Panel: from the distance slider - the selected one takes the stagger of the panel, the slider is one undo group)
void catenary_pair_move(std::size_t Selected, glm::dvec3 const &Foot, bool Panel);
// a third cantilever on the pole of spread cantilevers, in the axis of the pole, to the same track (shared_pole)
void catenary_third_create(std::size_t Index);
// the span of a chain the spot falls in (in the plan: the wire point near the line of the span, between its ends)
struct chain_insert
{
int chain{-1};
std::size_t after{0}; // the new pole goes after this position of the chain
std::size_t from{0}, to{0}; // the suspensions at both ends of the span
};
bool catenary_insert_at(catenary_spot const &Spot, chain_insert &Insert) const;
// the next pole placed starts a new chain
void catenary_chain_end();
// the next poles placed go on from this pole: at the end (or the start) of its chain, or a new chain from it;
// false when the pole is in the middle of a chain
bool catenary_chain_continue(std::size_t Pole);
// the chain block of the poles page: what the next click does, end, anchor, go on from the selected pole
void render_catenary_chain_state();
void catenary_chain_apply();
void render_catenary_messenger_from(std::size_t Chain);
void catenary_connect(std::size_t const To);
int catenary_gantry_hit() const;
bool catenary_gantry_beam(std::size_t const Index, glm::dvec3 &From, glm::dvec3 &To, double &Bottom) const;
bool catenary_gantry_second(glm::dvec3 const &Point, glm::dvec3 &Second, std::string &Beam) const;
glm::dvec3 catenary_mast_point() const;
// the face of the mast from the axis of the nearest track (within 20 m); false: no track near
bool catenary_mast_face(std::string const &Mast, glm::dvec3 const &Point, double &Face, glm::dvec2 &Away) const;
// the selected gantry (all its spans and what hangs on them) moved in the plan
void catenary_gantry_shift(glm::dvec2 const &Shift);
// the place of the first mast at Axis from the axis of the track nearest to Point, on the side of Point; false: no track near
bool catenary_along_track(glm::dvec3 const &Point, double const Axis, glm::dvec3 &Place) const;
bool catenary_on_gantry(catenary_spot &Spot);
void catenary_drag_update();
// the selected gantry anew: another beam, masts, turned around its middle (degrees), or square to the nearest track
void catenary_gantry_reshape(std::string const &Beam, double const Turn, bool const Square);
void catenary_release();
void catenary_gantry_hangers(std::size_t const Gantry, std::vector<std::pair<std::size_t, catenary::suspension>> &Hangers) const;
bool catenary_delete_selected();
bool catenary_undo();
void render_catenary_ui();
bool render_catenary_catalog_ui();
void render_catenary_chain_ui();
// the anchors at both ends of the selected chain: add, settings, remove
void render_catenary_anchor_ui(std::size_t Chain);
// the anchor of the chain at its start (Start) or end; catenary_anchor_size() when none
std::size_t catenary_anchor_of(catenary::chain const &Chain, std::string const &File, bool Start, bool Contact = false) const;
// anchors the start or the end of the chain: on a new pole past the last one, or (On >= 0) on that existing pole
bool catenary_anchor_create(std::size_t Chain, bool Start, int On, bool Contact = false);
// removes the anchor at the start or the end of the chain (one undo step with the chain)
void catenary_anchor_delete(std::size_t Chain, bool Start, bool Contact = false);
// the anchor whose tensioning device is under the cursor; -1 if none
int catenary_anchor_hit() const;
// shows the chain in the panel as a click on its span does
void catenary_chain_show(int Chain);
void render_catenary_cantilever_ui();
void render_catenary_wires_ui();
void catenary_choose_page(int const Page);
void draw_catenary_overlay() const;
std::string catenary_intent_label(catenary_spot const &Spot) const;
// the hooks of editor_mode
bool catenary_intent(track_intent &Intent);
std::string catenary_mode_name() const;
void catenary_key_hints(std::vector<key_hint> &Hints) const;
bool catenary_on_key(int const Key, int const Action, int const Mods);
};

View File

@@ -348,9 +348,9 @@ void editor_mode::restore_track_snapshot(EditorSnapshot const &Snapshot, std::ve
profile_restore();
}
std::array<editor_mode::track_mode, 11> const &editor_mode::track_modes()
std::array<editor_mode::track_mode, 12> const &editor_mode::track_modes()
{
static std::array<track_mode, 11> const modes{{
static std::array<track_mode, 12> const modes{{
{"Select", "Esc", track_tab::path, "LMB on a path selects it: its points, control vectors and parameters", editor_icons::icon::select, 0},
{"Lay track", "L", track_tab::lay, "Lays new track through the clicked points, on the ground or from a free end of a track", editor_icons::icon::lay_track, 0},
{"Plan", "G", track_tab::straights, "The plan of the line laid: LMB on a straight drags its ends or the middle, on a curve its vertex and radius. C works too", editor_icons::icon::curve, 0},
@@ -358,6 +358,7 @@ std::array<editor_mode::track_mode, 11> const &editor_mode::track_modes()
{"Parallel", "N", track_tab::parallel, "A second track beside a track: LMB on it, then the side and the spacing", editor_icons::icon::parallel, 0},
{"Turntable", "U", track_tab::turntable, "Places a turntable, leads tracks out of it and fits the tracks around to it", editor_icons::icon::turntable, 1},
{"Signals", "H", track_tab::signals, "Signals by the track, from the templates of the scenery, with the event the train reads them by", editor_icons::icon::signal, 1},
{"Catenary", "F6", track_tab::catenary, "Poles with cantilevers by the track, the card chosen from the catalogue by the distance of the pole; the spans checked against the catalogue", editor_icons::icon::catenary, 1},
{"Objects", "B", track_tab::lineside, "Along the track: hectometre posts, fouling point markers, trainsets", editor_icons::icon::objects, 1},
{"Profile", "P", track_tab::profile, "Vertical profile (grade line) along the line", editor_icons::icon::profile, 2},
{"Speed", "V", track_tab::speed, "Speed limits of the paths against the speed their geometry allows", editor_icons::icon::speed, 2},
@@ -416,6 +417,9 @@ void editor_mode::show_track_tab(track_tab const Wanted)
case track_tab::signals:
signal_refresh(true);
break;
case track_tab::catenary:
catenary_scan();
break;
default:
break;
}
@@ -491,6 +495,7 @@ std::string editor_mode::track_mode_name() const
case track_tab::parallel: return STR_C("Parallel track");
case track_tab::signals: return STR_C("Signal");
case track_tab::turntable: return STR_C("Turntable");
case track_tab::catenary: return catenary_mode_name();
default: return STR_C("Select");
}
}
@@ -511,6 +516,7 @@ void editor_mode::render_track_inspector()
m_vehicle.open = false;
}
ui()->set_track(m_track_window_open);
ui()->set_track_title(m_track_tab == track_tab::catenary ? STR("Catenary") : std::string{});
if (false == m_track_window_open)
{
// a closed window takes its mode with it
@@ -549,9 +555,13 @@ void editor_mode::render_track_tool_options()
{
auto *track{selected_track()};
// the tools themselves are in the toolbar
render_track_search();
ImGui::Spacing();
render_track_guide();
if (m_track_tab != track_tab::catenary)
{
// the catenary is a field of work of its own: no search of the paths there
render_track_search();
ImGui::Spacing();
render_track_guide();
}
// the texts of the tools go on to the next line at the edge of the panel, none of them makes it scroll sideways
ImGui::PushTextWrapPos(0.0f);
switch (m_track_tab)
@@ -584,6 +594,7 @@ void editor_mode::render_track_tool_options()
break;
case track_tab::lineside: render_lineside_ui(); break;
case track_tab::signals: render_signal_ui(); break;
case track_tab::catenary: render_catenary_ui(); break;
case track_tab::turntable: render_turntable_ui(); break;
default:
render_track_set_ui();
@@ -1591,6 +1602,10 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
hints = {{"LMB around it", "leads a track out"}, {"LMB on a free end", "fits that track to the turntable"}};
else if (m_track_window_open && m_track_tab == track_tab::turntable)
hints = {{"LMB", "places a new turntable"}};
else if (m_track_window_open && m_track_tab == track_tab::catenary)
{
catenary_key_hints(hints);
}
else if (m_track_window_open && m_track_tab == track_tab::joints)
hints = {{"LMB on a row", "show the place"}, {"Fix", "joins the ends, aligns them"}};
else if (m_track_window_open && m_track_tab == track_tab::infra)
@@ -2038,6 +2053,8 @@ void editor_mode::draw_build_overlay() const
auto const in_reach = [&](glm::dvec3 const &Point) { return Global.EditorOrtho || glm::distance(Point, eye) <= kDotsRange; };
for (auto const *track : simulation::Paths.sequence())
{
if (m_track_tab == track_tab::catenary)
break;
if (track == nullptr || track->m_editorremoved || track->m_paths.empty())
continue;
for (auto const &path : track->m_paths)

View File

@@ -44,6 +44,7 @@ using geometry::plan_of;
float const kHoverPixels{12.0f};
float const kFarHoverPixels{16.0f};
double const kLinesFrom{0.5};
double const kCatenaryHoverRange{1500.0}; // m from the camera: the paths the catenary looks at under the cursor
double const kHoverLabelDelay{0.35}; // s the cursor rests on a path before its data shows up
double const kFocusDistance{25.0};
auto const kContextClick{std::chrono::milliseconds(350)};
@@ -130,7 +131,9 @@ editor_mode::track_hover editor_mode::track_under_cursor(TTrack const *Only, flo
for (int i = 0; i < static_cast<int>(track->m_paths.size()); ++i)
{
bezier const curve{track->m_paths[i]};
if (cull && false == plan_near(curve, plan_of(origin), reach))
// the catenary works close to the camera: the paths far away aren't looked at (in a big scenery they cost a lot)
if ((cull && false == plan_near(curve, plan_of(origin), reach)) ||
(m_track_tab == track_tab::catenary && glm::distance(curve.point(0.5), glm::dvec3{Global.pCamera.Pos}) > curve.plan_length() * 0.5 + kCatenaryHoverRange))
continue;
auto const count{std::clamp(static_cast<int>(curve.plan_length() / 3.0), 6, 64)};
auto previous{curve.point(0.0)};
@@ -238,7 +241,7 @@ void editor_mode::draw_track_hover() const
bool editor_mode::track_gesture_on() const
{
return mouseHold || m_track_set_using || m_track_box.active || m_input.mouse.button(GLFW_MOUSE_BUTTON_RIGHT) == GLFW_PRESS || m_track_gizmo_using || m_route_dragging || m_extend.active || m_switch.placing || m_straights.dragging || m_straights.tool_mouse ||
m_point_drag.active || m_handle_drag.active || m_sweep.dragging || m_turntable.placing || m_signal.placing || m_signal.moving || m_switch.slide.active || ImGuizmo::IsUsing();
m_point_drag.active || m_handle_drag.active || m_sweep.dragging || m_turntable.placing || m_signal.placing || m_signal.moving || m_switch.slide.active || catenary_dragging() || ImGuizmo::IsUsing();
}
editor_mode::track_intent editor_mode::track_intent_at(int const Mods)
@@ -286,6 +289,11 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods)
}
if (m_track_tab == track_tab::turntable)
return turntable_intent(ground, shift);
if (m_track_tab == track_tab::catenary)
{
catenary_intent(intent);
return intent;
}
if (m_track_tab == track_tab::signals)
{
if (auto const hit{signal_hit()}; hit >= 0)
@@ -583,6 +591,7 @@ void editor_mode::track_press(track_intent const &Intent)
case kind::turntable_fit: turntable_press(Intent); return;
case kind::signal_place:
case kind::signal_move: signal_press(Intent); return;
case kind::catenary: catenary_press(Intent); return;
case kind::switch_slide:
if (Intent.track != nullptr)
{

View File

@@ -202,6 +202,15 @@ void editor_mode::work_area_tools(std::vector<tool_button> &Buttons)
Buttons.back().accent = family_colour(2);
break;
}
case work_area::catenary:
{
// the pages of the catenary: poles with cantilevers, gantries, the wires of the chains
auto const page{catenary_page()};
Buttons.push_back({icon::catenary, STR_C("Poles"), nullptr, STR_C("Poles with cantilevers: LMB by a track places one, LMB on a pole selects it"), page == 0, [this]() { catenary_choose_page(0); }});
Buttons.push_back({icon::gantry, STR_C("Gantries"), nullptr, STR_C("Gantries: masts and beams, the cantilevers on their drops and masts"), page == 1, [this]() { catenary_choose_page(1); }});
Buttons.push_back({icon::wires, STR_C("Wires"), nullptr, STR_C("The chains: the type of the network, the wires, the spans between the poles"), page == 2, [this]() { catenary_choose_page(2); }});
break;
}
case work_area::roads:
{
auto &tool{m_roadtool};

View File

@@ -740,7 +740,7 @@ void editor_ui::render_tool_options()
name = STR_C("Copy to bank");
break;
case nodebank_panel::TRACK:
name = STR_C("Tracks");
name = m_tracktitle.empty() ? STR_C("Tracks") : m_tracktitle.c_str();
break;
default:
break;

View File

@@ -55,6 +55,8 @@ class editor_ui : public ui_layer
m_filloptions = std::move(Renderer);
}
// the open track window takes the mouse over, whichever toolset tab is chosen
// the title of the tool options of the tracks; empty: "Tracks"
void set_track_title(std::string Title) { m_tracktitle = std::move(Title); }
void set_track(bool const Track)
{
m_track = Track;
@@ -218,6 +220,7 @@ class editor_ui : public ui_layer
bool m_sceneopen{true};
std::function<void(scene::basic_node *, bool)> m_sceneselect;
std::function<void()> m_tracktools;
std::string m_tracktitle;
std::function<void()> m_trackselection;
int m_toolmode{-1}; // edit mode or field of work the tool options were drawn for the last time
workspace m_workspace{workspace::none};