mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 08:09:19 +02:00
Reorganize source files into logical subdirectories
Co-authored-by: Hirek193 <23196899+Hirek193@users.noreply.github.com>
This commit is contained in:
35
world/Curve.h
Normal file
35
world/Curve.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#ifndef CurveH
|
||||
#define CurveH
|
||||
|
||||
#include "QueryParserComp.hpp"
|
||||
#include "usefull.h"
|
||||
|
||||
class TCurve
|
||||
{
|
||||
public:
|
||||
TCurve();
|
||||
~TCurve();
|
||||
bool Init(int n, int c);
|
||||
float GetValue(int c, float p);
|
||||
bool SetValue(int c, float p, float v);
|
||||
bool Load(TQueryParserComp *Parser);
|
||||
bool LoadFromFile(AnsiString asName);
|
||||
bool SaveToFile(AnsiString asName);
|
||||
|
||||
int iNumValues;
|
||||
int iNumCols;
|
||||
|
||||
private:
|
||||
float **Values;
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
#endif
|
||||
329
world/EvLaunch.cpp
Normal file
329
world/EvLaunch.cpp
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
/*
|
||||
MaSzyna EU07 locomotive simulator
|
||||
Copyright (C) 2001-2004 Marcin Wozniak and others
|
||||
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "EvLaunch.h"
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Logs.h"
|
||||
#include "Event.h"
|
||||
#include "MemCell.h"
|
||||
#include "Timer.h"
|
||||
#include "parser.h"
|
||||
#include "Console.h"
|
||||
#include "simulationtime.h"
|
||||
#include "utilities.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// encodes expected key in a short, where low byte represents the actual key,
|
||||
// and the high byte holds modifiers: 0x1 = shift, 0x2 = ctrl, 0x4 = alt
|
||||
int vk_to_glfw_key( int const Keycode ) {
|
||||
char modifier = 0;
|
||||
char key = 0;
|
||||
|
||||
if (Keycode < 'A') {
|
||||
key = Keycode;
|
||||
} else if (Keycode <= 'Z') {
|
||||
key = Keycode;
|
||||
modifier = GLFW_MOD_SHIFT;
|
||||
} else if (Keycode < 'a') {
|
||||
key = Keycode;
|
||||
} else if (Keycode <= 'z') {
|
||||
key = Keycode - 32;
|
||||
} else {
|
||||
ErrorLog("unknown key: " + std::to_string(Keycode));
|
||||
}
|
||||
|
||||
return ((int)modifier << 8) | key;
|
||||
}
|
||||
|
||||
bool TEventLauncher::Load(cParser *parser)
|
||||
{ // wczytanie wyzwalacza zdarzeń
|
||||
std::string token;
|
||||
parser->getTokens();
|
||||
*parser >> dRadius; // promień działania
|
||||
if (dRadius > 0.0)
|
||||
dRadius *= dRadius; // do kwadratu, pod warunkiem, że nie jest ujemne
|
||||
parser->getTokens(); // klawisz sterujący
|
||||
*parser >> token;
|
||||
if (token != "none") {
|
||||
|
||||
if( token.size() == 1 ) {
|
||||
// single char, assigned activation key
|
||||
iKey = vk_to_glfw_key( token[ 0 ] );
|
||||
}
|
||||
else {
|
||||
// this launcher may be activated by radio message
|
||||
std::map<std::string, int> messages {
|
||||
{ "radio_call1", radio_message::call1 },
|
||||
{ "radio_call3", radio_message::call3 }
|
||||
};
|
||||
auto lookup = messages.find( token );
|
||||
iKey = (
|
||||
lookup != messages.end() ?
|
||||
lookup->second :
|
||||
// a jak więcej, to jakby numer klawisza jest
|
||||
vk_to_glfw_key( stol_def( token, 0 ) ) );
|
||||
}
|
||||
}
|
||||
parser->getTokens();
|
||||
*parser >> DeltaTime;
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
asEvent1Name = token; // pierwszy event
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
asEvent2Name = token; // drugi event
|
||||
if ((asEvent2Name == "end") || (asEvent2Name == "condition") || (asEvent2Name == "traintriggered"))
|
||||
{ // drugiego eventu może nie być, bo są z tym problemy, ale ciii...
|
||||
token = asEvent2Name; // rozpoznane słowo idzie do dalszego przetwarzania
|
||||
asEvent2Name = "none"; // a drugiego eventu nie ma
|
||||
}
|
||||
else
|
||||
{ // gdy są dwa eventy
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
//str = AnsiString(token.c_str());
|
||||
}
|
||||
if (token == "condition")
|
||||
{ // obsługa wyzwalania warunkowego
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
asMemCellName = token;
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
szText = token;
|
||||
if (token != "*") //*=nie brać command pod uwagę
|
||||
iCheckMask |= basic_event::flags::text;
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
if (token != "*") //*=nie brać wartości 1. pod uwagę
|
||||
{
|
||||
iCheckMask |= basic_event::flags::value1;
|
||||
fVal1 = atof(token.c_str());
|
||||
}
|
||||
else
|
||||
fVal1 = 0;
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
if (token.compare("*") != 0) //*=nie brać wartości 2. pod uwagę
|
||||
{
|
||||
iCheckMask |= basic_event::flags::value2;
|
||||
fVal2 = atof(token.c_str());
|
||||
}
|
||||
else
|
||||
fVal2 = 0;
|
||||
parser->getTokens(); // słowo zamykające
|
||||
*parser >> token;
|
||||
}
|
||||
if (token == "traintriggered")
|
||||
{
|
||||
train_triggered = true;
|
||||
}
|
||||
|
||||
if( DeltaTime < 0 )
|
||||
DeltaTime = -DeltaTime; // dla ujemnego zmieniamy na dodatni
|
||||
else if( DeltaTime > 0 ) { // wartość dodatnia oznacza wyzwalanie o określonej godzinie
|
||||
iMinute = int( DeltaTime ) % 100; // minuty są najmłodszymi cyframi dziesietnymi
|
||||
iHour = int( DeltaTime - iMinute ) / 100; // godzina to setki
|
||||
DeltaTime = 0; // bez powtórzeń
|
||||
// potentially shift the provided time by requested offset
|
||||
auto const timeoffset{ static_cast<int>( Global.ScenarioTimeOffset * 60 ) };
|
||||
if( timeoffset != 0 ) {
|
||||
auto const adjustedtime{ clamp_circular( iHour * 60 + iMinute + timeoffset, 24 * 60 ) };
|
||||
iHour = ( adjustedtime / 60 ) % 24;
|
||||
iMinute = adjustedtime % 60;
|
||||
}
|
||||
|
||||
WriteLog(
|
||||
"EventLauncher at "
|
||||
+ std::to_string( iHour ) + ":"
|
||||
+ ( iMinute < 10 ? "0" : "" ) + to_string( iMinute )
|
||||
+ " (" + asEvent1Name
|
||||
+ ( asEvent2Name != "none" ? " / " + asEvent2Name : "" )
|
||||
+ ")" ); // wyświetlenie czasu
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TEventLauncher::check_activation_key() {
|
||||
if (iKey <= 0)
|
||||
return false;
|
||||
|
||||
char key = iKey & 0xff;
|
||||
|
||||
bool result = Console::Pressed(key);
|
||||
|
||||
char modifier = iKey >> 8;
|
||||
if (modifier & GLFW_MOD_SHIFT)
|
||||
result &= Global.shiftState;
|
||||
if (modifier & GLFW_MOD_CONTROL)
|
||||
result &= Global.ctrlState;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool TEventLauncher::check_activation() {
|
||||
|
||||
auto bCond { false };
|
||||
|
||||
if (DeltaTime == 10000.0) {
|
||||
if (UpdatedTime == 0.0)
|
||||
bCond = true;
|
||||
UpdatedTime = 1.0;
|
||||
}
|
||||
else if( DeltaTime > 0 ) {
|
||||
if( UpdatedTime > DeltaTime ) {
|
||||
UpdatedTime = 0; // naliczanie od nowa
|
||||
bCond = true;
|
||||
}
|
||||
else {
|
||||
// aktualizacja naliczania czasu
|
||||
UpdatedTime += Timer::GetDeltaTime();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// jeśli nie cykliczny, to sprawdzić czas
|
||||
if( simulation::Time.data().wHour == iHour ) {
|
||||
if( simulation::Time.data().wMinute == iMinute ) {
|
||||
// zgodność czasu uruchomienia
|
||||
if( UpdatedTime < 10 ) {
|
||||
UpdatedTime = 20; // czas do kolejnego wyzwolenia?
|
||||
bCond = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
UpdatedTime = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return bCond;
|
||||
}
|
||||
|
||||
bool TEventLauncher::check_conditions() {
|
||||
|
||||
auto bCond { true };
|
||||
|
||||
if( ( iCheckMask != 0 )
|
||||
&& ( MemCell != nullptr ) ) {
|
||||
// sprawdzanie warunku na komórce pamięci
|
||||
bCond = MemCell->Compare( szText, fVal1, fVal2, iCheckMask );
|
||||
}
|
||||
|
||||
return bCond; // sprawdzanie dRadius w Ground.cpp
|
||||
}
|
||||
|
||||
// sprawdzenie, czy jest globalnym wyzwalaczem czasu
|
||||
bool TEventLauncher::IsGlobal() const {
|
||||
|
||||
return ( ( DeltaTime == 0 )
|
||||
&& ( iHour >= 0 )
|
||||
&& ( iMinute >= 0 )
|
||||
&& ( dRadius < 0.0 ) ); // bez ograniczenia zasięgu
|
||||
}
|
||||
|
||||
bool TEventLauncher::IsRadioActivated() const {
|
||||
|
||||
return ( iKey < 0 );
|
||||
}
|
||||
|
||||
// radius() subclass details, calculates node's bounding radius
|
||||
float
|
||||
TEventLauncher::radius_() {
|
||||
|
||||
return std::sqrt( dRadius );
|
||||
}
|
||||
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void
|
||||
TEventLauncher::serialize_( std::ostream &Output ) const {
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void
|
||||
TEventLauncher::deserialize_( std::istream &Input ) {
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void
|
||||
TEventLauncher::export_as_text_( std::ostream &Output ) const {
|
||||
// header
|
||||
Output << "eventlauncher ";
|
||||
// location
|
||||
Output
|
||||
<< location().x << ' '
|
||||
<< location().y << ' '
|
||||
<< location().z << ' ';
|
||||
// activation radius
|
||||
Output
|
||||
<< ( dRadius > 0 ? std::sqrt( dRadius ) : dRadius ) << ' ';
|
||||
// activation key
|
||||
if( iKey != 0 ) {
|
||||
auto key { iKey & 0xff };
|
||||
auto const modifier { iKey >> 8 };
|
||||
|
||||
if (key >= 'A' && key <= 'Z' && !(modifier & GLFW_MOD_SHIFT))
|
||||
key += 32;
|
||||
|
||||
Output << (char)key;
|
||||
}
|
||||
else {
|
||||
Output << "none ";
|
||||
}
|
||||
// activation interval or hour
|
||||
if( DeltaTime != 0.0 ) {
|
||||
// cyclical launcher
|
||||
Output << -DeltaTime << ' ';
|
||||
}
|
||||
else {
|
||||
// single activation at specified time
|
||||
if( ( iHour < 0 )
|
||||
&& ( iMinute < 0 ) ) {
|
||||
Output << DeltaTime << ' ';
|
||||
}
|
||||
else {
|
||||
// NOTE: activation hour might be affected by user-requested time offset
|
||||
auto const timeoffset{ static_cast<int>( Global.ScenarioTimeOffset * 60 ) };
|
||||
auto const adjustedtime{ clamp_circular( iHour * 60 + iMinute - timeoffset, 24 * 60 ) };
|
||||
Output
|
||||
<< ( adjustedtime / 60 ) % 24
|
||||
<< ( adjustedtime % 60 )
|
||||
<< ' ';
|
||||
}
|
||||
}
|
||||
// associated event(s)
|
||||
Output << ( asEvent1Name.empty() ? "none" : asEvent1Name ) << ' ';
|
||||
Output << ( asEvent2Name.empty() ? "none" : asEvent2Name ) << ' ';
|
||||
if( false == asMemCellName.empty() ) {
|
||||
// conditional event
|
||||
Output
|
||||
<< "condition "
|
||||
<< asMemCellName << ' '
|
||||
<< szText << ' '
|
||||
<< ( ( iCheckMask & basic_event::flags::value1 ) != 0 ? to_string( fVal1 ) : "*" ) << ' '
|
||||
<< ( ( iCheckMask & basic_event::flags::value2 ) != 0 ? to_string( fVal2 ) : "*" ) << ' ';
|
||||
}
|
||||
// footer
|
||||
Output
|
||||
<< "end"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
75
world/EvLaunch.h
Normal file
75
world/EvLaunch.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Classes.h"
|
||||
#include "scenenode.h"
|
||||
|
||||
// radio-transmitted event launch messages
|
||||
enum radio_message {
|
||||
call3 = -3,
|
||||
call1 = -1
|
||||
};
|
||||
|
||||
class TEventLauncher : public scene::basic_node {
|
||||
|
||||
public:
|
||||
// constructor
|
||||
explicit TEventLauncher( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {}
|
||||
// legacy constructor
|
||||
TEventLauncher() = default;
|
||||
|
||||
// methods
|
||||
bool Load( cParser *parser );
|
||||
bool check_activation_key();
|
||||
bool check_activation();
|
||||
// checks conditions associated with the event. returns: true if the conditions are met
|
||||
bool check_conditions();
|
||||
inline
|
||||
auto key() const {
|
||||
return iKey; }
|
||||
bool IsGlobal() const;
|
||||
bool IsRadioActivated() const;
|
||||
// members
|
||||
std::string asEvent1Name;
|
||||
std::string asEvent2Name;
|
||||
std::string asMemCellName;
|
||||
basic_event *Event1 { nullptr };
|
||||
basic_event *Event2 { nullptr };
|
||||
TMemCell *MemCell { nullptr };
|
||||
int iCheckMask { 0 };
|
||||
double dRadius { 0.0 };
|
||||
bool train_triggered { false };
|
||||
|
||||
private:
|
||||
// methods
|
||||
// radius() subclass details, calculates node's bounding radius
|
||||
float radius_();
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void serialize_( std::ostream &Output ) const;
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void deserialize_( std::istream &Input );
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void export_as_text_( std::ostream &Output ) const;
|
||||
|
||||
// members
|
||||
int iKey { 0 };
|
||||
double DeltaTime { -1.0 };
|
||||
double UpdatedTime { 0.0 };
|
||||
double fVal1 { 0.0 };
|
||||
double fVal2 { 0.0 };
|
||||
std::string szText;
|
||||
int iHour { -1 };
|
||||
int iMinute { -1 }; // minuta uruchomienia
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
2577
world/Event.cpp
Normal file
2577
world/Event.cpp
Normal file
File diff suppressed because it is too large
Load Diff
753
world/Event.h
Normal file
753
world/Event.h
Normal file
@@ -0,0 +1,753 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Classes.h"
|
||||
#include "scene.h"
|
||||
#include "Names.h"
|
||||
#include "EvLaunch.h"
|
||||
#include "Logs.h"
|
||||
#include "command.h"
|
||||
#include "comparison.h"
|
||||
|
||||
#ifdef WITH_LUA
|
||||
#include "lua.h"
|
||||
#endif
|
||||
|
||||
// common event interface
|
||||
class basic_event {
|
||||
|
||||
public:
|
||||
// types
|
||||
enum flags {
|
||||
// shared values
|
||||
text = 1 << 0,
|
||||
value1 = 1 << 1,
|
||||
value2 = 1 << 2,
|
||||
// update values
|
||||
mode_add = 1 << 3,
|
||||
// whois
|
||||
mode_alt = 1 << 3,
|
||||
whois_load = 1 << 4,
|
||||
whois_name = 1 << 5,
|
||||
// condition values
|
||||
track_busy = 1 << 3,
|
||||
track_free = 1 << 4,
|
||||
probability = 1 << 5
|
||||
};
|
||||
// constructor
|
||||
basic_event() = default;
|
||||
// destructor
|
||||
virtual ~basic_event();
|
||||
// methods
|
||||
// restores event data from provided stream
|
||||
virtual
|
||||
void
|
||||
deserialize( cParser &Input, scene::scratch_data &Scratchpad );
|
||||
// prepares event for use
|
||||
virtual
|
||||
void
|
||||
init() = 0;
|
||||
// executes event
|
||||
virtual
|
||||
void
|
||||
run();
|
||||
// sends basic content of the class in legacy (text) format to provided stream
|
||||
virtual
|
||||
void
|
||||
export_as_text( std::ostream &Output ) const;
|
||||
// adds a sibling event executed together
|
||||
void
|
||||
append( basic_event *Event );
|
||||
// returns: true if the event should be executed immediately
|
||||
virtual
|
||||
bool
|
||||
is_instant() const;
|
||||
// sends content of associated data cell to specified vehicle controller
|
||||
virtual
|
||||
void
|
||||
send_command( TController &Controller );
|
||||
// returns: true if associated data cell contains a command for vehicle controller
|
||||
virtual
|
||||
bool
|
||||
is_command() const;
|
||||
// input data access
|
||||
virtual std::string input_text() const;
|
||||
virtual TCommandType input_command() const;
|
||||
virtual double input_value( int Index ) const;
|
||||
virtual glm::dvec3 input_location() const;
|
||||
void group( scene::group_handle Group );
|
||||
scene::group_handle group() const;
|
||||
std::string const &name() const { return m_name; }
|
||||
// members
|
||||
basic_event *m_next { nullptr }; // następny w kolejce // TODO: replace with event list in the manager
|
||||
basic_event *m_sibling { nullptr }; // kolejny event z tą samą nazwą - od wersji 378
|
||||
std::string m_name;
|
||||
bool m_ignored { false }; // replacement for tp_ignored
|
||||
bool m_passive { false }; // false gdy ma nie być dodawany do kolejki (skanowanie sygnałów)
|
||||
int m_inqueue { 0 }; // ile razy dodany do kolejki
|
||||
TDynamicObject const *m_activator { nullptr };
|
||||
double m_launchtime { 0.0 };
|
||||
double m_delay { 0.0 };
|
||||
double m_delayrandom { 0.0 }; // zakres dodatkowego opóźnienia // standardowo nie będzie dodatkowego losowego opóźnienia
|
||||
double m_delaydeparture { std::numeric_limits<double>::quiet_NaN() }; // departure-based event delay
|
||||
|
||||
protected:
|
||||
// types
|
||||
using basic_node = std::tuple<std::string, scene::basic_node *>;
|
||||
using node_sequence = std::vector<basic_node>;
|
||||
|
||||
struct event_conditions {
|
||||
unsigned int flags { 0 };
|
||||
float probability { 0.0 }; // used by conditional_probability
|
||||
double memcompare_value1 { 0.0 }; // used by conditional_memcompare
|
||||
double memcompare_value2 { 0.0 }; // used by conditional_memcompare
|
||||
std::string memcompare_text; // used by conditional_memcompare
|
||||
comparison_operator memcompare_value1_operator { comparison_operator::equal }; // used by conditional_memcompare
|
||||
comparison_operator memcompare_value2_operator { comparison_operator::equal }; // used by conditional_memcompare
|
||||
comparison_operator memcompare_text_operator { comparison_operator::equal }; // used by conditional_memcompare
|
||||
comparison_pass memcompare_pass { comparison_pass::all }; // used by conditional_memcompare
|
||||
basic_event::node_sequence *memcompare_cells; // used by conditional_memcompare
|
||||
std::vector<TTrack *> tracks; // used by conditional_track
|
||||
bool has_else { false };
|
||||
|
||||
void deserialize( cParser &Input );
|
||||
void bind( basic_event::node_sequence *Nodes );
|
||||
void init();
|
||||
// verifies whether event meets execution condition(s)
|
||||
bool test() const;
|
||||
// sends basic content of the class in legacy (text) format to provided stream
|
||||
void export_as_text( std::ostream &Output ) const;
|
||||
};
|
||||
// methods
|
||||
template <class TableType_>
|
||||
void init_targets( TableType_ &Repository, std::string const &Targettype, bool const Logerrors = true );
|
||||
// returns true if provided token is a an event desription keyword, false otherwise
|
||||
static bool is_keyword( std::string const &Token );
|
||||
|
||||
// members
|
||||
node_sequence m_targets; // targets of operation performed when this event is executed
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
virtual std::string type() const = 0;
|
||||
// deserialization helper, converts provided string to a list of target nodes
|
||||
virtual void deserialize_targets( std::string const &Input );
|
||||
// deserialize() subclass details
|
||||
virtual void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) = 0;
|
||||
// run() subclass details
|
||||
virtual void run_() = 0;
|
||||
// export_as_text() subclass details
|
||||
virtual void export_as_text_( std::ostream &Output ) const = 0;
|
||||
// members
|
||||
scene::group_handle m_group { null_handle }; // group this event belongs to, if any
|
||||
};
|
||||
|
||||
|
||||
|
||||
// specialized event, sends received input to its target(s)
|
||||
// TBD: replace the generic module with specialized mixins
|
||||
class input_event : public basic_event {
|
||||
|
||||
friend basic_event * make_event( cParser &Input, scene::scratch_data &Scratchpad );
|
||||
|
||||
protected:
|
||||
// types
|
||||
struct input_data {
|
||||
unsigned int flags { 0 };
|
||||
std::string data_text;
|
||||
double data_value_1 { 0.0 };
|
||||
double data_value_2 { 0.0 };
|
||||
basic_node data_source { "", nullptr };
|
||||
glm::dvec3 location { 0.0 };
|
||||
TCommandType command_type { TCommandType::cm_Unknown };
|
||||
|
||||
TMemCell const * data_cell() const;
|
||||
TMemCell * data_cell();
|
||||
};
|
||||
|
||||
// members
|
||||
input_data m_input;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class updatevalues_event : public input_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
// returns: true if the event should be executed immediately
|
||||
bool is_instant() const override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
event_conditions m_conditions;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class copyvalues_event : public input_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class getvalues_event : public input_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
// sends content of associated data cell to specified vehicle controller
|
||||
void send_command( TController &Controller ) override;
|
||||
// returns: true if associated data cell contains a command for vehicle controller
|
||||
bool is_command() const override;
|
||||
// input data access
|
||||
std::string input_text() const override;
|
||||
TCommandType input_command() const override;
|
||||
double input_value( int Index ) const override;
|
||||
glm::dvec3 input_location() const override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class putvalues_event : public input_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
// input data access
|
||||
std::string input_text() const override;
|
||||
TCommandType input_command() const override;
|
||||
double input_value( int Index ) const override;
|
||||
glm::dvec3 input_location() const override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
//determines whether provided input should be passed to consist owner
|
||||
bool is_command_for_owner( input_data const &Input ) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class whois_event : public input_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class logvalues_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class multi_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
std::vector<std::string> dump_children_names() const;
|
||||
|
||||
private:
|
||||
// types
|
||||
// wrapper for binding between editor-supplied name, event, and execution conditional flag
|
||||
using conditional_event = std::tuple<std::string, basic_event *, bool>;
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
std::vector<conditional_event> m_children; // events which are placed in the query when this event is executed
|
||||
event_conditions m_conditions;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class sound_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// types
|
||||
// wrapper for binding between editor-supplied name and sound object
|
||||
using basic_sound = std::tuple<std::string, sound_source *>;
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialization helper, converts provided string to a list of target nodes
|
||||
void deserialize_targets( std::string const &Input ) override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
std::vector<basic_sound> m_sounds;
|
||||
int m_soundmode{ 0 };
|
||||
int m_soundradiochannel{ 0 };
|
||||
};
|
||||
|
||||
|
||||
|
||||
// assigns a texture as specified replacable skin to a list of specified scene model nodes
|
||||
// skin filename is built dynamically using specified expression and list of parameters from optional specified memory cell
|
||||
class texture_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// types
|
||||
struct input_data {
|
||||
basic_node data_source { "", nullptr };
|
||||
|
||||
TMemCell const * data_cell() const;
|
||||
TMemCell * data_cell();
|
||||
};
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
int m_skinindex { 0 }; // index of target replacable skin
|
||||
std::string m_skin; // expression defining skin filename
|
||||
input_data m_input; // optional source of expression parameters
|
||||
};
|
||||
|
||||
|
||||
|
||||
class animation_event : public basic_event {
|
||||
|
||||
public:
|
||||
// destuctor
|
||||
~animation_event() override;
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
int m_animationtype{ 0 };
|
||||
std::array<double, 4> m_animationparams{ 0.0 };
|
||||
std::string m_animationsubmodel;
|
||||
std::list<std::weak_ptr<TAnimContainer>> m_animationcontainers;
|
||||
std::string m_animationfilename;
|
||||
std::size_t m_animationfilesize{ 0 };
|
||||
char *m_animationfiledata{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class lights_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
std::vector<float> m_lights;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class switch_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
int m_switchstate{ 0 };
|
||||
float m_switchmoverate{ -1.f };
|
||||
float m_switchmovedelay{ -1.f };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class track_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
float m_velocity{ 0.f };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class voltage_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
float m_voltage{ -1.f };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class visible_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
bool m_visible{ true };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class friction_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
float m_friction{ -1.f };
|
||||
};
|
||||
|
||||
#ifdef WITH_LUA
|
||||
class lua_event : public basic_event {
|
||||
public:
|
||||
lua_event(lua::eventhandler_t func);
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
std::string type() const override;
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
void run_() override;
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
bool is_instant() const override;
|
||||
|
||||
lua::eventhandler_t lua_func = nullptr;
|
||||
};
|
||||
#endif
|
||||
|
||||
class message_event : public basic_event {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// prepares event for use
|
||||
void init() override;
|
||||
|
||||
private:
|
||||
// methods
|
||||
// event type string
|
||||
std::string type() const override;
|
||||
// deserialize() subclass details
|
||||
void deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) override;
|
||||
// run() subclass details
|
||||
void run_() override;
|
||||
// export_as_text() subclass details
|
||||
void export_as_text_( std::ostream &Output ) const override;
|
||||
// members
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
|
||||
|
||||
basic_event *
|
||||
make_event( cParser &Input, scene::scratch_data &Scratchpad );
|
||||
|
||||
|
||||
|
||||
class event_manager {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
event_manager() = default;
|
||||
// destructor
|
||||
~event_manager();
|
||||
// methods
|
||||
// adds specified event launcher to the list of global launchers
|
||||
void
|
||||
queue( TEventLauncher *Launcher );
|
||||
// inserts in the event query events assigned to event launchers capable of receiving specified radio message sent from specified location
|
||||
void
|
||||
queue_receivers( radio_message const Message, glm::dvec3 const &Location );
|
||||
// legacy method, updates event queues
|
||||
void
|
||||
update();
|
||||
// adds provided event to the collection. returns: true on success
|
||||
// TBD, TODO: return handle to the event
|
||||
bool
|
||||
insert( basic_event *Event );
|
||||
inline
|
||||
bool
|
||||
insert( TEventLauncher *Launcher ) {
|
||||
return (
|
||||
Launcher->IsRadioActivated() ?
|
||||
m_radiodrivenlaunchers.insert( Launcher ) :
|
||||
m_inputdrivenlaunchers.insert( Launcher ) ); }
|
||||
inline void purge (TEventLauncher *Launcher) {
|
||||
m_radiodrivenlaunchers.purge(Launcher);
|
||||
m_inputdrivenlaunchers.purge(Launcher); }
|
||||
// returns first event in the queue
|
||||
inline
|
||||
basic_event *
|
||||
begin() {
|
||||
return QueryRootEvent; }
|
||||
|
||||
basic_event*
|
||||
FindEventById(uint32_t id);
|
||||
uint32_t GetEventId(const basic_event *ev);
|
||||
uint32_t GetEventId(std::string const &Name);
|
||||
|
||||
// legacy method, returns pointer to specified event, or null
|
||||
basic_event *
|
||||
FindEvent( std::string const &Name );
|
||||
inline TEventLauncher* FindEventlauncher(std::string const &Name) {
|
||||
auto ptr = m_inputdrivenlaunchers.find(Name);
|
||||
return ptr ? ptr : m_radiodrivenlaunchers.find(Name);
|
||||
}
|
||||
// legacy method, inserts specified event in the event query
|
||||
bool
|
||||
AddToQuery( basic_event *Event, TDynamicObject const *Owner, double delay = 0.0 );
|
||||
// legacy method, executes queued events
|
||||
bool
|
||||
CheckQuery();
|
||||
// legacy method, initializes events after deserialization from scenario file
|
||||
void
|
||||
InitEvents();
|
||||
// legacy method, initializes event launchers after deserialization from scenario file
|
||||
void
|
||||
InitLaunchers();
|
||||
// sends basic content of the class in legacy (text) format to provided stream
|
||||
void
|
||||
export_as_text( std::ostream &Output ) const;
|
||||
// returns all eventlaunchers in radius ignoring height
|
||||
std::vector<TEventLauncher *>
|
||||
find_eventlaunchers(glm::vec2 center, float radius) const;
|
||||
|
||||
private:
|
||||
// types
|
||||
using event_sequence = std::deque<basic_event *>;
|
||||
using event_map = std::unordered_map<std::string, std::size_t>;
|
||||
using eventlauncher_sequence = std::vector<TEventLauncher *>;
|
||||
// members
|
||||
event_sequence m_events;
|
||||
/*
|
||||
// NOTE: disabled until event class refactoring
|
||||
event_sequence m_eventqueue;
|
||||
*/
|
||||
// legacy version of the above
|
||||
basic_event *QueryRootEvent { nullptr };
|
||||
basic_event *m_workevent { nullptr };
|
||||
event_map m_eventmap;
|
||||
basic_table<TEventLauncher> m_inputdrivenlaunchers;
|
||||
basic_table<TEventLauncher> m_radiodrivenlaunchers;
|
||||
eventlauncher_sequence m_launcherqueue;
|
||||
command_relay m_relay;
|
||||
};
|
||||
|
||||
|
||||
|
||||
inline
|
||||
void
|
||||
basic_event::group( scene::group_handle Group ) {
|
||||
m_group = Group;
|
||||
}
|
||||
|
||||
inline
|
||||
scene::group_handle
|
||||
basic_event::group() const {
|
||||
return m_group;
|
||||
}
|
||||
|
||||
template <class TableType_>
|
||||
void
|
||||
basic_event::init_targets( TableType_ &Repository, std::string const &Targettype, bool const Logerrors ) {
|
||||
|
||||
for( auto &target : m_targets ) {
|
||||
std::get<scene::basic_node *>( target ) = Repository.find( std::get<std::string>( target ) );
|
||||
if( std::get<scene::basic_node *>( target ) == nullptr ) {
|
||||
m_ignored = true; // deaktywacja
|
||||
if( Logerrors )
|
||||
ErrorLog( "Bad event: \"" + m_name + "\" (type: " + type() + ") can't find " + Targettype +" \"" + std::get<std::string>( target ) + "\"" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
283
world/MemCell.cpp
Normal file
283
world/MemCell.cpp
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
/*
|
||||
MaSzyna EU07 locomotive simulator
|
||||
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
|
||||
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "MemCell.h"
|
||||
|
||||
#include "simulation.h"
|
||||
#include "Driver.h"
|
||||
#include "Event.h"
|
||||
#include "Logs.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
TMemCell::TMemCell( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {}
|
||||
|
||||
void TMemCell::UpdateValues( std::string const &szNewText, double const fNewValue1, double const fNewValue2, int const CheckMask )
|
||||
{
|
||||
if (CheckMask & basic_event::flags::mode_add)
|
||||
{ // dodawanie wartości
|
||||
if( TestFlag( CheckMask, basic_event::flags::text ) )
|
||||
szText += szNewText;
|
||||
if (TestFlag(CheckMask, basic_event::flags::value1))
|
||||
fValue1 += fNewValue1;
|
||||
if (TestFlag(CheckMask, basic_event::flags::value2))
|
||||
fValue2 += fNewValue2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( TestFlag( CheckMask, basic_event::flags::text ) )
|
||||
szText = szNewText;
|
||||
if (TestFlag(CheckMask, basic_event::flags::value1))
|
||||
fValue1 = fNewValue1;
|
||||
if (TestFlag(CheckMask, basic_event::flags::value2))
|
||||
fValue2 = fNewValue2;
|
||||
}
|
||||
if (TestFlag(CheckMask, basic_event::flags::text))
|
||||
CommandCheck(); // jeśli zmieniony tekst, próbujemy rozpoznać komendę
|
||||
}
|
||||
|
||||
TCommandType TMemCell::CommandCheck()
|
||||
{ // rozpoznanie komendy
|
||||
if( szText == "SetVelocity" ) // najpopularniejsze
|
||||
{
|
||||
eCommand = TCommandType::cm_SetVelocity;
|
||||
bCommand = false; // ta komenda nie jest wysyłana
|
||||
}
|
||||
else if( szText == "ShuntVelocity" ) // w tarczach manewrowych
|
||||
{
|
||||
eCommand = TCommandType::cm_ShuntVelocity;
|
||||
bCommand = false; // ta komenda nie jest wysyłana
|
||||
}
|
||||
else if( szText == "Change_direction" ) // zdarza się
|
||||
{
|
||||
eCommand = TCommandType::cm_ChangeDirection;
|
||||
bCommand = true; // do wysłania
|
||||
}
|
||||
else if( szText == "OutsideStation" ) // zdarza się
|
||||
{
|
||||
eCommand = TCommandType::cm_OutsideStation;
|
||||
bCommand = false; // tego nie powinno być w komórce
|
||||
}
|
||||
else if( starts_with( szText, "PassengerStopPoint:" ) ) // porównanie początków
|
||||
{
|
||||
eCommand = TCommandType::cm_PassengerStopPoint;
|
||||
bCommand = false; // tego nie powinno być w komórce
|
||||
}
|
||||
else if( szText == "SetProximityVelocity" ) // nie powinno tego być
|
||||
{
|
||||
eCommand = TCommandType::cm_SetProximityVelocity;
|
||||
bCommand = false; // ta komenda nie jest wysyłana
|
||||
}
|
||||
else if( szText == "Emergency_brake" )
|
||||
{
|
||||
eCommand = TCommandType::cm_EmergencyBrake;
|
||||
bCommand = false;
|
||||
}
|
||||
else if( szText == "CabSignal" ) {
|
||||
eCommand = TCommandType::cm_SecuritySystemMagnet;
|
||||
bCommand = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
eCommand = TCommandType::cm_Unknown; // ciąg nierozpoznany (nie jest komendą)
|
||||
bCommand = true; // do wysłania
|
||||
}
|
||||
return eCommand;
|
||||
}
|
||||
|
||||
bool TMemCell::Load(cParser *parser)
|
||||
{
|
||||
std::string token;
|
||||
parser->getTokens( 6, false );
|
||||
*parser
|
||||
>> m_area.center.x
|
||||
>> m_area.center.y
|
||||
>> m_area.center.z
|
||||
>> szText
|
||||
>> fValue1
|
||||
>> fValue2;
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
if (token != "none") // gdy różne od "none"
|
||||
asTrackName = token; // sprawdzane przez IsEmpty()
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
if (token != "endmemcell")
|
||||
Error("endmemcell statement missing");
|
||||
CommandCheck();
|
||||
return true;
|
||||
}
|
||||
|
||||
void TMemCell::PutCommand( TController *Mech, glm::dvec3 const *Loc ) const
|
||||
{ // wysłanie zawartości komórki do AI
|
||||
if (Mech)
|
||||
Mech->PutCommand(szText, fValue1, fValue2, Loc);
|
||||
}
|
||||
|
||||
bool TMemCell::Compare( std::string const &szTestText, double const fTestValue1, double const fTestValue2, int const CheckMask,
|
||||
comparison_operator const TextOperator, comparison_operator const Value1Operator, comparison_operator const Value2Operator,
|
||||
comparison_pass const Pass ) const {
|
||||
// porównanie zawartości komórki pamięci z podanymi wartościami
|
||||
/*
|
||||
if( TestFlag( CheckMask, basic_event::flags::text ) ) {
|
||||
// porównać teksty
|
||||
auto range = szTestText.find( '*' );
|
||||
if( range != std::string::npos ) {
|
||||
// compare string parts
|
||||
if( 0 != szText.compare( 0, range, szTestText, 0, range ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// compare full strings
|
||||
if( szText != szTestText ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// tekst zgodny, porównać resztę
|
||||
return ( ( !TestFlag( CheckMask, basic_event::flags::value1 ) || ( fValue1 == fTestValue1 ) )
|
||||
&& ( !TestFlag( CheckMask, basic_event::flags::value2 ) || ( fValue2 == fTestValue2 ) ) );
|
||||
*/
|
||||
bool checkpassed { false };
|
||||
bool checkfailed { false };
|
||||
|
||||
if( TestFlag( CheckMask, basic_event::flags::text ) ) {
|
||||
// porównać teksty
|
||||
auto range = szTestText.find( '*' );
|
||||
auto const result { (
|
||||
range == std::string::npos ?
|
||||
compare( szText, szTestText, TextOperator ) :
|
||||
compare( szText.substr( 0, range ), szTestText.substr( 0, range ), TextOperator ) ) };
|
||||
checkpassed |= result;
|
||||
checkfailed |= ( !result );
|
||||
}
|
||||
if( TestFlag( CheckMask, basic_event::flags::value1 ) ) {
|
||||
auto const result { compare( fValue1, fTestValue1, Value1Operator ) };
|
||||
checkpassed |= result;
|
||||
checkfailed |= ( !result );
|
||||
}
|
||||
if( TestFlag( CheckMask, basic_event::flags::value2 ) ) {
|
||||
auto const result { compare( fValue2, fTestValue2, Value2Operator ) };
|
||||
checkpassed |= result;
|
||||
checkfailed |= ( !result );
|
||||
}
|
||||
|
||||
switch( Pass ) {
|
||||
case comparison_pass::all: { return ( checkfailed == false ); }
|
||||
case comparison_pass::any: { return ( checkpassed == true ); }
|
||||
case comparison_pass::none: { return ( checkpassed == false ); }
|
||||
default: { return false; }
|
||||
}
|
||||
};
|
||||
|
||||
bool TMemCell::IsVelocity() const
|
||||
{ // sprawdzenie, czy event odczytu tej komórki ma być do skanowania, czy do kolejkowania
|
||||
if (eCommand == TCommandType::cm_SetVelocity)
|
||||
return true;
|
||||
if (eCommand == TCommandType::cm_ShuntVelocity)
|
||||
return true;
|
||||
return (eCommand == TCommandType::cm_SetProximityVelocity);
|
||||
};
|
||||
|
||||
void TMemCell::StopCommandSent()
|
||||
{ //
|
||||
if (!bCommand)
|
||||
return;
|
||||
bCommand = false;
|
||||
if( OnSent ) {
|
||||
// jeśli jest event
|
||||
simulation::Events.AddToQuery( OnSent, nullptr );
|
||||
}
|
||||
};
|
||||
|
||||
void TMemCell::AssignEvents(basic_event *e)
|
||||
{ // powiązanie eventu
|
||||
OnSent = e;
|
||||
};
|
||||
|
||||
std::string TMemCell::Values() const {
|
||||
|
||||
return { "[" + Text() + "] [" + to_string( Value1(), 2 ) + "] [" + to_string( Value2(), 2 ) + "]" };
|
||||
}
|
||||
|
||||
void TMemCell::LogValues() const {
|
||||
|
||||
WriteLog( "Memcell " + name() + ": " + Values() );
|
||||
}
|
||||
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void
|
||||
TMemCell::serialize_( std::ostream &Output ) const {
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void
|
||||
TMemCell::deserialize_( std::istream &Input ) {
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void
|
||||
TMemCell::export_as_text_( std::ostream &Output ) const {
|
||||
// header
|
||||
Output << "memcell ";
|
||||
// location
|
||||
Output
|
||||
<< location().x << ' '
|
||||
<< location().y << ' '
|
||||
<< location().z << ' '
|
||||
// cell data
|
||||
<< szText << ' '
|
||||
<< fValue1 << ' '
|
||||
<< fValue2 << ' '
|
||||
// associated track
|
||||
<< ( Track ? Track->name() : asTrackName.empty() ? "none" : asTrackName ) << ' '
|
||||
// footer
|
||||
<< "endmemcell"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// legacy method, initializes traction after deserialization from scenario file
|
||||
void
|
||||
memory_table::InitCells() {
|
||||
|
||||
for( auto *cell : m_items ) {
|
||||
// Ra: eventy komórek pamięci, wykonywane po wysłaniu komendy do zatrzymanego pojazdu
|
||||
cell->AssignEvents( simulation::Events.FindEvent( cell->name() + ":sent" ) );
|
||||
// bind specified path with the memory cell
|
||||
if( false == cell->asTrackName.empty() ) {
|
||||
cell->Track = simulation::Paths.find( cell->asTrackName );
|
||||
if( cell->Track == nullptr ) {
|
||||
ErrorLog( "Bad memcell: track \"" + cell->asTrackName + "\" referenced in memcell \"" + cell->name() + "\" doesn't exist" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// legacy method, sends content of all cells to the log
|
||||
void
|
||||
memory_table::log_all() {
|
||||
|
||||
for( auto *cell : m_items ) {
|
||||
cell->LogValues();
|
||||
}
|
||||
}
|
||||
94
world/MemCell.h
Normal file
94
world/MemCell.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Classes.h"
|
||||
#include "scenenode.h"
|
||||
#include "Names.h"
|
||||
#include "comparison.h"
|
||||
|
||||
class TMemCell : public scene::basic_node {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
explicit TMemCell( scene::node_data const &Nodedata );
|
||||
// methods
|
||||
void
|
||||
UpdateValues( std::string const &szNewText, double const fNewValue1, double const fNewValue2, int const CheckMask );
|
||||
bool
|
||||
Load(cParser *parser);
|
||||
void
|
||||
PutCommand( TController *Mech, glm::dvec3 const *Loc ) const;
|
||||
bool
|
||||
Compare( std::string const &szTestText, double const fTestValue1, double const fTestValue2, int const CheckMask,
|
||||
comparison_operator const TextOperator = comparison_operator::equal,
|
||||
comparison_operator const Value1Operator = comparison_operator::equal,
|
||||
comparison_operator const Value2Operator = comparison_operator::equal,
|
||||
comparison_pass const Pass = comparison_pass::all ) const;
|
||||
std::string const &
|
||||
Text() const {
|
||||
return szText; }
|
||||
double
|
||||
Value1() const {
|
||||
return fValue1; };
|
||||
double
|
||||
Value2() const {
|
||||
return fValue2; };
|
||||
TCommandType
|
||||
Command() const {
|
||||
return eCommand; };
|
||||
bool
|
||||
StopCommand() const {
|
||||
return bCommand; };
|
||||
void StopCommandSent();
|
||||
TCommandType CommandCheck();
|
||||
bool IsVelocity() const;
|
||||
void AssignEvents(basic_event *e);
|
||||
std::string Values() const;
|
||||
void LogValues() const;
|
||||
// members
|
||||
std::string asTrackName; // McZapkie-100302 - zeby nazwe toru na ktory jest Putcommand wysylane pamietac
|
||||
TTrack *Track { nullptr }; // resolved binding with the specified track
|
||||
bool is_exportable { true }; // export helper; autogenerated cells don't need to be exported
|
||||
|
||||
private:
|
||||
// methods
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void serialize_( std::ostream &Output ) const;
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void deserialize_( std::istream &Input );
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void export_as_text_( std::ostream &Output ) const;
|
||||
|
||||
// members
|
||||
// content
|
||||
std::string szText;
|
||||
double fValue1 { 0.0 };
|
||||
double fValue2 { 0.0 };
|
||||
// other
|
||||
TCommandType eCommand { TCommandType::cm_Unknown };
|
||||
bool bCommand { false }; // czy zawiera komendę dla zatrzymanego AI
|
||||
basic_event *OnSent { nullptr }; // event dodawany do kolejki po wysłaniu komendy zatrzymującej skład
|
||||
};
|
||||
|
||||
|
||||
|
||||
class memory_table : public basic_table<TMemCell> {
|
||||
|
||||
public:
|
||||
// legacy method, initializes traction after deserialization from scenario file
|
||||
void
|
||||
InitCells();
|
||||
// legacy method, sends content of all cells to the log
|
||||
void
|
||||
log_all();
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
577
world/Segment.cpp
Normal file
577
world/Segment.cpp
Normal file
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Segment.h"
|
||||
|
||||
#include "Globals.h"
|
||||
#include "Logs.h"
|
||||
#include "parser.h"
|
||||
#include "utilities.h"
|
||||
#include "Track.h"
|
||||
#include "renderer.h"
|
||||
|
||||
void
|
||||
segment_data::deserialize( cParser &Input, glm::dvec3 const &Offset ) {
|
||||
|
||||
points[ segment_data::point::start ] = LoadPoint( Input ) + Offset;
|
||||
Input.getTokens();
|
||||
Input >> rolls[ 0 ];
|
||||
points[ segment_data::point::control1 ] = LoadPoint( Input );
|
||||
points[ segment_data::point::control2 ] = LoadPoint( Input );
|
||||
points[ segment_data::point::end ] = LoadPoint( Input ) + Offset;
|
||||
Input.getTokens( 2 );
|
||||
Input
|
||||
>> rolls[ 1 ]
|
||||
>> radius;
|
||||
}
|
||||
|
||||
|
||||
TSegment::TSegment(TTrack *owner) :
|
||||
pOwner( owner )
|
||||
{}
|
||||
|
||||
bool TSegment::Init(Math3D::vector3 NewPoint1, Math3D::vector3 NewPoint2, double fNewStep, double fNewRoll1, double fNewRoll2)
|
||||
{ // wersja dla prostego - wyliczanie punktów kontrolnych
|
||||
Math3D::vector3 dir;
|
||||
|
||||
// NOTE: we're enforcing division also for straight track, to ensure dense enough mesh for per-vertex lighting
|
||||
/*
|
||||
if (fNewRoll1 == fNewRoll2)
|
||||
{ // faktyczny prosty
|
||||
dir = Normalize(NewPoint2 - NewPoint1); // wektor kierunku o długości 1
|
||||
return TSegment::Init(NewPoint1, dir, -dir, NewPoint2, fNewStep, fNewRoll1, fNewRoll2,
|
||||
false);
|
||||
}
|
||||
else
|
||||
*/
|
||||
{ // prosty ze zmienną przechyłką musi być segmentowany jak krzywe
|
||||
dir = (NewPoint2 - NewPoint1) / 3.0; // punkty kontrolne prostego są w 1/3 długości
|
||||
return TSegment::Init(
|
||||
NewPoint1, NewPoint1 + dir,
|
||||
NewPoint2 - dir, NewPoint2,
|
||||
fNewStep, fNewRoll1, fNewRoll2, true);
|
||||
}
|
||||
};
|
||||
|
||||
bool TSegment::Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, Math3D::vector3 NewCPointIn, Math3D::vector3 &NewPoint2, double fNewStep, double fNewRoll1, double fNewRoll2, bool bIsCurve)
|
||||
{ // wersja uniwersalna (dla krzywej i prostego)
|
||||
Point1 = NewPoint1;
|
||||
CPointOut = NewCPointOut;
|
||||
CPointIn = NewCPointIn;
|
||||
Point2 = NewPoint2;
|
||||
// poprawienie przechyłki
|
||||
fRoll1 = glm::radians(fNewRoll1); // Ra: przeliczone jest bardziej przydatne do obliczeń
|
||||
fRoll2 = glm::radians(fNewRoll2);
|
||||
bCurve = bIsCurve;
|
||||
if (Global.bRollFix)
|
||||
{ // Ra: poprawianie przechyłki
|
||||
// Przechyłka powinna być na środku wewnętrznej szyny, a standardowo jest w osi
|
||||
// toru. Dlatego trzeba podnieść tor oraz odpowiednio podwyższyć podsypkę.
|
||||
// Nie wykonywać tej funkcji, jeśli podwyższenie zostało uwzględnione w edytorze.
|
||||
// Problematyczne mogą byc rozjazdy na przechyłce - lepiej je modelować w edytorze.
|
||||
// Na razie wszystkie scenerie powinny być poprawiane.
|
||||
// Jedynie problem będzie z podwójną rampą przechyłkową, która w środku będzie
|
||||
// mieć moment wypoziomowania, ale musi on być również podniesiony.
|
||||
if (fRoll1 != 0.0)
|
||||
{ // tylko jeśli jest przechyłka
|
||||
double w1 = std::abs(std::sin(fRoll1) * 0.75); // 0.5*w2+0.0325; //0.75m dla 1.435
|
||||
Point1.y += w1; // modyfikacja musi być przed policzeniem dalszych parametrów
|
||||
if (bCurve)
|
||||
CPointOut.y += w1; // prosty ma wektory jednostkowe
|
||||
pOwner->MovedUp1(w1); // zwrócić trzeba informację o podwyższeniu podsypki
|
||||
}
|
||||
if (fRoll2 != 0.f)
|
||||
{
|
||||
double w2 = std::abs(std::sin(fRoll2) * 0.75); // 0.5*w2+0.0325; //0.75m dla 1.435
|
||||
Point2.y += w2; // modyfikacja musi być przed policzeniem dalszych parametrów
|
||||
if (bCurve)
|
||||
CPointIn.y += w2; // prosty ma wektory jednostkowe
|
||||
// zwrócić trzeba informację o podwyższeniu podsypki
|
||||
}
|
||||
}
|
||||
// kąt w planie, żeby nie liczyć wielokrotnie
|
||||
// Ra: ten kąt jeszcze do przemyślenia jest
|
||||
fDirection = -std::atan2(Point2.x - Point1.x, Point2.z - Point1.z);
|
||||
if (bCurve)
|
||||
{ // przeliczenie współczynników wielomianu, będzie mniej mnożeń i można policzyć pochodne
|
||||
vC = 3.0 * (CPointOut - Point1); // t^1
|
||||
vB = 3.0 * (CPointIn - CPointOut) - vC; // t^2
|
||||
vA = Point2 - Point1 - vC - vB; // t^3
|
||||
fLength = ComputeLength();
|
||||
}
|
||||
else {
|
||||
fLength = ( Point1 - Point2 ).Length();
|
||||
}
|
||||
if (fLength <= 0) {
|
||||
|
||||
ErrorLog( "Bad track: zero length spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" );
|
||||
fLength = 0.01; // crude workaround TODO: fix this properly
|
||||
}
|
||||
|
||||
fStoop = std::atan2((Point2.y - Point1.y), fLength); // pochylenie toru prostego, żeby nie liczyć wielokrotnie
|
||||
|
||||
fStep = fNewStep;
|
||||
// NOTE: optionally replace this part with the commented version, after solving geometry issues with double switches
|
||||
if( ( pOwner->eType == tt_Switch )
|
||||
&& ( fStep * ( 3.0 * Global.SplineFidelity ) > fLength ) ) {
|
||||
// NOTE: a workaround for too short switches (less than 3 segments) messing up animation/generation of blades
|
||||
fStep = fLength / ( 3.0 * Global.SplineFidelity );
|
||||
}
|
||||
// iSegCount = static_cast<int>( std::ceil( fLength / fStep ) ); // potrzebne do VBO
|
||||
iSegCount = (
|
||||
pOwner->eType == tt_Switch ?
|
||||
6 * Global.SplineFidelity :
|
||||
static_cast<int>( std::ceil( fLength / fStep ) ) ); // potrzebne do VBO
|
||||
|
||||
fStep = fLength / iSegCount; // update step to equalize size of individual pieces
|
||||
|
||||
fTsBuffer.resize( iSegCount + 1 );
|
||||
fTsBuffer[ 0 ] = 0.0;
|
||||
for( int i = 1; i < iSegCount; ++i ) {
|
||||
fTsBuffer[ i ] = GetTFromS( i * fStep );
|
||||
}
|
||||
fTsBuffer[ iSegCount ] = 1.0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Math3D::vector3 TSegment::GetFirstDerivative(double const fTime) const
|
||||
{
|
||||
|
||||
double fOmTime = 1.0 - fTime;
|
||||
double fPowTime = fTime;
|
||||
Math3D::vector3 kResult = fOmTime * (CPointOut - Point1);
|
||||
|
||||
// int iDegreeM1 = 3 - 1;
|
||||
|
||||
double fCoeff = 2 * fPowTime;
|
||||
kResult = (kResult + fCoeff * (CPointIn - CPointOut)) * fOmTime;
|
||||
fPowTime *= fTime;
|
||||
|
||||
kResult += fPowTime * (Point2 - CPointIn);
|
||||
kResult *= 3;
|
||||
|
||||
return kResult;
|
||||
}
|
||||
|
||||
double TSegment::RombergIntegral(double const fA, double const fB) const
|
||||
{
|
||||
double fH = fB - fA;
|
||||
|
||||
const int ms_iOrder = 5;
|
||||
|
||||
double ms_apfRom[2][ms_iOrder];
|
||||
|
||||
ms_apfRom[0][0] =
|
||||
0.5 * fH * ((GetFirstDerivative(fA).Length()) + (GetFirstDerivative(fB).Length()));
|
||||
for (int i0 = 2, iP0 = 1; i0 <= ms_iOrder; i0++, iP0 *= 2, fH *= 0.5)
|
||||
{
|
||||
// approximations via the trapezoid rule
|
||||
double fSum = 0.0;
|
||||
int i1;
|
||||
for (i1 = 1; i1 <= iP0; i1++)
|
||||
fSum += (GetFirstDerivative(fA + fH * (i1 - 0.5)).Length());
|
||||
|
||||
// Richardson extrapolation
|
||||
ms_apfRom[1][0] = 0.5 * (ms_apfRom[0][0] + fH * fSum);
|
||||
for (int i2 = 1, iP2 = 4; i2 < i0; i2++, iP2 *= 4)
|
||||
{
|
||||
ms_apfRom[1][i2] = (iP2 * ms_apfRom[1][i2 - 1] - ms_apfRom[0][i2 - 1]) / (iP2 - 1);
|
||||
}
|
||||
|
||||
for (i1 = 0; i1 < i0; i1++)
|
||||
ms_apfRom[0][i1] = ms_apfRom[1][i1];
|
||||
}
|
||||
|
||||
return ms_apfRom[0][ms_iOrder - 1];
|
||||
}
|
||||
|
||||
double TSegment::GetTFromS(double const s) const
|
||||
{
|
||||
// initial guess for Newton's method
|
||||
double fTolerance = 0.001;
|
||||
double fRatio = s / RombergIntegral(0, 1);
|
||||
double fTime = interpolate( 0.0, 1.0, fRatio );
|
||||
|
||||
int iteration = 0;
|
||||
double fDifference {}; // exposed for debug down the road
|
||||
do {
|
||||
fDifference = RombergIntegral(0, fTime) - s;
|
||||
if( std::abs( fDifference ) < fTolerance ) {
|
||||
return fTime;
|
||||
}
|
||||
fTime -= fDifference / GetFirstDerivative(fTime).Length();
|
||||
++iteration;
|
||||
}
|
||||
while( iteration < 10 ); // arbitrary limit
|
||||
|
||||
// Newton's method failed. If this happens, increase iterations or
|
||||
// tolerance or integration accuracy.
|
||||
// return -1; //Ra: tu nigdy nie dojdzie
|
||||
ErrorLog( "Bad track: shape estimation failed for spline \"" + pOwner->name() + "\" (location: " + to_string( glm::dvec3{ Point1 } ) + ")" );
|
||||
// MessageBox(0,"Too many iterations","GetTFromS",MB_OK);
|
||||
return fTime;
|
||||
};
|
||||
|
||||
Math3D::vector3 TSegment::RaInterpolate(double const t) const
|
||||
{ // wyliczenie XYZ na krzywej Beziera z użyciem współczynników
|
||||
return t * (t * (t * vA + vB) + vC) + Point1; // 9 mnożeń, 9 dodawań
|
||||
};
|
||||
|
||||
Math3D::vector3 TSegment::RaInterpolate0(double const t) const
|
||||
{ // wyliczenie XYZ na krzywej Beziera, na użytek liczenia długości nie jest dodawane Point1
|
||||
return t * (t * (t * vA + vB) + vC); // 9 mnożeń, 6 dodawań
|
||||
};
|
||||
|
||||
double TSegment::ComputeLength() const // McZapkie-150503: dlugosc miedzy punktami krzywej
|
||||
{ // obliczenie długości krzywej Beziera za pomocą interpolacji odcinkami
|
||||
// Ra: zamienić na liczenie rekurencyjne średniej z cięciwy i łamanej po kontrolnych
|
||||
// Ra: koniec rekurencji jeśli po podziale suma długości nie różni się więcej niż 0.5mm od
|
||||
// poprzedniej
|
||||
// Ra: ewentualnie rozpoznać łuk okręgu płaskiego i liczyć ze wzoru na długość łuku
|
||||
double t, l = 0;
|
||||
Math3D::vector3 last = Math3D::vector3(0, 0, 0); // długość liczona po przesunięciu odcinka do początku układu
|
||||
Math3D::vector3 tmp = Point2 - Point1;
|
||||
int m = 20.0 * tmp.Length(); // było zawsze do 10000, teraz jest liczone odcinkami po około 5cm
|
||||
for (int i = 1; i <= m; i++)
|
||||
{
|
||||
t = double(i) / double(m); // wyznaczenie parametru na krzywej z przedziału (0,1>
|
||||
// tmp=Interpolate(t,p1,cp1,cp2,p2);
|
||||
tmp = RaInterpolate0(t); // obliczenie punktu dla tego parametru
|
||||
t = Math3D::vector3(tmp - last).Length(); // obliczenie długości wektora
|
||||
l += t; // zwiększenie wyliczanej długości
|
||||
last = tmp;
|
||||
}
|
||||
return (l);
|
||||
}
|
||||
|
||||
// finds point on segment closest to specified point in 3d space. returns: point on segment as value in range 0-1
|
||||
double
|
||||
TSegment::find_nearest_point( glm::dvec3 const &Point ) const {
|
||||
|
||||
if( ( false == bCurve ) || ( iSegCount == 1 ) ) {
|
||||
// for straight track just treat it as a single segment
|
||||
return
|
||||
nearest_segment_point(
|
||||
glm::dvec3{ FastGetPoint_0() },
|
||||
glm::dvec3{ FastGetPoint_1() },
|
||||
Point );
|
||||
}
|
||||
else {
|
||||
// for curves iterate through segment chunks, and find the one which gives us the least distance to the specified point
|
||||
double distance = std::numeric_limits<double>::max();
|
||||
double nearest;
|
||||
// NOTE: we're reusing already created segment chunks, which are created based on splinefidelity setting
|
||||
// this means depending on splinefidelity the results can be potentially slightly different
|
||||
for( int segmentidx = 0; segmentidx < iSegCount; ++segmentidx ) {
|
||||
|
||||
auto const segmentpoint =
|
||||
clamp(
|
||||
nearest_segment_point(
|
||||
glm::dvec3{ FastGetPoint( fTsBuffer[ segmentidx ] ) },
|
||||
glm::dvec3{ FastGetPoint( fTsBuffer[ segmentidx + 1 ] ) },
|
||||
Point ) // point in range 0-1 on current segment
|
||||
* ( fTsBuffer[ segmentidx + 1 ] - fTsBuffer[ segmentidx ] ) // segment length
|
||||
+ fTsBuffer[ segmentidx ], // segment offset
|
||||
0.0, 1.0 ); // we clamp the range in case there's some floating point math inaccuracies
|
||||
|
||||
auto const segmentdistance = glm::length2( Point - glm::dvec3{ FastGetPoint( segmentpoint ) } );
|
||||
if( segmentdistance < distance ) {
|
||||
|
||||
nearest = segmentpoint;
|
||||
distance = segmentdistance;
|
||||
}
|
||||
}
|
||||
//
|
||||
return nearest;
|
||||
}
|
||||
}
|
||||
|
||||
const double fDirectionOffset = 0.1; // długość wektora do wyliczenia kierunku
|
||||
|
||||
Math3D::vector3 TSegment::GetDirection(double const fDistance) const
|
||||
{ // takie toporne liczenie pochodnej dla podanego dystansu od Point1
|
||||
double t1 = GetTFromS(fDistance - fDirectionOffset);
|
||||
if (t1 <= 0.0)
|
||||
return (CPointOut - Point1); // na zewnątrz jako prosta
|
||||
double t2 = GetTFromS(fDistance + fDirectionOffset);
|
||||
if (t2 >= 1.0)
|
||||
return (Point1 - CPointIn); // na zewnątrz jako prosta
|
||||
return (FastGetPoint(t2) - FastGetPoint(t1));
|
||||
}
|
||||
|
||||
Math3D::vector3 TSegment::FastGetDirection(double fDistance, double fOffset)
|
||||
{ // takie toporne liczenie pochodnej dla parametru 0.0÷1.0
|
||||
double t1 = fDistance - fOffset;
|
||||
if (t1 <= 0.0)
|
||||
return (CPointOut - Point1); // wektor na początku jest stały
|
||||
double t2 = fDistance + fOffset;
|
||||
if (t2 >= 1.0)
|
||||
return (Point2 - CPointIn); // wektor na końcu jest stały
|
||||
return (FastGetPoint(t2) - FastGetPoint(t1));
|
||||
}
|
||||
/*
|
||||
Math3D::vector3 TSegment::GetPoint(double const fDistance) const
|
||||
{ // wyliczenie współrzędnych XYZ na torze w odległości (fDistance) od Point1
|
||||
if (bCurve)
|
||||
{ // można by wprowadzić uproszczony wzór dla okręgów płaskich
|
||||
double t = GetTFromS(fDistance); // aproksymacja dystansu na krzywej Beziera
|
||||
// return Interpolate(t,Point1,CPointOut,CPointIn,Point2);
|
||||
return RaInterpolate(t);
|
||||
}
|
||||
else {
|
||||
// wyliczenie dla odcinka prostego jest prostsze
|
||||
return
|
||||
interpolate(
|
||||
Point1, Point2,
|
||||
clamp(
|
||||
fDistance / fLength,
|
||||
0.0, 1.0 ) );
|
||||
}
|
||||
};
|
||||
*/
|
||||
// ustalenie pozycji osi na torze, przechyłki, pochylenia i kierunku jazdy
|
||||
void TSegment::RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const {
|
||||
|
||||
if (bCurve) {
|
||||
// można by wprowadzić uproszczony wzór dla okręgów płaskich
|
||||
auto const t = GetTFromS(fDistance); // aproksymacja dystansu na krzywej Beziera na parametr (t)
|
||||
p = FastGetPoint( t );
|
||||
// przechyłka w danym miejscu (zmienia się liniowo)
|
||||
a.x = interpolate<double>( fRoll1, fRoll2, t );
|
||||
// pochodna jest 3*A*t^2+2*B*t+C
|
||||
auto const tangent = t * ( t * 3.0 * vA + vB + vB ) + vC;
|
||||
// pochylenie krzywej (w pionie)
|
||||
a.y = std::atan( tangent.y );
|
||||
// kierunek krzywej w planie
|
||||
a.z = -std::atan2( tangent.x, tangent.z );
|
||||
}
|
||||
else {
|
||||
// wyliczenie dla odcinka prostego jest prostsze
|
||||
auto const t = fDistance / fLength; // zerowych torów nie ma
|
||||
p = FastGetPoint( t );
|
||||
// przechyłka w danym miejscu (zmienia się liniowo)
|
||||
a.x = interpolate<double>( fRoll1, fRoll2, t );
|
||||
a.y = fStoop; // pochylenie toru prostego
|
||||
a.z = fDirection; // kierunek toru w planie
|
||||
}
|
||||
};
|
||||
|
||||
Math3D::vector3 TSegment::FastGetPoint(double const t) const
|
||||
{
|
||||
// return (bCurve?Interpolate(t,Point1,CPointOut,CPointIn,Point2):((1.0-t)*Point1+(t)*Point2));
|
||||
return (
|
||||
( ( true == bCurve ) || ( iSegCount != 1 ) ) ?
|
||||
RaInterpolate( t ) :
|
||||
interpolate( Point1, Point2, t ) );
|
||||
}
|
||||
|
||||
bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, const gfx::vertex_array &ShapePoints, bool const Transition, double fTextureLength, double Texturescale, int iSkip, int iEnd, std::pair<float, float> fOffsetX, glm::vec3 **p, bool bRender)
|
||||
{ // generowanie trójkątów dla odcinka trajektorii ruchu
|
||||
// standardowo tworzy triangle_strip dla prostego albo ich zestaw dla łuku
|
||||
// po modyfikacji - dla ujemnego (iNumShapePoints) w dodatkowych polach tabeli podany jest przekrój końcowy
|
||||
|
||||
if( fTsBuffer.empty() )
|
||||
return false; // prowizoryczne zabezpieczenie przed wysypem - ustalić faktyczną przyczynę
|
||||
|
||||
glm::vec3 pos1, pos2, dir, parallel1, parallel2, pt, norm;
|
||||
float s, step, fOffset, tv1, tv2, t, fEnd;
|
||||
auto const iNumShapePoints = Transition ? ShapePoints.size() / 2 : ShapePoints.size();
|
||||
float const texturelength = fTextureLength * Texturescale;
|
||||
float const texturescale = Texturescale;
|
||||
|
||||
float m1, jmm1, m2, jmm2; // pozycje względne na odcinku 0...1 (ale nie parametr Beziera)
|
||||
step = fStep;
|
||||
tv1 = 1.0; // Ra: to by można było wyliczać dla odcinka, wyglądało by lepiej
|
||||
s = fStep * iSkip; // iSkip - ile odcinków z początku pominąć
|
||||
int i = iSkip; // domyślnie 0
|
||||
t = fTsBuffer[ i ]; // tabela wattości t dla segmentów
|
||||
// BUG: length of spline can be 0, we should skip geometry generation for such cases
|
||||
fOffset = 0.1 / fLength; // pierwsze 10cm
|
||||
pos1 = glm::dvec3{ FastGetPoint( t ) - Origin }; // wektor początku segmentu
|
||||
dir = glm::dvec3{ FastGetDirection( t, fOffset ) }; // wektor kierunku
|
||||
parallel1 = glm::vec3{ -dir.z, 0.f, dir.x }; // wektor poprzeczny
|
||||
if( glm::length2( parallel1 ) == 0.f ) {
|
||||
// temporary workaround for malformed situations with control points placed above endpoints
|
||||
parallel1 = glm::vec3{ glm::dvec3{ FastGetPoint_1() - FastGetPoint_0() } };
|
||||
}
|
||||
parallel1 = glm::normalize( parallel1 );
|
||||
if( iEnd == 0 )
|
||||
iEnd = iSegCount;
|
||||
fEnd = fLength * double( iEnd ) / double( iSegCount );
|
||||
/*
|
||||
m2 = s / fEnd;
|
||||
*/
|
||||
m2 = static_cast<float>( i - iSkip ) / ( iEnd - iSkip );
|
||||
|
||||
jmm2 = 1.f - m2;
|
||||
|
||||
while( i < iEnd ) {
|
||||
|
||||
++i; // kolejny punkt łamanej
|
||||
s += step; // końcowa pozycja segmentu [m]
|
||||
m1 = m2;
|
||||
jmm1 = jmm2; // stara pozycja
|
||||
/*
|
||||
m2 = s / fEnd;
|
||||
*/
|
||||
m2 = static_cast<float>( i - iSkip ) / ( iEnd - iSkip );
|
||||
|
||||
jmm2 = 1.f - m2; // nowa pozycja
|
||||
if( i == iEnd ) { // gdy przekroczyliśmy koniec - stąd dziury w torach...
|
||||
step -= ( s - fEnd ); // jeszcze do wyliczenia mapowania potrzebny
|
||||
s = fEnd;
|
||||
m2 = 1.f;
|
||||
jmm2 = 0.f;
|
||||
}
|
||||
/*
|
||||
while( tv1 < 0.0 ) {
|
||||
tv1 += 1.0;
|
||||
}
|
||||
*/
|
||||
tv1 = clamp_circular( tv1, 1.0f );
|
||||
tv2 = tv1 - step / texturelength; // mapowanie na końcu segmentu
|
||||
|
||||
t = fTsBuffer[ i ]; // szybsze od GetTFromS(s);
|
||||
pos2 = glm::dvec3{ FastGetPoint( t ) - Origin };
|
||||
dir = glm::dvec3{ FastGetDirection( t, fOffset ) }; // nowy wektor kierunku
|
||||
parallel2 = glm::vec3{ -dir.z, 0.f, dir.x }; // wektor poprzeczny
|
||||
if( glm::length2( parallel2 ) == 0.f ) {
|
||||
// temporary workaround for malformed situations with control points placed above endpoints
|
||||
parallel2 = glm::vec3{ glm::dvec3{ FastGetPoint_1() - FastGetPoint_0() } };
|
||||
}
|
||||
parallel2 = glm::normalize( parallel2 );
|
||||
|
||||
// TODO: refactor the loop, there's no need to calculate starting points for each segment when we can copy the end points of the previous one
|
||||
if( Transition ) {
|
||||
for( int j = 0; j < iNumShapePoints; ++j ) {
|
||||
pt = parallel1 * ( jmm1 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m1 * ( ShapePoints[ j + iNumShapePoints ].position.x - fOffsetX.second ) ) + pos1;
|
||||
pt.y += jmm1 * ShapePoints[ j ].position.y + m1 * ShapePoints[ j + iNumShapePoints ].position.y;
|
||||
norm = ( jmm1 * ShapePoints[ j ].normal.x + m1 * ShapePoints[ j + iNumShapePoints ].normal.x ) * parallel1;
|
||||
norm.y += jmm1 * ShapePoints[ j ].normal.y + m1 * ShapePoints[ j + iNumShapePoints ].normal.y;
|
||||
if( bRender ) {
|
||||
// skrzyżowania podczas łączenia siatek mogą nie renderować poboczy, ale potrzebować punktów
|
||||
Output.emplace_back(
|
||||
pt,
|
||||
glm::normalize( norm ),
|
||||
glm::vec2 { ( jmm1 * ShapePoints[ j ].texture.x + m1 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv1 } );
|
||||
}
|
||||
if( p ) // jeśli jest wskaźnik do tablicy
|
||||
if( *p )
|
||||
if( !j ) // to dla pierwszego punktu
|
||||
{
|
||||
*( *p ) = pt;
|
||||
( *p )++;
|
||||
} // zapamiętanie brzegu jezdni
|
||||
// dla trapezu drugi koniec ma inne współrzędne
|
||||
pt = parallel2 * ( jmm2 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m2 * ( ShapePoints[ j + iNumShapePoints ].position.x - fOffsetX.second ) ) + pos2;
|
||||
pt.y += jmm2 * ShapePoints[ j ].position.y + m2 * ShapePoints[ j + iNumShapePoints ].position.y;
|
||||
norm = ( jmm2 * ShapePoints[ j ].normal.x + m2 * ShapePoints[ j + iNumShapePoints ].normal.x ) * parallel2;
|
||||
norm.y += jmm2 * ShapePoints[ j ].normal.y + m2 * ShapePoints[ j + iNumShapePoints ].normal.y;
|
||||
if( bRender ) {
|
||||
// skrzyżowania podczas łączenia siatek mogą nie renderować poboczy, ale potrzebować punktów
|
||||
Output.emplace_back(
|
||||
pt,
|
||||
glm::normalize( norm ),
|
||||
glm::vec2 { ( jmm2 * ShapePoints[ j ].texture.x + m2 * ShapePoints[ j + iNumShapePoints ].texture.x ) / texturescale, tv2 } );
|
||||
}
|
||||
if( p ) // jeśli jest wskaźnik do tablicy
|
||||
if( *p )
|
||||
if( !j ) // to dla pierwszego punktu
|
||||
if( i == iSegCount ) {
|
||||
*( *p ) = pt;
|
||||
( *p )++;
|
||||
} // zapamiętanie brzegu jezdni
|
||||
}
|
||||
}
|
||||
else {
|
||||
if( bRender ) {
|
||||
for( int j = 0; j < iNumShapePoints; ++j ) {
|
||||
//łuk z jednym profilem
|
||||
pt = parallel1 * ( jmm1 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m1 * ( ShapePoints[ j ].position.x - fOffsetX.second ) ) + pos1;
|
||||
pt.y += ShapePoints[ j ].position.y;
|
||||
norm = ShapePoints[ j ].normal.x * parallel1;
|
||||
norm.y += ShapePoints[ j ].normal.y;
|
||||
|
||||
Output.emplace_back(
|
||||
pt,
|
||||
glm::normalize( norm ),
|
||||
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv1 } );
|
||||
|
||||
pt = parallel2 * ( jmm2 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m2 * ( ShapePoints[ j ].position.x - fOffsetX.second ) ) + pos2;
|
||||
pt.y += ShapePoints[ j ].position.y;
|
||||
norm = ShapePoints[ j ].normal.x * parallel2;
|
||||
norm.y += ShapePoints[ j ].normal.y;
|
||||
|
||||
Output.emplace_back(
|
||||
pt,
|
||||
glm::normalize( norm ),
|
||||
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv2 } );
|
||||
}
|
||||
}
|
||||
}
|
||||
pos1 = pos2;
|
||||
parallel1 = parallel2;
|
||||
tv1 = tv2;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
void TSegment::render_lines(std::vector<gfx::basic_vertex> &out, float quality) const
|
||||
{
|
||||
float step = 1.0f / iSegCount / quality;
|
||||
|
||||
float x;
|
||||
|
||||
glm::vec3 previous = FastGetPoint(0.0);
|
||||
|
||||
for (x = step; x <= 1.0f; x += step) {
|
||||
out.push_back(gfx::basic_vertex(previous, glm::vec3(0.0f), glm::vec2(0.0f)));
|
||||
|
||||
previous = glm::vec3(FastGetPoint(x));
|
||||
out.push_back(gfx::basic_vertex(previous, glm::vec3(0.0f), glm::vec2(0.0f)));
|
||||
}
|
||||
|
||||
out.push_back(gfx::basic_vertex(previous, glm::vec3(0.0f), glm::vec2(0.0f)));
|
||||
|
||||
previous = glm::vec3(FastGetPoint(1.0));
|
||||
out.push_back(gfx::basic_vertex(previous, glm::vec3(0.0f), glm::vec2(0.0f)));
|
||||
}
|
||||
|
||||
glm::vec3 TSegment::get_nearest_point(const glm::dvec3 &point, float quality) const
|
||||
{
|
||||
float step = 1.0f / iSegCount / quality;
|
||||
|
||||
float x;
|
||||
|
||||
glm::vec3 nearest;
|
||||
float min = std::numeric_limits<float>::max();
|
||||
|
||||
for (x = step; x <= 1.0f; x += step) {
|
||||
glm::vec3 p1 = FastGetPoint(x);
|
||||
glm::vec3 p2 = FastGetPoint(glm::min(1.0f, x + step));
|
||||
|
||||
if (p1 != p2) {
|
||||
float l2 = glm::distance2(p1, p2);
|
||||
float t = glm::max(0.0f, glm::min(1.0f, glm::dot((glm::vec3)point - p1, p2 - p1) / l2));
|
||||
glm::vec3 proj = p1 + t * (p2 - p1);
|
||||
p1 = proj;
|
||||
}
|
||||
|
||||
float dist2 = glm::distance2(p1, (glm::vec3)point);
|
||||
|
||||
if (dist2 < min) {
|
||||
nearest = p1;
|
||||
min = dist2;
|
||||
}
|
||||
}
|
||||
|
||||
return nearest;
|
||||
}
|
||||
147
world/Segment.h
Normal file
147
world/Segment.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Classes.h"
|
||||
#include "dumb3d.h"
|
||||
#include "geometrybank.h"
|
||||
#include "utilities.h"
|
||||
|
||||
struct map_colored_paths {
|
||||
std::vector<gfx::geometrybank_handle> switches;
|
||||
std::vector<gfx::geometrybank_handle> occupied;
|
||||
std::vector<gfx::geometrybank_handle> future;
|
||||
std::vector<gfx::geometrybank_handle> highlighted;
|
||||
};
|
||||
|
||||
struct segment_data {
|
||||
// types
|
||||
enum point {
|
||||
start = 0,
|
||||
control1,
|
||||
control2,
|
||||
end
|
||||
};
|
||||
// members
|
||||
std::array<glm::dvec3, 4> points {};
|
||||
std::array<float, 2> rolls {};
|
||||
float radius {};
|
||||
// constructors
|
||||
segment_data() = default;
|
||||
// methods
|
||||
void deserialize( cParser &Input, glm::dvec3 const &Offset );
|
||||
};
|
||||
|
||||
class TSegment
|
||||
{ // aproksymacja toru (zwrotnica ma dwa takie, jeden z nich jest aktywny)
|
||||
private:
|
||||
Math3D::vector3 Point1, CPointOut, CPointIn, Point2;
|
||||
float
|
||||
fRoll1 { 0.f },
|
||||
fRoll2 { 0.f }; // przechyłka na końcach
|
||||
double fLength { -1.0 }; // długość policzona
|
||||
std::vector<double> fTsBuffer; // wartości parametru krzywej dla równych odcinków
|
||||
double fStep = 0.0;
|
||||
int iSegCount = 0; // ilość odcinków do rysowania krzywej
|
||||
double fDirection = 0.0; // Ra: kąt prostego w planie; dla łuku kąt od Point1
|
||||
double fStoop = 0.0; // Ra: kąt wzniesienia; dla łuku od Point1
|
||||
Math3D::vector3 vA, vB, vC; // współczynniki wielomianów trzeciego stopnia vD==Point1
|
||||
TTrack *pOwner = nullptr; // wskaźnik na właściciela
|
||||
|
||||
Math3D::vector3
|
||||
GetFirstDerivative(double const fTime) const;
|
||||
double
|
||||
RombergIntegral(double const fA, double const fB) const;
|
||||
double
|
||||
GetTFromS(double const s) const;
|
||||
Math3D::vector3
|
||||
RaInterpolate(double const t) const;
|
||||
Math3D::vector3
|
||||
RaInterpolate0(double const t) const;
|
||||
|
||||
public:
|
||||
bool bCurve = false;
|
||||
|
||||
TSegment(TTrack *owner);
|
||||
bool
|
||||
Init( Math3D::vector3 NewPoint1, Math3D::vector3 NewPoint2, double fNewStep, double fNewRoll1 = 0, double fNewRoll2 = 0);
|
||||
bool
|
||||
Init( Math3D::vector3 &NewPoint1, Math3D::vector3 NewCPointOut, Math3D::vector3 NewCPointIn, Math3D::vector3 &NewPoint2, double fNewStep, double fNewRoll1 = 0, double fNewRoll2 = 0, bool bIsCurve = true);
|
||||
double
|
||||
ComputeLength() const; // McZapkie-150503
|
||||
// finds point on segment closest to specified point in 3d space. returns: point on segment as value in range 0-1
|
||||
double
|
||||
find_nearest_point( glm::dvec3 const &Point ) const;
|
||||
inline
|
||||
Math3D::vector3
|
||||
GetDirection1() const {
|
||||
return bCurve ? CPointOut - Point1 : CPointOut; };
|
||||
inline
|
||||
Math3D::vector3
|
||||
GetDirection2() const {
|
||||
return bCurve ? CPointIn - Point2 : CPointIn; };
|
||||
Math3D::vector3
|
||||
GetDirection(double const fDistance) const;
|
||||
inline
|
||||
Math3D::vector3
|
||||
GetDirection() const {
|
||||
return CPointOut; };
|
||||
Math3D::vector3
|
||||
FastGetDirection(double const fDistance, double const fOffset);
|
||||
/*
|
||||
Math3D::vector3
|
||||
GetPoint(double const fDistance) const;
|
||||
*/
|
||||
void
|
||||
RaPositionGet(double const fDistance, Math3D::vector3 &p, Math3D::vector3 &a) const;
|
||||
Math3D::vector3
|
||||
FastGetPoint(double const t) const;
|
||||
inline
|
||||
Math3D::vector3
|
||||
FastGetPoint_0() const {
|
||||
return Point1; };
|
||||
inline
|
||||
Math3D::vector3
|
||||
FastGetPoint_1() const {
|
||||
return Point2; };
|
||||
inline
|
||||
float
|
||||
GetRoll(double const Distance) const {
|
||||
return interpolate( fRoll1, fRoll2, static_cast<float>(Distance / fLength) ); }
|
||||
inline
|
||||
void
|
||||
GetRolls(float &r1, float &r2) const {
|
||||
// pobranie przechyłek (do generowania trójkątów)
|
||||
r1 = fRoll1;
|
||||
r2 = fRoll2; }
|
||||
|
||||
bool
|
||||
RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, gfx::vertex_array const &ShapePoints, bool const Transition, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, std::pair<float, float> fOffsetX = {0.f, 0.f}, glm::vec3 **p = nullptr, bool bRender = true );
|
||||
/*
|
||||
void
|
||||
Render();
|
||||
*/
|
||||
inline
|
||||
double
|
||||
GetLength() const {
|
||||
return fLength; };
|
||||
inline
|
||||
int
|
||||
RaSegCount() const {
|
||||
return ( fTsBuffer.empty() ? 1 : iSegCount ); };
|
||||
|
||||
void
|
||||
render_lines(std::vector<gfx::basic_vertex> &out, float quality = 1.0f) const;
|
||||
|
||||
glm::vec3
|
||||
get_nearest_point(const glm::dvec3 &point, float quality = 1.0f) const;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
51
world/Spring.cpp
Normal file
51
world/Spring.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Spring.h"
|
||||
|
||||
void TSpring::Init(double nKs, double nKd) {
|
||||
Ks = nKs;
|
||||
Kd = nKd;
|
||||
ks = Ks;
|
||||
kd = Kd;
|
||||
}
|
||||
|
||||
Math3D::vector3 TSpring::ComputateForces( Math3D::vector3 const &pPosition1, Math3D::vector3 const &pPosition2) {
|
||||
|
||||
Math3D::vector3 springForce;
|
||||
// p1 = &system[spring->p1];
|
||||
// p2 = &system[spring->p2];
|
||||
// VectorDifference(&p1->pos,&p2->pos,&deltaP); // Vector distance
|
||||
auto deltaP = pPosition1 - pPosition2;
|
||||
// dist = VectorLength(&deltaP); // Magnitude of deltaP
|
||||
auto dist = deltaP.Length();
|
||||
if( dist > restLen ) {
|
||||
|
||||
// Hterm = (dist - spring->restLen) * spring->Ks; // Ks * (dist - rest)
|
||||
auto Hterm = ( dist - restLen ) * Ks; // Ks * (dist - rest)
|
||||
|
||||
// VectorDifference(&p1->v,&p2->v,&deltaV); // Delta Velocity Vector
|
||||
auto deltaV = pPosition1 - pPosition2;
|
||||
|
||||
// Dterm = (DotProduct(&deltaV,&deltaP) * spring->Kd) / dist; // Damping Term
|
||||
auto Dterm = (DotProduct(deltaV,deltaP) * Kd) / dist;
|
||||
//Dterm = 0;
|
||||
|
||||
// ScaleVector(&deltaP,1.0f / dist, &springForce); // Normalize Distance Vector
|
||||
// ScaleVector(&springForce,-(Hterm + Dterm),&springForce); // Calc Force
|
||||
springForce = deltaP / dist * ( -( Hterm + Dterm ));
|
||||
// VectorSum(&p1->f,&springForce,&p1->f); // Apply to Particle 1
|
||||
// VectorDifference(&p2->f,&springForce,&p2->f); // - Force on Particle 2
|
||||
}
|
||||
|
||||
return springForce;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
42
world/Spring.h
Normal file
42
world/Spring.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#ifndef ParticlesH
|
||||
#define ParticlesH
|
||||
|
||||
#include "dumb3d.h"
|
||||
/*
|
||||
#define STATIC_THRESHOLD 0.17f
|
||||
const double m_Kd = 0.02f; // DAMPING FACTOR
|
||||
const double m_Kr = 0.8f; // 1.0 = SUPERBALL BOUNCE 0.0 = DEAD WEIGHT
|
||||
const double m_Ksh = 5.0f; // HOOK'S SPRING CONSTANT
|
||||
const double m_Ksd = 0.1f; // SPRING DAMPING CONSTANT
|
||||
|
||||
const double m_Csf = 0.9f; // Default Static Friction
|
||||
const double m_Ckf = 0.7f; // Default Kinetic Friction
|
||||
*/
|
||||
class TSpring {
|
||||
|
||||
public:
|
||||
TSpring() = default;
|
||||
// void Init(TParticnp1, TParticle *np2, double nKs= 0.5f, double nKd= 0.002f,
|
||||
// double nrestLen= -1.0f);
|
||||
void Init(double nKs = 0.5f, double nKd = 0.002f);
|
||||
Math3D::vector3 ComputateForces( Math3D::vector3 const &pPosition1, Math3D::vector3 const &pPosition2);
|
||||
//private:
|
||||
// members
|
||||
double restLen { 0.01 }; // LENGTH OF SPRING AT REST
|
||||
double Ks { 0.0 }; // SPRING CONSTANT
|
||||
double Kd { 0.0 }; // SPRING DAMPING
|
||||
float ks{ 0.f };
|
||||
float kd{ 0.f };
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
#endif
|
||||
3622
world/Track.cpp
Normal file
3622
world/Track.cpp
Normal file
File diff suppressed because it is too large
Load Diff
375
world/Track.h
Normal file
375
world/Track.h
Normal file
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "Classes.h"
|
||||
#include "Segment.h"
|
||||
#include "material.h"
|
||||
#include "scenenode.h"
|
||||
#include "Names.h"
|
||||
|
||||
namespace scene {
|
||||
class basic_cell;
|
||||
}
|
||||
|
||||
enum TTrackType {
|
||||
tt_Unknown,
|
||||
tt_Normal,
|
||||
tt_Switch,
|
||||
tt_Table,
|
||||
tt_Cross,
|
||||
tt_Tributary
|
||||
};
|
||||
// McZapkie-100502
|
||||
|
||||
enum TEnvironmentType {
|
||||
e_unknown = -1,
|
||||
e_flat = 0,
|
||||
e_mountains,
|
||||
e_canyon,
|
||||
e_tunnel,
|
||||
e_bridge,
|
||||
e_bank
|
||||
};
|
||||
// Ra: opracować alternatywny system cieni/świateł z definiowaniem koloru oświetlenia w halach
|
||||
|
||||
class basic_event;
|
||||
class TTrack;
|
||||
class TGroundNode;
|
||||
class TSubRect;
|
||||
class TTraction;
|
||||
|
||||
class TSwitchExtension
|
||||
{ // dodatkowe dane do toru, który jest zwrotnicą
|
||||
public:
|
||||
TSwitchExtension(TTrack *owner, int const what);
|
||||
~TSwitchExtension();
|
||||
std::shared_ptr<TSegment> Segments[6]; // dwa tory od punktu 1, pozosta³e dwa od 2? Ra 140101: 6 po³¹czeñ dla skrzy¿owañ
|
||||
TTrack *pNexts[2]; // tory do³¹czone do punktów 2 i 4
|
||||
TTrack *pPrevs[2]; // tory do³¹czone do punktów 1 i 3
|
||||
int iNextDirection[2]; // to te¿ z [2]+[2] przerobiæ na [4]
|
||||
int iPrevDirection[2];
|
||||
int CurrentIndex = 0; // dla zwrotnicy
|
||||
float
|
||||
fOffset{ 0.f },
|
||||
fDesiredOffset{ 0.f }; // aktualne i docelowe położenie napędu iglic
|
||||
float fOffsetSpeed = 0.1f; // prędkość liniowa ruchu iglic
|
||||
float fOffsetDelay = 0.05f; // opóźnienie ruchu drugiej iglicy względem pierwszej // dodatkowy ruch drugiej iglicy po zablokowaniu pierwszej na opornicy
|
||||
union
|
||||
{
|
||||
struct
|
||||
{ // zmienne potrzebne tylko dla zwrotnicy
|
||||
float fOffset1,
|
||||
fOffset2; // przesunięcia iglic - 0=na wprost
|
||||
bool RightSwitch; // czy zwrotnica w prawo
|
||||
};
|
||||
struct
|
||||
{ // zmienne potrzebne tylko dla obrotnicy/przesuwnicy
|
||||
// TAnimContainer *pAnim; //animator modelu dla obrotnicy
|
||||
TAnimModel *pModel; // na razie model
|
||||
};
|
||||
struct
|
||||
{ // zmienne dla skrzyżowania
|
||||
int iRoads; // ile dróg się spotyka?
|
||||
glm::vec3 *vPoints; // tablica wierzchołków nawierzchni, generowana przez pobocze
|
||||
bool bPoints; // czy utworzone?
|
||||
};
|
||||
};
|
||||
bool bMovement = false; // czy w trakcie animacji
|
||||
scene::basic_cell *pOwner = nullptr; // TODO: convert this to observer pattern
|
||||
TTrack *pNextAnim = nullptr; // następny tor do animowania
|
||||
basic_event *evPlus = nullptr,
|
||||
*evMinus = nullptr; // zdarzenia sygnalizacji rozprucia
|
||||
float fVelocity = -1.0; // maksymalne ograniczenie prędkości (ustawianej eventem)
|
||||
Math3D::vector3 vTrans; // docelowa translacja przesuwnicy
|
||||
material_handle m_material3 = 0; // texture of auto generated switch trackbed
|
||||
gfx::geometry_handle Geometry3; // geometry of auto generated switch trackbed
|
||||
|
||||
gfx::geometrybank_handle map_geometry[6]; // geometry for map visualisation
|
||||
};
|
||||
|
||||
class TIsolated
|
||||
{ // obiekt zbierający zajętości z kilku odcinków
|
||||
public:
|
||||
// constructors
|
||||
TIsolated(const std::string &n, TIsolated *i);
|
||||
// methods
|
||||
static void DeleteAll();
|
||||
static TIsolated * Find(const std::string &n); // znalezienie obiektu albo utworzenie nowego
|
||||
void AssignEvents();
|
||||
void Modify(int i, TDynamicObject *o); // dodanie lub odjęcie osi
|
||||
inline
|
||||
bool
|
||||
Busy() {
|
||||
return (iAxles > 0); };
|
||||
inline
|
||||
static TIsolated *
|
||||
Root() {
|
||||
return (pRoot); };
|
||||
inline
|
||||
TIsolated *
|
||||
Next() {
|
||||
return (pNext); };
|
||||
inline
|
||||
void
|
||||
parent( TIsolated *Parent ) {
|
||||
pParent = Parent; }
|
||||
inline
|
||||
TIsolated *
|
||||
parent() const {
|
||||
return pParent; }
|
||||
// members
|
||||
std::string asName; // nazwa obiektu, baza do nazw eventów
|
||||
basic_event *evBusy { nullptr }; // zdarzenie wyzwalane po zajęciu grupy
|
||||
basic_event *evFree { nullptr }; // zdarzenie wyzwalane po całkowitym zwolnieniu zajętości grupy
|
||||
basic_event *evInc { nullptr }; // wyzwalane po wjeździe na grupę
|
||||
basic_event *evDec { nullptr }; // wyzwalane po zjazdu z grupy
|
||||
TMemCell *pMemCell { nullptr }; // automatyczna komórka pamięci, która współpracuje z odcinkiem izolowanym
|
||||
private:
|
||||
// members
|
||||
int iAxles { 0 }; // ilość osi na odcinkach obsługiwanych przez obiekt
|
||||
TIsolated *pNext { nullptr }; // odcinki izolowane są trzymane w postaci listy jednikierunkowej
|
||||
TIsolated *pParent { nullptr }; // optional parent piece, receiving data from its children
|
||||
static TIsolated *pRoot; // początek listy
|
||||
};
|
||||
|
||||
// trajektoria ruchu - opakowanie
|
||||
class TTrack : public scene::basic_node
|
||||
{
|
||||
friend opengl_renderer;
|
||||
friend opengl33_renderer;
|
||||
// NOTE: temporary arrangement
|
||||
friend itemproperties_panel;
|
||||
|
||||
public:
|
||||
std::vector<TIsolated*> Isolated; // obwód izolowany obsługujący zajęcia/zwolnienia grupy torów
|
||||
std::shared_ptr<TSwitchExtension> SwitchExtension; // dodatkowe dane do toru, który jest zwrotnicą
|
||||
std::shared_ptr<TSegment> Segment;
|
||||
TTrack * trNext = nullptr; // odcinek od strony punktu 2 - to powinno być w segmencie
|
||||
TTrack * trPrev = nullptr; // odcinek od strony punktu 1
|
||||
|
||||
// McZapkie-070402: dodalem zmienne opisujace rozmiary tekstur
|
||||
int iTrapezoid = 0; // 0-standard, 1-przechyłka, 2-trapez, 3-oba
|
||||
double fRadiusTable[ 2 ] = { 0.0, 0.0 }; // dwa promienie, drugi dla zwrotnicy
|
||||
float fVerticalRadius { 0.f }; // y-axis track radius (currently not supported)
|
||||
float fTexLength = 4.0f; // długość powtarzania tekstury w metrach
|
||||
float fTexRatio1 = 1.0f; // proporcja boków tekstury nawierzchni (żeby zaoszczędzić na rozmiarach tekstur...)
|
||||
float fTexRatio2 = 1.0f; // proporcja boków tekstury chodnika (żeby zaoszczędzić na rozmiarach tekstur...)
|
||||
float fTexHeight1 = 0.6f; // wysokość brzegu względem trajektorii
|
||||
float fTexHeightOffset = 0.f; // potential adjustment to account for the path roll
|
||||
float fTexWidth = 0.9f; // szerokość boku
|
||||
float fTexSlope = 0.9f;
|
||||
|
||||
glm::dvec3 m_origin;
|
||||
// TODO: store material names as strings, for lossless serialization and export
|
||||
material_handle m_material1 = 0; // tekstura szyn albo nawierzchni
|
||||
material_handle m_material2 = 0; // tekstura automatycznej podsypki albo pobocza
|
||||
std::pair<std::string, int> m_profile1 {}; // profile of geometry chunks textured with texture 1
|
||||
using geometryhandle_sequence = std::vector<gfx::geometry_handle>;
|
||||
geometryhandle_sequence Geometry1; // geometry chunks textured with texture 1
|
||||
geometryhandle_sequence Geometry2; // geometry chunks textured with texture 2
|
||||
|
||||
std::vector<segment_data> m_paths; // source data for owned paths
|
||||
int iterate_stamp = 0;
|
||||
|
||||
public:
|
||||
using dynamics_sequence = std::deque<TDynamicObject *>;
|
||||
using event_sequence = std::vector<std::pair<std::string, basic_event *> >;
|
||||
|
||||
dynamics_sequence Dynamics;
|
||||
event_sequence
|
||||
m_events0all,
|
||||
m_events1all,
|
||||
m_events2all,
|
||||
m_events0,
|
||||
m_events1,
|
||||
m_events2;
|
||||
bool m_events { false }; // Ra: flaga informująca o obecności eventów
|
||||
std::pair<std::string, TMemCell *> m_friction { "", nullptr };
|
||||
|
||||
int iNextDirection = 0; // 0:Point1, 1:Point2, 3:do odchylonego na zwrotnicy
|
||||
int iPrevDirection = 0; // domyślnie wirtualne odcinki dołączamy stroną od Point1
|
||||
TTrackType eType = tt_Normal; // domyślnie zwykły
|
||||
int iCategoryFlag = 1; // 0x100 - usuwanie pojazów // 1-tor, 2-droga, 4-rzeka, 8-samolot?
|
||||
float fTrackWidth = 1.435f; // szerokość w punkcie 1 // rozstaw toru, szerokość nawierzchni
|
||||
float fTrackWidth2 = 0.0f; // szerokość w punkcie 2 (głównie drogi i rzeki)
|
||||
float fFriction = 0.15f; // współczynnik tarcia
|
||||
float fSoundDistance = -1.0f;
|
||||
int iQualityFlag = 20;
|
||||
int iDamageFlag = 0;
|
||||
TEnvironmentType eEnvironment = e_flat; // dźwięk i oświetlenie
|
||||
int iAction = 0; // czy modyfikowany eventami (specjalna obsługa przy skanowaniu)
|
||||
float fOverhead = -1.0; // można normalnie pobierać prąd (0 dla jazdy bezprądowej po danym odcinku, >0-z opuszczonym i ograniczeniem prędkości)
|
||||
private:
|
||||
double fVelocity = -1.0; // ograniczenie prędkości // prędkość dla AI (powyżej rośnie prawdopowobieństwo wykolejenia)
|
||||
public:
|
||||
// McZapkie-100502:
|
||||
// double fTrackLength = 100.0; // długość z wpisu, nigdzie nie używana
|
||||
double fRadius = 0.0; // promień, dla zwrotnicy kopiowany z tabeli
|
||||
bool ScannedFlag = false; // McZapkie: do zaznaczania kolorem torów skanowanych przez AI
|
||||
TGroundNode *nFouling[ 2 ] = { nullptr, nullptr }; // współrzędne ukresu albo oporu kozła
|
||||
|
||||
explicit TTrack( scene::node_data const &Nodedata );
|
||||
virtual ~TTrack();
|
||||
|
||||
void Init();
|
||||
static bool sort_by_material( TTrack const *Left, TTrack const *Right );
|
||||
static TTrack * Create400m(int what, double dx);
|
||||
TTrack * NullCreate(int dir);
|
||||
inline bool IsEmpty() {
|
||||
return Dynamics.empty(); };
|
||||
void ConnectPrevPrev(TTrack *pNewPrev, int typ);
|
||||
void ConnectPrevNext(TTrack *pNewPrev, int typ);
|
||||
void ConnectNextPrev(TTrack *pNewNext, int typ);
|
||||
void ConnectNextNext(TTrack *pNewNext, int typ);
|
||||
material_handle copy_adjacent_trackbed_material( TTrack const *Exclude = nullptr );
|
||||
inline double Length() const {
|
||||
return Segment->GetLength(); };
|
||||
inline std::shared_ptr<TSegment> CurrentSegment() const {
|
||||
return Segment; };
|
||||
inline TTrack *CurrentNext() const {
|
||||
return trNext; };
|
||||
inline TTrack *CurrentPrev() const {
|
||||
return trPrev; };
|
||||
TTrack *Connected(int s, double &d) const;
|
||||
bool SetConnections(int i);
|
||||
bool Switch(int i, float const t = -1.f, float const d = -1.f);
|
||||
bool SwitchForced(int i, TDynamicObject *o);
|
||||
int CrossSegment(int from, int into);
|
||||
inline int GetSwitchState() {
|
||||
return (
|
||||
SwitchExtension ?
|
||||
SwitchExtension->CurrentIndex :
|
||||
-1); };
|
||||
// returns number of different routes possible to take from given point
|
||||
// TODO: specify entry point, number of routes for switches can vary
|
||||
inline
|
||||
int
|
||||
RouteCount() const {
|
||||
return (
|
||||
SwitchExtension != nullptr ?
|
||||
SwitchExtension->iRoads - 1 :
|
||||
1 ); }
|
||||
void Load(cParser *parser, glm::dvec3 const &pOrigin);
|
||||
bool AssignEvents();
|
||||
bool AssignForcedEvents(basic_event *NewEventPlus, basic_event *NewEventMinus);
|
||||
void QueueEvents( event_sequence const &Events, TDynamicObject const *Owner );
|
||||
void QueueEvents( event_sequence const &Events, TDynamicObject const *Owner, double const Delaylimit );
|
||||
bool CheckDynamicObject(TDynamicObject *Dynamic);
|
||||
bool AddDynamicObject(TDynamicObject *Dynamic);
|
||||
bool RemoveDynamicObject(TDynamicObject *Dynamic);
|
||||
|
||||
// set origin point
|
||||
void
|
||||
origin( glm::dvec3 Origin ) {
|
||||
m_origin = Origin; }
|
||||
// retrieves list of the track's end points
|
||||
std::vector<glm::dvec3>
|
||||
endpoints() const;
|
||||
|
||||
gfx::geometrybank_handle extra_map_geometry; // handle for map highlighting
|
||||
|
||||
TTrack *Next(TTrack *visitor);
|
||||
double ActiveLength();
|
||||
|
||||
void create_geometry( gfx::geometrybank_handle const &Bank ); // wypełnianie VBO
|
||||
void create_map_geometry(std::vector<gfx::basic_vertex> &Bank, const gfx::geometrybank_handle Extra);
|
||||
void get_map_active_paths(map_colored_paths &handles);
|
||||
void get_map_paths_for_state(map_colored_paths &handles, int state);
|
||||
void get_map_future_paths(map_colored_paths &handles);
|
||||
glm::vec3 get_nearest_point(const glm::dvec3 &point) const;
|
||||
void RenderDynSounds(); // odtwarzanie dźwięków pojazdów jest niezależne od ich wyświetlania
|
||||
|
||||
void RaOwnerSet( scene::basic_cell *o ) {
|
||||
if( SwitchExtension ) { SwitchExtension->pOwner = o; } };
|
||||
bool InMovement(); // czy w trakcie animacji?
|
||||
void RaAssign( TAnimModel *am, basic_event *done, basic_event *joined );
|
||||
void RaAnimListAdd(TTrack *t);
|
||||
TTrack * RaAnimate();
|
||||
|
||||
void RadioStop();
|
||||
void AxleCounter(int i, TDynamicObject *o) {
|
||||
for (TIsolated *iso : Isolated)
|
||||
iso->Modify(i, o); }; // dodanie lub odjęcie osi
|
||||
std::string RemoteIsolatedName();
|
||||
void AddIsolated(TIsolated *iso) {
|
||||
Isolated.push_back(iso);
|
||||
}
|
||||
double WidthTotal();
|
||||
bool IsGroupable();
|
||||
int TestPoint( Math3D::vector3 *Point);
|
||||
void MovedUp1(float const dh);
|
||||
void VelocitySet(float v);
|
||||
double VelocityGet();
|
||||
float Friction() const;
|
||||
void ConnectionsLog();
|
||||
bool DoubleSlip() const;
|
||||
static void fetch_default_profiles();
|
||||
std::string tooltip() const override;
|
||||
|
||||
private:
|
||||
// types
|
||||
using profiles_array = std::vector<gfx::vertex_array>;
|
||||
using profiles_map = std::unordered_map<std::string, int>;
|
||||
// methods
|
||||
// radius() subclass details, calculates node's bounding radius
|
||||
float radius_();
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void serialize_( std::ostream &Output ) const;
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void deserialize_( std::istream &Input );
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void export_as_text_( std::ostream &Output ) const;
|
||||
// locates specified profile in the profile database, potentially loading it from a file
|
||||
static std::pair<std::string, int> fetch_track_rail_profile( std::string const &Profile );
|
||||
// loads content of specified file and converts it into a vertex array
|
||||
static gfx::vertex_array deserialize_profile( std::string const &Profile );
|
||||
// provides direct access to vertex data of specified profile
|
||||
static gfx::vertex_array const & track_rail_profile( int const Profile );
|
||||
// returns texture length for specified material
|
||||
float texture_length( material_handle const Material );
|
||||
// creates profile for a part of current path
|
||||
void create_switch_trackbed( gfx::vertex_array &Output );
|
||||
void create_track_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &Left );
|
||||
void create_track_blade_profile( gfx::vertex_array &Right, gfx::vertex_array &Left );
|
||||
void create_track_bed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next );
|
||||
void create_road_profile( gfx::vertex_array &Output, bool const Forcetransition = false );
|
||||
void create_road_side_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition = false );
|
||||
// members
|
||||
static profiles_array m_profiles; // shared database of path element profiles
|
||||
static profiles_map m_profilesmap;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// collection of virtual tracks and roads present in the scene
|
||||
class path_table : public basic_table<TTrack> {
|
||||
|
||||
public:
|
||||
~path_table();
|
||||
// legacy method, initializes tracks after deserialization from scenario file
|
||||
void
|
||||
InitTracks();
|
||||
// legacy method, sends list of occupied paths over network
|
||||
void
|
||||
TrackBusyList() const;
|
||||
// legacy method, sends list of occupied path sections over network
|
||||
void
|
||||
IsolatedBusyList() const;
|
||||
// legacy method, sends state of specified path section over network
|
||||
void
|
||||
IsolatedBusy( std::string const &Name ) const;
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
902
world/Traction.cpp
Normal file
902
world/Traction.cpp
Normal file
@@ -0,0 +1,902 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
/*
|
||||
MaSzyna EU07 locomotive simulator
|
||||
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
|
||||
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "Traction.h"
|
||||
|
||||
#include "simulation.h"
|
||||
#include "Globals.h"
|
||||
#include "TractionPower.h"
|
||||
#include "Logs.h"
|
||||
#include "renderer.h"
|
||||
#include "utilities.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/*
|
||||
|
||||
=== Koncepcja dwustronnego zasilania sekcji sieci trakcyjnej, Ra 2014-02 ===
|
||||
0. Każde przęsło sieci może mieć wpisaną nazwę zasilacza, a także napięcie
|
||||
nominalne i maksymalny prąd, które stanowią redundancję do danych zasilacza.
|
||||
Rezystancja może się zmieniać, materiał i grubość drutu powinny być wspólny
|
||||
dla segmentu. Podanie punktów umożliwia łączenie przęseł w listy dwukierunkowe,
|
||||
co usprawnia wyszukiwania kolejnych przeseł podczas jazdy. Dla bieżni wspólnej
|
||||
powinna być podana nazwa innego przęsła w parametrze "parallel". Wskaźniki
|
||||
na przęsła bieżni wspólnej mają być układane w jednokierunkowy pierścień.
|
||||
1. Problemem jest ustalenie topologii sekcji dla linii dwutorowych. Nad każdym
|
||||
torem powinna znajdować się oddzielna sekcja sieci, aby mogła zostać odłączona
|
||||
w przypadku zwarcia. Sekcje nad równoległymi torami są również łączone
|
||||
równolegle przez kabiny sekcyjne, co zmniejsza płynące prądy i spadki napięć.
|
||||
2. Drugim zagadnieniem jest zasilanie sekcji jednocześnie z dwóch stron, czyli
|
||||
sekcja musi mieć swoją nazwę oraz wskazanie dwóch zasilaczy ze wskazaniem
|
||||
geograficznym ich położenia. Dodatkową trudnością jest brak połączenia
|
||||
pomiędzy segmentami naprężania. Podsumowując, każdy segment naprężania powinien
|
||||
mieć przypisanie do sekcji zasilania, a dodatkowo skrajne segmenty powinny
|
||||
wskazywać dwa różne zasilacze.
|
||||
3. Zasilaczem sieci może być podstacja, która w wersji 3kV powinna generować
|
||||
pod obciążeniem napięcie maksymalne rzędu 3600V, a spadek napięcia następuje
|
||||
na jej rezystancji wewnętrznej oraz na przewodach trakcyjnych. Zasilaczem może
|
||||
być również kabina sekcyjna, która jest zasilana z podstacji poprzez przewody
|
||||
trakcyjne.
|
||||
4. Dla uproszczenia można przyjąć, że zasilanie pojazdu odbywać się będzie z
|
||||
dwóch sąsiednich podstacji, pomiędzy którymi może być dowolna liczba kabin
|
||||
sekcyjnych. W przypadku wyłączenia jednej z tych podstacji, zasilanie może
|
||||
być pobierane z kolejnej. Łącznie należy rozważać 4 najbliższe podstacje,
|
||||
przy czym do obliczeń można przyjmować 2 z nich.
|
||||
5. Przęsła sieci są łączone w listę dwukierunkową, więc wystarczy nazwę
|
||||
sekcji wpisać w jednym z nich, wpisanie w każdym nie przeszkadza.
|
||||
Alternatywnym sposobem łączenia segmentów naprężania może być wpisywanie
|
||||
nazw przęseł jako "parallel", co może być uciążliwe dla autorów scenerii.
|
||||
W skrajnych przęsłach należałoby dodatkowo wpisać nazwę zasilacza, będzie
|
||||
to jednocześnie wskazanie przęsła, do którego podłączone są przewody
|
||||
zasilające. Konieczne jest odróżnienie nazwy sekcji od nazwy zasilacza, co
|
||||
można uzyskać różnicując ich nazwy albo np. specyficznie ustawiając wartość
|
||||
prądu albo napięcia przęsła.
|
||||
6. Jeśli dany segment naprężania jest wspólny dla dwóch sekcji zasilania,
|
||||
to jedno z przęseł musi mieć nazwę "*" (gwiazdka), co będzie oznaczało, że
|
||||
ma zamontowany izolator. Dla uzyskania efektów typu łuk elektryczny, należało
|
||||
by wskazać położenie izolatora i jego długość (ew. typ).
|
||||
7. Również w parametrach zasilacza należało by określić, czy jest podstacją,
|
||||
czy jedynie kabiną sekcyjną. Różnić się one będą fizyką działania.
|
||||
8. Dla zbudowanej topologii sekcji i zasilaczy należało by zbudować dynamiczny
|
||||
schemat zastępczy. Dynamika polega na wyłączaniu sekcji ze zwarciem oraz
|
||||
przeciążonych podstacji. Musi być też możliwość wyłączenia sekcji albo
|
||||
podstacji za pomocą eventu.
|
||||
9. Dla każdej sekcji musi być tworzony obiekt, wskazujący na podstacje
|
||||
zasilające na końcach, stan włączenia, zwarcia, przepięcia. Do tego obiektu
|
||||
musi wskazywać każde przęsło z aktywnym zasilaniem.
|
||||
|
||||
z.1 z.2 z.3
|
||||
-=-a---1*1---c-=---c---=-c--2*2--e---=---e-3-*-3--g-=-
|
||||
-=-b---1*1---d-=---d---=-d--2*2--f---=---e-3-*-3--h-=-
|
||||
|
||||
nazwy sekcji (@): a,b,c,d,e,f,g,h
|
||||
nazwy zasilaczy (#): 1,2,3
|
||||
przęsło z izolatorem: *
|
||||
przęsła bez wskazania nazwy sekcji/zasilacza: -
|
||||
segment napręzania: =-x-=
|
||||
segment naprężania z izolatorem: =---@---#*#---@---=
|
||||
segment naprężania bez izolatora: =--------@------=
|
||||
|
||||
Obecnie brak nazwy sekcji nie jest akceptowany i każde przęsło musi mieć wpisaną
|
||||
jawnie nazwę sekcji, ewentualnie nazwę zasilacza (zostanie zastąpiona wskazaniem
|
||||
sekcji z sąsiedniego przęsła).
|
||||
*/
|
||||
|
||||
TTraction::TTraction( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {}
|
||||
|
||||
void
|
||||
TTraction::Load( cParser *parser, glm::dvec3 const &pOrigin ) {
|
||||
|
||||
parser->getTokens( 4 );
|
||||
*parser
|
||||
>> asPowerSupplyName
|
||||
>> NominalVoltage
|
||||
>> MaxCurrent
|
||||
>> fResistivity;
|
||||
if( fResistivity == 0.01f ) {
|
||||
// tyle jest w sceneriach [om/km]
|
||||
// taka sensowniejsza wartość za http://www.ikolej.pl/fileadmin/user_upload/Seminaria_IK/13_05_07_Prezentacja_Kruczek.pdf
|
||||
fResistivity = 0.075f;
|
||||
}
|
||||
fResistivity *= 0.001f; // teraz [om/m]
|
||||
// Ra 2014-02: a tutaj damy symbol sieci i jej budowę, np.:
|
||||
// SKB70-C, CuCd70-2C, KB95-2C, C95-C, C95-2C, YC95-2C, YpC95-2C, YC120-2C
|
||||
// YpC120-2C, YzC120-2C, YwsC120-2C, YC150-C150, YC150-2C150, C150-C150
|
||||
// C120-2C, 2C120-2C, 2C120-2C-1, 2C120-2C-2, 2C120-2C-3, 2C120-2C-4
|
||||
auto const material = parser->getToken<std::string>();
|
||||
// 1=miedziana, rysuje się na zielono albo czerwono
|
||||
// 2=aluminiowa, rysuje się na czarno
|
||||
if( material == "none" ) { Material = 0; }
|
||||
else if( material == "al" ) { Material = 2; }
|
||||
else { Material = 1; }
|
||||
parser->getTokens( 2 );
|
||||
*parser
|
||||
>> WireThickness
|
||||
>> DamageFlag;
|
||||
pPoint1 = LoadPoint( *parser ) + pOrigin;
|
||||
pPoint2 = LoadPoint( *parser ) + pOrigin;
|
||||
pPoint3 = LoadPoint( *parser ) + pOrigin;
|
||||
pPoint4 = LoadPoint( *parser ) + pOrigin;
|
||||
auto const minheight { parser->getToken<double>() };
|
||||
fHeightDifference = ( pPoint3.y - pPoint1.y + pPoint4.y - pPoint2.y ) * 0.5 - minheight;
|
||||
auto const segmentlength { parser->getToken<double>() };
|
||||
iNumSections = (
|
||||
segmentlength ?
|
||||
glm::length( ( pPoint1 - pPoint2 ) ) / segmentlength :
|
||||
0 );
|
||||
parser->getTokens( 2 );
|
||||
*parser
|
||||
>> Wires
|
||||
>> WireOffset;
|
||||
m_visible = ( parser->getToken<std::string>() == "vis" );
|
||||
|
||||
std::string token { parser->getToken<std::string>() };
|
||||
if( token == "parallel" ) {
|
||||
// jawne wskazanie innego przęsła, na które może przestawić się pantograf
|
||||
parser->getTokens();
|
||||
*parser >> asParallel;
|
||||
}
|
||||
while( ( false == token.empty() )
|
||||
&& ( token != "endtraction" ) ) {
|
||||
|
||||
token = parser->getToken<std::string>();
|
||||
}
|
||||
|
||||
Init(); // przeliczenie parametrów
|
||||
|
||||
// calculate traction location
|
||||
location( interpolate( pPoint2, pPoint1, 0.5 ) );
|
||||
}
|
||||
|
||||
// retrieves list of the track's end points
|
||||
std::vector<glm::dvec3>
|
||||
TTraction::endpoints() const {
|
||||
|
||||
return { pPoint1, pPoint2 };
|
||||
}
|
||||
|
||||
std::size_t
|
||||
TTraction::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
if( m_geometry != null_handle ) {
|
||||
return GfxRenderer->Vertices( m_geometry ).size() / 2;
|
||||
}
|
||||
|
||||
gfx::vertex_array vertices;
|
||||
|
||||
double ddp = std::hypot( pPoint2.x - pPoint1.x, pPoint2.z - pPoint1.z );
|
||||
if( Wires == 2 )
|
||||
WireOffset = 0;
|
||||
// jezdny
|
||||
gfx::basic_vertex startvertex, endvertex;
|
||||
startvertex.position =
|
||||
glm::vec3(
|
||||
pPoint1.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
|
||||
pPoint1.y - m_origin.y,
|
||||
pPoint1.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pPoint2.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
|
||||
pPoint2.y - m_origin.y,
|
||||
pPoint2.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
// Nie wiem co 'Marcin
|
||||
glm::dvec3 pt1, pt2, pt3, pt4, v1, v2;
|
||||
v1 = pPoint4 - pPoint3;
|
||||
v2 = pPoint2 - pPoint1;
|
||||
float step = 0;
|
||||
if( iNumSections > 0 )
|
||||
step = 1.0f / (float)iNumSections;
|
||||
double f = step;
|
||||
float mid = 0.5;
|
||||
float t;
|
||||
// Przewod nosny 'Marcin
|
||||
if( Wires > 1 ) { // lina nośna w kawałkach
|
||||
startvertex.position =
|
||||
glm::vec3(
|
||||
pPoint3.x - m_origin.x,
|
||||
pPoint3.y - m_origin.y,
|
||||
pPoint3.z - m_origin.z );
|
||||
for( int i = 0; i < iNumSections - 1; ++i ) {
|
||||
pt3 = pPoint3 + v1 * f;
|
||||
t = ( 1 - std::fabs( f - mid ) * 2 );
|
||||
if( ( Wires < 4 )
|
||||
|| ( ( i != 0 )
|
||||
&& ( i != iNumSections - 2 ) ) ) {
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pt3.x - m_origin.x,
|
||||
pt3.y - std::sqrt( t ) * fHeightDifference - m_origin.y,
|
||||
pt3.z - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
startvertex = endvertex;
|
||||
}
|
||||
f += step;
|
||||
}
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pPoint4.x - m_origin.x,
|
||||
pPoint4.y - m_origin.y,
|
||||
pPoint4.z - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
}
|
||||
// Drugi przewod jezdny 'Winger
|
||||
if( Wires > 2 ) {
|
||||
startvertex.position =
|
||||
glm::vec3(
|
||||
pPoint1.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
|
||||
pPoint1.y - m_origin.y,
|
||||
pPoint1.z + ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pPoint2.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
|
||||
pPoint2.y - m_origin.y,
|
||||
pPoint2.z + ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
}
|
||||
|
||||
f = step;
|
||||
|
||||
if( Wires == 4 ) {
|
||||
startvertex.position =
|
||||
glm::vec3(
|
||||
pPoint3.x - m_origin.x,
|
||||
pPoint3.y - 0.65f * fHeightDifference - m_origin.y,
|
||||
pPoint3.z - m_origin.z );
|
||||
for( int i = 0; i < iNumSections - 1; ++i ) {
|
||||
pt3 = pPoint3 + v1 * f;
|
||||
t = ( 1 - std::fabs( f - mid ) * 2 );
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pt3.x - m_origin.x,
|
||||
pt3.y - std::sqrt( t ) * fHeightDifference - (
|
||||
( ( i == 0 )
|
||||
|| ( i == iNumSections - 2 ) ) ?
|
||||
0.25f * fHeightDifference :
|
||||
0.05 )
|
||||
- m_origin.y,
|
||||
pt3.z - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
startvertex = endvertex;
|
||||
f += step;
|
||||
}
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pPoint4.x - m_origin.x,
|
||||
pPoint4.y - 0.65f * fHeightDifference - m_origin.y,
|
||||
pPoint4.z - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
}
|
||||
f = step;
|
||||
|
||||
// Przewody pionowe (wieszaki) 'Marcin, poprawki na 2 przewody jezdne 'Winger
|
||||
if( Wires > 1 ) {
|
||||
auto const flo { static_cast<float>( Wires == 4 ? 0.25f * fHeightDifference : 0 ) };
|
||||
auto const flo1 { static_cast<float>( Wires == 4 ? +0.05 : 0 ) };
|
||||
for( int i = 0; i < iNumSections - 1; ++i ) {
|
||||
pt3 = pPoint3 + v1 * f;
|
||||
pt4 = pPoint1 + v2 * f;
|
||||
t = ( 1 - std::fabs( f - mid ) * 2 );
|
||||
if( ( i % 2 ) == 0 ) {
|
||||
startvertex.position =
|
||||
glm::vec3(
|
||||
pt3.x - m_origin.x,
|
||||
pt3.y - std::sqrt( t ) * fHeightDifference - ( ( i == 0 ) || ( i == iNumSections - 2 ) ? flo : flo1 ) - m_origin.y,
|
||||
pt3.z - m_origin.z );
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pt4.x - ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
|
||||
pt4.y - m_origin.y,
|
||||
pt4.z - ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
}
|
||||
else {
|
||||
startvertex.position =
|
||||
glm::vec3(
|
||||
pt3.x - m_origin.x,
|
||||
pt3.y - std::sqrt( t ) * fHeightDifference - ( ( i == 0 ) || ( i == iNumSections - 2 ) ? flo : flo1 ) - m_origin.y,
|
||||
pt3.z - m_origin.z );
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pt4.x + ( pPoint2.z / ddp - pPoint1.z / ddp ) * WireOffset - m_origin.x,
|
||||
pt4.y - m_origin.y,
|
||||
pt4.z + ( -pPoint2.x / ddp + pPoint1.x / ddp ) * WireOffset - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
}
|
||||
if( ( ( Wires == 4 )
|
||||
&& ( ( i == 1 )
|
||||
|| ( i == iNumSections - 3 ) ) ) ) {
|
||||
startvertex.position =
|
||||
glm::vec3(
|
||||
pt3.x - m_origin.x,
|
||||
pt3.y - std::sqrt( t ) * fHeightDifference - 0.05 - m_origin.y,
|
||||
pt3.z - m_origin.z );
|
||||
endvertex.position =
|
||||
glm::vec3(
|
||||
pt3.x - m_origin.x,
|
||||
pt3.y - std::sqrt( t ) * fHeightDifference - m_origin.y,
|
||||
pt3.z - m_origin.z );
|
||||
vertices.emplace_back( startvertex );
|
||||
vertices.emplace_back( endvertex );
|
||||
}
|
||||
f += step;
|
||||
}
|
||||
}
|
||||
|
||||
auto const elementcount = vertices.size() / 2;
|
||||
|
||||
gfx::userdata_array empty_userdata{};
|
||||
m_geometry = GfxRenderer->Insert( vertices, empty_userdata, Bank, GL_LINES );
|
||||
|
||||
return elementcount;
|
||||
}
|
||||
|
||||
int TTraction::TestPoint(glm::dvec3 const &Point)
|
||||
{ // sprawdzanie, czy przęsła można połączyć
|
||||
if( ( hvNext[ 0 ] == nullptr )
|
||||
&& ( glm::all( glm::epsilonEqual( Point, pPoint1, 0.025 ) ) ) ) {
|
||||
return 0;
|
||||
}
|
||||
if( ( hvNext[ 1 ] == nullptr )
|
||||
&& ( glm::all( glm::epsilonEqual( Point, pPoint2, 0.025 ) ) ) ) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
//łączenie segmentu (with) od strony (my) do jego (to)
|
||||
void
|
||||
TTraction::Connect(int my, TTraction *with, int to) {
|
||||
|
||||
hvNext[ my ] = with;
|
||||
iNext[ my ] = to;
|
||||
|
||||
if( ( hvNext[ 0 ] != nullptr )
|
||||
&& ( hvNext[ 1 ] != nullptr ) ) {
|
||||
// jeśli z obu stron podłączony to nie jest ostatnim
|
||||
iLast = 0;
|
||||
}
|
||||
|
||||
if( with == nullptr ) { return; }
|
||||
|
||||
with->hvNext[ to ] = this;
|
||||
with->iNext[ to ] = my;
|
||||
|
||||
if( ( with->hvNext[ 0 ] != nullptr )
|
||||
&& ( with->hvNext[ 1 ] != nullptr ) ) {
|
||||
// temu też, bo drugi raz łączenie się nie nie wykona
|
||||
with->iLast = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ustalenie przedostatnich przęseł
|
||||
bool TTraction::WhereIs() {
|
||||
|
||||
if( iLast ) {
|
||||
// ma już ustaloną informację o położeniu
|
||||
return ( iLast & 1 );
|
||||
}
|
||||
for( int endindex = 0; endindex < 2; ++endindex ) {
|
||||
if( hvNext[ endindex ] == nullptr ) {
|
||||
// no neighbour piece means this one is last
|
||||
iLast |= 1;
|
||||
}
|
||||
else if( hvNext[ endindex ]->hvNext[ 1 - iNext[ endindex ] ] == nullptr ) {
|
||||
// otherwise if that neighbour has no further connection on the opposite end then this piece is second-last
|
||||
iLast |= 2;
|
||||
}
|
||||
}
|
||||
return (iLast & 1); // ostatnie będą dostawać zasilanie
|
||||
}
|
||||
|
||||
void TTraction::Init()
|
||||
{ // przeliczenie parametrów
|
||||
vParametric = pPoint2 - pPoint1; // wektor mnożników parametru dla równania parametrycznego
|
||||
};
|
||||
|
||||
void TTraction::ResistanceCalc(int d, double r, TTractionPowerSource *ps)
|
||||
{ //(this) jest przęsłem zasilanym, o rezystancji (r), policzyć rezystancję zastępczą sąsiednich
|
||||
if (d >= 0)
|
||||
{ // podążanie we wskazanym kierunku
|
||||
TTraction *t = hvNext[d], *p;
|
||||
if (ps)
|
||||
psPower[d ^ 1] = ps; // podłączenie podanego
|
||||
else
|
||||
ps = psPower[d ^ 1]; // zasilacz od przeciwnej strony niż idzie analiza
|
||||
d = iNext[d]; // kierunek
|
||||
PowerState = 4;
|
||||
while( ( t != nullptr )
|
||||
&& ( t->psPower[d] == nullptr ) ) // jeśli jest jakiś kolejny i nie ma ustalonego zasilacza
|
||||
{ // ustawienie zasilacza i policzenie rezystancji zastępczej
|
||||
if( t->PowerState != 4 ) {
|
||||
// przęsła zasilającego nie modyfikować
|
||||
if( t->psPowered != nullptr ) {
|
||||
|
||||
t->PowerState = 4;
|
||||
}
|
||||
else {
|
||||
// kolor zależny od strony, z której jest zasilanie
|
||||
t->PowerState |= d ? 2 : 1;
|
||||
}
|
||||
}
|
||||
t->psPower[d] = ps; // skopiowanie wskaźnika zasilacza od danej strony
|
||||
t->fResistance[d] = r; // wpisanie rezystancji w kierunku tego zasilacza
|
||||
r += t->fResistivity * glm::length(t->vParametric); // doliczenie oporu kolejnego odcinka
|
||||
p = t; // zapamiętanie dotychczasowego
|
||||
t = p->hvNext[d ^ 1]; // podążanie w tę samą stronę
|
||||
d = p->iNext[d ^ 1];
|
||||
// w przypadku zapętlenia sieci może się zawiesić?
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // podążanie w obu kierunkach, można by rekurencją, ale szkoda zasobów
|
||||
r = 0.5 * fResistivity * glm::length(vParametric); // powiedzmy, że w zasilanym przęśle jest połowa
|
||||
if (fResistance[0] == 0.0)
|
||||
ResistanceCalc(0, r); // do tyłu (w stronę Point1)
|
||||
if (fResistance[1] == 0.0)
|
||||
ResistanceCalc(1, r); // do przodu (w stronę Point2)
|
||||
}
|
||||
};
|
||||
|
||||
void TTraction::PowerSet(TTractionPowerSource *ps)
|
||||
{ // podłączenie przęsła do zasilacza
|
||||
if (ps->bSection)
|
||||
psSection = ps; // ustalenie sekcji zasilania
|
||||
else
|
||||
{ // ustalenie punktu zasilania (nie ma jeszcze połączeń między przęsłami)
|
||||
psPowered = ps; // ustawienie bezpośredniego zasilania dla przęsła
|
||||
psPower[0] = psPower[1] = ps; // a to chyba nie jest dobry pomysł, bo nawet zasilane przęsło
|
||||
// powinno mieć wskazania na inne
|
||||
fResistance[0] = fResistance[1] = 0.0; // a liczy się tylko rezystancja zasilacza
|
||||
}
|
||||
};
|
||||
|
||||
double TTraction::VoltageGet(double u, double i)
|
||||
{ // pobranie napięcia na przęśle po podłączeniu do niego rezystancji (res) - na razie jest to prąd
|
||||
if (!psSection)
|
||||
if (!psPowered)
|
||||
return NominalVoltage; // jak nie ma zasilacza, to napięcie podane w przęśle
|
||||
// na początek można założyć, że wszystkie podstacje mają to samo napięcie i nie płynie prąd
|
||||
// pomiędzy nimi
|
||||
// dla danego przęsła mamy 3 źródła zasilania
|
||||
// 1. zasilacz psPower[0] z rezystancją fResistance[0] oraz jego wewnętrzną
|
||||
// 2. zasilacz psPower[1] z rezystancją fResistance[1] oraz jego wewnętrzną
|
||||
// 3. zasilacz psPowered z jego wewnętrzną rezystancją dla przęseł zasilanych bezpośrednio
|
||||
double res = (
|
||||
(i != 0.0) ?
|
||||
(u / i) :
|
||||
10000.0 );
|
||||
if( psPowered != nullptr ) {
|
||||
// yB: dla zasilanego nie baw się w gwiazdy, tylko bierz bezpośrednio
|
||||
return (
|
||||
( res != 0.0 ) ?
|
||||
psPowered->CurrentGet( res ) * res :
|
||||
0.0 );
|
||||
}
|
||||
if( ( psPower[0] && psPower[0]->Fuse() )
|
||||
|| ( psPower[1] && psPower[1]->Fuse() ) ) {
|
||||
// if either power source is out, so are we
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double r0t, r1t, r0g, r1g;
|
||||
double i0, i1;
|
||||
r0t = fResistance[0]; //średni pomysł, ale lepsze niż nic
|
||||
r1t = fResistance[1]; // bo nie uwzględnia spadków z innych pojazdów
|
||||
if (psPower[0] && psPower[1])
|
||||
{ // gdy przęsło jest zasilane z obu stron - mamy trójkąt: res, r0t, r1t
|
||||
// yB: Gdy wywali podstacja, to zaczyna się robić nieciekawie - napięcie w sekcji na jednym końcu jest równe zasilaniu,
|
||||
// yB: a na drugim końcu jest równe 0. Kolejna sprawa to rozróżnienie uszynienia sieci na podstacji/odłączniku (czyli
|
||||
// yB: potencjał masy na sieci) od braku zasilania (czyli odłączenie źródła od sieci i brak jego wpływu na napięcie).
|
||||
if ((r0t > 0.0) && (r1t > 0.0))
|
||||
{ // rezystancje w mianowniku nie mogą być zerowe
|
||||
r0g = res + r0t + (res * r0t) / r1t; // przeliczenie z trójkąta na gwiazdę
|
||||
r1g = res + r1t + (res * r1t) / r0t;
|
||||
// pobierane są prądy dla każdej rezystancji, a suma jest mnożona przez rezystancję
|
||||
// pojazdu w celu uzyskania napięcia
|
||||
i0 = psPower[0]->CurrentGet(r0g); // oddzielnie dla sprawdzenia
|
||||
i1 = psPower[1]->CurrentGet(r1g);
|
||||
return (i0 + i1) * res;
|
||||
}
|
||||
else if (r0t >= 0.0)
|
||||
return psPower[0]->CurrentGet(res + r0t) * res;
|
||||
else if (r1t >= 0.0)
|
||||
return psPower[1]->CurrentGet(res + r1t) * res;
|
||||
else
|
||||
return 0.0; // co z tym zrobić?
|
||||
}
|
||||
else if (psPower[0] && (r0t >= 0.0))
|
||||
{ // jeśli odcinek podłączony jest tylko z jednej strony
|
||||
return psPower[0]->CurrentGet(res + r0t) * res;
|
||||
}
|
||||
else if (psPower[1] && (r1t >= 0.0))
|
||||
return psPower[1]->CurrentGet(res + r1t) * res;
|
||||
return 0.0; // gdy nie podłączony wcale?
|
||||
};
|
||||
|
||||
glm::vec3
|
||||
TTraction::wire_color() const {
|
||||
|
||||
glm::vec3 color;
|
||||
if( !DebugTractionFlag )
|
||||
{
|
||||
switch( Material ) { // Ra: kolory podzieliłem przez 2, bo po zmianie ambient za jasne były
|
||||
// trzeba uwzględnić kierunek świecenia Słońca - tylko ze Słońcem widać kolor
|
||||
case 1: {
|
||||
if( TestFlag( DamageFlag, 1 ) ) {
|
||||
color.r = 0.00000f;
|
||||
color.g = 0.32549f;
|
||||
color.b = 0.2882353f; // zielona miedź
|
||||
}
|
||||
else {
|
||||
color.r = 0.35098f;
|
||||
color.g = 0.22549f;
|
||||
color.b = 0.1f; // czerwona miedź
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if( TestFlag( DamageFlag, 1 ) ) {
|
||||
color.r = 0.10f;
|
||||
color.g = 0.10f;
|
||||
color.b = 0.10f; // czarne Al
|
||||
}
|
||||
else {
|
||||
color.r = 0.25f;
|
||||
color.g = 0.25f;
|
||||
color.b = 0.25f; // srebrne Al
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {break; }
|
||||
}
|
||||
color *= 0.2;
|
||||
// w zaleźności od koloru swiatła
|
||||
color.r *= Global.DayLight.ambient[ 0 ];
|
||||
color.g *= Global.DayLight.ambient[ 1 ];
|
||||
color.b *= Global.DayLight.ambient[ 2 ];
|
||||
color *= 0.35f;
|
||||
}
|
||||
else {
|
||||
// tymczasowo pokazanie zasilanych odcinków
|
||||
switch( PowerState ) {
|
||||
|
||||
case 1: {
|
||||
// cyan
|
||||
color = glm::vec3 { 0.f, 174.f / 255.f, 239.f / 255.f };
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// yellow
|
||||
color = glm::vec3 { 240.f / 255.f, 228.f / 255.f, 0.f };
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
// green
|
||||
color = glm::vec3 { 0.f, 239.f / 255.f, 118.f / 255.f };
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
// white for powered, red for ends
|
||||
color = (
|
||||
psPowered != nullptr ?
|
||||
glm::vec3{ 239.f / 255.f, 239.f / 255.f, 239.f / 255.f } :
|
||||
glm::vec3{ 239.f / 255.f, 128.f / 255.f, 128.f / 255.f } );
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
}
|
||||
if( hvParallel ) { // jeśli z bieżnią wspólną, to dodatkowo przyciemniamy
|
||||
color.r *= 0.6f;
|
||||
color.g *= 0.6f;
|
||||
color.b *= 0.6f;
|
||||
}
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
// radius() subclass details, calculates node's bounding radius
|
||||
float
|
||||
TTraction::radius_() {
|
||||
|
||||
auto const points { endpoints() };
|
||||
auto radius { 0.f };
|
||||
for( auto &point : points ) {
|
||||
radius = std::max(
|
||||
radius,
|
||||
static_cast<float>( glm::length( m_area.center - point ) ) );
|
||||
}
|
||||
return radius;
|
||||
}
|
||||
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void
|
||||
TTraction::serialize_( std::ostream &Output ) const {
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void
|
||||
TTraction::deserialize_( std::istream &Input ) {
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void
|
||||
TTraction::export_as_text_( std::ostream &Output ) const {
|
||||
// header
|
||||
Output << "traction ";
|
||||
// basic attributes
|
||||
Output
|
||||
<< asPowerSupplyName << ' '
|
||||
<< NominalVoltage << ' '
|
||||
<< MaxCurrent << ' '
|
||||
<< ( fResistivity * 1000 ) << ' '
|
||||
<< (
|
||||
Material == 2 ? "al" :
|
||||
Material == 0 ? "none" :
|
||||
"cu" ) << ' '
|
||||
<< WireThickness << ' '
|
||||
<< DamageFlag << ' ';
|
||||
// path data
|
||||
Output
|
||||
<< pPoint1.x << ' ' << pPoint1.y << ' ' << pPoint1.z << ' '
|
||||
<< pPoint2.x << ' ' << pPoint2.y << ' ' << pPoint2.z << ' '
|
||||
<< pPoint3.x << ' ' << pPoint3.y << ' ' << pPoint3.z << ' '
|
||||
<< pPoint4.x << ' ' << pPoint4.y << ' ' << pPoint4.z << ' ';
|
||||
// minimum height
|
||||
Output << ( ( pPoint3.y - pPoint1.y + pPoint4.y - pPoint2.y ) * 0.5 - fHeightDifference ) << ' ';
|
||||
// segment length
|
||||
Output << static_cast<int>( iNumSections ? glm::length( pPoint1 - pPoint2 ) / iNumSections : 0.0 ) << ' ';
|
||||
// wire data
|
||||
Output
|
||||
<< Wires << ' '
|
||||
<< WireOffset << ' ';
|
||||
// visibility
|
||||
// NOTE: 'invis' would be less wrong than 'unvis', but potentially incompatible with old 3rd party tools
|
||||
Output << ( m_visible ? "vis" : "unvis" ) << ' ';
|
||||
// optional attributes
|
||||
if( false == asParallel.empty() ) {
|
||||
Output << "parallel " << asParallel << ' ';
|
||||
}
|
||||
// footer
|
||||
Output
|
||||
<< "endtraction"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// legacy method, initializes traction after deserialization from scenario file
|
||||
void
|
||||
traction_table::InitTraction() {
|
||||
|
||||
//łączenie drutów ze sobą oraz z torami i eventami
|
||||
// TGroundNode *nCurrent, *nTemp;
|
||||
// TTraction *tmp; // znalezione przęsło
|
||||
|
||||
int connection { -1 };
|
||||
TTraction *matchingtraction { nullptr };
|
||||
|
||||
for( auto *traction : m_items ) {
|
||||
// podłączenie do zasilacza, żeby można było sumować prąd kilku pojazdów
|
||||
// a jednocześnie z jednego miejsca zmieniać napięcie eventem
|
||||
// wykonywane najpierw, żeby można było logować podłączenie 2 zasilaczy do jednego drutu
|
||||
// izolator zawieszony na przęśle jest ma być osobnym odcinkiem drutu o długości ok. 1m,
|
||||
// podłączonym do zasilacza o nazwie "*" (gwiazka); "none" nie będzie odpowiednie
|
||||
auto *powersource = simulation::Powergrid.find( traction->asPowerSupplyName );
|
||||
if( powersource ) {
|
||||
// jak zasilacz znaleziony to podłączyć do przęsła
|
||||
traction->PowerSet( powersource );
|
||||
}
|
||||
else {
|
||||
if( ( traction->asPowerSupplyName != "*" ) // gwiazdka dla przęsła z izolatorem
|
||||
&& ( traction->asPowerSupplyName != "none" ) ) { // dopuszczamy na razie brak podłączenia?
|
||||
// logowanie błędu i utworzenie zasilacza o domyślnej zawartości
|
||||
ErrorLog( "Bad scenario: traction piece connected to nonexistent power source \"" + traction->asPowerSupplyName + "\"" );
|
||||
scene::node_data nodedata;
|
||||
nodedata.name = traction->asPowerSupplyName;
|
||||
powersource = new TTractionPowerSource( nodedata );
|
||||
powersource->IsAutogenerated = true;
|
||||
powersource->Init( traction->NominalVoltage, traction->MaxCurrent );
|
||||
simulation::Powergrid.insert( powersource );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EU07_IGNORE_LEGACYPROCESSINGORDER
|
||||
for( auto *traction : m_items ) {
|
||||
#else
|
||||
// NOTE: legacy code peformed item operations last-to-first due to way the items were added to the list
|
||||
// this had impact in situations like two possible connection candidates, where only the first one would be used
|
||||
for( auto first = std::rbegin(m_items); first != std::rend(m_items); ++first ) {
|
||||
auto *traction = *first;
|
||||
#endif
|
||||
if( traction->hvNext[ 0 ] == nullptr ) {
|
||||
// tylko jeśli jeszcze nie podłączony
|
||||
std::tie( matchingtraction, connection ) = simulation::Region->find_traction( traction->pPoint1, traction );
|
||||
switch (connection) {
|
||||
case 0:
|
||||
case 1: {
|
||||
traction->Connect( 0, matchingtraction, connection );
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( traction->hvNext[ 0 ] ) {
|
||||
// jeśli został podłączony
|
||||
if( ( traction->psSection != nullptr )
|
||||
&& ( matchingtraction->psSection != nullptr ) ) {
|
||||
// tylko przęsło z izolatorem może nie mieć zasilania, bo ma 2, trzeba sprawdzać sąsiednie
|
||||
if( traction->psSection != matchingtraction->psSection ) {
|
||||
// połączone odcinki mają różne zasilacze
|
||||
// to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo błąd
|
||||
if( ( true == traction->psSection->bSection )
|
||||
&& ( false == matchingtraction->psSection->bSection ) ) {
|
||||
//(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji
|
||||
matchingtraction->PowerSet( traction->psSection ); // zastąpienie wskazaniem sekcji
|
||||
}
|
||||
else if( ( false == traction->psSection->bSection )
|
||||
&& ( true == matchingtraction->psSection->bSection ) ) {
|
||||
//(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji
|
||||
traction->PowerSet( matchingtraction->psSection ); // zastąpienie wskazaniem sekcji
|
||||
}
|
||||
else {
|
||||
// jeśli obie to sekcje albo obie podstacje, to będzie błąd
|
||||
ErrorLog( "Bad scenario: faulty traction power connection at location " + to_string( traction->pPoint1 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if( traction->hvNext[ 1 ] == nullptr ) {
|
||||
// tylko jeśli jeszcze nie podłączony
|
||||
std::tie( matchingtraction, connection ) = simulation::Region->find_traction( traction->pPoint2, traction );
|
||||
switch (connection) {
|
||||
case 0:
|
||||
case 1: {
|
||||
traction->Connect( 1, matchingtraction, connection );
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( traction->hvNext[ 1 ] ) {
|
||||
// jeśli został podłączony
|
||||
if( ( traction->psSection != nullptr )
|
||||
&& ( matchingtraction->psSection != nullptr ) ) {
|
||||
// tylko przęsło z izolatorem może nie mieć zasilania, bo ma 2, trzeba sprawdzać sąsiednie
|
||||
if( traction->psSection != matchingtraction->psSection ) {
|
||||
// to może być albo podłączenie podstacji lub kabiny sekcyjnej do sekcji, albo błąd
|
||||
if( ( true == traction->psSection->bSection )
|
||||
&& ( false == matchingtraction->psSection->bSection ) ) {
|
||||
//(tmp->psSection) jest podstacją, a (Traction->psSection) nazwą sekcji
|
||||
matchingtraction->PowerSet( traction->psSection ); // zastąpienie wskazaniem sekcji
|
||||
}
|
||||
else if( ( false == traction->psSection->bSection )
|
||||
&& ( true == matchingtraction->psSection->bSection ) ) {
|
||||
//(Traction->psSection) jest podstacją, a (tmp->psSection) nazwą sekcji
|
||||
traction->PowerSet( matchingtraction->psSection ); // zastąpienie wskazaniem sekcji
|
||||
}
|
||||
else {
|
||||
// jeśli obie to sekcje albo obie podstacje, to będzie błąd
|
||||
ErrorLog( "Bad scenario: faulty traction power connection at location " + to_string( traction->pPoint2 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto endcount { 0 };
|
||||
for( auto *traction : m_items ) {
|
||||
// operacje mające na celu wykrywanie bieżni wspólnych i łączenie przęseł naprążania
|
||||
if( traction->WhereIs() ) {
|
||||
// true for outer pieces of the traction section
|
||||
traction->iTries = 5; // oznaczanie końcowych
|
||||
++endcount;
|
||||
}
|
||||
if (traction->fResistance[0] == 0.0) {
|
||||
// obliczanie przęseł w segmencie z bezpośrednim zasilaniem
|
||||
traction->ResistanceCalc();
|
||||
traction->iTries = 0; // nie potrzeba mu szukać zasilania
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<TTraction *> ends; ends.reserve( endcount );
|
||||
for( auto *traction : m_items ) {
|
||||
//łączenie bieżni wspólnych, w tym oznaczanie niepodanych jawnie
|
||||
if( false == traction->asParallel.empty() ) {
|
||||
// będzie wskaźnik na inne przęsło
|
||||
if( ( traction->asParallel == "none" )
|
||||
|| ( traction->asParallel == "*" ) ) {
|
||||
// jeśli nieokreślone
|
||||
traction->iLast |= 2; // jakby przedostatni - niech po prostu szuka (iLast już przeliczone)
|
||||
}
|
||||
else if( traction->hvParallel == nullptr ) {
|
||||
// jeśli jeszcze nie został włączony w kółko
|
||||
auto *nTemp = find( traction->asParallel );
|
||||
if( nTemp != nullptr ) {
|
||||
// o ile zostanie znalezione przęsło o takiej nazwie
|
||||
if( nTemp->hvParallel == nullptr ) {
|
||||
// jeśli tamten jeszcze nie ma wskaźnika bieżni wspólnej
|
||||
traction->hvParallel = nTemp; // wpisać siebie i dalej dać mu wskaźnik zwrotny
|
||||
}
|
||||
else {
|
||||
// a jak ma, to albo dołączyć się do kółeczka
|
||||
traction->hvParallel = nTemp->hvParallel; // przjąć dotychczasowy wskaźnik od niego
|
||||
}
|
||||
nTemp->hvParallel = traction; // i na koniec ustawienie wskaźnika zwrotnego
|
||||
}
|
||||
if( traction->hvParallel == nullptr ) {
|
||||
ErrorLog( "Missed overhead: " + traction->asParallel ); // logowanie braku
|
||||
}
|
||||
}
|
||||
}
|
||||
if( traction->iTries == 5 ) {
|
||||
// jeśli zaznaczony do podłączenia
|
||||
// wypełnianie tabeli końców w celu szukania im połączeń
|
||||
ends.emplace_back( traction );
|
||||
}
|
||||
}
|
||||
|
||||
bool connected; // nieefektywny przebieg kończy łączenie
|
||||
do {
|
||||
// ustalenie zastępczej rezystancji dla każdego przęsła
|
||||
// flaga podłączonych przęseł końcowych: -1=puste wskaźniki, 0=coś zostało, 1=wykonano łączenie
|
||||
connected = false;
|
||||
for( auto &end : ends ) {
|
||||
// załatwione będziemy zerować
|
||||
if( end == nullptr ) { continue; }
|
||||
// każdy przebieg to próba podłączenia końca segmentu naprężania do innego zasilanego przęsła
|
||||
if( end->hvNext[ 0 ] != nullptr ) {
|
||||
// jeśli końcowy ma ciąg dalszy od strony 0 (Point1), szukamy odcinka najbliższego do Point2
|
||||
std::tie( matchingtraction, connection ) = simulation::Region->find_traction( end->pPoint2, end, 0 );
|
||||
if( matchingtraction != nullptr ) {
|
||||
// jak znalezione przęsło z zasilaniem, to podłączenie "równoległe"
|
||||
end->ResistanceCalc( 0, matchingtraction->fResistance[ connection ], matchingtraction->psPower[ connection ] );
|
||||
// jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo dotrze
|
||||
connected = true;
|
||||
end = nullptr;
|
||||
}
|
||||
}
|
||||
else if( end->hvNext[ 1 ] != nullptr ) {
|
||||
// jeśli końcowy ma ciąg dalszy od strony 1 (Point2), szukamy odcinka najbliższego do Point1
|
||||
std::tie( matchingtraction, connection ) = simulation::Region->find_traction( end->pPoint1, end, 1 );
|
||||
if( matchingtraction != nullptr ) {
|
||||
// jak znalezione przęsło z zasilaniem, to podłączenie "równoległe"
|
||||
end->ResistanceCalc( 1, matchingtraction->fResistance[ connection ], matchingtraction->psPower[ connection ] );
|
||||
// jak coś zostało podłączone, to może zasilanie gdzieś dodatkowo dotrze
|
||||
connected = true;
|
||||
end = nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// gdy koniec jest samotny, to na razie nie zostanie podłączony (nie powinno takich być)
|
||||
end = nullptr;
|
||||
}
|
||||
}
|
||||
} while( true == connected );
|
||||
}
|
||||
102
world/Traction.h
Normal file
102
world/Traction.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "scenenode.h"
|
||||
#include "Segment.h"
|
||||
#include "material.h"
|
||||
#include "Names.h"
|
||||
|
||||
class TTractionPowerSource;
|
||||
|
||||
class TTraction : public scene::basic_node
|
||||
{
|
||||
friend opengl_renderer;
|
||||
friend opengl33_renderer;
|
||||
|
||||
public: // na razie
|
||||
TTractionPowerSource *psPower[ 2 ] { nullptr, nullptr }; // najbliższe zasilacze z obu kierunków
|
||||
TTractionPowerSource *psPowered { nullptr }; // ustawione tylko dla bezpośrednio zasilanego przęsła
|
||||
TTraction *hvNext[ 2 ] { nullptr, nullptr }; //łączenie drutów w sieć
|
||||
int iNext[ 2 ] { 0, 0 }; // do którego końca się łączy
|
||||
int iLast { 0 }; //że niby ostatni drut // ustawiony bit 0, jeśli jest ostatnim drutem w sekcji; bit1 - przedostatni
|
||||
public:
|
||||
glm::dvec3 pPoint1, pPoint2, pPoint3, pPoint4;
|
||||
glm::dvec3 vParametric; // współczynniki równania parametrycznego odcinka
|
||||
double fHeightDifference { 0.0 };
|
||||
int iNumSections { 0 };
|
||||
float NominalVoltage { 0.0f };
|
||||
float MaxCurrent { 0.0f };
|
||||
float fResistivity { 0.0f }; //[om/m], przeliczone z [om/km]
|
||||
unsigned int Material { 0 }; // 1: Cu, 2: Al
|
||||
float WireThickness { 0.0f };
|
||||
unsigned int DamageFlag { 0 }; // 1: zasniedziale, 128: zerwana
|
||||
int Wires { 2 };
|
||||
float WireOffset { 0.0f };
|
||||
std::string asPowerSupplyName; // McZapkie: nazwa podstacji trakcyjnej
|
||||
TTractionPowerSource *psSection { nullptr }; // zasilacz (opcjonalnie może to być pulpit sterujący EL2 w hali!)
|
||||
std::string asParallel; // nazwa przęsła, z którym może być bieżnia wspólna
|
||||
TTraction *hvParallel { nullptr }; // jednokierunkowa i zapętlona lista przęseł ewentualnej bieżni wspólnej
|
||||
float fResistance[ 2 ] { -1.0f, -1.0f }; // rezystancja zastępcza do punktu zasilania (0: przęsło zasilane, <0: do policzenia)
|
||||
int iTries { 1 }; // 0 is used later down the road to mark directly powered pieces
|
||||
int PowerState { 0 }; // type of incoming power, if any
|
||||
// visualization data
|
||||
glm::dvec3 m_origin;
|
||||
gfx::geometry_handle m_geometry;
|
||||
|
||||
explicit TTraction( scene::node_data const &Nodedata );
|
||||
|
||||
void Load( cParser *parser, glm::dvec3 const &pOrigin );
|
||||
// set origin point
|
||||
void
|
||||
origin( glm::dvec3 Origin ) {
|
||||
m_origin = Origin; }
|
||||
// retrieves list of the track's end points
|
||||
std::vector<glm::dvec3>
|
||||
endpoints() const;
|
||||
// creates geometry data in specified geometry bank. returns: number of created elements, or NULL
|
||||
// NOTE: deleting nodes doesn't currently release geometry data owned by the node. TODO: implement erasing individual geometry chunks and banks
|
||||
std::size_t create_geometry( gfx::geometrybank_handle const &Bank );
|
||||
int TestPoint(glm::dvec3 const &Point);
|
||||
void Connect(int my, TTraction *with, int to);
|
||||
void Init();
|
||||
bool WhereIs();
|
||||
void ResistanceCalc(int d = -1, double r = 0, TTractionPowerSource *ps = nullptr);
|
||||
void PowerSet(TTractionPowerSource *ps);
|
||||
double VoltageGet(double u, double i);
|
||||
|
||||
private:
|
||||
// methods
|
||||
glm::vec3 wire_color() const;
|
||||
// radius() subclass details, calculates node's bounding radius
|
||||
float radius_();
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void serialize_( std::ostream &Output ) const;
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void deserialize_( std::istream &Input );
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void export_as_text_( std::ostream &Output ) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// collection of virtual tracks and roads present in the scene
|
||||
class traction_table : public basic_table<TTraction> {
|
||||
|
||||
public:
|
||||
// legacy method, initializes traction after deserialization from scenario file
|
||||
void
|
||||
InitTraction();
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
203
world/TractionPower.cpp
Normal file
203
world/TractionPower.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
/*
|
||||
MaSzyna EU07 locomotive simulator component
|
||||
Copyright (C) 2004 Maciej Czapkiewicz and others
|
||||
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "TractionPower.h"
|
||||
|
||||
#include "parser.h"
|
||||
#include "Logs.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
TTractionPowerSource::TTractionPowerSource( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {}
|
||||
// legacy constructor
|
||||
|
||||
void TTractionPowerSource::Init(double const u, double const i)
|
||||
{ // ustawianie zasilacza przy braku w scenerii
|
||||
NominalVoltage = u;
|
||||
VoltageFrequency = 0;
|
||||
MaxOutputCurrent = i;
|
||||
};
|
||||
|
||||
bool TTractionPowerSource::Load(cParser *parser) {
|
||||
|
||||
parser->getTokens( 10, false );
|
||||
*parser
|
||||
>> m_area.center.x
|
||||
>> m_area.center.y
|
||||
>> m_area.center.z
|
||||
>> NominalVoltage
|
||||
>> VoltageFrequency
|
||||
>> InternalRes
|
||||
>> MaxOutputCurrent
|
||||
>> FastFuseTimeOut
|
||||
>> FastFuseRepetition
|
||||
>> SlowFuseTimeOut;
|
||||
|
||||
std::string token { parser->getToken<std::string>() };
|
||||
if( token == "recuperation" ) {
|
||||
Recuperation = true;
|
||||
}
|
||||
else if( token == "section" ) {
|
||||
// odłącznik sekcyjny
|
||||
// nie jest źródłem zasilania, a jedynie informuje o prądzie odłączenia sekcji z obwodu
|
||||
bSection = true;
|
||||
}
|
||||
// skip rest of the section
|
||||
while( ( false == token.empty() )
|
||||
&& ( token != "end" ) ) {
|
||||
|
||||
token = parser->getToken<std::string>();
|
||||
}
|
||||
|
||||
if( InternalRes < 0.1 ) {
|
||||
// coś mała ta rezystancja była...
|
||||
// tak około 0.2, wg
|
||||
// http://www.ikolej.pl/fileadmin/user_upload/Seminaria_IK/13_05_07_Prezentacja_Kruczek.pdf
|
||||
InternalRes = 0.2;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
bool TTractionPowerSource::Update(double dt)
|
||||
{ // powinno być wykonane raz na krok fizyki
|
||||
// iloczyn napięcia i admitancji daje prąd
|
||||
if( FastFuse || SlowFuse ) {
|
||||
TotalCurrent = 0.0;
|
||||
}
|
||||
if (TotalCurrent > MaxOutputCurrent) {
|
||||
|
||||
FastFuse = true;
|
||||
FuseCounter += 1;
|
||||
if (FuseCounter > FastFuseRepetition) {
|
||||
|
||||
SlowFuse = true;
|
||||
ErrorLog( "Power overload: \"" + m_name + "\" disabled for " + std::to_string( SlowFuseTimeOut ) + "s" );
|
||||
}
|
||||
else {
|
||||
ErrorLog( "Power overload: \"" + m_name + "\" disabled for " + std::to_string( FastFuseTimeOut ) + "s" );
|
||||
}
|
||||
|
||||
FuseTimer = 0;
|
||||
}
|
||||
if (FastFuse || SlowFuse)
|
||||
{ // jeśli któryś z bezpieczników zadziałał
|
||||
TotalAdmitance = 0;
|
||||
FuseTimer += dt;
|
||||
if (!SlowFuse)
|
||||
{ // gdy szybki, odczekać krótko i załączyć
|
||||
if (FuseTimer > FastFuseTimeOut)
|
||||
FastFuse = false;
|
||||
}
|
||||
else if (FuseTimer > SlowFuseTimeOut)
|
||||
{
|
||||
SlowFuse = false;
|
||||
FuseCounter = 0; // dajemy znów szansę
|
||||
}
|
||||
}
|
||||
TotalPreviousAdmitance = TotalAdmitance; // używamy admitancji z poprzedniego kroku
|
||||
if (TotalPreviousAdmitance == 0.0)
|
||||
TotalPreviousAdmitance = 1e-10; // przynajmniej minimalna upływność
|
||||
TotalAdmitance = 1e-10; // a w aktualnym kroku sumujemy admitancję
|
||||
return true;
|
||||
};
|
||||
|
||||
double TTractionPowerSource::CurrentGet(double res)
|
||||
{ // pobranie wartości prądu przypadającego na rezystancję (res)
|
||||
// niech pamięta poprzednią admitancję i wg niej przydziela prąd
|
||||
if (SlowFuse || FastFuse)
|
||||
{ // czekanie na zanik obciążenia sekcji
|
||||
if (res < 100.0) // liczenie czasu dopiero, gdy obciążenie zniknie
|
||||
FuseTimer = 0;
|
||||
return 0;
|
||||
}
|
||||
if ((res > 0) || ((res < 0) && (Recuperation || true)))
|
||||
TotalAdmitance += 1.0 / res; // połączenie równoległe rezystancji jest równoważne sumie admitancji
|
||||
float NomVolt = (TotalPreviousAdmitance < 0 ? NominalVoltage * 1.083 : NominalVoltage);
|
||||
TotalCurrent = (TotalPreviousAdmitance != 0.0) ?
|
||||
NomVolt / (InternalRes + 1.0 / TotalPreviousAdmitance) :
|
||||
0.0; // napięcie dzielone przez sumę rezystancji wewnętrznej i obciążenia
|
||||
OutputVoltage = NomVolt - InternalRes * TotalCurrent; // napięcie na obciążeniu
|
||||
return TotalCurrent / (res * TotalPreviousAdmitance); // prąd proporcjonalny do udziału (1/res)
|
||||
// w całkowitej admitancji
|
||||
};
|
||||
|
||||
void TTractionPowerSource::PowerSet(TTractionPowerSource *ps)
|
||||
{ // wskazanie zasilacza w obiekcie sekcji
|
||||
if (!psNode[0])
|
||||
psNode[0] = ps;
|
||||
else if (!psNode[1])
|
||||
psNode[1] = ps;
|
||||
// else ErrorLog("nie może być więcej punktów zasilania niż dwa");
|
||||
};
|
||||
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void
|
||||
TTractionPowerSource::serialize_( std::ostream &Output ) const {
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void
|
||||
TTractionPowerSource::deserialize_( std::istream &Input ) {
|
||||
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void
|
||||
TTractionPowerSource::export_as_text_( std::ostream &Output ) const {
|
||||
// header
|
||||
Output << "tractionpowersource ";
|
||||
// placement
|
||||
Output
|
||||
<< location().x << ' '
|
||||
<< location().y << ' '
|
||||
<< location().z << ' ';
|
||||
// basic attributes
|
||||
Output
|
||||
<< NominalVoltage << ' '
|
||||
<< VoltageFrequency << ' '
|
||||
<< InternalRes << ' '
|
||||
<< MaxOutputCurrent << ' '
|
||||
<< FastFuseTimeOut << ' '
|
||||
<< FastFuseRepetition << ' '
|
||||
<< SlowFuseTimeOut << ' ';
|
||||
// optional attributes
|
||||
if( true == Recuperation ) {
|
||||
Output << "recuperation ";
|
||||
}
|
||||
if( true == bSection ) {
|
||||
Output << "section ";
|
||||
}
|
||||
// footer
|
||||
Output
|
||||
<< "end"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// legacy method, calculates changes in simulation state over specified time
|
||||
void
|
||||
powergridsource_table::update( double const Deltatime ) {
|
||||
|
||||
for( auto *powersource : m_items ) {
|
||||
powersource->Update( Deltatime );
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
79
world/TractionPower.h
Normal file
79
world/TractionPower.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Classes.h"
|
||||
#include "scenenode.h"
|
||||
#include "Names.h"
|
||||
|
||||
class TTractionPowerSource : public scene::basic_node {
|
||||
|
||||
friend class debug_panel;
|
||||
|
||||
public:
|
||||
// constructor
|
||||
TTractionPowerSource( scene::node_data const &Nodedata );
|
||||
// methods
|
||||
void Init(double const u, double const i);
|
||||
bool Load(cParser *parser);
|
||||
bool Update(double dt);
|
||||
double CurrentGet(double res);
|
||||
void VoltageSet(double const v) {
|
||||
NominalVoltage = v; };
|
||||
void PowerSet(TTractionPowerSource *ps);
|
||||
bool Fuse() const {
|
||||
return ( FastFuse || SlowFuse ); }
|
||||
// members
|
||||
TTractionPowerSource *psNode[ 2 ] = { nullptr, nullptr }; // zasilanie na końcach dla sekcji
|
||||
bool bSection = false; // czy jest sekcją
|
||||
bool IsAutogenerated { false }; // indicates autogenerated source for individual traction piece
|
||||
|
||||
private:
|
||||
// methods
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
void serialize_( std::ostream &Output ) const;
|
||||
// deserialize() subclass details, restores content of the subclass from provided stream
|
||||
void deserialize_( std::istream &Input );
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void export_as_text_( std::ostream &Output ) const;
|
||||
|
||||
// members
|
||||
double NominalVoltage = 0.0;
|
||||
double VoltageFrequency = 0.0;
|
||||
double InternalRes = 0.2;
|
||||
double MaxOutputCurrent = 0.0;
|
||||
double FastFuseTimeOut = 1.0;
|
||||
int FastFuseRepetition = 3;
|
||||
double SlowFuseTimeOut = 60.0;
|
||||
bool Recuperation = false;
|
||||
|
||||
double TotalCurrent = 0.0;
|
||||
double TotalAdmitance = 1e-10; // 10Mom - jakaś tam upływność
|
||||
double TotalPreviousAdmitance = 1e-10; // zero jest szkodliwe
|
||||
double OutputVoltage = 0.0;
|
||||
bool FastFuse = false;
|
||||
bool SlowFuse = false;
|
||||
double FuseTimer = 0.0;
|
||||
int FuseCounter = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
// collection of generators for power grid present in the scene
|
||||
class powergridsource_table : public basic_table<TTractionPowerSource> {
|
||||
|
||||
public:
|
||||
// legacy method, calculates changes in simulation state over specified time
|
||||
void
|
||||
update( double const Deltatime );
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
285
world/TrkFoll.cpp
Normal file
285
world/TrkFoll.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
/*
|
||||
MaSzyna EU07 locomotive simulator
|
||||
Copyright (C) 2001-2004 Marcin Wozniak and others
|
||||
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "TrkFoll.h"
|
||||
|
||||
#include "simulation.h"
|
||||
#include "Globals.h"
|
||||
#include "DynObj.h"
|
||||
#include "Driver.h"
|
||||
#include "Logs.h"
|
||||
|
||||
TTrackFollower::~TTrackFollower()
|
||||
{
|
||||
}
|
||||
|
||||
bool TTrackFollower::Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir)
|
||||
{
|
||||
fDirection = fDir;
|
||||
Owner = NewOwner;
|
||||
SetCurrentTrack(pTrack, 0);
|
||||
iEventFlag = 3; // na torze startowym również wykonać eventy 1/2
|
||||
iEventallFlag = 3;
|
||||
if ((pCurrentSegment)) // && (pCurrentSegment->GetLength()<fFirstDistance))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TTrackFollower::Reset()
|
||||
{
|
||||
fCurrentDistance = 0.0;
|
||||
fDirection = 1.0;
|
||||
}
|
||||
|
||||
TTrack * TTrackFollower::SetCurrentTrack(TTrack *pTrack, int end)
|
||||
{ // przejechanie na inny odcinkek toru, z ewentualnym rozpruciem
|
||||
if (pTrack)
|
||||
switch (pTrack->eType)
|
||||
{
|
||||
case tt_Switch: // jeśli zwrotnica, to przekładamy ją, aby uzyskać dobry segment
|
||||
{
|
||||
int i = (end ? pCurrentTrack->iNextDirection : pCurrentTrack->iPrevDirection);
|
||||
if (i > 0) // jeżeli wjazd z ostrza
|
||||
pTrack->SwitchForced(i >> 1, Owner); // to przełożenie zwrotnicy - rozprucie!
|
||||
}
|
||||
break;
|
||||
case tt_Cross: // skrzyżowanie trzeba tymczasowo przełączyć, aby wjechać na właściwy tor
|
||||
{
|
||||
iSegment = Owner->RouteWish(pTrack); // nr segmentu został ustalony podczas skanowania
|
||||
// Ra 2014-08: aby ustalić dalszą trasę, należy zapytać AI - trasa jest ustalana podczas
|
||||
// skanowania
|
||||
// Ra 15-01: zapytanie AI nie wybiera segmentu - kolejny skanujący może przestawić
|
||||
// pTrack->CrossSegment(end?pCurrentTrack->iNextDirection:pCurrentTrack->iPrevDirection,i);
|
||||
// //ustawienie właściwego wskaźnika
|
||||
// powinno zwracać kierunek do zapamiętania, bo segmenty mogą mieć różny kierunek
|
||||
// if fDirection=(iSegment>0)?1.0:-1.0; //kierunek na tym segmencie jest ustalany
|
||||
// bezpośrednio
|
||||
if (iSegment == 0)
|
||||
{ // to jest błędna sytuacja - generuje pętle zawracające za skrzyżowaniem - ustalić,
|
||||
// kiedy powstaje!
|
||||
iSegment = 1; // doraźna poprawka
|
||||
}
|
||||
if ((end ? iSegment : -iSegment) < 0)
|
||||
fDirection = -fDirection; // wtórna zmiana
|
||||
pTrack->SwitchForced(abs(iSegment) - 1, NULL); // wybór zapamiętanego segmentu
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!pTrack) {
|
||||
// gdy nie ma toru w kierunku jazdy tworzenie toru wykolejącego na przedłużeniu pCurrentTrack
|
||||
pTrack = pCurrentTrack->NullCreate(end);
|
||||
if (!end) // jeśli dodana od strony zero, to zmiana kierunku
|
||||
fDirection = -fDirection; // wtórna zmiana
|
||||
}
|
||||
else
|
||||
{ // najpierw +1, później -1, aby odcinek izolowany wspólny dla tych torów nie wykrył zera
|
||||
pTrack->AxleCounter(+1, Owner); // zajęcie nowego toru
|
||||
if (pCurrentTrack)
|
||||
pCurrentTrack->AxleCounter(-1, Owner); // opuszczenie tamtego toru
|
||||
}
|
||||
pCurrentTrack = pTrack;
|
||||
pCurrentSegment = ( pCurrentTrack != nullptr ? pCurrentTrack->CurrentSegment() : nullptr );
|
||||
if (!pCurrentTrack)
|
||||
Error(Owner->MoverParameters->Name + " at NULL track");
|
||||
return pCurrentTrack;
|
||||
};
|
||||
|
||||
bool TTrackFollower::Move(double fDistance, bool bPrimary)
|
||||
{ // przesuwanie wózka po torach o odległość (fDistance), z wyzwoleniem eventów
|
||||
// bPrimary=true - jest pierwszą osią w pojeździe, czyli generuje eventy i przepisuje pojazd
|
||||
// Ra: zwraca false, jeśli pojazd ma być usunięty
|
||||
auto const ismoving { /* ( std::abs( fDistance ) > 0.01 ) && */ ( Owner->GetVelocity() > 0.01 ) };
|
||||
fDistance *= fDirection; // dystans mnożnony przez kierunek
|
||||
double s; // roboczy dystans
|
||||
double dir; // zapamiętany kierunek do sprawdzenia, czy się zmienił
|
||||
bool bCanSkip; // czy przemieścić pojazd na inny tor
|
||||
while (true) // pętla wychodzi, gdy przesunięcie wyjdzie zerowe
|
||||
{ // pętla przesuwająca wózek przez kolejne tory, aż do trafienia w jakiś
|
||||
if( pCurrentTrack == nullptr ) { return false; } // nie ma toru, to nie ma przesuwania
|
||||
// TODO: refactor following block as track method
|
||||
if( pCurrentTrack->m_events ) { // sumaryczna informacja o eventach
|
||||
// omijamy cały ten blok, gdy tor nie ma on żadnych eventów (większość nie ma)
|
||||
int const eventfilter { (
|
||||
false == ismoving ? 0 : // only moving vehicles activate events type 1/2
|
||||
false == bPrimary ? 0 : // only primary axle activates events type 1/2
|
||||
Owner->ctOwner == nullptr ?
|
||||
( fDistance > 0 ? 1 : -1 ) : // loose vehicle has no means to determine 'intended' direction so the filter does effectively nothing in such case
|
||||
( fDirection > 0 ? 1 : -1 ) * Owner->ctOwner->Direction() * ( Owner->ctOwner->Vehicle()->DirectionGet() == Owner->DirectionGet() ? 1 : -1 ) ) };
|
||||
|
||||
if( false == ismoving ) {
|
||||
//McZapkie-140602: wyzwalanie zdarzenia gdy pojazd stoi
|
||||
if( ( Owner->Mechanik != nullptr )
|
||||
&& ( Owner->Mechanik->primary() ) ) {
|
||||
// tylko dla jednego członu
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events0, Owner );
|
||||
}
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events0all, Owner );
|
||||
}
|
||||
else if( (fDistance < 0) && ( eventfilter < 0 ) ) {
|
||||
// event1, eventall1
|
||||
if( SetFlag( iEventFlag, -1 ) ) {
|
||||
// zawsze zeruje flagę sprawdzenia, jak mechanik dosiądzie, to się nie wykona
|
||||
if( ( Owner->Mechanik != nullptr )
|
||||
&& ( Owner->Mechanik->primary() ) ) {
|
||||
// tylko dla jednego członu
|
||||
// McZapkie-280503: wyzwalanie event tylko dla pojazdow z obsada
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events1, Owner );
|
||||
}
|
||||
}
|
||||
if( SetFlag( iEventallFlag, -1 ) ) {
|
||||
// McZapkie-280503: wyzwalanie eventall dla wszystkich pojazdow
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events1all, Owner );
|
||||
}
|
||||
}
|
||||
else if( ( fDistance > 0 ) && ( eventfilter > 0 ) ) {
|
||||
// event2, eventall2
|
||||
if( SetFlag( iEventFlag, -2 ) ) {
|
||||
// zawsze ustawia flagę sprawdzenia, jak mechanik dosiądzie, to się nie wykona
|
||||
if( ( Owner->Mechanik != nullptr )
|
||||
&& ( Owner->Mechanik->primary() ) ) {
|
||||
// tylko dla jednego członu
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events2, Owner );
|
||||
}
|
||||
}
|
||||
if( SetFlag( iEventallFlag, -2 ) ) {
|
||||
// sprawdza i zeruje na przyszłość, true jeśli zmieni z 2 na 0
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events2all, Owner );
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!pCurrentSegment) // jeżeli nie ma powiązanego segmentu toru?
|
||||
return false;
|
||||
// if (fDistance==0.0) return true; //Ra: jak stoi, to chyba dalej nie ma co kombinować?
|
||||
s = fCurrentDistance + fDistance; // doliczenie przesunięcia
|
||||
// Ra: W Point2 toru może znajdować się "dziura", która zamieni energię kinetyczną
|
||||
// ruchu wzdłużnego na energię potencjalną, zamieniającą się potem na energię
|
||||
// sprężystości na amortyzatorach. Należałoby we wpisie toru umieścić współczynnik
|
||||
// podziału energii kinetycznej.
|
||||
// Współczynnik normalnie 1, z dziurą np. 0.99, a -1 będzie oznaczało 100% odbicia (kozioł).
|
||||
// Albo w postaci kąta: normalnie 0°, a 180° oznacza 100% odbicia (cosinus powyższego).
|
||||
/*
|
||||
if (pCurrentTrack->eType==tt_Cross)
|
||||
{//zjazdu ze skrzyżowania nie da się określić przez (iPrevDirection) i (iNextDirection)
|
||||
//int segment=Owner->RouteWish(pCurrentTrack); //numer segmentu dla skrzyżowań
|
||||
//pCurrentTrack->SwitchForced(abs(segment)-1,NULL); //tymczasowo ustawienie tego segmentu
|
||||
//pCurrentSegment=pCurrentTrack->CurrentSegment(); //odświeżyć sobie wskaźnik segmentu
|
||||
(?)
|
||||
}
|
||||
*/
|
||||
if (s < 0)
|
||||
{ // jeśli przekroczenie toru od strony Point1
|
||||
bCanSkip = ( bPrimary && pCurrentTrack->CheckDynamicObject( Owner ) );
|
||||
if( bCanSkip ) {
|
||||
// tylko główna oś przenosi pojazd do innego toru
|
||||
// zdejmujemy pojazd z dotychczasowego toru
|
||||
Owner->MyTrack->RemoveDynamicObject( Owner );
|
||||
}
|
||||
dir = fDirection;
|
||||
if (pCurrentTrack->eType == tt_Cross)
|
||||
{
|
||||
if (!SetCurrentTrack(pCurrentTrack->Connected(iSegment, fDirection), 0))
|
||||
return false; // wyjście z błędem
|
||||
}
|
||||
else if (!SetCurrentTrack(pCurrentTrack->Connected(-1, fDirection), 0)) // ustawia fDirection
|
||||
return false; // wyjście z błędem
|
||||
if (dir == fDirection) //(pCurrentTrack->iPrevDirection)
|
||||
{ // gdy kierunek bez zmiany (Point1->Point2)
|
||||
fCurrentDistance = pCurrentSegment->GetLength();
|
||||
fDistance = s;
|
||||
}
|
||||
else
|
||||
{ // gdy zmiana kierunku toru (Point1->Point1)
|
||||
fCurrentDistance = 0;
|
||||
fDistance = -s;
|
||||
}
|
||||
if (bCanSkip)
|
||||
{ // jak główna oś, to dodanie pojazdu do nowego toru
|
||||
pCurrentTrack->AddDynamicObject(Owner);
|
||||
iEventFlag = 3; // McZapkie-020602: umozliwienie uruchamiania event1,2 po zmianie toru
|
||||
iEventallFlag = 3; // McZapkie-280503: jw, dla eventall1,2
|
||||
if (!Owner->MyTrack)
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if (s > pCurrentSegment->GetLength())
|
||||
{ // jeśli przekroczenie toru od strony Point2
|
||||
bCanSkip = ( bPrimary && pCurrentTrack->CheckDynamicObject( Owner ) );
|
||||
if (bCanSkip) // tylko główna oś przenosi pojazd do innego toru
|
||||
Owner->MyTrack->RemoveDynamicObject(Owner); // zdejmujemy pojazd z dotychczasowego toru
|
||||
fDistance = s - pCurrentSegment->GetLength();
|
||||
dir = fDirection;
|
||||
if (pCurrentTrack->eType == tt_Cross)
|
||||
{
|
||||
if (!SetCurrentTrack(pCurrentTrack->Connected(iSegment, fDirection), 1))
|
||||
return false; // wyjście z błędem
|
||||
}
|
||||
else if (!SetCurrentTrack(pCurrentTrack->Connected(1, fDirection), 1)) // ustawia fDirection
|
||||
return false; // wyjście z błędem
|
||||
if (dir != fDirection) //(pCurrentTrack->iNextDirection)
|
||||
{ // gdy zmiana kierunku toru (Point2->Point2)
|
||||
fDistance = -fDistance; //(s-pCurrentSegment->GetLength());
|
||||
fCurrentDistance = pCurrentSegment->GetLength();
|
||||
}
|
||||
else // gdy kierunek bez zmiany (Point2->Point1)
|
||||
fCurrentDistance = 0;
|
||||
if (bCanSkip)
|
||||
{ // jak główna oś, to dodanie pojazdu do nowego toru
|
||||
pCurrentTrack->AddDynamicObject(Owner);
|
||||
iEventFlag = 3; // McZapkie-020602: umozliwienie uruchamiania event1,2 po zmianie toru
|
||||
iEventallFlag = 3;
|
||||
if (!Owner->MyTrack)
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{ // gdy zostaje na tym samym torze (przesuwanie już nie zmienia toru)
|
||||
if (bPrimary)
|
||||
{ // tylko gdy początkowe ustawienie, dodajemy eventy stania do kolejki
|
||||
if (Owner->MoverParameters->CabOccupied != 0) {
|
||||
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events1, Owner, -1.0 );
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events2, Owner, -1.0 );
|
||||
}
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events1all, Owner, -1.0 );
|
||||
pCurrentTrack->QueueEvents( pCurrentTrack->m_events2all, Owner, -1.0 );
|
||||
}
|
||||
fCurrentDistance = s;
|
||||
return ComputatePosition(); // przeliczenie XYZ, true o ile nie wyjechał na NULL
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool TTrackFollower::ComputatePosition()
|
||||
{ // ustalenie współrzędnych XYZ
|
||||
if (pCurrentSegment) // o ile jest tor
|
||||
{
|
||||
pCurrentSegment->RaPositionGet(fCurrentDistance, pPosition, vAngles);
|
||||
if (fDirection < 0) // kąty zależą jeszcze od zwrotu na torze
|
||||
{ // kąty są w przedziale <-M_PI;M_PI>
|
||||
vAngles.x = -vAngles.x; // przechyłka jest w przecinwą stronę
|
||||
vAngles.y = -vAngles.y; // pochylenie jest w przecinwą stronę
|
||||
vAngles.z +=
|
||||
(vAngles.z >= M_PI) ? -M_PI : M_PI; // ale kierunek w planie jest obrócony o 180°
|
||||
}
|
||||
if (fOffsetH != 0.0)
|
||||
{ // jeśli przesunięcie względem osi toru, to je doliczyć
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
63
world/TrkFoll.h
Normal file
63
world/TrkFoll.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#ifndef TrkFollH
|
||||
#define TrkFollH
|
||||
|
||||
#include "Classes.h"
|
||||
#include "Segment.h"
|
||||
|
||||
// oś poruszająca się po torze
|
||||
class TTrackFollower {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
TTrackFollower() = default;
|
||||
// destructor
|
||||
~TTrackFollower();
|
||||
// methods
|
||||
TTrack * SetCurrentTrack(TTrack *pTrack, int end);
|
||||
bool Move(double fDistance, bool bPrimary);
|
||||
inline TTrack * GetTrack() const {
|
||||
return pCurrentTrack; };
|
||||
// przechyłka policzona przy ustalaniu pozycji
|
||||
inline double GetRoll() {
|
||||
return vAngles.x; };
|
||||
//{return pCurrentSegment->GetRoll(fCurrentDistance)*fDirection;}; //zamiast liczyć można pobrać
|
||||
// zwrot na torze
|
||||
inline double GetDirection() const {
|
||||
return fDirection; };
|
||||
// ABu-030403
|
||||
inline double GetTranslation() const {
|
||||
return fCurrentDistance; };
|
||||
// inline double GetLength(vector3 p1, vector3 cp1, vector3 cp2, vector3 p2)
|
||||
//{ return pCurrentSegment->ComputeLength(p1,cp1,cp2,p2); };
|
||||
// inline double GetRadius(double L, double d); //McZapkie-150503
|
||||
bool Init(TTrack *pTrack, TDynamicObject *NewOwner, double fDir);
|
||||
void Reset();
|
||||
void Render(float fNr);
|
||||
// members
|
||||
double fOffsetH = 0.0; // Ra: odległość środka osi od osi toru (dla samochodów) - użyć do wężykowania
|
||||
Math3D::vector3 pPosition; // współrzędne XYZ w układzie scenerii
|
||||
Math3D::vector3 vAngles; // x:przechyłka, y:pochylenie, z:kierunek w planie (w radianach)
|
||||
private:
|
||||
// methods
|
||||
bool ComputatePosition(); // przeliczenie pozycji na torze
|
||||
// members
|
||||
TTrack *pCurrentTrack = nullptr; // na którym torze siê znajduje
|
||||
std::shared_ptr<TSegment> pCurrentSegment; // zwrotnice mog¹ mieæ dwa segmenty
|
||||
double fCurrentDistance = 0.0; // przesuniêcie wzglêdem Point1 w stronê Point2
|
||||
double fDirection = 1.0; // ustawienie wzglêdem toru: -1.0 albo 1.0, mno¿one przez dystans // jest przodem do Point2
|
||||
TDynamicObject *Owner = nullptr; // pojazd posiadający
|
||||
int iEventFlag = 0; // McZapkie-020602: informacja o tym czy wyzwalac zdarzenie: 0,1,2,3
|
||||
int iEventallFlag = 0;
|
||||
int iSegment = 0; // który segment toru jest używany (żeby nie przeskakiwało po przestawieniu zwrotnicy pod taborem)
|
||||
};
|
||||
//---------------------------------------------------------------------------
|
||||
#endif
|
||||
692
world/mtable.cpp
Normal file
692
world/mtable.cpp
Normal file
@@ -0,0 +1,692 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "mtable.h"
|
||||
#include "Globals.h"
|
||||
#include "simulationtime.h"
|
||||
#include "dictionary.h"
|
||||
#include "utilities.h"
|
||||
|
||||
double TTrainParameters::CheckTrainLatency()
|
||||
{
|
||||
if ((LastStationLatency > 1.0) || (LastStationLatency < 0))
|
||||
return LastStationLatency; /*spoznienie + lub do przodu - z tolerancja 1 min*/
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
double TTrainParameters::WatchMTable(double DistCounter)
|
||||
{ // zwraca odleglość do najblizszej stacji z zatrzymaniem
|
||||
double dist;
|
||||
|
||||
if (Direction == 1)
|
||||
dist = TimeTable[StationIndex].km - TimeTable[0].km - DistCounter;
|
||||
else
|
||||
dist = TimeTable[0].km - TimeTable[StationIndex].km - DistCounter;
|
||||
return dist;
|
||||
}
|
||||
|
||||
std::string TTrainParameters::NextStop() const
|
||||
{ // pobranie nazwy następnego miejsca zatrzymania
|
||||
if (StationIndex <= StationCount)
|
||||
return NextStationName; // nazwa następnego przystanku;
|
||||
else
|
||||
return "[End of route]"; //że niby koniec
|
||||
}
|
||||
|
||||
sound_source
|
||||
TTrainParameters::next_stop_sound() const {
|
||||
if( StationIndex > StationCount ) {
|
||||
return { sound_placement::engine };
|
||||
}
|
||||
for( auto stationidx { StationIndex }; stationidx < StationCount + 1; ++stationidx ) {
|
||||
auto &station{ TimeTable[ stationidx ] };
|
||||
if( station.Ah == -1 ) {
|
||||
continue;
|
||||
}
|
||||
// specified arrival time means it's a scheduled stop
|
||||
return station.name_sound;
|
||||
}
|
||||
// shouldn't normally get here, unless the timetable is malformed
|
||||
return { sound_placement::engine };
|
||||
}
|
||||
|
||||
sound_source
|
||||
TTrainParameters::last_stop_sound() const {
|
||||
|
||||
return TimeTable[ StationCount ].name_sound;
|
||||
}
|
||||
|
||||
bool TTrainParameters::IsStop() const
|
||||
{ // zapytanie, czy zatrzymywać na następnym punkcie rozkładu
|
||||
if ((StationIndex <= StationCount))
|
||||
return TimeTable[StationIndex].Ah >= 0; //-1 to brak postoju
|
||||
else
|
||||
return true; // na ostatnim się zatrzymać zawsze
|
||||
}
|
||||
|
||||
bool TTrainParameters::IsLastStop() const {
|
||||
|
||||
return ( StationIndex >= StationCount );
|
||||
}
|
||||
|
||||
|
||||
bool TTrainParameters::IsMaintenance() const {
|
||||
if( ( StationIndex <= StationCount ) )
|
||||
return TimeTable[ StationIndex ].is_maintenance;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
int TTrainParameters::radio_channel() const {
|
||||
if( ( StationIndex <= StationCount ) )
|
||||
return TimeTable[ StationIndex ].radio_channel;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
// returns: sound file associated with current station, or -1
|
||||
sound_source TTrainParameters::current_stop_sound() const {
|
||||
if( ( StationIndex <= StationCount ) )
|
||||
return TimeTable[ StationIndex ].name_sound;
|
||||
else
|
||||
return { sound_placement::engine };
|
||||
}
|
||||
|
||||
bool TTrainParameters::UpdateMTable( scenario_time const &Time, std::string const &NewName ) {
|
||||
|
||||
return UpdateMTable( Time.data().wHour, Time.data().wMinute + Time.data().wSecond * 0.0167, NewName );
|
||||
}
|
||||
|
||||
bool TTrainParameters::UpdateMTable(double hh, double mm, std::string const &NewName)
|
||||
/*odfajkowanie dojechania do stacji (NewName) i przeliczenie opóźnienia*/
|
||||
{
|
||||
bool OK;
|
||||
OK = false;
|
||||
if (StationIndex <= StationCount) // Ra: "<=", bo ostatni przystanek jest traktowany wyjątkowo
|
||||
{
|
||||
if (NewName == NextStationName) // jeśli dojechane do następnego
|
||||
{ // Ra: wywołanie może być powtarzane, jak stoi na W4
|
||||
if (TimeTable[StationIndex + 1].km - TimeTable[StationIndex].km < 0) // to jest bez sensu
|
||||
Direction = -1;
|
||||
else
|
||||
Direction = 1; // prowizorka bo moze byc zmiana kilometrazu
|
||||
// ustalenie, czy opóźniony (porównanie z czasem odjazdu)
|
||||
LastStationLatency =
|
||||
CompareTime(hh, mm, TimeTable[StationIndex].Dh, TimeTable[StationIndex].Dm);
|
||||
// inc(StationIndex); //przejście do następnej pozycji StationIndex<=StationCount
|
||||
// Ra: "<", bo dodaje 1 przy przejściu do następnej stacji
|
||||
if (StationIndex < StationCount) {
|
||||
// jeśli nie ostatnia stacja
|
||||
NextStationName = TimeTable[StationIndex + 1].StationName; // zapamiętanie nazwy
|
||||
// Ra: nowa prędkość rozkładowa na kolejnym odcinku
|
||||
TTVmax = TimeTable[StationIndex + 1].vmax;
|
||||
}
|
||||
else {
|
||||
// gdy ostatnia stacja, nie ma następnej stacji
|
||||
NextStationName = "";
|
||||
}
|
||||
OK = true;
|
||||
}
|
||||
}
|
||||
return OK; /*czy jest nastepna stacja*/
|
||||
}
|
||||
|
||||
bool Mtable::TTrainParameters::RewindTimeTable(std::string actualStationName) {
|
||||
|
||||
if( actualStationName.compare( 0, 19, "PassengerStopPoint:" ) == 0 ) {
|
||||
actualStationName = ToLower( actualStationName.substr( 19 ) );
|
||||
}
|
||||
for( auto i = 1; i <= StationCount; ++i ) {
|
||||
// przechodzimy po całej tabelce i sprawdzamy nazwy stacji (bez pierwszej)
|
||||
if (ToLower(TimeTable[i].StationName) == actualStationName) {
|
||||
// nazwa stacji zgodna więc ustawiamy na poprzednią, żeby w następnym kroku poprawnie obsłużyć
|
||||
StationIndex = i;
|
||||
NextStationName = TimeTable[ i ].StationName;
|
||||
TTVmax = TimeTable[ i ].vmax;
|
||||
return true; // znaleźliśmy więc kończymy
|
||||
}
|
||||
}
|
||||
// failed to find a match
|
||||
return false;
|
||||
}
|
||||
|
||||
void TTrainParameters::StationIndexInc()
|
||||
{ // przejście do następnej pozycji StationIndex<=StationCount
|
||||
++StationIndex;
|
||||
}
|
||||
|
||||
bool TTrainParameters::IsTimeToGo(double hh, double mm)
|
||||
// sprawdzenie, czy można już odjechać z aktualnego zatrzymania
|
||||
// StationIndex to numer następnego po dodarciu do aktualnego
|
||||
{
|
||||
if ((StationIndex < 1))
|
||||
return true; // przed pierwszą jechać
|
||||
else if ((StationIndex < StationCount))
|
||||
{ // oprócz ostatniego przystanku
|
||||
if ((TimeTable[StationIndex].Ah < 0)) // odjazd z poprzedniego
|
||||
return true; // czas przyjazdu nie był podany - przelot
|
||||
else
|
||||
return CompareTime(hh, mm, TimeTable[StationIndex].Dh, TimeTable[StationIndex].Dm) <= 0;
|
||||
}
|
||||
else // gdy rozkład się skończył
|
||||
return false; // dalej nie jechać
|
||||
}
|
||||
|
||||
// returns: difference between specified time and scheduled departure from current stop, in seconds
|
||||
double TTrainParameters::seconds_until_departure( double const Hour, double const Minute ) const {
|
||||
|
||||
if( ( TimeTable[ StationStart ].Ah < 0 ) ) { // passthrough
|
||||
return 0;
|
||||
}
|
||||
return ( 60.0 * CompareTime( Hour, Minute, TimeTable[ StationStart ].Dh, TimeTable[ StationStart ].Dm ) );
|
||||
}
|
||||
|
||||
std::string TTrainParameters::ShowRelation() const
|
||||
/*zwraca informację o relacji*/
|
||||
{
|
||||
// if (Relation1=TimeTable[1].StationName) and (Relation2=TimeTable[StationCount].StationName)
|
||||
if ((Relation1 != "") && (Relation2 != ""))
|
||||
return Relation1 + " - " + Relation2;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
TTrainParameters::TTrainParameters(std::string const &NewTrainName)
|
||||
/*wstępne ustawienie parametrów rozkładu jazdy*/
|
||||
{
|
||||
NewName(NewTrainName);
|
||||
}
|
||||
|
||||
void TTrainParameters::NewName(std::string const &NewTrainName)
|
||||
/*wstępne ustawienie parametrów rozkładu jazdy*/
|
||||
{
|
||||
TrainName = NewTrainName;
|
||||
StationCount = 0;
|
||||
StationIndex = 0;
|
||||
StationStart = 0;
|
||||
NextStationName = "nowhere";
|
||||
LastStationLatency = 0;
|
||||
Direction = 1;
|
||||
Relation1 = "";
|
||||
Relation2 = "";
|
||||
for (int i = 0; i < MaxTTableSize + 1; ++i)
|
||||
{
|
||||
TimeTable[ i ] = TMTableLine();
|
||||
}
|
||||
TTVmax = 100; /*wykasowac*/
|
||||
BrakeRatio = 0;
|
||||
LocSeries = "";
|
||||
LocLoad = 0;
|
||||
}
|
||||
|
||||
void TTrainParameters::UpdateVelocity(int StationCount, double vActual)
|
||||
// zapisywanie prędkości maksymalnej do wcześniejszych odcinków
|
||||
// wywoływane z numerem ostatniego przetworzonego przystanku
|
||||
{
|
||||
int i = StationCount;
|
||||
// TTVmax:=vActual; {PROWIZORKA!!!}
|
||||
while ((i >= 0) && (TimeTable[i].vmax == -1))
|
||||
{
|
||||
TimeTable[i].vmax = vActual; // prędkość dojazdu do przystanku i
|
||||
--i; // ewentualnie do poprzedniego też
|
||||
}
|
||||
}
|
||||
|
||||
// bool TTrainParameters::LoadTTfile(std::string scnpath, int iPlus, double vmax)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
|
||||
bool TTrainParameters::LoadTTfile(std::string scnpath, int iPlus, double vmax)
|
||||
// wczytanie pliku-tabeli z rozkładem przesuniętym o (fPlus); (vMax) nie ma znaczenia
|
||||
{
|
||||
std::string lines;
|
||||
std::string s;
|
||||
std::ifstream fin;
|
||||
bool EndTable;
|
||||
double vActual;
|
||||
|
||||
int ConversionError = 0;
|
||||
EndTable = false;
|
||||
if ((TrainName == ""))
|
||||
{ // jeśli pusty rozkład
|
||||
// UpdateVelocity(StationCount,vMax); //ograniczenie do prędkości startowej
|
||||
}
|
||||
else
|
||||
{
|
||||
ConversionError = 666;
|
||||
vActual = -1;
|
||||
s = scnpath + TrainName + ".txt";
|
||||
replace_slashes(s);
|
||||
// Ra 2014-09: ustalić zasady wyznaczenia pierwotnego pliku przy przesuniętych rozkładach
|
||||
// (kolejny pociąg dostaje numer +2)
|
||||
fin.open(s.c_str()); // otwieranie pliku
|
||||
replace_slashes(s);
|
||||
|
||||
if (!fin.is_open())
|
||||
{ // jeśli nie ma pliku
|
||||
vmax = atoi(TrainName.c_str()); // nie ma pliku ale jest liczba
|
||||
if ((vmax > 10) && (vmax < 200))
|
||||
{
|
||||
TTVmax = vmax; // Ra 2014-07: zamiast rozkładu można podać Vmax
|
||||
UpdateVelocity(StationCount, vmax); // ograniczenie do prędkości startowej
|
||||
ConversionError = 0;
|
||||
}
|
||||
else
|
||||
ConversionError = -8; /*Ra: ten błąd jest niepotrzebny*/
|
||||
}
|
||||
else
|
||||
{ /*analiza rozkładu jazdy*/
|
||||
ConversionError = 0;
|
||||
while (fin.good() && !((ConversionError != 0) || EndTable))
|
||||
{
|
||||
std::getline(fin, lines); /*wczytanie linii*/
|
||||
if (contains( lines, "___________________") ) /*linia pozioma górna*/
|
||||
{
|
||||
fin >> s;
|
||||
if (s == "[") /*lewy pion*/
|
||||
{
|
||||
fin >> s;
|
||||
if (s == "Rodzaj") /*"Rodzaj i numer pociagu"*/
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!(s == "|") || (fin.eof())); /*środkowy pion*/
|
||||
}
|
||||
}
|
||||
else if (lines == "")
|
||||
{
|
||||
fin.close();
|
||||
break;
|
||||
}
|
||||
fin >> s; /*nazwa pociągu*/
|
||||
// if LowerCase(s)<>ExtractFileName(TrainName) then {musi być taka sama, jak nazwa
|
||||
// pliku}
|
||||
// ConversionError:=-7 {błąd niezgodności}
|
||||
TrainName = s; // nadanie nazwy z pliku TXT (bez ścieżki do pliku)
|
||||
while (fin >> s || fin.bad())
|
||||
{
|
||||
if (contains( s,"_______|") )
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (s == "Kategoria")
|
||||
{
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!((s == "|") || (fin.bad())));
|
||||
fin >> TrainCategory;
|
||||
continue;
|
||||
}
|
||||
if (s == "Nazwa")
|
||||
{
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!((s == "|") || (fin.bad())));
|
||||
fin >> TrainLabel;
|
||||
continue;
|
||||
}
|
||||
} // while (!(s == "Seria"));
|
||||
// else
|
||||
{ /*czytaj naglowek*/
|
||||
while (fin >> s || !fin.bad())
|
||||
{
|
||||
if (s == "[")
|
||||
break;
|
||||
} // while (!() || (fin.eof())); /*pierwsza linia z relacją*/
|
||||
while (fin >> s || !fin.bad())
|
||||
{
|
||||
if (s != "|")
|
||||
break;
|
||||
} // while (!(() || fin.eof()));
|
||||
if( s != "|" ) {
|
||||
Relation1 = s;
|
||||
win1250_to_ascii( Relation1 );
|
||||
}
|
||||
else
|
||||
ConversionError = -5;
|
||||
while (fin >> s || !fin.bad())
|
||||
{
|
||||
if (s == "Relacja")
|
||||
break;
|
||||
} // while (
|
||||
// !( || (fin.eof()))); /*druga linia z relacją*/
|
||||
while (fin >> s || !fin.bad())
|
||||
{
|
||||
if (s == "|")
|
||||
break;
|
||||
} // while (!( || (fin.eof())));
|
||||
fin >> Relation2;
|
||||
win1250_to_ascii( Relation2 );
|
||||
while (fin >> s || !fin.bad())
|
||||
{
|
||||
if (s == "Wymagany")
|
||||
break;
|
||||
} // while (!();
|
||||
while (fin >> s || !fin.bad())
|
||||
{
|
||||
if ((s == "|") || (s == "\n"))
|
||||
break;
|
||||
} // while (!());
|
||||
fin >> s;
|
||||
s = s.substr(0, s.find("%"));
|
||||
BrakeRatio = atof(s.c_str());
|
||||
while (fin >> s || fin.bad())
|
||||
{
|
||||
if (s == "Seria")
|
||||
break;
|
||||
} // while (!(s == "Seria"));
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!((s == "|") || (fin.bad())));
|
||||
fin >> LocSeries;
|
||||
fin >> LocLoad; // = s2rE(ReadWord(fin));
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!(contains( s,"[______________" ) || fin.bad()));
|
||||
auto activeradiochannel{ -1 };
|
||||
while (!fin.bad() && !EndTable)
|
||||
{
|
||||
++StationCount;
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!((s == "[") || (fin.bad())));
|
||||
TMTableLine *record = &TimeTable[StationCount];
|
||||
{
|
||||
if (s == "[")
|
||||
fin >> s;
|
||||
else
|
||||
ConversionError = -4;
|
||||
if (false == contains( s,"|") )
|
||||
{
|
||||
record->km = atof(s.c_str());
|
||||
fin >> s;
|
||||
}
|
||||
if (contains( s,"|_____|")) /*zmiana predkosci szlakowej*/
|
||||
UpdateVelocity(StationCount, vActual);
|
||||
else
|
||||
{
|
||||
fin >> s;
|
||||
if (false == contains(s,"|"))
|
||||
vActual = atof(s.c_str());
|
||||
}
|
||||
while (false == contains( s,"|"))
|
||||
fin >> s;
|
||||
fin >> record->StationName;
|
||||
// get rid of non-ascii chars. TODO: run correct version based on locale
|
||||
win1250_to_ascii( record->StationName );
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!((s == "1") || (s == "2") || fin.bad()));
|
||||
record->TrackNo = atoi(s.c_str());
|
||||
fin >> s;
|
||||
if (s != "|")
|
||||
{
|
||||
if (contains( s, hrsd) )
|
||||
{
|
||||
record->Ah = atoi( s.substr(0, s.find(hrsd)).c_str()); // godzina przyjazdu
|
||||
record->Am = atof(s.substr(s.find(hrsd) + 1, s.length()).c_str()); // minuta przyjazdu
|
||||
}
|
||||
else
|
||||
{
|
||||
record->Ah = TimeTable[StationCount - 1].Ah; // godzina z poprzedniej pozycji
|
||||
record->Am = atof(s.c_str()); // bo tylko minuty podane
|
||||
}
|
||||
}
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!((s != "|") || (fin.bad())));
|
||||
if (s != "]")
|
||||
record->tm = atof(s.c_str());
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!((s == "[") || fin.bad()));
|
||||
fin >> s;
|
||||
if (false == contains(s,"|"))
|
||||
{
|
||||
/*tu s moze byc miejscem zmiany predkosci szlakowej*/
|
||||
fin >> s;
|
||||
}
|
||||
if (s.find("|_____|") !=
|
||||
std::string::npos) /*zmiana predkosci szlakowej*/
|
||||
UpdateVelocity(StationCount, vActual);
|
||||
else
|
||||
{
|
||||
fin >> s;
|
||||
if (false == contains(s,"|"))
|
||||
vActual = atof(s.c_str());
|
||||
}
|
||||
while (false == contains(s,"|"))
|
||||
fin >> s;
|
||||
// stationware. added fix for empty entry
|
||||
fin >> s;
|
||||
while( false == ( ( s == "1" )
|
||||
|| ( s == "2" )
|
||||
|| fin.bad() ) ) {
|
||||
record->StationWare += s;
|
||||
fin >> s;
|
||||
}
|
||||
// cache relevant station data
|
||||
record->is_maintenance = ( contains( s, "pt" ) );
|
||||
{
|
||||
auto const stationware { Split( record->StationWare, ',' ) };
|
||||
for( auto const &entry : stationware ) {
|
||||
if( entry.front() != 'R' ) {
|
||||
continue;
|
||||
}
|
||||
auto const entrysplit { split_string_and_number( entry ) };
|
||||
if( ( entrysplit.first == "R" )
|
||||
&& ( entrysplit.second <= 10 ) ) {
|
||||
auto const radiochannel { entrysplit.second };
|
||||
if( ( record->radio_channel == -1 )
|
||||
|| ( radiochannel != activeradiochannel ) ) {
|
||||
// if the station has more than one radiochannel listed,
|
||||
// it generally means we should switch to the one we weren't using so far
|
||||
// TODO: reverse this behaviour (keep the channel used so far) once W28 signs are included in the system
|
||||
record->radio_channel = radiochannel;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( record->radio_channel != -1 ) {
|
||||
activeradiochannel = record->radio_channel;
|
||||
}
|
||||
}
|
||||
record->TrackNo = atoi(s.c_str());
|
||||
fin >> s;
|
||||
if (s != "|")
|
||||
{
|
||||
if (contains( s, hrsd) )
|
||||
{
|
||||
record->Dh = atoi(s.substr(0, s.find(hrsd)).c_str()); // godzina odjazdu
|
||||
record->Dm = atof(s.substr(s.find(hrsd) + 1, s.length()).c_str()); // minuta odjazdu
|
||||
}
|
||||
else
|
||||
{
|
||||
record->Dh = TimeTable[StationCount - 1].Dh; // godzina z poprzedniej pozycji
|
||||
record->Dm = atof(s.c_str()); // bo tylko minuty podane
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
record->Dh = record->Ah; // odjazd o tej samej, co przyjazd (dla ostatniego też)
|
||||
record->Dm = record->Am; // bo są używane do wyliczenia opóźnienia po dojechaniu
|
||||
}
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!((s != "|") || (fin.bad())));
|
||||
if (s != "]")
|
||||
record->tm = atof(s.c_str());
|
||||
do
|
||||
{
|
||||
fin >> s;
|
||||
} while (!(contains( s, "[" ) || fin.bad()));
|
||||
if (false == contains( s, "_|_"))
|
||||
fin >> s;
|
||||
if (false == contains( s, "|"))
|
||||
{
|
||||
/*tu s moze byc miejscem zmiany predkosci szlakowej*/
|
||||
fin >> s;
|
||||
}
|
||||
if (contains( s, "|_____|") ) /*zmiana predkosci szlakowej*/
|
||||
UpdateVelocity(StationCount, vActual);
|
||||
else
|
||||
{
|
||||
fin >> s;
|
||||
if (false == contains( s, "|"))
|
||||
vActual = atof(s.c_str());
|
||||
}
|
||||
while (false == contains( s, "|" ) )
|
||||
fin >> s;
|
||||
while ((false == contains( s,"]") ))
|
||||
fin >> s;
|
||||
if (contains( s,"_|_") )
|
||||
EndTable = true;
|
||||
} /*timetableline*/
|
||||
}
|
||||
}
|
||||
} /*while eof*/
|
||||
fin.close();
|
||||
}
|
||||
}
|
||||
if (ConversionError == 0)
|
||||
{
|
||||
if ((TimeTable[1].StationName == Relation1)) // jeśli nazwa pierwszego zgodna z relacją
|
||||
if ((TimeTable[1].Ah < 0)) // a nie podany czas przyjazdu
|
||||
{ // to mamy zatrzymanie na pierwszym, a nie przelot
|
||||
TimeTable[1].Ah = TimeTable[1].Dh;
|
||||
TimeTable[1].Am = TimeTable[1].Dm;
|
||||
}
|
||||
}
|
||||
//
|
||||
load_sounds();
|
||||
// potentially offset table times
|
||||
auto const timeoffset { static_cast<int>( Global.ScenarioTimeOffset * 60 ) + iPlus };
|
||||
if( timeoffset != 0 ) // jeżeli jest przesunięcie rozkładu
|
||||
{
|
||||
long i_end = StationCount + 1;
|
||||
float adjustedtime; // do zwiększania czasu
|
||||
for (auto i = 1; i < i_end; ++i) // bez with, bo ciężko się przenosi na C++
|
||||
{
|
||||
if ((TimeTable[i].Ah >= 0))
|
||||
{
|
||||
adjustedtime = clamp_circular<float>( TimeTable[i].Ah * 60 + TimeTable[i].Am + timeoffset, 24 * 60 ); // nowe minuty
|
||||
TimeTable[i].Am = (int(60 * adjustedtime) % 3600) / 60.f;
|
||||
TimeTable[i].Ah = int((adjustedtime) / 60) % 24;
|
||||
}
|
||||
if ((TimeTable[i].Dh >= 0))
|
||||
{
|
||||
adjustedtime = clamp_circular<float>( TimeTable[i].Dh * 60 + TimeTable[i].Dm + timeoffset, 24 * 60 ); // nowe minuty
|
||||
TimeTable[i].Dm = (int(60 * adjustedtime) % 3600) / 60.f;
|
||||
TimeTable[i].Dh = int((adjustedtime) / 60) % 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ConversionError == 0;
|
||||
}
|
||||
|
||||
void
|
||||
TTrainParameters::load_sounds() {
|
||||
|
||||
for( auto stationidx = 1; stationidx < StationCount + 1; ++stationidx ) {
|
||||
auto &station { TimeTable[ stationidx ] };
|
||||
if( station.Ah == -1 ) {
|
||||
continue;
|
||||
}
|
||||
// specified arrival time means it's a scheduled stop
|
||||
auto const stationname { (
|
||||
ends_with( station.StationName, "_po" ) ?
|
||||
station.StationName.substr( 0, station.StationName.size() - 3 ) :
|
||||
station.StationName ) };
|
||||
|
||||
auto const lookup {
|
||||
FileExists(
|
||||
{ Global.asCurrentSceneryPath + stationname, std::string{ szSoundPath } + "sip/" + stationname },
|
||||
{ ".ogg", ".flac", ".wav" } ) };
|
||||
if( lookup.first.empty() ) {
|
||||
continue;
|
||||
}
|
||||
// wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie)
|
||||
station.name_sound =
|
||||
sound_source{ sound_placement::engine, EU07_SOUND_CABANNOUNCEMENTCUTOFFRANGE }
|
||||
.deserialize( lookup.first + lookup.second, sound_type::single );
|
||||
}
|
||||
}
|
||||
|
||||
bool TTrainParameters::DirectionChange()
|
||||
// sprawdzenie, czy po zatrzymaniu wykonać kolejne komendy
|
||||
{
|
||||
if ((StationIndex > 0) && (StationIndex < StationCount)) // dla ostatniej stacji nie
|
||||
if (contains( TimeTable[StationIndex].StationWare, '@') )
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void TTrainParameters::serialize( dictionary_source *Output ) const {
|
||||
|
||||
Output->insert( "trainnumber", TrainName );
|
||||
Output->insert( "traincategory", TrainCategory );
|
||||
Output->insert( "trainname", TrainLabel );
|
||||
Output->insert( "train_brakingmassratio", BrakeRatio );
|
||||
Output->insert( "train_enginetype", LocSeries );
|
||||
Output->insert( "train_engineload", LocLoad );
|
||||
|
||||
Output->insert( "train_stationfrom", Relation1 );
|
||||
Output->insert( "train_stationto", Relation2 );
|
||||
Output->insert( "train_stationindex", StationIndex );
|
||||
Output->insert( "train_stationcount", StationCount );
|
||||
Output->insert( "train_stationstart", StationStart );
|
||||
if( StationCount > 0 ) {
|
||||
// timetable stations data, if there's any
|
||||
for( auto stationidx = 1; stationidx <= StationCount; ++stationidx ) {
|
||||
auto const stationlabel { "train_station" + std::to_string( stationidx ) + "_" };
|
||||
auto const &timetableline { TimeTable[ stationidx ] };
|
||||
Output->insert( ( stationlabel + "name" ), Bezogonkow( timetableline.StationName ) );
|
||||
Output->insert( ( stationlabel + "fclt" ), Bezogonkow( timetableline.StationWare ) );
|
||||
Output->insert( ( stationlabel + "lctn" ), timetableline.km );
|
||||
Output->insert( ( stationlabel + "vmax" ), timetableline.vmax );
|
||||
Output->insert( ( stationlabel + "ah" ), timetableline.Ah );
|
||||
Output->insert( ( stationlabel + "am" ), timetableline.Am );
|
||||
Output->insert( ( stationlabel + "dh" ), timetableline.Dh );
|
||||
Output->insert( ( stationlabel + "dm" ), timetableline.Dm );
|
||||
Output->insert( ( stationlabel + "tracks" ), timetableline.TrackNo );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TMTableTime::UpdateMTableTime(double deltaT)
|
||||
// dodanie czasu (deltaT) w sekundach, z przeliczeniem godziny
|
||||
{
|
||||
mr += deltaT; // dodawanie sekund
|
||||
while (mr >= 60.0) // przeliczenie sekund do właściwego przedziału
|
||||
{
|
||||
mr -= 60.0;
|
||||
++mm;
|
||||
}
|
||||
while (mm > 59) // przeliczenie minut do właściwego przedziału
|
||||
{
|
||||
mm -= 60;
|
||||
++hh;
|
||||
}
|
||||
while (hh > 23) // przeliczenie godzin do właściwego przedziału
|
||||
{
|
||||
hh -= 24;
|
||||
++dd; // zwiększenie numeru dnia
|
||||
}
|
||||
}
|
||||
120
world/mtable.h
Normal file
120
world/mtable.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Classes.h"
|
||||
#include "sound.h"
|
||||
|
||||
namespace Mtable
|
||||
{
|
||||
|
||||
static int const MaxTTableSize = 100; // można by to robić dynamicznie
|
||||
static char const *hrsd = ".";
|
||||
|
||||
// Ra: pozycja zerowa rozkładu chyba nie ma sensu
|
||||
// Ra: numeracja przystanków jest 1..StationCount
|
||||
|
||||
struct TMTableLine
|
||||
{
|
||||
float km{ 0.f }; // kilometraz linii
|
||||
float vmax{ -1.f }; // predkosc rozkladowa przed przystankiem
|
||||
// StationName:string[32]; //nazwa stacji ('_' zamiast spacji)
|
||||
// StationWare:string[32]; //typ i wyposazenie stacji, oddz. przecinkami}
|
||||
std::string StationName{ "nowhere" }; // nazwa stacji ('_' zamiast spacji)
|
||||
std::string StationWare; // typ i wyposazenie stacji, oddz. przecinkami}
|
||||
int TrackNo{ 1 }; // ilosc torow szlakowych
|
||||
int Ah{ -1 };
|
||||
float Am{ -1.f }; // godz. i min. przyjazdu, -1 gdy bez postoju
|
||||
int Dh{ -1 };
|
||||
float Dm{ -1.f }; // godz. i min. odjazdu
|
||||
float tm{ 0.f }; // czas jazdy do tej stacji w min. (z kolumny)
|
||||
bool is_maintenance{ false };
|
||||
int radio_channel{ -1 };
|
||||
sound_source name_sound{ sound_placement::engine };
|
||||
};
|
||||
|
||||
typedef TMTableLine TMTable[MaxTTableSize + 1];
|
||||
|
||||
// typedef TTrainParameters *PTrainParameters;
|
||||
|
||||
class TTrainParameters
|
||||
{
|
||||
public:
|
||||
std::string TrainName;
|
||||
std::string TrainCategory;
|
||||
std::string TrainLabel;
|
||||
double TTVmax;
|
||||
std::string Relation1;
|
||||
std::string Relation2; // nazwy stacji danego odcinka
|
||||
double BrakeRatio;
|
||||
std::string LocSeries; // seria (typ) pojazdu
|
||||
double LocLoad;
|
||||
TMTable TimeTable;
|
||||
int StationCount; // ilość przystanków (0-techniczny)
|
||||
int StationIndex; // numer najbliższego (aktualnego) przystanku
|
||||
int StationStart; // numer pierwszej stacji pokazywanej na podglądzie rozkładu
|
||||
std::string NextStationName;
|
||||
double LastStationLatency;
|
||||
int Direction; /*kierunek jazdy w/g kilometrazu*/
|
||||
double CheckTrainLatency();
|
||||
/*todo: str hh:mm to int i z powrotem*/
|
||||
std::string ShowRelation() const;
|
||||
double WatchMTable(double DistCounter);
|
||||
std::string NextStop() const;
|
||||
sound_source next_stop_sound() const;
|
||||
sound_source last_stop_sound() const;
|
||||
bool IsStop() const;
|
||||
bool IsLastStop() const;
|
||||
bool IsMaintenance() const;
|
||||
bool IsTimeToGo(double hh, double mm);
|
||||
// returns: difference between specified time and scheduled departure from current stop, in seconds
|
||||
double seconds_until_departure( double const Hour, double const Minute ) const;
|
||||
bool UpdateMTable(double hh, double mm, std::string const &NewName);
|
||||
bool UpdateMTable( scenario_time const &Time, std::string const &NewName );
|
||||
bool RewindTimeTable( std::string actualStationName );
|
||||
TTrainParameters( std::string const &NewTrainName = "none" );
|
||||
void NewName(std::string const &NewTrainName);
|
||||
void UpdateVelocity(int StationCount, double vActual);
|
||||
bool LoadTTfile(std::string scnpath, int iPlus, double vmax);
|
||||
bool DirectionChange();
|
||||
void StationIndexInc();
|
||||
void serialize( dictionary_source *Output ) const;
|
||||
// returns: radio channel associated with current station, or -1
|
||||
int radio_channel() const;
|
||||
// returns: sound file associated with current station, or -1
|
||||
sound_source current_stop_sound() const;
|
||||
private:
|
||||
void load_sounds();
|
||||
};
|
||||
|
||||
class TMTableTime
|
||||
|
||||
{
|
||||
public:
|
||||
int dd = 0;
|
||||
int hh = 0;
|
||||
int mm = 0;
|
||||
double mr = 0.0;
|
||||
void UpdateMTableTime(double deltaT);
|
||||
TMTableTime(int InitH, int InitM ) :
|
||||
hh( InitH ),
|
||||
mm( InitM )
|
||||
{}
|
||||
|
||||
TMTableTime() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#if !defined(NO_IMPLICIT_NAMESPACE_USE)
|
||||
using namespace Mtable;
|
||||
#endif
|
||||
89
world/station.cpp
Normal file
89
world/station.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "station.h"
|
||||
|
||||
#include "DynObj.h"
|
||||
#include "mtable.h"
|
||||
|
||||
namespace simulation {
|
||||
|
||||
basic_station Station;
|
||||
|
||||
}
|
||||
|
||||
// exchanges load with consist attached to specified vehicle, operating on specified schedule
|
||||
double
|
||||
basic_station::update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule, int const Platform ) {
|
||||
|
||||
// TODO: filter out maintenance stops when determining first and last stop
|
||||
auto const firststop { Schedule.StationIndex == 1 };
|
||||
auto const laststop { Schedule.StationIndex == Schedule.StationCount };
|
||||
// HACK: determine whether current station is a (small) stop from the presence of "po" at the name end
|
||||
auto const stationname { Schedule.TimeTable[ Schedule.StationIndex ].StationName };
|
||||
auto const stationequipment { Schedule.TimeTable[ Schedule.StationIndex ].StationWare };
|
||||
auto const trainstop { (
|
||||
( ends_with( stationname, "po" ) )
|
||||
|| ( contains( stationequipment, "po" ) ) ) };
|
||||
auto const maintenancestop { ( contains( stationequipment, "pt" ) ) };
|
||||
// train stops exchange smaller groups than regular stations
|
||||
// NOTE: this is crude and unaccurate, but for now will do
|
||||
auto const stationsizemodifier { ( trainstop ? 1.0 : 2.0 ) };
|
||||
// go through all vehicles and update their load
|
||||
// NOTE: for the time being we limit ourselves to passenger-carrying cars only
|
||||
auto exchangetime { 0.f };
|
||||
|
||||
auto *vehicle { First };
|
||||
while( vehicle != nullptr ) {
|
||||
|
||||
auto ¶meters { *vehicle->MoverParameters };
|
||||
|
||||
if( parameters.Doors.range == 0.f ) { goto next; }
|
||||
|
||||
if( parameters.LoadType.name.empty() ) {
|
||||
// (try to) set the cargo type for empty cars
|
||||
parameters.LoadAmount = 0.f; // safety measure against edge cases
|
||||
parameters.AssignLoad( "passengers" );
|
||||
parameters.ComputeMass();
|
||||
}
|
||||
|
||||
if( parameters.LoadType.name == "passengers" ) {
|
||||
// NOTE: for the time being we're doing simple, random load change calculation
|
||||
// TODO: exchange driven by station parameters and time of the day
|
||||
auto unloadcount = static_cast<int>(
|
||||
TestFlag( parameters.DamageFlag, dtrain_out ) ? parameters.LoadAmount :
|
||||
laststop ? parameters.LoadAmount :
|
||||
firststop ? 0 :
|
||||
maintenancestop ? 0 :
|
||||
std::min<float>(
|
||||
parameters.LoadAmount,
|
||||
Random( parameters.MaxLoad * 0.15f * stationsizemodifier ) ) );
|
||||
auto loadcount = static_cast<int>(
|
||||
TestFlag( parameters.DamageFlag, dtrain_out ) ? 0 :
|
||||
laststop ? 0 :
|
||||
maintenancestop ? 0 :
|
||||
Random( parameters.MaxLoad * 0.15f * stationsizemodifier ) );
|
||||
if( true == firststop ) {
|
||||
// larger group at the initial station
|
||||
loadcount *= 2;
|
||||
}
|
||||
|
||||
if( ( unloadcount > 0 ) || ( loadcount > 0 ) ) {
|
||||
|
||||
vehicle->LoadExchange( unloadcount, loadcount, Platform );
|
||||
exchangetime = std::max( exchangetime, vehicle->LoadExchangeTime() );
|
||||
}
|
||||
}
|
||||
next:
|
||||
vehicle = vehicle->Next();
|
||||
}
|
||||
|
||||
return exchangetime;
|
||||
}
|
||||
29
world/station.h
Normal file
29
world/station.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Classes.h"
|
||||
|
||||
// a simple station, performs freight and passenger exchanges with visiting consists
|
||||
class basic_station {
|
||||
|
||||
public:
|
||||
// methods
|
||||
// exchanges load with consist attached to specified vehicle, operating on specified schedule; returns: time needed for exchange, in seconds
|
||||
double
|
||||
update_load( TDynamicObject *First, Mtable::TTrainParameters &Schedule, int const Platform );
|
||||
};
|
||||
|
||||
namespace simulation {
|
||||
|
||||
extern basic_station Station; // temporary object, for station functionality tests
|
||||
|
||||
} // simulation
|
||||
|
||||
Reference in New Issue
Block a user