chython 2.4__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chython/__init__.py +31 -0
- chython/_functions.py +69 -0
- chython/algorithms/__init__.py +18 -0
- chython/algorithms/_isomorphism.cp312-win_amd64.pyd +0 -0
- chython/algorithms/_isomorphism.pyx +189 -0
- chython/algorithms/aromatics/__init__.py +27 -0
- chython/algorithms/aromatics/_rules.py +124 -0
- chython/algorithms/aromatics/kekule.py +530 -0
- chython/algorithms/aromatics/test/__init__.py +18 -0
- chython/algorithms/aromatics/test/test_kekule.py +160 -0
- chython/algorithms/aromatics/test/test_thiele.py +263 -0
- chython/algorithms/aromatics/thiele.py +226 -0
- chython/algorithms/calculate2d/__init__.py +23 -0
- chython/algorithms/calculate2d/clean2d.js +1 -0
- chython/algorithms/calculate2d/molecule.py +149 -0
- chython/algorithms/calculate2d/reaction.py +80 -0
- chython/algorithms/depict.py +523 -0
- chython/algorithms/fingerprints/__init__.py +48 -0
- chython/algorithms/fingerprints/linear.py +208 -0
- chython/algorithms/fingerprints/morgan.py +137 -0
- chython/algorithms/fingerprints/test/__init__.py +18 -0
- chython/algorithms/fingerprints/test/test_linear.py +127 -0
- chython/algorithms/fingerprints/test/test_morgan.py +79 -0
- chython/algorithms/isomorphism.py +667 -0
- chython/algorithms/mapping/__init__.py +28 -0
- chython/algorithms/mapping/_groups.py +94 -0
- chython/algorithms/mapping/_reactions.py +72 -0
- chython/algorithms/mapping/attention.py +216 -0
- chython/algorithms/mapping/fixmapper.py +84 -0
- chython/algorithms/mapping/groups.py +137 -0
- chython/algorithms/mcs.py +202 -0
- chython/algorithms/morgan.py +82 -0
- chython/algorithms/rings.py +551 -0
- chython/algorithms/smiles.py +556 -0
- chython/algorithms/standardize/__init__.py +30 -0
- chython/algorithms/standardize/_charged.py +115 -0
- chython/algorithms/standardize/_groups.py +926 -0
- chython/algorithms/standardize/_metal_organics.py +189 -0
- chython/algorithms/standardize/_reagents.py +44 -0
- chython/algorithms/standardize/_salts.py +68 -0
- chython/algorithms/standardize/molecule.py +479 -0
- chython/algorithms/standardize/reaction.py +429 -0
- chython/algorithms/standardize/resonance.py +196 -0
- chython/algorithms/standardize/salts.py +128 -0
- chython/algorithms/standardize/saturation.py +378 -0
- chython/algorithms/standardize/test/__init__.py +18 -0
- chython/algorithms/standardize/test/test_groups.py +118 -0
- chython/algorithms/stereo.py +1222 -0
- chython/algorithms/tautomers/__init__.py +171 -0
- chython/algorithms/tautomers/_acid.py +59 -0
- chython/algorithms/tautomers/_base.py +116 -0
- chython/algorithms/tautomers/_keto_enol.py +107 -0
- chython/algorithms/tautomers/acid_base.py +214 -0
- chython/algorithms/tautomers/heteroarenes.py +108 -0
- chython/algorithms/tautomers/keto_enol.py +148 -0
- chython/algorithms/tautomers/test/__init__.py +18 -0
- chython/algorithms/tautomers/test/test_tautomers.py +80 -0
- chython/algorithms/test/__init__.py +18 -0
- chython/algorithms/test/test_isomorphism.py +58 -0
- chython/algorithms/test/test_smiles.py +76 -0
- chython/algorithms/x3dom.py +355 -0
- chython/containers/__init__.py +43 -0
- chython/containers/_pack_v2.cp312-win_amd64.pyd +0 -0
- chython/containers/_pack_v2.pyx +271 -0
- chython/containers/_unpack_v0v2.cp312-win_amd64.pyd +0 -0
- chython/containers/_unpack_v0v2.pyx +300 -0
- chython/containers/bonds.py +255 -0
- chython/containers/cgr.py +115 -0
- chython/containers/graph.py +200 -0
- chython/containers/molecule.py +695 -0
- chython/containers/query.py +66 -0
- chython/containers/reaction.py +324 -0
- chython/exceptions.py +114 -0
- chython/files/MRVrw.py +523 -0
- chython/files/PDBrw.py +257 -0
- chython/files/RDFrw.py +298 -0
- chython/files/SDFrw.py +221 -0
- chython/files/__init__.py +29 -0
- chython/files/_convert.py +207 -0
- chython/files/_mapping.py +117 -0
- chython/files/_xyz.cp312-win_amd64.pyd +0 -0
- chython/files/_xyz.pyx +74 -0
- chython/files/daylight/__init__.py +24 -0
- chython/files/daylight/parser.py +157 -0
- chython/files/daylight/smarts.py +107 -0
- chython/files/daylight/smiles.py +245 -0
- chython/files/daylight/test/__init__.py +18 -0
- chython/files/daylight/test/test_daylight_smarts.py +289 -0
- chython/files/daylight/test/test_daylight_smiles.py +101 -0
- chython/files/daylight/test/test_parser.py +118 -0
- chython/files/daylight/test/test_tokenize.py +77 -0
- chython/files/daylight/tokenize.py +376 -0
- chython/files/libinchi/__init__.py +22 -0
- chython/files/libinchi/libinchi.dll +0 -0
- chython/files/libinchi/wrapper.py +555 -0
- chython/files/mdl/__init__.py +25 -0
- chython/files/mdl/emol.py +209 -0
- chython/files/mdl/erxn.py +67 -0
- chython/files/mdl/mol.py +144 -0
- chython/files/mdl/read.py +225 -0
- chython/files/mdl/rxn.py +67 -0
- chython/files/mdl/stereo.py +63 -0
- chython/files/mdl/write.py +165 -0
- chython/files/xyz.py +78 -0
- chython/periodictable/__init__.py +69 -0
- chython/periodictable/base/__init__.py +25 -0
- chython/periodictable/base/dynamic.py +149 -0
- chython/periodictable/base/element.py +452 -0
- chython/periodictable/base/groups.py +90 -0
- chython/periodictable/base/periods.py +46 -0
- chython/periodictable/base/query.py +473 -0
- chython/periodictable/base/vector.py +119 -0
- chython/periodictable/groupI.py +252 -0
- chython/periodictable/groupII.py +223 -0
- chython/periodictable/groupIII.py +1154 -0
- chython/periodictable/groupIV.py +210 -0
- chython/periodictable/groupIX.py +196 -0
- chython/periodictable/groupV.py +192 -0
- chython/periodictable/groupVI.py +183 -0
- chython/periodictable/groupVII.py +162 -0
- chython/periodictable/groupVIII.py +161 -0
- chython/periodictable/groupX.py +166 -0
- chython/periodictable/groupXI.py +164 -0
- chython/periodictable/groupXII.py +158 -0
- chython/periodictable/groupXIII.py +239 -0
- chython/periodictable/groupXIV.py +262 -0
- chython/periodictable/groupXV.py +276 -0
- chython/periodictable/groupXVI.py +457 -0
- chython/periodictable/groupXVII.py +324 -0
- chython/periodictable/groupXVIII.py +260 -0
- chython/reactor/__init__.py +24 -0
- chython/reactor/base.py +212 -0
- chython/reactor/deprotection.py +565 -0
- chython/reactor/groups.py +128 -0
- chython/reactor/reactions/__init__.py +157 -0
- chython/reactor/reactions/_amidation.py +58 -0
- chython/reactor/reactions/_amine_isocyanate.py +52 -0
- chython/reactor/reactions/_buchwald_hartwig.py +50 -0
- chython/reactor/reactions/_esterification.py +49 -0
- chython/reactor/reactions/_macmillan.py +43 -0
- chython/reactor/reactions/_reductive_amination.py +50 -0
- chython/reactor/reactions/_sonogashira.py +49 -0
- chython/reactor/reactions/_sulfonamidation.py +52 -0
- chython/reactor/reactions/_suzuki_miyaura.py +88 -0
- chython/reactor/reactions/ufe.py +99 -0
- chython/reactor/reactor.py +170 -0
- chython/reactor/retro/__init__.py +77 -0
- chython/reactor/retro/_amidation.py +39 -0
- chython/reactor/retro/_aryl_amination.py +32 -0
- chython/reactor/retro/_mitsunobu.py +49 -0
- chython/reactor/retro/_sonogashira.py +49 -0
- chython/reactor/retro/_suzuki_miyaura.py +54 -0
- chython/reactor/scaffold.py +122 -0
- chython/reactor/test/__init__.py +18 -0
- chython/reactor/test/test_deprotection.py +57 -0
- chython/reactor/test/test_reactor.py +40 -0
- chython/reactor/test/test_scaffold.py +57 -0
- chython/reactor/test/test_transformer.py +40 -0
- chython/reactor/transformer.py +62 -0
- chython/utils/__init__.py +57 -0
- chython/utils/free_wilson.py +114 -0
- chython/utils/grid.py +130 -0
- chython/utils/rdkit.py +165 -0
- chython/utils/retro.py +139 -0
- chython/utils/svg.py +57 -0
- chython/utils/test/__init__.py +18 -0
- chython/utils/test/test_rdkit.py +71 -0
- chython-2.4.dist-info/LICENSE +165 -0
- chython-2.4.dist-info/METADATA +101 -0
- chython-2.4.dist-info/RECORD +171 -0
- chython-2.4.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2018-2024 Ramil Nugmanov <nougmanoff@protonmail.com>
|
|
4
|
+
# Copyright 2019-2020 Dinar Batyrshin <batyrshin-dinar@mail.ru>
|
|
5
|
+
# This file is part of chython.
|
|
6
|
+
#
|
|
7
|
+
# chython is free software; you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
|
9
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
10
|
+
# (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU Lesser General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
18
|
+
# along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
#
|
|
20
|
+
from CachedMethods import cached_method
|
|
21
|
+
from collections import defaultdict
|
|
22
|
+
from math import atan2, sin, cos, hypot
|
|
23
|
+
from typing import Tuple, TYPE_CHECKING, Union
|
|
24
|
+
from uuid import uuid4
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from chython import ReactionContainer, MoleculeContainer
|
|
29
|
+
|
|
30
|
+
cpk = tuple('''
|
|
31
|
+
#909090 #D9FFFF
|
|
32
|
+
#CC80FF #C2FF00 #FFB5B5 #101010 #3050F8 #FF0D0D #90E050 #B3E3F5
|
|
33
|
+
#AB5CF2 #8AFF00 #BFA6A6 #F0C8A0 #FF8000 #C6C600 #1FF01F #80D1E3
|
|
34
|
+
#8F40D4 #3DFF00 #E6E6E6 #BFC2C7 #A6A6AB #8A99C7 #9C7AC7
|
|
35
|
+
#E06633 #F090A0 #50D050 #C88033 #7D80B0 #C28F8F #668F8F #BD80E3 #FFA100 #A62929 #5CB8D1
|
|
36
|
+
#702EB0 #00FF00 #94FFFF #94E0E0 #73C2C9 #54B5B5 #3B9E9E
|
|
37
|
+
#248F8F #0A7D8C #006985 #C0C0C0 #FFD98F #A67573 #668080 #9E63B5 #D47A00 #940094 #429EB0
|
|
38
|
+
#57178F #00C900 #70D4FF
|
|
39
|
+
#FFFFC7 #D9FFC7 #C7FFC7 #A3FFC7 #8FFFC7 #61FFC7 #45FFC7
|
|
40
|
+
#30FFC7 #1FFFC7 #00FF9C #00E675 #00D452 #00BF38 #00AB24
|
|
41
|
+
#4DC2FF #4DA6FF #2194D6 #267DAB
|
|
42
|
+
#266696 #175487 #D0D0E0 #FFD123 #B8B8D0 #A6544D #575961 #9E4FB5 #AB5C00 #754F45 #428296
|
|
43
|
+
#420066 #007D00 #70ABFA
|
|
44
|
+
#00BAFF #00A1FF #008FFF #0080FF #006BFF #545CF2 #785CE3
|
|
45
|
+
#8A4FE3 #A136D4 #B31FD4 #B31FBA #B30DA6 #BD0D87 #C70066
|
|
46
|
+
#CC0059 #D1004F #D90045 #E00038
|
|
47
|
+
#E6002E #EB0026 #EB0026 #EB0026 #EB0026 #EB0026 #EB0026 #EB0026 #EB0026 #EB0026 #EB0026
|
|
48
|
+
'''.split())
|
|
49
|
+
_render_charge = {-4: '4-', -3: '3-', -2: '2-', -1: '-', 1: '+', 2: '2+', 3: '3+', 4: '4+'}
|
|
50
|
+
_render_config = {'carbon': False, 'dashes': (.2, .1), 'span_dy': .15, 'mapping': True, 'font_size': .5,
|
|
51
|
+
'span_size': .35, 'other_size': 0.3, 'monochrome': False, 'bond_color': 'black', 'bond_width': .04,
|
|
52
|
+
'other_color': 'black', 'bond_radius': .02, 'atom_radius': -.2, 'mapping_size': .25,
|
|
53
|
+
'atoms_colors': cpk, 'triple_space': .13, 'double_space': .06, 'mapping_color': '#0305A7',
|
|
54
|
+
'aromatic_space': .14, 'aromatic_dashes': (.15, .05), 'dx_m': .05, 'dy_m': .2,
|
|
55
|
+
'other_font_style': 'monospace', 'dx_ci': .05, 'dy_ci': 0.2, 'symbols_font_style': 'sans-serif',
|
|
56
|
+
'mapping_font_style': 'monospace', 'wedge_space': .08}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _rotate_vector(x1, y1, x2, y2):
|
|
60
|
+
"""
|
|
61
|
+
rotate x,y vector over x2-x1, y2-y1 angle
|
|
62
|
+
"""
|
|
63
|
+
angle = atan2(y2, x2)
|
|
64
|
+
cos_rad = cos(angle)
|
|
65
|
+
sin_rad = sin(angle)
|
|
66
|
+
return cos_rad * x1 - sin_rad * y1, sin_rad * x1 + cos_rad * y1
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _graph_svg(atoms, bonds, define, masks, uid, viewbox_x, viewbox_y, width, height):
|
|
70
|
+
svg = [f' <g id="{uid}-molecule">\n <defs>']
|
|
71
|
+
svg.extend(define)
|
|
72
|
+
if bonds:
|
|
73
|
+
if masks:
|
|
74
|
+
svg.append(f' <mask id="{uid}-mask">\n'
|
|
75
|
+
f' <rect x="{viewbox_x:.2f}" y="{viewbox_y:.2f}" '
|
|
76
|
+
f'width="{width:.2f}" height="{height:.2f}" fill="white"/>')
|
|
77
|
+
svg.extend(masks)
|
|
78
|
+
svg.append(' </mask>\n </defs>\n'
|
|
79
|
+
f' <g fill="none" stroke="{_render_config["bond_color"]}" '
|
|
80
|
+
f'stroke-width="{_render_config["bond_width"]:.2f}" mask="url(#{uid}-mask)">')
|
|
81
|
+
if len(bonds) == 1: # SVG BUG adhoc
|
|
82
|
+
svg.append(f' <line x1="{viewbox_x:.2f}" y1="{viewbox_y:.2f}" '
|
|
83
|
+
f'x2="{viewbox_x + width:.2f}" y2="{viewbox_y:.2f}" stroke="none"/>')
|
|
84
|
+
else:
|
|
85
|
+
svg.append(f' </defs>\n <g fill="none" stroke="{_render_config["bond_color"]}" '
|
|
86
|
+
f'stroke-width="{_render_config["bond_width"]:.2f}">')
|
|
87
|
+
svg.extend(bonds)
|
|
88
|
+
svg.append(' </g>')
|
|
89
|
+
else:
|
|
90
|
+
svg.append(' </defs>')
|
|
91
|
+
|
|
92
|
+
svg.extend(atoms)
|
|
93
|
+
svg.append(' </g>')
|
|
94
|
+
return svg
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _render_aromatic_bond(n_x, n_y, m_x, m_y, c_x, c_y):
|
|
98
|
+
aromatic_space = _render_config['aromatic_space']
|
|
99
|
+
dash3, dash4 = _render_config['aromatic_dashes']
|
|
100
|
+
# n aligned xy
|
|
101
|
+
mn_x, mn_y, cn_x, cn_y = m_x - n_x, m_y - n_y, c_x - n_x, c_y - n_y
|
|
102
|
+
|
|
103
|
+
# nm reoriented xy
|
|
104
|
+
mr_x, mr_y = hypot(mn_x, mn_y), 0
|
|
105
|
+
cr_x, cr_y = _rotate_vector(cn_x, cn_y, mn_x, -mn_y)
|
|
106
|
+
|
|
107
|
+
if cr_y and aromatic_space / cr_y < .65:
|
|
108
|
+
if cr_y > 0:
|
|
109
|
+
r_y = aromatic_space
|
|
110
|
+
else:
|
|
111
|
+
r_y = -aromatic_space
|
|
112
|
+
cr_y = -cr_y
|
|
113
|
+
|
|
114
|
+
ar_x = aromatic_space * cr_x / cr_y
|
|
115
|
+
br_x = mr_x - aromatic_space * (mr_x - cr_x) / cr_y
|
|
116
|
+
|
|
117
|
+
# backward reorienting
|
|
118
|
+
an_x, an_y = _rotate_vector(ar_x, r_y, mn_x, mn_y)
|
|
119
|
+
bn_x, bn_y = _rotate_vector(br_x, r_y, mn_x, mn_y)
|
|
120
|
+
a_x, a_y = n_x + an_x, n_y + an_y
|
|
121
|
+
b_x, b_y = n_x + bn_x, n_y + bn_y
|
|
122
|
+
|
|
123
|
+
return f' <line x1="{a_x:.2f}" y1="{-a_y:.2f}" x2="{b_x:.2f}" y2="{-b_y:.2f}" ' \
|
|
124
|
+
f'stroke-dasharray="{dash3:.2f} {dash4:.2f}"/>'
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def depict_settings(*, carbon: bool = False, aam: bool = True, monochrome: bool = False,
|
|
128
|
+
bond_color: str = 'black', aam_color: str = '#0305A7', atoms_colors: tuple = cpk,
|
|
129
|
+
bond_width: float = .04, wedge_space: float = .08, dashes: Tuple[float, float] = (.2, .1),
|
|
130
|
+
aromatic_dashes: Tuple[float, float] = (.15, .05), dx_ci: float = .05, dy_ci: float = .2,
|
|
131
|
+
dx_m: float = .05, dy_m: float = .2, span_dy: float = .15, double_space: float = .06,
|
|
132
|
+
triple_space: float = .13, aromatic_space: float = .14, atom_radius: float = .2, bond_radius=.02,
|
|
133
|
+
font_size: float = .5, other_size: float = .3, span_size: float = .35, aam_size: float = .25,
|
|
134
|
+
symbols_font_style: str = 'sans-serif', other_font_style: str = 'monospace',
|
|
135
|
+
other_color: str = 'black', mapping_font_style: str = 'monospace'):
|
|
136
|
+
"""
|
|
137
|
+
Settings for depict of chemical structures
|
|
138
|
+
|
|
139
|
+
:param carbon: if True, depict atom C
|
|
140
|
+
:param font_size: font size
|
|
141
|
+
:param aam_size: atom-to-atom mapping font size
|
|
142
|
+
:param span_size: font size for hydrogen count
|
|
143
|
+
:param other_size: isotope, radical, charges, neighbors and hybridization symbols size
|
|
144
|
+
:param bond_width: bond width
|
|
145
|
+
:param bond_color: color of bonds
|
|
146
|
+
:param aam_color: atom-to-atom mapping color
|
|
147
|
+
:param atoms_colors: atom colors where key is atomic number - 1, value is atom color (str)
|
|
148
|
+
:param other_color: color for charges, radicals, isotopes
|
|
149
|
+
:param symbols_font_style: font style for atom symbols
|
|
150
|
+
:param other_font_style: font style for charges, radicals, isotopes, hybridization and neighbors
|
|
151
|
+
:param aam: if True, depict mapping
|
|
152
|
+
:param monochrome: if True, colors of items in molecule not used
|
|
153
|
+
:param dashes: first value is long of visible line, second is long of invisible line
|
|
154
|
+
:param aromatic_space: space between simple and aromatic bonds
|
|
155
|
+
:param triple_space: space between simple and triple bonds
|
|
156
|
+
:param double_space: space between simple and double bonds
|
|
157
|
+
:param aromatic_dashes: first value is long of visible line, second is long of invisible line
|
|
158
|
+
:param atom_radius: radius of atoms spheres in depict3d. if negative is multiplier to covalent radii
|
|
159
|
+
:param bond_radius: radius of bonds spheres in depict3d
|
|
160
|
+
:param dx_ci: x-axis offset relative to the center of the atom symbol for radical, charges, isotope
|
|
161
|
+
:param dy_ci: y-axis offset relative to the center of the atom symbol for radical, charges, isotope
|
|
162
|
+
:param dx_m: x-axis offset relative to the center of the atom symbol for atom-to-atom mapping
|
|
163
|
+
:param dy_m: y-axis offset relative to the center of the atom symbol for atom-to-atom mapping
|
|
164
|
+
:param span_dy: y-axis offset relative to the center of the atom symbol for hydrogen count
|
|
165
|
+
:param mapping_font_style: font style for mapping
|
|
166
|
+
:param wedge_space: wedge bond width
|
|
167
|
+
"""
|
|
168
|
+
_render_config['carbon'] = carbon
|
|
169
|
+
_render_config['dashes'] = dashes
|
|
170
|
+
_render_config['span_dy'] = span_dy
|
|
171
|
+
_render_config['mapping'] = aam
|
|
172
|
+
_render_config['font_size'] = font_size
|
|
173
|
+
_render_config['span_size'] = span_size
|
|
174
|
+
_render_config['other_size'] = other_size
|
|
175
|
+
_render_config['monochrome'] = monochrome
|
|
176
|
+
_render_config['bond_color'] = bond_color
|
|
177
|
+
_render_config['bond_width'] = bond_width
|
|
178
|
+
_render_config['other_color'] = other_color
|
|
179
|
+
_render_config['bond_radius'] = bond_radius
|
|
180
|
+
_render_config['atom_radius'] = -atom_radius
|
|
181
|
+
_render_config['mapping_size'] = aam_size
|
|
182
|
+
_render_config['atoms_colors'] = atoms_colors
|
|
183
|
+
_render_config['triple_space'] = triple_space
|
|
184
|
+
_render_config['double_space'] = double_space
|
|
185
|
+
_render_config['mapping_color'] = aam_color
|
|
186
|
+
_render_config['aromatic_space'] = aromatic_space
|
|
187
|
+
_render_config['aromatic_dashes'] = aromatic_dashes
|
|
188
|
+
_render_config['dx_m'], _render_config['dy_m'] = dx_m, dy_m
|
|
189
|
+
_render_config['other_font_style'] = other_font_style
|
|
190
|
+
_render_config['dx_ci'], _render_config['dy_ci'] = dx_ci, dy_ci
|
|
191
|
+
_render_config['symbols_font_style'] = symbols_font_style
|
|
192
|
+
_render_config['mapping_font_style'] = mapping_font_style
|
|
193
|
+
_render_config['wedge_space'] = wedge_space
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class DepictMolecule:
|
|
197
|
+
__slots__ = ()
|
|
198
|
+
|
|
199
|
+
def depict(self: Union['MoleculeContainer', 'DepictMolecule'], *, width=None, height=None, clean2d: bool = True,
|
|
200
|
+
_embedding=False) -> str:
|
|
201
|
+
"""
|
|
202
|
+
Depict molecule in SVG format.
|
|
203
|
+
|
|
204
|
+
:param width: set svg width param. by default auto-calculated.
|
|
205
|
+
:param height: set svg height param. by default auto-calculated.
|
|
206
|
+
:param clean2d: calculate coordinates if necessary.
|
|
207
|
+
"""
|
|
208
|
+
uid = str(uuid4())
|
|
209
|
+
min_x = min(a.x for _, a in self.atoms())
|
|
210
|
+
max_x = max(a.x for _, a in self.atoms())
|
|
211
|
+
min_y = min(a.y for _, a in self.atoms())
|
|
212
|
+
max_y = max(a.y for _, a in self.atoms())
|
|
213
|
+
if clean2d and len(self) > 1 and max_y - min_y < .01 and max_x - min_x < 0.01:
|
|
214
|
+
self.clean2d()
|
|
215
|
+
min_x = min(a.x for _, a in self.atoms())
|
|
216
|
+
max_x = max(a.x for _, a in self.atoms())
|
|
217
|
+
min_y = min(a.y for _, a in self.atoms())
|
|
218
|
+
max_y = max(a.y for _, a in self.atoms())
|
|
219
|
+
|
|
220
|
+
bonds = self.__render_bonds()
|
|
221
|
+
atoms, define, masks = self.__render_atoms(uid)
|
|
222
|
+
if _embedding:
|
|
223
|
+
return atoms, bonds, define, masks, uid, min_x, min_y, max_x, max_y
|
|
224
|
+
|
|
225
|
+
font_size = _render_config['font_size']
|
|
226
|
+
font125 = 1.25 * font_size
|
|
227
|
+
_width = max_x - min_x + 4.0 * font_size
|
|
228
|
+
_height = max_y - min_y + 2.5 * font_size
|
|
229
|
+
viewbox_x = min_x - font125
|
|
230
|
+
viewbox_y = -max_y - font125
|
|
231
|
+
|
|
232
|
+
if width is None:
|
|
233
|
+
width = f'{_width:.2f}cm'
|
|
234
|
+
if height is None:
|
|
235
|
+
height = f'{_height:.2f}cm'
|
|
236
|
+
|
|
237
|
+
svg = [f'<svg width="{width}" height="{height}" '
|
|
238
|
+
f'viewBox="{viewbox_x:.2f} {viewbox_y:.2f} {_width:.2f} {_height:.2f}" '
|
|
239
|
+
'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">']
|
|
240
|
+
svg.extend(_graph_svg(atoms, bonds, define, masks, uid, viewbox_x, viewbox_y, _width, _height))
|
|
241
|
+
svg.append('</svg>')
|
|
242
|
+
return '\n'.join(svg)
|
|
243
|
+
|
|
244
|
+
@cached_method
|
|
245
|
+
def _repr_svg_(self):
|
|
246
|
+
return self.depict()
|
|
247
|
+
|
|
248
|
+
def __render_bonds(self: Union['MoleculeContainer', 'DepictMolecule']):
|
|
249
|
+
atoms = self._atoms
|
|
250
|
+
svg = []
|
|
251
|
+
double_space = _render_config['double_space']
|
|
252
|
+
triple_space = _render_config['triple_space']
|
|
253
|
+
wedge_space = _render_config['wedge_space']
|
|
254
|
+
dash1, dash2 = _render_config['dashes']
|
|
255
|
+
color = f' fill="{_render_config["bond_color"]}"'
|
|
256
|
+
|
|
257
|
+
wedge = defaultdict(set)
|
|
258
|
+
for n, m, s in self._wedge_map:
|
|
259
|
+
wedge[n].add(m)
|
|
260
|
+
wedge[m].add(n)
|
|
261
|
+
|
|
262
|
+
nx, ny = atoms[n].xy
|
|
263
|
+
mx, my = atoms[m].xy
|
|
264
|
+
ny, my = -ny, -my
|
|
265
|
+
dx, dy = _rotate_vector(0, wedge_space, mx - nx, ny - my)
|
|
266
|
+
|
|
267
|
+
svg.append(f' <path d="M{nx:.2f} {ny:.2f} L{mx + dx:.2f} {my + dy:.2f} '
|
|
268
|
+
f'L{mx - dx:.2f} {my - dy:.2f} Z"{s == 1 and color or ""}/>')
|
|
269
|
+
|
|
270
|
+
for n, m, bond in self.bonds():
|
|
271
|
+
if m in wedge[n]:
|
|
272
|
+
continue
|
|
273
|
+
nx, ny = atoms[n].xy
|
|
274
|
+
mx, my = atoms[m].xy
|
|
275
|
+
ny, my = -ny, -my
|
|
276
|
+
if bond in (1, 4):
|
|
277
|
+
svg.append(f' <line x1="{nx:.2f}" y1="{ny:.2f}" x2="{mx:.2f}" y2="{my:.2f}"/>')
|
|
278
|
+
elif bond == 2:
|
|
279
|
+
dx, dy = _rotate_vector(0, double_space, mx - nx, ny - my)
|
|
280
|
+
svg.append(f' <line x1="{nx + dx:.2f}" y1="{ny - dy:.2f}" x2="{mx + dx:.2f}" y2="{my - dy:.2f}"/>')
|
|
281
|
+
svg.append(f' <line x1="{nx - dx:.2f}" y1="{ny + dy:.2f}" x2="{mx - dx:.2f}" y2="{my + dy:.2f}"/>')
|
|
282
|
+
elif bond == 3:
|
|
283
|
+
dx, dy = _rotate_vector(0, triple_space, mx - nx, ny - my)
|
|
284
|
+
svg.append(f' <line x1="{nx + dx:.2f}" y1="{ny - dy:.2f}" x2="{mx + dx:.2f}" y2="{my - dy:.2f}"/>')
|
|
285
|
+
svg.append(f' <line x1="{nx:.2f}" y1="{ny:.2f}" x2="{mx:.2f}" y2="{my:.2f}"/>')
|
|
286
|
+
svg.append(f' <line x1="{nx - dx:.2f}" y1="{ny + dy:.2f}" x2="{mx - dx:.2f}" y2="{my + dy:.2f}"/>')
|
|
287
|
+
else:
|
|
288
|
+
svg.append(f' <line x1="{nx:.2f}" y1="{ny:.2f}" x2="{mx:.2f}" y2="{my:.2f}" '
|
|
289
|
+
f'stroke-dasharray="{dash1:.2f} {dash2:.2f}"/>')
|
|
290
|
+
|
|
291
|
+
for ring in self.aromatic_rings:
|
|
292
|
+
cx = sum(atoms[n].x for n in ring) / len(ring)
|
|
293
|
+
cy = sum(atoms[n].y for n in ring) / len(ring)
|
|
294
|
+
|
|
295
|
+
for n, m in zip(ring, ring[1:]):
|
|
296
|
+
nx, ny = atoms[n].xy
|
|
297
|
+
mx, my = atoms[m].xy
|
|
298
|
+
aromatic = _render_aromatic_bond(nx, ny, mx, my, cx, cy)
|
|
299
|
+
if aromatic:
|
|
300
|
+
svg.append(aromatic)
|
|
301
|
+
|
|
302
|
+
nx, ny = atoms[ring[-1]].xy
|
|
303
|
+
mx, my = atoms[ring[0]].xy
|
|
304
|
+
aromatic = _render_aromatic_bond(nx, ny, mx, my, cx, cy)
|
|
305
|
+
if aromatic:
|
|
306
|
+
svg.append(aromatic)
|
|
307
|
+
return svg
|
|
308
|
+
|
|
309
|
+
def __render_atoms(self: 'MoleculeContainer', uid):
|
|
310
|
+
bonds = self._bonds
|
|
311
|
+
|
|
312
|
+
carbon = _render_config['carbon']
|
|
313
|
+
mapping = _render_config['mapping']
|
|
314
|
+
span_size = _render_config['span_size']
|
|
315
|
+
font_size = _render_config['font_size']
|
|
316
|
+
monochrome = _render_config['monochrome']
|
|
317
|
+
other_size = _render_config['other_size']
|
|
318
|
+
atoms_colors = _render_config['atoms_colors']
|
|
319
|
+
mapping_size = _render_config['mapping_size']
|
|
320
|
+
dx_m, dy_m = _render_config['dx_m'], _render_config['dy_m']
|
|
321
|
+
dx_ci, dy_ci = _render_config['dx_ci'], _render_config['dy_ci']
|
|
322
|
+
symbols_font_style = _render_config['symbols_font_style']
|
|
323
|
+
span_dy = _render_config['span_dy']
|
|
324
|
+
other_font_style = _render_config['other_font_style']
|
|
325
|
+
mapping_font_style = _render_config['mapping_font_style']
|
|
326
|
+
|
|
327
|
+
if monochrome:
|
|
328
|
+
map_fill = other_fill = 'black'
|
|
329
|
+
else:
|
|
330
|
+
map_fill = _render_config['mapping_color']
|
|
331
|
+
other_fill = _render_config['other_color']
|
|
332
|
+
|
|
333
|
+
font2 = .2 * font_size
|
|
334
|
+
font3 = .3 * font_size
|
|
335
|
+
font4 = .4 * font_size
|
|
336
|
+
font5 = .5 * font_size
|
|
337
|
+
font6 = .6 * font_size
|
|
338
|
+
font7 = .7 * font_size
|
|
339
|
+
font15 = .15 * font_size
|
|
340
|
+
font25 = .25 * font_size
|
|
341
|
+
stroke_width_s = font_size * .1
|
|
342
|
+
stroke_width_o = other_size * .1
|
|
343
|
+
stroke_width_m = mapping_size * .1
|
|
344
|
+
|
|
345
|
+
svg = []
|
|
346
|
+
maps = []
|
|
347
|
+
symbols = []
|
|
348
|
+
fill_zone = []
|
|
349
|
+
others = []
|
|
350
|
+
define = []
|
|
351
|
+
mask = []
|
|
352
|
+
|
|
353
|
+
for n, atom in self.atoms():
|
|
354
|
+
x, y = atom.x, -atom.y
|
|
355
|
+
symbol = atom.atomic_symbol
|
|
356
|
+
if (symbol != 'C' or atom.charge or atom.is_radical or atom.isotope or carbon
|
|
357
|
+
or not bonds[n] or sum(b == 2 for b in bonds[n].values()) == 2):
|
|
358
|
+
if atom.charge:
|
|
359
|
+
others.append(f' <text x="{x:.2f}" y="{y:.2f}" dx="{dx_ci:.2f}" dy="-{dy_ci:.2f}">'
|
|
360
|
+
f'{_render_charge[atom.charge]}{"↑" if atom.is_radical else ""}</text>')
|
|
361
|
+
elif atom.is_radical:
|
|
362
|
+
others.append(f' <text x="{x:.2f}" y="{y:.2f}" dx="{dx_ci:.2f}" dy="-{dy_ci:.2f}">↑</text>')
|
|
363
|
+
if atom.isotope:
|
|
364
|
+
others.append(f' <text x="{x:.2f}" y="{y:.2f}" dx="-{dx_ci:.2f}" dy="-{dy_ci:.2f}" '
|
|
365
|
+
f'text-anchor="end">{atom.isotope}</text>')
|
|
366
|
+
|
|
367
|
+
if len(symbol) > 1:
|
|
368
|
+
dx = font7
|
|
369
|
+
dx_mm = dx_m + font5
|
|
370
|
+
if symbol[-1] in ('l', 'i', 'r', 't'):
|
|
371
|
+
rx = font6
|
|
372
|
+
ax = font25
|
|
373
|
+
else:
|
|
374
|
+
rx = font7
|
|
375
|
+
ax = font15
|
|
376
|
+
fill_zone.append(f' <ellipse cx="{x - ax:.2f}" cy="{y:.2f}" rx="{rx}" ry="{font4}"/>')
|
|
377
|
+
else:
|
|
378
|
+
if symbol == 'I':
|
|
379
|
+
dx = font15
|
|
380
|
+
dx_mm = dx_m
|
|
381
|
+
else:
|
|
382
|
+
dx = font4
|
|
383
|
+
dx_mm = dx_m + font2
|
|
384
|
+
fill_zone.append(f' <circle cx="{x:.2f}" cy="{y:.2f}" r="{font4:.2f}"/>')
|
|
385
|
+
|
|
386
|
+
h = atom.implicit_hydrogens
|
|
387
|
+
if h == 1:
|
|
388
|
+
h = 'H'
|
|
389
|
+
elif h:
|
|
390
|
+
h = f'H<tspan dy="{span_dy:.2f}" font-size="{span_size:.2f}">{h}</tspan>'
|
|
391
|
+
else:
|
|
392
|
+
h = ''
|
|
393
|
+
symbols.append(f' <text id="{uid}-{n}" x="{x:.2f}" y="{y:.2f}" dx="-{dx:.2f}" dy="{font4:.2f}">'
|
|
394
|
+
f'{symbol}{h}</text>')
|
|
395
|
+
|
|
396
|
+
svg.append(f' <use xlink:href="#{uid}-{n}" '
|
|
397
|
+
f'fill="{"black" if monochrome else atoms_colors[atom.atomic_number - 1]}"/>')
|
|
398
|
+
|
|
399
|
+
if mapping:
|
|
400
|
+
maps.append(f' <text x="{x:.2f}" y="{y:.2f}" dx="-{dx_mm:.2f}" '
|
|
401
|
+
f'dy="{dy_m + font3:.2f}">{n}</text>')
|
|
402
|
+
elif mapping:
|
|
403
|
+
maps.append(f' <text x="{x:.2f}" y="{y:.2f}" dx="-{dx_m:.2f}" dy="{dy_m:.2f}">{n}</text>')
|
|
404
|
+
|
|
405
|
+
if svg: # group atoms symbols
|
|
406
|
+
if fill_zone:
|
|
407
|
+
mask.append(' <g>')
|
|
408
|
+
mask.extend(fill_zone)
|
|
409
|
+
mask.append(' </g>')
|
|
410
|
+
|
|
411
|
+
svg.insert(0, f' <g font-size="{font_size:.2f}" font-family="{symbols_font_style}">')
|
|
412
|
+
svg.append(' </g>')
|
|
413
|
+
define.append(f' <g id="{uid}-symbols" font-size="{font_size:.2f}" '
|
|
414
|
+
f'font-family="{symbols_font_style}">')
|
|
415
|
+
define.extend(symbols)
|
|
416
|
+
define.append(' </g>')
|
|
417
|
+
mask.append(' <g stroke="black">\n '
|
|
418
|
+
f'<use xlink:href="#{uid}-symbols" stroke-width="{stroke_width_s:.2f}"/>')
|
|
419
|
+
|
|
420
|
+
if others:
|
|
421
|
+
define.append(f' <g id="{uid}-attrs" font-size="{other_size:.2f}" '
|
|
422
|
+
f'font-family="{other_font_style}">')
|
|
423
|
+
define.extend(others)
|
|
424
|
+
define.append(' </g>')
|
|
425
|
+
svg.append(f' <use xlink:href="#{uid}-attrs" fill="{other_fill}"/>')
|
|
426
|
+
mask.append(f' <use xlink:href="#{uid}-attrs" stroke-width="{stroke_width_o:.2f}"/>')
|
|
427
|
+
if maps:
|
|
428
|
+
if not svg: # no atoms but maps
|
|
429
|
+
mask.append(' <g stroke="black">')
|
|
430
|
+
define.append(f' <g id="{uid}-mapping" font-size="{mapping_size:.2f}" '
|
|
431
|
+
f'font-family="{mapping_font_style}" text-anchor="end">')
|
|
432
|
+
define.extend(maps)
|
|
433
|
+
define.append(' </g>')
|
|
434
|
+
svg.append(f' <use xlink:href="#{uid}-mapping" fill="{map_fill}"/>')
|
|
435
|
+
mask.append(f' <use xlink:href="#{uid}-mapping" stroke-width="{stroke_width_m:.2f}"/>\n'
|
|
436
|
+
' </g>')
|
|
437
|
+
elif svg: # no maps but atoms
|
|
438
|
+
mask.append(' </g>')
|
|
439
|
+
return svg, define, mask
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
class DepictReaction:
|
|
443
|
+
__slots__ = ()
|
|
444
|
+
|
|
445
|
+
def depict(self: 'ReactionContainer', *, width=None, height=None, clean2d: bool = True) -> str:
|
|
446
|
+
"""
|
|
447
|
+
Depict reaction in SVG format.
|
|
448
|
+
|
|
449
|
+
:param width: set svg width param. by default auto-calculated.
|
|
450
|
+
:param height: set svg height param. by default auto-calculated.
|
|
451
|
+
:param clean2d: calculate coordinates if necessary.
|
|
452
|
+
"""
|
|
453
|
+
if not self._arrow:
|
|
454
|
+
if clean2d:
|
|
455
|
+
for m in self.molecules():
|
|
456
|
+
if len(m) > 1:
|
|
457
|
+
min_x = min(a.x for _, a in m.atoms())
|
|
458
|
+
max_x = max(a.x for _, a in m.atoms())
|
|
459
|
+
min_y = min(a.y for _, a in m.atoms())
|
|
460
|
+
max_y = max(a.y for _, a in m.atoms())
|
|
461
|
+
if max_y - min_y < .01 and max_x - min_x < 0.01:
|
|
462
|
+
m.clean2d()
|
|
463
|
+
self.fix_positions()
|
|
464
|
+
|
|
465
|
+
r_atoms = []
|
|
466
|
+
r_bonds = []
|
|
467
|
+
r_defines = []
|
|
468
|
+
r_masks = []
|
|
469
|
+
r_uids = []
|
|
470
|
+
r_max_x = r_max_y = r_min_y = 0
|
|
471
|
+
for m in self.molecules():
|
|
472
|
+
atoms, bonds, define, masks, uid, min_x, min_y, max_x, max_y = m.depict(clean2d=False, _embedding=True)
|
|
473
|
+
r_atoms.append(atoms)
|
|
474
|
+
r_bonds.append(bonds)
|
|
475
|
+
r_defines.append(define)
|
|
476
|
+
r_masks.append(masks)
|
|
477
|
+
r_uids.append(uid)
|
|
478
|
+
if max_x > r_max_x:
|
|
479
|
+
r_max_x = max_x
|
|
480
|
+
if max_y > r_max_y:
|
|
481
|
+
r_max_y = max_y
|
|
482
|
+
if min_y < r_min_y:
|
|
483
|
+
r_min_y = min_y
|
|
484
|
+
|
|
485
|
+
font_size = _render_config['font_size']
|
|
486
|
+
font125 = 1.25 * font_size
|
|
487
|
+
_width = r_max_x + 4.0 * font_size
|
|
488
|
+
_height = r_max_y - r_min_y + 2.5 * font_size
|
|
489
|
+
viewbox_x = -font125
|
|
490
|
+
viewbox_y = -r_max_y - font125
|
|
491
|
+
|
|
492
|
+
if width is None:
|
|
493
|
+
width = f'{_width:.2f}cm'
|
|
494
|
+
if height is None:
|
|
495
|
+
height = f'{_height:.2f}cm'
|
|
496
|
+
|
|
497
|
+
svg = [f'<svg width="{width}" height="{height}" '
|
|
498
|
+
f'viewBox="{viewbox_x:.2f} {viewbox_y:.2f} {_width:.2f} {_height:.2f}" '
|
|
499
|
+
'xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">\n'
|
|
500
|
+
' <defs>\n <marker id="arrow" markerWidth="10" markerHeight="10" '
|
|
501
|
+
'refX="0" refY="3" orient="auto">\n <path d="M0,0 L0,6 L9,3"/>\n </marker>\n </defs>\n'
|
|
502
|
+
f' <line x1="{self._arrow[0]:.2f}" y1="0" x2="{self._arrow[1]:.2f}" y2="0" '
|
|
503
|
+
'fill="none" stroke="black" stroke-width=".04" marker-end="url(#arrow)"/>']
|
|
504
|
+
|
|
505
|
+
sings_plus = self._signs
|
|
506
|
+
if sings_plus:
|
|
507
|
+
svg.append(f' <g fill="none" stroke="black" stroke-width=".04">')
|
|
508
|
+
for x in sings_plus:
|
|
509
|
+
svg.append(f' <line x1="{x + .35:.2f}" y1="0" x2="{x + .65:.2f}" y2="0"/>')
|
|
510
|
+
svg.append(f' <line x1="{x + .5:.2f}" y1="0.15" x2="{x + .5:.2f}" y2="-0.15"/>')
|
|
511
|
+
svg.append(' </g>')
|
|
512
|
+
|
|
513
|
+
for atoms, bonds, define, masks, uid in zip(r_atoms, r_bonds, r_defines, r_masks, r_uids):
|
|
514
|
+
svg.extend(_graph_svg(atoms, bonds, define, masks, uid, viewbox_x, viewbox_y, _width, _height))
|
|
515
|
+
svg.append('</svg>')
|
|
516
|
+
return '\n'.join(svg)
|
|
517
|
+
|
|
518
|
+
@cached_method
|
|
519
|
+
def _repr_svg_(self):
|
|
520
|
+
return self.depict()
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
__all__ = ['DepictMolecule', 'DepictReaction', 'depict_settings']
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2021-2023 Ramil Nugmanov <nougmanoff@protonmail.com>
|
|
4
|
+
# Copyright 2021 Aleksandr Sizov <murkyrussian@gmail.com>
|
|
5
|
+
# This file is part of chython.
|
|
6
|
+
#
|
|
7
|
+
# chython is free software; you can redistribute it and/or modify
|
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
|
9
|
+
# the Free Software Foundation; either version 3 of the License, or
|
|
10
|
+
# (at your option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
# GNU Lesser General Public License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
|
18
|
+
# along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
19
|
+
#
|
|
20
|
+
from typing import TYPE_CHECKING
|
|
21
|
+
from .linear import *
|
|
22
|
+
from .morgan import *
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from chython import MoleculeContainer, CGRContainer
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Fingerprints(LinearFingerprint, MorganFingerprint):
|
|
30
|
+
__slots__ = ()
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def _atom_identifiers(self: 'MoleculeContainer'):
|
|
34
|
+
return {idx: hash((atom.isotope or 0, atom.atomic_number, atom.charge, atom.is_radical))
|
|
35
|
+
for idx, atom in self.atoms()}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class FingerprintsCGR(LinearFingerprint, MorganFingerprint):
|
|
39
|
+
__slots__ = ()
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def _atom_identifiers(self: 'CGRContainer'):
|
|
43
|
+
return {idx: hash((atom.isotope or 0, atom.atomic_number, atom.charge, atom.p_charge,
|
|
44
|
+
atom.is_radical, atom.p_is_radical))
|
|
45
|
+
for idx, atom in self._atoms.items()}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = ['Fingerprints', 'FingerprintsCGR']
|