""" 顶级期刊图表全局配置。 所有绘图脚本通过 `from plot_config import apply_style, COLORS, ...` 导入。 风格参考:中国宇航学报 / Automatica / IEEE TAC 等期刊惯例。 - 字体:Times New Roman(正文)+ STSong(中文) - LaTeX 渲染数学符号 - 输出格式:PDF(矢量)+ 300 dpi PNG(备用) """ import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np # ── 颜色方案 ──────────────────────────────────────────────────── # 使用色觉友好配色 (adapted from Tol's qualitative palette) COLORS = { 'blue': '#4477AA', 'cyan': '#66CCEE', 'green': '#228833', 'yellow': '#CCBB44', 'red': '#EE6677', 'purple': '#AA3377', 'grey': '#BBBBBB', 'black': '#000000', } # 用于消融实验对比的离散色板 ABLATION_COLORS = [COLORS['blue'], COLORS['red'], COLORS['green'], COLORS['yellow'], COLORS['purple']] # 控制通道颜色 CTRL_COLORS = { 'x': COLORS['red'], 'y': COLORS['blue'], 'z': COLORS['green'], } # ── 图幅尺寸 ──────────────────────────────────────────────────── # 中国期刊单栏约 8 cm (3.15 in),双栏约 16 cm (6.3 in) SINGLE_COL = 3.5 # inches DOUBLE_COL = 7.0 # inches GOLDEN = (1 + np.sqrt(5)) / 2 def figsize(cols=1, aspect=None): """返回 (width, height) 英寸,cols=1 单栏, cols=2 双栏。""" w = SINGLE_COL if cols == 1 else DOUBLE_COL if aspect is None: aspect = 1.0 / GOLDEN return (w, w * aspect) # ── 全局样式 ───────────────────────────────────────────────────── def apply_style(): """应用顶级期刊全局 matplotlib 样式。""" # 尝试加载中文字体(如可用) import matplotlib.font_manager as fm _cjk_fonts = ['SimSun', 'STSong', 'Songti SC', 'Noto Serif CJK SC'] _found_cjk = None for fn in _cjk_fonts: if any(fn.lower() in f.name.lower() for f in fm.fontManager.ttflist): _found_cjk = fn break serif_list = ['Times New Roman'] if _found_cjk: serif_list.append(_found_cjk) plt.rcParams.update({ # 字体:使用 serif + 中文后备字体 'font.family': 'serif', 'font.serif': serif_list, 'font.sans-serif': ['Arial', 'Microsoft YaHei', 'SimHei'], 'font.size': 8, 'axes.titlesize': 9, 'axes.labelsize': 8, 'xtick.labelsize': 7, 'ytick.labelsize': 7, 'legend.fontsize': 7, # LaTeX 'text.usetex': False, # 使用 mathtext 避免依赖 LaTeX 'mathtext.fontset': 'cm', 'axes.unicode_minus': False, # 防止负号显示问题 # 线条 'lines.linewidth': 1.0, 'lines.markersize': 3, 'patch.linewidth': 0.5, # 坐标轴 'axes.linewidth': 0.6, 'axes.grid': False, 'axes.spines.top': False, 'axes.spines.right': False, # 刻度 'xtick.direction': 'in', 'ytick.direction': 'in', 'xtick.major.width': 0.5, 'ytick.major.width': 0.5, 'xtick.major.size': 3, 'ytick.major.size': 3, 'xtick.minor.visible': False, 'ytick.minor.visible': False, # 图例 'legend.frameon': True, 'legend.framealpha': 0.9, 'legend.edgecolor': '0.8', 'legend.borderpad': 0.3, 'legend.handlelength': 1.5, # 保存 'savefig.dpi': 300, 'savefig.bbox': 'tight', 'savefig.pad_inches': 0.02, # 图形 'figure.dpi': 150, 'figure.constrained_layout.use': True, }) def save_fig(fig, name, out_dir='Plots'): """同时保存 PDF(矢量)和 PNG(位图)。""" import os os.makedirs(out_dir, exist_ok=True) fig.savefig(os.path.join(out_dir, f'{name}.pdf'), format='pdf') fig.savefig(os.path.join(out_dir, f'{name}.png'), format='png', dpi=300) print(f" ✓ saved {name}.pdf / .png → {out_dir}/") def add_subfig_label(ax, label, x=-0.12, y=1.08): """在子图左上角添加 (a), (b) 等标签。""" ax.text(x, y, f'({label})', transform=ax.transAxes, fontsize=9, fontweight='bold', va='top', ha='right')