示例#1
0
        internal static ElementBase GetChemicalElement(XElement cmlElement, out string message)
        {
            message = "";
            XAttribute xa = cmlElement.Attribute(CMLConstants.AttributeElementType);

            if (xa != null)
            {
                string      symbol = xa.Value;
                ElementBase eb;
                AtomHelpers.TryParse(symbol, out eb);

                if (eb is Element element)
                {
                    return(element);
                }

                if (eb is FunctionalGroup functionalGroup)
                {
                    return(functionalGroup);
                }

                //if we got here then it went very wrong
                message = $"Unrecognised element '{symbol}' in {cmlElement}";
            }
            else
            {
                message = $"cml attribute 'elementType' missing from {cmlElement}";
            }

            return(null);
        }
示例#2
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="drawingContext"></param>
        /// <param name="mainAtomMetrics"></param>
        /// <param name="hMetrics"></param>
        /// <param name="isoMetrics"></param>
        /// <param name="defaultHOrientation"></param>
        /// <returns></returns>
        private LabelMetrics DrawCharges(DrawingContext drawingContext,
                                         AtomTextMetrics mainAtomMetrics,
                                         AtomTextMetrics hMetrics,
                                         LabelMetrics isoMetrics,
                                         CompassPoints defaultHOrientation)
        {
            var chargeString = AtomHelpers.GetChargeString(Charge);
            var chargeText   = DrawChargeOrRadical(drawingContext, mainAtomMetrics, hMetrics, isoMetrics, chargeString, Fill, defaultHOrientation);

            chargeText.TextMetrics.FlattenedPath = chargeText.TextRun.GetOutline();
            return(chargeText.TextMetrics);
        }
示例#3
0
            /// <summary>
            /// Measures the dimensions of the atom prior to rendering
            /// </summary>
            /// <param name="parentMetrics">Metrics of the parent atom</param>
            /// <param name="direction">Orientation of the group relative to the parent atom, i.e. NESW</param>
            /// <returns>AtomTextMetrics object describing placement</returns>
            public AtomTextMetrics Measure(AtomTextMetrics parentMetrics, CompassPoints direction, float pixelsPerDip)
            {
                _subText = null;

                List <Point> mainOutline;

                //first, get some initial size measurements
                _mainText = new GlyphText(Text, SymbolTypeface, _fontSize, pixelsPerDip);
                _mainText.Premeasure();

                //measure up the subscript (if we have one)
                string subscriptText = AtomHelpers.GetSubText(Count);

                if (subscriptText != "")
                {
                    _subText = new SubLabelText(subscriptText, pixelsPerDip);
                    _subText.Premeasure();
                }

                //calculate the center of the H Atom depending on the direction
                var groupCenter = GetAdjunctCenter(parentMetrics, direction, _mainText.GlyphInfo, _subText?.GlyphInfo);

                //remeasure the main text
                _mainText.MeasureAtCenter(groupCenter);

                mainOutline = _mainText.FlattenedPath;

                if (_subText != null)
                //get the offset for the subscript
                {
                    Vector subscriptOffset = new Vector(_mainText.TextMetrics.TotalBoundingBox.Width + _mainText.TrailingBearing + _subText.LeadingBearing,
                                                        _subText.TextMetrics.BoundingBox.Height / 2);
                    Point subBottomLeft = _mainText.TextMetrics.TotalBoundingBox.BottomLeft + subscriptOffset;
                    _subText.MeasureAtBottomLeft(subBottomLeft, pixelsPerDip);
                    //merge the total bounding boxes
                    _mainText.Union(_subText);
                    mainOutline.AddRange(_subText.FlattenedPath);
                }
                //return the placement metrics for the subscripted atom.
                AtomTextMetrics result = new AtomTextMetrics
                {
                    Geocenter        = groupCenter,
                    BoundingBox      = _mainText.TextMetrics.BoundingBox,
                    TotalBoundingBox = _mainText.TextMetrics.TotalBoundingBox,
                    FlattenedPath    = mainOutline
                };

                return(result);
            }
