Beispiel #1
0
 def get_angle_at_side( v, d, d2, relation, attach_angle):
   if attach_angle == 180:
     # shortcut
     return attach_angle
   side = geometry.on_which_side_is_point( (d.x,d.y,v.x,v.y), (d2.x,d2.y))
   an = angle + deg_to_rad( attach_angle)
   x = v.x + self.bond_length*cos( an)
   y = v.y + self.bond_length*sin( an)
   if relation*side == geometry.on_which_side_is_point( (d.x,d.y,v.x,v.y), (x,y)):
     return attach_angle
   else:
     return -attach_angle
Beispiel #2
0
    def _draw_edge(self, e):
        v1, v2 = e.vertices
        start = self.transformer.transform_xy(v1.x, v1.y)
        end = self.transformer.transform_xy(v2.x, v2.y)
        parent = self._create_parent(e, self.top)

        if e.order == 1:
            self._draw_line(parent, start, end, line_width=self.line_width)

        if e.order == 2:
            side = 0
            # find how to center the bonds
            # rings have higher priority in setting the positioning
            for ring in self.molecule.get_smallest_independent_cycles():
                if v1 in ring and v2 in ring:
                    side += reduce(operator.add, [
                        geometry.on_which_side_is_point(
                            start + end,
                            (self.transformer.transform_xy(a.x, a.y)))
                        for a in ring if a != v1 and a != v2
                    ])
            # if rings did not decide, use the other neigbors
            if not side:
                for v in v1.neighbors + v2.neighbors:
                    if v != v1 and v != v2:
                        side += geometry.on_which_side_is_point(
                            start + end,
                            (self.transformer.transform_xy(v.x, v.y)))
            if side:
                self._draw_line(parent, start, end, line_width=self.line_width)
                x1, y1, x2, y2 = geometry.find_parallel(
                    start[0], start[1], end[0], end[1],
                    self.bond_width * misc.signum(side))
                self._draw_line(parent, (x1, y1), (x2, y2),
                                line_width=self.line_width)
            else:
                for i in (1, -1):
                    x1, y1, x2, y2 = geometry.find_parallel(
                        start[0], start[1], end[0], end[1],
                        i * self.bond_width * 0.5)
                    self._draw_line(parent, (x1, y1), (x2, y2),
                                    line_width=self.line_width)

        elif e.order == 3:
            self._draw_line(parent, start, end, line_width=self.line_width)
            for i in (1, -1):
                x1, y1, x2, y2 = geometry.find_parallel(
                    start[0], start[1], end[0], end[1],
                    i * self.bond_width * 0.7)
                self._draw_line(parent, (x1, y1), (x2, y2),
                                line_width=self.line_width)
Beispiel #3
0
 def _process_multi_anelated_ring( self, ring, angle_shift=0):
   out = []
   to_go = [v for v in ring if v.x == None or v.y == None]
   if not to_go:
     # it was all already done
     return []
   back = [v for v in ring if v.x != None and v.y != None]
   sorted_back = self.mol.sort_vertices_in_path( back)
   if not sorted_back:
     # the already set atoms are not in one path - we have to process it "per partes"
     # it should not happen with the construction method we use
     raise( "i am not able to handle this, it should normaly not happen. please send me the input.")
   else:
     v1 = sorted_back[0]
     v2 = sorted_back[-1]
     v3 = sorted_back[1]
     to_go = self.mol.sort_vertices_in_path( to_go)
     if v1 not in to_go[0].get_neighbors():
       v1, v2 = v2, v1
     blocked_angle = sum_of_ring_internal_angles( len( back))
     overall_angle = sum_of_ring_internal_angles( len( ring))
     da = optimal_ring_iternal_angle( len( ring))  # internal angle
     # if there are 2 rings of same size inside each other, we need to use the angle_shift
     if angle_shift:
       da += 2*angle_shift/(len( to_go))
     ca = deg_to_rad( 180-(overall_angle - blocked_angle - len( to_go) * da + angle_shift)/2)  # connection angle
     side = sum( [geometry.on_which_side_is_point( (v1.x,v1.y,v2.x,v2.y),(v.x,v.y)) for v in back if v != v1 and v != v2])
     # we need to make sure that the ring is drawn on the right side
     if side > 0:
       ca = -ca
     ca += geometry.clockwise_angle_from_east( v1.x-v2.x, v1.y-v2.y)
     da = 180-da  # for drawing we use external angle
     # we must ensure that the ring will progress towards the second end
     if geometry.on_which_side_is_point( (v1.x,v1.y,v3.x,v3.y),(v2.x,v2.y)) < 0:
       da = -da
     # dry run to see where we get
     gcoords = gen_angle_stream( deg_to_rad( da), start_from= ca)
     x, y = v1.x, v1.y
     for i in range( len( to_go) +1):
       a = gcoords.next()
       x += self.bond_length*cos( a)
       y += self.bond_length*sin( a)
     # end of dry run, we can scale the bond_length now
     length = geometry.line_length( (v1.x,v1.y,v2.x,v2.y))
     real_length = geometry.line_length( (v1.x,v1.y,x,y))
     bl = self.bond_length * length / real_length
     gcoords = gen_angle_stream( deg_to_rad( da), start_from= ca)
     # and here we go
     self.apply_gen_to_atoms( gcoords, to_go, v1, bond_length=bl)
     out += to_go
   return out
