private static void ComputeFieldOfViewInDodecantZero(
          int radius,
          int observerHeight,
          Func<HexCoords, bool> isOnBoard,
          Func<HexCoords, int> targetHeight,
          Func<HexCoords, int> terrainHeight,
          Action<HexCoords> setFieldOfView)
        {
#if TraceFOV
        radius = 16;
        DebugTracing.EnabledFags |= TraceFlag.FieldOfView;
#endif

            var currentCoords = HexCoords.NewCanonCoords(0, 1);
            if (!isOnBoard(currentCoords)) return;

            if (radius > 0) setFieldOfView(currentCoords);

            var queue = new FovQueue();
            var current = new FovCone(
                          2,
                          new IntVector2D(1, 2),
                          new IntVector2D(0, 1),
                          new RiseRun(terrainHeight(currentCoords) - observerHeight, 1));
            while (current.Range <= radius)
            {
                current = ComputeFoVForRange(
                  observerHeight,
                  current,
                  isOnBoard,
                  targetHeight,
                  terrainHeight,
                  setFieldOfView,
                  queue);
            }
        }
        /// <summary></summary>
        /// <param name="observerHeight"></param>
        /// <param name="cone"></param>
        /// <param name="isOnBoard"></param>
        /// <param name="targetHeight"></param>
        /// <param name="terrainHeight"></param>
        /// <param name="setFieldOfView"></param>
        /// <param name="queue"></param>
        /// <returns></returns>
        ///<remarks>
        /// This method: 
        /// (1) marks points inside the cone-arc that are within the radius of the field 
        ///     of view; and 
        /// (2) computes which portions of the following column are in the field of view, 
        ///     queueing them for later processing. 
        ///
        /// This algorithm is "center-to-center"; a more sophisticated algorithm 
        /// would say that a cell is visible if there is *any* straight line segment that 
        /// passes through *any* portion of the origin cell and any portion of the target 
        /// cell, passing through only transparent cells along the way. This is the 
        /// "Permissive Field Of View" algorithm, and it is much harder to implement.
        ///
        /// Search for transitions from opaque to transparent or transparent to opaque and 
        /// use those to determine what portions of the *next* column are visible from the 
        /// origin.
        ///</remarks>
        private static FovCone ComputeFoVForRange(
          int observerHeight,
          FovCone cone,
          Func<HexCoords, bool> isOnBoard,
          Func<HexCoords, int> targetHeight,
          Func<HexCoords, int> terrainHeight,
          Action<HexCoords> setFieldOfView,
          FovQueue queue)
        {
            Action<FovCone> enqueue = queue.Enqueue;

            var range = cone.Range;
            var topVector = cone.VectorTop;
            var topRiseRun = cone.RiseRun;
            var bottomVector = cone.VectorBottom;

            // track the overlap-cone between adjacent hexes as we move down.
            var overlapVector = cone.VectorTop;
            var hexX = XFromVector(range, topVector);
#if TraceFOV
        TraceFlag.FieldOfView.Trace(false, "DQ:   ({0}) from {1}", cone, hexX);
#endif

            do
            {
                while (overlapVector.GT(bottomVector))
                {
                    var coordsCurrent = HexCoords.NewCanonCoords(hexX, range);
                    var hexVectorBottom = VectorHexBottom(coordsCurrent);
                    if (isOnBoard(coordsCurrent))
                    {
                        #region Set current hex parameters
                        var hexVectorTop = VectorHexTop(coordsCurrent);
                        var hexElevation = targetHeight(coordsCurrent);
                        var hexHeight = terrainHeight(coordsCurrent);
                        var hexRiseRun = new RiseRun(hexHeight - observerHeight, range);
                        #endregion

                        #region Check visibility of current hex
                        var riseRun = new RiseRun(hexElevation - observerHeight, GetRange(coordsCurrent));
                        if (riseRun >= cone.RiseRun
                        && bottomVector.LE(coordsCurrent.Canon) && coordsCurrent.Canon.LE(topVector)
                        )
                        {
                            setFieldOfView(coordsCurrent);
#if TraceFOV
                TraceFlag.FieldOfView.Trace(false,"    Set visible: {0} / {1}; {2} >= {3}", 
                    MapCoordsDodecant(coordsCurrent), coordsCurrent.ToString(), riseRun, cone.RiseRun);
#endif
                        }
                        #endregion

                        #region Check hex transition
                        if (hexRiseRun > topRiseRun)
                        {
                            topVector = LogAndEnqueue(enqueue, range, topVector, hexVectorTop, topRiseRun, 0);
                            topRiseRun = hexRiseRun;
                        }
                        else if (hexRiseRun > cone.RiseRun)
                        {
                            topVector = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 1);
                            topRiseRun = hexRiseRun;
                        }
                        else if (hexRiseRun < cone.RiseRun)
                        {
                            topVector = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 2);
                            topRiseRun = cone.RiseRun;
                        }
                        #endregion
                    }

                    overlapVector = VectorMax(hexVectorBottom, bottomVector);
                    if (hexVectorBottom.GT(bottomVector)) --hexX;
                }

                #region Dequeue next cone
                if (queue.Count == 0)
                {
                    topVector = LogAndEnqueue(enqueue, range, topVector, bottomVector, topRiseRun, 3);
                    cone = queue.Dequeue();
                    break;
                }
                else
                {
                    cone = queue.Dequeue();
                    if (cone.Range != range)
                    {
                        topVector = LogAndEnqueue(enqueue, range, topVector, bottomVector, topRiseRun, 4);
                        break;
                    }
#if TraceFOV
            TraceFlag.FieldOfView.Trace(false, "DQ:   ({0}) from {1}", cone, hexX);
#endif
                }
                #endregion

                #region Check cone transition
                if (cone.RiseRun > topRiseRun)
                {
                    topVector = LogAndEnqueue(enqueue, range, topVector, cone.VectorTop, topRiseRun, 5);
                    topRiseRun = cone.RiseRun;
                }
                else if (cone.RiseRun < topRiseRun)
                {
                    topVector = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 6);
                    topRiseRun = cone.RiseRun;  // TODO Why is this commented out?
                }
                #endregion

                overlapVector = topVector;
                bottomVector = cone.VectorBottom;
            } while (true);

            // Pick-up any cone portion at bottom of range still unprocessed
            if (topVector.GT(bottomVector))
                LogAndEnqueue(enqueue, range, topVector, bottomVector, cone.RiseRun, 7);

            return cone;
        }