Are repeating points in Delaunay possible? - scipy

I am using scipy.spatial.Delaunay to generate a triangulated mesh from a series of 3d points. Can I have repeating x,y data points that have differing z values?
I am noticing that right now it doesn't allow/adhere to this - see below example. Is there a setting that can allow this?
I wish to create a mesh that looks like a 'step', ie, like the below:
In order to achieve this mesh, there will be duplicate x,y points - 2 repeating points at (0.5,0) and also at (0.5,1).
The following produces an incorrect mesh:
points = np.array([[0,0], [0,1], [1,1], [1,0], [0.5,0], [0.5,0], [0.5,1], [0.5,1]])
z = np.array([0,0,1,1,0,1,0,1])
tri = Delaunay(points)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.plot_trisurf(points[:,0], points[:,1], z, triangles=tri.simplices, cmap=plt.cm.Spectral)
plt.show()
I can get it to work if I use different/non-repeating points:
points = np.array([[0,0], [0,1], [1,1], [1,0], [0.5,0], [0.51,0], [0.5,1], [0.51,1]])

Related

MATLAB - 3D volume fill based on inequalities

I have three variables x, y and z. I have inequalities of the form
x >= a, y>= b, z>=c, x+y>=d, y+z>=e, x+z>=f, x+y+z>=g
where a to g are positive numbers. On a 3D plot with axes x, y and z, this is an open volume. I would like to fill the open side (i.e. away from 0) shape with color and show it in a plot. What is the way to do this on MATLAB?
I attempted to use fill3 and a mesh but the result was not very good
[x,y,z] = meshgrid(0:0.01:2,0:0.01:2,0:0.01:2);
ineq = (x>=1)& (y>0.5)&(z>=0.25)&(x+y>1.25)&(y+z>0.6)&(x+z>1.1)&(x+y+z>1.6);
fill3(x(:),y(:),z(:), 'r')
box on
grid on
Using plot3 also was not very good. Is there any other way to generate a nice 3D figure on MATLAB?
Mathematica does this using RegionPlot3D. I was hoping for a similar resultant image.
First of all, be careful when using 3D meshes, the one you defined contains 8M+ points.
Assuming your shape is convex, you can use convhull and trisurf:
Not that the option 'Simplify' is set as true to reduce the number of elements accounted for in the convex hull.
[x,y,z] = meshgrid(0:0.1:2,0:0.1:2,0:0.1:2);
ineq = (x>=1)& (y>0.5)&(z>=0.25)&(x+y>1.25)&(y+z>0.6)&(x+z>1.1)&(x+y+z>1.6);
figure;
x_ineq = x(ineq);
y_ineq = y(ineq);
z_ineq = z(ineq);
id_cvhl = convhull(x_ineq,y_ineq,z_ineq,'Simplify',true);
trisurf(id_cvhl,x_ineq,y_ineq,z_ineq,'FaceColor','cyan','edgecolor','none')
xlim([0 2])
ylim([0 2])
zlim([0 2])
In case you want the result to look a bit more than RegionPlot3D, don't use Simplify, and plot the edges (Be careful not too have a mesh with too many points!).
id_cvhl = convhull(x_ineq,y_ineq,z_ineq);
trisurf(id_cvhl,x_ineq,y_ineq,z_ineq,'Facecolor','yellow')

Generating a curve around the point on a scatter plot

I am generating a scatter plot containing data from multiple sources, as displayed below.
I would like to be able to generate a curve surrounding an arbitrary query point and passing through points on scatter plot. Final goal is to calculate the area between the lines on the plot.
I have implemented solution using finding points with knnsearch in a circular fashion and then applying hampel filter to eliminate noise. In the example below, I have selected a point right about in the middle of the blue-shaded area. As you can see, the result is far from perfect, and I need more precision.
I am looking for something similar to boundary function, but to work from the inside of the point cloud, not from the outside.
Final goal is to calculate the area between the lines on the plot.
I would do it differently. Just take any two lines of the plot, calculate the area under the curves with some kind of numerical approximation (for example trapezoidal numerical integration), then subtract the areas and obtain the area between the lines.
Thank to idea in Trilarion's answer, I was able to come up with the better solution.
Note that I use notation for YZ plane instead of XY (to keep consistent with robot coordinate system).
Solution
Generate curves for each set of scatter data
% Scatter data is in iy and iz vectors.
curve = fit(iy, iz, 'smoothingspline', 'SmoothingParam', 0.5);
% Remove outliers.
fdata = feval(curve, iy);
I = abs(fdata - iz) > 0.5 * std(iz);
outliers = excludedata(iy, iz, 'indices', I);
% Final curve without outliers.
curve = fit(iy, iz, 'smoothingspline', 'Exclude', outliers, 'SmoothingParam', 0.5);
Plot curves and scatter data
% Color maps generated by MATLAB's colormap function.
h_curve = plot(curve);
set(h_curve, 'Color', color_map_light(i,:));
scatter(iy, iz, '.', 'MarkerFaceColor', color_map(i,:))
Let user provide an input by selecting points
User selects one point as a query point and two points for limits along Y axis. This is because some curves come close, but never intersect.
[cs_position.y, cs_position.z] = ginput(1);
[cs_area_limits, ~] = ginput(2);
if cs_area_limits(1) > cs_area_limits(2)
cs_area_limits = flipud(cs_area_limits);
end
plot_cross_section(cs_position);
Finally calculate and plot surface area
This section uses fantastic answer by Doresoom.
function [ ] = plot_cross_section(query_point)
%PLOT_CROSS_SECTION Calculates and plots cross-section area.
% query_point Query point.
% Find values on query point's Y on each of the curves.
z_values = cellfun(#(x, y) feval(x, y),...
curves, num2cell(ones(size(curves)) * query_point.y))
% Find which curves are right above and below the query point.
id_top = find(z_values >= query_point.z, 1, 'first')
id_bottom = find(z_values < query_point.z, 1, 'last')
if isempty(id_top) || isempty(id_bottom)
return
end
% Generate points along curves on the range over Y.
y_range = cs_area_limits(1):0.1:cs_area_limits(2);
z_top = feval(curves{id_top}, y_range).';
z_bottom = feval(curves{id_bottom}, y_range).';
% Plot area.
Y = [ y_range, fliplr(y_range) ];
Z = [ z_top, fliplr(z_bottom) ];
fill(Y, Z, 'b', 'LineStyle', 'none')
alpha 0.5
hold on
% Calculate area and show to user.
cs_area = polyarea(Y, Z);
area_string = sprintf('%.2f mm^2', cs_area);
text(0, -3, area_string, 'HorizontalAlignment', 'center')
end
Result

