/// <summary> /// Determines whether the current <see cref="NodeArc"/> completely obscures the specified /// instance, or vice versa.</summary> /// <param name="arc"> /// The <see cref="NodeArc"/> to examine.</param> /// <returns><para> /// An <see cref="Int32"/> value indicating the relative obscuration of this instance and /// <paramref name="arc"/>, as follows: /// </para><list type="table"><listheader> /// <term>Return Value</term><description>Condition</description> /// </listheader><item> /// <term>Less than zero</term><description> /// <paramref name="arc"/> completely obscures this instance.</description> /// </item><item> /// <term>Zero</term><description> /// Neither instance completely obscures the other.</description> /// </item><item> /// <term>Greater than zero</term><description> /// This instance completely obscures <paramref name="arc"/>.</description> /// </item></list></returns> /// <remarks> /// <b>IsObscured</b> takes the <see cref="Distance"/> of both instances into account to /// determine which instance obscures the other, if any.</remarks> public int IsObscured(NodeArc arc) { // start of specified arc relative to current arc double relativeStart = arc.Start - Start; if (relativeStart <= -Math.PI) { relativeStart += 2 * Math.PI; } else if (relativeStart > Math.PI) { relativeStart -= 2 * Math.PI; } Debug.Assert(relativeStart > -Math.PI && relativeStart <= Math.PI); double relativeSweep = relativeStart + arc.Sweep; // specified arc completely obscures current arc if (relativeStart <= 0 && relativeSweep >= Sweep && arc.Distance <= Distance) { return(-1); } // current arc completely obscures specified arc if (relativeStart >= 0 && relativeSweep <= Sweep && arc.Distance > Distance) { return(+1); } return(0); }
/// <summary> /// Expands the current visibility area with all visible <see cref="Graph"/> nodes, within /// maximum world distance from the source node.</summary> /// <remarks><para> /// <b>FindVisibleNodes</b> iterates over all <see cref="NodeArcs"/> found by <see /// cref="FindObscuringNodes"/>, and adjusts their <see cref="NodeArc.VisibleFraction"/> /// according to the collection of obscuring nodes also created by that method. /// </para><para> /// Any node whose <see cref="NodeArc"/> remains unobscured by at least the current <see /// cref="Threshold"/> is added to the <see cref="Nodes"/> collection.</para></remarks> private void FindVisibleNodes() { // iterate over all visited nodes that may be visible foreach (var pair in _nodeArcs) { NodeArc arc = pair.Value; if (arc._visibleFraction == 0) { continue; } double start = arc.Start, sweep = arc.Sweep; // compare visited node to all obscuring nodes (except itself) foreach (var obscuringPair in _obscuringNodes) { if (ComparerCache <T> .EqualityComparer.Equals(pair.Key, obscuringPair.Key)) { continue; } // ignore obscuring nodes at a greater source distance NodeArc obscuringAngle = obscuringPair.Value; if (obscuringAngle.Distance > arc.Distance) { continue; } // obscure visibility arc of visited node obscuringAngle.Obscure(ref start, ref sweep); arc._visibleFraction = sweep / arc.Sweep; // check if arc is sufficiently obscured if (arc._visibleFraction < _threshold) { goto nextVisited; } } // add visible node to search results _nodes.Add(pair.Key); nextVisited: continue; } }
/// <summary> /// Expands the current collection of obscuring <see cref="Graph"/> nodes with all neighbors /// of the specified node, within maximum world distance from the source node.</summary> /// <param name="node"> /// The <see cref="Graph"/> node whose neighbors to examine.</param> /// <remarks><para> /// <b>FindObscuringNodes</b> recursively visits all directly connected nodes, and adds them /// to an internal collection of obscuring nodes if they are opaque. Nodes which are fully /// obscured by other obscuring nodes are removed from the collection. /// </para><para> /// <b>FindObscuringNodes</b> never revisits nodes that were already examined. All visited /// nodes are added to <see cref="NodeArcs"/> for later processing by <see /// cref="FindVisibleNodes"/>.</para></remarks> private void FindObscuringNodes(T node) { // get valid neighbors of current node IList <T> neighbors = Graph.GetNeighbors(node); // recurse into all valid neighbors for (int i = 0; i < neighbors.Count; i++) { T neighbor = neighbors[i]; // skip source and previously visited nodes if (ComparerCache <T> .EqualityComparer.Equals(_source, neighbor) || _nodeArcs.ContainsKey(neighbor)) { continue; } // compute tangential arc and source distance NodeArc arc = CreateNodeArc(neighbor); // skip nodes beyond maximum distance if (_distance > 0 && arc.Distance > _distance) { continue; } // record visited node with tangential arc _nodeArcs.Add(neighbor, arc); // nothing else to do for transparent nodes if (!_isOpaque(neighbor)) { goto nextNeighbor; } /* * Try adding current opaque node to list of all obscuring nodes recorded so far. * * If any single recorded node completely obscures the current node, we skip it. * If the current node completely obscures any recorded nodes, we delete those. * * We also clear the VisiblityFraction for all completely obscured nodes (current * or recorded) so we won't waste time testing them again in FindVisibleNodes. */ foreach (var pair in _obscuringNodes) { int result = arc.IsObscured(pair.Value); if (result < 0) { arc._visibleFraction = 0; goto nextNeighbor; } if (result > 0) { pair.Value._visibleFraction = 0; _removeNodes.Add(pair.Key); } } // remove obscuring nodes that were themselves obscured for (int j = 0; j < _removeNodes.Count; j++) { _obscuringNodes.Remove(_removeNodes[j]); } _removeNodes.Clear(); // add neighbor to obscuring nodes _obscuringNodes.Add(neighbor, arc); nextNeighbor: FindObscuringNodes(neighbor); } }