Exemplo n.º 1
0
def get_parallel_signum( l1, l2):
  x1a, y1a, x2a, y2a = l1
  x1b, y1b, x2b, y2b = l2
  if x1a == x2a:
    return signum( -x1a + x1b)
  elif y1a == y2a:
    return signum( -y1a+y1b)
  else:
    return signum( -x2a+x2b)
Exemplo n.º 2
0
def get_parallel_signum(l1, l2):
    x1a, y1a, x2a, y2a = l1
    x1b, y1b, x2b, y2b = l2
    if x1a == x2a:
        return signum(-x1a + x1b)
    elif y1a == y2a:
        return signum(-y1a + y1b)
    else:
        return signum(-x2a + x2b)
Exemplo n.º 3
0
def single_sided_arrow_head (x1,y1,x2,y2,a,b,c,lw):
  '''last two points of arrow 1->2
  a,b,c like tkinter
  a = leght from point 2 where the head touches the line (out point A)
  b = total lenght of the head (defines also help point P on the line)
  c = width 
  Point B will be the outer Point of the head
  rl = "r" the head is on the right , = "l" left,
  lw is the line_width of the line the arrow will be attached to'''

  xa,ya = geometry.elongate_line (x1,y1,x2,y2,-a)
  xa,ya = geometry.point_at_distance_from_line (x1,y1,xa,ya,-misc.signum(c)*(lw-1.0)/2.0)
  xp,yp = geometry.elongate_line (x1,y1,x2,y2,-b)
  xb,yb = geometry.point_at_distance_from_line (x1,y1,xp,yp,c)
  xc,yc = geometry.point_at_distance_from_line (x1,y1,x2,y2,-misc.signum(c)*(lw-1.0)/2.0)
  return xa,ya, xc,yc, xb,yb
Exemplo n.º 4
0
 def resize(self, coords, fix=()):
     if not fix:
         x1, y1, x2, y2 = misc.normalize_coords(coords)
         dx = x2 - x1
         dy = y2 - y1
         d = (abs(dx) + abs(dy)) / 2
         self.coords = (x1, y1, x1 + d, y1 + d)
     else:
         x1, y1, x2, y2 = coords
         dx = (fix[0] - x1) or (fix[0] - x2)
         dy = (fix[1] - y2) or (fix[1] - y1)
         d = (abs(dx) + abs(dy)) / 2
         self.coords = misc.normalize_coords(
             (fix[0], fix[1], x1 - (d * misc.signum(dx) or d),
              y1 - (d * misc.signum(dy) or d)))
     self.paper.coords(self.item, self.coords)
Exemplo n.º 5
0
 def _valency_to_charge( self, v, charge):
   """sets charge of the atom so that it has minimal free_valency,
   returns the 'unused' charge in case it is not comsumed whole"""
   ch = misc.signum( charge) * min( abs( charge), v.free_valency)
   if charge < 0:
     charge += ch
     v.charge = -ch
   else:
     charge -= ch
     v.charge = ch
   return charge
Exemplo n.º 6
0
 def _valency_to_charge(self, v, charge):
     """sets charge of the atom so that it has minimal free_valency,
 returns the 'unused' charge in case it is not comsumed whole"""
     ch = misc.signum(charge) * min(abs(charge), v.free_valency)
     if charge < 0:
         charge += ch
         v.charge = -ch
     else:
         charge -= ch
         v.charge = ch
     return charge
Exemplo n.º 7
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)
Exemplo n.º 8
0
 def read_standard_values( self, standard, old_standard=None):
   meta_enabled.read_standard_values( self, standard, old_standard=old_standard)
   # wedge width
   if not old_standard or (standard.wedge_width != old_standard.wedge_width):
     self.wedge_width = Screen.any_to_px( standard.wedge_width)
   # line width
   if not old_standard or (standard.line_width != old_standard.line_width):
     self.line_width = Screen.any_to_px( standard.line_width)
   # bond width
   if not old_standard or (standard.bond_width != old_standard.bond_width):
     if hasattr( self, 'bond_width'):
       self.bond_width = misc.signum( self.bond_width) * Screen.any_to_px( standard.bond_width)
     else:
       self.bond_width = Screen.any_to_px( standard.bond_width)
