def _find_place_around_atom( self, atom): x, y = atom.x, atom.y coords = [(a.x,a.y) for a in atom.neighbors] # now we can compare the angles angles = [geometry.clockwise_angle_from_east( x1-x, y1-y) for x1,y1 in coords] angles.append( 2*math.pi + min( angles)) angles.sort() angles.reverse() diffs = misc.list_difference( angles) i = diffs.index( max( diffs)) angle = (angles[i] +angles[i+1]) / 2 return angle
def _find_place_around_atom(self, atom): x, y = atom.x, atom.y coords = [(a.x, a.y) for a in atom.neighbors] # now we can compare the angles angles = [ geometry.clockwise_angle_from_east(x1 - x, y1 - y) for x1, y1 in coords ] angles.append(2 * math.pi + min(angles)) angles.sort() angles.reverse() diffs = misc.list_difference(angles) i = diffs.index(max(diffs)) angle = (angles[i] + angles[i + 1]) / 2 return angle
def find_least_crowded_place_around_atom( self, a, range=10): atms = a.neighbors x, y = a.get_xy() if not atms: # single atom molecule if a.show_hydrogens and a.pos == "center-first": return x -range, y else: return x +range, y angles = [geometry.clockwise_angle_from_east( at.x-a.x, at.y-a.y) for at in atms] angles.append( 2*pi + min( angles)) angles = sorted(angles, reverse=True) diffs = misc.list_difference( angles) i = diffs.index( max( diffs)) angle = (angles[i] +angles[i+1]) / 2 return x +range*cos( angle), y +range*sin( angle)
def find_least_crowded_place_around_atom(self, a, range=10): atms = a.neighbors x, y = a.get_xy() if not atms: # single atom molecule if a.show_hydrogens and a.pos == "center-first": return x - range, y else: return x + range, y angles = [ geometry.clockwise_angle_from_east(at.x - a.x, at.y - a.y) for at in atms ] angles.append(2 * pi + min(angles)) angles = sorted(angles, reverse=True) diffs = misc.list_difference(angles) i = diffs.index(max(diffs)) angle = (angles[i] + angles[i + 1]) / 2 return x + range * cos(angle), y + range * sin(angle)
def find_place_for_mark( self, mark, resolution=30): """resolution says if the angles should be somehow 'rounded', it is given in degrees; see geometry.point_on_circle for a similar thing""" mark_name, mark_class = self._mark_to_name_and_class( mark) # deal with marks centered if mark_class.meta__mark_positioning == 'atom': return self.x, self.y # deal with statically positioned marks if mark_class.meta__mark_positioning == 'righttop': bbox = self.bbox() return bbox[2]+2, bbox[1] # deal with marks in linear_form if self.is_part_of_linear_fragment(): if mark_name == "atom_number": bbox = self.bbox() return int( self.x-0.5*self.font_size), bbox[1]-2 if not self.show: dist = 5 + round( mark_class.standard_size / 2) else: dist = 0.75*self.font_size + round( mark_class.standard_size / 2) atms = self.get_neighbors() x, y = self.get_xy() # special cases if not atms: # single atom molecule if self.show_hydrogens and self.pos == "center-first": return x -dist, y-3 else: return x +dist, y-3 # normal case coords = [(a.x,a.y) for a in atms] # we have to take marks into account [coords.append( (m.x, m.y)) for m in self.marks] # hydrogen positioning is also important if self.show_hydrogens and self.show: if self.pos == 'center-last': coords.append( (x-10,y)) else: coords.append( (x+10,y)) # now we can compare the angles angles = [geometry.clockwise_angle_from_east( x1-x, y1-y) for x1,y1 in coords] angles.append( 2*pi + min( angles)) angles.sort() angles.reverse() diffs = misc.list_difference( angles) i = diffs.index( max( diffs)) angle = (angles[i] +angles[i+1]) / 2 # we calculate the distance here again as it is anisotropic (depends on direction) bbox = list( misc.normalize_coords( self.bbox())) x0, y0 = geometry.point_on_circle( x, y, 500, direction=(cos(angle), sin( angle)), resolution=resolution) x1, y1 = geometry.intersection_of_line_and_rect( (x,y,x0,y0), bbox, round_edges=0) dist = geometry.point_distance( x, y, x1, y1) + round( mark_class.standard_size / 2) # // retx, rety = geometry.point_on_circle( x, y, dist, direction=(cos(angle), sin( angle)), resolution=resolution) # in visible text x,y are not on the center, therefore we compensate for it # if self.show: # y -= 0.166 * self.font_size return retx, rety
def process_atom_neigbors( self, v): 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 to_go = [a for a in v.get_neighbors() if a.x == None or a.y == None] done = [a for a in v.get_neighbors() if a not in to_go] if len( done) == 1 and (len( to_go) == 1 or len( to_go) == 2 and [1 for _t in to_go if _t in self.stereo]): # only simple non-branched chain or branched with stereo d = done[0] if len( to_go) == 1: t = to_go[0] else: t = [_t for _t in to_go if _t in self.stereo][0] # decide angle angle_to_add = 120 bond = v.get_edge_leading_to( t) # triple bonds if 3 in [_e.order for _e in v.get_neighbor_edges()]: angle_to_add = 180 # cumulated double bonds if bond.order == 2: _b = v.get_edge_leading_to( d) if _b.order == 2: angle_to_add = 180 angle = geometry.clockwise_angle_from_east( d.x-v.x, d.y-v.y) dns = d.get_neighbors() placed = False # stereochemistry (E/Z) if t in self.stereo: ss = [st for st in self.stereo[t] if not None in st.get_other_end( t).coords[:2]] if ss: st = ss[0] # we choose the first one if more are present d2 = st.get_other_end( t) # other is processed, we need to adapt relation = st.value == st.OPPOSITE_SIDE and -1 or 1 angle_to_add = get_angle_at_side( v, d, d2, relation, angle_to_add) placed = True if not placed and len( dns) == 2: # to support the all trans of simple chains without stereochemistry d2 = (dns[0] == v) and dns[1] or dns[0] if d2.x != None and d2.y != None: angle_to_add = get_angle_at_side( v, d, d2, -1, angle_to_add) an = angle + deg_to_rad( angle_to_add) t.x = v.x + self.bond_length*cos( an) t.y = v.y + self.bond_length*sin( an) if len( to_go) > 1: self.process_atom_neigbors( v) else: # branched chain angles = [geometry.clockwise_angle_from_east( at.x-v.x, at.y-v.y) for at in done] angles.append( 2*pi + min( angles)) angles.sort() angles.reverse() diffs = misc.list_difference( angles) i = diffs.index( max( diffs)) angle = (angles[i] -angles[i+1]) / (len( to_go)+1) gcoords = gen_coords_from_stream( gen_angle_stream( angle, start_from=angles[i+1]+angle), length = self.bond_length) for a in to_go: dx, dy = gcoords.next() a.x = v.x + dx a.y = v.y + dy return to_go
def find_place_for_mark(self, mark, resolution=30): """resolution says if the angles should be somehow 'rounded', it is given in degrees; see geometry.point_on_circle for a similar thing""" mark_name, mark_class = self._mark_to_name_and_class(mark) # deal with marks centered if mark_class.meta__mark_positioning == 'atom': return self.x, self.y # deal with statically positioned marks if mark_class.meta__mark_positioning == 'righttop': bbox = self.bbox() return bbox[2] + 2, bbox[1] # deal with marks in linear_form if self.is_part_of_linear_fragment(): if mark_name == "atom_number": bbox = self.bbox() return int(self.x - 0.5 * self.font_size), bbox[1] - 2 if not self.show: dist = 5 + round(mark_class.standard_size / 2) else: dist = 0.75 * self.font_size + round(mark_class.standard_size / 2) atms = self.get_neighbors() x, y = self.get_xy() # special cases if not atms: # single atom molecule if self.show_hydrogens and self.pos == "center-first": return x - dist, y - 3 else: return x + dist, y - 3 # normal case coords = [(a.x, a.y) for a in atms] # we have to take marks into account [coords.append((m.x, m.y)) for m in self.marks] # hydrogen positioning is also important if self.show_hydrogens and self.show: if self.pos == 'center-last': coords.append((x - 10, y)) else: coords.append((x + 10, y)) # now we can compare the angles angles = [ geometry.clockwise_angle_from_east(x1 - x, y1 - y) for x1, y1 in coords ] angles.append(2 * pi + min(angles)) angles.sort() angles.reverse() diffs = misc.list_difference(angles) i = diffs.index(max(diffs)) angle = (angles[i] + angles[i + 1]) / 2 # we calculate the distance here again as it is anisotropic (depends on direction) bbox = list(misc.normalize_coords(self.bbox())) x0, y0 = geometry.point_on_circle(x, y, 500, direction=(cos(angle), sin(angle)), resolution=resolution) x1, y1 = geometry.intersection_of_line_and_rect((x, y, x0, y0), bbox, round_edges=0) dist = geometry.point_distance(x, y, x1, y1) + round( mark_class.standard_size / 2) # // retx, rety = geometry.point_on_circle(x, y, dist, direction=(cos(angle), sin(angle)), resolution=resolution) # in visible text x,y are not on the center, therefore we compensate for it # if self.show: # y -= 0.166 * self.font_size return retx, rety