Example #1
0
  def on_begin( self, scaling=None):
    self.paper.unselect_all()
    if self.paper.get_paper_property( 'crop_svg'):

      if len( self.paper.find_all()) <= 1: # background only
        Store.log( _('There is nothing to export. If you want to export an empty paper disable cropping of the drawing in the File/Properties menu.'), message_type="error")
        return 0

      x1, y1, x2, y2 = self.paper.get_cropping_bbox()
      dx = x2-x1
      dy = y2-y1
      scalex, scaley = scaling or self.get_scaling( dx, dy)
      if not scalex:
        # the setting of scaling was canceled
        return 0

      self.transformer = transform.transform()
      self.transformer.set_move( -x1, -y1)
      self.transformer.set_scaling_xy( scalex, scaley)
    else:
      dx = Screen.mm_to_px( self.paper._paper_properties['size_x'])
      dy = Screen.mm_to_px( self.paper._paper_properties['size_y'])
      scalex, scaley = scaling or self.get_scaling( dx, dy)
      if not scalex:
        # the setting of scaling was canceled
        return 0

      self.transformer = transform.transform()
      self.transformer.set_scaling_xy( scalex, scaley)

    x1, y1, x2, y2 = self.transformer.transform_4( (0, 0, dx, dy))
    self.pagesize = tuple( map( round, (x2-x1, y2-y1)))
    self.attrs['text_to_curves'] = False
    self.converter = self.converter_class( **self.attrs)
    return 1
Example #2
0
def convert_selected_to_linear_fragment(paper):
    # check the selection
    bond_length = 10
    changes = False
    mols = [
        m for m in paper.selected_to_unique_top_levels()[0]
        if m.object_type == "molecule"
    ]
    for mol in mols:
        vs = [v for v in mol.vertices if v in paper.selected]
        try:
            change = atoms_to_linear_fragment(mol, vs, bond_length=bond_length)
        except ValueError:
            Store.log(_("The selection does not define connected subgraph"),
                      message_type="error")
            return
        except excs.bkchem_graph_error, e:
            if e.id == "circular_selection":
                Store.log(e.value, message_type="error")
            else:
                raise
        else:
            changes = changes or change
            if changes:
                f = mol.create_fragment(
                    "linear_form",
                    mol.vertex_subgraph_to_edge_subgraph(vs),
                    vs,
                    type="linear_form")
                f.properties['bond_length'] = bond_length
Example #3
0
  def on_begin( self):
    self.paper.unselect_all()
    scale = 720.0/self.paper.winfo_fpixels( '254m')
    if self.paper.get_paper_property( 'crop_svg'):
      margin = self.paper.get_paper_property('crop_margin')
      items = list( self.paper.find_all())
      items.remove( self.paper.background)

      if not items:
        Store.log( _('There is nothing to export. If you want to export an empty paper disable cropping of the drawing in the File/Properties menu.'), message_type="error")
        return 0

      x1, y1, x2, y2 = self.paper.list_bbox( items)
      self.transformer = transform.transform()
      self.transformer.set_move( -x1+margin, -y1+margin)
      self.transformer.set_scaling( scale)
      dx = x2-x1 +2*margin
      dy = y2-y1 +2*margin
    else:
      self.transformer = transform.transform()
      self.transformer.set_scaling( scale)
      dx = Screen.mm_to_px( self.paper._paper_properties['size_x'])
      dy = Screen.mm_to_px( self.paper._paper_properties['size_y'])

    self.canvas = self.init_canvas( pagesize=(scale*dx, scale*dy))
    self.converter = self.converter_class()
    return 1
