-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbreakyaxis.m
322 lines (270 loc) · 11.4 KB
/
breakyaxis.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
% 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