text: # $Id$
  #
  #  Copyright (C) 2008-2011 Greg Landrum
  #
  #   @@ All Rights Reserved @@
  #  This file is part of the RDKit.
  #  The contents are covered by the terms of the BSD license
  #  which is included in the file license.txt, found at the root
  #  of the RDKit source tree.
  #
  from rdkit import Chem
  import numpy
  import math
  import copy
  from rdkit.six import cmp
  import functools
  
  periodicTable = Chem.GetPeriodicTable()
  
  
  class Font(object):
  
    def __init__(self, face=None, size=None, name=None, weight=None):
      self.face = face or 'sans'
      self.size = size or '12'
      self.weight = weight or 'normal'
      self.name = name
  
  
  class DrawingOptions(object):
    dotsPerAngstrom = 30
    useFraction = 0.85
  
    atomLabelFontFace = "sans"
    atomLabelFontSize = 12
    atomLabelMinFontSize = 7
    atomLabelDeuteriumTritium = False
  
    bondLineWidth = 1.2
    dblBondOffset = .25
    dblBondLengthFrac = .8
  
    defaultColor = (1, 0, 0)
    selectColor = (1, 0, 0)
    bgColor = (1, 1, 1)
  
    colorBonds = True
    noCarbonSymbols = True
    includeAtomNumbers = False
    atomNumberOffset = 0
    radicalSymbol = u'\u2219'
  
    dash = (4, 4)
  
    wedgeDashedBonds = True
    showUnknownDoubleBonds = True
  
    # used to adjust overall scaling for molecules that have been laid out with non-standard
    # bond lengths
    coordScale = 1.0
  
    elemDict = {
      1: (0.55, 0.55, 0.55),
      7: (0, 0, 1),
      8: (1, 0, 0),
      9: (.2, .8, .8),
      15: (1, .5, 0),
      16: (.8, .8, 0),
      17: (0, .8, 0),
      35: (.5, .3, .1),
      53: (.63, .12, .94),
      0: (.5, .5, .5),
    }
  
  
  class MolDrawing(object):
  
    def __init__(self, canvas=None, drawingOptions=None):
      self.canvas = canvas
      self.canvasSize = None
      if canvas:
        self.canvasSize = canvas.size
      self.drawingOptions = drawingOptions or DrawingOptions()
  
      self.atomPs = {}
      self.boundingBoxes = {}
  
      if self.drawingOptions.bgColor is not None:
        self.canvas.addCanvasPolygon(((0, 0), (canvas.size[0], 0), (canvas.size[0], canvas.size[1]),
                                      (0, canvas.size[1])), color=self.drawingOptions.bgColor,
                                     fill=True, stroke=False)
  
    def transformPoint(self, pos):
      res = [0, 0]
      res[0] = (pos[0] + self.molTrans[0]
                ) * self.currDotsPerAngstrom * self.drawingOptions.useFraction + self.drawingTrans[0]
      res[1] = self.canvasSize[1] - ((pos[1] + self.molTrans[1]) * self.currDotsPerAngstrom *
                                     self.drawingOptions.useFraction + self.drawingTrans[1])
      return res
  
    def _getBondOffset(self, p1, p2):
      # get the vector between the points:
      dx = p2[0] - p1[0]
      dy = p2[1] - p1[1]
  
      # figure out the angle and the perpendicular:
      ang = math.atan2(dy, dx)
      perp = ang + math.pi / 2.
  
      # here's the offset for the parallel bond:
      offsetX = math.cos(perp) * self.drawingOptions.dblBondOffset * self.currDotsPerAngstrom
      offsetY = math.sin(perp) * self.drawingOptions.dblBondOffset * self.currDotsPerAngstrom
  
      return perp, offsetX, offsetY
  
    def _getOffsetBondPts(self, p1, p2, offsetX, offsetY, lenFrac=None):
      lenFrac = lenFrac or self.drawingOptions.dblBondLengthFrac
  
      dx = p2[0] - p1[0]
      dy = p2[1] - p1[1]
      # ----
      # now figure out where to start and end it:
  
      # offset the start point:
      fracP1 = p1[0] + offsetX, p1[1] + offsetY
  
      # now move a portion of the way along the line to the neighbor:
      frac = (1. - lenFrac) / 2
      fracP1 = fracP1[0] + dx * frac, fracP1[1] + dy * frac
      fracP2 = fracP1[0] + dx * lenFrac, fracP1[1] + dy * lenFrac
      return fracP1, fracP2
  
    def _offsetDblBond(self, p1, p2, bond, a1, a2, conf, direction=1, lenFrac=None):
      perp, offsetX, offsetY = self._getBondOffset(p1, p2)
      offsetX = offsetX * direction
      offsetY = offsetY * direction
  
      # if we're a ring bond, we may need to flip over to the other side:
      if bond.IsInRing():
        bondIdx = bond.GetIdx()
        a2Idx = a2.GetIdx()
        # find a ring bond from a1 to an atom other than a2:
        for otherBond in a1.GetBonds():
          if otherBond.GetIdx() != bondIdx and otherBond.IsInRing():
            sharedRing = False
            for ring in self.bondRings:
              if bondIdx in ring and otherBond.GetIdx() in ring:
                sharedRing = True
                break
            if not sharedRing:
              continue
            a3 = otherBond.GetOtherAtom(a1)
            if a3.GetIdx() != a2Idx:
              p3 = self.transformPoint(
                conf.GetAtomPosition(a3.GetIdx()) * self.drawingOptions.coordScale)
              dx2 = p3[0] - p1[0]
              dy2 = p3[1] - p1[1]
              dotP = dx2 * offsetX + dy2 * offsetY
              if dotP < 0:
                perp += math.pi
                offsetX = math.cos(
                  perp) * self.drawingOptions.dblBondOffset * self.currDotsPerAngstrom
                offsetY = math.sin(
                  perp) * self.drawingOptions.dblBondOffset * self.currDotsPerAngstrom
  
      fracP1, fracP2 = self._getOffsetBondPts(p1, p2, offsetX, offsetY, lenFrac=lenFrac)
      return fracP1, fracP2
  
    def _getBondAttachmentCoordinates(self, p1, p2, labelSize):
      newpos = [None, None]
      if labelSize is not None:
        labelSizeOffset = [labelSize[0][0] / 2 + (cmp(p2[0], p1[0]) * labelSize[0][2]),
                           labelSize[0][1] / 2]
        if p1[1] == p2[1]:
          newpos[0] = p1[0] + cmp(p2[0], p1[0]) * labelSizeOffset[0]
        else:
          if abs(labelSizeOffset[1] * (p2[0] - p1[0]) / (p2[1] - p1[1])) < labelSizeOffset[0]:
            newpos[0] = p1[0] + cmp(p2[0], p1[0]) * abs(labelSizeOffset[1] * (p2[0] - p1[0]) /
                                                        (p2[1] - p1[1]))
          else:
            newpos[0] = p1[0] + cmp(p2[0], p1[0]) * labelSizeOffset[0]
        if p1[0] == p2[0]:
          newpos[1] = p1[1] + cmp(p2[1], p1[1]) * labelSizeOffset[1]
        else:
          if abs(labelSizeOffset[0] * (p1[1] - p2[1]) / (p2[0] - p1[0])) < labelSizeOffset[1]:
            newpos[1] = p1[1] + cmp(p2[1], p1[1]) * abs(labelSizeOffset[0] * (p1[1] - p2[1]) /
                                                        (p2[0] - p1[0]))
          else:
            newpos[1] = p1[1] + cmp(p2[1], p1[1]) * labelSizeOffset[1]
      else:
        newpos = copy.deepcopy(p1)
      return newpos
  
    def _drawWedgedBond(self, bond, pos, nbrPos, width=None, color=None, dash=None):
      width = width or self.drawingOptions.bondLineWidth
      color = color or self.drawingOptions.defaultColor
      _, offsetX, offsetY = self._getBondOffset(pos, nbrPos)
      offsetX *= .75
      offsetY *= .75
      poly = ((pos[0], pos[1]), (nbrPos[0] + offsetX, nbrPos[1] + offsetY),
              (nbrPos[0] - offsetX, nbrPos[1] - offsetY))
      # canvas.drawPolygon(poly,edgeColor=color,edgeWidth=1,fillColor=color,closed=1)
      if not dash:
        self.canvas.addCanvasPolygon(poly, color=color)
      elif self.drawingOptions.wedgeDashedBonds and self.canvas.addCanvasDashedWedge:
        self.canvas.addCanvasDashedWedge(poly[0], poly[1], poly[2], color=color)
      else:
        self.canvas.addCanvasLine(pos, nbrPos, linewidth=width * 2, color=color, dashes=dash)
  
    def _drawBond(self, bond, atom, nbr, pos, nbrPos, conf, width=None, color=None, color2=None,
                  labelSize1=None, labelSize2=None):
      width = width or self.drawingOptions.bondLineWidth
      color = color or self.drawingOptions.defaultColor
      color2 = color2 or self.drawingOptions.defaultColor
      p1_raw = copy.deepcopy(pos)
      p2_raw = copy.deepcopy(nbrPos)
      newpos = self._getBondAttachmentCoordinates(p1_raw, p2_raw, labelSize1)
      newnbrPos = self._getBondAttachmentCoordinates(p2_raw, p1_raw, labelSize2)
      addDefaultLine = functools.partial(self.canvas.addCanvasLine, linewidth=width, color=color,
                                         color2=color2)
      bType = bond.GetBondType()
      if bType == Chem.BondType.SINGLE:
        bDir = bond.GetBondDir()
        if bDir in (Chem.BondDir.BEGINWEDGE, Chem.BondDir.BEGINDASH):
          # if the bond is "backwards", change the drawing direction:
          if bond.GetBeginAtom().GetChiralTag() in (Chem.ChiralType.CHI_TETRAHEDRAL_CW,
                                                    Chem.ChiralType.CHI_TETRAHEDRAL_CCW):
            p1, p2 = newpos, newnbrPos
            wcolor = color
          else:
            p2, p1 = newpos, newnbrPos
            wcolor = color2
          if bDir == Chem.BondDir.BEGINWEDGE:
            self._drawWedgedBond(bond, p1, p2, color=wcolor, width=width)
          elif bDir == Chem.BondDir.BEGINDASH:
            self._drawWedgedBond(bond, p1, p2, color=wcolor, width=width,
                                 dash=self.drawingOptions.dash)
        else:
          addDefaultLine(newpos, newnbrPos)
      elif bType == Chem.BondType.DOUBLE:
        crossBond = (self.drawingOptions.showUnknownDoubleBonds and
                     bond.GetStereo() == Chem.BondStereo.STEREOANY)
        if (not crossBond and (bond.IsInRing() or
                               (atom.GetDegree() != 1 and bond.GetOtherAtom(atom).GetDegree() != 1))):
          addDefaultLine(newpos, newnbrPos)
          fp1, fp2 = self._offsetDblBond(newpos, newnbrPos, bond, atom, nbr, conf)
          addDefaultLine(fp1, fp2)
        else:
          fp1, fp2 = self._offsetDblBond(newpos, newnbrPos, bond, atom, nbr, conf, direction=.5,
                                         lenFrac=1.0)
          fp3, fp4 = self._offsetDblBond(newpos, newnbrPos, bond, atom, nbr, conf, direction=-.5,
                                         lenFrac=1.0)
          if crossBond:
            fp2, fp4 = fp4, fp2
          addDefaultLine(fp1, fp2)
          addDefaultLine(fp3, fp4)
  
      elif bType == Chem.BondType.AROMATIC:
        addDefaultLine(newpos, newnbrPos)
        fp1, fp2 = self._offsetDblBond(newpos, newnbrPos, bond, atom, nbr, conf)
        addDefaultLine(fp1, fp2, dash=self.drawingOptions.dash)
      elif bType == Chem.BondType.TRIPLE:
        addDefaultLine(newpos, newnbrPos)
        fp1, fp2 = self._offsetDblBond(newpos, newnbrPos, bond, atom, nbr, conf)
        addDefaultLine(fp1, fp2)
        fp1, fp2 = self._offsetDblBond(newpos, newnbrPos, bond, atom, nbr, conf, direction=-1)
        addDefaultLine(fp1, fp2)
      else:
        addDefaultLine(newpos, newnbrPos, dash=(1, 2))
  
    def scaleAndCenter(self, mol, conf, coordCenter=False, canvasSize=None, ignoreHs=False):
      canvasSize = canvasSize or self.canvasSize
      xAccum = 0
      yAccum = 0
      minX = 1e8
      minY = 1e8
      maxX = -1e8
      maxY = -1e8
  
      nAts = mol.GetNumAtoms()
      for i in range(nAts):
        if ignoreHs and mol.GetAtomWithIdx(i).GetAtomicNum() == 1:
          continue
        pos = conf.GetAtomPosition(i) * self.drawingOptions.coordScale
        xAccum += pos[0]
        yAccum += pos[1]
        minX = min(minX, pos[0])
        minY = min(minY, pos[1])
        maxX = max(maxX, pos[0])
        maxY = max(maxY, pos[1])
  
      dx = abs(maxX - minX)
      dy = abs(maxY - minY)
      xSize = dx * self.currDotsPerAngstrom
      ySize = dy * self.currDotsPerAngstrom
  
      if coordCenter:
        molTrans = -xAccum / nAts, -yAccum / nAts
      else:
        molTrans = -(minX + (maxX - minX) / 2), -(minY + (maxY - minY) / 2)
      self.molTrans = molTrans
  
      if xSize >= .95 * canvasSize[0]:
        scale = .9 * canvasSize[0] / xSize
        xSize *= scale
        ySize *= scale
        self.currDotsPerAngstrom *= scale
        self.currAtomLabelFontSize = max(self.currAtomLabelFontSize * scale,
                                         self.drawingOptions.atomLabelMinFontSize)
      if ySize >= .95 * canvasSize[1]:
        scale = .9 * canvasSize[1] / ySize
        xSize *= scale
        ySize *= scale
        self.currDotsPerAngstrom *= scale
        self.currAtomLabelFontSize = max(self.currAtomLabelFontSize * scale,
                                         self.drawingOptions.atomLabelMinFontSize)
      drawingTrans = canvasSize[0] / 2, canvasSize[1] / 2
      self.drawingTrans = drawingTrans
  
    def _drawLabel(self, label, pos, baseOffset, font, color=None, **kwargs):
      color = color or self.drawingOptions.defaultColor
      x1 = pos[0]
      y1 = pos[1]
      labelSize = self.canvas.addCanvasText(label, (x1, y1, baseOffset), font, color, **kwargs)
      return labelSize
  
    def AddMol(self, mol, centerIt=True, molTrans=None, drawingTrans=None, highlightAtoms=[],
               confId=-1, flagCloseContactsDist=2, highlightMap=None, ignoreHs=False,
               highlightBonds=[], **kwargs):
      """Set the molecule to be drawn.
  
      Parameters:
        hightlightAtoms -- list of atoms to highlight (default [])
        highlightMap -- dictionary of (atom, color) pairs (default None)
  
      Notes:
        - specifying centerIt will cause molTrans and drawingTrans to be ignored
      """
      conf = mol.GetConformer(confId)
      if 'coordScale' in kwargs:
        self.drawingOptions.coordScale = kwargs['coordScale']
  
      self.currDotsPerAngstrom = self.drawingOptions.dotsPerAngstrom
      self.currAtomLabelFontSize = self.drawingOptions.atomLabelFontSize
      if centerIt:
        self.scaleAndCenter(mol, conf, ignoreHs=ignoreHs)
      else:
        self.molTrans = molTrans or (0, 0)
        self.drawingTrans = drawingTrans or (0, 0)
  
      font = Font(face=self.drawingOptions.atomLabelFontFace, size=self.currAtomLabelFontSize)
  
      obds = None
      if not mol.HasProp('_drawingBondsWedged'):
        # this is going to modify the molecule, get ready to undo that
        obds = [x.GetBondDir() for x in mol.GetBonds()]
        Chem.WedgeMolBonds(mol, conf)
  
      includeAtomNumbers = kwargs.get('includeAtomNumbers', self.drawingOptions.includeAtomNumbers)
      self.atomPs[mol] = {}
      self.boundingBoxes[mol] = [0] * 4
      self.activeMol = mol
      self.bondRings = mol.GetRingInfo().BondRings()
      labelSizes = {}
      for atom in mol.GetAtoms():
        labelSizes[atom.GetIdx()] = None
        if ignoreHs and atom.GetAtomicNum() == 1:
          drawAtom = False
        else:
          drawAtom = True
        idx = atom.GetIdx()
        pos = self.atomPs[mol].get(idx, None)
        if pos is None:
          pos = self.transformPoint(conf.GetAtomPosition(idx) * self.drawingOptions.coordScale)
          self.atomPs[mol][idx] = pos
          if drawAtom:
            self.boundingBoxes[mol][0] = min(self.boundingBoxes[mol][0], pos[0])
            self.boundingBoxes[mol][1] = min(self.boundingBoxes[mol][1], pos[1])
            self.boundingBoxes[mol][2] = max(self.boundingBoxes[mol][2], pos[0])
            self.boundingBoxes[mol][3] = max(self.boundingBoxes[mol][3], pos[1])
  
        if not drawAtom:
          continue
        nbrSum = [0, 0]
        for bond in atom.GetBonds():
          nbr = bond.GetOtherAtom(atom)
          if ignoreHs and nbr.GetAtomicNum() == 1:
            continue
          nbrIdx = nbr.GetIdx()
          if nbrIdx > idx:
            nbrPos = self.atomPs[mol].get(nbrIdx, None)
            if nbrPos is None:
              nbrPos = self.transformPoint(
                conf.GetAtomPosition(nbrIdx) * self.drawingOptions.coordScale)
              self.atomPs[mol][nbrIdx] = nbrPos
              self.boundingBoxes[mol][0] = min(self.boundingBoxes[mol][0], nbrPos[0])
              self.boundingBoxes[mol][1] = min(self.boundingBoxes[mol][1], nbrPos[1])
              self.boundingBoxes[mol][2] = max(self.boundingBoxes[mol][2], nbrPos[0])
              self.boundingBoxes[mol][3] = max(self.boundingBoxes[mol][3], nbrPos[1])
  
          else:
            nbrPos = self.atomPs[mol][nbrIdx]
          nbrSum[0] += nbrPos[0] - pos[0]
          nbrSum[1] += nbrPos[1] - pos[1]
  
        iso = atom.GetIsotope()
        labelIt = (not self.drawingOptions.noCarbonSymbols or iso or atom.GetAtomicNum() != 6 or
                   atom.GetFormalCharge() != 0 or atom.GetNumRadicalElectrons() or
                   includeAtomNumbers or atom.HasProp('molAtomMapNumber') or atom.GetDegree() == 0)
        orient = ''
        if labelIt:
          baseOffset = 0
          if includeAtomNumbers:
            symbol = str(atom.GetIdx())
            symbolLength = len(symbol)
          else:
            base = atom.GetSymbol()
            if (base == 'H' and (iso == 2 or iso == 3) and
                self.drawingOptions.atomLabelDeuteriumTritium):
              if iso == 2:
                base = 'D'
              else:
                base = 'T'
              iso = 0
            symbolLength = len(base)
            if not atom.HasQuery():
              nHs = atom.GetTotalNumHs()
            else:
              nHs = 0
            if nHs > 0:
              if nHs > 1:
                hs = 'H<sub>%d</sub>' % nHs
                symbolLength += 1 + len(str(nHs))
              else:
                hs = 'H'
                symbolLength += 1
            else:
              hs = ''
            chg = atom.GetFormalCharge()
            if chg == 0:
              chg = ''
            elif chg == 1:
              chg = '+'
            elif chg == -1:
              chg = '-'
            else:
              chg = '%+d' % chg
            symbolLength += len(chg)
            if chg:
              chg = '<sup>%s</sup>' % chg
  
            if atom.GetNumRadicalElectrons():
              rad = self.drawingOptions.radicalSymbol * atom.GetNumRadicalElectrons()
              rad = '<sup>%s</sup>' % rad
              symbolLength += atom.GetNumRadicalElectrons()
            else:
              rad = ''
  
            isotope = ''
            isotopeLength = 0
            if iso:
              isotope = '<sup>%d</sup>' % atom.GetIsotope()
              isotopeLength = len(str(atom.GetIsotope()))
              symbolLength += isotopeLength
            mapNum = ''
            mapNumLength = 0
            if atom.HasProp('molAtomMapNumber'):
              mapNum = ':' + atom.GetProp('molAtomMapNumber')
              mapNumLength = 1 + len(str(atom.GetProp('molAtomMapNumber')))
              symbolLength += mapNumLength
            deg = atom.GetDegree()
            # This should be done in a better way in the future:
            # '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'
            # (depending on 'deg' and 'nbrSum[0]') in order to determine the exact
            # position of the base
            if deg == 0:
              if periodicTable.GetElementSymbol(atom.GetAtomicNum()) in ('O', 'S', 'Se', 'Te', 'F',
                                                                         'Cl', 'Br', 'I', 'At'):
                symbol = '%s%s%s%s%s%s' % (hs, isotope, base, chg, rad, mapNum)
              else:
                symbol = '%s%s%s%s%s%s' % (isotope, base, hs, chg, rad, mapNum)
            elif deg > 1 or nbrSum[0] < 1:
              symbol = '%s%s%s%s%s%s' % (isotope, base, hs, chg, rad, mapNum)
              baseOffset = 0.5 - (isotopeLength + len(base) / 2.) / symbolLength
            else:
              symbol = '%s%s%s%s%s%s' % (rad, chg, hs, isotope, base, mapNum)
              baseOffset = -0.5 + (mapNumLength + len(base) / 2.) / symbolLength
            if deg == 1:
              if abs(nbrSum[1]) > 1:
                islope = nbrSum[0] / abs(nbrSum[1])
              else:
                islope = nbrSum[0]
              if abs(islope) > .3:
                if islope > 0:
                  orient = 'W'
                else:
                  orient = 'E'
              elif abs(nbrSum[1]) > 10:
                if nbrSum[1] > 0:
                  orient = 'N'
                else:
                  orient = 'S'
            else:
              orient = 'C'
          if highlightMap and idx in highlightMap:
            color = highlightMap[idx]
          elif highlightAtoms and idx in highlightAtoms:
            color = self.drawingOptions.selectColor
          else:
            color = self.drawingOptions.elemDict.get(atom.GetAtomicNum(), (0, 0, 0))
          labelSize = self._drawLabel(symbol, pos, baseOffset, font, color=color, orientation=orient)
          labelSizes[atom.GetIdx()] = [labelSize, orient]
  
      for bond in mol.GetBonds():
        atom, idx = bond.GetBeginAtom(), bond.GetBeginAtomIdx()
        nbr, nbrIdx = bond.GetEndAtom(), bond.GetEndAtomIdx()
        pos = self.atomPs[mol].get(idx, None)
        nbrPos = self.atomPs[mol].get(nbrIdx, None)
        if highlightBonds and bond.GetIdx() in highlightBonds:
          width = 2.0 * self.drawingOptions.bondLineWidth
          color = self.drawingOptions.selectColor
          color2 = self.drawingOptions.selectColor
        elif highlightAtoms and idx in highlightAtoms and nbrIdx in highlightAtoms:
          width = 2.0 * self.drawingOptions.bondLineWidth
          color = self.drawingOptions.selectColor
          color2 = self.drawingOptions.selectColor
        elif highlightMap is not None and idx in highlightMap and nbrIdx in highlightMap:
          width = 2.0 * self.drawingOptions.bondLineWidth
          color = highlightMap[idx]
          color2 = highlightMap[nbrIdx]
        else:
          width = self.drawingOptions.bondLineWidth
          if self.drawingOptions.colorBonds:
            color = self.drawingOptions.elemDict.get(atom.GetAtomicNum(), (0, 0, 0))
            color2 = self.drawingOptions.elemDict.get(nbr.GetAtomicNum(), (0, 0, 0))
          else:
            color = self.drawingOptions.defaultColor
            color2 = color
        self._drawBond(bond, atom, nbr, pos, nbrPos, conf, color=color, width=width, color2=color2,
                       labelSize1=labelSizes[idx], labelSize2=labelSizes[nbrIdx])
  
    # if we modified the bond wedging state, undo those changes now
      if obds:
        for i, d in enumerate(obds):
          mol.GetBondWithIdx(i).SetBondDir(d)
  
      if flagCloseContactsDist > 0:
        tol = flagCloseContactsDist * flagCloseContactsDist
        for i, _ in enumerate(mol.GetAtoms()):
          pi = numpy.array(self.atomPs[mol][i])
          for j in range(i + 1, mol.GetNumAtoms()):
            pj = numpy.array(self.atomPs[mol][j])
            d = pj - pi
            dist2 = d[0] * d[0] + d[1] * d[1]
            if dist2 <= tol:
              self.canvas.addCanvasPolygon(
                ((pi[0] - 2 * flagCloseContactsDist, pi[1] - 2 * flagCloseContactsDist),
                 (pi[0] + 2 * flagCloseContactsDist, pi[1] - 2 * flagCloseContactsDist),
                 (pi[0] + 2 * flagCloseContactsDist, pi[1] + 2 * flagCloseContactsDist),
                 (pi[0] - 2 * flagCloseContactsDist, pi[1] + 2 * flagCloseContactsDist)),
                color=(1., 0, 0), fill=False, stroke=True)
  