Revision f0c5c7cb930dc7952c24021113edac8be7e4bf32 authored by Guy Avraham on 26 October 2022, 13:32:18 UTC, committed by GitHub on 26 October 2022, 13:32:18 UTC
1 parent ac59451
breakyaxis.m
% breakyaxes splits data in an axes so that data is in a low and high pane.
%
% breakYAxes(splitYLim) splitYLim is a 2 element vector containing a range
% of y values from splitYLim(1) to splitYLim(2) to remove from the axes.
% They must be within the current yLimis of the axes.
%
% breakYAxes(splitYLim,splitHeight) splitHeight is the distance to
% seperate the low and high side. Units are the same as
% get(AX,'uints') default is 0.015
%
% breakYAxes(splitYLim,splitHeight,xOverhang) xOverhang stretches the
% axis split graphic to extend past the top and bottom of the plot by
% the distance set by XOverhang. Units are the same as get(AX,'units')
% default value is 0.015
%
% breakYAxes(AX, ...) performs the operation on the axis specified by AX
%
function breakInfo = breakyaxis(varargin)
%Validate Arguements
if nargin < 1 || nargin > 4
error('Wrong number of arguements');
end
if isscalar(varargin{1}) && ishandle(varargin{1})
mainAxes = varargin{1};
argOffset = 1;
argCnt = nargin - 1;
if ~strcmp(get(mainAxes,'Type'),'axes')
error('Handle object must be Type Axes');
end
else
mainAxes = gca;
argOffset = 0;
argCnt = nargin;
end
if (strcmp(get(mainAxes,'XScale'),'log'))
error('Log X Axes are not supported');
end
if (argCnt < 3)
xOverhang = 0.015;
else
xOverhang = varargin{3 + argOffset};
if numel(xOverhang) ~= 1 || ~isreal(xOverhang) || ~isnumeric(xOverhang)
error('XOverhang must be a scalar number');
elseif (xOverhang < 0)
error('XOverhang must not be negative');
end
xOverhang = double(xOverhang);
end
if (argCnt < 2)
splitHeight = 0.015;
else
splitHeight = varargin{2 + argOffset};
if numel(xOverhang) ~= 1 || ~isreal(xOverhang) || ~isnumeric(xOverhang)
error('splitHeight must be a scalar number');
elseif (xOverhang < 0)
error('splitHeight must not be negative');
end
splitHeight = double(splitHeight);
end
splitYLim = varargin{1 + argOffset};
if numel(splitYLim) ~= 2 || ~isnumeric(splitYLim) || ~isreal(xOverhang)
error(splitYLim,'Must be a vector length 2');
end
splitYLim = double(splitYLim);
mainYLim = get(mainAxes,'YLim');
if (any(splitYLim >= mainYLim(2)) || any(splitYLim <= mainYLim(1)))
error('splitYLim must be in the range given by get(AX,''YLim'')');
end
mainPosition = get(mainAxes,'Position');
if (splitHeight > mainPosition(3) )
error('Split width is too large')
end
%We need to create 4 axes
% lowAxes - is used for the low y axis and low pane data
% highAxes - is used to the high y axis and high pane data
% annotationAxes - is used to display the x axis and title
% breakAxes - this is an axes with the same size and position as main
% is it used to draw a seperator between the low and high side
%Grab Some Parameters from the main axis (e.g the one we are spliting)
mainYLim = get(mainAxes,'YLim');
mainXLim = get(mainAxes,'XLim');
mainPosition = get(mainAxes,'Position');
mainParent = get(mainAxes,'Parent');
mainHeight = mainPosition(4); %Positions have the format [low bottom width height]
%mainYRange = mainYLim(2) - mainYLim(1);
mainFigure = get(mainAxes,'Parent');
mainXColor = get(mainAxes,'XColor');
mainLineWidth = get(mainAxes,'LineWidth');
figureColor = get(mainFigure,'Color');
mainXTickLabelMode = get(mainAxes,'XTickLabelMode');
mainYLabel = get(mainAxes,'YLabel');
mainYDir = get(mainAxes,'YDir');
mainLayer = get(mainAxes,'Layer');
%Save Main Axis Z Order
figureChildren = get(mainFigure,'Children');
zOrder = find(figureChildren == mainAxes);
%Calculate where axesLow and axesHigh will be layed on screen
%And their respctive YLimits
lowYLimTemp = [mainYLim(1) splitYLim(1)];
highYLimTemp = [splitYLim(2) mainYLim(2)];
lowYRangeTemp = lowYLimTemp(2) - lowYLimTemp(1);
highYRangeTemp = highYLimTemp(2) - highYLimTemp(1);
lowHeightTemp = lowYRangeTemp / (lowYRangeTemp + highYRangeTemp) * (mainHeight - splitHeight);
highHeightTemp = highYRangeTemp / (lowYRangeTemp + highYRangeTemp) * (mainHeight - splitHeight);
lowStretch = (lowHeightTemp + splitHeight/2) / lowHeightTemp;
lowYRange = lowYRangeTemp * lowStretch;
lowHeight = lowHeightTemp * lowStretch;
highStretch = (highHeightTemp + splitHeight/2) / highHeightTemp;
highYRange = highYRangeTemp * highStretch;
highHeight = highHeightTemp * highStretch;
lowYLim = [mainYLim(1) mainYLim(1)+lowYRange];
highYLim = [mainYLim(2)-highYRange mainYLim(2)];
if (strcmp(mainYDir, 'normal'))
lowPosition = mainPosition;
lowPosition(4) = lowHeight;
highPosition = mainPosition; %(!!!) look here for position indices!
highPosition(2) = mainPosition(2) + lowHeight;
highPosition(4) = highHeight;
else
%Low Axis will actually go on the high side a vise versa
highPosition = mainPosition;
highPosition(4) = highHeight;
lowPosition = mainPosition;
lowPosition(2) = mainPosition(2) + highHeight;
lowPosition(4) = lowHeight;
end
%Create the Annotations layer, if the Layer is top, draw the axes on
%top (e.g. after) drawing the low and high pane
if strcmp(mainLayer,'bottom')
annotationAxes = CreateAnnotaionAxes(mainAxes,mainParent)
end
%Create and position the lowAxes. Remove all X Axis Annotations, the
%title, and a potentially offensive tick mark
lowAxes = copyobj(mainAxes,mainParent);
set(lowAxes,'Position', lowPosition, ...
'YLim', lowYLim, ...
'XLim', mainXLim, ...
'XGrid' ,'off', ...
'XMinorGrid', 'off', ...
'XMinorTick','off', ...
'XTick', [], ...
'XTickLabel', [], ...
'box','off');
if strcmp(mainLayer,'bottom')
set(lowAxes,'Color','none');
end
delete(get(lowAxes,'XLabel'));
delete(get(lowAxes,'YLabel'));
delete(get(lowAxes,'Title'));
if strcmp(mainXTickLabelMode,'auto')
yTick = get(lowAxes,'YTick');
set(lowAxes,'YTick',yTick(1:(end-1)));
end
%Create and position the highAxes. Remove all X Axis annotations, the
%title, and a potentially offensive tick mark
highAxes = copyobj(mainAxes,mainParent);
set(highAxes,'Position', highPosition, ...
'YLim', highYLim, ...
'XLim', mainXLim, ...
'XGrid' ,'off', ...
'XMinorGrid', 'off', ...
'XMinorTick','off', ...
'XTick', [], ...
'XTickLabel', [], ...
'box','off');
if strcmp(mainLayer,'bottom') %(!!!) is it only about layers?
set(highAxes,'Color','none');
end
delete(get(highAxes,'XLabel'));
delete(get(highAxes,'YLabel'));
delete(get(highAxes,'Title'));
if strcmp(mainXTickLabelMode,'auto')
yTick = get(highAxes,'YTick');
set(highAxes,'YTick',yTick(2:end));
end
%Create the Annotations layer, if the Layer is top, draw the axes on
%top (e.g. after) drawing the low and high pane
if strcmp(mainLayer,'top')
annotationAxes = CreateAnnotaionAxes(mainAxes,mainParent);
set(annotationAxes, 'Color','none');
end
%Create breakAxes, remove all graphics objects and hide all annotations
breakAxes = copyobj(mainAxes,mainParent);
children = get(breakAxes,'Children');
for i = 1:numel(children)
delete(children(i));
end
set(breakAxes,'Color','none');
%Stretch the breakAxes horizontally to cover the vertical axes lines
orignalUnits = get(breakAxes,'Units');
set(breakAxes,'Units','Pixel');
breakPosition = get(breakAxes,'Position');
nudgeFactor = get(breakAxes,'LineWidth');
breakPosition(3) = breakPosition(3) + nudgeFactor;
set(breakAxes,'Position',breakPosition);
set(breakAxes,'Units',orignalUnits);
%Stretch the breakAxes horizontally to create an overhang for sylistic
%effect
breakPosition = get(breakAxes,'Position');
breakPosition(1) = breakPosition(1) - xOverhang;
breakPosition(3) = breakPosition(3) + 2*xOverhang;
set(breakAxes,'Position',breakPosition);
%Create a sine shaped patch to seperate the 2 sides
breakYLim = [mainPosition(2) mainPosition(2)+mainPosition(4)];
set(breakAxes,'ylim',breakYLim);
theta = linspace(0,2*pi,100);
xPoints = linspace(mainXLim(1),mainXLim(2),100);
amp = splitHeight/2 * 0.9;
yPoints1 = amp * sin(theta) + mainPosition(2) + lowHeightTemp;
yPoints2 = amp * sin(theta) + mainPosition(2) + mainPosition(4) - highHeightTemp;
patchPointsY = [yPoints1 yPoints2(end:-1:1) yPoints1(1)];
patchPointsX = [xPoints xPoints(end:-1:1) xPoints(1)];
patch(patchPointsX,patchPointsY ,figureColor,'EdgeColor',figureColor,'Parent',breakAxes); %use of pathc(!!!)?
%Create A Line To Delineate the low and high edge of the patch
line('yData',yPoints1,'xdata',xPoints,'Parent',breakAxes,'Color',mainXColor,'LineWidth',mainLineWidth);
line('yData',yPoints2,'xdata',xPoints,'Parent',breakAxes,'Color',mainXColor,'LineWidth',mainLineWidth);
set(breakAxes,'Visible','off');
%Make the old main axes invisiable
invisibleObjects = RecursiveSetVisibleOff(mainAxes);
%Preserve the z-order of the figure
uistack([lowAxes highAxes breakAxes annotationAxes],'down',zOrder-1)
%Set the rezise mode to position so that we can dynamically change the
%size of the figure without screwing things up
set([lowAxes highAxes breakAxes annotationAxes],'ActivePositionProperty','Position');
%Playing with the titles labels etc can cause matlab to reposition
%the axes in some cases. Mannually force the position to be correct.
set([breakAxes annotationAxes],'Position',mainPosition);
%Save the axes so we can unbreak the axis easily
breakInfo = struct();
breakInfo.lowAxes = lowAxes;
breakInfo.highAxes = highAxes;
breakInfo.breakAxes = breakAxes;
breakInfo.annotationAxes = annotationAxes;
breakInfo.invisibleObjects = invisibleObjects;
end
function list = RecursiveSetVisibleOff(handle)
list = [];
list = SetVisibleOff(handle,list);
end
function list = SetVisibleOff(handle, list)
if (strcmp(get(handle,'Visible'),'on'))
set(handle,'Visible','off');
list = [list handle];
end
children = get(handle,'Children');
for i = 1:numel(children)
list = SetVisibleOff(children(i),list);
end
end
function annotationAxes = CreateAnnotaionAxes(mainAxes,mainParent)
%Create Annotation Axis, Remove graphics objects, YAxis annotations
%(except YLabel) and make background transparent
annotationAxes = copyobj(mainAxes,mainParent);
set(annotationAxes,'XLimMode','Manual');
children = get(annotationAxes,'Children');
for i = 1:numel(children)
delete(children(i));
end
%Save the yLabelpostion because it will move when we delete yAxis
%ticks
yLabel = get(annotationAxes,'YLabel');
yLabelPosition = get(yLabel,'Position');
set(annotationAxes,'YGrid' ,'off', ...
'YMinorGrid', 'off', ...
'YMinorTick','off', ...
'YTick', [], ...
'YTickLabel', []);
%Restore the pevious label postition
set(yLabel,'Position',yLabelPosition);
end

Computing file changes ...