Beispiel #4
0
 def _process_simply_anelated_ring( self, ring, base):
   out = []
   inter = [v for v in ring if v.x != None and v.y != None]
   if len( inter) == 1:
     # rings are connected via one atom
     v = inter.pop() # the atom of concatenation
     ring = self.mol.sort_vertices_in_path( ring, start_from=v)
     base_neighs = [a for a in v.get_neighbors() if a in base]
     if len( base_neighs) < 2:
       raise "this should not happen"
     d1 = base_neighs[0] 
     d2 = base_neighs[1]
     ca1 = geometry.clockwise_angle_from_east( v.x-d1.x, v.y-d1.y)
     ca2 = geometry.clockwise_angle_from_east( v.x-d2.x, v.y-d2.y)
     ca = (ca1+ca2)/2
     if abs( ca1-ca2) < pi:
       ca += -pi/2
     else:
       ca += pi/2
     size = len( ring)
     da = deg_to_rad(180 -180.0*(size-2)/size)
     gcoords = gen_angle_stream( da, start_from=ca + da/2)
     ring.remove( v)
     # here we generate the coords
     self.apply_gen_to_atoms( gcoords, ring, v)
     ring.append( v)
     out += ring
   elif len( inter) == 2:
     # there are two atoms common to the rings
     v1, v2 = inter # the atoms of concatenation
     ring = self.mol.sort_vertices_in_path( ring, start_from=v1)
     ring.remove( v1)
     ring.remove( v2)
     if not v1 in ring[0].get_neighbors():
       v1, v2 = v2, v1
     side = sum( [geometry.on_which_side_is_point((v1.x,v1.y,v2.x,v2.y),(v.x,v.y)) for v in base])
     if not side:
       warnings.warn( "this should not happen")
     ca = geometry.clockwise_angle_from_east( v1.x-v2.x, v1.y-v2.y)
     size = len( ring)+2
     da = deg_to_rad(180 -180.0*(size-2)/size)
     if side > 0:
       da = -da
     gcoords = gen_angle_stream( da, start_from=ca+da)
     self.apply_gen_to_atoms( gcoords, ring, v1)
     ring.append( v1)
     ring.append( v2)
     out += ring
   else:
     # there are more than 2 atoms common
     if len( ring) == len( base):
       out += self._process_multi_anelated_ring( ring, angle_shift=15)
       #raise( "i don't how to handle this yet")
     else:
       out += self._process_multi_anelated_ring( ring)
   return out
