def aromatizeRing(self, ring, center_x, center_y): ''' render a ring that is aromatic and is a regular polygon ''' # first, set all bonds to aromatic ringbonds = list(ring.iterateBonds()) for tkbond in ringbonds: bond = self._getBond(tkbond) bond.bond_type = 'aromatic' # any bond can serve as the anchor for the circle, # so we'll just use the last one from the loop atom = bond.end_atom outer_r, angle = compare_positions(atom.x, atom.y, center_x, center_y) # angle is based on raw coordinates - adjust for user-set rotation angle += self.options['rotate'] # outer_r calculated from raw coordinates, must be adjusted # for bond scaling that may have taken place outer_r *= self.bond_scale alpha = ( math.pi / 2 - math.pi / len(ringbonds) ) inner_r = math.sin(alpha) * outer_r arb = AromaticRingBond(self.options, bond, angle, outer_r, inner_r) bond.descendants.append(arb)
def annotateRing(self, ring, is_aromatic): ''' determine center, symmetry and aromatic character of ring I wonder if indigo would tell us directly about these ... annotate double bonds in rings, or alternatively decorate ring with aromatic circle. ''' atoms = set() bond_lengths = [] bonds = [] for tkbond in ring.iterateBonds(): bond = self._getBond(tkbond) bonds.append(bond) atoms.add(self.atoms[bond.start_atom.idx]) atoms.add(self.atoms[bond.end_atom.idx]) bond_lengths.append(bond.length) if len(bonds) > 8: # large rings may foul things up, so we skip them. return bl_max = max(bond_lengths) bl_spread = (bl_max - min(bond_lengths)) / bl_max # determine ring center center_x = sum([atom.x for atom in atoms]) / len(atoms) center_y = sum([atom.y for atom in atoms]) / len(atoms) # compare distances from center. Also remember atoms and bond # angles; if the ring ends up being aromatized, we flag those # angles as occupied (by the fancy circle inside the ring). atom_angles = [] center_distances = [] for atom in atoms: length, angle = compare_positions(atom.x, atom.y, center_x, center_y) center_distances.append(length) atom_angles.append((atom, angle)) cd_max = max(center_distances) cd_spread = (cd_max - min(center_distances)) / cd_max tolerance = 0.05 is_symmetric = (cd_spread <= tolerance and bl_spread <= tolerance) if is_aromatic and is_symmetric and self.options['aromatic_circles']: # ring meets all requirements to be displayed with circle inside self.aromatizeRing(ring, center_x, center_y) # flag bond angles as occupied for atom, angle in atom_angles: atom.bond_angles.append(angle) else: # flag orientation individual bonds - will influence # rendering of double bonds for bond in bonds: bond.is_clockwise(center_x, center_y)