static Sphere ReadSphere(IEnumerable <String> ts)
        {
            var ns = from t in ts
                     let p = OptParse(t)
                             where p.HasValue
                             select p.Value;

            if (ns.Count() < 9)
            {
                throw new ApplicationException("Expected 9 values for a sphere.");
            }
            var vs = ns.Take(9).ToArray();

            return(new Sphere()
            {
                Center = Vector.Make(vs[0], vs[1], vs[2]),
                Radius = vs[3],
                Surface = new Surface()
                {
                    Diffuse = pos => Color.Make(vs[4], vs[5], vs[6]),
                    Specular = pos => Color.Make(.5, .5, .5),
                    Reflect = pos => vs[7],
                    Roughness = 200 * vs[8]
                }
            });
        }
        static Camera ReadCamera(IEnumerable <String> ts)
        {
            var ns = from t in ts
                     let p = OptParse(t)
                             where p.HasValue
                             select p.Value;

            if (ns.Count() < 6)
            {
                throw new ApplicationException("Expected 6 values for a camera.");
            }
            var vs = ns.Take(6).ToArray();

            return(Camera.Create(Vector.Make(vs[0], vs[1], vs[2]), Vector.Make(vs[3], vs[4], vs[5])));
        }
        internal static ((int, int), Scene) ReadScene(String fileName)
        {
            var os = new List <SceneObject>();
            var ls = new List <Light>();
            var c  = Camera.Create(Vector.Make(3, 2, 4), Vector.Make(-1, 0.5, 0));
            var v  = (600, 600);

            foreach (var l in File.ReadAllLines(fileName))
            {
                var ts = l.Split(' ', '\t');
                if (ts.Length <= 0)
                {
                    continue;
                }
                switch (ts[0])
                {
                default:
                    break;

                case "#":
                    continue;

                case "sphere":
                    os.Add(ReadSphere(ts.Skip(1)));
                    break;

                case "light":
                    ls.Add(ReadLight(ts.Skip(1)));
                    break;

                case "camera":
                    c = ReadCamera(ts.Skip(1));
                    break;

                case "view":
                    v = ReadView(ts.Skip(1));
                    break;
                }
            }
            return(v, new Scene()
            {
                Things = os.ToArray(), Lights = ls.ToArray(), Camera = c
            });
        }
        static Light ReadLight(IEnumerable <String> ts)
        {
            var ns = from t in ts
                     let p = OptParse(t)
                             where p.HasValue
                             select p.Value;

            if (ns.Count() < 6)
            {
                throw new ApplicationException("Expected 6 values for a light.");
            }
            var vs = ns.Take(6).ToArray();
            var n  = vs.Skip(3).Max();

            if (n < 1.0)
            {
                n = 1.0;
            }
            return(new Light()
            {
                Pos = Vector.Make(vs[0], vs[1], vs[2]),
                Color = Color.Make(vs[3] / n, vs[4] / n, vs[5] / n)
            });
        }