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
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 _process_multi_anelated_ring( self, ring, angle_shift=0): out = [] to_go = [v for v in ring if v.x == None or v.y == None] if not to_go: # it was all already done return [] back = [v for v in ring if v.x != None and v.y != None] sorted_back = self.mol.sort_vertices_in_path( back) if not sorted_back: # the already set atoms are not in one path - we have to process it "per partes" # it should not happen with the construction method we use raise( "i am not able to handle this, it should normaly not happen. please send me the input.") else: v1 = sorted_back[0] v2 = sorted_back[-1] v3 = sorted_back[1] to_go = self.mol.sort_vertices_in_path( to_go) if v1 not in to_go[0].get_neighbors(): v1, v2 = v2, v1 blocked_angle = sum_of_ring_internal_angles( len( back)) overall_angle = sum_of_ring_internal_angles( len( ring)) da = optimal_ring_iternal_angle( len( ring)) # internal angle # if there are 2 rings of same size inside each other, we need to use the angle_shift if angle_shift: da += 2*angle_shift/(len( to_go)) ca = deg_to_rad( 180-(overall_angle - blocked_angle - len( to_go) * da + angle_shift)/2) # connection angle side = sum( [geometry.on_which_side_is_point( (v1.x,v1.y,v2.x,v2.y),(v.x,v.y)) for v in back if v != v1 and v != v2]) # we need to make sure that the ring is drawn on the right side if side > 0: ca = -ca ca += geometry.clockwise_angle_from_east( v1.x-v2.x, v1.y-v2.y) da = 180-da # for drawing we use external angle # we must ensure that the ring will progress towards the second end if geometry.on_which_side_is_point( (v1.x,v1.y,v3.x,v3.y),(v2.x,v2.y)) < 0: da = -da # dry run to see where we get gcoords = gen_angle_stream( deg_to_rad( da), start_from= ca) x, y = v1.x, v1.y for i in range( len( to_go) +1): a = gcoords.next() x += self.bond_length*cos( a) y += self.bond_length*sin( a) # end of dry run, we can scale the bond_length now length = geometry.line_length( (v1.x,v1.y,v2.x,v2.y)) real_length = geometry.line_length( (v1.x,v1.y,x,y)) bl = self.bond_length * length / real_length gcoords = gen_angle_stream( deg_to_rad( da), start_from= ca) # and here we go self.apply_gen_to_atoms( gcoords, to_go, v1, bond_length=bl) out += to_go return out
def _process_simply_anelated_ring( self, ring, base): out = [] inter = [v for v in ring if v.x != None and v.y != None] if len( inter) == 1: # rings are connected via one atom v = inter.pop() # the atom of concatenation ring = self.mol.sort_vertices_in_path( ring, start_from=v) base_neighs = [a for a in v.get_neighbors() if a in base] if len( base_neighs) < 2: raise "this should not happen" d1 = base_neighs[0] d2 = base_neighs[1] ca1 = geometry.clockwise_angle_from_east( v.x-d1.x, v.y-d1.y) ca2 = geometry.clockwise_angle_from_east( v.x-d2.x, v.y-d2.y) ca = (ca1+ca2)/2 if abs( ca1-ca2) < pi: ca += -pi/2 else: ca += pi/2 size = len( ring) da = deg_to_rad(180 -180.0*(size-2)/size) gcoords = gen_angle_stream( da, start_from=ca + da/2) ring.remove( v) # here we generate the coords self.apply_gen_to_atoms( gcoords, ring, v) ring.append( v) out += ring elif len( inter) == 2: # there are two atoms common to the rings v1, v2 = inter # the atoms of concatenation ring = self.mol.sort_vertices_in_path( ring, start_from=v1) ring.remove( v1) ring.remove( v2) if not v1 in ring[0].get_neighbors(): v1, v2 = v2, v1 side = sum( [geometry.on_which_side_is_point((v1.x,v1.y,v2.x,v2.y),(v.x,v.y)) for v in base]) if not side: warnings.warn( "this should not happen") ca = geometry.clockwise_angle_from_east( v1.x-v2.x, v1.y-v2.y) size = len( ring)+2 da = deg_to_rad(180 -180.0*(size-2)/size) if side > 0: da = -da gcoords = gen_angle_stream( da, start_from=ca+da) self.apply_gen_to_atoms( gcoords, ring, v1) ring.append( v1) ring.append( v2) out += ring else: # there are more than 2 atoms common if len( ring) == len( base): out += self._process_multi_anelated_ring( ring, angle_shift=15) #raise( "i don't how to handle this yet") else: out += self._process_multi_anelated_ring( ring) return out
def get_angle_gradient2( self, opt_angle, refv, v1, v2): ang1 = geometry.clockwise_angle_from_east( v1.x-refv.x, v1.y-refv.y) ang2 = geometry.clockwise_angle_from_east( v2.x-refv.x, v2.y-refv.y) ang = ang1 - ang2 sign = -geometry.on_which_side_is_point( (refv.x, refv.y, v1.x, v1.y), (v2.x, v2.y)) while ang < 0: ang += 2*pi if ang > pi: ang = 2*pi - ang sign *= -1 dang = (ang - opt_angle) / 2 gx1 = (cos( ang1 - sign*dang) - cos( ang1)) * sqrt( (refv.x-v1.x)**2 + (refv.y-v1.y)**2) #self.bond_length #(v1.x-refv.x) gy1 = (sin( ang1 - sign*dang) - sin( ang1)) * sqrt( (refv.x-v1.x)**2 + (refv.y-v1.y)**2) #self.bond_length #(v1.y-refv.y) gx2 = (cos( ang2 + sign*dang) - cos( ang2)) * sqrt( (refv.x-v2.x)**2 + (refv.y-v2.y)**2) #self.bond_length #(v2.x-refv.x) gy2 = (sin( ang2 + sign*dang) - sin( ang2)) * sqrt( (refv.x-v2.x)**2 + (refv.y-v2.y)**2) #self.bond_length #(v2.y-refv.y) # control ang1 = geometry.clockwise_angle_from_east( v1.x-refv.x+gx1, v1.y-refv.y+gy1) ang2 = geometry.clockwise_angle_from_east( v2.x-refv.x+gx2, v2.y-refv.y+gy2) ang = ang1 - ang2 sign = geometry.on_which_side_is_point( (refv.x, refv.y, v1.x, v1.y), (v2.x, v2.y)) while ang < 0: ang += 2*pi #sign *= -1 if ang > pi: ang = 2*pi - ang #sign *= -1 dang2 = (ang - opt_angle) / 2 if abs( dang2) > abs( dang): print "f**k", rad_to_deg( ang1), rad_to_deg( ang2), rad_to_deg( dang) else: #rint "good", rad_to_deg( ang1), rad_to_deg( ang2), rad_to_deg( dang) pass return gx1, gy1, gx2, gy2
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 _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): 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)