1
2
3
4
5
6
7 import Chem
8 import RDConfig
9 import math
10
11 elemDict={
12 7:(0,0,1),
13 8:(1,0,0),
14 9:(.2,.8,.8),
15 15:(1,.5,0),
16 16:(.8,.8,0),
17 17:(0,.8,0),
18 35:(.5,.3,.1),
19 0:(.5,.5,.5),
20 }
21
32
34 dotsPerAngstrom = 30
35
36 atomLabelFontFace = "sans"
37 atomLabelFontSize = 12
38 atomLabelMinFontSize = 10
39
40 bondLineWidth = 1.2
41 dblBondOffset = .2
42 dblBondLengthFrac = .8
43
44 defaultColor = (1,0,0)
45 selectColor = (1,0,0)
46
47 colorBonds=True
48 noCarbonSymbols=True
49 includeAtomNumbers=False
50 atomNumberOffset=0
51
52 dash = (1,1)
53 atomPs = None
54 canvas = None
55 canvasSize=None
56
57 wedgeDashedBonds=True
58
64
71
72
87
91 if not lenFrac:
92 lenFrac = self.dblBondLengthFrac
93
94 dx = p2[0]-p1[0]
95 dy = p2[1]-p1[1]
96
97
98
99
100 fracP1 = p1[0]+offsetX,p1[1]+offsetY
101
102
103 frac = (1.-lenFrac)/2
104 fracP1 = fracP1[0]+dx*frac,\
105 fracP1[1]+dy*frac
106
107 fracP2 = fracP1[0]+dx*lenFrac,\
108 fracP1[1]+dy*lenFrac
109 return fracP1,fracP2
110
111 - def _offsetDblBond(self,p1,p2,bond,a1,a2,conf,dir=1,
112 lenFrac=None):
113 perp,offsetX,offsetY = self._getBondOffset(p1,p2)
114 offsetX = offsetX*dir
115 offsetY = offsetY*dir
116
117
118 if bond.IsInRing():
119 bondIdx = bond.GetIdx()
120 a1Idx = a1.GetIdx()
121 a2Idx = a2.GetIdx()
122
123 for otherBond in a1.GetBonds():
124 if otherBond.GetIdx()!=bondIdx and \
125 otherBond.IsInRing():
126 sharedRing=False
127 for ring in self.bondRings:
128 if bondIdx in ring and otherBond.GetIdx() in ring:
129 sharedRing=True
130 break
131 if not sharedRing:
132 continue
133 a3 = otherBond.GetOtherAtom(a1)
134 if a3.GetIdx() != a2Idx:
135 p3 = self.transformPoint(conf.GetAtomPosition(a3.GetIdx()))
136 dx2 = p3[0] - p1[0]
137 dy2 = p3[1] - p1[1]
138 dotP = dx2*offsetX + dy2*offsetY
139 if dotP < 0:
140 perp += math.pi
141 offsetX = math.cos(perp)*self.dblBondOffset*self.dotsPerAngstrom
142 offsetY = math.sin(perp)*self.dblBondOffset*self.dotsPerAngstrom
143
144 fracP1,fracP2 = self._getOffsetBondPts(p1,p2,
145 offsetX,offsetY,
146 lenFrac=lenFrac)
147 return fracP1,fracP2
148
152 perp,offsetX,offsetY = self._getBondOffset(pos,nbrPos)
153 offsetX *=.75
154 offsetY *=.75
155 poly = ((pos[0],pos[1]),
156 (nbrPos[0]+offsetX,nbrPos[1]+offsetY),
157 (nbrPos[0]-offsetX,nbrPos[1]-offsetY))
158
159 if not dash:
160 addCanvasPolygon(canvas,poly,color=color)
161 elif self.wedgeDashedBonds and addCanvasDashedWedge:
162 addCanvasDashedWedge(canvas,poly[0],poly[1],poly[2],color=color)
163 else:
164 addCanvasLine(canvas,pos,nbrPos,linewidth=width*2,color=color,
165 dashes=dash)
166
167 - def _drawBond(self,canvas,bond,atom,nbr,pos,nbrPos,conf,
168 width=bondLineWidth,color=defaultColor,color2=None):
169 bType=bond.GetBondType()
170 if bType == Chem.BondType.SINGLE:
171 bDir = bond.GetBondDir()
172 if bDir in (Chem.BondDir.BEGINWEDGE,Chem.BondDir.BEGINDASH):
173
174 if bond.GetBeginAtom().GetChiralTag() in (Chem.ChiralType.CHI_TETRAHEDRAL_CW,
175 Chem.ChiralType.CHI_TETRAHEDRAL_CCW):
176 p1,p2 = pos,nbrPos
177 else:
178 p2,p1 = pos,nbrPos
179 if bDir==Chem.BondDir.BEGINWEDGE:
180 self._drawWedgedBond(canvas,bond,p1,p2,color=(0,0,0),width=width)
181 elif bDir==Chem.BondDir.BEGINDASH:
182 self._drawWedgedBond(canvas,bond,p1,p2,color=(0,0,0),width=width,
183 dash=self.dash)
184 else:
185 addCanvasLine(canvas,pos,nbrPos,linewidth=width,color=color,color2=color2)
186 elif bType == Chem.BondType.DOUBLE:
187 if bond.IsInRing() or (atom.GetDegree()!=1 and bond.GetOtherAtom(atom).GetDegree()!=1):
188 addCanvasLine(canvas,pos,nbrPos,linewidth=width,color=color,color2=color2)
189 fp1,fp2 = self._offsetDblBond(pos,nbrPos,bond,atom,nbr,conf)
190 addCanvasLine(canvas,fp1,fp2,linewidth=width,color=color,color2=color2)
191 else:
192 fp1,fp2 = self._offsetDblBond(pos,nbrPos,bond,atom,nbr,conf,dir=.5,
193 lenFrac=1.0)
194 addCanvasLine(canvas,fp1,fp2,linewidth=width,color=color,color2=color2)
195 fp1,fp2 = self._offsetDblBond(pos,nbrPos,bond,atom,nbr,conf,dir=-.5,
196 lenFrac=1.0)
197 addCanvasLine(canvas,fp1,fp2,linewidth=width,color=color,color2=color2)
198 elif bType == Chem.BondType.AROMATIC:
199 addCanvasLine(canvas,pos,nbrPos,linewidth=width,color=color,color2=color2)
200 fp1,fp2 = self._offsetDblBond(pos,nbrPos,bond,atom,nbr,conf)
201 addCanvasLine(canvas,fp1,fp2,linewidth=width,color=color,color2=color2,
202 dash=self.dash)
203 elif bType == Chem.BondType.TRIPLE:
204 addCanvasLine(canvas,pos,nbrPos,linewidth=width,color=color,color2=color2)
205 fp1,fp2 = self._offsetDblBond(pos,nbrPos,bond,atom,nbr,conf)
206 addCanvasLine(canvas,fp1,fp2,linewidth=width,color=color,color2=color2)
207 fp1,fp2 = self._offsetDblBond(pos,nbrPos,bond,atom,nbr,conf,dir=-1)
208 addCanvasLine(canvas,fp1,fp2,linewidth=width,color=color,color2=color2)
209
211 canvasSize = self.canvasSize
212 xAccum = 0
213 yAccum = 0
214 minX = 1e8
215 minY = 1e8
216 maxX = -1e8
217 maxY = -1e8
218
219 nAts = mol.GetNumAtoms()
220 for i in range(nAts):
221 pos = conf.GetAtomPosition(i)
222 xAccum += pos[0]
223 yAccum += pos[1]
224 minX = min(minX,pos[0])
225 minY = min(minY,pos[1])
226 maxX = max(maxX,pos[0])
227 maxY = max(maxY,pos[1])
228
229 dx = abs(maxX-minX)
230 dy = abs(maxY-minY)
231 xSize = dx*self.dotsPerAngstrom
232 ySize = dy*self.dotsPerAngstrom
233
234 if coordCenter:
235 molTrans = -xAccum/nAts,-yAccum/nAts
236 else:
237 molTrans = -(minX+(maxX-minX)/2),-(minY+(maxY-minY)/2)
238 self.dotsPerAngstrom=30.0
239 self.molTrans = molTrans
240
241 if xSize>=.95*canvasSize[0]:
242 scale = .9*canvasSize[0]/xSize
243 xSize*=scale
244 ySize*=scale
245 self.dotsPerAngstrom*=scale
246 self.atomLabelFontSize = max(self.atomLabelFontSize*scale,
247 self.atomLabelMinFontSize)
248 if ySize>=.95*canvasSize[1]:
249 scale = .9*canvasSize[1]/ySize
250 xSize*=scale
251 ySize*=scale
252 self.dotsPerAngstrom*=scale
253 self.atomLabelFontSize = max(self.atomLabelFontSize*scale,
254 self.atomLabelMinFontSize)
255 drawingTrans = canvasSize[0]/2,canvasSize[1]/2
256 self.drawingTrans = drawingTrans
257 tMax = self.transformPoint((maxX,maxY))
258 tMin = self.transformPoint((minX,minY))
259
260 - def _drawLabel(self,canvas,label,pos,font,color=None,
261 highlightIt=False):
270
271 - def AddMol(self,mol,canvas=None,centerIt=True,molTrans=(0,0),drawingTrans=(0,0),
272 highlightAtoms=[],confId=-1):
273 """
274
275 Notes:
276 - specifying centerIt will cause molTrans and drawingTrans to be ignored
277
278 """
279 try:
280 dl = addCanvasLine
281 except NameError:
282 registerCanvas('sping')
283 if canvas is None:
284 canvas = self.canvas
285 else:
286 self.canvas = canvas
287 self.canvasSize=canvas.size
288
289 conf = mol.GetConformer(confId)
290
291 if centerIt:
292 self._scaleAndCenter(mol,conf)
293 else:
294 self.molTrans = molTrans
295 self.drawingTrans = drawingTrans
296 font = Font(face=self.atomLabelFontFace,size=self.atomLabelFontSize)
297
298 if not mol.HasProp('_drawingBondsWedged'):
299 Chem.WedgeMolBonds(mol,conf)
300
301 self.atomPs[mol] = {}
302 self.activeMol = mol
303 self.bondRings = mol.GetRingInfo().BondRings()
304 for atom in mol.GetAtoms():
305 idx = atom.GetIdx()
306 pos = self.atomPs[mol].get(idx,None)
307 if pos is None:
308 pos = self.transformPoint(conf.GetAtomPosition(idx))
309 self.atomPs[mol][idx] = pos
310 nbrSum = [0,0]
311 for bond in atom.GetBonds():
312 nbr = bond.GetOtherAtom(atom)
313 nbrIdx = nbr.GetIdx()
314 if nbrIdx > idx:
315 nbrPos = self.atomPs[mol].get(nbrIdx,None)
316 if nbrPos is None:
317 nbrPos = self.transformPoint(conf.GetAtomPosition(nbrIdx))
318 self.atomPs[mol][nbrIdx] = nbrPos
319
320 if highlightAtoms and idx in highlightAtoms and nbrIdx in highlightAtoms:
321 width=2.0*self.bondLineWidth
322 color = self.selectColor
323 color2 = self.selectColor
324 else:
325 width=self.bondLineWidth
326 if self.colorBonds:
327 color = elemDict.get(atom.GetAtomicNum(),(0,0,0))
328 color2 = elemDict.get(nbr.GetAtomicNum(),(0,0,0))
329 else:
330 color = self.defaultColor
331 color2= color
332
333
334
335 if idx==bond.GetBeginAtomIdx():
336 self._drawBond(canvas,bond,atom,nbr,pos,nbrPos,conf,
337 color=color,width=width,color2=color2)
338 else:
339 self._drawBond(canvas,bond,nbr,atom,nbrPos,pos,conf,
340 color=color2,width=width,color2=color)
341 else:
342 nbrPos = self.atomPs[mol][nbrIdx]
343 nbrSum[0] += nbrPos[0]-pos[0]
344 nbrSum[1] += nbrPos[1]-pos[1]
345
346
347 labelIt= not self.noCarbonSymbols or \
348 atom.GetAtomicNum()!=6 or \
349 atom.GetFormalCharge()!=0 or \
350 self.includeAtomNumbers
351 if labelIt:
352 if self.includeAtomNumbers:
353 symbol = str(atom.GetIdx())
354 else:
355 base = atom.GetSymbol()
356 nHs = atom.GetTotalNumHs()
357 if nHs>0:
358 if nHs>1:
359 hs='H%d'%nHs
360 else:
361 hs ='H'
362 else:
363 hs = ''
364 chg = atom.GetFormalCharge()
365 if chg!=0:
366 if chg==1:
367 chg = '+'
368 elif chg==-1:
369 chg = '-'
370 elif chg>1:
371 chg = '+%d'%chg
372 elif chg<-1:
373 chg = '-%d'%chg
374 else:
375 chg = ''
376 if nbrSum[0]<=0:
377 symbol = '%s%s%s'%(base,hs,chg)
378 else:
379 symbol = '%s%s%s'%(chg,hs,base)
380
381 color = elemDict.get(atom.GetAtomicNum(),(0,0,0))
382 self._drawLabel(canvas,symbol,pos,font,color=color,
383 highlightIt=(highlightAtoms and idx in highlightAtoms))
384
386 g= globals()
387 if canvasNm in ('sping','SPING'):
388 from spingCanvas import addCanvasLine,addCanvasText,addCanvasPolygon,addCanvasDashedWedge
389 elif canvasNm in ('agg','AGG'):
390 from aggCanvas import addCanvasLine,addCanvasText,addCanvasPolygon,addCanvasDashedWedge
391 elif canvasNm in ('mpl','MPL'):
392 from mplCanvas import addCanvasLine,addCanvasText,addCanvasPolygon
393 addCanvasDashedWedge=None
394 else:
395 raise ValueError,'unrecognized canvas type'
396 g['addCanvasLine']=addCanvasLine
397 g['addCanvasText']=addCanvasText
398 g['addCanvasPolygon']=addCanvasPolygon
399 g['addCanvasDashedWedge']=addCanvasDashedWedge
400
401 if __name__=='__main__':
402 import sys
403 if len(sys.argv)<2:
404 mol = Chem.MolFromSmiles('O=C1C([C@@H](F)C=CN[C@H](Cl)Br)C(c2c(O)c(NN)ccc2)=C1C#N')
405 else:
406 mol = Chem.MolFromSmiles(sys.argv[1])
407
408
409 Chem.Kekulize(mol)
410 from Chem import rdDepictor
411 rdDepictor.Compute2DCoords(mol)
412
413 if 1:
414 from aggdraw import Draw
415 registerCanvas('agg')
416 from PIL import Image
417 img = Image.new("RGBA",(300,300),"white")
418 canvas=Draw(img)
419 canvas.setantialias(True)
420 drawer = MolDrawing(canvas)
421 drawer.AddMol(mol)
422 canvas.flush()
423 img.save("foo.png")
424 elif 0:
425 from matplotlib.figure import Figure
426 from matplotlib.backends.backend_agg import FigureCanvasAgg
427 registerCanvas('mpl')
428 fig = Figure(figsize=(3,3))
429 ax = fig.add_axes((0,0,1,1),xticks=[],yticks=[],frame_on=False)
430 xd = ax.get_xlim()
431 xd = xd[1]-xd[0]
432 yd = ax.get_ylim()
433 yd = yd[1]-yd[0]
434 ax.size=(xd,yd)
435 drawer = MolDrawing(ax)
436 drawer.AddMol(mol)
437 canv = FigureCanvasAgg(fig)
438 canv.print_figure("foo.png",dpi=80)
439 else:
440 from sping.PDF.pidPDF import PDFCanvas as Canvas
441 canvas = Canvas(size=(300,300),name='test.pdf')
442 registerCanvas('sping')
443 drawer = MolDrawing(canvas)
444 drawer.AddMol(mol)
445 canvas.save()
446