Source code for figrecipe._diagram._shared._presets

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: 2025-01-24
# File: figrecipe/_diagram/_presets.py

"""
Presets for common diagram types in scientific papers.

Each preset defines rules for compiling the semantic spec to backend formats.
These encode domain knowledge about "what makes a good paper figure."
"""

from dataclasses import dataclass
from typing import Dict


[docs] @dataclass class DiagramPreset: """Rules for compiling a diagram type.""" # Mermaid settings mermaid_direction: str # TB, LR, RL, BT mermaid_theme: Dict[str, str] # Graphviz settings graphviz_rankdir: str # TB, LR, RL, BT graphviz_ranksep: float graphviz_nodesep: float # Spacing mappings spacing_map: Dict[str, Dict[str, float]] # Shape mappings (semantic -> backend) mermaid_shapes: Dict[str, str] graphviz_shapes: Dict[str, str] # Emphasis styles (colors, borders) emphasis_styles: Dict[str, Dict[str, str]]
# Workflow preset: sequential processes, emphasize flow WORKFLOW_PRESET = DiagramPreset( mermaid_direction="LR", # Left-to-right for workflows mermaid_theme={ "primaryColor": "#1a2634", "primaryTextColor": "#e0e0e0", "primaryBorderColor": "#3a4a5a", "lineColor": "#5a9fcf", }, graphviz_rankdir="LR", graphviz_ranksep=0.8, graphviz_nodesep=0.5, spacing_map={ "tight": {"ranksep": 0.3, "nodesep": 0.2}, # Publication: minimal "compact": {"ranksep": 0.4, "nodesep": 0.3}, "medium": {"ranksep": 0.8, "nodesep": 0.5}, "large": {"ranksep": 1.2, "nodesep": 0.8}, }, mermaid_shapes={ "box": '["__LABEL__"]', "rounded": '("__LABEL__")', "diamond": '{"__LABEL__"}', "circle": '(("__LABEL__"))', "stadium": '(["__LABEL__"])', "codeblock": '["__LABEL__"]', }, graphviz_shapes={ "box": "box", "rounded": "box", # with style=rounded "diamond": "diamond", "circle": "circle", "stadium": "box", # with style=rounded "codeblock": "box", }, emphasis_styles={ "primary": {"fill": "#0d4a6b", "stroke": "#5a9fcf", "stroke-width": "2px"}, "success": {"fill": "#ccffcc", "stroke": "#00cc00"}, "warning": {"fill": "#ffcccc", "stroke": "#cc0000"}, "muted": {"fill": "#f0f0f0", "stroke": "#999999"}, "normal": {"fill": "#1a2634", "stroke": "#3a4a5a"}, }, ) # Decision tree preset: top-to-bottom with branches DECISION_PRESET = DiagramPreset( mermaid_direction="TB", mermaid_theme={ "primaryColor": "#f5f5f5", "primaryTextColor": "#333333", "primaryBorderColor": "#666666", "lineColor": "#666666", }, graphviz_rankdir="TB", graphviz_ranksep=1.0, graphviz_nodesep=0.6, spacing_map={ "tight": {"ranksep": 0.4, "nodesep": 0.3}, "compact": {"ranksep": 0.6, "nodesep": 0.4}, "medium": {"ranksep": 1.0, "nodesep": 0.6}, "large": {"ranksep": 1.5, "nodesep": 1.0}, }, mermaid_shapes={ "box": '["__LABEL__"]', "rounded": '("__LABEL__")', "diamond": '{"__LABEL__"}', "circle": '(("__LABEL__"))', "stadium": '(["__LABEL__"])', "codeblock": '["__LABEL__"]', }, graphviz_shapes={ "box": "box", "rounded": "box", "diamond": "diamond", "circle": "circle", "stadium": "box", "codeblock": "box", }, emphasis_styles={ "primary": {"fill": "#e6f3ff", "stroke": "#0066cc", "stroke-width": "2px"}, "success": {"fill": "#e6ffe6", "stroke": "#00aa00"}, "warning": {"fill": "#ffe6e6", "stroke": "#cc0000"}, "muted": {"fill": "#f0f0f0", "stroke": "#aaaaaa"}, "normal": {"fill": "#ffffff", "stroke": "#666666"}, }, ) # Pipeline preset: strict horizontal stages PIPELINE_PRESET = DiagramPreset( mermaid_direction="LR", mermaid_theme={ "primaryColor": "#ffffff", "primaryTextColor": "#333333", "primaryBorderColor": "#0066cc", "lineColor": "#0066cc", }, graphviz_rankdir="LR", graphviz_ranksep=1.2, graphviz_nodesep=0.4, spacing_map={ "tight": {"ranksep": 0.5, "nodesep": 0.2}, "compact": {"ranksep": 0.8, "nodesep": 0.3}, "medium": {"ranksep": 1.2, "nodesep": 0.4}, "large": {"ranksep": 1.8, "nodesep": 0.6}, }, mermaid_shapes={ "box": '["__LABEL__"]', "rounded": '("__LABEL__")', "diamond": '{"__LABEL__"}', "circle": '(("__LABEL__"))', "stadium": '(["__LABEL__"])', "codeblock": '["__LABEL__"]', }, graphviz_shapes={ "box": "box", "rounded": "box", "diamond": "diamond", "circle": "circle", "stadium": "box", "codeblock": "box", }, emphasis_styles={ "primary": {"fill": "#e6f0ff", "stroke": "#0044aa", "stroke-width": "2px"}, "success": {"fill": "#e6ffe6", "stroke": "#00aa00"}, "warning": {"fill": "#fff3e6", "stroke": "#ff8800"}, "muted": {"fill": "#f5f5f5", "stroke": "#cccccc"}, "normal": {"fill": "#ffffff", "stroke": "#0066cc"}, }, ) # Scientific preset: clean, publication-ready, matches SCITEX figure style SCIENTIFIC_PRESET = DiagramPreset( mermaid_direction="LR", mermaid_theme={ "primaryColor": "#ffffff", "primaryTextColor": "#333333", "primaryBorderColor": "#0080C0", # SCITEX blue "lineColor": "#0080C0", "secondaryColor": "#f8f9fa", "tertiaryColor": "#e9ecef", }, graphviz_rankdir="LR", graphviz_ranksep=0.6, graphviz_nodesep=0.4, spacing_map={ "tight": {"ranksep": 0.3, "nodesep": 0.2}, "compact": {"ranksep": 0.4, "nodesep": 0.3}, "medium": {"ranksep": 0.6, "nodesep": 0.4}, "large": {"ranksep": 1.0, "nodesep": 0.6}, }, mermaid_shapes={ "box": '["__LABEL__"]', "rounded": '("__LABEL__")', "diamond": '{"__LABEL__"}', "circle": '(("__LABEL__"))', "stadium": '(["__LABEL__"])', "codeblock": '["__LABEL__"]', }, graphviz_shapes={ "box": "box", "rounded": "box", "diamond": "diamond", "circle": "circle", "stadium": "box", "codeblock": "box", }, emphasis_styles={ # SCITEX color palette "primary": { "fill": "#0080C0", "stroke": "#0060A0", "stroke-width": "1.5px", }, # blue "success": { "fill": "#14B414", "stroke": "#0f8f0f", "stroke-width": "1.5px", }, # green "warning": { "fill": "#FF4632", "stroke": "#cc382a", "stroke-width": "1.5px", }, # red "muted": {"fill": "#f0f0f0", "stroke": "#999999", "stroke-width": "0.5px"}, "normal": {"fill": "#ffffff", "stroke": "#0080C0", "stroke-width": "1px"}, }, )
[docs] def get_preset(diagram_type: str) -> DiagramPreset: """Get preset for diagram type.""" presets = { "workflow": WORKFLOW_PRESET, "decision": DECISION_PRESET, "pipeline": PIPELINE_PRESET, "hierarchy": DECISION_PRESET, "comparison": WORKFLOW_PRESET, "scientific": SCIENTIFIC_PRESET, "scitex": SCIENTIFIC_PRESET, # Alias } return presets.get(diagram_type.lower(), WORKFLOW_PRESET)
[docs] def list_presets() -> dict: """List all available presets with descriptions.""" return { "workflow": "Sequential process, left-to-right flow", "decision": "Decision tree, top-to-bottom with branches", "pipeline": "Data pipeline, strict horizontal stages", "hierarchy": "Tree structure, top-to-bottom with levels", "comparison": "Side-by-side comparison layout", "scientific": "Publication-ready, SCITEX color palette", }