示例#4
0
        internal static ElementBase GetElementOrFunctionalGroup(XElement cmlElement, out string message)
        {
            message = "";
            XAttribute xa = cmlElement.Attribute(CMLConstants.AttributeElementType);

            if (xa != null)
            {
                string      symbol = xa.Value;
                ElementBase eb;
                AtomHelpers.TryParse(symbol, out eb);

                if (eb is Element element)
                {
                    return(element);
                }

                if (eb is FunctionalGroup functionalGroup)
                {
                    // Fix Invalid data; Force internal FG to prime Element
                    if (functionalGroup.Internal)
                    {
                        AtomHelpers.TryParse(functionalGroup.Components[0].Component, out eb);
                        if (eb is Element chemicalElement)
                        {
                            return(chemicalElement);
                        }
                    }
                    return(functionalGroup);
                }

                //if we got here then it went very wrong
                message = $"Unrecognised element '{symbol}' in {cmlElement}";
            }
            else
            {
                message = $"cml attribute 'elementType' missing from {cmlElement}";
            }

            return(null);
        }
示例#5
0
        public override void Render()
        {
            //draw the bracket first
            var bb        = _boundingBox;
            var serifSize = _molecule.Model.XamlBondLength * Globals.BracketFactor;

            bb.Inflate(new Size(serifSize, serifSize));
            Vector         horizontal   = new Vector(serifSize, 0.0);
            Brush          bracketBrush = new SolidColorBrush(Colors.Black);
            Pen            bracketPen   = new Pen(bracketBrush, Globals.BracketThickness);
            StreamGeometry sg           = new StreamGeometry();

            using (DrawingContext dc = RenderOpen())
            {
                using (StreamGeometryContext sgc = sg.Open())
                {
                    //left bracket
                    sgc.BeginFigure(bb.BottomLeft + horizontal, false, false);
                    sgc.LineTo(bb.BottomLeft, true, true);
                    sgc.LineTo(bb.TopLeft, true, true);
                    sgc.LineTo(bb.TopLeft + horizontal, true, true);
                    //right bracket
                    sgc.BeginFigure(bb.BottomRight - horizontal, false, false);
                    sgc.LineTo(bb.BottomRight, true, true);
                    sgc.LineTo(bb.TopRight, true, true);
                    sgc.LineTo(bb.TopRight - horizontal, true, true);
                    sgc.Close();
                }

                dc.DrawGeometry(bracketBrush, bracketPen, sg);

                //now draw the charges and radicals
                string chargeString = AtomHelpers.GetChargeString(_molecule.FormalCharge);
                int?   spinMult     = _molecule.SpinMultiplicity;

                if (_molecule.SpinMultiplicity.HasValue && _molecule.SpinMultiplicity.Value > 1)
                {
                    // Append SpinMultiplicity
                    switch (_molecule.SpinMultiplicity.Value)
                    {
                    case 2:
                        chargeString += "•";
                        break;

                    case 3:
                        chargeString += "••";
                        break;
                    }
                }

                if (chargeString != "")
                {
                    Point pos = bb.TopRight +
                                horizontal;
                    var mlv = new MoleculeLabelVisual(chargeString, pos, bracketBrush, _molecule.Model.XamlBondLength / 2.0d);
                    mlv.Render(dc);
                    AddVisualChild(mlv);
                }

                string countString = _molecule.Count.ToString();
                if (!string.IsNullOrEmpty(countString))
                {
                    Point pos = bb.BottomRight +
                                horizontal;
                    var mlv = new MoleculeLabelVisual(countString, pos, bracketBrush, _molecule.Model.XamlBondLength / 2.0d);
                    mlv.Render(dc, true);
                    AddVisualChild(mlv);
                }
            }
        }
