mcpField
S
scientific-schematics

scientific-schematics

github.com/jimmc414设计与 UI60550 次安装4 周前更新
代码仓库

关于 scientific-schematics

scientific-schematics 是用于生成科研示意图和图表的技能,面向论文、海报及技术文档作者。它按图表类型推荐 Python 绘图库,并指导输出矢量文件、检查质量及集成到文档中。

主要功能

  1. 01生成神经网络与系统架构图
  2. 02绘制流程图、电路图和生物通路
  3. 03按图表类型选择绘图库
  4. 04输出 SVG、PDF 或 EPS
  5. 05提供可访问性与出版规范建议

适用场景

  1. 01为论文制作模型架构图
  2. 02绘制研究流程图
  3. 03创建电路或生物通路示意图
  4. 04在 LaTeX 文档中引用图表

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jimmc414/kosmos --skill scientific-schematics

安装后在 Claude Code 中输入/scientific-schematics确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
block_diagram_template.tex
circuit_template.tex
flowchart_template.tex
pathway_template.tex
tikz_styles.tex
references/
best_practices.md
diagram_types.md
python_libraries.md
tikz_guide.md
scripts/
circuit_generator.py
compile_tikz.py
generate_flowchart.py
pathway_diagram.py

详细介绍

科学示意图与图表

概述

科学示意图与图表将复杂概念转化为清晰的视觉呈现,适用于发表。使用一流的 Python 库,生成神经网络架构、流程图、电路图、生物通路和系统图。所有图表均以 SVG/EPS 文件创建,存储在 figures/ 子文件夹中,并在论文/海报中引用——绝不直接嵌入 LaTeX。

零样本图表生成工作流

所有图表的标准工作流:

  1. 分析需求——确定图表类型和组成元素
  2. 选择最佳库——针对特定图表类型选择最合适的工具
  3. 生成矢量图形——创建间距和布局恰当的 SVG/EPS
  4. 存储在 figures/——将文件以描述性名称保存到 figures/ 子文件夹中
  5. 运行质量检查——确认没有重叠、对比度良好且分辨率恰当
  6. 在文档中引用——在 LaTeX 中使用 \includegraphics{figures/diagram_name.pdf}

**核心原则:**先生成独立的矢量图形,再将其整合到文档中。

何时使用此技能

在以下情况下应使用此技能:

  • 创建神经网络架构图(Transformer、CNN、RNN 等)
  • 绘制系统架构图和数据流图
  • 绘制研究设计的方法流程图(CONSORT、PRISMA)
  • 可视化算法工作流和处理管线
  • 创建电路图和电气示意图
  • 描绘生物通路和分子相互作用
  • 生成网络拓扑图和分层结构图
  • 绘制概念框架和理论模型
  • 为技术论文设计框图

各类图表的最佳库

根据图表类型选择最合适的库:

神经网络架构(Transformer、CNN 等)

**最佳库:**通过 Python 的 pygraphviz 或 pydot 使用 graphviz

  • 优秀的自动布局算法
  • 外观简洁、专业
  • 非常适合展示层堆叠和连接关系
  • 能很好地处理复杂的交叉连接

**替代方案:**通过精细定位自定义 matplotlib

  • 可更精确地控制放置位置
  • 更适合高度自定义的设计
  • 需要更多手动定位
流程图和过程图

**最佳库:**使用 dot 或 flowchart 布局的 graphviz

  • 自动优化位置
  • 标准流程图形状
  • 箭头路径清晰
  • 重叠问题极少

替代方案:diagrams 库(适用于云/系统架构风格)

电路图

最佳库:schemdraw

  • 专为电路设计
  • 丰富的元件库
  • 自动布线
  • 输出符合专业工程标准
生物通路

**最佳库:**使用自定义渲染的 networkx

  • 基于图的通路表示
  • 算法驱动布局
  • 节点/边样式灵活
框图和系统架构

最佳库:graphviz 或 diagrams

  • 清晰的分层布局
  • 自动调整间距
  • 外观专业

常见图表类型的零样本示例

示例 1:Transformer 架构(神经网络)

创建类似《Attention Is All You Need》中的 Transformer 编码器—解码器图:

import graphviz
from pathlib import Path

def create_transformer_diagram(output_dir='figures'):
    """
    Create a Transformer architecture diagram.
    Zero-shot generation with automatic layout.
    """
    Path(output_dir).mkdir(exist_ok=True)
    
    # Create directed graph with TB (top-to-bottom) layout
    dot = graphviz.Digraph(
        'transformer',
        format='pdf',
        graph_attr={
            'rankdir': 'BT',  # Bottom to top (like the original paper)
            'splines': 'ortho',  # Orthogonal edges
            'nodesep': '0.5',
            'ranksep': '0.8',
            'bgcolor': 'white',
            'dpi': '300'
        },
        node_attr={
            'shape': 'box',
            'style': 'rounded,filled',
            'fillcolor': 'lightgray',
            'fontname': 'Arial',
            'fontsize': '11',
            'width': '2.5',
            'height': '0.5'
        },
        edge_attr={
            'color': 'black',
            'penwidth': '1.5'
        }
    )
    
    # ENCODER STACK (left side)
    with dot.subgraph(name='cluster_encoder') as enc:
        enc.attr(label='Encoder', fontsize='14', fontname='Arial-Bold')
        enc.attr(style='rounded', color='blue', penwidth='2')
        
        # Encoder layers (bottom to top)
        enc.node('enc_input_emb', 'Input Embedding', fillcolor='#E8F4F8')
        enc.node('enc_pos', 'Positional Encoding', fillcolor='#E8F4F8')
        enc.node('enc_mha', 'Multi-Head\nAttention', fillcolor='#B3D9E6')
        enc.node('enc_an1', 'Add & Norm', fillcolor='#CCE5FF')
        enc.node('enc_ff', 'Feed Forward', fillcolor='#B3D9E6')
        enc.node('enc_an2', 'Add & Norm', fillcolor='#CCE5FF')
        
        # Encoder flow
        enc.edge('enc_input_emb', 'enc_pos')
        enc.edge('enc_pos', 'enc_mha')
        enc.edge('enc_mha', 'enc_an1')
        enc.edge('enc_an1', 'enc_ff')
        enc.edge('enc_ff', 'enc_an2')
    
    # DECODER STACK (right side)
    with dot.subgraph(name='cluster_decoder') as dec:
        dec.attr(label='Decoder', fontsize='14', fontname='Arial-Bold')
        dec.attr(style='rounded', color='red', penwidth='2')
        
        # Decoder layers (bottom to top)
        dec.node('dec_output_emb', 'Output Embedding', fillcolor='#FFE8E8')
        dec.node('dec_pos', 'Positional Encoding', fillcolor='#FFE8E8')
        dec.node('dec_mmha', 'Masked Self-\nAttention', fillcolor='#FFB3B3')
        dec.node('dec_an1', 'Add & Norm', fillcolor='#FFCCCC')
        dec.node('dec_cross', 'Cross-Attention', fillcolor='#FFB3B3')
        dec.node('dec_an2', 'Add & Norm', fillcolor='#FFCCCC')
        dec.node('dec_ff', 'Feed Forward', fillcolor='#FFB3B3')
        dec.node('dec_an3', 'Add & Norm', fillcolor='#FFCCCC')
        dec.node('dec_linear', 'Linear & Softmax', fillcolor='#FF9999')
        dec.node('dec_output', 'Output\nProbabilities', fillcolor='#FFE8E8')
        
        # Decoder flow
        dec.edge('dec_output_emb', 'dec_pos')
        dec.edge('dec_pos', 'dec_mmha')
        dec.edge('dec_mmha', 'dec_an1')
        dec.edge('dec_an1', 'dec_cross')
        dec.edge('dec_cross', 'dec_an2')
        dec.edge('dec_an2', 'dec_ff')
        dec.edge('dec_ff', 'dec_an3')
        dec.edge('dec_an3', 'dec_linear')
        dec.edge('dec_linear', 'dec_output')
    
    # Cross-attention connection (encoder to decoder)
    dot.edge('enc_an2', 'dec_cross', 
             style='dashed', 
             color='purple', 
             label='  context  ',
             fontsize='9')
    
    # Input and output labels
    dot.node('input_seq', 'Input Sequence', 
             shape='ellipse', fillcolor='lightgreen')
    dot.node('target_seq', 'Target Sequence', 
             shape='ellipse', fillcolor='lightgreen')
    
    dot.edge('input_seq', 'enc_input_emb')
    dot.edge('target_seq', 'dec_output_emb')
    
    # Render to files
    output_path = f'{output_dir}/transformer_architecture'
    dot.render(output_path, cleanup=True)
    
    # Also save as SVG and EPS
    dot.format = 'svg'
    dot.render(output_path, cleanup=True)
    dot.format = 'eps'
    dot.render(output_path, cleanup=True)
    
    print(f"✓ Transformer diagram created:")
    print(f"  - {output_path}.pdf")
    print(f"  - {output_path}.svg")
    print(f"  - {output_path}.eps")
    
    return f"{output_path}.pdf"

