2

I am trying to visualize the data contained a 3D array in MATLAB.

The array is has the dimension of 20*20*40 which all except some of the elements being zero.

I am looking for a way to plot these non-zero points in a scatter plot so that the non-zero points are connected to each other.

here is what I've done so far:

b=zeros(6,6,3);
a=[1 1 1;2 2 1;2 1 1;1 2 1;
1 1 2;1 2 2;2 1 2;2 2 2;
6 6 3;6 5 3;5 6 3;5 5 3;
6 6 2;6 5 2;5 6 2;5 5 2];
[r c]=size(a);
for i = 1:r
 b(a(i,c-2),a(i,c-1),a(i,c)) = 1;
end
[m n o]=size(b);
figure (1)
[x,y,z] = meshgrid(1:m,1:n,1:o);
scatter3(x(:),y(:),z(:),90,b(:),'filled')

So, what I am after is being able to connect each of the eight points two form to cubic lattices. Any thoughts is much appreciated. enter image description here

EDIT: Many thanks to all the experts who helped a lot. Now, I am facing another issue with memory.

The b matrix for my real case would be 1000*1000*2000 and I have the "a" matrix of the size 4,4700,000*3 . All the elements of "a" matrix are integer values. Although I have up to 48GB of memory availabe. However, in the "for loop" above the program bounces back an "out of memory" error.

Any ideas to make this more memory efficient is greatly appreciated.

Zero
  • 74,117
  • 18
  • 147
  • 154
regex99
  • 97
  • 3
  • 9
  • What do the 3 dimensions of the matrix represent? Are you trying to plot it in 3D? And have you looked into `plot3`, which is an obvious first choice? – tmpearce Jun 15 '12 at 19:26

3 Answers3

3

Consider the following (based on a previous answer):

%# adjacency matrix
adj = false(numel(b));

%# extract first cube, and connect all its points
bb = b;
bb(:,1:3,:) = false;
idx = find(bb);
adj(idx,idx) = true;

%# extract second cube, and connect all its points
bb = b;
bb(:,4:6,:) = false;
idx = find(bb);
adj(idx,idx) = true;

%# points indices
[r c] = find(adj);
p = [r c]';

%# plot
plot3(x(p), y(p), z(p), 'LineWidth',2, 'Color',[.4 .4 1], ...
    'Marker','o', 'MarkerSize',6, ...
    'MarkerFaceColor','g', 'MarkerEdgeColor','g')
axis equal, axis vis3d, grid on, view(3)
xlabel x, ylabel y, zlabel z

enter image description here


EDIT:

It would be easier to just manually connect the points:

%# edges: connecting points indices
p = [
    1 2; 2 8; 8 7; 7 1;
    37 38; 38 44; 44 43; 43 37;
    1 37; 2 38; 7 43; 8 44;
    65 66; 66 72; 72 71; 71 65;
    101 102; 102 108; 108 107; 107 101;
    65 101; 66 102; 71 107; 72 108
]';

%# plot
figure
plot3(x(p), y(p), z(p), 'LineWidth',2, 'Color',[.4 .4 1], ...
    'Marker','o', 'MarkerSize',6, ...
    'MarkerFaceColor','g', 'MarkerEdgeColor','g')
axis equal, axis vis3d, grid on, view(3)
xlabel x, ylabel y, zlabel z

