codex/active-sites-induced-fit-residue-map.py

active-sites-induced-fit-residue-map.py
"""Original schematic only; run from _media with Python 3.14 and Matplotlib."""
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Circle, Polygon


FOLDED = np.array([
    [-3.2, -1.8], [-2.5, -1.0], [-0.85, 0.0], [-2.3, 0.9],
    [-2.0, 2.0], [-0.5, 2.5], [0.0, 0.95], [1.8, 2.3],
    [2.6, 1.2], [0.9, 0.0], [2.2, -1.0], [0.2, -2.0],
])
ROLES = {3: '#2479b0', 7: '#d86b18', 10: '#8855a5'}


def chain(ax, positions, radius):
    """Preserve every residue and the eleven backbone connections."""
    ax.plot(*positions.T, color='#adb7c0', linewidth=3, zorder=1)
    for i, (x, y) in enumerate(positions, 1):
        ax.add_patch(Circle((x, y), radius, facecolor=ROLES.get(i, '#e7edf2'),
                            edgecolor='#435363', linewidth=1.2, zorder=3))
        ax.text(x, y, str(i), ha='center', va='center', fontsize=10,
                color='white' if i in ROLES else '#253544', zorder=4)
    ax.set_aspect('equal')
    ax.axis('off')


def main():
    plt.rcParams.update({'font.family': 'DejaVu Sans', 'font.size': 11})
    fig = plt.figure(figsize=(10, 6.4), facecolor='white')
    fig.suptitle('An active site is a spatial team within a larger chain',
                 fontsize=16, x=0.5, y=0.975)
    top = fig.add_axes([0.05, 0.72, 0.90, 0.19])
    linear = np.column_stack((np.arange(12), np.zeros(12)))
    chain(top, linear, 0.29)
    top.set_xlim(-0.7, 11.7)
    top.set_ylim(-0.8, 1.2)
    top.text(5.5, 0.85, 'Sequence order: highlighted positions are separated',
             ha='center', fontsize=12)
    top.text(-0.35, -0.65, 'N end', ha='center', fontsize=10)
    top.text(11.35, -0.65, 'C end', ha='center', fontsize=10)

    folded = fig.add_axes([0.04, 0.09, 0.57, 0.60])
    chain(folded, FOLDED, 0.22)
    folded.set_xlim(-3.7, 3.1)
    folded.set_ylim(-2.5, 3.2)
    folded.text(-0.3, 3.0, 'Imagined fold: positions 3, 7 and 10 meet',
                ha='center', fontsize=12)
    substrate = np.array([[-0.35, -0.35], [0.28, -0.35], [0.42, 0.12],
                          [0.12, 0.43], [-0.36, 0.24]])
    folded.add_patch(Polygon(substrate, facecolor='#dbf0df',
                             edgecolor='#367846', linewidth=1.7, zorder=2))
    folded.text(0, 0, 'S', color='#255c33', ha='center', va='center',
                fontsize=13, weight='bold', zorder=4)
    for i in ROLES:
        folded.plot([0, FOLDED[i-1, 0]], [0, FOLDED[i-1, 1]],
                    linestyle='--', linewidth=1.2, color=ROLES[i], zorder=2)
    folded.text(-3.25, -2.2, 'N end', fontsize=10)
    folded.text(0.2, -2.4, 'C end', ha='center', fontsize=10)

    legend = fig.add_axes([0.63, 0.13, 0.34, 0.52])
    legend.axis('off')
    entries = [(0.85, '#2479b0', '3: substrate recognition'),
               (0.67, '#d86b18', '7: catalytic chemistry'),
               (0.49, '#8855a5', '10: both roles'),
               (0.31, '#e7edf2', 'Other positions: folded support')]
    for y, color, label in entries:
        legend.scatter([0.05], [y], s=175, color=color, edgecolor='#435363')
        legend.text(0.14, y, label, va='center', fontsize=11)
    legend.text(0.03, 0.10, 'S = generic substrate\nRoles may overlap.', fontsize=11)
    legend.set_xlim(0, 1)
    legend.set_ylim(0, 1)
    fig.text(0.5, 0.025, 'Original toy diagram • no actual coordinates, bond geometry or universal site size',
             ha='center', fontsize=10, color='#53616d')
    fig.savefig(Path(__file__).with_suffix('.png').name, dpi=120,
                facecolor='white', transparent=False)
    plt.close(fig)


if __name__ == '__main__':
    main()