# Usage
if __name__ == '__main__':
    diagram_path = create_transformer_diagram('figures')
    
    # Run quality checks
    from quality_checker import run_quality_checks
    run_quality_checks(diagram_path.replace('.pdf', '.png'))

LaTeX 集成:

\begin{figure}[htbp]
\centering
\includegraphics[width=0.9\textwidth]{figures/transformer_architecture.pdf}
\caption{Transformer encoder-decoder architecture showing multi-head attention,
         feed-forward layers, and cross-attention mechanism.}
\label{fig:transformer}
\end{figure}
示例 2:简单流程图(CONSORT 风格)
import graphviz
from pathlib import Path

def create_consort_flowchart(output_dir='figures'):
    """Create a CONSORT participant flow diagram."""
    Path(output_dir).mkdir(exist_ok=True)
    
    dot = graphviz.Digraph(
        'consort',
        format='pdf',
        graph_attr={
            'rankdir': 'TB',
            'splines': 'ortho',
            'nodesep': '0.6',
            'ranksep': '0.8',
            'bgcolor': 'white'
        },
        node_attr={
            'shape': 'box',
            'style': 'rounded,filled',
            'fillcolor': '#E8F4F8',
            'fontname': 'Arial',
            'fontsize': '10',
            'width': '3',
            'height': '0.6'
        }
    )
    
    # Enrollment
    dot.node('assessed', 'Assessed for eligibility\n(n=500)')
    dot.node('excluded', 'Excluded (n=150)\n• Age < 18: n=80\n• Declined: n=50\n• Other: n=20')
    dot.node('randomized', 'Randomized\n(n=350)')
    
    # Allocation
    dot.node('treatment', 'Allocated to treatment\n(n=175)', fillcolor='#C8E6C9')
    dot.node('control', 'Allocated to control\n(n=175)', fillcolor='#FFECB3')
    
    # Follow-up
    dot.node('treat_lost', 'Lost to follow-up (n=15)', fillcolor='#FFCDD2')
    dot.node('ctrl_lost', 'Lost to follow-up (n=10)', fillcolor='#FFCDD2')
    
    # Analysis
    dot.node('treat_analyzed', 'Analyzed (n=160)', fillcolor='#C8E6C9')
    dot.node('ctrl_analyzed', 'Analyzed (n=165)', fillcolor='#FFECB3')
    
    # Connect nodes
    dot.edge('assessed', 'excluded')
    dot.edge('assessed', 'randomized')
    dot.edge('randomized', 'treatment')
    dot.edge('randomized', 'control')
    dot.edge('treatment', 'treat_lost')
    dot.edge('treatment', 'treat_analyzed')
    dot.edge('control', 'ctrl_lost')
    dot.edge('control', 'ctrl_analyzed')
    
    # Render
    output_path = f'{output_dir}/consort_flowchart'
    dot.render(output_path, cleanup=True)
    
    print(f"✓ CONSORT flowchart created: {output_path}.pdf")
    return f"{output_path}.pdf"
示例 3:CNN 架构
def create_cnn_architecture(output_dir='figures'):
    """Create a CNN architecture diagram."""
    dot = graphviz.Digraph(
        'cnn',
        format='pdf',
        graph_attr={'rankdir': 'LR', 'bgcolor': 'white'}
    )
    
    # Define layers
    layers = [
        ('input', 'Input\n32×32×3', '#FFE8E8'),
        ('conv1', 'Conv 3×3\n32 filters', '#B3D9E6'),
        ('pool1', 'MaxPool\n2×2', '#FFE5B3'),
        ('conv2', 'Conv 3×3\n64 filters', '#B3D9E6'),
        ('pool2', 'MaxPool\n2×2', '#FFE5B3'),
        ('flatten', 'Flatten', '#D4E8D4'),
        ('fc1', 'FC 128', '#C8B3E6'),
        ('fc2', 'FC 10', '#C8B3E6'),
        ('softmax', 'Softmax', '#FFC8C8')
    ]
    
    # Create nodes
    for node_id, label, color in layers:
        dot.node(node_id, label, 
                shape='box', style='rounded,filled', 
                fillcolor=color, fontname='Arial')
    
    # Connect layers
    for i in range(len(layers) - 1):
        dot.edge(layers[i][0], layers[i+1][0])
    
    output_path = f'{output_dir}/cnn_architecture'
    dot.render(output_path, cleanup=True)
    
    print(f"✓ CNN diagram created: {output_path}.pdf")
    return f"{output_path}.pdf"

核心功能

1. 支持的图表类型

神经网络架构

  • Transformer 编码器-解码器模型
  • 卷积神经网络(CNN)
  • 循环网络(LSTM、GRU)
  • 注意力机制及其变体
  • 自定义深度学习架构

方法学流程图

  • CONSORT 参与者流程图
  • PRISMA 系统综述流程图
  • 数据处理流水线
  • 算法工作流
  • 受试者招募流程

电路图

  • 模拟和数字电子电路
  • 信号处理框图
  • 控制系统图

生物学图表

  • 信号通路
  • 代谢通路图
  • 基因调控网络
  • 蛋白质相互作用网络

系统架构图

  • 软件架构和组件
  • 数据流图
  • 网络拓扑图
  • 层级组织结构图

所需库及安装

主要库:Graphviz(推荐用于 90% 的图表)

Graphviz 凭借自动布局、清晰渲染和保证不重叠的特性,是绘制大多数科学图表的最佳工具。

安装:

# Install Graphviz binary (required)
# macOS
brew install graphviz

# Ubuntu/Debian
sudo apt-get install graphviz

# Install Python bindings
pip install graphviz

Graphviz 的优势:

  • ✓ 自动优化布局(无需手动定位)
  • ✓ 布局算法保证元素不会重叠
  • ✓ 开箱即用的专业外观
  • ✓ 支持复杂的层级结构和交叉连接
  • ✓ 原生支持 SVG、PDF、EPS 输出
  • ✓ 用最少代码实现最高质量
专用库

Schemdraw - 仅用于电路图

pip install schemdraw

NetworkX - 复杂网络分析和可视化

pip install networkx matplotlib

Matplotlib - 自定义手动绘图(需要精确控制时使用)

pip install matplotlib

零样本图表绘制快速入门指南

绘制任何类型的图表时,请遵循以下系统化步骤:

步骤 1:识别图表结构

请思考:

  • 这是层级结构吗? → 使用 rankdir='TB' 或 'BT'(从上到下或从下到上)
  • 这是序列结构吗? → 使用 rankdir='LR'(从左到右)
  • 它有并行分支吗? → 使用子图/集群
  • 它有交叉连接吗? → Graphviz 会自动处理
步骤 2:设置基础模板

从此模板开始并进行自定义:

import graphviz
from pathlib import Path

def create_diagram(output_dir='figures', diagram_name='my_diagram'):
    """Universal diagram creation template."""
    Path(output_dir).mkdir(exist_ok=True, parents=True)
    
    dot = graphviz.Digraph(
        name=diagram_name,
        format='pdf',
        graph_attr={
            'rankdir': 'TB',      # TB, BT, LR, or RL
            'splines': 'ortho',   # ortho (straight) or curved
            'nodesep': '0.6',     # horizontal spacing
            'ranksep': '0.8',     # vertical spacing
            'bgcolor': 'white',
            'dpi': '300'
        },
        node_attr={
            'shape': 'box',       # box, ellipse, diamond, etc.
            'style': 'rounded,filled',
            'fillcolor': 'lightgray',
            'fontname': 'Arial',
            'fontsize': '11',
            'margin': '0.2',
            'width': '2',         # minimum width
            'height': '0.5'       # minimum height
        },
        edge_attr={
            'color': 'black',
            'penwidth': '1.5',
            'arrowsize': '0.8'
        }
    )
    
    # Add your nodes and edges here
    dot.node('node1', 'Label 1')
    dot.node('node2', 'Label 2')
    dot.edge('node1', 'node2')
    
    # Render to multiple formats
    output_path = f'{output_dir}/{diagram_name}'
    dot.render(output_path, cleanup=True)  # PDF
    dot.format = 'svg'
    dot.render(output_path, cleanup=True)  # SVG
    dot.format = 'eps'
    dot.render(output_path, cleanup=True)  # EPS
    
    print(f"✓ Diagram saved: {output_path}.{{pdf,svg,eps}}")
    return f"{output_path}.pdf"
