This commit is contained in:
Silas
2024-10-29 14:46:57 +01:00
219 changed files with 29653 additions and 744 deletions

View File

@@ -27,7 +27,7 @@ classdef Signal
[~,obj.gitSHA] = system('git rev-parse HEAD');
[~,obj.gitStatus] = system('git status --porcelain');
[~,obj.gitPatch] = system('git diff');
% [~,obj.gitPatch] = system('git diff');
%%% Stuff for Logbook %%%
SignalType = [];
@@ -379,7 +379,7 @@ classdef Signal
%xlim([-obj.fs/2 obj.fs/2].*1e-9)
edgetick = 2^(nextpow2(obj.fs*1e-9));
xticks([-edgetick:16:edgetick]);
xlim([100*round( min(w.*1e-9)/100,1)-10,100*round( max(w.*1e-9)/100,1)+10])
xlim([100*round( min(w)/100,1)-10,100*round( max(w)/100,1)+10])
else
xlabel("Normalized Frequency");
xlim([-pi, pi]);
@@ -510,7 +510,7 @@ classdef Signal
end
%%
function [obj,S] = tsynch(obj,options)
function [obj,S,isFlipped] = tsynch(obj,options)
% time sync and cut
arguments
obj Signal
@@ -525,14 +525,16 @@ classdef Signal
q = obj.fs/options.fs_ref;
b = options.reference.resample("fs_in",options.fs_ref,"fs_out",obj.fs).normalize("mode","oneone").signal;
max_occurences = floor(length(a)/length(b));
%estimate delay between signals
[co,lags] = xcorr(a,b);
[~,pos] = max(co);
[~,pos] = max(abs(co));
D = lags(pos);
%estimate start pos of signal
maxpeaknum = floor(length(a)/length(b));
[pks,pkpos] = findpeaks(co./max(co),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum);
[pks,pkpos] = findpeaks(abs(co./max(co)),'MinPeakDistance',length(b)/2,'MinPeakHeight',0.2,'NPeaks',maxpeaknum);
shifts = lags(pkpos);
%Cut occurences of ref signal from signal (only positive shifts)
@@ -543,9 +545,19 @@ classdef Signal
S{end+1,1} = sig;
end
%
isFlipped=0;
if all(sign(co(pkpos)))
isFlipped = 1;
end
%return/keep the sinal with the highest correlation (only within positive shifts)
[~,idx]=max(pks(shifts>0));
obj.signal = S{idx}.signal;
%put signal with highest corr. to first index in S array
swap = S{1};
S{1} = S{idx};
S{idx} = swap;
for c = 1:numel(shifts(shifts>0))
S{c}.logbook = [];

View File

@@ -202,19 +202,19 @@ classdef ChannelFreqResp < handle
function plot(obj)
figure(55);
clf;
%clf;
Havg = obj.H;
%1)
subplot(2,1,1);hold all;box on;title('Magnitude Freq. Response');
subplot(2,1,1);hold on;box on;title('Magnitude Freq. Response');
plot(obj.faxis/1e9, 20*log10(abs(obj.H_all)),'linewidth',0.1,'LineStyle','-','Color','#808080') ;
xlim([0.2 .5*max(obj.faxis)*1e-9]);
plot(obj.faxis/1e9, 20*log10(abs(Havg)),'LineWidth',2);
grid on;
%2)
subplot(2,1,2); hold all; box on; title('Phase Freq. Response');
subplot(2,1,2); hold on; box on; title('Phase Freq. Response');
plot(obj.faxis/1e9, angle(obj.H_all),'linewidth',0.1,'LineStyle','-','Color','#808080') ;
plot(obj.faxis/1e9, unwrap(angle(Havg)),'LineWidth',2) ;
xlim([0.2 .5*max(obj.faxis)*1e-9]);
@@ -228,7 +228,7 @@ classdef ChannelFreqResp < handle
Havg = Havg./mean(Havg(2:10));
%3)
subplot(2,1,1); hold all; box on; title('Inverse Magnitude Freq. Response');
subplot(2,1,1); hold on; box on; title('Inverse Magnitude Freq. Response');
plot(obj.faxis/1e9, 20*log10(abs(1./Havg)),"LineWidth",2,"Color",[0.3467 0.5360 0.6907]) ;
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
ylim([-1 15]);
@@ -236,15 +236,17 @@ classdef ChannelFreqResp < handle
yline(3,'LineWidth',2,'LineStyle','--');
%4)
subplot(2,1,2); hold all; box on; title('Inverse Phase Freq. Response');
subplot(2,1,2); hold on; box on; title('Inverse Phase Freq. Response');
plot(obj.faxis/1e9, unwrap(angle(1./Havg)),"LineWidth",2,"Color",[0.3467 0.5360 0.6907]) ;
xlim([0.2 .5*max(obj.faxis)*1e-9]); grid on;
%%% plot for publication
figure(98989);hold all;box on;title('Magnitude Freq. Response');
xlim([0.2 .5*max(obj.faxis)*1e-9]);
ylim([-20, 2]);
plot(obj.faxis/1e9, 20*log10(abs(Havg)),'LineWidth',2);
figure(30);hold on;box on;title('Magnitude Freq. Response');
% xlim([0 max(obj.faxis)*1e-9]);
% ylim([-20, 10]);
fax = obj.faxis - obj.f_ref/2;
Havg = Havg ./ max(abs(Havg));
plot(fax/1e9, 20*log10(abs(fftshift(Havg)))+7,'LineWidth',2);
grid on;
@@ -360,7 +362,7 @@ classdef ChannelFreqResp < handle
end
fprintf('Frequency response information successfully loaded from %s\n', fullFileName);
% fprintf('Frequency response information successfully loaded from %s\n', fullFileName);
end
end

View File

@@ -77,9 +77,42 @@ classdef PAMsource
bitpattern=[];
if obj.useprbs
for i = 1:log2(obj.M)
[bitpattern(:,i),seed] = prbs(O,N,seed);
% for i = 1:log2(obj.M)
% [bitpattern(:,i),seed] = prbs(O,N,seed);
% end
%%%%% MOVE-IT PRMS %%%%
state = struct();
para = struct();
if obj.M == 6
para.bl = 2^(obj.order-2);
para.dimension = 5;
else
para.bl = 2^(obj.order-1);
para.dimension = log2(obj.M); %2.5bits/sym -> 2 bit/sym
end
para.rand = 0;
para.order = floor(obj.order / log2(obj.M));
para.skip =0;
para.bruijn = 0;
para.reset_prms = 0;
para.method = 1;
data_in = [];
global loop;
loop = 0;
[data_out,state_] = prms(data_in, state, para);
loop = 1;
[data_out,state_out] = prms(data_in, state_, para);
bitpattern = data_out';
%%%%% END MOVE-IT %%%%%%%
else
s = RandStream('twister','Seed',obj.randkey);
for i = 1:log2(obj.M)
@@ -88,7 +121,7 @@ classdef PAMsource
end
if obj.M == 6
bitpattern = reshape(bitpattern,[],1);
bitpattern = reshape(bitpattern',[],1);
bitpattern = bitpattern(1:end-mod(length(bitpattern),5));
end
@@ -108,7 +141,7 @@ classdef PAMsource
end
% figure(12);hold on;histogram(symbols.signal,'Normalization','probability');
if obj.mrds_code
symbols = MRDS_coding("blocklength",obj.mrds_blocklength).encode(symbols);
end
@@ -128,7 +161,7 @@ classdef PAMsource
%%%%% Re-sample to f DAC %%%%%%
digi_sig = digi_sig.resample("fs_in",digi_sig.fs,"fs_out",obj.fs_out,"n",10,"beta",5);
% digi_sig.spectrum("fignum",111,"displayname","after pulseforming");
% digi_sig.spectrum("fignum",111,"displayname","after pulseforming");
%%%%% Hard clip digital signal to PAM range before DAC %%%%%%
if obj.applyclipping

View File

@@ -164,11 +164,11 @@ classdef Duobinary
elseif I == 11
%todo
data = data .* sqrt(5.8);
warning('Check if PAM16 implementation, mapping and scaling is correct!')
warning('Check db decode implementation, mapping and scaling is correct!')
elseif I == 15
data = data .* sqrt(10.5);
elseif I == 16
warning('Check if PAM16 implementation, mapping and scaling is correct!')
warning('Check db decode implementation, mapping and scaling is correct!')
end
data = round(data);

View File

@@ -7,11 +7,12 @@ classdef Awg2Scope
Scope
mapping;
waitUntilClick
end
methods (Access=public)
function obj = Awg2Scope(Awg,Scope,mapping)
function obj = Awg2Scope(Awg,Scope,mapping,options)
%Simple class to call the Awg and Scope and map the signals
%accordingly in the correct formats with correct l
% ogbook
@@ -21,16 +22,19 @@ classdef Awg2Scope
Awg
Scope
mapping
options.waitUntilClick = 0;
end
obj.Awg = Awg;
obj.Scope = Scope;
obj.mapping = mapping; % AWG CH [1,2,3,4] -> Scope CH [0,0,0,1]
obj.waitUntilClick = options.waitUntilClick;
end
function [S1,S2,S3,S4] = process(obj,channels)
function [S1,S2,S3,S4] = process(obj,channels, options)
arguments
obj
@@ -41,15 +45,33 @@ classdef Awg2Scope
channels.signal4 Informationsignal = Informationsignal([])
% add new optional arguments here
options.waitUntilClick = obj.waitUntilClick;
end
%%% UPLOAD TO AWG %%%
[S1,S2,S3,S4]=obj.Awg.upload("signal1",channels.signal1,...
"signal2",channels.signal2,...
"signal3",channels.signal3,...
"signal4",channels.signal4...
);
%%% UPLOAD TO AWG %%%
scpe_sig_cell = obj.Scope.read();
%%% READ FROM SCOPE %%%
scpe_sig_cell = obj.Scope.read("waitUntilClick",options.waitUntilClick);
%%% READ FROM SCOPE %%%
% Map Scope measurement to output signal
% mapping index is the AWG chanel and mapping number is the

View File

@@ -176,7 +176,11 @@ classdef AwgKeysight
% The waveform granularity is e.g. 128. This means that the waveform length must be a multiple of this granularity. The minimum waveform length is 128 samples.
if rem(length(signal{s}),obj.waveformGranularity) ~= 0
signal{s} = [signal{s} signal{s}(:,1:(obj.waveformGranularity-rem(length(signal{s}),obj.waveformGranularity)))];
warning(['data should have a length multiple of ',num2str(obj.waveformGranularity),'!']);
debug = 0;
if debug
warning(['data should have a length multiple of ',num2str(obj.waveformGranularity),'!']);
end
end
% Digitally apply skews to the signals

View File

@@ -51,90 +51,90 @@ classdef DC_supply < handle
success = [0,0];
try
% %connect to device
% if exist('v','var')
% %v = visadev("GPIB1::19::INSTR");
% v = obj.connectDevice();
% else
%
% end
% %connect to device
% if exist('v','var')
% %v = visadev("GPIB1::19::INSTR");
% v = obj.connectDevice();
% else
%
% end
v = visadev("GPIB1::19::INSTR");
v = visadev("GPIB1::19::INSTR");
debug = 0;
if debug
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
end
debug = 0;
if debug
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
end
cmd = 'INST:SEL?';
cmd = 'INST:SEL?';
writeline(v, cmd);
prev_selected_channel = readline(v);
for ch = 1:2
% choose channel
cmd = ['INST:SEL OUT',num2str(ch)];
writeline(v, cmd);
prev_selected_channel = readline(v);
for ch = 1:2
% get current voltage level
cmd = 'VOLT?';
writeline(v, cmd);
act_volt = str2num(readline(v));
% choose channel
cmd = ['INST:SEL OUT',num2str(ch)];
writeline(v, cmd);
% desired voltage (round to two digits after comma)
des_volt = round(options.voltage(ch),2);
cnt = 0;
while abs(act_volt-des_volt) > 0
selected_channel = ['OUT',num2str(ch)];
% get current voltage level
cmd = 'VOLT?';
writeline(v, cmd);
act_volt = str2num(readline(v));
% desired voltage (round to two digits after comma)
des_volt = round(options.voltage(ch),2);
% difference
diff_volt = act_volt-des_volt;
cnt = 0;
while abs(act_volt-des_volt) > 0
selected_channel = ['OUT',num2str(ch)];
% get current voltage level
cmd = 'VOLT?';
writeline(v, cmd);
act_volt = str2num(readline(v));
% difference
diff_volt = act_volt-des_volt;
% set new voltage
increment_voltage = -0.01* sign(diff_volt) ;
cmd = ['VOLT ',num2str(act_volt+increment_voltage)];
writeline(v, cmd);
cnt = cnt+1;
if mod(cnt,100) == 0
wait(1);
end
% get current voltage level
cmd = 'VOLT?';
writeline(v, cmd);
act_volt = str2num(readline(v));
% set new voltage
increment_voltage = -0.01* sign(diff_volt) ;
cmd = ['VOLT ',num2str(act_volt+increment_voltage)];
writeline(v, cmd);
cnt = cnt+1;
if mod(cnt,100) == 0
pause(1);
end
% check if voltage is set
if act_volt ~= des_volt
hMsgBox = msgbox('An error occurred in dc supply module. Check if voltage is set correctly.');
uiwait(hMsgBox);
else
success(ch) = 1;
end
% get current voltage level
cmd = 'VOLT?';
writeline(v, cmd);
act_volt = str2num(readline(v));
end
% choose channel
cmd = ['INST:SEL ',char(strtrim(prev_selected_channel))];
writeline(v, cmd);
% check if voltage is set
if act_volt ~= des_volt
hMsgBox = msgbox('An error occurred in dc supply module. Check if voltage is set correctly.');
uiwait(hMsgBox);
else
success(ch) = 1;
end
%disconnect
delete(v);
catch
end
% choose channel
cmd = ['INST:SEL ',char(strtrim(prev_selected_channel))];
writeline(v, cmd);
%disconnect
delete(v);
end
function [voltage,current] = readVals(obj)

View File

@@ -1,8 +1,14 @@
classdef Exfo_laser
classdef Exfo_laser < handle
properties(Access=private)
serialport_number
mainframe_channel
end
properties(Access=public)
wavelength
power
cur_state
cur_wavelength
cur_power
safety_mode
end
methods (Access=public)
@@ -11,8 +17,9 @@ classdef Exfo_laser
arguments
options.wavelength = 1310; %dbm
options.power = -10; %dbm
options.safety_mode = 1;
options.serialport_number = 'COM8';
options.mainframe_channel = 1;
end
%
@@ -23,52 +30,330 @@ classdef Exfo_laser
end
end
end
function success = set(obj,options)
arguments
obj
options.wavelength = obj.wavelength; %dbm
options.power = obj.power; %dbm
end
% Connect to the laser
o = serialport("COM8", 9600);
configureTerminator(o, "CR"); % Set the terminator to carriage return (CR)
o = serialport(obj.serialport_number, 9600);
configureTerminator(o, "CR", "CR"); % Set the terminator to carriage return (CR)
flush(o);
writeline(o, "*IDN?");
pause(1);
if o.NumBytesAvailable ~= 0
disp(['Laser Mainframe: ', readline(o)]);
answ = readline(o);
if obj.safety_mode
disp(['Yay! We can talk to Instrument: ',char(answ)]);
end
% Read states the first time
obj.cur_state = obj.getLaserStatus_(o,obj.mainframe_channel);
obj.cur_wavelength = obj.getLaserWavelength_(o,obj.mainframe_channel);
obj.cur_power = obj.getLaserPower_(o,obj.mainframe_channel);
clear o;
if obj.safety_mode
warning("Safety_mode ON: Display all information. Ask when switching laser. Turn safety_mode off in class instance or during initialization Exfo_laser(...,'safety_mode',0)");
else
error('No connection to the mainframe');
clear o;
warning("safety_mode OFF: EVERYTHING IS EXECUTED WITHOUT ASKING :-) BE SURE WHAT YOU DO");
end
end
function [isEnabled,power,lambda] = getLaserInfo(obj)
o = obj.connectLaser_();
isEnabled = obj.getLaserStatus_(o,obj.mainframe_channel);
power = obj.getLaserPower_(o,obj.mainframe_channel);
lambda = obj.getLaserWavelength_(o,obj.mainframe_channel);
end
function success = enableLaser(obj)
success = 0;
o = obj.connectLaser_();
if obj.safety_mode
% Prompt the user to enable the laser
choice = questdlg('Do you want to enable the laser?', ...
'Enable Laser', ...
'Yes', 'No', 'No');
else
choice = 'Yes';
end
% Handle the response
switch choice
case 'Yes'
% Enable the laser on channel 1
success = obj.enableLaser_(o,obj.mainframe_channel);
disp('Laser enabled.');
case 'No'
% Abort the operation
disp('Operation aborted. Laser remains disabled.');
end
end
% Function to set the wavelength of the laser
function setLaserWavelength(~,serialObj, channel, wavelength)
command = ['CH', num2str(channel), ':L=', num2str(wavelength)];
writeline(serialObj, command);
pause(0.5); % Allow time for the wavelength to change
function success = disableLaser(obj)
success = 0;
o = obj.connectLaser_();
if obj.safety_mode
% Prompt the user to enable the laser
choice = questdlg('Do you want to disable the laser?', ...
'Enable Laser', ...
'Yes', 'No', 'No');
else
choice = 'Yes';
end
% Handle the response
switch choice
case 'Yes'
% Enable the laser on channel 1
success = obj.disableLaser_(o,obj.mainframe_channel);
case 'No'
% Abort the operation
disp('Operation aborted. Laser remains enabled.');
end
end
function success = setPower(obj,desiredPower)
success = 0;
o = obj.connectLaser_();
if obj.safety_mode
% Prompt the user to enable the laser
choice = questdlg(['Do you want to set the laser to ', num2str(desiredPower) ,' dBm?'], ...
'SET POWER', ...
'Yes', 'No', 'No');
else
choice = 'Yes';
end
% Handle the response
switch choice
case 'Yes'
% Enable the laser on channel 1
success = obj.setLaserPower_(o,obj.mainframe_channel,desiredPower);
case 'No'
% Abort the operation
disp('Operation aborted. Laser remains at power.');
end
end
function success = setWavelength(obj,desriedLambda)
success = 0;
o = obj.connectLaser_();
if obj.safety_mode
% Prompt the user to enable the laser
choice = questdlg(['Do you want to set the laser wavelength to ', num2str(desriedLambda) ,' nm?'], ...
'SET WAVELENGTH', ...
'Yes', 'No', 'No');
else
choice = 'Yes';
end
% Handle the response
switch choice
case 'Yes'
% Enable the laser on channel 1
success = obj.setLaserWavelength_(o,obj.mainframe_channel,desriedLambda);
case 'No'
% Abort the operation
disp('Operation aborted. Laser remains at wavelength.');
end
end
end %public mehtods
methods (Access=private)
function serialobj = connectLaser_(obj)
% Connect to the laser
serialobj = serialport(obj.serialport_number, 9600);
configureTerminator(serialobj, "CR", "CR"); % Set the terminator to carriage return (CR)
flush(serialobj);
writeline(serialobj, "*IDN?");
answ = readline(serialobj);
if obj.safety_mode
disp(['Yay! We can talk to Instrument: ',char(answ)]);
end
end
% Function to GET the STATE of the laser
function isEnabled = getLaserStatus_(obj,serialObj, channel)
writeline(serialObj, ['CH', num2str(channel), ':ENABLE?']); % Query current wavelength
manual_flush = 1;
while manual_flush
response = readline(serialObj);
manual_flush = contains(response, {'OK'});
end
isEnabled = 0;
isEnabled = contains(response, {'ENABLED'});
isDisabled = 0;
isDisabled = contains(response, {'DISABLED'});
if isEnabled && ~isDisabled
obj.cur_state = isEnabled;
elseif ~isEnabled && isDisabled
obj.cur_state = isEnabled;
else
error(['Unknown Laser State ->', char(response)]);
end
if obj.safety_mode
if isEnabled
disp(['Laser ', num2str(channel) ,' is currently ON -> ', char(response)]);
elseif isDisabled
disp(['Laser ', num2str(channel) ,' is currently OFF ->', char(response)]);
else
error(['Unknown Laser State ->', char(response)]);
end
end
end
% Function to GET the wavelength of the laser
function current_wavelen = getLaserWavelength_(obj,serialObj, channel)
writeline(serialObj, ['CH', num2str(channel), ':L?']); % Query current wavelength
current_wavelen = readline(serialObj);
disp(['Current Wavelength: ', current_wavelen]);
response = readline(serialObj);
current_wavelen = str2double(regexp(response, '\d+\.\d+', 'match', 'once'));
if obj.safety_mode
disp(['Current Laser ', num2str(channel) ,' Wavelength: ', num2str(current_wavelen),' --> ',char(response)]);
end
obj.cur_wavelength = current_wavelen;
end
% Function to set the laser power
function setLaserPower(~,serialObj, channel, power_dBm)
command = ['CH', num2str(channel), ':P=', num2str(power_dBm)];
writeline(serialObj, command);
pause(0.2); % Allow time for power to adjust
% Function to GET the laser power
function powerValue = getLaserPower_(obj,serialObj, channel)
writeline(serialObj, ['CH', num2str(channel), ':P?']); % Query current power
current_power = readline(serialObj);
disp(['Current Power: ', current_power, ' dBm']);
response = readline(serialObj);
isDisabled = contains(response, 'Disabled');
if ~isDisabled
powerValue = str2double(regexp(response, '\d+\.\d+', 'match', 'once'));
else
powerValue = -200;
end
if obj.safety_mode
disp(['Current Laser ', num2str(channel) ,' Power: ', num2str(powerValue), ' dBm --> ',char(response)]);
end
obj.cur_power = powerValue;
end
% Function to enable the laser
function success = enableLaser_(obj,serialObj,channel)
success = 0;
isEnabled = obj.getLaserStatus_(serialObj,obj.mainframe_channel);
writeline(serialObj, ['CH',num2str(channel),':ENABLE']);
response = readline(serialObj);
isok = contains(response, {'OK'});
cnt=0;
while ~isEnabled || cnt>10
isEnabled = obj.getLaserStatus_(serialObj,obj.mainframe_channel);
pause(0.2);
cnt = cnt+1;
end
if ~isEnabled
error('Laser not enabled but command was send')
else
success = 1;
end
end
% Function to enable the laser
function success = disableLaser_(obj,serialObj,channel)
success = 0;
isEnabled = obj.getLaserStatus_(serialObj,obj.mainframe_channel);
writeline(serialObj, ['CH',num2str(channel),':DISABLE']);
response = readline(serialObj);
isok = contains(response, {'OK'});
cnt=0;
while isEnabled || cnt>10
isEnabled = obj.getLaserStatus_(serialObj,obj.mainframe_channel);
pause(0.2);
cnt = cnt+1;
end
if isEnabled
error('Laser not enabled but command was send')
else
success = 1;
end
end
% Function to SET the wavelength of the laser
function success = setLaserWavelength_(~,serialObj, channel, desiredWavelength_nm)
success = 0;
writeline(serialObj, ['CH',num2str(channel),':NM?']);
modeResponse = readline(serialObj);
isNMMode = contains(modeResponse, ['CH',num2str(channel),':1']);
if ~isNMMode
warning(['Laser is in GHz Mode, you requested to set Wavelength (nm) -> ',modeResponse])
return
end
command = ['CH', num2str(channel), ':L=', num2str(desiredWavelength_nm)];
writeline(serialObj, command);
pause(0.2);
response = readline(serialObj);
success = contains(response, 'OK');
end
% Function to SET the laser power
function success = setLaserPower_(~,serialObj, channel, desiredPower_dBm)
success = 0;
writeline(serialObj, ['CH',num2str(channel),':MW?']);
modeResponse = readline(serialObj);
isMWMode = contains(modeResponse, ['CH',num2str(channel),':1']);
if isMWMode
warning('Laser is in MW Mode, you requested to set dBm power')
return
end
command = ['CH', num2str(channel), ':P=', num2str(desiredPower_dBm)];
writeline(serialObj, command);
pause(0.2);
response = readline(serialObj);
success = contains(response, 'OK');
end
end
end
end

View File

@@ -53,8 +53,11 @@ classdef OptAtten < handle
try
%connect to device
v = visadev('TCPIP::134.245.243.248::INSTR');
debug = 0;
if debug
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
@@ -193,8 +196,9 @@ classdef OptAtten < handle
differ = abs(state.outputpower(i)-options.value(i));
%create msgbox when necessary
if differ >= 2
hMsgBox = msgbox(['Output Power at attenuator slot ' num2str(2) ' differs by 2dB or more!']);
uiwait(hMsgBox);
warning(['Output Power at attenuator slot ' num2str(2) ' differs by ', num2str(differ), 'or more!'])
% hMsgBox = msgbox(['Output Power at attenuator slot ' num2str(2) ' differs by 2dB or more!']);
% uiwait(hMsgBox);
end
end
end
@@ -212,7 +216,7 @@ classdef OptAtten < handle
ch_info_txt = ['OptAtten: ',num2str(i),' -> Attenuated by: ',num2str(options.value(i)),' dB; Cur Output: ',num2str(state.outputpower(i)),'dBm'];
disp(ch_info_txt);
end
end
end
@@ -257,11 +261,6 @@ classdef OptAtten < handle
answer = sscanf(line,'%f');
obj.power_state(cnt) = answer;
writeline(v, [':READ' num2str(s) ':POW?']);
line = readline(v);
answer = sscanf(line,'%f');
obj.power_state(cnt) = answer;
writeline(v, [':INP' num2str(s) ':ATT?']);
line = readline(v);
answer = sscanf(line,'%f');
@@ -272,7 +271,7 @@ classdef OptAtten < handle
answer = sscanf(line,'%f');
obj.wavelength_state(cnt) = answer;
writeline(v, [':INP' num2str(s) ':WAV?']);
writeline(v, [':INP' num2str(s) ':ATT:SPE?']);
line = readline(v);
answer = sscanf(line,'%f');
obj.speed_state(cnt) = answer;
@@ -293,6 +292,25 @@ classdef OptAtten < handle
end
end
function [p1,p2,p3,p4]=readPower(obj)
v = visadev('TCPIP::134.245.243.248::INSTR');
cnt = 1;
for s = 1:2:7
writeline(v, [':READ' num2str(s) ':POW?']);
line = readline(v);
answer = sscanf(line,'%f');
obj.power_state(cnt) = answer;
cnt = cnt+1;
end
p1 = obj.power_state(1);
p2 = obj.power_state(2);
p3 = obj.power_state(3);
p4 = obj.power_state(4);
end
end
end

View File

@@ -12,6 +12,8 @@ classdef ScopeKeysight
interpolate
recordLen
IPaddress
waitUntilClick
end
methods (Access=public)
@@ -30,6 +32,7 @@ classdef ScopeKeysight
options.interpolate logical = 0;
options.recordLen double = 1000000;
options.IPaddress
options.waitUntilClick = 0;
end
%
@@ -55,6 +58,7 @@ classdef ScopeKeysight
arguments
obj
options.channel logical = obj.channel
options.waitUntilClick = obj.waitUntilClick;
end
%%%%%%%%%%%%%%%%%%%%%%%%
@@ -79,7 +83,7 @@ classdef ScopeKeysight
end
end
debug = 1;
debug = 0;
if debug
disp(['Connected to Instrument: ',char(v.Vendor),' ',char(v.Model),' SerNo:',char(v.SerialNumber)]);
end
@@ -119,14 +123,48 @@ classdef ScopeKeysight
obj.writeNcheck(v,sprintf(':CHANnel%u:DISPlay ON',n));% display captured data trace
end
set(v,'Timeout',150);
if obj.autoScale
obj.writeNcheck(v,':AUTOscale');
for n = 1:4
range = str2double(obj.writeReceiveCheck(v,sprintf(':CHANnel%u:RANGe?',n)));
obj.writeNcheck(v,sprintf(':CHANnel%u:RANGe %.3f',n,range/1.2));
%use this to finetune autoscaling - VERY helpful
% higher value leads to higher scaling
fintunefactor = 1.5; % within [1,...,2]
obj.writeNcheck(v,sprintf(':CHANnel%u:RANGe %.3f',n,range/fintunefactor));
end
else
obj.writeNcheck(v,':SINGLE');
%obj.writeNcheck(v,':SINGLE');
end
% After Autoscale, let the user adjust the scope scaling...
% "options.waitUntilClick" was a shit name but now this is it :-)
if options.waitUntilClick
if 0
% Create a dialog box with the desired text
d = dialog('Name', 'Scale Scope then press continue', 'Position', [300, 300, 300, 180]);
% Add a text label with the instruction
uicontrol('Parent', d, ...
'Style', 'text', ...
'Position', [20, 100, 260, 40], ...
'String', 'Please adjust scope scaling now, then press continue', ...
'HorizontalAlignment', 'center');
% Add a button to close the dialog and resume execution
uicontrol('Parent', d, ...
'Style', 'pushbutton', ...
'Position', [100, 40, 100, 40], ...
'String', 'Continue', ...
'Callback', 'uiresume(gcbf); delete(gcbf)');
% Pause execution until the dialog box is closed
uiwait(d);
else
holdAndShowValue
end
end
if obj.extRef

309
Classes/05_Lab/Thor_PDFA.m Normal file
View File

@@ -0,0 +1,309 @@
classdef Thor_PDFA < handle
properties(Access= public)
serialport_number
safety_mode
statusInfo
StatusRegisterValue
Interlock
TEC0_temp
TEC1_temp
Temp_stable
Temp_fault
LASER
LOS_Status
PumpLevel
end
methods (Access=public)
function obj = Thor_PDFA(options)
arguments
options.serialport_number = 'COM12'
options.safety_mode = 1;
end
%
fn = fieldnames(options);
for n = 1:numel(fn)
try
obj.(fn{n}) = options.(fn{n});
end
end
end
function o = connectSerial(obj)
% Connect to the PDFA
o = serialport(obj.serialport_number, 9600);
configureTerminator(o, "CR", "CR"); % Set the terminator to carriage return (CR)
flush(o);
end
function getStatus(obj)
o = obj.connectSerial();
obj.getStatus_(o);
if obj.safety_mode
obj
end
end
function enablePDFA(obj)
o = obj.connectSerial();
if obj.safety_mode
% Prompt the user to enable
choice = questdlg('Do you want to enable the PDFA?', ...
'TURN ON PUMP', ...
'PUMP IT!', 'No', 'No');
else
choice = 'PUMP IT!';
end
% Handle the response
switch choice
case 'PUMP IT!'
% Enable the pump
success = obj.enablePump_(o);
disp('PDFA enabled.');
case 'No'
% Abort the operation
disp('Operation aborted. Laser remains disabled.');
end
end
function disablePDFA(obj)
o = obj.connectSerial();
if obj.safety_mode
% Prompt the user to disable
choice = questdlg('Do you want to disable the PDFA?', ...
'TURN OFF PUMP', ...
'Turn off', 'No', 'No');
else
choice = 'Turn off';
end
% Handle the response
switch choice
case 'Turn off'
% Enable
success = obj.disablePump_(o);
disp('PDFA enabled.');
case 'No'
% Abort the operation
disp('Operation aborted. Laser remains disabled.');
end
end
function success = setPumpLevel(obj,desiredPump)
success = 0;
o = obj.connectSerial();
obj.setPumpLevel_(o,desiredPump);
end
function cleanedResponse = cleanResponse(~,responseToClean)
cleanedResponse = regexprep(responseToClean, '\x1B\[[0-9;]*[a-zA-Z]', '');
end
end
methods(Access= private)
function pumpLevel = readPumpLevel(obj,serialObj)
writeline(serialObj, "gloc");
pause(0.05);
response = read(serialObj,serialObj.NumBytesAvailable,"char");
cleanedResponse = obj.cleanResponse(response);
currentPercentage = regexp(cleanedResponse, '([0-9]+\.[0-9]+)%', 'tokens', 'once');
obj.PumpLevel = str2double(currentPercentage{1});
pumpLevel = obj.PumpLevel;
end
function getStatus_(obj,o)
writeline(o, "stat");
pause(0.5);
response = read(o,o.NumBytesAvailable,"char");
cleanedResponse = obj.cleanResponse(response);
% Parse and convert values
% Convert Status Register Value (hexadecimal to decimal)
statusValueHex = regexp(cleanedResponse, 'Status Register Value = (0x[0-9A-F]+)', 'tokens', 'once');
obj.StatusRegisterValue = hex2dec(statusValueHex{1});
% Convert Interlock status to logical (Closed = true, Open = false)
interlockStatus = regexp(cleanedResponse, 'Interlock\s*:\s*(\w+)', 'tokens', 'once');
obj.Interlock = strcmp(interlockStatus{1}, 'Closed');
% Convert TEC0 and TEC1 temp to logical (Good = true, Fault = false)
tec0Temp = regexp(cleanedResponse, 'TEC0 temp\s*:\s*(\w+)', 'tokens', 'once');
obj.TEC0_temp = strcmp(tec0Temp{1}, 'Good');
tec1Temp = regexp(cleanedResponse, 'TEC1 temp\s*:\s*(\w+)', 'tokens', 'once');
obj.TEC1_temp = strcmp(tec1Temp{1}, 'Good');
% Convert Temp stable status to logical (Stable = true, Unstable = false)
tempStable = regexp(cleanedResponse, 'Temp stable\s*:\s*(\w+)', 'tokens', 'once');
obj.Temp_stable = strcmp(tempStable{1}, 'Stable');
% Convert Temp fault status to logical (No Fault = false, Fault = true)
tempFault = regexp(cleanedResponse, 'Temp fault\s*:\s*(\w+)', 'tokens', 'once');
obj.Temp_fault = strcmp(tempFault{1}, 'Fault');
% Convert LASER status to logical (OFF = false, ON = true)
laserStatus = regexp(cleanedResponse, 'LASER\s*:\s*(\w+)', 'tokens', 'once');
obj.LASER = strcmp(laserStatus{1}, 'ON');
% Convert LOS Status to logical (Good = true, Faulty = false)
losStatus = regexp(cleanedResponse, 'LOS Status\s*:\s*(\w+)', 'tokens', 'once');
obj.LOS_Status = strcmp(losStatus{1}, 'Good');
writeline(o, "gloc");
pause(0.5);
response = read(o,o.NumBytesAvailable,"char");
cleanedResponse = obj.cleanResponse(response);
% Use a regular expression to extract the operating current value in percentage
currentPercentage = regexp(cleanedResponse, '([0-9]+\.[0-9]+)%', 'tokens', 'once');
% Convert the extracted percentage to a numeric value
obj.PumpLevel = str2double(currentPercentage{1});
end
function success = enablePump_(obj,o)
success = 0;
curPump = obj.readPumpLevel(o);
if curPump ~= 0
cnt = 0;
pumpSetToZero = 0;
while ~pumpSetToZero
pumpZero = 0;
pumpSetToZero = obj.setPumpLevel_(o,pumpZero);
cnt = cnt+1;
end
end
% set pump on
writeline(o, 'le');
pause(0.5);
response = read(o,o.NumBytesAvailable,"char");
cleanedResponse = obj.cleanResponse(response);
isEnabled = contains(cleanedResponse, {'ENABLE LASER'});
pause(5);
obj.getStatus_(o);
if isEnabled && obj.LASER
success = 1;
else
error('Could not enable the pump....');
end
end
function success = disablePump_(obj,o)
success = 0;
curPump = obj.readPumpLevel(o);
if curPump ~= 0
cnt = 0;
pumpSetToZero = 0;
while ~pumpSetToZero
pumpZero = 0;
pumpSetToZero = obj.setPumpLevel_(o,pumpZero);
cnt = cnt+1;
end
end
% set pump on
writeline(o, 'ld');
pause(0.5);
response = read(o,o.NumBytesAvailable,"char");
cleanedResponse = obj.cleanResponse(response);
isDisabled = contains(cleanedResponse, {'DISABLE LASER'});
pause(5);
obj.getStatus_(o);
if isDisabled && ~obj.LASER
success = 1;
else
error('Could not disable the pump....');
end
end
function success = setPumpLevel_(obj,o,desiredPump)
success = 0;
curPump = obj.readPumpLevel(o);
while abs(curPump-desiredPump) > 0
% difference
diff_pump = curPump-desiredPump;
% set new pump
increment_pump = -1 * sign(diff_pump) ;
% set new incr. pump
writeline(o, ['sloc ',num2str(curPump+increment_pump)]);
if increment_pump < 0
pause(0.1);
else
pause(0.3);
end
response = read(o,o.NumBytesAvailable,"char");
cleanedResponse = obj.cleanResponse(response);
curPump = str2double(regexp(cleanedResponse, '([0-9]+\.[0-9]+)%', 'tokens', 'once'));
end
curPump = obj.readPumpLevel(o);
if obj.PumpLevel == desiredPump
success = 1;
else
warning('Pump nicht richtig?')
end
end
end
end

View File

@@ -1,246 +0,0 @@
classdef DataStorage < handle
%DATASTORAGE Summary of this class goes here
% Detailed explanation goes here
properties
inputParams = struct;
parameter = struct;
fn = [];
dim = [];
sto = {};
% getPhysForIndex = struct;
% getIndexForPhys = struct;
end
methods
function obj = DataStorage(inputParams)
%DATASTORAGE Construct an instance of this class
% Detailed explanation goes here
% scheiß Variablenname
obj.inputParams = inputParams;
% Field Names
obj.fn = string(fieldnames(inputParams));
% _______________
% Two dicts that map between physical and array index :-)
% Dictionary: 10 km -> 3
% obj.getIndexForPhys = obj.buildIndexDict();
% Dictionary: 3 -> 10Km
% obj.getPhysForIndex = obj.buildPhysDict();
% _______________
% This is the 2nd Idea -> ecery given Param will be a class
% instance of "Parameter", therin user can access the dicts and
% informations... Have not decided which way is best..
obj = obj.buildParameter();
% get dimension of dataStorage
% e.g. if we have L = [1,2,10,80] and D=[8, 17, 21], we would
% need an array with dimesion [4,3].
obj.dim = obj.getDimension();
% finally, create the main storage as cell array
obj.sto = struct;
end
function save(obj,path)
try
save(path,"obj");
catch
end
end
function showInfo(obj)
disp("Data Structure with fields:");
fprintf('%-12s', 'Name'); fprintf('%1s', '| '); fprintf('%0s ', 'Dimension'); fprintf('%4s', '| '); fprintf('%0s ', 'Physical Values'); fprintf('\n');
disp('----------------------------------------------------------------');
for i = 1:numel(obj.fn)
fprintf('%-12s', char(obj.fn(i))); fprintf('%1s', '| '); fprintf('%8.1i ', obj.dim(i)); fprintf('%5s', '| '); fprintf('%-7s ', string(obj.parameter.(obj.fn(i)).values) ); fprintf('\n');
end
disp('----------------------------------------------------------------');
stofn = string(fieldnames(obj.sto));
for s = 1:numel(stofn)
nonempty = numel(find(~cellfun(@isempty,obj.sto.(stofn(s)))));
overall = numel(obj.sto.(stofn(s)));
fprintf('%-8s', 'Storage '); fprintf('%-10s', char(stofn(s))); fprintf('%4s', 'filled with '); fprintf('%-5s', num2str(nonempty)); fprintf('%-1s', ' entries -> '); fprintf('%-5s', num2str(nonempty/overall*100)); fprintf('%-1s', '% filled'); fprintf('\n');
end
end
function dim = getDimension(obj)
dim = zeros(1,numel(obj.fn));
for p = 1:numel(obj.fn)
%loop all parameter names and add their length :-)
dim(p)=obj.parameter.(obj.fn(p)).length;
end
end
function obj = buildParameter(obj)
for p = 1:numel(obj.fn)
name = obj.fn(p);
values = obj.inputParams.(name);
obj.parameter.(name) = Parameter(name,values);
end
end
function addStorage(obj,varName)
% add a storage
storage = cell(obj.dim);
obj.sto.(string(varName)) = storage;
end
function addValueToStorage(obj, valueToStore ,storageVarName, varargin)
if nargin-3 == numel(obj.fn)
lin_idx = obj.getIndicesByPhys(varargin);
obj.sto.(storageVarName){lin_idx} = valueToStore;
else
error('Specify all the indices to access the right place in storage!')
end
end
% Access Value(s)
function value = getStoValue(obj,storageVarName, varargin)
if nargin-2 == numel(obj.fn)
%es wurden aausreichend argumente übergeben :-)
%es gibt jedoch erstmal keinen Check ob die Reihenfolge
%richtig ist
value = [];
lin_idx = obj.getIndicesByPhys(varargin);
errcnt = 0;
for i=1:numel(lin_idx)
% try
tmp = obj.sto.(storageVarName){lin_idx(i)};
if ~isempty(tmp)
if isa(tmp,'Signal')
if i == 1
value = {};
end
value{i} = tmp ;
elseif isa(tmp,'cell')
if isa(tmp{1},'Signal')
if i == 1
value = {};
end
value{i} = tmp{1} ;
end
else
value(i,:) = tmp ;
end
else
errcnt = errcnt+1;
value(i,:) = NaN ;
if errcnt < 3
%get back the n-dimensional subiondices...
% [sub{1:length(size(obj.sto.(storageVarName)))}] = ind2sub(size(obj.sto.(storageVarName)),lin_idx(i));
%get back the physical representaion
% word = [];
% for phys_idx = 1:numel(obj.fn)
% parametername = obj.fn(phys_idx);
% word = [word,char(parametername),': ', num2str(obj.parameter.(parametername).getPhysForIndex(sub{phys_idx})),' ;'];
% end
% warning(['Requested Data is not in Warehouse ', word]);
elseif errcnt == 3
% warning(['... ', word]);
end
end
if errcnt > 2
% warning([num2str(errcnt),' requested datapoint(s) not in warehouse.']);
end
% catch
% error('Error in Datastorage: Something happened while looking up in warehouse.')
% end
end
else
error('Wrong Request using ExampleWarehouse.getStoValue(*parameter set*). Give me all the Parameters! Please!')
end
end
% Mapping for several Indices (calls the mapping for single index)
function lin_idx = getIndicesByPhys(obj,varargin)
%map _all_ phys. to indices - several calls of single mapping
inputsz = cellfun(@size,varargin{1},'UniformOutput',false);
inputmax = cell2mat(cellfun(@max,inputsz,'UniformOutput',false));
% I wrote this method for a single query, then refined it for vectorial
% inputs (which work fine), but lastly recognized that I
% destroyed single query... however, this if / else will fix it!
if sum(inputmax)==length(inputmax)
vecQuery = 1; %set any value to one
else
vecQuery = find(inputmax~=1); %position of vectorial queries
end
q={};
for i = 1:numel(vecQuery)
q{i} = varargin{1,1}{vecQuery(i)}; %the two vectors
end
combination = combvec(q{:}); %combine all possible combinations
for c = 1:length(combination)
%loop over all possible combinations
indices = {};
str = [];
for r = 1:numel(vecQuery)
%replace varargin with current query (could have been renamed... however it works)
varargin{1}{vecQuery(r)} = combination(r,c);
end
for p = 1:numel(obj.fn)
curPhysQuery = varargin{1}{p}; %can be: a) single value // b) range
curParameterName = obj.fn(p);
indices{end+1} = obj.getIndexByPhys(curParameterName,curPhysQuery);
% str = [str, ',indices{', num2str(p), '}'];
end
%append to index list :-)
fn_=fieldnames(obj.sto);
n_ = fn_{1};
lin_idx(c,:) = sub2ind(size(obj.sto.(n_)),indices{:});
% lin_idx(c,:) = eval(['sub2ind(size(obj.sto.',n_,')',str,');']);
end
end
% Mapping for single Index
function idx = getIndexByPhys(obj,fieldname,phys)
%map single phys to index
idx = obj.parameter.(fieldname).getIndexForPhys(phys);
end
end
end

View File

@@ -0,0 +1,24 @@
function channelplan_nm = calcWavelengthPlan(N, df_hz, center_nm)
vec = [N:-1:1]-(N/2+0.5);
channelplan_hz = nm2hz(center_nm) + (vec * df_hz) ;
channelplan_nm = hz2nm(channelplan_hz);
% dfft = 1.367053998632946e+07; %hz
% a = diff(channelplan_hz)./2./dfft;
end
function hz = nm2hz(nm)
wavelen_in_m = nm.* 1e-9;
hz = (299792458 ./ wavelen_in_m); %frequency in Terahertz
end
function nm = hz2nm(hz)
m = (299792458 ./ hz); %wavelength in meter
nm = m .* 10^9;
end

View File

@@ -0,0 +1,11 @@
Copyright 2016 Bastian Bechtold
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,60 @@
# Violin Plots for Matlab
A violin plot is an easy to read substitute for a box plot that
replaces the box shape with a kernel density estimate of the data, and
optionally overlays the data points itself. The original boxplot shape
is still included as a grey box/line in the center of the violin.
Violin plots are a superset of box plots, and give a much richer
understanding of the data distribution, while not taking more space.
You will be able to instantly spot too-sparse data, or multi-modal
distributions, which could go unnoticed in boxplots.
`violinplot` is meant as a direct substitute for `boxplot` (excluding
named arguments). Additional constructor parameters include the width
of the plot, the bandwidth of the kernel density estimation, and the
X-axis position of the violin plot.
For more information about violin plots, read "[Violin plots: a box
plot-density trace synergism](http://www.stat.cmu.edu/~rnugent/PCMI2016/papers/ViolinPlots.pdf)"
by J. L. Hintze and R. D. Nelson in The American Statistician, vol.
52, no. 2, pp. 181-184, 1998 (DOI: 10.2307/2685478).
For a simple call:
```matlab
load carbig MPG Origin
Origin = cellstr(Origin);
figure
vs = violinplot(MPG, Origin);
ylabel('Fuel Economy in MPG');
xlim([0.5, 7.5]);
```
![example image](./example.png)
You can also play around with the different options, and tune your violin plots to your liking.
```matlab
grouporder={'England','Sweden','Japan','Italy','Germany','France','USA'};
vs = Violin({MPG(strcmp(Origin, grouporder{pos}))},...
position,...
'HalfViolin','right',...% left, full
'QuartileStyle','shadow',... % boxplot, none
'DataStyle', 'histogram',... % scatter, none
'ShowNotches', false,...
'ShowMean', false,...
'ShowMedian', true,...
'ViolinColor', color);
```
![example image2](example2.png)
## Citation
[![DOI](https://zenodo.org/badge/60771923.svg)](https://zenodo.org/badge/latestdoi/60771923)
If you want to cite this repository, use
> Bechtold, Bastian, 2016. Violin Plots for Matlab, Github Project
> https://github.com/bastibe/Violinplot-Matlab, DOI: 10.5281/zenodo.4559847

View File

@@ -0,0 +1,713 @@
classdef Violin < handle
% Violin creates violin plots for some data
% A violin plot is an easy to read substitute for a box plot
% that replaces the box shape with a kernel density estimate of
% the data, and optionally overlays the data points itself.
% It is also possible to provide two sets of data which are supposed
% to be compared by plotting each column of the two datasets together
% on each side of the violin.
%
% Additional constructor parameters include the width of the
% plot, the bandwidth of the kernel density estimation, the
% X-axis position of the violin plot, and the categories.
%
% Use <a href="matlab:help('violinplot')">violinplot</a> for a
% <a href="matlab:help('boxplot')">boxplot</a>-like wrapper for
% interactive plotting.
%
% See for more information on Violin Plots:
% J. L. Hintze and R. D. Nelson, "Violin plots: a box
% plot-density trace synergism," The American Statistician, vol.
% 52, no. 2, pp. 181-184, 1998.
%
% Violin Properties:
% ViolinColor - Fill color of the violin area and data points.
% Can be either a matrix nx3 or an array of up to two
% cells containing nx3 matrices.
% Defaults to the next default color cycle.
% ViolinAlpha - Transparency of the violin area and data points.
% Can be either a single scalar value or an array of
% up to two cells containing scalar values.
% Defaults to 0.3.
% EdgeColor - Color of the violin area outline.
% Defaults to [0.5 0.5 0.5]
% BoxColor - Color of the box, whiskers, and the outlines of
% the median point and the notch indicators.
% Defaults to [0.5 0.5 0.5]
% MedianColor - Fill color of the median and notch indicators.
% Defaults to [1 1 1]
% ShowData - Whether to show data points.
% Defaults to true
% ShowNotches - Whether to show notch indicators.
% Defaults to false
% ShowMean - Whether to show mean indicator.
% Defaults to false
% ShowBox - Whether to show the box.
% Defaults to true
% ShowMedian - Whether to show the median indicator.
% Defaults to true
% ShowWhiskers - Whether to show the whiskers
% Defaults to true
% HalfViolin - Whether to do a half violin(left, right side) or
% full. Defaults to full.
% QuartileStyle - Option on how to display quartiles, with a
% boxplot, shadow or none. Defaults to boxplot.
% DataStyle - Defines the style to show the data points. Opts:
% 'scatter', 'histogram' or 'none'. Default is 'scatter'.
%
%
% Violin Children:
% ScatterPlot - <a href="matlab:help('scatter')">scatter</a> plot of the data points
% ScatterPlot2 - <a href="matlab:help('scatter')">scatter</a> second plot of the data points
% ViolinPlot - <a href="matlab:help('fill')">fill</a> plot of the kernel density estimate
% ViolinPlot2 - <a href="matlab:help('fill')">fill</a> second plot of the kernel density estimate
% BoxPlot - <a href="matlab:help('fill')">fill</a> plot of the box between the quartiles
% WhiskerPlot - line <a href="matlab:help('plot')">plot</a> between the whisker ends
% MedianPlot - <a href="matlab:help('scatter')">scatter</a> plot of the median (one point)
% NotchPlots - <a href="matlab:help('scatter')">scatter</a> plots for the notch indicators
% MeanPlot - line <a href="matlab:help('plot')">plot</a> at mean value
% Copyright (c) 2016, Bastian Bechtold
% This code is released under the terms of the BSD 3-clause license
properties (Access=public)
ScatterPlot % scatter plot of the data points
ScatterPlot2 % comparison scatter plot of the data points
ViolinPlot % fill plot of the kernel density estimate
ViolinPlot2 % comparison fill plot of the kernel density estimate
BoxPlot % fill plot of the box between the quartiles
WhiskerPlot % line plot between the whisker ends
MedianPlot % scatter plot of the median (one point)
NotchPlots % scatter plots for the notch indicators
MeanPlot % line plot of the mean (horizontal line)
HistogramPlot % histogram of the data
ViolinPlotQ % fill plot of the Quartiles as shadow
end
properties (Dependent=true)
ViolinColor % fill color of the violin area and data points
ViolinAlpha % transparency of the violin area and data points
MarkerSize % marker size for the data dots
MedianMarkerSize % marker size for the median dot
LineWidth % linewidth of the median plot
EdgeColor % color of the violin area outline
BoxColor % color of box, whiskers, and median/notch edges
BoxWidth % width of box between the quartiles in axis space (default 10% of Violin plot width, 0.03)
MedianColor % fill color of median and notches
ShowData % whether to show data points
ShowNotches % whether to show notch indicators
ShowMean % whether to show mean indicator
ShowBox % whether to show the box
ShowMedian % whether to show the median line
ShowWhiskers % whether to show the whiskers
HalfViolin % whether to do a half violin(left, right side) or full
end
methods
function obj = Violin(data, pos, varargin)
%Violin plots a violin plot of some data at pos
% VIOLIN(DATA, POS) plots a violin at x-position POS for
% a vector of DATA points.
%
% VIOLIN(..., 'PARAM1', val1, 'PARAM2', val2, ...)
% specifies optional name/value pairs:
% 'Width' Width of the violin in axis space.
% Defaults to 0.3
% 'Bandwidth' Bandwidth of the kernel density
% estimate. Should be between 10% and
% 40% of the data range.
% 'ViolinColor' Fill color of the violin area
% and data points.Can be either a matrix
% nx3 or an array of up to two cells
% containing nx3 matrices.
% 'ViolinAlpha' Transparency of the violin area and data
% points. Can be either a single scalar
% value or an array of up to two cells
% containing scalar values. Defaults to 0.3.
% 'MarkerSize' Size of the data points, if shown.
% Defaults to 24
% 'MedianMarkerSize' Size of the median indicator, if shown.
% Defaults to 36
% 'EdgeColor' Color of the violin area outline.
% Defaults to [0.5 0.5 0.5]
% 'BoxColor' Color of the box, whiskers, and the
% outlines of the median point and the
% notch indicators. Defaults to
% [0.5 0.5 0.5]
% 'MedianColor' Fill color of the median and notch
% indicators. Defaults to [1 1 1]
% 'ShowData' Whether to show data points.
% Defaults to true
% 'ShowNotches' Whether to show notch indicators.
% Defaults to false
% 'ShowMean' Whether to show mean indicator.
% Defaults to false
% 'ShowBox' Whether to show the box
% Defaults to true
% 'ShowMedian' Whether to show the median line
% Defaults to true
% 'ShowWhiskers' Whether to show the whiskers
% Defaults to true
% 'HalfViolin' Whether to do a half violin(left, right side) or
% full. Defaults to full.
% 'QuartileStyle' Option on how to display quartiles, with a
% boxplot or as a shadow. Defaults to boxplot.
% 'DataStyle' Defines the style to show the data points. Opts:
% 'scatter', 'histogram' or 'none'. Default is 'Scatter'.
st = dbstack; % get the calling function for reporting errors
namefun = st.name;
args = obj.checkInputs(data, pos, varargin{:});
if length(data)==1
data2 = [];
data = data{1};
else
data2 = data{2};
data = data{1};
end
if isempty(args.ViolinColor)
Release= strsplit(version('-release'), {'a','b'}); %Check release
if str2num(Release{1})> 2019 || strcmp(version('-release'), '2019b')
C = colororder;
else
C = lines;
end
if pos > length(C)
C = lines;
end
args.ViolinColor = {repmat(C,ceil(size(data,2)/length(C)),1)};
end
data = data(not(isnan(data)));
data2 = data2(not(isnan(data2)));
if numel(data) == 1
obj.MedianPlot = scatter(pos, data, 'filled');
obj.MedianColor = args.MedianColor;
obj.MedianPlot.MarkerEdgeColor = args.EdgeColor;
return
end
hold('on');
%% Calculate kernel density estimation for the violin
[density, value, width] = obj.calcKernelDensity(data, args.Bandwidth, args.Width);
% also calculate the kernel density of the comparison data if
% provided
if ~isempty(data2)
[densityC, valueC, widthC] = obj.calcKernelDensity(data2, args.Bandwidth, args.Width);
end
%% Plot the data points within the violin area
if length(density) > 1
[~, unique_idx] = unique(value);
jitterstrength = interp1(value(unique_idx), density(unique_idx)*width, data, 'linear','extrap');
else % all data is identical:
jitterstrength = density*width;
end
if isempty(data2) % if no comparison data
jitter = 2*(rand(size(data))-0.5); % both sides
else
jitter = rand(size(data)); % only right side
end
switch args.HalfViolin % this is more modular
case 'left'
jitter = -1*(rand(size(data))); %left
case 'right'
jitter = 1*(rand(size(data))); %right
case 'full'
jitter = 2*(rand(size(data))-0.5);
end
% Make scatter plot
switch args.DataStyle
case 'scatter'
if ~isempty(data2)
jitter = 1*(rand(size(data))); %right
obj.ScatterPlot = ...
scatter(pos + jitter.*jitterstrength, data, args.MarkerSize, 'filled');
% plot the data points within the violin area
if length(densityC) > 1
jitterstrength = interp1(valueC, densityC*widthC, data2);
else % all data is identical:
jitterstrength = densityC*widthC;
end
jitter = -1*rand(size(data2));% left
obj.ScatterPlot2 = ...
scatter(pos + jitter.*jitterstrength, data2, args.MarkerSize, 'filled');
else
obj.ScatterPlot = ...
scatter(pos + jitter.*jitterstrength, data, args.MarkerSize, 'filled');
end
case 'histogram'
[counts,edges] = histcounts(data, size(unique(data),1));
switch args.HalfViolin
case 'right'
obj.HistogramPlot= plot([pos-((counts')/max(counts))*max(jitterstrength)*2, pos*ones(size(counts,2),1)]',...
[edges(1:end-1)+max(diff(edges))/2; edges(1:end-1)+max(diff(edges))/2],'-','LineWidth',1, 'Color', 'k');
case 'left'
obj.HistogramPlot= plot([pos*ones(size(counts,2),1), pos+((counts')/max(counts))*max(jitterstrength)*2]',...
[edges(1:end-1)+max(diff(edges))/2; edges(1:end-1)+max(diff(edges))/2],'-','LineWidth',1, 'Color', 'k');
otherwise
fprintf([namefun, ' No histogram/bar plot option available for full violins, as it would look overcrowded.\n'])
end
case 'none'
end
%% Plot the violin
halfViol= ones(1, size(density,2));
if isempty(data2) % if no comparison data
switch args.HalfViolin
case 'right'
obj.ViolinPlot = ... % plot color will be overwritten later
fill([pos+density*width halfViol*pos], ...
[value value(end:-1:1)], [1 1 1],'LineWidth',1);
case 'left'
obj.ViolinPlot = ... % plot color will be overwritten later
fill([halfViol*pos pos-density(end:-1:1)*width], ...
[value value(end:-1:1)], [1 1 1],'LineWidth',1);
case 'full'
obj.ViolinPlot = ... % plot color will be overwritten later
fill([pos+density*width pos-density(end:-1:1)*width], ...
[value value(end:-1:1)], [1 1 1],'LineWidth',1);
end
else
% plot right half of the violin
obj.ViolinPlot = ...
fill([pos+density*width pos-density(1)*width], ...
[value value(1)], [1 1 1],'LineWidth',1);
% plot left half of the violin
obj.ViolinPlot2 = ...
fill([pos-densityC(end)*widthC pos-densityC(end:-1:1)*widthC], ...
[valueC(end) valueC(end:-1:1)], [1 1 1],'LineWidth',1);
end
%% Plot the quartiles within the violin
quartiles = quantile(data, [0.25, 0.5, 0.75]);
flat= [halfViol*pos halfViol*pos];
switch args.QuartileStyle
case 'shadow'
switch args.HalfViolin
case 'right'
w = [pos+density*width halfViol*pos];
h= [value value(end:-1:1)];
case 'left'
w = [halfViol*pos pos-density(end:-1:1)*width];
h= [value value(end:-1:1)];
case 'full'
w = [pos+density*width pos-density(end:-1:1)*width];
h= [value value(end:-1:1)];
end
w(h<quartiles(1))=flat(h<quartiles(1));
w(h>quartiles(3))=flat((h>quartiles(3)));
obj.ViolinPlotQ = ... % plot color will be overwritten later
fill(w, ...
h, [1 1 1]);
case 'boxplot'
obj.BoxPlot = ... % plot color will be overwritten later
fill(pos+[-1,1,1,-1]*args.BoxWidth, ...
[quartiles(1) quartiles(1) quartiles(3) quartiles(3)], ...
[1 1 1]);
case 'none'
end
%% Plot the data mean
meanValue = mean(data);
if length(density) > 1
[~, unique_idx] = unique(value);
meanDensityWidth = interp1(value(unique_idx), density(unique_idx), meanValue, 'linear','extrap')*width;
else % all data is identical:
meanDensityWidth = density*width;
end
if meanDensityWidth<args.BoxWidth/2
meanDensityWidth=args.BoxWidth/2;
end
switch args.HalfViolin
case 'right'
obj.MeanPlot = plot(pos+[0,1].*meanDensityWidth, ...
[meanValue, meanValue]);
case 'left'
obj.MeanPlot = plot(pos+[-1,0].*meanDensityWidth, ...
[meanValue, meanValue]);
case 'full'
obj.MeanPlot = plot(pos+[-1,1].*meanDensityWidth, ...
[meanValue, meanValue]);
end
obj.MeanPlot.LineWidth = 1;
%% Plot the median, notch, and whiskers
IQR = quartiles(3) - quartiles(1);
lowhisker = quartiles(1) - 1.5*IQR;
lowhisker = max(lowhisker, min(data(data > lowhisker)));
hiwhisker = quartiles(3) + 1.5*IQR;
hiwhisker = min(hiwhisker, max(data(data < hiwhisker)));
if ~isempty(lowhisker) && ~isempty(hiwhisker)
obj.WhiskerPlot = plot([pos pos], [lowhisker hiwhisker]);
end
% Median
obj.MedianPlot = scatter(pos, quartiles(2), args.MedianMarkerSize, [1 1 1], 'filled');
% Notches
obj.NotchPlots = ...
scatter(pos, quartiles(2)-1.57*IQR/sqrt(length(data)), ...
[], [1 1 1], 'filled', '^');
obj.NotchPlots(2) = ...
scatter(pos, quartiles(2)+1.57*IQR/sqrt(length(data)), ...
[], [1 1 1], 'filled', 'v');
%% Set graphical preferences
obj.EdgeColor = args.EdgeColor;
obj.MedianPlot.LineWidth = args.LineWidth;
obj.BoxColor = args.BoxColor;
obj.BoxWidth = args.BoxWidth;
obj.MedianColor = args.MedianColor;
obj.ShowData = args.ShowData;
obj.ShowNotches = args.ShowNotches;
obj.ShowMean = args.ShowMean;
obj.ShowBox = args.ShowBox;
obj.ShowMedian = args.ShowMedian;
obj.ShowWhiskers = args.ShowWhiskers;
if not(isempty(args.ViolinColor))
if size(args.ViolinColor{1},1) > 1
ViolinColor{1} = args.ViolinColor{1}(pos,:);
else
ViolinColor{1} = args.ViolinColor{1};
end
if length(args.ViolinColor)==2
if size(args.ViolinColor{2},1) > 1
ViolinColor{2} = args.ViolinColor{2}(pos,:);
else
ViolinColor{2} = args.ViolinColor{2};
end
else
ViolinColor{2} = ViolinColor{1};
end
else
% defaults
if args.scpltBool
ViolinColor{1} = obj.ScatterPlot.CData;
else
ViolinColor{1} = [0 0 0];
end
ViolinColor{2} = [0 0 0];
end
obj.ViolinColor = ViolinColor;
if not(isempty(args.ViolinAlpha))
if length(args.ViolinAlpha{1})>1
error('Only scalar values are accepted for the alpha color channel');
else
ViolinAlpha{1} = args.ViolinAlpha{1};
end
if length(args.ViolinAlpha)==2
if length(args.ViolinAlpha{2})>1
error('Only scalar values are accepted for the alpha color channel');
else
ViolinAlpha{2} = args.ViolinAlpha{2};
end
else
ViolinAlpha{2} = ViolinAlpha{1}/2; % default unless specified
end
else
% default
ViolinAlpha = {1,1};
end
obj.ViolinAlpha = ViolinAlpha;
end
%% SET METHODS
function set.EdgeColor(obj, color)
if ~isempty(obj.ViolinPlot)
obj.ViolinPlot.EdgeColor = color;
obj.ViolinPlotQ.EdgeColor = color;
if ~isempty(obj.ViolinPlot2)
obj.ViolinPlot2.EdgeColor = color;
end
end
end
function color = get.EdgeColor(obj)
if ~isempty(obj.ViolinPlot)
color = obj.ViolinPlot.EdgeColor;
end
end
function set.MedianColor(obj, color)
obj.MedianPlot.MarkerFaceColor = color;
if ~isempty(obj.NotchPlots)
obj.NotchPlots(1).MarkerFaceColor = color;
obj.NotchPlots(2).MarkerFaceColor = color;
end
end
function color = get.MedianColor(obj)
color = obj.MedianPlot.MarkerFaceColor;
end
function set.BoxColor(obj, color)
if ~isempty(obj.BoxPlot)
obj.BoxPlot.FaceColor = color;
obj.BoxPlot.EdgeColor = color;
obj.WhiskerPlot.Color = color;
obj.MedianPlot.MarkerEdgeColor = color;
obj.NotchPlots(1).MarkerFaceColor = color;
obj.NotchPlots(2).MarkerFaceColor = color;
elseif ~isempty(obj.ViolinPlotQ)
obj.WhiskerPlot.Color = color;
obj.MedianPlot.MarkerEdgeColor = color;
obj.NotchPlots(1).MarkerFaceColor = color;
obj.NotchPlots(2).MarkerFaceColor = color;
end
end
function color = get.BoxColor(obj)
if ~isempty(obj.BoxPlot)
color = obj.BoxPlot.FaceColor;
end
end
function set.BoxWidth(obj,width)
if ~isempty(obj.BoxPlot)
pos=mean(obj.BoxPlot.XData);
obj.BoxPlot.XData=pos+[-1,1,1,-1]*width;
end
end
function width = get.BoxWidth(obj)
width=max(obj.BoxPlot.XData)-min(obj.BoxPlot.XData);
end
function set.ViolinColor(obj, color)
obj.ViolinPlot.FaceColor = color{1};
obj.ScatterPlot.MarkerFaceColor = color{1};
obj.MeanPlot.Color = color{1};
if ~isempty(obj.ViolinPlot2)
obj.ViolinPlot2.FaceColor = color{2};
obj.ScatterPlot2.MarkerFaceColor = color{2};
end
if ~isempty(obj.ViolinPlotQ)
obj.ViolinPlotQ.FaceColor = color{1};
end
for idx = 1: size(obj.HistogramPlot,1)
obj.HistogramPlot(idx).Color = color{1};
end
end
function color = get.ViolinColor(obj)
color{1} = obj.ViolinPlot.FaceColor;
if ~isempty(obj.ViolinPlot2)
color{2} = obj.ViolinPlot2.FaceColor;
end
end
function set.ViolinAlpha(obj, alpha)
obj.ViolinPlotQ.FaceAlpha = .65;
obj.ViolinPlot.FaceAlpha = alpha{1};
obj.ScatterPlot.MarkerFaceAlpha = 1;
if ~isempty(obj.ViolinPlot2)
obj.ViolinPlot2.FaceAlpha = alpha{2};
obj.ScatterPlot2.MarkerFaceAlpha = 1;
end
end
function alpha = get.ViolinAlpha(obj)
alpha{1} = obj.ViolinPlot.FaceAlpha;
if ~isempty(obj.ViolinPlot2)
alpha{2} = obj.ViolinPlot2.FaceAlpha;
end
end
function set.ShowData(obj, yesno)
if yesno
obj.ScatterPlot.Visible = 'on';
for idx = 1: size(obj.HistogramPlot,1)
obj.HistogramPlot(idx).Visible = 'on';
end
else
obj.ScatterPlot.Visible = 'off';
for idx = 1: size(obj.HistogramPlot,1)
obj.HistogramPlot(idx).Visible = 'off';
end
end
if ~isempty(obj.ScatterPlot2)
obj.ScatterPlot2.Visible = obj.ScatterPlot.Visible;
end
end
function yesno = get.ShowData(obj)
if ~isempty(obj.ScatterPlot)
yesno = strcmp(obj.ScatterPlot.Visible, 'on');
end
end
function set.ShowNotches(obj, yesno)
if ~isempty(obj.NotchPlots)
if yesno
obj.NotchPlots(1).Visible = 'on';
obj.NotchPlots(2).Visible = 'on';
else
obj.NotchPlots(1).Visible = 'off';
obj.NotchPlots(2).Visible = 'off';
end
end
end
function yesno = get.ShowNotches(obj)
if ~isempty(obj.NotchPlots)
yesno = strcmp(obj.NotchPlots(1).Visible, 'on');
end
end
function set.ShowMean(obj, yesno)
if ~isempty(obj.MeanPlot)
if yesno
obj.MeanPlot.Visible = 'on';
else
obj.MeanPlot.Visible = 'off';
end
end
end
function yesno = get.ShowMean(obj)
if ~isempty(obj.BoxPlot)
yesno = strcmp(obj.BoxPlot.Visible, 'on');
end
end
function set.ShowBox(obj, yesno)
if ~isempty(obj.BoxPlot)
if yesno
obj.BoxPlot.Visible = 'on';
else
obj.BoxPlot.Visible = 'off';
end
end
end
function yesno = get.ShowBox(obj)
if ~isempty(obj.BoxPlot)
yesno = strcmp(obj.BoxPlot.Visible, 'on');
end
end
function set.ShowMedian(obj, yesno)
if ~isempty(obj.MedianPlot)
if yesno
obj.MedianPlot.Visible = 'on';
else
obj.MedianPlot.Visible = 'off';
end
end
end
function yesno = get.ShowMedian(obj)
if ~isempty(obj.MedianPlot)
yesno = strcmp(obj.MedianPlot.Visible, 'on');
end
end
function set.ShowWhiskers(obj, yesno)
if ~isempty(obj.WhiskerPlot)
if yesno
obj.WhiskerPlot.Visible = 'on';
else
obj.WhiskerPlot.Visible = 'off';
end
end
end
function yesno = get.ShowWhiskers(obj)
if ~isempty(obj.WhiskerPlot)
yesno = strcmp(obj.WhiskerPlot.Visible, 'on');
end
end
end
methods (Access=private)
function results = checkInputs(~, data, pos, varargin)
isscalarnumber = @(x) (isnumeric(x) & isscalar(x));
p = inputParser();
p.addRequired('Data', @(x)isnumeric(vertcat(x{:})));
p.addRequired('Pos', isscalarnumber);
p.addParameter('Width', 0.3, isscalarnumber);
p.addParameter('Bandwidth', [], isscalarnumber);
iscolor = @(x) (isnumeric(x) & size(x,2) == 3);
p.addParameter('ViolinColor', [], @(x)iscolor(vertcat(x{:})));
p.addParameter('MarkerSize', 24, @isnumeric);
p.addParameter('MedianMarkerSize', 36, @isnumeric);
p.addParameter('LineWidth', 0.75, @isnumeric);
p.addParameter('BoxColor', [0.5 0.5 0.5], iscolor);
p.addParameter('BoxWidth', 0.01, isscalarnumber);
p.addParameter('EdgeColor', [0.5 0.5 0.5], iscolor);
p.addParameter('MedianColor', [1 1 1], iscolor);
p.addParameter('ViolinAlpha', {0.3,0.15}, @(x)isnumeric(vertcat(x{:})));
isscalarlogical = @(x) (islogical(x) & isscalar(x));
p.addParameter('ShowData', true, isscalarlogical);
p.addParameter('ShowNotches', false, isscalarlogical);
p.addParameter('ShowMean', false, isscalarlogical);
p.addParameter('ShowBox', true, isscalarlogical);
p.addParameter('ShowMedian', true, isscalarlogical);
p.addParameter('ShowWhiskers', true, isscalarlogical);
validSides={'full', 'right', 'left'};
checkSide = @(x) any(validatestring(x, validSides));
p.addParameter('HalfViolin', 'full', checkSide);
validQuartileStyles={'boxplot', 'shadow', 'none'};
checkQuartile = @(x)any(validatestring(x, validQuartileStyles));
p.addParameter('QuartileStyle', 'boxplot', checkQuartile);
validDataStyles = {'scatter', 'histogram', 'none'};
checkStyle = @(x)any(validatestring(x, validDataStyles));
p.addParameter('DataStyle', 'scatter', checkStyle);
p.parse(data, pos, varargin{:});
results = p.Results;
end
end
methods (Static)
function [density, value, width] = calcKernelDensity(data, bandwidth, width)
if isempty(data)
error('Empty input data');
end
[density, value] = ksdensity(data, 'bandwidth', bandwidth);
density = density(value >= min(data) & value <= max(data));
value = value(value >= min(data) & value <= max(data));
value(1) = min(data);
value(end) = max(data);
value = [value(1)*(1-1E-5), value, value(end)*(1+1E-5)];
density = [0, density, 0];
% all data is identical
if min(data) == max(data)
density = 1;
value= mean(value);
end
width = width/max(density);
end
end
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,87 @@
function testviolinplot()
figure();
% One could use tiled layout for better plotting them but it would be
% incompatible with older versions
subplot(2,4,1);
% TEST CASE 1
disp('Test 1: Violin plot default options');
load carbig MPG Origin
Origin = cellstr(Origin);
vs = violinplot(MPG, Origin);
plotdetails(1);
% TEST CASE 2
disp('Test 2: Test the plot ordering option');
grouporder={'USA','Sweden','Japan','Italy','Germany','France','England'};
subplot(2,4,2);
vs2 = violinplot(MPG,Origin,'GroupOrder',grouporder);
plotdetails(2);
% TEST CASE 3
disp('Test 3: Test the numeric input construction mode');
subplot(2,4,3);
cats = categorical(Origin);
catnames = (unique(cats)); % this ignores categories without any data
catnames_labels = {};
thisData = NaN(length(MPG),length(catnames));
for n = 1:length(catnames)
thisCat = catnames(n);
catnames_labels{n} = char(thisCat);
thisData(1:length(MPG(cats == thisCat)),n) = MPG(cats == thisCat);
end
vs3 = violinplot(thisData,catnames_labels);
plotdetails(3);
% TEST CASE 4
disp('Test 4: Test two sided violin plots. Japan is being compared.');
subplot(2,4,4);
C = colororder;
vs4 = violinplot({thisData,repmat(thisData(:,5),1,7)},catnames_labels,'ViolinColor',{C,C(5,:)},'ViolinAlpha',{0.3 0.3}, 'ShowMean', true, 'MarkerSize',8);
plotdetails(4);
% TEST CASE 5
disp('Test 5: Test shadow for quartiles.');
subplot(2,4,5);
vs5 = violinplot(MPG, Origin, 'QuartileStyle','shadow');
plotdetails(5);
% TEST CASE 6
disp('Test 6: Test plotting only right side & histogram plot, with quartiles as boxplot.');
subplot(2,4,6);
vs5 = violinplot(MPG, Origin, 'QuartileStyle','boxplot', 'HalfViolin','right',...
'DataStyle', 'histogram');
plotdetails(6);
% TEST CASE 7
disp('Test 7: Test plotting only left side & histogram plot, and quartiles as shadow.');
subplot(2,4,7);
vs5 = violinplot(MPG, Origin, 'QuartileStyle','shadow', 'HalfViolin','left',...
'DataStyle', 'histogram', 'ShowMean', true);
plotdetails(7);
% TEST CASE 8
disp('Test 8: Same as previous one, just removing the data of half of the violins afterwards.');
subplot(2,4,8);
vs5 = violinplot([MPG; 5;5;5;5;5], [Origin; 'test';'test';'test';'test';'test'], 'QuartileStyle','shadow', 'HalfViolin','full',...
'DataStyle', 'scatter', 'ShowMean', false);
plotdetails(8);
for n= 1:round(length(vs5)/2)
vs5(1,n).ShowData = 0;
end
xlim([0, 9]);
%other test cases could be added here
end
function plotdetails(n)
title(sprintf('Test %02.0f \n',n));
ylabel('Fuel Economy in MPG ');
xlim([0, 8]); grid minor;
set(gca, 'color', 'none');
xtickangle(-30);
fprintf('Test %02.0f passed ok! \n ',n);
end

View File

@@ -0,0 +1,193 @@
function violins = violinplot(data, cats, varargin)
%Violinplots plots violin plots of some data and categories
% VIOLINPLOT(DATA) plots a violin of a double vector DATA
%
% VIOLINPLOT(DATAMATRIX) plots violins for each column in
% DATAMATRIX.
%
% VIOLINPLOT(DATAMATRIX, CATEGORYNAMES) plots violins for each
% column in DATAMATRIX and labels them according to the names in the
% cell-of-strings CATEGORYNAMES.
%
% In the cases above DATA and DATAMATRIX can be a vector or a matrix,
% respectively, either as is or wrapped in a cell.
% To produce violins which have one distribution on one half and another
% one on the other half, DATA and DATAMATRIX have to be cell arrays
% with two elements, each containing a vector or a matrix. The number of
% columns of the two data sets has to be the same.
%
% VIOLINPLOT(DATA, CATEGORIES) where double vector DATA and vector
% CATEGORIES are of equal length; plots violins for each category in
% DATA.
%
% VIOLINPLOT(TABLE), VIOLINPLOT(STRUCT), VIOLINPLOT(DATASET)
% plots violins for each column in TABLE, each field in STRUCT, and
% each variable in DATASET. The violins are labeled according to
% the table/dataset variable name or the struct field name.
%
% violins = VIOLINPLOT(...) returns an object array of
% <a href="matlab:help('Violin')">Violin</a> objects.
%
% VIOLINPLOT(..., 'PARAM1', val1, 'PARAM2', val2, ...)
% specifies optional name/value pairs for all violins:
% 'Width' Width of the violin in axis space.
% Defaults to 0.3
% 'Bandwidth' Bandwidth of the kernel density estimate.
% Should be between 10% and 40% of the data range.
% 'ViolinColor' Fill color of the violin area and data points. Accepts
% 1x3 color vector or nx3 color vector where n = num
% groups. In case of two data sets being compared it can
% be an array of up to two cells containing nx3
% matrices.
% Defaults to the next default color cycle.
% 'ViolinAlpha' Transparency of the violin area and data points.
% Can be either a single scalar value or an array of
% up to two cells containing scalar values.
% Defaults to 0.3.
% 'MarkerSize' Size of the data points, if shown.
% Defaults to 24
% 'MedianMarkerSize' Size of the median indicator, if shown.
% Defaults to 36
% 'EdgeColor' Color of the violin area outline.
% Defaults to [0.5 0.5 0.5]
% 'BoxColor' Color of the box, whiskers, and the outlines of
% the median point and the notch indicators.
% Defaults to [0.5 0.5 0.5]
% 'MedianColor' Fill color of the median and notch indicators.
% Defaults to [1 1 1]
% 'ShowData' Whether to show data points.
% Defaults to true
% 'ShowNotches' Whether to show notch indicators.
% Defaults to false
% 'ShowMean' Whether to show mean indicator
% Defaults to false
% 'ShowBox' Whether to show the box.
% Defaults to true
% 'ShowMedian' Whether to show the median indicator.
% Defaults to true
% 'ShowWhiskers' Whether to show the whiskers
% Defaults to true
% 'GroupOrder' Cell of category names in order to be plotted.
% Defaults to alphabetical ordering
% Copyright (c) 2016, Bastian Bechtold
% This code is released under the terms of the BSD 3-clause license
hascategories = exist('cats','var') && not(isempty(cats));
%parse the optional grouporder argument
%if it exists parse the categories order
% but also delete it from the arguments passed to Violin
grouporder = {};
idx=find(strcmp(varargin, 'GroupOrder'));
if ~isempty(idx) && numel(varargin)>idx
if iscell(varargin{idx+1})
grouporder = varargin{idx+1};
varargin(idx:idx+1)=[];
else
error('Second argument of ''GroupOrder'' optional arg must be a cell of category names')
end
end
% check and correct the structure of ViolinColor input
idx=find(strcmp(varargin, 'ViolinColor'));
if ~isempty(idx) && iscell(varargin{idx+1})
if length(varargin{idx+1}(:))>2
error('ViolinColor input can be at most a two element cell array');
end
elseif ~isempty(idx) && isnumeric(varargin{idx+1})
varargin{idx+1} = varargin(idx+1);
end
% check and correct the structure of ViolinAlpha input
idx=find(strcmp(varargin, 'ViolinAlpha'));
if ~isempty(idx) && iscell(varargin{idx+1})
if length(varargin{idx+1}(:))>2
error('ViolinAlpha input can be at most a two element cell array');
end
elseif ~isempty(idx) && isnumeric(varargin{idx+1})
varargin{idx+1} = varargin(idx+1);
end
% tabular data
if isa(data, 'dataset') || isstruct(data) || istable(data)
if isa(data, 'dataset')
colnames = data.Properties.VarNames;
elseif istable(data)
colnames = data.Properties.VariableNames;
elseif isstruct(data)
colnames = fieldnames(data);
end
catnames = {};
if isempty(grouporder)
for n=1:length(colnames)
if isnumeric(data.(colnames{n}))
catnames = [catnames colnames{n}]; %#ok<*AGROW>
end
end
catnames = sort(catnames);
else
for n=1:length(grouporder)
if isnumeric(data.(grouporder{n}))
catnames = [catnames grouporder{n}];
end
end
end
for n=1:length(catnames)
thisData = data.(catnames{n});
violins(n) = Violin({thisData}, n, varargin{:});
end
set(gca, 'XTick', 1:length(catnames), 'XTickLabels', catnames);
set(gca,'Box','on');
return
elseif iscell(data) && length(data(:))==2 % cell input
if not(size(data{1},2)==size(data{2},2))
error('The two input data matrices have to have the same number of columns');
end
elseif iscell(data) && length(data(:))>2 % cell input
error('Up to two datasets can be compared');
elseif isnumeric(data) % numeric input
% 1D data, one category for each data point
if hascategories && numel(data) == numel(cats)
if isempty(grouporder)
cats = categorical(cats);
else
cats = categorical(cats, grouporder);
end
catnames = (unique(cats)); % this ignores categories without any data
catnames_labels = {};
for n = 1:length(catnames)
thisCat = catnames(n);
catnames_labels{n} = char(thisCat);
thisData = data(cats == thisCat);
violins(n) = Violin({thisData}, n, varargin{:});
end
set(gca, 'XTick', 1:length(catnames), 'XTickLabels', catnames_labels);
set(gca,'Box','on');
return
else
data = {data};
end
end
% 1D data, no categories
if not(hascategories) && isvector(data{1})
violins = Violin(data, 1, varargin{:});
set(gca, 'XTick', 1);
% 2D data with or without categories
elseif ismatrix(data{1})
for n=1:size(data{1}, 2)
thisData = cellfun(@(x)x(:,n),data,'UniformOutput',false);
violins(n) = Violin(thisData, n, varargin{:});
end
set(gca, 'XTick', 1:size(data{1}, 2));
if hascategories && length(cats) == size(data{1}, 2)
set(gca, 'XTickLabels', cats);
end
end
set(gca,'Box','on');
end

View File

@@ -0,0 +1,115 @@
function autoArrangeFigures(NH, NW, monitor_id)
% INPUT :
% NH : number of grid of vertical direction
% NW : number of grid of horizontal direction
% OUTPUT :
%
% get every figures that are opened now and arrange them.
%
% autoArrangeFigures selects automatically Monitor1.
% If you are dual(or more than that) monitor user, I recommend to set wide
% monitor as Monitor1.
%
% if you want arrange automatically, type 'autoArrangeFigures(0,0)' or 'autoArrangeFigures()'.
% But maximum number of figures for automatic mode is 27.
%
% if you want specify grid for figures, give numbers for parameters.
% but if your grid size is smaller than required one for accommodating
% all figures, this function changes to automatic mode and if more
% figures are opend than maximum number, then it gives error.
%
% Notes
% + 2017.1.20 use monitor id(Adam Danz's idea)
%
% leejaejun, Koreatech, Korea Republic, 2014.12.13
% jaejun0201@gmail.com
if nargin < 2
NH = 0;
NW = 0;
monitor_id = 1;
end
task_bar_offset = [30 50];
%%
N_FIG = NH * NW;
if N_FIG == 0
autoArrange = 1;
else
autoArrange = 0;
end
figHandle = sortFigureHandles(findobj('Type','figure'));
moveitHandle = findall(figHandle, 'Type', 'figure', 'Tag', 'Hfroot');
figHandle = figHandle(ismember(figHandle,moveitHandle)~=1);
n_fig = size(figHandle,1);
if n_fig <= 0
warning('figures are not found');
return
end
screen_sz = get(0,'MonitorPositions');
screen_sz = screen_sz(monitor_id, :);
scn_w = screen_sz(3) - task_bar_offset(1);
scn_h = screen_sz(4) - task_bar_offset(2);
scn_w_begin = screen_sz(1) + task_bar_offset(1);
scn_h_begin = screen_sz(2) + task_bar_offset(2);
if autoArrange==0
if n_fig > N_FIG
autoArrange = 1;
warning('too many figures than you told. change to autoArrange');
else
nh = NH;
nw = NW;
end
end
if autoArrange == 1
grid = [2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4;
3 3 3 3 3 3 3 3 4 4 4 5 5 5 5 5 5 5 5 6 6 6 7 7 7 7 7]';
if n_fig > length(grid)
warning('too many figures(maximum = %d)',length(grid))
return
end
if scn_w > scn_h
nh = grid(n_fig,1);
nw = grid(n_fig,2);
else
nh = grid(n_fig,2);
nw = grid(n_fig,1);
end
end
fig_width = scn_w/nw;
fig_height = scn_h/nh;
fig_cnt = 1;
for i=1:1:nh
for k=1:1:nw
if fig_cnt>n_fig
return
end
fig_pos = [scn_w_begin+fig_width*(k-1) ...
scn_h_begin+scn_h-fig_height*i ...
fig_width ...
fig_height];
set(figHandle(fig_cnt),'OuterPosition',fig_pos);
figure(figHandle(fig_cnt)); %bring to top
fig_cnt = fig_cnt + 1;
end
end
end
function figSorted = sortFigureHandles(figs)
[tmp, idx] = sort([figs.Number]);
figSorted = figs(idx);
end

View File

@@ -0,0 +1,45 @@
# MATLAB #
##########
# Editor autosave files
*~
*.asv
# Compiled MEX binaries (all platforms)
*.mex*
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

View File

@@ -0,0 +1,37 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

View File

@@ -0,0 +1,161 @@
<html xmlns:mwsh="http://www.mathworks.com/namespace/mcode/v1/syntaxhighlight.dtd">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--
This HTML is auto-generated from an M-file.
To make changes, update the M-file and republish this document.
-->
<title>inpaint_nans_demo</title>
<meta name="generator" content="MATLAB 7.0.1">
<meta name="date" content="2006-06-28">
<meta name="m-file" content="inpaint_nans_demo"><style>
body {
background-color: white;
margin:10px;
}
h1 {
color: #990000;
font-size: x-large;
}
h2 {
color: #990000;
font-size: medium;
}
p.footer {
text-align: right;
font-size: xx-small;
font-weight: lighter;
font-style: italic;
color: gray;
}
pre.codeinput {
margin-left: 30px;
}
span.keyword {color: #0000FF}
span.comment {color: #228B22}
span.string {color: #A020F0}
span.untermstring {color: #B20000}
span.syscmd {color: #B28C00}
pre.showbuttons {
margin-left: 30px;
border: solid black 2px;
padding: 4px;
background: #EBEFF3;
}
pre.codeoutput {
color: gray;
font-style: italic;
}
pre.error {
color: red;
}
/* Make the text shrink to fit narrow windows, but not stretch too far in
wide windows. On Gecko-based browsers, the shrink-to-fit doesn't work. */
p,h1,h2,div {
/* for MATLAB's browser */
width: 600px;
/* for Mozilla, but the "width" tag overrides it anyway */
max-width: 600px;
/* for IE */
width:expression(document.body.clientWidth > 620 ? "600px": "auto" );
}
</style></head>
<body><pre class="codeinput"><span class="comment">% Surface fit artifact removal</span>
[x,y] = meshgrid(0:.01:1);
z0 = exp(x+y);
znan = z0;
znan(20:50,40:70) = NaN;
znan(30:90,5:10) = NaN;
znan(70:75,40:90) = NaN;
z = inpaint_nans(znan,3);
<span class="comment">% Comparison to griddata</span>
k = isnan(znan);
zk = griddata(x(~k),y(~k),z(~k),x(k),y(k));
zg = znan;
zg(k) = zk;
close <span class="string">all</span>
figure
surf(z0)
title <span class="string">'Original surface'</span>
figure
surf(znan)
title <span class="string">'Artifacts (large holes) in surface'</span>
figure
surf(zg)
title([<span class="string">'Griddata inpainting ('</span>,num2str(sum(isnan(zg(:)))),<span class="string">' NaNs remain)'</span>])
figure
surf(z)
title <span class="string">'Inpainted surface'</span>
figure
surf(zg-z0)
title <span class="string">'Griddata error surface'</span>
figure
surf(z-z0)
title <span class="string">'Inpainting error surface (Note z-axis scale)'</span>
</pre><img vspace="5" hspace="5" src="inpaint_nans_demo_01.png"> <img vspace="5" hspace="5" src="inpaint_nans_demo_02.png"> <img vspace="5" hspace="5" src="inpaint_nans_demo_03.png"> <img vspace="5" hspace="5" src="inpaint_nans_demo_04.png"> <img vspace="5" hspace="5" src="inpaint_nans_demo_05.png"> <img vspace="5" hspace="5" src="inpaint_nans_demo_06.png"> <p class="footer"><br>
Published with MATLAB&reg; 7.0.1<br></p>
<!--
##### SOURCE BEGIN #####
% Surface fit artifact removal
[x,y] = meshgrid(0:.01:1);
z0 = exp(x+y);
znan = z0;
znan(20:50,40:70) = NaN;
znan(30:90,5:10) = NaN;
znan(70:75,40:90) = NaN;
z = inpaint_nans(znan,3);
% Comparison to griddata
k = isnan(znan);
zk = griddata(x(~k),y(~k),z(~k),x(k),y(k));
zg = znan;
zg(k) = zk;
close all
figure
surf(z0)
title 'Original surface'
figure
surf(znan)
title 'Artifacts (large holes) in surface'
figure
surf(zg)
title(['Griddata inpainting (',num2str(sum(isnan(zg(:)))),' NaNs remain)'])
figure
surf(z)
title 'Inpainted surface'
figure
surf(zg-z0)
title 'Griddata error surface'
figure
surf(z-z0)
title 'Inpainting error surface (Note z-axis scale)'
##### SOURCE END #####
-->
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,42 @@
% Surface fit artifact removal
[x,y] = meshgrid(0:.01:1);
z0 = exp(x+y);
znan = z0;
znan(20:50,40:70) = NaN;
znan(30:90,5:10) = NaN;
znan(70:75,40:90) = NaN;
z = inpaint_nans(znan,3);
% Comparison to griddata
k = isnan(znan);
zk = griddata(x(~k),y(~k),z(~k),x(k),y(k));
zg = znan;
zg(k) = zk;
close all
figure
surf(z0)
title 'Original surface'
figure
surf(znan)
title 'Artifacts (large holes) in surface'
figure
surf(zg)
title(['Griddata inpainting (',num2str(sum(isnan(zg(:)))),' NaNs remain)'])
figure
surf(z)
title 'Inpainted surface'
figure
surf(zg-z0)
title 'Griddata error surface'
figure
surf(z-z0)
title 'Inpainting error surface (Note z-axis scale)'

View File

@@ -0,0 +1,39 @@
{\rtf1\mac\ansicpg10000\cocoartf102
{\fonttbl\f0\fswiss\fcharset77 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\margl1440\margr1440\vieww10780\viewh13720\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\ql\qnatural
\f0\fs24 \cf0 Nomination comments:\
\
Inpaint_nans fills a hole in matlab. (Yes, the pun was intentional.) But there\
is indeed a niche that inpaint_nans falls into.\
\
The alternative to inpaint_nans is griddata (interp1 can be used for the 1-d \
problems) but griddata fails to extrapolate well. Griddata also has serious\
problems when its data already lies on a grid, due to its use of a Delaunay \
triangulation. The other serious problem with the use of griddata is the\
triangulation itself. The shape of the hole to be filled can sometimes result\
in triangles with a poor aspect ratio (long, thin triangles) which are in turn\
poor for interpolation. In fact, Griddata can even leave interior points\
uninterpolated (see the tests.)\
\
A future plan for inpaint_nans is to add an option that will use a locally\
anisotropic membrane model. This will allow better modeling for certain\
classes of wavy surfaces. I'm also highly tempted to remove method 5.\
I've never really liked it, having put it in at the request of one user. It has\
no valid theory behind it in the context of inpaint_nans.\
\
In the interest of openness, I'll also say what inpaint_nans does not do. It\
does not handle non-uniform grids. It is limited by the amount of memory \
in the size of the arrays it can handle, although some of the methods were\
explicitly provided to be more memory efficient than others. Inpaint_nans\
also makes heavy use of sparse matrices, so surprisingly large problems\
are accessible.\
\
Finally, while inpaint_nans does work for 1-d problems, they are not my\
target. Interp1 (with 'spline' as the method) is as accurate, and should be\
faster in general.\
\
John\
}

View File

@@ -0,0 +1,187 @@
%{
The methods of inpaint_nans
Digital inpainting is the craft of replacing missing elements in an
"image" array. A Google search on the words "digita inpainting" will turn
up many hits. I just tried this search and found 18300 hits.
If you wish to do inpainting in matlab, one place to start is with my
inpaint_nans code. Inpaint_nans is on the file exchange:
http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=4551&objectType=file
It looks for NaN elements in an array (or vector) and attempts to interpolate
(or extrapolate) smoothly to replace those elements.
The name "inpainting" itself comes from the world of art restoration.
Damaged paintings are restored by an artist/craftsman skilled in matching
the style of the original artist to fill in any holes in the painting.
In digital inpainting, the goal is to interpolate in from the boundaries
of a hole to smoothly replace an artifact. Obviously, where the hole is
large the digitally inpainted repair may not be an accurate approximation
to the original.
Inpaint_nans itself is really only a boundary value solver. The basic idea
is to formulate a partial differential equation (PDE) that is assumed to
apply in the domain of the artifact to be inpainted. The perimeter of the
hole supplies boundary values for the PDE. Then the PDE is approximated
using finite difference methods (the array elements are assumed to be
equally spaced in each dimension) and then a large (and very sparse) linear
system of equations is solved for the NaN elements in the array.
I've chosen a variety of simple differental equation models the user can
specify to be solved. All the methods current use a basically elliptic
PDE. This means that the resulting linear system will generally be well
conditioned. It does mean that the solution will generally be fairly smooth,
and over large holes, it will tend towards an average of the boundary
elements. These are characteristics of the elliptic PDEs chosen. (My hope
is to expand these options in the future.)
%}
%%
% Lets formulate a simple problem, and see how we could solve it using
% some of these ideas.
A = [0 0 0 0;1 NaN NaN 4;2 3 5 8];
% Although we can't plot this matrix using the functions surf or mesh,
% surely we can visualize what the fudamental shape is.
% There are only two unknown elements, the artifacts that inpaint_nans
% would fill in: A(2,2) and A(2,3).
% For an equally spaced grid, the Laplacian equation (or Poisson's equation
% of heat conduction at steady state if you prefer. Or, for the fickle,
% Ficke's law of diffusion would apply.) All of these result in the PDE
%
% u_xx + u_yy = 0
%
% where u_xx is the second partial derivative of u with respect to x,
% and u_yy is the second partial with respect to y.
%
% Approximating this PDE using finite differences for the partial
% derivatives, implies that at any node in the grid, we could replace
% it by the average of its 4 neighbors. Thus the two NaN elements
% generate two linear equations:
%
% A(2,2) = (A(1,2) + A(3,2) + A(2,1) + A(2,3)) / 4
% A(2,3) = (A(1,3) + A(3,3) + A(2,2) + A(2,4)) / 4
%
% Since we know all the parameters but A(2,2) and A(2,3), substitute their
% known values.
%
% A(2,2) = (0 + 3 + 1 + A(2,3)) / 4
% A(2,3) = (0 + 5 + A(2,2) + 4) / 4
%
% Or,
%
% 4*A(2,2) - A(2,3) = 4
% -A(2,2) + 4*A(2,3) = 9
%
% We can solve for the unkowns now using
u = [4 -1;-1 4]\[4;9]
A(2,2) = u(1);
A(2,3) = u(2);
% and finally plot the surface
close
surf(A)
title 'A simply inpainted surface'
% Neat huh? For an arbitrary number of NaN elements in an array,
% the above scheme is all there is to method 2 of inpaint_nans,
% together with a very slick application of sparse linear algebra
% in Matlab.
% Method 0 is very similar, but I've optimized it to build as
% small a linear system as possible for those cases where an array
% has only a few NaN elements.
% Method 1 is another subtle variation on this scheme, but it
% tries to be slightly smoother at some cost of efficiency, while
% still not modifying the known (non-NaN) elements of the array.
% Method 5 of inpaint_nans is also very similar to method 2, except
% that it uses a simple average of all 8 neighbors of an element.
% Its not actually an approximation to our PDE.
% Method 3 is yet another variation on this theme, except the PDE
% model used is one more suited to a model of a thin plate than for
% heat diffusion. Here the governing PDE is:
%
% u_xxxx + 2*u_xxyy + u_yyyy = 0
%
% again discretized into a linear system of equations.
%%
% Finally, method 4 of inpaint_nans has a different underlying
% model. Pretend that each element in the array was connected to
% its immediate neighbors to the left, right, up, and down by
% "springs". They are also connected to their neighbors at 45
% degree angles by springs with a weaker spring constant. Since
% the potential energy stored in a spring is proportional to its
% extension, we can formulate this again as a linear system of
% equations to be solved. For the example above, we would generate
% the set of equations:
% A(2,2) - A(1,2) = 0
% A(2,2) - A(2,1) = 0
% A(2,2) - A(3,2) = 0
% A(2,2) - A(2,3) = 0
% (A(2,2) - A(1,1))/sqrt(2) = 0
% (A(2,2) - A(1,3))/sqrt(2) = 0
% (A(2,2) - A(3,1))/sqrt(2) = 0
% (A(2,2) - A(3,3))/sqrt(2) = 0
% A(2,3) - A(1,3) = 0
% A(2,3) - A(2,2) = 0
% A(2,3) - A(3,3) = 0
% A(2,3) - A(2,4) = 0
% (A(2,3) - A(1,2))/sqrt(2) = 0
% (A(2,3) - A(1,4))/sqrt(2) = 0
% (A(2,3) - A(3,2))/sqrt(2) = 0
% (A(2,3) - A(3,4))/sqrt(2) = 0
% Substitute for the known elements to get
% A(2,2) - 0 = 0
% A(2,2) - 1 = 0
% A(2,2) - 3 = 0
% A(2,2) - A(2,3) = 0
% (A(2,2) - 0)/sqrt(2) = 0
% (A(2,2) - 0)/sqrt(2) = 0
% (A(2,2) - 2)/sqrt(2) = 0
% (A(2,2) - 5)/sqrt(2) = 0
% A(2,3) - 0 = 0
% A(2,3) - A(2,2) = 0
% A(2,3) - 5 = 0
% A(2,3) - 4 = 0
% (A(2,3) - 0)/sqrt(2) = 0
% (A(2,3) - 0)/sqrt(2) = 0
% (A(2,3) - 3)/sqrt(2) = 0
% (A(2,3) - 8)/sqrt(2) = 0
% This system is also solvable now:
r2 = 1/sqrt(2);
M=[1 0;1 0;1 0;1 -1;r2 0;r2 0;r2 0;r2 0;0 1;-1 1;0 1;0 1;0 r2;0 r2;0 r2;0 r2];
v = M\[0 1 3 0 0 0 2*r2 5*r2 0 0 5 4 0 0 3*r2 8*r2]'
A(2,2) = v(1);
A(2,3) = v(2);
% and finally plot the surface
surf(A)
title 'A simply inpainted surface using a spring model'
%%
% Why did I provide this approach, based on a spring metaphor?
% As you should have observed, methods 2 and 4 are really quite close
% in what they do for internal NaN elements. Its on the perimeter that
% they differ significantly. The diffusion/Laplacian model will
% extrapolate smoothly, and as linearly as possible. The spring model
% will tend to extrapolate as a constant function.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,43 @@
%% Surface Fit Artifact Removal
%% Construct the Surface
[x,y] = meshgrid(0:.01:1);
z0 = exp(x+y);
close all
figure
surf(z0)
title 'Original surface'
znan = z0;
znan(20:50,40:70) = NaN;
znan(30:90,5:10) = NaN;
znan(70:75,40:90) = NaN;
figure
surf(znan)
title 'Artifacts (large holes) in surface'
%% In-paint Over NaNs
z = inpaint_nans(znan,3);
figure
surf(z)
title 'Inpainted surface'
figure
surf(z-z0)
title 'Inpainting error surface (Note z-axis scale)'
%% Comapre to GRIDDATA
k = isnan(znan);
zk = griddata(x(~k),y(~k),z(~k),x(k),y(k));
zg = znan;
zg(k) = zk;
figure
surf(zg)
title(['Griddata inpainting (',num2str(sum(isnan(zg(:)))),' NaNs remain)'])
figure
surf(zg-z0)
title 'Griddata error surface'

View File

@@ -0,0 +1,24 @@
Copyright (c) 2009, John D'Errico
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View File

@@ -0,0 +1,178 @@
%% Repair to an image with 50% random artifacts
% Garden at Sainte-Adresse (Monet, 1867)
garden = imread('monet_adresse.jpg');
G = double(garden);
G(rand(size(G))<0.50) = NaN;
Gnan = G;
G(:,:,1) = inpaint_nans(G(:,:,1),2);
G(:,:,2) = inpaint_nans(G(:,:,2),2);
G(:,:,3) = inpaint_nans(G(:,:,3),2);
figure
subplot(1,3,1)
image(garden)
title 'Garden at Sainte-Adresse (Monet)'
subplot(1,3,2)
image(uint8(Gnan))
title 'Corrupted - 50%'
subplot(1,3,3)
image(uint8(G))
title 'Inpainted Garden'
%% Surface fit artifact removal
[x,y] = meshgrid(0:.01:1);
z0 = exp(x+y);
znan = z0;
znan(20:50,40:70) = NaN;
znan(30:90,5:10) = NaN;
znan(70:75,40:90) = NaN;
tic,z = inpaint_nans(znan,3);toc
tic
k = isnan(znan);
zk = griddata(x(~k),y(~k),z(~k),x(k),y(k));
zg = znan;
zg(k) = zk;
toc
figure
surf(z0)
title 'Original surface'
figure
surf(znan)
title 'Artifacts (large holes) in surface'
figure
surf(zg)
title(['Griddata inpainting (',num2str(sum(isnan(zg(:)))),' NaNs remain)'])
figure
surf(z)
title 'Inpainted surface'
figure
surf(zg-z0)
title 'Griddata error surface'
figure
surf(z-z0)
title 'Inpainting error surface (Note z-axis scale)'
%% Comparison of methods
[x,y] = meshgrid(-1:.02:1);
r = sqrt(x.^2 + y.^2);
z = exp(-(x.^2+ y.^2));
z(r>=0.9) = NaN;
z((r<=.5) & (x<0)) = NaN;
figure
pcolor(z);
title 'Surface provided to inpaint_nans'
% Method 0
tic,z0 = inpaint_nans(z,0);toc
% Method 1
tic,z1 = inpaint_nans(z,1);toc
% Method 2
tic,z2 = inpaint_nans(z,2);toc
% Method 3
tic,z3 = inpaint_nans(z,3);toc
% Method 4
tic,z4 = inpaint_nans(z,4);toc
% Method 5
tic,z5 = inpaint_nans(z,5);toc
figure
surf(z0)
colormap copper
hold on
h = surf(z);
set(h,'facecolor','r')
hold off
title 'Method 0 (Red was provided)'
figure
surf(z1)
hold on
h = surf(z);
set(h,'facecolor','r')
hold off
title 'Method 1 (Red was provided)'
figure
surf(z2)
hold on
h = surf(z);
set(h,'facecolor','r')
hold off
title 'Method 2 (Red was provided) - least accurate, but fastest'
figure
surf(z3)
hold on
h = surf(z);
set(h,'facecolor','r')
hold off
title 'Method 3 (Red was provided) - Slow, but accurate'
figure
surf(z4)
hold on
h = surf(z);
set(h,'facecolor','r')
hold off
title 'Method 4 (Red was provided) - designed for constant extrapolation!'
figure
h = surf(z5);
set(h,'facecolor','y')
hold on
h = surf(z);
set(h,'facecolor','r')
hold off
title 'Method 5 (Red was provided)'
%% 1-d "inpainting" using interp1
x = linspace(0,3*pi,100);
y0 = sin(x);
y = y0;
% Drop out 2/3 of the data
y(1:3:end) = NaN;
y(2:3:end) = NaN;
% inpaint_nans
y_inpaint = inpaint_nans(y,1);
% interpolate using interp1
k = isnan(y);
y_interp1c = y;
y_interp1s = y;
y_interp1c(k) = interp1(x(~k),y(~k),x(k),'cubic');
y_interp1s(k) = interp1(x(~k),y(~k),x(k),'spline');
figure
plot(x,y,'ro',x,y_inpaint,'b+')
legend('sin(x), missing 2/3 points','inpaint-nans','Location','North')
figure
plot(x,y0-y_inpaint,'r-',x,y0-y_interp1c,'b--',x,y0-y_interp1s,'g--')
title 'Inpainting residuals'
legend('Inpaint-nans','Pchip','Spline','Location','North')

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2015 Kelly Kearney
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,377 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--
This HTML was auto-generated from MATLAB code.
To make changes, update the MATLAB code and republish this document.
--><title>boundedline.m: line with shaded error/confidence bounds</title><meta name="generator" content="MATLAB 9.1"><link rel="schema.DC" href="http://purl.org/dc/elements/1.1/"><meta name="DC.date" content="2017-08-08"><meta name="DC.source" content="./readmeExtras/README.m"><style type="text/css">
html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}:focus{outine:0}ins{text-decoration:none}del{text-decoration:line-through}table{border-collapse:collapse;border-spacing:0}
html { min-height:100%; margin-bottom:1px; }
html body { height:100%; margin:0px; font-family:Arial, Helvetica, sans-serif; font-size:10px; color:#000; line-height:140%; background:#fff none; overflow-y:scroll; }
html body td { vertical-align:top; text-align:left; }
h1 { padding:0px; margin:0px 0px 25px; font-family:Arial, Helvetica, sans-serif; font-size:1.5em; color:#d55000; line-height:100%; font-weight:normal; }
h2 { padding:0px; margin:0px 0px 8px; font-family:Arial, Helvetica, sans-serif; font-size:1.2em; color:#000; font-weight:bold; line-height:140%; border-bottom:1px solid #d6d4d4; display:block; }
h3 { padding:0px; margin:0px 0px 5px; font-family:Arial, Helvetica, sans-serif; font-size:1.1em; color:#000; font-weight:bold; line-height:140%; }
a { color:#005fce; text-decoration:none; }
a:hover { color:#005fce; text-decoration:underline; }
a:visited { color:#004aa0; text-decoration:none; }
p { padding:0px; margin:0px 0px 20px; }
img { padding:0px; margin:0px 0px 20px; border:none; }
p img, pre img, tt img, li img, h1 img, h2 img { margin-bottom:0px; }
ul { padding:0px; margin:0px 0px 20px 23px; list-style:square; }
ul li { padding:0px; margin:0px 0px 7px 0px; }
ul li ul { padding:5px 0px 0px; margin:0px 0px 7px 23px; }
ul li ol li { list-style:decimal; }
ol { padding:0px; margin:0px 0px 20px 0px; list-style:decimal; }
ol li { padding:0px; margin:0px 0px 7px 23px; list-style-type:decimal; }
ol li ol { padding:5px 0px 0px; margin:0px 0px 7px 0px; }
ol li ol li { list-style-type:lower-alpha; }
ol li ul { padding-top:7px; }
ol li ul li { list-style:square; }
.content { font-size:1.2em; line-height:140%; padding: 20px; }
pre, code { font-size:12px; }
tt { font-size: 1.2em; }
pre { margin:0px 0px 20px; }
pre.codeinput { padding:10px; border:1px solid #d3d3d3; background:#f7f7f7; }
pre.codeoutput { padding:10px 11px; margin:0px 0px 20px; color:#4c4c4c; }
pre.error { color:red; }
@media print { pre.codeinput, pre.codeoutput { word-wrap:break-word; width:100%; } }
span.keyword { color:#0000FF }
span.comment { color:#228B22 }
span.string { color:#A020F0 }
span.untermstring { color:#B20000 }
span.syscmd { color:#B28C00 }
.footer { width:auto; padding:10px 0px; margin:25px 0px 0px; border-top:1px dotted #878787; font-size:0.8em; line-height:140%; font-style:italic; color:#878787; text-align:left; float:none; }
.footer p { margin:0px; }
.footer a { color:#878787; }
.footer a:hover { color:#878787; text-decoration:underline; }
.footer a:visited { color:#878787; }
table th { padding:7px 5px; text-align:left; vertical-align:middle; border: 1px solid #d6d4d4; font-weight:bold; }
table td { padding:7px 5px; text-align:left; vertical-align:top; border:1px solid #d6d4d4; }
</style></head><body><div class="content"><h1><tt>boundedline.m</tt>: line with shaded error/confidence bounds</h1><!--introduction--><p>Author: Kelly Kearney</p><p>This repository includes the code for the <tt>boundedline.m</tt> Matlab function and the accompanying <tt>outlinebounds.m</tt> function, along with all dependent functions required to run them.</p><p>The <tt>boundedline</tt> function allows a user to easily plot and line with a shaded patch around it. Ths sort of plot is often used to indicate uncertainty intervals or error bounds around a line.</p><!--/introduction--><h2>Contents</h2><div><ul><li><a href="#1">Getting started</a></li><li><a href="#2">Syntax</a></li><li><a href="#3">Example 1: Plotting lines using various syntax options</a></li><li><a href="#7">Example 2: Filling gaps</a></li><li><a href="#12">Contributions</a></li></ul></div><h2 id="1">Getting started</h2><p><b>Prerequisites</b></p><p>This function requires Matlab R14 or later.</p><p><b>Downloading and installation</b></p><p>This code can be downloaded from <a href="https://github.com/kakearney/boundedline-pkg/">Github</a> or the <a href="http://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m">MatlabCentral File Exchange</a>. The File Exchange entry is updated daily from the GitHub repository.</p><p><b>Matlab Search Path</b></p><p>The following folders need to be added to your Matlab Search path (via <tt>addpath</tt>, <tt>pathtool</tt>, etc.):</p><pre class="language-matlab">boundedline-pkg/Inpaint_nans
boundedline-pkg/boundedline
boundedline-pkg/catuneven
boundedline-pkg/singlepatch
</pre><h2 id="2">Syntax</h2><p><tt>boundedline(x, y, b)</tt> plots a line with coordinates given by <tt>x</tt> and <tt>y</tt>, surrounded by a patch extending a certain distance <tt>b</tt> above/below that line. The dimensions of the <tt>x</tt>, <tt>y</tt>, and <tt>b</tt> arrays can vary to allow for multiple lines to be plotted at once, and for patch bounds to be either constant or varying along the length of the line. See function header help for full details of these variations.</p><p><tt>boundedline(..., 'alpha')</tt> renders the bounded area patch using a partially-transparent patch the same color as the corresponding line(s). If not included, the bounded area will use a fully-opaque patch in a lighter shade of the corresponding line color.</p><p><tt>boundedline(..., 'transparency', transp)</tt> indicates the tranparency or intensity of the bounds patch, using a scalar between 0 and 1. Default is 0.2.</p><p><tt>boundedline(..., 'orientation', orient)</tt> indicates the orientation of the bounds. Orientation can be either <tt>'vert'</tt> for vertical (y-direction) bounds, or <tt>'horiz'</tt> for horizontal (x-direction) bounds. Default is <tt>'vert'</tt>.</p><p><tt>boundedline(..., 'nan', nanflag)</tt> indicates how the bounds patch should handle NaNs in the line coordinates or bounds values. Options are <tt>'fill'</tt>, to smooth over the gap using neighboring values, <tt>'gap'</tt> to leave a blank space in the patch at those points, or <tt>'remove'</tt> to drop the NaN-points entirely, leading to linear interpolation of the gap in the patch. See examples below for more details on these options.</p><p><tt>boundedline(..., 'cmap', cmap)</tt> colors the lines (in order of plotting) acording to the colors in this n x 3 colormap array, overriding any linespec or default colors.</p><p><tt>boundedline(..., ax)</tt> plots the bounded line to the axis indicated by handle <tt>ax</tt>. If not included, the current axis is used.</p><p><tt>[hl, hp] = boundedline(...)</tt> returns the handles the resulting line and patch object(s).</p><p><tt>hout = outlinebounds(hl, hp)</tt> adds an outline to the bounds patch generated by <tt>boundedline</tt>, returning the handle of the resulting line object in <tt>hout</tt>.</p><p>Full details of all input and output variables for both functions can be accessed via the <tt>help</tt> function.</p><h2 id="3">Example 1: Plotting lines using various syntax options</h2><p>This example builds the 4-panel example image used on the MatlabCentral File Exchange, which shows several different methods for supplying line coordinates, bounds coordinates, and shading options.</p><p>The first axis plots two lines using the LineSpec option for input, which allows yoy to set line color, line color, and marker type for each line. The bounds on the first line vary over x, while the bounds on the second line are constant for all x. An outline is added to the bounds so the overlapping region can be seen more clearly.</p><pre class="codeinput">x = linspace(0, 2*pi, 50);
y1 = sin(x);
y2 = cos(x);
e1 = rand(size(y1))*.5+.5;
e2 = [.25 .5];
ax(1) = subplot(2,2,1);
[l,p] = boundedline(x, y1, e1, <span class="string">'-b*'</span>, x, y2, e2, <span class="string">'--ro'</span>);
outlinebounds(l,p);
title(<span class="string">'Opaque bounds, with outline'</span>);
axis <span class="string">tight</span>;
</pre><img vspace="5" hspace="5" src="./readmeExtras/README_01.png" alt=""> <p>For our second axis, we use the same 2 lines, and this time assign x-varying bounds to both lines. Rather than using the LineSpec syntax, this example uses the default color order to assign the colors of the lines and patches. I also turn on the <tt>'alpha'</tt> option, which renders the patch wit partial transparency.</p><pre class="codeinput">ax(2) = subplot(2,2,2);
boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, <span class="string">'alpha'</span>);
title(<span class="string">'Transparent bounds'</span>);
axis <span class="string">tight</span>;
</pre><img vspace="5" hspace="5" src="./readmeExtras/README_02.png" alt=""> <p>The bounds can also be assigned to a horizontal orientation, for a case where the x-axis represents the dependent variable. In this case, the scalar error bound value applies to both lines and both sides of the lines.</p><pre class="codeinput">ax(3) = subplot(2,2,3);
boundedline([y1;y2], x, e1(1), <span class="string">'orientation'</span>, <span class="string">'horiz'</span>)
title(<span class="string">'Horizontal bounds'</span>);
axis <span class="string">tight</span>;
</pre><img vspace="5" hspace="5" src="./readmeExtras/README_03.png" alt=""> <p>Rather than use a LineSpec or the default color order, a colormap array can be used to assign colors. In this case, increasingly-narrower bounds are added on top of the same line.</p><pre class="codeinput">ax(4) = subplot(2,2,4);
boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), <span class="keyword">...</span>
<span class="string">'cmap'</span>, cool(4), <span class="keyword">...</span>
<span class="string">'transparency'</span>, 0.5);
title(<span class="string">'Multiple bounds using colormap'</span>);
set(ax([1 2 4]), <span class="string">'xlim'</span>, [0 2*pi]);
set(ax(3), <span class="string">'ylim'</span>, [0 2*pi]);
axis <span class="string">tight</span>;
</pre><img vspace="5" hspace="5" src="./readmeExtras/README_04.png" alt=""> <h2 id="7">Example 2: Filling gaps</h2><p>If you plot a line with one or more NaNs in either the <tt>x</tt> or <tt>y</tt> vector, the NaN location is rendered as a missing marker with a gap in the line. However, the <tt>patch</tt> command does not handle NaNs gracefully; it simply fails to show the patch at all if any of the coordinates include NaNs.</p><p>Because of this, the expected behavior of the patch part of boundedline when confronted with a NaN in either the bounds array (<tt>b</tt>) or the x/y-coordinates of the line (which are used to calculate the patch coordinates) is ambiguous. I offer a few options.</p><p>Before I demonstrate the options, I'll create a dataset that has a few different types of gaps:</p><pre class="codeinput">x = linspace(0, 2*pi, 50);
y = sin(x);
b = [ones(size(y))*0.2; rand(size(y))*.5+.5]';
y(10) = NaN; <span class="comment">% NaN in the line but not bounds</span>
b(20,1) = NaN; <span class="comment">% NaN in lower bound but not line</span>
b(30,2) = NaN; <span class="comment">% NaN in upper bound but not line</span>
b(40,:) = NaN; <span class="comment">% NaN in both sides of bound but not line</span>
</pre><p>Here's what that looks like in an errorbar plot.</p><pre class="codeinput">figure;
he = errorbar(x,y,b(:,1), b(:,2), <span class="string">'-bo'</span>);
line([x([10 20 30 40]); x([10 20 30 40])], [ones(1,4)*-2;ones(1,4)*2], <span class="keyword">...</span>
<span class="string">'color'</span>, ones(1,3)*0.5, <span class="string">'linestyle'</span>, <span class="string">':'</span>);
text(x(10), sin(x(10))-0.2, {<span class="string">'\uparrow'</span>,<span class="string">'Line'</span>,<span class="string">'gap'</span>}, <span class="string">'vert'</span>, <span class="string">'top'</span>, <span class="string">'horiz'</span>, <span class="string">'center'</span>);
text(x(20), sin(x(20))-0.2, {<span class="string">'\uparrow'</span>,<span class="string">'Lower'</span>,<span class="string">'bound'</span>,<span class="string">'gap'</span>}, <span class="string">'vert'</span>, <span class="string">'top'</span>, <span class="string">'horiz'</span>, <span class="string">'center'</span>);
text(x(30), sin(x(30))-0.2, {<span class="string">'\uparrow'</span>,<span class="string">'Upper'</span>,<span class="string">'bound'</span>,<span class="string">'gap'</span>}, <span class="string">'vert'</span>, <span class="string">'top'</span>, <span class="string">'horiz'</span>, <span class="string">'center'</span>);
text(x(40), sin(x(40))-0.2, {<span class="string">'\uparrow'</span>,<span class="string">'Two-sided'</span>,<span class="string">'bound'</span>,<span class="string">'gap'</span>}, <span class="string">'vert'</span>, <span class="string">'top'</span>, <span class="string">'horiz'</span>, <span class="string">'center'</span>);
axis <span class="string">tight</span> <span class="string">equal</span>;
</pre><img vspace="5" hspace="5" src="./readmeExtras/README_05.png" alt=""> <p>The default method for dealing with NaNs in boundedline is to leave the gap in the line, but smooth over the gap in the bounds based on the neighboring points. This option can be nice if you only have one or two missing points, and you're not interested in emphasizing those gaps in your plot:</p><pre class="codeinput">delete(he);
[hl,hp] = boundedline(x,y,b,<span class="string">'-bo'</span>, <span class="string">'nan'</span>, <span class="string">'fill'</span>);
ho = outlinebounds(hl,hp);
set(ho, <span class="string">'linestyle'</span>, <span class="string">':'</span>, <span class="string">'color'</span>, <span class="string">'r'</span>, <span class="string">'marker'</span>, <span class="string">'.'</span>);
</pre><img vspace="5" hspace="5" src="./readmeExtras/README_06.png" alt=""> <p>I've added bounds outlines in a contrasting color so you can see how I'm handling individual points.</p><p>The second option leaves a full gap in the patch for any NaN. I considered allowing one-sided gaps, but couldn't think of a good way to distinguish a gap from a zero-valued bound. I'm open to suggestions if you have any (email me).</p><pre class="codeinput">delete([hl hp ho]);
[hl,hp] = boundedline(x,y,b,<span class="string">'-bo'</span>, <span class="string">'nan'</span>, <span class="string">'gap'</span>);
ho = outlinebounds(hl,hp);
set(ho, <span class="string">'linestyle'</span>, <span class="string">':'</span>, <span class="string">'color'</span>, <span class="string">'r'</span>, <span class="string">'marker'</span>, <span class="string">'.'</span>);
</pre><img vspace="5" hspace="5" src="./readmeExtras/README_07.png" alt=""> <p>The final option removes points from the patch that are NaNs. The visual result is very similar to the fill option, but the missing points are apparent if you plot the bounds outlines.</p><pre class="codeinput">delete([hl hp ho]);
[hl,hp] = boundedline(x,y,b,<span class="string">'-bo'</span>, <span class="string">'nan'</span>, <span class="string">'remove'</span>);
ho = outlinebounds(hl,hp);
set(ho, <span class="string">'linestyle'</span>, <span class="string">':'</span>, <span class="string">'color'</span>, <span class="string">'r'</span>, <span class="string">'marker'</span>, <span class="string">'.'</span>);
</pre><img vspace="5" hspace="5" src="./readmeExtras/README_08.png" alt=""> <h2 id="12">Contributions</h2><p>Community contributions to this package are welcome!</p><p>To report bugs, please submit <a href="https://github.com/kakearney/boundedline-pkg/issues">an issue</a> on GitHub and include:</p><div><ul><li>your operating system</li><li>your version of Matlab and all relevant toolboxes (type <tt>ver</tt> at the Matlab command line to get this info)</li><li>code/data to reproduce the error or buggy behavior, and the full text of any error messages received</li></ul></div><p>Please also feel free to submit enhancement requests, or to send pull requests (via GitHub) for bug fixes or new features.</p><p>I do monitor the MatlabCentral FileExchange entry for any issues raised in the comments, but would prefer to track issues on GitHub.</p><p class="footer"><br><a href="http://www.mathworks.com/products/matlab/">Published with MATLAB&reg; R2016b</a><br></p></div><!--
##### SOURCE BEGIN #####
%% |boundedline.m|: line with shaded error/confidence bounds
% Author: Kelly Kearney
%
% This repository includes the code for the |boundedline.m| Matlab function
% and the accompanying |outlinebounds.m| function, along with all dependent
% functions required to run them.
%
% The |boundedline| function allows a user to easily plot and line with a
% shaded patch around it. Ths sort of plot is often used to indicate
% uncertainty intervals or error bounds around a line.
%
%% Getting started
%
% *Prerequisites*
%
% This function requires Matlab R14 or later.
%
% *Downloading and installation*
%
% This code can be downloaded from <https://github.com/kakearney/boundedline-pkg/ Github>
% or the
% <http://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m
% MatlabCentral File Exchange>. The File Exchange entry is updated daily
% from the GitHub repository.
%
% *Matlab Search Path*
%
% The following folders need to be added to your Matlab Search path (via
% |addpath|, |pathtool|, etc.):
%
% boundedline-pkg/Inpaint_nans
% boundedline-pkg/boundedline
% boundedline-pkg/catuneven
% boundedline-pkg/singlepatch
%% Syntax
%
% |boundedline(x, y, b)| plots a line with coordinates given by
% |x| and |y|, surrounded by a patch extending a certain distance |b|
% above/below that line. The dimensions of the |x|, |y|, and |b| arrays
% can vary to allow for multiple lines to be plotted at once, and for
% patch bounds to be either constant or varying along the length of the
% line. See function header help for full details of these variations.
%
% |boundedline(..., 'alpha')| renders the bounded area patch using a
% partially-transparent patch the same color as the corresponding line(s).
% If not included, the bounded area will use a fully-opaque patch in a
% lighter shade of the corresponding line color.
%
% |boundedline(..., 'transparency', transp)| indicates the
% tranparency or intensity of the bounds patch, using a scalar between 0
% and 1. Default is 0.2.
%
% |boundedline(..., 'orientation', orient)| indicates the orientation of
% the bounds. Orientation can be either |'vert'| for vertical (y-direction)
% bounds, or |'horiz'| for horizontal (x-direction) bounds. Default is
% |'vert'|.
%
% |boundedline(..., 'nan', nanflag)| indicates how the bounds patch should
% handle NaNs in the line coordinates or bounds values. Options are
% |'fill'|, to smooth over the gap using neighboring values, |'gap'| to
% leave a blank space in the patch at those points, or |'remove'| to drop
% the NaN-points entirely, leading to linear interpolation of the gap in
% the patch. See examples below for more details on these options.
%
% |boundedline(..., 'cmap', cmap)| colors the lines (in order of plotting)
% acording to the colors in this n x 3 colormap array, overriding any
% linespec or default colors.
%
% |boundedline(..., ax)| plots the bounded line to the axis indicated by
% handle |ax|. If not included, the current axis is used.
%
% |[hl, hp] = boundedline(...)| returns the handles the resulting line
% and patch object(s).
%
% |hout = outlinebounds(hl, hp)| adds an outline to the bounds patch
% generated by |boundedline|, returning the handle of the resulting line
% object in |hout|.
%
% Full details of all input and output variables for both functions can be
% accessed via the |help| function.
%% Example 1: Plotting lines using various syntax options
%
% This example builds the 4-panel example image used on the MatlabCentral
% File Exchange, which shows several different methods for supplying line
% coordinates, bounds coordinates, and shading options.
%
% The first axis plots two lines using the LineSpec option for input, which
% allows yoy to set line color, line color, and marker type for each line.
% The bounds on the first line vary over x, while the bounds on the second
% line are constant for all x. An outline is added to the bounds so the
% overlapping region can be seen more clearly.
x = linspace(0, 2*pi, 50);
y1 = sin(x);
y2 = cos(x);
e1 = rand(size(y1))*.5+.5;
e2 = [.25 .5];
ax(1) = subplot(2,2,1);
[l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, 'REPLACE_WITH_DASH_DASHro');
outlinebounds(l,p);
title('Opaque bounds, with outline');
axis tight;
%%
% For our second axis, we use the same 2 lines, and this time assign
% x-varying bounds to both lines. Rather than using the LineSpec syntax,
% this example uses the default color order to assign the colors of the
% lines and patches. I also turn on the |'alpha'| option, which renders
% the patch wit partial transparency.
ax(2) = subplot(2,2,2);
boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha');
title('Transparent bounds');
axis tight;
%%
% The bounds can also be assigned to a horizontal orientation, for a case
% where the x-axis represents the dependent variable. In this case, the
% scalar error bound value applies to both lines and both sides of the
% lines.
ax(3) = subplot(2,2,3);
boundedline([y1;y2], x, e1(1), 'orientation', 'horiz')
title('Horizontal bounds');
axis tight;
%%
% Rather than use a LineSpec or the default color order, a colormap array
% can be used to assign colors. In this case, increasingly-narrower bounds
% are added on top of the same line.
ax(4) = subplot(2,2,4);
boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ...
'cmap', cool(4), ...
'transparency', 0.5);
title('Multiple bounds using colormap');
set(ax([1 2 4]), 'xlim', [0 2*pi]);
set(ax(3), 'ylim', [0 2*pi]);
axis tight;
%% Example 2: Filling gaps
%
% If you plot a line with one or more NaNs in either the |x| or |y| vector,
% the NaN location is rendered as a missing marker with a gap in the line.
% However, the |patch| command does not handle NaNs gracefully; it simply
% fails to show the patch at all if any of the coordinates include NaNs.
%
% Because of this, the expected behavior of the patch part of boundedline
% when confronted with a NaN in either the bounds array (|b|) or the
% x/y-coordinates of the line (which are used to calculate the patch
% coordinates) is ambiguous. I offer a few options.
%
% Before I demonstrate the options, I'll create a dataset that has a few
% different types of gaps:
x = linspace(0, 2*pi, 50);
y = sin(x);
b = [ones(size(y))*0.2; rand(size(y))*.5+.5]';
y(10) = NaN; % NaN in the line but not bounds
b(20,1) = NaN; % NaN in lower bound but not line
b(30,2) = NaN; % NaN in upper bound but not line
b(40,:) = NaN; % NaN in both sides of bound but not line
%%
% Here's what that looks like in an errorbar plot.
figure;
he = errorbar(x,y,b(:,1), b(:,2), '-bo');
line([x([10 20 30 40]); x([10 20 30 40])], [ones(1,4)*-2;ones(1,4)*2], ...
'color', ones(1,3)*0.5, 'linestyle', ':');
text(x(10), sin(x(10))-0.2, {'\uparrow','Line','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(20), sin(x(20))-0.2, {'\uparrow','Lower','bound','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(30), sin(x(30))-0.2, {'\uparrow','Upper','bound','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(40), sin(x(40))-0.2, {'\uparrow','Two-sided','bound','gap'}, 'vert', 'top', 'horiz', 'center');
axis tight equal;
%%
% The default method for dealing with NaNs in boundedline is to leave the
% gap in the line, but smooth over the gap in the bounds based on the
% neighboring points. This option can be nice if you only have one or two
% missing points, and you're not interested in emphasizing those gaps in
% your plot:
delete(he);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'fill');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
%%
% I've added bounds outlines in a contrasting color so you can see how I'm
% handling individual points.
%
% The second option leaves a full gap in the patch for any NaN. I
% considered allowing one-sided gaps, but couldn't think of a good way to
% distinguish a gap from a zero-valued bound. I'm open to suggestions if
% you have any (email me).
delete([hl hp ho]);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'gap');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
%%
% The final option removes points from the patch that are NaNs. The visual
% result is very similar to the fill option, but the missing points are
% apparent if you plot the bounds outlines.
delete([hl hp ho]);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'remove');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
%% Contributions
%
% Community contributions to this package are welcome!
%
% To report bugs, please submit
% <https://github.com/kakearney/boundedline-pkg/issues an issue> on GitHub and
% include:
%
% * your operating system
% * your version of Matlab and all relevant toolboxes (type |ver| at the Matlab command line to get this info)
% * code/data to reproduce the error or buggy behavior, and the full text of any error messages received
%
% Please also feel free to submit enhancement requests, or to send pull
% requests (via GitHub) for bug fixes or new features.
%
% I do monitor the MatlabCentral FileExchange entry for any issues raised
% in the comments, but would prefer to track issues on GitHub.
%
##### SOURCE END #####
--></body></html>

View File

@@ -0,0 +1,240 @@
%% |boundedline.m|: line with shaded error/confidence bounds
% Author: Kelly Kearney
%
% This repository includes the code for the |boundedline.m| Matlab function
% and the accompanying |outlinebounds.m| function, along with all dependent
% functions required to run them.
%
% The |boundedline| function allows a user to easily plot and line with a
% shaded patch around it. Ths sort of plot is often used to indicate
% uncertainty intervals or error bounds around a line.
%
%% Getting started
%
% *Prerequisites*
%
% This function requires Matlab R14 or later.
%
% *Downloading and installation*
%
% This code can be downloaded from <https://github.com/kakearney/boundedline-pkg/ Github>
% or the
% <http://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m
% MatlabCentral File Exchange>. The File Exchange entry is updated daily
% from the GitHub repository.
%
% *Matlab Search Path*
%
% The following folders need to be added to your Matlab Search path (via
% |addpath|, |pathtool|, etc.):
%
% boundedline-pkg/Inpaint_nans
% boundedline-pkg/boundedline
% boundedline-pkg/catuneven
% boundedline-pkg/singlepatch
%% Syntax
%
% |boundedline(x, y, b)| plots a line with coordinates given by
% |x| and |y|, surrounded by a patch extending a certain distance |b|
% above/below that line. The dimensions of the |x|, |y|, and |b| arrays
% can vary to allow for multiple lines to be plotted at once, and for
% patch bounds to be either constant or varying along the length of the
% line. See function header help for full details of these variations.
%
% |boundedline(..., 'alpha')| renders the bounded area patch using a
% partially-transparent patch the same color as the corresponding line(s).
% If not included, the bounded area will use a fully-opaque patch in a
% lighter shade of the corresponding line color.
%
% |boundedline(..., 'transparency', transp)| indicates the
% tranparency or intensity of the bounds patch, using a scalar between 0
% and 1. Default is 0.2.
%
% |boundedline(..., 'orientation', orient)| indicates the orientation of
% the bounds. Orientation can be either |'vert'| for vertical (y-direction)
% bounds, or |'horiz'| for horizontal (x-direction) bounds. Default is
% |'vert'|.
%
% |boundedline(..., 'nan', nanflag)| indicates how the bounds patch should
% handle NaNs in the line coordinates or bounds values. Options are
% |'fill'|, to smooth over the gap using neighboring values, |'gap'| to
% leave a blank space in the patch at those points, or |'remove'| to drop
% the NaN-points entirely, leading to linear interpolation of the gap in
% the patch. See examples below for more details on these options.
%
% |boundedline(..., 'cmap', cmap)| colors the lines (in order of plotting)
% acording to the colors in this n x 3 colormap array, overriding any
% linespec or default colors.
%
% |boundedline(..., ax)| plots the bounded line to the axis indicated by
% handle |ax|. If not included, the current axis is used.
%
% |[hl, hp] = boundedline(...)| returns the handles the resulting line
% and patch object(s).
%
% |hout = outlinebounds(hl, hp)| adds an outline to the bounds patch
% generated by |boundedline|, returning the handle of the resulting line
% object in |hout|.
%
% Full details of all input and output variables for both functions can be
% accessed via the |help| function.
%% Example 1: Plotting lines using various syntax options
%
% This example builds the 4-panel example image used on the MatlabCentral
% File Exchange, which shows several different methods for supplying line
% coordinates, bounds coordinates, and shading options.
%
% The first axis plots two lines using the LineSpec option for input, which
% allows yoy to set line color, line color, and marker type for each line.
% The bounds on the first line vary over x, while the bounds on the second
% line are constant for all x. An outline is added to the bounds so the
% overlapping region can be seen more clearly.
x = linspace(0, 2*pi, 50);
y1 = sin(x);
y2 = cos(x);
e1 = rand(size(y1))*.5+.5;
e2 = [.25 .5];
ax(1) = subplot(2,2,1);
[l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, '--ro');
outlinebounds(l,p);
title('Opaque bounds, with outline');
axis tight;
%%
% For our second axis, we use the same 2 lines, and this time assign
% x-varying bounds to both lines. Rather than using the LineSpec syntax,
% this example uses the default color order to assign the colors of the
% lines and patches. I also turn on the |'alpha'| option, which renders
% the patch with partial transparency.
ax(2) = subplot(2,2,2);
boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha');
title('Transparent bounds');
axis tight;
%%
% The bounds can also be assigned to a horizontal orientation, for a case
% where the x-axis represents the dependent variable. In this case, the
% scalar error bound value applies to both lines and both sides of the
% lines.
ax(3) = subplot(2,2,3);
boundedline([y1;y2], x, e1(1), 'orientation', 'horiz')
title('Horizontal bounds');
axis tight;
%%
% Rather than use a LineSpec or the default color order, a colormap array
% can be used to assign colors. In this case, increasingly-narrower bounds
% are added on top of the same line.
ax(4) = subplot(2,2,4);
boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ...
'cmap', cool(4), ...
'transparency', 0.5);
title('Multiple bounds using colormap');
set(ax([1 2 4]), 'xlim', [0 2*pi]);
set(ax(3), 'ylim', [0 2*pi]);
axis tight;
%% Example 2: Filling gaps
%
% If you plot a line with one or more NaNs in either the |x| or |y| vector,
% the NaN location is rendered as a missing marker with a gap in the line.
% However, the |patch| command does not handle NaNs gracefully; it simply
% fails to show the patch at all if any of the coordinates include NaNs.
%
% Because of this, the expected behavior of the patch part of boundedline
% when confronted with a NaN in either the bounds array (|b|) or the
% x/y-coordinates of the line (which are used to calculate the patch
% coordinates) is ambiguous. I offer a few options.
%
% Before I demonstrate the options, I'll create a dataset that has a few
% different types of gaps:
x = linspace(0, 2*pi, 50);
y = sin(x);
b = [ones(size(y))*0.2; rand(size(y))*.5+.5]';
y(10) = NaN; % NaN in the line but not bounds
b(20,1) = NaN; % NaN in lower bound but not line
b(30,2) = NaN; % NaN in upper bound but not line
b(40,:) = NaN; % NaN in both sides of bound but not line
%%
% Here's what that looks like in an errorbar plot.
figure;
he = errorbar(x,y,b(:,1), b(:,2), '-bo');
line([x([10 20 30 40]); x([10 20 30 40])], [ones(1,4)*-2;ones(1,4)*2], ...
'color', ones(1,3)*0.5, 'linestyle', ':');
text(x(10), sin(x(10))-0.2, {'\uparrow','Line','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(20), sin(x(20))-0.2, {'\uparrow','Lower','bound','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(30), sin(x(30))-0.2, {'\uparrow','Upper','bound','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(40), sin(x(40))-0.2, {'\uparrow','Two-sided','bound','gap'}, 'vert', 'top', 'horiz', 'center');
axis tight equal;
%%
% The default method for dealing with NaNs in boundedline is to leave the
% gap in the line, but smooth over the gap in the bounds based on the
% neighboring points. This option can be nice if you only have one or two
% missing points, and you're not interested in emphasizing those gaps in
% your plot:
delete(he);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'fill');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
%%
% I've added bounds outlines in a contrasting color so you can see how I'm
% handling individual points.
%
% The second option leaves a full gap in the patch for any NaN. I
% considered allowing one-sided gaps, but couldn't think of a good way to
% distinguish a gap from a zero-valued bound. I'm open to suggestions if
% you have any (email me).
delete([hl hp ho]);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'gap');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
%%
% The final option removes points from the patch that are NaNs. The visual
% result is very similar to the fill option, but the missing points are
% apparent if you plot the bounds outlines.
delete([hl hp ho]);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'remove');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
%% Contributions
%
% Community contributions to this package are welcome!
%
% To report bugs, please submit
% <https://github.com/kakearney/boundedline-pkg/issues an issue> on GitHub and
% include:
%
% * your operating system
% * your version of Matlab and all relevant toolboxes (type |ver| at the Matlab command line to get this info)
% * code/data to reproduce the error or buggy behavior, and the full text of any error messages received
%
% Please also feel free to submit enhancement requests, or to send pull
% requests (via GitHub) for bug fixes or new features.
%
% I do monitor the MatlabCentral FileExchange entry for any issues raised
% in the comments, but would prefer to track issues on GitHub.
%

View File

@@ -0,0 +1,279 @@
# boundedline.m: line with shaded error/confidence bounds
Author: Kelly Kearney
[![View boundedline.m on File Exchange](https://www.mathworks.com/matlabcentral/images/matlab-file-exchange.svg)](https://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m)
This repository includes the code for the `boundedline.m` Matlab function and the accompanying `outlinebounds.m` function, along with all dependent functions required to run them.
The `boundedline` function allows a user to easily plot and line with a shaded patch around it. Ths sort of plot is often used to indicate uncertainty intervals or error bounds around a line.
## Contents
- Getting started
- Syntax
- Example 1: Plotting lines using various syntax options
- Example 2: Filling gaps
- Contributions
## Getting started
**Prerequisites**
This function requires Matlab R14 or later.
**Downloading and installation**
This code can be downloaded from [Github](https://github.com/kakearney/boundedline-pkg/) or the [MatlabCentral File Exchange](http://www.mathworks.com/matlabcentral/fileexchange/27485-boundedline-m). The File Exchange entry is updated daily from the GitHub repository.
**Matlab Search Path**
The following folders need to be added to your Matlab Search path (via `addpath`, `pathtool`, etc.):
```matlab
boundedline-pkg/Inpaint_nans
boundedline-pkg/boundedline
boundedline-pkg/catuneven
boundedline-pkg/singlepatch
```
## Syntax
`boundedline(x, y, b)` plots a line with coordinates given by `x` and `y`, surrounded by a patch extending a certain distance `b` above/below that line. The dimensions of the `x`, `y`, and `b` arrays can vary to allow for multiple lines to be plotted at once, and for patch bounds to be either constant or varying along the length of the line. See function header help for full details of these variations.
`boundedline(..., 'alpha')` renders the bounded area patch using a partially-transparent patch the same color as the corresponding line(s). If not included, the bounded area will use a fully-opaque patch in a lighter shade of the corresponding line color.
`boundedline(..., 'transparency', transp)` indicates the tranparency or intensity of the bounds patch, using a scalar between 0 and 1. Default is 0.2.
`boundedline(..., 'orientation', orient)` indicates the orientation of the bounds. Orientation can be either `'vert'` for vertical (y-direction) bounds, or `'horiz'` for horizontal (x-direction) bounds. Default is `'vert'`.
`boundedline(..., 'nan', nanflag)` indicates how the bounds patch should handle NaNs in the line coordinates or bounds values. Options are `'fill'`, to smooth over the gap using neighboring values, `'gap'` to leave a blank space in the patch at those points, or `'remove'` to drop the NaN-points entirely, leading to linear interpolation of the gap in the patch. See examples below for more details on these options.
`boundedline(..., 'cmap', cmap)` colors the lines (in order of plotting) acording to the colors in this n x 3 colormap array, overriding any linespec or default colors.
`boundedline(..., ax)` plots the bounded line to the axis indicated by handle `ax`. If not included, the current axis is used.
`[hl, hp] = boundedline(...)` returns the handles the resulting line and patch object(s).
`hout = outlinebounds(hl, hp)` adds an outline to the bounds patch generated by `boundedline`, returning the handle of the resulting line object in `hout`.
Full details of all input and output variables for both functions can be accessed via the `help` function.
## Example 1: Plotting lines using various syntax options
This example builds the 4-panel example image used on the MatlabCentral File Exchange, which shows several different methods for supplying line coordinates, bounds coordinates, and shading options.
The first axis plots two lines using the LineSpec option for input, which allows you to set line color, line color, and marker type for each line. The bounds on the first line vary over x, while the bounds on the second line are constant for all x. An outline is added to the bounds so the overlapping region can be seen more clearly.
```matlab
x = linspace(0, 2*pi, 50);
y1 = sin(x);
y2 = cos(x);
e1 = rand(size(y1))*.5+.5;
e2 = [.25 .5];
ax(1) = subplot(2,2,1);
[l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, '--ro');
outlinebounds(l,p);
title('Opaque bounds, with outline');
axis tight;
```
![](./readmeExtras/README_01.png)
For our second axis, we use the same 2 lines, and this time assign x-varying bounds to both lines. Rather than using the LineSpec syntax, this example uses the default color order to assign the colors of the lines and patches. I also turn on the `'alpha'` option, which renders the patch with partial transparency.
```matlab
ax(2) = subplot(2,2,2);
boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha');
title('Transparent bounds');
axis tight;
```
![](./readmeExtras/README_02.png)
The bounds can also be assigned to a horizontal orientation, for a case where the x-axis represents the dependent variable. In this case, the scalar error bound value applies to both lines and both sides of the lines.
```matlab
ax(3) = subplot(2,2,3);
boundedline([y1;y2], x, e1(1), 'orientation', 'horiz')
title('Horizontal bounds');
axis tight;
```
![](./readmeExtras/README_03.png)
Rather than use a LineSpec or the default color order, a colormap array can be used to assign colors. In this case, increasingly-narrower bounds are added on top of the same line.
```matlab
ax(4) = subplot(2,2,4);
boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ...
'cmap', cool(4), ...
'transparency', 0.5);
title('Multiple bounds using colormap');
set(ax([1 2 4]), 'xlim', [0 2*pi]);
set(ax(3), 'ylim', [0 2*pi]);
axis tight;
```
![](./readmeExtras/README_04.png)
## Example 2: Filling gaps
If you plot a line with one or more NaNs in either the `x` or `y` vector, the NaN location is rendered as a missing marker with a gap in the line. However, the `patch` command does not handle NaNs gracefully; it simply fails to show the patch at all if any of the coordinates include NaNs.
Because of this, the expected behavior of the patch part of boundedline when confronted with a NaN in either the bounds array (`b`) or the x/y-coordinates of the line (which are used to calculate the patch coordinates) is ambiguous. I offer a few options.
Before I demonstrate the options, I'll create a dataset that has a few different types of gaps:
```matlab
x = linspace(0, 2*pi, 50);
y = sin(x);
b = [ones(size(y))*0.2; rand(size(y))*.5+.5]';
y(10) = NaN; % NaN in the line but not bounds
b(20,1) = NaN; % NaN in lower bound but not line
b(30,2) = NaN; % NaN in upper bound but not line
b(40,:) = NaN; % NaN in both sides of bound but not line
```
Here's what that looks like in an errorbar plot.
```matlab
figure;
he = errorbar(x,y,b(:,1), b(:,2), '-bo');
line([x([10 20 30 40]); x([10 20 30 40])], [ones(1,4)*-2;ones(1,4)*2], ...
'color', ones(1,3)*0.5, 'linestyle', ':');
text(x(10), sin(x(10))-0.2, {'\uparrow','Line','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(20), sin(x(20))-0.2, {'\uparrow','Lower','bound','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(30), sin(x(30))-0.2, {'\uparrow','Upper','bound','gap'}, 'vert', 'top', 'horiz', 'center');
text(x(40), sin(x(40))-0.2, {'\uparrow','Two-sided','bound','gap'}, 'vert', 'top', 'horiz', 'center');
axis tight equal;
```
![](./readmeExtras/README_05.png)
The default method for dealing with NaNs in boundedline is to leave the gap in the line, but smooth over the gap in the bounds based on the neighboring points. This option can be nice if you only have one or two missing points, and you're not interested in emphasizing those gaps in your plot:
```matlab
delete(he);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'fill');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
```
![](./readmeExtras/README_06.png)
I've added bounds outlines in a contrasting color so you can see how I'm handling individual points.
The second option leaves a full gap in the patch for any NaN. I considered allowing one-sided gaps, but couldn't think of a good way to distinguish a gap from a zero-valued bound. I'm open to suggestions if you have any (email me).
```matlab
delete([hl hp ho]);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'gap');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
```
![](./readmeExtras/README_07.png)
The final option removes points from the patch that are NaNs. The visual result is very similar to the fill option, but the missing points are apparent if you plot the bounds outlines.
```matlab
delete([hl hp ho]);
[hl,hp] = boundedline(x,y,b,'-bo', 'nan', 'remove');
ho = outlinebounds(hl,hp);
set(ho, 'linestyle', ':', 'color', 'r', 'marker', '.');
```
![](./readmeExtras/README_08.png)
## Contributions
Community contributions to this package are welcome!
To report bugs, please submit [an issue](https://github.com/kakearney/boundedline-pkg/issues) on GitHub and include:
- your operating system
- your version of Matlab and all relevant toolboxes (type `ver` at the Matlab command line to get this info)
- code/data to reproduce the error or buggy behavior, and the full text of any error messages received
Please also feel free to submit enhancement requests, or to send pull requests (via GitHub) for bug fixes or new features.
I do monitor the MatlabCentral FileExchange entry for any issues raised in the comments, but would prefer to track issues on GitHub.
<sub>[Published with MATLAB R2016b]("http://www.mathworks.com/products/matlab/")</sub>

View File

@@ -0,0 +1,37 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

View File

@@ -0,0 +1,501 @@
function varargout = boundedline(varargin)
%BOUNDEDLINE Plot a line with shaded error/confidence bounds
%
% [hl, hp] = boundedline(x, y, b)
% [hl, hp] = boundedline(x, y, b, linespec)
% [hl, hp] = boundedline(x1, y1, b1, linespec1, x2, y2, b2, linespec2)
% [hl, hp] = boundedline(..., 'alpha')
% [hl, hp] = boundedline(..., ax)
% [hl, hp] = boundedline(..., 'transparency', trans)
% [hl, hp] = boundedline(..., 'orientation', orient)
% [hl, hp] = boundedline(..., 'nan', nanflag)
% [hl, hp] = boundedline(..., 'cmap', cmap)
%
% Input variables:
%
% x, y: x and y values, either vectors of the same length, matrices
% of the same size, or vector/matrix pair where the row or
% column size of the array matches the length of the vector
% (same requirements as for plot function).
%
% b: npoint x nside x nline array. Distance from line to
% boundary, for each point along the line (dimension 1), for
% each side of the line (lower/upper or left/right, depending
% on orientation) (dimension 2), and for each plotted line
% described by the preceding x-y values (dimension 3). If
% size(b,1) == 1, the bounds will be the same for all points
% along the line. If size(b,2) == 1, the bounds will be
% symmetrical on both sides of the lines. If size(b,3) == 1,
% the same bounds will be applied to all lines described by
% the preceding x-y arrays (only applicable when either x or
% y is an array). Bounds cannot include Inf, -Inf, or NaN,
%
% linespec: line specification that determines line type, marker
% symbol, and color of the plotted lines for the preceding
% x-y values.
%
% 'alpha': if included, the bounded area will be rendered with a
% partially-transparent patch the same color as the
% corresponding line(s). If not included, the bounded area
% will be an opaque patch with a lighter shade of the
% corresponding line color.
%
% ax: handle of axis where lines will be plotted. If not
% included, the current axis will be used.
%
% transp: Scalar between 0 and 1 indicating with the transparency or
% intensity of color of the bounded area patch. Default is
% 0.2.
%
% orient: direction to add bounds
% 'vert': add bounds in vertical (y) direction (default)
% 'horiz': add bounds in horizontal (x) direction
%
% nanflag: Sets how NaNs in the boundedline patch should be handled
% 'fill': fill the value based on neighboring values,
% smoothing over the gap
% 'gap': leave a blank space over/below the line
% 'remove': drop NaNs from patches, creating a linear
% interpolation over the gap. Note that this
% applies only to the bounds; NaNs in the line will
% remain.
%
% cmap: n x 3 colormap array. If included, lines will be colored
% (in order of plotting) according to this colormap,
% overriding any linespec or default colors.
%
% Output variables:
%
% hl: handles to line objects
%
% hp: handles to patch objects
%
% Example:
%
% x = linspace(0, 2*pi, 50);
% y1 = sin(x);
% y2 = cos(x);
% e1 = rand(size(y1))*.5+.5;
% e2 = [.25 .5];
%
% ax(1) = subplot(2,2,1);
% [l,p] = boundedline(x, y1, e1, '-b*', x, y2, e2, '--ro');
% outlinebounds(l,p);
% title('Opaque bounds, with outline');
%
% ax(2) = subplot(2,2,2);
% boundedline(x, [y1;y2], rand(length(y1),2,2)*.5+.5, 'alpha');
% title('Transparent bounds');
%
% ax(3) = subplot(2,2,3);
% boundedline([y1;y2], x, e1(1), 'orientation', 'horiz')
% title('Horizontal bounds');
%
% ax(4) = subplot(2,2,4);
% boundedline(x, repmat(y1, 4,1), permute(0.5:-0.1:0.2, [3 1 2]), ...
% 'cmap', cool(4), 'transparency', 0.5);
% title('Multiple bounds using colormap');
% Copyright 2010 Kelly Kearney
%--------------------
% Parse input
%--------------------
% Color and cmap are mechanically the same:
tmp = strncmpi(varargin,'color',3);
if any(tmp)
varargin{tmp} = 'cmap';
end
% Alpha flag
isalpha = cellfun(@(x) ischar(x) && strcmp(x, 'alpha'), varargin);
if any(isalpha)
usealpha = true;
varargin = varargin(~isalpha);
else
usealpha = false;
end
% Axis
isax = cellfun(@(x) isscalar(x) && ishandle(x) && strcmp('axes', get(x,'type')), varargin);
if any(isax)
hax = varargin{isax};
varargin = varargin(~isax);
else
hax = gca;
end
% Transparency
[found, trans, varargin] = parseparam(varargin, 'transparency');
if ~found
trans = 0.2;
end
if ~isscalar(trans) || trans < 0 || trans > 1
error('Transparency must be scalar between 0 and 1');
end
% Orientation
[found, orient, varargin] = parseparam(varargin, 'orientation');
if ~found
orient = 'vert';
end
if strcmp(orient, 'vert')
isvert = true;
elseif strcmp(orient, 'horiz')
isvert = false;
else
error('Orientation must be ''vert'' or ''horiz''');
end
% Colormap
[hascmap, cmap, varargin] = parseparam(varargin, 'cmap');
% NaN flag
[found, nanflag, varargin] = parseparam(varargin, 'nan');
if ~found
nanflag = 'fill';
end
if ~ismember(nanflag, {'fill', 'gap', 'remove'})
error('Nan flag must be ''fill'', ''gap'', or ''remove''');
end
[haslw, lwidth, varargin] = parseparam(varargin, 'linewidth');
if ~haslw
lwidth = get(0, 'DefaultLineLineWidth');
end
% X, Y, E triplets, and linespec
[x,y,err,linespec] = deal(cell(0));
while ~isempty(varargin)
if length(varargin) < 3
error('Unexpected input: should be x, y, bounds triplets');
end
if all(cellfun(@isnumeric, varargin(1:3)))
x = [x varargin(1)];
y = [y varargin(2)];
err = [err varargin(3)];
varargin(1:3) = [];
else
if any(cellfun(@(x) isa(x, 'datetime'), varargin(1:3)))
% Special error message for most likely culprit: datetimes
error('boundedline cannot support datetime input due to incompatibility between patches and datetime axes; please convert to datenumbers instead');
else
% Otherwise
error('Unexpected input: should be numeric x, y, bounds triplets');
end
end
if ~isempty(varargin) && ischar(varargin{1})
linespec = [linespec varargin(1)];
varargin(1) = [];
else
linespec = [linespec {[]}];
end
end
%--------------------
% Reformat x and y
% for line and patch
% plotting
%--------------------
% Calculate y values for bounding lines
plotdata = cell(0,7);
htemp = figure('visible', 'off');
for ix = 1:length(x)
% Get full x, y, and linespec data for each line (easier to let plot
% check for properly-sized x and y and expand values than to try to do
% it myself)
try
if isempty(linespec{ix})
hltemp = plot(x{ix}, y{ix});
else
hltemp = plot(x{ix}, y{ix}, linespec{ix});
end
catch
close(htemp);
error('X and Y matrices and/or linespec not appropriate for line plot');
end
linedata = get(hltemp, {'xdata', 'ydata', 'marker', 'linestyle', 'color'});
nline = size(linedata,1);
% Expand bounds matrix if necessary
if nline > 1
if ndims(err{ix}) == 3
err2 = squeeze(num2cell(err{ix},[1 2]));
else
err2 = repmat(err(ix),nline,1);
end
else
err2 = err(ix);
end
% Figure out upper and lower bounds
[lo, hi] = deal(cell(nline,1));
for iln = 1:nline
x2 = linedata{iln,1};
y2 = linedata{iln,2};
nx = length(x2);
if isvert
lineval = y2;
else
lineval = x2;
end
sz = size(err2{iln});
if isequal(sz, [nx 2])
lo{iln} = lineval - err2{iln}(:,1)';
hi{iln} = lineval + err2{iln}(:,2)';
elseif isequal(sz, [nx 1])
lo{iln} = lineval - err2{iln}';
hi{iln} = lineval + err2{iln}';
elseif isequal(sz, [1 2])
lo{iln} = lineval - err2{iln}(1);
hi{iln} = lineval + err2{iln}(2);
elseif isequal(sz, [1 1])
lo{iln} = lineval - err2{iln};
hi{iln} = lineval + err2{iln};
elseif isequal(sz, [2 nx]) % not documented, but accepted anyways
lo{iln} = lineval - err2{iln}(:,1);
hi{iln} = lineval + err2{iln}(:,2);
elseif isequal(sz, [1 nx]) % not documented, but accepted anyways
lo{iln} = lineval - err2{iln};
hi{iln} = lineval + err2{iln};
elseif isequal(sz, [2 1]) % not documented, but accepted anyways
lo{iln} = lineval - err2{iln}(1);
hi{iln} = lineval + err2{iln}(2);
else
error('Error bounds must be npt x nside x nline array');
end
end
% Combine all data (xline, yline, marker, linestyle, color, lower bound
% (x or y), upper bound (x or y)
plotdata = [plotdata; linedata lo hi];
end
close(htemp);
% Override colormap
if hascmap
nd = size(plotdata,1);
cmap = repmat(cmap, ceil(nd/size(cmap,1)), 1);
cmap = cmap(1:nd,:);
plotdata(:,5) = num2cell(cmap,2);
end
%--------------------
% Plot
%--------------------
% Setup of x and y, plus line and patch properties
nline = size(plotdata,1);
[xl, yl, xp, yp, marker, lnsty, lncol, ptchcol, alpha] = deal(cell(nline,1));
for iln = 1:nline
xl{iln} = plotdata{iln,1};
yl{iln} = plotdata{iln,2};
% if isvert
% xp{iln} = [plotdata{iln,1} fliplr(plotdata{iln,1})];
% yp{iln} = [plotdata{iln,6} fliplr(plotdata{iln,7})];
% else
% xp{iln} = [plotdata{iln,6} fliplr(plotdata{iln,7})];
% yp{iln} = [plotdata{iln,2} fliplr(plotdata{iln,2})];
% end
[xp{iln}, yp{iln}] = calcpatch(plotdata{iln,1}, plotdata{iln,2}, isvert, plotdata{iln,6}, plotdata{iln,7}, nanflag);
marker{iln} = plotdata{iln,3};
lnsty{iln} = plotdata{iln,4};
if usealpha
lncol{iln} = plotdata{iln,5};
ptchcol{iln} = plotdata{iln,5};
alpha{iln} = trans;
else
lncol{iln} = plotdata{iln,5};
ptchcol{iln} = interp1([0 1], [1 1 1; lncol{iln}], trans);
alpha{iln} = 1;
end
end
% Plot patches and lines
if verLessThan('matlab', '8.4.0')
[hp,hl] = deal(zeros(nline,1));
else
[hp,hl] = deal(gobjects(nline,1));
end
for iln = 1:nline
hp(iln) = patch(xp{iln}, yp{iln}, ptchcol{iln}, ...
'facealpha', alpha{iln}, ...
'edgecolor', 'none', ...
'parent', hax);
end
for iln = 1:nline
hl(iln) = line(xl{iln}, yl{iln}, ...
'marker', marker{iln}, ...
'linestyle', lnsty{iln}, ...
'color', lncol{iln}, ...
'linewidth', lwidth, ...
'parent', hax);
end
%--------------------
% Assign output
%--------------------
nargoutchk(0,2);
if nargout >= 1
varargout{1} = hl;
end
if nargout == 2
varargout{2} = hp;
end
%--------------------
% Parse optional
% parameters
%--------------------
function [found, val, vars] = parseparam(vars, param)
isvar = cellfun(@(x) ischar(x) && strcmpi(x, param), vars);
if sum(isvar) > 1
error('Parameters can only be passed once');
end
if any(isvar)
found = true;
idx = find(isvar);
val = vars{idx+1};
vars([idx idx+1]) = [];
else
found = false;
val = [];
end
%----------------------------
% Calculate patch coordinates
%----------------------------
function [xp, yp] = calcpatch(xl, yl, isvert, lo, hi, nanflag)
ismissing = isnan([xl;yl;lo;hi]);
% If gap method, split
if any(ismissing(:)) && strcmp(nanflag, 'gap')
tmp = [xl;yl;lo;hi];
idx = find(any(ismissing,1));
n = diff([0 idx length(xl)]);
tmp = mat2cell(tmp, 4, n);
isemp = cellfun('isempty', tmp);
tmp = tmp(~isemp);
tmp = cellfun(@(a) a(:,~any(isnan(a),1)), tmp, 'uni', 0);
isemp = cellfun('isempty', tmp);
tmp = tmp(~isemp);
xl = cellfun(@(a) a(1,:), tmp, 'uni', 0);
yl = cellfun(@(a) a(2,:), tmp, 'uni', 0);
lo = cellfun(@(a) a(3,:), tmp, 'uni', 0);
hi = cellfun(@(a) a(4,:), tmp, 'uni', 0);
else
xl = {xl};
yl = {yl};
lo = {lo};
hi = {hi};
end
[xp, yp] = deal(cell(size(xl)));
for ii = 1:length(xl)
iseq = ~verLessThan('matlab', '8.4.0') && isequal(lo{ii}, hi{ii}); % deal with zero-width bug in R2014b/R2015a
if isvert
if iseq
xp{ii} = [xl{ii} nan(size(xl{ii}))];
yp{ii} = [lo{ii} fliplr(hi{ii})];
else
xp{ii} = [xl{ii} fliplr(xl{ii})];
yp{ii} = [lo{ii} fliplr(hi{ii})];
end
else
if iseq
xp{ii} = [lo{ii} fliplr(hi{ii})];
yp{ii} = [yl{ii} nan(size(yl{ii}))];
else
xp{ii} = [lo{ii} fliplr(hi{ii})];
yp{ii} = [yl{ii} fliplr(yl{ii})];
end
end
if strcmp(nanflag, 'fill')
xp{ii} = inpaint_nans(xp{ii}', 4);
yp{ii} = inpaint_nans(yp{ii}', 4);
if iseq % need to maintain NaNs for zero-width bug
nx = length(xp{ii});
xp{ii}((nx/2)+1:end) = NaN;
end
elseif strcmp(nanflag, 'remove')
if iseq
nx = length(xp{ii});
keepnan = false(size(xp));
keepnan((nx/2)+1:end) = true;
isn = (isnan(xp{ii}) | isnan(yp{ii})) & ~keepnan;
else
isn = isnan(xp{ii}) | isnan(yp{ii});
end
xp{ii} = xp{ii}(~isn);
yp{ii} = yp{ii}(~isn);
end
end
if strcmp(nanflag, 'gap')
[xp, yp] = singlepatch(xp, yp);
else
xp = xp{1};
yp = yp{1};
end

View File

@@ -0,0 +1,43 @@
function hnew = outlinebounds(hl, hp)
%OUTLINEBOUNDS Outline the patch of a boundedline
%
% hnew = outlinebounds(hl, hp)
%
% This function adds an outline to the patch objects created by
% boundedline, matching the color of the central line associated with each
% patch.
%
% Input variables:
%
% hl: handles to line objects from boundedline
%
% hp: handles to patch objects from boundedline
%
% Output variables:
%
% hnew: handle to new line objects
% Copyright 2012 Kelly Kearney
hnew = zeros(size(hl));
for il = 1:numel(hp)
col = get(hl(il), 'color');
xy = get(hp(il), {'xdata','ydata'});
ax = ancestor(hl(il), 'axes');
nline = size(xy{1},2);
if mod(size(xy{1}, 1), 2) == 0
% Insert a NaN between upper and lower lines, so they're disconnected
L = size(xy{1}, 1) / 2;
xy{1} = [xy{1}(1:L, :); nan(1, nline); xy{1}(L+1:end, :)];
xy{2} = [xy{2}(1:L, :); nan(1, nline); xy{2}(L+1:end, :)];
end
if nline > 1
xy{1} = reshape([xy{1}; nan(1,nline)], [], 1);
xy{2} = reshape([xy{2}; nan(1,nline)], [], 1);
end
hnew(il) = line(xy{1}, xy{2}, 'parent', ax, 'linestyle', '-', 'color', col);
end

View File

@@ -0,0 +1,37 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

View File

@@ -0,0 +1,57 @@
function b = catuneven(dim, padval, varargin)
%CATUNEVEN Concatenate unequally-sized arrays, padding with a value
%
% This function is similar to cat, except it does not require the arrays to
% be equally-sized along non-concatenated dimensions. Instead, all arrays
% are padded to be equally-sized using the value specified.
%
% b = catuneven(dim, padval, a1, a2, ...)
%
% Input variables:
%
% dim: dimension along which to concatenate
%
% padval: value used as placeholder when arrays are expanded
%
% a#: arrays to be concatenated, numerical
%
% Output variables:
%
% b: concatenated array
% Copyright 2013 Kelly Kearney
ndim = max(cellfun(@ndims, varargin));
ndim = max(ndim, dim);
for ii = 1:ndim
sz(:,ii) = cellfun(@(x) size(x, ii), varargin);
end
maxsz = max(sz, [], 1);
nv = length(varargin);
val = cell(size(varargin));
for ii = 1:nv
sztmp = maxsz;
sztmp(dim) = sz(ii,dim);
idx = cell(ndim,1);
[idx{:}] = ind2sub(sz(ii,:), 1:numel(varargin{ii}));
idxnew = sub2ind(sztmp, idx{:});
try
val{ii} = ones(sztmp) * padval;
catch
val{ii} = repmat(padval, sztmp);
end
val{ii}(idxnew) = varargin{ii};
end
b = cat(dim, val{:});

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -0,0 +1,37 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

View File

@@ -0,0 +1,65 @@
function varargout = singlepatch(varargin)
%SINGLEPATCH Concatenate patches to be plotted as one
%
% [xp, yp, zp, ...] = singlepatch(x, y, z, ...)
%
% Concatenates uneven vectors of x and y coordinates by replicating the
% last point in each polygon. This allows patches with different numbers
% of vertices to be plotted as one, which is often much, much more
% efficient than plotting lots of individual patches.
%
% Input variables:
%
% x: cell array, with each cell holding a vector of coordinates
% associates with a single patch. The input variables must all be
% of the same size, and usually will correspond to x, y, z, and c
% data for the patches.
%
% Output variables:
%
% xp: m x n array of coordinates, where m is the maximum length of the
% vectors in x and n is numel(x).
% Copyright 2015 Kelly Kearney
if nargin ~= nargout
error('Must supply the same number of input variables as output variables');
end
nv = nargin;
vars = varargin;
sz = cellfun(@size, vars{1}(:), 'uni', 0);
sz = cat(1, sz{:});
if all(sz(:,1) == 1)
for ii = 1:nv
vars{ii} = catuneven(1, NaN, vars{ii}{:})';
end
% x = catuneven(1, NaN, x{:})';
% y = catuneven(1, NaN, y{:})';
elseif all(sz(:,2) == 1)
for ii = 1:nv
vars{ii} = catuneven(2, NaN, vars{ii}{:});
end
% x = catuneven(2, NaN, x{:});
% y = catuneven(2, NaN, y{:});
else
error('Inputs must be cell arrays of vectors');
end
[ii,jj] = find(isnan(vars{1}));
ind = accumarray(jj, ii, [size(vars{1},2) 1], @min);
ij1 = [ii jj];
ij2 = [ind(jj)-1 jj];
idx1 = sub2ind(size(vars{1}), ij1(:,1), ij1(:,2));
idx2 = sub2ind(size(vars{1}), ij2(:,1), ij2(:,2));
for ii = 1:nv
vars{ii}(idx1) = vars{ii}(idx2);
end
varargout = vars;

View File

@@ -0,0 +1,260 @@
%CBREWER2 Interpolated versions of Cynthia Brewer's ColorBrewer colormaps
% CBREWER2(CNAME, NCOL) returns the colour scheme CNAME with the number
% of colours equal to NCOL. If there is a ColorBrewer scheme with exactly
% this number of colours, the color scheme is returned as-is. If NCOL
% larger (or smaller) than the designed colormaps for this scheme, the
% largest (smallest) one is interpolated to provide enough colours,
% unless the requested colour scheme CNAME is a qualitative palette. For
% a qualitative scheme, the colours are repeated, cycling from the
% beginning again, to output the requested NCOL colours.
%
% CBREWER2(CNAME) without an NCOL input will use the same number of
% colours as the current colormap.
%
% CBREWER2(CNAME, NCOL, INTERP_METHOD) allows you to change the method
% used for the interpolation. The default is 'cubic'.
%
% CBREWER2(CNAME, NCOL, INTERP_METHOD, INTERP_SPACE) allows you to
% change the colorspace used for the interpolation. By default, this is
% in the CIELAB colorspace, which is approximately perceptually uniform.
% Options for INTERP_SPACE are
% 'rgb' : interpolation in sRGB (as used in original CBREWER)
% 'lab' : interpolation in CIELAB (default)
% 'lch' : interpolation in CIELCH_ab (not recommended due to the
% discontinuities at C=0 and H=0)
% Anything else supported by COLORSPACE will also function.
%
% The input format CBREWER2(TYPE, ...) can also be used, where TYPE is
% one of 'seq', 'div', 'qual'. This input is redandant and will be
% ignored. This input format is provided for backwards compatibility with
% the original CBREWER.
%
% Example 1 (sequential heatmap):
% C = [0 2 4 6; 8 10 12 14; 16 18 20 22];
% imagesc(C);
% colorbar;
% colormap(cbrewer('YlOrRd', 256);
%
% Example 2 (line plot):
% x = 0:0.01:2;
% sc = [0.5; 1; 2];
% t0 = [0; 0.2; 0.4];
% t = bsxfun(@rdivide, bsxfun(@plus, x, t0), sc);
% y = sin(t * 2 * pi);
% cmap = cbrewer2('Set1', numel(sc));
% axes('ColorOrder', cmap, 'NextPlot', 'ReplaceChildren');
% plot(x, y);
%
% Example 3 (divergent heatmap):
% [X,Y,Z] = peaks(30);
% surfc(X,Y,Z);
% colormap(cbrewer2('RdBu'));
%
% This product includes color specifications and designs developed by
% Cynthia Brewer (http://colorbrewer.org/). For more information on
% ColorBrewer, please visit http://colorbrewer.org/.
%
% CBREWER2 uses a cached copy of the Cynthia Brewer color schemes which
% was converted to .mat format by Charles Robert for use with CBREWER.
% CBREWER is available from the MATLAB FileExchange under the MIT license.
%
% See also CBREWER, BREWERMAP, COLORSPACE, INTERP1.
% Copyright (c) 2016 Scott Lowe
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
function colormap = cbrewer2(...
cname, ncol, interp_method, interp_space, varargin)
% Definitions -------------------------------------------------------------
% List of all of Cynthia Brewer's colormaps and their types
% seq: sequential
% div: divergent
% qual: qualitative
cbdict = {...
'Blues', 'seq'; ...
'BuGn', 'seq'; ...
'BuPu', 'seq'; ...
'GnBu', 'seq'; ...
'Greens', 'seq'; ...
'Greys', 'seq'; ...
'Oranges', 'seq'; ...
'OrRd', 'seq'; ...
'PuBu', 'seq'; ...
'PuBuGn', 'seq'; ...
'PuRd', 'seq'; ...
'Purples', 'seq'; ...
'RdPu', 'seq'; ...
'Reds', 'seq'; ...
'YlGn', 'seq'; ...
'YlGnBu', 'seq'; ...
'YlOrBr', 'seq'; ...
'YlOrRd', 'seq'; ...
'BrBG', 'div'; ...
'PiYG', 'div'; ...
'PRGn', 'div'; ...
'PuOr', 'div'; ...
'RdBu', 'div'; ...
'RdGy', 'div'; ...
'RdYlBu', 'div'; ...
'RdYlGn', 'div'; ...
'Spectral', 'div'; ...
'Accent', 'qual'; ...
'Dark2', 'qual'; ...
'Paired', 'qual'; ...
'Pastel1', 'qual'; ...
'Pastel2', 'qual'; ...
'Set1', 'qual'; ...
'Set2', 'qual'; ...
'Set3', 'qual'; ...
};
% Input handling ----------------------------------------------------------
narginchk(1, 5);
% Initialise variables if not supplied
if nargin<2
ncol = [];
end
if nargin<3
interp_method = [];
end
if nargin<4
interp_space = [];
end
if nargin<5
varargin = {[]};
end
% Check if the colormap type was unnecessarily input
types = unique(cbdict(:, 2));
if nargin > 1 && ischar(cname) && ischar(ncol)
LI = ismember({cname ncol}, types);
if ~any(LI); error('Number of colors cannot be a string'); end;
if all(LI); error('Incorrect colormap name'); end;
if LI(1)
vgn = {cname; ncol; interp_method; interp_space};
cname = vgn{2};
ncol = vgn{3};
interp_method = vgn{4};
interp_space = varargin{1};
ctype_input = vgn{1};
elseif LI(2)
vgn = {cname; ncol; interp_method; interp_space};
cname = vgn{1};
ncol = vgn{3};
interp_method = vgn{4};
interp_space = varargin{1};
ctype_input = vgn{2};
end
else
ctype_input = '';
end
% Default values
if isempty(ncol)
% Number of colours in the colormap
ncol = size(get(gcf,'colormap'), 1);
end
if isempty(interp_method)
interp_method = 'pchip';
end
if isempty(interp_space)
interp_space = 'lab';
end
% Load colorbrewer data ---------------------------------------------------
Tmp = load('colorbrewer.mat');
colorbrewer = Tmp.colorbrewer;
[TF, idict] = ismember(lower(cname), lower(cbdict(:, 1)));
if ~TF
error('%s is not a recognised Brewer colormap',cname);
end
cname = cbdict{idict, 1};
ctype = cbdict{idict, 2};
if (~isfield(colorbrewer.(ctype), cname))
error('Colormap %s is not present in loaded data',cname);
end
% Main script -------------------------------------------------------------
if ncol > length(colorbrewer.(ctype).(cname))
% If we specified too many colours, we take the maximum and interpolate
colormap = colorbrewer.(ctype).(cname){length(colorbrewer.(ctype).(cname))};
colormap = colormap ./ 255;
elseif isempty(colorbrewer.(ctype).(cname){ncol})
% If we specified too few colours, we take the minimum and interpolate
nmin = find(~cellfun(@isempty, colorbrewer.(ctype).(cname)), 1);
colormap = colorbrewer.(ctype).(cname){nmin};
colormap = colormap./255;
else
% If we specified a number of colours in the pre-designed range, no
% need to interpolate
colormap = (colorbrewer.(ctype).(cname){ncol}) ./ 255;
return;
end
% Don't interpolate if qualitative type
if strcmp(ctype,'qual')
if size(colormap, 1) >= ncol
colormap = colormap(1:ncol, :);
return;
end
warning('CBREWER2:QualTooManyColors', ...
['Too many colors requested: cannot interpolate a qualitative' ...
' colorscheme']);
% Cycle the colours from the beginning again, so we have enough to
% return
colormap = repmat(colormap, ceil(ncol / size(colormap, 1)), 1);
colormap = colormap(1:ncol, :);
return;
end
% Make sure we have colorspace downloaded from the FEX
if ~strcmpi(interp_space, 'rgb') && ~exist('colorspace.m', 'file')
P = requireFEXpackage(28790);
if isempty(P);
error(...
['You need to download COLORSPACE from the MATLAB FEX and' ...
' add it to the MATLAB path.']);
end;
end
% Move to perceptually uniform space
if ~strcmpi(interp_space,'rgb')
colormap = colorspace(['rgb->' interp_space], colormap);
end
% Linearly interpolate
X = linspace(0, 1, size(colormap, 1));
XI = linspace(0, 1, ncol);
colormap = interp1(X, colormap, XI, interp_method);
% Move from perceptually uniform space back to sRGB
if ~strcmpi(interp_space,'rgb')
colormap = colorspace(['rgb<-' interp_space], colormap);
end
end

View File

@@ -0,0 +1,194 @@
function AddedPath = requireFEXpackage(FEXSubmissionID)
%Function requireFEXpackage -
%installs Matlab Central File Exchange (FEX) submission
%with given ID into the directory chosen by the user.
%A new FEX submissions may use previous FEX submissions as its part.
%The function 'requireFEXpackage' helps in adding those previous
%submissions to the user's MATLAB installation.
%
%This function is a part of File Exchange submission 31069.
%Download the entire submission:
%http://www.mathworks.com/matlabcentral/fileexchange/31069
%
% SYNTAX:
% AddedPath = requireFEXpackage(FEXSubmissionID)
%
% INPUT:
% ID of the required submission to File Exchange
%
% OUTPUT:
% the path to that submission added to the user's MATLAB path.
%
% HOW TO CALL:
% The command
% P = requireFEXpackage(8277)
% will download and install the package with ID 8277
% (namely, nice 'fminsearchbnd' by John D'Errico)
%
% EXAMPLES -- HOW TO USE:
%
% EXAMPLE 1 (using 'exist' command):
%
% % first, somewhere in the very beginning of your code,
% % check if the function 'fminsearchbnd' from the FEX package 8277
% % is on your MATLAB path, and if it is not there,
% % require the FEX package 8277:
% if ~(exist('fminsearchbnd', 'file') == 2)
% P = requireFEXpackage(8277); % fminsearchbnd is part of 8277
% end
%
% % Then just use 'fminsearchbnd' where you need it:
% syms x
% RosenbrockBananaFunction = @(x) (1-x(1)).^2 + 100*(x(2)-x(1).^2).^2;
% x = fminsearchbnd(RosenbrockBananaFunction,[3 3])
% EXAMPLE 2 (using 'try-catch' command):
%
% syms x
% RosenbrockBananaFunction = @(x) (1-x(1)).^2+100*(x(2)-x(1).^2).^2;
% try
% % if function 'fminsearchbnd' already exists in your MATLAB
% % installation, just use it:
% x = fminsearchbnd(RosenbrockBananaFunction,[3 3])
% catch
% % if function 'fminsearchbnd' is not present in your MATLAB
% % installation, first get the package 8277 (to which it belongs)
% % from the MATLAB Central File Exchange (FEX)
% P = requireFEXpackage(8277); % fminsearchbnd is part of 8277
% % and then use that function:
% x = fminsearchbnd(RosenbrockBananaFunction,[3 3])
% end
%
%
% NOTE: on Mac platform, the title of the dialog box for
% choosing the directory for installing the required FEX package
% is not shown; this is not a bug, this is how UIGETDIR works on Macs --
% see the documentation for UIGETDIR
% http://www.mathworks.com/help/techdoc/ref/uigetdir.html
%
% (C) Igor Podlubny, 2011
% Copyright (c) 2011, Igor Podlubny
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in
% the documentation and/or other materials provided with the distribution
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
ID = num2str(FEXSubmissionID);
% Ask user for the confirmation of the installation
% of the required FEX package
yes = ['YES, Install package ' ID];
no = 'NO, do not install';
userchoice = questdlg(['The Matlab function/toolbox, which you are running, ' ...
'requires the presence of the package ' ID ...
' from Matlab Central File Exchange.' ...
sprintf('\n\n') ...
'Would you like to install the FEX package ' ID ' now?'] , ...
['Required package ' ID], ...
yes, no, yes);
% Handle response
switch userchoice
case yes,
install = 1;
case no,
install = 0;
otherwise,
install = 0;
end
if install == 1
baseURL = 'http://www.mathworks.com/matlabcentral/fileexchange/';
query = '?download=true';
location = uigetdir(pwd, ['Select the directory for installing the required FEX package' ID ]);
if location ~= 0
% download package 'ID' from Matlab Central File Exchange
filetosave = [location filesep ID '.zip'];
FEXpackage = [baseURL ID query];
[f,status] = urlwrite(FEXpackage,filetosave);
if status==0
warndlg(['No connection to Matlab Central File Exchange,' ' or package ' ID ' does not exist.' ...
' Package ' ID ' has not been installed. ' ...
' Check you internet settings and the ID of the required package, and try again. '] , ...
['No connection to Matlab Central File Exchange' ' or package ' ID ' does not exist'], ...
'modal');
AddedPath = '';
return
end
% unzip the downloaded file to the subdirectory 'ID'
todir = [location filesep ID];
% if the directory 'ID' doesn't exist at given location, create it
if ~(exist([location filesep ID], 'dir') == 7)
mkdir(location, ID);
end
try
unzip(filetosave, todir);
% after unzipping, delete the downloaded ZIP file
delete(filetosave);
% prepend the paths to the downloaded package to the MATLAB path
P = genpath([location filesep ID]);
path(P,path);
catch
% if the FEX package is not ZIP, then it is a single m-file
% just move the file to the ID directory
[pathstr, name, ext] = fileparts(filetosave);
movefile(filetosave, [todir filesep name '.m']);
P = genpath([location filesep ID]);
path(P,path);
end
else
P = '';
end
AddedPath = P;
else
AddedPath = '';
end
if install == 1,
% Ask user about reviewing and saving the modified MATLAB path,
% and take him to PATHTOOL, if the user wants to save the modified path
yes = 'YES, I want to review and save the MATLAB path';
no = 'NO, I don''t want to save the path permanently';
userchoice = questdlg(['After adding the package ' ID ...
' from Matlab Central File Exchange to your MATLAB installation,' ...
' the MATLAB path has been modified accordingly. ', ...
'Would you like to review and save the modified MATLAB path?'] , ...
'Review and save the modified MATLAB path for future use?', ...
yes, no, yes);
% Handle response
switch userchoice
case yes,
pathtool;
case no,
otherwise,
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,72 @@
/**
* @file colorspace.h
* @author Pascal Getreuer 2005-2010 <getreuer@gmail.com>
*/
#ifndef _COLORSPACE_H_
#define _COLORSPACE_H_
/** @brief Datatype to use for representing real numbers
* Set this typedef to either double or float depending on the application.
*/
typedef double num;
/** @brief XYZ color of the D65 white point */
#define WHITEPOINT_X 0.950456
#define WHITEPOINT_Y 1.0
#define WHITEPOINT_Z 1.088754
/** @brief struct for representing a color transform */
typedef struct
{
int NumStages;
void (*Fun[2])(num*, num*, num*, num, num, num);
} colortransform;
int GetColorTransform(colortransform *Trans, const char *TransformString);
void ApplyColorTransform(colortransform Trans,
num *D0, num *D1, num *D2, num S0, num S1, num S2);
void Rgb2Yuv(num *Y, num *U, num *V, num R, num G, num B);
void Yuv2Rgb(num *R, num *G, num *B, num Y, num U, num V);
void Rgb2Ycbcr(num *Y, num *Cb, num *Cr, num R, num G, num B);
void Ycbcr2Rgb(num *R, num *G, num *B, num Y, num Cb, num Cr);
void Rgb2Jpegycbcr(num *R, num *G, num *B, num Y, num Cb, num Cr);
void Jpegycbcr2Rgb(num *R, num *G, num *B, num Y, num Cb, num Cr);
void Rgb2Ypbpr(num *Y, num *Pb, num *Pr, num R, num G, num B);
void Ypbpr2Rgb(num *R, num *G, num *B, num Y, num Pb, num Pr);
void Rgb2Ydbdr(num *Y, num *Db, num *Dr, num R, num G, num B);
void Ydbdr2Rgb(num *R, num *G, num *B, num Y, num Db, num Dr);
void Rgb2Yiq(num *Y, num *I, num *Q, num R, num G, num B);
void Yiq2Rgb(num *R, num *G, num *B, num Y, num I, num Q);
void Rgb2Hsv(num *H, num *S, num *V, num R, num G, num B);
void Hsv2Rgb(num *R, num *G, num *B, num H, num S, num V);
void Rgb2Hsl(num *H, num *S, num *L, num R, num G, num B);
void Hsl2Rgb(num *R, num *G, num *B, num H, num S, num L);
void Rgb2Hsi(num *H, num *S, num *I, num R, num G, num B);
void Hsi2Rgb(num *R, num *G, num *B, num H, num S, num I);
void Rgb2Xyz(num *X, num *Y, num *Z, num R, num G, num B);
void Xyz2Rgb(num *R, num *G, num *B, num X, num Y, num Z);
void Xyz2Lab(num *L, num *a, num *b, num X, num Y, num Z);
void Lab2Xyz(num *X, num *Y, num *Z, num L, num a, num b);
void Xyz2Luv(num *L, num *u, num *v, num X, num Y, num Z);
void Luv2Xyz(num *X, num *Y, num *Z, num L, num u, num v);
void Xyz2Lch(num *L, num *C, num *H, num X, num Y, num Z);
void Lch2Xyz(num *X, num *Y, num *Z, num L, num C, num H);
void Xyz2Cat02lms(num *L, num *M, num *S, num X, num Y, num Z);
void Cat02lms2Xyz(num *X, num *Y, num *Z, num L, num M, num S);
void Rgb2Lab(num *L, num *a, num *b, num R, num G, num B);
void Lab2Rgb(num *R, num *G, num *B, num L, num a, num b);
void Rgb2Luv(num *L, num *u, num *v, num R, num G, num B);
void Luv2Rgb(num *R, num *G, num *B, num L, num u, num v);
void Rgb2Lch(num *L, num *C, num *H, num R, num G, num B);
void Lch2Rgb(num *R, num *G, num *B, num L, num C, num H);
void Rgb2Cat02lms(num *L, num *M, num *S, num R, num G, num B);
void Cat02lms2Rgb(num *R, num *G, num *B, num L, num M, num S);
#endif /* _COLORSPACE_H_ */

View File

@@ -0,0 +1,493 @@
function varargout = colorspace(Conversion,varargin)
%COLORSPACE Transform a color image between color representations.
% B = COLORSPACE(S,A) transforms the color representation of image A
% where S is a string specifying the conversion. The input array A
% should be a real full double array of size Mx3 or MxNx3. The output B
% is the same size as A.
%
% S tells the source and destination color spaces, S = 'dest<-src', or
% alternatively, S = 'src->dest'. Supported color spaces are
%
% 'RGB' sRGB IEC 61966-2-1
% 'YCbCr' Luma + Chroma ("digitized" version of Y'PbPr)
% 'JPEG-YCbCr' Luma + Chroma space used in JFIF JPEG
% 'YDbDr' SECAM Y'DbDr Luma + Chroma
% 'YPbPr' Luma (ITU-R BT.601) + Chroma
% 'YUV' NTSC PAL Y'UV Luma + Chroma
% 'YIQ' NTSC Y'IQ Luma + Chroma
% 'HSV' or 'HSB' Hue Saturation Value/Brightness
% 'HSL' or 'HLS' Hue Saturation Luminance
% 'HSI' Hue Saturation Intensity
% 'XYZ' CIE 1931 XYZ
% 'Lab' CIE 1976 L*a*b* (CIELAB)
% 'Luv' CIE L*u*v* (CIELUV)
% 'LCH' CIE L*C*H* (CIELCH)
% 'CAT02 LMS' CIE CAT02 LMS
%
% All conversions assume 2 degree observer and D65 illuminant.
%
% Color space names are case insensitive and spaces are ignored. When
% sRGB is the source or destination, it can be omitted. For example
% 'yuv<-' is short for 'yuv<-rgb'.
%
% For sRGB, the values should be scaled between 0 and 1. Beware that
% transformations generally do not constrain colors to be "in gamut."
% Particularly, transforming from another space to sRGB may obtain
% R'G'B' values outside of the [0,1] range. So the result should be
% clamped to [0,1] before displaying:
% image(min(max(B,0),1)); % Clamp B to [0,1] and display
%
% sRGB (Red Green Blue) is the (ITU-R BT.709 gamma-corrected) standard
% red-green-blue representation of colors used in digital imaging. The
% components should be scaled between 0 and 1. The space can be
% visualized geometrically as a cube.
%
% Y'PbPr, Y'CbCr, Y'DbDr, Y'UV, and Y'IQ are related to sRGB by linear
% transformations. These spaces separate a color into a grayscale
% luminance component Y and two chroma components. The valid ranges of
% the components depends on the space.
%
% HSV (Hue Saturation Value) is related to sRGB by
% H = hexagonal hue angle (0 <= H < 360),
% S = C/V (0 <= S <= 1),
% V = max(R',G',B') (0 <= V <= 1),
% where C = max(R',G',B') - min(R',G',B'). The hue angle H is computed on
% a hexagon. The space is geometrically a hexagonal cone.
%
% HSL (Hue Saturation Lightness) is related to sRGB by
% H = hexagonal hue angle (0 <= H < 360),
% S = C/(1 - |2L-1|) (0 <= S <= 1),
% L = (max(R',G',B') + min(R',G',B'))/2 (0 <= L <= 1),
% where H and C are the same as in HSV. Geometrically, the space is a
% double hexagonal cone.
%
% HSI (Hue Saturation Intensity) is related to sRGB by
% H = polar hue angle (0 <= H < 360),
% S = 1 - min(R',G',B')/I (0 <= S <= 1),
% I = (R'+G'+B')/3 (0 <= I <= 1).
% Unlike HSV and HSL, the hue angle H is computed on a circle rather than
% a hexagon.
%
% CIE XYZ is related to sRGB by inverse gamma correction followed by a
% linear transform. Other CIE color spaces are defined relative to XYZ.
%
% CIE L*a*b*, L*u*v*, and L*C*H* are nonlinear functions of XYZ. The L*
% component is designed to match closely with human perception of
% lightness. The other two components describe the chroma.
%
% CIE CAT02 LMS is the linear transformation of XYZ using the MCAT02
% chromatic adaptation matrix. The space is designed to model the
% response of the three types of cones in the human eye, where L, M, S,
% correspond respectively to red ("long"), green ("medium"), and blue
% ("short").
% Pascal Getreuer 2005-2010
%%% Input parsing %%%
if nargin < 2, error('Not enough input arguments.'); end
[SrcSpace,DestSpace] = parse(Conversion);
if nargin == 2
Image = varargin{1};
elseif nargin >= 3
Image = cat(3,varargin{:});
else
error('Invalid number of input arguments.');
end
FlipDims = (size(Image,3) == 1);
if FlipDims, Image = permute(Image,[1,3,2]); end
if ~isa(Image,'double'), Image = double(Image)/255; end
if size(Image,3) ~= 3, error('Invalid input size.'); end
SrcT = gettransform(SrcSpace);
DestT = gettransform(DestSpace);
if ~ischar(SrcT) && ~ischar(DestT)
% Both source and destination transforms are affine, so they
% can be composed into one affine operation
T = [DestT(:,1:3)*SrcT(:,1:3),DestT(:,1:3)*SrcT(:,4)+DestT(:,4)];
Temp = zeros(size(Image));
Temp(:,:,1) = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3) + T(10);
Temp(:,:,2) = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3) + T(11);
Temp(:,:,3) = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3) + T(12);
Image = Temp;
elseif ~ischar(DestT)
Image = rgb(Image,SrcSpace);
Temp = zeros(size(Image));
Temp(:,:,1) = DestT(1)*Image(:,:,1) + DestT(4)*Image(:,:,2) + DestT(7)*Image(:,:,3) + DestT(10);
Temp(:,:,2) = DestT(2)*Image(:,:,1) + DestT(5)*Image(:,:,2) + DestT(8)*Image(:,:,3) + DestT(11);
Temp(:,:,3) = DestT(3)*Image(:,:,1) + DestT(6)*Image(:,:,2) + DestT(9)*Image(:,:,3) + DestT(12);
Image = Temp;
else
Image = feval(DestT,Image,SrcSpace);
end
%%% Output format %%%
if nargout > 1
varargout = {Image(:,:,1),Image(:,:,2),Image(:,:,3)};
else
if FlipDims, Image = permute(Image,[1,3,2]); end
varargout = {Image};
end
return;
function [SrcSpace,DestSpace] = parse(Str)
% Parse conversion argument
if ischar(Str)
Str = lower(strrep(strrep(Str,'-',''),'=',''));
k = find(Str == '>');
if length(k) == 1 % Interpret the form 'src->dest'
SrcSpace = Str(1:k-1);
DestSpace = Str(k+1:end);
else
k = find(Str == '<');
if length(k) == 1 % Interpret the form 'dest<-src'
DestSpace = Str(1:k-1);
SrcSpace = Str(k+1:end);
else
error(['Invalid conversion, ''',Str,'''.']);
end
end
SrcSpace = alias(SrcSpace);
DestSpace = alias(DestSpace);
else
SrcSpace = 1; % No source pre-transform
DestSpace = Conversion;
if any(size(Conversion) ~= 3), error('Transformation matrix must be 3x3.'); end
end
return;
function Space = alias(Space)
Space = strrep(strrep(Space,'cie',''),' ','');
if isempty(Space)
Space = 'rgb';
end
switch Space
case {'ycbcr','ycc'}
Space = 'ycbcr';
case {'hsv','hsb'}
Space = 'hsv';
case {'hsl','hsi','hls'}
Space = 'hsl';
case {'rgb','yuv','yiq','ydbdr','ycbcr','jpegycbcr','xyz','lab','luv','lch'}
return;
end
return;
function T = gettransform(Space)
% Get a colorspace transform: either a matrix describing an affine transform,
% or a string referring to a conversion subroutine
switch Space
case 'ypbpr'
T = [0.299,0.587,0.114,0;-0.1687367,-0.331264,0.5,0;0.5,-0.418688,-0.081312,0];
case 'yuv'
% sRGB to NTSC/PAL YUV
% Wikipedia: http://en.wikipedia.org/wiki/YUV
T = [0.299,0.587,0.114,0;-0.147,-0.289,0.436,0;0.615,-0.515,-0.100,0];
case 'ydbdr'
% sRGB to SECAM YDbDr
% Wikipedia: http://en.wikipedia.org/wiki/YDbDr
T = [0.299,0.587,0.114,0;-0.450,-0.883,1.333,0;-1.333,1.116,0.217,0];
case 'yiq'
% sRGB in [0,1] to NTSC YIQ in [0,1];[-0.595716,0.595716];[-0.522591,0.522591];
% Wikipedia: http://en.wikipedia.org/wiki/YIQ
T = [0.299,0.587,0.114,0;0.595716,-0.274453,-0.321263,0;0.211456,-0.522591,0.311135,0];
case 'ycbcr'
% sRGB (range [0,1]) to ITU-R BRT.601 (CCIR 601) Y'CbCr
% Wikipedia: http://en.wikipedia.org/wiki/YCbCr
% Poynton, Equation 3, scaling of R'G'B to Y'PbPr conversion
T = [65.481,128.553,24.966,16;-37.797,-74.203,112.0,128;112.0,-93.786,-18.214,128];
case 'jpegycbcr'
% Wikipedia: http://en.wikipedia.org/wiki/YCbCr
T = [0.299,0.587,0.114,0;-0.168736,-0.331264,0.5,0.5;0.5,-0.418688,-0.081312,0.5]*255;
case {'rgb','xyz','hsv','hsl','lab','luv','lch','cat02lms'}
T = Space;
otherwise
error(['Unknown color space, ''',Space,'''.']);
end
return;
function Image = rgb(Image,SrcSpace)
% Convert to sRGB from 'SrcSpace'
switch SrcSpace
case 'rgb'
return;
case 'hsv'
% Convert HSV to sRGB
Image = huetorgb((1 - Image(:,:,2)).*Image(:,:,3),Image(:,:,3),Image(:,:,1));
case 'hsl'
% Convert HSL to sRGB
L = Image(:,:,3);
Delta = Image(:,:,2).*min(L,1-L);
Image = huetorgb(L-Delta,L+Delta,Image(:,:,1));
case {'xyz','lab','luv','lch','cat02lms'}
% Convert to CIE XYZ
Image = xyz(Image,SrcSpace);
% Convert XYZ to RGB
T = [3.2406, -1.5372, -0.4986; -0.9689, 1.8758, 0.0415; 0.0557, -0.2040, 1.057];
R = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3); % R
G = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3); % G
B = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3); % B
% Desaturate and rescale to constrain resulting RGB values to [0,1]
AddWhite = -min(min(min(R,G),B),0);
R = R + AddWhite;
G = G + AddWhite;
B = B + AddWhite;
% Apply gamma correction to convert linear RGB to sRGB
Image(:,:,1) = gammacorrection(R); % R'
Image(:,:,2) = gammacorrection(G); % G'
Image(:,:,3) = gammacorrection(B); % B'
otherwise % Conversion is through an affine transform
T = gettransform(SrcSpace);
temp = inv(T(:,1:3));
T = [temp,-temp*T(:,4)];
R = T(1)*Image(:,:,1) + T(4)*Image(:,:,2) + T(7)*Image(:,:,3) + T(10);
G = T(2)*Image(:,:,1) + T(5)*Image(:,:,2) + T(8)*Image(:,:,3) + T(11);
B = T(3)*Image(:,:,1) + T(6)*Image(:,:,2) + T(9)*Image(:,:,3) + T(12);
Image(:,:,1) = R;
Image(:,:,2) = G;
Image(:,:,3) = B;
end
% Clip to [0,1]
Image = min(max(Image,0),1);
return;
function Image = xyz(Image,SrcSpace)
% Convert to CIE XYZ from 'SrcSpace'
WhitePoint = [0.950456,1,1.088754];
switch SrcSpace
case 'xyz'
return;
case 'luv'
% Convert CIE L*uv to XYZ
WhitePointU = (4*WhitePoint(1))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3));
WhitePointV = (9*WhitePoint(2))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3));
L = Image(:,:,1);
Y = (L + 16)/116;
Y = invf(Y)*WhitePoint(2);
U = Image(:,:,2)./(13*L + 1e-6*(L==0)) + WhitePointU;
V = Image(:,:,3)./(13*L + 1e-6*(L==0)) + WhitePointV;
Image(:,:,1) = -(9*Y.*U)./((U-4).*V - U.*V); % X
Image(:,:,2) = Y; % Y
Image(:,:,3) = (9*Y - (15*V.*Y) - (V.*Image(:,:,1)))./(3*V); % Z
case {'lab','lch'}
Image = lab(Image,SrcSpace);
% Convert CIE L*ab to XYZ
fY = (Image(:,:,1) + 16)/116;
fX = fY + Image(:,:,2)/500;
fZ = fY - Image(:,:,3)/200;
Image(:,:,1) = WhitePoint(1)*invf(fX); % X
Image(:,:,2) = WhitePoint(2)*invf(fY); % Y
Image(:,:,3) = WhitePoint(3)*invf(fZ); % Z
case 'cat02lms'
% Convert CAT02 LMS to XYZ
T = inv([0.7328, 0.4296, -0.1624;-0.7036, 1.6975, 0.0061; 0.0030, 0.0136, 0.9834]);
L = Image(:,:,1);
M = Image(:,:,2);
S = Image(:,:,3);
Image(:,:,1) = T(1)*L + T(4)*M + T(7)*S; % X
Image(:,:,2) = T(2)*L + T(5)*M + T(8)*S; % Y
Image(:,:,3) = T(3)*L + T(6)*M + T(9)*S; % Z
otherwise % Convert from some gamma-corrected space
% Convert to sRGB
Image = rgb(Image,SrcSpace);
% Undo gamma correction
R = invgammacorrection(Image(:,:,1));
G = invgammacorrection(Image(:,:,2));
B = invgammacorrection(Image(:,:,3));
% Convert RGB to XYZ
T = inv([3.2406, -1.5372, -0.4986; -0.9689, 1.8758, 0.0415; 0.0557, -0.2040, 1.057]);
Image(:,:,1) = T(1)*R + T(4)*G + T(7)*B; % X
Image(:,:,2) = T(2)*R + T(5)*G + T(8)*B; % Y
Image(:,:,3) = T(3)*R + T(6)*G + T(9)*B; % Z
end
return;
function Image = hsv(Image,SrcSpace)
% Convert to HSV
Image = rgb(Image,SrcSpace);
V = max(Image,[],3);
S = (V - min(Image,[],3))./(V + (V == 0));
Image(:,:,1) = rgbtohue(Image);
Image(:,:,2) = S;
Image(:,:,3) = V;
return;
function Image = hsl(Image,SrcSpace)
% Convert to HSL
switch SrcSpace
case 'hsv'
% Convert HSV to HSL
MaxVal = Image(:,:,3);
MinVal = (1 - Image(:,:,2)).*MaxVal;
L = 0.5*(MaxVal + MinVal);
temp = min(L,1-L);
Image(:,:,2) = 0.5*(MaxVal - MinVal)./(temp + (temp == 0));
Image(:,:,3) = L;
otherwise
Image = rgb(Image,SrcSpace); % Convert to sRGB
% Convert sRGB to HSL
MinVal = min(Image,[],3);
MaxVal = max(Image,[],3);
L = 0.5*(MaxVal + MinVal);
temp = min(L,1-L);
S = 0.5*(MaxVal - MinVal)./(temp + (temp == 0));
Image(:,:,1) = rgbtohue(Image);
Image(:,:,2) = S;
Image(:,:,3) = L;
end
return;
function Image = lab(Image,SrcSpace)
% Convert to CIE L*a*b* (CIELAB)
WhitePoint = [0.950456,1,1.088754];
switch SrcSpace
case 'lab'
return;
case 'lch'
% Convert CIE L*CH to CIE L*ab
C = Image(:,:,2);
Image(:,:,2) = cos(Image(:,:,3)*pi/180).*C; % a*
Image(:,:,3) = sin(Image(:,:,3)*pi/180).*C; % b*
otherwise
Image = xyz(Image,SrcSpace); % Convert to XYZ
% Convert XYZ to CIE L*a*b*
X = Image(:,:,1)/WhitePoint(1);
Y = Image(:,:,2)/WhitePoint(2);
Z = Image(:,:,3)/WhitePoint(3);
fX = f(X);
fY = f(Y);
fZ = f(Z);
Image(:,:,1) = 116*fY - 16; % L*
Image(:,:,2) = 500*(fX - fY); % a*
Image(:,:,3) = 200*(fY - fZ); % b*
end
return;
function Image = luv(Image,SrcSpace)
% Convert to CIE L*u*v* (CIELUV)
WhitePoint = [0.950456,1,1.088754];
WhitePointU = (4*WhitePoint(1))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3));
WhitePointV = (9*WhitePoint(2))./(WhitePoint(1) + 15*WhitePoint(2) + 3*WhitePoint(3));
Image = xyz(Image,SrcSpace); % Convert to XYZ
Denom = Image(:,:,1) + 15*Image(:,:,2) + 3*Image(:,:,3);
U = (4*Image(:,:,1))./(Denom + (Denom == 0));
V = (9*Image(:,:,2))./(Denom + (Denom == 0));
Y = Image(:,:,2)/WhitePoint(2);
L = 116*f(Y) - 16;
Image(:,:,1) = L; % L*
Image(:,:,2) = 13*L.*(U - WhitePointU); % u*
Image(:,:,3) = 13*L.*(V - WhitePointV); % v*
return;
function Image = lch(Image,SrcSpace)
% Convert to CIE L*ch
Image = lab(Image,SrcSpace); % Convert to CIE L*ab
H = atan2(Image(:,:,3),Image(:,:,2));
H = H*180/pi + 360*(H < 0);
Image(:,:,2) = sqrt(Image(:,:,2).^2 + Image(:,:,3).^2); % C
Image(:,:,3) = H; % H
return;
function Image = cat02lms(Image,SrcSpace)
% Convert to CAT02 LMS
Image = xyz(Image,SrcSpace);
T = [0.7328, 0.4296, -0.1624;-0.7036, 1.6975, 0.0061; 0.0030, 0.0136, 0.9834];
X = Image(:,:,1);
Y = Image(:,:,2);
Z = Image(:,:,3);
Image(:,:,1) = T(1)*X + T(4)*Y + T(7)*Z; % L
Image(:,:,2) = T(2)*X + T(5)*Y + T(8)*Z; % M
Image(:,:,3) = T(3)*X + T(6)*Y + T(9)*Z; % S
return;
function Image = huetorgb(m0,m2,H)
% Convert HSV or HSL hue to RGB
N = size(H);
H = min(max(H(:),0),360)/60;
m0 = m0(:);
m2 = m2(:);
F = H - round(H/2)*2;
M = [m0, m0 + (m2-m0).*abs(F), m2];
Num = length(m0);
j = [2 1 0;1 2 0;0 2 1;0 1 2;1 0 2;2 0 1;2 1 0]*Num;
k = floor(H) + 1;
Image = reshape([M(j(k,1)+(1:Num).'),M(j(k,2)+(1:Num).'),M(j(k,3)+(1:Num).')],[N,3]);
return;
function H = rgbtohue(Image)
% Convert RGB to HSV or HSL hue
[M,i] = sort(Image,3);
i = i(:,:,3);
Delta = M(:,:,3) - M(:,:,1);
Delta = Delta + (Delta == 0);
R = Image(:,:,1);
G = Image(:,:,2);
B = Image(:,:,3);
H = zeros(size(R));
k = (i == 1);
H(k) = (G(k) - B(k))./Delta(k);
k = (i == 2);
H(k) = 2 + (B(k) - R(k))./Delta(k);
k = (i == 3);
H(k) = 4 + (R(k) - G(k))./Delta(k);
H = 60*H + 360*(H < 0);
H(Delta == 0) = nan;
return;
function Rp = gammacorrection(R)
Rp = zeros(size(R));
i = (R <= 0.0031306684425005883);
Rp(i) = 12.92*R(i);
Rp(~i) = real(1.055*R(~i).^0.416666666666666667 - 0.055);
return;
function R = invgammacorrection(Rp)
R = zeros(size(Rp));
i = (Rp <= 0.0404482362771076);
R(i) = Rp(i)/12.92;
R(~i) = real(((Rp(~i) + 0.055)/1.055).^2.4);
return;
function fY = f(Y)
fY = real(Y.^(1/3));
i = (Y < 0.008856);
fY(i) = Y(i)*(841/108) + (4/29);
return;
function Y = invf(fY)
Y = fY.^3;
i = (Y < 0.008856);
Y(i) = (fY(i) - 4/29)*(108/841);
return;

View File

@@ -0,0 +1,261 @@
% function lineStyles = linspecer(N)
% This function creates an Nx3 array of N [R B G] colors
% These can be used to plot lots of lines with distinguishable and nice
% looking colors.
%
% lineStyles = linspecer(N); makes N colors for you to use: lineStyles(ii,:)
%
% colormap(linspecer); set your colormap to have easily distinguishable
% colors and a pleasing aesthetic
%
% lineStyles = linspecer(N,'qualitative'); forces the colors to all be distinguishable (up to 12)
% lineStyles = linspecer(N,'sequential'); forces the colors to vary along a spectrum
%
% % Examples demonstrating the colors.
%
% LINE COLORS
% N=6;
% X = linspace(0,pi*3,1000);
% Y = bsxfun(@(x,n)sin(x+2*n*pi/N), X.', 1:N);
% C = linspecer(N);
% axes('NextPlot','replacechildren', 'ColorOrder',C);
% plot(X,Y,'linewidth',5)
% ylim([-1.1 1.1]);
%
% SIMPLER LINE COLOR EXAMPLE
% N = 6; X = linspace(0,pi*3,1000);
% C = linspecer(N)
% hold off;
% for ii=1:N
% Y = sin(X+2*ii*pi/N);
% plot(X,Y,'color',C(ii,:),'linewidth',3);
% hold on;
% end
%
% COLORMAP EXAMPLE
% A = rand(15);
% figure; imagesc(A); % default colormap
% figure; imagesc(A); colormap(linspecer); % linspecer colormap
%
% See also NDHIST, NHIST, PLOT, COLORMAP, 43700-cubehelix-colormaps
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% by Jonathan Lansey, March 2009-2013 Lansey at gmail.com %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%% credits and where the function came from
% The colors are largely taken from:
% http://colorbrewer2.org and Cynthia Brewer, Mark Harrower and The Pennsylvania State University
%
%
% She studied this from a phsychometric perspective and crafted the colors
% beautifully.
%
% I made choices from the many there to decide the nicest once for plotting
% lines in Matlab. I also made a small change to one of the colors I
% thought was a bit too bright. In addition some interpolation is going on
% for the sequential line styles.
%
%
%%
function lineStyles=linspecer(N,varargin)
if nargin==0 % return a colormap
lineStyles = linspecer(128);
return;
end
if ischar(N)
lineStyles = linspecer(128,N);
return;
end
if N<=0 % its empty, nothing else to do here
lineStyles=[];
return;
end
% interperet varagin
qualFlag = 0;
colorblindFlag = 0;
if ~isempty(varargin)>0 % you set a parameter?
switch lower(varargin{1})
case {'qualitative','qua'}
if N>12 % go home, you just can't get this.
warning('qualitiative is not possible for greater than 12 items, please reconsider');
else
if N>9
warning(['Default may be nicer for ' num2str(N) ' for clearer colors use: whitebg(''black''); ']);
end
end
qualFlag = 1;
case {'sequential','seq'}
lineStyles = colorm(N);
return;
case {'white','whitefade'}
lineStyles = whiteFade(N);return;
case 'red'
lineStyles = whiteFade(N,'red');return;
case 'blue'
lineStyles = whiteFade(N,'blue');return;
case 'green'
lineStyles = whiteFade(N,'green');return;
case {'gray','grey'}
lineStyles = whiteFade(N,'gray');return;
case {'colorblind'}
colorblindFlag = 1;
otherwise
warning(['parameter ''' varargin{1} ''' not recognized']);
end
end
% *.95
% predefine some colormaps
set3 = colorBrew2mat({[141, 211, 199];[ 255, 237, 111];[ 190, 186, 218];[ 251, 128, 114];[ 128, 177, 211];[ 253, 180, 98];[ 179, 222, 105];[ 188, 128, 189];[ 217, 217, 217];[ 204, 235, 197];[ 252, 205, 229];[ 255, 255, 179]}');
set1JL = brighten(colorBrew2mat({[228, 26, 28];[ 55, 126, 184]; [ 77, 175, 74];[ 255, 127, 0];[ 255, 237, 111]*.85;[ 166, 86, 40];[ 247, 129, 191];[ 153, 153, 153];[ 152, 78, 163]}'));
set1 = brighten(colorBrew2mat({[ 55, 126, 184]*.85;[228, 26, 28];[ 77, 175, 74];[ 255, 127, 0];[ 152, 78, 163]}),.8);
% colorblindSet = {[215,25,28];[253,174,97];[171,217,233];[44,123,182]};
colorblindSet = {[215,25,28];[253,174,97];[171,217,233]*.8;[44,123,182]*.8};
set3 = dim(set3,.93);
if colorblindFlag
switch N
% sorry about this line folks. kind of legacy here because I used to
% use individual 1x3 cells instead of nx3 arrays
case 4
lineStyles = colorBrew2mat(colorblindSet);
otherwise
colorblindFlag = false;
warning('sorry unsupported colorblind set for this number, using regular types');
end
end
if ~colorblindFlag
switch N
case 1
lineStyles = { [ 55, 126, 184]/255};
case {2, 3, 4, 5 }
lineStyles = set1(1:N);
case {6 , 7, 8, 9}
lineStyles = set1JL(1:N)';
case {10, 11, 12}
if qualFlag % force qualitative graphs
lineStyles = set3(1:N)';
else % 10 is a good number to start with the sequential ones.
lineStyles = cmap2linspecer(colorm(N));
end
otherwise % any old case where I need a quick job done.
lineStyles = cmap2linspecer(colorm(N));
end
end
lineStyles = cell2mat(lineStyles);
end
% extra functions
function varIn = colorBrew2mat(varIn)
for ii=1:length(varIn) % just divide by 255
varIn{ii}=varIn{ii}/255;
end
end
function varIn = brighten(varIn,varargin) % increase the brightness
if isempty(varargin),
frac = .9;
else
frac = varargin{1};
end
for ii=1:length(varIn)
varIn{ii}=varIn{ii}*frac+(1-frac);
end
end
function varIn = dim(varIn,f)
for ii=1:length(varIn)
varIn{ii} = f*varIn{ii};
end
end
function vOut = cmap2linspecer(vIn) % changes the format from a double array to a cell array with the right format
vOut = cell(size(vIn,1),1);
for ii=1:size(vIn,1)
vOut{ii} = vIn(ii,:);
end
end
%%
% colorm returns a colormap which is really good for creating informative
% heatmap style figures.
% No particular color stands out and it doesn't do too badly for colorblind people either.
% It works by interpolating the data from the
% 'spectral' setting on http://colorbrewer2.org/ set to 11 colors
% It is modified a little to make the brightest yellow a little less bright.
function cmap = colorm(varargin)
n = 100;
if ~isempty(varargin)
n = varargin{1};
end
if n==1
cmap = [0.2005 0.5593 0.7380];
return;
end
if n==2
cmap = [0.2005 0.5593 0.7380;
0.9684 0.4799 0.2723];
return;
end
frac=.95; % Slight modification from colorbrewer here to make the yellows in the center just a bit darker
cmapp = [158, 1, 66; 213, 62, 79; 244, 109, 67; 253, 174, 97; 254, 224, 139; 255*frac, 255*frac, 191*frac; 230, 245, 152; 171, 221, 164; 102, 194, 165; 50, 136, 189; 94, 79, 162];
x = linspace(1,n,size(cmapp,1));
xi = 1:n;
cmap = zeros(n,3);
for ii=1:3
cmap(:,ii) = pchip(x,cmapp(:,ii),xi);
end
cmap = flipud(cmap/255);
end
function cmap = whiteFade(varargin)
n = 100;
if nargin>0
n = varargin{1};
end
thisColor = 'blue';
if nargin>1
thisColor = varargin{2};
end
switch thisColor
case {'gray','grey'}
cmapp = [255,255,255;240,240,240;217,217,217;189,189,189;150,150,150;115,115,115;82,82,82;37,37,37;0,0,0];
case 'green'
cmapp = [247,252,245;229,245,224;199,233,192;161,217,155;116,196,118;65,171,93;35,139,69;0,109,44;0,68,27];
case 'blue'
cmapp = [247,251,255;222,235,247;198,219,239;158,202,225;107,174,214;66,146,198;33,113,181;8,81,156;8,48,107];
case 'red'
cmapp = [255,245,240;254,224,210;252,187,161;252,146,114;251,106,74;239,59,44;203,24,29;165,15,21;103,0,13];
otherwise
warning(['sorry your color argument ' thisColor ' was not recognized']);
end
cmap = interpomap(n,cmapp);
end
% Eat a approximate colormap, then interpolate the rest of it up.
function cmap = interpomap(n,cmapp)
x = linspace(1,n,size(cmapp,1));
xi = 1:n;
cmap = zeros(n,3);
for ii=1:3
cmap(:,ii) = pchip(x,cmapp(:,ii),xi);
end
cmap = (cmap/255); % flipud??
end

View File

@@ -0,0 +1,6 @@
*.sublime-workspace
*.tap
test/*.test.*
*.asv
*.m~
octave-workspace

View File

@@ -0,0 +1,16 @@
language: c++
before_install:
- sudo add-apt-repository -y ppa:octave/stable
- sudo apt-get update -qq
- sudo apt-get install gdb # to capture backtrace of eventual failures
- sudo apt-get install octave
- sudo apt-get purge libopenblas-base # fixes PPA Octave 4.0 crash on Travis
before_script:
- ulimit -c unlimited -S # enable core dumps for Octave crash debugging
script:
- ./runtests.sh /usr/bin/octave
notifications:
hipchat: f4c2c5f87adc85025545e5b59b3fbe@Matlab2tikz
after_failure:
- COREFILE=$(find . -maxdepth 1 -name "core*" | head -n 1) # find core file
- gdb -c "$COREFILE" -ex "thread apply all bt" -ex "set pagination 0" -batch /usr/bin/octave-cli # print stack trace

View File

@@ -0,0 +1,64 @@
# Maintainer
* [Egon Geerardyn](https://github.com/egeerardyn) is the current maintainer (2015 - now).
* [Nico Schlömer](https://github.com/nschloe) designed and implemented the intial version and was the first maintainer (2008 - 2015).
# Contributors
Thanks for patches, suggestions, and other contributions go to:
* [Ben Abbott](https://github.com/bpabbott)
* Martijn Aben (The MathWorks)
* [Nicolas Alt](https://github.com/nalt)
* [Eshwar Andhavarapu](https://github.com/gontadu)
* [Matt Bauman](https://github.com/mbauman)
* Eike Blechschmidt
* [Klaus Broelemann](https://github.com/Broele)
* [Katherine Elkington](https://github.com/kelkington)
* [Thomas Emmert](https://github.com/murmlgrmpf)
* Andreas Gäb
* [Egon Geerardyn](https://github.com/egeerardyn)
* Roman Gesenhues
* Michael Glasser (The MathWorks)
* [David Haberthür](https://github.com/habi)
* [Patrick Häcker](https://github.com/MagicMuscleMan)
* [Ulrich Herter](https://github.com/ulijh)
* [David Horsley](https://github.com/widdma)
* Kári Hreinsson
* [Lucas Jeub](https://github.com/LJeub)
* [Martin Kiefel](https://github.com/mkiefel)
* [Andreas Kloeckner](https://github.com/akloeckner)
* Mykel Kochenderfer
* [Oleg Komarov](https://github.com/okomarov)
* Henk Kortier
* [Tom Lankhorst](https://github.com/tomlankhorst)
* [Burkart Lingner](https://github.com/burkart)
* Theo Markettos
* [Dragan Mitrevski](https://github.com/nidrosianDeath)
* [Jason Monschke](https://github.com/jam4375)
* Francesco Montorsi
* Ricardo Santiago Mozos
* Johannes Mueller-Roemer
* [Ali Ozdagli](https://github.com/aliirmak)
* [Richard Peschke](https://github.com/RPeschke)
* [Peter Ploß](https://github.com/PeterPablo)
* Julien Ridoux
* [Christoph Rüdiger](https://github.com/mredd)
* Carlos Russo
* [Michael Schellenberger Costa](https://github.com/miscco)
* [Manuel Schiller](https://github.com/dachziegel)
* [Nico Schlömer](https://github.com/nschloe)
* Johannes Schmitz
* Michael Schoeberl
* [Jan Taro Svejda](https://github.com/JTSvejda)
* [José Vallet](https://github.com/josombio)
* [Thomas Wagner](https://github.com/Aikhjarto)
* Donghua Wang
* [Patrick Wang](https://github.com/patrickkwang)
* Robert Whittlesey
* Pooya Ziraksaz
* Bastiaan Zuurendonk (The MathWorks)
* GitHub users: [andreas12345](https://github.com/andreas12345), [karih](https://github.com/karih), [theswitch](https://github.com/theswitch)
# Acknowledgements
Matlab2tikz has once greatly profited from its ancestor: [Matfig2PGF](http://www.mathworks.com/matlabcentral/fileexchange/12962) written by Paul Wagenaars.
Also, the authors would like to thank [Christian Feuersänger](https://github.com/cfeuersaenger) for the [Pgfplots](http://pgfplots.sourceforge.net) package which forms the basis for the matlab2tikz output on the LaTeX side.

View File

@@ -0,0 +1,439 @@
# 2016-08-15 Version 1.1.0 [Egon Geerardyn](egon.geerardyn@gmail.com)
* Added or improved support for:
- Octave 4.0 (#759)
- `scatter`, `quiver` and `errorbar` support in Octave (#669)
- `cleanfigure` has been improved:
* New and superior (Opheim) simplification algorithm
* Simplification for `plot3` (3D plots) (#790)
* Vectorized implementations (#756, #737)
* Overall clean-up of the code (#797, #787, #776, #744)
* Optional limitation of data precision (#791)
* Textbox removal is being phased out (#817)
- Quiver plots now translate to native pgfplots quivers (#679, #690)
- Legends, especially with `plotyy`, now use `\label` (#140, #760, #773)
- Tick labels with `datetime` (#383, #803)
- `contourf`/`contour` plots with matrix arguments and nonstandard line widths (#592, #721, #722, #871)
- Colored ticks and axes (#880, #908)
- Scatter plots with different marker colors and sizes (#859, #861)
- `colorbar` positioning and tick placement (#933, #937, #941)
- The self-updater has been improved
* New parameters:
- `arrowHeadSizeFactor` for tweaking the size of arrowheads
- `semanticLineWidths` for tweaking semantic line width conversion (e.g. `thick` instead of `0.8pt`)
* Extra requirements:
- Quiver plots require `\usetikzlibrary{arrows.meta}`
* Bug fixes:
- Errorbars without lines & markers (#813)
- `light`/`camera` objects are now ignored (#684)
- Draw baseline in bar/stem plots (#798)
- Multiple annotation containers (#728, #730)
- Legends of bode plots (#700, #702)
- Titles of bode plots (#715, #716, #753)
- Patch without fill/edge color (#682, #701, #740)
- Warn about usage of faceted interp shader (#699)
- Tick labels are properly escaped now (#711)
- Swapped image dimensions (#714)
- Width of bar plots was incorrect (#727, #696)
- Stacking and placement of bar plots (#851, #845, #840, #785, #903)
- Handling of tick labels when `parseStrings=false` (#86, #871)
- Properly escape tick labels for LaTeX (#710, #711, #820, #821)
- Respect edge color in `scatter` plots (#900)
- Output directory is created automatically (#889, #929)
- TikZ output format has been improved slightly (#936, #921, #801)
* For developers:
- Please check out the (guidelines)[CONTRIBUTING.md]
- We now use `allchild` and `findall` (#718)
- SublimeText project files
- Test hashes can be saved selectively (#720)
- Continuous testing for MATLAB and Octave 3.8 with Jenkins
- Test suite timing is tracked (#738)
- The testing reports have been improved for GitHub (#708)
- Testing can output to different directories (#818)
- A new tool to help track regressions (#814)
- A new tool to consistently format the code (#808, #809)
- `figure2dot` updated for HG2
# 2015-06-15 Version 1.0.0 [Egon Geerardyn](egon.geerardyn@gmail.com)
* Added support for:
- Annotations (except arrows) in R2014b (#534)
- `Histogram` in R2014b (#525)
- Filled contour plots in R2014b (#379, #500)
- Contour plots with color maps in R2014b (#380, #500)
- Axes background color and overlap (#6, #509, #510)
- Horizontal/Vertical text alignment (#491)
* Extra requirements:
- Patch plots now require `\usepgfplotslibrary{patchplots}` (#386, #497)
* Bug fixes:
- Pgfplots 1.12 (`row sep=crcr`) in combination with `externalData==true` (#548)
- Updater has been fixed (#502)
- 3D plot sizing takes viewing angle into account (#560, #630, #631)
- Alpha channel (transparency) in images (#561)
- Colorbar labels in R2014b (#429, #488)
- Scaling of color data at axes level (#486)
- Text formatting (for `TeX` parser) is improved (#417)
- Support for `|` character in labels (#587, #589)
- Legends for `stairs` and `area` plots (#601, #602)
- `cleanfigure()` removes points outside of the axes for `stairs` plots (#226, #533)
- `cleanfigure()` removes points outside of the axes better (#392, #400, #547)
- Support `>` and `<` in text (#522)
- Better text positioning (#518)
- Text boxes on 3D graphs (#528)
- File closing is more robust (#496, #555)
- TikZ picture output, i.e.`imageAsPng==false`, improved (#581, #596)
- `standalone==true` sets the font and input encoding in LaTeX (#590)
- Legend text alignment in Octave (#668)
- Improved Octave legend if not all lines have an entry (#607, #619, #653)
- Legend without a drawn box in R2014b+ (#652)
- Misc. fixes: #426, #513, #520, #665
* For developers:
- The testing framework has been revamped (see also `test/README.md`)
- A lot of the tests have been updated (#604, #614, #638, ...)
- Cyclomatic complexity of the code has been reduced (#391)
- Repository has been moved to [matlab2tikz/matlab2tikz](https://github.com/matlab2tikz/matlab2tikz)
- Extra files have been pruned (#616)
# 2014-11-02 Version 0.6.0 [Nico Schlömer](nico.schloemer@gmail.com)
* Annotation support in R2014a and earlier
* New subplot positioning approach (by Klaus Broelemann) that uses absolute instead of relative positions.
* Support stacked bar plots and others in the same axes (needs pgfplots 1.11).
* Support legends with multiline entries.
* Support for the alpha channel in PNG output.
* Test framework updated and doesn't display figures by default.
* Major code clean-up and code complexity checks.
* Bug fixes:
- Cycle paths only when needed (#317, #49, #404)
- Don't use infinite xmin/max, etc. (#436)
- Warn about the `noSize` parameter (#431)
- Images aren't flipped anymore (#401)
- No scientific notation in width/height (#396)
- Axes with custom colors (#376)
- Mesh plots are exported properly (#382)
- Legend colors are handled better (#389)
- Handle Z axis properties for quiver3 (#406)
- Better text handling, e.g. degrees (#402)
- Don't output absolute paths into TikZ by default
- ...
# 2014-10-20 Version 0.5.0 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for MATLAB 2014b (with it's substantial graphics changes).
All credit goes to Egon Geerardyn.
* Bugfixes:
- single bar width
- invisible bar plots
- surface options
- patch plots and cycling
- patches with literal colors
# 2014-03-07 Version 0.4.7 [Nico Schlömer](nico.schloemer@gmail.com)
* Acid tests: Remove MATLAB-based `eps2pdf`.
* Bugfixes:
- multiple patches
- log plot with nonzero baseline
- marker options for scatter plots
- table data formatting
- several fixes for Octave
# 2014-02-07 Version 0.4.6 [Nico Schlömer](nico.schloemer@gmail.com)
* Set `externalData` default to `false`.
* Properly check for required Pgfplots version.
* Marker scaling in scatter plots.
# 2014-02-02 Version 0.4.5 [Nico Schlömer](nico.schloemer@gmail.com)
* Arrange data in tables.
* Optionally define custom colors.
* Allow for strict setting of font sizes.
* Bugfixes:
- tick labels for log plots
- tick labels with commas
# 2014-01-02 Version 0.4.4 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for color maps with scatter plots.
* Support for different-length up-down error bars.
* Input options validation.
* Bugfixes:
- legends for both area and line plots
- invisible text fields
# 2013-10-20 Version 0.4.3 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for 3D quiver plots.
* Extended support for colorbar axis options.
* New logo!
* Bugfixes:
- text generation
- extraCode option
- join strings
- ...
# 2013-09-12 Version 0.4.2 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for explicit color specification in 3D plots.
* Better color handling for patch plots.
* Support for various unicode characters.
* Bugfixes:
- edge colors for bar plots
- multiple color bars
- ...
# 2013-08-14 Version 0.4.1 [Nico Schlömer](nico.schloemer@gmail.com)
* Replaced option `extraTikzpictureCode` by `extraCode`
for inserting code at the beginning of the file.
* Support for relative text positioning.
* Improved documentation.
* Code cleanup: moved all figure manipulations over to cleanfigure()
* Bugfixes:
- error bars
- empty tick labels
- ...
# 2013-06-26 Version 0.4.0 [Nico Schlömer](nico.schloemer@gmail.com)
* Added `cleanfigure()` for removing unwanted entities from a plot
before conversion
* Add option `floatFormat` to allow for custom specification of the format
of float numbers
* Bugfixes:
- linewidth for patches
- ...
# 2013-04-13 Version 0.3.3 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for:
- pictures in LaTeX subfloats
* Bugfixes:
- axes labels
- extra* options
- logscaled axes
- ...
# 2013-03-14 Version 0.3.2 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for:
- waterfall plots
* Bugfixes:
- axis locations
- color handling
- stacked bars
- ...
# 2013-02-15 Version 0.3.1 [Nico Schlömer](nico.schloemer@gmail.com)
* Use `table{}` for plots for cleaner output files.
* Support for:
- hg transformations
- pcolor plots
* Removed command line options:
- `minimumPointsDistance`
* Bugfixes:
- legend positioning and alignment
- tick labels
- a bunch of fixed for Octave
- line width for markers
- axis labels for color bars
- image trimming
- subplots with bars
- ...
# 2012-11-19 Version 0.3.0 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for:
- area plots
- legend position
- inner color bars
- log-scaled color bars
* New command line options:
- `standalone` (create compilable TeX file)
- `checkForUpdates`
* `mlint` cleanups.
* Removed deprecated options.
* Bugfixes:
- colorbar-axis association
- option parsing
- automatic updater
- unit 'px'
- ...
# 2012-09-01 Version 0.2.3 [Nico Schlömer](nico.schloemer@gmail.com)
* Multiline text for all entities.
* Support for logical images.
* Support for multiple legends (legends in subplots).
* Fixed version check bug.
* Fix `minimumPointsDistance`.
# 2012-07-19 Version 0.2.2 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for multiline titles and axis labels.
* Respect log-scaled axes for `minimumPointsDistance`.
* Add support for automatic graph labels via new option.
* About 5 bugfixes.
# 2012-05-04 Version 0.2.1 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for color maps.
* Support for native color bars.
* Partial support for hist3 plots.
* Support for spectrogram plots.
* Support for rotated text.
* Native handling of `Inf`s and `NaN`s.
* Better info text.
* matlab2tikz version checking.
* Line plotting code cleanup.
* About 10 bugfixes.
# 2012-03-17 Version 0.2.0 [Nico Schlömer](nico.schloemer@gmail.com)
* Greatly overhauled text handling. (Burkhart Lingner)
* Added option `tikzFileComment`.
* Added option `parseStrings`.
* Added option `extraTikzpictureSettings`.
* Added proper documetion (for `help matlab2tikz`).
* Improved legend positioning, orientation.
* Support for horizontal bar plots.
* Get bar widths right.
* Doubles are plottet with 15-digit precision now.
* Support for rectangle objects.
* Better color handling.
* Testing framework improvements.
* Several bugfixes:
- ticks handled more concisely
- line splitting bugs
- ...
# 2011-11-22 Version 0.1.4 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for scatter 3D plots.
* Support for 3D parameter curves.
* Support for 3D patches.
* Support for minor ticks.
* Add option `interpretTickLabelsAsTex` (default `false`).
* Several bugfixes:
- `%` sign in annotations
- fixed `\omega` and friends in annotations
- proper legend for bar plots
- don't override PNG files if there is more than one image plot
- don't always close patch paths
# 2011-08-22 Version 0.1.3 [Nico Schlömer](nico.schloemer@gmail.com)
* Greatly overhauled text handling.
* Better Octave compatibility.
* Several bugfixes:
- subplot order
- environment detection
# 2011-06-02 Version 0.1.2 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for logscaled color bar.
* Support for truecolor images.
* Initial support for text handles.
* Speed up processing for line plots.
* Several bugfixes:
- axis labels, tick labels, etc. for z-axis
- marker handling for scatter plots
- fix for unicolor scatter plots
# 2011-04-06 Version 0.1.1 [Nico Schlömer](nico.schloemer@gmail.com)
* Improved Octave compatibility.
* Several bugfixes:
- input parser
# 2011-01-31 Version 0.1.0 [Nico Schlömer](nico.schloemer@gmail.com)
* Basic Octave compatibility.
* Several bugfixes:
- bar plots fix (thanks to Christoph Rüdiger)
- fix legends with split graphs
# 2010-09-10 Version 0.0.7 [Nico Schlömer](nico.schloemer@gmail.com)
* Compatibility fixes for older MATLAB installations.
* Several bugfixes:
- line plots with only one point
- certain surface plots
- orientation of triangle markers (`<` vs. `>`)
- display of the color `purple`
# 2010-05-06 Version 0.0.6 [Nico Schlömer](nico.schloemer@gmail.com)
* Support for scatter plots.
* Preliminary support for surface plots; thanks to Pooya.
* Large changes in the codebase:
- next to `matlab2tikz.m`, the file `pgfplotsEnvironment.m` is now needed as well; it provides a much better structured approach to storing and writing environments when parsing the MATLAB(R) figure
* proper MATLAB(R) version check
* lots of small fixes
# 2009-12-21 Version 0.0.5 [Nico Schlömer](nico.schloemer@ua.ac.be)
* Improvements in axis handling:
- colored axes
- allow different left and right ordinates
* Improvements for line plots:
- far outliers are moved toward the plot,
avoiding `Dimension too large`-type errors in LaTeX
- optional point reduction by new option `minimumPointsDistance`
* Improvements for image handling:
- creation of a PNG file, added by `\addplot graphics`
- fixed axis orientation bug
* Bugfixes for:
- multiple axes
- CMYK colors
- legend text alignment (thanks Dragan Mitrevski)
- transparent patches (thanks Carlos Russo)
* Added support for:
- background color
- Bode plots
- zplane plots
- freqz plots
# 2009-06-09 Version 0.0.4 [Nico Schlömer](nico.schloemer@ua.ac.be)
* Added support for:
- error bars (thanks Robert Whittlesey for the suggestion)
* Improvents in:
- legends (thanks Theo Markettos for the patch),
- images,
- quiver plots (thanks Robert for spotting this).
* Improved options handling.
* Allow for custom file encoding (thanks Donghua Wang for the suggestion).
* Numerous bugfixes (thanks Andreas Gäb).
# 2009-03-08 Version 0.0.3 [Nico Schlömer](nico.schloemer@ua.ac.be)
* Added support for:
- subplots
- reverse axes
* Completed support for:
- images
# 2009-01-08 Version 0.0.2 [Nico Schlömer](nico.schloemer@ua.ac.be)
* Added support for:
- quiver (arrow) plots
- bar plots
- stem plots
- stairs plots
* Added preliminary support for:
- images
- rose plots
- compass plots
- polar plots
* Moreover, large code improvement have been introduced, notably:
- aspect ratio handling
- color handling
- plot options handling
# 2008-11-07 Version 0.0.1 [Nico Schlömer](nico.schloemer@ua.ac.be)
* Initial version

View File

@@ -0,0 +1,66 @@
# Contributing to matlab2tikz
You can contribute in many ways to `matlab2tikz`:
- report bugs,
- suggest new features,
- write documentation,
- fix some of our bugs and implement new features.
The first part of this document is geared more towards users of `matlab2tikz`.
The latter part is only relevant if you want to write some code for `matlab2tikz`.
## How to report a bug or ask for help
1. Make sure you are using the [latest release](https://github.com/matlab2tikz/matlab2tikz/releases/latest) or even the [development version](https://github.com/matlab2tikz/matlab2tikz/tree/develop) of `matlab2tikz` and check that the problem still exists.
2. Also make sure you are using a recent version of the required LaTeX packages (especially [`pgfplots`](http://ctan.org/pkg/pgfplots) and the [`TikZ`](http://ctan.org/pkg/pgf) libraries)
3. You can submit your bug report or question to our [issue tracker](https://github.com/matlab2tikz/matlab2tikz/issues).
Please, have a look at "[How to Ask Questions the Smart Way](http://www.catb.org/esr/faqs/smart-questions.html)" and "[Writing Better Bug Reports](http://martiancraft.com/blog/2014/07/good-bug-reports/)" for generic guidelines. In short:
- Mention the version of MATLAB/Octave, the operating system, `matlab2tikz`, `pgfplots` and which `LaTeX` compiler you are using.
- Choose a descriptive title for your issue report.
- A short MATLAB code snippet that generates a plot where the problem occurs. Please limit this to what is strictly necessary to show the issue!
- Explain what is wrong with the conversion of the figure (or what error messages you see).
- Often it can be useful to also include a figure, `TikZ` code, ... to illustrate your point.
## How to request new features
Please check first whether the feature hasn't been [requested](https://github.com/matlab2tikz/matlab2tikz/labels/feature%20request) before and do join the relevant topic in that case or maybe it has already been implemented in the [latest development version](https://github.com/matlab2tikz/matlab2tikz/tree/develop).
If your feature is something new and graphical, please also have a look at the [`pgfplots`](https://www.ctan.org/pkg/pgfplots) manual to see if it supports the feature you want.
In some cases it is more constructive to request the feature in the [`pgfplots` bug tracker](https://sourceforge.net/p/pgfplots/bugs/).
Please submit you feature request as any [bug report](https://github.com/matlab2tikz/matlab2tikz/labels/feature%20request) and make sure that you include enough details in your post, e.g.:
- What are you trying to do?
- What should it look like or how should it work?
- Is there a relevant section in the `pgfplots` or `MATLAB` documentation?
## Submitting pull requests (PRs)
Before you start working on a bug or new feature, you might want to check that nobody else has been assigned to the relevant issue report.
To avoid wasted hours, please just indicate your interest to tackle the issue.
### Recommended workflow
[Our wiki](https://github.com/matlab2tikz/matlab2tikz/wiki/Recommended-git-workflow) contains more elaborate details on this process. Here is the gist:
- It is highly recommended to start a feature branch for your work.
- Once you have finished the work, please try to run the test suite and report on the outcome in your PR (see below).
- Make sure that you file your pull request against the `develop` branch and *not* the `master` branch!
- Once you have filed your PR, the review process starts. Everybody is free to join this discussion.
- At least one other developer will review the code and signal their approval (often using a thumbs-up, :+1:) before the PR gets pulled into `develop`.
- Once you have addressed all comments, one of the developers will merge your code into the `develop` branch.
If you still feel uncomfortable with `git`, please have a look at [this page](https://github.com/matlab2tikz/matlab2tikz/wiki/Learning-git) for a quick start.
### Running the test suite
We know that at first the test suite can seem a bit intimidating, so we tend to be lenient during your first few PRs. However, we encourage you to run the test suite on your local computer and report on the results in your PR if any failures pop up.
To run the test suite, please consult its [README](https://github.com/matlab2tikz/matlab2tikz/blob/develop/test/README.md).
## Becoming a member of [matlab2tikz](https://github.com/matlab2tikz)
Once you have submitted your first pull request that is of reasonable quality, you may get invited to join the [Associate Developers](https://github.com/orgs/matlab2tikz/teams/associate-developers) group.
This group comes with *no* responsibility whatsoever and merely serves to make it easier for you to "claim" the features you want to work on.
Once you have gained some experience (with `git`/GitHub, our codebase, ...) and have contributed your fair share of great material, you will get invited to join the [Developers](https://github.com/orgs/matlab2tikz/teams/developers) team.
This status gives you push access to our repository and hence comes with the responsibility to not abuse your push access.
If you feel you should have gotten an invite for a team, feel free to contact one of the [owners](https://github.com/orgs/matlab2tikz/teams/owners).

View File

@@ -0,0 +1,24 @@
Copyright (c) 2008--2016 Nico Schlömer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,95 @@
**The updater in matlab2tikz 0.6.0 (and older) no longer works.**
**Please [update manually](http://www.mathworks.com/matlabcentral/fileexchange/22022-matlab2tikz-matlab2tikz?download=true) if you are not using matlab2tikz 1.0.0 or newer!**
[![Build Status](https://travis-ci.org/matlab2tikz/matlab2tikz.svg?branch=master)](https://travis-ci.org/matlab2tikz/matlab2tikz) [![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.18605.svg)](http://dx.doi.org/10.5281/zenodo.18605)
![matlab2tikz](https://raw.githubusercontent.com/wiki/matlab2tikz/matlab2tikz/matlab2tikz.png)
`matlab2tikz` is a MATLAB(R) script to convert native MATLAB(R) figures to TikZ/Pgfplots figures that integrate seamlessly in LaTeX documents.
To download the official releases and rate `matlab2tikz`, please visit its page on [FileExchange](http://www.mathworks.com/matlabcentral/fileexchange/22022).
`matlab2tikz` converts most MATLAB(R) figures, including 2D and 3D plots.
For plots constructed with third-party packages, however, your mileage may vary.
Installation
============
1. Extract the ZIP file (or clone the git repository) somewhere you can easily reach it.
2. Add the `src/` folder to your path in MATLAB/Octave: e.g.
- using the "Set Path" dialog in MATLAB, or
- by running the `addpath` function from your command window or `startup` script.
Make sure that your LaTeX installation is up-to-date and includes:
* [TikZ/PGF](http://www.ctan.org/pkg/pgf) version 3.0 or higher
* [Pgfplots](http://www.ctan.org/pkg/pgfplots) version 1.13 or higher
* [Amsmath](https://www.ctan.org/pkg/amsmath) version 2.14 or higher
* [Standalone](http://www.ctan.org/pkg/standalone) (optional)
It is recommended to use the latest stable version of these packages.
Older versions may work depending on the actual MATLAB(R) figure you are converting.
Usage
=====
Typical usage of `matlab2tikz` consists of converting your MATLAB plot to a TikZ/LaTeX file and then running a LaTeX compiler to produce your document.
MATLAB
------
1. Generate your plot in MATLAB(R).
2. Run `matlab2tikz`, e.g. using
```matlab
matlab2tikz('myfile.tex');
```
LaTeX
-----
Add the contents of `myfile.tex` into your LaTeX source code, for example using `\input{myfile.tex}`.
Make sure that the required packages (such as `pgfplots`) are loaded in the preamble of your document as in the example:
```latex
\documentclass{article}
\usepackage{pgfplots}
\pgfplotsset{compat=newest}
%% the following commands are needed for some matlab2tikz features
\usetikzlibrary{plotmarks}
\usetikzlibrary{arrows.meta}
\usepgfplotslibrary{patchplots}
\usepackage{grffile}
\usepackage{amsmath}
%% you may also want the following commands
%\pgfplotsset{plot coordinates/math parser=false}
%\newlength\figureheight
%\newlength\figurewidth
\begin{document}
\input{myfile.tex}
\end{document}
```
Remarks
-------
Most functions accept numerous options; you can check them out by inspecting their help:
```matlab
help matlab2tikz
```
Sometimes, MATLAB(R) plots contain some features that impede conversion to LaTeX; e.g. points that are far outside of the actual bounding box.
You can invoke the `cleanfigure` function to remove such unwanted entities before calling `matlab2tikz`:
```matlab
cleanfigure;
matlab2tikz('myfile.tex');
```
More information
================
* For more information about `matlab2tikz`, have a look at our [GitHub repository](https://github.com/matlab2tikz/matlab2tikz). If you are a good MATLAB(R) programmer or LaTeX writer, you are always welcome to help improving `matlab2tikz`!
* Some common problems and pit-falls are documented in our [wiki](https://github.com/matlab2tikz/matlab2tikz/wiki/Common-problems).
* If you experience (other) bugs or would like to request a feature, please visit our [issue tracker](https://github.com/matlab2tikz/matlab2tikz/issues).

View File

@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="2083.4766"
height="856.6734"
id="svg3051"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="matlab2tikz.svg">
<defs
id="defs3053" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.5"
inkscape:cx="1424.8959"
inkscape:cy="275.08087"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1680"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
fit-margin-top="5"
fit-margin-left="5"
fit-margin-right="5"
fit-margin-bottom="5" />
<metadata
id="metadata3056">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(96.763877,-104.02549)">
<path
style="fill:#ef8200;fill-opacity:1;stroke:none"
d="m 229.79347,889.91953 c -51.20296,-69.53548 -110.45905,-150.5284 -117.8477,-161.07762 -3.66043,-5.22621 -4.15923,-6.45766 -2.93039,-7.23468 0.81449,-0.51503 2.1559,-0.94763 2.9809,-0.96134 4.45183,-0.074 21.84491,-16.50352 39.70528,-37.5057 64.93246,-76.3547 212.14442,-292.5117 313.27925,-460 28.45805,-47.12908 55.23448,-94.70724 59.01417,-107.81682 2.21373,-7.67817 3.30364,-4.58186 5.54982,7.259 0.84717,4.46595 9.42069,39.94343 19.05225,78.83886 61.4356,248.09709 88.96885,376.22196 95.45994,444.21896 1.34274,14.06576 0.80116,31.67542 -1.06339,34.57694 -0.67969,1.05768 -29.41344,23.07306 -63.85279,48.92306 -46.63668,35.00526 -216.41083,162.82778 -297.77739,224.19582 l -3.13285,2.36286 z"
id="path3072"
inkscape:connector-curvature="0" />
<path
style="fill:#ffd912;fill-opacity:1"
d="M 828.49628,790.87316 C 751.45425,746.41551 656.62349,689.46978 647.33146,682.08395 c -2.47911,-1.97053 -2.52321,-2.17947 -1.05805,-5.01277 3.18772,-6.16438 4.02557,-14.85566 3.44538,-35.74002 -0.80529,-28.98647 -5.98761,-65.55929 -17.38517,-122.69097 -18.80756,-94.27528 -55.9766,-241.89492 -91.4729,-363.29152 -4.95189,-16.93533 -13.8484,-44.15875 -13.64905,-44.7568 0.19935,-0.59804 7.77507,16.91106 10.71396,23.16944 14.72516,31.35732 169.10504,368.5638 262.04653,572.37888 18.81036,41.25 35.965,78.78344 38.1214,83.40766 2.15641,4.62421 3.80419,8.50202 3.66173,8.61735 -0.14245,0.11533 -6.10901,-3.16608 -13.25901,-7.29204 z"
id="path3070"
inkscape:connector-curvature="0" />
<path
style="fill:#00b0cf;fill-opacity:1"
d="M 98.496283,712.93087 C 76.224153,691.69469 25.659453,651.42885 -55.133796,590.59166 l -36.630081,-27.58239 14.630081,-4.80606 C 1.4604828e-5,532.86436 84.848253,489.14509 155.49628,438.33721 c 90.71369,-65.23848 211.18904,-171.14032 339.75,-298.65155 14.7125,-14.59237 30.24771,-31.09621 30.24771,-30.65137 0,1.46965 -5.56006,9.74411 -33.50167,53.6059 -117.938,185.13504 -236.74752,364.94776 -300.3065,454.5 -22.45559,31.6391 -46.86362,64.24839 -58.75719,78.5 -10.15154,12.16419 -23.13943,25.02746 -25.20109,24.95928 -0.6772,-0.0224 -4.83126,-3.47326 -9.231257,-7.6686 z"
id="path2987"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:256.08959961px;line-height:125%;font-family:'Monotype Corsiva';-inkscape-font-specification:'Monotype Corsiva Bold Italic';letter-spacing:0px;word-spacing:0px;fill:#ef8200;fill-opacity:1;stroke:none"
x="835.56165"
y="469.78217"
id="text3863"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3865"
x="835.56165"
y="469.78217"
style="font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Liberation Sans';-inkscape-font-specification:'Liberation Sans Italic';fill:#000000;fill-opacity:1"><tspan
style="font-style:normal;-inkscape-font-specification:'Liberation Sans'"
id="tspan2999">MATLAB</tspan><tspan
style="font-style:italic;font-weight:bold;-inkscape-font-specification:'Liberation Sans Italic'"
id="tspan2997">2</tspan></tspan><tspan
sodipodi:role="line"
x="835.56165"
y="789.89417"
id="tspan3867"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Liberation Sans';-inkscape-font-specification:'Liberation Sans';fill:#ef8200;fill-opacity:1"><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Liberation Sans';-inkscape-font-specification:'Liberation Sans';fill:#000000"
id="tspan3875">Ti</tspan><tspan
style="font-style:italic;-inkscape-font-specification:'Liberation Sans Italic'"
id="tspan2995">k</tspan><tspan
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Liberation Sans';-inkscape-font-specification:'Liberation Sans';fill:#000000"
id="tspan3873">Z</tspan></tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -0,0 +1,49 @@
{
"folders":
[
{
"path": "."
}
],
"build_systems":
[
{
"name": "CI tests (Octave)",
"selector": "source.matlab",
"working_dir": "${project_path}",
"cmd": ["./runtests.sh", "octave"]
},
{
"name": "CI tests (MATLAB)",
"selector": "source.matlab",
"working_dir": "${project_path}",
"cmd": ["./runtests.sh", "matlab"]
},
{
"name": "CI tests (MATLAB HG1)",
"selector": "source.matlab",
"working_dir": "${project_path}",
"cmd": ["./runtests.sh", "matlab-hg1"]
},
{
"name": "CI tests (MATLAB HG2)",
"selector": "source.matlab",
"working_dir": "${project_path}",
"cmd": ["./runtests.sh", "matlab-hg2"]
},
{
"name": "ACID: make",
"working_dir": "${project_path}/test/tex",
"cmd": ["make","-j8"]
},
{
"name": "ACID: distclean",
"working_dir": "${project_path}/test/tex",
"cmd": ["make","distclean"]
}
],
"settings":
{
"FuzzyFilePath":{}
}
}

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env bash
#
# Test script runner for MATLAB2TIKZ continuous integration
#
# You can influence the execution by passing one or two parameters
# to this function, as
#
# ./runtests.sh RUNNER SWITCHES
#
# Arguments:
# - RUNNER: (path of) the binary you want to use to execute the tests
# default value: "octave"
# - SWITCHES: switches you want to pass to the executable
# default value: * "-nodesktop -r" if runner contains "matlab"
# * "--no-gui --eval" if runner contains "octave" and otherwise
#
# Used resources:
# - http://askubuntu.com/questions/299710/how-to-determine-if-a-string-is-a-substring-of-another-in-bash
# - http://www.thegeekstuff.com/2010/07/bash-case-statement/
# - http://stackoverflow.com/questions/229551/string-contains-in-bash
# - http://stackoverflow.com/questions/2870992/automatic-exit-from-bash-shell-script-on-error
# - http://www.davidpashley.com/articles/writing-robust-shell-scripts/
# - http://stackoverflow.com/questions/13998941/how-can-i-propagate-an-exit-status-from-expect-to-its-parent-bash-script
# - http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-8.html
## Make sure some failures are detected by the CI runners
function exitIfError {
# pass "$?" as argument: i.e. the exit status of the last call
if [ "$1" -ne 0 ]; then
exit $1;
fi
}
## Handle Runner and Switches variables
Runner=$1
Switches=$2
if [ -z "$Runner" ] ; then
Runner="octave"
fi
if [ -z "$Switches" ] ; then
case "$Runner" in
*matlab* )
Switches="-nodesktop -r"
;;
*octave* )
Switches="--no-gui --eval"
;;
* )
# Fall back to Octave switches
Switches="--no-gui --eval"
;;
esac
fi
## Make sure MATLAB/Octave know the intent
# note: the export is required
export CONTINUOUS_INTEGRATION=true
export CI=true
## Actually run the test suite
cd test
TESTDIR=`pwd`
# also CD in MATLAB/Octave to make sure that startup files
# cannot play any role in setting the path
${Runner} ${Switches} "cd('${TESTDIR}'); runMatlab2TikzTests"
exitIfError $?
cd ..
## Post-processing
# convert MD report into HTML using pandoc if available
MDFILE="test/results.test.md"
if [ ! -z `which pandoc` ]; then
if [ -f $MDFILE ]; then
HTMLFILE=${MDFILE/md/html}
# replace the emoji while we're at it
pandoc -f markdown -t html $MDFILE -o $HTMLFILE
sed -i -- 's/:heavy_exclamation_mark:/❗️/g' $HTMLFILE
sed -i -- 's/:white_check_mark:/✅/g' $HTMLFILE
sed -i -- 's/:grey_question:/❔/g' $HTMLFILE
fi
fi

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
function formatWhitespace(filename)
% FORMATWHITESPACE Formats whitespace and indentation of a document
%
% FORMATWHITESPACE(FILENAME)
% Indents currently active document if FILENAME is empty or not
% specified. FILENAME must be the name of an open document in the
% editor.
%
% Rules:
% - Smart-indent with all function indent option
% - Indentation is 4 spaces
% - Remove whitespace in empty lines
% - Preserve indentantion after line continuations, i.e. ...
%
import matlab.desktop.editor.*
if nargin < 1, filename = ''; end
d = getDoc(filename);
oldLines = textToLines(d.Text);
% Smart indent as AllFunctionIndent
% Using undocumented feature from http://undocumentedmatlab.com/blog/changing-system-preferences-programmatically
editorProp = 'EditorMFunctionIndentType';
oldVal = com.mathworks.services.Prefs.getStringPref(editorProp);
com.mathworks.services.Prefs.setStringPref(editorProp, 'AllFunctionIndent');
restoreSettings = onCleanup(@() com.mathworks.services.Prefs.setStringPref(editorProp, oldVal));
d.smartIndentContents()
% Preserve crafted continuations of line
lines = textToLines(d.Text);
iContinuation = ~cellfun('isempty',strfind(lines, '...'));
iComment = ~cellfun('isempty',regexp(lines, '^ *%([^%]|$)','once'));
pAfterDots = find(iContinuation & ~iComment)+1;
for ii = 1:numel(pAfterDots)
% Carry over the change in space due to smart-indenting from the
% first continuation line to the last
p = pAfterDots(ii);
nWhiteBefore = find(~isspace(oldLines{p-1}),1,'first');
nWhiteAfter = find(~isspace(lines{p-1}),1,'first');
df = nWhiteAfter - nWhiteBefore;
if df > 0
lines{p} = [blanks(df) oldLines{p}];
elseif df < 0
df = min(abs(df)+1, find(~isspace(oldLines{p}),1,'first'));
lines{p} = oldLines{p}(df:end);
else
lines{p} = oldLines{p};
end
end
% Remove whitespace lines
idx = cellfun('isempty',regexp(lines, '[^ \t\n]','once'));
lines(idx) = {''};
d.Text = linesToText(lines);
end
function d = getDoc(filename)
import matlab.desktop.editor.*
if ~ischar(filename)
error('formatWhitespace:charFilename','The FILENAME should be a char.')
end
try
isEditorAvailable();
catch
error('formatWhitespace:noEditorApi','Check that the Editor API is available.')
end
if isempty(filename)
d = getActive();
else
% TODO: open file if it isn't open in the editor already
d = findOpenDocument(filename);
try
[~,filenameFound] = fileparts(d.Filename);
catch
filenameFound = '';
end
isExactMatch = strcmp(filename, filenameFound);
if ~isExactMatch
error('formatWhitespace:filenameNotFound','Filename "%s" not found in the editor.', filename)
end
end
end

View File

@@ -0,0 +1,123 @@
function figure2dot(filename, varargin)
%FIGURE2DOT Save figure in Graphviz (.dot) file.
% FIGURE2DOT(filename) saves the current figure as dot-file.
%
% FIGURE2DOT(filename, 'object', HGOBJECT) constructs the graph representation
% of the specified object (default: gcf)
%
% You can visualize the constructed DOT file using:
% - [GraphViz](http://www.graphviz.org) on your computer
% - [WebGraphViz](http://www.webgraphviz.com) online
% - [Gravizo](http://www.gravizo.com) for your markdown files
% - and a lot of other software such as OmniGraffle
%
% See also: matlab2tikz, cleanfigure, uiinspect, inspect
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'filename', @ischar);
ipp = ipp.addParamValue(ipp, 'object', gcf, @ishghandle);
ipp = ipp.parse(ipp, filename, varargin{:});
args = ipp.Results;
filehandle = fopen(args.filename, 'w');
finally_fclose_filehandle = onCleanup(@() fclose(filehandle));
% start printing
fprintf(filehandle, 'digraph simple_hierarchy {\n\n');
fprintf(filehandle, 'node[shape=box];\n\n');
% define the root node
node_number = 0;
p = get(args.object, 'Parent');
% define root element
type = get(p, 'Type');
fprintf(filehandle, 'N%d [label="%s"]\n\n', node_number, type);
% start recursion
plot_children(filehandle, p, node_number);
% finish off
fprintf(filehandle, '}');
% ----------------------------------------------------------------------------
function plot_children(fh, h, parent_node)
children = allchild(h);
for h = children(:)'
if shouldSkip(h), continue, end;
node_number = node_number + 1;
label = {};
label = addHGProperty(label, h, 'Type', '');
try
hClass = class(handle(h));
label = addProperty(label, 'Class', hClass);
catch
% don't do anything
end
label = addProperty(label, 'Handle', sprintf('%g', double(h)));
label = addHGProperty(label, h, 'Title', '');
label = addHGProperty(label, h, 'Axes', []);
label = addHGProperty(label, h, 'String', '');
label = addHGProperty(label, h, 'Tag', '');
label = addHGProperty(label, h, 'DisplayName', '');
label = addHGProperty(label, h, 'Visible', 'on');
label = addHGProperty(label, h, 'HandleVisibility', 'on');
% print node
fprintf(fh, 'N%d [label="%s"]\n', ...
node_number, m2tstrjoin(label, '\n'));
% connect to the child
fprintf(fh, 'N%d -> N%d;\n\n', parent_node, node_number);
% recurse
plot_children(fh, h, node_number);
end
end
end
% ==============================================================================
function bool = shouldSkip(h)
% returns TRUE for objects that can be skipped
objType = get(h, 'Type');
bool = ismember(lower(objType), guitypes());
end
% ==============================================================================
function label = addHGProperty(label, h, propName, default)
% get a HG property and assign it to a GraphViz node label
if ~exist('default','var') || isempty(default)
shouldOmit = @isempty;
elseif isa(default, 'function_handle')
shouldOmit = default;
else
shouldOmit = @(v) isequal(v,default);
end
if isprop(h, propName)
propValue = get(h, propName);
if numel(propValue) == 1 && ishghandle(propValue) && isprop(propValue, 'String')
% dereference Titles, labels, ...
propValue = get(propValue, 'String');
elseif ishghandle(propValue)
% dereference other HG objects to their raw handle value (double)
propValue = double(propValue);
elseif iscell(propValue)
propValue = ['{' m2tstrjoin(propValue,',') '}'];
end
if ~shouldOmit(propValue)
label = addProperty(label, propName, propValue);
end
end
end
function label = addProperty(label, propName, propValue)
% add a property to a GraphViz node label
if isnumeric(propValue)
propValue = num2str(propValue);
elseif iscell(propValue)
propValue = m2tstrjoin(propValue,sprintf('\n'));
end
label = [label, sprintf('%s: %s', propName, propValue)];
end
% ==============================================================================

View File

@@ -0,0 +1,231 @@
function parser = m2tInputParser()
%MATLAB2TIKZINPUTPARSER Input parsing for matlab2tikz.
% This implementation exists because Octave is lacking one.
% Initialize the structure.
parser = struct ();
% Public Properties
parser.Results = {};
% Enabel/disable parameters case sensitivity.
parser.CaseSensitive = false;
% Keep parameters not defined by the constructor.
parser.KeepUnmatched = false;
% Enable/disable warning for parameters not defined by the constructor.
parser.WarnUnmatched = true;
% Enable/disable passing arguments in a structure.
parser.StructExpand = true;
% Names of parameters defined in input parser constructor.
parser.Parameters = {};
% Names of parameters not defined in the constructor.
parser.Unmatched = struct ();
% Names of parameters using default values.
parser.UsingDefaults = {};
% Names of deprecated parameters and their alternatives
parser.DeprecatedParameters = struct();
% Handles for functions that act on the object.
parser.addRequired = @addRequired;
parser.addOptional = @addOptional;
parser.addParamValue = @addParamValue;
parser.deprecateParam = @deprecateParam;
parser.parse = @parse;
% Initialize the parser plan
parser.plan = {};
end
% =========================================================================
function p = parser_plan (q, arg_type, name, default, validator)
p = q;
plan = p.plan;
if (isempty (plan))
plan = struct ();
n = 1;
else
n = numel (plan) + 1;
end
plan(n).type = arg_type;
plan(n).name = name;
plan(n).default = default;
plan(n).validator = validator;
p.plan = plan;
end
% =========================================================================
function p = addRequired (p, name, validator)
p = parser_plan (p, 'required', name, [], validator);
end
% =========================================================================
function p = addOptional (p, name, default, validator)
p = parser_plan (p, 'optional', name, default, validator);
end
% =========================================================================
function p = addParamValue (p, name, default, validator)
p = parser_plan (p, 'paramvalue', name, default, validator);
end
% =========================================================================
function p = deprecateParam (p, name, alternatives)
if isempty(alternatives)
alternatives = {};
elseif ischar(alternatives)
alternatives = {alternatives}; % make cellstr
elseif ~iscellstr(alternatives)
error('m2tInputParser:BadAlternatives',...
'Alternatives for a deprecated parameter must be a char or cellstr');
end
p.DeprecatedParameters.(name) = alternatives;
end
% =========================================================================
function p = parse (p, varargin)
plan = p.plan;
results = p.Results;
using_defaults = {};
if (p.CaseSensitive)
name_cmp = @strcmp;
else
name_cmp = @strcmpi;
end
if (p.StructExpand)
k = find (cellfun (@isstruct, varargin));
for m = numel(k):-1:1
n = k(m);
s = varargin{n};
c = [fieldnames(s).'; struct2cell(s).'];
c = c(:).';
if (n > 1 && n < numel (varargin))
varargin = horzcat (varargin(1:n-1), c, varargin(n+1:end));
elseif (numel (varargin) == 1)
varargin = c;
elseif (n == 1);
varargin = horzcat (c, varargin(n+1:end));
else % n == numel (varargin)
varargin = horzcat (varargin(1:n-1), c);
end
end
end
if (isempty (results))
results = struct ();
end
type = {plan.type};
n = find( strcmp( type, 'paramvalue' ) );
m = setdiff (1:numel( plan ), n );
plan = plan ([n,m]);
for n = 1 : numel (plan)
found = false;
results.(plan(n).name) = plan(n).default;
if (~ isempty (varargin))
switch plan(n).type
case 'required'
found = true;
if (strcmpi (varargin{1}, plan(n).name))
varargin(1) = [];
end
value = varargin{1};
varargin(1) = [];
case 'optional'
m = find (cellfun (@ischar, varargin));
k = find (name_cmp (plan(n).name, varargin(m)));
if (isempty (k) && validate_arg (plan(n).validator, varargin{1}))
found = true;
value = varargin{1};
varargin(1) = [];
elseif (~ isempty (k))
m = m(k);
found = true;
value = varargin{max(m)+1};
varargin(union(m,m+1)) = [];
end
case 'paramvalue'
m = find( cellfun (@ischar, varargin) );
k = find (name_cmp (plan(n).name, varargin(m)));
if (~ isempty (k))
found = true;
m = m(k);
value = varargin{max(m)+1};
varargin(union(m,m+1)) = [];
end
otherwise
error( sprintf ('%s:parse', mfilename), ...
'parse (%s): Invalid argument type.', mfilename ...
)
end
end
if (found)
if (validate_arg (plan(n).validator, value))
results.(plan(n).name) = value;
else
error( sprintf ('%s:invalidinput', mfilename), ...
'%s: Input argument ''%s'' has invalid value.\n', mfilename, plan(n).name ...
);
end
p.Parameters = union (p.Parameters, {plan(n).name});
elseif (strcmp (plan(n).type, 'required'))
error( sprintf ('%s:missinginput', mfilename), ...
'%s: input ''%s'' is missing.\n', mfilename, plan(n).name ...
);
else
using_defaults = union (using_defaults, {plan(n).name});
end
end
if ~isempty(varargin)
% Include properties that do not match specified properties
for n = 1:2:numel(varargin)
if ischar(varargin{n})
if p.KeepUnmatched
results.(varargin{n}) = varargin{n+1};
end
if p.WarnUnmatched
warning(sprintf('%s:unmatchedArgument',mfilename), ...
'Ignoring unknown argument "%s"', varargin{n});
end
p.Unmatched.(varargin{n}) = varargin{n+1};
else
error (sprintf ('%s:invalidinput', mfilename), ...
'%s: invalid input', mfilename)
end
end
end
% Store the results of the parsing
p.Results = results;
p.UsingDefaults = using_defaults;
warnForDeprecatedParameters(p);
end
% =========================================================================
function result = validate_arg (validator, arg)
try
result = validator (arg);
catch %#ok
result = false;
end
end
% =========================================================================
function warnForDeprecatedParameters(p)
usedDeprecatedParameters = intersect(p.Parameters, fieldnames(p.DeprecatedParameters));
for iParam = 1:numel(usedDeprecatedParameters)
oldParameter = usedDeprecatedParameters{iParam};
alternatives = p.DeprecatedParameters.(oldParameter);
switch numel(alternatives)
case 0
replacements = '';
case 1
replacements = ['''' alternatives{1} ''''];
otherwise
replacements = deblank(sprintf('''%s'' and ',alternatives{:}));
replacements = regexprep(replacements,' and$','');
end
if ~isempty(replacements)
replacements = sprintf('From now on, please use %s to control the output.\n',replacements);
end
message = ['\n===============================================================================\n', ...
'You are using the deprecated parameter ''%s''.\n', ...
'%s', ...
'==============================================================================='];
warning('matlab2tikz:deprecatedParameter', ...
message, oldParameter, replacements);
end
end

View File

@@ -0,0 +1,216 @@
function [value] = m2tcustom(handle, varargin)
% M2TCUSTOM creates user-defined options for matlab2tikz output
%
% M2TCUSTOM can be used to create, get and set a properly formatted data
% structure to customize the conversion of MATLAB figures to TikZ using
% matlab2tikz. In particular, it allows to:
%
% * add blocks of LaTeX/TikZ code around any HG object,
% * add blocks of comments around any HG object,
% * add TikZ options to any HG object,
% * add LaTeX/TikZ code inside some HG objects (e.g. axes),
% * provide a custom handler to convert a particular HG object.
%
% Note that this provides advanced functionality. Only very basic
% sanity checks are performed such that injudicious use may produce
% broken TikZ figures!
%
% It is HIGHLY recommended that you are comfortable with:
%
% * writing pgfplots, TikZ and LaTeX code,
% * using the Handle Graphics (HG) framework in MATLAB/Octave, and
% * the inner working of matlab2tikz (for custom handlers)
%
% when you use this function. I.e. you should know what you are doing.
%
%
% Usage as a GETTER:
% ------------------
%
% value = M2TCUSTOM(handle)
% retrieves the current custom data structure from the HG object "handle"
%
%
% Usage as a SETTER:
% ------------------
%
% M2TCUSTOM(handle, ...)
% value = M2TCUSTOM(handle, ...)
% will construct the proper data structure and try to set it to the object
% |handle| if possible. The arguments (see below) are specified in
% key-value pairs akin to a normal |struct|, but here we do a few checks
% and data normalization.
%
% If we denote BLOCK to mean either a |char| or a |cellstr|, the following
% options can be passed. Different entries in a cellstr are assumed
% separated by a newline. The default values are empty.
%
% M2TCUSTOM(handle, 'commentBefore', BLOCK, ...)
% M2TCUSTOM(handle, 'commentAfter' , BLOCK, ...)
% to add comments before/after the object. Our code translates newlines
% and adds the percentage signs for you.
%
% M2TCUSTOM(handle, 'codeBefore', BLOCK, ...)
% M2TCUSTOM(handle, 'codeAfter', BLOCK, ...)
% M2TCUSTOM(handle, 'codeInsideFirst', BLOCK, ...)
% M2TCUSTOM(handle, 'codeInsideLast', BLOCK, ...)
% to add raw LaTeX/TikZ code respectively before, after, as first thing
% inside or as last thing inside the pgfplots representation of the object.
% Note that for some HG objects, (e.g. line objects), |codeInsideFirst|
% and |codeInsideLast| do not make any sense and are hence ignored.
%
% M2TCUSTOM(handle, 'extraOptions', OPTIONS, ...)
% adds extra pgfplots/TikZ options to the end of the option list. Here,
% OPTIONS is properly formatted TikZ code in
% - a |char| (e.g. 'color=red, line width=1pt' )
% - a |cellstr| (e.g. {'color=red','line width=1pt'})
%
% M2TCUSTOM(handle, 'customHandler', FUNCTION_HANDLE, ...)
% allows you to replace the default matlab2tikz handler for this object.
% This is not for the faint of heart and requires intimate knowledge of
% the matlab2tikz code base! We expect a function (either as |char| or
% function handle) that will be called as
%
% [m2t, str] = feval(handler, m2t, handle, custom)
%
% such that the expected function signature is:
%
% function [m2t, str] = handler(m2t, handle, custom)
%
% where |m2t| is an undocumented/unstable data structure,
% |str| is a char containing TikZ code representing the
% HG object |handle| as generated by your handler,
% |custom| is a structure as returned by |m2tcustom|
% from which you only need to handle |extraOptions|,
% |codeInsideFirst| and |codeInsideLast| when applicable.
% A particularly useful value for |customHandler| is 'drawNothing',
% which remove the object from the output.
%
%
% Example:
% --------
%
% Executing the following MATLAB code fragment:
%
% figure;
% plot(1:10);
% EOL = sprintf('\n');
% m2tCustom(gca, 'codeBefore' , ['<codeBefore>' EOL] , ...
% 'codeAfter' , ['<codeAfter>' EOL] , ...
% 'commentsBefore' , '<commentsBefore>' , ...
% 'commentsAfter' , '<commentsAfter>' , ...
% 'codeInsideFirst' , ['<codeInsideFirst>' EOL], ...
% 'codeInsideLast' , ['<codeInsideLast>' EOL], ...
% 'extraOptions' , '<extraOptions>');
%
% matlab2tikz('test.tikz')
%
% Should result in a |test.tikz| file with contents that look somewhat
% like this:
%
% \begin{tikzpicture}
% %<commentsBefore>
% <codeBefore>
% \begin{axis}[..., <extraOptions>]
% <codeInsideFirst>
% \addplot{...};
% <codeInsideLast>
% \end{axis}
% %<commentsAfter>
% <codeAfter>
% \end{tikzpicture}
%
% See also: matlab2tikz, setappdata, getappdata
%% arguments specific to this constructor function
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'handle', @isHgObject);
%% Declaration of the custom data structure
ipp = ipp.addParamValue(ipp, 'codeBefore', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'codeAfter', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'commentsBefore', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'commentsAfter', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'codeInsideFirst', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'codeInsideLast', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'extraOptions', '', @isCellstrOrChar);
ipp = ipp.addParamValue(ipp, 'customHandler', '', @isHandler);
%% Parse the arguments
ipp = ipp.parse(ipp, handle, varargin{:});
%% Construct custom data structure
% We leverage the results from the input parser. It provides us
% with validation already. We just need to remove bookkeeping fields
value = ipp.Results;
value = rmfield(value, {'handle'});
%% Normalize the actual values
value.codeBefore = cellstr2char(value.codeBefore);
value.codeAfter = cellstr2char(value.codeAfter);
value.codeInsideFirst = cellstr2char(value.codeInsideFirst);
value.codeInsideLast = cellstr2char(value.codeInsideLast);
value.commentsBefore = cellstr2char(value.commentsBefore);
value.commentsAfter = cellstr2char(value.commentsAfter);
if isempty(value.customHandler)
value = rmfield(value, 'customHandler');
end
% extraOptions gets normalized by |opts_append_userdefined|
%% Different Usage modes
MATLAB2TIKZ = 'matlab2tikz'; % key used for application data storage
if numel(varargin) == 0
%% GETTER MODE
% syntax: value = m2tcustom(h);
if ~isempty(handle)
object = getappdata(handle, MATLAB2TIKZ);
else
object = [];
end
if ~isempty(object)
value = object;
else
% |value| contains all default (empty) values
end
else
%% SETTER MODE
% syntax: m2tcustom(h , key1, val1, ...)
% syntax: value = m2tcustom([], key1, val1, ...)
if ~isempty(handle)
setappdata(handle, MATLAB2TIKZ, value);
end
end
end
% == INPUT VALIDATORS ==========================================================
function bool = isHgObject(value)
% true for HG object or empty (or numeric for backwards compatibility)
bool = isempty(value) || ishghandle(value) || isnumeric(value);
end
function bool = isCellstrOrChar(value)
% true for cellstr or char
bool = ischar(value) || iscellstr(value);
end
function bool = isHandler(value)
% true for char or function handle of the form [m2t, str] = f(m2t, h, opts)
bool = isempty(value) || ischar(value) || ...
(isa(value, 'function_handle') && ...
atLeastOrUnknown(nargin(value), 3) && ...
atLeastOrUnknown(nargout(value), 2));
end
function bool = atLeastOrUnknown(nargs, limit)
% checks for |nargin| and |nargout| >= |limit| (or equal to -1)
UNKNOWN = -1;
bool = (nargs == UNKNOWN) || nargs >= limit;
end
% == FIELD NORMALIZATION =======================================================
function value = cellstr2char(value)
% convert cellstr to char (and keep char unaffected)
if iscellstr(value)
EOL = sprintf('\n');
value = m2tstrjoin(value, EOL);
end
end
% ==============================================================================

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,5 @@
function errorUnknownEnvironment()
% Throw an error to indicate an unknwon environment (i.e. not MATLAB/Octave).
error('matlab2tikz:unknownEnvironment',...
'Unknown environment "%s". Need MATLAB(R) or Octave.', getEnvironment);
end

View File

@@ -0,0 +1,25 @@
function [env, versionString] = getEnvironment()
% Determine environment (Octave, MATLAB) and version string
% TODO: Unify private `getEnvironment` functions
persistent cache
if isempty(cache)
isOctave = exist('OCTAVE_VERSION', 'builtin') ~= 0;
if isOctave
env = 'Octave';
versionString = OCTAVE_VERSION;
else
env = 'MATLAB';
vData = ver(env);
versionString = vData.Version;
end
% store in cache
cache.env = env;
cache.versionString = versionString;
else
env = cache.env;
versionString = cache.versionString;
end
end

View File

@@ -0,0 +1,19 @@
function types = guitypes()
% GUITYPES returns a cell array of MATLAB/Octave GUI object types
%
% Syntax
% types = guitypes()
%
% These types are ignored by matlab2tikz and figure2dot.
%
% See also: matlab2tikz, figure2dot
types = {'uitoolbar', 'uimenu', 'uicontextmenu', 'uitoggletool',...
'uitogglesplittool', 'uipushtool', 'hgjavacomponent',...
'matlab.graphics.shape.internal.Button', ...
'matlab.ui.controls.ToolbarPushButton', ...
'matlab.ui.controls.ToolbarStateButton', ...
'matlab.ui.controls.ToolbarDropdown', ...
'matlab.ui.controls.AxesToolbar', ...
};
end

View File

@@ -0,0 +1,5 @@
function bool = isAxis3D(axisHandle)
% Check if elevation is not orthogonal to xy plane
axisView = get(axisHandle,'view');
bool = ~ismember(axisView(2),[90,-90]);
end

View File

@@ -0,0 +1,13 @@
function isBelow = isVersionBelow(versionA, versionB)
% Checks if versionA is smaller than versionB
vA = versionArray(versionA);
vB = versionArray(versionB);
n = min(length(vA), length(vB));
deltaAB = vA(1:n) - vB(1:n);
difference = find(deltaAB, 1, 'first');
if isempty(difference)
isBelow = false; % equal versions
else
isBelow = (deltaAB(difference) < 0);
end
end

View File

@@ -0,0 +1,320 @@
function m2tUpdater(about, verbose)
%UPDATER Auto-update matlab2tikz.
% Only for internal usage.
% Copyright (c) 2012--2014, Nico Schlömer <nico.schloemer@gmail.com>
% All rights reserved.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are
% met:
%
% * Redistributions of source code must retain the above copyright
% notice, this list of conditions and the following disclaimer.
% * Redistributions in binary form must reproduce the above copyright
% notice, this list of conditions and the following disclaimer in
% the documentation and/or other materials provided with the distribution
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
% POSSIBILITY OF SUCH DAMAGE.
% =========================================================================
fileExchangeUrl = about.website;
version = about.version;
mostRecentVersion = determineLatestRelease(version, fileExchangeUrl);
if askToUpgrade(mostRecentVersion, version, verbose)
tryToUpgrade(fileExchangeUrl, verbose);
userInfo(verbose, '');
end
end
% ==============================================================================
function shouldUpgrade = askToUpgrade(mostRecentVersion, version, verbose)
shouldUpgrade = false;
if ~isempty(mostRecentVersion)
userInfo(verbose, '**********************************************\n');
userInfo(verbose, 'New version (%s) available!\n', mostRecentVersion);
userInfo(verbose, '**********************************************\n');
warnAboutUpgradeImplications(version, mostRecentVersion, verbose);
askToShowChangelog(version);
reply = input(' *** Would you like to upgrade? y/n [n]:','s');
shouldUpgrade = ~isempty(reply) && strcmpi(reply(1),'y');
if ~shouldUpgrade
userInfo(verbose, ['\nTo disable the self-updater in the future, add ' ...
'"''checkForUpdates'',false" to the parameters.\n'] );
end
end
end
% ==============================================================================
function tryToUpgrade(fileExchangeUrl, verbose)
% Download the files and unzip its contents into two folders
% above the folder that contains the current script.
% This assumes that the file structure is something like
%
% src/matlab2tikz.m
% src/[...]
% src/private/m2tUpdater
% src/private/[...]
% AUTHORS
% ChangeLog
% [...]
%
% on the hard drive and the zip file. In particular, this assumes
% that the folder on the hard drive is writable by the user
% and that matlab2tikz.m is not symlinked from some other place.
pathstr = fileparts(mfilename('fullpath'));
targetPath = fullfile(pathstr, '..', '..');
% Let the user know where the .zip is downloaded to
userInfo(verbose, 'Downloading and unzipping to ''%s'' ...', targetPath);
% Try upgrading
try
% List current folder structure. Will use last for cleanup
currentFolderFiles = rdirfiles(targetPath);
% The FEX now forwards the download request to Github.
% Go through the forwarding to update the download count and
% unzip
html = urlread([fileExchangeUrl, '?download=true']);
expression = '(?<=\<a href=")[\w\-\/:\.]+(?=">redirected)';
url = regexp(html, expression,'match','once');
unzippedFiles = unzip(url, targetPath);
% The folder structure is additionally packed into the
% 'MATLAB Search Path' folder defined in FEX. Retrieve the
% top folder name
tmp = strrep(unzippedFiles,[targetPath, filesep],'');
tmp = regexp(tmp, filesep,'split','once');
tmp = cat(1,tmp{:});
topZipFolder = unique(tmp(:,1));
% If packed into the top folder, overwrite files into m2t
% main directory
if numel(topZipFolder) == 1
unzippedFilesTarget = fullfile(targetPath, tmp(:,2));
for ii = 1:numel(unzippedFiles)
movefile(unzippedFiles{ii}, unzippedFilesTarget{ii})
end
% Add topZipFolder to current folder structure
currentFolderFiles = [currentFolderFiles; fullfile(targetPath, topZipFolder{1})];
end
cleanupOldFiles(currentFolderFiles, unzippedFilesTarget);
userInfo(verbose, 'Upgrade has completed successfully.');
catch
err = lasterror(); %#ok needed for Octave
userInfo(verbose, ...
['Upgrade has failed with error message "%s".\n', ...
'Please install the latest version manually from %s !'], ...
err.message, fileExchangeUrl);
end
end
% ==============================================================================
function cleanupOldFiles(currentFolderFiles, unzippedFilesTarget)
% Delete files that were there in the old folder, but that are no longer
% present in the new release.
newFolderStructure = [getFolders(unzippedFilesTarget); unzippedFilesTarget];
deleteFolderFiles = setdiff(currentFolderFiles, newFolderStructure);
for ii = 1:numel(deleteFolderFiles)
x = deleteFolderFiles{ii};
if exist(x, 'dir') == 7
% First check for directories since
% `exist(x, 'file')` also checks for directories!
rmdir(x,'s');
elseif exist(x, 'file') == 2
delete(x);
end
end
end
% ==============================================================================
function mostRecentVersion = determineLatestRelease(version, fileExchangeUrl)
% Read in the Github releases page
url = 'https://github.com/matlab2tikz/matlab2tikz/releases/';
try
html = urlread(url);
catch %#ok
% Couldn't load the URL -- never mind.
html = '';
warning('m2tUpdate:siteNotFound', ...
['Cannot determine the latest version.\n', ...
'Either your internet is down or something went wrong.\n', ...
'You might want to check for updates by hand at %s.\n'], ...
fileExchangeUrl);
end
% Parse tag names which are the version number in the format ##.##.##
% It assumes that releases will always be tagged with the version number
expression = '(?<=matlab2tikz\/matlab2tikz\/releases\/tag\/)\d+\.\d+\.\d+';
tags = regexp(html, expression, 'match');
ntags = numel(tags);
% Keep only new releases
inew = false(ntags,1);
for ii = 1:ntags
inew(ii) = isVersionBelow(version, tags{ii});
end
nnew = nnz(inew);
% One new release
if nnew == 1
mostRecentVersion = tags{inew};
% Several new release, pick latest
elseif nnew > 1
tags = tags(inew);
tagnum = zeros(nnew,1);
for ii = 1:nnew
tagnum(ii) = [10000,100,1] * versionArray(tags{ii});
end
[~, imax] = max(tagnum);
mostRecentVersion = tags{imax};
% No new
else
mostRecentVersion = '';
end
end
% ==============================================================================
function askToShowChangelog(currentVersion)
% Asks whether the user wants to see the changelog and then shows it.
reply = input(' *** Would you like to see the changelog? y/n [y]:' ,'s');
shouldShow = isempty(reply) || ~strcmpi(reply(1),'n') ;
if shouldShow
fprintf(1, '\n%s\n', changelogUntilVersion(currentVersion));
end
end
% ==============================================================================
function changelog = changelogUntilVersion(currentVersion)
% This function retrieves the chunk of the changelog until the current version.
URL = 'https://github.com/matlab2tikz/matlab2tikz/raw/master/CHANGELOG.md';
changelog = urlread(URL);
currentVersion = versionString(currentVersion);
% Header is "# YYYY-MM-DD Version major.minor.patch [Manager](email)"
% Just match for the part until the version number. Here, we're actually
% matching a tiny bit too broad due to the periods in the version number
% but the outcome should be the same if we keep the changelog format
% identical.
pattern = ['\#\s*[\d-]+\s*Version\s*' currentVersion];
idxVersion = regexpi(changelog, pattern);
if ~isempty(idxVersion)
changelog = changelog(1:idxVersion-1);
else
% Just show the whole changelog if we don't find the old version.
end
changelog = replaceIssuesWithUrls(changelog);
end
% ==============================================================================
function changelog = replaceIssuesWithUrls(changelog)
% Replaces GitHub issues ("#...") with URLs
baseurl = 'https://github.com/matlab2tikz/matlab2tikz/issues/';
if strcmpi(getEnvironment(), 'MATLAB')
replacement = sprintf('<a href="%s$1">#$1</a>', baseurl);
changelog = regexprep(changelog, '\#(\d+)', replacement);
end
end
% ==============================================================================
function warnAboutUpgradeImplications(currentVersion, latestVersion, verbose)
% This warns the user about the implications of upgrading as dictated by
% Semantic Versioning.
switch upgradeSize(currentVersion, latestVersion);
case 'major'
% The API might have changed in a backwards incompatible way.
userInfo(verbose, 'This is a MAJOR upgrade!\n');
userInfo(verbose, ' - New features may have been introduced.');
userInfo(verbose, ' - Some old code/options may no longer work!\n');
case 'minor'
% The API may NOT have changed in a backwards incompatible way.
userInfo(verbose, 'This is a MINOR upgrade.\n');
userInfo(verbose, ' - New features may have been introduced.');
userInfo(verbose, ' - Some options may have been deprecated.');
userInfo(verbose, ' - Old code should continue to work but might produce warnings.\n');
case 'patch'
% No new functionality is introduced
userInfo(verbose, 'This is a PATCH.\n');
userInfo(verbose, ' - Only bug fixes are included in this upgrade.');
userInfo(verbose, ' - Old code should continue to work as before.')
end
userInfo(verbose, 'Please check the changelog for detailed information.\n');
userWarn(verbose, '\n!! By upgrading you will lose any custom changes !!\n');
end
% ==============================================================================
function cls = upgradeSize(currentVersion, latestVersion)
% Determines whether the upgrade is major, minor or a patch.
currentVersion = versionArray(currentVersion);
latestVersion = versionArray(latestVersion);
description = {'major', 'minor', 'patch'};
for ii = 1:numel(description)
if latestVersion(ii) > currentVersion(ii)
cls = description{ii};
return
end
end
cls = 'unknown';
end
% ==============================================================================
function userInfo(verbose, message, varargin)
% Display information (i.e. to stdout)
if verbose
userPrint(1, message, varargin{:});
end
end
function userWarn(verbose, message, varargin)
% Display warnings (i.e. to stderr)
if verbose
userPrint(2, message, varargin{:});
end
end
function userPrint(fid, message, varargin)
% Print messages (info/warnings) to a stream/file.
mess = sprintf(message, varargin{:});
% Replace '\n' by '\n *** ' and print.
mess = strrep( mess, sprintf('\n'), sprintf('\n *** ') );
fprintf(fid, ' *** %s\n', mess );
end
% =========================================================================
function list = rdirfiles(rootdir)
% Recursive files listing
s = dir(rootdir);
list = {s.name}';
% Exclude .git, .svn, . and ..
[list, idx] = setdiff(list, {'.git','.svn','.','..'});
% Add root
list = fullfile(rootdir, list);
% Loop for sub-directories
pdir = find([s(idx).isdir]);
for ii = pdir
list = [list; rdirfiles(list{ii})]; %#ok<AGROW>
end
% Drop directories
list(pdir) = [];
end
% =========================================================================
function list = getFolders(list)
% Extract the folder structure from a list of files and folders
for ii = 1:numel(list)
if exist(list{ii},'file') == 2
list{ii} = fileparts(list{ii});
end
end
list = unique(list);
end
% =========================================================================

View File

@@ -0,0 +1,35 @@
function newstr = m2tstrjoin(cellstr, delimiter, floatFormat)
% This function joins a cell of strings to a single string (with a
% given delimiter in between two strings, if desired).
if ~exist('delimiter','var') || isempty(delimiter)
delimiter = '';
end
if ~exist('floatFormat','var') || isempty(floatFormat)
floatFormat = '%g';
end
if isempty(cellstr)
newstr = '';
return
end
% convert all values to strings first
nElem = numel(cellstr);
for k = 1:nElem
if isnumeric(cellstr{k})
cellstr{k} = sprintf(floatFormat, cellstr{k});
elseif iscell(cellstr{k})
cellstr{k} = m2tstrjoin(cellstr{k}, delimiter, floatFormat);
% this will fail for heavily nested cells
elseif ~ischar(cellstr{k})
error('matlab2tikz:join:NotCellstrOrNumeric',...
'Expected cellstr or numeric.');
end
end
% inspired by strjoin of recent versions of MATLAB
newstr = cell(2,nElem);
newstr(1,:) = reshape(cellstr, 1, nElem);
newstr(2,1:nElem-1) = {delimiter}; % put delimiters in-between the elements
newstr(2,end) = {''}; % for Octave 4 compatibility
newstr = [newstr{:}];
end

View File

@@ -0,0 +1,18 @@
function arr = versionArray(str)
% Converts a version string to an array.
if ischar(str)
% Translate version string from '2.62.8.1' to [2; 62; 8; 1].
switch getEnvironment
case 'MATLAB'
split = regexp(str, '\.', 'split'); % compatibility MATLAB < R2013a
case 'Octave'
split = strsplit(str, '.');
otherwise
errorUnknownEnvironment();
end
arr = str2num(char(split)); %#ok
else
arr = str;
end
arr = arr(:)';
end

View File

@@ -0,0 +1,9 @@
function str = versionString(arr)
% Converts a version array to string
if ischar(arr)
str = arr;
elseif isnumeric(arr)
str = sprintf('%d.', arr);
str = str(1:end-1); % remove final period
end
end

View File

@@ -0,0 +1,101 @@
This test module is part of matlab2tikz.
Its use is mainly of interest to the matlab2tikz developers to assert that the produced output is good.
Ideally, the tests should be run on every supported environment, i.e.:
* MATLAB R2014a/8.3 (or an older version)
* MATLAB R2014b/8.4 (or a newer version)
* Octave 3.8
Preparing your environment
==========================
Before you can run the tests, you need to make sure that you have all relevant
functions available on your path. From within the `/test` directory run the
following code in your MATLAB/Octave console:
```matlab
addpath(pwd); % for the test harness
addpath(fullfile(pwd,'..','src')); % for matlab2tikz
addpath(fullfile(pwd,'suites')); % for the test suites
```
Running the tests
=================
We have two kinds of tests runners available that each serve a slightly different
purpose.
* "Graphical" tests produce an output report that graphically shows test
figures as generated by MATLAB/Octave and our TikZ output.
* "Headless" tests do not produce graphical output, but instead check the MD5
hash of the generated TikZ files to make sure that the same output
as before is generated.
It is recommended to run the headless tests first and check the problems in
the graphical tests afterwards.
Headless tests
--------------
These tests check that the TikZ output file produced by `matlab2tikz` matches
a reference output. The actual checking is done by hashing the file and the
corresponding hashes are stored in `.md5` files next to the test suites.
For each environment, different reference hashes can be stored.
The headless tests can be invoked using
```matlab
testHeadless;
```
, or, equivalently,
```matlab
makeTravisReport(testHeadless)
```
There are some caveats for this method of testing:
* The MD5 hash is extremely brittle to small details in the output: e.g.
extra whitespace or some other characters will change the hash.
* This automated test does NOT test whether the output is desirable or not.
It only checks whether the previous output is not altered!
* Hence, when structural changes are made, the reference hash should be changed.
This SHOULD be motivated in the pull request (e.g. with a picture)!
Graphical tests
---------------
These tests allow easy comparison of a native PDF `print` output and the
output produced by `matlab2tikz`. For the large amount of cases, however,
this comparison has become somewhat unwieldy.
You can execute the tests using
```matlab
testGraphical;
```
or, equivalently,
```matlab
makeLatexReport(testGraphical)
```
This generates a LaTeX report in `test/output/current/acid.tex` which can then be compiled.
Compilation of this file can be done using the Makefile `test/output/current/Makefile` if you are on a Unix-like system (OS X, Linux) or have [Cygwin](https://www.cygwin.com) installed on Windows.
If all goes well, the result will be the file `test/output/current/acid.pdf` that contains
a list of the test figures, exported as PDF and right next to it the matlab2tikz generated plot.
Advanced Use
------------
Both `testHeadless` and `testGraphical` can take multiple arguments, those are documented in the raw test runner `testMatlab2tikz` that is used behind the scenes. Note that this file sits in a private directory, so `help testMatlab2tikz` will not work!
Also, both can be called with a single output argument, for programmatical
access to the test results as
```matlab
status = testHeadless()
```
These test results in `status` can be passed to `saveHashTable` for updating the hash tables.
Obviously, this should be done with the due diligence!
Automated Tests
===============
The automated tests run on [Travis-CI](https://travis-ci.org) for Octave and on a [personal Jenkins server](https://github.com/matlab2tikz/matlab2tikz/wiki/Jenkins) for MATLAB.
These are effectively the "headless" tests that get called by the `runMatlab2TikzTests` function.
Without verification of those automated tests, a pull request is unlikely to get merged.

View File

@@ -0,0 +1,280 @@
function [ report ] = codeReport( varargin )
%CODEREPORT Builds a report of the code health
%
% This function generates a Markdown report on the code health. At the moment
% this is limited to the McCabe (cyclomatic) complexity of a function and its
% subfunctions.
%
% This makes use of |checkcode| in MATLAB.
%
% Usage:
%
% CODEREPORT('function', functionName) to determine which function is
% analyzed. (default: matlab2tikz)
%
% CODEREPORT('complexityThreshold', integer ) to set above which complexity, a
% function is added to the report (default: 10)
%
% CODEREPORT('stream', stream) to set to which stream/file to output the report
% (default: 1, i.e. stdout). The stream is used only when no output argument
% for `codeReport` is specified!.
%
% See also: checkcode, mlint
SM = StreamMaker();
%% input options
ipp = m2tInputParser();
ipp = ipp.addParamValue(ipp, 'function', 'matlab2tikz', @ischar);
ipp = ipp.addParamValue(ipp, 'complexityThreshold', 10, @isnumeric);
ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream);
ipp = ipp.parse(ipp, varargin{:});
stream = SM.make(ipp.Results.stream, 'w');
%% generate report data
data = checkcode(ipp.Results.function,'-cyc','-struct');
[complexityAll, mlintMessages] = splitCycloComplexity(data);
%% analyze cyclomatic complexity
categorizeComplexity = @(x) categoryOfComplexity(x, ...
ipp.Results.complexityThreshold, ...
ipp.Results.function);
complexityAll = arrayfun(@parseCycloComplexity, complexityAll);
complexityAll = arrayfun(categorizeComplexity, complexityAll);
complexity = filter(complexityAll, @(x) strcmpi(x.category, 'Bad'));
complexity = sortBy(complexity, 'line', 'ascend');
complexity = sortBy(complexity, 'complexity', 'descend');
[complexityStats] = complexityStatistics(complexityAll);
%% analyze other messages
%TODO: handle all mlint messages and/or other metrics of the code
%% format report
dataStr = complexity;
dataStr = arrayfun(@(d) mapField(d, 'function', @markdownInlineCode), dataStr);
if ~isempty(dataStr)
dataStr = addFooterRow(dataStr, 'complexity', @sum, {'line',0, 'function',bold('Total')});
end
dataStr = arrayfun(@(d) mapField(d, 'line', @integerToString), dataStr);
dataStr = arrayfun(@(d) mapField(d, 'complexity', @integerToString), dataStr);
report = makeTable(dataStr, {'function', 'complexity'}, ...
{'Function', 'Complexity'});
%% command line usage
if nargout == 0
if ismember(stream.name, {'stdout','stderr'})
stream.print('%s\n', codelinks(report, ipp.Results.function));
else
stream.print('%s\n', report);
end
figure('name',sprintf('Complexity statistics of %s', ipp.Results.function));
h = statisticsPlot(complexityStats, 'Complexity', 'Number of functions');
for hh = h
plot(hh, [1 1]*ipp.Results.complexityThreshold, ylim(hh), ...
'k--','DisplayName','Threshold');
end
legend(h(1),'show','Location','NorthEast');
clear report
end
end
%% CATEGORIZATION ==============================================================
function [complexity, others] = splitCycloComplexity(list)
% splits codereport into McCabe complexity and others
filter = @(l) ~isempty(strfind(l.message, 'McCabe complexity'));
idxComplexity = arrayfun(filter, list);
complexity = list( idxComplexity);
others = list(~idxComplexity);
end
function [data] = categoryOfComplexity(data, threshold, mainFunc)
% categorizes the complexity as "Good", "Bad" or "Accepted"
TOKEN = '#COMPLEX'; % token to signal allowed complexity
try %#ok
helpStr = help(sprintf('%s>%s', mainFunc, data.function));
if ~isempty(strfind(helpStr, TOKEN))
data.category = 'Accepted';
return;
end
end
if data.complexity > threshold
data.category = 'Bad';
else
data.category = 'Good';
end
end
%% PARSING =====================================================================
function [out] = parseCycloComplexity(in)
% converts McCabe complexity report strings into a better format
out = regexp(in.message, ...
'The McCabe complexity of ''(?<function>[A-Za-z0-9_]+)'' is (?<complexity>[0-9]+).', ...
'names');
out.complexity = str2double(out.complexity);
out.line = in.line;
end
%% DATA PROCESSING =============================================================
function selected = filter(list, filterFunc)
% filters an array according to a binary function
idx = logical(arrayfun(filterFunc, list));
selected = list(idx);
end
function [data] = mapField(data, field, mapping)
data.(field) = mapping(data.(field));
end
function sorted = sortBy(list, fieldName, mode)
% sorts a struct array by a single field
% extra arguments are as for |sort|
values = arrayfun(@(m)m.(fieldName), list);
[dummy, idxSorted] = sort(values(:), 1, mode); %#ok
sorted = list(idxSorted);
end
function [stat] = complexityStatistics(list)
% calculate some basic statistics of the complexities
stat.values = arrayfun(@(c)(c.complexity), list);
stat.binCenter = sort(unique(stat.values));
categoryPerElem = {list.category};
stat.categories = unique(categoryPerElem);
nCategories = numel(stat.categories);
groupedHist = zeros(numel(stat.binCenter), nCategories);
for iCat = 1:nCategories
category = stat.categories{iCat};
idxCat = ismember(categoryPerElem, category);
groupedHist(:,iCat) = hist(stat.values(idxCat), stat.binCenter);
end
stat.histogram = groupedHist;
stat.median = median(stat.values);
end
function [data] = addFooterRow(data, column, func, otherFields)
% adds a footer row to data table based on calculations of a single column
footer = data(end);
for iField = 1:2:numel(otherFields)
field = otherFields{iField};
value = otherFields{iField+1};
footer.(field) = value;
end
footer.(column) = func([data(:).(column)]);
data(end+1) = footer;
end
%% FORMATTING ==================================================================
function str = integerToString(value)
% convert integer to string
str = sprintf('%d',value);
end
function str = markdownInlineCode(str)
% format as inline code for markdown
str = sprintf('`%s`', str);
end
function str = makeTable(data, fields, header)
% make a markdown table from struct array
nData = numel(data);
str = '';
if nData == 0
return; % empty input
end
%TODO: use gfmTable from makeTravisReport instead to do the formatting
% determine column sizes
nFields = numel(fields);
table = cell(nFields, nData);
columnWidth = zeros(1,nFields);
for iField = 1:nFields
field = fields{iField};
table(iField, :) = {data(:).(field)};
columnWidth(iField) = max(cellfun(@numel, table(iField, :)));
end
columnWidth = max(columnWidth, cellfun(@numel, header));
columnWidth = columnWidth + 2; % empty space left and right
columnWidth([1,end]) = columnWidth([1,end]) - 1; % except at the edges
% format table inside cell array
table = [header; table'];
for iField = 1:nFields
FORMAT = ['%' int2str(columnWidth(iField)) 's'];
for jData = 1:size(table, 1)
table{jData, iField} = strjust(sprintf(FORMAT, ...
table{jData, iField}), 'center');
end
end
% insert separator
table = [table(1,:)
arrayfun(@(n) repmat('-',1,n), columnWidth, 'UniformOutput',false)
table(2:end,:)]';
% convert cell array to string
FORMAT = ['%s' repmat('|%s', 1,nFields-1) '\n'];
str = sprintf(FORMAT, table{:});
end
function str = codelinks(str, functionName)
% replaces inline functions with clickable links in MATLAB
str = regexprep(str, '`([A-Za-z0-9_]+)`', ...
['`<a href="matlab:edit ' functionName '>$1">$1</a>`']);
%NOTE: editing function>subfunction will focus on that particular subfunction
% in the editor (this also works for the main function)
end
function str = bold(str)
str = ['**' str '**'];
end
%% PLOTTING ====================================================================
function h = statisticsPlot(stat, xLabel, yLabel)
% plot a histogram and box plot
nCategories = numel(stat.categories);
colors = colorscheme;
h(1) = subplot(5,1,1:4);
hold all;
hb = bar(stat.binCenter, stat.histogram, 'stacked');
for iCat = 1:nCategories
category = stat.categories{iCat};
set(hb(iCat), 'DisplayName', category, 'FaceColor', colors.(category), ...
'LineStyle','none');
end
%xlabel(xLabel);
ylabel(yLabel);
h(2) = subplot(5,1,5);
hold all;
boxplot(stat.values,'orientation','horizontal',...
'boxstyle', 'outline', ...
'symbol', 'o', ...
'colors', colors.All);
xlabel(xLabel);
xlims = [min(stat.binCenter)-1 max(stat.binCenter)+1];
c = 1;
ylims = (ylim(h(2)) - c)/3 + c;
set(h,'XTickMode','manual','XTick',stat.binCenter,'XLim',xlims);
set(h(1),'XTickLabel','');
set(h(2),'YTickLabel','','YLim',ylims);
linkaxes(h, 'x');
end
function colors = colorscheme()
% recognizable color scheme for the categories
colors.All = [ 0 113 188]/255;
colors.Good = [118 171 47]/255;
colors.Bad = [161 19 46]/255;
colors.Accepted = [236 176 31]/255;
end

View File

@@ -0,0 +1,256 @@
function compareTimings(statusBefore, statusAfter)
% COMPARETIMINGS compare timing of matlab2tikz test suite runs
%
% This function plots some analysis plots of the timings of different test
% cases. When the test suite is run repeatedly, the median statistics are
% reported as well as the individual runs.
%
% Usage:
% COMPARETIMINGS(statusBefore, statusAfter)
%
% Parameters:
% - statusBefore and statusAfter are expected to be
% N x R cell arrays, each cell contains a status of a test case
% where there are N test cases, repeated R times each.
%
% You can build such cells, e.g. with the following snippet.
%
% suite = @ACID
% N = numel(suite(0)); % number of test cases
% R = 10; % number of repetitions of each test case
%
% statusBefore = cell(N, R);
% for r = 1:R
% statusBefore(:, r) = testHeadless;
% end
%
% % now check out the after commit
%
% statusAfter = cell(N, R);
% for r = 1:R
% statusAfter(:, r) = testHeadless;
% end
%
% compareTimings(statusBefore, statusAfter)
%
% See also: testHeadless
%% Extract timing information
time_cf = extract(statusBefore, statusAfter, @(s) s.tikzStage.cleanfigure_time);
time_m2t = extract(statusBefore, statusAfter, @(s) s.tikzStage.m2t_time);
%% Construct plots
hax(1) = subplot(3,2,1);
histograms(time_cf, 'cleanfigure');
legend('show')
hax(2) = subplot(3,2,3);
histograms(time_m2t, 'matlab2tikz');
legend('show')
linkaxes(hax([1 2]),'x');
hax(3) = subplot(3,2,5);
histogramSpeedup('cleanfigure', time_cf, 'matlab2tikz', time_m2t);
legend('show');
hax(4) = subplot(3,2,2);
plotByTestCase(time_cf, 'cleanfigure');
legend('show')
hax(5) = subplot(3,2,4);
plotByTestCase(time_m2t, 'matlab2tikz');
legend('show')
hax(6) = subplot(3,2,6);
plotSpeedup('cleanfigure', time_cf, 'matlab2tikz', time_m2t);
legend('show');
linkaxes(hax([4 5 6]), 'x');
% ------------------------------------------------------------------------------
end
%% Data processing
function timing = extract(statusBefore, statusAfter, func)
otherwiseNaN = {'ErrorHandler', @(varargin) NaN};
timing.before = cellfun(func, statusBefore, otherwiseNaN{:});
timing.after = cellfun(func, statusAfter, otherwiseNaN{:});
end
function [names,timings] = splitNameTiming(vararginAsCell)
names = vararginAsCell(1:2:end-1);
timings = vararginAsCell(2:2:end);
end
%% Plot subfunctions
function [h] = histograms(timing, name)
% plot histogram of time measurements
colors = colorscheme;
histostyle = {'DisplayStyle', 'bar',...
'Normalization','pdf',...
'EdgeColor','none',...
'BinWidth',0.025};
hold on;
h{1} = myHistogram(timing.before, histostyle{:}, ...
'FaceColor', colors.before, ...
'DisplayName', 'Before');
h{2} = myHistogram(timing.after , histostyle{:}, ...
'FaceColor', colors.after,...
'DisplayName', 'After');
xlabel(sprintf('%s runtime [s]',name))
ylabel('Empirical PDF');
end
function [h] = histogramSpeedup(varargin)
% plot histogram of observed speedup
histostyle = {'DisplayStyle', 'bar',...
'Normalization','pdf',...
'EdgeColor','none'};
[names,timings] = splitNameTiming(varargin);
nData = numel(timings);
h = cell(nData, 1);
minTime = NaN; maxTime = NaN;
for iData = 1:nData
name = names{iData};
timing = timings{iData};
hold on;
speedup = computeSpeedup(timing);
color = colorOptionsOfName(name, 'FaceColor');
h{iData} = myHistogram(speedup, histostyle{:}, color{:},...
'DisplayName', name);
[minTime, maxTime] = minAndMax(speedup, minTime, maxTime);
end
xlabel('Speedup')
ylabel('Empirical PDF');
set(gca,'XScale','log', 'XLim', [minTime, maxTime].*[0.9 1.1]);
end
function [h] = plotByTestCase(timing, name)
% plot all time measurements per test case
colors = colorscheme;
hold on;
if size(timing.before, 2) > 1
h{3} = plot(timing.before, '.',...
'Color', colors.before, 'HandleVisibility', 'off');
h{4} = plot(timing.after, '.',...
'Color', colors.after, 'HandleVisibility', 'off');
end
h{1} = plot(median(timing.before, 2), '-',...
'LineWidth', 2, ...
'Color', colors.before, ...
'DisplayName', 'Before');
h{2} = plot(median(timing.after, 2), '-',...
'LineWidth', 2, ...
'Color', colors.after,...
'DisplayName', 'After');
ylabel(sprintf('%s runtime [s]', name));
set(gca,'YScale','log')
end
function [h] = plotSpeedup(varargin)
% plot speed up per test case
[names, timings] = splitNameTiming(varargin);
nDatasets = numel(names);
minTime = NaN;
maxTime = NaN;
h = cell(nDatasets, 1);
for iData = 1:nDatasets
name = names{iData};
timing = timings{iData};
color = colorOptionsOfName(name);
hold on
[speedup, medSpeedup] = computeSpeedup(timing);
if size(speedup, 2) > 1
plot(speedup, '.', color{:}, 'HandleVisibility','off');
end
h{iData} = plot(medSpeedup, color{:}, 'DisplayName', name, ...
'LineWidth', 2);
[minTime, maxTime] = minAndMax(speedup, minTime, maxTime);
end
nTests = size(speedup, 1);
plot([-nTests nTests*2], ones(2,1), 'k','HandleVisibility','off');
legend('show', 'Location','NorthWest')
set(gca,'YScale','log','YLim', [minTime, maxTime].*[0.9 1.1], ...
'XLim', [0 nTests+1])
xlabel('Test case');
ylabel('Speed-up (t_{before}/t_{after})');
end
%% Histogram wrapper
function [h] = myHistogram(data, varargin)
% this is a very crude wrapper that mimics Histogram in R2014a and older
if ~isempty(which('histogram'))
h = histogram(data, varargin{:});
else % no "histogram" available
options = struct(varargin{:});
minData = min(data(:));
maxData = max(data(:));
if isfield(options, 'BinWidth')
numBins = ceil((maxData-minData)/options.BinWidth);
elseif isfield(options, 'NumBins')
numBins = options.NumBins;
else
numBins = 10;
end
[counts, bins] = hist(data(:), numBins);
if isfield(options,'Normalization') && strcmp(options.Normalization,'pdf')
binWidth = mean(diff(bins));
counts = counts./sum(counts)/binWidth;
end
h = bar(bins, counts, 1);
% transfer properties as well
names = fieldnames(options);
for iName = 1:numel(names)
option = names{iName};
if isprop(h, option)
set(h, option, options.(option));
end
end
set(allchild(h),'FaceAlpha', 0.75); % only supported with OpenGL renderer
% but this should look a bit similar with matlab2tikz then...
end
end
%% Calculations
function [speedup, medSpeedup] = computeSpeedup(timing)
% computes the timing speedup (and median speedup)
dRep = 2; % dimension containing the repeated tests
speedup = timing.before ./ timing.after;
medSpeedup = median(timing.before, dRep) ./ median(timing.after, dRep);
end
function [minTime, maxTime] = minAndMax(speedup, minTime, maxTime)
% calculates the minimum/maximum time in an array and peviously
% computed min/max times
minTime = min([minTime; speedup(:)]);
maxTime = min([maxTime; speedup(:)]);
end
%% Color scheme
function colors = colorscheme()
% defines the color scheme
colors.matlab2tikz = [161 19 46]/255;
colors.cleanfigure = [ 0 113 188]/255;
colors.before = [236 176 31]/255;
colors.after = [118 171 47]/255;
end
function color = colorOptionsOfName(name, keyword)
% returns a cell array with a keyword (default: 'Color') and a named color
% if it exists in the colorscheme
if ~exist('keyword','var') || isempty(keyword)
keyword = 'Color';
end
colors = colorscheme;
if isfield(colors,name)
color = {keyword, colors.(name)};
else
color = {};
end
end

View File

@@ -0,0 +1,53 @@
function example_bar_plot()
test_data =[18 0; 20 0; 21 2; 30 14; 35 34; 40 57; 45 65; 50 46; 55 9; 60 2; 65 1; 70 0];
% Create figure
figure1 = figure('Color',[1 1 1]);
subplot(1,2,1)
hb=barh(test_data(:,1),test_data(:,2),'DisplayName','Test Data');
ylabel('parameter [units]');
xlabel('#');
legend('show','Location','northwest');
subplot(1,2,2)
hb=bar(test_data(:,1),test_data(:,2),'DisplayName','Test Data');
xlabel('parameter [units]');
ylabel('#');
legend('show','Location','northwest');
xdata=test_data(:,1);
barWidth=test_getBarWidthInAbsolutUnits(hb);
x_l=xdata-barWidth/2;
x_u=xdata+barWidth/2;
max_y=max(test_data(:,2))*1.2;
x=[];
y=[];
for i=1:length(x_l)
x = [x , x_l(i),x_l(i),nan,x_u(i),x_u(i),nan];
y = [y, 0,max_y ,nan,0 ,max_y ,nan];
end
hold on
plot(x,y,'r');
matlab2tikz('figurehandle',figure1,'filename','example_v_bar_plot.tex' ,'standalone', true);
function BarWidth=test_getBarWidthInAbsolutUnits(h)
% astimates the width of a bar plot
XData_bar=get(h,'XData');
length_bar = length(XData_bar);
BarWidth= get(h, 'BarWidth');
if length_bar > 1
BarWidth = min(diff(XData_bar))*BarWidth;
end

View File

@@ -0,0 +1,80 @@
%% Quiver calculations
% These are calculations for the quiver dimensions as implemented in MATLAB
% (HG1) as in the |quiver.m| function.
%
% For HG2 and Octave, the situation might be different.
%
% A single quiver is defined as:
%
% C
% \
% \
% A ----------------- B
% /
% /
% D
%
% To know the dimensions of the arrow head, MATLAB defines the quantities
% alpha = beta = 0.33 that determine the coordinates of C and D as given below.
clc;
clear variables;
close all;
%% Parameters
try
syms x y z u v w alpha beta epsilon real
catch
warning('Symbolic toolbox not found. Interpret the values with care!');
x = randn(); y = randn(); z = randn();
u = randn(); v = randn(); w = randn();
end
alpha = 0.33;
beta = alpha;
epsilon = 0;
is2D = true;
%% Coordinates as defined in MATLAB
% Note that in 3D, the arrow head is oriented in a weird way. Let' just ignore
% that and only focus on 2D and use the same in 3D. Due to the lack
% of [u,v,w]-symmetry in those equations, the angle is bound to depend on the
% length of |delta|, i.e. something we don't know beforehand.
A = [x y z].';
delta = [u v w].';
B = A + delta;
C = B - alpha*[u+beta*(v+epsilon);
v-beta*(u+epsilon)
w];
D = B - alpha*[u-beta*(v+epsilon);
v+beta*(u+epsilon)
w];
if is2D
A = A(1:2);
B = B(1:2);
C = C(1:2);
D = D(1:2);
delta = delta(1:2);
end
%% Calculating the angle of the arrowhead
% Calculate the cos(angle) using the inner product
unitVector = @(v) v/norm(v);
cosAngleBetween = @(a,b,c) unitVector(a-b).' * unitVector(c-b);
cosTwiceTheta = cosAngleBetween(C,B,D);
if isa(cosTwiceTheta, 'sym')
cosTwiceTheta = simplify(cosTwiceTheta);
end
theta = acos(cosTwiceTheta) / 2
radToDeg = @(rads) (rads * 180 / pi);
thetaVal = radToDeg(theta)
try
thetaVal = double(thetaVal)
end
% For the MATLAB parameters alpha=beta=0.33, we get theta = 18.263 degrees.

View File

@@ -0,0 +1,222 @@
function makeLatexReport(status, output)
% generate a LaTeX report
%
%
if ~exist('output','var')
output = m2troot('test','output','current');
end
% first, initialize the tex output
SM = StreamMaker();
stream = SM.make(fullfile(output, 'acid.tex'), 'w');
texfile_init(stream);
printFigures(stream, status);
printSummaryTable(stream, status);
printErrorMessages(stream, status);
printEnvironmentInfo(stream, status);
texfile_finish(stream);
end
% =========================================================================
function texfile_init(stream)
stream.print(['\\documentclass[landscape]{scrartcl}\n' , ...
'\\pdfminorversion=6\n\n' , ...
'\\usepackage{amsmath} %% required for $\\text{xyz}$\n\n', ...
'\\usepackage{hyperref}\n' , ...
'\\usepackage{graphicx}\n' , ...
'\\usepackage{epstopdf}\n' , ...
'\\usepackage{tikz}\n' , ...
'\\usetikzlibrary{plotmarks}\n\n' , ...
'\\usepackage{pgfplots}\n' , ...
'\\pgfplotsset{compat=newest}\n\n' , ...
'\\usepackage[margin=0.5in]{geometry}\n' , ...
'\\newlength\\figurewidth\n' , ...
'\\setlength\\figurewidth{0.4\\textwidth}\n\n' , ...
'\\begin{document}\n\n']);
end
% =========================================================================
function texfile_finish(stream)
stream.print('\\end{document}');
end
% =========================================================================
function printFigures(stream, status)
for k = 1:length(status)
texfile_addtest(stream, status{k});
end
end
% =========================================================================
function printSummaryTable(stream, status)
texfile_tab_completion_init(stream)
for k = 1:length(status)
stat = status{k};
testNumber = stat.index;
% Break table up into pieces if it gets too long for one page
%TODO: use booktabs instead
%TODO: maybe just write a function to construct the table at once
% from a cell array (see makeTravisReport for GFM counterpart)
if ~mod(k,35)
texfile_tab_completion_finish(stream);
texfile_tab_completion_init(stream);
end
stream.print('%d & \\texttt{%s}', testNumber, name2tex(stat.function));
if stat.skip
stream.print(' & --- & skipped & ---');
else
for err = [stat.plotStage.error, ...
stat.saveStage.error, ...
stat.tikzStage.error]
if err
stream.print(' & \\textcolor{red}{failed}');
else
stream.print(' & \\textcolor{green!50!black}{passed}');
end
end
end
stream.print(' \\\\\n');
end
texfile_tab_completion_finish(stream);
end
% =========================================================================
function printErrorMessages(stream, status)
if errorHasOccurred(status)
stream.print('\\section*{Error messages}\n\\scriptsize\n');
for k = 1:length(status)
stat = status{k};
testNumber = stat.index;
if isempty(stat.plotStage.message) && ...
isempty(stat.saveStage.message) && ...
isempty(stat.tikzStage.message)
continue % No error messages for this test case
end
stream.print('\n\\subsection*{Test case %d: \\texttt{%s}}\n', testNumber, name2tex(stat.function));
print_verbatim_information(stream, 'Plot generation', stat.plotStage.message);
print_verbatim_information(stream, 'PDF generation' , stat.saveStage.message);
print_verbatim_information(stream, 'matlab2tikz' , stat.tikzStage.message);
end
stream.print('\n\\normalsize\n\n');
end
end
% =========================================================================
function printEnvironmentInfo(stream, status)
[env,versionString] = getEnvironment();
testsuites = unique(cellfun(@(s) func2str(s.testsuite) , status, ...
'UniformOutput', false));
testsuites = name2tex(m2tstrjoin(testsuites, ', '));
stream.print(['\\newpage\n',...
'\\begin{tabular}{ll}\n',...
' Suite & ' testsuites ' \\\\ \n', ...
' Created & ' datestr(now) ' \\\\ \n', ...
' OS & ' OSVersion ' \\\\ \n',...
' ' env ' & ' versionString ' \\\\ \n', ...
VersionControlIdentifier, ...
' TikZ & \\expandafter\\csname ver@tikz.sty\\endcsname \\\\ \n',...
' Pgfplots & \\expandafter\\csname ver@pgfplots.sty\\endcsname \\\\ \n',...
'\\end{tabular}\n']);
end
% =========================================================================
function print_verbatim_information(stream, title, contents)
if ~isempty(contents)
stream.print(['\\subsubsection*{%s}\n', ...
'\\begin{verbatim}\n%s\\end{verbatim}\n'], ...
title, contents);
end
end
% =========================================================================
function texfile_addtest(stream, status)
% Actually add the piece of LaTeX code that'll later be used to display
% the given test.
if ~status.skip
ref_error = status.plotStage.error;
gen_error = status.tikzStage.error;
ref_file = status.saveStage.texReference;
gen_file = status.tikzStage.pdfFile;
stream.print(...
['\\begin{figure}\n' , ...
' \\centering\n' , ...
' \\begin{tabular}{cc}\n' , ...
' %s & %s \\\\\n' , ...
' reference rendering & generated\n' , ...
' \\end{tabular}\n' , ...
' \\caption{%s \\texttt{%s}, \\texttt{%s(%d)}.%s}\n', ...
'\\end{figure}\n' , ...
'\\clearpage\n\n'],...
include_figure(ref_error, 'includegraphics', ref_file), ...
include_figure(gen_error, 'includegraphics', gen_file), ...
status.description, ...
name2tex(status.function), name2tex(status.testsuite), status.index, ...
formatIssuesForTeX(status.issues));
end
end
% =========================================================================
function str = include_figure(errorOccured, command, filename)
if errorOccured
str = sprintf(['\\tikz{\\draw[red,thick] ', ...
'(0,0) -- (\\figurewidth,\\figurewidth) ', ...
'(0,\\figurewidth) -- (\\figurewidth,0);}']);
else
switch command
case 'includegraphics'
strFormat = '\\includegraphics[width=\\figurewidth]{%s}';
case 'input'
strFormat = '\\input{%s}';
otherwise
error('Matlab2tikz_acidtest:UnknownFigureCommand', ...
'Unknown figure command "%s"', command);
end
str = sprintf(strFormat, filename);
end
end
% =========================================================================
function texfile_tab_completion_init(stream)
stream.print(['\\clearpage\n\n' , ...
'\\begin{table}\n' , ...
'\\centering\n' , ...
'\\caption{Test case completion summary}\n' , ...
'\\begin{tabular}{rlccc}\n' , ...
'No. & Test case & Plot & PDF & TikZ \\\\\n' , ...
'\\hline\n']);
end
% =========================================================================
function texfile_tab_completion_finish(stream)
stream.print( ['\\end{tabular}\n' , ...
'\\end{table}\n\n' ]);
end
% =========================================================================
function texName = name2tex(matlabIdentifier)
% convert a MATLAB identifier/function handle to a TeX string
if isa(matlabIdentifier, 'function_handle')
matlabIdentifier = func2str(matlabIdentifier);
end
texName = strrep(matlabIdentifier, '_', '\_');
end
% =========================================================================
function str = formatIssuesForTeX(issues)
% make links to GitHub issues for the LaTeX output
issues = issues(:)';
if isempty(issues)
str = '';
return
end
BASEURL = 'https://github.com/matlab2tikz/matlab2tikz/issues/';
SEPARATOR = sprintf(' \n');
strs = arrayfun(@(n) sprintf(['\\href{' BASEURL '%d}{\\#%d}'], n,n), issues, ...
'UniformOutput', false);
strs = [strs; repmat({SEPARATOR}, 1, numel(strs))];
str = sprintf('{\\color{blue} \\texttt{%s}}', [strs{:}]);
end
% ==============================================================================

View File

@@ -0,0 +1,74 @@
function makeTapReport(status, varargin)
% Makes a Test Anything Protocol report
%
% This function produces a testing report of HEADLESS tests for
% display on Jenkins (or any other TAP-compatible system)
%
% MAKETAPREPORT(status) produces the report from the `status` output of
% `testHeadless`.
%
% MAKETAPREPORT(status, 'stream', FID, ...) changes the filestream to use
% to output the report to. (Default: 1 (stdout)).
%
% TAP Specification: https://testanything.org
%
% See also: testHeadless, makeTravisReport, makeLatexReport
%% Parse input arguments
SM = StreamMaker();
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'status', @iscell);
ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream);
ipp = ipp.parse(ipp, status, varargin{:});
arg = ipp.Results;
%% Construct stream
stream = SM.make(arg.stream, 'w');
%% build report
printTAPVersion(stream);
printTAPPlan(stream, status);
for iStatus = 1:numel(status)
printTAPReport(stream, status{iStatus}, iStatus);
end
end
% ==============================================================================
function printTAPVersion(stream)
% prints the TAP version
stream.print('TAP version 13\n');
end
function printTAPPlan(stream, statuses)
% prints the TAP test plan
firstTest = 1;
lastTest = numel(statuses);
stream.print('%d..%d\n', firstTest, lastTest);
end
function printTAPReport(stream, status, testNum)
% prints a TAP test case report
message = status.function;
if hasTestFailed(status)
result = 'not ok';
else
result = 'ok';
end
directives = getTAPDirectives(status);
stream.print('%s %d %s %s\n', result, testNum, message, directives);
%TODO: we can provide more information on the failure using YAML syntax
end
function directives = getTAPDirectives(status)
% add TAP directive (a todo or skip) to the test directives
directives = {};
if status.skip
directives{end+1} = '# SKIP skipped';
end
if status.unreliable
directives{end+1} = '# TODO unreliable';
end
directives = strtrim(m2tstrjoin(directives, ' '));
end
% ==============================================================================

View File

@@ -0,0 +1,360 @@
function [nErrors] = makeTravisReport(status, varargin)
% Makes a readable report for Travis/Github of test results
%
% This function produces a testing report of HEADLESS tests for
% display on GitHub and Travis.
%
% MAKETRAVISREPORT(status) produces the report from the `status` output of
% `testHeadless`.
%
% MAKETRAVISREPORT(status, 'stream', FID, ...) changes the filestream to use
% to output the report to. (Default: 1 (stdout)).
%
% MAKETRAVISREPORT(status, 'length', CHAR, ...) changes the report length.
% A few values are possible that cover different aspects in less/more detail.
% - 'default': all unreliable tests, failed & skipped tests and summary
% - 'short' : only show the brief summary
% - 'long' : all tests + summary
%
% See also: testHeadless, makeLatexReport
SM = StreamMaker();
%% Parse input arguments
ipp = m2tInputParser();
ipp = ipp.addRequired(ipp, 'status', @iscell);
ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream);
ipp = ipp.addParamValue(ipp, 'length', 'default', @isReportLength);
ipp = ipp.parse(ipp, status, varargin{:});
arg = ipp.Results;
arg.length = lower(arg.length);
stream = SM.make(arg.stream, 'w');
%% transform status data into groups
S = splitStatuses(status);
%% build report
stream.print(gfmHeader(describeEnvironment));
reportUnreliableTests(stream, arg, S);
reportReliableTests(stream, arg, S);
displayTestSummary(stream, S);
%% set output arguments if needed
if nargout >= 1
nErrors = countNumberOfErrors(S.reliable);
end
end
% == INPUT VALIDATOR FUNCTIONS =================================================
function bool = isReportLength(val)
% validates the report length
bool = ismember(lower(val), {'default','short','long'});
end
% == GITHUB-FLAVORED MARKDOWN FUNCTIONS ========================================
function str = gfmTable(data, header, alignment)
% Construct a Github-flavored Markdown table
%
% Arguments:
% - data: nRows x nCols cell array that represents the data
% - header: cell array with the (nCol) column headers
% - alignment: alignment specification per column
% * 'l': left-aligned (default)
% * 'c': centered
% * 'r': right-aligned
% When not enough entries are specified, the specification is repeated
% cyclically.
%
% Output: table as a string
%
% See https://help.github.com/articles/github-flavored-markdown/#tables
% input argument validation and normalization
nCols = size(data, 2);
if ~exist('alignment','var') || isempty(alignment)
alignment = 'l';
end
if numel(alignment) < nCols
% repeat the alignment specifications along the columns
alignment = repmat(alignment, 1, nCols);
alignment = alignment(1:nCols);
end
% calculate the required column width
cellWidth = cellfun(@length, [header(:)' ;data]);
columnWidth = max(max(cellWidth, [], 1),3); % use at least 3 places
% prepare the table format
COLSEP = '|'; ROWSEP = sprintf('\n');
rowformat = [COLSEP sprintf([' %%%ds ' COLSEP], columnWidth) ROWSEP];
alignmentRow = formatAlignment(alignment, columnWidth);
% actually print the table
fullTable = [header; alignmentRow; data];
strs = cell(size(fullTable,1), 1);
for iRow = 1:numel(strs)
thisRow = fullTable(iRow,:);
%TODO: maybe preprocess thisRow with strjust first
strs{iRow} = sprintf(rowformat, thisRow{:});
end
str = [strs{:}];
%---------------------------------------------------------------------------
function alignRow = formatAlignment(alignment, columnWidth)
% Construct a row of dashes to specify the alignment of each column
% See https://help.github.com/articles/github-flavored-markdown/#tables
DASH = '-'; COLON = ':';
N = numel(columnWidth);
alignRow = arrayfun(@(w) repmat(DASH, 1, w), columnWidth, ...
'UniformOutput', false);
for iColumn = 1:N
thisAlign = alignment(iColumn);
thisSpec = alignRow{iColumn};
switch lower(thisAlign)
case 'l'
thisSpec(1) = COLON;
case 'r'
thisSpec(end) = COLON;
case 'c'
thisSpec([1 end]) = COLON;
otherwise
error('gfmTable:BadAlignment','Unknown alignment "%s"',...
thisAlign);
end
alignRow{iColumn} = thisSpec;
end
end
end
function str = gfmCode(str, inline, language)
% Construct a GFM code fragment
%
% Arguments:
% - str: code to be displayed
% - inline: - true -> formats inline
% - false -> formats as code block
% - [] -> automatic mode (default): picks one of the above
% - language: which language the code is (enforces a code block)
%
% Output: GFM formatted string
%
% See https://help.github.com/articles/github-flavored-markdown
if ~exist('inline','var')
inline = [];
end
if ~exist('language','var') || isempty(language)
language = '';
else
inline = false; % highlighting is not supported for inline code
end
if isempty(inline)
inline = isempty(strfind(str, sprintf('\n')));
end
if inline
prefix = '`';
postfix = '`';
else
prefix = sprintf('\n```%s\n', language);
postfix = sprintf('\n```\n');
if str(end) == sprintf('\n')
postfix = postfix(2:end); % remove extra endline
end
end
str = sprintf('%s%s%s', prefix, str, postfix);
end
function str = gfmHeader(str, level)
% Constructs a GFM/Markdown header
if ~exist('level','var')
level = 1;
end
str = sprintf('\n%s %s\n', repmat('#', 1, level), str);
end
function symbols = githubEmoji()
% defines the emojis to signal the test result
symbols = struct('pass', ':white_check_mark:', ...
'fail', ':heavy_exclamation_mark:', ...
'skip', ':grey_question:');
end
% ==============================================================================
function S = splitStatuses(status)
% splits a cell array of statuses into a struct of cell arrays
% of statuses according to their value of "skip", "reliable" and whether
% an error has occured.
% See also: splitUnreliableTests, splitPassFailSkippedTests
S = struct('all', {status}); % beware of cell array assignment to structs!
[S.reliable, S.unreliable] = splitUnreliableTests(status);
[S.passR, S.failR, S.skipR] = splitPassFailSkippedTests(S.reliable);
[S.passU, S.failU, S.skipU] = splitPassFailSkippedTests(S.unreliable);
end
% ==============================================================================
function [short, long] = describeEnvironment()
% describes the environment in a short and long format
[env, ver] = getEnvironment;
[dummy, VCID] = VersionControlIdentifier(); %#ok
if ~isempty(VCID)
VCID = [' commit ' VCID(1:10)];
end
OS = OSVersion;
short = sprintf('%s %s (%s)', env, ver, OS, VCID);
long = sprintf('Test results for m2t%s running with %s %s on %s.', ...
VCID, env, ver, OS);
end
% ==============================================================================
function reportUnreliableTests(stream, arg, S)
% report on the unreliable tests
if ~isempty(S.unreliable) && ~strcmpi(arg.length, 'short')
stream.print(gfmHeader('Unreliable tests',2));
stream.print('These do not cause the build to fail.\n\n');
displayTestResults(stream, S.unreliable);
end
end
function reportReliableTests(stream, arg, S)
% report on the reliable tests
switch arg.length
case 'long'
tests = S.reliable;
message = '';
case 'default'
tests = [S.failR; S.skipR];
message = 'Passing tests are not shown (only failed and skipped tests).\n\n';
case 'short'
return; % don't show this part
end
stream.print(gfmHeader('Reliable tests',2));
stream.print('Only the reliable tests determine the build outcome.\n');
stream.print(message);
displayTestResults(stream, tests);
end
% ==============================================================================
function displayTestResults(stream, status)
% display a table of specific test outcomes
headers = {'Testcase', 'Name', 'OK', 'Status'};
data = cell(numel(status), numel(headers));
symbols = githubEmoji;
for iTest = 1:numel(status)
data(iTest,:) = fillTestResultRow(status{iTest}, symbols);
end
str = gfmTable(data, headers, 'llcl');
stream.print('%s', str);
end
function row = fillTestResultRow(oneStatus, symbol)
% format the status of a single test for the summary table
testNumber = oneStatus.index;
testSuite = func2str(oneStatus.testsuite);
summary = '';
if oneStatus.skip
summary = 'SKIPPED';
passOrFail = symbol.skip;
else
stages = getStagesFromStatus(oneStatus);
for jStage = 1:numel(stages)
thisStage = oneStatus.(stages{jStage});
if ~thisStage.error
continue;
end
stageName = strrep(stages{jStage},'Stage','');
switch stageName
case 'plot'
summary = sprintf('%s plot failed', summary);
case 'tikz'
summary = sprintf('%s m2t failed', summary);
case 'hash'
summary = sprintf('new hash %32s != expected (%32s) %s', ...
thisStage.found, thisStage.expected, summary);
otherwise
summary = sprintf('%s %s FAILED', summary, thisStage);
end
end
if isempty(summary)
passOrFail = symbol.pass;
else
passOrFail = symbol.fail;
end
summary = strtrim(summary);
end
row = { gfmCode(sprintf('%s(%d)', testSuite, testNumber)), ...
gfmCode(oneStatus.function), ...
passOrFail, ...
summary};
end
% ==============================================================================
function displayTestSummary(stream, S)
% display a table of # of failed/passed/skipped tests vs (un)reliable
% compute number of cases per category
reliableSummary = cellfun(@numel, {S.passR, S.failR, S.skipR});
unreliableSummary = cellfun(@numel, {S.passU, S.failU, S.skipU});
% make summary table + calculate totals
summary = [unreliableSummary numel(S.unreliable);
reliableSummary numel(S.reliable);
reliableSummary+unreliableSummary numel(S.all)];
% put results into cell array with proper layout
summary = arrayfun(@(v) sprintf('%d',v), summary, 'UniformOutput', false);
table = repmat({''}, 3, 5);
header = {'','Pass','Fail','Skip','Total'};
table(:,1) = {'Unreliable','Reliable','Total'};
table(:,2:end) = summary;
% print table
[envShort, envDescription] = describeEnvironment(); %#ok
stream.print(gfmHeader('Test summary', 2));
stream.print('%s\n', envDescription);
stream.print('%s\n', gfmCode(generateCode(S),false,'matlab'));
stream.print(gfmTable(table, header, 'lrrrr'));
% print overall outcome
symbol = githubEmoji;
nErrors = numel(S.failR);
if nErrors == 0
stream.print('\nBuild passes. %s\n', symbol.pass);
else
stream.print('\nBuild fails with %d errors. %s\n', nErrors, symbol.fail);
end
end
function code = generateCode(S)
% generates some MATLAB code to easily replicate the results
code = sprintf('%s = %s;\n', ...
'suite', ['@' func2str(S.all{1}.testsuite)], ...
'alltests', testNumbers(S.all), ...
'reliable', testNumbers(S.reliable), ...
'unreliable', testNumbers(S.unreliable), ...
'failReliable', testNumbers(S.failR), ...
'passUnreliable', testNumbers(S.passU), ...
'skipped', testNumbers([S.skipR; S.skipU]));
% --------------------------------------------------------------------------
function str = testNumbers(status)
str = intelligentVector( cellfun(@(s) s.index, status) );
end
end
function str = intelligentVector(numbers)
% Produce a string that is an intelligent vector notation of its arguments
% e.g. when numbers = [ 1 2 3 4 6 7 8 9 ], it should return '[ 1:4 6:9 ]'
% The order in the vector is not retained!
if isempty(numbers)
str = '[]';
else
numbers = sort(numbers(:).');
delta = diff([numbers(1)-1 numbers]);
% place virtual bounds at the first element and beyond the last one
bounds = [1 find(delta~=1) numel(numbers)+1];
idx = 1:(numel(bounds)-1); % start index of each segment
start = numbers(bounds(idx ) );
stop = numbers(bounds(idx+1)-1);
parts = arrayfun(@formatRange, start, stop, 'UniformOutput', false);
str = sprintf('[%s]', strtrim(sprintf('%s ', parts{:})));
end
end
function str = formatRange(start, stop)
% format a range [start:stop] of integers in MATLAB syntax
if start==stop
str = sprintf('%d',start);
else
str = sprintf('%d:%d',start, stop);
end
end
% ==============================================================================

Some files were not shown because too many files have changed in this diff Show More