classdef SurfacePlotter < fusion.theaterPlot.TheaterPlotter
    %SurfacePlotter Create a new surface plotter
    %
    %   See:  <a href="matlab:help theaterPlot/surfacePlotter">theaterPlot/surfacePlotter</a>

    %   Copyright 2022-2023 The MathWorks, Inc.

    properties (Dependent)
        %FaceAlpha    Surface face transparency
        % The alpha value to use for surface faces, specified as a scalar.
        % The same alpha value is used for all plotted surfaces.
        %
        % Default: 1
        FaceAlpha
        
        %EdgeColor    Surface edge color
        % The color to use for surface edges, specified as a 'color' string
        % or an [R, G, B] vector. Can be 'none' to disable edges.
        %
        % Default: 'black'
        EdgeColor
    end
    
    properties (Access = private)
        Patch
    end
    
    methods
        function obj = SurfacePlotter(tp, varargin)
            obj@fusion.theaterPlot.TheaterPlotter(tp, varargin{:});
        end
        
        function plotSurface(obj, plotdata)
            validateattributes(plotdata,{'struct'},{'vector'},'SurfacePlotter','plotdata')
            obj.validatePlotData(plotdata)
            
            drawSurface(obj,plotdata)
            drawnow limitrate
        end
        
        function clearData(obj)
            %clearData remove all data from a plotter
            %   clearData(trajPlotter) removes all trajectories associated with the
            %   plotter, trajPlotter.  
            drawSurface(obj, []);
            drawnow limitrate
        end
        
        function value = get.FaceAlpha(obj)
            hSurf = obj.Patch(1);
            value = hSurf.FaceAlpha;
        end
        
        function set.FaceAlpha(obj,value)
            for ind = 1:numel(obj.Patch)
                hSurf = obj.Patch(ind);
                if ishghandle(hSurf)
                    hSurf.FaceAlpha = value;
                end
            end
            drawnow limitrate
        end
        
        function value = get.EdgeColor(obj)
            hSurf = obj.Patch(1);
            value = hSurf.EdgeColor;
        end
        
        function set.EdgeColor(obj,value)
            for ind = 1:numel(obj.Patch)
                hSurf = obj.Patch(ind);
                if ishghandle(hSurf)
                    hSurf.EdgeColor = value;
                end
            end
            drawnow limitrate
        end
        
    end
    
    methods (Access = protected)
        function initParser(obj, p)
            initParser@fusion.theaterPlot.TheaterPlotter(obj, p);
            addParameter(p,'FaceAlpha',1);
            addParameter(p,'EdgeColor','black')
        end

        function value = getPrimaryObject(obj)
            value = obj.Patch(1);
        end
        
        function createGraphics(obj, hAxes, p)
            pr = p.Results;
            
            hPatch = matlab.graphics.primitive.Patch('Parent',hAxes,...
                'Faces',[],'Vertices',[],...
                'FaceAlpha',pr.FaceAlpha,'EdgeColor',pr.EdgeColor,...
                'FaceColor','flat');

            if isempty(pr.DisplayName)
                hPatch.DisplayName = '';
                hPatch.Tag = 'tpSurfaces_';
                hPatch.HandleVisibility = 'off';
            else
                hPatch.DisplayName = pr.DisplayName;
                hPatch.Tag = ['tpSurfaces_' pr.DisplayName];
            end

            % apply theme defaults when using defaults
            pu = p.UsingDefaults;
            if ismember('EdgeColor',pu)
                matlab.graphics.internal.themes.specifyThemePropertyMappings( ...
                    hPatch,'EdgeColor','--mw-graphics-borderColor-axes-primary');
            end
            
            obj.Patch = hPatch;
        end
        
        function deleteGraphics(obj)
            delete(obj.Patch);
        end
    end
    
    methods (Access = private)
        function drawSurface(obj,pd)
            
            if isempty(pd)
                % clear data
                for ind = 1:numel(obj.Patch)
                    hPatch = obj.Patch(ind);
                    if ishghandle(hPatch)
                        set(hPatch,'Vertices',[],'Faces',[],'FaceVertexCData',[]);
                    end
                end
            end
            
            for ind = 1:numel(pd)

                if numel(obj.Patch) < ind
                    hPatch = copyobj(obj.Patch(1),obj.ParentPlot.Parent);
                    obj.Patch(end+1) = hPatch;
                else
                    hPatch = obj.Patch(ind);
                end
                
                isposinfx = pd(ind).X == inf;
                isneginfx = pd(ind).X == -inf;
                isposinfy = pd(ind).Y == inf;
                isneginfy = pd(ind).Y == -inf;
                
                if any(isposinfx,'all') || any(isneginfx,'all') || any(isposinfy,'all') || any(isneginfy,'all')
                    if ishghandle(hPatch)
                        set(hPatch,'XLimInclude',false,'YLimInclude',false)
                    end
                else
                    if ishghandle(hPatch)
                        set(hPatch,'XLimInclude',true,'YLimInclude',true)
                    end
                end
                
                pd(ind).X(isposinfx) = obj.ParentPlot.XLimits(2);
                pd(ind).X(isneginfx) = obj.ParentPlot.XLimits(1);
                pd(ind).Y(isposinfy) = obj.ParentPlot.YLimits(2);
                pd(ind).Y(isneginfy) = obj.ParentPlot.YLimits(1);

                [f,v,c] = surf2patch(pd(ind).X,pd(ind).Y,pd(ind).Z,pd(ind).C);
                
                ea = obj.edgeLengthToAlpha(max(size(pd(ind).Z)));
                
                if ishghandle(hPatch)
                    set(hPatch,'Vertices',v,'Faces',f,'FaceVertexCData',c,'EdgeAlpha',ea);
                end
                
            end
            
        end
        
    end
    
    methods(Access = private, Static)
        
        function validatePlotData(pd)
            if ~isfield(pd(1),'X') || ~isfield(pd(1),'Y') ...
                    || ~isfield(pd(1),'Z') || ~isfield(pd(1),'C')
                error(message('shared_radarfusion:theaterPlot:SurfacePlotterDataWrongFields'))
            end
            for ind = 1:numel(pd)
                nx = numel(pd(ind).X);
                ny = numel(pd(ind).Y);
                sz = size(pd(ind).Z);
                sc = size(pd(ind).C);
                % Both domain fields must be vectors
                checkDomVecs = isvector(pd(ind).X) && isvector(pd(ind).Y);
                % Rows and columns of Z must match domain size
                checkSizeZ = sz(1)==ny && sz(2)==nx;
                % Rows and columns of C must match domain size or be
                % singleton
                checkSizeC = (sc(1)==1 && sc(2)==1) || (sc(1)==ny && sc(2)==nx);
                if ~checkDomVecs || ~checkSizeZ || ~checkSizeC
                    error(message('shared_radarfusion:theaterPlot:SurfacePlotterDataWrongFieldSizes'))
                end
                if numel(sc) ~= 3 || sc(3) ~= 3
                    error(message('shared_radarfusion:theaterPlot:SurfacePlotterDataInvalidColorsArray'))
                end
            end
        end
        
        function a = edgeLengthToAlpha(n)
            % Choose edge alpha as a function of edge length
            K1 = 0.06;
            K2 = 0.30;            
            if n < (1-K2)/K1
                a = 1;
            else
                a = 1/(K1*n+K2);
            end            
        end
        
    end

end

% LocalWords:  plotdata traj tp mw
