/// <summary> /// Finds all contiguous <see cref="Graph"/> nodes that match the specified conditions, /// starting from the specified node.</summary> /// <param name="match"> /// The <see cref="Predicate{T}"/> delegate that defines the conditions each <see /// cref="Graph"/> node must match.</param> /// <param name="source"> /// The source node within <see cref="Graph"/> where the search starts.</param> /// <returns> /// <c>true</c> if one or more <see cref="Graph"/> nodes could be reached from the specified /// <paramref name="source"/>; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="match"/> or <paramref name="source"/> is a null reference.</exception> /// <remarks><para> /// <b>FindMatching</b> returns <c>false</c> if the specified <paramref name="source"/> node /// is invalid, or if there are no contiguous nodes for which <paramref name="match"/> /// succeeds. /// </para><para> /// Otherwise, <b>FindMatching</b> returns <c>true</c> and sets the <see cref="Nodes"/> /// property to the result of the flood fill.</para></remarks> public bool FindMatching(Predicate <T> match, T source) { if (match == null) { ThrowHelper.ThrowArgumentNullException("match"); } if (source == null) { ThrowHelper.ThrowArgumentNullException("source"); } _match = match; // clear previous results _nodes.Clear(); // fail if source node is invalid if (!Graph.Contains(source)) { return(false); } // mark source node as visited _visited.Add(source); // expand area around source ExpandArea(source); // clear intermediate data _match = null; _visited.Clear(); // succeed if any nodes reached return(_nodes.Count > 0); }
/// <summary> /// Finds all contiguous <see cref="Graph"/> nodes within the specified maximum world /// distance that are visible from the specified node.</summary> /// <param name="isOpaque"> /// The <see cref="Predicate{T}"/> delegate that determines whether a <see cref="Graph"/> /// node blocks the line of sight.</param> /// <param name="source"> /// The source node within <see cref="Graph"/> where the search starts.</param> /// <param name="distance"><para> /// The maximum world distance from the specified <paramref name="source"/> to search. /// </para><para>-or-</para><para> /// Zero to search the entire <see cref="Graph"/>. The default is zero.</para></param> /// <returns> /// <c>true</c> if one or more nodes are visible from <paramref name="source"/> within the /// specified <paramref name="distance"/>; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="isOpaque"/> or <paramref name="source"/> is a null reference. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="distance"/> is less than zero.</exception> /// <remarks><para> /// <b>FindVisible</b> returns <c>false</c> if the specified <paramref name="source"/> node /// is invalid, or if there are no visible nodes. Otherwise, <b>FindVisible</b> returns /// <c>true</c> and sets the <see cref="Nodes"/> and <see cref="NodeArcs"/> properties to /// the result of the visibility search. /// </para><para> /// All nodes within the specified maximum <paramref name="distance"/> are considered /// visible, except for those that are obscured by a node for which <paramref /// name="isOpaque"/> succeeds, as described for the <see cref="Visibility{T}"/> class. /// </para><para> /// If <paramref name="distance"/> is positive, any visible node must be reachable by a path /// that only includes other nodes within <paramref name="distance"/>; otherwise, it will /// not be found. This condition holds for any <see cref="PolygonGrid"/>, and for any <see /// cref="Subdivision"/> that was created from a Delaunay triangulation.</para></remarks> public bool FindVisible(Predicate <T> isOpaque, T source, double distance = 0) { if (isOpaque == null) { ThrowHelper.ThrowArgumentNullException("isOpaque"); } if (source == null) { ThrowHelper.ThrowArgumentNullException("source"); } if (distance < 0) { ThrowHelper.ThrowArgumentOutOfRangeException( "distance", distance, Strings.ArgumentNegative); } // clear previous results _nodes.Clear(); _nodeArcs.Clear(); // fail if source node is invalid if (!Graph.Contains(source)) { return(false); } _isOpaque = isOpaque; _source = source; _distance = distance; // compute world coordinates of source node _sourceWorld = Graph.GetWorldLocation(source); // expand visibility area from source FindObscuringNodes(source); FindVisibleNodes(); // clear intermediate data _isOpaque = null; _source = default(T); _obscuringNodes.Clear(); // succeed if any nodes reached return(_nodes.Count > 0); }
/// <summary> /// Finds all <see cref="Graph"/> nodes that the specified agent can reach from the /// specified node with the specified maximum path cost.</summary> /// <param name="agent"> /// The <see cref="IGraphAgent{T}"/> that performs the movement.</param> /// <param name="source"> /// The source node within <see cref="Graph"/> where the movement starts.</param> /// <param name="maxCost"> /// The maximum total cost of the best path from the specified <paramref name="source"/> to /// any reachable <see cref="Graph"/> node.</param> /// <returns> /// <c>true</c> if one or more <see cref="Graph"/> nodes could be reached from the specified /// <paramref name="source"/>; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="agent"/> or <paramref name="source"/> is a null reference.</exception> /// <exception cref="ArgumentOutOfRangeException"> /// <paramref name="maxCost"/> is zero or negative.</exception> /// <remarks><para> /// <b>FindReachable</b> returns <c>false</c> if the specified <paramref name="source"/> /// node is invalid, or if there are no reachable nodes whose total path cost is equal to or /// less than the specified <paramref name="maxCost"/>. /// </para><para> /// Otherwise, <b>FindReachable</b> returns <c>true</c> and sets the <see cref="Nodes"/> /// property to the result of the path search. This collection contains only those reachable /// nodes for which <see cref="IGraphAgent{T}.CanOccupy"/> has succeeded. /// </para><para> /// If <see cref="IGraphAgent{T}.RelaxedRange"/> is <c>true</c> for the specified <paramref /// name="agent"/>, the <b>Nodes</b> collection includes those nodes whose total path cost /// exceeds <paramref name="maxCost"/>, but which can be reached from a neighbor whose total /// path cost is less than (but not equal to) <paramref name="maxCost"/>. These nodes are /// considered reachable regardless of their actual step costs.</para></remarks> public bool FindReachable(IGraphAgent <T> agent, T source, double maxCost) { if (agent == null) { ThrowHelper.ThrowArgumentNullException("agent"); } if (source == null) { ThrowHelper.ThrowArgumentNullException("source"); } if (maxCost <= 0) { ThrowHelper.ThrowArgumentOutOfRangeException( "maxCost", maxCost, Strings.ArgumentNotPositive); } _agent = agent; _maxCost = maxCost; // clear previous results _nodes.Clear(); // fail if source node is invalid if (!Graph.Contains(source)) { return(false); } // mark source node as visited _pathCosts.Add(source, -1); // expand coverage to maxCost ExpandArea(source, 0); // clear intermediate data _pathCosts.Clear(); // succeed if any nodes reached return(_nodes.Count > 0); }
/// <summary> /// Finds the best path to move the specified agent from one specified <see cref="Graph"/> /// node to another.</summary> /// <param name="agent"> /// The <see cref="IGraphAgent{T}"/> that performs the movement.</param> /// <param name="source"> /// The source node within <see cref="Graph"/>.</param> /// <param name="target"> /// The target node within <see cref="Graph"/>.</param> /// <returns> /// <c>true</c> if a best path between <paramref name="source"/> and <paramref /// name="target"/> could be found; otherwise, <c>false</c>.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="agent"/>, <paramref name="source"/>, or <paramref name="target"/> is a /// null reference.</exception> /// <remarks><para> /// <b>FindBestPath</b> returns <c>false</c> if either of the specified <paramref /// name="source"/> and <paramref name="target"/> nodes is invalid, or if no connecting path /// could be found. /// </para><para> /// Otherwise, <b>FindBestPath</b> returns <c>true</c> and sets the <see cref="BestNode"/>, /// <see cref="Nodes"/>, and <see cref="TotalCost"/> properties to the results of the path /// search. /// </para><para> /// <b>FindBestPath</b> calls <see cref="IGraphAgent{T}.CanOccupy"/> and <see /// cref="IGraphAgent{T}.IsNearTarget"/> on the specified <paramref name="agent"/> to /// determine whether a given <see cref="BestNode"/> candidate is acceptable. Depending on /// the implementation of <b>IsNearTarget</b>, the <see cref="PathNode{T}.Node"/> of the /// final <see cref="BestNode"/> may differ from the specified <paramref name="target"/>, /// and possibly equal the specified <paramref name="source"/>. <b>CanOccupy</b> is never /// called on path nodes that match the <paramref name="source"/> node. /// </para><para> /// <b>FindBestPath</b> operates with a <em>restricted search radius</em> if <see /// cref="RelativeLimit"/> is greater than zero. In this case, <see cref="AbsoluteLimit"/> /// is set to the product (rounded up) of <b>RelativeLimit</b> and the distance between /// <paramref name="source"/> and <paramref name="target"/>. Whenever a node is considered /// for inclusion in the search path, its distances from <paramref name="source"/> and /// <paramref name="target"/> are calculated, and the node is ignored if the sum exceeds /// <b>AbsoluteLimit</b>.</para></remarks> public bool FindBestPath(IGraphAgent <T> agent, T source, T target) { if (agent == null) { ThrowHelper.ThrowArgumentNullException("agent"); } if (source == null) { ThrowHelper.ThrowArgumentNullException("source"); } if (target == null) { ThrowHelper.ThrowArgumentNullException("target"); } _agent = agent; _source = source; _target = target; // clear previous results _absoluteLimit = 0; _bestNode = null; _nodes.Clear(); _targetWorld = PointD.Empty; // fail if either node is invalid if (!Graph.Contains(source) || !Graph.Contains(target)) { return(false); } // compute absolute distance limit if desired double distance = Graph.GetDistance(source, target); if (_relativeLimit > 0) { _absoluteLimit = distance * _relativeLimit; } // compute world distance to target if desired if (UseWorldDistance) { _targetWorld = Graph.GetWorldLocation(target); } // initialize search list _openList = new PathNode <T>(source, Graph.Connectivity); _openList._h = distance; bool success = false; while (SetBestNode()) { T node = _bestNode.Node; // succeed if occupation target is in range if (_agent.IsNearTarget(node, target, _bestNode._h) && (ComparerCache <T> .EqualityComparer.Equals(source, node) || _agent.CanOccupy(node))) { success = true; break; } // add children to search space CreateChildren(_bestNode); } // clear intermediate data _source = _target = default(T); Debug.Assert(_parents.Count == 0); _openList = null; _openTable.Clear(); _closedTable.Clear(); return(success); }