Example #4
0
def ask_name_for_selected(paper):
    """opens dialog for input of molecule name and sets it"""
    top_levels, unique = paper.selected_to_unique_top_levels()
    ms = [o for o in top_levels if isinstance(o, molecule)]

    if not ms:
        tkMessageBox.showerror(
            _("No molecule selected."),
            _("At least one molecule must be selected. Please select it."))
        return

    dial = Pmw.PromptDialog(paper,
                            title=_('Name'),
                            label_text=_('Name:'),
                            entryfield_labelpos='n',
                            buttons=(_('OK'), _('Cancel')))
    # if only one mol is selected use it as default
    if len(ms) == 1 and ms[0].name:
        dial.insertentry(0, ms[0].name)
    res = dial.activate()
    if res == _('OK'):
        name = dial.get()
    else:
        return

    for m in ms:
        m.name = name
    Store.log(_('Name %s was set to molecule(s)') % name)
    paper.start_new_undo_record()
Example #5
0
def ask_name_for_selected( paper):
  """opens dialog for input of molecule name and sets it"""
  top_levels, unique = paper.selected_to_unique_top_levels()
  ms = [o for o in top_levels if isinstance( o, molecule)]

  if not ms:
    tkMessageBox.showerror( _("No molecule selected."),
                            _("At least one molecule must be selected. Please select it."))
    return

  dial = Pmw.PromptDialog( paper,
                           title=_('Name'),
                           label_text=_('Name:'),
                           entryfield_labelpos = 'n',
                           buttons=(_('OK'),_('Cancel')))
  # if only one mol is selected use it as default
  if len( ms) == 1 and ms[0].name:
    dial.insertentry( 0, ms[0].name)
  res = dial.activate()
  if res == _('OK'):
    name = dial.get()
  else:
    return

  for m in ms:
    m.name = name
  Store.log( _('Name %s was set to molecule(s)') % name)
  paper.start_new_undo_record()
Example #6
0
  def on_begin( self, scaling=None):
    self.paper.unselect_all()
    if self.paper.get_paper_property( 'crop_svg'):

      if len( self.paper.find_all()) <= 1: # background only
        Store.log( _('There is nothing to export. If you want to export an empty paper disable cropping of the drawing in the File/Properties menu.'), message_type="error")
        return 0

      x1, y1, x2, y2 = self.paper.get_cropping_bbox()
      dx = x2-x1
      dy = y2-y1
      scalex, scaley = scaling or self.get_scaling( dx, dy)
      if not scalex:
        # the setting of scaling was canceled
        return 0

      self.transformer = transform.transform()
      self.transformer.set_move( -x1, -y1)
      self.transformer.set_scaling_xy( scalex, scaley)
    else:
      dx = Screen.mm_to_px( self.paper._paper_properties['size_x'])
      dy = Screen.mm_to_px( self.paper._paper_properties['size_y'])
      scalex, scaley = scaling or self.get_scaling( dx, dy)
      if not scalex:
        # the setting of scaling was canceled
        return 0

      self.transformer = transform.transform()
      self.transformer.set_scaling_xy( scalex, scaley)

    x1, y1, x2, y2 = self.transformer.transform_4( (0, 0, dx, dy))
    self.pagesize = tuple( map( round, (x2-x1, y2-y1)))
    self.attrs['text_to_curves'] = False
    self.converter = self.converter_class( **self.attrs)
    return 1
Example #7
0
    def on_begin(self):
        self.paper.unselect_all()
        scale = 720.0 / self.paper.winfo_fpixels('254m')
        if self.paper.get_paper_property('crop_svg'):
            margin = self.paper.get_paper_property('crop_margin')
            items = list(self.paper.find_all())
            items.remove(self.paper.background)

            if not items:
                Store.log(_(
                    'There is nothing to export. If you want to export an empty paper disable cropping of the drawing in the File/Properties menu.'
                ),
                          message_type="error")
                return 0

            x1, y1, x2, y2 = self.paper.list_bbox(items)
            self.transformer = transform.transform()
            self.transformer.set_move(-x1 + margin, -y1 + margin)
            self.transformer.set_scaling(scale)
            dx = x2 - x1 + 2 * margin
            dy = y2 - y1 + 2 * margin
        else:
            self.transformer = transform.transform()
            self.transformer.set_scaling(scale)
            dx = Screen.mm_to_px(self.paper._paper_properties['size_x'])
            dy = Screen.mm_to_px(self.paper._paper_properties['size_y'])

        self.canvas = self.init_canvas(pagesize=(scale * dx, scale * dy))
        self.converter = self.converter_class()
        return 1
