def get_parallel_signum( l1, l2): x1a, y1a, x2a, y2a = l1 x1b, y1b, x2b, y2b = l2 if x1a == x2a: return signum( -x1a + x1b) elif y1a == y2a: return signum( -y1a+y1b) else: return signum( -x2a+x2b)
def get_parallel_signum(l1, l2): x1a, y1a, x2a, y2a = l1 x1b, y1b, x2b, y2b = l2 if x1a == x2a: return signum(-x1a + x1b) elif y1a == y2a: return signum(-y1a + y1b) else: return signum(-x2a + x2b)
def single_sided_arrow_head (x1,y1,x2,y2,a,b,c,lw): '''last two points of arrow 1->2 a,b,c like tkinter a = leght from point 2 where the head touches the line (out point A) b = total lenght of the head (defines also help point P on the line) c = width Point B will be the outer Point of the head rl = "r" the head is on the right , = "l" left, lw is the line_width of the line the arrow will be attached to''' xa,ya = geometry.elongate_line (x1,y1,x2,y2,-a) xa,ya = geometry.point_at_distance_from_line (x1,y1,xa,ya,-misc.signum(c)*(lw-1.0)/2.0) xp,yp = geometry.elongate_line (x1,y1,x2,y2,-b) xb,yb = geometry.point_at_distance_from_line (x1,y1,xp,yp,c) xc,yc = geometry.point_at_distance_from_line (x1,y1,x2,y2,-misc.signum(c)*(lw-1.0)/2.0) return xa,ya, xc,yc, xb,yb
def resize(self, coords, fix=()): if not fix: x1, y1, x2, y2 = misc.normalize_coords(coords) dx = x2 - x1 dy = y2 - y1 d = (abs(dx) + abs(dy)) / 2 self.coords = (x1, y1, x1 + d, y1 + d) else: x1, y1, x2, y2 = coords dx = (fix[0] - x1) or (fix[0] - x2) dy = (fix[1] - y2) or (fix[1] - y1) d = (abs(dx) + abs(dy)) / 2 self.coords = misc.normalize_coords( (fix[0], fix[1], x1 - (d * misc.signum(dx) or d), y1 - (d * misc.signum(dy) or d))) self.paper.coords(self.item, self.coords)
def _valency_to_charge( self, v, charge): """sets charge of the atom so that it has minimal free_valency, returns the 'unused' charge in case it is not comsumed whole""" ch = misc.signum( charge) * min( abs( charge), v.free_valency) if charge < 0: charge += ch v.charge = -ch else: charge -= ch v.charge = ch return charge
def _valency_to_charge(self, v, charge): """sets charge of the atom so that it has minimal free_valency, returns the 'unused' charge in case it is not comsumed whole""" ch = misc.signum(charge) * min(abs(charge), v.free_valency) if charge < 0: charge += ch v.charge = -ch else: charge -= ch v.charge = ch return charge
def _draw_edge(self, e): v1, v2 = e.vertices start = self.transformer.transform_xy(v1.x, v1.y) end = self.transformer.transform_xy(v2.x, v2.y) parent = self._create_parent(e, self.top) if e.order == 1: self._draw_line(parent, start, end, line_width=self.line_width) if e.order == 2: side = 0 # find how to center the bonds # rings have higher priority in setting the positioning for ring in self.molecule.get_smallest_independent_cycles(): if v1 in ring and v2 in ring: side += reduce(operator.add, [ geometry.on_which_side_is_point( start + end, (self.transformer.transform_xy(a.x, a.y))) for a in ring if a != v1 and a != v2 ]) # if rings did not decide, use the other neigbors if not side: for v in v1.neighbors + v2.neighbors: if v != v1 and v != v2: side += geometry.on_which_side_is_point( start + end, (self.transformer.transform_xy(v.x, v.y))) if side: self._draw_line(parent, start, end, line_width=self.line_width) x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], self.bond_width * misc.signum(side)) self._draw_line(parent, (x1, y1), (x2, y2), line_width=self.line_width) else: for i in (1, -1): x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i * self.bond_width * 0.5) self._draw_line(parent, (x1, y1), (x2, y2), line_width=self.line_width) elif e.order == 3: self._draw_line(parent, start, end, line_width=self.line_width) for i in (1, -1): x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i * self.bond_width * 0.7) self._draw_line(parent, (x1, y1), (x2, y2), line_width=self.line_width)
def read_standard_values( self, standard, old_standard=None): meta_enabled.read_standard_values( self, standard, old_standard=old_standard) # wedge width if not old_standard or (standard.wedge_width != old_standard.wedge_width): self.wedge_width = Screen.any_to_px( standard.wedge_width) # line width if not old_standard or (standard.line_width != old_standard.line_width): self.line_width = Screen.any_to_px( standard.line_width) # bond width if not old_standard or (standard.bond_width != old_standard.bond_width): if hasattr( self, 'bond_width'): self.bond_width = misc.signum( self.bond_width) * Screen.any_to_px( standard.bond_width) else: self.bond_width = Screen.any_to_px( standard.bond_width)
def resize( self, coords, fix=()): if not fix: x1, y1, x2, y2 = misc.normalize_coords( coords) dx = x2 - x1 dy = y2 - y1 d = (abs( dx) + abs( dy))/2 self.coords = (x1, y1, x1+d, y1+d) else: x1, y1, x2, y2 = coords dx = (fix[0] - x1) or (fix[0] - x2) dy = (fix[1] - y2) or (fix[1] - y1) d = (abs( dx) + abs( dy))/2 self.coords = misc.normalize_coords( (fix[0], fix[1], x1-(d*misc.signum( dx) or d), y1-( d*misc.signum( dy) or d))) self.paper.coords( self.item, self.coords)
def _draw_edge( self, e): def draw_plain_or_colored_line( _start, _end, second=False): """second means if this is not the main line, drawing might be different""" if not has_shown_vertex or not self.color_bonds: if not second: self._draw_line( _start, _end, line_width=self.line_width, capstyle=cairo.LINE_CAP_ROUND) else: self._draw_line( _start, _end, line_width=self.line_width, capstyle=cairo.LINE_CAP_BUTT) else: self._draw_colored_line( _start, _end, line_width=self.line_width, start_color=color1, end_color=color2) def draw_plain_or_colored_wedge( _start, _end): x1, y1 = _start x2, y2 = _end x, y, x0, y0 = geometry.find_parallel( x1, y1, x2, y2, self.wedge_width/2.0) xa, ya, xb, yb = geometry.find_parallel( x1, y1, x2, y2, self.line_width/2.0) # no coloring now if not has_shown_vertex or not self.color_bonds: self._create_cairo_path( [(xa, ya), (x0, y0), (2*x2-x0, 2*y2-y0), (2*x1-xa, 2*y1-ya)], closed=True) self.context.set_source_rgb( 0,0,0) self.context.fill() else: # ratio 0.4 looks better than 0.5 because the area difference # is percieved more than length difference ratio = 0.4 xm1 = ratio*xa + (1-ratio)*x0 ym1 = ratio*ya + (1-ratio)*y0 xm2 = (1-ratio)*(2*x2-x0) + ratio*(2*x1-xa) ym2 = (1-ratio)*(2*y2-y0) + ratio*(2*y1-ya) self.context.set_source_rgb( *color1) self._create_cairo_path( [(xa,ya), (xm1,ym1), (xm2,ym2), (2*x1-xa, 2*y1-ya)], closed=True) self.context.fill() self.context.set_source_rgb( *color2) self._create_cairo_path( [(xm1,ym1), (x0, y0), (2*x2-x0, 2*y2-y0), (xm2,ym2)], closed=True) self.context.fill() def draw_plain_or_colored_hatch( _start, _end): x1, y1 = _start x2, y2 = _end # no coloring now x, y, x0, y0 = geometry.find_parallel( x1, y1, x2, y2, self.wedge_width/2.0) xa, ya, xb, yb = geometry.find_parallel( x1, y1, x2, y2, self.line_width/2.0) d = math.sqrt( (x1-x2)**2 + (y1-y2)**2) # length of the bond if d == 0: return # to prevent division by zero dx1 = (x0 - xa)/d dy1 = (y0 - ya)/d dx2 = (2*x2 -x0 -2*x1 +xa)/d dy2 = (2*y2 -y0 -2*y1 +ya)/d # we have to decide if the first line should be at the position of the first atom draw_start = 1 # is index not boolean if not v1 in self._vertex_to_bbox and v1.occupied_valency > 1: draw_start = 1 draw_end = 1 # is added to index not boolean if not v2 in self._vertex_to_bbox and v2.occupied_valency > 1: draw_end = 0 # adjust the step length step_size = 2*(self.line_width) ns = round( d / step_size) or 1 step_size = d / ns # now we finally draw self.context.set_line_cap( cairo.LINE_CAP_BUTT) self.context.set_source_rgb( *color1) middle = 0.5 * (draw_start + int( round( d/ step_size)) + draw_end - 2) for i in range( draw_start, int( round( d/ step_size)) +draw_end): coords = [xa+dx1*i*step_size, ya+dy1*i*step_size, 2*x1-xa+dx2*i*step_size, 2*y1-ya+dy2*i*step_size] if coords[0] == coords[2] and coords[1] == coords[3]: if (dx1+dx2) > (dy1+dy2): coords[0] += 1 else: coords[1] += 1 self._create_cairo_path( [coords[:2],coords[2:]]) if i >= middle: self.context.stroke() self.context.set_source_rgb( *color2) self.context.stroke() # code itself # at first detect the need to make 3D adjustments self._transform = transform3d.transform3d() self._invtransform = transform3d.transform3d() transform = None if e.order > 1: atom1,atom2 = e.vertices for n in atom1.neighbors + atom2.neighbors: # e.atom1 and e.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( e) #transform = None break if transform: for n in atom1.neighbors + atom2.neighbors: n.coords = transform.transform_xyz( *n.coords) self._transform = transform self._invtransform = transform.get_inverse() # // end of 3D adjustments # now the code itself coords = self._where_to_draw_from_and_to( e) if coords: start = coords[:2] end = coords[2:] v1, v2 = e.vertices if self.color_bonds: color1 = self.atom_colors.get( v1.symbol, (0,0,0)) color2 = self.atom_colors.get( v2.symbol, (0,0,0)) else: color1 = color2 = (0,0,0) has_shown_vertex = bool( [1 for _v in e.vertices if _v in self._vertex_to_bbox]) if e.order == 1: if e.type == 'w': draw_plain_or_colored_wedge( start, end) elif e.type == 'h': draw_plain_or_colored_hatch( start, end) else: draw_plain_or_colored_line( start, end) if e.order == 2: side = 0 # find how to center the bonds # rings have higher priority in setting the positioning in_ring = False for ring in self.molecule.get_smallest_independent_cycles_dangerous_and_cached(): double_bonds = len( [b for b in self.molecule.vertex_subgraph_to_edge_subgraph(ring) if b.order == 2]) if v1 in ring and v2 in ring: in_ring = True side += double_bonds * reduce( operator.add, [geometry.on_which_side_is_point( start+end, (a.x,a.y)) for a in ring if a!=v1 and a!=v2]) # if rings did not decide, use the other neigbors if not side: for v in v1.neighbors + v2.neighbors: if v != v1 and v!= v2: side += geometry.on_which_side_is_point( start+end, (v.x, v.y)) # if neighbors did not decide either if not side and (in_ring or not has_shown_vertex): if in_ring: # we don't want centered bonds inside rings side = 1 # select arbitrary value else: # bond between two unshown atoms - we want to center them only in some cases if len( v1.neighbors) == 1 and len( v2.neighbors) == 1: # both atoms have only one neighbor side = 0 elif len( v1.neighbors) < 3 and len( v2.neighbors) < 3: # try to figure out which side is more towards the center of the molecule side = reduce( operator.add, [geometry.on_which_side_is_point( start+end, (a.x,a.y)) for a in self.molecule.vertices if a!=v1 and a!=v2], 0) if not side: side = 1 # we choose arbitrary value, we don't want centering if side: draw_plain_or_colored_line( start, end) x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], self.bond_width*misc.signum( side)) # shorten the second line length = geometry.point_distance( x1,y1,x2,y2) if v2 not in self._vertex_to_bbox: x2, y2 = geometry.elongate_line( x1, y1, x2, y2, -self.bond_second_line_shortening*length) if v1 not in self._vertex_to_bbox: x1, y1 = geometry.elongate_line( x2, y2, x1, y1, -self.bond_second_line_shortening*length) draw_plain_or_colored_line( (x1, y1), (x2, y2), second=True) else: for i in (1,-1): x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i*self.bond_width*0.5) draw_plain_or_colored_line( (x1, y1), (x2, y2)) elif e.order == 3: self._draw_line( start, end, line_width=self.line_width) for i in (1,-1): x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i*self.bond_width*0.7) draw_plain_or_colored_line( (x1, y1), (x2, y2), second=True) if transform: # if transform was used, we need to transform back for n in atom1.neighbors + atom2.neighbors: n.coords = self._invtransform.transform_xyz( *n.coords)
def _draw_edge( self, e): v1, v2 = e.vertices start = self.transformer.transform_xy( v1.x, v1.y) end = self.transformer.transform_xy( v2.x, v2.y) parent = self._create_parent( e, self.top) if e.order == 1: self._draw_line( parent, start, end, line_width=self.line_width) if e.order == 2: side = 0 # find how to center the bonds # rings have higher priority in setting the positioning for ring in self.molecule.get_smallest_independent_cycles(): if v1 in ring and v2 in ring: side += reduce( operator.add, [geometry.on_which_side_is_point( start+end, (self.transformer.transform_xy( a.x,a.y))) for a in ring if a!=v1 and a!=v2]) # if rings did not decide, use the other neigbors if not side: for v in v1.neighbors + v2.neighbors: if v != v1 and v!= v2: side += geometry.on_which_side_is_point( start+end, (self.transformer.transform_xy( v.x, v.y))) if side: self._draw_line( parent, start, end, line_width=self.line_width) x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], self.bond_width*misc.signum( side)) self._draw_line( parent, (x1, y1), (x2, y2), line_width=self.line_width) else: for i in (1,-1): x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i*self.bond_width*0.5) self._draw_line( parent, (x1, y1), (x2, y2), line_width=self.line_width) elif e.order == 3: self._draw_line( parent, start, end, line_width=self.line_width) for i in (1,-1): x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i*self.bond_width*0.7) self._draw_line( parent, (x1, y1), (x2, y2), line_width=self.line_width)
def done(self, button): """called on dialog exit""" self.dialog.deactivate() if button != _('OK'): pass else: #print self.marks.get() # apply changes for o in self.items: change = 0 # ATOM if o.object_type == 'atom': a = self.atom_show.index(Pmw.SELECT) if a != 2: o.show = a change = 1 # positionning a = self.atom_pos.index(Pmw.SELECT) if a != 2: o.pos = ('center-first', 'center-last')[a] change = 1 if self.atom_charge.get(): a = int(self.atom_charge.get()) if hasattr(o, 'charge') and o.charge != a: o.charge = a change = 1 # hydrogens a = int(self.atom_show_h.index(Pmw.SELECT)) if a != 2: o.show_hydrogens = a change = 1 # font is in common now # BOND elif o.object_type == 'bond': # width is in common now # bond_width d = Screen.any_to_px(self.bond_dist.getvalue()) if d: if d != abs(o.bond_width): o.bond_width = d * misc.signum(o.bond_width) change = 1 # wedge_width d = Screen.any_to_px(self.wedge_width.getvalue()) if d: if d != o.wedge_width: o.wedge_width = d change = 1 # ratio ratio = self.double_length_ratio.getvalue() if ratio: ratio = float(self.double_length_ratio.getvalue()) if ratio != o.double_length_ratio: o.double_length_ratio = ratio change = 1 # ARROW - most is in common now elif o.object_type == 'arrow': if self.arrow_start_changed: o.set_pins(start=self.arrow_start.get()) change = 1 if self.arrow_end_changed: o.set_pins(end=self.arrow_end.get()) change = 1 if self.spline_changed: o.spline = self.spline.get() change = 1 # TEXT - all is in common now # PLUS - all is in common now # VECTOR - all is in common now # COMMON PROPERTIES # LINE COLOR if hasattr(o, 'line_color') and self.line_color.color: if self.line_color.color != o.line_color: o.line_color = self.line_color.color change = 1 # AREA COLOR if hasattr(o, 'area_color') and self.area_color.color: if self.area_color.color != o.area_color: o.area_color = self.area_color.color change = 1 # LINE WIDTH if hasattr(o, 'line_width'): w = Screen.any_to_px(self.line_width.getvalue()) if w: if w != o.line_width: o.line_width = w change = 1 # FONT if hasattr(o, 'font_family'): if self.font_size.get(): a = int(self.font_size.get()) o.font_size = a change = 1 if self.font_family.getcurselection( ) and self.font_family.getcurselection( )[0] != self.used_family: a = self.font_family.getcurselection()[0] o.font_family = a change = 1 # APPLY THE CHANGES if change: o.redraw() self.changes_made = 1 self.cleanup()
def done( self, button): """called on dialog exit""" self.dialog.deactivate() if button != _('OK'): pass else: #print(self.marks.get()) # apply changes for o in self.items: change = 0 # ATOM if o.object_type == 'atom': a = self.atom_show.index( Pmw.SELECT) if a != 2: o.show = a change = 1 # positionning a = self.atom_pos.index( Pmw.SELECT) if a != 2: o.pos = ('center-first', 'center-last')[ a] change = 1 if self.atom_charge.get(): a = int( self.atom_charge.get()) if hasattr( o, 'charge') and o.charge != a: o.charge = a change = 1 # hydrogens a = int( self.atom_show_h.index( Pmw.SELECT)) if a != 2: o.show_hydrogens = a change = 1 # font is in common now # BOND elif o.object_type == 'bond': # width is in common now # bond_width d = Screen.any_to_px( self.bond_dist.getvalue()) if d: if d != abs( o.bond_width): o.bond_width = d * misc.signum( o.bond_width) change = 1 # wedge_width d = Screen.any_to_px( self.wedge_width.getvalue()) if d: if d != o.wedge_width: o.wedge_width = d change = 1 # ratio ratio = self.double_length_ratio.getvalue() if ratio: ratio = float( self.double_length_ratio.getvalue()) if ratio != o.double_length_ratio: o.double_length_ratio = ratio change = 1 # ARROW - most is in common now elif o.object_type == 'arrow': if self.arrow_start_changed: o.set_pins( start = self.arrow_start.get()) change = 1 if self.arrow_end_changed: o.set_pins( end = self.arrow_end.get()) change = 1 if self.spline_changed: o.spline = self.spline.get() change = 1 # TEXT - all is in common now # PLUS - all is in common now # VECTOR - all is in common now # COMMON PROPERTIES # LINE COLOR if hasattr( o, 'line_color') and self.line_color.color: if self.line_color.color != o.line_color: o.line_color = self.line_color.color change = 1 # AREA COLOR if hasattr( o, 'area_color') and self.area_color.color: if self.area_color.color != o.area_color: o.area_color = self.area_color.color change = 1 # LINE WIDTH if hasattr( o, 'line_width'): w = Screen.any_to_px( self.line_width.getvalue()) if w: if w != o.line_width: o.line_width = w change = 1 # FONT if hasattr( o, 'font_family'): if self.font_size.get(): a = int( self.font_size.get()) o.font_size = a change = 1 if self.font_family.getcurselection() and self.font_family.getcurselection()[0] != self.used_family: a = self.font_family.getcurselection()[0] o.font_family = a change = 1 # APPLY THE CHANGES if change: o.redraw() self.changes_made = 1 self.cleanup()
def _draw_edge(self, e): def draw_plain_or_colored_line(_start, _end, second=False): """second means if this is not the main line, drawing might be different""" if not has_shown_vertex or not self.color_bonds: if not second: self._draw_line(_start, _end, line_width=self.line_width, capstyle=cairo.LINE_CAP_ROUND) else: self._draw_line(_start, _end, line_width=self.line_width, capstyle=cairo.LINE_CAP_BUTT) else: self._draw_colored_line(_start, _end, line_width=self.line_width, start_color=color1, end_color=color2) def draw_plain_or_colored_wedge(_start, _end): x1, y1 = _start x2, y2 = _end x, y, x0, y0 = geometry.find_parallel(x1, y1, x2, y2, self.wedge_width / 2.0) xa, ya, xb, yb = geometry.find_parallel(x1, y1, x2, y2, self.line_width / 2.0) # no coloring now if not has_shown_vertex or not self.color_bonds: self._create_cairo_path([(xa, ya), (x0, y0), (2 * x2 - x0, 2 * y2 - y0), (2 * x1 - xa, 2 * y1 - ya)], closed=True) self.context.set_source_rgb(0, 0, 0) self.context.fill() else: # ratio 0.4 looks better than 0.5 because the area difference # is percieved more than length difference ratio = 0.4 xm1 = ratio * xa + (1 - ratio) * x0 ym1 = ratio * ya + (1 - ratio) * y0 xm2 = (1 - ratio) * (2 * x2 - x0) + ratio * (2 * x1 - xa) ym2 = (1 - ratio) * (2 * y2 - y0) + ratio * (2 * y1 - ya) self.context.set_source_rgb(*color1) self._create_cairo_path([(xa, ya), (xm1, ym1), (xm2, ym2), (2 * x1 - xa, 2 * y1 - ya)], closed=True) self.context.fill() self.context.set_source_rgb(*color2) self._create_cairo_path([(xm1, ym1), (x0, y0), (2 * x2 - x0, 2 * y2 - y0), (xm2, ym2)], closed=True) self.context.fill() def draw_plain_or_colored_hatch(_start, _end): x1, y1 = _start x2, y2 = _end # no coloring now x, y, x0, y0 = geometry.find_parallel(x1, y1, x2, y2, self.wedge_width / 2.0) xa, ya, xb, yb = geometry.find_parallel(x1, y1, x2, y2, self.line_width / 2.0) d = math.sqrt((x1 - x2)**2 + (y1 - y2)**2) # length of the bond if d == 0: return # to prevent division by zero dx1 = (x0 - xa) / d dy1 = (y0 - ya) / d dx2 = (2 * x2 - x0 - 2 * x1 + xa) / d dy2 = (2 * y2 - y0 - 2 * y1 + ya) / d # we have to decide if the first line should be at the position of the first atom draw_start = 1 # is index not boolean if not v1 in self._vertex_to_bbox and v1.occupied_valency > 1: draw_start = 1 draw_end = 1 # is added to index not boolean if not v2 in self._vertex_to_bbox and v2.occupied_valency > 1: draw_end = 0 # adjust the step length step_size = 2 * (self.line_width) ns = round(d / step_size) or 1 step_size = d / ns # now we finally draw self.context.set_line_cap(cairo.LINE_CAP_BUTT) self.context.set_source_rgb(*color1) middle = 0.5 * (draw_start + int(round(d / step_size)) + draw_end - 2) for i in range(draw_start, int(round(d / step_size)) + draw_end): coords = [ xa + dx1 * i * step_size, ya + dy1 * i * step_size, 2 * x1 - xa + dx2 * i * step_size, 2 * y1 - ya + dy2 * i * step_size ] if coords[0] == coords[2] and coords[1] == coords[3]: if (dx1 + dx2) > (dy1 + dy2): coords[0] += 1 else: coords[1] += 1 self._create_cairo_path([coords[:2], coords[2:]]) if i >= middle: self.context.stroke() self.context.set_source_rgb(*color2) self.context.stroke() # code itself # at first detect the need to make 3D adjustments self._transform = transform3d.transform3d() self._invtransform = transform3d.transform3d() transform = None if e.order > 1: atom1, atom2 = e.vertices for n in atom1.neighbors + atom2.neighbors: # e.atom1 and e.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(e) #transform = None break if transform: for n in atom1.neighbors + atom2.neighbors: n.coords = transform.transform_xyz(*n.coords) self._transform = transform self._invtransform = transform.get_inverse() # // end of 3D adjustments # now the code itself coords = self._where_to_draw_from_and_to(e) if coords: start = coords[:2] end = coords[2:] v1, v2 = e.vertices if self.color_bonds: color1 = self.atom_colors.get(v1.symbol, (0, 0, 0)) color2 = self.atom_colors.get(v2.symbol, (0, 0, 0)) else: color1 = color2 = (0, 0, 0) has_shown_vertex = bool( [1 for _v in e.vertices if _v in self._vertex_to_bbox]) if e.order == 1: if e.type == 'w': draw_plain_or_colored_wedge(start, end) elif e.type == 'h': draw_plain_or_colored_hatch(start, end) else: draw_plain_or_colored_line(start, end) if e.order == 2: side = 0 # find how to center the bonds # rings have higher priority in setting the positioning in_ring = False for ring in self.molecule.get_smallest_independent_cycles_dangerous_and_cached( ): double_bonds = len([ b for b in self.molecule.vertex_subgraph_to_edge_subgraph(ring) if b.order == 2 ]) if v1 in ring and v2 in ring: in_ring = True side += double_bonds * reduce(operator.add, [ geometry.on_which_side_is_point( start + end, (a.x, a.y)) for a in ring if a != v1 and a != v2 ]) # if rings did not decide, use the other neigbors if not side: for v in v1.neighbors + v2.neighbors: if v != v1 and v != v2: side += geometry.on_which_side_is_point( start + end, (v.x, v.y)) # if neighbors did not decide either if not side and (in_ring or not has_shown_vertex): if in_ring: # we don't want centered bonds inside rings side = 1 # select arbitrary value else: # bond between two unshown atoms - we want to center them only in some cases if len(v1.neighbors) == 1 and len(v2.neighbors) == 1: # both atoms have only one neighbor side = 0 elif len(v1.neighbors) < 3 and len(v2.neighbors) < 3: # try to figure out which side is more towards the center of the molecule side = reduce(operator.add, [ geometry.on_which_side_is_point( start + end, (a.x, a.y)) for a in self.molecule.vertices if a != v1 and a != v2 ], 0) if not side: side = 1 # we choose arbitrary value, we don't want centering if side: draw_plain_or_colored_line(start, end) x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], self.bond_width * misc.signum(side)) # shorten the second line length = geometry.point_distance(x1, y1, x2, y2) if v2 not in self._vertex_to_bbox: x2, y2 = geometry.elongate_line( x1, y1, x2, y2, -self.bond_second_line_shortening * length) if v1 not in self._vertex_to_bbox: x1, y1 = geometry.elongate_line( x2, y2, x1, y1, -self.bond_second_line_shortening * length) draw_plain_or_colored_line((x1, y1), (x2, y2), second=True) else: for i in (1, -1): x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i * self.bond_width * 0.5) draw_plain_or_colored_line((x1, y1), (x2, y2)) elif e.order == 3: self._draw_line(start, end, line_width=self.line_width) for i in (1, -1): x1, y1, x2, y2 = geometry.find_parallel( start[0], start[1], end[0], end[1], i * self.bond_width * 0.7) draw_plain_or_colored_line((x1, y1), (x2, y2), second=True) if transform: # if transform was used, we need to transform back for n in atom1.neighbors + atom2.neighbors: n.coords = self._invtransform.transform_xyz(*n.coords)