diff --git a/plotly/plotly_aux/expandStruct.m b/plotly/plotly_aux/expandStruct.m new file mode 100644 index 00000000..c2c2ffce --- /dev/null +++ b/plotly/plotly_aux/expandStruct.m @@ -0,0 +1,20 @@ +function strP = expandStruct(S,strP) + +if nargin < 2 + strP="pr(1)"; +end + +% fprintf('%s\n',strP(end)); + +strIn = strP(end); +if isstruct(S) + for j=1:numel(S) + fn=fieldnames(S(j)); + for i = 1:numel(fn) + strP = [strP; join([strIn,fn{i}],'.')]; + if isstruct(S(j).(fn{i})) + strP = expandStruct(S(j).(fn{i}), strP); + end + end + end +end diff --git a/plotly/plotly_aux/searchDoc.m b/plotly/plotly_aux/searchDoc.m new file mode 100644 index 00000000..3ed06cc2 --- /dev/null +++ b/plotly/plotly_aux/searchDoc.m @@ -0,0 +1,20 @@ +function ss = searchDoc(varargin) +%SEARCHDOC Searches plotly JSON documentation for keywords +if nargin==0 + return; +end + +if ~isstruct(varargin{1}) + pr = load('plotly_reference.mat').pr; +else + pr = varargin{1}; + varargin=varargin(2:end); +end + +ss = expandStruct(pr,"pr(1)"); +if ~isempty(varargin{1}) + for i = 1:numel(varargin) + idx = cellfun(@(x) ~isempty(x), regexp(ss,['(\.',varargin{i},')$|(\.',varargin{i},'\.)'])); + ss = ss(idx); + end +end diff --git a/plotly/plotly_help_aux/.gitignore b/plotly/plotly_help_aux/.gitignore new file mode 100644 index 00000000..0df14cbb --- /dev/null +++ b/plotly/plotly_help_aux/.gitignore @@ -0,0 +1,2 @@ +plot-schema.json +plotly_reference_old.mat \ No newline at end of file diff --git a/plotly/plotly_help_aux/plotly_reference.mat b/plotly/plotly_help_aux/plotly_reference.mat index 37fa4a77..4f1baad2 100644 Binary files a/plotly/plotly_help_aux/plotly_reference.mat and b/plotly/plotly_help_aux/plotly_reference.mat differ diff --git a/plotly/plotly_help_aux/updateplotlyhelp.m b/plotly/plotly_help_aux/updateplotlyhelp.m index 2e6776e1..ef2dd406 100644 --- a/plotly/plotly_help_aux/updateplotlyhelp.m +++ b/plotly/plotly_help_aux/updateplotlyhelp.m @@ -2,28 +2,67 @@ function updateplotlyhelp % remote Plotly Graph Reference url -remote = ['https://raw.githubusercontent.com/plotly/',... - 'graph_reference/master/graph_objs/matlab/graph_objs_keymeta.json']; % download the remote content + +% !!! old remote !!! +% remote = ['https://raw.githubusercontent.com/plotly/',... +% 'graph_reference/master/graph_objs/matlab/graph_objs_keymeta.json']; + +% !!! new remote -- not recommended !!! +% remote = 'https://api.plot.ly/v2/plot-schema?format=json&sha1='; + +% !!! new remote -- Recommended !!! +remote = 'https://github.com/plotly/plotly.js/raw/master/dist/plot-schema.json'; + +fprintf('Downloading plotly-schema.json from remote...\n%s\n',remote); +newFile=false; try - prContent = urlread(remote); + prContent = webread(remote); + pr = jsondecode(prContent); + newFile=true; catch fprintf(['\nAn error occurred while trying to read the latest\n',... 'Plotly MATLAB API graph reference from:\n',... - 'https://github.com/plotly/graph_reference.\n']); - return + 'https://github.com/plotly/plotly.js/blob/master/dist/plot-schema.json.\n']); + try + pr = loadjson('plot-schema.json'); + catch + fprintf('Failed to load the local file... Quiting!\n'); + return; + end end -% load the json into a struct -pr = loadjson(prContent); +if newFile + f=fopen('plot-schema.json','w'); + fwrite(f,prContent); + fclose(f); +end %------------------------MATLAB SPECIFIC TWEAKS---------------------------% +%-Dump non-relevant info & simplify the content-% +% fields=fieldnames(pr.traces); +% for i = 1:numel(fields) +% field = fields{i}; +% try +% prN.(field) = pr.traces.(field); +% catch +% fprintf('No attributes for: %s | Adding entire field.\n',field); +% prN.(field) = pr.traces.(field); +% end +% end +% prN.layout = pr.layout.layoutAttributes; + +% prN.annotation = pr.layout.layoutAttributes.annotations.items.annotation; +% prN.line = pr.line; + %-key_type changes-% -pr.annotation.xref.key_type = 'plot_info'; -pr.annotation.yref.key_type = 'plot_info'; -pr.line.shape.key_type = 'plot_info'; +% prN.annotation.xref.key_type = 'plot_info'; +% prN.annotation.yref.key_type = 'plot_info'; +% prN.line.shape.key_type = 'plot_info'; + +% pr=prN; %-------------------------------------------------------------------------% @@ -34,6 +73,6 @@ prname = fullfile(helpdir); %----save----% -save(prname,'pr'); +save(prname,'pr'); end \ No newline at end of file diff --git a/plotly/plotlyfig.m b/plotly/plotlyfig.m index 6797bb4b..f1150eee 100644 --- a/plotly/plotlyfig.m +++ b/plotly/plotlyfig.m @@ -276,17 +276,21 @@ % strip the style keys from data for d = 1:length(obj.data) - if ( ... - strcmpi(obj.data{d}.type, 'scatter') || ... - strcmpi(obj.data{d}.type, 'bar') ... - ) + if strcmpi(obj.data{d}.type, 'scatter') return end - obj.data{d} = obj.stripkeys(obj.data{d}, obj.data{d}.type, 'style'); + obj.data{d} = obj.stripkeys(obj.data{d},'style', 'traces',obj.data{d}.type,'attributes'); end % strip the style keys from layout - obj.layout = obj.stripkeys(obj.layout, 'layout', 'style'); + try + fields = fieldnames(obj.PlotlyReference.traces.(obj.data{d}.type).layoutAttributes); + for i = 1:numel(fields) + field = fields{i}; + obj.PlotlyReference.layout.layoutAttributes.(field) = obj.PlotlyReference.traces.(obj.data{d}.type).layoutAttributes.(field); + end + end + obj.layout = obj.stripkeys(obj.layout,'style', 'layout','layoutAttributes'); end @@ -301,7 +305,7 @@ % remove style / plot_info types in data for d = 1:length(obj.data) - data{d} = obj.stripkeys(obj.data{d}, obj.data{d}.type, {'style','plot_info'}); + data{d} = obj.stripkeys(obj.data{d}, {'style','plot_info'}, 'traces', obj.data{d}.type, 'attributes'); end end @@ -314,11 +318,18 @@ function validate(obj) % validate data fields for d = 1:length(obj.data) - obj.stripkeys(obj.data{d}, obj.data{d}.type, {'style','plot_info'}); + obj.stripkeys(obj.data{d}, {'style','plot_info'}, 'traces', obj.data{d}.type, 'attributes'); end % validate layout fields - obj.stripkeys(obj.layout, 'layout', 'style'); + try + fields = fieldnames(obj.PlotlyReference.traces.(obj.data{d}.type).layoutAttributes); + for i = 1:numel(fields) + field = fields{i}; + obj.PlotlyReference.layout.layoutAttributes.(field) = obj.PlotlyReference.traces.(obj.data{d}.type).layoutAttributes.(field); + end + end + obj.stripkeys(obj.layout, 'style', 'layout', 'layoutAttributes'); end @@ -897,14 +908,15 @@ function delete(obj) methods (Access=private) %----STRIP THE FIELDS OF A SPECIFIED KEY-----% - function stripped = stripkeys(obj, fields, fieldname, key) - - %plorlt reference - pr = obj.PlotlyReference; + function stripped = stripkeys(obj,stripped,key,pr,varargin) + if ~isstruct(pr) + varargin=[pr,varargin]; + pr = obj.PlotlyReference; + end % initialize output % fields - stripped = fields; + % get fieldnames fn = fieldnames(stripped); @@ -918,16 +930,35 @@ function delete(obj) fnmod{d} = fn{d}(1:length('_axis')); end - % keys:(object, style, plot_info, data) - keytype = getfield(pr,fieldname,fnmod{d},'key_type'); + if ~strcmp(fnmod{d},'type') + % keys:(object, style, plot_info, data) + txt = 'keytype = getfield(pr'; + for i = 1:numel(varargin) + txt=[txt,',''',varargin{i},'''']; + end + txt=[txt,',''',fnmod{d},''',''role'');']; + + try + eval(txt); + catch ME + if strcmp(ME.message,'Unrecognized field name "role".') + keytype = ''; + else + txt + throw(ME); + end + end + else + keytype = ''; + end % check for objects if strcmp(keytype,'object') % clean up font keys - if any(strfind(fn{d},'font')) - fnmod{d} = 'font'; - end +% if any(strfind(fn{d},'font')) +% fnmod{d} = 'font'; +% end % handle annotations if strcmp(fn{d},'annotations') @@ -935,11 +966,22 @@ function delete(obj) fnmod{d} = 'annotation'; for a = 1:length(annot) %recursive call to stripkeys - stripped.annotations{a} = obj.stripkeys(annot{a}, fnmod{d}, key); + stripped.annotations{a} = obj.stripkeys(annot{a}, key, 'layout', 'layoutAttributes', 'annotations', 'items', fnmod{d}); end else %recursive call to stripkeys - stripped.(fn{d}) = obj.stripkeys(stripped.(fn{d}), fnmod{d}, key); + txt2 = 'prN = pr'; + for i = 1:numel(varargin) + txt2 = [txt2,'.',varargin{i}]; + end + txt2 = [txt2,';']; + try + eval(txt2); + catch ME + txt2 + throw(ME); + end + stripped.(fn{d}) = obj.stripkeys(stripped.(fn{d}), key, prN, fnmod{d}); end % look for desired key and strip if not an exception @@ -948,7 +990,6 @@ function delete(obj) stripped = rmfield(stripped, fn{d}); end end - end %----CLEAN UP----% @@ -968,14 +1009,7 @@ function delete(obj) catch exception if obj.UserData.Verbose % catch 3D output until integrated into graphref - if ~( ... - strcmpi(fieldname,'surface') || strcmpi(fieldname,'scatter3d') ... - || strcmpi(fieldname,'mesh3d') || strcmpi(fieldname,'bar') ... - || strcmpi(fieldname,'scatterpolar') || strcmpi(fieldname,'barpolar') ... - || strcmpi(fieldname,'scene') ... - ) - fprintf(['\nWhoops! ' exception.message(1:end-1) ' in ' fieldname '\n\n']); - end + fprintf(['\nWhoops! ' exception.message(1:end-1) ' in ' varargin{1} '\n\n']); end end end diff --git a/plotly/plotlyfig_aux/core/updateColorbar.m b/plotly/plotlyfig_aux/core/updateColorbar.m index a38273d3..46588c4d 100644 --- a/plotly/plotlyfig_aux/core/updateColorbar.m +++ b/plotly/plotlyfig_aux/core/updateColorbar.m @@ -124,34 +124,34 @@ if ~isempty(colorbar_title_data.String) %-colorbar titleside-% - colorbar.titleside = 'top'; + colorbar.title.side = 'top'; %-colorbar titlefont family-%: - colorbar.titlefont.family = matlab2plotlyfont(colorbar_title_data.FontName); + colorbar.title.font.family = matlab2plotlyfont(colorbar_title_data.FontName); %-colorbar titlefont color-% col = 255*colorbar_title_data.Color; - colorbar.titlefont.color = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')']; + colorbar.title.font.color = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')']; %-colorbar titlefont size-% - colorbar.titlefont.size = colorbar_title_data.FontSize; + colorbar.title.font.size = colorbar_title_data.FontSize; elseif ~isempty(colorbar_xlabel_data.String) %-colorbar titleside-% - colorbar.titleside = 'right'; + colorbar.title.side = 'right'; %-colorbar titlefont family-%: - colorbar.titlefont.family = matlab2plotlyfont(colorbar_xlabel_data.FontName); + colorbar.title.font.family = matlab2plotlyfont(colorbar_xlabel_data.FontName); %-colorbar titlefont color-% col = 255*colorbar_xlabel_data.Color; - colorbar.titlefont.color = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')']; + colorbar.title.font.color = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')']; %-colorbar titlefont size-% - colorbar.titlefont.size = colorbar_xlabel_data.FontSize; + colorbar.title.font.size = colorbar_xlabel_data.FontSize; elseif ~isempty(colorbar_ylabel_data.String) %-colorbar titleside-% - colorbar.titleside = 'bottom'; + colorbar.title.side = 'bottom'; %-colorbar titlefont family-%: - colorbar.titlefont.family = matlab2plotlyfont(colorbar_ylabel_data.FontName); + colorbar.title.font.family = matlab2plotlyfont(colorbar_ylabel_data.FontName); %-colorbar titlefont color-% col = 255*colorbar_ylabel_data.Color; - colorbar.titlefont.color = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')']; + colorbar.title.font.color = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')']; %-colorbar titlefont size-% - colorbar.titlefont.size = colorbar_ylabel_data.FontSize; + colorbar.title.font.size = colorbar_ylabel_data.FontSize; end %-REVERT UNITS-% diff --git a/plotly/plotlyfig_aux/core/updateData.m b/plotly/plotlyfig_aux/core/updateData.m index d8331ebd..c4b7ec6e 100644 --- a/plotly/plotlyfig_aux/core/updateData.m +++ b/plotly/plotlyfig_aux/core/updateData.m @@ -152,5 +152,4 @@ end %-------------------------------------------------------------------------% - end diff --git a/plotly/plotlyfig_aux/core/updateLegend.m b/plotly/plotlyfig_aux/core/updateLegend.m index d2159c24..90a26c52 100644 --- a/plotly/plotlyfig_aux/core/updateLegend.m +++ b/plotly/plotlyfig_aux/core/updateLegend.m @@ -36,11 +36,6 @@ %-------------------------------------------------------------------------% -%-legend xref-% -obj.layout.legend.xref = 'paper'; - -%-------------------------------------------------------------------------% - %-legend xanchor-% obj.layout.legend.xanchor = 'left'; @@ -51,11 +46,6 @@ %-------------------------------------------------------------------------% -%-legend yref-% -obj.layout.legend.yref = 'paper'; - -%-------------------------------------------------------------------------% - %-legend yanchor-% obj.layout.legend.yanchor = 'bottom'; diff --git a/plotly/plotlyfig_aux/handlegraphics/updateBar.m b/plotly/plotlyfig_aux/handlegraphics/updateBar.m index 3626e1fb..0acbc9cc 100644 --- a/plotly/plotlyfig_aux/handlegraphics/updateBar.m +++ b/plotly/plotlyfig_aux/handlegraphics/updateBar.m @@ -45,7 +45,8 @@ %-------------------------------------------------------------------------% %-AXIS INDEX-% -axIndex = obj.getAxisIndex(obj.State.Plot(barIndex).AssociatedAxis); +axis_data = obj.State.Plot(barIndex).AssociatedAxis; +axIndex = obj.getAxisIndex(axis_data); %-BAR DATA STRUCTURE- % bar_data = obj.State.Plot(barIndex).Handle; @@ -100,7 +101,11 @@ %---------------------------------------------------------------------% %-layout bargap-% -obj.layout.bargap = obj.PlotlyDefaults.Bargap; +if strcmp(obj.layout.barmode,'group') + obj.layout.bargap = 0.3; +else + obj.layout.bargap = obj.PlotlyDefaults.Bargap; +end %-------------------------------------------------------------------------% @@ -116,8 +121,15 @@ obj.data{barIndex}.x = bar_data.XData; %-bar y data-% - obj.data{barIndex}.y = bar_data.YData; + obj.data{barIndex}.y = bar_data.YData - bar_data.BaseValue; + eval(['obj.layout.yaxis',num2str(axIndex),'.range = [',num2str(minmax(axis_data.YTick-bar_data.BaseValue)),'];']); + eval(['obj.layout.yaxis',num2str(axIndex),'.tickvals = [',sprintf('%d,',axis_data.YTick-bar_data.BaseValue),'];']); + eval(['obj.layout.yaxis',num2str(axIndex),'.ticktext = [',num2str(axis_data.YTick),'];']); + eval(['obj.layout.yaxis',num2str(axIndex),'.tickmode = ''array'';']); + if bar_data.BaseValue ~= 0 + eval(['obj.layout.yaxis',num2str(axIndex),'.zeroline = 1;']); + end case 'on' @@ -125,12 +137,21 @@ obj.data{barIndex}.orientation = 'h'; %-bar x data-% - obj.data{barIndex}.x = bar_data.YData; + obj.data{barIndex}.x = bar_data.YData - bar_data.BaseValue; %-bar y data-% obj.data{barIndex}.y = bar_data.XData; + + eval(['obj.layout.xaxis',num2str(axIndex),'.range = [',num2str(minmax(axis_data.XTick-bar_data.BaseValue)),'];']); + eval(['obj.layout.xaxis',num2str(axIndex),'.tickvals = [',sprintf('%d,',axis_data.XTick-bar_data.BaseValue),'];']); + eval(['obj.layout.xaxis',num2str(axIndex),'.ticktext = [',num2str(axis_data.XTick),'];']); + eval(['obj.layout.xaxis',num2str(axIndex),'.tickmode = ''array'';']); + if bar_data.BaseValue ~= 0 + eval(['obj.layout.xaxis',num2str(axIndex),'.zeroline = 1;']); + end end + %---------------------------------------------------------------------% %-bar showlegend-% @@ -155,7 +176,11 @@ %-bar marker line-% markerline = extractAreaLine(bar_data); -obj.data{barIndex}.marker.line = markerline; + +% No dash property in line for bars. +markerline = rmfield(markerline,'dash'); + +obj.data{barIndex}.marker.line = markerline; %-------------------------------------------------------------------------% end diff --git a/plotly/plotlyfig_aux/handlegraphics/updatePatch.m b/plotly/plotlyfig_aux/handlegraphics/updatePatch.m index 1e942dd2..e7c6cffb 100644 --- a/plotly/plotlyfig_aux/handlegraphics/updatePatch.m +++ b/plotly/plotlyfig_aux/handlegraphics/updatePatch.m @@ -78,6 +78,9 @@ else obj.data{patchIndex}.type = 'scatter3d'; + % No xaxis/yaxis fields in obj.data for scatter3d in updated JSON + obj.data{patchIndex} = rmfield(obj.data{patchIndex},'xaxis'); + obj.data{patchIndex} = rmfield(obj.data{patchIndex},'yaxis'); end else obj.data{patchIndex}.type = 'scatter'; @@ -149,7 +152,9 @@ %---------------------------------------------------------------------% %-patch fill-% - obj.data{patchIndex}.fill = 'tozeroy'; + if ~strcmp(obj.data{patchIndex}.type,'scatter3d') + obj.data{patchIndex}.fill = 'tozeroy'; + end %-PATCH MODE-% if ~strcmpi('none', patch_data.Marker) && ~strcmpi('none', patch_data.LineStyle) diff --git a/plotly/plotlyfig_aux/helpers/cleanFeedTitle.m b/plotly/plotlyfig_aux/helpers/cleanFeedTitle.m index 657796ed..a893130b 100644 --- a/plotly/plotlyfig_aux/helpers/cleanFeedTitle.m +++ b/plotly/plotlyfig_aux/helpers/cleanFeedTitle.m @@ -32,14 +32,14 @@ function cleanFeedTitle(obj) first_title = obj.layout.annotations{annotation_index}.text; % use that as the filename - obj.layout.title = first_title; + obj.layout.title.text = first_title; % check for a single plot if (obj.State.Figure.NumPlots == 1) % grab the font style if not stripped if ~obj.PlotOptions.Strip - obj.layout.titlefont = ... + obj.layout.title.font = ... obj.layout.annotations{annotation_index}.font; end @@ -55,7 +55,7 @@ function cleanFeedTitle(obj) else % multiple plots ---> make the title invisible - obj.layout.titlefont.color = 'rgba(0,0,0,0)'; + obj.layout.title.font.color = 'rgba(0,0,0,0)'; end end end diff --git a/plotly/plotlyfig_aux/helpers/extractAxisData.m b/plotly/plotlyfig_aux/helpers/extractAxisData.m index a42a4721..bd5d79b1 100644 --- a/plotly/plotlyfig_aux/helpers/extractAxisData.m +++ b/plotly/plotlyfig_aux/helpers/extractAxisData.m @@ -51,7 +51,7 @@ axis.linecolor = axiscol; %-axis tickcolor-% axis.tickcolor = axiscol; -%-axis tickfont-% +%-axis tick font-% axis.tickfont.color = axiscol; %-axis grid color-% axis.gridcolor = axiscol; @@ -171,7 +171,7 @@ if (~isduration(temp)) axis.range = temp; axis.type = 'duration'; - axis.title = type; + axis.title.text = type; else nticks = eval(['length(axis_data.' axisName 'Tick)-1;']); delta = 0.1; @@ -251,24 +251,24 @@ %-title-% if ~isempty(label_data.String) - axis.title = parseString(label_data.String,label_data.Interpreter); + axis.title.text = parseString(label_data.String,label_data.Interpreter); end %-------------------------------------------------------------------------% %-axis title font color-% col = 255*label_data.Color; -axis.titlefont.color = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')']; +axis.title.font.color = ['rgb(' num2str(col(1)) ',' num2str(col(2)) ',' num2str(col(3)) ')']; %-------------------------------------------------------------------------% %-axis title font size-% -axis.titlefont.size = label_data.FontSize; +axis.title.font.size = label_data.FontSize; %-------------------------------------------------------------------------% %-axis title font family-% -axis.titlefont.family = matlab2plotlyfont(label_data.FontName); +axis.title.font.family = matlab2plotlyfont(label_data.FontName); %-------------------------------------------------------------------------% @@ -296,6 +296,10 @@ end %-------------------------------------------------------------------------% + +% Autotick is depcrecated. +axis = rmfield(axis,'autotick'); + end diff --git a/plotly/plotlyhelp.m b/plotly/plotlyhelp.m index 40512815..a1b712d8 100644 --- a/plotly/plotlyhelp.m +++ b/plotly/plotlyhelp.m @@ -7,24 +7,26 @@ pr.online = 'Access the online docs!'; try - switch length(varargin) - case 0 - plotlyref = pr; - case 1 - if(strcmpi('online',varargin{1})); - web('http://plot.ly/matlab/','-browser') - else - plotlyref = pr.(lower(varargin{1})); + if (nargin==1) && (strcmpi('online',varargin{1})) + web('http://plot.ly/matlab/','-browser'); + else + try + txt = 'plotlyref = pr'; + for i=1:nargin + txt=[txt,'.',varargin{i}]; end - case 2 - plotlyref = pr.(lower(varargin{1})).(lower(varargin{2})); - case 3 - plotlyref = pr.(lower(varargin{1})).(lower(varargin{2})).(lower(varargin{3})); - case 4 %does the struct nesting ever go beyond 4? - plotlyref = pr.(lower(varargin{1})).(lower(varargin{2})).(lower(varargin{3})).(lower(varargin{4})); + txt = [txt,';']; + eval(txt); + catch + strVec = expandStruct(pr,"pr(1)"); + for i = 1:nargin + idx = cellfun(@(x) ~isempty(x), regexp(strVec,['(\.',varargin{i},')$|(\.',varargin{i},'\.)'])); + strVec = strVec(idx); + end + eval(join(["plotlyref = ",strVec(1),";"],'')); + end end - -catch exception +catch fprintf('\n\nSorry! We could not find what you were looking for. Please specify a valid Plotly reference.\n\n'); end