17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-05 19:29:11 +01:00

Sieć trakcyjna: kotwienia, kotwienie środkowe, parasole i wysięgniki rozstawione, konsole 4890/4690

Sam kod exe z gałęzi siec-trakcyjna od poprzedniego commita (bez testów, notatek i danych z tests/catenary).
Dane (data/traction_*.txt) leżą poza repozytorium.

- kotwienia ciężarowe i stałe (0404/0405, przewody zakotwione osobno, sieci bez własnych danych: network <symbol> as <kod>)
- kotwienie środkowe 0406/0408/0418/0416: lina, V i zaciski na linie nośnej liczonej jak przęsło, obejma 4160 na teownikach
- parasole i wysięgniki rozstawione na konsolach 4890, wysięgi pomocnicze 0167 na belkach 4690, trzeci wysięgnik w osi słupa,
  lina nośna z innego wysięgnika (messenger_from)
- przęsła w nietypowych sytuacjach nie przechodzą na stary wygląd (bez Y, bez wieszaków, lina nad przewodem)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GfTogWDsfCE8RGFGvm2eyp
This commit is contained in:
Claude
2026-10-26 01:12:06 +01:00
parent f30fefd91c
commit 619f048f31
18 changed files with 4237 additions and 285 deletions

View File

@@ -140,6 +140,9 @@ TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) {
std::string token { parser->getToken<std::string>() };
std::string catenarytype;
double catenarytension[ 2 ] { 0.0, 0.0 };
glm::dvec3 catenaryrope2[ 2 ];
bool catenaryrope2set[ 2 ] { false, false };
while( false == token.empty()
&& token != "endtraction" ) {
@@ -152,11 +155,24 @@ TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) {
// symbol typu sieci, np. YwsC120-2C; starsze wersje symulatora pomijają ten dopisek
catenarytype = parser->getToken<std::string>( false );
}
else if( token == "rope2" ) {
// koniec drugiej liny nośnej przy kotwieniu: "rope2 start|end x y z"
auto const which { parser->getToken<std::string>() };
auto const point { LoadPoint( *parser ) + pOrigin };
auto const end { which == "end" ? 1 : 0 };
catenaryrope2[ end ] = point;
catenaryrope2set[ end ] = true;
}
else if( token == "tension" ) {
// naciąg liny nośnej i przewodów jezdnych w daN inny niż w typie sieci (brakujące ciężary w kotwieniu)
parser->getTokens( 2 );
*parser >> catenarytension[ 0 ] >> catenarytension[ 1 ];
}
token = parser->getToken<std::string>();
}
Init(); // przeliczenie parametrów
catenary_span_assign( *this, catenarytype );
catenary_span_assign( *this, catenarytype, catenarytension[ 0 ], catenarytension[ 1 ], catenaryrope2, catenaryrope2set );
// calculate traction location
location( glm::mix( pPoint2, pPoint1, 0.5 ) );

View File

