Skip to content

十二面体的纽结怎么画

shap_interaction_matrix

绘制思路

  1. 绘制正十二面体
  2. 找到每条边的特征点,创建顺时针绕过边的折线。
  3. 遍历并绘制平滑插值后的折线(下列代码按面遍历,请注意每条边需要遍历两次)

dodeca

dodeca knots

Julia 代码

展开
Julia
# Wang Zilong @ ciac <ZilongWang@mail.ustc.edu.cn> 2025.02.21
# Julia version

using Plots
using LinearAlgebra
using Interpolations

# 正十二面体的面数据 (已提供)
dodeca = [
    [
        -0.5773503 0.5773503 0.5773503
        0.0 0.3567822 0.9341724
        0.0 -0.3567822 0.9341724
        -0.5773503 -0.5773503 0.5773503
        -0.9341724 0.0 0.3567822
    ],
    [
        0.5773503 -0.5773503 0.5773503
        0.3567822 -0.9341724 0.0
        -0.3567822 -0.9341724 0.0
        -0.5773503 -0.5773503 0.5773503
        0.0 -0.3567822 0.9341724
    ],
    [
        -0.5773503 -0.5773503 0.5773503
        -0.3567822 -0.9341724 0.0
        -0.5773503 -0.5773503 -0.5773503
        -0.9341724 0.0 -0.3567822
        -0.9341724 0.0 0.3567822
    ],
    [
        -0.9341724 0.0 0.3567822
        -0.9341724 0.0 -0.3567822
        -0.5773503 0.5773503 -0.5773503
        -0.3567822 0.9341724 0.0
        -0.5773503 0.5773503 0.5773503
    ],
    [
        0.0 0.3567822 0.9341724
        -0.5773503 0.5773503 0.5773503
        -0.3567822 0.9341724 0.0
        0.3567822 0.9341724 0.0
        0.5773503 0.5773503 0.5773503
    ],
    [
        0.0 -0.3567822 0.9341724
        0.0 0.3567822 0.9341724
        0.5773503 0.5773503 0.5773503
        0.9341724 0.0 0.3567822
        0.5773503 -0.5773503 0.5773503
    ],
    [
        0.5773503 0.5773503 -0.5773503
        0.9341724 0.0 -0.3567822
        0.5773503 -0.5773503 -0.5773503
        0.0 -0.3567822 -0.9341724
        0.0 0.3567822 -0.9341724
    ],
    [
        0.5773503 -0.5773503 -0.5773503
        0.0 -0.3567822 -0.9341724
        -0.5773503 -0.5773503 -0.5773503
        -0.3567822 -0.9341724 0.0
        0.3567822 -0.9341724 0.0
    ],
    [
        -0.5773503 0.5773503 -0.5773503
        -0.9341724 0.0 -0.3567822
        -0.5773503 -0.5773503 -0.5773503
        0.0 -0.3567822 -0.9341724
        0.0 0.3567822 -0.9341724
    ],
    [
        -0.5773503 0.5773503 -0.5773503
        0.0 0.3567822 -0.9341724
        0.5773503 0.5773503 -0.5773503
        0.3567822 0.9341724 0.0
        -0.3567822 0.9341724 0.0
    ],
    [
        0.5773503 0.5773503 0.5773503
        0.3567822 0.9341724 0.0
        0.5773503 0.5773503 -0.5773503
        0.9341724 0.0 -0.3567822
        0.9341724 0.0 0.3567822
    ],
    [
        0.5773503 -0.5773503 0.5773503
        0.9341724 0.0 0.3567822
        0.9341724 0.0 -0.3567822
        0.5773503 -0.5773503 -0.5773503
        0.3567822 -0.9341724 0.0
    ],
]

