I need to extract the isoline coordinates of a 4D variable from a 3D surface defined using a triangulated mesh in MATLAB. I need the isoline coordinates to be a ordered in such a manner that if they were followed in order they would trace the path i.e. the order of the points a 3D printer would follow.
I have found a function that can calculate the coordinates of these isolines (see Isoline function here) but the problem is this function does not consider the isolines to be joined in the correct order and is instead a series of 2 points separated by a Nan value. This makes this function only suitable for visualisation purposes and not the path to follow.
Here is a MWE of the problem of a simplified problem, the surface I'm applying it too is much more complex and I cannot share it. Where x, y and z are nodes, with TRI providing the element connectivity list and v is the variable of which I want the isolines extracted from and is not equal to z.
If anyone has any idea on either.....
A function to extract isoline values in the correct order for a 3D tri mesh.
How to sort the data given by the function Isoline so that they are in the correct order.
.... it would be very much appreciated.
Here is the MWE,
% Create coordinates
[x y] = meshgrid( -10:0.5:10, -10:0.5:10 );
z = (x.^2 + y.^2)/20; % Z height
v = x+y; % 4th dimension value
% Reshape coordinates into list to be converted to tri mesh
x = reshape(x,[],1); y = reshape(y,[],1); z = reshape(z,[],1); v = reshape(v,[],1);
TRI = delaunay(x,y); % Convertion to a tri mesh
% This function calculates the isoline coordinates
[xTows, yTows, zTows] = IsoLine( {TRI,[x, y, z]}, v, -18:2:18);
% Plotting
figure(1); clf(1)
subplot(1,2,1)
trisurf(TRI,x,y,z,v)
hold on
for i = 1:size(xTows,1)
plot3( xTows{i,1}, yTows{i,1}, zTows{i,1}, '-k')
end
hold off
shading interp
xlabel('x'); ylabel('y'); zlabel('z'); title('Isolines'), axis equal
%% This section is solely to show that the isolines are not in order
for i = 1:size(xTows,1)
% Arranging data into colums and getting rid of Nans that appear
xb = xTows{i,1}; yb = yTows{i,1}; zb = zTows{i,1};
xb = reshape(xb, 3, [])'; xb(:,3) = [];
yb = reshape(yb, 3, [])'; yb(:,3) = [];
zb = reshape(zb, 3, [])'; zb(:,3) = [];
subplot(1,2,2)
trisurf(TRI,x,y,z,v)
shading interp
view(2)
xlabel('x'); ylabel('y'); zlabel('z'); title('Plotting Isolines in Order')
axis equal; axis tight; hold on
for i = 1:size(xb,1)
plot3( [xb(i,1) xb(i,2)], [yb(i,1) yb(i,2)], [zb(i,1) zb(i,2)], '-k')
drawnow
end
end
and here is the function Isoline, which I have slightly adpated.
function [xTows, yTows, zTows] = IsoLine(Surf,F,V,Col)
if length(Surf)==3 % convert mesh to triangulation
P = [Surf{1}(:) Surf{2}(:) Surf{3}(:)];
Surf{1}(end,:) = 1i;
Surf{1}(:,end) = 1i;
i = find(~imag(Surf{1}(:)));
n = size(Surf{1},1);
T = [i i+1 i+n; i+1 i+n+1 i+n];
else
T = Surf{1};
P = Surf{2};
end
f = F(T(:));
if nargin==2
V = linspace(min(f),max(f),22);
V = V(2:end-1);
elseif numel(V)==1
V = linspace(min(f),max(f),V+2);
V = V(2:end-1);
end
if nargin<4
Col = 'k';
end
H = NaN + V(:);
q = [1:3 1:3];
% -------------------------------------------------------------------------
% Loop over iso-values ----------------------------------------------------
xTows = [];
yTows = [];
zTows = [];
for k = 1:numel(V)
R = {[],[]};
G = F(T) - V(k);
C = 1./(1-G./G(:,[2 3 1]));
f = unique(T(~isfinite(C))); % remove degeneracies by random perturbation
F(f) = F(f).*(1+1e-12*rand(size(F(f)))) + 1e-12*rand(size(F(f)));
G = F(T) - V(k);
C = 1./(1-G./G(:,[2 3 1]));
C(C<0|C>1) = -1;
% process active triangles
for i = 1:3
f = any(C>=0,2) & C(:,i)<0;
for j = i+1:i+2
w = C(f,q([j j j]));
R{j-i} = [R{j-i}; w.*P(T(f,q(j)),:)+(1-w).*P(T(f,q(j+1)),:)];
end
end
% define isoline
for i = 1:3
X{i} = [R{1}(:,i) R{2}(:,i) nan+R{1}(:,i)]';
% X{i} = [R{1}(:,i) R{2}(:,i)]'; % Changed by Matt
X{i} = X{i}(:)';
end
% plot isoline
if ~isempty(R{1})
% hold on
% H(k) = plot3(X{1},X{2},X{3},Col);
% Added by M.Thomas
xTows{k,1} = X{1};
yTows{k,1} = X{2};
zTows{k,1} = X{3};
end
end
What you will notice is that the isolines (xTows, yTows and zTows) are not in order there "jump around" when plotted sequentially. I need to sort the tows so that they give a smooth plot in order.
Related
I made two 3D plots on the same axis. now I desire to give them different colors for easy identification. How do I do this coloring? The MATLAB code is shown below.
tic
Nx = 50;
Ny = 50;
x = linspace(0,1,Nx);
y = linspace(0,0.5,Ny);
[X,Y] = meshgrid(x,y);
[M,N] = size(X);
for m=1:M
for n=1:N
%get x,y coordinate
x_mn = X(m,n);
y_mn = Y(m,n);
%%% X=D2 and Y=D1
%Check if x_mn and y_mn satisfy requirement
if(x_mn >= y_mn)
%evaluate function 1
Z(m,n) = (x_mn^2 - 2*x_mn*y_mn + y_mn^2);
Z_1(m,n) = (x_mn^2);
elseif(x_mn < y_mn)
%evaluate function 2
Z(m,n) = 0;
Z_1(m,n) = (x_mn^2);
%% Z(m,n) = 2*(2*x_mn*y_mn + y_mn - y_mn^2 - 2*x_mn);
else
Z(m,n) = 0;
end
end
end
%Plot the surface
figure
surf(X,Y,Z) %first plot
surfc(X,Y,Z)
hold on
surf(X,Y,Z_1) %second plot
xlabel('Dm');
ylabel('D');
zlabel('pR');
grid on
shading interp
toc
disp('DONE!')
How can I create two differently colored surfaces?
figure
surf(X,Y,Z) %first plot
surfc(X,Y,Z)
hold on
surf(X,Y,Z_1)
Your surfc() call actually overwrites your surf() call, is this intended?
As to your colour: the documentation is a marvellous thing:
surfc(X,Y,Z,C) additionally specifies the surface color.
In other words: just specify the colour as you want it. C needs to be a matrix of size(Z) with the desired colours, i.e. set all of them equal to create an monocoloured surface:
x = 1:100;
y = 1:100;
z = rand(100);
figure;
surfc(x,y,z,ones(size(z)))
hold on
surfc(x,y,z+6,ones(size(z))+4)
Results in (MATLAB R2007b, but the syntax is the same nowadays)
I would like to generate surface of revolution from bezier curve. I have made bezier curve in MATLAB but beyond this point I am stuck and do not know how to proceed. Please help.
Below is the code that I have made.
clc
clear
close all
% Name : Savla Jinesh Shantilal
% Bits ID : 2021HT30609
%% Define inout parameters
B = [1,1; 2,3; 4,3; 3,1]; % Creating matrix for polygon vertices
[r,s] = size(B); % getting size of matrix in terms of rows and columns
n = r-1; % n+1 represents number of vertices of the polygon
np = 20; % represents number of equi-distance points on the bezier curve
t = linspace(0,1,np);
%% Plot polygon
for k = 1:n
plot([B(k,1),B(k+1,1)], [B(k,2),B(k+1,2)], 'r', 'LineWidth', 2)
hold on
grid on
end
%% Generate the points on the bezier curve
for j = 1:np
P = [0,0];
for i = 0:n
M(i+1) = (factorial(n)/(factorial(i)*factorial(n-i)))*((t(j))^i)*((1-t(j))^(n-i));
P = P + B(i+1,:)*M(i+1);
end
Q(j,:) = P;
end
%% Plot the bezier curve from the obtained points
for l = 1:np-1
plot([Q(l,1),Q(l+1,1)],[Q(l,2),Q(l+1,2)], '-- b', 'LineWidth', 2);
hold on
end
Usually one can use the built-in cylinder function for monotonically increasing x-values. Here, the bezier curve has non monotonic values from max(x) so we break it to two parts to parameterize it, and then add an angle rotation.
% first define the x and y coordinate from your Q info:
xx = Q(:,1);
yy = Q(:,2);
N = 1e2;
[~, id] = max(xx); % the position where we split
t = linspace(xx(1),xx(id),N);
% Parameterize the function:
t = linspace(0,2*(xx(id)-xx(1)),N);
x = zeros(1, N);
L = t <= ( xx(id)-xx(1) ); % the "Left" side of the curve
x(L) = t(L)+xx(1);
x(~L) = flip(x(L));
%define the r value
r = x;
r(L) = interp1(xx(1:id) ,yy(1:id) , x(L) ); % Left side
r(~L) = interp1(xx(id:end),yy(id:end), x(~L)); % right side (not left)
% define your x values
x = repmat(x', [1 N]);
% define the theta that will perform the rotation
theta = linspace(0,2*pi, N);
% initialize values for y and z
y = zeros(N);
z = zeros(N);
% calculate the y and z values
for i=1:N
y(i,:) = r(i) *cos(theta);
z(i,:) = r(i) *sin(theta);
end
%plot the surface of revolution and the original curve
s = surf(x,y,z);
alpha 0.4
hold on
plot(xx,yy,'k','LineWidth',3)
I am currently trying to compute Origami structures on Matlab, and I am looking for a method to bend my crease patterns on a conical surface, in the same way that this answer : Bending a plane into a closed surface/cylinder into a cylinder.
How can I do this, please ?
Cheers,
You should just have to map an (x,y) coordinate into a 3D coordinate for a conical surface. So to do this, it is important to parametrize a conical surface as a function of two variables. Thus:
( r, theta, z ) = ( a * z + c, theta, z ) for some a,c you define
Then you just need to create a relationship between (x,y) and (theta,z) so you can find a given (theta,z) as a function of (x,y). Then it comes down to a simple set of for loops to iterate through the (x,y) points to find the mapped coordinates on the conical surface.
=== Edit ===
So I wrote some codes to illustrate how simple the mapping can be. First, here are some images. The first is the planar mesh that will be mapped, and the second image is the result.
% This is file: gen_mesh.m
function [x, y, tri_mesh] = gen_mesh()
% Initialize output coordinates of points that make up the mesh
y = [];
x = [];
% Initialize mesh
tri_mesh= [];
% Number of points in the x dimension
Nx = 5;
% Number of points in the y dimension
Ny = 17;
% For this mesh, make each row have a slightly
% different number of points
x1 = linspace(0,1,Nx);
dx = x1(2)-x1(1);
x2 = linspace(dx/2, 1-dx/2, Nx-1);
% Create the array with the y values
y1 = linspace(0,1,Ny);
%% Generate the associated (x,y) pairs
for iy = 1:Ny
if( mod(iy,2) == 0 )
y = [y, ones(1,Nx-1)*y1(iy)];
x = [x, x2];
else
y = [y, ones(1,Nx)*y1(iy)];
x = [x, x1];
end
end
%% Generate the Mesh of triangles
% Make sure that the mesh wraps to each
% opposite x bound. This is to make sure that
% the cyclic nature of the conical surface
% doesnt mess up the look of the mesh
count = 1;
curr = 1;
ol = [];
el = [];
for iy = 1:Ny
if( mod(iy, 2) == 0 )
el.x = x(curr:curr+(Nx-2));
el.y = y(curr:curr+(Nx-2));
else
ol.x = x(curr:curr + Nx - 1);
ol.y = y(curr:curr + Nx - 1);
end
if( iy ~= 1 )
for i = 2:Nx
tri_mesh(count).x = [ol.x(i), el.x(i-1), ol.x(i-1)];
tri_mesh(count).y = [ol.y(i), el.y(i-1), ol.y(i-1)];
count = count + 1;
end
for i = 2:(Nx-1)
tri_mesh(count).x = [el.x(i), ol.x(i), el.x(i-1)];
tri_mesh(count).y = [el.y(i), ol.y(i), el.y(i-1)];
count = count + 1;
end
tri_mesh(count).x = [el.x(1), ol.x(1), el.x(end)];
tri_mesh(count).y = [el.y(1), ol.y(1), el.y(end)];
count = count + 1;
end
if( mod(iy, 2) == 0 )
curr = curr + (Nx-1);
else
curr = curr + Nx;
end
end
end
% This is file: map_2D_to_3DCone.m
function [xh, yh, z] = map_2D_to_3DCone( x, x_rng, y, y_rng )
% x: an array of x values part of a planar coordinate
%
% x_rng: the smallest and largest possible x values in the planar domain
% -> x_rng = [min_x, max_x]
%
% y: an array of y values part of a planar coordinate
%
% y_rng: the smallest and largest possible y values in the planar domain
% -> y_rng = [min_y, max_y]
% The bottom z (height) value
zb = 0;
% The top z value
zt = 1;
% The radius value at z = zb
rb = 3;
% The radius value at z = zt
rt = 1;
%% Obtain the Conical Surface 3D coordinates
% Find z as a function of y in the planar domain
% This mapping is a simple 1-D Lagrange interpolation
z = (zt*( y - y_rng(1) ) - zb*( y - y_rng(2) ))/(diff(y_rng));
% Find the parametrized angle as a function of x
% using a 1D Lagrange interpolation
theta = 2*pi*( x - x_rng(1) )/(diff(x_rng));
% Find the radius as a function of z using
% a simple 1D legrange interpolation
r = ( rt*(z - zb) - rb*(z - zt) )/( zt - zb );
% Find the absolute x and y components of the
% 3D conical coordinates
xh = r.*cos(theta);
yh = r.*sin(theta);
end
% This is in file: PlaneMesh_to_ConicalMesh.m
function mesh3d = PlaneMesh_to_ConicalMesh( mesh2d )
% Generate the 3D version of each planar triangle, based
% on the mapping function that takes an (x,y) planar
% coordinate and maps it onto a conical surface
N = length(mesh2d);
mesh3d(N).x = [];
mesh3d(N).y = [];
mesh3d(N).z = [];
for i = 1:N
[xh, yh, z] = map_2D_to_3DCone( mesh2d(i).x, [0,1], mesh2d(i).y, [0,1] );
mesh3d(i).x = xh;
mesh3d(i).y = yh;
mesh3d(i).z = z;
end
end
% This is in file: gen3D_MappedCone.m
% Generate the 3D object
close all
% Generate a 2D planar mesh to morph onto
% a conical surface
[x, y, mesh2d] = gen_mesh();
% Map the 2D mesh into a 3D one based on the
% planar to conical surface mapping
mesh3d = PlaneMesh_to_ConicalMesh( mesh2d );
% Obtain the number of triangles making up
% the mesh
N = length(mesh3d);
% Define a color mapping function for the sake
% of visualizing the mapping
color_map = #(x) [1, 0, 0].*(1-x) + [0, 0, 1].*x;
% Create the first image based on the planar
% mesh
figure(1)
hold on
for i = 1:N
color = color_map( (i-1)/(N-1) );
h = fill( mesh2d(i).x,mesh2d(i).y, color );
set(h, 'facealpha',0.9)
end
axis([0,1,0,1])
% Create the next figure showing the 3D mesh
% based on the planar to conical surface transformation
figure(2)
hold on
for i = 1:N
color = color_map( (i-1)/(N-1) );
h = fill3(mesh3d(i).x,mesh3d(i).y,mesh3d(i).z, color);
set(h, 'facealpha',0.9)
end
grid on
hold off
This is a continuation from the question already posted here. I used the method that #Andrey suggested. But there seems to be a limitation. the set(handle, 'XData', x) command seems to work as long as x is a vector. what if x is a matrix?
Let me explain with an example.
Say we want to draw 3 rectangles whose vertices are given by the matrices x_vals (5,3 matrix) and y_vals (5,3 matrix). The command that will be used to plot is simply plot(x,y).
Now, we want to update the above plot. This time we want to draw 4 rectangles. whose vertices are present in the matrices x_new(5,4 matrix) and y_new (5,4 matrix) that we obtain after some calculations. Now using the command set(handle, 'XData', x, 'YData', y) after updating x and y with new values results in an error that states
Error using set
Value must be a column or row vector
Any way to solve this problem?
function [] = visualizeXYZ_struct_v3(super_struct, start_frame, end_frame)
% create first instance
no_objs = length(super_struct(1).result);
x = zeros(1,3000);
y = zeros(1,3000);
box_x = zeros(5, no_objs);
box_y = zeros(5, no_objs);
fp = 1;
% cascade values across structures in a frame so it can be plot at once;
for i = 1:1:no_objs
XYZ = super_struct(1).result(i).point_xyz;
[r,~] = size(XYZ);
x(fp:fp+r-1) = XYZ(:,1);
y(fp:fp+r-1) = XYZ(:,2);
% z(fp:fp+r-1) = xyz):,3);
fp = fp + r;
c = super_struct(1).result(i).box;
box_x(:,i) = c(:,1);
box_y(:,i) = c(:,2);
end
x(fp:end) = [];
y(fp:end) = [];
fig = figure('position', [50 50 1280 720]);
hScatter = scatter(x,y,1);
hold all
hPlot = plot(box_x,box_y,'r');
axis([-10000, 10000, -10000, 10000])
xlabel('X axis');
ylabel('Y axis');
hold off
grid off
title('Filtered Frame');
tic
for num = start_frame:1:end_frame
no_objs = length(super_struct(num).result);
x = zeros(1,3000);
y = zeros(1,3000);
box_x = zeros(5, no_objs);
box_y = zeros(5, no_objs);
fp = 1;
% cascade values accross structures in a frame so it can be plot at once;
for i = 1:1:no_objs
XYZ = super_struct(num).result(i).point_xyz;
[r,~] = size(XYZ);
x(fp:fp+r-1) = XYZ(:,1);
y(fp:fp+r-1) = XYZ(:,2);
fp = fp + r;
c = super_struct(num).result(i).box;
box_x(:,i) = c(:,1);
box_y(:,i) = c(:,2);
end
x(fp:end) = [];
y(fp:end) = [];
set(hScatter, 'XData', x, 'YData', y);
set(hPlot, 'XData', box_x, 'YData', box_y); % This is where the error occurs
end
toc
end
Each line on the plot has its own XData and YData properties, and each can be set to a vector individually. See the reference. I am not at a Matlab console right now, but as I recall...
kidnum = 1
h_axis = gca % current axis - lines are children of the axis
kids = get(h_axis,'Children')
for kid = kids
kid_type = get(kid,'type')
if kid_type == 'line'
set(kid,'XData',x_new(:,kidnum))
set(kid,'YData',y_new(:,kidnum))
kidnum = kidnum+1
end
end
Hope that helps! See also the overall reference to graphics objects and properties.
To add a series, say
hold on % so each "plot" won't touch the lines that are already there
plot(x_new(:,end), y_new(:,end)) % or whatever parameters you want to plot
After that, the new series will be a child of h_axis and can be modified.
I want to find weight matrix for Algebraic reconstruction method. For this I have to find the line intersection with grid. I can find direct line intersection with line but I have to store the intersected line segment grid number wise. So suppose if in grid first square don't intersect with grid then put zero on first element of weight matrix.
Here code which I tried for line intersection:
ak = 3:6
aka = 3:6
x = zeros(size(aka))
y = zeros(size(ak))
for k = 1:length(ak)
line([ak(1) ak(end)], [aka(k) aka(k)],'color','r')
end
% Vertical grid
for k = 1:length(aka)
line([ak(k) ak(k)], [aka(1) aka(end)],'color','r')
end
hold on;
X =[0 15.5]
Y = [2.5 8.5]
m = (Y(2)-Y(1))/(X(2)-X(1)) ;
c = 2.5 ;
plot(X,Y)
axis([0 10 0 10])
axis square
% plotting y intercept
for i = 1:4
y(i) = m * ak(i) + c
if y(i)<2 || y(i)>6
y(i) = 0
end
end
% plotting x intercept
for i = 1:4
x(i) = (y(i) - c)/m
if x(i)<2 || x(i)>6
x(i) = 0
end
end
z = [x' y']
I have a line, defined by the parameters m, h, where y = m*x + h This line goes across a grid (i.e. pixels).
For each square (a, b) of the grid (i.e. the square [a, a+1]x[b, b+1]), I want to determine if the given line crosses this square or not, and if so, what is the length of the segment in the square so that I can construct the weight matrix which is essential for algebraic reconstruction method.
Here's a nice way to intersect a line with grid of rectangles and getting the lengths of each of the intersection segments: I used the line line intersection from the pseudo code in the third answer from this link
% create some line form the equation y=mx+h
m = 0.5; h = 0.2;
x = -2:0.01:2;
y = m*x+h;
% create a grid on the range [-1,1]
[X,Y] = meshgrid(linspace(-1,1,10),linspace(-1,1,10));
% create a quad mesh on this range
fvc = surf2patch(X,Y,zeros(size(X)));
% extract topology
v = fvc.vertices(:,[1,2]);
f = fvc.faces;
% plot the grid and the line
patch(fvc,'EdgeColor','g','FaceColor','w'); hold on;
plot(x,y);
% use line line intersection from the link
DC = [f(:,[1,2]);f(:,[2,3]);f(:,[3,4]);f(:,[4,1])];
D = v(DC(:,1),:);
C = v(DC(:,2),:);
A = repmat([x(1),y(1)],size(DC,1),1);
B = repmat([x(end),y(end)],size(DC,1),1);
E = A-B;
F = D-C;
P = [-E(:,2),E(:,1)];
h = dot(A-C,P,2)./dot(F,P,2);
% calc intersections
idx = (0<=h & h<=1);
intersections = C(idx,:)+F(idx,:).*repmat(h(idx),1,2);
intersections = uniquetol(intersections,1e-8,'ByRows',true);
% sort by x axis values
[~,ii] = sort(intersections(:,1));
intersections = intersections(ii,:);
scatter(intersections(:,1),intersections(:,2));
% get segments lengths
directions = diff(intersections);
lengths = sqrt(sum(directions.^2,2));
directions = directions./repmat(sqrt(sum(directions.^2,2)),1,2);
directions = directions.*repmat(lengths,1,2);
quiver(intersections(1:end-1,1),intersections(1:end-1,2),directions(:,1),directions(:,2),'AutoScale','off','Color','k');
This is the result (the lengths of the arrows in the image are the segment lengths)