import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.interpolate import griddata
from matplotlib.colors import LinearSegmentedColormap, Normalize
import os
from matplotlib.colorbar import ColorbarBase
def generate_multi_fsim_surfaces():
# 1. 数据加载部分
print("从Excel加载原始数据...")
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
excel_path = os.path.join(script_dir, 'fsim_data.xlsx')
df = pd.read_excel(excel_path)
# 验证必要列存在
required_columns = ['BOW', 'WAVE']
if not all(col in df.columns for col in required_columns):
missing = [col for col in required_columns if col not in df.columns]
raise ValueError(f"Excel文件缺少必要的列: {missing}")
# 计算BOW列的最小值和最大值
bow_min = df['BOW'].min()
bow_max = df['BOW'].max()
# 添加10%的边距
bow_range = bow_max - bow_min
x_min = max(0.1, bow_min - bow_range * 0.1) # 确保最小值不小于0.1
x_max = bow_max + bow_range * 0.1
print(f"BOW数据范围: {bow_min:.2f} 到 {bow_max:.2f}")
print(f"设置X轴范围: {x_min:.2f} 到 {x_max:.2f}")
# 定义所有可能的曲面描述信息
column_descriptions = {
'Fsim_E1': '0.5mm Edge deformation',
'Fsim_E2': '1.0mm Edge deformation',
'Fsim_E3': '1.5mm Edge deformation',
'Fsim_E4': '1.0mm Edge deformation',
'Fsim_E5': '2.5mm Edge deformation',
'Fsim_E6': '3.0mm Edge deformation',
'Fsim_DP': 'recommended range'
}
# 找出所有Fsim列并验证描述信息
fsim_columns = [col for col in df.columns if col.startswith('Fsim')]
# 检查是否有未定义的列并添加默认描述
missing_descriptions = [col for col in fsim_columns if col not in column_descriptions]
for col in missing_descriptions:
column_descriptions[col] = f'custom Fsim metric ({col})'
print(f"警告: 为未定义的列添加默认描述: {col} -> {column_descriptions[col]}")
if not fsim_columns:
raise ValueError("未找到任何Fsim列")
print(f"找到 {len(fsim_columns)} 个Fsim列: {', '.join(fsim_columns)}")
points = df[['BOW', 'WAVE']].values
fsim_values = {col: df[col].values for col in fsim_columns}
# 计算全局Fsim范围
all_fsim_min = min(df[col].min() for col in fsim_columns)
all_fsim_max = max(df[col].max() for col in fsim_columns)
z_padding = (all_fsim_max - all_fsim_min) * 0.1
z_min = all_fsim_min - z_padding
z_max = all_fsim_max + z_padding
print(f"全局Fsim范围: {all_fsim_min:.4f} 到 {all_fsim_max:.4f}")
print(f"设置Z轴范围: {z_min:.4f} 到 {z_max:.4f}")
except FileNotFoundError:
print(f"错误: 找不到数据文件 {excel_path}")
print("请确保fsim_data.xlsx文件位于脚本同一目录下")
return
except Exception as e:
print(f"加载数据时出错: {str(e)}")
return
# 2. 创建高分辨率网格
print("创建高分辨率网格...")
# 使用动态计算的x轴范围
x_fine = np.linspace(x_min, x_max, 150) # 使用动态范围
y_fine = np.linspace(0.1, 1.0, 100)
X_fine, Y_fine = np.meshgrid(x_fine, y_fine)
# 3. 创建3D图表
print("创建3D图表...")
fig = plt.figure(figsize=(18, 14))
ax = fig.add_axes([0.1, 0.07, 0.7, 0.83], projection='3d')
# 4. 定义全局颜色映射
print("创建颜色映射...")
global_cmap = LinearSegmentedColormap.from_list(
"global_cmap",
[(0.1, 0.2, 0.8), (0.2, 0.8, 0.4), (1.0, 0.9, 0.1)],
N=512
)
global_norm = Normalize(vmin=all_fsim_min, vmax=all_fsim_max)
alphas = [0.65, 0.55, 0.45, 0.35, 0.25, 0.15]
# 5. 曲面绘制和标签添加
print(f"开始处理 {len(fsim_columns)} 个曲面并添加标签...")
# 查找固定点(BOW=1, WAVE=1)的网格索引
bow_target = 1.0
wave_target = 1.0
# 如果固定点不在范围内,使用平均值
if bow_target < x_min or bow_target > x_max:
bow_target = (x_min + x_max) / 2
print(f"警告: BOW固定点超出范围,调整为中点值: {bow_target:.2f}")
bow_idx = np.abs(x_fine - bow_target).argmin()
wave_idx = np.abs(y_fine - wave_target).argmin()
# 预计算所有曲面在固定点的Z值
fixed_point_z = {}
for col_name in fsim_columns:
Z_fine = griddata(points, fsim_values[col_name], (X_fine, Y_fine), method='cubic')
fixed_point_z[col_name] = Z_fine[wave_idx, bow_idx]
# 按固定点Z值降序排列(从高到低)- 确保 Fsim_DP 最后绘制
fsim_columns_without_dp = [col for col in fsim_columns if col != 'Fsim_DP']
sorted_columns = sorted(fsim_columns_without_dp, key=lambda col: fixed_point_z[col], reverse=True)
# 如果有 Fsim_DP,则添加到列表末尾以确保最后绘制(最上层)
if 'Fsim_DP' in fsim_columns:
sorted_columns.append('Fsim_DP')
# 记录标签位置和颜色
label_positions = []
label_colors = {}
# 计算最小间距防止标签重叠
min_spacing = 0.035
base_offset = 0.015
# 第一遍:确定所有标签位置
last_label_z = float('-inf')
label_z_values = {}
max_label_z = float('-inf')
for col_name in sorted_columns:
base_z = fixed_point_z[col_name]
label_z = base_z + base_offset
# 防止标签重叠
if label_z <= last_label_z + min_spacing:
label_z = last_label_z + min_spacing
last_label_z = label_z
label_z_values[col_name] = label_z
if label_z > max_label_z:
max_label_z = label_z
# 第二遍:绘制曲面和标签
for i, col_name in enumerate(sorted_columns):
values = fsim_values[col_name]
print(f"正在处理列: {col_name} ({i+1}/{len(sorted_columns)})")
# 执行插值
print(" 执行插值...")
Z_fine = griddata(points, values, (X_fine, Y_fine), method='cubic')
# 曲面着色 - 为 Fsim_DP 使用单一红色
if col_name == 'Fsim_DP':
facecolors = np.zeros((Z_fine.shape[0]-1, Z_fine.shape[1]-1, 4))
facecolors[:, :, 0] = 1.0 # R
facecolors[:, :, 1] = 0.0 # G
facecolors[:, :, 2] = 0.0 # B
else:
# 常规着色方式
Z_top_left = Z_fine[:-1, :-1]
Z_top_right = Z_fine[:-1, 1:]
Z_bottom_left = Z_fine[1:, :-1]
Z_bottom_right = Z_fine[1:, 1:]
Z_midpoints = (Z_top_left + Z_top_right + Z_bottom_left + Z_bottom_right) / 4.0
facecolors = global_cmap(global_norm(Z_midpoints))
# 设置透明度
alpha_idx = min(i, len(alphas)-1)
alpha = alphas[alpha_idx]
facecolors[:, :, 3] = alpha
print(f" 渲染曲面 (透明度: {alpha:.2f})...")
# 优化渲染密度
stride_x = 1
stride_y = 1
if len(x_fine) > 100:
render_density = max(1, int(len(x_fine)/75))
stride_x = render_density
stride_y = max(1, int(render_density/1.5))
# 绘制曲面
surf = ax.plot_surface(
X_fine[::stride_y, ::stride_x],
Y_fine[::stride_y, ::stride_x],
Z_fine[::stride_y, ::stride_x],
facecolors=facecolors[::stride_y, ::stride_x],
shade=False,
rcount=min(300, len(y_fine)),
ccount=min(300, len(x_fine)),
alpha=alpha,
antialiased=True,
edgecolor='black',
linewidth=0.1,
zorder=len(fsim_columns)-i
)
# 添加曲面标签
print(" 添加曲面标签...")
label_x = X_fine[wave_idx, bow_idx]
label_y = Y_fine[wave_idx, bow_idx]
base_z = fixed_point_z[col_name]
# 为 Fsim_DP 使用红色标签,其他使用基于值的颜色
if col_name == 'Fsim_DP':
text_color = (1.0, 0.0, 0.0) # 纯红色
else:
normalized_value = global_norm(base_z)
text_color = global_cmap(normalized_value)[:3] # RGB颜色
# 存储标签颜色
label_colors[col_name] = text_color
# 使用计算的标签高度
label_z = label_z_values[col_name]
# 添加标签
ax.text(
label_x, label_y, label_z,
" " + col_name,
fontsize=11,
color=text_color,
fontweight='bold',
ha='left',
va='center',
zorder=100
)
label_positions.append((label_x, label_y, label_z))
# 6. 调整Z轴上限以容纳标签
if max_label_z > z_max:
print(f"调整Z轴上限以容纳标签: 原上限 {z_max:.4f}, 新上限 {max_label_z + 0.03:.4f}")
z_max = max_label_z + 0.03
# 7. 添加全局注释(确保位置合适)
print("添加全局注释...")
annotation_x = 0.90 # 右侧位置
annotation_y = 0.95 # 顶部位置,避免遮挡
# 添加带颜色的注释文本
for i, col in enumerate(fsim_columns):
# 使用预定义的描述信息
description = column_descriptions[col]
text = f"{col}: {description}"
text_y = annotation_y - i * 0.025 # 从上到下排列
# 使用对应的标签颜色
color = label_colors.get(col, 'darkblue')
# 添加注释文本(无背景框)
plt.figtext(
annotation_x, text_y,
text,
fontsize=12,
color=color,
ha='right',
va='top', # 顶部对齐
zorder=100,
fontweight='bold'
)
# 8. 坐标轴设置
ax.set_xlabel('BOW (‰)', fontsize=15, labelpad=25)
ax.set_ylabel('WAVE (‰)', fontsize=15, labelpad=25)
ax.set_zlabel('Fsim', fontsize=15, labelpad=25)
title = ax.set_title(
f'Multi-Fsim Analysis ({len(fsim_columns)} Surfaces)',
fontsize=20,
pad=15,
y=0.98
)
# 自动计算合适的x轴刻度
num_ticks = min(6, max(3, int((x_max - x_min) / 0.5) + 1))
ax.set_xticks(np.linspace(x_min, x_max, num_ticks))
ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
z_step = 0.05
z_min_rounded = np.floor(z_min / z_step) * z_step
z_max_rounded = np.ceil(z_max / z_step) * z_step
z_ticks = np.arange(z_min_rounded, z_max_rounded + z_step/2, z_step)
if len(z_ticks) > 10:
step_idx = max(1, int(len(z_ticks) / 5))
z_ticks = z_ticks[::step_idx]
ax.set_zticks(z_ticks)
ax.set_zticklabels([f"{z:.2f}" for z in z_ticks])
ax.tick_params(axis='both', which='major', labelsize=11, pad=10)
# 9. 网格和平面设置
print("添加背景网格线...")
grid_params = {
'visible': True,
'linestyle': '--',
'linewidth': 0.5,
'alpha': 0.2,
'color': 'gray'
}
ax.xaxis._axinfo["grid"].update(grid_params)
ax.yaxis._axinfo["grid"].update(grid_params)
ax.zaxis.axinfo["grid"].update(grid_params)
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.pane.set_edgecolor('lightgray')
ax.yaxis.pane.set_edgecolor('lightgray')
ax.zaxis.pane.set_edgecolor('lightgray')
ax.xaxis.pane.set_alpha(0.05)
ax.yaxis.pane.set_alpha(0.05)
ax.zaxis.pane.set_alpha(0.05)
# 10. 视图设置
print("配置轴测图投影...")
ax.set_proj_type('ortho')
ax.view_init(elev=32, azim=42)
ax.set_box_aspect(aspect=(1, 1, 1))
# 关键修改:设置视图范围(移除了额外的固定边距)
ax.set_xlim(x_min, x_max) # 直接使用动态计算的范围
ax.set_ylim(0.0, 1.1)
ax.set_zlim(z_min, z_max)
# 11. 添加全局颜色条
print("添加全局Fsim颜色条...")
cax = fig.add_axes([0.85, 0.25, 0.02, 0.5])
cbar = ColorbarBase(
cax,
cmap=global_cmap,
norm=global_norm,
orientation='vertical',
label='Fsim Value'
)
cbar.set_label('Fsim Value', fontsize=12, labelpad=15)
cbar.ax.tick_params(labelsize=10)
# 12. 保存和显示
print("保存多曲面轴测图...")
plt.savefig('multi_fsim_surfaces.png', dpi=350, bbox_inches='tight')
print("渲染完成,显示图表...")
plt.tight_layout(pad=3.0)
plt.show()
if __import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.interpolate import griddata
from matplotlib.colors import LinearSegmentedColormap, Normalize
import os
from matplotlib.colorbar import ColorbarBase
def generate_multi_fsim_surfaces():
# 1. 数据加载部分
print("从Excel加载原始数据...")
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
excel_path = os.path.join(script_dir, 'fsim_data.xlsx')
df = pd.read_excel(excel_path)
# 验证必要列存在
required_columns = ['BOW', 'WAVE']
if not all(col in df.columns for col in required_columns):
missing = [col for col in required_columns if col not in df.columns]
raise ValueError(f"Excel文件缺少必要的列: {missing}")
# 计算BOW列的最小值和最大值
bow_min = df['BOW'].min()
bow_max = df['BOW'].max()
# 添加10%的边距
bow_range = bow_max - bow_min
x_min = max(0.1, bow_min - bow_range * 0.1) # 确保最小值不小于0.1
x_max = bow_max + bow_range * 0.1
print(f"BOW数据范围: {bow_min:.2f} 到 {bow_max:.2f}")
print(f"设置X轴范围: {x_min:.2f} 到 {x_max:.2f}")
# 定义所有可能的曲面描述信息
column_descriptions = {
'Fsim_E1': '0.5mm Edge deformation',
'Fsim_E2': '1.0mm Edge deformation',
'Fsim_E3': '1.5mm Edge deformation',
'Fsim_E4': '1.0mm Edge deformation',
'Fsim_E5': '2.5mm Edge deformation',
'Fsim_E6': '3.0mm Edge deformation',
'Fsim_DP': 'recommended range'
}
# 找出所有Fsim列并验证描述信息
fsim_columns = [col for col in df.columns if col.startswith('Fsim')]
# 检查是否有未定义的列并添加默认描述
missing_descriptions = [col for col in fsim_columns if col not in column_descriptions]
for col in missing_descriptions:
column_descriptions[col] = f'custom Fsim metric ({col})'
print(f"警告: 为未定义的列添加默认描述: {col} -> {column_descriptions[col]}")
if not fsim_columns:
raise ValueError("未找到任何Fsim列")
print(f"找到 {len(fsim_columns)} 个Fsim列: {', '.join(fsim_columns)}")
points = df[['BOW', 'WAVE']].values
fsim_values = {col: df[col].values for col in fsim_columns}
# 计算全局Fsim范围
all_fsim_min = min(df[col].min() for col in fsim_columns)
all_fsim_max = max(df[col].max() for col in fsim_columns)
z_padding = (all_fsim_max - all_fsim_min) * 0.1
z_min = all_fsim_min - z_padding
z_max = all_fsim_max + z_padding
print(f"全局Fsim范围: {all_fsim_min:.4f} 到 {all_fsim_max:.4f}")
print(f"设置Z轴范围: {z_min:.4f} 到 {z_max:.4f}")
except FileNotFoundError:
print(f"错误: 找不到数据文件 {excel_path}")
print("请确保fsim_data.xlsx文件位于脚本同一目录下")
return
except Exception as e:
print(f"加载数据时出错: {str(e)}")
return
# 2. 创建高分辨率网格
print("创建高分辨率网格...")
# 使用动态计算的x轴范围
x_fine = np.linspace(x_min, x_max, 150) # 使用动态范围
y_fine = np.linspace(0.1, 1.0, 100)
X_fine, Y_fine = np.meshgrid(x_fine, y_fine)
# 3. 创建3D图表
print("创建3D图表...")
fig = plt.figure(figsize=(18, 14))
ax = fig.add_axes([0.1, 0.07, 0.7, 0.83], projection='3d')
# 4. 定义全局颜色映射
print("创建颜色映射...")
global_cmap = LinearSegmentedColormap.from_list(
"global_cmap",
[(0.1, 0.2, 0.8), (0.2, 0.8, 0.4), (1.0, 0.9, 0.1)],
N=512
)
global_norm = Normalize(vmin=all_fsim_min, vmax=all_fsim_max)
alphas = [0.65, 0.55, 0.45, 0.35, 0.25, 0.15]
# 5. 曲面绘制和标签添加
print(f"开始处理 {len(fsim_columns)} 个曲面并添加标签...")
# 查找固定点(BOW=1, WAVE=1)的网格索引
bow_target = 1.0
wave_target = 1.0
# 如果固定点不在范围内,使用平均值
if bow_target < x_min or bow_target > x_max:
bow_target = (x_min + x_max) / 2
print(f"警告: BOW固定点超出范围,调整为中点值: {bow_target:.2f}")
bow_idx = np.abs(x_fine - bow_target).argmin()
wave_idx = np.abs(y_fine - wave_target).argmin()
# 预计算所有曲面在固定点的Z值
fixed_point_z = {}
for col_name in fsim_columns:
Z_fine = griddata(points, fsim_values[col_name], (X_fine, Y_fine), method='cubic')
fixed_point_z[col_name] = Z_fine[wave_idx, bow_idx]
# 按固定点Z值降序排列(从高到低)- 确保 Fsim_DP 最后绘制
fsim_columns_without_dp = [col for col in fsim_columns if col != 'Fsim_DP']
sorted_columns = sorted(fsim_columns_without_dp, key=lambda col: fixed_point_z[col], reverse=True)
# 如果有 Fsim_DP,则添加到列表末尾以确保最后绘制(最上层)
if 'Fsim_DP' in fsim_columns:
sorted_columns.append('Fsim_DP')
# 记录标签位置和颜色
label_positions = []
label_colors = {}
# 计算最小间距防止标签重叠
min_spacing = 0.035
base_offset = 0.015
# 第一遍:确定所有标签位置
last_label_z = float('-inf')
label_z_values = {}
max_label_z = float('-inf')
for col_name in sorted_columns:
base_z = fixed_point_z[col_name]
label_z = base_z + base_offset
# 防止标签重叠
if label_z <= last_label_z + min_spacing:
label_z = last_label_z + min_spacing
last_label_z = label_z
label_z_values[col_name] = label_z
if label_z > max_label_z:
max_label_z = label_z
# 第二遍:绘制曲面和标签
for i, col_name in enumerate(sorted_columns):
values = fsim_values[col_name]
print(f"正在处理列: {col_name} ({i+1}/{len(sorted_columns)})")
# 执行插值
print(" 执行插值...")
Z_fine = griddata(points, values, (X_fine, Y_fine), method='cubic')
# 曲面着色 - 为 Fsim_DP 使用单一红色
if col_name == 'Fsim_DP':
facecolors = np.zeros((Z_fine.shape[0]-1, Z_fine.shape[1]-1, 4))
facecolors[:, :, 0] = 1.0 # R
facecolors[:, :, 1] = 0.0 # G
facecolors[:, :, 2] = 0.0 # B
else:
# 常规着色方式
Z_top_left = Z_fine[:-1, :-1]
Z_top_right = Z_fine[:-1, 1:]
Z_bottom_left = Z_fine[1:, :-1]
Z_bottom_right = Z_fine[1:, 1:]
Z_midpoints = (Z_top_left + Z_top_right + Z_bottom_left + Z_bottom_right) / 4.0
facecolors = global_cmap(global_norm(Z_midpoints))
# 设置透明度
alpha_idx = min(i, len(alphas)-1)
alpha = alphas[alpha_idx]
facecolors[:, :, 3] = alpha
print(f" 渲染曲面 (透明度: {alpha:.2f})...")
# 优化渲染密度
stride_x = 1
stride_y = 1
if len(x_fine) > 100:
render_density = max(1, int(len(x_fine)/75))
stride_x = render_density
stride_y = max(1, int(render_density/1.5))
# 绘制曲面
surf = ax.plot_surface(
X_fine[::stride_y, ::stride_x],
Y_fine[::stride_y, ::stride_x],
Z_fine[::stride_y, ::stride_x],
facecolors=facecolors[::stride_y, ::stride_x],
shade=False,
rcount=min(300, len(y_fine)),
ccount=min(300, len(x_fine)),
alpha=alpha,
antialiased=True,
edgecolor='black',
linewidth=0.1,
zorder=len(fsim_columns)-i
)
# 添加曲面标签
print(" 添加曲面标签...")
label_x = X_fine[wave_idx, bow_idx]
label_y = Y_fine[wave_idx, bow_idx]
base_z = fixed_point_z[col_name]
# 为 Fsim_DP 使用红色标签,其他使用基于值的颜色
if col_name == 'Fsim_DP':
text_color = (1.0, 0.0, 0.0) # 纯红色
else:
normalized_value = global_norm(base_z)
text_color = global_cmap(normalized_value)[:3] # RGB颜色
# 存储标签颜色
label_colors[col_name] = text_color
# 使用计算的标签高度
label_z = label_z_values[col_name]
# 添加标签
ax.text(
label_x, label_y, label_z,
" " + col_name,
fontsize=11,
color=text_color,
fontweight='bold',
ha='left',
va='center',
zorder=100
)
label_positions.append((label_x, label_y, label_z))
# 6. 调整Z轴上限以容纳标签
if max_label_z > z_max:
print(f"调整Z轴上限以容纳标签: 原上限 {z_max:.4f}, 新上限 {max_label_z + 0.03:.4f}")
z_max = max_label_z + 0.03
# 7. 添加全局注释(确保位置合适)
print("添加全局注释...")
annotation_x = 0.90 # 右侧位置
annotation_y = 0.95 # 顶部位置,避免遮挡
# 添加带颜色的注释文本
for i, col in enumerate(fsim_columns):
# 使用预定义的描述信息
description = column_descriptions[col]
text = f"{col}: {description}"
text_y = annotation_y - i * 0.025 # 从上到下排列
# 使用对应的标签颜色
color = label_colors.get(col, 'darkblue')
# 添加注释文本(无背景框)
plt.figtext(
annotation_x, text_y,
text,
fontsize=12,
color=color,
ha='right',
va='top', # 顶部对齐
zorder=100,
fontweight='bold'
)
# 8. 坐标轴设置
ax.set_xlabel('BOW (‰)', fontsize=15, labelpad=25)
ax.set_ylabel('WAVE (‰)', fontsize=15, labelpad=25)
ax.set_zlabel('Fsim', fontsize=15, labelpad=25)
title = ax.set_title(
f'Multi-Fsim Analysis ({len(fsim_columns)} Surfaces)',
fontsize=20,
pad=15,
y=0.98
)
# 自动计算合适的x轴刻度
num_ticks = min(6, max(3, int((x_max - x_min) / 0.5) + 1))
ax.set_xticks(np.linspace(x_min, x_max, num_ticks))
ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
z_step = 0.05
z_min_rounded = np.floor(z_min / z_step) * z_step
z_max_rounded = np.ceil(z_max / z_step) * z_step
z_ticks = np.arange(z_min_rounded, z_max_rounded + z_step/2, z_step)
if len(z_ticks) > 10:
step_idx = max(1, int(len(z_ticks) / 5))
z_ticks = z_ticks[::step_idx]
ax.set_zticks(z_ticks)
ax.set_zticklabels([f"{z:.2f}" for z in z_ticks])
ax.tick_params(axis='both', which='major', labelsize=11, pad=10)
# 9. 网格和平面设置
print("添加背景网格线...")
grid_params = {
'visible': True,
'linestyle': '--',
'linewidth': 0.5,
'alpha': 0.2,
'color': 'gray'
}
ax.xaxis._axinfo["grid"].update(grid_params)
ax.yaxis._axinfo["grid"].update(grid_params)
ax.zaxis.axinfo["grid"].update(grid_params)
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.pane.set_edgecolor('lightgray')
ax.yaxis.pane.set_edgecolor('lightgray')
ax.zaxis.pane.set_edgecolor('lightgray')
ax.xaxis.pane.set_alpha(0.05)
ax.yaxis.pane.set_alpha(0.05)
ax.zaxis.pane.set_alpha(0.05)
# 10. 视图设置
print("配置轴测图投影...")
ax.set_proj_type('ortho')
ax.view_init(elev=32, azim=42)
ax.set_box_aspect(aspect=(1, 1, 1))
# 关键修改:设置视图范围(移除了额外的固定边距)
ax.set_xlim(x_min, x_max) # 直接使用动态计算的范围
ax.set_ylim(0.0, 1.1)
ax.set_zlim(z_min, z_max)
# 11. 添加全局颜色条
print("添加全局Fsim颜色条...")
cax = fig.add_axes([0.85, 0.25, 0.02, 0.5])
cbar = ColorbarBase(
cax,
cmap=global_cmap,
norm=global_norm,
orientation='vertical',
label='Fsim Value'
)
cbar.set_label('Fsim Value', fontsize=12, labelpad=15)
cbar.ax.tick_params(labelsize=10)
# 12. 保存和显示
print("保存多曲面轴测图...")
plt.savefig('multi_fsim_surfaces.png', dpi=350, bbox_inches='tight')
print("渲染完成,显示图表...")
plt.tight_layout(pad=3.0)
plt.show()
if __name__ == "__main__":
generate_multi_fsim_surfaces()
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.interpolate import griddata
from matplotlib.colors import LinearSegmentedColormap, Normalize
import os
from matplotlib.colorbar import ColorbarBase
def generate_multi_fsim_surfaces():
# 1. 数据加载部分
print("从Excel加载原始数据...")
try:
script_dir = os.path.dirname(os.path.abspath(__file__))
excel_path = os.path.join(script_dir, 'fsim_data.xlsx')
df = pd.read_excel(excel_path)
# 验证必要列存在
required_columns = ['BOW', 'WAVE']
if not all(col in df.columns for col in required_columns):
missing = [col for col in required_columns if col not in df.columns]
raise ValueError(f"Excel文件缺少必要的列: {missing}")
# 计算BOW列的最小值和最大值
bow_min = df['BOW'].min()
bow_max = df['BOW'].max()
# 添加10%的边距
bow_range = bow_max - bow_min
x_min = max(0.1, bow_min - bow_range * 0.1) # 确保最小值不小于0.1
x_max = bow_max + bow_range * 0.1
print(f"BOW数据范围: {bow_min:.2f} 到 {bow_max:.2f}")
print(f"设置X轴范围: {x_min:.2f} 到 {x_max:.2f}")
# 定义所有可能的曲面描述信息
column_descriptions = {
'Fsim_E1': '0.5mm Edge deformation',
'Fsim_E2': '1.0mm Edge deformation',
'Fsim_E3': '1.5mm Edge deformation',
'Fsim_E4': '1.0mm Edge deformation',
'Fsim_E5': '2.5mm Edge deformation',
'Fsim_E6': '3.0mm Edge deformation',
'Fsim_DP': 'recommended range'
}
# 找出所有Fsim列并验证描述信息
fsim_columns = [col for col in df.columns if col.startswith('Fsim')]
# 检查是否有未定义的列并添加默认描述
missing_descriptions = [col for col in fsim_columns if col not in column_descriptions]
for col in missing_descriptions:
column_descriptions[col] = f'custom Fsim metric ({col})'
print(f"警告: 为未定义的列添加默认描述: {col} -> {column_descriptions[col]}")
if not fsim_columns:
raise ValueError("未找到任何Fsim列")
print(f"找到 {len(fsim_columns)} 个Fsim列: {', '.join(fsim_columns)}")
points = df[['BOW', 'WAVE']].values
fsim_values = {col: df[col].values for col in fsim_columns}
# 计算全局Fsim范围
all_fsim_min = min(df[col].min() for col in fsim_columns)
all_fsim_max = max(df[col].max() for col in fsim_columns)
z_padding = (all_fsim_max - all_fsim_min) * 0.1
z_min = all_fsim_min - z_padding
z_max = all_fsim_max + z_padding
print(f"全局Fsim范围: {all_fsim_min:.4f} 到 {all_fsim_max:.4f}")
print(f"设置Z轴范围: {z_min:.4f} 到 {z_max:.4f}")
except FileNotFoundError:
print(f"错误: 找不到数据文件 {excel_path}")
print("请确保fsim_data.xlsx文件位于脚本同一目录下")
return
except Exception as e:
print(f"加载数据时出错: {str(e)}")
return
# 2. 创建高分辨率网格
print("创建高分辨率网格...")
# 使用动态计算的x轴范围
x_fine = np.linspace(x_min, x_max, 150) # 使用动态范围
y_fine = np.linspace(0.1, 1.0, 100)
X_fine, Y_fine = np.meshgrid(x_fine, y_fine)
# 3. 创建3D图表
print("创建3D图表...")
fig = plt.figure(figsize=(18, 14))
ax = fig.add_axes([0.1, 0.07, 0.7, 0.83], projection='3d')
# 4. 定义全局颜色映射
print("创建颜色映射...")
global_cmap = LinearSegmentedColormap.from_list(
"global_cmap",
[(0.1, 0.2, 0.8), (0.2, 0.8, 0.4), (1.0, 0.9, 0.1)],
N=512
)
global_norm = Normalize(vmin=all_fsim_min, vmax=all_fsim_max)
alphas = [0.65, 0.55, 0.45, 0.35, 0.25, 0.15]
# 5. 曲面绘制和标签添加
print(f"开始处理 {len(fsim_columns)} 个曲面并添加标签...")
# 查找固定点(BOW=1, WAVE=1)的网格索引
bow_target = 1.0
wave_target = 1.0
# 如果固定点不在范围内,使用平均值
if bow_target < x_min or bow_target > x_max:
bow_target = (x_min + x_max) / 2
print(f"警告: BOW固定点超出范围,调整为中点值: {bow_target:.2f}")
bow_idx = np.abs(x_fine - bow_target).argmin()
wave_idx = np.abs(y_fine - wave_target).argmin()
# 预计算所有曲面在固定点的Z值
fixed_point_z = {}
for col_name in fsim_columns:
Z_fine = griddata(points, fsim_values[col_name], (X_fine, Y_fine), method='cubic')
fixed_point_z[col_name] = Z_fine[wave_idx, bow_idx]
# 按固定点Z值降序排列(从高到低)- 确保 Fsim_DP 最后绘制
fsim_columns_without_dp = [col for col in fsim_columns if col != 'Fsim_DP']
sorted_columns = sorted(fsim_columns_without_dp, key=lambda col: fixed_point_z[col], reverse=True)
# 如果有 Fsim_DP,则添加到列表末尾以确保最后绘制(最上层)
if 'Fsim_DP' in fsim_columns:
sorted_columns.append('Fsim_DP')
# 记录标签位置和颜色
label_positions = []
label_colors = {}
# 计算最小间距防止标签重叠
min_spacing = 0.035
base_offset = 0.015
# 第一遍:确定所有标签位置
last_label_z = float('-inf')
label_z_values = {}
max_label_z = float('-inf')
for col_name in sorted_columns:
base_z = fixed_point_z[col_name]
label_z = base_z + base_offset
# 防止标签重叠
if label_z <= last_label_z + min_spacing:
label_z = last_label_z + min_spacing
last_label_z = label_z
label_z_values[col_name] = label_z
if label_z > max_label_z:
max_label_z = label_z
# 第二遍:绘制曲面和标签
for i, col_name in enumerate(sorted_columns):
values = fsim_values[col_name]
print(f"正在处理列: {col_name} ({i+1}/{len(sorted_columns)})")
# 执行插值
print(" 执行插值...")
Z_fine = griddata(points, values, (X_fine, Y_fine), method='cubic')
# 曲面着色 - 为 Fsim_DP 使用单一红色
if col_name == 'Fsim_DP':
facecolors = np.zeros((Z_fine.shape[0]-1, Z_fine.shape[1]-1, 4))
facecolors[:, :, 0] = 1.0 # R
facecolors[:, :, 1] = 0.0 # G
facecolors[:, :, 2] = 0.0 # B
else:
# 常规着色方式
Z_top_left = Z_fine[:-1, :-1]
Z_top_right = Z_fine[:-1, 1:]
Z_bottom_left = Z_fine[1:, :-1]
Z_bottom_right = Z_fine[1:, 1:]
Z_midpoints = (Z_top_left + Z_top_right + Z_bottom_left + Z_bottom_right) / 4.0
facecolors = global_cmap(global_norm(Z_midpoints))
# 设置透明度
alpha_idx = min(i, len(alphas)-1)
alpha = alphas[alpha_idx]
facecolors[:, :, 3] = alpha
print(f" 渲染曲面 (透明度: {alpha:.2f})...")
# 优化渲染密度
stride_x = 1
stride_y = 1
if len(x_fine) > 100:
render_density = max(1, int(len(x_fine)/75))
stride_x = render_density
stride_y = max(1, int(render_density/1.5))
# 绘制曲面
surf = ax.plot_surface(
X_fine[::stride_y, ::stride_x],
Y_fine[::stride_y, ::stride_x],
Z_fine[::stride_y, ::stride_x],
facecolors=facecolors[::stride_y, ::stride_x],
shade=False,
rcount=min(300, len(y_fine)),
ccount=min(300, len(x_fine)),
alpha=alpha,
antialiased=True,
edgecolor='black',
linewidth=0.1,
zorder=len(fsim_columns)-i
)
# 添加曲面标签
print(" 添加曲面标签...")
label_x = X_fine[wave_idx, bow_idx]
label_y = Y_fine[wave_idx, bow_idx]
base_z = fixed_point_z[col_name]
# 为 Fsim_DP 使用红色标签,其他使用基于值的颜色
if col_name == 'Fsim_DP':
text_color = (1.0, 0.0, 0.0) # 纯红色
else:
normalized_value = global_norm(base_z)
text_color = global_cmap(normalized_value)[:3] # RGB颜色
# 存储标签颜色
label_colors[col_name] = text_color
# 使用计算的标签高度
label_z = label_z_values[col_name]
# 添加标签
ax.text(
label_x, label_y, label_z,
" " + col_name,
fontsize=11,
color=text_color,
fontweight='bold',
ha='left',
va='center',
zorder=100
)
label_positions.append((label_x, label_y, label_z))
# 6. 调整Z轴上限以容纳标签
if max_label_z > z_max:
print(f"调整Z轴上限以容纳标签: 原上限 {z_max:.4f}, 新上限 {max_label_z + 0.03:.4f}")
z_max = max_label_z + 0.03
# 7. 添加全局注释(确保位置合适)
print("添加全局注释...")
annotation_x = 0.90 # 右侧位置
annotation_y = 0.95 # 顶部位置,避免遮挡
# 添加带颜色的注释文本
for i, col in enumerate(fsim_columns):
# 使用预定义的描述信息
description = column_descriptions[col]
text = f"{col}: {description}"
text_y = annotation_y - i * 0.025 # 从上到下排列
# 使用对应的标签颜色
color = label_colors.get(col, 'darkblue')
# 添加注释文本(无背景框)
plt.figtext(
annotation_x, text_y,
text,
fontsize=12,
color=color,
ha='right',
va='top', # 顶部对齐
zorder=100,
fontweight='bold'
)
# 8. 坐标轴设置
ax.set_xlabel('BOW (‰)', fontsize=15, labelpad=25)
ax.set_ylabel('WAVE (‰)', fontsize=15, labelpad=25)
ax.set_zlabel('Fsim', fontsize=15, labelpad=25)
title = ax.set_title(
f'Multi-Fsim Analysis ({len(fsim_columns)} Surfaces)',
fontsize=20,
pad=15,
y=0.98
)
# 自动计算合适的x轴刻度
num_ticks = min(6, max(3, int((x_max - x_min) / 0.5) + 1))
ax.set_xticks(np.linspace(x_min, x_max, num_ticks))
ax.set_yticks([0.2, 0.4, 0.6, 0.8, 1.0])
z_step = 0.05
z_min_rounded = np.floor(z_min / z_step) * z_step
z_max_rounded = np.ceil(z_max / z_step) * z_step
z_ticks = np.arange(z_min_rounded, z_max_rounded + z_step/2, z_step)
if len(z_ticks) > 10:
step_idx = max(1, int(len(z_ticks) / 5))
z_ticks = z_ticks[::step_idx]
ax.set_zticks(z_ticks)
ax.set_zticklabels([f"{z:.2f}" for z in z_ticks])
ax.tick_params(axis='both', which='major', labelsize=11, pad=10)
# 9. 网格和平面设置
print("添加背景网格线...")
grid_params = {
'visible': True,
'linestyle': '--',
'linewidth': 0.5,
'alpha': 0.2,
'color': 'gray'
}
ax.xaxis._axinfo["grid"].update(grid_params)
ax.yaxis._axinfo["grid"].update(grid_params)
ax.zaxis.axinfo["grid"].update(grid_params)
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.pane.set_edgecolor('lightgray')
ax.yaxis.pane.set_edgecolor('lightgray')
ax.zaxis.pane.set_edgecolor('lightgray')
ax.xaxis.pane.set_alpha(0.05)
ax.yaxis.pane.set_alpha(0.05)
ax.zaxis.pane.set_alpha(0.05)
# 10. 视图设置
print("配置轴测图投影...")
ax.set_proj_type('ortho')
ax.view_init(elev=32, azim=42)
ax.set_box_aspect(aspect=(1, 1, 1))
# 关键修改:设置视图范围(移除了额外的固定边距)
ax.set_xlim(x_min, x_max) # 直接使用动态计算的范围
ax.set_ylim(0.0, 1.1)
ax.set_zlim(z_min, z_max)
# 11. 添加全局颜色条
print("添加全局Fsim颜色条...")
cax = fig.add_axes([0.85, 0.25, 0.02, 0.5])
cbar = ColorbarBase(
cax,
cmap=global_cmap,
norm=global_norm,
orientation='vertical',
label='Fsim Value'
)
cbar.set_label('Fsim Value', fontsize=12, labelpad=15)
cbar.ax.tick_params(labelsize=10)
# 12. 保存和显示
print("保存多曲面轴测图...")
plt.savefig('multi_fsim_surfaces.png', dpi=350, bbox_inches='tight')
print("渲染完成,显示图表...")
plt.tight_layout(pad=3.0)
plt.show()
if __name__ == "__main__":
generate_multi_fsim_surfaces()
name__ == "__main__":
generate_multi_fsim_surfaces()
将python中的x轴调整为固定的0~2,刻度是0.5设置一个,y轴调整为0~3,刻度是0.5设置一个,z轴调整为0.6~1.0,刻度式0.1设置一个,调整python代码