public static IFastImage Render(LightMap lightMap, IEnumerable <LightDefinition> lights, int scale = 10)
        {
            var image = Render(lightMap, scale);

            foreach (var light in lights)
            {
                image.SetPixel(light.Center.X, light.Center.Y, light.Height switch
                {
                    LightHeight.Ceiling => SKColors.Orange,
                    LightHeight.Middle => SKColors.HotPink,
                    LightHeight.Floor => SKColors.Red,
                    _ => throw new Exception("Impossible")
                });
        public static IFastImage Render(LightMap lightMap, int scale = 10)
        {
            var image = new FastImage(lightMap.Size.Width, lightMap.Size.Height, scale);

            for (int y = 0; y < lightMap.Size.Height; y++)
            {
                for (int x = 0; x < lightMap.Size.Width; x++)
                {
                    var  light     = lightMap[x, y];
                    byte intensity = (byte)((((double)light + lightMap.Range.DarkLevels) / lightMap.Range.Total) * 256);

                    image.SetPixel(x, y, new SKColor(intensity, intensity, intensity));
                }
            }

            return(image);
        }
        public static (LightMap FloorLight, LightMap CeilingLight) Trace(
            Size dimensions,
            Func <Position, bool> isPositionObscured,
            LightRange lightRange,
            IEnumerable <LightDefinition> lights)
        {
            var floorLight   = new LightMap(lightRange, dimensions).Blackout();
            var ceilingLight = new LightMap(lightRange, dimensions).Blackout();

            foreach (var light in lights)
            {
                // Check a big square around the light. This is very brute force but it doesn't appear to cause any
                // problems.
                for (int y = 0; y < light.LengthAffected; y++)
                {
                    for (int x = 0; x < light.LengthAffected; x++)
                    {
                        var delta = new PositionDelta(x, y) - light.Radius;

                        var location = light.Center + delta;

                        if (!dimensions.Contains(location))
                        {
                            continue;
                        }

                        // check for line of sight
                        var obscured =
                            DrawingUtil.BresenhamLine(
                                start: light.Center,
                                end: location)
                            .Any(isPositionObscured);

                        if (!obscured)
                        {
                            var(floorIncrement, ceilingIncrement) = light.GetBrightness(location);

                            floorLight.Lighten(location, floorIncrement);
                            ceilingLight.Lighten(location, ceilingIncrement);
                        }
                    }
                }
            }

            return(floorLight, ceilingLight);
        }