def find_place( self, a, distance, added_order=1): """tries to find accurate place for next atom around atom 'id', returns x,y and list of ids of 'items' found there for overlap, those atoms are not bound to id""" ids_bonds = a.neighbors if len( ids_bonds) == 0: x = a.x + cos( pi/6) *distance y = a.y - sin( pi/6) *distance elif len( ids_bonds) == 1: neigh = ids_bonds[0] if a.neighbor_edges[0].order != 3 and added_order != 3: # we add a normal bond to atom with one normal bond if a == self._last_used_atom or len( neigh.neighbors) != 2: # the user has either deleted the last added bond and wants it to be on the other side # or it is simply impossible to define a transoid configuration self.sign = -self.sign x = a.x + cos( self.get_angle( a, ids_bonds[0]) +self.sign*2*pi/3) *distance y = a.y + sin( self.get_angle( a, ids_bonds[0]) +self.sign*2*pi/3) *distance else: # we would add the new bond transoid neighs2 = neigh.neighbors neigh2 = (neighs2[0] == a) and neighs2[1] or neighs2[0] x = a.x + cos( self.get_angle( a, neigh) +self.sign*2*pi/3) *distance y = a.y + sin( self.get_angle( a, neigh) +self.sign*2*pi/3) *distance side = geometry.on_which_side_is_point( (neigh.x,neigh.y,a.x,a.y), (x,y)) if side == geometry.on_which_side_is_point( (neigh.x,neigh.y,a.x,a.y), (neigh2.x,neigh2.y)): self.sign = -self.sign x = a.x + cos( self.get_angle( a, neigh) +self.sign*2*pi/3) *distance y = a.y + sin( self.get_angle( a, neigh) +self.sign*2*pi/3) *distance self._last_used_atom = a else: x = a.x + cos( self.get_angle( a, ids_bonds[0]) + pi) *distance y = a.y + sin( self.get_angle( a, ids_bonds[0]) + pi) *distance else: x, y = self.find_least_crowded_place_around_atom( a, range=distance) return x, y
def _draw_second_line( self, coords): my_x1, my_y1 = self.atom1.get_xy() my_x2, my_y2 = self.atom2.get_xy() my_coords = (my_x1,my_y1,my_x2,my_y2) x, y, x0, y0 = coords # shortening of the second bond dx = x-x0 dy = y-y0 if self.center: _k = 0 else: _k = (1-self.double_length_ratio)/2 x, y, x0, y0 = x-_k*dx, y-_k*dy, x0+_k*dx, y0+_k*dy # shift according to the bonds arround side = geometry.on_which_side_is_point( my_coords, (x,y)) for atom in (self.atom1,self.atom2): second_atom = atom is self.atom1 and self.atom2 or self.atom1 neighs = [n for n in atom.neighbors if geometry.on_which_side_is_point( my_coords, n.get_xy())==side and n is not second_atom] for n in neighs: dist2 = _k*geometry.point_distance(*my_coords)*geometry.on_which_side_is_point((atom.x, atom.y, n.x, n.y), (second_atom.x, second_atom.y)) xn1, yn1, xn2, yn2 = geometry.find_parallel( atom.x, atom.y, n.x, n.y, dist2) xp,yp,parallel,online = geometry.intersection_of_two_lines( x,y,x0,y0,xn1,yn1,xn2,yn2) if not parallel: if not geometry.is_point_beween_points_of_line( (x,y,x0,y0),(xp,yp)): # only shorten the line - do not elongate it continue if geometry.point_distance( atom.x,atom.y,x,y) < geometry.point_distance( atom.x,atom.y,x0,y0): x,y = xp, yp else: x0,y0 = xp, yp else: # parallel pass return [self._create_line_with_transform( (x, y, x0, y0), width=self.line_width, fill=self.line_color)]
def find_place(self, a, distance, added_order=1): """tries to find accurate place for next atom around atom 'id', returns x,y and list of ids of 'items' found there for overlap, those atoms are not bound to id""" ids_bonds = a.neighbors if len(ids_bonds) == 0: x = a.x + cos(pi / 6) * distance y = a.y - sin(pi / 6) * distance elif len(ids_bonds) == 1: neigh = ids_bonds[0] if a.neighbor_edges[0].order != 3 and added_order != 3: # we add a normal bond to atom with one normal bond if a == self._last_used_atom or len(neigh.neighbors) != 2: # the user has either deleted the last added bond and wants it to be on the other side # or it is simply impossible to define a transoid configuration self.sign = -self.sign x = a.x + cos( self.get_angle(a, ids_bonds[0]) + self.sign * 2 * pi / 3) * distance y = a.y + sin( self.get_angle(a, ids_bonds[0]) + self.sign * 2 * pi / 3) * distance else: # we would add the new bond transoid neighs2 = neigh.neighbors neigh2 = (neighs2[0] == a) and neighs2[1] or neighs2[0] x = a.x + cos( self.get_angle(a, neigh) + self.sign * 2 * pi / 3) * distance y = a.y + sin( self.get_angle(a, neigh) + self.sign * 2 * pi / 3) * distance side = geometry.on_which_side_is_point( (neigh.x, neigh.y, a.x, a.y), (x, y)) if side == geometry.on_which_side_is_point( (neigh.x, neigh.y, a.x, a.y), (neigh2.x, neigh2.y)): self.sign = -self.sign x = a.x + cos( self.get_angle(a, neigh) + self.sign * 2 * pi / 3) * distance y = a.y + sin( self.get_angle(a, neigh) + self.sign * 2 * pi / 3) * distance self._last_used_atom = a else: x = a.x + cos(self.get_angle(a, ids_bonds[0]) + pi) * distance y = a.y + sin(self.get_angle(a, ids_bonds[0]) + pi) * distance else: x, y = self.find_least_crowded_place_around_atom(a, range=distance) return x, y
def mark_template_bond( self, b): if b in self.edges: atms = b.atom1.neighbors + b.atom2.neighbors atms = misc.difference( atms, [b.atom1, b.atom2]) coords = [a.get_xy() for a in atms] line = b.atom1.get_xy() + b.atom2.get_xy() if sum(geometry.on_which_side_is_point(line, xy) for xy in coords) > 0: self.t_bond_first = b.atom1 self.t_bond_second = b.atom2 else: self.t_bond_first = b.atom2 self.t_bond_second = b.atom1 else: raise ValueError("Submitted bond does not belong to this molecule.")
def mark_template_bond(self, b): if b in self.edges: atms = b.atom1.neighbors + b.atom2.neighbors atms = misc.difference(atms, [b.atom1, b.atom2]) coords = [a.get_xy() for a in atms] line = b.atom1.get_xy() + b.atom2.get_xy() if sum(geometry.on_which_side_is_point(line, xy) for xy in coords) > 0: self.t_bond_first = b.atom1 self.t_bond_second = b.atom2 else: self.t_bond_first = b.atom2 self.t_bond_second = b.atom1 else: raise ValueError( "Submitted bond does not belong to this molecule.")
def transform( self, tr): if not self.item: return for i in [self.item] + self.second + self.third + self.items: coords = self.paper.coords( i) tr_coords = tr.transform_xy_flat_list( coords) self.paper.coords( i, tuple( tr_coords)) if self.selector: self.unselect() self.select() # we need to check if the sign of double bond width has not changed # this happens during 3d rotation if self.order == 2 and not self.center: line = list( self.atom1.get_xy()) line += self.atom2.get_xy() x, y = self.paper.coords( self.second[0])[0:2] sign = geometry.on_which_side_is_point( line, (x,y)) if sign * self.bond_width < 0: self.bond_width *= -1
def _compute_sign_and_center( self): """returns tuple of (sign, center) where sign is the default sign of the self.bond_width""" # check if we need to transform 3D before computation transform = None for n in self.atom1.neighbors + self.atom2.neighbors: # self.atom1 and self.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() break if transform: for n in self.atom1.neighbors + self.atom2.neighbors: n.transform( transform) # /end of check line = self.atom1.get_xy() + self.atom2.get_xy() atms = self.atom1.neighbors + self.atom2.neighbors atms = misc.difference( atms, [self.atom1, self.atom2]) coords = [a.get_xy() for a in atms] # searching for circles circles = 0 for ring in self.molecule.get_smallest_independent_cycles_dangerous_and_cached(): if self.atom1 in ring and self.atom2 in ring: on_which_side = lambda xy: geometry.on_which_side_is_point( line, xy) circles += sum(map(on_which_side, [a.get_xy() for a in ring if a not in self.atoms])) if circles: side = circles else: sides = [geometry.on_which_side_is_point( line, xy, threshold=0.1) for xy in coords] side = sum(sides) # on which side to put the second line if side == 0 and (len(self.atom1.neighbors) == 1 or len(self.atom2.neighbors) == 1): # maybe we should center, but this is usefull only when one of the atoms has no other substitution ret = (1 ,1) else: ret = None if not circles: # we center when both atoms have visible symbol and are not in circle if self.atom1.show and self.atom2.show: ret = (1, 1) # recompute side with weighting of atom types else: for i in range( len( sides)): if sides[i] and atms[i].__class__.__name__ == "atom": if atms[i].symbol == 'H': sides[i] *= 0.1 # this discriminates H elif atms[i].symbol != 'C': sides[i] *= 0.2 # this makes "non C" less then C but more then H side = sum(sides) if not ret: if side < 0: ret = (-1, 0) else: ret = (1, 0) # transform back if necessary if transform: inv = transform.get_inverse() for n in self.atom1.neighbors + self.atom2.neighbors: n.transform( inv) # /end of back transform return ret
def _compute_sign_and_center( self): """returns tuple of (sign, center) where sign is the default sign of the self.bond_width""" # check if we need to transform 3D before computation transform = None for n in self.atom1.neighbors + self.atom2.neighbors: # self.atom1 and self.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() break if transform: for n in self.atom1.neighbors + self.atom2.neighbors: n.transform( transform) # /end of check line = self.atom1.get_xy() + self.atom2.get_xy() atms = self.atom1.neighbors + self.atom2.neighbors atms = misc.difference( atms, [self.atom1, self.atom2]) coords = [a.get_xy() for a in atms] # searching for circles circles = 0 for ring in self.molecule.get_smallest_independent_cycles_dangerous_and_cached(): if self.atom1 in ring and self.atom2 in ring: on_which_side = lambda xy: geometry.on_which_side_is_point( line, xy) circles += sum(map(on_which_side, [a.get_xy() for a in ring if a not in self.atoms])) if circles: side = circles else: sides = [geometry.on_which_side_is_point( line, xy, threshold=0.1) for xy in coords] side = sum(sides) # on which side to put the second line if side == 0 and (len(self.atom1.neighbors) == 1 or len(self.atom2.neighbors) == 1): # maybe we should center, but this is usefull only when one of the atoms has no other substitution ret = (1 ,1) else: ret = None if not circles: # we center when both atoms have visible symbol and are not in circle if self.atom1.show and self.atom2.show: ret = (1, 1) # recompute side with weighting of atom types else: for i in range( len( sides)): if sides[i] and atms[i].__class__.__name__ == "atom": if atms[i].symbol == 'H': sides[i] *= 0.1 # this discriminates H elif atms[i].symbol != 'C': sides[i] *= 0.2 # this makes "non C" less then C but more then H side = sum(sides) if ret is None: if side < 0: ret = (-1, 0) else: ret = (1, 0) # transform back if necessary if transform: inv = transform.get_inverse() for n in self.atom1.neighbors + self.atom2.neighbors: n.transform( inv) # /end of back transform return ret