【问题标题】:How to plot geodesic curves on a surface embedded in 3D?如何在嵌入 3D 的表面上绘制测地线曲线?
【发布时间】:2020-03-03 11:05:58
【问题描述】:

我想到了this video,或者这个simulation,我想从某个起点以函数 f(x,y) 给出的某种 3D 表面上的测地线再现。

midpoint method 似乎计算量大且代码密集,所以我想问一下是否有一种方法可以根据表面不同点的法线向量生成近似测地线曲线。每个点都有一个与之关联的切向量空间,因此,似乎知道法线向量并不能确定曲线向前移动的特定方向。

我曾尝试使用 Geogebra,但我意识到可能需要转移到其他软件平台,例如 Python(或 Poser?)、Matlab 或其他。

这个想法可行吗?我能得到一些关于如何实现它的想法吗?


如果它提供了一些关于如何回答问题的想法,以前有一个答案(现在不幸被删除了)建议使用函数形式 z = F(x,y) 的地形的中点方法,从端点之间的直线,分割成短段[我假设 XY 平面上的直线(?)],并在表面上提升 [我假设 XY 平面上的段之间的节点(?)]。接下来它建议找到“一个中点”[我猜连接表面上每对连续投影点的线段的中点(?)],并投影“它”[我猜这些中点中的每一个都很接近,但不是完全在表面(?)]在表面上正交(在法线方向上),使用方程 Z + t = F(X + t Fx, Y + t Fy) [我猜这是一个点积意味着为零...

(?)],其中 (X,Y,Z) 是中点的坐标,Fx,Fy 是 F 的偏导数,而 t 是未知数 [这是我理解这一点的主要问题……我是什么一旦我找到它应该怎么做?将其添加到 (X,Y,Z) 的每个坐标中,如 (X+t, Y+t, Z+t)?接着?]。这是 t 中的一个非线性方程,通过 Newton's iterations 求解。


作为更新/书签,Alvise Vianello 在 this 页面 on GitHub 上发布了一个 Python 计算机模拟测地线。非常感谢!

【问题讨论】:

标签: python matlab geometry computational-geometry geometry-surface


【解决方案1】:

我有一种方法应该适用于任意 3D 表面,即使该表面有孔或嘈杂。现在它很慢,但它似乎可以工作,并且可能会给你一些关于如何做到这一点的想法。

基本前提是微分几何,并且是:

1.) 生成代表您的表面的点集

2.) 从这个点集生成一个 k 最近邻邻近图(我在这里还标准化了跨维度的距离,因为我觉得它更准确地捕捉了“邻居”的概念)

3.) 通过使用点及其邻居作为矩阵的列,计算与此邻近图中每个节点关联的切线空间,然后我对其执行 SVD。 SVD之后,左奇异向量为我的切空间提供了新的基础(前两个列向量是我的平面向量,第三个垂直于平面)

4.) 使用 dijkstra 算法在此邻近图上从起始节点移动到结束节点,但不是使用欧几里德距离作为边权重,而是使用通过切线空间并行传输的向量之间的距离。

受到这篇论文的启发(减去所有展开的内容):https://arxiv.org/pdf/1806.09039.pdf

请注意,我留下了一些我正在使用的辅助函数,它们可能与您没有直接关系(主要是平面绘图)。

您要查看的函数是 get_knn、build_proxy_graph、generate_tangent_spaces 和 geodesic_single_path_dijkstra。

实施也可能会有所改进。

代码如下:

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mayavi import mlab
from sklearn.neighbors import NearestNeighbors
from scipy.linalg import svd
import networkx as nx
import heapq
from collections import defaultdict


def surface_squares(x_min, x_max, y_min, y_max, steps):
    x = np.linspace(x_min, x_max, steps)
    y = np.linspace(y_min, y_max, steps)
    xx, yy = np.meshgrid(x, y)
    zz = xx**2 + yy**2
    return xx, yy, zz


def get_meshgrid_ax(x, y, z):
    # fig = plt.figure()
    # ax = fig.gca(projection='3d')
    # ax.plot_surface(X=x, Y=y, Z=z)
    # return ax
    fig = mlab.figure()
    su = mlab.surf(x.T, y.T, z.T, warp_scale=0.1)


def get_knn(flattened_points, num_neighbors):
    # need the +1 because each point is its own nearest neighbor
    knn = NearestNeighbors(num_neighbors+1)
    # normalize flattened points when finding neighbors
    neighbor_flattened = (flattened_points - np.min(flattened_points, axis=0)) / (np.max(flattened_points, axis=0) - np.min(flattened_points, axis=0))
    knn.fit(neighbor_flattened)
    dist, indices = knn.kneighbors(neighbor_flattened)
    return dist, indices


def rotmatrix(axis, costheta):
    """ Calculate rotation matrix

    Arguments:
    - `axis`     : Rotation axis
    - `costheta` : Rotation angle
    """
    x, y, z = axis
    c = costheta
    s = np.sqrt(1-c*c)
    C = 1-c
    return np.matrix([[x*x*C+c,    x*y*C-z*s,  x*z*C+y*s],
                      [y*x*C+z*s,  y*y*C+c,    y*z*C-x*s],
                      [z*x*C-y*s,  z*y*C+x*s,  z*z*C+c]])