步骤 3:添加带有清晰标签的节点

最佳实践:

  • 使用描述性节点 ID:'encoder_layer1',而不是 'n1'
  • 使用 \n 添加多行标签
  • 使用填充颜色对相关组件进行分组
  • 标签保持简洁(每行最多 3-5 个词)
# Good node definitions
dot.node('input_layer', 'Input Layer\n(512 dims)', fillcolor='#E8F4F8')
dot.node('attention', 'Multi-Head\nAttention', fillcolor='#B3D9E6')
dot.node('output', 'Output', fillcolor='#C8E6C9')
步骤 4:使用边连接节点

边类型:

# Standard arrow
dot.edge('node1', 'node2')

# Dashed line (for information flow)
dot.edge('encoder', 'decoder', style='dashed')

# Bidirectional
dot.edge('node1', 'node2', dir='both')

# With label
dot.edge('layer1', 'layer2', label='  ReLU  ')

# Different color
dot.edge('input', 'output', color='red', penwidth='2')
步骤 5:使用子图进行分组

用于并行结构(如 Encoder/Decoder):

# Encoder cluster
with dot.subgraph(name='cluster_encoder') as enc:
    enc.attr(label='Encoder', style='rounded', color='blue')
    enc.node('enc1', 'Encoder Layer 1')
    enc.node('enc2', 'Encoder Layer 2')
    enc.edge('enc1', 'enc2')

# Decoder cluster
with dot.subgraph(name='cluster_decoder') as dec:
    dec.attr(label='Decoder', style='rounded', color='red')
    dec.node('dec1', 'Decoder Layer 1')
    dec.node('dec2', 'Decoder Layer 2')
    dec.edge('dec1', 'dec2')

# Cross-connection between clusters
dot.edge('enc2', 'dec1', style='dashed', color='purple')
步骤 6:渲染并验证
# Always render to PDF (for LaTeX) and SVG (for web/slides)
output_path = f'{output_dir}/{diagram_name}'

# PDF for papers
dot.format = 'pdf'
dot.render(output_path, cleanup=True)

# SVG for posters/slides
dot.format = 'svg'
dot.render(output_path, cleanup=True)

# EPS for some journals
dot.format = 'eps'
dot.render(output_path, cleanup=True)

Graphviz 常用属性速查

图属性(整体布局)
graph_attr={
    'rankdir': 'TB',        # Direction: TB, BT, LR, RL
    'splines': 'ortho',     # Edge style: ortho, curved, line, polyline
    'nodesep': '0.5',       # Space between nodes (inches)
    'ranksep': '0.8',       # Space between ranks (inches)
    'bgcolor': 'white',     # Background color
    'dpi': '300',           # Resolution for raster output
    'compound': 'true',     # Allow edges between clusters
    'concentrate': 'true'   # Merge multiple edges
}
节点属性(方框/形状)
node_attr={
    'shape': 'box',         # box, ellipse, circle, diamond, plaintext
    'style': 'rounded,filled',  # rounded, filled, dashed, bold
    'fillcolor': '#E8F4F8', # Fill color (hex or name)
    'color': 'black',       # Border color
    'penwidth': '1.5',      # Border width
    'fontname': 'Arial',    # Font family
    'fontsize': '11',       # Font size (points)
    'fontcolor': 'black',   # Text color
    'width': '2',           # Minimum width (inches)
    'height': '0.5',        # Minimum height (inches)
    'margin': '0.2'         # Internal padding
}
边属性(箭头/连接)
edge_attr={
    'color': 'black',       # Line color
    'penwidth': '1.5',      # Line width
    'style': 'solid',       # solid, dashed, dotted, bold
    'arrowsize': '1.0',     # Arrow head size
    'dir': 'forward',       # forward, back, both, none
    'arrowhead': 'normal'   # normal, vee, diamond, dot, none
}

色觉障碍友好型调色板

使用以下配色,确保内容易于访问:

Okabe-Ito 调色板(8 种颜色)
OKABE_ITO = {
    'orange': '#E69F00',
    'sky_blue': '#56B4E9',
    'green': '#009E73',
    'yellow': '#F0E442',
    'blue': '#0072B2',
    'vermillion': '#D55E00',
    'purple': '#CC79A7',
    'black': '#000000'
}
浅色背景(用于填充节点)
LIGHT_FILLS = {
    'blue': '#E8F4F8',
    'green': '#E8F5E9',
    'orange': '#FFF3E0',
    'purple': '#F3E5F5',
    'red': '#FFEBEE',
    'yellow': '#FFFDE7',
    'gray': '#F5F5F5'
}
4. 出版标准

所有图表均遵循科学出版最佳实践:

矢量格式输出

  • 用于 LaTeX 集成的 PDF(首选)
  • 用于网页和演示文稿的 SVG
  • 用于传统出版系统的 EPS
  • 高分辨率 PNG 作为备用格式(300+ DPI)

色觉障碍友好型设计

  • 分类元素使用 Okabe-Ito 调色板
  • 连续数据使用感知均匀的色图
  • 冗余编码(形状 + 颜色)
  • 验证灰度兼容性

排版标准

  • 使用无衬线字体(Arial、Helvetica),确保一致性
  • 最终打印尺寸下的文字不小于 7-8 pt
  • 标签清晰易读,并标明单位
  • 全文使用一致的符号表示法

无障碍性

  • 元素之间保持高对比度
  • 线条粗细适当(至少 0.5-1 pt)
  • 清晰的视觉层级
  • 描述性标题和替代文本

如需完整的出版指南,请参阅 references/best_practices.md。

快速入门示例

示例 1:使用 TikZ 绘制简单流程图
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric, arrows.meta}

% Load colorblind-safe colors
\input{tikz_styles.tex}

\begin{document}

\begin{figure}[h]
\centering
\begin{tikzpicture}[
    node distance=2cm,
    process/.style={rectangle, rounded corners, draw=black, thick, 
                    fill=okabe-blue!20, minimum width=3cm, minimum height=1cm},
    decision/.style={diamond, draw=black, thick, fill=okabe-orange!20, 
                     minimum width=2cm, aspect=2},
    arrow/.style={-Stealth, thick}
]

% Nodes
\node (start) [process] {Screen Participants\\(n=500)};
\node (exclude) [process, below of=start] {Exclude (n=150)\\Age $<$ 18 years};
\node (randomize) [process, below of=exclude] {Randomize (n=350)};
\node (treatment) [process, below left=1.5cm and 2cm of randomize] 
                  {Treatment Group\\(n=175)};
\node (control) [process, below right=1.5cm and 2cm of randomize] 
                {Control Group\\(n=175)};
\node (analyze) [process, below=3cm of randomize] {Analyze Data};

% Arrows
\draw [arrow] (start) -- (exclude);
\draw [arrow] (exclude) -- (randomize);
\draw [arrow] (randomize) -| (treatment);
\draw [arrow] (randomize) -| (control);
\draw [arrow] (treatment) |- (analyze);
\draw [arrow] (control) |- (analyze);

\end{tikzpicture}
\caption{Study participant flow diagram following CONSORT guidelines.}
\label{fig:consort}
\end{figure}

\end{document}
示例 2:使用 Schemdraw 绘制电路图
import schemdraw
import schemdraw.elements as elm

# Create drawing with colorblind-safe colors
d = schemdraw.Drawing()

# Voltage source
d += elm.SourceV().label('$V_s$')

# Resistors in series
d += elm.Resistor().right().label('$R_1$\n1kΩ')
d += elm.Resistor().label('$R_2$\n2kΩ')

# Capacitor
d += elm.Capacitor().down().label('$C_1$\n10µF')

