www.gusucode.com > mbcview 工具箱matlab源码程序 > mbcview/+cageview/+optimoutput/Values2dView.m

    classdef Values2dView < mbcgui.multiview.View
    %cageview.optimoutput.Values2dView class
    %   cageview.optimoutput.Values2dView extends mbcgui.multiview.View.
    %
    %    cageview.optimoutput.Values2dView properties:
    %       Parent - Property is of type 'MATLAB array'
    %       Position - Property is of type 'rect'
    %       Enable - Property is of type 'on/off'
    %       Visible - Property is of type 'on/off'
    %       UserData - Property is of type 'MATLAB array'
    %       Tag - Property is of type 'string'
    %       MessageService - Property is of type 'handle'
    %       Options - Property is of type 'handle vector'
    %       Actions - Property is of type 'handle vector'
    %       UIContextMenu - Property is of type 'MATLAB array'
    %       DisplayData - Property is of type 'ustring'
    %       CanRotate - Property is of type 'bool'
    %
    %    cageview.optimoutput.Values2dView methods:
    %       createDefaultWindowContainer - Create an appropriate container for the view
    %       gettitle - Return string to use as title for view
    %       setDisplayFilter - Set the display filter
    
    %  Copyright 2007-2015 The MathWorks, Inc.
    
    properties (AbortSet, SetObservable)
        %DISPLAYDATA Property is of type 'ustring'
        DisplayData = 'All';
        %CANROTATE Property is of type 'bool'
        CanRotate = true;
    end
    
    properties (Access=protected, AbortSet)
        %OUTPUTVALUES Property is of type 'MATLAB array'
        OutputValues = struct( 'Data', [  ], 'bRedIdx', [  ], 'bOrangeIdx', [  ], 'bGreenIdx', [  ] );
        %HSELECTOR Property is of type 'handle'
        hSelector = [];
        %HREDLINE Property is of type 'MATLAB array'
        hRedLine = [];
        %HORANGELINE Property is of type 'MATLAB array'
        hOrangeLine = [];
        %HGREENLINE Property is of type 'MATLAB array'
        hGreenLine = [];
        %HCURRENTPOINT Property is of type 'MATLAB array'
        hCurrentPoint = [];
        %HTEXT Property is of type 'MATLAB array'
        hText = [];
        %VIEWACTION Property is of type 'handle'
        ViewAction = [];
    end
    
    
    methods  % constructor block
        function obj = Values2dView(varargin)
        %Values2dView Constructor for Values2dView
        %   OBJ = Values2dView(PROP, VALUE) constructs a class that
        %   displays the results of an optimization on a 3-d plot.
        %
        %   Subclasses can additionally display extrapolations or fits through the
        %   optimization results.
        
        % Call the inherited constructor
        obj@mbcgui.multiview.View(varargin{ : }); % converted super class constructor call
        
        % Create the factor selector object. This creates the axes.
        SC = xregGui.SystemColorsDbl;
        obj.hSelector = mbcwidgets.factorselector3d( ...
            'Parent',obj.Parent, ...
            'Visible', obj.Visible, ...
            'BackgroundColor', SC.CTRL_BG, ...
            'SelectionType', 'exclusive', ...
            'HasAxes', true, ...
            'UIContextMenu',obj.UIContextMenu,...
            'FactorChangeCallback', {@i_inputschanged, obj});
        
        % Set initial axes properties.
        set(obj.hSelector.Axes, 'Color', 'w', 'Box', 'on', ...
            'XGrid', 'on', 'YGrid', 'on','ZGrid','on', ...
            'ButtonDownFcn', @obj.axesClick);
        view(obj.hSelector.Axes, 3);
        
        % Deal with rotation
        
        % Create the lines for the solution points
        obj.hRedLine = line('Parent', obj.hSelector.Axes,...
            'Tag', 'RedLine',...
            'Visible',get(obj.hSelector.Axes, 'Visible'),...
            'LineStyle', 'none', ...
            'Marker', 'o', ...
            'MarkerSize', 8, ...
            'MarkerFaceColor', 'r', ...
            'MarkerEdgeColor', 'k', ...
            'Color', 'r', ...
            'XData', [],...
            'YData', [],...
            'ZData', [],...
            'ButtonDownFcn', {@i_redLineButtonDown, obj, false});
        obj.hRedLine(2) = line('Parent', obj.hSelector.Axes,...
            'Tag', 'RedLine_Accepted',...
            'Visible',get(obj.hSelector.Axes, 'Visible'),...
            'LineStyle', 'none', ...
            'Marker', 'p', ...
            'MarkerSize', 14, ...
            'MarkerFaceColor', 'r', ...
            'MarkerEdgeColor', 'k', ...
            'Color', 'r', ...
            'XData', [],...
            'YData', [],...
            'ZData', [],...
            'ButtonDownFcn', {@i_redLineButtonDown, obj, true});
        
        cOrange = [1 0.69 0.39];
        obj.hOrangeLine = line('Parent', obj.hSelector.Axes,...
            'Tag', 'OrangeLine',...
            'Visible',get(obj.hSelector.Axes, 'Visible'),...
            'LineStyle', 'none', ...
            'Marker', '^', ...
            'MarkerSize', 10, ...
            'MarkerFaceColor', cOrange, ...
            'MarkerEdgeColor', 'k', ...
            'Color', cOrange, ...
            'XData', [],...
            'YData', [],...
            'ZData', [],...
            'ButtonDownFcn', {@i_orangeLineButtonDown, obj, false});
        obj.hOrangeLine(2) = line('Parent', obj.hSelector.Axes,...
            'Tag', 'OrangeLine_Accepted',...
            'Visible',get(obj.hSelector.Axes, 'Visible'),...
            'LineStyle', 'none', ...
            'Marker', 'p', ...
            'MarkerSize', 14, ...
            'MarkerFaceColor', cOrange, ...
            'MarkerEdgeColor', 'k', ...
            'Color', cOrange, ...
            'XData', [],...
            'YData', [],...
            'ZData', [],...
            'ButtonDownFcn', {@i_orangeLineButtonDown, obj, true});
        obj.hGreenLine = line('Parent', obj.hSelector.Axes,...
            'Tag', 'GreenLine',...
            'Visible',get(obj.hSelector.Axes, 'Visible'),...
            'LineStyle', 'none', ...
            'Marker', 's', ...
            'MarkerSize', 10, ...
            'MarkerFaceColor', 'g', ...
            'MarkerEdgeColor', 'k', ...
            'Color', 'g', ...
            'XData', [],...
            'YData', [],...
            'ZData', [],...
            'ButtonDownFcn', {@i_greenLineButtonDown, obj, false});
        obj.hGreenLine(2) = line('Parent', obj.hSelector.Axes,...
            'Tag', 'GreenLine_NotAccepted',...
            'Visible',get(obj.hSelector.Axes, 'Visible'),...
            'LineStyle', 'none', ...
            'Marker', 'p', ...
            'MarkerSize', 14, ...
            'MarkerFaceColor', 'g', ...
            'MarkerEdgeColor', 'k', ...
            'Color', 'g', ...
            'XData', [],...
            'YData', [],...
            'ZData', [],...
            'ButtonDownFcn', {@i_greenLineButtonDown, obj, true});
        
        % Current point
        obj.hCurrentPoint = line('Parent', obj.hSelector.Axes,...
            'Visible',get(obj.hSelector.Axes, 'Visible'),...
            'LineStyle', 'none', ...
            'Marker', 'o', ...
            'MarkerSize', 16, ...
            'MarkerEdgeColor', 'k', ...
            'MarkerFaceColor', 'w', ...
            'LineWidth', 2.5, ...
            'Color', 'w', ...
            'XData', [],...
            'YData', [],...
            'ZData', [], ...
            'Tag', 'CurrentPoint');
        uistack(obj.hCurrentPoint, 'bottom');
        
        % "No display" text
        obj.hText = text('Parent', obj.hSelector.Axes, ...
            'Position', [.5 .5 .5], ...
            'HorizontalAlignment', 'center', ...
            'VerticalAlignment', 'middle', ...
            'Clipping', 'on', ...
            'Visible', 'off');
        
        % Layout
        L = xreggridbaglayout(obj.Parent, ...
            'Dimension', [1 1], ...
            'position', obj.Position, ...
            'elements', {obj.hSelector});
        obj.ContentHandle = L;
        
        createActions(obj);
        
        % Hook up to the message service if it exists
        if ~isempty(obj.MessageService)
            obj.pPostSetMessageService;
        end
        end  % Values2dView
        
    end  % constructor block
    
    methods
        function set.DisplayData(obj,value)
        obj.DisplayData = i_setDisplayData(obj,value);
        end
        
    end   % set and get functions
    
    methods  % public methods
        %----------------------------------------
        function hPane = createDefaultWindowContainer(obj,varargin)
        %CREATEDEFAULTWINDOWCONTAINER Create an appropriate container for the view
        %  HCONAINER = CREATEDEFAULTWINDOWCONTAINER(OBJ) creates and returns a
        %  handle to a ViewContainer that has been set up to contain OBJ.  The
        %  container should be appropriate for viewing OBJ in a standard
        %  application window.  The default implementation creates a
        %  PanelTitleViewContainer.
        
        % Call the super class implementation
        hPane = createDefaultWindowContainer@mbcgui.multiview.View(obj,varargin{:});
        % Ensure the background color is the window background
        SC = xregGui.SystemColorsDbl;
        hPane.BackgroundColor = SC.CTRL_BG;
        
        end  % createDefaultWindowContainer
        
        %----------------------------------------
        function str = gettitle(obj) %#ok<MANU>
        %GETTITLE Return string to use as title for view
        %  STR = GETTITLE(OBJ) returns a string that should be used as a title for
        %  the container the view sits in.
        
        str = 'Results 2d View';
        
        end  % gettitle
        
        %----------------------------------------
        function setDisplayFilter(obj, DisplayData)
        %SETDISPLAYFILTER Set the display filter
        %   OUT = SETDISPLAYFILTER(OBJ) sets the filter that is used to remove
        %   points from the display.
        
        set(obj.ViewAction.Actions(1).Actions, 'Selected', false);
        obj.DisplayData = DisplayData;
        obj.pRefresh;
        obj.pRefreshCurrentPoint;
        DisplayActions = obj.ViewAction.Actions(1).Actions;
        switch obj.DisplayData
            case 'Acceptable'
                DisplayActions(2).Selected = true;
            case 'Green'
                DisplayActions(3).Selected = true;
            case 'Orange'
                DisplayActions(4).Selected = true;
            case 'Red'
                DisplayActions(5).Selected = true;
            otherwise
                DisplayActions(1).Selected = true;
        end
        
        
        
        end  % setDisplayFilter
        
    end  % public methods
    
    methods (Access=protected)
        %----------------------------------------
        function ExportAx = pCopyLinesToPrintAxes(obj, ExportAx)
        %PCOPYLINESTOPRINTAXES Copy lines to the supplied printable axes
        %   EXPORTAX = PCOPYLINESTOPRINTAXES(OBJ, EXPORTAX) copies the lines
        %   containing the optimization results to the supplied printable axes. Any
        %   button down functions on the lines are removed.
        
        % Get all the lines in the 2-d view
        lines = [obj.hCurrentPoint; obj.hRedLine(:); obj.hOrangeLine(:); ...
            obj.hGreenLine(:)];
        
        % Set the buttondownfcns on the lines to be empty
        set(lines, 'ButtonDownFcn', '');
        
        % Copy the visible optimization results to the new axes.
        newLines = xregGui.HGPlotCopy(lines, ExportAx);
        
        % Place the current point at the bottom of the uistack, so other lines are
        % plotted over it.
        if ~isempty(newLines)
            uistack(newLines(1), 'bottom');
        end
        
        end  % pCopyLinesToPrintAxes
        
        %----------------------------------------
        function ExportAx = pCreatePrintableAxes(obj, fig, AxCopyProps)
        %PCREATEPRINTABLEAXES Create a printable copy of the axes
        %   EXPORTAX = PCREATEPRINTABLEAXES(OBJ, FIG, AXCOPYPROPS) creates a
        %   printable copy of the axes. The specified axes properties are copied
        %   from OBJ to EXPORTAX.
        
        % Create new axes and position them in the standard location
        ExportAx = axes('Parent', fig, 'Units', 'Pixels');
        
        % Copy settings from the view axes to the export ones
        AxCopyPropData = get(obj.pGetSelector.Axes, AxCopyProps);
        set(ExportAx, AxCopyProps, AxCopyPropData);
        end  % pCreatePrintableAxes
        
        %----------------------------------------
        function idxNearestPoint = pFindNearestPoint(obj, lineColor, acceptChanged)
        %PFINDNEARESTPOINT Find the nearest point to mouse click
        %   OUT = PFINDNEARESTPOINT(OBJ, LINECOLOR, ACCEPTCHANGED) finds the index
        %   amongst all the displayed points of the point nearest to the mouse
        %   click.
        
        % Get the selected indices
        idxX = obj.hSelector.XFactor;
        idxY = obj.hSelector.YFactor;
        idxZ = obj.hSelector.ZFactor;
        
        % Get the current optimization output data
        [data, bRedIdx, bOrangeIdx, bGreenIdx] = pGetOutputData(obj);
        bAcceptableIdx = pGetUserOutputData(obj);
        data = pTransformDataForPoints(obj, data);
        
        % Retrieve display data for the line that has been clicked
        % Indices into red/orange/green lines
        switch lineColor
            case 'red'
                bIdx = bRedIdx;
                if acceptChanged
                    bIdx(~bAcceptableIdx) = false;
                else
                    bIdx(bAcceptableIdx) = false;
                end
            case 'orange'
                bIdx = bOrangeIdx;
                if acceptChanged
                    bIdx(~bAcceptableIdx) = false;
                else
                    bIdx(bAcceptableIdx) = false;
                end
            case 'green'
                bIdx = bGreenIdx;
                if acceptChanged
                    bIdx(bAcceptableIdx) = false;
                else
                    bIdx(~bAcceptableIdx) = false;
                end
            otherwise
                error(message('mbc:Values2dView:InvalidState'));
        end
        if strcmpi(obj.DisplayData, 'Acceptable')
            bIdx = bIdx & bAcceptableIdx;
        end
        xdata = data(bIdx, idxX);
        ydata = data(bIdx, idxY);
        zdata = data(bIdx, idxZ);
        
        % Current point
        ax = obj.hSelector.Axes;
        cp = get(ax, 'CurrentPoint');
        
        % Scale current point and line data onto [0, 1]
        [xdata, ydata, zdata, cp] = i_scaleData(ax, xdata, ydata, zdata, cp);
        xb = cp(1, :);
        xf = cp(2, :);
        
        % Minimum distance between the points and the current point vector
        nPts = size(xdata, 1);
        vecToBackPoint = bsxfun(@minus, xb, [xdata,ydata,zdata]);
        cursorLine = repmat(xf - xb, nPts, 1);
        crossProd = cross(cursorLine, vecToBackPoint, 2);
        dmin = sqrt(dot(crossProd, crossProd, 2))/norm(xf-xb);
        
        % Minimum of the minimum distances
        [~, ptidx] = min(dmin);
        
        % Index is obtained into the filtered data. Need find the index in the raw
        % data. Note we are assuming that the line is visible here, i.e. at
        % least one element of bIdx is true.
        idxVisible = find(bIdx);
        idxNearestPoint = idxVisible(ptidx);
        end  % pFindNearestPoint
        
        %----------------------------------------
        function [Data, bRedIdx, bOrangeIdx, bGreenIdx] = pGetOutputData(obj)
        %PGETOUTPUTDATA Return the table data.
        %  [DATA, BREDIDX, BORANGEIDX, BGREENIDX, BACCEPTABLEIDX] =
        %  PGETOUTPUTDATA(OBJ) returns data for the 2-d view as a NPTS-by-NFACS
        %  matrix. NPTS will be displayed in the view and the user will be able to
        %  choose from one of NFACS factors. NPTS-by-1 boolean vectors are returned
        %  to indicate whether a point has a negative exit flag (BREDIDX), zero
        %  exit flag (BORANGEIDX) or a positive exit flag (BGREENIDX).
        
        % Initialisation
        Data  = [];
        bRedIdx = [];
        bOrangeIdx = [];
        bGreenIdx = [];
        
        if obj.hasData
            
            % Retrieve optimization results from the cache
            Data = obj.OutputValues.Data;
            bRedIdx = obj.OutputValues.bRedIdx;
            bOrangeIdx = obj.OutputValues.bOrangeIdx;
            bGreenIdx = obj.OutputValues.bGreenIdx;
            
        end
        
        
        end  % pGetOutputData
        
        %----------------------------------------
        function len = pGetQuantityLength(obj)
        %PGETQUANTITYLENGTH Return the quantity length in the output
        %
        %   LEN = PGETQUANTITYLENGTH(OBJ) returns the length of a quantity in the
        %   output. This is defined to be the maximum length of the fixed/free
        %   variables.
        
        
        if obj.hasData
            MS = obj.MessageService;
            out = MS.getOptimOutput;
            len = max([getNumFixedVariables(out), getNumFreeVariables(out)]);
        else
            len = 0;
        end
        
        end  % pGetQuantityLength
        
        %----------------------------------------
        function hSelector = pGetSelector(obj)
        %PGETSELECTOR Return axes selector
        %   OUT = PGETSELECTOR(OBJ) returns OBJ.HSELECTOR. This method is not
        %   intended for public consumption.
        
        hSelector = obj.hSelector;
        
        end  % pGetSelector
        
        %----------------------------------------
        function [bAcceptableIdx, CurrentIdx] = pGetUserOutputData(obj)
        %PGETUSEROUTPUTDATA Return the user changeable data.
        %  [BACCEPTABLEIDX, CURRENTIDX] = PGETUSEROUTPUTDATA(OBJ) returns the
        %  output data for the 2-d view that can be edited by the user. A NPTS-by-1
        %  boolean vector is returned to indicate the acceptibility of each point
        %  and the index of the current point is returned as a double.
        
        % Initialisation
        bAcceptableIdx = [];
        CurrentIdx = [];
        
        if obj.hasData
            
            % Get the accceptable status for each point and determine the current
            % point
            out = obj.MessageService.getOptimOutput;
            RunIdx = obj.MessageService.getFocusRun;
            SolIdx = obj.MessageService.getFocusSolution;
            FocusPtIdx = obj.MessageService.getFocusPoint;
            nRunOptim = getNumRuns(out);
            if strcmp(obj.MessageService.CurrentSliceDirection, 'solution')
                % Display all runs at focussed solution selection
                if SolIdx>0
                    % Get acceptable indices
                    bAcceptableIdx = isAcceptable(out, 1:nRunOptim, SolIdx)';
                    % Set current point index
                    CurrentRunSolIdx = RunIdx;
                end
                
            elseif strcmp(obj.MessageService.CurrentSliceDirection, 'pareto')
                % Display all solutions at focussed run selection
                if RunIdx>0
                    % Get acceptable indices
                    nSolOptim = getNumSolutions(out);
                    bAcceptableIdx = isAcceptable(out, RunIdx, 1:nSolOptim);
                    % Set current point index
                    CurrentRunSolIdx = SolIdx;
                end
                
            elseif strcmp(obj.MessageService.CurrentSliceDirection, 'selectedsolution')
                % Display selected solution for each run
                if hasSelectedSolution(out)
                    % Get acceptable indices
                    bAcceptableIdx = isAcceptable(out, 1:nRunOptim, -1)';
                    % Set current point index
                    CurrentRunSolIdx = RunIdx;
                end
                
            else
                % This object does not plot any data for the weighted solution
                % view. It will display a message instead - this is dealt with in
                % pCheckOutputData
            end
            
            if ~isempty(obj.OutputValues.Data)
                
                % At this point, the exit flag and acceptable indices are row
                % vectors, each element determining the status of each run/solution
                % in the data matrix. We need to repmat these vectors so that each
                % element of a solution has an exit flag and acceptable index
                % associated with it.
                maxLen = pGetQuantityLength(obj);
                bAcceptableIdx = repmat(bAcceptableIdx, maxLen, 1);
                bAcceptableIdx = bAcceptableIdx(:);
                
                % Return the index of the current point (not solution or run)
                % selected in the table
                if FocusPtIdx > maxLen
                    % The current selected point does not have a free/fixed
                    % variable associated with it.
                    CurrentIdx = [];
                else
                    CurrentIdx = maxLen*(CurrentRunSolIdx - 1) + FocusPtIdx;
                end
                
            end
            
        end
        end  % pGetUserOutputData
        
        %----------------------------------------
        function [nms, bIdx, defIdx] = pGetValidItems(obj)
        %PGETVALIDITEMS Returns the optimization items that can be plotted
        %   [NMS, BIDX, DEFIDX] = PGETVALIDITEMS(OBJ) returns a cell array of the
        %   items in the optimization results that can be plotted. A boolean index
        %   is returned such that NMS = ALLNAMES(BIDX), where ALLNAMES is a list of
        %   the column names returned from the output object with the
        %   'OutputContents' option set to 'cell' and 'OutputContents' set to
        %   OutputContents = {'FixedVars', 'FreeVars', 'Objectives'}. DEFIDX
        %   denotes the default X, Y and Z factors. This is a 1-by-3 index vector
        %   into NMS, or empty if the number of factors is less than 3.
        
        
        if ~obj.hasData ...
                || strcmp(obj.MessageService.CurrentSliceDirection, 'weightedsolution') ...
                || ~obj.MessageService.hasFocusIndex
            
            nms = {};
            bIdx = [];
            defIdx = [];
        else
            MS = obj.MessageService;
            out = MS.getOptimOutput;
            
            % Find the maximum length of the fixed and free variables
            maxLen = pGetQuantityLength(obj);
            
            % Eliminate the optimization results that do not have length either 1
            % or maxLen. Base this test on the first solution in the optimization
            % results - it should exist for all optimizations.
            nms = getColumnNames(out, 'OutputFormat', 'cell', ...
                'OutputContents', pOutputContents(obj));
            lenSolData = [getNumFixedVariables(out), getNumFreeVariables(out), ...
                getNumObjectives(out)];
            bIdx = (lenSolData == 1) | (lenSolData == maxLen);
            nms = nms(bIdx);
            
            if nargout > 2
                % Return the default X, Y and Z factors. If possible, try to set the
                % first two fixed variables *that are not obj/con weights* as the X and
                % Y axis factor defaults. The first free variable is selected as
                % the Z-axis factor default.
                nFree = length(getNumFreeVariables(out));
                [~, fixedNames] = getSolution(out, 1, 'OutputFormat', 'cell', ...
                    'OutputContents', 'FixedVars');
                nFixed = length(fixedNames);
                cellIsWeight = regexp(fixedNames, '_weights');
                idxFixedNotWeight = find(cellfun('isempty', cellIsWeight));
                if length(nms) < 3
                    % Do not set a default if there are fewer than three valid items.
                    defIdx = [];
                elseif length(idxFixedNotWeight) > 1
                    % More than one fixed variable that is not a weight. Can set the desired default.
                    defIdx = [idxFixedNotWeight(1) idxFixedNotWeight(2) nFixed+1];
                elseif length(idxFixedNotWeight) == 1 && nFree > 1
                    % Select the one fixed variable that is not a weight and the first
                    % two free variables.
                    defIdx = [idxFixedNotWeight(1) nFixed+1 nFixed+2];
                elseif nFree > 1
                    % Any fixed variables are weights, at least two free variables.
                    % Let's select the first three items after the fixed variables.
                    defIdx = nFixed + (1:3);
                else
                    % Select the first three items
                    defIdx = 1:3;
                end
            else
                defIdx = [];
            end
            
        end
        
        
        end  % pGetValidItems
        
        %----------------------------------------
        function OutputContents = pOutputContents(obj) %#ok<MANU>
        %POUTPUTCONTENTS Valid value types for Values2dView.
        %   OUTPUTCONTENTS = POUTPUTCONTENTS(OBJ) returns the valid value types
        %   that can be displayed in Values2dView. The cell array,
        %   OUTPUTCONTENTS, is used when querying the output object for
        %   optimization results data.
        
        OutputContents = {'FixedVars', 'FreeVars', 'Objectives'};
        end  % pOutputContents
        
        %----------------------------------------
        function pPostSetMessageService(obj)
        %PPOSTSETMESSAGESERVICE Method that is called when the message service is set
        %  PPOSTSETMESSAGESERVICE(OBJ) is called in response to a new message
        %  service being set in the object.
        
        pPostSetMessageService@mbcgui.multiview.View(obj)
        
        obj.addMessageServiceListener( {'ObjectChanged', 'SliceDirectionChanged'}, ...
            {@obj.redraw, @obj.redraw});
        
        % Add custom redraw events for a single index changing
        obj.addMessageServiceListener( ...
            {'RunIndexChanged', 'SolutionIndexChanged'}, ...
            {{@i_runchange, obj}, {@i_solchange, obj}});
        
        % Set the current point when the current focus or the focus point changes
        obj.addMessageServiceListener( {'FocusOrFocusPointChanged'}, ...
            {{@i_refreshcurrentpoint, obj}});
        
        % Add custom redraw events for the selected solution changing
        obj.addMessageServiceListener( {'SelectedSolutionChanged'}, ...
            {{@i_targetedchange, obj, 'selectedsolution'}});
        
        % Add custom redraws for the Acceptable status changing
        obj.addMessageServiceListener( {'AcceptableChanged'}, {{@i_acceptchange, obj}});
        
        % Redraw now
        redraw(obj);
        end  % pPostSetMessageService
        
        %----------------------------------------
        function pRefresh(obj)
        %PREFRESH Refresh view
        %   PREFRESH(OBJ) Refresh view in OBJ.
        
        if obj.hasData && size(obj.OutputValues.Data, 2) > 2
            
            % Set the "no display" text invisible
            set(obj.hText, 'String', '', 'Visible', 'off');
            
            % Get the selected indices
            idxX = obj.hSelector.XFactor;
            idxY = obj.hSelector.YFactor;
            idxZ = obj.hSelector.ZFactor;
            
            % Get the data for the current view
            [data, bRedIdx, bOrangeIdx, bGreenIdx] = pGetOutputData(obj);
            bAcceptableIdx = pGetUserOutputData(obj);
            
            % Update the boolean indices to reflect user selection
            [bRedIdx, bOrangeIdx, bGreenIdx, bRedAcceptIdx, bOrangeAcceptIdx, bGreenNotAcceptIdx] = ...
                pGetDisplayIndices(obj, bRedIdx, bOrangeIdx, bGreenIdx, bAcceptableIdx);
            
            % Make any required data transformations before plotting
            data = pTransformDataForPoints(obj, data);
            
            % Plot the data
            set(obj.hRedLine(1), 'XData', data(bRedIdx, idxX), ...
                'YData', data(bRedIdx, idxY), 'ZData', data(bRedIdx, idxZ));
            set(obj.hRedLine(2), 'XData', data(bRedAcceptIdx, idxX), ...
                'YData', data(bRedAcceptIdx, idxY), 'ZData', data(bRedAcceptIdx, idxZ));
            set(obj.hOrangeLine(1), 'XData', data(bOrangeIdx, idxX), ...
                'YData', data(bOrangeIdx, idxY), 'ZData', data(bOrangeIdx, idxZ));
            set(obj.hOrangeLine(2), 'XData', data(bOrangeAcceptIdx, idxX), ...
                'YData', data(bOrangeAcceptIdx, idxY), 'ZData', data(bOrangeAcceptIdx, idxZ));
            set(obj.hGreenLine(1), 'XData', data(bGreenIdx, idxX), ...
                'YData', data(bGreenIdx, idxY), 'ZData', data(bGreenIdx, idxZ));
            set(obj.hGreenLine(2), 'XData', data(bGreenNotAcceptIdx, idxX), ...
                'YData', data(bGreenNotAcceptIdx, idxY), 'ZData', data(bGreenNotAcceptIdx, idxZ));
            
            % Determine x, y axis limits from entire data - do not automatically
            % rescale on filtered data. Expand the axis range by 200*eps to ensure
            % any data on the axis limits gets plotted.
            xLim = mbcmakelimits(data(:, idxX)) + 100*[-eps eps];
            yLim = mbcmakelimits(data(:, idxY)) + 100*[-eps eps];
            
            % Determine z-axis limits from filtered data. Expand the axis range by
            % 200*eps to ensure any data on the axis limits gets plotted.
            bDisplayIdx = bRedIdx | bOrangeIdx | bGreenIdx | ...
                bRedAcceptIdx | bOrangeAcceptIdx | bGreenNotAcceptIdx;
            zLim = mbcmakelimits(data(bDisplayIdx, idxZ)) + 100*[-eps eps];
            set(obj.hSelector.Axes, 'XLim', xLim, 'YLim', yLim, 'ZLim', zLim);
            
        else
            
            % Deal with unsupported cases.
            
            % Clear the plot
            set(obj.hRedLine, 'XData', [], 'YData', [], 'ZData', []);
            set(obj.hOrangeLine, 'XData', [], 'YData', [], 'ZData', []);
            set(obj.hGreenLine, 'XData', [], 'YData', [], 'ZData', []);
            
            % Set the axes ranges to [0, 1]
            set(obj.hSelector.Axes, 'XLim', [0, 1], 'YLim', [0, 1], 'ZLim', [0, 1]);
            
            % Inform the user of the problem
            if strcmp(obj.MessageService.CurrentSliceDirection, 'weightedsolution')
                msg = sprintf('%s\n%s', 'Cannot plot output values in the', ...
                    'weighted objectives view');
            else
                % Catch all for any other problems - shouldn't get here.
                msg = 'Three or more valid factors are required for';
                msg = sprintf('%s\n%s', msg, gettitle(obj));
            end
            set(obj.hText, 'Visible', 'on', ...
                'Units','normalized',...
                'Position',[.5 .5 .5],...
                'HorizontalAlignment','center',...
                'VerticalAlignment','middle',...
                'String', msg);
            
        end
        
        end  % pRefresh
        
        %----------------------------------------
        function pRefreshAcceptabilityMenu(obj, currIdx, isCurrPointDisplayed)
        %PREFRESHACCEPTABILITYMENU Update the acceptability menus
        %   PREFRESHACCEPTABILITYMENU(OBJ, CURRIDX, ISCURRPOINTDISPLAYED) updates
        %   the enabled state of the acceptability menus.
        %
        %   PREFRESHACCEPTABILITYMENU(OBJ) sets the enabled state of both
        %   acceptability menus to false.
        
        if nargin > 1 && obj.hasData && ...
                size(obj.OutputValues.Data, 2) > 2 && isCurrPointDisplayed
            bAcceptableIdx = pGetUserOutputData(obj);
            obj.ViewAction.Actions(2).Enabled = ~bAcceptableIdx(currIdx);
            obj.ViewAction.Actions(3).Enabled = bAcceptableIdx(currIdx);
        else
            obj.ViewAction.Actions(2).Enabled = false;
            obj.ViewAction.Actions(3).Enabled = false;
        end
        
        end  % pRefreshAcceptabilityMenu
        
        %----------------------------------------
        function pRefreshCurrentPoint(obj)
        %PREFRESHCURRENTPOINT Refresh the current point
        %
        %   PREFRESHCURRENTPOINT(OBJ) refreshes the location of the current point
        %   in OBJ. The marker style of the current point is dependent on the color
        %   and acceptability of the current point.
        
        idxCurrPoint = [];
        colorCurrPoint = '';
        xdata = [];
        ydata = [];
        zdata = [];
        currPointMarker = 'o';
        currPointSize = 16;
        lineWidth = 2.5;
        
        if obj.hasData
            % Get the data for the current view
            [data, bRedIdx, bOrangeIdx, bGreenIdx] = pGetOutputData(obj);
            data = pTransformDataForPoints(obj, data);
            [bAcceptableIdx, idxCurrPoint] = pGetUserOutputData(obj);
            
            % Update the boolean indices to reflect user selection
            [bRedIdx, bOrangeIdx, bGreenIdx, bRedAcceptIdx, bOrangeAcceptIdx, bGreenNotAcceptIdx] = ...
                pGetDisplayIndices(obj, bRedIdx, bOrangeIdx, bGreenIdx, bAcceptableIdx);
            
            % Determine the color of the current point.
            if size(data, 2) < 3 || isempty(idxCurrPoint)
                colorCurrPoint = '';
            elseif bRedIdx(idxCurrPoint) || bRedAcceptIdx(idxCurrPoint)
                colorCurrPoint = 'red';
            elseif bOrangeIdx(idxCurrPoint) || bOrangeAcceptIdx(idxCurrPoint)
                colorCurrPoint = 'orange';
            elseif bGreenIdx(idxCurrPoint) || bGreenNotAcceptIdx(idxCurrPoint)
                colorCurrPoint = 'green';
            else
                colorCurrPoint = '';
            end
            
            % If no color has been specified, then the current point is not visible
            if size(data, 2) > 2 && ~isempty(colorCurrPoint)
                % Get the coordinates of the current point
                idxX = obj.hSelector.XFactor;
                idxY = obj.hSelector.YFactor;
                idxZ = obj.hSelector.ZFactor;
                xdata = data(idxCurrPoint, idxX);
                ydata = data(idxCurrPoint, idxY);
                zdata = data(idxCurrPoint, idxZ);
                
                % Determine whether the current point's acceptability has been changed
                % by the user
                out = obj.MessageService.getOptimOutput;
                RunIdx = obj.MessageService.getFocusRun;
                SolIdx = obj.MessageService.getFocusSolution;
                isAcceptChanged = isAcceptableUserSet(out, RunIdx, SolIdx);
                
                % Refresh the current point marker
                if isAcceptChanged
                    currPointMarker = 'p';
                    currPointSize = 24;
                    lineWidth = 1.5;
                elseif strcmpi(colorCurrPoint, 'red')
                    currPointMarker = 'o';
                    currPointSize = 16;
                    lineWidth = 2.5;
                elseif strcmpi(colorCurrPoint, 'orange')
                    currPointMarker = '^';
                    currPointSize = 18;
                    lineWidth = 2.5;
                else
                    currPointMarker = 's';
                    currPointSize = 18;
                    lineWidth = 2.5;
                end
            end
        end
        
        set(obj.hCurrentPoint, 'XData', xdata, 'YData', ydata, 'ZData', zdata, ...
            'Marker', currPointMarker, 'MarkerSize', currPointSize, ...
            'LineWidth', lineWidth);
        
        % Update the acceptability menus
        obj.pRefreshAcceptabilityMenu(idxCurrPoint, ~isempty(colorCurrPoint));
        
        end  % pRefreshCurrentPoint
        
        %----------------------------------------
        function pResetInputList(obj)
        %PRESETINPUTLIST Re-fill the list of optimization quantities
        %
        %   PRESETINPUTLIST(OBJ) resets the list of available optimization
        %   quantities.
        
        
        
        % Get the items that can be plotted
        [nms, ~, defIdx] = pGetValidItems(obj);
        
        % Handle to the factor selector
        hSel = obj.hSelector;
        
        % Only need to reset the input list if the factors have changed
        if ~isequal(nms(:), hSel.Factors(:))
            % Set them as the factors in the selector. If there are less than three
            % valid items then set an empty cell array of factors in the selector.
            if length(nms) > 2
                hSel.Factors = nms;
            else
                hSel.Factors = {};
                defIdx = [];
            end
            
            % Set the default factors in the selector
            if ~isempty(defIdx)
                hSel.DefaultFactorIndices = defIdx;
                hSel.setFactorsToDefault;
            end
        end
        
        
        
        end  % pResetInputList
        
        %----------------------------------------
        function pSetFocusRunSolPt(obj, idxView)
        %PGETFOCUSRUNSOLPT Set the current run, solution and point
        %
        %   PGETFOCUSRUNSOLPT(OBJ, IDXVIEW) sets the current optimization output
        %   run, solution and point from the IDXVIEW-th point clicked in OBJ.
        %
        %   See also PGETOUTPUTDATA
        
        
        if obj.hasData
            
            % Length of the quantities
            qLen = obj.pGetQuantityLength;
            
            % Current point
            idxCurrPt = rem(idxView-1, qLen) + 1;
            
            % Run or solution depends on slice direction
            ms = obj.MessageService;
            switch ms.CurrentSliceDirection
                case {'solution', 'selectedsolution'}
                    curSol = ms.CurrentSolution;
                    idxCurrRun = ceil(idxView/qLen);
                    ms.setCurrentIndex(idxCurrRun, curSol, idxCurrPt);
                case 'pareto'
                    curRun = ms.CurrentRun;
                    idxCurrSol = ceil(idxView/qLen);
                    ms.setCurrentIndex(curRun, idxCurrSol, idxCurrPt);
            end
            
        end
        
        
        end  % pSetFocusRunSolPt
        
        %----------------------------------------
        function data = pTransformDataForPoints(obj, data) %#ok<INUSL>
        %PTRANSFORMDATAFORPOINTS Apply any data transormations before plotting
        %   DATA = PTRANSFORMDATAFORPOINTS(OBJ, DATA) is called on the
        %   NPTS-by-NFACS data array before it is used to plot the solution points.
        %
        %   See also pGetOutputData.
        
        % The default action is no transform.
        
        end  % pTransformDataForPoints
        
        %----------------------------------------
        function pUpdateOutputValuesCache(obj)
        %PUPDATEOUTPUTVALUESCACHE Update optimization results cache.
        %   PUPDATEOUTPUTVALUESCACHE(OBJ) updates the optimization results in the
        %   output values cache.
        
        
        if obj.hasData
            DataMats = {};
            out = obj.MessageService.getOptimOutput;
            SolIdx = obj.MessageService.getFocusSolution;
            nRunOptim = getNumRuns(out);
            OutputDataArgs = {'OutputFormat', 'cell', ...
                'OutputContents', pOutputContents(obj)};
            if strcmp(obj.MessageService.CurrentSliceDirection, 'solution')
                % Display all runs at focussed solution selection
                if SolIdx>0
                    DataMats = getSolution(out, SolIdx, OutputDataArgs{:});
                    % Get the exit flag for each run
                    exitFlag = getExitFlag(out, 1:nRunOptim, SolIdx)';
                end
                
            elseif strcmp(obj.MessageService.CurrentSliceDirection, 'pareto')
                % Display all solutions at focussed run selection
                RunIdx = obj.MessageService.getFocusRun;
                if RunIdx>0
                    DataMats = getParetoSolution(out, RunIdx, OutputDataArgs{:});
                    % Get the exit flag for each solution
                    nSolOptim = getNumSolutions(out);
                    exitFlag = getExitFlag(out, RunIdx, 1:nSolOptim);
                end
                
            elseif strcmp(obj.MessageService.CurrentSliceDirection, 'selectedsolution')
                % Display selected solution for each run
                if hasSelectedSolution(out)
                    DataMats = getSelectedSolution(out, OutputDataArgs{:});
                    % Get the exit flag for the selected solution
                    exitFlag = getExitFlag(out,  1:nRunOptim, -1)';
                end
                
            else
                % This object does not plot any data for the weighted solution
                % view. It will display a message instead - this is dealt with in
                % pCheckOutputData
            end
            
            if isempty(DataMats)
                
                Data = [];
                bRedIdx = [];
                bOrangeIdx = [];
                bGreenIdx = [];
                
            else
                
                % Only keep valid data
                [~, bIdx] = pGetValidItems(obj);
                DataMats = DataMats(bIdx);
                
                % Convert data cell array into matrix. We assume at this point that the
                % length of each cell array is 1 or max(lenData)
                lenData = cellfun('size', DataMats, 2);
                maxLen = max(lenData);
                scalarIdx = lenData == 1;
                DataMats(scalarIdx) = cellfun(@(x)x(:, ones(maxLen, 1)), ...
                    DataMats(scalarIdx), 'UniformOutput', false);
                DataMats = cellfun(@i_makeCol, DataMats, 'UniformOutput', false);
                Data = [DataMats{:}];
                
                % At this point, the exit flag and acceptable indices are row
                % vectors, each element determining the status of each run/solution
                % in the data matrix. We need to repmat these vectors so that each
                % element of a solution has an exit flag and acceptable index
                % associated with it.
                exitFlag = repmat(exitFlag, maxLen, 1);
                exitFlag = exitFlag(:);
                
                % Generate indices for red(bad), orange(warning) and green(good)
                % points
                bRedIdx = (exitFlag < 0);
                bOrangeIdx = (exitFlag == 0);
                bGreenIdx = (exitFlag > 0);
                
            end
            
            obj.OutputValues.Data = Data;
            obj.OutputValues.bRedIdx = bRedIdx;
            obj.OutputValues.bOrangeIdx = bOrangeIdx;
            obj.OutputValues.bGreenIdx = bGreenIdx;
        end
        end  % pUpdateOutputValuesCache
        
        
        function setUIContextMenu(obj)
        obj.hSelector.Axes.UIContextMenu = obj.UIContextMenu;
        end
        
        function createActions(obj)
        % Create option actions
        AG = mbcgui.actions.ActionGroup('', '&View Options');
        AG.MenuType = 'separate';
        AGDisplay = mbcgui.actions.ActionGroup('', '&Results to Display');
        AGDisplay.MenuType = 'submenu';
        Adisp1 = mbcgui.actions.ToggleAction({@i_setdisplaydata, 'All', obj}, '&All');
        Adisp2 = mbcgui.actions.ToggleAction({@i_setdisplaydata, 'Acceptable', obj}, 'A&cceptable');
        Adisp3 = mbcgui.actions.ToggleAction({@i_setdisplaydata, 'Green', obj}, '&Green');
        Adisp4 = mbcgui.actions.ToggleAction({@i_setdisplaydata, 'Orange', obj}, '&Orange');
        Adisp5 = mbcgui.actions.ToggleAction({@i_setdisplaydata, 'Red', obj}, '&Red');
        AGDisplay.Actions = [Adisp1 Adisp2 Adisp3 Adisp4 Adisp5];
        AAcceptable = mbcgui.actions.StatefulAction({@i_setacceptable, obj}, '&Set Acceptable');
        AUnacceptable = mbcgui.actions.StatefulAction({@i_setunacceptable, obj}, '&Set Unacceptable');
        switch obj.DisplayData
            case 'Acceptable'
                Adisp2.Selected = true;
            case 'Green'
                Adisp3.Selected = true;
            case 'Orange'
                Adisp4.Selected = true;
            case 'Red'
                Adisp5.Selected = true;
            otherwise
                Adisp1.Selected = true;
        end
        AG.Actions = [AGDisplay, AAcceptable, AUnacceptable];
        obj.ViewAction = AG;
        
        % Set the options in the multiview class
        obj.Options = AG;
        end
        function redraw(obj,~, ~)
        % redraw plot
        obj.pUpdateOutputValuesCache;
        obj.pResetInputList;
        obj.pRefresh;
        obj.pRefreshCurrentPoint;
        end  % redraw

        function axesClick(obj,~, ~)
        
        % Determine whether this is a left or right click
        f = ancestor(obj.hSelector.Axes, 'figure');
        selType = get(f, 'SelectionType');
        
        % Only want to perform rotation on left clicks of the axes - this reserves
        % right-click for the context menu.
        if strcmpi(selType, 'normal') && obj.CanRotate
            % Start rotation
            mv_rotate3d(obj.hSelector.Axes, 'ON');
            
            % Launch the box for rotation
            wbdf = get(f, 'WindowButtonDownFcn');
            feval(wbdf{1}, f, [], wbdf{2});
            
            % Use the button up manager to listen for the next button up and shut off
            % rotation
            but_manager = xregGui.ButtonUpManager(f);
            but_manager.getNextEvent({@i_switchRotateOff, obj});
        end
        
        % Send the view's button down event to make it the current view
        obj.notify('ButtonDown');
        end  % i_axesClick

    end
    
    methods (Hidden) % possibly private or hidden
        %----------------------------------------
        function [bRedIdx, bOrangeIdx, bGreenIdx, bRedAcceptIdx, bOrangeAcceptIdx, bGreenNotAcceptIdx] = ...
            pGetDisplayIndices(obj, bRedIdx, bOrangeIdx, bGreenIdx, bAcceptableIdx)
        %PGETDISPLAYINDICES Return the display indices
        %
        %   [BREDIDX, BORANGEIDX, BGREENIDX, BREDACCEPTIDX, BORANGEACCEPTIDX,
        %   BGREENNOTACCEPTIDX] = PGETDISPLAYINDICES(OBJ, BREDIDX, BORANGEIDX,
        %   BGREENIDX, BACCEPTABLEIDX) returns a boolean index for each data point
        %   determining whether it is displayed in that colour/shape or not.
        
        
        switch obj.DisplayData
            case 'Acceptable'
                bRedIdx = bRedIdx & bAcceptableIdx;
                bOrangeIdx = bOrangeIdx & bAcceptableIdx;
                bGreenIdx = bGreenIdx & bAcceptableIdx;
            case 'Red'
                bOrangeIdx = false(size(bOrangeIdx));
                bGreenIdx = false(size(bGreenIdx));
            case 'Orange'
                bRedIdx = false(size(bRedIdx));
                bGreenIdx = false(size(bGreenIdx));
            case 'Green'
                bRedIdx = false(size(bRedIdx));
                bOrangeIdx = false(size(bOrangeIdx));
        end
        
        % Find any points whose acceptability has been changed. These will be
        % marked by stars on the plot.
        bRedAcceptIdx = bRedIdx & bAcceptableIdx;
        bRedIdx(bRedAcceptIdx) = false;
        bOrangeAcceptIdx = bOrangeIdx & bAcceptableIdx;
        bOrangeIdx(bOrangeAcceptIdx) = false;
        bGreenNotAcceptIdx = bGreenIdx & ~bAcceptableIdx;
        bGreenIdx(bGreenNotAcceptIdx) = false;
        
        end  % pGetDisplayIndices
        
    end  % possibly private or hidden
    