def plane(Lx, Ly, Nx, Ny, n, d):
    """ Calculate points of a generic plane 

    Arguments:
    - `Lx` : Plane Length first direction
    - `Ly` : Plane Length second direction
    - `Nx` : Number of points, first direction
    - `Ny` : Number of points, second direction
    - `n`  : Plane orientation, normal vector
    - `d`  : distance from the origin
    """

    x = np.linspace(-Lx/2, Lx/2, Nx)
    y = np.linspace(-Ly/2, Ly/2, Ny)
    # Create the mesh grid, of a XY plane sitting on the orgin
    X, Y = np.meshgrid(x, y)
    Z = np.zeros([Nx, Ny])
    n0 = np.array([0, 0, 1])

    # Rotate plane to the given normal vector
    if any(n0 != n):
        costheta = np.dot(n0, n)/(np.linalg.norm(n0)*np.linalg.norm(n))
        axis = np.cross(n0, n)/np.linalg.norm(np.cross(n0, n))
        rotMatrix = rotmatrix(axis, costheta)
        XYZ = np.vstack([X.flatten(), Y.flatten(), Z.flatten()])
        X, Y, Z = np.array(rotMatrix*XYZ).reshape(3, Nx, Ny)

    eps = 0.000000001
    dVec = d #abs((n/np.linalg.norm(n)))*d#np.array([abs(n[i])/np.linalg.norm(n)*val if abs(n[i]) > eps else val for i, val in enumerate(d)]) #
    X, Y, Z = X+dVec[0], Y+dVec[1], Z+dVec[2]
    return X, Y, Z


def build_proxy_graph(proxy_n_dist, proxy_n_indices):
    G = nx.Graph()

    for distance_list, neighbor_list in zip(proxy_n_dist, proxy_n_indices):
        # first element is always point
        current_node = neighbor_list[0]
        neighbor_list = neighbor_list[1:]
        distance_list = distance_list[1:]
        for neighbor, dist in zip(neighbor_list, distance_list):
            G.add_edge(current_node, neighbor, weight=dist)
    return G


def get_plane_points(normal_vec, initial_point, min_range=-10, max_range=10, steps=1000):
    steps_for_plane = np.linspace(min_range, max_range, steps)
    xx, yy = np.meshgrid(steps_for_plane, steps_for_plane)
    d = -initial_point.dot(normal_vec)
    eps = 0.000000001
    if abs(normal_vec[2]) < eps and abs(normal_vec[1]) > eps:
        zz = (-xx*normal_vec[2] - yy*normal_vec[0] - d)/normal_vec[1]
    else:
        zz = (-xx*normal_vec[0] - yy*normal_vec[1] - d)/normal_vec[2]
    return xx, yy, zz


# def plot_tangent_plane_at_point(pointset, flattened_points, node, normal_vec):
#     ax = get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
#     node_loc = flattened_points[node]
#     print("Node loc: {}".format(node_loc))
#     xx, yy, zz = plane(10, 10, 500, 500, normal_vec, node_loc)
#     # xx, yy, zz = get_plane_points(normal_vec, node_loc)
#     print("Normal Vec: {}".format(normal_vec))
#     ax.plot_surface(X=xx, Y=yy, Z=zz)
#     ax.plot([node_loc[0]], [node_loc[1]], [node_loc[2]], markerfacecolor='k', markeredgecolor='k', marker='o', markersize=10)
#     plt.show()


def generate_tangent_spaces(proxy_graph, flattened_points):
    # This depth should gaurantee at least 16 neighbors
    tangent_spaces = {}
    for node in proxy_graph.nodes():
        neighbors = list(nx.neighbors(proxy_graph, node))
        node_point = flattened_points[node]
        zero_mean_mat = np.zeros((len(neighbors)+1, len(node_point)))
        for i, neighbor in enumerate(neighbors):
            zero_mean_mat[i] = flattened_points[neighbor]
        zero_mean_mat[-1] = node_point

        zero_mean_mat = zero_mean_mat - np.mean(zero_mean_mat, axis=0)
        u, s, v = svd(zero_mean_mat.T)
        # smat = np.zeros(u.shape[0], v.shape[0])
        # smat[:s.shape[0], :s.shape[0]] = np.diag(s)
        tangent_spaces[node] = u
    return tangent_spaces