# Close the circuit
d += elm.Line().left().tox(d.elements[0].start)

# Add ground
d += elm.Ground()

# Save as vector graphics
d.save('circuit_diagram.svg')
d.save('circuit_diagram.pdf')
示例 3:使用 Python 绘制生物通路
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch

# Okabe-Ito colorblind-safe palette
colors = {
    'protein': '#56B4E9',    # Blue
    'gene': '#009E73',       # Green
    'process': '#F0E442',    # Yellow
    'inhibition': '#D55E00'  # Orange
}

fig, ax = plt.subplots(figsize=(8, 6))

# Define proteins as rounded rectangles
proteins = [
    ('Receptor', 1, 5),
    ('Kinase A', 3, 5),
    ('Kinase B', 5, 5),
    ('TF', 7, 5),
    ('Gene', 7, 3)
]

for name, x, y in proteins:
    color = colors['gene'] if name == 'Gene' else colors['protein']
    box = FancyBboxPatch((x-0.4, y-0.3), 0.8, 0.6, 
                         boxstyle="round,pad=0.1", 
                         facecolor=color, edgecolor='black', linewidth=2)
    ax.add_patch(box)
    ax.text(x, y, name, ha='center', va='center', fontsize=10, fontweight='bold')

# Add activation arrows
arrows = [
    (1.5, 5, 2.5, 5, 'black'),   # Receptor -> Kinase A
    (3.5, 5, 4.5, 5, 'black'),   # Kinase A -> Kinase B
    (5.5, 5, 6.5, 5, 'black'),   # Kinase B -> TF
    (7, 4.7, 7, 3.6, 'black')    # TF -> Gene
]

for x1, y1, x2, y2, color in arrows:
    arrow = FancyArrowPatch((x1, y1), (x2, y2),
                           arrowstyle='->', mutation_scale=20, 
                           linewidth=2, color=color)
    ax.add_patch(arrow)

# Configure axes
ax.set_xlim(0, 8.5)
ax.set_ylim(2, 6)
ax.set_aspect('equal')
ax.axis('off')

plt.tight_layout()
plt.savefig('signaling_pathway.pdf', bbox_inches='tight', dpi=300)
plt.savefig('signaling_pathway.png', bbox_inches='tight', dpi=300)

生产工作流(从构想到发布)

所有图表都遵循以下系统化工作流:

阶段 1:分析(2 分钟)
  1. 确定图表类型——你要可视化什么?

    • 神经网络架构?→ 使用 graphviz
    • 流程图(CONSORT、PRISMA)?→ 使用 graphviz
    • 电路图?→ 使用 schemdraw
    • 复杂网络?→ 使用 networkx + graphviz
  2. 确定布局方向

    • 垂直流向(从上到下)?→ rankdir='TB'
    • 从下到上(如 Transformer)?→ rankdir='BT'
    • 从左到右的序列?→ rankdir='LR'
    • 从右到左?→ rankdir='RL'
  3. 确定分组

    • 并行结构(编码器/解码器)?→ 使用集群/子图
    • 仅有顺序关系?→ 使用简单节点链
    • 有交叉连接?→ Graphviz 会自动处理
阶段 2:实现(10-15 分钟)

95% 的图表都适用的标准流程:

import graphviz
from pathlib import Path

# 1. Set up output directory
output_dir = 'figures'
Path(output_dir).mkdir(exist_ok=True, parents=True)

# 2. Create diagram with base template
dot = graphviz.Digraph(
    'my_diagram',
    format='pdf',
    graph_attr={
        'rankdir': 'TB',      # Adjust based on Phase 1
        'splines': 'ortho',   # Clean orthogonal edges
        'nodesep': '0.6',     # Good default spacing
        'ranksep': '0.8',
        'bgcolor': 'white',
        'dpi': '300'
    },
    node_attr={
        'shape': 'box',
        'style': 'rounded,filled',
        'fillcolor': 'lightgray',
        'fontname': 'Arial',
        'fontsize': '11'
    },
    edge_attr={'color': 'black', 'penwidth': '1.5'}
)

# 3. Add nodes (with descriptive IDs and clear labels)
dot.node('input', 'Input Layer', fillcolor='#E8F4F8')
dot.node('hidden', 'Hidden Layer', fillcolor='#B3D9E6')
dot.node('output', 'Output Layer', fillcolor='#C8E6C9')

# 4. Add edges
dot.edge('input', 'hidden')
dot.edge('hidden', 'output')

# 5. Render to figures/ folder
output_path = f'{output_dir}/my_diagram'
dot.render(output_path, cleanup=True)  # Creates PDF
dot.format = 'svg'
dot.render(output_path, cleanup=True)  # Creates SVG
dot.format = 'eps'
dot.render(output_path, cleanup=True)  # Creates EPS

print(f"✓ Saved to: {output_path}.{{pdf,svg,eps}}")
阶段 3:质量验证(5 分钟)

自动检查:

# Convert PDF to PNG for quality checking
from pdf2image import convert_from_path

pages = convert_from_path(f'{output_path}.pdf', dpi=300)
pages[0].save(f'{output_path}.png')

# Run quality checks
from quality_checker import run_quality_checks
report = run_quality_checks(f'{output_path}.png')

if report['overall_status'] != 'PASS':
    print("⚠️ Issues detected - review quality_reports/")
    # Adjust spacing: increase nodesep or ranksep
    # Adjust colors: check accessibility report
else:
    print("✓ Quality checks passed!")

手动验证:

  1. 在查看器中打开 PDF——检查是否存在重叠
  2. 确认文字清晰可读(缩放至 100%)
  3. 检查对齐和间距是否美观专业
  4. 确保颜色容易区分
阶段 4:集成到 LaTeX(2 分钟)

在 LaTeX 文档中:

% In preamble
\usepackage{graphicx}

% In document
\begin{figure}[htbp]
\centering
\includegraphics[width=0.8\textwidth]{figures/my_diagram.pdf}
\caption{Clear, descriptive caption explaining all components and abbreviations.
         Define any non-standard notation used in the diagram.}
\label{fig:my_diagram}
\end{figure}

% Reference in text
As shown in Figure~\ref{fig:my_diagram}, the architecture consists of...

用于海报(beamer):

\begin{frame}{Architecture}
\begin{center}
\includegraphics[width=0.9\textwidth]{figures/my_diagram.pdf}
\end{center}
\end{frame}
阶段 5:版本控制(1 分钟)

始终提交:

  1. Python 源代码(create_my_diagram.py)
  2. 生成的输出文件(figures/my_diagram.{pdf,svg,eps})
  3. 质量报告(my_diagram_quality_reports/)
git add create_my_diagram.py
git add figures/my_diagram.*
git add my_diagram_quality_reports/
git commit -m "Add architecture diagram with quality verification"

常见问题排查

Graphviz 特有问题

问题: 节点重叠或间距过小

# Solution: Increase spacing
graph_attr={
    'nodesep': '1.0',   # Increase from default 0.6
    'ranksep': '1.2'    # Increase from default 0.8
}

问题: 边交叉方式令人困惑

# Solution 1: Use orthogonal splines
graph_attr={'splines': 'ortho'}

# Solution 2: Adjust rank direction
graph_attr={'rankdir': 'LR'}  # Try different directions

问题: 标签被截断或太小

# Solution: Adjust node size and font
node_attr={
    'fontsize': '12',      # Increase from 11
    'margin': '0.3',       # More internal padding
    'width': '2.5',        # Wider boxes
    'height': '0.6'        # Taller boxes
}

问题: 集群/子图未显示

# Solution: Cluster names MUST start with 'cluster_'
with dot.subgraph(name='cluster_encoder') as enc:  # ✓ Correct
    enc.attr(label='Encoder')

with dot.subgraph(name='encoder') as enc:  # ✗ Won't show as cluster
    enc.attr(label='Encoder')

问题: 集群间的边无法正常工作

# Solution: Enable compound edges
dot.attr(compound='true')

# Then use lhead/ltail for cluster connections
dot.edge('node1', 'node2', lhead='cluster_decoder')

问题: 找不到 Graphviz 错误

# Solution: Install graphviz binary (not just Python package)
# macOS
brew install graphviz

# Ubuntu
sudo apt-get install graphviz

# Then install Python bindings
pip install graphviz
视觉质量问题

问题: 颜色不适合色盲人士

