/// <summary> /// Creates a new instance of a complex lens using a definition of /// elements. /// </summary> /// <remarks> /// The first and last surfaces have to be spherical. TODO: this is /// needed only for simpler sampling. In general planar surfaces or /// stops could be sampled too. /// </remarks> /// <param name="surfaceDefs">List of definitions of spherical or /// planar element surfaces or stops. Ordered from front to back. /// Must not be empty or null. /// </param> /// <param name="mediumRefractiveIndex">Index of refraction of medium /// outside the lens. It is assumed there is one medium on the scene /// side, senzor side and inside the lens.</param> /// <returns>The created complex lens instance.</returns> public static ComplexLens Create( IList<SphericalElementSurfaceDefinition> surfaceDefs, double mediumRefractiveIndex, double scale) { var surfaces = new List<ElementSurface>(); var surfaceDefsReverse = surfaceDefs.Reverse().ToList(); // scale the lens if needed if (Math.Abs(scale - 1.0) > epsilon) { surfaceDefsReverse = surfaceDefsReverse.Select(surface => surface.Scale(scale)).ToList(); } // thickness of the whole lens (from front to back apex) // (without the distance to the senzor - backmost surface def.) double lensThickness = surfaceDefsReverse.Skip(1).Sum(def => def.Thickness); double elementBasePlaneShiftZ = lensThickness; double lastCapHeight = 0; double capHeight = 0; // definition list is ordered from front to back, working list // must be ordered from back to front, so a conversion has to be // performed int defIndex = 0; foreach (var definition in surfaceDefsReverse) { if (defIndex > 0) { elementBasePlaneShiftZ -= definition.Thickness; } ElementSurface surface = new ElementSurface(); surface.ApertureRadius = 0.5 * definition.ApertureDiameter; if (defIndex + 1 < surfaceDefsReverse.Count) { surface.NextRefractiveIndex = surfaceDefsReverse[defIndex + 1].NextRefractiveIndex; } else { surface.NextRefractiveIndex = mediumRefractiveIndex; } if (definition.CurvatureRadius.HasValue) { // spherical surface double radius = definition.CurvatureRadius.Value; // convexity reverses when converting from front-to-back // back-to-front ordering surface.Convex = radius < 0; Sphere sphere = new Sphere() { Radius = Math.Abs(radius) }; sphere.Center = Math.Sign(radius) * sphere.GetCapCenter(surface.ApertureRadius, Vector3d.UnitZ); capHeight = Math.Sign(radius) * sphere.GetCapHeight(sphere.Radius, surface.ApertureRadius); elementBasePlaneShiftZ -= lastCapHeight - capHeight; sphere.Center += new Vector3d(0, 0, elementBasePlaneShiftZ); surface.Surface = sphere; surface.SurfaceNormalField = sphere; } else { // planar surface // both media are the same -> circular stop // else -> planar element surface surface.NextRefractiveIndex = definition.NextRefractiveIndex; surface.Convex = true; capHeight = 0; elementBasePlaneShiftZ -= lastCapHeight - capHeight; Circle circle = new Circle() { Radius = 0.5 * definition.ApertureDiameter, Z = elementBasePlaneShiftZ, }; surface.Surface = circle; surface.SurfaceNormalField = circle; } lastCapHeight = capHeight; surfaces.Add(surface); defIndex++; } //DEBUG //foreach (var surface in surfaces) //{ // Console.WriteLine("{0}, {1}, {2}", surface.ApertureRadius, // surface.Convex, surface.NextRefractiveIndex); //} ComplexLens lens = new ComplexLens(surfaces) { MediumRefractiveIndex = mediumRefractiveIndex }; return lens; }