def geodesic_single_path_dijkstra(flattened_points, proximity_graph, tangent_frames, start, end):
    # short circuit
    if start == end:
        return []
    # Create min priority queue
    minheap = []
    pred = {}
    dist = defaultdict(lambda: 1.0e+100)
    # for i, point in enumerate(flattened_points):
    R = {}
    t_dist = {}
    geo_dist = {}
    R[start] = np.eye(3)
    t_dist[start] = np.ones((3,))
    dist[start] = 0
    start_vector = flattened_points[start]
    for neighbor in nx.neighbors(proxy_graph, start):
        pred[neighbor] = start
        dist[neighbor] = np.linalg.norm(start_vector - flattened_points[neighbor])
        heapq.heappush(minheap, (dist[neighbor], neighbor))
    while minheap:
        r_dist, r_ind = heapq.heappop(minheap)
        if r_ind == end:
            break
        q_ind = pred[r_ind]
        u, s, v = svd(tangent_frames[q_ind].T*tangent_frames[r_ind])
        R[r_ind] = np.dot(R[q_ind], u * v.T)
        t_dist[r_ind] = t_dist[q_ind]+np.dot(R[q_ind], tangent_frames[q_ind].T * (r_dist - dist[q_ind]))
        geo_dist[r_ind] = np.linalg.norm(t_dist[r_ind])
        for neighbor in nx.neighbors(proxy_graph, r_ind):
            temp_dist = dist[r_ind] + np.linalg.norm(flattened_points[neighbor] - flattened_points[r_ind])
            if temp_dist < dist[neighbor]:
                dist[neighbor] = temp_dist
                pred[neighbor] = r_ind
                heapq.heappush(minheap, (dist[neighbor], neighbor))
    # found ending index, now loop through preds for path
    current_ind = end
    node_path = [end]
    while current_ind != start:
        node_path.append(pred[current_ind])
        current_ind = pred[current_ind]

    return node_path


def plot_path_on_surface(pointset, flattened_points, path):
    # ax = get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
    # ax.plot(points_in_path[:, 0], points_in_path[:, 1], points_in_path[:, 2], linewidth=10.0)
    # plt.show()
    get_meshgrid_ax(x=pointset[:, :, 0], y=pointset[:, :, 1], z=pointset[:, :, 2])
    points_in_path = flattened_points[path]
    mlab.plot3d(points_in_path[:, 0], points_in_path[:, 1], points_in_path[:, 2] *.1)
    mlab.show()


"""
    True geodesic of graph.
    Build proximity graph
    Find tangent space using geodisic neighborhood at each point in graph
    Parallel transport vectors between tangent space points
    Use this as your distance metric
    Dijkstra's Algorithm
"""
if __name__ == "__main__":
    x, y, z = surface_squares(-5, 5, -5, 5, 500)
    # plot_meshgrid(x, y, z)
    pointset = np.stack([x, y, z], axis=2)
    proxy_graph_num_neighbors = 16
    flattened_points = pointset.reshape(pointset.shape[0]*pointset.shape[1], pointset.shape[2])
    flattened_points = flattened_points
    proxy_n_dist, proxy_n_indices = get_knn(flattened_points, proxy_graph_num_neighbors)
    # Generate a proximity graph using proxy_graph_num_neighbors
    # Nodes = number of points, max # of edges = number of points * num_neighbors
    proxy_graph = build_proxy_graph(proxy_n_dist, proxy_n_indices)
    # Now, using the geodesic_num_neighbors, get geodesic neighborshood for tangent space construction
    tangent_spaces = generate_tangent_spaces(proxy_graph, flattened_points)
    node_to_use = 2968
    # 3rd vector of tangent space is normal to plane
    # plot_tangent_plane_at_point(pointset, flattened_points, node_to_use, tangent_spaces[node_to_use][:, 2])
    path = geodesic_single_path_dijkstra(flattened_points, proxy_graph, tangent_spaces, 250, 249750)
    plot_path_on_surface(pointset, flattened_points, path)

请注意,我安装并设置了 mayavi 以获得不错的输出图像(matplotlib 没有真正的 3d 渲染,因此它的绘图很糟糕)。但是,如果您想使用 matplotlib 代码,我确实保留了它。如果这样做,只需在路径绘图仪中将缩放比例删除 0.1 并取消注释绘图代码。无论如何,这是 z=x^2+y^2 的示例图像。白线是测地线路径:

您也可以相当容易地调整它以从 dijkstra 算法返回节点之间的所有成对测地线距离(查看论文的附录以了解您需要进行的细微修改)。然后你可以在你的表面上画出你想要的任何线条。

【讨论】:

  • 非常感谢。我一心想要让中点方法继续下去,但我喜欢统计和机器学习,你的方法非常有创意和酷!
【解决方案2】:

使用midpoint search method

应用于函数 f(x,y) = x^3 + y^2,我将线段的点在 XY 平面 y = x 上从 x = -1 投影到 x = 1。

了解一下,一次迭代,XY平面上的直线上只有4个点,黑色球体是投影到曲面上的直线的这4个原始点,而红点是单次迭代的中点, 黄点是红点沿表面法线投影的结果:

使用 Matlab fmincon(),经过 5 次迭代,我们可以得到从 A 点到 B 点的测地线:

代码如下:

% Creating the surface
x = linspace(-1,1);
y = linspace(-1,1);
[x,y] = meshgrid(x,y);
z = x.^3 + y.^2;
S = [x;y;z];
h = surf(x,y,z)
set(h,'edgecolor','none')
colormap summer

% Number of points
n = 1000;

% Line to project on the surface with n values to get a feel for it...
t = linspace(-1,1,n);
height = t.^3 + t.^2;
P = [t;t;height];

% Plotting the projection of the line on the surface:
hold on
%plot3(P(1,:),P(2,:),P(3,:),'o')

for j=1:5
% First midpoint iteration updates P...
P = [P(:,1), (P(:,1:end-1) + P(:,2:end))/2, P(:,end)];
%plot3(P(1,:), P(2,:), P(3,:), '.', 'MarkerSize', 20)