# Solution: Use Okabe-Ito palette
COLORS = {
    'blue': '#56B4E9',
    'green': '#009E73',
    'orange': '#E69F00',
    'purple': '#CC79A7'
}
dot.node('n1', 'Node', fillcolor=COLORS['blue'])

问题: 打印时文字太小

# Solution: Increase font size and DPI
node_attr={'fontsize': '12'}  # Minimum 11-12 for print
graph_attr={'dpi': '300'}     # Publication quality

问题: PDF 太大

# Solution 1: Use simpler edge routing
graph_attr={'splines': 'line'}  # Simpler than 'ortho'

# Solution 2: Reduce DPI for drafts
graph_attr={'dpi': '150'}  # For drafts only
工作流问题

问题: 修改后需要重新生成图表

# Solution: Make diagram generation a function
def create_diagram(params):
    # ... diagram code ...
    return output_path

# Easy to regenerate with different parameters
create_diagram({'nodesep': '0.8', 'ranksep': '1.0'})

问题: 图表风格与论文中的插图不一致

# Solution: Create a reusable style configuration
PAPER_STYLE = {
    'graph_attr': {
        'rankdir': 'TB',
        'bgcolor': 'white',
        'dpi': '300'
    },
    'node_attr': {
        'fontname': 'Arial',
        'fontsize': '11',
        'style': 'rounded,filled',
        'fillcolor': '#E8F4F8'
    },
    'edge_attr': {
        'color': 'black',
        'penwidth': '1.5'
    }
}

# Use for all diagrams
dot = graphviz.Digraph(**PAPER_STYLE)

视觉验证与质量控制

所有图表都会经过自动化视觉质量检查,以避免重叠、确保可读性并验证无障碍性。这一多阶段验证流程使用计算机视觉技术来检测常见问题。

阶段 1:重叠检测

自动检测会降低清晰度的元素重叠:

import numpy as np
from PIL import Image
import json
from pathlib import Path

def detect_overlaps(image_path, threshold=0.95):
    """
    Detect potential overlapping regions in a diagram.
    
    Args:
        image_path: Path to the rendered diagram (PNG/PDF)
        threshold: Similarity threshold for detecting overlaps (0-1)
        
    Returns:
        dict: Overlap report with locations and severity
    """
    # Load image
    img = Image.open(image_path).convert('RGB')
    img_array = np.array(img)
    
    # Detect dense regions (potential overlaps)
    gray = np.mean(img_array, axis=2)
    
    # Edge detection to find boundaries
    from scipy.ndimage import sobel
    edges_x = sobel(gray, axis=0)
    edges_y = sobel(gray, axis=1)
    edge_magnitude = np.hypot(edges_x, edges_y)
    
    # Find regions with high edge density (overlaps)
    from scipy.ndimage import label, find_objects
    binary_edges = edge_magnitude > np.percentile(edge_magnitude, 85)
    labeled_regions, num_features = label(binary_edges)
    
    overlaps = []
    slices = find_objects(labeled_regions)
    
    for i, slice_obj in enumerate(slices):
        if slice_obj is not None:
            region = edge_magnitude[slice_obj]
            density = np.mean(region)
            
            # High density suggests potential overlap
            if density > threshold * np.max(edge_magnitude):
                y_center = (slice_obj[0].start + slice_obj[0].stop) // 2
                x_center = (slice_obj[1].start + slice_obj[1].stop) // 2
                
                overlaps.append({
                    'region_id': i + 1,
                    'position': (x_center, y_center),
                    'density': float(density),
                    'severity': 'high' if density > 0.98 * np.max(edge_magnitude) else 'medium'
                })
    
    report = {
        'image': str(image_path),
        'overlaps_detected': len(overlaps),
        'overlap_regions': overlaps,
        'status': 'PASS' if len(overlaps) == 0 else 'WARNING'
    }
    
    return report

def save_overlap_report(report, output_path='overlap_report.json'):
    """Save overlap detection report to JSON."""
    with open(output_path, 'w') as f:
        json.dump(report, indent=2, fp=f)
    
    print(f"Overlap Report: {report['status']}")
    print(f"  - Overlaps detected: {report['overlaps_detected']}")
    if report['overlap_regions']:
        print("  - Regions requiring review:")
        for region in report['overlap_regions']:
            print(f"    * Region {region['region_id']}: "
                  f"Position {region['position']}, Severity: {region['severity']}")
阶段 2:对比度与无障碍性验证

确保图表符合色盲读者的无障碍标准:

def verify_accessibility(image_path):
    """
    Verify diagram meets accessibility standards.
    
    Checks:
    - Sufficient contrast ratios
    - Grayscale readability
    - Text size adequacy
    """
    from PIL import ImageFilter, ImageStat
    
    img = Image.open(image_path).convert('RGB')
    
    # Test 1: Grayscale conversion
    grayscale = img.convert('L')
    gray_stat = ImageStat.Stat(grayscale)
    
    # Calculate contrast (std dev of grayscale)
    contrast = gray_stat.stddev[0]
    min_contrast = 30  # Minimum standard deviation for good contrast
    
    # Test 2: Color distribution
    rgb_array = np.array(img)
    unique_colors = len(np.unique(rgb_array.reshape(-1, 3), axis=0))
    
    # Test 3: Simulate common color blindness (deuteranopia)
    def simulate_colorblind(img_array):
        # Simplified deuteranopia simulation
        colorblind = img_array.copy().astype(float)
        colorblind[:, :, 0] = 0.625 * img_array[:, :, 0] + 0.375 * img_array[:, :, 1]
        colorblind[:, :, 1] = 0.7 * img_array[:, :, 1] + 0.3 * img_array[:, :, 0]
        return colorblind.astype(np.uint8)
    
    colorblind_img = simulate_colorblind(np.array(img))
    cb_image = Image.fromarray(colorblind_img)
    cb_gray = cb_image.convert('L')
    cb_stat = ImageStat.Stat(cb_gray)
    cb_contrast = cb_stat.stddev[0]
    
    report = {
        'image': str(image_path),
        'checks': {
            'grayscale_contrast': {
                'value': contrast,
                'threshold': min_contrast,
                'status': 'PASS' if contrast >= min_contrast else 'FAIL'
            },
            'colorblind_contrast': {
                'value': cb_contrast,
                'threshold': min_contrast * 0.8,
                'status': 'PASS' if cb_contrast >= min_contrast * 0.8 else 'FAIL'
            },
            'color_diversity': {
                'unique_colors': unique_colors,
                'status': 'INFO'
            }
        },
        'overall_status': 'PASS' if (contrast >= min_contrast and 
                                     cb_contrast >= min_contrast * 0.8) else 'FAIL'
    }
    
    return report

def save_accessibility_report(report, output_path='accessibility_report.json'):
    """Save accessibility report to JSON."""
    with open(output_path, 'w') as f:
        json.dump(report, indent=2, fp=f)
    
    print(f"Accessibility Report: {report['overall_status']}")
    for check_name, check_data in report['checks'].items():
        print(f"  - {check_name}: {check_data['status']}")
        if 'value' in check_data and 'threshold' in check_data:
            print(f"    Value: {check_data['value']:.2f}, Threshold: {check_data['threshold']:.2f}")
阶段 3:文字大小与分辨率验证

确认文字在出版尺寸下清晰可读:

def validate_resolution(image_path, target_dpi=300, min_text_size_pt=7):
    """
    Validate image resolution and estimated text size.
    
    Args:
        image_path: Path to diagram image
        target_dpi: Target DPI for publication (default 300)
        min_text_size_pt: Minimum acceptable text size in points
    """
    from PIL import Image
    import pytesseract
    
    img = Image.open(image_path)
    
    # Check DPI
    dpi = img.info.get('dpi', (72, 72))
    actual_dpi = dpi[0] if isinstance(dpi, tuple) else dpi
    
    # Estimate text size (simplified - assumes text detection)
    # For production, use OCR or PDF text extraction
    width, height = img.size
    dpi_ratio = actual_dpi / 72  # Convert to screen pixels
    
    # Calculate physical size in inches
    width_inches = width / actual_dpi if actual_dpi > 0 else width / 72
    height_inches = height / actual_dpi if actual_dpi > 0 else height / 72
    
    report = {
        'image': str(image_path),
        'resolution': {
            'dpi': actual_dpi,
            'target_dpi': target_dpi,
            'status': 'PASS' if actual_dpi >= target_dpi else 'WARNING'
        },
        'dimensions': {
            'pixels': {'width': width, 'height': height},
            'inches': {'width': round(width_inches, 2), 
                      'height': round(height_inches, 2)}
        },
        'recommendations': []
    }
    
    if actual_dpi < target_dpi:
        report['recommendations'].append(
            f"Increase resolution to {target_dpi} DPI for publication quality"
        )
    
    if width_inches > 7:
        report['recommendations'].append(
            "Image width exceeds typical single-column width (7 inches)"
        )
    
    return report