Exemplo n.º 9
0
 def resize( self, coords, fix=()):
   if not fix:
     x1, y1, x2, y2 = misc.normalize_coords( coords)
     dx = x2 - x1
     dy = y2 - y1
     d = (abs( dx) + abs( dy))/2
     self.coords = (x1, y1, x1+d, y1+d)
   else:
     x1, y1, x2, y2 = coords
     dx = (fix[0] - x1) or (fix[0] - x2)
     dy = (fix[1] - y2) or (fix[1] - y1)
     d = (abs( dx) + abs( dy))/2
     self.coords = misc.normalize_coords( (fix[0], fix[1], x1-(d*misc.signum( dx) or d), y1-( d*misc.signum( dy) or d)))
   self.paper.coords( self.item, self.coords)
Exemplo n.º 10
0
 def read_standard_values( self, standard, old_standard=None):
   meta_enabled.read_standard_values( self, standard, old_standard=old_standard)
   # wedge width
   if not old_standard or (standard.wedge_width != old_standard.wedge_width):
     self.wedge_width = Screen.any_to_px( standard.wedge_width)
   # line width
   if not old_standard or (standard.line_width != old_standard.line_width):
     self.line_width = Screen.any_to_px( standard.line_width)
   # bond width
   if not old_standard or (standard.bond_width != old_standard.bond_width):
     if hasattr( self, 'bond_width'):
       self.bond_width = misc.signum( self.bond_width) * Screen.any_to_px( standard.bond_width)
     else:
       self.bond_width = Screen.any_to_px( standard.bond_width)
Exemplo n.º 11
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)
Exemplo n.º 12
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)
Exemplo n.º 13
0
    def done(self, button):
        """called on dialog exit"""
        self.dialog.deactivate()
        if button != _('OK'):
            pass
        else:
            #print self.marks.get()
            # apply changes
            for o in self.items:
                change = 0
                # ATOM
                if o.object_type == 'atom':
                    a = self.atom_show.index(Pmw.SELECT)
                    if a != 2:
                        o.show = a
                        change = 1
                    # positionning
                    a = self.atom_pos.index(Pmw.SELECT)
                    if a != 2:
                        o.pos = ('center-first', 'center-last')[a]
                        change = 1
                    if self.atom_charge.get():
                        a = int(self.atom_charge.get())
                        if hasattr(o, 'charge') and o.charge != a:
                            o.charge = a
                        change = 1
                    # hydrogens
                    a = int(self.atom_show_h.index(Pmw.SELECT))
                    if a != 2:
                        o.show_hydrogens = a
                        change = 1
                    # font is in common now
                # BOND
                elif o.object_type == 'bond':
                    # width is in common now
                    # bond_width
                    d = Screen.any_to_px(self.bond_dist.getvalue())
                    if d:
                        if d != abs(o.bond_width):
                            o.bond_width = d * misc.signum(o.bond_width)
                            change = 1
                    # wedge_width
                    d = Screen.any_to_px(self.wedge_width.getvalue())
                    if d:
                        if d != o.wedge_width:
                            o.wedge_width = d
                            change = 1
                    # ratio
                    ratio = self.double_length_ratio.getvalue()
                    if ratio:
                        ratio = float(self.double_length_ratio.getvalue())
                        if ratio != o.double_length_ratio:
                            o.double_length_ratio = ratio
                            change = 1

                # ARROW - most is in common now
                elif o.object_type == 'arrow':
                    if self.arrow_start_changed:
                        o.set_pins(start=self.arrow_start.get())
                        change = 1
                    if self.arrow_end_changed:
                        o.set_pins(end=self.arrow_end.get())
                        change = 1
                    if self.spline_changed:
                        o.spline = self.spline.get()
                        change = 1

                # TEXT - all is in common now
                # PLUS - all is in common now
                # VECTOR - all is in common now

                # COMMON PROPERTIES
                # LINE COLOR
                if hasattr(o, 'line_color') and self.line_color.color:
                    if self.line_color.color != o.line_color:
                        o.line_color = self.line_color.color
                        change = 1
                # AREA COLOR
                if hasattr(o, 'area_color') and self.area_color.color:
                    if self.area_color.color != o.area_color:
                        o.area_color = self.area_color.color
                        change = 1
                # LINE WIDTH
                if hasattr(o, 'line_width'):
                    w = Screen.any_to_px(self.line_width.getvalue())
                    if w:
                        if w != o.line_width:
                            o.line_width = w
                            change = 1
                # FONT
                if hasattr(o, 'font_family'):
                    if self.font_size.get():
                        a = int(self.font_size.get())
                        o.font_size = a
                        change = 1
                    if self.font_family.getcurselection(
                    ) and self.font_family.getcurselection(
                    )[0] != self.used_family:
                        a = self.font_family.getcurselection()[0]
                        o.font_family = a
                        change = 1

                # APPLY THE CHANGES
                if change:
                    o.redraw()
                    self.changes_made = 1
        self.cleanup()
