예제 #1
0
        private static int CalculateBottomY(int x, DirectionVector bottomVector)
        {
            switch (x)
            {
            case 0:
                return(0);

            default:
            {
                int quotient  = (2 * x - 1) * bottomVector.Y / (2 * bottomVector.X);
                int remainder = (2 * x - 1) * bottomVector.Y % (2 * bottomVector.X);

                return(remainder >= bottomVector.X
                        ? quotient + 1
                        : quotient);
            }
            }
        }
예제 #2
0
        private static int CalculateTopY(int x, DirectionVector topVector)
        {
            switch (x)
            {
            case 0:
                return(0);

            default:
            {
                int quotient  = (2 * x + 1) * topVector.Y / (2 * topVector.X);
                int remainder = (2 * x + 1) * topVector.Y % (2 * topVector.X);

                return(remainder > topVector.X
                        ? quotient + 1
                        : quotient);
            }
            }
        }
예제 #3
0
        // This method has two main purposes: (1) it marks points inside the
        // portion that are within the radius as in the field of view, and
        // (2) it computes which portions of the following column are in the
        // field of view, and puts them on a work queue for later processing.
        private static void ComputeFoVForColumnPortion <T>(
            int x,
            DirectionVector topVector,
            DirectionVector bottomVector,
            Func <int, int, bool> isOpaque,
            Func <int, int, T> setFieldOfView,
            decimal radius,
            Queue <ColumnPortion> queue)
        {
            // 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.

            // Start at the top of the column portion and work down.
            var topY = CalculateTopY(x, topVector);

            // Note that this can find a top cell that is actually entirely blocked by
            // the cell below it; consider detecting and eliminating that.
            var bottomY = CalculateBottomY(x, bottomVector);

            // 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.

            bool?wasLastCellOpaque = null;

            for (int y = topY; y >= bottomY; --y)
            {
                bool inRadius = CheckInRadiusAndSetFieldOfViewAsNeeded(x, y, setFieldOfView, radius);

                // A cell that was too far away to be seen is effectively
                // an opaque cell; nothing "above" it is going to be visible
                // in the next column, so we might as well treat it as
                // an opaque cell and not scan the cells that are also too
                // far away in the next column.

                bool currentIsOpaque = !inRadius || isOpaque(x, y);
                if (wasLastCellOpaque != null)
                {
                    if (currentIsOpaque)
                    {
                        // We've found a boundary from transparent to opaque. Make a note
                        // of it and revisit it later.
                        if (!wasLastCellOpaque.Value)
                        {
                            // The new bottom vector touches the upper left corner of
                            // opaque cell that is below the transparent cell.
                            queue.Enqueue(new ColumnPortion(
                                              x + 1,
                                              new DirectionVector(x * 2 - 1, y * 2 + 1),
                                              topVector));
                        }
                    }
                    else if (wasLastCellOpaque.Value)
                    {
                        // We've found a boundary from opaque to transparent. Adjust the
                        // top vector so that when we find the next boundary or do
                        // the bottom cell, we have the right top vector.
                        //
                        // The new top vector touches the lower right corner of the
                        // opaque cell that is above the transparent cell, which is
                        // the upper right corner of the current transparent cell.
                        topVector = new DirectionVector(x * 2 + 1, y * 2 + 1);
                    }
                }
                wasLastCellOpaque = currentIsOpaque;
            }

            // Make a note of the lowest opaque-->transparent transition, if there is one.
            if (wasLastCellOpaque != null && !wasLastCellOpaque.Value)
            {
                queue.Enqueue(new ColumnPortion(x + 1, bottomVector, topVector));
            }
        }