%KWAVEGRID   Class definition for k-space grid structure.
%
% DESCRIPTION:
%       See makeGrid for function arguments.
%
% ABOUT:
%       author      - Bradley Treeby
%       date        - 22nd July 2010
%       last update - 29th January 2011
%       
% This function is part of the k-Wave Toolbox (http://www.k-wave.org)
% Copyright (C) 2009, 2010, 2011 Bradley Treeby and Ben Cox

% This file is part of k-Wave. k-Wave is free software: you can
% redistribute it and/or modify it under the terms of the GNU Lesser
% General Public License as published by the Free Software Foundation,
% either version 3 of the License, or (at your option) any later version.
% 
% k-Wave is distributed in the hope that it will be useful, but WITHOUT ANY
% WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
% FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
% more details. 
% 
% You should have received a copy of the GNU Lesser General Public License
% along with k-Wave. If not, see <http://www.gnu.org/licenses/>.

classdef kWaveGrid    
    
    % define the properties (these parameters are stored)
    properties
        % grid dimensions in pixels/voxels
        Nx = 0;
        Ny = 0;
        Nz = 0;
        
        % pixel/voxel size in metres
      	dx = 0;
        dy = 0;
        dz = 0;
        
        % wavenumber grids
        kx = 0;
        ky = 0;
        kz = 0;
        k = 0;
        
        % maximum supported spatial frequency
        kx_max = 0;
        ky_max = 0;
        kz_max = 0;
        k_max = 0;
        
        % time array
        t_array = 'auto';
        
        % number of dimensions
        dim = 0;
    end
    
    % define the dependent properties (these parameters are computed)
    properties(Dependent = true)
        % Cartesian spatial grids
        x;
        y;
        z;
        
        % 1D spatial grids
        x_vec;
        y_vec;
        z_vec;
        
        % Cartesian grid size
        x_size;
        z_size;
        y_size;
    end
    
    % constructor function
    methods
        function kgrid = kWaveGrid(varargin)
            
            % assign the input values to the grid object
            if nargin == 6
                kgrid.Ny = varargin{3};
                kgrid.dy = varargin{4};
                kgrid.Nz = varargin{5};
                kgrid.dz = varargin{6};
            elseif nargin == 4
                kgrid.Nz = varargin{3};
                kgrid.dz = varargin{4};
            elseif nargin ~= 2
                error('Incorrect number of input arguments');
            end
            kgrid.Nx = varargin{1};
            kgrid.dx = varargin{2};
                       
            switch nargin
                case 2
                    % assign the grid parameters for the x spatial direction
                    kgrid.kx = kgrid.makeDim(kgrid.Nx, kgrid.dx);

                    % define the scalar wavenumber based on the wavenumber components
                    kgrid.k = abs(kgrid.kx);

                    % define maximum supported frequency
                    kgrid.kx_max = max(abs(kgrid.kx(:)));
                    kgrid.k_max = kgrid.kx_max;
                    
                    % set the number of dimensions
                    kgrid.dim = 1;
                case 4
                    % assign the grid parameters for the x and z spatial directions
                    kx_vec = kgrid.makeDim(kgrid.Nx, kgrid.dx);
                    kz_vec = kgrid.makeDim(kgrid.Nz, kgrid.dz);

                    % define plaid grids of the wavenumber components centered about 0
                    [kgrid.kz, kgrid.kx] = ndgrid(kz_vec, kx_vec);

                    % define the scalar wavenumber based on the wavenumber components
                    kgrid.k = sqrt(kgrid.kx.^2 + kgrid.kz.^2);

                    % define maximum supported frequency
                    kgrid.kx_max = max(abs(kgrid.kx(:)));
                    kgrid.kz_max = max(abs(kgrid.kz(:)));
                    kgrid.k_max = min([kgrid.kx_max, kgrid.kz_max]);    
                    
                    % set the number of dimensions
                    kgrid.dim = 2;                    
                case 6
                    % assign the grid parameters for the x ,y and z spatial directions
                    kx_vec = kgrid.makeDim(kgrid.Nx, kgrid.dx);
                    kz_vec = kgrid.makeDim(kgrid.Nz, kgrid.dz);
                    ky_vec = kgrid.makeDim(kgrid.Ny, kgrid.dy);

                    % define plaid grids of the wavenumber components centered about 0
                    [kgrid.kz, kgrid.kx, kgrid.ky] = ndgrid(kz_vec, kx_vec, ky_vec);

                    % define the scalar wavenumber based on the wavenumber components
                    kgrid.k = sqrt(kgrid.kx.^2 + kgrid.ky.^2 + kgrid.kz.^2);

                    % define maximum supported frequency
                    kgrid.kx_max = max(abs(kgrid.kx(:)));
                    kgrid.ky_max = max(abs(kgrid.ky(:)));
                    kgrid.kz_max = max(abs(kgrid.kz(:)));
                    kgrid.k_max = min([kgrid.kx_max, kgrid.ky_max, kgrid.kz_max]);  
                    
                    % set the number of dimensions
                    kgrid.dim = 3;                    
            end
        end
    end
    
    % functions for dependent variables that only run when queried
    methods
        function x = get.x(obj)
            x = obj.x_size*obj.kx*obj.dx/(2*pi);
        end
        
        function y = get.y(obj)
            y = obj.y_size*obj.ky*obj.dy/(2*pi);
        end
        
        function z = get.z(obj)
            z = obj.z_size*obj.kz*obj.dz/(2*pi);
        end        
        
        function x_vec = get.x_vec(obj)
            if numDim(obj.x) == 1
                x_vec = obj.x.';
            else
                x_vec = reshape(obj.x(1, :, 1), 1, []);
            end
        end
        
        function y_vec = get.y_vec(obj)
            y_vec = reshape(obj.y(1, 1, :), 1, []);
        end
        
        function z_vec = get.z_vec(obj)
            z_vec = reshape(obj.z(:, 1, 1), 1, []);
        end  
        
        function x_size = get.x_size(obj)
            x_size = obj.Nx*obj.dx;
        end
        
        function y_size = get.y_size(obj)
            y_size = obj.Ny*obj.dy;
        end
        
        function z_size = get.z_size(obj)
            z_size = obj.Nz*obj.dz;
        end        
    end
   
    % functions that can only be accessed by class members
    methods (Access = 'protected', Static = true) 
        
        % subfunction to create the grid parameters for a single spatial direction
        function kx_vec = makeDim(Nx, dx)

            % define the discretisation of the spatial dimension such that there is
            % always a DC component
            if rem(Nx, 2) == 0
                % grid dimension has an even number of points
                nx = (-0.5:1/Nx:0.5-1/Nx).';
            else
                % grid dimension has an odd number of points
                nx = (-0.5:1/(Nx-1):0.5).';
            end

            % define the wavenumber vector components
            kx_vec = (2*pi/dx).*nx;       
        end
        
    end
end

