/// <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> /// Is there a connection on the same track between two nodes? /// </summary> /// <param name="fromNode">The node to connect from</param> /// <param name="toNode">The node to connect to</param> /// <param name="tvnIndex">Possibly a requirement on the index of the trackvectornode</param> private bool ExistsConnectionSameTrack(ConnectableNode fromNode, ConnectableNode toNode, int?tvnIndex) { if (connectableNodeOptions.Count == 0) { return(false); } // we can only connect on the same track if the both start and reconnectnodes are on the same track // This means that both need to be vector nodes. // It also means, this is only possible for the first node TrainpathVectorNode fromAsVector = fromNode.OriginalNodeAsVector; TrainpathVectorNode toAsVector = toNode.OriginalNodeAsVector; if (fromAsVector == null) { return(false); } if (toAsVector == null) { return(false); } if (fromAsVector.TvnIndex != toAsVector.TvnIndex) { return(false); } if (tvnIndex.HasValue && (tvnIndex.Value != fromAsVector.TvnIndex)) { return(false); } //for a reverse point the orientation is defined as being after the reversal. bool reconnectIsForward = (toAsVector.NodeType == TrainpathNodeType.Reverse) ? !toAsVector.ForwardOriented : toAsVector.ForwardOriented; if (fromAsVector.ForwardOriented != reconnectIsForward) { return(false); } if (isForwardConnecting) { if (fromAsVector.ForwardOriented != fromAsVector.IsEarlierOnTrackThan(toAsVector)) { return(false); } } else { if (fromAsVector.ForwardOriented == fromAsVector.IsEarlierOnTrackThan(toAsVector)) { return(false); } } return(true); }
/// <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() { TrainpathVectorNode newNode = (TrainpathVectorNode)this.MemberwiseClone(); newNode.NextMainNode = null; newNode.NextSidingNode = null; newNode.PrevNode = null; return(newNode); }
/// <summary> /// Determine if a connection can be made (and store the found solution), from a 'dynamic node' meaning that /// it can change. /// </summary> /// <param name="fromNode">The node that is from which to connect</param> public bool CanConnect(TrainpathVectorNode fromNode) { autoConnectFromNode = new ConnectableNode(fromNode, true, isForward); FromNodeNeedsReverse = false; // reset to correct value bool canIndeedConnect = FindConnectionFromTo(null, true); //debugWindows[isForward].DrawString = String.Format("{0}:{1} ({2}) {3}", canIndeedConnect, // (autoConnectToNodeOptions.ActualReconnectNode == null ? "none" : autoConnectToNodeOptions.ActualReconnectNode.OriginalNode.ToStringShort()), // LinkingTvnsAsString(), debugString); return(canIndeedConnect); }
void CreateNonMenuActions() { activeTrackLocation = new TrainpathVectorNode(trackDB, tsectionDat); nonInteractiveAction = new EditorActionNonInteractive(); editorActionMouseDragVector = new EditorActionMouseDragVectorNode(); editorActionMouseDragAuto = new EditorActionMouseDragAutoConnect(); mouseDragActions = new List <EditorActionMouseDrag>() { editorActionMouseDragAuto }; }
/// <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> /// 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> /// 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> /// 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> /// constructor based on a nodeCandidate: a TrainpathVectorNode based on mouse location, does not contain all information /// </summary> /// <param name="nodeCandidate"></param> public TrainpathVectorNode(TrainpathVectorNode nodeCandidate) : base(nodeCandidate) { TvnIndex = nodeCandidate.TvnIndex; TrackVectorSectionIndex = nodeCandidate.TrackVectorSectionIndex; TrackSectionOffset = nodeCandidate.TrackSectionOffset; NextMainTvnIndex = nodeCandidate.TvnIndex; Location = nodeCandidate.Location; ForwardOriented = true; // only initial setting TrackVectorNode tn = TrackDB.TrackNodes[TvnIndex] as TrackVectorNode; Traveller traveller = new Traveller(TsectionDat, TrackDB.TrackNodes, tn, Location, Traveller.TravellerDirection.Forward); CopyDataFromTraveller(traveller); trackAngleForward = traveller.RotY; // traveller also has TvnIndex, tvs, offset, etc, but we are not using that (should be consistent though) }
private List <string> StationNamesBetweenNodes(TrainpathNode firstNode, TrainpathNode secondNode) { var stationNames = new List <string>(); int tvnIndex = firstNode.NextMainTvnIndex; if (tvnIndex < 0) { return(stationNames); } TrackNode tn = trackDB.TrackNodes[tvnIndex]; TrVectorNode tvn = tn.TrVectorNode; if (tvn == null) { return(stationNames); } if (tvn.TrItemRefs == null) { return(stationNames); } foreach (int trackItemIndex in tvn.TrItemRefs) { TrItem trItem = trackDB.TrItemTable[trackItemIndex]; if (trItem.ItemType == TrItem.trItemType.trPLATFORM) { var traveller = new Traveller(tsectionDat, trackDB.TrackNodes, tn, trItem.TileX, trItem.TileZ, trItem.X, trItem.Z, Traveller.TravellerDirection.Forward); if (traveller != null) { var platformNode = new TrainpathVectorNode(firstNode, traveller); if (platformNode.IsBetween(firstNode, secondNode)) { PlatformItem platform = trItem as PlatformItem; stationNames.Add(platform.Station); } } } } return(stationNames); }
/// <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); }
private List <string> StationNamesBetweenNodes(TrainpathNode firstNode, TrainpathNode secondNode) { var stationNames = new List <string>(); int tvnIndex = firstNode.NextMainTvnIndex; if (tvnIndex < 0) { return(stationNames); } TrackVectorNode tvn = trackDB.TrackNodes[tvnIndex] as TrackVectorNode; if (tvn == null) { return(stationNames); } if (tvn.TrackItemIndices == null) { return(stationNames); } foreach (int trackItemIndex in tvn.TrackItemIndices) { TrackItem trItem = trackDB.TrackItems[trackItemIndex]; if (trItem is PlatformItem) { var traveller = new Traveller(tsectionDat, trackDB.TrackNodes, tvn, trItem.Location, Traveller.TravellerDirection.Forward); if (traveller != null) { var platformNode = new TrainpathVectorNode(firstNode, traveller); if (platformNode.IsBetween(firstNode, secondNode)) { PlatformItem platform = trItem as PlatformItem; stationNames.Add(platform.Station); } } } } return(stationNames); }
/// <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> /// Find situations where vectors nodes do not point correctly towards the next node /// </summary> void FindWronglyOrientedLinks() { // start of path TrainpathNode currentMainNode = FirstNode; while (currentMainNode.NextMainNode != null) { TrainpathVectorNode currentNodeAsVector = currentMainNode as TrainpathVectorNode; if (currentNodeAsVector != null && !currentMainNode.IsBroken) { // if it is forward oriented it should also be earlier on track as next node // if it is not forward oriented it should also not be earlier on track as next node if (currentNodeAsVector.ForwardOriented != currentNodeAsVector.IsEarlierOnTrackThan(currentMainNode.NextMainNode)) { // the link is broken (although the nodes themselves might be fine) currentMainNode.NextMainTvnIndex = -1; } } TrainpathNode currentSidingNode = currentMainNode; while (currentSidingNode.NextSidingNode != null) { currentNodeAsVector = currentSidingNode as TrainpathVectorNode; if (currentNodeAsVector != null && !currentSidingNode.IsBroken) { // if it is forward oriented it should also be earlier on track as next node // if it is not forward oriented it should also not be earlier on track as next node if (currentNodeAsVector.ForwardOriented != currentNodeAsVector.IsEarlierOnTrackThan(currentSidingNode.NextSidingNode)) { // the link is broken (although the nodes themselves might be fine) currentMainNode.NextSidingTvnIndex = -1; } } currentSidingNode = currentSidingNode.NextSidingNode; } currentMainNode = currentMainNode.NextMainNode; } }
/// <summary> /// Stitch two paths together. /// In case both nodes are a junction (and supposedly the same junction), then patch the two on top of each other. /// Otherwise, simply connect the two. /// No checking here on whether the connection is good. /// </summary> /// <param name="lastNodeFirstPath">Node to connect from (last node of the first partial path)</param> /// <param name="firstNodeSecondPath">Node to connect to (first node of the second partial path)</param> /// <param name="isMainPath">Do we add the node to the main path or not</param> public void StitchTwoPaths(TrainpathNode lastNodeFirstPath, TrainpathNode firstNodeSecondPath, bool isMainPath) { TrainpathVectorNode lastNodeFirstPathAsVector = lastNodeFirstPath as TrainpathVectorNode; TrainpathVectorNode firstNodeSecondPathAsVector = firstNodeSecondPath as TrainpathVectorNode; if (lastNodeFirstPathAsVector == null && firstNodeSecondPathAsVector == null) { //both are junctions. Remove the last node of the first path lastNodeFirstPath = lastNodeFirstPath.PrevNode; NetNodesAdded--; } // make the connection if (isMainPath) { lastNodeFirstPath.NextMainNode = firstNodeSecondPath; // For the moment, if it is a siding path, we always reconnect to the main path. And then prevnode does not need to be relinked firstNodeSecondPath.PrevNode = lastNodeFirstPath; } else { lastNodeFirstPath.NextSidingNode = firstNodeSecondPath; } //in case the first node of the second path is a vector, make sure that its next tvn index is copied if (firstNodeSecondPathAsVector != null) { if (isMainPath) { lastNodeFirstPath.NextMainTvnIndex = firstNodeSecondPathAsVector.TvnIndex; } else { lastNodeFirstPath.NextSidingTvnIndex = firstNodeSecondPathAsVector.TvnIndex; } } }
/// <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> /// Add a new vector path node at the location of nodeCandidate. /// </summary> /// <param name="lastNode">node that will be predecessor of the new nodeCandidate</param> /// <param name="nodeCandidate">partial trainpath vector node describing the current mouse location</param> /// <param name="isMainPath">Do we add the node to the main path or not</param> /// <returns>The newly created vector node</returns> public TrainpathVectorNode AddAdditionalVectorNode(TrainpathNode lastNode, TrainpathVectorNode nodeCandidate, bool isMainPath) { // we add a new activeNodeAsJunction TrainpathVectorNode newNode = new TrainpathVectorNode(nodeCandidate); newNode.DetermineOrientation(lastNode, newNode.TvnIndex); // simple linking if (isMainPath) { lastNode.NextMainTvnIndex = newNode.NextMainTvnIndex; lastNode.NextMainNode = newNode; newNode.PrevNode = lastNode; } else { lastNode.NextSidingTvnIndex = newNode.NextMainTvnIndex; newNode.NextSidingTvnIndex = newNode.NextMainTvnIndex; lastNode.NextSidingNode = newNode; } NetNodesAdded++; return(newNode); }
/// <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); }
void CreateNonMenuActions() { activeTrackLocation = new TrainpathVectorNode(trackDB, tsectionDat); nonInteractiveAction = new EditorActionNonInteractive(); }