%# label points
labels = strtrim(cellstr( num2str((1:numel(b))','%d') ));
idx = find(b);
text(x(idx(:)), y(idx(:)), z(idx(:)), labels(idx(:)), ...
    'Color','m', ...
    'VerticalAlignment','bottom', 'HorizontalAlignment','left')

screenshot


EDIT#2: (credit goes to @tmpearce)

You can semi-automate the process of builing the edges list. Just replace the manually constructed matrx p in the above code with the following:

%# compute edges: pairs of vertex indices 
yIdx = {1:3 4:6};           %# hack to separate each cube points
p = cell(numel(yIdx),1);
for i=1:numel(yIdx)         %# for each cube
    %# find indices of vertices in this cube
    bb = b;
    bb(:,yIdx{i},:) = false;
    idx = find(bb);

    %# compute L1-distance between all pairs of vertices,
    %# and find pairs which are unit length apart
    [r,c] = find(triu(squareform(pdist([x(idx) y(idx) z(idx)],'cityblock'))==1));

    %# store the edges
    p{i} = [idx(r) idx(c)]';
end
p = cat(2,p{:});            %# merge all edges found

The idea is, for each cube, we compute the city-block distance between all vertices. The "side" edges will have a distance of 1, while the diagonals have a distance greater than or equal to 2.

This process still assume that the list of vertices belonging to each cube is provided to us, or easily extracted as we did in the code above...

Community
  • 1
  • 1
Amro
  • 123,847
  • 25
  • 243
  • 454
  • Actually I made the connections by hand too. Just wondering if there is a faster way of doing it as there is a big number of such cubes. – regex99 Jun 17 '12 at 14:52
  • 1
    @Amro I added a method in a supplemental answer that finds the edges automatically. I recommend incorporating it into this answer if possible. – tmpearce Jun 18 '12 at 21:15
  • Cool, I hadn't used `pdist` before, though I should have known there'd be a built-in function to do pairwise distances. If the original matrix is set up such that all "true" points belong to cubes, there's no need to loop over the cubes (unless memory or computation time are a problem for large matrices). – tmpearce Jun 18 '12 at 22:56
  • @tmpearce: that definitely would be "cleaner" (read properly vectorized), but imagine the case where the two cubes are next to each other, separated by only one unit... – Amro Jun 18 '12 at 23:04
  • 1
    @Amro true. If you have "cube membership id" in a vector the size of idx (call it `mid` maybe), and create a logical index with `mid(r)==mid(c)`, you can limit yourself to edges within cubes while keeping everything vectorized. – tmpearce Jun 18 '12 at 23:13
2

I thought I'd answer the follow-up to Amro's answer, and show how you can automate the connection of edges along a cubic matrix. This answer is intended to be a supplement to Amro's, and I would recommend incorporating it into that solution so it is easier to read for future viewers.

This starts with idx from Amro's solution, which contains the linear indices of the corners of the cubes. Below I've done it with only one cube so the matrices aren't grossly large and we can see them here.

>> idx' %# transposed to save space in the printed output
ans =
 1     2     7     8    37    38    43    44 '

%# now get the subindex of each point in 3D
>> [sx sy sz] = ind2sub([6 6 3],idx);
%# calculate the "edge distance" between each corner (not euclidean distance)
>> dist = abs(bsxfun(@minus,sx,sx')) +...
          abs(bsxfun(@minus,sy,sy')) +...
          abs(bsxfun(@minus,sz,sz'))
dist =

 0     1     1     2     1     2     2     3
 1     0     2     1     2     1     3     2
 1     2     0     1     2     3     1     2
 2     1     1     0     3     2     2     1
 1     2     2     3     0     1     1     2
 2     1     3     2     1     0     2     1
 2     3     1     2     1     2     0     1
 3     2     2     1     2     1     1     0 '

%# find row and column index of points one edge apart
>> [r c]=find(triu(dist==1)); %# triu means don't duplicate edges

%# create 'p' matrix like in Amro's answer
>> p = [idx(r) idx(c)]'
p =
 1     1     2     7     1     2    37     7    37     8    38    43
 2     7     8     8    37    38    38    43    43    44    44    44

To visualize, you can plot exactly like in Amro's answer.

tmpearce
  • 12,523
  • 4
  • 42
  • 60
  • this actually reminds me of a [previous post](http://stackoverflow.com/a/3284384/97160) where we construct an adjacency matrix in a similar manner... I incorporated your solution into mine +1 – Amro Jun 18 '12 at 22:41
0

Is this what you need?

b=logical(b);                                  
scatter3(x(b(:)),y(b(:)),z(b(:)),90,'filled')

enter image description here

Oli
  • 15,935
  • 7
  • 50
  • 66
  • This looks really cool. what I need is being able to connect each of the 8 corner-points of these cubes so that they form two cubic lattices. I hope this explanation is clear. Cheers, – regex99 Jun 16 '12 at 14:16
  • Could you explain a little bit more why you want to do that? Maybe that will help to answer your question. – Oli Jun 16 '12 at 14:21
  • I need to track the change in the shape of the cubic lattices as the whole structure goes under some load. for that reason, I need to have the data-points connected. This helps a lot visually to see how it has changed in shape,e.g., if it has sheared etc... – regex99 Jun 16 '12 at 14:32
  • @Oli: maybe replace `boolean` with `logical` instead (which is a core MATLAB function, the other requires Simulink) – Amro Jun 16 '12 at 23:24