bug fixes
This commit is contained in:
1 parent
5767a2e26d
commit
2a22d2dbea
4 files changed
+257
-227
No files matched your search
@@ -24,8 +24,8 @@ xlims = xlim(ax);
|
||||
energyMin = xlims(1);
|
||||
energyMax = xlims(2);
|
||||
|
||||
% Create energy range with 0.2 eV step
|
||||
energyStep = 0.2;
|
||||
% Create energy range with 0.01 eV step
|
||||
energyStep = 0.01;
|
||||
energyRange = (energyMin:energyStep:energyMax)';
|
||||
|
||||
% =====================================================================
|
||||
|
||||
+16
-8
@@ -548,7 +548,7 @@ if ~isempty(TmpFileList{1})
|
||||
current_file_info.lastModifiedDatestr = current_file_info.date;
|
||||
current_file_info.image = {[]};
|
||||
current_file_info.imageRoh = {[]};
|
||||
current_file_info.xRayEkin_eV = NaN;
|
||||
current_file_info.xRayEkin_eV = {[]};
|
||||
current_file_info.xRayEkinRange_eV = [NaN NaN];
|
||||
current_file_info.scientaPassEnergy_eV = NaN;
|
||||
current_file_info.scientaDwellTime_ms = NaN;
|
||||
@@ -942,7 +942,8 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
|
||||
end
|
||||
|
||||
% Make unique
|
||||
[TmpFileList{2}(ii).xRayEkin_eV, ind] = unique(TmpXrayEkin);
|
||||
[tmpE, ind] = unique(TmpXrayEkin);
|
||||
TmpFileList{2}(ii).xRayEkin_eV = {tmpE(:)'};
|
||||
TmpFileList{2}(ii).image = TmpImage(:,ind);
|
||||
TmpFileList{2}(ii).image = TmpImage(:,ind);
|
||||
% NEXAFS pass energy
|
||||
@@ -1029,8 +1030,10 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
|
||||
TmpFileList{2}(ii).scientaEkinRange_eV = [double(tmp(1))-TmpFileList{2}(ii).scientaPassEnergy_eV/20 double(tmp(1))+TmpFileList{2}(ii).scientaPassEnergy_eV/20];
|
||||
|
||||
|
||||
TmpFileList{2}(ii).scientaEkin_eV = linspace(TmpFileList{2}(ii).scientaEkinRange_eV(:,1)...
|
||||
,TmpFileList{2}(ii).scientaEkinRange_eV(:,2),size(TmpFileList{2}(ii).image,1))';
|
||||
TmpFileList{2}(ii).scientaEkin_eV = { ...
|
||||
linspace(TmpFileList{2}(ii).scientaEkinRange_eV(1), ...
|
||||
TmpFileList{2}(ii).scientaEkinRange_eV(2), ...
|
||||
size(TmpFileList{2}(ii).image,1))' };
|
||||
else
|
||||
% NEXAFS kinetic energy window - scan mode
|
||||
try
|
||||
@@ -1057,8 +1060,11 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
|
||||
TmpFileList{2}(ii).scientaEkinStep_eV = abs(tmp(1));
|
||||
end
|
||||
end
|
||||
TmpFileList{2}(ii).scientaEkin_eV = [TmpFileList{2}(ii).scientaEkinRange_eV(:,1):TmpFileList{2}(ii).scientaEkinStep_eV(1):TmpFileList{2}(ii).scientaEkinRange_eV(:,2)]';
|
||||
end
|
||||
TmpFileList{2}(ii).scientaEkin_eV = { ...
|
||||
(TmpFileList{2}(ii).scientaEkinRange_eV(1) : ...
|
||||
TmpFileList{2}(ii).scientaEkinStep_eV : ...
|
||||
TmpFileList{2}(ii).scientaEkinRange_eV(2))' };
|
||||
end
|
||||
|
||||
clearvars tmp
|
||||
% read I_0
|
||||
@@ -1202,8 +1208,7 @@ for ii = 1:size(TmpFileList{3},1) % do this for each picture in the list
|
||||
TmpFileList{3}(ii).lastModifiedDatestr = TmpFileList{3}(ii).date;
|
||||
end
|
||||
|
||||
%% Merge lists into one
|
||||
%
|
||||
|
||||
|
||||
%% Merge lists into one
|
||||
ListFiles = table();
|
||||
@@ -1473,6 +1478,9 @@ if istable(p.Results.DataTable) % do this only if previous list was input to fun
|
||||
ListFiles = outerjoinRowToTable(p.Results.DataTable, ListFiles);
|
||||
% Then, we sort the table based on the time (datenum)
|
||||
ListFiles = sortrows(ListFiles,'lastModifiedDatenum');
|
||||
% check fromat
|
||||
assert(iscell(ListFiles.xRayEkin_eV), 'xRayEkin_eV must be cell')
|
||||
assert(iscell(ListFiles.scientaEkin_eV), 'scientaEkin_eV must be cell')
|
||||
end
|
||||
|
||||
%% Export
|
||||
|
||||
+91
-51
@@ -1,20 +1,25 @@
|
||||
function dataTable = importJeti(filename)
|
||||
function dataTable = importJeti(filename, selectedDatasets)
|
||||
%IMPORTJETI Import JETI SPECBOS irradiance data (CSV or TXT format)
|
||||
% DATATABLE = IMPORTJETI(FILENAME) reads data from text file FILENAME.
|
||||
% DATATABLE = IMPORTJETI(FILENAME) reads all data from the text file.
|
||||
%
|
||||
% DATATABLE = IMPORTJETI(FILENAME, SELECTEDDATASETS) reads only the
|
||||
% Wavelength and the specific dataset indices you request.
|
||||
% Example: table = importJeti("20260421_1500_Jeti.csv", [1, 5, 10]);
|
||||
%
|
||||
% Supports:
|
||||
% 1. CSV: Semicolon-separated with header metadata
|
||||
% 1. CSV: Auto-detects comma (,) or semicolon (;) delimiters, handles
|
||||
% various Date/Time formats, and flexible header lengths.
|
||||
% 2. isd: txt-style format with [sections] and tab-separated data
|
||||
%
|
||||
% Example:
|
||||
% table = importJeti("20251212_1606_Jeti.csv");
|
||||
% table = importJeti("spectrum_#2.isd");
|
||||
|
||||
if nargin < 2
|
||||
selectedDatasets = []; % Empty means import all
|
||||
end
|
||||
|
||||
%% Determine file type and route accordingly
|
||||
[~, ~, ext] = fileparts(filename);
|
||||
|
||||
if strcmpi(ext, '.csv')
|
||||
dataTable = importCSV(filename);
|
||||
dataTable = importCSV(filename, selectedDatasets);
|
||||
elseif strcmpi(ext, '.isd')
|
||||
dataTable = importTXT(filename);
|
||||
else
|
||||
@@ -24,13 +29,27 @@ end
|
||||
end
|
||||
|
||||
%% ========== CSV IMPORT FUNCTION ==========
|
||||
function dataTable = importCSV(filename)
|
||||
function dataTable = importCSV(filename, selectedDatasets)
|
||||
|
||||
fid = fopen(filename, 'r');
|
||||
if fid == -1
|
||||
error('Cannot open file: %s', filename);
|
||||
end
|
||||
|
||||
% 1. Auto-detect Delimiter
|
||||
fileDelimiter = ','; % default
|
||||
while ~feof(fid)
|
||||
thisLine = fgetl(fid);
|
||||
if contains(thisLine, ';')
|
||||
fileDelimiter = ';';
|
||||
break;
|
||||
elseif contains(thisLine, ',')
|
||||
fileDelimiter = ',';
|
||||
break;
|
||||
end
|
||||
end
|
||||
frewind(fid); % Reset back to beginning of file
|
||||
|
||||
metadata = struct();
|
||||
metadata.numDatasets = NaN;
|
||||
metadata.numPoints = NaN;
|
||||
@@ -39,7 +58,9 @@ metadata.Time = {};
|
||||
|
||||
lineIdx = 0;
|
||||
dataHeaderLineIdx = NaN;
|
||||
nHeaderCols = NaN;
|
||||
|
||||
% 2. Parse Metadata and find Wavelength header
|
||||
while true
|
||||
thisLine = fgetl(fid);
|
||||
if ~ischar(thisLine)
|
||||
@@ -47,18 +68,20 @@ while true
|
||||
end
|
||||
lineIdx = lineIdx + 1;
|
||||
|
||||
% Detect start of data header line
|
||||
if contains(thisLine, 'Wavelength [nm]')
|
||||
dataHeaderLineIdx = lineIdx;
|
||||
break
|
||||
end
|
||||
|
||||
parts = strsplit(thisLine, ';');
|
||||
|
||||
parts = strsplit(thisLine, fileDelimiter);
|
||||
if isempty(parts)
|
||||
continue
|
||||
end
|
||||
|
||||
% Detect start of data header line (usually 'Wavelength [nm]')
|
||||
if contains(parts{1}, 'Wavelength', 'IgnoreCase', true) && ...
|
||||
~contains(parts{1}, 'Start Wavelength') && ...
|
||||
~contains(parts{1}, 'End Wavelength')
|
||||
dataHeaderLineIdx = lineIdx;
|
||||
nHeaderCols = numel(parts);
|
||||
break
|
||||
end
|
||||
|
||||
key = strtrim(parts{1});
|
||||
|
||||
switch key
|
||||
@@ -72,47 +95,35 @@ while true
|
||||
end
|
||||
case 'Date'
|
||||
if numel(parts) >= 2
|
||||
% Remove empty trailing cells
|
||||
metadata.Date = parts(2:end);
|
||||
metadata.Date = metadata.Date(~cellfun('isempty', metadata.Date));
|
||||
end
|
||||
case 'Time'
|
||||
if numel(parts) >= 2
|
||||
metadata.Time = parts(2:end);
|
||||
metadata.Time = metadata.Time(~cellfun('isempty', metadata.Time));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if isnan(dataHeaderLineIdx)
|
||||
fclose(fid);
|
||||
error('Could not find data header line in CSV.');
|
||||
error('Could not find Wavelength data header line in CSV.');
|
||||
end
|
||||
|
||||
firstDataLineInFile = dataHeaderLineIdx + 1;
|
||||
fclose(fid);
|
||||
|
||||
% Derive number of columns:
|
||||
% If 'Number of datasets' is present: use it.
|
||||
% Otherwise infer from the header line (new Spec1/Spec2 format).[file:1]
|
||||
fid = fopen(filename, 'r');
|
||||
if fid == -1
|
||||
error('Cannot reopen file: %s', filename);
|
||||
end
|
||||
headerLine = '';
|
||||
for k = 1:dataHeaderLineIdx
|
||||
headerLine = fgetl(fid);
|
||||
end
|
||||
fclose(fid);
|
||||
|
||||
headerParts = strsplit(headerLine, ';');
|
||||
nHeaderCols = numel(headerParts);
|
||||
|
||||
% 3. Determine column structures
|
||||
if isnan(metadata.numDatasets)
|
||||
% New format: first column is wavelength, remaining are datasets
|
||||
% Infer from header columns (Subtract 1 for Wavelength column)
|
||||
metadata.numDatasets = nHeaderCols - 1;
|
||||
end
|
||||
|
||||
numCols = 1 + metadata.numDatasets;
|
||||
|
||||
% Build dataLines
|
||||
% Build dataLines bounds
|
||||
if isnan(metadata.numPoints)
|
||||
dataLines = [firstDataLineInFile, Inf];
|
||||
else
|
||||
@@ -123,9 +134,9 @@ end
|
||||
% Set up Import Options
|
||||
opts = delimitedTextImportOptions("NumVariables", numCols);
|
||||
opts.DataLines = dataLines;
|
||||
opts.Delimiter = ";";
|
||||
opts.Delimiter = fileDelimiter;
|
||||
|
||||
% Build variable names with units
|
||||
% 4. Build Variable Names
|
||||
varNames = cell(1, numCols);
|
||||
varNames{1} = "Wavelength [nm]";
|
||||
|
||||
@@ -134,13 +145,9 @@ if ~isempty(metadata.Date) && ~isempty(metadata.Time)
|
||||
for k = 1:nMeta
|
||||
dstr = strtrim(metadata.Date{k});
|
||||
tstr = strtrim(metadata.Time{k});
|
||||
|
||||
try
|
||||
% JETI CSV: e.g. 12/18/2025 and 10:40:14 for 12h clock.[file:1]
|
||||
dt = datetime([dstr ' ' tstr], ...
|
||||
'InputFormat', 'MM/dd/yyyy HH:mm:ss');
|
||||
timestr = datestr(dt, 'yyyymmdd_HHMMss');
|
||||
catch
|
||||
|
||||
timestr = parseJetiDateTime(dstr, tstr);
|
||||
if isempty(timestr)
|
||||
timestr = sprintf('dataset%02d', k);
|
||||
end
|
||||
|
||||
@@ -162,10 +169,48 @@ opts.ExtraColumnsRule = "ignore";
|
||||
opts.EmptyLineRule = "read";
|
||||
opts.VariableNamingRule = "preserve";
|
||||
|
||||
% Handle Specific Selected Columns
|
||||
if ~isempty(selectedDatasets)
|
||||
colsToKeep = [1, selectedDatasets + 1];
|
||||
% Prevent out-of-bounds error
|
||||
colsToKeep = colsToKeep(colsToKeep <= numCols);
|
||||
opts.SelectedVariableNames = opts.VariableNames(colsToKeep);
|
||||
end
|
||||
|
||||
% 5. Read the Table
|
||||
dataTable = readtable(filename, opts);
|
||||
|
||||
end
|
||||
|
||||
%% ========== DATETIME PARSER HELPER ==========
|
||||
function timestr = parseJetiDateTime(dstr, tstr)
|
||||
% Tries several Date/Time formats to safely parse standard 24h, 12h(am/pm),
|
||||
% and regional variations (dd.MM.yyyy vs MM/dd/yyyy).
|
||||
combinedStr = [dstr ' ' tstr];
|
||||
|
||||
formatsToTry = {
|
||||
'MM/dd/yyyy hh:mm:ssa', ... % US 12-hour (03:05:17pm)
|
||||
'dd.MM.yyyy HH:mm:ss', ... % EU 24-hour (12.12.2025 15:54:00)
|
||||
'MM/dd/yyyy HH:mm:ss', ... % US 24-hour
|
||||
'dd/MM/yyyy HH:mm:ss', ... % EU slash 24-hour
|
||||
'yyyy-MM-dd HH:mm:ss' % ISO 8601
|
||||
};
|
||||
|
||||
timestr = '';
|
||||
for i = 1:numel(formatsToTry)
|
||||
try
|
||||
dt = datetime(combinedStr, 'InputFormat', formatsToTry{i});
|
||||
if ~isnat(dt)
|
||||
timestr = datestr(dt, 'yyyymmdd_HHMMss');
|
||||
return;
|
||||
end
|
||||
catch
|
||||
% Format mismatched, try the next one
|
||||
continue;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
%% ========== TXT IMPORT FUNCTION ==========
|
||||
function dataTable = importTXT(filename)
|
||||
|
||||
@@ -194,13 +239,11 @@ while true
|
||||
end
|
||||
lineIdx = lineIdx + 1;
|
||||
|
||||
% Check for data section marker
|
||||
if strcmp(strtrim(thisLine), 'Data')
|
||||
dataStartLineIdx = lineIdx + 1;
|
||||
break
|
||||
end
|
||||
|
||||
% Parse key=value pairs
|
||||
if contains(thisLine, '=')
|
||||
parts = strsplit(thisLine, '=');
|
||||
if numel(parts) == 2
|
||||
@@ -234,13 +277,11 @@ end
|
||||
|
||||
fclose(fid);
|
||||
|
||||
% Set up import options for tab-separated data
|
||||
opts = delimitedTextImportOptions("NumVariables", 2);
|
||||
opts.DataLines = [dataStartLineIdx, Inf];
|
||||
opts.Delimiter = '\t';
|
||||
opts.VariableNamingRule = "preserve";
|
||||
|
||||
% Build column names as string array
|
||||
if ~isempty(metadata.XUnit) && ~isempty(metadata.YUnit)
|
||||
varNames = string({
|
||||
sprintf("Wavelength [%s]", metadata.XUnit)
|
||||
@@ -257,5 +298,4 @@ opts.VariableNames = varNames;
|
||||
opts.VariableTypes = ["double", "double"];
|
||||
|
||||
dataTable = readtable(filename, opts);
|
||||
|
||||
end
|
||||
end
|
||||
+148
-166
@@ -21,48 +21,6 @@ function exportFigureData(figHandle)
|
||||
% For non-tiled figures:
|
||||
% <FigureTitle>_<YLabel>_vs_<XLabel>_<LineLabel>.txt
|
||||
% Each file contains two columns: X-data and Y-data (tab-delimited)
|
||||
%
|
||||
% NOTES:
|
||||
% - Works with line plots (line objects) and scatter plots (scatter objects)
|
||||
% - Handles both regular axes and tiled layouts
|
||||
% - Data outside the x-axis scale limits are excluded
|
||||
% - Lines without legend entries are named as 'line_N' where N is a counter
|
||||
% - Scatter plots without legend entries are named as 'scatter_N'
|
||||
% - Axis labels default to 'x' and 'y' if not set
|
||||
% - Tile titles are included only for tiled layouts
|
||||
% - If no figure title is set, 'figure' is used as the base name
|
||||
%
|
||||
% EXAMPLE:
|
||||
% % Create a tiled figure with multiple subplots
|
||||
% figure
|
||||
% tiledlayout(2, 1, 'TileSpacing', 'compact')
|
||||
%
|
||||
% nexttile
|
||||
% x = 0:0.1:10;
|
||||
% plot(x, sin(x), 'DisplayName', 'sin(x)')
|
||||
% hold on
|
||||
% plot(x, cos(x), 'DisplayName', 'cos(x)')
|
||||
% title('Sine and Cosine')
|
||||
% xlabel('Time (s)')
|
||||
% ylabel('Amplitude')
|
||||
% legend
|
||||
%
|
||||
% nexttile
|
||||
% plot(x, sin(x).*cos(x), 'DisplayName', 'sin*cos')
|
||||
% title('Product')
|
||||
% xlabel('Time (s)')
|
||||
% ylabel('Value')
|
||||
% legend
|
||||
%
|
||||
% sgtitle('Trigonometric Functions')
|
||||
% exportFigureData(gcf)
|
||||
%
|
||||
% % Creates files like:
|
||||
% % Trigonometric_Functions_Sine_and_Cosine_Amplitude_vs_Time_(s)_sin(x).txt
|
||||
% % Trigonometric_Functions_Sine_and_Cosine_Amplitude_vs_Time_(s)_cos(x).txt
|
||||
% % Trigonometric_Functions_Product_Value_vs_Time_(s)_sin*cos.txt
|
||||
%
|
||||
% See also: tiledlayout, nexttile, legend, line, scatter
|
||||
|
||||
% Handle input: use current figure if none provided
|
||||
if nargin == 0 || isempty(figHandle)
|
||||
@@ -106,45 +64,14 @@ for ii = 1:length(allAxes)
|
||||
continue
|
||||
end
|
||||
|
||||
% Get axis titles
|
||||
xLabel = ax.XLabel.String;
|
||||
yLabel = ax.YLabel.String;
|
||||
tileTitle = ax.Title.String;
|
||||
|
||||
% Convert to cell if char, then handle empty cases
|
||||
if ischar(xLabel), xLabel = cellstr(xLabel); end
|
||||
if ischar(yLabel), yLabel = cellstr(yLabel); end
|
||||
if ischar(tileTitle), tileTitle = cellstr(tileTitle); end
|
||||
|
||||
if isempty(xLabel) || (iscell(xLabel) && isempty(xLabel{1}))
|
||||
xLabel = 'x';
|
||||
% Check if this axes uses yyaxis
|
||||
isYYAxis = isprop(ax, 'YAxis') && numel(ax.YAxis) == 2;
|
||||
if isYYAxis
|
||||
sidesToProcess = {'left', 'right'};
|
||||
else
|
||||
xLabel = xLabel{1};
|
||||
sidesToProcess = {'single'};
|
||||
end
|
||||
|
||||
if isempty(yLabel) || (iscell(yLabel) && isempty(yLabel{1}))
|
||||
yLabel = 'y';
|
||||
else
|
||||
yLabel = yLabel{1};
|
||||
end
|
||||
|
||||
if isempty(tileTitle) || (iscell(tileTitle) && isempty(tileTitle{1}))
|
||||
tileTitle = '';
|
||||
else
|
||||
tileTitle = tileTitle{1};
|
||||
end
|
||||
|
||||
% Sanitize axis labels and tile title
|
||||
xLabel = sanitizeFilename(xLabel);
|
||||
yLabel = sanitizeFilename(yLabel);
|
||||
tileTitle = sanitizeFilename(tileTitle);
|
||||
|
||||
% Get x-axis limits for filtering
|
||||
xLims = ax.XLim;
|
||||
|
||||
% Get all children of the axes (lines, scatter, etc.)
|
||||
children = get(ax, 'Children');
|
||||
|
||||
% Build a legend object if it exists (for extracting labels)
|
||||
legendObj = legend(ax);
|
||||
legendLabels = {};
|
||||
@@ -158,99 +85,156 @@ for ii = 1:length(allAxes)
|
||||
% Initialize a counter for matching legend entries to graphics objects
|
||||
graphicsIndex = 0;
|
||||
|
||||
% Loop over all child graphics objects
|
||||
for jj = 1:length(children)
|
||||
obj = children(jj);
|
||||
% Loop through both sides if yyaxis is used, otherwise process normally once
|
||||
for s = 1:length(sidesToProcess)
|
||||
|
||||
% Check if this is a line object
|
||||
if isa(obj, 'matlab.graphics.chart.primitive.Line')
|
||||
graphicsIndex = graphicsIndex + 1;
|
||||
|
||||
% Get x and y data
|
||||
xData = get(obj, 'XData');
|
||||
yData = get(obj, 'YData');
|
||||
|
||||
% Skip if empty
|
||||
if isempty(xData) || isempty(yData)
|
||||
continue
|
||||
end
|
||||
|
||||
% Filter data to x-axis range
|
||||
mask = (xData >= xLims(1)) & (xData <= xLims(2));
|
||||
xData = xData(mask);
|
||||
yData = yData(mask);
|
||||
|
||||
% Determine label: use legend entry or auto-generate name
|
||||
if graphicsIndex <= length(legendLabels) && ~isempty(legendLabels{graphicsIndex})
|
||||
label = legendLabels{graphicsIndex};
|
||||
else
|
||||
lineCounter = lineCounter + 1;
|
||||
label = sprintf('line_%d', lineCounter);
|
||||
end
|
||||
|
||||
% Sanitize label for filename
|
||||
label = sanitizeFilename(label);
|
||||
|
||||
% Create filename with tile title if present
|
||||
if ~isempty(tileTitle)
|
||||
fileName = sprintf('%s_%s_%s_vs_%s_%s.txt', figTitle, tileTitle, yLabel, xLabel, label);
|
||||
else
|
||||
fileName = sprintf('%s_%s_vs_%s_%s.txt', figTitle, yLabel, xLabel, label);
|
||||
end
|
||||
|
||||
% Write data to ASCII file
|
||||
data = [xData(:), yData(:)];
|
||||
writematrix(data, fileName, 'Delimiter', '\t');
|
||||
fprintf('Saved: %s\n', fileName);
|
||||
if isYYAxis
|
||||
yyaxis(ax, sidesToProcess{s}); % Switch active side to expose its lines and label
|
||||
end
|
||||
|
||||
% Check if this is a scatter object
|
||||
elseif isa(obj, 'matlab.graphics.chart.primitive.Scatter')
|
||||
graphicsIndex = graphicsIndex + 1;
|
||||
% Get axis titles (now correctly tied to the active side)
|
||||
xLabel = ax.XLabel.String;
|
||||
yLabel = ax.YLabel.String;
|
||||
tileTitle = ax.Title.String;
|
||||
|
||||
% Convert to cell if char, then handle empty cases
|
||||
if ischar(xLabel), xLabel = cellstr(xLabel); end
|
||||
if ischar(yLabel), yLabel = cellstr(yLabel); end
|
||||
if ischar(tileTitle), tileTitle = cellstr(tileTitle); end
|
||||
|
||||
if isempty(xLabel) || (iscell(xLabel) && isempty(xLabel{1}))
|
||||
xLabel = 'x';
|
||||
else
|
||||
xLabel = xLabel{1};
|
||||
end
|
||||
|
||||
if isempty(yLabel) || (iscell(yLabel) && isempty(yLabel{1}))
|
||||
yLabel = 'y';
|
||||
else
|
||||
yLabel = yLabel{1};
|
||||
end
|
||||
|
||||
if isempty(tileTitle) || (iscell(tileTitle) && isempty(tileTitle{1}))
|
||||
tileTitle = '';
|
||||
else
|
||||
tileTitle = tileTitle{1};
|
||||
end
|
||||
|
||||
% Sanitize axis labels and tile title
|
||||
xLabel = sanitizeFilename(xLabel);
|
||||
yLabel = sanitizeFilename(yLabel);
|
||||
tileTitle = sanitizeFilename(tileTitle);
|
||||
|
||||
% Get x-axis limits for filtering
|
||||
xLims = ax.XLim;
|
||||
|
||||
% Get all children of the active side
|
||||
children = get(ax, 'Children');
|
||||
|
||||
% Reverse so we extract in creation order to match the legend
|
||||
if ~isempty(children)
|
||||
children = flipud(children(:));
|
||||
end
|
||||
|
||||
% Loop over all child graphics objects for this side
|
||||
for jj = 1:length(children)
|
||||
obj = children(jj);
|
||||
|
||||
% Get x and y data
|
||||
xData = get(obj, 'XData');
|
||||
yData = get(obj, 'YData');
|
||||
% Check if this is a line object
|
||||
if isa(obj, 'matlab.graphics.chart.primitive.Line')
|
||||
graphicsIndex = graphicsIndex + 1;
|
||||
|
||||
% Get x and y data
|
||||
xData = get(obj, 'XData');
|
||||
yData = get(obj, 'YData');
|
||||
|
||||
% Skip if empty
|
||||
if isempty(xData) || isempty(yData)
|
||||
continue
|
||||
end
|
||||
|
||||
% Filter data to x-axis range
|
||||
mask = (xData >= xLims(1)) & (xData <= xLims(2));
|
||||
xData = xData(mask);
|
||||
yData = yData(mask);
|
||||
|
||||
% Determine label: prefer internal DisplayName to fix yyaxis mismatch
|
||||
label = '';
|
||||
if isprop(obj, 'DisplayName') && ~isempty(obj.DisplayName)
|
||||
label = obj.DisplayName;
|
||||
elseif graphicsIndex <= length(legendLabels) && ~isempty(legendLabels{graphicsIndex})
|
||||
label = legendLabels{graphicsIndex};
|
||||
else
|
||||
lineCounter = lineCounter + 1;
|
||||
label = sprintf('line_%d', lineCounter);
|
||||
end
|
||||
|
||||
% Sanitize label for filename
|
||||
label = sanitizeFilename(label);
|
||||
|
||||
% Create filename with tile title if present
|
||||
if ~isempty(tileTitle)
|
||||
fileName = sprintf('%s_%s_%s_vs_%s_%s.txt', figTitle, tileTitle, yLabel, xLabel, label);
|
||||
else
|
||||
fileName = sprintf('%s_%s_vs_%s_%s.txt', figTitle, yLabel, xLabel, label);
|
||||
end
|
||||
|
||||
% Write data to ASCII file
|
||||
data = [xData(:), yData(:)];
|
||||
writematrix(data, fileName, 'Delimiter', '\t');
|
||||
fprintf('Saved: %s\n', fileName);
|
||||
|
||||
% Skip if empty
|
||||
if isempty(xData) || isempty(yData)
|
||||
continue
|
||||
% Check if this is a scatter object
|
||||
elseif isa(obj, 'matlab.graphics.chart.primitive.Scatter')
|
||||
graphicsIndex = graphicsIndex + 1;
|
||||
|
||||
% Get x and y data
|
||||
xData = get(obj, 'XData');
|
||||
yData = get(obj, 'YData');
|
||||
|
||||
% Skip if empty
|
||||
if isempty(xData) || isempty(yData)
|
||||
continue
|
||||
end
|
||||
|
||||
% Ensure row vectors for indexing
|
||||
xData = xData(:)';
|
||||
yData = yData(:)';
|
||||
|
||||
% Filter data to x-axis range
|
||||
mask = (xData >= xLims(1)) & (xData <= xLims(2));
|
||||
xData = xData(mask);
|
||||
yData = yData(mask);
|
||||
|
||||
% Determine label
|
||||
label = '';
|
||||
if isprop(obj, 'DisplayName') && ~isempty(obj.DisplayName)
|
||||
label = obj.DisplayName;
|
||||
elseif graphicsIndex <= length(legendLabels) && ~isempty(legendLabels{graphicsIndex})
|
||||
label = legendLabels{graphicsIndex};
|
||||
else
|
||||
scatterCounter = scatterCounter + 1;
|
||||
label = sprintf('scatter_%d', scatterCounter);
|
||||
end
|
||||
|
||||
% Sanitize label for filename
|
||||
label = sanitizeFilename(label);
|
||||
|
||||
% Create filename with tile title if present
|
||||
if ~isempty(tileTitle)
|
||||
fileName = sprintf('%s_%s_%s_vs_%s_%s.txt', figTitle, tileTitle, yLabel, xLabel, label);
|
||||
else
|
||||
fileName = sprintf('%s_%s_vs_%s_%s.txt', figTitle, yLabel, xLabel, label);
|
||||
end
|
||||
|
||||
% Write data to ASCII file
|
||||
data = [xData(:), yData(:)];
|
||||
writematrix(data, fileName, 'Delimiter', '\t');
|
||||
fprintf('Saved: %s\n', fileName);
|
||||
end
|
||||
|
||||
% Ensure row vectors for indexing
|
||||
xData = xData(:)';
|
||||
yData = yData(:)';
|
||||
|
||||
% Filter data to x-axis range
|
||||
mask = (xData >= xLims(1)) & (xData <= xLims(2));
|
||||
xData = xData(mask);
|
||||
yData = yData(mask);
|
||||
|
||||
% Determine label: use legend entry or auto-generate name
|
||||
if graphicsIndex <= length(legendLabels) && ~isempty(legendLabels{graphicsIndex})
|
||||
label = legendLabels{graphicsIndex};
|
||||
else
|
||||
scatterCounter = scatterCounter + 1;
|
||||
label = sprintf('scatter_%d', scatterCounter);
|
||||
end
|
||||
|
||||
% Sanitize label for filename
|
||||
label = sanitizeFilename(label);
|
||||
|
||||
% Create filename with tile title if present
|
||||
if ~isempty(tileTitle)
|
||||
fileName = sprintf('%s_%s_%s_vs_%s_%s.txt', figTitle, tileTitle, yLabel, xLabel, label);
|
||||
else
|
||||
fileName = sprintf('%s_%s_vs_%s_%s.txt', figTitle, yLabel, xLabel, label);
|
||||
end
|
||||
|
||||
% Write data to ASCII file
|
||||
data = [xData(:), yData(:)];
|
||||
writematrix(data, fileName, 'Delimiter', '\t');
|
||||
fprintf('Saved: %s\n', fileName);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
fprintf('Data export complete.\n');
|
||||
end
|
||||
|
||||
@@ -262,7 +246,6 @@ function cleanStr = sanitizeFilename(str)
|
||||
%
|
||||
% Replaces spaces, slashes, backslashes, colons, asterisks, parentheses,
|
||||
% and other problematic characters with underscores.
|
||||
|
||||
cleanStr = str;
|
||||
cleanStr = strrep(cleanStr, ' ', '_');
|
||||
cleanStr = strrep(cleanStr, '/', '_');
|
||||
@@ -277,5 +260,4 @@ cleanStr = strrep(cleanStr, '|', '_');
|
||||
cleanStr = strrep(cleanStr, '(', '_');
|
||||
cleanStr = strrep(cleanStr, ')', '_');
|
||||
cleanStr = strrep(cleanStr, '.', '_'); % Optional: replace dots
|
||||
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user