A = zeros(3,size(P,2));
for i = 1:size(P,2)
% Starting point will be the vertical projection of the mid-points:
    A(:,i) = [P(1,i), P(2,i), P(1,i)^3 + P(2,i)^2];
end

% Linear constraints:
nonlincon = @nlcon;

% Placing fmincon in a loop for all the points

for i = 1:(size(A,2))
    % Objective function:
    objective = @(x)(P(1,i) - x(1))^2 + (P(2,i) - x(2))^2 + (P(3,i)-x(3))^2;
    A(:,i) = fmincon(objective, A(:,i), [], [], [], [], [], [], nonlincon);
end

P = A;
end

plot3(P(1,:), P(2,:), P(3,:), '.', 'MarkerSize', 5,'Color','y')

在一个名为nlcon.m的单独文件中:

function[c,ceq] = nlcon(x)
   c   = [];
   ceq = x(3) - x(1)^3 - x(2)^2;

对于在 XY 上具有直线非对角线的非常凉爽的表面上的测地线也是如此:

% Creating the surface
x = linspace(-1,1);
y = linspace(-1,1);
[x,y] = meshgrid(x,y);
z = sin(3*(x.^2+y.^2))/10;
S = [x;y;z];
h = surf(x,y,z)
set(h,'edgecolor','none')
colormap summer

% Number of points
n = 1000;

% Line to project on the surface with n values to get a feel for it...
t = linspace(-1,1,n);
height = sin(3*((.5*ones(1,n)).^2+ t.^2))/10;
P = [(.5*ones(1,n));t;height];

% Plotting the line on the surface:
hold on
%plot3(P(1,:),P(2,:),P(3,:),'o')

for j=1:2
% First midpoint iteration updates P...
P = [P(:,1), (P(:,1:end-1) + P(:,2:end))/2, P(:,end)];
%plot3(P(1,:), P(2,:), P(3,:), '.', 'MarkerSize', 20)

A = zeros(3,size(P,2));
for i = 1:size(P,2) 
% Starting point will be the vertical projection of the first mid-point:
    A(:,i) = [P(1,i), P(2,i), sin(3*(P(1,i)^2+ P(2,i)^2))/10];
end

% Linear constraints:
nonlincon = @nonlincon;

% Placing fmincon in a loop for all the points

for i = 1:(size(A,2))
    % Objective function:
    objective = @(x)(P(1,i) - x(1))^2 + (P(2,i) - x(2))^2 + (P(3,i)-x(3))^2;
    A(:,i) = fmincon(objective, A(:,i), [], [], [], [], [], [], nonlincon);
end

P = A;
end

plot3(P(1,:), P(2,:), P(3,:), '.', 'MarkerSize',5,'Color','r')

带有nonlincon.m中的非线性约束:

function[c,ceq] = nlcon(x)
   c   = [];
   ceq = x(3) - sin(3*(x(1)^2+ x(2)^2))/10;

一个令人烦恼的问题是使用这种方法可能会过度拟合曲线,后一个图就是一个例子。所以我调整代码只选择一个起点和一个终点,并允许迭代过程找到曲线的其余部分,100 次迭代似乎朝着正确的方向前进:


上面的例子似乎是在XY平面上遵循线性投影,但幸运的是这不是一个固定的模式,这会进一步对方法产生怀疑。参见例如双曲抛物面 x^2 - y^2:


请注意,有一些算法可以沿表面 f(x,y) 推进或推动测地线,其增量由起点和表面的法线向量确定,如here。感谢 Alvise Vianello 在该模拟中研究 JS 的工作和他的sharing in GitHub,我能够将该算法转换为 Matlab 代码,为第一个示例生成此图,f(x,y) = x^3 + y^2:

这是 Matlab 代码:

x = linspace(-1,1);
y = linspace(-1,1);
[x,y] = meshgrid(x,y);
z = x.^3 + y.^2;
S = [x;y;z];
h = surf(x,y,z)
set(h,'edgecolor','none')
colormap('gray');
hold on

f = @(x,y) x.^3 + y.^2; % The actual surface

dfdx = @(x,y) (f(x + eps, y) - f(x - eps, y))/(2 * eps); % ~ partial f wrt x
dfdy = @(x,y) (f(x, y + eps) - f(x, y - eps))/(2 * eps); % ~ partial f wrt y

N = @(x,y) [- dfdx(x,y), - dfdy(x,y), 1]; % Normal vec to surface @ any pt.

C = {'k','b','r','g','y','m','c',[.8 .2 .6],[.2,.8,.1],[0.3010 0.7450 0.9330],[0.9290 0.6940 0.1250],[0.8500 0.3250 0.0980]}; % Color scheme

for s = 1:11     % No. of lines to be plotted.
start = -5:5;    % Distributing the starting points of the lines.  
y0 = start(s)/5; % Fitting the starting pts between -1 and 1 along y axis.
x0 = 1;          % Along x axis always starts at 1.
dx0 = 0;         % Initial differential increment along x
dy0 = 0.05;      % Initial differential increment along y
step_size = 0.000008; % Will determine the progression rate from pt to pt.
eta =  step_size / sqrt(dx0^2 + dy0^2); % Normalization.
eps = 0.0001;          % Epsilon
max_num_iter = 100000; % Number of dots in each line.

