/// <summary> /// Constructor /// </summary> /// <param name="node">The original trainpath node</param> /// <param name="isAFromNode">In a reconnect path, is this the junction from which to connect, or the junction to which to connect</param> /// <param name="isConnectingForward">Is this node connecting forwards (along the path) or not</param> public ConnectableNode(TrainpathNode node, bool isAFromNode, bool isConnectingForward) { OriginalNode = node; IsFrom = isAFromNode; IsConnectingForward = isConnectingForward; DetermineJunction(); }
/// <summary> /// Link the various nodes to each other. Do some initial processing on the path, like finding linking TVNs /// and determining whether junctions are facing or not. /// </summary> /// <param name="patFile">Patfile object containing the various unprocessed Track Path Nodes</param> /// <param name="Nodes">The list of as-of-yet unlinked processed path nodes</param> static private void LinkNodes(PathFile patFile, List <TrainpathNode> Nodes) { // Connect the various nodes to each other for (int i = 0; i < Nodes.Count; i++) { TrainpathNode node = Nodes[i]; TrPathNode tpn = patFile.TrPathNodes[i]; // find TvnIndex to next main node. if (tpn.HasNextMainNode) { node.NextMainNode = Nodes[(int)tpn.nextMainNode]; node.NextMainNode.PrevNode = node; node.NextMainTvnIndex = node.FindTvnIndex(node.NextMainNode); } // find TvnIndex to next siding node if (tpn.HasNextSidingNode) { node.NextSidingNode = Nodes[(int)tpn.nextSidingNode]; if (node.NextSidingNode.PrevNode == null) { node.NextSidingNode.PrevNode = node; } node.NextSidingTvnIndex = node.FindTvnIndex(node.NextSidingNode); } if (node.NextMainNode != null && node.NextSidingNode != null) { node.NodeType = TrainpathNodeType.SidingStart; } } }
/// <summary> /// Can a node in a path be reversed without breaking something? /// </summary> /// <param name="node"></param> /// <returns></returns> private static bool CanReverse(TrainpathNode node) { bool outgoingAllowsReversal; if (node.NextSidingNode != null) { // if there is a siding node, this is a siding start (probably) and things become too complex outgoingAllowsReversal = false; } else { if (node.NextMainNode == null) { // no next main node, so we are fine with reversing outgoingAllowsReversal = true; } else { outgoingAllowsReversal = node.NextMainNode.IsBroken || (node.NextMainTvnIndex == -1); } } bool incomingAllowsReversal; if (node.PrevNode == null) { incomingAllowsReversal = true; } else { incomingAllowsReversal = node.PrevNode.IsBroken || (node.PrevNode.NextMainTvnIndex == -1); } return(incomingAllowsReversal && outgoingAllowsReversal); }
/// <summary> /// Calculate the number of this node in the total path. FirstNode is 1. /// </summary> /// <param name="node">Node for which to calculate the number</param> /// <returns>The sequential number of the node in the path. -1 if not found</returns> public int GetNodeNumber(TrainpathNode node) { // first backup till we are on main track. int nodesOnSidingPath = 0; while (node.NextMainNode == null) { node = node.PrevNode; nodesOnSidingPath++; if (node == null) { return(-1); } } int nodesOnMainPath = 1; TrainpathNode mainNode = FirstNode; while (mainNode != null && mainNode != node) { nodesOnMainPath++; mainNode = mainNode.NextMainNode; } if (mainNode == null) { return(-1); } return(nodesOnSidingPath + nodesOnMainPath); }
/// <summary> /// Determine the orientation of the current node, by using the previousNode as well as the TVN that links the /// previous node with this node. /// </summary> /// <param name="previousNode">previouse node</param> /// <param name="linkingTvnIndex">the index of the Track Vector Node linking the previous node to this node</param> public override void DetermineOrientation(TrainpathNode previousNode, int linkingTvnIndex) { // the TVN is from the previous node, so backwards. Therefore:reverse if (DetermineOrientationSucceeded(linkingTvnIndex, true)) { return; } // if it did not succeed, most likely previous node is broken. // Retry with next main or siding TVN. This will fail for the last node, so be it.) if (NextSidingNode != null) { linkingTvnIndex = NextSidingTvnIndex; } if (NextMainNode != null) { linkingTvnIndex = NextMainTvnIndex; } // might override result from previous line if (DetermineOrientationSucceeded(linkingTvnIndex, false)) // no reverse needed { return; } // nothing seems to work. Get default value, unless it is broken if (IsBroken) { return; } DetermineOrientationSucceeded(TrailingTvn, IsFacingPoint); }
/// <summary> /// Constructor where location is copied from the given traveller /// </summary> /// <param name="otherNode">just another node to have access to trackDB and tsectiondat</param> /// <param name="traveller">The traveller that contains the exact location and distance on track to initialize the node</param> public TrainpathVectorNode(TrainpathNode otherNode, Traveller traveller) : base(otherNode) { CopyDataFromTraveller(traveller); Location = traveller.WorldLocation; // Not part of CopyDataFromTraveller ForwardOriented = true; // only initial setting }
/// <summary> /// Try to fix all broken nodes. Even if a node cannot be fixed, do try to fix the others. /// </summary> /// <returns>Whether all nodes were fixed and hence the path is now fine</returns> public bool AutoFixAllBrokenNodes() { bool fixSucceeded = true; int nodeToTry = 0; Collection <TrainpathNode> brokenNodes = CurrentTrainPath.GetBrokenNodes(); EditorActionAutoFixBrokenNodes actionFixBroken = new EditorActionAutoFixBrokenNodes(); EditorActionFixInvalidNode actionFixInvalid = new EditorActionFixInvalidNode(); while (CurrentTrainPath.IsBroken && (nodeToTry < brokenNodes.Count)) { brokenNodes = CurrentTrainPath.GetBrokenNodes(); TrainpathNode nodeToFix = brokenNodes[nodeToTry]; bool canExecuteBroken = actionFixBroken.MenuState(CurrentTrainPath, nodeToFix, null, UpdateAfterEdits, 0, 0); bool canExecuteInvalid = actionFixInvalid.MenuState(CurrentTrainPath, nodeToFix, null, UpdateAfterEdits, 0, 0); if (canExecuteBroken) { actionFixBroken.DoAction(); brokenNodes = CurrentTrainPath.GetBrokenNodes(); } else if (canExecuteInvalid) { actionFixInvalid.DoAction(); brokenNodes = CurrentTrainPath.GetBrokenNodes(); } else { fixSucceeded = false; nodeToTry++; } } return(fixSucceeded); }
/// <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) { // from junction to vector node. if (this.ConnectsToTrack(nextAsVectorNode.TvnIndex)) { return(nextAsVectorNode.TvnIndex); } else { //node is perhaps not broken, but connecting track is return(-1); } } //both this node and the next node are junctions: find the vector node connecting them. //Probably this can be faster, by just finding the TrPins from this and next junction and find the common one. int nextJunctionIndex = (nextNode as TrainpathJunctionNode).JunctionIndex; for (int i = 0; i < TrackDB.TrackNodes.Count(); i++) { TrackNode tn = TrackDB.TrackNodes[i]; if (tn == null || tn.TrVectorNode == null) { continue; } if ((tn.JunctionIndexAtStart() == this.JunctionIndex && tn.JunctionIndexAtEnd() == nextJunctionIndex) || (tn.JunctionIndexAtEnd() == this.JunctionIndex && tn.JunctionIndexAtStart() == nextJunctionIndex)) { return(i); } } return(-1); }
/// <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> /// 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> /// Find all nodes that are the end of a siding (so where main path and siding path come together again) /// </summary> private void FindSidingEnds() { TrainpathNode curSidingEnd = null; // if we are still looking for a sidingEnd for (TrainpathNode curMainNode = FirstNode; curMainNode != null; curMainNode = curMainNode.NextMainNode) { if (curSidingEnd != null) { if (curMainNode == curSidingEnd) { // end of siding curSidingEnd = null; } else { curMainNode.HasSidingPath = true; } } TrainpathNode curSidingNode = curMainNode.NextSidingNode; while (curSidingNode != null && curSidingNode.NextSidingNode != null) { curSidingNode = curSidingNode.NextSidingNode; } if (curSidingNode != null) { curSidingEnd = curSidingNode; curSidingEnd.NodeType = TrainpathNodeType.SidingEnd; curMainNode.HasSidingPath = true; } } }
/// <summary> /// Create the initial list of nodes from the patFile. No linking or preoccessing /// </summary> /// <param name="patFile">Patfile object containing the various unprocessed Track Path Nodes</param> /// <param name="Nodes">The list that is going to be filled with as-of-yet unlinked and almost unprocessed path nodes</param> private void CreateNodes(PathFile patFile, List <TrainpathNode> Nodes) { foreach (TrPathNode tpn in patFile.TrPathNodes) { Nodes.Add(TrainpathNode.CreatePathNode(tpn, patFile.TrackPDPs[(int)tpn.fromPDP], trackDB, tsectionDat)); } FirstNode = Nodes[0]; FirstNode.NodeType = TrainpathNodeType.Start; }
/// <summary> /// Add zero or more additional main nodes /// </summary> /// <param name="lastNode">currently last node</param> /// <param name="numberOfNodesToAdd">The number of nodes to add</param> public void AddAdditionalMainNodes(TrainpathNode lastNode, int numberOfNodesToAdd) { int wantedNetNodesAdded = NetNodesAdded + numberOfNodesToAdd; while (NetNodesAdded < wantedNetNodesAdded && lastNode != null) { lastNode = AddAdditionalNode(lastNode, true); } }
/// <summary> /// Add the fromNode and toNode to the list of drawn nodes indexed for the trackindex /// </summary> /// <param name="fromNode">The starting node of a drawn path-section</param> /// <param name="toNode">The end node of a drawn path-section</param> public void NoteAsDrawn(TrainpathNode fromNode, TrainpathNode toNode) { if (!DrawnTrackIndexes.ContainsKey(fromNode.NextMainTvnIndex)) { DrawnTrackIndexes[fromNode.NextMainTvnIndex] = new List<TrainpathNode>(); } DrawnTrackIndexes[fromNode.NextMainTvnIndex].Add(fromNode); DrawnTrackIndexes[fromNode.NextMainTvnIndex].Add(toNode); }
/// <summary> /// Find all broken nodes of a path /// </summary> /// <returns>A collection of the broken nodes</returns> public Collection <TrainpathNode> GetBrokenNodes() { var brokenNodes = new Collection <TrainpathNode>(); if (FirstNode == null) { return(brokenNodes); } TrainpathNode currentMainNode = FirstNode; while (currentMainNode.NextMainNode != null) { if (currentMainNode.IsBroken) { brokenNodes.Add(currentMainNode); } else if (currentMainNode.NextMainTvnIndex == -1) { brokenNodes.Add(currentMainNode.NextMainNode); } else { // For siding paths, it is difficult to get the right main node to draw until // Most important however is that at least IsBroken is set correctly TrainpathNode currentSidingNode = currentMainNode; while (currentSidingNode.NextSidingNode != null) { if (currentSidingNode.NextSidingNode.IsBroken) { brokenNodes.Add(currentMainNode.NextMainNode); // we cannot draw until a sidingNode } if (currentSidingNode.NextSidingTvnIndex == -1) { brokenNodes.Add(currentMainNode.NextMainNode); } currentSidingNode = currentSidingNode.NextSidingNode; if (currentSidingNode.NextSidingNode == null && currentSidingNode.NodeType != TrainpathNodeType.SidingEnd) { // The end of a siding track while still not on siding end brokenNodes.Add(currentMainNode.NextMainNode); } } } currentMainNode = currentMainNode.NextMainNode; } if (currentMainNode.IsBroken) { //for last node brokenNodes.Add(currentMainNode); } return(brokenNodes); }
/// <summary> /// Add information from Trainpaths /// </summary> /// <param name="trackViewer"></param> private void AddTrainpathStatus(TrackViewer trackViewer) { if (Properties.Settings.Default.statusShowTrainpath && (trackViewer.PathEditor != null)) { if (trackViewer.PathEditor.HasValidPath) { //gather some info on path status List <string> statusItems = new List <string>(); if (trackViewer.PathEditor.HasEndingPath) { statusItems.Add("good end"); } if (trackViewer.PathEditor.HasBrokenPath) { statusItems.Add("broken"); } if (trackViewer.PathEditor.HasModifiedPath) { statusItems.Add("modified"); } if (trackViewer.PathEditor.HasStoredTail) { statusItems.Add("stored tail"); } string pathStatus = String.Join(", ", statusItems.ToArray()); ORTS.TrackViewer.Editing.TrainpathNode curNode = trackViewer.PathEditor.CurrentNode; statusAdditional.Text += string.Format(System.Globalization.CultureInfo.CurrentCulture, " {0} ({4}): TVNs=[{1} {2}] (type={3})", trackViewer.PathEditor.FileName, curNode.NextMainTvnIndex, curNode.NextSidingTvnIndex, curNode.NodeType, pathStatus); if (curNode.IsBroken) { statusAdditional.Text += string.Format(System.Globalization.CultureInfo.CurrentCulture, " Broken: {0} ", curNode.BrokenStatusString()); } TrainpathVectorNode curVectorNode = curNode as TrainpathVectorNode; if (curVectorNode != null && curNode.NodeType == TrainpathNodeType.Stop) { statusAdditional.Text += string.Format(System.Globalization.CultureInfo.CurrentCulture, " (wait-time={0}s)", curVectorNode.WaitTimeS); } } else { statusAdditional.Text += "Invalid path"; } } }
/// <summary> /// Reverse the path, including the metadata. /// Assumption is that it is not broken, and that both start and end are given /// </summary> public void ReversePath() { this.PathId = "new"; string oldStart = this.PathStart; this.PathStart = this.PathEnd; this.PathEnd = oldStart; this.PathName += " (reversed)"; List <TrainpathNode> mainNodes = new List <TrainpathNode>(); // Create list of nodes, in new order TrainpathNode currentMainNode = this.FirstNode; mainNodes.Add(currentMainNode); while (currentMainNode.NextMainNode != null) { mainNodes.Add(currentMainNode.NextMainNode); currentMainNode = currentMainNode.NextMainNode; } mainNodes.Reverse(); int lastIndex = mainNodes.Count() - 1; // we now this is at least 1 //new start this.FirstNode = mainNodes[0]; mainNodes[0].NextMainNode = mainNodes[1]; mainNodes[0].NextMainTvnIndex = mainNodes[1].NextMainTvnIndex; // note main TVN index was in reverse direction mainNodes[0].PrevNode = null; mainNodes[0].ReverseOrientation(); mainNodes[0].NodeType = TrainpathNodeType.Start; //all intermediate nodes for (int i = 1; i < lastIndex; i++) { mainNodes[i].NextMainNode = mainNodes[i + 1]; mainNodes[i].NextMainTvnIndex = mainNodes[i + 1].NextMainTvnIndex; // note main TVN index was in reverse direction mainNodes[i].PrevNode = mainNodes[i - 1]; if (mainNodes[i].NodeType != TrainpathNodeType.Reverse) { // reverse nodes have input and output swapped, but they are not changed themselves! mainNodes[i].ReverseOrientation(); } if (mainNodes[i].NodeType == TrainpathNodeType.SidingStart) { ReverseSidingPath(mainNodes[i]); } } //new end mainNodes[lastIndex].NextMainNode = null; mainNodes[lastIndex].PrevNode = mainNodes[lastIndex - 1]; mainNodes[lastIndex].ReverseOrientation(); mainNodes[lastIndex].NodeType = TrainpathNodeType.End; }
/// <summary> /// Draw the tail, and the connection to the tail from the last drawn node /// </summary> /// <param name="drawArea">Area to draw upon</param> /// <param name="colors">Colors to use for drawing</param> /// <param name="lastDrawnNode">Last drawn node, used as a starting point of connecting dashed line. Can be null</param> /// <param name="firstTailNode">Node where the tail starts</param> private void DrawTail(DrawArea drawArea, ColorScheme colors, TrainpathNode lastDrawnNode, TrainpathNode firstTailNode) { if (firstTailNode == null) return; if (lastDrawnNode != null) { drawArea.DrawDashedLine(1f, colors.BrokenPath, lastDrawnNode.Location, firstTailNode.Location); } DrawNodeItself(drawArea, firstTailNode); drawArea.DrawTexture(firstTailNode.Location, "ring", 8f, 7, colors.BrokenPath); }
/// <summary> /// Perform a deep copy of a path consisting of linked nodes /// </summary> /// <param name="curFirstNode">First node of the current path</param> /// <returns>First node of the copied path</returns> static TrainpathNode DeepCopyOfLinkedNodes(TrainpathNode curFirstNode) { if (curFirstNode == null) { return(null); } TrainpathNode newFirstNode = curFirstNode.ShallowCopyNoLinks(); TrainpathNode curMainNode = curFirstNode; TrainpathNode newMainNode = newFirstNode; TrainpathNode curSidingNode = null; TrainpathNode newSidingNode = null; TrainpathNode newNextMainNode; while (curMainNode.NextMainNode != null) { // in case there is a passing path, follow that first. // At the end of the path, curSidingNode will be the main Node to link again to if (curMainNode.NextSidingNode != null) { curSidingNode = curMainNode; newSidingNode = newMainNode; while (curSidingNode.NextSidingNode != null) { newSidingNode.NextSidingNode = curSidingNode.NextSidingNode.ShallowCopyNoLinks(); newSidingNode.NextSidingNode.PrevNode = newSidingNode; curSidingNode = curSidingNode.NextSidingNode; newSidingNode = newSidingNode.NextSidingNode; } } if (curSidingNode == curMainNode.NextMainNode) { // We need to relink to the end of a siding path. The corresponding node has already been created newNextMainNode = newSidingNode; curSidingNode = null; // no linking needed anymore } else { newNextMainNode = curMainNode.NextMainNode.ShallowCopyNoLinks(); } newNextMainNode.PrevNode = newMainNode; newMainNode.NextMainNode = newNextMainNode; curMainNode = curMainNode.NextMainNode; newMainNode = newMainNode.NextMainNode; } return(newFirstNode); }
/// <summary> /// Check the next and the previous nodes on whether they are disambiguity node, and if yes, remove them. /// </summary> /// <param name="keepNode">The (vector) node to keep</param> public void CleanAmbiguityNodes(TrainpathNode keepNode) { TrainpathNode[] nodesToCheck = { keepNode.PrevNode, keepNode.NextMainNode }; foreach (TrainpathNode node in nodesToCheck) { if (node != null && node.NodeType == TrainpathNodeType.Other && node is TrainpathVectorNode) { RemoveIntermediatePoint(node); } } }
/// <summary> /// Try to find a connection between two given nodes. Depth-first search via main track at junctions. /// Also reversing the start or reconnectNode is tried, in case one of these nodes has a non-defined orientation /// because both before and after the node the path is broken. /// </summary> /// <param name="fromNode">Node at which the reconnection should start</param> /// <param name="toNode">Node at which the reconnection should end</param> /// <param name="firstTvnIndex">In case defined, the index of the first TVN the path has to follow</param> /// <returns>True if a connection has been found</returns> public bool FindConnection(TrainpathNode fromNode, TrainpathNode toNode, int?firstTvnIndex) { // We try to find a connection between two non-broken nodes. // We store the connection as a stack of linking tvns (track-node-vector-indexes) // The connection will only contain junctions (apart from maybe start and end=reconnect nodes) autoConnectFromNode = new ConnectableNode(fromNode, true, true); autoConnectToNodeOptions = new ReconnectNodeOptions(true); autoConnectToNodeOptions.AddNode(toNode, false); // only one option here return(FindConnectionFromTo(firstTvnIndex, false)); }
/// <summary> /// Draw the current path node texture, showing what kind of node it is /// </summary> /// <param name="drawArea">area to Draw upon</param> /// <param name="trainpathNode">current node for which we need to draw our texture</param> /// <param name="isLastNode">Is this the last node that will be drawn?</param> private void DrawNodeItself(DrawArea drawArea, TrainpathNode trainpathNode, bool isLastNode) { float pathPointSize = 7f; // in meters int minPixelSize = 7; int maxPixelSize = 24; float angle = trainpathNode.TrackAngle; Color colorMain = isLastNode ? this.ColorSchemeLast.TrackStraight : ColorSchemeMain.TrackStraight; Color colorSiding = this.ColorSchemeSiding.TrackStraight; Color colorBroken = this.ColorSchemeMain.BrokenNode; switch (trainpathNode.NodeType) { case TrainpathNodeType.Start: // first node; texture is not rotated drawArea.DrawTexture(trainpathNode.Location, "pathStart", pathPointSize, minPixelSize, maxPixelSize, colorMain); break; case TrainpathNodeType.End: // formal end node; texture is not rotated drawArea.DrawTexture(trainpathNode.Location, "pathEnd", pathPointSize, minPixelSize, maxPixelSize, colorMain); break; case TrainpathNodeType.Reverse: drawArea.DrawTexture(trainpathNode.Location, "pathReverse", pathPointSize, minPixelSize, maxPixelSize, colorMain, angle); break; case TrainpathNodeType.Stop: drawArea.DrawTexture(trainpathNode.Location, "pathWait", pathPointSize, minPixelSize, maxPixelSize, colorMain); break; case TrainpathNodeType.Temporary: drawArea.DrawTexture(trainpathNode.Location, "crossedRing", pathPointSize, minPixelSize, maxPixelSize, colorBroken); break; default: bool isSidingNode = (trainpathNode.NextMainNode == null) && ((trainpathNode.NextSidingNode != null) || trainpathNode.IsBroken); // The IsBroken condition should indicate a dangling siding node Color normalColor = (isSidingNode) ? colorSiding : colorMain; drawArea.DrawTexture(trainpathNode.Location, "pathNormal", pathPointSize, minPixelSize, maxPixelSize, normalColor, angle); break; } if (trainpathNode.IsBroken) { drawArea.DrawTexture(trainpathNode.Location, "crossedRing", pathPointSize, minPixelSize, maxPixelSize, colorBroken); } //drawArea.DrawExpandingString(trainpathNode.Location, trainpathNode.NodeType.ToString()); //debug only }
/// <summary> /// Remove a intermediate vector node. Possibly add a disambiguity node if needed. /// </summary> /// <param name="currentNode">Node to be removed</param> public void RemoveIntermediatePoint(TrainpathNode currentNode) { TrainpathNode prevNode = currentNode.PrevNode; prevNode.NextMainNode = currentNode.NextMainNode; prevNode.NextSidingNode = null; // should not be needed //lastNodeSidingPath.NextMainTvnIndex should be the same still if (prevNode.NextMainNode != null) { // there might not be a next node. prevNode.NextMainNode.PrevNode = prevNode; } NetNodesAdded--; AddDisambiguityNodeIfNeeded(prevNode); }
/// <summary> /// Add a new vector node at the given location in the middle of a path /// </summary> /// <param name="nodeCandidate"></param> /// <returns>The just created node</returns> public TrainpathVectorNode AddIntermediateMainNode(TrainpathVectorNode nodeCandidate) { TrainpathNode prevNode = nodeCandidate.PrevNode; TrainpathNode nextNode = prevNode.NextMainNode; TrainpathVectorNode newNode = AddAdditionalVectorNode(prevNode, nodeCandidate, true); newNode.NextMainNode = nextNode; newNode.NextSidingNode = null; // should not be needed nextNode.PrevNode = newNode; CleanAmbiguityNodes(newNode); return(newNode); }
/// <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> /// Determine the orientation of the current node, by using the previousNode as well as the TVN that links the /// previous node with this node. /// </summary> /// <param name="previousNode">previouse node</param> /// <param name="linkingTvnIndex">the index of the Track Vector Node linking the previous node to this node</param> public override void DetermineOrientation(TrainpathNode previousNode, int linkingTvnIndex) { if (IsBroken) { // do not update the orientation. Just use default return; } // this is a non-junction node. linkingTvnIndex should be the same as TvnIndex. ForwardOriented = !this.IsEarlierOnTrackThan(previousNode); if (NodeType == TrainpathNodeType.Reverse) { // since direction is determined from previous node, after a reversal the direction is changed // needs to be done after checking with previous node ReverseOrientation(); } }
/// <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> /// Actually create the path by linking nodes following the stored linking tvns. /// </summary> /// <param name="modificationTools">The tool set that is used to actually modify the path</param> /// <param name="isMainPath">Do we add the node to the main path or not</param> /// <param name="isFromNodeDirectionOK">Set this to true when the FromNode has already been set to correct orientation elsewhere</param> public void CreateFoundConnection(ModificationTools modificationTools, bool isMainPath, bool isFromNodeDirectionOK) { ConnectableNode autoConnectToNode = autoConnectToNodeOptions.ActualReconnectNode; if (FromNodeNeedsReverse && !isFromNodeDirectionOK) { autoConnectFromNode.ReverseOrientation(); } if (ToNodeNeedsReverse) { autoConnectToNode.ReverseOrientation(); } if (!autoConnectToNode.IsConnectingForward) { linkingTvns.Reverse(); ConnectableNode swap = autoConnectToNode; autoConnectToNode = autoConnectFromNode; autoConnectFromNode = swap; } TrainpathNode currentNode = autoConnectFromNode.OriginalNode; if ((currentNode is TrainpathVectorNode) && !sameTrackConnect) { // in case the first node is a vector node (and not a direct connect), go to its junction first currentNode = modificationTools.AddAdditionalNode(currentNode, isMainPath); } //create the new path using the stored Tvns foreach (int tvn in linkingTvns) { currentNode = modificationTools.AddAdditionalNode(currentNode, tvn, isMainPath); while (currentNode is TrainpathVectorNode) { // apparently a disambiguity node has been added. currentNode = modificationTools.AddAdditionalNode(currentNode, tvn, isMainPath); } } //make the final connections TrainpathNode toNode = autoConnectToNode.OriginalNode; modificationTools.StitchTwoPaths(currentNode, toNode, isMainPath); }
//static Dictionary<bool, Drawing.DebugWindow> debugWindows = new Dictionary<bool, Drawing.DebugWindow>(); //static ContinuousAutoConnecting() //{ // debugWindows[true] = new Drawing.DebugWindow(10, 40); // debugWindows[false] = new Drawing.DebugWindow(10, 60); //} //private string debugString = String.Empty; /// <summary> /// Constructor. This will also find store the candidates for reconnecting /// </summary> /// <param name="startNode">The node to start from for reconnection. Only used for initial determination of possible reconnection nodes</param> /// <param name="isConnectingForward">Is this node connecting forwards (along the path) or not</param> public ContinuousAutoConnecting(TrainpathNode startNode, bool isConnectingForward) { isForward = isConnectingForward; List <TrainpathNode> reconnectNodes = this.FindReconnectNodeCandidates(startNode, isConnectingForward, true); autoConnectToNodeOptions = new ReconnectNodeOptions(isConnectingForward); int count = 0; foreach (TrainpathNode node in reconnectNodes) { autoConnectToNodeOptions.AddNode(node, false); //debugString += node.ToStringShort(); if (count++ > maxNumberNodesToCheckForAutoFix) { break; } } }