end  % classdef

function val = i_setDisplayData(~, val)

validDisplayData = {'All', 'Acceptable', 'Green', 'Orange', 'Red'};
errmsg = 'Display data must be one of All, Acceptable, Green, Orange, Red';
if ~ismember(val, validDisplayData)
    error('mbc:Values2dView:InvalidArgument', errmsg);
end
end  % i_setDisplayData

function i_inputschanged(~, ~, obj)

obj.pRefresh;
obj.pRefreshCurrentPoint;
end  % i_inputschanged

function i_setdisplaydata(~, ~, DisplayData, obj)

obj.setDisplayFilter(DisplayData);
end  % i_setdisplaydata


function i_switchRotateOff(~, ~, obj)

% Stop rotation
hSelector = obj.pGetSelector;
mv_rotate3d(hSelector.Axes, 'off');
end  % i_switchRotateOff

function i_redLineButtonDown(~, ~, obj, isAcceptChangedLine)

% Switch
% Find the nearest point on the line to the mouse click.
idxLine = obj.pFindNearestPoint('red', isAcceptChangedLine);

% Perform update actions
i_updateAfterLeftClick(obj, idxLine);
end  % i_redLineButtonDown

function i_orangeLineButtonDown(~, ~, obj, isAcceptChangedLine)

