/// <summary>TODO</summary>
        /// <param name="radius"></param>
        /// <param name="heightObserver"></param>
        /// <param name="isOnboard"></param>
        /// <param name="heightTarget"></param>
        /// <param name="heightTerrain"></param>
        /// <param name="setFieldOfView"></param>
        private static void ComputeFieldOfViewInDodecantZero(
            int radius,
            int heightObserver,
            Func <HexCoords, bool> isOnboard,
            Func <HexCoords, int> heightTarget,
            Func <HexCoords, Hexside, int> heightTerrain,
            Action <HexCoords> setFieldOfView
            )
        {
            var currentCoords = HexCoords.NewCanonCoords(0, 1);

            if (!isOnboard(currentCoords))
            {
                return;
            }

            if (radius > 0)
            {
                setFieldOfView(currentCoords);
            }

            var queue   = new FovConeQueue();
            var current = new FovCone(
                2,
                new IntVector2D(1, 2),
                new IntVector2D(0, 1),
                new RiseRun(2 * (heightTerrain(currentCoords, Hexside.North) - heightObserver), 1));

            while (current.Range <= radius)
            {
                current = ComputeFoVForRange(heightObserver, isOnboard, heightTarget, heightTerrain, setFieldOfView, queue, current);
            }
        }
        /// <summary>Processes the supplied FovCone and returns the next FovCone to process.</summary>
        /// <param name="heightObserver"></param>
        /// <param name="isOnboard"></param>
        /// <param name="heightTarget"></param>
        /// <param name="heightTerrain"></param>
        /// <param name="setFieldOfView"></param>
        /// <param name="queue"></param>
        /// <param name="cone"></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 neighbour 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 heightObserver,
            Func <HexCoords, bool> isOnboard,
            Func <HexCoords, int> heightTarget,
            Func <HexCoords, Hexside, int> heightTerrain,
            Action <HexCoords> setFieldOfView,
            FovConeQueue queue,
            FovCone cone
            )
        {
            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);

            FieldOfViewTrace(false, "DQ:   ({0}) from {1}", cone, hexX);

            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 = heightTarget(coordsCurrent);
                        var hexHeight    = heightTerrain(coordsCurrent, Hexside.North);
                        var hexRiseRun   = new RiseRun(hexHeight - heightObserver, range);
                        #endregion

                        #region Check visibility of current hex
                        var riseRun = new RiseRun(hexElevation - heightObserver, coordsCurrent.RangeFromOrigin);
                        if (riseRun >= cone.RiseRun &&
                            bottomVector.LE(coordsCurrent.Canon) && coordsCurrent.Canon.LE(topVector)
                            )
                        {
                            setFieldOfView(coordsCurrent);
                            FieldOfViewTrace(false, "    Set visible: {0} / {1}; {2} >= {3}",
                                             _mapCoordsDodecant(coordsCurrent), coordsCurrent.ToString(), riseRun, cone.RiseRun);
                        }
                        #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;
                    }
                    FieldOfViewTrace(false, "DQ:   ({0}) from {1}", cone, hexX);
                }
                #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;
                }
                #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);
        }