diff --git a/docs/make.jl b/docs/make.jl
index 3819485eb..7bf3b15f0 100644
--- a/docs/make.jl
+++ b/docs/make.jl
@@ -21,7 +21,7 @@
# SOFTWARE
using Documenter
-using OpticSim, OpticSim.Geometry
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters
makedocs(
sitename = "OpticSim.jl",
diff --git a/docs/src/emitters_new.md b/docs/src/emitters_new.md
new file mode 100644
index 000000000..ff43fc982
--- /dev/null
+++ b/docs/src/emitters_new.md
@@ -0,0 +1,303 @@
+# Emitters (NEW)
+
+Emitters create rays in a certain pattern, usually controlled by some parameters. Emitters are defined by Pixels and Spatial Layouts, and have a spectrum and an optical power distribution over the hemisphere. These are intrinsic physical properties of the emitter.
+
+The **basic emitter** ([`Source`](@ref sources)) is constructed as a combination of 4 basic elements and a 3D [`Transform`](@ref). The basic elements include:
+- [`Spectrum`](@ref spectrum)
+- [`Angular Power Distribution`](@ref angular_power_distribution)
+- [`Rays Origins Distribution`](@ref rays_origins_distribution)
+- [`Rays Directions Distribution`](@ref rays_directions_distribution)
+
+The [`OpticSim`](index.html) package comes with various implementations of each of these basic elements:
+- Spectrum - the **generate** interface returns a tuple (power, wavelength)
+ * [`Emitters.Spectrum.Uniform`](@ref) - A flat spectrum bounded (default: from 450nm to 680nm). the range is sampled uniformly.
+ * [`Emitters.Spectrum.DeltaFunction`](@ref) - Constant wave length.
+ * [`Emitters.Spectrum.Measured`](@ref) - measured spectrum to compute emitter power and wavelength (created by reading CSV files – more details will follow).
+- Angular Power Distribution - the interface **apply** returns an OpticalRay with modified power
+ * [`Emitters.AngularPower.Lambertian`](@ref)
+ * [`Emitters.AngularPower.Cosine`](@ref)
+ * [`Emitters.AngularPower.Gaussian`](@ref)
+- Rays Origins Distribution - the interface **length** returns the number of samples, and **generate** returns the n'th sample.
+ * [`Emitters.Origins.Point`](@ref) - a single point
+ * [`Emitters.Origins.RectUniform`](@ref) - a uniformly sampled rectangle with user defined number of samples
+ * [`Emitters.Origins.RectGrid`](@ref) - a rectangle sampled in a grid fashion
+ * [`Emitters.Origins.Hexapolar`](@ref) - a circle (or an ellipse) sampled in an hexapolar fasion (rings)
+- Rays Directions Distribution - the interface **length** returns the number of samples, and **generate** returns the n'th sample.
+ * [`Emitters.Directions.Constant`](@ref)
+ * [`Emitters.Directions.RectGrid`](@ref)
+ * [`Emitters.Directions.UniformCone`](@ref)
+ * [`Emitters.Directions.HexapolarCone`](@ref)
+
+## [Examples of Basic Emitters](@id basic_emitters)
+
+**Note**: All the examples on this page assumes the followin statement was executed:
+
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters
+```
+
+### Point origin with various Direction distributions
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide
+src = Sources.Source(origins=Origins.Point(), directions=Directions.RectGrid(π/4, π/4, 15, 15))
+Vis.draw(src, debug=true)
+Vis.save("assets/emitters_example_rect_grid.png") # hide
+nothing #hide
+```
+
+```@raw html
+
+
+
+
+
+ |
+
+An example of RectGrid Direction Distribution
+ |
+
+```
+
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide
+src = Sources.Source(origins=Origins.Point(), directions=Directions.UniformCone(π/6, 1000))
+Vis.draw(src, debug=true)
+Vis.save("assets/emitters_example_uniform_cone.png") # hide
+nothing #hide
+```
+
+```@raw html
+
+
+
+
+
+ |
+
+An example of UniformCone Direction Distribution with 1000 sampled directions
+ |
+
+```
+
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide
+src = Sources.Source(origins=Origins.Point(), directions=Directions.HexapolarCone(π/6, 10))
+Vis.draw(src, debug=true)
+Vis.save("assets/emitters_example_hexapolar_cone.png") # hide
+nothing #hide
+```
+
+```@raw html
+
+
+
+
+
+ |
+
+An example of HexapolarCone Direction Distribution
+ |
+
+```
+
+### Various origins distributions
+
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide
+src = Sources.Source(origins=Origins.RectGrid(1.0, 1.0, 10, 10), directions=Directions.Constant())
+Vis.draw(src, debug=true)
+Vis.save("assets/emitters_example_origin_rectgrid.png") # hide
+nothing #hide
+```
+
+```@raw html
+
+
+
+
+
+ |
+
+An example of RectGrid origin distribution
+ |
+
+```
+
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide
+src = Sources.Source(origins=Origins.Hexapolar(5, 1.0, 2.0), directions=Directions.Constant())
+Vis.draw(src, debug=true)
+Vis.save("assets/emitters_example_origin_hexapolar.png") # hide
+nothing #hide
+```
+
+```@raw html
+
+
+
+
+
+ |
+
+An example of Hexapolar origin distribution
+ |
+
+```
+
+### Examples of Angular Power Distribution
+
+In these example, the arrow width is proportional to the ray power.
+
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide
+src = Sources.Source(
+ origins=Origins.Hexapolar(1, 8.0, 8.0), # Hexapolar Origins
+ directions=Directions.RectGrid(π/6, π/6, 15, 15), # RectGrid Directions
+ power=AngularPower.Cosine(10.0) # Cosine Angular Power
+)
+Vis.draw(src, debug=true)
+Vis.save("assets/emitters_example_angular1.png") # hide
+nothing #hide
+```
+
+```@raw html
+
+
+
+
+
+ |
+
+An example of Cosine angular power distribution
+ |
+
+```
+
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide
+src = Sources.Source(
+ origins=Origins.RectGrid(1.0, 1.0, 3, 3), # RectGrid Origins
+ directions=Directions.HexapolarCone(π/6, 10), # HexapolarCone Directions
+ power=AngularPower.Gaussian(2.0, 2.0) # Gaussian Angular Power
+)
+Vis.draw(src, debug=true)
+Vis.save("assets/emitters_example_angular2.png") # hide
+nothing #hide
+```
+
+```@raw html
+
+
+
+
+
+ |
+
+An example of Gaussian angular power distribution
+ |
+
+```
+
+### Composite Sources - Display Example
+
+```@example
+using OpticSim, OpticSim.Geometry, OpticSim.Emitters # hide
+
+# construct the emitter's basic components
+S = Spectrum.Uniform()
+P = AngularPower.Lambertian()
+O = Origins.RectGrid(1.0, 1.0, 3, 3)
+D = Directions.HexapolarCone(deg2rad(5.0), 3)
+
+# construct the srouce. in this example a "pixel" source will contain only one source as we are simulating a "b/w" display.
+# for RGB displays we can combine 3 sources to simulate "a pixel".
+Tr = Transform(Vec3(0.5, 0.5, 0.0))
+source1 = Sources.Source(Tr, S, O, D, P)
+
+# create a list of pixels - each one is a composite source
+pixels = Vector{Sources.CompositeSource{Float64}}(undef, 0)
+for y in 1:5 # image_height
+ for x in 1:10 # image_width
+ # pixel position relative to the display's origin
+ local pixel_position = Vec3((x-1) * 1.1, (y-1) * 1.5, 0.0)
+ local Tr = Transform(pixel_position)
+
+ # constructing the "pixel"
+ pixel = Sources.CompositeSource(Tr, [source1])
+
+ push!(pixels, pixel)
+ end
+end
+
+Tr = Transform(Vec3(0.0, 0.0, 0.0))
+my_display = Sources.CompositeSource(Tr, pixels)
+
+Vis.draw(my_display) # render the display - nothing bu the origins primities
+rays = AbstractArray{OpticalRay{Float64, 3}}(collect(my_display)) # collect the rays generated by the display
+Vis.draw!(rays) # render the rays
+
+Vis.save("assets/emitters_example_composite_display.png") # hide
+nothing #hide
+```
+
+```@raw html
+
+
+
+
+
+ |
+
+A display example using composite sources.
+ |
+
+```
+
+
+
+## [Spectrum](@id spectrum)
+
+```@docs
+Emitters.Spectrum.Uniform
+Emitters.Spectrum.DeltaFunction
+Emitters.Spectrum.Measured
+```
+
+## [Angular Power Distribution](@id angular_power_distribution)
+
+```@docs
+Emitters.AngularPower.Lambertian
+Emitters.AngularPower.Cosine
+Emitters.AngularPower.Gaussian
+```
+
+## [Rays Origins Distribution](@id rays_origins_distribution)
+
+```@docs
+Emitters.Origins.Point
+Emitters.Origins.RectUniform
+Emitters.Origins.RectGrid
+Emitters.Origins.Hexapolar
+```
+
+## [Rays Directions Distribution](@id rays_directions_distribution)
+
+```@docs
+Emitters.Directions.Constant
+Emitters.Directions.RectGrid
+Emitters.Directions.UniformCone
+Emitters.Directions.HexapolarCone
+```
+
+## [Sources](@id sources)
+
+```@docs
+Emitters.Sources.Source
+Emitters.Sources.CompositeSource
+```
+
+
+
+
+
+
diff --git a/src/Optical/Emitters.jl b/src/Optical/Emitters.jl
index 5b918975e..a4a9d5fe6 100644
--- a/src/Optical/Emitters.jl
+++ b/src/Optical/Emitters.jl
@@ -47,9 +47,17 @@ const UNIFORMLONG = 0.680 #um
#---------------------------------------
# Uniform Spectrum
#---------------------------------------
+"""
+Encapsulates a flat spectrum range which is sampled uniformly. Unless stated diferrently, the range used will be 450nm to 680nm.
+
+```julia
+Uniform(low::T, high::T) where {T<:Real}
+Uniform(::Type{T} = Float64) where {T<:Real}
+```
+"""
struct Uniform{T} <: AbstractSpectrum{T}
low_end::T
- high_end::T
+ high_end::T
# user defined range of spectrum
function Uniform(low::T, high::T) where {T<:Real}
@@ -68,6 +76,13 @@ Emitters.generate(s::Uniform{T}) where {T} = (T(1), rand(Distributions.Uniform(s
#---------------------------------------
# Delta Function Spectrum
#---------------------------------------
+"""
+Encapsulates a constant spectrum.
+
+```julia
+DeltaFunction{T<:Real}
+```
+"""
struct DeltaFunction{T<:Real} <: AbstractSpectrum{T}
λ::T
end
@@ -79,6 +94,14 @@ Emitters.generate(s::DeltaFunction{T}) where {T} = (T(1), s.λ)
# Measured Spectrum
#---------------------------------------
+"""
+Encapsulates a measured spectrum to compute emitter power. Create spectrum by reading CSV files.
+Evaluate spectrum at arbitrary wavelength with [`spectrumpower`](@ref) (**more technical details coming soon**)
+
+```julia
+Measured(samples::DataFrame)
+```
+"""
struct Measured{T<:Real} <: AbstractSpectrum{T}
low_wave_length::Int
high_wave_length::Int
@@ -174,6 +197,14 @@ Base.copy(a::AbstractDirectionDistribution) = a # most don't have any heap alloc
#---------------------------------------
# Constant Ray Direction
#---------------------------------------
+"""
+Encapsulates a single ray direction, where the default direction is unitZ3 [0, 0, 1].
+
+```julia
+Constant(direction::Vec3{T}) where {T<:Real}
+Constant(::Type{T} = Float64) where {T<:Real}
+```
+"""
struct Constant{T} <: AbstractDirectionDistribution{T}
direction::Vec3{T}
@@ -196,6 +227,14 @@ end
#---------------------------------------
# Grid Ray Distribution
#---------------------------------------
+"""
+Encapsulates a single ray direction, where the default direction is unitZ3 [0, 0, 1].
+
+```julia
+Constant(direction::Vec3{T}) where {T<:Real}
+Constant(::Type{T} = Float64) where {T<:Real}
+```
+"""
struct RectGrid{T} <: AbstractDirectionDistribution{T}
direction::Vec3{T}
halfangleu::T
@@ -237,6 +276,14 @@ end
#---------------------------------------
# Cone Ray Distribution
#---------------------------------------
+"""
+Encapsulates a numsamples rays sampled uniformlly from a cone with max angle θmax.
+
+```julia
+UniformCone(direction::Vec3{T}, θmax::T, numsamples::Int) where {T<:Real}
+UniformCone(θmax::T, numsamples::Int) where {T<:Real}
+```
+"""
struct UniformCone{T} <: AbstractDirectionDistribution{T}
direction::Vec3{T}
θmax::T
@@ -271,6 +318,15 @@ end
#----------------------------------------------------------------------------
# Cone Ray Hexapolar Distribution
#-----------------------------------------------------------------------------
+"""
+Rays are generated by sampling a cone with θmax angle in an hexapolar fasion. The number of rays depends on the requested rings and is computed using the following formula:
+`1 + round(Int, (nrings * (nrings + 1) / 2) * 6)`
+
+```julia
+HexapolarCone(direction::Vec3{T}, θmax::T, nrings::Int) where {T<:Real}
+HexapolarCone(θmax::T, nrings::Int = 3) where {T<:Real}
+```
+"""
struct HexapolarCone{T} <: AbstractDirectionDistribution{T}
direction::Vec3{T}
θmax::T
@@ -354,6 +410,15 @@ export generate
#--------------------------------------
# Point Origin
#--------------------------------------
+"""
+Encapsulates a single point origin.
+
+```julia
+Point(position::Vec3{T}) where {T<:Real}
+Point(x::T, y::T, z::T) where {T<:Real}
+Point(::Type{T} = Float64) where {T<:Real}
+```
+"""
struct Point{T} <: AbstractOriginDistribution{T}
origin::Vec3{T}
@@ -382,6 +447,13 @@ end
#-------------------------------------
# Random Rectangle Origin
#-------------------------------------
+"""
+Encapsulates a uniformly sampled rectangle with user defined number of samples.
+
+```julia
+RectUniform(width::T, height::T, count::Int) where {T<:Real}
+```
+"""
struct RectUniform{T} <: AbstractOriginDistribution{T}
width::T
height::T
@@ -409,6 +481,13 @@ end
#-------------------------------------
# Grid Rectangle Origin
#-------------------------------------
+"""
+Encapsulates a rectangle sampled in a grid fashion.
+
+```julia
+RectGrid(width::T, height::T, usamples::Int, vsamples::Int) where {T<:Real}
+```
+"""
struct RectGrid{T} <: AbstractOriginDistribution{T}
width::T
height::T
@@ -438,8 +517,13 @@ end
#-------------------------------------
# Hexapolar Origin
#-------------------------------------
+"""
+Encapsulates an ellipse (or a circle where halfsizeu=halfsizev) sampled in an hexapolar fasion (rings)
-# TODO: Finish this one
+```julia
+Hexapolar(nrings::Int, halfsizeu::T, halfsizev::T) where {T<:Real}
+```
+"""
struct Hexapolar{T} <: AbstractOriginDistribution{T}
halfsizeu::T
halfsizev::T
@@ -498,7 +582,9 @@ abstract type AbstractAngularPowerDistribution{T<:Real} end
#---------------------------------------
# Lambertian
#---------------------------------------
-
+"""
+Ray power is unaffected by angle.
+"""
struct Lambertian{T} <: AbstractAngularPowerDistribution{T}
Lambertian(::Type{T} = Float64) where {T<:Real} = new{T}()
end
@@ -511,7 +597,13 @@ export Lambertian
#---------------------------------------
# Cosine Power Distribution
#---------------------------------------
+"""
+ Cosine(cosine_exp::T = one(T)) where {T<:Real}
+
+Cosine power distribution. Ray power is calculated by:
+`power = power * (cosine_angle ^ cosine_exp)`
+"""
struct Cosine{T} <: AbstractAngularPowerDistribution{T}
cosine_exp::T
@@ -531,7 +623,14 @@ end
#---------------------------------------
# Gaussian Power Distribution
#---------------------------------------
+"""
+ Gaussian(gaussianu::T, gaussianv::T) where {T<:Real}
+GGaussian power distribution. Ray power is calculated by:
+
+`power = power * exp(-(gaussianu * l^2 + gaussianv * m^2))`
+where l and m are the cos_angles between the two axes respectivly.
+"""
struct Gaussian{T} <: AbstractAngularPowerDistribution{T}
gaussianu::T
gaussianv::T
@@ -582,6 +681,33 @@ Base.copy(a::AbstractSource) = a # most don't have any heap allocated stuff so d
#---------------------------------------
# Source
#---------------------------------------
+"""
+This data-type represents the basic emitter (Source), which is a combination of a Spectrum, Angular Power Distribution, Origins and Directions distibution and a 3D Transform.
+
+```julia
+Source(::Type{T} = Float64;
+ transform::Tr = Transform(),
+ spectrum::S = Spectrum.Uniform(),
+ origins::O = Origins.Point(),
+ directions::D = Directions.Constant(),
+ power::P = AngularPower.Lambertian(),
+ sourcenum::Int = 0) where {
+ Tr<:Transform,
+ S<:Spectrum.AbstractSpectrum,
+ O<:Origins.AbstractOriginDistribution,
+ D<:Directions.AbstractDirectionDistribution,
+ P<:AngularPower.AbstractAngularPowerDistribution,
+ T<:Real}
+Source(transform::Tr, spectrum::S, origins::O, directions::D, power::P, ::Type{T} = Float64; sourcenum::Int = 0) where {
+ Tr<:Transform,
+ S<:Spectrum.AbstractSpectrum,
+ O<:Origins.AbstractOriginDistribution,
+ D<:Directions.AbstractDirectionDistribution,
+ P<:AngularPower.AbstractAngularPowerDistribution,
+ T<:Real}
+
+```
+"""
struct Source{ T<:Real,
Tr<:Transform{T},
S<:Spectrum.AbstractSpectrum{T},
@@ -621,6 +747,7 @@ struct Source{ T<:Real,
new{T, Tr, S, O, D, P}(transform, spectrum, origins, directions, power, sourcenum)
end
end
+export Source
# used to not generate new origin points if we can use last points - mainly to keep consistency of origin generation when randomness is involved
struct SourceGenerationState{T<:Real}
@@ -680,6 +807,13 @@ end
#---------------------------------------
# Composite Source
#---------------------------------------
+"""
+This data-type represents the composit emitter (Source) which is constructed with a list of basic or composite emitters and a 3D Transform.
+
+```julia
+CompositeSource(transform::Transform{T}, sources::Array, ::Type{T} = Float64) where {T<:Real}
+```
+"""
struct CompositeSource{T} <: AbstractSource{T}
transform::Transform{T}
sources::Array
@@ -715,7 +849,7 @@ struct CompositeSource{T} <: AbstractSource{T}
new{T}(transform, sources, uniform_length, total_length, start_indexes)
end
end
-
+export CompositeSource
Base.length(s::CompositeSource{T}) where {T} = s.total_length
@@ -949,4 +1083,4 @@ export Geometry, Spectrum, Directions, Origins, AngularPower, Sources
end # module Emitters
-
+export Emitters