x = [[x0, x0 + eta * dx0], zeros(1,max_num_iter - 2)]; % Vec of x values
y = [[y0, y0 + eta * dy0], zeros(1,max_num_iter - 2)]; % Vec of y values

for i = 2:(max_num_iter - 1)  % Creating the geodesic:
            xt = x(i);        % Values at point t of x, y and the function:
            yt = y(i);
            ft = f(xt,yt);

            xtm1 = x(i - 1);  % Values at t minus 1 (prior point) for x,y,f
            ytm1 = y(i - 1);
            ftm1 = f(xtm1,ytm1);

            xsymp = xt + (xt - xtm1); % Adding the prior difference forward:
            ysymp = yt + (yt - ytm1);
            fsymp = ft + (ft - ftm1);

            df = fsymp - f(xsymp,ysymp); % Is the surface changing? How much?
            n = N(xt,yt);                % Normal vector at point t
            gamma = df * n(3);           % Scalar x change f x z value of N

            xtp1 = xsymp - gamma * n(1); % Gamma to modulate incre. x & y.
            ytp1 = ysymp - gamma * n(2);

            x(i + 1) = xtp1;
            y(i + 1) = ytp1;
end

P = [x; y; f(x,y)]; % Compiling results into a matrix.

indices = find(abs(P(1,:)) < 1); % Avoiding lines overshooting surface.
P = P(:,indices);
indices = find(abs(P(2,:)) < 1);
P = P(:,indices);

    units = 15; % Deternines speed (smaller, faster)
    packet = floor(size(P,2)/units);
    P = P(:,1: packet * units);

  for k = 1:packet:(packet * units)
        hold on
        plot3(P(1, k:(k+packet-1)), P(2,(k:(k+packet-1))), P(3,(k:(k+packet-1))),...
            '.', 'MarkerSize', 3.5,'color',C{s})
        drawnow
  end

end

这是上面的一个较早示例,但现在计算方式不同,线并排开始,仅遵循测地线(无点对点轨迹):

    x = linspace(-1,1);
    y = linspace(-1,1);
    [x,y] = meshgrid(x,y);
    z = sin(3*(x.^2+y.^2))/10;  
    S = [x;y;z];
    h = surf(x,y,z)
    set(h,'edgecolor','none')
    colormap('gray');
    hold on

    f = @(x,y) sin(3*(x.^2+y.^2))/10; % The actual surface

    dfdx = @(x,y) (f(x + eps, y) - f(x - eps, y))/(2 * eps); % ~ partial f wrt x
    dfdy = @(x,y) (f(x, y + eps) - f(x, y - eps))/(2 * eps); % ~ partial f wrt y

    N = @(x,y) [- dfdx(x,y), - dfdy(x,y), 1]; % Normal vec to surface @ any pt.

    C = {'k','r','g','y','m','c',[.8 .2 .6],[.2,.8,.1],[0.3010 0.7450 0.9330],[0.7890 0.5040 0.1250],[0.9290 0.6940 0.1250],[0.8500 0.3250 0.0980]}; % Color scheme

    for s = 1:11     % No. of lines to be plotted.
    start = -5:5;    % Distributing the starting points of the lines.  
    x0 = -start(s)/5; % Fitting the starting pts between -1 and 1 along y axis.
    y0 = -1;          % Along x axis always starts at 1.
    dx0 = 0;         % Initial differential increment along x
    dy0 = 0.05;      % Initial differential increment along y
    step_size = 0.00005; % Will determine the progression rate from pt to pt.
    eta =  step_size / sqrt(dx0^2 + dy0^2); % Normalization.
    eps = 0.0001;          % Epsilon
    max_num_iter = 100000; % Number of dots in each line.

    x = [[x0, x0 + eta * dx0], zeros(1,max_num_iter - 2)]; % Vec of x values
    y = [[y0, y0 + eta * dy0], zeros(1,max_num_iter - 2)]; % Vec of y values

    for i = 2:(max_num_iter - 1)  % Creating the geodesic:
                xt = x(i);        % Values at point t of x, y and the function:
                yt = y(i);
                ft = f(xt,yt);

                xtm1 = x(i - 1);  % Values at t minus 1 (prior point) for x,y,f
                ytm1 = y(i - 1);
                ftm1 = f(xtm1,ytm1);

                xsymp = xt + (xt - xtm1); % Adding the prior difference forward:
                ysymp = yt + (yt - ytm1);
                fsymp = ft + (ft - ftm1);

                df = fsymp - f(xsymp,ysymp); % Is the surface changing? How much?
                n = N(xt,yt);                % Normal vector at point t
                gamma = df * n(3);           % Scalar x change f x z value of N

                xtp1 = xsymp - gamma * n(1); % Gamma to modulate incre. x & y.
                ytp1 = ysymp - gamma * n(2);

                x(i + 1) = xtp1;
                y(i + 1) = ytp1;
    end

    P = [x; y; f(x,y)]; % Compiling results into a matrix.

    indices = find(abs(P(1,:)) < 1); % Avoiding lines overshooting surface.
    P = P(:,indices);
    indices = find(abs(P(2,:)) < 1);
    P = P(:,indices);
    units = 35; % Deternines speed (smaller, faster)
    packet = floor(size(P,2)/units);
    P = P(:,1: packet * units);


  for k = 1:packet:(packet * units)
        hold on

        plot3(P(1, k:(k+packet-1)), P(2,(k:(k+packet-1))), P(3,(k:(k+packet-1))), '.', 'MarkerSize', 5,'color',C{s})
        drawnow
  end

    end

