From 601cc9f79e51fc48de0afb2ff2fe2dadebb2a992 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 6 Feb 2010 20:58:40 +0000 Subject: [PATCH] Let track segments have multiple states --- include/ITrackSegment.hpp | 8 +- src/CrossoverTrack.cpp | 183 +++++++++++++------------- src/CurvedTrack.cpp | 264 +++++++++++++++++++------------------- src/Points.cpp | 14 ++ src/StraightTrack.cpp | 5 + 5 files changed, 253 insertions(+), 221 deletions(-) diff --git a/include/ITrackSegment.hpp b/include/ITrackSegment.hpp index 176f02a..def8e26 100644 --- a/include/ITrackSegment.hpp +++ b/include/ITrackSegment.hpp @@ -38,7 +38,7 @@ namespace track { // Uniquely identifies the location of a train and its orientation // along a piece of track // Used for verifying whether bits of track can join together - typedef std::pair Connection; + typedef pair Connection; // Angles for curved track typedef int Angle; @@ -141,6 +141,12 @@ struct ITrackSegment { // Convert the track segment to XML for writing out in the // map configuration file virtual xml::element toXml() const = 0; + + // Some track segments may have several states - e.g. points + // These functions change the track state + virtual bool hasMultipleStates() const = 0; + virtual void prevState() = 0; + virtual void nextState() = 0; }; ITrackSegmentPtr makeStraightTrack(const track::Direction& aDirection); diff --git a/src/CrossoverTrack.cpp b/src/CrossoverTrack.cpp index 3e3fbed..ad8dfba 100644 --- a/src/CrossoverTrack.cpp +++ b/src/CrossoverTrack.cpp @@ -35,159 +35,162 @@ using namespace boost; class CrossoverTrack : public ITrackSegment, public enable_shared_from_this { public: - CrossoverTrack() : myX(0), myY(0) {} - ~CrossoverTrack() {} + CrossoverTrack() : myX(0), myY(0) {} + ~CrossoverTrack() {} - void setOrigin(int x, int y) { myX = x; myY = y; } - - void render() const; - double segmentLength(const track::TravelToken& aToken) const; - bool isValidDirection(const track::Direction& aDirection) const; - track::Connection nextPosition(const track::TravelToken& aToken) const; - void getEndpoints(std::list >& aList) const; - ITrackSegmentPtr mergeExit(const Point& aPoint, - const track::Direction& aDirection); - track::TravelToken getTravelToken(track::Position aPosition, - track::Direction aDirection) const; - xml::element toXml() const; + void setOrigin(int x, int y) { myX = x; myY = y; } + void render() const; + double segmentLength(const track::TravelToken& aToken) const; + bool isValidDirection(const track::Direction& aDirection) const; + track::Connection nextPosition(const track::TravelToken& aToken) const; + void getEndpoints(std::list >& aList) const; + ITrackSegmentPtr mergeExit(const Point& aPoint, + const track::Direction& aDirection); + track::TravelToken getTravelToken(track::Position aPosition, + track::Direction aDirection) const; + xml::element toXml() const; + void nextState() {} + void prevState() {} + bool hasMultipleStates() const { return false; } + private: - void transform(const track::TravelToken& aToken, double aDelta) const; + void transform(const track::TravelToken& aToken, double aDelta) const; - int myX, myY; + int myX, myY; }; void CrossoverTrack::render() const { - // Render the y-going rails and sleepers - glPushMatrix(); + // Render the y-going rails and sleepers + glPushMatrix(); - renderStraightRail(); + renderStraightRail(); - glRotated(90.0, 0.0, 1.0, 0.0); - glTranslated(-0.4, 0.0, 0.0); + glRotated(90.0, 0.0, 1.0, 0.0); + glTranslated(-0.4, 0.0, 0.0); - for (int i = 0; i < 4; i++) { - renderSleeper(); - glTranslated(0.25, 0.0, 0.0); - } + for (int i = 0; i < 4; i++) { + renderSleeper(); + glTranslated(0.25, 0.0, 0.0); + } - glPopMatrix(); + glPopMatrix(); - // Render the x-going rails and sleepers - glPushMatrix(); + // Render the x-going rails and sleepers + glPushMatrix(); - glRotated(90.0, 0.0, 1.0, 0.0); + glRotated(90.0, 0.0, 1.0, 0.0); - renderStraightRail(); + renderStraightRail(); - glRotated(90.0, 0.0, 1.0, 0.0); - glTranslated(-0.4, 0.0, 0.0); + glRotated(90.0, 0.0, 1.0, 0.0); + glTranslated(-0.4, 0.0, 0.0); - for (int i = 0; i < 4; i++) { - renderSleeper(); - glTranslated(0.25, 0.0, 0.0); - } + for (int i = 0; i < 4; i++) { + renderSleeper(); + glTranslated(0.25, 0.0, 0.0); + } - glPopMatrix(); + glPopMatrix(); } double CrossoverTrack::segmentLength(const track::TravelToken& aToken) const { - return 1.0; + return 1.0; } track::TravelToken CrossoverTrack::getTravelToken(track::Position aPosition, - track::Direction aDirection) const + track::Direction aDirection) const { - if (!isValidDirection(aDirection)) - throw runtime_error - ("Invalid direction on crossover: " + lexical_cast(aDirection)); - - track::TravelToken tok = { - aDirection, - aPosition, - track::CHOOSE_STRAIGHT_ON, - bind(&CrossoverTrack::transform, this, _1, _2) - }; - tok.choices.insert(track::CHOOSE_STRAIGHT_ON); - return tok; + if (!isValidDirection(aDirection)) + throw runtime_error + ("Invalid direction on crossover: " + lexical_cast(aDirection)); + + track::TravelToken tok = { + aDirection, + aPosition, + track::CHOOSE_STRAIGHT_ON, + bind(&CrossoverTrack::transform, this, _1, _2) + }; + tok.choices.insert(track::CHOOSE_STRAIGHT_ON); + return tok; } void CrossoverTrack::transform(const track::TravelToken& aToken, - double aDelta) const + double aDelta) const { - assert(aDelta < 1.0); + assert(aDelta < 1.0); - bool backwards = aToken.direction== -axis::X || aToken.direction == -axis::Y; + bool backwards = aToken.direction== -axis::X || aToken.direction == -axis::Y; - if (backwards) { - aDelta = 1.0 - aDelta; - } + if (backwards) { + aDelta = 1.0 - aDelta; + } - track::Direction dir = backwards ? -aToken.direction : aToken.direction; + track::Direction dir = backwards ? -aToken.direction : aToken.direction; - const double xTrans = dir == axis::X ? aDelta : 0; - const double yTrans = dir == axis::Y ? aDelta : 0; + const double xTrans = dir == axis::X ? aDelta : 0; + const double yTrans = dir == axis::Y ? aDelta : 0; - glTranslated(static_cast(myX) + xTrans, - 0.0, - static_cast(myY) + yTrans); + glTranslated(static_cast(myX) + xTrans, + 0.0, + static_cast(myY) + yTrans); - if (dir == axis::Y) - glRotated(-90.0, 0.0, 1.0, 0.0); + if (dir == axis::Y) + glRotated(-90.0, 0.0, 1.0, 0.0); - glTranslated(-0.5, 0.0, 0.0); + glTranslated(-0.5, 0.0, 0.0); - if (backwards) - glRotated(-180.0, 0.0, 1.0, 0.0); + if (backwards) + glRotated(-180.0, 0.0, 1.0, 0.0); } bool CrossoverTrack::isValidDirection(const track::Direction& aDirection) const { - return aDirection == axis::X || aDirection == axis::Y - || aDirection == -axis::Y || aDirection == -axis::X; + return aDirection == axis::X || aDirection == axis::Y + || aDirection == -axis::Y || aDirection == -axis::X; } track::Connection CrossoverTrack::nextPosition(const track::TravelToken& aToken) const { - if (aToken.direction == axis::X) - return make_pair(makePoint(myX + 1, myY), axis::X); - else if (aToken.direction == -axis::X) - return make_pair(makePoint(myX - 1, myY), -axis::X); - else if (aToken.direction == axis::Y) - return make_pair(makePoint(myX, myY + 1), axis::Y); - else if (aToken.direction == -axis::Y) - return make_pair(makePoint(myX, myY - 1), -axis::Y); - else - throw runtime_error - ("Invalid direction on crossover: " + lexical_cast(aToken.direction)); + if (aToken.direction == axis::X) + return make_pair(makePoint(myX + 1, myY), axis::X); + else if (aToken.direction == -axis::X) + return make_pair(makePoint(myX - 1, myY), -axis::X); + else if (aToken.direction == axis::Y) + return make_pair(makePoint(myX, myY + 1), axis::Y); + else if (aToken.direction == -axis::Y) + return make_pair(makePoint(myX, myY - 1), -axis::Y); + else + throw runtime_error + ("Invalid direction on crossover: " + lexical_cast(aToken.direction)); } void CrossoverTrack::getEndpoints(std::list >& aList) const { - aList.push_back(makePoint(myX, myY)); + aList.push_back(makePoint(myX, myY)); } ITrackSegmentPtr CrossoverTrack::mergeExit(const Point& aPoint, - const track::Direction& aDirection) + const track::Direction& aDirection) { - if (aPoint == makePoint(myX, myY) - && isValidDirection(aDirection)) - return shared_from_this(); + if (aPoint == makePoint(myX, myY) + && isValidDirection(aDirection)) + return shared_from_this(); - // No way to extend a crossover - return ITrackSegmentPtr(); + // No way to extend a crossover + return ITrackSegmentPtr(); } xml::element CrossoverTrack::toXml() const { - return xml::element("crossoverTrack"); + return xml::element("crossoverTrack"); } ITrackSegmentPtr makeCrossoverTrack() { - return ITrackSegmentPtr(new CrossoverTrack); + return ITrackSegmentPtr(new CrossoverTrack); } diff --git a/src/CurvedTrack.cpp b/src/CurvedTrack.cpp index 33392bc..0dc684f 100644 --- a/src/CurvedTrack.cpp +++ b/src/CurvedTrack.cpp @@ -35,42 +35,46 @@ using namespace boost; class CurvedTrack : public ITrackSegment, public enable_shared_from_this { public: - CurvedTrack(track::Angle aStartAngle, track::Angle aFinishAngle, - int aRadius); - ~CurvedTrack(); + CurvedTrack(track::Angle aStartAngle, track::Angle aFinishAngle, + int aRadius); + ~CurvedTrack(); - void render() const; + void render() const; - void setOrigin(int x, int y) { origin.x = x; origin.y = y; } - double segmentLength(const track::TravelToken& aToken) const; + void setOrigin(int x, int y) { origin.x = x; origin.y = y; } + double segmentLength(const track::TravelToken& aToken) const; - Connection nextPosition(const track::TravelToken& aToken) const; - bool isValidDirection(const Direction& aDirection) const; - void getEndpoints(list >& aList) const; + Connection nextPosition(const track::TravelToken& aToken) const; + bool isValidDirection(const Direction& aDirection) const; + void getEndpoints(list >& aList) const; - ITrackSegmentPtr mergeExit(const Point& aPoint, - const track::Direction& aDirection); - - xml::element toXml() const; - track::TravelToken getTravelToken(track::Position aPosition, - track::Direction aDirection) const; + ITrackSegmentPtr mergeExit(const Point& aPoint, + const track::Direction& aDirection); + + xml::element toXml() const; + track::TravelToken getTravelToken(track::Position aPosition, + track::Direction aDirection) const; + + bool hasMultipleStates() const { return false; } + void nextState() {} + void prevState() {} private: - void transform(const track::TravelToken& aToken, double aDelta) const; - Vector cwEntryVector() const; - Vector ccwEntryVector() const; - void ensureValidDirection(const Direction& aDirection) const; - - Point origin; - int baseRadius; - track::Angle startAngle, finishAngle; + void transform(const track::TravelToken& aToken, double aDelta) const; + Vector cwEntryVector() const; + Vector ccwEntryVector() const; + void ensureValidDirection(const Direction& aDirection) const; + + Point origin; + int baseRadius; + track::Angle startAngle, finishAngle; }; CurvedTrack::CurvedTrack(track::Angle aStartAngle, - track::Angle aFinishAngle, - int aRadius) - : origin(makePoint(0, 0)), baseRadius(aRadius), - startAngle(aStartAngle), finishAngle(aFinishAngle) + track::Angle aFinishAngle, + int aRadius) + : origin(makePoint(0, 0)), baseRadius(aRadius), + startAngle(aStartAngle), finishAngle(aFinishAngle) { } @@ -82,49 +86,49 @@ CurvedTrack::~CurvedTrack() track::TravelToken CurvedTrack::getTravelToken(track::Position aPosition, - track::Direction aDirection) const + track::Direction aDirection) const { - ensureValidDirection(aDirection); - - track::TravelToken tok = { - aDirection, - aPosition, - track::CHOOSE_STRAIGHT_ON, - bind(&CurvedTrack::transform, this, _1, _2) - }; - tok.choices.insert(track::CHOOSE_STRAIGHT_ON); - return tok; + ensureValidDirection(aDirection); + + track::TravelToken tok = { + aDirection, + aPosition, + track::CHOOSE_STRAIGHT_ON, + bind(&CurvedTrack::transform, this, _1, _2) + }; + tok.choices.insert(track::CHOOSE_STRAIGHT_ON); + return tok; } void CurvedTrack::transform(const track::TravelToken& aToken, double aDelta) const { - assert(aDelta < segmentLength(aToken)); + assert(aDelta < segmentLength(aToken)); - glTranslated(static_cast(origin.x), - 0.0, - static_cast(origin.y)); + glTranslated(static_cast(origin.x), + 0.0, + static_cast(origin.y)); - transformToOrigin(baseRadius, startAngle); + transformToOrigin(baseRadius, startAngle); - bool backwards = aToken.direction == cwEntryVector(); + bool backwards = aToken.direction == cwEntryVector(); - double ratio = aDelta / segmentLength(aToken); - if (backwards) - ratio = 1.0 - ratio; + double ratio = aDelta / segmentLength(aToken); + if (backwards) + ratio = 1.0 - ratio; - double angle = startAngle + (90.0 * ratio); + double angle = startAngle + (90.0 * ratio); - glRotated(angle, 0.0, 1.0, 0.0); - glTranslated(0.0, 0.0, static_cast(baseRadius - 0.5)); + glRotated(angle, 0.0, 1.0, 0.0); + glTranslated(0.0, 0.0, static_cast(baseRadius - 0.5)); - if (backwards) - glRotatef(180.0, 0, 1, 0); + if (backwards) + glRotatef(180.0, 0, 1, 0); } double CurvedTrack::segmentLength(const track::TravelToken& aToken) const { - // Assume curve is only through 90 degrees - return M_PI * (static_cast(baseRadius) - 0.5) / 2.0; + // Assume curve is only through 90 degrees + return M_PI * (static_cast(baseRadius) - 0.5) / 2.0; } // @@ -149,122 +153,122 @@ double CurvedTrack::segmentLength(const track::TravelToken& aToken) const // The vector the train is moving on if it enters clockwise Vector CurvedTrack::cwEntryVector() const { - return makeVector(-cos(degToRad(finishAngle)), 0, - sin(degToRad(finishAngle))); + return makeVector(-cos(degToRad(finishAngle)), 0, + sin(degToRad(finishAngle))); } // The vector the train is moving on if it enters counter-clockwise Vector CurvedTrack::ccwEntryVector() const { - return makeVector(cos(degToRad(startAngle)), 0.0, - -sin(degToRad(startAngle))); + return makeVector(cos(degToRad(startAngle)), 0.0, + -sin(degToRad(startAngle))); } void CurvedTrack::ensureValidDirection(const Direction& aDirection) const { - if (!isValidDirection(aDirection)) - throw runtime_error - ("Invalid direction on curved track from " - + lexical_cast(startAngle) + " to " - + lexical_cast(finishAngle) + " degrees: " - + lexical_cast(aDirection) - + " (should be " - + lexical_cast(cwEntryVector()) + " or " - + lexical_cast(ccwEntryVector()) + ")"); + if (!isValidDirection(aDirection)) + throw runtime_error + ("Invalid direction on curved track from " + + lexical_cast(startAngle) + " to " + + lexical_cast(finishAngle) + " degrees: " + + lexical_cast(aDirection) + + " (should be " + + lexical_cast(cwEntryVector()) + " or " + + lexical_cast(ccwEntryVector()) + ")"); } bool CurvedTrack::isValidDirection(const Direction& aDirection) const { - return aDirection == cwEntryVector() || aDirection == ccwEntryVector(); + return aDirection == cwEntryVector() || aDirection == ccwEntryVector(); } Connection CurvedTrack::nextPosition(const track::TravelToken& aToken) const { - bool backwards; - Vector nextDir; - if (aToken.direction == cwEntryVector()) { - nextDir = -ccwEntryVector(); - backwards = true; - } - else if (aToken.direction == ccwEntryVector()) { - nextDir = -cwEntryVector(); - backwards = false; - } - else - assert(false); - - // Assuming 90 degree curves again - const int cosEnd = static_cast(cos(degToRad(finishAngle))); - const int cosStart = static_cast(cos(degToRad(startAngle))); - const int sinEnd = static_cast(sin(degToRad(finishAngle))); - const int sinStart = static_cast(sin(degToRad(startAngle))); - - int xDelta, yDelta; - - if (backwards) - xDelta = yDelta = 0; - else { - xDelta = (baseRadius - 1) * (sinEnd - sinStart); - yDelta = (baseRadius - 1) * (cosEnd - cosStart); - } + bool backwards; + Vector nextDir; + if (aToken.direction == cwEntryVector()) { + nextDir = -ccwEntryVector(); + backwards = true; + } + else if (aToken.direction == ccwEntryVector()) { + nextDir = -cwEntryVector(); + backwards = false; + } + else + assert(false); + + // Assuming 90 degree curves again + const int cosEnd = static_cast(cos(degToRad(finishAngle))); + const int cosStart = static_cast(cos(degToRad(startAngle))); + const int sinEnd = static_cast(sin(degToRad(finishAngle))); + const int sinStart = static_cast(sin(degToRad(startAngle))); + + int xDelta, yDelta; + + if (backwards) + xDelta = yDelta = 0; + else { + xDelta = (baseRadius - 1) * (sinEnd - sinStart); + yDelta = (baseRadius - 1) * (cosEnd - cosStart); + } - return make_pair(makePoint(origin.x + xDelta + nextDir.x, - origin.y + yDelta + nextDir.z), - nextDir); + return make_pair(makePoint(origin.x + xDelta + nextDir.x, + origin.y + yDelta + nextDir.z), + nextDir); } void CurvedTrack::getEndpoints(list >& aList) const { - aList.push_back(origin); + aList.push_back(origin); - // Assuming 90 degree curves again - const int cosEnd = static_cast(cos(degToRad(finishAngle))); - const int cosStart = static_cast(cos(degToRad(startAngle))); - const int sinEnd = static_cast(sin(degToRad(finishAngle))); - const int sinStart = static_cast(sin(degToRad(startAngle))); + // Assuming 90 degree curves again + const int cosEnd = static_cast(cos(degToRad(finishAngle))); + const int cosStart = static_cast(cos(degToRad(startAngle))); + const int sinEnd = static_cast(sin(degToRad(finishAngle))); + const int sinStart = static_cast(sin(degToRad(startAngle))); - const int xDelta = (baseRadius - 1) * (sinEnd - sinStart); - const int yDelta = (baseRadius - 1) * (cosEnd - cosStart); + const int xDelta = (baseRadius - 1) * (sinEnd - sinStart); + const int yDelta = (baseRadius - 1) * (cosEnd - cosStart); - aList.push_back(makePoint(origin.x + xDelta, origin.y + yDelta)); + aList.push_back(makePoint(origin.x + xDelta, origin.y + yDelta)); } ITrackSegmentPtr CurvedTrack::mergeExit(const Point& aPoint, - const track::Direction& aDirection) + const track::Direction& aDirection) { - // See if this is already an exit - if (isValidDirection(aDirection)) { - list > exits; - getEndpoints(exits); - - for (list >::iterator it = exits.begin(); - it != exits.end(); ++it) - if (*it == aPoint) - return shared_from_this(); - } - - // No way to merge this as an exit - return ITrackSegmentPtr(); + // See if this is already an exit + if (isValidDirection(aDirection)) { + list > exits; + getEndpoints(exits); + + for (list >::iterator it = exits.begin(); + it != exits.end(); ++it) + if (*it == aPoint) + return shared_from_this(); + } + + // No way to merge this as an exit + return ITrackSegmentPtr(); } void CurvedTrack::render() const { - renderCurvedTrack(baseRadius, startAngle, finishAngle); + renderCurvedTrack(baseRadius, startAngle, finishAngle); } xml::element CurvedTrack::toXml() const { - return xml::element("curvedTrack") - .addAttribute("startAngle", startAngle) - .addAttribute("finishAngle", finishAngle) - .addAttribute("radius", baseRadius); + return xml::element("curvedTrack") + .addAttribute("startAngle", startAngle) + .addAttribute("finishAngle", finishAngle) + .addAttribute("radius", baseRadius); } ITrackSegmentPtr makeCurvedTrack(track::Angle aStartAngle, - track::Angle aFinishAngle, int aRadius) + track::Angle aFinishAngle, int aRadius) { - assert(aStartAngle < aFinishAngle); + assert(aStartAngle < aFinishAngle); - return ITrackSegmentPtr - (new CurvedTrack(aStartAngle, aFinishAngle, aRadius)); + return ITrackSegmentPtr + (new CurvedTrack(aStartAngle, aFinishAngle, aRadius)); } diff --git a/src/Points.cpp b/src/Points.cpp index 7f33fdd..ef69805 100644 --- a/src/Points.cpp +++ b/src/Points.cpp @@ -43,6 +43,10 @@ public: xml::element toXml() const; track::TravelToken getTravelToken(track::Position aPosition, track::Direction aDirection) const; + void nextState(); + void prevState(); + bool hasMultipleStates() const { return true; } + private: void transform(const track::TravelToken& aToken, double aDelta) const; void ensureValidDirection(track::Direction aDirection) const; @@ -452,6 +456,16 @@ xml::element Points::toXml() const .addAttribute("reflect", amReflected); } +void Points::nextState() +{ + +} + +void Points::prevState() +{ + +} + ITrackSegmentPtr makePoints(track::Direction aDirection, bool reflect) { return ITrackSegmentPtr(new Points(aDirection, reflect)); diff --git a/src/StraightTrack.cpp b/src/StraightTrack.cpp index e161436..1923864 100644 --- a/src/StraightTrack.cpp +++ b/src/StraightTrack.cpp @@ -53,6 +53,11 @@ public: track::TravelToken getTravelToken(track::Position aPosition, track::Direction aDirection) const; xml::element toXml() const; + + bool hasMultipleStates() const { return false; } + void nextState() {} + void prevState() {} + private: void transform(const track::TravelToken& aToken, double aDelta) const; void ensureValidDirection(const track::Direction& aDirection) const; -- 2.39.2