% Find the nearest point on the line to the mouse click.
idxLine = obj.pFindNearestPoint('orange', isAcceptChangedLine);

% Perform update actions
i_updateAfterLeftClick(obj, idxLine);
end  % i_orangeLineButtonDown

function i_greenLineButtonDown(~, ~, obj, isAcceptChangedLine)

% Find the nearest point on the line to the mouse click.
idxLine = obj.pFindNearestPoint('green', isAcceptChangedLine);

% Perform update actions
i_updateAfterLeftClick(obj, idxLine);
end  % i_greenLineButtonDown

function i_updateAfterLeftClick(obj, idxLine)

% Send the view's button down event to make it the current view
obj.notify('ButtonDown');

% Temporarily disable the listeners - we want to update before everyone else.
obj.disableMessageServiceListeners;

% Set the focus run, solution and point corresponding to the idx-th point
% clicked in the view.
pSetFocusRunSolPt(obj, idxLine);

% Move the current point
obj.pRefreshCurrentPoint;

% Enable the listeners
obj.enableMessageServiceListeners;
end  % i_updateAfterLeftClick

function i_setacceptable(~, ~, obj)

i_setacceptability(obj, true);
end  % i_setacceptable

function i_setunacceptable(~, ~, obj)

i_setacceptability(obj, false);
end  % i_setunacceptable

