I would like to refresh my figure (3D, obtained with the matlab slice command) using a slider (GUI) to update the slice position.
Here is my code to create the figure (it works):
% Create figure
Fig_3dview = figure;
Fig_3dview.Name= 'Microstructure viewer';
% hold on;
% The data to be displayed are within "Volume", a n*m*k matrix
Domain_size = size(Volume)
% Initial position of the slices
% Basically, the 6 faces of the volume
xslice = [1,Domain_size(1)];
yslice = [1,Domain_size(2)];
zslice = [1,Domain_size(3)];
% Display all the slices
imslice = slice(Volume,xslice,yslice,zslice,'parent',Fig_3dview);
% Remove voxel edge
set(imslice,'edgecolor','none')
% Current axis label
ax_3d_slice = gca;
% Set axis equal and tight
axis equal tight
hold off;
The code for the GUI, to control the slice position (it doesn't work):
% Create GUI figure
Fig_3dview_GUI = figure;
Fig_3dview_GUI.Name= 'Microstructure viewer 3D GUI';
% Create slider
h_slice1 = uicontrol('Style','slider','BackgroundColor','w',...
'Unit','pixels','Position',[10,200,250,50],'Parent',Fig_3dview_GUI,...
'Callback',{#slider_slice1_Callback});
% Define function associated with h_slice1
function slider_slice1_Callback(source,eventdata)
% Determine the selected data set.
pos_normalized = source.Value;
position_slice_1= round(pos_normalized*Domain_size(1)); % Get closest integer value
position_slice_1=max(1,position_slice_1); % 0 must be avoided
% Update figure
And here I don't know how to update the figure Fig_3dview with the new position for the slice normal to the first direction.
I would like the new figure looks like:
xslice = [position_slice_1,Domain_size(1)];
imslice = slice(Volume,xslice,yslice,zslice,'parent',Fig_3dview);
I do not want to close the figure and regenerate it, I want to refresh it, so that I get a smooth visualization.
% Update figure
end
Thanks for any advice.
François
Related
What is a proper way to separate the initialization and the display of plots in Matlab? (I mean plots in a wide sense here; could be plot, plot3, scatter etc.) To give a concrete example, I have a pretty complex 3D visualization that uses sphere and mesh to draw a static sphere mesh and then scatter3 to plot a moving trajectory on the sphere. To be able to do this in real time I have implemented some simple optimizations, such as only updating the scatter3 object each frame. But the code is a bit messy, making it hard to add additional features that I want, so I would like improve code separation.
I also feel like it might sometimes be useful to return some kind of plot object from a function without displaying it, for example to combine it with other plots in a nice modular way.
An example of what I have in mind would be something like this:
function frames = spherePlot(solution, options)
% Initialize sphere mesh and scatter objects, configure properties.
...
% Configure axes, maybe figure as well.
...
% Draw sphere.
...
if options.display
% Display figure.
end
for step = 1:solution.length
% Update scatter object, redraw, save frame.
% The frames are saved for use with 'movie' or 'VideoWriter'.
end
end
Each step might also be separated out as a function.
So, what is a neat and proper way to do stuff like this? All documentation seems to assume that one wants to display everything right away.
For example
% some sample data
N = 100;
phi = linspace(-pi, pi, N);
theta = linspace(-pi, pi, N);
f = #(phi, theta) [sin(phi).*cos(theta); sin(phi).*sin(theta); cos(phi)];
data = f(phi, theta);
% init plot
figure(1); clf
plot3(data(1,:), data(2,:), data(3,:)); % plot path, not updated
hold on
p = plot3([0 data(1,1)], [0 data(2,1)], [0 data(3,1)]); % save handle to graphics objects to update
s = scatter3(data(1,1), data(2,1), data(3,1), 'filled');
axis equal
xlabel('x'); ylabel('y'); zlabel('z');
t = title('first frame'); % also store handle for title or labels to update during animation
% now animate the figure
for k = 1:N
p.XData = [0 data(1,k)]; % update line data
p.YData = [0 data(2,k)];
p.ZData = [0 data(3,k)];
s.XData = data(1,k); % update scatter data
s.YData = data(2,k);
s.ZData = data(3,k);
t.String = sprintf('frame %i', k); % update title
drawnow % update figure
end
Basically you can update all values for a graphics handle, in this case 'p' and 's'. If you open the matlab doc for plot or plot3 you will find a link to all properties of that primitive: e.g. Line Properties. Similar documentation pages exist for scatter/imagesc etc.
So the general idea is to first create a figure with the first frame, save the handles to the objects you would like to update (p = plot(...), and then enter a loop in which you update the required property of that graphics object (e.g. p.Color = 'r', or p.XData = ...).
Given a .fig file of a Matlab boxplot (i.e. underlying data not available), is it possible to change the PlotStyle attribute (from 'traditional' to 'compact')?
This question is kind of tricky because not like other graphic objects in Matlab, boxplot is a group of lines. As so, all the properties that are set while you create it are inaccessible (and in fact does not exist) after plotting.
One option to deal with that is to create a 'dummy' boxplot, and then alter it to your data. Because boxplot has no simple properties of XData and YData, at least not as we use to them, it takes some work to do that.
Here is a short code to demonstrate that:
% this is just to make a figure for example:
X = normrnd(10,1,100,1);
boxplot(X) % this is the 'Traditional' figure that you load
% you start here, after you load your figure:
bx = findobj('tag','boxplot');
% get the properties of the axes:
axlimx = bx.Parent.XLim;
axlimy = bx.Parent.YLim;
% get all needed properties for plotting compact box plot
txt = bx.Parent.XAxis.TickLabels;
med = get(findobj(bx,'tag','Median'),'YData'); % Median
out = get(findobj(bx,'tag','Outliers'),'YData'); % Outliers
box = get(findobj(bx,'tag','Box'),'YData'); % the Box
whis = cell2mat(get([findobj(bx,'tag','Lower Whisker')...
findobj(bx,'tag','Upper Whisker')],'YData')); % Whisker
minmax = #(R) [min(R(:)) max(R(:))]; % helper function
close all
% Now we closed the original figure, and create a new one for manipulation:
boxplot(normrnd(10,1,100,1),'PlotStyle','Compact');
bxc = findobj('tag','boxplot');
% set the properties of the axes:
bxc.Parent.XLim = axlimx;
bxc.Parent.YLim = axlimy;
% set all properties of the compact box plot:
bxc.Children(1).String = txt;
set(bxc.Children(2),'YData',out) % Outliers
set(bxc.Children(3:4),'YData',med(1)) % MedianInner & MedianOuter
set(bxc.Children(5),'YData',minmax(box)) % the Box
set(bxc.Children(6),'YData',minmax(whis)) % Whisker
Another way to alter the boxplot to 'compact' style, is to change the graphics directly. In this case, we don't create a new dummy figure but work on the loaded figure.
Here is a code for that approach:
% this is just to make a figure for example:
X = normrnd(10,1,100,1);
boxplot(X) % this is the 'Traditional' figure that you load
% you start here, after you load your figure:
bx = findobj('tag','boxplot');
minmax = #(R) [min(R(:)) max(R(:))]; % helper function
% get the whisker limits:
whis = cell2mat(get([findobj(bx,'tag','Lower Whisker')...
findobj(bx,'tag','Upper Whisker')],'YData')); % Whisker
set(findobj(bx,'tag','Upper Whisker'),{'YData','Color','LineStyle'},...
{minmax(whis),'b','-'})
% set the median:
set(findobj(bx,'tag','Median'),{'XData','YData','LineStyle','Marker',...
'Color','MarkerSize','MarkerFaceColor'},...
{1,min(get(findobj(bx,'tag','Median'),'YData')),'none','o','b',6,'auto'});
% set the box:
set(findobj(bx,'tag','Box'),{'XData','YData','LineWidth'},...
{[1 1],minmax(get(findobj(bx,'tag','Box'),'YData')),4});
im_med = copyobj(findobj(bx,'tag','Median'),bx);
im_med.Marker = '.';
% set the outliers:
out = get(findobj(bx,'tag','Outliers'),'YData'); % Outliers
set(findobj(bx,'tag','Outliers'),{'XData','LineStyle','Marker','MarkerEdgeColor',...
'MarkerSize','MarkerFaceColor'},{0.9+0.2*rand(size(out)),'none','o','b',4,'none'});
% rotate x-axis labels:
bx.Parent.XAxis.TickLabelRotation = 90;
% delete all the rest:
delete(findobj(bx,'tag','Lower Whisker'))
delete(findobj(bx,'tag','Lower Adjacent Value'))
delete(findobj(bx,'tag','Upper Adjacent Value'))
I am life plotting a dataset for creating hysteresis loops. I agreed with the plot speed before but since I added the scatter it got really slow.
The scatter adds a marker to the current plotted datapoint so that if the plot overwrites you can still pursuite the animated line.
Here is my code:
hAL = animatedline; % line handle
hAL.LineStyle='-';
hAL.Color='blue';
% defaults for the marker
hAx=gca; % get the axis handle
sz=10; % size of marker
clr='b'; % color
hS=scatter(hAx,nan,nan,sz,clr); % initial point won't show but creates handle
x = mmatrix(:,2);
y = mmatrix(:,1);
for k = 1:length(x)
addpoints(hAL,x(k),y(k));
set(hS,'xdata',x(k),'ydata',y(k)) % update the marker position
drawnow
end
Do you know of an option to regulate the plot speed?
I tried drawnow limitrate but it's to fast.
Regards jw
I'm trying to find a way to nicely plot my measurement data of digital signals.
So I have my data available as csv and mat file, exported from an Agilent Oscilloscope. The reason I'm not just taking a screen shot of the Oscilloscope screen is that I need to be more flexible (make several plots with one set of data, only showing some of the lines). Also I need to be able to change the plot in a month or two so my only option is creating a plot from the data with a computer.
What I'm trying to achieve is something similar to this picture:
The only thing missing on that pic is a yaxis with 0 and 1 lines.
My first try was to make a similar plot with Matlab. Here's what I got:
What's definitely missing is that the signal names are right next to the actual line and also 0 and 1 ticks on the y-axis.
I'm not even sure if Matlab is the right tool for this and I hope you guys can give me some hints/a solution on how to make my plots :-)
Here's my Matlab code:
clear;
close all;
clc;
MD.RAW = load('Daten/UVLOT1 debounced 0.mat'); % get MeasurementData
MD.N(1) = {'INIT\_DONE'};
MD.N(2) = {'CONF\_DONE'};
MD.N(3) = {'NSDN'};
MD.N(4) = {'NRST'};
MD.N(5) = {'1V2GD'};
MD.N(6) = {'2V5GD'};
MD.N(7) = {'3V3GD'};
MD.N(8) = {'5VGD'};
MD.N(9) = {'NERR'};
MD.N(10) = {'PGD'};
MD.N(11) = {'FGD'};
MD.N(12) = {'IGAGD'};
MD.N(13) = {'GT1'};
MD.N(14) = {'NERRA'};
MD.N(15) = {'GT1D'};
MD.N(16) = {'GB1D'};
% concat vectors into one matrix
MD.D = [MD.RAW.Trace_D0, MD.RAW.Trace_D1(:,2), MD.RAW.Trace_D2(:,2), MD.RAW.Trace_D3(:,2), ...
MD.RAW.Trace_D4(:,2), MD.RAW.Trace_D5(:,2), MD.RAW.Trace_D6(:,2), MD.RAW.Trace_D7(:,2), ...
MD.RAW.Trace_D8(:,2), MD.RAW.Trace_D9(:,2), MD.RAW.Trace_D10(:,2), MD.RAW.Trace_D11(:,2), ...
MD.RAW.Trace_D12(:,2), MD.RAW.Trace_D13(:,2), MD.RAW.Trace_D14(:,2), MD.RAW.Trace_D15(:,2)];
cm = hsv(size(MD.D,2)); % make colormap for plot
figure;
hold on;
% change timebase to ns
MD.D(:,1) = MD.D(:,1) * 1e9;
% plot lines
for i=2:1:size(MD.D,2)
plot(MD.D(:,1), MD.D(:,i)+(i-2)*1.5, 'color', cm(i-1,:));
end
hold off;
legend(MD.N, 'Location', 'EastOutside');
xlabel('Zeit [ns]'); % x axis label
title('Messwerte'); % title
set(gca, 'ytick', []); % hide y axis
Thank you guys for your help!
Dan
EDIT:
Here's a pic what I basically want. I added the signal names via text now the only thing that's missing are the 0, 1 ticks. They are correct for the init done signal. Now I just need them repeated instead of the other numbers on the y axis (sorry, kinda hard to explain :-)
So as written in my comment to the question. For appending Names to each signal I would recommend searching the documentation of how to append text to graph. There you get many different ways how to do it. You can change the position (above, below) and the exact point of data. As an example you could use:
text(x_data, y_data, Var_Name,'VerticalAlignment','top');
Here (x_data, y_data) is the data point where you want to append the text and Var_Name is the name you want to append.
For the second question of how to get a y-data which contains 0 and 1 values for each signal. I would do it by creating your signal the way, that your first signal has values of 0 and 1. The next signal is drawn about 2 higher. Thus it changes from 2 to 3 and so on. That way when you turn on y-axis (grid on) you get values at each integer (obviously you can change that to other values if you prefer less distance between 2 signals). Then you can relabel the y-axis using the documentation of axes (check the last part, because the documentation is quite long) and the set() function:
set(gca, 'YTick',0:1:last_entry, 'YTickLabel',new_y_label(0:1:last_entry))
Here last_entry is 2*No_Signals-1 and new_y_label is an array which is constructed of 0,1,0,1,0,....
For viewing y axis, you can turn the grid('on') option. However, you cannot chage the way the legends appear unless you resize it in the matlab figure. If you really want you can insert separate textboxes below each of the signal plots by using the insert ->Textbox option and then change the property (linestyle) of the textbox to none to get the exact same plot as above.
This is the end result and all my code, in case anybody else wants to use the good old ctrl-v ;-)
Code:
clear;
close all;
clc;
MD.RAW = load('Daten/UVLOT1 debounced 0.mat'); % get MeasurementData
MD.N(1) = {'INIT\_DONE'};
MD.N(2) = {'CONF\_DONE'};
MD.N(3) = {'NSDN'};
MD.N(4) = {'NRST'};
MD.N(5) = {'1V2GD'};
MD.N(6) = {'2V5GD'};
MD.N(7) = {'3V3GD'};
MD.N(8) = {'5VGD'};
MD.N(9) = {'NERR'};
MD.N(10) = {'PGD'};
MD.N(11) = {'FGD'};
MD.N(12) = {'IGAGD'};
MD.N(13) = {'GT1'};
MD.N(14) = {'NERRA'};
MD.N(15) = {'GT1D'};
MD.N(16) = {'GB1D'};
% concat vectors into one matrix
MD.D = [MD.RAW.Trace_D0, MD.RAW.Trace_D1(:,2), MD.RAW.Trace_D2(:,2), MD.RAW.Trace_D3(:,2), ...
MD.RAW.Trace_D4(:,2), MD.RAW.Trace_D5(:,2), MD.RAW.Trace_D6(:,2), MD.RAW.Trace_D7(:,2), ...
MD.RAW.Trace_D8(:,2), MD.RAW.Trace_D9(:,2), MD.RAW.Trace_D10(:,2), MD.RAW.Trace_D11(:,2), ...
MD.RAW.Trace_D12(:,2), MD.RAW.Trace_D13(:,2), MD.RAW.Trace_D14(:,2), MD.RAW.Trace_D15(:,2)];
cm = hsv(size(MD.D,2)); % make colormap for plot
figure;
hold on;
% change timebase to ns
MD.D(:,1) = MD.D(:,1) * 1e9;
% plot lines
for i=2:1:size(MD.D,2)
plot(MD.D(:,1), MD.D(:,i)+(i-2)*2, 'color', cm(i-1,:));
text(MD.D(2,1), (i-2)*2+.5, MD.N(i-1));
end
hold off;
%legend(MD.N, 'Location', 'EastOutside');
xlabel('Zeit [ns]'); % x axis label
title('Messwerte'); % title
% make y axis and grid the way I want it
set(gca, 'ytick', 0:size(MD.D,2)*2-3);
grid off;
set(gca,'ygrid','on');
set(gca, 'YTickLabel', {'0'; '1'});
ylim([-1,(size(MD.D,2)-1)*2]);
I have a image in my gui with added gridlines. I need be able to to create and save a text file with the N by M dimensions of the graph containing the contents of each box in the grid.
Is there a way to do this?
Here is an example.
Main file
This should be the main script.
Grid_Size = [5 5];
Grid_Type = 1; % The group of the new clicked points.
% Initialize
Grid_Matrix = zeros(Grid_Size);
grid_h = []; % Grid image handle
fig_h = figure('Units', 'Normalized');
% Show your image
rgb = imread('peppers.png');
im_h = imshow(rgb);
% Shut off the 'HitTest' to keep the functionality of figure ButtonDownFcn
set(im_h, 'HitTest', 'Off');
hold on % Hold on for the overlaying grid image.
% Image normalized position
Image_Pos = get(ancestor(im_h, 'axes'), 'Position');
im_size = size(rgb(:,:,1));
% Set the Mouse click callback for the figure (to a script in this case)
set(fig_h, 'ButtonDownFcn', 'Mouse_Click');
Mouse_Click.m
The script callback, I suggest script rather than function to avoid the complexity of scope and etc.
Clicked = fliplr((get(fig_h, 'CurrentPoint') - Image_Pos(1:2))./Image_Pos(3:4));
if ~sum(Clicked < 0) && ~sum(Clicked > 1) % If inside of the image is clicked
% Find the corresponding matrix index
ind = num2cell(ceil(Clicked.*Grid_Size));
% and toggle that element
if Grid_Matrix(ind{:}) == 0
Grid_Matrix(ind{:}) = Grid_Type;
else
Grid_Matrix(ind{:}) = 0;
end
% Show the result to user
delete(grid_h); % delete the previous grid image
% resize the Grid Matrix to the image size
Grid_Matrix_rs = imresize(flipud(Grid_Matrix), im_size, 'box');
Grid_Matrix_rgb = label2rgb(Grid_Matrix_rs, 'spring', 'b');
grid_h = imshow(Grid_Matrix_rgb);
set(grid_h, 'HitTest', 'Off');
set(grid_h, 'AlphaData', 0.3); % Show the grid image with an opacity
% Write data to file after each update (user click)
dlmwrite('GridBox.dat', flipud(Grid_Matrix));
end
Note that flipud is used to flip the matrix to match the image indexing.
During clicking you can change Grid_Type (Using a UI or in command window) to another integer so that you get another color. However the previous colors might change, but finally all the same colors have the same index inside the Grid_Matrix.
You will get something like:
Corresponding to this Grid_Matrix:
4,0,3,0,2
0,1,0,0,0
3,0,1,0,4
2,0,2,1,0
0,0,3,0,0
To Read the result from file
res = dlmread('GridBox.dat')