更多示例:

    x = linspace(-1,1);
    y = linspace(-1,1);
    [x,y] = meshgrid(x,y);
    z = x.^2 - y.^2;
    S = [x;y;z];
    h = surf(x,y,z)
    set(h,'edgecolor','none')
    colormap('gray');


    f = @(x,y) x.^2 - y.^2; % The actual surface

    dfdx = @(x,y) (f(x + eps, y) - f(x - eps, y))/(2 * eps); % ~ partial f wrt x
    dfdy = @(x,y) (f(x, y + eps) - f(x, y - eps))/(2 * eps); % ~ partial f wrt y

    N = @(x,y) [- dfdx(x,y), - dfdy(x,y), 1]; % Normal vec to surface @ any pt.

    C = {'b','w','r','g','y','m','c',[0.75, 0.75, 0],[0.9290, 0.6940, 0.1250],[0.3010 0.7450 0.9330],[0.1290 0.6940 0.1250],[0.8500 0.3250 0.0980]}; % Color scheme

    for s = 1:11     % No. of lines to be plotted.
    start = -5:5;    % Distributing the starting points of the lines.  
    x0 = -start(s)/5; % Fitting the starting pts between -1 and 1 along y axis.
    y0 = -1;          % Along x axis always starts at 1.
    dx0 = 0;         % Initial differential increment along x
    dy0 = 0.05;      % Initial differential increment along y
    step_size = 0.00005; % Will determine the progression rate from pt to pt.
    eta =  step_size / sqrt(dx0^2 + dy0^2); % Normalization.
    eps = 0.0001;          % Epsilon
    max_num_iter = 100000; % Number of dots in each line.

    x = [[x0, x0 + eta * dx0], zeros(1,max_num_iter - 2)]; % Vec of x values
    y = [[y0, y0 + eta * dy0], zeros(1,max_num_iter - 2)]; % Vec of y values

    for i = 2:(max_num_iter - 1)  % Creating the geodesic:
                xt = x(i);        % Values at point t of x, y and the function:
                yt = y(i);
                ft = f(xt,yt);

                xtm1 = x(i - 1);  % Values at t minus 1 (prior point) for x,y,f
                ytm1 = y(i - 1);
                ftm1 = f(xtm1,ytm1);

                xsymp = xt + (xt - xtm1); % Adding the prior difference forward:
                ysymp = yt + (yt - ytm1);
                fsymp = ft + (ft - ftm1);

                df = fsymp - f(xsymp,ysymp); % Is the surface changing? How much?
                n = N(xt,yt);                % Normal vector at point t
                gamma = df * n(3);           % Scalar x change f x z value of N

                xtp1 = xsymp - gamma * n(1); % Gamma to modulate incre. x & y.
                ytp1 = ysymp - gamma * n(2);

                x(i + 1) = xtp1;
                y(i + 1) = ytp1;
    end

    P = [x; y; f(x,y)]; % Compiling results into a matrix.

    indices = find(abs(P(1,:)) < 1); % Avoiding lines overshooting surface.
    P = P(:,indices);
    indices = find(abs(P(2,:)) < 1);
    P = P(:,indices);
    units = 45; % Deternines speed (smaller, faster)
    packet = floor(size(P,2)/units);
    P = P(:,1: packet * units);

  for k = 1:packet:(packet * units)
        hold on
        plot3(P(1, k:(k+packet-1)), P(2,(k:(k+packet-1))), P(3,(k:(k+packet-1))), '.', 'MarkerSize', 5,'color',C{s})
        drawnow
  end

  end