Exemplo n.º 14
0
  def done( self, button):
    """called on dialog exit"""
    self.dialog.deactivate()
    if button != _('OK'):
      pass
    else:
      #print(self.marks.get())
      # apply changes
      for o in self.items:
        change = 0
        # ATOM
        if o.object_type == 'atom':
          a = self.atom_show.index( Pmw.SELECT)
          if a != 2:
            o.show = a
            change = 1
          # positionning
          a = self.atom_pos.index( Pmw.SELECT)
          if a != 2:
            o.pos = ('center-first', 'center-last')[ a]
            change = 1
          if self.atom_charge.get():
            a = int( self.atom_charge.get())
            if hasattr( o, 'charge') and o.charge != a:
              o.charge = a
            change = 1
          # hydrogens
          a = int( self.atom_show_h.index( Pmw.SELECT))
          if a != 2:
            o.show_hydrogens = a
            change = 1
          # font is in common now
        # BOND
        elif o.object_type == 'bond':
          # width is in common now
          # bond_width
          d = Screen.any_to_px( self.bond_dist.getvalue())
          if d:
            if d != abs( o.bond_width):
              o.bond_width = d * misc.signum( o.bond_width)
              change = 1
          # wedge_width
          d = Screen.any_to_px( self.wedge_width.getvalue())
          if d:
            if d != o.wedge_width:
              o.wedge_width = d
              change = 1
          # ratio
          ratio = self.double_length_ratio.getvalue()
          if ratio:
            ratio = float( self.double_length_ratio.getvalue())
            if ratio != o.double_length_ratio:
              o.double_length_ratio = ratio
              change = 1

        # ARROW - most is in common now
        elif o.object_type == 'arrow':
          if self.arrow_start_changed:
            o.set_pins( start = self.arrow_start.get())
            change = 1
          if self.arrow_end_changed:
            o.set_pins( end = self.arrow_end.get())
            change = 1
          if self.spline_changed:
            o.spline = self.spline.get()
            change = 1

        # TEXT - all is in common now
        # PLUS - all is in common now
        # VECTOR - all is in common now

        # COMMON PROPERTIES
        # LINE COLOR
        if hasattr( o, 'line_color') and self.line_color.color:
          if self.line_color.color != o.line_color:
            o.line_color = self.line_color.color
            change = 1
        # AREA COLOR
        if hasattr( o, 'area_color') and self.area_color.color:
          if self.area_color.color != o.area_color:
            o.area_color = self.area_color.color
            change = 1
        # LINE WIDTH
        if hasattr( o, 'line_width'):
          w = Screen.any_to_px( self.line_width.getvalue())
          if w:
            if w != o.line_width:
              o.line_width = w
              change = 1
        # FONT
        if hasattr( o, 'font_family'):
          if self.font_size.get():
            a = int( self.font_size.get())
            o.font_size = a
            change = 1
          if self.font_family.getcurselection() and self.font_family.getcurselection()[0] != self.used_family:
            a = self.font_family.getcurselection()[0]
            o.font_family = a
            change = 1

        # APPLY THE CHANGES
        if change:
          o.redraw()
          self.changes_made = 1
    self.cleanup()
Exemplo n.º 15
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)