File size: 7,262 Bytes
9dd777e
 
7d38fe9
9dd777e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d38fe9
9dd777e
 
 
 
 
 
 
 
 
 
 
 
7d38fe9
9dd777e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d38fe9
9dd777e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7d38fe9
9dd777e
7d38fe9
9dd777e
 
 
 
 
 
 
 
 
 
 
7d38fe9
9dd777e
7d38fe9
9dd777e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
import os
import sys
import logging
from typing import Optional

from rdkit import Chem
from rdkit.Chem import Draw

if 'ipykernel' in sys.modules:
    from IPython.display import SVG

from .chemoinformatics import get_atom_idx_at_attachment, canonize

def safe_display(*args):
    """Displays content only if running in a Jupyter notebook."""
    if 'ipykernel' in sys.modules:
        display(*args)
    else:
        logging.warning(*args)


def display_mol(
        mol: Chem.Mol,
        w: int = 800,
        h: int = 300,
        legend: Optional[str] = None,
        use_smiles_as_legend: bool = True,
        display_svg: bool = True,
):
    """ Display a molecule in a Jupyter notebook. Useful for having """
    if mol is None:
        logging.warning('Molecule is None')
        return None
    if use_smiles_as_legend and legend is None:
        legend = Chem.MolToSmiles(mol)
    if display_svg:
        mol.SetProp("_Name", Chem.MolToSmiles(mol, canonical=True))
        d = Draw.rdMolDraw2D.MolDraw2DSVG(w, h, noFreetype=True)
        font_path = '/System/Library/Fonts/Supplemental/Arial.ttf'
        if os.path.exists(font_path):
            d.fontFile = font_path
        d.DrawMolecule(mol, legend=legend)
        d.FinishDrawing()
        svg = d.GetDrawingText()
        # Check if in Jupyter notebook
        if sys.modules.get('ipykernel', None):
            from IPython.display import SVG
            safe_display(SVG(svg))
    else:
        img = Draw.MolToImage(mol, size=(w, h))
        safe_display(img)