示例#6
0
        private void ReadAtoms(StreamReader reader, int atoms)
        {
            int idx = 0;

            while (!reader.EndOfStream && idx < atoms)
            {
                string line = SdFileConverter.GetNextLine(reader);
                if (!string.IsNullOrEmpty(line))
                {
                    // create atom
                    Atom thisAtom = new Atom();

                    double x = Double.Parse(GetSubString(line, 0, 9), CultureInfo.InvariantCulture);
                    double y = Double.Parse(GetSubString(line, 10, 9), CultureInfo.InvariantCulture);
                    double z = Double.Parse(GetSubString(line, 20, 9), CultureInfo.InvariantCulture);

                    // Inverting Y co-ordinate to make it the right way up.
                    thisAtom.Position = new Point(x, 0 - y);

                    // element type
                    string      elType = GetSubString(line, 31, 3);
                    ElementBase eb;
                    var         ok = AtomHelpers.TryParse(elType, out eb);
                    if (ok)
                    {
                        if (eb is Element element)
                        {
                            thisAtom.Element = element;
                        }

                        if (eb is FunctionalGroup functionalGroup)
                        {
                            thisAtom.Element = functionalGroup;

                            // Fix Invalid data; Force internal FG to prime Element
                            if (functionalGroup.Internal)
                            {
                                AtomHelpers.TryParse(functionalGroup.Components[0].Component, out eb);
                                if (eb is Element chemicalElement)
                                {
                                    thisAtom.Element = chemicalElement;
                                }
                            }
                        }
                    }
                    else
                    {
                        _molecule.Errors.Add($"{elType} at Line {SdFileConverter.LineNumber} is not a valid Element");
                        _molecule.Errors.Add($"{line}");
                    }

                    // isotope
                    int delta = ParseInteger(line, 34, 2);
                    if (delta != 0)
                    {
                        thisAtom.IsotopeNumber = delta;
                    }

                    // charge
                    int ch = ParseInteger(line, 36, 3);
                    thisAtom.FormalCharge   = FormalChargeFromMolfile(ch);
                    thisAtom.DoubletRadical = DoubletRadicalFromMolfile(ch);

                    // field 3 is atom parity (a *write-only* field, so not used)
                    // int parity = ParseInteger(line, 39, 3)

                    // field 4 hydrogen count
                    // int hCount = ParseInteger(line, 42, 3)

                    // field 5 stereoCareBox
                    // int field5 = ParseInteger(line, 45, 3)

                    // field 6 valency/oxidation state
                    // int oxState = ParseInteger(line, 48, 3)

                    // field 7 H0 designator
                    // int hZero = ParseInteger(line, 51, 3)

                    // atom-atom mapping
                    int atomMap = ParseInteger(line, 60, 3);
                    if (atomMap != 0)
                    {
                        // ToDo: What to do here ???
                    }

                    // inversion/retention flag
                    // int hZero = ParseInteger(line, 63, 3)

                    // exact change flag
                    // int hZero = ParseInteger(line, 66, 3)

                    idx++;

                    // Add atom to molecule
                    thisAtom.Id = $"a{idx}";
                    _molecule.AddAtom(thisAtom);
                    thisAtom.Parent = _molecule;
                    atomByNumber.Add(idx, thisAtom);
                }
            }
        }