function i_setacceptability(obj, acceptState)

% Get current run and solution
ms = obj.MessageService;
runIdx = ms.getFocusRun;
solIdx = ms.getFocusSolution;

% Set run/solution to be acceptable
out = obj.MessageService.getOptimOutput;
out = setAcceptableToUser(out, runIdx, solIdx, acceptState);
obj.MessageService.setOptimOutput(out, 'AcceptableChanged');
end  % i_setacceptability

function [xdata, ydata, zdata, cp] = i_scaleData(ax, xdata, ydata, zdata, cp)

% Get axes limits
xlim = get(ax, 'XLim');
ylim = get(ax, 'YLim');
zlim = get(ax, 'ZLim');

% Rescale xdata
xdata = (xdata - xlim(1))./(xlim(2) - xlim(1));
cp(:, 1) = (cp(:, 1) - xlim(1))./(xlim(2) - xlim(1));

% Rescale ydata
ydata = (ydata - ylim(1))./(ylim(2) - ylim(1));
cp(:, 2) = (cp(:, 2) - ylim(1))./(ylim(2) - ylim(1));

% Rescale zdata
zdata = (zdata - zlim(1))./(zlim(2) - zlim(1));
cp(:, 3) = (cp(:, 3) - zlim(1))./(zlim(2) - zlim(1));
end  % i_scaleData