Undesirable results while interpolating

I'm trying to create a surface plot from (x,y,z) data on an irregular grid. The datasets are located along diagonal lines of positive gradient in the (x,y) plane. The method is illustrated below
xi = linspace (min(x), max(x), 1000);
yi = linspace (min(y), max(y), 1000);
zi = linspace (min(z), max(z), 400);
[XI YI]=meshgrid(xi,yi);
F = TriScatteredInterp (x,y,z);
Vi = F(XI,YI);
surf(Xi,Yi,Vi);
shading interp;
view(2)
An example result is shown below:
The data should be smooth in the x direction (so at y=860 there should be a single, continuous blue dip). I think the problem is that the interpolation is being carried out well along each dataset but poorly between them as the spacing between the datasets is far larger than between points within a dataset.
What would be the best way to deal with this?

How to create a 2d color map plot from x and y coordinates and a vector of values?

I'm trying to plot a 2 dimensional signal on a specific plane in a 3d model. I have the matrix:
xyzp (nx3)
that contains all the points which are closest to the plane (e.g. when the plane is in the z direction, all the z coordinates are fairly similar).
and I have a vector:
signal (nx1)
that contains a value for each point in xyzp.
when I use:
"surf([xyzp(:,[1,2]),signal)" or "mesh([xyzp(:,[1,2]),signal])"
The plot I get doesn't look at all like the intersection of the plane with the model from any angle (I expected "view(2)" to show the signal in the Z direction), so I assume I didn't use the plot function correctly.
Can anyone show me an example? For instance - A circle on an xy plane with some random signal indicated by color
surf and mesh can be used when the points form a rectangular grid on the xy plane.
In the general case (points are arbitrarily placed), you can use scatter3. For purposes of illustration, consider the following example xyzp and signal:
[x y] = ndgrid(-1:.01:1);
x = x+.3*y; %// example values which do not form a rectangular grid
z = x+y; %// example z as a function of x, y
xyzp = [x(:) y(:) z(:)];
signal = z(:)+x(:)-y(:); %// example values
Then
scatter3(xyzp(:,1), xyzp(:,2), xyzp(:,3), 1, signal, '.');
produces the following figure.
Since scatter3 plots each point separately, the picture is not as smooth as it would be with surf. But this seems hard to improve if the coordinates do not a have any "structure" (as surf requires) .

MATLAB - 3D Contour Plot (Hydrogen 2p Orbitals)

I've got the following code which works for plotting an x-y slice of the Hydrogen 2pz orbital:
%probability densities
pd_psi_210 = #(r,theta,phi) exp(-r).*(r.^2).*(cos(theta).^2)/(32*pi);
%configuring the range
[x y z] = meshgrid(-10:.1:10,-10:.1:10,-2:.1:2);
[THETA,PHI,R] = cart2sph(x,y,z);
%create array of probability density magnitudes
psi_210_vals = pd_psi_210(R,THETA,PHI);
%plotting
imagesc(psi_210_vals(:,:,1)); %x-y plane
I'd like to plot the 3d contour plot of the orbital. I've tried this (and it doesn't seem to get me what I wanted):
isosurface(psi_210_vals(:,:,:)); %3D contour
How can I get this to work?
You just have to specify the underlying grid, and the level you want. For example:
>> isosurface(-10:.1:10, -10:.1:10, -2:.1:2, psi_210_vals, 0.001);
>> axis equal