/// <summary> /// Hide the repeated atoms and bonds of a multiple group. We hide al atoms that /// belong to the group unless they are defined in the parent atom list. Any /// bond to those atoms that is not a crossing bond or one connecting atoms in /// the parent list is hidden. /// </summary> /// <param name="container">molecule</param> /// <param name="sgroup">multiple group display shortcut</param> private static void HideMultipleParts(IAtomContainer container, Sgroup sgroup) { var crossing = sgroup.Bonds; var atoms = sgroup.Atoms; var parentAtoms = (ICollection <IAtom>)sgroup.GetValue(SgroupKey.CtabParentAtomList); foreach (var bond in container.Bonds) { if (parentAtoms.Contains(bond.Begin) && parentAtoms.Contains(bond.End)) { continue; } if (atoms.Contains(bond.Begin) || atoms.Contains(bond.End)) { StandardGenerator.Hide(bond); } } foreach (var atom in atoms) { if (!parentAtoms.Contains(atom)) { StandardGenerator.Hide(atom); } } foreach (var bond in crossing) { StandardGenerator.Unhide(bond); } }
public void CopySgroups() { List <Sgroup> sgroups = new List <Sgroup>(); IAtom a1 = new Mock <IAtom>().Object; IAtom a2 = new Mock <IAtom>().Object; IBond b1 = new Mock <IBond>().Object; IBond b2 = new Mock <IBond>().Object; Sgroup sgroup = new Sgroup { Type = SgroupType.CtabStructureRepeatUnit, Subscript = "n" }; sgroup.Atoms.Add(a1); sgroup.Atoms.Add(a2); sgroup.Bonds.Add(b1); sgroup.Bonds.Add(b2); sgroups.Add(sgroup); var copied = SgroupManipulator.Copy(sgroups, null); Sgroup copiedSgroup = copied[0]; Assert.AreNotSame(sgroup, copiedSgroup); Assert.AreEqual(sgroup.Type, copiedSgroup.Type); Assert.AreEqual(sgroup.Subscript, copiedSgroup.Subscript); Assert.IsTrue(Compares.AreDeepEqual(sgroup.Atoms, copiedSgroup.Atoms)); Assert.IsTrue(Compares.AreDeepEqual(sgroup.Bonds, copiedSgroup.Bonds)); }
public void WriteSRUs() { var mol = builder.NewAtomContainer(); mol.Atoms.Add(builder.NewAtom("C")); mol.Atoms.Add(builder.NewAtom("C")); mol.Atoms.Add(builder.NewAtom("O")); mol.Atoms.Add(builder.NewAtom("O")); mol.AddBond(mol.Atoms[0], mol.Atoms[1], BondOrder.Single); mol.AddBond(mol.Atoms[1], mol.Atoms[2], BondOrder.Single); mol.AddBond(mol.Atoms[2], mol.Atoms[3], BondOrder.Single); mol.Atoms[0].ImplicitHydrogenCount = 3; mol.Atoms[1].ImplicitHydrogenCount = 2; mol.Atoms[2].ImplicitHydrogenCount = 0; mol.Atoms[3].ImplicitHydrogenCount = 1; var sgroups = new List <Sgroup>(); var sgroup = new Sgroup(); sgroup.Atoms.Add(mol.Atoms[1]); sgroup.Atoms.Add(mol.Atoms[2]); sgroup.Bonds.Add(mol.Bonds[0]); sgroup.Bonds.Add(mol.Bonds[2]); sgroup.Type = SgroupType.CtabStructureRepeatUnit; sgroup.Subscript = "n"; sgroup.PutValue(SgroupKey.CtabConnectivity, "HH"); sgroups.Add(sgroup); mol.SetCtabSgroups(sgroups); var res = WriteToStr(mol); Assert.IsTrue(res.Contains("M V30 1 SRU 0 ATOMS=(2 2 3) XBONDS=(2 1 3) LABEL=n CONNECT=HH\n")); }
public async Task <IActionResult> Edit(int id, [Bind("SgroupId,SgroupName,CourseName,Specialization")] Sgroup sgroup) { if (id != sgroup.SgroupId) { return(NotFound()); } if (ModelState.IsValid) { try { _context.Update(sgroup); await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!SgroupExists(sgroup.SgroupId)) { return(NotFound()); } else { throw; } } return(RedirectToAction(nameof(Index))); } return(View(sgroup)); }
private static string GetSgroupPolymerKey(Sgroup sgroup) { switch (sgroup.Type) { case SgroupType.CtabStructureRepeatUnit: return("n"); case SgroupType.CtabMonomer: return("mon"); case SgroupType.CtabMer: return("mer"); case SgroupType.CtabCopolymer: string subtype = (string)sgroup.GetValue(SgroupKey.CtabSubType); if (subtype == null) { return("co"); } switch (subtype) { case "RAN": return("ran"); case "ALT": return("alt"); case "BLO": return("blk"); } goto case SgroupType.CtabCrossLink; case SgroupType.CtabCrossLink: return("xl"); case SgroupType.CtabModified: return("mod"); case SgroupType.CtabMixture: return("mix"); case SgroupType.CtabFormulation: return("f"); case SgroupType.CtabAnyPolymer: return("any"); case SgroupType.CtabGeneric: return("gen"); case SgroupType.CtabComponent: return("c"); case SgroupType.CtabGraft: return("grf"); default: throw new ArgumentException($"{sgroup.Type} is not proper."); } }
/// <summary> /// Checks whether the given abbreviation Sgroup either has no highlight or is fully highlighted. If an /// abbreviation is partially highlighted we don't want to contract it as this would hide the part /// being highlighted. /// </summary> /// <param name="container">molecule</param> /// <param name="sgroup">abbreviation Sgroup</param> /// <returns>the abbreviation can be contracted</returns> private static bool CheckAbbreviationHighlight(IAtomContainer container, Sgroup sgroup) { Debug.Assert(sgroup.Type == SgroupType.CtabAbbreviation); var sgroupAtoms = sgroup.Atoms; int atomHighlight = 0; int bondHighlight = 0; int numSgroupAtoms = sgroupAtoms.Count; int numSgroupBonds = 0; Color?color = null; Color?refcolor = null; foreach (var atom in sgroupAtoms) { if ((color = atom.GetProperty <Color?>(StandardGenerator.HighlightColorKey)) != null) { atomHighlight++; if (refcolor == null) { refcolor = color; } else if (!color.Equals(refcolor)) { return(false); // multi-colored } } else if (atomHighlight != 0) { return(false); // fail fast } } foreach (var bond in container.Bonds) { var beg = bond.Begin; var end = bond.End; if (sgroupAtoms.Contains(beg) && sgroupAtoms.Contains(end)) { numSgroupBonds++; if ((color = bond.GetProperty <Color?>(StandardGenerator.HighlightColorKey)) != null) { bondHighlight++; if (refcolor == null) { refcolor = color; } else if (!color.Equals(refcolor)) { return(false); // multi-colored } } else if (bondHighlight != 0) { return(false); // fail fast } } } return(atomHighlight + bondHighlight == 0 || (atomHighlight == numSgroupAtoms && bondHighlight == numSgroupBonds)); }
private IRenderingElement GenerateAbbreviationSgroup(IAtomContainer mol, Sgroup sgroup) { string label = sgroup.Subscript; // already handled by symbol remapping if (sgroup.Bonds.Count > 0 || string.IsNullOrEmpty(label)) { return(new ElementGroup()); } if (!CheckAbbreviationHighlight(mol, sgroup)) { return(new ElementGroup()); } // we're showing a label where there were no atoms before, we put it in the // middle of all of those which were hidden var sgroupAtoms = sgroup.Atoms; Debug.Assert(sgroupAtoms.Any()); var highlight = sgroupAtoms.First().GetProperty <Color>(StandardGenerator.HighlightColorKey); var style = parameters.GetHighlighting(); var glowWidth = parameters.GetOuterGlowWidth(); Vector2 labelCoords = GeometryUtil.Get2DCenter(sgroupAtoms); ElementGroup labelgroup = new ElementGroup(); foreach (var outline in atomGenerator.GenerateAbbreviatedSymbol(label, HydrogenPosition.Right) .Resize(1 / scale, 1 / -scale) .GetOutlines()) { if (highlight != null && style == HighlightStyle.Colored) { labelgroup.Add(GeneralPath.ShapeOf(outline, highlight)); } else { labelgroup.Add(GeneralPath.ShapeOf(outline, foreground)); } } if (highlight != null && style == HighlightStyle.OuterGlow) { ElementGroup group = new ElementGroup { // outer glow needs to be being the label StandardGenerator.OuterGlow(labelgroup, highlight, glowWidth, stroke), labelgroup }; return(group); } else { return(MarkedElement.MarkupAtom(labelgroup, null)); } }
public async Task <IActionResult> Create([Bind("SgroupId,SgroupName,CourseName,Specialization")] Sgroup sgroup) { if (ModelState.IsValid) { _context.Add(sgroup); await _context.SaveChangesAsync(); return(RedirectToAction(nameof(Index))); } return(View(sgroup)); }
private IRenderingElement GenerateMixtureSgroup(Sgroup sgroup) { // draw the brackets // TODO - mixtures normally have attached Sgroup data // TODO - e.g. COMPONENT_FRACTION, ACTIVITY_TYPE, WEIGHT_PERCENT var brackets = (IList <SgroupBracket>)sgroup.GetValue(SgroupKey.CtabBracket); if (brackets != null) { var type = sgroup.Type; string subscript = "?"; switch (type) { case SgroupType.CtabComponent: var compNum = (int?)sgroup.GetValue(SgroupKey.CtabComponentNumber); if (compNum != null) { subscript = "c" + compNum.ToString(); } else { subscript = "c"; } break; case SgroupType.CtabMixture: subscript = "mix"; break; case SgroupType.CtabFormulation: subscript = "f"; break; } return(GenerateSgroupBrackets(sgroup, brackets, null, subscript, null)); } else { return(new ElementGroup()); } }
private IRenderingElement GenerateMultipleSgroup(Sgroup sgroup) { // just draw the brackets - multiplied group parts have already been hidden in prep phase var brackets = (IList <SgroupBracket>)sgroup.GetValue(SgroupKey.CtabBracket); if (brackets != null) { return(GenerateSgroupBrackets(sgroup, brackets, Dictionaries.Empty <IAtom, AtomSymbol>(), (string)sgroup.GetValue(SgroupKey.CtabSubScript), null)); } else { return(new ElementGroup()); } }
public void DontOverwriteExistingSgroups() { var factory = new Abbreviations { "*CCC Bu" }; var mol = Smi("c1ccccc1CCC"); var sgroup = new Sgroup(); sgroup.Atoms.Add(mol.Atoms[6]); sgroup.Atoms.Add(mol.Atoms[7]); sgroup.Atoms.Add(mol.Atoms[8]); sgroup.Type = SgroupType.CtabAbbreviation; sgroup.Subscript = "n-Bu"; mol.SetCtabSgroups(new[] { sgroup }); var sgroups = factory.Generate(mol); Assert.AreEqual(0, sgroups.Count); }
public void CopySgroups2() { var sgroups = new List <Sgroup>(); var replace = new CDKObjectMap(); IAtom a1 = new Mock <IAtom>().Object; IAtom a2 = new Mock <IAtom>().Object; IBond b1 = new Mock <IBond>().Object; IBond b2 = new Mock <IBond>().Object; IAtom a1copy = new Mock <IAtom>().Object; IAtom a2copy = new Mock <IAtom>().Object; IBond b1copy = new Mock <IBond>().Object; IBond b2copy = new Mock <IBond>().Object; replace.Add(a1, a1copy); replace.Add(a2, a2copy); replace.Add(b1, b1copy); replace.Add(b2, b2copy); Sgroup sgroup = new Sgroup { Type = SgroupType.CtabStructureRepeatUnit, Subscript = "n" }; sgroup.Atoms.Add(a1); sgroup.Atoms.Add(a2); sgroup.Bonds.Add(b1); sgroup.Bonds.Add(b2); sgroups.Add(sgroup); var copied = SgroupManipulator.Copy(sgroups, replace); Sgroup copiedSgroup = copied[0]; Assert.AreNotSame(sgroup, copiedSgroup); Assert.AreEqual(sgroup.Type, copiedSgroup.Type); Assert.AreEqual(sgroup.Subscript, copiedSgroup.Subscript); Assert.IsFalse(Compares.AreDeepEqual(sgroup.Atoms, copiedSgroup.Atoms)); Assert.IsFalse(Compares.AreDeepEqual(sgroup.Bonds, copiedSgroup.Bonds)); Assert.IsTrue(copiedSgroup.Atoms.Contains(a1copy)); Assert.IsTrue(copiedSgroup.Atoms.Contains(a2copy)); Assert.IsTrue(copiedSgroup.Bonds.Contains(b1copy)); Assert.IsTrue(copiedSgroup.Bonds.Contains(b2copy)); }
public void WriteMultipleGroup() { int repeatAtoms = 50; var mol = builder.NewAtomContainer(); mol.Atoms.Add(builder.NewAtom("C")); for (int i = 0; i < repeatAtoms; i++) { mol.Atoms.Add(builder.NewAtom("C")); } mol.Atoms.Add(builder.NewAtom("O")); mol.AddBond(mol.Atoms[0], mol.Atoms[1], BondOrder.Single); for (int i = 0; i < repeatAtoms; i++) { mol.AddBond(mol.Atoms[i + 1], mol.Atoms[i + 2], BondOrder.Single); } mol.Atoms[0].ImplicitHydrogenCount = 3; for (int i = 0; i < repeatAtoms; i++) { mol.Atoms[1 + i].ImplicitHydrogenCount = 2; } mol.Atoms[mol.Atoms.Count - 1].ImplicitHydrogenCount = 1; var sgroups = new List <Sgroup>(); var sgroup = new Sgroup(); for (int i = 0; i < repeatAtoms; i++) { sgroup.Atoms.Add(mol.Atoms[i + 1]); } sgroup.Bonds.Add(mol.Bonds[0]); sgroup.Bonds.Add(mol.Bonds[mol.Bonds.Count - 1]); sgroup.PutValue(SgroupKey.CtabParentAtomList, new[] { mol.Atoms[1] }); sgroup.Type = SgroupType.CtabMultipleGroup; sgroup.Subscript = repeatAtoms.ToString(); sgroups.Add(sgroup); mol.SetCtabSgroups(sgroups); var res = WriteToStr(mol); Assert.IsTrue(res.Contains("M V30 1 MUL 0 ATOMS=(50 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 2-\n" + "M V30 2 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 -\n" + "M V30 46 47 48 49 50 51) XBONDS=(2 1 51) MULT=50 PATOMS=(1 2)\n")); }
private Sgroup GetAbbr(IAtomContainer part) { string label; string cansmi; if (part.Atoms.Count == 1) { var atom = part.Atoms[0]; label = GetBasicElementSymbol(atom); if (label != null) { var sgroup = new Sgroup { Type = SgroupType.CtabAbbreviation, Subscript = label }; sgroup.Atoms.Add(atom); return(sgroup); } } else { cansmi = usmigen.Create(part); if (disconnectedAbbreviations.TryGetValue(cansmi, out label) && !disabled.Contains(label)) { var sgroup = new Sgroup { Type = SgroupType.CtabAbbreviation, Subscript = label }; foreach (var atom in part.Atoms) { sgroup.Atoms.Add(atom); } return(sgroup); } } return(null); }
public void TestSgroupAtomListWrapping() { IAtomContainer mol = TestMoleculeFactory.MakeEthylPropylPhenantren(); Sgroup sgroup = new Sgroup(); foreach (var atom in mol.Atoms) { sgroup.Atoms.Add(atom); } mol.SetCtabSgroups(new[] { sgroup }); StringWriter sw = new StringWriter(); using (MDLV2000Writer mdlw = new MDLV2000Writer(sw)) { mdlw.Write(mol); string output = sw.ToString(); Assert.IsTrue(output.Contains("M SAL 1 15")); Assert.IsTrue(output.Contains("M SAL 1 4")); } }
/// <summary> /// Find all enabled abbreviations in the provided molecule. They are not /// added to the existing Sgroups and may need filtering. /// </summary> /// <param name="mol">molecule</param> /// <returns>list of new abbreviation Sgroups</returns> public IList <Sgroup> Generate(IAtomContainer mol) { // mark which atoms have already been abbreviated or are // part of an existing Sgroup var usedAtoms = new HashSet <IAtom>(); var sgroups = mol.GetCtabSgroups(); if (sgroups != null) { foreach (var sgroup in sgroups) { foreach (var atom in sgroup.Atoms) { usedAtoms.Add(atom); } } } var newSgroups = new List <Sgroup>(); // disconnected abbreviations, salts, common reagents, large compounds if (!usedAtoms.Any()) { try { var copy = AtomContainerManipulator.CopyAndSuppressedHydrogens(mol); string cansmi = usmigen.Create(copy); if (disconnectedAbbreviations.TryGetValue(cansmi, out string label) && !disabled.Contains(label) && ContractToSingleLabel) { var sgroup = new Sgroup { Type = SgroupType.CtabAbbreviation, Subscript = label }; foreach (var atom in mol.Atoms) { sgroup.Atoms.Add(atom); } return(new[] { sgroup }); } else if (cansmi.Contains(".")) { var parts = ConnectivityChecker.PartitionIntoMolecules(mol); // leave one out Sgroup best = null; for (int i = 0; i < parts.Count; i++) { var a = parts[i]; var b = a.Builder.NewAtomContainer(); for (int j = 0; j < parts.Count; j++) { if (j != i) { b.Add(parts[j]); } } var sgroup1 = GetAbbr(a); var sgroup2 = GetAbbr(b); if (sgroup1 != null && sgroup2 != null && ContractToSingleLabel) { var combined = new Sgroup(); label = null; foreach (var atom in sgroup1.Atoms) { combined.Atoms.Add(atom); } foreach (var atom in sgroup2.Atoms) { combined.Atoms.Add(atom); } if (sgroup1.Subscript.Length > sgroup2.Subscript.Length) { combined.Subscript = sgroup1.Subscript + String_Interpunct + sgroup2.Subscript; } else { combined.Subscript = sgroup2.Subscript + String_Interpunct + sgroup1.Subscript; } combined.Type = SgroupType.CtabAbbreviation; return(new[] { combined }); } if (sgroup1 != null && (best == null || sgroup1.Atoms.Count > best.Atoms.Count)) { best = sgroup1; } if (sgroup2 != null && (best == null || sgroup2.Atoms.Count < best.Atoms.Count)) { best = sgroup2; } } if (best != null) { newSgroups.Add(best); foreach (var atom in best.Atoms) { usedAtoms.Add(atom); } } } } catch (CDKException) { } } var fragments = GenerateFragments(mol); var sgroupAdjs = new MultiDictionary <IAtom, Sgroup>(); foreach (var frag in fragments) { try { var smi = usmigen.Create(AtomContainerManipulator.CopyAndSuppressedHydrogens(frag)); if (!connectedAbbreviations.TryGetValue(smi, out string label) || disabled.Contains(label)) { continue; } bool overlap = false; // note: first atom is '*' var numAtoms = frag.Atoms.Count; var numBonds = frag.Bonds.Count; for (int i = 1; i < numAtoms; i++) { if (usedAtoms.Contains(frag.Atoms[i])) { overlap = true; break; } } // overlaps with previous assignment if (overlap) { continue; } // create new abbreviation Sgroup var sgroup = new Sgroup { Type = SgroupType.CtabAbbreviation, Subscript = label }; var attachBond = frag.Bonds[0].GetProperty <IBond>(PropertyName_CutBond); IAtom attachAtom = null; sgroup.Bonds.Add(attachBond); for (int i = 1; i < numAtoms; i++) { var atom = frag.Atoms[i]; usedAtoms.Add(atom); sgroup.Atoms.Add(atom); if (attachBond.Begin.Equals(atom)) { attachAtom = attachBond.End; } else if (attachBond.End.Equals(atom)) { attachAtom = attachBond.Begin; } } if (attachAtom != null) { sgroupAdjs.Add(attachAtom, sgroup); } newSgroups.Add(sgroup); } catch (CDKException) { // ignore } } if (!ContractOnHetero) { return(newSgroups); } // now collapse foreach (var attach in mol.Atoms) { if (usedAtoms.Contains(attach)) { continue; } // skip charged or isotopic labelled, C or R/*, H, He if ((attach.FormalCharge != null && attach.FormalCharge != 0) || attach.MassNumber != null || attach.AtomicNumber == 6 || attach.AtomicNumber < 2) { continue; } var hcount = attach.ImplicitHydrogenCount.Value; var xatoms = new HashSet <IAtom>(); var xbonds = new HashSet <IBond>(); var newbonds = new HashSet <IBond>(); xatoms.Add(attach); var nbrSymbols = new List <string>(); var todelete = new HashSet <Sgroup>(); foreach (var sgroup in sgroupAdjs[attach]) { if (ContainsChargeChar(sgroup.Subscript)) { continue; } if (sgroup.Bonds.Count != 1) { continue; } var xbond = sgroup.Bonds.First(); xbonds.Add(xbond); foreach (var a in sgroup.Atoms) { xatoms.Add(a); } if (attach.Symbol.Length == 1 && char.IsLower(sgroup.Subscript[0])) { if (ChemicalElement.OfSymbol(attach.Symbol + sgroup.Subscript[0]) != ChemicalElement.R) { goto continue_collapse; } } nbrSymbols.Add(sgroup.Subscript); todelete.Add(sgroup); } int numSGrpNbrs = nbrSymbols.Count; foreach (var bond in mol.GetConnectedBonds(attach)) { if (!xbonds.Contains(bond)) { var nbr = bond.GetOther(attach); // contract terminal bonds if (mol.GetConnectedBonds(nbr).Count() == 1) { if (nbr.MassNumber != null || (nbr.FormalCharge != null && nbr.FormalCharge != 0)) { newbonds.Add(bond); } else if (nbr.AtomicNumber == 1) { hcount++; xatoms.Add(nbr); } else if (nbr.AtomicNumber > 0) { nbrSymbols.Add(NewSymbol(nbr.AtomicNumber, nbr.ImplicitHydrogenCount.Value, false)); xatoms.Add(nbr); } } else { newbonds.Add(bond); } } } // reject if no symbols // reject if no bonds (<1), except if all symbols are identical... (HashSet.size==1) // reject if more that 2 bonds if (!nbrSymbols.Any() || newbonds.Count < 1 && (new HashSet <string>(nbrSymbols).Count != 1) || newbonds.Count > 2) { continue; } // create the symbol var sb = new StringBuilder(); sb.Append(NewSymbol(attach.AtomicNumber, hcount, newbonds.Count == 0)); string prev = null; int count = 0; nbrSymbols.Sort((o1, o2) => { int cmp = o1.Length.CompareTo(o2.Length); if (cmp != 0) { return(cmp); } return(o1.CompareTo(o2)); }); foreach (string nbrSymbol in nbrSymbols) { if (nbrSymbol.Equals(prev)) { count++; } else { bool useParen = count == 0 || CountUpper(prev) > 1 || (prev != null && nbrSymbol.StartsWith(prev)); AppendGroup(sb, prev, count, useParen); prev = nbrSymbol; count = 1; } } AppendGroup(sb, prev, count, false); // remove existing foreach (var e in todelete) { newSgroups.Remove(e); } // create new var newSgroup = new Sgroup { Type = SgroupType.CtabAbbreviation, Subscript = sb.ToString() }; foreach (var bond in newbonds) { newSgroup.Bonds.Add(bond); } foreach (var atom in xatoms) { newSgroup.Atoms.Add(atom); } newSgroups.Add(newSgroup); foreach (var a in xatoms) { usedAtoms.Add(a); } continue_collapse: ; } return(newSgroups); }
private IRenderingElement GenerateSgroupBrackets(Sgroup sgroup, IList <SgroupBracket> brackets, IReadOnlyDictionary <IAtom, AtomSymbol> symbols, string subscriptSuffix, string superscriptSuffix) { // brackets are square by default (style:0) var style = (int?)sgroup.GetValue(SgroupKey.CtabBracketStyle); bool round = style != null && style == 1; var result = new ElementGroup(); var atoms = sgroup.Atoms; var crossingBonds = sgroup.Bonds; // easy to depict in correct orientation, we just // point each bracket at the atom of a crossing // bond that is 'in' the group - this scales // to more than two brackets // first we need to pair the brackets with the bonds var pairs = crossingBonds.Count == brackets.Count ? BracketBondPairs(brackets, crossingBonds) : Dictionaries.Empty <SgroupBracket, IBond>(); // override bracket layout around single atoms to bring them in closer if (atoms.Count == 1) { var atom = atoms.First(); // e.g. 2 HCL, 8 H2O etc. if (IsUnsignedInt(subscriptSuffix) && !crossingBonds.Any() && symbols.ContainsKey(atom)) { var prefix = new TextOutline('·' + subscriptSuffix, font, emSize).Resize(1 / scale, 1 / -scale); var prefixBounds = prefix.LogicalBounds; var symbol = symbols[atom]; var bounds = symbol.GetConvexHull().Outline.Bounds; // make slightly large bounds = new Rect(bounds.Bottom - 2 * stroke, bounds.Left - 2 * stroke, bounds.Width + 4 * stroke, bounds.Height + 4 * stroke); prefix = prefix.Translate(bounds.Bottom - prefixBounds.Top, symbol.GetAlignmentCenter().Y - prefixBounds.CenterY()); result.Add(GeneralPath.ShapeOf(prefix.GetOutline(), foreground)); } // e.g. CC(O)nCC else if (crossingBonds.Count > 0) { var scriptscale = labelScale; var leftBracket = new TextOutline("(", font, emSize).Resize(1 / scale, 1 / -scale); var rightBracket = new TextOutline(")", font, emSize).Resize(1 / scale, 1 / -scale); var leftCenter = leftBracket.GetCenter(); var rightCenter = rightBracket.GetCenter(); if (symbols.ContainsKey(atom)) { var symbol = symbols[atom]; var bounds = symbol.GetConvexHull().Outline.Bounds; // make slightly large bounds = new Rect(bounds.Left - 2 * stroke, bounds.Top - 2 * stroke, bounds.Width + 4 * stroke, bounds.Height + 4 * stroke); leftBracket = leftBracket.Translate(bounds.Left - 0.1 - leftCenter.X, symbol.GetAlignmentCenter().Y - leftCenter.Y); rightBracket = rightBracket.Translate(bounds.Right + 0.1 - rightCenter.X, symbol.GetAlignmentCenter().Y - rightCenter.Y); } else { var p = atoms.First().Point2D.Value; leftBracket = leftBracket.Translate(p.X - 0.2 - leftCenter.X, p.Y - leftCenter.Y); rightBracket = rightBracket.Translate(p.X + 0.2 - rightCenter.X, p.Y - rightCenter.Y); } result.Add(GeneralPath.ShapeOf(leftBracket.GetOutline(), foreground)); result.Add(GeneralPath.ShapeOf(rightBracket.GetOutline(), foreground)); var rightBracketBounds = rightBracket.GetBounds(); // subscript/superscript suffix annotation if (subscriptSuffix != null && subscriptSuffix.Any()) { TextOutline subscriptOutline = LeftAlign(MakeText(subscriptSuffix.ToLowerInvariant(), new Vector2(rightBracketBounds.Right, rightBracketBounds.Top - 0.1), new Vector2(-0.5 * rightBracketBounds.Width, 0), scriptscale)); result.Add(GeneralPath.ShapeOf(subscriptOutline.GetOutline(), foreground)); } if (superscriptSuffix != null && superscriptSuffix.Any()) { var superscriptOutline = LeftAlign(MakeText(superscriptSuffix.ToLowerInvariant(), new Vector2(rightBracketBounds.Right, rightBracketBounds.Bottom + 0.1), new Vector2(-rightBracketBounds.Width, 0), scriptscale)); result.Add(GeneralPath.ShapeOf(superscriptOutline.GetOutline(), foreground)); } } } else if (pairs.Any()) { SgroupBracket suffixBracket = null; Vector2? suffixBracketPerp = null; foreach (var e in pairs) { var bracket = e.Key; var bond = e.Value; var inGroupAtom = atoms.Contains(bond.Begin) ? bond.Begin : bond.End; var p1 = bracket.FirstPoint; var p2 = bracket.SecondPoint; var perp = VecmathUtil.NewPerpendicularVector(VecmathUtil.NewUnitVector(p1, p2)); // point the vector at the atom group var midpoint = VecmathUtil.Midpoint(p1, p2); if (Vector2.Dot(perp, VecmathUtil.NewUnitVector(midpoint, inGroupAtom.Point2D.Value)) < 0) { perp = Vector2.Negate(perp); } perp *= bracketDepth; if (round) { result.Add(CreateRoundBracket(p1, p2, perp, midpoint)); } else { result.Add(CreateSquareBracket(p1, p2, perp)); } if (suffixBracket == null) { suffixBracket = bracket; suffixBracketPerp = perp; } else { // is this bracket better as a suffix? var sp1 = suffixBracket.FirstPoint; var sp2 = suffixBracket.SecondPoint; var bestMaxX = Math.Max(sp1.X, sp2.X); var thisMaxX = Math.Max(p1.X, p2.X); var bestMaxY = Math.Max(sp1.Y, sp2.Y); var thisMaxY = Math.Max(p1.Y, p2.Y); // choose the most eastern or.. the most southern var xDiff = thisMaxX - bestMaxX; var yDiff = thisMaxY - bestMaxY; if (xDiff > EQUIV_THRESHOLD || (xDiff > -EQUIV_THRESHOLD && yDiff < -EQUIV_THRESHOLD)) { suffixBracket = bracket; suffixBracketPerp = perp; } } } // write the labels if (suffixBracket != null) { var subSufPnt = suffixBracket.FirstPoint; var supSufPnt = suffixBracket.SecondPoint; // try to put the subscript on the bottom var xDiff = subSufPnt.X - supSufPnt.X; var yDiff = subSufPnt.Y - supSufPnt.Y; if (yDiff > EQUIV_THRESHOLD || (yDiff > -EQUIV_THRESHOLD && xDiff > EQUIV_THRESHOLD)) { var tmpP = subSufPnt; subSufPnt = supSufPnt; supSufPnt = tmpP; } // subscript/superscript suffix annotation if (subscriptSuffix != null && subscriptSuffix.Any()) { var subscriptOutline = LeftAlign(MakeText(subscriptSuffix.ToLowerInvariant(), subSufPnt, suffixBracketPerp.Value, labelScale)); result.Add(GeneralPath.ShapeOf(subscriptOutline.GetOutline(), foreground)); } if (superscriptSuffix != null && superscriptSuffix.Any()) { var superscriptOutline = LeftAlign(MakeText(superscriptSuffix.ToLowerInvariant(), supSufPnt, suffixBracketPerp.Value, labelScale)); result.Add(GeneralPath.ShapeOf(superscriptOutline.GetOutline(), foreground)); } } } else if (brackets.Count == 2) { var b1p1 = brackets[0].FirstPoint; var b1p2 = brackets[0].SecondPoint; var b2p1 = brackets[1].FirstPoint; var b2p2 = brackets[1].SecondPoint; var b1vec = VecmathUtil.NewUnitVector(b1p1, b1p2); var b2vec = VecmathUtil.NewUnitVector(b2p1, b2p2); var b1pvec = VecmathUtil.NewPerpendicularVector(b1vec); var b2pvec = VecmathUtil.NewPerpendicularVector(b2vec); // Point the vectors at each other if (Vector2.Dot(b1pvec, VecmathUtil.NewUnitVector(b1p1, b2p1)) < 0) { b1pvec = Vector2.Negate(b1pvec); } if (Vector2.Dot(b2pvec, VecmathUtil.NewUnitVector(b2p1, b1p1)) < 0) { b2pvec = Vector2.Negate(b2pvec); } // scale perpendicular vectors by how deep the brackets need to be b1pvec *= bracketDepth; b2pvec *= bracketDepth; // bad brackets if (double.IsNaN(b1pvec.X) || double.IsNaN(b1pvec.Y) || double.IsNaN(b2pvec.X) || double.IsNaN(b2pvec.Y)) { return(result); } { var path = new PathGeometry(); if (round) { { // bracket 1 (cp: control point) var pf = new PathFigure { StartPoint = new Point(b1p1.X + b1pvec.X, b1p1.Y + b1pvec.Y) }; Vector2 cpb1 = VecmathUtil.Midpoint(b1p1, b1p2); cpb1 += VecmathUtil.Negate(b1pvec); var seg = new QuadraticBezierSegment { Point1 = new Point(cpb1.X, cpb1.Y), Point2 = new Point(b1p2.X + b1pvec.X, b1p2.Y + b1pvec.Y) }; pf.Segments.Add(seg); path.Figures.Add(pf); } { // bracket 2 (cp: control point) var pf = new PathFigure { StartPoint = new Point(b2p1.X + b2pvec.X, b2p1.Y + b2pvec.Y) }; var cpb2 = VecmathUtil.Midpoint(b2p1, b2p2); cpb2 += VecmathUtil.Negate(b2pvec); var seg = new QuadraticBezierSegment { Point1 = new Point(cpb2.X, cpb2.Y), Point2 = new Point(b2p2.X + b2pvec.X, b2p2.Y + b2pvec.Y) }; pf.Segments.Add(seg); path.Figures.Add(pf); } } else { { // bracket 1 var pf = new PathFigure { StartPoint = new Point(b1p1.X + b1pvec.X, b1p1.Y + b1pvec.Y) }; var seg = new PolyLineSegment(); seg.Points.Add(new Point(b1p1.X, b1p1.Y)); seg.Points.Add(new Point(b1p2.X, b1p2.Y)); seg.Points.Add(new Point(b1p2.X + b1pvec.X, b1p2.Y + b1pvec.Y)); pf.Segments.Add(seg); path.Figures.Add(pf); } { // bracket 2 var pf = new PathFigure { StartPoint = new Point(b2p1.X + b2pvec.X, b2p1.Y + b2pvec.Y) }; var seg = new PolyLineSegment(); seg.Points.Add(new Point(b2p1.X, b2p1.Y)); seg.Points.Add(new Point(b2p2.X, b2p2.Y)); seg.Points.Add(new Point(b2p2.X + b2pvec.X, b2p2.Y + b2pvec.Y)); pf.Segments.Add(seg); path.Figures.Add(pf); } } result.Add(GeneralPath.OutlineOf(path, stroke, foreground)); } // work out where to put the suffix labels (e.g. ht/hh/eu) superscript // and (e.g. n, xl, c, mix) subscript // TODO: could be improved var b1MaxX = Math.Max(b1p1.X, b1p2.X); var b2MaxX = Math.Max(b2p1.X, b2p2.X); var b1MaxY = Math.Max(b1p1.Y, b1p2.Y); var b2MaxY = Math.Max(b2p1.Y, b2p2.Y); var subSufPnt = b2p2; var supSufPnt = b2p1; var subpvec = b2pvec; var bXDiff = b1MaxX - b2MaxX; var bYDiff = b1MaxY - b2MaxY; if (bXDiff > EQUIV_THRESHOLD || (bXDiff > -EQUIV_THRESHOLD && bYDiff < -EQUIV_THRESHOLD)) { subSufPnt = b1p2; supSufPnt = b1p1; subpvec = b1pvec; } var xDiff = subSufPnt.X - supSufPnt.X; var yDiff = subSufPnt.Y - supSufPnt.Y; if (yDiff > EQUIV_THRESHOLD || (yDiff > -EQUIV_THRESHOLD && xDiff > EQUIV_THRESHOLD)) { var tmpP = subSufPnt; subSufPnt = supSufPnt; supSufPnt = tmpP; } // subscript/superscript suffix annotation if (subscriptSuffix != null && subscriptSuffix.Any()) { var subscriptOutline = LeftAlign(MakeText(subscriptSuffix.ToLowerInvariant(), subSufPnt, subpvec, labelScale)); result.Add(GeneralPath.ShapeOf(subscriptOutline.GetOutline(), foreground)); } if (superscriptSuffix != null && superscriptSuffix.Any()) { var superscriptOutline = LeftAlign(MakeText(superscriptSuffix.ToLowerInvariant(), supSufPnt, subpvec, labelScale)); result.Add(GeneralPath.ShapeOf(superscriptOutline.GetOutline(), foreground)); } } return(result); }
/// <summary> /// Generates polymer Sgroup elements. /// </summary> /// <param name="sgroup">the Sgroup</param> /// <returns>the rendered elements (empty if no brackets defined)</returns> private IRenderingElement GeneratePolymerSgroup(Sgroup sgroup, IReadOnlyDictionary <IAtom, AtomSymbol> symbolMap) { // draw the brackets var brackets = (IList <SgroupBracket>)sgroup.GetValue(SgroupKey.CtabBracket); if (brackets != null) { var type = sgroup.Type; var subscript = (string)sgroup.GetValue(SgroupKey.CtabSubScript); var connectivity = (string)sgroup.GetValue(SgroupKey.CtabConnectivity); switch (type) { case SgroupType.CtabCopolymer: subscript = "co"; string subtype = (string)sgroup.GetValue(SgroupKey.CtabSubType); if (string.Equals("RAN", subtype, StringComparison.Ordinal)) { subscript = "ran"; } else if (string.Equals("BLK", subtype, StringComparison.Ordinal)) { subscript = "blk"; } else if (string.Equals("ALT", subtype, StringComparison.Ordinal)) { subscript = "alt"; } break; case SgroupType.CtabCrossLink: subscript = "xl"; break; case SgroupType.CtabAnyPolymer: subscript = "any"; break; case SgroupType.CtabGraft: subscript = "grf"; break; case SgroupType.CtabMer: subscript = "mer"; break; case SgroupType.CtabMonomer: subscript = "mon"; break; case SgroupType.CtabModified: subscript = "mod"; break; case SgroupType.CtabStructureRepeatUnit: if (subscript == null) { subscript = "n"; } if (connectivity == null) { connectivity = "eu"; } break; } // connectivity doesn't matter if symmetric... which is hard to test // here but we can certainly ignore it for single atoms (e.g. methylene) // also when we see brackets we presume head-to-tail repeating if ("ht".Equals(connectivity) || sgroup.Atoms.Count == 1) { connectivity = null; } return(GenerateSgroupBrackets(sgroup, brackets, symbolMap, subscript, connectivity)); } else { return(new ElementGroup()); } }
private IRenderingElement GenerateAbbreviationSgroup(IAtomContainer mol, Sgroup sgroup) { string label = sgroup.Subscript; // already handled by symbol remapping if (sgroup.Bonds.Count > 0 || string.IsNullOrEmpty(label)) { return(new ElementGroup()); } if (!CheckAbbreviationHighlight(mol, sgroup)) { return(new ElementGroup()); } // we're showing a label where there were no atoms before, we put it in the // middle of all of those which were hidden var sgroupAtoms = sgroup.Atoms; Debug.Assert(sgroupAtoms.Any()); var highlight = sgroupAtoms.First().GetProperty <Color>(StandardGenerator.HighlightColorKey); var style = parameters.GetHighlighting(); var glowWidth = parameters.GetOuterGlowWidth(); Vector2 labelLocation; if (mol.Atoms.Count == sgroup.Atoms.Count) { labelLocation = GeometryUtil.Get2DCenter(sgroupAtoms); } else { // contraction of part of a fragment, e.g. SALT // here we work out the point we want to place the contract relative // to the SGroup Atoms labelLocation = new Vector2(); var sgrpCenter = GeometryUtil.Get2DCenter(sgroupAtoms); var molCenter = GeometryUtil.Get2DCenter(mol); var minMax = GeometryUtil.GetMinMax(sgroupAtoms); var xDiff = sgrpCenter.X - molCenter.X; var yDiff = sgrpCenter.Y - molCenter.Y; if (xDiff > 0.1) { labelLocation.X = minMax[0]; // min x label = INTERPUNCT + label; } else if (xDiff < -0.1) { labelLocation.X = minMax[2]; // max x label = label + INTERPUNCT; } else { labelLocation.X = sgrpCenter.X; label = INTERPUNCT + label; } if (yDiff > 0.1) { labelLocation.Y = minMax[1]; // min y } else if (yDiff < -0.1) { labelLocation.Y = minMax[3]; // max y } else { labelLocation.Y = sgrpCenter.Y; } } var labelgroup = new ElementGroup(); foreach (var outline in atomGenerator.GenerateAbbreviatedSymbol(label, HydrogenPosition.Right) .Center(labelLocation.X, labelLocation.Y) .Resize(1 / scale, 1 / -scale) .GetOutlines()) { if (highlight != null && style == HighlightStyle.Colored) { labelgroup.Add(GeneralPath.ShapeOf(outline, highlight)); } else { labelgroup.Add(GeneralPath.ShapeOf(outline, foreground)); } } if (highlight != null && style == HighlightStyle.OuterGlow) { var group = new ElementGroup { // outer glow needs to be being the label StandardGenerator.OuterGlow(labelgroup, highlight, glowWidth, stroke), labelgroup }; return(group); } else { return(MarkedElement.MarkupAtom(labelgroup, null)); } }
/// <summary> /// Reads the bond atoms, order and stereo configuration. /// </summary> public void ReadBondBlock(IAtomContainer readData) { Trace.TraceInformation("Reading BOND block"); bool foundEND = false; while (!foundEND) { string command = ReadCommand(ReadLine()); if (string.Equals("END BOND", command, StringComparison.Ordinal)) { foundEND = true; } else { Debug.WriteLine($"Parsing bond from: {command}"); var tokenizer = Strings.Tokenize(command).GetEnumerator(); IBond bond = readData.Builder.NewBond(); // parse the index try { tokenizer.MoveNext(); string indexString = tokenizer.Current; bond.Id = indexString; } catch (Exception exception) { string error = "Error while parsing bond index"; Trace.TraceError(error); Debug.WriteLine(exception); throw new CDKException(error, exception); } // parse the order try { tokenizer.MoveNext(); string orderString = tokenizer.Current; int order = int.Parse(orderString, NumberFormatInfo.InvariantInfo); if (order >= 4) { Trace.TraceWarning("Query order types are not supported (yet). File a bug if you need it"); } else { bond.Order = BondManipulator.CreateBondOrder((double)order); } } catch (Exception exception) { string error = "Error while parsing bond index"; Trace.TraceError(error); Debug.WriteLine(exception); throw new CDKException(error, exception); } // parse index atom 1 try { tokenizer.MoveNext(); string indexAtom1String = tokenizer.Current; int indexAtom1 = int.Parse(indexAtom1String, NumberFormatInfo.InvariantInfo); IAtom atom1 = readData.Atoms[indexAtom1 - 1]; bond.Atoms.Add(atom1); // bond.Atoms[0] } catch (Exception exception) { string error = "Error while parsing index atom 1 in bond"; Trace.TraceError(error); Debug.WriteLine(exception); throw new CDKException(error, exception); } // parse index atom 2 try { tokenizer.MoveNext(); string indexAtom2String = tokenizer.Current; int indexAtom2 = int.Parse(indexAtom2String, NumberFormatInfo.InvariantInfo); IAtom atom2 = readData.Atoms[indexAtom2 - 1]; bond.Atoms.Add(atom2); // bond.Atoms[1] } catch (Exception exception) { string error = "Error while parsing index atom 2 in bond"; Trace.TraceError(error); Debug.WriteLine(exception); throw new CDKException(error, exception); } var endpts = new List <IAtom>(); string attach = null; // the rest are key=value fields if (command.IndexOf('=') != -1) { var options = ParseOptions(ExhaustStringTokenizer(tokenizer)); foreach (var key in options.Keys) { string value = options[key]; try { switch (key) { case "CFG": int configuration = int.Parse(value, NumberFormatInfo.InvariantInfo); if (configuration == 0) { bond.Stereo = BondStereo.None; } else if (configuration == 1) { bond.Stereo = BondStereo.Up; } else if (configuration == 2) { bond.Stereo = BondStereo.None; } else if (configuration == 3) { bond.Stereo = BondStereo.Down; } break; case "ENDPTS": string[] endptStr = value.Split(' '); // skip first value that is count for (int i = 1; i < endptStr.Length; i++) { endpts.Add(readData.Atoms[int.Parse(endptStr[i], NumberFormatInfo.InvariantInfo) - 1]); } break; case "ATTACH": attach = value; break; default: Trace.TraceWarning("Not parsing key: " + key); break; } } catch (Exception exception) { string error = "Error while parsing key/value " + key + "=" + value + ": " + exception.Message; Trace.TraceError(error); Debug.WriteLine(exception); throw new CDKException(error, exception); } } } // storing bond readData.Bonds.Add(bond); // storing positional variation if (string.Equals("ANY", attach, StringComparison.Ordinal)) { Sgroup sgroup = new Sgroup { Type = SgroupType.ExtMulticenter }; sgroup.Atoms.Add(bond.Begin); // could be other end? sgroup.Bonds.Add(bond); foreach (var endpt in endpts) { sgroup.Atoms.Add(endpt); } var sgroups = readData.GetCtabSgroups(); if (sgroups == null) { readData.SetCtabSgroups(sgroups = new List <Sgroup>(4)); } sgroups.Add(sgroup); } Debug.WriteLine($"Added bond: {bond}"); } } }
/// <summary> /// Hide the atoms and bonds of a contracted abbreviation. If the abbreviations is attached /// we remap the attachment symbol to display the name. If there are no attachments the symbol /// we be added later ({@see #generateSgroups}). /// </summary> /// <param name="container">molecule</param> /// <param name="sgroup">abbreviation group display shortcut</param> private static void ContractAbbreviation(IAtomContainer container, IDictionary <IAtom, string> symbolRemap, Sgroup sgroup) { var crossing = sgroup.Bonds; var atoms = sgroup.Atoms; // only do 0,1 attachments for now if (crossing.Count > 1) { return; } foreach (var atom in atoms) { StandardGenerator.Hide(atom); } foreach (var bond in container.Bonds) { if (atoms.Contains(bond.Begin) || atoms.Contains(bond.End)) { StandardGenerator.Hide(bond); } } foreach (var bond in crossing) { StandardGenerator.Unhide(bond); var a1 = bond.Begin; var a2 = bond.End; StandardGenerator.Unhide(a1); if (atoms.Contains(a1)) { symbolRemap[a1] = sgroup.Subscript; } StandardGenerator.Unhide(a2); if (atoms.Contains(a2)) { symbolRemap[a2] = sgroup.Subscript; } } }
/// <summary> /// Transfers the CXSMILES state onto the CDK atom/molecule data-structures. /// </summary> /// <param name="bldr">chem-object builder</param> /// <param name="atoms">atoms parsed from the molecule or reaction. Reaction molecules are list left to right.</param> /// <param name="atomToMol">look-up of atoms to molecules when connectivity/sgroups need modification</param> /// <param name="cxstate">the CXSMILES state to read from</param> private void AssignCxSmilesInfo(IChemObjectBuilder bldr, IChemObject chemObj, List <IAtom> atoms, Dictionary <IAtom, IAtomContainer> atomToMol, CxSmilesState cxstate) { // atom-labels - must be done first as we replace atoms if (cxstate.atomLabels != null) { foreach (var e in cxstate.atomLabels) { // bounds check if (e.Key >= atoms.Count) { continue; } var old = atoms[e.Key]; var pseudo = bldr.NewPseudoAtom(); var val = e.Value; // specialised label handling if (val.EndsWith("_p", StringComparison.Ordinal)) // pseudo label { val = val.Substring(0, val.Length - 2); } else if (val.StartsWith("_AP", StringComparison.Ordinal)) // attachment point { pseudo.AttachPointNum = ParseIntSafe(val.Substring(3)); } pseudo.Label = val; pseudo.AtomicNumber = 0; pseudo.ImplicitHydrogenCount = 0; var mol = atomToMol[old]; AtomContainerManipulator.ReplaceAtomByAtom(mol, old, pseudo); atomToMol.Add(pseudo, mol); atoms[e.Key] = pseudo; } } // atom-values - set as comment, mirrors Molfile reading behavior if (cxstate.atomValues != null) { foreach (var e in cxstate.atomValues) { atoms[e.Key].SetProperty(CDKPropertyName.Comment, e.Value); } } // atom-coordinates if (cxstate.atomCoords != null) { var numAtoms = atoms.Count; var numCoords = cxstate.atomCoords.Count; var lim = Math.Min(numAtoms, numCoords); if (cxstate.coordFlag) { for (int i = 0; i < lim; i++) { atoms[i].Point3D = new Vector3( cxstate.atomCoords[i][0], cxstate.atomCoords[i][1], cxstate.atomCoords[i][2]); } } else { for (int i = 0; i < lim; i++) { atoms[i].Point2D = new Vector2( cxstate.atomCoords[i][0], cxstate.atomCoords[i][1]); } } } // atom radicals if (cxstate.atomRads != null) { foreach (var e in cxstate.atomRads) { // bounds check if (e.Key >= atoms.Count) { continue; } int count = 0; var aa = e.Value; switch (e.Value) { case CxSmilesState.Radical.Monovalent: count = 1; break; // no distinction in CDK between singled/triplet case CxSmilesState.Radical.Divalent: case CxSmilesState.Radical.DivalentSinglet: case CxSmilesState.Radical.DivalentTriplet: count = 2; break; // no distinction in CDK between doublet/quartet case CxSmilesState.Radical.Trivalent: case CxSmilesState.Radical.TrivalentDoublet: case CxSmilesState.Radical.TrivalentQuartet: count = 3; break; } var atom = atoms[e.Key]; var mol = atomToMol[atom]; while (count-- > 0) { mol.SingleElectrons.Add(bldr.NewSingleElectron(atom)); } } } var sgroupMap = new MultiDictionary <IAtomContainer, Sgroup>(); // positional-variation if (cxstate.positionVar != null) { foreach (var e in cxstate.positionVar) { var sgroup = new Sgroup { Type = SgroupType.ExtMulticenter }; var beg = atoms[e.Key]; var mol = atomToMol[beg]; var bonds = mol.GetConnectedBonds(beg); if (bonds.Count() == 0) { continue; // bad } sgroup.Add(beg); sgroup.Add(bonds.First()); foreach (var endpt in e.Value) { sgroup.Add(atoms[endpt]); } sgroupMap.Add(mol, sgroup); } } // data sgroups if (cxstate.dataSgroups != null) { foreach (var dsgroup in cxstate.dataSgroups) { if (dsgroup.Field != null && dsgroup.Field.StartsWith("cdk:", StringComparison.Ordinal)) { chemObj.SetProperty(dsgroup.Field, dsgroup.Value); } } } // polymer Sgroups if (cxstate.sgroups != null) { foreach (var psgroup in cxstate.sgroups) { var sgroup = new Sgroup(); var atomset = new HashSet <IAtom>(); IAtomContainer mol = null; foreach (var idx in psgroup.AtomSet) { if (idx >= atoms.Count) { continue; } var atom = atoms[idx]; var amol = atomToMol[atom]; if (mol == null) { mol = amol; } else if (amol != mol) { goto C_PolySgroup; } atomset.Add(atom); } if (mol == null) { continue; } foreach (var atom in atomset) { foreach (var bond in mol.GetConnectedBonds(atom)) { if (!atomset.Contains(bond.GetOther(atom))) { sgroup.Add(bond); } } sgroup.Add(atom); } sgroup.Subscript = psgroup.Subscript; sgroup.PutValue(SgroupKey.CtabConnectivity, psgroup.Supscript); switch (psgroup.Type) { case "n": sgroup.Type = SgroupType.CtabStructureRepeatUnit; break; case "mon": sgroup.Type = SgroupType.CtabMonomer; break; case "mer": sgroup.Type = SgroupType.CtabMer; break; case "co": sgroup.Type = SgroupType.CtabCopolymer; break; case "xl": sgroup.Type = SgroupType.CtabCrossLink; break; case "mod": sgroup.Type = SgroupType.CtabModified; break; case "mix": sgroup.Type = SgroupType.CtabMixture; break; case "f": sgroup.Type = SgroupType.CtabFormulation; break; case "any": sgroup.Type = SgroupType.CtabAnyPolymer; break; case "gen": sgroup.Type = SgroupType.CtabGeneric; break; case "c": sgroup.Type = SgroupType.CtabComponent; break; case "grf": sgroup.Type = SgroupType.CtabGraft; break; case "alt": sgroup.Type = SgroupType.CtabCopolymer; sgroup.PutValue(SgroupKey.CtabSubType, "ALT"); break; case "ran": sgroup.Type = SgroupType.CtabCopolymer; sgroup.PutValue(SgroupKey.CtabSubType, "RAN"); break; case "blk": sgroup.Type = SgroupType.CtabCopolymer; sgroup.PutValue(SgroupKey.CtabSubType, "BLO"); break; } sgroupMap.Add(mol, sgroup); C_PolySgroup: ; } } // assign Sgroups foreach (var e in sgroupMap) { e.Key.SetCtabSgroups(new List <Sgroup>(e.Value)); } }
/// <summary> /// Copy a collection of Sgroups, replacing any <see cref="IAtom"/>/<see cref="IBond"/> /// references with those present in the provided 'replace' map. If an empty /// replace map is provided (null or empty) the sgroups are simply /// duplicated. If an item is not present in the replacement map the original /// item is preserved. /// </summary> /// <example> /// <code> /// var replace = new Dictionary<Sgroup, Sgroup>(); /// replace[orgAtom] = newAtom; /// replace[orgBond] = newBond; /// newSgroups = Copy(orgSgroups, replace); /// </code> /// </example> /// <param name="sgroups">collection of sgroups, can be null</param> /// <param name="replace">the replacement map, can be null</param> /// <returns>list of copied sgroups, null if sgroups input was null</returns> public static IList <Sgroup> Copy(IList <Sgroup> sgroups, CDKObjectMap replace) { if (sgroups == null) { return(null); } var sgroupMap = new Dictionary <Sgroup, Sgroup>(); foreach (var sgroup in sgroups) { sgroupMap[sgroup] = new Sgroup(); } foreach (var e in sgroupMap) { var orgSgroup = e.Key; var cpySgroup = e.Value; cpySgroup.Type = orgSgroup.Type; foreach (var atom in orgSgroup.Atoms) { cpySgroup.Atoms.Add(Get(replace, atom)); } foreach (var bond in orgSgroup.Bonds) { cpySgroup.Bonds.Add(Get(replace, bond)); } foreach (var parent in orgSgroup.Parents) { cpySgroup.Parents.Add(sgroupMap[parent]); } foreach (var key in SgroupTool.SgroupKeyValues) { switch (key) { case SgroupKey.CtabParentAtomList: { var orgVal = (ICollection <IAtom>)orgSgroup.GetValue(key); if (orgVal != null) { var cpyVal = new List <IAtom>(); foreach (IAtom atom in orgVal) { cpyVal.Add(Get(replace, atom)); } cpySgroup.PutValue(key, cpyVal); } } break; case SgroupKey.CtabBracket: { var orgVals = (ICollection <SgroupBracket>)orgSgroup.GetValue(key); if (orgVals != null) { foreach (var bracket in orgVals) { cpySgroup.AddBracket(new SgroupBracket(bracket)); } } } break; default: // primitive values, String, Integer are immutable object val = orgSgroup.GetValue(key); if (val != null) { cpySgroup.PutValue(key, val); } break; } } } return(new List <Sgroup>(sgroupMap.Values)); }