public AtomSymbol GenerateAbbreviatedSymbol(string label, HydrogenPosition position) { var tokens = new List <string>(); if (AbbreviationLabel.Parse(label, tokens)) { return(GenerateAbbreviationSymbol(tokens, position)); } else { return(new AtomSymbol(new TextOutline(label, font, emSize), Array.Empty <TextOutline>())); } }
/// <summary> /// Generate the displayed atom symbol for an atom in given structure with the specified hydrogen /// position. /// </summary> /// <param name="container">structure to which the atom belongs</param> /// <param name="atom">the atom to generate the symbol for</param> /// <param name="position">the hydrogen position</param> /// <param name="model">additional rendering options</param> /// <returns>atom symbol</returns> public AtomSymbol GenerateSymbol(IAtomContainer container, IAtom atom, HydrogenPosition position, RendererModel model) { if (atom is IPseudoAtom pAtom) { if (pAtom.AttachPointNum <= 0) { if ("*".Equals(pAtom.Label, StringComparison.Ordinal)) { var mass = pAtom.MassNumber ?? 0; var charge = pAtom.FormalCharge ?? 0; var hcnt = pAtom.ImplicitHydrogenCount ?? 0; var nrad = container.GetConnectedSingleElectrons(atom).Count(); if (mass != 0 || charge != 0 || hcnt != 0) { return(GeneratePeriodicSymbol(0, hcnt, mass, charge, nrad, position)); } } return(GeneratePseudoSymbol(AccessPseudoLabel(pAtom, "?"), position)); } else { return(null); // attach point drawn in bond generator } } else { var number = atom.AtomicNumber; // unset the mass if it's the major isotope (could be an option) var mass = atom.MassNumber; if (number != 0 && mass != null && model != null && model.GetOmitMajorIsotopes() && IsMajorIsotope(number, mass.Value)) { mass = null; } return(GeneratePeriodicSymbol( number, atom.ImplicitHydrogenCount ?? 0, mass ?? -1, atom.FormalCharge ?? 0, container.GetConnectedSingleElectrons(atom).Count(), position)); } }
/// <summary> /// Position the hydrogen label relative to the element label. /// </summary> /// <param name="position">relative position where the hydrogen is placed</param> /// <param name="element">the outline of the element label</param> /// <param name="hydrogen">the outline of the hydrogen</param> /// <returns>positioned hydrogen label</returns> public TextOutline PositionHydrogenLabel(HydrogenPosition position, TextOutline element, TextOutline hydrogen) { var elementBounds = element.GetBounds(); var hydrogenBounds = hydrogen.GetBounds(); switch (position) { case HydrogenPosition.Above: return(hydrogen.Translate(0, (elementBounds.Top - padding) - hydrogenBounds.Bottom)); case HydrogenPosition.Right: return(hydrogen.Translate((elementBounds.Right + padding) - hydrogenBounds.Left, 0)); case HydrogenPosition.Below: return(hydrogen.Translate(0, (elementBounds.Bottom + padding) - hydrogenBounds.Top)); case HydrogenPosition.Left: return(hydrogen.Translate((elementBounds.Left - padding) - hydrogenBounds.Right, 0)); } return(hydrogen); // never reached }
/// <summary> /// Position the charge label on the top right of either the element or hydrogen label. Where the /// charge is placed depends on the number of hydrogens and their position relative to the /// element symbol. /// </summary> /// <param name="hydrogens">number of hydrogen</param> /// <param name="position">position of hydrogen</param> /// <param name="charge">the charge label outline (to be positioned)</param> /// <param name="element">the element label outline</param> /// <param name="hydrogen">the hydrogen label outline</param> /// <returns>positioned charge label</returns> public TextOutline PositionChargeLabel(int hydrogens, HydrogenPosition position, TextOutline charge, TextOutline element, TextOutline hydrogen) { var chargeBounds = charge.GetBounds(); // the charge is placed to the top right of the element symbol // unless either the hydrogen label or the hydrogen count label // are in the way - in which case we place it relative to the // hydrogen var referenceBounds = element.GetBounds(); if (hydrogens > 0 && position == HydrogenPosition.Right) { referenceBounds = hydrogen.GetBounds(); } else if (hydrogens > 1 && position == HydrogenPosition.Above) { referenceBounds = hydrogen.GetBounds(); } return(charge.Translate((referenceBounds.Right + padding) - chargeBounds.Left, (referenceBounds.Top - (chargeBounds.Height / 2)) - chargeBounds.Top)); }
public static string Name(this HydrogenPosition value) => names[(int)value];
/// <summary> /// The directional vector for this hydrogen position. /// </summary> public static Vector2 Vector(this HydrogenPosition value) => V[(int)value].Vector;
/// <summary> /// Generate an atom symbol for a periodic element with the specified number of hydrogens, ionic /// charge, mass, /// </summary> /// <param name="number">atomic number</param> /// <param name="hydrogens">labelled hydrogen count</param> /// <param name="mass">atomic mass</param> /// <param name="charge">ionic formal charge</param> /// <param name="unpaired">number of unpaired electrons</param> /// <param name="position">placement of hydrogen</param> /// <returns>laid out atom symbol</returns> public AtomSymbol GeneratePeriodicSymbol(int number, int hydrogens, int mass, int charge, int unpaired, HydrogenPosition position) { var element = number == 0 ? new TextOutline("*", font, emSize) : new TextOutline(ChemicalElement.Of(number).Symbol, font, emSize); var hydrogenAdjunct = defaultHydrogenLabel; // the hydrogen count, charge, and mass adjuncts are script size var hydrogenCount = new TextOutline(hydrogens.ToString(), font, emSize).Resize(scriptSize, scriptSize); var chargeAdjunct = new TextOutline(ChargeAdjunctText(charge, unpaired), font, emSize).Resize(scriptSize, scriptSize); var massAdjunct = new TextOutline(mass.ToString(), font, emSize).Resize(scriptSize, scriptSize); // position each adjunct relative to the element label and each other hydrogenAdjunct = PositionHydrogenLabel(position, element, hydrogenAdjunct); hydrogenCount = PositionSubscript(hydrogenAdjunct, hydrogenCount); chargeAdjunct = PositionChargeLabel(hydrogens, position, chargeAdjunct, element, hydrogenAdjunct); massAdjunct = PositionMassLabel(massAdjunct, element); // when the hydrogen label is positioned to the left we may need to nudge it // over to account for the hydrogen count and/or the mass adjunct colliding // with the element label if (position == HydrogenPosition.Left) { var nudgeX = HydrogenXDodge(hydrogens, mass, element, hydrogenAdjunct, hydrogenCount, massAdjunct); hydrogenAdjunct = hydrogenAdjunct.Translate(nudgeX, 0); hydrogenCount = hydrogenCount.Translate(nudgeX, 0); } var adjuncts = new List <TextOutline>(4); if (hydrogens > 0) { adjuncts.Add(hydrogenAdjunct); } if (hydrogens > 1) { adjuncts.Add(hydrogenCount); } if (charge != 0 || unpaired > 0) { adjuncts.Add(chargeAdjunct); } if (mass > 0) { adjuncts.Add(massAdjunct); } return(new AtomSymbol(element, adjuncts)); }
/// <summary> /// Generate a formatted abbreviation AtomSymbol for the given Hydrogen position. /// </summary> /// <param name="tokens">the parsed tokens</param> /// <param name="position">hydrogen position - determines if we reverse the label</param> /// <returns>the generated symbol</returns> public AtomSymbol GenerateAbbreviationSymbol(List <string> tokens, HydrogenPosition position) { if (position == HydrogenPosition.Left) { AbbreviationLabel.Reverse(tokens); } var tmpRefPoint = new TextOutline("H", font, emSize); var fTexts = AbbreviationLabel.Format(tokens); var italicFont = new Typeface(font.FontFamily, FontStyles.Italic, font.Weight, font.Stretch); var outlines = new List <TextOutline>(fTexts.Count); foreach (var fText in fTexts) { var outline = fText.Style == AbbreviationLabel.STYLE_ITALIC ? new TextOutline(fText.Text, italicFont, emSize) : new TextOutline(fText.Text, font, emSize); // resize and position scripts if (fText.Style == AbbreviationLabel.STYLE_SUBSCRIPT) { outline = outline.Resize(scriptSize, scriptSize); outline = PositionSubscript(tmpRefPoint, outline); } else if (fText.Style == AbbreviationLabel.STYLE_SUPSCRIPT) { outline = outline.Resize(scriptSize, scriptSize); outline = PositionSuperscript(tmpRefPoint, outline); } outlines.Add(outline); } // position the outlines relative to each other for (int i = 1; i < outlines.Count; i++) { var ref_ = outlines[i - 1]; var curr = outlines[i]; // charge aligns to symbol not a subscript part if (fTexts[i].Style == AbbreviationLabel.STYLE_SUPSCRIPT && fTexts[i - 1].Style == AbbreviationLabel.STYLE_SUBSCRIPT && i > 1) { ref_ = outlines[i - 2]; } outlines[i] = PositionAfter(ref_, curr); } // find symbol where we want to attach the bond // this becomes the primary outline int index; if (position == HydrogenPosition.Left) { for (index = outlines.Count - 1; index >= 0; index--) { if ((fTexts[index].Style & 0x1) == 0) { break; } } } else { for (index = 0; index < outlines.Count; index++) { if ((fTexts[index].Style & 0x1) == 0) { break; } } } var primary = outlines[index]; outlines.RemoveAt(index); return(new AtomSymbol(primary, outlines)); }
/// <summary> /// Generates an atom symbol for a pseudo atom. /// </summary> /// <returns>the atom symbol</returns> public AtomSymbol GeneratePseudoSymbol(string label, HydrogenPosition position) { var italicFont = new Typeface(font.FontFamily, FontStyles.Italic, FontWeights.Bold, font.Stretch); var outlines = new List <TextOutline>(3); int beg = 0; int pos = 0; int len = label.Length; // upper case followed by lower case while (pos < len && IsUpperCase(label[pos])) { pos++; } if (label[0] != 'R') // Ar is not A^r but 'Ra' is R^a etc { while (pos < len && IsLowerCase(label[pos])) { pos++; } } if (pos > beg) { outlines.Add(new TextOutline(label.Substring(beg, pos), italicFont, emSize)); beg = pos; // 2a etc. while (pos < len && IsDigit(label[pos])) { pos++; } while (pos < len && IsLowerCase(label[pos])) { pos++; } if (pos > beg) { var outline = new TextOutline(label.Substring(beg, pos - beg), italicFont, emSize); outline = outline.Resize(scriptSize, scriptSize); outline = PositionSuperscript(outlines[0], outline); outlines.Add(outline); } int numPrimes = 0; while (pos < len) { switch (label[pos]) { case '\'': numPrimes++; break; case '`': numPrimes++; break; case '‘': numPrimes++; break; case '’': numPrimes++; break; case '‛': numPrimes++; break; case '“': numPrimes += 2; break; case '”': numPrimes += 2; break; case '′': numPrimes++; break; case '″': numPrimes += 2; break; case '‴': numPrimes += 3; break; case '⁗': numPrimes += 4; break; case '‵': numPrimes++; break; case '‶': numPrimes += 2; break; case '‷': numPrimes += 3; break; case '´': numPrimes++; break; case 'ˊ': numPrimes++; break; case '́': numPrimes++; break; case '˝': numPrimes += 2; break; case '̋': numPrimes += 2; break; default: goto break_PRIMES; } pos++; } break_PRIMES: if (pos < len) { return(new AtomSymbol( new TextOutline(label, italicFont, emSize), Array.Empty <TextOutline>())); } else { TextOutline outline = null; var ref_ = outlines[outlines.Count - 1]; switch (numPrimes) { case 0: break; case 1: outline = new TextOutline("′", font, emSize); break; case 2: outline = new TextOutline("″", font, emSize); break; case 3: outline = new TextOutline("‴", font, emSize); break; default: string lab = ""; while (numPrimes-- > 0) { lab += "′"; } outline = new TextOutline(lab, font, emSize); break; } if (outline != null) { if (outlines.Count > 1) { outline = outline.Resize(scriptSize, scriptSize); } outline = PositionSuperscript(ref_, outline); outlines.Add(outline); } } // line up text for (int i = 1; i < outlines.Count; i++) { var ref_ = outlines[i - 1]; var curr = outlines[i]; outlines[i] = PositionAfter(ref_, curr); } return(new AtomSymbol(outlines[0], outlines.GetRange(1, outlines.Count - 1))); } else { return(new AtomSymbol(new TextOutline(label, italicFont, emSize), Array.Empty <TextOutline>())); } }