Example #8
0
def convert_selected_to_linear_fragment( paper):
  # check the selection
  bond_length = 10
  changes = False
  mols = [m for m in paper.selected_to_unique_top_levels()[0] if m.object_type == "molecule"]
  for mol in mols:
    vs = [v for v in mol.vertices if v in paper.selected]
    try:
      change = atoms_to_linear_fragment( mol, vs, bond_length=bond_length)
    except ValueError:
      Store.log( _("The selection does not define connected subgraph"), message_type="error")
      return
    except excs.bkchem_graph_error as e:
      if e.id == "circular_selection":
        Store.log( e.value, message_type="error")
      else:
        raise
    else:
      changes = changes or change
      if changes:
        f = mol.create_fragment( "linear_form", mol.vertex_subgraph_to_edge_subgraph( vs), vs, type="linear_form")
        f.properties['bond_length'] = bond_length

  if changes:
    paper.start_new_undo_record()
Example #9
0
def atoms_to_linear_fragment( mol, vs, bond_length=10):
  changes = False
  if vs and mol.defines_connected_subgraph_v( vs):
    # the selection is connected
    for v in vs:
      if len( [n for n in v.neighbors if n in vs]) > 2:
        Store.log( _("The selection is not linear - there are some splittings."), message_type="error")
        return
    # ok, we are clear
    # here comes the code to do the work
    # we start from the end atom that is more on the left side
    changes = True
    ends = [v for v in vs if len( [n for n in v.neighbors if n in vs]) == 1]
    if not ends and len( vs) != 1:
      # whole ring is selected, how could this be possibly linearized?
      raise excs.bkchem_graph_error( "circular_selection",
                                     _("The selected part of a molecule is a whole ring, there is no way to linearize it"))
    if len( vs) == 1:
      start = list(vs)[0]
      end = start
    else:
      start = ends[0].x > ends[1].x and ends[1] or ends[0]
      end = start == ends[0] and ends[1] or ends[0]
    current = start
    x = current.x
    y = current.y
    processed = set()
    current_e = None
    while 1:
      processed.add( current)
      current.show_hydrogens = True
      current.redraw()
      if current != start:
        dx = x - current.bbox()[0] + bond_length
        dy = start.y - current.y
        # move all neighbors that are not selected with their fragments
        ps = mol.get_pieces_after_edge_removal( current_e)
        if len( ps) == 2:
          p = current in ps[0] and ps[0] or ps[1]
          for a in p:
            a.move( dx, dy)
        else:
          # we are in a ring - move only current
          current.move( dx, dy)
      x = current.bbox()[2]
      if current != end:
        new = [n for n in current.neighbors if n in vs and n not in processed][0]
        current_e = new.get_edge_leading_to( current)
        current = new
      else:
        break

    mol.redraw()
    return changes

  else:
    raise ValueError, "the vertices do not define connected subgraph"
Example #10
0
def set_atom_number( atoms):
  dial = Pmw.PromptDialog( Store.app,
                           title=_('Atom number'),
                           label_text=_('Enter atom number:'),
                           entryfield_labelpos = 'w',
                           buttons=(_('OK'),_('Cancel')))
  res = dial.activate()
  if res == _('OK'):
    for a in atoms:
      a.number = dial.get()
    Store.log( _("Number %s was set to atom(s).") % dial.get(), message_type="info")
Example #11
0
def set_atom_number(atoms):
    dial = Pmw.PromptDialog(Store.app,
                            title=_('Atom number'),
                            label_text=_('Enter atom number:'),
                            entryfield_labelpos='w',
                            buttons=(_('OK'), _('Cancel')))
    res = dial.activate()
    if res == _('OK'):
        for a in atoms:
            a.number = dial.get()
        Store.log(_("Number %s was set to atom(s).") % dial.get(),
                  message_type="info")
