Merge branch 'next' of D:\Git_New\flightgear into next
Conflicts: src/AIModel/AIBallistic.cxx
This commit is contained in:
commit
9e44be1df7
45 changed files with 797 additions and 556 deletions
|
@ -174,7 +174,7 @@
|
|||
<TypeLibraryName>.\Release/FlightGear.tlb</TypeLibraryName>
|
||||
</Midl>
|
||||
<ClCompile>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
<AdditionalIncludeDirectories>..\..\..\src;..\..\..\src\include;..\..\..\src\FDM\JSBSim;..\..\..\..\SimGear;..\..\..\..\install\msvc100\OpenSceneGraph\include;..\..\..\..\3rdParty\include;..\..\..\..\boost_1_43_0;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>NDEBUG;WIN32;_CONSOLE;HAVE_CONFIG_H;FGFS;ENABLE_AUDIO_SUPPORT;_FG_NDEBUG;ENABLE_THREADS=1;FG_ENABLE_MULTIPASS_CLOUDS;ENABLE_SP_FMDS;_USE_MATH_DEFINES;FG_JPEG_SERVER;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
|
|
|
@ -149,6 +149,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>sg.lib;net.lib;ul.lib;ws2_32.lib;winmm.lib;zlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>sg.lib;net.lib;ul.lib;ws2_32.lib;winmm.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>sg.lib;net.lib;ul.lib;ws2_32.lib;winmm.lib;zlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -142,7 +142,7 @@
|
|||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<AdditionalIncludeDirectories>..\..\..\..\SimGear;..\..\..\..\install\msvc100\OpenSceneGraph\include;..\..\..\..\3rdParty\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;HAVE_ZLIB;NOMINMAX;_USE_MATH_DEFINES;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>sg.lib;js.lib;ul.lib;zlib.lib;winmm.lib;ws2_32.lib;OpenThreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>opengl32.lib;osg.lib;osgDB.lib;osgViewer.lib;osgGA.lib;openthreads.lib;osgParticle.lib;osgUtil.lib;osgText.lib;ul.lib;sg.lib;zlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -148,6 +148,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>sg.lib;ul.lib;net.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>ul.lib;sg.lib;net.lib;ws2_32.lib;libsvn_client-1.lib;libsvn_diff-1.lib;libsvn_delta-1.lib;libsvn_ra-1.lib;libsvn_subr-1.lib;libsvn_wc-1.lib;libapr-1.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -142,6 +142,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>sg.lib;ul.lib;OpenThreads.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
|
|
@ -940,7 +940,7 @@ void FGAIBallistic::handle_impact() {
|
|||
invisible = true;
|
||||
} else if (_subID == 0) // kill the AIObject if there is no subsubmodel
|
||||
setDie(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGAIBallistic::handle_expiry() {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
|
|
@ -236,7 +236,7 @@ void FGAIBase::initModel(osg::Node *node)
|
|||
SG_LOG(SG_INPUT, SG_WARN, "AIBase: Could not load model " << model_path);
|
||||
}
|
||||
|
||||
//props->setStringValue("submodels/path", _path.c_str());
|
||||
//props->setStringValue("submodels/path", _path.c_str());
|
||||
setDie(false);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// FGAIBase.hxx - abstract base class for AI objects
|
||||
// Written by David Culp, started Nov 2003, based on
|
||||
// David Luff's FGAIEntity class.
|
||||
// - davidculp2@comcast.net
|
||||
// - davidculp2@comcast.net
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// - a global management type for AI objects
|
||||
//
|
||||
// Written by David Culp, started October 2003.
|
||||
// - davidculp2@comcast.net
|
||||
// - davidculp2@comcast.net
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// FGAIWingman - FGAIBllistic-derived class creates an AI Wingman
|
||||
//
|
||||
// Written by Vivian Meazza, started February 2008.
|
||||
// - vivian.meazza at lineone.net
|
||||
// - vivian.meazza at lineone.net
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
|
|
|
@ -896,7 +896,7 @@ void FGSubmodelMgr::setParentNode(int id) {
|
|||
if (!model)
|
||||
continue;
|
||||
|
||||
}// end for loop
|
||||
}// end for loop
|
||||
|
||||
if (_selected_ac != 0){
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include <Main/fg_props.hxx>
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::string;
|
||||
using std::list;
|
||||
|
||||
class FGAIBase;
|
||||
|
|
|
@ -25,8 +25,9 @@
|
|||
#include "pavement.hxx"
|
||||
|
||||
FGPavement::FGPavement(const std::string& aIdent, const SGGeod& aPos) :
|
||||
FGPositioned(PAVEMENT, aIdent, aPos, false)
|
||||
FGPositioned(PAVEMENT, aIdent, aPos)
|
||||
{
|
||||
init(false); // FGPositioned::init
|
||||
}
|
||||
|
||||
void FGPavement::addNode(const SGGeod &aPos, bool aClose)
|
||||
|
|
|
@ -51,12 +51,14 @@ FGRunwayBase::FGRunwayBase(Type aTy, const string& aIdent,
|
|||
const double width,
|
||||
const int surface_code,
|
||||
bool index) :
|
||||
FGPositioned(aTy, aIdent, aGeod, index)
|
||||
FGPositioned(aTy, aIdent, aGeod)
|
||||
{
|
||||
_heading = heading;
|
||||
_length = length;
|
||||
_width = width;
|
||||
_surface_code = surface_code;
|
||||
|
||||
init(index);
|
||||
}
|
||||
|
||||
SGGeod FGRunwayBase::pointOnCenterline(double aOffset) const
|
||||
|
|
|
@ -63,6 +63,7 @@ FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tow
|
|||
mRunwaysLoaded(false),
|
||||
mTaxiwaysLoaded(true)
|
||||
{
|
||||
init(true); // init FGPositioned
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -97,11 +97,11 @@ void Autopilot::add_component( Component * component )
|
|||
std::string name = component->get_name();
|
||||
for( unsigned i = 0; get_subsystem( name.c_str() ) != NULL; i++ ) {
|
||||
ostringstream buf;
|
||||
buf << name << "_" << i;
|
||||
buf << component->get_name() << "_" << i;
|
||||
name = buf.str();
|
||||
}
|
||||
if( name != component->get_name() )
|
||||
SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot component " << component->get_name() << ", renamed to " << name );
|
||||
SG_LOG( SG_ALL, SG_WARN, "Duplicate autopilot component " << component->get_name() << ", renamed to " << name );
|
||||
|
||||
set_subsystem( name.c_str(), component );
|
||||
}
|
||||
|
|
|
@ -28,117 +28,35 @@
|
|||
#include "autopilot.hxx"
|
||||
#include "autopilotgroup.hxx"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/util.hxx>
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/structure/SGExpression.hxx>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
using simgear::PropertyList;
|
||||
|
||||
FGXMLAutopilotGroup::FGXMLAutopilotGroup() :
|
||||
SGSubsystemGroup()
|
||||
#ifdef XMLAUTO_USEHELPER
|
||||
,average(0.0), // average/filtered prediction
|
||||
v_last(0.0), // last velocity
|
||||
last_static_pressure(0.0),
|
||||
vel(fgGetNode( "/velocities/airspeed-kt", true )),
|
||||
// Estimate speed in 5,10 seconds
|
||||
lookahead5(fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true )),
|
||||
lookahead10(fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true )),
|
||||
bug(fgGetNode( "/autopilot/settings/heading-bug-deg", true )),
|
||||
mag_hdg(fgGetNode( "/orientation/heading-magnetic-deg", true )),
|
||||
bug_error(fgGetNode( "/autopilot/internal/heading-bug-error-deg", true )),
|
||||
fdm_bug_error(fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true )),
|
||||
target_true(fgGetNode( "/autopilot/settings/true-heading-deg", true )),
|
||||
true_hdg(fgGetNode( "/orientation/heading-deg", true )),
|
||||
true_error(fgGetNode( "/autopilot/internal/true-heading-error-deg", true )),
|
||||
target_nav1(fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true )),
|
||||
true_nav1(fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true )),
|
||||
true_track_nav1(fgGetNode( "/autopilot/internal/nav1-track-error-deg", true )),
|
||||
nav1_course_error(fgGetNode( "/autopilot/internal/nav1-course-error", true )),
|
||||
nav1_selected_course(fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true )),
|
||||
vs_fps(fgGetNode( "/velocities/vertical-speed-fps", true )),
|
||||
vs_fpm(fgGetNode( "/autopilot/internal/vert-speed-fpm", true )),
|
||||
static_pressure(fgGetNode( "/systems/static[0]/pressure-inhg", true )),
|
||||
pressure_rate(fgGetNode( "/autopilot/internal/pressure-rate", true )),
|
||||
track(fgGetNode( "/orientation/track-deg", true ))
|
||||
#endif
|
||||
class FGXMLAutopilotGroupImplementation : public FGXMLAutopilotGroup
|
||||
{
|
||||
}
|
||||
public:
|
||||
void init();
|
||||
void reinit();
|
||||
void update( double dt );
|
||||
private:
|
||||
void initFrom( SGPropertyNode_ptr rootNode, const char * childName );
|
||||
vector<string> _autopilotNames;
|
||||
|
||||
void FGXMLAutopilotGroup::update( double dt )
|
||||
};
|
||||
|
||||
void FGXMLAutopilotGroupImplementation::update( double dt )
|
||||
{
|
||||
// update all configured autopilots
|
||||
SGSubsystemGroup::update( dt );
|
||||
#ifdef XMLAUTO_USEHELPER
|
||||
// update helper values
|
||||
double v = vel->getDoubleValue();
|
||||
double a = 0.0;
|
||||
if ( dt > 0.0 ) {
|
||||
a = (v - v_last) / dt;
|
||||
|
||||
if ( dt < 1.0 ) {
|
||||
average = (1.0 - dt) * average + dt * a;
|
||||
} else {
|
||||
average = a;
|
||||
}
|
||||
|
||||
lookahead5->setDoubleValue( v + average * 5.0 );
|
||||
lookahead10->setDoubleValue( v + average * 10.0 );
|
||||
v_last = v;
|
||||
}
|
||||
|
||||
// Calculate heading bug error normalized to +/- 180.0
|
||||
double diff = bug->getDoubleValue() - mag_hdg->getDoubleValue();
|
||||
SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
|
||||
bug_error->setDoubleValue( diff );
|
||||
|
||||
fdm_bug_error->setDoubleValue( diff );
|
||||
|
||||
// Calculate true heading error normalized to +/- 180.0
|
||||
diff = target_true->getDoubleValue() - true_hdg->getDoubleValue();
|
||||
SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
|
||||
true_error->setDoubleValue( diff );
|
||||
|
||||
// Calculate nav1 target heading error normalized to +/- 180.0
|
||||
diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue();
|
||||
SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
|
||||
true_nav1->setDoubleValue( diff );
|
||||
|
||||
// Calculate true groundtrack
|
||||
diff = target_nav1->getDoubleValue() - track->getDoubleValue();
|
||||
SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
|
||||
true_track_nav1->setDoubleValue( diff );
|
||||
|
||||
// Calculate nav1 selected course error normalized to +/- 180.0
|
||||
diff = nav1_selected_course->getDoubleValue() - mag_hdg->getDoubleValue();
|
||||
SG_NORMALIZE_RANGE( diff, -180.0, 180.0 );
|
||||
nav1_course_error->setDoubleValue( diff );
|
||||
|
||||
// Calculate vertical speed in fpm
|
||||
vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 );
|
||||
|
||||
|
||||
// Calculate static port pressure rate in [inhg/s].
|
||||
// Used to determine vertical speed.
|
||||
if ( dt > 0.0 ) {
|
||||
double current_static_pressure = static_pressure->getDoubleValue();
|
||||
double current_pressure_rate =
|
||||
( current_static_pressure - last_static_pressure ) / dt;
|
||||
|
||||
pressure_rate->setDoubleValue(current_pressure_rate);
|
||||
last_static_pressure = current_static_pressure;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FGXMLAutopilotGroup::reinit()
|
||||
void FGXMLAutopilotGroupImplementation::reinit()
|
||||
{
|
||||
SGSubsystemGroup::unbind();
|
||||
|
||||
|
@ -152,18 +70,29 @@ void FGXMLAutopilotGroup::reinit()
|
|||
init();
|
||||
}
|
||||
|
||||
void FGXMLAutopilotGroup::init()
|
||||
void FGXMLAutopilotGroupImplementation::init()
|
||||
{
|
||||
PropertyList autopilotNodes = fgGetNode( "/sim/systems", true )->getChildren("autopilot");
|
||||
if( autopilotNodes.size() == 0 ) {
|
||||
SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration specified for this model!");
|
||||
return;
|
||||
}
|
||||
static const char * nodeNames[] = {
|
||||
"autopilot",
|
||||
"property-rule"
|
||||
};
|
||||
for( unsigned i = 0; i < sizeof(nodeNames)/sizeof(nodeNames[0]); i++ )
|
||||
initFrom( fgGetNode( "/sim/systems" ), nodeNames[i] );
|
||||
|
||||
SGSubsystemGroup::bind();
|
||||
SGSubsystemGroup::init();
|
||||
}
|
||||
|
||||
void FGXMLAutopilotGroupImplementation::initFrom( SGPropertyNode_ptr rootNode, const char * childName )
|
||||
{
|
||||
if( rootNode == NULL )
|
||||
return;
|
||||
|
||||
PropertyList autopilotNodes = rootNode->getChildren(childName);
|
||||
for( PropertyList::size_type i = 0; i < autopilotNodes.size(); i++ ) {
|
||||
SGPropertyNode_ptr pathNode = autopilotNodes[i]->getNode( "path" );
|
||||
if( pathNode == NULL ) {
|
||||
SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration file specified for this autopilot!");
|
||||
SG_LOG( SG_ALL, SG_WARN, "No configuration file specified for this property-rule!");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -182,40 +111,36 @@ void FGXMLAutopilotGroup::init()
|
|||
string name = apName;
|
||||
for( unsigned i = 0; get_subsystem( apName.c_str() ) != NULL; i++ ) {
|
||||
ostringstream buf;
|
||||
buf << apName << "_" << i;
|
||||
buf << name << "_" << i;
|
||||
apName = buf.str();
|
||||
}
|
||||
if( apName != name )
|
||||
SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot component " << name << ", renamed to " << apName );
|
||||
SG_LOG( SG_ALL, SG_WARN, "Duplicate property-rule configuration name " << name << ", renamed to " << apName );
|
||||
}
|
||||
|
||||
if( get_subsystem( apName.c_str() ) != NULL ) {
|
||||
SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot configuration name " << apName << " ignored" );
|
||||
continue;
|
||||
}
|
||||
SGPath config = globals->resolve_maybe_aircraft_path(pathNode->getStringValue());
|
||||
|
||||
SGPath config = globals->resolve_aircraft_path(pathNode->getStringValue());
|
||||
|
||||
SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from " << config.str() );
|
||||
SG_LOG( SG_ALL, SG_INFO, "Reading property-rule configuration from " << config.str() );
|
||||
|
||||
try {
|
||||
SGPropertyNode_ptr root = new SGPropertyNode();
|
||||
readProperties( config.str(), root );
|
||||
|
||||
SG_LOG( SG_AUTOPILOT, SG_INFO, "adding autopilot subsystem " << apName );
|
||||
SG_LOG( SG_AUTOPILOT, SG_INFO, "adding property-rule subsystem " << apName );
|
||||
FGXMLAutopilot::Autopilot * ap = new FGXMLAutopilot::Autopilot( autopilotNodes[i], root );
|
||||
ap->set_name( apName );
|
||||
set_subsystem( apName, ap );
|
||||
_autopilotNames.push_back( apName );
|
||||
|
||||
} catch (const sg_exception& e) {
|
||||
SG_LOG( SG_AUTOPILOT, SG_ALERT, "Failed to load autopilot configuration: "
|
||||
SG_LOG( SG_AUTOPILOT, SG_ALERT, "Failed to load property-rule configuration: "
|
||||
<< config.str() << ":" << e.getMessage() );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
SGSubsystemGroup::bind();
|
||||
SGSubsystemGroup::init();
|
||||
}
|
||||
|
||||
FGXMLAutopilotGroup * FGXMLAutopilotGroup::createInstance()
|
||||
{
|
||||
return new FGXMLAutopilotGroupImplementation();
|
||||
}
|
||||
|
|
|
@ -24,11 +24,6 @@
|
|||
#ifndef _XMLAUTO_HXX
|
||||
#define _XMLAUTO_HXX 1
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
/**
|
||||
* @brief Model an autopilot system by implementing a SGSubsystemGroup
|
||||
|
@ -37,12 +32,9 @@
|
|||
class FGXMLAutopilotGroup : public SGSubsystemGroup
|
||||
{
|
||||
public:
|
||||
FGXMLAutopilotGroup();
|
||||
void init();
|
||||
void reinit();
|
||||
void update( double dt );
|
||||
private:
|
||||
std::vector<std::string> _autopilotNames;
|
||||
static FGXMLAutopilotGroup * createInstance();
|
||||
protected:
|
||||
FGXMLAutopilotGroup() : SGSubsystemGroup() {}
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/sg_inlines.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
@ -245,7 +246,8 @@ float get_climb_rate( void )
|
|||
float get_view_direction( void )
|
||||
{
|
||||
double view_off = 360.0 - globals->get_current_view()->getHeadingOffset_deg();
|
||||
double view = SGMiscd::normalizeAngle(fgGetDouble("/orientation/heading-deg") + view_off);
|
||||
double view = fgGetDouble("/orientation/heading-deg") + view_off;
|
||||
SG_NORMALIZE_RANGE(view, 0.0, 360.0);
|
||||
return view;
|
||||
}
|
||||
|
||||
|
|
|
@ -135,7 +135,9 @@ FGInterpolateEnvironmentCtrl::init ()
|
|||
read_table( boundary_n, _boundary_table);
|
||||
// pass in a pointer to the environment of the last bondary layer as
|
||||
// a starting point
|
||||
read_table( aloft_n, _aloft_table, &(*(_boundary_table.end()-1))->environment);
|
||||
read_table( aloft_n, _aloft_table,
|
||||
_boundary_table.size() > 0 ?
|
||||
&(*(_boundary_table.end()-1))->environment : NULL );
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "environment_ctrl.hxx"
|
||||
#include "fgclouds.hxx"
|
||||
#include "precipitation_mgr.hxx"
|
||||
#include "ridge_lift.hxx"
|
||||
|
||||
class SGSky;
|
||||
extern SGSky *thesky;
|
||||
|
@ -65,10 +66,18 @@ FGEnvironmentMgr::FGEnvironmentMgr ()
|
|||
|
||||
_precipitationManager = new FGPrecipitationMgr;
|
||||
set_subsystem("precipitation", _precipitationManager);
|
||||
|
||||
set_subsystem("ridgelift", new FGRidgeLift);
|
||||
}
|
||||
|
||||
FGEnvironmentMgr::~FGEnvironmentMgr ()
|
||||
{
|
||||
SGSubsystem * subsys;
|
||||
|
||||
subsys = get_subsystem( "ridgelift" );
|
||||
remove_subsystem( "ridgelift" );
|
||||
delete subsys;
|
||||
|
||||
remove_subsystem("precipitation");
|
||||
delete _precipitationManager;
|
||||
|
||||
|
|
402
src/Environment/terrainsampler.cxx
Normal file
402
src/Environment/terrainsampler.cxx
Normal file
|
@ -0,0 +1,402 @@
|
|||
// terrainsampler.cxx --
|
||||
//
|
||||
// Written by Torsten Dreyer, started July 2010
|
||||
// Based on local weather implementation in nasal from
|
||||
// Thorsten Renk
|
||||
//
|
||||
// Copyright (C) 2010 Curtis Olson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <simgear/math/sg_random.h>
|
||||
#include <Scenery/scenery.hxx>
|
||||
#include <deque>
|
||||
|
||||
#include "terrainsampler.hxx"
|
||||
using simgear::PropertyList;
|
||||
|
||||
#include "tiedpropertylist.hxx"
|
||||
|
||||
namespace Environment {
|
||||
/**
|
||||
* @brief Class for presampling the terrain roughness
|
||||
*/
|
||||
class AreaSampler : public SGSubsystem {
|
||||
public:
|
||||
AreaSampler( SGPropertyNode_ptr rootNode );
|
||||
virtual ~AreaSampler();
|
||||
void update( double dt );
|
||||
void bind();
|
||||
void unbind();
|
||||
void init();
|
||||
void reinit();
|
||||
|
||||
int getElevationHistogramStep() const { return _elevationHistogramStep; }
|
||||
void setElevationHistograpStep( int value ) {
|
||||
_elevationHistogramStep = value > 0 ? value : 500;
|
||||
_elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;
|
||||
}
|
||||
|
||||
int getElevationHistogramMax() const { return _elevationHistogramMax; }
|
||||
void setElevationHistograpMax( int value ) {
|
||||
_elevationHistogramMax = value > 0 ? value : 10000;
|
||||
_elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;
|
||||
}
|
||||
|
||||
int getElevationHistogramCount() const { return _elevationHistogramCount; }
|
||||
|
||||
private:
|
||||
void analyse();
|
||||
|
||||
SGPropertyNode_ptr _rootNode;
|
||||
|
||||
bool _enabled;
|
||||
bool _useAircraftPosition;
|
||||
double _heading_deg;
|
||||
double _speed_kt;
|
||||
int _radius;
|
||||
double _max_computation_time_norm;
|
||||
int _max_samples; // keep xx samples in queue for analysis
|
||||
double _reuse_samples_norm;
|
||||
double _recalc_distance_norm;
|
||||
int _elevationHistogramMax;
|
||||
int _elevationHistogramStep;
|
||||
int _elevationHistogramCount;
|
||||
SGGeod _inputPosition;
|
||||
|
||||
double _altOffset;
|
||||
double _altMedian;
|
||||
double _altMin;
|
||||
double _altLayered;
|
||||
double _altMean;
|
||||
SGGeod _outputPosition;
|
||||
|
||||
SGPropertyNode_ptr _signalNode;
|
||||
SGPropertyNode_ptr _positionLatitudeNode;
|
||||
SGPropertyNode_ptr _positionLongitudeNode;
|
||||
|
||||
deque<double> _elevations;
|
||||
TiedPropertyList _tiedProperties;
|
||||
};
|
||||
|
||||
AreaSampler::AreaSampler( SGPropertyNode_ptr rootNode ) :
|
||||
_rootNode(rootNode),
|
||||
_enabled(true),
|
||||
_useAircraftPosition(false),
|
||||
_heading_deg(0.0),
|
||||
_speed_kt(0.0),
|
||||
_radius(40000.0),
|
||||
_max_computation_time_norm(0.1),
|
||||
_max_samples(1000),
|
||||
_reuse_samples_norm(0.8),
|
||||
_recalc_distance_norm(0.1),
|
||||
_elevationHistogramMax(10000),
|
||||
_elevationHistogramStep(500),
|
||||
_elevationHistogramCount(_elevationHistogramMax/_elevationHistogramStep),
|
||||
_altOffset(0),
|
||||
_altMedian(0),
|
||||
_altMin(0),
|
||||
_altLayered(0),
|
||||
_altMean(0),
|
||||
_signalNode(rootNode->getNode("output/valid", true )),
|
||||
_positionLatitudeNode(fgGetNode( "/position/latitude-deg", true )),
|
||||
_positionLongitudeNode(fgGetNode( "/position/longitude-deg", true ))
|
||||
{
|
||||
_inputPosition.setElevationM( SG_MAX_ELEVATION_M );
|
||||
}
|
||||
|
||||
AreaSampler::~AreaSampler()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void AreaSampler::bind()
|
||||
{
|
||||
_tiedProperties.setRoot( _rootNode );
|
||||
_tiedProperties.Tie( "enabled", &_enabled );
|
||||
|
||||
_tiedProperties.setRoot( _rootNode->getNode( "input", true ) );
|
||||
_tiedProperties.Tie( "use-aircraft-position", &_useAircraftPosition );
|
||||
_tiedProperties.Tie( "latitude-deg", &_inputPosition, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg );
|
||||
_tiedProperties.Tie( "longitude-deg", &_inputPosition, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg );
|
||||
_tiedProperties.Tie( "heading-deg", &_heading_deg );
|
||||
_tiedProperties.Tie( "speed-kt", &_speed_kt );
|
||||
_tiedProperties.Tie( "radius-m", &_radius );
|
||||
_tiedProperties.Tie( "max-computation-time-norm", &_max_computation_time_norm );
|
||||
_tiedProperties.Tie( "max-samples", &_max_samples );
|
||||
_tiedProperties.Tie( "reuse-samples-norm", &_reuse_samples_norm );
|
||||
_tiedProperties.Tie( "recalc-distance-norm", &_recalc_distance_norm );
|
||||
_tiedProperties.Tie( "elevation-histogram-max-ft", this, &AreaSampler::getElevationHistogramMax, &AreaSampler::setElevationHistograpMax );
|
||||
_tiedProperties.Tie( "elevation-histogram-step-ft", this, &AreaSampler::getElevationHistogramStep, &AreaSampler::setElevationHistograpStep );
|
||||
_tiedProperties.Tie( "elevation-histogram-count", this, &AreaSampler::getElevationHistogramCount );
|
||||
|
||||
_tiedProperties.setRoot( _rootNode->getNode( "output", true ) );
|
||||
_tiedProperties.Tie( "alt-offset-ft", &_altOffset );
|
||||
_tiedProperties.Tie( "alt-median-ft", &_altMedian );
|
||||
_tiedProperties.Tie( "alt-min-ft", &_altMin );
|
||||
_tiedProperties.Tie( "alt-layered-ft", &_altLayered );
|
||||
_tiedProperties.Tie( "alt-mean-ft", &_altMean );
|
||||
_tiedProperties.Tie( "longitude-deg", &_outputPosition, &SGGeod::getLongitudeDeg );
|
||||
_tiedProperties.Tie( "latitude-deg", &_outputPosition, &SGGeod::getLatitudeDeg );
|
||||
|
||||
}
|
||||
|
||||
void AreaSampler::unbind()
|
||||
{
|
||||
_tiedProperties.Untie();
|
||||
}
|
||||
|
||||
void AreaSampler::init()
|
||||
{
|
||||
_signalNode->setBoolValue(false);
|
||||
_elevations.clear();
|
||||
_altOffset = 0.0;
|
||||
_altMedian = 0.0;
|
||||
_altMin = 0.0;
|
||||
_altLayered = 0.0;
|
||||
_altMean = 0.0;
|
||||
}
|
||||
|
||||
void AreaSampler::reinit()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void AreaSampler::update( double dt )
|
||||
{
|
||||
// if not enabled or time has stalled, do nothing
|
||||
if( !(_enabled && dt > SGLimitsd::min()) )
|
||||
return;
|
||||
|
||||
// get the aircraft's position if requested
|
||||
if( _useAircraftPosition && _speed_kt < 0.5 ) {
|
||||
_inputPosition = SGGeod::fromDegM(
|
||||
_positionLongitudeNode->getDoubleValue(),
|
||||
_positionLatitudeNode->getDoubleValue(),
|
||||
SG_MAX_ELEVATION_M );
|
||||
}
|
||||
|
||||
// need geocentric coordinates
|
||||
SGGeoc center = SGGeoc::fromGeod( _inputPosition );
|
||||
|
||||
// if a speed is set, move the input position
|
||||
if( _speed_kt >= 0.5 ) {
|
||||
double distance_m = _speed_kt * dt * SG_NM_TO_METER;
|
||||
center = center.advanceRadM( _heading_deg * SG_DEGREES_TO_RADIANS, distance_m );
|
||||
_inputPosition = SGGeod::fromGeoc( center );
|
||||
}
|
||||
|
||||
if( _signalNode->getBoolValue() ) {
|
||||
// if we had finished the iteration and moved more than 10% of the radius
|
||||
// of the sampling area, drop the oldest samples and continue sampling
|
||||
if( SGGeoc::distanceM( center, SGGeoc::fromGeod(_outputPosition ) ) >= _recalc_distance_norm * _radius ) {
|
||||
_elevations.resize( _max_samples * _reuse_samples_norm );
|
||||
_signalNode->setBoolValue( false );
|
||||
}
|
||||
}
|
||||
|
||||
if( _signalNode->getBoolValue() )
|
||||
return; // nothing to do.
|
||||
|
||||
FGScenery * scenery = globals->get_scenery();
|
||||
|
||||
SGTimeStamp start = SGTimeStamp::now();
|
||||
while( (SGTimeStamp::now() - start).toSecs() < dt * _max_computation_time_norm ) {
|
||||
// sample until we used up all our configured time
|
||||
double distance = sg_random();
|
||||
distance = _radius * (1-distance*distance);
|
||||
double course = sg_random() * 2.0 * SG_PI;
|
||||
SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, distance ));
|
||||
double elevation_m = 0.0;
|
||||
|
||||
if (scenery->get_elevation_m( probe, elevation_m, NULL ))
|
||||
_elevations.push_front(elevation_m *= SG_METER_TO_FEET);
|
||||
|
||||
if( _elevations.size() >= (deque<unsigned>::size_type)_max_samples ) {
|
||||
// sampling complete?
|
||||
analyse();
|
||||
_outputPosition = _inputPosition;
|
||||
_signalNode->setBoolValue( true );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AreaSampler::analyse()
|
||||
{
|
||||
double sum;
|
||||
|
||||
vector<int> histogram(_elevationHistogramCount,0);
|
||||
|
||||
for( deque<double>::size_type i = 0; i < _elevations.size(); i++ ) {
|
||||
int idx = SGMisc<int>::clip( (int)(_elevations[i]/_elevationHistogramStep), 0, histogram.size()-1 );
|
||||
histogram[idx]++;
|
||||
}
|
||||
|
||||
_altMedian = 0.0;
|
||||
sum = 0.0;
|
||||
for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
|
||||
sum += histogram[i];
|
||||
if( sum > 0.5 * _elevations.size() ) {
|
||||
_altMedian = i * _elevationHistogramStep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_altOffset = 0.0;
|
||||
sum = 0.0;
|
||||
for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
|
||||
sum += histogram[i];
|
||||
if( sum > 0.3 * _elevations.size() ) {
|
||||
_altOffset = i * _elevationHistogramStep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_altMean = 0.0;
|
||||
for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
|
||||
_altMean += histogram[i] * i;
|
||||
}
|
||||
_altMean *= _elevationHistogramStep;
|
||||
if( _elevations.size() != 0.0 ) _altMean /= _elevations.size();
|
||||
|
||||
_altMin = 0.0;
|
||||
for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
|
||||
if( histogram[i] > 0 ) {
|
||||
_altMin = i * _elevationHistogramStep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double alt_low_min = 0.0;
|
||||
double n_max = 0.0;
|
||||
sum = 0.0;
|
||||
for( vector<int>::size_type i = 0; i < histogram.size()-1; i++ ) {
|
||||
sum += histogram[i];
|
||||
if( histogram[i] > n_max ) n_max = histogram[i];
|
||||
if( n_max > histogram[i+1] && sum > 0.3*_elevations.size()) {
|
||||
alt_low_min = i * _elevationHistogramStep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_altLayered = 0.5 * (_altMin + _altOffset);
|
||||
|
||||
#if 0
|
||||
append(alt_50_array, alt_med);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* --------------------- End of AreaSampler implementation ------------- */
|
||||
|
||||
/* --------------------- TerrainSamplerImplementation -------------------------- */
|
||||
|
||||
class TerrainSamplerImplementation : public TerrainSampler
|
||||
{
|
||||
public:
|
||||
TerrainSamplerImplementation ( SGPropertyNode_ptr rootNode );
|
||||
virtual ~TerrainSamplerImplementation ();
|
||||
|
||||
virtual void init ();
|
||||
virtual void postinit();
|
||||
virtual void reinit ();
|
||||
virtual void bind();
|
||||
virtual void unbind();
|
||||
virtual void update (double delta_time_sec);
|
||||
private:
|
||||
inline string areaSubsystemName( unsigned i ) {
|
||||
ostringstream name;
|
||||
name << "area" << i;
|
||||
return name.str();
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr _rootNode;
|
||||
bool _enabled;
|
||||
TiedPropertyList _tiedProperties;
|
||||
};
|
||||
|
||||
TerrainSamplerImplementation::TerrainSamplerImplementation( SGPropertyNode_ptr rootNode ) :
|
||||
_rootNode( rootNode ),
|
||||
_enabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
TerrainSamplerImplementation::~TerrainSamplerImplementation()
|
||||
{
|
||||
}
|
||||
|
||||
void TerrainSamplerImplementation::init()
|
||||
{
|
||||
PropertyList areaNodes = _rootNode->getChildren( "area" );
|
||||
|
||||
for( PropertyList::size_type i = 0; i < areaNodes.size(); i++ )
|
||||
set_subsystem( areaSubsystemName(i), new AreaSampler( areaNodes[i] ) );
|
||||
|
||||
SGSubsystemGroup::init();
|
||||
}
|
||||
|
||||
void TerrainSamplerImplementation::postinit()
|
||||
{
|
||||
SGSubsystemGroup::bind();//
|
||||
}
|
||||
|
||||
void TerrainSamplerImplementation::reinit()
|
||||
{
|
||||
for( unsigned i = 0;; i++ ) {
|
||||
string subsystemName = areaSubsystemName(i);
|
||||
SGSubsystem * subsys = get_subsystem( subsystemName );
|
||||
if( subsys == NULL )
|
||||
break;
|
||||
remove_subsystem( subsystemName );
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
void TerrainSamplerImplementation::bind()
|
||||
{
|
||||
SGSubsystemGroup::bind();
|
||||
_tiedProperties.Tie( _rootNode->getNode("enabled",true), &_enabled );
|
||||
}
|
||||
|
||||
void TerrainSamplerImplementation::unbind()
|
||||
{
|
||||
_tiedProperties.Untie();
|
||||
SGSubsystemGroup::unbind();
|
||||
}
|
||||
|
||||
void TerrainSamplerImplementation::update( double dt )
|
||||
{
|
||||
if( !(_enabled && dt > SGLimitsd::min()) )
|
||||
return;
|
||||
SGSubsystemGroup::update(dt);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
/* implementation of the TerrainSampler factory to hide the implementation
|
||||
details */
|
||||
TerrainSampler * TerrainSampler::createInstance( SGPropertyNode_ptr rootNode )
|
||||
{
|
||||
return new TerrainSamplerImplementation( rootNode );
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
36
src/Environment/terrainsampler.hxx
Normal file
36
src/Environment/terrainsampler.hxx
Normal file
|
@ -0,0 +1,36 @@
|
|||
// terrainsampler.hxx --
|
||||
//
|
||||
// Written by Torsten Dreyer, started July 2010
|
||||
// Based on local weather implementation in nasal from
|
||||
// Thorsten Renk
|
||||
//
|
||||
// Copyright (C) 2010 Curtis Olson
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
#ifndef _TERRAIN_SAMPLER_HXX
|
||||
#define _TERRAIN_SAMPLER_HXX
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
namespace Environment {
|
||||
class TerrainSampler : public SGSubsystemGroup
|
||||
{
|
||||
public:
|
||||
static TerrainSampler * createInstance( SGPropertyNode_ptr rootNode );
|
||||
};
|
||||
|
||||
} // namespace
|
||||
#endif
|
80
src/Environment/tiedpropertylist.hxx
Normal file
80
src/Environment/tiedpropertylist.hxx
Normal file
|
@ -0,0 +1,80 @@
|
|||
#ifndef __TIEDPROPERTYLIST_HXX
|
||||
#define __TIEDPROPERTYLIST_HXX
|
||||
#include <simgear/props/props.hxx>
|
||||
using simgear::PropertyList;
|
||||
|
||||
// Maybe this goes into SimGear's props.hxx later?
|
||||
class TiedPropertyList : PropertyList {
|
||||
public:
|
||||
TiedPropertyList() {}
|
||||
TiedPropertyList( SGPropertyNode_ptr root ) { _root = root; }
|
||||
|
||||
void setRoot( SGPropertyNode_ptr root ) { _root = root; }
|
||||
SGPropertyNode_ptr getRoot() const { return _root; }
|
||||
|
||||
template<typename T> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, const SGRawValue<T> &rawValue, bool useDefault = true ) {
|
||||
bool success = node->tie( rawValue, useDefault );
|
||||
if( success ) {
|
||||
SG_LOG( SG_ALL, SG_INFO, "Tied " << node->getPath() );
|
||||
push_back( node );
|
||||
} else {
|
||||
#if PROPS_STANDALONE
|
||||
cerr << "Failed to tie property " << node->getPath() << endl;
|
||||
#else
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "Failed to tie property " << node->getPath() );
|
||||
#endif
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
template <class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, V * value, bool useDefault = true ) {
|
||||
return Tie( node, SGRawValuePointer<V>(value), useDefault );
|
||||
}
|
||||
|
||||
template <class V> SGPropertyNode_ptr Tie( const char * relative_path, V * value, bool useDefault = true ) {
|
||||
return Tie( _root->getNode(relative_path,true), SGRawValuePointer<V>(value), useDefault );
|
||||
}
|
||||
|
||||
template <class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, V (*getter)(), void (*setter)(V) = 0, bool useDefault = true ) {
|
||||
return Tie(node, SGRawValueFunctions<V>(getter, setter), useDefault );
|
||||
}
|
||||
|
||||
template <class V> SGPropertyNode_ptr Tie( const char * relative_path, V (*getter)(), void (*setter)(V) = 0, bool useDefault = true ) {
|
||||
return Tie(_root->getNode(relative_path, true), SGRawValueFunctions<V>(getter, setter), useDefault );
|
||||
}
|
||||
|
||||
template <class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, int index, V (*getter)(int), void (*setter)(int, V) = 0, bool useDefault = true) {
|
||||
return Tie( node, SGRawValueFunctionsIndexed<V>(index, getter, setter), useDefault );
|
||||
}
|
||||
|
||||
template <class V> SGPropertyNode_ptr Tie( const char * relative_path, int index, V (*getter)(int), void (*setter)(int, V) = 0, bool useDefault = true) {
|
||||
return Tie( _root->getNode( relative_path, true ), SGRawValueFunctionsIndexed<V>(index, getter, setter), useDefault );
|
||||
}
|
||||
|
||||
template <class T, class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, T * obj, V (T::*getter)() const, void (T::*setter)(V) = 0, bool useDefault = true) {
|
||||
return Tie( node, SGRawValueMethods<T,V>(*obj, getter, setter), useDefault );
|
||||
}
|
||||
|
||||
template <class T, class V> SGPropertyNode_ptr Tie( const char * relative_path, T * obj, V (T::*getter)() const, void (T::*setter)(V) = 0, bool useDefault = true) {
|
||||
return Tie( _root->getNode( relative_path, true), SGRawValueMethods<T,V>(*obj, getter, setter), useDefault );
|
||||
}
|
||||
|
||||
template <class T, class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, T * obj, int index, V (T::*getter)(int) const, void (T::*setter)(int, V) = 0, bool useDefault = true) {
|
||||
return Tie( node, SGRawValueMethodsIndexed<T,V>(*obj, index, getter, setter), useDefault);
|
||||
}
|
||||
|
||||
template <class T, class V> SGPropertyNode_ptr Tie( const char * relative_path, T * obj, int index, V (T::*getter)(int) const, void (T::*setter)(int, V) = 0, bool useDefault = true) {
|
||||
return Tie( _root->getNode( relative_path, true ), SGRawValueMethodsIndexed<T,V>(*obj, index, getter, setter), useDefault);
|
||||
}
|
||||
|
||||
void Untie() {
|
||||
while( size() > 0 ) {
|
||||
SG_LOG( SG_ALL, SG_INFO, "untie of " << back()->getPath() );
|
||||
back()->untie();
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
private:
|
||||
SGPropertyNode_ptr _root;
|
||||
};
|
||||
#endif
|
|
@ -1102,7 +1102,8 @@ FGPositioned* DCLGPS::FindTypedFirstById(const string& id, FGPositioned::Type ty
|
|||
|
||||
if (exact) {
|
||||
FGPositioned::List matches =
|
||||
FGPositioned::findAllWithIdentSortedByRange(id, SGGeod::fromRad(_lon, _lat), &filter);
|
||||
FGPositioned::findAllWithIdent(id, &filter);
|
||||
FGPositioned::sortByRange(matches, SGGeod::fromRad(_lon, _lat));
|
||||
multi = (matches.size() > 1);
|
||||
return matches.empty() ? NULL : matches.front().ptr();
|
||||
}
|
||||
|
|
|
@ -220,7 +220,6 @@ GPS::GPS ( SGPropertyNode *node) :
|
|||
_mode("init"),
|
||||
_name(node->getStringValue("name", "gps")),
|
||||
_num(node->getIntValue("number", 0)),
|
||||
_searchResultsCached(false),
|
||||
_computeTurnData(false),
|
||||
_anticipateTurn(false),
|
||||
_inTurn(false)
|
||||
|
@ -333,7 +332,7 @@ GPS::bind()
|
|||
|
||||
tie(_gpsNode, "desired-course-deg", SGRawValueMethods<GPS, double>
|
||||
(*this, &GPS::getDesiredCourse, NULL));
|
||||
_desiredCourseNode = _gpsNode->getChild("desired-course-deg");
|
||||
_desiredCourseNode = _gpsNode->getChild("desired-course-deg", 0, true);
|
||||
|
||||
tieSGGeodReadOnly(_gpsNode, _indicated_pos, "indicated-longitude-deg",
|
||||
"indicated-latitude-deg", "indicated-altitude-ft");
|
||||
|
@ -1384,9 +1383,6 @@ void GPS::setScratchFromRouteWaypoint(int aIndex)
|
|||
_scratchValid = true;
|
||||
_scratchNode->setDoubleValue("course", wp.get_track());
|
||||
_scratchNode->setIntValue("index", aIndex);
|
||||
|
||||
int lastResult = _routeMgr->size() - 1;
|
||||
_searchHasNext = (_searchResultIndex < lastResult);
|
||||
}
|
||||
|
||||
void GPS::loadNearest()
|
||||
|
@ -1411,17 +1407,14 @@ void GPS::loadNearest()
|
|||
|
||||
_searchResults =
|
||||
FGPositioned::findClosestN(searchPos, limitCount, cutoffDistance, f.get());
|
||||
_searchResultsCached = true;
|
||||
_searchResultIndex = 0;
|
||||
_searchIsRoute = false;
|
||||
_searchHasNext = false;
|
||||
|
||||
if (_searchResults.empty()) {
|
||||
SG_LOG(SG_INSTR, SG_INFO, "GPS:loadNearest: no matches at all");
|
||||
return;
|
||||
}
|
||||
|
||||
_searchHasNext = (_searchResults.size() > 1);
|
||||
setScratchFromCachedSearchResult();
|
||||
}
|
||||
|
||||
|
@ -1478,66 +1471,47 @@ void GPS::search()
|
|||
}
|
||||
|
||||
_searchExact = _scratchNode->getBoolValue("exact", true);
|
||||
_searchOrderByRange = _scratchNode->getBoolValue("order-by-distance", true);
|
||||
_searchResultIndex = 0;
|
||||
_searchIsRoute = false;
|
||||
_searchHasNext = false;
|
||||
|
||||
if (_searchExact && _searchOrderByRange) {
|
||||
// immediate mode search, get all the results now and cache them
|
||||
auto_ptr<FGPositioned::Filter> f(createFilter(_searchType));
|
||||
if (_searchNames) {
|
||||
_searchResults = FGPositioned::findAllWithNameSortedByRange(_searchQuery, _indicated_pos, f.get());
|
||||
} else {
|
||||
_searchResults = FGPositioned::findAllWithIdentSortedByRange(_searchQuery, _indicated_pos, f.get());
|
||||
}
|
||||
|
||||
_searchResultsCached = true;
|
||||
|
||||
if (_searchResults.empty()) {
|
||||
clearScratch();
|
||||
return;
|
||||
}
|
||||
|
||||
_searchHasNext = (_searchResults.size() > 1);
|
||||
setScratchFromCachedSearchResult();
|
||||
} else {
|
||||
// iterative search, look up result zero
|
||||
_searchResultsCached = false;
|
||||
performSearch();
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::performSearch()
|
||||
{
|
||||
auto_ptr<FGPositioned::Filter> f(createFilter(_searchType));
|
||||
clearScratch();
|
||||
|
||||
FGPositionedRef r;
|
||||
if (_searchNames) {
|
||||
if (_searchOrderByRange) {
|
||||
r = FGPositioned::findClosestWithPartialName(_indicated_pos, _searchQuery, f.get(), _searchResultIndex, _searchHasNext);
|
||||
} else {
|
||||
r = FGPositioned::findWithPartialName(_searchQuery, f.get(), _searchResultIndex, _searchHasNext);
|
||||
}
|
||||
_searchResults = FGPositioned::findAllWithName(_searchQuery, f.get());
|
||||
} else {
|
||||
if (_searchOrderByRange) {
|
||||
r = FGPositioned::findClosestWithPartialId(_indicated_pos, _searchQuery, f.get(), _searchResultIndex, _searchHasNext);
|
||||
} else {
|
||||
r = FGPositioned::findWithPartialId(_searchQuery, f.get(), _searchResultIndex, _searchHasNext);
|
||||
}
|
||||
_searchResults = FGPositioned::findAllWithIdent(_searchQuery, f.get());
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
bool orderByRange = _scratchNode->getBoolValue("order-by-distance", true);
|
||||
if (orderByRange) {
|
||||
FGPositioned::sortByRange(_searchResults, _indicated_pos);
|
||||
}
|
||||
|
||||
if (_searchResults.empty()) {
|
||||
clearScratch();
|
||||
return;
|
||||
}
|
||||
|
||||
setScratchFromPositioned(r.get(), _searchResultIndex);
|
||||
setScratchFromCachedSearchResult();
|
||||
}
|
||||
|
||||
bool GPS::getScratchHasNext() const
|
||||
{
|
||||
int lastResult;
|
||||
if (_searchIsRoute) {
|
||||
lastResult = _routeMgr->size() - 1;
|
||||
} else {
|
||||
lastResult = (int) _searchResults.size() - 1;
|
||||
}
|
||||
|
||||
if (lastResult < 0) { // search array might be empty
|
||||
return false;
|
||||
}
|
||||
|
||||
return (_searchResultIndex < lastResult);
|
||||
}
|
||||
|
||||
void GPS::setScratchFromCachedSearchResult()
|
||||
{
|
||||
assert(_searchResultsCached);
|
||||
int index = _searchResultIndex;
|
||||
|
||||
if ((index < 0) || (index >= (int) _searchResults.size())) {
|
||||
|
@ -1546,9 +1520,6 @@ void GPS::setScratchFromCachedSearchResult()
|
|||
}
|
||||
|
||||
setScratchFromPositioned(_searchResults[index], index);
|
||||
|
||||
int lastResult = (int) _searchResults.size() - 1;
|
||||
_searchHasNext = (_searchResultIndex < lastResult);
|
||||
}
|
||||
|
||||
void GPS::setScratchFromPositioned(FGPositioned* aPos, int aIndex)
|
||||
|
@ -1566,9 +1537,7 @@ void GPS::setScratchFromPositioned(FGPositioned* aPos, int aIndex)
|
|||
}
|
||||
|
||||
_scratchValid = true;
|
||||
if (_searchResultsCached) {
|
||||
_scratchNode->setIntValue("result-count", _searchResults.size());
|
||||
}
|
||||
_scratchNode->setIntValue("result-count", _searchResults.size());
|
||||
|
||||
switch (aPos->type()) {
|
||||
case FGPositioned::VOR:
|
||||
|
@ -1652,20 +1621,17 @@ void GPS::selectLegMode()
|
|||
|
||||
void GPS::nextResult()
|
||||
{
|
||||
if (!_searchHasNext) {
|
||||
if (!getScratchHasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearScratch();
|
||||
if (_searchIsRoute) {
|
||||
setScratchFromRouteWaypoint(++_searchResultIndex);
|
||||
} else if (_searchResultsCached) {
|
||||
++_searchResultIndex;
|
||||
setScratchFromCachedSearchResult();
|
||||
} else {
|
||||
++_searchResultIndex;
|
||||
performSearch();
|
||||
} // of iterative search case
|
||||
setScratchFromCachedSearchResult();
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::previousResult()
|
||||
|
@ -1679,10 +1645,8 @@ void GPS::previousResult()
|
|||
|
||||
if (_searchIsRoute) {
|
||||
setScratchFromRouteWaypoint(_searchResultIndex);
|
||||
} else if (_searchResultsCached) {
|
||||
setScratchFromCachedSearchResult();
|
||||
} else {
|
||||
performSearch();
|
||||
setScratchFromCachedSearchResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1701,14 +1665,15 @@ void GPS::defineWaypoint()
|
|||
|
||||
// check for duplicate idents
|
||||
FGPositioned::TypeFilter f(FGPositioned::WAYPOINT);
|
||||
FGPositioned::List dups = FGPositioned::findAllWithIdentSortedByRange(ident, _indicated_pos, &f);
|
||||
FGPositioned::List dups = FGPositioned::findAllWithIdent(ident, &f);
|
||||
if (!dups.empty()) {
|
||||
SG_LOG(SG_INSTR, SG_WARN, "GPS:defineWaypoint: non-unique waypoint identifier, ho-hum");
|
||||
}
|
||||
|
||||
SG_LOG(SG_INSTR, SG_INFO, "GPS:defineWaypoint: creating waypoint:" << ident);
|
||||
FGPositionedRef wpt = FGPositioned::createUserWaypoint(ident, _scratchPos);
|
||||
_searchResultsCached = false;
|
||||
_searchResults.clear();
|
||||
_searchResults.push_back(wpt);
|
||||
setScratchFromPositioned(wpt.get(), -1);
|
||||
}
|
||||
|
||||
|
|
|
@ -264,7 +264,7 @@ private:
|
|||
double getScratchDistance() const;
|
||||
double getScratchMagBearing() const;
|
||||
double getScratchTrueBearing() const;
|
||||
bool getScratchHasNext() const { return _searchHasNext; }
|
||||
bool getScratchHasNext() const;
|
||||
|
||||
double getSelectedCourse() const { return _selectedCourse; }
|
||||
void setSelectedCourse(double crs);
|
||||
|
@ -394,8 +394,6 @@ private:
|
|||
std::string _searchQuery;
|
||||
FGPositioned::Type _searchType;
|
||||
bool _searchExact;
|
||||
bool _searchOrderByRange;
|
||||
bool _searchResultsCached;
|
||||
FGPositioned::List _searchResults;
|
||||
bool _searchIsRoute; ///< set if 'search' is actually the current route
|
||||
bool _searchHasNext; ///< is there a result after this one?
|
||||
|
|
|
@ -92,6 +92,7 @@ void FGInstrumentMgr::init()
|
|||
SGPropertyNode_ptr nd(new SGPropertyNode);
|
||||
nd->setStringValue("name", "gps");
|
||||
nd->setIntValue("number", 0);
|
||||
_instruments.push_back("gps[0]");
|
||||
set_subsystem("gps[0]", new GPS(nd));
|
||||
}
|
||||
|
||||
|
|
|
@ -1349,6 +1349,10 @@ do_load_xml_to_proptree(const SGPropertyNode * arg)
|
|||
if (file.extension() != "xml")
|
||||
file.concat(".xml");
|
||||
|
||||
if (file.isRelative()) {
|
||||
file = globals->resolve_maybe_aircraft_path(file.str());
|
||||
}
|
||||
|
||||
if (!fgValidatePath(file.c_str(), false)) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "loadxml: reading '" << file.str() << "' denied "
|
||||
"(unauthorized access)");
|
||||
|
|
|
@ -109,7 +109,6 @@
|
|||
#include <FDM/fdm_shell.hxx>
|
||||
|
||||
#include <Environment/environment_mgr.hxx>
|
||||
#include <Environment/ridge_lift.hxx>
|
||||
|
||||
#include "fg_init.hxx"
|
||||
#include "fg_io.hxx"
|
||||
|
@ -1259,8 +1258,8 @@ bool fgInitGeneral() {
|
|||
curr->setStringValue(cwd ? cwd : "");
|
||||
curr->setAttribute(SGPropertyNode::WRITE, false);
|
||||
|
||||
fgSetBool("/sim/startup/stdout-to-terminal", isatty(1));
|
||||
fgSetBool("/sim/startup/stderr-to-terminal", isatty(2));
|
||||
fgSetBool("/sim/startup/stdout-to-terminal", isatty(1) != 0 );
|
||||
fgSetBool("/sim/startup/stderr-to-terminal", isatty(2) != 0 );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1349,13 +1348,6 @@ bool fgInitSubsystems() {
|
|||
// Initialize the weather modeling subsystem
|
||||
globals->add_subsystem("environment", new FGEnvironmentMgr);
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the ridge lift simulation.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize the ridgelift subsystem
|
||||
globals->add_subsystem("ridgelift", new FGRidgeLift);
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Initialize the aircraft systems and instrumentation (before the
|
||||
// autopilot.)
|
||||
|
@ -1368,7 +1360,7 @@ bool fgInitSubsystems() {
|
|||
// Initialize the XML Autopilot subsystem.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
globals->add_subsystem( "xml-autopilot", new FGXMLAutopilotGroup, SGSubsystemMgr::FDM );
|
||||
globals->add_subsystem( "xml-autopilot", FGXMLAutopilotGroup::createInstance(), SGSubsystemMgr::FDM );
|
||||
globals->add_subsystem( "route-manager", new FGRouteMgr );
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -105,6 +105,7 @@ FGGlobals::FGGlobals() :
|
|||
airwaynet( NULL ),
|
||||
multiplayer_mgr( NULL )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -239,7 +240,15 @@ void FGGlobals::append_aircraft_path(const std::string& path)
|
|||
return;
|
||||
}
|
||||
|
||||
unsigned int index = fg_aircraft_dirs.size();
|
||||
fg_aircraft_dirs.push_back(path);
|
||||
|
||||
// make aircraft dirs available to Nasal
|
||||
SGPropertyNode* sim = fgGetNode("/sim", true);
|
||||
sim->removeChild("fg-aircraft", index, false);
|
||||
SGPropertyNode* n = sim->getChild("fg-aircraft", index, true);
|
||||
n->setStringValue(path);
|
||||
n->setAttribute(SGPropertyNode::WRITE, false);
|
||||
}
|
||||
|
||||
void FGGlobals::append_aircraft_paths(const std::string& path)
|
||||
|
|
|
@ -30,7 +30,8 @@ using namespace simgear;
|
|||
osg::Node *
|
||||
fgLoad3DModelPanel(const std::string &path, SGPropertyNode *prop_root)
|
||||
{
|
||||
osg::Node* node = SGModelLib::loadModel(path, prop_root);
|
||||
bool loadPanels = true;
|
||||
osg::Node* node = SGModelLib::loadModel(path, prop_root, NULL, loadPanels);
|
||||
if (node)
|
||||
node->setNodeMask(~SG_NODEMASK_TERRAIN_BIT);
|
||||
return node;
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
FGFix::FGFix(const std::string& aIdent, const SGGeod& aPos) :
|
||||
FGPositioned(FIX, aIdent, aPos)
|
||||
{
|
||||
init(true); // init FGPositioned
|
||||
}
|
||||
|
||||
// Constructor
|
||||
|
|
|
@ -65,4 +65,5 @@ FGMarkerBeaconRecord::FGMarkerBeaconRecord(Type aTy, FGRunway* aRunway, const SG
|
|||
FGPositioned(aTy, string(), aPos),
|
||||
_runway(aRunway)
|
||||
{
|
||||
init(true); // init FGPositioned
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ FGNavRecord::FGNavRecord(Type aTy, const std::string& aIdent,
|
|||
}
|
||||
}
|
||||
|
||||
init(true); // init FGPositioned (now position is adjusted)
|
||||
}
|
||||
|
||||
void FGNavRecord::initAirportRelation()
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "positioned.hxx"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <algorithm> // for sort
|
||||
|
@ -35,7 +37,7 @@
|
|||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/math/SGGeometry.hxx>
|
||||
|
||||
#include "positioned.hxx"
|
||||
|
||||
|
||||
typedef std::multimap<std::string, FGPositioned*> NamedPositionedIndex;
|
||||
typedef std::pair<NamedPositionedIndex::const_iterator, NamedPositionedIndex::const_iterator> NamedIndexRange;
|
||||
|
@ -355,13 +357,17 @@ addToIndices(FGPositioned* aPos)
|
|||
{
|
||||
assert(aPos);
|
||||
if (!aPos->ident().empty()) {
|
||||
std::string u(boost::to_upper_copy(aPos->ident()));
|
||||
|
||||
global_identIndex.insert(global_identIndex.begin(),
|
||||
std::make_pair(aPos->ident(), aPos));
|
||||
std::make_pair(u, aPos));
|
||||
}
|
||||
|
||||
if (!aPos->name().empty()) {
|
||||
std::string u(boost::to_upper_copy(aPos->name()));
|
||||
|
||||
global_nameIndex.insert(global_nameIndex.begin(),
|
||||
std::make_pair(aPos->name(), aPos));
|
||||
std::make_pair(u, aPos));
|
||||
}
|
||||
|
||||
if (!Octree::global_spatialOctree) {
|
||||
|
@ -378,8 +384,9 @@ removeFromIndices(FGPositioned* aPos)
|
|||
assert(aPos);
|
||||
|
||||
if (!aPos->ident().empty()) {
|
||||
NamedPositionedIndex::iterator it = global_identIndex.find(aPos->ident());
|
||||
while (it != global_identIndex.end() && (it->first == aPos->ident())) {
|
||||
std::string u(boost::to_upper_copy(aPos->ident()));
|
||||
NamedPositionedIndex::iterator it = global_identIndex.find(u);
|
||||
while (it != global_identIndex.end() && (it->first == u)) {
|
||||
if (it->second == aPos) {
|
||||
global_identIndex.erase(it);
|
||||
break;
|
||||
|
@ -390,8 +397,9 @@ removeFromIndices(FGPositioned* aPos)
|
|||
}
|
||||
|
||||
if (!aPos->name().empty()) {
|
||||
NamedPositionedIndex::iterator it = global_nameIndex.find(aPos->name());
|
||||
while (it != global_nameIndex.end() && (it->first == aPos->name())) {
|
||||
std::string u(boost::to_upper_copy(aPos->name()));
|
||||
NamedPositionedIndex::iterator it = global_nameIndex.find(u);
|
||||
while (it != global_nameIndex.end() && (it->first == u)) {
|
||||
if (it->second == aPos) {
|
||||
global_nameIndex.erase(it);
|
||||
break;
|
||||
|
@ -402,91 +410,6 @@ removeFromIndices(FGPositioned* aPos)
|
|||
}
|
||||
}
|
||||
|
||||
class DistanceOrdering
|
||||
{
|
||||
public:
|
||||
DistanceOrdering(const SGGeod& aPos) :
|
||||
mPos(SGVec3d::fromGeod(aPos))
|
||||
{ }
|
||||
|
||||
bool operator()(const FGPositionedRef& a, const FGPositionedRef& b) const
|
||||
{
|
||||
if (!a || !b) {
|
||||
throw sg_exception("empty reference passed to DistanceOrdering");
|
||||
}
|
||||
|
||||
double dA = distSqr(a->cart(), mPos),
|
||||
dB = distSqr(b->cart(), mPos);
|
||||
return dA < dB;
|
||||
}
|
||||
|
||||
private:
|
||||
SGVec3d mPos;
|
||||
};
|
||||
|
||||
static void
|
||||
sortByDistance(const SGGeod& aPos, FGPositioned::List& aResult)
|
||||
{
|
||||
std::sort(aResult.begin(), aResult.end(), DistanceOrdering(aPos));
|
||||
}
|
||||
|
||||
static FGPositionedRef
|
||||
namedFindClosest(const NamedPositionedIndex& aIndex, const std::string& aName,
|
||||
const SGGeod& aOrigin, FGPositioned::Filter* aFilter)
|
||||
{
|
||||
NamedIndexRange range = aIndex.equal_range(aName);
|
||||
if (range.first == range.second) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// common case, only one result. looks a bit ugly because these are
|
||||
// sequential iterators, not random-access ones
|
||||
NamedPositionedIndex::const_iterator check = range.first;
|
||||
if (++check == range.second) {
|
||||
// excellent, only one match in the range
|
||||
FGPositioned* r = range.first->second;
|
||||
if (aFilter) {
|
||||
if (aFilter->hasTypeRange() && !aFilter->passType(r->type())) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!aFilter->pass(r)) {
|
||||
return NULL;
|
||||
}
|
||||
} // of have a filter
|
||||
|
||||
return r;
|
||||
} // of short-circuit logic for single-element range
|
||||
|
||||
// multiple matches, we need to actually check the distance to each one
|
||||
double minDist = HUGE_VAL;
|
||||
FGPositionedRef result;
|
||||
NamedPositionedIndex::const_iterator it = range.first;
|
||||
SGVec3d cartOrigin(SGVec3d::fromGeod(aOrigin));
|
||||
|
||||
for (; it != range.second; ++it) {
|
||||
FGPositioned* r = it->second;
|
||||
if (aFilter) {
|
||||
if (aFilter->hasTypeRange() && !aFilter->passType(r->type())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!aFilter->pass(r)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// find distance
|
||||
double d2 = distSqr(cartOrigin, r->cart());
|
||||
if (d2 < minDist) {
|
||||
minDist = d2;
|
||||
result = r;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class OrderByName
|
||||
|
@ -498,6 +421,27 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void findInIndex(NamedPositionedIndex& aIndex, const std::string& aFind, std::vector<FGPositioned*>& aResult)
|
||||
{
|
||||
NamedPositionedIndex::const_iterator it = aIndex.begin();
|
||||
NamedPositionedIndex::const_iterator end = aIndex.end();
|
||||
|
||||
bool haveFilter = !aFind.empty();
|
||||
|
||||
for (; it != end; ++it) {
|
||||
FGPositioned::Type ty = it->second->type();
|
||||
if ((ty < FGPositioned::AIRPORT) || (ty > FGPositioned::SEAPORT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (haveFilter && it->first.find(aFind) == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
aResult.push_back(it->second);
|
||||
} // of index iteration
|
||||
}
|
||||
|
||||
/**
|
||||
* A special purpose helper (imported by FGAirport::searchNamesAndIdents) to
|
||||
* implement the AirportList dialog. It's unfortunate that it needs to reside
|
||||
|
@ -505,43 +449,23 @@ public:
|
|||
*/
|
||||
char** searchAirportNamesAndIdents(const std::string& aFilter)
|
||||
{
|
||||
const std::ctype<char> &ct = std::use_facet<std::ctype<char> >(std::locale());
|
||||
std::string filter(aFilter);
|
||||
bool hasFilter = !filter.empty();
|
||||
if (hasFilter) {
|
||||
ct.toupper((char *)filter.data(), (char *)filter.data() + filter.size());
|
||||
}
|
||||
|
||||
NamedPositionedIndex::const_iterator it = global_identIndex.begin();
|
||||
NamedPositionedIndex::const_iterator end = global_identIndex.end();
|
||||
|
||||
// note this is a vector of raw pointers, not smart pointers, because it
|
||||
// may get very large and smart-pointer-atomicity-locking then becomes a
|
||||
// bottleneck for this case.
|
||||
// note this is a vector of raw pointers, not smart pointers, because it
|
||||
// may get very large and smart-pointer-atomicity-locking then becomes a
|
||||
// bottleneck for this case.
|
||||
std::vector<FGPositioned*> matches;
|
||||
std::string upper;
|
||||
|
||||
for (; it != end; ++it) {
|
||||
FGPositioned::Type ty = it->second->type();
|
||||
if ((ty < FGPositioned::AIRPORT) || (ty > FGPositioned::SEAPORT)) {
|
||||
continue;
|
||||
}
|
||||
if (!aFilter.empty()) {
|
||||
std::string filter = boost::to_upper_copy(aFilter);
|
||||
findInIndex(global_identIndex, filter, matches);
|
||||
findInIndex(global_nameIndex, filter, matches);
|
||||
} else {
|
||||
|
||||
if (hasFilter && (it->second->ident().find(aFilter) == std::string::npos)) {
|
||||
upper = it->second->name(); // string copy, sadly
|
||||
ct.toupper((char *)upper.data(), (char *)upper.data() + upper.size());
|
||||
if (upper.find(aFilter) == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
matches.push_back(it->second);
|
||||
findInIndex(global_identIndex, std::string(), matches);
|
||||
}
|
||||
|
||||
// sort alphabetically on name
|
||||
// sort alphabetically on name
|
||||
std::sort(matches.begin(), matches.end(), OrderByName());
|
||||
|
||||
// convert results to format comptible with puaList
|
||||
// convert results to format comptible with puaList
|
||||
unsigned int numMatches = matches.size();
|
||||
char** result = new char*[numMatches + 1];
|
||||
result[numMatches] = NULL; // end-of-list marker
|
||||
|
@ -592,14 +516,23 @@ FGPositioned::Filter::passType(Type aTy) const
|
|||
return (minType() <= aTy) && (maxType() >= aTy);
|
||||
}
|
||||
|
||||
static FGPositioned::List
|
||||
findAllSortedByRange(const NamedPositionedIndex& aIndex,
|
||||
const std::string& aName, const SGGeod& aPos, FGPositioned::Filter* aFilter)
|
||||
static FGPositioned::List
|
||||
findAll(const NamedPositionedIndex& aIndex,
|
||||
const std::string& aName, FGPositioned::Filter* aFilter)
|
||||
{
|
||||
FGPositioned::List result;
|
||||
NamedIndexRange range = aIndex.equal_range(aName);
|
||||
for (; range.first != range.second; ++range.first) {
|
||||
FGPositioned* candidate = range.first->second;
|
||||
if (aName.empty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string name = boost::to_upper_copy(aName);
|
||||
std::string upperBoundId = name;
|
||||
upperBoundId[upperBoundId.size()-1]++;
|
||||
NamedPositionedIndex::const_iterator upperBound = aIndex.lower_bound(upperBoundId);
|
||||
NamedPositionedIndex::const_iterator it = aIndex.lower_bound(name);
|
||||
|
||||
for (; it != upperBound; ++it) {
|
||||
FGPositionedRef candidate = it->second;
|
||||
if (aFilter) {
|
||||
if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
|
||||
continue;
|
||||
|
@ -610,70 +543,28 @@ findAllSortedByRange(const NamedPositionedIndex& aIndex,
|
|||
}
|
||||
}
|
||||
|
||||
result.push_back(range.first->second);
|
||||
result.push_back(candidate);
|
||||
}
|
||||
|
||||
sortByDistance(aPos, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
static FGPositionedRef
|
||||
findWithPartial(const NamedPositionedIndex& aIndex, const std::string& aName,
|
||||
FGPositioned::Filter* aFilter, int aOffset, bool& aNext)
|
||||
{
|
||||
// see comment in findNextWithPartialId concerning upperBoundId
|
||||
std::string upperBoundId = aName;
|
||||
upperBoundId[upperBoundId.size()-1]++;
|
||||
NamedPositionedIndex::const_iterator upperBound = aIndex.lower_bound(upperBoundId);
|
||||
|
||||
NamedIndexRange range = aIndex.equal_range(aName);
|
||||
FGPositionedRef result;
|
||||
|
||||
while (range.first != upperBound) {
|
||||
for (; range.first != range.second; ++range.first) {
|
||||
FGPositionedRef candidate = range.first->second;
|
||||
if (aFilter) {
|
||||
if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!aFilter->pass(candidate)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
aNext = true;
|
||||
return result;
|
||||
} else if (aOffset == 0) {
|
||||
// okay, found our result. we need to go around once more to set aNext
|
||||
result = candidate;
|
||||
} else {
|
||||
--aOffset; // seen one more valid result, decrement the count
|
||||
}
|
||||
}
|
||||
|
||||
// Unable to match the filter with this range - try the next range.
|
||||
range = aIndex.equal_range(range.second->first);
|
||||
}
|
||||
|
||||
// if we fell out, we reached the end of the valid range. We might have a
|
||||
// valid result, but we definitiely don't have a next result.
|
||||
aNext = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos, bool aIndexed) :
|
||||
FGPositioned::FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos) :
|
||||
mPosition(aPos),
|
||||
mType(ty),
|
||||
mIdent(aIdent)
|
||||
{
|
||||
}
|
||||
|
||||
void FGPositioned::init(bool aIndexed)
|
||||
{
|
||||
SGReferenced::get(this); // hold an owning ref, for the moment
|
||||
mCart = SGVec3d::fromGeod(mPosition);
|
||||
|
||||
if (aIndexed) {
|
||||
assert(ty != TAXIWAY && ty != PAVEMENT);
|
||||
assert(mType != TAXIWAY && mType != PAVEMENT);
|
||||
addToIndices(this);
|
||||
}
|
||||
}
|
||||
|
@ -687,13 +578,15 @@ FGPositioned::~FGPositioned()
|
|||
FGPositioned*
|
||||
FGPositioned::createUserWaypoint(const std::string& aIdent, const SGGeod& aPos)
|
||||
{
|
||||
return new FGPositioned(WAYPOINT, aIdent, aPos, true);
|
||||
FGPositioned* wpt = new FGPositioned(WAYPOINT, aIdent, aPos);
|
||||
wpt->init(true);
|
||||
return wpt;
|
||||
}
|
||||
|
||||
SGVec3d
|
||||
const SGVec3d&
|
||||
FGPositioned::cart() const
|
||||
{
|
||||
return SGVec3d::fromGeod(mPosition);
|
||||
return mCart;
|
||||
}
|
||||
|
||||
FGPositioned::Type FGPositioned::typeFromName(const std::string& aName)
|
||||
|
@ -770,7 +663,13 @@ const char* FGPositioned::nameForType(Type aTy)
|
|||
FGPositionedRef
|
||||
FGPositioned::findClosestWithIdent(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter)
|
||||
{
|
||||
return namedFindClosest(global_identIndex, aIdent, aPos, aFilter);
|
||||
FGPositioned::List r(findAll(global_identIndex, aIdent, aFilter));
|
||||
if (r.empty()) {
|
||||
return FGPositionedRef();
|
||||
}
|
||||
|
||||
sortByRange(r, aPos);
|
||||
return r.front();
|
||||
}
|
||||
|
||||
FGPositioned::List
|
||||
|
@ -783,15 +682,15 @@ FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilt
|
|||
}
|
||||
|
||||
FGPositioned::List
|
||||
FGPositioned::findAllWithIdentSortedByRange(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter)
|
||||
FGPositioned::findAllWithIdent(const std::string& aIdent, Filter* aFilter)
|
||||
{
|
||||
return findAllSortedByRange(global_identIndex, aIdent, aPos, aFilter);
|
||||
return findAll(global_identIndex, aIdent, aFilter);
|
||||
}
|
||||
|
||||
FGPositioned::List
|
||||
FGPositioned::findAllWithNameSortedByRange(const std::string& aName, const SGGeod& aPos, Filter* aFilter)
|
||||
FGPositioned::findAllWithName(const std::string& aName, Filter* aFilter)
|
||||
{
|
||||
return findAllSortedByRange(global_nameIndex, aName, aPos, aFilter);
|
||||
return findAll(global_nameIndex, aName, aFilter);
|
||||
}
|
||||
|
||||
FGPositionedRef
|
||||
|
@ -861,115 +760,24 @@ FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId
|
|||
return NULL; // Reached the end of the valid sequence with no match.
|
||||
}
|
||||
|
||||
FGPositionedRef
|
||||
FGPositioned::findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset, bool& aNext)
|
||||
void
|
||||
FGPositioned::sortByRange(List& aResult, const SGGeod& aPos)
|
||||
{
|
||||
return findWithPartial(global_identIndex, aId, aFilter, aOffset, aNext);
|
||||
}
|
||||
|
||||
|
||||
FGPositionedRef
|
||||
FGPositioned::findWithPartialName(const std::string& aName, Filter* aFilter, int aOffset, bool& aNext)
|
||||
{
|
||||
return findWithPartial(global_nameIndex, aName, aFilter, aOffset, aNext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper filter which proxies to an inner filter, but also ensures the
|
||||
* ident starts with supplied partial ident.
|
||||
*/
|
||||
class PartialIdentFilter : public FGPositioned::Filter
|
||||
{
|
||||
public:
|
||||
PartialIdentFilter(const std::string& ident, FGPositioned::Filter* filter) :
|
||||
_inner(filter)
|
||||
{
|
||||
_ident = boost::to_upper_copy(ident);
|
||||
SGVec3d cartPos(SGVec3d::fromGeod(aPos));
|
||||
// computer ordering values
|
||||
Octree::FindNearestResults r;
|
||||
List::iterator it = aResult.begin(), lend = aResult.end();
|
||||
for (; it != lend; ++it) {
|
||||
double d2 = distSqr((*it)->cart(), cartPos);
|
||||
r.push_back(Octree::OrderedPositioned(*it, d2));
|
||||
}
|
||||
|
||||
virtual bool pass(FGPositioned* aPos) const
|
||||
{
|
||||
if (!_inner->pass(aPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (boost::algorithm::starts_with(aPos->ident(), _ident));
|
||||
}
|
||||
|
||||
virtual FGPositioned::Type minType() const
|
||||
{ return _inner->minType(); }
|
||||
|
||||
virtual FGPositioned::Type maxType() const
|
||||
{ return _inner->maxType(); }
|
||||
|
||||
private:
|
||||
std::string _ident;
|
||||
FGPositioned::Filter* _inner;
|
||||
};
|
||||
|
||||
static FGPositionedRef
|
||||
findClosestWithPartial(const SGGeod& aPos, FGPositioned::Filter* aFilter, int aOffset, bool& aNext)
|
||||
{
|
||||
// why aOffset +2 ? at offset=3, we want the fourth search result, but also
|
||||
// to know if the fifth result exists (to set aNext flag for iterative APIs)
|
||||
FGPositioned::List matches;
|
||||
Octree::findNearestN(SGVec3d::fromGeod(aPos), aOffset + 2, 1000 * SG_NM_TO_METER, aFilter, matches);
|
||||
// sort
|
||||
std::sort(r.begin(), r.end());
|
||||
|
||||
if ((int) matches.size() <= aOffset) {
|
||||
SG_LOG(SG_GENERAL, SG_INFO, "findClosestWithPartial, couldn't match enough with prefix");
|
||||
aNext = false;
|
||||
return NULL; // couldn't find a match within the cutoff distance
|
||||
// convert to a plain list
|
||||
unsigned int count = aResult.size();
|
||||
for (unsigned int i=0; i<count; ++i) {
|
||||
aResult[i] = r[i].get();
|
||||
}
|
||||
|
||||
aNext = ((int) matches.size() >= (aOffset + 2));
|
||||
return matches[aOffset];
|
||||
|
||||
}
|
||||
|
||||
FGPositionedRef
|
||||
FGPositioned::findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset, bool& aNext)
|
||||
{
|
||||
PartialIdentFilter pf(aId, aFilter);
|
||||
return findClosestWithPartial(aPos, &pf, aOffset, aNext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper filter which proxies to an inner filter, but also ensures the
|
||||
* name starts with supplied partial name.
|
||||
*/
|
||||
class PartialNameFilter : public FGPositioned::Filter
|
||||
{
|
||||
public:
|
||||
PartialNameFilter(const std::string& nm, FGPositioned::Filter* filter) :
|
||||
_inner(filter)
|
||||
{
|
||||
_name = nm;
|
||||
}
|
||||
|
||||
virtual bool pass(FGPositioned* aPos) const
|
||||
{
|
||||
if (!_inner->pass(aPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (boost::algorithm::istarts_with(aPos->name(), _name));
|
||||
}
|
||||
|
||||
virtual FGPositioned::Type minType() const
|
||||
{ return _inner->minType(); }
|
||||
|
||||
virtual FGPositioned::Type maxType() const
|
||||
{ return _inner->maxType(); }
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
FGPositioned::Filter* _inner;
|
||||
};
|
||||
|
||||
FGPositionedRef
|
||||
FGPositioned::findClosestWithPartialName(const SGGeod& aPos, const std::string& aName, Filter* aFilter, int aOffset, bool& aNext)
|
||||
{
|
||||
PartialNameFilter pf(aName, aFilter);
|
||||
return findClosestWithPartial(aPos, &pf, aOffset, aNext);
|
||||
}
|
||||
|
||||
|
|
|
@ -84,9 +84,9 @@ public:
|
|||
{ return mPosition; }
|
||||
|
||||
/**
|
||||
* Compute the cartesian position associated with this object
|
||||
* The cartesian position associated with this object
|
||||
*/
|
||||
SGVec3d cart() const;
|
||||
const SGVec3d& cart() const;
|
||||
|
||||
double latitude() const
|
||||
{ return mPosition.getLatitudeDeg(); }
|
||||
|
@ -157,27 +157,20 @@ public:
|
|||
static FGPositionedRef findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter = NULL);
|
||||
|
||||
/**
|
||||
* As above, but searches using an offset index
|
||||
*/
|
||||
static FGPositionedRef findWithPartialId(const std::string& aId, Filter* aFilter, int aOffset, bool& aNext);
|
||||
|
||||
/**
|
||||
* As above, but search names instead of idents
|
||||
*/
|
||||
static FGPositionedRef findWithPartialName(const std::string& aName, Filter* aFilter, int aOffset, bool& aNext);
|
||||
|
||||
/**
|
||||
* Find all items with the specified ident, and return then sorted by
|
||||
* distance from a position
|
||||
*
|
||||
* Find all items with the specified ident
|
||||
* @param aFilter - optional filter on items
|
||||
*/
|
||||
static List findAllWithIdentSortedByRange(const std::string& aIdent, const SGGeod& aPos, Filter* aFilter = NULL);
|
||||
static List findAllWithIdent(const std::string& aIdent, Filter* aFilter = NULL);
|
||||
|
||||
/**
|
||||
* As above, but searches names instead of idents
|
||||
*/
|
||||
static List findAllWithNameSortedByRange(const std::string& aName, const SGGeod& aPos, Filter* aFilter = NULL);
|
||||
static List findAllWithName(const std::string& aName, Filter* aFilter = NULL);
|
||||
|
||||
/**
|
||||
* Sort an FGPositionedList by distance from a position
|
||||
*/
|
||||
static void sortByRange(List&, const SGGeod& aPos);
|
||||
|
||||
/**
|
||||
* Find the closest item to a position, which pass the specified filter
|
||||
|
@ -201,18 +194,6 @@ public:
|
|||
*/
|
||||
static List findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter = NULL);
|
||||
|
||||
/**
|
||||
* Find the closest match based on partial id (with an offset to allow selecting the n-th closest).
|
||||
* Cutoff distance is limited internally, to avoid making this very slow.
|
||||
*/
|
||||
static FGPositionedRef findClosestWithPartialId(const SGGeod& aPos, const std::string& aId, Filter* aFilter, int aOffset, bool& aNext);
|
||||
|
||||
/**
|
||||
* As above, but matches on name
|
||||
*/
|
||||
static FGPositionedRef findClosestWithPartialName(const SGGeod& aPos, const std::string& aName, Filter* aFilter, int aOffset, bool& aNext);
|
||||
|
||||
|
||||
/**
|
||||
* Map a candidate type string to a real type. Returns INVALID if the string
|
||||
* does not correspond to a defined type.
|
||||
|
@ -227,12 +208,15 @@ public:
|
|||
static FGPositioned* createUserWaypoint(const std::string& aIdent, const SGGeod& aPos);
|
||||
protected:
|
||||
|
||||
FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos, bool aIndex = true);
|
||||
FGPositioned(Type ty, const std::string& aIdent, const SGGeod& aPos);
|
||||
|
||||
void init(bool aIndexed);
|
||||
|
||||
// can't be const right now, navrecord at least needs to fix up the position
|
||||
// after navaids are parsed
|
||||
SGGeod mPosition;
|
||||
|
||||
SGVec3d mCart; // once mPosition is const, this can be const too
|
||||
const Type mType;
|
||||
const std::string mIdent;
|
||||
};
|
||||
|
|
|
@ -374,6 +374,19 @@ static naRef f_directory(naContext c, naRef me, int argc, naRef* args)
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a data path, resolve it in FG_ROOT or an FG_AIRCRFT directory
|
||||
*/
|
||||
static naRef f_resolveDataPath(naContext c, naRef me, int argc, naRef* args)
|
||||
{
|
||||
if(argc != 1 || !naIsString(args[0]))
|
||||
naRuntimeError(c, "bad arguments to resolveDataPath()");
|
||||
|
||||
SGPath p = globals->resolve_maybe_aircraft_path(naStr_data(args[0]));
|
||||
const char* pdata = p.c_str();
|
||||
return naStr_fromdata(naNewString(c), const_cast<char*>(pdata), strlen(pdata));
|
||||
}
|
||||
|
||||
// Parse XML file.
|
||||
// parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
|
||||
//
|
||||
|
@ -635,6 +648,7 @@ static struct { const char* name; naCFunction func; } funcs[] = {
|
|||
{ "srand", f_srand },
|
||||
{ "abort", f_abort },
|
||||
{ "directory", f_directory },
|
||||
{ "resolvepath", f_resolveDataPath },
|
||||
{ "parsexml", f_parsexml },
|
||||
{ "systime", f_systime },
|
||||
{ "carttogeod", f_carttogeod },
|
||||
|
|
Loading…
Reference in a new issue