或者这个:

    x = linspace(-1,1);
    y = linspace(-1,1);
    [x,y] = meshgrid(x,y);
    z = .07 * (.1 + x.^2 + y.^2).^(-1);
    S = [x;y;z];
    h = surf(x,y,z)
    zlim([0 8])
    set(h,'edgecolor','none')
    colormap('gray');
    axis off
    hold on

    f = @(x,y) .07 * (.1 + x.^2 + y.^2).^(-1);    % The actual surface

    dfdx = @(x,y) (f(x + eps, y) - f(x - eps, y))/(2 * eps); % ~ partial f wrt x
    dfdy = @(x,y) (f(x, y + eps) - f(x, y - eps))/(2 * eps); % ~ partial f wrt y

    N = @(x,y) [- dfdx(x,y), - dfdy(x,y), 1]; % Normal vec to surface @ any pt.

     C = {'w',[0.8500, 0.3250, 0.0980],[0.9290, 0.6940, 0.1250],'g','y','m','c',[0.75, 0.75, 0],'r',...
         [0.56,0,0.85],'m'}; % Color scheme

    for s = 1:10     % No. of lines to be plotted.  
    start = -9:2:9;
    x0 = -start(s)/10;
    y0 = -1;          % Along x axis always starts at 1.
    dx0 = 0;         % Initial differential increment along x
    dy0 = 0.05;      % Initial differential increment along y
    step_size = 0.00005; % Will determine the progression rate from pt to pt.
    eta =  step_size / sqrt(dx0^2 + dy0^2); % Normalization.
    eps = 0.0001;          % EpsilonA
    max_num_iter = 500000; % Number of dots in each line.

    x = [[x0, x0 + eta * dx0], zeros(1,max_num_iter - 2)]; % Vec of x values
    y = [[y0, y0 + eta * dy0], zeros(1,max_num_iter - 2)]; % Vec of y values

    for i = 2:(max_num_iter - 1)  % Creating the geodesic:
                xt = x(i);        % Values at point t of x, y and the function:
                yt = y(i);
                ft = f(xt,yt);

                xtm1 = x(i - 1);  % Values at t minus 1 (prior point) for x,y,f
                ytm1 = y(i - 1);
                ftm1 = f(xtm1,ytm1);

                xsymp = xt + (xt - xtm1); % Adding the prior difference forward:
                ysymp = yt + (yt - ytm1);
                fsymp = ft + (ft - ftm1);

                df = fsymp - f(xsymp,ysymp); % Is the surface changing? How much?
                n = N(xt,yt);                % Normal vector at point t
                gamma = df * n(3);           % Scalar x change f x z value of N

                xtp1 = xsymp - gamma * n(1); % Gamma to modulate incre. x & y.
                ytp1 = ysymp - gamma * n(2);

                x(i + 1) = xtp1;
                y(i + 1) = ytp1;
    end

     P = [x; y; f(x,y)]; % Compiling results into a matrix.

    indices = find(abs(P(1,:)) < 1.5); % Avoiding lines overshooting surface.
    P = P(:,indices);
    indices = find(abs(P(2,:)) < 1);
    P = P(:,indices);

    units = 15; % Deternines speed (smaller, faster)
    packet = floor(size(P,2)/units);
    P = P(:,1: packet * units);

  for k = 1:packet:(packet * units)
        hold on
        plot3(P(1, k:(k+packet-1)), P(2,(k:(k+packet-1))), P(3,(k:(k+packet-1))),...
            '.', 'MarkerSize', 3.5,'color',C{s})
        drawnow
  end

    end

或者一个sinc函数:

    x = linspace(-10, 10);
    y = linspace(-10, 10);
    [x,y] = meshgrid(x,y);
    z = sin(1.3*sqrt (x.^ 2 + y.^ 2) + eps)./ (sqrt (x.^ 2 + y.^ 2) + eps);
    S = [x;y;z];
    h = surf(x,y,z)
    set(h,'edgecolor','none')
    colormap('gray');
    axis off
    hold on

    f = @(x,y) sin(1.3*sqrt (x.^ 2 + y.^ 2) + eps)./ (sqrt (x.^ 2 + y.^ 2) + eps);   % The actual surface

    dfdx = @(x,y) (f(x + eps, y) - f(x - eps, y))/(2 * eps); % ~ partial f wrt x
    dfdy = @(x,y) (f(x, y + eps) - f(x, y - eps))/(2 * eps); % ~ partial f wrt y

    N = @(x,y) [- dfdx(x,y), - dfdy(x,y), 1]; % Normal vec to surface @ any pt.

    C = {'w',[0.8500, 0.3250, 0.0980],[0.9290, 0.6940, 0.1250],'g','y','r','c','m','w',...
         [0.56,0,0.85],[0.8500, 0.7250, 0.0980],[0.2290, 0.1940, 0.6250],'w',...
         [0.890, 0.1940, 0.4250],'y',[0.2290, 0.9940, 0.3250],'w',[0.1500, 0.7250, 0.0980],...
         [0.8500, 0.3250, 0.0980],'m','w'}; % Color scheme

    for s = 1:12     % No. of lines to be plotted.  

    x0 = 10;
    y0 = 10;          % Along x axis always starts at 1.
    dx0 = -0.001*(cos(pi /2 *s/11));         % Initial differential increment along x
    dy0 = -0.001*(sin(pi /2 *s/11));         % Initial differential increment along y
    step_size = 0.0005; % Will determine the progression rate from pt to pt.
    % Making it smaller increases the length of the curve.
    eta =  step_size / sqrt(dx0^2 + dy0^2); % Normalization.
    eps = 0.0001;          % EpsilonA
    max_num_iter = 500000; % Number of dots in each line.

    x = [[x0, x0 + eta * dx0], zeros(1,max_num_iter - 2)]; % Vec of x values
    y = [[y0, y0 + eta * dy0], zeros(1,max_num_iter - 2)]; % Vec of y values

    for i = 2:(max_num_iter - 1)  % Creating the geodesic:
                xt = x(i);        % Values at point t of x, y and the function:
                yt = y(i);
                ft = f(xt,yt);

                xtm1 = x(i - 1);  % Values at t minus 1 (prior point) for x,y,f
                ytm1 = y(i - 1);
                ftm1 = f(xtm1,ytm1);

                xsymp = xt + (xt - xtm1); % Adding the prior difference forward:
                ysymp = yt + (yt - ytm1);
                fsymp = ft + (ft - ftm1);

                df = fsymp - f(xsymp,ysymp); % Is the surface changing? How much?
                n = N(xt,yt);                % Normal vector at point t
                gamma = df * n(3);           % Scalar x change f x z value of N

                xtp1 = xsymp - gamma * n(1); % Gamma to modulate incre. x & y.
                ytp1 = ysymp - gamma * n(2);

                x(i + 1) = xtp1;
                y(i + 1) = ytp1;
    end

     P = [x; y; f(x,y)]; % Compiling results into a matrix.

    indices = find(abs(P(1,:)) < 10); % Avoiding lines overshooting surface.
    P = P(:,indices);
    indices = find(abs(P(2,:)) < 10);
    P = P(:,indices);

    units = 15; % Deternines speed (smaller, faster)
    packet = floor(size(P,2)/units);
    P = P(:,1: packet * units);

  for k = 1:packet:(packet * units)
        hold on
        plot3(P(1, k:(k+packet-1)), P(2,(k:(k+packet-1))), P(3,(k:(k+packet-1))),...
            '.', 'MarkerSize', 3.5,'color',C{s})
        drawnow
  end

    end