tetra = [
    # 面1
    [
        0.0 0.0 1.0
        0.9428090415820634 0.0 -0.3333333333333333
        -0.4714045207910317 0.8164965809277259 -0.3333333333333333
    ],
    # 面2 
    [
        0.0 0.0 1.0
        -0.4714045207910317 0.8164965809277259 -0.3333333333333333
        -0.4714045207910317 -0.8164965809277259 -0.3333333333333333
    ],
    # 面3
    [
        0.0 0.0 1.0;
        -0.4714045207910317 -0.8164965809277259 -0.3333333333333333
        0.9428090415820634 0.0 -0.3333333333333333
    ],
    # 面4
    [
        0.9428090415820634 0.0 -0.3333333333333333
        -0.4714045207910317 -0.8164965809277259 -0.3333333333333333
        -0.4714045207910317 0.8164965809277259 -0.3333333333333333
    ]
]

# 计算三等分点和中点
function get_points(v1, v2)
    midpoint = (v1 + v2) / 2
    third1 = v1 + (v2 - v1) / 3
    third2 = v1 + 2 * (v2 - v1) / 3
    return midpoint, third1, third2
end

# 计算面心和体心
function calculate_centers(face)
    face_center = sum(face, dims=1) / size(face, 1)
    body_center = [0.0, 0.0, 0.0]  # 体心为原点
    return face_center[:], body_center
end

# 创建平滑的插值曲线
function interpolate_curve(points)
    if length(points) < 3
        error("At least 3 points are required for interpolation.")
    end
    points_matrix = hcat(points...)'
    t = 1:size(points_matrix, 1)
    itp_x = cubic_spline_interpolation(t, points_matrix[:, 1])
    itp_y = cubic_spline_interpolation(t, points_matrix[:, 2])
    itp_z = cubic_spline_interpolation(t, points_matrix[:, 3])
    t_smooth = range(1, size(points_matrix, 1), length=100)
    x_smooth = itp_x.(t_smooth)
    y_smooth = itp_y.(t_smooth)
    z_smooth = itp_z.(t_smooth)
    return hcat(x_smooth, y_smooth, z_smooth)
end

function sort_face_vertices!(face, face_center)
    normal = -face_center # 近似法向量:面心指向体心

    # 找到一个与法向量不平行的向量 (例如 [1, 0, 0])
    arbitrary_vector = [1.0, 0.0, 0.0]
    if abs(dot(normal, arbitrary_vector)) > 0.99 # 如果太接近平行,换一个向量
        arbitrary_vector = [0.0, 1.0, 0.0]
    end

    u = normalize(cross(normal, arbitrary_vector)) # 第一个基向量
    v = normalize(cross(normal, u)) # 第二个基向量,与 u 和 normal 都正交

    angles = []
    for vertex in eachrow(face)
        vec_to_vertex = vertex - face_center
        x_proj = dot(vec_to_vertex, u)
        y_proj = dot(vec_to_vertex, v)
        angle = atan(y_proj, x_proj) # 计算角度
        push!(angles, angle)
    end

    # 获取排序后的索引 (顺时针 - 角度递减排序)
    sorted_indices = sortperm(angles, rev=true) # rev=true for clockwise, 可以尝试 rev=false for counter-clockwise

    # 根据排序后的索引重新排列顶点
    face[:] = face[sorted_indices, :] #  使用 [:] 来原地修改 face 的内容
end

# Sort vertices for each face in dodeca
for i in eachindex(dodeca)
    face_center, _ = calculate_centers(dodeca[i])
    sort_face_vertices!(dodeca[i], face_center)
end

for i in eachindex(tetra)
    face_center, _ = calculate_centers(tetra[i])
    sort_face_vertices!(tetra[i], face_center)
end

# --- Modified Plotting Section ---

# Choose a backend (try these one at a time)
# gr()
plotlyjs()
# pgfplotsx()

# Create 3D plot with some initial viewing angle
plot3d(
    1,
    xlim = (-1.1, 1.1),
    ylim = (-1.1, 1.1),
    zlim = (-1.1, 1.1),
    aspect_ratio = :equal,
    legend = false,
    xlabel = "X",
    ylabel = "Y",
    zlabel = "Z",
    title = "Dodecahedron with Smooth Inner Curves (Sorted Vertices)",
    camera = (30, 60)  # Set initial camera angle (azimuth, elevation)
)