Beispiel #5
0
  def get_angle_gradient2( self, opt_angle, refv, v1, v2):
    ang1 = geometry.clockwise_angle_from_east( v1.x-refv.x, v1.y-refv.y)
    ang2 = geometry.clockwise_angle_from_east( v2.x-refv.x, v2.y-refv.y)
    ang = ang1 - ang2
    sign = -geometry.on_which_side_is_point( (refv.x, refv.y, v1.x, v1.y), (v2.x, v2.y))
    
    while ang < 0:
      ang += 2*pi
    if ang > pi:
      ang = 2*pi - ang
      sign *= -1

    dang = (ang - opt_angle) / 2

    gx1 = (cos( ang1 - sign*dang) - cos( ang1)) * sqrt( (refv.x-v1.x)**2 + (refv.y-v1.y)**2) #self.bond_length #(v1.x-refv.x)
    gy1 = (sin( ang1 - sign*dang) - sin( ang1)) * sqrt( (refv.x-v1.x)**2 + (refv.y-v1.y)**2) #self.bond_length #(v1.y-refv.y)

    gx2 = (cos( ang2 + sign*dang) - cos( ang2)) * sqrt( (refv.x-v2.x)**2 + (refv.y-v2.y)**2) #self.bond_length #(v2.x-refv.x)
    gy2 = (sin( ang2 + sign*dang) - sin( ang2)) * sqrt( (refv.x-v2.x)**2 + (refv.y-v2.y)**2) #self.bond_length #(v2.y-refv.y)


    # control
    ang1 = geometry.clockwise_angle_from_east( v1.x-refv.x+gx1, v1.y-refv.y+gy1)
    ang2 = geometry.clockwise_angle_from_east( v2.x-refv.x+gx2, v2.y-refv.y+gy2)
    ang = ang1 - ang2
    sign = geometry.on_which_side_is_point( (refv.x, refv.y, v1.x, v1.y), (v2.x, v2.y))
    
    while ang < 0:
      ang += 2*pi
      #sign *= -1
    if ang > pi:
      ang = 2*pi - ang
      #sign *= -1

    dang2 = (ang - opt_angle) / 2

    if abs( dang2) > abs( dang):
      print "f**k", rad_to_deg( ang1), rad_to_deg( ang2), rad_to_deg( dang)
    else:
      #rint "good", rad_to_deg( ang1), rad_to_deg( ang2), rad_to_deg( dang)
      pass

    return gx1, gy1, gx2, gy2
Beispiel #6
0
  def _draw_edge( self, e):
    v1, v2 = e.vertices
    start = self.transformer.transform_xy( v1.x, v1.y)
    end = self.transformer.transform_xy( v2.x, v2.y)
    parent = self._create_parent( e, self.top)

    if e.order == 1:
      self._draw_line( parent, start, end, line_width=self.line_width)

    if e.order == 2:
      side = 0
      # find how to center the bonds
      # rings have higher priority in setting the positioning
      for ring in self.molecule.get_smallest_independent_cycles():
        if v1 in ring and v2 in ring:
          side += reduce( operator.add, [geometry.on_which_side_is_point( start+end, (self.transformer.transform_xy( a.x,a.y))) for a in ring if a!=v1 and a!=v2])
      # if rings did not decide, use the other neigbors
      if not side:
        for v in v1.neighbors + v2.neighbors:
          if v != v1 and v!= v2:
            side += geometry.on_which_side_is_point( start+end, (self.transformer.transform_xy( v.x, v.y)))
      if side:
        self._draw_line( parent, start, end, line_width=self.line_width)
        x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], self.bond_width*misc.signum( side))
        self._draw_line( parent, (x1, y1), (x2, y2), line_width=self.line_width)
      else:
        for i in (1,-1):
          x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i*self.bond_width*0.5)
          self._draw_line( parent, (x1, y1), (x2, y2), line_width=self.line_width)
        
        
    elif e.order == 3:
      self._draw_line( parent, start, end, line_width=self.line_width)
      for i in (1,-1):
        x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i*self.bond_width*0.7)
        self._draw_line( parent, (x1, y1), (x2, y2), line_width=self.line_width)