阶段 4:综合质量检查流程

按顺序运行所有验证阶段:

def run_quality_checks(image_path, output_dir='quality_reports'):
    """
    Run comprehensive quality checks on diagram.
    
    Args:
        image_path: Path to diagram to verify
        output_dir: Directory to save reports
        
    Returns:
        dict: Comprehensive quality report
    """
    import os
    from datetime import datetime
    
    Path(output_dir).mkdir(exist_ok=True)
    
    print(f"Running quality checks on: {image_path}")
    print("=" * 60)
    
    # Stage 1: Overlap Detection
    print("\n[Stage 1/4] Detecting overlaps...")
    overlap_report = detect_overlaps(image_path)
    save_overlap_report(
        overlap_report, 
        f"{output_dir}/overlap_report.json"
    )
    
    # Stage 2: Accessibility
    print("\n[Stage 2/4] Verifying accessibility...")
    accessibility_report = verify_accessibility(image_path)
    save_accessibility_report(
        accessibility_report,
        f"{output_dir}/accessibility_report.json"
    )
    
    # Stage 3: Resolution
    print("\n[Stage 3/4] Validating resolution...")
    resolution_report = validate_resolution(image_path)
    with open(f"{output_dir}/resolution_report.json", 'w') as f:
        json.dump(resolution_report, f, indent=2)
    
    # Stage 4: Generate visual report
    print("\n[Stage 4/4] Generating visual report...")
    create_visual_report(
        image_path,
        overlap_report,
        accessibility_report,
        f"{output_dir}/visual_report.png"
    )
    
    # Comprehensive summary
    all_pass = (
        overlap_report['status'] != 'FAIL' and
        accessibility_report['overall_status'] != 'FAIL' and
        resolution_report['resolution']['status'] != 'FAIL'
    )
    
    summary = {
        'timestamp': datetime.now().isoformat(),
        'image': str(image_path),
        'overall_status': 'PASS' if all_pass else 'NEEDS REVIEW',
        'stage_results': {
            'overlap_detection': overlap_report['status'],
            'accessibility': accessibility_report['overall_status'],
            'resolution': resolution_report['resolution']['status']
        },
        'reports_saved_to': output_dir
    }
    
    with open(f"{output_dir}/summary.json", 'w') as f:
        json.dump(summary, f, indent=2)
    
    print("\n" + "=" * 60)
    print(f"OVERALL STATUS: {summary['overall_status']}")
    print(f"Reports saved to: {output_dir}/")
    print("=" * 60)
    
    return summary

def create_visual_report(image_path, overlap_report, accessibility_report, output_path):
    """Create visual report with annotations."""
    import matplotlib.pyplot as plt
    from matplotlib.patches import Circle
    
    fig, axes = plt.subplots(1, 3, figsize=(15, 5))
    
    # Load original image
    img = Image.open(image_path)
    
    # Panel 1: Original with overlap markers
    axes[0].imshow(img)
    axes[0].set_title('Overlap Detection', fontsize=12, fontweight='bold')
    if overlap_report['overlap_regions']:
        for region in overlap_report['overlap_regions']:
            x, y = region['position']
            color = 'red' if region['severity'] == 'high' else 'orange'
            circle = Circle((x, y), 20, color=color, fill=False, linewidth=2)
            axes[0].add_patch(circle)
    axes[0].axis('off')
    axes[0].text(0.02, 0.98, f"Status: {overlap_report['status']}", 
                transform=axes[0].transAxes, fontsize=10,
                verticalalignment='top', bbox=dict(boxstyle='round', 
                facecolor='wheat', alpha=0.5))
    
    # Panel 2: Grayscale version
    gray_img = img.convert('L')
    axes[1].imshow(gray_img, cmap='gray')
    axes[1].set_title('Grayscale Preview', fontsize=12, fontweight='bold')
    axes[1].axis('off')
    gray_status = accessibility_report['checks']['grayscale_contrast']['status']
    axes[1].text(0.02, 0.98, f"Status: {gray_status}", 
                transform=axes[1].transAxes, fontsize=10,
                verticalalignment='top', bbox=dict(boxstyle='round', 
                facecolor='wheat', alpha=0.5))
    
    # Panel 3: Colorblind simulation
    img_array = np.array(img)
    colorblind = img_array.copy().astype(float)
    colorblind[:, :, 0] = 0.625 * img_array[:, :, 0] + 0.375 * img_array[:, :, 1]
    colorblind[:, :, 1] = 0.7 * img_array[:, :, 1] + 0.3 * img_array[:, :, 0]
    axes[2].imshow(colorblind.astype(np.uint8))
    axes[2].set_title('Colorblind Simulation', fontsize=12, fontweight='bold')
    axes[2].axis('off')
    cb_status = accessibility_report['checks']['colorblind_contrast']['status']
    axes[2].text(0.02, 0.98, f"Status: {cb_status}", 
                transform=axes[2].transAxes, fontsize=10,
                verticalalignment='top', bbox=dict(boxstyle='round', 
                facecolor='wheat', alpha=0.5))
    
    plt.tight_layout()
    plt.savefig(output_path, dpi=150, bbox_inches='tight')
    print(f"Visual report saved: {output_path}")
    plt.close()
使用示例:包含验证的完整工作流

以下是创建图表并进行完整质量验证的方法:

import matplotlib.pyplot as plt
from matplotlib.patches import FancyBboxPatch, FancyArrowPatch

# Step 1: Create diagram
def create_flowchart_with_verification(output_base='flowchart'):
    """Create flowchart with automated quality checks."""
    
    # Okabe-Ito colorblind-safe palette
    colors = {
        'process': '#56B4E9',    # Blue
        'decision': '#E69F00',   # Orange
        'data': '#009E73',       # Green
        'terminal': '#CC79A7'    # Purple
    }
    
    fig, ax = plt.subplots(figsize=(8, 10))
    
    # Define flowchart elements with careful spacing
    elements = [
        ('Start', 4, 9, 'terminal'),
        ('Input Data', 4, 7.5, 'data'),
        ('Process A', 4, 6, 'process'),
        ('Decision?', 4, 4.5, 'decision'),
        ('Process B1', 2, 3, 'process'),
        ('Process B2', 6, 3, 'process'),
        ('Output', 4, 1.5, 'data'),
        ('End', 4, 0, 'terminal')
    ]
    
    # Draw boxes with adequate spacing
    box_positions = {}
    for label, x, y, element_type in elements:
        color = colors[element_type]
        width, height = (1.2, 0.6) if element_type != 'decision' else (1.5, 1.0)
        
        box = FancyBboxPatch(
            (x - width/2, y - height/2), width, height,
            boxstyle="round,pad=0.1" if element_type != 'decision' else "round,pad=0.05",
            facecolor=color, edgecolor='black', linewidth=2
        )
        ax.add_patch(box)
        ax.text(x, y, label, ha='center', va='center', 
               fontsize=10, fontweight='bold')
        box_positions[label] = (x, y)
    
    # Draw arrows with proper spacing
    arrows = [
        ('Start', 'Input Data'),
        ('Input Data', 'Process A'),
        ('Process A', 'Decision?'),
        ('Decision?', 'Process B1'),
        ('Decision?', 'Process B2'),
        ('Process B1', 'Output'),
        ('Process B2', 'Output'),
        ('Output', 'End')
    ]
    
    for start, end in arrows:
        x1, y1 = box_positions[start]
        x2, y2 = box_positions[end]
        
        # Calculate arrow start/end points to avoid overlap
        if x1 == x2:  # Vertical arrow
            y1_adj = y1 - 0.3 if y2 < y1 else y1 + 0.3
            y2_adj = y2 + 0.3 if y2 < y1 else y2 - 0.3
            arrow = FancyArrowPatch(
                (x1, y1_adj), (x2, y2_adj),
                arrowstyle='->', mutation_scale=20, linewidth=2, color='black'
            )
        else:  # Diagonal arrow
            arrow = FancyArrowPatch(
                (x1, y1 - 0.5), (x2, y2 + 0.3),
                arrowstyle='->', mutation_scale=20, linewidth=2, color='black'
            )
        ax.add_patch(arrow)
    
    ax.set_xlim(0, 8)
    ax.set_ylim(-0.5, 9.5)
    ax.set_aspect('equal')
    ax.axis('off')
    
    plt.tight_layout()
    
    # Save in multiple formats
    plt.savefig(f'{output_base}.pdf', bbox_inches='tight', dpi=300)
    plt.savefig(f'{output_base}.png', bbox_inches='tight', dpi=300)
    plt.savefig(f'{output_base}.svg', bbox_inches='tight')
    print(f"Diagram saved: {output_base}.pdf/.png/.svg")
    plt.close()
    
    # Step 2: Run quality checks
    print("\nRunning quality verification...")
    quality_report = run_quality_checks(
        f'{output_base}.png',
        output_dir=f'{output_base}_quality_reports'
    )
    
    # Step 3: Review and iterate if needed
    if quality_report['overall_status'] != 'PASS':
        print("\n⚠️  Diagram needs review. Check quality reports for details.")
        return False
    else:
        print("\n✓ Diagram passed all quality checks!")
        return True

