// TODO - At hot 'n high airports this may be 500ft AGL though - need to make this a variable.
if((_pos.getElevationM() - rwy.threshold_pos.getElevationM()) * SG_METER_TO_FEET > 700) {
double cc = 0.0;
- if(tower && tower->GetCrosswindConstraint(cc)) {
+ if(_controlled && tower->GetCrosswindConstraint(cc)) {
if(orthopos.y() > cc) {
//cout << "Turning to crosswind, distance from threshold = " << orthopos.y() << '\n';
leg = TURN1;
// turn 1000m out for now, taking other traffic into accout
if(fabs(orthopos.x()) > 900) {
double dd = 0.0;
- if(tower && tower->GetDownwindConstraint(dd)) {
+ if(_controlled && tower->GetDownwindConstraint(dd)) {
if(fabs(orthopos.x()) > fabs(dd)) {
//cout << "Turning to downwind, distance from centerline = " << fabs(orthopos.x()) << '\n';
leg = TURN2;
// For now we're assuming that we aim to follow the same glidepath regardless of wind.
double d1;
double d2;
- CalculateSoD(((tower && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), ((tower && tower->GetDownwindConstraint(d2)) ? d2 : 1000.0 * patternDirection), (patternDirection ? true : false));
+ CalculateSoD(((_controlled && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), ((_controlled && tower->GetDownwindConstraint(d2)) ? d2 : 1000.0 * patternDirection), (patternDirection ? true : false));
if(SoD.leg == DOWNWIND) {
descending = (orthopos.y() < SoD.y ? true : false);
}
if(orthopos.y() < -1000.0 + turn_radius) {
//if(orthopos.y() < -980) {
double bb = 0.0;
- if(tower && tower->GetBaseConstraint(bb)) {
+ if(_controlled && tower->GetBaseConstraint(bb)) {
if(fabs(orthopos.y()) > fabs(bb)) {
//cout << "Turning to base, distance from threshold = " << fabs(orthopos.y()) << '\n';
leg = TURN3;
double d1;
// Make downwind leg position artifically large to avoid any chance of SoD being returned as
// on downwind when we are already on base.
- CalculateSoD(((tower && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), (10000.0 * patternDirection), (patternDirection ? true : false));
+ CalculateSoD(((_controlled && tower->GetBaseConstraint(d1)) ? d1 : -1000.0), (10000.0 * patternDirection), (patternDirection ? true : false));
if(SoD.leg == BASE) {
descending = (fabs(orthopos.y()) < fabs(SoD.y) ? true : false);
}
double axx = gxx - wxx; // Plane in-air velocity x component
double ayy = gyy - wyy; // Plane in-air velocity y component
// Now we want the angle between gxx and axx (which is the crab)
- double maga = sqrt(axx*axx + ayy*ayy);
- double magg = sqrt(gxx*gxx + gyy*gyy);
- crab = acos((axx*gxx + ayy*gyy) / (maga * magg));
- // At this point this works except we're getting the modulus of the angle
+ crab = atan2(ayy - gyy, axx - gxx) * DCL_RADIANS_TO_DEGREES;
//cout << "crab = " << crab << '\n';
-
- // Make sure both headings are in the 0->360 circle in order to get sane differences
- dclBoundHeading(wind_from);
- dclBoundHeading(track);
- if(track > wind_from) {
- if((track - wind_from) <= 180) {
- crab *= -1.0;
- }
- } else {
- if((wind_from - track) >= 180) {
- crab *= -1.0;
- }
- }
} else { // on the ground - crab dosen't apply
crab = 0.0;
}
//cout << "X " << orthopos.x() << " Y " << orthopos.y() << " SLOPE " << slope << " elev " << _pos.elev() * SG_METER_TO_FEET << '\n';
_hdg = track + crab;
+ dclBoundHeading(_hdg);
dist = vel * 0.514444 * dt;
_pos = dclUpdatePosition(_pos, track, slope, dist);
}
// airport name + "traffic" + airplane callsign + pattern direction + pattern leg + rwy + ?
string trns;
int code = 0;
- const string& apt_name = tower ? tower->get_name() : airportID;
+ const string& apt_name = _controlled ? tower->get_name() : airportID;
trns += apt_name;
trns += " Traffic ";
TowerPlaneRec::TowerPlaneRec() :
planePtr(NULL),
+ eta(0),
+ dist_out(0),
clearedToLand(false),
clearedToLineUp(false),
clearedToTakeOff(false),
TowerPlaneRec::TowerPlaneRec(const PlaneRec& p) :
planePtr(NULL),
+ eta(0),
+ dist_out(0),
clearedToLand(false),
clearedToLineUp(false),
clearedToTakeOff(false),
TowerPlaneRec::TowerPlaneRec(const SGGeod& pt) :
planePtr(NULL),
+ eta(0),
+ dist_out(0),
clearedToLand(false),
clearedToLineUp(false),
clearedToTakeOff(false),
TowerPlaneRec::TowerPlaneRec(const PlaneRec& p, const SGGeod& pt) :
planePtr(NULL),
+ eta(0),
+ dist_out(0),
clearedToLand(false),
clearedToLineUp(false),
clearedToTakeOff(false),
t->plane.type = GA_SINGLE; // FIXME - Another assumption!
t->plane.callsign = usercall;
+ CalcETA(t);
t->vfrArrivalReported = true;
responseReqd = true;
void FGRouteMgr::setDepartureICAO(const char* aIdent)
{
- _departure = FGAirport::findByIdent(aIdent);
+ if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
+ _departure = NULL;
+ } else {
+ _departure = FGAirport::findByIdent(aIdent);
+ }
}
const char* FGRouteMgr::getDestinationICAO() const
void FGRouteMgr::setDestinationICAO(const char* aIdent)
{
- _destination = FGAirport::findByIdent(aIdent);
+ if ((aIdent == NULL) || (strlen(aIdent) < 4)) {
+ _destination = NULL;
+ } else {
+ _destination = FGAirport::findByIdent(aIdent);
+ }
}
_liftRatio = 1;
_cruiseAoA = 0;
_tailIncidence = 0;
+
+ _failureMsg = 0;
}
Airplane::~Airplane()
float fwd[3];
Math::sub3(f->front, f->back, fwd);
float len = Math::mag3(fwd);
+ if (len == 0) {
+ _failureMsg = "Zero length fuselage";
+ return 0;
+ }
float wid = f->width;
int segs = (int)Math::ceil(len/wid);
float segWgt = len*wid/segs;
_model.setGroundEffect(gepos, gespan, 0.15f);
}
+ // solve function below resets failure message
+ // so check if we have any problems and abort here
+ if (_failureMsg) return;
+
solveGear();
if(_wing && _tail) solve();
else
}
}
-static void tieSGGeod(SGPropertyNode* aNode, SGGeod& aRef,
- const char* lonStr, const char* latStr, const char* altStr)
-{
- aNode->tie(lonStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg));
- aNode->tie(latStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg));
-
- if (altStr) {
- aNode->tie(altStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getElevationFt, &SGGeod::setElevationFt));
- }
-}
-
-static void tieSGGeodReadOnly(SGPropertyNode* aNode, SGGeod& aRef,
- const char* lonStr, const char* latStr, const char* altStr)
-{
- aNode->tie(lonStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLongitudeDeg, NULL));
- aNode->tie(latStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLatitudeDeg, NULL));
-
- if (altStr) {
- aNode->tie(altStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getElevationFt, NULL));
- }
-}
-
static const char* makeTTWString(double TTW)
{
if ((TTW <= 0.0) || (TTW >= 356400.5)) { // 99 hours
_extCourseSource = fgGetNode("/instrumentation/nav[0]/radials/selected-deg", true);
}
-void GPS::Config::init(SGPropertyNode* aCfgNode)
+void GPS::Config::bind(GPS* aOwner, SGPropertyNode* aCfg)
{
- aCfgNode->tie("turn-rate-deg-sec", SGRawValuePointer<double>(&_turnRate));
- aCfgNode->tie("turn-anticipation", SGRawValuePointer<bool>(&_enableTurnAnticipation));
- aCfgNode->tie("wpt-alert-time", SGRawValuePointer<double>(&_waypointAlertTime));
- aCfgNode->tie("tune-nav-radio-to-ref-vor", SGRawValuePointer<bool>(&_tuneRadio1ToRefVor));
- aCfgNode->tie("min-runway-length-ft", SGRawValuePointer<double>(&_minRunwayLengthFt));
- aCfgNode->tie("hard-surface-runways-only", SGRawValuePointer<bool>(&_requireHardSurface));
+ aOwner->tie(aCfg, "turn-rate-deg-sec", SGRawValuePointer<double>(&_turnRate));
+
+ aOwner->tie(aCfg, "turn-anticipation", SGRawValuePointer<bool>(&_enableTurnAnticipation));
+ aOwner->tie(aCfg, "wpt-alert-time", SGRawValuePointer<double>(&_waypointAlertTime));
+ aOwner->tie(aCfg, "tune-nav-radio-to-ref-vor", SGRawValuePointer<bool>(&_tuneRadio1ToRefVor));
+ aOwner->tie(aCfg, "min-runway-length-ft", SGRawValuePointer<double>(&_minRunwayLengthFt));
+ aOwner->tie(aCfg, "hard-surface-runways-only", SGRawValuePointer<bool>(&_requireHardSurface));
- aCfgNode->tie("course-source", SGRawValueMethods<GPS::Config, const char*>
+ aOwner->tie(aCfg, "course-source", SGRawValueMethods<GPS::Config, const char*>
(*this, &GPS::Config::getCourseSource, &GPS::Config::setCourseSource));
- aCfgNode->tie("cdi-max-deflection-nm", SGRawValuePointer<double>(&_cdiMaxDeflectionNm));
- aCfgNode->tie("drive-autopilot", SGRawValuePointer<bool>(&_driveAutopilot));
+ aOwner->tie(aCfg, "cdi-max-deflection-nm", SGRawValuePointer<double>(&_cdiMaxDeflectionNm));
+ aOwner->tie(aCfg, "drive-autopilot", SGRawValuePointer<bool>(&_driveAutopilot));
}
const char*
_anticipateTurn(false),
_inTurn(false)
{
+ string branch = "/instrumentation/" + _name;
+ _gpsNode = fgGetNode(branch.c_str(), _num, true );
+ _scratchNode = _gpsNode->getChild("scratch", 0, true);
}
GPS::~GPS ()
void
GPS::init ()
{
- _routeMgr = (FGRouteMgr*) globals->get_subsystem("route-manager");
- assert(_routeMgr);
+ _routeMgr = (FGRouteMgr*) globals->get_subsystem("route-manager");
+ assert(_routeMgr);
- string branch;
- branch = "/instrumentation/" + _name;
-
- SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true );
- _config.init(node->getChild("config", 0, true));
-
_position.init("/position/longitude-deg", "/position/latitude-deg", "/position/altitude-ft");
_magvar_node = fgGetNode("/environment/magnetic-variation-deg", true);
- _serviceable_node = node->getChild("serviceable", 0, true);
+ _serviceable_node = _gpsNode->getChild("serviceable", 0, true);
_serviceable_node->setBoolValue(true);
_electrical_node = fgGetNode("/systems/electrical/outputs/gps", true);
// basic GPS outputs
- node->tie("selected-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getSelectedCourse, NULL));
-
- _raim_node = node->getChild("raim", 0, true);
-
- tieSGGeodReadOnly(node, _indicated_pos, "indicated-longitude-deg",
- "indicated-latitude-deg", "indicated-altitude-ft");
-
- node->tie("indicated-vertical-speed", SGRawValueMethods<GPS, double>
- (*this, &GPS::getVerticalSpeed, NULL));
- node->tie("indicated-track-true-deg", SGRawValueMethods<GPS, double>
- (*this, &GPS::getTrueTrack, NULL));
- node->tie("indicated-track-magnetic-deg", SGRawValueMethods<GPS, double>
- (*this, &GPS::getMagTrack, NULL));
- node->tie("indicated-ground-speed-kt", SGRawValueMethods<GPS, double>
- (*this, &GPS::getGroundspeedKts, NULL));
-
- _odometer_node = node->getChild("odometer", 0, true);
- _trip_odometer_node = node->getChild("trip-odometer", 0, true);
- _true_bug_error_node = node->getChild("true-bug-error-deg", 0, true);
- _magnetic_bug_error_node = node->getChild("magnetic-bug-error-deg", 0, true);
-
-// command system
- node->tie("mode", SGRawValueMethods<GPS, const char*>(*this, &GPS::getMode, NULL));
- node->tie("command", SGRawValueMethods<GPS, const char*>(*this, &GPS::getCommand, &GPS::setCommand));
-
- _scratchNode = node->getChild("scratch", 0, true);
- tieSGGeod(_scratchNode, _scratchPos, "longitude-deg", "latitude-deg", "altitude-ft");
- _scratchNode->tie("valid", SGRawValueMethods<GPS, bool>(*this, &GPS::getScratchValid, NULL));
- _scratchNode->tie("distance-nm", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchDistance, NULL));
- _scratchNode->tie("true-bearing-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchTrueBearing, NULL));
- _scratchNode->tie("mag-bearing-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchMagBearing, NULL));
- _scratchNode->tie("has-next", SGRawValueMethods<GPS, bool>(*this, &GPS::getScratchHasNext, NULL));
- _scratchValid = false;
-
-// waypoint data (including various historical things)
- SGPropertyNode *wp_node = node->getChild("wp", 0, true);
- SGPropertyNode *wp0_node = wp_node->getChild("wp", 0, true);
+ _raim_node = _gpsNode->getChild("raim", 0, true);
+ _odometer_node = _gpsNode->getChild("odometer", 0, true);
+ _trip_odometer_node = _gpsNode->getChild("trip-odometer", 0, true);
+ _true_bug_error_node = _gpsNode->getChild("true-bug-error-deg", 0, true);
+ _magnetic_bug_error_node = _gpsNode->getChild("magnetic-bug-error-deg", 0, true);
+
+// waypoints
+ SGPropertyNode *wp_node = _gpsNode->getChild("wp", 0, true);
SGPropertyNode *wp1_node = wp_node->getChild("wp", 1, true);
- tieSGGeodReadOnly(wp0_node, _wp0_position, "longitude-deg", "latitude-deg", "altitude-ft");
- wp0_node->tie("ID", SGRawValueMethods<GPS, const char*>
- (*this, &GPS::getWP0Ident, NULL));
- wp0_node->tie("name", SGRawValueMethods<GPS, const char*>
- (*this, &GPS::getWP0Name, NULL));
-
- tieSGGeodReadOnly(wp1_node, _wp1_position, "longitude-deg", "latitude-deg", "altitude-ft");
- wp1_node->tie("ID", SGRawValueMethods<GPS, const char*>
- (*this, &GPS::getWP1Ident, NULL));
- wp1_node->tie("name", SGRawValueMethods<GPS, const char*>
- (*this, &GPS::getWP1Name, NULL));
-
// for compatability, alias selected course down to wp/wp[1]/desired-course-deg
SGPropertyNode* wp1Crs = wp1_node->getChild("desired-course-deg", 0, true);
- wp1Crs->alias(node->getChild("selected-course-deg"));
+ wp1Crs->alias(_gpsNode->getChild("selected-course-deg"));
// _true_wp1_bearing_error_node =
// wp1_node->getChild("true-bearing-error-deg", 0, true);
// _magnetic_wp1_bearing_error_node =
// wp1_node->getChild("magnetic-bearing-error-deg", 0, true);
- wp1_node->tie("distance-nm", SGRawValueMethods<GPS, double>
- (*this, &GPS::getWP1Distance, NULL));
- wp1_node->tie("bearing-true-deg", SGRawValueMethods<GPS, double>
- (*this, &GPS::getWP1Bearing, NULL));
- wp1_node->tie("bearing-mag-deg", SGRawValueMethods<GPS, double>
- (*this, &GPS::getWP1MagBearing, NULL));
- wp1_node->tie("TTW-sec", SGRawValueMethods<GPS, double>
- (*this, &GPS::getWP1TTW, NULL));
- wp1_node->tie("TTW", SGRawValueMethods<GPS, const char*>
- (*this, &GPS::getWP1TTWString, NULL));
-
- wp1_node->tie("course-deviation-deg", SGRawValueMethods<GPS, double>
- (*this, &GPS::getWP1CourseDeviation, NULL));
- wp1_node->tie("course-error-nm", SGRawValueMethods<GPS, double>
- (*this, &GPS::getWP1CourseErrorNm, NULL));
- wp1_node->tie("to-flag", SGRawValueMethods<GPS, bool>
- (*this, &GPS::getWP1ToFlag, NULL));
- wp1_node->tie("from-flag", SGRawValueMethods<GPS, bool>
- (*this, &GPS::getWP1FromFlag, NULL));
-
- _tracking_bug_node = node->getChild("tracking-bug", 0, true);
+ _tracking_bug_node = _gpsNode->getChild("tracking-bug", 0, true);
-// leg properties (only valid in DTO/LEG modes, not OBS)
- wp_node->tie("leg-distance-nm", SGRawValueMethods<GPS, double>(*this, &GPS::getLegDistance, NULL));
- wp_node->tie("leg-true-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getLegCourse, NULL));
- wp_node->tie("leg-mag-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getLegMagCourse, NULL));
- wp_node->tie("alt-dist-ratio", SGRawValueMethods<GPS, double>(*this, &GPS::getAltDistanceRatio, NULL));
-
// reference navid
- SGPropertyNode_ptr ref_navaid = node->getChild("ref-navaid", 0, true);
+ SGPropertyNode_ptr ref_navaid = _gpsNode->getChild("ref-navaid", 0, true);
_ref_navaid_id_node = ref_navaid->getChild("id", 0, true);
_ref_navaid_name_node = ref_navaid->getChild("name", 0, true);
_ref_navaid_bearing_node = ref_navaid->getChild("bearing-deg", 0, true);
// route properties
// should these move to the route manager?
- _routeDistanceNm = node->getChild("route-distance-nm", 0, true);
- _routeETE = node->getChild("ETE", 0, true);
+ _routeDistanceNm = _gpsNode->getChild("route-distance-nm", 0, true);
+ _routeETE = _gpsNode->getChild("ETE", 0, true);
_routeEditedSignal = fgGetNode("/autopilot/route-manager/signals/edited", true);
_routeFinishedSignal = fgGetNode("/autopilot/route-manager/signals/finished", true);
_routeFinishedSignal->addChangeListener(_listener);
// navradio slaving properties
- node->tie("cdi-deflection", SGRawValueMethods<GPS,double>
- (*this, &GPS::getCDIDeflection));
-
- SGPropertyNode* toFlag = node->getChild("to-flag", 0, true);
+ SGPropertyNode* toFlag = _gpsNode->getChild("to-flag", 0, true);
toFlag->alias(wp1_node->getChild("to-flag"));
- SGPropertyNode* fromFlag = node->getChild("from-flag", 0, true);
+ SGPropertyNode* fromFlag = _gpsNode->getChild("from-flag", 0, true);
fromFlag->alias(wp1_node->getChild("from-flag"));
-
// autopilot drive properties
_apTrueHeading = fgGetNode("/autopilot/settings/true-heading-deg",true);
_apTargetAltitudeFt = fgGetNode("/autopilot/settings/target-altitude-ft", true);
}
// last thing, add the deprecated prop watcher
- new DeprecatedPropListener(node);
+ new DeprecatedPropListener(_gpsNode);
clearOutput();
}
+void
+GPS::bind()
+{
+ _config.bind(this, _gpsNode->getChild("config", 0, true));
+// basic GPS outputs
+ tie(_gpsNode, "selected-course-deg", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getSelectedCourse, NULL));
+
+
+ tieSGGeodReadOnly(_gpsNode, _indicated_pos, "indicated-longitude-deg",
+ "indicated-latitude-deg", "indicated-altitude-ft");
+
+ tie(_gpsNode, "indicated-vertical-speed", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getVerticalSpeed, NULL));
+ tie(_gpsNode, "indicated-track-true-deg", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getTrueTrack, NULL));
+ tie(_gpsNode, "indicated-track-magnetic-deg", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getMagTrack, NULL));
+ tie(_gpsNode, "indicated-ground-speed-kt", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getGroundspeedKts, NULL));
+
+// command system
+ tie(_gpsNode, "mode", SGRawValueMethods<GPS, const char*>(*this, &GPS::getMode, NULL));
+ tie(_gpsNode, "command", SGRawValueMethods<GPS, const char*>(*this, &GPS::getCommand, &GPS::setCommand));
+
+ tieSGGeod(_scratchNode, _scratchPos, "longitude-deg", "latitude-deg", "altitude-ft");
+ tie(_scratchNode, "valid", SGRawValueMethods<GPS, bool>(*this, &GPS::getScratchValid, NULL));
+ tie(_scratchNode, "distance-nm", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchDistance, NULL));
+ tie(_scratchNode, "true-bearing-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchTrueBearing, NULL));
+ tie(_scratchNode, "mag-bearing-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getScratchMagBearing, NULL));
+ tie(_scratchNode, "has-next", SGRawValueMethods<GPS, bool>(*this, &GPS::getScratchHasNext, NULL));
+ _scratchValid = false;
+
+// waypoint data (including various historical things)
+ SGPropertyNode *wp_node = _gpsNode->getChild("wp", 0, true);
+ SGPropertyNode *wp0_node = wp_node->getChild("wp", 0, true);
+ SGPropertyNode *wp1_node = wp_node->getChild("wp", 1, true);
+
+ tieSGGeodReadOnly(wp0_node, _wp0_position, "longitude-deg", "latitude-deg", "altitude-ft");
+ tie(wp0_node, "ID", SGRawValueMethods<GPS, const char*>
+ (*this, &GPS::getWP0Ident, NULL));
+ tie(wp0_node, "name", SGRawValueMethods<GPS, const char*>
+ (*this, &GPS::getWP0Name, NULL));
+
+ tieSGGeodReadOnly(wp1_node, _wp1_position, "longitude-deg", "latitude-deg", "altitude-ft");
+ tie(wp1_node, "ID", SGRawValueMethods<GPS, const char*>
+ (*this, &GPS::getWP1Ident, NULL));
+ tie(wp1_node, "name", SGRawValueMethods<GPS, const char*>
+ (*this, &GPS::getWP1Name, NULL));
+
+ tie(wp1_node, "distance-nm", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getWP1Distance, NULL));
+ tie(wp1_node, "bearing-true-deg", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getWP1Bearing, NULL));
+ tie(wp1_node, "bearing-mag-deg", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getWP1MagBearing, NULL));
+ tie(wp1_node, "TTW-sec", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getWP1TTW, NULL));
+ tie(wp1_node, "TTW", SGRawValueMethods<GPS, const char*>
+ (*this, &GPS::getWP1TTWString, NULL));
+
+ tie(wp1_node, "course-deviation-deg", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getWP1CourseDeviation, NULL));
+ tie(wp1_node, "course-error-nm", SGRawValueMethods<GPS, double>
+ (*this, &GPS::getWP1CourseErrorNm, NULL));
+ tie(wp1_node, "to-flag", SGRawValueMethods<GPS, bool>
+ (*this, &GPS::getWP1ToFlag, NULL));
+ tie(wp1_node, "from-flag", SGRawValueMethods<GPS, bool>
+ (*this, &GPS::getWP1FromFlag, NULL));
+
+// leg properties (only valid in DTO/LEG modes, not OBS)
+ tie(wp_node, "leg-distance-nm", SGRawValueMethods<GPS, double>(*this, &GPS::getLegDistance, NULL));
+ tie(wp_node, "leg-true-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getLegCourse, NULL));
+ tie(wp_node, "leg-mag-course-deg", SGRawValueMethods<GPS, double>(*this, &GPS::getLegMagCourse, NULL));
+ tie(wp_node, "alt-dist-ratio", SGRawValueMethods<GPS, double>(*this, &GPS::getAltDistanceRatio, NULL));
+
+// navradio slaving properties
+ tie(_gpsNode, "cdi-deflection", SGRawValueMethods<GPS,double>
+ (*this, &GPS::getCDIDeflection));
+}
+
+void
+GPS::unbind()
+{
+ for (unsigned int t=0; t<_tiedNodes.size(); ++t) {
+ _tiedNodes[t]->untie();
+ }
+ _tiedNodes.clear();
+}
+
void
GPS::clearOutput()
{
return;
}
- // FIXME: we want to set desired track, not heading, here
- _apTrueHeading->setDoubleValue(getWP1Bearing());
+ // compatability feature - allow the route-manager / GPS to drive the
+ // generic autopilot heading hold *in leg mode only*
+ if (_mode == "leg") {
+ // FIXME: we want to set desired track, not heading, here
+ _apTrueHeading->setDoubleValue(getWP1Bearing());
+ }
}
void GPS::wp1Changed()
int index = _scratchNode->getIntValue("index", -9999);
clearScratch();
- if (index == -9999) { // no index supplied, use current wp
+ if ((index < 0) || (index >= _routeMgr->size())) { // no index supplied, use current wp
index = _routeMgr->currentWaypoint();
}
int limitCount = _scratchNode->getIntValue("max-results", 1);
double cutoffDistance = _scratchNode->getDoubleValue("cutoff-nm", 400.0);
- clearScratch(); // clear now, regardless of whether we find a match or not
+ SGGeod searchPos = _indicated_pos;
+ if (isScratchPositionValid()) {
+ searchPos = _scratchPos;
+ }
+ clearScratch(); // clear now, regardless of whether we find a match or not
+
_searchResults =
- FGPositioned::findClosestN(_indicated_pos, limitCount, cutoffDistance, f.get());
+ FGPositioned::findClosestN(searchPos, limitCount, cutoffDistance, f.get());
_searchResultsCached = true;
_searchResultIndex = 0;
_searchIsRoute = false;
_routeMgr->pop_waypoint(aIndex);
}
+void GPS::tieSGGeod(SGPropertyNode* aNode, SGGeod& aRef,
+ const char* lonStr, const char* latStr, const char* altStr)
+{
+ tie(aNode, lonStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg));
+ tie(aNode, latStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg));
+
+ if (altStr) {
+ tie(aNode, altStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getElevationFt, &SGGeod::setElevationFt));
+ }
+}
+
+void GPS::tieSGGeodReadOnly(SGPropertyNode* aNode, SGGeod& aRef,
+ const char* lonStr, const char* latStr, const char* altStr)
+{
+ tie(aNode, lonStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLongitudeDeg, NULL));
+ tie(aNode, latStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getLatitudeDeg, NULL));
+
+ if (altStr) {
+ tie(aNode, altStr, SGRawValueMethods<SGGeod, double>(aRef, &SGGeod::getElevationFt, NULL));
+ }
+}
+
// end of gps.cxx
virtual void init ();
virtual void update (double delta_time_sec);
-
+
+ virtual void bind();
+ virtual void unbind();
private:
friend class GPSListener;
friend class SearchFilter;
{
public:
Config();
-
- void init(SGPropertyNode*);
+
+ void bind(GPS* aOwner, SGPropertyNode* aCfg);
bool turnAnticipationEnabled() const
{ return _enableTurnAnticipation; }
// true-bearing-error and mag-bearing-error
+
+ /**
+ * Tied-properties helper, record nodes which are tied for easy un-tie-ing
+ */
+ template <typename T>
+ void tie(SGPropertyNode* aNode, const char* aRelPath, const SGRawValue<T>& aRawValue)
+ {
+ SGPropertyNode* nd = aNode->getNode(aRelPath, true);
+ _tiedNodes.push_back(nd);
+ nd->tie(aRawValue);
+ }
+
+ /// helper, tie the lat/lon/elev of a SGGeod to the named children of aNode
+ void tieSGGeod(SGPropertyNode* aNode, SGGeod& aRef,
+ const char* lonStr, const char* latStr, const char* altStr);
-
+ /// helper, tie a SGGeod to proeprties, but read-only
+ void tieSGGeodReadOnly(SGPropertyNode* aNode, SGGeod& aRef,
+ const char* lonStr, const char* latStr, const char* altStr);
+
// members
+ SGPropertyNode_ptr _gpsNode;
SGPropertyNode_ptr _magvar_node;
SGPropertyNode_ptr _serviceable_node;
SGPropertyNode_ptr _electrical_node;
SGPropertyNode_ptr _apTrueHeading;
SGPropertyNode_ptr _apTargetAltitudeFt;
SGPropertyNode_ptr _apAltitudeLock;
+
+ std::vector<SGPropertyNode*> _tiedNodes;
};
lon_node(fgGetNode("/position/longitude-deg", true)),
lat_node(fgGetNode("/position/latitude-deg", true)),
alt_node(fgGetNode("/position/altitude-ft", true)),
- is_valid_node(NULL),
- power_btn_node(NULL),
- freq_node(NULL),
- alt_freq_node(NULL),
- sel_radial_node(NULL),
- vol_btn_node(NULL),
- ident_btn_node(NULL),
- audio_btn_node(NULL),
- backcourse_node(NULL),
- nav_serviceable_node(NULL),
- cdi_serviceable_node(NULL),
- gs_serviceable_node(NULL),
- tofrom_serviceable_node(NULL),
- dme_serviceable_node(NULL),
- fmt_freq_node(NULL),
- fmt_alt_freq_node(NULL),
- heading_node(NULL),
- radial_node(NULL),
- recip_radial_node(NULL),
- target_radial_true_node(NULL),
- target_auto_hdg_node(NULL),
- time_to_intercept(NULL),
- to_flag_node(NULL),
- from_flag_node(NULL),
- inrange_node(NULL),
- signal_quality_norm_node(NULL),
- cdi_deflection_node(NULL),
- cdi_deflection_norm_node(NULL),
- cdi_xtrack_error_node(NULL),
- cdi_xtrack_hdg_err_node(NULL),
- has_gs_node(NULL),
- loc_node(NULL),
- loc_dist_node(NULL),
- gs_deflection_node(NULL),
- gs_deflection_deg_node(NULL),
- gs_deflection_norm_node(NULL),
- gs_rate_of_climb_node(NULL),
- gs_dist_node(NULL),
- gs_inrange_node(NULL),
- nav_id_node(NULL),
- id_c1_node(NULL),
- id_c2_node(NULL),
- id_c3_node(NULL),
- id_c4_node(NULL),
- nav_slaved_to_gps_node(NULL),
- gps_cdi_deflection_node(NULL),
- gps_to_flag_node(NULL),
- gps_from_flag_node(NULL),
- gps_has_gs_node(NULL),
- gps_xtrack_error_nm_node(NULL),
play_count(0),
last_time(0),
target_radial(0.0),
_name(node->getStringValue("name", "nav")),
_num(node->getIntValue("number", 0)),
_time_before_search_sec(-1.0),
- _falseCoursesEnabled(true),
_sgr(NULL)
{
SGPath path( globals->get_fg_root() );
term_tbl = new SGInterpTable( term.str() );
low_tbl = new SGInterpTable( low.str() );
high_tbl = new SGInterpTable( high.str() );
+
+
+ string branch("/instrumentation/" + _name);
+ _radio_node = fgGetNode(branch.c_str(), _num, true);
}
morse.init();
- string branch;
- branch = "/instrumentation/" + _name;
-
- SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true );
-
+ SGPropertyNode* node = _radio_node.get();
bus_power_node =
fgGetNode(("/systems/electrical/outputs/" + _name).c_str(), true);
tofrom_serviceable_node = createServiceableProp(node, "to-from");
dme_serviceable_node = createServiceableProp(node, "dme");
- globals->get_props()->tie("sim/realism/false-radio-courses-enabled",
- SGRawValuePointer<bool>(&_falseCoursesEnabled));
-
+ falseCoursesEnabledNode =
+ fgGetNode("/sim/realism/false-radio-courses-enabled");
+ if (!falseCoursesEnabledNode) {
+ falseCoursesEnabledNode =
+ fgGetNode("/sim/realism/false-radio-courses-enabled", true);
+ falseCoursesEnabledNode->setBoolValue(true);
+ }
+
// frequencies
SGPropertyNode *subnode = node->getChild("frequencies", 0, true);
freq_node = subnode->getChild("selected-mhz", 0, true);
id_c3_node = node->getChild("nav-id_asc3", 0, true);
id_c4_node = node->getChild("nav-id_asc4", 0, true);
- node->tie("dme-in-range", SGRawValuePointer<bool>(&_dmeInRange));
-
// gps slaving support
nav_slaved_to_gps_node = node->getChild("slaved-to-gps", 0, true);
gps_cdi_deflection_node = fgGetNode("/instrumentation/gps/cdi-deflection", true);
void
FGNavRadio::bind ()
{
-
+ tie("dme-in-range", SGRawValuePointer<bool>(&_dmeInRange));
+ tie("operable", SGRawValueMethods<FGNavRadio, bool>(*this, &FGNavRadio::isOperable, NULL));
}
void
FGNavRadio::unbind ()
{
+ for (unsigned int t=0; t<_tiedNodes.size(); ++t) {
+ _tiedNodes[t]->untie();
+ }
+ _tiedNodes.clear();
}
if (power_btn_node->getBoolValue()
&& (bus_power_node->getDoubleValue() > 1.0)
&& nav_serviceable_node->getBoolValue() )
- {
+ {
+ _operable = true;
if (nav_slaved_to_gps_node->getBoolValue()) {
updateGPSSlaved();
} else {
from_flag_node->setBoolValue( false );
_dmeInRange = false;
+ _operable = false;
}
void FGNavRadio::updateReceiver(double dt)
SG_NORMALIZE_RANGE(r, -180.0, 180.0);
if ( is_loc ) {
- if (_falseCoursesEnabled) {
+ if (falseCoursesEnabledNode->getBoolValue()) {
// The factor of 30.0 gives a period of 120 which gives us 3 cycles and six
// zeros i.e. six courses: one front course, one back course, and four
// false courses. Three of the six are reverse sensing.
double angle = atan2(dot_v, dot_h) * SGD_RADIANS_TO_DEGREES;
double deflectionAngle = target_gs - angle;
- if (_falseCoursesEnabled) {
+ if (falseCoursesEnabledNode->getBoolValue()) {
// Construct false glideslopes. The scale factor of 1.5
// in the sawtooth gives a period of 6 degrees.
// There will be zeros at 3, 6r, 9, 12r et cetera
SGInterpTable *low_tbl;
SGInterpTable *high_tbl;
+ SGPropertyNode_ptr _radio_node;
SGPropertyNode_ptr lon_node;
SGPropertyNode_ptr lat_node;
SGPropertyNode_ptr alt_node;
SGPropertyNode_ptr gps_xtrack_error_nm_node;
SGPropertyNode_ptr _magvarNode;
+ // realism setting, are false courses and GS lobes enabled?
+ SGPropertyNode_ptr falseCoursesEnabledNode;
+
// internal (private) values
+ bool _operable; ///< is the unit serviceable, on, powered, etc
int play_count;
time_t last_time;
FGNavRecordPtr _navaid;
double _gsNeedleDeflection;
double _gsNeedleDeflectionNorm;
- // realism setting, are false courses and GS lobes enabled?
- bool _falseCoursesEnabled;
-
SGSharedPtr<SGSampleGroup> _sgr;
+ std::vector<SGPropertyNode*> _tiedNodes;
bool updateWithPower(double aDt);
*/
double localizerWidth(FGNavRecord* aLOC);
FGNavRecord* findPrimaryNavaid(const SGGeod& aPos, double aFreqMHz);
+
+ /// accessor for tied, read-only 'operable' property
+ bool isOperable() const
+ { return _operable; }
+
+ /**
+ * Tied-properties helper, record nodes which are tied for easy un-tie-ing
+ */
+ template <typename T>
+ void tie(const char* aRelPath, const SGRawValue<T>& aRawValue)
+ {
+ SGPropertyNode* nd = _radio_node->getNode(aRelPath, true);
+ _tiedNodes.push_back(nd);
+ nd->tie(aRawValue);
+ }
public:
FGNavRadio(SGPropertyNode *node);
void
TACAN::update (double delta_time_sec)
{
+ // don't do anything when paused
+ if (delta_time_sec == 0) return;
+
if (!_serviceable_node->getBoolValue() || !_electrical_node->getBoolValue()) {
_last_distance_nm = 0;
_in_range_node->setBoolValue(false);
}
_distance_node->setDoubleValue( tmp_dist );
_speed_node->setDoubleValue(speed_kt);
- _time_node->setDoubleValue(distance_nm/speed_kt*60.0);
+ _time_node->setDoubleValue(speed_kt > 0 ? (distance_nm/speed_kt*60.0) : 0);
_bearing_node->setDoubleValue(bearing);
_x_shift_node->setDoubleValue(x_shift);
_y_shift_node->setDoubleValue(y_shift);
SG_LOG(SG_GENERAL, SG_ALERT, "hello world!");
+ const FGAirport* egph = fgFindAirportID("EGPH");
+ SG_LOG(SG_GENERAL, SG_ALERT, "egph: cart location:" << egph->cart());
+
+ FGAirport::AirportFilter af;
+ FGPositioned::List l = FGPositioned::findClosestN(egph->geod(), 20, 2000.0, &af);
+ for (unsigned int i=0; i<l.size(); ++i) {
+ SG_LOG(SG_GENERAL, SG_ALERT, "\t" << l[i]->ident() << "/" << l[i]->name());
+ }
+
+ //l = FGPositioned::findWithinRange(egph->geod(), 500.0, &af);
+ //for (unsigned int i=0; i<l.size(); ++i) {
+ // SG_LOG(SG_GENERAL, SG_ALERT, "\t" << l[i]->ident() << "/" << l[i]->name());
+ //}
+
+
FGRouteMgr* rm = new FGRouteMgr;
globals->add_subsystem( "route-manager", rm );
GPS* gps = new GPS(nd);
globals->add_subsystem("gps", gps);
- const FGAirport* egph = fgFindAirportID("EGPH");
+
testSetPosition(egph->geod());
// startup the route manager
/**
* Get the logging classes.
*/
+// XXX Making the result buffer be global is a band-aid that hopefully
+// delays its destruction 'til after its last use.
+namespace
+{
+string loggingResult;
+}
+
static const char *
getLoggingClasses ()
{
sgDebugClass classes = logbuf::get_log_classes();
- static string result;
- result = "";
+ loggingResult.clear();
for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
if ((classes&log_class_mappings[i].c) > 0) {
- if (!result.empty())
- result += '|';
- result += log_class_mappings[i].name;
+ if (!loggingResult.empty())
+ loggingResult += '|';
+ loggingResult += log_class_mappings[i].name;
}
}
- return result.c_str();
+ return loggingResult.c_str();
}
{
// Dynamic stuff, do not store geometry
setUseDisplayList(false);
+ setDataVariance(Object::DYNAMIC);
osg::StateSet* stateSet = getOrCreateStateSet();
stateSet->setRenderBinDetails(1001, "RenderBin");
{
// Dynamic stuff, do not store geometry
setUseDisplayList(false);
+ setDataVariance(Object::DYNAMIC);
osg::StateSet* stateSet = getOrCreateStateSet();
stateSet->setRenderBinDetails(1000, "RenderBin");
m(1,i) *= 1.0/_ymax;
}
+ _lastViewport[0] = 0;
+ _lastViewport[1] = 0;
+ _lastViewport[2] = 0;
+ _lastViewport[3] = 0;
+
dirtyBound();
// All done. Add us to the list
bool FGPanelNode::doMouseAction(int button, int updown, int x, int y)
{
+ if (_lastViewport[2] == 0 || _lastViewport[3] == 0) {
+ // we haven't been drawn yet, presumably
+ return false;
+ }
+
// Covert the screen coordinates to viewport coordinates in the
// range [0:1], then transform to OpenGL "post projection" coords
// in [-1:1]. Remember the difference in Y direction!
return NULL;
}
- FGRunway* runway = apt->getRunwayByIdent(parts[1]);
- if (!runway) {
+ if (!apt->hasRunwayWithIdent(parts[1])) {
SG_LOG(SG_GENERAL, SG_WARN, "navaid " << aName << " associated with bogus runway ID:" << parts[1]);
return NULL;
}
- return runway;
+ return apt->getRunwayByIdent(parts[1]);
}
}
mRunway = getRunwayFromName(_name);
+ if (!mRunway) {
+ return;
+ }
if (type() != GS) {
readAirportSceneryData();
#include <map>
#include <set>
#include <algorithm> // for sort
-#include <locale> // for char-traits toupper
-
-#include <iostream>
+#include <queue>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <simgear/timing/timestamp.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>
+#include <simgear/math/SGBox.hxx>
#include "positioned.hxx"
using std::lower_bound;
using std::upper_bound;
+static NamedPositionedIndex global_identIndex;
+static NamedPositionedIndex global_nameIndex;
+
+//////////////////////////////////////////////////////////////////////////////
+
+namespace Octree
+{
+
+const double LEAF_SIZE = SG_NM_TO_METER * 8.0;
+const double LEAF_SIZE_SQR = LEAF_SIZE * LEAF_SIZE;
+
+typedef SGBox<double> SGBoxd;
+
+template<typename T1, typename T2>
+inline bool
+intersects(const SGVec3<T1>& v, const SGBox<T2>& box)
+{
+ if (v[0] < box.getMin()[0])
+ return false;
+ if (box.getMax()[0] < v[0])
+ return false;
+ if (v[1] < box.getMin()[1])
+ return false;
+ if (box.getMax()[1] < v[1])
+ return false;
+ if (v[2] < box.getMin()[2])
+ return false;
+ if (box.getMax()[2] < v[2])
+ return false;
+ return true;
+}
+
/**
- * Order positioned elements by type, then pointer address. This allows us to
- * use range searches (lower_ and upper_bound) to grab items of a particular
- * type out of bucket efficently.
+ * Decorate an object with a double value, and use that value to order
+ * items, for the purpoises of the STL algorithms
*/
-class OrderByType
+template <class T>
+class Ordered
{
public:
- bool operator()(const FGPositioned* a, const FGPositioned* b) const
- {
- if (a->type() == b->type()) return a < b;
- return a->type() < b->type();
- }
+ Ordered(const T& v, double x) :
+ _order(x),
+ _inner(v)
+ {
+ }
+
+ Ordered(const Ordered<T>& a) :
+ _order(a._order),
+ _inner(a._inner)
+ {
+ }
+
+ Ordered<T>& operator=(const Ordered<T>& a)
+ {
+ _order = a._order;
+ _inner = a._inner;
+ return *this;
+ }
+
+ bool operator<(const Ordered<T>& other) const
+ {
+ return _order < other._order;
+ }
+
+ bool operator>(const Ordered<T>& other) const
+ {
+ return _order > other._order;
+ }
+
+ const T& get() const
+ { return _inner; }
+
+ double order() const
+ { return _order; }
+
+private:
+ double _order;
+ T _inner;
};
-class LowerLimitOfType
+class Node;
+typedef Ordered<Node*> OrderedNode;
+typedef std::greater<OrderedNode> FNPQCompare;
+
+/**
+ * the priority queue is fundamental to our search algorithm. When searching,
+ * we know the front of the queue is the nearest unexpanded node (to the search
+ * location). The default STL pqueue returns the 'largest' item from top(), so
+ * to get the smallest, we need to replace the default Compare functor (less<>)
+ * with greater<>.
+ */
+typedef std::priority_queue<OrderedNode, std::vector<OrderedNode>, FNPQCompare> FindNearestPQueue;
+
+typedef Ordered<FGPositioned*> OrderedPositioned;
+typedef std::vector<OrderedPositioned> FindNearestResults;
+
+Node* global_spatialOctree = NULL;
+
+/**
+ * Octree node base class, tracks its bounding box and provides various
+ * queries relating to it
+ */
+class Node
{
public:
- bool operator()(const FGPositioned* a, const FGPositioned::Type b) const
- {
- return a->type() < b;
- }
-
- bool operator()(const FGPositioned::Type a, const FGPositioned* b) const
- {
- return a < b->type();
- }
+ bool contains(const SGVec3d& aPos) const
+ {
+ return intersects(aPos, _box);
+ }
- // The operator below is required by VS2005 in debug mode
- bool operator()(const FGPositioned* a, const FGPositioned* b) const
- {
- return a->type() < b->type();
- }
+ double distSqrToNearest(const SGVec3d& aPos) const
+ {
+ return distSqr(aPos, getClosestPoint(aPos));
+ }
+
+ virtual void insert(FGPositioned* aP) = 0;
+
+ SGVec3d getClosestPoint(const SGVec3d& aPos) const
+ {
+ SGVec3d r;
+
+ for (unsigned int i=0;i<3; ++i) {
+ if (aPos[i] < _box.getMin()[i]) {
+ r[i] = _box.getMin()[i];
+ } else if (aPos[i] > _box.getMax()[i]) {
+ r[i] = _box.getMax()[i];
+ } else {
+ r[i] = aPos[i];
+ }
+ } // of axis iteration
+
+ return r;
+ }
+
+ virtual void visit(const SGVec3d& aPos, double aCutoff,
+ FGPositioned::Filter* aFilter,
+ FindNearestResults& aResults, FindNearestPQueue&) = 0;
+protected:
+ Node(const SGBoxd &aBox) :
+ _box(aBox)
+ {
+ }
+
+ const SGBoxd _box;
};
+class Leaf : public Node
+{
+public:
+ Leaf(const SGBoxd& aBox) :
+ Node(aBox)
+ {
+ }
+
+ const FGPositioned::List& members() const
+ { return _members; }
+
+ virtual void insert(FGPositioned* aP)
+ {
+ _members.push_back(aP);
+ }
+
+ virtual void visit(const SGVec3d& aPos, double aCutoff,
+ FGPositioned::Filter* aFilter,
+ FindNearestResults& aResults, FindNearestPQueue&)
+ {
+ int previousResultsSize = aResults.size();
+ int addedCount = 0;
+
+ for (unsigned int i=0; i<_members.size(); ++i) {
+ FGPositioned* p = _members[i];
+ double d2 = distSqr(aPos, p->cart());
+ if (d2 > aCutoff) {
+ continue;
+ }
+
+ if (aFilter) {
+ if (aFilter->hasTypeRange() && !aFilter->passType(p->type())) {
+ continue;
+ }
+
+ if (!aFilter->pass(p)) {
+ continue;
+ }
+ } // of have a filter
-typedef std::set<FGPositioned*, OrderByType> BucketEntry;
-typedef std::map<long int, BucketEntry> SpatialPositionedIndex;
+ ++addedCount;
+ aResults.push_back(OrderedPositioned(p, d2));
+ }
+
+ if (addedCount == 0) {
+ return;
+ }
+
+ // keep aResults sorted
+ // sort the new items, usually just one or two items
+ std::sort(aResults.begin() + previousResultsSize, aResults.end());
+
+ // merge the two sorted ranges together - in linear time
+ std::inplace_merge(aResults.begin(),
+ aResults.begin() + previousResultsSize, aResults.end());
+ }
+private:
+ FGPositioned::List _members;
+};
-static NamedPositionedIndex global_identIndex;
-static NamedPositionedIndex global_nameIndex;
-static SpatialPositionedIndex global_spatialIndex;
+class Branch : public Node
+{
+public:
+ Branch(const SGBoxd& aBox) :
+ Node(aBox)
+ {
+ memset(children, 0, sizeof(Node*) * 8);
+ }
+
+ virtual void insert(FGPositioned* aP)
+ {
+ SGVec3d cart(aP->cart());
+ assert(contains(cart));
+ int childIndex = 0;
+
+ SGVec3d center(_box.getCenter());
+ // tests must match indices in SGbox::getCorner
+ if (cart.x() < center.x()) {
+ childIndex += 1;
+ }
+
+ if (cart.y() < center.y()) {
+ childIndex += 2;
+ }
+
+ if (cart.z() < center.z()) {
+ childIndex += 4;
+ }
+
+ Node* child = children[childIndex];
+ if (!child) { // lazy building of children
+ SGBoxd cb(boxForChild(childIndex));
+ double d2 = dot(cb.getSize(), cb.getSize());
+ if (d2 < LEAF_SIZE_SQR) {
+ child = new Leaf(cb);
+ } else {
+ child = new Branch(cb);
+ }
+
+ children[childIndex] = child;
+ }
+
+ child->insert(aP);
+ }
+
+ virtual void visit(const SGVec3d& aPos, double aCutoff,
+ FGPositioned::Filter*,
+ FindNearestResults&, FindNearestPQueue& aQ)
+ {
+ for (unsigned int i=0; i<8; ++i) {
+ if (!children[i]) {
+ continue;
+ }
+
+ double d2 = children[i]->distSqrToNearest(aPos);
+ if (d2 > aCutoff) {
+ continue; // exceeded cutoff
+ }
+
+ aQ.push(Ordered<Node*>(children[i], d2));
+ } // of child iteration
+ }
+
+
+private:
+ /**
+ * Return the box for a child touching the specified corner
+ */
+ SGBoxd boxForChild(unsigned int aCorner) const
+ {
+ SGBoxd r(_box.getCenter());
+ r.expandBy(_box.getCorner(aCorner));
+ return r;
+ }
+
+ Node* children[8];
+};
-SpatialPositionedIndex::iterator
-bucketEntryForPositioned(FGPositioned* aPos)
+void findNearestN(const SGVec3d& aPos, unsigned int aN, double aCutoffM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults)
{
- int bucketIndex = aPos->bucket().gen_index();
- SpatialPositionedIndex::iterator it = global_spatialIndex.find(bucketIndex);
- if (it != global_spatialIndex.end()) {
- return it;
- }
-
- // create a new BucketEntry
- return global_spatialIndex.insert(it, std::make_pair(bucketIndex, BucketEntry()));
+ aResults.clear();
+ FindNearestPQueue pq;
+ FindNearestResults results;
+ pq.push(Ordered<Node*>(global_spatialOctree, 0));
+ double cut = aCutoffM * aCutoffM;
+
+ while (!pq.empty()) {
+ if (!results.empty()) {
+ // terminate the search if we have sufficent results, and we are
+ // sure no node still on the queue contains a closer match
+ double furthestResultOrder = results.back().order();
+ if ((results.size() >= aN) && (furthestResultOrder < pq.top().order())) {
+ break;
+ }
+ }
+
+ Node* nd = pq.top().get();
+ pq.pop();
+
+ nd->visit(aPos, cut, aFilter, results, pq);
+ } // of queue iteration
+
+ // depending on leaf population, we may have (slighty) more results
+ // than requested
+ unsigned int numResults = std::min((unsigned int) results.size(), aN);
+ // copy results out
+ aResults.resize(numResults);
+ for (unsigned int r=0; r<numResults; ++r) {
+ aResults[r] = results[r].get();
+ }
+}
+
+void findAllWithinRange(const SGVec3d& aPos, double aRangeM, FGPositioned::Filter* aFilter, FGPositioned::List& aResults)
+{
+ aResults.clear();
+ FindNearestPQueue pq;
+ FindNearestResults results;
+ pq.push(Ordered<Node*>(global_spatialOctree, 0));
+ double rng = aRangeM * aRangeM;
+
+ while (!pq.empty()) {
+ Node* nd = pq.top().get();
+ pq.pop();
+
+ nd->visit(aPos, rng, aFilter, results, pq);
+ } // of queue iteration
+
+ unsigned int numResults = results.size();
+ // copy results out
+ aResults.resize(numResults);
+ for (unsigned int r=0; r<numResults; ++r) {
+ aResults[r] = results[r].get();
+ }
}
+} // of namespace Octree
+
+//////////////////////////////////////////////////////////////////////////////
+
static void
addToIndices(FGPositioned* aPos)
{
std::make_pair(aPos->name(), aPos));
}
-
- SpatialPositionedIndex::iterator it = bucketEntryForPositioned(aPos);
- it->second.insert(aPos);
+ if (!Octree::global_spatialOctree) {
+ double RADIUS_EARTH_M = 7000 * 1000.0; // 7000km is plenty
+ SGVec3d earthExtent(RADIUS_EARTH_M, RADIUS_EARTH_M, RADIUS_EARTH_M);
+ Octree::global_spatialOctree = new Octree::Branch(SGBox<double>(-earthExtent, earthExtent));
+ }
+ Octree::global_spatialOctree->insert(aPos);
}
static void
++it;
} // of multimap walk
}
-
- SpatialPositionedIndex::iterator sit = bucketEntryForPositioned(aPos);
- sit->second.erase(aPos);
-}
-
-static void
-spatialFilterInBucket(const SGBucket& aBucket, FGPositioned::Filter* aFilter, FGPositioned::List& aResult)
-{
- SpatialPositionedIndex::const_iterator it;
- it = global_spatialIndex.find(aBucket.gen_index());
- if (it == global_spatialIndex.end()) {
- return;
- }
-
- BucketEntry::const_iterator l = it->second.begin();
- BucketEntry::const_iterator u = it->second.end();
-
- if (!aFilter) { // pass everything
- aResult.insert(aResult.end(), l, u);
- return;
- }
-
- if (aFilter->hasTypeRange()) {
- // avoid many calls to the filter hook
- l = lower_bound(it->second.begin(), it->second.end(), aFilter->minType(), LowerLimitOfType());
- u = upper_bound(l, it->second.end(), aFilter->maxType(), LowerLimitOfType());
- }
-
- for ( ; l != u; ++l) {
- if ((*aFilter)(*l)) {
- aResult.push_back(*l);
- }
- }
-}
-
-static void
-spatialFind(const SGGeod& aPos, double aRange,
- FGPositioned::Filter* aFilter, FGPositioned::List& aResult)
-{
- SGBucket buck(aPos);
- double lat = aPos.getLatitudeDeg(),
- lon = aPos.getLongitudeDeg();
-
- int bx = (int)( aRange*SG_NM_TO_METER / buck.get_width_m() / 2);
- int by = (int)( aRange*SG_NM_TO_METER / buck.get_height_m() / 2 );
-
- // loop over bucket range
- for ( int i=-bx; i<=bx; i++) {
- for ( int j=-by; j<=by; j++) {
- spatialFilterInBucket(sgBucketOffset(lon, lat, i, j), aFilter, aResult);
- } // of j-iteration
- } // of i-iteration
-}
-
-/**
- */
-class RangePredictate
-{
-public:
- RangePredictate(const SGGeod& aOrigin, double aRange) :
- mOrigin(SGVec3d::fromGeod(aOrigin)),
- mRangeSqr(aRange * aRange)
- { ; }
-
- bool operator()(const FGPositionedRef& aPos)
- {
- double dSqr = distSqr(aPos->cart(), mOrigin);
- return (dSqr > mRangeSqr);
- }
-
-private:
- SGVec3d mOrigin;
- double mRangeSqr;
-};
-
-static void
-filterListByRange(const SGGeod& aPos, double aRange, FGPositioned::List& aResult)
-{
- RangePredictate pred(aPos, aRange * SG_NM_TO_METER);
- FGPositioned::List::iterator newEnd;
- newEnd = std::remove_if(aResult.begin(), aResult.end(), pred);
- aResult.erase(newEnd, aResult.end());
}
class DistanceOrdering
return result;
}
-static FGPositioned::List
-spatialGetClosest(const SGGeod& aPos, unsigned int aN, double aCutoffNm, FGPositioned::Filter* aFilter)
-{
- FGPositioned::List result;
- int radius = 1; // start at 1, radius 0 is handled explicitly
- SGBucket buck;
- double lat = aPos.getLatitudeDeg(),
- lon = aPos.getLongitudeDeg();
- // final cutoff is in metres, and scaled to account for testing the corners
- // of the 'box' instead of the centre of each edge
- double cutoffM = aCutoffNm * SG_NM_TO_METER * 1.5;
-
- // base case, simplifes loop to do it seperately here
- spatialFilterInBucket(sgBucketOffset(lon, lat, 0, 0), aFilter, result);
-
- for (;result.size() < aN; ++radius) {
- // cutoff check
- double az1, az2, d1, d2;
- SGGeodesy::inverse(aPos, sgBucketOffset(lon, lat, -radius, -radius).get_center(), az1, az2, d1);
- SGGeodesy::inverse(aPos, sgBucketOffset(lon, lat, radius, radius).get_center(), az1, az2, d2);
-
- if ((d1 > cutoffM) && (d2 > cutoffM)) {
- //std::cerr << "spatialGetClosest terminating due to range cutoff" << std::endl;
- break;
- }
-
- FGPositioned::List hits;
- for ( int i=-radius; i<=radius; i++) {
- spatialFilterInBucket(sgBucketOffset(lon, lat, i, -radius), aFilter, hits);
- spatialFilterInBucket(sgBucketOffset(lon, lat, -radius, i), aFilter, hits);
- spatialFilterInBucket(sgBucketOffset(lon, lat, i, radius), aFilter, hits);
- spatialFilterInBucket(sgBucketOffset(lon, lat, radius, i), aFilter, hits);
- }
-
- result.insert(result.end(), hits.begin(), hits.end()); // append
- } // of outer loop
-
- sortByDistance(aPos, result);
- if (result.size() > aN) {
- result.resize(aN); // truncate at requested number of matches
- }
-
- return result;
-}
-
//////////////////////////////////////////////////////////////////////////////
class OrderByName
// aliases
{"waypoint", WAYPOINT},
{"apt", AIRPORT},
+ {"arpt", AIRPORT},
{"any", INVALID},
{"all", INVALID},
FGPositioned::findWithinRange(const SGGeod& aPos, double aRangeNm, Filter* aFilter)
{
List result;
- spatialFind(aPos, aRangeNm, aFilter, result);
- filterListByRange(aPos, aRangeNm, result);
+ Octree::findAllWithinRange(SGVec3d::fromGeod(aPos),
+ aRangeNm * SG_NM_TO_METER, aFilter, result);
return result;
}
FGPositionedRef
FGPositioned::findClosest(const SGGeod& aPos, double aCutoffNm, Filter* aFilter)
{
- FGPositioned::List l(spatialGetClosest(aPos, 1, aCutoffNm, aFilter));
+ List l(findClosestN(aPos, 1, aCutoffNm, aFilter));
if (l.empty()) {
return NULL;
}
FGPositioned::List
FGPositioned::findClosestN(const SGGeod& aPos, unsigned int aN, double aCutoffNm, Filter* aFilter)
{
- return spatialGetClosest(aPos, aN, aCutoffNm, aFilter);
+ List result;
+ Octree::findNearestN(SGVec3d::fromGeod(aPos), aN, aCutoffNm * SG_NM_TO_METER, aFilter, result);
+ return result;
}
FGPositionedRef
FGPositioned::findNextWithPartialId(FGPositionedRef aCur, const std::string& aId, Filter* aFilter)
{
+ if (aId.empty()) {
+ return NULL;
+ }
+
std::string id(boost::to_upper_copy(aId));
// It is essential to bound our search, to avoid iterating all the way to the end of the database.
{
// 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 =
- spatialGetClosest(aPos, aOffset + 2, 1000.0, aFilter);
+ FGPositioned::List matches;
+ Octree::findNearestN(SGVec3d::fromGeod(aPos), aOffset + 2, 1000 * SG_NM_TO_METER, aFilter, matches);
if ((int) matches.size() <= aOffset) {
SG_LOG(SG_GENERAL, SG_INFO, "findClosestWithPartial, couldn't match enough with prefix");
#include <Main/fg_props.hxx>
#include <Main/util.hxx>
#include <Scenery/scenery.hxx>
+#include <Navaids/navrecord.hxx>
#include "NasalSys.hxx"
HASHSET("width", 5, naNum(rwy->widthM()));
HASHSET("threshold", 9, naNum(rwy->displacedThresholdM()));
HASHSET("stopway", 7, naNum(rwy->stopwayM()));
+
+ if (rwy->ILS()) {
+ HASHSET("ils_frequency_mhz", 17, naNum(rwy->ILS()->get_freq() / 100.0));
+ }
+
#undef HASHSET
naHash_set(rwys, rwyid, rwydata);
}
test_up_PLIB_LIBS = -lplibsg -lplibul
endif
-est_epsilon_SOURCES = est-epsilon.c
+est_epsilon_SOURCES = est-epsilon.cxx
est_epsilon_LDADD = $(opengl_LIBS)
-gl_info_SOURCES = gl-info.c
+gl_info_SOURCES = gl-info.cxx
gl_info_LDADD = $(opengl_LIBS)
alcinfo_SOURCES = alcinfo.cxx
+++ /dev/null
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#ifdef HAVE_WINDOWS_H
-# include <windows.h>
-#endif
-
-#include <stdio.h>
-
-#include <simgear/compiler.h>
-#if defined( __APPLE__)
-# include <OpenGL/OpenGL.h>
-#else
-# include <GL/gl.h>
-#endif
-
-int main() {
- GLfloat a, t;
-
- a = 1.0;
-
- do {
- printf("a = %.10f\n", a);
- a = a / 2.0;
- t = 1.0 + a;
- } while ( t > 1.0 );
-
- a = a + a;
-
- printf("Estimated GLfloat epsilon = %.10f\n", a);
-
- return(0);
-}
--- /dev/null
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+# include <windows.h>
+#endif
+
+#include <stdio.h>
+
+#include <simgear/compiler.h>
+#if defined( __APPLE__)
+# include <OpenGL/OpenGL.h>
+#else
+# include <GL/gl.h>
+#endif
+
+int main() {
+ GLfloat a, t;
+
+ a = 1.0;
+
+ do {
+ printf("a = %.10f\n", a);
+ a = a / 2.0;
+ t = 1.0 + a;
+ } while ( t > 1.0 );
+
+ a = a + a;
+
+ printf("Estimated GLfloat epsilon = %.10f\n", a);
+
+ return(0);
+}
+++ /dev/null
-/*
-From: Steve Baker <sbaker@link.com>
-Sender: root@fatcity.com
-To: OPENGL-GAMEDEV-L <OPENGL-GAMEDEV-L@fatcity.com>
-Subject: Re: Win32 OpenGL Resource Page
-Date: Fri, 24 Apr 1998 07:33:51 -0800
-*/
-
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#ifdef HAVE_WINDOWS_H
-# include <windows.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <simgear/compiler.h>
-#if defined( __APPLE__)
-# include <OpenGL/OpenGL.h>
-# include <GLUT/glut.h>
-#else
-# include <GL/gl.h>
-# ifdef HAVE_GLUT_H
-# include <GL/glut.h>
-# endif
-#endif
-
-
-void getPrints ( GLenum token, char *string )
-{
- printf ( "%s = \"%s\"\n", string, glGetString ( token ) ) ;
-}
-
-void getPrint2f ( GLenum token, char *string )
-{
- GLfloat f[2] ;
- glGetFloatv ( token, f ) ;
- printf ( "%s = %g,%g\n", string, f[0],f[1] ) ;
-}
-
-void getPrintf ( GLenum token, char *string )
-{
- GLfloat f ;
- glGetFloatv ( token, &f ) ;
- printf ( "%s = %g\n", string, f ) ;
-}
-
-void getPrint2i ( GLenum token, char *string )
-{
- GLint i[2] ;
- glGetIntegerv ( token, i ) ;
- printf ( "%s = %d,%d\n", string, i[0],i[1] ) ;
-}
-
-void getPrinti ( GLenum token, char *string )
-{
- GLint i ;
- glGetIntegerv ( token, &i ) ;
- printf ( "%s = %d\n", string, i ) ;
-}
-
-int main ( int argc, char **argv )
-{
-#ifdef HAVE_GLUT_H
- glutInit ( &argc, argv ) ;
- glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ;
- glutCreateWindow ( "You should never see this window!" ) ;
-
- getPrints ( GL_VENDOR , "GL_VENDOR" ) ;
- getPrints ( GL_RENDERER , "GL_RENDERER" ) ;
- getPrints ( GL_VERSION , "GL_VERSION" ) ;
- getPrints ( GL_EXTENSIONS , "GL_EXTENSIONS" ) ;
-
- getPrinti ( GL_RED_BITS , "GL_RED_BITS" ) ;
- getPrinti ( GL_GREEN_BITS , "GL_GREEN_BITS" ) ;
- getPrinti ( GL_BLUE_BITS , "GL_BLUE_BITS" ) ;
- getPrinti ( GL_ALPHA_BITS , "GL_ALPHA_BITS" ) ;
- getPrinti ( GL_DEPTH_BITS , "GL_DEPTH_BITS" ) ;
- getPrinti ( GL_INDEX_BITS , "GL_INDEX_BITS" ) ;
- getPrinti ( GL_STENCIL_BITS, "GL_STENCIL_BITS" ) ;
-
- getPrinti ( GL_ACCUM_RED_BITS , "GL_ACCUM_RED_BITS" ) ;
- getPrinti ( GL_ACCUM_GREEN_BITS, "GL_ACCUM_GREEN_BITS" ) ;
- getPrinti ( GL_ACCUM_BLUE_BITS , "GL_ACCUM_BLUE_BITS" ) ;
- getPrinti ( GL_ACCUM_ALPHA_BITS, "GL_ACCUM_ALPHA_BITS" ) ;
-
- getPrinti ( GL_AUX_BUFFERS, "GL_AUX_BUFFERS" ) ;
-
- getPrinti ( GL_MAX_ATTRIB_STACK_DEPTH , "GL_MAX_ATTRIB_STACK_DEPTH" ) ;
- getPrinti ( GL_MAX_NAME_STACK_DEPTH , "GL_MAX_NAME_STACK_DEPTH" ) ;
- getPrinti ( GL_MAX_TEXTURE_STACK_DEPTH , "GL_MAX_TEXTURE_STACK_DEPTH" ) ;
- getPrinti ( GL_MAX_PROJECTION_STACK_DEPTH, "GL_MAX_PROJECTION_STACK_DEPTH" ) ;
- getPrinti ( GL_MAX_MODELVIEW_STACK_DEPTH , "GL_MAX_MODELVIEW_STACK_DEPTH" ) ;
-
- getPrinti ( GL_MAX_CLIP_PLANES , "GL_MAX_CLIP_PLANES" ) ;
- getPrinti ( GL_MAX_EVAL_ORDER , "GL_MAX_EVAL_ORDER" ) ;
- getPrinti ( GL_MAX_LIGHTS , "GL_MAX_LIGHTS" ) ;
- getPrinti ( GL_MAX_LIST_NESTING , "GL_MAX_LIST_NESTING" ) ;
- getPrinti ( GL_MAX_TEXTURE_SIZE , "GL_MAX_TEXTURE_SIZE" ) ;
- getPrint2i( GL_MAX_VIEWPORT_DIMS , "GL_MAX_VIEWPORT_DIMS" ) ;
-
- getPrintf ( GL_POINT_SIZE_GRANULARITY, "GL_POINT_SIZE_GRANULARITY" ) ;
- getPrint2f( GL_POINT_SIZE_RANGE , "GL_POINT_SIZE_RANGE" ) ;
-
- printf("Default values:\n\n");
-
- getPrinti( GL_UNPACK_ALIGNMENT , "GL_UNPACK_ALIGNMENT" ) ;
- getPrinti( GL_UNPACK_ROW_LENGTH , "GL_UNPACK_ROW_LENGTH" ) ;
- getPrinti( GL_UNPACK_SKIP_PIXELS , "GL_UNPACK_SKIP_PIXELS" ) ;
- getPrinti( GL_UNPACK_SKIP_ROWS , "GL_UNPACK_SKIP_ROWS" ) ;
- getPrinti( GL_BLEND_SRC , "GL_BLEND_SRC" ) ;
- getPrinti( GL_BLEND_DST , "GL_BLEND_DST" ) ;
-#else
-
- printf("GL Utility Toolkit (glut) was not found on this system.\n");
-#endif
-
- return 0 ;
-}
--- /dev/null
+/*
+From: Steve Baker <sbaker@link.com>
+Sender: root@fatcity.com
+To: OPENGL-GAMEDEV-L <OPENGL-GAMEDEV-L@fatcity.com>
+Subject: Re: Win32 OpenGL Resource Page
+Date: Fri, 24 Apr 1998 07:33:51 -0800
+*/
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_WINDOWS_H
+# include <windows.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <simgear/compiler.h>
+#if defined( __APPLE__)
+# include <OpenGL/OpenGL.h>
+# include <GLUT/glut.h>
+#else
+# include <GL/gl.h>
+# ifdef HAVE_GLUT_H
+# include <GL/glut.h>
+# endif
+#endif
+
+
+void getPrints ( GLenum token, char *string )
+{
+ printf ( "%s = \"%s\"\n", string, glGetString ( token ) ) ;
+}
+
+void getPrint2f ( GLenum token, char *string )
+{
+ GLfloat f[2] ;
+ glGetFloatv ( token, f ) ;
+ printf ( "%s = %g,%g\n", string, f[0],f[1] ) ;
+}
+
+void getPrintf ( GLenum token, char *string )
+{
+ GLfloat f ;
+ glGetFloatv ( token, &f ) ;
+ printf ( "%s = %g\n", string, f ) ;
+}
+
+void getPrint2i ( GLenum token, char *string )
+{
+ GLint i[2] ;
+ glGetIntegerv ( token, i ) ;
+ printf ( "%s = %d,%d\n", string, i[0],i[1] ) ;
+}
+
+void getPrinti ( GLenum token, char *string )
+{
+ GLint i ;
+ glGetIntegerv ( token, &i ) ;
+ printf ( "%s = %d\n", string, i ) ;
+}
+
+int main ( int argc, char **argv )
+{
+#ifdef HAVE_GLUT_H
+ glutInit ( &argc, argv ) ;
+ glutInitDisplayMode ( GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ) ;
+ glutCreateWindow ( "You should never see this window!" ) ;
+
+ getPrints ( GL_VENDOR , "GL_VENDOR" ) ;
+ getPrints ( GL_RENDERER , "GL_RENDERER" ) ;
+ getPrints ( GL_VERSION , "GL_VERSION" ) ;
+ getPrints ( GL_EXTENSIONS , "GL_EXTENSIONS" ) ;
+
+ getPrinti ( GL_RED_BITS , "GL_RED_BITS" ) ;
+ getPrinti ( GL_GREEN_BITS , "GL_GREEN_BITS" ) ;
+ getPrinti ( GL_BLUE_BITS , "GL_BLUE_BITS" ) ;
+ getPrinti ( GL_ALPHA_BITS , "GL_ALPHA_BITS" ) ;
+ getPrinti ( GL_DEPTH_BITS , "GL_DEPTH_BITS" ) ;
+ getPrinti ( GL_INDEX_BITS , "GL_INDEX_BITS" ) ;
+ getPrinti ( GL_STENCIL_BITS, "GL_STENCIL_BITS" ) ;
+
+ getPrinti ( GL_ACCUM_RED_BITS , "GL_ACCUM_RED_BITS" ) ;
+ getPrinti ( GL_ACCUM_GREEN_BITS, "GL_ACCUM_GREEN_BITS" ) ;
+ getPrinti ( GL_ACCUM_BLUE_BITS , "GL_ACCUM_BLUE_BITS" ) ;
+ getPrinti ( GL_ACCUM_ALPHA_BITS, "GL_ACCUM_ALPHA_BITS" ) ;
+
+ getPrinti ( GL_AUX_BUFFERS, "GL_AUX_BUFFERS" ) ;
+
+ getPrinti ( GL_MAX_ATTRIB_STACK_DEPTH , "GL_MAX_ATTRIB_STACK_DEPTH" ) ;
+ getPrinti ( GL_MAX_NAME_STACK_DEPTH , "GL_MAX_NAME_STACK_DEPTH" ) ;
+ getPrinti ( GL_MAX_TEXTURE_STACK_DEPTH , "GL_MAX_TEXTURE_STACK_DEPTH" ) ;
+ getPrinti ( GL_MAX_PROJECTION_STACK_DEPTH, "GL_MAX_PROJECTION_STACK_DEPTH" ) ;
+ getPrinti ( GL_MAX_MODELVIEW_STACK_DEPTH , "GL_MAX_MODELVIEW_STACK_DEPTH" ) ;
+
+ getPrinti ( GL_MAX_CLIP_PLANES , "GL_MAX_CLIP_PLANES" ) ;
+ getPrinti ( GL_MAX_EVAL_ORDER , "GL_MAX_EVAL_ORDER" ) ;
+ getPrinti ( GL_MAX_LIGHTS , "GL_MAX_LIGHTS" ) ;
+ getPrinti ( GL_MAX_LIST_NESTING , "GL_MAX_LIST_NESTING" ) ;
+ getPrinti ( GL_MAX_TEXTURE_SIZE , "GL_MAX_TEXTURE_SIZE" ) ;
+ getPrint2i( GL_MAX_VIEWPORT_DIMS , "GL_MAX_VIEWPORT_DIMS" ) ;
+
+ getPrintf ( GL_POINT_SIZE_GRANULARITY, "GL_POINT_SIZE_GRANULARITY" ) ;
+ getPrint2f( GL_POINT_SIZE_RANGE , "GL_POINT_SIZE_RANGE" ) ;
+
+ printf("Default values:\n\n");
+
+ getPrinti( GL_UNPACK_ALIGNMENT , "GL_UNPACK_ALIGNMENT" ) ;
+ getPrinti( GL_UNPACK_ROW_LENGTH , "GL_UNPACK_ROW_LENGTH" ) ;
+ getPrinti( GL_UNPACK_SKIP_PIXELS , "GL_UNPACK_SKIP_PIXELS" ) ;
+ getPrinti( GL_UNPACK_SKIP_ROWS , "GL_UNPACK_SKIP_ROWS" ) ;
+ getPrinti( GL_BLEND_SRC , "GL_BLEND_SRC" ) ;
+ getPrinti( GL_BLEND_DST , "GL_BLEND_DST" ) ;
+#else
+
+ printf("GL Utility Toolkit (glut) was not found on this system.\n");
+#endif
+
+ return 0 ;
+}
--- /dev/null
+#!BPY
+
+# """
+# Name: 'YASim (.xml)'
+# Blender: 245
+# Group: 'Import'
+# Tooltip: 'Loads and visualizes a YASim FDM geometry'
+# """
+
+__author__ = "Melchior FRANZ < mfranz # aon : at >"
+__url__ = ["http://www.flightgear.org/", "http://cvs.flightgear.org/viewvc/source/utils/Modeller/yasim_import.py"]
+__version__ = "0.2"
+__bpydoc__ = """\
+yasim_import.py loads and visualizes a YASim FDM geometry
+=========================================================
+
+It is recommended to load the model superimposed over a greyed out and immutable copy of the aircraft model:
+
+ (0) put this script into ~/.blender/scripts/
+ (1) load or import aircraft model (menu -> "File" -> "Import" -> "AC3D (.ac) ...")
+ (2) create new *empty* scene (menu -> arrow button left of "SCE:scene1" combobox -> "ADD NEW" -> "empty")
+ (3) rename scene to yasim (not required)
+ (4) link to scene1 (F10 -> "Output" tab in "Buttons Window" -> arrow button left of text entry "No Set Scene" -> "scene1")
+ (5) now load the YASim config file (menu -> "File" -> "Import" -> "YASim (.xml) ...")
+
+This is good enough for simple checks. But if you are working on the YASim configuration, then you need a
+quick and convenient way to reload the file. In that case continue after (4):
+
+ (5) switch the button area at the bottom of the blender screen to "Scripts Window" mode (green python snake icon)
+ (6) load the YASim config file (menu -> "Scripts" -> "Import" -> "YASim (.xml) ...")
+ (7) make the "Scripts Window" area as small as possible by dragging the area separator down
+ (8) optionally split the "3D View" area and switch the right part to the "Outliner"
+ (9) press the "Reload YASim" button in the script area to reload the file
+
+
+If the 3D model is displaced with respect to the FDM model, then the <offsets> values from the
+model animation XML file should be added as comment to the YASim config file, as a line all by
+itself, with no spaces surrounding the equal signs. Spaces elsewhere are allowed. For example:
+
+ <offsets>
+ <x-m>3.45</x-m>
+ <z-m>-0.4</z-m>
+ <pitch-deg>5</pitch-deg>
+ </offsets>
+
+becomes:
+
+ <!-- offsets: x=3.45 z=-0.4 p=5 -->
+
+Possible variables are:
+
+ x ... <x-m>
+ y ... <y-m>
+ z ... <z-m>
+ h ... <heading-deg>
+ p ... <pitch-deg>
+ r ... <roll-deg>
+
+Of course, absolute FDM coordinates can then no longer directly be read from Blender's 3D view.
+The cursor coordinates display in the script area, however, shows the coordinates in YASim space.
+Note that object names don't contain XML indices but element numbers. YASim_flap0#2 is the third
+flap0 in the whole file, not necessarily in its parent XML group. A floating point part in the
+object name (e.g. YASim_flap0#2.004) only means that the geometry has been reloaded that often.
+It's an unavoidable consequence of how Blender deals with meshes.
+
+
+Elements are displayed as follows:
+
+ cockpit -> monkey head
+ fuselage -> blue "tube" (with only 12 sides for less clutter); center at "a"
+ vstab -> red with yellow control surfaces (flap0, flap1, slat, spoiler)
+ wing/mstab/hstab -> green with yellow control surfaces (which are always 20 cm deep);
+ symmetric surfaces are only displayed on the left side, unless
+ the "Mirror" button is active
+ thrusters (jet/propeller/thruster) -> dashed line from center to actionpt;
+ arrow from actionpt along thrust vector (always 1 m long);
+ propeller circle
+ rotor -> radius and rel_len_blade_start circle, normal and forward vector,
+ one blade at phi0 with direction arrow near blade tip
+ gear -> contact point and compression vector (no arrow head)
+ tank -> magenta cube (10 cm side length)
+ weight -> inverted cyan cone
+ ballast -> yellow cylinder
+ hitch -> hexagon (10 cm diameter)
+ hook -> dashed line for up angle, T-line for down angle
+ launchbar -> dashed line for up angles, T-line for down angles
+ (launchbar and holdback each)
+
+
+The Mirror button complements symmetrical surfaces (wing/hstab/mstab) and control surfaces
+(flap0/flap1/slat/spoiler). This is useful for asymmetrical aircraft, but has the disadvantage
+that it moves the surfaces' object centers from their usual place, yasim's [x, y, z] value,
+to [0, 0, 0]. Turning mirroring off restores the object center.
+
+
+
+Environment variable BLENDER_YASIM_IMPORT can be set to a space-separated list of options:
+
+ $ BLENDER_YASIM_IMPORT="mirror verbose" blender
+
+whereby:
+
+ verbose ... enables verbose logs
+ mirror ... enables mirroring of symmetric surfaces
+"""
+
+
+#--------------------------------------------------------------------------------
+# Copyright (C) 2009 Melchior FRANZ < mfranz # aon : at >
+#
+# 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.
+#--------------------------------------------------------------------------------
+
+
+import Blender, BPyMessages, string, math, os
+from Blender.Mathutils import *
+from xml.sax import handler, make_parser
+
+
+CONFIG = string.split(os.getenv("BLENDER_YASIM_IMPORT") or "")
+YASIM_MATRIX = Matrix([-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])
+ORIGIN = Vector(0, 0, 0)
+X = Vector(1, 0, 0)
+Y = Vector(0, 1, 0)
+Z = Vector(0, 0, 1)
+DEG2RAD = math.pi / 180
+RAD2DEG = 180 / math.pi
+
+NO_EVENT = 0
+RELOAD_BUTTON = 1
+CURSOR_BUTTON = 2
+MIRROR_BUTTON = 3
+
+
+
+class Global:
+ verbose = "verbose" in CONFIG
+ path = ""
+ matrix = None
+ data = None
+ cursor = ORIGIN
+ last_cursor = Vector(Blender.Window.GetCursorPos())
+ mirror_button = Blender.Draw.Create("mirror" in CONFIG)
+
+
+
+class Abort(Exception):
+ def __init__(self, msg, term = None):
+ self.msg = msg
+ self.term = term
+
+
+
+def log(msg):
+ if Global.verbose:
+ print(msg)
+
+
+
+def draw_dashed_line(mesh, start, end):
+ w = 0.04
+ step = w * (end - start).normalize()
+ n = len(mesh.verts)
+ for i in range(int(1 + 0.5 * (end - start).length / w)):
+ a = start + 2 * i * step
+ b = a + step
+ if (b - end).length < step.length:
+ b = end
+ mesh.verts.extend([a, b])
+ mesh.edges.extend([n + 2 * i, n + 2 * i + 1])
+
+
+
+def draw_arrow(mesh, start, end):
+ v = end - start
+ m = v.toTrackQuat('x', 'z').toMatrix().resize4x4() * TranslationMatrix(start)
+ v = v.length * X
+ n = len(mesh.verts)
+ mesh.verts.extend([ORIGIN * m , v * m, (v - 0.05 * X + 0.05 * Y) * m, (v - 0.05 * X - 0.05 * Y) * m]) # head
+ mesh.verts.extend([(ORIGIN + 0.05 * Y) * m, (ORIGIN - 0.05 * Y) * m]) # base
+ mesh.edges.extend([[n, n + 1], [n + 1, n + 2], [n + 1, n + 3], [n + 4, n + 5]])
+
+
+
+def draw_circle(mesh, numpoints, radius, matrix):
+ n = len(mesh.verts)
+ for i in range(numpoints):
+ angle = 2.0 * math.pi * i / numpoints
+ v = Vector(radius * math.cos(angle), radius * math.sin(angle), 0)
+ mesh.verts.extend([v * matrix])
+ for i in range(numpoints):
+ i1 = (i + 1) % numpoints
+ mesh.edges.extend([[n + i, n + i1]])
+
+
+
+class Item:
+ scene = Blender.Scene.GetCurrent()
+
+ def make_twosided(self, mesh):
+ mesh.faceUV = True
+ for f in mesh.faces:
+ f.mode |= Blender.Mesh.FaceModes.TWOSIDE | Blender.Mesh.FaceModes.OBCOL
+
+ def set_color(self, obj, color):
+ mat = Blender.Material.New()
+ mat.setRGBCol(color[0], color[1], color[2])
+ mat.setAlpha(color[3])
+ mat.mode |= Blender.Material.Modes.ZTRANSP | Blender.Material.Modes.TRANSPSHADOW
+ obj.transp = True
+
+ mesh = obj.getData(mesh = True)
+ mesh.materials += [mat]
+
+ for f in mesh.faces:
+ f.smooth = True
+ mesh.calcNormals()
+
+
+
+class Cockpit(Item):
+ def __init__(self, center):
+ mesh = Blender.Mesh.Primitives.Monkey()
+ mesh.transform(ScaleMatrix(0.13, 4) * Euler(90, 0, 90).toMatrix().resize4x4() * TranslationMatrix(Vector(-0.1, 0, -0.032)))
+ obj = self.scene.objects.new(mesh, "YASim_cockpit")
+ obj.setMatrix(TranslationMatrix(center) * Global.matrix)
+
+
+
+class Tank(Item):
+ def __init__(self, name, center):
+ mesh = Blender.Mesh.Primitives.Cube()
+ mesh.transform(ScaleMatrix(0.05, 4))
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(TranslationMatrix(center) * Global.matrix)
+ self.set_color(obj, [1, 0, 1, 0.5])
+
+
+
+class Ballast(Item):
+ def __init__(self, name, center):
+ mesh = Blender.Mesh.Primitives.Cylinder()
+ mesh.transform(ScaleMatrix(0.05, 4))
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(TranslationMatrix(center) * Global.matrix)
+ self.set_color(obj, [1, 1, 0, 0.5])
+
+
+
+class Weight(Item):
+ def __init__(self, name, center):
+ mesh = Blender.Mesh.Primitives.Cone()
+ mesh.transform(ScaleMatrix(0.05, 4))
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(TranslationMatrix(center) * Global.matrix)
+ self.set_color(obj, [0, 1, 1, 0.5])
+
+
+
+class Gear(Item):
+ def __init__(self, name, center, compression):
+ mesh = Blender.Mesh.New()
+ mesh.verts.extend([ORIGIN, compression])
+ mesh.edges.extend([0, 1])
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(TranslationMatrix(center) * Global.matrix)
+
+
+
+class Hook(Item):
+ def __init__(self, name, center, length, up_angle, dn_angle):
+ mesh = Blender.Mesh.New()
+ up = ORIGIN - length * math.cos(up_angle * DEG2RAD) * X - length * math.sin(up_angle * DEG2RAD) * Z
+ dn = ORIGIN - length * math.cos(dn_angle * DEG2RAD) * X - length * math.sin(dn_angle * DEG2RAD) * Z
+ mesh.verts.extend([ORIGIN, dn, dn + 0.05 * Y, dn - 0.05 * Y])
+ mesh.edges.extend([[0, 1], [2, 3]])
+ draw_dashed_line(mesh, ORIGIN, up)
+ draw_dashed_line(mesh, ORIGIN, dn)
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(TranslationMatrix(center) * Global.matrix)
+
+
+
+class Launchbar(Item):
+ def __init__(self, name, lb, lb_length, hb, hb_length, up_angle, dn_angle):
+ mesh = Blender.Mesh.New()
+ hb = hb - lb
+ lb_tip = ORIGIN + lb_length * math.cos(dn_angle * DEG2RAD) * X - lb_length * math.sin(dn_angle * DEG2RAD) * Z
+ hb_tip = hb - hb_length * math.cos(dn_angle * DEG2RAD) * X - hb_length * math.sin(dn_angle * DEG2RAD) * Z
+ mesh.verts.extend([lb_tip, ORIGIN, hb, hb_tip, lb_tip + 0.05 * Y, lb_tip - 0.05 * Y, hb_tip + 0.05 * Y, hb_tip - 0.05 * Y])
+ mesh.edges.extend([[0, 1], [1, 2], [2, 3], [4, 5], [6, 7]])
+ draw_dashed_line(mesh, ORIGIN, lb_length * math.cos(up_angle * DEG2RAD) * X - lb_length * math.sin(up_angle * DEG2RAD) * Z)
+ draw_dashed_line(mesh, hb, hb - hb_length * math.cos(up_angle * DEG2RAD) * X - hb_length * math.sin(up_angle * DEG2RAD) * Z)
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(TranslationMatrix(lb) * Global.matrix)
+
+
+
+class Hitch(Item):
+ def __init__(self, name, center):
+ mesh = Blender.Mesh.Primitives.Circle(6, 0.1)
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(RotationMatrix(90, 4, "x") * TranslationMatrix(center) * Global.matrix)
+
+
+
+class Thrust:
+ def set_actionpt(self, p):
+ self.actionpt = p
+
+ def set_dir(self, d):
+ self.thrustvector = d
+
+
+
+class Thruster(Thrust, Item):
+ def __init__(self, name, center, thrustvector):
+ (self.name, self.center, self.actionpt, self.thrustvector) = (name, center, center, thrustvector)
+
+ def __del__(self):
+ a = self.actionpt - self.center
+ mesh = Blender.Mesh.New()
+ draw_dashed_line(mesh, ORIGIN, a)
+ draw_arrow(mesh, a, a + self.thrustvector.normalize())
+ obj = self.scene.objects.new(mesh, self.name)
+ obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
+
+
+
+class Propeller(Thrust, Item):
+ def __init__(self, name, center, radius):
+ (self.name, self.center, self.radius, self.actionpt, self.thrustvector) = (name, center, radius, center, -X)
+
+ def __del__(self):
+ a = self.actionpt - self.center
+ matrix = self.thrustvector.toTrackQuat('z', 'x').toMatrix().resize4x4() * TranslationMatrix(a)
+
+ mesh = Blender.Mesh.New()
+ mesh.verts.extend([ORIGIN * matrix, (ORIGIN + self.radius * X) * matrix])
+ mesh.edges.extend([[0, 1]])
+ draw_dashed_line(mesh, ORIGIN, a)
+ draw_arrow(mesh, a, a + self.thrustvector.normalize())
+
+ draw_circle(mesh, 128, self.radius, matrix)
+ obj = self.scene.objects.new(mesh, self.name)
+ obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
+
+
+
+class Jet(Thrust, Item):
+ def __init__(self, name, center, rotate):
+ (self.name, self.center, self.actionpt) = (name, center, center)
+ self.thrustvector = -X * RotationMatrix(rotate, 4, "y")
+
+ def __del__(self):
+ a = self.actionpt - self.center
+ mesh = Blender.Mesh.New()
+ draw_dashed_line(mesh, ORIGIN, a)
+ draw_arrow(mesh, a, a + self.thrustvector.normalize())
+ obj = self.scene.objects.new(mesh, self.name)
+ obj.setMatrix(TranslationMatrix(self.center) * Global.matrix)
+
+
+
+class Fuselage(Item):
+ def __init__(self, name, a, b, width, taper, midpoint):
+ numvert = 12
+ angle = []
+ for i in range(numvert):
+ alpha = i * 2 * math.pi / float(numvert)
+ angle.append([math.cos(alpha), math.sin(alpha)])
+
+ axis = b - a
+ length = axis.length
+ mesh = Blender.Mesh.New()
+
+ for i in range(numvert):
+ mesh.verts.extend([[0, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
+ for i in range(numvert):
+ mesh.verts.extend([[midpoint * length, 0.5 * width * angle[i][0], 0.5 * width * angle[i][1]]])
+ for i in range(numvert):
+ mesh.verts.extend([[length, 0.5 * width * taper * angle[i][0], 0.5 * width * taper * angle[i][1]]])
+ for i in range(numvert):
+ i1 = (i + 1) % numvert
+ mesh.faces.extend([[i, i1, i1 + numvert, i + numvert]])
+ mesh.faces.extend([[i + numvert, i1 + numvert, i1 + 2 * numvert, i + 2 * numvert]])
+
+ mesh.verts.extend([ORIGIN, length * X])
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(axis.toTrackQuat('x', 'y').toMatrix().resize4x4() * TranslationMatrix(a) * Global.matrix)
+ self.set_color(obj, [0, 0, 0.5, 0.4])
+
+
+
+class Rotor(Item):
+ def __init__(self, name, center, up, fwd, numblades, radius, chord, twist, taper, rel_len_blade_start, phi0, ccw):
+ matrix = RotationMatrix(phi0, 4, "z") * up.toTrackQuat('z', 'x').toMatrix().resize4x4()
+ invert = matrix.copy().invert()
+ direction = [-1, 1][ccw]
+ twist *= DEG2RAD
+ a = ORIGIN + rel_len_blade_start * radius * X
+ b = ORIGIN + radius * X
+ tw = 0.5 * chord * taper * math.cos(twist) * Y + 0.5 * direction * chord * taper * math.sin(twist) * Z
+
+ mesh = Blender.Mesh.New()
+ mesh.verts.extend([ORIGIN, a, b, a + 0.5 * chord * Y, a - 0.5 * chord * Y, b + tw, b - tw])
+ mesh.edges.extend([[0, 1], [1, 2], [1, 3], [1, 4], [3, 5], [4, 6], [5, 6]])
+ draw_circle(mesh, 64, rel_len_blade_start * radius, Matrix())
+ draw_circle(mesh, 128, radius, Matrix())
+ draw_arrow(mesh, ORIGIN, up * invert)
+ draw_arrow(mesh, ORIGIN, fwd * invert)
+ b += 0.1 * X + direction * chord * Y
+ draw_arrow(mesh, b, b + min(0.5 * radius, 1) * direction * Y)
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(matrix * TranslationMatrix(center) * Global.matrix)
+
+
+
+class Wing(Item):
+ def __init__(self, name, root, length, chord, incidence, twist, taper, sweep, dihedral):
+ # <1--0--2
+ # \ | /
+ # 4-3-5
+ self.is_symmetric = not name.startswith("YASim_vstab#")
+ mesh = Blender.Mesh.New()
+ mesh.verts.extend([ORIGIN, ORIGIN + 0.5 * chord * X, ORIGIN - 0.5 * chord * X])
+ tip = ORIGIN + math.cos(sweep * DEG2RAD) * length * Y - math.sin(sweep * DEG2RAD) * length * X
+ tipfore = tip + 0.5 * taper * chord * math.cos(twist * DEG2RAD) * X + 0.5 * taper * chord * math.sin(twist * DEG2RAD) * Z
+ tipaft = tip + tip - tipfore
+ mesh.verts.extend([tip, tipfore, tipaft])
+ mesh.faces.extend([[0, 1, 4, 3], [2, 0, 3, 5]])
+
+ self.make_twosided(mesh)
+
+ obj = self.scene.objects.new(mesh, name)
+ mesh.transform(Euler(dihedral, -incidence, 0).toMatrix().resize4x4())
+ self.set_color(obj, [[0.5, 0.0, 0, 0.5], [0.0, 0.5, 0, 0.5]][self.is_symmetric])
+ (self.obj, self.mesh) = (obj, mesh)
+
+ if self.is_symmetric and Global.mirror_button.val:
+ mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR)
+ mod[Blender.Modifier.Settings.AXIS_X] = False
+ mod[Blender.Modifier.Settings.AXIS_Y] = True
+ mod[Blender.Modifier.Settings.AXIS_Z] = False
+ mesh.transform(TranslationMatrix(root)) # must move object center to x axis
+ obj.setMatrix(Global.matrix)
+ else:
+ obj.setMatrix(TranslationMatrix(root) * Global.matrix)
+
+ def add_flap(self, name, start, end):
+ a = Vector(self.mesh.verts[2].co)
+ b = Vector(self.mesh.verts[5].co)
+ c = 0.2 * (Vector(self.mesh.verts[0].co - a)).normalize()
+ m = self.obj.getMatrix()
+
+ mesh = Blender.Mesh.New()
+ i0 = a + start * (b - a)
+ i1 = a + end * (b - a)
+ mesh.verts.extend([i0, i1, i0 + c, i1 + c])
+ mesh.faces.extend([[0, 1, 3, 2]])
+
+ self.make_twosided(mesh)
+
+ obj = self.scene.objects.new(mesh, name)
+ obj.setMatrix(m)
+ self.set_color(obj, [0.8, 0.8, 0, 0.9])
+
+ if self.is_symmetric and Global.mirror_button.val:
+ mod = obj.modifiers.append(Blender.Modifier.Type.MIRROR)
+ mod[Blender.Modifier.Settings.AXIS_X] = False
+ mod[Blender.Modifier.Settings.AXIS_Y] = True
+ mod[Blender.Modifier.Settings.AXIS_Z] = False
+
+
+
+class import_yasim(handler.ErrorHandler, handler.ContentHandler):
+ ignored = ["cruise", "approach", "control-input", "control-output", "control-speed", \
+ "control-setting", "stall", "airplane", "piston-engine", "turbine-engine", \
+ "rotorgear", "tow", "winch", "solve-weight"]
+
+
+ # err_handler
+ def warning(self, exception):
+ print((self.error_string("Warning", exception)))
+
+ def error(self, exception):
+ print((self.error_string("Error", exception)))
+
+ def fatalError(self, exception):
+ raise Abort(str(exception), self.error_string("Fatal", exception))
+
+ def error_string(self, tag, e):
+ (column, line) = (e.getColumnNumber(), e.getLineNumber())
+ return "%s: %s\n%s%s^" % (tag, str(e), Global.data[line - 1], column * ' ')
+
+
+ # doc_handler
+ def setDocumentLocator(self, locator):
+ self.locator = locator
+
+ def startDocument(self):
+ self.tags = []
+ self.counter = {}
+ self.items = [None]
+
+ def endDocument(self):
+ for o in Item.scene.objects:
+ o.sel = True
+
+ def startElement(self, tag, attrs):
+ if len(self.tags) == 0 and tag != "airplane":
+ raise Abort("this isn't a YASim config file (bad root tag at line %d)" % self.locator.getLineNumber())
+
+ self.tags.append(tag)
+ path = string.join(self.tags, '/')
+ item = Item()
+ parent = self.items[-1]
+
+ if self.counter.has_key(tag):
+ self.counter[tag] += 1
+ else:
+ self.counter[tag] = 0
+
+ if tag == "cockpit":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ log("\033[31mcockpit x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
+ item = Cockpit(c)
+
+ elif tag == "fuselage":
+ a = Vector(float(attrs["ax"]), float(attrs["ay"]), float(attrs["az"]))
+ b = Vector(float(attrs["bx"]), float(attrs["by"]), float(attrs["bz"]))
+ width = float(attrs["width"])
+ taper = float(attrs.get("taper", 1))
+ midpoint = float(attrs.get("midpoint", 0.5))
+ log("\033[32mfuselage ax=%f ay=%f az=%f bx=%f by=%f bz=%f width=%f taper=%f midpoint=%f\033[m" % \
+ (a[0], a[1], a[2], b[0], b[1], b[2], width, taper, midpoint))
+ item = Fuselage("YASim_%s#%d" % (tag, self.counter[tag]), a, b, width, taper, midpoint)
+
+ elif tag == "gear":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ compression = float(attrs.get("compression", 1))
+ up = Z * compression
+ if attrs.has_key("upx"):
+ up = Vector(float(attrs["upx"]), float(attrs["upy"]), float(attrs["upz"])).normalize() * compression
+ log("\033[35;1mgear x=%f y=%f z=%f compression=%f upx=%f upy=%f upz=%f\033[m" \
+ % (c[0], c[1], c[2], compression, up[0], up[1], up[2]))
+ item = Gear("YASim_gear#%d" % self.counter[tag], c, up)
+
+ elif tag == "jet":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ rotate = float(attrs.get("rotate", 0))
+ log("\033[36;1mjet x=%f y=%f z=%f rotate=%f\033[m" % (c[0], c[1], c[2], rotate))
+ item = Jet("YASim_jet#%d" % self.counter[tag], c, rotate)
+
+ elif tag == "propeller":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ radius = float(attrs["radius"])
+ log("\033[36;1m%s x=%f y=%f z=%f radius=%f\033[m" % (tag, c[0], c[1], c[2], radius))
+ item = Propeller("YASim_propeller#%d" % self.counter[tag], c, radius)
+
+ elif tag == "thruster":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ v = Vector(float(attrs["vx"]), float(attrs["vy"]), float(attrs["vz"]))
+ log("\033[36;1m%s x=%f y=%f z=%f vx=%f vy=%f vz=%f\033[m" % (tag, c[0], c[1], c[2], v[0], v[1], v[2]))
+ item = Thruster("YASim_thruster#%d" % self.counter[tag], c, v)
+
+ elif tag == "actionpt":
+ if not isinstance(parent, Thrust):
+ raise Abort("%s is not part of a thruster/propeller/jet at line %d" \
+ % (path, self.locator.getLineNumber()))
+
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ log("\t\033[36mactionpt x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
+ parent.set_actionpt(c)
+
+ elif tag == "dir":
+ if not isinstance(parent, Thrust):
+ raise Abort("%s is not part of a thruster/propeller/jet at line %d" \
+ % (path, self.locator.getLineNumber()))
+
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ log("\t\033[36mdir x=%f y=%f z=%f\033[m" % (c[0], c[1], c[2]))
+ parent.set_dir(c)
+
+ elif tag == "tank":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ log("\033[34;1m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
+ item = Tank("YASim_tank#%d" % self.counter[tag], c)
+
+ elif tag == "ballast":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
+ item = Ballast("YASim_ballast#%d" % self.counter[tag], c)
+
+ elif tag == "weight":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ log("\033[34m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
+ item = Weight("YASim_weight#%d" % self.counter[tag], c)
+
+ elif tag == "hook":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ length = float(attrs.get("length", 1))
+ up_angle = float(attrs.get("up-angle", 0))
+ down_angle = float(attrs.get("down-angle", 70))
+ log("\033[35m%s x=%f y=%f z=%f length=%f up-angle=%f down-angle=%f\033[m" \
+ % (tag, c[0], c[1], c[2], length, up_angle, down_angle))
+ item = Hook("YASim_hook#%d" % self.counter[tag], c, length, up_angle, down_angle)
+
+ elif tag == "hitch":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ log("\033[35m%s x=%f y=%f z=%f\033[m" % (tag, c[0], c[1], c[2]))
+ item = Hitch("YASim_hitch#%d" % self.counter[tag], c)
+
+ elif tag == "launchbar":
+ c = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ length = float(attrs.get("length", 1))
+ up_angle = float(attrs.get("up-angle", -45))
+ down_angle = float(attrs.get("down-angle", 45))
+ holdback = Vector(float(attrs.get("holdback-x", c[0])), float(attrs.get("holdback-y", c[1])), float(attrs.get("holdback-z", c[2])))
+ holdback_length = float(attrs.get("holdback-length", 2))
+ log("\033[35m%s x=%f y=%f z=%f length=%f down-angle=%f up-angle=%f holdback-x=%f holdback-y=%f holdback-z+%f holdback-length=%f\033[m" \
+ % (tag, c[0], c[1], c[2], length, down_angle, up_angle, \
+ holdback[0], holdback[1], holdback[2], holdback_length))
+ item = Launchbar("YASim_launchbar#%d" % self.counter[tag], c, length, holdback, holdback_length, up_angle, down_angle)
+
+ elif tag == "wing" or tag == "hstab" or tag == "vstab" or tag == "mstab":
+ root = Vector(float(attrs["x"]), float(attrs["y"]), float(attrs["z"]))
+ length = float(attrs["length"])
+ chord = float(attrs["chord"])
+ incidence = float(attrs.get("incidence", 0))
+ twist = float(attrs.get("twist", 0))
+ taper = float(attrs.get("taper", 1))
+ sweep = float(attrs.get("sweep", 0))
+ dihedral = float(attrs.get("dihedral", [0, 90][tag == "vstab"]))
+ log("\033[33;1m%s x=%f y=%f z=%f length=%f chord=%f incidence=%f twist=%f taper=%f sweep=%f dihedral=%f\033[m" \
+ % (tag, root[0], root[1], root[2], length, chord, incidence, twist, taper, sweep, dihedral))
+ item = Wing("YASim_%s#%d" % (tag, self.counter[tag]), root, length, chord, incidence, twist, taper, sweep, dihedral)
+
+ elif tag == "flap0" or tag == "flap1" or tag == "slat" or tag == "spoiler":
+ if not isinstance(parent, Wing):
+ raise Abort("%s is not part of a wing or stab at line %d" \
+ % (path, self.locator.getLineNumber()))
+
+ start = float(attrs["start"])
+ end = float(attrs["end"])
+ log("\t\033[33m%s start=%f end=%f\033[m" % (tag, start, end))
+ parent.add_flap("YASim_%s#%d" % (tag, self.counter[tag]), start, end)
+
+ elif tag == "rotor":
+ c = Vector(float(attrs.get("x", 0)), float(attrs.get("y", 0)), float(attrs.get("z", 0)))
+ norm = Vector(float(attrs.get("nx", 0)), float(attrs.get("ny", 0)), float(attrs.get("nz", 1)))
+ fwd = Vector(float(attrs.get("fx", 1)), float(attrs.get("fy", 0)), float(attrs.get("fz", 0)))
+ diameter = float(attrs.get("diameter", 10.2))
+ numblades = int(attrs.get("numblades", 4))
+ chord = float(attrs.get("chord", 0.3))
+ twist = float(attrs.get("twist", 0))
+ taper = float(attrs.get("taper", 1))
+ rel_len_blade_start = float(attrs.get("rel-len-blade-start", 0))
+ phi0 = float(attrs.get("phi0", 0))
+ ccw = not not int(attrs.get("ccw", 0))
+
+ log(("\033[36;1mrotor x=%f y=%f z=%f nx=%f ny=%f nz=%f fx=%f fy=%f fz=%f numblades=%d diameter=%f " \
+ + "chord=%f twist=%f taper=%f rel_len_blade_start=%f phi0=%f ccw=%d\033[m") \
+ % (c[0], c[1], c[2], norm[0], norm[1], norm[2], fwd[0], fwd[1], fwd[2], numblades, \
+ diameter, chord, twist, taper, rel_len_blade_start, phi0, ccw))
+ item = Rotor("YASim_rotor#%d" % self.counter[tag], c, norm, fwd, numblades, 0.5 * diameter, chord, \
+ twist, taper, rel_len_blade_start, phi0, ccw)
+
+ elif tag not in self.ignored:
+ log("\033[30;1m%s\033[m" % path)
+
+ self.items.append(item)
+
+ def endElement(self, tag):
+ self.tags.pop()
+ self.items.pop()
+
+
+
+def extract_matrix(filedata, tag):
+ v = { 'x': 0.0, 'y': 0.0, 'z': 0.0, 'h': 0.0, 'p': 0.0, 'r': 0.0 }
+ has_offsets = False
+ for line in filedata:
+ line = string.strip(line)
+ if not line.startswith("<!--") or not line.endswith("-->"):
+ continue
+ line = string.strip(line[4:-3])
+ if not string.lower(line).startswith("%s:" % tag):
+ continue
+ line = string.strip(line[len(tag) + 1:])
+ for assignment in string.split(line):
+ (key, value) = string.split(assignment, '=', 2)
+ v[string.strip(key)] = float(string.strip(value))
+ has_offsets = True
+
+ if not has_offsets:
+ return None
+
+ print(("using offsets: x=%f y=%f z=%f h=%f p=%f r=%f" % (v['x'], v['y'], v['z'], v['h'], v['p'], v['r'])))
+ return Euler(v['r'], v['p'], v['h']).toMatrix().resize4x4() * TranslationMatrix(Vector(v['x'], v['y'], v['z']))
+
+
+
+def load_yasim_config(path):
+ if BPyMessages.Error_NoFile(path):
+ return
+
+ Blender.Window.WaitCursor(1)
+ Blender.Window.EditMode(0)
+
+ print(("loading '%s'" % path))
+ try:
+ for o in Item.scene.objects:
+ if o.name.startswith("YASim_"):
+ Item.scene.objects.unlink(o)
+
+ f = open(path)
+ Global.data = f.readlines()
+ f.close
+
+ Global.path = path
+ Global.matrix = YASIM_MATRIX
+ matrix = extract_matrix(Global.data, "offsets")
+ if matrix:
+ Global.matrix *= matrix.invert()
+
+ Global.yasim.parse(path)
+ Blender.Registry.SetKey("FGYASimImportExport", { "path": path }, False)
+ Global.data = None
+
+ except Abort, e:
+ print(("%s\nAborting ..." % (e.term or e.msg)))
+ Blender.Draw.PupMenu("Error%t|" + e.msg)
+
+ Blender.Window.RedrawAll()
+ Blender.Window.WaitCursor(0)
+
+
+
+def gui_draw():
+ from Blender import BGL, Draw
+ (width, height) = Blender.Window.GetAreaSize()
+
+ BGL.glClearColor(0.4, 0.4, 0.45, 1)
+ BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
+
+ BGL.glColor3f(1, 1, 1)
+ BGL.glRasterPos2f(5, 55)
+ Draw.Text("FlightGear YASim Import: '%s'" % Global.path)
+
+ Draw.PushButton("Reload", RELOAD_BUTTON, 5, 5, 80, 32, "reload YASim config file")
+ Global.mirror_button = Draw.Toggle("Mirror", MIRROR_BUTTON, 100, 5, 50, 16, Global.mirror_button.val, \
+ "show symmetric surfaces on both sides (reloads config)")
+ Draw.PushButton("Update Cursor", CURSOR_BUTTON, width - 650, 5, 100, 32, "update cursor display (in YASim coordinate system)")
+
+ BGL.glRasterPos2f(width - 530 + Blender.Draw.GetStringWidth("Vector from last") - Blender.Draw.GetStringWidth("Current"), 24)
+ Draw.Text("Current cursor pos: x = %+.3f y = %+.3f z = %+.3f" % tuple(Global.cursor))
+
+ c = Global.cursor - Global.last_cursor
+ BGL.glRasterPos2f(width - 530, 7)
+ Draw.Text("Vector from last cursor pos: x = %+.3f y = %+.3f z = %+.3f length = %.3f m" % (c[0], c[1], c[2], c.length))
+
+
+
+def gui_event(ev, value):
+ if ev == Blender.Draw.ESCKEY:
+ Blender.Draw.Exit()
+
+
+
+def gui_button(n):
+ if n == NO_EVENT:
+ return
+
+ elif n == RELOAD_BUTTON:
+ load_yasim_config(Global.path)
+
+ elif n == CURSOR_BUTTON:
+ Global.last_cursor = Global.cursor
+ Global.cursor = Vector(Blender.Window.GetCursorPos()) * Global.matrix.invert()
+ d = Global.cursor - Global.last_cursor
+ print(("cursor: x=\"%f\" y=\"%f\" z=\"%f\" dx=%f dy=%f dz=%f length=%f" \
+ % (Global.cursor[0], Global.cursor[1], Global.cursor[2], d[0], d[1], d[2], d.length)))
+
+ elif n == MIRROR_BUTTON:
+ load_yasim_config(Global.path)
+
+ Blender.Draw.Redraw(1)
+
+
+
+def main():
+ log(6 * "\n")
+ registry = Blender.Registry.GetKey("FGYASimImportExport", False)
+ if registry and "path" in registry and Blender.sys.exists(Blender.sys.expandpath(registry["path"])):
+ path = registry["path"]
+ else:
+ path = ""
+
+ xml_handler = import_yasim()
+ Global.yasim = make_parser()
+ Global.yasim.setContentHandler(xml_handler)
+ Global.yasim.setErrorHandler(xml_handler)
+
+ if Blender.Window.GetScreenInfo(Blender.Window.Types.SCRIPT):
+ Blender.Draw.Register(gui_draw, gui_event, gui_button)
+
+ Blender.Window.FileSelector(load_yasim_config, "Import YASim Configuration File", path)
+
+
+
+main()
+