Robochameleon  v1.0
magnifyOnFigure.m
1 % NAME: magnifyOnFigure
2 %
3 % AUTHOR: David Fernandez Prim (david.fernandez.prim@gmail.com)
4 %
5 % PURPOSE: Shows a functional zoom tool, suitable for publishing of zoomed
6 % images and 2D plots
7 %
8 % INPUT ARGUMENTS:
9 % figureHandle [double 1x1]: graphic handle of the target figure
10 % axesHandle [double 1x1]: graphic handle of the target axes.
11 %
12 % OUTPUT ARGUMENTS:
13 % none
14 %
15 % SINTAX:
16 % 1) magnifyOnFigure;
17 % $ Adds magnifier on the first axes of the current figure, with
18 % default behavior.
19 %
20 % 2) magnifyOnFigure( figureHandle );
21 % $ Adds magnifier on the first axes of the figure with handle
22 % 'figureHandle', with default behavior.
23 %
24 % 3) magnifyOnFigure( figureHandle, 'property1', value1,... );
25 % $ Adds magnifier on the first axes of the figure with handle
26 % 'figureHandle', with modified behavior.
27 %
28 % 4) magnifyOnFigure( axesHandle );
29 % $ Adds magnifier on the axes with handle 'axesHandle', with
30 % default behavior.
31 %
32 % 5) magnifyOnFigure( axesHandle, 'property1', value1,... );
33 % $ Adds magnifier on the axes with handle 'axesHandle', with
34 % modified behavior.
35 %
36 % 6) Consecutive calls to this function (in any of the syntaxes
37 % exposed above) produce multiple selectable magnifiers on the target axes.
38 %
39 % USAGE EXAMPLES: see script 'magnifyOnFigure_examples.m'
40 %
41 % PROPERTIES:
42 % 'magnifierShape': 'Shape of the magnifier ('rectangle' or 'ellipse' allowed, 'rectangle' as default)
43 % 'secondaryAxesFaceColor': ColorSpec
44 % 'edgeWidth' Color of the box surrounding the secondary
45 % axes, magnifier and link. Default 1
46 % 'edgeColor': Color of the box surrounding the secondary
47 % axes, magnifier and link. Default 'black'
48 % 'displayLinkStyle': Style of the link. 'none', 'straight' or
49 % 'edges', with 'straight' as default.
50 % 'mode': 'manual' or 'interactive' (allowing
51 % adjustments through mouse/keyboard). Default
52 % 'interactive'.
53 % 'units' Units in which the position vectors are
54 % given. Only 'pixels' currently supported
55 % 'initialPositionSecondaryAxes': Initial position vector ([left bottom width height])
56 % of secondary axes, in pixels
57 % 'initialPositionMagnifier': Initial position vector ([left bottom width height])
58 % of magnifier, in pixels
59 % 'secondaryAxesXLim': Initial XLim value of the secondary axes
60 % 'secondaryAxesYLim': Initial YLim value of the secondary axes
61 % 'frozenZoomAspectRatio': Specially useful for images, forces the use of the same zoom
62 % factor on both X and Y axes, in order to keep the aspect ratio
63 % ('on' or 'off' allowed, 'off' by default
64 %
65 % HOT KEYS (active if 'mode' set to 'interactive')
66 %
67 % -In a figure with multiple tool instances
68 % 'Tab': Switch the focus from one magnifier instance
69 % to the next one on the current figure.
70 % 'Mouse pointer on secondary axes or magnifier of a tool+double left click'
71 % Regain focus
72 %
73 % -On the focused magnifier instance
74 % 'up arrow': Moves magnifier 1 pixel upwards
75 % 'down arrow': Moves magnifier 1 pixel downwards
76 % 'left arrow': Moves magnifier 1 pixel to the left
77 % 'right arrow': Moves magnifier 1 pixel to the right
78 % 'Shift+up arrow': Expands magnifier 10% on the Y-axis
79 % 'Shift+down arrow': Compress magnifier 10% on the Y-axis
80 % 'Shift+left arrow': Compress magnifier 10% on the X-axis
81 % 'Shift+right arrow': Expands magnifier 10% on the X-axis
82 % 'Control+up arrow': Moves secondary axes 1 pixel upwards
83 % 'Control+down arrow': Moves secondary axes 1 pixel downwards
84 % 'Control+left arrow': Moves secondary axes 1 pixel to the left
85 % 'Control+right arrow': Moves secondary axes 1 pixel to the right
86 % 'Alt+up arrow': Expands secondary axes 10% on the Y-axis
87 % 'Alt+down arrow': Compress secondary axes 10% on the Y-axis
88 % 'Alt+left arrow': Compress secondary axes 10% on the X-axis
89 % 'Alt+right arrow': Expands secondary axes 10% on the X-axis
90 % 'PageUp': Increase additional zooming factor on X-axis
91 % 'PageDown': Decrease additional zooming factor on X-axis
92 % 'Shift+PageUp': Increase additional zooming factor on Y-axis
93 % 'Shift+PageDown': Decrease additional zooming factor on Y-axis
94 % 'Control+Q': Resets the additional zooming factors to 0
95 % 'Control+A': Displays position of secondary axes and
96 % magnifier in the command window
97 % 'Control+D': Deletes the focused tool
98 % 'Control+I': Shows/hides the tool identifier (red
99 % background color when the tool has the focus,
100 % black otherwise)
101 % 'Mouse pointer on magnifier+left click' Drag magnifier to any
102 % direction
103 % 'Mouse pointer on secondary axes+left click' Drag secondary axes in any
104 % direction
105 %
106 % TODO:
107 % - Use another axes copy as magnifier instead of rectangle (no ticks).
108 % - Adapt to work on 3D plots.
109 % - Add tip tool with interface description?.
110 %
111 % KNOWN ISSUES:
112 % - Secondary axes are not updated when the zoomming or panning tools of the figure are used.
113 % - Degraded performance for big data sets or big window sizes.
114 % - The size and position of the magnifier are modified for
115 % 'PaperPositionMode' equal to 'auto', when the figure is printed to file
116 % through 'print'
117 %
118 % CHANGE HISTORY:
119 %
120 % Version | Date | Author | Description
121 %---------------|---------------|-------------------|---------------------------------------
122 % 1.0 | 28/11/2009 | D. Fernandez | First version
123 % 1.1 | 29/11/2009 | D. Fernandez | Added link from magnifier to secondary axes
124 % 1.2 | 30/11/2009 | D. Fernandez | Keyboard support added
125 % 1.3 | 01/12/2009 | D. Fernandez | Properties added
126 % 1.4 | 02/12/2009 | D. Fernandez | Manual mode supported
127 % 1.5 | 03/12/2009 | D. Fernandez | New link style added ('edges')
128 % 1.6 | 03/12/2009 | D. Fernandez | Bug solved in display of link style 'edges'
129 % 1.7 | 04/12/2009 | D. Fernandez | Target axes selection added
130 % 1.8 | 07/12/2009 | D. Fernandez | Solved bug when any of the axes are reversed.
131 % 1.9 | 08/12/2009 | D. Fernandez | Adapted to work under all axes modes (tight, square, image, ...)
132 % 1.10 | 08/12/2009 | D. Fernandez | Added frozenZoomAspectRatio zoom mode, useful for images
133 % 1.11 | 08/12/2009 | D. Fernandez | Solved bug when axes contain other than 'line' or 'image' objects
134 % 1.12 | 05/01/2010 | D. Fernandez | Added support to multiple instances
135 % 1.13 | 05/01/2010 | D. Fernandez | Added 'delete' functionality
136 % 1.14 | 05/01/2010 | D. Fernandez | Solved bug when initial positions for secondary axes and/or magnifier are specified
137 % 1.15 | 07/01/2010 | D. Fernandez | Solved bug when resizing window
138 % 1.16 | 07/01/2010 | D. Fernandez | Improved documentation
139 % 1.17 | 28/03/2010 | D. Fernandez | Added tool identifications feature
140 %
141 
142 function magnifyOnFigure( varargin )
143 
144 clear global appDataStruct
145 global appDataStruct
146 
147 
148 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
149 %CHECK OUTPUT ARGUMENTS
150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151 switch nargout
152 
153  case 0
154  %Correct
155  outputObjectExpected = false;
156 
157  case 1
158  %tool object expected at the output
159  outputObjectExpected = false;
160 
161  otherwise
162  error('Number of output arguments not supported.');
163 end
164 
165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166 %CHECK INPUT ARGUMENTS
167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
168 if nargin == 0
169  %Initialize 'appDataStructuct' with default values
170  appDataStruct = initializeToolStruct();
171  %Set figure handle
172  appDataStruct.figureHandle = gcf;
173  % Get number of axes in the same figure
174  childHandle = get(appDataStruct.figureHandle, 'Children');
175  iAxes = find(strcmpi(get(childHandle, 'Type'), 'axes'));
176  % If no target axes specified, select the first found as mainAxes
177  appDataStruct.mainAxesHandle = childHandle( iAxes(end) );
178 elseif nargin > 0
179 
180  if isstruct(varargin{1})
181  %Initialize 'appDataStructuct' with existent structure
182  appDataStruct = initializeToolStruct( varargin{1} );
183 
184  elseif ishandle(varargin{1}) && strcmpi(get(varargin{1}, 'Type'), 'figure')
185  %Initialize 'appDataStructuct' with default values
186  appDataStruct = initializeToolStruct();
187  %Set figure handle
188  appDataStruct.figureHandle = varargin{1};
189  % Get number of axes in the same figure
190  childHandle = get(appDataStruct.figureHandle, 'Children');
191  iAxes = find(strcmpi(get(childHandle, 'Type'), 'axes'));
192  % If no target axes specified, select the first found as mainAxes
193  appDataStruct.mainAxesHandle = childHandle( iAxes(end) );
194 
195  elseif ishandle(varargin{1}) && strcmpi(get(varargin{1}, 'Type'), 'axes')
196  %Initialize 'appDataStructuct' with default values
197  appDataStruct = initializeToolStruct();
198  appDataStruct.mainAxesHandle = varargin{1};
199  % Get figure handle
200  parentHandle = get(varargin{1}, 'Parent');
201  iHandle = find(strcmpi(get(parentHandle, 'Type'), 'figure'));
202  % Figure is the parent of the axes
203  appDataStruct.figureHandle = parentHandle( iHandle(1) );
204 
205  else
206  if ishandle(varargin{1})
207  warning('Wrong figure/axes handle specified. The magnifier will be applied on the current figure.');
208  elseif isobject(varargin{1})
209  error('Wrong object specified.');
210  else
211  error('Wrong input class specified.');
212  end
213 
214  end
215 
216  if mod(nargin-1, 2) == 0
217 
218  %Check input properties
219  for i = 2:2:nargin
220  if ~strcmpi( varargin{i}, 'frozenZoomAspectRatio' ) &&...
221  ~strcmpi( varargin{i}, 'magnifierShape' ) &&...
222  ~strcmpi( varargin{i}, 'secondaryaxesxlim' ) &&...
223  ~strcmpi( varargin{i}, 'secondaryaxesylim' ) &&...
224  ~strcmpi( varargin{i}, 'secondaryaxesfacecolor' ) &&...
225  ~strcmpi( varargin{i}, 'edgewidth' ) &&...
226  ~strcmpi( varargin{i}, 'edgecolor' ) &&...
227  ~strcmpi( varargin{i}, 'displayLinkStyle' ) &&...
228  ~strcmpi( varargin{i}, 'mode' ) &&...
229  ~strcmpi( varargin{i}, 'units' ) &&...
230  ~strcmpi( varargin{i}, 'initialpositionsecondaryaxes' ) &&...
231  ~strcmpi( varargin{i}, 'initialpositionmagnifier' )
232  error('Illegal property specified. Please check.');
233  end
234  if strcmpi( varargin{i}, 'frozenZoomAspectRatio' )
235  if ischar(varargin{i+1}) &&...
236  ( strcmpi(varargin{i+1}, 'on') || strcmpi(varargin{i+1}, 'off') )
237  appDataStruct.globalZoomMode = lower(varargin{i+1});
238  else
239  warning(sprintf('Specified zoom mode not supported. Default values will be applied [%s].', appDataStruct.globalZoomMode));
240  end
241  end
242  if strcmpi( varargin{i}, 'mode' )
243  if ischar(varargin{i+1}) &&...
244  ( strcmpi(varargin{i+1}, 'manual') || strcmpi(varargin{i+1}, 'interactive') )
245  appDataStruct.globalMode = lower(varargin{i+1});
246  else
247  warning(sprintf('Specified mode descriptor not supported. Default values will be applied [%s].', appDataStruct.globalMode));
248  end
249  end
250  if strcmpi( varargin{i}, 'magnifierShape' )
251  if ischar(varargin{i+1}) &&...
252  ( strcmpi(varargin{i+1}, 'rectangle') || strcmpi(varargin{i+1}, 'ellipse') )
253  appDataStruct.magnifierShape = lower(varargin{i+1});
254  else
255  warning(sprintf('Specified magnifier shape not supported. Default values will be applied [%s].', appDataStruct.magnifierShape));
256  end
257  end
258  if strcmpi( varargin{i}, 'displayLinkStyle' )
259  if ischar(varargin{i+1}) &&...
260  ( strcmpi(varargin{i+1}, 'straight') || strcmpi(varargin{i+1}, 'none') || strcmpi(varargin{i+1}, 'edges') )
261  if ~strcmpi(appDataStruct.magnifierShape, 'rectangle') && strcmpi(varargin{i+1}, 'edges')
262  warning(sprintf('Specified link style not supported. Default values will be applied for ''displayLinkStyle''[%s].', appDataStruct.linkDisplayStyle));
263  else
264  appDataStruct.linkDisplayStyle = lower(varargin{i+1});
265  end
266  else
267  warning(sprintf('Specified descriptor not supported. Default values will be applied for ''displayLink''[%s].', appDataStruct.linkDisplayStyle));
268  end
269  end
270  if strcmpi( varargin{i}, 'units' )
271  if ischar(varargin{i+1}) && strcmpi(varargin{i+1}, 'pixels')
272  appDataStruct.globalUnits = lower(varargin{i+1});
273  else
274  warning(sprintf('Specified units descriptor not supported. Default values will be applied [%s].', appDataStruct.globalUnits));
275  end
276  end
277  if strcmpi( varargin{i}, 'edgewidth' )
278  if length(varargin{i+1})==1 && isnumeric(varargin{i+1})
279  appDataStruct.globalEdgeWidth = varargin{i+1};
280  else
281  warning(sprintf('Incorrect edge width value. Default value will be applied [%g].', appDataStruct.globalEdgeWidth ))
282  end
283  end
284  if strcmpi( varargin{i}, 'edgecolor' )
285  if ( length(varargin{i+1})==3 && isnumeric(varargin{i+1}) ) ||...
286  ( ischar(varargin{i+1}) )
287  appDataStruct.globalEdgeColor = varargin{i+1};
288  else
289  warning('Incorrect edge color value. Default black will be applied.');
290  end
291  end
292  if strcmpi( varargin{i}, 'secondaryaxesfacecolor' )
293  if ( length(varargin{i+1})==3 && isnumeric(varargin{i+1}) ) ||...
294  ( ischar(varargin{i+1}) )
295  appDataStruct.secondaryAxesFaceColor = varargin{i+1};
296  else
297  warning('Incorrect secondary axes face color value. Default white will be applied.');
298  end
299  end
300 
301  if strcmpi( varargin{i}, 'secondaryaxesxlim' )
302  if ( length(varargin{i+1})==2 && isnumeric(varargin{i+1}) )
303  appDataStruct.secondaryAxesXLim = varargin{i+1};
304  else
305  warning('Incorrect secondary axes XLim value. Default white will be applied.');
306  end
307  end
308 
309  if strcmpi( varargin{i}, 'secondaryaxesylim' )
310  if ( length(varargin{i+1})==2 && isnumeric(varargin{i+1}) )
311  appDataStruct.secondaryAxesYLim = varargin{i+1};
312  else
313  warning('Incorrect secondary axes YLim value. Default white will be applied.');
314  end
315  end
316 
317  if strcmpi( varargin{i}, 'initialpositionsecondaryaxes' )
318  if length(varargin{i+1})==4 && isnumeric(varargin{i+1})
319  appDataStruct.secondaryAxesPosition = varargin{i+1};
320  else
321  warning('Incorrect initial position of secondary axes. Default values will be applied.')
322  end
323  end
324  if strcmpi( varargin{i}, 'initialpositionmagnifier' )
325  if length(varargin{i+1})==4 && isnumeric(varargin{i+1})
326  appDataStruct.magnifierPosition = varargin{i+1};
327  else
328  warning('Incorrect initial position of magnifier. Default values will be applied.')
329  end
330  end
331  end
332 
333  else
334  error('Number of input arguments not supported.');
335  end
336 end
337 
338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
339 %ENTRY POINT
340 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341 % Create secondary axes
342 if isempty(appDataStruct.secondaryAxesHandle)
343  appDataStruct.secondaryAxesHandle = copyobj(appDataStruct.mainAxesHandle, appDataStruct.figureHandle);
344 end
345 
346 %Configure secondary axis
347 set( appDataStruct.secondaryAxesHandle, 'Color', get(appDataStruct.mainAxesHandle,'Color'), 'Box','on');
348 set( appDataStruct.secondaryAxesHandle, 'FontWeight', 'bold',...
349  'LineWidth', appDataStruct.globalEdgeWidth,...
350  'XColor', appDataStruct.globalEdgeColor,...
351  'YColor', appDataStruct.globalEdgeColor,...
352  'Color', appDataStruct.secondaryAxesFaceColor );
353 set( appDataStruct.figureHandle, 'CurrentAxes', appDataStruct.secondaryAxesHandle );
354 xlabel(''); ylabel(''); zlabel(''); title('');
355 axis( appDataStruct.secondaryAxesHandle, 'normal'); %Ensure that secondary axes are not resizing
356 set( appDataStruct.figureHandle, 'CurrentAxes', appDataStruct.mainAxesHandle );
357 
358 %Default magnifier position
359 if isempty(appDataStruct.magnifierPosition)
360  appDataStruct.magnifierPosition = computeMagnifierDefaultPosition();
361 end
362 
363 %Default secondary axes position
364 if isempty(appDataStruct.secondaryAxesPosition)
365  appDataStruct.secondaryAxesPosition = computeSecondaryAxesDefaultPosition();
366 end
367 
368 % #PATCH 1 (part 1)
369 toolArrayAux = get(appDataStruct.figureHandle, 'userdata');
370 set(appDataStruct.figureHandle, 'userdata', []);
371 % #END PATCH 1 (part 1)
372 
373 %Set initial position of secondary axes
374 setSecondaryAxesPositionInPixels( appDataStruct.secondaryAxesPosition );
375 %Set initial position of magnifier
376 setMagnifierPositionInPixels( appDataStruct.magnifierPosition );
377 
378 % #PATCH 1 (part 2)
379 set(appDataStruct.figureHandle, 'userdata', toolArrayAux);
380 % #END PATCH 1 (part 2)
381 
382 %Update view limits on secondary axis
383 refreshSecondaryAxisLimits();
384 
385 %Update link between secondary axes and magnifier
386 refreshMagnifierToSecondaryAxesLink();
387 
388 %Set actions for interactive mode
389 if strcmpi( appDataStruct.globalMode, 'interactive')
390 
391  toolArray = get(appDataStruct.figureHandle, 'userdata');
392  nTools = length(toolArray);
393  if nTools == 0
394 
395  %Store figure position
396  appDataStruct.figurePosition = getFigurePositionInPixels();
397 
398  %Store old callbacks
399  appDataStruct.figureOldWindowButtonDownFcn = get( appDataStruct.figureHandle, 'WindowButtonDownFcn');
400  appDataStruct.figureOldWindowButtonUpFcn = get( appDataStruct.figureHandle, 'WindowButtonUpFcn');
401  appDataStruct.figureOldWindowButtonMotionFcn = get( appDataStruct.figureHandle, 'WindowButtonMotionFcn');
402  appDataStruct.figureOldKeyPressFcn = get( appDataStruct.figureHandle, 'KeyPressFcn');
403  appDataStruct.figureOldDeleteFcn = get( appDataStruct.figureHandle, 'DeleteFcn');
404  appDataStruct.figureOldResizeFcn = get( appDataStruct.figureHandle, 'ResizeFcn');
405 
406  %Set service funcions to events
407  set( appDataStruct.figureHandle, ...
408  'WindowButtonDownFcn', @ButtonDownCallback, ...
409  'WindowButtonUpFcn', @ButtonUpCallback, ...
410  'WindowButtonMotionFcn', @ButtonMotionCallback, ...
411  'KeyPressFcn', @KeyPressCallback, ...
412  'DeleteFcn', @DeleteCallback,...
413  'ResizeFcn', @ResizeCallback...
414  );
415  end
416 else
417 
418  %Set service funcions to events
419  set( appDataStruct.figureHandle, ...
420  'WindowButtonDownFcn', '', ...
421  'WindowButtonUpFcn', '', ...
422  'WindowButtonMotionFcn', '', ...
423  'KeyPressFcn', '', ...
424  'DeleteFcn', '',...
425  'ResizeFcn', ''...
426  );
427 end
428 
429 %Set focus
430 appDataStruct.focusOnThisTool = true;
431 
432 %Compute unique ID of this magnifying tool, from handles of its elements
433 toolId = appDataStruct.figureHandle +...
434  appDataStruct.mainAxesHandle +...
435  appDataStruct.magnifierHandle +...
436  appDataStruct.linkHandle +...
437  appDataStruct.secondaryAxesHandle;
438 %Set ID of this magnifying tool
439 appDataStruct.toolId = toolId;
440 
441 %Get active figure
442 figureHandle = appDataStruct.figureHandle;
443 
444 %Save object of this tool to userdata in figure object
445 toolArray = get(figureHandle, 'UserData');
446 if isempty(toolArray)
447  toolArray = struct(appDataStruct);
448  toolArray.focusOnThisTool = true;
449 else
450  %Set focus to this tool
451  focusedTool = find([toolArray.focusOnThisTool] == 1);
452  toolArray(focusedTool).focusOnThisTool = false;
453 
454  %search tool ID
455  indexFoundToolId = find([toolArray.toolId] == toolId);
456  if isempty(indexFoundToolId)
457  %If not found, create new
458  indexFoundToolId = length(toolArray)+1;
459  end
460  toolArray(indexFoundToolId) = struct(appDataStruct);
461  toolArray(indexFoundToolId).focusOnThisTool = true;
462 
463 end
464 set( figureHandle, 'UserData', toolArray );
465 
466 %Set callback global behaviour
467 set(appDataStruct.figureHandle, 'Interruptible', 'off');
468 set(appDataStruct.figureHandle, 'BusyAction', 'cancel');
469 
470 %Return created object if requested
471 if outputObjectExpected == true
472  varargout{1} = appDataStruct;
473 end
474 
475 
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 % NAME: refreshSecondaryAxisLimits
478 %
479 % PURPOSE: Updates the view on the secondary axis, based on position and
480 % span of magnifier, and extend of secondary axis.
481 %
482 % INPUT ARGUMENTS:
483 % appDataStructuct [struct 1x1]: global variable
484 % OUTPUT ARGUMENTS:
485 % change 'XLim' and 'YLim' of secondary axis (ACTION)
486 %
487 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488 function refreshSecondaryAxisLimits()
489 
490 global appDataStruct;
491 
492 if isempty(appDataStruct)
493  return;
494 end
495 
496 %If limits specified
497 if ~(isempty(appDataStruct.secondaryAxesXLim) ||...
498  isempty(appDataStruct.secondaryAxesYLim))
499 
500  initialXLim = appDataStruct.secondaryAxesXLim;
501  initialYLim = appDataStruct.secondaryAxesYLim;
502 
503  limitsToSet = [...
504  initialXLim(1)...
505  initialXLim(2)...
506  initialYLim(1)...
507  initialYLim(2)...
508  ];
509  axis(appDataStruct.secondaryAxesHandle, limitsToSet);
510 
511  appDataStruct.secondaryAxesXLim = [];
512  appDataStruct.secondaryAxesYLim = [];
513 else
514  %Get main axes limits, in axes units
515  mainAxesXLim = get( appDataStruct.mainAxesHandle, 'XLim' );
516  mainAxesYLim = get( appDataStruct.mainAxesHandle, 'YLim' );
517  mainAxesXDir = get( appDataStruct.mainAxesHandle, 'XDir' );
518  mainAxesYDir = get( appDataStruct.mainAxesHandle, 'YDir' );
519 
520  %Get position and size of main axes in pixels
521  mainAxesPositionInPixels = getMainAxesPositionInPixels();
522 
523  %Compute Pixels-to-axes units conversion factors
524  xMainAxisPixels2UnitsFactor = determineSpan( mainAxesXLim(1), mainAxesXLim(2) )/mainAxesPositionInPixels(3);
525  yMainAxisPixels2UnitsFactor = determineSpan( mainAxesYLim(1), mainAxesYLim(2) )/mainAxesPositionInPixels(4);
526 
527  %Get position and extend of magnifier, in pixels
528  magnifierPosition = getMagnifierPositionInPixels(); %In pixels
529 
530  %Relative to the lower-left corner of the axes
531  magnifierPosition(1) = magnifierPosition(1) - mainAxesPositionInPixels(1);
532  magnifierPosition(2) = magnifierPosition(2) - mainAxesPositionInPixels(2);
533 
534  %Compute position and exted of magnifier, in axes units
535  magnifierPosition(3) = magnifierPosition(3) * xMainAxisPixels2UnitsFactor;
536  magnifierPosition(4) = magnifierPosition(4) * yMainAxisPixels2UnitsFactor;
537  if strcmpi(mainAxesXDir, 'normal') && strcmpi(mainAxesYDir, 'normal')
538  magnifierPosition(1) = mainAxesXLim(1) + magnifierPosition(1)*xMainAxisPixels2UnitsFactor;
539  magnifierPosition(2) = mainAxesYLim(1) + magnifierPosition(2)*yMainAxisPixels2UnitsFactor;
540  end
541  if strcmpi(mainAxesXDir, 'normal') && strcmpi(mainAxesYDir, 'reverse')
542  magnifierPosition(1) = mainAxesXLim(1) + magnifierPosition(1)*xMainAxisPixels2UnitsFactor;
543  magnifierPosition(2) = mainAxesYLim(2) - magnifierPosition(2)*yMainAxisPixels2UnitsFactor - magnifierPosition(4);
544  end
545  if strcmpi(mainAxesXDir, 'reverse') && strcmpi(mainAxesYDir, 'normal')
546  magnifierPosition(1) = mainAxesXLim(2) - magnifierPosition(1)*xMainAxisPixels2UnitsFactor - magnifierPosition(3);
547  magnifierPosition(2) = mainAxesYLim(1) + magnifierPosition(2)*yMainAxisPixels2UnitsFactor;
548  end
549  if strcmpi(mainAxesXDir, 'reverse') && strcmpi(mainAxesYDir, 'reverse')
550  magnifierPosition(1) = mainAxesXLim(2) - magnifierPosition(1)*xMainAxisPixels2UnitsFactor - magnifierPosition(3);
551  magnifierPosition(2) = mainAxesYLim(2) - magnifierPosition(2)*yMainAxisPixels2UnitsFactor - magnifierPosition(4);
552  end
553 
554  secondaryAxisXlim = [magnifierPosition(1) magnifierPosition(1)+magnifierPosition(3)];
555  secondaryAxisYlim = [magnifierPosition(2) magnifierPosition(2)+magnifierPosition(4)];
556 
557  zoomFactor = appDataStruct.secondaryAxesAdditionalZoomingFactor;
558  xZoom = zoomFactor(1);
559  yZoom = zoomFactor(2);
560 
561  aux_secondaryAxisXlim(1) = mean(secondaryAxisXlim) -...
562  determineSpan( secondaryAxisXlim(1), mean(secondaryAxisXlim) )*(1-xZoom);
563  aux_secondaryAxisXlim(2) = mean(secondaryAxisXlim) +...
564  determineSpan( secondaryAxisXlim(2), mean(secondaryAxisXlim) )*(1-xZoom);
565  aux_secondaryAxisYlim(1) = mean(secondaryAxisYlim) -...
566  determineSpan( secondaryAxisYlim(1), mean(secondaryAxisYlim) )*(1-yZoom);
567  aux_secondaryAxisYlim(2) = mean(secondaryAxisYlim) +...
568  determineSpan( secondaryAxisYlim(2), mean(secondaryAxisYlim) )*(1-yZoom);
569 
570  if aux_secondaryAxisXlim(1)<aux_secondaryAxisXlim(2) &&...
571  all(isfinite(aux_secondaryAxisXlim))
572  set( appDataStruct.secondaryAxesHandle, 'XLim', aux_secondaryAxisXlim );
573  end
574  if aux_secondaryAxisYlim(1)<aux_secondaryAxisYlim(2) &&...
575  all(isfinite(aux_secondaryAxisYlim))
576  set( appDataStruct.secondaryAxesHandle, 'YLim', aux_secondaryAxisYlim );
577  end
578 
579 end
580 
581 %Increase line width in plots on secondary axis
582 childHandle = get( appDataStruct.secondaryAxesHandle, 'Children');
583 for iChild = 1:length(childHandle)
584  if strcmpi(get(childHandle(iChild), 'Type'), 'line')
585  set(childHandle(iChild), 'LineWidth', 2);
586  end
587  if strcmpi(get(childHandle(iChild), 'Type'), 'image')
588  %Do nothing for now
589  end
590 end
591 
592 
593 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
594 % NAME: determineSpan
595 %
596 % PURPOSE: Computes the distance between two real numbers on a 1D space.
597 %
598 % INPUT ARGUMENTS:
599 % v1 [double 1x1]: first number
600 % v2 [double 1x1]: second number
601 % OUTPUT ARGUMENTS:
602 % span [double 1x1]: computed span
603 %
604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
605 function span = determineSpan( v1, v2 )
606 
607 if v1>=0 && v2>=0
608  span = max(v1,v2) - min(v1,v2);
609 end
610 if v1>=0 && v2<0
611  span = v1 - v2;
612 end
613 if v1<0 && v2>=0
614  span = -v1 + v2;
615 end
616 if v1<0 && v2<0
617  span = max(-v1,-v2) - min(-v1,-v2);
618 end
619 
620 
621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
622 % NAME: ResizeCallback
623 %
624 % PURPOSE: Service routine to Resize event.
625 %
626 % INPUT ARGUMENTS:
627 %
628 % OUTPUT ARGUMENTS:
629 %
630 %
631 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
632 function ResizeCallback(src,eventdata)
633 
634 global appDataStruct;
635 
636 if isempty(appDataStruct)
637  return;
638 end
639 
640 %Get userdata
641 toolArray = get(src, 'userdata');
642 
643 if isempty(toolArray)
644  return;
645 end
646 
647 nTools = length( toolArray );
648 
649 %Store Old&New Figure positions
650 oldFigurePosition = toolArray(1).figurePosition;
651 newFigurePosition = getFigurePositionInPixels();
652 
653 %Backup global appDataStruct
654 appDataStructAux = appDataStruct;
655 
656 for i=1:nTools
657 
658  %Modify global vaiable, accessed by functions called below
659  appDataStruct = initializeToolStruct( toolArray(i) );
660 
661  %Set position of secondaryAxes (automatically modified)
662  toolArray(i).secondaryAxesPosition = getSecondaryAxesPositionInPixels();
663 
664 
665  toolArray(i).magnifierPosition(1) = toolArray(i).magnifierPosition(1) * newFigurePosition(3)/oldFigurePosition(3);
666  toolArray(i).magnifierPosition(2) = toolArray(i).magnifierPosition(2) * newFigurePosition(4)/oldFigurePosition(4);
667  toolArray(i).magnifierPosition(3) = toolArray(i).magnifierPosition(3) * newFigurePosition(3)/oldFigurePosition(3);
668  toolArray(i).magnifierPosition(4) = toolArray(i).magnifierPosition(4) * newFigurePosition(4)/oldFigurePosition(4);
669  setMagnifierPositionInPixels( toolArray(i).magnifierPosition );
670 
671  %Update view limits on secondary axis
672  refreshSecondaryAxisLimits();
673  %Update link between secondary axes and magnifier
674  refreshMagnifierToSecondaryAxesLink();
675 
676  if i==1
677  %Update figure position
678  toolArray(i).figurePosition = getFigurePositionInPixels();
679  appDataStruct.figurePosition = toolArray(i).figurePosition;
680  end
681 end
682 
683 %Update userdata
684 set(toolArray(1).figureHandle, 'userdata', toolArray);
685 
686 %Update appDataStruct
687 appDataStruct = appDataStructAux;
688 %Get current position of seconday axes (in pixels)
689 appDataStruct.secondaryAxesPosition = getSecondaryAxesPositionInPixels();
690 %Get magnifier current position and size
691 appDataStruct.magnifierPosition = getMagnifierPositionInPixels();
692 
693 clear appDataStructAux;
694 
695 
696 
697 
698 
699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
700 % NAME: KeyPressCallback
701 %
702 % PURPOSE: Service routine to KeyPress event.
703 %
704 % INPUT ARGUMENTS:
705 %
706 % OUTPUT ARGUMENTS:
707 %
708 %
709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710 function KeyPressCallback(src,eventdata)
711 
712 global appDataStruct
713 
714 if isempty(appDataStruct)
715  return;
716 end
717 
718 currentCaracter = eventdata.Key;
719 currentModifier = eventdata.Modifier;
720 
721 switch(currentCaracter)
722  case {'leftarrow'} % left arrow
723  %Move magnifier to the left
724  if isempty(currentModifier)
725 
726  position = getMagnifierPositionInPixels();
727  magnifierPosition(1) = position(1)-1;
728  magnifierPosition(2) = position(2);
729  magnifierPosition(3) = position(3);
730  magnifierPosition(4) = position(4);
731  setMagnifierPositionInPixels( magnifierPosition );
732 
733  toolArray = get( src, 'UserData' );
734  focusedTool = find([toolArray.focusOnThisTool] == 1);
735  toolArray(focusedTool) = updateToolId( toolArray(focusedTool), focusedTool, 'noToggle' );
736  set( src, 'UserData', toolArray );
737 
738  end
739  %Compress magnifier on the X axis
740  if strcmp(currentModifier, 'shift')
741  position = getMagnifierPositionInPixels();
742  magnifierPosition(3) = position(3)*(1 - 0.1);
743  if strcmpi( appDataStruct.globalZoomMode, 'off')
744  magnifierPosition(4) = position(4);
745  else
746  %If 'freezeZoomAspectRatio' to 'on', be consistent
747  magnifierPosition(4) = position(4)*(1 - 0.1);
748  end
749  magnifierPosition(1) = position(1)-(-position(3)+magnifierPosition(3))/2;
750  magnifierPosition(2) = position(2)-(-position(4)+magnifierPosition(4))/2;
751  setMagnifierPositionInPixels( magnifierPosition );
752  end
753  %Move secondary axes to the left
754  if strcmp(currentModifier, 'control')
755  position = getSecondaryAxesPositionInPixels();
756  secondaryAxesPosition(1) = position(1)-1;
757  secondaryAxesPosition(2) = position(2);
758  secondaryAxesPosition(3) = position(3);
759  secondaryAxesPosition(4) = position(4);
760  setSecondaryAxesPositionInPixels( secondaryAxesPosition );
761  end
762  %Compress secondary axes on the X axis
763  if strcmp(currentModifier, 'alt')
764  position = getSecondaryAxesPositionInPixels();
765  secondaryAxesPosition(3) = position(3)*(1 - 0.1);
766  if strcmpi( appDataStruct.globalZoomMode, 'off')
767  secondaryAxesPosition(4) = position(4);
768  else
769  %If 'freezeZoomAspectRatio' to 'on', be consistent
770  secondaryAxesPosition(4) = position(4)*(1 - 0.1);
771  end
772  secondaryAxesPosition(1) = position(1)-(-position(3)+secondaryAxesPosition(3))/2;
773  secondaryAxesPosition(2) = position(2)-(-position(4)+secondaryAxesPosition(4))/2;
774  setSecondaryAxesPositionInPixels( secondaryAxesPosition );
775  end
776 
777  case {'rightarrow'} % right arrow
778  %Move magnifier to the right
779  if isempty(currentModifier)
780  position = getMagnifierPositionInPixels();
781  magnifierPosition(1) = position(1)+1;
782  magnifierPosition(2) = position(2);
783  magnifierPosition(3) = position(3);
784  magnifierPosition(4) = position(4);
785  setMagnifierPositionInPixels( magnifierPosition );
786 
787  toolArray = get( src, 'UserData' );
788  focusedTool = find([toolArray.focusOnThisTool] == 1);
789  toolArray(focusedTool) = updateToolId( toolArray(focusedTool), focusedTool, 'noToggle' );
790  set( src, 'UserData', toolArray );
791 
792  end
793  %Expand magnifier on the X axis
794  if strcmp(currentModifier, 'shift')
795  position = getMagnifierPositionInPixels();
796  magnifierPosition(3) = position(3)*(1 + 0.1);
797  if strcmpi( appDataStruct.globalZoomMode, 'off')
798  magnifierPosition(4) = position(4);
799  else
800  %If 'freezeZoomAspectRatio' to 'on', be consistent
801  magnifierPosition(4) = position(4)*(1 + 0.1);
802  end
803  magnifierPosition(1) = position(1)-(-position(3)+magnifierPosition(3))/2;
804  magnifierPosition(2) = position(2)-(-position(4)+magnifierPosition(4))/2;
805  setMagnifierPositionInPixels( magnifierPosition );
806  end
807  %Move secondary axes to the right
808  if strcmp(currentModifier, 'control')
809  position = getSecondaryAxesPositionInPixels();
810  secondaryAxesPosition(1) = position(1)+1;
811  secondaryAxesPosition(2) = position(2);
812  secondaryAxesPosition(3) = position(3);
813  secondaryAxesPosition(4) = position(4);
814  setSecondaryAxesPositionInPixels( secondaryAxesPosition );
815  end
816  %Expand secondary axes on the X axis
817  if strcmp(currentModifier, 'alt')
818  position = getSecondaryAxesPositionInPixels();
819  secondaryAxesPosition(3) = position(3)*(1 + 0.1);
820  if strcmpi( appDataStruct.globalZoomMode, 'off')
821  secondaryAxesPosition(4) = position(4);
822  else
823  %If 'freezeZoomAspectRatio' to 'on', be consistent
824  secondaryAxesPosition(4) = position(4)*(1 + 0.1);
825  end
826  secondaryAxesPosition(1) = position(1)-(-position(3)+secondaryAxesPosition(3))/2;
827  secondaryAxesPosition(2) = position(2)-(-position(4)+secondaryAxesPosition(4))/2;
828  setSecondaryAxesPositionInPixels( secondaryAxesPosition );
829  end
830 
831  case {'uparrow'} % up arrow
832  %Move magnifier to the top
833  if isempty(currentModifier)
834  position = getMagnifierPositionInPixels();
835  magnifierPosition(1) = position(1);
836  magnifierPosition(2) = position(2)+1;
837  magnifierPosition(3) = position(3);
838  magnifierPosition(4) = position(4);
839  setMagnifierPositionInPixels( magnifierPosition );
840 
841  toolArray = get( src, 'UserData' );
842  focusedTool = find([toolArray.focusOnThisTool] == 1);
843  toolArray(focusedTool) = updateToolId( toolArray(focusedTool), focusedTool, 'noToggle' );
844  set( src, 'UserData', toolArray );
845 
846  end
847  %Expand magnifier on the Y axis
848  if strcmp(currentModifier, 'shift')
849  position = getMagnifierPositionInPixels();
850  if strcmpi( appDataStruct.globalZoomMode, 'off')
851  magnifierPosition(3) = position(3);
852  else
853  %If 'freezeZoomAspectRatio' to 'on', be consistent
854  magnifierPosition(3) = position(3)*(1 + 0.1);
855  end
856  magnifierPosition(4) = position(4)*(1 + 0.1);
857  magnifierPosition(1) = position(1)-(-position(3)+magnifierPosition(3))/2;
858  magnifierPosition(2) = position(2)-(-position(4)+magnifierPosition(4))/2;
859  setMagnifierPositionInPixels( magnifierPosition );
860  end
861  %Move secondary axes to the top
862  if strcmp(currentModifier, 'control')
863  position = getSecondaryAxesPositionInPixels();
864  secondaryAxesPosition(1) = position(1);
865  secondaryAxesPosition(2) = position(2)+1;
866  secondaryAxesPosition(3) = position(3);
867  secondaryAxesPosition(4) = position(4);
868  setSecondaryAxesPositionInPixels( secondaryAxesPosition );
869  end
870  %Expand secondary axes on the Y axis
871  if strcmp(currentModifier, 'alt')
872  position = getSecondaryAxesPositionInPixels();
873  if strcmpi( appDataStruct.globalZoomMode, 'off')
874  secondaryAxesPosition(3) = position(3);
875  else
876  %If 'freezeZoomAspectRatio' to 'on', be consistent
877  secondaryAxesPosition(3) = position(3)*(1 + 0.1);
878  end
879  secondaryAxesPosition(4) = position(4)*(1 + 0.1);
880  secondaryAxesPosition(1) = position(1)-(-position(3)+secondaryAxesPosition(3))/2;
881  secondaryAxesPosition(2) = position(2)-(-position(4)+secondaryAxesPosition(4))/2;
882  setSecondaryAxesPositionInPixels( secondaryAxesPosition );
883  end
884 
885  case {'downarrow'} % down arrow
886  %Move magnifier to the bottom
887  if isempty(currentModifier)
888  position = getMagnifierPositionInPixels();
889  magnifierPosition(1) = position(1);
890  magnifierPosition(2) = position(2)-1;
891  magnifierPosition(3) = position(3);
892  magnifierPosition(4) = position(4);
893  setMagnifierPositionInPixels( magnifierPosition );
894 
895  toolArray = get( src, 'UserData' );
896  focusedTool = find([toolArray.focusOnThisTool] == 1);
897  toolArray(focusedTool) = updateToolId( toolArray(focusedTool), focusedTool, 'noToggle' );
898  set( src, 'UserData', toolArray );
899 
900  end
901  %Compress magnifier on the Y axis
902  if strcmp(currentModifier, 'shift')
903  position = getMagnifierPositionInPixels();
904  if strcmpi( appDataStruct.globalZoomMode, 'off')
905  magnifierPosition(3) = position(3);
906  else
907  %If 'freezeZoomAspectRatio' to 'on', be consistent
908  magnifierPosition(3) = position(3)*(1 - 0.1);
909  end
910  magnifierPosition(4) = position(4)*(1 - 0.1);
911  magnifierPosition(1) = position(1)-(-position(3)+magnifierPosition(3))/2;
912  magnifierPosition(2) = position(2)-(-position(4)+magnifierPosition(4))/2;
913  setMagnifierPositionInPixels( magnifierPosition );
914  end
915  %Move secondary axes to the bottom
916  if strcmp(currentModifier, 'control')
917  position = getSecondaryAxesPositionInPixels();
918  secondaryAxesPosition(1) = position(1);
919  secondaryAxesPosition(2) = position(2)-1;
920  secondaryAxesPosition(3) = position(3);
921  secondaryAxesPosition(4) = position(4);
922  setSecondaryAxesPositionInPixels( secondaryAxesPosition );
923  end
924  %Compress secondary axes on the Y axis
925  if strcmp(currentModifier, 'alt')
926  position = getSecondaryAxesPositionInPixels();
927  if strcmpi( appDataStruct.globalZoomMode, 'off')
928  secondaryAxesPosition(3) = position(3);
929  else
930  %If 'freezeZoomAspectRatio' to 'on', be consistent
931  secondaryAxesPosition(3) = position(3)*(1 - 0.1);
932  end
933  secondaryAxesPosition(4) = position(4)*(1 - 0.1);
934  secondaryAxesPosition(1) = position(1)-(-position(3)+secondaryAxesPosition(3))/2;
935  secondaryAxesPosition(2) = position(2)-(-position(4)+secondaryAxesPosition(4))/2;
936  setSecondaryAxesPositionInPixels( secondaryAxesPosition );
937  end
938 
939  case {'tab'} % Tabulator
940  %Switch focus to next magnifier instance on the current figure
941  toolArray = get( src, 'UserData' );
942  nTools = length(toolArray);
943  focusedTool = find([toolArray.focusOnThisTool] == 1);
944  if focusedTool ~= nTools
945  nextFocusedTool = focusedTool+1;
946  else
947  nextFocusedTool = 1;
948  end
949  appDataStruct = initializeToolStruct( toolArray(nextFocusedTool) );
950  appDataStruct.focusOnThisTool = 1;
951  toolArray(focusedTool).focusOnThisTool = 0;
952  toolArray(nextFocusedTool).focusOnThisTool = 1;
953  if not(isempty(toolArray(focusedTool).toolIdHandle))
954  set(toolArray(focusedTool).toolIdHandle,'BackgroundColor', 'black', 'Color', 'white');
955  set(toolArray(nextFocusedTool).toolIdHandle,'BackgroundColor', 'red', 'Color', 'white');
956  end
957 
958  set( src, 'UserData', toolArray );
959 
960  case {'d'} % 'd'
961  %Delete focused instance
962  if strcmp(currentModifier, 'control')
963  toolArray = get( src, 'UserData' );
964  nTools = length(toolArray);
965  focusedTool = find([toolArray.focusOnThisTool] == 1);
966 
967  delete(toolArray(focusedTool).magnifierHandle);
968  delete(toolArray(focusedTool).linkHandle);
969  delete(toolArray(focusedTool).secondaryAxesHandle);
970 
971  if nTools > 1
972  %Set focus to next instance
973  if focusedTool ~= nTools
974  nextFocusedTool = focusedTool+1;
975  else
976  nextFocusedTool = 1;
977  end
978  toolArray(nextFocusedTool).focusOnThisTool = 1;
979  appDataStruct = initializeToolStruct( toolArray(nextFocusedTool) );
980 
981  toolArray(focusedTool) = [];
982  set( src, 'UserData', toolArray );
983 
984  else
985  %No instance to set focus on
986  appDataStruct = [];
987  set( src, 'UserData', [] );
988 
989  end
990  end
991 
992  case {'a'} % 'a'
993  %Debug info
994  if strcmp(currentModifier, 'control')
995  magnifierPosition = getMagnifierPositionInPixels();
996  disp(sprintf('Magnifier position: [%g %g %g %g];', magnifierPosition(1), magnifierPosition(2), magnifierPosition(3), magnifierPosition(4) ));
997  secondaryAxesPosition = getSecondaryAxesPositionInPixels();
998  disp(sprintf('Secondary axes position: [%g %g %g %g];', secondaryAxesPosition(1), secondaryAxesPosition(2), secondaryAxesPosition(3), secondaryAxesPosition(4) ));
999  end
1000 
1001  case {'q'} % 'q'
1002  %additional xooming factors reseted
1003  if strcmp(currentModifier, 'control')
1004  appDataStruct.secondaryAxesAdditionalZoomingFactor = [0 0];
1005  end
1006 
1007  case {'i'} % 'i'
1008  %display/hide on-screen tool identifier
1009  if strcmp(currentModifier, 'control')
1010  toolArray = get( src, 'UserData' );
1011  nTools = length(toolArray);
1012  for iTool = 1:nTools
1013 
1014  toolArray(iTool) = updateToolId( toolArray(iTool), iTool, 'toggle' );
1015 
1016  end
1017  set( src, 'UserData', toolArray );
1018 
1019  end
1020 
1021  case {'pageup'} % '+'
1022  zoomFactor = appDataStruct.secondaryAxesAdditionalZoomingFactor;
1023 
1024  %Increase additional zooming factor on X-axis
1025  if isempty(currentModifier)
1026  zoomFactor(1) = zoomFactor(1) + 0.1;
1027  if strcmpi( appDataStruct.globalZoomMode, 'on')
1028  zoomFactor(2) = zoomFactor(2) + 0.1;
1029  end
1030  appDataStruct.secondaryAxesAdditionalZoomingFactor = zoomFactor;
1031  end
1032  %Increase additional zooming factor on Y-axis
1033  if strcmp(currentModifier, 'shift')
1034  zoomFactor(2) = zoomFactor(2) + 0.1;
1035  if strcmpi( appDataStruct.globalZoomMode, 'on')
1036  zoomFactor(1) = zoomFactor(1) + 0.1;
1037  end
1038  appDataStruct.secondaryAxesAdditionalZoomingFactor = zoomFactor;
1039  end
1040 
1041  case {'pagedown'} % '-'
1042  zoomFactor = appDataStruct.secondaryAxesAdditionalZoomingFactor;
1043 
1044  %Redude additional zooming factor on X-axis
1045  if isempty(currentModifier)
1046  zoomFactor(1) = zoomFactor(1) - 0.1;
1047  if strcmpi( appDataStruct.globalZoomMode, 'on')
1048  zoomFactor(2) = zoomFactor(2) - 0.1;
1049  end
1050  appDataStruct.secondaryAxesAdditionalZoomingFactor = zoomFactor;
1051  end
1052  %Redude additional zooming factor on Y-axis
1053  if strcmp(currentModifier, 'shift')
1054  zoomFactor(2) = zoomFactor(2) - 0.1;
1055  if strcmpi( appDataStruct.globalZoomMode, 'on')
1056  zoomFactor(1) = zoomFactor(1) - 0.1;
1057  end
1058  appDataStruct.secondaryAxesAdditionalZoomingFactor = zoomFactor;
1059  end
1060 
1061  otherwise
1062 
1063 
1064 end
1065 
1066 %Update view limits on secondary axis
1067 refreshSecondaryAxisLimits();
1068 
1069 %Update link between secondary axes and magnifier
1070 refreshMagnifierToSecondaryAxesLink();
1071 
1072 % %Update userdata
1073 % toolArray = get(src, 'userdata');
1074 % focusedTool = find([toolArray.focusOnThisTool] == 1);
1075 % toolArray(focusedTool) = appDataStruct;
1076 % set(src, 'userData', toolArray);
1077 
1078 
1079 
1080 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1081 % NAME: ButtonMotionCallback
1082 %
1083 % PURPOSE: Service routine to ButtonMotion event.
1084 %
1085 % INPUT ARGUMENTS:
1086 %
1087 % OUTPUT ARGUMENTS:
1088 %
1089 %
1090 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1091 function ButtonMotionCallback(src,eventdata)
1092 
1093 global appDataStruct
1094 
1095 if isempty(appDataStruct)
1096  return;
1097 end
1098 
1099 % pointerPos = get(appDataStructuct.figure.handle, 'CurrentPoint');
1100 % disp(sprintf('X: %g ; Y: %g', pointerPos(1), pointerPos(2)) );
1101 
1102 getPointerArea();
1103 
1104 %If Left mouse button not pressed, exit
1105 if appDataStruct.ButtonDown == false
1106  return;
1107 end
1108 
1109 
1110 %If Left mouse button pressed while the pointer is moving (drag)
1111 switch appDataStruct.pointerArea
1112 
1113  case 'insideSecondaryAxis'
1114  %Get current position of seconday axes (in pixels)
1115  appDataStruct.secondaryAxesPosition = getSecondaryAxesPositionInPixels();
1116 
1117  %Get pointer position on figure's frame
1118  currentPointerPositionOnFigureFrame = getPointerPositionOnFigureFrame();
1119 
1120  pointerPositionOnButtonDown = appDataStruct.pointerPositionOnButtonDown;
1121 
1122  %Modify position
1123  secondaryAxisPosition_W = appDataStruct.secondaryAxesPosition(3);
1124  secondaryAxisPosition_H = appDataStruct.secondaryAxesPosition(4);
1125  secondaryAxisPosition_X = appDataStruct.secondaryAxesPosition(1) + (-pointerPositionOnButtonDown(1)+currentPointerPositionOnFigureFrame(1));
1126  secondaryAxisPosition_Y = appDataStruct.secondaryAxesPosition(2) + (-pointerPositionOnButtonDown(2)+currentPointerPositionOnFigureFrame(2));
1127  appDataStruct.pointerPositionOnButtonDown = currentPointerPositionOnFigureFrame;
1128 
1129  %Set initial position and size of secondary axes
1130  setSecondaryAxesPositionInPixels( [...
1131  secondaryAxisPosition_X,...
1132  secondaryAxisPosition_Y,...
1133  secondaryAxisPosition_W,...
1134  secondaryAxisPosition_H...
1135  ] );
1136 
1137 
1138  case 'insideMagnifier'
1139  %Get magnifier current position and size
1140  appDataStruct.magnifierPosition = getMagnifierPositionInPixels();
1141 
1142  %Get pointer position on figure's frame
1143  currentPointerPosition = getPointerPositionOnFigureFrame();
1144 
1145  pointerPositionOnButtonDown = appDataStruct.pointerPositionOnButtonDown;
1146 
1147  %Modify magnifier position
1148  magnifierPosition_W = appDataStruct.magnifierPosition(3);
1149  magnifierPosition_H = appDataStruct.magnifierPosition(4);
1150  magnifierPosition_X = appDataStruct.magnifierPosition(1) + (-pointerPositionOnButtonDown(1)+currentPointerPosition(1));
1151  magnifierPosition_Y = appDataStruct.magnifierPosition(2) + (-pointerPositionOnButtonDown(2)+currentPointerPosition(2));
1152  appDataStruct.pointerPositionOnButtonDown = currentPointerPosition;
1153 
1154  %Set initial position and size of magnifying rectangle
1155  setMagnifierPositionInPixels( [...
1156  magnifierPosition_X...
1157  magnifierPosition_Y...
1158  magnifierPosition_W...
1159  magnifierPosition_H...
1160  ] );
1161 
1162  %Refresh zooming on secondary axis, based on magnifier position and extend
1163  refreshSecondaryAxisLimits();
1164 
1165  toolArray = get( src, 'UserData' );
1166  focusedTool = find([toolArray.focusOnThisTool] == 1);
1167  toolArray(focusedTool) = updateToolId( toolArray(focusedTool), focusedTool, 'noToggle' );
1168  set( src, 'UserData', toolArray );
1169 
1170  otherwise
1171 % appDataStructuct.pointerArea
1172 
1173 end
1174 
1175 %Update link between secondary axes and magnifier
1176 refreshMagnifierToSecondaryAxesLink();
1177 
1178 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1179 % NAME: ButtonDownCallback
1180 %
1181 % PURPOSE: Service routine to ButtonDown event.
1182 %
1183 % INPUT ARGUMENTS:
1184 %
1185 % OUTPUT ARGUMENTS:
1186 %
1187 %
1188 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1189 function ButtonDownCallback(src,eventdata)
1190 
1191 global appDataStruct
1192 
1193 if isempty(appDataStruct)
1194  return;
1195 end
1196 
1197 if strcmpi( get(appDataStruct.figureHandle, 'SelectionType'), 'normal' )
1198 
1199  %Respond to left mouse button
1200  appDataStruct.ButtonDown = true;
1201  %Get pointer position on figure's frame
1202  appDataStruct.pointerPositionOnButtonDown = getPointerPositionOnFigureFrame();
1203 
1204 elseif strcmpi( get(appDataStruct.figureHandle, 'SelectionType'), 'alt' )
1205 
1206  %Display contextual menu?
1207 
1208 elseif strcmpi( get(appDataStruct.figureHandle, 'SelectionType'), 'open' )
1209 
1210  %Is pointer on any active area?
1211  toolArray = get(src, 'userdata');
1212  nTools = length(toolArray);
1213  focusedTool = find([toolArray.focusOnThisTool] == 1);
1214  nextFocusedTool = 0;
1215  foundActive = false;
1216  while nextFocusedTool<=nTools-1 && foundActive == false
1217 
1218  nextFocusedTool = nextFocusedTool+1;
1219  appDataStructAux = appDataStruct;
1220  appDataStruct = initializeToolStruct( toolArray(nextFocusedTool) );
1221  getPointerArea();
1222  if ~strcmp( appDataStruct.pointerArea, 'none')
1223  foundActive = true;
1224  end
1225 
1226  end
1227  if foundActive == true
1228  %Switch focus to next magnifier instance on the current figure
1229  appDataStruct = initializeToolStruct( toolArray(nextFocusedTool) );
1230  appDataStruct.focusOnThisTool = 1;
1231  toolArray(focusedTool).focusOnThisTool = 0;
1232  toolArray(nextFocusedTool).focusOnThisTool = 1;
1233  if not(isempty(toolArray(focusedTool).toolIdHandle))
1234  set(toolArray(focusedTool).toolIdHandle,'BackgroundColor', 'black', 'Color', 'white');
1235  set(toolArray(nextFocusedTool).toolIdHandle,'BackgroundColor', 'red', 'Color', 'white');
1236  end
1237  set( src, 'UserData', toolArray );
1238 
1239 
1240  else
1241  appDataStruct = appDataStructAux;
1242  end
1243 
1244 end
1245 
1246 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1247 % NAME: ButtonUpCallback
1248 %
1249 % PURPOSE: Service routine to ButtonUp event.
1250 %
1251 % INPUT ARGUMENTS:
1252 %
1253 % OUTPUT ARGUMENTS:
1254 %
1255 %
1256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1257 function ButtonUpCallback(src,eventdata)
1258 
1259 global appDataStruct
1260 
1261 if isempty(appDataStruct)
1262  return;
1263 end
1264 
1265 % if strcmp(appDataStruct.pointerArea, 'insideMagnifier')
1266 % %Refresh zooming on secondary axis, based on magnifier position and extend
1267 % refreshSecondaryAxisLimits();
1268 % end
1269 
1270 appDataStruct.ButtonDown = false;
1271 
1272 toolArray = get(src, 'userdata');
1273 focusedTool = find([toolArray.focusOnThisTool] == 1);
1274 toolArray(focusedTool).ButtonDown = false;
1275 set(src, 'userdata', toolArray);
1276 
1277 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278 % NAME: DeleteCallback
1279 %
1280 % PURPOSE: Service routine to Delete event.
1281 %
1282 % INPUT ARGUMENTS:
1283 %
1284 % OUTPUT ARGUMENTS:
1285 %
1286 %
1287 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1288 function DeleteCallback(src,eventdata)
1289 
1290 global appDataStruct;
1291 
1292 if isempty(appDataStruct)
1293  return;
1294 end
1295 
1296 toolArray = get(src, 'UserData');
1297 
1298 %Recover old callback handles from the first instance
1299 set( src, 'WindowButtonDownFcn', toolArray(1).figureOldWindowButtonDownFcn );
1300 set( src, 'WindowButtonUpFcn', toolArray(1).figureOldWindowButtonUpFcn );
1301 set( src, 'WindowButtonMotionFcn', toolArray(1).figureOldWindowButtonMotionFcn );
1302 set( src, 'KeyPressFcn', toolArray(1).figureOldKeyPressFcn );
1303 set( src, 'DeleteFcn', toolArray(1).figureOldDeleteFcn );
1304 set( src, 'ResizeFcn', toolArray(1).figureOldResizeFcn );
1305 
1306 %Clear global variable when figure is closed
1307 clear global appDataStructuct;
1308 
1309 
1310 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1311 % NAME: getPointerPositionOnFigureFrame
1312 %
1313 % PURPOSE: determine if the position of the mouse pointer on the figure frame, in pixels.
1314 %
1315 % INPUT ARGUMENTS:
1316 % none
1317 % OUTPUT ARGUMENTS:
1318 % pointerPositionOnFigureFrame [double 1x2]: (X Y)
1319 % position
1320 %
1321 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1322 function pointerPositionOnFigureFrame = getPointerPositionOnFigureFrame()
1323 
1324 global appDataStruct
1325 
1326 if isempty(appDataStruct)
1327  return;
1328 end
1329 
1330 %Get position of mouse pointer on screen
1331 defaultUnits = get(appDataStruct.figureHandle,'Units');
1332 set(appDataStruct.figureHandle, 'Units', 'pixels');
1333 pointerPositionOnFigureFrame = get(appDataStruct.figureHandle,'CurrentPoint');
1334 set(appDataStruct.figureHandle, 'Units', defaultUnits);
1335 
1336 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1337 % NAME: getPointerArea
1338 %
1339 % PURPOSE: determine if the mouse pointer is on an active area. Change
1340 % pointer image if this is the case, and communicate the status.
1341 %
1342 % INPUT ARGUMENTS:
1343 % appDataStructuct [struct 1x1]: global variable
1344 % OUTPUT ARGUMENTS:
1345 % change image of pointer (ACTION)
1346 % appDataStructuct.pointerArea: ID of the active area
1347 %
1348 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1349 function getPointerArea()
1350 
1351 global appDataStruct
1352 
1353 if isempty(appDataStruct)
1354  return;
1355 end
1356 
1357 %Get current pointer position on figure frame
1358 pointerPositionOnFigureFrame = getPointerPositionOnFigureFrame();
1359 
1360 %Get current secondaryAxes position
1361 secondaryAxesPosition = getSecondaryAxesPositionInPixels();
1362 
1363 %Get current magnifier position
1364 magnifierPosition = getMagnifierPositionInPixels();
1365 
1366 %If mouse pointer on the secondary axis
1367 if pointerPositionOnFigureFrame(1)>=secondaryAxesPosition(1) &&...
1368  pointerPositionOnFigureFrame(1)<=secondaryAxesPosition(1)+secondaryAxesPosition(3) &&...
1369  pointerPositionOnFigureFrame(2)>=secondaryAxesPosition(2) &&...
1370  pointerPositionOnFigureFrame(2)<=secondaryAxesPosition(2)+secondaryAxesPosition(4)
1371  %Pointer inside secondary axis
1372  set(appDataStruct.figureHandle, 'Pointer', 'fleur');
1373 
1374  appDataStruct.pointerArea = 'insideSecondaryAxis';
1375 
1376 elseif pointerPositionOnFigureFrame(1)>=magnifierPosition(1) &&...
1377  pointerPositionOnFigureFrame(1)<=magnifierPosition(1)+magnifierPosition(3) &&...
1378  pointerPositionOnFigureFrame(2)>=magnifierPosition(2) &&...
1379  pointerPositionOnFigureFrame(2)<=magnifierPosition(2)+magnifierPosition(4)
1380  %Pointer inside magnifier
1381  set(appDataStruct.figureHandle, 'Pointer', 'fleur');
1382 
1383  appDataStruct.pointerArea = 'insideMagnifier';
1384 
1385 else
1386  %Otherwise
1387  set(appDataStruct.figureHandle, 'Pointer', 'arrow');
1388 
1389  appDataStruct.pointerArea = 'none';
1390 end
1391 
1392 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1393 % NAME: getFigurePositionInPixels
1394 %
1395 % PURPOSE: obtain the position and size of the figure, relative to the
1396 % lower left corner of the screen, in pixels.
1397 %
1398 % INPUT ARGUMENTS:
1399 % none
1400 % OUTPUT ARGUMENTS:
1401 % position [double 1x4]:
1402 % X of lower left corner of the figure frame
1403 % Y of lower left corner of the figure frame
1404 % Width of the figure frame
1405 % Height of the figure frame
1406 %
1407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1408 function position = getFigurePositionInPixels()
1409 
1410 global appDataStruct
1411 
1412 if isempty(appDataStruct)
1413  return;
1414 end
1415 
1416 defaultUnits = get(appDataStruct.figureHandle,'Units');
1417 set(appDataStruct.figureHandle,'Units', 'pixels');
1418 position = get(appDataStruct.figureHandle,'Position'); %pixels [ low bottom width height]
1419 set(appDataStruct.figureHandle,'Units', defaultUnits);
1420 
1421 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1422 % NAME: getMainAxesPositionInPixels
1423 %
1424 % PURPOSE: obtain the position and size of the main axes, relative to the
1425 % lower left corner of the figure, in pixels. This fucntion locates the
1426 % lower-left corner of the displayed axes, accounting for all conditions of
1427 % DataAspectRatio and PlotBoxAspectRatio.
1428 %
1429 % INPUT ARGUMENTS:
1430 % none
1431 % OUTPUT ARGUMENTS:
1432 % position [double 1x4]:
1433 % X of lower left corner of the axis frame
1434 % Y of lower left corner of the axis frame
1435 % Width of the axis frame
1436 % Height of the axis frame
1437 %
1438 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1439 function position = getMainAxesPositionInPixels()
1440 
1441 global appDataStruct
1442 
1443 if isempty(appDataStruct)
1444  position = [];
1445  return;
1446 end
1447 
1448 %Characterize mainAxes in axes units
1449 mainAxisXLim = get( appDataStruct.mainAxesHandle, 'XLim' );
1450 mainAxisYLim = get( appDataStruct.mainAxesHandle, 'YLim' );
1451 spanX = determineSpan(mainAxisXLim(1), mainAxisXLim(2) );
1452 spanY = determineSpan(mainAxisYLim(1), mainAxisYLim(2));
1453 
1454 %Capture default units of mainAxes, and fix units to 'pixels'
1455 defaultUnits = get(appDataStruct.mainAxesHandle,'Units');
1456 set(appDataStruct.mainAxesHandle, 'Units', 'pixels');
1457 
1458 %Obtain values in 'pixels'
1459 mainAxesPosition = get(appDataStruct.mainAxesHandle, 'Position');
1460 dataAspectRatioMode = get(appDataStruct.mainAxesHandle, 'DataAspectRatioMode');
1461 dataAspectRatio = get(appDataStruct.mainAxesHandle, 'DataAspectRatio');
1462 plotBoxAspectRatioMode = get(appDataStruct.mainAxesHandle, 'PlotBoxAspectRatioMode');
1463 plotBoxAspectRatio = get(appDataStruct.mainAxesHandle, 'PlotBoxAspectRatio');
1464 
1465 %Determine correction values
1466 dataAspectRatioLimits = (spanX/dataAspectRatio(1))/(spanY/dataAspectRatio(2));
1467 plotBoxAspectRatioRelation = plotBoxAspectRatio(1)/plotBoxAspectRatio(2);
1468 mainAxesRatio = mainAxesPosition(3)/mainAxesPosition(4);
1469 
1470 %Id DataAspectRatio to auto and PlotBoxAspectRatio to auto
1471 if ~strcmpi( dataAspectRatioMode, 'manual') && ~strcmpi( plotBoxAspectRatioMode, 'manual')
1472 
1473  %Recover default units of mainAxes
1474  set(appDataStruct.mainAxesHandle,'Units', defaultUnits);
1475 
1476  %Obtain 'real' position from a temporal axes
1477  temporalAxes = axes('Visible', 'off');
1478  set(temporalAxes, 'Units', 'pixels');
1479  set(temporalAxes, 'Position', mainAxesPosition);
1480  position = get(temporalAxes, 'Position');
1481  delete(temporalAxes);
1482 
1483  return;
1484 end
1485 
1486 %If DataAspectRatio to manual
1487 if strcmpi( dataAspectRatioMode, 'manual')
1488  if dataAspectRatioLimits <= mainAxesRatio
1489  position(4) = mainAxesPosition(4);
1490  position(3) = mainAxesPosition(4) * dataAspectRatioLimits;
1491  position(2) = mainAxesPosition(2);
1492  position(1) = mainAxesPosition(1) + (mainAxesPosition(3) - position(3))/2;
1493 
1494  else
1495  position(1) = mainAxesPosition(1);
1496  position(3) = mainAxesPosition(3);
1497  position(4) = mainAxesPosition(3)/dataAspectRatioLimits;
1498  position(2) = mainAxesPosition(2) + (mainAxesPosition(4) - position(4))/2;
1499 
1500  end
1501 elseif strcmpi( plotBoxAspectRatioMode, 'manual')
1502  % Or PlotBoxAspectRatio to manual
1503  if plotBoxAspectRatioRelation <= mainAxesRatio
1504  position(4) = mainAxesPosition(4);
1505  position(3) = mainAxesPosition(4) * plotBoxAspectRatioRelation;
1506  position(2) = mainAxesPosition(2);
1507  position(1) = mainAxesPosition(1) + (mainAxesPosition(3) - position(3))/2;
1508 
1509  else
1510  position(1) = mainAxesPosition(1);
1511  position(3) = mainAxesPosition(3);
1512  position(4) = mainAxesPosition(3)/plotBoxAspectRatioRelation;
1513  position(2) = mainAxesPosition(2) + (mainAxesPosition(4) - position(4))/2;
1514 
1515  end
1516 end
1517 
1518 %Recover default units of mainAxes
1519 set(appDataStruct.mainAxesHandle, 'Units', defaultUnits);
1520 
1521 %Obtain 'real' position from a temporal axes
1522 temporalAxes = axes('Visible', 'off');
1523 set(temporalAxes, 'Units', 'pixels');
1524 set(temporalAxes, 'Position', position );
1525 position = get(temporalAxes, 'Position');
1526 delete(temporalAxes);
1527 
1528 
1529 
1530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1531 % NAME: getMagnifierPositionInPixels
1532 %
1533 % PURPOSE: obtain the position (of the lower left corner) and size of the
1534 % magnifier, relative to the lower left corner of the figure, in pixels.
1535 %
1536 % INPUT ARGUMENTS:
1537 % none
1538 % OUTPUT ARGUMENTS:
1539 % position [double 1x4]:
1540 % X of lower left corner of the magnifier
1541 % Y of lower left corner of the magnifier
1542 % Width of the magnifier
1543 % Height of the magnifier
1544 %
1545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1546 function position = getMagnifierPositionInPixels()
1547 
1548 global appDataStruct
1549 
1550 if isempty(appDataStruct)
1551  return;
1552 end;
1553 
1554 defaultUnits = get(appDataStruct.magnifierHandle, 'Units');
1555 set(appDataStruct.magnifierHandle, 'Units', 'pixels');
1556 position = get(appDataStruct.magnifierHandle, 'Position');
1557 set(appDataStruct.magnifierHandle, 'Units', defaultUnits );
1558 
1559 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1560 % NAME: getSecondaryAxesPositionInPixels
1561 %
1562 % PURPOSE: obtain the position and size of the secondary axis, relative to the
1563 % lower left corner of the figure, in pixels. Includes legends and axes
1564 % numbering
1565 %
1566 % INPUT ARGUMENTS:
1567 % none
1568 % OUTPUT ARGUMENTS:
1569 % position [double 1x4]:
1570 % X of lower left corner of the axis frame
1571 % Y of lower left corner of the axis frame
1572 % Width of the axis frame
1573 % Height of the axis frame
1574 %
1575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1576 function position = getSecondaryAxesPositionInPixels()
1577 
1578 global appDataStruct
1579 
1580 if isempty(appDataStruct)
1581  return;
1582 end
1583 
1584 defaultUnits = get(appDataStruct.secondaryAxesHandle,'Units');
1585 set(appDataStruct.secondaryAxesHandle,'Units', 'pixels');
1586 position = get(appDataStruct.secondaryAxesHandle,'Position'); %[ left bottom width height]
1587 set(appDataStruct.secondaryAxesHandle,'Units', defaultUnits);
1588 
1589 
1590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1591 % NAME: setSecondaryAxesPositionInPixels
1592 %
1593 % PURPOSE: fix the position and size of the secondary axis, relative to the
1594 % lower left corner of the figure, in pixels.
1595 %
1596 % INPUT ARGUMENTS:
1597 % position [double 1x4]:
1598 % X of lower left corner of the axis frame
1599 % Y of lower left corner of the axis frame
1600 % Width of the axis frame
1601 % Height of the axis frame
1602 % OUTPUT ARGUMENTS:
1603 % none
1604 %
1605 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1606 function setSecondaryAxesPositionInPixels( position )
1607 
1608 global appDataStruct
1609 
1610 if isempty(appDataStruct)
1611  return;
1612 end
1613 
1614 %Get position of secondary axes
1615 defaultUnits = get(appDataStruct.secondaryAxesHandle,'Units');
1616 set(appDataStruct.secondaryAxesHandle, 'Units', 'pixels');
1617 set( appDataStruct.secondaryAxesHandle,...
1618  'Position', [...
1619  position(1),...
1620  position(2),...
1621  position(3),...
1622  position(4)...
1623  ]...
1624  );
1625 % tightInset = get( appDataStruct.secondaryAxes.handle, 'TightInset' );
1626 set(appDataStruct.secondaryAxesHandle,'Units', defaultUnits);
1627 
1628 %Update appDataStruct
1629 appDataStruct.secondaryAxesPosition = getSecondaryAxesPositionInPixels();
1630 
1631 %Update 'userdata'
1632 toolArray = get(appDataStruct.figureHandle, 'userdata');
1633 if ~isempty(toolArray)
1634  focusedTool = find([toolArray.focusOnThisTool] == 1);
1635  toolArray(focusedTool).secondaryAxesPosition = appDataStruct.secondaryAxesPosition;
1636  set(appDataStruct.figureHandle, 'userdata', toolArray);
1637 end
1638 
1639 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1640 % NAME: setMagnifierPositionInPixels
1641 %
1642 % PURPOSE: fix the position and size of the magnifier, relative to the
1643 % lower left corner of the figure, in pixels.
1644 %
1645 % INPUT ARGUMENTS:
1646 % position [double 1x4]:
1647 % X of lower left corner of the magnifier
1648 % Y of lower left corner of the magnifier
1649 % Width of the magnifier frame
1650 % Height of the magnifier frame
1651 % OUTPUT ARGUMENTS:
1652 % none
1653 %
1654 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1655 function setMagnifierPositionInPixels( position )
1656 
1657 
1658 global appDataStruct
1659 
1660 if isempty(appDataStruct)
1661  return;
1662 end
1663 
1664 %Limit position of magnifier within the main axes
1665 mainAxesPosition = getMainAxesPositionInPixels();
1666 if position(1)<mainAxesPosition(1)
1667  position(1) = mainAxesPosition(1);
1668 end
1669 if position(1)+position(3)>mainAxesPosition(1)+mainAxesPosition(3)
1670  position(1) = mainAxesPosition(1)+mainAxesPosition(3)-position(3);
1671 end
1672 if position(2)<mainAxesPosition(2)
1673  position(2) = mainAxesPosition(2);
1674 end
1675 if position(2)+position(4)>mainAxesPosition(2)+mainAxesPosition(4)
1676  position(2) = mainAxesPosition(2)+mainAxesPosition(4)-position(4);
1677 end
1678 
1679 %Create of set magnifier
1680 if isempty(appDataStruct.magnifierHandle)
1681 
1682  if strcmpi(appDataStruct.magnifierShape, 'rectangle')
1683  appDataStruct.magnifierHandle = ...
1684  annotation( 'rectangle',...
1685  'Units', 'pixels',...
1686  'Position', position,...
1687  'LineWidth', appDataStruct.globalEdgeWidth,...
1688  'LineStyle','-',...
1689  'EdgeColor', appDataStruct.globalEdgeColor...
1690  );
1691  end
1692  if strcmpi(appDataStruct.magnifierShape, 'ellipse')
1693  appDataStruct.magnifierHandle = ...
1694  annotation( 'ellipse',...
1695  'Units', 'pixels',...
1696  'Position', position,...
1697  'LineWidth', appDataStruct.globalEdgeWidth,...
1698  'LineStyle','-',...
1699  'EdgeColor', appDataStruct.globalEdgeColor...
1700  );
1701  end
1702 
1703 else
1704  set( appDataStruct.magnifierHandle,...
1705  'Position', position,...
1706  'LineWidth', appDataStruct.globalEdgeWidth,...
1707  'EdgeColor', appDataStruct.globalEdgeColor );
1708 end
1709 
1710 %Update appDataStruct
1711 appDataStruct.magnifierPosition = getMagnifierPositionInPixels();
1712 
1713 %Update 'userdata'
1714 toolArray = get(appDataStruct.figureHandle, 'userdata');
1715 if ~isempty(toolArray)
1716  focusedTool = find([toolArray.focusOnThisTool] == 1);
1717  toolArray(focusedTool).magnifierPosition = appDataStruct.magnifierPosition;
1718  set(appDataStruct.figureHandle, 'userdata', toolArray);
1719 end
1720 
1721 
1722 
1723 
1724 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1725 % NAME: refreshMagnifierToSecondaryAxesLink
1726 %
1727 % PURPOSE: Updates the line connection between the magnifier and the secondary axes.
1728 %
1729 % INPUT ARGUMENTS:
1730 %
1731 % OUTPUT ARGUMENTS:
1732 %
1733 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1734 function refreshMagnifierToSecondaryAxesLink()
1735 
1736 global appDataStruct;
1737 
1738 if isempty(appDataStruct)
1739  return;
1740 end
1741 
1742 %Don't display link if not requestred
1743 linkStyle = appDataStruct.linkDisplayStyle;
1744 if strcmpi( linkStyle(1), 'none')
1745  return;
1746 end
1747 
1748 %Get position and size of figure in pixels
1749 figurePosition = getFigurePositionInPixels();
1750 
1751 %Get position and size of secondary axes in pixels
1752 secondaryAxesPosition = getSecondaryAxesPositionInPixels();
1753 
1754 defaultUnits = get(appDataStruct.secondaryAxesHandle, 'Units');
1755 set(appDataStruct.secondaryAxesHandle, 'Units', 'pixels');
1756 tightInset = get(appDataStruct.secondaryAxesHandle, 'TightInset');
1757 set(appDataStruct.secondaryAxesHandle, 'Units', defaultUnits);
1758 
1759 %Get position and size of secondary axes in pixels
1760 magnifierPosition = getMagnifierPositionInPixels();
1761 
1762 
1763 if strcmpi( linkStyle, 'straight')
1764 
1765  %Magnifier Hot points
1766  magnifierHotPoints = [...
1767  magnifierPosition(1) + magnifierPosition(3)/2 magnifierPosition(2);...
1768  magnifierPosition(1) + magnifierPosition(3) magnifierPosition(2)+magnifierPosition(4)/2;...
1769  magnifierPosition(1) + magnifierPosition(3)/2 magnifierPosition(2)+magnifierPosition(4);...
1770  magnifierPosition(1) magnifierPosition(2)+magnifierPosition(4)/2;...
1771  ];
1772 
1773  %Secondary axes Hot points
1774  secondaryAxesHotPoints = [...
1775  secondaryAxesPosition(1) + secondaryAxesPosition(3)/2 secondaryAxesPosition(2) - tightInset(2) - 2;...
1776  secondaryAxesPosition(1) + secondaryAxesPosition(3) secondaryAxesPosition(2)+secondaryAxesPosition(4)/2;...
1777  secondaryAxesPosition(1) + secondaryAxesPosition(3)/2 secondaryAxesPosition(2)+secondaryAxesPosition(4);...
1778  secondaryAxesPosition(1) - tightInset(1) - 2 secondaryAxesPosition(2)+secondaryAxesPosition(4)/2;...
1779  ];
1780 
1781  %Minimize distance between hot spots
1782  L1 = size(magnifierHotPoints, 1);
1783  L2 = size(secondaryAxesHotPoints, 1);
1784  [iMagnifierHotPoints iSecondaryAxesHotPoints] = meshgrid(1:L1, 1:L2);
1785  D2 = ( magnifierHotPoints(iMagnifierHotPoints(:),1) - secondaryAxesHotPoints(iSecondaryAxesHotPoints(:),1) ).^2 + ...
1786  ( magnifierHotPoints(iMagnifierHotPoints(:),2) - secondaryAxesHotPoints(iSecondaryAxesHotPoints(:),2) ).^2;
1787 
1788  [C,I] = sort( D2, 'ascend' );
1789 
1790  X(1) = magnifierHotPoints(iMagnifierHotPoints(I(1)),1);
1791  Y(1) = magnifierHotPoints(iMagnifierHotPoints(I(1)),2);
1792  X(2) = secondaryAxesHotPoints(iSecondaryAxesHotPoints(I(1)),1);
1793  Y(2) = secondaryAxesHotPoints(iSecondaryAxesHotPoints(I(1)),2);
1794 
1795  %Plot/update line
1796  if isempty(appDataStruct.linkHandle)
1797 
1798  appDataStruct.linkHandle = annotation( 'line', X/figurePosition(3), Y/figurePosition(4),...
1799  'LineWidth', appDataStruct.globalEdgeWidth,...
1800  'Color', appDataStruct.globalEdgeColor );
1801 
1802  else
1803 
1804  set(appDataStruct.linkHandle, 'X', X/figurePosition(3), 'Y', Y/figurePosition(4),...
1805  'LineWidth', appDataStruct.globalEdgeWidth,...
1806  'Color', appDataStruct.globalEdgeColor );
1807 
1808  end
1809 end
1810 
1811 if strcmpi( linkStyle, 'edges')
1812  %Magnifier Hot points
1813  magnifierHotPoints = [...
1814  magnifierPosition(1) - 3 magnifierPosition(2);...
1815  magnifierPosition(1) + magnifierPosition(3) magnifierPosition(2);...
1816  magnifierPosition(1) + magnifierPosition(3) magnifierPosition(2)+magnifierPosition(4);...
1817  magnifierPosition(1) - 3 magnifierPosition(2)+magnifierPosition(4)...
1818  ];
1819 
1820  %Secondary axes Hot points
1821  secondaryAxesHotPoints = [...
1822  secondaryAxesPosition(1) secondaryAxesPosition(2);...
1823  secondaryAxesPosition(1) + secondaryAxesPosition(3) secondaryAxesPosition(2);...
1824  secondaryAxesPosition(1) + secondaryAxesPosition(3) secondaryAxesPosition(2)+secondaryAxesPosition(4);...
1825  secondaryAxesPosition(1) secondaryAxesPosition(2)+secondaryAxesPosition(4)...
1826  ];
1827 
1828 
1829  for i=1:4
1830  X(1) = magnifierHotPoints(i,1);
1831  Y(1) = magnifierHotPoints(i,2);
1832  X(2) = secondaryAxesHotPoints(i,1);
1833  Y(2) = secondaryAxesHotPoints(i,2);
1834 
1835  %If intersection with secondary Axes bottom edge
1836 % intersectionPoint = intersectionPointInPixels(...
1837 % [X(1) Y(1) X(2) Y(2)], ...
1838 % [ secondaryAxesPosition(1)...
1839 % secondaryAxesPosition(2)...
1840 % secondaryAxesPosition(1)+secondaryAxesPosition(3)...
1841 % secondaryAxesPosition(2) ]...
1842 % );
1843 % if ~isempty(intersectionPoint)
1844 % D2_1 = (X(1)-X(2))^2 + (Y(1)-Y(2))^2;
1845 % D2_2 = (X(1)-intersectionPoint(1))^2 + (Y(1)-intersectionPoint(2))^2;
1846 % if D2_2<D2_1
1847 % %link to intersecting point
1848 % X(2) = intersectionPoint(1);
1849 % Y(2) = intersectionPoint(2);
1850 % end
1851 % end
1852 %
1853 % %If intersection with secondary Axes top edge
1854 % intersectionPoint = intersectionPointInPixels(...
1855 % [X(1) Y(1) X(2) Y(2)], ...
1856 % [ secondaryAxesPosition(1)...
1857 % secondaryAxesPosition(2)+secondaryAxesPosition(4)...
1858 % secondaryAxesPosition(1)+secondaryAxesPosition(3)...
1859 % secondaryAxesPosition(2)+secondaryAxesPosition(4) ]...
1860 % );
1861 % if ~isempty(intersectionPoint)
1862 % D2_1 = (X(1)-X(2))^2 + (Y(1)-Y(2))^2;
1863 % D2_2 = (X(1)-intersectionPoint(1))^2 + (Y(1)-intersectionPoint(2))^2;
1864 % if D2_2<D2_1
1865 % %link to intersecting point
1866 % X(2) = intersectionPoint(1);
1867 % Y(2) = intersectionPoint(2);
1868 % end
1869 % end
1870 %
1871 % %If intersection with secondary Axes left edge
1872 % intersectionPoint = intersectionPointInPixels(...
1873 % [X(1) Y(1) X(2) Y(2)], ...
1874 % [ secondaryAxesPosition(1)...
1875 % secondaryAxesPosition(2)...
1876 % secondaryAxesPosition(1)...
1877 % secondaryAxesPosition(2)+secondaryAxesPosition(4) ]...
1878 % );
1879 % if ~isempty(intersectionPoint)
1880 % D2_1 = (X(1)-X(2))^2 + (Y(1)-Y(2))^2;
1881 % D2_2 = (X(1)-intersectionPoint(1))^2 + (Y(1)-intersectionPoint(2))^2;
1882 % if D2_2<D2_1
1883 % %link to intersecting point
1884 % X(2) = intersectionPoint(1);
1885 % Y(2) = intersectionPoint(2);
1886 % end
1887 % end
1888 %
1889 % %If intersection with secondary Axes right edge
1890 % intersectionPoint = intersectionPointInPixels(...
1891 % [X(1) Y(1) X(2) Y(2)], ...
1892 % [ secondaryAxesPosition(1)+secondaryAxesPosition(3)...
1893 % secondaryAxesPosition(2)...
1894 % secondaryAxesPosition(1)+secondaryAxesPosition(3)...
1895 % secondaryAxesPosition(2)+secondaryAxesPosition(4) ]...
1896 % );
1897 % if ~isempty(intersectionPoint)
1898 % D2_1 = (X(1)-X(2))^2 + (Y(1)-Y(2))^2;
1899 % D2_2 = (X(1)-intersectionPoint(1))^2 + (Y(1)-intersectionPoint(2))^2;
1900 % if D2_2<D2_1
1901 % %link to intersecting point
1902 % X(2) = intersectionPoint(1);
1903 % Y(2) = intersectionPoint(2);
1904 % end
1905 % end
1906 
1907  %Plot/update line
1908  if isempty( appDataStruct.linkHandle )
1909 
1910  newlinkHandle = annotation( 'line', X/figurePosition(3), Y/figurePosition(4),...
1911  'LineWidth', appDataStruct.globalEdgeWidth,...
1912  'LineStyle', ':',...
1913  'Color', appDataStruct.globalEdgeColor );
1914  linkHandle = appDataStruct.linkHandle;
1915  appDataStruct.linkHandle = [linkHandle newlinkHandle];
1916 
1917  else
1918  linkHandle = appDataStruct.linkhandle;
1919  set( linkHandle(i), 'X', X/figurePosition(3), 'Y', Y/figurePosition(4),...
1920  'LineWidth', appDataStruct.globalEdgeWidth,...
1921  'Color', appDataStruct.globalEdgeColor );
1922  end
1923 
1924  end
1925 end
1926 
1927 if strcmpi( linkStyle, 'elbow')
1928 
1929  %Magnifier Hot points
1930  magnifierHotPoints = [...
1931  magnifierPosition(1) + magnifierPosition(3)/2 magnifierPosition(2);...
1932  magnifierPosition(1) + magnifierPosition(3) magnifierPosition(2)+magnifierPosition(4)/2;...
1933  magnifierPosition(1) + magnifierPosition(3)/2 magnifierPosition(2)+magnifierPosition(4);...
1934  magnifierPosition(1) magnifierPosition(2)+magnifierPosition(4)/2;...
1935  ];
1936 
1937  %Secondary axes Hot points
1938  secondaryAxesHotPoints = [...
1939  secondaryAxesPosition(1) + secondaryAxesPosition(3)/2 secondaryAxesPosition(2) - tightInset(2) - 2;...
1940  secondaryAxesPosition(1) + secondaryAxesPosition(3) secondaryAxesPosition(2)+secondaryAxesPosition(4)/2;...
1941  secondaryAxesPosition(1) + secondaryAxesPosition(3)/2 secondaryAxesPosition(2)+secondaryAxesPosition(4);...
1942  secondaryAxesPosition(1) - tightInset(1) - 2 secondaryAxesPosition(2)+secondaryAxesPosition(4)/2;...
1943  ];
1944 
1945 
1946  %Allowed connections
1947 % iMagnifierHotPoints(1) = 1;
1948 % iSecondaryAxesHotPoints(1) = 4;
1949 % iMagnifierHotPoints(2) = 1;
1950 % iSecondaryAxesHotPoints(2) = 2;
1951 % iMagnifierHotPoints(3) = 2;
1952 % iSecondaryAxesHotPoints(3) = 3;
1953 % iMagnifierHotPoints(4) = 2;
1954 % iSecondaryAxesHotPoints(4) = 1;
1955 % iMagnifierHotPoints(5) = 3;
1956 % iSecondaryAxesHotPoints(5) = 4;
1957 % iMagnifierHotPoints(6) = 3;
1958 % iSecondaryAxesHotPoints(6) = 2;
1959 % iMagnifierHotPoints(7) = 4;
1960 % iSecondaryAxesHotPoints(7) = 1;
1961 % iMagnifierHotPoints(8) = 4;
1962 % iSecondaryAxesHotPoints(8) = 3;
1963 % iMagnifierHotPoints(9) = 1;
1964 % iSecondaryAxesHotPoints(9) = 3;
1965 % iMagnifierHotPoints(10) = 2;
1966 % iSecondaryAxesHotPoints(10) = 4;
1967 % iMagnifierHotPoints(11) = 3;
1968 % iSecondaryAxesHotPoints(11) = 1;
1969 % iMagnifierHotPoints(12) = 4;
1970 % iSecondaryAxesHotPoints(12) = 2;
1971 
1972  %Minimize distance between hot spots
1973  L1 = size(magnifierHotPoints, 1);
1974  L2 = size(secondaryAxesHotPoints, 1);
1975  [iMagnifierHotPoints iSecondaryAxesHotPoints] = meshgrid(1:L1, 1:L2);
1976  D2 = ( magnifierHotPoints(iMagnifierHotPoints(:),1) - secondaryAxesHotPoints(iSecondaryAxesHotPoints(:),1) ).^2 + ...
1977  ( magnifierHotPoints(iMagnifierHotPoints(:),2) - secondaryAxesHotPoints(iSecondaryAxesHotPoints(:),2) ).^2;
1978 
1979  [C,I] = sort( D2, 'ascend' );
1980 
1981  X(1) = magnifierHotPoints(iMagnifierHotPoints(I(1)),1);
1982  Y(1) = magnifierHotPoints(iMagnifierHotPoints(I(1)),2);
1983  X(2) = secondaryAxesHotPoints(iSecondaryAxesHotPoints(I(1)),1);
1984  Y(2) = secondaryAxesHotPoints(iSecondaryAxesHotPoints(I(1)),2);
1985 
1986  %Plot/update line
1987  if isempty( appDataStruct.linkHandle )
1988 
1989  newlinkHandle = annotation( 'line', X/figurePosition(3), Y/figurePosition(4),...
1990  'LineWidth', appDataStruct.globalEdgeWidth,...
1991  'Color', appDataStruct.globalEdgeColor );
1992  linkHandle = appDataStruct.linkHandle;
1993  appDataStruct.linkHandle = [linkHandle newlinkHandle];
1994  else
1995 
1996  linkHandle = appDataStruct.linkhandle;
1997  set( linkHandle, 'X', X/figurePosition(3), 'Y', Y/figurePosition(4),...
1998  'LineWidth', appDataStruct.globalEdgeWidth,...
1999  'Color', appDataStruct.globalEdgeColor );
2000 
2001  end
2002 end
2003 
2004 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2005 % NAME: intersectionPointInPixels
2006 %
2007 % PURPOSE: Computes constrained intersection of two lines in pixels, on the 2D space
2008 %
2009 % INPUT ARGUMENTS:
2010 % line1 [double 1x4]: [Xstart Ystart Xend Yend]
2011 % line2 [double 1x4]: [Xstart Ystart Xend Yend]
2012 %
2013 % OUTPUT ARGUMENTS:
2014 % intersectionPont [double 1x2]: [X Y] intersection
2015 %
2016 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2017 function intersectionPoint = intersectionPointInPixels( line1, line2)
2018 
2019 %Cartessian caracterization of line 1
2020 X1(1) = line1(1);
2021 Y1(1) = line1(2);
2022 X1(2) = line1(3);
2023 Y1(2) = line1(4);
2024 
2025 a1 = (Y1(2) - Y1(1)) / (X1(2) - X1(1));
2026 b1 = Y1(1) - X1(1)*a1;
2027 
2028 %Cartessian caracterization of line 2
2029 X2(1) = line2(1);
2030 Y2(1) = line2(2);
2031 X2(2) = line2(3);
2032 Y2(2) = line2(4);
2033 
2034 a2 = (Y2(2) - Y2(1)) / (X2(2) - X2(1));
2035 b2 = Y2(1) - X2(1)*a2;
2036 
2037 %Intersection
2038 if isfinite(a1) && isfinite(a2)
2039  intersectionPoint(1) = (b2-b1) / (a1-a2);
2040  intersectionPoint(2) = intersectionPoint(1)*a1 + b1;
2041 end
2042 %Pathologic case 1 (line2 x=constant)
2043 if isfinite(a1) && ~isfinite(a2)
2044  intersectionPoint(1) = X2(1);
2045  intersectionPoint(2) = intersectionPoint(1)*a1 + b1;
2046 end
2047 %Pathologic case 2 (line1 x=constant)
2048 if ~isfinite(a1) && isfinite(a2)
2049  intersectionPoint(1) = X1(1);
2050  intersectionPoint(2) = intersectionPoint(1)*a2 + b2;
2051 end
2052 
2053 if intersectionPoint(1)<min([X1(1) X2(1)]) ||...
2054  intersectionPoint(1)>max([X1(2) X2(2)]) ||...
2055  intersectionPoint(2)<min([Y1(1) Y2(1)]) ||...
2056  intersectionPoint(2)>max([Y1(2) Y2(2)])
2057 
2058  intersectionPoint = [];
2059 end
2060 
2061 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2062 % NAME: computeSecondaryAxesDefaultPosition
2063 %
2064 % PURPOSE: obtain the default position and size of the secondary axis, relative to the
2065 % lower left corner of the figure, in pixels. Includes legends and axes
2066 % numbering
2067 %
2068 % INPUT ARGUMENTS:
2069 % none
2070 % OUTPUT ARGUMENTS:
2071 % position [double 1x4]:
2072 % X of lower left corner of the axis frame
2073 % Y of lower left corner of the axis frame
2074 % Width of the axis frame
2075 % Height of the axis frame
2076 %
2077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2078 function defaultPosition = computeSecondaryAxesDefaultPosition()
2079 
2080 global appDataStruct
2081 
2082 if isempty(appDataStruct)
2083  return;
2084 end;
2085 
2086 % If image, defualt aspect ratio of magnifier and secondary axes to [1 1]
2087 childHandle = get(appDataStruct.mainAxesHandle, 'Children');
2088 plotFlag = ~isempty( find(strcmpi(get(childHandle, 'Type'), 'line'),1) );
2089 imageFlag = ~isempty( find(strcmpi(get(childHandle, 'Type'), 'image'),1) );
2090 
2091 %Get position and size of main Axis (left & bottom relative to figure frame)
2092 mainAxesPosition = getMainAxesPositionInPixels();
2093 
2094 if plotFlag
2095  %Set initial position and size for secondary axis
2096  secondaryAxisPosition_W = mainAxesPosition(3)*0.3;
2097  secondaryAxisPosition_H = mainAxesPosition(4)*0.3;
2098  secondaryAxisPosition_X = mainAxesPosition(1)+mainAxesPosition(3)-secondaryAxisPosition_W-10;
2099  secondaryAxisPosition_Y = mainAxesPosition(2)+mainAxesPosition(4)-secondaryAxisPosition_H-10;
2100 else
2101  %Set initial position and size for secondary axis
2102  secondaryAxisPosition_W = mainAxesPosition(3)*0.3;
2103  secondaryAxisPosition_H = mainAxesPosition(4)*0.3;
2104  secondaryAxisPosition_X = mainAxesPosition(1)+mainAxesPosition(3)-secondaryAxisPosition_W-10;
2105  secondaryAxisPosition_Y = mainAxesPosition(2)+mainAxesPosition(4)-secondaryAxisPosition_H-10;
2106 end
2107 
2108 defaultPosition = [...
2109  secondaryAxisPosition_X...
2110  secondaryAxisPosition_Y...
2111  secondaryAxisPosition_W...
2112  secondaryAxisPosition_H...
2113  ];
2114 
2115 
2116 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2117 % NAME: computeMagnifierDefaultPosition
2118 %
2119 % PURPOSE: obtain the default position and size of the magnifier, relative to the
2120 % lower left corner of the figure, in pixels. Includes legends and axes
2121 % numbering
2122 %
2123 % INPUT ARGUMENTS:
2124 % none
2125 % OUTPUT ARGUMENTS:
2126 % position [double 1x4]:
2127 % X of lower left corner of the rectangle
2128 % Y of lower left corner of the rectangle
2129 % Width of the rectangle
2130 % Height of the rectangle
2131 %
2132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2133 function defaultPosition = computeMagnifierDefaultPosition()
2134 
2135 global appDataStruct
2136 
2137 if isempty(appDataStruct)
2138  return;
2139 end;
2140 
2141 % If image, defualt aspect ratio of magnifier and secondary axes to [1 1]
2142 childHandle = get(appDataStruct.mainAxesHandle, 'Children');
2143 plotFlag = ~isempty( find(strcmpi(get(childHandle, 'Type'), 'line'),1) );
2144 imageFlag = ~isempty( find(strcmpi(get(childHandle, 'Type'), 'image'),1) );
2145 
2146 %Set initial position and size of magnifying rectangle
2147 mainAxisXLim = get( appDataStruct.mainAxesHandle, 'XLim' );
2148 mainAxisYLim = get( appDataStruct.mainAxesHandle, 'YLim' );
2149 mainAxesPositionInPixels = getMainAxesPositionInPixels();
2150 xMainAxisUnits2PixelsFactor = mainAxesPositionInPixels(3)/determineSpan( mainAxisXLim(1), mainAxisXLim(2) );
2151 yMainAxisUnits2PixelsFactor = mainAxesPositionInPixels(4)/determineSpan( mainAxisYLim(1), mainAxisYLim(2) );
2152 
2153 if plotFlag
2154  %Get main axis position and dimensions, in pixels
2155  magnifierPosition_W = determineSpan(mainAxisXLim(1), mainAxisXLim(2))*xMainAxisUnits2PixelsFactor*0.1;
2156  magnifierPosition_H = determineSpan(mainAxisYLim(1), mainAxisYLim(2))*yMainAxisUnits2PixelsFactor*0.3;
2157  magnifierPosition_X = determineSpan(mean(mainAxisXLim), mainAxisXLim(1))*xMainAxisUnits2PixelsFactor - magnifierPosition_W/2;
2158  magnifierPosition_Y = determineSpan(mean(mainAxisYLim), mainAxisYLim(1))*yMainAxisUnits2PixelsFactor - magnifierPosition_H/2;
2159 else
2160  %Get main axis position and dimensions, in pixels
2161  magnifierPosition_W = determineSpan(mainAxisXLim(1), mainAxisXLim(2))*xMainAxisUnits2PixelsFactor*0.1;
2162  magnifierPosition_H = determineSpan(mainAxisYLim(1), mainAxisYLim(2))*yMainAxisUnits2PixelsFactor*0.1;
2163  magnifierPosition_X = determineSpan(mean(mainAxisXLim), mainAxisXLim(1))*xMainAxisUnits2PixelsFactor - magnifierPosition_W/2;
2164  magnifierPosition_Y = determineSpan(mean(mainAxisYLim), mainAxisYLim(1))*yMainAxisUnits2PixelsFactor - magnifierPosition_H/2;
2165 end
2166 
2167 defaultPosition = [...
2168  magnifierPosition_X+mainAxesPositionInPixels(1)...
2169  magnifierPosition_Y+mainAxesPositionInPixels(2)...
2170  magnifierPosition_W...
2171  magnifierPosition_H...
2172  ];
2173 
2174 
2175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2176 % NAME: initializeToolStruct
2177 %
2178 % PURPOSE: Set value for default properties
2179 %
2180 % INPUT ARGUMENTS:
2181 % none
2182 % OUTPUT ARGUMENTS:
2183 % none
2184 %
2185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2186 function obj = initializeToolStruct(varargin)
2187 
2188  if nargin == 0
2189  obj.toolId = [];
2190  obj.focusOnThisTool = true;
2191  obj.toolIdHandle = [];
2192 
2193  %figure
2194  obj.figureHandle = [];
2195  obj.figurePosition = [];
2196  obj.figureOldWindowButtonDownFcn = [];
2197  obj.figureOldWindowButtonUpFcn = [];
2198  obj.figureOldWindowButtonMotionFcn = [];
2199  obj.figureOldKeyPressFcn = [];
2200  obj.figureOldDeleteFcn = [];
2201  obj.figureOldResizeFcn = [];
2202 
2203  %main axes
2204  obj.mainAxesHandle = [];
2205 
2206  %magnifier
2207  obj.magnifierHandle = [];
2208  obj.magnifierPosition = [];
2209  obj.magnifierShape = 'rectangle';
2210 
2211  %link
2212  obj.linkHandle = [];
2213  obj.linkDisplayStyle = 'straight';
2214 
2215  %secondary axes
2216  obj.secondaryAxesHandle = [];
2217  obj.secondaryAxesFaceColor = 'white';
2218  obj.secondaryAxesPosition = [];
2219  obj.secondaryAxesXLim = [];
2220  obj.secondaryAxesYLim = [];
2221  obj.secondaryAxesAdditionalZoomingFactor = [0 0];
2222 
2223  %global
2224  obj.globalUnits = 'pixels';
2225  obj.globalMode = 'interactive';
2226  obj.globalEdgeWidth = 1;
2227  obj.globalEdgeColor = 'black';
2228  obj.globalZoomMode = 'off';
2229 
2230  %Temp
2231  obj.pointerArea = 'none';
2232  obj.ButtonDown = false;
2233  obj.pointerPositionOnButtonDown = [];
2234  end
2235 
2236  if nargin == 1 && isstruct(varargin{1})
2237  structIn = varargin{1};
2238  if all(isfield(structIn, {'toolId','focusOnThisTool',...
2239  'figureHandle', 'figurePosition', 'figureOldWindowButtonDownFcn',...
2240  'figureOldWindowButtonUpFcn', 'figureOldWindowButtonMotionFcn',...
2241  'figureOldKeyPressFcn', 'figureOldDeleteFcn', 'figureOldResizeFcn',...
2242  'mainAxesHandle', 'magnifierHandle', 'magnifierPosition', ...
2243  'magnifierShape', 'linkHandle', 'linkDisplayStyle', 'secondaryAxesHandle',...
2244  'secondaryAxesFaceColor', 'secondaryAxesPosition', 'secondaryAxesXLim',...
2245  'secondaryAxesYLim', 'secondaryAxesAdditionalZoomingFactor', ...
2246  'globalUnits', 'globalMode', 'globalEdgeWidth', 'globalEdgeColor',...
2247  'globalZoomMode', 'pointerArea', 'ButtonDown', 'pointerPositionOnButtonDown'}))
2248 
2249  else
2250  error('Input structure not recognized');
2251  end
2252 
2253  obj = structIn;
2254 
2255  end
2256 
2257 
2258 
2259 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2260 % NAME: updateToolId
2261 %
2262 % PURPOSE: Updated position and status of tool id
2263 %
2264 % INPUT ARGUMENTS:
2265 % toolArray structure of the tool in focus
2266 % OUTPUT ARGUMENTS:
2267 % none
2268 %
2269 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2270 function toolArrayOut = updateToolId( toolArrayIn, toolNum, modeStr )
2271 
2272 toolArrayOut = toolArrayIn;
2273 
2274 set(toolArrayOut.figureHandle, 'currentAxes', toolArrayOut.secondaryAxesHandle);
2275 
2276 if strcmp(modeStr, 'toggle')
2277  if isempty(toolArrayOut.toolIdHandle)
2278  xL = get(toolArrayOut.secondaryAxesHandle, 'XLim');
2279  yL = get(toolArrayOut.secondaryAxesHandle, 'YLim');
2280  toolArrayOut.toolIdHandle = text( (xL(2)+xL(1))/2, (yL(2)+yL(1))/2, num2str(toolNum));
2281  set(toolArrayOut.toolIdHandle, 'FontSize', 30, 'FontWeight', 'bold' );
2282  if toolArrayOut.focusOnThisTool
2283  set(toolArrayOut.toolIdHandle,'BackgroundColor', 'red', 'Color', 'white');
2284  else
2285  set(toolArrayOut.toolIdHandle,'BackgroundColor', 'black', 'Color', 'white');
2286  end
2287  else
2288  delete(toolArrayOut.toolIdHandle);
2289  toolArrayOut.toolIdHandle = [];
2290  end
2291 else
2292  if not(isempty(toolArrayOut.toolIdHandle))
2293  xL = get(toolArrayOut.secondaryAxesHandle, 'XLim');
2294  yL = get(toolArrayOut.secondaryAxesHandle, 'YLim');
2295  set(toolArrayOut.toolIdHandle, 'Position', [(xL(2)+xL(1))/2, (yL(2)+yL(1))/2] );
2296  if toolArrayOut.focusOnThisTool
2297  set(toolArrayOut.toolIdHandle,'BackgroundColor', 'red', 'Color', 'white');
2298  else
2299  set(toolArrayOut.toolIdHandle,'BackgroundColor', 'black', 'Color', 'white');
2300  end
2301  end
2302 end
2303 
2304 set(toolArrayOut.figureHandle, 'currentAxes', toolArrayOut.mainAxesHandle);
2305 
2306