I have the following Matlab script:
%%// Create a function handle of your function
f = #(x,a) 976.975786299./x + 1.67417230901e-4*x./a;
%%// Plot the data
x = linspace(500, 15000);
as = 7:10;
plot(x, bsxfun(f,x(:),as));
%%// Add a legend
legendTexts = arrayfun(#(a) sprintf('A = %d', a), as, 'uni', 0);
legend(legendTexts, 'Location', 'best');
xlabel(gca, '$W_{to}/S_w$ (Pa)', ...
'Interpreter', 'latex', ...
'FontName', 'Times New Roman', ...
'FontSize', 14, ...
'FontWeight', 'normal', ...
'FontAngle', 'normal')
ylabel(gca, {'$T_{to}/W_{to}$'}, ...
'Interpreter', 'latex', ...
'FontName', 'Times New Roman', ...
'FontSize', 14, ...
'FontWeight', 'normal', ...
'FontAngle', 'normal')
hold 'on'
f = #(x,a) functions(x,a)
%%// Plot the data
x = linspace(1, 15000,30000);
y = bsxfun(f,x(:),1.9)
as = 1.9:0.1:2.4;
plot(x, bsxfun(f,x(:),as));
%%// Add a legend
legendTexts = arrayfun(#(a) sprintf('C_{L{max}_{TO}} = %g', a), as, 'uni', 0);
legend(legendTexts, 'Location', 'best');
xlabel(gca, '$W_{to}/S_w$ (Pa)', ...
'Interpreter', 'latex', ...
'FontName', 'Times New Roman', ...
'FontSize', 14, ...
'FontWeight', 'normal', ...
'FontAngle', 'normal')
ylabel(gca, {'$T_{to}/W_{to}$'}, ...
'Interpreter', 'latex', ...
'FontName', 'Times New Roman', ...
'FontSize', 14, ...
'FontWeight', 'normal', ...
'FontAngle', 'normal')
xline(5717,'k','LineWidth',1)
function y = functions(x,a)
Bounds_1 = [0,((2.34117647059/(18.0173492479 - 3.14916666667*a)) + 5.61882352942e-2)/(1.53640039877e-4/a)];
Bounds_2 = [((2.34117647059/(18.0173492479 - 3.14916666667*a)) + 5.61882352942e-2)/(1.53640039877e-4/a),15000];
Bounds = [Bounds_1 Bounds_2];
Min = min(Bounds);
Max = max(Bounds);
f1 = ((2.34117647059./(18.0173492479 - 3.14916666667*a)) + 5.61882352942e-2).*(Bounds_1(1) < x & x < Bounds_1(2));
f2 = (1.53640039877e-4*x/a).*(Bounds_2(1) < x & x <= Bounds_2(2));
y = f1 + f2;
end
The legend shows "data1" on the horizontal line. How do I remove that? I've tried different ways of attempting to remove it, but I can't seem to find the way to do so.
You can use the HandleVisibility property on lines to "hide" them from things like the legend
xline(5717,'k','LineWidth',1,'HandleVisibility','off');
Assign your vertical line to a variable (x below) and then remove the DisplayName so it doesn't appear in the legend. Finally, place the legend call below that.
x=xline(5717,'k','LineWidth',1);
x.DisplayName = '';
legend(legendTexts, 'Location', 'best');`
It looks fine on my side after making these changes.
Related
I have the following code, which plots a line that crosses the origin. I need to plot a horizontal line at a given constant until I cross the line.
For example: y = 0.31 until the constant crosses the line y= 7.51e-5*x. How do I go about doing this?
%%// Create a function handle of your function
f = #(x,a) 7.51619693312e-5*x;
%%// Plot the data
x = linspace(0, 15000);
as = 9.5;
plot(x, bsxfun(f,x(:),as));
xlabel(gca, '$W_{to}/S_w$ (Pa)', ...
'Interpreter', 'latex', ...
'FontName', 'Times New Roman', ...
'FontSize', 14, ...
'FontWeight', 'normal', ...
'FontAngle', 'normal')
ylabel(gca, {'$T_{to}/W_{to}$'}, ...
'Interpreter', 'latex', ...
'FontName', 'Times New Roman', ...
'FontSize', 14, ...
'FontWeight', 'normal', ...
'FontAngle', 'normal')
How about solving for the point of intersection and plotting the function as two parts?
m = 7.51e-5; %some slope
y1 = 0.31; %Some y value
x_intercept = y1/slope; %Compute the part were the piecing takes place.
x1 = 0; %Left x-limit (Choose some start value)
x2 = x_intercept*1.2; %right x-limit (Choose some end value)
y2 = m*x2;
ax = axes; %Axes object in which to plot.
hold(ax,'on'); %Tell ax to remember all plots.
p1 = plot([x1,x_intercept],[y1,y1]); %Plots the constant part.
p2 = plot([x_intercept,x2],[y1,y2]); %Plots the slant part.
%Add labels and customize...
Also, for plotting purposes, there is no need to evaluate the equation of a line over several discrete points. Just evaluate the end points and the plot function will plot a straight line between the end points.
I am making a frequency plot and I would like some help on tick labeling.
Here is what I have:
semilogx([200,1000,5000], [0,6,0]);
xlim([20 20000]);
sc = [20:10:100,125:25:175];
scale = [sc,sc*10,sc*100, 20000];
xticks(scale);
xticklabels(scale);
set(gca,'XMinorTick','Off')
grid on;
set (gca, "xminorgrid", "off")
xlabel('frequency (Hz)');
ylabel('dB');
How can I make all numbers from 1000 and upwards appear as 1K, 2K, 5K and so on?
How could I make the lines on 50,100,200,500,1K,2K,5K,10K appear thicker/more black?
Octave approach (probably works on matlab too though)
I wouldn't rely on latex trickery to do this to be honest.
Here is the way I usually do stuff like this.
Effectively, because the axis labels object is considered a single object, and you cannot split it into parts, the trick is to overlay an invisible, bare-minimum axes object defining only the labels you want, and treat those as you'd like (e.g. adjust its fontweight, fontsize, xcolor, etc etc).
H = semilogx([200,1000,5000], [0,6,0]);
A = gca();
B = axes();
subscale = [20:10:100,125:25:175];
scale = [subscale,subscale * 10,subscale * 100, 20000];
ScaleTextLabels = {};
for i = 1 : length( scale )
if scale(i) >= 1000, ScaleTextLabels{i} = sprintf("%dk", scale(i) / 1000 );
else, ScaleTextLabels{i} = num2str( scale(i) );
end
end
SpecialTickLabels = { '50', '100', '200', '500', '1k', '2k', '5k', '10k'};
ScaleIndices = 1 : length( ScaleTextLabels );
SpecialIndices = nthargout( 2, #ismember, SpecialTickLabels, ScaleTextLabels );
NormalIndices = setdiff( ScaleIndices, SpecialIndices );
set( A, 'xgrid', 'on', 'xlabel', 'frequency (Hz)', 'xlim', [20 20000] , 'xminorgrid', 'off', 'xminortick', 'off', 'xticklabel', ScaleTextLabels(NormalIndices), 'xtick', scale(NormalIndices) , 'ylabel', 'dB', 'gridlinestyle', ':', 'gridcolor', 'k', 'gridalpha', 0.5 );
set( B, 'xgrid', 'on', 'xlabel', '' , 'xlim', get( A, 'xlim' ), 'xminorgrid', 'off', 'xminortick', 'off', 'xticklabel', ScaleTextLabels(SpecialIndices), 'xtick', scale(SpecialIndices), 'ylabel', '' , 'color', 'none', 'fontsize', 12, 'fontweight', 'bold', 'position', get( A, 'position'), 'xcolor', [0,0,0], 'xscale', 'log', 'ylim', get( A, 'ylim'), 'ytick', [], 'gridlinestyle', '--', 'gridcolor', 'k', 'gridalpha', 0.8 );
This "layers of transparent axes objects" technique is very useful to keep in mind in general, it allows great flexibility when designing complex graphs. :)
In MATLAB
*I unfortunately could not yet find how to bold the specific lines
Adding the following code allows the ticks to converted to the new names/format suggested in part 1. For part 2 the best I could find out right now is bolding the specific numbers, unfortunately not the specific ticks/lines. Here \bf indicates which labels are to be bolded. All the names will correspond to the positions set originally by your axis vector scale. The last line in the code below indicates the replacement of the current axis, gca.
semilogx([200,1000,5000],[0,6,0]);
sc = [20:10:100,125:25:175];
scale = [sc,sc*10,sc*100, 20000];
Current_Axis = gca;
Current_Axis.XMinorTick = 'off';
xlabel('frequency (Hz)'); ylabel('dB');
xlim([20 20000]);
grid on;
X_Scale_Names = {'\bf20'; '30'; '40'; '\bf50'; '60';
'70';'80';'90';'\bf100';'125';'150';'175';'\bf200';'300';'400';
'500';'600';'700';'800';'900';'\bf1K';'1.25K';'1.5K';'1.75K';
'\bf2K';'3K';'4K';'\bf5K';'6K';'7K';'8K';'9K';'\bf10K';'12.5K';'15K';
'17.5K';'20K'};
To Adjust More Grid and Axis Properties:
Current_Axis = gca;
set(Current_Axis,'xtick',scale,'xticklabel',X_Scale_Names);
Current_Axis.LineWidth = 1;
Current_Axis.GridColor = 'k';
Current_Axis.GridAlpha = 0.5;
Ran using MATLAB R2019b
I did it like this:
semilogx([200,1000,5000], [0,6,0]);
xlim([20 20000]);
sc = [20:5:35,40:10:100,125:25:175];
scale = [sc,sc*10,sc*100, 20000];
xticks(scale);
xticklabels(scale);
set(gca,'XMinorTick','Off')
grid on;
set(gca,'gridlinestyle',':');
set(gca,'gridalpha',0.6);
set (gca, "xminorgrid", "off");
xg = [50,100,200,500,1000,2000,5000,10000]; #highlight grids
xx = reshape([xg;xg;NaN(1,length(xg))],1,length(xg)*3);
yy = repmat([ylim() NaN],1,length(xg));
line(xx,yy,'Color',[0.65,0.65,0.65]);
xlabel('frequency (Hz)');
ylabel('dB');
X_Scale_Names = {'\fontsize{11}\bf20'; '25'; '30';'35';'40'; '\fontsize{11}\bf50'; '60';
'70';'80';'90';'\fontsize{11}\bf100';'125';'150';'175';'\fontsize{11}\bf200';'250';'300';'350';'400';
'\fontsize{11}\bf500';'600';'700';'800';'900';'\fontsize{11}\bf1K';'1.25K';'1.5K';'1.75K';
'\fontsize{11}\bf2K';'2.5K';'3K';'3.5K';'4K';'\fontsize{11}\bf5K';'6K';'7K';'8K';'9K';'\fontsize{11}\bf10K';'12.5K';'15K';
'17.5K';'\fontsize{11}\bf20K'};
set(gca,'xtick',scale,'xticklabel',X_Scale_Names);
But I don't think this is the best/fastest/easiest way to do it...
Here is a sample of a strange problem. I'd like to plot curves with multiple y axes and I use a fairly common method in MATLAB.
function plot_test()
clear;
savefile = 1;
scrsz = get(0,'ScreenSize');
figure('Position',[1 1 0.9 * scrsz(3) 0.9 * scrsz(4)]);
hold on;
box on;
x = 0:1:10;
h1 = plot(x, x.^2 , 'r', 'LineWidth', 2.5);
%Axis label
xlabel('XLabel', 'FontSize', 20, 'Interpreter', 'latex');
ylabel('YLabel', 'FontSize', 20, 'Interpreter', 'latex');
set(gca, 'FontSize', 20, 'LineWidth', 3);
ax1 = gca;
ax2 = axes('Position',get(ax1,'Position'),'XAxisLocation','top','YAxisLocation','right','Color','none','XColor','none','YColor','k');
linkaxes([ax1 ax2],'x');
hold on
box on;
h2 = plot(x, x, 'b', 'Parent', ax2, 'LineWidth', 2.5);
ylabel('Second YLabel', 'FontSize', 20, 'Interpreter', 'latex');
set(gca, 'FontSize', 20, 'LineWidth', 3);
hl=legend([h1 h2],{'First Line','Second Line'});
set(hl,'FontSize',15,'Location','Northwest', 'Orientation','Vertical')
%Save pdf
if savefile
% Backup previous settings
prePaperType = get(gcf,'PaperType');
prePaperUnits = get(gcf,'PaperUnits');
preUnits = get(gcf,'Units');
prePaperPosition = get(gcf,'PaperPosition');
prePaperSize = get(gcf,'PaperSize');
% Make changing paper type possible
set(gcf,'PaperType','<custom>');
% Set units to all be the same
set(gcf,'PaperUnits','inches');
set(gcf,'Units','inches');
% Save the pdf
print -dpdf Test.pdf;
% Restore the previous settings
set(gcf,'PaperType',prePaperType);
set(gcf,'PaperUnits',prePaperUnits);
set(gcf,'Units',preUnits);
set(gcf,'PaperPosition',prePaperPosition);
set(gcf,'PaperSize',prePaperSize);
end
The objective is to print a PDF of the figure and save it in the same folder as Test.pdf. This is accomplished but the axes are misaligned. On my Windows machine it looks horrible while on a Mac it looks almost okay (but if you look closely, the y-axes are indeed misaligned at the bottom).
This only happens when I use a second axis. Without that, all this runs perfectly. Any idea why?
Okay, so I found a way: The trick is to use plotyy. Sample code below
function plot_test2()
clear;
savefile = 1;
scrsz = get(0,'ScreenSize');
figure('Position',[1 1 0.9 * scrsz(3) 0.9 * scrsz(4)]);
hold on;
box on;
x=(0:1:10);
y1=x;
y2=x.^2;
[hAx, hLine1, hLine2] = plotyy(x,y1,x,y2);
%Axis label
xlabel(hAx(1),'XLabel', 'FontSize', 20, 'Interpreter', 'latex', 'Color', 'k');
ylabel(hAx(1),'YLabel', 'FontSize', 20, 'Interpreter', 'latex', 'Color', 'k');
ylabel(hAx(2),'Second YLabel', 'FontSize', 20, 'Interpreter', 'latex');
set(hAx,{'ycolor'},{'k';'k'})
set(hAx,{'FontSize'},{20;20}, {'LineWidth'}, {3;3})
set(hLine1,'LineWidth', 3)
set(hLine2,'LineWidth', 3)
set(hLine1,'Color', 'r')
set(hLine2,'Color', 'b')
hl=legend([hLine1 hLine2],{'First Line','Second Line'});
set(hl,'FontSize',15,'Location','Northwest', 'Orientation','Vertical')
%Save pdf
if savefile
% Backup previous settings
prePaperType = get(gcf,'PaperType');
prePaperUnits = get(gcf,'PaperUnits');
preUnits = get(gcf,'Units');
prePaperPosition = get(gcf,'PaperPosition');
prePaperSize = get(gcf,'PaperSize');
% Make changing paper type possible
set(gcf,'PaperType','<custom>');
% Set units to all be the same
set(gcf,'PaperUnits','inches');
set(gcf,'Units','inches');
% Save the pdf
print -dpdf Test.pdf;
% Restore the previous settings
set(gcf,'PaperType',prePaperType);
set(gcf,'PaperUnits',prePaperUnits);
set(gcf,'Units',preUnits);
set(gcf,'PaperPosition',prePaperPosition);
set(gcf,'PaperSize',prePaperSize);
end
I'd like to change the font size of the x & y tick labels, but have only been able to change the size of the y tick labels.
Below is the code that changes only the font size of the y tick labels:
figure(1);
for z=1:length(percentsolar)
for i=1:h
percentimprovement4(:,i) = percentimprovement2(1,:,i,z,1);
end
ax(z) = subplot(3,2,z);
boxplot(percentimprovement4);
set(ax(z), 'fontsize', 6);
ylabel('% improvement', 'fontsize',8,'fontweight', 'bold');
xlabel('Hour of the day', 'fontsize', 8,'fontweight', 'bold');
title(['PF improvement for ', num2str(percentsolar(z)),'% solar penetration'], 'fontsize', 10 ,'fontweight', 'bold');
clear percentimprovement4
end
linkaxes(ax);
saveas(gcf,'Boxplotshourly.jpg');
As written here:
boxplot() uses the default axes labeling for the Y axes, but for the X
axes, it uses text() to put the labels in place and it does not
grab the axes FontSize when it does so.
Thus, in addition to set(ax(z), 'fontsize', 6); you should also use set(findobj(ax(z),'Type','text'),'FontSize', 6);. For example,
figure(1);
percentsolar = zeros(1,6);
z = 6;
ax = zeros(0, length(percentsolar));
for z = 1:length(percentsolar)
ax(z) = subplot(3,2, z);
x1 = normrnd(5,1,100,1);
x2 = normrnd(6,1,100,1);
boxplot([x1, x2]);
set(ax(z), 'fontsize', 6);
set(findobj(ax(z),'Type','text'),'FontSize', 6);
ylabel('% improvement', 'fontsize',8,'fontweight', 'bold');
xlabel('Hour of the day', 'fontsize', 8,'fontweight', 'bold');
end
I would like to give the subplots I make a simple label. Unfortunately I'm getting an ugly behavior. Consider the following function:
function h = set_label1(label)
tlh = get(gca, 'Title');
if strcmp(get(tlh, 'String'), '')
title(' ');
end
ylh = get(gca, 'YLabel');
if strcmp(get(ylh, 'String'), '')
ylabel(' ');
end
ylp = get(ylh, 'Position');
x = ylp(1);
tlp = get(tlh, 'Position');
y = tlp(2);
h = text('String', label, ...
'HorizontalAlignment', 'right',...
'VerticalAlignment', 'Baseline', ...
'FontUnits', 'pixels', ...
'FontSize', 16, ...
'FontWeight', 'bold', ...
'FontName', 'Arial', ...
'Position', [x y 0]);
end
Here is a simple test run:
figure;
h1 = axes('OuterPosition', [0,0,.5 1]);
set(h1,'LooseInset',get(h1,'TightInset'));
h2 = axes('OuterPosition', [.5,0,.5 1]);
set(h2,'LooseInset',get(h2,'TightInset'));
axes(h1);
plot([0 1], [4 5]);
set_label1('A');
axes(h2);
plot([0 1], [4 5]);
set_label1('B');
The picture I obtain is:
If you resize the figure the labels will not be in the right position anymore. That is fine, I expected it (If you know how to put them back where they belong and you tell us that would make me very happy).
THe problem I'm facing is that I do not want to specify the position of the label in 'data' units.
Instead, I want to use normalized units. So I used modified form of function. Now let us use this:
function h = set_label2(label)
tlh = get(gca, 'Title');
if strcmp(get(tlh, 'String'), '')
title(' ');
end
ylh = get(gca, 'YLabel');
if strcmp(get(ylh, 'String'), '')
ylabel(' ');
end
oldUnits = replace_prop(ylh, 'Units', 'normalized');
ylp = get(ylh, 'Position');
x = ylp(1);
set(ylh, 'Units', oldUnits);
oldUnits = replace_prop(tlh, 'Units', 'normalized');
tlp = get(tlh, 'Position');
y = tlp(2);
set(ylh, 'Units', oldUnits);
h = text('String', label, ...
'HorizontalAlignment', 'right',...
'VerticalAlignment', 'Baseline', ...
'FontUnits', 'pixels', ...
'FontSize', 16, ...
'FontWeight', 'bold', ...
'FontName', 'Arial', ...
'Units', 'normalized',...
'Position', [x y 0]);
end
function oldvalue = replace_prop(handle, propName, newvalue)
oldvalue = get(handle, propName);
set(handle, propName, newvalue);
end
Running the same test:
figure;
h1 = axes('OuterPosition', [0,0,.5 1]);
set(h1,'LooseInset',get(h1,'TightInset'));
h2 = axes('OuterPosition', [.5,0,.5 1]);
set(h2,'LooseInset',get(h2,'TightInset'));
axes(h1);
plot([0 1], [4 5]);
set_label2('A');
axes(h2);
plot([0 1], [4 5]);
set_label2('B');
We obtain the exact same picture as before. The only problem is that when we resize it now something bad happens:
The labels are actually in the correct position. But it seems that the 'LooseInset' and 'TightInset' property I used make the axes act as if there is no labels.
Is there any fix for this? Really all I am doing is getting the position of the title and ylabel in normalized units as opposed in data units and this seems to mess it up.
The reason I need to get it in normalized units is so that when we get a 3D plot I can position the label with respect to the title and the zlabel.
For posterity's sake here is the version I decided to go with. It does what I expect it to do, but now I have a problem which I have no idea how to solve. OK, first the good news, here is the function called axes_label.
function c = axes_label(varargin)
if isa(varargin{1}, 'char')
axesHandle = gca;
else
axesHandle = get(varargin{1}{1}, 'Parent');
end
if strcmp(get(get(axesHandle, 'Title'), 'String'), '')
title(axesHandle, ' ');
end
if strcmp(get(get(axesHandle, 'YLabel'), 'String'), '')
ylabel(axesHandle, ' ');
end
if strcmp(get(get(axesHandle, 'ZLabel'), 'String'), '')
zlabel(axesHandle, ' ');
end
if isa(varargin{1}, 'char')
label = varargin{1};
if nargin >=2
dx = varargin{2};
if nargin >= 3
dy = varargin{3};
else
dy = 0;
end
else
dx = 3;
dy = 3;
end
h = text('String', label, ...
'HorizontalAlignment', 'left',...
'VerticalAlignment', 'top', ...
'FontUnits', 'pixels', ...
'FontSize', 16, ...
'FontWeight', 'bold', ...
'FontName', 'Arial', ...
'Units', 'normalized');
el = addlistener(axesHandle, 'Position', 'PostSet', #(o, e) posChanged(o, e, h, dx, dy));
c = {h, el};
else
h = varargin{1}{1};
delete(varargin{1}{2});
if nargin >= 2
if isa(varargin{2}, 'char')
set(h, 'String', varargin{2});
if nargin >=3
dx = varargin{3};
dy = varargin{4};
else
dx = 3;
dy = 3;
end
else
dx = varargin{2};
dy = varargin{3};
end
else
error('Needs more arguments. Type help axes_label');
end
el = addlistener(axesHandle, 'Position', 'PostSet', #(o, e) posChanged(o, e, h, dx, dy));
c = {h, el};
end
posChanged(0, 0, h, dx, dy);
end
function posChanged(~, ~, h, dx, dy)
axh = get(h, 'Parent');
p = get(axh, 'Position');
o = get(axh, 'OuterPosition');
xp = (o(1)-p(1))/p(3);
yp = (o(2)-p(2)+o(4))/p(4);
set(h, 'Units', get(axh, 'Units'),'Position', [xp yp]);
set(h, 'Units', 'pixels');
p = get(h, 'Position');
set(h, 'Position', [p(1)+dx, p(2)+5-dy]);
set(h, 'Units', 'normalized');
end
Ok, so how do we use this crappy function? I made it so that we can have these uses:
% c = axes_label('label')
% Places the text object with the string 'label' on the upper-left
% corner of the current axes and returns a cell containing the handle
% of the text and an event listener.
%
% c = axes_label('label', dx, dy)
% Places the text object dx pixels from the left side of the axes
% and dy pixels from the top. These values are set to 3 by default.
%
% c = axes_label(c, ...)
% Peforms the operations mentioned above on cell c containing the
% handle of the text and the event listener.
%
% c = axes_label(c, dx, dy)
% Adjusts the current label to the specifed distance from the
% upper-left corner of the current axes.
If we perform the same test as before:
figure;
h1 = axes('OuterPosition', [0,0,.5 1]);
set(h1,'LooseInset',get(h1,'TightInset'));
h2 = axes('OuterPosition', [.5,0,.5 1]);
set(h2,'LooseInset',get(h2,'TightInset'));
axes(h1);
plot([0 1], [4 5]);
axes_label('A');
axes(h2);
plot([0 1], [4 5]);
axes_label('B', 250, 250);
Now we obtain what I wanted. Label 'A' is set at the upper-left corner of the axes's Outerbox. And label B I explicitly set it to be 250 pixels from its upper-left corner. Here is a plot:
What I like about this function is that if I were to store the cell returned from it and then I put back I can change the position. For instance if label = axes_label('A'); Then on the command prompt I can do label = axes_label(label, 10, 20); and I will see my label move.
The problem I'm facing now is ralated to the function export_fig
If I try to use this:
export_fig('testing.png', '-nocrop', '-painters');
Then this is the figure I obtain.
This is the reason why I exaggerated with label B. Before I added the event listeners export_fig would do an OK job at printing the labels where I had positioned them. But somehow now export_fig doesn't do what it claims it does. Mainly exporting an image with
Figure/axes reproduced as it appears on screen
If instead we remove the option -painters then we get this:
There is probably a bug in code since I'm not experienced with listeners, so if anyone can fix this behavior and/or can improve on this code please feel free to do so and share it as an answer.
First of all, I like your idea of using the title/y-label to position the text on the upper left corner, clever :)
Now, instead of using normalized units, keep using data units and create an event listener for whenever the title or the y-label change their positions, and use their new values to re-adjust the created text.
So add the following to the end of your set_label1 function:
addlistener(ylh, 'Position', 'PostSet', #(o,e) posChanged(o,e,h,1))
addlistener(tlh, 'Position', 'PostSet', #(o,e) posChanged(o,e,h,2))
and here is the callback function used for both cases (we use the last argument idx to control whether to set x or y coordinate):
function posChanged(src,evt,hTxt,idx)
posLabel = evt.NewValue; %# new position of either title/y-label
posText = get(hTxt, 'Position'); %# current text position
posText(idx) = posLabel(idx); %# update x or y position (based on idx)
set(hTxt, 'Position',posText) %# adjust the text position
end