示例#7
0
        private static void AddMolecule(dynamic data, Model newModel)
        {
            Dictionary <int, string> atoms = new Dictionary <int, string>();
            var         newMol             = new Molecule();
            ElementBase ce        = Globals.PeriodicTable.C;
            int         atomCount = 0;

            // GitHub: Issue #13 https://github.com/Chem4Word/Version3/issues/13
            if (data.a != null)
            {
                foreach (AtomJSON a in data.a)
                {
                    if (!string.IsNullOrEmpty(a.l))
                    {
                        ElementBase eb;
                        var         ok = AtomHelpers.TryParse(a.l, out eb);
                        if (ok)
                        {
                            if (eb is Element element)
                            {
                                ce = element;
                            }

                            if (eb is FunctionalGroup functionalGroup)
                            {
                                ce = functionalGroup;
                            }
                        }
                    }
                    else
                    {
                        ce = Globals.PeriodicTable.C;
                    }

                    Atom atom = new Atom()
                    {
                        Element  = ce,
                        Position = new Point(a.x, a.y)
                    };

                    if (a.c != null)
                    {
                        atom.FormalCharge = a.c.Value;
                    }

                    atoms.Add(atomCount++, atom.InternalId);
                    newMol.AddAtom(atom);
                    atom.Parent = newMol;
                }
            }

            if (data.b != null)
            {
                foreach (BondJSON b in data.b)
                {
                    string o;
                    if (b.o != null)
                    {
                        o = Globals.OrderValueToOrder(double.Parse(b.o.ToString()));
                    }
                    else
                    {
                        o = Globals.OrderSingle;
                    }

                    Globals.BondStereo s;
                    if (!string.IsNullOrEmpty(b.s))
                    {
                        if (o == Globals.OrderDouble)
                        {
                            if (b.s.Equals(Ambiguous))
                            {
                                s = Globals.BondStereo.Indeterminate;
                            }
                            else
                            {
                                s = Globals.BondStereo.None;
                            }
                        }
                        else
                        {
                            if (b.s.Equals(Recessed))
                            {
                                s = Globals.BondStereo.Hatch;
                            }
                            else if (b.s.Equals(Protruding))
                            {
                                s = Globals.BondStereo.Wedge;
                            }
                            else if (b.s.Equals(Ambiguous))
                            {
                                s = Globals.BondStereo.Indeterminate;
                            }
                            else
                            {
                                s = Globals.BondStereo.None;
                            }
                        }
                    }
                    else
                    {
                        s = Globals.BondStereo.None;
                    }

                    // Azure DevOps #715
                    if (b.b.HasValue && b.b.Value < atoms.Count && b.e.HasValue && b.e.Value < atoms.Count)
                    {
                        var  sa      = atoms[b.b.Value];
                        var  ea      = atoms[b.e.Value];
                        Bond newBond = new Bond()
                        {
                            StartAtomInternalId = sa,
                            EndAtomInternalId   = ea,
                            Stereo = s,
                            Order  = o
                        };
                        newMol.AddBond(newBond);
                        newBond.Parent = newMol;
                    }
                }
            }

            newModel.AddMolecule(newMol);
            newMol.Parent = newModel;
        }
