www.gusucode.com > mbctools 工具箱 matlab 源码程序 > mbctools/+xregbdrygui/AbstractBdrySlice.m
classdef AbstractBdrySlice < xregbdrygui.AbstractBdryView %xregbdrygui.AbstractBdrySlice class % xregbdrygui.AbstractBdrySlice extends xregbdrygui.AbstractBdryView. % % xregbdrygui.AbstractBdrySlice 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' % InputFactors - Property is of type 'MATLAB array' (read only) % NAxes - Property is of type 'int' (read only) % Axes - Property is of type 'MATLAB array' (read only) % hAxes - Property is of type 'MATLAB array' (read only) % hPatch - Property is of type 'MATLAB array' (read only) % hBlackDots - Property is of type 'MATLAB array' (read only) % hRedRings - Property is of type 'MATLAB array' (read only) % hValidationInside - Property is of type 'MATLAB array' (read only) % hValidationOutside - Property is of type 'MATLAB array' (read only) % hNoDisplayText - Property is of type 'MATLAB array' (read only) % hSelectButton - Property is of type 'MATLAB array' (read only) % hSliceTable - Property is of type 'handle' (read only) % % xregbdrygui.AbstractBdrySlice methods: % createDefaultWindowContainer - Create an appropriate container for the view % getEvaluationPoints - Get evaluation points from slice control table % getSliceRange - Get slice range from slice control table % gettitle - Return a suitable title for a window % setAxes - Set the indices of the axes in a boundary slice view % setInputFactors - Sets the input factors in the view % setSlicePoint - Set the point where the current slice is at. % Copyright 2005-2015 The MathWorks, Inc. and Ford Global Technologies, Inc. properties (Access=protected, AbortSet) %HAXISLABELS Property is of type 'MATLAB array' hAxisLabels = []; %HAXISPOPUPS Property is of type 'MATLAB array' hAxisPopups = []; end properties (SetAccess=protected, AbortSet) %HAXES Property is of type 'MATLAB array' (read only) hAxes = []; %HPATCH Property is of type 'MATLAB array' (read only) hPatch = []; %HBLACKDOTS Property is of type 'MATLAB array' (read only) hBlackDots = []; %HREDRINGS Property is of type 'MATLAB array' (read only) hRedRings = []; %HVALIDATIONINSIDE Property is of type 'MATLAB array' (read only) hValidationInside = []; %HVALIDATIONOUTSIDE Property is of type 'MATLAB array' (read only) hValidationOutside = []; %HNODISPLAYTEXT Property is of type 'MATLAB array' (read only) hNoDisplayText = []; %HSELECTBUTTON Property is of type 'MATLAB array' (read only) hSelectButton = []; %HSLICETABLE Property is of type 'handle' (read only) hSliceTable = []; end properties (SetAccess=protected, AbortSet, SetObservable) %INPUTFACTORS Property is of type 'MATLAB array' (read only) InputFactors = []; %NAXES Property is of type 'int' (read only) NAxes %AXES Property is of type 'MATLAB array' (read only) Axes = []; end methods % constructor block function obj = AbstractBdrySlice( varargin ) % XREGBDRYGUI.ABSTRACTBDRYSLICE class constructor % % Also does the GUI layout % Call the inherited constructor obj@xregbdrygui.AbstractBdryView(varargin{ : }); % converted super class constructor call SC = xregGui.SystemColorsDbl; % Set the number of axes (usually via the sub-class) % -- this will fixed from now for this object obj.NAxes = pNAxes(obj); % Generate GUI primitives and link together into a layout % Create the layout for the axes lytAxesPanel = mbcgui.widget.AxesContainer(... 'Parent',obj.Parent, ... 'Border', [60 45 10 10]); obj.hAxes = lytAxesPanel.AxesHandle; set(obj.hAxes,... 'Box','on',... 'XGrid','on',... 'YGrid','on',... 'ZGrid','on',... 'Units','pixels',... 'Layer','top', ... 'HitTest', 'off'); xlabel( obj.hAxes, '', 'Interpreter', 'none' ); ylabel( obj.hAxes, '', 'Interpreter', 'none' ); zlabel( obj.hAxes, '', 'Interpreter', 'none' ); obj.pSetupAxes( obj.hAxes ); obj.hPatch = patch( ... 'Parent',obj.hAxes, ... 'Visible',obj.Visible,... 'XData', NaN, ... 'YData', NaN, ... 'ZData', NaN, ... 'EdgeColor','none',... 'FaceColor','b',... 'HitTest','off' ); obj.hBlackDots = line( ... 'Parent',obj.hAxes, ... 'Visible',obj.Visible,... 'LineStyle', 'none', ... 'Marker', '.', ... 'MarkerEdgeColor', 'k', ... 'MarkerFaceColor', 'k', ... 'MarkerSize', 15, ... 'XData', [], ... 'YData', [], ... 'ZData', [], ... 'ButtonDownFcn', @(s, e) obj.pViewDot ); obj.hRedRings = line(... 'Parent',obj.hAxes, ... 'Visible',obj.Visible,... 'LineStyle', 'none', ... 'LineWidth', 2, ... 'Marker', 'o', ... 'MarkerEdgeColor', 'r', ... 'MarkerFaceColor', 'none', ... 'MarkerSize', 10, ... 'XData', [], ... 'YData', [], ... 'ZData', [], ... 'HitTest', 'off' ); obj.hValidationInside = line( ... 'Parent',obj.hAxes, ... 'LineStyle', 'none', ... 'Marker', '^', ... 'MarkerFaceColor', [50, 200, 50]/255, ... 'MarkerSize', 6, ... 'XData', [], ... 'YData', [], ... 'ZData', [], ... 'HitTest', 'off', ... 'Visible', 'off', ... 'Tag', 'BdrySliceValidationInside'); obj.hValidationOutside = line( ... 'Parent',obj.hAxes, ... 'LineStyle', 'none', ... 'Marker', '^', ... 'MarkerFaceColor', [200, 50, 50]/255, ... 'MarkerSize', 6, ... 'XData', [], ... 'YData', [], ... 'ZData', [], ... 'HitTest', 'off', ... 'Visible', 'off', ... 'Tag', 'BdrySliceValidationOutside'); obj.hNoDisplayText = text( ... 'Parent', obj.hAxes,... 'String', sprintf( 'Too few factors for %s.', obj.gettitle ), ... 'Visible', obj.Visible,... 'Position', [0.5, 0.5, 0.5],... 'HorizontalAlignment', 'center',... 'VerticalAlignment', 'middle',... 'Clipping', 'on',... 'HitTest', 'off' ); SlicePanel = mbcgui.container.layoutpanel(... 'Parent', obj.Parent, ... 'Visible', obj.Visible); % Slice control table % -- This table is what the user used to control the evlaution points for % the slice and the associated tolerance on the data points to be shown % in the slice. obj.hSliceTable = mbcwidgets.VariableEditorTable( ... 'Parent', SlicePanel, ... 'ValueColumnCount', 2, ... 'ValueColumnHeader', {'Value', 'Tolerance'}, ... 'ValueChangedCallback', {@i_SliceTableEdited, obj}, ... 'ValueConstraint', 'plot'); obj.hSelectButton = uicontrol(... 'Parent', SlicePanel, ... 'Style','pushbutton',... 'String','Select Data Point...',... 'Interruptible','off',... 'Callback', {@i_SelectTest, obj} ); % Create the pop-up menus and label controls for the axes chooser axisNames = {'X', 'Y', 'Z'}; hLabels = cell(1, obj.NAxes ); hPopups = cell(1, obj.NAxes ); SelectorPanel = mbcgui.container.layoutpanel(... 'Parent', SlicePanel, ... 'BorderType', 'etchedin'); for i = 1:obj.NAxes, hPopups{i} = uicontrol(... 'Parent', SelectorPanel, ... 'Style', 'popupmenu', ... 'String', ' ', ... 'Value', 1, ... 'BackgroundColor', SC.WINDOW_BG, ... 'Callback', {@i_EditAxis, i, obj} ); hLabels{i} = xregGui.labelcontrol( ... 'parent', SelectorPanel, ... 'String', sprintf('%s-axis:', axisNames{i}), ... 'LabelSize', 40, ... 'LabelSizeMode', 'absolute', ... 'ControlSize', 1, ... 'ControlSizeMode', 'relative', ... 'Control', hPopups{i} ); end hPopups = [hPopups{:}]; hLabels = [hLabels{:}]; obj.hAxisLabels = hLabels; obj.hAxisPopups = hPopups; % Layouts % Create the layout for the axes selectors lytSelectors = xreggridbaglayout( SelectorPanel, ... 'packgroup', 'XREG_PERM_ON', ... 'packstatus', 'off', ... 'dimension', [obj.NAxes, 1], ... 'rowsizes', repmat( 21, 1, obj.NAxes ), ... 'gapy', 5, ... 'border', [2, 2, 2, 2], ... 'elements', num2cell( obj.hAxisLabels )); set(SelectorPanel, 'LayoutComponent', {lytSelectors}); % Create the layout that combines the slice table and axes selectors. This % is the slice control lytSliceControl = xreggridbaglayout( SlicePanel, ... 'packgroup', 'XREG_PERM_ON', ... 'Dimension', [3, 2], ... 'ColSizes', [-1, 105], ... 'RowSizes', [25, -1, obj.NAxes*26+3], ... 'gapy', 2, ... 'border', [0 0 0 2], ... 'MergeBlock', {[2 2], [1 2]}, ... 'MergeBlock', {[3 3], [1 2]}, ... 'elements', {[], obj.hSliceTable, SelectorPanel, obj.hSelectButton}); set(SlicePanel, 'LayoutComponent', {lytSliceControl}); % Create the main layout for the view obj.ContentHandle = xregsplitlayout( obj.Parent,... 'packgroup', 'XREG_PERM_ON',... 'orientation', 'lr', ... 'dividerstyle', 'flat', ... 'dividerwidth', 4, ... 'split', [0.2, 0.8], ... 'left', SlicePanel,... 'right', lytAxesPanel,... 'position', obj.Position, ... 'packstatus','on' ); % We should be already to rock & roll now. So we the BMS to get everything % fully connected. obj.pPostSetMessageService; % Attach listeners to properties and slice control events obj.addPropertyListeners( 'Axes', @(s, e) obj.pPostSetAxes ); % Setting the input factors sets variables & values in the slice table % -- This will cause a full draw obj.setInputFactors; end % AbstractBdrySlice end % constructor block methods % public methods %---------------------------------------- function [vectors, grid] = getEvaluationPoints(obj) %GETEVALUATIONPOINTS Get evaluation points from slice control table % [VECTORS, GRID] = GETEVALUATIONPOINTS(OBJ) % % VECTORS is a cell array with the x-, y- and (possibly) z-axis evaluation % points are single vectors. These should be suitable for being passed to % CONTOUR or similar as the X, Y arguments. % % GRID is an nPoints by nFactors matrix that is the MESHGRID form of VECTORS % but combined with the input factors that determine the slice space. This % matrix should be suitable for passing directly to constraint evaluation % functions. % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. % CONTOURS and ISOSURFACE don't play with NDGRID for some unknown reason. Hence % we have to use MESHGRID! % Get raw values from tables values = obj.hSliceTable.getNumericValues; values = values(:,1); % Get the values from inside the slice space vectors = values(obj.Axes,1); vectors = cellfun( @sort, vectors, 'UniformOutput', false ); % Form the grid if nargout > 1, nf = length( values ); % Perform the NDGRID ndVectors = cell( size( vectors ) ); [ndVectors{:}] = meshgrid( vectors{:} ); % Fill out the GRID with all the required values % -- For columns that correspond to factors slected as axes, we use the % NDGRIDed values. % -- For the other columns, put in the appropriate constants grid = zeros( numel( ndVectors{1} ), nf ); for i = 1:nf, loc = (i == obj.Axes); if any( loc ), grid(:,i) = ndVectors{loc}(:); else grid(:,i) = values{i}; end end end end % getEvaluationPoints %---------------------------------------- function range = getSliceRange(obj) %GETSLICERANGE Get slice range from slice control table % [RANGE, INDICES] = GETSLICERANGE(OBJ) % % RANGE is a two by nFactors matrix. The first row is the minimum value % of the slice range and the second row is the maximum. For factors where % there is no constraint on the slice, i.e., those factors that are the % axes of the slice, the min and max value will be -Inf and +Inf. % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. % Get raw values from tables values = obj.hSliceTable.getNumericValues; types = obj.hSliceTable.VariableTypes; nf = size( values, 1 ); boundedFactors = (types(:,1) == 1); range = zeros( 2, nf ); for i = 1:nf, if boundedFactors( i ), % Min range(1,i) = values{i,1} - values{i,2}; % Max range(2,i) = values{i,1} + values{i,2}; else % This factor is a axis of the slice range(:,i) = [-Inf; Inf]; end end end % getSliceRange %---------------------------------------- function setAxes(obj, newAxes, index) %SETAXES Set the indices of the axes in a boundary slice view % SETAXES(OBJ, AXES) % SETAXES(OBJ, AXES, INDEX) allows one axis to be changed by specifying % the index of the axis to change in this case the newly created AXES % vector is checked to ensure that there are no two entries the same and % it there are, other entries are modified to fix this. No such checking % happens in the first form. % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. narginchk(2,3); if nargin == 3, % Need to check the new axes value to ensure that the same axis % isn't slected twice. In the case that one axis is selected twice, % we modify a different value in the "axes" vector to the one being % set by the pop-up. axes = obj.Axes; axes(index) = newAxes; tf = axes == axes(index); tf(index) = false; if any( tf ), % Two pop-ups have the same value otherValues = setdiff( 1:obj.NAxes, axes ); axes(tf) = otherValues(1:nnz( tf )); % -- If the previous line errors it could be because there are % insufficient factors for the view. We shouldn't get into % this code in that situation. end elseif length( newAxes ) ~= obj.NAxes || ~isnumeric( newAxes ), error(message('mbc:xregbdrygui:AbstractBdrySlice:InvalidArgument', obj.NAxes)); else axes = newAxes; end % Set the axes in the object % -- also updates the pop-ups obj.Axes = axes; % Set the slice table to have the correct columns linked and the other % columns obj.pUpdateTableValues; % Draw the new constraint obj.pDrawConstraint; end % setAxes %---------------------------------------- function setInputFactors(obj, cif) %SETINPUTFACTORS Sets the input factors in the view % SETINPUTFACTORS(OBJ, CIF) sets the input factors that are stored in the % view based on the input factors from the current node. % % SETINPUTFACTORS(OBJ) gets the input factors from the boundary message % service. % % If the old input factors in the view differ from those from the node % then slice control table is reset with new variables and values. % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. % Check input arguments if nargin < 2, if obj.hasData, % Get the input factors from the BMS cif = obj.MessageService.getInputFactors; else % No input factors are available so create an empty array of them % to process in this function. We want to go ahead with a refresh % because having no data means we ought to be displaying nothing, % not what was previously being drawn. cif = coninputfactor({}); end end % Check the new input factors and set properties as appropriate nCif = length( cif ); if nCif < obj.NAxes, % Insufficient factors % -- Need to disable the controls and hide the plot set( obj.hAxisLabels, 'Enable', 'off' ); set( obj.hSliceTable, 'Enable', 'off' ); obj.pTurnOffPlot; else % Ensure that the controls are enabled and that the plot is visible set( obj.hAxisLabels, 'Enable', 'on' ); set( obj.hSliceTable, 'Enable', 'on' ); obj.pTurnOnPlot; end if isequal( cif, obj.InputFactors ), % Nothing to do else % Need to update the stored input factors and the slice table obj.InputFactors = cif; % Update slice table % % Names names = cif.Name; % % Types % -- Default is a single value (code = 1) in both the first and second % column types = ones( nCif, 2 ); % % Values % -- By default these will be the mid points of the ranges of the input % factors % -- For the axes, the default will be 30 evenly spaced points values = cell( nCif, 2 ); [lb, ub] = getRange( cif ); for i = 1:nCif, values(i,:) = { 0.5*(lb(i)+ub(i)), 0.1*(ub(i)-lb(i)) }; end obj.hSliceTable.setVariables( names, types, values ); % Set the axes if nCif < obj.NAxes set( obj.hAxisPopups, 'String', ' ' ); obj.setAxes( ones(1, obj.NAxes) ); else set( obj.hAxisPopups, 'String', names ); if length(unique(obj.Axes))<obj.NAxes || any(obj.Axes>nCif) ax = 1:obj.NAxes; else ax = obj.Axes(1:obj.NAxes); end obj.setAxes( ax ); end end end % setInputFactors %---------------------------------------- function setSlicePoint(obj, point) %SETSLICEPOINT Set the point where the current slice is at. % SETSLICEPOINT(OBJ, POINT) % #define TYPE_SCALAR = 1; hTable = obj.hSliceTable; for i = 1:length( point ), % Only set those points that are of scalar type, i.e., those that % define where the slice is. if hTable.VariableTypes(i,1) == TYPE_SCALAR, hTable.VariableValues{i,1} = point(i); end end % Redraw obj.pDrawConstraint; end % setSlicePoint end % public methods methods (Access=protected) %---------------------------------------- function pDrawConstraint(obj) %#ok<MANU> %pDrawConstraint abstract end %---------------------------------------- function pDrawPoints(obj) %PDRAWPOINTS Draw the data/validation points that are in the current slice % PDRAWPOINTS(OBJ) % Assign the X-, Y- and Z-data for points and rings in the current slice. % % See also XREGBDRYGUI.BDRYSLICE2. if ~obj.hasData, return end XYZDATA = {'XData', 'YData', 'ZData'}; BMS = obj.MessageService; % Put up a message in the status bar to explain the delay and change the % pointer to an hour-glass to make it clear to the user that stuff is % happening %>> msgID = i_addmessage( psbar, 'Drawing 2D graph...' ); %>> ptrID = i_setpointer( psbar, 'watch'); % FIX ME: Does this need to be done via the editor? % Get the current node and the data associated with that node. data = BMS.getDataPoints; [valData, valIsInside] = BMS.getValidationPoints; if size( data, 2 ) < obj.NAxes return end nData = size( data, 1 ); nValData = size(valData, 1); % Get the slice information % -- Slice subspace factors % -- Slice point and thickness (tolerance) sliceRange = obj.getSliceRange; lb = repmat( sliceRange(1,:), nData, 1 ); ub = repmat( sliceRange(2,:), nData, 1 ); lbVal = repmat( sliceRange(1,:), nValData, 1 ); ubVal = repmat( sliceRange(2,:), nValData, 1 ); % Work out which points are in the slice ind = find( all( lb <= data & data <= ub, 2 ) ); valInd = find( all( lbVal <= valData & valData <= ubVal, 2 ) ); bind = intersect( ind, BMS.getBoundaryPoints ); if isempty(valData) indInside = zeros(0, 1); indOutside = zeros(0, 1); else indInside = valInd(valIsInside(valInd)); indOutside = valInd(~valIsInside(valInd)); end % Assign the data to the line objects for i = 1:obj.NAxes, set( obj.hBlackDots, XYZDATA{i}, data(ind,obj.Axes(i)) ); set( obj.hRedRings, XYZDATA{i}, data(bind,obj.Axes(i)) ); set( obj.hValidationInside, XYZDATA{i}, valData(indInside,obj.Axes(i)) ); set( obj.hValidationOutside, XYZDATA{i}, valData(indOutside,obj.Axes(i)) ); end for i =(obj.NAxes+1):3, set( obj.hBlackDots, XYZDATA{i}, zeros( size( ind ) ) ); set( obj.hRedRings, XYZDATA{i}, zeros( size( bind ) ) ); set( obj.hValidationInside, XYZDATA{i}, zeros( size( indInside ) )); set( obj.hValidationOutside, XYZDATA{i}, zeros( size( indOutside ) )); end % The callback on the dots needs to be able to map between the points that % are displayed and the actual data points. Hence for displayed point we % store the indices of the that point in the original data set set( obj.hBlackDots, 'UserData', ind ); set( obj.hRedRings, 'UserData', bind ); % set correct visibility of validation data set(obj.hValidationInside, 'Visible', BMS.ValidationPointHighlight); set(obj.hValidationOutside, 'Visible', BMS.ValidationPointHighlight); % Return the mouse pointer back to what it was before and remove the % message from the status bar. %>> i_deletemessage( psbar, msgID); %>> i_deletepointer( psbar, ptrID ); % FIX ME: Does this need to be done via the editor? end % pDrawPoints %---------------------------------------- function v = pGetAxisPopupValue(obj, index) %PGETAXISPOPUPVALUE Get the value of one of the axis popups % PGETAXISPOPUPVALUE(OBJ, INDEX) v = get(obj.hAxisPopups(index), 'Value'); end % pGetAxisPopupValue %---------------------------------------- function n = pNAxes(obj) %PNAXES Number of axes for a slice view % N = PNAXES(OBJ) % This should be overloaded by sub-classes to return the correct number of % axes. % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. n = 2; end % pNAxes %---------------------------------------- function pPostSetAxes(obj) %PPOSTSETAXES If the axes get set then the pop-ups need updating % PPOSTSETAXES(OBJ) % This method does not foce a redraw of the constraint. % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. % Set the correct values in the pop-up menus for i = 1:obj.NAxes, set(obj.hAxisPopups(i), 'Value', obj.Axes(i)); end end % pPostSetAxes %---------------------------------------- function pPostSetMessageService(obj) %PPOSTSETMESSAGESERVICE Respond to change of MessageService % PPOSTSETMESSAGESERVICE(OBJ) is called when the MessageService property % is changed. % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. % Clear the listeners obj.clearMessageServiceListeners; % Add new listeners obj.addMessageServiceListener( 'NodeChange', @(src, evt) obj.setInputFactors ); obj.addMessageServiceListener( 'ViewModeChange', @(src, evt) obj.setInputFactors ); obj.addMessageServiceListener( 'ViewTestChange', @(src, evt) obj.setInputFactors ); obj.addMessageServiceListener( 'ConstraintChange', @(src, evt) obj.pDrawConstraint ); obj.addMessageServiceListener( 'BoundaryPointsHighlight', @(src, evt) obj.pShowBoundaryPoints ); obj.addMessageServiceListener( 'ValidationPointsToggled', @(src, evt) obj.pShowValidationPoints ); end % pPostSetMessageService %---------------------------------------- function pSetupAxes(obj, hAxes) %PSETUPAXES Setup the axes for the boundary slice view % PSETUPAXES(OBJ, HAXES) % % This should be overloaded by sub-classes to set up the axes as required. % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. % Nothing to do for abstract class end % pSetupAxes %---------------------------------------- function pShowBoundaryPoints(obj) %PSHOWBOUNDARYPOINTS Turn boundary point highlighting on or off % PSHOWBOUNDARYPOINTS(OBJ) % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. BMS = obj.MessageService; set( obj.hRedRings, 'Visible', BMS.BoundaryPointHighlight ); end % pShowBoundaryPoints %---------------------------------------- function pShowValidationPoints(obj) %PSHOWVALIDATIONPOINTS Turn validation point highlighting on or off % PSHOWVALIDATIONPOINTS(OBJ) % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. BMS = obj.MessageService; set(obj.hValidationInside, 'Visible', BMS.ValidationPointHighlight); set(obj.hValidationOutside, 'Visible', BMS.ValidationPointHighlight); end % pShowValidationPoints %---------------------------------------- function pTurnOffPlot(obj) %PTURNOFFPLOT Display "No Display" text % PTURNOFFPLOT(OBJ) % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. set([obj.hPatch, obj.hBlackDots, obj.hRedRings], 'Visible', 'off'); set(obj.hAxes, 'XGrid', 'off', 'YGrid', 'off', 'ZGrid', 'off'); set(obj.hNoDisplayText, 'Visible', 'on'); % Set the limits of the axes so that the text appears in the center set( obj.hAxes, 'XLim', [0, 1], 'YLim', [0, 1], 'ZLim', [0, 1] ); end % pTurnOffPlot %---------------------------------------- function pTurnOnPlot(obj) %PTURNONPLOT Un-display "No Display" text % PTURNONPLOT(OBJ) % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. BMS = obj.MessageService; set([obj.hPatch, obj.hBlackDots], 'Visible', 'on'); set(obj.hRedRings, 'Visible', BMS.BoundaryPointHighlight); set(obj.hAxes, 'XGrid', 'on', 'YGrid', 'on', 'ZGrid', 'on'); set(obj.hNoDisplayText, 'Visible', 'off'); end % pTurnOnPlot %---------------------------------------- function pUpdateTableValues(obj) %PUPDATETABLEVALUES Update the values in the slice table % PUPDATETABLEVALUES(OBJ) % This is usually called in response to change in the pop-ups that control % which subspace the slice is in. % % Will cause a redraw of the constraint % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. % #define TYPE_SCALAR = 1; TYPE_LINEAR = 2; TYPE_FREEFORM = 3; TYPE_LINKED = 4; AXES_NAMES = {'X-Axis', 'Y-Axis', 'Z-Axis'}; % Get some input factor data from the object cif = obj.InputFactors; nCif = length( cif ); % Get the current variable type and values types = obj.hSliceTable.VariableTypes; values = obj.hSliceTable.VariableValues; % Check each one in turn % -- If the type matches what it should be for the obj.Axes then we leave % it as is. If it is the wrong type then we change the type and set the % value to some default. switch length(unique(obj.Axes)); case {1,2} Resolution = 101; otherwise Resolution = 21; end [lb, ub] = getRange( cif ); for i = 1:nCif, [tf, loc] = ismember( i, obj.Axes ); if tf, % This factor is an axis of the plot and we want it to be a vector % type if types(i,1) == TYPE_LINEAR || types(i,1) == TYPE_FREEFORM, % This factor has the right type, so we can leave it alone else % This factor needs it type set [TYPE_LINEAR, TYPE_LINKED] and % appropriate values set types(i,:) = [TYPE_LINEAR, TYPE_LINKED]; values{i,1} = [lb(i), ub(i), Resolution]; values{i,2} = AXES_NAMES{loc}; end else % This factor is not an axis of the plot and so it must be a scalar % type and it must have a tolerance if types(i,1) == TYPE_SCALAR, % This factor has the right type, else % We need to set the type for this factor to scalar and the % values to the midpoint and to 10% of the range types(i,:) = [TYPE_SCALAR, TYPE_SCALAR]; values(i,:) = { 0.5*(lb(i)+ub(i)), 0.1*(ub(i)-lb(i)) }; end end end obj.hSliceTable.setVariables( cif.Name, types, values ); % Redraw? obj.pDrawConstraint; end % pUpdateTableValues %---------------------------------------- function pViewDot(obj) %PVIEWDOT Callback for when user clicks on the data points in the view % PVIEWDOT(OBJ) % Two possibilities for this callback:- % -- Single Click:- display information about the points, e.g., the % values of the input factors for this point, how far it is from the % constraint boundary. % -- Double Click:- change the values of the slice control so that the % slice is at the selected point % % See also XREGBDRYGUI.ABSTRACTBDRYSLICE. if ~obj.hasData, return end hFigure = obj.Parent; BMS = obj.MessageService; data = BMS.getDataPoints; cif = BMS.getInputFactors; con = BMS.getConstraint; currentPoint = get(obj.hAxes, 'CurrentPoint'); % Find the points that were clicked on by the user % =========== % Get the current data points blackDots = get( obj.hBlackDots, {'XData', 'YData', 'ZData'} ); % Get the limits of the axes limits = get( obj.hAxes, {'XLim', 'YLim', 'ZLim'} ); % Calculate a distance metric that takes account of the current axes limits distance = i_PointToLineDistance( currentPoint(1,:).', currentPoint(2,:).', ... vertcat( blackDots{:} ), [diff( limits{1} ); diff( limits{2} ); diff( limits{3} )] ); % The points with the smallest distance are the ones clicked on % % In the user data we store the indices of the points displayed, i.e., the % map between the indices of the line data and the actual data indexMap = get( obj.hBlackDots, 'UserData' ); % Switch the action based on what type click the user made switch lower( get(ancestor(hFigure,'figure'),'SelectionType') ) case 'normal', % left click i_DataTooltip; case 'open', % double click i_MoveToPoint; case {'alt', 'extend'}, % right, middle click %ignore otherwise warning(message('mbc:xregbdrygui:AbstractBdrySlice:InvalidState')); return end function i_MoveToPoint [~, i] = min( distance ); i = indexMap(i); obj.setSlicePoint( data(i,:) ); end % of nested function i_MoveToPoint function i_DataTooltip % Find those that are within 3% of the axes limits and visible i = find( distance < 1.5e-2 ); if isempty( i ), [~, i] = min( distance ); end i = indexMap(i); % Generate the text for the patch if isempty( con ), space = repmat( blanks(1), length( cif ), 1 ); txt = [space, char( cif.Symbol ), space, space, num2str( data(i,:).', 4 ), space ]; else space = repmat( blanks(1), length( cif )+1, 1 ); symbols = cif.Symbol; d = constraintDistance( con, data(i,:) ); txt = [space, char( symbols{:}, 'Dist:' ), space, space, ... num2str( [data(i,:), d].', 4 ), space ]; end xregDisplayDataPatch( obj.hAxes, txt, [], false ); end % of nested function i_DataTooltip end % of main function pViewDot end end % classdef function i_SliceTableEdited(hTable, evt, obj) % If the user edits the first column (value) then a complete % re-draw is required. However, if they only edit a tolerance then % we only need to redisplay the points. if evt.data.Columns == 2, % The user is editing column one, the evaluation points obj.pDrawConstraint; else % The user is editing column two, the tolerances obj.pDrawPoints; end end % i_SliceTableEdited function i_EditAxis(src, evt, index, obj ) obj.setAxes( obj.pGetAxisPopupValue( index ), index ); end % i_EditAxis function i_SelectTest( src, evt, obj ) point = obj.MessageService.guiSelectPoint; if ~isempty( point ), obj.setSlicePoint( point ); end end % i_SelectTest function d = i_PointToLineDistance( x1, x2, x0, w ) % Distance from the points in x0 to the line though <x1, x2> % The last argument, w, is a scaling vector. nPoints = size( x0, 2 ); delta = (x2 - x1); T = (delta/sum( delta.^2 ))'; d = zeros( 1, nPoints ); for i = 1:nPoints, t = T * (x0(:,i) - x1); % A "nice" formula for the point-line distance is: % d(i) = norm( cross( x2 - x1, x1 - x0(:,i) ) )/norm( x2 - x1 ); % (note that this doesn't take into account the scaling vector) % However, it is numerically more efficient to use d(i) = norm( (x0(:,i) - (x1 + t*delta))./w ); end end