Пример #1
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);
        }
Пример #2
0
        /// <inheritdoc/>
        public IRenderingElement Generate(IAtomContainer container, RendererModel parameters)
        {
            if (container.Atoms.Count == 0)
            {
                return(new ElementGroup());
            }

            var symbolRemap = new Dictionary <IAtom, string>();

            StandardSgroupGenerator.PrepareDisplayShortcuts(container, symbolRemap);

            var scale = parameters.GetScale();

            var visibility      = parameters.GetVisibility();
            var coloring        = parameters.GetAtomColorer();
            var annotationColor = parameters.GetAnnotationColor();
            var foreground      = coloring.GetAtomColor(container.Builder.NewAtom("C"));

            // the stroke width is based on the font. a better method is needed to get
            // the exact font stroke but for now we use the width of the pipe character.
            var fontStroke = new TextOutline("|", font, emSize).Resize(1 / scale, 1 / scale).GetBounds().Width;
            var stroke     = parameters.GetStrokeRatio() * fontStroke;

            var annotations = new ElementGroup();

            var donutGenerator = new StandardDonutGenerator(container, font, emSize, parameters, stroke);
            var donuts         = donutGenerator.Generate();

            var symbols = GenerateAtomSymbols(container, symbolRemap,
                                              visibility, parameters,
                                              annotations, foreground,
                                              stroke, donutGenerator);
            var bondElements = StandardBondGenerator.GenerateBonds(container, symbols,
                                                                   parameters, stroke,
                                                                   font, emSize, annotations,
                                                                   donutGenerator);

            var style     = parameters.GetHighlighting();
            var glowWidth = parameters.GetOuterGlowWidth();

            var backLayer   = new ElementGroup();
            var middleLayer = new ElementGroup();
            var frontLayer  = new ElementGroup();

            // bond elements can simply be added to the element group
            for (int i = 0; i < container.Bonds.Count; i++)
            {
                var bond = container.Bonds[i];

                if (IsHidden(bond))
                {
                    continue;
                }

                var highlight = GetHighlightColor(bond, parameters);
                if (highlight != null && style == HighlightStyle.OuterGlow)
                {
                    backLayer.Add(MarkedElement.Markup(OuterGlow(bondElements[i], highlight.Value, glowWidth, stroke), "outerglow"));
                }
                if (highlight != null && style == HighlightStyle.Colored)
                {
                    frontLayer.Add(MarkedElement.MarkupBond(Recolor(bondElements[i], highlight.Value), bond));
                }
                else
                {
                    middleLayer.Add(MarkedElement.MarkupBond(bondElements[i], bond));
                }
            }

            // bonds for delocalised aromatic
            frontLayer.Add(donuts);

            // convert the atom symbols to IRenderingElements
            for (int i = 0; i < container.Atoms.Count; i++)
            {
                var atom = container.Atoms[i];

                if (IsHidden(atom))
                {
                    continue;
                }

                var highlight = GetHighlightColor(atom, parameters);
                var color     = GetColorOfAtom(symbolRemap, coloring, foreground, style, atom, highlight);

                if (symbols[i] == null)
                {
                    // we add a 'ball' around atoms with no symbols (e.g. carbons)
                    if (highlight != null && style == HighlightStyle.OuterGlow)
                    {
                        backLayer.Add(MarkedElement.Markup(new OvalElement(ToPoint(atom.Point2D.Value), 1.75 * glowWidth * stroke, true, highlight.Value), "outerglow"));
                    }
                    continue;
                }

                var symbolElements = new ElementGroup();
                foreach (var shape in symbols[i].GetOutlines())
                {
                    GeneralPath path = GeneralPath.ShapeOf(shape, color);
                    symbolElements.Add(path);
                }

                // add the annotations of the symbol to the annotations ElementGroup
                foreach (var shape in symbols[i].GetAnnotationOutlines())
                {
                    annotations.Add(MarkedElement.Markup(GeneralPath.ShapeOf(shape, annotationColor), "annotation"));
                }

                if (highlight != null && style == HighlightStyle.OuterGlow)
                {
                    backLayer.Add(MarkedElement.Markup(OuterGlow(symbolElements, highlight.Value, glowWidth, stroke), "outerglow"));
                }

                if (highlight != null && style == HighlightStyle.Colored)
                {
                    frontLayer.Add(MarkedElement.MarkupAtom(symbolElements, atom));
                }
                else
                {
                    middleLayer.Add(MarkedElement.MarkupAtom(symbolElements, atom));
                }
            }

            // Add the Sgroups display elements to the front layer
            var sgroups = StandardSgroupGenerator.Generate(parameters, stroke, font, emSize, foreground, atomGenerator, symbols, container);

            frontLayer.Add(sgroups);

            // Annotations are added to the front layer.
            frontLayer.Add(annotations);

            var group = new ElementGroup
            {
                backLayer,
                middleLayer,
                frontLayer
            };

            return(MarkedElement.MarkupMol(group, container));
        }