示例#8
0
        /// <summary>
        /// Expand the Functional Group into a flattened list of terms
        /// </summary>
        /// <param name="reverse"></param>
        /// <param name="consolidate"></param>
        /// <returns></returns>
        public List <FunctionalGroupTerm> ExpandIntoTerms(bool reverse = false, bool consolidate = true)
        {
            List <FunctionalGroupTerm> result = new List <FunctionalGroupTerm>();

            if (ShowAsSymbol)
            {
                var term = new FunctionalGroupTerm
                {
                    IsAnchor = true
                };

                term.Parts = ExpandSymbol(Symbol);
                result.Add(term);
            }
            else
            {
                for (int i = 0; i < Components.Count; i++)
                {
                    var term = new FunctionalGroupTerm
                    {
                        IsAnchor = i == 0,
                        Parts    = ExpandGroupV2(Components[i])
                    };

                    result.Add(term);
                }
            }

            // Consolidate parts of each term
            if (consolidate)
            {
                foreach (var term in result)
                {
                    if (term.Parts.Count > 1)
                    {
                        var newParts = new List <FunctionalGroupPart>();

                        var newPart = term.Parts[0];
                        newParts.Add(newPart);

                        for (int i = 1; i < term.Parts.Count; i++)
                        {
                            if (term.Parts[i].Type == term.Parts[i - 1].Type)
                            {
                                newPart.Text += term.Parts[i].Text;
                            }
                            else
                            {
                                newPart = term.Parts[i];
                                newParts.Add(newPart);
                            }
                        }

                        term.Parts = newParts;
                    }
                }
            }

            if (Flippable && reverse)
            {
                result.Reverse();
            }

            return(result);

            // Local Functions

            // Ensure that Symbols such as "R{1}" and "{i}Pr" are expanded into parts
            List <FunctionalGroupPart> ExpandSymbol(string symbol)
            {
                List <FunctionalGroupPart> expanded = new List <FunctionalGroupPart>();

                if (symbol.Contains("{") || symbol.Contains("}"))
                {
                    var part = new FunctionalGroupPart();
                    foreach (char c in symbol)
                    {
                        switch (c)
                        {
                        case '{':
                            if (!string.IsNullOrEmpty(part.Text))
                            {
                                expanded.Add(part);
                            }

                            part = new FunctionalGroupPart
                            {
                                Type = FunctionalGroupPartType.Superscript
                            };
                            break;

                        case '}':
                            expanded.Add(part);
                            part = new FunctionalGroupPart();
                            break;

                        default:
                            part.Text += c;
                            break;
                        }
                    }

                    // Ensure that trailing characters are not lost
                    if (!string.IsNullOrEmpty(part.Text))
                    {
                        expanded.Add(part);
                    }
                }
                else
                {
                    expanded.Add(new FunctionalGroupPart
                    {
                        Text = symbol
                    });
                }

                return(expanded);
            }

            List <FunctionalGroupPart> ExpandGroupV2(Group componentGroup, bool flipped = false)
            {
                List <FunctionalGroupPart> expanded = new List <FunctionalGroupPart>();

                ElementBase elementBase;

                if (AtomHelpers.TryParse(componentGroup.Component, out elementBase))
                {
                    if (elementBase is Element element)
                    {
                        expanded.Add(new FunctionalGroupPart
                        {
                            Text = element.Symbol
                        });

                        if (componentGroup.Count != 1)
                        {
                            var part = new FunctionalGroupPart
                            {
                                Type = FunctionalGroupPartType.Subscript,
                                Text = $"{componentGroup.Count}"
                            };
                            expanded.Add(part);
                        }
                    }

                    if (elementBase is FunctionalGroup functionalGroup)
                    {
                        var part = new FunctionalGroupPart();

                        if (componentGroup.Count != 1)
                        {
                            part.Text += "(";
                            expanded.Add(part);
                            part = new FunctionalGroupPart();
                        }

                        if (functionalGroup.ShowAsSymbol)
                        {
                            if (!string.IsNullOrEmpty(part.Text))
                            {
                                expanded.Add(part);
                            }

                            expanded.AddRange(ExpandSymbol(functionalGroup.Symbol));
                            part = new FunctionalGroupPart();
                        }
                        else
                        {
                            if (functionalGroup.Flippable && flipped)
                            {
                                for (int ii = functionalGroup.Components.Count - 1; ii >= 0; ii--)
                                {
                                    expanded.AddRange(ExpandGroupV2(functionalGroup.Components[ii]));
                                }
                            }
                            else
                            {
                                foreach (var fgc in functionalGroup.Components)
                                {
                                    expanded.AddRange(ExpandGroupV2(fgc));
                                }
                            }
                        }

                        if (componentGroup.Count != 1)
                        {
                            part.Text += ")";
                            expanded.Add(part);

                            part = new FunctionalGroupPart
                            {
                                Type = FunctionalGroupPartType.Subscript,
                                Text = $"{componentGroup.Count}"
                            };
                            expanded.Add(part);

                            part = new FunctionalGroupPart();
                        }

                        // Ensure that trailing characters are not lost
                        if (!string.IsNullOrEmpty(part.Text))
                        {
                            expanded.Add(part);
                        }
                    }
                }

                return(expanded);
            }
        }