Example #12
0
def ask_id_for_selected(paper):
    """opens dialog for input of molecule ID and sets it"""
    top_levels, unique = paper.selected_to_unique_top_levels()
    ms = [o for o in top_levels if isinstance(o, molecule)]

    if not ms:
        tkMessageBox.showerror(
            _("No molecule selected."),
            _("At least one molecule must be selected. Please select it."))
        return

    if len(ms) > 1:
        tkMessageBox.showerror(
            _("Only one molecule should be selected."),
            _("ID must be unique value, therefore it is obviously possible to set it to one molecule only. Please select only one molecule"
              ))
        return

    m = ms[0]

    while 1:
        dial = Pmw.PromptDialog(paper,
                                title=_('Id'),
                                label_text=_('Id:'),
                                entryfield_labelpos='n',
                                buttons=(_('OK'), _('Cancel')))
        # put the recent value
        if m.id:
            dial.insertentry(0, m.id)

        res = dial.activate()
        if res == _('OK'):
            id = dial.get()
        else:
            return

        collision = 0
        for mol in paper.molecules:
            if mol != m and mol.id == id:
                tkMessageBox.showerror(
                    _("ID collision"),
                    _("This ID is already used, use a different one"))
                collision = 1
                break
        if not collision:
            break

    m.id = id
    Store.log(_('ID %s was set to molecule') % id)
    paper.start_new_undo_record()
Example #13
0
def check_linear_fragments( paper):
  """checks the state of linear fragments present on the paper and resets their appearance"""
  #mols = paper.um.get_changed_molecules()
  last_record = paper.um.get_last_record()
  for mol in paper.molecules:
    to_del = set()
    fs = [f for f in mol.fragments if f.type == "linear_form"]
    if fs and (last_record == None or last_record.object_changed( mol)):
      for f in fs:
        if mol.check_linear_form_fragment( f) == False:
          to_del.add( f)
    for f in to_del:
      Store.log( _('The linear form was no longer consistent - it has been removed'))
      mol.delete_fragment( f)
Example #14
0
def ask_display_form_for_selected(paper):
    top_levels, unique = paper.selected_to_unique_top_levels()
    ms = [o for o in top_levels if isinstance(o, molecule)]

    if not ms:
        tkMessageBox.showerror(
            _("No molecule selected."),
            _("At least one molecule must be selected. Please select it."))
        return

    dial = Pmw.Dialog(
        paper,
        title=_('Display Form'),
        #defaultbutton = _('OK'),
        buttons=(_('OK'), _('Cancel')))
    input = widgets.HTMLLikeInput(dial.interior())
    input.pack()
    input.editPool.focus_set()

    # if only one mol is selected use it as default
    if len(ms) == 1 and ms[0].display_form:
        input.text = ms[0].display_form
    res = dial.activate()
    if res == _('OK'):
        df = input.editPool.get()
        df = unicode(df).encode('utf-8')
        ## catch not well-formed text
        try:
            xml.sax.parseString("<a>%s</a>" % df, xml.sax.ContentHandler())
        except xml.sax.SAXParseException:
            df = xml.sax.saxutils.escape(df)
            # the second round of try: except: should catch problems not
            # related to XML wellfomedness but rather to encoding
            try:
                xml.sax.parseString("<a>%s</a>" % df, xml.sax.ContentHandler())
            except xml.sax.SAXParseException:
                tkMessageBox.showerror(
                    _("Parse Error"),
                    _("Unable to parse the text-\nprobably problem with input encoding!"
                      ))
                Store.app.paper.bell()
                return
    else:
        return

    for m in ms:
        m.display_form = df
    Store.log(_('Display form %s was set to molecule(s)') % df)
    paper.start_new_undo_record()
Example #15
0
def check_linear_fragments(paper):
    """checks the state of linear fragments present on the paper and resets their appearance"""
    #mols = paper.um.get_changed_molecules()
    last_record = paper.um.get_last_record()
    for mol in paper.molecules:
        to_del = set()
        fs = [f for f in mol.fragments if f.type == "linear_form"]
        if fs and (last_record == None or last_record.object_changed(mol)):
            for f in fs:
                if mol.check_linear_form_fragment(f) == False:
                    to_del.add(f)
        for f in to_del:
            Store.log(
                _('The linear form was no longer consistent - it has been removed'
                  ))
            mol.delete_fragment(f)
