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 create_mark( self, mark='radical', angle='auto', draw=1, angle_resolution=1): """creates the mark, does not care about the chemical meaning of this""" # decide where to put the mark mark_name, mark_class = self._mark_to_name_and_class( mark) if angle == 'auto': x, y = self.find_place_for_mark( mark, resolution=angle_resolution) else: if not self.show: dist = 5 + round( mark_class.standard_size / 2) else: bbox = self.bbox() x2 = self.x + round( cos( angle) *1000) y2 = self.y + round( sin( angle) *1000) x1, y1 = geometry.intersection_of_line_and_rect( (self.x,self.y,x2,y2), bbox, round_edges=0) dist = geometry.point_distance( self.x, self.y, x1, y1) + round( mark_class.standard_size / 2) x = self.x + round( cos( angle) *dist) y = self.y + round( sin( angle) *dist) #ang = angle m = mark_class( self, x, y, auto=(angle=='auto')) if draw: m.draw() self.marks.add( m) return m
def create_mark(self, mark='radical', angle='auto', draw=1, angle_resolution=1): """creates the mark, does not care about the chemical meaning of this""" # decide where to put the mark mark_name, mark_class = self._mark_to_name_and_class(mark) if angle == 'auto': x, y = self.find_place_for_mark(mark, resolution=angle_resolution) else: if not self.show: dist = 5 + round(mark_class.standard_size / 2) else: bbox = self.bbox() x2 = self.x + round(cos(angle) * 1000) y2 = self.y + round(sin(angle) * 1000) x1, y1 = geometry.intersection_of_line_and_rect( (self.x, self.y, x2, y2), bbox, round_edges=0) dist = geometry.point_distance(self.x, self.y, x1, y1) + round( mark_class.standard_size / 2) x = self.x + round(cos(angle) * dist) y = self.y + round(sin(angle) * dist) #ang = angle m = mark_class(self, x, y, auto=(angle == 'auto')) if draw: m.draw() self.marks.add(m) return m
def _where_to_draw_from_and_to( self): x1, y1 = self.atom1.get_xy() x2, y2 = self.atom2.get_xy() # at first check if the bboxes are not overlapping bbox1 = list( misc.normalize_coords( self.atom1.bbox( substract_font_descent=True))) bbox2 = list( misc.normalize_coords( self.atom2.bbox( substract_font_descent=True))) if geometry.do_rectangles_intersect( bbox1, bbox2): return None # then we continue with computation if self.atom1.show: x1, y1 = geometry.intersection_of_line_and_rect( (x1,y1,x2,y2), bbox1, round_edges=0) if self.atom2.show: x2, y2 = geometry.intersection_of_line_and_rect( (x1,y1,x2,y2), bbox2, round_edges=0) if geometry.point_distance( x1, y1, x2, y2) <= 1.0: return None else: return (x1, y1, x2, y2)
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 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