/// <summary> /// Add an additional node, from the current last node along the next TrackNodeVector (given by index) /// The added node will always be a junction node. /// </summary> /// <param name="lastNode">Currently last node of path</param> /// <param name="nextTvnIndex">TrackNodeVector index along which to place the track</param> /// <param name="isMainPath">Are we adding a node on the main path (alternative is passing path)</param> /// <returns>The newly created junction path node</returns> TrainpathJunctionNode AddAdditionalJunctionNode(TrainpathNode lastNode, int nextTvnIndex, bool isMainPath) { // we add a new activeNodeAsJunction TrainpathJunctionNode newNode = new TrainpathJunctionNode(lastNode); if (TrackExtensions.TrackNode(nextTvnIndex) == null) { return(null); // apparently there is some issue in the track. } newNode.JunctionIndex = lastNode.GetNextJunctionIndex(nextTvnIndex); newNode.SetLocationFromTrackNode(); // simple linking newNode.PrevNode = lastNode; if (isMainPath) { lastNode.NextMainTvnIndex = nextTvnIndex; lastNode.NextMainNode = newNode; } else { lastNode.NextSidingTvnIndex = nextTvnIndex; lastNode.NextSidingNode = newNode; } newNode.SetFacingPoint(); newNode.DetermineOrientation(lastNode, nextTvnIndex); NetNodesAdded++; return(newNode); }
/// <summary> /// Make sure the junction nodes of have the exact location of the junctions in the track database. /// This is to make sure changes in the track database are taken over in the path /// </summary> void SnapAllJunctionNodes() { TrainpathNode mainNode = CurrentTrainPath.FirstNode; while (mainNode != null) { //siding path. For this routine we do not care if junctions are done twice TrainpathNode sidingNode = mainNode.NextSidingNode; while (sidingNode != null) { TrainpathJunctionNode sidingNodeAsJunction = sidingNode as TrainpathJunctionNode; if ((sidingNodeAsJunction != null) && !sidingNode.IsBroken) { sidingNode.Location = DrawTrackDB.UidLocation(trackDB.TrackNodes[sidingNodeAsJunction.JunctionIndex].UiD); } sidingNode = sidingNode.NextSidingNode; } TrainpathJunctionNode mainNodeAsJunction = mainNode as TrainpathJunctionNode; if ((mainNodeAsJunction != null) && !mainNode.IsBroken) { mainNode.Location = DrawTrackDB.UidLocation(trackDB.TrackNodes[mainNodeAsJunction.JunctionIndex].UiD); } mainNode = mainNode.NextMainNode; } }
/// <summary> /// Make a shallow copy with all links to other nodes set to null; /// </summary> /// <returns>a copy of this node</returns> public override TrainpathNode ShallowCopyNoLinks() { TrainpathJunctionNode newNode = (TrainpathJunctionNode)this.MemberwiseClone(); newNode.NextMainNode = null; newNode.NextSidingNode = null; newNode.PrevNode = null; return(newNode); }
/// <summary> /// Create a (still unlinked) node halfway through the next section (so halfway between this /// and the next junction. Needed specially for disambiguity. /// </summary> /// <param name="junctionNode">The junction node where we start</param> /// <param name="tvnIndex">The TrackVectorNode index for the path</param> /// <returns>An unlinked vectorNode at the midpoint.</returns> private TrainpathVectorNode CreateHalfWayNode(TrainpathJunctionNode junctionNode, int tvnIndex) { // The idea here is to use all the code in traveller to make life easier. // move the traveller halfway through the next vector section Traveller traveller = junctionNode.PlaceTravellerAfterJunction(tvnIndex); float distanceToTravel = traveller.TrackNodeLength / 2; traveller.Move(distanceToTravel); TrainpathVectorNode halfwayNode = new TrainpathVectorNode(junctionNode, traveller); halfwayNode.DetermineOrientation(junctionNode, tvnIndex); return(halfwayNode); }
/// <summary> /// For all the junction nodes, set whether it is a facing point or not /// </summary> /// <param name="Nodes">The list of path nodes that now need to be linked</param> static private void SetFacingPoints(List <TrainpathNode> Nodes) { // It is just a convenience to use the list of Nodes. // In principle this can be done without the list by following the path for (int i = 0; i < Nodes.Count; i++) { TrainpathJunctionNode node = Nodes[i] as TrainpathJunctionNode; if (node == null) { continue; } node.SetFacingPoint(); } }
/// <summary> /// Determine whether this node is earlier on a track than the given otherNode. Earlier here is defined /// in terms of the track orientation itself (so not in terms of the direction of a path). /// </summary> /// <param name="otherNode">Other node to compare against</param> /// <returns>true if this node is earlier on the track.</returns> public bool IsEarlierOnTrackThan(TrainpathNode otherNode) { TrainpathJunctionNode otherJunctionNode = otherNode as TrainpathJunctionNode; if (otherJunctionNode != null) { return(otherJunctionNode.JunctionIndex == TrackDB.TrackNodes[TvnIndex].JunctionIndexAtEnd()); } TrainpathVectorNode otherVectorNode = otherNode as TrainpathVectorNode; return((TrackVectorSectionIndex < otherVectorNode.TrackVectorSectionIndex) || ((TrackVectorSectionIndex == otherVectorNode.TrackVectorSectionIndex) && (TrackSectionOffset < otherVectorNode.TrackSectionOffset))); }
/// <summary> /// Check if after this node a disambiguity node needs to be added /// </summary> /// <param name="currentNode"></param> public void AddDisambiguityNodeIfNeeded(TrainpathNode currentNode) { //Check if we need to add an disambiguity node TrainpathJunctionNode currentNodeAsJunction = currentNode as TrainpathJunctionNode; if ((currentNodeAsJunction != null) && (currentNode.NextMainNode != null) && (currentNode.NextMainNode is TrainpathJunctionNode) && (currentNodeAsJunction.IsSimpleSidingStart()) ) { TrainpathVectorNode halfwayNode = CreateHalfWayNode(currentNodeAsJunction, currentNodeAsJunction.NextMainTvnIndex); halfwayNode.PrevNode = currentNode; AddIntermediateMainNode(halfwayNode); } }
/// <summary> /// Add a single TrPathNode. Make sure the pdp's are updated as needed. /// </summary> /// <param name="node">path node, needed for location, and various flags</param> /// <param name="nextMainIndex">Index of the next main node</param> /// <param name="nextSidingIndex">Index of the next siding node</param> private static void AddNode(TrainpathNode node, uint nextMainIndex, uint nextSidingIndex) { int pdpIndex; string trackPDPstart = String.Format(System.Globalization.CultureInfo.InvariantCulture, "\tTrackPDP ( {0,6:D} {1,6:D} {2,9} {3,9:F3} {4,9:F3}", node.Location.TileX, node.Location.TileZ, node.Location.Location.X.ToString("F3", System.Globalization.CultureInfo.CreateSpecificCulture("en-US")), node.Location.Location.Y.ToString("F3", System.Globalization.CultureInfo.CreateSpecificCulture("en-US")), node.Location.Location.Z.ToString("F3", System.Globalization.CultureInfo.CreateSpecificCulture("en-US"))); pdpIndex = trackPDPs.Count(); // default PDP index TrainpathJunctionNode nodeAsJunction = node as TrainpathJunctionNode; if (nodeAsJunction != null) { int junctionIndex = nodeAsJunction.JunctionIndex; if (!node.IsBroken && pdpOfJunction.ContainsKey(junctionIndex)) { //this junction is already in the list of PDPs, so use another PDP index; pdpIndex = pdpOfJunction[junctionIndex]; } else { trackPDPs.Add(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2} )", trackPDPstart, 2, 0)); pdpOfJunction[junctionIndex] = pdpIndex; } } else { // TrainpathVectorNode if (node.NodeType == TrainpathNodeType.Start || node.NodeType == TrainpathNodeType.End) { trackPDPs.Add(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2} )", trackPDPstart, 1, 0)); } else { trackPDPs.Add(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} {2} )", trackPDPstart, 1, 1)); } } trpathnodes.Add(String.Format(System.Globalization.CultureInfo.InvariantCulture, "\t\tTrPathNode ( {0} {1} {2} {3} )", node.FlagsToString(), nextMainIndex, nextSidingIndex, pdpIndex)); }
/// <summary> /// Determine the details of junction from which we connect /// </summary> private void DetermineJunction() { OriginalNodeAsVector = OriginalNode as TrainpathVectorNode; if (OriginalNodeAsVector != null) { DetermineJunctionForVectorNode(); } else { TrainpathJunctionNode nodeAsJunction = OriginalNode as TrainpathJunctionNode; // cannot be null this.ConnectingJunctionIndex = nodeAsJunction.JunctionIndex; this.IsConnectingJunctionFacing = nodeAsJunction.IsFacingPoint; } if (!IsConnectingForward) { this.IsConnectingJunctionFacing = !this.IsConnectingJunctionFacing; } }
/// <summary> /// Try to find the index of the vector node connecting this path node to the (given) nextNode. /// </summary> /// <returns>The index of the vector node connection, or -1</returns> public override int FindTvnIndex(TrainpathNode nextNode) { TrainpathVectorNode nextAsVectorNode = nextNode as TrainpathVectorNode; if ((nextAsVectorNode != null) && (this.TvnIndex == nextAsVectorNode.TvnIndex)) { // two vector nodes, tvn indices must be the same return(this.TvnIndex); } TrainpathJunctionNode nextAsJunctionNode = nextNode as TrainpathJunctionNode; if ((nextAsJunctionNode != null) && nextAsJunctionNode.ConnectsToTrack(TvnIndex)) { // vector node to junction node, junction must connect to tvnIndex return(this.TvnIndex); } //The nodes themselves might not be broken, but the link between them is. return(-1); }
/// <summary> /// Add an additional node starting at the given node, following the TvnIndex, /// but take care of a possible need for disambiguity. /// </summary> /// <param name="lastNode">Node after which a new node needs to be added</param> /// <param name="tvnIndex">TrackVectorNode index of the track the path needs to be on</param> /// <param name="isMainPath">Do we add the node to the main path or not</param> /// <returns>The newly created path node</returns> public TrainpathNode AddAdditionalNode(TrainpathNode lastNode, int tvnIndex, bool isMainPath) { TrainpathVectorNode lastNodeAsVector = lastNode as TrainpathVectorNode; if (lastNodeAsVector != null) { return(AddAdditionalJunctionNode(lastNode, lastNodeAsVector.TvnIndex, isMainPath)); } TrainpathJunctionNode junctionNode = lastNode as TrainpathJunctionNode; if (junctionNode.IsSimpleSidingStart()) { // start of a simple siding. So the next node should be a node to remove disambiguity. TrainpathVectorNode halfwayNode = CreateHalfWayNode(junctionNode, tvnIndex); return(AddAdditionalVectorNode(junctionNode, halfwayNode, isMainPath)); } else { return(AddAdditionalJunctionNode(junctionNode, tvnIndex, isMainPath)); } }
/// <summary> /// Add an additional node, where the next track node is not yet given. /// </summary> /// <param name="lastNode">currently last node</param> /// <param name="isMainPath">Do we add the node to the main path or not</param> /// <returns>The newly created (unlinked) path node</returns> public TrainpathNode AddAdditionalNode(TrainpathNode lastNode, bool isMainPath) { TrainpathVectorNode lastNodeAsVector = lastNode as TrainpathVectorNode; if (lastNodeAsVector != null) { return(AddAdditionalNode(lastNode, lastNodeAsVector.TvnIndex, isMainPath)); } TrainpathJunctionNode junctionNode = lastNode as TrainpathJunctionNode; if (junctionNode.IsEndNode) { return(null); // if it happens to be the end of a path, forget about it. } if (junctionNode.IsFacingPoint) { return(AddAdditionalNode(lastNode, junctionNode.MainTvn, isMainPath)); } else { return(AddAdditionalNode(lastNode, junctionNode.TrailingTvn, isMainPath)); } }
/// <summary> /// Create a partial path from the current node, along the new track vector index, until we can reconnect again /// </summary> /// <param name="currentNode">Starting place of the new partial path</param> /// <param name="newTvnIndex">Index of the new track vector node along which the path starts</param> /// <param name="reconnectNode">Node at we will reconnect to current path again</param> /// <param name="isMainPath">Do we add the node to the main path or not</param> /// <returns>The last node on the partial path, just before the reconnect node.</returns> public TrainpathNode CreatePartialPath(TrainpathNode currentNode, int newTvnIndex, TrainpathJunctionNode reconnectNode, bool isMainPath) { bool newHasSidingPath = isMainPath && currentNode.HasSidingPath; currentNode.HasSidingPath = newHasSidingPath; TrainpathNode newNode = AddAdditionalNode(currentNode, newTvnIndex, isMainPath); do { TrainpathJunctionNode newNodeAsJunction = newNode as TrainpathJunctionNode; if (newNodeAsJunction != null && (newNodeAsJunction.JunctionIndex == reconnectNode.JunctionIndex)) { //we have reached the reconnection point break; } currentNode = newNode; currentNode.HasSidingPath = newHasSidingPath; newNode = AddAdditionalNode(currentNode, isMainPath); } while (newNode != null); // if we get here, something is wrong, because we checked we could reconnect // The returned node will not be the last node created, because that one is at the same location as the reconnect node return(currentNode); }
/// <summary> /// Find the nodes that can be used to relink for a siding path, or a 'take-other-exit' path, ... /// The reconnecing nodes all have to be before the first special node (wait, uncouple, reverse, end). /// They also have to be before the end of the (current path), even if it does not have a formal end, /// and they have to be before a possible next siding start. At last, it needs to be a non-facing junction. /// (similar conditions apply for searching backwards. /// </summary> /// <param name="startNode">Node on train path to start searching</param> /// <param name="searchForward">Do you want the reconnect nodes forward or backwards along the path?</param> /// <param name="includeLastVectorNode">Is a vectorNode (start, end, wait, reverse) allowed?</param> /// <returns>List of possible reconnect nodes. Might be empty</returns> public List <TrainpathNode> FindReconnectNodeCandidates(TrainpathNode startNode, bool searchForward, bool includeLastVectorNode) { List <TrainpathNode> reconnectNodeCandidates = new List <TrainpathNode>(); TrainpathNode mainNode = startNode; //follow the train path and see what we find while (true) { mainNode = searchForward ? mainNode.NextMainNode : mainNode.PrevNode; if (mainNode == null) { break; } TrainpathJunctionNode mainNodeAsJunction = mainNode as TrainpathJunctionNode; if (mainNodeAsJunction == null) { if (mainNode.NodeType != TrainpathNodeType.Other) { // if it is not an other-node (so not a disambiguity node), stop searching if (includeLastVectorNode) { reconnectNodeCandidates.Add(mainNode); } break; } } else { if (searchForward) { if (mainNode.NodeType == TrainpathNodeType.SidingStart) { // if a new siding path is started, stop searching // But nevertheless, it is still possible to connect a vector node here reconnectNodeCandidates.Add(mainNode); break; } if (mainNode.NodeType == TrainpathNodeType.SidingEnd) { // A siding end already has already both an incoming main and incoming end node // So there is no way to reconnect to an empty track break; } if (!mainNodeAsJunction.IsFacingPoint) { // add the trailing junction. reconnectNodeCandidates.Add(mainNode); } } else // searching backward { if (mainNode.NodeType == TrainpathNodeType.SidingEnd) { // if a new siding path is started (looking backwards), stop searching // But nevertheless, it is still possible to connect a vector node here reconnectNodeCandidates.Add(mainNode); break; } if (mainNode.NodeType == TrainpathNodeType.SidingStart) { // Searching back, a siding start has already two outgoing tracks. so there is no free track we could use break; } if (mainNodeAsJunction.IsFacingPoint) { // add the facing junction. reconnectNodeCandidates.Add(mainNode); } } } } return(reconnectNodeCandidates); }
/// <summary> /// Draw a path on a vector node, meaning that the vector node will be drawn (possibly partly), in path colors /// </summary> /// <param name="drawArea">Area to draw upon</param> /// <param name="colors">Colorscheme to use</param> /// <param name="currentNode">Current path node</param> /// <param name="nextNode">Next path Node</param> /// <param name="TvnIndex">The index of the track vector node that is between the two path nodes</param> /// <remarks>Note that it is not clear yet whether the direction of current to next is the same as the /// direction of the vector node</remarks> private void DrawPathOnVectorNode(DrawArea drawArea, ColorScheme colors, TrainpathNode currentNode, TrainpathNode nextNode, int TvnIndex) { if (currentNode.IsBrokenOffTrack || nextNode.IsBrokenOffTrack || (TvnIndex == -1)) { DrawPathBrokenNode(drawArea, colors, currentNode, nextNode); return; } TrackNode tn = trackDB.TrackNodes[TvnIndex]; TrainpathJunctionNode nextJunctionNode = nextNode as TrainpathJunctionNode; TrainpathVectorNode nextVectorNode = nextNode as TrainpathVectorNode; //Default situation (and most occuring) is to draw the complete vector node int tvsiStart = 0; int tvsiStop = tn.TrVectorNode.TrVectorSections.Length-1; float sectionOffsetStart = 0; float sectionOffsetStop = -1; if (currentNode is TrainpathJunctionNode) { // If both ends are junctions, just draw the full track. Otherwise: if (nextVectorNode != null) { // Draw from the current junction node to the next mid-point node if (nextVectorNode.IsEarlierOnTrackThan(currentNode)) { // trackvectornode is oriented the other way as path tvsiStart = nextVectorNode.TrackVectorSectionIndex; sectionOffsetStart = nextVectorNode.TrackSectionOffset; } else { // trackvectornode is oriented in the same way as path tvsiStop = nextVectorNode.TrackVectorSectionIndex; sectionOffsetStop = nextVectorNode.TrackSectionOffset; } } } else { TrainpathVectorNode currentVectorNode = currentNode as TrainpathVectorNode; if (nextJunctionNode != null) { // Draw from current mid-point node to next junction node if (currentVectorNode.IsEarlierOnTrackThan(nextNode)) { // trackvectornode is oriented in the same way as path tvsiStart = currentVectorNode.TrackVectorSectionIndex; sectionOffsetStart = currentVectorNode.TrackSectionOffset; } else { // trackvectornode is oriented the other way around. tvsiStop = currentVectorNode.TrackVectorSectionIndex; sectionOffsetStop = currentVectorNode.TrackSectionOffset; } } if (nextVectorNode != null) { // Draw from a current vector node to the next vector node, e.g. for multiple wait points if (currentVectorNode.IsEarlierOnTrackThan(nextVectorNode)) { // from current to next is in the direction of the vector node tvsiStart = currentVectorNode.TrackVectorSectionIndex; tvsiStop = nextVectorNode.TrackVectorSectionIndex; sectionOffsetStart = currentVectorNode.TrackSectionOffset; sectionOffsetStop = nextVectorNode.TrackSectionOffset; } else { // from next to current is in the direction of the vector node tvsiStart = nextVectorNode.TrackVectorSectionIndex; tvsiStop = currentVectorNode.TrackVectorSectionIndex; sectionOffsetStart = nextVectorNode.TrackSectionOffset; sectionOffsetStop = currentVectorNode.TrackSectionOffset; } } } DrawVectorNode(drawArea, tn, colors, tvsiStart, tvsiStop, sectionOffsetStart, sectionOffsetStop); }