Example #16
0
def ask_id_for_selected( paper):
  """opens dialog for input of molecule ID and sets it"""
  top_levels, unique = paper.selected_to_unique_top_levels()
  ms = [o for o in top_levels if isinstance( o, molecule)]

  if not ms:
    tkMessageBox.showerror( _("No molecule selected."),
                            _("At least one molecule must be selected. Please select it."))
    return 

  if len( ms) > 1:
    tkMessageBox.showerror( _("Only one molecule should be selected."),
                            _("ID must be unique value, therefore it is obviously possible to set it to one molecule only. Please select only one molecule"))
    return

  m = ms[0]

  while 1:
    dial = Pmw.PromptDialog( paper,
                             title=_('Id'),
                             label_text=_('Id:'),
                             entryfield_labelpos = 'n',
                             buttons=(_('OK'),_('Cancel')))
    # put the recent value
    if m.id:
      dial.insertentry( 0, m.id)

    res = dial.activate()
    if res == _('OK'):
      id = dial.get()
    else:
      return

    collision = 0
    for mol in paper.molecules:
      if mol != m and mol.id == id:
        tkMessageBox.showerror( _("ID collision"),
                                _("This ID is already used, use a different one"))
        collision = 1
        break
    if not collision:
      break

  m.id = id
  Store.log( _('ID %s was set to molecule') % id)
  paper.start_new_undo_record()
Example #17
0
def ask_display_form_for_selected( paper):
  top_levels, unique = paper.selected_to_unique_top_levels()
  ms = [o for o in top_levels if isinstance( o, molecule)]

  if not ms:
    tkMessageBox.showerror( _("No molecule selected."),
                            _("At least one molecule must be selected. Please select it."))
    return

  dial = Pmw.Dialog( paper,
                     title=_('Display Form'),
                     #defaultbutton = _('OK'),
                     buttons=(_('OK'),_('Cancel')))
  input = widgets.HTMLLikeInput( dial.interior())
  input.pack()
  input.editPool.focus_set()
                                 
  # if only one mol is selected use it as default
  if len( ms) == 1 and ms[0].display_form:
    input.text = ms[0].display_form
  res = dial.activate()
  if res == _('OK'):
    df = input.editPool.get()
    df = unicode( df).encode( 'utf-8')
    ## catch not well-formed text
    try:
      xml.sax.parseString( "<a>%s</a>" % df, xml.sax.ContentHandler())
    except xml.sax.SAXParseException:
      df = xml.sax.saxutils.escape( df)
      # the second round of try: except: should catch problems not
      # related to XML wellfomedness but rather to encoding
      try:
        xml.sax.parseString( "<a>%s</a>" % df, xml.sax.ContentHandler())
      except xml.sax.SAXParseException:        
        tkMessageBox.showerror( _("Parse Error"), _("Unable to parse the text-\nprobably problem with input encoding!"))
        Store.app.paper.bell()
        return
  else:
    return

  for m in ms:
    m.display_form = df
  Store.log( _('Display form %s was set to molecule(s)') % df)
  paper.start_new_undo_record()
Example #18
0
def compute_oxidation_number( paper):
  v = validator.validator()
  v.validate( paper.selected_atoms)
  logged = False
  if v.report.group_atoms:
    Store.log( _("Groups must be expanded to compute oxidation number for them."), message_type="hint")
    logged = True
  # we have to check if the neighbors of the atoms we are processing are not groups or so...
  ns = list( reduce( operator.or_, map(set, [a.neighbors for a in paper.selected_atoms])))
  v.validate( ns)
  if v.report.group_atoms or v.report.text_atoms:
    Store.log( _("Unexpanded groups or text-only atoms may cause incorrect computation of oxidation number."), message_type="warning")
    logged = True
    
  for a in paper.selected_atoms:
    if isinstance( a, atom):
      oxes = a.get_marks_by_type( "oxidation_number")
      if not oxes:
        a.set_mark( "oxidation_number", draw=a.drawn)
      elif a.drawn:
        oxes[0].redraw()

  paper.start_new_undo_record()
  if not logged:
    Store.log( _("You can move and delete the created oxidation numbers in the mark mode"), message_type="hint")