Beispiel #7
0
  def _draw_edge( self, e):
    def draw_plain_or_colored_line( _start, _end, second=False):
      """second means if this is not the main line, drawing might be different"""
      if not has_shown_vertex or not self.color_bonds:
        if not second:
          self._draw_line( _start, _end, line_width=self.line_width, capstyle=cairo.LINE_CAP_ROUND)
        else:
          self._draw_line( _start, _end, line_width=self.line_width, capstyle=cairo.LINE_CAP_BUTT)
      else:
        self._draw_colored_line( _start, _end, line_width=self.line_width, start_color=color1, end_color=color2)

    def draw_plain_or_colored_wedge( _start, _end):
      x1, y1 = _start
      x2, y2 = _end
      x, y, x0, y0 = geometry.find_parallel( x1, y1, x2, y2, self.wedge_width/2.0)
      xa, ya, xb, yb = geometry.find_parallel( x1, y1, x2, y2, self.line_width/2.0) 
      # no coloring now
      if not has_shown_vertex or not self.color_bonds:
        self._create_cairo_path( [(xa, ya), (x0, y0), (2*x2-x0, 2*y2-y0), (2*x1-xa, 2*y1-ya)], closed=True)
        self.context.set_source_rgb( 0,0,0)
        self.context.fill()
      else:
        # ratio 0.4 looks better than 0.5 because the area difference
        # is percieved more than length difference
        ratio = 0.4
        xm1 = ratio*xa + (1-ratio)*x0 
        ym1 = ratio*ya + (1-ratio)*y0
        xm2 = (1-ratio)*(2*x2-x0) + ratio*(2*x1-xa)
        ym2 = (1-ratio)*(2*y2-y0) + ratio*(2*y1-ya)
        self.context.set_source_rgb( *color1)
        self._create_cairo_path( [(xa,ya), (xm1,ym1), (xm2,ym2), (2*x1-xa, 2*y1-ya)], closed=True)
        self.context.fill()
        self.context.set_source_rgb( *color2)
        self._create_cairo_path( [(xm1,ym1), (x0, y0), (2*x2-x0, 2*y2-y0), (xm2,ym2)], closed=True)
        self.context.fill()

    def draw_plain_or_colored_hatch( _start, _end):
      x1, y1 = _start
      x2, y2 = _end
      # no coloring now
      x, y, x0, y0 = geometry.find_parallel( x1, y1, x2, y2, self.wedge_width/2.0)
      xa, ya, xb, yb = geometry.find_parallel( x1, y1, x2, y2, self.line_width/2.0) 
      d = math.sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond
      if d == 0:  
        return  # to prevent division by zero
      dx1 = (x0 - xa)/d 
      dy1 = (y0 - ya)/d 
      dx2 = (2*x2 -x0 -2*x1 +xa)/d 
      dy2 = (2*y2 -y0 -2*y1 +ya)/d 
      # we have to decide if the first line should be at the position of the first atom
      draw_start = 1  # is index not boolean
      if not v1 in self._vertex_to_bbox and v1.occupied_valency > 1:
        draw_start = 1
      draw_end = 1    # is added to index not boolean
      if not v2 in self._vertex_to_bbox and v2.occupied_valency > 1:
        draw_end = 0
      # adjust the step length
      step_size = 2*(self.line_width)
      ns = round( d / step_size) or 1
      step_size = d / ns
      # now we finally draw
      self.context.set_line_cap( cairo.LINE_CAP_BUTT)
      self.context.set_source_rgb( *color1)
      middle = 0.5 * (draw_start + int( round( d/ step_size)) + draw_end - 2)
      for i in range( draw_start, int( round( d/ step_size)) +draw_end):
        coords = [xa+dx1*i*step_size, ya+dy1*i*step_size, 2*x1-xa+dx2*i*step_size, 2*y1-ya+dy2*i*step_size] 
        if coords[0] == coords[2] and coords[1] == coords[3]:
          if (dx1+dx2) > (dy1+dy2): 
            coords[0] += 1
          else:
            coords[1] += 1
        self._create_cairo_path( [coords[:2],coords[2:]])
        if i >= middle:
          self.context.stroke()
          self.context.set_source_rgb( *color2)
      self.context.stroke()

    # code itself
    # at first detect the need to make 3D adjustments
    self._transform = transform3d.transform3d()
    self._invtransform = transform3d.transform3d()
    transform = None
    if e.order > 1:
      atom1,atom2 = e.vertices
      for n in atom1.neighbors + atom2.neighbors:
        # e.atom1 and e.atom2 are in this list as well
        if n.z != 0:
          # engage 3d transform prior to detection of where to draw
          transform = self._get_3dtransform_for_drawing( e)
          #transform = None
          break
      if transform:
        for n in atom1.neighbors + atom2.neighbors:
          n.coords = transform.transform_xyz( *n.coords)
        self._transform = transform
        self._invtransform = transform.get_inverse()
    # // end of 3D adjustments
    # now the code itself
    coords = self._where_to_draw_from_and_to( e)
    if coords:
      start = coords[:2]
      end = coords[2:]
      v1, v2 = e.vertices
      if self.color_bonds:
        color1 = self.atom_colors.get( v1.symbol, (0,0,0))
        color2 = self.atom_colors.get( v2.symbol, (0,0,0))
      else:
        color1 = color2 = (0,0,0)
      has_shown_vertex = bool( [1 for _v in e.vertices if _v in self._vertex_to_bbox])

      if e.order == 1:
        if e.type == 'w':
          draw_plain_or_colored_wedge( start, end)
        elif e.type == 'h':
          draw_plain_or_colored_hatch( start, end)
        else:
          draw_plain_or_colored_line( start, end)

      if e.order == 2:
        side = 0
        # find how to center the bonds
        # rings have higher priority in setting the positioning
        in_ring = False
        for ring in self.molecule.get_smallest_independent_cycles_dangerous_and_cached():
          double_bonds = len( [b for b in self.molecule.vertex_subgraph_to_edge_subgraph(ring) if b.order == 2])
          if v1 in ring and v2 in ring:
            in_ring = True
            side += double_bonds * reduce( operator.add, [geometry.on_which_side_is_point( start+end, (a.x,a.y)) for a in ring if a!=v1 and a!=v2])
        # if rings did not decide, use the other neigbors
        if not side:
          for v in v1.neighbors + v2.neighbors:
            if v != v1 and v!= v2:
              side += geometry.on_which_side_is_point( start+end, (v.x, v.y))
        # if neighbors did not decide either
        if not side and (in_ring or not has_shown_vertex):
          if in_ring:
            # we don't want centered bonds inside rings
            side = 1 # select arbitrary value
          else:
            # bond between two unshown atoms - we want to center them only in some cases
            if len( v1.neighbors) == 1 and len( v2.neighbors) == 1:
              # both atoms have only one neighbor 
              side = 0
            elif len( v1.neighbors) < 3 and len( v2.neighbors) < 3:
              # try to figure out which side is more towards the center of the molecule
              side = reduce( operator.add, [geometry.on_which_side_is_point( start+end, (a.x,a.y))
                                            for a in self.molecule.vertices if a!=v1 and a!=v2], 0)
              if not side:
                side = 1 # we choose arbitrary value, we don't want centering
        if side:
          draw_plain_or_colored_line( start, end)
          x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], self.bond_width*misc.signum( side))
          # shorten the second line
          length = geometry.point_distance( x1,y1,x2,y2)
          if v2 not in self._vertex_to_bbox:
            x2, y2 = geometry.elongate_line( x1, y1, x2, y2, -self.bond_second_line_shortening*length)
          if v1 not in self._vertex_to_bbox:
            x1, y1 = geometry.elongate_line( x2, y2, x1, y1, -self.bond_second_line_shortening*length)
          draw_plain_or_colored_line( (x1, y1), (x2, y2), second=True)
        else:
          for i in (1,-1):
            x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i*self.bond_width*0.5)
            draw_plain_or_colored_line( (x1, y1), (x2, y2))

      elif e.order == 3:
        self._draw_line( start, end, line_width=self.line_width)
        for i in (1,-1):
          x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i*self.bond_width*0.7)
          draw_plain_or_colored_line( (x1, y1), (x2, y2), second=True)

    if transform:
      # if transform was used, we need to transform back
      for n in atom1.neighbors + atom2.neighbors:
        n.coords = self._invtransform.transform_xyz( *n.coords)
