for ii=1:length(marker_color)
col_ind = strfind(colors,marker_color(ii));
if ~isempty(col_ind) %It's a color
color = marker_color(ii);
else %Try a marker instead
mark_ind = strfind(markers,marker_color(ii));
if ~isempty(mark_ind)
marker = marker_color(ii);
end;
end;
end;
end;
%Handle default marker and color
if ~exist('color','var'), color = 'r'; end; %set default
if ~exist('marker','var'), marker = '*'; end; %set default
%Add the cursors.
%Ideally, the user specified the 2 x coordinates.
%If not, just put the cursors somewhere nice.
lineh = local_findlines(axh);
%Line data
if isempty(lineh)
x1 = 0;
y1 = 0;
% erasemode = 'normal'; %default
else
%if multiple lines, define callback to allow for selection
set(lineh,'ButtonDownFcn', ...
'setappdata(get(gco,''Parent''),''SelectedLine'',gco);dualcursor(''selectline'',[],[],[],get(gco,''Parent''))');
%set erasemode to xor. This speeds things up a ton with large data sets
% erasemode = get(lineh,'EraseMode');
% set(lineh,'EraseMode','xor');
lineh = min(lineh); %Lets just use one line. This was probably added first
setappdata(axh,'SelectedLine',lineh); %The currently selected line.
%Why the last line? Because it is the first one added
xl = get(lineh,'XData');
yl = get(lineh,'YData');
end;
%Find nearest value on the line
[xv1,yv1] = local_nearest(x_init(1),xl,yl);
[xv2,yv2] = local_nearest(x_init(2),xl,yl);
%Build the string for the data label
textstring1 = feval(datalabelformatfcnh,xv1,yv1);
textstring2 = feval(datalabelformatfcnh,xv2,yv2);
%Add the data label
th1 = text(xv1,yv1,textstring1,'FontSize',8,'Tag','CursorText','Parent',axh);
th2 = text(xv2,yv2,textstring2,'FontSize',8,'Tag','CursorText','Parent',axh);
%For R13 or higher (MATLAB 6.5), use a background color on the text string
v=ver('MATLAB');
v=str2num(v.Version(1:3));
if v>=6.5
set(th1,'BackgroundColor','y');
set(th2,'BackgroundColor','y');
end;
yl = ylim(axh);
lim = localObjbounds(axh);
lim = lim(3:4); %y values only
yl(isinf(yl)) = lim(isinf(yl));
%Add the cursors
ph1 = line([xv1 xv1 xv1],[yl(1) yv1 yl(2)], ...
'Color',color, ...
'Marker',marker, ...
'Tag','Cursor', ...
'UserData',[lineh th1], ...
'LineStyle','-', ...
'Parent',axh);
ph2 = line([xv2 xv2 xv2],[yl(1) yv2 yl(2)], ...
'Color',color, ...
'Marker',marker, ...
'Tag','Cursor', ...
'UserData',[lineh th2], ...
'LineStyle','-', ...
'Parent',axh);
%Add context menu to the cursors
% cmenu = uicontextmenu('Parent',get(axh,'Parent'));
cmenu = uicontextmenu('Parent',hFig);
set([ph1 ph2],'UIContextMenu',cmenu);
% Define the context menu items
item1 = uimenu(cmenu, 'Label', 'Export region to workspace', ...
'Callback', 'dualcursor(''exportws'',[],[],[],get(gco,''Parent''))');
item2 = uimenu(cmenu, 'Label', 'Export region to new figure', ...
'Callback', 'dualcursor(''exportfig'',[],[],[],get(gco,''Parent''))');
item3 = uimenu(cmenu, 'Label', 'Export cursor data to workspace', ...
'Callback', 'dualcursor(''exportcursor'',[],[],[],get(gco,''Parent''));');
item4 = uimenu(cmenu, 'Label', 'Turn cursors off', ...
'Callback', 'dualcursor(''off'',[],[],[],get(gco,''Parent''))', ...
'Separator','on');
setappdata(th1,'Coordinates',[xv1 yv1]);
setappdata(ph1,'Coordinates',[xv1 yv1]);
setappdata(th2,'Coordinates',[xv2 yv2]);
setappdata(ph2,'Coordinates',[xv2 yv2]);
setappdata(th1,'CursorNumber',1);
setappdata(ph1,'CursorNumber',1);
setappdata(th2,'CursorNumber',2);
setappdata(ph2,'CursorNumber',2);
setappdata(th1,'FormatFcnH',datalabelformatfcnh);
setappdata(th2,'FormatFcnH',datalabelformatfcnh);
setappdata(th1,'Offset',[0 0]); %Offset for the text label from the data value
setappdata(th2,'Offset',[0 0]); %Offset for the text label from the data value
% mh = uicontextmenu('Tag','DeleteObject', ...
% 'Callback','ud = get(gco,''UserData'');delete([gco ud(2)]);');
% set([th1 th2],'UIContextMenu',mh);
set(th1,'UserData',[lineh ph1]); %Store handle to line
set(th2,'UserData',[lineh ph2]); %Store handle to line
%Calculate Difference
dx = xv2 - xv1;
dy = yv2 - yv1;
%Add display for cursor deltas
deltah(1) = text(deltalabelpos(1,1),deltalabelpos(1,2),['({x}_{2}-{x}_{1}) = ' num2str(dx,'%+.2f')], ...
'Units','Normalized', ...
'HorizontalAlignment','left', ...
'Tag','CursorDeltaText', ...
'FontSize',8, ...
'Interpreter','tex', ...
'Parent',axh); %dx
deltah(2) = text(deltalabelpos(2,1),deltalabelpos(2,2),['{y}_{2}-{y}_{1} = ' num2str(dy,'%+.2f')], ...
'Units','Normalized', ...
'HorizontalAlignment','left', ...
'Tag','CursorDeltaText', ...
'FontSize',8, ...
'Interpreter','tex', ...
'Parent',axh); %dy
%Set Application Data.
setappdata(axh,'Delta_Handle',deltah);
setappdata(axh,'Marker',marker);
setappdata(axh,'Color',color);
set(hFig,'WindowButtonDownFcn','dualcursor(''down'',[],[],[],get(gco,''Parent''))')
set(hFig,'DoubleBuffer','on'); %eliminate flicker
case 'down' % Execute the WindowButtonDownFcn
htype = get(gco,'Type');
tag = get(gco,'Tag');
marker = getappdata(axh,'Marker');
color = getappdata(axh,'Color');
%If it's a movable object (Cursor, CursorText label, or Cursor Delta Text
%label), make it movable.
if strcmp(tag,'CursorText') | strcmp(tag,'Cursor') | strcmp(tag,'CursorDeltaText')
set(gco,'EraseMode','xor')
set(gcf,'WindowButtonMotionFcn','dualcursor(''move'',[],[],[],get(gco,''Parent''))', ...
'WindowButtonUpFcn','dualcursor(''up'',[],[],[],get(gco,''Parent''))');
end;
if strcmp(tag,'Cursor') %If clicked on a cursor
%Label the cursor we are moving. Add a text label just above the plot
CursorNumber = getappdata(gco,'CursorNumber');
cnstr = num2str(CursorNumber);
xl = xlim(axh);
lim = localObjbounds(axh);
lim = lim(1:2); %x values only
xl(isinf(xl)) = lim(isinf(xl));
% yl = lim(3:4);
xv = getappdata(gco,'Coordinates');
xv = xv(1);
%Convert to normalized
xn = (xv - xl(1))/(xl(2) - xl(1));
yn = 1.05;
CNh = text(xn,yn,cnstr, ...
'Units','Normalized', ...
'HorizontalAlignment','center', ...
'Parent',axh); %dx
%For R13 or higher (MATLAB 6.5), use a background color on the text string
v=ver('MATLAB');
v=str2num(v.Version);
if v>=6.5
set(CNh,'BackgroundColor','y');
end;
setappdata(gco,'CNh',CNh);
end
case 'move' % Execute the WindowButtonMotionFcn
htype = get(gco,'Type');
tag = get(gco,'Tag');
if ~isempty(gco)
%Special (simple) case - just repositioning the delta labels
if strcmp(tag,'CursorDeltaText') %The cursor delta labels.
cp = get(axh,'CurrentPoint');
pt = cp(1,[1 2]);
%Put into normalized units
ax = axis;
lim = localObjbounds(axh);
ax(isinf(ax)) = lim(isinf(ax));
pt(1) = (pt(1) - ax(1))/(ax(2)-ax(1));
pt(2) = (pt(2) - ax(3))/(ax(4)-ax(3));
set(gco,'Position', [pt 0])
drawnow
return
end;
%Is this the cursor or the text
if strcmp(tag,'CursorText') %The text
th = gco;
handles = get(gco,'UserData');
ph = handles(2);
slide = 0; %Don't slide along line; just reposition text
else %The marker
ph = gco;
handles = get(gco,'UserData');
th = handles(2);
slide = 1; %Slide along line to next data point
end;
offset = getappdata(th,'Offset'); %Offset from data value
cp = get(axh,'CurrentPoint');
pt = cp(1,[1 2]);
%Constrain to Line
lh = getappdata(get(th,'Parent'),'SelectedLine');
% lh = handles(1); %Line
x = cp(1,1); %first xy values
y = cp(1,2); %first xy values
if slide %Move to new data value
xl = get(lh,'XData');
yl = get(lh,'YData');
%Get nearest value
[xv,yv]=local_nearest(x,xl,yl);
%If we are moving a cursor, must move the cursor number label, too
if strcmp(tag,'Cursor')
%Move the Cursor Number label, too
CNh = getappdata(gco,'CNh');
pos = get(CNh,'Position');
xlm = xlim(axh);
lim = localObjbounds(axh);
lim = lim(1:2); %x values only
xlm(isinf(xlm)) = lim(isinf(xlm));
xn = (xv - xlm(1))/(xlm(2)-xlm(1));
set(CNh,'Position',[xn pos(2:3)])
end;
yl = ylim(axh);
lim = localObjbounds(get(lh,'Parent'));
lim = lim(3:4); %y values only
yl(isinf(yl)) = lim(isinf(yl));
datalabelformatfcnh = getappdata(th,'FormatFcnH');
textstring = feval(datalabelformatfcnh,xv,yv);
set(th,'Position', [xv yv 0] + [offset 0],'String',textstring)
set(ph,'XData',[xv xv xv],'YData',[yl(1) yv yl(2)]);
setappdata(ph,'Coordinates',[xv yv]);
setappdata(th,'Coordinates',[xv yv]);
%Update delta calculation
cursors = findobj(axh,'Tag','Cursor');
cn1 = getappdata(cursors(1),'CursorNumber');
if cn1==2 %Switch order
temp = cursors(1);
cursors(1) = cursors(2);
cursors(2) = temp;
end;
deltah = getappdata(axh,'Delta_Handle'); %Handle to cursors
%Positions of two dualcursors
xy1 = getappdata(cursors(1),'Coordinates');
xy2 = getappdata(cursors(2),'Coordinates');
%Calculate Difference
dx = xy2(1) - xy1(1);
dy = xy2(2) - xy1(2);
set(deltah(1),'String',['({x}_{2}-{x}_{1}) = ' num2str(dx,'%+.2f')]);
set(deltah(2),'String',['({y}_{2}-{y}_{1}) = ' num2str(dy,'%+.2f')]);
else %Just move text around.
set(th,'Position', [x y 0])
end;
drawnow
end;
case 'up' % Execute the WindowButtonUpFcn
htype = get(gco,'Type');
tag = get(gco,'Tag');
if strcmp(tag,'CursorText') | strcmp(tag,'Cursor') | strcmp(tag,'CursorDeltaText');
set(gco,'EraseMode','Normal')
set(gcf,'WindowButtonMotionFcn','')
if strcmp(tag,'CursorText') %If the text label, record it's relative position
cp = get(axh,'CurrentPoint');
pt = cp(1,[1 2]);
coords = getappdata(gco,'Coordinates');
offset(1) = pt(1) - coords(1);
offset = pt - coords;
setappdata(gco,'Offset',offset);
end;
if strcmp(tag,'Cursor') %Delete the temporary cursor number label
CNh = getappdata(gco,'CNh');
delete(CNh);
end;
end;
case 'selectline' % User selected a new line to be active
%Make the selected line bold
lh = getappdata(axh,'SelectedLine');
lw = get(lh,'LineWidth');
set(lh,'LineWidth',5*lw);
drawnow
%Update the cursors
dualcursor('update',deltalabelpos,marker_color,datalabelformatfcnh,axh);
%Put the line back the way you found it!
set(lh,'LineWidth',lw);
case 'update' %Update the cursor value
%Find the position of the existing cursors
cursors = findobj(axh,'Tag','Cursor');
cd1 = getappdata(cursors(1),'Coordinates');
cd2 = getappdata(cursors(2),'Coordinates');
cn1 = getappdata(cursors(1),'CursorNumber');
cn2 = getappdata(cursors(2),'CursorNumber');
clear x
x(cn1) = cd1(1); %x value of cursor number cn1
x(cn2) = cd2(1); %x value of cursor number cn2
lh = getappdata(axh,'SelectedLine');
handles = get(cursors(cn1),'UserData');
th1 = handles(2);
handles = get(cursors(cn2),'UserData');
th2 = handles(2);
offset1 = getappdata(th1,'Offset'); %Offset from data value
offset2 = getappdata(th2,'Offset'); %Offset from data value
ylm = ylim(axh);
lim = localObjbounds(get(lh,'Parent'));
lim = lim(3:4); %y values only
ylm(isinf(ylm)) = lim(isinf(ylm));
xl = get(lh,'XData');
yl = get(lh,'YData');
%Get nearest value
[xv1,yv1]=local_nearest(x(1),xl,yl);
[xv2,yv2]=local_nearest(x(2),xl,yl);
datalabelformatfcnh = getappdata(th1,'FormatFcnH');
% textstring1 = {['x=' num2str(xv1)];['y=' num2str(yv1)]};
% textstring2 = {['x=' num2str(xv2)];['y=' num2str(yv2)]};
textstring1 = feval(datalabelformatfcnh,xv1,yv1);
textstring2 = feval(datalabelformatfcnh,xv2,yv2);
set(th1,'Position', [xv1 yv1 0] + [offset1 0],'String',textstring1)
set(th2,'Position', [xv2 yv2 0] + [offset2 0],'String',textstring2)
set(cursors(cn1),'XData',[xv1 xv1 xv1],'YData',[ylm(1) yv1 ylm(2)]);
set(cursors(cn2),'XData',[xv2 xv2 xv2],'YData',[ylm(1) yv2 ylm(2)]);
%Update delta calculation
deltah = getappdata(axh,'Delta_Handle'); %Handle to delta calculation
% %Positions of two dualcursors
xy1 = getappdata(cursors(1),'Coordinates');
xy2 = getappdata(cursors(2),'Coordinates');
%Calculate Difference
dx = xv2 - xv1;
dy = yv2 - yv1;
set(deltah(1),'String',['{x}_{2}-{x}_{1} ' num2str(dx,'%+.2f')]);
set(deltah(2),'String',['{y}_{2}-{y}_{1} ' num2str(dy,'%+.2f')]);
case 'exportws' %Export selected region to workspace
[xd,yd] = local_extractregion(axh);
%If there's only one line, don't make the user hassle with cell arrays
if length(xd)==1
xd = xd{1};
yd = yd{1};
end;
cursors.xd = xd;
cursors.yd = yd;
assignin('base','cursors',cursors);
disp('Variable: cursors created in workspace');
case 'exportfig' %Export selected region to a new figure
% [xd,yd,proplist,props] = local_extractregion;
[xd,yd,hgS] = local_extractregion(axh);
%Create new plot
fh = figure;
ax = axes;
struct2handle(hgS,ax);
%Get labels from axes, too
%Clunky, but it seems to work
%First, create empty title, xlabel, ylabel
newhandles(1) = title('');
newhandles(2) = xlabel('');
newhandles(3) = ylabel('');
%Get strings from original plot
props = {'Title','xlabel','ylabel'};
vals = get(axh,props);
handles = [vals{:}];
str = get(handles,'String');
%Set the new strings to match
set(newhandles,{'String'},str)
case 'exportcursor'
% Get cursor coordinates
cursordata = dualcursor(axh);
% Push the data into the base workspace.
assignin('base','cursordata',cursordata);
disp('Variable: cursordata created in workspace');
case 'off' % Unset the WindowButton...Fcns
% set(get(axh,'Parent'),'WindowButtonDownFcn','','WindowButtonUpFcn','')
set(hFig,'WindowButtonDownFcn','','WindowButtonUpFcn','')
h1 = findobj(axh,'Tag','CursorText'); %All text
h2 = findobj(axh,'Tag','Cursor'); %The cursors
h3 = findobj(axh,'Tag','CursorDeltaText'); %The cursors
lineh = local_findlines(axh);
set(lineh,'ButtonDownFcn','');
% erasemode = getappdata(axh,'OriginalEraseMode');
% if isempty(erasemode), erasemode = 'normal'; end; %handles first time
% set(local_findlines(axh),'EraseMode',erasemode);
delete([h1;h2;h3]);
end %switch/case on action
end
% function hFig = figHandle(axh)
% % Get the handle to the parent figure
% hFig = ancestor(axh,'figure');
% end
function [xv,yv]=local_nearest(x,xl,yl)
%Inputs:
% x Selected x value
% xl Line Data (x)
%Find nearest value of [xl] to (x)
%Special Case: Line has a single non-singleton value
if sum(isfinite(xl))==1
fin = find(isfinite(xl));
xv = xl(fin);
yv = yl(fin);
else
%Normalize axes
xlmin = min(xl);
xlmax = max(xl);
xln = (xl - xlmin)./(xlmax - xlmin);
xn = (x - xlmin)./(xlmax - xlmin);
%Find nearest x value only.
c = abs(xln - xn);
[junk,ind] = min(c);
%Nearest value on the line
xv = xl(ind);
yv = yl(ind);
end;
end
function textstring = local_maketextstring(xv,yv)
textstring = {['x = ' num2str(xv,'%2g')];
['y = ' num2str(yv,'%2.2g')]};
end
function [xd,yd,hgS] = local_extractregion(axh)
val = dualcursor(axh);
%Find all lines
lineh = local_findlines(axh);
Nl = length(lineh);
%Get all line properties, so we can reproduce the appearance
hgS = handle2struct(lineh); %Get properties
xd = get(lineh,'XData');
yd = get(lineh,'YData');
%Figure out the index into these lines that val corresponds to
x1ind = zeros(Nl,1);
x2ind = zeros(Nl,1);
%Special handling for single line case
if Nl==1
xd= {xd};
yd = {yd};
end;
for ii = 1:Nl
x1ind(ii) = max(find(xd{ii}<=val(1)));
x2ind(ii) = max(find(xd{ii}<=val(3)));
%Keep data from this region only
xd{ii} = xd{ii}(x1ind(ii):x2ind(ii));
yd{ii} = yd{ii}(x1ind(ii):x2ind(ii));
%Update handle structure, to make plotting really easy
hgS(ii).properties.XData = xd{ii};
hgS(ii).properties.YData = yd{ii};
end;
end
function lineh = local_findlines(axh);
lineh = findobj(axh,'Type','line'); %Find a line to add cursor to
dots = findobj(axh,'Type','line','Tag','Cursor'); %Ignore existing cursors
lineh = setdiff(lineh,dots);
%Ignore lines with only one or two values - these are probably annotations of some
%sort
xdtemp = get(lineh,'XData');
linehtemp = lineh;
lineh=[];
if ~iscell(xdtemp) %If there's only one line, force data into a cell array
xdtemp = {xdtemp};
end;
for ii=1:length(xdtemp);
if length(xdtemp{ii})>2
lineh = [lineh; linehtemp(ii)];
end;
end;
end
function lim = localObjbounds(axh);
% Get x limits of all data in axes axh
kids = get(axh,'Children');
xmin = Inf; xmax = -Inf;
ymin = Inf; ymax = -Inf;
for ii=1:length(kids)
try % Pass through if can't get data. hopefully we hit at least one
xd = get(kids(ii),'XData');
xmin = min([xmin min(xd(:))]);
xmax = max([xmax max(xd(:))]);
yd = get(kids(ii),'YData');
ymin = min([ymin min(yd(:))]);
ymax = max([ymax max(yd(:))]);
end
end
% Nuclear option, in case things went really bad
xmin(xmin==Inf) = 0; xmax(xmax==-Inf) = 1;
ymin(ymin==Inf) = 0; ymax(ymax==-Inf) = 1;
lim = [xmin xmax ymin ymax];
end(李洋 安期货)
(责任编辑:金明正)