@@ -280,6 +280,7 @@ void cantilever_catalog::parse( std::vector<std::string> const &Tokens, std::str
else if( key == "top" ) { number( pole.top, good ); }
else if( key == "length" ) { number( pole.length, good ); }
else if( key == "depth" ) { number( pole.depth_top, good ); number( pole.depth_base, good ); }
else if( key == "width" ) { number( pole.width, good ); }
else if( key == "mount_max" ) { number( pole.mount_max, good ); }
else if( key == "mount_min" ) { number( pole.mount_min, good ); pole.has_mount_min = true; }
else if( key == "turn" ) { number( pole.turn, good ); }
@@ -287,10 +288,13 @@ void cantilever_catalog::parse( std::vector<std::string> const &Tokens, std::str
else if( key == "strut" ) { text( pole.strut ); }
else if( key == "stay" ) { text( pole.stay ); }
else if( key == "steady" ) { text( pole.steady ); }
else if( key == "console" ) { text( pole.console ); }
else if( key == "steady_console" ) { text( pole.steady_console ); }
else { report( entry, "unknown keyword \"" + key + "\"" ); good = false; --index; }
}
else {
if( key == "back" ) { text( mount.back ); }
else if( key == "console" ) { mount.console = true; number( mount.console_x[ 0 ], good ); number( mount.console_x[ 1 ], good ); number( mount.console_face, good ); }
else if( key == "hinge" ) { text( mount.hinge ); number( mount.hinge_eye[ 0 ], good ); number( mount.hinge_eye[ 1 ], good ); }
else if( key == "rod" ) { text( mount.rod ); }
else if( key == "twin" ) { text( mount.twin ); }
@@ -2071,6 +2075,20 @@ bool solve_cantilever_on_pole( cantilever_catalog const &Catalog, cantilever_sch
return solve_cantilever_on_pole( Catalog, Scheme, Pole, Distance, RailHead, Messenger, Overrides, cantilever_umbrella(), Result );
}
void spread_mounts( cantilever_pole const &Pole, cantilever_result const &First, cantilever_result const &Second,
cantilever_umbrella &FirstSide, cantilever_umbrella &SecondSide ) {
FirstSide.shared.clear();
SecondSide.shared.clear();
for( auto const *role : { "stay", "strut", "steady" } ) {
auto const a { First.values.find( std::string( "mount." ) + role ) }, b { Second.values.find( std::string( "mount." ) + role ) };
if( a == First.values.end() || b == Second.values.end() ) { continue; }
auto shared { 0.5 * ( a->second + b->second ) };
if( Pole.mount_max != 0.0 ) { shared = std::min( shared, std::max( Pole.mount_max, std::min( a->second, b->second ) ) ); }
FirstSide.shared[ role ] = SecondSide.shared[ role ] = shared;
}
}
void umbrella_mounts( cantilever_catalog const &Catalog, cantilever_pole const &Pole, cantilever_result const &First, cantilever_result const &Second,
cantilever_umbrella &FirstSide, cantilever_umbrella &SecondSide ) {
@@ -2142,17 +2160,40 @@ bool solve_on_pole_once( cantilever_catalog const &Catalog, cantilever_scheme co
if( mount == nullptr ) { fail( std::string( Use ) + " mount \"" + Name + "\" isn't in the catalogue" ); }
return mount;
};
auto const *strut { mountpart( Pole.strut, "strut" ) };
auto const *stay { mountpart( Pole.stay, "stay" ) };
auto const *steady { Scheme.steady_mounted ? mountpart( Pole.steady, "steady arm" ) : nullptr };
// wysięgniki rozstawione: mocowania -06 tej samej karty (4610-06, 4620-06, 4630-06) na konsoli słupa (4890); rozstawione
// wysięgi pomocnicze (0167, bez ukośnika i odciągu) na belce 4690 (steady_console, karta 33-0519), gdy słup ją ma
auto const onconsole = [ & ]( std::string const &Name ) {
if( false == Umbrella.spread || Name.empty() ) { return Name; }
return Name.substr( 0, Name.find( '-' ) ) + "-06";
};
auto const steadyonly { Scheme.roles.count( "strut" ) == 0 && Scheme.roles.count( "stay" ) == 0 };
std::map<std::string, cantilever_mount const *> consoles;
if( Umbrella.spread ) {
for( auto const *role : { "stay", "strut", "steady" } ) {
if( ( std::string( role ) != "steady" && steadyonly ) || ( std::string( role ) == "steady" && false == Scheme.steady_mounted ) ) { continue; }
// sam wysięg pomocniczy (0167) na belce 4690; wysięg wysięgnika trzyzaczepowego na tej samej konsoli 4890, co reszta
auto const &name { steadyonly && false == Pole.steady_console.empty() ? Pole.steady_console : Pole.console };
auto const *found { name.empty() ? nullptr : Catalog.mount( name ) };
if( found == nullptr || false == found->console ) {
return fail( name.empty() ? "the pole has no console for spread cantilevers (\"console\" / \"steady_console\" in its entry)" : "console \"" + name + "\" isn't in the catalogue as a console" );
}
consoles[ role ] = found;
}
}
auto const *strut { mountpart( onconsole( Pole.strut ), "strut" ) };
auto const *stay { mountpart( onconsole( Pole.stay ), "stay" ) };
auto const *steady { Scheme.steady_mounted ? mountpart( onconsole( Pole.steady ), "steady arm" ) : nullptr };
if( strut == nullptr || stay == nullptr || ( Scheme.steady_mounted && steady == nullptr ) ) { return false; }
// oś słupa wzdłuż toru: przy wysięgnikach rozstawionych przesunięta od płaszczyzny wysięgnika
auto const polex { Umbrella.spread ? Umbrella.console_x : 0.0 };
std::vector<cantilever_placement> poleparts;
auto const add = [ & ]( std::string const &Role, std::string const &Part, std::string const &Model, double const Y, double const Z ) {
auto const add = [ & ]( std::string const &Role, std::string const &Part, std::string const &Model, double const Y, double const Z, double const X = 0.0 ) {
cantilever_placement placement;
placement.role = Role;
placement.part = Part;
placement.model = Model;
placement.position[ 0 ] = X;
placement.position[ 1 ] = Y;
placement.position[ 2 ] = Z;
poleparts.push_back( placement );
@@ -2163,15 +2204,25 @@ bool solve_on_pole_once( cantilever_catalog const &Catalog, cantilever_scheme co
auto const fix = [ & ]( std::string const &Role, cantilever_mount const &Mount, double Bolts ) {
// parasol: mocowanie dwustronne na wspólnej wysokości, bez tyłu; po stronie z nakrętkami jego połówka "twin"
auto const shared { Umbrella.shared.find( Role ) };
auto const twin { shared != Umbrella.shared.end() && false == Mount.twin.empty() };
if( twin ) { Bolts = RailHead + shared->second; }
auto const twin { false == Umbrella.spread && shared != Umbrella.shared.end() && false == Mount.twin.empty() };
if( twin || ( Umbrella.spread && shared != Umbrella.shared.end() ) ) { Bolts = RailHead + shared->second; }
auto const height { Bolts - RailHead };
heights[ Role ] = height;
if( height > Pole.top ) { notes.push_back( Role + " mount " + to_text( height - Pole.top ) + " m above the top of the pole" ); }
auto const depth { Pole.depth( height ) };
auto const face { Distance - 0.5 * depth };
auto face { Distance - 0.5 * depth };
auto const found { consoles.find( Role ) };
auto const *console { found != consoles.end() ? found->second : nullptr };
if( console != nullptr ) {
// konsola na słupie (jedna na rolę, stawia ją jedna strona), mocowanie na jej punkcie, bliżej toru o jej grubość
if( Umbrella.console_parts ) {
add( Role + "_console", console->name, "", face, Bolts, polex );
if( false == console->back.empty() ) { add( Role + "_console_back", console->back, "", face + depth, Bolts, polex ); }
}
face += console->console_face;
}
add( Role + "_mount", ( twin && Umbrella.nuts.count( Role ) ) ? Mount.twin : Mount.name, "", face, Bolts );
if( false == twin && false == Mount.back.empty() ) { add( Role + "_back", Mount.back, "", face + depth, Bolts ); }
if( false == twin && console == nullptr && false == Mount.back.empty() ) { add( Role + "_back", Mount.back, "", face + depth, Bolts ); }
if( false == Mount.hinge.empty() ) { add( Role + "_hinge", Mount.hinge, "", face + Mount.point[ 0 ], Bolts + Mount.point[ 1 ] ); }
if( false == Mount.rod.empty() ) { add( Role + "_rod", Mount.rod, "", face + Mount.point[ 0 ], Bolts + Mount.point[ 1 ] ); }
return cantilever_point { face + Mount.attach_y(), Bolts + Mount.attach_z() };
@@ -2179,7 +2230,7 @@ bool solve_on_pole_once( cantilever_catalog const &Catalog, cantilever_scheme co
// słup bramki stawia bramka
if( Umbrella.pole && false == Pole.gantry ) {
add( "pole", Pole.name, Pole.model, Distance, RailHead );
add( "pole", Pole.name, Pole.model, Distance, RailHead, polex );
if( Pole.turn != 0.0 ) {
// model obrócony wokół pionu (zwis na bramce: profil szerszym bokiem wzdłuż toru)
auto const angle { Pole.turn * 3.14159265358979323846 / 180.0 };
@@ -2188,7 +2239,7 @@ bool solve_on_pole_once( cantilever_catalog const &Catalog, cantilever_scheme co
std::copy( std::begin( turned ), std::end( turned ), poleparts.back().rotation );
}
// fundament ma to samo zero co słup (oś słupa na wysokości główki szyny)
if( false == Pole.foundation.empty() ) { add( "foundation", Pole.name + "_foundation", Pole.foundation, Distance, RailHead ); }
if( false == Pole.foundation.empty() ) { add( "foundation", Pole.name + "_foundation", Pole.foundation, Distance, RailHead, polex ); }
}
auto const raise { Messenger.z - Scheme.height };
auto staybolts { Scheme.mount_stay + raise - stay->attach_z() };

View File

@@ -114,6 +114,7 @@ struct cantilever_pole {
double length { 0.0 }; // długość, na której biegnie zbieżność, liczona od góry
double depth_top { 0.0 }; // wymiar od toru w głąb (prostopadle do toru) u góry
double depth_base { 0.0 }; // to samo na końcu długości zbieżności
double width { 0.0 }; // wzdłuż toru (słup kratowy 161x: wysokość ceownika, karta 33-1611-E wymiar a); 0 = nieznana
double mount_max { 0.0 }; // najwyżej położona oś śrub mocowania; 0 = bez ograniczenia (zwis na bramce: ujemna, pod dźwigarem)
double mount_min { 0.0 }; // najniżej położona oś śrub mocowania (zwis: nad jego końcem); tylko gdy has_mount_min
bool has_mount_min { false };
@@ -122,6 +123,8 @@ struct cantilever_pole {
std::string strut; // mocowanie ukośnika (część z punktem axis i zawiasem)
std::string stay; // mocowanie izolatora odciągu
std::string steady; // mocowanie izolatora wysięgu pomocniczego (schematy z trzema zaczepami)
std::string console; // konsola wysięgników rozstawionych (wpis "mount ... console"); pusta = słup ich nie nosi
std::string steady_console; // belka 4690 rozstawionych wysięgów pomocniczych (karta 33-0519, wysięgi 0167); mocowanie wysięgu na niej
// słup bramki z wysięgnikiem (zrobiony z wpisu "mast" z mocowaniami): model stawia bramka, nie wysięgnik; mount_max pod
// dźwigarem bramki liczy sceneria wg jej poziomu (mast_low/mast_high minus below_beam), tu - pod dolnym mocowaniem
bool gantry { false };
@@ -140,6 +143,11 @@ struct cantilever_mount {
std::string hinge; // zawias obrotowy stawiany w punkcie axis (wspornik ukośnika)
std::string rod; // pręt z uchem stawiany w punkcie eye (uchwyt odciągu)
std::string twin; // połówka z nakrętkami mocowania dwustronnego (parasol); pusta = mocowanie bez regulacji, parasol z dwóch niezależnych
// konsola wysięgników rozstawionych (4890, karty 0517/0518): dwa punkty mocowań w X (wzdłuż toru) i ich odsunięcie od
// płaszczyzny słupa (Y, ujemne - w stronę toru); na konsoli mocowania -06 (4610-06, 4620-06, 4630-06)
bool console { false };
double console_x[ 2 ] { 0.0, 0.0 };
double console_face { 0.0 };
double point[ 2 ] { 0.0, 0.0 }; // Y, Z punktu axis (gdy jest zawias) albo eye
double hinge_eye[ 2 ] { 0.0, 0.0 }; // Y, Z ucha zawiasu względem jego zera
// punkt zaczepu izolatora względem zera mocowania
@@ -293,6 +301,11 @@ struct cantilever_umbrella {
std::map<std::string, double> shared; // rola mocowania -> wspólna wysokość osi śrub nad główką szyny
std::set<std::string> nuts; // role mocowań, które po tej stronie są połówką z nakrętkami
bool pole { true }; // czy stawiać słup (stawia go tylko jedna strona)
// wysięgniki rozstawione: oba po tej samej stronie słupa, na konsolach (konsola słupa), mocowania -06 na wspólnych
// wysokościach z shared; płaszczyzna wysięgnika przechodzi przez punkt mocowania na konsoli, oś słupa leży w X = ConsoleX
bool spread { false };
double console_x { 0.0 };
bool console_parts { true }; // czy stawiać konsole (stawia je jedna strona)
};
bool solve_cantilever_on_pole( cantilever_catalog const &Catalog, cantilever_scheme const &Scheme, cantilever_pole const &Pole,
double Distance, double RailHead, cantilever_point const &Messenger, std::map<std::string, std::string> const &Overrides,
@@ -306,6 +319,11 @@ bool solve_cantilever_on_pole( cantilever_catalog const &Catalog, cantilever_sch
void umbrella_mounts( cantilever_catalog const &Catalog, cantilever_pole const &Pole, cantilever_result const &First, cantilever_result const &Second,
cantilever_umbrella &FirstSide, cantilever_umbrella &SecondSide );
// wspólne wysokości mocowań wysięgników rozstawionych: każda rola obu wysięgników na jednej konsoli, na średniej wysokości
// (nie ponad mount_max słupa). Wypełnia shared obu stron
void spread_mounts( cantilever_pole const &Pole, cantilever_result const &First, cantilever_result const &Second,
cantilever_umbrella &FirstSide, cantilever_umbrella &SecondSide );
// część ustawiona w układzie scenerii symulatora (X, Y w górę, Z), względem punktu osi przewodów jezdnych
struct cantilever_scene_part {
double offset[ 3 ] { 0.0, 0.0, 0.0 }; // przesunięcie od punktu osi przewodów jezdnych

View File

@@ -99,7 +99,10 @@ void type_registry::load_tables() {
double mass { 0.0 }, diameter { 0.0 };
stream >> mass >> diameter;
if( stream.fail() || mass <= 0.0 ) { report( "Catenary: bad wire entry in \"" + filename + "\" (line " + std::to_string( linenumber ) + ")" ); continue; }
m_wires[ key ] = { mass, diameter * 0.001 };
// dalej opcjonalnie: przekrój mm² i moduł sprężystości GPa
double area { 0.0 }, modulus { 0.0 };
if( stream >> area ) { stream >> modulus; }
m_wires[ key ] = { mass, diameter * 0.001, std::max( 0.0, area ) * 1e-6, std::max( 0.0, modulus ) * 1e9 };
}
else if( kind == "spacing" ) {
int span { 0 };
@@ -267,25 +270,34 @@ bool solve_span_points( network_definition const &Definition, std::vector<double
input.y_at_start = Ystart;
input.y_at_end = Yend;
span_result result;
try {
// bez rozstawu wieszaków: odcinek do kotwienia, same liny
result = ( Hangergaps.empty() ?
solve_free_span( type, length, input.height_start, input.height_end ) :
solve_span( type, input, 4.0 ) );
}
catch( std::exception const &exception ) {
Error = exception.what();
return false;
}
// lina poniżej przewodu jezdnego: przęsło za długie albo za mała wysokość konstrukcyjna dla tego typu sieci
for( auto const *line : { &result.messenger, &result.y_start, &result.y_end } ) {
for( auto const &point : *line ) {
if( point.y - ( type.messenger_count > 1 ? type.messenger_stack : 0.0 ) < -0.01 ) {
Error = "messenger wire would hang below the contact wire (span too long or suspension too low for this network type)";
return false;
auto solved { false };
// nietypowe przęsła (niska wysokość konstrukcyjna jednego końca, naciąg liny obniżony brakującymi ciężarami poniżej naciągu
// linki Y): zamiast rysowania po staremu najpierw bez linek Y, potem jako odcinek bez wieszaków
for( int attempt = 0; attempt < 3 && false == solved; ++attempt ) {
try {
if( attempt == 2 || Hangergaps.empty() ) { result = solve_free_span( type, length, input.height_start, input.height_end ); }
else {
if( attempt == 1 ) { input.y_at_start = input.y_at_end = false; }
result = solve_span( type, input, 4.0 );
}
solved = true;
}
catch( std::exception const &exception ) {
Error = exception.what();
}
}
if( false == solved ) { return false; }
// lina poniżej przewodu jezdnego (przęsło za długie albo za mała wysokość konstrukcyjna dla tego typu sieci): podciągnięta
// nad przewód, wieszaki skrócone - przęsło wygląda dziwnie, ale się rysuje
// (końce zostają tam, gdzie są punkty przęsła: najniżej na prostej między nimi)
auto const lowest { ( type.messenger_count > 1 ? type.messenger_stack : 0.0 ) + 0.02 };
for( auto *line : { &result.messenger, &result.y_start, &result.y_end } ) {
for( auto &point : *line ) {
auto const chord { input.height_start + ( input.height_end - input.height_start ) * std::clamp( point.x / length, 0.0, 1.0 ) };
point.y = std::max( point.y, std::min( lowest, chord ) );
}
}
for( auto &hanger : result.hangers ) { hanger.length = std::max( hanger.length, 0.0 ); }
Solution.length = length;
Solution.result = std::move( result );
return true;
@@ -435,10 +447,27 @@ void build_geometry( network_definition const &Definition, span_solution const &
if( type.messenger_count > 1 ) {
std::vector<vec3> middle;
if( false == start.empty() ) { middle.push_back( start.back() ); }
// końce dolnej liny podane osobno (kotwienie): przesunięcie rozłożone liniowo wzdłuż przęsła
vec3 shiftstart { 0.0, 0.0, 0.0 }, shiftend { 0.0, 0.0, 0.0 };
if( Points.messenger2_start_set && false == result.messenger.empty() ) {
auto const from { rope( result.messenger.front().x, result.messenger.front().y - type.messenger_stack ) };
shiftstart = vec3 { Points.messenger2_start.x - from.x, Points.messenger2_start.y - from.y, Points.messenger2_start.z - from.z };
}
if( Points.messenger2_end_set && false == result.messenger.empty() ) {
auto const to { rope( result.messenger.back().x, result.messenger.back().y - type.messenger_stack ) };
shiftend = vec3 { Points.messenger2_end.x - to.x, Points.messenger2_end.y - to.y, Points.messenger2_end.z - to.z };
}
for( auto const &point : result.messenger ) {
auto const instart { reachstart > 0.0 && point.x < reachstart + 1e-6 };
auto const inend { reachend > 0.0 && point.x > length - reachend - 1e-6 };
if( false == instart && false == inend ) { middle.push_back( rope( point.x, point.y - type.messenger_stack ) ); }
if( false == instart && false == inend ) {
auto at { rope( point.x, point.y - type.messenger_stack ) };
auto const t { length > 1e-9 ? point.x / length : 0.0 };
at.x += shiftstart.x * ( 1.0 - t ) + shiftend.x * t;
at.y += shiftstart.y * ( 1.0 - t ) + shiftend.y * t;
at.z += shiftstart.z * ( 1.0 - t ) + shiftend.z * t;
middle.push_back( at );
}
}
if( false == end.empty() ) { middle.push_back( end.front() ); }
polyline( middle, element_kind::messenger );

View File

@@ -16,6 +16,8 @@ namespace catenary {
struct wire_data {
double mass { 0.0 }; // kg/m
double diameter { 0.0 }; // m
double area { 0.0 }; // m², przekrój czynny (do wydłużeń sprężystych); 0 = nieznany
double modulus { 0.0 }; // Pa, moduł sprężystości przewodu (liny skręcane: niższy niż drutu); 0 = nieznany
};
// pełny opis typu sieci: dane dla solvera i dane potrzebne tylko do rysowania
@@ -67,6 +69,9 @@ struct vec3 { double x, y, z; };
struct span_points {
vec3 contact_start, contact_end;
vec3 messenger_start, messenger_end;
// druga (dolna) lina nośna kończy się gdzie indziej niż o odstęp lin pod pierwszą (uchwyt krańcowy w kotwieniu z dwiema liniami)
bool messenger2_start_set { false }, messenger2_end_set { false };
vec3 messenger2_start, messenger2_end;
};
// policzone przęsło

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,7 @@ void catenary_scene_data( std::string const &Directory );
// Zmiany trafiają do plików sieci dopiero przy catenary_scene_save().
#include <vector>
#include <utility>
#include "world/catenary_suspension.h"
#include "world/catenary_cantilever.h"
#include "world/catenary_layout.h"
@@ -87,6 +88,9 @@ std::size_t catenary_chain_add( std::string const &File, catenary::chain const &
// zmienia łańcuch i robi jego przęsła od nowa (mniej niż dwa podwieszenia = łańcuch usunięty)
bool catenary_chain_update( std::size_t Index, catenary::chain const &Data );
void catenary_chain_remove( std::size_t Index );
// kotwienie środkowe łańcucha: liczba wstawionych części (na słupach sąsiednich podwieszeń) i trójkątów brył (lina, V, zaciski);
// false, gdy łańcuch go nie ma albo się nie złożyło
bool catenary_chain_middle( std::size_t Index, std::size_t &Parts, std::size_t &Triangles );
// łańcuch, do którego należy podwieszenie, i jego miejsce w łańcuchu; -1 gdy żaden
int catenary_chain_of( std::size_t Suspension, std::size_t &Position );
@@ -97,6 +101,23 @@ std::string catenary_anchor_file( std::size_t Index );
std::size_t catenary_anchor_parts( std::size_t Index );
// wynik solvera ostatniego złożenia (b, s, ciężary, naciągi, uwagi); nullptr gdy usunięte
catenary::tensioner_result const *catenary_anchor_result( std::size_t Index );
// obrys złożonego urządzenia dla edytora (współrzędne scenerii): czoło słupa i oś rolki ruchomej na wysokości rolek, góra i spód
// kolumny ciężarów; false gdy usunięte albo niezłożone
bool catenary_anchor_shape( std::size_t Index, double ( &Shape )[ 4 ][ 3 ] );
// karty urządzeń kotwienia do wyboru (bez odmian dla typów sieci, np. 0461-2x2): nazwa, kotwienie stałe, rolki szeregowe
struct catenary_anchor_card {
std::string card;
bool fixed { false };
bool series { false };
bool split { false }; // dwa stosy (lina i przewody osobno)
std::string title; // opis z danych (odmiany pokazane osobno, np. 0461-2x2)
};
// Only: puste - kotwienia całości; "messenger" / "contact" - samej liny / samych przewodów (przewody zakotwione osobno)
std::vector<catenary_anchor_card> catenary_anchor_devices( std::string const &Only = {} );
// katalog urządzeń naprężających (traction_tensioners.txt); nullptr, gdy go nie ma
catenary::tensioner_catalog const *catenary_scene_tensioners();
// łańcuch, który kończy się tym kotwieniem (Start: na początku), -1 gdy żaden
int catenary_anchor_chain( std::size_t Index, bool &Start );
// dodaje kotwienie, składa je i przebudowuje łańcuchy, które go używają. zwraca numer
std::size_t catenary_anchor_add( std::string const &File, catenary::anchor const &Data );
// zmienia kotwienie (także przywraca usunięte) i przebudowuje łańcuchy, które go używają

View File

@@ -10,6 +10,8 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "world/catenary_span.h"
#include <memory>
#include "world/Traction.h"
#include "world/catenary_network.h"
#include "utilities/Globals.h"
@@ -31,6 +33,10 @@ struct catenary_span {
gfx::geometrybank_handle solidbank; // osobny bank na bryły tego przęsła
catenary_solid solid; // bryły do oglądania z bliska
bool solidloaded { false };
std::vector<catenary::solid_vertex> attached; // bryła dołączona (lina kotwienia), układ scenerii
std::shared_ptr<catenary::network_definition> own; // kopia typu z naciągiem z dopisku "tension"
bool rope2set[ 2 ] { false, false }; // końce drugiej liny nośnej z dopisku "rope2"
catenary::vec3 rope2[ 2 ];
};
namespace {
@@ -43,11 +49,19 @@ definition( TTraction const &Traction ) {
catenary::span_points
points( TTraction const &Traction ) {
return {
catenary::span_points result {
{ Traction.pPoint1.x, Traction.pPoint1.y, Traction.pPoint1.z },
{ Traction.pPoint2.x, Traction.pPoint2.y, Traction.pPoint2.z },
{ Traction.pPoint3.x, Traction.pPoint3.y, Traction.pPoint3.z },
{ Traction.pPoint4.x, Traction.pPoint4.y, Traction.pPoint4.z } };
if( Traction.m_catenaryspan ) {
auto const &span { *Traction.m_catenaryspan };
result.messenger2_start_set = span.rope2set[ 0 ];
result.messenger2_end_set = span.rope2set[ 1 ];
result.messenger2_start = span.rope2[ 0 ];
result.messenger2_end = span.rope2[ 1 ];
}
return result;
}
// długość podwieszenia Y, jaką typ sieci przewiduje dla tego przęsła; 0 gdy brak
@@ -249,13 +263,17 @@ catenary_span_metal( TTraction const &Traction, bool const Fitting ) {
// przypisuje przęsłu typ sieci z dopisku "type" i dobiera rozstaw wieszaków
void
catenary_span_assign( TTraction &Traction, std::string const &Symbol ) {
catenary_span_assign( TTraction &Traction, std::string const &Symbol, double const Messenger, double const Contact, glm::dvec3 const *Rope2, bool const *Rope2set ) {
Traction.m_catenaryspan.reset();
if( Symbol.empty() ) { return; }
auto span { std::make_shared<catenary_span>() };
span->symbol = Symbol;
for( int end = 0; end < 2 && Rope2 != nullptr && Rope2set != nullptr; ++end ) {
span->rope2set[ end ] = Rope2set[ end ];
span->rope2[ end ] = catenary::vec3 { Rope2[ end ].x, Rope2[ end ].y, Rope2[ end ].z };
}
Traction.m_catenaryspan = span;
if( false == Global.CatenaryProcedural ) { return; }
@@ -265,6 +283,13 @@ catenary_span_assign( TTraction &Traction, std::string const &Symbol ) {
}
span->definition = registry.find( Symbol );
if( span->definition == nullptr ) { return; } // brak typu został już zgłoszony, przęsło rysuje się po staremu
// naciąg z dopisku "tension" (daN): przęsło liczone z kopią typu
if( Messenger > 0.0 || Contact > 0.0 ) {
span->own = std::make_shared<catenary::network_definition>( *span->definition );
if( Messenger > 0.0 ) { span->own->type.messenger_tension = 10.0 * Messenger; }
if( Contact > 0.0 && span->own->type.contact_tension > 0.0 ) { span->own->type.contact_tension = 10.0 * Contact; }
span->definition = span->own.get();
}
// odstęp wieszaków 0 w scenerii oznacza odcinek bez wieszaków (do kotwienia albo urządzenia naprężającego):
// rysowane są same liny i przewody w liczbie wg typu, a rozstaw wieszaków zostaje pusty
if( Traction.iNumSections == 0 ) { return; }
@@ -274,6 +299,13 @@ catenary_span_assign( TTraction &Traction, std::string const &Symbol ) {
std::hypot( Traction.pPoint2.x - Traction.pPoint1.x, Traction.pPoint2.z - Traction.pPoint1.z ) );
}
// wygląd liny stalowej (ocynk, lekko zmatowiały)
catenary_metal const &
catenary_rope_metal() {
static catenary_metal const steel { { 0.56f, 0.57f, 0.58f }, 0.8f, 0.30f, 0.20f };
return steel;
}
// symbol typu sieci przypisany przęsłu; pusty gdy brak
std::string
catenary_span_symbol( TTraction const &Traction ) {
@@ -318,6 +350,7 @@ catenary_span_solid( TTraction &Traction, glm::dvec3 const &Camera ) {
// kamera daleko: zwolnienie pamięci, linie wystarczą
release( span.solid.wires );
release( span.solid.fittings );
release( span.solid.rope );
span.solid = {};
span.solidloaded = false;
return {};
@@ -344,6 +377,10 @@ catenary_span_solid( TTraction &Traction, glm::dvec3 const &Camera ) {
auto data { vertices( Traction, fittings ) };
span.solid.fittings = GfxRenderer->Insert( data, userdata, span.solidbank, GL_TRIANGLES );
}
if( false == span.attached.empty() ) {
auto data { vertices( Traction, span.attached ) };
span.solid.rope = GfxRenderer->Insert( data, userdata, span.solidbank, GL_TRIANGLES );
}
span.solidloaded = ( span.solid.wires != null_handle );
return ( span.solidloaded ? span.solid : catenary_solid() );
}
@@ -358,6 +395,7 @@ drop_solid( TTraction &Traction ) {
if( false == span.solidloaded ) { return; }
release( span.solid.wires );
release( span.solid.fittings );
release( span.solid.rope );
span.solid = {};
span.solidloaded = false;
}
@@ -383,3 +421,12 @@ catenary_span_release( TTraction &Traction ) {
drop_solid( Traction );
release( Traction.m_geometry );
}
// bryła spoza przęsła rysowana razem z nim (lina kotwienia); zrobi się przy następnym podejściu kamery
bool
catenary_span_attach( TTraction &Traction, std::vector<catenary::solid_vertex> const &Triangles ) {
if( false == static_cast<bool>( Traction.m_catenaryspan ) ) { return false; }
drop_solid( Traction );
Traction.m_catenaryspan->attached = Triangles;
return ( Traction.m_catenaryspan->definition != nullptr );
}

View File

@@ -14,10 +14,12 @@ http://mozilla.org/MPL/2.0/.
#pragma once
#include <string>
#include <vector>
#include "rendering/geometrybank.h"
class TTraction;
namespace catenary { struct solid_vertex; }
// ustawienia rysowania sieci, zmieniane z eu07.ini i z panelu ustawień
struct catenary_settings {
@@ -27,7 +29,11 @@ extern catenary_settings CatenarySettings;
// przypisuje przęsłu typ sieci z dopisku "type" i dobiera rozstaw wieszaków; wołane po wczytaniu punktów przęsła.
// pusty symbol albo wyłączone rysowanie wg typu zostawia przęsło rysowane po staremu
void catenary_span_assign( TTraction &Traction, std::string const &Symbol );
// Messenger, Contact: naciąg liny nośnej i przewodów jezdnych w daN zamiast naciągu typu (dopisek "tension"); 0 = jak w typie
// Rope2: końce drugiej liny nośnej podane w wpisie ("rope2 start|end x y z", kotwienie z dwiema liniami): [0] start, [1] koniec,
// Rope2set - które podane
void catenary_span_assign( TTraction &Traction, std::string const &Symbol, double Messenger = 0.0, double Contact = 0.0,
glm::dvec3 const *Rope2 = nullptr, bool const *Rope2set = nullptr );
// symbol typu sieci przypisany przęsłu; pusty gdy brak
std::string catenary_span_symbol( TTraction const &Traction );
// odcinki przęsła policzone wg typu sieci, dopisywane do Vertices; false gdy przęsło ma być narysowane po staremu
@@ -37,6 +43,7 @@ bool catenary_span_lines( TTraction &Traction, gfx::vertex_array &Vertices );
struct catenary_solid {
gfx::geometry_handle wires;
gfx::geometry_handle fittings;
gfx::geometry_handle rope; // bryła dołączona z zewnątrz (lina urządzenia naprężającego na końcu przęsła), stal
};
// bryły tworzone są, gdy kamera jest blisko przęsła, i zwalniane, gdy się oddali
catenary_solid catenary_span_solid( TTraction &Traction, glm::dvec3 const &Camera );
@@ -44,6 +51,9 @@ catenary_solid catenary_span_solid( TTraction &Traction, glm::dvec3 const &Camer
void catenary_span_refresh( TTraction &Traction );
// zwalnia linie i bryły przęsła przed jego usunięciem
void catenary_span_release( TTraction &Traction );
// dołącza do brył przęsła bryłę spoza przęsła (lina kotwienia przy jego końcu; trójkąty w układzie scenerii), rysowaną
// razem z nim z bliska, ze stali. Pusta lista odłącza. false, gdy przęsło nie jest rysowane wg typu sieci
bool catenary_span_attach( TTraction &Traction, std::vector<catenary::solid_vertex> const &Triangles );
// wygląd metalu brył: kolor własny (sRGB), metaliczność, połysk i siła odbicia otoczenia, każde w zakresie 0–1
struct catenary_metal {
@@ -55,3 +65,5 @@ struct catenary_metal {
// wygląd przewodów (Fitting = false) albo osprzętu przęsła o podanym materiale i stanie z wpisu scenerii.
// wartości domyślne można zmienić bez kompilacji plikiem data/traction_materials.txt, czytanym przy pierwszym użyciu
catenary_metal const &catenary_span_metal( TTraction const &Traction, bool Fitting );
// wygląd liny stalowej (cięgło 4490 urządzenia naprężającego)
catenary_metal const &catenary_rope_metal();

View File

@@ -33,7 +33,7 @@ std::string normalize_path( std::string Path ) {
// słowa kluczowe wewnątrz wpisu podwieszenia
bool is_keyword( std::string const &Token ) {
static std::set<std::string> const keywords { "support", "scheme", "messenger", "contact", "insulators", "override", "skin", "at", "attach", "rope", "pole", "umbrella", "endsuspension" };
static std::set<std::string> const keywords { "support", "scheme", "messenger", "contact", "insulators", "override", "skin", "at", "attach", "rope", "pole", "umbrella", "spread", "shared_pole", "endsuspension" };
return keywords.find( Token ) != keywords.end();
}
@@ -101,6 +101,15 @@ void parse_suspensions( std::vector<std::string> const &Tokens, std::string cons
else if( key == "parallel" ) { one( line.parallel ); }
else if( key == "anchor_start" ) { one( line.anchor_start ); }
else if( key == "anchor_end" ) { one( line.anchor_end ); }
else if( key == "anchor_start_contact" ) { one( line.anchor_start_contact ); }
else if( key == "anchor_end_contact" ) { one( line.anchor_end_contact ); }
else if( key == "middle" ) { one( line.middle ); }
else if( key == "middle_device" ) { one( line.middle_device ); }
else if( key == "middle_height" ) { number( line.middle_heights[ 0 ] ); number( line.middle_heights[ 1 ] ); }
else if( key == "messenger_from" ) {
std::string listed, holder;
if( one( listed ) && one( holder ) ) { line.messenger_from.emplace_back( listed, holder ); }
}
else if( key == "hangers" ) { for( auto &value : line.hangers ) { number( value ); } }
else if( key == "suspensions" ) {
while( index < count && Tokens[ index ] != "endchain" && Tokens[ index ] != "node" ) { line.suspensions.push_back( Tokens[ index++ ] ); }
@@ -177,12 +186,21 @@ void parse_suspensions( std::vector<std::string> const &Tokens, std::string cons
else if( key == "network" ) { line.network = one(); }
else if( key == "pole" ) { line.pole_type = one(); point( line.pole ); haspole = true; }
else if( key == "toward" ) { point( line.toward ); hastoward = true; }
else if( key == "on" ) { line.on = one(); }
else if( key == "distance" ) { number( line.distance ); }
else if( key == "color" ) { line.color = one(); }
else if( key == "top_color" ) { line.top_color = one(); }
else if( key == "elongation" ) { number( line.elongation ); }
else if( key == "missing" ) { double value { 0.0 }; number( value ); line.missing = static_cast<int>( value ); }
else if( key == "ground" ) { number( line.ground ); }
else if( key == "height" ) { number( line.height ); }
else if( key == "rope_height" ) { number( line.rope_height ); }
else if( key == "insulators" ) {
line.insulators = one();
if( line.insulators != "new" && line.insulators != "old" && line.insulators != "messenger" && line.insulators != "contact" ) {
report( line.name, "\"insulators\" takes new, old, messenger or contact" ); good = false;
}
}
else if( key == "skin" ) { line.skin = one(); }
else { report( line.name, "unexpected token \"" + key + "\" in an anchor" ); good = false; }
}
@@ -250,6 +268,14 @@ void parse_suspensions( std::vector<std::string> const &Tokens, std::string cons
}
else { report( entry.name, "\"" + key + "\" needs three numbers: a point in scenery coordinates" ); good = false; }
}
else if( key == "shared_pole" ) {
if( list.empty() ) { entry.shared_pole = true; }
else { report( entry.name, "\"shared_pole\" takes no values" ); good = false; }
}
else if( key == "spread" ) {
if( list.size() == 3 && to_number( list[ 0 ], entry.spread[ 0 ] ) && to_number( list[ 1 ], entry.spread[ 1 ] ) && to_number( list[ 2 ], entry.spread[ 2 ] ) ) { entry.has_spread = true; }
else { report( entry.name, "\"spread\" needs three numbers: the point on the console in the plane of the cantilever" ); good = false; }
}
else if( key == "umbrella" ) {
if( list.size() == 1 ) { entry.umbrella = list.front(); }
else { report( entry.name, "\"umbrella\" needs exactly one name" ); good = false; }
@@ -270,6 +296,8 @@ void parse_suspensions( std::vector<std::string> const &Tokens, std::string cons
if( entry.has_attach && entry.has_pole ) { report( entry.name, "\"attach\" and \"pole\" can't be used together" ); good = false; }
if( entry.card == 0 ) { report( entry.name, "explicit points need a \"scheme\"" ); good = false; }
if( false == entry.umbrella.empty() && false == entry.has_pole ) { report( entry.name, "\"umbrella\" needs a \"pole\"" ); good = false; }
if( entry.shared_pole && false == entry.has_pole ) { report( entry.name, "\"shared_pole\" needs a \"pole\"" ); good = false; }
if( entry.has_spread && false == entry.has_pole ) { report( entry.name, "\"spread\" needs a \"pole\"" ); good = false; }
}
else {
if( entry.support.empty() ) { report( entry.name, "missing \"support\"" ); good = false; }
@@ -318,9 +346,18 @@ std::string write_suspension( suspension const &Entry ) {
if( Entry.has_attach ) { text += " attach " + point( Entry.attach ); }
if( Entry.has_pole ) { text += " pole " + Entry.pole_type + " " + point( Entry.pole ); }
if( false == Entry.umbrella.empty() ) { text += " umbrella " + Entry.umbrella; }
if( Entry.has_spread ) { text += " spread " + point( Entry.spread ); }
if( Entry.shared_pole ) { text += " shared_pole"; }
return text + " endsuspension";
}
std::string const &messenger_holder( chain const &Chain, std::string const &Listed ) {
for( auto const &pair : Chain.messenger_from ) {
if( pair.first == Listed && false == pair.second.empty() ) { return pair.second; }
}
return Listed;
}
std::string write_chain( chain const &Entry ) {
auto const number = []( double const Value ) {
@@ -337,6 +374,13 @@ std::string write_chain( chain const &Entry ) {
if( false == Entry.parallel.empty() ) { text += " parallel " + Entry.parallel; }
if( false == Entry.anchor_start.empty() ) { text += " anchor_start " + Entry.anchor_start; }
if( false == Entry.anchor_end.empty() ) { text += " anchor_end " + Entry.anchor_end; }
if( false == Entry.anchor_start_contact.empty() ) { text += " anchor_start_contact " + Entry.anchor_start_contact; }
if( false == Entry.anchor_end_contact.empty() ) { text += " anchor_end_contact " + Entry.anchor_end_contact; }
if( false == Entry.middle.empty() ) {
text += " middle " + Entry.middle + ( Entry.middle_device != "0406" ? " middle_device " + Entry.middle_device : std::string() );
if( Entry.middle_heights[ 0 ] > 0.0 || Entry.middle_heights[ 1 ] > 0.0 ) { text += " middle_height " + number( Entry.middle_heights[ 0 ] ) + " " + number( Entry.middle_heights[ 1 ] ); }
}
for( auto const &pair : Entry.messenger_from ) { text += " messenger_from " + pair.first + " " + pair.second; }
text += " suspensions";
for( auto const &name : Entry.suspensions ) { text += " " + name; }
return text + " endchain";
@@ -378,12 +422,17 @@ std::string write_anchor( anchor const &Entry ) {
std::string text { "node " + number( Entry.range_max, 0 ) + " " + number( Entry.range_min, 0 ) + " " + ( Entry.name.empty() ? std::string( "none" ) : Entry.name ) + " anchor" };
text += " device " + Entry.device;
if( false == Entry.network.empty() ) { text += " network " + Entry.network; }
text += " pole " + Entry.pole_type + " " + point( Entry.pole ) + " toward " + point( Entry.toward ) + " distance " + number( Entry.distance, 1 );
text += " pole " + Entry.pole_type + " " + point( Entry.pole );
if( false == Entry.on.empty() ) { text += " on " + Entry.on; }
text += " toward " + point( Entry.toward ) + " distance " + number( Entry.distance, 1 );
if( false == Entry.color.empty() ) { text += " color " + Entry.color; }
if( false == Entry.top_color.empty() ) { text += " top_color " + Entry.top_color; }
if( Entry.elongation != 0.0 ) { text += " elongation " + number( Entry.elongation, 3 ); }
if( Entry.missing != 0 ) { text += " missing " + std::to_string( Entry.missing ); }
if( Entry.ground != -0.50 ) { text += " ground " + number( Entry.ground, 2 ); }
if( Entry.height > 0.0 ) { text += " height " + number( Entry.height, 3 ); }
if( Entry.rope_height > 0.0 ) { text += " rope_height " + number( Entry.rope_height, 3 ); }
if( false == Entry.insulators.empty() ) { text += " insulators " + Entry.insulators; }
if( false == Entry.skin.empty() ) { text += " skin " + Entry.skin; }
return text + " endanchor";
}

View File

@@ -38,6 +38,12 @@ struct suspension {
// parasol: dwa wpisy ze słupem i tą samą nazwą parasola to dwa wysięgniki na jednym słupie, po przeciwnych stronach.
// słup stawia pierwszy z nich; mocowania regulowane (z połówką z nakrętkami) idą na wspólną wysokość
std::string umbrella;
// wysięgnik rozstawiony (konsola 4890; para wpisów z tą samą nazwą "umbrella" po tej samej stronie słupa albo jeden): punkt na osi
// płaszczyzny wysięgnika przy słupie (oś słupa przesunięta wzdłuż toru do punktu mocowania na konsoli), "spread x y z"
// słup stawia inny wpis w tym samym miejscu (trzeci wysięgnik przy rozstawionych, w osi słupa): ten tylko z niego korzysta
bool shared_pole { false };
bool has_spread { false };
double spread[ 3 ] { 0.0, 0.0, 0.0 };
};
// łańcuch sieci: kolejne przęsła między podwieszeniami z listy (od kotwy do kotwy), wpis
@@ -58,12 +64,28 @@ struct chain {
double hangers[ 4 ] { 0.4, 4.0, 3.0, 0.04 }; // jak w node traction: najniższy wieszak, odstęp, liczba drutów, odstęp drutów
std::string parallel; // przęsło bieżni wspólnej (jak "parallel" w node traction); puste = brak
std::string anchor_start, anchor_end; // kotwienia (wpisy "anchor") przed pierwszym i za ostatnim podwieszeniem; puste = brak
// przewody jezdne zakotwione osobno (inne kotwienie, zwykle na innym słupie; 0404 albo przewody rozdzielonego): wtedy
// anchor_start/anchor_end trzyma samą linę nośną (0405, lina rozdzielonego). Puste = całość na anchor_start/anchor_end
std::string anchor_start_contact, anchor_end_contact;
// kotwienie środkowe (karty 63-0406 / 0408): podwieszenie, przy którym lina kotwienia środkowego jest zaciśnięta na linie nośnej,
// i karta (0406 z izolacją, 0408 bez); lina idzie do słupów sąsiednich podwieszeń. Puste = brak
std::string middle;
std::string middle_device { "0406" };
double middle_heights[ 2 ] { 0.0, 0.0 }; // uchwyty liny kotwienia na słupach przed i za (nad główką); 0 = lina nośna sąsiada
// lina nośna z innego podwieszenia niż przewody jezdne (wysięgniki rozstawione na konsoli 4890, wysięgnik samej liny przy
// odciągu przewodów): w punkcie łańcucha <podwieszenie z listy> przewody idą przez jego "at", lina przez "rope" podwieszenia
// <trzymające linę>. Wpis "messenger_from <z listy> <trzymające linę>", przed "suspensions"
std::vector<std::pair<std::string, std::string>> messenger_from;
std::vector<std::string> suspensions; // nazwy podwieszeń z tego samego pliku, po kolei
};
// podwieszenie, które trzyma linę nośną łańcucha w punkcie Listed (samo Listed, gdy nie ma wpisu messenger_from)
std::string const &messenger_holder( chain const &Chain, std::string const &Listed );
// kotwienie ciężarowe (urządzenie naprężające z pliku traction_tensioners.txt) na słupie kotwowym, wpis
// "node <max> <min> <nazwa> anchor device <karta> network <symbol typu> pole <słup> x y z toward x y z distance <m>
// [color <kolor>] [top_color <kolor>] [elongation <m>] [missing <n>] [ground <m>] [skin <tekstura>] endanchor".
// "node <max> <min> <nazwa> anchor device <karta> network <symbol typu> pole <słup> x y z [on <podwieszenie>] toward x y z distance <m>
// [color <kolor>] [top_color <kolor>] [elongation <m>] [missing <n>] [ground <m>] [height <m>] [rope_height <m>] [insulators <rodzaj>] [skin <tekstura>]
// endanchor".
// pole: oś słupa na wysokości główki szyny; toward: punkt, z którego przychodzą druty (urządzenie leży w tym kierunku od
// słupa); distance: odległość od kotwienia środkowego albo stałego. Łańcuch przypina się do niego "anchor_start/anchor_end",
// a ostatnie przęsło (bez wieszaków) kończy się na uchwytach krańcowych urządzenia
@@ -75,12 +97,19 @@ struct anchor {
std::string network; // symbol typu sieci (traction_<symbol>.txt): numer typu i liczba przewodów jezdnych
std::string pole_type;
double pole[ 3 ] { 0.0, 0.0, 0.0 };
std::string on; // kotwienie na istniejącym słupie: nazwa podwieszenia (z tego pliku), którego słup to
// jest; słupa nie stawia, typ i punkt bierze z tamtego wpisu (pole tylko zapasowo)
double toward[ 3 ] { 0.0, 0.0, 0.0 };
double distance { 700.0 };
std::string color, top_color;
double elongation { 0.0 };
int missing { 0 };
double ground { -0.50 };
double height { 0.0 }; // oś rolek nad główką szyny; 0 = z tablicy montażowej (6,70). Kotwienie stałe: przewody
double rope_height { 0.0 }; // kotwienie stałe: uchwyt liny nośnej nad główką szyny; 0 = jak przy ostatnim słupie
// izolatory w łańcuchach do drutów: puste = auto (rolkowe ESN-2025 na obu przy drutach zaśniedziałych, inaczej żywiczne 7150),
// "new" żywiczne, "old" rolkowe na obu, "messenger" rolkowy tylko na linie nośnej, "contact" rolkowe tylko na przewodach
std::string insulators;
std::string skin;
};

File diff suppressed because it is too large Load Diff

View File

@@ -26,13 +26,18 @@ http://mozilla.org/MPL/2.0/.
// Ułożenie modeli (w układzie części):
// rolka 4460 zero w osi, koło w płaszczyźnie X-Z, oś wzdłuż Y
// części liniowe (uchwyty 4440/4450, łączniki, izolatory, śruby, wyrównywacz, uchwyty krańcowe, łączniki na linie)
// zero w sworzniu od strony konstrukcji/dźwigni, biegną w +X do drugiego sworznia (długość z danych)
// dźwignia 6406 zero w otworze I, otwory w +Z (I na dole), otwór III odsunięty w +X; przy otworze liny poniżej
// otworu przewodów dźwignia odwrócona (obrót o 180 stopni wokół Y)
// zero w sworzniu od strony konstrukcji/dźwigni, biegną w -X do drugiego sworznia (pusty węzeł,
// np. hole_1 uchwytu 4440; długość z danych); "~część" w łańcuchu - postawiona odwrotnie (4440: zero
// w wąskim końcu, widełki z osią rolki na -X; przy rolce ruchomej widełki są od strony słupa)
// dźwignia 6406 zero w otworze osi II (mount_2 = III), otwory w +Z (I na dole), otwory na druty odsunięte w -X
// modelu (w stronę drutów); przy otworze liny poniżej otworu przewodów dźwignia odwrócona (180 wokół Y)
// pręt 6401/6402 zero na dole (pod stosem), w +Z do zaczepu na górze (długość z danych)
// ciężar 63xx zero w środku spodu, w +Z; kolejne obracane o "turn" wokół Z; kolor numerem wykonania modelu
// prowadnica 5070 zero w osi przesuwu pierścienia na dole, w +Z; mocowania do wsporników w -X (w stronę konstrukcji)
// pierścień 5080 zero w osi stosu, poziomo, punkt przesuwu po prowadnicy w -X (w stronę konstrukcji); ring_at nad spodem stosu
// lina linka rozciągana (część typu cord, zero w zaczepie, w +Z, skalowana wzdłuż Z)
#pragma once
#include <functional>
#include <map>
#include <string>
#include <vector>
@@ -77,6 +82,9 @@ struct tensioner_pulley {
double diameter { 0.220 }; // po dnie rowka
double width { 0.080 };
int grooves { 2 };
// krążek drugiego rowka jako osobny model (4460-11), stawiany w osi rolki; model rolki niesie wtedy oś i krążek
// pierwszego rowka. Oba obracają się osobno o przewiniętą linę; pusty = cała rolka jednym modelem, obracana z rowkiem A
std::string sheave;
};
struct tensioner_lever {
@@ -88,6 +96,7 @@ struct tensioner_lever {
struct tensioner_link {
std::string name;
double length { 0.0 };
double roll { 0.0 }; // stopnie wokół osi łańcucha (w danych "<część>@<stopnie>"), np. 90 - sworzeń obrócony do następnej części
};
// stos ciężarów i otwory dźwigni dla typu sieci (numer typu z karty 0002 = traction_code)
@@ -95,6 +104,9 @@ struct tensioner_type {
int code { -1 };
std::vector<std::pair<std::string, int>> stack; // od dołu: ciężar, liczba
std::string messenger_hole, contact_hole, axis_hole;
// dźwignie szeregowo (2 liny + 2 przewody na jednym stosie): otwór przewodów pierwszej dźwigni ciągnie łańcuchem "cascade"
// drugą dźwignię za jej otwór osi; druga dzieli dalej na drugą linę nośną i przewody. Puste = jedna dźwignia
std::string second_messenger_hole, second_contact_hole, second_axis_hole;
std::string rod; // pręt, gdy inny niż urządzenia
std::string messenger_clamp; // uchwyt krańcowy liny nośnej, gdy inny niż urządzenia
};
@@ -102,15 +114,88 @@ struct tensioner_type {
struct tensioner_device {
std::string name; // karta, np. 0461
std::string table, pulley, lever, rod, rope;
std::string lever2; // druga dźwignia (szeregowo), gdy inna niż pierwsza
double rope_diameter { 0.010 };
std::string rope_eye; // ucho (kausza) na obu końcach liny, nieskalowane; pusty = lina bez uch
double rope_eye_length { 0.0 }; // od zera ucha (styk z liną) do punktu "eye" (zaczep), wzdłuż +Z modelu
std::string rope_spacer; // przekładka na sworzniu ucha przy pręcie (6411, oś w +Z modelu); pusta = bez
double rope_spacer_length { 0.0 };
std::vector<tensioner_link> mount; // od czoła konstrukcji do osi rolki stałej
std::vector<tensioner_link> holder; // od osi rolki ruchomej do osi dźwigni
std::vector<tensioner_link> hanger; // od rolki stałej w dół do pręta (bez liny)
std::vector<tensioner_link> messenger; // od otworu dźwigni do uchwytu krańcowego liny nośnej (bez uchwytu)
std::vector<tensioner_link> contact; // od otworu dźwigni do wyrównywacza albo uchwytu krańcowego przewodu
// dźwignie szeregowo: od otworu przewodów pierwszej do osi drugiej (z izolatorem) i od otworu liny drugiej do uchwytu drugiej
// liny nośnej; przewody ("contact") wtedy z drugiej dźwigni
std::vector<tensioner_link> cascade, messenger2;
// kotwienie stałe (kind fixed): bez rolek i ciężarów; na konstrukcji dwa przegubowe uchwyty kotwienia (pierwsza część "mount",
// 4430) - liny na wysokości liny nośnej, przewodów na wysokości przewodów, każdy w parze uchwytów poz. 1 (karta 43-0453).
// Za wyrównywaczem łańcuch każdego drutu osobno (…_each), dwie liny nośne przez messenger_equalizer (sieć 32, 35)
bool fixed { false };
// rolki szeregowe (karty 0421, 0471, 0480): przy konstrukcji duża rolka (pulley) i za nią mała (small_pulley) o small_spacing
// dalej na uchwycie fixed_holder (4440-6), na wózku mała i duża (uchwyt 4440-5 = holder); lina od zaczepu na uchwycie stałym
// (dead_end od osi dużej) przez małą ruchomą, małą stałą, dużą ruchomą i dużą stałą w dół. b z tablicy = oś dużej ruchomej.
// Puste small_pulley = rolki równoległe (podwójne)
std::string small_pulley, fixed_holder;
// kotwienie rozdzielone: urządzenie tylko liny albo tylko przewodów (only), bez dźwigni - łańcuch wprost z uchwytu rolki
// ruchomej; dwa takie składa urządzenie "kind split" (split <liny> <przewodów>). Prowadnica na ramach 5090 (frame <rama>
// <od czoła do osi prowadnic> <prowadnica w Y> <strona stosu: -1 przy konstrukcji, +1 dalej> [owner]): stos pod liną (Y 0),
// pierścień obrócony tak, żeby szekla trafiała w prowadnicę; owner stawia ramy i ich uchwyty
std::string only;
bool split { false };
std::string split_messenger, split_contact;
std::string frame;
double frame_length { 0.0 }, frame_y { 0.0 };
int frame_side { -1 };
bool frame_owner { false };
double messenger_spread { 0.08 }; // rozstaw dwóch lin za messenger_equalizer
bool messenger_equalizer_z { false }; // otwory messenger_equalizer w Z modelu (dźwignia 6403), a nie w Y (wyrównywacz 4410)
double small_spacing { 0.336 }, dead_end { 0.703 };
std::vector<tensioner_link> messenger_each, contact_each;
tensioner_link messenger_equalizer;
// stare złożenia: izolator rolkowy ESN-2025 zamiast 7150 (Jan 2026-10-24: ESN-2025 - 4420 - [wyrównywacz] - uchwyt);
// puste = jak nowe
std::vector<tensioner_link> messenger_old, contact_old;
tensioner_link messenger_clamp, contact_clamp;
tensioner_link contact_clamp_old; // uchwyt krańcowy przewodów w starych złożeniach (5110, stara karta 33-0401); pusty = jak nowe
tensioner_link equalizer; // wyrównywacz przewodów jezdnych (tylko przy dwóch przewodach)
double equalizer_spread { 0.08 }; // rozstaw przewodów za wyrównywaczem
// zabezpieczenie 4600 wyk. 1: dwa ramiona z osi rolek (stałej i ruchomej) zbiegające się na dolnej śrubie, para po obu stronach rolek
std::string safety_fixed, safety_moving;
double safety_arm { 1.0 }; // rozstaw otworów ramienia
double safety_offset { 0.06 }; // odsunięcie każdej pary od środka rolki w bok
// zabezpieczenie 4600 wyk. 2 (rolki szeregowe, karta 13-0421 poz. 10): ramiona z osi małych rolek (stałej i ruchomej) w dół,
// na końcach drążek; puste = bez niego. safety_series <ramię przy stałej> <ramię przy ruchomej> <drążek> <ramię> <drążek> <w bok>
std::string series_arm_fixed, series_arm_moving, series_bar;
double series_arm { 0.46268 }, series_bar_length { 1.30 };
// tytuł do wyboru w edytorze (karta z odmianą widoczna na liście mimo "-" w nazwie); puste = bez
std::string title;
// prowadnica ciężarów (5070, karta 33-0455) i pierścień prowadzący na wierzchu stosu (5080). Model prowadnicy: zero w osi
// przesuwu pierścienia na dole, w +Z (długość), punkty "mount"/"end" mocowań do wsporników o guide_mount w -X (w stronę
// konstrukcji). Pierścień: zero w osi stosu, poziomo, "mount" o ring_arm w -X (w osi przesuwu po prowadnicy); pusty = bez nich
std::string guide, ring;
double guide_length { 4.50 };
double guide_mount { 0.07 };
double guide_bottom { 0.80 }; // spód prowadnicy nad główką szyny (karta 23-0018-2: 0,80, prowadnica 4,50 do 5,30)
double ring_arm { 0.200 }; // od osi stosu do osi szekli, która chodzi po prowadnicy (karta 23-5080 wyk. 1: 200)
double ring_turn { 0.0 }; // stopnie wokół osi stosu: 0 = prowadnica w stronę konstrukcji, 90 = w bok (+Y)
double ring_at { 1.00 }; // wysokość pierścienia nad spodem stosu (karta 23-0018-2: 1,00 m); ujemna = na wierzchu stosu
// uchwyty do konstrukcji (karta 33-0455): para przód + tył ściśnięta śrubami przez słup, na ścianach wzdłuż drutów (obrócone
// o 90 stopni względem wysięgnika; rozstaw śrub obejmuje słup w poprzek). Przód: zero na czole konstrukcji, śruby w +X modelu
// (w głąb), hole_1 o hole_out przed czołem i hole_drop pod zerem. Tył: ten sam układ, po drugiej stronie.
// Uchwyt przegubowy 4450 (pierwsza część "mount") trzymają dwa: górny wprost za mount_top, dolny odwrócony o 180 stopni za
// mount_bottom (pivot_top / pivot_bottom nad / pod zerem 4450)
struct bracket_set {
std::vector<std::string> poles; // przedrostki nazw słupów, np. 1611-11 (najdłuższy pasujący wygrywa)
double hole_out { 0.04 }, hole_drop { 0.04 }, bolt { 0.217 };
std::string top_front, top_back, bottom_front, bottom_back;
};
std::vector<bracket_set> brackets;
double pivot_top { 0.0 }, pivot_bottom { 0.0 };
// otwór na końcu uchwytu (oś stałej rolki) nad jego zerem: uchwyt schodzi o tyle, żeby rolka została na swojej wysokości
// (5040: hole_1 0,03 nad mount_bottom; wpis "pivot <góra> <dół> [rise <m>]")
double mount_rise { 0.0 };
bracket_set const *brackets_for( std::string const &Pole ) const;
std::vector<tensioner_type> types;
tensioner_type const *type( int Code ) const;
};
@@ -126,6 +211,8 @@ public:
tensioner_device const *device( std::string const &Name ) const;
std::vector<tensioner_device> const &devices() const { return m_devices; }
std::map<std::string, double> const &part_lengths() const { return m_lengths; } // długości części liniowych (pręt)
// numer typu sieci dla urządzeń: sieć bez własnych danych bierze dane innej (wpis "network <symbol> as <kod>"), inaczej jej kod
int code_for( std::string const &Symbol, int Code ) const;
private:
std::vector<tensioner_table> m_tables;
@@ -134,19 +221,45 @@ private:
std::vector<tensioner_lever> m_levers;
std::vector<tensioner_device> m_devices;
std::map<std::string, double> m_lengths;
std::map<std::string, int> m_networks;
};
struct tensioner_input {
int type { 3 }; // numer typu sieci (traction_code)
int contacts { 2 }; // liczba przewodów jezdnych
int messengers { 1 }; // liczba lin nośnych (kotwienie stałe sieci 32, 35: dwie)
double messenger_stack { 0.04 }; // odstęp pionowy dwóch lin nośnych w przęśle (druga celuje o tyle niżej)
double temperature { 10.0 }; // °C
double distance { 700.0 }; // m, od kotwienia stałego albo środkowego
double elongation { 0.0 }; // m, trwałe wydłużenie sieci od ostatniej regulacji (rolka bliżej konstrukcji)
int missing { 0 }; // brakujące ciężary (z góry stosu)
// sieć do skrócenia przy naciągu mniejszym niż od pełnego stosu (brakujące ciężary): sztywności EA wszystkich lin
// nośnych i wszystkich przewodów jezdnych (daN), ciężar sieci na metr wiszący na linie (daN/m) i średnia rozpiętość
// przęseł (m). Lina i przewody skracają się sprężyście, a lina dodatkowo zwisa bardziej, więc urządzenie ściąga je ku
// przęsłom: rolka dalej od konstrukcji, ciężary wyżej. Sztywność 0 = bez tego (tablica jak przy pełnym stosie)
double messenger_stiffness { 0.0 }, contact_stiffness { 0.0 };
double load { 0.0 };
double span { 0.0 };
std::string color; // kolor ciężarów (tensioner_color); pusty = szary
std::string top_color; // kolor najwyższego ciężaru; pusty = jak reszta
bool old_fittings { false }; // uchwyty krańcowe przewodów 5110 (druty zaśniedziałe)
bool old_messenger { false }, old_contact { false }; // izolatory rolkowe (łańcuchy messenger_old / contact_old) na linie / przewodach
// punkty, w które celują łańcuchy do liny nośnej i do przewodów jezdnych (ostatnie podwieszenie, układ urządzenia);
// bez nich łańcuchy poziomo wzdłuż +X
bool aimed { false };
double messenger_target[ 3 ] { 0.0, 0.0, 0.0 };
double contact_target[ 3 ] { 0.0, 0.0, 0.0 }; // stare złożenie (druty zaśniedziałe): uchwyty krańcowe przewodów 5110 zamiast 5120
double height { 0.0 }; // oś rolek nad główką szyny; 0 = z tablicy. Wyżej/niżej: ciężary o tyle samo (lina ta sama)
double rope_height { 0.0 }; // kotwienie stałe: uchwyt liny nośnej nad główką; 0 = jak drut przy ostatnim podwieszeniu
double ground { -0.50 }; // najniższe położenie spodu ciężarów względem główki szyny (grunt, fundament); ZAŁOŻENIE
double gravity { 0.981 }; // daN/kg
// konstrukcja: nazwa słupa (dobór uchwytów) i jej grubość od czoła do tyłu (wzdłuż drutów; słup 161x - szerokość wzdłuż toru,
// przez nią idą śruby uchwytów) na wysokości nad główką; puste = bez uchwytów
std::string pole;
std::function<double( double )> depth;
// kąt osi konstrukcji (od czoła w stronę kotwionego przęsła) w układzie urządzenia, radiany wokół +Z: urządzenie od przegubu
// 4450 ciągnie do drutów, a uchwyty, prowadnica, pierścień i stos zostają przy konstrukcji (0 = konstrukcja w linii drutów)
double structure_angle { 0.0 };
};
struct tensioner_placement {
@@ -162,19 +275,46 @@ struct tensioner_result {
std::vector<std::vector<double>> rope; // punkty liny (x, y, z) po kolei, od zaczepu przy rolce stałej do pręta
double b { 0.0 }, s { 0.0 }; // położenie rolki ruchomej i spodu ciężarów
double stack_height { 0.0 };
double height { 0.0 }; // oś rolek nad główką szyny
double lever_tilt { 0.0 }; // radiany: odchylenie uchwytu dźwigni od poziomu w dół (dźwignia od pionu w stronę konstrukcji u góry)
// obrót krążków (radiany wokół +Y urządzenia) od położenia przy +10 °C: lina przewinięta przez rowek / promień
double turn_fixed_a { 0.0 }, turn_fixed_b { 0.0 }, turn_moving_a { 0.0 }, turn_moving_b { 0.0 };
int weights { 0 }; // zawieszone ciężary
double tension { 0.0 }; // daN, całkowity naciąg sieci od ciężarów (zero, gdy ciężary stoją na gruncie)
double nominal_tension { 0.0 }; // daN, naciąg od pełnego stosu (jak w tablicy)
double shortening { 0.0 }; // m, o ile rolka odjechała od tablicy przez mniejszy naciąg (sprężystość i zwis)
double messenger_tension { 0.0 }, contact_tension { 0.0 };
double messenger_end[ 3 ] { 0.0, 0.0, 0.0 }; // gdzie kończy się lina nośna (uchwyt krańcowy)
std::vector<std::vector<double>> rope2; // kotwienie rozdzielone: lina urządzenia przewodów (rope - urządzenia liny)
// dźwignie szeregowo: druga lina nośna (z drugiej dźwigni) i naciągi obu lin osobno (messenger_tension to ich suma)
bool cascade { false };
bool fixed { false }; // kotwienie stałe (bez ciężarów; b, s, naciąg zero)
double rope_height { 0.0 }; // kotwienie stałe: wysokość uchwytu liny nośnej (height - przewodów)
double messenger2_end[ 3 ] { 0.0, 0.0, 0.0 }; // druga lina nośna: z drugiej dźwigni albo za wyrównywaczem kotwienia stałego
double messenger_tensions[ 2 ] { 0.0, 0.0 };
std::vector<std::vector<double>> contact_ends; // gdzie kończą się przewody jezdne
bool grounded { false }; // ciężary oparte o grunt: sieć bez naciągu od urządzenia
// mocowania prowadnicy do wsporników na konstrukcji (dolne i górne, układ urządzenia); has_guide = false bez prowadnicy
bool has_guide { false };
double guide_mount[ 3 ] { 0.0, 0.0, 0.0 }, guide_end[ 3 ] { 0.0, 0.0, 0.0 };
std::vector<std::string> warnings;
};
// składa urządzenie. false, gdy brakuje danych (urządzenia, typu, tablicy, rolki, dźwigni, otworów)
bool solve_tensioner( tensioner_catalog const &Catalog, tensioner_device const &Device, tensioner_input const &Input, tensioner_result &Result );
// lina jako odcinki linki rozciąganej (część Cord o długości spoczynkowej Length, skala w poprzek Girth)
void tensioner_rope_parts( tensioner_result &Result, std::string const &Cord, double Length, double Girth );
// wierzchołek bryły proceduralnej w układzie urządzenia; kolejne trójki to trójkąty, obchodzone przeciwnie do ruchu
// wskazówek zegara patrząc z zewnątrz (w prawoskrętnym układzie urządzenia)
struct tensioner_vertex {
double position[ 3 ];
double normal[ 3 ];
};
// lina jako jedna bryła proceduralna: rura o promieniu Radius i Sides ścianach po całej ścieżce Result.rope (łuki na
// rolkach co 5°), bez łączeń. Eye (gdy niepusty): ucho o długości EyeLength (zero w styku z liną, punkt "eye" w +Z w
// zaczepie) na obu końcach, dopisywane do Result.parts; rura skrócona o ucha
// Spacer: przekładka ślizgowa (6411) na sworzniu ucha przy pręcie ciężarów (koniec liny), oś wzdłuż sworznia; pusta = bez
void tensioner_rope_solid( tensioner_result &Result, double Radius, int Sides, std::string const &Eye, double EyeLength, std::vector<tensioner_vertex> &Triangles,
std::string const &Spacer = {}, double SpacerLength = 0.0 );
} // namespace catenary