# Run complete workflow
if __name__ == '__main__':
    success = create_flowchart_with_verification('my_flowchart')
迭代优化循环

对于复杂图表,请使用迭代优化流程:

def iterative_diagram_refinement(create_function, max_iterations=3):
    """
    Iteratively refine diagram until it passes quality checks.
    
    Args:
        create_function: Function that creates and saves diagram
        max_iterations: Maximum refinement attempts
    """
    for iteration in range(1, max_iterations + 1):
        print(f"\n{'='*60}")
        print(f"ITERATION {iteration}/{max_iterations}")
        print(f"{'='*60}")
        
        # Create diagram
        diagram_path = create_function(iteration)
        
        # Run checks
        quality_report = run_quality_checks(
            diagram_path,
            output_dir=f'iteration_{iteration}_reports'
        )
        
        if quality_report['overall_status'] == 'PASS':
            print(f"\n✓ Diagram approved after {iteration} iteration(s)")
            return True
        else:
            print(f"\n⚠️  Issues found. Adjusting parameters for next iteration...")
            # Here you would adjust spacing, colors, etc. based on reports
    
    print(f"\n❌ Maximum iterations reached. Manual review required.")
    return False

常见用例

用例 1:CONSORT 参与者流程图

临床试验需要标准化的参与者流程图。请使用流程图模板:

% Load template
\input{assets/flowchart_template.tex}

% Customize with your numbers
\begin{tikzpicture}[consort]
  \node (assessed) [flowbox] {Assessed for eligibility (n=500)};
  \node (excluded) [flowbox, below=of assessed] {Excluded (n=150)};
  \node (reasons) [infobox, right=of excluded] {
    \begin{tabular}{l}
    Age $<$ 18: n=80 \\
    Declined: n=50 \\
    Other: n=20
    \end{tabular}
  };
  % ... continue diagram
\end{tikzpicture}

完整模板请参见 assets/flowchart_template.tex。

用例 2:电子电路原理图

电子学论文可使用 Schemdraw 或 CircuitikZ:

# Python with Schemdraw - see scripts/circuit_generator.py
from scripts.circuit_generator import create_circuit

circuit = create_circuit(
    components=['voltage_source', 'resistor', 'capacitor', 'ground'],
    values=['5V', '1kΩ', '10µF', None],
    layout='series'
)
circuit.save('my_circuit.pdf')

也可以在 LaTeX 中使用 CircuitikZ——请参见 assets/circuit_template.tex。

用例 3:生物信号通路

可视化分子相互作用和信号级联:

# Python script - see scripts/pathway_diagram.py
from scripts.pathway_diagram import PathwayGenerator

pathway = PathwayGenerator()
pathway.add_protein('EGFR', position=(1, 5))
pathway.add_protein('RAS', position=(3, 5))
pathway.add_protein('RAF', position=(5, 5))
pathway.add_activation('EGFR', 'RAS')
pathway.add_activation('RAS', 'RAF')
pathway.save('mapk_pathway.pdf')

也可以使用 TikZ 创建——请参见 assets/pathway_template.tex。

用例 4:系统架构图

展示软件/硬件组件及其关系:

% Use block diagram template
\input{assets/block_diagram_template.tex}

\begin{tikzpicture}[architecture]
  \node (sensor) [component] {Sensor};
  \node (adc) [component, right=of sensor] {ADC};
  \node (micro) [component, right=of adc] {Microcontroller};
  \node (wifi) [component, above right=of micro] {WiFi Module};
  \node (display) [component, below right=of micro] {Display};
  
  \draw [dataflow] (sensor) -- node[above] {Analog} (adc);
  \draw [dataflow] (adc) -- node[above] {Digital} (micro);
  \draw [dataflow] (micro) -- (wifi);
  \draw [dataflow] (micro) -- (display);
\end{tikzpicture}

完整模板请参见 assets/block_diagram_template.tex。

辅助脚本

scripts/ 目录包含用于自动生成图表和质量验证的 Python 工具:

generate_flowchart.py

将文本描述转换为 TikZ 流程图,并自动进行质量检查:

from scripts.generate_flowchart import text_to_flowchart, create_with_verification

description = """
1. Screen participants (n=500)
2. Exclude if age < 18 (n=150)
3. Randomize remaining (n=350)
4. Treatment group (n=175)
5. Control group (n=175)
6. Follow up at 3 months
7. Analyze data
"""

# Generate TikZ code
tikz_code = text_to_flowchart(description)
with open('methodology_flow.tex', 'w') as f:
    f.write(tikz_code)

# Or create with automatic verification
success = create_with_verification(
    description, 
    output='methodology_flow',
    verify=True
)
circuit_generator.py

使用 Schemdraw 生成电路图并进行质量验证:

from scripts.circuit_generator import CircuitBuilder

builder = CircuitBuilder()
builder.add_voltage_source('Vs', '5V')
builder.add_resistor('R1', '1kΩ')
builder.add_capacitor('C1', '10µF')
builder.add_ground()

# Save with automatic quality checks
builder.save('circuit.pdf', verify=True)

# Access quality report
print(builder.quality_report)
pathway_diagram.py

创建生物通路图并检测重叠:

from scripts.pathway_diagram import PathwayGenerator

gen = PathwayGenerator(
    colorblind_safe=True,
    auto_spacing=True  # Automatically adjust spacing to prevent overlaps
)

gen.add_node('Receptor', type='protein', position=(1, 5))
gen.add_node('Kinase', type='protein', position=(3, 5))
gen.add_edge('Receptor', 'Kinase', interaction='activation')

# Save with quality verification
quality_report = gen.save('pathway.pdf', verify=True)

# Iteratively refine if needed
if quality_report['status'] != 'PASS':
    gen.auto_adjust_spacing()
    gen.save('pathway.pdf', verify=True)
compile_tikz.py

独立的 TikZ 编译工具,并带有质量检查功能:

# Compile TikZ to PDF with verification
python scripts/compile_tikz.py flowchart.tex -o flowchart.pdf --verify

# Generate PNG with quality report
python scripts/compile_tikz.py flowchart.tex -o flowchart.pdf --png --dpi 300 --verify

# Preview with quality overlay
python scripts/compile_tikz.py flowchart.tex --preview --show-quality
quality_checker.py

适用于任意图表的独立质量验证工具:

# Check single diagram
python scripts/quality_checker.py diagram.png

# Check with detailed report
python scripts/quality_checker.py diagram.png --detailed --output-dir reports/