# # 绘制正十二面体的边 (same as before)
# for face in dodeca
#     x = face[:, 1]
#     y = face[:, 2]
#     z = face[:, 3]
#     x = [x; x[1]; NaN]
#     y = [y; y[1]; NaN]
#     z = [z; z[1]; NaN]
#     plot!(x, y, z, linecolor = :blue, linewidth = 2)
# end

# 绘制内部曲线 (same as before)
for (l,face) in enumerate(dodeca) # dodeca tetra
    face_center, body_center = calculate_centers(face)
    num_vertices = size(face, 1)

    color = [:red, :green, :blue, :orange, :purple, :cyan, :magenta, :yellow, :brown, :pink, :gray, :black]
    lines2Plot = []
    for i in 1:num_vertices
        v1 = face[i, :]
        v2 = face[mod(i, num_vertices) + 1, :]

        midpoint, third1, third2 = get_points(v1, v2)

        p1 = v1 + 0.2 * (face_center - v1)
        p2 = third1 + 0.1 * (body_center - third1)  # Corrected: towards body center
        p3 = midpoint - 0.1 * (face_center - midpoint)
        p4 = third2 - 0.1 * (body_center - third2)  # Corrected: away from body center
        p5 = v2 + 0.2 * (face_center - v2)
        curve1 = interpolate_curve([p1, p2, p3, p4, p5])
        plot!(curve1[:, 1], curve1[:, 2], curve1[:, 3], linecolor = color[l], linewidth = 7)

        # append!(lines2Plot, [p1, p2, p3, p4, p5])
    end
    # curve1 = interpolate_curve(lines2Plot)

    # plot!(curve1[:, 1], curve1[:, 2], curve1[:, 3], linecolor = color[l], linewidth = 15)
end

display(plot!())

Python 代码

展开
Python
# Wang Zilong @ ciac <ZilongWang@mail.ustc.edu.cn> 2025.02.21
# Python version

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.interpolate import CubicSpline

# tetra = [
#     # 面1
#     [
#         0.0 0.0 1.0
#         0.9428090415820634 0.0 -0.3333333333333333
#         -0.4714045207910317 0.8164965809277259 -0.3333333333333333
#     ],
#     # 面2 
#     [
#         0.0 0.0 1.0
#         -0.4714045207910317 0.8164965809277259 -0.3333333333333333
#         -0.4714045207910317 -0.8164965809277259 -0.3333333333333333
#     ],
#     # 面3
#     [
#         0.0 0.0 1.0;
#         -0.4714045207910317 -0.8164965809277259 -0.3333333333333333
#         0.9428090415820634 0.0 -0.3333333333333333
#     ],
#     # 面4
#     [
#         0.9428090415820634 0.0 -0.3333333333333333
#         -0.4714045207910317 -0.8164965809277259 -0.3333333333333333
#         -0.4714045207910317 0.8164965809277259 -0.3333333333333333
#     ]
# ]

tetra = [
    np.array([
        [0.0, 0.0, 1.0],
        [0.9428090415820634, 0.0, -0.3333333333333333],
        [-0.4714045207910317, 0.8164965809277259, -0.3333333333333333],
    ]),
    np.array([
        [0.0, 0.0, 1.0],
        [-0.4714045207910317, 0.8164965809277259, -0.3333333333333333],
        [-0.4714045207910317, -0.8164965809277259, -0.3333333333333333],
    ]),
    np.array([
        [0.0, 0.0, 1.0],
        [-0.4714045207910317, -0.8164965809277259, -0.3333333333333333],
        [0.9428090415820634, 0.0, -0.3333333333333333],
    ]),
    np.array([
        [0.9428090415820634, 0.0, -0.3333333333333333],
        [-0.4714045207910317, -0.8164965809277259, -0.3333333333333333],
        [-0.4714045207910317, 0.8164965809277259, -0.3333333333333333],
    ]),
]

