140 lines
4.7 KiB
Python
140 lines
4.7 KiB
Python
"""
|
||
顶级期刊图表全局配置。
|
||
|
||
所有绘图脚本通过 `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')
|