Example #19
0
def create_fragment_from_selected(paper):
    top_levels, unique = paper.selected_to_unique_top_levels()
    if len(top_levels) != 1:
        Store.log(
            _("The selected items must be part of exactly one molecule."),
            message_type="error")
        return

    mol = top_levels[0]
    es = [e for e in paper.selected if e in mol.edges]
    vs = [v for v in paper.selected if v in mol.vertices]

    # ask for name
    dial = Pmw.PromptDialog(paper,
                            title=_('Fragment name'),
                            label_text=_('Enter fragment name:'),
                            entryfield_labelpos='w',
                            buttons=(_('OK'), _('Cancel')),
                            defaultbutton=_('OK'))
    res = dial.activate()
    if res == _('OK'):
        if mol.create_fragment(dial.get(), es, vs):
            Store.log(_(
                "The bonds and atoms were used for creation of a new molecular fragment."
            ),
                      message_type="info")
        else:
            Store.log(_(
                "The bonds and atoms could not have been used for creation of a new molecular fragment, they are probably not defining a connected subgraph of the molecular graph."
            ),
                      message_type="warning")
Example #20
0
def compute_oxidation_number(paper):
    v = validator.validator()
    v.validate(paper.selected_atoms)
    logged = False
    if v.report.group_atoms:
        Store.log(
            _("Groups must be expanded to compute oxidation number for them."),
            message_type="hint")
        logged = True
    # we have to check if the neighbors of the atoms we are processing are not groups or so...
    ns = list(
        reduce(operator.or_,
               map(set, [a.neighbors for a in paper.selected_atoms])))
    v.validate(ns)
    if v.report.group_atoms or v.report.text_atoms:
        Store.log(_(
            "Unexpanded groups or text-only atoms may cause incorrect computation of oxidation number."
        ),
                  message_type="warning")
        logged = True

    for a in paper.selected_atoms:
        if isinstance(a, atom):
            oxes = a.get_marks_by_type("oxidation_number")
            if not oxes:
                a.set_mark("oxidation_number", draw=a.drawn)
            elif a.drawn:
                oxes[0].redraw()

    paper.start_new_undo_record()
    if not logged:
        Store.log(_(
            "You can move and delete the created oxidation numbers in the mark mode"
        ),
                  message_type="hint")
Example #21
0
def log_atom_type( vtype):
  """according to vtype tells the user how an atom text was interpreted"""
  if  vtype == "atom":
    Store.log( _("BKChem interpreted the entered text as an atom"))
  elif vtype == "group":
    Store.log( _("BKChem thinks it can interpret the entered text as a group, try to expand it to find out how it was interpreted."))
  elif vtype == "textatom":
    Store.log( _("BKChem could not interpret the entered text as anything with chemical meaning"))
Example #22
0
def log_atom_type(vtype):
    """according to vtype tells the user how an atom text was interpreted"""
    if vtype == "atom":
        Store.log(_("BKChem interpreted the entered text as an atom"))
    elif vtype == "group":
        Store.log(
            _("BKChem thinks it can interpret the entered text as a group, try to expand it to find out how it was interpreted."
              ))
    elif vtype == "textatom":
        Store.log(
            _("BKChem could not interpret the entered text as anything with chemical meaning"
              ))