# 正十二面体的面数据 (已提供)
dodeca = [
    np.array([
        [-0.5773503,  0.5773503,  0.5773503],
        [ 0.0,        0.3567822,  0.9341724],
        [ 0.0,       -0.3567822,  0.9341724],
        [-0.5773503, -0.5773503,  0.5773503],
        [-0.9341724,  0.0,        0.3567822],
    ]),
    np.array([
        [ 0.5773503, -0.5773503,  0.5773503],
        [ 0.3567822, -0.9341724,  0.0],
        [-0.3567822, -0.9341724,  0.0],
        [-0.5773503, -0.5773503,  0.5773503],
        [ 0.0,       -0.3567822,  0.9341724],
    ]),
    np.array([
        [-0.5773503, -0.5773503,  0.5773503],
        [-0.3567822, -0.9341724,  0.0],
        [-0.5773503, -0.5773503, -0.5773503],
        [-0.9341724,  0.0,       -0.3567822],
        [-0.9341724,  0.0,        0.3567822],
    ]),
    np.array([
        [-0.9341724,  0.0,        0.3567822],
        [-0.9341724,  0.0,       -0.3567822],
        [-0.5773503,  0.5773503, -0.5773503],
        [-0.3567822,  0.9341724,  0.0],
        [-0.5773503,  0.5773503,  0.5773503],
    ]),
    np.array([
        [ 0.0,        0.3567822,  0.9341724],
        [-0.5773503,  0.5773503,  0.5773503],
        [-0.3567822,  0.9341724,  0.0],
        [ 0.3567822,  0.9341724,  0.0],
        [ 0.5773503,  0.5773503,  0.5773503],
    ]),
    np.array([
        [ 0.0,       -0.3567822,  0.9341724],
        [ 0.0,        0.3567822,  0.9341724],
        [ 0.5773503,  0.5773503,  0.5773503],
        [ 0.9341724,  0.0,        0.3567822],
        [ 0.5773503, -0.5773503,  0.5773503],
    ]),
    np.array([
        [ 0.5773503,  0.5773503, -0.5773503],
        [ 0.9341724,  0.0,       -0.3567822],
        [ 0.5773503, -0.5773503, -0.5773503],
        [ 0.0,       -0.3567822, -0.9341724],
        [ 0.0,        0.3567822, -0.9341724],
    ]),
    np.array([
        [ 0.5773503, -0.5773503, -0.5773503],
        [ 0.0,       -0.3567822, -0.9341724],
        [-0.5773503, -0.5773503, -0.5773503],
        [-0.3567822, -0.9341724,  0.0],
        [ 0.3567822, -0.9341724,  0.0],
    ]),
    np.array([
        [-0.5773503,  0.5773503, -0.5773503],
        [-0.9341724,  0.0,       -0.3567822],
        [-0.5773503, -0.5773503, -0.5773503],
        [ 0.0,       -0.3567822, -0.9341724],
        [ 0.0,        0.3567822, -0.9341724],
    ]),
    np.array([
        [-0.5773503,  0.5773503, -0.5773503],
        [ 0.0,        0.3567822, -0.9341724],
        [ 0.5773503,  0.5773503, -0.5773503],
        [ 0.3567822,  0.9341724,  0.0],
        [-0.3567822,  0.9341724,  0.0],
    ]),
    np.array([
        [ 0.5773503,  0.5773503,  0.5773503],
        [ 0.3567822,  0.9341724,  0.0],
        [ 0.5773503,  0.5773503, -0.5773503],
        [ 0.9341724,  0.0,       -0.3567822],
        [ 0.9341724,  0.0,        0.3567822],
    ]),
    np.array([
        [ 0.5773503, -0.5773503,  0.5773503],
        [ 0.9341724,  0.0,        0.3567822],
        [ 0.9341724,  0.0,       -0.3567822],
        [ 0.5773503, -0.5773503, -0.5773503],
        [ 0.3567822, -0.9341724,  0.0],
    ]),
]