最后一个:

    x = linspace(-1.5,1.5);
    y = linspace(-1,1);
    [x,y] = meshgrid(x,y);
    z = 0.5 *y.*sin(5 * x) - 0.5 * x.*cos(5 * y)+1.5; 
    S = [x;y;z];
    h = surf(x,y,z)
    zlim([0 8])
    set(h,'edgecolor','none')
    colormap('gray');
    axis off
    hold on

    f = @(x,y) 0.5 *y.* sin(5 * x) - 0.5 * x.*cos(5 * y)+1.5;     % The actual surface

    dfdx = @(x,y) (f(x + eps, y) - f(x - eps, y))/(2 * eps); % ~ partial f wrt x
    dfdy = @(x,y) (f(x, y + eps) - f(x, y - eps))/(2 * eps); % ~ partial f wrt y

    N = @(x,y) [- dfdx(x,y), - dfdy(x,y), 1]; % Normal vec to surface @ any pt.

     C = {'w',[0.8500, 0.3250, 0.0980],[0.9290, 0.6940, 0.1250],'g','y','k','c',[0.75, 0.75, 0],'r',...
         [0.56,0,0.85],'m'}; % Color scheme

    for s = 1:11     % No. of lines to be plotted.  
    start = [0, 0.7835,  -0.7835, 0.5877, -0.5877, 0.3918, -0.3918, 0.1959, -0.1959, 0.9794, -0.9794];
    x0 = start(s);
    y0 = -1;          % Along x axis always starts at 1.
    dx0 = 0;         % Initial differential increment along x
    dy0 = 0.05;      % Initial differential increment along y
    step_size = 0.00005; % Will determine the progression rate from pt to pt.
    % Making it smaller increases the length of the curve.
    eta =  step_size / sqrt(dx0^2 + dy0^2); % Normalization.
    eps = 0.0001;          % EpsilonA
    max_num_iter = 500000; % Number of dots in each line.

    x = [[x0, x0 + eta * dx0], zeros(1,max_num_iter - 2)]; % Vec of x values
    y = [[y0, y0 + eta * dy0], zeros(1,max_num_iter - 2)]; % Vec of y values

    for i = 2:(max_num_iter - 1)  % Creating the geodesic:
                xt = x(i);        % Values at point t of x, y and the function:
                yt = y(i);
                ft = f(xt,yt);

                xtm1 = x(i - 1);  % Values at t minus 1 (prior point) for x,y,f
                ytm1 = y(i - 1);
                ftm1 = f(xtm1,ytm1);

                xsymp = xt + (xt - xtm1); % Adding the prior difference forward:
                ysymp = yt + (yt - ytm1);
                fsymp = ft + (ft - ftm1);

                df = fsymp - f(xsymp,ysymp); % Is the surface changing? How much?
                n = N(xt,yt);                % Normal vector at point t
                gamma = df * n(3);           % Scalar x change f x z value of N

                xtp1 = xsymp - gamma * n(1); % Gamma to modulate incre. x & y.
                ytp1 = ysymp - gamma * n(2);

                x(i + 1) = xtp1;
                y(i + 1) = ytp1;
    end

     P = [x; y; f(x,y)]; % Compiling results into a matrix.

    indices = find(abs(P(1,:)) < 1.5); % Avoiding lines overshooting surface.
    P = P(:,indices);
    indices = find(abs(P(2,:)) < 1);
    P = P(:,indices);

    units = 15; % Deternines speed (smaller, faster)
    packet = floor(size(P,2)/units);
    P = P(:,1: packet * units);

  for k = 1:packet:(packet * units)
        hold on
        plot3(P(1, k:(k+packet-1)), P(2,(k:(k+packet-1))), P(3,(k:(k+packet-1))),...
            '.', 'MarkerSize', 3.5,'color',C{s})
        drawnow
  end

    end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    • 2023-02-15
    • 1970-01-01
    • 2011-11-29
    • 1970-01-01
    相关资源
    最近更新 更多