Dose Positions
Dose positions are positions in the patient volume where dose is computed. While this could span the whole patient volume, for some applications a smaller dose volume is preferred which target various regions or structures in the body.
A number of dose position types are provided. There are generally two ways of creating dose positions: through the default constructor, or using a bounding object.
Dose Grids
Dose grids are a subtype of dose positions where each position is located on a Cartesian grid. Each axis need not be uniform, they can take any vector of positions. These are useful as they reduce memory usage, and allow for easy export to visualisation software.
DoseGrid
is a simple Cartesian grid where dose is computed on every point in the box.
They are constructed by supplying grid axes:
julia> axes = (-10.:5.:10, -10.:5.:10, -10.:5.:10)
(-10.0:5.0:10.0, -10.0:5.0:10.0, -10.0:5.0:10.0)
julia> pos = DoseGrid(axes);
When using a bounding object, the dose grid dimensions are set to encompass the whole bounding object. For example, a dose grid bounded by a cylinder will produce the following:
julia> pos = DoseGrid(5., CylinderBounds(10., 10.));
julia> getaxes(pos)
(-5.0:5.0:5.0, -5.0:5.0:5.0, -5.0:5.0:5.0)
It supports both linear and Cartesian indexing:
julia> pos[1]
3-element StaticArraysCore.SVector{3, Float64} with indices SOneTo(3): -5.0 -5.0 -5.0
julia> pos[1, 2, 3]
3-element StaticArraysCore.SVector{3, Float64} with indices SOneTo(3): -5.0 0.0 5.0
Masked Dose Grid
Masked dose grids are Cartesian grids where points can be masked such that they are not used when iterating through the grid. This is an efficient implementation of sparse-like grids which maintain their 3D grid structure while avoiding computations on unnecessary positions.
Masked dose grids (DoseGridMasked
) can be constructed by suppling grid axes and a vector of CartesianIndex
where the points are enabled:
julia> indices = [CartesianIndex(1, 1, 1), CartesianIndex(2, 4, 1)]
2-element Vector{CartesianIndex{3}}: CartesianIndex(1, 1, 1) CartesianIndex(2, 4, 1)
julia> pos = DoseGridMasked(axes, indices);
In this example, only positions at indices [1, 1, 1]
and [2, 4, 1]
are not masked and used in iteration:
julia> for i in eachindex(pos) @show i, pos[i] end
(i, pos[i]) = (1, [-10.0, -10.0, -10.0]) (i, pos[i]) = (2, [-5.0, 5.0, -10.0])
When using a bounding object, the dose grid dimensions are set to encompass the whole bounding object, but points outside the bounding object are masked. For example, a masked dose grid bounded by a cylinder will produce the following:
julia> pos = DoseGridMasked(5., CylinderBounds(10., 10.));
julia> getaxes(pos)
(-5.0:5.0:5.0, -5.0:5.0:5.0, -5.0:5.0:5.0)
julia> length(pos)
15
While the axes still encompass the cylinder, the number of points (length
) are less (15<5^3).
Linear indexing is supported, and Cartesian indexing will work if the index is not masked. For example the following will work,
julia> pos[2]
3-element StaticArraysCore.SVector{3, Float64} with indices SOneTo(3): -5.0 0.0 -5.0
julia> pos[1,1,1]
3-element StaticArraysCore.SVector{3, Float64} with indices SOneTo(3): 0.0 -5.0 -5.0
Suppling an index where the point is masked will throw a BoundsError
,
julia> pos[1,2,1]
ERROR: BoundsError: attempt to access 15-element DoseGridMasked{Float64, StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}, StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}, StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}} at index [1, 2, 1]