1
0
Fork 0

Merge branch 'topics/bug150' into next

This commit is contained in:
James Turner 2010-08-14 19:17:23 +01:00
commit dae7e961c9
11 changed files with 116 additions and 361 deletions

View file

@ -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)

View file

@ -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

View file

@ -63,6 +63,7 @@ FGAirport::FGAirport(const string &id, const SGGeod& location, const SGGeod& tow
mRunwaysLoaded(false),
mTaxiwaysLoaded(true)
{
init(true); // init FGPositioned
}

View file

@ -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();
}

View file

@ -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)
@ -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);
}

View file

@ -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?

View file

@ -39,6 +39,7 @@
FGFix::FGFix(const std::string& aIdent, const SGGeod& aPos) :
FGPositioned(FIX, aIdent, aPos)
{
init(true); // init FGPositioned
}
// Constructor

View file

@ -65,4 +65,5 @@ FGMarkerBeaconRecord::FGMarkerBeaconRecord(Type aTy, FGRunway* aRunway, const SG
FGPositioned(aTy, string(), aPos),
_runway(aRunway)
{
init(true); // init FGPositioned
}

View file

@ -82,6 +82,7 @@ FGNavRecord::FGNavRecord(Type aTy, const std::string& aIdent,
}
}
init(true); // init FGPositioned (now position is adjusted)
}
void FGNavRecord::initAirportRelation()

View file

@ -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;
@ -402,91 +404,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
@ -592,14 +509,22 @@ 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 upperBoundId = aName;
upperBoundId[upperBoundId.size()-1]++;
NamedPositionedIndex::const_iterator upperBound = aIndex.lower_bound(upperBoundId);
NamedPositionedIndex::const_iterator it = aIndex.lower_bound(aName);
for (; it != upperBound; ++it) {
FGPositionedRef candidate = it->second;
if (aFilter) {
if (aFilter->hasTypeRange() && !aFilter->passType(candidate->type())) {
continue;
@ -610,70 +535,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 +570,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 +655,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 +674,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 +752,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);
}

View file

@ -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;
};