Beispiel #8
0
    def _draw_edge(self, e):
        def draw_plain_or_colored_line(_start, _end, second=False):
            """second means if this is not the main line, drawing might be different"""
            if not has_shown_vertex or not self.color_bonds:
                if not second:
                    self._draw_line(_start,
                                    _end,
                                    line_width=self.line_width,
                                    capstyle=cairo.LINE_CAP_ROUND)
                else:
                    self._draw_line(_start,
                                    _end,
                                    line_width=self.line_width,
                                    capstyle=cairo.LINE_CAP_BUTT)
            else:
                self._draw_colored_line(_start,
                                        _end,
                                        line_width=self.line_width,
                                        start_color=color1,
                                        end_color=color2)

        def draw_plain_or_colored_wedge(_start, _end):
            x1, y1 = _start
            x2, y2 = _end
            x, y, x0, y0 = geometry.find_parallel(x1, y1, x2, y2,
                                                  self.wedge_width / 2.0)
            xa, ya, xb, yb = geometry.find_parallel(x1, y1, x2, y2,
                                                    self.line_width / 2.0)
            # no coloring now
            if not has_shown_vertex or not self.color_bonds:
                self._create_cairo_path([(xa, ya), (x0, y0),
                                         (2 * x2 - x0, 2 * y2 - y0),
                                         (2 * x1 - xa, 2 * y1 - ya)],
                                        closed=True)
                self.context.set_source_rgb(0, 0, 0)
                self.context.fill()
            else:
                # ratio 0.4 looks better than 0.5 because the area difference
                # is percieved more than length difference
                ratio = 0.4
                xm1 = ratio * xa + (1 - ratio) * x0
                ym1 = ratio * ya + (1 - ratio) * y0
                xm2 = (1 - ratio) * (2 * x2 - x0) + ratio * (2 * x1 - xa)
                ym2 = (1 - ratio) * (2 * y2 - y0) + ratio * (2 * y1 - ya)
                self.context.set_source_rgb(*color1)
                self._create_cairo_path([(xa, ya), (xm1, ym1), (xm2, ym2),
                                         (2 * x1 - xa, 2 * y1 - ya)],
                                        closed=True)
                self.context.fill()
                self.context.set_source_rgb(*color2)
                self._create_cairo_path([(xm1, ym1), (x0, y0),
                                         (2 * x2 - x0, 2 * y2 - y0),
                                         (xm2, ym2)],
                                        closed=True)
                self.context.fill()

        def draw_plain_or_colored_hatch(_start, _end):
            x1, y1 = _start
            x2, y2 = _end
            # no coloring now
            x, y, x0, y0 = geometry.find_parallel(x1, y1, x2, y2,
                                                  self.wedge_width / 2.0)
            xa, ya, xb, yb = geometry.find_parallel(x1, y1, x2, y2,
                                                    self.line_width / 2.0)
            d = math.sqrt((x1 - x2)**2 + (y1 - y2)**2)  # length of the bond
            if d == 0:
                return  # to prevent division by zero
            dx1 = (x0 - xa) / d
            dy1 = (y0 - ya) / d
            dx2 = (2 * x2 - x0 - 2 * x1 + xa) / d
            dy2 = (2 * y2 - y0 - 2 * y1 + ya) / d
            # we have to decide if the first line should be at the position of the first atom
            draw_start = 1  # is index not boolean
            if not v1 in self._vertex_to_bbox and v1.occupied_valency > 1:
                draw_start = 1
            draw_end = 1  # is added to index not boolean
            if not v2 in self._vertex_to_bbox and v2.occupied_valency > 1:
                draw_end = 0
            # adjust the step length
            step_size = 2 * (self.line_width)
            ns = round(d / step_size) or 1
            step_size = d / ns
            # now we finally draw
            self.context.set_line_cap(cairo.LINE_CAP_BUTT)
            self.context.set_source_rgb(*color1)
            middle = 0.5 * (draw_start + int(round(d / step_size)) + draw_end -
                            2)
            for i in range(draw_start, int(round(d / step_size)) + draw_end):
                coords = [
                    xa + dx1 * i * step_size, ya + dy1 * i * step_size,
                    2 * x1 - xa + dx2 * i * step_size,
                    2 * y1 - ya + dy2 * i * step_size
                ]
                if coords[0] == coords[2] and coords[1] == coords[3]:
                    if (dx1 + dx2) > (dy1 + dy2):
                        coords[0] += 1
                    else:
                        coords[1] += 1
                self._create_cairo_path([coords[:2], coords[2:]])
                if i >= middle:
                    self.context.stroke()
                    self.context.set_source_rgb(*color2)
            self.context.stroke()

        # code itself
        # at first detect the need to make 3D adjustments
        self._transform = transform3d.transform3d()
        self._invtransform = transform3d.transform3d()
        transform = None
        if e.order > 1:
            atom1, atom2 = e.vertices
            for n in atom1.neighbors + atom2.neighbors:
                # e.atom1 and e.atom2 are in this list as well
                if n.z != 0:
                    # engage 3d transform prior to detection of where to draw
                    transform = self._get_3dtransform_for_drawing(e)
                    #transform = None
                    break
            if transform:
                for n in atom1.neighbors + atom2.neighbors:
                    n.coords = transform.transform_xyz(*n.coords)
                self._transform = transform
                self._invtransform = transform.get_inverse()
        # // end of 3D adjustments
        # now the code itself
        coords = self._where_to_draw_from_and_to(e)
        if coords:
            start = coords[:2]
            end = coords[2:]
            v1, v2 = e.vertices
            if self.color_bonds:
                color1 = self.atom_colors.get(v1.symbol, (0, 0, 0))
                color2 = self.atom_colors.get(v2.symbol, (0, 0, 0))
            else:
                color1 = color2 = (0, 0, 0)
            has_shown_vertex = bool(
                [1 for _v in e.vertices if _v in self._vertex_to_bbox])

            if e.order == 1:
                if e.type == 'w':
                    draw_plain_or_colored_wedge(start, end)
                elif e.type == 'h':
                    draw_plain_or_colored_hatch(start, end)
                else:
                    draw_plain_or_colored_line(start, end)

            if e.order == 2:
                side = 0
                # find how to center the bonds
                # rings have higher priority in setting the positioning
                in_ring = False
                for ring in self.molecule.get_smallest_independent_cycles_dangerous_and_cached(
                ):
                    double_bonds = len([
                        b for b in
                        self.molecule.vertex_subgraph_to_edge_subgraph(ring)
                        if b.order == 2
                    ])
                    if v1 in ring and v2 in ring:
                        in_ring = True
                        side += double_bonds * reduce(operator.add, [
                            geometry.on_which_side_is_point(
                                start + end, (a.x, a.y))
                            for a in ring if a != v1 and a != v2
                        ])
                # if rings did not decide, use the other neigbors
                if not side:
                    for v in v1.neighbors + v2.neighbors:
                        if v != v1 and v != v2:
                            side += geometry.on_which_side_is_point(
                                start + end, (v.x, v.y))
                # if neighbors did not decide either
                if not side and (in_ring or not has_shown_vertex):
                    if in_ring:
                        # we don't want centered bonds inside rings
                        side = 1  # select arbitrary value
                    else:
                        # bond between two unshown atoms - we want to center them only in some cases
                        if len(v1.neighbors) == 1 and len(v2.neighbors) == 1:
                            # both atoms have only one neighbor
                            side = 0
                        elif len(v1.neighbors) < 3 and len(v2.neighbors) < 3:
                            # try to figure out which side is more towards the center of the molecule
                            side = reduce(operator.add, [
                                geometry.on_which_side_is_point(
                                    start + end, (a.x, a.y))
                                for a in self.molecule.vertices
                                if a != v1 and a != v2
                            ], 0)
                            if not side:
                                side = 1  # we choose arbitrary value, we don't want centering
                if side:
                    draw_plain_or_colored_line(start, end)
                    x1, y1, x2, y2 = geometry.find_parallel(
                        start[0], start[1], end[0], end[1],
                        self.bond_width * misc.signum(side))
                    # shorten the second line
                    length = geometry.point_distance(x1, y1, x2, y2)
                    if v2 not in self._vertex_to_bbox:
                        x2, y2 = geometry.elongate_line(
                            x1, y1, x2, y2,
                            -self.bond_second_line_shortening * length)
                    if v1 not in self._vertex_to_bbox:
                        x1, y1 = geometry.elongate_line(
                            x2, y2, x1, y1,
                            -self.bond_second_line_shortening * length)
                    draw_plain_or_colored_line((x1, y1), (x2, y2), second=True)
                else:
                    for i in (1, -1):
                        x1, y1, x2, y2 = geometry.find_parallel(
                            start[0], start[1], end[0], end[1],
                            i * self.bond_width * 0.5)
                        draw_plain_or_colored_line((x1, y1), (x2, y2))

            elif e.order == 3:
                self._draw_line(start, end, line_width=self.line_width)
                for i in (1, -1):
                    x1, y1, x2, y2 = geometry.find_parallel(
                        start[0], start[1], end[0], end[1],
                        i * self.bond_width * 0.7)
                    draw_plain_or_colored_line((x1, y1), (x2, y2), second=True)

        if transform:
            # if transform was used, we need to transform back
            for n in atom1.neighbors + atom2.neighbors:
                n.coords = self._invtransform.transform_xyz(*n.coords)