# Batch check multiple diagrams
python scripts/quality_checker.py figures/*.png --batch

# Export visual comparison report
python scripts/quality_checker.py diagram.png --visual-report
# Python API
from scripts.quality_checker import DiagramQualityChecker

checker = DiagramQualityChecker()

# Run all checks
report = checker.check_diagram('diagram.png')

# Access specific checks
overlap_report = checker.check_overlaps('diagram.png')
accessibility_report = checker.check_accessibility('diagram.png')
resolution_report = checker.check_resolution('diagram.png')

# Generate visual report
checker.create_visual_report('diagram.png', output='quality_report.png')

# Batch processing
results = checker.batch_check(['fig1.png', 'fig2.png', 'fig3.png'])
checker.save_batch_report(results, 'batch_quality_report.json')

模板和资源

assets/ 目录中的预制模板可供使用:

  • flowchart_template.tex - 方法流程图(CONSORT 风格)
  • circuit_template.tex - 电路图
  • pathway_template.tex - 生物通路图
  • block_diagram_template.tex - 系统架构图
  • tikz_styles.tex - 可复用的样式定义(务必加载)

所有模板均采用适合色觉障碍者的 Okabe-Ito 调色板和适用于出版物的样式。

最佳实践摘要

设计原则
  1. 清晰优先于复杂 - 简化图表,移除不必要的元素
  2. 样式一致 - 使用模板和样式文件
  3. 满足色觉障碍者的无障碍需求 - 使用 Okabe-Ito 调色板和冗余编码
  4. 采用恰当的字体 - 无衬线字体,最小字号为 7-8 pt
  5. 使用矢量格式 - 出版时始终使用 PDF/SVG
技术要求
  1. 分辨率 - 优先使用矢量格式,或使用 300+ DPI 的栅格图像
  2. 文件格式 - LaTeX 使用 PDF,网页使用 SVG,PNG 作为备用格式
  3. 色彩空间 - 数字用途使用 RGB,印刷用途使用 CMYK(必要时进行转换)
  4. 线条粗细 - 最小 0.5 pt,通常为 1-2 pt
  5. 文字大小 - 最终尺寸至少为 7-8 pt
集成指南
  1. 在 LaTeX 中引入 - TikZ 使用 \input{},外部文件使用 \includegraphics{}
  2. 完善图注 - 描述所有元素和缩写
  3. 在正文中引用 - 在叙述中解释图表
  4. 保持一致 - 论文中的所有图表使用相同样式
  5. 版本控制 - 在仓库中保留源文件(.tex、.py)

常见问题排查

TikZ 编译错误

问题:! Package tikz Error: I do not know the key '/tikz/...

  • 解决方案:缺少库 - 在导言区添加 \usetikzlibrary{...}

问题:文字或元素重叠

  • 解决方案:运行质量检查工具以识别重叠:python scripts/quality_checker.py diagram.png
  • 解决方案:增大 node distance,并根据重叠报告手动调整位置
  • 解决方案:在通路生成器中使用 auto_spacing=True 自动调整

问题:箭头未正确连接

  • 解决方案:使用锚点:(node.east)、(node.north) 等
  • 解决方案:检查重叠报告中的箭头/节点交叉情况
Python 生成问题

问题:Schemdraw 元素未对齐

  • 解决方案:使用 .at() 方法进行精确定位
  • 解决方案:启用 auto_spacing 以避免重叠

问题:Matplotlib 文本渲染问题

  • 解决方案:设置 plt.rcParams['text.usetex'] = True 以启用 LaTeX 渲染
  • 解决方案:确保已安装 LaTeX

问题:导出质量较差

  • 解决方案:提高 DPI:plt.savefig(..., dpi=300, bbox_inches='tight')
  • 解决方案:运行分辨率检查器:quality_checker.check_resolution(image_path, target_dpi=300)

问题:生成后元素重叠

  • 解决方案:运行 detect_overlaps() 函数以识别问题区域
  • 解决方案:使用迭代优化:iterative_diagram_refinement(create_function)
  • 解决方案:将元素间距增加 20-30%
质量检查问题

问题:误报重叠检测

  • 解决方案:调整阈值:detect_overlaps(image_path, threshold=0.98)
  • 解决方案:在可视化报告中手动检查标记的区域

问题:质量检查器无法处理 PDF 文件

  • 解决方案:先将 PDF 转换为 PNG:from pdf2image import convert_from_path
  • 解决方案:质量检查时使用 PNG 输出格式

问题:色盲模拟显示对比度较差

  • 解决方案:在代码中明确切换为 Okabe-Ito 调色板
  • 解决方案:添加冗余编码(形状、图案、线条样式)
  • 解决方案:增大颜色的饱和度和明度差异

问题:检测到严重重叠

  • 解决方案:查看 overlap_report.json 以获取准确位置
  • 解决方案:增大这些特定区域的间距
  • 解决方案:使用调整后的参数重新运行并再次验证

问题:可视化报告生成失败

  • 解决方案:检查 Pillow 和 matplotlib 是否已安装
  • 解决方案:确保图像文件可读取:Image.open(path).verify()
  • 解决方案:检查报告生成所需的磁盘空间是否充足
无障碍问题

问题:灰度图中无法区分颜色

  • 解决方案:运行无障碍检查器:verify_accessibility(image_path)
  • 解决方案:添加图案、形状或线条样式作为冗余标识
  • 解决方案:提高相邻元素之间的对比度

问题:打印时文本过小

  • 解决方案:运行分辨率验证器:validate_resolution(image_path)
  • 解决方案:按最终尺寸进行设计,字体至少使用 7-8 pt
  • 解决方案:在分辨率报告中检查实际尺寸

问题:无障碍检查持续失败

  • 解决方案:查看 accessibility_report.json,了解具体失败项
  • 解决方案:将颜色对比度至少提高 20%
  • 解决方案:定稿前测试实际的灰度转换效果

资源和参考资料

详细参考资料

加载以下文件,获取特定主题的完整信息:

  • references/tikz_guide.md - 完整的 TikZ 语法、定位、样式和技巧
  • references/diagram_types.md - 科学图表类型目录及示例
  • references/best_practices.md - 出版标准和无障碍指南
  • references/python_libraries.md - 使用 Schemdraw、NetworkX 和 Matplotlib 绘制图表的指南
外部资源

TikZ 和 LaTeX

Python 库

出版标准

与其他技能的集成

此技能可与以下技能协同使用:

  • 科学写作 - 图表遵循图表最佳实践
  • 科学可视化 - 共享调色板和样式
  • LaTeX 海报 - 复用 TikZ 样式绘制海报图表
  • 研究资助 - 为提案绘制方法图
  • 同行评审 - 评估图表的清晰度和可访问性

快速参考清单

提交图表前,请确认:

视觉质量
  • 矢量格式(PDF/SVG)或 300+ DPI 位图
  • 无重叠元素(已通过质量检查器验证)
  • 所有组件之间间距充足
  • 对齐整洁、专业
  • 所有箭头均正确连接到目标位置
可访问性
  • 使用适合色盲的调色板(Okabe-Ito)
  • 灰度下显示正常(已使用可访问性检查器测试)
  • 元素之间对比度充足(已验证)
  • 在适当情况下使用冗余编码(形状 + 颜色)
  • 通过所有色盲模拟检查
排版与可读性
  • 最终尺寸下文字至少为 7-8 pt
  • 所有元素均有清晰、完整的标签
  • 字体系列和字号保持一致
  • 无文字重叠或截断
  • 适用时包含单位
出版标准
  • 与稿件中的其他图表保持样式一致
  • 编写完整图注,并定义所有缩写
  • 在稿件正文中恰当引用
  • 符合期刊对尺寸的具体要求
  • 按期刊要求的格式导出(PDF/EPS/TIFF)
质量验证(必需)
  • 运行 run_quality_checks() 并达到 PASS 状态
  • 检查重叠检测报告(无高严重程度重叠)
  • 通过可访问性验证(灰度和色盲)
  • 在目标 DPI 下验证分辨率(印刷版为 300+)
  • 生成并检查视觉质量报告
  • 将所有质量报告与图表文件一同保存
文档与版本控制
  • 保存源文件(.tex、.py),以便日后修改
  • 将质量报告归档到 quality_reports/ 目录
  • 记录配置参数(颜色、间距、尺寸)
  • Git 提交包含源文件、输出文件和质量报告
  • README 或注释说明如何重新生成图表
最终集成检查
  • 图表在编译后的稿件中显示正确
  • 交叉引用正常(\ref{} 指向正确的图表)
  • 图表编号与正文引用一致
  • 图注相对于图表显示在正确的页面上
  • 无与图表相关的编译警告或错误

使用此技能创建清晰、易于访问且达到出版质量的图表,有效传达复杂的科学概念。集成的质量验证工作流可确保所有图表在发表前均符合专业标准。

评论

支持 Markdown