# 计算三等分点和中点
def get_points(v1, v2):
    midpoint = (v1 + v2) / 2
    third1 = v1 + (v2 - v1) / 3
    third2 = v1 + 2 * (v2 - v1) / 3
    return midpoint, third1, third2

# 计算面心和体心
def calculate_centers(face):
    face_center = np.mean(face, axis=0)
    body_center = np.array([0.0, 0.0, 0.0])  # 体心为原点
    return face_center, body_center

# 创建平滑的插值曲线
def interpolate_curve(points):
    if len(points) < 3:
        raise ValueError("At least 3 points are required for interpolation.")
    
    points_matrix = np.array(points)
    t = np.arange(len(points_matrix))
    
    # 使用CubicSpline进行插值
    cs_x = CubicSpline(t, points_matrix[:, 0])
    cs_y = CubicSpline(t, points_matrix[:, 1])
    cs_z = CubicSpline(t, points_matrix[:, 2])
    
    t_smooth = np.linspace(t[0], t[-1], 100)
    x_smooth = cs_x(t_smooth)
    y_smooth = cs_y(t_smooth)
    z_smooth = cs_z(t_smooth)
    
    return np.vstack((x_smooth, y_smooth, z_smooth)).T

# 排序顶点
def sort_face_vertices(face, face_center):
    normal = -face_center  # 近似法向量:面心指向体心
    arbitrary_vector = np.array([1.0, 0.0, 0.0])
    
    if np.abs(np.dot(normal, arbitrary_vector)) > 0.99:
        arbitrary_vector = np.array([0.0, 1.0, 0.0])
    
    u = np.cross(normal, arbitrary_vector)
    u = u / np.linalg.norm(u)
    v = np.cross(normal, u)
    v = v / np.linalg.norm(v)
    
    angles = []
    for vertex in face:
        vec_to_vertex = vertex - face_center
        x_proj = np.dot(vec_to_vertex, u)
        y_proj = np.dot(vec_to_vertex, v)
        angle = np.arctan2(y_proj, x_proj)
        angles.append(angle)
    
    sorted_indices = np.argsort(angles)[::-1]  # 顺时针排序
    return face[sorted_indices]

# Sort vertices for each face in dodeca
for i in range(len(dodeca)):
    face_center, _ = calculate_centers(dodeca[i])
    dodeca[i] = sort_face_vertices(dodeca[i], face_center)

# 绘制正十二面体的边和内部曲线
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)
ax.set_zlim(-1.1, 1.1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_title('Dodecahedron with Smooth Inner Curves (Sorted Vertices)')

# color = [:red, :green, :blue, :orange, :purple, :cyan, :magenta, :yellow, :brown, :pink, :gray, :black]
color = ['red', 'green', 'blue', 'orange', 'purple', 'cyan', 'magenta', 'yellow', 'brown', 'pink', 'gray', 'black']

# tetra dodeca
for face, c in zip(tetra, color):
    face_center, body_center = calculate_centers(face)
    num_vertices = len(face)
    
    for i in range(num_vertices):
        v1 = face[i]
        v2 = face[(i + 1) % num_vertices]
        
        midpoint, third1, third2 = get_points(v1, v2)

        p1 = v1 + 0.2 * (face_center - v1)
        p2 = third1 + 0.05 * (body_center - third1)
        p3 = midpoint - 0.05 * (face_center - midpoint)
        p4 = third2 - 0.05 * (body_center - third2)
        p5 = v2 + 0.2 * (face_center - v2)

        curve1 = interpolate_curve([p1, p2, p3, p4, p5])
        ax.plot(curve1[:, 0], curve1[:, 1], curve1[:, 2], color=c, linewidth=2)

plt.show()

ps: 网上找到了这个 https://www.math.utoronto.ca/graduate/pce/algebra/Alg-1994Jan.pdf