我用椭球函数绘制两个椭圆体,并使用旋转手柄。为了计算交叉点体积(即透镜)和椭圆体之间没有旋转的交叉体积的情况,我使用了球体交叉体积的分析代码。 但是我被卡住了,如果椭圆体处于某些旋转状态,我将如何计算它们之间的交叉体积(即,间距)。我还需要计算交叉点直径。镜头 椭球相对于沿z轴的最大轴具有不同的旋转角度。这是一个代码:
draw ellipsoid, [x,y,z] = ellipsoid(xc,yc,zc,xr,yr,zr,n);
(xc,yc,zc)=center; semi-axis lengths = (Fmax,Fmin,Fmin);n
X=[0,2]; two ellipsoid x coordinates i.e 0 is first ellipsoid
and 2 is second ellipsoid respectively
Y=[0,2]; two ellipsoid y coordinates
Z=[0,2]; two ellipsoid z coordinates;
ROTATIONANGLE=[90,30];
RMIN=[1,2]; two ellipsoid minor axis radius
RMAX=[3,5]; two ellipsoid major axis radius.
for a = 1:2 display ellipsoid
[x, y, z] = ellipsoid(X(a),Y(a),Z(a),RMIN(a),RMAX(a),3.25,30);
S = surfl(x, y, z);
rotate(S,[0,0,1],ROTATIONANGLE(a))
colormap copper
axis equal
xlabel('X')
ylabel('Y')`enter code here
zlabel('Z')
check the boolean condition
hold on
end
答案 0 :(得分:1)
据我所知,Matlab中没有这样的功能。我会使用Monte Carlo method来找到交叉点的近似体积。
您需要从某个框中生成随机点,并检查它们是否属于交叉点。你可以算出有多少分落入交叉点。知道了盒子的体积,落入交叉点的点数以及生成点的总数,就可以计算出所需的体积。
在图片上你可以看到交叉点内的红点:
找到一些足以解决问题的迭代非常重要。我通过计算一个小椭圆体的体积来验证该方法,这个椭球体放在一个较大的体内。在这种情况下,交点是一个小椭球本身,其体积可以通过分析找到。
您可以在下一张图片上看到相对误差作为迭代次数的函数:
以下是代码:
该代码仅适用于围绕Z轴的旋转
X=[0,2]; %two ellipsoid x coordinates
Y=[0,2]; %two ellipsoid y coordinates
Z=[0,2]; %two ellipsoid z coordinates
ROTATIONANGLE=[90,30];
AXIS_A=[1,2]; %two ellipsoid minor axis radius
AXIS_B=[3,5]; %two ellipsoid major axis radius
AXIS_C=[3.25,3.25]; %two ellipsoid major axis radius
ranges = zeros(3, 2);
do_plot = 1; %either plot ellipsoids or not
step_number = 100000;
for i = 1:2 %display ellipsoid
if (do_plot == 1)
[x, y, z] = ellipsoid(X(i),Y(i),Z(i),AXIS_A(i),AXIS_B(i),AXIS_C(i),20);
S = surf(x, y, z);
alpha(.1);
rotate(S,[0,0,1], ROTATIONANGLE(i), [X(i),Y(i),Z(i)]);
end
%calculate the ranges for the simulation box
ranges(1, 1) = min(ranges(1, 1), X(i) - max(AXIS_A(i), AXIS_B(i)) );
ranges(1, 2) = max(ranges(1, 2), X(i) + max(AXIS_A(i), AXIS_B(i)) );
ranges(2, 1) = min(ranges(2, 1), Y(i) - max(AXIS_A(i), AXIS_B(i)) );
ranges(2, 2) = max(ranges(2, 2), Y(i) + max(AXIS_A(i), AXIS_B(i)) );
ranges(3, 1) = min(ranges(3, 1), Z(i) - AXIS_C(i));
ranges(3, 2) = max(ranges(3, 2), Z(i) + AXIS_C(i));
if (do_plot == 1)
hold on;
end
end
counter = 0; %how many points targeted the intersection
for i = 1:step_number
R = rand(3, 1).*(ranges(:, 2) - ranges(:, 1)) + ranges(:, 1); %a random point
n = 1;
val = calc_ellipsoid( R(1), R(2), R(3), X(n),Y(n),Z(n),AXIS_A(n),AXIS_B(n),AXIS_C(n),ROTATIONANGLE(n)*pi/180);
if (val <= 1.0)
n = 2;
val = calc_ellipsoid( R(1), R(2), R(3), X(n),Y(n),Z(n),AXIS_A(n),AXIS_B(n),AXIS_C(n),ROTATIONANGLE(n)*pi/180);
if (val <= 1.0)
if (do_plot == 1)
plot3(R(1), R(2), R(3), 'or', 'MarkerSize', 1, 'MarkerFaceColor','r');
end
counter = counter + 1;
end
end
end
cube_vol = 1; %the volume of the simulation box
for i=1:3
cube_vol = cube_vol * (ranges(i, 2) - ranges(i, 1));
end
%approximated volume of the intersection
section_vol = cube_vol * counter / step_number;
display(['Cube volume: ', num2str(cube_vol)]);
display(['Targeted points: ', num2str(counter), ' from ', num2str(step_number)]);
display(['Section volume: ', num2str(section_vol)]);
if (do_plot == 1)
hold off;
end
查找点是否属于椭圆体的函数(如果val
是&lt; = 1.0),则执行此操作:
function [ val ] = calc_ellipsoid( x, y, z, x0, y0, z0, a, b, c, theta)
x_cmp = ((x - x0)*cos(theta) + (y - y0)*sin(theta))^2 /(a^2);
y_cmp = ((x - x0)*sin(theta) - (y - y0)*cos(theta))^2 /(b^2);
z_cmp = (z - z0)^2 / (c^2);
val = x_cmp + y_cmp + z_cmp;
end
<强>更新强>
为了找到共同身体的最大轴,您可以执行以下操作:
保存所有落入数组交叉点的点(代码中为R_arr
)
从结果数组中构造一个凸包:
[K, v] = convhull(R_arr(:, 1), R_arr(:, 2), R_arr(:, 3));
它将为您提供一个近似的体积v
和用于构建船体的点的索引K
现在您有一个点的子集,它们位于交叉点体的表面上。数组中的所有点都存在多次。让我们摆脱重复:
K = K(:);
K = unique(K, 'stable');
现在阵列非常小(上面的示例大约300点),你可以通过它找到最长的距离。我为它编写了函数findAxis
。
[A, B, d] = findAxis(R_arr(K, :));
它会返回最远的点A
和B
,以及它们之间的距离d
。
现在,当您知道这两个点时,您可以定义属于轴的另一个点。使用函数calc_coord
:
C = calc_coord(A, B, ranges(3, 1));
D = calc_coord(A, B, ranges(3, 2));
我使用数组ranges
中的值来指定轴的z
- 坐标。
绘制轴!
对于上面的例子,情节如下所示:
凸包的体积为5.933
。蒙特卡罗方法给了我们6.1889
,因此结果非常接近。
最长轴的长度为4.3264
。
为了获得体积近似更好的直觉,我计算了相对误差:
正如您所看到的,即使经过几次迭代,蒙特卡罗方法也能提供更好的准确性。
以下是具有轴计算功能的更新代码:
X=[0,2]; %two ellipsoid x coordinates
Y=[0,2]; %two ellipsoid y coordinates
Z=[0,2]; %two ellipsoid z coordinates
ROTATIONANGLE=[90,30];
AXIS_A=[1,2]; %two ellipsoid minor axis radius
AXIS_B=[3,5]; %two ellipsoid major axis radius
AXIS_C=[3.25,13.25]; %two ellipsoid major axis radius
ranges = zeros(3, 2);
do_plot = 1; %either plot ellipsoids or not
step_number = 1000000;
for i = 1:2 %display ellipsoid
if (do_plot == 1)
[x, y, z] = ellipsoid(X(i),Y(i),Z(i),AXIS_A(i),AXIS_B(i),AXIS_C(i),20);
S = surf(x, y, z);
alpha(.05);
rotate(S,[0,0,1], ROTATIONANGLE(i), [X(i),Y(i),Z(i)]);
end
%calculate the ranges for the simulation box
ranges(1, 1) = min(ranges(1, 1), X(i) - max(AXIS_A(i), AXIS_B(i)) );
ranges(1, 2) = max(ranges(1, 2), X(i) + max(AXIS_A(i), AXIS_B(i)) );
ranges(2, 1) = min(ranges(2, 1), Y(i) - max(AXIS_A(i), AXIS_B(i)) );
ranges(2, 2) = max(ranges(2, 2), Y(i) + max(AXIS_A(i), AXIS_B(i)) );
ranges(3, 1) = min(ranges(3, 1), Z(i) - AXIS_C(i));
ranges(3, 2) = max(ranges(3, 2), Z(i) + AXIS_C(i));
if (do_plot == 1)
hold on;
end
end
counter = 0; %how many points targeted the intersection
R_arr = [];
for i = 1:step_number
R = rand(3, 1).*(ranges(:, 2) - ranges(:, 1)) + ranges(:, 1); %a random point
n = 1;
val = calc_ellipsoid( R(1), R(2), R(3), X(n),Y(n),Z(n),AXIS_A(n),AXIS_B(n),AXIS_C(n),ROTATIONANGLE(n)*pi/180);
if (val <= 1.0)
n = 2;
val = calc_ellipsoid( R(1), R(2), R(3), X(n),Y(n),Z(n),AXIS_A(n),AXIS_B(n),AXIS_C(n),ROTATIONANGLE(n)*pi/180);
if (val <= 1.0)
if (do_plot == 1)
%plot3(R(1), R(2), R(3), 'or', 'MarkerSize', 1, 'MarkerFaceColor','r');
end
counter = counter + 1;
R_arr = [R_arr; R'];
end
end
end
cube_vol = 1; %the volume of the simulation box
for i=1:3
cube_vol = cube_vol * (ranges(i, 2) - ranges(i, 1));
end
%approximated volume of the intersection
section_vol = cube_vol * counter / step_number;
display(['Cube volume: ', num2str(cube_vol)]);
display(['Targeted points: ', num2str(counter), ' from ', num2str(step_number)]);
display(['Section volume: ', num2str(section_vol)]);
if (counter > 0)
%hull
[K, v] = convhull(R_arr(:, 1), R_arr(:, 2), R_arr(:, 3));
display(['Approximated volume: ', num2str(v)]);
trisurf(K, R_arr(:, 1), R_arr(:, 2), R_arr(:, 3), 'FaceColor','cyan')
hold on;
axis equal
K = K(:);
K = unique(K, 'stable');
[A, B, d] = findAxis(R_arr(K, :));
plot3(A(1, 1), A(1, 2), A(1, 3), 'or', 'MarkerSize', 10, 'MarkerFaceColor','r');
plot3(B(1, 1), B(1, 2), B(1, 3), 'or', 'MarkerSize', 10, 'MarkerFaceColor','r');
%axis
C = calc_coord(A, B, ranges(3, 1));
D = calc_coord(A, B, ranges(3, 2));
p = [C; D];
plot3(p(:, 1), p(:, 2), p(:, 3), '--g', 'LineWidth', 3);
else
display('There is no intersection!');
end
hold off;
功能:
function [ C] = calc_coord( A, B, z)
x1 = A(1, 1);
x2 = B(1, 1);
y1 = A(1, 2);
y2 = B(1, 2);
z1 = A(1, 3);
z2 = B(1, 3);
y = (y2 - y1)*(z - z1)/(z2 - z1) + y1;
x = (x2 - x1)*(y - y1)/(y2 - y1) + x1;
C = [x, y, z];
end
function [ A, B, d ] = findAxis( point_arr )
A_ind = 0; B_ind = 0; d = -1;
n = size(point_arr, 1);
i = 1;
while (i < n)
for j=(i+1):n
cur_d = norm(point_arr(i, :) - point_arr(j, :));
if (cur_d > d)
d = cur_d;
A_ind = i;
B_ind = j;
end
end
i = i + 1;
end
A = point_arr(A_ind, :);
B = point_arr(B_ind, :);
end
<强>说明强>
功能calc_ellipsoid
该函数基于椭球方程(椭圆体围绕z轴旋转角度theta
):
该函数定义给定点是否位于椭球内部
%the function calculates a value for some given point, which shows if the
%point is inside of the ellipsoid or not.
%for a point to be inside, the value has to be <=1
function [ val ] = calc_ellipsoid( x, y, z, x0, y0, z0, a, b, c, theta)
%x, y, z - coordinates of the point to be checked
%x0, y0, z0 - center of the ellipsoid
%a, b, c - axes of the ellipsoid
%theta - angle of the rotation about the Z-axis
x_cmp = ((x - x0)*cos(theta) + (y - y0)*sin(theta))^2 /(a^2);
y_cmp = ((x - x0)*sin(theta) - (y - y0)*cos(theta))^2 /(b^2);
z_cmp = (z - z0)^2 / (c^2);
val = x_cmp + y_cmp + z_cmp;
end
如果您需要使用其他形状,您需要找到这样的方程式,以检查您的点是否属于形状。椭球方程是最简单的方法之一。
功能findAxis
该函数从点云中搜索两个点,它们之间的距离最长。在这里我们假设交叉点形状的轴经过这两个点(这是一个非常粗略的假设,但它有效)。
%the function is given an array of all points which lay on the constructed
%body. we assume, that the axis of the body is the longest line, connecting
%some two points of this array. the function goes through all the points
%and calculates the euclidian distance between them.
%the function returns points A and B, and the distance between them
function [ A, B, d ] = findAxis( point_arr )
%point_arr - points from the bodies surface
%A, B - end points of the found exis
%d - distance between A and B
A_ind = 0; B_ind = 0; d = -1;
n = size(point_arr, 1);
i = 1;
while (i < n)
for j=(i+1):n
cur_d = norm(point_arr(i, :) - point_arr(j, :));
if (cur_d > d)
d = cur_d;
A_ind = i;
B_ind = j;
end
end
i = i + 1;
end
A = point_arr(A_ind, :);
B = point_arr(B_ind, :);
end
功能calc_coord
该功能基于3D空间中的线方程:
该等式定义了一条线,该线穿过坐标为(x1, y1, z1)
和(x2, y2, z2)
的2个点。我们使用这个函数两个在交点外形找到另外两个点,以便绘制轴更具说明性。如果你不打算拍出漂亮的照片,你真的不需要这个功能。
%the function is given two points A and B, which were found in the
%'findAxis' function. we want to find a new point C, which lay on the axis,
%and have z-coordinate equal to 'z'. it is done merely to visualize the
%axis in a better way.
function [C] = calc_coord( A, B, z)
x1 = A(1, 1);
x2 = B(1, 1);
y1 = A(1, 2);
y2 = B(1, 2);
z1 = A(1, 3);
z2 = B(1, 3);
y = (y2 - y1)*(z - z1)/(z2 - z1) + y1;
x = (x2 - x1)*(y - y1)/(y2 - y1) + x1;
C = [x, y, z];
end