Package rdkit :: Package Chem :: Package Draw :: Module MolDrawing

[hide private]
[frames] | no frames]

# Source Code for Module rdkit.Chem.Draw.MolDrawing

      1  # $Id$
      2  #
      3  #  Copyright (C) 2008-2011 Greg Landrum
      4  #
      5  #   @@ All Rights Reserved @@
      6  #  This file is part of the RDKit.
      7  #  The contents are covered by the terms of the BSD license
      8  #  which is included in the file license.txt, found at the root
      9  #  of the RDKit source tree.
     10  #
     11  from rdkit import Chem
     12  from rdkit import RDConfig
     13  import numpy
     14  import math
     15  import sys
     16  import copy
     17  import pprint
     18  from rdkit.six import cmp
     19
     20  periodicTable=Chem.GetPeriodicTable()
     21
     22 -class Font(object):
     23    face='sans'
     24    size='12'
     25    weight='normal'
     26    name=None
     27 -  def __init__(self,face=None,size=None,name=None,weight=None):
     28      if face: self.face=face
     29      if size: self.size=size
     30      if name: self.name=name
     31      if weight: self.weight=weight
     32
     33 -class DrawingOptions(object):
     34    dotsPerAngstrom= 30
     35    useFraction= 0.85
     36
     37    atomLabelFontFace= "sans"
     38    atomLabelFontSize= 12
     39    atomLabelMinFontSize= 7
     40
     41    bondLineWidth= 1.2
     42    dblBondOffset= .25
     43    dblBondLengthFrac= .8
     44
     45    defaultColor= (1,0,0)
     46    selectColor= (1,0,0)
     47    bgColor = (1,1,1)
     48
     49    colorBonds= True
     50    noCarbonSymbols= True
     51    includeAtomNumbers= False
     52    atomNumberOffset= 0
     53    radicalSymbol= u'\u2219'
     54
     55    dash= (4,4)
     56
     57    wedgeDashedBonds= True
     58    showUnknownDoubleBonds= True
     59
     60    # used to adjust overall scaling for molecules that have been laid out with non-standard
     61    # bond lengths
     62    coordScale= 1.0
     63
     64    elemDict={
     65      1:(0.55,0.55,0.55),
     66      7:(0,0,1),
     67      8:(1,0,0),
     68      9:(.2,.8,.8),
     69      15:(1,.5,0),
     70      16:(.8,.8,0),
     71      17:(0,.8,0),
     72      35:(.5,.3,.1),
     73      53:(.63,.12,.94),
     74      0:(.5,.5,.5),
     75      }
     76
     77 -class MolDrawing(object):
     78    atomPs = None
     79    canvas = None
     80    canvasSize = None
     81
     82 -  def __init__(self,canvas=None, drawingOptions=None):
     83      self.canvas = canvas
     84      if canvas:
     85        self.canvasSize=canvas.size
     86      self.atomPs = {}
     87      if drawingOptions is None:
     88        self.drawingOptions=DrawingOptions()
     89      else:
     90        self.drawingOptions=drawingOptions
     91      self.boundingBoxes = {}
     92
     93      if self.drawingOptions.bgColor is not None:
     94        self.canvas.addCanvasPolygon(((0,0),
     95                                      (canvas.size[0],0),
     96                                      (canvas.size[0],canvas.size[1]),
     97                                      (0,canvas.size[1])),
     98                                     color=self.drawingOptions.bgColor,
     99                                     fill=True,stroke=False)
    100
    101
    102 -  def transformPoint(self,pos):
    103      res = [0,0]
    104      res[0] = (pos[0] + self.molTrans[0])*self.currDotsPerAngstrom*self.drawingOptions.useFraction + self.drawingTrans[0]
    105      res[1] = self.canvasSize[1]-((pos[1] + self.molTrans[1])*self.currDotsPerAngstrom*self.drawingOptions.useFraction + \
    106                                   self.drawingTrans[1])
    107      return res
    108
    109
    110 -  def _getBondOffset(self,p1,p2):
    111      # get the vector between the points:
    112      dx = p2[0]-p1[0]
    113      dy = p2[1]-p1[1]
    114
    115      # figure out the angle and the perpendicular:
    116      ang = math.atan2(dy,dx)
    117      perp = ang + math.pi/2.
    118
    119      # here's the offset for the parallel bond:
    120      offsetX = math.cos(perp)*self.drawingOptions.dblBondOffset*self.currDotsPerAngstrom
    121      offsetY = math.sin(perp)*self.drawingOptions.dblBondOffset*self.currDotsPerAngstrom
    122
    123      return perp,offsetX,offsetY
    124
    125 -  def _getOffsetBondPts(self,p1,p2,
    126                          offsetX,offsetY,
    127                          lenFrac=None):
    128      if not lenFrac:
    129        lenFrac = self.drawingOptions.dblBondLengthFrac
    130
    131      dx = p2[0]-p1[0]
    132      dy = p2[1]-p1[1]
    133      # ----
    134      # now figure out where to start and end it:
    135
    136      # offset the start point:
    137      fracP1 = p1[0]+offsetX,p1[1]+offsetY
    138
    139      # now move a portion of the way along the line to the neighbor:
    140      frac = (1.-lenFrac)/2
    141      fracP1 = fracP1[0]+dx*frac,\
    142               fracP1[1]+dy*frac
    143
    144      fracP2 = fracP1[0]+dx*lenFrac,\
    145               fracP1[1]+dy*lenFrac
    146      return fracP1,fracP2
    147
    148 -  def _offsetDblBond(self,p1,p2,bond,a1,a2,conf,dir=1,
    149                       lenFrac=None):
    150      perp,offsetX,offsetY = self._getBondOffset(p1,p2)
    151      offsetX = offsetX*dir
    152      offsetY = offsetY*dir
    153
    154      # if we're a ring bond, we may need to flip over to the other side:
    155      if bond.IsInRing():
    156        bondIdx = bond.GetIdx()
    157        a1Idx = a1.GetIdx()
    158        a2Idx = a2.GetIdx()
    159        # find a ring bond from a1 to an atom other than a2:
    160        for otherBond in a1.GetBonds():
    161          if otherBond.GetIdx()!=bondIdx and \
    162             otherBond.IsInRing():
    163            sharedRing=False
    164            for ring in self.bondRings:
    165              if bondIdx in ring and otherBond.GetIdx() in ring:
    166                sharedRing=True
    167                break
    168            if not sharedRing:
    169              continue
    170            a3 = otherBond.GetOtherAtom(a1)
    171            if a3.GetIdx() != a2Idx:
    172              p3 = self.transformPoint(conf.GetAtomPosition(a3.GetIdx())*self.drawingOptions.coordScale)
    173              dx2 = p3[0] - p1[0]
    174              dy2 = p3[1] - p1[1]
    175              dotP = dx2*offsetX + dy2*offsetY
    176              if dotP < 0:
    177                perp += math.pi
    178                offsetX = math.cos(perp)*self.drawingOptions.dblBondOffset*self.currDotsPerAngstrom
    179                offsetY = math.sin(perp)*self.drawingOptions.dblBondOffset*self.currDotsPerAngstrom
    180
    181      fracP1,fracP2 = self._getOffsetBondPts(p1,p2,
    182                                             offsetX,offsetY,
    183                                             lenFrac=lenFrac)
    184      return fracP1,fracP2
    185
    186 -  def _getBondAttachmentCoordinates(self, p1, p2, labelSize):
    187      newpos = [None, None]
    188      if labelSize != None:
    189        labelSizeOffset = [labelSize[0][0]/2 + (cmp(p2[0], p1[0]) * labelSize[0][2]), labelSize[0][1]/2]
    190        if p1[1] == p2[1]:
    191          newpos[0] = p1[0] + cmp(p2[0], p1[0]) * labelSizeOffset[0]
    192        else:
    193          if abs(labelSizeOffset[1] * (p2[0] - p1[0]) / (p2[1] - p1[1])) < labelSizeOffset[0]:
    194            newpos[0] = p1[0] + cmp(p2[0], p1[0]) * abs(labelSizeOffset[1] * (p2[0] - p1[0]) / (p2[1] - p1[1]))
    195          else:
    196            newpos[0] = p1[0] + cmp(p2[0], p1[0]) * labelSizeOffset[0]
    197        if p1[0] == p2[0]:
    198          newpos[1] = p1[1] + cmp(p2[1], p1[1]) * labelSizeOffset[1]
    199        else:
    200          if abs(labelSizeOffset[0] * (p1[1] - p2[1]) / (p2[0] - p1[0])) < labelSizeOffset[1]:
    201            newpos[1] = p1[1] + cmp(p2[1], p1[1]) * abs(labelSizeOffset[0] * (p1[1] - p2[1]) / (p2[0] - p1[0]))
    202          else:
    203            newpos[1] = p1[1] + cmp(p2[1], p1[1]) * labelSizeOffset[1]
    204      else:
    205        newpos = copy.deepcopy(p1)
    206      return newpos
    207
    208 -  def _drawWedgedBond(self,bond,pos,nbrPos,
    209                        width=None,color=None,
    210                        dash=None):
    211      if width is None:
    212          width = self.drawingOptions.bondLineWidth
    213      if color is None:
    214          color = self.drawingOptions.defaultColor
    215      perp,offsetX,offsetY = self._getBondOffset(pos,nbrPos)
    216      offsetX *=.75
    217      offsetY *=.75
    218      poly = ((pos[0],pos[1]),
    219              (nbrPos[0]+offsetX,nbrPos[1]+offsetY),
    220              (nbrPos[0]-offsetX,nbrPos[1]-offsetY))
    221      #canvas.drawPolygon(poly,edgeColor=color,edgeWidth=1,fillColor=color,closed=1)
    222      if not dash:
    223        self.canvas.addCanvasPolygon(poly,color=color)
    224      elif self.drawingOptions.wedgeDashedBonds and self.canvas.addCanvasDashedWedge:
    225        self.canvas.addCanvasDashedWedge(poly[0],poly[1],poly[2],color=color)
    226      else:
    227        self.canvas.addCanvasLine(pos,nbrPos,linewidth=width*2,color=color,
    228                      dashes=dash)
    229
    230 -  def _drawBond(self,bond,atom,nbr,pos,nbrPos,conf,
    231                  width=None,color=None,color2=None,labelSize1=None,labelSize2=None):
    232      if width is None:
    233        width = self.drawingOptions.bondLineWidth
    234      if color is None:
    235        color = self.drawingOptions.defaultColor
    236      p1_raw = copy.deepcopy(pos)
    237      p2_raw = copy.deepcopy(nbrPos)
    238      newpos = self._getBondAttachmentCoordinates(p1_raw, p2_raw, labelSize1)
    239      newnbrPos = self._getBondAttachmentCoordinates(p2_raw, p1_raw, labelSize2)
    240      bType=bond.GetBondType()
    241      if bType == Chem.BondType.SINGLE:
    242        bDir = bond.GetBondDir()
    243        if bDir in (Chem.BondDir.BEGINWEDGE,Chem.BondDir.BEGINDASH):
    244          # if the bond is "backwards", change the drawing direction:
    245          if bond.GetBeginAtom().GetChiralTag() in (Chem.ChiralType.CHI_TETRAHEDRAL_CW,
    246                                                    Chem.ChiralType.CHI_TETRAHEDRAL_CCW):
    247            p1,p2 = newpos,newnbrPos
    248            wcolor=color
    249          else:
    250            p2,p1 = newpos,newnbrPos
    251            if color2 is not None:
    252              wcolor=color2
    253            else:
    254              wcolor=self.drawingOptions.defaultColor
    255          if bDir==Chem.BondDir.BEGINWEDGE:
    256            self._drawWedgedBond(bond,p1,p2,color=wcolor,width=width)
    257          elif bDir==Chem.BondDir.BEGINDASH:
    258            self._drawWedgedBond(bond,p1,p2,color=wcolor,width=width,
    259                                 dash=self.drawingOptions.dash)
    260
    261        else:
    262          self.canvas.addCanvasLine(newpos, newnbrPos, linewidth=width, color=color, color2=color2)
    263      elif bType == Chem.BondType.DOUBLE:
    264        crossBond = (self.drawingOptions.showUnknownDoubleBonds and \
    265                       bond.GetStereo() == Chem.BondStereo.STEREOANY)
    266        if not crossBond and \
    267              ( bond.IsInRing() or (atom.GetDegree()!=1 and bond.GetOtherAtom(atom).GetDegree()!=1) ):
    268          self.canvas.addCanvasLine(newpos,newnbrPos,linewidth=width,color=color,color2=color2)
    269          fp1,fp2 = self._offsetDblBond(newpos,newnbrPos,bond,atom,nbr,conf)
    270          self.canvas.addCanvasLine(fp1,fp2,linewidth=width,color=color,color2=color2)
    271        else:
    272          fp1,fp2 = self._offsetDblBond(newpos,newnbrPos,bond,atom,nbr,conf,dir=.5,
    273                                        lenFrac=1.0)
    274          fp3,fp4 = self._offsetDblBond(newpos,newnbrPos,bond,atom,nbr,conf,dir=-.5,
    275                                        lenFrac=1.0)
    276          if crossBond:
    277            fp2,fp4=fp4,fp2
    278          self.canvas.addCanvasLine(fp1,fp2,linewidth=width,color=color,color2=color2)
    279          self.canvas.addCanvasLine(fp3,fp4,linewidth=width,color=color,color2=color2)
    280
    281      elif bType == Chem.BondType.AROMATIC:
    282        self.canvas.addCanvasLine(newpos,newnbrPos,linewidth=width,color=color,color2=color2)
    283        fp1,fp2 = self._offsetDblBond(newpos,newnbrPos,bond,atom,nbr,conf)
    284        self.canvas.addCanvasLine(fp1,fp2,linewidth=width,color=color,color2=color2,
    285                      dash=self.drawingOptions.dash)
    286      elif bType == Chem.BondType.TRIPLE:
    287        self.canvas.addCanvasLine(newpos,newnbrPos,linewidth=width,color=color,color2=color2)
    288        fp1,fp2 = self._offsetDblBond(newpos,newnbrPos,bond,atom,nbr,conf)
    289        self.canvas.addCanvasLine(fp1,fp2,linewidth=width,color=color,color2=color2)
    290        fp1,fp2 = self._offsetDblBond(newpos,newnbrPos,bond,atom,nbr,conf,dir=-1)
    291        self.canvas.addCanvasLine(fp1,fp2,linewidth=width,color=color,color2=color2)
    292      else:
    293        self.canvas.addCanvasLine(newpos, newnbrPos, linewidth=width, color=color, color2=color2,
    294                                  dash=(1,2))
    295
    296 -  def scaleAndCenter(self,mol,conf,coordCenter=False,canvasSize=None,ignoreHs=False):
    297      if canvasSize is None:
    298        canvasSize=self.canvasSize
    299      xAccum = 0
    300      yAccum = 0
    301      minX = 1e8
    302      minY = 1e8
    303      maxX = -1e8
    304      maxY = -1e8
    305
    306      nAts = mol.GetNumAtoms()
    307      for i in range(nAts):
    308        if ignoreHs and mol.GetAtomWithIdx(i).GetAtomicNum()==1: continue
    309        pos = conf.GetAtomPosition(i)*self.drawingOptions.coordScale
    310        xAccum += pos[0]
    311        yAccum += pos[1]
    312        minX = min(minX,pos[0])
    313        minY = min(minY,pos[1])
    314        maxX = max(maxX,pos[0])
    315        maxY = max(maxY,pos[1])
    316
    317      dx = abs(maxX-minX)
    318      dy = abs(maxY-minY)
    319      xSize = dx*self.currDotsPerAngstrom
    320      ySize = dy*self.currDotsPerAngstrom
    321
    322      if coordCenter:
    323        molTrans = -xAccum/nAts,-yAccum/nAts
    324      else:
    325        molTrans = -(minX+(maxX-minX)/2),-(minY+(maxY-minY)/2)
    326      self.molTrans = molTrans
    327
    328      if xSize>=.95*canvasSize[0]:
    329        scale = .9*canvasSize[0]/xSize
    330        xSize*=scale
    331        ySize*=scale
    332        self.currDotsPerAngstrom*=scale
    333        self.currAtomLabelFontSize = max(self.currAtomLabelFontSize*scale,
    334                                         self.drawingOptions.atomLabelMinFontSize)
    335      if ySize>=.95*canvasSize[1]:
    336        scale = .9*canvasSize[1]/ySize
    337        xSize*=scale
    338        ySize*=scale
    339        self.currDotsPerAngstrom*=scale
    340        self.currAtomLabelFontSize = max(self.currAtomLabelFontSize*scale,
    341                                         self.drawingOptions.atomLabelMinFontSize)
    342      drawingTrans = canvasSize[0]/2,canvasSize[1]/2
    343      self.drawingTrans = drawingTrans
    344
    345 -  def _drawLabel(self,label,pos,baseOffset,font,color=None,**kwargs):
    346      if color is None:
    347        color = self.drawingOptions.defaultColor
    348      x1 = pos[0]
    349      y1 = pos[1]
    350      labelP = x1,y1
    351      labelSize = self.canvas.addCanvasText(label,(x1,y1,baseOffset),font,color,**kwargs)
    352      return labelSize
    353
    354 -  def AddMol(self,mol,centerIt=True,molTrans=None,drawingTrans=None,
    355               highlightAtoms=[],confId=-1,flagCloseContactsDist=2,
    356               highlightMap=None, ignoreHs=False,highlightBonds=[],**kwargs):
    357      """Set the molecule to be drawn.
    358
    359      Parameters:
    360        hightlightAtoms -- list of atoms to highlight (default [])
    361        highlightMap -- dictionary of (atom, color) pairs (default None)
    362
    363      Notes:
    364        - specifying centerIt will cause molTrans and drawingTrans to be ignored
    365      """
    366      conf = mol.GetConformer(confId)
    367      if 'coordScale' in kwargs:
    368        self.drawingOptions.coordScale=kwargs['coordScale']
    369
    370      self.currDotsPerAngstrom=self.drawingOptions.dotsPerAngstrom
    371      self.currAtomLabelFontSize=self.drawingOptions.atomLabelFontSize
    372      if centerIt:
    373        self.scaleAndCenter(mol,conf,ignoreHs=ignoreHs)
    374      else:
    375        if molTrans is None:
    376          molTrans = (0,0)
    377        self.molTrans = molTrans
    378        if drawingTrans is None:
    379          drawingTrans = (0,0)
    380        self.drawingTrans = drawingTrans
    381
    382      font = Font(face=self.drawingOptions.atomLabelFontFace,size=self.currAtomLabelFontSize)
    383
    384      obds=None
    385      if not mol.HasProp('_drawingBondsWedged'):
    386        # this is going to modify the molecule, get ready to undo that
    387        obds=[x.GetBondDir() for x in mol.GetBonds()]
    388        Chem.WedgeMolBonds(mol,conf)
    389
    390      includeAtomNumbers = kwargs.get('includeAtomNumbers',self.drawingOptions.includeAtomNumbers)
    391      self.atomPs[mol] = {}
    392      self.boundingBoxes[mol] = [0]*4
    393      self.activeMol = mol
    394      self.bondRings = mol.GetRingInfo().BondRings()
    395      labelSizes = {}
    396      for atom in mol.GetAtoms():
    397        labelSizes[atom.GetIdx()] = None
    398        if ignoreHs and atom.GetAtomicNum()==1:
    399          drawAtom=False
    400        else:
    401          drawAtom=True
    402        idx = atom.GetIdx()
    403        pos = self.atomPs[mol].get(idx,None)
    404        if pos is None:
    405          pos = self.transformPoint(conf.GetAtomPosition(idx)*self.drawingOptions.coordScale)
    406          self.atomPs[mol][idx] = pos
    407          if drawAtom:
    408            self.boundingBoxes[mol][0]=min(self.boundingBoxes[mol][0],pos[0])
    409            self.boundingBoxes[mol][1]=min(self.boundingBoxes[mol][1],pos[1])
    410            self.boundingBoxes[mol][2]=max(self.boundingBoxes[mol][2],pos[0])
    411            self.boundingBoxes[mol][3]=max(self.boundingBoxes[mol][3],pos[1])
    412
    413        if not drawAtom: continue
    414        nbrSum = [0,0]
    415        for bond in atom.GetBonds():
    416          nbr = bond.GetOtherAtom(atom)
    417          if ignoreHs and nbr.GetAtomicNum()==1: continue
    418          nbrIdx = nbr.GetIdx()
    419          if nbrIdx > idx:
    420            nbrPos = self.atomPs[mol].get(nbrIdx,None)
    421            if nbrPos is None:
    422              nbrPos = self.transformPoint(conf.GetAtomPosition(nbrIdx)*self.drawingOptions.coordScale)
    423              self.atomPs[mol][nbrIdx] = nbrPos
    424              self.boundingBoxes[mol][0]=min(self.boundingBoxes[mol][0],nbrPos[0])
    425              self.boundingBoxes[mol][1]=min(self.boundingBoxes[mol][1],nbrPos[1])
    426              self.boundingBoxes[mol][2]=max(self.boundingBoxes[mol][2],nbrPos[0])
    427              self.boundingBoxes[mol][3]=max(self.boundingBoxes[mol][3],nbrPos[1])
    428
    429          else:
    430            nbrPos = self.atomPs[mol][nbrIdx]
    431          nbrSum[0] += nbrPos[0]-pos[0]
    432          nbrSum[1] += nbrPos[1]-pos[1]
    433
    434        iso = atom.GetIsotope()
    435
    436        labelIt= not self.drawingOptions.noCarbonSymbols or \
    437                 atom.GetAtomicNum()!=6 or \
    438                 atom.GetFormalCharge()!=0 or \
    439                 atom.GetNumRadicalElectrons() or \
    440                 includeAtomNumbers or \
    441                 iso or \
    442                 atom.HasProp('molAtomMapNumber') or \
    443                 atom.GetDegree()==0
    444        orient=''
    445        if labelIt:
    446          baseOffset = 0
    447          if includeAtomNumbers:
    448            symbol = str(atom.GetIdx())
    449            symbolLength = len(symbol)
    450          else:
    451            base = atom.GetSymbol()
    452            symbolLength = len(base)
    453            nHs = atom.GetTotalNumHs()
    454            if nHs>0:
    455              if nHs>1:
    456                hs='H<sub>%d</sub>'%nHs
    457                symbolLength += 1 + len(str(nHs))
    458              else:
    459                hs ='H'
    460                symbolLength += 1
    461            else:
    462              hs = ''
    463            chg = atom.GetFormalCharge()
    464            if chg!=0:
    465              if chg==1:
    466                chg = '+'
    467              elif chg==-1:
    468                chg = '-'
    469              elif chg>1:
    470                chg = '+%d'%chg
    471              elif chg<-1:
    472                chg = '-%d'%chg
    473              symbolLength += len(chg)
    474            else:
    475              chg = ''
    476            if chg:
    477              chg = '<sup>%s</sup>'%chg
    478
    479            if atom.GetNumRadicalElectrons():
    480              rad = self.drawingOptions.radicalSymbol*atom.GetNumRadicalElectrons()
    481              rad = '<sup>%s</sup>'%rad
    482              symbolLength += atom.GetNumRadicalElectrons()
    483            else:
    484              rad = ''
    485
    486            isotope=''
    487            isotopeLength = 0
    488            if iso:
    489              isotope='<sup>%d</sup>'%atom.GetIsotope()
    490              isotopeLength = len(str(atom.GetIsotope()))
    491              symbolLength += isotopeLength
    492            mapNum=''
    493            mapNumLength = 0
    494            if atom.HasProp('molAtomMapNumber'):
    495              mapNum=':'+atom.GetProp('molAtomMapNumber')
    496              mapNumLength = 1 + len(str(atom.GetProp('molAtomMapNumber')))
    497              symbolLength += mapNumLength
    498            deg = atom.GetDegree()
    499            # This should be done in a better way in the future:
    500            # 'baseOffset' should be determined by getting the size of 'isotope' and the size of 'base', or the size of 'mapNum' and the size of 'base'
    501            # (depending on 'deg' and 'nbrSum[0]') in order to determine the exact position of the base
    502            if deg==0:
    503              if periodicTable.GetElementSymbol(atom.GetAtomicNum()) in ('O','S','Se','Te','F','Cl','Br','I','At'):
    504                symbol = '%s%s%s%s%s%s'%(hs,isotope,base,chg,rad,mapNum)
    505              else:
    506                symbol = '%s%s%s%s%s%s'%(isotope,base,hs,chg,rad,mapNum)
    507            elif deg>1 or nbrSum[0]<1:
    508              symbol = '%s%s%s%s%s%s'%(isotope,base,hs,chg,rad,mapNum)
    509              baseOffset = 0.5 - (isotopeLength + len(base) / 2.) / symbolLength
    510            else:
    511              symbol = '%s%s%s%s%s%s'%(rad,chg,hs,isotope,base,mapNum)
    512              baseOffset = -0.5 + (mapNumLength + len(base) / 2.) / symbolLength
    513            if deg==1:
    514              if abs(nbrSum[1])>1:
    515                islope=nbrSum[0]/abs(nbrSum[1])
    516              else:
    517                islope=nbrSum[0]
    518              if abs(islope)>.3:
    519                if islope>0:
    520                  orient='W'
    521                else:
    522                  orient='E'
    523              elif abs(nbrSum[1])>10:
    524                if nbrSum[1]>0:
    525                  orient='N'
    526                else :
    527                  orient='S'
    528            else:
    529              orient = 'C'
    530          if highlightMap and idx in highlightMap:
    531            color = highlightMap[idx]
    532          elif highlightAtoms and idx in highlightAtoms:
    533            color = self.drawingOptions.selectColor
    534          else:
    535            color = self.drawingOptions.elemDict.get(atom.GetAtomicNum(),(0,0,0))
    536          labelSize = self._drawLabel(symbol, pos, baseOffset, font, color=color,orientation=orient)
    537          labelSizes[atom.GetIdx()] = [labelSize, orient]
    538
    539      for bond in mol.GetBonds():
    540            atom, idx = bond.GetBeginAtom(), bond.GetBeginAtomIdx()
    541            nbr, nbrIdx = bond.GetEndAtom(), bond.GetEndAtomIdx()
    542            pos = self.atomPs[mol].get(idx,None)
    543            nbrPos = self.atomPs[mol].get(nbrIdx,None)
    544            if highlightBonds and bond.GetIdx() in highlightBonds:
    545              width=2.0*self.drawingOptions.bondLineWidth
    546              color = self.drawingOptions.selectColor
    547              color2 = self.drawingOptions.selectColor
    548            elif highlightAtoms and idx in highlightAtoms and nbrIdx in highlightAtoms:
    549              width=2.0*self.drawingOptions.bondLineWidth
    550              color = self.drawingOptions.selectColor
    551              color2 = self.drawingOptions.selectColor
    552            elif highlightMap is not None and idx in highlightMap and nbrIdx in highlightMap:
    553              width=2.0*self.drawingOptions.bondLineWidth
    554              color = highlightMap[idx]
    555              color2 = highlightMap[nbrIdx]
    556            else:
    557              width=self.drawingOptions.bondLineWidth
    558              if self.drawingOptions.colorBonds:
    559                color = self.drawingOptions.elemDict.get(atom.GetAtomicNum(),(0,0,0))
    560                color2 = self.drawingOptions.elemDict.get(nbr.GetAtomicNum(),(0,0,0))
    561              else:
    562                color = self.drawingOptions.defaultColor
    563                color2= color
    564            self._drawBond(bond,atom,nbr,pos,nbrPos,conf,
    565                             color=color,width=width,color2=color2,labelSize1=labelSizes[idx],labelSize2=labelSizes[nbrIdx])
    566
    567      # if we modified the bond wedging state, undo those changes now
    568      if obds:
    569        for i,d in enumerate(obds):
    570          mol.GetBondWithIdx(i).SetBondDir(d)
    571
    572
    573      if flagCloseContactsDist>0:
    574        tol = flagCloseContactsDist*flagCloseContactsDist
    575        for i,atomi in enumerate(mol.GetAtoms()):
    576          pi = numpy.array(self.atomPs[mol][i])
    577          for j in range(i+1,mol.GetNumAtoms()):
    578            pj = numpy.array(self.atomPs[mol][j])
    579            d = pj-pi
    580            dist2 = d[0]*d[0]+d[1]*d[1]
    581            if dist2<=tol:
    582              self.canvas.addCanvasPolygon(((pi[0]-2*flagCloseContactsDist,
    583                                        pi[1]-2*flagCloseContactsDist),
    584                                       (pi[0]+2*flagCloseContactsDist,
    585                                        pi[1]-2*flagCloseContactsDist),
    586                                       (pi[0]+2*flagCloseContactsDist,
    587                                        pi[1]+2*flagCloseContactsDist),
    588                                       (pi[0]-2*flagCloseContactsDist,
    589                                        pi[1]+2*flagCloseContactsDist)),
    590                               color=(1.,0,0),
    591                               fill=False,stroke=True)
    592