def get_mapped_protac_img(
        protac_smiles: str,
        poi_smiles: str,
        linker_smiles: str,
        e3_smiles: str,
        w: int = 1000,
        h: int = 1000,
        useSVG: bool = False,
        display_image: bool = False,
        legend: Optional[str] = None,
        show_bond_indices: bool = False,
):
    """ Display a PROTAC molecule with the POI, linker, and E3 ligase highlighted.
    
    If `useSVG` is True, then the POI-Linker bond is highlighted in purple, whereas the E3-Linker bond is highlighted in green.
    If `useSVG` is False, then both splitting points are highlighted in purple.
    
    Args:
        protac_smiles: The SMILES string of the PROTAC.
        poi_smiles: The SMILES string of the POI.
        linker_smiles: The SMILES string of the linker.
        e3_smiles: The SMILES string of the E3 ligase.
        w: The width of the image.
        h: The height of the image.
        useSVG: Whether to use SVG format.
        display_image: Whether to display the image.
        legend: The legend to display.
        show_bond_indices: Whether to show bond indices in the image.
    """
    protac_smiles = canonize(protac_smiles)
    e3_smiles = canonize(e3_smiles)
    poi_smiles = canonize(poi_smiles)
    linker_smiles = canonize(linker_smiles)

    # Check if any of the canonicalized SMILES is None
    if None in [protac_smiles, e3_smiles, poi_smiles, linker_smiles]:
        return None

    protac_mol = Chem.MolFromSmiles(protac_smiles)
    e3_mol = Chem.MolFromSmiles(e3_smiles)
    poi_mol = Chem.MolFromSmiles(poi_smiles)
    linker_mol = Chem.MolFromSmiles(linker_smiles)

    if None in [protac_mol, e3_mol, poi_mol, linker_mol]:
        return None

    if linker_smiles in ['[*:1][*:2]', '[*:2][*:1]']:
        logging.warning('WARNING. Linker is empty.')
        poi_attachment_idx = get_atom_idx_at_attachment(protac_mol, poi_mol, e3_mol)
        e3_attachment_idx = get_atom_idx_at_attachment(protac_mol, e3_mol, poi_mol)
    else:
        poi_attachment_idx = get_atom_idx_at_attachment(protac_mol, poi_mol, linker_mol)
        e3_attachment_idx = get_atom_idx_at_attachment(protac_mol, e3_mol, linker_mol)

    cyan = (0, 1, 1, 0.5)
    red = (1, 0, 0, 0.5)
    green = (0, 1, 0, 0.5)
    blue = (0, 0, 1, 0.5)
    purple = (1, 0, 1, 0.3)

    highlight_atoms = []
    highlight_bonds = []
    atom_colors = {}
    bond_colors = {}

    if poi_attachment_idx is not None:
        if len(poi_attachment_idx) != 2:
            if linker_smiles in ['[*:1][*:2]', '[*:2][*:1]']:
                logging.warning(f'WARNING. Linker is empty, no highlighting will be showed for the POI.')
            else:
                logging.warning(f'WARNING. POI attachment points must be only two, got instead: {poi_attachment_idx}')
        else:
            poi_bond_idx = protac_mol.GetBondBetweenAtoms(*poi_attachment_idx).GetIdx()
            highlight_atoms += poi_attachment_idx
            highlight_bonds.append(poi_bond_idx)
            atom_colors[poi_attachment_idx[0]] = purple
            atom_colors[poi_attachment_idx[1]] = purple
            bond_colors[poi_bond_idx] = purple

    if e3_attachment_idx is not None:
        if len(e3_attachment_idx) != 2:
            if linker_smiles in ['[*:1][*:2]', '[*:2][*:1]']:
                logging.warning(f'WARNING. Linker is empty, no highlighting will be showed for the E3.')
            else:
                logging.warning(f'WARNING. E3 attachment points must be only two, got instead: {e3_attachment_idx}')
        else:
            e3_bond_idx = protac_mol.GetBondBetweenAtoms(*e3_attachment_idx).GetIdx()
            highlight_atoms += e3_attachment_idx
            highlight_bonds.append(e3_bond_idx)
            atom_colors[e3_attachment_idx[0]] = green
            atom_colors[e3_attachment_idx[1]] = green
            bond_colors[e3_bond_idx] = green

    if useSVG:
        drawer = Draw.rdMolDraw2D.MolDraw2DSVG(w, h, noFreetype=True)
        options = drawer.drawOptions()
        options.fontFile = '/System/Library/Fonts/Supplemental/Arial.ttf'

        if legend is None:
            # legend = '.'.join([e3_smiles, linker_smiles, poi_smiles])
            legend = ""

        drawer.DrawMolecule(
            protac_mol,
            legend=legend,
            highlightAtoms=highlight_atoms,
            highlightBonds=highlight_bonds,
            highlightAtomColors=atom_colors,
            highlightBondColors=bond_colors,
        )

        # Add bond indices as text in the center of each bond
        if show_bond_indices:
            # Needs coordinates; ensure 2D coords present
            Chem.rdDepictor.Compute2DCoords(protac_mol)
            for bond in protac_mol.GetBonds():
                idx = bond.GetIdx()
                begin = bond.GetBeginAtomIdx()
                end = bond.GetEndAtomIdx()
                begin_pos = drawer.GetDrawCoords(begin)
                end_pos = drawer.GetDrawCoords(end)
                mid_y = (begin_pos.y + end_pos.y) / 2
                mid_x = (begin_pos.x + end_pos.x) / 2
                drawer.DrawString(f"{idx}", Chem.rdGeometry.Point2D(mid_x, mid_y), rawCoords=True)

        drawer.FinishDrawing()
        svg_text = drawer.GetDrawingText()

        if display_image:
            safe_display(SVG(svg_text))

        return svg_text
    else:
        img = Draw.MolToImage(
            protac_mol,
            size=(w, h),
            highlightColor=purple,
            highlightAtoms=highlight_atoms,
            highlightBonds=highlight_bonds,
            highlightAtomColors=atom_colors,
            highlightBondColors=bond_colors,
        )

        if display_image:
            safe_display(img)

        return img