/// <summary> /// The activeTrackLocation needs to be between two nodes on the track, if it is to be valid. /// Here we find whether indeed it is. /// </summary> /// <param name="drawnPathData">The data structure with the information on the drawn path</param> /// <param name="trackNodeIndex">The index of the track node</param> /// <returns>The node that will be before a possible new node on the track</returns> TrainpathNode FindPrevNodeOfActiveTrack(DrawnPathData drawnPathData, int trackNodeIndex) { Collection <TrainpathNode> nodesOnTrack = drawnPathData.NodesOnTrack(trackNodeIndex); // We are given a set of nodes. We know that a part of the path was drawn following those nodes // All of these parts are on the same track. // The nodes are ordered in pairs. for (int i = nodesOnTrack.Count - 1; i > 0; i -= 2) { if (activeTrackLocation.IsBetween(nodesOnTrack[i - 1], nodesOnTrack[i])) { return(nodesOnTrack[i - 1]); } } return(null); }
/// <summary> /// Find the location on the track that can be used as a new end or wait node (or possibly start) /// </summary> /// <param name="drawnPathData">The data structure with the information on the drawn path</param> void FindActiveTrackLocation(DrawnPathData drawnPathData) { if (drawTrackDB.ClosestTrack == null || drawTrackDB.ClosestTrack.TrackNode == null) { activeTrackLocation.Location = WorldLocation.None; return; } uint tni = drawTrackDB.ClosestTrack.TrackNode.Index; int tni_int = (int)tni; if (!drawnPathData.TrackHasBeenDrawn(tni_int) && CurrentTrainPath.FirstNode != null && activeMouseDragAction == null) { activeTrackLocation.Location = WorldLocation.None; return; } int tvsi = drawTrackDB.ClosestTrack.TrackVectorSectionIndex; float distance = drawTrackDB.ClosestTrack.DistanceAlongTrack; // find location WorldLocation location = drawTrackDB.FindLocation(tni, tvsi, distance, true); // fill the properties of the activeTrackLocation activeTrackLocation.TvnIndex = tni_int; activeTrackLocation.TrackVectorSectionIndex = tvsi; activeTrackLocation.TrackSectionOffset = distance; activeTrackLocation.Location = location; if ((CurrentTrainPath.FirstNode != null) && (activeMouseDragAction == null)) { //Only in case this is not the first path. TrainpathNode prevNode = FindPrevNodeOfActiveTrack(drawnPathData, tni_int); if (prevNode == null || prevNode.HasSidingPath) { activeTrackLocation.Location = WorldLocation.None; } else { activeTrackLocation.PrevNode = prevNode; } } }
/// <summary> /// Find the node in the path that is closest to the mouse, /// </summary> /// <param name="drawArea">Area that is being drawn upon and where we have a mouse location</param> /// <param name="drawnPathData">The data structure with the information on the drawn path</param> void FindActiveNode(DrawArea drawArea, DrawnPathData drawnPathData) { // Initial simplest implementation: find simply the closest and first. float closestMouseDistanceSquared = float.MaxValue; TrainpathNode closestNode = null; foreach (TrainpathNode node in drawnPathData.DrawnNodes) { float distanceSquared = CloseToMouse.GetGroundDistanceSquared(node.Location, drawArea.MouseLocation); // by using '<=' instead of '<' we should get the latest one, which overrides earlier ones if (distanceSquared <= closestMouseDistanceSquared) { closestMouseDistanceSquared = distanceSquared; closestNode = node; } } activeNode = closestNode; }
/// <summary> /// Find the active node and active node candidate and draw them /// </summary> /// <param name="drawArea">Area to draw upon</param> public void Draw(DrawArea drawArea) { DrawnPathData drawnPathData = new DrawnPathData(); int numberDrawn = drawPath.Draw(drawArea, currentTrainPath.FirstNode, currentTrainPath.FirstNodeOfTail, numberToDraw, drawnPathData); if (numberDrawn < numberToDraw) { // Apparently we were not able to draw all nodes. Reset maximum number to draw, and possibly add a node numberToDraw = numberDrawn; if (EditingIsActive && allowAddingNodes && (CurrentNode != null) && (CurrentNode.NodeType != TrainpathNodeType.End)) { nonInteractiveAction.AddMainNode(CurrentNode, UpdateAfterEdits); } } if (!EditingIsActive) { return; } if (EnableMouseUpdate) { FindActiveNode(drawArea, drawnPathData); FindActiveTrackLocation(drawnPathData); } float textureSize = 8f; int minPixelSize = 7; int maxPixelSize = 24; if (activeNode != null && activeNode.Location != null) { drawArea.DrawTexture(activeNode.Location, "ring", textureSize, minPixelSize, maxPixelSize, DrawColors.colorsNormal.ActiveNode); } if (activeTrackLocation != null && activeTrackLocation.Location != WorldLocation.None) { drawArea.DrawTexture(activeTrackLocation.Location, "ring", textureSize, minPixelSize, maxPixelSize, DrawColors.colorsNormal.CandidateNode); } }
/// <summary> /// Find the node in the path that is closest to the mouse, /// </summary> /// <param name="drawArea">Area that is being drawn upon and where we have a mouse location</param> /// <param name="drawnPathData">The data structure with the information on the drawn path</param> void FindActiveNode(DrawArea drawArea, DrawnPathData drawnPathData) { // Initial simplest implementation: find simply the closest and first. float closestMouseDistanceSquared = float.MaxValue; TrainpathNode closestNode = null; foreach (TrainpathNode node in drawnPathData.DrawnNodes) { float distanceSquared = CloseToMouse.GetGroundDistanceSquared(node.Location, drawArea.MouseLocation); // by using '<=' instead of '<' we should get the latest one, which overrides earlier ones // To prevent numerical issues, we add a small number (smaller than two junctions would normally be together if (distanceSquared <= closestMouseDistanceSquared + 0.1f) { closestMouseDistanceSquared = distanceSquared; closestNode = node; } } activeNode = closestNode; }
/// <summary> /// Draw the actual path coded in the PATfile (for a number of nodes that can be extended or reduced) /// </summary> /// <param name="drawArea">Area to draw upon</param> /// <param name="firstNode">The first node of the path to draw</param> /// <param name="firstNodeOfTail">The node that is the start of the tail (if available)</param> /// <param name="numberToDraw">The requested number of nodes to draw</param> /// <param name="drawnPathData">Data structure that we will fill with information about the path we have drawn</param> /// <returns>the number of nodes actually drawn (not taking into account nodes on a siding)</returns> public int Draw(DrawArea drawArea, TrainpathNode firstNode, TrainpathNode firstNodeOfTail, int numberToDraw, DrawnPathData drawnPathData) { //List of all nodes that need to be drawn. List<TrainpathNode> drawnNodes = new List<TrainpathNode>(); // start of path TrainpathNode currentSidingNode = null; // we start without siding path CurrentMainNode = firstNode; if (CurrentMainNode == null) { // no path, but there might still be a tail DrawTail(drawArea, colorSchemeMain, null, firstNodeOfTail); return 0; } drawnNodes.Add(CurrentMainNode); drawnPathData.AddNode(CurrentMainNode); // We want to draw only a certain number of nodes. And if there is a siding, for the siding // we also want to draw the same number of nodes from where it splits from the main track int numberDrawn = 1; while (numberDrawn < numberToDraw) { // If we have a current siding track, we draw it if (currentSidingNode != null) { //finish the complete siding path if the main path is at end of siding already int sidingNodesToDraw = (CurrentMainNode.NodeType == TrainpathNodeType.SidingEnd) ? Int32.MaxValue : 1; while (sidingNodesToDraw >= 1) { //while tracking a siding, it has its own next node TrainpathNode nextNodeOnSiding = currentSidingNode.NextSidingNode; if (nextNodeOnSiding != null) // because also this path can run off at the end { DrawPathOnVectorNode(drawArea, colorSchemeSiding, currentSidingNode, nextNodeOnSiding, currentSidingNode.NextSidingTvnIndex); drawnNodes.Add(nextNodeOnSiding); drawnPathData.AddNode(nextNodeOnSiding); //siding nodes will not be added to drawnPathData sidingNodesToDraw--; } else { sidingNodesToDraw = 0; } currentSidingNode = nextNodeOnSiding; } } // Draw the start of a siding path, so from this main line point to the next siding node. // If there is a next siding node, we also reset the currentSidingNode // but probably it is not allowed to have siding on a siding TrainpathNode nextSidingNode = CurrentMainNode.NextSidingNode; if (nextSidingNode != null) { DrawPathOnVectorNode(drawArea, colorSchemeSiding, CurrentMainNode, nextSidingNode, CurrentMainNode.NextSidingTvnIndex); drawnNodes.Add(nextSidingNode); drawnPathData.AddNode(nextSidingNode); currentSidingNode = nextSidingNode; } // From this mainline point to the next TrainpathNode nextMainNode = CurrentMainNode.NextMainNode; if (nextMainNode != null) { DrawPathOnVectorNode(drawArea, colorSchemeMain, CurrentMainNode, nextMainNode, CurrentMainNode.NextMainTvnIndex); drawnNodes.Add(nextMainNode); drawnPathData.AddNode(nextMainNode); drawnPathData.NoteAsDrawn(CurrentMainNode, nextMainNode); CurrentMainNode = nextMainNode; numberDrawn++; } else { // no more nodes, so leave the loop even if we did not draw the amount of points requested break; } } //Draw all the nodes themselves foreach (TrainpathNode node in drawnNodes) { DrawNodeItself(drawArea, node); } DrawTail(drawArea, colorSchemeMain, drawnNodes.Last(), firstNodeOfTail); return numberDrawn; }
/// <summary> /// Draw the actual path coded in the PATfile, completely. /// </summary> /// <param name="drawArea">Area to draw upon</param> /// <param name="firstNode">The first node of the path to draw</param> public void Draw(DrawArea drawArea, TrainpathNode firstNode) { DrawnPathData dummyData = new DrawnPathData(); Draw(drawArea, firstNode, null, int.MaxValue, dummyData); }