Remove internal edges in 3D MATLAB plot using patch function - matlab

I am plotting a 3D object, say a cube, in MATLAB.
Node = [0 0 0; 1 0 0; 1 1 0; 0 1 0; 0 0 1; 1 0 1; 1 1 1; 0 1 1];
Elem = cell(1); Elem{1} = 1:8;
figure
for elm = 1:size(Elem,1)
X = Node(Elem{elm},:); K = convhulln(X); hold on;
patch('Faces',K,'Vertices',X,'FaceColor',rand(1,3),'FaceAlpha',1.0);
end
view(3); grid off; axis equal; cameramenu; axis off;
In the plot, how do I remove the internal diagonal lines? The plot should just show edges of cube. I am looking for a general solution which is applicable to any polyhedron.

the output of K=convhulln(X); is causing this, because convex hull will have triangular facets... (that's the default).
if instead you'd define K to be:
K= [1 2 3 4; ...
2 6 7 3; ...
4 3 7 8; ...
1 5 8 4; ...
1 2 6 5; ...
5 6 7 8];
You'll get it right.
Another option is to use geom3D from the FEX.

Related

Generate mesh and refine mesh of triangles

I need to find a way to mesh triangles then to refine using refine.
My vertices of my original triangles are stored inside a matrix of size nb_points * 2.
My faces are stored in a nb_faces * 3 matrix.
The data for each face is stored in a nb_face * 1 matrix.
The meshing is done by diving the area using the middles of the segments of the triangles.
Example :
Origin :
vertices = [0 1 ;
2 3 ;
4 1 ;
4 5];
faces = [1 2 3;
2 3 4];
data = [1 2];
Result expected after meshing :
vertices = [0 1;
2 3;
4 1;
4 5;
1 2;
3 2;
2 1;
3 4;
4 3];
faces = [1 5 7;
2 5 6;
5 6 7;
7 6 3;
2 6 8;
6 8 9;
6 9 3;
8 4 9];
data = [1 1 1 1 2 2 2 2];
I am displaying using :
FV.Vertices = vertices;
FV.Faces = faces;
FV.FaceVertexCData = data;
figure; hold on; axis equal; grid on;
patch(FV,'FaceColor','flat');
Precision :
I do not want to use the following functions which gives way too many vertices and faces :
generateMesh()
refinemesh()
The data are temperatures since this is a simulation of heat transfer.
With a for loop it can be done quite easily, here is one solution:
% Dummy data
vertices = [0 1 ;
2 3 ;
4 1 ;
4 5];
faces = [1 2 3;
2 3 4];
data = [1 2];
% Number of vertices
vnum = size(vertices,1);
% new faces empty vector
nfaces = [];
% triangular shift
tshift = [2,-1,-1].';
% Run the algorithm
for ii = 1:size(faces,1)
% For each triangle get the 3 pairs of vertices
nsk = [faces(ii,1), faces(ii,2);faces(ii,2), faces(ii,3);faces(ii,3), faces(ii,1)];
% Compute the center of each pair of vertices
cmiddle = (vertices(nsk(:,1),:)+vertices(nsk(:,2),:))/2
% Compute the new faces
nfaces = [nfaces;[nsk(:,1),vnum+(ii*3-3+[1:3].'),vnum+(ii*3-3+[1:3].')+tshift];[(vnum+(ii*3-2)):(vnum+(ii*3))]]
% Add the new vertices
vertices = [vertices;cmiddle];
end
% Delete the duplicate vertices
[vertices,~,ind] = unique(vertices,'rows');
faces = ind(nfaces);
% Plot
figure; hold on; axis equal; grid on;
patch('Faces',faces,'Vertices',vertices,'FaceVertexCData',kron(data,ones(1,4)).','FaceColor','flat')
colorbar
If you find a way to generate the nsk vector without for loop you could even get rid of the loop. This code will only work on triangle, but it can be adapted if needed.
Result:
You can repeat the operation:

MATLAB calculate area of shape on plot

I Create a plot using imagesc. The X/Y axis are longitude and latitude respectively. The Z values are the intensity of the images for the image shown below. What I'd like to be able to do is calculate the area in each of the polygons shown. Can anybody recommend a straightforward (or any) method in accomplishing this?
EDIT
Forgot to include image.
Below is a toy example. It hinges on the assumption that the Z values are different inside the objects from outside (here: not 0). Also here I assume a straight divider at column 4, but the same principle (applying a mask) can be applied with other boundaries. This also assumes that the values are equidistant along x and y axes, but the question does not state the opposite. If that is not the case, a little more work using bsxfun is needed.
A = [0 2 0 0 0 2 0
3 5 3 0 1 4 0
1 4 0 0 3 2 3
2 3 0 0 0 4 2
0 2 6 0 1 6 1
0 3 0 0 2 3 0
0 0 0 0 0 0 0];
area_per_pix = 0.5; % or whatever
% plot it
cm = parula(10);
cm(1, :) = [1 1 1];
figure(1);
clf
imagesc(A);
colormap(cm);
% divider
dv_idx = 4;
left_object = A(:, 1:(dv_idx-1));
left_mask = left_object > 0; % threshold object
num_pix_left = sum(left_mask(:));
% right object, different method
right_mask = repmat((1:size(A, 2)) > dv_idx, size(A, 1), 1);
right_mask = (A > 0) & right_mask;
num_pix_right = sum(right_mask(:));
fprintf('The left object is %.2f units large, the right one %.2f units.\n', ...
num_pix_left * area_per_pix, num_pix_right * area_per_pix);
This might be helpful: http://se.mathworks.com/matlabcentral/answers/35501-surface-area-from-a-z-matrix
He has not used imagesc, but it's a similar problem.

Why does my 3 axes system coordinate orientation change x with y values?

I am using Matlab and Euler Angles in order to reorient a 3axes coordinate system. Specifically,
Rz = [cos(ψ) sin(ψ) 0;-sin(ψ) cos(ψ) 0;0 0 1];
Ry = [cos(φ) 0 -sin(φ);0 1 0;sin(φ) 0 cos(φ)];
Rx = [1 0 0;0 cos(θ) -sin(θ);0 sin(θ) cos(θ)];
Rtotal = Rz*Ry*Rz
Then I loop through my old system coordinates (x,y,z) and make a vector coord_old. Then I get the reoriented system with (xn,yn,zn)
for i=1:size(num,1)
coord_old = [x(i,1);y(i,1);z(i,1)];
coord_new = Rtotal*coord_old;
xn(i,1) = coord_new(1,1);
yn(i,1) = coord_new(2,1);
zn(i,1) = coord_new(3,1);
end
My issue is that when θ,φ,ψ≃0 then x->-y and y->x and when θ,φ≃0 and ψ=90 then x and y will not rotate! That means that when x,y should rotate they don't and when they shouldn't rotate they stay as they were!
--EDIT--
For example, when ψ=20.0871, φ=0.0580 and θ=0.0088 I get these results
See that x->-y and y->x while z doesn't change at all!
Any thoughts?
Ok, I see two main problems here:
Rtotal = Rz*Ry*Rz is probably not what you want since Rz is multiplied twice. I think you mean Rtotal = Rz*Ry*Rx.
Your rotation matrix seems to be incorrect. Check this Wikipedia artice to get the correct signs.
Here a corrected rotation matrix:
Rz = [cos(psi) -sin(psi) 0; sin(psi) cos(psi) 0; 0 0 1];
Ry = [cos(phi) 0 sin(phi); 0 1 0; -sin(phi) 0 cos(phi)];
Rx = [1 0 0; 0 cos(theta) -sin(theta); 0 sin(theta) cos(theta)];
Rtotal = Rz*Ry*Rx;
With this matrix I get the correct results:
x=1; y=2; z=3;
psi=0; phi=0; theta=0;
[xn,yn,zn] >> 1 2 3
x=1; y=2; z=3;
psi=90/180*pi; phi=0; theta=0;
[xn,yn,zn] >> -2 1 3
And here a full graphical example of a cube in 3d-space:
% Create cube (not in origin)
DVert = [0 0 0; 0 1 0; 1 1 0; 1 0 0 ; ...
0 0 1; 0 1 1; 1 1 1; 1 0 1];
DSide = [1 2 3 4; 2 6 7 3; 4 3 7 8; ...
1 5 8 4; 1 2 6 5; 5 6 7 8];
DCol = [0 0 1; 0 0.33 1; 0 0.66 1; ...
0 1 0.33; 0 1 0.66; 0 1 1];
% Rotation angles
psi = 20 /180*pi; % Z
phi = 45 /180*pi; % Y
theta = 0 /180*pi; % X
% Rotation matrix
Rz = [cos(psi) -sin(psi) 0; sin(psi) cos(psi) 0; 0 0 1];
Ry = [cos(phi) 0 sin(phi); 0 1 0; -sin(phi) 0 cos(phi)];
Rx = [1 0 0; 0 cos(theta) -sin(theta); 0 sin(theta) cos(theta)];
Rtotal = Rz*Ry*Rz;
% Apply rotation
DVertNew = Rtotal * DVert';
% Plot cubes
figure;
patch('Faces',DSide,'Vertices',DVert,'FaceColor','flat','FaceVertexCData',DCol);
patch('Faces',DSide,'Vertices',DVertNew','FaceColor','flat','FaceVertexCData',DCol);
% Customize view
grid on;
axis equal;
view(30,30);
When I use your code and insert 0 for all angles, I get Rtotal:
Rtotal =
1 0 0
0 1 0
0 0 1
This is the identity matrix and will not change your values.
You have an error in your matrix multiplication. I think you should multiply: Rtotal*coord_old. I think you are missing the _old. depending on what is in you coordvariable, this may be the bug.
When I run:
for i=1:size(1,1)
coord_old = [1;2;3];
coord_new = Rtotal*coord_old;
xn(i,1) = coord_new(1,1);
yn(i,1) = coord_new(2,1);
zn(i,1) = coord_new(3,1);
end
I get the correct result:
coord_new =
1
2
3
Thank you both #Steffen and #Matt. Unfortunately, my reputation is not high enough to vote Up your answers!
The problem was not with Rtotal as #Matt correctly stated. It should be as it was Rz*Ry*Rx. However, both your ideas helped me test my code with simple examples (5 sets of coordinates and right hand rule), and realize where my (amateur) mistake was.
I had forgotten I had erased parts of codes where I was expressing my angles to degrees... I should be using sind & cosd instead of sin and cos.

Set length of a column vector equal to the length of a submatrix

I am trying to use the convhull function in a loop and for that I need to split matrices into submatrices of different sizes. Here is the code I am using:
x1=data(:,5); % x centre location
y1=data(:,16); % y centre location
z1=phi*90; % phi angle value
n=300;
%Create regular grid across data space
[X,Y] = meshgrid(linspace(min(x1),max(x1),n), linspace(min(y1),max(y1),n));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% PLOT USING SCATTER - TRYING TO ISOLATE SOME REGIONS %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
c=z1>10 & z1 < 20;
c=c.*1;
j=1;
for i=1:length(z1)
if z1(i)< 20 && z1(i)> 10
c(i) = 1;
else
c(i)= 0;
end
end
C=[c c c];
C = ~C;
elementalLengthA = cellfun('length',regexp(sprintf('%i',all(C,2)),'1+','match'));
elementalStartA = regexp(sprintf('%i',all(C,2)),'1+','start');
result = cell(length(elementalLengthA),1);
for i = 1:length(elementalLengthA)
result(i) = {C(elementalStartA(i):elementalStartA(i)+elementalLengthA(i)-1,:)};
length(x1(i))=length(cell2mat(result(i)));
length(y1(i))=length(cell2mat(result(i)));
end
My for loop doens't work properly and I get this error: ??? Subscript indices must either be real positive integers or
logicals.
My matrix C is an nx3 matrix made of lines of 1 and 0. With the result(i) line I am splitting the C matrix into submatrices of 1. Let's say
c = [1 1 1;
0 0 0;
0 0 0;
1 1 1;
1 1 1;
1 1 1;
0 0 0;
1 1 1;
1 1 1;]
Then
>> cell2mat(result(1))
ans =
1 1 1
>> cell2mat(result(2))
ans =
1 1 1
1 1 1
1 1 1
>> cell2mat(result(3))
ans =
1 1 1
1 1 1
Now x1 and y1 are two vector column nx1. And I want to split them according to the length of C submatrices. so length(x1(1)) should be 1, length(x1(2))=3, length(x1(3))=2 and same for the y vector.
Is it possible to do that?
EDIT:
Just to make it more clear
For instance
x1 =
1
2
3
4
5
6
7
8
9
and
y1 =
2
4
6
8
10
12
14
16
18
I want to get this as an output:
x1(1)=[1], x1(2)=[4 5 6]' and x1(3)=[8 9]'
y1(1)=[2], y1(2)[8 10 12]' and y1(3)=[16 18]'
Thanks
Dorian

Edge Coordinates of a Cube

I have a matrix corresponding to 8 vortex of a cube,
CubeVortex = [3 3 0;
0 3 0;
0 3 3;
3 3 3;
0 0 3;
3 0 3;
3 0 0;
0 0 0];
Now I want to get the coordinates of all the edges divided in 3, like,
As you can see, there will be 12x2 = 24 coordinates.
It would be a little hard to write them.
Is there a way to calculate them from CubeVortex?
One way to do this:
Cube = [
3 3 0;
0 3 0;
0 3 3;
3 3 3;
0 0 3;
3 0 3;
3 0 0;
0 0 0];
% find edges by looking for all combinations of points on cube that
% differ by only one coordinate
sections_per_edge = 3;
weights = ((1:sections_per_edge-1) / sections_per_edge).';
edges = []; % indices into Cube
points = [];
n = size(Cube, 1);
for i = 1:n-1
pointA = Cube(i, :);
for j = i+1:n
pointB = Cube(j, :);
if nnz(pointA - pointB) == 1
edges = [edges; i, j];
% find points along edge as weighted average of point A and B
points = [points; weights * pointA + (1 - weights) * pointB];
end
end
end
% plot corners
plot3(Cube(:,1), Cube(:,2), Cube(:,3), '.r', 'markersize', 20)
hold on
% plot points along edges
plot3(points(:,1), points(:,2), points(:,3), '.b', 'markersize', 20)
% draw edges
line([Cube(edges(:,1), 1), Cube(edges(:,2), 1)].', ...
[Cube(edges(:,1), 2), Cube(edges(:,2), 2)].', ...
[Cube(edges(:,1), 3), Cube(edges(:,2), 3)].', 'color', 'k')
axis([-1,4,-1,4])
Result:
Increasing sections_per_edge to 10, you get
One approach could be this -
n = 3; %// number of IDs
m = 3; %// number of columns
combs = dec2base(0:(n+1)^m-1,n+1,m)-'0' %// form repeated combinations
out = c1(sum(ismember(combs,[1 2]),2)==1,:) %// combinations for intermediate points
You can make this generic for a N-point case and more efficient one, with this -
N = 3;
[x,y,z] = ndgrid(0:N,0:N,0:N)
combs = [z(:) y(:) x(:)]
out = combs(sum(combs~=0 & combs~=N,2)==1,:)
Thus, for your 3-point (0 to 3 that is) case, you would have -
out =
0 0 1
0 0 2
0 1 0
0 1 3
0 2 0
0 2 3
0 3 1
0 3 2
1 0 0
1 0 3
1 3 0
1 3 3
2 0 0
2 0 3
2 3 0
2 3 3
3 0 1
3 0 2
3 1 0
3 1 3
3 2 0
3 2 3
3 3 1
3 3 2
This should do it:
NewVortex=[];
for i=1:3
NewVortex=[CubeVortex*i/3;NewVortex];
end
NewVortex