function i_targetedchange(~, ~, obj, target)
if strcmpi(obj.MessageService.CurrentSliceDirection,target)
    % Only redraw if the specified target view is currently being used
    % Do a full redraw because we may be on a selected view that is going
    % from empty to an initialised state.
    redraw(obj);
end
end  % i_targetedchange

function i_runchange(~, ~, obj)
if strcmpi(obj.MessageService.CurrentSliceDirection, 'pareto')
    % Redraw the view
    obj.pUpdateOutputValuesCache;
    obj.pRefresh;
end
end  % i_runchange

function i_solchange(~, ~, obj)
redrawDirs = {'solution', 'selectedsolution'};
if any(strcmpi(obj.MessageService.CurrentSliceDirection, redrawDirs))
    % Redraw the view
    obj.pUpdateOutputValuesCache;
    obj.pRefresh;
end
end  % i_solchange

function i_acceptchange(~, ~, obj)
obj.pRefresh;
obj.pRefreshCurrentPoint;
end  % i_acceptchange

function i_refreshcurrentpoint(~, ~, obj)
obj.pRefreshCurrentPoint;
end  % i_refreshcurrentpoint

function DataElt = i_makeCol(DataElt)

DataElt = DataElt';
DataElt = DataElt(:);
end  % i_makeCol