public void GeneratesRgroupPseudoAtom()
        {
            var mock_container = new Mock <IAtomContainer>(); var container = mock_container.Object;
            var mock_atom = new Mock <IPseudoAtom>(); var atom = mock_atom.Object;

            mock_atom.Setup(n => n.Label).Returns("R1");
            var atomSymbol = atomGenerator.GenerateSymbol(container, atom, HydrogenPosition.Left, new RendererModel());
            var shapes     = atomSymbol.GetOutlines();

            Assert.AreEqual(2, shapes.Count);
        }
Example #2
0
        /// <summary>
        /// Generate the intermediate <see cref="AtomSymbol"/> instances.
        /// </summary>
        /// <param name="container">structure representation</param>
        /// <param name="symbolRemap">use alternate symbols (used for Sgroup shortcuts)</param>
        /// <param name="visibility">defines whether an atom symbol is displayed</param>
        /// <param name="parameters">render model parameters</param>
        /// <returns>generated atom symbols (can contain <see langword="null"/>)</returns>
        private AtomSymbol[] GenerateAtomSymbols(IAtomContainer container,
                                                 Dictionary <IAtom, string> symbolRemap,
                                                 SymbolVisibility visibility,
                                                 RendererModel parameters,
                                                 ElementGroup annotations,
                                                 Color foreground,
                                                 double stroke,
                                                 StandardDonutGenerator donutGen)
        {
            var scale      = parameters.GetScale();
            var annDist    = parameters.GetAnnotationDistance() * (parameters.GetBondLength() / scale);
            var annScale   = (1 / scale) * parameters.GetAnnotationFontScale();
            var annColor   = parameters.GetAnnotationColor();
            var halfStroke = stroke / 2;

            var symbols = new AtomSymbol[container.Atoms.Count];
            var builder = container.Builder;

            // check if we should annotate attachment point numbers (maxAttach>1)
            // and queue them all up for processing
            var attachPoints = new List <IPseudoAtom>();
            int maxAttach    = 0;

            for (int i = 0; i < container.Atoms.Count; i++)
            {
                var atom = container.Atoms[i];

                if (IsHiddenFully(atom))
                {
                    continue;
                }

                if (atom is IPseudoAtom)
                {
                    var attachNum = ((IPseudoAtom)atom).AttachPointNum;
                    if (attachNum > 0)
                    {
                        attachPoints.Add((IPseudoAtom)atom);
                    }
                    if (attachNum > maxAttach)
                    {
                        maxAttach = attachNum;
                    }
                }

                var remapped     = symbolRemap.ContainsKey(atom);
                var bonds        = container.GetConnectedBonds(atom);
                var neighbors    = container.GetConnectedAtoms(atom);
                var visNeighbors = new List <IAtom>();

                // if a symbol is remapped we only want to consider
                // visible neighbors in the alignment calculation, otherwise
                // we include all neighbors
                foreach (var neighbor in neighbors)
                {
                    if (!remapped || !IsHidden(neighbor))
                    {
                        visNeighbors.Add(neighbor);
                    }
                }

                var auxVectors = new List <Vector2>(1);

                // only generate if the symbol is visible
                if (visibility.Visible(atom, bonds, parameters) || remapped)
                {
                    var hPosition = HydrogenPositionTools.Position(atom, visNeighbors);

                    if (atom.ImplicitHydrogenCount != null && atom.ImplicitHydrogenCount > 0)
                    {
                        auxVectors.Add(hPosition.Vector());
                    }

                    if (remapped)
                    {
                        symbols[i] = atomGenerator.GenerateAbbreviatedSymbol(symbolRemap[atom], hPosition);
                    }
                    else if (donutGen.IsChargeDelocalised(atom))
                    {
                        var charge = atom.FormalCharge;
                        atom.FormalCharge = 0;
                        // can't think of a better way to handle this without API
                        // change to symbol visibility
                        if (atom.AtomicNumber != 6)
                        {
                            symbols[i] = atomGenerator.GenerateSymbol(container, atom, hPosition, parameters);
                        }
                        atom.FormalCharge = charge;
                    }
                    else
                    {
                        symbols[i] = atomGenerator.GenerateSymbol(container, atom, hPosition, parameters);
                    }

                    if (symbols[i] != null)
                    {
                        // defines how the element is aligned on the atom point, when
                        // aligned to the left, the first character 'e.g. Cl' is used.
                        if (visNeighbors.Count < 4)
                        {
                            if (hPosition == HydrogenPosition.Left)
                            {
                                symbols[i] = symbols[i].AlignTo(AtomSymbol.SymbolAlignment.Right);
                            }
                            else
                            {
                                symbols[i] = symbols[i].AlignTo(AtomSymbol.SymbolAlignment.Left);
                            }
                        }

                        var p = atom.Point2D;

                        if (p == null)
                        {
                            throw new ArgumentException("Atom did not have 2D coordinates");
                        }

                        symbols[i] = symbols[i].Resize(1 / scale, 1 / -scale).Center(p.Value.X, p.Value.Y);
                    }
                }

                var label = GetAnnotationLabel(atom);

                if (label != null)
                {
                    // to ensure consistent draw distance we need to adjust the annotation distance
                    // depending on whether we are drawing next to an atom symbol or not.
                    double strokeAdjust = symbols[i] != null ? -halfStroke : 0;

                    var vector     = NewAtomAnnotationVector(atom, bonds, auxVectors);
                    var annOutline = GenerateAnnotation(atom.Point2D.Value, label, vector, annDist + strokeAdjust, annScale, font, emSize, symbols[i]);

                    // the AtomSymbol may migrate during bond generation and therefore the annotation
                    // needs to be tied to the symbol. If no symbol is available the annotation is
                    // fixed and we can add it to the annotation ElementGroup right away.
                    if (symbols[i] != null)
                    {
                        symbols[i] = symbols[i].AddAnnotation(annOutline);
                    }
                    else
                    {
                        annotations.Add(GeneralPath.ShapeOf(annOutline.GetOutline(), annColor));
                    }
                }
            }

            // label attachment points
            if (maxAttach > 1)
            {
                var    attachNumOutlines = new List <TextOutline>();
                double maxRadius         = 0;

                foreach (IPseudoAtom atom in attachPoints)
                {
                    int attachNum = atom.AttachPointNum;

                    // to ensure consistent draw distance we need to adjust the annotation distance
                    // depending on whether we are drawing next to an atom symbol or not.
                    var strokeAdjust = -halfStroke;

                    var vector = NewAttachPointAnnotationVector(
                        atom,
                        container.GetConnectedBonds(atom),
                        new List <Vector2>());

                    var outline = GenerateAnnotation(atom.Point2D.Value,
                                                     attachNum.ToString(),
                                                     vector,
                                                     1.75 * annDist + strokeAdjust,
                                                     annScale,
                                                     new Typeface(font.FontFamily, font.Style, WPF.FontWeights.Bold, font.Stretch),
                                                     emSize,
                                                     null);

                    attachNumOutlines.Add(outline);

                    var w = outline.GetBounds().Width;
                    var h = outline.GetBounds().Height;
                    var r = Math.Sqrt(w * w + h * h) / 2;
                    if (r > maxRadius)
                    {
                        maxRadius = r;
                    }
                }

                foreach (var outline in attachNumOutlines)
                {
                    var group  = new ElementGroup();
                    var radius = 2 * stroke + maxRadius;
                    var shape  = new EllipseGeometry(outline.GetCenter(), radius, radius);
                    var area1  = Geometry.Combine(shape, outline.GetOutline(), GeometryCombineMode.Exclude, Transform.Identity);
                    group.Add(GeneralPath.ShapeOf(area1, foreground));
                    annotations.Add(group);
                }
            }

            return(symbols);
        }