# Last edited on 2013-02-19 23:07:03 by stolfilocal # From mformula_svg.py: def find_charge_symbol_direction(svg, fm,i) : "Finds a good direction to draw the charge of atom number {i} of formula {fm} in style 'S'.\n" \ "\n" \ "" # Get angles of all bonds with one end at atom {i} ang = [ 0 ]; # Justin Käse. for j in range(fm.nbonds) : i1 = fm.bond_atom_1_index[j]; i2 = fm.bond_atom_2_index[j]; if (i2 != None) : # Get angle of vector from {i1} to {i2}: a1 = rn.scale(svg.bondlength, fm.atom_center[i1]); a2 = rn.scale(svg.bondlength, fm.atom_center[i2]); e = rn.sub(a2, a1); aj = math.atan2(e[1],e[0]); else: aj = fm.bond_angle[j]; # Reduce {aj} to the range {[0,2*pi)} while (aj < 0) : aj = aj + 2*math.pi; while (aj >= 2*math.pi) : aj = aj - 2*math.pi; ang[j:j] = [ aj ]; # Sort angles: for j in range(fm.nbonds) : aj = ang[j]; jp = j; while ((jp > 0) & (ang[jp-1] > aj)) : ang[jp] = ang[jp-1]; jp = jp-1; ang[jp] = aj; # Replicate first angle: ang[fm.nbonds:fm.nbonds] = [ ang[0] + 2*math.pi ]; # Look for largest gap: jm = 0; gm = 0; for j in range(fm.nbonds) : g = ang[j+1] - ang[j]; assert (g >= 0); if (g > gm) : gm = g; jm = j; # Return direction of bisector of that gap: am = ang[jm] + gm/2; return [cos(am), sin(am)] #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Add charge clouds for a few single atoms and rings. # The charge clouds are named 'q{S}{Q.QQQQ}.{N}.{V.VVVV}' where # {S} is the sign of the charge, 'n' or 'p'; # {Q} is the absolute value of the charge, if format '%6.4f'; # {R} is the radius expressed as the number of atoms in the ring (1 for single atom). for den in range(1,7) : for num in range(1,den) : q = (num + 0.0)/(den + 0.0); rgb_nq = rn.mix(q, rgb_n, 1-q, rgb_v); rgb_pq = rn.mix(q, rgb_p, 1-q, rgb_v); # Compute the radius {R} of the charge cloud for a ring with {den} atoms: R = aromatic_ring_radius( svg.add_elem("qn%6.4f.%d" % (q,den), 1.25, [0.700, 0.850, 1.000], 1, ""); # Charge cloud -1 for one atom. if gcd(num,den) == 1 : svg.add_elem("cp1.1.000", 1.25, [1.000, 0.850, 0.700], 1, ""); # Charge cloud +1 for one atom. svg.add_elem("cn1.1.000", 1.25, [0.700, 0.850, 1.000], 1, ""); # Charge cloud -1 for one atom. svg.add_elem("cp1.1.000", 1.25, [1.000, 0.850, 0.700], 1, ""); # Charge cloud +1 for one atom. # Add charge clouds for fractional charges on single atoms: for ch in range(1,7) : for n in range(2,9) : db = svg.fm.bond_length(2.0); # Decide length of bonds {mb} along main ring: if n == 2 : mb = svg.fm.bond_length(3.0); else : mb = svg.fm.bond_length(1.5); # Radius of ring (mol. center to atom center). R = (mb/2.0)/sin(pi/n); col_neg = [1.000-ch*0.100, 1.000-ch*0.020, 1.000 ]; col_pos = [1.000, 1.000-ch*0.080, 1.000-ch*0.100]; svg.add_elem("cn%d.ar%d" % (ch,n), R + 1.00 + 0.5*db, col_neg, 1, ""); svg.add_elem("cp%d.ar%d" % (ch,n), R + 1.00 + 0.5*db, col_pos, 1, ""); # Add charge clouds for aromatic ions: for ch in range(1,7) : for n in range(2,9) : db = svg.fm.bond_length(2.0); # Decide length of bonds {mb} along main ring: if n == 2 : mb = svg.fm.bond_length(3.0); else : mb = svg.fm.bond_length(1.5); # Radius of ring (mol. center to atom center). R = (mb/2.0)/sin(pi/n); col_neg = [1.000-ch*0.100, 1.000-ch*0.020, 1.000 ]; col_pos = [1.000, 1.000-ch*0.080, 1.000-ch*0.100]; svg.add_elem("cn%d.ar%d" % (ch,n), R + 1.00 + 0.5*db, col_neg, 1, ""); svg.add_elem("cp%d.ar%d" % (ch,n), R + 1.00 + 0.5*db, col_pos, 1, ""); # ---------------------------------------------------------------------- def add_charge_cloud_type(svg, sym,chnum,chden,lab) : "Defines the appearance of a charge cloud for use in formula {svg}.\n" \ "\n" \ " The cloud has the identifying symbol {sym} and will be" \ " plotted as a circle or sphere. It represents a charge of {chnum/chden}. The" \ " string {lab}, if not empty will be written inside the circle," \ " centered, if the rendering style demands it." \ "\n" \ " The radius and color are computed from {chnum,chden}." # Compute the fractional charge: q = (chnum+0.0)/(chden+0.0); # Compute {h} the relative intensity of the color (0 to 1) h = 1 - (1 - q)*(1 - q); # Compute color {col} of core # Also compute radius {rad}, assuming that an atom has radius ~1.0. col_v = [1.000, 1.000, 1.000]; # Color of vacuum. if q < 0.0 : col_n = [0.700, 0.850, 1.000]; # Color of one electron. col = rn.mix(h, col_n, 1-h, col_v); else : col_p = [1.000, 0.850, 0.700]; # Color of one proton. col = rn.mix(h, col_p, 1-h, col_v); svg.add_elem(sym,0.0,col,chnum,chden,lab,0,0); #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Add charge clouds for single atoms. # The charge clouds are named 'Q{S}{Q.QQQQ}' where # {S} is the sign of the charge, 'n' or 'p'; # {Q} is the absolute value of the charge, if format '%6.4f'. for num in range(1,7) : for den in range(1,7) : q = (num + 0.0)/(den + 0.0); if (svg.gcd(num,den) == 1) or (den == 1) : svg.add_charge_cloud_type("Qn%6.4f" % q, -q, 1, 1.0, ""); svg.add_charge_cloud_type("Qp%6.4f" % q, +q, 1, 1.0, ""); def add_charge_cloud(fm, q,ctr) : "Adds a charge cloud with value {q} to the formula {fm}.\n" \ "\n" \ " The charge cloud is represented by an atom with symbol 'Q{S}{V.VVVV}'" \ " where {S} is 'n' or 'p' and {V.VVVV} is the absolute value of the charge" \ " in 4 decimals. The charge is centered at {ctr} (a list of two" \ " floats {[x,y]}). The origin is" \ " arbitrary, the X axis points to the right, and the Y axis" \ " points UP. Returns the index of the cloud in the atom list." if q < 0 : sym = "Qn%6.4f" % -q; elif q > 0 : sym = "Qp%6.4f" % +q; else : return; i = fm.add_atom(sym,ctr); return i; #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def draw_atom(svg, cx,cy,rad,RGB,fuzzy,label,labdx,labdy): "Fills and draws an atom with center {(cx,cy)} and radius {rad} (in px, user axes).\n" \ "\n" \ " If {svg.style} is 'B' draws a disk and then the label over it. If {svg.style} is 'S' draws only the label.\n" \ "\n" \ " If {fuzzy} is 0, the circle will be filled with color {RGB} (a triplet of floats in [0_1]) and" \ " will have a stroked outline of a burned-out version of the same color.\n" \ "\n" \ " If {fuzzy} is 1, the circle will have no outline and will fade out to white." \ "\n" \ " The label is displaced {labdx,labdy} times the font size.." # Select font height {fh}, raw ball if appropriate, and note its color {bgRGB}: fh = svg.fontheight; if (svg.style == 'B') : if rad > 0 : svg.draw_ball(cx,cy,rad,RGB,fuzzy); bgRGB = RGB else : bgRGB = [1,1,1]; elif (svg.style == 'S') : svg.draw_ball(cx,cy,0.90*svg.atomradius,[1,1,1],-1); bgRGB = [1,1,1]; else : assert false; if label != '' : # Print label string with proper font formatting: str = make_roman_style(label); white = (luminance(bgRGB) < 0.290); svg.draw_label(cx+labdx*fh,cy+labdy*fh,str,white,fh); #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -