ribesstefano's picture
Removed splitting bonds highlights and added examples.
7d38fe9
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