Example #23
0
def create_fragment_from_selected( paper):
  top_levels, unique = paper.selected_to_unique_top_levels()
  if len( top_levels) != 1:
    Store.log( _("The selected items must be part of exactly one molecule."), message_type="error")
    return

  mol = top_levels[0]
  es = [e for e in paper.selected if e in mol.edges]
  vs = [v for v in paper.selected if v in mol.vertices]

  # ask for name
  dial = Pmw.PromptDialog( paper,
                           title=_('Fragment name'),
                           label_text=_('Enter fragment name:'),
                           entryfield_labelpos = 'w',
                           buttons=(_('OK'),_('Cancel')),
                           defaultbutton=_('OK'))
  res = dial.activate()
  if res == _('OK'):
    if mol.create_fragment( dial.get(), es, vs):
      Store.log( _("The bonds and atoms were used for creation of a new molecular fragment."), message_type="info")
    else:
      Store.log( _("The bonds and atoms could not have been used for creation of a new molecular fragment, they are probably not defining a connected subgraph of the molecular graph."), message_type="warning")
Example #24
0
def atoms_to_linear_fragment(mol, vs, bond_length=10):
    changes = False
    if vs and mol.defines_connected_subgraph_v(vs):
        # the selection is connected
        for v in vs:
            if len([n for n in v.neighbors if n in vs]) > 2:
                Store.log(_(
                    "The selection is not linear - there are some splittings."
                ),
                          message_type="error")
                return
        # ok, we are clear
        # here comes the code to do the work
        # we start from the end atom that is more on the left side
        changes = True
        ends = [v for v in vs if len([n for n in v.neighbors if n in vs]) == 1]
        if not ends and len(vs) != 1:
            # whole ring is selected, how could this be possibly linearized?
            raise excs.bkchem_graph_error(
                "circular_selection",
                _("The selected part of a molecule is a whole ring, there is no way to linearize it"
                  ))
        if len(vs) == 1:
            start = list(vs)[0]
            end = start
        else:
            start = ends[0].x > ends[1].x and ends[1] or ends[0]
            end = start == ends[0] and ends[1] or ends[0]
        current = start
        x = current.x
        y = current.y
        processed = set()
        current_e = None
        while 1:
            processed.add(current)
            current.show_hydrogens = True
            current.redraw()
            if current != start:
                dx = x - current.bbox()[0] + bond_length
                dy = start.y - current.y
                # move all neighbors that are not selected with their fragments
                ps = mol.get_pieces_after_edge_removal(current_e)
                if len(ps) == 2:
                    p = current in ps[0] and ps[0] or ps[1]
                    for a in p:
                        a.move(dx, dy)
                else:
                    # we are in a ring - move only current
                    current.move(dx, dy)
            x = current.bbox()[2]
            if current != end:
                new = [
                    n for n in current.neighbors
                    if n in vs and n not in processed
                ][0]
                current_e = new.get_edge_leading_to(current)
                current = new
            else:
                break

        mol.redraw()
        return changes

    else:
        raise ValueError, "the vertices do not define connected subgraph"
from __future__ import print_function

import math
import operator

from singleton_store import Store



bs = [b for b in App.paper.selected if b.object_type == "bond"]
if not len(bs) == 2:
    Store.log(_("You have to have 2 bonds selected"), message_type="hint")
else:
    b1, b2 = bs
    center = set(b1.vertices) & set(b2.vertices)
    if center:
        a11 = center.pop()
        a12 = b1.atom1 == a11 and b1.atom2 or b1.atom1
        a22 = b2.atom1 == a11 and b2.atom2 or b2.atom1
        v1 = (a12.x - a11.x, a12.y - a11.y, a12.z - a11.z)
        v2 = (a22.x - a11.x, a22.y - a11.y, a22.z - a11.z)
        print(v1, v2)
    else:
        v1 = (b1.atom1.x - b1.atom2.x, b1.atom1.y - b1.atom2.y, b1.atom1.z - b1.atom2.z)
        v2 = (b2.atom1.x - b2.atom2.x, b2.atom1.y - b2.atom2.y, b2.atom1.z - b2.atom2.z)

    dot = sum(map(operator.mul, v1, v2))
    dv1 = math.sqrt(sum(x**2 for x in v1))
    dv2 = math.sqrt(sum(x**2 for x in v2))

    cos_a = dot / dv1 / dv2